使用你的手机(iOS 或 Android)控制机器人。
本指南将介绍:
要使用手机控制机器人,请使用以下命令安装相关依赖:
pip install lerobot[phone]
teleop 包(WebXR)。当你启动 Python 进程时,它会打印一个本地 URL。在手机上打开该链接,点击 Start,然后使用 Move 流式传输位姿。链接:
teleopB1 启用 teleoperation,松开停止。第一次按下会捕获一个参考位姿。Move 按钮,松开停止。第一次按下会捕获一个参考位姿。A3 作为速度输入控制 gripper。A 和 B 的作用类似于递增/递减(A 打开,B 关闭)。你可以在 GripperVelocityToJoint 步骤中调整速度。
修改示例,在 PhoneConfig 中使用 PhoneOS.IOS 或 PhoneOS.ANDROID。各平台的 API 完全相同,只是输入源不同。所有示例都在 examples/ 下,并具有 phone_so100_*.py 变体。
teleoperation 示例:
from lerobot.teleoperators.phone import Phone, PhoneConfig
from lerobot.teleoperators.phone.config_phone import PhoneOS
teleop_config = PhoneConfig(phone_os=PhoneOS.IOS) # or PhoneOS.ANDROID
teleop_device = Phone(teleop_config)创建 Phone(teleop_config) 并调用 connect() 时,会自动提示 calibration。以上述方向握住手机,然后:
B1 以捕获参考位姿。Move 按钮以捕获参考位姿。为什么要 calibration?我们捕获当前位姿,以便后续位姿在机器人对齐的坐标系中表示。当你再次按下按钮启用控制时,会重新捕获位置,以避免手机在禁用期间被移动时产生漂移。
运行其中一个示例脚本以进行 teleoperation、录制 dataset、回放 dataset 或评估 policy。
所有脚本都假定你已配置好机器人(例如 SO-100 follower arm)并设置了正确的串口。
此外,你需要将机器人的 URDF 复制到示例文件夹中。对于本教程中的示例(使用 SO100/SO101),将 SO-ARM100 仓库中的 SO101 文件夹复制到 examples/phone_to_so100/ 目录中,使 URDF 文件路径变为 examples/phone_to_so100/SO101/so101_new_calib.urdf。
运行此示例进行 teleoperation:
cd examples/phone_to_so100
python teleoperate.py运行示例后:
此外,你可以通过编辑示例中展示的处理器步骤来自定义映射或安全限制。你还可以通过修改输入和运动学步骤来重新映射输入(例如使用不同的模拟输入)或将流水线适配到其他机器人(例如 LeKiwi)。更多内容请参阅机器人与 teleoperator 的处理器指南。
运行此示例以录制 dataset,它会保存绝对的 end-effector observation 和 action:
cd examples/phone_to_so100
python record.py运行此示例回放录制的 episode:
cd examples/phone_to_so100
python replay.py运行此示例评估预训练 policy:
cd examples/phone_to_so100
python evaluate.py运动学用于多个步骤。我们使用 Placo,它是 Pinocchio 的封装,用于处理我们的运动学。我们通过传入机器人的 URDF 和目标坐标系来构造运动学对象。我们将 target_frame_name 设置为 gripper 坐标系。
kinematics_solver = RobotKinematics(
urdf_path="./SO101/so101_new_calib.urdf",
target_frame_name="gripper_frame_link",
joint_names=list(robot.bus.motors.keys()),
)
MapPhoneActionToRobotAction 步骤将 calibration 后的手机位姿和输入转换为目标增量和 gripper 命令,下面展示了该步骤的输出。
action["enabled"] = enabled
action["target_x"] = -pos[1] if enabled else 0.0
action["target_y"] = pos[0] if enabled else 0.0
action["target_z"] = pos[2] if enabled else 0.0
action["target_wx"] = rotvec[1] if enabled else 0.0
action["target_wy"] = rotvec[0] if enabled else 0.0
action["target_wz"] = -rotvec[2] if enabled else 0.0
action["gripper_vel"] = gripper_vel # Still send gripper action when disabledEEReferenceAndDelta 步骤将目标增量转换为绝对期望 EE 位姿,并在启用时存储一个参考值,end_effector_step_sizes 是 EE 位姿的步长,可以修改以改变运动速度。
EEReferenceAndDelta(
kinematics=kinematics_solver,
end_effector_step_sizes={"x": 0.5, "y": 0.5, "z": 0.5},
motor_names=list(robot.bus.motors.keys()),
use_latched_reference=True,
),EEBoundsAndSafety 步骤将 EE 运动限制在工作空间内,并检查大的 EE 步进跳跃以确保安全。end_effector_bounds 是 EE 位姿的边界,可以修改以改变工作空间。max_ee_step_m 是 EE 位姿的步进限制,可以修改以改变安全限制。
EEBoundsAndSafety(
end_effector_bounds={"min": [-1.0, -1.0, -1.0], "max": [1.0, 1.0, 1.0]},
max_ee_step_m=0.10,
)GripperVelocityToJoint 步骤使用当前测量 state 将类似速度的 gripper 输入转换为绝对 gripper 位置。speed_factor 是速度乘数因子。
GripperVelocityToJoint(speed_factor=20.0)我们在运动学步骤中使用不同的 IK 初始猜测。初始猜测使用当前测量的关节或先前的 IK 解。
闭环(用于录制/评估):设置 initial_guess_current_joints=True,使 IK 每帧从测量的关节开始。
InverseKinematicsEEToJoints(
kinematics=kinematics_solver,
motor_names=list(robot.bus.motors.keys()),
initial_guess_current_joints=True, # closed loop
)开环(用于回放):设置 initial_guess_current_joints=False,使 IK 从先前的 IK 解而非测量 state 继续。这在我们无反馈回放时保持 action 稳定性。
InverseKinematicsEEToJoints(
kinematics=kinematics_solver,
motor_names=list(robot.bus.motors.keys()),
initial_guess_current_joints=False, # open loop
)action.ee.* 特征。initial_guess_current_joints=True;为稳定性在开环回放中设置 False。observation.state.ee.*,用于记录和基于 EE state 进行训练。https 而不是 http,使用脚本打印的精确 IP,并允许浏览器进入并忽略证书问题。MapPhoneActionToRobotAction 中的符号翻转或交换轴以匹配你的设置。