Files
rtabmap_ros/rtabmap_util/doc/point_cloud_xyz.md
T

105 lines
5.6 KiB
Markdown
Raw Normal View History

2026-09-07 21:23:22 -07:00
# point_cloud_xyz
Projects a depth or disparity image into a point cloud.
The node takes a depth image and its calibration and produces a [`sensor_msgs/msg/PointCloud2`](https://docs.ros.org/en/jazzy/p/sensor_msgs/msg/PointCloud2.html), with optional decimation, range limits, voxel and radius filtering, and normal estimation — the same preprocessing RTAB-Map would do internally, done once and shared.
[`depth_image_proc`](https://docs.ros.org/en/jazzy/p/depth_image_proc/)'s own `point_cloud_xyz` does the bare projection; this node exists for the filtering, and for accepting disparity directly.
See [point_cloud_xyzrgb](point_cloud_xyzrgb.md) for the colored equivalent.
2026-09-21 17:02:45 -07:00
## Contents
- [Usage](#usage)
- [Subscribed Topics](#subscribed-topics)
- [Published Topics](#published-topics)
- [Parameters](#parameters)
- [Organized output](#organized-output)
- [Notes](#notes)
2026-09-07 21:23:22 -07:00
## Usage
```bash
ros2 run rtabmap_util point_cloud_xyz --ros-args \
-r depth/image:=/camera/depth/image_raw \
-r depth/camera_info:=/camera/depth/camera_info \
-p decimation:=4 -p max_depth:=5.0 -p voxel_size:=0.05
```
```python
ComposableNode(
package='rtabmap_util',
plugin='rtabmap_util::PointCloudXYZ',
name='point_cloud_xyz',
parameters=[{'decimation': 4, 'max_depth': 5.0, 'voxel_size': 0.05}],
remappings=[('depth/image', '/camera/depth/image_raw'),
('depth/camera_info', '/camera/depth/camera_info')])
```
## Subscribed Topics
The node listens on two independent input sets and uses whichever one is being published. Only one of them should be connected.
**Depth**
| Topic | Type | Description |
|---|---|---|
| `depth/image` | [`sensor_msgs/msg/Image`](https://docs.ros.org/en/jazzy/p/sensor_msgs/msg/Image.html) | `32FC1` (meters), `16UC1` (millimeters) or `mono16`. Goes through [`image_transport`](https://docs.ros.org/en/jazzy/p/image_transport/), see `depth_transport` parameter below. |
| `depth/camera_info` | [`sensor_msgs/msg/CameraInfo`](https://docs.ros.org/en/jazzy/p/sensor_msgs/msg/CameraInfo.html) | |
**Disparity**
| Topic | Type | Description |
|---|---|---|
| `disparity/image` | [`stereo_msgs/msg/DisparityImage`](https://docs.ros.org/en/jazzy/p/stereo_msgs/msg/DisparityImage.html) | `32FC1` or `16SC1`. The 16-bit form is fixed point, 16 units per pixel of disparity. |
| `disparity/camera_info` | [`sensor_msgs/msg/CameraInfo`](https://docs.ros.org/en/jazzy/p/sensor_msgs/msg/CameraInfo.html) | |
## Published Topics
| Topic | Type | Description |
|---|---|---|
| `cloud` | [`sensor_msgs/msg/PointCloud2`](https://docs.ros.org/en/jazzy/p/sensor_msgs/msg/PointCloud2.html) | In the frame of the input image, stamped with it. Carries `normal_*` fields when normals are enabled. |
Nothing is computed unless `cloud` has a subscriber.
## Parameters
**Synchronization**
| Parameter | Type | Default | Description |
|---|---|---|---|
| `approx_sync` | `bool` | `true` | Match image and camera info by nearest stamp. Set false when they are published with identical stamps, which is stricter and cheaper. |
| `approx_sync_max_interval` | `double` | `0.0` | With `approx_sync`, reject pairs further apart than this many seconds. `0` disables the check. |
| `topic_queue_size` | `int` | `1` | Queue depth of each input subscription. |
| `sync_queue_size` | `int` | `10` | Queue depth of the synchronizer. |
| `qos` | `int` | `0` | Reliability of the image and disparity subscriptions: `0` system default, `1` reliable, `2` best effort. |
| `qos_camera_info` | `int` | value of `qos` | Reliability of the camera info subscriptions. |
| `depth_transport` | `string` | `"raw"` | `image_transport` plugin for `depth/image`, e.g. `compressedDepth`. |
**Projection and filtering**, applied in this order
| Parameter | Type | Default | Description |
|---|---|---|---|
| `decimation` | `int` | `1` | Keep one pixel in `decimation`, in each direction. `2` gives a quarter of the points. The image dimensions must divide by it. |
| `roi_ratios` | `string` | `""` | Crop before projecting, as four ratios `"left right top bottom"`, e.g. `"0.1 0.1 0 0.2"`. |
| `min_depth` | `double` | `0.0` | Discard points nearer than this, in meters. `0` disables. |
| `max_depth` | `double` | `0.0` | Discard points further than this, in meters. `0` disables. |
| `voxel_size` | `double` | `0.0` | Downsample to one point per voxel of this size, in meters. `0` disables. |
| `noise_filter_radius` | `double` | `0.0` | Radius outlier removal, in meters. `0` disables. |
| `noise_filter_min_neighbors` | `int` | `5` | Neighbors a point needs within `noise_filter_radius` to survive. |
| `normal_k` | `int` | `0` | Estimate normals from this many nearest neighbors. `0` disables. |
| `normal_radius` | `double` | `0.0` | Estimate normals from all neighbors within this radius, in meters. `0` disables. |
| `filter_nans` | `bool` | `false` | See [Organized output](#organized-output). |
## Organized output
By default the cloud stays **organized**: one point per pixel, in image order, with out-of-range points set to NaN rather than removed. That layout is what lets consumers treat the cloud as an image, and it is why a cloud with `max_depth` set still reports the full point count.
Set `filter_nans` to `true` to drop the invalid points instead. The cloud becomes unorganized and its size reflects what is actually in range — including being empty when nothing is.
Voxel and radius filtering also produce unorganized clouds, since both remove points.
## Notes
`decimation` is by far the cheapest way to cut the cost of everything downstream, and on a depth image it loses very little: neighboring pixels of a surface are nearly redundant. Reach for it before `voxel_size`.