我没有实体 Q1 机器人,所以这次把验证范围收在开发环境里:从公开 SDK 构建消息包,按接口定义发起 Service 调用,再用本地 Mock 收到一条速度 Topic。这样能检查接口类型和 ROS 2 消息流是否接得上;它不回答机器人是否会接受指令、是否会运动。

操作环境是 Mac 上的 Ubuntu 22.04.5 LTS ARM64 虚拟机,安装的是 ROS 2 Humble ros-humble-ros-base。Q1 SDK 仓库检出 v1.0.0,其中 aimdk_msgs 包版本为 1.0.0;本地演示包 q1_interface_mock_demo 版本为 0.1.0。启元的快速入门指南以 Ubuntu 22.04 和 ROS 2 Humble 为开发环境,并把 RESTful/gRPC API 标为暂未支持;我按仓库里的 ROS 2 接口做验证。ROS 安装按官方 Humble 指南完成,这里从 SDK 构建开始,不重复基础环境安装步骤。

构建工具用 apt 安装,命令在普通 Ubuntu 用户终端执行,系统包安装需要 sudo:

sudo apt install -y git build-essential cmake python3-colcon-common-extensions python3-rosdep python3-vcstool ros-humble-rmw-fastrtps-cpp

这些是本次构建用到的工具和 Fast DDS RMW 实现。ROS 2 本身仍按前面的 Humble 官方指南安装。

先把消息包单独编译出来

我在工作区里克隆指定标签,再核对标签、提交号和 package.xml 版本。具体命令如下:

mkdir -p ~/q1-article/ws/src
cd ~/q1-article/ws/src
git clone --branch v1.0.0 --depth 1 https://atomgit.com/primebot/q1-sdk.git q1-sdk
cd q1-sdk
git describe --tags --always
git rev-parse HEAD
grep -nE '<(name|version)>' aimdk_msgs/package.xml | sed -n '1,4p'

我检出的是 AtomGit 上的 v1.0.0 标签,对应提交 7310314eee058323a2c15ab1f4f947df4112573e。仓库标签和 ROS 包版本是两项信息,记录时分开写,免得把 SDK 下载包版本、Git 标签和 package.xml 版本混成一个数字。

Q1 SDK 标签、提交号与 aimdk_msgs 包版本

图 1|本机检出的 SDK 标签为 v1.0.0,aimdk_msgs 的 package.xml 版本为 1.0.0。

依赖解析时我碰到一个小问题:rosdep 报 python3-rclpy 没有对应的定义。这个报错本身不能判断 rclpy 是否缺失,aimdk_msgs/package.xml 声明了这个运行依赖,但 rosdep 数据库没有解析字面键 python3-rclpy。第一次执行时,输出末尾虽有 All required rosdeps installed successfully,前面仍报 unresolved key,所以我没有直接把它记作干净通过。

我在同一环境里查了包前缀、Python 导入和 rosdep 的标准键:

source /opt/ros/humble/setup.bash
ros2 pkg prefix rclpy
python3 -c 'import rclpy; print(rclpy.__file__)'
rosdep resolve rclpy --rosdistro humble

实际输出分别指向 /opt/ros/humble、显示 rclpy 模块路径,并将 rclpy 映射到 ros-humble-rclpy。因此我没有改 SDK 的 package.xml,而是在这台机器上跳过无法解析的字面键,再确认其余依赖已满足:

cd ~/q1-article/ws
source /opt/ros/humble/setup.bash
rosdep install --from-paths src/q1-sdk/aimdk_msgs --ignore-src --rosdistro humble -r -y --skip-keys=python3-rclpy

随后只构建 aimdk_msgs,没有把整个 Q1 SDK 示例工程说成已编译。输出同时写入日志,pipefail 让构建失败状态不会被后面的 tee 覆盖:

cd ~/q1-article/ws
source /opt/ros/humble/setup.bash
set -o pipefail
colcon build --packages-select aimdk_msgs --symlink-install 2>&1 | tee ~/q1-article/aimdk_msgs-build.log
source install/setup.bash
ros2 pkg prefix aimdk_msgs

本机输出是 1 package finished [47.9s],包前缀落在当前工作区的 install/aimdk_msgs 下。47.9 秒只是这台虚拟机这一次的构建时间,不是性能指标。source install/setup.bash 会把这个工作区叠加到 ROS 环境上;如果只加载 /opt/ros/humble,新生成的接口类型不会从这个工作区被选中。确认前缀后,我才继续查消息定义。

aimdk_msgs 构建结果与工作区安装前缀

图 2|colcon 只构建 aimdk_msgs;包已安装到当前工作区。

这次构建只生成了消息定义包;我没有构建或运行 SDK 里的运动控制示例。后面的命令使用这个包导出的类型,不能因为它编译成功就顺带说整个 SDK 都验证过。

Topic 和 Service 各自承担什么

我接着查了 McLocomotionVelocity 的定义,命令是 ros2 interface show aimdk_msgs/msg/McLocomotionVelocity。消息头里有时间戳、frame_id、序号和测量时间戳;后面我把 header 留空,Mock 收到的序号因此是 0。消息还包含输入源名称,以及前进、侧移、旋转等速度字段;pitch_velocity 和 level 也在结构里。字段存在只说明接口提供了位置,具体作用要以接口说明为准,不能单凭名字推断真机效果。Q1 SDK 接口说明将后两项标为预留、当前不生效。

McLocomotionVelocity 消息字段定义

图 3|速度消息中可见 header、source 和各速度字段。

后面的发布命令直接使用这个接口类型,所以字段名也必须与编译出来的定义一致。我没有另造一套本地消息格式。

我先把这一步当作消息格式核对:时间戳和序号放在 header,输入源和速度数值是消息主体里的字段。字段名能被 ROS 2 识别,不等于真机一定会按这些值运动;本次只发全零消息,先确认类型和订阅路径接通。接下来转到输入源的 Service,分别看一次写入和查询的请求、响应结构。

这个消息走 Topic:发布者发出消息,订阅者接收数据,适合持续发布的控制数据。消息结构描述数据有哪些字段,不会单独告诉调用方真机如何仲裁输入源或限制速度。ROS 2 文档也将 Topic 说明为发布—订阅模型。ROS 2 Humble Topics 我的本地验证只用 --once 发一条样例,所以能观察一次消息从 Publisher 到 Subscriber 的传递,不能据此讨论控制频率、时延或连续运动效果。

输入源的添加和查询则走 Service。SetMcInputSource 的请求包含公共请求头、操作类型和 McInputSource;接口枚举列出了 ADD(1001)、MODIFY(1002)、DELETE(1003)、ENABLE(2001)和 DISABLE(2002)。实际演示只调用 ADD。调用前我用 ros2 interface show aimdk_msgs/srv/SetMcInputSource 查看请求和响应字段;响应中的 CommonTaskResponse 又包含返回码、任务 ID 和状态字段。GetCurrentInputSource 的请求只有公共请求,响应除公共任务状态外,还包含当前输入源结构。读写拆成两个服务后,调用方能分别查看请求字段和返回字段。

SetMcInputSource 服务结构

图 4|设置服务的请求字段与操作枚举,ADD 的值为 1001。

这里需要分清接口定义和服务端行为:枚举里有多个操作,不代表我写的 Mock 都实现了。本地程序只处理 ADD,其他动作不用于这次演示。查询接口我另外查看 .srv 源文件,确认 --- 后的响应包含 CommonTaskResponse response 和 McInputSource input_source;查询请求本身没有输入源名称字段,因此返回的是服务端当前记录,而不是按名称搜索。嵌套类型展开时,终端视口不一定能显示完整响应,直接读 .srv 文件更容易核对分隔线两侧的字段。要复核这个源定义,可以在 SDK 工作区运行:

cat src/q1-sdk/aimdk_msgs/mc/motion/srv/GetCurrentInputSource.srv

GetCurrentInputSource 服务源定义

