Skip to content

PyBullet¤

PyBullet wraps the Bullet physics engine. It runs in-process -no Docker, no server- and gives you forward kinematics, inverse kinematics (numerical), collision checking, and Cartesian motion planning.

Free-space plan_motion not yet implemented

PyBulletPlanner does not currently implement free-space plan_motion. Cartesian motion via plan_cartesian_motion works. For free-space planning, use one of the ROS backends.

When to use¤

  • You want fast IK + collision checking without starting up ROS.
  • You want to simulate multiple robots or kinematic tools in one scene.
  • You are running headless (CI, batch planning) or from VS Code.
  • You want a PyBullet GUI to visualize the planning scene.

PyBullet is difficult to install inside Rhino 8+.

Trade-offs¤

What you get What you give up
Numerical IK + collision checking + motion planning Numerical IK is slower than analytical IK
Runs anywhere Python runs (no Docker) PyBullet has no pre-built wheels for newest Python versions
Optional GUI showing the planning scene Not usable from inside Rhino

Setup¤

PyBullet is an optional dependency of compas_fab. It is not installed by default because upstream does not ship wheels for the newest Python versions.

uv pip install pybullet

On macOS, PyBullet sometimes needs an extra build flag:

CFLAGS="-fno-define-target-os-macros" uv pip install pybullet

PyBulletClient supports three connection types:

  • direct — headless, no GUI (default)
  • gui — opens a PyBullet OpenGL window
  • shared_memory — connect to an external PyBullet server

First example¤

Inverse + forward kinematics on a UR5 with a GUI:

from compas.geometry import Frame

from compas_fab.backends import PyBulletClient
from compas_fab.backends import PyBulletPlanner
from compas_fab.robots import FrameTarget
from compas_fab.robots import RigidBodyLibrary
from compas_fab.robots import RobotCellLibrary
from compas_fab.robots import TargetMode

with PyBulletClient() as client:
    # Create a robot cell with a UR5 robot
    robot_cell, robot_cell_state = RobotCellLibrary.ur5()

    # Add a floor
    floor = RigidBodyLibrary.floor()
    robot_cell.rigid_body_models["floor"] = floor
    robot_cell_state = robot_cell.default_cell_state()

    planner = PyBulletPlanner(client)
    planner.set_robot_cell(robot_cell)

    # The FrameTarget represents the robot's planner coordinate frame (PCF)
    # For the UR5 robot, the PCF is equal to the frame of the 'tool0' link
    frame_WCF = Frame([0.3, 0.1, 0.5], [1, 0, 0], [0, 1, 0])
    target = FrameTarget(frame_WCF, TargetMode.ROBOT)
    config = planner.inverse_kinematics(target, robot_cell_state)

    print("Inverse kinematics result: ", config)

    input("Observe the IK result in PyBullet's GUI, Press Enter to continue...")

    # To verify the IK result, we can compute the FK with the obtained joint positions
    robot_cell_state.robot_configuration.merge(config)
    frame_WCF = planner.forward_kinematics(robot_cell_state, TargetMode.ROBOT)
    print("Forward kinematics result (main group): \n ", frame_WCF)

    # The result is the same as the 'tool0' link's frame
    frame_WCF = planner.forward_kinematics_to_link(robot_cell_state, "tool0")
    print("Forward kinematics result: (tool0 link): \n ", frame_WCF)

    # However, note that the 'flange' link's frame has a different orientation
    frame_WCF = planner.forward_kinematics_to_link(robot_cell_state, "flange")
    print("Forward kinematics result: (flange link): \n ", frame_WCF)

More examples¤

Setup:

Kinematics:

Motion planning:

API reference¤