回答

收藏

零知IDE——基于零知ESP32与DRV8833的稳定电机测速系统实现教程

其他 其他 179 人阅读 | 0 人回复 | 2025-12-15

本帖最后由 eefocus_4087784 于 2025-12-15 18:13 编辑

​ ✔零知开源(零知IDE)是一个专为电子初学者/电子兴趣爱好者设计的开源软硬件平台,在硬件上提供超高性价比STM32系列开发板、物联网控制板。取消了Bootloader程序烧录,让开发重心从 “配置环境” 转移到 “创意实现”,极大降低了技术门槛。零知IDE编程软件,内置上千个覆盖多场景的示例代码,支持项目源码一键下载,项目文章在线浏览。零知开源(零知IDE)平台通过软硬件协同创新,让你的创意快速转化为实物,来动手试试吧!
✔访问零知实验室,获取更多实战项目和教程资源吧!
www.lingzhilab.com

项目概述
        本项目基于零知 ESP32主控,搭配DRV8833 电机驱动模块红外对射计数传感器,实现了高精度、高稳定性的电机速度测量与控制。核心功能包括:通过串口接收 PWM 指令控制电机转速,利用红外对射传感器采集电机码盘脉冲,结合定时器中断精准采样、EMA 指数加权滤波和平滑阈值处理,最终在 OLED 屏实时显示稳定的速度值(cm/s)、当前 PWM 输出和原始脉冲数
项目难点及解决方案
        问题描述1:实时脉冲数据经常出现大幅跳动

解决方案:采用ESP32的硬件定时器提供精确的1秒采样周期,同时结合指数加权移动平均(EMA)滤波算法和变化阈值检测机制,实现了速度值的稳定输出

一、硬件操作部分硬件清单[td]
组件名称型号规格数量备注
主控板                       零知ESP32-WROOM-32                            1     核心控制器                                    
电机驱动DRV8833双H桥驱动模块1电机速度控制
传感器红外对射计数传感器1脉冲检测
OLED显示屏0.96寸I2C SSD13061数据显示
直流电机微型直流电机1待测对象
码盘20格黑白码盘1安装于电机轴

接线方案表
        DRV8833采用5V供电确保电机驱动能力

[td]
零知 ESP32 引脚连接器件器件引脚功能说明
12(MOTOR_AIN1)        DRV8833AIN1电机 PWM 控制引脚 1
13(MOTOR_AIN2)DRV8833AIN2电机方向 / 停止控制引脚 2      
14(SENSOR_PIN)红外对射传感器         信号脚(OUT)     脉冲计数信号输入
21(SDA)OLED1306SDAI2C 数据总线
22(SCL)OLED1306SCLI2C 时钟总线
DRV8833 OUT1直流电机电机引脚 1电机动力输出
DRV8833 OUT2直流电机电机引脚 2电机动力输出

具体接线图

连接实物图



二、代码讲解部分
        代码核心在于实现了多层级的稳定性算法,下面详细解析关键部分:

硬件定时器中断

  1. // ================= 硬件定时器配置 =================
  2. hw_timer_t *speedSampleTimer = NULL;          // 速度采样定时器
  3. volatile unsigned int timerInterruptCount = 0;// 定时器中断计数(10次=1秒)
  4. portMUX_TYPE timerCriticalMux = portMUX_INITIALIZER_UNLOCKED; // 临界区保护锁

  5. // ================= 中断服务函数 =================
  6. /**
  7. * @brief 红外传感器中断服务函数(上升沿触发+1ms去抖)
  8. * 功能:过滤毛刺信号,统计有效脉冲数
  9. */
  10. void IRAM_ATTR infraredSensorIsr() {
  11.   unsigned long currentIsrTime = micros();
  12.   // 1ms去抖:两次中断间隔>1000μs→视为有效脉冲
  13.   if (currentIsrTime - lastSensorIsrTime > 1000) {
  14.     portENTER_CRITICAL_ISR(&timerCriticalMux); // 禁止其他中断,保护计数变量
  15.     sensorPulseCount++;
  16.     portEXIT_CRITICAL_ISR(&timerCriticalMux);
  17.     lastSensorIsrTime = currentIsrTime;
  18.   }
  19. }

  20. /**
  21. * @brief 定时器中断服务函数(100ms触发一次)
  22. * 功能:累计中断次数,触发1秒一次的速度计算
  23. */
  24. void IRAM_ATTR speedSampleTimerIsr() {
  25.   portENTER_CRITICAL_ISR(&timerCriticalMux);
  26.   timerInterruptCount++;
  27.   portEXIT_CRITICAL_ISR(&timerCriticalMux);
  28. }
