本文档详细说明每个机器人部分(part)可用的函数及其功能。文档按部分分类,每个部分内按功能(发送命令、获取状态、检查到达)组织。
- 手臂控制 (Arm Handler)
- 夹爪控制 (Gripper Handler)
- 头部控制
- 身体控制
- 统一接口方法
- 常量 (Constants)
- 运动生成功能 (motion_generation)
- 快速参考表
- 注意事项
手臂控制统一通过 left_arm_handler 和 right_arm_handler 访问。
# 左臂(始终可用)
interface.left_arm_handler
# 右臂(仅双臂模式)
interface.right_arm_handlersend_target_stamped.mp4
功能: 发送带坐标系信息的目标位姿。
参数:
frame_id(Optional[str]): 目标位姿所在坐标系,如arm_base、base_link、head_link2、left_eef;可省略,省略时回退到最近订阅到的self.frame_idpose(Optional[Pose]): 目标位姿
特点:
- 发布到
/left_target/stamped或/right_target/stamped - 会自动做 TF 变换,统一转换到
base_frame - 适合跨坐标系目标和相对位姿控制
- 目标位姿后续会通过
/left_current_target或/right_current_target订阅回来,用于到位判断 - 兼容两种调用方式:
send_target_stamped("frame_id", pose)和send_target_stamped(pose) - 如果省略
frame_id且当前还没有可用的默认self.frame_id,会抛出ValueError
示例:
from geometry_msgs.msg import Pose
# 使用其他坐标系
target_pose = Pose()
target_pose.position.x = 0.5
target_pose.position.y = 0.0
target_pose.position.z = 0.3
target_pose.orientation.w = 1.0
interface.left_arm_handler.send_target_stamped("head_link2", target_pose)
# 使用末端坐标系做相对运动
relative_pose = Pose()
relative_pose.position.x = 0.15
relative_pose.position.y = 0.0
relative_pose.position.z = 0.0
relative_pose.orientation.w = 1.0
interface.left_arm_handler.send_target_stamped("left_eef", relative_pose)
# 如果已经收到过 pose 订阅并缓存了 self.frame_id,也可以省略 frame_id
interface.left_arm_handler.send_target_stamped(relative_pose)send_target.mp4
功能: 发送不带坐标系信息的目标位姿。
参数:
pose(Pose): 已经位于base_frame坐标系下的目标位姿
特点:
- 直接发布到
/left_target或/right_target - 不做 TF 变换
- 只适合你已经明确知道目标位姿就在
base_frame下的场景
注意:
- 如果目标位姿不在
base_frame下,应使用send_target_stamped()
示例:
from geometry_msgs.msg import Pose
target_pose = Pose()
target_pose.position.x = 0.5
target_pose.position.y = 0.0
target_pose.position.z = 0.3
target_pose.orientation.w = 1.0
interface.left_arm_handler.send_target(target_pose)send_joint_position.mp4
功能: 发送关节位置命令(MoveJ)。
参数:
positions(List[float]): 目标关节角列表,单位为弧度
特点:
- 会自动切换到 MOVEJ 状态(FSM 命令 4)
- 发布到对应关节控制器话题,如
/ocs2_wbc_controller/target_joint_position/left - 关节数量必须与当前配置一致
异常:
ROS2NotConnectedError: 发布器未初始化ValueError: 初始化时未提供 FSM 命令回调函数
示例:
joint_positions = [0.0, 0.5, -1.57, 0.0, 1.57, 0.0]
interface.left_arm_handler.send_joint_positions(joint_positions)功能: 获取当前末端执行器实际位姿。
返回值:
Pose:当前位置和姿态,位于base_frame坐标系下None:数据不存在、过期或暂不可用
适用场景:
- 实时状态监控
- 保存初始位姿,用于后续相对运动
- 配合
check_arrival()观察当前状态
示例:
current_pose = interface.left_arm_handler.get_pose()
if current_pose:
print(f"当前位置: ({current_pose.position.x}, {current_pose.position.y}, {current_pose.position.z})")
print(
f"姿态: ({current_pose.orientation.x}, {current_pose.orientation.y}, "
f"{current_pose.orientation.z}, {current_pose.orientation.w})"
)功能: 获取当前目标位姿。
返回值:
Pose:当前目标位姿,位于base_frame坐标系下None:尚未设置目标,或未配置目标位姿订阅话题
说明:
- 目标位姿来自
/left_current_target或/right_current_target - 常用于调试目标是否已正确下发
check_arrival()内部也会使用这个目标位姿进行比较
示例:
target_pose = interface.left_arm_handler.get_target_pose()
if target_pose:
print(f"目标位置: ({target_pose.position.x}, {target_pose.position.y}, {target_pose.position.z})")
else:
print("未设置目标位姿或目标位姿话题未配置")功能: 获取当前目标位姿对应的 frame_id。
返回值:
str:当前目标位姿的坐标系 IDNone:未配置目标位姿订阅话题
说明:
- 该值与
get_target_pose()配套使用 - 常用于确认目标位姿最终使用的是哪个参考坐标系
- 记录数据时也可以一起保存
示例:
frame_id = interface.left_arm_handler.get_frame_id()
if frame_id is not None:
print(f"当前目标 frame_id: {frame_id}")
else:
print("未配置目标位姿订阅话题,无法获取 frame_id")check_arrival(pose_threshold: float | None = None, orient_threshold: float | None = None) -> Dict[str, Any] ⭐ 最常用
功能: 检查手臂是否到达目标位姿。
参数:
pose_threshold(float | None): 位置距离阈值,单位米;为None时使用config.pose_position_thresholdorient_threshold(float | None): 姿态距离阈值;为None时使用config.pose_orientation_threshold
返回值:
{
"arrived": bool,
"distance": float,
"position_distance": float,
"orientation_distance": float,
"status_message": str,
}特点:
- 内部会自动读取当前位姿
get_pose()和目标位姿get_target_pose() - 比较的两个位姿都在
base_frame下 - 位置距离为欧氏距离,姿态距离基于四元数计算
- 若未传阈值,则使用配置中的默认阈值
示例:
# 使用默认阈值
result = interface.left_arm_handler.check_arrival()
if result["arrived"]:
print("手臂已到达目标位置")
# 使用自定义阈值
result = interface.left_arm_handler.check_arrival(
pose_threshold=0.05,
orient_threshold=0.08,
)
print(f"位置距离: {result['position_distance']:.4f} 米")
print(f"姿态距离: {result['orientation_distance']:.4f}")
# 一个完整流程
target_pose = Pose()
target_pose.position.x = 0.15
target_pose.position.y = 0.0
target_pose.position.z = 0.0
target_pose.orientation.w = 1.0
interface.left_arm_handler.send_target_stamped("left_eef", target_pose)
while True:
result = interface.left_arm_handler.check_arrival()
if result["arrived"]:
print("到达目标!")
break
print(f"距离目标: {result['position_distance']:.4f} 米")
time.sleep(0.1)这些方法通过 ROS2RobotInterface 直接访问,用于同时控制左臂和右臂。
功能: 发送双臂目标 pose(仅双臂模式)
参数:
left_pose(Pose): 左臂目标 pose(在frame_id指定的坐标系下)right_pose(Pose): 右臂目标 pose(在frame_id指定的坐标系下)frame_id(str, 可选): 坐标系 ID;省略时默认"arm_base"(见下方说明)
说明:
- 坐标系默认值:不传
frame_id时默认使用"arm_base"。若你的机器人 TF 或末端位姿话题中没有名为arm_base的坐标系,必须显式传入与实际一致的frame_id(例如与left_arm_handler.get_frame_id()或当前 pose 消息的header.frame_id一致)。 - 发布到
/dual_target/stampedtopic(使用nav_msgs/Path消息类型,包含2个PoseStamped:第一个是左臂,第二个是右臂) - 内部实现:直接发布到
/dual_target/stampedtopic,不调用 handler 的方法 - 目标位姿会通过话题订阅获取(
/left_current_target和/right_current_target),用于到达判断 - 可以使用
left_arm_handler.check_arrival()和right_arm_handler.check_arrival()分别检查到达状态
示例:
from geometry_msgs.msg import Pose
left_pose = Pose()
left_pose.position.x = 0.5
left_pose.position.y = 0.2
left_pose.position.z = 0.3
left_pose.orientation.w = 1.0
right_pose = Pose()
right_pose.position.x = 0.5
right_pose.position.y = -0.2
right_pose.position.z = 0.3
right_pose.orientation.w = 1.0
# 发送双臂目标(pose 在 frame_id 指定的坐标系下,接收端会进行坐标转换)
interface.send_dual_arm_target_stamped(left_pose, right_pose, frame_id="arm_base")
# 检查到达状态
left_result = interface.left_arm_handler.check_arrival()
right_result = interface.right_arm_handler.check_arrival()
if left_result['arrived'] and right_result['arrived']:
print("双臂都已到达目标位置")send_dual_arm_joint_positions(left_arm_positions: List[float], right_arm_positions: List[float]) -> None ⭐ 新增
功能: 发送双臂关节位置命令(MoveJ 模式,统一 topic 控制)
参数:
left_arm_positions(List[float]): 左臂关节位置列表(弧度)right_arm_positions(List[float]): 右臂关节位置列表(弧度)
说明:
- 同时控制左臂和右臂的所有关节,发布到统一的 topic
- 自动检测控制器类型:
- WBC 控制器 (
/ocs2_wbc_controller/target_joint_position):需要body_joints + left_arm_joints + right_arm_joints(18个关节)- 自动从当前关节状态获取身体关节位置
- 如果没有身体关节数据,使用默认值(4个零值)
- ARM 控制器 (
/ocs2_arm_controller/target_joint_position):只需要left_arm_joints + right_arm_joints(14个关节)
- WBC 控制器 (
- 自动切换到 MOVEJ 状态(FSM 命令 4)
- 左臂和右臂的关节数量必须相同
- 关节顺序:WBC 控制器为
[body_joint1-4] + [left_joint1-7] + [right_joint1-7],ARM 控制器为[left_joint1-7] + [right_joint1-7]
Raises:
ROS2NotConnectedError: 如果接口未连接或发布器未初始化ValueError: 如果双臂模式未启用、参数无效或左右臂关节数量不一致
示例:
# 左臂7个关节,右臂7个关节
left_positions = [0.0, 0.5, -1.57, 0.0, 1.57, 0.0, 0.0]
right_positions = [0.0, -0.5, 1.57, 0.0, -1.57, 0.0, 0.0]
# 发送双臂关节位置(自动检测控制器类型并添加身体关节)
interface.send_dual_arm_joint_positions(left_positions, right_positions)
# WBC 控制器会自动添加身体关节:
# 最终发送:[body_joint1-4] + [left_joint1-7] + [right_joint1-7] = 18个关节
# ARM 控制器只发送双臂关节:
# 最终发送:[left_joint1-7] + [right_joint1-7] = 14个关节注意事项:
⚠️ WBC 控制器会自动添加身体关节,确保身体关节数据可用(从/joint_states获取)⚠️ 如果使用 WBC 控制器且没有身体关节数据,会使用默认值(4个零值),可能导致意外运动
夹爪控制通过 left_gripper_handler 和 right_gripper_handler 访问。
⚠️ 重要:使用夹爪前必须正确配置行程范围
ROS2RobotInterfaceConfig中的以下两个参数必须根据实际硬件设置,默认值仅为示例:config.gripper_min_position = 0.0 config.gripper_max_position = 0.0384这两个值会直接影响:
send_joint_positions()的位置限幅send_position_percent()的百分比到实际行程换算get_target_position()的内部目标值check_arrival()的到位检测结果不同夹爪型号的行程范围差异较大,请在使用前替换为实测值。
# 左夹爪(单臂模式下也使用这个)
interface.left_gripper_handler
# 右夹爪(仅双臂模式)
interface.right_gripper_handlergripper_command.mp4
功能: 发送夹爪开关控制命令,使用 target_command 话题。
参数:
target_value(int):0表示关闭,1表示打开
特点:
- 单臂模式统一使用
interface.left_gripper_handler.send_target_command(...) - 使用
std_msgs/Int32消息类型 - 会自动订阅相同话题并同步
is_open状态 - 如果参数不是
0或1,会抛出ValueError
自动检测到的话题:
- 双臂灵巧手:
/left_hand_controller/target_command、/right_hand_controller/target_command - 双臂夹爪:
/left_gripper_controller/target_command、/right_gripper_controller/target_command - 单臂灵巧手:
/hand_controller/target_command - 单臂夹爪:
/gripper_controller/target_command
示例:
# 打开
interface.left_gripper_handler.send_target_command(1)
# 关闭
interface.left_gripper_handler.send_target_command(0)
# 根据当前状态切换
current_state = interface.left_gripper_handler.is_open
target_value = 0 if current_state else 1
interface.left_gripper_handler.send_target_command(target_value)gripper_position.mp4
功能: 发送夹爪实际行程位置命令,使用位置控制话题。
参数:
position(float): 目标行程位置值
特点:
- 单臂模式统一使用
interface.left_gripper_handler.send_joint_positions(...) - 位置会自动限制在
gripper_min_position和gripper_max_position之间 - 会更新内部
target_position,并清空位置历史 - 这里的
position是实际行程值,不是百分比
示例:
interface.left_gripper_handler.send_joint_positions(0.01)gripper_percentage.mp4
功能: 发送夹爪百分比位置控制命令,使用 target_percent 话题。
参数:
percent(float): 百分比目标值,范围0.0到1.0
特点:
- 单臂模式统一使用
interface.left_gripper_handler.send_position_percent(...) - 使用
std_msgs/Float64消息类型 - 传入
int会自动转换为float - 超出
[0.0, 1.0]会抛出ValueError - 如果
target_percent话题未检测到,会抛出ROS2NotConnectedError - 会按
gripper_min_position ~ gripper_max_position线性换算并更新内部target_position
自动检测到的话题:
- 双臂模式:
/left_gripper_controller/target_percent、/right_gripper_controller/target_percent - 单臂模式:
/left_gripper_controller/target_percent
可用性判断:
if interface.left_gripper_handler.target_percent_pub is not None:
interface.left_gripper_handler.send_position_percent(0.5)示例:
# 完全关闭
interface.left_gripper_handler.send_position_percent(0.0)
# 中间位置
interface.left_gripper_handler.send_position_percent(0.5)
# 完全打开
interface.left_gripper_handler.send_position_percent(1.0)
# 双臂模式右夹爪
interface.right_gripper_handler.send_position_percent(0.75)夹爪当前位置从 get_joint_state(categorized=True) 返回的分类字典中读取。
单个夹爪分类数据结构:
{
"names": ["left_gripper_joint"],
"positions": [0.012],
"velocities": [0.0],
"efforts": [0.0],
}单臂模式:
joint_state = interface.get_joint_state(categorized=True)
if joint_state:
gripper_data = joint_state.get("gripper", {})
current_pos = gripper_data.get("positions", [None])[0]
print(f"夹爪当前位置: {current_pos}")双臂模式:
joint_state = interface.get_joint_state(categorized=True)
if joint_state:
left_pos = joint_state.get("left_gripper", {}).get("positions", [None])[0]
right_pos = joint_state.get("right_gripper", {}).get("positions", [None])[0]
print(f"左夹爪: {left_pos}, 右夹爪: {right_pos}")通用写法:
is_dual_arm = interface.config.right_end_effector_pose_topic is not None
joint_state = interface.get_joint_state(categorized=True)
if joint_state:
key = "left_gripper" if is_dual_arm else "gripper"
current_pos = joint_state.get(key, {}).get("positions", [None])[0]注意: 如果
current_pos为None,说明/joint_states里还没有夹爪关节数据,通常在连接后等待 1~2 秒即可。
功能: 获取当前内部维护的夹爪目标位置。
返回值:
float: 当前目标位置None: 尚未设置目标
说明:
- 这是
GripperHandler内部维护的状态,不是单独从外部话题订阅得到的 - 以下操作会更新它:
send_joint_positions(position):更新为限幅后的实际位置send_position_percent(percent):先换算为实际位置,再更新send_target_command(0/1)对应话题回调:关闭时更新为gripper_min_position,打开时更新为gripper_max_position
check_arrival()会使用该值与当前位置比较,判断是否到达目标
示例:
target_position = interface.left_gripper_handler.get_target_position()
if target_position is not None:
print(f"目标位置: {target_position}")功能: 检查夹爪是否到达目标位置。
参数:
current_position(Optional[float]): 当前夹爪位置,需要从get_joint_state(categorized=True)中提取threshold(float | None): 可选的距离阈值;为None时使用config.gripper_position_threshold
返回值:
{
"arrived": bool,
"distance": float,
}特点:
- 需要手动传入当前位置
- 打开方向主要按距离阈值判断
- 关闭方向除了距离阈值,还会结合位置历史的稳定性判断,适合夹住物体时使用
示例:
categorized_state = interface.get_joint_state(categorized=True)
gripper_data = categorized_state.get("gripper", {}) # 单臂模式
# gripper_data = categorized_state.get("left_gripper", {}) # 双臂模式
current_position = gripper_data.get("positions", [None])[0]
if current_position is not None:
result = interface.left_gripper_handler.check_arrival(current_position)
if result["arrived"]:
print("夹爪已到达目标位置")
result = interface.left_gripper_handler.check_arrival(
current_position=current_position,
threshold=0.005,
)头部控制通过 ROS2RobotInterface 的直接方法访问。
功能: 发送头部关节位置命令
参数:
positions(List[float]): 目标关节位置列表(弧度)
说明:
- 发布到头部关节控制器 topic(如
/head_joint_controller/target_joint_position) - 会自动保存目标位置(用于
check_arrive()) - 关节数量必须与配置一致
示例:
# 发送头部关节位置(例如:2个关节)
head_positions = [0.5, -0.3] # 弧度
interface.send_head_joint_positions(head_positions)功能: 获取关节状态(包括头部关节)
参数:
categorized(bool): 是否返回分类后的状态,默认 False
返回值:
原始模式(categorized=False):
{
'names': List[str], # 关节名称列表
'positions': List[float], # 关节位置列表(弧度)
'velocities': List[float], # 关节速度列表(弧度/秒)
'efforts': List[float], # 关节力矩列表
'timestamp': float # 时间戳
}分类模式(categorized=True):
{
'head': {...}, # 头部关节
'body': {...}, # 身体关节
'left_arm': {...}, # 左臂关节(双臂模式)
'right_arm': {...}, # 右臂关节(双臂模式)
'arm': {...}, # 手臂关节(单臂模式)
'left_gripper': {...}, # 左夹爪(双臂模式)
'right_gripper': {...}, # 右夹爪(双臂模式)
'gripper': {...}, # 夹爪(单臂模式)
'other': {...}, # 其他关节
'timestamp': float
}示例:
# 获取分类后的关节状态
categorized_state = interface.get_joint_state(categorized=True)
if categorized_state:
head_data = categorized_state.get('head', {})
head_positions = head_data.get('positions', [])功能: 检查头部是否到达目标位置
参数:
part(str): 设置为'head'position_threshold(Optional[float]): 关节位置阈值,如果为 None 则使用默认值
返回值:
{
'arrived': bool, # 是否到达
'distance': float # 距离
}示例:
# 检查头部到达状态
result = interface.check_arrive('head')
if result['arrived']:
print("头部已到达目标位置")
# 使用自定义阈值
result = interface.check_arrive('head', position_threshold=0.01)身体控制通过 ROS2RobotInterface 的直接方法访问。
功能: 发送身体关节位置命令
参数:
positions(List[float]): 目标关节位置列表(弧度)
说明:
- 发布到身体关节控制器 topic(如
/body_joint_controller/target_joint_position) - 会自动保存目标位置(用于
check_arrive()) - 关节数量必须与配置一致
示例:
# 发送身体关节位置(例如:4个关节)
body_positions = [0.0, 0.0, 0.0, 0.0] # 弧度
interface.send_body_joint_positions(body_positions)功能: 获取关节状态(包括身体关节)
详见 头部控制 - 获取状态 部分,使用 categorized_state.get('body', {}) 获取身体关节数据。
功能: 检查身体是否到达目标位置
参数:
part(str): 设置为'body'position_threshold(Optional[float]): 关节位置阈值,如果为 None 则使用默认值
返回值:
{
'arrived': bool, # 是否到达
'distance': float # 距离
}示例:
# 检查身体到达状态
result = interface.check_arrive('body')
if result['arrived']:
print("身体已到达目标位置")这些方法通过 ROS2RobotInterface 直接访问,可以操作多个部分。
功能: 发送 FSM(有限状态机)状态切换命令
参数:
command(int): FSM 命令值1: HOME 状态2: HOLD 状态3: OCS2 状态(笛卡尔空间控制)4: MOVEJ 状态(关节空间控制)
说明:
- 发布到
/fsm_commandtopic - 用于切换机器人的控制模式
示例:
# 切换到 HOME 状态
interface.send_fsm_command(1)
# 切换到 OCS2 状态(用于 pose 控制)
interface.send_fsm_command(3)
# 切换到 MOVEJ 状态(用于关节控制)
interface.send_fsm_command(4)功能: 获取当前 FSM 命令值。
返回值:
1: HOME2: HOLD3: OCS24: MOVEJ
功能: 获取当前 FSM 状态名。
返回值:
"HOME"/"HOLD"/"OCS2"/"MOVEJ"
示例:
cmd = interface.get_fsm_command()
state = interface.get_fsm_state()
print(f"FSM command={cmd}, state={state}")功能: 获取所有关节状态
参数:
categorized(bool): 是否返回分类后的状态,默认 False
返回值:
原始模式(categorized=False):
{
'names': List[str], # 关节名称列表
'positions': List[float], # 关节位置列表(弧度)
'velocities': List[float], # 关节速度列表(弧度/秒)
'efforts': List[float], # 关节力矩列表
'timestamp': float # 时间戳
}分类模式(categorized=True):
{
'left_arm': { # 左臂关节(双臂模式)
'names': List[str],
'positions': List[float],
'velocities': List[float],
'efforts': List[float]
},
'right_arm': {...}, # 右臂关节(双臂模式)
'left_gripper': {...}, # 左夹爪(双臂模式)
'right_gripper': {...}, # 右夹爪(双臂模式)
'arm': {...}, # 手臂关节(单臂模式)
'gripper': {...}, # 夹爪(单臂模式)
'head': {...}, # 头部关节
'body': {...}, # 身体关节
'other': {...}, # 其他关节
'timestamp': float
}示例:
# 获取原始关节状态
joint_state = interface.get_joint_state()
if joint_state:
print(f"关节数: {len(joint_state['names'])}")
print(f"位置: {joint_state['positions']}")
# 获取分类后的关节状态
categorized_state = interface.get_joint_state(categorized=True)
if categorized_state:
left_arm_data = categorized_state.get('left_arm', {})
gripper_data = categorized_state.get('gripper', {})
head_data = categorized_state.get('head', {})功能: 发送左手(灵巧手)关节位置命令。
说明:
- 发布到
left_hand_joint_controller_topic(自动检测或手动配置) - 适用于需要独立控制灵巧手每个关节的位置场景
功能: 发送右手(灵巧手)关节位置命令(双臂模式)。
示例:
# 例如每只手 6 个关节
interface.send_left_hand_joint_positions([0.0, 0.3, 0.5, 0.2, 0.1, 0.0])
interface.send_right_hand_joint_positions([0.0, 0.3, 0.5, 0.2, 0.1, 0.0])功能: 获取左臂末端执行器的当前位姿。
返回值:
Pose对象:左臂末端执行器的当前位置和姿态(在base_frame坐标系下)None:如果位姿不可用
说明:
- 返回的位姿在
base_frame坐标系下
示例:
# 获取左臂末端执行器位姿
pose = interface.left_arm_handler.get_pose()
if pose:
print(f"位置: ({pose.position.x}, {pose.position.y}, {pose.position.z})")
print(f"姿态: ({pose.orientation.x}, {pose.orientation.y}, {pose.orientation.z}, {pose.orientation.w})")
else:
print("位姿不可用")功能: 获取右臂末端执行器的当前位姿(双臂模式)。
返回值:
Pose对象:右臂末端执行器的当前位置和姿态(在base_frame坐标系下)None:如果位姿不可用
说明:
- 仅在双臂模式下可用
- 返回的位姿在
base_frame坐标系下
示例:
# 获取右臂末端执行器位姿(双臂模式)
pose = interface.right_arm_handler.get_pose()
if pose:
print(f"位置: ({pose.position.x}, {pose.position.y}, {pose.position.z})")
else:
print("位姿不可用")功能: 获取最后一次接收到关节状态消息的时间戳
返回值:
float:时间戳(秒,系统时间)None:如果未连接或尚未接收到关节状态消息
说明:
- 返回的是系统时间(不是消息中的时间戳)
- 用于检测关节状态数据是否已更新(即使超时检查被禁用)
- 如果接口未连接,返回
None而不是抛出异常
示例:
# 获取最后接收关节状态的时间
last_time = interface.get_last_joint_state_time()
if last_time:
print(f"最后接收时间: {last_time}")
current_time = time.time()
time_since_last = current_time - last_time
print(f"距离上次接收: {time_since_last:.2f} 秒")
else:
print("未连接或尚未接收到关节状态")功能: 通过 topic 发送双臂路径(nav_msgs/Path)。
说明:
- 要求双臂模式
- 左右轨迹点数量需要一致(按阶段一一对应)
- 可传入
Pose或PoseStamped - 会清空旧 target 缓存,避免到达判断误判
execute_path(left_poses, right_poses, trajectory_duration: float = 0.0, frame_id: Optional[str] = None) -> bool
功能: 通过 ExecutePath service 执行双臂路径并等待服务响应。
说明:
- 不要求左右轨迹点数量一致(支持不等长路径)
返回值:
bool: 服务返回的执行成功标志
示例:
from geometry_msgs.msg import Pose
def make_pose(x, y, z):
p = Pose()
p.position.x = x
p.position.y = y
p.position.z = z
p.orientation.w = 1.0
return p
# 左右路径点数量可以不一致
left_poses = [
make_pose(0.40, 0.20, 0.30),
make_pose(0.45, 0.20, 0.32),
make_pose(0.50, 0.20, 0.34),
]
right_poses = [
make_pose(0.40, -0.20, 0.30),
make_pose(0.48, -0.20, 0.33),
]
ok = interface.execute_path(
left_poses=left_poses,
right_poses=right_poses,
trajectory_duration=2.0,
frame_id="arm_base",
)
print(f"execute_path success: {ok}")功能: 发送多路点关节轨迹(JointTrajectory),支持单臂/双臂统一接口。
示例:
# 左臂轨迹示例
joint_names = ["left_joint1", "left_joint2", "left_joint3", "left_joint4", "left_joint5", "left_joint6", "left_joint7"]
waypoints = [
[0.0, 0.3, -0.2, 0.0, 1.2, 0.0, 0.0],
[0.1, 0.4, -0.3, 0.1, 1.1, 0.1, 0.0],
]
interface.send_joint_trajectory(joint_names, waypoints)check_arrive(part: Optional[str] = None, position_threshold: Optional[float] = None) -> Optional[Dict[str, Any]]
功能: 统一检查多个部分的到达状态
参数:
part(Optional[str]): 要检查的部分,可选值:None: 检查所有部分'left_arm': 左臂'right_arm': 右臂(双臂模式)'left_gripper': 左夹爪'right_gripper': 右夹爪(双臂模式)'head': 头部'body': 身体'arm': 手臂(单臂模式,会自动转换为 'left_arm')'gripper': 夹爪(单臂模式,会自动转换为 'left_gripper')
position_threshold(Optional[float]): 关节位置阈值(仅用于 head/body),如果为 None 则使用默认值
说明:
- 手臂和夹爪使用各自 handler 的默认阈值(可通过直接调用 handler 的方法来自定义)
- 头部和身体使用
position_threshold参数或默认的self.position_threshold - 如果接口未连接,返回
None而不是抛出异常
返回值:
None:如果接口未连接
检查单个部分:
{
'arrived': bool, # 是否到达
'distance': float # 距离
}检查所有部分:
{
'left_arm': {
'arrived': bool,
'distance': float,
'position_distance': float,
'orientation_distance': float,
'status_message': str
},
'right_arm': {...}, # 双臂模式
'left_gripper': {
'arrived': bool,
'distance': float
},
'right_gripper': {...}, # 双臂模式
'head': {
'arrived': bool,
'distance': float
},
'body': {
'arrived': bool,
'distance': float
}
}示例:
# 检查单个部分
result = interface.check_arrive('left_arm')
if result and result['arrived']:
print("左臂已到达")
# 检查所有部分
results = interface.check_arrive()
if results:
print(f"左臂到达: {results.get('left_arm', {}).get('arrived', False)}")
print(f"夹爪到达: {results.get('gripper', {}).get('arrived', False)}")
else:
print("接口未连接")
# 使用自定义阈值(仅用于 head/body)
results = interface.check_arrive(
part='head',
position_threshold=0.01 # 头部关节位置阈值
)
# 如果需要自定义手臂或夹爪的阈值,直接调用 handler 的方法
arm_result = interface.left_arm_handler.check_arrival(pose_threshold=0.05)
gripper_result = interface.left_gripper_handler.check_arrival(current_position, threshold=0.005)wait_until_arrive(part: str = "arm", timeout: float = 3.0, poll_period: float = 0.05, position_threshold: Optional[float] = None, ...) -> Dict[str, Any]
功能: 轮询 check_arrive() 并在超时前等待指定部分到达目标,替代固定 sleep。
返回值:
{
"arrived": bool,
"elapsed": float,
"result": dict | None
}示例:
wait_result = interface.wait_until_arrive(
part="left_arm",
timeout=5.0,
poll_period=0.05,
)
if not wait_result["arrived"]:
print(f"超时未到达,耗时: {wait_result['elapsed']:.2f}s")lookup_transform(target_frame: str, source_frame: str, timeout: Optional[float] = None) -> Optional[TransformStamped]
功能: 查询两个坐标系之间的变换关系
参数:
target_frame(str): 参考坐标系(在这个坐标系下观察)source_frame(str): 被查询的坐标系(要查询它的位置)timeout(Optional[float]): 可选超时时间(秒),如果为None则立即返回(不等待)
返回值:
TransformStamped对象:表示source_frame相对于target_frame的位姿(source_frame在target_frame坐标系下的位姿)None:如果查询失败
示例:
# 查询 left_link6 相对于 head_link2 的位置
# 返回结果表示:left_link6 在 head_link2 坐标系下的位姿
transform = interface.lookup_transform("head_link2", "left_link6")
if transform:
trans = transform.transform.translation
rot = transform.transform.rotation
print(f"平移: ({trans.x:.4f}, {trans.y:.4f}, {trans.z:.4f}) 米")
print(f"旋转: ({rot.x:.4f}, {rot.y:.4f}, {rot.z:.4f}, {rot.w:.4f})")transform_pose(pose: Pose, source_frame: str, target_frame: str, timeout: Optional[float] = None) -> Optional[Pose]
功能: 将坐标从一个坐标系转换到另一个坐标系
参数:
pose(Pose): 要转换的 Pose(在source_frame坐标系下)source_frame(str): 源坐标系(pose当前所在的坐标系)target_frame(str): 目标坐标系(转换后的pose所在的坐标系)timeout(Optional[float]): 可选超时时间(秒),如果为None则立即返回(不等待)
返回值:
Pose对象:转换后的 Pose(在target_frame坐标系下)None:如果转换失败
示例:
from geometry_msgs.msg import Pose
# 将 pose 从 head_link2 坐标系转换到 arm_base 坐标系
pose_in_head = Pose()
pose_in_head.position.x = 0.5 # 在 head_link2 坐标系下
pose_in_head.position.y = 0.0
pose_in_head.position.z = 0.3
pose_in_head.orientation.w = 1.0
# 参数说明:
# - pose_in_head: 要转换的 Pose
# - "head_link2": 源坐标系(pose_in_head 当前所在的坐标系)
# - "arm_base": 目标坐标系(转换后的 pose 所在的坐标系)
pose_in_base = interface.transform_pose(pose_in_head, "head_link2", "arm_base")
if pose_in_base:
print(f"转换后的位置: ({pose_in_base.position.x:.4f}, {pose_in_base.position.y:.4f}, {pose_in_base.position.z:.4f})")功能: 查询当前运行的 ROS 2 节点列表
说明:
- 此方法可以在连接或未连接状态下使用
- 如果接口已连接,会使用现有节点进行查询
- 如果未连接,会创建一个临时节点来查询
返回值:
List[Dict[str, str]]: 节点信息列表,每个字典包含:'name': 节点名称(不含命名空间)'namespace': 节点命名空间'full_name': 完整节点名称(命名空间 + 名称)
示例:
# 查询节点列表
nodes = interface.list_nodes()
print(f"当前运行的节点数量: {len(nodes)}")
for node in nodes:
print(f"节点: {node['full_name']}")
print(f" 名称: {node['name']}")
print(f" 命名空间: {node['namespace']}")
# 查找特定节点
target_node = "ros2_robot_interface"
matching_nodes = [n for n in nodes if target_node in n['name']]
if matching_nodes:
print(f"找到节点: {matching_nodes[0]['full_name']}")功能: 获取最近一次接收到的 robot_description(URDF XML 字符串)。
功能: 判断是否已接收到 robot_description。
功能: 查询指定节点的可动态参数及其信息。
功能: 批量设置指定节点参数。
示例:
node_name = "/my_controller"
params = interface.list_node_parameters(node_name)
print(f"可配置参数数量: {len(params)}")
ok = interface.set_node_parameters(node_name, {
"trajectory_duration": 2.0,
"enable_safety_check": True,
})
print(f"参数设置结果: {ok}")可直接从包顶层导入 FSM 状态常量:
FSM_HOME = 1FSM_HOLD = 2FSM_OCS2 = 3FSM_MOVEJ = 4
from ros2_robot_interface import FSM_HOME, FSM_OCS2
interface.send_fsm_command(FSM_OCS2)包顶层已导出可组合的动作序列构建与执行工具,适合 pick/place/handover 这类阶段化任务。
ArmSide:LEFT/RIGHTSendMode:UNSTAMPED/STAMPED/DUAL_ARM_STAMPEDGripperMode:JOINT_POSITION/TARGET_COMMANDArmTarget: 单臂目标(Pose + gripper)StageTarget: 单阶段目标(可含 left/right)
build_single_arm_pick_sequence(...)build_single_arm_place_sequence(...)build_single_arm_return_home_sequence(...)assign_to_arm(sequence, side)compose_bimanual_synchronized_sequence(left_sequence, right_sequence)build_handover_sequence(...)execute_stage_sequence(interface=..., sequence=..., ...)
from ros2_robot_interface import (
ArmSide,
SendMode,
GripperMode,
build_single_arm_pick_sequence,
assign_to_arm,
execute_stage_sequence,
)
pick_seq = build_single_arm_pick_sequence(
target_pose=target_pose,
approach_clearance=0.12,
grasp_clearance=0.02,
grasp_orientation=(0.0, 1.0, 0.0, 0.0),
gripper_open=1.0,
gripper_closed=0.0,
)
stages = assign_to_arm(pick_seq, ArmSide.LEFT)
execute_stage_sequence(
interface=interface,
sequence=stages,
send_mode=SendMode.STAMPED,
gripper_mode=GripperMode.TARGET_COMMAND,
arrival_timeout=3.0,
arrival_poll=0.05,
time_now_fn=time.monotonic,
sleep_fn=time.sleep,
gripper_action_wait=0.2,
)| 部分 | Handler 访问 | 发送命令 | 获取状态 | 检查到达 |
|---|---|---|---|---|
| 左臂 | left_arm_handler |
send_target_stamped() ⭐send_target()send_joint_positions() |
get_pose() ⭐get_target_pose() |
check_arrival() ⭐ |
| 右臂 | right_arm_handler |
send_target_stamped() ⭐send_target()send_joint_positions() |
get_pose() ⭐get_target_pose() |
check_arrival() ⭐ |
| 左夹爪 | left_gripper_handler |
send_target_command() ⭐send_joint_positions() |
get_target_position() |
check_arrival() |
| 右夹爪 | right_gripper_handler |
send_target_command() ⭐send_joint_positions() |
get_target_position() |
check_arrival() |
| 头部 | 直接方法 | send_head_joint_positions() |
get_joint_state() |
check_arrive('head') |
| 身体 | 直接方法 | send_body_joint_positions() |
get_joint_state() |
check_arrive('body') |
-
线程安全:所有方法都是线程安全的,可以在多线程环境中使用。
-
连接检查:
- 状态获取方法(如
left_arm_handler.get_pose(),get_joint_state(),check_arrive()等):如果接口未连接或数据不可用,可能返回None,调用者需要检查返回值 - 命令发送方法(如
left_arm_handler.send_target(),send_fsm_command()等):如果接口未连接,会抛出ROS2NotConnectedError异常
- 状态获取方法(如
-
数据可用性:
get_pose(),get_joint_state()等状态获取方法可能返回None(未连接、数据未到达或过期)check_arrive()在未连接时返回None,需要检查返回值- 夹爪的
check_arrival()需要手动传入当前位置 - 手臂的
get_target_pose()需要配置目标位置话题(/left_current_target或/right_current_target)
-
坐标系转换:
send_target_stamped()会自动进行 TF 转换到base_framesend_target()不进行转换,需要确保传入的 pose 已在base_frame下get_pose()和get_target_pose()返回的位姿都在base_frame坐标系下check_arrival()比较的两个位姿都在base_frame下,因此可以安全比较
-
函数选择建议:
- 推荐使用
send_target_stamped()而不是send_target(),因为它支持坐标系转换,更灵活 - 使用
get_pose()获取当前实际位姿,用于状态监控或计算相对运动 - 使用
get_target_pose()查询已设置的目标位姿,主要用于调试 check_arrival()会自动调用get_pose()和get_target_pose(),通常不需要手动调用这两个函数
- 推荐使用
-
默认阈值(可在配置中修改):
- 手臂位置阈值:
config.pose_position_threshold(默认 0.05 米) - 手臂姿态阈值:
config.pose_orientation_threshold(默认 0.1) - 夹爪位置阈值:
config.gripper_position_threshold(默认 0.01) - 头部/身体关节阈值:
config.position_threshold(默认 0.05 弧度)
- 手臂位置阈值:
-
双臂模式检测:
- 如果配置了
right_end_effector_pose_topic,会自动检测为双臂模式 - 单臂模式下,
'arm'和'gripper'会自动转换为'left_arm'和'left_gripper'
- 如果配置了
-
目标位置获取:
- 手臂的目标位置通过话题订阅获取(
/left_current_target或/right_current_target) - 如果未配置这些话题,
get_target_pose()和check_arrival()将无法正常工作
- 手臂的目标位置通过话题订阅获取(