Console Guide
The BulletLab Console is a built-in, interactive Python execution environment embedded directly within the BulletLab UI.
It allows you to inspect the simulation, test out API calls on the fly, and even execute long-running scripts sequentially without freezing the physics or the UI.
Why Use the Console?
- Live Debugging: Inspect the state of your robots (e.g.,
robot.joints['motor'].position) instantly without adding print statements and restarting the simulation. - Interactive Tuning: Adjust PID gains, mass, friction, or other properties interactively to see their physical effects in real-time.
- Scripting Motions: Write small multi-line scripts to sequence robot movements and delays without writing separate control loop threads.
How to Use the Console
The console panel appears at the bottom-right of the BulletLab UI by default. It has two modes of operation:
Single-Line Mode (REPL)
When you type a single Python statement or expression, it acts exactly like a standard Python REPL. It evaluates immediately and prints the output.
Multi-Line Mode (Sequential Scripting)
When you expand the console (using the Expand button) or type a multi-line script (like a for loop), BulletLab hands the code to the ConsoleEngine.
The engine executes your script in a background thread, running one statement per simulation frame. This allows you to write sequential control logic without freezing the UI or the physics engine!
Example:
# The loop will execute smoothly, holding the joint for 500ms on each iteration.
for i in range(5):
robot.joints['arm_joint'].position = 1.0
wait(500)
robot.joints['arm_joint'].position = -1.0
wait(500)
Accessing the Namespace
Code executed in the console doesn't run in a vacuum. By default, the BulletLabUI injects key objects from your active script into the console's namespace.
If you initialized your UI like this:
Then the console will automatically have access tosim, my_robot, and telemetry. You can interact with these variables directly:
Built-in Commands
BulletLab's latest version provides 12 built-in commands out of the box to make console usage faster and more intuitive. Because of the dynamic namespace engine, you don't need to import anything to use them—they are always available.
Utility Commands
wait(ms): Pause your script formsmilliseconds. The simulation and UI continue to run normally in the background.step(n=1): Instantly force the simulation to advance bynphysics steps.
Simulation Commands
sim.start(): Start the physics clock.sim.pause(): Freeze physics and the simulation clock.sim.resume(): Unfreeze physics.sim.reset(): Clear the physics state and restore objects to their spawn points.
World Commands
gravity(x, y, z): Set the global gravity vector for the PyBullet world.timescale(factor): Speed up or slow down the simulation execution speed (1.0 = real-time) without changing the underlying physics timestep.
Robot Commands
robot.reset(name): Restore a specific robot to its initial spawn position, orientation, and joint states.robot.scale(name, factor): Uniformly scale a robot.robot.tilt(name, axis, degrees): Rotate a robot around thex,y, orzaxis.robot.delete(name): Remove a robot from the simulation.
Tip: If the variable
robotis already in your namespace as aRobotobject (e.g.robots=[robot]), the built-in commandsrobot.resetandrobot.deleteare safely hidden in favor of the actual object-oriented methods on yourRobotinstance! This means you can just typerobot.delete()without needing to pass the name.
Loading Commands
load(path, position=(0,0,0)): Load a URDF file, robot directory, or Arsenal asset dynamically.
Creating Your Own Commands
You are not limited to the default commands. You can easily create your own custom console commands using the @command decorator!
Just import the decorator, define your function, and it will automatically be registered and available in the console namespace (complete with autocomplete and help text).
Example: Custom Reset Sequence
from bulletlab import Simulation, Robot, command
sim = Simulation(mode="gui").start()
robot = Robot.load("husky.urdf", sim=sim)
@command(
name="dance",
description="Make the robot do a little dance.",
category="Custom"
)
def make_robot_dance():
"""Wiggles the wheels back and forth."""
# Note: custom commands can import and use global variables or context.
robot.joints['front_left'].velocity = 10
robot.joints['front_right'].velocity = -10
# Start your UI
from bulletlab.ui import BulletLabUI
ui = BulletLabUI(sim=sim, robots=[robot])
ui.start()
Now, in the BulletLab UI console, you can simply type:
And your custom logic will execute!Nesting Custom Commands
The ConsoleEngine supports nested proxy objects natively. If you want your command to feel like an object-oriented method, simply use dot-notation in the name!
@command(name="camera.orbit", description="Orbit the camera around the origin.")
def orbit_cam(speed: float = 1.0):
# logic here...
pass
camera.orbit(1.5).