📁 Sunhao's Log
学习笔记 ✍️ Second Brain

非线性约束微分动态规划 (Nonlinear Constrained DDP, CDDP) 深度剖析:活跃集灵敏度、阶段微型 QP 与工程实战

Differential Dynamic Programming with Nonlinear Constraints: Theory, Active-Set Sensitivities, Stage-wise QP & Benchmarks

#optimal-control #trajectory-optimization #cddp #nonlinear-constraints #active-set #robotics #quadcopter #math-heavy

非线性约束微分动态规划 (Nonlinear Constrained DDP, CDDP) 深度剖析

Differential Dynamic Programming with Nonlinear Constraints: Theory, Active-Set Sensitivities, Stage-wise QP & Benchmarks
基于 Zhaoming Xie, C. Karen Liu, Kris Hauser (ICRA 2017 / IEEE TRO) 经典论文 · 结合全库标准化符号规范与工程实践


💡 导语
在第一篇笔记《微分动态规划 (DDP) 全景探秘》中,我们详细推导了标准 DDP 和 iLQR 的二阶展开与 Riccati 反向传播。然而,在自动驾驶车辆避障、空间飞行器视场角安全走廊(Safe Corridors)以及无人机/双足机器人多约束穿越等严苛物理场景中,系统往往面临着极其复杂的非线性状态与控制不等式约束
传统 DDP 无法直接处理非线性约束;对数障碍(Log-Barrier)方法在边界附近严重病态且极易陷入较差的局部极小;而直接配点法(Direct Collocation / SQP)求解单块巨型非线性规划(NLP)则随着时域长度 出现计算开销激增,且无法直接获得反馈增益矩阵
本文以 Zhaoming Xie 等人的奠基性论文为核心,深入剖析约束微分动态规划 (Constrained DDP, CDDP):如何在反向传播中解析引入活跃集一阶扰动灵敏度(Active-Set Sensitivity)对偶乘子符号判定机制,并在前向传播中结合阶段二次规划(Stage-wise QP)与自适应盒式信赖域,在保持 线性时间复杂度的同时,实现受限非线性轨迹的严格可行收敛。


🧭 论文导读与核心速览 · 本地已收录复现工程

本文是微分动态规划处理非线性约束领域的奠基性经典文献 (Zhaoming Xie, C. Karen Liu, Kris Hauser, ICRA 2017)。它首次在不将系统打平为全轨迹大规模 NLP 的前提下,通过活跃集反向灵敏度解析求解前向阶段微型 QP 滚动,在严格保持 线性时间复杂度的同时,实现了非线性状态与控制硬约束的严格可行收敛。
在本地工程库 differential-dynamic-programming/Xie_2017_Nonlinear_Constrained_DDP/ 中已收录完整的 JAX 自动微分、确定性微型 Active-Set QP 求解器以及质点、车辆避障与四旋翼的完整复现套件。

⚙️ 精读提问 1 · 动力学相对度与欧拉陷阱:为什么位置避障对控制偏导恒为 0?
📍 跳转至 6.2 节系统考据 ↘
💬 读者提问:
“在多刚体或质点运动规划中,为什么直接用前向欧拉法离散化会导致 CDDP 阶段 QP 彻底失效?原论文在这个问题上隐藏了什么关键细节?”

核心答案:
相对度为 2 的偏导消亡:控制量是加速度/推力,而避障约束是空间位置函数。标准前向欧拉离散下,当步位置 ,与当步控制 毫无依赖关系,导致约束雅可比 !阶段 QP 无法感知控制量对避障的任何调整作用,算法立刻奇异退化;
工程修复方案:在本地复现中,必须改用**零阶保持器 (ZOH)** 或**中点法**,将下一时刻位置对当步控制的灵敏度恢复为 ,并将约束施加在状态转移上:

🐣 精读提问 2 · 严格可行初值魔咒:CDDP“鸡生蛋蛋生鸡”的困局
📍 跳转至 1.1 节系统考据 ↘
💬 读者提问:
“CDDP 要求初始名义轨迹必须处处可行。在复杂的未知障碍物或狭窄迷宫环境中,这个假设是否现实?它与后来 Howell 2019 ALTRO 的不可行启动相比有何差距?”

核心答案:
CDDP 的阿喀琉斯之踵:CDDP 是纯粹的**局部可行下降流方法**。如果初始控制序列仿真出的轨迹穿透了障碍物,阶段 QP 在第一轮前向滚动中就会因找不到可行解而信赖域崩溃发散;
ALTRO 的降维打击:Howell 2019 (ALTRO) 提出了虚构松弛控制输入 ,允许用任意粗糙的几何度线或 RRT 路径做初值,通过增广拉格朗日外环驱动缺陷归零,彻底打破了必须提供动力学可行初值的魔咒。

🔀 精读提问 3 · 策略失配:反向等式 QP 与前向不等式 QP 的贝尔曼断层
📍 跳转至 5.1 节系统考据 ↘
💬 读者提问:
“为什么 CDDP 在反向传递时将约束当作等式求解闭式增益,而在前向回放时却要逐时刻求解不等式 QP?这种前后不一致会带来什么理论后果?”

核心答案:
反向传播的线性折衷:反向传播为了获得解析的值函数二阶导数 ,只能假设名义活跃集保持不变,将不等式简化为切平面等式;
前向非线性曲率越界:但在前向非线性前推中,弯曲的障碍物边界迫使状态脱离切平面。前向逐阶段 QP 实际激活的活跃集经常不同于反向预测!这种策略失配(Policy Mismatch)破坏了贝尔曼最优性原理的一致性,使得前向成本往往不如二次模型预测的那样平滑下降,必须依靠信赖域缩小强制回退。

