4. 电机测速算法:M法、T法与M/T法
电机测速,说白了就是回答一个问题:这电机现在转多快?
在STM32的编码器模式下,我们拿到的是一串脉冲。怎么把这串脉冲变成转速(RPM),就是测速算法要干的事。我个人习惯把测速算法分成三类:M法、T法、M/T法。每种方法都有自己的脾气,选对了事半功倍,选错了……嗯,我踩过坑。
核心概念速览
测速的本质 = 脉冲数 ÷ 时间。M法固定时间数脉冲,T法固定脉冲数测时间,M/T法两边都测。
4.1 M法(测频法)—— 简单粗暴,适合高速
M法的思路很直接:固定一个时间窗口,数这个窗口里来了多少个脉冲。
公式长这样:
转速 n = (M × 60) / (P × T)
其中:
- M —— 时间窗口内计数的脉冲数
- P —— 电机转一圈产生的脉冲数(编码器线数 × 4倍频)
- T —— 时间窗口,单位秒
举个例子:我用一个1000线的编码器(4倍频后一圈4000脉冲),时间窗口设100ms,数到400个脉冲。那么转速就是:
n = (400 × 60) / (4000 × 0.1) = 60 RPM
M法的问题在哪?
低速的时候,时间窗口内可能只抓到几个脉冲,甚至一个都没有。你想想看,如果100ms只抓到1个脉冲,算出来转速是1.5 RPM,但实际可能是1.0或者2.0,误差非常大。我在项目里遇到过,电机在10 RPM以下运行时,M法测出来的转速跳得像心电图,根本没法用。
我的经验:M法适合中高速场景,一般建议每个时间窗口至少抓到20个以上脉冲,精度才够看。时间窗口通常选10ms~200ms,看你的控制周期要求。
4.2 T法(测周期法)—— 低速王者,高速拉胯
T法反过来:固定脉冲数,测这固定脉冲花了多少时间。
公式:
转速 n = (60 × X) / (P × t)
其中:
- X —— 固定的脉冲数(通常设1或2)
- t —— 测到X个脉冲所用的时间,单位秒
为什么说T法是低速王者?因为低速时脉冲间隔长,时间t很大,计时精度反而高。我曾经用T法测一个5 RPM的电机,用1000线编码器,测两个脉冲的时间,算出来非常稳。
但T法高速就不行了。高速时脉冲间隔极短,t可能只有几十微秒,计时器分辨率不够的话,误差会很大。而且每次测速都要等固定脉冲数到,实时性不如M法。
| 特性 | M法(测频法) | T法(测周期法) |
|---|---|---|
| 适用速度 | 中高速 | 低速 |
| 低速精度 | 差(脉冲少) | 好(时间测量准) |
| 高速精度 | 好 | 差(时间太短) |
| 实时性 | 固定周期,好 | 周期不固定,差 |
| 实现复杂度 | 简单 | 中等 |
4.3 M/T法 —— 全速域通吃,但有点复杂
M/T法就是把上面两种方法结合起来。它同时测量一个时间窗口内的脉冲数,以及这个时间窗口的实际长度。
公式:
转速 n = (60 × M) / (P × T_actual)
这里的T_actual不是固定的,而是用定时器精确测量的实际时间。M/T法会同步启动脉冲计数和计时,在最后一个脉冲到达时同时停止计数和计时。
为什么M/T法好?
- 低速时,时间窗口自动拉长(等脉冲),精度高
- 高速时,时间窗口自动缩短,实时性好
- 全速域都能保持不错的精度
我记得第一次在STM32上实现M/T法时,踩了个坑:脉冲计数和计时必须同步启停。如果计数停了计时还在跑,或者反过来,算出来的转速就是错的。后来我用定时器的从模式,让编码器脉冲直接触发计时器捕获,才解决了这个问题。
避坑指南:我曾经在M/T法里直接用软件判断脉冲边沿来启停计时,结果中断响应延迟导致计时不准。后来改用硬件捕获(STM32的输入捕获模式),精度直接提升了一个数量级。
4.4 速度滤波与平滑处理
不管你用哪种算法,原始测速值都会有噪声。电机本身有机械振动,编码器有量化误差,电磁干扰也会引入毛刺。直接拿原始值去控制,电机就会抖得像筛子。
我常用的几种滤波方法:
4.4.1 限幅滤波
最简单粗暴:如果当前测速值和上次的差值超过一个阈值,就丢弃或者限幅。
if (abs(current_speed - last_speed) > MAX_DELTA) {
current_speed = last_speed + sign(current_speed - last_speed) * MAX_DELTA;
}
适合剔除明显的异常跳变,但治标不治本。
4.4.2 滑动平均滤波
维护一个长度为N的数组,每次取平均值。N越大越平滑,但延迟也越大。
// 滑动平均示例
#define FILTER_LEN 8
static uint16_t buffer[FILTER_LEN] = {0};
static uint8_t index = 0;
static uint32_t sum = 0;
// 每次新数据进来
sum -= buffer[index];
buffer[index] = new_speed;
sum += buffer[index];
index = (index + 1) % FILTER_LEN;
filtered_speed = sum / FILTER_LEN;
我个人习惯用8~16点的滑动平均,延迟和平滑度比较均衡。
4.4.3 一阶低通滤波
这是我最常用的方法,计算量小,效果也不错。
filtered_speed = alpha * new_speed + (1 - alpha) * filtered_speed;
alpha取值0.1~0.3,越小越平滑,但响应越慢。我一般设0.15,在响应和平滑之间取个折中。
我的经验:滤波不是越强越好。有一次我把alpha设到0.05,速度曲线确实平滑了,但电机对指令的响应慢得像在游泳。后来我改成动态alpha——速度变化大时alpha大(快速跟踪),稳定时alpha小(平滑输出)。
4.5 三种测速方法对比总结
下面这张SVG图把三种方法的逻辑关系画清楚了:
4.6 实际项目中的选型建议
说了这么多理论,到底怎么选?我根据实际项目经验给个参考:
- 普通直流电机调速(几百到几千RPM):M法就够了,时间窗口设50ms,8点滑动平均,稳得很。
- 低速伺服(10~100 RPM):T法或者M/T法。我做过一个转台项目,要求0.1 RPM精度,用的T法+10000线编码器。
- 宽调速范围(1~5000 RPM):别犹豫,直接上M/T法。虽然代码复杂点,但省心。
- 对实时性要求极高(控制周期1ms以内):M法,时间窗口固定,好做同步。
最后说一句:没有最好的算法,只有最合适的。我见过有人用M法+强滤波在低速下跑得也不错,也见过M/T法因为硬件触发没做好反而更差。关键是你得理解每种方法的原理和局限,然后根据你的电机、编码器、控制周期来调参。