前面聊的控制方法,不管是PID、LQR还是MPC,都有一个隐含假设:机械臂在自由空间中运动,不接触任何东西。
但实际机器人经常要和 environment 打交道。机械臂拧螺丝、擦桌子、装配零件、给人做按摩——这些任务都涉及接触。
接触问题为什么难?因为你不知道环境在哪。位置控制说"手臂伸到x=0.5m的位置",但如果那里有一堵墙,机械臂就会用力撞上去。力控制说"输出10N的力",但如果前面是空的,机械臂就会一直加速伸出去。
传统的做法是位置/力切换——自由空间用位置控制,碰到东西切换到力控制。但切换瞬间会有冲击,控制不连续,体验很差。
阻抗控制换了个思路:不直接控制位置也不直接控制力,而是控制位置和力之间的"关系"——机械臂的动态阻抗特性。
一、阻抗控制的核心思想
阻抗控制的概念是Neville Hogan在1985年提出的。核心思想一句话:把机械臂变成一个"弹簧-阻尼-质量"系统。
目标动态:
M_d * (x_ddot - x_ddot_d) + B_d * (x_dot - x_dot_d) + K_d * (x - x_d) = F_ext
M_d是期望惯性矩阵,B_d是期望阻尼矩阵,K_d是期望刚度矩阵。F_ext是环境施加在机械臂末端的外力。
这个方程的物理意义:机械臂末端和环境之间的力F_ext,通过一个"虚拟弹簧-阻尼-质量"系统来响应。你推机械臂,它像弹簧一样退让;你松手,它像弹簧一样回弹。
说白了,阻抗控制不是让机械臂"硬"地到达某个位置或输出某个力,而是让机械臂"软"地和环境互动——像一个有弹性的系统。
二、阻抗 vs 导纳:两种实现路径
阻抗控制和导纳控制是一对"对偶"方案:
阻抗控制:输入是位置误差,输出是力。控制器模拟一个"阻抗"(力/速度),让机械臂表现出期望的力学特性。适合刚性执行器(电机+减速器)。
导纳控制:输入是力,输出是位置/加速度。控制器模拟一个"导纳"(速度/力),根据测到的外力生成运动。适合柔性执行器或者力传感器精度高的场景。
两者的区别:阻抗控制"用力来调位置",导纳控制"用位置来响应力"。
实际工程中,导纳控制用得更多。原因很简单:导纳控制的输入是力传感器信号,输出是位置修正量,实现起来更直观。你只需要在位置控制器的基础上加一个"力反馈修正"就行。
之前做协作机器人拖拽示教的团队,用的就是导纳控制。用户抓住机械臂末端拖动,六维力传感器测到外力,导纳控制器把这个力转换成期望的运动,位置控制器跟踪这个运动。整个过程流畅自然,就像在推一个没有重量的东西。
三、刚度、阻尼、惯性的选择
阻抗控制的三个参数——刚度K_d、阻尼B_d、惯性M_d——直接决定了机械臂和环境交互时的"手感"。
刚度K_d:机械臂对外力位移的抵抗程度。K_d大,你推一下机械臂只动一点点(硬);K_d小,轻轻一推就动很多(软)。装配任务需要低刚度(柔顺),打磨任务需要高刚度(精确)。
阻尼B_d:机械臂运动的"粘滞"程度。B_d大,机械臂移动时感觉像在蜂蜜里(慢慢来);B_d小,机械臂移动时感觉像在空气中(轻快)。阻尼影响系统的振荡特性——阻尼不够会振荡,阻尼太大响应太慢。
惯性M_d:机械臂对外力加速的抵抗程度。M_d大,你推一下感觉推不动(重);M_d小,轻轻一推就加速(轻)。
工程上最常用的简化:M_d = 单位矩阵(或者忽略),只调K_d和B_d。这样阻抗方程退化成:
B_d * (x_dot - x_dot_d) + K_d * (x - x_d) = F_ext
一阶系统,更简单更稳定。很多协作机器人的拖拽示教就是这么实现的。
临界阻尼设计:B_d = 2 * sqrt(K_d * M_d)。这个关系让系统不产生振荡,响应速度也最快。和PID调参中"临界阻尼"的思路一样。
四、代码示例:导纳控制实现
import numpy as np class AdmittanceController: """导纳控制器:力 -> 运动""" def __init__(self, M_d, B_d, K_d, dt=0.001): self.M_d = M_d # 期望惯性 self.B_d = B_d # 期望阻尼 self.K_d = K_d # 期望刚度 self.dt = dt # 状态 self.x = np.zeros(3) # 修正位置 self.x_dot = np.zeros(3) # 修正速度 def update(self, F_ext, x_d_current): """ F_ext: 六维力传感器测到的外力 [fx, fy, fz] x_d_current: 当前期望位置(来自轨迹规划) 返回: 修正后的期望位置 """ # 导纳关系: M_d * x_ddot + B_d * x_dot + K_d * x = F_ext # 解出加速度 x_ddot = np.linalg.inv(self.M_d) @ ( F_ext - self.B_d @ self.x_dot - self.K_d @ self.x ) # 积分更新 self.x_dot += x_ddot * self.dt self.x += self.x_dot * self.dt # 返回修正后的期望位置 return x_d_current + self.x # 使用示例 ctrl = AdmittanceController( M_d=np.eye(3) * 5.0, # 5kg虚拟质量 B_d=np.eye(3) * 50.0, # 临界阻尼 K_d=np.eye(3) * 200.0, # 刚度200 N/m dt=0.001 ) # 仿真:用户施加一个恒力10N沿x方向 F_ext = np.array([10.0, 0, 0]) x_d = np.array([0.5, 0.0, 0.3]) # 原始期望位置 for i in range(1000): x_new = ctrl.update(F_ext, x_d) if i % 200 == 0: print(f"t={i*0.001:.1f}s: 修正位置 = {x_new}") # 机械臂会沿x方向偏移,最终稳定在 F/K = 10/200 = 0.05m 处这段代码实现了一个简单的导纳控制器。力传感器测到外力后,导纳控制器算出"虚拟质量-弹簧-阻尼"系统的响应,生成修正后的期望位置。位置控制器(PID)负责跟踪这个修正位置。
五、面试实战
Q:阻抗控制和力控制有什么区别?A:力控制直接目标力("输出10N"),阻抗控制目标力和位置的关系("像弹簧一样响应")。阻抗控制更灵活——自由空间时力为零,机械臂自然跟随;接触时力增大,机械臂柔顺退让。不需要切换模式。
Q:阻抗控制和位置控制能结合吗?A:能,而且通常就是这么用的。阻抗控制器输出修正后的期望位置,位置控制器(PID)跟踪这个位置。阻抗控制是"上层策略",位置控制是"底层执行"。
Q:协作机器人的"柔顺"是怎么实现的?A:核心就是阻抗/导纳控制。力传感器测到外力,导纳控制器生成柔顺运动。刚度参数K_d决定了"多柔"——K_d越小越柔顺。拖拽示教时K_d设得很低,精密装配时K_d设得高一些。
Q:阻抗控制怎么保证稳定性?A:可以用Lyapunov方法证明。选V = 0.5 * x_dot^T * M_d * x_dot + 0.5 * x^T * K_d * x(动能+势能),证明V_dot <= 0。关键条件是M_d正定、B_d正定、K_d正定。如果环境刚度已知,还可以做更精细的稳定性分析。
讲真,阻抗控制在协作机器人和医疗机器人领域是必考知识点。面试时你能讲清楚阻抗参数的物理意义和调参方法,基本就过关了。
小结
阻抗控制的核心思想:不直接控制位置或力,而是控制两者之间的动态关系——把机械臂变成一个可调的"弹簧-阻尼-质量"系统。
三个参数决定"手感":刚度K_d(软硬)、阻尼B_d(粘滞)、惯性M_d(轻重)。工程上常用导纳控制实现——力传感器测外力,导纳控制器生成修正位置,位置控制器跟踪。
阻抗控制让机器人从"刚性执行者"变成了"柔顺合作者"。这也是协作机器人能和人安全共事的理论基础。
控制理论系列到这里告一段落。从PID到最优控制、从线性到非线性、从自由空间到接触交互,基本覆盖了机器人控制的核心知识点。后续我们会进入感知和规划领域。
如果这篇文章对你有帮助,欢迎点赞、在看、转发三连。 你的支持是我持续更新的最大动力。
「机器人软件开发面试·从入门到精通」连载系列
上一篇:第200篇 滑模控制——对扰动"免疫"的强鲁棒方案
下一篇预告:第202篇 导纳控制——力控场景的另一种实现思路
有任何问题欢迎评论区留言,我会尽量回复。