Skip to content

Getting started

1. Install

Use Python 3.9 or newer. Download and extract the repository ZIP, then run this from its root. For balance near upright, the base install is enough:

python -m pip install -e .

For swing-up, images, and faster mechanics, add the matching extras:

python -m pip install -e '.[opt,viz,speed]'

opt installs CasADi/IPOPT. viz installs Matplotlib and Pillow. speed installs Numba. docs installs MkDocs for local site previews. In Windows PowerShell, replace python -m pip with py -m pip if needed. Quote the bracket expression in both shells.

After the PyPI release, python -m pip install 'zcartpole[opt,viz,speed]' will also work. Example JSON files live in the repository, not in the installed wheel.

2. Simulate in Python

For arrays and plots in one Python session, install [viz] and run:

import matplotlib.pyplot as plt
from zcartpole import ZNCartPole, simulate

model = ZNCartPole(m=[0.1], l=[0.4], M=1.0, u_max=80.0)
sol = simulate(model, lambda t, state: model.lqr_control(state),
               th0=[0.05], t_max=3.0, dt=0.002)
plt.plot(sol.t, sol.y[0])
plt.xlabel('Time (s)')
plt.ylabel('Cart position (m)')
plt.show()

sol.t, sol.y, and sol.u are NumPy arrays. This example applies ideal force without an actuator. The full runnable file is examples/python_balance.py. For a configured actuator, see the Python API.

3. Run a configured balance model

Use the source JSON example:

python -m zcartpole examples/quick_transfer_balance.json -o balance_run

From any directory, you can instead pass an absolute path to your own JSON file. A new output directory is required each run. For balance, start near upright: angle 0 is upright; angle π is hanging. Use one angle per link.

For one, two, or three links, run the Python examples. The script builds the JSON-equivalent configuration directly with SystemConfig.from_dict.

From the extracted source directory:

python -m zcartpole examples/actuator_transfer_system.json -o triple_run

This example uses three links, a first-order transfer function, a 10 ms command delay, and a six-second nominal plan. It requires [opt]. It may take much longer than the balance example because it solves a nonlinear program for each seed. The values are illustrative; this run does not describe a specific motor.

To save a GIF, set "animation": true in simulation and install [viz]. The file will be triple_run/simulation.gif when a trajectory exists. The source example examples/animate_link_counts.py also generates GIFs for n = 1, 2, and 3.

5. Read the run

Start with summary.json: check status, plan.success for swing-up, simulation.peak_cart_m, simulation.peak_applied_force_n, saturation fractions when present, and catch or balance_terminal. Then plot trajectory.csv. plan.csv is the nominal ideal-force path, not the achieved response.

Status Meaning
caught Final state meets the built-in numerical catch tolerances.
missed_catch Simulation ended inside the rail but outside a catch tolerance.
rail_violation Cart crossed the rail limit; the run stopped before any collision model.
planning_failed No nominal swing-up candidate passed the planner checks.

The runner returns shell exit code 0 only for caught, 1 for another run status, and argparse code 2 for input or file errors. See outputs for each file and field.

6. Change the model

Update lengths, masses, friction, and the complete command-to-force transfer function in the JSON. Set force_limit_n and command bounds separately. The transfer function can include the motor, gearbox, controller, and transmission. See the configuration reference and actuator guide for exact fields and units.