跳转至

有限时域线性二次调节器

背景

线性二次调节器(LQR)用于权衡状态调节误差与控制消耗。它是少数具有 完整连续时间参考解的最优控制问题之一,因此不仅能演示优化器成功 终止,还能用来检验数值转录的实际精度。

这个标量问题同时包含运行代价、自由终端状态和终端惩罚。文中将 Pockit 配点解与独立积分得到的 Riccati 解逐点比较。

问题定义

线性动力学为

\[ \dot x=Ax+Bu,\qquad x(0)=x_0, \]

目标是最小化

\[ J=\int_0^T\left(Qx^2+Ru^2\right)\,\mathrm{d}t +\frac{F}{2}x(T)^2. \]

本例参数为

\[ A=-1,\quad B=1,\quad Q=1,\quad R=0.1, \quad F=1,\quad x_0=1,\quad T=1. \]

终端状态 \(x(T)\) 是自由变量。问题没有状态或控制边界;正权重 \(Q\)\(R\)\(F\) 使标量问题关于控制严格凸。

Riccati 参考解

将最优值函数写为

\[ V(t,x)=\frac{1}{2}P(t)x^2. \]

对 Hamilton-Jacobi-Bellman 方程中的 \(u\) 求极小,可得

\[ u^*(t)=-\frac{B P(t)}{2R}x^*(t). \]

标量 Riccati 方程及终端条件为

\[ \dot P=-2Q-2AP+\frac{B^2P^2}{2R}, \qquad P(T)=F. \]

反向积分 \(P\) 后,参考状态满足

\[ \dot x^*=\left(A-\frac{B^2P}{2R}\right)x^*, \qquad x^*(0)=x_0, \]

相应初态下的精确最优值为

\[ J^*=\frac{1}{2}P(0)x_0^2. \]

示例用高精度 solve_ivp 独立积分上述方程,不复用 Pockit 的配点 方程或 Ipopt 结果。

变量与单位

本基准问题无量纲。

符号 含义 数值或单位
\(t\) 归一化时间 -
\(x\) 标量调节误差 -
\(u\) 标量控制 -
\(A,B\) 动力学系数 \(-1,1\)
\(Q,R\) 运行状态和控制权重 \(1,0.1\)
\(F\) 终端状态权重 \(1\)

建模取舍与限制

实现采用固定区间 \([0,1]\) 上的单个 Lobatto 阶段,共八个网格区间, 每个区间六个点。系统级变量 x_final 表示自由终端状态,并与阶段 末端相连,从而在不固定终端值的情况下构造终端惩罚。

该模型刻意保持为标量、线性、无约束且无量纲。它用于验证目标函数 构造、自由终端状态和轨迹精度。带约束、非线性、随机或估计器耦合的 控制需要更丰富的模型,并且通常不再具有这里的闭式参考结构。

运行示例

python -m examples.linear_quadratic_regulator

无界面运行并保存图片:

python -m examples.linear_quadratic_regulator --save lqr.png --no-show

示例遵循统一流程:

system, phase = build_problem()
guess = initial_guess(phase)
solution = solve_problem(system, guess)
plot_solution(solution)

验证结果

脚本在 2,001 个时刻重构配点状态和控制,并与独立积分的 Riccati 参考 解比较。

指标 验证值
目标函数 \(J\) \(0.231913974452\)
终端状态 \(x(1)\) \(0.025836906771\)
相对 Riccati 解的最大状态误差 \(6.104\times10^{-9}\)
相对 Riccati 解的最大控制误差 \(1.303\times10^{-6}\)
相对 \(\tfrac12P(0)x_0^2\) 的目标函数误差 \(4.149\times10^{-14}\)

终端状态很小,但并未被固定为零;其优化值反映了残余误差与控制代价 之间的既定权衡。数值目标与连续 Riccati 最优值几乎达到机器精度一致。

Pockit 状态和控制轨迹与 Riccati 参考解的叠加比较

源代码

完整可运行示例见: examples/linear_quadratic_regulator.py