Skip to content

Add interactive "How balancing works" page to the website#516

Closed
tomquist wants to merge 4 commits into
developfrom
claude/clever-heisenberg-oxkxrd
Closed

Add interactive "How balancing works" page to the website#516
tomquist wants to merge 4 commits into
developfrom
claude/clever-heisenberg-oxkxrd

Conversation

@tomquist

Copy link
Copy Markdown
Owner

A new homeowner-friendly page (how-balancing-works.html) with a live canvas simulation of the grid-balancing control loop. A synthetic household load wanders and spikes while the battery chases the grid back to zero; three toggles let visitors switch off latency compensation, smooth ramping, and anti-oscillation to see the overshoot/hunting each one prevents.

The toy model in ts/balancing.ts is a deliberately simplified cartoon of the real controller (predictor / pacing / oscillation damping in balancer.py), kept pure so balancing.test.ts can assert each mechanism measurably steadies the simulated grid. Wired into the build (new HTML page + esbuild entry point), linked from the nav and the landing "How it works" section.

Claude-Session: https://claude.ai/code/session_011iig48zU6fkxczi4SeVwMv

A new homeowner-friendly page (how-balancing-works.html) with a live
canvas simulation of the grid-balancing control loop. A synthetic
household load wanders and spikes while the battery chases the grid back
to zero; three toggles let visitors switch off latency compensation,
smooth ramping, and anti-oscillation to see the overshoot/hunting each
one prevents.

The toy model in ts/balancing.ts is a deliberately simplified cartoon of
the real controller (predictor / pacing / oscillation damping in
balancer.py), kept pure so balancing.test.ts can assert each mechanism
measurably steadies the simulated grid. Wired into the build (new HTML
page + esbuild entry point), linked from the nav and the landing "How it
works" section.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011iig48zU6fkxczi4SeVwMv
@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e7df13ee-0822-445b-ae69-833a6e367a54

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/clever-heisenberg-oxkxrd

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

🧹 The preview for this PR has been removed now that it is closed.

@github-actions

github-actions Bot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Steering evaluation (base vs head)

Overall: 0 improved, 0 regressed, 14 unchanged across 14 metrics — mean 0% (unchanged).

Priority: priority-weighted 0% (unchanged) — ✅ no do-no-harm guardrail regressions.

Lower is better for every metric. See src/astrameter/simulator/evaluation.py for definitions.

Metrics are the per-scenario mean of 5 seeds.

Aggregate — mean across 31 scenarios

Metric Base Head Δ
settle_mean_s 30.6 30.6 =
settle_p95_s 49.6 49.6 =
unsettled_events 0.3 0.3 =
overshoot_mean_w 51.6 51.6 =
overshoot_max_w 105.0 105.0 =
band_crossings_per_h 306.4 306.4 =
grid_p2p_w 251.1 251.1 =
grid_rms_w 246.4 246.4 =
steady_rms_w 113.9 113.9 =
mean_abs_grid_w 96.8 96.8 =
avoidable_import_wh 35.0 35.0 =
avoidable_export_wh 25.9 25.9 =
cost_regret_ct 0.81 0.81 =
battery_travel_w_per_h 21636.2 21636.2 =

📊 Interactive grid-power charts (zoom / hover / toggle series) are in the self-contained steering-eval-report.html report — see the link below (it opens directly in the browser).

