
我没有实体 Q1 机器人所以这次把验证范围收在开发环境里从公开 SDK 构建消息包按接口定义发起 Service 调用再用本地 Mock 收到一条速度 Topic。这样能检查接口类型和 ROS 2 消息流是否接得上它不回答机器人是否会接受指令、是否会运动。操作环境是 Mac 上的 Ubuntu 22.04.5 LTS ARM64 虚拟机安装的是 ROS 2 Humbleros-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 用户终端执行系统包安装需要sudosudoaptinstall-ygitbuild-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/srccd~/q1-article/ws/srcgitclone--branchv1.0.0--depth1https://atomgit.com/primebot/q1-sdk.git q1-sdkcdq1-sdkgitdescribe--tags--alwaysgitrev-parse HEADgrep-nE(name|version)aimdk_msgs/package.xml|sed-n1,4p我检出的是 AtomGit 上的v1.0.0标签对应提交7310314eee058323a2c15ab1f4f947df4112573e。仓库标签和 ROS 包版本是两项信息记录时分开写免得把 SDK 下载包版本、Git 标签和package.xml版本混成一个数字。图 1本机检出的 SDK 标签为 v1.0.0aimdk_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-cimport rclpy; print(rclpy.__file__)rosdep resolve rclpy--rosdistrohumble实际输出分别指向/opt/ros/humble、显示rclpy模块路径并将rclpy映射到ros-humble-rclpy。因此我没有改 SDK 的package.xml而是在这台机器上跳过无法解析的字面键再确认其余依赖已满足cd~/q1-article/wssource/opt/ros/humble/setup.bash rosdepinstall--from-paths src/q1-sdk/aimdk_msgs --ignore-src--rosdistrohumble-r-y--skip-keyspython3-rclpy随后只构建aimdk_msgs没有把整个 Q1 SDK 示例工程说成已编译。输出同时写入日志pipefail让构建失败状态不会被后面的tee覆盖cd~/q1-article/wssource/opt/ros/humble/setup.bashset-opipefail colcon build --packages-select aimdk_msgs --symlink-install21|tee~/q1-article/aimdk_msgs-build.logsourceinstall/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新生成的接口类型不会从这个工作区被选中。确认前缀后我才继续查消息定义。图 2colcon 只构建 aimdk_msgs包已安装到当前工作区。这次构建只生成了消息定义包我没有构建或运行 SDK 里的运动控制示例。后面的命令使用这个包导出的类型不能因为它编译成功就顺带说整个 SDK 都验证过。Topic 和 Service 各自承担什么我接着查了McLocomotionVelocity的定义命令是ros2 interface show aimdk_msgs/msg/McLocomotionVelocity。消息头里有时间戳、frame_id、序号和测量时间戳后面我把header留空Mock 收到的序号因此是0。消息还包含输入源名称以及前进、侧移、旋转等速度字段pitch_velocity和level也在结构里。字段存在只说明接口提供了位置具体作用要以接口说明为准不能单凭名字推断真机效果。Q1 SDK 接口说明将后两项标为预留、当前不生效。图 3速度消息中可见 header、source 和各速度字段。后面的发布命令直接使用这个接口类型所以字段名也必须与编译出来的定义一致。我没有另造一套本地消息格式。我先把这一步当作消息格式核对时间戳和序号放在header输入源和速度数值是消息主体里的字段。字段名能被 ROS 2 识别不等于真机一定会按这些值运动本次只发全零消息先确认类型和订阅路径接通。接下来转到输入源的 Service分别看一次写入和查询的请求、响应结构。这个消息走 Topic发布者发出消息订阅者接收数据适合持续发布的控制数据。消息结构描述数据有哪些字段不会单独告诉调用方真机如何仲裁输入源或限制速度。ROS 2 文档也将 Topic 说明为发布—订阅模型。ROS 2 Humble Topics 我的本地验证只用--once发一条样例所以能观察一次消息从 Publisher 到 Subscriber 的传递不能据此讨论控制频率、时延或连续运动效果。输入源的添加和查询则走 Service。SetMcInputSource的请求包含公共请求头、操作类型和McInputSource接口枚举列出了 ADD1001、MODIFY1002、DELETE1003、ENABLE2001和 DISABLE2002。实际演示只调用 ADD。调用前我用ros2 interface show aimdk_msgs/srv/SetMcInputSource查看请求和响应字段响应中的CommonTaskResponse又包含返回码、任务 ID 和状态字段。GetCurrentInputSource的请求只有公共请求响应除公共任务状态外还包含当前输入源结构。读写拆成两个服务后调用方能分别查看请求字段和返回字段。图 4设置服务的请求字段与操作枚举ADD 的值为 1001。这里需要分清接口定义和服务端行为枚举里有多个操作不代表我写的 Mock 都实现了。本地程序只处理ADD其他动作不用于这次演示。查询接口我另外查看.srv源文件确认---后的响应包含CommonTaskResponse response和McInputSource input_source查询请求本身没有输入源名称字段因此返回的是服务端当前记录而不是按名称搜索。嵌套类型展开时终端视口不一定能显示完整响应直接读.srv文件更容易核对分隔线两侧的字段。要复核这个源定义可以在 SDK 工作区运行catsrc/q1-sdk/aimdk_msgs/mc/motion/srv/GetCurrentInputSource.srv图 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_msgssetup.py把q1-interface-mock命令指向q1_interface_mock_demo.mock_node:main。在本机我把这个包放进同一工作区的src目录使用前面构建好的aimdk_msgs构建安装后可以直接用ros2 run启动。cd~/q1-article/wssource/opt/ros/humble/setup.bashsourceinstall/setup.bash colcon build --packages-select q1_interface_mock_demo --symlink-installsourceinstall/setup.bash ros2 pkg prefix q1_interface_mock_demo图 6q1_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_ID42。ROS 2 节点要在同一个 domain 中才能发现彼此这个值只是本次本地验证使用的配置不等于网络隔离。ROS 2 Humble Domain ID 如果同一网络里还有其他节点也使用 42它们可能会相互发现Domain ID 不是访问控制。本地照做时要结合自己的 ROS 网络安排这个值。先在终端 A 启动本地节点source/opt/ros/humble/setup.bashsource~/q1-article/ws/install/setup.bashexportROS_DOMAIN_ID42ros2 run q1_interface_mock_demo q1-interface-mock节点在终端 A 保持运行。启动日志列出两个服务和一个速度 Topic并明确提示它不与 Q1 通信。这个提示很重要后面的响应和收包日志都来自这个本地测试替身。看到服务端点已经启动只能继续做本地调用还不能说明真实设备在线或拥有控制权。客户端要能发现服务端两个终端都要加载 ROS 与工作区环境并设置相同 Domain ID。这时还没有发出服务请求。图 7Mock 注册 Set/Get 两个服务和速度订阅端并标明不连接 Q1。两个 Service 和 Topic Subscriber 都由同一节点进程提供。终端 B 发起 ADD 请求。priority20和timeout1000是这次传给 Mock 的演示数据不是我测出的真机优先级或控制参数source/opt/ros/humble/setup.bashsource~/q1-article/ws/install/setup.bashexportROS_DOMAIN_ID42ros2servicecall /aimdk_5Fmsgs/srv/SetMcInputSource aimdk_msgs/srv/SetMcInputSource{request: {}, action: {value: 1001}, input_source: {name: local_demo, priority: 20, timeout: 1000}}请求中的action.value1001对应接口定义里的 ADD。客户端返回code0、task_id1、state.value1服务端日志则记录了local_demo。这些字段是 Mock 程序按本地演示逻辑填写的不是 Q1 服务端的返回值。priority20和timeout1000只作为两个普通整数存取没有在这次实验里解释或验证它们在真机上的控制语义。图 8终端 B 收到本地 Mock 响应终端 A 记录了写入内存的演示输入源。ADD 已返回接着用独立的 GET 请求核对保存值。紧接着查询刚才写入的数据。GetCurrentInputSource不接收输入源名称参数Mock 直接返回当前进程保存的那条记录如果重启节点内存状态会清空。这是演示程序的实现方式不是设备端数据的持久化保证ros2servicecall /aimdk_5Fmsgs/srv/GetCurrentInputSource aimdk_msgs/srv/GetCurrentInputSource{}响应里再次出现local_demo、priority20和timeout1000。这验证的是同一个 Mock 进程的内存读写以及 ROS 2 Service 请求—响应流程。此处的code0和state.value1是这个 Mock 的返回逻辑不能当作真实 Q1 服务状态码关闭进程后记录也就不存在了。图 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 收到后打印了sourcelocal_demo、sequence0以及forward、lateral、angular均为0.0。发布命令中的--once限定只发一条Mock 回调也只打印这几个字段所以我只把它们作为订阅端观察结果。这说明本机 ROS 2 Domain 内发布和订阅成功不能据此讨论连续控制流、发布周期或丢包情况CLI 显示消息已发布也不等于机器人收到了或执行了指令。图 10Mock 订阅回调收到了全零速度消息右侧是 ros2 topic pub 的输出。这次验证到这里在这台 Ubuntu 虚拟机上我构建了 Q1 SDKv1.0.0中的aimdk_msgs并用同一套类型完成了本地 Service 请求/响应和 Topic 发布/订阅。实体机器人、固件行为、控制权限仲裁和运动效果都没有在这次操作中验证。我的虚拟机是 ARM64这次成功只说明aimdk_msgs与演示包在当前环境完成构建和本机运行不代表 Q1 SDK 的全部功能都已获得 ARM64 官方支持。准备接真机时还需要按对应版本的官方资料和设备安全流程单独确认本地示例里的输入源名称、优先级和超时值也不应直接照搬到设备配置。后续开发资料可查看启元机器人开发者社区。