4. 位置环控制策略:P/PI控制、前馈控制、轨迹规划与插补
位置环,说白了就是让电机精准地停在你想让它去的地方。很多刚入行的工程师觉得这很简单——给个位置指令,电机跑过去不就完了?但实际项目里,你会发现事情远没那么简单。过冲、震荡、跟随误差大,这些问题我几乎在每个项目里都遇到过。
今天我们就来拆解位置环的核心策略。我个人习惯把位置环分成三个层次:基础调节器、前馈补偿、轨迹规划。这三层一层比一层精细,也一层比一层吃功夫。
核心观点:位置环的性能天花板,往往不在调节器本身,而在于你给调节器喂了什么样的指令。
4.1 P控制与PI控制:基础但够用
位置环最常用的就是P控制。比例增益Kp直接把位置误差映射成速度指令。简单、直观、好调。
// 位置环P控制
float pos_err = target_pos - current_pos;
float speed_cmd = Kp * pos_err;
但P控制有个硬伤——稳态误差。比如你让电机停在1000个脉冲的位置,如果负载有摩擦力,它可能停在995就动不了了。为什么?因为误差小了,速度指令也小了,小到不足以克服静摩擦力。
这时候就需要引入积分项。PI控制能消除稳态误差,但代价是可能引入震荡。我在一个贴片机项目里就吃过这个亏——加了积分后,位置环在目标点附近来回晃了3个来回才稳定下来,直接影响了贴片节拍。
| 特性 | P控制 | PI控制 |
|---|---|---|
| 稳态误差 | 有(受摩擦力影响) | 无(积分消除) |
| 响应速度 | 快 | 略慢(积分滞后) |
| 抗扰能力 | 弱 | 强(积分持续补偿) |
| 调试难度 | 低(一个参数) | 中(Kp+Ki配合) |
| 适用场景 | 低摩擦、高刚性系统 | 有持续负载、需要精确定位 |
我的调试习惯:先调P到系统刚好不震荡,再加I。I的积分限幅一定要做,否则积分饱和会让你吃大亏。我曾经在卷绕机上没做限幅,结果积分项攒了半天的误差,一释放直接把膜拉断了。
4.2 前馈控制:让系统跑得更快更准
纯反馈控制有个本质问题——它总是等误差出现了才去纠正。你想想看,这就像开车时你总是等车偏了才打方向盘,永远在追误差。
前馈控制的思路完全不同:它提前计算出系统需要多少力,直接给出去。反馈只负责修正模型误差和扰动。
// 速度前馈 + 位置环PI
float pos_err = target_pos - current_pos;
float speed_feedforward = target_vel * Kff; // 前馈项
float speed_feedback = Kp * pos_err + Ki * integral; // 反馈项
float speed_cmd = speed_feedforward + speed_feedback;
前馈系数Kff的理想值是1.0——意味着前馈完全承担了速度指令,反馈只负责修正。但实际项目中,我一般从0.8开始试。为什么?因为模型不准,Kff=1.0反而可能让系统震荡。
前馈的类型不止一种。我个人常用的是:
- 速度前馈:最基础,补偿匀速段
- 加速度前馈:补偿加减速段的惯性力
- 摩擦力前馈:补偿静摩擦和库仑摩擦
注意:前馈不是万能的。如果系统模型偏差太大,前馈反而会帮倒忙。我曾经在一条老旧的传送带上试加速度前馈,结果因为机械间隙太大,前馈给的力全变成了冲击,位置精度反而下降了30%。
4.3 轨迹规划:给电机喂"好"的指令
前面说的都是怎么控制电机去追指令。但你有没有想过——指令本身是不是合理的?
直接给一个阶跃位置指令,相当于让电机瞬间从0速跳到目标速度。物理上不可能,实际表现就是过冲和震荡。所以我们需要轨迹规划,把粗暴的阶跃指令变成平滑的S曲线。
最常用的就是S形速度曲线。它把运动分成7段:加加速、匀加速、减加速、匀速、加减速、匀减速、减减速。
// S形曲线位置插补(简化版)
float t = current_time - start_time;
float T = total_time; // 总运动时间
if (t < T/4) {
// 加加速段:位置与t³成正比
pos = 0.5 * accel_max * t * t * (2 * t / T);
} else if (t < 3*T/4) {
// 匀速段
pos = ...;
} else {
// 减减速段
pos = ...;
}
实际项目中,我很少用完整的7段S曲线。为什么?计算量大,而且很多场景用不上。对于大多数伺服应用,梯形速度曲线加一个简单的低通滤波就够用了。
我的经验:轨迹规划的平滑度要和机械系统的带宽匹配。你规划得再平滑,如果机械共振频率在100Hz,而你的轨迹激励了120Hz的分量,照样震荡。我一般会在轨迹规划后加一个陷波滤波器,专门干掉机械共振频率。
4.4 插补算法:让运动连续起来
轨迹规划算出了整条路径,但控制器在每个控制周期(比如1ms)都需要一个位置指令。这就需要插补——在规划好的轨迹点上,实时计算出当前时刻的位置。
插补算法主要有两种:
- 时间基插补:以时间为自变量,查表或计算位置。简单,但要求控制周期严格固定。
- 距离基插补:以走过的距离为自变量,适合多轴联动。复杂,但更精确。
我做过一个焊接机器人项目,要求6轴同步运动。时间基插补在单轴上没问题,但6轴一联动,每个轴的控制周期抖动导致轨迹跑偏。后来换成距离基插补,问题才解决。
// 时间基线性插补
float t_ratio = (current_time - segment_start_time) / segment_duration;
float pos = start_pos + (end_pos - start_pos) * t_ratio;
插补的精度直接影响最终定位精度。一般来说,插补周期越短越好,但受限于CPU算力。我建议:
- 伺服系统:插补周期 ≤ 1ms
- 步进系统:插补周期 ≤ 5ms
- 多轴联动:插补周期 ≤ 500μs
4.5 整体策略:怎么搭才合理?
说了这么多,我们来画一张整体架构图。这是我做项目时常用的位置环分层结构:
这个架构的核心思想是分层解耦。轨迹规划只管"怎么走",插补只管"当前在哪",前馈只管"提前给力",反馈只管"修偏差"。每一层各司其职,互不干扰。
实际项目中,我建议从下往上调。先把反馈层调稳,再加前馈提响应,最后用轨迹规划保证平滑。反过来调,出了问题你都不知道是哪一层的问题。
避坑指南:我曾经在一个项目中,把轨迹规划做得特别平滑,但反馈层参数没调好,结果电机在匀速段一直有微小震荡。查了两天才发现是反馈的Ki太大,积分项在匀速段持续累积误差。所以记住——上层再漂亮,下层不稳也是白搭。
好了,位置环的策略就聊到这里。这些方法我在十几个项目里反复验证过,从几块钱的步进电机到几万块的伺服驱动器,核心逻辑都一样。你只要把这几层搭好,位置精度做到0.01度或者0.1mm,基本不是问题。