libstp.step.motion.auto_tune

Auto-tune PID controllers via system identification and iterative optimization.

Three-phase sequential pipeline:

Phase 1 - Drive characterization:

Measure physical limits (max velocity, acceleration, deceleration) per axis by commanding raw velocities and observing response. Uses the existing CharacterizeDrive step. Results are applied in-memory and persisted so that subsequent phases have accurate constraints.

Phase 2 - Velocity controller tuning:

Step-response identification using inflection tangent method (ported from Torsten Brischalle’s ControlTheory, MIT license). Extracts plant parameters (gain Ks, dead time Tu, time constant Tg), then computes PID gains via CHR set-point-follow formulas (kp, ki, kd). Validates by comparing ISE before/after.

Phase 3 - Motion controller tuning:

Coordinate descent (Hooke-Jeeves) optimizer that runs real test motions, scores settling time + overshoot + final error, and iteratively adjusts distance.kp/kd and heading.kp/kd.

Factory functions:

auto_tune() - full pipeline (characterize, velocity, motion) auto_tune_velocity() - phase 2 only auto_tune_motion() - phase 3 only

Classes

PlantParams

Identified plant parameters from step response.

StepResponseData

Raw step response recording.

VelocityTuneResult

Result of velocity controller tuning for one axis.

MotionTuneResult

Result of motion controller tuning for one parameter set.

AutoTuneVelocity

Tune velocity controllers via step-response system identification.

AutoTuneMotion

Tune motion PID controllers via Hooke-Jeeves coordinate descent.

AutoTune

Full auto-tune pipeline: characterize -> velocity PID -> motion PID.

Functions

auto_tune(→ AutoTune)

Auto-tune the full drive system: characterize, velocity PID, motion PID.

auto_tune_velocity(→ AutoTuneVelocity)

Tune velocity controllers via step-response system identification.

auto_tune_motion(→ AutoTuneMotion)

Tune motion PID controllers via iterative real-world optimization.

Module Contents

class libstp.step.motion.auto_tune.PlantParams

Identified plant parameters from step response.

Ks: float = 0.0
Tu: float = 0.0
Tg: float = 0.0
method: str = ''
class libstp.step.motion.auto_tune.StepResponseData

Raw step response recording.

times: list[float] = []
commanded: list[float] = []
measured: list[float] = []
class libstp.step.motion.auto_tune.VelocityTuneResult

Result of velocity controller tuning for one axis.

axis: str = ''
plant: PlantParams
pid: libstp.foundation.PidGains
ff: libstp.foundation.Feedforward
baseline_ise: float = 0.0
tuned_ise: float = 0.0
accepted: bool = False
class libstp.step.motion.auto_tune.MotionTuneResult

Result of motion controller tuning for one parameter set.

param_name: str = ''
initial_kp: float = 0.0
initial_kd: float = 0.0
final_kp: float = 0.0
final_kd: float = 0.0
initial_score: float = 0.0
final_score: float = 0.0
iterations: int = 0
class libstp.step.motion.auto_tune.AutoTuneVelocity(axes: list[str], persist: bool, csv_dir: str | None)

Bases: libstp.step.Step

Tune velocity controllers via step-response system identification.

For each axis, records a baseline step response, identifies plant parameters (Ks, Tu, Tg) via inflection tangent or rise-time fallback, computes CHR set-point-follow PID gains, validates with a second step response comparing ISE, and accepts or reverts. Corresponds to Phase 2 of the full auto-tune pipeline.

axes
persist
csv_dir
results: dict[str, VelocityTuneResult]
class libstp.step.motion.auto_tune.AutoTuneMotion(axes: list[str], persist: bool, csv_dir: str | None)

Bases: libstp.step.Step

Tune motion PID controllers via Hooke-Jeeves coordinate descent.

For each parameter set (distance, heading), runs alternating test motions (forward/backward or CW/CCW), scores on settling time + overshoot + final error with constraint-aware penalties, and iteratively adjusts kp/kd. Also runs secondary checks at lower speeds after optimization. Corresponds to Phase 3 of the full auto-tune pipeline.