复制代码

IRAM_ATTR确保中断函数存储在IRAM中,提高执行速度

硬件定时器配置

  1. void setup() {
  2.   // 串口初始化(115200波特率,用于控制和波形输出)
  3.   Serial.begin(115200);
  4.   while (!Serial); // 等待串口就绪(兼容部分开发板)
  5.   
  6.   // OLED初始化
  7.   if (!oledDisplay.begin(SSD1306_SWITCHCAPVCC, OLED_I2C_ADDR)) {
  8.     Serial.println("OLED Init Failed!");
  9.     while (1); // 初始化失败则卡死,提示错误
  10.   }
  11.   oledDisplay.setTextColor(SSD1306_WHITE); // 设置字体颜色为白色

  12.   // PWM初始化(适配DRV8833)
  13.   ledcSetup(PWM_CHANNEL_MOTOR, PWM_FREQUENCY, PWM_RESOLUTION);
  14.   ledcSetup(PWM_CHANNEL_STANDBY, PWM_FREQUENCY, PWM_RESOLUTION);
  15.   ledcAttachPin(MOTOR_DRV_AIN1, PWM_CHANNEL_MOTOR);
  16.   ledcAttachPin(MOTOR_DRV_AIN2, PWM_CHANNEL_STANDBY);

  17.   // 红外传感器初始化(下拉输入+上升沿中断)
  18.   pinMode(INFRARED_SENSOR_PIN, INPUT_PULLDOWN);
  19.   attachInterrupt(digitalPinToInterrupt(INFRARED_SENSOR_PIN),
  20.                   infraredSensorIsr, RISING);

  21.   // 速度采样定时器初始化(100ms中断一次)
  22.   speedSampleTimer = timerBegin(0, 80, true); // 定时器0,80分频(1MHz计数),向上计数
  23.   timerAttachInterrupt(speedSampleTimer, &speedSampleTimerIsr, true); // 绑定中断函数
  24.   timerAlarmWrite(speedSampleTimer, 100000ul, true); // 100000×1μs=100ms,自动重载
  25.   timerAlarmEnable(speedSampleTimer); // 使能定时器中断
  26.   
  27.   // 系统就绪提示
  28.   Serial.println("=== System Ready ===");
  29.   Serial.println("Usage: Send PWM value (0~255) via Serial to control motor");
  30.   Serial.println("Serial Output Format: RawPulse | FilteredPulse | Speed(cm/s)");
  31.   Serial.println("====================");
  32. }
复制代码

timerBegin 使用定时器0,预分频80(80MHz/80=1MHz),向上计数;timerAlarmWrite设置报警值为100,000微秒,自动重载;每10次中断构成1秒的完整采样周期

