4. 前馈控制:前馈控制原理、重力补偿与摩擦力补偿、前馈与反馈的协同

说到前馈控制,我脑子里第一个蹦出来的画面,是刚入行时调一个六轴机器人。那家伙在低速时抖得像筛糠,我折腾了一周的PID参数,效果都不理想。后来一位老工程师走过来,淡淡说了句:“你试试加个重力前馈。” 嗯,从那以后,我才真正理解了什么叫“防患于未然”。

反馈控制是“出了错再纠正”,前馈控制则是“提前预判,主动补偿”。两者结合,才是工业级关节控制的精髓。

4.1 前馈控制原理

前馈控制的核心思想很简单:既然知道扰动或目标的变化规律,为什么不提前把补偿量算进去?

举个例子,你让电机从静止加速到1000rpm。反馈控制要等到实际速度与目标速度出现偏差才开始加力,这必然有滞后。而前馈控制直接根据加速度需求,提前给出一份“预估力矩”。

前馈控制的数学本质:

τff = M(q) · q̈des + C(q, q̇) · q̇des + G(q) + F(q̇)

其中:τff 为前馈力矩,M为惯性矩阵,C为科氏力/离心力项,G为重力项,F为摩擦力项。

说白了,前馈就是“开环地预测系统需要多少力”。它不依赖误差,只依赖模型和期望轨迹。

特性 前馈控制 反馈控制
触发条件 期望轨迹变化 实际误差出现
响应速度 即时(无滞后) 有滞后(等待误差)
依赖条件 精确模型 误差信号
抗干扰能力 弱(无法应对未建模扰动) 强(能抑制扰动)

我的经验:前馈不是万能的。模型越准,前馈效果越好。但如果你连关节的减速比都搞错了,前馈反而会帮倒忙。我建议先做系统辨识,再上前馈。

4.2 重力补偿

重力补偿是前馈控制里最基础、也最常用的一项。你想想看,一个机械臂在水平位置和竖直位置,重力对每个关节的影响完全不同。如果只用反馈去扛,那低速时的位置静差会非常明显。

重力补偿的计算公式:

// 伪代码:重力补偿计算
float gravity_compensation(JointState q) {
    // 根据当前关节角度,计算重力矩
    float G[6];
    G[0] = m1 * g * l1 * cos(q[0]) + m2 * g * (l1*cos(q[0]) + l2*cos(q[0]+q[1]));
    G[1] = m2 * g * l2 * cos(q[0] + q[1]);
    // ... 更多关节
    return G;
}

实际工程中,重力补偿通常以两种方式实现:

  • 静态重力补偿:只在关节静止或极低速时生效,补偿重力引起的稳态误差。
  • 动态重力补偿:实时根据关节角度计算重力矩,随运动连续更新。

注意:我曾经在一个协作机器人项目里,只做了静态重力补偿。结果机器人一跑起来,末端抖动得厉害。后来才发现,动态重力补偿的更新频率至少要达到控制周期的2倍,否则会引入高频噪声。

重力补偿的调试步骤,我一般这样走:

  1. 让机器人处于空载状态,记录各关节角度。
  2. 手动推动机器人,感受重力矩的变化。
  3. 根据DH参数计算理论重力矩。
  4. 在控制器中开启重力补偿,观察位置误差是否减小。
  5. 微调质心位置和质量参数,直到误差在允许范围内。

4.3 摩擦力补偿

摩擦力这东西,说大不大,说小不小。但在精密定位场景下,它往往是“最后一公里”的拦路虎。我调过一台高精度转台,反馈控制已经把误差压到0.01度以内了,但就是过不去。最后发现是静摩擦力在作祟。

摩擦力模型有很多种,工程上常用的有:

模型 表达式 适用场景
库仑+粘滞 F = Fc·sign(v) + B·v 中高速运动
Stribeck模型 F = Fc + (Fs - Fc)·e-(v/vs)² + B·v 低速/换向场景
LuGre模型 动态模型(鬃毛模型) 高精度仿真

实际代码实现时,我通常用分段函数:

// 摩擦力补偿(库仑+粘滞)
float friction_compensation(float velocity) {
    float Fc = 0.5;   // 库仑摩擦力矩 (Nm)
    float B  = 0.02;  // 粘滞摩擦系数 (Nm·s/rad)
    float epsilon = 0.01; // 死区阈值

    if (fabs(velocity) < epsilon) {
        return 0.0;  // 速度接近零时,不补偿(避免震荡)
    } else {
        return Fc * sign(velocity) + B * velocity;
    }
}

避坑指南:我曾经在低速时直接用了库仑模型,结果关节在零速附近来回切换方向,产生了极限环震荡。后来加了死区,并在死区内用线性插值过渡,问题才解决。记住:摩擦力补偿不是越精确越好,稳定第一。

4.4 前馈与反馈的协同

前馈和反馈不是二选一,而是搭档。前馈负责“大头”,反馈负责“小头”和“意外”。

典型的控制结构是这样的:

// 总控制力矩 = 前馈 + 反馈
float total_torque = feedforward_torque + feedback_torque;

// 前馈部分:重力 + 摩擦力 + 惯性力
feedforward_torque = gravity_comp(q) + friction_comp(qd) + inertia_comp(q, qdd);

// 反馈部分:PID
feedback_torque = Kp * error + Ki * integral + Kd * derivative;

我个人的习惯是:先调好反馈,让系统稳定。然后逐步加入前馈,观察误差变化。如果前馈加得太多,反馈反而会“对抗”前馈,造成系统振荡。

协同原则:

  • 前馈覆盖已知的、可建模的力矩需求(重力、摩擦力、惯性力)。
  • 反馈覆盖未知的、不可建模的扰动(外部负载、模型误差、噪声)。
  • 前馈占比通常为总力矩的60%-80%,反馈占20%-40%。

下面这张图,是我自己总结的前馈与反馈协同控制流程:

前馈与反馈协同控制流程图 前馈与反馈协同控制流程 期望轨迹 q_des 前馈计算 重力 + 摩擦力 + 惯性力 反馈计算 PID 误差调节 Σ 总力矩 关节 + 电机 实际轨迹 q_act 前馈占60%-80% 反馈占20%-40%

从图中可以看到,期望轨迹同时进入前馈和反馈两条路径。前馈直接给出预测力矩,反馈根据实际误差做修正。两者在求和节点合并,最终驱动关节。

一个常见的坑:前馈和反馈的带宽不能重叠太多。如果前馈的响应频率和反馈的积分频率接近,两者会互相“抢功”,导致系统出现低频振荡。我一般把前馈的截止频率设得比反馈的穿越频率低一个数量级。

最后说一句:前馈控制不是锦上添花,而是雪中送炭。尤其在高速、高精度、大惯量场景下,没有前馈,反馈再强也白搭。但前提是——你得把模型搞准。模型不准的前馈,还不如没有。