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

使用隐式PD控制+重力补偿控制iiwa机械臂时出现漂移

问题分析与解决方案:IIWA机械臂隐式PD+重力补偿的位置漂移问题

问题背景

控制方案为组合使用Drake最新版隐式PD控制器和基于InverseDynamics的重力补偿。当将机械臂期望位置固定为q0并运行仿真时,机械臂无法保持在q0,会漂移至另一平衡位置;漂移量随PD反馈增益减小而增大,且重力设为0时漂移现象消失。可通过以下代码复现该现象:

import numpy as np
import matplotlib.pyplot as plt

from pydrake.all import (
    DiagramBuilder,
    MultibodyPlant,
    AddMultibodyPlant,
    ProcessModelDirectives,
    MultibodyPlantConfig,
    Parser,
    LoadModelDirectivesFromString,
    InverseDynamics,
    JointActuatorIndex,
    PdControllerGains,
    VisualizationConfig,
    ApplyVisualizationConfig,
    StartMeshcat,
    Simulator,
    LogVectorOutput,
)


directives = """
directives:
    - add_model:
        name: iiwa
        file: package://drake/manipulation/models/iiwa_description/iiwa7/iiwa7_no_collision.sdf
        default_joint_positions:
            iiwa_joint_1: [ 0 ]
            iiwa_joint_2: [ 0 ]
            iiwa_joint_3: [ 0 ]
            iiwa_joint_4: [ -1.7 ]
            iiwa_joint_5: [ 0 ]
            iiwa_joint_6: [ 1.0 ]
            iiwa_joint_7: [ 0 ]
    - add_weld:
        parent: world
        child: iiwa::iiwa_link_0
"""

meshcat = StartMeshcat()

#%%
builder = DiagramBuilder()

plant_config = MultibodyPlantConfig()
plant_config.discrete_contact_solver = "sap"
plant_config.time_step = 1e-3
plant, scene_graph = AddMultibodyPlant(config=plant_config, builder=builder)

model_directives = LoadModelDirectivesFromString(
    directives
)
parser = Parser(plant)
added_models = ProcessModelDirectives(
    directives=model_directives,
    parser=parser,
)
iiwa_model = added_models[0].model_instance

# Implicit PD
Kp = np.ones(7) * 10
Kd = np.ones(7) * 5

actuator_indices = []
for i in range(plant.num_actuators()):
    actuator_index = JointActuatorIndex(i)
    if plant.get_joint_actuator(actuator_index).model_instance() == iiwa_model:
        actuator_indices.append(actuator_index)

for actuator_index, Kp, Kd in zip(actuator_indices, Kp, Kd):
    plant.get_joint_actuator(actuator_index).set_controller_gains(
        PdControllerGains(p=Kp, d=Kd)
    )

plant.Finalize()

# Gravity compensation
inverse_dynamics_controller = builder.AddSystem(
    InverseDynamics(
        plant,
        InverseDynamics.InverseDynamicsMode.kGravityCompensation,
    )
)
builder.Connect(
    plant.get_state_output_port(iiwa_model),
    inverse_dynamics_controller.get_input_port_estimated_state(),
)

builder.Connect(
    inverse_dynamics_controller.get_output_port_generalized_force(),
    plant.get_actuation_input_port(iiwa_model)
)

state_log_sink = LogVectorOutput(
    src=plant.get_state_output_port(iiwa_model),
    builder=builder
)

ApplyVisualizationConfig(VisualizationConfig(), builder, meshcat=meshcat)

diagram = builder.Build()


#%%
q0 = np.array([0, 0, 0, -1.7, 0, 1.0, 0])
v0 = np.zeros(7)
x0 = np.hstack([q0, v0])

simulator = Simulator(diagram)
context = simulator.get_mutable_context()
context_plant = plant.GetMyContextFromRoot(context)

