1. 这个项目到底在做什么:把"猫"和"清洁"拆成可实现的工程问题
先把话说在前头:基于Arduino与激光测距传感器的猫型清洁机器人,本质上是三件不相干的事情叠加在一起——一个能自主移动的两轮差速小车、一套可靠的非接触测距避障系统、一个能钻进家具底下的小体积清洁执行机构,最后再套一层"猫"的外观与行为表现。它解决的不是"替代大型吸尘器"这种宏大命题,而是一个很具体的痛点:家里沙发底、床底、电视柜下沿那几厘米的缝隙,常规清洁工具伸不进去,人蹲下去又懒得动。做一个高度不超过8厘米、能自己乱窜、碰到障碍会拐弯、跑完一圈会自己停下来的小东西,成本控制在两三百块,这就是这个项目的全部野心。
适合看这篇东西的人,我大致分三类。第一类是完全没碰过单片机的新手,想找一个"看得见结果"的入门项目,比点灯有意思,比机械臂便宜;第二类是有一定电子基础、手上有闲置开发板和传感器,想找个周末把零件消耗掉;第三类是想做宠物互动类的玩具,需要一个能跑、能避障、有动态外观的运动平台。不管哪一类,下面这些内容都能直接抄:硬件清单、接线表、参数计算、控制代码、调试步骤、踩坑记录,一样不少。
我先说清楚这个项目的边界,免得你做到一半发现方向不对。它不是扫拖一体机,没有吸力,没有滚刷,没有水箱泵送,清洁部分就是一块静电除尘布或者一块超细纤维抹布被动拖擦,最多加一个小风扇做负压辅助。它也不做地图构建和全覆盖路径规划,因为激光测距单点扫描做SLAM在这个算力上不现实,硬做只会得到一个抖来抖去、覆盖率还不到30%的东西。它的行走逻辑是"随机游走+撞墙转向",虽然听起来很土,但实测在一个十几平的房间里跑二十分钟,对地面浮尘和毛发的清除效果是肉眼可见的,足够对得起这个成本。
1.1 为什么是猫型形态,而不是更好做的圆盘形
从工程角度讲,圆盘形是标准答案:没有棱角,卡住的概率低,转向时不会刮到踢脚线,外壳也好做。那我为什么还是选了猫型?因为这玩意儿是要放在家里的,不是放在实验室的。一个白色圆盘在地上转来转去,家里人第一反应是"这是个什么东西,是不是坏了";一只猫形的在地上拱来拱去,家里人第一反应是"哎这个挺好玩的"。这个差别直接决定了这东西最后是被天天用,还是三天后吃灰。
但猫型带来的代价必须提前认清楚。四个腿不能真的着地走路,四足步态的控制难度和成本会瞬间翻十倍,所以做法是"猫的造型、两轮的自平衡式底盘、前面两个前爪做成低摩擦的滑撑"。也就是说,真正接触地面的是两个驱动轮加上前面两个支撑点,构成三点或四点支撑。这里有个很关键的取舍:支撑点如果用轮子,越障能力强但结构复杂;如果用光滑的尼龙滑块,结构极简但过门槛基本别想。我选的是前部两个万向球轮,直径小、藏在爪子造型内部,成本几块钱,能过1厘米以内的地毯压边。
猫的"头"和"尾巴"是这套设计里最有性价比的部分。头部装一个舵机,让激光测距模块能左右扫查,这本身就是避障决策必需的硬件;尾巴装一个小舵机或者干脆用偏心轮做被动摆动,成本不到十块钱,但动态感立刻拉满。你看,造型需求和技术需求在这里刚好对上了,这也是我愿意做猫型的最主要理由——它不是纯粹为了好看而增加的结构负担,大部分造型件都承担了真实功能。
1.2 为什么是Arduino加激光测距,而不是更贵的方案
主控这块我一点都不纠结。这类项目对算力的要求低到离谱,最复杂的运算不过是几次排序和几次浮点乘法,一块最基础的AVR核心开发板绰绰有余。选它的理由只有三个:供电宽容度高、烧录不用折腾、库生态成熟到几乎不会卡住新手。真要上更高级的板子做图像识别、做建图,那是另一个项目,成本翻五倍,学习曲线陡三倍,收益在这一代产品上根本体现不出来。
激光测距这一块是真正需要认真选的。市面上能用的非接触测距方案主要有三类,我把它们的实际表现列出来,你对着自己的预算和场景挑:
| 方案类型 | 典型量程 | 精度表现 | 响应速度 | 成本区间 | 适用判断 |
|---|---|---|---|---|---|
| 超声波 | 2cm–300cm | 厘米级,受材质影响大 | 慢,单次30–60ms | 很低 | 便宜但软性障碍物(窗帘、布沙发)测不准 |
| 红外三角测距 | 10cm–80cm | 近距离尚可,远距离漂 | 快 | 低 | 量程太短,地面反光干扰严重 |
| ToF飞行时间 | 3cm–200cm | 毫米级,线性度好 | 快,可连续输出 | 中等 | 本项目首选,抗环境光较好 |
ToF的原理说白了就是"计时":发一束极短的红外光出去,光打到物体反射回来,模块内部用极高精度的时间数字转换器算出往返时间,光速乘时间除以二就是距离。因为是直接测时间,不像三角测距那样依赖几何角度推算,所以距离越远精度衰减得越慢,线性度也好得多。这一条很重要,因为避障决策需要的是"300毫米"和"320毫米"这种可比较的数值,而不是"大概有点近"。
注意:ToF模块的标称量程是在理想条件下测的。室内白墙能到1.5米以上,但深色绒布沙发、黑色踢脚线、玻璃茶几这几类物体,回波强度低,实测有效距离可能掉到40厘米以内。做避障阈值的时候一定要按"最差反射物"留余量,不能按白墙的数据算。
1.3 目标能力清单与验收标准
做东西最怕没验收标准,做完之后自己都不知道算不算成功。我把这个项目的验收指标写下来,你照着测,达标了就算做完,不达标就针对性优化,别陷入无限调参。
| 能力项 | 验收标准 | 测法 |
|---|---|---|
| 持续运行 | 单次充电连续工作≥90分钟 | 满电开机跑,秒表计时到自动停机 |
| 避障可靠性 | 连续30次正面遇障,成功转向≥28次 | 拿纸箱正面堵,记成功次数 |
| 最小通过高度 | ≤85mm | 卡尺量机身高点 |
| 转向半径 | 原地转向,半径≤40mm | 地面上贴纸,看轨迹直径 |
| 续航充电 | 从保护电压充到满电≤3小时 | 充电器计时 |
| 噪声 | 1米外≤55dB | 手机分贝App粗测 |
| 清洁效果 | 瓷砖地面浮尘清除可见 | 撒面粉画格,跑20分钟看残留 |
这里我要重点讲一下"避障可靠性"为什么定在28/30而不是30/30。任何基于单点测距的避障方案都有必然的盲区——太矮的障碍物(比如一根筷子)、太细的障碍物(比如椅子腿的横撑)、高反光或者全吸光表面,都会有概率漏检。追求100%成功率的唯一办法是加传感器融合,那就是成本翻倍的另一个故事了。所以工程上正确的做法是承认概率,然后通过"降速接近+多次确认"把失败率压到可接受区间。
2. 硬件选型:每一分钱该花在哪儿
硬件清单这块我不给你一个干巴巴的表格就完事,我要说清楚每一项为什么选它、有哪些替代方案、哪些钱可以省、哪些钱绝对不能省。整个项目的成本结构大致是:主控与传感器占40%,电机驱动与电源占35%,结构件与耗材占25%。这个比例意味着,你把主控从基础板换成高级板,对最终体验的提升几乎为零;但你把电机驱动省了十块钱,可能会得到一台只会原地打转的废铁。
2.1 主控板:够用就好,别为不存在的需求埋单
我用的是一块最基础的AVR核心板,尺寸小巧,引脚数刚好够用。这块板子的资源情况是这样的:14个数字IO、6个模拟输入、6路硬件PWM、32KB程序空间、2KB内存。听起来寒酸,但盘一下这个项目的实际开销:两路电机各需要2个方向脚加1个PWM脚,共6个数字脚;舵机1个PWM脚;清洁电机1个开关脚;ToF走I2C占2个脚,还需要1个脚做复位控制;电池电压检测占1个模拟脚。加起来11个引脚,还剩几个余量给未来的升级。内存方面,测距滤波的几个数组加起来不到50字节,状态机变量几十字节,程序空间占用通常在15KB以内,还剩一半。
所以主控这一块的结论很明确:不需要任何升级。唯一需要注意的是板子的供电方式。很多基础板有一个板载线性稳压器,输入7–12V输出5V,看起来很方便,但它的额定输出电流只有几百毫安,而且多余的能量全变成热量。我就吃了这个亏,一开始直接从电池接到板子的电源座,结果舵机一动,主控就复位,折腾了半天才明白是电压跌落。正确做法是从电池直接引到独立的降压模块,再把5V稳稳地送进板子的5V引脚,绕开板载稳压器。
提示:如果板子只有电源座输入,没有独立的5V引脚,那就必须在外部做一个5V降压,然后把5V接到板子的VIN或者5V脚。接VIN的时候要注意板载稳压器的压差,输入5V输出只有4.3V左右,I2C通信可能不稳。
2.2 激光测距模块:量程、采样率、视场角三个参数定生死
选ToF模块,我盯三个参数。第一是量程,室内避障1.2米的有效距离足够,因为你的机器人跑起来速度也就每秒二三十厘米,1.2米对应大约4–5秒的反应时间,非常充裕。第二是采样率,最好是连续测量模式,模块内部自己以固定频率出数据,主控直接读最新值,这样不会因为等待测距而阻塞控制循环。第三是视场角,这个参数最容易被忽略。视场角大的模块测出来的是一个圆锥区域内的最近物体,好处是不容易漏掉细腿障碍,坏处是地面反光会混进来,导致读数突然变小。视场角小的模块指向性强,但需要靠舵机扫查才能覆盖前方区域。
我的方案是单模块加舵机扫查,理由是:单模块成本低,扫查带来的角度信息对转向决策非常有用(能判断左边空还是右边空),而且扫查的过程本身就让"猫头转动"这个动作有了技术意义。代价是每个决策周期需要多花几百毫秒,但对于一个满地乱窜的清洁机器人来说,这个延迟完全可以接受。
模块的安装位置有讲究。我一开始装在机身正前方,离地高度约45毫米,结果发现测地面上的电线、拖鞋边缘时读数剧烈跳动。后来把模块抬高到60毫米并向下倾斜大约10度,读数就稳定多了。倾斜的意义在于让光斑落在前方300–400毫米的地面上,而不是紧贴着机身正下方,这样既能检测到墙面,又不会被机身正下方的地面反光干扰。
| 安装高度 | 倾斜角 | 3mm电线检出率 | 30cm外白墙稳定性 | 结论 |
|---|---|---|---|---|
| 45mm | 0度 | 40% | 好 | 地面反光干扰严重 |
| 45mm | 向下10度 | 55% | 好 | 仍有抖动 |
| 60mm | 向下10度 | 70% | 好 | 采用此方案 |
| 60mm | 向下20度 | 65% | 一般 | 远处地面回波过强 |
2.3 电机、驱动与电源的匹配计算:这部分算错全盘皆输
这是整个项目最容易翻车的地方,我把计算过程完整写一遍,你照着套自己的参数就行。
先定运动需求。清洁机器人需要慢速、稳定、有一定扭矩,不需要快。我定的巡航速度是0.25米/秒,转向时降到0.12米/秒。轮子选的是直径43毫米的减速电机自带轮,周长算一下:3.14 × 43 = 135毫米,也就是每转一圈走13.5厘米。要跑0.25米/秒,需要每秒转1.85圈,即111转/分钟。这个转速在减速电机的输出轴上属于低转速区间,意味着扭矩充足、噪声低,是个好区间。
然后算扭矩。整机重量实测约420克(含电池),两个驱动轮承担大约70%的重量,剩下的由前部球轮承担。滚动阻力系数按瓷砖地面取0.03,需要的牵引力大约0.42 × 9.8 × 0.03 ≈ 0.12牛。轮子半径21.5毫米,需要的扭矩约2.6毫牛米。这个数值非常小,任何常见的微型减速电机都能满足,实际上我用的电机堵转扭矩标称大约15毫牛米,余量五倍以上。所以扭矩不是瓶颈,真正的瓶颈是电流。
再看电流。这类微型减速电机空载电流几十毫安,额定负载下大约150–250毫安,堵转时能冲到1安以上。两个电机同时堵转,瞬间电流2安。加上舵机堵转的0.7安、主控和传感器的0.1安,峰值电流能到2.8安。这个数字直接决定了两件事:驱动模块的额定电流必须大于3安,电源的放电能力必须大于3安。
驱动模块我试过两种。第一种是老式的双H桥模块,便宜、接线直观,但内部用的是达林顿管,压降大,实测输出7.4V进去只剩6V左右,而且发热明显,跑十分钟摸上去烫手。第二种是MOSFET方案的驱动模块,压降只有零点几伏,效率高,还带待机功能。差价的十几块钱,换来的是续航提升和不用装散热片,我觉得非常值。
电源方案我选了2节锂电芯串联,标称7.4V,容量2600毫安时。为什么不用单节3.7V?因为单节电压太低,经过降压给电机只剩3V出头,转速和扭矩都上不去,而且单节大电流放电时压降明显。为什么不用三节?因为三节11.1V需要更复杂的降压,体积也上去了。7.4V是个甜点:直接降压到6V给电机驱动,压差小、效率高;降压到5V给主控,压差也合理。
续航时间算一下。总容量2600毫安时,串联后电压翻倍容量不变,可用能量约19.2瓦时。整机平均功耗实测:待机0.35瓦、巡航0.9瓦、清洁电机开启1.6瓦、转向峰值2.2瓦。按巡航和清洁各占一半时间的混合工况,平均功耗约1.5瓦。理论续航19.2 / 1.5 = 12.8小时,这个数字明显不对,因为我忘了降压效率和电池可用容量。实际可用容量按80%算,降压效率按85%算,有效能量约13瓦时,对应8.7小时。但实测只有2小时出头,差距在哪?差距在电池的持续放电能力和内阻上——小容量电芯在大电流脉冲下电压跌得快,保护板提前动作。这也是为什么我建议容量往大了选,2600只是及格线,3500以上体验会好很多。
注意:锂电池必须有保护板,必须有保险丝。我在电池正极串了一个3安的自恢复保险丝,成本一块钱,但它在一次电机堵转测试里救了我一整组电芯。这个钱别省。
2.4 清洁执行单元与猫型外观件
清洁部分我做了两个版本,说一下取舍。版本一是纯被动拖擦:机身底部用魔术贴固定一块超细纤维布,靠机身重量压在地面上摩擦。优点是零功耗、零噪声、结构简单到几乎不会坏;缺点是对浮尘效果好,对颗粒物和毛发基本无效,而且布脏了之后会二次污染地面。版本二是被动拖擦加一个小轴流风扇做负压:风扇从底部吸口抽气,把灰尘带进一个小尘盒。优点是能吸起毛发和细沙,缺点是增加约0.4瓦功耗、噪声明显、尘盒很小需要频繁清理。
我最后选的是版本一加局部改良:抹布前缘加一条静电除尘纸,用夹子固定,脏了直接撕掉换新的。这个方案的好处是把"维护"这件事的成本降到最低——用户不需要拆尘盒、不需要洗滤网,只需要撕纸。对于一个DIY项目来说,后期维护成本比初始性能重要得多,因为没人愿意天天伺候一个小玩具。
猫型外观件全部用3D打印。材料选PLA,理由是打印容易、翘边少、表面质感好,缺点是耐温差、脆。机身内部温度不高,PLA完全够用。如果要做跌落测试或者家里有暖气管,建议换PETG。打印参数我用的是:层高0.2毫米、壁厚2.4毫米(三层)、填充20%的网格、顶部和底部各四层实心。这套参数下的零件强度足够,一个成年人踩上去不会塌(别问我怎么知道的)。
| 零件 | 建议壁厚 | 填充率 | 打印时长 | 备注 |
|---|---|---|---|---|
| 底盘 | 2.4mm | 25% | 约3.5h | 电机座位置加实心加强筋 |
| 上壳(猫身) | 1.6mm | 15% | 约4h | 薄壁减轻重量 |
| 猫头 | 1.6mm | 20% | 约1.5h | 内部留舵机安装槽 |
| 尾巴 | 1.2mm | 10% | 约0.5h | 轻量化,减小舵机负载 |
| 前爪滑撑 | 2.0mm | 40% | 约0.8h | 受力件,加密 |
3. 结构设计与装配:猫的骨架到底怎么搭
很多人做这类项目,电路搞得很漂亮,结构一塌糊涂,最后装起来发现电池没地方放、线束卡住轮子、重心偏前导致前爪一直打滑。结构设计必须和电路同步做,不能等电路调通了再想怎么装。我这一版结构改了三轮才稳定,下面写的是第三轮的定稿方案,前两轮的教训我也会点出来。
3.1 底盘布局与重心控制:一个厘米决定成败
底盘整体尺寸定为长150毫米、宽120毫米、高(不含猫头)55毫米。这个尺寸是怎么来的?宽度受限于轮距需求,轮距太窄转向容易侧翻,太宽进不去窄缝。120毫米轮距加上轮子本身的宽度,整机宽度约145毫米,比常见家具底部缝隙(一般150毫米以上)窄一点点,勉强能钻。长度方向上,150毫米要容纳电池、驱动板、主控板和清洁抹布,非常紧张,所以电池必须偏后放置。
重心控制是这个项目里最容易被忽略的隐形杀手。我第一版把电池放在正中间,静态看很平衡,但一加速就出问题:两个驱动轮在后方,电池在中间,重心落在驱动轮前方约30毫米处,急停时整机前倾,前爪承压增大,摩擦力上升,转向变得迟钝。第二版把电池挪到最后端,重心落到驱动轮正上方,问题变成了轻微后仰,直行时前爪几乎不吃力,导致机身左右摇摆。
最终的解法是把电池仓做成可前后滑动调节的样式,底板上开两条长槽,电池用扎带固定,装配完成后在实际地面上跑一圈,微调前后位置,找到"前爪有轻微下压但不打滑"的那个点,再锁死。实测最佳位置大致在驱动轴心后方5毫米处。这个技巧非常实用,因为不同人用的电池重量差异很大,靠计算不如靠实测。
前部支撑我用了两个直径12毫米的万向球轮,安装在猫前爪造型内部。选择球轮而不是普通万向轮的考虑是:球轮高度低(10毫米以内),能塞进爪子造型;转向阻力小,不会和差速转向打架;缺点是越障能力弱,遇到5毫米以上的门槛会卡住。如果你家门槛多,建议换成大一点的方向轮,代价是机身高度增加。
提示:前部支撑的安装高度必须可调,哪怕只留2毫米的垫片调整空间。因为打印件的公差、轮子的磨损、地面的不平整,都会让四点支撑变成三点支撑,导致机身晃动和行走跑偏。
3.2 猫型外壳的建模与打印要点
外壳建模我用的是最普通的参数化建模软件,先做底盘实体,再做外壳,最后布尔运算挖出内部空间。这里有个顺序上的讲究:先别急着画猫的可爱造型,先把所有内部零件的包围盒画出来,做一次干涉检查,确认没有冲突之后再加造型。我第一版就是先画了个漂亮的猫,结果发现电池塞不进去,返工重做了两天。
猫身的造型关键是"低矮圆润"。设计上有三个约束:整体高度不超过85毫米(为了钻家具)、前脸要留出激光测距的开窗、背部要有一个方便掀开的检修盖。前脸开窗的位置和角度必须严格对准测距模块的光轴,开窗直径至少8毫米,太小会遮挡视场角,太大又容易进灰。我最后开了10毫米的圆孔,内侧贴了一圈薄海绵做防尘,效果不错。
打印方向非常重要。底盘如果平躺打印,电机座的悬空部分需要大量支撑,拆支撑的时候很容易崩掉细节。我把底盘侧过来打印,让电机座的受力方向沿着层纹的垂直方向,强度明显提升。猫头是个近球形的零件,必须加支撑,支撑角度设50度以上,接触面留0.2毫米的间隙,拆起来轻松。
后处理别偷懒。打印件表面有层纹,直接喷漆很难看。我的流程是:400目砂纸粗磨、800目细磨、补土、再磨、喷底漆、喷色漆、喷哑光保护漆。一套下来一个零件大约两小时,但成品质感和没处理过的完全是两个东西。如果你嫌麻烦,至少喷一层哑光白,能把层纹弱化一大半。
3.3 传感器开窗与走线:看不见的地方最容易出事
线束管理是这个项目里最不性感但最要命的部分。我踩过的最大的坑是:一根杜邦线在转向时被轮子卷进去,直接把线扯断,主控瞬间失电,整机撞墙。从那以后我定了几条规矩。
第一,所有线束必须分区走线。电源线走底盘左侧,信号线走右侧,中间用打印件的筋位隔开,减少干扰。第二,所有接插件必须有机械固定,要么用热熔胶点一下,要么用扎带绑在固定柱上,光靠插拔力是不够的。第三,活动部件附近的线必须留出足够的松弛量,松弛量的标准是:机构运动到极限位置时,线束仍能保持一个自然的弧度,不能绷直、不能折叠。
ToF模块的连线要特别注意。I2C是两根信号线,走线长度超过20厘米之后,通信稳定性会下降,尤其是在电机PWM工作时。我的做法是把模块的线束缩短到12厘米以内,并且在SDA和SCL上各加一个4.7千欧的上拉电阻(有些模块自带,需要确认)。如果通信还是不稳,把电机PWM频率调高,避开I2C的敏感频段,或者干脆在软件上让测距和电机换向错开时间。
4. 电路连接与供电:把每一根线接对是第一步
电路这块我不打算画一堆原理图,那对新手不友好。我直接给你接线表和电源树,你对着插就行,插完再逐个测试。记住一个原则:先接电源,测电压;再接电机,测转向;最后接传感器,测数据。别一次性全接上再通电,出了问题你会不知道该查哪一根。
4.1 完整的引脚分配与接线表
| 功能 | 主控引脚 | 接到哪里 | 备注 |
|---|---|---|---|
| 左电机PWM | D5 | 驱动模块左通道PWM | 硬件PWM,频率可调 |
| 左电机方向A | D8 | 驱动模块左IN1 | 数字输出 |
| 左电机方向B | D9 | 驱动模块左IN2 | 数字输出 |
| 右电机PWM | D6 | 驱动模块右通道PWM | 硬件PWM |
| 右电机方向A | D10 | 驱动模块右IN3 | 数字输出 |
| 右电机方向B | D11 | 驱动模块右IN4 | 数字输出 |
| 头部舵机 | D3 | 舵机信号线 | 硬件PWM,50Hz |
| 清洁电机开关 | D4 | MOS管栅极 | 低电平关断 |
| ToF SDA | A4 | 模块SDA | 需上拉 |
| ToF SCL | A5 | 模块SCL | 需上拉 |
| ToF复位 | D7 | 模块XSHUT | 上电复位用 |
| 电池检测 | A0 | 分压中点 | 10k/4.7k分压 |
| 尾部舵机 | D12 | 舵机信号线 | 可选,普通IO模拟 |
电池电压检测的分压电阻我选10千欧和4.7千欧,分压比是4.7 / (10 + 4.7) = 0.32。满电8.4V时中点电压2.69V,保护电压6.4V时中点电压2.05V,都在模拟输入的安全范围内(0–5V),而且落在分辨率较好的区段。如果想让读数更精确,可以在中点加一个0.1微法的电容做低通滤波,抑制电机换向时的尖峰干扰。
舵机的供电必须单独说。标准微型舵机的堵转电流能到700毫安以上,瞬间还会更高。如果直接从主控板的5V取电,电压瞬间跌落,主控会复位。我的接法是:舵机电源正极直接接到5V降压模块的输出端,地线共地,信号线接主控。这样舵机的电流不经过主控板的覆铜,避免干扰。
4.2 共地与电平匹配:新手最容易忽略的两个坑
第一个坑是共地。整机有多个电源域:电池7.4V、6V电机、5V逻辑。如果电机驱动的地和主控的地没有可靠连接,PWM信号就没有参考电平,电机会乱转或者根本不转。我的做法是在底盘上设一个"星形接地点",所有模块的地线都接到同一个螺丝柱上,用压接端子固定,而不是靠排线一根根串过去。这样电流回路清晰,干扰小。
第二个坑是电平匹配。ToF模块有的是3.3V逻辑,有的是5V兼容。如果模块是3.3V逻辑而主控是5V输出,长期使用会损伤模块的I2C引脚。确认方法是查模块的规格说明,或者在SDA和SCL线上串一个电平转换模块。我用的模块标称是宽电压兼容,但为了保险,还是在两条线上各串了100欧姆的电阻做限流,实测通信正常,这个电阻在信号频率不高的时候影响很小。
注意:电机是整机最大的干扰源。在电机的两个接线端各焊一个0.1微法的瓷片电容到外壳,能明显降低换向时的高频噪声。这个操作成本两毛钱,但对测距数据的稳定性提升非常明显。
4.3 电源树与保护设计
电源从电池正极出来,先经过3安自恢复保险丝,再经过总开关,然后分成三路。
第一路:直接到电机驱动的电源输入端,同时在输入端并一个470微法以上的电解电容,吸收电机启动和换向的电流冲击。这个电容非常关键,没有它,电机一启动电压就塌,主控直接重启。
第二路:经过一个可调降压模块,输出稳定的5V,最大输出电流不低于2安。给主控、ToF模块、舵机、尾部电机供电。这个模块我建议买带散热的,因为线性降压在这个压差下发热不小,尤其是舵机动作频繁的时候。
第三路:经过一个固定6V降压模块,给电机驱动供电。为什么要单独一路?因为电机电流大且波动剧烈,和逻辑电路共用一路会互相干扰。分开之后,逻辑电路的电压非常干净,测距读数也稳了不少。
电池保护方面,我串了一块带均衡的保护板,过放保护阈值设在6.0V,过流保护设在5安。软件层面也在A0上做电压监测,低于6.6V就自动降速,低于6.4V就强制停机。这两道保护缺一不可——硬件保护是最后一道防线,软件保护是为了避免"跑到一半突然断电导致机器卡在床底下"这种尴尬。
5. 控制程序:让这只猫自己判断该往哪走
代码这块我不追求优雅,追求的是"你复制过去改改引脚就能跑"。整个程序是一个状态机,主循环里根据当前状态做对应的事情,不写任何阻塞式的延时(除了舵机转动后的稳定等待)。这一点很重要,因为一旦用了长延时,测距和电机控制就会失去实时性,机器人的反应会变得迟钝。
5.1 程序整体框架与状态机设计
状态机的五个状态:巡游、扫查、转向、后退脱离、休眠。它们的跳转关系是:巡游中检测到前方距离小于减速阈值,进扫查;扫查完成比较左右距离,选方向进转向;转向完成后如果前方还是太近,进后退脱离;后退脱离完成后回到巡游;电压低于阈值或者累计运行超时,进休眠。
enum RobotState : uint8_t { ST_CRUISE, // 巡游 ST_SCAN, // 头部扫查 ST_TURN, // 转向 ST_BACKOFF, // 后退脱离 ST_SLEEP // 休眠 }; RobotState state = ST_CRUISE; const int DIST_SLOW = 300; // 毫米,低于此值减速 const int DIST_STOP = 150; // 毫米,低于此值必须转向 const int DIST_BACK = 90; // 毫米,低于此值后退阈值这三个数字不是拍脑袋定的。测量精度按最差情况±15毫米算,机械响应延迟约200毫秒,巡航速度0.25米/秒对应200毫秒走50毫米。所以从"检测到150毫米"到"真的停下来",实际已经吃掉了50毫米余量,剩下的100毫米是安全距离。如果你把巡航速度提到0.4米/秒,那DIST_STOP就得提到200毫米以上,否则一定撞。这个换算关系你得自己会算。
5.2 测距数据的滤波与有效值提取
ToF的原始读数会有毛刺,尤其是遇到深色物体或者地面反光的时候,偶尔会蹦出一个几十毫米的异常值。如果不做滤波,机器人会莫名其妙地对空气转向。我用了"中值滤波加一阶低通"的组合,效果很好。
uint16_t readRawMM() { // 模块配置为连续测量模式,这里直接取最新一次结果 // 具体读取函数取决于你用的驱动库,返回值为毫米 return tof.readRangeContinuousMillimeters(); } float readFilteredMM() { static float ema = 400.0f; uint16_t buf[5]; for (uint8_t i = 0; i < 5; i++) { buf[i] = readRawMM(); delay(8); // 给模块留出更新周期 } // 五点中值:简单插入排序后取中间值 for (uint8_t i = 1; i < 5; i++) { uint16_t key = buf[i]; int8_t j = i - 1; while (j >= 0 && buf[j] > key) { buf[j + 1] = buf[j]; j--; } buf[j + 1] = key; } float median = (float)buf[2]; // 一阶低通,alpha 越小越平滑但越迟钝 const float alpha = 0.35f; ema = ema * (1.0f - alpha) + median * alpha; // 异常值保护:突变超过400mm认为是毛刺,直接丢弃 if (fabs(median - ema) > 400.0f) { return ema; } return ema; }中值滤波的作用是干掉孤立的异常点,五点中值可以容忍两个异常值。低通的作用是平滑抖动,alpha取0.35是一个折中:太小了反应迟钝,撞墙前才反应过来;太大了等于没滤波。这个参数和你的测量周期有关,8毫秒乘5等于40毫秒一个滤波周期,对应0.25米/秒的速度是10毫米的位移,够用。
提示:如果模块支持,把测距模式设成"长距离模式",采样时间会从20毫秒涨到33毫秒左右,但量程和抗环境光能力都会提升。对于室内避障这个场景,长距离模式更合适。
5.3 避障决策与转向策略
决策逻辑的核心是"往哪边转"。最土的办法是固定右转,遇到障碍就右转,简单但容易在角落里死循环。稍微好一点的方案是比较左右距离,往空的一边转。我采用的是这个方案,具体做法是:头部舵机依次指向左偏40度、正前、右偏40度,各停留150毫秒读一次滤波后的距离,然后比较三个值。
struct ScanResult { float left; float front; float right; }; ScanResult doScan() { ScanResult r; r.left = lookAt(-40); r.front = lookAt(0); r.right = lookAt(40); return r; } float lookAt(int angleDeg) { servo.write(90 + angleDeg); // 90度为中位 delay(150); // 等舵机到位并稳定 return readFilteredMM(); }转向策略:如果左边距离大于右边距离且大于DIST_STOP,左转;否则右转。如果两边都小于DIST_STOP,说明进了死角,直接后退。
void decideTurn(const ScanResult& s) { if (s.left > s.right && s.left > DIST_STOP) { turnLeft(350); // 350毫秒的左转 } else if (s.right > DIST_STOP) { turnRight(350); } else { state = ST_BACKOFF; } }转向时间的350毫秒是怎么定的?原地转向时,两个轮子反向等速转动,转向角速度等于(右轮速度-左轮速度)/轮距。速度取0.12米/秒,轮距0.12米,角速度约2弧度/秒,350毫秒转过0.7弧度,约40度。要转90度需要约780毫秒。我实际只需要转开一个角度继续走,40度足够,转太多反而浪费时间。
5.4 头部扫查与猫的行为表现联动
这是我觉得这个项目最有意思的部分。同样是舵机转动,如果只是机械地扫三下,看起来像个仪器;如果加一点节奏变化,看起来就像猫在"嗅探"。
我的实现是:扫查时不是匀速转过去,而是"转到位—停顿—轻微回摆—再转",配合尾巴的一个小抖动。代码上就是在扫查完成之后插入一段随机化的动作序列,时长80到200毫秒之间随机。这个改动对功能没有任何影响,但整个机器人的"活物感"提升非常明显。家里人看到的第一反应从"这什么"变成了"它在闻什么"。
void catSniff() { servo.write(50); delay(120); servo.write(62); delay(90); servo.write(48); delay(140); servo.write(90); }尾巴的驱动更简单,用一个慢速的往复动作就行。如果没有额外的舵机,可以用一个偏心轮加弹簧,靠电机自身的转动带动尾巴摆动,零成本。
5.5 清洁电机的软启动与堵转保护
清洁电机虽然功率不大,但直接硬启动会有两个问题:启动电流冲击导致电压跌落,主控可能复位;启动瞬间机身会有一个反扭矩,导致跑偏。软启动就是让PWM占空比在300毫秒内从0线性升到目标值。
void softStartClean(uint8_t target, uint16_t ms) { uint16_t step = ms / target; for (uint8_t i = 0; i <= target; i++) { analogWrite(PIN_CLEAN, i); delay(step); } }堵转保护靠两件事:一是软件计时,如果清洁电机连续工作超过设定时长(比如25分钟),强制停两分钟散热;二是间接判断,如果电池电压在清洁电机开启的瞬间跌落超过0.8V,说明负载异常,降占空比到70%。这两个逻辑很粗糙,但足够应付实际使用,比装一个电流传感器省钱多了。
6. 联调与实测:从能跑到跑得稳中间隔着十次返工
装机完成只是开始,真正的功夫在调试。我的建议是严格分阶段,每个阶段都有明确的通过标准,不达标不进入下一阶段。这样出问题时排查范围小,不会一锅粥。
6.1 分阶段调试法
第一阶段:电源验证。只接电池和降压模块,不接任何负载。用万用表量每一路的输出电压,5V那一路要在4.9–5.1之间,6V那一路在5.8–6.2之间。然后接上主控,观察指示灯是否正常,串口能否输出。这一阶段的通过标准是连续通电30分钟,电压漂移不超过0.1V。
第二阶段:电机验证。只接驱动和电机,写一个最简单的测试程序,让左轮正转2秒、反转2秒、右轮同样。观察转向是否正确,速度是否一致。如果发现一个轮子转得慢,先检查PWM值是否相同,再检查轮子是否摩擦外壳,最后才怀疑电机本身。通过标准是两个轮子空载转速差异小于5%。
第三阶段:传感器验证。接上ToF,用串口不停打印距离值。拿一本书在30厘米处缓慢移动,观察读数是否平滑跟随。如果读数乱跳,先排查供电和地线,再排查I2C上拉,最后才怀疑模块。通过标准是在30–120厘米范围内,读数误差小于20毫米。
第四阶段:整机联调。全部接上,先让机器人悬空运行(轮子不接触地面),观察状态机跳转是否正常,舵机动作是否干扰测距。然后放到地面上低速跑,最后才提到全速。这一阶段最常见的现象是"空转正常,落地就乱",绝大多数是电源功率不足或者重心不对,按前面的方法逐项排查。
6.2 参数整定记录
我把最终稳定的参数列在这儿,你可以作为起点,然后根据自己机器的重量和地面材质微调。
| 参数 | 数值 | 调整方向说明 |
|---|---|---|
| 巡航PWM | 120(约47%) | 地面滑就调低,地毯就调高 |
| 转向PWM | 90 | 太高会打滑,太低转不动 |
| 减速阈值 | 300mm | 速度快就加大 |
| 停止阈值 | 150mm | 不要低于120mm |
| 后退阈值 | 90mm | 低于此值直接后退 |
| 滤波alpha | 0.35 | 抖动大就调小 |
| 扫查停留 | 150ms | 舵机慢就加长 |
| 转向时长 | 350ms | 按目标角度换算 |
| 低电压降速 | 6.6V | 保护电芯 |
| 低电压停机 | 6.4V | 硬保护 |
调参的顺序建议是:先调直行,让机器人能跑直线(两侧PWM做微调补偿),再调转向角度,最后调避障阈值。顺序反了会反复推翻之前的结论。
6.3 实测续航与覆盖效率
在约14平方米的客厅(瓷砖地面,有几件家具腿)实测,满电跑到低电压保护停机,用时1小时52分钟。这个结果比理论估算低很多,原因在上一节说过,主要是电芯容量偏小和脉冲放电的压降。换成3500毫安时的电芯后,同样工况跑到2小时40分钟左右。
覆盖效率这块我做了个粗略测试:在地面上撒一薄层面粉,画成一平方米的格子,让机器人跑20分钟,看每格里残留的面粉情况。结果是靠近墙边和家具腿周围的格子清除率最高(因为机器人会反复在那里转向摩擦),房间中央的格子清除率反而一般。这个结果说明随机游走策略天然偏向边缘,如果想提升中央区域的覆盖率,可以在程序里加一个"定时强制直行"的逻辑,避免它老在墙角打转。
噪声实测:安静环境下距离1米,待机32分贝,巡航41分贝,清洁电机开启48分贝,转向瞬间峰值52分贝。这个水平放在客厅里基本无感,晚上开着也不会吵到人。
7. 常见问题速查与踩坑记录
这一节是全篇最值钱的部分,因为下面每一条都是我真金白银试出来的,你看一眼能省下几个小时。
7.1 电气类问题
问题:主控频繁复位,尤其在电机启动或者舵机动作时。
排查顺序是:先量5V电压在动作瞬间跌到多少,如果低于4.3V,基本就是电源功率不足。接着检查电池到降压模块的线径,很多人用细杜邦线走大电流,线损能吃掉0.5V。再检查电机驱动输入端有没有并大电容。最后检查是不是从主控板取电给舵机了。这四条占这类故障的90%。
问题:测距读数偶尔跳到很大或者很小。
先确认是哪种跳法。跳到很大(比如8000多,这是超量程的典型值)通常是目标反射太弱或者超出了量程;跳到很小通常是地面反光或者镜头脏了。处理办法:擦镜头、调整安装角度、开启长距离模式、在软件里做过量程判断。如果只是偶尔一两次,中值滤波能吃掉,不用管。
问题:I2C通信时好时坏,电机一转就丢数据。
这是典型的干扰问题。三步解决:缩短信号线、加上拉电阻、在电机端加消噪电容。如果还不行,把I2C时钟频率降到100kHz试试,很多时候能立刻稳住。
7.2 机械类问题
问题:跑起来往一边偏,需要不停修正。
先检查两个轮子的直径是否真的一样(打印件和橡胶圈的公差能差1毫米)。再检查两个电机的空载转速。最后检查重心是否偏在一侧。解决办法是在软件里给偏慢的那一侧加一点PWM补偿,这个补偿值通常是5到15,具体多少靠试。
问题:转向时抖动,像卡住一样。
大概率是前部支撑的摩擦力太大,或者万向球轮卡了毛发。把前部抬起来一点点(垫0.5毫米),让重心更多落在驱动轮上,转向会顺畅很多。另一个可能是转向PWM给太高,轮子打滑,降低到能转动的最小值反而更稳。
问题:外壳螺丝孔打滑或者开裂。
打印件的螺纹强度不够,反复拆装必然滑丝。解决办法是用热熔螺母,或者干脆把螺丝孔设计成穿通孔,用长螺丝加螺母固定。这个改动要在建模阶段就做,后期改很麻烦。
7.3 软件类问题
问题:机器人在墙角反复转向,出不来。
这是决策逻辑的死循环。原因是左右两侧距离都小于停止阈值,程序一直判定"该转向",但转向后又立刻检测到障碍,又转向。解决办法是加一个"连续转向计数",连续转三次还没脱离就强制后退一段距离再重新决策。
问题:明明前面很空,却突然转向。
看一下是不是扫查时舵机还没到位就开始读数。舵机从90度转到50度需要时间,如果只等50毫秒,模块测的还是旧方向的距离。把等待时间加到150毫秒以上,或者用一个"到位确认"的逻辑。
问题:跑一会儿就自动停机,但电池还有电。
检查软件里的超时停机逻辑和低电压阈值。很多电芯在负载下电压会瞬间跌到阈值以下,负载一撤又升回来。解决办法是电压采样加一个延时确认,连续200毫秒低于阈值才判定为真低电。
7.4 一张表把常遇到的坑串起来
| 现象 | 最可能原因 | 快速验证方法 | 处理办法 |
|---|---|---|---|
| 主控反复重启 | 电源功率不足 | 动作瞬间量5V电压 | 换大功率降压、加电容 |
| 测距乱跳 | 镜头脏/安装角不对/干扰 | 擦镜头后静止测30次看方差 | 调角度、加滤波、加消噪电容 |
| 直行跑偏 | 轮径差/电机差/重心偏 | 空载测两轮转速 | 软件PWM补偿 |
| 转向卡顿 | 前支撑摩擦大 | 抬起前部再试 | 垫高0.5mm或换球轮 |
| 墙角死循环 | 决策逻辑无退出机制 | 观察串口状态跳转 | 加连续转向计数+强制后退 |
| 续航远低于预期 | 电芯容量小/内阻大 | 带载测电压跌落 | 换大容量电芯 |
| 舵机动作导致复位 | 舵机从主控取电 | 单独给舵机供电测试 | 舵机电源直连降压模块 |
| 清洁效果差 | 抹布压不实/材质不对 | 手动压着拖对比 | 调整机身配重、换静电纸 |
| 打印件开裂 | 层纹方向受力/填充不足 | 观察裂纹走向 | 改变打印方向、加壁厚 |
| 离家后卡死 | 没有脱困逻辑 | 人为制造死角 | 加随机后退+转向 |
注意:排查问题时永远遵循"一次只改一个变量"的原则。我见过太多人同时改代码、换电机、调电源,最后问题解决了但根本不知道是哪个改动起的作用,下次遇到同样问题还是不会。
8. 还能怎么玩:几个低成本的升级方向
这台东西做完之后,你会发现它的骨架其实是个通用移动平台,猫只是外壳。下面这几个方向我都试过或者评估过,成本都不高,适合作为下一步的练手。
8.1 加装悬崖检测,让它敢上桌子
现在的避障只测前方,机器人放在桌面上会直接开下去。加两个朝下的红外反射式传感器,装在底盘前缘两侧,检测到下方没有反射(悬空)就立即后退。成本十块钱以内,逻辑就是在主循环里加一个判断,优先级高于一切其他状态。要注意的是深色桌面反射弱,容易误判,所以阈值要现场标定,最好用可调电位器的那种模块。
8.2 做一个简易停靠点,解决"跑完不知道停哪"的问题
不做复杂的自动回充,只做一个"停靠区":在地面固定位置放一块反光贴纸,机器人电量低时进入"找家"模式,一边慢速巡游一边用测距模块找特定距离的反射特征。这个方案的可靠性一般,但胜在简单,配合"低电后原地画小圈"的策略,大概率能停在附近。真要做得准,还是得上红外信标或者视觉标签,那是另一个量级的工作。
8.3 把行为表现层做厚,让它更像"活物"
这是我最推荐的方向,因为成本几乎为零,全是软件。具体做法是给状态机加一层"情绪"变量:连续顺利巡航一段时间,动作变得活泼(速度快一点、尾巴摆得勤);连续遇到障碍,动作变得谨慎(速度降下来、扫查更频繁);长时间没检测到障碍,进入"巡视"模式(走直线更久)。这些逻辑加起来不到五十行代码,但观察体验完全不同。我做这个改动之后,家里人开始给它起名字了,这就是行为设计的价值。
最后一个实际体会:这类项目的成败,八成在电源和结构,两成在代码。我见过太多人一头扎进算法里,想着怎么优化路径规划,结果机器跑十分钟就没电、转向就打滑。把电供稳、把重心调对、把线束绑好,这台东西就已经成功了一大半,剩下的都是锦上添花。真正开始动手之前,先花半小时把电源的电流账算清楚,比后面debug三天都管用。