指数加权移动平均滤波

  1. void loop() {
  2.   // 1. 串口接收PWM指令,控制电机速度
  3.   if (Serial.available() > 0) {
  4.     int inputPwm = Serial.parseInt(); // 读取串口发送的PWM值
  5.     // 验证输入有效性(仅接收0~255的数值)
  6.     if ((Serial.read() == '\n' || Serial.read() == '\r') && inputPwm >= 0 && inputPwm <= 255) {
  7.       setMotorPwmSpeed(inputPwm);
  8.       Serial.printf("Motor PWM Set to: %d\n", currentMotorPwm);
  9.     } else {
  10.       Serial.println("Invalid Input! Please send number between 0~255");
  11.     }
  12.   }

  13.   // 2. 每1秒采样一次,计算稳定速度(定时器中断累计10次=1秒)
  14.   if (timerInterruptCount >= 10) {
  15.     portENTER_CRITICAL(&timerCriticalMux); // 临界区保护,避免数据冲突
  16.     unsigned long rawPulse = sensorPulseCount; // 读取原始脉冲数
  17.     sensorPulseCount = 0;                      // 重置脉冲计数
  18.     timerInterruptCount = 0;                   // 重置定时器计数
  19.     portEXIT_CRITICAL(&timerCriticalMux);

  20.     // --- 核心算法:数据平滑+稳定速度计算 ---
  21.     // A. 静态噪声过滤:低速截止
  22.     if (rawPulse < minValidPulse) {
  23.       rawPulse = 0;
  24.       filteredPulseCount = 0.0; // 强制归零,避免噪声导致的虚假速度
  25.     }

  26.     // B. EMA指数加权滤波:平滑原始脉冲数据
  27.     // 公式:滤波后值 = α×当前测量值 + (1-α)×上一次滤波值
  28.     filteredPulseCount = (emaFilterAlpha * rawPulse) + ((1.0 - emaFilterAlpha) * filteredPulseCount);

  29.     // C. 阈值判断:仅当脉冲变化足够大时更新显示(抑制微小波动)
  30.     if (abs(filteredPulseCount - lastDisplayedPulse) > speedUpdateThreshold || rawPulse == 0) {
  31.       lastDisplayedPulse = filteredPulseCount; // 更新显示用脉冲数
  32.     }

  33.     // D. 计算最终稳定速度(cm/s)
  34.     currentSpeedCmS = lastDisplayedPulse * cmPerPulse;

  35.     // --- 串口输出(格式固定,适配波形工具) ---
  36.     // 输出格式:Raw脉冲数 | 滤波后脉冲数 | 速度(cm/s),保留2位小数
  37.     Serial.print(rawPulse);
  38.     Serial.print(" | ");
  39.     Serial.print(filteredPulseCount, 1); // 滤波后脉冲数保留1位小数
  40.     Serial.print(" | ");
  41.     Serial.println(currentSpeedCmS, 2); // 速度保留2位小数,波形更细腻

  42.     // --- 更新OLED显示 ---
  43.     updateOledDisplay(lastDisplayedPulse, currentSpeedCmS);
  44.   }
  45. }
复制代码

[td]
环节输入输出核心作用
低速截止          原始脉冲数0 或原始值过滤静态噪声(无电机转动时的误计数)     
EMA 滤波原始脉冲数 / 上次平滑值平滑后脉冲数         抑制大幅波动,保留趋势
变化阈值平滑后脉冲数 / 上次显示值       稳定显示值过滤 ±1~2 的微小波动,保持显示稳定


