做机械臂轨迹规划的人,应该都遇到过类似的痛点:给定一个末端位姿,你不仅要规划出一条不撞障碍物的路径,还得保证关节力矩不超过上限、加速度不猛冲、末端不抖。一旦把这些动力学约束全部塞进去,问题瞬间变成一个高维非线性边界值问题,求解起来极其痛苦。这几年我在各种论文和项目里频繁撞见同一个概念——微分平坦(Differential Flatness),它在四旋翼轨迹规划里几乎封神,现在正快速向机械臂规划领域蔓延。这篇是轨迹规划理论系列的第一篇,专门把微分平坦这件事讲透:它到底是什么、凭什么能简化轨迹规划、在机械臂场景里怎么落地、又有哪些坑等着你去踩。
不管你是做机械臂的工程师、研究运动规划的学生,还是对四旋翼/移动机器人感兴趣的人,这篇内容都能帮你在现有规划框架里多一把趁手的工具。理解微分平坦之后,你会看明白一件事:很多看似复杂的轨迹优化问题,本质上只是你选错了规划空间。
1. 先搞懂微分平坦在说什么
1.1 平地起高楼:从系统方程到平坦输出
先别急着记公式,我们从“系统长什么样”开始。一个连续时间非线性系统,通常写成状态方程形式:
x_dot = f(x, u)
其中 x 是状态向量,u 是控制输入向量。对于机械臂,x 常见的就是关节角和关节角速度,u 就是关节力矩。轨迹规划要做的事,就是找一条满足这个微分方程的状态轨迹和控制输入序列。
微分平坦说的是这么一件事:如果这个系统存在一组特殊的输出变量 z,使得系统所有的状态 x 和控制输入 u,都能由 z 以及它的有限阶导数直接表示出来,那么这个系统就是微分平坦的,z 就叫平坦输出。用大白话讲就是:
x = Φ(z, z_dot, z_ddot, ..., z^(q)) u = Ψ(z, z_dot, z_ddot, ..., z^(q))
这里的 q 是一个有限整数,意思是只需要取到平坦输出有限阶导数就够了。你不需要求解任何微分方程,只需要给定一条足够光滑的 z(t) 曲线,代入上面两个式子,系统的全部状态和输入就全出来了。
有人可能会问:这不是多此一举吗?本来状态就是 x,你现在非要用另一个变量 z 来表示 x,有什么意义?关键就在这里。对于大多数非线性的欠驱动系统,x 和 u 之间隔着一层非线性微分约束,直接规划 x 和 u 非常难。但如果你能找到一组好的 z,微分约束就被打散变成代数关系了。
1.2 一个比方能听懂的平坦直觉
我打个比方。假设你要让一个朋友从北京坐高铁去上海,同时要求他全程不晕车、按时吃饭、手机有电。直接规划“他每一秒钟的吃饭速度、手机充电功率”是非常麻烦的,因为这些东西之间相互耦合。但如果你先规划一条从北京到上海的高铁线路,再根据这条线路反推每一站停多久、每一段用什么速度跑、什么时候该点外卖,事情就简单多了。高铁线路就是平坦输出,吃饭和充电就是由它决定的状态和控制。
四旋翼是最好的例子。四旋翼一共有 12 个状态(位置、速度、姿态、角速度)和 4 个输入(总推力 + 三轴力矩),直接规划姿态和解算推力的过程非常绕。但如果你选位置 (x,y,z) 和偏航角 ψ 作为平坦输出,剩下所有状态和输入都由它们以及它们的导数代数给出。你只需要规划出一条光滑的空间曲线和偏航角曲线,推力、姿态、角速度全部自动反算出来。这就是微分平坦在四旋翼领域被广泛使用的原因。
1.3 为什么轨迹规划者把它当宝贝
传统轨迹规划方法多数是“打靶法”或者“直接配点法”:把时间离散成一堆节点,把状态和控制当作优化变量去迭代求解,每一步都要反复检查系统动力学约束是否满足。这种做法有两个问题:一是优化变量暴涨,二是非线性约束让求解器经常陷入局部最小值。
微分平坦的思路完全不同。它把问题从高维状态空间投影到低维平坦输出空间。你只需要在平坦输出空间里设计曲线,然后解析地求出状态和控制量。规划一个四旋翼轨迹,本质变成规划三个位置分量和偏航角的多项式;规划一台吊车防摆轨迹,本质变成规划负载位置的光滑曲线。原来那些动力学微分约束,全部变成了平坦输出及其导数的代数校验,你可以直接写进约束条件里。
这种降维打击的爽感,用过一次就回不去。
2. 轨迹规划里的微分平坦玩法
2.1 规划流程:在平坦输出空间里开挂
假设我们已经确认系统是微分平坦的,并且找到了平坦输出 z,接下来该怎么做轨迹规划?我一般按四步走:
第一步,选参数化形式。确定用多项式、B样条、贝塞尔曲线,还是分段多项式来参数化平坦输出 z(t)。这一步决定了轨迹的光滑程度和优化变量数量。
第二步,建立约束映射。把原系统里你关心的约束全部转写成 z 及其导数的约束。比如四旋翼的推力不能为负、关节力矩不能超限、末端不能碰到障碍物,这些都是可以转写为 z 的代数条件的。避障这类空间约束在平坦输出空间往往特别直观,因为平坦输出通常就是你真正关心的物理量(位置、末端坐标)。
第三步,求解优化问题。在平坦输出空间里解一个带约束的优化问题,目标函数可以是轨迹时间最短、加速度平方积分最小,或者总能量最少。因为变量少、约束大多是代数约束,求解速度比直接配点法快一个数量级。
第四步,反演验证。把优化得到的 z(t) 代回映射关系,得到完整的状态轨迹和控制输入,然后丢进仿真或者实机里验证。
这套流程我实测下来,最大的优势是“可解释性”特别强。你可以直接看到轨迹在物理空间里长什么样,而不是面对一堆抽象的状态变量。
2.2 参数化大法:多项式、B样条、min-snap
在平坦输出空间里,参数化方式是整个规划效果的分水岭。
最朴素的是单段多项式。比如让位置轨迹是一个时间 t 的 N 阶多项式,给定起点和终点的位置、速度、加速度约束,解线性方程就能得到系数。优点是简单,缺点是轨迹自由度太小,中间很难插入中间点约束,还容易产生龙格振荡。
工程上我更喜欢分段多项式,尤其是样条。把时间轴切成多段,每一段用低阶多项式拼接,同时保证段间的高阶导数连续。常用于四旋翼的 minimum snap(最小加加速度平方积分)就是这类思路。因为四旋翼的控制输入跟加加速度、加加速度的变化率有关,直接在平坦输出空间里最小化 snap,反算出来的控制输入自然平滑,无人机飞起来不会一顿一顿。
B样条是另一个常用选项。它的好处是局部支撑:你移动一个控制点,只影响附近一小段轨迹,不会牵一发动全身。做实时避障重规划时非常方便。代价是边界条件处理起来比分段多项式麻烦一些,需要额外的预约束技巧。
我做机械臂相关工作的时候,通常先看问题对“实时性”的要求:一次求解离线用的,用分段多项式加非线性优化;需要在线重规划的,优先上B样条。
2.3 常见平坦系统对照表
我习惯遇到一个新系统时,先查它是不是微分平坦的。下面这张表是我日常用的快速对照:
| 系统 | 典型平坦输出 | 说明 |
|---|---|---|
| 四旋翼 | 三维位置 + 偏航角 | 其余状态与控制由位置和偏航的导数代数给出 |
| 差速驱动小车 | 车轮中点坐标 (x,y) | 线速度和角速度由位置导数反算 |
| 桥式吊车 | 吊钩/负载的水平位置 | 可以直接规划负载路径并同时保证防摆 |
| 倒立摆小车 | 小车位置(带一定组合) | 具体平坦输出形式跟杆长和质量相关 |
| 柔性关节机械臂 | 电机侧位置或负载侧位置 | 可以根据需要选择,用于抑制残余振动 |
| 全驱动串联机械臂 | 关节角(平凡情形) | 关节角本身就是平坦输出,动力学完全由力矩决定 |
这张表只是起点。实际工程里很多系统不是天生平坦的,但通过反馈线性化或者部分模型简化,可以在局部近似平坦,这也是一个很常用的取巧思路。
3. 机械臂场景下的实操拆解
3.1 全驱动串联臂:为什么关节角本身就是平坦输出
先聊最常见的工业机械臂。一个刚性串联机械臂,每个关节都有独立的电机驱动,自由度等于驱动数,这类系统其实是“平凡平坦”的——你取关节角 q 作为平坦输出,关节速度就是关节角的一阶导数,关节加速度是二阶导数,再代入动力学方程:
M(q) q_ddot + C(q, q_dot) q_dot + g(q) = τ
力矩 τ 可以直接由 q、q_dot、q_ddot 算出。这意味着,理论上你只要规划出关节角轨迹,力矩轨迹就完全确定了,这就是典型的平坦输出退化情境。
但这里有一个容易忽略的重点:虽然关节角是平坦输出,但关节角的任意光滑曲线并不一定对应物理上合理的运动。因为力矩约束、电机速度约束、加速度约束都会对 q 的光滑性提出额外要求。所以机械臂规划的核心往往不是“如何解系统微分方程”,而是“如何把 q 的曲线约束到满足力矩和加加速度限制的可行空间里”。
在实际做的时候,我会直接把动力学模型当作一个“约束校验器”:在平坦输出空间里生成候选轨迹,代入动力学反算力矩,看有没有超限。而没有微分平坦的人,往往只能用数值积分一遍遍仿真试错。
3.2 欠驱动/柔性关节:平坦输出怎么找
真正让微分平坦大放异彩的机械臂场景,其实是欠驱动和柔性关节系统。比如带有弹性减速器的机械臂,电机端和连杆端不是刚性连接,存在弹簧形变。这个系统的状态比输入多得多,直接控制很难。
以单柔性关节臂为例,电机转角 θ 驱动一个弹簧,弹簧再驱动连杆。这个系统是微分平坦的,平坦输出可以选电机转角的位置,也可以选连杆位置。选电机转角作为平坦输出时,可以通过规划电机侧的平滑转角来抑制连杆侧振动;选连杆位置作为平坦输出时,可以直接给定末端期望轨迹,反算电机该给多大输入。
我当时第一次把柔性关节臂的轨迹规划改成微分平坦方法时,最大的感触是:以前要处理一个二阶动态微分约束和振动抑制约束的耦合,经常数值求解失败;改成平坦输出之后,约束变成了平坦输出四阶导数的显示表达式,优化求解器基本没有不收敛的。
不过要注意,平坦输出的选取不是唯一的,不同选取会导致映射关系复杂程度完全不同。我建议多试几个候选输出,比较一下它们代数表达式的复杂度,再决定用哪一个。
3.3 一个可落地的仿真案例:2R机械臂末端轨迹规划
讲一个我做过的最小可复现案例。平面 2R 机械臂,两个关节,末端要沿一条直线从 A 点运动到 B 点,同时保证关节力矩不大幅超限。
第一步,我选末端位置 (x, y) 作为“候选平坦输出”。严格说,对于 2R 臂,末端位置并不能全局唯一确定关节角,存在腕部奇异问题,所以这个方法只在非奇异区间有效。我在规划时先检查末端路径是否经过奇异点。
第二步,用五阶多项式参数化 x(t) 和 y(t),满足起点和终点的位置、速度、加速度约束。
第三步,对每个时间点,用逆解求关节角 q(t),再用解析或数值微分得到 q_dot 和 q_ddot,代入 2R 臂的动力学方程计算力矩。
第四步,检查力矩约束。如果超限,就提高多项式阶数、增加中间节点,或者重新分配时间。
这么一套操作在 MATLAB 或者 Python 里大概一百行代码就能写完。相比直接用数值最优控制解非线性规划,它的求解速度快、调试直观,而且每一步你都知道问题出在轨迹的哪个段。
4. 坑与排查:微分平坦不是银弹
4.1 平坦输出构造没有通用算法
我见过最多人踩的坑,就是以为系统平坦就一定能找到平坦输出。事实是,微分平坦是一个性质,而“找到平坦输出”是一个技术活。它没有通用的自动构造算法,更多依赖物理直觉和试错。
比如你要找一个新型欠驱动机械臂的平坦输出,通常做法是先从系统总能量、动量、几何关系里猜候选变量,然后验证是否满足状态和输入都能被有限阶导数表示的条件。这个过程没有标准脚手架,理论功底和工程直觉各占一半。
我的建议是:先从简单的候选开始试,比如位置坐标、角度组合、重心投影、能量相关量。验证时不要只做符号演算,直接在仿真里丢几个不同输入,看反算出来的状态是否和真实系统一致。只要一个候选输出能稳定复现状态,基本就是对的。
4.2 奇异点与微分同胚失效
微分平坦映射本质上是平坦输出空间到状态空间的一个微分同胚,但这个同胚通常不是全局成立的。当系统处于奇异位形时,映射关系会退化,状态和控制量可能瞬间变成无穷大。
以 2R 机械臂为例,末端位置作平坦输出时,当机械臂完全伸直(肘部伸直)或完全折叠,雅可比矩阵奇异,关节角速度反解会爆炸。如果规划轨迹恰好穿过这个区域,你再光滑的末端轨迹也会换算出一个疯狂抖动的关节轨迹。
排查方法很简单:在反演过程的每个时间点检查状态转换矩阵的条件数。条件数过大,说明你正靠近奇异点,这时候需要重新规划路径,绕开奇异区,或者切换到关节空间的普通插值方式。我把这个检查写成了规划器里的一个默认警报,每次跑完轨迹就自动报告最大条件数。
4.3 常见误区和排查速查表
我整理了几条高频问题,直接拿去对照:
| 症状 | 可能原因 | 解决办法 |
|---|---|---|
| 反算出的控制输入振荡 | 平坦输出轨迹不够光滑 | 提高参数化阶数,或改用B样条并保证高阶导数连续 |
| 轨迹满足约束但实机抖动 | 忽略了执行器带宽限制 | 在平坦输出空间加低通滤波,或加频率域约束 |
| 求解器报约束无解 | 时间分配过紧 | 增加轨迹总时间,或放宽加加速度上限 |
| 奇异点附近数值爆炸 | 映射雅可比奇异 | 绕开奇异区,或切换局部规划器 |
| 优化结果对初值敏感 | 目标函数非凸性强 | 用分阶段优化:先求可行解再优化 |
还有一个容易出事的点是:微分平坦轨迹规划虽然能精确给出前馈控制,但它本质上是开环的。如果用前馈直接上机,任何模型误差和外部扰动都会累积。所以工程落地时一定要搭配反馈控制器,前馈负责把轨迹算准,反馈负责把偏差拉回来。
5. 实战心得与一点扩展
5.1 和传统方法比,微分平坦赢在哪输在哪
赢的地方很明显:建模清晰、变量少、求解快,而且规划出来的轨迹物理可解释。尤其是做四旋翼和吊车这类欠驱动系统时,微分平坦几乎是一个“开箱即用”的利器。
输的地方同样明显:它对系统结构要求苛刻,不是所有系统都平坦;平坦输出的构造依赖人工经验;约束处理在平坦输出空间经常变成高度非线性的不等式,优化求解时依然需要小心处理。
所以在机械臂项目里,我的选择思路是这样的:如果是全驱动刚性臂,普通关节空间规划器就够用,微分平坦更多是给你提供一种动力学校验视角;如果是柔性关节、移动机械臂这类欠驱动或者强耦合系统,微分平坦就是改变游戏规则的存在。
另外,微分平坦还有个隐藏用法——它可以当“轨迹生成器”的前置滤波器。哪怕你最终用 RRT、MPC 这类算法,也可以先在平坦输出空间里生成多条候选轨迹,然后用其他算法再做避障和优化。我最近在做的移动机械臂协同规划,就是把移动底座和机械臂的联合模型近似成平坦系统,先出几何可行路径,再用局部 MPC 修整动态约束,效果比从头到尾硬解一套非线性规划好太多。
5.2 我踩过的几个坑
第一个坑是参数化阶数贪多。一开始我总喜欢把轨迹阶数设得很高,觉得高阶光滑肯定好。结果反算出来的控制输入在边界附近剧烈抖动,频域上一堆高频分量。后来才明白,平坦输出空间的光滑不等于控制输入空间的平滑,关键要看系统对这个输出的“相对阶”。高相对阶系统,输出轨迹哪怕只抖一点点,控制量也会被放大得很厉害。解决方式是限制平坦输出最高非零导数的平方积分,也就是做各阶导数的加权正则化。
第二个坑是忽略了模型参数的不确定性。微分平坦反演完全依赖模型,参数稍微一偏,前馈控制就是错的。我第一次拿柔性关节臂上真机时,弹簧刚度标定偏了 5%,规划轨迹看着没毛病,一上电连杆直接开始震荡。之后我在规划目标函数里加了对模型误差的鲁棒项:把弹簧刚度的不确定区间代入,如果反算的控制量在不同刚度下差异过大,就降低该处轨迹的曲率或速度。
第三个坑是时间最优的诱惑。微分平坦让你很容易写出一个“时间最优”目标函数,让轨迹在约束边界上飞。解出来很漂亮,工程上一跑就废,因为实机不是一个精确的代数系统,一点摩擦、一点间隙都会让贴着约束边界的轨迹失稳。我现在做的项目几乎都改成“时间次优 + 约束裕量”,把力矩上限的 90% 当 100% 用,反而总任务时间差别不大,稳定性大幅提升。
最后分享一个小技巧。如果你拿到的系统不满足微分平坦,别急着换方法,试着做一下状态扩展:把某个状态的时间导数定义成新状态,有可能会让扩展后的系统变成平坦的。这也是很多仓库机器人、飞行机械臂团队常用的手段。做规划之前,花一天时间在模型上找平坦输出,比后面花一周调优化求解器划算得多。