主臂控制

主臂控制示例使用 robot.master_left_arm / robot.master_right_arm。下面示例覆盖主臂状态读取、主臂控制模式切换、主臂关节小幅控制和主臂状态流读取。更完整的命令行示例可参考 examples/quanta_x1/master_arm_control.py,其中包含 --write 安全开关、目标关节位置、末端位姿和 TOPPRA 轨迹示例。

量子 1 号环境中,主臂写控制能力依赖机器人端是否接入主臂硬件、是否启动对应服务以及配置中是否启用写控制 topic;如果当前环境只提供只读数据,写接口会返回失败或 RPC 错误。

import time
from typing import Annotated
import typer

from x2robot import Robot, connect
from x2robot.sdk import JointPositions
from x2robot.sdk import ManipulatorControlMode, ManipulatorControlModeParam


def select_master_arm(robot: Robot, arm: str):
    if arm == "left":
        return robot.master_left_arm
    if arm == "right":
        return robot.master_right_arm
    raise ValueError("arm must be left or right")


def set_master_mode(master_arm, mode: str):
    if mode == "joint_pos":
        target_mode = ManipulatorControlMode.MANIPULATOR_JOINT_POSITIONS
    elif mode == "end_pose":
        target_mode = ManipulatorControlMode.MANIPULATOR_END_POSE
    else:
        raise ValueError("mode must be joint_pos or end_pose")

    result = master_arm.set_control_mode(ManipulatorControlModeParam(mode=target_mode))
    print(f"set_control_mode result: {result.is_success}, {result.error_message}")
    return result


def read_master_arm(master_arm):
    print(f"control_mode: {master_arm.get_control_mode()}")

    joint_state = master_arm.get_joint_states()
    print(f"joint names: {joint_state.name}")
    print(f"joint positions: {joint_state.position}")

    end_pose = master_arm.get_end_pose()
    print(f"end pose: {end_pose}")

    trigger = master_arm.get_gripper_position()
    print(f"trigger position: {trigger.position}")


def move_master_joint(master_arm, joint_index: int = 0, delta: float = 0.01):
    set_master_mode(master_arm, "joint_pos")

    joint_state = master_arm.get_joint_states()
    original = list(joint_state.position)
    target = list(original)
    target[joint_index] += delta

    result = master_arm.set_joint_positions(JointPositions(positions=target))
    print(f"set_joint_positions result: {result.is_success}, {result.error_message}")
    time.sleep(0.5)

    result = master_arm.set_joint_positions(JointPositions(positions=original))
    print(f"return result: {result.is_success}, {result.error_message}")


def stream_master_arm(master_arm, samples: int = 10):
    count = 0
    for joint_state in master_arm.get_joint_states_stream():
        print(joint_state)
        count += 1
        if count >= samples:
            break
        time.sleep(0.1)


def main(
    server: Annotated[str, typer.Option(help="server address")] = "192.168.10.1:50051",
    model: Annotated[str, typer.Option(help="robot model")] = "auto",
    arm: Annotated[str, typer.Option(help="left or right")] = "left",
    action: Annotated[str, typer.Option(help="read, move_joint, stream")] = "read",
):
    robot = connect(f"x2://{server}", model=model)
    master_arm = select_master_arm(robot, arm)

    if action == "read":
        read_master_arm(master_arm)
    elif action == "move_joint":
        print("请确认主臂周围安全,再执行写控制")
        if input("continue? (y/n): ").lower() == "y":
            move_master_joint(master_arm)
    elif action == "stream":
        stream_master_arm(master_arm)
    else:
        raise ValueError("action must be read, move_joint or stream")


if __name__ == "__main__":
    typer.run(main)