几百块的桌面机器人只能用手柄在跟前遥控?给 Microduck 双足机器人加一层远程遥控面板,再用 cpolar 让在外地的家人也能逗它走两步

远程遥控 Microduck 桌面双足机器人:悬浮遥控面板与远程网络信号示意封面图

家里那只 25 厘米高的 Microduck 鸭子机器人,玩了不到一周我就发现一个问题:它确实好玩,走路、叼东西、被推倒还能自己站起来,但每次都得我拿手柄坐在离它两米以内的地方指挥。

我妈在外地,视频里看它走两步觉得新鲜,让我"也让我玩玩"。我总不能把手柄隔着屏幕递过去。

这篇就干一件事:给 Microduck 加一层远程遥控面板,再用 cpolar 把它开给外地的家人,手机点开网页就能让它走两步。

先把话说在前面,这篇不是那种"买回来就能远程"的教程。Microduck 官方的遥控方式就是蓝牙手柄和手机 App,你要的远程能力得自己搭一层。想清楚这一点,后面每一步你都会踏实很多。

1 先搞清楚 Microduck 到底是个什么东西

Microduck 是 Pollen Robotics 做的开源双足机器人,大约 25 厘米高、800 克重,身体里跑的是一块 Rockchip RK3566 板子。

它的"大脑"是一个用 Rust 写的 daemon 集合,没有大框架,一个 workspace 全包了。这些 daemon 各管一摊:robotd 负责 50Hz 的控制环和电机总线,configd 管 Wi-Fi 和身份,btd 是手机走蓝牙的通道,padd 读手柄,mediad 通过 WebRTC 推摄像头画面,tofd 管深度传感器。它们之间用一套统一的 JSON-RPC 契约在 Unix socket 上通信。

翻译成人话:无论你是用 App、控制台、手柄还是自己写的脚本,发出去的调用长得一模一样。

这一点对我们特别重要,因为它意味着——只要我能发出一条和官方客户端等价的控制指令,机器人就会动。我不需要去改它的固件。

它出厂自带的玩法有这些:走路、加轮子后切换成滚动模式、叼东西、摔倒自起,还能坐、踢球、翻滚、用自己的声音叫两声。手柄上的按键映射官网上写得很全,左摇杆前进后退加横移,右摇杆转向,Start 第一次按下开扭矩进入站立姿态,第二次按下才真正让策略驱动它走。

看到这里你就明白了:所有遥控逻辑都发生在手边这台设备上,机器人本机只认指令。这就是我们能接一层远程面板的底层依据。

2 环境准备:先把机器人连上 Wi-Fi,能在网络里找到它

远程遥控的前提是机器人得在一个网络里,而不是只靠蓝牙跟你的手柄绑着。

先把机器人网络打开。通过 ssh 登上板子(默认账号是 microduck),在机器人上执行:

robotctl net status
robotctl net scan
sudo robotctl net connect <你的SSID> --psk-stdin

--psk-stdin 这个写法是有讲究的,它让密码从标准输入读进来,不出现在 ps 的进程列表里。如果你直接用 --psk 传密码,同一台机器上任何用户都能在命令存活期间看到它。这种板子给家里人用,能防一手是一手。

这里要提醒一句:切换 Wi-Fi 会断开机器人当前所在的那个网络,如果你的 ssh 是走 Wi-Fi 连过去的,这条命令执行下去的瞬间你的会话就掉了。这不是出错,这是操作本身就是这样。重新连上新网之后再 ssh 回来即可。

连好之后确认一下:

robotctl net status

你会看到 SSID、信号强度和它拿到的地址。

敲黑板:这一步做完要看到的是一个 IPv4 地址。如果 net status 里没有地址,先别急着往下走,网络没通后面全是空谈。

robotctl net status 终端输出,显示 SSID home-5g 与 IPv4 地址 192.168.1.42

3 找到那台"已经存在但没人用"的本地服务

接下来是全文最关键的一个发现。

Microduck 的 mediad 这个 daemon 本身就在板子上开了一个 HTTP 服务,默认监听 :8080,它能把摄像头画面以 PNG 形式吐出来:

curl -o /tmp/frame.png http://<机器人地址>:8080/frame

也就是说,机器人自己就是一个可以被浏览的服务。你不必从零写一个"摄像头中转"。

但光有画面不够,家里人要的是"能逗它走两步",得能发控制指令。

我在这里做了一个取舍:不去直接暴露机器人本机的 JSON-RPC socket,而是在我家里的一台常开的机器上跑一层薄薄的遥控面板,由它转发指令给机器人。这么做有几个实在的理由。

第一,机器人本机的控制接口是给局域网和蓝牙客户端用的,直接怼到公网上风险太大,一旦被猜到地址,陌生人就能操纵你家里的机器人。

