给坏情绪一个出口: 从 PrimeConsole 仿真验证到 PrimeHub 上架——我给 Q1 写了个“抱抱补给站”技能
前言
那天晚上调试到十一点,一段轨迹改了五版还是不对,我盯着屏幕忽然冒出一个念头:现在要是有人抱我一下就好了。
这个念头挥之不去。第二天想起桌边资料里那台 Q1——88 厘米、15 公斤的小个子人形机器人,手臂能张能合,内置动作库里躺着一个几乎没人用过的"拥抱"。那就它了:做一个抱抱补给站,谁难过的时候喊一声,机器人就颠颠地跑过来(好吧,目前是原地),张开手臂给你一个抱抱。

不过这篇文章不只是讲一个技能。从一句话的想法到市场上架,中间隔着选题、包结构、接口调用、仿真验证、无真机测试一整条链路;路上还有些小机关这条链怎么走、这些坑怎么绕,这篇文章一次走完。
一、PrimeConsole 是什么:一个桌面端的两副面孔
启元官方给开发者的工具链是两件套:PrimeConsole(开发与验证)和 PrimeHub(技能市场分发)。PrimeConsole 从开发者社区 dev.primebot.com 的下载页安装,Windows / macOS / Linux 各有安装包,我用的 1.0.2 Windows 版。
打开后主界面只有两个模块,边界很清楚:
1.1 机器人控制
连接真机后的工作台:实时视频流、运行日志、方向控制、急停复位、动作与表情的快捷触发。不连机器人时是空态——第一次打开会觉得"这软件怎么什么都没有",其实它在如实告诉你:第一步永远是连接设备。

1.2 Skill 验证(本文主战场)
这一页面向的不是真机,而是技能包(.rpk 文件)。拆开看有六块:
| 区域 | 内容 |
|---|---|
| 左侧技能面板 | 已导入技能列表、当前选中技能的包名/版本/机型 |
| 原子能力列表 | PrimeConsole 解析出的技能所调用的能力(语音合成、预设动作、表情播放、LED 灯效) |
| 源码视图 | 解析结果的 JSON 展示,含 skillId / version / robotModel / atomicCapabilities |
| 3D 仿真视口 | "开始模拟仿真 / 复位仿真 / 部署到机器人"按钮,右上角挂着一条容易被忽略的提示:“只能仿真上肢动作” |
| 验证报告 | 最近 3 条验证记录 |
| 右下机器人状态 | 未连接时的健康检查占位 |

两个入口按钮在验证报告面板下:导入 .rpk 和 上传到 PrimeHub——从本地包到市场上架,全程不用离开这个应用。
1.3 先说结论:仿真能验证什么
把结论放在前面,能省掉后面所有困惑:PrimeConsole 不执行你的代码。 它做的是静态扫描——解析包结构和元数据、识别你调用了哪些原子能力、把代码里引用的预设动作编号收进一个队列,点"开始模拟仿真"时按队列在 3D 模型上回放这些内置预设动作。
这意味着两件事:
- PrimeConsole 把输出面验证得很周到:包结构合法性、原子能力识别、动作引用回放,全都不需要真机;而触发与业务逻辑(语音识别、节流、状态机)这层,平台的设计是交给开发者自测(第七节给出我的测法);
- 仿真的动作面 = 内置预设库的子集。全预设编排的技能,仿真即真机效果;带自研轨迹的技能,仿真只能看到回退动作。
理解了这条边界,下面的一切都顺理成章。
二、开发环境:比想象中轻
Q1 的 SDK 二次开发环境是 Ubuntu 22.04 + ROS2 Humble(编译 aimdk_msgs、跑 ROS2 节点)。但**做技能包不需要 **ROS——这条"轻量发布链"全程 Windows 可完成:
| 工具 | 用途 |
|---|---|
| Windows + Python 3(纯标准库) | 写 main.py、打包 .rpk |
| PrimeConsole 1.0.2 | 导入、静态验证、仿真、上架 |
| Q1 SDK v1.0.0.0 文档 | 接口依据(《接口说明.md》,接口协议 PDF 均随 SDK 提供) |
| (可选)WSL2 + ROS2 Humble | 无真机的端到端逻辑测试,第四节的 mock 方案 |
三、技能包解剖:三个文件一个包
.rpk 本质是 ZIP,压缩包根目录放三样东西:
hug_buddy_1.0.1.rpk (ZIP)
├── main.py # 入口:ROS2 节点,全部业务逻辑
├── cfg/pkg.yaml # 包元数据
└── README.md # 技能说明
把后缀改成 .zip 用任意解压工具打开,看到的就这三样——没有二进制、没有依赖地狱,结构透明到可以用文本编辑器逐行读完:

