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图把三种方法的逻辑关系画清楚了:

电机测速算法对比图 电机测速算法对比 M法(测频法) T法(测周期法) M/T法(综合法) 固定时间窗口 计数脉冲数 n = M×60/(P×T) 固定脉冲数 测量时间 n = 60×X/(P×t) 同时测脉冲和时间 同步启停 n = 60×M/(P×T_actual) ✅ 中高速 ✅ 低速 ✅ 全速域 选型建议 • 高速电机(>1000 RPM):M法,简单够用 • 低速电机(<100 RPM):T法,精度优先 • 宽调速范围:M/T法,全速域通吃

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法因为硬件触发没做好反而更差。关键是你得理解每种方法的原理和局限,然后根据你的电机、编码器、控制周期来调参。