第二,面板放在中间,我可以在这一层加鉴权、做限流、把复杂的指令收敛成几个按钮。家里人不需要理解什么叫策略槽位,只需要看到"站起来""坐下""踢一脚""叫一声"。

第三,机器人重启、换 Wi-Fi、临时下线,都不会影响外网那一端的地址。

面板本身用一个最小的 Python HTTP 服务就能写清楚,它对外只有几个接口,对内去调机器人的控制接口。下面是核心骨架:

# duck_panel.py —— 跑在常开机器上,负责给外网提供遥控面板
import subprocess, secrets
from http.server import BaseHTTPRequestHandler, HTTPServer

ROBOT_HOST = "192.168.1.42"      # 改成你机器人的实际地址
ROBOT_USER = "microduck"
ACCESS_TOKEN = secrets.token_urlsafe(16)   # 启动时生成,只发给家人

# 把"外网按钮"翻译成"机器人上的一条指令",这是唯一允许被远程触发的白名单
# stand 对应 robotctl robot init,等价于手柄按一下 Start;其余是机器人在驱动状态下能应答的技能
ALLOWED_ACTIONS = {
    "stand":  ["robotctl", "robot", "init"],
    "sit":    ["robotctl", "robot", "do", "sit_toggle"],
    "kick":   ["robotctl", "robot", "do", "kick_right"],
    "quack":  ["robotctl", "quack"],
}

def run_on_robot(action):
    cmd = ALLOWED_ACTIONS.get(action)
    if not cmd:
        return False, "unknown action"
    full = ["ssh", f"{ROBOT_USER}@{ROBOT_HOST}", "--"] + cmd
    r = subprocess.run(full, capture_output=True, text=True, timeout=10)
    return r.returncode == 0, (r.stdout + r.stderr).strip()