axes
persist
csv_dir
results: dict[str, MotionTuneResult]
class libstp.step.motion.auto_tune.AutoTune(characterize_axes: list[str], vel_axes: list[str], motion_axes: list[str], tune_characterize: bool, tune_velocity: bool, tune_motion: bool, characterize_trials: int, characterize_command_speed: float, persist: bool, csv_dir: str | None)

Bases: libstp.step.Step

Full auto-tune pipeline: characterize -> velocity PID -> motion PID.

Orchestrates the three tuning phases in sequence. Phase 1 characterizes drive limits, Phase 2 tunes velocity controllers via step-response identification, and Phase 3 optimizes motion PID gains via coordinate descent on real test motions. Each phase can be individually enabled/disabled.

characterize_axes
vel_axes
motion_axes
tune_characterize
tune_velocity
tune_motion
characterize_trials
characterize_command_speed
persist
csv_dir
libstp.step.motion.auto_tune.auto_tune(vel_axes: list[str] | None = None, characterize_axes: list[str] | None = None, motion_axes: list[str] | None = None, tune_characterize: bool = True, tune_velocity: bool = True, tune_motion: bool = True, characterize_trials: int = 3, characterize_command_speed: float = 1.0, persist: bool = True, csv_dir: str = _DEFAULT_CSV_DIR) AutoTune

Auto-tune the full drive system: characterize, velocity PID, motion PID.

Runs a three-phase sequential pipeline that takes the robot from unknown hardware limits to fully tuned PID controllers. Each phase builds on the results of the previous one:

Phase 1 – Drive characterization. Commands raw velocities to measure max velocity, acceleration, and deceleration for each axis. Multiple trials are run and the median is taken. Results are stored so that subsequent phases have accurate physical constraints for profile generation.

Phase 2 – Velocity controller tuning. For each velocity axis (e.g., vx, wz), a step-response is recorded at 100 Hz, plant parameters (gain Ks, dead time Tu, time constant Tg) are identified via the inflection tangent method, and PID gains are computed using CHR set-point-follow formulas. A validation step-response is run with the new gains; if the integral of squared error (ISE) improves, the gains are accepted, otherwise the baseline is kept.

Phase 3 – Motion controller tuning. Uses Hooke-Jeeves coordinate descent to optimize the high-level distance and heading PID controllers. Real test drives and turns are executed, scored on a weighted combination of settling time, overshoot, and final error. The optimizer adjusts kp/kd iteratively, halving the search delta when no improvement is found.

All results are applied in-memory immediately and, if persist is enabled, written to raccoon.project.yml so they survive restarts.

This step requires significant clear space and takes several minutes to complete. It is intended for initial robot setup, not competition runs.

Parameters:
  • vel_axes – Velocity axes to tune in Phase 2. Each entry is a velocity component name ("vx" for forward, "wz" for angular). Default ["vx", "wz"].

  • characterize_axes – Axes to characterize in Phase 1. Options are "forward", "lateral", "angular". Default ["forward", "angular"].

  • motion_axes – Motion parameters to optimize in Phase 3. Options are "distance" and "heading". Default ["distance", "heading"].

  • tune_characterize – Whether to run Phase 1. Set to False if the robot’s limits are already known. Default True.

  • tune_velocity – Whether to run Phase 2. Default True.

  • tune_motion – Whether to run Phase 3. Default True.

  • characterize_trials – Number of trials per axis in Phase 1. More trials improve robustness but take longer. Default 3.

  • characterize_command_speed – Raw velocity command magnitude for Phase 1, in m/s (linear) or rad/s (angular). Default 1.0.

  • persist – If True, write all results to raccoon.project.yml. Default True.

  • csv_dir – Directory for diagnostic CSV output (step-response recordings, etc.). Default "/tmp/auto_tune".

Returns:

An AutoTune step that runs the full three-phase pipeline.

Example:

from libstp.step.motion import auto_tune

# Full auto-tune with defaults
step = auto_tune()

# Skip characterization (already done), tune only velocity + motion
step = auto_tune(tune_characterize=False)

# Tune only the forward velocity axis and distance controller
step = auto_tune(
    vel_axes=["vx"],
    motion_axes=["distance"],
    characterize_axes=["forward"],
)

# Dry run without persisting to YAML
step = auto_tune(persist=False, csv_dir="/tmp/auto_tune_test")
libstp.step.motion.auto_tune.auto_tune_velocity(axes: list[str] | None = None, persist: bool = True, csv_dir: str = _DEFAULT_CSV_DIR) AutoTuneVelocity

