给你的垃圾桶装上 UWB 超宽带无线电
Home Assistant 已经知道哪些垃圾桶(给国际读者解释一下:trash cans 就是垃圾桶)该被收集了。它能告诉我今天是倒垃圾的日子,在仪表盘上标上对应的颜色,还会给我发提醒。但它不知道我是否真的照做了。
我敢打赌,大多数人只是会在手机上设个定期提醒。我老婆肯定会这么说,但我毕竟不是大多数人。有些人就像正常人那样,记得把垃圾桶放出来就行。我非常清楚,我把这件事搞得过分局大了。我也确实离“正常”远得很,所以觉得弄六个带无线电的垃圾桶是消磨晚上的好办法。:)
几年前我有个想法:在轮式垃圾桶上贴便宜的蓝牙标签,用一个户外蓝牙代理来检测它们是否在附近。那体验很糟心。电池掉电很快,距离读数乱漂,而且那个特定安装环境里的代理也不靠谱。我本来只想知道垃圾桶有没有被移动,结果又多了一样要捣鼓的玩意儿。
是 AirTag 让我对超宽带无线电(UWB)产生了兴趣。进一步了解其工作原理后,垃圾桶的想法又冒了出来。过去几周,它变成了 BinRange:一个固定的无线电锚点、六个小型电池标签,以及一套 Home Assistant 配置,它把垃圾桶被探测到的位置和该收集的时间结合起来。项目还包括打印外壳、定制固件和无线更新。这也算是给了“你倒垃圾了吗?”一个相当硬核的回答。:D
整个构建过程中,我重度依赖了 Codex。Astra 在中途可用时,我特别好奇它的表现。我喜欢和 AI 合作的地方在于,我能快速地把一个想法抛出去,对结果做出反应,给出反馈,然后马上又有新东西可以试。这方面的内容在其他文章里还有很多可写的。现在,还是回到垃圾桶的话题。

喏,这就是那几个垃圾桶。挺美吧?
六个垃圾桶,好几套日程
我家有一个普通垃圾箱、一个厨余堆肥箱、一个花园垃圾箱、两个回收箱,还有一个玻璃瓶箱——一个房子六个箱子,连收运时间都不完全一样。幸好 Home Assistant 的 Waste Collection Schedule 集成能抓取市政的收运时间表并自动提醒哪个箱子该收了,不然光是理清这些就是一项大工程。
用便宜的蓝牙标签测距,通常靠 RSSI(接收信号强度指示):测量收到的信号有多强,再据此猜测标签大概多远。问题在于,信号强度受距离之外的太多因素影响——墙壁、停着的车、反射、天线朝向,以及不同 radio 和天线的差异,都会造成干扰。在某些硬件上,连电池电压都可能影响发射功率,具体取决于设备的设计。信号变弱,可能是垃圾箱真的更远了,也可能是中间多了什么遮挡。这种测距方式太不精确,也解释了我最初那套方案为什么用起来那么糟心。
UWB 让我感兴趣的地方在于,它直接测量无线电信号在设备之间传播所需的时间,从而得到真实的距离,而不必靠信号强度去猜。当然,信号还是得穿过周围环境,换个 radio 并不能让路边停的车消失。
我还一度跑偏,琢磨起一个学术问题:要在这个感知能力上增加更多维度,需要多少个 anchor?如果除了距离还想知道方向呢?或者想知道垃圾箱的精确位置——在一个 2D 平面上三角定位需要几个固定参考点?对这件小事来说纯属浪费,但胡思乱想嘛,就是这样。
对于垃圾桶,我只需要判断它们是否从常规存放区域移动了足够远的距离,从而算作“在外”。一开始,只需一个锚点就能测试这一功能。我设定了十米边界:有最新读数时,边界内为“在家”,边界外为“在外”。这个设定取决于我的具体安装环境,但它让我只需一块通电板卡即可开始测试,而不必把整个花园改造成定位测试场。
两块板卡与一次户外步行
第一次实验使用了两块 Makerfabs ESP32-WROVER/DW3000 板卡,均通过 USB 供电。在利用 Home Assistant 做高级操作之前,我想先看看它们能否测量距离。本地网页视图允许我将两块板卡分开,并观察发生的情况,无需拉一根 USB 线连接两者。

