Optuna PID Tuning — Design
Date: 2026-03-10 Status: Approved
Problem
The AR4 arm's PID gains for gravity-enabled Gazebo simulation were tuned manually. Joint 5 (wrist pitch) had 27 degrees of sag before joint dynamics were injected. Current manual tuning achieves ~1.5 degrees error, but systematic optimization can do better and cover trajectory tracking.
Architecture
The optimizer is fully decoupled from ROS 2 — it communicates only via Zenoh. This follows the same pattern used in turtlebot-maze for the YOLO detector and SLAM containers.
Trial Sequence
Each Optuna trial runs this sequence:
- Optimizer samples 24 parameters (P, I, D, i_clamp x 6 joints)
- Publishes gains JSON to
ar4/tuning/gains - Publishes
reconfiguretoar4/tuning/command - Bridge node deactivates JTC, updates params, reactivates JTC
- Bridge node publishes
readyonar4/tuning/status - Phase 1 (Home hold): Optimizer observes
ar4/joint_statesfor ~10s sim time, computes steady-state error at home position (all zeros) - Optimizer publishes
move_to_reachtoar4/tuning/command - Bridge node sends trajectory goal to reach pose via JTC action
- Phase 2 (Trajectory tracking): Optimizer observes joint_states for ~20s sim time, computes settling time and steady-state error at reach pose
- Optimizer computes cost, returns to Optuna
Optimization Parameters
Search Space (per joint, 24 total)
| Param | Range | Scale |
|---|---|---|
| P | 100 - 10000 | log-uniform |
| I | 10 - 2000 | log-uniform |
| D | 5 - 500 | log-uniform |
| i_clamp | 10 - 500 | log-uniform |
Objective Function
steady_state_error: mean absolute error over last 2s of each phasesettling_time: time for joint error to stay within 0.01 rad (~0.6 degrees) of target- Weights: w1=10 (prioritize accuracy), w2=1 (secondary: speed)
Reach Pose
A joint configuration that exercises all joints within safe limits:
Zenoh Interface
Keys
| Key | Direction | Format | Purpose |
|---|---|---|---|
ar4/tuning/gains | optimizer -> bridge | JSON | PID gain vector (24 params) |
ar4/tuning/command | optimizer -> bridge | JSON | Trial commands: reconfigure, move_to_reach, move_to_home |
ar4/tuning/status | bridge -> optimizer | JSON | Trial state: ready, moving, settled, error |
ar4/joint_states | bridge -> optimizer | CDR | Bridged from ROS /joint_states |
Zenoh Storage
In-memory storage for ar4/tuning/* keys and ar4/joint_states, configured in zenoh/zenoh-storage.json5.
Components
| Component | Location | Dependencies | ROS? |
|---|---|---|---|
pid_optimizer.py | ar4_skills/tuning/pid_optimizer.py | optuna, zenoh, pycdr2, numpy | No |
tuning_bridge_node.py | ar4_skills/ar4_skills/tuning_bridge_node.py | rclpy, zenoh, controller_manager_msgs | Yes |
Dockerfile.tuning | docker/Dockerfile.tuning | python:3.12-slim base | No |
| zenoh-storage.json5 | zenoh/zenoh-storage.json5 | - | No |
| docker-compose services | docker-compose.yaml | - | - |
Docker Compose Services
Persistence
- Optuna study:
data/tuning/optuna_study.db(SQLite, resumable) - Best gains:
data/tuning/best_gains.yaml(written after study completes) - Trial logs:
data/tuning/trials.jsonl(per-trial metrics for analysis)
Run Configuration
- Trials: 300
- Sampler: TPE (Optuna default, good for 24-dim)
- Estimated time: ~30s per trial, ~2.5 hours total
- Resumable: yes, Optuna SQLite storage allows stopping and resuming
Success Criteria
- All joints hold home position within 0.5 degrees steady-state error
- Trajectory to reach pose settles within 5 seconds sim time
- No oscillation (velocity stays below 0.1 rad/s after settling)