文章目录
- 先说结论
- 一、物理拓扑:三块板子,一个网段
- 二、aimdk_msgs:一包消息定义,四层语义
- 2.1 命名空间:`/aima` 之下三段式
- 2.2 一条完整的调用链长什么样
- 三、运控层:一台会拒绝你的状态机
- 3.1 动作是一台状态机,不是一个函数
- 3.2 两条时序红线
- 3.3 输入源仲裁:为什么你的技能抢不到控制权
- 3.4 一次动作切换的时序长什么样
- 四、感知通路:一个服务,一条流
- 4.1 “1 fps”是怎么来的
- 五、交互层:为什么“表情”是服务,“触摸”是消息
- 5.1 为什么“触摸”是最可靠的确认通道
- 六、PrimeConsole:它不是遥控器,是 IDE
- 七、从技能到包:`.rpk` 的结构
- 7.1 一个技能的完整生命周期
- 八、这套架构的三个优点,三个缺口
- 8.1 为什么不用“私有 RPC + SDK 封装”
- 8.2 四道安全闸,缺一道都危险
- 九、给开发者的阅读与上机顺序
- 9.1 五个“文档没写但要命”的细节
- 结语
判断一台机器人“能不能二次开发”,不看发布会上说了什么,看它 SDK 目录里有什么。
。
先说结论
启元的软件栈可以压缩成一句话:一套 ROS2 + FastDDS 的去中心化节点图,按“运控 / 硬件抽象 / 交互 / Agent”四层组织消息,任何人都能在 PC、运控板或大脑板上挂一个节点,用同一套接口访问全部能力。
这个设计有三个直接后果,后面会逐一验证:
- 部署位置自由。程序跑在哪儿不重要,只要能连上同一个 DDS 域。
- 接口即权限。你能调哪些服务,就等于你被允许动哪些硬件——没有额外的“权限层”。
- 能力缺口会立刻暴露。没有导航服务,就是没有;没有夹爪话题,就是没有。SDK 目录里找不到的东西,写代码时一定绕不过去。
一、物理拓扑:三块板子,一个网段
先看硬件怎么连,这决定了代码往哪儿放。
| 节点 | 地址(社区文档口径) | 角色 |
|---|---|---|
| 开发 PC | 10.1.1.99/24(有线网卡) | 跑你自己的 ROS2 节点、编译 SDK |
| 运控板 | 10.1.1.100 | 关节、步态、动作状态机;SSH 用户run |
| 大脑板 | 10.1.1.101 | 算力密集任务、视觉模型、技能常驻进程 |
这三个地址来自社区开源技能 carry-clothes 的实测记录,不是官方文档的正式命名,但足以说明架构:运控板管“身体”,大脑板管“脑子”,两者用同一条 DDS 总线通信。
这个分工解释了 SDK 目录里两个容易混淆的词:mc(motion control,运控)和hal(hardware abstraction layer,硬件抽象)。前者是“让机器人怎么动”,后者是“让机器人看见、听见、说话、亮灯”。
官方 README 给出的 SDK 目录树:agent/hal/interaction/mc四层,外加common与config两个支撑层。看一个 SDK 的分层,比看它的功能列表更能判断它能不能长期用。 另外注意三份文档的分工:README.md讲结构、快速入门讲环境、接口说明讲参数。
先看物理层。这套栈里最容易被忽略的事实是:三块板子不是“主从”关系,而是同一个 DDS 域里的对等节点。这决定了一件很实用的事——你的代码写在哪儿都行。
三块板子的拓扑。开发 PC、运控板、大脑板各跑各的节点,靠 DDS 互相发现。所以“部署位置”不是架构问题,只是网络配置问题;真正要小心的是把实时任务和通用任务混在一台机器上。
二、aimdk_msgs:一包消息定义,四层语义
官方 README 给出的目录树,是理解这套架构最直接的材料:
primebot_sdk/ ├── README.md ├── PrimeBot_SDK快速入门与集成指南.md ├── 接口说明.md ├── 友好用户使用声明.md ├── aimdk_msgs/ │ ├── agent/ # AI Agent 层:ASR 语音识别结果 │ ├── config/ # SDK 默认 FastDDS 通信配置 │ ├── env-hooks/ # colcon setup 自动加载的环境钩子 │ ├── common/ # 任务响应与基础类型定义 │ ├── hal/ # 硬件抽象层 │ │ ├── audio/ # 音频采集/播放 │ │ ├── camera/ # 相机图像/RTSP 流 │ │ ├── msg/ # BMS / 触摸 / 关节 等状态 │ │ └── srv/ # 灯带控制 等服务 │ ├── interaction/ # 交互层 │ │ ├── msg/ # TTS 请求/响应 / 优先级 │ │ └── srv/ # 表情播放 / TTS 语音播报 │ └── mc/ # 运控层 │ ├── action/ # 动作定义 / 动作状态 / 动作指令 │ └── motion/ # 运动速度 / 预设动作 / 自定义关节指令 └── examples/ # cpp / python / opencv / ruckig 例程这张树里有两个细节值得停留。
细节一:agent只放 ASR。语音识别没有归到hal/audio下,而是单独开了一层。这是个语义上的判断——麦克风采样是“硬件”,把声波变成文字是“理解”。放在 agent 层,等于承认“语音转文字”属于智能层,不属于驱动层。这个划法在未来接大模型时是加分项:换 ASR 引擎不用动硬件抽象。
细节二:common是单独一层。里面是“任务响应与基础类型定义”,说白了就是统一的Response{code, msg}+ 统一的 token 结构。全 SDK 的错误码、来源校验都靠它。一个 SDK 有没有认真做,就看它敢不敢统一错误码——各家自己编一套-1的,通常维护三年就烂了。
2.1 命名空间:/aima之下三段式
接口路径的实际前缀是/aima,往下按层切:
| 前缀 | 归属 | 典型内容 |
|---|---|---|
/aima/mc/... | 运控 | 动作切换、速度指令、上肢关节指令、动作状态 |
/aima/hal/... | 硬件抽象 | camera/CaptureJpegImage、touch/state、BMS、灯带 |
/aima/interaction/... | 交互 | TTS 播报、表情播放、优先级 |
/aima/agent/... | 智能 | ASR 结果 |
三段式命名带来的好处是可发现性:你不用背接口表,ros2 service list | grep /aima/mc就能知道自己能干什么。这也意味着——想知道一台启元机器人有什么能力,列一次服务列表就够了。
2.2 一条完整的调用链长什么样
把四层串起来看,一次“让它走过来抱衣服”的动作在软件上发生了什么:
- agent 层:麦克风阵列拾音,ASR 出文本,得到“把衣服拿到洗衣机旁”。
- interaction 层:TTS 回一句“好的,我来了”,表情切到“开心”,给用户一个已接收的反馈。
- mc 层:查询当前动作状态,从
100 RUNNING切到300 BIPED_LOCOMOTION_WBC,等状态回落到 100,然后以 ≥50 Hz 持续发布速度指令。 - hal 层:相机按需抓拍,服务返回 JPEG + 元信息,视觉模块算出目标在画面里的偏移,回喂给第 3 步的速度指令。
这条链最有意思的地方是第 2 步和第 4 步的对称性:一个往人身上走(TTS/表情),一个往机器身上走(相机/关节)。四层不是按“技术栈”切的,是按“跟谁打交道”切的——跟人打交道归 interaction,跟硬件打交道归 hal,跟身体运动打交道归 mc,跟语义理解打交道归 agent。
理解了这条切法,你就能预判:想加一个“挥手打招呼”,改的是 mc(预设动作)+ interaction(TTS);想加一个“认人脸”,改的是 hal(相机)+ agent(识别)。
把消息包拆开看,会发现它的分层依据不是“技术栈”,而是“跟谁打交道”。这个划分方式直接决定了你写技能时该去哪一层找接口。
四层加两个支撑层。agent只放 ASR 结果这件事值得单独说:语音识别属于理解,不属于驱动,所以它没有被塞进hal/audio。换 ASR 引擎不用动硬件抽象层,这就是分层带来的自由度。
三、运控层:一台会拒绝你的状态机
运控是整套架构里最“有脾气”的部分。社区技能 README 里记下了几条硬约束,每一条都是踩坑换来的:
3.1 动作是一台状态机,不是一个函数
动作切换有个明确的编号体系(社区文档口径):
| 编号 | 名称 | 含义 |
|---|---|---|
| 100 | RUNNING | 已进入稳定运行态 |
| 200 | TRANSITION | 切换中,正在补中间状态 |
| 300 | BIPED_LOCOMOTION_WBC | 双足/轮足全身控制(走跑) |
| 302 | BIPED_RECORD_UPPER | 上肢录制模式(零力矩,可手扳) |
关键规则:下发走跑指令前,必须确认状态已经从 200 落到 100。在TRANSITION里发速度指令,机器人不会理你,或者直接报错。这不是“容错没做好”,而是有意为之——动作切换过程中关节正在从一套控制律交接到另一套,此时插入外部指令,等于在一个正在换挡的变速箱里塞一根棍子。
理解了分层,再看运控层这台状态机。它最容易被误解的地方是:TRANSITION期间不接受指令,这不是 bug,是刻意的排他窗口。
状态机与三条硬约束。200 是一个“换挡期”:关节正在从一套控制律交接到另一套,此时插外部指令,机械上不一定坏,软件上一定乱。正确写法是下发后每 50 ms 轮询,等到 100 再起速度环。
3.2 两条时序红线
SetMcAction调用间隔 < 500 ms → 返回-10001(请求过于频繁)。社区技能把TaskConfig.set_action_interval_s定成 0.6 s,就是为了留余量。这类限流在服务端做,说明设计者不信任调用方。- 走跑控制要求 ≥50 Hz 持续发布,单一信源中断超过 1000 ms 直接脱锁停机。这是典型的**看门狗(watchdog)**设计:控制指令是“心跳”,不是“命令”。发一帧就去干别的,机器人会认为你掉线了,主动停下来。
第二条对写技能的人是致命的。它意味着“发个速度指令然后阻塞等视觉结果”这种写法根本不能上真机——你必须开一个独立线程持续发,且这个线程不能因为 GC、IO、模型推理被卡住 1 秒。
3.3 输入源仲裁:为什么你的技能抢不到控制权
速度指令不是“谁发谁算”,而是按信源优先级仲裁:
| 信源 | 优先级 |
|---|---|
rc/pnc/interaction | 80 级 |
| SDK 二次开发 | 39–20 级 |
| 社区技能(如 carry-clothes) | 30 级 |
优先级低意味着:App 或遥控器正在操作时,你的技能拿不到控制权。正确做法是注册输入源后先调GetCurrentInputSource校验,被占用就明确中止并告诉用户“谁在占用”,而不是闷头发指令、看起来“没反应”。
这套“优先级 + 仲裁 + 看门狗”的组合,是机器人软件里最像汽车电子的部分:任何时刻只有一个主子,且主子必须持续证明自己还活着。
3.4 一次动作切换的时序长什么样
把 3.1–3.3 的规则串起来,一次“从站立切到走跑”在时间轴上大致是这样:
| 时刻 | 动作 | 依据 |
|---|---|---|
| t0 | 查询当前动作状态 | 必须先读后写 |
| t0+5 ms | 下发SetMcAction(300) | 距上次调用 ≥500 ms,否则 -10001 |
| t0+5 ms ~ t0+800 ms | 状态停在200 TRANSITION,关节在换控制律 | 此时任何速度指令都会被忽略 |
| t0+800 ms | 状态落到100 RUNNING | 每 50 ms 轮询一次,最多等 10 s |
| t0+805 ms | 启动 50 Hz 速度发布线程 | 中断 >1000 ms 会脱锁停机 |
| 任意时刻 | 上位机/遥控器抢控制权 | 优先级 80 > 技能 30,技能必须让位 |
这张表解释了一个新手最常见的困惑:“我发了指令,机器人没反应,但也没报错。”大概率是你正好卡在200 TRANSITION那 800 毫秒里。
所以正确的写法不是“发完就等”,而是“发完 → 轮询到 RUNNING → 再发速度”。多写这五行轮询,能省掉一整天的怀疑人生。
四、感知通路:一个服务,一条流
视觉只有两个入口,这个设计非常克制:
| 入口 | 形式 | 频率建议 | 适合场景 |
|---|---|---|---|
/aima/hal/camera/CaptureJpegImage | ROS2 Service,请求-响应 | 约 1 fps,一次响应结束后再发下一次 | 按需抓拍、存档、单帧识别 |
rtsp://<IP>:2554/live_<camera_id> | RTSP 流 | 连续 | 预览、实时推理、录像 |
单帧服务的响应体不是一张裸图,而是一个JpegImage包装:camera_id、width/height、exposure、gain,图像本体用sensor_msgs/CompressedImage承载。
为什么要带元信息?因为同一段识别代码,可能今天收到 1280×720、曝光 50 的图,明天收到 640×480、曝光 200 的图。没有这些字段,你无法解释“为什么同一个模型昨天准、今天不准”。工程上,把元信息随图存下来,等于给每次失败留了证据。
RTSP 那条路的坑在客户端:OpenCV 的VideoCapture优先用 FFmpeg 后端,失败再退默认后端;read()一次调用里同时完成取帧、解码、返回图像。于是“模型接在哪”非常明确——read()返回有效 frame 之后。
但这里有个几乎所有人都会犯的错:把耗时模型直接塞进读帧循环。结果是读一帧、推一遍、写一次盘、再读下一帧,模型越慢,取流越卡,最后帧率被模型拖到个位数。正确做法是“取流与消费分离”:一个线程只管把最新帧放进有界缓存,推理线程取走最新的一帧,慢就丢帧。实时系统里,丢帧永远优于积压。
4.1 “1 fps”是怎么来的
文档建议单帧抓拍按约 1 fps,且要“一次响应结束后再发下一次”。这不是性能上限,是排队成本的考虑。
一次抓拍的完整链路是:请求进服务 → 等服务端拿到一帧新数据(曝光 + 读出 + JPEG 编码)→ 序列化 → 走 DDS → 客户端反序列化。任何一环变慢,请求就在队列里等着。服务端默认等待新帧的时间是 1000 ms,客户端还有自己的 RPC 超时——两层超时,任意一层触发都表现为“调用失败”。
如果你按 30 fps 去刷这个服务,会发生两件坏事:请求在队列里堆积,越等越久;相机被频繁占用,其它模块(比如视觉识别、录像)拿不到图。
想看实时画面请走 RTSP,那是为连续流设计的;抓拍服务是为“我要一张确定的图”设计的。用错入口,怎么调参数都不顺。
感知层只有两个入口,但这两个入口的服务对象完全不同:一个要“确定的这一张”,一个要“连续的那一帧”。混用就会出现各种玄学问题。
两条视觉通路。抓拍是请求—响应,适合存档与单帧识别;RTSP 是持续流,适合预览与实时推理。最容易犯的错是把耗时模型塞进取帧循环,结果取流被推理拖慢,缓冲区越积越旧。
五、交互层:为什么“表情”是服务,“触摸”是消息
这一层有个很能说明设计思路的区分:
- 服务(srv):TTS 播报、表情播放、灯带控制。特点是“我要你做一件事,并告诉我结果”。
- 消息(msg):触摸状态、BMS 电量、关节状态。特点是“状态一直在变,谁关心谁订阅”。
这不是随意划分,而是通信语义的必然:请求-响应适合一次性动作,发布-订阅适合持续状态。
顺带说一个真机上的现实约束:启元的能力清单里没有 ASR 服务(语音识别在 agent 层,属于可选能力)。所以社区技能用“头部触摸”当确认通道——用户摸一下头,表示“我放好了”。这是个很聪明的降级:没有语音识别,就用触觉;没有视觉,就用超时。做技能的人必须学会这种“能力降级链”的思维。
5.1 为什么“触摸”是最可靠的确认通道
交互层里最不起眼的接口是触摸,但它在真机上往往是唯一稳定的输入。
原因有三层。第一,带宽极低但语义明确。一次触摸只能表达“是/否”,恰好覆盖了技能里 90% 的确认需求(开始、继续、找到了、停下)。第二,不依赖声学环境。语音识别在混响、多人说话、电视背景音下会掉点,触摸不会。第三,抗遮挡。人站在机器人侧后方说话,麦克风阵列可能定位不准;伸手摸一下头,一定会被触到。
所以社区技能用“摸头”当确认,不是偷懒,是在能力约束下选了最稳的那条路。做产品的人要习惯这种取舍:不是用最好的技术,是用最不容易出错的技术。
六、PrimeConsole:它不是遥控器,是 IDE
上半部分是 PrimeConsole 的六个模块,下半部分是资料包清单:Q1 机器人 SDK v1.0.0.0(13 MB)、T1 机器人 SDK v1.0.0.0(13 MB)、ARM 原生编译环境(4.8 MB,支持 Orin 与 RK 平台)。六项功能不是堆叠,是调试顺序——先确认在线,再看画面,再验运动,最后才整包验证;而 13 MB 的 SDK 里装的就是aimdk_msgs与示例代码。
官方列出的六个模块,恰好对应开发者的六件事:
| 模块 | 解决什么 |
|---|---|
| 连接状态与日志 | 设备在不在线、通信正不正常 —— 排障第一现场 |
| 实时视频流监控 | 相机画面与传输状态 —— 感知类问题的眼睛 |
| 语音与运动控制 | 方向控制、急停、复位 —— 验证运动响应与安全策略 |
| 机器人仿真 | 虚拟全身关节运动,提前验证轨迹 —— 降低真机风险 |
| 动作与表情测试 | 挥手、握手、舞蹈、表情预设 —— 检查表现是否符合预期 |
| Skill 验证与 AI 中心 | 验证 Skill、管理模型与算力 —— 从“能跑”到“能装” |
注意第四项和第六项的组合:先仿真、再真机;先单点验证、再整包验证。这不是功能列表,是一条工作流。一个成熟的机器人工具链,价值不在于功能多,而在于它把“正确的调试顺序”固化进了界面。
七、从技能到包:.rpk的结构
技能最终要变成一个能安装、能卸载、能声明能力的包。社区技能 carry-clothes 的包结构可以作为参考(官方.rpkschema 尚未公开,字段命名按通用机器人技能包惯例):
carry-clothes-2.1.0.rpk ├── manifest.json # 清单:capabilities 写真实 ROS2 接口路径 ├── main.py # 入口 ├── SKILL.md / README.md # 技能说明 ├── src/ # primebot_iface / task / poses / vision ├── tools/teach_record.py # 位姿标定工具 ├── config/task_config.yaml ├── assets/icon.png └── META-INF/checksums.json最有意思的是打包器的反向能力校验:逐个检查manifest.json里声明的 ROS2 接口,在代码里是否真的有对应实现符号,漏了直接报错。
PrimeHub 的技能卡片:视频电话、拿衣服、天气预报、PrimeBuddy。每张卡都标了能力范围与安装入口,这是“机器人能力像软件一样流通”最朴素的产品形态——没有它,每个技能都得靠文档和 QQ 群分发。
这个“声明即契约”的做法值得所有做技能生态的团队抄一遍:能力不能靠文档对齐,要靠构建期校验。声明了没实现——打包就挂;实现了没声明——权限就下不来。两边一夹,清单和代码就不会走偏。
7.1 一个技能的完整生命周期
把散落各处的线索拼起来,一个技能从想法到上架大概是六步:
| 阶段 | 做什么 | 落在哪个工具上 |
|---|---|---|
| 1. 找能力 | 列/aima接口,确认要用的东西真存在 | ROS2 CLI / 《接口说明.md》 |
| 2. 写流程 | 状态机 + 位姿 + 视觉,先用--dry-run打印计划 | 本地 Python |
| 3. 仿真 | 无真机跑全流程,验证时序与分支 | PrimeConsole 仿真 /--sim |
| 4. 标定 | 真机上手动掰姿态、录制、回放、取角度 | 拖动示教 +teach_record.py |
| 5. 上机 | 切开发者模式、调内核缓冲、连真机调试 | PC + 机器人同网段 |
| 6. 打包发布 | 生成.rpk、能力自检、发布到 PrimeHub | 打包脚本 + 技能市场 |
这一步里第 4 阶段最容易被跳过,也最容易翻车。纸面上算出来的关节角,和真机上能托住衣服的关节角,往往差 10–20 度。官方给出的解法是物理拖动示教:切到零力矩录制模式(302),用手把手臂掰到想要的姿态,回放,取末帧角度,粘进配置。这套流程听着原始,但它把“不可言说的手感”变成了可复现的数字。
八、这套架构的三个优点,三个缺口
优点:
- 扁平。没有网关、没有中间件服务、没有私有协议栈。ROS2 + FastDDS 就是全部,开发者已有的 ROS 知识直接复用,学习成本接近零。
- 位置无关。同一个节点跑在 PC 上调试、跑在大脑板上常驻,代码不用改,只改
ROS_DOMAIN_ID和网络配置。 - 接口即能力。
ros2 service list就是能力清单,缺什么一目了然,不用猜、不用试。
缺口(截至 2026 年 10 月的公开资料):
| 缺口 | 影响 | 社区现状的替代方案 |
|---|---|---|
| 无 SLAM / 导航 / 语义地图 | 不能“导航到某点” | 视觉伺服闭环,用相机把误差吃掉 |
| 无夹爪 / 末端执行器接口 | 不能抓取 | 上肢关节插值,双臂合拢托住 |
| 无 ASR 服务 | 听不懂口令确认 | 头部触摸 / 超时兜底 |
| 无末端力 / 负载传感 | 感知不到重量 | 视觉判据 + 触摸 + 超时三件套 |
这份缺口清单不是黑稿,恰恰相反——它是官方接口诚实的证据。一个 SDK 如果什么都“有”,通常意味着它把没实现的东西也写进了文档,那才是灾难。
8.1 为什么不用“私有 RPC + SDK 封装”
很多机器人厂商的做法是:底层私有协议,外面包一层 C++/Python SDK,开发者只能调函数。启元选了更“裸”的路——直接把 ROS2 接口暴露出来。
| 维度 | 私有 RPC + 封装 | 原生 ROS2 接口 |
|---|---|---|
| 上手成本 | 学一套新 API | 复用已有 ROS 知识,几乎为零 |
| 调试手段 | 只能靠日志和 SDK 打点 | ros2 topic echo/rqt/rviz全套现成 |
| 生态兼容 | 自成孤岛 | 能直接接 Nav2、MoveIt、Foxglove 等 |
| 可控性 | 高(能锁能力) | 低(接口一旦公开就收不回) |
| 责任边界 | 清晰 | 需要靠优先级、限流、校验来兜 |
选第二条路,等于把“接口设计”这件事一次做到位,代价是没有回头路:一个接口名一旦被一百个技能引用,就再也不能改。
从这个角度看,aimdk_msgs里那个common层(统一响应 + 统一 token)才是整套设计里最关键的一块——它决定了这套接口能不能撑过三年。各家自己编错误码的 SDK,通常第三年就开始出现“同一个 -1 有七种含义”的问题。
8.2 四道安全闸,缺一道都危险
这套架构里,安全不是单独一个模块,是四层各管一段:
| 闸 | 位置 | 拦什么 | 失效后果 |
|---|---|---|---|
| ① 优先级仲裁 | 运控层 | 多信源抢控制权 | 两套指令打架,机器人抽搐 |
| ② 调用限流 | 运控层(500 ms) | 指令风暴 | -10001,或被高频指令带乱节拍 |
| ③ 看门狗 | 运控层(1000 ms) | 上位机崩溃/断连 | 机器人带着最后一条速度指令一直走 |
| ④ 力控/急停 | 驱动与硬件 | 碰撞、堵转 | 伤人、烧电机 |
值得注意的是第 ③ 道——它把"控制"变成了"心跳"。这个设计在工业机器人里很常见(示教器松开就停),在消费级产品上尤其重要:用户的笔记本随时可能休眠、Wi-Fi 随时可能掉,机器人不能因为上位机睡着就继续往前走。
第 ④ 道则说明一件事:最终的兜底永远在电流环,不在应用层。你写的任何保护逻辑,都比不上驱动器的过流保护快。
九、给开发者的阅读与上机顺序
按这套架构,我建议的顺序是:
- 先列接口:连上机器人,
ros2 node list/ros2 service list/ros2 topic list,把/aima开头的全部抄一遍。这一步比读文档快。 - 再读接口说明:对着抄下来的清单看文档,重点看参数单位(弧度还是角度、米还是毫米)和错误码。
- 切开发者模式:SSH 进
10.1.1.100,走“开发者功能”开关,重启。不开这个开关,网口根本连不上。 - 调内核缓冲:
net.core.rmem_max/wmem_max调到 24 MB 级别,否则高频 topic 会丢帧——这个坑几乎人人踩一次。 - 先仿真后真机:PrimeConsole 的仿真模块和
--sim参数先用起来,把 10 步流程跑通再上机。 - 打包前做能力自检:manifest 里写的每个接口,都要能在代码里指出实现位置。
9.1 五个“文档没写但要命”的细节
- 单位是弧度,不是角度。关节指令、位姿数组全部用 rad。传度数不会报错,只会让机器人摆出一个很怪的姿势。
- 关节顺序是写死的。官方示例里上肢按固定顺序给 6 个数,顺序错了不报错,姿态全歪。
camera_id拼错不会提示"相机不存在"。通常表现为超时,很容易被误判成网络问题。- 两层超时要分开调。请求里的
timeout_ms管"等新帧",客户端的等待管"等 RPC"。调错层,白折腾。 ROS_DOMAIN_ID不一致,节点互相看不见。多机调试时先对一下这个环境变量,比查网络快。
carry-clothes的 README 直接把“缺什么”列成表:没有 SLAM/导航、没有夹爪、没有 ASR、没有末端力传感,并给出每一项的替代方案。在开源技能里看到这种表格,说明作者真的上过机。
结语
把架构拆完,回头看那个最初的问题:“这台机器人能不能二次开发?”
答案不在参数表里,而在这三件事上:接口是否分层清晰、部署是否位置无关、能力边界是否诚实。启元这套栈在前两点做得干净,第三点做得罕见地坦白——它把“没有导航”“没有夹爪”直接摆在社区技能里让人看见。
对开发者来说,这种坦白其实是好消息:你不需要猜它能干什么,你只需要决定在它的边界内做什么。
过来kk https://dev.primebot.com/