ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

我用启元 Q1 做了个「小事授勋官」:一句“今天的你值得一个大大的赞”

我用启元 Q1 做了个「小事授勋官」:一句“今天的你值得一个大大的赞”

文章目录

    • 授勋流程设计
    • 开发环境与实验条件
    • 项目结构与构建流程
      • 配置、模板与构建脚本
      • 配置校验与代码生成
      • 技能包结构
    • 剧情与灯效配置
    • SDK 接口与调用限制
      • 动作编号、状态检查与重试
      • TTS 请求与异步播报
      • LED 模式与参数
      • 表情编号与请求参数
    • 请求顺序与执行时序
    • 技能打包与离线预览
    • PrimeConsole 导入与仿真结果
    • 写在最后

大家好,我是颜颜yan_。

最近我在想,生活里有很多值得被肯定的小事。比如按时起床,完成一件拖延很久的任务,或者在不太顺利的一天里,坚持把事情做完。

这些努力常常悄悄发生,也很少得到掌声。

如果这时候有一个机器人向你招招手,认真地说一句“今天的你,值得一个大大的赞”,会不会开心一整天。

带着这个想法,我做了小事授勋官。

我为它安排了一场小小的授勋仪式。先招手邀请你,再为你的努力加油,接着双手比心,送出一枚**“平凡生活闪光者勋章”**,最后用庆祝动作收尾。

仪式在脑海里已经有了画面,到了动手这一步,才要面对一个实际问题——我手头没有实体 Q1。

那就先从电脑上开始。我用 Windows 编写配置和代码,对照 Q1 SDK 核对接口,再把它打包成 PrimeConsole 可以导入的技能包。

授勋流程设计

我想通过“小事授勋官”表达的,是小事,也值得郑重其事。

围绕这个想法,我安排了招手邀请、努力认证、颁发勋章和共同庆祝四个环节。

我最想保留的是颁勋时双手比心,配上那句“小事,也值得郑重其事”。前面的邀请和加油,都是在为这一刻做准备。

每个环节都安排了对应的动作和台词,再配上开心表情与灯效,让邀请、肯定和庆祝有各自的表达。

环节动作与编号配套台词灯效设计
授勋邀请招手,3001这里是 Malyu 小事授勋局,请接受今天的授勋。暖金呼吸
努力认证加油,3008经本局认证,今天的你,值得一个大大的赞。金色闪烁
颁发勋章双手比心,3007现授予你,平凡生活闪光者勋章。小事,也值得郑重其事。粉色呼吸
庆祝时刻跳舞动作 A,3046授勋完成!请带着这份肯定,继续闪闪发光。彩虹

这四个环节组成了当前版本的完整流程。

程序按顺序提交动作、台词、表情和灯效请求,台词使用表中预先写好的内容。

流程确定后,下一步就是准备开发环境,把这些设计落实到配置和代码中。

开发环境与实验条件

要在电脑上看到这段动作,得先把剧情做成技能包。

我把这次实践分为本地构建和模拟仿真两部分,先确认手头的工具能完成哪些工作。

下面是本次用到的工具和资料,也列出了后续真机联调需要的环境,有需要的小伙伴也可以参考呀。

软件或资料在本次实验中的用途当前情况
Windows 与 PowerShell管理项目、执行打包与离线预览已使用
Python 3.12.9 与项目.venv读取 JSON、生成 Python 和 YAML、封装.rpk已使用
代码编辑器,例如 PyCharm编辑配置、模板与生成器,查看生成代码可使用现有编辑器
Q1 SDK v1.0.0.0核对动作、表情、灯带、TTS 和运行环境要求已用于接口核对
PrimeConsole导入技能包、匹配预设动作、查看仿真表现当前测试环境可呈现上肢动作
Ubuntu 22.04 与 ROS 2 Humble构建 SDK 消息并连接 Q1 服务真机联调环境,本次未验证
截图或录屏工具记录配置、代码、打包结果和仿真画面用于补充本文配图

本地构建可以先做起来。

打包脚本只使用argparse、json、shutil、zipfile和pathlib等 Python 标准库,手头的 Windows 环境就能生成.rpk、打印离线清单,可以先动手把剧情写进去。

后续连接实体 Q1、通过 SDK 调用机器人服务时,还需要准备相应的运行环境。

推荐在 Ubuntu 22.04 + ROS2 Humble 环境下进行开发,暂不支持在 Mac、Windows 系统下进行开发。
除开发 PC 外,机器人自带的运控板与大脑板均支持直接进行二次开发。

