没有真机也能做启元 Q1 二次开发:从 JSON 动作剧本到 RPK 仿真验证

本文围绕启元 Q1 完成一个可复现的二次开发项目:不直接把动作顺序写死在控制程序中,而是设计一套 JSON 动作剧本格式,加入白名单、时长和安全复位校验,再构建为 RPK 技能包,最终导入 PrimeConsole 进行本地上肢模拟仿真。

实验边界:本文没有连接实体机器人。文中的命令行结果与 PrimeConsole 画面均来自真实操作。

1. 为什么要给 Q1 增加“动作剧本层”

调用一次预设动作并不困难,真正影响二次开发质量的是:多个动作如何组织、错误配置如何提前发现、技能包能否稳定复现,以及没有真机时能验证到哪一步。

如果把动作 ID、等待时间和执行顺序全部写死在 Python 代码里,每次调整流程都要修改程序。本文换一个思路:把“做什么”放进 JSON,把“是否合法”交给独立校验器,把“如何调用 Q1”留给运行时入口。

整个数据流如下:

scene.json
    │
    ├── 静态校验:动作白名单 / 单步时长 / 总时长 / 安全复位
    │
    ├── 编译输出:skill/cfg/compiled_scene.json
    │
    ├── 自动化测试:正常用例 + 非法动作 + 时长越界
    │
    └── 确定性打包:q1_action_storyboard.rpk
                              │
                              └── PrimeConsole 本地上肢模拟仿真

这个项目不追求复杂算法,而是补齐机器人技能开发中常被忽略的工程链路:配置、校验、失败保护、构建和仿真证据。

2. 实验环境与项目结构

本次真实使用的环境如下:

项目实际版本或状态
操作系统Windows,本地开发环境
离线脚本Python 3.14.7
PrimeConsole1.0.2
Skillq1_action_storyboard 1.0.0
目标模型启元 Q 系列 Q1
真机连接未连接
仿真范围PrimeConsole 本地上肢动作

目录结构如下:

q1-action-storyboard/
├─ scene.json                    # 动作剧本源文件
├─ storyboard.py                 # 校验与编译核心
├─ validate_scene.py             # 命令行校验入口
├─ dry_run.py                    # 不依赖 ROS 2 的时间线预演
├─ build.py                      # 确定性 RPK 打包
├─ tests/
│  ├─ test_storyboard.py
│  └─ fixtures/
│     └─ invalid_unknown_motion.json
├─ skill/
│  ├─ main.py                    # Q1 ROS 2 运行时入口
│  ├─ README.md
│  └─ cfg/
│     ├─ pkg.yaml
│     └─ compiled_scene.json
└─ dist/
   └─ q1_action_storyboard.rpk

这里要区分两个环境:storyboard.py、测试和打包脚本可以在普通 Python 环境执行;skill/main.py 引用了 rclpy 与 aimdk_msgs,真机运行仍需要官方 Q1 SDK、ROS 2 Humble、正确控制模式和安全条件。

3. 用 JSON 描述三步动作剧本

本次剧本包含“招手欢迎—双手比心—恢复默认姿态”三步:

{
  "schema_version": 1,
  "skill_name": "q1_action_storyboard",
  "version": "1.0.0",
  "steps": [
    {
      "name": "招手欢迎",
      "action": "beckon",
      "motion_id": 3001,
      "duration_ms": 2600
    },
    {
      "name": "双手比心",
      "action": "make_heart",
      "motion_id": 3007,
      "duration_ms": 3200
    },
    {
      "name": "恢复默认姿态",
      "action": "return_neutral",
      "motion_id": 0,
      "duration_ms": 1800
    }
  ],
  "safety": {
    "max_total_duration_ms": 12000,
    "require_neutral_end": true,
    "stop_on_service_error": true
  }
}

在这里插入图片描述

图 1 启元 Q1 动作剧本的 JSON 配置。剧本依次执行招手、双手比心和默认姿态复位,并配置总时长与异常停止约束。

动作 ID 不是自行编造的数字。根据启元 Q1 SDK 中 aimdk_msgs/mc/motion/McPresetMotion.msg 的定义,本项目使用的映射为:

官方符号ID本文用途
Q_BECKON3001招手欢迎
Q_MAKE_HEART3007双手比心
DEFAULT_POS0恢复默认姿态