其中一块开发板。这是实验中使用的 DW3000 型号。 硬件参考。
天线方向对结果影响 surprisingly 大。在约十米距离处,在该测试中,将板卡从平放改为竖立,观测成功率从 37% 提升至 100%。采用更慢的无线电设置和更长的前导码(preamble),在初始快速设置效果不佳的场景下也能正常工作。这些都是在基于首个结果设计系统之前值得做的有用发现。
第一次用卷尺校准时,发现报告距离与实测距离误差不到两厘米,我当时大为震惊。两块小板卡交换无线电信号,其一致性竟能与卷尺精准到这种程度。
在后来的记录测试中,实测间距为 10.1 米时,平均读数约为 10.09 米,标准差为三厘米。对于一个只需识别垃圾桶离开存放区域的项目来说,这是一个令人鼓舞的结果。小于两厘米的误差是我在首次校准时观察到的,而非对每个标签、每种朝向或每个户外位置都保证的精度承诺。
户外实地测试更有启发性。信号在约三十米范围内读数可靠,再往外就变得断断续续。记录到的最远距离为 37.28 米,且中间存在间隙。汽车可能会完全阻断信号路径。在户外竖起一块测试板,以及在垃圾桶边缘下方安装一枚小标签,显然是两种完全不同的测试场景。
双板步行测试。它确立了一个有用的起点,但无法证明已安装垃圾桶的实际覆盖范围。查看测试记录。
锚点设备通过 MQTT 发布读数,Home Assistant 会为每个标签发现独立的设备。我考虑过做一个 ESPHome 组件,但它对我想要的架构来说并没有增加多少价值。BinRange 没有额外的服务器或云服务,即使 Home Assistant 或 MQTT 连接不可用,无线电仍能继续测距。
能装进垃圾桶的小型设备
开发板在验证链路连通性时很有用。但我不希望每个垃圾桶上都挂着一个开发板,还带着电池和临时拼凑的盒子。垃圾桶一侧的硬件必须是一个小巧、自成一体的圆盘。
我探索了定制标签和商用标签,关键问题是能否在上面运行自己的固件,并配合我自建的锚点使用。小型 UWB 标签并不天然兼容其他 UWB 产品。最终我购买的成品标签是 KKM K4Ws,内置 nRF52833 处理器、DW3110 无线电和 LIS3DH 加速度计。它使用可拆卸的 CR2477 纽扣电池(顺便说一句,这种电池尺寸惊人!我之前没见过这么胖的纽扣电池)。
两块 MakerFab 开发板含运费共 123.64 美元。十个样品标签每个 25 美元,加上编程夹具和运费,又花了 330 美元。这是当时实验的实际成本,而非当前购物清单或一套六仓组合成品的价格。我估计最好别去算回本周期。
供应商说这些标签可以刷我自己的固件,还提供了一个治具方便接触到编程接口。比起这个,我更关心他们提供的硬件本身,以及确认编程接口没有被锁死。至于他们原有的软件功能,我完全用不上。电池续航的宣传对我也没有意义——反正由我决定 CPU 什么时候休眠、什么事件把它唤醒。
我对 nRF52 设备非常熟悉,从还在 rlab 的时候就开始用了,有好几年。它们是非常出色的低功耗微处理器,大部分时间都在睡觉。只要调度和中断设计得巧妙,在合适的应用场景下,一颗小电池就能撑好几年。这比他们预装的固件能做什么有意思多了。
我拿一个闲置的 Nesso 设备当作编程探针,配合弹簧顶针治具使用。先从一枚开发标签开始,然后逐一处理封装好的标签:刷固件、写入身份、配对、贴标签、分配到对应的垃圾桶,再装回去。这里涉及好几个身份信息:印刷标签、芯片 ID,以及无线模块使用的地址。
顺带一提:标签出厂时外壳上印有看似唯一的序列号,还有包含该序列号的二维码。它们在供应商的原厂固件和软件里大概有用,但我找不到这些序列号与设备 MAC 地址或类似标识之间的任何关联,所以对我毫无用处。还挺可惜的——如果它们就是无线模块的 MAC 地址之类的信息,那就有用了!


