Skip to main content
Version: 0.2.7

3D Simulators — Usage Guide

This guide shows how to use the three 3D simulators, from the simplest case (CartPoleView().run()) to customizing physical parameters and initial conditions.


Cart-Pole

Classic control benchmark: a cart on a track with an articulated inverted pendulum. 4-dimensional state, unstable at the vertical equilibrium.

CartPole — real-time 3D simulation

from synapsys.viz import CartPoleView

CartPoleView().run()

Physical parameters (default values):

ParameterDefaultDescription
m_c1.0 kgcart mass
m_p0.1 kgbob mass
l0.5 mpole length
g9.81 m/s²gravity

State: x = [cart position, velocity, angle θ, angular velocity θ̇]

Default LQR: Q = diag([1, 0.1, 100, 10]), R = 0.01·I

Custom initial state:

import numpy as np
CartPoleView(x0=np.array([0.0, 0.0, 0.30, 0.0])).run() # initial angle 0.30 rad
Auto-reset

The cart changes color to amber at 72% of the track and to red at 92%. When it exceeds 92%, the simulation resets automatically.


Inverted Pendulum

Single-link pendulum on a fixed base. The simplest system for testing controllers — only 2 states, unstable pole at +√(g/l).

Inverted Pendulum — real-time 3D simulation

from synapsys.viz import PendulumView

PendulumView().run()

Physical parameters (default values):

ParameterDefaultDescription
m1.0 kgbob mass
l1.0 mpole length
g9.81 m/s²gravity
b0.1viscous damping coefficient

State: x = [θ, θ̇]

Default LQR: Q = diag([80, 5]), R = I


Mass-Spring-Damper

1D mass-spring-damper system with setpoint tracking. The MSD has extra controls in the bar: buttons to select 3 reference positions (0 m, +1.5 m, −1.5 m) and keyboard shortcuts 1/2/3.

Mass-Spring-Damper — real-time 3D simulation

from synapsys.viz import MassSpringDamperView

MassSpringDamperView().run()

Physical parameters (default values):

ParameterDefaultDescription
m1.0 kgmass
c0.5 N·s/mdamping coefficient
k2.0 N/mspring constant

State: x = [q, q̇]

LQR control law (with setpoint feed-forward):

u = −K·(x − x_ref) + k·sp

Available setpoints (keyboard):

KeySetpoint
10.0 m
2+1.5 m
3−1.5 m

Custom setpoints and initial state:

import numpy as np
MassSpringDamperView(
setpoints=[("0", 0.0), ("+2m", 2.0), ("-2m", -2.0)],
x0=np.array([1.0, 0.0]),
).run()

Window anatomy

┌──────────────────────────────────────────────────────────────────────┐
│ Window title │
├──────────────────────────────┬───────────────────────────────────────┤
│ │ ┌─────────────────────────────────┐ │
│ PyVista 3D │ │ Position / angle │ │
│ • physics animation │ ├─────────────────────────────────┤ │
│ • HUD with live │ │ Velocity / angular velocity │ │
│ state values │ ├─────────────────────────────────┤ │
│ │ │ Control force / torque │ │
│ A/D=pert R=reset │ ├─────────────────────────────────┤ │
│ SPACE=pause Q=close │ │ Phase portrait │ │
│ │ │ (current point in cyan) │ │
│ │ └─────────────────────────────────┘ │
├──────────────────────────────┴───────────────────────────────────────┤
│ [◀ Perturb] [──●────── Magnitude: 20 N ──] [Perturb ▶] │
│ [⏸ Pause] [↺ Reset] │
├──────────────────────────────────────────────────────────────────────┤
│ t = 3.42 s | pos = +0.012 m | θ = −0.03° | running │
└──────────────────────────────────────────────────────────────────────┘
RegionContents
3D panel (left, ~55%)Animated physics scene + state HUD + keyboard hints
Telemetry panel (right, ~45%)4 matplotlib charts (update rate: CartPole ~17 Hz · Pendulum/MSD ~20 Hz)
Control bar (80 px)Hold-to-apply perturbation + slider + pause/reset
Status barSimulation time, key variables, state (running/PAUSED)

