把一台服务机器人从“能走”调到“跟得住人、躲得开障碍”,中间隔着的不是一两个算法,而是一整套从电机驱动到多传感器协同的系统工程。我最近基于 Arduino 平台做的这套多传感器融合动态避障跟随方案,动力核心选定了 BLDC 无刷电机,感知层面融合了摄像头、激光测距、超声波和 IMU,最终实现的效果是:人在前面正常走路,机器人在后面保持距离跟随,遇到突然出现的障碍物能先躲开,绕过去之后重新锁定目标继续跟。
整套方案并不是一步到位的。最初我用普通直流减速电机搭了一版底盘,跑起来噪音大、速度一高就发飘,换到 BLDC 之后稳定性明显上了一个台阶。这条路走下来,最值得记录的其实不是某个算法多精妙,而是每一个“让它真正跑起来”的细节。这篇文章我会把电机选型计算、传感器融合思路、驱动闭环搭建、跟随与避障的决策逻辑,以及实测路测中的标定和排障过程完整写出来,适合正在做课程设计、开源服务机器人,或者想从零搭一套可复现移动底盘的人直接参考。
1. 为什么服务机器人底盘选 BLDC:从动力特性到选型计算
1.1 无刷电机在室内服务场景里的核心优势
室内服务机器人跟工业机械臂不同,它要长时间在人员附近工作,对噪音、寿命、动态响应都有隐性要求。BLDC 无刷电机在这些方面的优势是结构性的:没有碳刷和换向器摩擦,运行噪音低,寿命主要取决于轴承;转子是永磁体,同样体积下扭矩密度比有刷直流电机高;散热条件好,持续运行不容易热衰减。
更重要的是动态性能。跟随和避障场景里,机器人需要频繁启停、加减速,BLDC 配合霍尔传感器或磁编码器做闭环控制,响应速度比普通直流减速电机快一个量级。我在测试中用突然急停的方式对比过,BLDC 版本从 1.2m/s 刹停的滑行距离明显更短,这对室内避障的安全边界很关键。
1.2 和直流减速电机、步进电机放一起比一比
很多初学者会在三种电机之间犹豫,我直接列一张对比表,把室内服务机器人场景最关心的几项指标放一起看:
| 对比项 | BLDC + 行星减速箱 | 直流减速电机 | 步进电机 |
|---|---|---|---|
| 噪音 | 低,主要来自齿轮 | 中高,碳刷摩擦明显 | 低速啸叫明显 |
| 寿命 | 长,无电刷磨损 | 碳刷需定期更换 | 无磨损但怕堵转 |
| 扭矩密度 | 高 | 中 | 中 |
| 动态响应 | 快,适合频繁启停 | 一般 | 慢,跟随性差 |
| 闭环成本 | 需要驱动板+编码器 | 编码器便宜 | 开环也能走 |
| 能耗效率 | 高(80%以上) | 中(50-65%) | 低,持续通电 |
| 初学者友好度 | 中,驱动稍复杂 | 高 | 高 |
步进电机最大的问题是在低速重载时容易出现失步,一旦丢步,机器人的里程计数据就全乱了,跟随目标时位置会越偏越多。直流减速电机便宜好用,但效率和噪音在要求安静、长时间运行的室内场景里是硬伤。所以我的结论是:只要预算允许,服务机器人底盘优先选 BLDC,哪怕驱动成本高一点也值得。
1.3 一个15kg室内机器人的动力估算实例
选型不能凭感觉,我用一个 15kg 左右的室内服务机器人平台做粗算。先定目标:最高速度 1.2m/s,加速度 0.5m/s²,轮径 125mm。
- 轮子转速:1.2 / (π × 0.125) ≈ 3.06 转/秒 ≈ 183 RPM
- 滚动阻力:取滚动阻力系数 0.02,F滚 = 0.02 × 15 × 9.8 ≈ 2.94N
- 加速力:F加 = 15 × 0.5 = 7.5N
- 总驱动力 ≈ 10.4N,车轮半径 0.0625m,总扭矩 ≈ 0.65Nm,单轮约 0.33Nm
- 机械功率 = 10.4 × 1.2 ≈ 12.5W,考虑传动效率 60%-70%,电功率至少 20W,实际留 2 倍余量
按这个结果,单轮输出扭矩按 0.6-0.7Nm 去选电机和减速比。常见做法是选额定转速 1000-3000 RPM 的 BLDC,配 5:1 到 8:1 的行星减速箱,使轮端转速落在 180-200 RPM 附近。关键提醒:不要只看电机标称功率,一定要把“减速后轮端扭矩”和“额定转速下轮端线速度”两个值同时验算,缺一个就容易出现跑得动但太慢,或者速度快了但坡道和急停扛不住的情况。
2. 多传感器融合的系统设计:每类传感器负责哪个“器官”
2.1 跟随链路:摄像头给角度,激光给距离
“跟随”这个功能,本质上要回答两个问题:目标在哪?离我多远?单靠一只传感器很难同时回答好。
摄像头擅长回答方位:通过颜色识别、二维码或者人形检测,可以得到目标在图像中的像素位置,换算成相对机器人的水平偏转角。但摄像头测距天生不靠谱,单目没有任何尺度信息,同一个目标在不同距离下的像素大小只能大致估。所以我用激光测距模块专门负责距离:一旦视觉锁定目标,就朝目标方向发出测距请求,拿到稳定的距离值。
这里有一个经验:摄像头识别到目标后,先不急着用像素尺寸换距离,而是把目标的水平偏转角传给云台或转向指令,让测距模块对准那个方向。这样视觉给角度、激光给距离,各自干最擅长的事,比任何单一传感器都稳。
2.2 避障链路:超声波打近身,激光雷达看全局
避障比跟随更讲究层次。贴近车身的障碍(20cm 内)必须用超声波,因为激光雷达存在扫描盲区,而且对玻璃、黑色哑光面容易失效。超声波模块成本低、响应快,近距离精度足够,缺点是波束角大、方向分辨率差,三个模块一起装在同一平面时还容易互相串扰。
中远距离的动态避障,我建议用单线激光雷达或者大视场角的 ToF 测距模组。激光雷达能提供 360 度或 270 度的扫描点云,可以提前发现 1-3 米外正在接近的行人。IMU 在这条链路里的角色是“运动感知”:机器人急转、打滑、下坡时,IMU 的角速度和加速度数据能告诉主控“当前姿态不对”,避免传感器数据在车体倾斜时做出错误判断。
2.3 融合仲裁:为什么“加权平均”在这里行不通
很多教程喜欢讲“把多个传感器数据加权平均”,但在动态避障这个场景里,加权平均非常危险。假设超声波说前方 15cm 有障碍,激光雷达因为视角问题说前方 80cm 空旷,平均一下得到 47cm,机器人可能会继续往前开,然后撞上。
正确的做法是优先级仲裁,而不是数值平均。安全类传感器有最高优先级:任何传感器报告近距离障碍,都立即触发避障行为,其他传感器数据只能辅助判断绕行方向,不能否决“必须停下或转向”这个结论。换一种说法:跟随是目标导向的需求,避障是安全约束,约束永远大于需求。
融合策略可以简单归纳成一张表:
| 场景 | 主导传感器 | 辅助传感器 | 决策 |
|---|---|---|---|
| 前方 30cm 内障碍 | 超声波 | 激光雷达确认宽度 | 急停或原地转向 |
| 目标在视野内,前方无碍 | 摄像头 | 激光测距 | 直线跟随 |
| 目标被遮挡,前方开阔 | 激光雷达 | 超声波 | 暂停跟随,原地搜索 |
| 左右近距离同时有障碍 | 左右超声波 | IMU 判断打滑 | 原地旋转到安全方向 |
| 前方部分阻塞,可绕行 | 激光雷达 | 摄像头持续跟踪 | 减速,绕行后恢复 |
2.4 主控资源分配与传感器时序安排
Arduino 平台的资源有限,传感器一多,轮询顺序就不能乱来。我的主循环跑在 10ms 定时中断里,传感器各自分配采样周期:IMU 10ms 一次,超声波三个模块每 30ms 错开触发,激光测距 20Hz,摄像头 15FPS。错开超声波触发时间这一点很重要,同一时刻触发多个超声波模块,回波会互相干扰,测出来全是假距离。
主控选型上,如果只是跑一个超声波加一个摄像头,普通 Arduino 型号够用;但要把激光雷达、IMU、双路 BLDC 闭环全塞进一颗芯片,建议选主频更高的型号,或者用“Arduino 负责运动控制 + 协处理器负责视觉”的分工方案。我最后采用了后者,摄像头识别放协处理器,Arduino 只接收目标角度和距离的打包数据,主控负载从接近满载降到了一半以下,稳定性明显提升。
3. 底盘搭建与驱动闭环:接线、供电和 PID 初调
3.1 硬件清单与布线原则
这版底盘的核心硬件如下:
- 主控:Arduino 高性能型号
- 动力:两个 BLDC 电机(带霍尔传感器)+ 行星减速箱,轮端额定 180RPM 左右
- 驱动:双路 BLDC 驱动板,支持方波或 FOC 控制
- 感知:摄像头模组、激光测距模块、三个超声波模块、六轴 IMU
- 供电:3S 锂电池组、DC-DC 降压模块、大容量电解电容
- 底盘:两轮差速结构,前部万向轮
布线遵循一个铁律:动力线和信号线彻底分离。电机线走底盘下层并尽量短,传感器信号线走上层且远离电机线;IMU 不要和电机驱动板放在同一块金属板上,振动和电磁干扰都会让陀螺仪漂移。所有信号线尽量用带屏蔽或双绞形式,实在没有条件,至少把排线缩短。
3.2 供电预算的粗算方法
BLDC 启动瞬间电流很大,这是整个系统最容易翻车的地方。按前面算的功率需求,正常行驶两个电机合计电流 3-4A,但堵转或急加速瞬间可能冲到 8-10A。加上主控、传感器和视觉协处理器,整机持续电流按 5A@12V 设计,峰值按 15A 留余量。
具体做法:3S 锂电池(11.1V 标称)直接供电机驱动,传感器和主控单独用一路 DC-DC 降压到 5V,不能共用电机电源。同时在驱动板的电源输入端并联一只 1000-2200μF 的低内阻电解电容,用来吸收电机换相时的电流尖峰。这一步救了我两次,第一次是电机一启动主控就复位,第二次是激光雷达在急加速时丢包,都是加电容之后解决的。
3.3 速度闭环与 PID 参数起步值
BLDC 的驱动方式有两类:方波驱动(六步换相)和 FOC 矢量控制。方波驱动接线简单、用 PWM 控制油门即可,适合先用霍尔传感器跑通速度闭环;FOC 扭矩控制更细腻,低速性能更好,但需要电流采样和编码器。我的建议是原型阶段先方波驱动把整车跑起来,后续再升级 FOC。
速度闭环用霍尔传感器测转速,PID 输出给驱动板的油门。调试顺序很关键:先只调比例 P,让速度能跟上设定值但可能有静差;再加积分 I 消除静差;最后加微分 D 抑制超调。我在这套底盘上用的起步参数是 Kp=0.8、Ki=0.05、Kd=0.1,采样周期 20ms,实测跑起来稳定在设定速度的 ±2% 以内。参数从哪来?先用小 Kp 观察响应,缓慢加到轮速出现轻微振荡再退回一半,这个“经验值”比任何公式都实用。
4. 跟随与动态避障的算法落地:状态机加优先级仲裁
4.1 目标跟随:从像素偏移到差速指令
目标跟随的控制链路是:摄像头识别目标 → 计算目标中心相对画面中心的像素偏移 → 换算成偏转角 → 结合激光测距的距离值 → 生成左右轮差速指令。
设画面水平视野角为 FOV,画面宽度为 W,目标像素偏移为 Δx,则偏转角 θ ≈ Δx × FOV / W。目标距离由激光测得,记为 d。期望保持距离 d_ref(我通常设 60-80cm),那么:
- 线速度 v = clamp(kp_dist × (d - d_ref), 0, v_max)(目标太近就停)
- 角速度 ω = clamp(kp_angle × θ, -ω_max, ω_max)
- 左轮速度 = v - ω × B,右轮速度 = v + ω × B,B 是轮距相关换算系数
这个结构简单但非常好用。kp_dist 决定跟得快不快,kp_angle 决定转弯的灵敏程度。我在实测里发现一个要点:跟随阶段的线速度上限不要超过 0.8m/s,一旦目标突然停下,机器人的制动距离才能在安全范围内;速度上限过高时,跟随会变成“追尾”,体验非常差。
4.2 动态避障:左中右分区与逃逸方向选择
动态避障的核心是快速判断“往哪躲”。我把车体前方按 60 度一个区划分成左、中、右三个扇区,每个扇区由对应方向的超声波和激光雷达数据共同维护一个“最小障碍距离”。默认安全距离 40cm,一旦某个扇区低于阈值,就标记为占用。
如果中间扇区占用,优先尝试转向无障碍的一侧;两侧都占用,原地旋转找一个历史数据里障碍最远的方向;三个方向都堵死,急停等待。绕行过程中摄像头持续工作,一旦障碍物离开中间扇区,立刻切回跟随模式。这个“先避障、再恢复”的流程,靠一个简单的状态机就能实现。
4.3 主循环伪代码与状态切换条件
// 简化版主循环逻辑 void loop() { sensors_update(); // 同步读取各传感器最新数据 float obs_front = min(ultrasonic_center, lidar_center); if (obs_front < SAFE_DIST) { // 安全优先级最高:进入避障状态 if (left_free && !right_free) turn_left(AVOID_SPEED); else if (!left_free && right_free) turn_right(AVOID_SPEED); else if (!left_free && !right_free) rotate_in_place(SEARCH_DIR); else drive_straight(AVOID_SPEED); state = AVOIDING; } else if (target_locked) { // 前方安全,恢复跟随 float angle = target_bearing(); float dist = target_distance(); drive_with_follow(angle, dist); state = FOLLOWING; } else { // 目标丢失:原地搜索 rotate_in_place(SEARCH_SPEED); state = SEARCHING; } }状态切换的条件写清楚之后,整个项目的逻辑就透明了。我测试时发现最常出问题的是“避障结束后切回跟随”的判断:如果障碍物只是从侧面划过,中间扇区短暂低于阈值,机器人就会频繁急停。解决办法是加一个 300ms 的去抖时间,障碍距离连续低于阈值超过 300ms 才触发避障,避免单帧噪声误触发。
4.4 参数调优与平滑处理
算法逻辑对了,还要注意输出平滑。直接按 PID 输出值驱动电机,速度跳变会非常生硬,表现为机器人“一顿一顿”的。我在速度指令输出前加了一层斜坡限制:线速度和角速度的变化率分别限制在 0.5m/s² 和 1.5rad/s² 以内,实测跟随的流畅度提升很明显。
另外建议在所有传感器数据入口加一个简单的中值滤波或一阶低通滤波。超声波偶尔会蹦出异常值,激光测距在目标表面倾斜时也会有抖动,一个窗口为 3 的中值滤波就能过滤掉大部分噪声,成本极低,收益极大。
5. 实测路测:标定流程、常见故障与我的排查记录
5.1 四步标定:先让底盘“说实话”
任何算法要想好使,传感器数据必须先可信。我每次换硬件之后都会做一轮完整标定:
- PWM-转速映射标定:给驱动板一组递增的 PWM 脉宽,记录霍尔传感器反馈的轮速,建立查询表。这个表是速度闭环的基准,不同电池电量下会有偏差,建议至少标定一次满电和一次低电状态。
- IMU 零偏标定:机器人静止放置,连续采集 2000 个 IMU 样本取平均,作为陀螺仪零漂补偿值。
- 轮径修正:让机器人沿直线跑 5 米,对比里程计读数,计算轮径修正系数。这个系数对转向精度影响巨大,我最初的机器人跑 5 米会偏 30 度,修正后 5 米偏差不超过 2 度。
- PID 闭环验证:速度闭环稳定后,再叠加一个航向保持的闭环,用 IMU 的偏航角做反馈,让机器人能直线走。
标定不仅是“校准”,更是“验证硬件是否正常”。有一次我把超声波探测距离设置的特别远,发现数据总在跳,标定时才发现是一个模块的供电电压不稳,换成独立供电后立刻稳定。这类问题如果直接调试算法,会浪费大量时间。
5.2 动力与供电类故障
第一个坑是启动瞬间主控复位,这是我在文章开头提到的那个事故。排查链路:先怀疑代码死循环,用串口打印发现主控在电机启动瞬间完全失联;再测电源,发现电机启动瞬间驱动板电源输入端电压从 12V 跌到 8V 以下。验证方法很简单,给驱动板单独供电,问题消失。解决措施是在驱动板端并联 2200μF 电容,主控和传感器走独立 DC-DC,问题彻底解决。
第二个坑是急加速时传感器丢包。激光雷达和摄像头都通过串口通讯,电机急加速时电源波动导致串口电平不稳,数据帧校验失败被丢弃。解决方法是传感器全部由独立稳压供电,并在代码里对串口数据做帧超时重试。这个坑在纯功能测试时不会出现,只有上了真实路面急加速才暴露。
5.3 传感器与感知类故障
传感器层面的坑更隐蔽。三个超声波模块同时触发时,A 模块发出的声波被 B 模块收到,就会产生一个假的远距离回波。我用的是“错开触发时序”的办法,每个模块间隔 30ms 以上,串扰立刻消失。如果你用的是多路超声波,务必检查这一类问题。
激光测距模块在黑色哑光地面和玻璃幕墙附近会有异常读数:黑色表面吸收红外光导致测距偏大甚至无数据,玻璃表面会穿透或者产生镜面反射导致测距偏小。解决办法是给机器人加一个“近身超声波兜底”逻辑:当激光数据异常跳变时,以超声波数据为准;同时安装高度控制在 20-30cm,避免照到地面。
摄像头跟随在逆光场景下目标容易丢失。我测试时发现,窗口逆光时目标变成一个黑色剪影,颜色识别完全失效。解决思路是改用特征更稳定的标记(比如 ArUco 码)而不是颜色,同时在摄像头设置里固定曝光时间,避免画面亮度剧烈波动。
线缆屏蔽问题也值得一提:IMU 靠近电机线时,偏航角数据会出现周期性波动,后来把 IMU 移远并给信号线套了磁环,波形才恢复正常。这类电磁干扰问题在 Arduino 项目中很常见,排查时最容易迷惑人。
5.4 调试方法论:单变量替换与数据回放
整个路测过程给我最大的教训是:一次只改一个变量。同时调整 PID 参数、更换传感器位置、修改避障阈值,出问题后根本无法定位。我后来固定了一套路测流程:先在干净走廊上跑直线验证底盘,再分别测跟随和避障,最后合在一起做综合测试。
数据回放是关键工具。运行时把传感器数据、状态机状态、速度指令都通过串口或者 SD 卡记录下来,跑完之后逐帧回放,对比每个时刻的“感知-决策-执行”链路。大多数诡异问题,比如转向时偏、丢目标后原地转圈,都能从回放数据里一眼看出来。比如有一次跟随总是过度矫正方向,回放数据发现角速度输出滞后了传感器一拍,是滤波延迟太大导致的,调小滤波窗口后立刻好转。
整套项目做完,我最大的体会是“系统集成比单项技术更考验人”。BLDC 驱动、多传感器、PID 控制,每一项单独拿出来都有现成教程,难点全在于如何让它们在一个 15kg 的移动平台上稳定协同。如果你也准备做类似的机器人项目,我建议不要一上来就追求所有传感器全部到位,先用“一个超声波 + 一个摄像头”跑通跟随和避障的最小闭环,再逐步加入激光雷达、IMU 和更复杂的融合逻辑。每加一路传感器,都重新做一遍完整标定,这样的节奏看起来慢,实际反而是最快能稳定跑起来的路。后续如果继续扩展,我计划把轮速里程计和 IMU 做航迹推算,再叠加激光雷达做室内定位,让机器人不再依赖“跟着目标走”,而是真正知道自己身处房间的哪个位置——那就是下一阶段的课题了。