我从连续时间动力学开始整理这组学习笔记,梳理离散化、求导与数值优化的基础工具,为后续 LQR 和 MPC 做准备。
**先修知识:**线性代数、多元微积分,以及基本的 Julia 语法。
阅读路线:
- 先建立状态、输入和动力学方程,再讨论平衡点附近的线性化。
- 比较显式欧拉、RK4 和隐式中点法,关注离散化误差与稳定性。
- 结合作业理解自动微分、牛顿法及二次规划中的约束处理。
笔记结合 CMU 16-745 的课程与作业整理,相关材料见课程作业页面。文中的代码片段保留学习时的实现,运行时还需使用对应作业的依赖与上下文。
其他学习视角可参考向阳的笔记和知乎我爱科研 的整理
Lecture 1:动力学、平衡点与线性化#
以质量为 m、长度为 l、输入为关节力矩 u 的理想单摆为例:
ml2θ¨+mglsinθ=u.取状态 x=[θ,θ˙]⊤,连续时间模型为
x˙=f(x,u)=[x2−lgsinx1+ml2u].它对状态是非线性的,对输入则是仿射的。更一般地,输入仿射系统可以写成 x˙=f0(x)+G(x)u。
机械系统常用的动力学形式为
M(q)q¨+C(q,q˙)q˙+g(q)=Bu,其中 M 为惯性矩阵,C(q,q˙)q˙ 表示科里奥利力与离心力项,g(q) 为重力项。此前的笔记把加速度写成了速度,此处作了修正。
在平衡点 (xˉ,uˉ) 附近,先令 f(xˉ,uˉ)=0,再定义扰动 δx=x−xˉ、δu=u−uˉ,得到一阶近似
δx˙≈Aδx+Bδu,A=∂x∂fxˉ,uˉ,B=∂u∂fxˉ,uˉ.在输入固定、模型足够光滑时,若 A 的所有特征值实部严格为负,可判断该平衡点局部渐近稳定;出现零实部特征值时,仅靠这个线性化判据不能下结论。参考课程的动力学笔记。
Lecture 2:动力学离散化#
以下假设每个采样区间内输入保持为 uk,采样周期为 h。
显式欧拉法#
xk+1=xk+hf(xk,uk).经典四阶 Runge–Kutta(RK4)#
k1k2k3k4xk+1=f(xk,uk),=f(xk+2hk1,uk),=f(xk+2hk2,uk),=f(xk+hk3,uk),=xk+6h(k1+2k2+2k3+k4).此前的笔记遗漏了中间两项的系数,并把最后一项误写为 k3;上式为修正后的表达。
隐式中点法#
xk+1=xk+hf(2xk+xk+1,uk).由于右端含有未知的 xk+1,每一步通常需要求解隐式方程。原先写的 xk+1=xk+hf(xk+1,uk) 是后向欧拉法,不是隐式中点法。
比较积分方法时,应同时考察步长、误差、稳定性和每一步的求解成本。课程材料见 Dynamics Discretization & Stability。
Lecture 3:数值优化基础#
基础概念(梯度,雅可比矩阵,Hessian矩阵)#
- 标量函数f:Rn→R的梯度向量(Gradient)
∇f(x)=[∂x1∂f,∂x2∂f,..,∂xn∂f]⊤示例f(x,y)=x2+y3的梯度为∇f=[2x,3y2]⊤
- 向量值函数F(x):Rn→Rm的雅可比矩阵(Jacobian)
JF=∂x1∂f1⋮∂x1∂fm⋯⋱⋯∂xn∂f1⋮∂xn∂fmF(x,y)=[x2ysiny]的雅可比矩阵为
J=[2xy0x2cosy]
-
标量函数f:Rn→R的Hessian 矩阵:n×n对称矩阵
Hf=∂x12∂2f⋮∂xn∂x1∂2f⋯⋱⋯∂x1∂xn∂2f⋮∂xn2∂2f
- 标量函数梯度的雅可比矩阵即是Hessian矩阵
- m=1时,雅可比矩阵退化为梯度的转置
- 行主导和列主导向量函数的复合求导会导致链式法则的式子不一样,由于矩阵的维度不一致)
JF∘g(x)=JF(g(x))Jg(x)HW1-Q1#
Julia 求导————————ForwardDiff.jl
- 标量函数f(x)对标量x的导数
- 标量函数f(x)对向量X=[x1,x2,...,xn]的导数—雅可比矩阵/梯度
- 向量函数f(X)=[f1(X),f2(X),...,fm(X),]对向量X=[x1,x2,...,xn]的Jacobian矩阵
求解一个非线性方程f(x)=0的方法(求根)#
-
不动点迭代法
离散系统动力学求平衡点
xk+1=f(xk,uk)f∗=x−f
-
Newton方法
f(x+dx)=f(x)+dxdfΔx=0Δx=−dxdf−1fx←x+ΔxLoop until convergence
在无约束优化问题中,可以用 Newton 法求解 ∇f(x)=0(一阶必要条件);驻点是否为局部极小值还需要进一步判断
HW1_S25_Q3 QP求解器(Log-Domain Interior Point Method)#
xmins.t.21xTQx+qTxAx−b=0Gx−h≥0引入拉格朗日乘子
- μ∈Rp 对应等式约束
- λ∈Rm对应不等式约束(要求 λ≥0)
拉格朗日函数为:
L(x,μ,λ)=21x⊤Qx+q⊤x+μ⊤(Ax−b)−λ⊤(Gx−h)KKT条件:梯度条件、原始可行性、对偶可行性、互补松弛条件
Qx+q+A⊤μ−G⊤λAx−bGx−hλλ∘(Gx−h)=0(stationarity)=0(primal feasibility)≥0(primal feasibility)≥0(dual feasibility)=0(complementarity)引入非负松弛变量 s≥0,使得 Gx−h=s;内点迭代时取 s>0。
新的拉格朗日函数
L(x,λ,μ)=21x⊤Qx+q⊤x+μ⊤(Ax−b)+λ⊤[s−(Gx−h)]Qx+q+A⊤μ−G⊤λAx−bGx−h−sλsλ∘s=0(stationarity)=0(primal feasibility)=0(primal feasibility)≥0(dual feasibility)≥0=ρ1(perturbed complementarity)这里使用 ρ>0 的扰动互补条件;原始 KKT 的右侧应为零。令 λ=ρe−σ,s=ρeσ(指数逐元素计算),得到内点中心路径上的方程。需要逐步减小 ρ 才能逼近原问题,不能固定一个正数就宣称解满足原始互补条件。
Qx+q+A⊤μ−G⊤ρe−σAx−bGx−h−ρeσ=0(stationarity)=0(primal feasibility)=0(primal feasibility)定义关于 z=[x;μ;σ] 的残差向量 rρ(z),及其 Jacobian Drρ(z)。残差向量不是 Jacobian,下面的分块矩阵也不是目标函数的 Hessian。
rρ(z)=Qx+q+A⊤μ−G⊤ρe−σAx−bGx−h−ρeσDrρ(z)=QAGA⊤00G⊤diag(ρ⊙e−σ)0−diag(ρeσ)每个 Newton 步求解 Drρ(z)Δz=−rρ(z),再配合线搜索和中心路径参数更新。这是算法推导,尚未构成完整求解器;终止时还需检查原始可行性、对偶可行性和互补残差。这里假设 Q 对称半正定;一般非凸 QP 的 KKT 驻点不能直接认定为全局最优解。

