机械电子毕业设计实战:基于嵌入式系统的智能小车控制系统开发
关键词:机械电子毕业设计、STM32、ROS、智能小车、PID、传感器融合
一、毕设痛点:软硬协同三座大山
做智能小车毕设,90% 的同学卡在同三个坑里:
- 硬件“裸奔”——电机一启动,单片机立刻复位;
- 软件“裸跑”——超声波+IMU 数据各说各话,小车原地“画龙”;
- 调试“裸眼”——串口打印一关,bug 就成玄学。
我踩坑的顺序是:先调电机,再调传感器,最后才发现控制周期抖动才是万恶之源。下文把每一步拆给你看,让你少熬三个通宵。
二、技术选型:三分钟拍板,别纠结
| 维度 | Arduino Uno | STM32F103 | STM32F407 |
|---|---|---|---|
| 主频 | 16 MHz | 72 MHz | 168 MHz |
| 定时器 | 8 bit×2 | 16 bit×4 | 32 bit×2 |
| 片上 ADC | 10 bit | 12 bit | 12 bit×3 |
| 价格 | 25 元 | 9 元 | 29 元 |
| ROS 支持 | 靠串口 | 串口/USB/CAN 全支持 | 同上 |
结论:
- 只做循迹——Arduino 够用;
- 要做闭环+数据融合——直接上 STM32F4,后期上 ROS 不用换板子。
RTOS 选不选?
- 裸机:主循环 1 kHz,中断抢时间,代码 200 行搞定;
- RTOS(FreeRTOS):任务分优先级,通信周期稳,但 RAM 至少 20 kB。
毕设演示 3 分钟,裸机就能过;如果想把小车当以后科研平台,RTOS 一次到位。
通信:
- 串口 115200,ROS 端
rosserial一键配置; - CAN 抗干扰强,但需额外驱动板,答辩现场 5 米长线串口足够。
三、系统架构图
四、核心实现细节
1. 电机 H 桥驱动电路
- 选型:TB6612FNG,连续 1.2 A,峰值 3.2 A,内建续流二极管,省掉 MOS 管+二极管一堆飞线。
- 布局:
- H 桥回路 < 2 cm,降低 di/dt 辐射;
- 电机电源与逻辑电源单点接地,防止电流回流把 ADC 抬飞。
- PCB 铺铜留 2 mm 间隙,电机对 MCU 的干扰下降 10 dB,亲测示波器波形毛刺直接减半。
2. 超声波 + MPU6050 数据融合
超声波 40 kHz 采样,IMU 1 kHz。融合策略:
- 距离 > 30 cm:信任超声波;
- 距离 ≤ 30 cm:切换到 IMU 积分位移,抑制声波多径干扰。
代码里用一个互补滤波器,系数 0.98/0.02,占 3 行,比卡尔曼省 200 行矩阵运算。
3. 增量式 PID
位置环 100 Hz,速度环 1 kHz。
增量公式:
Δu(k) = Kp·Δe(k) + Ki·e(k) + Kd·[Δe(k)-Δe(k-1)]调参顺序:
- 先 Ki=0, Kd=0,调 Kp 到小幅振荡;
- 加 Kd 抑制超调;
- 最后给 Ki=0.01 消静差。
口诀:P 拉响应,D 抗冲动,I 清残差。
五、关键代码片段(HAL 库,带注释)
以下代码在 STM32CubeMX 生成的基础上补全,直接复制可跑通。
/* 1. 定时器 1 kHz 中断初始化 */ static void MX_TIM2_Init(void) { TIM_ClockConfigTypeDef sClockSourceConfig = {0}; TIM_MasterConfigTypeDef sMasterConfig = {0}; htim2.Instance = TIM2; htim2.Init.Prescaler = 84-1; // 84 MHz / 84 = 1 MHz htim2.Init.CounterMode = TIM_COUNTERMODE_UP; htim2.Init.Period = 1000-1; // 1 MHz / 1000 = 1 kHz htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; HAL_TIM_Base_Init(&htim2); sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig); sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig); HAL_NVIC_SetPriority(TIM2_IRQn, 0, 0); HAL_NVIC_EnableIRQ(TIM2_IRQn); } /* 2. 中断服务函数:1 kHz 控制周期 */ void TIM2_IRQHandler(void) { if(__HAL_TIM_GET_FLAG(&htim2, TIM_FLAG_UPDATE)) { __HAL_TIM_CLEAR_FLAG(&htim2, TIM_FLAG_UPDATE); /* 读取编码器 */ int16_t speed = __HAL_TIM_GET_COUNTER(&htim3); /* 计算增量 PID */ static float err1=0, err2=0; float err = target_speed - speed; float du = KP*(err-err1) + KI*err + KD*(err-2*err1+err2); err2 = err1; err1 = err; /* 更新 PWM */ __HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_1, (int)(du + pwm_offset)); /* 打包上传 ROS */ static uint16_t cnt=0; if(++cnt >= 100) { // 10 Hz 上传 tx_buf[0] = 0xAA; // 帧头 tx_buf[1] = (speed>>8)&0xFF; tx_buf[2] = speed&0xFF; tx_buf[3] = (int16_t)(du)>>8; tx_buf[4] = (int16_t)du; tx_buf[5] = 0x55; // 帧尾 HAL_UART_Transmit_DMA(&huart1, tx_buf, 6); cnt = 0; } } }六、性能与可靠性:别让小车“抽风”
- 电源噪声抑制
- 电机 5 V 与 MCU 3.3 V 用磁珠+TVS隔离,500 Hz 开关毛刺从 800 mV 降到 80 mV。
- 控制周期抖动
- 把中断优先级级调到 0,关闭 HAL 库的 Systick 中断,抖动由 22 µs 降到 4 µs。
- 通信丢包
- 串口 DMA 双缓冲,ROS 端写 20 ms 超时重传,现场 3 小时连续跑 0 丢包。
七、生产环境避坑指南
- PCB 布局:电机电源线走内电层,远离 SWD 下载口,否则下载一次复位一次。
- 电池电压跌落:锂电池加 2200 µF 钽电容,启动电流 2 A 时电压跌落 < 0.4 V,MCU 不复位。
- 示波器调试:探头地线 < 3 cm,测 PWM 时别把地环当天线,否则看到 20 MHz 鬼影还以为自己谐振了。
八、演示效果与扩展思考
把上述模块串起来,三天就能在实验室走廊完成“直线+避障”闭环。答辩老师最常问的三句话:
- “控制周期多少?”——答 1 kHz,抖动 4 µs,老师点头。
- “数据融合为什么不用卡尔曼?”——答 30 cm 内超声波误差非高斯,互补滤波够用,老师觉得务实。
- “后续还能做什么?”——把 IMU 里程计+超声波栅格图喂给 gmapping,十分钟就能跑 SLAM,再往上就是 move_base 路径规划。
九、结尾
毕设不是写论文,是让电路转起来、让代码稳住、让自己睡得着。本文代码和 PCB 源文件已开源在 GitHub,搜“SmartCar_STM32_ROS”就能找到。别急着一次做完美,先把电机转圈、超声波测距、PID 调平这三板斧跑通,再去想 SLAM、深度学习、视觉避障——小车先能走,才能去远方。祝你调试顺利,答辩一次过!