cfg/pkg.yaml 的字段(实测反推,官方未公开规范):
name: hug_buddy # 技能名
package_name: hug_buddy # 必须与 name 同时写!
version: 1.0.1
author: your_name
robot_model: q1 # 机型
description: 一句话描述
第一个坑就在这:只写 name 不写 package_name,导入后包名显示 unknown。社区文章实测过,我复核属实。
打包脚本只需要 Python 标准库:
import zipfile
from pathlib import Path
SKILL_DIR = Path("skill")
with zipfile.ZipFile("dist/hug_buddy_1.0.1.rpk", "w", zipfile.ZIP_DEFLATED) as zf:
for path in sorted(SKILL_DIR.rglob("*")):
if path.is_file() and "__pycache__" not in path.parts:
zf.write(path, path.relative_to(SKILL_DIR).as_posix())
注意两点:cfg/pkg.yaml 的相对路径必须原样保留(不能拍平到根目录);所有文件 UTF-8 编码——台词里有中文,编码错了真机 TTS 会念乱码。
四、五个服务一个话题:API 调用速查
抱抱补给站用到的全部接口(服务名前缀 /aimdk_5Fmsgs/srv/ 是平台固定写法):
| 能力 | 接口 | 关键参数 |
|---|---|---|
| 语音识别 | 话题 /aima/agent/asr_result | text / is_final / confidence |
| 台词 | PlayTts | priority_level.value = 6(交互档)、is_interrupted |
| 表情 | PlayEmotion | emotion_ids=[表情编号]、loop_count=1 |
| 灯效 | LedStripCommand | led_strip_mode=254(自定义呼吸)+ RGB + period |
| 预设动作 | SetMcPresetMotion | motion.value=动作编号、interrupt |
| 运控状态 | GetMcAction / SetMcAction | 动作前置态准备(下文) |
三个实战要点:
① 预设动作有前置态。SetMcPresetMotion 要求运控处于 BIPED_WHOLE_BODY_CTRL。从默认站立态过去要先过桥接态,调用前查一次当前状态、必要时切两步:

这段做成 best-effort:失败不阻塞,动作请求照发,被拒就跳过——交互类技能不值得为状态机卡死主流程。
② TTS 是异步的。 服务返回成功只代表进了队列,说完没说完要自己估时(经验值 200ms/字)。排动作节奏时按"台词起 0.3~0.5 秒后再动胳膊"处理,体验最自然。
③ 防自听自话。 机器人自己说的台词里含触发词(“抱抱补给站开张啦”),麦克风可能识别成新触发。解法是 busy 锁覆盖整个序列 + 冷却时间从序列结束起算,实测能挡住回声循环。
五、完整案例:抱抱补给站的 160 行
5.1 设计
三段式编排,动作全部来自内置预设库(招手、拥抱、双手比心):
| 段落 | 动作 | 台词 | 灯效 |
|---|---|---|---|
| 上门 | 招手 | “抱抱补给站开张啦,我来了!” | 暖橙呼吸 |
| 拥抱 | 拥抱 | “张开手,接好咯——” | 暖橙呼吸 |
| 收尾 | 双手比心 | “难过的时候,我的抱抱免费无限量供应。” | 粉色呼吸 → 白色呼吸 |
截至笔者发稿,技能市场还没有技能用到"拥抱"这个动作——纯陪伴向也是空位。
5.2 触发:语音关键词
HUG_KEYWORDS = ("抱抱", "抱一下", "拥抱", "难过", "不开心", "安慰")
def on_asr(self, msg: AsrResult):
if not msg.is_final:
return
if msg.confidence and msg.confidence < 0.5:
return
text = (msg.text or "").strip()
if not any(k in text for k in HUG_KEYWORDS):
return
self._try_trigger(f"语音「{text}」")
_try_trigger 里做 busy + 冷却双重检查,通过后开工作线程跑序列——别在回调里直接跑,会堵住 executor,服务响应都收不回来。
5.3 动作调用:一个方法一个动作
def _motion_hug(self):
if not self.motion_cli.wait_for_service(timeout_sec=1.0):
return
req = SetMcPresetMotion.Request()
req.header = RequestHeader()
req.header.stamp = self.get_clock().now().to_msg()
req.source = "hug_buddy"
req.motion.value = 3018 # 拥抱——必须写字面量!
req.interrupt = True
self.motion_cli.call_async(req)
为什么三个动作拆成三个方法、编号写死在赋值语句里?这是被静态扫描逼的,第六节细说。
5.4 打包、导入、仿真
python build.py 产出 5.6KB 的 .rpk → PrimeConsole 导入 → 原子能力四项全识别(语音合成 / 预设动作 / 表情播放 / LED 灯效)→ 点"开始模拟仿真":