🧭 精读提问 4 · 对偶乘子符号检验:为什么负乘子必须被立即剔除?
📍 跳转至 4.3 节系统考据 ↘
💬 读者提问:
“式 (4-17) 中对候选活跃集求解名义乘子后,为什么必须剔除所有负乘子行?这在几何最优化中有何深刻含义?”

核心答案:
负乘子意味着向内吸引:对于不等式约束 ,若解出的乘子 ,表明无约束负梯度方向实际上指向**可行域内部**!
释放非绑定约束:此时如果强行锁定该约束作为等式,相当于在系统前方人为竖立了一堵空气墙,阻挡系统向代价更低的深层可行域脱离。只有立即释放负乘子约束,才能保证活跃集的动态可脱离性。

🐞 精读提问 5 · 经典文献捉虫:原论文四旋翼关键物理参数全缺失
📍 跳转至 7.3 节系统考据 ↘
💬 读者提问:
“为什么说按照 Xie 2017 论文原文是不可能独立复现出四旋翼实验的?我们在本地复现库中是如何补齐这套物理参数的?”

核心答案:
原论文的严重遗漏:作者在论文第 V-C 节中给出了复杂的动力学微分方程,却**没有公开四旋翼质量、转动惯量矩阵、空气阻力系数与电机升力极值**;
本地标准化补齐:在本地复现代码 make_quadcopter_experiment() 中,我们集中确立了一套标准物理常数:,阻力系数 ,重力加速度 ,从而使高维 12 状态飞行器仿真具备可复现与单元回归能力。

⚡ 精读提问 6 · 阶段微型 QP:为什么手写 Active-Set 远胜外部求解器?
📍 跳转至 5.2 节系统考据 ↘
💬 读者提问:
“在前向滚动中每一步都要解一个 QP,这难道不会极大拖慢计算速度吗?为什么不需要调用 OSQP 或 qpOASES?”

核心答案:
超微变量维度:阶段 QP 的决策变量仅仅是控制增量 (质点为 2,车辆为 2,四旋翼为 4);
盒式边界快速预剪枝:在本地 src/qp.py 中,利用信赖域盒式边界先做快速区间排除,绝大多数不相关的障碍物约束直接被滤除;剩余候选活跃约束组合极少,通过确定性矩阵消元只需几微秒即可得到严格解析解,开销微乎其微。

🌐 精读提问 7 · 欧氏局限与流形鸿沟:为什么无法直接迁移至航天器 SE(3)?
📍 跳转至 第 9 节系统考据 ↘
💬 读者提问:
“Xie 2017 的公式能否直接用于非合作空间航天器交会对接的六自由度位姿优化?”

核心答案:
无法直接迁移:Xie 2017 的所有推导均基于平坦欧氏空间向量加减。若在航天器姿态中使用欧拉角,在翻滚对接机动中必将发生万向节死锁;若使用四元数,其单位模长硬约束在反向 Riccati 展开与前向推进中会导致灾难性的模长退化;
出路:必须结合 Boutselis 2018 与 Alcan 2023 的李群切空间对数映射与伴随传播,构建严格保流形的 Lie-CDDP 框架。

🌌 精读提问 8 · 对 Chu 2026 几何 CDDP 论文的技术反哺与升华路线
📍 跳转至 第 9 节系统考据 ↘
💬 读者提问:
“我们的课题 Chu 2026 如何吸收 Xie 2017 的优点并克服其所有缺陷?”

核心答案:
吸收:吸收其活跃集投影算子与约束灵敏度反馈增益的几何直觉;
升级:针对空间航天器半直积物理约束(视场进近走廊锥、太阳翼防撞、制动速度包络),抛弃容易策略失配的前向阶段 QP,升级为 ALTRO 的“对角 AL 粗解 + 有效集投影牛顿二次精修”,并与李群保结构 DtL 步进相融合!


目录


1. 约束最优控制的困境与 CDDP 核心动机

在控制与机器人学中,绝大多数真实物理系统都不可避免地带有各种约束:

  1. 控制执行机构边界:电机最大扭矩、油门开度、推力非负性;
  2. 状态安全走廊与几何包络:避免撞击静止或移动障碍物、保持在车道线内、飞行器视场角限制;
  3. 状态-控制耦合动力学限制:摩擦锥(Friction Cone)约束、倾角与升力耦合边界。

1.1 传统约束处理机制的瓶颈对比

为了在轨迹优化中处理这些约束,学术界与工业界曾探索过数种主流路径:

约束处理范式典型代表算法核心数学机制核心优势严重物理/数值缺陷
罚函数与对数障碍法Log-Barrier DDP, Interior Point在目标函数中追加 可直接复用无约束 DDP 求解器代码边界处 Hessian 矩阵严重病态,极易卡在较差局部极小,中间迭代违反约束
控制受限投影法Box-QP iLQR (Tassa 2014)反向传递求解带边界的单步控制 QP,行截断更新增益计算极快(微秒级),适合电机扭矩限制完全无法处理状态不等式约束 与耦合约束
直接配点法 / 全局 NLPDirect Collocation + SNOPT / IPOPT将全时域状态和控制同时离散为决策变量,求解巨型稀疏 NLP能通用处理任意非线性硬约束求解单块巨型稀疏 KKT 系统,开销大,不输出闭环时变反馈增益
约束 DDP (CDDP)Xie 2017 CDDP活跃集反向灵敏度解析求解 + 前向阶段 QP 滚动与信赖域重算严格可行收敛、保持 复杂度、天然伴生受约束闭环反馈增益需严格物理可行初始轨迹热启动,反向要求 正定
graph TD
    Problem["受约束非线性轨迹优化问题 (Nonlinear Constrained OCP)"] --> BranchA["罚函数 / 障碍函数法 (Log-Barrier)"]
    Problem --> BranchB["全轨迹直接转录法 (Direct Collocation / SQP)"]
    Problem --> BranchC["约束微分动态规划 (Xie 2017 CDDP)"]
    
    BranchA --> FlawA["⚠️ 缺陷: 边界 Hessian 严重病态,难以严格满足硬约束"]
    BranchB --> FlawB["⚠️ 缺陷: 决策变量维度随 $N$ 线性膨胀,无天然伴生反馈增益"]
    BranchC --> WinC["✅ 优势: 时域 $O(N)$ 递归分解 + 活跃集解析灵敏度 + 逐阶段 QP 严格保可行性"]

