
简介《智能扫地机器人课程设计》面向自动化、电子信息、嵌入式及相关专业学生适合正进行课程设计、毕业设计选题或机器人入门实践的读者。内容围绕 AT89S52 主控展开涵盖电机驱动、红外与超声波避障、充电站检测、路径规划、无线通信、TFT 液晶显示和独立按键交互并对主控、驱动芯片与无线模块进行方案论证可帮助梳理系统框图、硬件电路原理和程序设计思路。资源包共 1 个 PDF 文件约 464KB结构紧凑便于阅读与打印。目前已有 390 人学习浏览说明该题目在课程设计场景中具有一定关注度。内容对 L298、ULN2003、RF903 等选型分析以及避障、自动回充、清扫模式切换等功能的讲解能辅助读者形成从需求分析到电路设计、再到算法实现的完整设计框架。1. 智能扫地机器人课程设计从“能动”到“扫得干净”的分水岭答辩现场最常见的画面是一台小车在桌面上绕着矿泉水瓶转圈转够三分钟老师喊停——因为它除了“会动”没有任何可量化的指标。智能扫地机器人课程设计真正的分水岭不在电机能不能转而在三个能被当场验证的数字固定场地内的覆盖率、单位时间内的碰撞次数、低电量回充的成功率。它把一个综合题目压缩进一台小车机械与底盘、嵌入式实时控制、传感器融合、路径规划与决策一层都不能省。近几年“具身智能机器人”成为热词很多题目 PDF 里也开始出现感知—决策—执行分层的字眼。但对课程设计这个尺度来说具身智能的落点很具体底盘是本体超声波与红外是感知状态机与覆盖算法是决策双轮差速是执行。这篇文章面向做智能机器人课程设计的学生和带课教师按硬件最小系统、运动闭环、覆盖路径、联调验证的顺序把每一步的参数怎么定、代码怎么写、坑在哪说清楚。2. 智能扫地机器人的硬件最小系统主控、传感器与电机驱动怎么定选型阶段最容易犯的错是先买激光雷达再想主控结果算力和供电都撑不住。合理的顺序是先看题目 PDF 里写了哪些必做功能再倒推主控和传感器。绝大多数课程设计的功能清单是“自主避障清扫 沿墙行走 低电回充”这个清单用裸机 MCU 就能完成不需要上 Linux。2.1 主控与电机驱动的三类方案对照方案典型配置算力与资源适合的功能清单主要短板单片机裸机STM32F103C8T6 或同级 MCU72MHz64KB Flash无操作系统避障、弓字形、回充上激光建图基本无望带无线 MCUESP32-S3 一类双核 240MHz带 PSRAM 与无线需要上位机/手机联调ADC 抖动大无线功耗高MCU Linux 协处理STM32 负责执行树莓派负责决策应用处理器 1GHz 级要跑 ROS、要建图供电与调试链路复杂成本翻倍电机驱动比主控更容易被忽略。L298N 这类老模块压降大、发热明显两节 18650 供电时电机端往往只剩 6V 上下跑一会儿就掉速TB6612 或 DRV8833 的导通电阻低得多同样电压下爬坡和越障表现差别很大。驱动模块的供电必须与主控分开走一路否则电机启动瞬间的浪涌会把 MCU 拉复位。提示如果题目里写了“具身智能”或“分层架构”最省事的做法是把决策层放到协处理器上MCU 只保留实时执行循环两边用串口协议通信答辩时分层讲得清实现量也不爆炸。2.2 传感器清单、安装高度与关键参数避障传感器的安装高度直接决定误报率这一栏比型号更重要。下表的参数是常见落地值实际以实物实测为准。用途常见器件安装位置关键参数常见坑前方避障超声波测距模块车头中线离地 120~150mm量程 2~400cm需 60ms 冷却对窗帘、斜面回波弱连续触发要限频侧向沿墙红外测距模拟输出右侧离地约 100mm有效 10~80cm输出非线性黑色踢脚线吸红外读数虚高悬崖检测反射式光电对管底盘前缘 4 个离地 8~12mm数字或模拟输出离地过高测不到地面过低刮地毯姿态与转角六轴陀螺仪加速度计车体几何中心保持水平量程 ±250~±2000 dps 可选零偏随温度漂移必须上电标定里程计霍尔或光电编码器减速箱输出轴或电机尾轴11~13 PPR只用单轮累计航向会越跑越偏碰撞检测微动开关或碰撞条车头左右前角常开触点不做去抖会连续触发轮径、轮距、编码器线数这三个机械参数必须在装配完成后实测不要直接抄电机规格书。用游标卡尺量轮子外径用卷尺量两轮中心距误差 2% 就能让弓字形在 4 米长的房间里跑出明显的斜线。2.3 上电自检代码把故障挡在跑之前每次改完接线都先跑自检比跑起来再排查省时间。下面这段覆盖了悬崖误报、超声波无回波、电机接线反相三类高频故障。// 上电自检悬崖、避障、电机依次验证任何一项失败就停在这一步 #define PIN_MOTOR_L_PWM 9 #define PIN_MOTOR_R_PWM 10 #define PIN_TRIG 7 #define PIN_ECHO 8 #define PIN_CLIFF_L A0 #define CLIFF_THRESHOLD 520 // 12 位 ADC白地面实测约 720悬空约 300 void setup() { Serial.begin(115200); pinMode(PIN_MOTOR_L_PWM, OUTPUT); pinMode(PIN_MOTOR_R_PWM, OUTPUT); pinMode(PIN_TRIG, OUTPUT); pinMode(PIN_ECHO, INPUT); selfTest(); } float readDistanceCm() { digitalWrite(PIN_TRIG, LOW); delayMicroseconds(3); digitalWrite(PIN_TRIG, HIGH); delayMicroseconds(10); digitalWrite(PIN_TRIG, LOW); // 20ms 超时约等于 3.4m避免无回波时死等 unsigned long us pulseIn(PIN_ECHO, HIGH, 20000UL); return us 0 ? -1.0f : us * 0.0343f / 2.0f; } void selfTest() { if (analogRead(PIN_CLIFF_L) CLIFF_THRESHOLD) { Serial.println(FAIL: cliff sensor on flat ground); while (1); } float d readDistanceCm(); if (d 0 || d 200) { Serial.println(FAIL: ultrasonic no echo); while (1); } analogWrite(PIN_MOTOR_L_PWM, 90); // 8 位 PWM约 35% 占空比低于死区电机不转 analogWrite(PIN_MOTOR_R_PWM, 90); delay(600); // 这 600ms 用人眼确认两轮同向转动 analogWrite(PIN_MOTOR_L_PWM, 0); analogWrite(PIN_MOTOR_R_PWM, 0); Serial.println(SELFTEST PASS); }pulseIn的第三个参数是超时时间单位微秒不设的话在无回波时会把主循环卡住表现就是“车突然不动了”。系数 0.0343 是声速的 cm/us 换算值除以 2 是因为声波走了往返。CLIFF_THRESHOLD必须现场标定把车放在实际地面上读一次再抬起来读一次取两者中间偏地面一侧的值取太靠中间会在地毯上误报。PWM 的 90 是试探性起点具体值取决于电机减速比和电池电压如果 90 不转就往上加加到手能明显感觉到转动为止。3. 运动控制与避障闭环编码器里程计、PID 调速与沿墙清扫避障和路径规划都建立在“底盘能按指令走准”这个前提上。很多课程设计卡在这一层传感器没问题、算法也写对了但车走不直弓字形就变成锯齿形覆盖率自然上不去。这一章把里程计、速度环 PID、避障状态机三件事按依赖顺序做完。3.1 差速底盘的里程计模型与标定差速底盘的运动学很简单左右轮各走一段距离用两轮差值算转角用平均值算位移。难点全在标定上。// 里程计更新10ms 定时中断里调用dTickL/dTickR 由编码器中断累加得到 const float WHEEL_DIA 0.065f; // 轮径 65mm卡尺实测 const float PPR 13.0f; // 编码器每转脉冲数 const float GEAR 30.0f; // 减速比 1:30 const float TICK_MM (3.1415926f * WHEEL_DIA * 1000.0f) / (PPR * GEAR * 4.0f); const float WHEEL_BASE 0.145f; // 两轮中心距 145mm float x 0, y 0, th 0; // 位姿单位 m 与 rad void updateOdometry(long dTickL, long dTickR, float gyroZ) { float dL dTickL * TICK_MM / 1000.0f; float dR dTickR * TICK_MM / 1000.0f; float dTheta (dR - dL) / WHEEL_BASE; // 编码器与陀螺仪加权融合抑制单侧打滑造成的航向漂移 float fused 0.4f * dTheta 0.6f * (gyroZ * 0.01f); th fused; float dS (dL dR) * 0.5f; x dS * cosf(th); y dS * sinf(th); }TICK_MM里乘 4 是四倍频计数如果代码只在单相上升沿计数就改成乘 1否则里程会差 4 倍。融合权重 0.4/0.6 不是理论推导值是跑五次 2 米直行后按末端横向误差调出来的经验值打滑严重的场地可以再往陀螺仪一侧偏。下表是三项必做标定的判据。标定项方法合格判据不合格时的调整轮径系数直行 2000mm末端横向偏差小于 80mm按比例微调 TICK_MM轮距原地转 10 圈航向误差小于 5°调 WHEEL_BASE每次 ±5%陀螺仪零偏上电静置 2s 取 200 次均值零偏小于 0.5 dps写入 Flash开机直接加载3.2 双轮速度环 PID三个参数从零怎么调起来调速环的输入是目标线速度输出是 PWM 增量。课程设计里最容易出问题的是积分项长直道上积分一路累积一遇到障碍转向就“回冲”车会猛地窜一下。// 双轮速度环 PID20ms 调用一次目标速度单位 mm/s typedef struct { float kp, ki, kd; float i, lastErr, outMax; } PID; float pidStep(PID *p, float target, float actual, float dt) { float err target - actual; p-i err * dt; // 积分限幅防止长直道结束后电机回冲 float iMax p-outMax / (p-ki 0.0001f ? p-ki : 1.0f); if (p-i iMax) p-i iMax; if (p-i -iMax) p-i -iMax; float d (err - p-lastErr) / dt; p-lastErr err; float out p-kp * err p-ki * p-i p-kd * d; if (out p-outMax) out p-outMax; if (out -p-outMax) out -p-outMax; return out; }参数起始值作用现象与改法kp0.8决定响应速度车轮啸叫抖动就减半转向迟钝每次加 0.2ki0.15消除左右轮稳态速度差直线越跑越偏就加来回摆动就减kd0.02抑制超调起步顿挫就加噪声敏感先给速度加一阶滤波调参顺序固定先把 ki、kd 置零kp 加到轮子刚开始振荡退回该值的 60%再加 ki 直到稳态误差消失最后补一点 kd 压制起步超调。实际速度由编码器在固定 20ms 窗口内的脉冲数换算不要用 PWM 值代替速度做反馈电池电压下降时两者差异很大。3.3 沿墙清扫与悬崖检测的状态机实现避障逻辑用状态机写比用一堆 if 嵌套清晰得多。关键是把优先级排对悬崖最高碰撞次之前方避障再次沿墙跟随最后。enum State { GO_STRAIGHT, FOLLOW_WALL, BACK_OFF, TURN }; State st GO_STRAIGHT; void controlLoop() { float dFront readDistanceCm(); bool cliff (analogRead(PIN_CLIFF_L) CLIFF_THRESHOLD) || (analogRead(PIN_CLIFF_R) CLIFF_THRESHOLD); if (cliff) { st BACK_OFF; return; } // 悬崖优先级最高先退再转向 switch (st) { case GO_STRAIGHT: if (dFront 0 dFront 250) st FOLLOW_WALL; // 25cm 内进入沿墙 else setWheelSpeed(220, 220); // 220mm/s 直行 break; case FOLLOW_WALL: { float dSide readSideIrCm(); float err 120.0f - dSide; // 目标贴墙 12cm setWheelSpeed(180 1.2f * err, 180 - 1.2f * err); // 比例纠偏 if (dFront 350) st GO_STRAIGHT; // 前方开阔回到直行 break; } case BACK_OFF: setWheelSpeed(-150, -150); delay(350); st TURN; break; case TURN: setWheelSpeed(-160, 160); delay(500); st GO_STRAIGHT; break; } }沿墙目标距离取 12cm是因为侧向红外在 10~30cm 区间线性度最好太近进入盲区。比例系数 1.2 是“毫米误差到毫米每秒”的映射系数大了会贴着墙来回蹭小了贴不上。delay写法的阻塞问题在答辩时一定会被问标准回答是把延时改成定时器计时的非阻塞状态迁移代码量增加不多。注意红外测距的输出是非线性的直接用analogRead值线性换算距离误差很大稳妥做法是取 5~8 个标定点做分段线性或查表。4. 覆盖路径与回充逻辑弓字形、螺旋、分区在智能扫地机器人上的落地避障解决的是“不撞”覆盖解决的是“扫全”。这两件事在课程设计里经常被混在一起写结果代码里到处是“遇到障碍就随机转个角度”覆盖率自然只有六成。4.1 三种覆盖策略的取舍策略20㎡ 空场 10 分钟覆盖率重复率代码量适用场景随机碰撞60%~75%高且不可预测约 50 行家具密集的窄小空间弓字形88%~95%低200~300 行空旷、接近矩形的房间螺旋加沿边80% 左右中约 150 行从房间中央起步选哪种取决于题目要求。如果 PDF 里明确写了“覆盖率不低于 85%”那就只能做弓字形随机碰撞靠时间堆不出稳定结果。如果只要求“能避障清扫”随机碰撞加碰撞后随机转向反而是最省事且不容易翻车的方案答辩时如实说明策略与适用场景比硬写一个跑不通的规划算法得分高。4.2 弓字形路径的生成与换行逻辑弓字形的本质是“沿主方向走直线到边界后水平平移一个行距反向再走”。行距设置是唯一需要仔细算的参数大于机身宽度会漏扫小于机身宽度重复率上升、耗时增加取机身宽度的 0.8~0.9 倍比较合适。// 弓字形覆盖主方向直行到达边界后平移一个行距再反向 const float LANE_PITCH 300.0f; // 行距 300mm机身宽约 340mm void boustrophedon() { while (true) { // 1) 沿主方向直行直到前方 25cm 内有障碍或检测到悬崖 while (readDistanceCm() 250 !cliffDetected()) { straightByGyro(); // 陀螺仪锁航向抵消左右轮差异 } // 2) 侧移一个行距右转 90° → 前进 LANE_PITCH → 左转 90° turnToHeading(90); moveDistance(LANE_PITCH); turnToHeading(0); // 3) 侧向遇到长边障碍时先沿墙走完这条边再回到弓字形 if (obstacleOnSide()) followBoundaryOnce(); } }straightByGyro是内层闭环每 20ms 读一次陀螺仪航向偏差超过 2° 就用左右轮差速纠回turnToHeading用角度积分判断到位而不是靠固定的delay因为电池电压变化会让同样的延时转出不同角度。LANE_PITCH建议做成可调变量联调时从 250mm 试到 350mm用覆盖率数据挑最优值。实际房间里一定会有椅子腿这类障碍撞到后局部补扫再回到主方向比强行重规划整张图的工程代价低得多。4.3 回充对接与电量阈值策略回充是课程设计里最容易被砍掉的功能也是答辩最容易加分的一项。电量分三档管理思路是先保命再谈回充。电量区间动作关键参数大于 60%正常清扫风机全速行进速度 220mm/s20%~60%继续清扫每到一个区域边界探测一次充电座信号探测间隔约 30s小于 20%中止清扫转入回充流程关闭吸尘风机限速 80mm/s// 红外回充对接充电座发两束 38kHz 调制红外左右接收头幅度相同时判定对准 int irL readDockIr(LEFT); int irR readDockIr(RIGHT); if (irL 200 || irR 200) { // 阈值按实测定典型 150~400 float err (float)(irL - irR); if (fabsf(err) 30) { // 已对准低速直行对接 setWheelSpeed(80, 80); } else { setWheelSpeed(60 - 0.4f * err, 60 0.4f * err); // 差速纠偏 } if (bumpDetected()) stopAndCharge(); // 碰到充电触点即为到位 }调制红外必须用定时中断连续采样取峰值主循环里直接analogRead会被室内灯光和阳光干扰表现就是白天对不上、拉上窗帘就好了。对准窗口取 30 是左右 ADC 的差值调太小在远处永远进不了窗口调太大又会以歪斜姿态撞上去导致接触不良。对接阶段关掉风机和边刷一是省电二是避免边刷把车头顶偏。5. 联调与答辩用日志和覆盖率数据把智能扫地机器人课程设计做实跑通不等于能交付。答辩时老师最常问的两句是“你怎么知道覆盖率是多少”和“出故障你怎么定位”。这两件事都靠同一套东西解决位姿日志加一套可复现的验证流程。先给串口输出定格式每 100ms 打印一行时间, x, y, 航向同时把状态机当前状态和几个关键传感器读数一起打出来。跑完一轮清扫把日志存成 CSV用下面这段脚本按 30cm 网格离散化算出真实覆盖率。# 覆盖率统计把位姿日志按 0.3m 网格离散化统计被覆盖的格子比例 import sys CELL 0.3 grid set() for line in sys.stdin: # 每行格式: t,x,y,th parts line.strip().split(,) if len(parts) ! 4: continue x, y float(parts[1]), float(parts[2]) grid.add((int(x / CELL), int(y / CELL))) # 机身按 30cm 网格计入 room_cells 20.0 / (CELL * CELL) # 20 平方米房间的总格数 print(coverage %.1f%% % (100.0 * len(grid) / room_cells))网格边长取 0.3m 是折中接近一般课程设计小车的机身宽度取太细会把机身宽度带来的覆盖算漏。同一段日志还能反过来验证里程计精度把日志里的终点坐标和卷尺实测坐标对比偏差超过 150mm 就说明轮径系数或陀螺仪零偏没调好。下面这张表是联调阶段最高频的五类故障建议直接抄进课程设计报告的“调试记录”一节。现象大概率原因验证方法弓字形越跑越斜陀螺仪零偏未标定或轮径系数偏差静置读零偏值直行 2m 量横向偏差沿墙贴不上或撞墙红外受深色踢脚线影响用黑胶带贴在传感器前对比读数回充在 30cm 处打转红外阈值被阳光或灯光干扰遮光后重测阈值给接收头加遮光筒白地毯上突然停车悬崖反射率低于阈值实拍地面 ADC 值重新定阈值电机一给 PWM 主控复位电机启动电流拉低主控电压主控与电机分开供电就近加 470µF 电容答辩陈述里加一句分层说明会很占便宜感知层是超声波、红外与陀螺仪决策层是状态机加覆盖策略执行层是双轮差速与风机。这套说法正好对应具身智能机器人的经典分层讲清楚每层的输入输出和调试手段比展示一堆代码截图有说服力。最后留一个可现场演示的细节把陀螺仪零偏在每次上电静置 2 秒内取 200 次采样求均值并写入 Flash下次开机直接加载弓字形跑偏的概率会明显下降这一条改动量不到二十行却是评委最容易验证的改进点。本文还有配套的精品资源点击获取