图 5|查询服务的响应包含 CommonTaskResponse 和 McInputSource。

Service 是一次请求对应一次响应;ROS 2 文档也将它与持续发布消息的 Topic 分开说明。ROS 2 Humble Services 实际写命令时还要区分服务名称和服务类型:调用端点用 /aimdk_5Fmsgs/srv/SetMcInputSource,类型用 aimdk_msgs/srv/SetMcInputSource。两者来自不同位置,不能把 ros2 interface show 显示的类型名直接当作服务端点名称。这样查接口时,我先看类型和字段,再在 Mock 运行后看请求返回;前者不要求服务端在线,后者必须有服务节点在同一个通信域内运行。

用本地 Mock 跑通一来一回

Mock 包使用刚编译好的官方消息类型,自己创建两个 Service Server 和一个 Topic Subscriber。它只实现 ADD:收到输入源后存进当前进程内存;查询时把这条记录返回;Topic 回调则打印收到的字段。订阅队列深度在 Mock 代码里设为 10,只是这个本地节点的设置,不是 Q1 的 QoS 规范。代码没有连接 Q1,也没有模拟运控板的控制权仲裁。Mock 代码还会拒绝非 ADD 操作和空名称;进程里尚无记录时,GET 会返回无效状态。这次只实测了 ADD 后再 GET 的成功路径,错误分支不写成运行结果。

本地 Mock 源码和复现说明已发布在 AtomGit 的 q1-interface-mock-demo。包元数据版本是 0.1.0,运行依赖声明为 rclpy 和 aimdk_msgs;setup.py 把 q1-interface-mock 命令指向 q1_interface_mock_demo.mock_node:main。在本机,我把这个包放进同一工作区的 src 目录,使用前面构建好的 aimdk_msgs;构建安装后可以直接用 ros2 run 启动。

cd ~/q1-article/ws
source /opt/ros/humble/setup.bash
source install/setup.bash
colcon build --packages-select q1_interface_mock_demo --symlink-install
source install/setup.bash
ros2 pkg prefix q1_interface_mock_demo

本地 Mock 包构建与安装路径

图 6|q1_interface_mock_demo 构建成功,前缀位于当前工作区。

这一步构建的是本地演示包;官方 SDK 仓库中的其他示例并没有一并构建。若 ros2 pkg prefix 找不到这个包,先检查当前终端是否重新 source 了工作区,而不是重复构建所有 SDK 示例。前缀来自这次工作区的安装目录。

Mock 能编译通过,说明这个 Python 节点包在当前工作区可以被 colcon 安装;运行前仍要在新终端重新加载 install/setup.bash,让 ROS 2 找到刚装好的包,同时也要让 Python 环境看到 aimdk_msgs。Mock 的 Service Server 和 Topic Subscriber 都在这个节点里创建;客户端由终端中的 ros2 service call 和 ros2 topic pub 命令临时承担。

我在两个终端加载同一个 ROS 环境,并都设置 ROS_DOMAIN_ID=42。ROS 2 节点要在同一个 domain 中才能发现彼此;这个值只是本次本地验证使用的配置,不等于网络隔离。ROS 2 Humble Domain ID 如果同一网络里还有其他节点也使用 42,它们可能会相互发现;Domain ID 不是访问控制。本地照做时要结合自己的 ROS 网络安排这个值。

先在终端 A 启动本地节点:

source /opt/ros/humble/setup.bash
source ~/q1-article/ws/install/setup.bash
export ROS_DOMAIN_ID=42
ros2 run q1_interface_mock_demo q1-interface-mock

节点在终端 A 保持运行。启动日志列出两个服务和一个速度 Topic,并明确提示它不与 Q1 通信。这个提示很重要:后面的响应和收包日志都来自这个本地测试替身。看到服务端点已经启动,只能继续做本地调用;还不能说明真实设备在线或拥有控制权。客户端要能发现服务端,两个终端都要加载 ROS 与工作区环境,并设置相同 Domain ID。这时还没有发出服务请求。

本地 Mock 服务和 Topic 已启动

