前阵子帮朋友看一台六轴协作臂的低速轨迹精度,问题很典型:末端走直线的时候,在关节换向的位置会冒出一个肉眼可见的小台阶,测下来跟随误差尖峰能到0.08毫米,速度越低越明显。机械那边查了背隙、查了装配同轴度,也怀疑过编码器,全都没问题。最后把关节电流指令和速度曲线拉到一张图上对比,问题就露馅了——换向瞬间摩擦力矩的方向跟着速度一起反向,可控制器给的力矩指令完全没有跟着变,于是关节在那一瞬间被"卡"了一下。这就是机器人运动控制里绕不开的一块硬骨头:摩擦力矩补偿。
说它硬,不是因为它高深,而是因为它琐碎。摩擦这个东西,理论上写出来就一行公式,落地的时候却要跟减速器型号、润滑脂温度、负载惯量、控制周期、电流环带宽全都打一遍交道。做过机器人运动控制的人大概都有体会:位置环调得再漂亮,低速段那一小段抖动和台阶,往往就卡在摩擦上。这篇就按我自己的实操顺序,把摩擦力矩补偿从建模、辨识、实现到排查整条链路摊开来聊,既讲清楚每一步为什么这么干,也把踩过的坑和实测数据一并放出来。不管你是做工业臂、协作臂、四足关节,还是自己搭个小机械臂玩,这套思路都能直接抄。
1. 先把摩擦力矩这件事说透
1.1 关节上的摩擦力矩到底从哪冒出来的
很多人一提摩擦就想到"两个金属面相互滑动",这在机器人关节里其实只占一小部分。真实关节的阻力矩来源相当杂,我一般把它拆成几大块来看。第一块是减速器本体,谐波减速器里柔轮和刚轮齿面啮合时有滑动摩擦,波发生器上的柔性轴承滚道也有滚动和微滑,这部分在高预紧状态下贡献非常明显。第二块是支撑轴承,交叉滚子轴承、角接触轴承在轴向和径向载荷下都会产生阻力矩,预紧力越大阻力越大,而且跟速度、温度都有关系。第三块是润滑脂,脂的黏性剪切在高速段贡献大,温度低的时候尤其明显,冬天刚上电和跑热了之后完全是两个手感。第四块是密封圈和线束,密封唇口的压紧力、拖链和线缆的弯折阻力,看着不起眼,但对小力矩关节来说能占到10%以上。
还有一个特别容易被误当成摩擦的东西:电机的齿槽转矩。它跟摩擦的表现很像,都是周期性、位置相关的阻力,但补偿方式完全不同——齿槽转矩是位置函数,摩擦主要是速度函数。如果你把齿槽转矩当成摩擦去拟合,参数会拟合得很离谱,低速段的补偿曲线会一圈一个样。我在第一次做关节辨识的时候就吃过这个亏,后来学乖了,辨识摩擦之前先做一次位置相关的转矩扫描,把齿槽项单独剔出来。
量级上心里要有数:典型工业关节的摩擦力矩大概占额定力矩的5%到20%,小关节、高减速比、大预紧的能到25%以上。低速段库仑项占绝对主导,速度降到1度每秒以下时,黏滞项几乎可以忽略,这时候模型里少一项多一项影响不大,符号判断准不准才是关键。而到了额定的中高速段,黏滞项和风阻项就起来了,尤其是大臂的肩关节,速度上去以后黏滞系数的影响非常直观。
1.2 不补偿会怎样:四个典型症状
摩擦力矩不补偿,问题不是"精度差一点"这么简单,它会以几种很具体的形式冒出来,认准这几种症状,你基本就能反推出是不是摩擦的锅。
第一种是象限突起,英文文献里叫 quadrant glitch。表现是在关节换向或者经过速度零点的时候,位置误差曲线上会出现一个尖峰,形状像台阶。原因很简单:速度过零时摩擦力矩方向要翻转,但翻转不是瞬时完成的,中间有个预滑动过渡区,控制器在这个区间里输出力矩不匹配,误差就堆起来了。数控机床和机器人上这个问题一模一样,做直线插补时末端轨迹会凸出去一小块。
第二种是低速爬行,stick-slip。关节在极低速下发出一顿一顿的运动,像老式缝纫机。本质上是静摩擦和动摩擦之间的差值造成的:卡住→蓄力→突然滑动→再卡住,循环往复。你越想低速走准,它越明显,因为低速时库仑项相对惯性和黏滞项占比最大。
第三种是跟随误差不对称。正向走和反向走的误差不一样,同一个轨迹来回跑两遍,误差曲线对不上。这一般是摩擦力矩的方向依赖导致的,衬套、密封件这类部件最典型。
第四种是力控手感差。做拖动示教或者柔顺控制的时候,操作者手上的感觉是"要多大给多大、给多了又窜",接触力控制会跳动,接触力波动能有几牛甚至十几牛。摩擦把外力估计的精度直接吃掉了,力矩传感器测到的值里混进了关节摩擦。
把这四种症状记住,现场排查的时候能省很多时间。我后来的习惯是:只要看到换向处有尖峰、低速有爬行、正反向不对称,第一反应就是摩擦,先做一次辨识再说,别急着调位置环的PID。
1.3 补偿在整条控制链路里的位置
讲到实现,得先把补偿放在哪里说清楚。一个典型的关节伺服链路是:位置指令→位置环→速度环→电流环→电机。摩擦力矩补偿属于前馈,它的输入是关节速度和位置,输出是一个力矩(或者折算成电流)指令,直接叠加到电流环或者速度环的力矩指令上。
按实现复杂度,我把补偿方案分成三档。第一档是基于模型的前馈补偿,离线辨识出一组固定参数,在线按速度查表或者实时计算,简单、可解释、调试快,绝大多数工业场景够用。第二档是基于观测器的在线补偿,用扰动观测器把包括摩擦在内的所有未建模扰动一起估出来再反向叠加,好处是不用精确知道摩擦模型,坏处是观测器带宽有限,低速段和换向瞬间跟不上,而且会把其他扰动也一起补进去,可能引入低频振荡。第三档是自适应或者学习型补偿,用迭代学习控制、神经网络、高斯过程之类的在线调整参数,能适应温度漂移和磨损,但需要数据、需要算力,调试周期长。
我的建议是:除非你的场景对低速精度和力控有极端要求,否则优先把第一档做好做扎实。我见过太多项目一上来就上观测器和神经网络,结果基础模型都没辨识准,叠了一层又一层,最后连是谁在抖都说不清。这一档做好,低速精度和力控手感已经能解决八成的实际问题。
2. 摩擦力矩模型怎么选:从库仑到 LuGre
2.1 静态模型:库仑项、黏滞项和 Stribeck 项
工程上用得最多的还是静态模型,写出来是这样:
$$\tau_f = \tau_c \cdot \text{sgn}(\omega) + b \cdot \omega + \tau_s \cdot \text{sgn}(\omega) \cdot e^{-(\omega/\omega_s)^2}$$
四个参数,物理意义都很清楚。τc 是库仑摩擦,也就是速度趋于零时模型中剩下的那部分,它决定了低速段补偿的量级。b 是黏滞摩擦系数,跟速度成正比,高速段主要靠它。τs 是 Stribeck 项幅值,描述从静摩擦向动摩擦过渡时那段"下滑"的阻力,ωs 是 Stribeck 速度,一般在0.01到0.1弧度每秒这个区间,决定了过渡区的宽度。
这条 Stribeck 曲线是理解摩擦行为的核心。当速度从零开始增大,摩擦力矩先是从静摩擦值快速下降,到 ωs 附近降到最低,然后黏滞项开始起作用缓慢上升。那条"下凹"的谷底就是 stick-slip 的根源。你在辨识的时候如果发现拟合曲线在这个位置对不上,多半就是 ωs 选得不对,或者数据里低速段的采样点太稀疏。
符号项 sgn 是绕不过去的一个坑。它在零点是跳变的、不可导的,直接写进控制器,在速度穿越零点的时候输出会瞬间从正的最大值跳到负的最大值,物理上不可能实现,数字上就是一次阶跃,容易激起机械谐振。工程上有三种处理方式:一是用 tanh(ω/ε) 或者 saturating 函数去近似,把跳变变成一段斜率很大的过渡,ε 取1e-3到1e-2弧度每秒;二是设一个速度死区,|ω| 小于阈值时输出零,避免在零点附近来回抖;三是用双曲正切加时间常数滤波,让补偿本身也有惯性。我更推荐第一种加第三种,先用 tanh 平滑,再给这个补偿项过一个一阶低通,截止频率取位置环带宽的3到5倍,这样既保留了方向信息,又不会在换向时引发冲击。
2.2 动态模型:LuGre 与 GMS
如果场景要求更高,比如力控接触、极低速轨迹跟踪,静态模型就有点不够用了,因为它描述不了预滑动位移和滞后环——也就是在速度还没真正建立起来之前,接触点已经在微观上发生了弹性变形,这时候摩擦是位移的函数而不是速度的函数。
LuGre 模型是这一类的代表。它引入一个内部状态 z 表示鬃毛的平均变形量:
$$\dot{z} = \omega - \frac{|\omega| \cdot z}{g(\omega)}, \quad g(\omega) = \frac{\tau_c + (\tau_s - \tau_c) \cdot e^{-(\omega/\omega_s)^2}}{\sigma_0}$$
$$\tau_f = \sigma_0 z + \sigma_1 \dot{z} + \sigma_2 \omega$$
σ0 是鬃毛刚度,σ1 是微观阻尼,σ2 是黏滞系数。它能自然描述预滑动位移、Stribeck 效应和摩擦滞后,理论上是比静态模型更完整的。
但 LuGre 在工程上落地有两个坎。第一是状态 z 不可观测,只能靠观测器估,而 z 的估计对采样噪声非常敏感,微小的速度噪声会被放大成一个来回抖的摩擦估计。第二是数值刚性,σ0 通常很大(几千到几万牛米每弧度),用显式欧拉积分在1毫秒的控制周期里很容易发散,要么改小步长要么换隐式积分,都增加算力负担。GMS 模型是 LuGre 的推广,用一组状态表示多个鬃毛簇,表达能力更强,参数也更多,辨识难度进一步上升。
我的实际选择是:静态模型打底,在低速区叠加一个预滑动项来近似 LuGre 的效果,而不是完整实现 LuGre。具体做法是设一个很小的位移阈值,当关节处于换向过渡区、速度很低但力矩已经反向时,用一个基于位置误差的弹性项来代替符号项,输出连续变化,过了阈值再切回静态模型。这样做计算量小、数值稳定,低速台阶能压掉七七八八,性价比最高。
2.3 一张表看清怎么选
| 模型 | 参数量 | 低速表现 | 换向过渡 | 计算开销 | 辨识难度 | 适合场景 |
|---|---|---|---|---|---|---|
| 库仑 + 黏滞 | 2 | 一般 | 会跳变 | 极低 | 很低 | 快速上线、精度要求不高 |
| 库仑 + 黏滞 + Stribeck | 4 | 好 | 用 tanh 平滑后可接受 | 低 | 低 | 大部分工业关节,首选 |
| 静态 + 预滑动项 | 5~6 | 很好 | 连续,无跳变 | 低 | 中 | 低速轨迹跟踪、协作臂 |
| LuGre | 6 | 很好 | 连续,有滞后 | 高 | 高 | 高精度力控、科研 |
| GMS 多状态 | 7+ | 很好 | 连续 | 高 | 很高 | 学术研究、专项验证 |
| 数据驱动 | 视结构 | 好 | 取决于训练 | 很高 | 高 | 数据充足、模型难建 |
表格里"参数量"这一列是给你做参数敏感性分析的参考:参数越少,你越容易在有限实验里把每个参数都标定准;参数越多,模型越能拟合细节,但也越容易过拟合到某一次实验的工况上,换个温度、换个负载就失效。我见过有人用神经网络拟合出漂亮到离谱的曲线,结果换个工件重量,补偿效果直接崩掉,原因就是训练数据里没有覆盖负载变化。
3. 参数辨识实操:把四个参数从数据里抠出来
3.1 数据采集:需要哪些信号,怎么取
辨识摩擦最核心的公式其实是一句话:恒速运动时,关节的电机力矩减去重力项和惯性项,剩下的就是摩擦力矩。
$$\tau_f(\omega) = \tau_{motor} \cdot N \cdot \eta - \tau_{gravity}(q) - \tau_{inertia}$$
所以要采集的信号很明确:关节位置 q、关节速度 ω、电机电流 i、以及一套能算出重力项的运动学模型。电流到力矩的换算是 τ_motor = Kt · i,Kt 是电机的转矩常数,从电机手册拿;N 是减速比,η 是减速器效率,这两个也都在手册里。
这里有几个实操细节必须提醒。第一,电流采样要有实际值,不能只用指令值,因为电流环有跟踪误差,尤其在换向瞬间指令和实际差得比较多,用指令值辨识会把电流环的动态混进摩擦参数里。第二,速度信号尽量用编码器差分加滤波,别用控制器内部已经经过速度环滤波的值,那个值在低速段被压得很厉害,会把真实摩擦曲线抹平。第三,采样率至少是控制周期的2到5倍,我一般用1千赫兹到5千赫兹,低速实验时甚至更高,因为低速段的曲线最陡。第四,关节要用抱闸或者机械限位锁住,或者让它在水平面内往复运动把重力项消掉,这样能少一层误差来源。
还有一个容易被忽略的点:标定前让机器人空跑十分钟以上,让润滑脂和电机温度稳定下来。我第一次做实验,冷机状态下辨识的库仑项比热机高了将近30%,导致跑一段时间后过补偿,关节开始微微抖,回头找原因花了一整天才意识到是温度。
3.2 恒速跟踪实验:最靠谱的静摩擦曲线获取方法
辨识流程我一般这么做。把单个关节单独拿出来(其他关节锁死),让它以一系列恒定速度正反两个方向转动。速度点怎么取?在低速段密集取,因为曲线在那里变化最快,我一般取0.002、0.005、0.01、0.02、0.05、0.1、0.2、0.5、1.0弧度每秒,往上一路到额定速度,每个方向每个速度点跑够5秒以上取稳态均值。
每个速度点稳定后,记录电流均值和实际速度均值,代入上面的公式算出摩擦力矩。正反两个方向分别算,因为方向不同摩擦可能不对称,别偷懒只做一边然后取绝对值。
数据采完之后你会得到一张"摩擦力矩-速度"散点图。理想情况下它应该是一条:低速段有一个从静摩擦值跌落的小凹、然后随速度缓慢上升的曲线。如果画出来是一条水平线或者乱七八糟的点云,先别急着拟合,回去查三个地方:电流传感器零点有没有漂、关节是不是真的锁住了、速度均值是不是被滤波滤没了。
3.3 最小二乘拟合:代码可以直接抄
拟合用非线性最小二乘就行,参数初值按经验给,后面让它自己收敛。下面这段是我常用的辨识脚本,稍微改改就能用:
import numpy as np from scipy.optimize import least_squares # w: 关节速度 (rad/s),tau: 实测摩擦力矩 (Nm) w = np.array([...]) tau = np.array([...]) def friction_residual(params, w, tau): tc, ts, ws, b = params # 用 tanh 近似符号函数,避免零点不可导 eps = 1e-3 s = np.tanh(w / eps) tau_pred = tc * s + ts * s * np.exp(-(w / ws) ** 2) + b * w return tau_pred - tau # 初值:库仑0.5,Stribeck幅值0.3,Stribeck速度0.05,黏滞0.02 p0 = [0.5, 0.3, 0.05, 0.02] # 加边界约束,防止参数跑到物理上不可能的区域 lb = [0.0, 0.0, 1e-4, 0.0] ub = [5.0, 5.0, 1.0, 1.0] res = least_squares(friction_residual, p0, args=(w, tau), bounds=(lb, ub), method='trf', loss='soft_l1', f_scale=0.05) tc, ts, ws, b = res.x print(f"库仑={tc:.4f} Nm Stribeck幅值={ts:.4f} Nm " f"Stribeck速度={ws:.5f} rad/s 黏滞={b:.5f} Nm·s/rad")这段代码里有两个细节值得说。一是 loss='soft_l1',用软 L1 损失代替默认的平方损失,目的是抗离群点。实测数据里总会有几个点因为换向、电流尖峰或者采样抖动偏得离谱,平方损失会被它们带跑,软 L1 影响就小很多。二是参数边界不能省,尤其 ws 如果让它自由跑,很容易收敛到一个物理上不合理的大值,拟合残差看着不错,实际运行完全不对——相当于用黏滞项去硬凑低速段的斜率,速度一变化就露馅。
拟合完一定要做残差分析,不要只看总的残差平方和。把残差按速度段画出来:如果低速段残差系统性偏正、高速段偏负,说明模型结构不对;如果残差在零点附近集中变大,说明符号项的处理有问题。我看过太多人只看一个总误差就拍板,结果上线才发现低速段完全没补上。
3.4 温度、负载和磨损的修正
参数标完不是一劳永逸。摩擦对温度非常敏感,减速器脂温从10度升到50度,库仑项能掉20%到35%,黏滞项的变化更复杂一些,先降后升。如果对精度有要求,可以做两件事。
一是加温度修正项,把库仑项写成 τc(T) = τc0 · [1 + kT · (T - T0)],kT 靠标定几组温度点拟合出来,温度用电机温度传感器或者关节处的温度传感器读。二是做分段参数表,按温度区间存几组参数,在线插值切换。我一般用第一种,简单够用;只有在温度跨度特别大、精度要求特别高的场合(比如需要在低温环境下立即精确定位的设备)才上第二种。
负载的影响主要体现在黏滞项和轴承阻力上,负载越大,轴承滚道阻力越大,黏滞系数会上升。这个可以在辨识时用不同末端负载各做一遍,标一个负载相关系数。不过说实话,大多数场景下负载对摩擦的影响远小于温度,优先把温度搞定。
磨损是长期问题,减速器跑几千小时之后摩擦特性会变,尤其是预紧力衰减。工程上的做法是定期重辨识,或者在控制器里加一个在线估计算法做缓慢自适应,把长期漂移吃掉。如果你做的是产线上的设备,建议把重辨识写进定期维护流程,比什么都可靠。
4. 补偿怎么加到控制器里
4.1 前馈补偿的三种注入点
补偿量算出来之后,加到哪一层,效果差别很大。加到力矩指令层是最常见的做法,也就是在动力学前馈的出力上直接叠加摩擦项,然后一起经过电流环。这种做法的好处是物理意义清晰,补偿量就是力矩,跟重力补偿、惯量前馈的形式统一,好维护。
第二种是加到电流指令层,也就是把摩擦项折算成电流,在电流环的输入上叠加。这么做的好处是绕过了部分动力学计算的时延,响应更快,但要小心电流环的增益——如果电流环的积分比较强,你加进去的前馈会在积分器里被"抵消"一部分,实际效果比预期弱。折算关系是 i_ff = τ_f / (Kt · N · η),注意 η 放在分母。
第三种是加到速度环输出,本质上是给速度环一个前馈偏置。这种用法比较少,一般出现在老式控制器没有力矩前馈接口的时候,属于变通方案,不推荐。
我自己的习惯是加在力矩指令层,和重力、惯量前馈统一走一条通道,然后用一次标定过的力矩到电流的换算统一进电流环。这样整条前馈链路只有一个换算点,出错好定位。
4.2 减速比和效率换算:一个完整的计算例子
这部分容易被忽略,但出错的人特别多,我拿一个真实的关节参数走一遍。假设某关节电机转矩常数 Kt = 0.12 牛米每安培,减速比 N = 100,谐波减速器效率 η = 0.75,辨识出的库仑摩擦(折算到关节输出端)τc = 2.4 牛米。
第一步,把关节输出端的摩擦折算到电机端:
τc_motor = τc / (N · η) = 2.4 / (100 × 0.75) = 0.032 牛米
第二步,折算成电机电流:
i_c = τc_motor / Kt = 0.032 / 0.12 = 0.267 安培
也就是说,要在关节输出端产生2.4牛米的库仑摩擦补偿,电机侧只需要额外给0.267安培的指令电流。这个数看起来小,但对于额定电流几安的电机来说,占比并不低,低速段它几乎决定了关节能不能动起来。
再算黏滞项。设辨识出的黏滞系数(关节端)b = 0.8 牛米·秒每弧度,关节速度 ω = 0.5 弧度每秒:
τ_visc = 0.8 × 0.5 = 0.4 牛米(关节端) i_b = 0.4 / (100 × 0.75 × 0.12) = 0.044 安培
这里有个关键点必须强调:效率 η 只在摩擦折算里出现,因为摩擦是"阻力",它经过减速器时的传递关系和其他力矩一样。但重力项和惯量项的前馈计算里,η 的处理要看你补的是哪一侧的量。如果你在关节端建模、要在电机端出力,也要除以 η;如果你直接在关节端算、通过动力学接口下发,就不需要再除。混着算是最常见的错误来源,我见过的"补偿量差了百分之七八十"基本都是这个原因。
还有一点,正反向效率可能不一样。蜗轮蜗杆这类自锁传动,反向效率远低于正向,甚至为零。谐波减速器相对对称,但也不是完全一致。如果你的关节有方向性的效率差异,辨识的时候正反分开标,别用一个 η 通吃。
4.3 一个可以直接用的补偿函数
下面这段 C++ 是我在实际控制器里用的摩擦补偿函数,控制周期1毫秒,运行在实时线程里,计算量很小:
struct FrictionParams { double coulomb; // 关节端库仑摩擦 Nm double stribeck; // 关节端 Stribeck 幅值 Nm double stribeck_vel; // Stribeck 速度 rad/s double viscous; // 关节端黏滞系数 Nm·s/rad double kt; // 电机转矩常数 Nm/A double ratio; // 减速比 double eff; // 减速器效率 double tanh_eps; // 符号平滑系数 rad/s double lpf_alpha; // 一阶低通系数 0~1 }; class FrictionCompensator { public: explicit FrictionCompensator(const FrictionParams& p) : p_(p), filtered_(0.0) {} // 输入:关节速度 rad/s;输出:应叠加到电机力矩指令上的补偿量 Nm double compute(double omega) { const double s = std::tanh(omega / p_.tanh_eps); const double stribeck_term = p_.stribeck * s * std::exp(-(omega / p_.stribeck_vel) * (omega / p_.stribeck_vel)); const double tau_joint = p_.coulomb * s + stribeck_term + p_.viscous * omega; // 折算到电机端 const double tau_motor = tau_joint / (p_.ratio * p_.eff); // 一阶低通,抑制换向瞬间的补偿过冲 filtered_ = p_.lpf_alpha * tau_motor + (1.0 - p_.lpf_alpha) * filtered_; return filtered_; } private: FrictionParams p_; double filtered_; };这里有几个参数需要现场调。tanh_eps 决定符号过渡的陡峭程度,取太小(比如1e-5)就接近理想符号函数,换向会有冲击;取太大(比如0.1)会让低速段的补偿量被过度削弱,压不住爬行。我一般在1e-3到1e-2弧度每秒之间试,从1e-3开始往上加,加到换向不抖为止。
lpf_alpha 是低通系数,alpha 越小滤波越强、延迟越大。计算方式可以按截止频率推:alpha = 1 - exp(-2π · fc · Ts),Ts 是控制周期。fc 取位置环带宽的3到5倍比较稳,比如位置环带宽20赫兹,fc 取80赫兹,Ts 是1毫秒,alpha ≈ 1 - exp(-0.5) ≈ 0.39。这个值别拍脑袋,算一次心里有底。
4.4 和重力补偿、惯量前馈怎么合
摩擦补偿从来不是单独存在的,它和重力补偿、惯量前馈一起构成完整的动力学前馈:
τ_ff = τ_gravity(q) + M(q) · q̈_des + C(q, q̇) · q̇_des + τ_friction(q̇)
顺序上我一般这么排:先算重力和惯量,这是主项,量级大;再算科氏和离心项,中小型机器人这一项可以忽略;最后加摩擦。加起来之后统一做限幅,防止某一项算飞了把整个前馈带偏。限幅的阈值跟关节额定力矩挂钩,我一般取额定力矩的1.2倍,超过就截断并打一个日志,方便排查。
这里有个经验:如果你发现摩擦补偿加上去之后系统开始低频振荡,八成不是摩擦参数的问题,而是几项前馈叠加后总的前馈量相位不一致,尤其是惯量前馈用了规划加速度而实际加速度滞后的时候。这时候可以给惯量前馈单独加一个相位超前或者用实测加速度,别去动摩擦参数,越动越乱。
4.5 低速段的观测器补偿:什么时候值得上
如果做完上面这些,低速段还是不达标,可以考虑叠加一个扰动观测器专门处理换向区。基本形式是用一个逆动力学模型加一个低通滤波估计外部扰动:
$$\hat{d} = Q(s) \cdot [\tau_{cmd} - \hat{M}(q)\ddot{q} - \hat{C}(q,\dot{q}) - \hat{\tau}_{friction}]$$
Q(s) 是一个低通滤波器,决定了观测带宽。估出来的 d̂ 反向加到指令上。它的优势是能补上模型没覆盖的部分,包括没辨识到的摩擦分量、负载变化、装配误差引起的附加阻力。
但它的边界必须清楚:观测器带宽受机械谐振和采样率限制,一般只能做到几十赫兹,而换向过程的时间尺度在几毫秒量级,观测器基本跟不上,所以它主要能改善的是中低速的稳态误差,对换向尖峰的帮助有限。指望观测器解决象限突起,效果大概率不如把预滑动项做好。
5. 常见问题与排查速查表
5.1 补偿加上了反而抖起来了
这是最常见的翻车场景,我至少遇到过四五次。原因基本跑不出这几个:符号项没平滑,换向瞬间补偿量从正的最大值跳到负的最大值,相当于给系统一个脉冲激励;低通截止频率设得太高,等于没滤;或者摩擦参数辨识时过拟合,把噪声当成了真实摩擦。
排查顺序建议这样走:先把符号平滑系数 eps 加大十倍,看抖动是否明显减轻,如果是,说明就是符号跳变的问题,再慢慢往回调;如果没变化,把低通截止频率减半,看效果;还是不行,把摩擦参数整体缩小到原来的50%跑一遍,如果抖动消失但精度变差,说明是过补偿,回去重新辨识;如果缩小了还抖,问题多半不在摩擦上,去查位置环增益和机械谐振。
5.2 换向台阶依然存在
如果换向的台阶一点没改善,先别怀疑模型,查三个基础项。一是补偿量的符号方向对不对,把补偿量单独打出来看,关节正向运动时它应该是抵抗运动的,方向反了会加倍误差。二是速度信号的符号在零点附近对不对,编码器差分在低速段噪声大,符号可能在零点附近乱翻,需要加一个小的滞环。三是补偿有没有真的送到电流环,有些控制器里力矩前馈的接口单位是标幺值或者千分比,你按牛顿米送进去,等于没送,这个坑我踩过,查了半天代码才发现单位。
5.3 长时间跑下来精度变差
这个基本就是温度的事。跑热之后摩擦下降,固定的补偿参数就过补偿了。验证方法很简单:冷机状态测一次跟随误差,连续跑一个小时再测一次,如果误差明显恶化,那就是温度。解决就是加温度修正或者分段参数表,前面3.4节讲过。
5.4 排查速查表
| 现象 | 最可能的原因 | 快速验证方法 | 处理方向 |
|---|---|---|---|
| 补偿后关节高频抖动 | 符号项未平滑或低通截止过高 | 加大 eps 看是否缓解 | 调平平滑系数与低通 |
| 低频振荡(1~5 Hz) | 前馈总相位不一致或过补偿 | 单独关闭摩擦项看是否消失 | 检查惯量前馈相位 |
| 换向台阶无改善 | 补偿方向错、单位错、未接入 | 打印补偿量与指令电流 | 核对符号与单位 |
| 低速仍爬行 | 库仑项偏小或 eps 过大 | 低速恒速实验复测 | 重辨识、减小 eps |
| 跑热后精度变差 | 温度导致摩擦下降 | 冷机/热机对比误差 | 加温度修正 |
| 正反向精度不对称 | 方向性摩擦未分开标定 | 正反向各做一次辨识 | 分开标定参数 |
| 力控接触力波动 | 摩擦占据外力估计误差 | 空载测外力估计偏差 | 补偿精度先做足 |
| 换负载后补偿失效 | 参数与负载强相关 | 换负载复测摩擦曲线 | 加负载相关系数 |
这张表我一般直接贴在工位上,出问题先按表走一遍,能覆盖八成现场情况,剩下的才需要深入分析。
6. 在仿真里先验证,再上真机
6.1 仿真环境里的摩擦模型有个大坑
现在做机器人开发,很多人习惯先在 MuJoCo、Gazebo 或者基于 pinocchio 自己搭的仿真里跑算法。这是好习惯,但在摩擦这件事上要小心:仿真器里的摩擦模型和真机差别非常大。MuJoCo 默认用的是基于速度的库仑加黏滞,有些版本还带一个椭圆锥约束的接触摩擦,参数是人为设的,跟真实减速器的摩擦完全不是一回事。你在仿真里把参数调得再漂亮,换到真机上照样要重做一遍辨识。
我的用法是:仿真里验证的是补偿算法本身的正确性——符号处理对不对、低通会不会引起振荡、前馈叠加的限幅逻辑对不对、换向时会不会数值发散。这些跟具体参数无关,仿真里跑通能省很多事。至于参数,仿真里随便给一组,只要量级对就行,真机上再辨识。
另外,仿真里如果用的是理想速度信号,你永远看不到编码器量化噪声对符号项的影响。想验证得真实一点,可以在仿真里给速度和位置信号加上跟真机量级相当的量化噪声和滤波延迟,这样测出来的抖动特性才有参考价值。我在一次项目里就是因为仿真速度信号太干净,到了真机上符号项在零点附近疯狂翻转,折腾了两天才定位。
6.2 资源受限平台的取舍
不是所有机器人都有高性能工控机。有些小型四足、桌面机械臂用的是单片机或者低配嵌入式板,控制周期可能只有2到5毫秒,算力也紧张。这种情况下模型要简化,我一般这么砍:去掉 Stribeck 项只留库仑加黏滞,符号用 tanh 加固定系数不做自适应,低通用简单的滑动平均代替一阶滤波,温度修正直接不做或者只做两段式切换。
算力账要算清楚。上面那个 C++ 函数里有一次 tanh、一次 exp、几次乘除,在 Cortex-M4 上大概几百个时钟周期,5毫秒周期完全跑得动。但如果叠加多关节、重力模型、惯量前馈,开销就上来了。我的经验是先把重力和摩擦做好,惯量前馈在低速场景下可以后置,因为低速时惯量项本身就不大。
6.3 验收指标怎么定,别用"看着差不多"
补偿做完了,怎么判断合格?我一般定四个量化指标。第一,换向处的跟随误差尖峰高度,跟补偿前对比,至少压掉60%才算有实质效果。第二,低速最小平稳速度,也就是关节能不爬行地稳定运行的最低速度,一般能做到0.001到0.005弧度每秒算不错。第三,正反向跟随误差的对称度,同一轨迹来回跑,误差均方根的差异应该小于10%。第四,力控场景下的接触力波动,稳态接触时的力波动应该压到满量程的1%以内。
这四个指标测出来存档,换硬件、换润滑脂、改参数之后复测,能快速判断有没有变差。这比任何主观评价都靠谱。
7. 一些实测心得
我个人在实际操作中的体会是,摩擦力矩补偿这件事,八成的效果来自于把最基础的库仑项标定准、符号处理平滑。剩下的两成才是 Stribeck、预滑动、温度修正、观测器这些进阶手段。我见过不少团队一上来就攻 LuGre,参数辨识做了一堆实验,最后上线的还是简化版静态模型,因为复杂模型在生产环境里的可维护性太差——换个减速器批次就要重标一遍,产线根本受不了。
还有一个特别实用的技巧:把辨识流程脚本化。写一个自动跑恒速实验、自动采数据、自动拟合、自动生成参数表顺便画残差图的脚本,每次换硬件或者做定期维护的时候一键跑完。这一套我大概花了两天做出来,后面省下的时间远远不止两天,而且避免了手工记录出错。
最后一个坑提醒:辨识和补偿一定要在同一条控制周期的时序下验证。有的人辨识用的是离线录的数据,时戳对齐没做,速度信号和电流信号差了半个周期,参数标出来就是错的。离线分析的时候先确认时戳对齐,这个看起来是小事,实际能让你的参数偏个百分之十几。
这个方向后续还可以往在线自适应和基于学习的方法走,尤其是产线上有大量重复轨迹的场景,用迭代学习去补摩擦的周期性成分效果很好。但那属于另一个话题了,要把基础的前馈补偿做扎实,再去碰这些,路会顺很多。