配置驱动的价值在于:业务人员可以调整动作顺序与时长,而校验规则仍由程序统一执行。JSON 不是直接下发给机器人,而是先经过编译和安全检查。

4. 在动作调用之前建立安全契约

校验器中的核心白名单只包含本项目实际使用的三个动作:

OFFICIAL_Q1_MOTIONS = {
    0: "DEFAULT_POS",
    3001: "Q_BECKON",
    3007: "Q_MAKE_HEART",
}

MIN_STEP_MS = 500
MAX_STEP_MS = 6000

随后逐项检查 motion_id、动作名称和时长,并计算累计时间线:

if motion_id not in OFFICIAL_Q1_MOTIONS:
    errors.append(
        f"steps[{index}].motion_id={motion_id!r} 不在 Q1 动作白名单"
    )

if not isinstance(duration_ms, int) or not MIN_STEP_MS <= duration_ms <= MAX_STEP_MS:
    errors.append(
        f"steps[{index}].duration_ms 必须在 {MIN_STEP_MS}..{MAX_STEP_MS} ms"
    )

最后再执行两项剧本级约束:累计时长不得超过配置上限;启用 require_neutral_end 后,最后一步必须为 motion_id=0。

在项目目录执行:

$env:PYTHONIOENCODING='utf-8'
python .\validate_scene.py

真实运行得到三段连续时间线,总时长为 7600 ms,白名单、时长和安全复位检查均通过。

在这里插入图片描述

图 2 动作剧本静态校验结果。三个步骤均通过动作白名单、单步时长和中立姿态结尾检查。

这里的 duration_ms 是剧本的调度等待时间,不等同于机器人控制器内部每个轨迹点的持续时间。真机接入时仍需结合动作完成状态、服务返回值和现场安全策略,不能仅依赖固定 sleep。

5. 把剧本接到 Q1 预设动作服务

Q1 SDK 提供 SetMcPresetMotion 服务,官方接口名称为:

/aimdk_5Fmsgs/srv/SetMcPresetMotion

运行时入口创建 ROS 2 客户端,并把配置中的动作转换为官方预设动作请求:

from aimdk_msgs.srv import SetMcPresetMotion

self.client = self.create_client(
    SetMcPresetMotion,
    "/aimdk_5Fmsgs/srv/SetMcPresetMotion"
)

request = SetMcPresetMotion.Request()
request.source = "q1_action_storyboard"
request.interrupt = True

if motion_id == 3001:
    request.motion.value = 3001  # Q_BECKON
elif motion_id == 3007:
    request.motion.value = 3007  # Q_MAKE_HEART
elif motion_id == 0:
    request.motion.value = 0     # DEFAULT_POS
else:
    return False

interrupt=True 表示新请求允许中断当前动作。它并不意味着可以忽略安全条件:官方接口说明指出,预设动作需要在全身控制模式 BIPED_WHOLE_BODY_CTRL 下调用。本文没有实体 Q1,因此没有执行控制模式切换和真实 ROS 2 服务调用;这里只完成了符合 SDK 接口定义的运行时入口与 PrimeConsole 可识别的技能包。

6. 不只展示成功:用自动化测试验证失败路径

为了避免“能跑一次就算完成”,项目增加了 5 个自动化测试:

  1. 单步时长越界必须被拒绝;
  2. 剧本必须以默认姿态结束;
  3. pkg.yaml 必须包含正确的技能身份信息;
  4. 未知动作 ID 必须被拒绝;
  5. 正常剧本应编译为 3 个步骤。

执行命令:

python -m unittest discover -s tests -v

在这里插入图片描述

图 3 测试覆盖时长越界、默认姿态结尾、技能包身份、未知动作拦截和正常剧本编译,5 项测试全部通过。

仅有成功截图不能证明保护逻辑真的工作,因此我又准备了一个包含 motion_id=9999 的非法剧本:

python .\validate_scene.py .\tests\fixtures\invalid_unknown_motion.json

在这里插入图片描述

图 4 非法动作注入测试。校验器同时识别出未知动作 ID 和缺少默认姿态结尾,并以非零状态退出。

这张失败结果不是程序故障,而是预期行为。它说明错误配置在生成技能包之前就被阻断,没有进入仿真或机器人执行阶段。

7. 构建可重复的 RPK 技能包

