CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-pybullet

Official Python Interface for the Bullet Physics SDK specialized for Robotics Simulation and Reinforcement Learning

Pending
Overview
Eval results
Files

joint-motor-control.mddocs/

Joint and Motor Control

Comprehensive joint control capabilities including position, velocity, and torque control with PD controllers, multi-DOF joint support, and force/torque sensing. Essential for robotic manipulation, locomotion, and precise motion control.

Capabilities

Joint Information

Query joint properties, constraints, and current state for control and analysis.

def getNumJoints(bodyUniqueId, physicsClientId=0):
    """
    Get number of joints for a multibody.
    
    Args:
        bodyUniqueId (int): Body unique ID
        physicsClientId (int, optional): Physics client ID. Defaults to 0.
    
    Returns:
        int: Number of joints in the body
    """

def getJointInfo(bodyUniqueId, jointIndex, physicsClientId=0):
    """
    Get comprehensive joint information.
    
    Args:
        bodyUniqueId (int): Body unique ID
        jointIndex (int): Joint index (0 to getNumJoints-1)
        physicsClientId (int, optional): Physics client ID. Defaults to 0.
    
    Returns:
        tuple: Joint information containing:
            - jointIndex (int): Joint index
            - jointName (str): Joint name from URDF
            - jointType (int): Joint type constant (p.JOINT_REVOLUTE, etc.)
            - qIndex (int): Position index in generalized coordinates
            - uIndex (int): Velocity index in generalized coordinates
            - flags (int): Joint flags
            - jointDamping (float): Joint damping coefficient
            - jointFriction (float): Joint friction coefficient
            - jointLowerLimit (float): Lower position limit
            - jointUpperLimit (float): Upper position limit
            - jointMaxForce (float): Maximum force/torque
            - jointMaxVelocity (float): Maximum velocity
            - linkName (str): Link name from URDF
            - jointAxis (list): Joint axis in local frame [x, y, z]
            - parentFramePos (list): Position in parent frame [x, y, z]
            - parentFrameOrn (list): Orientation in parent frame [x, y, z, w]
            - parentIndex (int): Parent link index
    """

def getJointState(bodyUniqueId, jointIndex, physicsClientId=0):
    """
    Get current joint state including position, velocity, and forces.
    
    Args:
        bodyUniqueId (int): Body unique ID
        jointIndex (int): Joint index
        physicsClientId (int, optional): Physics client ID. Defaults to 0.
    
    Returns:
        tuple: Joint state containing:
            - jointPosition (float): Current joint position
            - jointVelocity (float): Current joint velocity
            - jointReactionForces (list): Reaction forces/torques [Fx, Fy, Fz, Mx, My, Mz]
            - appliedJointMotorTorque (float): Applied motor torque
    """

def getJointStates(bodyUniqueId, jointIndices, physicsClientId=0):
    """
    Get multiple joint states efficiently.
    
    Args:
        bodyUniqueId (int): Body unique ID
        jointIndices (list): List of joint indices
        physicsClientId (int, optional): Physics client ID. Defaults to 0.
    
    Returns:
        list: List of joint state tuples (same format as getJointState)
    """

Multi-DOF Joint Support

Support for joints with multiple degrees of freedom (spherical joints, planar joints).

def getJointStateMultiDof(bodyUniqueId, jointIndex, physicsClientId=0):
    """
    Get multi-DOF joint state (for spherical and planar joints).
    
    Args:
        bodyUniqueId (int): Body unique ID
        jointIndex (int): Joint index
        physicsClientId (int, optional): Physics client ID. Defaults to 0.
    
    Returns:
        tuple: Multi-DOF joint state containing:
            - jointPosition (list): Position values (length depends on DOF)
            - jointVelocity (list): Velocity values
            - jointReactionForces (list): Reaction forces/torques
            - appliedJointMotorTorque (list): Applied motor torques
    """

def getJointStatesMultiDof(bodyUniqueId, jointIndices, physicsClientId=0):
    """
    Get multiple multi-DOF joint states efficiently.
    
    Returns:
        list: List of multi-DOF joint state tuples
    """

Joint State Modification

Reset joint positions and velocities directly for initialization and testing.

