news 2026/10/21 22:56:37

MoveIt2 的 Servo 遥操作客户端(moveit_py.servo_client)使用与源码解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MoveIt2 的 Servo 遥操作客户端(moveit_py.servo_client)使用与源码解析
  • 机器人
  • ROS

【免费下载链接】moveit2

:robot: MoveIt for ROS 2

项目地址:https://gitcode.com/gh_mirrors/mo/moveit2
点击查看免费下载

导读

moveit_py是 MoveIt2 的 Python 接口包,其中的servo_client子模块(moveit_py/moveit/servo_client)为开发者提供了一个面向MoveIt Servo(实时伺服控制节点)的客户端实现,允许通过游戏手柄等输入设备直接操控机械臂末端执行器。阅读本文后,你将掌握:该模块如何与servo_node的 Twist 命令话题和启停服务对接、PS4 DualShock 手柄的轴/按键映射与速度指令生成逻辑,以及如何基于其抽象基类扩展自定义遥操作设备或用于学习型(learning-based)策略部署。

一、模块定位:MoveIt2 Python 世界里的遥操作入口

仓库中 moveit_py/moveit/servo_client/init.py 的模块文档给出了清晰定位:

A client providing access to the MoveIt Servo node. This module can be used to configure new devices for teleoperation. It is also possible to use this module for sending position or velocity commands to your robot which can be useful for learning based approaches.

即该模块承担两类职责:

  1. 遥操作:为不同输入设备(当前支持 PS4 DualShock 手柄)提供与 MoveIt Servo 交互的客户端;
  2. 指令注入:绕过手柄,直接向机器人发送位置或速度指令,可用于强化学习 / 示教学习等基于学习的方法。

从目录结构看,模块由两层组成:

  • teleop.py:定义TeleopDevice抽象基类,封装所有设备共用的 ROS 2 话题/服务对接逻辑;
  • devices/ps4_dualshock.py:PS4 DualShock 手柄的具体实现,含轴/按键映射数据类与手柄遥操作类。

此外,官方 API 文档入口 moveit_py/docs/source/index.rst 将moveit_py.servo_client与core、planning、policies一并列为顶层公开模块,说明它属于 MoveItPy 对外稳定提供的 API 面。

二、TeleopDevice:所有遥操作设备的抽象基类

teleop.py 中的TeleopDevice同时继承ABC与rclpy.node.Node,把「设备无关」的 ROS 2 基础设施集中到基类,具体设备只需实现两个抽象方法。

2.1 构造参数

def __init__(self, node_name, device_name, device_config, ee_frame_name): super().__init__(node_name=node_name) self.device_name = device_name self.device_config = device_config self.ee_frame_name = ee_frame_name
  • node_name:ROS 2 节点名;
  • device_name/device_config:设备标识与配置对象(具体设备可传入自己的映射数据类);
  • ee_frame_name:末端执行器坐标系名,写入TwistStamped.header.frame_id,用于告诉 servo 节点 Twist 指令参考哪个坐标系。

2.2 与 MoveIt Servo 的对接接口

基类在构造时创建了四条 ROS 2 通信链路(话题/服务名可在 servo_node.cpp 与 servo_parameters.yaml 中找到对应实现):

类型名称消息/服务类型作用
Subscriber/joysensor_msgs/Joy订阅手柄原始输入,回调即publish_command
Publisher/servo_node/delta_twist_cmdsgeometry_msgs/TwistStamped向 servo 发送笛卡尔增量 Twist 指令
Client/servo_node/start_servostd_srvs/Trigger启动伺服装载
Client/servo_node/stop_servostd_srvs/Trigger停止伺服装载

其中 Twist 指令话题正是 servo 参数cartesian_command_in_topic(默认~/delta_twist_cmds)在servo_node节点命名下的实际解析结果,servo 侧通过 servo_node.cpp 的twist_subscriber_订阅该话题。

2.3 启动与停止遥操作

start_teleop()完成两件事:

  1. 等待并调用start_servo服务:wait_for_service(10.0)最多等待 10 秒,随后call_async(Trigger.Request())异步触发;
  2. 以多线程执行器后台旋转节点:显式使用rclpy.executors.MultiThreadedExecutor并把节点加入其中,注释说明原因是"Servo has concurrent processes for moving the robot and avoiding collisions"——即 MoveIt Servo 内部并行处理机器人运动与避障,客户端需要多线程执行器才能保证/joy回调与异步服务调用不被阻塞:
ex = rclpy.executors.MultiThreadedExecutor() ex.add_node(self) self.teleop_thread = threading.Thread(target=ex.spin, daemon=True) self.teleop_thread.start()

stop_teleop()则对称地调用stop_servo服务并join()后台线程,实现优雅退出。

2.4 必须由子类实现的两个抽象方法

@abstractmethod def publish_command(self): ... # 将设备输入转换为并发布遥操作指令 @abstractmethod def record(self): ... # 记录轨迹数据(示教/数据采集用途)

从源码结构看,record()目前在各实现中均为空实现(pass),属于为轨迹记录预留的扩展点。

三、PS4 DualShock 设备实现:映射与指令生成

