Isaac Lab 操作空间控制器(OSC)实战:同时控制机械臂末端位姿与接触力
【免费下载链接】IsaacLabUnified framework for robot learning with multi-physics/renderer support项目地址: https://gitcode.com/GitHub_Trending/is/IsaacLab
本篇教程基于 Isaac Lab 的run_osc.py示例,讲解如何利用OperationalSpaceController(操作空间控制器)驱动机械臂,在跟踪期望末端位姿的同时,对倾斜墙面施加恒定接触力(模拟擦桌子等接触作业)。读完本文,你将掌握 OSC 的核心原理、OperationalSpaceControllerCfg全量配置参数、与差分 IK 控制器的区别,以及如何在 Isaac Lab 仿真环境中完成"力/位混合控制"的完整闭环。
为什么需要操作空间控制器
在 Isaac Lab 中,差分 IK 控制器教程 通过运动学求解将末端位姿映射为关节位置指令,适用于纯轨迹跟踪场景。但以下任务场景中差分 IK 并不够用:
- 需要强制指定任务空间的位姿跟踪误差动力学(如保证末端以指定刚度、阻尼特性逼近目标);
- 需要用关节力矩/力(effort/torque)指令直接驱动机器人;
- 需要在一个方向上控制运动、同时在其他方向施加接触力(例如用抹布擦桌面、墙面抛光等接触作业)。
此时就需要操作空间控制器(Operational Space Controller,OSC)。OSC 的理论基础是 Khatib 于 1987 年提出的操作空间公式(Operational Space Formulation),它把任务空间(笛卡尔空间)的期望动力学映射到关节空间力矩。控制器实现及配置定义于:
- 控制器实现
- 控制器配置
本教程将复用OperationalSpaceController,让 Franka Panda 机械臂对一面红色倾斜墙持续施加垂直于墙面(沿任务帧 z 轴)的恒定力,同时在其他五个方向跟踪期望末端位姿。
运行示例:搭建场景并驱动 128 台机器人
示例脚本位于 scripts/tutorials/05_controllers/run_osc.py,通过仓库根目录下的启动脚本运行:
./isaaclab.sh -p scripts/tutorials/05_controllers/run_osc.py --num_envs 128脚本将启动包含 128 台机器人的并行仿真环境。场景构成(SceneCfg)包括:
- 地面平面(
GroundPlaneCfg)与半球光(DomeLightCfg); - 一面半透明红色倾斜墙(
CuboidCfg),尺寸为 2.0 × 1.5 × 0.01 m,启用碰撞与接触传感器,姿态四元数(0, -0.3827, 0, 0.9239)(绕 y 轴旋转约 45°),位置(0.685, 0, 0.3); - 一个
ContactSensorCfg(prim_path="/World/envs/env_.*/TiltedWall",history_length=2)用于读取接触力; - Franka Panda 机械臂:关键设定是把
panda_shoulder、panda_forearm驱动器的刚度与阻尼清零(力矩控制模式),并关闭重力(disable_gravity=True),使关节完全由 OSC 输出的力矩指令驱动。
仿真循环中,机械臂将在三组目标(三个不同的末端位姿,均对应墙面上不同的接触点)之间循环切换。运行结果中,当前与目标末端位姿通过帧标记(frame marker)显示,你将看到机械臂到达目标位姿的同时保持垂直于墙面施加恒定力。运行效果如下:
停止仿真只需关闭窗口或在终端按下Ctrl+C。
创建操作空间控制器:OperationalSpaceControllerCfg 全参数解析
OperationalSpaceController的任务是在任务空间内同时进行运动控制与力控制,输出关节力矩。任务空间参考系(task frame)可以是欧氏空间中的任意坐标系,默认取机器人基座坐标系。当目标坐标相对其他坐标系更容易定义时(例如本教程中墙面平行坐标系),应在set_command的current_task_frame_pose_b参数中提供该任务参考系相对基座的位姿。本教程中,目标位姿与墙面同姿态,将其作为任务帧后,力控制方向恰好只落在任务帧的 z 轴,全部配置参数都围绕这一任务参考系展开。
控制器在 operational_space.py 中以批处理 + PyTorch 运算实现,天然支持并行环境(num_envs)。
target_types:任务空间目标类型
指定任务空间目标的类型与参考系,由"类型 + 参考"两个子串用下划线拼接:
| 取值 | 含义 | 命令维度 |
|---|---|---|
"pose_abs" | 绝对位姿(相对机器人基座),含位置 + 四元数 | 7 |
"pose_rel" | 相对末端当前位姿的位姿增量,旋转用轴角表示 | 6 |
"wrench_abs" | 绝对力/力矩(相对机器人基座) | 6 |
若需同时做位姿与力控制,将target_types设为列表,如["pose_abs", "wrench_abs"]或["pose_rel", "wrench_abs"]。各目标的命令向量按target_types顺序拼接,action_dim为各目标维度之和(见下节)。
本教程使用target_types=["pose_abs", "wrench_abs"]:除 z 轴外的五个方向做绝对位姿控制,z 轴做绝对力控制。
控制轴选择:motion_control_axes_task 与 contact_wrench_control_axes_task
用 0/1 长度为 6 的列表分别指定运动控制与力控制作用的轴(顺序为 x、y、z 平动 + 绕 x、y、z 转动)。两列表应互补:例如 x 轴若motion_control_axes_task为 0,则contact_wrench_control_axes_task应为 1。
本教程中:
motion_control_axes_task=[1, 1, 0, 1, 1, 1], # 除 z 平动外的所有轴做运动控制 contact_wrench_control_axes_task=[0, 0, 1, 0, 0, 0], # 仅在 z 平动方向做力控制注意:源码中会将增益矩阵与选择矩阵相乘,把非运动控制轴的增益清零(operational_space.py第 95-97 行),避免因轴间耦合导致未控制轴产生意外力矩。
运动控制:刚度、阻尼比与阻抗模式
运动控制轴上的期望刚度(stiffness)与阻尼比(damping ratio)通过以下参数设置:
motion_stiffness_task:位置增益(N/m 或 N·m/rad),可为标量(所有轴相同)或 6 元素列表,默认100.0 × 6;motion_damping_ratio_task:阻尼比,配合位置增益计算速度增益:d_gains = 2 * sqrt(p_gains) * damping_ratio,默认1.0 × 6。
若希望刚度/阻尼比作为命令参数动态改变(例如用 RL 学习阻抗或运行中在线调整),设置impedance_mode:
| 取值 | 含义 |
|---|---|
"fixed" | 增益固定,命令只含任务空间目标 |
"variable_kp" | 命令额外包含 6 个刚度值(action_dim = target_dim + 6) |
"variable" | 命令额外包含刚度与阻尼比(action_dim = target_dim + 6 + 6) |
使用"variable_kp"/"variable"时必须同时设置:
motion_stiffness_limits_task:刚度上下限,默认(0, 1000);motion_damping_ratio_limits_task:阻尼比上下限,默认(0, 100)(仅"variable"使用)。
命令中的刚度/阻尼比会被裁剪到上述范围内(见operational_space.py第 224-227、243-248 行)。
本教程设置impedance_mode="variable_kp"、motion_damping_ratio_task=1.0(阻尼比固定为 1,kd 随 kp 自适应,维持临界阻尼响应),并在命令中动态传入三组 kp:360、420、320(每个目标一组)。
力控制:开环与闭环
- 开环力控制:不设置
contact_wrench_stiffness_task(保持None),直接输出期望力; - 闭环力控制:设置
contact_wrench_stiffness_task(标量或 6 元素列表),在期望力上叠加刚度 × 期望力与实测力之差(含前馈项),形成反馈修正。
由于目前接触传感器只能测量线性力、无法测量力矩,闭环控制仅使用contact_wrench_stiffness_task的前三个元素(线性部分),力矩方向保持开环(见operational_space.py第 459-472 行)。
本教程设置contact_wrench_stiffness_task=[0.0, 0.0, 0.1, 0.0, 0.0, 0.0]:仅对 z 轴力做闭环反馈(增益 0.1),期望力为任务帧 z 轴方向 10 N。
惯性动力学解耦(inverse dynamics)
inertial_dynamics_decoupling=True:利用机器人的质量/惯性矩阵解耦任务空间的期望加速度(逆动力学),计算操作空间质量矩阵M_task = (J M⁻¹ Jᵀ)⁻¹,再得到F = M_task · ẍ_des。这对快速运动下的控制精度至关重要,解耦覆盖全部六个运动轴之间的耦合;partial_inertial_dynamics_decoupling=True:忽略平动与转动之间的惯性耦合,分别计算平移与旋转部分(如 测试用例 test_franka_pose_abs_with_partial_inertial_decoupling 所示)。
若为False,则退化为不包含惯性项的任务空间阻抗控制(命令力直接等于期望加速度)。本教程使用inertial_dynamics_decoupling=True且partial_inertial_dynamics_decoupling=False,做完整惯性解耦。
重力补偿
gravity_compensation=True时,将重力补偿向量gravity叠加到关节力矩中。本教程中机器人的重力已在场景配置中关闭(disable_gravity=True),因此不启用。
冗余机械臂的零空间控制
零空间(null-space)是关节空间中不影响任务空间坐标的子空间。若不加以控制,冗余关节会"飘浮"(甚至逼近关节限位)。有两种选择:
nullspace_control="none"(默认):不施加零空间控制;nullspace_control="position":集成零空间 PD 控制器,把关节吸引到期望位置而不影响任务空间。
零空间行为由nullspace_stiffness(默认 10.0)与nullspace_damping_ratio(默认 1.0)定义。理论上的零空间与任务空间完全解耦,仅在inertial_dynamics_decoupling=True且partial_inertial_dynamics_decoupling=False时成立(此时使用动力学一致的伪逆,否则退化为 Moore-Penrose 伪逆,见operational_space.py第 498-505 行)。
本教程设置nullspace_control="position",将关节目标设为关节限位中心:
joint_centers = torch.mean(robot.data.soft_joint_pos_limits.torch[:, arm_joint_ids, :], dim=-1)本教程的完整配置
osc_cfg = OperationalSpaceControllerCfg( target_types=["pose_abs", "wrench_abs"], impedance_mode="variable_kp", inertial_dynamics_decoupling=True, partial_inertial_dynamics_decoupling=False, gravity_compensation=False, motion_damping_ratio_task=1.0, contact_wrench_stiffness_task=[0.0, 0.0, 0.1, 0.0, 0.0, 0.0], motion_control_axes_task=[1, 1, 0, 1, 1, 1], contact_wrench_control_axes_task=[0, 0, 1, 0, 0, 0], nullspace_control="position", ) osc = OperationalSpaceController(osc_cfg, num_envs=scene.num_envs, device=sim.device)控制器实例化需要三个参数:配置、环境数num_envs与计算设备device。所有配置字段的完整说明见 operational_space_cfg.py。
更新机器人状态:向控制器喂入动力学量
OperationalSpaceController是纯计算类,不持有机器人内部状态,需要调用方在每个控制周期提供(全部在机器人基座/root 坐标系下):
- 末端雅可比矩阵
jacobian_b(root 系,形状num_envs × 6 × num_DoF); - 关节空间质量/惯性矩阵
mass_matrix(num_envs × num_DoF × num_DoF); - 重力补偿向量
gravity(num_envs × num_DoF); - 末端位姿
current_ee_pose_b(num_envs × 7,位置 + 四元数); - 末端速度
current_ee_vel_b(num_envs × 6,线速度 + 角速度); - 末端接触力
current_ee_force_b(num_envs × 3); - 关节位置/速度
current_joint_pos、current_joint_vel; - (可选)零空间关节目标位置
nullspace_joint_pos_target与重力向量。
示例脚本中的update_states()函数负责从 Isaac Lab 的Articulation与ContactSensor提取这些量,关键步骤包括:
- Jacobian 提取与坐标系转换:取
body_link_jacobian_w中末端连杆对应的块(注意固定基座机器人的基座自由度偏移num_base_dofs),再用matrix_from_quat(quat_inv(root_quat_w))将旋转部分与世界坐标下的 Jacobian 从世界系旋转到 root 系; - 质量矩阵与重力向量:从
robot.data.mass_matrix、robot.data.gravity_compensation_forces中按臂关节索引切片; - 末端位姿/速度:用
subtract_frame_transforms将世界系末端位姿转换到 root 系;速度用quat_apply_inverse把"末端相对 root 的相对速度"从世界系旋转到 root 系; - 接触力:更新接触传感器后,对历史窗口(
history_length=2)内的净接触力取平均再取三面最大值,得到平滑的末端接触力(脚本注释说明这是为了测试所做的简化处理)。
设置命令与计算关节力矩:控制循环三要素
OSC 将"设置期望命令"与"计算关节指令"分离,这是使用该控制器的关键编程约定。
1. 组装并转换目标命令
命令向量是一个形状为(num_envs, action_dim)的张量,内容按target_types顺序拼接。本教程的impedance_mode="variable_kp",因此命令为pose_abs(7) + wrench_abs(6) + kp(6) = 19维。每组目标定义为:
ee_target_set = torch.cat([ee_goal_pose_set_tilted_b, ee_goal_wrench_set_tilted_task, kp_set_task], dim=-1)期望力已在任务帧中定义(ee_goal_wrench_set_tilted_task,仅 z 轴为 10 N),而期望位姿定义在基座系。convert_to_task_frame()用subtract_frame_transforms把位姿目标从基座系转换到任务帧(即目标位姿本身作为任务参考系),命令全部统一到任务帧:
command, task_frame_pose_b = convert_to_task_frame(osc, command=command, ee_target_pose_b=ee_target_pose_b)2. 设置 OSC 命令
将任务帧下的命令向量、末端当前位姿(基座系)与任务参考系位姿(基座系)一起交给set_command。内部计算在基座系完成,因此需要任务帧相对基座的位姿用于坐标变换(包括增益矩阵、选择矩阵的旋转变换与期望力/力矩的传递,见operational_space.py第 293-345 行):
osc.set_command(command=command, current_ee_pose_b=ee_pose_b, current_task_frame_pose_b=task_frame_pose_b)3. 计算关节力矩并施加
compute()在内部依次完成:运动控制(末端位姿误差 + 弹簧阻尼动力学 → 惯性解耦 → 经选择矩阵与 Jacobian 转置映射到关节力矩)、力控制(开环或带前馈的闭环力修正同样映射到关节力矩)、重力补偿叠加、零空间控制叠加,最终返回(num_envs, num_DoF)的关节力矩:
joint_efforts = osc.compute( jacobian_b=jacobian_b, current_ee_pose_b=ee_pose_b, current_ee_vel_b=ee_vel_b, current_ee_force_b=ee_force_b, mass_matrix=mass_matrix, gravity=gravity, current_joint_pos=joint_pos, current_joint_vel=joint_vel, nullspace_joint_pos_target=joint_centers, ) # 施加关节力矩(仅作用于臂关节),并写回仿真 robot.set_joint_effort_target_index(target=joint_efforts, joint_ids=arm_joint_ids) robot.write_data_to_sim()重置与目标切换
仿真每 500 步重置一次:将关节状态恢复默认值、力矩清零、重置接触传感器,随后重新计算目标命令并调用osc.reset()清空内部目标缓存,再重新set_command。目标索引通过current_goal_idx在三个目标间循环(next_goal_idx = (current_goal_idx + 1) % len(ee_target_set))。
从控制器到动作项:OSC 在 RL 环境中的封装
除脚本级使用外,Isaac Lab 还将 OSC 封装为 Manager-based RL 环境的动作项(Action Term),配置类为 OperationalSpaceControllerActionCfg(实现位于task_space_actions.py的OperationalSpaceControllerAction)。其关键字段:
joint_names:动作映射到的关节列表(支持正则);body_name:执行运动/力控制的末端连杆/坐标系名称;body_offset:末端坐标系相对父刚体的偏移(很多机器人的末端是虚构坐标系,如 Franka 的末端相对panda_hand存在偏移);task_frame_rel_path:指定任务参考系对应的RigidObject路径(如一面墙);controller_cfg:内嵌的OperationalSpaceControllerCfg;position_scale/orientation_scale/wrench_scale/stiffness_scale/damping_ratio_scale:对各类命令的缩放因子;nullspace_joint_pos_target:零空间关节目标策略,取"none"/"zero"/"default"/"center"(仅当nullspace_control="position"时生效)。
验证与测试:仓库中的 OSC 覆盖矩阵
仓库测试 test_operational_space.py 系统验证了 OSC 的各种组合模式,可作为理解各配置项行为的最佳参照:
test_franka_pose_abs_without_inertial_decoupling:固定阻抗 + 无惯性解耦的绝对位姿控制;test_franka_pose_abs_with_partial_inertial_decoupling:部分惯性解耦;test_franka_pose_abs_fixed_impedance_with_gravity_compensation:启用重力补偿(机器人不关闭重力);test_franka_pose_abs/test_franka_pose_rel:绝对/相对位姿 + 完整惯性解耦;test_franka_pose_abs_variable_impedance:variable阻抗模式(命令含 kp 与阻尼比);test_franka_wrench_abs_open_loop/test_franka_wrench_abs_closed_loop:纯力控制的开环与闭环模式;test_franka_hybrid_decoupled_motion、test_franka_hybrid_variable_kp_impedance:位姿 + 力混合控制(固定阻抗 / variable_kp);test_franka_taskframe_pose_abs、test_franka_taskframe_pose_rel、test_franka_taskframe_hybrid:以任务参考系(非 root 系)定义目标的位姿控制与混合控制。
例如混合控制测试使用的配置与教程配置高度一致(target_types=["pose_abs", "wrench_abs"]、contact_wrench_stiffness_task=[0.1, 0, 0, 0, 0, 0]、互补的控制轴),进一步印证了"运动轴与力轴互补 + 任务帧定义"这一 OSC 标准用法。
小结
本文围绕run_osc.py完整演示了 Isaac Lab 操作空间控制器的使用闭环:场景搭建 →OperationalSpaceControllerCfg配置(目标类型、控制轴、阻抗模式、惯性解耦、零空间控制)→ 机器人状态提取(Jacobian、质量矩阵、接触力)→ 任务帧命令转换 →set_command/compute力矩输出。关键要点可归纳为:
- 运动与力控制通过互补的
motion_control_axes_task与contact_wrench_control_axes_task划分任务空间六轴; - 任务参考系可自由选择(默认基座系),配合
current_task_frame_pose_b传入即可简化目标定义; - 惯性解耦提升快速运动精度,且是零空间与任务空间理论解耦的前提;
- 接触力闭环只作用于线性力方向(传感器测不到接触力矩);
- 冗余机械臂务必规划零空间控制策略,避免关节失控飘移。
进一步阅读可参考仓库中的 差分 IK 控制器教程 对比两类任务空间控制器的差异,以及 任务空间动作项封装 了解如何将 OSC 接入端到端强化学习训练。
【免费下载链接】IsaacLabUnified framework for robot learning with multi-physics/renderer support项目地址: https://gitcode.com/GitHub_Trending/is/IsaacLab
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考