What do these metrics mean?
Metric Meaning
settle_mean_s Mean seconds after a load/PV step for grid power to return inside the ±25 W settle band and hold for 10 s (reaction speed).
settle_p95_s 95th-percentile settle time — the slow tail of reactions.
unsettled_events Number of disturbance events that never settled within the 10-minute measurement window.
overshoot_mean_w Mean overshoot (W): how far grid power swings past zero to the opposite sign after an event.
overshoot_max_w Worst-case overshoot (W) across all events.
band_crossings_per_h Sign flips per hour across the ±20 W hysteresis band — oscillation / hunting frequency.
grid_p2p_w Sustained peak-to-peak grid swing (95th - 5th percentile) over the whole run — oscillation amplitude. Non-zero whenever the loop keeps hunting, including continuous oscillation the step-response metrics (settle/overshoot) miss.
grid_rms_w RMS grid power (W) over the whole run, transients included — the L2 tracking error: how cleanly the loop held zero, penalising big excursions (overshoot, swings) far harder than a small steady offset. Pairs with battery_travel_w_per_h as the control-effort term.
steady_rms_w RMS grid power (W) during steady state (excluding the 120 s after each event) — residual jitter when nothing is changing.
mean_abs_grid_w Mean absolute grid power (W) over the whole run — overall tracking accuracy.
avoidable_import_wh Energy imported from the grid (Wh) the battery could have supplied (it had charge and discharge headroom) — missed self-consumption.
avoidable_export_wh Energy exported to the grid (Wh) an AC-chargeable battery could have absorbed (it had room and charge headroom) — missed charging.
cost_regret_ct Money north-star: electricity bill (eurocents, import @ 30 ct/kWh, export @ 8 ct/kWh) over what a perfect-foresight optimal battery would have paid on the same load. Ungameable (both grid directions cost); 0 = matched the optimum. The single number that says how much the controller left on the table.
battery_travel_w_per_h Total absolute change in battery setpoints per hour (W/h) — control effort / actuator wear; lower is smoother.
Per-scenario tables (31 scenarios)
mixed_cadence/eff — settle 26.1→26.1s, overshoot 232.6→232.6W, RMS 25.6→25.6W
Metric Base Head Δ
settle_mean_s 26.1 26.1 =
settle_p95_s 31.6 31.6 =
unsettled_events 0.0 0.0 =
overshoot_mean_w 110.0 110.0 =
overshoot_max_w 232.6 232.6 =
band_crossings_per_h 33.0 33.0 =
grid_p2p_w 78.8 78.8 =
grid_rms_w 253.5 253.5 =
steady_rms_w 25.6 25.6 =
mean_abs_grid_w 58.2 58.2 =
avoidable_import_wh 34.2 34.2 =
avoidable_export_wh 23.9 23.9 =
cost_regret_ct 0.83 0.83 =
battery_travel_w_per_h 25396.8 25396.8 =
mixed_cadence/fair — settle 24.8→24.8s, overshoot 176.3→176.3W, RMS 13.1→13.1W
Metric Base Head Δ
settle_mean_s 24.8 24.8 =
settle_p95_s 29.4 29.4 =
unsettled_events 0.0 0.0 =
overshoot_mean_w 111.3 111.3 =
overshoot_max_w 176.3 176.3 =
band_crossings_per_h 27.2 27.2 =
grid_p2p_w 27.6 27.6 =
grid_rms_w 246.0 246.0 =
steady_rms_w 13.1 13.1 =
mean_abs_grid_w 53.5 53.5 =
avoidable_import_wh 30.4 30.4 =
avoidable_export_wh 23.1 23.1 =
cost_regret_ct 0.73 0.73 =
battery_travel_w_per_h 23198.8 23198.8 =
mixed_cadence_solar/eff — settle 34.6→34.6s, overshoot 351.2→351.2W, RMS 30.3→30.3W
Metric Base Head Δ
settle_mean_s 34.6 34.6 =
settle_p95_s 68.8 68.8 =
unsettled_events 1.8 1.8 =
overshoot_mean_w 104.6 104.6 =
overshoot_max_w 351.2 351.2 =
band_crossings_per_h 40.0 40.0 =
grid_p2p_w 109.1 109.1 =
grid_rms_w 267.6 267.6 =
steady_rms_w 30.3 30.3 =
mean_abs_grid_w 71.0 71.0 =
avoidable_import_wh 55.4 55.4 =
avoidable_export_wh 51.2 51.2 =
cost_regret_ct 1.25 1.25 =
battery_travel_w_per_h 30571.0 30571.0 =
mixed_cadence_solar/fair — settle 30.8→30.8s, overshoot 169.0→169.0W, RMS 20.3→20.3W
Metric Base Head Δ
settle_mean_s 30.8 30.8 =
settle_p95_s 59.0 59.0 =
unsettled_events 1.4 1.4 =
overshoot_mean_w 86.2 86.2 =
overshoot_max_w 169.0 169.0 =
band_crossings_per_h 27.2 27.2 =
grid_p2p_w 74.5 74.5 =
grid_rms_w 257.9 257.9 =
steady_rms_w 20.3 20.3 =
mean_abs_grid_w 64.9 64.9 =
avoidable_import_wh 49.4 49.4 =
avoidable_export_wh 47.9 47.9 =
cost_regret_ct 1.1 1.1 =
battery_travel_w_per_h 24392.2 24392.2 =
mixed_venus_b2500/eff — settle 56.2→56.2s, overshoot 220.6→220.6W, RMS 14.9→14.9W
Metric Base Head Δ
settle_mean_s 56.2 56.2 =
settle_p95_s 123.7 123.7 =
unsettled_events 0.2 0.2 =
overshoot_mean_w 106.1 106.1 =
overshoot_max_w 220.6 220.6 =
band_crossings_per_h 55.7 55.7 =
grid_p2p_w 56.7 56.7 =
grid_rms_w 200.6 200.6 =
steady_rms_w 14.9 14.9 =
mean_abs_grid_w 42.4 42.4 =
avoidable_import_wh 41.3 41.3 =
avoidable_export_wh 22.3 22.3 =
cost_regret_ct 1.06 1.06 =
battery_travel_w_per_h 29523.6 29523.6 =
mixed_venus_b2500/fair — settle 30.1→30.1s, overshoot 106.9→106.9W, RMS 13.2→13.2W
Metric Base Head Δ
settle_mean_s 30.1 30.1 =
settle_p95_s 62.9 62.9 =
unsettled_events 0.0 0.0 =
overshoot_mean_w 70.6 70.6 =
overshoot_max_w 106.9 106.9 =
band_crossings_per_h 29.3 29.3 =
grid_p2p_w 42.7 42.7 =
grid_rms_w 194.1 194.1 =
steady_rms_w 13.2 13.2 =
mean_abs_grid_w 37.7 37.7 =
avoidable_import_wh 34.4 34.4 =
avoidable_export_wh 22.1 22.1 =
cost_regret_ct 0.85 0.85 =
battery_travel_w_per_h 21395.6 21395.6 =
phase_imbalance — settle 44.9→44.9s, overshoot 166.7→166.7W, RMS 30.2→30.2W
Metric Base Head Δ
settle_mean_s 44.9 44.9 =
settle_p95_s 106.4 106.4 =
unsettled_events 0.6 0.6 =
overshoot_mean_w 74.8 74.8 =
overshoot_max_w 166.7 166.7 =
band_crossings_per_h 79.2 79.2 =
grid_p2p_w 42.5 42.5 =
grid_rms_w 199.9 199.9 =
steady_rms_w 30.2 30.2 =
mean_abs_grid_w 40.5 40.5 =
avoidable_import_wh 25.7 25.7 =
avoidable_export_wh 14.8 14.8 =
cost_regret_ct 0.65 0.65 =
battery_travel_w_per_h 18583.6 18583.6 =
single_venus_d_solar — settle 24.2→24.2s, overshoot 94.4→94.4W, RMS 15.9→15.9W
Metric Base Head Δ
settle_mean_s 24.2 24.2 =
settle_p95_s 27.1 27.1 =
unsettled_events 0.0 0.0 =
overshoot_mean_w 76.8 76.8 =
overshoot_max_w 94.4 94.4 =
band_crossings_per_h 10.7 10.7 =
grid_p2p_w 29.9 29.9 =
grid_rms_w 104.3 104.3 =
steady_rms_w 15.9 15.9 =
mean_abs_grid_w 18.5 18.5 =
avoidable_import_wh 17.9 17.9 =
avoidable_export_wh 9.9 9.9 =
cost_regret_ct 0.46 0.46 =
battery_travel_w_per_h 9425.4 9425.4 =
single_venus_d_steps — settle 26.3→26.3s, overshoot 90.3→90.3W, RMS 15.5→15.5W
Metric Base Head Δ
settle_mean_s 26.3 26.3 =
settle_p95_s 33.8 33.8 =
unsettled_events 0.0 0.0 =
overshoot_mean_w 73.3 73.3 =
overshoot_max_w 90.3 90.3 =
band_crossings_per_h 25.8 25.8 =
grid_p2p_w 30.9 30.9 =
grid_rms_w 256.4 256.4 =
steady_rms_w 15.5 15.5 =
mean_abs_grid_w 58.6 58.6 =
avoidable_import_wh 32.3 32.3 =
avoidable_export_wh 26.2 26.2 =
cost_regret_ct 0.76 0.76 =
battery_travel_w_per_h 21425.2 21425.2 =
single_venus_d_washer — settle 0.0→0.0s, overshoot 0.0→0.0W, RMS 61.0→61.0W
Metric Base Head Δ
settle_mean_s 0.0 0.0 =
settle_p95_s 0.0 0.0 =
unsettled_events 0.0 0.0 =
overshoot_mean_w 0.0 0.0 =
overshoot_max_w 0.0 0.0 =
band_crossings_per_h 322.4 322.4 =
grid_p2p_w 192.7 192.7 =
grid_rms_w 61.0 61.0 =
steady_rms_w 61.0 61.0 =
mean_abs_grid_w 42.8 42.8 =
avoidable_import_wh 13.3 13.3 =
avoidable_export_wh 8.1 8.1 =
cost_regret_ct 0.33 0.33 =
battery_travel_w_per_h 24204.4 24204.4 =
single_venus_drain — settle 0.0→0.0s, overshoot 0.0→0.0W, RMS 907.3→907.3W
Metric Base Head Δ
settle_mean_s 0.0 0.0 =
settle_p95_s 0.0 0.0 =
unsettled_events 0.0 0.0 =
overshoot_mean_w 0.0 0.0 =
overshoot_max_w 0.0 0.0 =
band_crossings_per_h 73.2 73.2 =
grid_p2p_w 1598.9 1598.9 =
grid_rms_w 907.3 907.3 =
steady_rms_w 907.3 907.3 =
mean_abs_grid_w 645.3 645.3 =
avoidable_import_wh 14.4 14.4 =
avoidable_export_wh 10.2 10.2 =
cost_regret_ct 0.21 0.21 =
battery_travel_w_per_h 4062.0 4062.0 =
single_venus_fill — settle 360.0→360.0s, overshoot 0.0→0.0W, RMS 953.6→953.6W
Metric Base Head Δ
settle_mean_s 360.0 360.0 =
settle_p95_s 600.0 600.0 =
unsettled_events 4.0 4.0 =
overshoot_mean_w 0.0 0.0 =
overshoot_max_w 0.0 0.0 =
band_crossings_per_h 10.1 10.1 =
grid_p2p_w 1713.1 1713.1 =
grid_rms_w 978.6 978.6 =
steady_rms_w 953.6 953.6 =
mean_abs_grid_w 662.8 662.8 =
avoidable_import_wh 4.8 4.8 =
avoidable_export_wh 6.7 6.7 =
cost_regret_ct 0.14 0.14 =
battery_travel_w_per_h 3173.0 3173.0 =
single_venus_noisy — settle 0.0→0.0s, overshoot 0.0→0.0W, RMS 94.3→94.3W
Metric Base Head Δ
settle_mean_s 0.0 0.0 =
settle_p95_s 0.0 0.0 =
unsettled_events 0.0 0.0 =
overshoot_mean_w 0.0 0.0 =
overshoot_max_w 0.0 0.0 =
band_crossings_per_h 1452.6 1452.6 =
grid_p2p_w 296.7 296.7 =
grid_rms_w 94.3 94.3 =
steady_rms_w 94.3 94.3 =
mean_abs_grid_w 79.1 79.1 =
avoidable_import_wh 42.0 42.0 =
avoidable_export_wh 37.0 37.0 =
cost_regret_ct 0.96 0.96 =
battery_travel_w_per_h 22758.8 22758.8 =
single_venus_pv — settle 0.0→0.0s, overshoot 0.0→0.0W, RMS 60.8→60.8W
Metric Base Head Δ
settle_mean_s 0.0 0.0 =
settle_p95_s 0.0 0.0 =
unsettled_events 0.0 0.0 =
overshoot_mean_w 0.0 0.0 =
overshoot_max_w 0.0 0.0 =
band_crossings_per_h 38.5 38.5 =
grid_p2p_w 42.9 42.9 =
grid_rms_w 60.8 60.8 =
steady_rms_w 60.8 60.8 =
mean_abs_grid_w 17.3 17.3 =
avoidable_import_wh 9.9 9.9 =
avoidable_export_wh 16.1 16.1 =
cost_regret_ct 0.17 0.17 =
battery_travel_w_per_h 6782.0 6782.0 =
single_venus_solar — settle 26.8→26.8s, overshoot 80.3→80.3W, RMS 17.8→17.8W
Metric Base Head Δ
settle_mean_s 26.8 26.8 =
settle_p95_s 32.6 32.6 =
unsettled_events 0.0 0.0 =
overshoot_mean_w 67.1 67.1 =
overshoot_max_w 80.3 80.3 =
band_crossings_per_h 26.3 26.3 =
grid_p2p_w 41.9 41.9 =
grid_rms_w 108.7 108.7 =
steady_rms_w 17.8 17.8 =
mean_abs_grid_w 21.2 21.2 =
avoidable_import_wh 15.5 15.5 =
avoidable_export_wh 16.3 16.3 =
cost_regret_ct 0.34 0.34 =
battery_travel_w_per_h 7331.6 7331.6 =
single_venus_solar_slow — settle 33.9→33.9s, overshoot 68.3→68.3W, RMS 22.8→22.8W
Metric Base Head Δ
settle_mean_s 33.9 33.9 =
settle_p95_s 39.9 39.9 =
unsettled_events 0.0 0.0 =
overshoot_mean_w 32.1 32.1 =
overshoot_max_w 68.3 68.3 =
band_crossings_per_h 5.6 5.6 =
grid_p2p_w 60.6 60.6 =
grid_rms_w 131.7 131.7 =
steady_rms_w 22.8 22.8 =
mean_abs_grid_w 31.6 31.6 =
avoidable_import_wh 21.3 21.3 =
avoidable_export_wh 26.1 26.1 =
cost_regret_ct 0.43 0.43 =
battery_travel_w_per_h 6486.0 6486.0 =
single_venus_steps — settle 26.0→26.0s, overshoot 88.0→88.0W, RMS 14.7→14.7W
Metric Base Head Δ
settle_mean_s 26.0 26.0 =
settle_p95_s 32.7 32.7 =
unsettled_events 0.0 0.0 =
overshoot_mean_w 69.7 69.7 =
overshoot_max_w 88.0 88.0 =
band_crossings_per_h 23.8 23.8 =
grid_p2p_w 27.1 27.1 =
grid_rms_w 266.9 266.9 =
steady_rms_w 14.7 14.7 =
mean_abs_grid_w 61.1 61.1 =
avoidable_import_wh 34.1 34.1 =
avoidable_export_wh 27.0 27.0 =
cost_regret_ct 0.8 0.8 =
battery_travel_w_per_h 19543.6 19543.6 =
single_venus_steps_slow — settle 40.5→40.5s, overshoot 98.5→98.5W, RMS 14.8→14.8W
Metric Base Head Δ
settle_mean_s 40.5 40.5 =
settle_p95_s 56.2 56.2 =
unsettled_events 0.0 0.0 =
overshoot_mean_w 37.1 37.1 =
overshoot_max_w 98.5 98.5 =
band_crossings_per_h 9.4 9.4 =
grid_p2p_w 78.2 78.2 =
grid_rms_w 331.7 331.7 =
steady_rms_w 14.8 14.8 =
mean_abs_grid_w 88.2 88.2 =
avoidable_import_wh 47.5 47.5 =
avoidable_export_wh 40.7 40.7 =
cost_regret_ct 1.1 1.1 =
battery_travel_w_per_h 17699.8 17699.8 =
single_venus_trace — settle 0.0→0.0s, overshoot 0.0→0.0W, RMS 278.9→278.9W
Metric Base Head Δ
settle_mean_s 0.0 0.0 =
settle_p95_s 0.0 0.0 =
unsettled_events 0.0 0.0 =
overshoot_mean_w 0.0 0.0 =
overshoot_max_w 0.0 0.0 =
band_crossings_per_h 284.8 284.8 =
grid_p2p_w 689.3 689.3 =
grid_rms_w 278.5 278.5 =
steady_rms_w 278.9 278.9 =
mean_abs_grid_w 117.0 117.0 =
avoidable_import_wh 81.4 81.4 =
avoidable_export_wh 35.5 35.5 =
cost_regret_ct 1.36 1.36 =
battery_travel_w_per_h 33179.4 33179.4 =
single_venus_washer — settle 0.0→0.0s, overshoot 0.0→0.0W, RMS 61.0→61.0W
Metric Base Head Δ
settle_mean_s 0.0 0.0 =
settle_p95_s 0.0 0.0 =
unsettled_events 0.0 0.0 =
overshoot_mean_w 0.0 0.0 =
overshoot_max_w 0.0 0.0 =
band_crossings_per_h 320.4 320.4 =
grid_p2p_w 196.1 196.1 =
grid_rms_w 61.1 61.1 =
steady_rms_w 61.0 61.0 =
mean_abs_grid_w 40.6 40.6 =
avoidable_import_wh 11.3 11.3 =
avoidable_export_wh 9.0 9.0 =
cost_regret_ct 0.27 0.27 =
battery_travel_w_per_h 24290.4 24290.4 =
two_venus/eff — settle 18.9→18.9s, overshoot 150.3→150.3W, RMS 13.9→13.9W
Metric Base Head Δ
settle_mean_s 18.9 18.9 =
settle_p95_s 27.1 27.1 =
unsettled_events 0.0 0.0 =
overshoot_mean_w 70.3 70.3 =
overshoot_max_w 150.3 150.3 =
band_crossings_per_h 40.0 40.0 =
grid_p2p_w 27.8 27.8 =
grid_rms_w 221.6 221.6 =
steady_rms_w 13.9 13.9 =
mean_abs_grid_w 43.2 43.2 =
avoidable_import_wh 26.0 26.0 =
avoidable_export_wh 17.3 17.3 =
cost_regret_ct 0.64 0.64 =
battery_travel_w_per_h 21923.0 21923.0 =
two_venus/fair — settle 18.2→18.2s, overshoot 128.7→128.7W, RMS 13.8→13.8W
Metric Base Head Δ
settle_mean_s 18.2 18.2 =
settle_p95_s 22.2 22.2 =
unsettled_events 0.0 0.0 =
overshoot_mean_w 93.4 93.4 =
overshoot_max_w 128.7 128.7 =
band_crossings_per_h 20.0 20.0 =
grid_p2p_w 23.4 23.4 =
grid_rms_w 217.8 217.8 =
steady_rms_w 13.8 13.8 =
mean_abs_grid_w 41.5 41.5 =
avoidable_import_wh 24.3 24.3 =
avoidable_export_wh 17.2 17.2 =
cost_regret_ct 0.59 0.59 =
battery_travel_w_per_h 19252.6 19252.6 =
two_venus_noisy/eff — settle 0.0→0.0s, overshoot 0.0→0.0W, RMS 94.5→94.5W
Metric Base Head Δ
settle_mean_s 0.0 0.0 =
settle_p95_s 0.0 0.0 =
unsettled_events 0.0 0.0 =
overshoot_mean_w 0.0 0.0 =
overshoot_max_w 0.0 0.0 =
band_crossings_per_h 2901.2 2901.2 =
grid_p2p_w 295.9 295.9 =
grid_rms_w 94.5 94.5 =
steady_rms_w 94.5 94.5 =
mean_abs_grid_w 79.6 79.6 =
avoidable_import_wh 43.0 43.0 =
avoidable_export_wh 36.6 36.6 =
cost_regret_ct 1.0 1.0 =
battery_travel_w_per_h 24526.2 24526.2 =
two_venus_noisy/fair — settle 0.0→0.0s, overshoot 0.0→0.0W, RMS 94.5→94.5W
Metric Base Head Δ
settle_mean_s 0.0 0.0 =
settle_p95_s 0.0 0.0 =
unsettled_events 0.0 0.0 =
overshoot_mean_w 0.0 0.0 =
overshoot_max_w 0.0 0.0 =
band_crossings_per_h 2890.4 2890.4 =
grid_p2p_w 296.3 296.3 =
grid_rms_w 94.5 94.5 =
steady_rms_w 94.5 94.5 =
mean_abs_grid_w 79.6 79.6 =
avoidable_import_wh 43.1 43.1 =
avoidable_export_wh 36.5 36.5 =
cost_regret_ct 1.0 1.0 =
battery_travel_w_per_h 24134.8 24134.8 =
two_venus_slow/fair — settle 41.3→41.3s, overshoot 181.1→181.1W, RMS 14.0→14.0W
Metric Base Head Δ
settle_mean_s 41.3 41.3 =
settle_p95_s 52.0 52.0 =
unsettled_events 0.0 0.0 =
overshoot_mean_w 57.7 57.7 =
overshoot_max_w 181.1 181.1 =
band_crossings_per_h 11.0 11.0 =
grid_p2p_w 74.1 74.1 =
grid_rms_w 303.8 303.8 =
steady_rms_w 14.0 14.0 =
mean_abs_grid_w 76.8 76.8 =
avoidable_import_wh 41.3 41.3 =
avoidable_export_wh 35.5 35.5 =
cost_regret_ct 0.95 0.95 =
battery_travel_w_per_h 18570.4 18570.4 =
two_venus_solar/eff — settle 25.5→25.5s, overshoot 417.5→417.5W, RMS 19.6→19.6W
Metric Base Head Δ
settle_mean_s 25.5 25.5 =
settle_p95_s 50.2 50.2 =
unsettled_events 1.6 1.6 =
overshoot_mean_w 103.6 103.6 =
overshoot_max_w 417.5 417.5 =
band_crossings_per_h 43.3 43.3 =
grid_p2p_w 55.8 55.8 =
grid_rms_w 228.7 228.7 =
steady_rms_w 19.6 19.6 =
mean_abs_grid_w 51.3 51.3 =
avoidable_import_wh 41.9 41.9 =
avoidable_export_wh 35.1 35.1 =
cost_regret_ct 0.98 0.98 =
battery_travel_w_per_h 26691.8 26691.8 =
two_venus_solar/fair — settle 22.7→22.7s, overshoot 139.8→139.8W, RMS 19.4→19.4W
Metric Base Head Δ
settle_mean_s 22.7 22.7 =
settle_p95_s 37.1 37.1 =
unsettled_events 1.2 1.2 =
overshoot_mean_w 88.5 88.5 =
overshoot_max_w 139.8 139.8 =
band_crossings_per_h 30.4 30.4 =
grid_p2p_w 54.7 54.7 =
grid_rms_w 226.9 226.9 =
steady_rms_w 19.4 19.4 =
mean_abs_grid_w 50.3 50.3 =
avoidable_import_wh 40.4 40.4 =
avoidable_export_wh 35.1 35.1 =
cost_regret_ct 0.93 0.93 =
battery_travel_w_per_h 23851.6 23851.6 =
two_venus_trace/eff — settle 0.0→0.0s, overshoot 0.0→0.0W, RMS 283.2→283.2W
Metric Base Head Δ
settle_mean_s 0.0 0.0 =
settle_p95_s 0.0 0.0 =
unsettled_events 0.0 0.0 =
overshoot_mean_w 0.0 0.0 =
overshoot_max_w 0.0 0.0 =
band_crossings_per_h 306.0 306.0 =
grid_p2p_w 745.3 745.3 =
grid_rms_w 282.3 282.3 =
steady_rms_w 283.2 283.2 =
mean_abs_grid_w 122.0 122.0 =
avoidable_import_wh 80.2 80.2 =
avoidable_export_wh 41.9 41.9 =
cost_regret_ct 2.07 2.07 =
battery_travel_w_per_h 48431.2 48431.2 =
two_venus_trace/fair — settle 0.0→0.0s, overshoot 0.0→0.0W, RMS 282.6→282.6W
Metric Base Head Δ
settle_mean_s 0.0 0.0 =
settle_p95_s 0.0 0.0 =
unsettled_events 0.0 0.0 =
overshoot_mean_w 0.0 0.0 =
overshoot_max_w 0.0 0.0 =
band_crossings_per_h 300.4 300.4 =
grid_p2p_w 725.4 725.4 =
grid_rms_w 281.6 281.6 =
steady_rms_w 282.6 282.6 =
mean_abs_grid_w 121.2 121.2 =
avoidable_import_wh 79.7 79.7 =
avoidable_export_wh 41.6 41.6 =
cost_regret_ct 2.06 2.06 =
battery_travel_w_per_h 45944.0 45944.0 =
venus_d_plus_c/eff — settle 17.4→17.4s, overshoot 175.4→175.4W, RMS 14.7→14.7W
Metric Base Head Δ
settle_mean_s 17.4 17.4 =
settle_p95_s 21.3 21.3 =
unsettled_events 0.0 0.0 =
overshoot_mean_w 81.7 81.7 =
overshoot_max_w 175.4 175.4 =
band_crossings_per_h 33.4 33.4 =
grid_p2p_w 28.1 28.1 =
grid_rms_w 215.2 215.2 =
steady_rms_w 14.7 14.7 =
mean_abs_grid_w 41.9 41.9 =
avoidable_import_wh 25.1 25.1 =
avoidable_export_wh 16.8 16.8 =
cost_regret_ct 0.62 0.62 =
battery_travel_w_per_h 22973.2 22973.2 =
venus_d_plus_c/fair — settle 18.6→18.6s, overshoot 118.4→118.4W, RMS 14.6→14.6W
Metric Base Head Δ
settle_mean_s 18.6 18.6 =
settle_p95_s 24.7 24.7 =
unsettled_events 0.0 0.0 =
overshoot_mean_w 84.3 84.3 =
overshoot_max_w 118.4 118.4 =
band_crossings_per_h 27.0 27.0 =
grid_p2p_w 27.0 27.0 =
grid_rms_w 211.9 211.9 =
steady_rms_w 14.6 14.6 =
mean_abs_grid_w 40.7 40.7 =
avoidable_import_wh 24.0 24.0 =
avoidable_export_wh 16.7 16.7 =
cost_regret_ct 0.59 0.59 =
battery_travel_w_per_h 20999.4 20999.4 =