项目完整代码

  1. /**************************************************************************************
  2. 文件: /DRV8833_Motor_Stable_Speed/DRV8833_Motor_Stable_Speed.ino
  3. 作者:零知实验室(深圳市在芯间科技有限公司)
  4. -^^- 零知开源,让电子制作变得更简单! -^^-
  5. 时间: 2025-12-2 16:36:27
  6. 说明: 基于ESP32的电机稳速测速系统。通过红外传感器检测脉冲,结合EMA滤波和阈值算法计算稳定速度,并在OLED上实时显示。可通过串口调节电机PWM速度。
  7. ***************************************************************************************/

  8. #include <Wire.h>
  9. #include <Adafruit_GFX.h>
  10. #include <Adafruit_SSD1306.h>

  11. // ================= 用户可调参数 (校准区) =================
  12. // 1. 脉冲-距离转换系数 (cm/脉冲):根据实际硬件校准
  13. // 理论计算: 轮径25.75mm × π ÷ 码盘20格 ≈ 0.4045 cm/脉冲
  14. // 校准规则:测量距离偏短→增大;偏长→减小
  15. float cmPerPulse = 0.4045;

  16. // 2. EMA滤波平滑系数 (0.01~1.0)
  17. // 小值→更平滑但响应慢;大值→响应快但抖动大
  18. const float emaFilterAlpha = 0.3;

  19. // 3. 速度更新阈值 (死区):抑制微小波动
  20. // 仅当脉冲变化超过此值时更新显示,避免数值频繁跳动
  21. const float speedUpdateThreshold = 2.0;

  22. // 4. 最小有效脉冲数:过滤静态噪声
  23. // 1秒内脉冲数小于此值→视为电机停止
  24. const int minValidPulse = 2;

  25. // ================= 硬件引脚定义 =================
  26. #define OLED_SCREEN_WIDTH 128    // OLED屏幕宽度
  27. #define OLED_SCREEN_HEIGHT 64    // OLED屏幕高度
  28. #define OLED_I2C_ADDR 0x3C       // OLED默认I2C地址
  29. Adafruit_SSD1306 oledDisplay(OLED_SCREEN_WIDTH, OLED_SCREEN_HEIGHT, &Wire, -1);

  30. // DRV8833电机驱动引脚
  31. #define MOTOR_DRV_AIN1 12        // 电机PWM控制引脚1
  32. #define MOTOR_DRV_AIN2 13        // 电机方向/停止控制引脚2
  33. #define INFRARED_SENSOR_PIN 14   // 红外对射传感器信号引脚

  34. // ESP32 PWM配置参数
  35. #define PWM_CHANNEL_MOTOR 0      // 电机PWM通道1
  36. #define PWM_CHANNEL_STANDBY 1    // 电机备用通道2
  37. #define PWM_RESOLUTION 8         // PWM分辨率(8位→0~255)
  38. #define PWM_FREQUENCY 1000       // PWM频率(1kHz,适配DRV8833)

  39. // ================= 全局变量 =================
  40. volatile unsigned long sensorPulseCount = 0;   // 传感器原始脉冲计数
  41. volatile unsigned long lastSensorIsrTime = 0;  // 传感器中断上次触发时间(去抖用)

  42. // 滤波后数据存储
  43. float filteredPulseCount = 0.0;    // EMA滤波后的脉冲数
  44. float currentSpeedCmS = 0.0;       // 当前稳定速度(cm/s)
  45. float lastDisplayedPulse = 0.0;    // 上一次显示的脉冲数

  46. int currentMotorPwm = 0;           // 当前电机PWM输出值

  47. // ================= 硬件定时器配置 =================
  48. hw_timer_t *speedSampleTimer = NULL;          // 速度采样定时器
  49. volatile unsigned int timerInterruptCount = 0;// 定时器中断计数(10次=1秒)
  50. portMUX_TYPE timerCriticalMux = portMUX_INITIALIZER_UNLOCKED; // 临界区保护锁

  51. // ================= 中断服务函数 =================
  52. /**
  53. * @brief 红外传感器中断服务函数(上升沿触发+1ms去抖)
  54. * 功能:过滤毛刺信号,统计有效脉冲数
  55. */
  56. void IRAM_ATTR infraredSensorIsr() {
  57.   unsigned long currentIsrTime = micros();
  58.   // 1ms去抖:两次中断间隔>1000μs→视为有效脉冲
  59.   if (currentIsrTime - lastSensorIsrTime > 1000) {
  60.     portENTER_CRITICAL_ISR(&timerCriticalMux); // 禁止其他中断,保护计数变量
  61.     sensorPulseCount++;
  62.     portEXIT_CRITICAL_ISR(&timerCriticalMux);
  63.     lastSensorIsrTime = currentIsrTime;
  64.   }
  65. }

  66. /**
  67. * @brief 定时器中断服务函数(100ms触发一次)
  68. * 功能:累计中断次数,触发1秒一次的速度计算
  69. */
  70. void IRAM_ATTR speedSampleTimerIsr() {
  71.   portENTER_CRITICAL_ISR(&timerCriticalMux);
  72.   timerInterruptCount++;
  73.   portEXIT_CRITICAL_ISR(&timerCriticalMux);
  74. }

  75. // ================= 辅助函数 =================
  76. /**
  77. * @brief 设置电机PWM速度
  78. * @param pwmVal: PWM值(0~255),0→停止,越大转速越快
  79. */
  80. void setMotorPwmSpeed(int pwmVal) {
  81.   currentMotorPwm = constrain(pwmVal, 0, 255); // 限制PWM范围(0~255)
  82.   if (currentMotorPwm == 0) {
  83.     ledcWrite(PWM_CHANNEL_MOTOR, 0);
  84.     ledcWrite(PWM_CHANNEL_STANDBY, 0); // 两通道均为0→电机停止
  85.   } else {
  86.     ledcWrite(PWM_CHANNEL_MOTOR, currentMotorPwm);
  87.     ledcWrite(PWM_CHANNEL_STANDBY, 0); // DRV8833正转模式
  88.   }
  89. }

  90. /**
  91. * @brief 更新OLED显示内容
  92. * @param dispPulse: 显示用脉冲数(滤波后)
  93. * @param dispSpeed: 显示用速度(cm/s)
  94. */
  95. void updateOledDisplay(float dispPulse, float dispSpeed) {
  96.   oledDisplay.clearDisplay();
  97.   
  98.   // 标题栏
  99.   oledDisplay.setTextSize(1);
  100.   oledDisplay.setCursor(0, 0);
  101.   oledDisplay.println("STABLE SPEED METER");
  102.   oledDisplay.drawLine(0, 10, OLED_SCREEN_WIDTH, 10, SSD1306_WHITE);

  103.   // 速度显示(大号字体)
  104.   oledDisplay.setCursor(0, 20);
  105.   oledDisplay.setTextSize(2);
  106.   oledDisplay.print(dispSpeed, 2); // 保留2位小数,提高精度
  107.   oledDisplay.setTextSize(1);
  108.   oledDisplay.println(" cm/s");

  109.   // 辅助信息(PWM+脉冲数)
  110.   oledDisplay.setCursor(0, 45);
  111.   oledDisplay.print("PWM: "); oledDisplay.print(currentMotorPwm);
  112.   
  113.   oledDisplay.setCursor(64, 45);
  114.   oledDisplay.print("Pulse: "); oledDisplay.print((int)dispPulse);

  115.   oledDisplay.display();
  116. }

  117. // ================= 初始化函数 =================
  118. void setup() {
  119.   // 串口初始化(115200波特率,用于控制和波形输出)
  120.   Serial.begin(115200);
  121.   while (!Serial); // 等待串口就绪(兼容部分开发板)
  122.   
  123.   // OLED初始化
  124.   if (!oledDisplay.begin(SSD1306_SWITCHCAPVCC, OLED_I2C_ADDR)) {
  125.     Serial.println("OLED Init Failed!");
  126.     while (1); // 初始化失败则卡死,提示错误
  127.   }
  128.   oledDisplay.setTextColor(SSD1306_WHITE); // 设置字体颜色为白色

  129.   // PWM初始化(适配DRV8833)
  130.   ledcSetup(PWM_CHANNEL_MOTOR, PWM_FREQUENCY, PWM_RESOLUTION);
  131.   ledcSetup(PWM_CHANNEL_STANDBY, PWM_FREQUENCY, PWM_RESOLUTION);
  132.   ledcAttachPin(MOTOR_DRV_AIN1, PWM_CHANNEL_MOTOR);
  133.   ledcAttachPin(MOTOR_DRV_AIN2, PWM_CHANNEL_STANDBY);

  134.   // 红外传感器初始化(下拉输入+上升沿中断)
  135.   pinMode(INFRARED_SENSOR_PIN, INPUT_PULLDOWN);
  136.   attachInterrupt(digitalPinToInterrupt(INFRARED_SENSOR_PIN),
  137.                   infraredSensorIsr, RISING);

  138.   // 速度采样定时器初始化(100ms中断一次)
  139.   speedSampleTimer = timerBegin(0, 80, true); // 定时器0,80分频(1MHz计数),向上计数
  140.   timerAttachInterrupt(speedSampleTimer, &speedSampleTimerIsr, true); // 绑定中断函数
  141.   timerAlarmWrite(speedSampleTimer, 100000ul, true); // 100000×1μs=100ms,自动重载
  142.   timerAlarmEnable(speedSampleTimer); // 使能定时器中断
  143.   
  144.   // 系统就绪提示
  145.   Serial.println("=== System Ready ===");
  146.   Serial.println("Usage: Send PWM value (0~255) via Serial to control motor");
  147.   Serial.println("Serial Output Format: RawPulse | FilteredPulse | Speed(cm/s)");
  148.   Serial.println("====================");
  149. }

  150. // ================= 主循环 =================
  151. void loop() {
  152.   // 1. 串口接收PWM指令,控制电机速度
  153.   if (Serial.available() > 0) {
  154.     int inputPwm = Serial.parseInt(); // 读取串口发送的PWM值
  155.     // 验证输入有效性(仅接收0~255的数值)
  156.     if ((Serial.read() == '\n' || Serial.read() == '\r') && inputPwm >= 0 && inputPwm <= 255) {
  157.       setMotorPwmSpeed(inputPwm);
  158.       Serial.printf("Motor PWM Set to: %d\n", currentMotorPwm);
  159.     } else {
  160.       Serial.println("Invalid Input! Please send number between 0~255");
  161.     }
  162.   }

  163.   // 2. 每1秒采样一次,计算稳定速度(定时器中断累计10次=1秒)
  164.   if (timerInterruptCount >= 10) {
  165.     portENTER_CRITICAL(&timerCriticalMux); // 临界区保护,避免数据冲突
  166.     unsigned long rawPulse = sensorPulseCount; // 读取原始脉冲数
  167.     sensorPulseCount = 0;                      // 重置脉冲计数
  168.     timerInterruptCount = 0;                   // 重置定时器计数
  169.     portEXIT_CRITICAL(&timerCriticalMux);

  170.     // --- 核心算法:数据平滑+稳定速度计算 ---
  171.     // A. 静态噪声过滤:低速截止
  172.     if (rawPulse < minValidPulse) {
  173.       rawPulse = 0;
  174.       filteredPulseCount = 0.0; // 强制归零,避免噪声导致的虚假速度
  175.     }

  176.     // B. EMA指数加权滤波:平滑原始脉冲数据
  177.     // 公式:滤波后值 = α×当前测量值 + (1-α)×上一次滤波值
  178.     filteredPulseCount = (emaFilterAlpha * rawPulse) + ((1.0 - emaFilterAlpha) * filteredPulseCount);

  179.     // C. 阈值判断:仅当脉冲变化足够大时更新显示(抑制微小波动)
  180.     if (abs(filteredPulseCount - lastDisplayedPulse) > speedUpdateThreshold || rawPulse == 0) {
  181.       lastDisplayedPulse = filteredPulseCount; // 更新显示用脉冲数
  182.     }

  183.     // D. 计算最终稳定速度(cm/s)
  184.     currentSpeedCmS = lastDisplayedPulse * cmPerPulse;

  185.     // --- 串口输出(格式固定,适配波形工具) ---
  186.     // 输出格式:Raw脉冲数 | 滤波后脉冲数 | 速度(cm/s),保留2位小数
  187.     Serial.print(rawPulse);
  188.     Serial.print(" | ");
  189.     Serial.print(filteredPulseCount, 1); // 滤波后脉冲数保留1位小数
  190.     Serial.print(" | ");
  191.     Serial.println(currentSpeedCmS, 2); // 速度保留2位小数,波形更细腻

  192.     // --- 更新OLED显示 ---
  193.     updateOledDisplay(lastDisplayedPulse, currentSpeedCmS);
  194.   }
  195. }