贴在垃圾桶上的标签特写。可以看到它们相当厚实,电池真的很大!不过刚好能塞进桶沿下方,基本能挡住雨水。
厂商将加速度计的 interrupts 输出连接到了 nRF 的一个 GPIO 引脚,这非常实用。这意味着我可以让 nRF 休眠很长时间,在此期间不发送任何无线电信号,并在发生物理事件时唤醒它。待事情平息后,它会恢复为偶尔检查状态,UWB 无线电在两次尝试之间保持休眠。固件默认在静止时为十分钟;我后来通过 Home Assistant 的控制将已安装设备的间隔改为了三十分钟,同时保留了五秒钟的移动报告。
这样可以避免电池因反复报告垃圾桶仍在原处而消耗殆尽。这究竟能带来多长的续航时间,我还需要测量。我正在收集电压和活动信息,但一段简短且相当平坦的电压曲线并不能给我一个以月或年为单位、具有说服力的估算。我绝对不想用掉电池寿命的大部分去报告它自身的电压。那看起来价值很低 :)
不拆解即更新六个垃圾桶
一旦标签安装好,每次更改软件时都将其打开并重新装回编程工装夹具就变得相当令人厌恶。锚点现在可以通过蓝牙向配对的标签提供签名的固件更新。旧的版本会被保留,以便在更新失败时回滚,并且系统仅在标签运行了确切的预期映像并确认自身健康状态后才报告成功。
对此类事项进行测试和迭代是 AI 的另一个绝佳用例。Codex 帮助我生成并模拟了各种测试条件和故障模式,然后逐一解决这些问题,以确保通过空中接口向这些设备部署固件尽可能安全。我测试了损坏的映像、中断的传输、选定的断电点以及看门狗恢复。我还测试了锚点不在场的情况:标签需要在没有无线电响应的情况下确认自身健康,而不是因为联系不上锚点就放弃可用的固件。
从单标签扩展到多标签时,遇到了一类不同的问题。蓝牙库中的记录是按并发连接数而非配对设备数来分配大小的。这意味着即使新标签的密钥已存储,添加它仍可能失败。修复此问题后,新的物理标签即可配对,且无需清除现有连接。六个垃圾桶让软件获得了比只有一个顺从的演示标签时好得多的探测效果。
此后,我通过无线方式更新了已安装的锚点和全部六个标签,既未重新配对,也未使用测试夹具。对此我感到很高兴。尽管早期的部分连接尝试需要重试,但在宣称整个过程可以可靠地无人值守之前,我仍希望获得更多证据。编程探针依然是补救手段。
外壳做了几次才成功
锚点需要一个外壳,这变成了一个独立的小项目。最初是一个带螺丝的 OpenSCAD 设计。首次适配测试很有希望,但小螺母螺栓、PLA 的公差以及仍可能晃动的电路板让组装过程比我预期的更烦人。
我想要一个完全由打印 PLA 制成、无螺丝的外壳:用锥形定位销固定电路板,用打印弹簧将其压紧,以及一个能扣紧的盖子。销和弹簧起作用了。但盖子可以在各处移动。
内部凸缘改善了贴合度。更深的卡扣增强了固定性,但我仍然能太轻松地撕开盖子。将卡扣做到与壁同深似乎是明显的下一步,直到两个根部在插入时断裂。那个版本非常擅长变成两块破碎的塑料。
更好的改动是使底部和盖子壁重叠,并使用几个浅卡扣。对定位销和通风口稍作调整之后,我得到了一个能正确固定电路板并保持闭合的外壳。我对此非常满意,这大概说明到那时我已经扯掉了多少个盖子。


无螺丝外壳,由打印网格渲染而成。颜色和表面处理仅供参考。你可以在 MakerWorld 上打印;设计文件以及那些不太成功的尝试的历史记录都在 GitHub 上。
用 Codex 做外壳的那次快速迭代尤其令人愉快:改设计、检查、打印,一气呵成。不过我还是得去取打印件,看看盖子到底掉不掉。几何检查只能确认两个形状理论上能拼在一起,却不能告诉我卡扣按进去之后会不会断。
锚点现在已装到墙上,打印外壳放在一个更大的户外接线盒里,连同电源组件。为此又在 Amazon 下了单,相关配件如下:
- 透明盖户外接线盒,标称 IP67,尺寸 8.7 × 6.7 × 4.3 英寸(£27.99)。
- DIN 导轨安装的 12V 直流电源(£11.09)。
- 一个 DC 转 USB-C PD 模块,把 12V 电源转成 USB-C 供电。
- A 型 RCBO,标称 10A、30mA 剩余电流保护(£14.00)。
- 三米 2.5mm² 双芯护套线(£11.99)。
- 直角 USB-C 电源转接线,线长 25cm。我只需要一根,但它一包六根(£5.99)。
那个小小的 USB-C PD 模块是个相当酷、不多见的设备。它接收 12V 输入,输出 USB-C Power Delivery。我觉得自己还会再买一个。
以上是我订单上的价格,不是现价。一个防水盒、电源、USB-C 电源模块、保护装置、线缆和 USB 转接线——就为了提醒自己倒垃圾。我知道,我知道。
六个垃圾桶的标签都装好了。到这一步,我对整个系统运转良好的信心已经相当足了。

锚点安装在墙上。打印的电路板外壳放在大接线盒里面。安装照片。
提醒与通知
这里要特别感谢 Waste Collection Schedule 集成。它通过抓取市政的垃圾收集日程,省去了我手动维护多个固定日期、并记牢哪种垃圾桶组合该扔的麻烦。BinRange 负责提供垃圾桶的物理状态数据,Home Assistant 则将这两部分信息整合起来。
常规仪表盘会显示下一次收集时间、垃圾桶的紧凑状态以及需要注意的事项,而雷达范围和无线电诊断则拥有独立视图。虽然我喜欢钻研技术细节,但查看明天该扔哪些桶,不该还得去解读无线电轨迹。
提醒信息会按相关垃圾桶分组成一条消息:在前一晚的 18:00 和 21:00,以及如果垃圾桶仍被自信地判定为“在家”的次日早上 6:00,发送最终提醒。次日 18:00 还会提醒把仍处于“外出”状态的垃圾桶收回来。自动化流程会记忆已发送的尝试,避免重复推送相同消息。
无法读取信号时,不应指责某人忘了扔垃圾。同理,常规的收桶提醒只知道收集日程已定且垃圾桶仍在外,它无法判断工作人员是否已将其清空。

