导纳控制原理:导纳控制与阻抗控制的对偶关系、导纳控制器的离散化实现
各位工程师朋友,今天我们来聊聊导纳控制。说实话,这个知识点在机器人柔顺控制里,属于那种「看着简单,用起来容易翻车」的类型。我最早接触导纳控制是在做力控打磨项目的时候,当时被阻抗控制和导纳控制的关系绕得有点晕,后来才慢慢理清楚——它们俩其实就是一枚硬币的两面。
一、导纳控制与阻抗控制:一对天生的搭档
先说说这两个概念的本质区别。你想想看,阻抗控制描述的是「位置偏差产生力」——机器人碰到障碍物,位置被推偏了,就会产生一个反作用力。而导纳控制正好反过来,它是「力输入产生位置变化」——你推它,它就动。
用数学语言来说:
- 阻抗控制:F = M·Δẍ + B·Δẋ + K·Δx(力是位置偏差的函数)
- 导纳控制:Δx = G(s)·F(位置变化是力的函数)
说白了,阻抗控制是「硬碰硬」的思路,导纳控制是「以柔克刚」的思路。我在做机器人拖动示教的时候,就发现导纳控制特别适合这种场景——操作者施加力,机器人跟着走,感觉就像在推一个带阻尼的滑块。
核心理解:阻抗控制适用于机器人主动对环境施加力(比如打磨、装配),导纳控制适用于机器人被动响应外力(比如人机协作、拖动示教)。两者在频域上是互为倒数的关系——导纳 = 1 / 阻抗。
二、导纳控制器的数学模型
标准的导纳控制模型长这样:
M_d · (ẍ_c - ẍ_r) + B_d · (ẋ_c - ẋ_r) + K_d · (x_c - x_r) = F_ext
其中:
- M_d、B_d、K_d 是期望的惯量、阻尼、刚度
- x_c 是实际位置,x_r 是参考位置
- F_ext 是外部施加的力
嗯,这里要注意:我们通常把参考轨迹设为零,只考虑外力引起的修正量。这样公式就简化成:
M_d · Δẍ + B_d · Δẋ + K_d · Δx = F_ext
这个二阶系统,说白了就是一个「质量-弹簧-阻尼」模型。你施加一个力,系统就会按照你设定的惯量、阻尼、刚度来响应。我习惯把这三个参数叫做「手感三要素」——调好了,机器人就像在黄油里滑动;调不好,要么太硬像撞墙,要么太软像在果冻里游泳。
三、导纳控制器的离散化实现
连续域的理论很漂亮,但到了嵌入式控制器里,我们得把它变成离散的。我个人习惯用双线性变换法(Tustin变换),因为它稳定性好,而且没有频率扭曲的问题。
离散化的步骤是这样的:
- 把连续域的传递函数写出来:G(s) = 1 / (M_d·s² + B_d·s + K_d)
- 用双线性变换 s = (2/T) · (z-1)/(z+1) 代入
- 整理成差分方程的形式
直接给代码吧,这是我项目里用过的实现:
// 导纳控制器离散化实现(C语言)
typedef struct {
float M_d; // 期望惯量
float B_d; // 期望阻尼
float K_d; // 期望刚度
float T; // 控制周期
float x_prev; // x(k-1)
float x_prev2; // x(k-2)
float f_prev; // f(k-1)
float f_prev2; // f(k-2)
float a0, a1, a2; // 分子系数
float b1, b2; // 分母系数
} AdmittanceController;
void Admittance_Init(AdmittanceController* ctrl) {
float T = ctrl->T;
float M = ctrl->M_d;
float B = ctrl->B_d;
float K = ctrl->K_d;
// 双线性变换后的系数
float den = 4*M + 2*B*T + K*T*T;
ctrl->a0 = (T*T) / den;
ctrl->a1 = (2*T*T) / den;
ctrl->a2 = (T*T) / den;
ctrl->b1 = (2*K*T*T - 8*M) / den;
ctrl->b2 = (4*M - 2*B*T + K*T*T) / den;
}
float Admittance_Update(AdmittanceController* ctrl, float f_ext) {
// 差分方程:x(k) = a0*f(k) + a1*f(k-1) + a2*f(k-2) - b1*x(k-1) - b2*x(k-2)
float x = ctrl->a0 * f_ext
+ ctrl->a1 * ctrl->f_prev
+ ctrl->a2 * ctrl->f_prev2
- ctrl->b1 * ctrl->x_prev
- ctrl->b2 * ctrl->x_prev2;
// 更新状态
ctrl->f_prev2 = ctrl->f_prev;
ctrl->f_prev = f_ext;
ctrl->x_prev2 = ctrl->x_prev;
ctrl->x_prev = x;
return x;
}
避坑指南:我曾经在采样周期不匹配的情况下直接套用这个公式,结果系统震荡得一塌糊涂。后来发现,控制周期T必须和实际采样周期严格一致,差1ms都不行。建议用定时器中断来保证时序。
四、参数整定的实战经验
参数怎么调?我总结了一个「三步法」:
| 参数 | 作用 | 调大效果 | 调小效果 |
|---|---|---|---|
| M_d(惯量) | 决定响应速度 | 响应变慢,更平滑 | 响应变快,容易抖动 |
| B_d(阻尼) | 决定能量耗散 | 运动更「黏」,停止快 | 运动更「滑」,容易超调 |
| K_d(刚度) | 决定位置恢复力 | 像弹簧,回中力大 | 像软糖,回中力小 |
我的习惯是:先固定K_d为0(纯阻尼模式),调B_d让系统不震荡,再加M_d让响应变柔和,最后加一点点K_d防止漂移。你想想看,这就像调音响——先调音量,再调均衡,最后微调。
五、导纳控制与阻抗控制的对偶关系图解
下面这张图是我自己画的,帮你直观理解两者的关系:
注意:实际应用中,导纳控制和阻抗控制的选择不是绝对的。我见过有人把两者混合使用——内环用阻抗控制保证稳定性,外环用导纳控制实现柔顺。但这样做调试难度会翻倍,新手不建议尝试。
六、离散化实现的常见坑
最后说说我踩过的几个坑:
- 数值溢出:当K_d接近0时,分母会变得很小,导致系数爆炸。我习惯给K_d设一个下限,比如0.1。
- 初始状态:启动时力传感器有零偏,如果不做补偿,机器人会自己跑起来。记得做力传感器零点校准。
- 积分饱和:虽然导纳控制器本身没有积分项,但位置修正量累积起来也会很大。我一般会加一个限幅,防止修正量超出安全范围。
嗯,导纳控制这块内容就讲到这里。说白了,它就是让机器人学会「顺势而为」——你推它,它让路;你不推,它停住。这个思路在协作机器人上特别实用,也是实现安全人机交互的基础。
公众号:蓝海资料掘金营,微信deep3321