简介:面向电子工程、自动化及相关专业毕业设计阶段的单片机智能清洁小车项目资料包,围绕微控制器实现红外避障、沿墙清扫、定点清洁等复合任务展开。内容贯穿硬件电路选型与搭建、C语言固件编写、PID速度与方向控制、超声波与光敏传感器数据处理、蓝牙或串口通信交互,以及LED指示与LCD显示的人机界面和整机联调,可作为从需求分析到实物验证的系统性参考,也适用于课程设计与创新项目选题。压缩包为rar格式,约46.55MB,上游未提供具体文件清单,文件数量与类型以解压后实际内容为准。目前已有299人学习下载,适合需要借鉴整体框架、模块划分与调试思路的本科生及指导教师对照查阅。
1. 从「扫不干净还总撞墙」说起:这套多功能智能清洁小车要解决什么
做单片机毕业设计,智能清洁小车是最容易被低估的一类题目:看着简单,真跑起来问题一堆。第一版通常是这样——直道上左右摇摆像喝多了,遇到墙要贴着磨两秒才反应,吸尘口离地两厘米吸不起灰,擦完的地面留一道道水痕。导师看三分钟就问一句:你这车除了能动,还解决了什么?
这套设计要处理的就是这件事。它把循迹、避障、吸尘、擦拭四件事收进一台基于单片机的多功能智能清洁小车里,用有限的主控资源和几十块钱的传感器,做出一套能演示、能讲清原理、能现场验收的完整方案。适合正在准备单片机方向毕业设计的学生,也适合需要一个可复现控制框架的从业者参考。下面按需求、选型、架构、实现、避坑、验收六段讲透,能照着做。
2. 需求拆解与技术选型:主控、驱动、传感三层的取舍
2.1 功能清单怎么拆成可验收的指标
毕业设计最容易翻车的地方不是写不出代码,而是需求写成了一句话:「实现智能清洁小车的循迹避障与清洁功能」。这种描述在开题时能过,在验收时就是灾难——导师问「避障距离多少算合格」,你答不上来,就只能现场看运气。
我一般的做法是开题后第一周就把功能清单翻译成指标表,每个功能配一条量化标准和一种测法,写进任务书里。这样后期调试有靶子,答辩时也有话说。
| 功能项 | 量化指标 | 验收测法 |
|---|---|---|
| 循迹行驶 | 直线段偏差不超过 ±2cm,弯道不脱线 | 2m 长黑线,含一个 90° 弯,连续跑 5 圈 |
| 障碍避让 | 前方 20cm 内检测到障碍,0.5s 内停车并转向 | 卷尺量距,秒表计时,重复 10 次 |
| 吸尘能力 | 对 5g 均匀撒布的面粉,单次通过回收率不低于 70% | 电子秤称残留量 |
| 擦拭功能 | 湿擦后地面无明显积水,无水痕残留 | 目视 + 纸巾按压 |
| 续航 | 满电连续工作不少于 25 分钟 | 计时器记录 |
| 交互 | 按键可切换模式,OLED 显示电量与状态 | 现场逐项操作 |
这张表看着朴素,作用却很大。它把「智能」这种玄学词汇换成了距离、时间、克数、分钟,你后面选型时每一个决策都能对上表里的某一行。比如「20cm 内检测到障碍」这一条,直接决定了超声波必须放在车头正中且离地 10cm 以上,也决定了主循环里避障判定的周期不能超过 50ms。
指标定完还要做一件事:把功能按优先级分三层。第一层是必须实现的基础功能(循迹、避障、吸尘),第二层是加分功能(擦拭、电量显示、模式切换),第三层是可讲但可不做的扩展(蓝牙遥控、路径记忆)。答辩现场,第一层决定你过不过,第二层决定分数高低,第三层是留给问答环节的谈资。很多同学把精力全砸在第三层,结果基础循迹在光滑瓷砖上抖成筛子,得不偿失。
2.2 主控与电机驱动的选型权衡
主控这块,常见的选择是 STC89C52 和 STM32F103C8T6。两者都能做,但代价完全不同,我把当时的对比贴出来:
| 对比项 | STC89C52 | STM32F103C8T6 |
|---|---|---|
| 主频 | 11.0592MHz / 12MHz | 72MHz |
| 硬件 PWM | 无,需软件模拟 | TIM1/TIM3 多路互补 PWM |
| 定时器 | 2 个 16 位 | 4 个以上,支持输入捕获 |
| ADC | 无,需外挂 | 12 位多通道 |
| 调试 | 只能串口打印 | SWD 单步、看变量 |
| 成本 | 约 6 元 | 约 12 元 |
| 答辩风险 | 老师爱问「为什么不用 32」 | 代码量大,得讲得清 |
关键差异在 PWM 和调试。吸尘风机、左右轮、擦拭电机一共四路需要调速或开关控制,51 用软件模拟 PWM 会占掉大量 CPU 时间,而且一旦和超声波测距的定时中断打架,时序就乱了。更麻烦的是调试——51 出了怪现象只能靠串口一行行打印,STM32 可以直接 SWD 挂上去看变量,排查效率差一个数量级。
所以我推荐主控用 STM32F103C8T6,不是因为性能过剩,而是因为它把你的调试时间省下来了。如果学校明确要求用 51,那就得在需求上让步:擦拭电机改成定速开关控制,不做调速;超声波改用外部中断 + 定时器计数测宽,别用阻塞式延时。
驱动芯片同理。L298N 便宜、资料多、能带两路电机,但压降大(约 1.5~2V),发热明显,7.4V 供电过去只剩 5.5V 左右,风机转速直接打折。TB6612FNG 压降小、效率高、体积小,但峰值电流只有 1.2A/通道,堵转容易烧。我一般按负载选:行进电机用 TB6612,吸尘风机单独用 MOS 管(比如 AO3400 或 IRF540)做低边开关,把大电流从驱动芯片上挪走。这个拆分在实物上就是一块小板的事,但对稳定性的提升非常明显。
2.3 传感器组合与执行机构的搭配
循迹用四路 TCRT5000 红外对管,横向排布间距 15mm,离地 8~12mm。间距太小分不清弯道方向,太大则在直道上一路丢线。四路的排布方式决定了你能算出的偏差分辨率:按 -3、-1、+1、+3 加权,可以得到 7 个偏差档位,足够做比例控制了。
避障用 HC-SR04 超声波,装在车头正中,离地 12cm。这个高度是折中:太低会打到地面回波误判,太高则漏检矮障碍。有些同学喜欢加红外避障模块做辅助,实测在深色障碍物上反应距离会缩水一半,只能当补充,不能当主力。
执行机构分三块:左右两个 TT 减速电机(带编码器更好,不带也能做)、一个 5V 直流离心风机、一个 3~6V 减速电机带动擦拭辊。风机和擦拭电机都要并联续流二极管,否则关断瞬间的反电动势会把 MOS 管和单片机一起带走——这个坑我在第 5 章会细讲。
电源用两节 18650 串联成 7.4V,经 LM2596 降压到 5V 供风机和电机驱动,再经 AMS1117-3.3 供主控。注意:逻辑电源和功率电源必须分开走线,单点共地。这一句话值几百块钱的调试时间。
3. 总体架构设计:五模块分工与两级调度的职责划分
3.1 系统框图与模块职责划分
整个系统按职责切成五块,每块对应一组引脚和一组函数,边界清楚,后面改哪个模块都不牵连别人。
| 模块 | 核心器件 | 职责 | 接口 |
|---|---|---|---|
| 主控模块 | STM32F103C8T6 + 8MHz 晶振 | 调度、决策、状态机 | 全部 |
| 感知模块 | 4×TCRT5000 + HC-SR04 | 输出偏差值与距离值 | GPIO / TIM 捕获 |
| 驱动模块 | TB6612FNG ×1 + MOS 管 ×2 | 左右轮差速、风机与擦拭通断 | PWM / GPIO |
| 电源模块 | 2S 18650 + LM2596 + AMS1117 | 7.4V→5V→3.3V | — |
| 交互模块 | 按键 ×3 + 0.96 寸 OLED(I2C) | 模式切换、状态显示 | I2C / EXTI |
数据流是单向的:感知模块每 10ms 产出一组原始值,主控做滤波和判定,输出目标速度给驱动模块,同时刷新 OLED。整个链路里没有任何模块直接操作别的模块的引脚,全部通过中间变量传递。这个约束听起来啰嗦,但它是你能不能在两小时内定位一个 bug 的分水岭。
架构上还有一条隐含的线:所有涉及电机的动作都不允许在中断里直接执行。中断只置标志、存数据,真正的电机输出统一放在主循环里做。原因很实在——PWM 占空比寄存器被中断和主循环同时写,会出现某一刻左右轮速度不一致,车子在直道上突然扭一下,你还以为是循迹算法的问题。
3.2 时间片调度:2ms 节拍如何分配任务
没有 RTOS,也不能用一堆 delay 把主循环堵死。做法是用一个 2ms 的定时器中断做时基,产生软件节拍,主循环按节拍轮询任务。
/* SysTick 或 TIM4 配置为 2ms 中断,产生软件节拍 */ volatile uint32_t g_tick = 0; void TIM4_IRQHandler(void) { if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET) { TIM_ClearITPendingBit(TIM4, TIM_IT_Update); g_tick++; /* 全局节拍,每 2ms 加一 */ } } /* 判断某个周期是否到点,period 以 ms 为单位,必须是 2 的整数倍 */ static uint8_t Task_IsDue(uint32_t *last, uint32_t period) { if (g_tick - *last >= period / 2) { *last = g_tick; return 1; } return 0; }节拍分配如下表。原则是:高频任务短、低频任务长,凡是需要「等」的操作一律拆成状态机,不允许阻塞。
| 任务 | 周期 | 说明 |
|---|---|---|
| 循迹采样 | 10ms | 读 4 路 GPIO,算偏差 |
| 超声波触发 | 60ms | 触发一次,捕获回波 |
| 速度环更新 | 20ms | 计算 PWM 占空比,带斜坡限幅 |
| 避障判定 | 20ms | 结合距离与当前状态 |
| 清洁机构管理 | 100ms | 启停顺序、堵转检测 |
| OLED 刷新 | 200ms | 只刷变化的区域 |
| 按键扫描 | 20ms | 消抖用计数器 |
Task_IsDue里用减法而不是比较大小,是为了处理g_tick溢出回绕。uint32_t在 2ms 节拍下要 99 天才溢出一次,但写法上留个余量不吃亏。period 参数限定为 2 的倍数,是因为节拍精度就是 2ms,写个 15ms 出来实际会变成 16ms,不如一开始就对齐。
这套调度的好处是:任何时刻主循环都在几十微秒内跑完一圈,按键响应、避障反应都不会被拖慢。坏处是所有任务共享一个栈,某个任务里不小心写了死循环,整个系统就卡住了——所以每个任务函数入口我都加了一行超时保护,超过 5ms 没返回就置故障标志,OLED 上直接显示是哪个任务卡了,比盲猜快得多。
3.3 电源分配与干扰隔离的设计细节
小车这类系统,80% 的「玄学」问题都出在电源上。电机启动瞬间的浪涌电流能把 7.4V 拉到 5V 以下,主控跟着复位,表现出来就是「跑着跑着突然重启」,新手往往会去查是不是看门狗误触发、是不是堆栈溢出,查一天也查不到。
我的做法是把电源分成三条支路,各管各的:
| 支路 | 负载 | 稳压方案 | 关键器件 |
|---|---|---|---|
| 功率支路 A | 左右行进电机 | 直供 7.4V,经 TB6612 | 1000μF 电解 + 0.1μF 瓷片 |
| 功率支路 B | 吸尘风机、擦拭电机 | 直供 7.4V,经 MOS 管 | 470μF + 续流二极管 |
| 逻辑支路 | 主控、传感器、OLED | LM2596→5V→AMS1117→3.3V | 220μF + 磁珠 |
三条支路从电池端子出来后各走各的线,地线在电池负极处单点汇合,不要在中途随便连。功率地支路要粗,逻辑地支路要短。TB6612 和 MOS 管附近各并一个 0.1μF 瓷片电容滤高频,电解电容负责扛浪涌。
还有两个细节经常被忽略。一是超声波模块的 VCC 一定要从逻辑支路取,别图省事接到 5V 功率支路上,否则风机一启动,超声波就开始输出乱码距离。二是 OLED 的 I2C 上拉电阻用 4.7k 而不是 10k,线长超过 10cm 时 10k 上拉会导致通信偶尔丢包,屏幕上出现半屏花字。
4. 关键模块实现:从循迹避障到吸尘擦拭的控制逻辑
4.1 四路循迹的偏差计算与丢线找回
循迹的核心不是「看到黑线就转」,而是把四路开关量转换成一个连续的偏差值,再交给比例控制。加权法是最省事也最好调的一种。
/* 四路红外输入:从左到右为 L2, L1, R1, R2 黑线输出 1(按模块实际电平调整),加权值为 -3, -1, +1, +3 */ #define LINE_LOST 99 static int8_t Line_GetError(void) { int16_t sum = 0; uint8_t cnt = 0; if (TR_L2()) { sum += -3; cnt++; } if (TR_L1()) { sum += -1; cnt++; } if (TR_R1()) { sum += 1; cnt++; } if (TR_R2()) { sum += 3; cnt++; } if (cnt == 0) return LINE_LOST; /* 四路全白:丢线 */ if (cnt == 4) return LINE_LOST; /* 四路全黑:十字路口或全黑地面 */ return (int8_t)(sum / cnt); /* 归一化到 -3 ~ +3 */ }这里用「除以命中的路数」而不是直接求和,是为了让不同命中组合落在同一个量纲上。比如只有 L1 命中是 -1,只有 L2 命中是 -3,两路都命中是 -2,偏差值是连续变化的,比例控制的输出才不会跳变。如果直接求和,两路命中会得到 -4,超出预期范围,后面的限幅逻辑还得再补一层。
丢线处理是这套代码的关键。返回LINE_LOST时不能简单地停车,那在弯道外侧会直接卡死。我的做法是记住丢线前最后一次的偏差符号,然后朝那个方向原地慢速转,直到重新检测到线,同时设一个 1.5 秒的超时,超时就进故障态停车报警。
static void Line_Task(void) { static int8_t last_err = 0; int8_t err = Line_GetError(); if (err == LINE_LOST) { /* 沿丢线前的方向原地找线,速度给 30% */ int16_t dir = (last_err >= 0) ? 1 : -1; Motor_SetSpeed(-dir * 300, dir * 300); if (++lost_cnt > 150) { /* 150 × 10ms = 1.5s */ g_state = ST_FAULT; } return; } lost_cnt = 0; last_err = err; /* 比例控制:Kp 取 250,输出叠加到基础速度上 */ int16_t base = 600; int16_t adj = err * 250 / 3; Motor_SetSpeed(base + adj, base - adj); }Kp取 250 是调出来的,不是算出来的。取值太大车会左右画龙,取值太小弯道跟不上直接冲出去。调参顺序建议是:先把Kp设成 0,让车以固定速度跑直线,确认机械装配没问题;再逐步加大Kp,直到直道上开始轻微摆动,然后回退 30%。这套流程比对着公式算半天靠谱。
4.2 超声波避障的滤波与状态切换
HC-SR04 单次测量会跳,尤其是车在动、风机在转的时候。单次值直接拿来判定,车会在离墙 40cm 的地方突然刹车,又在 15cm 的地方继续往前冲,看着像失灵。
处理方法是三点:中值滤波、连续确认、距离死区。
/* 采 5 次,去最大最小取中间 3 次平均;超时返回 0xFF 表示无效 */ uint16_t Ultrasonic_ReadCm(void) { uint16_t buf[5]; uint8_t i, j; uint16_t tmp; for (i = 0; i < 5; i++) { buf[i] = Ultrasonic_Once(); /* 单次测量,超时返回 0xFF */ Delay_us(2000); /* 两次测量间隔 2ms,避免串扰 */ } /* 冒泡排序,5 个元素足够 */ for (i = 0; i < 5; i++) for (j = 0; j < 4 - i; j++) if (buf[j] > buf[j + 1]) { tmp = buf[j]; buf[j] = buf[j+1]; buf[j+1] = tmp; } if (buf[2] == 0xFF) return 0xFF; /* 中位数无效则整体判无效 */ return (buf[1] + buf[2] + buf[3]) / 3; } /* 避障判定:连续 3 次(60ms)小于阈值才触发,避免单次误判 */ static void Avoid_Task(void) { static uint8_t hit_cnt = 0; uint16_t d = Ultrasonic_ReadCm(); if (d != 0xFF && d < 20) { if (++hit_cnt >= 3) { g_state = ST_AVOID; hit_cnt = 0; } } else { hit_cnt = 0; } }参数上,20cm 是触发距离,15cm 是危险距离(此时不论状态机在哪都强制停车),50cm 是恢复距离。三个阈值拉开,是为了制造死区,防止车在阈值附近来回抖动——停在 20cm 处转一下、又退回来、又转一下,这种反复在演示时特别难看。
状态切换上,ST_AVOID的执行逻辑是:先停车 200ms,再反转 300ms 拉开距离,然后朝空旷一侧转向 600ms,最后回到ST_LINE重新找线。这段时序写死在代码里,不要用「转到看不见障碍为止」这种条件,否则车贴着墙角会转不出来。
4.3 差速 PWM 输出与加减速斜坡
两个轮子的速度差决定转向,但速度不能突变。直接给占空比跳变,电机瞬间大电流,电源一跌,主控就复位——这就是第 5 章要讲的第一个坑。所以输出层必须加斜坡限幅。
/* 目标速度范围 -1000 ~ +1000,对应占空比 -100% ~ +100% 每 20ms 更新一次,单次变化不超过 80,即 4000/s 的加速度 */ static int16_t cur_l = 0, cur_r = 0; static int16_t Ramp(int16_t cur, int16_t target) { int16_t diff = target - cur; if (diff > 80) diff = 80; if (diff < -80) diff = -80; int16_t next = cur + diff; if (next > 1000) next = 1000; if (next < -1000) next = -1000; return next; } void Motor_SetSpeed(int16_t left, int16_t right) { cur_l = Ramp(cur_l, left); cur_r = Ramp(cur_r, right); /* TB6612:正转置 AIN1=1, AIN2=0;反转反之;PWM 引脚给占空比 */ if (cur_l >= 0) { GPIO_ResetBits(GPIOB, GPIO_Pin_0); GPIO_SetBits (GPIOB, GPIO_Pin_1); TIM_SetCompare1(TIM3, cur_l); } else { GPIO_SetBits (GPIOB, GPIO_Pin_0); GPIO_ResetBits(GPIOB, GPIO_Pin_1); TIM_SetCompare1(TIM3, -cur_l); } /* 右轮同理,注意方向引脚取反 */ }PWM 频率取 8kHz。低于 1kHz 电机会有可闻啸叫,高于 20kHz 又会让 TB6612 开关损耗上升、芯片发烫。8kHz 刚好在听觉边缘,实测噪音可以接受。定时器预分频按 72MHz / 8kHz = 9000 计数值配置,占空比分辨率足够。
Ramp里 80 这个数字要和任务周期配套看:20ms 调一次,每次最多变 80,换算成加速度是 4000 单位/秒,从静止到全速大约 0.25 秒。这个加速手感比较自然,既能快速起步,又不会让电源跌得太狠。想更柔和就把 80 改成 40,代价是避障后的重新起步会慢半拍。
还有一点:斜坡函数必须放在最后一级,任何上层算法算出来的速度都要经过它。我见过有同学在避障逻辑里直接写TIM_SetCompare绕过去,结果避障时车「噌」地一下窜出去,查了半天以为是超声波的问题。
4.4 吸尘风机与擦拭机构的启停顺序和保护
清洁机构看着是两个开关,实际上是最容易烧器件的地方。风机是感性负载,关断瞬间反电动势能到几十伏;擦拭电机带水工作,堵转电流是额定的三到五倍。两者都需要单独的保护策略。
启停顺序上,先开风机,延迟 200ms 再开擦拭电机;关机时反过来,先停擦拭,再停风机。原因是同时启动两个大电流负载,浪涌叠加会把 5V 拉到 4V 以下,OLED 直接黑屏。这个 200ms 延迟用状态机实现,不要用Delay_ms。
typedef enum { CLN_IDLE, CLN_FAN_ON, CLN_BRUSH_ON, CLN_RUNNING, CLN_STOP } ClnState_t; static void Cleaner_Task(void) /* 100ms 调用一次 */ { static ClnState_t s = CLN_IDLE; static uint8_t step = 0; switch (s) { case CLN_IDLE: if (g_clean_req) { FAN_ON(); step = 0; s = CLN_FAN_ON; } break; case CLN_FAN_ON: /* 等 200ms,即 2 个周期 */ if (++step >= 2) { BRUSH_ON(); step = 0; s = CLN_RUNNING; } break; case CLN_RUNNING: /* 堵转检测:电流采样值连续 5 个周期超阈值,判定堵转 */ if (ADC_GetCurrent() > OVER_CUR_TH) { if (++step >= 5) { BRUSH_OFF(); FAN_OFF(); s = CLN_STOP; g_fault = 1; } } else { step = 0; } if (!g_clean_req) { BRUSH_OFF(); step = 0; s = CLN_STOP; } break; case CLN_STOP: if (++step >= 2) { FAN_OFF(); s = CLN_IDLE; } break; } }电流采样用一个小阻值采样电阻(0.1Ω)串在 MOS 管源极,经运放放大后送 ADC。OVER_CUR_TH的取值要先实测:正常吸尘时电流约 400mA,堵转时超过 1.2A,阈值取 800mA 比较稳。没有采样电路也能做简化版,用「风机开启但电流检测脚为低」这种粗糙判断,但精度差,答辩时不好讲。
硬件上,风机和擦拭电机两端各反并一个 1N5819 肖特基二极管做续流,MOS 管栅极串 100Ω 电阻、并 10k 下拉,防止上电瞬间误导通。这几毛钱的元件,决定了你能不能连跑二十分钟不炸管。
4.5 主状态机与按键显示交互
所有逻辑最后收敛到一个状态机里,好处是任何时刻车的行为只有一种解释,出问题能立刻定位在哪个状态。
typedef enum { ST_IDLE, /* 待机,等待按键 */ ST_LINE, /* 循迹清洁 */ ST_AVOID, /* 避障处理 */ ST_BACK, /* 短暂后退拉开距离 */ ST_FAULT /* 故障:丢线超时、堵转、低电量 */ } CarState_t; CarState_t g_state = ST_IDLE; static void State_Machine(void) /* 20ms 调用 */ { switch (g_state) { case ST_IDLE: Motor_SetSpeed(0, 0); if (g_key_start) { g_clean_req = 1; g_state = ST_LINE; } break; case ST_LINE: if (g_avoid_flag) { g_state = ST_BACK; } else { Line_Task(); } break; case ST_BACK: Motor_SetSpeed(-500, -500); if (++back_cnt > 15) { back_cnt = 0; g_state = ST_AVOID; } break; case ST_AVOID: /* 转向时序,完成后回 ST_LINE */ break; case ST_FAULT: Motor_SetSpeed(0, 0); g_clean_req = 0; if (g_key_reset) { g_fault = 0; g_state = ST_IDLE; } break; } }按键用 20ms 周期扫描 + 计数器消抖,连续 3 次读到相同电平才认账。三个键分别做启动/暂停、模式切换、故障复位。OLED 只刷变化的区域:电量每 200ms 刷一次,状态文字只在状态切换时刷,这样 I2C 占用的时间可以压到很低,不会和超声波抢时序。
低电量保护放在 ADC 任务里,电压低于 6.4V(2S 电池的单节 3.2V)就强制进ST_FAULT并停清洁机构。锂电池过放会永久损坏,这个保护比什么功能都重要,而且答辩时是个加分点——它说明你考虑过实际使用场景。
5. 避坑与常见问题排查:调试台上最容易翻车的五件事
5.1 电气与供电类问题
现象一:车跑着跑着突然重启,OLED 闪一下归零。
原因几乎都是电源跌落到主控复位阈值以下。电机启动或转向瞬间电流能到 2A,电池内阻加上线损,7.4V 瞬间掉到 5V 以下,LM2596 输入欠压,3.3V 跟着塌。查的时候别急着怀疑看门狗,先拿示波器(没有就用万用表直流档)搭在 3.3V 上看启动瞬间有没有下陷。
解决分三层:一是加大功率支路的电解电容,从 470μF 提到 1000μF,给浪涌提供本地储能;二是主控供电前串一个 SS34 肖特基再加 220μF,形成简单的单向隔离;三是软件层加斜坡限幅(见 4.3),从源头减少电流突变。三层做完,这个现象基本绝迹。
现象二:风机一转,超声波距离就开始乱跳,或者直接输出 0。
原因是风机换向火花产生的高频噪声窜进了逻辑电源。很多同学把超声波 VCC 就近接到 5V 功率轨上,风机一启动,5V 上叠了几百毫伏的毛刺,超声波模块内部比较器误触发。
解决:超声波和 OLED 的 VCC 一律从 AMS1117 输出的逻辑 3.3V/5V 支路取,并且在这条支路上串一个磁珠(FB 100Ω@100MHz)加 10μF 电容。另外超声波模块背面焊一个 0.1μF 瓷片到地。改完再测,波形干净很多。
5.2 传感与控制逻辑类问题
现象三:循迹在浅色瓷砖上正常,换到深色地面就一路丢线。
这是阈值固定的典型症状。TCRT5000 的输出取决于反射光强,深色地面反射弱,接收管输出的电平可能落在阈值附近,单片机读到的就是随机 0/1。
解决不能靠改一个电阻,要做动态标定:上电后让车原地转一圈,采样四路传感器在黑白两种地面上的电平,取中间值作为阈值存进变量。没有条件做标定的场合,退一步的做法是把四路读数做成模拟量(如果有 ADC),用比较相对大小而不是绝对电平来判断。这一点在答辩时很能讲,因为它体现你理解了传感器的物理原理,而不是只会调库。
现象四:十字路口或者黑线交叉的地方,车会停住或者原地打转。
原因是四路全黑时Line_GetError返回LINE_LOST,和四路全白(真丢线)混为一谈。这两个状态的处理逻辑完全不同:全黑应该直行冲过去,全白才该找线。
解决:把两种情况的返回值分开,全黑返回一个特殊值LINE_CROSS,状态机里遇到它就保持上一帧的偏差直行 300ms。同时把「四路全白」判定为丢线,走 4.1 里的找回逻辑。这个改动只有五行代码,但能解决现场演示时最尴尬的卡顿。
5.3 机械与执行机构类问题
现象五:擦拭辊转了两分钟就停,摸 MOS 管烫手,重启后又好了。
典型的堵转或过流。常见成因有三个:擦拭辊压得太紧,阻力过大;轴承进了水,摩擦上升;MOS 管没有散热,自身温升导致导通电阻变大,进一步发热,恶性循环。
解决:机械上把擦拭辊的压紧力调松,做到「贴上但不压死」,用手转能轻松转动;电气上给 MOS 管加一小片铝片散热,并在源极串 0.1Ω 采样电阻做堵转检测(见 4.4),连续 500ms 超阈值就关断,过 3 秒后自动重试一次,重试失败进故障态。这套逻辑写进去之后,即使机械上真出问题,也只是功能暂停,不至于烧板子。
排查顺序上,我的习惯是先电源、再信号、后机械。因为电源问题表现最像「玄学」,先排除掉能省一半时间;信号问题可以用万用表和串口打印定位;机械问题最好判断,但拆装最费时间,所以放最后。
6. 验收与答辩:怎么把一台小车讲成一份能过的毕业设计
验收这一步,考的其实不是你做了多少功能,而是你能不能把做过的事说清楚。我见过功能做得挺全但答辩被问懵的,也见过只做了循迹避障但拿了高分的。差别在准备。
先做一份自测记录表,验收前一天按表跑一遍,数据填进去。导师现场提问时,你翻出这张表,比任何解释都有说服力。
| 测试项 | 测试条件 | 实测结果 | 判定 |
|---|---|---|---|
| 直线循迹 | 2m 黑线,瓷砖地面 | 偏差 ±1.5cm,用时 12s | 通过 |
| 弯道循迹 | 90° 弯,半径 30cm | 5 圈无脱线 | 通过 |
| 避障响应 | 20cm 处放纸箱 | 平均 0.42s 停车转向 | 通过 |
| 吸尘回收率 | 5g 面粉,单次通过 | 残留 1.2g,回收 76% | 通过 |
| 擦拭效果 | 湿擦 1m² | 无水痕,纸巾按压无水渍 | 通过 |
| 连续续航 | 满电全程运行 | 27 分钟 | 通过 |
| 低电保护 | 调压至 6.3V | 30s 内停机并显示告警 | 通过 |
答辩现场的问答,高频问题就那么几个,提前准备好答案:「为什么选这个主控」——讲 PWM 路数和调试效率,别讲主频高;「循迹精度受什么影响」——讲传感器间距、地面反射率、控制周期三者关系;「如果地面是全黑的怎么办」——这正是第 5 章那个坑,你讲出来就是加分项;「成本多少」——把 BOM 列出来,主控 12 元、驱动 8 元、传感器 20 元、结构件 40 元,总数说清楚,显得你真算过账。
还有一个容易被忽略的细节:演示前把场地清干净,黑线用哑光胶带而不是亮面胶带,环境光别直射传感器。这些不是作弊,是控制变量。我见过有同学的車在实验室跑得好好的,搬到答辩教室的抛光地砖上直接失灵,当场懵住。带一卷哑光胶带和一张白纸过去,现场铺一段临时跑道,是最稳的做法。
从那以后我每次带学生做这类实物项目,都强制走一遍「换三个场地各跑五圈」的流程——实验室、走廊、答辩教室。因为很多时候问题不在代码里,而在你从没让它见过的那块地面上。这套多功能智能清洁小车的完整设计资料我整理好了,包括原理图、程序源码、结构件图纸和上面那份验收表,需要的可以拿去改,把这套框架套到自己的题目上,能省掉不少返工的夜晚。希望帮到你。
本文还有配套的精品资源,点击获取