📊 Open the interactive reportsteering-eval-report.html, a single self-contained file (opens in-browser; download it if your browser blocks inline scripts).

tomquist and others added 3 commits June 21, 2026 22:19
The previous predictor credited the battery's output change but then
re-applied the stale meter error every tick, so during the meter's
dead-time window it over-corrected ~7x and hunted — the demo oscillated
even at its smooth default.

Replace it with a proper dead-time compensator: estimate the current
grid as meter - (battOut_now - battOut_when_meter_was_sampled), adding
back the corrections the meter has not yet reflected. Add a sub-unity
control gain so the approach ramps smoothly, a discharge ceiling so the
un-compensated loop stays on-screen, and stronger oscillation-damping
params so that toggle has a visible effect. Default now settles with
zero sign-flips (RMS ~120 vs ~1500 with compensation off).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011iig48zU6fkxczi4SeVwMv
Analysis showed the three-toggle design was fundamentally flawed: in a
dead-time-compensated single loop, ramp pacing moves the aggregate error
essentially zero and damping barely registers — only latency
compensation matters. Two of the three toggles were effectively dead, so
flipping them did nothing, which is what made the demo feel useless.

Replace the toggles with a continuous side-by-side race on the *same*
household load: a plain meter-follower (textbook dead-time instability,
hunts ±~1000 W) versus AstraMeter (latency-compensated, eased, damped —
holds the grid at ~10 W RMS). The contrast is self-evident and live,
with no dead controls; the only interactive toggle just shows/hides the
naive line. A small input deadband and a calm baseline load keep
AstraMeter's line genuinely flat between events.

The per-trick toggles remain in the model API and are still asserted by
the test suite (latency compensation does the heavy lifting; the smart
controller converges and holds while the naive one hunts).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011iig48zU6fkxczi4SeVwMv
The side-by-side race showed *that* AstraMeter is better but not *how*.
Add an animated meter-lag explainer as the opening section: a scripted
"kettle" disturbance plotted against the same signal delayed by the
meter's reporting lag, so the delay is visible as a horizontal offset
between reality (green) and what the meter reports (grey). Two plain
panels explain the consequence (a basic battery chases the late reading
and overshoots) and the fix (AstraMeter adds back what it just commanded,
so it acts on the real grid now).

The page now reads problem -> mechanism -> payoff: the lag explainer,
then the live race, then the plain-language recap of all the tricks.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011iig48zU6fkxczi4SeVwMv
@tomquist tomquist closed this Jun 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant