3. 优化问题建模:目标函数设计(最小化加速度、最小化 jerk)

好,咱们接着聊。上一节我们把优化问题的“骨架”搭起来了——变量、约束都有了。但光有骨架不行,你得告诉优化器:“嘿,什么样的轨迹才是好轨迹?”

这个“好”的标准,就是目标函数。

我个人习惯把目标函数比作“裁判的评分标准”。你想想看,同样的起点终点,有的轨迹开起来像坐过山车,有的像在冰面上滑行。为什么?因为裁判(目标函数)给不同的行为打了不同的分。

3.1 为什么是加速度和 jerk?

先问个问题:什么样的轨迹坐着舒服?

答案其实很直观——不颠簸、不突兀。你坐在车里,如果司机一脚油门一脚刹车,你肯定不舒服。这就是加速度在作怪。加速度大,推背感强,乘客难受。

那 jerk 呢?jerk 是加速度的变化率。说白了,就是“加速度变化得有多猛”。

举个例子:

  • 如果加速度从 0 突然跳到 3 m/s²,jerk 无穷大——你会被猛地推一下。
  • 如果加速度从 0 平滑地增加到 3 m/s²,jerk 很小——你几乎感觉不到。

我在项目中遇到过这样的情况:只优化加速度,结果轨迹虽然加速度不大,但 jerk 忽高忽低。乘客反馈说“这车怎么一窜一窜的”。嗯,这就是忽略了 jerk 的后果。

核心结论:加速度影响“舒适度”,jerk 影响“平顺度”。两者都要管。

3.2 目标函数的数学形式

好,咱们上公式。别怕,很简单的。

假设我们有 N 个轨迹点,每个点的状态包括位置 p、速度 v、加速度 a。那么目标函数可以写成:

minimize:  w₁ * Σ a_i²  +  w₂ * Σ j_i²

其中:

  • Σ a_i²:所有轨迹点加速度的平方和。平方是为了让大的加速度受到更大的惩罚。
  • Σ j_i²:所有轨迹点 jerk 的平方和。jerk 可以用差分近似:j_i = (a_{i+1} - a_i) / Δt。
  • w₁, w₂:权重系数。这两个系数决定了你更看重“舒适”还是“平顺”。

你可能会问:“为什么不用绝对值?用平方有什么好处?”

嗯,这里有个小技巧。平方函数是凸函数,而且处处可导。这对优化求解器来说非常友好。绝对值函数在零点不可导,求解器会头疼。我刚开始做优化时也踩过这个坑——用了绝对值,结果求解器半天不收敛。

经验之谈:权重 w₁ 和 w₂ 的比例很关键。我一般先设 w₁=1.0,w₂=0.1,然后根据效果微调。如果轨迹太“硬”,就加大 w₂;如果轨迹太“软”(响应慢),就减小 w₂。

3.3 离散化形式(实际代码用的)

上面是连续形式,但计算机只能处理离散数据。所以我们要把目标函数写成矩阵形式。

假设轨迹有 N 个点,时间间隔为 Δt。那么:

  • 加速度向量:a = [a₁, a₂, ..., a_N]ᵀ
  • jerk 向量:j = D * a,其中 D 是差分矩阵

差分矩阵 D 长这样(N-1 行,N 列):

D = [ -1/Δt   1/Δt    0    ...   0   ]
    [   0    -1/Δt   1/Δt  ...   0   ]
    [   ...    ...    ...   ...  ...  ]
    [   0      0      0   -1/Δt  1/Δt ]

那么目标函数就是:

J = w₁ * aᵀ * a  +  w₂ * (D*a)ᵀ * (D*a)

展开后:

J = aᵀ * (w₁ * I + w₂ * Dᵀ * D) * a

你看,这其实是一个二次型。优化问题变成了一个二次规划(QP)问题。求解起来非常高效。

注意:这里的 Δt 不能太大也不能太小。太大会导致 jerk 近似不准,太小会让矩阵条件数变差。我一般取 Δt = 0.1s 左右,具体看你的控制频率。

3.4 要不要加其他项?

除了加速度和 jerk,有时候还会加一些“惩罚项”。比如:

  • 速度惩罚:如果希望轨迹尽量匀速,可以加 Σ (v_i - v_ref)²
  • 位置惩罚:如果希望轨迹尽量靠近参考线,可以加 Σ (p_i - p_ref)²
  • 时间惩罚:如果希望尽快到达终点,可以加总时间 T

但我个人建议:不要一开始就加太多项。先只优化加速度和 jerk,看看效果。如果轨迹偏离参考线太远,再加位置惩罚。我曾经犯过一个错误——一口气加了 5 个惩罚项,结果调参调了整整一周,最后发现去掉两项效果反而更好。

说白了,目标函数的设计是一个“做减法”的过程。少即是多。

3.5 一个小例子

假设我们有一段 10 米的直线轨迹,起点速度 0,终点速度 0。我们分别用两组权重来优化:

权重设置 最大加速度 最大 jerk 乘坐感受
w₁=1.0, w₂=0.0 2.5 m/s² 8.0 m/s³ 起步猛,刹车也猛
w₁=1.0, w₂=0.5 1.8 m/s² 3.2 m/s³ 平顺,像老司机
w₁=0.5, w₂=1.0 1.2 m/s² 1.5 m/s³ 非常平顺,但有点慢

你看,权重不同,结果天差地别。这就是目标函数设计的魅力所在。

3.6 知识体系图

下面这张图总结了目标函数设计的核心逻辑:

目标函数设计核心逻辑 轨迹状态量 加速度惩罚项 Jerk 惩罚项 权重 w₁ 权重 w₂ 总目标函数 J

嗯,这张图把整个逻辑串起来了。从轨迹状态量出发,分别计算加速度和 jerk 的惩罚,乘以各自的权重,最后求和得到总目标函数。优化器的工作就是让这个 J 尽可能小。

3.7 避坑指南

最后,分享几个我踩过的坑:

  • 权重不要相差太大:如果 w₁ 和 w₂ 差了几个数量级,优化器会忽略小的那个。我一般让两者在同一个量级。
  • 注意单位:加速度的单位是 m/s²,jerk 的单位是 m/s³。数值大小不同,直接比较没意义。建议先归一化。
  • 别忘了边界条件:目标函数再漂亮,如果起点终点的加速度约束没处理好,轨迹也是废的。

我曾经在一个项目中,花了两天时间调权重,结果发现是加速度的符号搞反了。嗯,这种低级错误,犯一次就记住了。

好,目标函数的设计就聊到这里。下一节我们讲约束条件——别忘了,光有目标还不够,你得告诉优化器“不能做什么”。


可可精品资料库