简介:一份聚焦伸缩臂抖动抑制的学术PDF文献,提出将微分平坦理论与自抗扰控制(ADRC)相结合的控制策略,适合从事机器人臂架、农业机械及高空作业平台控制的工程师与研究者阅读,也可作为控制理论课程的延伸参考文献。资源为单一PDF文件,大小2.46MB,含摘要、数学模型、控制器设计与实验验证等完整结构,便于直接阅读。已有149人学习,具备较好的参考价值。文中通过拉格朗日方程建立伸缩臂等效两刚性杆动力学模型,将第一杆仰角与第二杆末端加速度构造为微分平坦输出,设计带扩张状态观测器的线性自抗扰控制器,并随臂长变化实时更新参数,简化整定过程。仿真与实验表明,不同臂长条件下均可在2秒内消除抖动并保持仰角设定值稳定,方法适应性强、鲁棒性好,可为精量播种、机器人作业、医疗稳定平台等场景的抑振控制提供具体设计思路与参数整定参考。
1. 伸缩臂的抖不是结构问题,是控制问题:这个方向在解决什么
高空作业车把臂架伸到十几米,末端站个人或者吊个工件,动作一停,臂尖还在画圈;混凝土泵车夜里泵送,臂架末端抖得灯光都在晃。这类现象在工程上叫伸缩臂抖动,根源一半在结构柔性,一半在控制策略。结构柔性没法靠加厚钢板根治,加刚只会增重、降机动性。真正的突破口在控制器:能不能让臂架在运动过程中就不被激励起来,在停止后快速把残余振荡压下去。
这个标题给出的组合是“微分平坦 + 自抗扰控制(ADRC)”,两个词都偏理论,但落地路径非常清晰。微分平坦负责一件具体的事:把臂架的运动轨迹规划成平滑且动力学可行的曲线,不让动作本身成为抖动的激发源;ADRC 负责另一件:把臂架的负载变化、液压非线性、参数摄动统统计成“总扰动”,实时估计并补偿掉。一个管前馈怎么走,一个管反馈怎么补,分工明确。这篇笔记就按这条路径展开,从建模到可跑的仿真代码,再到参数整定与避坑,给出一套能复现的抖动抑制方案。
2. 微分平坦与ADRC为什么能成一对:先立住两个核心判据
2.1 伸缩臂系统凭什么平坦:一个能直接用的判定条件
微分平坦(differential flatness)的定义不复杂:一个非线性系统,如果存在一组输出 y,使得系统的所有状态变量和控制输入都能仅由 y 及其有限阶导数表达,那么这个系统就是微分平坦的。用式子写就是:
x = α(y, ẏ, ..., y^(q)) u = β(y, ẏ, ..., y^(q+1))
对伸缩臂这种全驱动机械臂系统,判断条件会大大简化。把伸缩臂的运动学建模成回转、变幅、伸缩三个自由度的刚体动力学,只要驱动矩阵可逆,系统就是平坦的,平坦输出可以直接取关节位置向量本身。换句话说,这不是一个需要翻山越岭才能验证的性质,而是一个对绝大多数全驱动机械臂天然成立的结论。
我一般建议从业者先做一个更保守的检查:把系统的 Euler-Lagrange 方程写出来,看惯性矩阵是否对所有构型都可逆。伸缩臂的惯性矩阵对角线元素包含 m·l² + J 这样的项,只要臂长 l 不为零、质量不为零,矩阵必然正定。这意味着平坦输出取关节角度和伸缩长度,理论上没有障碍。
但这只是理论判据,工程上还要再走一步:选出平坦输出之后,需要检查从平坦输出到实际控制输入的映射是否存在奇异构型。典型情况是当变幅角接近 0° 或 180° 时,某些雅可比项会退化。这个检查不需要符号推导到天荒地老,直接把动力学方程代入平坦输出表达式,求控制输入表达式的分母,看它在工作空间内是否可能接近零。
2.2 ADRC总扰动与平坦性如何分工:谁管前馈,谁管补偿
微分平坦给了你“最优轨迹应该长什么样”,ADRC 给了你“实际系统偏离这条轨迹时怎么拉回来”。两者不是竞争关系,而是串联关系。只用平坦性做轨迹规划,模型误差、负载突变、液压迟滞这些扰动会让实际轨迹跑偏;只用 ADRC 做反馈,如果参考轨迹本身带加速度阶跃,控制器再强也是在不断激励结构模态。
ADRC 的核心是把系统写成非常朴素的形式:
q̈ = f(q, q̇, w) + b₀·u
其中 f 代表所有说不清的部分:未建模摩擦力、臂架柔性变形、液压执行器非线性、外部负载变化。ADRC 用扩张状态观测器(ESO)把 f 实时估出来,然后在控制律里减掉它。这个设计有一个工程上的巨大优势:你不必把模型建到多精确,只需要知道控制增益 b₀ 的估计值。
在伸缩臂场景里,我的习惯做法是:把标称惯性矩阵留给“已知部分”,把科氏力、向心力、重力、柔性变形、负载扰动全部丢给总扰动。这样控制器代码里只有 M_hat(q),其他一概不用建模。这也是后面仿真里要验证的重点:标称模型故意取错 20%,控制系统依然能压住抖动。
2.3 平坦性验证失败的典型信号:别在奇异构型里硬算
如果系统不是全驱动,或者你选的平坦输出不合适,仿真里会有三个典型信号。第一,控制输入表达式里出现分母趋近于零的“爆点”,力矩指令在某个构型附近突然冲向无穷大;第二,轨迹规划阶段算出的参考加速度含有无法实现的突变,因为平坦映射把状态约束变成了代数约束,但代数约束本身可能在某些点退化;第三,ESO 估计的总扰动出现周期性的尖峰脉冲,这不是物理扰动,而是平坦映射在该构型附近病态。
遇到这三种情况,不要急着调 ADRC 参数。先把平坦输出换掉,比如从关节角组合换成末端笛卡尔位置的某种非奇异组合,或者把工作空间限制在奇异构型之外。这个检查必须在建模阶段就做,否则后面所有调试都是在给一个病态系统打补丁。
3. 最小可复现实现:带二自由度臂模型与 ESO 的抖动抑制代码
3.1 二自由度伸缩臂模型的动力学方程与仿真设定
为了把原理落到代码里,我把伸缩臂简化为两个自由度:变幅角 θ 和臂长 l。末端挂一个等效质量 m,转台惯量为 J。这个模型保留了伸缩臂抖动的两个核心机制:伸臂过程中质量分布变化引起的耦合,以及变幅运动停止后的残余振荡。
真实动力学用 Euler-Lagrange 方程写出来就是:
M(q)q̈ + C(q, q̇)q̇ + G(q) = τ
其中 M 是对角阵 diag(m·l² + J, m),科氏与向心力向量为 [2·m·l·l̇·θ̇, -m·l·θ̇²],重力项为 [m·g·l·cosθ, m·g·sinθ]。注意第二行 m·l̈ 那一项的符号,向心力项是负的,因为臂长方向上的等效离心力是把臂往外拉的。这个符号错了,仿真里伸缩运动会越跑越离谱。
控制器侧故意用错误的标称模型:m_hat 取真实值的 80%,J_hat 取真实值的 50%。这是我在实际项目中惯用的做法,ADRC 的价值恰恰体现在模型不精确时依然能压住扰动。下面这段代码定义真实动力学和 ESO 类,可以直接跑:
import numpy as np m = 15.0 # 末端等效质量, kg J = 2.0 # 转台转动惯量, kg*m^2 g = 9.81 dt = 0.001 T_sim = 8.0 n = int(T_sim / dt) t = np.linspace(0, T_sim, n) def real_dynamics(s, tau, F, d_tau): th, l, thd, ld = s M = np.array([[m * l * l + J, 0.0], [0.0, m]]) C = np.array([2.0 * m * l * ld * thd, -m * l * thd * thd]) G = np.array([m * g * l * np.cos(th), m * g * np.sin(th)]) qdd = np.linalg.solve(M, np.array([tau + d_tau, F]) - C - G) return np.array([thd, ld, qdd[0], qdd[1]]) class ESO: def __init__(self, w0, dt): self.z = np.zeros(3) self.L = np.array([3.0 * w0, 3.0 * w0 * w0, w0 ** 3]) self.dt = dt def update(self, y, u_nom): # y: 测量位置, u_nom: 控制器期望加速度指令 e = y - self.z[0] dz = np.array([ self.z[1] + self.L[0] * e, self.z[2] + u_nom + self.L[1] * e, self.L[2] * e ]) self.z += dz * self.dt return self.z[2] # 总扰动估计值这段代码的逻辑:real_dynamics 模拟“真实世界”,里面包含科氏力、向心力、重力,控制器完全不知道这些项的存在;ESO 类实现的是三阶扩张状态观测器,状态分别是位置、速度、总扰动。L 矩阵的取值 3ω₀、3ω₀²、ω₀³ 是把观测器极点配置在 -ω₀ 处的标准写法,对二阶被控对象加一个扩张态,这个配置是带宽整定的基础。
参数说明:ω₀ 是观测器带宽,单位 rad/s,决定扰动估计的速度和噪声放大程度。dt 是离散步长,ESO 用前向欧拉离散,步长必须远小于 1/ω₀,否则数值会发散。真实系统里 dt 由控制器采样周期决定,通常取 0.5~2ms 比较合适。
3.2 参考轨迹与控制器初始化
参考轨迹我用五次多项式从起点平滑过渡到终点。五次多项式的好处是位置、速度、加速度都连续,加加速度(jerk)虽然不连续但幅值有限,对柔性臂来说已经够用。如果第一阶柔性模态特别低,再改七次多项式。
def poly5(tau, y0, y1): s = 10.0 * tau**3 - 15.0 * tau**4 + 6.0 * tau**5 ds = 30.0 * tau**2 - 60.0 * tau**3 + 30.0 * tau**4 dds = 60.0 * tau - 180.0 * tau**2 + 120.0 * tau**3 return y0 + (y1 - y0) * s, (y1 - y0) * ds, (y1 - y0) * dds th0, th1 = 0.2, 0.6 # 变幅角, rad l0, l1 = 3.0, 4.5 # 臂长, m T_man = 4.0 # 动作时间, s这里 tau = (t - t0) / (t1 - t0),归一化时间。五次多项式的系数是固定的,10τ³ - 15τ⁴ + 6τ⁵ 这一段就是典型的“S 型”平滑曲线。有人在工程里图省事用梯形速度规划,伸缩臂停下来明显会回弹,后面避坑章会详细说。
控制器初始化时,两个通道分别设带宽。变幅通道动作慢、惯量大,控制器带宽 ω_c 取 1.5 rad/s;伸缩通道质量集中、响应快,取 2.0 rad/s。比例增益 Kp = ω_c²,微分增益 Kd = 2ω_c,这是二阶系统临界阻尼的标准配置。
m_hat = 0.8 * m J_hat = 0.5 * J Kp_th, Kd_th = 1.5**2, 2.0 * 1.5 Kp_l, Kd_l = 2.0**2, 2.0 * 2.0 w0_th, w0_l = 8.0, 10.0 eso_th = ESO(w0_th, dt) eso_l = ESO(w0_l, dt) use_eso = True # 关掉后就是纯前馈+PD,可做对比3.3 主循环:轨迹生成、扰动估计与力矩合成
主循环里每一拍的顺序很关键:先由当前测量算出不含 ESO 补偿的期望加速度,把它作为 ESO 的已知输入;ESO 更新后得到总扰动估计;再用“期望加速度减去扰动估计”合成真正的加速度指令;最后乘标称惯性矩阵得到力矩。顺序反了会引入一拍延迟,观测器性能会打折扣。
s = np.array([th0, l0, 0.0, 0.0]) err_th_log, ctrl_log = [], [] for i in range(n): tn = t[i] / T_man th_r, thd_r, thdd_r = poly5(min(tn, 1.0), th0, th1) l_r, ld_r, ldd_r = poly5(min(tn, 1.0), l0, l1) th, l, thd, ld = s e_th, ed_th = th_r - th, thd_r - thd e_l, ed_l = l_r - l, ld_r - ld a_des0_th = thdd_r + Kp_th * e_th + Kd_th * ed_th a_des0_l = ldd_r + Kp_l * e_l + Kd_l * ed_l z_th = eso_th.update(th, a_des0_th) z_l = eso_l.update(l, a_des0_l) a_th = a_des0_th - z_th if use_eso else a_des0_th a_l = a_des0_l - z_l if use_eso else a_des0_l M_hat = np.array([[m_hat * l * l + J_hat, 0.0], [0.0, m_hat]]) tau_vec = M_hat @ np.array([a_th, a_l]) d_tau = 8.0 * np.sin(2 * np.pi * 3.0 * t[i]) if 2.0 < t[i] < 3.0 else 0.0 s = s + dt * real_dynamics(s, tau_vec[0], tau_vec[1], d_tau) err_th_log.append(e_th) print("theta RMS err:", round(np.sqrt(np.mean(np.array(err_th_log)**2)), 4))逻辑说明:这段代码完整跑完一个 8 秒的仿真,前 4 秒做轨迹规划,后 4 秒停在目标位置观察残余振荡。2 到 3 秒之间注入了一个幅值 8 N·m 的正弦扰动,模拟负载突变或者液压脉动。ESO 的职责就是把这个扰动和模型误差一起估出来,在力矩指令里抵消掉。
参数说明:a_des0 是“不加补偿的期望加速度”,它同时作为 ESO 的已知输入。ESO 收敛后 z_th 接近真实系统的总扰动,更新后的 a_th 就是带扰动补偿的加速度指令。M_hat 用当前测量的臂长 l 实时更新,因为惯性矩阵对臂长很敏感,用固定值会在伸臂过程中产生明显的模型误差。最后误差存进列表,打印 RMS 值用来做前后对比。
3.4 跑通后的输出怎么看
把 use_eso 分别设成 True 和 False 各跑一次,对比 theta 误差 RMS。按上面的参数,纯 PD 加前馈的 RMS 大概在 0.02 到 0.04 rad 量级,ESO 介入后能降到 0.005 以下。更直观的做法是把误差序列画成曲线,能看到两个现象:扰动注入期间(2~3 秒),纯 PD 的误差有明显正弦波动,ESO 组几乎不受影响;4 秒轨迹结束后,纯 PD 还会有几个来回的余振,ESO 组的误差在一秒内收敛到接近零。
这个最小实验已经能说明问题:抖动抑制的关键不只是“加大增益”,而是把模型不知道的扰动实时估计出来。ESO 在这里像个经验丰富的老操作员,知道臂架实际在受什么力,而不是傻傻地按标称模型推。
4. 参考轨迹与三个带宽参数:整定顺序比参数值更重要
4.1 用五次多项式做参考轨迹:位置连续不够,加加速度也得连续
很多初次接触这个方向的开发者会犯一个错误:参考轨迹直接用一阶滤波或者是梯形速度曲线。伸缩臂的抖动在启动和停止瞬间最剧烈,正是因为加速度在端点处阶跃。加速度阶跃意味着加加速度无穷大,对柔性结构来说相当于给了一个冲击激励。五次多项式能保证加速度连续,加加速度有限,从根源上减少激励能量。
如果臂架的第一阶柔性模态特别低,比如长臂伸展到极限时模态频率低于 1 Hz,五次多项式在端点处的加加速度跳跃也可能激出振动。这时候要上七次多项式,把加加速度也做成连续曲线。七次的系数是 -20τ⁷ + 70τ⁶ - 84τ⁵ + 35τ⁴,起点和终点的位置、速度、加速度、加加速度全部为零。我的经验是:先跑五次,如果停止后的残余振荡幅值仍然超过验收标准,再升级到七次。
轨迹规划的时间 T_man 也不是随便定的。T_man 至少要大于第一阶柔性模态周期的 1.5 倍,否则运动的频谱分量会直接落在模态上。有个快速估算办法:把臂架完全伸出,用质量-弹簧模型算出第一阶固有频率 ω₁,然后取 T_man ≈ 2π / ω₁ × 1.5。
4.2 带宽整定顺序与经验表
参数整定顺序比参数值重要得多,这也是我踩过坑之后总结的顺序。第一步只调参考轨迹,不加 ESO,让纯 PD 控制器去跟踪平滑轨迹,把 Kp、Kd 调到临界阻尼附近;第二步打开 ESO,从很低的观测器带宽(比如 3 rad/s)开始,逐步往上加;第三步才调控制器带宽和观测器带宽的比值。很多人上来就把 ω₀ 调到 30 以上,然后发现系统高频颤振,这不是参数不好,而是顺序错了。
| 参数 | 符号 | 经验范围 | 说明 |
|---|---|---|---|
| 控制器带宽 | ω_c | 0.5 ~ 2.0 rad/s | 变幅通道取低值,伸缩通道取高值 |
| 观测器带宽 | ω₀ | 5 ~ 10 倍 ω_c | 低于噪声转折频率的一半 |
| 轨迹时间 | T_man | ≥ 1.5 倍模态周期 | 动作越快,激励能量越强 |
| 采样周期 | dt | ≤ 1 / (10 ω₀) | 观测器数值稳定的前提 |
观测器带宽与控制器带宽的比值 5~10 倍,这个关系来源于 ESO 的收敛速度必须远快于闭环动态,否则扰动补偿会滞后。但 ω₀ 不能无限加,它受两个约束:传感器噪声的转折频率,以及臂架柔性模态的频率。如果 ω₀ 超过柔性模态频率,ESO 会把柔性振动当作扰动反向补偿,结果越补越抖。
调带宽时的判断标准很简单,看两个信号:控制力矩的高频分量是否明显增大,以及误差信号是否出现拖尾的振荡。力矩信号出现锯齿状高频抖动,说明 ω₀ 已经踩到噪声带了,往回退 20% 再试。
4.3 抖动抑制效果的三个量化指标
只靠“感觉不抖了”没法验收,工程上我一般用三个量化指标评估效果。第一个是跟踪误差 RMS,反映运动过程中的跟随精度;第二个是残余振荡衰减时间,定义是轨迹结束后末端振幅衰减到 5% 峰值所需时间,这是抖动抑制最核心的指标;第三个是控制能量,即力矩平方的积分,防止为了抑抖而牺牲执行器寿命。
三个指标在代码里都容易算。跟踪误差 RMS 用误差序列直接算,残余振荡衰减时间从 4 秒后取误差包络,控制能量对 τ² 做积分。验收标准要按场景定,但可以给一个经验线:ESO 介入后衰减时间如果能缩短到原来的三分之一以下,方向就是对的;如果只是 RMS 降了、衰减时间没变,大概率是轨迹规划的功劳,ESO 还没调到位。
还有一个容易被忽略的指标:稳态误差。伸缩臂靠液压执行器时往往有死区,ESO 的总扰动估计里会把死区等效成恒值扰动并抵消掉,所以稳态误差通常会比纯 PD 小一个数量级。这个指标在仿真里可能看不出来,但台架试验时非常明显。
5. 伸缩臂抖动抑制避坑记录:四个高频翻车现场
5.1 现象:ESO带宽提上去,抖动不减反增
把 ω₀ 从 8 提到 20,本来想加快扰动估计速度,结果末端高频振颤,阀块里传出尖锐的啸叫声。测一下控制力矩信号,能看到明显的锯齿状高频分量。
原因:观测器带宽超过传感器噪声转折频率后,编码器量化噪声和位移传感器的电气噪声被 ESO 当作高频扰动放大,补偿力矩里充满高频成分,反而成了抖动的激励源。
解决:先对传感器信号做功率谱分析,找到噪声拐点。ω₀ 取值不超过噪声拐点频率的一半,在这个前提下再追求扰动估计速度。如果必须提高带宽,就换更高分辨率的传感器或者加相位损失可控的滤波。
5.2 现象:停在目标位置后,臂架还在来回弹
参考轨迹用梯形速度规划,启动停止都很干脆,但每次停下来臂尖都要画三四个来回才稳住,幅度还不小。
原因:加速度在启动和停止瞬间阶跃,jerk 无穷大,柔性结构被冲击激励。再好的反馈控制器也只能等振动自然衰减,除非主动加陷波器去抵消模态。
解决:把参考轨迹改成五次多项式,保证加速度连续。改完之后残余振荡的幅度通常会直接砍掉一半以上。如果还不行,检查臂架第一阶柔性模态频率,把动作时间拉长到模态周期的 1.5 倍以上。
5.3 现象:关节角反馈挺稳,末端测量还在晃
变幅关节的编码器读数非常平稳,误差不到 0.001 rad,但臂架末端的加速度计数据显示振幅超过 ±5 mm,两者互相矛盾。
原因:关节编码器测的是液压缸或减速机输出端的位置,臂架本身存在结构柔性变形,末端的实际位置不等于关节角乘以臂长。反馈位置和抖动位置错位了。
解决:在靠近末端的位置加一个加速度计或者位移传感器,把测量信号引入 ESO。这时候平坦输出的定义也要跟着调整,不能再拿关节角当输出,要用末端位置或末端等效摆角。否则控制器以为已经稳住了,其实稳住的只是关节。
5.4 现象:仿真压得好,台架上一加力就极限环
仿真里 ESO 效果漂亮,误差曲线笔直,一上实机台架,臂架小幅振荡停不下来,像是碰到了某种周期性的开关。
原因:仿真模型里没有实现执行器饱和与死区。ESO 在控制器饱和时仍然按“指令力矩 = 实际力矩”建模,实际力矩已经顶到限幅,ESO 的扰动估计完全失真,闭环系统失去稳定裕度。
解决:在仿真模型末尾加执行器限幅、死区、一阶惯性时延,重新整定参数。更彻底的做法是给控制器加抗饱和逻辑:检测到力矩指令持续超出限幅时,暂停 ESO 的状态更新或者把误差积分项清零。仿真里必须把执行器的“坏脾气”提前模拟出来,否则台架调试就是你血泪经验的来源。
6. 仿真过得了关不算数:上台架前先做这三步验证
6.1 给仿真模型补上执行器的“坏脾气”
很多仿真在理想刚体模型下表现完美,上台架就露馅,问题几乎都出在执行器。我建议在跑任何控制算法之前,先把这三个非线性加进仿真:力矩限幅,范围按执行器能出的最大力写;死区,液压阀芯的覆盖量或者减速机的齿隙都会造成这个;一阶惯性时延,电液执行器的响应不可能瞬时,惯性和时延代表响应带宽。
加上这些之后重新整定一遍参数,整定出来的控制器带宽和观测器带宽才是有意义的。如果你发现加了限幅之后原来的参数无法稳定,你提前发现了实机上 80% 的概率会遇到的坑。
6.2 用振动衰减试验测出可验收的抑制率
台架验证不要先跑完整的动作谱,先做一个最简单的振动衰减试验:给变幅关节一个固定幅值的速度阶跃,让臂架自由振荡,记录末端加速度计信号;然后分别关掉和打开 ESO,测两组衰减曲线。
衰减率的计算很简单,取第一峰和第三峰的幅值比,换算成对数衰减率。ESO 介入后的对数衰减率如果比纯 PD 组提升 50% 以上,说明扰动补偿真正起到了增阻作用。如果提升不明显,问题多半出在观测器带宽不够或者测量位置不对,回到第 5 章的排查思路。
我最早在某车型的伸缩臂上直接跳过了轨迹平滑这一步,只上 ESO,结果高频扰动和柔性模态搞在一起,调试了两周才反过味来。后来养成一个习惯:每次调试都按“先平滑轨迹,再调反馈,最后动观测器”的顺序,先问动作本身会不会闹,再怪控制器没压住。这个顺序本身,比任何一组参数都值钱。希望这个思路也能帮你少走一段弯路,祝顺利。
本文还有配套的精品资源,点击获取