Skip to content

feat(parser_ros): object producers for FrameTransforms, OccupancyGrid, TransformStamped, RobotDescription#100

Closed
facontidavide wants to merge 5 commits into
mainfrom
feat/parser-ros-frametransforms
Closed

feat(parser_ros): object producers for FrameTransforms, OccupancyGrid, TransformStamped, RobotDescription#100
facontidavide wants to merge 5 commits into
mainfrom
feat/parser-ros-frametransforms

Conversation

@facontidavide
Copy link
Copy Markdown
Contributor

@facontidavide facontidavide commented May 27, 2026

Summary

Adds canonical-object production to parser_ros for the ROS message types that map unambiguously and self-containedly to a builtin object available in core 0.3.1. Previously parser_ros produced only kImage and kPointCloud; these were the remaining clean cases, and they unblock the PJ4 3D scene (TF buffer, gridmaps, robot model).

ROS message → canonical stores notes
tf2_msgs/TFMessage kFrameTransforms datastore + objectstore one FrameTransform per TransformStamped, each with its own Header.stamp
geometry_msgs/TransformStamped kFrameTransforms datastore + objectstore single-element; feeds the same TF buffer
nav_msgs/OccupancyGrid kOccupancyGrid objectstore only byte-backed (zero-copy cells); a map isn't useful as scalar columns
std_msgs/String on a robot_description topic kRobotDescription objectstore only URDF/SDF/MJCF text + sniffed format hint; topic-gated (incl. namespaced <ns>/robot_description)

Store routing: only the TF producers are dual-stored — TF transforms are commonly plotted as time series and needed as objects for the 3D buffer. The map and the robot model are object-only (SchemaHandler permits a null scalar route).

Design notes

  • Per-transform timestamps: TF objects keep each transform's own Header.stamp (the per-sample time the TF buffer needs for zero-order-hold scrub lookups), independent of message receive time.
  • OccupancyGrid zero-copies its int8[] cells as a Span over the payload (pinned by the anchor), like PointCloud2.
  • RobotDescription is topic-gated: a generic std_msgs/String stays a scalar; only robot_description or any namespaced <ns>/robot_description produces a RobotDescription. Resolves the dispatch ambiguity by topic name (the catalog keys on type name, which can't distinguish it). Tests cover the plain topic, a namespaced topic, and a negative (non-robot String → no object).
  • Shared readStampedTransform() helper between the TFMessage and TransformStamped producers.

Deferred (intentionally)

Ambiguous / stateful, or type not in 0.3.1: Markers (type switch + action/lifetime/(ns,id) identity — a stateful accumulation stream, like OccupancyGridUpdate), DepthImage (encoding-dispatch + needs CameraInfo), CameraInfo / OccupancyGridUpdate (land in core via plotjuggler_core#98, not yet released).

Verification

Clean build against plotjuggler_core 0.3.1; 34/34 parser_ros tests pass (object-route decode for each new type, namespaced robot_description, + a negative RobotDescription test).

🤖 Generated with Claude Code

@facontidavide facontidavide changed the title feat(parser_ros): emit FrameTransforms object for tf2_msgs/TFMessage feat(parser_ros): object producers for FrameTransforms, OccupancyGrid, TransformStamped, RobotDescription May 27, 2026
Davide Faconti and others added 4 commits May 28, 2026 11:37
tf2_msgs/TFMessage now produces BOTH a canonical sdk::FrameTransforms object
(objectstore — for the 3D scene's per-dataset TF buffer) AND its existing
flattened scalar fields (datastore — for plotting transforms as time series).
This is dual registration in the schema catalog: object_type + parse_object
added alongside the existing parse_scalars, the same pattern Image/PointCloud2
already use.

parseFrameTransforms decodes the CDR TFMessage into one FrameTransform per
TransformStamped, each carrying its OWN Header.stamp — the per-sample time the
TF buffer needs for zero-order-hold scrub lookups — independent of the message
receive time.

Test: TFMessageProducesFrameTransformsObject verifies classifySchema reports
kFrameTransforms and that parseObject decodes a 2-transform message with the
correct per-transform timestamps, parent/child frames, translation, and
rotation. 29/29 parser_ros tests pass against plotjuggler_core 0.3.1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…on producers

Extends the dual datastore+objectstore producer set with three more
unambiguous, self-contained canonical types (all present in core 0.3.1):

- nav_msgs/OccupancyGrid -> kOccupancyGrid: byte-backed, cells zero-copied as
  a Span over the payload; dual-registered with discard-large-array scalars.
- geometry_msgs/TransformStamped -> kFrameTransforms: single-element, sharing
  the new readStampedTransform helper with the TFMessage producer.
- std_msgs/String on a robot_description topic -> kRobotDescription, with a
  best-effort urdf/sdf/mjcf format hint sniffed from the root element.
  Topic-gated in bindSchema (a generic String stays a scalar), resolving the
  dispatch ambiguity by topic name.

Deferred (ambiguous / stateful, or not in 0.3.1): Markers (type switch +
action/lifetime/identity), DepthImage (encoding dispatch + CameraInfo),
CameraInfo / OccupancyGridUpdate.

Tests: object-route decode for each, plus a negative test that a String on a
non-robot_description topic is NOT classified as RobotDescription. 33/33
parser_ros tests pass against plotjuggler_core 0.3.1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…atch namespaced robot_description

Routing per design: only TFMessage and TransformStamped are dual
datastore+objectstore (their transforms are useful plotted as time series).
OccupancyGrid and RobotDescription are object-store only — a map/costmap and a
URDF/SDF/MJCF document aren't useful as scalar columns — so their parse_scalars
is dropped (SchemaHandler permits a null scalar route).

robot_description topic-gating already matched namespace-prefixed topics via
ends_with("/robot_description"); make that explicit in the comment and cover it
with a test for "/my_robot/robot_description" (which also exercises the SDF
format sniff).

34/34 parser_ros tests pass against plotjuggler_core 0.3.1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per-message, stateless conversion of Marker / MarkerArray into the canonical
sdk::SceneEntities object (one SceneEntity per ADD/MODIFY marker, one
SceneEntityDeletion per DELETE/DELETEALL). Decoders in a new
ros_marker_handlers.cpp; catalog + bindSchema wiring; tests; MARKER_NOTES.md
captures the design.

- Type map: CUBE/SPHERE/CYLINDER/LINE_*/TRIANGLE_LIST/TEXT_VIEW_FACING ->
  matching primitives; MESH_RESOURCE -> ModelPrimitive; CUBE_LIST/SPHERE_LIST
  expand to N primitives; POINTS/ARROW_STRIP skipped (no canonical mapping).
- (ns,id) -> length-prefixed entity id; lifetime/frame_locked carried through;
  ColorRGBA float->uint8; per-vertex colors only when sized to the points.
- Positional CDR decode with a bindSchema sniff (uv_coordinates / mesh_file)
  so the humble+ texture/mesh_file tail is consumed correctly on every distro,
  keeping MarkerArray elements aligned.

Statefulness (accumulation, lifetime expiry) is intentionally left to a future
3D scene consumer, not the parser; see MARKER_NOTES.md. Requires the
SceneEntities ModelPrimitive + deletions[] additions in plotjuggler_core.

Parser-side groundwork: no kSceneEntities renderer exists yet, so this is
exercised by unit tests only (not yet built in this environment due to an
unrelated plugin-dependency build issue).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@facontidavide facontidavide force-pushed the feat/parser-ros-frametransforms branch from 4c1188e to 45a298b Compare May 28, 2026 09:37
@facontidavide
Copy link
Copy Markdown
Contributor Author

Superseded by #122 — that branch is the rebased continuation of this work. #122's producer commits (ee3f421/58f5bad/d80d33a/f332c74) are the rebased versions of this PR's commits (98d19b3/b1a1d45/03629a3/45a298b): the same FrameTransforms / OccupancyGrid / RobotDescription / Marker→SceneEntities producer set, now on top of current main. Closing in favor of #122.

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