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

家里那只 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 里没有地址,先别急着往下走,网络没通后面全是空谈。

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 完全一样,所以执行前一定要让机器人站在台面上,它会动到每一个关节。
如果机器人当着你面站了起来,说明"面板 → 机器人"这一段已经打通。这一步不是为了测试而测试,而是确认链路已经打通,否则待会把地址开到公网,你分不清是面板坏了还是穿透坏了。

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 小时内会变。今天发给我妈的地址,明天她再点就打不开了,又得重新发一遍。给家里人用,这种体验很劝退。

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 本来就是"谁都能发同一种指令"的设计,我只是在它前面加了一道带锁的门。哪天真想扩展,比如给家人多开放几个动作、或者接上它那个外网可见的摄像头做点别的,改的都是面板这一层,机器人那边一行不用动。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐



所有评论(0)