Plugins and Events
This page describes framework extension points for PyBulletFleet developers. If you only configure or operate a simulation, use callbacks and the public Python/ROS APIs instead of depending on this lifecycle.
Choose the extension point
Need |
Use |
Why |
|---|---|---|
Small scenario-specific update function |
|
Minimal setup; stays local to the simulation |
Reusable lifecycle-managed integration |
|
Receives simulation core and init/reset/shutdown hooks |
React only to a discrete state transition |
|
Avoids polling every step |
Expose a remote transport |
Public Fleet API or ROS bridge |
Keeps transport code outside the simulation loop |
SimPlugin is the framework equivalent of a world plugin. It is appropriate
for reusable integrations such as workcell and RMF bridge plugins, not for a
single action sequence in an example.
Plugin lifecycle
Every hook is optional. A plugin receives sim_core at construction and follows
this lifecycle:
construct -> on_init -> on_step (zero or more times) -> on_reset -> on_shutdown
on_init() runs after the world and robots have been created. Use it to inspect
agents, allocate resources, or subscribe to events. on_step(dt) runs at every
simulation step by default; a configured frequency invokes it less often and
passes the accumulated elapsed time. on_reset() must discard state that does
not survive a reset, and on_shutdown() must release external resources before
PyBullet disconnects.
Plugins can be configured by a registry name or dotted Python class path:
plugins:
- class: my_package.plugins.TrafficMonitor
frequency: 2.0
config:
log_interval: 5.0
Configuration keys are matched to the subclass constructor. Keep plugin configuration explicit and validate any values that affect external I/O.
EventBus contract
sim.events is the global bus for simulation lifecycle, object/agent lifecycle,
collision, pause/resume, and fleet-command events. Each object also lazily
creates object.events for entity-local events. Use SimEvents constants for
the built-in names; custom string names are permitted for package-local events.
Handlers run in ascending priority order; equal priorities preserve registration order. An exception is logged and does not prevent later handlers from running. Handlers should therefore keep work bounded and make externally visible side effects idempotent where practical.
from pybullet_fleet.events import SimEvents
def record_collision(obj_a, obj_b):
...
sim.events.on(SimEvents.COLLISION_STARTED, record_collision, priority=10)
Use events for transitions such as ACTION_COMPLETED or COLLISION_STARTED.
Do not subscribe to PRE_STEP just to poll state that can be queried on demand.
Step-order and pose rule
Plugin on_step, callbacks, and pre-step handlers run in Phase 1 of
step_once(). Kinematic set_pose() calls are buffered there. Framework
getters, such as agent.get_pose(), see the new cached pose; direct PyBullet
queries still see the previous pose until the pose flush completes.
Avoid raw p.getBasePositionAndOrientation() and p.getAABB() in on_step
when a same-step value is required. Use framework getters, a post-step event, or
defer the operation to the next step. The complete ordering and physics boundary
are documented in Two-Phase Step.