Tune velocity controllers via step-response system identification.

This is Phase 2 of the full auto-tune pipeline, usable standalone when drive characterization has already been performed (or when the hardware limits are already configured in raccoon.project.yml).

For each velocity axis the process is:

  1. Baseline recording – command a step velocity at 50% of the characterized max and record the response at 100 Hz.

  2. Plant identification – extract gain (Ks), dead time (Tu), and time constant (Tg) using the inflection tangent method (local quadratic regression to find the inflection point, then tangent-line intersections). Falls back to 10%/63% rise-time estimation if the inflection method fails.

  3. Gain computation – compute PID gains via CHR set-point-follow formulas, scaled down because a kV=1.0 feedforward handles steady-state.

  4. Validation – apply the new gains and re-run the step response. If the integral of squared error (ISE) improves, the gains are accepted; otherwise the baseline gains are restored.

Accepted gains are applied in-memory to the drive’s velocity control config and optionally persisted to raccoon.project.yml under robot.drive.vel_config.

Prerequisites: Drive characterization should have been run first so that max velocity values are available for computing the step command magnitude.

Parameters:
  • axes – Velocity axes to tune. Each entry is a velocity component name: "vx" (forward linear) or "wz" (angular). Default ["vx", "wz"].

  • persist – If True, write accepted gains to raccoon.project.yml. Default True.

  • csv_dir – Directory for step-response CSV files (baseline and tuned recordings per axis). Default "/tmp/auto_tune".

Returns:

An AutoTuneVelocity step that identifies and tunes velocity controllers.

Example:

from libstp.step.motion import auto_tune_velocity

# Tune both velocity axes with defaults
step = auto_tune_velocity()

# Tune only the forward axis, save CSVs for plotting
step = auto_tune_velocity(
    axes=["vx"],
    csv_dir="/tmp/vel_tune_plots",
)
libstp.step.motion.auto_tune.auto_tune_motion(axes: list[str] | None = None, persist: bool = True, csv_dir: str = _DEFAULT_CSV_DIR) AutoTuneMotion

Tune motion PID controllers via iterative real-world optimization.

This is Phase 3 of the full auto-tune pipeline, usable standalone when velocity controllers are already tuned and drive limits are characterized.

Uses Hooke-Jeeves coordinate descent to optimize the high-level distance and/or heading PID controllers (kp and kd). The process for each parameter set is:

  1. Initial evaluation – run a test motion (0.5 m drive for distance, 90-degree turn for heading) with the current gains and compute a weighted score from settling time, overshoot, and final error.

  2. Coordinate descent – try perturbing kp up/down by a delta, keep the direction that improves the score. Then do the same for kd. Repeat for up to 10 iterations. When neither direction improves, halve the deltas. Stop when deltas fall below a minimum threshold.

  3. Constraint-aware scoring – the optimizer prioritizes fast completion but heavily penalizes candidates that exceed soft limits on overshoot (10 mm / 3 degrees) or final error (10 mm / 2 degrees), preventing “fast but sloppy” solutions.

  4. Secondary speed checks – after optimization at full speed, the final gains are tested at lower speeds (50%, 30%) for monitoring purposes (these do not affect the optimization).

Trials alternate between forward/backward (or CW/CCW) to reduce directional bias.

Prerequisites: Velocity controllers should be tuned first (Phase 2) and drive limits characterized (Phase 1) for best results. The robot needs enough space for 0.5 m drives and 90-degree turns.

Parameters:
  • axes – Motion parameters to optimize. Options are "distance" (linear drive kp/kd) and "heading" (turn kp/kd). Default ["distance", "heading"].

  • persist – If True, write final gains to raccoon.project.yml under robot.motion_pid. Default True.

  • csv_dir – Directory for diagnostic CSV output. Default "/tmp/auto_tune".

Returns:

An AutoTuneMotion step that optimizes motion controller gains.

Example:

from libstp.step.motion import auto_tune_motion

# Tune both distance and heading controllers
step = auto_tune_motion()

# Tune only the heading controller
step = auto_tune_motion(axes=["heading"])

# Tune without persisting (for experimentation)
step = auto_tune_motion(persist=False)