用STM32F103C8T6做个能遥控能避障的平衡小车,保姆级教程(附代码)
从零打造STM32平衡小车避障与蓝牙遥控全攻略第一次看到平衡小车稳稳立在桌面上时那种成就感至今难忘。作为电子爱好者入门嵌入式开发的经典项目平衡小车融合了传感器技术、控制算法和硬件设计的精华。本文将带你用STM32F103C8T6这颗性价比之王从元器件焊接开始一步步实现能自动避障、手机遥控的智能平衡车。不同于学院派的理论讲解这里全是实打实的踩坑经验和即插即用的代码片段。1. 硬件选型与电路设计1.1 核心元器件清单选择合适器件是成功的第一步经过多次迭代测试这套配置在性能和成本间取得了最佳平衡部件型号关键参数单价主控STM32F103C8T672MHz Cortex-M3, 64KB Flash¥15陀螺仪MPU6050六轴(加速度陀螺仪), I2C接口¥8电机驱动TB6612FNG双路1.2A, 带制动功能¥6直流减速电机GA25-370减速比1:34, 编码器500线¥25/个蓝牙模块HC-05经典串口蓝牙, 10米传输距离¥12超声波模块HC-SR042cm-400cm测距, 3mm精度¥5避坑提示电机务必选择带编码器的型号早期测试使用普通电机因缺少速度反馈导致控制效果极差。GA25-370的霍尔编码器输出为AB相脉冲可通过STM32的编码器接口模式直接读取。1.2 电源系统设计平衡小车的心脏常被初学者忽视实测中80%的异常重启都源于电源问题。推荐三级供电方案输入级航模3S锂电池(11.1V)直接接入注意正负极防反接设计电机驱动级LM2596降压模块将11.1V降至7.4V供给TB6612的VM引脚控制级AMS1117-3.3为STM32和传感器供电单独7805为蓝牙模块提供5V电源避免数字噪声干扰// 电源检测代码示例防止低压运行 void Check_Battery(void) { float voltage ADC_GetValue() * 3.3 / 4096 * (102)/2; // 分压电阻10k2k if(voltage 9.5) { Motor_Stop(); // 紧急停止电机 OLED_ShowString(1,1,Low Battery!); while(1); // 阻塞等待充电 } }1.3 PCB布局技巧使用嘉立创EDA设计时这几个细节决定成败电机驱动线路走线宽度≥1mm避免大电流发热MPU6050周围预留15mm净空区减少振动干扰蓝牙天线区域(HC-05的金色部分)不要覆铜所有数字地模拟地单点连接在稳压芯片GND脚红色为高压线路蓝色为数字信号线绿色为模拟信号区域2. 软件架构与核心算法2.1 传感器数据融合MPU6050的原始数据需要经过三重处理才能用于控制DMP初始化数字运动处理器void MPU6050_Init(void) { I2C_WriteByte(MPU6050_ADDR, PWR_MGMT_1, 0x80); // 复位设备 delay_ms(100); I2C_WriteByte(MPU6050_ADDR, PWR_MGMT_1, 0x03); // 使用Z轴晶振 // 加载DMP固件 if(!mpu_load_memory(dmp_memory, sizeof(dmp_memory))) { OLED_ShowString(1,1,DMP Load Failed!); while(1); } I2C_WriteByte(MPU6050_ADDR, INT_ENABLE, 0x02); // 开启DMP中断 }卡尔曼滤波简化版角度 0.98*(上一角度 陀螺仪*dt) 0.02*加速度计角度零偏校准上电静止2秒自动采集200组数据求平均值运行时实时减去零偏值2.2 串级PID控制实现平衡车的控制分为三个闭环像俄罗斯套娃一样层层嵌套直立环(PD) → 速度环(PI) → 转向环(PD)typedef struct { float Kp,Ki,Kd; float Err,LastErr,Integral; } PID; void PID_Update(PID* pid, float current, float target) { pid-Err target - current; pid-Integral pid-Err; // 抗积分饱和 if(pid-Integral 1000) pid-Integral 1000; else if(pid-Integral -1000) pid-Integral -1000; float output pid-Kp * pid-Err pid-Ki * pid-Integral pid-Kd * (pid-Err - pid-LastErr); pid-LastErr pid-Err; return output; }调参口诀先直立后速度先比例后微分。直立环Kp从小往大调直到小车能勉强站立接着加Kd抑制抖动最后加入速度环Ki让小车能抵抗轻微推力。2.3 蓝牙遥控协议设计HC-05模块与手机APP通信采用自定义简协议字节含义取值0x55帧头固定0x550x01指令类型0x01:速度 0x02:转向data控制量-100~100sum校验和前面所有字节异或Android端示例代码MIT App Inventor导出APK// 当摇杆移动时 procedure 摇杆.PositionChanged(x number, y number) var speed y * 100 var turn x * 50 // 打包数据帧 var frame list make a list list add item to frame 0x55 list add item to frame 0x01 list add item to frame speed list add item to frame 0x02 list add item to frame turn list add item to frame 异或校验(frame) // 通过蓝牙发送 call BluetoothClient.SendBytes frame end procedure3. 机械组装与调试技巧3.1 车体结构优化经过五次迭代验证这些机械设计原则能显著提升稳定性重心位置电池安装在车轮轴线下方1/3处轮距选择建议12-15cm太窄易侧翻太宽响应慢减震措施电机与底盘间加3mm硅胶垫电路板使用尼龙柱悬浮安装3.2 系统联调步骤按这个顺序调试可事半功倍基础测试不装车轮用USB转串口打印MPU6050原始数据用手转动电机检查编码器计数方向直立环调试# 简易PID参数整定脚本需接串口 import serial ser serial.Serial(COM3,115200) while True: ser.write(bKP0.1\n) # 逐步增加Kp input(小车是否开始振荡)运动测试在光滑地板上标记2米直线观察小车能否保持直线行走偏差30cm需调整转向环3.3 常见故障排查这些症状和解决方案来自真实踩坑经验现象可能原因解决方法上电瞬间电机狂转TB6612使能信号未初始化在main()开头先拉低STBY引脚蓝牙连接后控制延迟大手机APP发送频率过高限制发送间隔≥50ms超声波误触发电机干扰导致误回波在Trig引脚加10uF去耦电容小车走圆圈两轮转速不一致在代码中对电机输出做补偿校准4. 功能扩展与进阶优化4.1 红外遥控兼容设计除了蓝牙可以增加红外遥控功能作为备用方案。VS1838B接收头仅需3个引脚// 红外解码核心逻辑NEC协议 void EXTI_IRQHandler(void) { static uint32_t last_time 0; uint32_t gap TIM_GetCounter(TIM2) - last_time; if(gap 13000 gap 14000) { // 起始码13.5ms ir_data 0; for(int i0; i32; i) { while(!GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0)); // 等待上升沿 uint32_t pulse TIM_GetCounter(TIM2); ir_data | (pulse 1000) i; // 1.12ms为1, 0.56ms为0 } } last_time TIM_GetCounter(TIM2); }4.2 手机APP数据监控通过蓝牙回传实时数据到手机实现专业级调试数据打包协议[0xAA][类型][数据1][数据2]...[校验] 类型0x01角度 0x02速度 0x03超声波Android端波形显示// 使用MPAndroidChart库 LineDataSet speedSet new LineDataSet(speedEntries, Speed); speedSet.setColor(Color.BLUE); speedSet.setDrawCircles(false); LineData data new LineData(speedSet); chart.setData(data); chart.invalidate(); // 刷新图表4.3 性能优化技巧当系统运行不稳定时这些底层优化立竿见影定时器配置// 使用TIM3产生20ms中断作为控制周期 TIM_TimeBaseInitTypeDef timer; timer.TIM_Prescaler 72-1; // 1MHz计数频率 timer.TIM_Period 20000-1; // 20ms TIM_TimeBaseInit(TIM3, timer); NVIC_EnableIRQ(TIM3_IRQn);DMA应用// 用DMA传输编码器数据减少CPU开销 DMA_InitStructure.DMA_PeripheralBaseAddr (uint32_t)TIM4-CNT; DMA_InitStructure.DMA_MemoryBaseAddr (uint32_t)encoder_val; DMA_Init(DMA1_Channel1, DMA_InitStructure); DMA_Cmd(DMA1_Channel1, ENABLE);内存优化// 在Keil中设置优化等级-O2 // 将频繁访问的变量定义为register类型 register float angle_err __asm__(r5);最后分享一个真实案例曾遇到小车在特定角度突然失控的问题最终发现是MPU6050安装位置过于靠近电机导致磁干扰。用铜箔包裹传感器后问题彻底解决。这提醒我们硬件项目有时需要跳出代码思维从物理层面寻找解决方案。