1.2 CDDP 的核心设计哲学

Zhaoming Xie, C. Karen Liu 与 Kris Hauser 在 2017 年提出的 CDDP (Constrained DDP) 提出了一种优雅的结构性平衡:

  • 在反向传递(Backward Pass)中:识别候选活跃约束,通过局部一阶泰勒线性化建立阶段等式约束 QP,应用 KKT 条件解析求解出受约束的开环前馈控制增量 与闭环状态反馈增益 ,并利用拉格朗日乘子符号判定释放非绑定约束;
  • 在前向回放(Forward Pass)中:鉴于非线性约束曲率的存在,不直接采用纯仿射反馈,而是在每个时间步求解一个极小规模(仅 维变量)的阶段 QP,并配以自适应盒式信赖域与不可行回退重启机制;
  • 整体保持严格可行性:算法从一条可行(但次优)的初始标称轨迹出发,在每一次外层迭代中只接受既严格满足非线性动力学与非线性不等式约束、又实现总代价单调下降的新轨迹。

2. 标准非线性约束轨迹优化问题表述

依据全库统一符号标准化规范,我们考虑离散时间有限时域非线性系统:

其中:

  • 状态向量
  • 控制输入向量
  • 为二次连续可微的系统动力学映射。

系统在全时域内的累积性能指标泛函(Cost Functional)定义为:

其中 为各阶段运行代价(Running Cost), 为终端代价(Terminal Cost)。

系统面临通用可微非线性不等式约束:

以及终端约束 (或等式目标 )。这里 包含了任意可微状态不等式约束(如与静态/动态障碍物的欧氏安全距离)、控制限幅约束以及状态-控制耦合约束。

值函数与 Bellman 最优性原理

定义从时刻 、状态 出发到时域终点的最优剩余代价为值函数(Value Function / Cost-to-Go):

根据 Bellman 最优性原理,逆向动态规划递推方程为:

终端边界条件为:

定义动作-状态价值函数(Action-Value / Q-function):

则阶段最优控制子问题等价于:


3. 活跃约束对 Bellman 局部模型的拓扑剪裁

在无约束 DDP 中,我们在当前名义轨迹 周围对 做二阶泰勒展开,由于控制量在全空间无障碍自由变化,一阶最优性条件 能够解析导出控制增量是状态偏差的无约束仿射函数

但在约束存在时,这一无约束解析律彻底破裂: 当系统状态处于约束边界(例如飞行器紧贴障碍物边缘、或电机输出达到最大饱和幅值)时,上游状态产生微小扰动 ,最优下游控制 不能沿任意无约束梯度方向自由调整,而必须受制于约束切平面的投影流动,强制停留在可行域内部或滑移在约束超曲面上。

因此,反向传播必须显式求解:在约束切空间投影下的局部最优解对于状态偏差 的灵敏度(Sensitivity Derivative)


4. 受约束反向传播 (Constrained Backward Pass) 严格数学推导

设当前已有可行名义轨迹 。定义偏差量:

在名义点二阶多元泰勒展开为:

其中一阶与二阶展开导数(利用链式法则,上标撇号表示在 时刻评估):

注:在 iLQR 高斯-牛顿近似下,可忽略动力学二阶张量项 ;而在 Full DDP 下则完整保留。

4.1 候选活跃集选取与局部线性化

在时刻 ,对于不等式约束向量 ,我们选取处于“近活跃(Near-Active)”状态的分量。设定微小正容差 (例如 ),定义候选活跃集

在名义点 处将候选活跃约束一阶线性化:

对于已经紧贴边界的活跃约束(),其局部演化必须严格满足切平面等式:

其中定义雅可比矩阵:

4.2 阶段等式约束 QP 与 KKT 线性系统

将第 步的最优控制扰动寻优建模为受约束二次规划:

引入拉格朗日乘子向量 ,构建拉格朗日函数:

求偏导并令其为 0,得到一阶 Karush-Kuhn-Tucker (KKT) 方程组:

写成标准分块矩阵 KKT 系统:

4.3 对偶乘子符号判定与非活跃约束释放机制

在 KKT 最优性理论中,对于不等式约束 ,其对偶拉格朗日乘子必须满足非负互补条件

  • :表明该约束处于**绑定(Binding / Active)**状态,阻止了目标函数进一步下降,该约束对最优解有实质性限制;
  • 若某个候选约束解出的 :表明目标函数梯度的下降方向是指向可行域内部的!此时如果强制将该约束当作等式锁定(),将错误地阻碍系统向代价更低的内部区域移动!

CDDP 采用如下活跃集动态释放机制

  1. 首先在名义无状态扰动点 处求解 KKT 系统的标称拉格朗日乘子: 由式 (4-15) 第一式得 ,代入第二式:
  1. 逐行检查 剔除所有 的不等式约束行(释放约束),仅保留严格正乘子行与原有等式约束;
  2. 形成修剪后的约化约束矩阵