按照这个范围,这次我先在 Windows 上完成配置编写、代码生成、技能打包和仿真观察。

等到真机联调时,再按 SDK 的要求准备运行环境,验证服务调用与实际表现。

项目结构与构建流程

有了开发环境,我开始把前面那张授勋流程表写进程序。

比如“颁发勋章”这一段,需要把双手比心、第三句台词和粉色呼吸灯效对应起来,其他三个环节也要各自配好。于是,我把这些内容按环节放进 JSON,保留和流程表相同的顺序,方便逐段核对。

接着要处理的是,这些配置怎样变成实际调用。

四个环节都会用到动作、灯效、表情和播报接口,我把这部分调用逻辑放进执行模板,由 Python 脚本读取 JSON,将每段的动作编号、灯效参数和台词填进去,生成入口代码。

这样,以后想给勋章换一句台词,或者调整邀请环节的灯光,就可以回到对应的配置里修改,再重新生成。

要把它放进 PrimeConsole,还需要补上包名、作者和版本等信息。

这部分由元数据模板生成,最后和入口代码、剧情配置一起封装成q1_skin_award_officer.rpk。从流程表到可导入的技能包,几个文件就这样连了起来。

下面按这条流程介绍当前“小事授勋官”项目中的文件。

整理后的项目根目录见下图。

这里的dist/_build/award_officer/是方便检查的展开结果。

每次运行build.py时,脚本会先删除这个目录,再根据当前 JSON 重新生成它;.rpk也是从这个目录重新压缩出来的。

把这些步骤连起来,就是从源文件到 PrimeConsole 的完整流程。

1. skins/award_officer.json ↓ 读取剧情、动作、表情与灯效 2. build.py ↓ 校验 storyboard、台词索引、动作顺序和灯效参数 3. template/award_show.py.tpl + template/pkg.yaml.tpl ↓ 替换占位符,生成实际 Python 代码和 YAML 元数据 4. dist/_build/award_officer/ ├─ main.py ├─ cfg/pkg.yaml ├─ skin.json └─ README.md ↓ ZIP_DEFLATED 封装 5. dist/q1_skin_award_officer.rpk ↓ 导入、识别动作编号并匹配本地资源 6. PrimeConsole 模拟仿真

配置、模板与构建脚本

文件作用
[skins/award_officer.json](C:/Users/user/Downloads/q1-persona-skin-kit-master/skins/award_officer.json)唯一的业务配置。保存名称、包名、四句台词、四个动作编号、默认表情、默认灯效和四段storyboard。修改创意时优先编辑这里。
[template/award_show.py.tpl](C:/Users/user/Downloads/q1-persona-skin-kit-master/template/award_show.py.tpl)执行层模板。定义离线预览、ROS 节点、Q1 服务客户端、状态检查、动作忙碌重试、TTS/LED/表情请求和失败退出逻辑。
[template/pkg.yaml.tpl](C:/Users/user/Downloads/q1-persona-skin-kit-master/template/pkg.yaml.tpl)包描述模板。提供package_name、author、robot_model、version和sdk_version等元数据字段,其中作者固定为Malyu。
[build.py](C:/Users/user/Downloads/q1-persona-skin-kit-master/build.py)构建控制器。读取 JSON,校验配置,展开每段剧情,替换两个模板中的占位符,生成中间目录并压缩.rpk。
[sdk_q1-v1.0.0.0/](C:/Users/user/Downloads/q1-persona-skin-kit-master/sdk_q1-v1.0.0.0)能力依据。这里保存 Q1 的.srv/.msg定义、动作编号、表情编号、灯带参数和运行环境说明;它不是本项目的皮肤源代码。

award_officer.json负责回答“授勋官要表达什么”,award_show.py.tpl负责回答“怎样调用 SDK 表达”,而build.py负责把两者组合起来。

这样修改台词或灯效时不必直接改生成后的main.py;修改服务调用和错误处理时,也不会把剧情数据散落到代码里。

配置校验与代码生成

build.py的处理可以拆成五步:

  1. 读取skins/award_officer.json,确认配置使用profile: "award_show"。
  2. 检查storyboard非空,并确认其中的motion_id顺序正好是[3001, 3008, 3007, 3046]。
  3. 根据每段的line_index找出台词;没有单段led时,继承顶层的暖金默认灯效。
  4. 把每段展开成实际调用:set_preset_motion→set_led→set_emotion→speak。任一调用失败,生成代码就返回False,不继续下一段。
  5. 将渲染后的文件写入dist/_build/award_officer/,再把该目录压缩为dist/q1_skin_award_officer.rpk。

