feat(dc_measurements): add the battery Measurement — percentage, voltage, current, charge sessions and cycles - #372
Merged
Minipada merged 1 commit intoAug 18, 2026
Conversation
Minipada
force-pushed
the
feature/361-add-measurement-battery-percentage-volta
branch
from
August 18, 2026 11:50
7d4c016 to
dc4f200
Compare
…ons and cycles DC recorded what the computer was doing and nothing about what powers the robot. `dc_measurements/Battery` subscribes to `sensor_msgs/BatteryState` and reports charge percentage, voltage and current on the polling interval, plus one Record when a charging session starts and one when it ends -- charging is unavailable time, and that is the fact operations schedules around. Records are facts, not metrics: an `event` field names which of `sample`, `charge_session_start` (carrying the depth and duration of the discharge before it) and `charge_session_end` (carrying the session's duration) a Record is. Aggregation stays in the SQL views, where the window is a query parameter. The counting lives in `dc_common::BatteryCycleAccumulator` -- header-only, no ROS dependency, no clock of its own. Sessions are delimited by `power_supply_status`, never by a percentage threshold, so a noisy percentage cannot open and close them repeatedly, and cycles accumulate discharge depth rather than counting full discharges: two half discharges are one cycle. That is the accumulator half of #360, which #361 needs to exist at all; #360 stays open for its `StateTransitionDetector`. A NaN field is left out of the Record instead of written as null, so a pack reporting only a voltage still validates; before the topic publishes at all, no Record is emitted. `topic` is a parameter, so a robot with two packs runs one Measurement per pack. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GEJQDuGUXVwF9sUz5E5M7x Signed-off-by: David Bensoussan <d.bensoussan@proton.me>
Minipada
force-pushed
the
feature/361-add-measurement-battery-percentage-volta
branch
from
August 18, 2026 12:56
dc4f200 to
3bbd12a
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## jazzy #372 +/- ##
==========================================
+ Coverage 67.55% 67.64% +0.09%
==========================================
Files 98 99 +1
Lines 6045 6188 +143
==========================================
+ Hits 4083 4185 +102
- Misses 1962 2003 +41
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Minipada
deleted the
feature/361-add-measurement-battery-percentage-volta
branch
September 2, 2026 12:45
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #361
DC recorded what the computer was doing and nothing about what powers the robot.
dc_measurements/Batterysubscribes tosensor_msgs/BatteryStateand reports charge percentage, voltage and current on the polling interval, plus one Record when a charging session starts and one when it ends — charging is unavailable time, and that is the fact an operations team schedules around.Facts, not metrics
Every Record carries an
eventfield naming which of three it is:eventsamplepercentage,voltage,current,power_supply_status,completed_cycles, and whatever else the pack reportscharge_session_startsession_id, and the depth and duration of the discharge that preceded itcharge_session_endsession_id,duration_sec, and the percentage points gained"Charging started after a discharge of 62 %", never "battery availability is 87 %" — aggregation stays in the SQL views, where the window is a query parameter.
dc_common::BatteryCycleAccumulatorHeader-only, no
rclcpp, no message package, next torecord_ring_buffer.hpp. It consumes (percentage, power-supply status, timestamp) triples and returns session boundaries; it owns no clock, so its 10 gtest cases drive whole shifts in microseconds. Two decisions worth reading the code for:power_supply_status, never by a percentage threshold — a percentage bouncing ±5 % inside one session opens and closes nothing, and anunknownstatus leaves an open session open rather than closing it.On #361's blocker
#361 declares #360 a blocker and #360 is still open. The Measurement cannot exist without the accumulator, so this PR lands the
BatteryCycleAccumulatorhalf of #360 with it. #360 stays open for its other half,StateTransitionDetector(for the intervention/mission Measurements), which is untouched here.The plugin is a thin adapter
Subscribes, delegates, serialises.
sensor_msgs/BatteryStateleaves most fields optional and signals "unmeasured" with NaN, so a NaN field is left out of the Record rather than written asnull— a pack reporting nothing but a voltage still produces a valid Record. Before the topic publishes at all, no Record is emitted: a gap means no battery data, not a battery at 0 %.power_supply_healthis reported when the pack sends one, andhealth_percentage(capacityagainstdesign_capacity) only when it sends both.Session boundaries are queued when they happen and leave on the next poll, one Record per poll, keeping the timestamp of the moment they happened. So they go through
publish()like everything else (Conditions, gate, incident buffering, Group) instead of being published from the subscription callback, which would race the polling timer under the node'sMultiThreadedExecutor. Shared state is behind a mutex and the queue is capped.topicsensor_msgs/BatteryStatetopic. One Measurement per pack/battery_statepercentage_scalepercentage(the message specifies 0–1; a driver publishing 0–100 sets1.0)100.0full_cycle_percent100.0Schema
plugins/measurements/json/battery.json, loaded through the existingvalidateSchema("dc_measurements", …)path: strict where the views look (eventalways;power_supply_statuson a sample;session_id, plusduration_secon an end, on a boundary), permissive everywhere the hardware decides.percentageis bounded 0–100, which also catches a mis-setpercentage_scale.Verification
Built and run in
localhost/dc-workspace:latest:dc_common, 10 cases: half-cycles, status-delimited sessions, unknown-status gaps, monotonic session ids, packs with no percentage at all, first-session-has-no-preceding-discharge, non-negative depth, configurable cycle size.dc_measurements, 5 cases over the live plugin: the happy path (percentage/voltage/current/health/health_percentage, plus schema validation applied in the test so a half-filled Record fails rather than only logging), an input topic that never publishes (no Record at all), an almost-emptyBatteryState(fields absent, not null, schema passes), a full charging session (start with a 62 % discharge depth, end with a duration and 66 points gained, then 0.62 completed cycles), and two packs on distinct topics producing distinct Records.colcon test --packages-select dc_common dc_measurements: 217 tests, 0 failures.prek run --all-files --skip build-docgreen;mdbook buildrenders the new page with no new warnings.The simulation still publishes no
BatteryState(#364), so there is no demo showing real charge state — as the issue anticipated, the tests publish syntheticBatteryStatethemselves.🤖 Generated with Claude Code
https://claude.ai/code/session_01GEJQDuGUXVwF9sUz5E5M7x