4.4 受约束前馈步长与反馈增益闭式解

在剔除负乘子后,设剩余有效活跃约束数量为 ,定义如下两个核心受约束算子矩阵:

💡 数学性质洞察

  • 矩阵 实质上是 在由约束诱导的切空间(Null-space of )上的正交投影算子;
  • 当不存在活跃约束时(),,公式无缝退化为经典无约束 DDP!

代入 KKT 方程组求解,得到最优控制扰动对于状态扰动的解析闭式解

其中:

  • 受约束开环前馈控制修正量 (Constrained Feedforward Gain)
  • 受约束闭环状态反馈增益矩阵 (Constrained Feedback Gain)

重点解析式 (4-22):反馈增益 由两部分构成:

  1. 第一项 :目标函数在约束切平面上的投影下降反馈;
  2. 第二项 约束沿流形法向的几何恢复项!当状态偏差 试图突破约束曲面时,该项产生反向补偿控制,强制将系统拉回约束边界内!

4.5 受约束值函数二阶模型逆向更新

将受约束最优控制律 (4-20) 代回二次模型 (4-2),并对 重新整理二次项与一次项,可得当前时刻值函数 的二阶导数逆向递推公式:

为了保证数值稳定性,每步对 实施对称化操作:

终端边界初始化条件为:

逆向循环递推至 ,即可完成整个受约束反向传播。

flowchart TD
    Start["终端初始化: $V_{\boldsymbol{x}}(N) = \nabla \phi, V_{\boldsymbol{x}\boldsymbol{x}}(N) = \nabla^2 \phi$"] --> Loop["逆向循环: $k = N-1, \dots, 0$"]
    Loop --> CalcQ["计算 Q 矩阵导数: $Q_{\boldsymbol{x}}, Q_{\boldsymbol{u}}, Q_{\boldsymbol{x}\boldsymbol{x}}, Q_{\boldsymbol{u}\boldsymbol{u}}, Q_{\boldsymbol{u}\boldsymbol{x}}$ 并加入 LM 阻尼"]
    CalcQ --> ActiveSet["判定候选活跃约束: $g_{k, i}(\bar{\boldsymbol{x}}_k, \bar{\boldsymbol{u}}_k) \ge -\epsilon$"]
    ActiveSet --> Multiplier["求解 $\delta \boldsymbol{x}=\boldsymbol{0}$ 标称对偶乘子 $\bar{\boldsymbol{\lambda}}_k$"]
    Multiplier --> Filter["过滤释放 $\bar{\lambda}_i < 0$ 的非活跃行,保留有效矩阵 $\hat{\boldsymbol{C}}_k, \hat{\boldsymbol{D}}_k$"]
    Filter --> Gains["计算受约束算子 $\boldsymbol{W}, \boldsymbol{H}$ 及增益 $\boldsymbol{k}_k, \boldsymbol{K}_k$"]
    Gains --> ValueUpdate["更新值函数导数: $V_{\boldsymbol{x}}(k), V_{\boldsymbol{x}\boldsymbol{x}}(k)$"]
    ValueUpdate --> Next["$k \leftarrow k-1$ 递推至初始时刻 $k=0$"]

5. 受约束前向回放 (Constrained Forward Pass) 与信赖域管理

5.1 为什么线性回放会失效?阶段 QP 的必要性

在无约束 DDP 中,前向传播只需简单地执行仿真:

但在强非线性约束下,纯线性回放会直接导致严重越界或碰撞

  1. 反向传播仅仅是基于当前名义点的局部一阶线性化,而真实的非线性障碍物表面(如球面、圆柱面)是弯曲的;
  2. 在全时域积分过程中,非线性动力学演化会导致状态累积漂移,使得线性增益给出的控制量 无法精确满足非线性约束
  3. 一旦在某个中间时刻发生越界,下游状态将严重偏离可行通道,导致整个轨迹发散。

因此,CDDP 在前向传播中采用了创新的“逐阶段 QP 重解算 (Stage-wise QP Rollout)”

5.2 阶段 QP 形式与自适应盒式信赖域

在前向仿真的每一个时刻 ,系统已经获得了当前真实的前向状态 。定义状态偏差

CDDP 在当前状态处求解一个小型凸二次规划(QP)来决定当前最优控制扰动

其中 为自适应盒式信赖域半径。

💡 阶段 QP 的极速求解特性
注意到该阶段 QP 的优化变量维度仅仅为控制输入维度 (对于质点为 2,车辆为 2,四旋翼为 4)。对于如此小规模的 QP,使用确定性积极集法(Active-Set QP)仅需几微秒即可精确求解,绝不会成为计算瓶颈!

解出阶段最优 后,更新控制并传播真实非线性动力学:

5.3 不可行回退重启与单调代价接受准则

在前向仿真过程中,CDDP 严格执行如下两重安全把关机制:

graph TD
    Start["开始前向滚动: $x \leftarrow x_0, k = 0$"] --> SolveQP["求解第 $k$ 步阶段 QP (含信赖域 $|\delta u| \le e$)"]
    SolveQP --> CheckFeasible{"阶段 QP 是否可行<br/>且满足非线性约束?"}
    
    CheckFeasible -- "❌ 不可行 (Infeasible)" --> Shrink["信赖域收缩: $e \leftarrow \alpha e$ (如 $\alpha=0.5$)<br/>重置回 $k=0$ 重新仿真"]
    Shrink --> Start
    
    CheckFeasible -- "✅ 可行 (Feasible)" --> Propagate["非线性动力学前向推进: $x_{k+1} = f(x_k, u_k)$"]
    Propagate --> CheckEnd{"是否已推至 $k=N$?"}
    CheckEnd -- "否" --> NextK["$k \leftarrow k+1$"] --> SolveQP
    
    CheckEnd -- "是" --> CheckCost{"评估总代价:<br/>$J_{\mathrm{temp}} < J_{\mathrm{init}}$ ?"}
    CheckCost -- "✅ 代价单调下降" --> Accept["接受新轨迹: $(\bar{\boldsymbol{X}}, \bar{\boldsymbol{U}}) \leftarrow (\boldsymbol{X}^{\mathrm{temp}}, \boldsymbol{U}^{\mathrm{temp}})$<br/>减小正则化阻尼: $\mu \leftarrow \beta \mu$"]
    CheckCost -- "❌ 代价上升" --> Reject["拒绝新轨迹<br/>增大正则化阻尼: $\mu \leftarrow \alpha_{\mu} \mu$"]
  1. 不可行回退与信赖域收缩 (Trust-Region Shrinking): 如果在某一步 ,阶段 QP 无解(出现不可行),或者数值积分后的真实状态严重违背了非线性物理约束,则判定当前步长 过大导致线性化模型失真。此时立即中断滚动,将信赖域半径缩减 ),并完全重置回 从头重新仿真
  2. 单调代价改善准则 (Monotone Cost Improvement): 当成功推进到时域终点 且全程物理可行后,计算整条候选轨迹的真实累积代价
    • :接受新轨迹,更新标称轨迹,并降低 LM 阻尼权重();
    • 若代价未改善:拒绝新轨迹,增大 LM 阻尼权重(),迫使下一次迭代更加偏向保守的受约束梯度流。

6. 正则化策略与动力学离散化避坑工程指南

6.1 状态与控制双重 LM 阻尼正则化

为了保证逆向递推时 Hessian 矩阵 严格正定可逆,同时防止新轨迹过度偏离旧轨迹,CDDP 采纳了 Tassa 等人的双重 Levenberg-Marquardt (LM) 正则化方案:

参数调节经验取值:

  • 衰减系数:
  • 放大系数:
  • 信赖域缩放系数:

6.2 动力学相对度(Relative Degree)与离散化陷阱

[!CAUTION] 致命工程避坑:欧拉离散化导致的约束灵敏度退化!

在多刚体动力学与质点系统中,系统的控制量通常是加速度/力/力矩 ,而避障约束通常是关于空间几何位置 的函数:

若在离散化时采用简单的标准显式欧拉法(Forward Euler):

此时,位置 对当步控制输入 的一阶偏导恒等于零:

后果是灾难性的 意味着约束矩阵在控制空间完全没有投影分量,反向传播与前向阶段 QP 均无法感知到当步控制对避碰约束的任何调整作用,导致求解器退化或奇异!

工业级解决方案: 采用具有二阶精度的零阶保持器 (Zero-Order Hold, ZOH)中点法 (Midpoint Runge-Kutta) 进行状态离散化:

将下一时刻的状态安全约束 作为当前步的转移约束,此时 ,约束灵敏度得以完美保持!


7. 三大经典基准物理系统实验深度剖析

论文与本地复现工程在三大典型欠驱动与受限非线性系统上对 CDDP 进行了严格的数值仿真与基准对标:

7.1 二维质点避障(Point Mass Double Integrator)

  • 状态与控制
  • 时域参数
  • 几何障碍物
    • 单障碍物:圆心位于 、半径 ,即
    • 双障碍物:追加圆心位于 、半径 的第二圆形障碍物;
  • 初始与目标
  • 表现:CDDP 顺利计算出平滑绕行切线轨迹,且在双障碍物狭窄通道中展现出优异的贴边过渡特性。

图 7.1.1:本地复现 CDDP 在二维质点单圆形障碍物避障任务中的轨迹迭代与收敛历程。

图 7.1.2:本地复现 CDDP 在双障碍物狭窄通道穿越任务中的精准贴边无碰撞轨迹。

图 7.1.3:原论文图 2,二维质点双障碍物避障轨迹与初值对比。


7.2 二维非完整约束车辆动态避障(2D Car with Kinematic Constraints)

  • 状态与控制(转向角速度与纵向加速度)
  • 动力学
  • 控制限幅硬约束:转向输入满足
  • 动态障碍物场景:一个半径为 的圆形障碍物自 向右匀速穿行();
  • 机动策略涌现:CDDP 自主规划出了极其智能的**“先低速等待避让,待动态障碍物穿过十字交叉口后急加速穿过”**的非凸时空机动行为!

图 7.2.1:本地复现非完整约束车辆绕行固定障碍物的平滑可行轨迹。

图 7.2.2:本地复现车辆面对动态横穿障碍物时的“减速礼让-加速超车”时空速度规划图。

图 7.2.3:原论文图 3,车辆避开固定障碍物的几何规划结果。

图 7.2.4:原论文图 4,车辆与动态穿行圆形障碍物在不同时间步的位置关系全貌。

图 7.2.5:原论文图 5,动态避障过程的时序动作分解图。


7.3 三维十二状态四旋翼飞行器(3D Quadcopter 6-DOF Dynamics)

  • 状态与控制:12 维非线性状态(位置 、速度 、欧拉角 、机体系角速度 ),4 维控制(电机平方转速 );
  • 执行机构单向推力约束
  • 三维双动态障碍物避碰
    • 障碍物 1:从 沿 轴匀速下坠;
    • 障碍物 2:从 沿 轴匀速横切;
  • 求解效果:在 12 维高维欠驱动强耦合非线性空间中,CDDP 在 5 秒计算预算内以终值代价 49.36 成功收敛并生成完美的三维避让航线。

