高级 IK 与并行 IK#
逆运动学与运动规划 中介绍的逆运动学(IK)求解器向两个方向做了扩展,这两个方向对真实的操作任务和大规模训练都很重要:同时为多个末端执行器连杆求解,以及在一次调用中求解并行场景的每一个环境。
本页可运行的源码是
advanced_IK_multilink.py
和
batched_IK.py。
多末端执行器连杆的 IK#
对 robot.inverse_kinematics() 的单次调用只定位一个连杆。当一个任务同时约束多个连杆(夹爪的两个指尖、双臂机器人的两只手,或一个闭链运动学环路)时,用 robot.inverse_kinematics_multilink() 对它们联合求解。分别求解每个连杆,后面的解可能会把前面的解破坏掉;多连杆求解器会找出一个同时满足所有目标的关节构型。
该调用接收若干并列的列表:每个条目对应一个目标连杆、一个位置和一个姿态。
left_finger = robot.get_link("left_finger")
right_finger = robot.get_link("right_finger")
q = robot.inverse_kinematics_multilink(
links=[left_finger, right_finger],
poss=[target_pos_left, target_pos_right],
quats=[target_quat, target_quat],
rot_mask=[False, False, True], # only restrict direction of z-axis
)
每个目标不必是完整的 6 自由度位姿。pos_mask 和 rot_mask 是长度为 3 的布尔掩码,用来选择求解器必须满足哪些位置轴和哪些旋转轴;两者默认都是 [True, True, True]。这里的 rot_mask=[False, False, True] 只要求每个指尖的 z 轴与 target_quat 的 z 轴对齐,水平面内的朝向则不受约束。把不关心的约束屏蔽掉,求解器就有更大的自由度,也更有可能收敛。
姿态遵循 (w, x, y, z) 四元数约定,因此 target_quat = np.array([0, 1, 0, 0]) 会让手指的 z 轴竖直向下。
这个示例是纯运动学的:它演示的是求解器,而不是物理过程。求解之后,它把结果直接写入机器人,而不是推进仿真。设置状态本身会重绘查看器,所以循环只需用 gs.tools.Rate 让每个位姿保持一个仿真步的墙钟时间,位姿就会按仿真的节奏播放,而不是按求解器运行的速度:
# The poses follow the target along a continuous path, so they are shown at the pace of the simulation
rate = gs.tools.Rate(1.0 / scene.dt)
...
# Note that this IK is for visualization purposes, so here we do not call scene.step(), but only set the state,
# which redraws the viewer. In actual control applications, you should instead use robot.control_dofs_position()
# and scene.step().
robot.set_dofs_position(q)
rate.sleep()
示例在视频中用 scene.draw_debug_frame() 绘制目标坐标系,并在每次迭代中用 scene.update_debug_objects() 移动它们。这些标记位于可视化器层面,不参与仿真。调试绘制 API 见 交互式 GUI 与调试。
跨并行环境的 IK#
当场景用多个环境构建时,同一个求解器可以在一次调用中作用于所有环境,这对于生成演示数据或驱动策略 rollout 而不必用 Python 循环逐个环境遍历很有用。该示例生成 16 个环境,并让每个机器人的末端执行器以不同的角速度旋转。
与单环境 IK 相比,唯一的结构性变化是批处理维度。用 n_envs 构建场景,然后给求解器传入首维为 n_envs 的批处理目标:
n_envs = 16
scene.build(n_envs=n_envs, env_spacing=(1.0, 1.0))
target_quat = np.tile(np.array([0, 1, 0, 0]), [n_envs, 1]) # (n_envs, 4), pointing downwards
center = np.tile(np.array([0.4, -0.2, 0.25]), [n_envs, 1])
angular_speed = np.random.uniform(-10, 10, n_envs)
r = 0.1
ee_link = robot.get_link("hand")
在循环内部,pos 和 quat 带有批处理维度,求解器为每个环境返回一个构型:
target_pos = np.zeros([n_envs, 3])
target_pos[:, 0] = center[:, 0] + np.cos(i / 360 * np.pi * angular_speed) * r
target_pos[:, 1] = center[:, 1] + np.sin(i / 360 * np.pi * angular_speed) * r
target_pos[:, 2] = center[:, 2]
q = robot.inverse_kinematics(
link=ee_link,
pos=target_pos, # shape (n_envs, 3)
quat=target_quat, # shape (n_envs, 4)
rot_mask=[False, False, True], # for demo purpose: only restrict direction of z-axis
)
robot.set_qpos(q)
scene.step()
返回的 q 形状为 ([n_envs,] n_dofs):场景含多个环境时是批处理的,否则就是一个普通的 (n_dofs,) 向量。同样的规则适用于输入:pos 为 ([n_envs,] 3),quat 为 ([n_envs,] 4)。掩码仍然是长度为 3 的,并在整个批次间共享。inverse_kinematics_multilink 以同样的方式批处理,poss/quats 的每个条目都带有首维 n_envs。
正运动学与雅可比矩阵#
逆运动学建立在两个更底层的操作之上,同一个求解器直接暴露了它们,二者本身对解析控制也很有用。正运动学是 IK 的逆运算:它把关节构型映射为各连杆的位姿。雅可比矩阵把关节速度与末端执行器的空间速度联系起来,而后者正是 IK 每一步所微分的量;它也是雅可比转置控制和可操作度分析的基础。
两者都可用于任何刚体实体。雅可比矩阵是实体的方法,而正运动学是求解器上的查询,因为它评估的是任意构型,而不是求解器当前持有的构型。
ee_link = robot.get_link("hand")
# Jacobian at the link origin: rows 0-2 are linear, rows 3-5 angular, one column per dof.
jacobian = robot.get_jacobian(link=ee_link) # shape ([n_envs,] 6, n_dofs)
# Forward kinematics: joint configuration -> world-frame link poses.
qpos = robot.get_qpos()
links_pos, links_quat = robot.solver.forward_kinematics_query(robot, qpos)
# links_pos: shape ([n_envs,] n_links, 3) link-frame origins, world coordinates
# links_quat: shape ([n_envs,] n_links, 4) orientations, (w, x, y, z)
get_jacobian 接受一个可选的 local_point(连杆局部坐标系中长度为 3 的点),用于在连杆原点之外的位置计算雅可比矩阵。forward_kinematics_query 返回实体每个连杆的世界坐标系位姿,并接受 envs_idx 来评估环境的一个子集。它在返回前会恢复求解器的构型,因此该查询不会改变仿真状态。
Note
morph 参数 requires_jac_and_IK 已废弃:传入它只会记录一条警告,没有任何效果。