以图中的颁勋配置为例。

构建脚本会将它展开为下面这段main.py代码。

ifnotset_preset_motion(3007):returnFalseifnotset_led(254,(255,100,150),1200):returnFalseifnotset_emotion(3003):returnFalseifnotspeak('现授予你,平凡生活闪光者勋章。小事,也值得郑重其事。'):returnFalse

因此,JSON 是便于修改和校验的剧情层,生成后的main.py才是 PrimeConsole 静态扫描和 Q1 真机入口实际看到的执行层。

技能包结构

q1_skin_award_officer.rpk使用 ZIP 结构封装,当前包内有四类文件:

包内路径来源用途
main.pyaward_show.py.tpl渲染结果入口程序,包含显式动作编号和声光表情请求
cfg/pkg.yamlpkg.yaml.tpl渲染结果PrimeConsole 读取的包名、作者、机器人型号和版本信息
skin.json原始award_officer.json的复制品保留完整剧情配置,便于导入后检查
README.mdbuild.py根据当前配置生成记录四段剧情、仿真限制、本地预览和真机入口

构建目录方便检查生成结果,最终包用于导入。

构建目录中的文件是普通文件,可以直接打开调试。.rpk是把这些文件打包后的交付物,导入 PrimeConsole 时选择的是.rpk。

剧情与灯效配置

写配置时,我又把四个环节在脑海里过了一遍。招手邀请时,希望灯光温暖一些;到了比心颁勋,换成粉色;最后再用彩虹配合庆祝。

这些小安排,都要一项项写进配置,才有机会出现在之后的演示里。

这里的 JSON 可以理解为一份给程序看的分镜表。

我先在顶层写好默认的开心表情和暖金呼吸灯效,再按四个环节的顺序填入动作编号。这部分配置写成下面这样。

{"id":"award_officer","display_name":"小事授勋官","package_name":"q1_skin_award_officer","profile":"award_show","emotion_id":3003,"led":{"mode":254,"rgb":[255,220,160],"period":1600},"motions":[3001,3008,3007,3046]}

上面展示了配置中的部分字段。完整文件还通过lines保存四句台词,通过storyboard逐段记录动作、台词的对应关系,以及各环节需要调整的灯效,使用时需要保留完整配置。

写到“颁发勋章”这一段时,我把双手比心和第三句台词配在一起,再单独设置粉色呼吸灯效。

{"stage":"颁发勋章","motion_id":3007,"line_index":2,"meaning":"双手比心,把一枚心意勋章送给你。","led":{"mode":254,"rgb":[255,100,150],"period":1200}}

这里的motion_id: 3007对应双手比心,line_index: 2取出第三句授勋台词,因为台词索引从 0 开始。

这一段还单独写了led,将默认的暖金色调整为粉色。其他环节如果省略led,就沿用顶层的默认灯效。

四段内容写好后,构建脚本会先检查剧情是否为空、动作顺序是否与motions一致、台词索引是否有效,再检查 RGB、灯效模式和period的取值范围。

这样可以在打包前发现配置中的遗漏或填写错误。设备上是否有对应的动作、表情资源,以及实际播放效果如何,还需要在后续联调中逐项确认。

SDK 接口与调用限制

写分镜时,“招手之后说一句话,再接着比心”是一句很自然的描述。

落到程序里,还需要逐个确认。动作能不能接着发?台词请求返回时,机器人是不是已经说完?灯光参数里的时间,又到底表示什么?

尤其是颁勋那句话,我希望它能和比心动作配合好。为了把这些细节安排清楚,我对照本地.srv文件和《接口说明》,逐项核对动作、声音、灯光和表情的调用方式。下面保留了这次用到的接口与代码,方便有需要的小伙伴参考。

能力ROS 2 服务名当前用途
预设动作/aimdk_5Fmsgs/srv/SetMcPresetMotion招手、加油、比心、庆祝
状态查询/aimdk_5Fmsgs/srv/GetMcAction真机入口开始前检查 Q1 状态
文本播报/aimdk_5Fmsgs/srv/PlayTts提交四句授勋台词
灯带控制/aimdk_5Fmsgs/srv/LedStripCommand提交四段灯效配置
表情播放/aimdk_5Fmsgs/srv/PlayEmotion请求开心表情 3003

动作编号、状态检查与重试

我先在 Q1《接口说明》的动作表里核对了授勋流程用到的四个动作,编号和名称如下:

