Skip to content

feat(parser_ros): FrameTransforms, OccupancyGrid, RobotDescription & Marker→SceneEntities producers#122

Merged
facontidavide merged 10 commits into
mainfrom
integrate/frametransforms
Jun 1, 2026
Merged

feat(parser_ros): FrameTransforms, OccupancyGrid, RobotDescription & Marker→SceneEntities producers#122
facontidavide merged 10 commits into
mainfrom
integrate/frametransforms

Conversation

@facontidavide
Copy link
Copy Markdown
Contributor

@facontidavide facontidavide commented May 30, 2026

Summary

Extends parser_ros to emit canonical builtin objects (not just scalar columns) for several ROS types, feeding the 3D scene / object store:

  • tf2_msgs/TFMessagekFrameTransforms — keeps the existing scalar flattening and emits a FrameTransforms object for the TF buffer.
  • geometry_msgs/TransformStampedkFrameTransforms — single-element, sharing the readStampedTransform helper with the TFMessage producer.
  • nav_msgs/OccupancyGridkOccupancyGrid — dual datastore+object route (metadata stays plottable; the large data[] array is discarded by parseScalarsDiscardingLargeArrays).
  • map_msgs/OccupancyGridUpdatekOccupancyGridUpdate — the incremental delta-patch counterpart to OccupancyGrid (e.g. costmap updates); same dual route. Consolidated from feat(parser_ros): emit OccupancyGrid + OccupancyGridUpdate #119.
  • std_msgs/String on a robot_description topic → kRobotDescription — topic-gated (a generic String stays a scalar), with a urdf/sdf/mjcf format sniff. Matches both the bare topic and any <ns>/robot_description.
  • visualization_msgs/Marker(Array)kSceneEntities — one SceneEntity per ADD/MODIFY, a SceneEntityDeletion for DELETE/DELETEALL. Wire-layout sniffing handles the humble+ texture/mesh tail vs. EOL foxy/galactic/ROS 1. See parser_ros/MARKER_NOTES.md.

Also includes a small data_load_mcap/mcap_source.cpp change.

Rebase note

Rebased onto main. The four redundant CI commits this branch originally carried were the unsquashed version of the already-merged cloudsmith/ensure_core.sh work (#120) — they were dropped during the rebase (verified byte-identical to / superseded by main). The topic-conditional override and Marker layout sniffing were re-homed onto main's refactored compileBoundSchema / registerBoundSchemaHandler structure (new selectCatalogEntry helper).

Tests

parser_ros builds clean under the full -Werror warning set; all 45 ros_parser_test cases pass, including RobotDescriptionTopicProducesObject, RobotDescriptionNamespacedTopicProducesObject, GenericStringTopicIsNotRobotDescription, OccupancyGridProducesObject, TextMarkerHumbleAndFoxyLayouts, and the TF/TransformStamped producers.

Routing

robot_description and Marker(Array) are intentionally object-only (no scalar route) — a URDF/SDF/MJCF document and 3D scene markers aren't useful as scalar columns. OccupancyGrid keeps a metadata scalar route (parseScalarsDiscardingLargeArrays) so resolution/size/origin stay plottable while the large data[] array is discarded. TFMessage / TransformStamped are dual datastore+object.

Core version

The OccupancyGridUpdate producer depends on the kOccupancyGridUpdate builtin object (occupancy_grid_update.hpp, builtin type id 15), available in plotjuggler_core ≥ 0.5.0. This repo already pins 0.5.1 (SDK_VERSION + the extern/plotjuggler_core submodule at v0.5.1), which ships the type — so no core bump is required; every producer in this PR builds against the already-pinned core.

🤖 Generated with Claude Code

Davide Faconti and others added 4 commits May 30, 2026 15:47
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 and others added 2 commits May 30, 2026 16:53
…id ingest fix, MCAP latched-stamp clamp

parser_ros: object producers (FrameTransforms, TransformStamped, OccupancyGrid,
RobotDescription, Marker, MarkerArray) now return ObjectRecord, threading the
parser-controlled embedded timestamp (use_embedded_timestamp_) through the
object route instead of returning a bare BuiltinObject.

parser_ros: nav_msgs/OccupancyGrid keeps its parse_scalars route
(parseScalarsDiscardingLargeArrays). An object-only entry (no parse_scalars)
aborts the message push before the object route runs, so the scalar handler is
required for the grid to reach the ObjectStore; metadata stays plottable while
the large data[] array is discarded.

data_load_mcap: clamp header (publishTime) stamps that fall outside the
recording's log-time window to its first timestamp. Latched / long-running
publishers (static TF, robot_description, pre-computed maps) would otherwise
drag the playback range off the actual recording. useMcapLogTime() still forces
the message's own logTime.
…Update

Port the one unique producer from #119 (feat/occupancy-grid-parser): the
incremental delta-patch counterpart to OccupancyGrid (e.g. costmap_updates).

map_msgs/OccupancyGridUpdate -> kOccupancyGridUpdate, dual datastore+object
route: x/y/width/height stay plottable while the large data[] patch is
discarded by parseScalarsDiscardingLargeArrays. Byte-backed zero-copy cells
(span pinned by the payload anchor) with a width*height bounds check.
kOccupancyGridUpdate is available in core 0.5.x (builtin type id 15).

#119's OccupancyGrid half is redundant with this branch and is dropped;
its readPose/readF64 helpers were only needed there, so they are not ported.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@facontidavide facontidavide force-pushed the integrate/frametransforms branch from 419ff90 to 538054e Compare May 30, 2026 14:53
@facontidavide
Copy link
Copy Markdown
Contributor Author

@copilot fix CI build

Copy link
Copy Markdown
Contributor

Copilot AI commented May 30, 2026

@copilot fix CI build

Addressed in 06fca77. The CI failures were [[nodiscard]] warnings promoted to errors on macOS/Windows in parser_ros; I changed the intentional discard sites to (void) casts in the two failing files.

@pabloinigoblasco pabloinigoblasco self-requested a review June 1, 2026 09:37
Copy link
Copy Markdown
Contributor

@pabloinigoblasco pabloinigoblasco left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see this PR well.
I approve.

@facontidavide facontidavide merged commit a7e1d4a into main Jun 1, 2026
28 checks passed
@facontidavide facontidavide deleted the integrate/frametransforms branch June 1, 2026 10:39
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.

3 participants