.. _api-simulation:
============================
Simulation API Reference
============================
This page documents **ManipulaPy.sim**, the module for PyBullet-based simulation capabilities with real-time visualization, physics simulation, and interactive control.
.. note::
All ``Simulation`` methods raise :class:`ImportError` with the hint
``pip install ManipulaPy[simulation]`` if PyBullet is not installed.
The ``[simulation]`` extra is required for any sim-related code.
.. versionchanged:: 1.3.2
Every public ``Simulation`` method that touches PyBullet now performs an
explicit availability check and raises :class:`ImportError` with the
install hint above when PyBullet is missing. Previously, calling these
methods after constructing a ``Simulation`` via ``__new__`` (or after
hot-swapping the module) surfaced an ``AttributeError`` on ``NoneType``
instead. PyBullet ``>= 3.2.5`` is now sufficient — quaternion construction
no longer depends on ``p.getQuaternionFromAxisAngle`` (added in 3.2.7) and
uses inline axis-angle math instead. Repeated ``Simulation()``
instantiation also no longer duplicates log handlers on
``SimulationLogger``.
.. tip::
For conceptual explanations, see :doc:`../user_guide/Simulation`.
---
Quick Navigation
================
.. contents::
:local:
:depth: 2
:backlinks: none
---
Simulation Class
================
.. currentmodule:: ManipulaPy.sim
.. autoclass:: Simulation
:no-members:
:show-inheritance:
Main class for PyBullet-based simulation of robotic manipulators with real-time physics, visualization, and interactive control capabilities.
.. rubric:: Constructor
.. automethod:: __init__
**Parameters:**
- **urdf_file_path** (*str*) -- Path to the URDF file describing the robot
- **joint_limits** (*list*) -- List of tuples representing joint angle limits
- **torque_limits** (*list, optional*) -- List of tuples representing torque limits
- **time_step** (*float, optional*) -- Simulation time step (default: 0.01)
- **real_time_factor** (*float, optional*) -- Real-time scaling factor (default: 1.0)
- **physics_client** (*int, optional*) -- PyBullet physics client ID
---
Simulation Management
=====================
Environment Setup
-----------------
.. automethod:: Simulation.setup_simulation
.. automethod:: Simulation.connect_simulation
.. automethod:: Simulation.disconnect_simulation
.. automethod:: Simulation.close_simulation
Robot Initialization
--------------------
.. automethod:: Simulation.initialize_robot
.. automethod:: Simulation.initialize_planner_and_controller
---
Robot Control and State
========================
Joint Control
-------------
.. automethod:: Simulation.set_joint_positions
.. py:method:: Simulation.set_joint_positions(joint_positions, forces=None)
:noindex:
:param joint_positions: Target joint positions, one per non-fixed joint.
:param forces: Per-joint maximum motor force passed to PyBullet's
``POSITION_CONTROL``. When ``None`` (the default), the value is
auto-derived from ``self.torque_limits``: if ``torque_limits`` is a
list of ``(min, max)`` pairs, each pair is collapsed to its
``max(|min|, |max|)`` so the motor can both push and pull within the
configured limits. If ``torque_limits`` is unset, a default of
``1000.0 N`` per joint is used.
.. versionchanged:: 1.3.2
Added the ``forces`` keyword argument and the auto-derivation of
per-joint force magnitudes from ``self.torque_limits`` when
``forces=None``. Previously, position commands were issued without an
explicit ``forces=`` array and ``torque_limits`` (when shaped as
``(N, 2)``) was passed straight through to PyBullet, which expects a
flat scalar per joint.
.. automethod:: Simulation.get_joint_positions
.. automethod:: Simulation.get_joint_parameters
Trajectory Execution
--------------------
.. automethod:: Simulation.run_trajectory
.. automethod:: Simulation.simulate_robot_motion
.. automethod:: Simulation.simulate_robot_with_desired_angles
Controller Integration
----------------------
.. automethod:: Simulation.run_controller
.. py:method:: Simulation.run_controller(desired_positions)
:noindex:
Drive the robot through ``desired_positions`` in open-loop position
control, one configuration per simulation step.
:param desired_positions: Array-like with shape ``(N, DOF)`` — one row per
waypoint, one column per non-fixed joint. Empty input or a row width
that does not match ``len(self.non_fixed_joints)`` raises
:class:`ValueError`.
:returns: The end-effector world position after the final waypoint, as
returned by ``p.getLinkState(...)[4]``.
:rtype: numpy.ndarray
.. versionchanged:: 1.3.2
The signature was reduced to a single positional argument. The previous
multi-argument form
``run_controller(thetalistd, dthetalistd, ddthetalistd, g, Ftip, Kp,
Ki, Kd, dt)`` — which accepted dynamics, gravity, end-effector wrench,
and PID gains — was removed because the loop body never produced
honest closed-loop behavior (computed torques were applied as position
deltas). For real closed-loop torque control, drive PyBullet's
``p.TORQUE_CONTROL`` mode directly in your own loop and feed it the
torques produced by :class:`ManipulaPy.control.ManipulatorController`.
---
Interactive Controls
====================
GUI Elements
------------
.. automethod:: Simulation.add_joint_parameters
.. automethod:: Simulation.add_reset_button
.. automethod:: Simulation.add_additional_parameters
Manual Control
--------------
.. automethod:: Simulation.manual_control
.. automethod:: Simulation.update_simulation_parameters
---
Visualization and Analysis
==========================
Trajectory Visualization
------------------------
.. automethod:: Simulation.plot_trajectory
.. py:method:: Simulation.plot_trajectory(ee_positions, line_width=3, color=None)
:noindex:
:param ee_positions: List of end-effector positions ``[[x, y, z], ...]``
to render as a 3D capsule spline.
:param line_width: Thickness factor for the trajectory; scales the
underlying capsule radius and the number of parallel capsules drawn
per segment.
:param color: RGB triplet ``[r, g, b]`` (values in ``0..1``) for the
trajectory. Defaults to ``None``, in which case a red colour
(``[1, 0, 0]``) is selected at call time.
.. versionchanged:: 1.3.2
The default value of ``color`` changed from a mutable list/tuple
default (which leaked the same object across calls) to ``None``. The
red default is now constructed fresh on each invocation when
``color is None``.
.. automethod:: Simulation.plot_trajectory_in_scene
Data Management
---------------
.. automethod:: Simulation.save_joint_states
---
Safety and Monitoring
=====================
.. automethod:: Simulation.check_collisions
.. automethod:: Simulation.step_simulation
---
Main Execution
==============
.. automethod:: Simulation.run
---
Logging and Utilities
=====================
.. automethod:: Simulation.setup_logger
---
Usage Examples
==============
Basic Simulation Setup
----------------------
.. code-block:: python
from ManipulaPy.sim import Simulation
import numpy as np
# Define joint limits for a 6-DOF robot
joint_limits = [(-np.pi, np.pi)] * 6
torque_limits = [(-50, 50)] * 6
# Create simulation instance
sim = Simulation(
urdf_file_path="robot.urdf",
joint_limits=joint_limits,
torque_limits=torque_limits,
time_step=0.01,
real_time_factor=1.0
)
# Initialize robot and components
sim.initialize_robot()
sim.initialize_planner_and_controller()
Running a Trajectory
--------------------
.. code-block:: python
# Define a simple trajectory
trajectory = [
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0], # Start position
[0.5, 0.3, -0.2, 0.1, 0.4, 0.0], # Intermediate
[1.0, 0.5, -0.5, 0.2, 0.6, 0.1] # End position
]
# Run the trajectory
final_ee_pos = sim.run_trajectory(trajectory)
print(f"Final end-effector position: {final_ee_pos}")
Interactive Manual Control
--------------------------
.. code-block:: python
# Add GUI controls
sim.add_joint_parameters()
sim.add_reset_button()
sim.add_additional_parameters()
# Start manual control mode
sim.manual_control()
Open-Loop Position Tracking
---------------------------
.. code-block:: python
import numpy as np
# Define joint configurations to visit
desired_positions = np.array([[0.1, 0.2, 0.3, 0.0, 0.0, 0.0]])
# Run open-loop position tracking
final_pos = sim.run_controller(desired_positions)
Collision Monitoring
--------------------
.. code-block:: python
# Enable collision checking during simulation
while True:
# Update robot state
joint_positions = sim.get_joint_parameters()
sim.set_joint_positions(joint_positions)
# Check for collisions
sim.check_collisions()
# Step simulation
sim.step_simulation()
Data Logging and Analysis
-------------------------
.. code-block:: python
# Save joint states during simulation
sim.save_joint_states("robot_states.csv")
# Visualize trajectory in 3D
joint_trajectory = [
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
[0.5, 0.5, 0.5, 0.0, 0.0, 0.0],
[1.0, 1.0, 1.0, 0.0, 0.0, 0.0]
]
ee_trajectory = []
for joints in joint_trajectory:
# Calculate end-effector position for each joint configuration
ee_pos = sim.robot.forward_kinematics(joints)[:3, 3]
ee_trajectory.append(ee_pos)
sim.plot_trajectory_in_scene(joint_trajectory, ee_trajectory)
Complete Simulation Loop
------------------------
.. code-block:: python
# Complete example with trajectory execution and manual control
try:
# Generate trajectory using path planner
thetastart = np.zeros(6)
thetaend = np.array([0.5, 0.3, -0.2, 0.1, 0.4, 0.0])
trajectory_data = sim.trajectory_planner.joint_trajectory(
thetastart, thetaend, Tf=5.0, N=100, method=3
)
# Run the main simulation loop
sim.run(trajectory_data["positions"])
except KeyboardInterrupt:
print("Simulation interrupted by user")
finally:
sim.close_simulation()
---
Configuration Options
=====================
Simulation Parameters
---------------------
The simulation can be configured with various parameters:
.. list-table::
:header-rows: 1
:widths: 20 15 65
* - Parameter
- Default
- Description
* - ``time_step``
- 0.01
- Physics simulation time step in seconds
* - ``real_time_factor``
- 1.0
- Speed multiplier for real-time simulation
* - ``joint_limits``
- Required
- List of (min, max) tuples for each joint
* - ``torque_limits``
- None
- Optional torque limits for each joint
Physics Settings
----------------
The simulation uses PyBullet's physics engine with the following default settings:
- **Gravity**: [0, 0, -9.81] m/s²
- **Solver iterations**: PyBullet default
- **Contact breaking threshold**: PyBullet default
- **Collision detection**: Enabled for all robot links
GUI Controls
------------
When using interactive mode, the following GUI elements are available:
- **Joint sliders**: Individual control for each robot joint
- **Reset button**: Return robot to home position
- **Gravity control**: Adjust gravitational acceleration
- **Time step control**: Modify simulation time step
---
Performance Considerations
==========================
GPU Acceleration
----------------
The simulation module integrates with CuPy for GPU-accelerated computations:
- Controller calculations use CuPy arrays for improved performance
- Large trajectory computations benefit from GPU acceleration
- Memory management is handled automatically
Real-time Performance
---------------------
For optimal real-time performance:
- Use appropriate ``time_step`` values (0.001-0.01 seconds)
- Adjust ``real_time_factor`` based on computational load
- Monitor collision detection frequency for complex robots
- Consider reducing visualization quality for faster simulation
---
Error Handling
==============
Common Issues and Solutions
---------------------------
.. note::
The simulation module includes comprehensive error handling for common issues:
**URDF Loading Errors**
- Verify URDF file path and format
- Check for missing mesh files or textures
- Ensure joint limits are properly defined
**Physics Instability**
- Reduce time step for better numerical stability
- Check for unrealistic joint limits or masses
- Verify contact parameters are reasonable
**GUI Issues**
- Ensure PyBullet GUI mode is properly initialized
- Check for conflicting parameter names
- Verify graphics drivers support OpenGL
**Memory Issues**
- Monitor CuPy memory usage for large trajectories
- Use batch processing for very long simulations
- Clear visualization lines periodically
---
Integration with Other Modules
==============================
Path Planning Integration
-------------------------
.. code-block:: python
# Using trajectory planner with simulation
from ManipulaPy.path_planning import TrajectoryPlanning
planner = TrajectoryPlanning(
sim.robot,
sim.urdf_file_path,
sim.dynamics,
sim.joint_limits,
sim.torque_limits
)
# Generate smooth trajectory
trajectory = planner.joint_trajectory(
thetastart, thetaend, Tf=3.0, N=150, method=5
)
# Execute in simulation
sim.run_trajectory(trajectory["positions"])
Open-Loop Tracking Integration
------------------------------
.. code-block:: python
import numpy as np
# Use positions generated by a planner or custom waypoint list
positions = np.array([
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
[0.1, 0.2, 0.1, 0.0, 0.1, 0.0],
])
# Execute open-loop position tracking
sim.run_controller(positions)
Vision System Integration
-------------------------
.. code-block:: python
# Camera-based simulation feedback
from ManipulaPy.vision import Vision
from ManipulaPy.perception import Perception
# Setup vision system (if available)
vision = Vision(use_pybullet_debug=True)
perception = Perception(vision_instance=vision)
# Use perception for obstacle avoidance in simulation
obstacles, labels = perception.detect_and_cluster_obstacles()
# Modify trajectory based on detected obstacles
# (Implementation depends on specific requirements)
---
See Also
========
* :doc:`control` -- Controller implementations for simulation
* :doc:`path_planning` -- Trajectory generation for simulation
* :doc:`urdf_processor` -- URDF loading and robot model creation
* :doc:`dynamics` -- Robot dynamics for physics simulation
* :doc:`kinematics` -- Forward and inverse kinematics
* :doc:`vision` -- Computer vision integration
* :doc:`perception` -- Environmental perception capabilities
External References
===================
* `PyBullet Documentation `_
* `CuPy Documentation `_
* `URDF Specification `_