Telemetry charts by simulator

PanelContentsColor
1Cart position x(t) in m (left axis) + velocity ẋ(t) in m/s (right axis)Blue + dashed orange
2Pole angle θ(t) in degreesOrange
3Control force u(t) in NRed
4Phase portrait (θ vs θ̇)Violet + cyan dot

Keyboard controls

KeyActionNote
A (hold)Negative perturbationRelease → perturbation returns to zero
D (hold)Positive perturbationRelease → perturbation returns to zero
RFull resetReturns to initial state, clears history
SpacePause / ResumeToggles between and
Q / EscClose windowStops timer and closes PyVista
1 / 2 / 3Change setpointMSD only

Shortcuts work regardless of which panel has focus (3D or matplotlib).


Custom physical parameters

All physical parameters can be passed in the constructor:

from synapsys.viz import CartPoleView, PendulumView, MassSpringDamperView

# Heavy cart, long pole
CartPoleView(m_c=3.0, m_p=0.5, l=1.0).run()

# Short pendulum with higher damping
PendulumView(m=0.5, l=0.6, b=0.3).run()

# Stiffer spring, low damping (underdamped)
MassSpringDamperView(m=1.0, c=0.1, k=10.0).run()

When physical parameters change, the automatic LQR is redesigned internally via sim.linearize() — no manual tuning needed.


Perturbations

The ◀ Perturb and Perturb ▶ buttons apply a force/torque while the button is held (hold-to-apply). Releasing the button returns the perturbation to zero. Equivalent to holding A or D on the keyboard.

The magnitude slider sets the maximum perturbation value. Ranges by simulator:

SimulatorRangeDefault
Cart-Pole1–80 N30 N
Pendulum1–40 N·m20 N·m
MSD1–30 N15 N

Camera presets

Four named camera angles are available via set_camera_preset(). Call it before run():

PresetDescription
"iso"Isometric — good for seeing the full scene
"top"Top-down orthographic
"side"Side view (X–Z plane)
"follow"Low follow-cam, close behind the system
from synapsys.viz import CartPoleView

view = CartPoleView()
view.set_camera_preset("top")
view.run()

3D trajectory trail

Enable a trajectory trail that traces the pole tip (or mass position) over time. Call toggle_trail() before run():

from synapsys.viz import CartPoleView

view = CartPoleView()
view.toggle_trail() # enable — shown in violet (#7c3aed)
view.run()

The trail keeps the last 200 points. Call toggle_trail() again to disable and clear it. Each view tracks its own physically meaningful position:

ViewTrail point
CartPoleViewPole tip [p + l·sin(θ), 0, pivot_z + l·cos(θ)]
PendulumViewPole tip [l·sin(θ), 0, pivot_z + l·cos(θ)]
MassSpringDamperViewMass centre [q, 0, mass_h/2]

Saving animations

Pass save= to any SimView constructor to record the session to a file when the window closes. Requires Pillow for GIF or ffmpeg for MP4.

from synapsys.viz import CartPoleView, PendulumView, MassSpringDamperView

CartPoleView(save="cartpole.gif").run()
PendulumView(save="pendulum.mp4").run()
MassSpringDamperView(save="msd.gif").run()

For the lightweight 2D view, pass save= to animate():

from synapsys.viz import CartPole2DView

CartPole2DView().animate(save="cartpole2d.gif")

Color palette

All visual elements use the canonical palette tokens. See Dark color tokens → and Light color tokens →

from synapsys.viz.palette import Dark, Light, mpl_theme

mpl_theme() # apply dark theme globally to matplotlib
mpl_theme("light") # light theme (white background)