简介:这份资源是一篇本科毕业设计论文文档,以基于Arduino单片机的超声波避障小车为研究对象,适合电子信息、自动化、计算机等专业需要完成毕业论文或课程设计的学生参考。论文围绕行为设计思路,完整展开移动机器人从机械结构搭建到软件控制逻辑的实现过程:硬件上以Arduino单片机为控制核心,配合超声波传感器检测前方障碍物距离,通过L298N驱动两个直流电机,并用PWM调速控制行进速度;软件上采用Arduino语言编程,实现前进、后退、转向等动作,当探测到约25厘米内有障碍物时自动转向避让。文中还涉及机器人机械设计、传感器检测与试验结果分析等内容,可作为超声波测距、电机驱动与嵌入式编程的综合实践参考。资源包共1个文件,为docx格式的毕业论文,压缩包大小12.69MB,结构完整,目录与摘要、绪论、硬件设计、软件编程及测试章节齐备。目前已有248人学习。
1. 答辩前一周小车跑不起来,才是这个题目真正的难点
选题表上写下「基于Arduino单片机避障小车机器人」的那一刻,多数人以为难点在论文,实际卡住的是车。实验室里最常见的一幕:代码编译通过,串口能打印距离,电机就是不转,或者原地打转撞上桌角,答辩前一天还在换驱动模块。这个题目的技术骨架其实很窄——一块Arduino UNO、一个超声波模块、一块电机驱动、两个直流减速电机,加几十行决策代码;但要把这几样凑成一辆每次都能绕开障碍的小车,牵涉供电、共地、定时器冲突、测距滤波和参数整定五件事,任何一件没处理好,演示就会翻车。下面按硬件、代码、实验数据、进阶调试的顺序走一遍,目标是让车先动起来、再让论文里的数据站得住。
2. Arduino UNO 避障小车的硬件选型与接线
2.1 主控与驱动选型:为什么 UNO 加 L298N 最不容易翻车
毕设现场比拼的不是算力,而是「一次点亮」。主控的选择直接决定了你需要花多少时间在底层,而不是在算法和论文上。
| 方案 | 资源与特点 | 上手门槛 | 适合的题目方向 |
|---|---|---|---|
| Arduino UNO R3 | 14 路数字 IO、6 路 PWM、5V 电平,Servo、NewPing 等库现成 | 低,接线即用 | 功能单一的避障小车课设、本科毕设 |
| STM32F103 | 定时器多,能同时跑编码器、PWM、串口,3.3V 需考虑电平 | 中,HAL 配置耗时 | stm32 循迹避障小车、需要测速与闭环 |
| 51 / STC 单片机 | 成本低、寄存器直观,IO 与定时器偏紧 | 中,多数驱动要自己写 | 教学演示、偏硬件设计的题目 |
我一般会按「论文里要写几个功能」倒推主控:只做避障,UNO 足够;要同时做避障、测速、循迹、串口上报,STM32 或 Arduino 加扩展板更省事。Arduino 生态里Servo.h、NewPing.h这类库把超声波和舵机的时序细节都封好了,答辩演示最怕的时序抖动问题能少一大半。
驱动模块是第二个分水岭。L298N 是双 H 桥,输入 7 到 12V,单路持续 2A 左右,缺点是内部压降约 1.4 到 2V,两节 18650 串成 7.4V 时,电机实际只拿到 5.5V 上下,速度偏慢;TB6612FNG 压降小、效率高,但对 PWM 频率和逻辑电平更挑;DRV8833 适合小功率空心杯。常见做法是 UNO 加 L298N 打底,如果论文里要写「不同驱动方案对能耗的影响」,正好把 TB6612 作为对照组,实验章节立刻有内容。
2.2 引脚分配表:超声波、舵机与 L298N 怎么接不打架
接线冲突里最隐蔽的一条是定时器。UNO 上Servo.h默认占用 Timer1,一旦调用attach(),9 号和 10 号引脚就失去analogWrite()的 PWM 能力——编译不会报错,表现是电机调速失灵或舵机抖动。
| 模块 | 模块引脚 | UNO 引脚 | 说明 |
|---|---|---|---|
| HC-SR04 | VCC / GND | 5V / GND | 工作电流约 15mA |
| HC-SR04 | Trig | D3 | 触发端,需要 10µs 高电平 |
| HC-SR04 | Echo | D4 | 回波高电平时长即往返时间 |
| SG90 舵机 | 信号 | D9 | 占用 Timer1,D9/D10 失去 PWM |
| SG90 舵机 | VCC | 外部 5V 优先 | 堵转电流可达 700mA |
| L298N | ENA | D5 | 左轮 PWM,Timer0 |
| L298N | IN1 / IN2 | D7 / D8 | 左轮方向 |
| L298N | ENB | D6 | 右轮 PWM,Timer0 |
| L298N | IN3 / IN4 | D12 / D13 | 右轮方向 |
| L298N | GND | GND | 必须与 UNO 共地 |
这套分配把两路 PWM 留给 Timer0 的 D5、D6,舵机单独用 D9,Trig 和 Echo 放在不冲突的 D3、D4。后面如果要加编码器测速,D2、D3 是外部中断引脚,TrIG 需要挪到 D11,这一点在做扩展实验之前就要规划好,不然后期改板子很痛苦。
2.3 供电与共地:小车一上电就复位的原因
新手最常见的现象是:接上电池,UNO 反复重启,串口不断打印启动信息。九成是电源问题。电机启动瞬间的浪涌电流能到 1A 以上,如果 UNO 和电机共用一路供电,电压被拉低到复位阈值以下,主控就会重启。
# 推荐的供电拓扑(文字描述,接线时对照) # 电池组 7.4V ──> L298N 的 12V 输入端子(同时给电机供电) # L298N 板载 5V 输出 ──> 舵机 VCC(拔掉 5V 使能跳帽后引出) # UNO 单独用 USB 或 9V 电池供电 # 三者的 GND 必须短接在一起参数上要盯三个点。第一,L298N 的 5V 使能跳帽:插上时板载稳压给内部逻辑供电,同时也从 5V 端子输出;如果要用它给 UNO 的 5V 引脚反供电,就必须拔掉 USB,否则两路电源互顶。第二,电机两端并一个 0.1µF 陶瓷电容,能明显减少对主控的干扰。第三,舵机和电机尽量分开取电,或者在 5V 母线并一只 470µF 到 1000µF 的电解电容做储能。
提示:所有模块的 GND 必须连到同一点。只接了信号线没接共地,是最难查的一类故障——串口有数据,电机纹丝不动。
3. 避障决策的最小可跑通代码与参数整定
3.1 HC-SR04 测距函数与超时保护
先把测距做扎实,后面所有逻辑都建立在这个数字上。下面这段是所有避障小车的地基,重点是超时保护,否则前方无遮挡、回波丢失时pulseIn会一直等下去,整辆车卡死。
#include <Servo.h> Servo scanServo; const uint8_t PIN_TRIG = 3; const uint8_t PIN_ECHO = 4; const uint8_t PIN_SERVO = 9; const uint8_t ENA = 5, IN1 = 7, IN2 = 8; // 左轮 const uint8_t ENB = 6, IN3 = 12, IN4 = 13; // 右轮 // 返回厘米;-1 表示本次没有收到回波 float readDistanceCM() { digitalWrite(PIN_TRIG, LOW); delayMicroseconds(4); // 拉低一小段,保证触发沿干净 digitalWrite(PIN_TRIG, HIGH); delayMicroseconds(10); // 规格书要求的 10µs 触发脉冲 digitalWrite(PIN_TRIG, LOW); // 25000µs 超时约等于 4.3m,超出量程直接放弃本次采样 unsigned long dur = pulseIn(PIN_ECHO, HIGH, 25000UL); if (dur == 0) return -1.0f; // 声速取 20℃ 下的 343m/s,即 0.0343cm/µs;除以 2 是往返距离 return dur * 0.0343f / 2.0f; }pulseIn的第三个参数是超时微秒数,这个值必须给。返回 0 代表超时,业务层把它当成「无限远」还是「无效值」,要写清楚,否则决策逻辑会出现「因为读不到就往前冲」这种危险分支。
3.1.1 中值滤波与温度补偿
单次采样跳变很大,尤其是地面反射和软质障碍物。取三次中值是最省 CPU 的做法,代码只有几行:
int medianOf3(float a, float b, float c) { if (a > b) { float t = a; a = b; b = t; } if (b > c) { float t = b; b = c; c = t; } if (a > b) { float t = a; a = b; b = t; } return (int)b; // 排序后取中间值 }如果论文里要做误差分析,温度补偿是个加分项:声速 v = 331.4 + 0.6 × T(T 为摄氏度),加一个 DHT11 或者 DS18B20 就能把 0.0343 换成实时计算值。需要注意的是,室温下这个修正量只有百分之几,测距误差的主要来源其实是障碍物表面材质和安装高度,别把精力全押在温度上。
3.2 三向扫描舵机的决策状态机
只靠一个前置超声波,小车只能在正前方做出反应,遇到直角墙角会来回犹豫。加一个舵机做三向扫描,决策空间立刻变清晰:左 150°、正前 90°、右 30°。
const int ANG_LEFT = 150, ANG_MID = 90, ANG_RIGHT = 30; const int DIST_STOP = 22; // 厘米,小于此值触发避障 // 转到指定角度后取三次中值,320ms 是 SG90 走完 60° 的经验值 int scanAt(int angle) { scanServo.write(angle); delay(320); float a = readDistanceCM(); float b = readDistanceCM(); float c = readDistanceCM(); return medianOf3(a, b, c); } void setMotor(int l, int r) { digitalWrite(IN1, l >= 0 ? HIGH : LOW); digitalWrite(IN2, l >= 0 ? LOW : HIGH); analogWrite(ENA, constrain(abs(l), 0, 255)); digitalWrite(IN3, r >= 0 ? HIGH : LOW); digitalWrite(IN4, r >= 0 ? LOW : HIGH); analogWrite(ENB, constrain(abs(r), 0, 255)); }setMotor传入正负值表示方向,负数就是反转,这样决策层只需要关心「左轮给多少、右轮给多少」,不用再关心电平组合。注意constrain把 PWM 夹在 0 到 255,负值在取绝对值之前不能直接喂给analogWrite。
决策表是论文里最值得画的一张图,实际代码就是它的直译:
| 左侧距离 | 正前距离 | 右侧距离 | 动作 |
|---|---|---|---|
| 远 | 近 | 远 | 后退 200ms,向更远一侧转 |
| 远 | 近 | 近 | 左转 380ms |
| 近 | 近 | 远 | 右转 380ms |
| 近 | 近 | 近 | 后退 300ms,原地掉头 |
| 远 | 远 | 远 | 直行 |
「远」和「近」用同一个 DIST_STOP 阈值判定即可,简单可靠。真正影响体验的是转向时长:转太少,下一轮扫描还是「近」;转太多,车会在走廊里左右画龙。这个值必须实测。
3.3 速度 PWM 与转向时间的参数表
参数不是拍脑袋定的,每个都对应一个物理量,改的时候要知道自己在改什么。
| 参数 | 建议初值 | 可调范围 | 影响 |
|---|---|---|---|
| SPEED_RUN | 165 | 120–220 | 直行速度,过高会冲过障碍 |
| SPEED_TURN | 130 | 100–180 | 转向速度,过高容易打滑 |
| PWM_DEADZONE | 85 | 60–100 | TT 电机起步的最小占空比 |
| TURN_MS | 380 | 250–500 | 单次转向时长,决定转弯角度 |
| DIST_STOP | 22 | 15–35 | 触发避障的距离阈值 |
| SCAN_DELAY | 320 | 250–450 | 舵机到位等待时间 |
整定顺序建议这样走:先把 DIST_STOP 设成 40cm,让车在安全距离外就停下来,确认避障分支能触发;再固定 TURN_MS 为 380ms,在瓷砖地面上量一次转弯的实际角度,按比例修正;最后把 DIST_STOP 逐步降到 20cm 左右,找到既不撞、又不会因为太保守而频繁停车的位置。PWM 死区也要单独测一遍,占空比低于某个值时电机只会嗡嗡响不转,这个值写进论文的「电机特性测试」小节正好。
注意:L298N 的 ENA、ENB 跳帽必须拔掉,PWM 才受控。跳帽插着的时候电机全速运转,
analogWrite完全无效,这个坑每年都有人踩。
4. 论文实验数据怎么采:测距误差、避障成功率与路径对比
4.1 串口输出 CSV:把每次避障记录成可分析的数据
论文里的表格不能用「大概」「基本可以」这种词,得有一行行原始记录。最省事的方式是让 Arduino 直接吐 CSV,按固定周期输出,后处理交给 Excel 或 Python。
const unsigned long LOG_INTERVAL = 200; // 每 200ms 记一行 unsigned long lastLog = 0; void logRow(unsigned long t, int c, int l, int r, const char* action) { Serial.print(t); Serial.print(','); Serial.print(c); Serial.print(','); Serial.print(l); Serial.print(','); Serial.print(r); Serial.print(','); Serial.println(action); }字段顺序是时间戳、前方距离、左侧距离、右侧距离、当前动作。采集时把 Arduino IDE 串口监视器的波特率设成 115200,关掉「显示时间戳」选项,否则打印出来的时间戳会混进 CSV,后期要多写一刀清洗脚本。数据量大或者要脱离电脑跑,可以加 SPI 的 TF 卡模块,CS 接 D10,每写一行flush()一次,避免断电丢数据。
这套记录方式还有个附带好处:答辩演示翻车时,串口日志能直接说明是测距异常还是决策分支没走到,排查速度比盯着车看快得多。
4.2 实验分组与数据表设计
数据要有对照才有说服力。每组障碍物重复 30 次,记录成功次数和响应时间,取平均值和标准差。
| 实验组 | 前方障碍物 | 距离设置 | 测试次数 | 成功次数 | 平均响应时间 |
|---|---|---|---|---|---|
| A | 硬质纸箱 | 50cm | 30 | 29 | 0.42s |
| B | 布质靠垫 | 50cm | 30 | 24 | 0.51s |
| C | 直径 3cm 细杆 | 50cm | 30 | 19 | 0.47s |
| D | 移动障碍(手持) | 50cm | 30 | 21 | 0.55s |
布质和细杆的失败率明显高,原因是回波散射或反射面太小,这一条正好写进「测距误差分析」;移动障碍那一组失败率高,说明当前决策周期偏长,可以推给「动态避障小车路径规划」作为后续改进方向。踩坑记录也是数据,pulseIn返回 0 的次数单独统计一列,比笼统说「偶尔失灵」有价值得多。
4.3 Arduino IDE 上传报错与常见故障排查
演示前一周的报错基本都集中在这几类,按表逐条对:
| 现象 | 常见原因 | 处理方式 |
|---|---|---|
| avrdude: stk500_recv(): programmer is not responding | 板子型号选错、串口被占用 | 确认开发板选 UNO,关掉串口监视器再上传 |
| 工具菜单里端口灰色 | 数据线只供电不传数据,或 CH340 驱动未装 | 换一根数据线,装完驱动重插 |
| 编译通过但电机不转 | ENA/ENB 跳帽未拔、未共地、PWM 占空比低于死区 | 拔跳帽、短接 GND、把速度提到 100 以上 |
| 舵机抖动、主控重启 | 舵机与电机抢电流 | 分开供电,母线并 470µF 以上电容 |
| 距离恒为 0 或跳变剧烈 | Trig/Echo 接反、采样间隔过短 | 交换引脚,两次采样间隔留 60ms 以上 |
如果用的是第三方库,从 Arduino IDE 的库管理器安装,版本尽量选最近一两个大版本,老版本对pulseIn超时参数的支持不一致。想在没硬件的情况下先验证逻辑,Wokwi 仿真平台能省掉大量插拔时间。
5. 从能演示到能复现:仿真、测速与论文落笔
5.1 用 Wokwi 仿真平台先验证决策逻辑
接线还没到货的时候,把 UNO、HC-SR04、Servo、两个 LED 拖进 Wokwi 的电路画布,LED 分别代表左右轮正转和反转,串口打印决策过程。仿真里可以拖动障碍物距离滑块,把「远、中、近」三种输入都跑一遍,确认状态机没有死角分支——比如左右都近的时候是否真的进了掉头逻辑。需要注意的是,Wokwi 的舵机是瞬时到位的,没有真实的 320ms 延时,所以scanAt里的等待时间必须到真车上再调,仿真只能验证逻辑正确性,验证不了时序。
5.2 加编码器做单片机小车测速与 PID 直行校正
两轮小车用同样 PWM 直行,走三米之后必然偏,原因是两个电机的减速箱阻力不一致。要写「单片机小车测速」相关内容,就得加霍尔编码器:A 相接 D2、B 相接 D3,用外部中断计数。
volatile long pulseL = 0, pulseR = 0; void countL() { pulseL++; } // D2 上升沿计数 void countR() { pulseR++; } // D3 上升沿计数 float speedOf(long pulses, float wheelCM, int ppr, float dt) { return (pulses / (float)ppr) * wheelCM / dt; // cm/s } // 主循环里每 100ms 校正一次 float err = speedL - speedR; int corr = (int)(Kp * err); // Kp 从 1.0 开始试 setMotor(baseSpeed + corr, baseSpeed - corr);ppr是每圈脉冲数,11 线编码器配 1:48 减速箱要乘上去,别直接填 11。Kp 从 1.0 起步,观察车头是否左右大幅摆动,摆就减半,反应迟钝就加大。加编码器后 Trig 要从 D3 挪到 D11 或 D12,把 D2、D3 这两个外部中断引脚让出来。
5.3 论文图表与代码附录的整理技巧
图表规范上,图题写在图下方、表题写在表上方,编号连续,正文里每一张都至少引用一次。数据图建议从 Excel 导出矢量图或者用 Origin 画,截图放进去打印会糊,答辩投影更明显。参数表要给出测试条件和取值依据,只列数字不写条件,评阅人会直接打问号。
代码附录里保留测距、决策、电机控制三个核心函数,调试用的串口打印全部删掉,行数控制在三到五页。查重方面,方法章节的表述尽量用自己的话重写,尤其是从数据手册翻译过来的模块介绍,那部分重复率通常最高。答辩被问到阈值和 PID 参数怎么定的,把原始测速数据和整定过程的记录一起翻出来,比背一句「经过多次调试」有用得多——把每次改参后的速度曲线留在附录,临界值就有据可查。
本文还有配套的精品资源,点击获取