复制代码

系统流程图



三、项目结果演示校准全过程1)硬件安装与接线
        按接线表连接所有器件,确保:

红外对射传感器与码盘间距 5~10mm,对齐齿隙;DRV8833 供电电压匹配电机;OLED I2C 地址为 0x3C
2)传统 PID 方案测试
        烧录原始 PID 代码,打开OLED 显示屏上的局域网地址,观察脉冲波形变化

原始脉冲数波动 52~71,抖动严重,无法稳定显示
3)改进方案校准
        烧录改进后代码,进行以下校准:

距离校准(CM_PER_PULSE):
        ①用尺子量取 1 米(100cm)距离,在串口发送 PWM=150,让电机带动轮子沿直线走 1 米;
        ②记录串口打印的Raw值(假设为 247),则CM_PER_PULSE=100/247≈0.405,修改代码后重新上传
滤波系数调节(FILTER_ALPHA):
        若速度仍抖,减小 α(如 0.2);若响应过慢,增大 α(如 0.4)
阈值调节(CHANGE_THRESHOLD):
        微小波动多则增大至 2.5~3.0;响应迟缓则减小至 1.5~2.0
4)稳定测试
        发送不同 PWM 值(50~255),观察串口和 OLED:

串口打印:Raw 值波动 ±3~5,但 Filtered 值波动≤±1,DispSpeed 波动≤±0.3 cm/s;
OLED 显示:速度值稳定,无频繁跳变

