state_space_setup_assistant¶
The web wizard’s backend. See the guide
for the step-by-step tour; the HTTP API is defined in app.create_app.
app — Flask application and HTTP API¶
Flask app: JSON API + static wizard, everything same-origin.
The linearize and design endpoints deliberately go through the YAML files: they write model.yaml / design.yaml to the session workdir and run the same loaders the CLIs use, so exported artifacts are byte-identical to what produced the numbers on screen.
session — wizard state¶
In-memory wizard state.
One WizardSession per server process (single local user). Two browser tabs against the same server share and clobber this state – documented limitation.
- class state_space_setup_assistant.session.WizardSession(urdf_source: Optional[str] = None, urdf_path: Optional[str] = None, urdf_xml: Optional[str] = None, robot_name: Optional[str] = None, joints: List[Any] = <factory>, validation: List[Dict] = <factory>, model_cfg: Optional[Dict] = None, model: Any = None, design_cfg: Optional[Dict] = None, result: Any = None, benchmark: Optional[Dict] = None, output_dir: Optional[str] = None, responses: Dict[str, Dict] = <factory>, workdir: str = <factory>)[source]¶
Bases:
object
introspect — URDF joint/link parsing¶
URDF joint/link introspection with the stdlib XML parser.
Pinocchio flattens some of this information away (fixed joints, mimic tags), so the wizard reads the URDF directly for the joint table, slider limits and validation, and leaves the dynamics to urdf_state_space.
- class state_space_setup_assistant.introspect.JointInfo(name: str, type: str, parent: str, child: str, axis: Tuple[float, float, float] | None = None, lower: float | None = None, upper: float | None = None, effort: float | None = None, velocity: float | None = None, mimic: str | None = None, movable: bool = False)[source]¶
Bases:
object
- class state_space_setup_assistant.introspect.RobotInfo(name: str, links: List[str], joints: List[state_space_setup_assistant.introspect.JointInfo], materials: Dict = <factory>)[source]¶
Bases:
object
validation — control-oriented URDF checks¶
Control-oriented URDF validation, run automatically when a robot loads.
Every check returns a Finding dict {level, code, message, subject} with level ‘ok’ | ‘warn’ | ‘error’. Errors indicate the linearization would be meaningless (bad inertia, disconnected tree); warnings flag things the user should know about (mimic joints, missing limits) but do not block.
equilibrium — automatic operating-point finder¶
Automatic operating-point (equilibrium) finder.
An equilibrium for the actuation set S is a configuration q where the gravity/bias torque on every unactuated joint vanishes:
rnea(q, 0, 0)[unactuated dofs] = 0
Actuated joints can hold any position (u_eq = gravity compensation), so their coordinates are held at the user’s seed values and the root-finding runs only over the unactuated coordinates – a square problem.
Indexing note: the residual and the decision variables are mapped through Pinocchio’s own joint.idx_q / joint.idx_v, never through the position of a name in a Python list; Pinocchio’s q ordering is not guaranteed to match URDF declaration order.
- state_space_setup_assistant.equilibrium.find_equilibrium(urdf_xml: str, actuated_joints: List[str], q_seed: Mapping[str, float] | None = None, floating_base: bool = False, tol: float = 1e-09) Dict[source]¶
Find q_eq near
q_seedsuch that unactuated joints need no torque.Returns {q_eq: {name: value}, u_eq: {name: value}, residual_norm, converged, iterations, free_joints}.
benchmark — controller comparison¶
Benchmark mode: synthesize several controllers and compare them fairly.
The whole point of this module is the single shared metric path:
simulate_metrics owns the time grid, the integration method and the
units for every controller type. It simulates the closed loop once and reads
both y(t) and u(t) off that same trajectory – u = -K x(t) for static state
feedback, controller-state propagation for dynamic output feedback – so the
control-effort column of the comparison table is never computed on different
grids or methods per controller type.
- state_space_setup_assistant.benchmark.design_controller(plant: Plant, name: str, params: Dict, timeout: float = 30.0)[source]¶
Run
make_controller(name, **params).design(plant)in a killable subprocess.Parameter validation (make_controller/__init__) runs in-process so bad params raise ValueError immediately; only .design() — which may call into Fortran code that cannot be interrupted — runs in the child.
- state_space_setup_assistant.benchmark.simulate_metrics(result, input_index: int = 0, t: ndarray | None = None, n_points: int = 600) Dict[source]¶
Step-response metrics for one designed controller.
A unit step enters at the plant input (disturbance rejection task, the natural experiment for regulators about an equilibrium); y(t) and u(t) are read off one simulation.
yamlgen — config file generation¶
Builders for the two YAML artifacts, in the existing schemas.
The wizard never calls the math engines with ad-hoc arguments: it writes these dicts to disk and runs urdf_state_space.build_from_yaml / the same controller path as ss_design on them, so the exported files are exactly what produced the on-screen results.
- state_space_setup_assistant.yamlgen.model_yaml_dict(urdf_source: str, joints: Dict[str, Dict], velocities: bool = False, output_joints: List[str] | None = None, floating_base: bool = False, export_npz: str | None = None, export_mat: str | None = None, dt: float | None = None) Dict[source]¶
Build a model.yaml dict (schema of urdf_state_space.config).
jointsmaps joint name -> {actuated?: bool, damping?: float, q_eq?: float}; empty per-joint entries are dropped.
schemas — controller parameter metadata¶
Parameter metadata for the controller registry, for building UI forms.
Hand-written schemas for the known designs; unknown (future plugin) controllers fall back to an inspect.signature-derived schema so they still get a usable form.
Param kinds understood by the frontend:
square_weight -- scalar / per-diagonal list / full matrix; ``dim`` names
which plant dimension sizes it, ``labels`` which name list
labels the diagonal entries.
tf_weight -- scalar gain or {num: [...], den: [...]} transfer function.
scalar -- plain float.
per_input -- scalar or one value per actuated input.
export — artifact bundle writer¶
Write the artifact bundle for a finished wizard run.
Everything in the bundle reproduces through the existing CLIs:
ros2 run urdf_state_space urdf2ss model.yaml ros2 run state_space_control ss_design plant.npz design.yaml -o controller.npz
ros2_control_export — runtime controller YAML writer¶
Writes the <name>_ros2_control.yaml file consumed by
kontrolem_controllers at runtime — see the
export contract.
Build the C++-consumable <name>_ros2_control.yaml artifact.
controller.npz (written by ControllerResult.save_npz) carries the
plant/controller matrices but not joint_names/q_eq – those live only
on the linearized StateSpaceModel. A ros2_control controller needs both
(to turn a raw measured joint position into the deviation state the gain/
compensator was designed for), so this module builds one flat dict from the
model and the result together, meant to be dumped with
yamlgen.dump_yaml right after result.save_npz in export.py.
Schema (flat nested lists only, so a C++ yaml-cpp reader needs no NumPy
support):
schema_version: 1 controller_type: lqr # or lqg / hinf / hinf_mixsyn / pid name: lqr joint_names: […] # full state order: x = [q - q_eq, qdot] q_eq: […] actuated_joint_names: […] # order matches K’s rows / u u_eq: […] output_names: […] # used only by dynamic-compensator designs K: [[…], …] # present iff a static-gain design ctrl_A/ctrl_B/ctrl_C/ctrl_D: [[…], …] # present instead of K otherwise info: {…} # informational scalars only, not consumed
- state_space_setup_assistant.ros2_control_export.ros2_control_yaml_dict(model, result) Dict[source]¶
Build the
*_ros2_control.yamldict from aStateSpaceModel(session.model) and aControllerResult(session.result).Exactly one of
result.K/result.controllermust be set, matchingControllerResult’s own invariant.
resources — package:// resolution¶
Resource resolution: package:// URIs and containment-guarded mesh paths.
- state_space_setup_assistant.resources.load_urdf(source: str, base_dir: str = '.') tuple[source]¶
Resolve
source(path or package:// URI) and return (absolute_path, processed_urdf_xml). Runs xacro on .xacro files.
Absolute path of
relativeinsidepackage’s share directory, or None if it escapes the share dir (traversal) or does not exist.Raises LookupError if the package itself is unknown (workspace not built/sourced).
cli — the ss_setup_assistant entry point¶
Entry point: run the setup assistant web server and open the browser.
- ros2 run state_space_setup_assistant ss_setup_assistant
–urdf package://rws_description/urdf/rws.urdf
Serves on 127.0.0.1 by default (single local user; see README: one browser tab at a time).