plant.SetPositions(context_plant, q0)
plant.SetVelocities(context_plant, v0)
plant.get_desired_state_input_port(iiwa_model).FixValue(context_plant, x0)


meshcat.StartRecording()
simulator.AdvanceTo(5.0)
meshcat.StopRecording()
meshcat.PublishRecording()


#%%
state_log = state_log_sink.FindLog(context)

t = state_log.sample_times()
x_log = state_log.data().T

#%%
figure = plt.figure()
plt.xlabel("t [s]")
plt.ylabel("joint angles [rad]")
for i in range(7):
    plt.plot(t, x_log[:, i], label=f"q_{i}")

plt.legend(loc='upper right')
plt.show()

原因分析

问题根源是力矩叠加导致的重力过补偿:

  • 隐式PD控制器输出力矩:$\tau_{pd} = K_p(q_{des} - q) + K_d(v_{des} - v)$,作用是通过反馈误差产生力矩,抵消重力并跟踪期望位置
  • InverseDynamics重力补偿模式输出力矩:$\tau_{gravity}$,即抵消当前关节位置下重力所需的力矩
  • 总力矩为$\tau_{total} = \tau_{pd} + \tau_{gravity}$,系统平衡时($\ddot{q}=\dot{q}=0$),动力学方程要求$\tau_{total} = -\tau_{gravity}(q)$
  • 代入总力矩表达式得:$K_p(q_{des} - q) + \tau_{gravity}(q) = -\tau_{gravity}(q)$,整理后平衡位置$q$满足$K_p(q_{des} - q) = -2\tau_{gravity}(q)$,显然$q \neq q_{des}$(除非$\tau_{gravity}(q_{des})=0$),因此机械臂会漂移到该平衡位置。增益$K_p$越小,$q$与$q_{des}$的偏差越大;重力为0时$\tau_{gravity}=0$,平衡位置回到$q_{des}$,漂移消失。

解决方案

提供两种可行修正方案:

方案1:仅使用隐式PD控制器

移除额外的重力补偿模块,让PD控制器通过反馈增益抵消重力。此时平衡时$K_p(q_{des} - q) = -\tau_{gravity}(q)$,稳态误差随$K_p$增大而减小,可通过调高增益满足精度要求。

修改方式:删除代码中所有重力补偿相关的模块创建与连接代码。

方案2:使用InverseDynamics位置跟踪模式

用InverseDynamics的kPositionTracking模式替代分开的隐式PD和重力补偿,该模式会自动整合PD反馈与重力补偿,输出正确的跟踪力矩$\tau = K_p(q_{des} - q) + K_d(v_{des} - v) + \tau_{gravity}(q,v)$,平衡时$q=q_{des}$,刚好抵消重力,无漂移。

修改后的核心代码:

# 移除原隐式PD增益设置代码
# for actuator_index, Kp, Kd in zip(actuator_indices, Kp, Kd):
#     plant.get_joint_actuator(actuator_index).set_controller_gains(
#         PdControllerGains(p=Kp, d=Kd)
#     )

plant.Finalize()

# 替换为InverseDynamics位置跟踪模式
Kp = np.ones(7) * 10
Kd = np.ones(7) * 5

inverse_dynamics_controller = builder.AddSystem(
    InverseDynamics(
        plant,
        InverseDynamics.InverseDynamicsMode.kPositionTracking,
    )
)
# 设置期望状态
inverse_dynamics_controller.get_input_port_desired_state().FixValue(
    inverse_dynamics_controller.GetMyContextFromRoot(context),
    x0
)
# 设置PD增益
inverse_dynamics_controller.set_pd_gains(Kp, Kd)

builder.Connect(
    plant.get_state_output_port(iiwa_model),
    inverse_dynamics_controller.get_input_port_estimated_state(),
)
builder.Connect(
    inverse_dynamics_controller.get_output_port_generalized_force(),
    plant.get_actuation_input_port(iiwa_model)
)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 21:05:55