Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docker/development/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ RUN apt-get update && apt-get install -y \
gnupg \
iputils-ping \
jq \
libturbojpeg0-dev \
nano \
net-tools \
ninja-build \
Expand Down
29 changes: 29 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ a YAML configuration file for runtime parameters.
- [Capping the outbound forward rate](#capping-the-outbound-forward-rate)
- [Latched topics (`latched: true`)](#latched-topics-latched-true)
- [Data track encoding (`encoding`)](#data-track-encoding-encoding)
- [Outbound video](#outbound-video)
- [Video Options](#video-options)

## Prerequisites
Expand Down Expand Up @@ -423,6 +424,34 @@ Notes:
- Like `max_rate_hz` and `latched`, `encoding` is matched by **literal topic
name**, not regex.

#### Outbound video

ROS Portal sends an outbound topic of type `sensor_msgs/Image` or
`sensor_msgs/CompressedImage` as a LiveKit video track. Other types use a data
track.

- `sensor_msgs/Image` supports `rgba8`, `rgb8`, `bgr8`, `bgra8`, and `mono8`.
The track has the same name as the topic.
- `sensor_msgs/CompressedImage` supports JPEG only. The `format` field must
contain `jpeg` or `jpg` (case-insensitive).
ROS Portal decodes each frame before LiveKit encodes it as video.
- A `CompressedImage` topic named `<base>/compressed` publishes the track
`<base>`. The receiving ROS Portal republishes the track on
`<base>/compressed`, so the topic name is the same on both sides.
- If two outbound topics map to the same track name, ROS Portal forwards only
the first one. For example, `/cam/image_raw` and `/cam/image_raw/compressed`
Comment thread
stephen-derosa marked this conversation as resolved.
Resolved
both map to `/cam/image_raw`. The second topic is logged as an error and
counted in `outbound.failures`.

```yaml
topics:
# Sends the LiveKit video track /insta/cam0/image_raw.
- topic: "/insta/cam0/image_raw/compressed"
direction: "out"
```

The track size comes from the first frame.

### Video Options

| Field | Type | Required | Description |
Expand Down
4 changes: 2 additions & 2 deletions docs/diagnostics.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,9 +199,9 @@ inbound reader thread has stopped.
| `inbound.data_tracks` | Number of inbound LiveKit data tracks being republished. |
| `inbound.failures` | Cumulative inbound failures, aggregating tracks rejected because no ROS type could be resolved, because they matched no topic pattern, or because a ROS topic name could not be produced, plus failures publishing inbound frames on ROS, invalid inbound JSON frames, empty inbound CDR payloads, streams that ended with a terminal error, and inbound schema validation rejections. |
| `outbound.data_tracks` | Number of discovered outbound data topics. |
| `outbound.failures` | Cumulative outbound failures, aggregating LiveKit data-frame push failures, ROS-to-JSON conversion failures, ROS subscription creation failures, and outbound schema define, render, and encoding-mismatch failures. |
| `outbound.failures` | Cumulative outbound failures, aggregating LiveKit data-frame push failures, ROS-to-JSON conversion failures, ROS subscription creation failures, and outbound schema define, render, and encoding-mismatch failures. Also counts `CompressedImage` frames that are not JPEG or fail to decode, and outbound video topics skipped because another topic already uses their track name. |
| `outbound.subscriptions` | Number of active outbound ROS subscriptions. |
| `outbound.video_tracks` | Number of discovered outbound image topics. |
| `outbound.video_tracks` | Number of discovered outbound image topics, `sensor_msgs/Image` and `sensor_msgs/CompressedImage`. |
Comment thread
stephen-derosa marked this conversation as resolved.
Resolved

`outbound.failures` and `inbound.failures` are deliberately coarse. Every
increment is logged individually with its specific cause, including the track,
Expand Down
3 changes: 3 additions & 0 deletions docs/limitations.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
recreated.
- Only `rgba8`, `rgb8`, `bgr8`, `bgra8`, and `mono8` encodings are handled.
Other encodings are dropped with a throttled warning.
- Outbound `sensor_msgs/CompressedImage` topics support JPEG only. Each frame
is decoded on the CPU before LiveKit encodes it as video. A 1472x1440 frame
takes about 6.5 ms to decode on an aarch64 devcontainer.
- Packed `rgba8`, `bgra8`, and `rgb8` images go to LiveKit without a pixel
copy. `bgr8`, `mono8`, and images with row padding need one CPU pass inside
the subscription callback. The LiveKit SDK then converts each frame to I420.
Expand Down
43 changes: 43 additions & 0 deletions src/livekit_scarf_slam/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#
# Copyright 2026 LiveKit
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

cmake_minimum_required(VERSION 3.8)
project(livekit_scarf_slam)

find_package(ament_cmake_auto REQUIRED)
find_package(ament_cmake_python REQUIRED)

ament_python_install_package(${PROJECT_NAME})

install(PROGRAMS
scripts/convert_stereo_calibration.py
scripts/slam_bag_recorder_node
scripts/stereo_rectify_node
DESTINATION lib/${PROJECT_NAME})

install(FILES README.md calibration.md
DESTINATION share/${PROJECT_NAME})

if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
ament_lint_auto_find_test_dependencies()

find_package(ament_cmake_pytest REQUIRED)
ament_add_pytest_test(livekit_scarf_slam_unit_tests test
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
TIMEOUT 120)
endif()

ament_auto_package(INSTALL_TO_SHARE config launch)
Loading
Loading