仓库当前唯一完整的设备实现位于 devices/ps4_dualshock.py,对应 README 中「Supported Devices」列表第一项。

3.1 设备映射数据类:遵循 Joy 消息约定

模块定义了三个@dataclass,其索引约定与sensor_msgs/Joy消息(axes[]与buttons[]数组)一一对应:

DualShockAxes(轴索引)

字段索引说明
LEFT_STICK_X/LEFT_STICK_Y0 / 1左摇杆(俯仰/侧倾输入)
LEFT_TRIGGER2左扳机(前进)
RIGHT_STICK_X/RIGHT_STICK_Y3 / 4右摇杆(平移输入)
RIGHT_TRIGGER5右扳机(后退)
D_PAD_X/D_PAD_Y6 / 7方向键

DualShockButtons(按键索引)

字段索引字段索引
X/O/TRIANGLE/SQUARE0–3L1/R14 / 5
L2/R26 / 7SHARE/OPTIONS/HOME8 / 9 / 10
LEFT_STICK_TRIGGER/RIGHT_STICK_TRIGGER11 / 12

PS4DualShock数据类将这些映射聚合为Axes与Buttons两个字段,作为设备配置整体传入。

3.2 publish_command:Joy 到 Twist 的转换逻辑

PS4DualShockTeleop.publish_command(data)是遥操作的核心,它将sensor_msgs/Joy原始输入换算为TwistStamped并发布:

twist = TwistStamped() # 右摇杆 → 平面内平移(Z 轴朝前、Y 轴侧向) twist.twist.linear.z = data.axes[RIGHT_STICK_Y] twist.twist.linear.y = data.axes[RIGHT_STICK_X] # 扳机 → 前后进(左右扳机线性组合,系数 ±0.5) lin_x_right = -0.5 * data.axes[RIGHT_TRIGGER] lin_x_left = 0.5 * data.axes[LEFT_TRIGGER] twist.twist.linear.x = lin_x_right + lin_x_left # 左摇杆 → 俯仰/侧倾角速度 twist.twist.angular.y = data.axes[LEFT_STICK_Y] twist.twist.angular.x = data.axes[LEFT_STICK_X] # L1/R1 → 滚转(绕 X 轴)角速度,按键值 0/1 映射为 ±1 roll_positive = data.buttons[R1] roll_negative = -1 * data.buttons[L1] twist.twist.angular.z = float(roll_positive + roll_negative) # 帧与时间戳 twist.header.frame_id = self.ee_frame_name twist.header.stamp = self.get_clock().now().to_msg() self.twist_publisher.publish(twist)

值得注意的细节:

  • 坐标系:TwistStamped.header.frame_id被设置为构造时传入的ee_frame_name,因此输出的是「相对末端执行器坐标系」的增量速度,配合 servo 参数apply_twist_commands_about_ee_frame: true(servo_parameters.yaml)使用,可实现手柄操作与机械臂末端姿态解耦;
  • 扳机组合:前进/后退不是简单取一个扳机值,而是左右扳机的线性组合,支持按住一侧扳机时获得半速、同时按压时相互抵消(速度为零)的直觉操作;
  • 单位约定:Joy 轴值域为[-1, 1],正好契合 servo 参数command_in_type的"unitless"模式(servo_parameters.yaml 描述为"unitless commands are in the range [-1:1] as if from joystick"),并在 servo 侧乘以scale.linear(默认 0.4 m/s)与scale.rotational(默认 0.8 rad/s)得到实际速度。

构造函数提供了便于直接实例化的默认值:

PS4DualShockTeleop( ee_frame_name, # 必填,如 "panda_link8" node_name="ps4_dualshock_teleop", device_name="ps4_dualshock", device_config=PS4DualShock(), )

四、自定义遥操作设备:扩展指南

模块文档明确指出「this module can be used to configure new devices for teleoperation」,扩展一个新设备只需三步:

  1. 继承TeleopDevice(可参考 ps4_dualshock.py 的写法),在__init__中调用super().__init__(...)并透传node_name、device_name、device_config、ee_frame_name;
  2. 实现publish_command(self, data):订阅/joy的sensor_msgs/Joy会作为回调参数传入,将其映射为TwistStamped发布到/servo_node/delta_twist_cmds;
  3. 实现record(self):填充自己的轨迹记录逻辑(当前各设备均为空实现,属预留接口)。

基类已经替你完成了节点创建、话题/服务绑定、start_servo/stop_servo调用与多线程执行器管理,新设备只需关注「输入 → Twist」的映射策略。类型桩文件 teleop.pyi 与 ps4_dualshock.pyi 可辅助 IDE 补全与静态检查。

五、支持设备现状与规划

README 的「Supported Devices」列表如实记录了当前状态:

  • PS4 DualShock Controller:✅ 已支持(即上文第三节的完整实现);
  • Oculus Quest 2:🕐 计划支持(README 原文"plans to support this device")。

Oculus Quest 2 属于 VR 头显输入设备,其交互方式(六自由度手柄姿态、手势)与游戏手柄的轴/按键模型差异较大,仓库中目前尚无对应实现文件,这一点在 README 中已被明确标注为规划项。如果读者需要 VR 遥操作,可结合「自定义设备扩展」一节自行实现,或关注该项目后续进展。

六、遥操作之外:servo_client 在学习型机器人中的应用

servo_client模块文档强调的另一大用途是learning based approaches——向机器人直接注入速度指令。仓库中与之配套的是 moveit_py/moveit/policies 模块,其模块文档(policies/init.py)写明:

This module can be used to configure moveit_servo for learning based applications.

其中 policy.py 的Policy抽象基类与 servo_client 共享同一套理念:

  • 通过register_command()创建指令发布器,支持geometry_msgs/PoseStamped、geometry_msgs/Twist、control_msgs/JointJog三种命令类型(policy.py),其中 Twist 指令同样面向 moveit_servo 的指令话题;
  • 通过ApproximateTimeSynchronizer对多个传感器话题(如sensor_msgs/Image)做时间同步,将前向推理结果作为控制指令发布。

也就是说,servo_client提供的是「人手/手柄 → 机器人」的实时回路,而policies提供的是「神经网络策略 → 机器人」的实时回路,两者都以moveit_servo作为底层实时控制通道,这正解释了模块文档中「既可用于遥操作,也可用于学习型方法」的定位。

七、与 moveit_servo 的协作要点

要让servo_client真正驱动机械臂,需要与 moveit_ros/moveit_servo 节点协同,几个关键对接点:

  1. 话题对齐:客户端发布/servo_node/delta_twist_cmds(TwistStamped),与 servo 参数cartesian_command_in_topic默认值在servo_node命名下的解析结果一致(参见 panda_simulated_config.yaml 中的cartesian_command_in_topic: ~/delta_twist_cmds);
  2. 输入单位:手柄轴值域[-1,1]对应 servo 的command_in_type: "unitless"模式;若使用"speed_units"(单位 m/s 与 rad/s),则需在设备端自行完成速度标定(scale.linear默认 0.4 m/s、scale.rotational默认 0.8 rad/s,见 servo_parameters.yaml);
  3. 启停服务:/servo_node/start_servo与/servo_node/stop_servo的调用由基类统一封装,无需在设备实现中重复处理;
  4. 坐标系一致性:ee_frame_name必须与机器人 URDF 中实际存在的末端坐标系名一致,否则 Twist 指令的frame_id无法被正确变换。

总结

moveit_py.servo_client是 MoveIt2 Python 生态中面向实时遥操作的精简客户端:TeleopDevice抽象基类完成了与servo_node的所有 ROS 2 对接(Twist 话题、启停服务、多线程执行器),PS4 DualShock 实现给出了「手柄轴/按键 → Twist 指令」的完整参考映射,而自定义设备扩展只需聚焦publish_command与record两个方法。结合policies模块,它还能支撑学习型控制策略的部署,是连接交互设备、智能策略与 MoveIt Servo 实时控制能力的桥梁。

相关参考文件:

  • servo_client README
  • TeleopDevice 抽象基类
  • PS4 DualShock 实现
  • servo_node 订阅实现
  • servo 参数定义
  • Panda 示例配置
  • Policy 基类
  • 机器人
  • ROS

【免费下载链接】moveit2

:robot: MoveIt for ROS 2

项目地址:https://gitcode.com/gh_mirrors/mo/moveit2
点击查看免费下载
上一篇:STL到STEP格式转换终极指南:5步实现网格到实体的专业转换
下一篇:使用 @inquirer/external-editor 调用系统编辑器编辑文本:API 全解与源码原理

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/21 21:01:30

SAP CTO谈预测AI:能解释业务,不等于能准确预测业务

企业希望AI预测客户付款、商品需求和设备故障,但这些问题并不适合全部交给语言模型。在CIO的采访中,SAP首席技术官Philipp Herzig指出,分类、回归等结构化数据任务,需要更合适的预测工具;SAP正在通过RPT和TabPFN等表格…

作者头像 李华
网站建设 2026/10/18 23:26:40

AnyPS5全攻略:存储扩展、远程串流与散热维护实战

我拿到“AnyPS5”这个标题时,第一反应是:这哥们儿多半不是来晒机器的,而是想告诉所有人——不管你是哪个版本、光驱还是数字版、新玩家还是老折腾党,手里的PS5都能玩出更多花样。后来跟群里的朋友一聊,发现大家关心的还…

作者头像 李华
网站建设 2026/10/19 5:19:07

STM32F103ZET6循迹小车实战:从硬件选型到PID调参全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/20 23:44:00

632流程变革框架:从PPT到可执行流程资产的落地方法论

简介:本资源是美的集团632项目核心成果——《EPF企业流程框架规划设计方案》PPT课件,面向企业管理咨询从业者、流程优化工程师、数字化转型负责人及高校工商管理/信息管理专业师生。内容系统呈现了美的集团2013–2015年推进的端到端流程变革实践&#xf…

作者头像 李华
网站建设 2026/10/21 16:16:16

libigl几何处理库入门指南:安装配置、核心模块与实战避坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华