这里的P0和P1代表原始KKT和IP_KKT的残差,也就是
P0=Qx+q+A⊤μ−G⊤λAx−bmin.(Gx−h,0)min.(λ,0)λ∘(Gx−h)P1=Qx+q+A⊤μ−G⊤λAx−bGx−h−s砖块掉落仿真#
不考虑砖块的旋转,一个掉落的砖块的动力学方程可以写成
Mv˙+Mg=JTμ where M=mI2×2,g=[09.81],J=[01]其中,v=[vx;vz] 是速度,q=[qx;qz] 是位置,竖直向上为正;μ 是法向接触力。这里忽略旋转、摩擦和反弹,采用非穿透接触与 backward Euler 离散,不能直接当作一般刚体碰撞模型。
[vk+1qk+1]=[vkqk]+Δt⋅[m1JTμk+1−gvk+1]约束
Jqk+1μk+1μk+1Jqk+1≥0≥0=0(不会穿透地面)(接触力方向只向上)(没接触无接触力)等价转换成如下的QP问题(可以通过KKT条件证明)
minimizevk+1subject to21vk+1TMvk+1+[M(Δt⋅g−vk)]Tvk+1J(qk+Δt⋅vk+1)≥0引入拉格朗日乘子μ≥0
L=21vk+1TMvk+1+[M(Δt⋅g−vk)]Tvk+1−μ⋅J(qk+Δt⋅vk+1)KKT 条件
Mvk+1+M(Δt⋅g−vk)−JTμΔtJ(qk+Δt⋅vk+1)μμ⋅J(qk+Δt⋅vk+1)=0梯度条件≥0原始可行性≥0对偶可行性=0互补松弛 引入间隙松弛变量 s=J(qk+Δtvk+1),并令 μ=ρe−σ、s=ρeσ,使 μs=ρ。这是与上文一致的数值参数化;在本例中 ρ 带有接触力乘间隙的单位,实际实现还应考虑尺度归一化。
Mvk+1+M(Δt⋅g−vk)−JTρe−σΔtJ(qk+Δt⋅vk+1)−ρeσ=0=0
Julia 简介#
-
下载:windows 通过winget下载juliaup下载指定julia版本
在 VS Code 中安装 Julia 扩展,并检查自动发现的 Julia 路径。只有日志明确提示缺少 Juliaup release 通道时,才按提示补装;这不是所有补全故障的通用修复。
winget install --name Julia --id 9NJNWW8PVKMN -e -s msstore
# 若扩展提示缺少 release 通道,再执行 juliaup add release
安装命令见 Juliaup 官方说明。release 随时间变化,课程复现应保留原项目的 Project.toml、Manifest.toml 和 Julia 版本。
-
Julia 项目环境创建(管理依赖,不等同于隔离整个解释器的 Python 虚拟环境)
-
pyplot的使用
# 在 Julia 中执行(替换为你的 Python 路径)
ENV["PYTHON"] = raw"C:\Python39\python.exe" # Windows 示例
# ENV["PYTHON"] = "/usr/bin/python3" # Linux/macOS 示例
-
eltype和typeof
#返回容器(collection)里元素的类型(数组、向量、矩阵、迭代器)
eltype([1, 2, 3]) # Int64
eltype([1.0, 2.0]) # Float64
eltype(["a", "b"]) # String
eltype(1.0:0.1:2.0) # Float64
-
向量集合与矩阵的转换
将等长向量按列拼成矩阵,再用 eachcol 拆回列向量:
columns = [[1, 2], [3, 4]]
restored = [collect(column) for column in eachcol(A)]
整理与核查说明#
本笔记原有许可为 CC BY 4.0。引用的课程材料、代码和图片仍须遵守其各自的许可。
核查状态:部分验证(2026-10-04)。 已核对主要公式,并用小型数值例子检验 KKT Jacobian;整套课程作业未重新运行。
2026-10-04 整理时修正了已定位的公式和实现问题。文中的图片、动画和输出保留自学习时的实验记录,不代表修订后的代码已经完整重跑。作业片段依赖原项目环境,不能直接作为完整可运行教程;具体核查范围与尚未复现事项见正文。