def resetJointState(bodyUniqueId, jointIndex, targetValue, targetVelocity=0, physicsClientId=0):
    """
    Reset joint state immediately (teleport).
    
    Args:
        bodyUniqueId (int): Body unique ID
        jointIndex (int): Joint index
        targetValue (float): Target joint position
        targetVelocity (float, optional): Target joint velocity. Defaults to 0.
        physicsClientId (int, optional): Physics client ID. Defaults to 0.
    
    Note:
        This function immediately sets joint state without physics simulation.
        Use for initialization or testing scenarios.
    """

def resetJointStateMultiDof(bodyUniqueId, jointIndex, targetValue, targetVelocity=None, physicsClientId=0):
    """
    Reset multi-DOF joint state immediately.
    
    Args:
        bodyUniqueId (int): Body unique ID
        jointIndex (int): Joint index
        targetValue (list): Target joint positions
        targetVelocity (list, optional): Target joint velocities
        physicsClientId (int, optional): Physics client ID. Defaults to 0.
    """

def resetJointStatesMultiDof(bodyUniqueId, jointIndices, targetValues, targetVelocities=None, physicsClientId=0):
    """
    Reset multiple multi-DOF joints efficiently.
    """

Motor Control

Advanced motor control with multiple control modes and precise parameter tuning.

def setJointMotorControl2(bodyUniqueId, jointIndex, controlMode, targetPosition=None, targetVelocity=None, force=None, positionGain=None, velocityGain=None, maxVelocity=None, physicsClientId=0):
    """
    Set joint motor control with advanced parameters.
    
    Args:
        bodyUniqueId (int): Body unique ID
        jointIndex (int): Joint index
        controlMode (int): Control mode constant:
            - p.POSITION_CONTROL: Position control with PD controller
            - p.VELOCITY_CONTROL: Velocity control
            - p.TORQUE_CONTROL: Direct torque control
            - p.PD_CONTROL: Pure PD control
            - p.STABLE_PD_CONTROL: Stable PD control
        targetPosition (float, optional): Target position (position control modes)
        targetVelocity (float, optional): Target velocity (velocity/position control)
        force (float, optional): Maximum force/torque to apply
        positionGain (float, optional): Position gain (Kp) for PD control
        velocityGain (float, optional): Velocity gain (Kd) for PD control
        maxVelocity (float, optional): Maximum velocity limit
        physicsClientId (int, optional): Physics client ID. Defaults to 0.
    
    Note:
        Different control modes require different parameter combinations:
        - POSITION_CONTROL: targetPosition, force, positionGain, velocityGain
        - VELOCITY_CONTROL: targetVelocity, force
        - TORQUE_CONTROL: force (direct torque)
        - PD_CONTROL: targetPosition, targetVelocity, force, positionGain, velocityGain
    """

def setJointMotorControlArray(bodyUniqueId, jointIndices, controlMode, targetPositions=None, targetVelocities=None, forces=None, positionGains=None, velocityGains=None, physicsClientId=0):
    """
    Set motor control for multiple joints efficiently.
    
    Args:
        bodyUniqueId (int): Body unique ID
        jointIndices (list): List of joint indices
        controlMode (int): Control mode for all joints
        targetPositions (list, optional): List of target positions
        targetVelocities (list, optional): List of target velocities
        forces (list, optional): List of maximum forces
        positionGains (list, optional): List of position gains
        velocityGains (list, optional): List of velocity gains
        physicsClientId (int, optional): Physics client ID. Defaults to 0.
    
    Note:
        More efficient than individual setJointMotorControl2 calls.
        All parameter lists must match length of jointIndices.
    """

def setJointMotorControlMultiDof(bodyUniqueId, jointIndex, controlMode, targetPosition=None, targetVelocity=None, force=None, positionGain=None, velocityGain=None, physicsClientId=0):
    """
    Set motor control for multi-DOF joints.
    
    Args:
        bodyUniqueId (int): Body unique ID
        jointIndex (int): Joint index
        controlMode (int): Control mode
        targetPosition (list, optional): Target positions for each DOF
        targetVelocity (list, optional): Target velocities for each DOF
        force (list, optional): Maximum forces for each DOF
        positionGain (list, optional): Position gains for each DOF
        velocityGain (list, optional): Velocity gains for each DOF
        physicsClientId (int, optional): Physics client ID. Defaults to 0.
    """

def setJointMotorControlMultiDofArray(bodyUniqueId, jointIndices, controlMode, targetPositions=None, targetVelocities=None, forces=None, positionGains=None, velocityGains=None, physicsClientId=0):
    """
    Set motor control for multiple multi-DOF joints efficiently.
    """

