用51单片机+红外传感器DIY循迹小车,我的毕业设计避坑实录(附完整C代码)

张开发
2026/4/18 2:38:33 15 分钟阅读

分享文章

用51单片机+红外传感器DIY循迹小车,我的毕业设计避坑实录(附完整C代码)
51单片机红外循迹小车实战从元器件选型到代码调试的全流程避坑指南去年这个时候我也和大多数电子专业的学生一样为了毕业设计焦头烂额。当导师建议我做智能循迹小车时我以为只是个简单的玩具项目没想到从元器件采购到最终调试整整踩了两个月坑。现在回想起来那些深夜调试PWM波形的日子那些因为传感器灵敏度问题反复修改的代码都成了最宝贵的学习经验。这篇文章不是简单的代码分享而是想把我从零开始完成这个项目的完整心路历程记录下来特别是那些教程里不会告诉你的坑和解决方案。1. 硬件选型与电路设计那些没人告诉你的细节1.1 红外传感器的选择与安装角度市面上常见的红外循迹模块主要有TCRT5000和RPR220两种。我最初贪便宜选了TCRT5000单价约2元结果发现探测距离不稳定黑色电工胶带在强光下反射率变化大安装高度敏感最佳高度在1-1.5cm需要反复调整对比度问题浅色地面如米色桌面误触发率高后来改用RPR220单价约5元虽然贵些但稳定性明显提升。关键安装参数参数推荐值备注安装高度1.2cm高于1.5cm会降低灵敏度倾斜角度15-20度向前倾斜减少环境光干扰间距2-2.5cm匹配常见黑线宽度实际测试发现传感器向外倾斜10度形成八字形布局可显著减少相邻传感器的串扰。1.2 电机驱动方案的抉择我对比了三种常见方案L298N模块经典但笨重优点驱动能力强支持2A电流缺点需要散热片占PCB面积大TB6612FNG我的最终选择优点集成度高支持1.2A连续电流缺点价格稍贵约8元/片晶体管阵列成本最低优点成本仅需2-3元缺点需要自己设计H桥电路// TB6612FNG的典型驱动代码 #define AIN1 P1_0 #define AIN2 P1_1 #define BIN1 P1_2 #define BIN2 P1_3 #define PWMA P1_4 #define PWMB P1_5 void Motor_Control(char motor, int speed) { if(motor L) { AIN1 (speed 0); AIN2 (speed 0); PWMA abs(speed); } else { BIN1 (speed 0); BIN2 (speed 0); PWMB abs(speed); } }1.3 电源管理的血泪教训我的第一个版本直接用7805线性稳压结果电机启动瞬间导致单片机复位电池续航不到1小时稳压芯片发烫严重改进方案改用DC-DC降压模块如MP2307电机电源与逻辑电源分离增加1000μF电容缓冲2. 循迹算法实战从基础到优化2.1 基础判断逻辑的陷阱大多数教程给的示例代码都是这样的简单判断if(Left_Sensor Right_Sensor) { go_forward(); } else if(Left_Sensor !Right_Sensor) { turn_right(); } else if(!Left_Sensor Right_Sensor) { turn_left(); } else { // 两个传感器都离开黑线怎么办 }实际测试中发现三个致命问题直角弯处理当小车完全离开黑线时两个传感器都检测不到会完全失控抖动问题传感器在边界处会产生高频切换导致电机频繁正反转速度突变直接全速转向容易冲出赛道2.2 状态记忆法的实现为解决直角弯问题我增加了转向状态记忆char last_turn F; // 记录上次转向 void track_logic() { if(Left_Sensor Right_Sensor) { go_forward(); last_turn F; } else if(!Left_Sensor Right_Sensor) { turn_left(); last_turn L; } else if(Left_Sensor !Right_Sensor) { turn_right(); last_turn R; } else { // 根据记忆的状态处理 if(last_turn L) sharp_turn_left(); else if(last_turn R) sharp_turn_right(); else stop(); // 完全丢失路线 } }2.3 PWM速度控制的优化直接全速转向会导致小车冲出赛道我采用了分级速度控制直行时全速PWM100%小弯时降速至70%直角弯时降速至50%纠偏时左右轮差速30%void set_motor_speed(char state) { switch(state) { case F: // 直行 Motor_Control(L, 100); Motor_Control(R, 100); break; case L: // 左转 Motor_Control(L, 50); Motor_Control(R, 80); break; case R: // 右转 Motor_Control(L, 80); Motor_Control(R, 50); break; case S: // 急转 Motor_Control(L, 30); Motor_Control(R, 70); break; } }3. 调试技巧与性能优化3.1 传感器阈值校准很多同学直接使用模块出厂设置结果在不同环境下表现差异很大。我总结的校准方法准备纯黑和纯白参考物测量传感器在两种状态下的输出电压取中间值作为阈值在代码中设置为可调参数#define BLACK_THRESHOLD 650 // ADC读数需实际测量 #define WHITE_THRESHOLD 350 // ADC读数需实际测量 #define ACTUAL_THRESHOLD ((BLACK_THRESHOLD WHITE_THRESHOLD)/2)3.2 使用示波器调试PWM电机控制不稳时用示波器检查PWM频率是否合适推荐1-5kHz占空比是否准确两个轮子的波形是否同步3.3 降低功耗的技巧关闭未用外设ADC、串口等使用睡眠模式当检测到长时间无黑线降低主频从12MHz降到6MHz// 进入低功耗模式 void enter_sleep_mode() { PCON | 0x01; // 置位IDL位 _nop_(); _nop_(); }4. 进阶改进方向4.1 多传感器阵列方案基础版的2传感器方案有局限性我后来升级为5传感器[L2][L1][C][R1][R2]对应的状态判断表传感器模式动作说明00011急右转即将偏离右侧00111缓右转轻微右偏11100急左转即将偏离左侧11100缓左转轻微左偏00100直行完美居中4.2 加入简单的PID控制虽然毕业设计不要求但我后来尝试了最简单的P控制int position_error() { // 根据传感器位置计算偏差 if(L2 !R2) return -2; if(L1 !R1) return -1; if(!L1 R1) return 1; if(!L2 R2) return 2; return 0; } void pid_control() { int error position_error(); int base_speed 60; int correction error * 10; // 比例系数Kp10 Motor_Control(L, base_speed - correction); Motor_Control(R, base_speed correction); }4.3 增加蓝牙调试接口为方便调试我增加了HC-05蓝牙模块可以实时查看传感器读数调整速度参数手动控制小车void uart_send(char *str) { while(*str) { SBUF *str; while(!TI); TI 0; } } // 发送传感器状态 void report_sensors() { char buf[20]; sprintf(buf, L:%d R:%d\r\n, Left_Sensor, Right_Sensor); uart_send(buf); }那些在实验室通宵调试的日子虽然辛苦但收获巨大。最让我自豪的不是小车最终能完美跑完全程而是在解决每一个问题时积累的实战经验。记得在答辩前一天我的小车突然开始原地转圈最后发现是电机驱动芯片的一个引脚虚焊——这种最后一刻出问题的经历可能每个做硬件的同学都深有体会吧。

更多文章