图 7|Mock 注册 Set/Get 两个服务和速度订阅端,并标明不连接 Q1。

两个 Service 和 Topic Subscriber 都由同一节点进程提供。

终端 B 发起 ADD 请求。priority=20 和 timeout=1000 是这次传给 Mock 的演示数据,不是我测出的真机优先级或控制参数:

source /opt/ros/humble/setup.bash
source ~/q1-article/ws/install/setup.bash
export ROS_DOMAIN_ID=42
ros2 service call /aimdk_5Fmsgs/srv/SetMcInputSource aimdk_msgs/srv/SetMcInputSource '{request: {}, action: {value: 1001}, input_source: {name: "local_demo", priority: 20, timeout: 1000}}'

请求中的 action.value=1001 对应接口定义里的 ADD。客户端返回 code=0、task_id=1、state.value=1;服务端日志则记录了 local_demo。这些字段是 Mock 程序按本地演示逻辑填写的,不是 Q1 服务端的返回值。priority=20 和 timeout=1000 只作为两个普通整数存取,没有在这次实验里解释或验证它们在真机上的控制语义。

本地 SetMcInputSource 请求与 Mock 响应

图 8|终端 B 收到本地 Mock 响应,终端 A 记录了写入内存的演示输入源。

ADD 已返回,接着用独立的 GET 请求核对保存值。

紧接着查询刚才写入的数据。GetCurrentInputSource 不接收输入源名称参数,Mock 直接返回当前进程保存的那条记录;如果重启节点,内存状态会清空。这是演示程序的实现方式,不是设备端数据的持久化保证:

ros2 service call /aimdk_5Fmsgs/srv/GetCurrentInputSource aimdk_msgs/srv/GetCurrentInputSource '{}'

响应里再次出现 local_demo、priority=20 和 timeout=1000。这验证的是同一个 Mock 进程的内存读写,以及 ROS 2 Service 请求—响应流程。此处的 code=0 和 state.value=1 是这个 Mock 的返回逻辑,不能当作真实 Q1 服务状态码;关闭进程后,记录也就不存在了。

本地 GetCurrentInputSource 查询结果

图 9|查询结果与刚才传入 Mock 的演示数据一致。

最后发一条全零 Topic 消息

我没有用速度值尝试驱动机器人,只发了一条各速度字段为零的消息,检查 Mock 订阅端能否收到:

ros2 topic pub --once /aima/mc/locomotion/velocity aimdk_msgs/msg/McLocomotionVelocity '{header: {}, source: "local_demo", forward_velocity: 0.0, lateral_velocity: 0.0, angular_velocity: 0.0, pitch_velocity: 0.0, level: 0.0}'

终端 A 收到后打印了 source='local_demo'、sequence=0,以及 forward、lateral、angular 均为 0.0。发布命令中的 --once 限定只发一条;Mock 回调也只打印这几个字段,所以我只把它们作为订阅端观察结果。这说明本机 ROS 2 Domain 内发布和订阅成功,不能据此讨论连续控制流、发布周期或丢包情况;CLI 显示消息已发布,也不等于机器人收到了或执行了指令。

本地速度 Topic 消息发布与 Mock 收包

图 10|Mock 订阅回调收到了全零速度消息;右侧是 ros2 topic pub 的输出。

这次验证到这里

在这台 Ubuntu 虚拟机上,我构建了 Q1 SDK v1.0.0 中的 aimdk_msgs,并用同一套类型完成了本地 Service 请求/响应和 Topic 发布/订阅。实体机器人、固件行为、控制权限仲裁和运动效果都没有在这次操作中验证。我的虚拟机是 ARM64,这次成功只说明 aimdk_msgs 与演示包在当前环境完成构建和本机运行,不代表 Q1 SDK 的全部功能都已获得 ARM64 官方支持。准备接真机时,还需要按对应版本的官方资料和设备安全流程单独确认;本地示例里的输入源名称、优先级和超时值也不应直接照搬到设备配置。

后续开发资料可查看启元机器人开发者社区。

Logo

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

更多推荐