SDK 动作编号SDK 描述
3001招手
3008加油
3007双手比心
3046跳舞动作A

确定动作编号后,接着看怎样把它填进请求。预设动作使用SetMcPresetMotion接口,下面节选了.srv文件中的字段:

RequestHeader header string source McControlArea area McPresetMotion motion bool interrupt string ani_path --- CommonTaskResponse response

按照这个结构,我在真机入口代码中填入动作编号,并将interrupt设为False,让后续请求保留前一个动作的执行过程。

这部分请求构造如下:

request=SetMcPresetMotion.Request()request.header.stamp=NODE.get_clock().now().to_msg()request.source="sdk_rpc"request.motion.value=motion_id request.interrupt=Falserequest.ani_path=""

发送请求前,程序会先按 SDK 的要求,确认机器人处于BIPED_WHOLE_BODY_CTRL,并通过状态反馈确认RUNNING。当前程序会在条件满足后继续执行;条件尚未满足时就退出,留待准备好控制状态后再运行。

机器人状态满足要求后,还要考虑前后两个动作的衔接。前面设置了interrupt=False,如果上一个动作仍在执行,新请求就可能被拒绝。程序会根据返回的错误码处理。遇到REASON_TASK_RUNNING,就在限定时间内重试;遇到其他拒绝或超时,则停止后续请求。

这里需要留意,被拒绝的请求也可能返回state=RUNNING。因此,代码会结合错误码判断这次请求是否被接受,避免把机器人正在运行的状态误读为新动作已经开始。

除了动作能否接续执行,我还核对了请求中的area字段,确认它对运动范围有什么影响。

按照 SDK 的说明,area控制掩码仅供上层标识参考,系统根据动作文件中提取出的曲线驱动电机,运动范围由对应的动作资源决定。

TTS 请求与异步播报

动作请求安排好后,接下来是让 Q1 说出每个环节的授勋台词。

我用到的是PlayTts接口,它在PlayTts.srv中定义的请求与响应结构如下:

CommonRequest header PlayTtsRequest tts_req --- CommonResponse header PlayTtsResponse tts_resp

按照这个结构,我先创建PlayTts.Request(),再把台词和播报参数填进其中的tts_req。

当前代码中的请求构造如下:

request=PlayTts.Request()request.header.header.stamp=NODE.get_clock().now().to_msg()request.tts_req.text=text request.tts_req.priority_level.value=TtsPriorityLevel.INTERACTION_L6 request.tts_req.priority_weight=0request.tts_req.domain="q1_skin_award_officer"request.tts_req.trace_id=""request.tts_req.is_interrupted=False

这段代码用于真机入口,需要先加载 ROS 2 和 SDK,并创建节点,才能构造和发送请求。本次在 Windows 上完成了这部分代码编写,实际播报留到真机联调时验证。

台词提交后,还要考虑它与动作怎样衔接。

PlayTts使用异步调度。服务返回is_success: true时,播报请求已经进入队列,声音会由调度系统安排播放。台词何时说完,还需要单独判断。

LED 模式与参数

台词之外,我还给四个环节配了不同的灯效。

邀请时用暖金呼吸,认证时用金色闪烁,颁勋时换成粉色呼吸,最后用彩虹配合庆祝。

这些设计通过LedStripCommand接口提交,相关字段和模式常量在LedStripCommand.srv中定义如下:

CommonRequest request uint8 led_strip_mode uint8 LED_RAINBOW = 2 uint8 LED_CUSTOM_BREATH = 254 uint8 LED_CUSTOM_BLINK = 255 uint8 r uint8 g uint8 b uint16 period --- CommonResponse header

对应到配置中,每段需要填入灯效模式、RGB 颜色和时间参数period。四个环节的取值如下:

环节modeRGBperiod
授勋邀请254,自定义呼吸255、220、1601600 ms
努力认证255,自定义闪烁255、180、0500 ms
颁发勋章254,自定义呼吸255、100、1501200 ms
庆祝时刻2,内置彩虹0、0、0,占位值0,占位值

这里的period要结合灯效模式来理解。

邀请和颁勋使用自定义呼吸模式,1600 ms 和 1200 ms 分别表示灯光从最亮到熄灭的时间;认证环节使用自定义闪烁模式,500 ms 表示点亮或熄灭各自的保持时间。

到了庆祝环节,我选用内置彩虹模式,颜色变化由该模式处理。

配置中的 RGB 和period都填为 0 作为占位值,接口会忽略这些自定义参数,按内置彩虹效果执行。

