Analytical IK + PyBullet¤
The hybrid back-end uses closed-form analytical inverse kinematics for speed and PyBullet underneath for collision checking. It is implemented by AnalyticalPyBulletPlanner paired with AnalyticalPyBulletClient.
When to use¤
- You need fast IK and collision checking without starting up ROS.
- Your robot is in the analytical solver's supported set (Universal Robots, Staubli TX2, ABB IRB4600 40/255).
- You want to filter or rank analytical IK solutions against a real collision scene (workpieces, tools, fixtures).
Trade-offs¤
| What you get | What you give up |
|---|---|
| Closed-form IK (8 solutions per pose, fast) | Only the supported analytical robot families |
| PyBullet collision checking against tools / workpieces / fixtures | No motion planning, IK only (use PyBulletPlanner for that) |
| Runs in-process; no Docker | PyBullet still has to be installed (see PyBullet setup) |
Setup¤
On macOS, PyBullet may need an extra build flag:
The analytical solvers ship with compas_fab itself, nothing else to install.
First example¤
Compute collision-aware IK for a UR5 against an empty cell:
from compas_robots import Configuration
from compas_fab.backends import AnalyticalPyBulletPlanner
from compas_fab.backends import PyBulletClient
from compas_fab.backends import UR5Kinematics
from compas_fab.robots import FrameTarget
from compas_fab.robots import RobotCellLibrary
from compas_fab.robots import TargetMode
with PyBulletClient(connection_type="direct") as client:
robot_cell, robot_cell_state = RobotCellLibrary.ur5()
planner = AnalyticalPyBulletPlanner(client, UR5Kinematics())
planner.set_robot_cell(robot_cell)
planner.set_robot_cell_state(robot_cell_state)
# Pick a reachable, collision-free starting pose and use forward kinematics
# to derive a target frame from it. This guarantees the target is reachable
# by at least one configuration of this robot.
seed = Configuration.from_revolute_values(
[0.0, -1.5708, 1.5708, -1.5708, -1.5708, 0.0],
joint_names=("shoulder_pan_joint", "shoulder_lift_joint", "elbow_joint", "wrist_1_joint", "wrist_2_joint", "wrist_3_joint"),
)
robot_cell_state.robot_configuration.merge(seed)
frame_WCF = planner.forward_kinematics(robot_cell_state, TargetMode.ROBOT)
print("Target frame (derived via FK):", frame_WCF)
target = FrameTarget(frame_WCF, TargetMode.ROBOT)
# `keep_order=True` returns one entry per analytical IK candidate, with
# configurations that fail collision checking returned as `None` rather
# than removed. The indices stay stable across calls, which is useful when
# feeding adjacent IK calls into a Cartesian path solver.
options = {"check_collision": True, "keep_order": True}
for i, config in enumerate(planner.iter_inverse_kinematics(target, robot_cell_state, options=options)):
if config is None:
print(f"[{i}] (in collision)")
continue
print(f"[{i}] {config}")
With keep_order=True, configurations that fail collision checking are
returned as None rather than removed, so the indices stay stable across
calls, useful when feeding adjacent IK calls into a Cartesian path solver.
More examples¤
03_analytical_pybullet_planner.py: full cell with tool attached, workpiece grasp, GUI visualisation04_cartesian_path_analytic_pybullet.py: Cartesian path planning with collision-aware analytical IK
API reference¤
- compas_fab.backends.AnalyticalPyBulletPlanner
- compas_fab.backends.AnalyticalPyBulletClient
- compas_fab.backends: all available analytical solvers