📈 Datapoints
A datapoint is one field of one topic — or the whole topic. Never several topics.
datapoints:
battery_soc:
topic: /battery
type: sensor_msgs/msg/BatteryState
field: percentage
rate_throttle_hz: 1
description: |
Main battery charge.
Below 15 % the robot returns to its dock on its own.
numeric:
scale: 100
unit: "%"
decimals: 1
retention:
enabled: true
interval_seconds: 60
max_buffer_values: 5000
chart:
y_min: 0
y_max: 100
style: line
default_window_minutes: 1440
alerts:
low_battery:
name: Battery low
severity: warning
condition: { fire_at: 15, resolve_at: 18 }
| field | type | bounds | absent means |
|---|---|---|---|
topic |
ROS graph name | absolute, at most 255 characters | required |
type |
ROS type name | pkg/msg/Type, at most 255 characters |
required |
field |
field path | at most 255 characters, one array index per segment | the whole message |
rate_throttle_hz |
number | 0 to 20 |
no throttling |
low_bandwidth |
keep |
— | rate-capped by low_bandwidth when active; keep exempts the rate only — its backfill still pauses |
description |
text | 1 to 2000 characters | offered as description: null |
numeric.scale |
number | — | no factor |
numeric.offset |
number | — | no offset |
numeric.unit |
text | at most 32 characters | no unit shown |
numeric.decimals |
integer | 0 to 6 |
the console’s own default |
retention.enabled |
boolean | — | false, live only |
retention.interval_seconds |
integer | 1 to 3600 |
300 |
retention.max_buffer_values |
integer | 1 to 100000 |
no buffer on the robot |
chart.y_min |
number | finite, not above y_max |
the axis scales to the data |
chart.y_max |
number | finite | the axis scales to the data |
chart.style |
line or step |
— | the console decides |
chart.default_window_minutes |
integer | 1 to 43200 |
60 |
alerts |
map of slugs | — | nothing watches this value |
topic and type are required, and so is nothing else. Leaving field out
means the whole message, and the value is then an object rather than a scalar.
There is no whole_message: true — omission already says it.
Without field, the numeric, chart and alerts groups are refused
as requires_single_field. All three need one comparable value: a scale
factor on a whole message means nothing, and neither does a y-axis.
field — a path into the message
Dot-separated field names, each name optionally followed by one array index. All four of these are valid:
percentagepose.position.xranges[0]points[0].x
The rules the path obeys:
- Names keep ROS’s spelling — lowercase letters, digits and underscores, starting with a letter or an underscore.
- An index only works on a field the type declares as an array.
points[0].xis a path;percentage[0]on afloat64is not. - One index per name. ROS 2 has
float64[],float64[3]andfloat64[<=10]and no multi-dimensional arrays at all, sopoints[0][1]could name nothing on any message. - The whole path is at most 255 characters.
Index bounds are not checked, and cannot be — an array’s length is not
part of a ROS type. ranges[999] publishes cleanly, and on a robot whose
message is shorter the datapoint simply produces no values.
Throttling with rate_throttle_hz
An upper bound, 0 to 20, and it need not be a whole number. Omitted or 0
means no throttling.
Three properties define it:
- It is a ceiling, not a clock. A topic that publishes more slowly reaches you more slowly, which is correct.
- A value is never repeated to manufacture a rate. A silent topic produces no traffic.
- Within a window the newest value wins. What arrives faster is dropped rather than queued, so a subscriber always sees the current state.
The bridge enforces this, not the cloud. Every realtime subscriber therefore sees the same rate and the robot’s bandwidth is genuinely saved; throttling in the cloud would arrive too late to save anything.
The 20 Hz ceiling is a product decision. For an app’s interface it is
plenty, and a control loop or millisecond debugging wants a tool that reads
directly on the robot.
numeric — needs a numeric field
scale and offset are applied on the robot, before sending:
value * scale + offset. That is why REST, realtime and history all carry
identical numbers — there is no second place where arithmetic happens.
unit is the unit after conversion. It appears beside the value and is
carried into the datasheet robot_describe answers an MCP client with, so a
model does not have to guess whether 15 means percent, volts or minutes.
decimals is an exact count of fraction digits, not a ceiling: decimals: 1
renders 18.0 as well as 18.4, which is what makes a column of readings
line up. Leaving it out is not the same as decimals: 0 — absent means the
console’s own default, while 0 is a request for whole numbers. It is
presentation only, and the stored value keeps the precision it arrived with.
retention — what outlives the moment
enabled is off by default and decides whether values are written to the time
series and become queryable. Without it the value is live only: if you are not
watching when it arrives, you never see it. That is often right, because not
every value deserves a past.
interval_seconds is how often a value is written to history, not how
often it is sent — that is rate_throttle_hz. Two consequences are worth
knowing: a bumper that is true for 200 ms does not appear unless a write
falls inside it, and stored points are billed. This interval is the direct
lever on what a robot costs.
max_buffer_values acts on the robot: how many values it holds while the
bridge is disconnected, pushed after reconnect. The catch-up runs behind live
telemetry and job results at a limited rate, so closing a gap never delays the
present. Without it the series simply has a gap, which is an honest answer.
chart — display only
y_min and y_max are fixed axis bounds, and omitting them lets the axis
scale to the data. y_min: 0 is a real axis floor and is read as 0, never
as unset. A y_max below y_min is refused at parse time, because nothing
downstream catches it and the chart would render empty.
style is line or step, and it is not a matter of taste. line claims
the value moved evenly between two samples, roughly true of a temperature or a
charge. step holds the value and then jumps, which for a mode, a switch or a
counter is the only honest drawing.
default_window_minutes is only the starting zoom. A viewer may look further
back, and nothing about what is stored follows from it.
What only the robot can settle
type is declared rather than discovered, so a configuration can be written
for a robot that has never connected. The cost is that some checks have
nothing to run against until one has.
| finding | severity | when |
|---|---|---|
unknown_type |
warning | the robot has not introspected this type, so nothing about it can be checked |
unknown_topic |
warning | the topic was not seen in the last introspection |
unknown_field_path |
error | the type is known and has no such field |
requires_numeric_field |
error | numeric or chart sits on a field that is not a number |
A warning does not block a publish. An error does, and the editor marks it as you type — long before the robot would have seen it.
Alerts on this datapoint are their own page. The message template that actions and services and publishers send is Messages & Parameters. Cameras have none, and the limits every section shares are on the overview.