从零搭建创客测速系统:红外传感器与MCU的防抖优化实战
·
从零搭建创客测速系统:红外传感器与MCU的防抖优化实战
1. 红外测速系统基础与选型指南
红外测速系统是智能小车和机器人项目中不可或缺的组成部分。它通过检测旋转编码器或码盘的脉冲信号来计算转速和位移,为运动控制提供关键反馈。市面上常见的测速模块主要采用槽型光耦结构,由红外发射管和接收管组成,当码盘齿通过时会遮挡红外线,产生脉冲信号。
核心组件对比分析:
| 型号 | 槽宽(mm) | 工作电压(V) | 输出类型 | 典型应用场景 |
|---|---|---|---|---|
| ITR9606 | 5 | 3.3-5 | 数字开关量 | 小型电机、低速场景 |
| HC-020K | 10 | 3.3-5 | 数字开关量 | 中型电机、中速场景 |
| FTR-0412 | 12 | 5-24 | 模拟/数字 | 工业级、高速应用 |
选择传感器时需要考虑三个关键因素:
- 机械兼容性:码盘厚度需与传感器槽宽匹配,一般留有1-2mm余量
- 电气特性:3.3V系统需注意模块输出电平兼容性
- 响应频率:高速应用需选择上升沿时间短的型号
提示:新手常见误区是直接选用最便宜的模块,而忽略了实际转速需求。建议先用示波器观察信号质量,再决定是否需要升级更高性能的传感器。
2. 硬件电路设计与抗干扰方案
优质的硬件设计是稳定测速的基础。我们采用LM393比较器方案,将红外接收管的模拟信号转换为干净的方波。电路设计中有几个关键点需要注意:
- 电源滤波:在传感器VCC引脚就近放置0.1μF陶瓷电容
- 信号调理:比较器基准电压建议设置为供电电压的1/3
- 输出保护:串联100Ω电阻限制MCU输入电流
典型接线示意图:
+3.3V ──┬───[传感器]───┬── GPIO (EXTI)
│ │
0.1μF 10kΩ
│ │
GND ────┴─────────────┴── GND
实测中发现,电机运行时会产生以下干扰:
- 电源纹波(50-200mVpp)
- 电磁辐射(特别是碳刷电机)
- 机械振动导致的信号抖动
针对这些干扰,我们采用三级滤波方案:
- 硬件RC滤波:在传感器输出端增加1kΩ+100nF低通滤波(截止频率≈1.6kHz)
- 软件消抖:在中断服务程序中加入10-20ms的延时验证
- 信号验证:连续采样3次确认信号有效性
3. STM32中断配置与防抖算法
STM32的外部中断配置看似简单,实则暗藏多个技术陷阱。以下是经过优化的配置流程:
// 初始化代码片段
void SpeedSensor_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 启用GPIO和AFIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
// 配置GPIO为上拉输入
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_14;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStruct);
// 配置外部中断
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);
EXTI_InitTypeDef EXTI_InitStruct = {
.EXTI_Line = EXTI_Line14,
.EXTI_Mode = EXTI_Mode_Interrupt,
.EXTI_Trigger = EXTI_Trigger_Rising_Falling, // 双边沿触发
.EXTI_LineCmd = ENABLE
};
EXTI_Init(&EXTI_InitStruct);
// 配置NVIC
NVIC_InitTypeDef NVIC_InitStruct = {
.NVIC_IRQChannel = EXTI15_10_IRQn,
.NVIC_IRQChannelPreemptionPriority = 0x0F,
.NVIC_IRQChannelSubPriority = 0x0F,
.NVIC_IRQChannelCmd = ENABLE
};
NVIC_Init(&NVIC_InitStruct);
}
中断服务程序优化技巧:
- 采用状态机模式处理信号边沿
- 添加时间戳校验防止重复计数
- 实现滑动窗口滤波算法
// 改进版中断服务程序
#define DEBOUNCE_TIME 15 // ms
static uint32_t lastTrigger = 0;
void EXTI15_10_IRQHandler(void) {
static uint8_t state = 0;
if(EXTI_GetITStatus(EXTI_Line14) != RESET) {
uint32_t now = HAL_GetTick();
if((now - lastTrigger) > DEBOUNCE_TIME) {
state ^= 1; // 状态翻转
if(state) SpeedCount++; // 仅上升沿计数
lastTrigger = now;
}
EXTI_ClearITPendingBit(EXTI_Line14);
}
}
4. 速度计算与系统校准
获得稳定的脉冲计数后,需要将其转换为有物理意义的转速或线速度。基本公式为:
转速(RPM) = (脉冲数 × 60) / (码盘齿数 × 采样周期)
线速度(m/s) = 转速 × 轮周长 / 60
校准步骤:
- 测量轮子直径,计算周长(π×直径)
- 统计码盘总齿数(通常为20-60个)
- 在固定转速下测试,调整补偿系数
建议采用移动平均滤波处理速度数据:
#define WINDOW_SIZE 5
float speedBuffer[WINDOW_SIZE] = {0};
uint8_t index = 0;
float GetFilteredSpeed(float newSpeed) {
speedBuffer[index] = newSpeed;
index = (index + 1) % WINDOW_SIZE;
float sum = 0;
for(int i=0; i<WINDOW_SIZE; i++) {
sum += speedBuffer[i];
}
return sum / WINDOW_SIZE;
}
性能优化技巧:
- 使用硬件定时器输入捕获模式替代外部中断
- 对于高速应用,启用DMA传输脉冲计数
- 采用32位累加器防止计数溢出
5. 实战调试与问题排查
调试过程中常见问题及解决方案:
问题1:信号随机抖动
- 检查电源稳定性,示波器观察VCC纹波
- 增加传感器与电机的物理隔离
- 在代码中实现软件消抖算法
问题2:高速时漏计数
- 改用双边沿触发模式
- 减小中断服务程序执行时间
- 考虑使用硬件计数器(如TIMx_ETR)
问题3:转速显示波动大
- 增加滤波窗口大小
- 检查码盘安装是否偏心
- 验证轮子与地面的接触情况
推荐使用以下调试工具组合:
- 逻辑分析仪(观察原始脉冲)
- 串口数据绘图(分析速度曲线)
- STM32的SWD调试(单步跟踪中断)
注意:当电机负载变化时,机械振动特性也会改变,建议在最终使用场景下重新校准系统参数。
更多推荐


所有评论(0)