duckctl:从笔记本控制机器人,不需要网络和 ssh
最后更新
duckctl 跑在你的笔记本上,通过蓝牙 LE 和机器人对话。它是手机 App 的替代品,也是触达一台从没见过 wifi 网络的机器人的唯一途径。
这解决了一个先有鸡还是先有蛋的问题:新机器人要连 wifi,但配 wifi 通常需要先能连上它。duckctl wifi connect 走无线电,自己不需要任何网络。
不要在机器人上跑它。发布版里没有任何东西依赖它——机器人上的工具是
robotctl。
命名上有个细节:工具名刻意不体现蓝牙。mediad 给机器人提供了第二条传输通道,触达的是另一组方法,所以工具按「它跟谁说话」命名,而不是按「它现在用哪种无线电」。它曾经叫 duck-btctl。
装它
从仓库克隆直接跑:
cargo run -q -p duckctl -- --name <robot-name> info
或者装一次(代价是它成为一个不再跟随分支的快照):
cargo install --path duckctl
duckctl --name <robot-name> info
下面的命令都按安装后的形式写。想从克隆里跑,前面加 cargo run -q -p duckctl --。
这个工具以前会把自己装成
btctl。如果which btctl还能找到一个,那是你当年安装时的构建产物,它永远不会再更新:cargo uninstall btd --bin btctl
找到机器人
duckctl scan
1 robot(s) advertising the duck service:
aa:bb:cc:dd:ee:ff duck-c51b — 192.168.1.42, 1 service(s) ← DUCK_ROBOT
7 other device(s) in range, not listed. …
只列机器人,无线电范围内的其它设备只计数不列出。--verbose 会展开——想找的机器人没出现在第一份列表里时,值得看一眼。
每台机器人会广播自己的 IPv4 地址,所以这里也是拿到 ssh 目标地址的地方。整个过程不建立连接、不需要 PIN。
- 行尾
no address表示机器人没连网络; - 完全没有地址那一行,说明它跑的是“机器人还不广播地址”之前的发布版,这时
duckctl wifi status依然能报告。
SSID 不在列表里——广播包塞不下。duckctl wifi status 有它,还有信号强度和两个地址。
只要地址
ssh radxa@$(duckctl ip)
ip 只打印地址,所以可以直接做命令替换。它读的是广播包,不建立连接、不需要 PIN、约一秒返回。而且答案不会过期:btd 每五秒重读一次地址,变了就重新广播。
已经和这台电脑绑定过的机器人,往往会停止向它广播该服务,这时 ip 会转而建立连接并调用 net.status——更慢,而且需要 PIN,但它总能给出答案。--verbose 会说明走的是哪条路。
机器人的名字
从没改过名的机器人叫 duck- 加上从序列号推出的四个字符,比如 duck-c51b。
macOS 会把一台机器人显示成两个名字合在一起,比如 radxa-zero3 [duck-c51b]——任意一半都能用作 --name。
两个常见的名字问题
扫到过 duck-c51b,连过一次之后只剩 radxa-zero3:机器人跑的发布版把名字给了广播包但没给蓝牙适配器,而客户端缓存了适配器那个。更新机器人。但这不会清掉客户端已经缓存的,所以还要手动清:Linux 上 bluetoothctl remove <mac>,macOS 上在蓝牙设置里“忽略此设备”。
两台机器人重名:
2 robots answer to "radxa-zero3": radxa-zero3, radxa-zero3
指定了名字却有两台应答,是错误而不是让你选。这发生在 bootloader 把序列号留空的板子上——它于是按主机名命名,而同一个镜像刷出来的板子主机名都一样。在机器人上改名:
robotctl system set-name ducky
固定用同一台机器人
把名字放进环境变量,不用每条命令都写:
export DUCK_ROBOT=duck-c51b
duckctl info
写进 ~/.zshrc 就能持久化。DUCK_PIN 同理,用于设了 PIN 的机器人:
export DUCK_PIN=418299
临时对另一台机器人执行,--name 优先:
duckctl --name duck-ffff info
临时忽略默认值(比如台架上放着别人的机器人),把它设为空:
DUCK_ROBOT= duckctl scan
改名不会跟着改
DUCK_ROBOT。工具会提醒你,但变量得手动改——否则之后每条命令都在找一个不再应答的名字。
网页控制台
机器人自己提供一个页面,能看摄像头画面并驱动它:
duckctl open
它先找到机器人,然后在浏览器里打开 http://<address>:8080/。--print 只给 URL(适合没有浏览器的机器或脚本),--port 用于 mediad --web-port 非默认的情况。
不需要安装任何东西,也不需要起服务——mediad 把这个页面内嵌了,所以跑着这个守护进程的机器人就自带控制台。
页面上有:摄像头画面,旁边是链路的码率、帧率、丢包和往返延迟;两个虚拟摇杆,以及 W/A/S/D 和 Q/E 按键驱动(速度是手柄的 0.3 m/s 和 1.5 rad/s);在画面上拖动可以让它看向某个点;enable / init / relax / stop / shutdown 按钮;技能和音色库做成菜单;以及 2 Hz 的状态流和 robot.health——舵机过热、电池没电、控制循环变慢都在这里报出来。
stop只是把页面正在发送的指令归零。它不是急停——这个系统里没有任何东西能从浏览器切断舵机电源,按钮做成普通样式就是这个原因。
控制台连不上时
涉及两个端口,而你只输入了其中一个。页面自己会用它被服务的那个主机去连 8443 上的信令服务器。
如果页面能加载,但提示信令端口没有应答——机器人是好的,你和 8443 之间有东西挡着,最常见的是防火墙。
另外:摄像头和驱动控制只走 WebRTC,所以没有网络地址的机器人没有控制台。先用下面的 wifi connect 通过无线电把它接入网络。
配 wifi(不需要自己有网络)
duckctl --name <robot-name> wifi status # SSID、信号、地址
duckctl --name <robot-name> wifi scan # 机器人现场扫描,要几秒
duckctl --name <robot-name> wifi connect <ssid> --psk <passphrase>
duckctl --name <robot-name> wifi forget <ssid>
开放网络省掉 --psk。
wifi scan 是机器人现场扫无线电,不是返回上次的结果,所以要等几秒。
wifi connect 有两个行为要有心理准备:
- 加入新网络会断开当前网络,所以走 wifi 的 ssh 会话会掉——这是命令在正常工作,不是失败。
- 它最多可能要 45 秒才返回。
设计上 net.connect 被明确禁止走 WebRTC,所以配网这件事只能走无线电——这也是为什么没有网络地址的机器人会被告知该怎么办,而不是简单报个空。
状态与版本
duckctl --name <robot-name> info # 名字、序列号、运行时长
duckctl --name <robot-name> health # 控制循环是否健康
duckctl --name <robot-name> status # 版本握手与更新状态
duckctl --name <robot-name> version # API 版本、发布版本、构建时的 git revision
duckctl --name <robot-name> name <new-name> # 最多 24 字符,几秒生效,不用重启
duckctl --name <robot-name> reboot
version 里 revision 为 null 意味着这个发布版是在某人的笔记本上构建的,不是 CI 出的。排查诡异问题时这条信息很有用。
更新
用词和 robotctl update 完全一致,所以在机器人上学会的命令这里直接能用。每条都接受 --component <name>,默认 daemon——目前机器人也只有这一个组件。
duckctl --name <robot-name> update check
duckctl --name <robot-name> update status
duckctl --name <robot-name> update versions
duckctl --name <robot-name> update log --limit 20
duckctl --name <robot-name> update apply
安装要几分钟,过程中会打印进度:
· daemon: preflight
· daemon: downloading 12%
· daemon: downloading 47%
· daemon: verifying
· daemon: swapping
· daemon: health_gate
{
"outcome": "applied",
"from": "0.5.1",
"to": "0.6.0"
}
装完连接会断开——机器人立刻重启各守护进程,btd 大约在这个应答之后五秒重启。这是更新在正常工作。重连后跑 duckctl update status 确认。
相关
- robotctl 命令速查——机器人上的对应命令
- 系统架构——
btd和mediad各自负责什么