跳转至

PyBullet 控制与动力学

引言

PyBullet 基于 Bullet 物理引擎,提供了丰富的关节控制和动力学仿真功能。本文介绍 PyBullet 中的关节控制模式、PD 控制、逆运动学(Inverse Kinematics, IK)、逆动力学(Inverse Dynamics)、接触力与约束等核心功能,并给出机械臂和四足机器人的控制示例。

关节控制模式

PyBullet 提供三种基本的关节控制模式:

控制模式 函数 说明
位置控制 setJointMotorControl2(POSITION_CONTROL) 内置 PD 控制器跟踪目标位置
速度控制 setJointMotorControl2(VELOCITY_CONTROL) 控制关节速度
力矩控制 setJointMotorControl2(TORQUE_CONTROL) 直接施加力矩

基本用法

import pybullet as p
import pybullet_data

p.connect(p.GUI)
p.setAdditionalSearchPath(pybullet_data.getDataPath())
p.setGravity(0, 0, -9.81)
p.loadURDF("plane.urdf")
robot_id = p.loadURDF("kuka_iiwa/model.urdf", [0, 0, 0], useFixedBase=True)

# 位置控制:内置 PD 控制器跟踪目标位置
p.setJointMotorControl2(robot_id, 0, p.POSITION_CONTROL,
                        targetPosition=0.5, force=240,
                        positionGain=0.3, velocityGain=1.0)

# 速度控制:适合轮式机器人
p.setJointMotorControl2(robot_id, 0, p.VELOCITY_CONTROL,
                        targetVelocity=5.0, force=10.0)

# 力矩控制:需先禁用默认控制器
for i in range(7):
    p.setJointMotorControl2(robot_id, i, p.VELOCITY_CONTROL, force=0)
p.setJointMotorControlArray(robot_id, range(7), p.TORQUE_CONTROL,
                            forces=[10, -5, 3, -2, 1, -0.5, 0.2])

PD 控制

自定义 PD 控制器

当内置 PD 控制器不满足需求时,可以在力矩模式下实现自定义控制器:

import numpy as np

kp = np.array([100, 100, 80, 80, 50, 50, 30])
kd = np.array([10, 10, 8, 8, 5, 5, 3])

for step in range(10000):
    joint_states = p.getJointStates(robot_id, range(7))
    q = np.array([s[0] for s in joint_states])
    dq = np.array([s[1] for s in joint_states])
    torques = np.clip(kp * (q_desired - q) - kd * dq, -240, 240)
    p.setJointMotorControlArray(
        robot_id, range(7), p.TORQUE_CONTROL, forces=torques.tolist()
    )
    p.stepSimulation()

计算力矩控制

利用逆动力学实现更精确的控制,补偿重力和科里奥利力:

def computed_torque_control(robot_id, q_des, dq_des, ddq_des, kp, kd):
    joint_states = p.getJointStates(robot_id, range(7))
    q = [s[0] for s in joint_states]
    dq = [s[1] for s in joint_states]
    tau_id = p.calculateInverseDynamics(robot_id, q, dq, ddq_des)
    tau = np.array(tau_id) + kp * (np.array(q_des) - np.array(q)) \
          + kd * (np.array(dq_des) - np.array(dq))
    return tau

逆运动学

PyBullet 内置了基于阻尼最小二乘法的 IK 求解器:

# 计算 IK:给定末端目标位置,求关节角度
target_pos = [0.4, 0.2, 0.3]
target_orn = p.getQuaternionFromEuler([0, -3.14, 0])
end_effector_link = 6

joint_positions = p.calculateInverseKinematics(
    robot_id,
    end_effector_link,
    target_pos,
    targetOrientation=target_orn,
    maxNumIterations=100,
    residualThreshold=1e-5
)

# 应用 IK 结果
for i, pos in enumerate(joint_positions):
    p.setJointMotorControl2(
        robot_id, i, p.POSITION_CONTROL, targetPosition=pos
    )

IK 带关节限制