class Panel(BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path == "/frame":
            # 直接从机器人 mediad 拉一帧,省掉自己写采集逻辑
            r = subprocess.run(
                ["curl", "-s", f"http://{ROBOT_HOST}:8080/frame"],
                capture_output=True, timeout=10)
            self.send_response(200)
            self.send_header("Content-Type", "image/png")
            self.end_headers()
            self.wfile.write(r.stdout)
            return
        self.send_response(404); self.end_headers()

    def do_POST(self):
        # 先鉴权,token 不对直接拒绝,绝不放行任何控制指令
        if self.headers.get("X-Duck-Token") != ACCESS_TOKEN:
            self.send_response(403); self.end_headers()
            self.wfile.write(b"forbidden"); return
        action = self.path.lstrip("/")
        ok, msg = run_on_robot(action)
        self.send_response(200 if ok else 500)
        self.end_headers()
        self.wfile.write(msg.encode() or b"ok")

if __name__ == "__main__":
    print("access token:", ACCESS_TOKEN)
    HTTPServer(("127.0.0.1", 8090), Panel).serve_forever()

几个细节值得说清楚。

ALLOWED_ACTIONS 这张白名单是这套方案的安全底线:外网能触发的动作是从这张表里查出来的,查不到的路径一律返回 unknown action。我故意没把任意命令执行留进去,家里人再好奇也点不出一个能删文件的按钮。

还有个前提得说在前面:机器人得先处于驱动状态,技能才会应答。stand 走的 robotctl robot init 就是干这个的,它给关节上电并进入站立姿态,之后 sit、kick 这些技能才能被触发。所以面板上第一个该按的按钮永远是"站起来",顺序反了技能是没反应的——这也是我把它放在列表第一位的原因。

监听地址我写的是 127.0.0.1,不是 0.0.0.0。原因后面讲穿透时你会看到——这正是配合 cpolar 的正确姿势。

ACCESS_TOKEN 是进程启动时随机生成的,我会把它连同网页地址一起发给家人。这一步看着麻烦,但它是"公网摄像头 + 机器人控制"这种组合的最低要求。

启动它:

python3 duck_panel.py

终端会打印出 access token,先把它记下来。

本地先自测一下,确认面板和机器人之间的链路是通的:

curl -X POST -H "X-Duck-Token: <你刚拿到的token>" http://127.0.0.1:8090/stand

robotctl robot init 的作用是给各关节上电并在约两秒内回到站立姿态,它做的事和手柄按一下 Start 完全一样,所以执行前一定要让机器人站在台面上,它会动到每一个关节。

如果机器人当着你面站了起来,说明"面板 → 机器人"这一段已经打通。这一步不是为了测试而测试,而是确认链路已经打通,否则待会把地址开到公网,你分不清是面板坏了还是穿透坏了。

本地 curl 调用 /stand 后 Microduck 机器人在桌面上站起,旁边电脑显示控制命令

4 用 cpolar 把遥控面板开给外地的家人

前面面板只监听 127.0.0.1:8090,除了本机谁也访问不到。现在要把它放到公网上。

先在跑面板的这台机器上装 cpolar。

Linux 或树莓派这类支持 systemd 的环境,官方一键脚本:

curl -L https://www.cpolar.com/static/downloads/install-release-cpolar.sh | sudo bash

macOS 走 Homebrew:

brew tap probezy/core && brew install cpolar
sudo cpolar service install
sudo cpolar service start

装完先验证服务起来了:

cpolar version
curl -s http://127.0.0.1:9200 || echo "服务未启动"

9200 是 cpolar 本地的 Web UI 端口。打开 http://127.0.0.1:9200,用邮箱登录,很多情况下登录后会自动把邮箱和 authtoken 写进配置文件,不需要你再手动绑一次。

如果你这台机器没有图形界面,或者 Web UI 自动写入没成功,手动绑定一下:

cpolar authtoken <你的authtoken>

authtoken 的位置是固定的:登录后台 https://dashboard.cpolar.com,点顶部导航的「验证」,进去就是「你的隧道 Authtoken」,右边有复制按钮。不用满后台翻。

绑好之后,把面板端口映射出去:

cpolar http 8090

注意这里映射的是 8090,也就是我们那个只监听本机的面板,而不是机器人的 8080。曲线是「公网 → 面板 → 机器人」,中间那层面板就是我们的闸门。这也是我前面说监听 127.0.0.1 的原因——面板和机器人的面都只朝内,对外只有一个入口,好管。

命令执行后,终端会输出一个公网地址,形如 https://xxxx.r2.cpolar.cn。把这个地址连同前面那个 access token 发给家人,她手机上打开就能看到机器人的实时画面,点按钮就能让它站起来、踢一脚、叫两声。

到这一步,功能上是成了。

但我得说一句实话:免费套餐给的是随机公网地址,24 小时内会变。今天发给我妈的地址,明天她再点就打不开了,又得重新发一遍。给家里人用,这种体验很劝退。

cpolar http 8090 终端输出公网地址 https://a1b2c3d4.r2.cpolar.cn 示意图

5 固定二级子域名,让家人存一个书签就行

要让地址不变,用 cpolar 的固定二级子域名。

按官方规则,固定二级子域名需要基础套餐或以上。基础套餐 99 元一年,包含 3 个保留二级子域名、1 个在线 cpolar 进程、每进程 8 个隧道、60 个连接数每分钟。

操作走 Web UI 比记住命令省事。在 http://127.0.0.1:9200 或者后台里新建隧道,字段这样填:

  • 隧道名称:自定义,比如 duck-panel
  • 协议:选 http
  • 本地地址:填 8090
  • 域名类型:选「二级子域名」,填上你想要的前缀,比如 duck
  • 地区:按你的实际位置选

保存之后回到「状态 → 在线隧道列表」,就能看到固定的公网地址了,形如 https://duck.cpolar.cn。

我妈把这个存成手机书签,以后点一下就是同一个页面,不用我再发链接。这一步做完,方案才算真的"能长期用",不然就是个一次性演示。

6 别忘了这几个安全点

远程操控自家机器人,开心归开心,几条底线得守住。

  • 面板的 access token 别写在文章里、也别发群里,它是唯一挡在陌生人和你机器人之间的东西。
  • 只开放白名单里的那几个动作。想加新玩法,手动往 ALLOWED_ACTIONS 里加,别图省事塞一个通用命令执行进去。
  • 不用的时候把 cpolar 隧道停掉。临时给家人演示,按需开就行,没必要让机器人面板 7×24 挂在公网上。
  • 面板里虽然只暴露了机器人的控制接口,但机器人本机的 mediad 那个 :8080 依然只在内网可达,这点没变,别自己手痒把它也穿出去。

说白了就一句话:能远程是一回事,该不该一直远程是另一回事。

7 总结

现在回头看,我们已经把一个"只有手柄在跟前能玩"的桌面机器人,变成了一台外地家人手机点开网页就能逗的远程玩具。中间那层面板把复杂都吃掉了——家里人看到的是几个按钮,背后是机器人本机一整套 Rust daemon 在跑控制环。

关键步骤其实就三步:

  • 先让机器人上 Wi-Fi,确认 robotctl net status 能拿到地址
  • 在本机跑一层只监听 127.0.0.1 的白名单遥控面板,把外网按钮翻译成机器人指令
  • 用 cpolar http 8090 把面板开出去,再用基础套餐的固定二级子域名换一个不变的地址

这套搭法最舒服的地方在于,它没有去碰机器人的固件,也没有依赖任何官方没有的能力——Microduck 本来就是"谁都能发同一种指令"的设计,我只是在它前面加了一道带锁的门。哪天真想扩展,比如给家人多开放几个动作、或者接上它那个外网可见的摄像头做点别的,改的都是面板这一层,机器人那边一行不用动。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