图 7.3.1:原论文图 1,四旋翼飞行器在三维空间穿越两个运动球体障碍物的复杂机动轨迹。

图 7.3.2:原论文图 6,四旋翼固定球形障碍物避障的三维几何视角。


7.4 CDDP vs 对数障碍 DDP vs SNOPT/IPOPT 全景横向评测

论文在相同硬件平台与 5 秒统一计算预算下,对三大算法在不同时域长度下的收敛总代价进行了严谨对比:

测试算例与时域参数约束 DDP (CDDP)对数障碍 DDP (Log-Barrier)直接 SQP (SNOPT)深度结果剖析
质点,单圆障碍 ()0.0650.0420.073短时域简单凸障碍,三者均能平稳收敛
质点,双圆障碍 ()0.280.640.43对数障碍开始出现边界振荡与较差局部解
车辆,动态圆障碍 ()0.100.320.08CDDP 成功捕捉“减速等待-加速突破”非凸机动
质点,单圆障碍 ()0.07118.300.32长时域下对数障碍彻底发散;SNOPT 变慢
车辆,动态圆障碍 ()0.214.8086.00SNOPT 在长时域巨型稀疏 NLP 下陷入局部停滞
四旋翼,固定球 ()53.69635.5398.28CDDP 收敛速度明显超越 SNOPT,代价低 45%
四旋翼,双动态球 ()52.45182.0149.12CDDP 展现出卓越的高维多动态约束解算能力

图 7.4.1:原论文图 7,CDDP、对数障碍法与通用 NLP 直接配点法(SNOPT)在各算例下的收敛速度与约束满足度横向对比曲线。


8. 完整 Python / JAX 算法实战架构

以下为严格依循 Xie 2017 理论公式与本地工程库 differential-dynamic-programming/Xie_2017_Nonlinear_Constrained_DDP/src/ 架构编写的轻量级、内置确定性 Active-Set QP 解算器的工业级 CDDP 核心解算器:

import numpy as np

def solve_stage_convex_qp(H, g, C, d_bound, lower, upper, tol=1e-8):
    """
    微型确定性 Active-Set QP 求解器 (专门针对 n_u = 2~4 的控制维度设计)
    求解: min 0.5 * u^T H u + g^T u   s.t.  C u <= d_bound,  lower <= u <= upper
    """
    nu = g.size
    H = 0.5 * (H + H.T)
    
    # 构造统一不等式矩阵 A_ineq @ u <= b_ineq
    A_list = [C] if C.size else []
    b_list = [d_bound] if C.size else []
    
    # 盒式信赖域边界 [-e, e]
    A_list.extend([np.eye(nu), -np.eye(nu)])
    b_list.extend([upper, -lower])
    
    A_all = np.vstack(A_list)
    b_all = np.concatenate(b_list)
    m = A_all.shape[0]
    
    best_u = None
    best_obj = float('inf')
    found = False
    
    # 枚举可能的活跃约束集组合 (0 到 nu 个约束激活)
    from itertools import combinations
    for k in range(nu + 1):
        for combo in combinations(range(m), k):
            if k == 0:
                try:
                    u_cand = -np.linalg.solve(H, g)
                except np.linalg.LinAlgError:
                    continue
                lam = np.array([])
            else:
                A_act = A_all[list(combo)]
                b_act = b_all[list(combo)]
                KKT = np.block([[H, A_act.T], [A_act, np.zeros((k, k))]])
                rhs = np.concatenate([-g, b_act])
                try:
                    sol = np.linalg.solve(KKT, rhs)
                    u_cand = sol[:nu]
                    lam = sol[nu:]
                except np.linalg.LinAlgError:
                    continue
                if np.any(lam < -tol):  # 对偶乘子必须非负
                    continue
            
            # 校验全局可行性
            if np.all(A_all @ u_cand <= b_all + tol):
                obj = 0.5 * u_cand @ H @ u_cand + g @ u_cand
                if obj < best_obj:
                    best_obj = obj
                    best_u = u_cand
                    found = True
        if found and k > 0:
            break
            
    return best_u, found

