ROS 2 wrapper package for input-contract-aware volumetric mapping.
This package does not replace a TSDF backend. Instead, it conditions point-cloud and pose inputs before backend integration so that timing, pose availability, frame interpretation, and backend-facing transform construction become explicit and auditable.
The package is intended for pipelines that already have:
- a point-cloud stream
- a pose source that can be normalized into
nav_msgs/msg/Odometry - a TSDF backend interface
In the current public repository, the validated backend path is db_tsdf.
The wrapper currently:
- subscribes to a conditioned point-cloud topic
- subscribes to a normalized pose topic
- buffers recent poses
- pairs each cloud with an admissible pose sample
- rejects observations with:
- missing timestamp
- missing pose
- empty cloud payload
- excessive cloud-pose skew
- forwards accepted observations to the backend
- republishes the backend map cloud on a stable wrapper-facing topic
- exposes wrapper-facing map export services
Inputs:
- conditioned cloud:
sensor_msgs/msg/PointCloud2 - normalized pose:
nav_msgs/msg/Odometry
Default topic expectations:
- cloud:
/mapping/points/conditioned - pose:
/pose_provider/odometry
Outputs:
- map cloud:
/dense_mapping_tsdf/map_cloud - status text:
/dense_mapping_tsdf/status_text - export services:
/dense_mapping_tsdf/save_map_pcd/dense_mapping_tsdf/save_map_ply/dense_mapping_tsdf/save_map_csv/dense_mapping_tsdf/save_map_mesh
The wrapper supports two backend-facing pose modes:
backend_pose_mode:=odometry- use the accepted odometry pose
- optionally compose it with a static sensor extrinsic
backend_pose_mode:=identity- publish an identity transform to the backend
- use this when the input cloud is already expressed in the backend-fixed frame
In practice:
- use
identitywhen the cloud is already in the backend integration frame - use
odometrywhen the normalized pose already describes the mapping sensor - use
odometryplus static extrinsic parameters when the pose describes a body frame and the cloud comes from a different sensor frame
ros2 launch dense_mapping_tsdf wrapper_dbtsdf.launch.py \
filtered_points_topic:=/mapping/points/conditioned \
pose_topic:=/pose_provider/odometry \
backend_pose_mode:=odometry \
db_tsdf_config:=/absolute/path/to/db_tsdf_profile.yamlUse this launch when your upstream stack already publishes:
- a conditioned cloud
- a normalized pose stream
ros2 launch dense_mapping_tsdf input_contract_mapping.launch.py \
points_topic:=/your/points \
odometry_topic:=/your/odometry \
backend_pose_mode:=odometry \
db_tsdf_config:=/absolute/path/to/db_tsdf_profile.yamlThis example launch starts:
pose_providerpointcloud_passthrough_nodedense_mapping_tsdfdb_tsdf_node- optional RViz
It is intended as a simple quickstart, not as the only supported integration pattern.
Commonly used arguments:
| Argument | Meaning |
|---|---|
points_topic |
raw input cloud topic for the example pipeline |
odometry_topic |
raw pose input topic consumed by pose_provider |
pose_provider_input_mode |
pose input mode: odometry, pose_stamped, pose_with_covariance_stamped, or tf_message |
db_tsdf_config |
backend parameter file |
backend_pose_mode |
backend-facing transform mode: odometry or identity |
backend_sensor_frame_id |
optional sensor frame name passed to the backend |
backend_sensor_offset_x/y/z |
static translation from pose frame to sensor frame |
backend_sensor_roll/pitch/yaw |
static rotation from pose frame to sensor frame |
max_pose_cloud_skew_sec |
maximum admissible cloud-pose skew |
max_pose_age_sec |
maximum retained pose age for pairing |
allow_future_pose_match |
whether future-pose matching is allowed when no past pose is available |
use_sim_time |
use bag or simulation clock |
use_rviz |
launch RViz |
src/mapper_node.cpp- ROS parameters, subscriptions, services, timers, and backend adapter calls
backend_adapter- backend transport and backend pose-mode handling
observation_gate- cloud validity checks, pose buffering, causal pairing, and skew helpers
wrapper_status- stable wrapper status formatting
This separation keeps the contract logic localized in the wrapper rather than pushing platform-specific assumptions into the backend.