3D 里的 Q1 依次招手、张开双臂、双手比心。 全预设编排的好处在这里兑现:仿真队列和真机行为完全一致,导出即所见。
六、PrimeHub 生态:上架前先看清市场
技能写好只是上半场,分发决定它被谁看见。动笔写代码前,建议先把 PrimeHub(skillhub.primebot.com)逛一遍——游客可浏览全部技能卡片,下载需手机验证码登录。以下是我发稿时的市场观察。
6.1 一个上线一个月的市场长什么样
笔者抓取时的快照:十余个技能、总下载四百次上下,规模还小,但结构性要素已经齐了:
- 官方示范带节奏:下载量前三(视频电话、拿衣服、天气预报)都是社区上线时预埋的官方示范技能,贡献了约六成下载量——应用商店冷启动的通用打法
- 社区最活跃的突破口是"对话":下载最高的社区技能 PrimeBuddy 是自定义对话框架(接管原厂对话 Agent),其后还有 Tina 语音助手、WorkBuddy 连接器——用户最想定制的不是动作,是"脑子"
- 技能依赖链萌芽:Yeelight 灯控技能声明"需另行安装 PrimeBuddy"——技能之间出现依赖关系,这是市场从展示柜走向生态的早期标志
- 评价体系尚未启动:全部技能的浏览、喜欢计数为零,下载量是唯一活跃指标

6.2 发布表单要点
上传入口就在 PrimeConsole 验证报告面板下(“上传到 PrimeHub”),表单字段:技能名称、版本号(与包内 pkg.yaml 一致)、一句话描述、分类、机器人类型(Q1)、标签、图标。提交后刷新"最近上新"即可看到——从写第一行代码到市场上架,一个桌面应用走完全程。

七、无真机怎么办:mock 服务的端到端测试
仿真能替我确认"包没写错、动作排对了",但"喊一声抱抱,服务就按顺序出发"这条业务链路,平台按设计留给了开发者。为了让它在上真机前就有底气,我在 WSL(Ubuntu 22.04 + ROS2 Humble + 编译好的 aimdk_msgs)里写了一个 mock** 机器人**:六个服务的假实现 + 语音话题发布器,被测技能作为子进程跑,stdout 逐行收集断言:
class MockRobot(Node):
def __init__(self):
...
self.create_service(SetMcPresetMotion,
"/aimdk_5Fmsgs/srv/SetMcPresetMotion",
self.on_preset_motion)
def on_preset_motion(self, req, res):
self._log(f"MOTION: value={req.motion.value}") # 记录请求
res.response.header.code = 0
return res
14 条断言覆盖:关键词命中、busy 期回声不重复触发、动作按 招手→拥抱→比心 有序下发、灯效双色切换、状态机先桥接后目标、冷却后二轮服务完整跑通。14/14 通过再打包,这套 mock 思路对任何语音/触摸触发型技能都适用。

踩过的一个测试坑值得记录:DDS 在进程启动后的"发现期"会丢消息,靠 sleep 留余量的触发时序偶发失败。改成事件驱动后根治——发一条触发消息,轮询技能日志出现 [触发] 再继续,没出现就重发,测试从"间歇性绿"变成"稳定绿"。
测试通过后回到第六节的发布流程:改好署名、重新打包、上传表单、上架——抱抱补给站就开张了。

八、这条工具链适合谁:三种情况三种走法
最后按情况给点选型建议,都是这几天实操下来的体会:
第一次做技能、机器上没有 ROS 环境:本文的路线就是为你写的。Windows + Python 标准库完成写包与打包,PrimeConsole 包办导入、验证、仿真、上架,全程不碰命令行。如果技能动作全部选自内置预设库,仿真里看到的就是真机效果——成品感来得最快。
手上已有 ROS2 环境(WSL 或双系统):图形端和命令行不是二选一,而是分工。PrimeConsole 管包验证和发布,业务逻辑交给第七节那样的 mock 服务在本地闭环测试;两边共用同一套 aimdk_msgs 协议,代码拿来就能跑。真机联调时,命令行还能做 PrimeConsole 覆盖不到的深度控制和自动化测试。
手上有真机:PrimeConsole 基本可以当默认工作台——连接、日志、视频流、动作调试、验证、发布一站式。建议保留开发者模式下的 SSH 通道,图形界面看状态、命令行查细节,两边互为备份。
写在最后
回到那天晚上想要的那个抱抱。技能做出来后我让仿真里的 Q1 对着我张开了手臂——理性知道那只是一段预设关节轨迹的回放,但"难过的时候,我的抱抱免费无限量供应"这句台词亮起来的时候,还是被治愈了一下。
技术的意义大抵如此:它不能替你消化情绪,但它可以认真地接住你。希望某个加班到深夜的人喊一声"抱抱",会有一台小机器人认真地张开手臂。
「抱抱补给站」已上架 PrimeHub 技能广场(skillhub.primebot.com 搜索"抱抱补给站"),喊一声"抱抱"就能收到机器人的拥抱,欢迎大家前往体验。有问题欢迎评论区交流。
更多资料获取
启元机器人开发者社区(dev.primebot.com)
PrimeHub 技能市场(skillhub.primebot.com)
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐

所有评论(0)