Force and Torque Sensing

Enable and configure joint force/torque sensors for feedback control and analysis.

def enableJointForceTorqueSensor(bodyUniqueId, jointIndex, enableSensor, physicsClientId=0):
    """
    Enable or disable joint force/torque sensor.
    
    Args:
        bodyUniqueId (int): Body unique ID
        jointIndex (int): Joint index
        enableSensor (int): 1 to enable sensor, 0 to disable
        physicsClientId (int, optional): Physics client ID. Defaults to 0.
    
    Note:
        When enabled, getJointState returns accurate reaction forces.
        Adds computational overhead, so only enable when needed.
    """

Control Modes

Position Control (p.POSITION_CONTROL)

PID position controller with optional velocity feedforward.

  • Use case: Precise positioning, trajectory following
  • Parameters: targetPosition, force, positionGain, velocityGain
  • Behavior: Drives joint to target position with PD control
  • Best for: Pick-and-place, precise manipulation tasks

Velocity Control (p.VELOCITY_CONTROL)

Direct velocity control with maximum force limiting.

  • Use case: Continuous motion, velocity profiles
  • Parameters: targetVelocity, force
  • Behavior: Maintains constant velocity until force limit reached
  • Best for: Locomotion, conveyor systems, scanning motions

Torque Control (p.TORQUE_CONTROL)

Direct force/torque application without position or velocity control.

  • Use case: Force control, impedance control, gravity compensation
  • Parameters: force (desired torque)
  • Behavior: Applies constant torque regardless of position/velocity
  • Best for: Force-sensitive tasks, compliant control, dynamic motions

PD Control (p.PD_CONTROL)

Pure proportional-derivative control without built-in integrator.

  • Use case: Custom control loops, research applications
  • Parameters: targetPosition, targetVelocity, force, positionGain, velocityGain
  • Behavior: PD control with user-specified gains and target velocity
  • Best for: Custom control algorithms, academic research

Stable PD Control (p.STABLE_PD_CONTROL)

Mathematically stable PD controller for improved performance.

  • Use case: High-gain control, fast precise movements
  • Parameters: Same as PD_CONTROL
  • Behavior: Stable PD implementation resistant to high gains
  • Best for: High-performance robotic applications

Usage Examples

Basic Position Control

import pybullet as p
import time

p.connect(p.GUI)
robotId = p.loadURDF("pr2_gripper.urdf")

# Get joint information
numJoints = p.getNumJoints(robotId)
for i in range(numJoints):
    info = p.getJointInfo(robotId, i)
    print(f"Joint {i}: {info[1]} (Type: {info[2]})")

# Position control for multiple joints
jointIndices = [0, 1, 2]
targetPositions = [0.5, -0.3, 0.8]

for step in range(1000):
    p.setJointMotorControlArray(robotId, 
                               jointIndices, 
                               p.POSITION_CONTROL,
                               targetPositions=targetPositions,
                               forces=[100, 100, 100])
    p.stepSimulation()
    time.sleep(1./240.)

Advanced PD Control Tuning

import pybullet as p

p.connect(p.GUI)
robotId = p.loadURDF("kuka_iiwa/model.urdf")

# High-performance PD control with custom gains
jointIndex = 1
targetPosition = 1.57  # 90 degrees

# Tuned PD gains for precise control
positionGain = 0.3  # Kp
velocityGain = 1.0  # Kd

for step in range(1000):
    # Get current joint state for monitoring
    jointState = p.getJointState(robotId, jointIndex)
    currentPos = jointState[0]
    currentVel = jointState[1]
    
    # Apply PD control
    p.setJointMotorControl2(robotId,
                           jointIndex,
                           p.PD_CONTROL,
                           targetPosition=targetPosition,
                           targetVelocity=0,
                           force=100,
                           positionGain=positionGain,
                           velocityGain=velocityGain)
    
    p.stepSimulation()
    
    # Print error for monitoring
    if step % 100 == 0:
        error = targetPosition - currentPos
        print(f"Step {step}: Position error = {error:.4f}")

Velocity Control for Scanning Motion

import pybullet as p
import math

p.connect(p.GUI)
robotId = p.loadURDF("kuka_iiwa/model.urdf")

jointIndex = 2
scanSpeed = 0.5  # rad/s

