恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

STM32电机驱动与编码器反馈:从零搭建机器人底盘的第一步

  • 首页
  • 资讯中心
  • /
  • STM32电机驱动与编码器反馈:从零搭建机器人底盘的第一步

相关资讯

Spring Boot 3.5.9工程实践:升级、监控与虚拟线程落地指南 2026/10/5 8:15:45
使用jps jstack排查系统问题 2026/10/5 8:15:45
Cursor插件开发全解析:plugin.json、TypeScript SDK与CLI协同机制 2026/10/5 8:15:45

最新资讯

51单片机与SYN6288语音合成芯片的中文播报系统实战
Python + Django + Vue 前后端分离的音乐管理系统实战
湖北珞珈实验室Android工程师:岗位职责深度拆解与面试准备
NLFM波形设计:分段线性逼近与多目标优化提升SAR成像质量
PetaLinux设备树单独编译实战:从DTG机制到dtc命令
计算机专业毕业生就业公示信息解析与职业发展启示

今日推荐

第26课:OpenClaw|日志审计与问题诊断:把日志链路改到 TaoToken 的排查清单
YOLOv5 OBB旋转框训练实战:从DOTA数据准备到调参避坑全流程
Zeron 终端、Worktree 与 Diff 面板:像 IDE 一样查看并驱动你的代码变更

本周热门

MR25H40CDF + PIC18F65K40:工业记录仪高可靠存储实战
基于STM32的数控恒压恒流电源设计:从硬件到PID调参全解析
LT9211 MIPI重定时器原理与双路扇出实战指南

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

STM32电机驱动与编码器反馈:从零搭建机器人底盘的第一步