class ConstrainedDDPSolver:
    """
    非线性约束微分动态规划 (Nonlinear CDDP) 求解器
    严格依据 Zhaoming Xie et al. (ICRA 2017) 闭式灵敏度与阶段 QP 实现
    """
    def __init__(self, dynamics, cost, constraint, nx, nu, horizon):
        self.dynamics = dynamics       # f(x, u), fx, fu, fxx, fuu, fux
        self.cost = cost               # l(x, u), lx, lu, lxx, luu, lux, lf, lfx, lfxx
        self.constraint = constraint   # g(x, u) <= 0, gx, gu
        self.nx = nx
        self.nu = nu
        self.N = horizon

        # 正则化参数与信赖域参数
        self.mu1 = 1e-3
        self.mu2 = 1e-3
        self.beta1 = 0.95
        self.beta2 = 0.95
        self.alpha1 = 1.05
        self.alpha2 = 1.05
        self.eps_active = 1e-3        # 候选活跃约束判定容差
        self.trust_region_init = 2.0  # 初始盒式信赖域半径 e
        self.alpha_trust = 0.5        # 信赖域缩减系数

    def solve(self, x0, u_nominal, max_iters=50, tol=1e-4):
        x_nom = np.zeros((self.N + 1, self.nx))
        u_nom = np.array(u_nominal)
        x_nom[0] = x0
        for k in range(self.N):
            x_nom[k + 1] = self.dynamics.f(x_nom[k], u_nom[k])

        current_cost = self._calc_total_cost(x_nom, u_nom)

        for iteration in range(max_iters):
            # 1. 受约束反向传播 (Constrained Backward Pass)
            k_seq, K_seq, success = self._backward_pass(x_nom, u_nom)
            if not success:
                self.mu1 *= self.alpha1
                self.mu2 *= self.alpha2
                continue

            # 2. 受约束前向回放 (Constrained Forward Pass with Stage-wise QP)
            x_new, u_new, new_cost, accepted = self._forward_pass(
                x_nom, u_nom, k_seq, K_seq, current_cost
            )

            if accepted:
                cost_reduction = current_cost - new_cost
                x_nom, u_nom, current_cost = x_new, u_new, new_cost
                self.mu1 = max(1e-6, self.mu1 * self.beta1)
                self.mu2 = max(1e-6, self.mu2 * self.beta2)
                if cost_reduction < tol:
                    print(f"[CDDP] 收敛于第 {iteration + 1} 次迭代,总代价: {current_cost:.6f}")
                    break
            else:
                self.mu1 *= self.alpha1
                self.mu2 *= self.alpha2

        return x_nom, u_nom, current_cost

    def _backward_pass(self, x_nom, u_nom):
        k_seq = np.zeros((self.N, self.nu))
        K_seq = np.zeros((self.N, self.nu, self.nx))

        Vx = self.cost.lfx(x_nom[self.N])
        Vxx = self.cost.lfxx(x_nom[self.N])

        for k in reversed(range(self.N)):
            xk, uk = x_nom[k], u_nom[k]
            fx, fu = self.dynamics.fx(xk, uk), self.dynamics.fu(xk, uk)

            Qx = self.cost.lx(xk, uk) + fx.T @ Vx
            Qu = self.cost.lu(xk, uk) + fu.T @ Vx
            Qxx = self.cost.lxx(xk, uk) + fx.T @ (Vxx + self.mu1 * np.eye(self.nx)) @ fx
            Quu = self.cost.luu(xk, uk) + fu.T @ (Vxx + self.mu1 * np.eye(self.nx)) @ fu + self.mu2 * np.eye(self.nu)
            Qux = self.cost.lux(xk, uk) + fu.T @ (Vxx + self.mu1 * np.eye(self.nx)) @ fx

            # Full DDP 动力学曲率项
            if hasattr(self.dynamics, 'fxx'):
                Qxx += self.dynamics.contract_tensor(Vx, self.dynamics.fxx(xk, uk))
                Quu += self.dynamics.contract_tensor(Vx, self.dynamics.fuu(xk, uk))
                Qux += self.dynamics.contract_tensor(Vx, self.dynamics.fux(xk, uk))

            try:
                Quu_inv = np.linalg.inv(Quu)
            except np.linalg.LinAlgError:
                return None, None, False

            # 候选活跃约束
            g_val = self.constraint.g(xk, uk)
            gx = self.constraint.gx(xk, uk)
            gu = self.constraint.gu(xk, uk)

            active_idx = np.where(g_val >= -self.eps_active)[0]

            if len(active_idx) == 0:
                k_k = -Quu_inv @ Qu
                K_k = -Quu_inv @ Qux
            else:
                C = gu[active_idx, :]
                D = -gx[active_idx, :]

                # 求解 dx = 0 标称乘子并过滤负乘子
                M = C @ Quu_inv @ C.T
                try:
                    lam = -np.linalg.solve(M, C @ Quu_inv @ Qu)
                except np.linalg.LinAlgError:
                    lam = np.zeros(len(active_idx))

                valid_mask = lam > 0
                if not np.any(valid_mask):
                    k_k = -Quu_inv @ Qu
                    K_k = -Quu_inv @ Qux
                else:
                    C_hat = C[valid_mask, :]
                    D_hat = D[valid_mask, :]
                    M_hat = C_hat @ Quu_inv @ C_hat.T
                    
                    W = np.linalg.solve(M_hat, C_hat @ Quu_inv)
                    H = Quu_inv @ (np.eye(self.nu) - C_hat.T @ W)

                    k_k = -H @ Qu
                    K_k = -H @ Qux + W.T @ D_hat

            Vx = Qx + K_k.T @ Qu + K_k.T @ Quu @ k_k + Qux.T @ k_k
            Vxx = Qxx + K_k.T @ Quu @ K_k + K_k.T @ Qux + Qux.T @ K_k
            Vxx = 0.5 * (Vxx + Vxx.T)

            k_seq[k] = k_k
            K_seq[k] = K_k

        return k_seq, K_seq, True

    def _forward_pass(self, x_nom, u_nom, k_seq, K_seq, current_cost):
        e = self.trust_region_init
        max_retries = 8

        for _ in range(max_retries):
            x_temp = np.zeros_like(x_nom)
            u_temp = np.zeros_like(u_nom)
            x_temp[0] = x_nom[0]
            rollout_feasible = True

            for k in range(self.N):
                dx = x_temp[k] - x_nom[k]
                
                # 真实阶段 QP:min 0.5 du^T Quu du + (Qu + Qux dx)^T du
                # s.t. gu du <= -g_val - gx dx,  -e <= du <= e
                gk = self.constraint.g(x_nom[k], u_nom[k])
                gx_k = self.constraint.gx(x_nom[k], u_nom[k])
                gu_k = self.constraint.gu(x_nom[k], u_nom[k])
                
                # 目标函数梯度在当前偏差处评估
                H_qp = self.cost.luu(x_nom[k], u_nom[k]) + self.mu2 * np.eye(self.nu)
                g_qp = self.cost.lu(x_nom[k], u_nom[k]) + self.cost.lux(x_nom[k], u_nom[k]) @ dx
                
                C_qp = gu_k
                d_bound = -gk - gx_k @ dx
                lower = -e * np.ones(self.nu)
                upper = e * np.ones(self.nu)

                delta_u, qp_ok = solve_stage_convex_qp(H_qp, g_qp, C_qp, d_bound, lower, upper)
                if not qp_ok:
                    rollout_feasible = False
                    break

                u_temp[k] = u_nom[k] + delta_u
                x_temp[k + 1] = self.dynamics.f(x_temp[k], u_temp[k])

                # 校验非线性约束是否满足
                if np.any(self.constraint.g(x_temp[k + 1], u_temp[k]) > 1e-4):
                    rollout_feasible = False
                    break

            if not rollout_feasible:
                e *= self.alpha_trust
                continue

            new_cost = self._calc_total_cost(x_temp, u_temp)
            if new_cost < current_cost:
                return x_temp, u_temp, new_cost, True
            else:
                return None, None, current_cost, False

        return None, None, current_cost, False

    def _calc_total_cost(self, x, u):
        total = self.cost.lf(x[self.N])
        for k in range(self.N):
            total += self.cost.l(x[k], u[k])
        return total