for step in range(2000):
    # Sinusoidal velocity profile for scanning
    targetVelocity = scanSpeed * math.sin(step * 0.01)
    
    p.setJointMotorControl2(robotId,
                           jointIndex,
                           p.VELOCITY_CONTROL,
                           targetVelocity=targetVelocity,
                           force=50)
    
    p.stepSimulation()

Torque Control for Gravity Compensation

import pybullet as p

p.connect(p.GUI)
robotId = p.loadURDF("pr2_gripper.urdf")

# Enable force sensors for feedback
for i in range(p.getNumJoints(robotId)):
    p.enableJointForceTorqueSensor(robotId, i, 1)

# Gravity compensation loop
for step in range(1000):
    # Get current joint positions and velocities
    numJoints = p.getNumJoints(robotId)
    jointPositions = []
    jointVelocities = []
    
    for i in range(numJoints):
        state = p.getJointState(robotId, i)
        jointPositions.append(state[0])
        jointVelocities.append(state[1])
    
    # Calculate gravity compensation torques
    gravityTorques = p.calculateInverseDynamics(robotId, 
                                               jointPositions, 
                                               [0] * numJoints,  # Zero velocity
                                               [0] * numJoints)  # Zero acceleration
    
    # Apply gravity compensation
    for i in range(numJoints):
        p.setJointMotorControl2(robotId, i, p.TORQUE_CONTROL, force=gravityTorques[i])
    
    p.stepSimulation()

Multi-DOF Spherical Joint Control

import pybullet as p

p.connect(p.GUI)
# Create object with spherical joint
sphericalJointId = 0  # Assuming spherical joint at index 0

# Multi-DOF position control
targetPositions = [0.5, 0.3, 0.1]  # 3 DOF for spherical joint
targetVelocities = [0, 0, 0]
forces = [10, 10, 10]
positionGains = [0.3, 0.3, 0.3]
velocityGains = [1, 1, 1]

for step in range(1000):
    p.setJointMotorControlMultiDof(robotId,
                                  sphericalJointId,
                                  p.POSITION_CONTROL,
                                  targetPosition=targetPositions,
                                  targetVelocity=targetVelocities,
                                  force=forces,
                                  positionGain=positionGains,
                                  velocityGain=velocityGains)
    
    # Monitor multi-DOF joint state
    if step % 100 == 0:
        state = p.getJointStateMultiDof(robotId, sphericalJointId)
        print(f"Spherical joint positions: {state[0]}")
    
    p.stepSimulation()

Trajectory Following with Interpolation

import pybullet as p
import numpy as np

p.connect(p.GUI)
robotId = p.loadURDF("kuka_iiwa/model.urdf")

# Define trajectory waypoints
waypoints = [
    [0, 0, 0, 0, 0, 0, 0],      # Home position
    [0.5, -0.5, 0.8, -1.2, 0, 0.5, 0],   # Waypoint 1
    [1.0, 0, 1.5, -0.8, 0.3, -0.2, 0],   # Waypoint 2
    [0, 0, 0, 0, 0, 0, 0]       # Return home
]

# Trajectory parameters
totalSteps = 2000
stepsPerSegment = totalSteps // (len(waypoints) - 1)
jointIndices = list(range(7))  # 7-DOF arm

