You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Drake中IIWA机械臂差分IK初始速度过大问题咨询

Drake机械臂初始移动速度过快问题排查与解决

问题描述

我是Drake框架及机械臂运动学领域的新手,已对iiwa_teleop代码进行修改,将遥操作模块替换为可从命令行(CLI)接收目标位姿的小型系统。我已设置关节速度限制,但在仿真过程中,机械臂初始阶段移动速度极快,随后逐渐减速,无法定位问题原因,特此咨询。

完整代码

import argparse
import numpy as np

from manipulation.meshcat_utils import WsgButton
from manipulation.scenarios import AddIiwaDifferentialIK
from manipulation.station import LoadScenario
from pydrake.all import (
    ApplySimulatorConfig,
    DiagramBuilder,
    MeshcatPoseSliders,
    MeshcatVisualizer,
    Simulator,
    AbstractValue,
    ConstantValueSource,
)
from pydrake.systems.framework import LeafSystem, BasicVector
from iiwa_setup.iiwa import IiwaForwardKinematics, IiwaHardwareStationDiagram
from pydrake.math import RigidTransform, RollPitchYaw
from pydrake.common.value import Value
from pydrake.multibody.inverse_kinematics import (
    DifferentialInverseKinematicsIntegrator,
    DifferentialInverseKinematicsParameters,
)


class CLIPoseCommand(LeafSystem):
    def __init__(self):
        super().__init__()
        self.DeclareAbstractOutputPort(
            "X_AE_desired", lambda: Value(RigidTransform()), self.CalcPoseOutput
        )
        self.DeclareAbstractInputPort("ee_pose", Value(RigidTransform()))
        self.pose_rpy_xyz = np.zeros(6)

    def CalcPoseOutput(self, context, output):
        rpy = RollPitchYaw(self.pose_rpy_xyz[:3])
        xyz = self.pose_rpy_xyz[3:]
        output.get_mutable_value().set(RollPitchYaw(rpy).ToRotationMatrix(), xyz)

    def update_pose(self, X_WE: RigidTransform):
        rpy = RollPitchYaw(X_WE.rotation()).vector()
        xyz = X_WE.translation()
        self.pose_rpy_xyz = np.hstack((rpy, xyz))


def main(use_hardware: bool, has_wsg: bool) -> None:
    scenario_data = """
    directives:
    - add_directives:
        file: package://iiwa_setup/iiwa7.dmd.yaml
    plant_config:
        # For some reason, this requires a small timestep
        time_step: 0.005
        contact_model: "hydroelastic"
        discrete_contact_approximation: "sap"
    model_drivers:
        iiwa: !IiwaDriver
            lcm_bus: "default"
            control_mode: position_only
    lcm_buses:
        default:
            lcm_url: ""
    """

    builder = DiagramBuilder()

    scenario = LoadScenario(data=scenario_data)
    station: IiwaHardwareStationDiagram = builder.AddNamedSystem(
        "station",
        IiwaHardwareStationDiagram(
            scenario=scenario, has_wsg=has_wsg, use_hardware=use_hardware
        ),
    )
    controller_plant = station.get_iiwa_controller_plant()

    lower_bnd = np.array([-0.1, -0.1, -0.1, -0.1, -0.1, -0.1, -0.1]) / 2
    upper_bnd = np.array([0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]) / 2

    params = DifferentialInverseKinematicsParameters(
        controller_plant.num_positions(), controller_plant.num_velocities()
    )

    time_step = 0.005
    params.set_time_step(time_step)
    params.set_joint_velocity_limits((lower_bnd, upper_bnd))

    differential_ik = builder.AddSystem(
        DifferentialInverseKinematicsIntegrator(
            controller_plant,
            controller_plant.GetFrameByName("iiwa_link_7"),
            time_step,
            params,
        )
    )

    builder.Connect(
        differential_ik.get_output_port(),
        station.GetInputPort("iiwa.position"),
    )
    builder.Connect(
        station.GetOutputPort("iiwa.state_estimated"),
        differential_ik.GetInputPort("robot_state"),
    )

    cli_cmder = CLIPoseCommand()
    pose_command_system = builder.AddSystem(cli_cmder)
    builder.Connect(
        cli_cmder.get_output_port(), differential_ik.GetInputPort("X_AE_desired")
    )

    iiwa_forward_kinematics = builder.AddSystem(
        IiwaForwardKinematics(station.get_internal_plant())
    )
    builder.Connect(
        station.GetOutputPort("iiwa.position_commanded"),
        iiwa_forward_kinematics.get_input_port(),
    )
    builder.Connect(
        iiwa_forward_kinematics.get_output_port(), cli_cmder.get_input_port()
    )

    # Required for visualizing the internal station
    _ = MeshcatVisualizer.AddToBuilder(
        builder, station.GetOutputPort("query_object"), station.internal_meshcat
    )

    diagram = builder.Build()
    simulator = Simulator(diagram)
    ApplySimulatorConfig(scenario.simulator_config, simulator)
    simulator.set_target_realtime_rate(1.0)

    station.internal_meshcat.AddButton("Stop Simulation")
    simulator.AdvanceTo(0.1)

    user_input = input("Enter roll pitch yaw x y z").split()
    if len(user_input) == 6:
        roll_deg, pitch_deg, yaw_deg, x, y, z = list(map(float, user_input))
        X_goal = RigidTransform(
            RollPitchYaw(
                np.radians(roll_deg), np.radians(pitch_deg), np.radians(yaw_deg)
            ),
            [x, y, z],
        )
        cli_cmder.update_pose(X_goal)

    while station.internal_meshcat.GetButtonClicks("Stop Simulation") < 1:
        simulator.AdvanceTo(simulator.get_context().get_time() + 1)
        station.internal_meshcat.DeleteButton("Stop Simulation")


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "--use_hardware",
        action="store_true",
        help="Whether to use real world hardware.",
    )
    parser.add_argument(
        "--has_wsg",
        action="store_true",
        help="Whether the iiwa has a WSG gripper or not.",
    )
    args = parser.parse_args()
    main(use_hardware=args.use_hardware, has_wsg=args.has_wsg)

