Quickstart

From a bare ROS 2 machine to an animated closed-loop response in five minutes, using the bundled example robot — a cart carrying a double inverted pendulum, linearized about its unstable upright equilibrium.

This is the fast, command-line tour

It assumes you’re comfortable in a terminal and just want the whole pipeline in one page. If you’d rather be walked through it gently — with what each step means — start with the plain-language concepts and the step-by-step tutorials instead.

Install

Requires ROS 2 Humble (or newer) and Python ≥ 3.10.

# 1. Create a workspace and get the source
mkdir -p ~/ws_kontrolem/src && cd ~/ws_kontrolem
git clone --recurse-submodules https://github.com/rahgirrafi/kontrolem.git src

# 2. Python dependencies
pip install pin            # Pinocchio dynamics backend (NOT "pip install pinocchio")
pip install numpy scipy pyyaml flask
pip install control slycot # required only for the H∞ controllers

# 3. Build
colcon build --symlink-install
source install/setup.bash

The five-minute tour

a) URDF → linear plant

ros2 run urdf_state_space urdf2ss \
    src/packages/urdf_state_space/examples/example_model.yaml

This reads the example YAML — which names the URDF, the actuated joints (cart_joint, joint1; joint2 stays passive), per-joint damping, and the operating point — linearizes analytically with Pinocchio, prints the (A, B, C, D) matrices, poles, controllability/observability, and writes model.npz. The upright pose is unstable: expect two poles in the right half-plane.

b) Plant → LQR controller

ros2 run state_space_control ss_design \
    model.npz src/packages/state_space_control/examples/lqr_design.yaml \
    -o controller.npz

Prints the gain K, the closed-loop poles (now all in the left half-plane) and a stability verdict; writes controller.npz.

c) Or do everything in the browser

ros2 run state_space_setup_assistant ss_setup_assistant \
    --urdf package://kontrolem_example_robots/urdf/cart_double_inverted_pendulum.urdf

Open http://127.0.0.1:8060/ and walk the eight steps: the URDF is validated for control use, the operating point is chosen on live 3D sliders (with an automatic equilibrium finder), the model is linearized, a controller designed, the closed-loop response animated on the robot model in sync with the analysis plots, candidate controllers benchmarked on one shared metric grid, and everything exported as a reproducible bundle. See the wizard guide.

d) Replay the response in RViz

The wizard’s export bundle contains trajectory.npz — a RobotTrajectory file:

ros2 launch state_space_response_viz view_response.launch.py \
    trajectory:=trajectory.npz \
    urdf:=install/kontrolem_example_robots/share/kontrolem_example_robots/urdf/cart_double_inverted_pendulum.urdf \
    fixed_frame:=world

Transport control at runtime:

ros2 service call /response_player/pause std_srvs/srv/Trigger
ros2 param set /response_player speed 0.5
ros2 param set /response_player seek 1.25

From Python

from urdf_state_space import build_from_yaml
from state_space_control import Plant, make_controller

ss, _ = build_from_yaml('example_model.yaml')      # URDF -> (A,B,C,D)
plant = Plant.from_model(ss)
result = make_controller('lqr', Q=[1, 10, 10, 1, 1, 1], R=0.1).design(plant)
print(result.summary())                            # gains, poles, stability

And to simulate + record a response trajectory without the wizard:

from state_space_control.excitations import make_excitation
from state_space_control.simulation import simulate_response, to_robot_trajectory

sim = simulate_response(result, make_excitation('impulse', area=1.0))
traj = to_robot_trajectory(ss, result, sim)        # absolute joint space
traj.save_npz('trajectory.npz')                    # playable in RViz / the wizard

Running the tests

Every Python package ships a headless pytest suite (no ROS graph, no display needed):

cd src/packages/state_space_control && python -m pytest test/ -v
# or, for the whole workspace after a colcon build:
colcon test && colcon test-result --verbose