参数调节指南[td]
参数默认值调节范围作用调节建议
CM_PER_PULSE0.4045        0.1-1.0每个脉冲对应的距离         根据实际测量校准
FILTER_ALPHA0.30.01-1.0          滤波平滑系数值越小越平滑但响应慢      
CHANGE_THRESHOLD     2.00.5-10.0变化检测阈值值越大显示越稳定
MIN_PULSE_CUTOFF21-10最小有效脉冲过滤静止噪声

稳定速度输出波形图
打开调试功能/串口绘图仪→设置波特率→观察波形脉冲和速度变化:



速度波形基本呈水平线,波动幅度≤±0.3 cm/s,无大幅跳变

视频演示


串口发送 PWM 指令(255→127→90→0)、实时展示 OLED 屏,速度值稳定更新,PWM 与原始脉冲数同步显示、串口波形区速度波形平稳,无剧烈波动

四、DRV8833 与红外对射传感器技术讲解DRV8833 电机驱动模块工作原理
        DRV8833 内部集成双 H 桥驱动电路,H 桥由 4 个 MOS 管组成,通过控制 MOS 管的导通 / 截止实现电机正转、反转、停止和调速

正转:AIN1=PWM,AIN2=LOW;
反转:AIN1=LOW,AIN2=PWM;
停止:AIN1=LOW,AIN2=LOW;
调速:改变 PWM 占空比(0~255),占空比越大,电机转速越高


