Optuna PID Tuning — Implementation Plan
For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
Goal: Automatically optimize AR4 PID gains using Optuna, communicating with the Gazebo sim via Zenoh (no ROS dependency in the optimizer).
Architecture: Standalone Optuna optimizer container publishes gain vectors and trial commands to Zenoh keys. A tuning bridge node inside the sim container subscribes to these, reconfigures the JointTrajectoryController, executes trajectories, and reports status back via Zenoh. The optimizer evaluates joint_states to compute a cost function (steady-state error + settling time).
Tech Stack: Python 3.12, Optuna (TPE sampler), eclipse-zenoh, pycdr2 (CDR deserialization), rclpy (bridge node only), Docker.
Design doc: docs/plans/2026-03-10-optuna-pid-tuning-design.md
Task 1: Zenoh Router Storage Configuration
Files:
- Create:
zenoh/zenoh-storage.json5
Step 1: Create the zenoh directory and storage config
Step 2: Verify syntax
Run: python3 -c "import json; json.load(open('zenoh/zenoh-storage.json5'))" 2>&1 || echo "JSON5 not valid JSON — that's OK, Zenoh parses JSON5 natively"
JSON5 allows comments and trailing commas which JSON doesn't. The Zenoh router handles it.
Step 3: Commit
Task 2: Docker Compose Zenoh Infrastructure Services
Files:
- Modify:
docker-compose.yaml - Modify:
docker/Dockerfile.gpu(add zenoh-bridge-ros2dds to overlay)
Step 1: Add zenoh-bridge-ros2dds to the overlay Dockerfile
In docker/Dockerfile.gpu, in the overlay stage, after the colcon build, add:
If the apt package doesn't exist for Jazzy, install from the Eclipse Zenoh GitHub releases:
Step 2: Add Zenoh services to docker-compose.yaml
Append these services after the foxglove-bridge service:
Step 3: Rebuild overlay to verify Dockerfile changes
Run: docker compose build overlay 2>&1 | tail -5
Expected: Successful build with zenoh-bridge-ros2dds installed.
Step 4: Test Zenoh router starts
Run: docker compose up -d zenoh-router && sleep 3 && curl -s http://localhost:8000/@/router/local | head -5 && docker compose down zenoh-router
Expected: JSON response from the Zenoh admin API.
Step 5: Commit
Task 3: Tuning Bridge Node (ROS + Zenoh)
This node runs inside the sim container. It receives PID gains and commands from Zenoh, reconfigures the JointTrajectoryController, executes trajectories, and reports status.
Files:
- Create:
ar4_skills/tuning/__init__.py - Create:
ar4_skills/tuning/tuning_bridge_node.py - Create:
tests/test_tuning_bridge.py
Step 1: Write the failing test
Create tests/test_tuning_bridge.py:
Step 2: Run test to verify it fails
Run: cd /mnt/nvme_internal_drive/robotics/arms/ar4-physical-ai && python -m pytest tests/test_tuning_bridge.py -v 2>&1 | tail -10
Expected: FAIL — module not found.
Step 3: Create the tuning bridge module
Create ar4_skills/tuning/__init__.py (empty).
Create ar4_skills/tuning/tuning_bridge_node.py:
Step 4: Run the unit tests (pure functions only, no ROS)
Run: cd /mnt/nvme_internal_drive/robotics/arms/ar4-physical-ai && python -m pytest tests/test_tuning_bridge.py -v 2>&1 | tail -10
Expected: 3 tests PASS.
Step 5: Commit
Task 4: Optuna PID Optimizer (Standalone, Zenoh-only)
Files:
- Create:
ar4_skills/tuning/pid_optimizer.py - Create:
tests/test_pid_optimizer.py
Step 1: Write the failing test
Create tests/test_pid_optimizer.py:
Step 2: Run to verify failure
Run: python -m pytest tests/test_pid_optimizer.py -v 2>&1 | tail -10
Expected: FAIL — module not found.
Step 3: Create the optimizer
Create ar4_skills/tuning/pid_optimizer.py:
Step 4: Run unit tests
Run: pip install optuna numpy && python -m pytest tests/test_pid_optimizer.py -v 2>&1 | tail -10
Expected: 4 tests PASS.
Step 5: Commit
Task 5: Optimizer Dockerfile
Files:
- Create:
docker/Dockerfile.tuning - Create:
ar4_skills/tuning/requirements.txt
Step 1: Create requirements file
Create ar4_skills/tuning/requirements.txt:
Step 2: Create the Dockerfile
Create docker/Dockerfile.tuning:
Step 3: Add docker-compose service
Append to docker-compose.yaml:
Step 4: Build and verify
Run: docker compose build pid-tuner 2>&1 | tail -5
Expected: Successful build.
Step 5: Commit
Task 6: Integration Test — Full Pipeline
Files:
- Create:
scripts/run_pid_tuning.sh
Step 1: Create the run script
Create scripts/run_pid_tuning.sh:
Step 2: Make executable
Run: chmod +x scripts/run_pid_tuning.sh
Step 3: Smoke test with 1 trial
Run: ./scripts/run_pid_tuning.sh 1
Expected: One trial completes, data/tuning/best_gains.yaml is written.
Step 4: Commit
Task 7: Write Best Gains to Controller Config
After the Optuna study completes (or whenever results look good):
Step 1: Review best gains
Run: cat data/tuning/best_gains.yaml
Step 2: Update controllers_gravity.yaml with the best gains
Manually copy the best values from data/tuning/best_gains.yaml into ar4_skills/config/controllers_gravity.yaml, replacing the current gains: section. Update the comment to say "tuning round 4 (Optuna-optimized)".
Step 3: Verify in simulation
Run: docker compose up sim-tabletop-headless and check joint states as we did earlier.
Step 4: Commit
Dependency Order
Tasks 3 and 4 can be developed in parallel since they have independent unit tests.