LSEP v0.2 — now a buildable ROS 2 reference implementation: typed lsep_msgs + managed lifecycle node, green CI

Hi everyone,

v0.2.0 now ships a runnable ROS 2 reference implementation — a package you can colcon build in a couple of minutes, with typed messages, a managed lifecycle node, and green CI. Before anything gets locked towards a 1.0, I’d like people who run real robots to poke holes in the architecture.

What’s in v0.2

1. Typed interfaces (lsep_msgs) lsep_msgs/Signal carries the state twice: as a uint8 enum for machines and as a state_name string for debugging via ros2 topic echo. Undefined physics are encoded as NaN instead of magic numbers (e.g. TTC when closing velocity is zero). Light, Sound, and Motion are separate message types — currently nested inside Signal on a single topic. The win today is type reuse and zero parsing overhead versus our previous JSON-in-String transport; whether modalities should get their own topics is an open question I’d like your input on (see below).

2. Managed lifecycle node The core lsep_node is an rclpy.lifecycle.Node following the ROS 2 managed-nodes design, so a safety supervisor can orchestrate configure/activate/deactivate deterministically and keep the signaling layer isolated from the navigation stack.

3. Engine v2.1 — two blind spots in my original state machine, fixed

  • Dwell-based de-escalation: a calmer raw state must remain stable for dwell_de_escalation_s (default 1.5 s) before the machine steps down. Escalation stays immediate; THREAT (< 0.5 s TTC) bypasses everything.
  • Input watchdog: if the sensor stream dies or occludes for longer than input_timeout_s, the reported state degrades to LOW_CONF instead of confidently latching the last danger state on dead sensors.

4. Deprecation policy The old JSON string on /lsep/state is kept as a deprecated mirror behind a publish_json parameter, so nothing existing breaks during migration.

Evidence

CI builds the workspace in a ros:jazzy container on every push, runs the 9/9 unit tests (the engine is testable without a ROS installation), and runs a headless smoke-test where a simulated human approaches, lingers, and retreats. The CI job hard-fails unless the observed state ladder actually reaches THREAT and then de-escalates — so green CI proves the temporal behavior, not just compilation. Ordered ladder from the smoke-test (two full cycles):

INTEGRITY → AWARENESS → INTENT → CARE → CRITICAL → THREAT → AWARENESS → IDLE
          → AWARENESS → INTENT → CARE → CRITICAL → THREAT → AWARENESS
                                            └─ dwell-based de-escalation, 2x ─┘

The THREAT -> AWARENESS step is the one that matters: it shows the dwell timer stepping the robot down only after the danger has genuinely cleared, rather than oscillating.

What I’d genuinely like review on

  1. State representation: uint8 enum + mirror string in one message — sane, or would you model this differently?
  2. NaN convention: NaN in float32 fields for undefined physics — acceptable, or do you prefer explicit validity bools?
  3. QoS: what profile would you expect for a safety-adjacent signaling topic at 10 Hz — reliable vs. best effort, what depth?
  4. Topic layout: one Signal topic with nested modalities (current), or per-modality topics (lsep/light, …) so e.g. an LED driver subscribes only to what it renders?
  5. Naming/namespacing conventions you’d want before this touches real fleets.

Known limits (honestly)

The TTC computation is currently 1D (distance / closing_velocity) — a placeholder; 2D time-to-intercept with trajectory prediction is on the roadmap. No hardware-in-the-loop testing yet — validation so far is the ros:jazzy CI build plus the simulated human, not a physical robot. This is a reference implementation, not certified production safety code.

Repo & 5 minute quick start: GitHub - NemanjaGalic/LSEP: Open protocol for standardized human-robot communication — 9 states, 3 modalities, 1 grammar. Physics-based. EU AI Act ready. · GitHub Release: Release ROS 2 Reference Implementation v0.2.0 · NemanjaGalic/LSEP · GitHub

Thanks for any time you spend tearing this apart.

— Nemanja

That’s all great, but what does LSEP actually do?

1 Like

LSEP is an open, vendor neutral vocabulary for a robot to show its state to people nearby, mostly through light (sound/motion optional):
AWARENESS (I’ve registered you),
INTENT (I’m about to move — and where),
CARE (I’m slowing/yielding for you),
on up to CRITICAL/THREAT.
The escalation is physics-grounded — time to collision drives how urgent the signal gets — so it’s closer to brake lights/turn signals for a robot’s intent than to every vendor inventing its own blink pattern. The ROS 2 package is just so that’s testable, not a slide.

It’s a proposal, not an adopted standard — which is exactly why I’d rather have someone on the governance side poke holes in the model than nod at it. Where would you expect it to break first?

— Nemanja