红外对射计数传感器工作原理
传感器由红外发射管和接收管组成,呈对射式安装:

        无遮挡时:发射管发出的红外光被接收管接收,接收管导通,输出低电平;有遮挡时(码盘齿经过):红外光被阻断,接收管截止,输出高电平;电机转动时,码盘的齿和间隙交替遮挡红外光,传感器输出连续脉冲,脉冲数 = 码盘齿数 × 转动圈数。
计数逻辑
本项目电机码盘为 20 格(20 齿 + 20 间隙),电机转 1 圈,传感器输出 20 个脉冲;结合轮径计算每脉冲对应的距离:

每脉冲距离(cm)= 轮子周长(cm)/ 码盘齿数 =(π×轮径cm)/20


五、常见问题解答(FAQ)Q1:为什么我的脉冲计数仍然不稳定?
        A:请按以下步骤排查:检查传感器安装是否牢固,码盘是否偏心;测量传感器输出信号,增大 FILTER_ALPHA 至 0.4~0.5,或增大 CHANGE_THRESHOLD 至 2.5~3.0,尝试更小的值(如0.2);检查电源稳定性
Q2:电机不转动
        A:请检查: DRV8833 供电,确保供电电大于等于电机工作电压; PWM 值大于等于70,电机启动需要最小电压驱动;DRV8833 模块引脚的 OUT1/OUT2 正确连接到电机,,AIN1=12,AIN2=13
项目资源整合
        DRV8833数据手册:    DRV8833 Dual H-Bridge Motor Driver datasheet (Rev. E)
        OLED SSD1306库文件:    Adafruit_SSD1306

65d7c9962f4448039bb6c2ab319e7b1f.png (27.48 KB, 下载次数: 0)

65d7c9962f4448039bb6c2ab319e7b1f.png
分享到:
回复

使用道具 举报

您需要登录后才可以回帖 注册/登录

本版积分规则

关闭

站长推荐上一条 /2 下一条