RPK 本质上是具有固定目录结构的压缩包。本项目写入以下四个文件:

cfg/compiled_scene.json
cfg/pkg.yaml
main.py
README.md

打包程序还会检查是否遗漏必要文件,以及是否错误地多嵌套了一层 skill/ 目录。为了让相同源码产生相同二进制结果,构建脚本固定了 ZIP 条目的时间戳、权限和文件排序:

for path in sorted(SKILL_DIR.rglob("*")):
    arcname = path.relative_to(SKILL_DIR).as_posix()
    info = zipfile.ZipInfo(arcname, date_time=(2026, 1, 1, 0, 0, 0))
    info.compress_type = zipfile.ZIP_DEFLATED
    info.external_attr = 0o644 << 16
    archive.writestr(info, path.read_bytes())

执行:

python .\build.py

在这里插入图片描述

图 5 RPK 构建结果。技能包共包含 3 个剧本步骤,文件大小为 2613 bytes,并输出 SHA-256 摘要。

本次产物数据如下:

指标结果
文件q1_action_storyboard.rpk
大小2613 bytes
SHA-2562e2f9b6968f577d7d92ad5413fe3569138581701ff6caa1656555eee0e96ecb7
连续两次构建哈希一致

固定时间戳的目的不是伪装构建时间,而是排除 ZIP 元数据变化,便于在代码评审、发布和回滚时确认技能包是否由同一份源码产生。

8. 导入 PrimeConsole 并完成本地仿真

在 PrimeConsole 的“Skill 验证”页面选择“导入本地 Skill”,载入刚生成的 .rpk。软件成功解析出:

  • Skill:q1_action_storyboard;
  • 版本:v1.0.0;
  • 原子能力:预设动作;
  • 入口:prime.motion.preset;
  • ID:motion.preset。

在这里插入图片描述

图 6 PrimeConsole 成功导入本地 RPK 技能包,并解析出技能身份与预设动作原子能力。

点击“开始模拟仿真”后,Q1 本地模型首先执行招手:

在这里插入图片描述

图 7 PrimeConsole 正在执行第 1 个预设动作“招手”。页面同时显示本地驱动模型和未连接真机状态。

随后切换到第 2 个动作“双手比心”:

在这里插入图片描述

图 8 PrimeConsole 正在执行第 2 个预设动作“双手比心”。

最后模型恢复默认姿态,界面显示“仿真播放完成(共 2 个预设动作)”。

在这里插入图片描述

图 9 两个预设动作播放完成后,Q1 模型恢复默认姿态。

这里有一个容易产生疑问的细节:JSON 明明配置了 3 个步骤,为什么 PrimeConsole 只显示 2 个预设动作?原因是 3001 和 3007 是可播放动作,motion_id=0 用于安全复位,模拟器不把它计入动作数量。因此,“3 步剧本”和“2 个预设动作”并不矛盾。

验证报告区域仍显示“暂无验证记录”。这说明本地模拟播放完成不会自动生成正式验证报告,不能把该页面描述为“平台验证报告通过”。本文能够确认的是 RPK 成功解析、两个上肢动作成功播放、默认姿态成功恢复。

9. 结果、局限与下一步

这次实践完成了从配置到仿真的闭环:

  • 动作 ID 来自 Q1 SDK 协议定义,而不是自行猜测;
  • JSON 剧本与 ROS 2 运行时解耦;
  • 正常配置经过静态校验和 5 项自动化测试;
  • 非法动作能够在打包前被阻断;
  • RPK 可以确定性构建,两次产物哈希一致;
  • PrimeConsole 成功解析技能并播放招手、双手比心;
  • 播放结束后恢复默认姿态。

如果后续接入真机,建议先在隔离区域完成低风险动作测试,并将固定等待改为“服务返回值 + 动作状态 + 超时”的联合判断;任何预设动作都应在确认机器人型号、控制模式、周边空间和急停能力后执行。

这次项目带来的启发不是“没有机器人也能完成所有机器人开发”,而是:在真机到位之前,可以先把配置格式、接口映射、失败保护、测试和技能包构建做扎实。这样进入真机阶段时,调试重点会更集中,也更容易定位问题究竟来自业务逻辑、SDK 接口还是物理系统。

更多启元机器人 SDK、工具和社区资料可访问启元机器人开发者社区:

https://dev.primebot.com/


Logo

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

更多推荐