The StateTimelineView shows how entities transition between discrete states over time. Each entity becomes a horizontal lane, and each logged state is rendered as a colored band that runs until the next change. This is a good fit for state machines, mode transitions, sensor health, or any other piece of data that's better described as "what state am I in right now?" than as a numerical value.
Logging state changes
Use StateChange to log a transition. Each call marks the start of a new state at the current time; the previous state implicitly ends. The state value is a string, so you can use any label that's meaningful for your application.
# Log state transitions for two entities. Each call marks the start of a new
# state; the previous state implicitly ends. The `/door` lane uses the
# `StateConfiguration` above, while `/window` gets default styling (raw value
# as label, hashed color).
rr.set_time("step", sequence=0)
rr.log("door", rr.StateChange(state="open"))
rr.log("window", rr.StateChange(state="closed"))
rr.set_time("step", sequence=1)
rr.log("door", rr.StateChange(state="closed"))
rr.set_time("step", sequence=3)
rr.log("window", rr.StateChange(state="open"))
rr.set_time("step", sequence=4)
rr.log("door", rr.StateChange(state="open"))
Notes
- The view groups state changes by entity path, so logging to
/doorand/windowproduces two separate lanes. - Logging the same state value twice in a row is a no-op for visualization, only transitions to a different value start a new phase.
- Each phase runs from its
StateChangetime to the nextStateChangetime on the same entity. The final phase extends indefinitely.
Customizing labels, colors, and visibility
To override the default styling, log a StateConfiguration to the same entity. values, labels, colors, and visible are parallel arrays β index i of each describes the same state value. Anything you don't provide falls back to the default (raw value as label, hashed color, visible).
It is usually best to log StateConfiguration as static, since it describes how to display values rather than a moment in time.
# Customize how each state value is displayed (label, color, visibility).
# Log as static so the configuration applies for the entire recording.
rr.log(
"door",
rr.StateConfiguration(
values=["open", "closed"],
labels=["Open", "Closed"],
colors=[0x4CAF50FF, 0xEF5350FF],
),
static=True,
)
Visualize any component as state
You don't have to log StateChange to use this view. Any component whose data is string-, boolean-, or number-like can drive a lane by remapping the visualizer's StateChange:state input to read from it instead. This lets you separate how you model your data from how you visualize it. For example, visualizing a robot mode that you logged as a plain string via AnyValues or DynamicArchetype (the same idea as Plot any scalar, applied to the state slot).
The supported source data types are:
Utf8andLargeUtf8(rendered as string states)Boolean(rendered as two states)Int8,Int16,Int32,Int64,UInt8,UInt16,UInt32,UInt64,Float16,Float32, andFloat64(rendered as numeric states)
For background on how visualizers resolve their inputs, see Component mappings and Customize views.
For example, log a robot mode as a plain string component:
# Log a robot mode as a plain string component β note that this is *not* a
# `StateChange`, just an arbitrary string logged via `AnyValues`. It shows up on
# the entity as the component `AnyValues:mode`.
modes = ["booting", "idle", "driving", "idle", "charging"]
for step, mode in enumerate(modes):
rr.set_time("step", sequence=step)
rr.log("robot", rr.AnyValues(mode=mode))Then point the state-timeline visualizer at it by remapping StateChange:state:
# Remap the state-timeline visualizer's `StateChange:state` input to read from
# the custom `AnyValues:mode` component instead of an actual `StateChange`.
# Any string, boolean, or numeric component can be visualized this way.
blueprint = rrb.Blueprint(
rrb.StateTimelineView(
origin="/",
name="Robot mode",
overrides={
"robot": [
rr.StateChange.from_fields().visualizer(
mappings=[
VisualizerComponentMapping(
target="StateChange:state",
source_kind=ComponentSourceKind.SourceComponent,
source_component="AnyValues:mode",
),
],
),
],
},
),
)
rr.send_blueprint(blueprint)Add data by dragging components
You can set up the same mapping interactively: drag a component from the streams tree onto a State Timeline view. If the component is a compatible source (string, boolean, or numeric), a new lane is added that remaps StateChange:state from it. Incompatible components (e.g. a blob or tensor) are rejected, as is dropping a component that the view already visualizes.
Setting up the view via blueprint
The State Timeline view is also created automatically when StateChange data is present, but you can also configure it explicitly via the blueprint API:
# Place a state timeline view at the root. The viewer will create one
# automatically as soon as it sees `StateChange` data, but the blueprint API
# lets you control the origin, name, and layout explicitly.
blueprint = rrb.Blueprint(
rrb.StateTimelineView(origin="/", name="Doors and windows"),
)
rr.send_blueprint(blueprint)