表情编号与请求参数

配合授勋时的肯定和庆祝,我给四个环节都选了开心表情。

对照 Q1 的 Emotion 表,这个表情的编号是 3003。查编号时需要留意所属的资源表。表情表中的 3003 是“开心”,动作表中的 3003 则是“碰拳”,调用时要配合对应的服务使用。

确定表情后,我在PlayEmotion请求中填写type="emotion"、emotion_ids=[3003]和loop_count=1,请求播放一次开心表情。

请求顺序与执行时序

接口核对好后,我按剧情串起各项请求。以开场的授勋邀请为例:

# 1. 授勋邀请:招手邀请你接受今天的小事勋章。ifnotset_preset_motion(3001):returnFalseifnotset_led(254,(255,220,160),1600):returnFalseifnotset_emotion(3003):returnFalseifnotspeak('这里是 Malyu 小事授勋局,请接受今天的授勋。'):returnFalse

四个环节都按**“动作 → 灯效 → 表情 → 台词”**的顺序提交请求。动作编号直接写入生成代码,方便核对和 PrimeConsole 导入匹配。

程序逐项检查返回结果,**任何一个必要请求失败,就停止后续流程。**TTS 还会检查tts_resp.is_success。

程序通过参数选择离线预览或真机调用,直接运行则显示帮助:

入口实际行为是否调用机器人
不带参数显示帮助否
--preview打印动作、灯效、表情和台词清单否
--robot加载 ROS 2,检查状态并尝试调用 Q1 服务是,尚未实机验证

技能打包与离线预览

配置和模板准备好后,我在项目根目录执行打包命令:

.\.venv\Scripts\python.exe build.py

也可以显式指定授勋官配置,两条命令生成的技能包相同:

.\.venv\Scripts\python.exe build.py--skin award_officer

构建完成,目录里有了dist/q1_skin_award_officer.rpk。前面分散在配置和模板里的内容,到这里装进了同一个文件,可以带进 PrimeConsole 了。

导入前,我核对了包内文件和元数据。包名为q1_skin_award_officer,作者为Malyu,机器人型号为q1。

版本信息也一并记录下来。SDK 文件夹标为v1.0.0.0,包元数据的sdk_version为v1.0.0。后续联调时,还需要确认它们的兼容情况。

导入 PrimeConsole 前,我先运行离线预览,检查每段内容是否对应:

.\.venv\Scripts\python.exe dist\_build\award_officer\main.py--preview

终端按顺序打印出四段内容,我对照流程表核对了台词和灯效参数。接下来,就可以打开仿真窗口,看看这些编号对应的动作了。

PrimeConsole 导入与仿真结果

离线预览完成后,我打开 PrimeConsole 的 Skill 页面,导入刚生成的.rpk。接下来,我按页面提示匹配本地预设资源。代码引用的3001、3008、3007、3046,分别对应招手、加油、比心和庆祝。

匹配好资源后,我启动模拟仿真。

窗口里的 Q1 动起来了。

前面写了那么多编号和参数,到这里,视线终于可以从代码移到机器人身上。想着流程表里安排的招手和比心,再看窗口中的上肢动作,授勋官的形象具体了许多。

这次记录的是上肢动作仿真,台词和灯效仍保留在程序设计中。看着这个画面,也更容易想象以后那句授勋台词配上动作的样子。


写在最后

写到这里,我让“小事授勋官”,给这次动手做点东西的自己颁一枚勋章。

没有实体机器人,就先查资料、写配置,在仿真里看看动作。原先脑海里的那个小念头,经过这些具体的小事,终于留下了一段可以回看的画面。

我们做成很多事情的过程,大概也藏在这样一个个小步骤里。

也许有人终于交出了拖了很久的作业,有人鼓起勇气发出一条消息,也有人忙了一整天,终于坐下来好好吃了顿饭。每个人心里,都有一些值得为自己鼓掌的时刻。

生活里的小事,值得我们用一点仪式感,认真收藏。

希望有一天,一个恰好需要鼓励的人,也能听到这句写在程序里的台词。

今天的你,值得一个大大的赞。

如果今天也给自己颁一枚勋章,你想奖励自己完成的哪件小事?

欢迎在评论区留下你的“小事”和勋章名字,让我们一起为彼此鼓掌。

喜欢这个小实验,也欢迎点个赞、点个“在看”,或转发给小伙伴一起瞅瞅。

欢迎大家加入启元开发者社区https://dev.primebot.com/,后续的 Q1 开发记录,我会继续和大家分享呀。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进