STM32智能小车实战:从选型到PID调试的完整嵌入式设计

发布时间:2026/9/17 22:43:44
STM32智能小车实战:从选型到PID调试的完整嵌入式设计 简介大学生电子设计竞赛智能小车项目完整设计方案文档面向电子、嵌入式及自动化专业学生与参赛团队。内容涵盖设计任务概述、基本任务与发挥部分的设计思路并从车体结构、控制模块、电源管理、电机驱动、避障与循迹等功能单元展开系统方案论证与选型分析涉及Arduino/Raspberry Pi微控制器选型、锂电池BMS供电设计、H桥驱动电路等关键知识点。文档还包含硬件系统的设计与功能实现覆盖微控制器电路、电源电路、电机驱动电路原理全文目录结构完整从方案论证到最终方案确定均给出清晰的比选过程。资源为1个DOC文件约2.04MB可作为赛前方案规划、模块选型和设计报告撰写的直接参考。目前已有444人学习下载适合正在备赛或想系统梳理智能小车设计流程的同学使用。1. 电子设计大赛里智能小车为什么总被当成“完整材料”来交智能小车在电子设计大赛中的定位不是“能跑就行”的玩具而是把需求拆解、方案选型、电路设计、机械装配、嵌入式软件开发、现场调试这六个环节都浓缩在同一个箱体里的课题。评审和指导教师看“完整材料”本质是验证材料能否支撑复现你画的电路图和实际飞线一致吗你写的标定数据和赛道实测对得上吗硬件选材没有依据代码注释再漂亮复盘时仍会露馅。对参赛者来说把小车当作一个缩小的产品原型来管理整个流程比任何单项技巧都更值得投入。这套思路不只适用于当年的赛题在课程设计、工创赛智能物流小车、智能搬运小车这些同样依赖小车载体的竞赛里逻辑也一致。下面按我从选型到成稿的完整路径把电子设计大赛智能小车从零做一遍。2. 硬件选型与电路设计STM32F103ZET6 的引脚分配逻辑2.1 主控选型从 51 单片机到 Arduino最后落回 STM32先解决“用什么主控”。电子设计大赛的智能小车题很少只考察一个传感器一般是多路循迹、避障、遥控切换、显示或蓝牙模块叠加。51 单片机的问题是定时器和中断资源有限四路编码器计数加两个 PWM 通道加串口基本就把外设占满后续加陀螺仪或 OLED 需要另想方案。Arduino 的优点是生态成熟库函数完备但大部分型号的 ADC 采样和数字滤波在同一频率下互相挤占用在需要严格时序的电机闭环上会出现“代码能跑但波形不稳”的状况。STM32F103ZET6 的优势在资源和性价比64 KB RAM、512 KB Flash内置 8 个定时器和 5 个 USART同时跑循迹、避障、蓝牙和显示不会出现资源紧张。这里的 ZET6 指 LQFP144 封装的增强型引脚数足够把每个功能模块单独引出一个接口调试时不需要频繁拔线重插。对于三到五天的竞赛周期引脚宽松比主频高低更关键。很多往年队伍在这里犯的错是选了最小系统板加转接模块结果外设一多飞线绕成一团排查一个问题要拆半小时调整方案和更换线缆的空间都没有了。2.2 电机驱动与编码器接口分配电机驱动选型有一条容易被忽视的指标导通压降。L298N 使用双极性晶体管典型饱和压降约 1.8 V 至 2.5 V两路全桥工作时发热明显在 7.4 V 电池供电下实际送到电机端的电压可能只有 5 V 左右转速和扭矩都打折扣。TB6612 是 MOSFET 工艺导通压降通常低于 0.5 V同样 7.4 V 输入能多给出接近 1.5 V 的余量低速启动时的堵转表现明显更好。若驱动 130 或 370 电机TB6612 的持续电流足够换到 540 等大功率电机时才需要考虑 BTN7971 或分立 MOS 全桥。竞赛底盘的常见做法是两个直流减速电机各带一个霍尔编码器编码器 A/B 相分别接 STM32 的 TIMx_CH1 和 TIMx_CH2用编码器接口模式计数不再占用外部中断。以下接口分配表是常规做法可直接作为设计参考功能模块引脚信号STM32F103ZET6 推荐引脚说明右电机 PWMPWM 输入PA8 (TIM1_CH1)高级定时器可输出互补 PWM右电机方向DIRPC6普通 GPIO左电机 PWMPWM 输入PA11 (TIM1_CH4)与右电机同用 TIM1保证同步左电机方向DIRPC7普通 GPIO编码器 A/BA、B 两相PB6、PB7TIM4_CH1/CH2编码器接口模式计数循迹模块4 路数字输出PC0~PC3上拉输入注意模块输出极性把两路 PWM 放在同一个 TIM1 上是为了让左右电机的 PWM 频率和相位完全一致避免出现“频率不同导致蜂鸣声差异、低速不线性”这种很难排查的现场问题。编码器使用硬件编码器接口模式比外部中断计数省 CPU还能自动处理方向判别后续做速度闭环时读数频率可以拉高到 100 Hz 以上。2.3 两层电源树先算电流再决定 DC-DC 与 LDO 的搭配电源是赛场故障率最高的子系统典型故障是“一启动电机单片机就重启”。原因通常是供电方案里把电机和单片机共用了一路 LDO或者 DC-DC 的输出电容容量不足。按常见配置估算主控板约 100 mA循迹模块约 20 mAOLED 约 30 mA舵机和超声波模块合计约 300 mA两个电机空载约 500 mA、堵转可能超过 2 A。总电流波动范围很大因此不能用单一 5 V 线性稳压一次带全部负载。常规做法是两级架构7.4 V 锂电池直接给电机驱动供电5 V 由 DC-DC 降压模块提供3.3 V 再由 LDO 从 5 V 转换。DC-DC 选择开关频率高于 300 kHz 的模块输出端并联 470 uF 电解电容和 100 nF 陶瓷电容避免电机启动瞬间电压跌落触发掉电复位。这里有一个容易忽略的细节电机驱动板上的逻辑电源和电机电源必须共地。若不共地编码器信号在单片机侧没有参考地读数会随机跳动。具体接法是电池负极同时接到驱动板 GND、DC-DC 输入 GND 和单片机 GND形成星形单点接地不要把电机大电流回流路径和传感器地线串接在同一条飞线上。提示先做一次整车上电测试用示波器观察单片机 3.3 V 引脚在电机启停瞬间的跌落幅度。跌落超过 0.3 V 时优先补 DCDC 输出电容而不是换更强的主控。3. 装配与标定循迹传感器、编码器和线缆布局3.1 循迹传感器的安装高度和前向间距循迹模块大多数使用 TCRT5000 红外对管检测距离约 1 cm 到 3 cm。安装高度过高会导致反射光强度不足以触发比较器翻转过低又会让杂散反射误判。实际调试时先用一张白纸和一段黑色电工胶带验证模块输出电平再装车。传感器支架固定后把高度调节到 8 mm 到 15 mm 之间以“黑线输出低电平、白底输出高电平”为基准。安装方向上传感器排布方向应垂直于行进方向间距需要根据赛道黑线宽度调整。比如赛道线宽 20 mm四个传感器按 15 mm 间隔排列中间两个探头刚好覆盖线宽两侧探头作为脱离边界的预警。对于电磁三轮智能小车循迹这种使用电感采磁场的方案传感器排布逻辑类似只是将红外探头换成交叉放置的工字电感安装高度需要结合磁场分布重新标定赛前至少要留出半天做磁场实测不要直接照抄网上的固定参数。3.2 轮径标定与编码器换算编码器读数需要先换算成实际速度计算公式是v (脉冲数 ÷ 每转脉冲数) × 轮周长 × 采样周期每转脉冲数由减速电机的减速比和霍尔编码器线数共同决定常见的 370 电机通常是 13 线霍尔乘以 30 减速比等于 390 个脉冲每转但实际值要查电机手册。轮径标定有一个很实用的笨办法在小车侧面贴一条标记推动小车在地面走十圈用卷尺量出总距离反过来算出有效周长。有效周长通常比理论值短 3% 到 8%因为轮胎受压变形。这个误差如果不标定直线行驶距离偏差会在长线赛道末端被放大成明显的入弯偏移。3.3 信号线与电机线的隔离装配时另一类问题是信号干扰。编码器线长距离贴近电机线时PWM 开关噪声会耦合进编码器信号导致计数多跳或少跳。常规规避手段有三个编码器线和电机线分别走底盘两侧使用双绞线传输编码器 A/B 相在编码器供电端并联 100 nF 电容。不能依赖编码器模块自带的滤波电容来解决电机侧传导噪声因为噪声频率通常落在几百 kHz 到几 MHz 之间恰好是陶瓷电容滤波较弱的频段。现场可以用一个简单测试判断干扰是否存在把电机 PWM 固定到 50% 占空比通过串口观察编码器读数方差如果数值在静止时剧烈跳动说明干扰路径没有断开。这种情况下先改线缆走向增加电容反而容易掩盖问题干扰经过温度变化后又会周期性复发。4. 固件架构与核心算法状态机、PID 与循迹控制4.1 软件状态机分解循迹、避障、停车智能小车固件的一个常见分层是每个传感器模块负责采集自己的测量值主循环维护一个状态机把当前位置映射到转向动作。不要把循迹、避障、蓝牙处理写成顺序执行的 if 语句因为一旦某个操作阻塞比如超声波测距的 10 ms 等待会影响编码器计数的及时读取。常见做法是把整个系统拆成三个状态状态进入条件执行动作退出条件循迹超声波距离大于 20 cm根据偏差用 P 控制左右轮转速超声波距离小于 20 cm避障超声波距离小于 20 cm先停车再固定方向转向避障计数减到 0 且距离大于 20 cm停车收到串口停止指令或终点光电触发两个电机 PWM 置 0重新初始化状态机先用有限的优先级决定当前动作来源再在每次主循环中执行一次统一的运动控制函数输出左右电机目标转速最后调用 PID 计算 PWM。下面的代码是一个简化的状态机框架typedef enum { STATE_TRACK, // 循迹 STATE_AVOID, // 避障 STATE_STOP // 停车 } RUN_STATE; RUN_STATE run_state STATE_TRACK; uint8_t avoid_count 0; void main_loop(void) { while (1) { // 1. 读取所有传感器 int track_error read_track_error(sensor_val); // 返回 -2 到 2 的偏差 int obstacle_dist read_ultrasonic_cm(); // 返回厘米值 // 2. 状态迁移 if (obstacle_dist 20) { run_state STATE_AVOID; avoid_count 20; // 记录避障持续周期 } else if (run_state STATE_AVOID avoid_count 0) { avoid_count--; } else { run_state STATE_TRACK; } // 3. 根据状态计算目标速度 set_target_speed(run_state, track_error); // 4. 执行 PID 速度闭环 pid_control(); // 5. 串口发送调试信息 send_telemetry(); delay_ms(10); // 调度节拍 } }代码逻辑是状态迁移放在主循环开头传感器统一采集后由状态机决定目标速度。avoid_count 的作用是给避障动作一个持续时间防止小车越过障碍后立刻又检测到目标而反复摇摆。delay_ms(10) 只是示意真正工程化时要改用定时器调度避免编码器采样与主循环节拍耦合。4.2 编码器速度采样与速度 PID 回路速度闭环的基础是可靠的速度反馈。编码器接口模式的读取方式比较固定在一个定时器里设置中断回调每隔 10 ms 读取一次编码器计数寄存器减去上次值得到脉冲增量再换算成转速。下面的代码展示定时器中断中做速度采样static int16_t last_cnt_right 0; void TIM6_IRQHandler(void) { if (TIM_GetITStatus(TIM6, TIM_IT_Update) ! RESET) { TIM_ClearITPendingBit(TIM6, TIM_IT_Update); int16_t cur_cnt (int16_t)(TIM4-CNT); // 读取硬件计数器 int16_t delta cur_cnt - last_cnt_right; // 本次周期内脉冲增量 last_cnt_right cur_cnt; speed_right_rpm (float)delta * 60.0f / (16.0f * 390.0f) / 0.01f; // 390 为每圈脉冲数, 16 为倍频系数, 0.01 为采样周期(秒) } }这里的转换公式前面已经推导过。需要刻意注意的是 TIM4-CNT 是 16 位寄存器直接读取不能区分正负要先把变量类型定义为 int16_t 再参与计算。倍频系数 16 来自定时器编码器模式的默认配置可以配置成 1 倍、2 倍或 4 倍PWM 输出通道配置不同倍频值也不同。如果不做这个处理高速反转时计数会从负值跳变成一个大正数PID 会误认为电机突然加速。4.3 转向的 P 控制与差值限制循迹转向不需要完整的 PIDP 控制加死区通常就够。把四个传感器的输出映射成一个偏差值比如只有中间两个传感器检测到黑线时为 0左一为 -2右一为 2然后根据偏差设置左右轮目标速度。核心计算是int track_ctrl(int error) { int base_speed 180; // 基础占空比 int kp 30; // 转向增益 int diff; if (error 0) { diff 0; // 死区 } else { diff error * kp; } diff diff 80 ? 80 : diff; diff diff -80 ? -80 : diff; target_right base_speed diff; target_left base_speed - diff; return 0; }转向增益 kp 的初始值有两个经验范围对于 1:30 减速比的电机kp 从 25 到 40 开始试对于 1:50 以上减速比电枢响应变慢kp 需要相应提高。限幅 80 是为了避免转速差过大导致内侧轮反转尤其是窄弯和大角度弯道交替出现时过大的差值会让小车在入弯瞬间甩尾。要让 PID 有效必须先保证编码器读数可靠否则调 K 系数没有意义。5. 现场调试与文档定稿用数据反推问题是哪一层5.1 上电三分钟检查万用表、示波器和串口日志最后阶段最怕的不是代码编译不过而是小车行为异常时无法判断问题属于硬件、算法还是装配。常见做法是建立一组可重复的调试步骤把每次测量的数据固化下来。上电后先不要跑程序测量电池端电压7.4 V 锂电池单节满电 4.2 V两节串联空载应该在 8.2 V 到 8.4 V接入负载后会跌落到 7.5 V 左右这是正常范围。接着看单片机 3.3 V 引脚如果掉到 3.1 V 以下大概率是 DC-DC 输出能力不够或接线压降过大。然后测量驱动板 PWM 输入引脚把示波器时间轴调到 20 us 每格能看到固定频率的方波信号占空比随给定值变化。方波消失或频率不是配置值时先查定时器时钟使能不要怀疑电机。串口是整车状态最直接的观察口把前面提到的 send_telemetry() 扩展为输出全部关键变量左右编码器增量、转速、目标速度和状态机状态。用一段短 Python 脚本把数据存成日志文件import serial, time ser serial.Serial(COM3, 115200, timeout1) with open(log.txt, a) as f: while True: line ser.readline().decode(errorsignore).strip() if line: f.write(f{time.time():.3f},{line}\n) print(line)这个脚本保存了时间戳和原始数据回放时可以按时间对齐传感器变化和运动变化。用手推动小车模拟赛道运行观察编码器读数是否稳定变化如果读数出现瞬间跳变记下对应电机 PWM 占空比再返回查线缆布置。5.2 标准测试记录表以及它在完整材料中的角色参数调整依赖可追溯的数据。建议在材料文档中放一张统一的测试记录表字段包括测试编号、工况描述、输入速度目标、实测左右轮转速、转向偏差、赛道表现、修改参数项、修改后的观测结果。这张表有两个作用一是横向比较不同 kp、积分系数下的小车表现二是向评审展示“参数如何随工况调整”的过程这正是“完整材料”中体现工程能力的地方。5.3 “完整材料”的章节顺序完整材料的组织建议按评审查看的顺序而不是按开发顺序题目需求分析、总体方案论证、硬件电路设计原理图与 PCB、机械装配与标定记录、固件框架与核心代码、关键问题分析与调试记录、最终性能数据与改进方向。调试记录要按“故障-原因-解决”三段式呈现例如编码器读数跳变由电机线缆耦合引起、通过分离布线和增加退耦电容解决这类信息对复现和评分的价值大过贴一份完整代码。本文还有配套的精品资源点击获取