9. 现代约束 DDP 四大流派横评与 Chu 2026 几何 CDDP 论文升级范式

在轨迹优化演进史中,约束处理经历了数次深刻的范式转移。下表总结了四大主流约束 DDP 流派与我们在 Chu_2026_Geometric_CDDP_on_SE3 论文中确立的终极技术定位:

对比维度Xie 2017 CDDPTassa 2014 Box-QPHowell 2019 ALTROJallet 2022 PROX-DDPChu 2026 (本课题范式)
约束类型覆盖通用非线性不等式/等式仅限控制输入盒式边界通用非线性状态/输入约束通用非线性状态/输入约束空间非合作多物理硬约束
核心算法机制反向解析灵敏度 + 前向阶段 QP反向投影牛顿法 + 增益行截断AL-iLQR 粗解 + 投影精修近端主-对偶增广拉格朗日 (PDAL)对角 AL + 几何走廊 + 速度包络
流形结构支持❌ 仅限欧氏空间 ❌ 仅限欧氏空间 ⚠️ 误差四元数切空间投影⚠️ 支持流形缩回✅ 严格保李群 DtL 步进
初值可行性依赖❌ 必须提供严格可行初值❌ 必须提供动力学初值✅ 允许不可行初值启动✅ 允许不可行初值启动✅ 支持两点边值不可行几何初值
数值病态抵抗依赖 LM 阻尼与盒式信赖域极佳 (微秒级)极佳 (平方根 QR 分解)极致 (近端正则化抗奇异)极致 (平方根 QR + NDOB 鲁棒管)
在航天交会中的局限欧拉角万向节死锁,阶段 QP 易失配无法处理视场角与太阳翼避碰缺少非合作旋转伴随动力学显式推导缺少航天近场制动包络硬约束证明全闭环:几何保流形 + 鲁棒管不变性

对 Chu 2026 几何 CDDP 论文的三大终极技术反哺:

  1. 批判性吸纳 Xie 2017 约束灵敏度反馈: Xie 2017 提出的反馈增益分解式 为流形约束反馈提供了极佳的几何直觉;
  2. 摒弃前向阶段 QP,采用 ALTRO 两阶段求解范式: 彻底废弃 Xie 2017 容易导致策略失配与频繁信赖域回退的前向阶段 QP 滚动,采用 Howell 2019 的“增广拉格朗日粗解快速锁定活跃集 + 有效集牛顿投影高精度二次精修”;
  3. 升华航天半直积物理约束: 摒弃平坦欧氏障碍物模型,严格建立空间非合作目标翻滚交会下的视场进近走廊锥、太阳翼防撞椭球与接触速度安全制动包络,完成现代几何约束 DDP 的理论闭环!

10. 权威参考文献与延伸阅读

  1. Xie, Zhaoming, Liu, C. Karen, and Hauser, Kris. (2017). “Differential Dynamic Programming with Nonlinear Constraints.” In IEEE International Conference on Robotics and Automation (ICRA), pp. 695–702. (本文核心奠基文献)
  2. Tassa, Yuval, Mansard, Nicolas, and Todorov, Emanuel. (2014). “Control-limited differential dynamic programming.” In IEEE International Conference on Robotics and Automation (ICRA), pp. 1168–1175.
  3. Howell, Taylor A., Jackson, Brian E., and Manchester, Zachary. (2019). “ALTRO: A fast solver for constrained trajectory optimization.” In IEEE/RSJ International Conference on Intelligent Robots and Systems (IROS), pp. 7674–7681.
  4. Pavlov, Andrei, Shames, Iman, and Manzie, Chris. (2021). “Interior point differential dynamic programming.” IEEE Transactions on Robotics (T-RO), 37(6):2105–2120.
  5. Jallet, Wilson, Mansard, Nicolas, and Todorov, Emanuel. (2022). “PROX-DDP: Proximal constrained differential dynamic programming.” In IEEE International Conference on Robotics and Automation (ICRA).
  6. Boutselis, George I., and Theodorou, Evangelos. (2018). “Discrete-time Differential Dynamic Programming on Lie Groups: Derivation, Convergence Analysis and Numerical Results.” arXiv:1809.07883.
  7. Alcan, Gokhan, Risiglione, Martino, Jackson, Brian E., and Howell, Taylor A. (2023). “Constrained differential dynamic programming on manifolds.” IEEE Transactions on Robotics (T-RO).
  8. Kim, Beomjoon, Fan, David, and Choi, Jongeun. (2024). “MPPI-IPDDP: Real-time nonlinear model predictive control via sample-based and gradient-based optimization.” IEEE Robotics and Automation Letters (RA-L).