3. 开环控制与开环误差
聊到差速底盘的直线运动,咱们得先面对一个现实:没有反馈的控制,就像蒙着眼走路。开环控制,说白了就是“我命令你走直线,你就得走直线”——但现实往往很打脸。
3.1 开环控制的原理
开环控制的结构特别简单。你给电机发一个PWM值,电机就转。你让左轮转1000 RPM,右轮也转1000 RPM,理论上车子就该直着走。嗯,理论上。
它的核心逻辑是:
- 输入:设定目标速度(比如左轮1000 RPM,右轮1000 RPM)
- 执行:电机驱动器按PWM值驱动电机
- 输出:底盘移动
- 没有反馈:系统不会检查实际速度是否等于目标速度
我刚开始做机器人那会儿,觉得这玩意儿太简单了。给两个轮子发同样的PWM,车不就直着走了吗?结果第一次试车,车子直接往右偏了30度。我当时还以为是代码写错了。
开环控制的本质: 只发命令,不检查结果。系统对实际输出是否达到预期,完全不知情。
3.2 开环控制下直线运动的实际表现
咱们来看一个真实场景。你给左右轮都发1500的PWM,期望车子走直线。实际跑起来会怎样?
我做过一个测试,记录了一台差速底盘在开环控制下跑2米距离的轨迹数据:
| 测试次数 | 左轮PWM | 右轮PWM | 实际偏移量(cm) | 偏移方向 |
|---|---|---|---|---|
| 1 | 1500 | 1500 | 23 | 右偏 |
| 2 | 1500 | 1500 | 19 | 右偏 |
| 3 | 1500 | 1500 | 27 | 右偏 |
| 4 | 1500 | 1500 | 21 | 右偏 |
| 5 | 1500 | 1500 | 25 | 右偏 |
看到了吗?每次都是右偏,而且偏移量还不一样。为什么会这样?
原因其实挺多的:
- 电机个体差异:两个电机即使型号相同,实际转速也会有3%-5%的偏差
- 轮胎直径不一致:哪怕差0.5mm,跑2米就能偏出十几厘米
- 地面摩擦不均匀:左边地板光滑一点,右边粗糙一点,车就偏了
- 电池电压波动:电压下降时,电机转速会变,而且两个电机受影响程度不同
- 负载分布:电池放左边还是右边,都会影响重心
我的经验: 开环控制下,差速底盘走直线,2米距离偏移20-30厘米是常态。如果你期望偏移小于5厘米,那基本不可能。我曾经在一个项目中,为了省成本只用开环控制,结果客户验收时拿尺子量偏移量,差点没通过验收。
3.3 开环误差的量化方法
既然开环控制有误差,那咱们得知道误差有多大。我个人习惯用三个指标来量化:
3.3.1 横向偏移量(Lateral Deviation)
这是最直观的指标。让底盘走一段直线距离D,测量终点位置与目标直线的垂直距离ΔL。
// 横向偏移量计算示例
float D = 2.0; // 目标距离,单位米
float delta_L = 0.23; // 实测偏移量,单位米
float deviation_ratio = delta_L / D; // 偏移率
printf("偏移率: %.2f%%\n", deviation_ratio * 100);
// 输出:偏移率: 11.50%
3.3.2 航向角偏差(Heading Error)
这个指标反映的是底盘实际朝向与目标朝向的夹角。你可以用IMU或者编码器来算。
// 航向角偏差计算
float left_encoder_ticks = 1000;
float right_encoder_ticks = 980;
float wheel_base = 0.3; // 轮距,单位米
float ticks_per_meter = 500;
float left_distance = left_encoder_ticks / ticks_per_meter;
float right_distance = right_encoder_ticks / ticks_per_meter;
float heading_error = (right_distance - left_distance) / wheel_base;
printf("航向角偏差: %.3f rad\n", heading_error);
// 输出:航向角偏差: 0.013 rad
3.3.3 速度一致性误差(Speed Consistency Error)
这个指标用来衡量左右轮实际速度的差异。我建议用编码器测实际转速,然后算偏差百分比。
// 速度一致性误差
float left_speed = 950; // 左轮实际转速,RPM
float right_speed = 920; // 右轮实际转速,RPM
float target_speed = 1000; // 目标转速,RPM
float left_error = (target_speed - left_speed) / target_speed * 100;
float right_error = (target_speed - right_speed) / target_speed * 100;
float consistency_error = fabs(left_error - right_error);
printf("左轮误差: %.1f%%, 右轮误差: %.1f%%\n", left_error, right_error);
printf("一致性误差: %.1f%%\n", consistency_error);
// 输出:左轮误差: 5.0%, 右轮误差: 8.0%
// 输出:一致性误差: 3.0%
注意: 开环误差不是固定值。它会随着电池电压下降、地面条件变化、轮胎磨损等因素而改变。你今天测的偏移量是20cm,明天可能就变成30cm了。我曾经在项目交付前忘了重新标定,结果现场跑出来的偏移量比实验室大了两倍,那叫一个尴尬。
3.4 开环误差的典型分布
我整理了一个典型数据,展示开环控制下误差的分布情况。这个数据来自我做过的一个项目,用的是常见的直流减速电机:
| 误差来源 | 典型偏差范围 | 对直线运动的影响 |
|---|---|---|
| 电机转速差异 | ±3% ~ ±5% | 持续偏航 |
| 轮胎直径差异 | ±0.5mm ~ ±1mm | 累积偏移 |
| 地面摩擦变化 | ±2% ~ ±8% | 随机偏航 |
| 电池电压波动 | ±2% ~ ±10% | 速度波动 |
| 负载分布不均 | ±1% ~ ±3% | 轻微偏航 |
你看,这么多因素叠加在一起,开环控制下走直线,说白了就是碰运气。你想想看,如果每个因素都往坏的方向叠加,那偏移量会相当可观。
核心结论: 开环控制适用于对直线精度要求不高的场景(比如误差允许在10%以上)。如果你需要高精度直线运动,必须引入闭环控制。这是我在多个项目中摔出来的教训。
好了,开环控制的内容就聊到这儿。下一节咱们会深入讨论闭环控制,看看怎么用反馈来纠正这些误差。不过在那之前,我建议你先动手测一下自己底盘的偏移量——数据会说话。