问题排查与解决建议

1. 初始位姿突变导致的冲击

你在仿真启动后直接通过update_pose设置目标位姿,机械臂从初始姿态到目标姿态的差值过大,微分IK会尝试以最大允许速度追赶目标,造成初始阶段速度暴增。解决方法是添加平滑轨迹过渡,让机械臂在指定时间内从当前位姿逐步移动到目标位姿:

# 在获取用户输入后,替换cli_cmder.update_pose(X_goal)的代码
from pydrake.trajectories import PiecewisePose

# 获取当前末端执行器位姿
diagram_context = diagram.GetMutableContextFromRoot(simulator.get_context())
current_pose = iiwa_forward_kinematics.get_output_port().Eval(diagram_context)

# 生成2秒内从当前位姿到目标位姿的线性平滑轨迹
traj = PiecewisePose.MakeLinear([0, 2], [current_pose, X_goal])

# 创建轨迹输出系统
class TrajectorySource(LeafSystem):
    def __init__(self, traj):
        super().__init__()
        self.traj = traj
        self.DeclareAbstractOutputPort(
            "X_AE_desired", lambda: Value(RigidTransform()), self.CalcOutput
        )
    
    def CalcOutput(self, context, output):
        t = context.get_time() - simulator.get_context().get_time()
        output.get_mutable_value().set(self.traj.GetPose(t))

# 添加系统并重新连接
traj_source = builder.AddSystem(TrajectorySource(traj))
builder.Disconnect(cli_cmder.get_output_port(), differential_ik.GetInputPort("X_AE_desired"))
builder.Connect(traj_source.get_output_port(), differential_ik.GetInputPort("X_AE_desired"))

2. 补充任务空间速度限制

仅设置关节速度限制不足以约束末端执行器的运动速度,微分IK会优先满足任务空间的位姿跟踪需求。可以添加末端执行器的线速度和角速度限制:

# 在设置params.set_joint_velocity_limits后添加
# 末端线速度限制(m/s):x、y、z方向
linear_vel_limits = [0.1, 0.1, 0.1]
# 末端角速度限制(rad/s):roll、pitch、yaw方向
angular_vel_limits = [0.2, 0.2, 0.2]
params.set_end_effector_velocity_limits(linear_vel_limits, angular_vel_limits)

3. 初始化微分IK的积分状态

确保微分IK的初始积分位置与机械臂的初始关节位置一致,避免积分器初始偏差导致的突跳:

# 在diagram = builder.Build()之后添加
diff_ik_context = differential_ik.GetMyContextFromRoot(simulator.get_context())
initial_joint_state = station.GetOutputPort("iiwa.state_estimated").Eval(simulator.get_context())
differential_ik.SetPositions(diff_ik_context, initial_joint_state[:7])
differential_ik.SetVelocities(diff_ik_context, initial_joint_state[7:])

4. 优化仿真循环步进

当前循环每次推进1秒,会导致视觉上的“瞬间移动”。改为持续推进仿真直到停止按钮被点击:

# 替换原来的while循环
while station.internal_meshcat.GetButtonClicks("Stop Simulation") < 1:
    simulator.AdvanceTo(simulator.get_context().get_time() + 0.01)

内容的提问来源于stack exchange,提问作者Liu Jinkua

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 23:40:53