# 指定关节角度范围
lower_limits = [-2.96, -2.09, -2.96, -2.09, -2.96, -2.09, -3.05]
upper_limits = [2.96, 2.09, 2.96, 2.09, 2.96, 2.09, 3.05]
joint_ranges = [u - l for l, u in zip(lower_limits, upper_limits)]
rest_poses = [0.0, 0.0, 0.0, -1.57, 0.0, 1.57, 0.0]

joint_positions = p.calculateInverseKinematics(
    robot_id, end_effector_link, target_pos,
    targetOrientation=target_orn,
    lowerLimits=lower_limits,
    upperLimits=upper_limits,
    jointRanges=joint_ranges,
    restPoses=rest_poses
)

逆动力学

逆动力学计算给定期望加速度时所需的关节力矩:

# 获取当前状态
joint_states = p.getJointStates(robot_id, range(7))
q = [s[0] for s in joint_states]
dq = [s[1] for s in joint_states]
ddq_desired = [0.0] * 7  # 期望加速度

# 计算所需力矩
torques = p.calculateInverseDynamics(robot_id, q, dq, ddq_desired)

# 获取质量矩阵
mass_matrix = p.calculateMassMatrix(robot_id, q)

接触力与碰撞

# 获取接触点:返回法线(contact[7])、法向力(contact[9])、摩擦力(contact[10/12])
contacts = p.getContactPoints(bodyA=robot_id, bodyB=plane_id)

# 最近点查询(无需仿真步进,可用于碰撞预检测)
closest = p.getClosestPoints(bodyA=robot_id, bodyB=obstacle_id, distance=0.1)
# closest[i][8] 为距离,负值表示穿透

约束

PyBullet 通过 createConstraint 创建约束连接物体,changeConstraint 修改参数,removeConstraint 移除。

约束类型

类型 常量 用途
固定关节 JOINT_FIXED 刚性连接,模拟抓取
点到点 JOINT_POINT2POINT 球铰
齿轮 JOINT_GEAR 齿轮传动关系
棱柱 JOINT_PRISMATIC 滑动约束

应用示例

机械臂圆形轨迹跟踪

import pybullet as p, pybullet_data, numpy as np, time

p.connect(p.GUI)
p.setAdditionalSearchPath(pybullet_data.getDataPath())
p.setGravity(0, 0, -9.81)
p.loadURDF("plane.urdf")
robot = p.loadURDF("kuka_iiwa/model.urdf", useFixedBase=True)

center, radius, ee_link = [0.4, 0.0, 0.4], 0.15, 6
for t in range(5000):
    angle = t * 0.01
    target = [center[0] + radius * np.cos(angle),
              center[1] + radius * np.sin(angle), center[2]]
    ik = p.calculateInverseKinematics(robot, ee_link, target)
    for i, pos in enumerate(ik):
        p.setJointMotorControl2(robot, i, p.POSITION_CONTROL,
                                targetPosition=pos, force=240)
    p.stepSimulation()

四足机器人对角步态

四足机器人步态的核心是为 12 个关节(每条腿 3 个:髋、膝、踝)生成周期性目标轨迹。对角步态中,对角线上的两条腿同相位运动:

def trot_gait(phase, speed=1.0):
    """生成对角步态的 12 个关节目标位置"""
    targets = np.zeros(12)
    for leg in range(4):
        offset = 0.0 if leg in [0, 3] else np.pi
        p = phase + offset
        targets[leg*3+0] = 0.3 * np.sin(p)                    # 髋关节
        targets[leg*3+1] = -0.6 + 0.3 * max(0, np.sin(p))     # 膝关节
        targets[leg*3+2] = 0.8 - 0.2 * max(0, np.sin(p))      # 踝关节
    return targets

动力学参数设置

通过 changeDynamics 可设置摩擦系数(lateralFriction)、质量(mass)、惯性(localInertiaDiagonal)、接触刚度(contactStiffness)、阻尼(contactDamping)和恢复系数(restitution)。使用 getDynamicsInfo 查询当前参数。

参考资料