Home Assistant 发出的提醒,要求把部分垃圾桶摆出去。
我还希望在垃圾桶刚被清空时收到消息。当时很想仅凭移动状态、时段和收集日程来判断,但回顾一次收集记录后发现,这些线索很难准确推断标签何时停止上报——因为一天中大部分时间都没有信号。虽然唤醒计数器能表明有过活动,却无法说明是谁操作了垃圾桶,以及操作原因。
标签本身已内置加速度计,于是补充了一个更具体的线索。相对于校准后的直立姿态,持续倾斜超过 90 度即记录一次“倾倒事件”。标签会保留该事件,即使在中继接收出现间隙,也会在下一次到达锚点时上报事件发生时间。Home Assistant 可将“即将收集且已知在外的垃圾桶上近期发生的一次倾倒”视为其已被清空的假设。
最初的自动化脚本每五秒检查一次是否有待处理的倾倒记录。对于每周只发生几次的操作来说,这频率显得太高了,所以我改成了 MQTT 事件流,配合一个定时器,仅在有截止期限待处理时运行。符合条件的垃圾桶会被分组监控六十秒,系统保存状态以防止重复触发,并支持在 Home Assistant 重启后恢复数据。定时提醒则作为后备方案。

Home Assistant 通知垃圾桶已清空,可以移回室内。
首次生产环境测试
9 月 28 日,我遇到了一次花园垃圾收集,可以借此进行详细观察。垃圾桶大约在上午 7:05 开始移动,随即越过十米范围的边界。在它离开期间,报告停止到达。当它在上午 10:25 左右重新出现时,携带了大约 9:27 时存储的倾倒记录,随后又回到 Home 区域并静止下来。
该标签检测到了倾倒行为并保留了记录。离开和返回过程清晰可见。但 Home Assistant 接收到该记录时,时间已经过去约五十八分钟,超出了自动化脚本的三十分钟接受限制。“刚清空”的消息未能发出。其他垃圾桶继续发送报告,且在该时间间隔内,锚点端的 MQTT 连接也没有故障。目前我仍无法解释为什么那个标签在收集位置没有接收到信号。
这次经历修正了我的乐观情绪。无线电可以产生令人信服的测量数据,标签能检测倾倒,仪表盘显示也看似正常,但手机依然错过了清空通知。通过检查真实收集过程,我得到了更清晰的待改进事项清单。
这也改变了我对“失联”垃圾桶的展示方式。最初,一旦“Out”(在户外)读数过期,状态就会变为“Unknown”(未知)。这种做法很谨慎,但亲眼看到垃圾桶离开并在路边失去联系后,用“Unknown”替代“Out”就不再那么有用了。
现在的实时系统会把观测到的离开状态记为推测在外,即使中间失联或 Home Assistant 重启也能保持。接收状况则单独展示。要让垃圾桶回到「在家」状态,需要在边界内重新测到一次新读数。如果最后记录是在家、然后没了信号,就标记为「未知」,因为我从未见过它离开。「该带回来了」提醒可以基于推测在外;而「记得拿回来」提醒仍然需要垃圾桶确实在家的最新证据。
这样一来,显示的信息就更接近我实际知道的情况。

等待更多收垃圾日
BinRange 已经装好并投入使用。(写下这句话时我才意识到,我把一个本来用途很通用的工具给局限住了 🤔)
接下来还有些相当常规的观察要做:各个存放点和收集点的覆盖情况、完整跑几个垃圾日周期并验证通知正确送达、观察电池几周内的表现,以及支架和外壳在户外的耐用性。另外我还希望设备更新时不用我手动重试也能可靠连上。这个项目本来就不是做给别人用的,但如果有人受到启发想尝试类似的东西,或者想复用其中任何部分,欢迎他们(或他们的机器人朋友)随便拿去用。
固件、外壳文件、实测数据和 Home Assistant 示例都在仓库里,有兴趣可以看看。这是我的业余项目,它在户外的实际表现还在摸索中。
当然,见识了 UWB 的能力之后,我开始琢磨能不能在猫的项圈上装标签,再用房子四周的四个锚点来定位它们。能不能做到厘米级的猫咪追踪?我还没试过,而且垃圾桶项目已经充分说明了环境和射频覆盖有多重要。但我实在很心动。
不过大多数人大概直接找猫就行了。