我正试图在pydrake数学程序中实现一个成本函数,然而,每当我试图除以一个决策变量并使用abs()时,我都会遇到问题。我的尝试实现的简要版本如下,我试图只包含我认为可能相关的内容。
T = 50
na = 3
nq = 5
prog = MathematicalProgram()
h = prog.NewContinuousVariables(rows=T, cols=1, name='h')
qd = prog.NewContinuousVariables(rows=T+1, cols=nq, name='qd')
d = prog.NewContinuousVariables(1, name='d')
u = prog.NewContinuousVariables(rows=T, cols=na, name='u')
def energyCost(vars):
assert vars.size == 2*na + 1 + 1
split_at = [na, 2*na, 2*na + 1]
qd, u, h, d = np.split(vars, split_at)
return np.abs([qd.dot(u)*h/d])
for t in range(T):
vars = np.concatenate((qd[t, 2:], u[t,:], h[t], d))
prog.AddCost(energyCost, vars=vars)
initial_guess = np.empty(prog.num_vars())
solver = SnoptSolver()
result = solver.Solve(prog, initial_guess)
我得到的错误是
RuntimeError Traceback (most recent call last)
<ipython-input-55-111da18cdce0> in <module>()
22 initial_guess = np.empty(prog.num_vars())
23 solver = SnoptSolver()
---> 24 result = solver.Solve(prog, initial_guess)
25 print(f'Solution found? {result.is_success()}.')
RuntimeError: PyFunctionCost: Output must be of .ndim = 0 (scalar) and .size = 1. Got .ndim = 2 and .size = 1 instead.
据我所知,问题出在输出的维度上 但我不知道该如何继续。我花了不少时间试图解决这个问题,但收效甚微。我还试着把np.abs改成pydrake.math.abs,但是我得到了以下错误。
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-56-c0c2f008616b> in <module>()
22 initial_guess = np.empty(prog.num_vars())
23 solver = SnoptSolver()
---> 24 result = solver.Solve(prog, initial_guess)
25 print(f'Solution found? {result.is_success()}.')
<ipython-input-56-c0c2f008616b> in energyCost(vars)
14 split_at = [na, 2*na, 2*na + 1]
15 qd, u, h, d = np.split(vars, split_at)
---> 16 return pydrake.math.abs([qd.dot(u)*h/d])
17
18 for t in range(T):
TypeError: abs(): incompatible function arguments. The following argument types are supported:
1. (arg0: float) -> float
2. (arg0: pydrake.autodiffutils.AutoDiffXd) -> pydrake.autodiffutils.AutoDiffXd
3. (arg0: pydrake.symbolic.Expression) -> pydrake.symbolic.Expression
Invoked with: [array([<AutoDiffXd 1.691961398933386e-257 nderiv=8>], dtype=object)]
任何帮助都将是非常感激的,谢谢!
一些看法。
你想使用的那种成本函数不需要使用python函数来强制执行。你可以直接说 (尽管这样做会引起其他错误) prog.AddCost(np.abs([qd[t, 2:].dot(u[t,:])*h[t]/d]))
.
论点: prog.AddCost
必须是一个 Drake 标量表达式。所以要确保你的numpy矩阵乘法返回一个标量。在上面的例子中,它们返回的是一个 (1,1)
numpy数组。
要最小化绝对值,你需要比这更复杂的东西。在当前的形式下,你传递的是一个无差异的目标函数:解算器并不太喜欢这样。假设你想最小化 abs(x)
. 优化中的一个标准技巧是增加一个额外的(松弛)变量,比如说 s
,并增加约束条件 s >= x
, s >= -x
,然后最小化 s
本身。所有这些约束条件和这个目标都是可微分和线性的。
关于用一个优化变量来划分目标。只要可以,就应该避免这种情况。例如(我有90%的把握),如果你不提供初始猜测,SNOPT或IPOPT等求解器会将初始猜测设置为零。这意味着,如果你不提供一个自定义的初始猜测,在第一次评估约束条件时,解算器将有一个零的除法,它会崩溃。
另外,正如Tobia所提到的,在成本函数中对决策变量进行除法可能会有问题。有两种方法可以避免这个问题
min f(x) / y
如果你能施加一个约束,即 y > 1
,那么SNOPT将不会尝试使用 y=0
这样就避免了零除法的问题。一个技巧是引入另一个变量作为除法的结果,然后将这个变量最小化。
例如,假设你想优化
min f(x) / y
你可以引入一个松弛变量 z = f(x) / y
. 并将此问题表述为
min z
s.t f(x) - y * z = 0