发布时间:2026/10/5 8:15:45
STM32电机驱动与编码器反馈:从零搭建机器人底盘的第一步 做机器人底盘最绕不开的一件事就是让电机转起来并且知道它转了几圈、转了多少速度。很多朋友一上来就急着跑ROS导航结果底盘一动就歪、轮子乱抖、里程计报错回头查半天才发现是底层电机驱动和编码器反馈没做扎实。这个系列讲的就是怎么从零做一个基于STM32的小车底盘后续再往上跑ROS运动控制与导航。这一节先把最基础、也最关键的部分搞定STM32电机驱动与编码器读取反馈。我把自己的实际搭建过程、踩过的坑、值得复用的配置都写下来希望能让刚接触这堆硬件的朋友少走点弯路。1. 项目整体拆解为什么第一节先啃电机与编码器1.1 底盘控制与ROS导航的关系很多初学ROS的朋友会有一个误解以为买块树莓派装上ROS、跑个gmapping和move_base小车就能自己跑了。实际上ROS只是一个“大脑”它发出的速度指令是cmd_vel说到底就是“左轮每秒转多少弧度、右轮每秒转多少弧度”这样的抽象信息。真正让轮子转动、并且把实际转速反馈回来的是STM32这一层。底盘控制可以拆成下面这几块接收上层指令比如从串口收到cmd_vel数据帧包含线速度和角速度。运动学解算把线速度角速度换算成左右轮的目标转速后面讲运动学时会细说。电机驱动STM32输出PWM通过驱动芯片控制电机电枢电压从而控制电机转速。编码器反馈电机轴上的编码器返回脉冲STM32通过计数器读脉冲次数计算实际转速。闭环控制把目标转速和实际转速做PID让轮速稳定在期望值。如果把ROS导航比作“开车时的导航软件”那STM32底盘就是“油门、刹车、转向系统”。导航软件告诉你“前方路口右转”但真正控制方向盘和车速的是底盘那一层。同样ROS发的是cmd_vel但真正让电机按照对应转速转起来的是STM32。所以这个系列第一节先做电机驱动和编码器目的很明确先把“手脚”做好再谈“大脑”。1.2 第一节的学习目标与硬件配置这一节其实解决了两个核心问题第一怎么让电机转起来并且能控制转速第二怎么知道电机实际的转速和转向。我这次用的硬件配置比较典型也是很多入门小车的通用配置主控STM32F103C8T6蓝色Pill板72MHz主频资源够用价格便宜。电机带霍尔编码器的直流减速电机常见减速比1:30编码器线数13PPR额定电压6V左右。电机驱动模块TB6612FNG也试过AT8236后面会讲两者的差异。供电两节18650锂电池串联约7.4V经过降压给MCU电机直接接电池或者经过驱动模块。调试USB转TTL串口模块用来打印编码器数据。在开始操作前我建议你先把硬件之间用杜邦线连接好但先别急着接电机电源等代码写好了再通电这样不容易烧东西。2. 电机驱动方案选型不是越大越猛2.1 TB6612、AT8236、L298N怎么选电机驱动模块可能是新手最容易纠结的地方。市面上最常见的三种是L298N、TB6612FNG、AT8236。我三种都用过简单说说我的真实感受。L298N经典模块很多教程都拿它入门。但它的压降比较大电源利用率低开关频率也低PWM频率稍微一高就容易发热。适合大功率电机或者你手里刚好有闲置模块。对小体型小车来说它体积大、效率低我其实不太推荐。TB6612FNG这是入门小车的主流选择内部是MOSFET构成的H桥压降小体积小双路驱动逻辑电压兼容3.3V和5V。PWM频率到10kHz~20kHz都没问题非常适合STM32直连。AT8236这个模块我最近用得比较多它电流更大单路可以到6A左右内阻低适合扭力比较大的直流电机。电源范围也宽从5.5V到24V都行。如果你后面想换大一点的车轮、爬坡能力强一点可以一步到位选这个。价格也便宜就是模块本身稍大。选型原则其实很简单先看电机堵转电流再看逻辑电平和PWM频率。电机在启动和堵转时的电流远大于额定电流驱动模块的峰值电流必须大于这个值。比如常见N20电机额定电流几百毫安堵转电流能到1~2ATB6612完全够用如果是37GB520这种大电机堵转电流能到5A以上TB6612就会烫手这时候AT8236更稳。2.2 H桥与PWM调速原理直流电机调速最常用的方式是PWM脉冲宽度调制本质上就是快速开关电机电源通过调节“开”和“关”的时间比例来控制平均电压。开的时间占比叫占空比比如80%占空比平均电压就是电源电压的80%。但电机需要正转和反转光靠一个开关不行所以需要H桥电路。四个开关管组成一个“H”形通过对角开关管的通断就能改变流过电机的电流方向从而实现正反转。TB6612、AT8236这类模块内部已经帮你做好了H桥你只需要给两类信号方向信号比如IN1、IN2一高一低控制转向。通常做法是IN11、IN20正转IN10、IN21反转IN1IN20刹车。速度信号PWM引脚输入一定频率和占空比的方波。占空比越大平均电压越高电机转速越快。许多模块还支持一个独立STBY引脚待机脚必须拉高模块才工作。这个引脚经常被忽略如果电机完全无反应先查它。2.3 供电、共地与关键接线接线看起来很简单但很多问题其实出在供电和共地上。先说共地。STM32的GND、驱动模块的GND、电池的负极必须全部连在一起。否则驱动模块的逻辑输入没有参考电位PWM信号根本识别不了。我之前有一次电机时转时不转最后发现就是GND没接好。再说供电。如果你用7.4V电池给电机供电同时用5V或者3.3V给STM32供电两块电源的参考地一定要统一。很多同学直接把电池正极接驱动模块VM负极接GND然后又用一块独立稳压给STM32供电结果两块电源的地没连信号就是“悬浮”的各种诡异现象都会出来。最后是接线顺序。我建议先接逻辑信号线和编码器线再给驱动模块供主电源最后再让电机转。通主电源之前先用手拨动车轮在串口里观察编码器读数是否变化这样能提前发现接线对不对而不是一上电就满负载跑。3. 电机控制代码实现从CubeMX到能跑3.1 CubeMX配置PWM与GPIO我习惯直接用STM32CubeMX生成初始化代码省去手动写寄存器的时间。以STM32F103C8T6为例电机驱动需要用到两类外设定时器输出PWMGPIO控制方向。第一步打开CubeMX新建项目选好芯片型号后先把系统时钟配置到72MHz。通常在RCC里选HSE外部晶振然后在Clock Configuration里把HCLK拉到72MHz这个操作会直接决定后面定时器频率的计算基准。第二步找一个定时器输出PWM。比如用TIM2的CH1、CH2分别控制左右两个电机。配置方式选择TIM2在Combined Channels里选PWM Generation CH1 CH2然后设置Prescaler分频系数和Counter Period自动重装载值。举个例子定时器时钟72MHz如果我想要PWM频率10kHz分频 (Prescaler) 设为71自动重装载值 (Counter Period) 设为99那么PWM频率等于 72MHz / ((711)*(991)) 10kHz。占空比则通过设置比较寄存器Compare Value来控制范围0~99。第三步配置方向控制引脚。比如PA0和PA1控制左电机正反转PA2和PA3控制右电机把它们配置为GPIO输出初始状态设为低电平。还有一个容易被忽略的点如果驱动模块需要STBY使能引脚也要配置成GPIO输出高电平。调试阶段我建议自然把STBY引脚直接拉高减少一个变量。3.2 电机控制代码与调速逻辑CubeMX生成的工程里PWM默认是停止状态需要在代码里启动PWM输出。同时要封装一个电机控制函数方便后续调用。下面是我一直在用的一套精简代码基于HAL库。先启动PWM// 在 main 中初始化后启动 TIM2 的 CH1 和 CH2 HAL_TIM_PWM_Start(htim2, TIM_CHANNEL_1); HAL_TIM_PWM_Start(htim2, TIM_CHANNEL_2);定义两个电机控制函数。以左电机为例参数为速度值speed正值正转负值反转数值范围 -100 到 100void set_left_motor_speed(int16_t speed) { if (speed 0) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET); // 正转方向 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET); __HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_1, speed 100 ? 100 : speed); } else { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET); // 反转方向 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET); speed -speed; __HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_1, speed 100 ? 100 : speed); } }这段代码的逻辑很直白先根据正负设置两路GPIO方向再把绝对值写入PWM比较寄存器。这里的100对应一个周期内的高电平时间相当于占空比100%。我习惯把set_left_motor_speed这种函数写在motor.c里把调速的核心逻辑和main分离后期如果要再加PID只需要在这个函数外面包一层目标速度计算即可。3.3 控制频率与占空比设置建议调试中我发现电机驱动效果和PWM频率关系很大。频率太低比如1kHz以下电机会有明显的“咔咔”声转速不均匀像是被一顿一顿地驱动。频率太高太高的话MOS管开关损耗变大模块发热而且对电机电感要求也高没必要。常见直流减速电机我建议10kHz~20kHz之间这个范围内电机噪音小、扭矩稳定。如果你是新手建议先用固定占空比比如50测试电机是否转动再慢慢调。同时要注意占空比不是线性对应转速的电机存在死区。也就是说占空比小于某个值比如15时电机可能根本不动这是正常的。到时候PID控制里就要考虑这个死区补偿不然低速时会出现“启动不了一启动又冲太快”的问题。4. 编码器读取反馈让小车知道自己轮子转了多少4.1 编码器原理与四倍频底盘测速最常用的就是增量式编码器。电机后面有一个小码盘上面均匀分布着黑白相间的条纹或者用霍尔传感器检测磁极变化。码盘转动时编码器输出方波脉冲。脉冲数量正比于转动角度所以只要数脉冲就能知道电机转了多少。更常用的是AB相正交编码器它输出两路方波A和B相位相差90度。为什么要两路因为通过判断A和B的相位关系能知道转动方向。A超前B 90度是正转B超前A 90度是反转。两路信号还能组合出4个边沿A上升沿、A下降沿、B上升沿、B下降沿每个边沿都算一次计数这就是“4倍频”相当于把编码器分辨率提高了4倍。举例说一个13PPR的霍尔编码器4倍频后每转有52个脉冲。再乘以减速比30轮子转一圈STM32计数器能数到1560个脉冲。如果轮子直径65mm那每个脉冲对应的距离只有0.13mm左右作为里程计已经相当够用了。4.2 CubeMX配置编码器模式与代码读取STM32的定时器硬件直接支持正交编码器模式不需要自己写逻辑去判断相位非常省事。配置方法在CubeMX里选一个定时器比如TIM4把它的Combined Channels设为Encoder Mode。定时器的CH1、CH2引脚会自动变成编码器A、B输入。把Prescaler设为0Counter Period设为6553516位计数器的最大值。计数模式选Encoder Mode TI1 and TI2这样就工作在4倍频模式。默认情况下定时器计数器是16位的计数值从0到65535。正转时加计数反转时减计数。这里有个巧妙的地方如果把计数器初值设为0反转时计数值会变成65535这个值按int16_t强制转换后就是-1正负号自然就代表了方向。读取代码很简单// 读取当前编码器计数值强制转换为int16_t负数代表反转 int16_t encoder_count (int16_t)__HAL_TIM_GET_COUNTER(htim4); // 读取后立即清零用于固定周期的脉冲计数 __HAL_TIM_SET_COUNTER(htim4, 0);核心思路是“定时清零累计计数”。每当一个固定时间片比如10ms到来读一次计数器然后清零这个值就是该时间片内的脉冲数。把若干时间片的脉冲累加就能算出每秒总脉冲数。4.3 速度计算与转向判断有了脉冲数剩下的就是换算成实际速度。这里我把公式写清楚后面写里程计时也要用。已知条件编码器原始线数PPR 134倍频因子MULT 4。电机减速比GEAR_RATIO 30。轮子直径WHEEL_DIAMETER_MM 65。电机输出轴高速侧每转一圈的脉冲数为PPR * MULT 52。轮子转一圈需要电机转30圈所以轮子一圈的总脉冲数为PULSE_PER_WHEEL_REV 52 * 30 1560如果每秒钟计数为N那么轮子每秒转速为WHEEL_REV_PER_SEC N / 1560线速度SPEED_MM_PER_SEC WHEEL_REV_PER_SEC * PI * WHEEL_DIAMETER_MM例子如果10ms内读到的计数是52那么一秒的计数是5200轮子每秒转5200/1560 3.33圈轮子周长约204.2mm线速度约680mm/s。需要注意的是这些参数要和你的实际硬件一致。不同厂家“13线”编码器的含义略有差异有的标称“13线”是指码盘转一圈输出13个脉冲有的却是指减速后输出轴转一圈13个脉冲。最靠谱的办法是焊好接线后用手把轮子推着转一整圈看串口打印出来的脉冲数是多少这就是你实际的PULSE_PER_WHEEL_REV。转向判断很简单编码器计数值如果为正就是正转方向为负就是反转方向。在代码里方向信息已经融合在正负号里了不需要额外读取方向引脚。4.4 给ROS层的接口预留这一节虽然还没开始写ROS代码但底层数据接口的设计要提前想好。后面要接ROS导航底盘实际上只需要暴露两个数据轮速左右轮的实时速度单位可以用m/s或者rad/s都行。里程计小车从启动到现在的位置累计量包括x、y坐标和航向角。在代码结构上我建议把编码器数据放到一个独立模块里比如encoder.c对外提供三个接口void encoder_init(void); void encoder_update(void); // 定时调用负责读取计数、计算速度 float get_left_wheel_speed(void); float get_right_wheel_speed(void);这样后面往上写PID也好、写串口通信也好直接调get_left_wheel_speed()就行。不用每加一个功能就把编码器读取逻辑重写一遍。5. 踩坑实录电机与编码器常见问题排查5.1 电机不转、抖动、反转问题我把实际调试中遇到最多的问题整理成下面这张表方便你对照排查现象可能原因排查方法电机完全不动STBY引脚没拉高用万用表量STBY电压确认是高电平电机完全不动PWM没启动检查是否调用了HAL_TIM_PWM_Start电机完全不动供电不足电机电源正负极是否接对电压是否被拉低电机抖动、正反乱跳共地问题所有GND连一起用万用表量GND之间电阻电机一直反转IN1、IN2接反交换IN1和IN2或者在代码里对调方向电机转起来有高频啸叫PWM频率过低把PWM频率提到10kHz以上有一个坑特别常见电机一启动STM32就重启。这多半是电源问题。电机启动瞬间电流很大会把电源电压瞬间拉低如果STM32和电机共用同一路电源且没有足够电解电容单片机会复位。解决办法是电机电源和单片机电源分开或者在电池两端并联一个大电容220uF以上再在单片机电源入口加一个100nF去耦电容。5.2 编码器读数异常排查编码器的问题通常不是“没读数”而是“读数乱跳”、“方向错误”或者“数据只增不减”。我的排查经验如下。第一确定接线。编码器A、B两相要分别接到定时器的CH1、CH2引脚不要接反。接反了方向判断会反但脉冲数一般没问题。如果方向反了可以直接对调A和B不用改代码。第二确定共地。编码器输出信号是相对编码器电源的地来说的如果编码器的GND和STM32的GND没连信号完全没法读。这个点便宜却关键至少一半的编码器异常问题都是共地没接好。第三排除电机电磁干扰。电机运行时产生火花和反电动势严重时会干扰编码器信号尤其用长杜邦线的时候。我遇到过编码器在电机静止时读数正常一转动就疯狂跳数的情况。解决的办法是把编码器线尽量短使用双绞线或者屏蔽线电机动力线跟编码器信号线分开走线不要绑在一起。有条件的话在电机两端并联一个104陶瓷电容也能减少一部分干扰。第四检查计数器溢出。16位计数器范围是-32768到32767。如果读数的周期太长比如一秒读一次高速转动时计数早就溢出好几轮了数据就会乱掉。解决办法是提高读取频率10ms读一次比较稳妥同时用int16_t强制转换把计数方向带出来。5.3 调试工具与流程建议调试阶段千万别一上来就跑PID先用最简单的开环模式把每个模块调通。我的调试流程是第一步让电机以固定占空比转动看是否顺畅稳定。第二步在串口打印编码器原始计数用手拨动车轮确认计数变化。第三步编写一个简单的测速程序定时打印左右轮速度推着小车走一段看速度是否合理。第四步再做闭环控制。串口打印这里有个小技巧。如果你用的是STM32CubeMX生成的工程想用printf打印调试信息需要重定向一下。// 在 usart.c 或者 main.c 中实现 #include stdio.h int fputc(int ch, FILE *f) { HAL_UART_Transmit(huart1, (uint8_t *)ch, 1, 0xFFFF); return ch; }然后就可以直接printf(encoder left %d\n, left_count);。建议每次打印不要太多数据10ms一次比较合适太频繁串口会卡。另外一个非常推荐的工具是逻辑分析仪。便宜的24M采样率逻辑分析仪就够用了。把编码器A、B两路信号接到逻辑分析仪上可以直接看到波形判断编码器输出是正常方波还是被干扰的毛刺比盲猜高效得多。6. 下一步与个人经验6.1 第一节到第二节的衔接规划第一节做完之后你的底盘应该已经具备两个能力能通过PWM控制电机转速能通过编码器测量实际转速。下一节要做的就是把这个速度闭环起来也就是PID控制。我在实际做的时候发现只有PID收敛后底盘才能稳定跑直线后面的ROS导航才有意义。否则即使ROS发了cmd_vel你左右轮转速不一致小车走个两米就偏了激光雷达建图也建不干净。等PID完成之后就可以做运动学解算了。把ROS下发的cmd_vel线速度和角速度换算成左右轮的转速再把编码器测得的实际速度和累计里程发回给ROS的/odom话题。这时候整个底盘的底层就闭环了。6.2 我的一些实际体会与建议这里分享几个自己在反复调试中积累的经验。第一复位之后先别急着上路先观察固定占空比下电机转动的平稳性。如果你发现同一个占空比下两次实验速度差很多说明供电或者机械阻力有问题先解决机电问题再做控制不然PID调死都调不好。第二不要迷信高分辨率编码器。对于普通差速底盘13PPR到500线左右都够用。分辨率过高反而会在高速时计数溢出、在低速时引入高频量化噪声增大PID调参难度。第三开始做PID之前先把编码器的速度滤波做好。常见做法是滑动平均或者一阶低通滤波。一阶低通很实用公式是filtered filtered * 0.8 current * 0.2系数根据你的控制周期调整目的是剔除编码器计数在临界值附近的抖动。还有一个小技巧调试时用小纸条贴在轮子上做一个标记每次手动转一圈观察编码器累计值是不是吻合。这样可以快速验证编码器参数设置是否正确比盯着代码猜高效很多。这个系列的第一节就讲到这里。电机驱动和编码器看起来基础但它们是整个机器人底盘的地基。把这一步做扎实了后面的PID、运动学解算、ROS导航才有底气往下走。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号