系列文章目录
目录
[1.1 支持平台](#1.1 支持平台)
[1.2 手机方向与控制方式](#1.2 手机方向与控制方式)
[1.3 选择平台](#1.3 选择平台)
[1.4 连接与校准](#1.4 连接与校准)
[1.5 运行示例脚本](#1.5 运行示例脚本)
[1.6 重要管道步骤与选项](#1.6 重要管道步骤与选项)
[1.7 不同的反向运动学初始猜测](#1.7 不同的反向运动学初始猜测)
[1.8 管道步骤说明](#1.8 管道步骤说明)
[1.9 故障排除](#1.9 故障排除)
[二、Teleop 入门教程](#二、Teleop 入门教程)
[2.1 安装](#2.1 安装)
[2.2 用法](#2.2 用法)
[2.2.1 基本接口](#2.2.1 基本接口)
[2.2.2 xArm](#2.2.2 xArm)
[2.2.3 ROS 2 接口](#2.2.3 ROS 2 接口)
[2.2.4 ROS 2 与反向运动学接口](#2.2.4 ROS 2 与反向运动学接口)
[2.2.5 自定义接口](#2.2.5 自定义接口)
[2.3 示例](#2.3 示例)
[2.4 实用工具](#2.4 实用工具)
[2.4.1 JacobiRobot](#2.4.1 JacobiRobot)
[2.4.2 JacobiRobotROS](#2.4.2 JacobiRobotROS)
前言
在本指南中您将学习:
- 如何连接iOS/Android手机
- 手机姿态如何映射至机器人末端执行器(EE)目标
- 如何调整安全限制、抓取器控制及逆向运动学设置
要使用手机控制机器人,请通过以下命令安装相关依赖项:
bash
pip install lerobot[phone]
一、开始使用
1.1 支持平台
- iOS:使用HEBI Mobile I/O应用(ARKit姿态+按钮)。请先下载该应用,打开后示例程序将自动在网络中发现它,并流式传输手机姿态和输入数据。
- Android:使用 teleop 包(WebXR)。启动 Python 进程时会打印本地 URL。在手机上打开链接,点击开始,然后使用移动功能传输姿态。
链接:
- Android WebXR 库:PyPI 上的 teleop
- iOS 应用:HEBI Mobile I/O
1.2 手机方向与控制方式
- 方向:将手机屏幕朝上握持,顶部边缘与机器人抓手方向一致。此举确保校准时手机坐标系与机器人坐标系对齐,使动作更自然,详见下图示意。
- 启用/禁用:
- iOS:长按B1键启用遥控操作,松开即停止。首次按下将捕获基准姿态。
- Android:长按移动按钮,松开停止。首次按下将捕获参考姿势。
- 抓手控制:
- iOS:模拟输入A3作为速度输入控制抓手。
- Android:按钮A和B分别作为增量/减量控制(A打开,B关闭)。可在"GripperVelocityToJoint"步骤中调整速度。

1.3 选择平台
修改示例代码,在 PhoneConfig 中使用 PhoneOS.IOS 或 PhoneOS.ANDROID。API 在不同平台上完全相同,仅输入源存在差异。所有示例均位于 examples/ 目录下,并包含 phone_so100_*.py 变体文件。
远程操作示例:
python
from lerobot.teleoperators.phone.config_phone import PhoneConfig, PhoneOS
teleop_config = PhoneConfig(phone_os=PhoneOS.IOS) # or PhoneOS.ANDROID
teleop_device = Phone(teleop_config)
1.4 连接与校准
当创建 Phone(teleop_config) 并调用 connect() 时,系统将自动提示校准。请按上述方向握持手机,然后:
- iOS:长按B1键捕捉基准姿态。
- Android:在WebXR页面点击移动按钮捕捉基准姿态。
为何需要校准?我们捕捉当前姿态以便后续姿态在机器人对齐坐标系中呈现。当您再次点击按钮启用控制时,系统会重新捕捉位置,避免手机在禁用状态下移动导致的漂移偏差。
1.5 运行示例脚本
运行其中一个示例脚本,以实现遥操作、记录数据集、回放数据集或评估策略.
所有脚本均假设您已完成机器人配置(例如 SO-100 跟随机器人)并设置正确的串口。
此外需将机器人的 urdf 文件复制到示例文件夹。对于本教程示例(使用 SO100/SO101),强烈建议使用 SO-ARM100 仓库中的 urdf 文件。
- 运行此示例进行遥操作:
python
python examples/phone_to_so100/teleoperate.py
运行示例后:
- Android:启动脚本后,在手机上打开打印的本地网址,点击"开始",然后长按"移动"。
- iOS:先打开HEBI Mobile I/O;B1控制运动,A3控制夹爪。
此外,您可通过编辑示例中的处理器步骤自定义映射或安全限值。您还可通过修改输入和运动学步骤来重新映射输入(例如使用不同模拟输入),或将管道适配至其他机器人(如LeKiwi)。更多详情请参阅《机器人与遥控操作器处理器指南》。
-
运行此示例可记录数据集,该数据集保存末端执行器的绝对位置观测值与动作:
bashpython examples/phone_to_so100/record.py
-
运行此示例以重放录制的剧集:
bashpython examples/phone_to_so100/replay.py
-
运行此示例以评估预训练策略:
bashpython examples/phone_to_so100/evaluate.py
1.6 重要管道步骤与选项
-
运动学在多个步骤中使用。我们采用Placo(作为Pinocchio的封装库)来处理运动学计算。通过传递机器人的URDF文件和目标坐标系来构造运动学对象。我们将target_frame_name设置为夹爪坐标系。
pythonkinematics_solver = RobotKinematics( urdf_path="./SO101/so101_new_calib.urdf", target_frame_name="gripper_frame_link", joint_names=list(robot.bus.motors.keys()), )
-
MapPhoneActionToRobotAction步骤将校准后的手机姿态和输入转换为目标位移量和夹爪指令,下文展示该步骤的输出结果。
pythonaction["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 disabled
-
EEReferenceAndDelta步骤将目标增量转换为绝对期望的末端执行器姿态,在启用时存储参考值。end_effector_step_sizes参数定义末端执行器姿态的步进尺寸,可通过修改该参数调整运动速度。
pythonEEReferenceAndDelta( 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步骤将末端执行器的运动限制在工作空间内,并检测末端执行器位移的较大跳变以确保安全。end_effector_bounds定义末端执行器姿态的边界范围,可通过修改该参数改变工作空间。max_ee_step_m和max_ee_twist_step_rad分别是末端执行器姿态的位移极限值和扭转极限值,修改这些参数可调整安全边界。
pythonEEBoundsAndSafety( end_effector_bounds={"min": [-1.0, -1.0, -1.0], "max": [1.0, 1.0, 1.0]}, max_ee_step_m=0.10, max_ee_twist_step_rad=0.50, )
-
夹持器速度到关节步骤将速度类型的夹持器输入转换为绝对夹持器位置,使用当前测量的状态。速度因子是乘以速度的系数。
pythonGripperVelocityToJoint(speed_factor=20.0)
1.7 不同的反向运动学初始猜测
我们在运动学步骤中采用不同的反向运动学初始猜测。初始猜测值可选当前测量关节或前帧反向运动学解。
-
闭环模式(用于记录/评估):设置 initial_guess_current_joints=True,使反向运动学每帧从测量关节开始计算。
pythonInverseKinematicsEEToJoints( kinematics=kinematics_solver, motor_names=list(robot.bus.motors.keys()), initial_guess_current_joints=True, # closed loop )
-
开环(用于回放):将initial_guess_current_joints设为False,使逆向运动学从前一次的逆向运动学解继续,而非基于测量状态。这在无反馈回放时能保持动作稳定性。
pythonInverseKinematicsEEToJoints( kinematics=kinematics_solver, motor_names=list(robot.bus.motors.keys()), initial_guess_current_joints=False, # open loop )
1.8 管道步骤说明
- MapPhoneActionToRobotAction:将校准后的手机姿态和输入转换为目标位移量和夹爪指令。运动由使能信号控制(iOS系统为B1,安卓系统为Move)。
- EEReferenceAndDelta:在使能时锁存参考EE姿态,并与目标位移量结合以生成每帧的绝对期望EE姿态。禁用时持续发送上次指令姿态。
- EEBoundsAndSafety:将EE姿态限制在工作空间内,并限速跳跃以确保安全。同时声明action.ee.*特征。
- InverseKinematicsEEToJoints:通过逆运动学将EE姿态转换为关节位置。闭环控制推荐设置initial_guess_current_joints=True;开环回放为稳定性需设为False。
- GripperVelocityToJoint:将速度型抓取器输入与当前测量状态融合,转换为绝对抓取器位置。
- ForwardKinematicsJointsToEE:根据观测关节计算 observation.state.ee.*,用于 EE 状态日志记录与训练。
1.9 故障排除
- iOS 未被发现:确保 HEBI Mobile I/O 处于开启状态,且笔记本电脑/手机处于同一网络。
- Android URL无法访问:检查是否使用https而非http协议,使用脚本打印的精确IP地址,允许浏览器进入并忽略证书问题。
- 运动感觉反转:调整MapPhoneActionToRobotAction中的符号翻转,或交换轴向以匹配您的设置。
二、Teleop 入门教程

只需三个简单步骤,即可将您的手机变身为机械臂远程操控设备:
- 在电脑上安装并启动服务器。
- 在手机上打开提供的网址。
- 点击开始,然后长按移动按钮即可操控机械臂。
重要提示
您的手机必须支持 WebXR API。遗憾的是,iPhone 不支持 WebXR API。
该网络应用程序利用WebXR API,整合手机传感器以检测其在三维空间中的方位与位置。服务器接收这些数据后,将其发送至机械臂控制器。
|----------------------------------------------------------------------------|----------------------------------------------------------------------------|
| |
|
| Teleoperation of a physical Lite6 robot | Teleoperation of a simulated UR5e robot in Webots |
2.1 安装
该软件包可在PyPI上获取。您可使用pip进行安装:
bash
pip install teleop
2.2 用法
我们提供了一些现成的机械臂接口,但您也可以通过将 teleop.Teleop 类集成到项目中来创建自己的接口。
2.2.1 基本接口
一个简单的接口,用于打印遥控响应。您可以将其作为参考来构建自己的接口。
bash
python -m teleop.basic
2.2.2 xArm
用于远程操控uFactory Lite 6机器人的接口。支持其他xArm机器人可能需要进行微调。
bash
python -m teleop.xarm
请注意该接口设计极为简洁,未实现任何过滤功能。因此,您可能需要通过高帧率设备进行远程操控。智能手机通常为30帧/秒,而VR操纵杆可达90帧/秒,后者在无过滤的远程操控场景中显然更具优势。
2.2.3 ROS 2 接口
ROS 2 接口主要设计用于与 cartesian_controllers 包配合使用,但也可适配 MoveIt Servo 或其他包。
发布主题:
- target_frame (geometry_msgs/PoseStamped):机器人机械臂末端执行器在机器人基座坐标系中的目标姿态。
- tf (tf2_msgs/TFMessage):用于可视化的机器人基座坐标系与目标坐标系之间的变换。
订阅主题:
- current_pose (geometry_msgs/PoseStamped):机器人末端执行器在机器人基座坐标系中的当前姿态。用于更新参考姿态。
您可通过标准ROS 2参数覆盖默认主题名称:
bash
python -m teleop.ros2 --ros-args -r target_frame:=/some_other_topic_name
2.2.4 ROS 2 与反向运动学接口
不支持伺服控制?没问题。teleop 通过 JacobiRobotROS 实用类提供伺服控制支持。
熊猫机械臂使用示例:
bash
python -m teleop.ros2_ik \
--joint-names panda_joint1 panda_joint2 panda_joint3 panda_joint4 panda_joint5 panda_joint6 panda_joint7 \
--ee-link panda_hand \
--ros-args -r /joint_trajectory:=/panda_arm_controller/joint_trajectory
xArm 使用示例:
bash
python -m teleop.ros2_ik \
--joint-names joint1 joint2 joint3 joint4 joint5 joint6 \
--ee-link link6 \
--ros-args -r /joint_trajectory:=/joint_trajectory_controller/joint_trajectory
2.2.5 自定义接口
对于大多数应用程序,您需要创建自定义接口来与机械臂交互。以下是一个示例:
python
import numpy as np
from teleop import Teleop
def callback(pose: np.ndarray, message: dict) -> None:
"""
Callback function triggered when pose updates are received.
Arguments:
- np.ndarray: A 4x4 transformation matrix representing the end-effector target pose.
- dict: A dictionary containing additional information.
"""
print(f'Pose: {pose}')
print(f'Message: {message}')
teleop = Teleop()
teleop.subscribe(callback)
teleop.run()
2.3 示例
探索示例以了解如何在各种场景中使用该软件包:
- examples/webots:在Webots仿真中使用ikpy实现UR5e机械臂的远程操作。
2.4 实用工具
该软件包包含若干实用工具类,用于简化机械臂集成:
注意
使用实用工具类时,请安装软件包及其附加依赖项:
bashpip install teleop[utils]
2.4.1 JacobiRobot
基于Pinocchio的机械臂伺服控制与运动学解决方案。
核心特性:
- 基于Pinocchio的前向/逆向运动学计算
- 带速度/加速度限制的姿态伺服控制
- 实时3D可视化
- 关节级控制与监测
应用场景:
python
from teleop.utils.jacobi_robot import JacobiRobot
robot = JacobiRobot("robot.urdf", ee_link="end_effector")
target_pose = np.eye(4) # 4x4 transformation matrix
reached = robot.servo_to_pose(target_pose, dt=0.01)
2.4.2 JacobiRobotROS
用于JacobiRobot的ROS 2封装器,可与标准ROS 2主题和消息集成。
关键特性:
- 从/robot_description主题自动加载URDF
- 关节状态订阅与轨迹发布
- 兼容joint_trajectory_controller
- 与现有ROS 2控制堆栈无缝集成
用法:
python
from teleop.utils.jacobi_robot_ros import JacobiRobotROS
import rclpy
rclpy.init()
node = rclpy.create_node("robot_control")
robot = JacobiRobotROS(
node=node,
ee_link="end_effector",
joint_names=["joint1", "joint2", "joint3"]
)
robot.reset_joint_states() # Wait for initial joint states
reached = robot.servo_to_pose(target_pose, dt=0.03)