for step in range(totalSteps):
    # Calculate current segment and interpolation factor
    segment = min(step // stepsPerSegment, len(waypoints) - 2)
    t = (step % stepsPerSegment) / stepsPerSegment
    
    # Linear interpolation between waypoints
    currentWaypoint = waypoints[segment]
    nextWaypoint = waypoints[segment + 1]
    
    targetPositions = []
    for i in range(len(jointIndices)):
        pos = currentWaypoint[i] + t * (nextWaypoint[i] - currentWaypoint[i])
        targetPositions.append(pos)
    
    # Apply position control
    p.setJointMotorControlArray(robotId,
                               jointIndices,
                               p.POSITION_CONTROL,
                               targetPositions=targetPositions,
                               forces=[100] * len(jointIndices))
    
    p.stepSimulation()
    
    # Monitor progress
    if step % 200 == 0:
        print(f"Step {step}, Segment {segment}, t={t:.2f}")

Best Practices

  1. Choose appropriate control modes - Use position control for precision, velocity for continuous motion, torque for force control
  2. Tune PD gains carefully - Start with low gains and increase gradually to avoid oscillation
  3. Use force limits - Always specify reasonable force limits to prevent damage
  4. Batch operations - Use array functions for multiple joints to improve performance
  5. Monitor joint states - Check joint positions and forces for safety and debugging
  6. Enable sensors selectively - Only enable force sensors when needed to minimize computational overhead
  7. Reset joint states carefully - Use resetJointState for initialization, not during normal control
  8. Consider joint limits - Respect joint position and velocity limits from URDF specifications

External Force and Torque Application

def applyExternalForce(objectUniqueId, linkIndex, forceObj, posObj, flags, physicsClientId=0):
    """
    Apply external force to an object or link.
    
    Args:
        objectUniqueId (int): Object unique ID
        linkIndex (int): Link index (-1 for base)
        forceObj (list): Force vector [Fx, Fy, Fz] in Newtons
        posObj (list): Position where force is applied [x, y, z]
        flags (int): Reference frame flag (WORLD_FRAME or LINK_FRAME)
        physicsClientId (int, optional): Physics client ID
    """

def applyExternalTorque(objectUniqueId, linkIndex, torqueObj, flags, physicsClientId=0):
    """
    Apply external torque to an object or link.
    
    Args:
        objectUniqueId (int): Object unique ID
        linkIndex (int): Link index (-1 for base)
        torqueObj (list): Torque vector [Tx, Ty, Tz] in Newton-meters
        flags (int): Reference frame flag (WORLD_FRAME or LINK_FRAME)
        physicsClientId (int, optional): Physics client ID
    """

def resetBaseVelocity(objectUniqueId, linearVelocity, angularVelocity, physicsClientId=0):
    """
    Reset base linear and angular velocity.
    
    Args:
        objectUniqueId (int): Object unique ID
        linearVelocity (list): Linear velocity [vx, vy, vz]
        angularVelocity (list): Angular velocity [wx, wy, wz]
        physicsClientId (int, optional): Physics client ID
    """

def resetJointState(bodyUniqueId, jointIndex, targetValue, targetVelocity=0, physicsClientId=0):
    """
    Reset joint position and velocity.
    
    Args:
        bodyUniqueId (int): Body unique ID
        jointIndex (int): Joint index
        targetValue (float): Target joint position
        targetVelocity (float, optional): Target joint velocity
        physicsClientId (int, optional): Physics client ID
    """

def resetJointStateMultiDof(bodyUniqueId, jointIndex, targetValue, targetVelocity=None, physicsClientId=0):
    """
    Reset multi-DOF joint state.
    
    Args:
        bodyUniqueId (int): Body unique ID
        jointIndex (int): Joint index
        targetValue (list): Target joint values
        targetVelocity (list, optional): Target joint velocities
        physicsClientId (int, optional): Physics client ID
    """

Force/Torque Application Examples

import pybullet as p
import time

p.connect(p.GUI)
p.setGravity(0, 0, -9.81)
p.loadURDF("plane.urdf")

# Load objects
cube_id = p.loadURDF("cube_small.urdf", [0, 0, 1])
robot_id = p.loadURDF("r2d2.urdf", [1, 0, 1])

# Apply force to cube in world coordinates
for i in range(1000):
    # Apply upward force to counteract gravity plus extra lift
    p.applyExternalForce(
        objectUniqueId=cube_id,
        linkIndex=-1,  # Base link
        forceObj=[0, 0, 15],  # Upward force in Newtons
        posObj=[0, 0, 0],     # At center of mass
        flags=p.WORLD_FRAME
    )
    
    # Apply spinning torque to robot
    p.applyExternalTorque(
        objectUniqueId=robot_id,
        linkIndex=-1,
        torqueObj=[0, 0, 0.5],  # Z-axis torque
        flags=p.WORLD_FRAME
    )
    
    p.stepSimulation()
    time.sleep(1./240.)

# Reset object velocities
p.resetBaseVelocity(cube_id, [0, 0, 0], [0, 0, 0])
p.resetBaseVelocity(robot_id, [0, 0, 0], [0, 0, 0])

# Reset joint to specific state
if p.getNumJoints(robot_id) > 0:
    p.resetJointState(robot_id, 0, 0.5, 0)  # Joint 0 to 0.5 rad, zero velocity

Install with Tessl CLI

npx tessl i tessl/pypi-pybullet

docs

collision-detection.md

connection-simulation.md

index.md

inverse-kinematics-dynamics.md

joint-motor-control.md

mathematical-utilities.md

object-loading.md

physics-configuration.md

rendering-visualization.md

state-management-logging.md

vr-input-handling.md

tile.json