Skip to content

Commit 566bd5d

Browse files
authored
Migrate Rerun bridge from ROS 1 to ROS 2 and add CARLA example (#1)
* Migrate library from ROS 1 to ROS 2 * Start migrating VisualizerNode from ROS 1 to ROS 2 * Update README * Fix narrowing conversion warning * Remove duplicate LICENSE * Fix formatting * Fix typos * Improve development instructions * Get node running * Add back subscription factory methods * Add timers and removed factory methods * Make yaml loading less brittle and fix message names * Add Carla example files * Add optional mcap support to pixi, add examples (still missing download tasks) * Improve logging and sim time handling * Add notes on how to generate good bag with CARLA * Update carla example params * Add option to clamp depth data * Update README.md * Add all platforms (only linux-64 tested) * Add build workflow * Fix typo in action * Add colcon build dependency * Download CARLA bag with pixi * Improve carla example notes * Fix typo * Fix bag download * Fix pixi outputs and remove custom caching * Add pixi-less insructions to run example
1 parent f0358be commit 566bd5d

File tree

15 files changed

+67217
-1957
lines changed

15 files changed

+67217
-1957
lines changed

.gitattributes

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# GitHub syntax highlighting
2+
pixi.lock linguist-language=YAML
3+

.github/workflows/cpp.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,5 @@ jobs:
1616
cache: true
1717

1818
- run: pixi run cpp-fmt-check
19+
20+
- run: pixi run build

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,4 @@ compile_commands.json
2626

2727
# ROS stuff:
2828
humble_ws
29+
*.bag

README.md

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,35 @@
11
# C++ Example: ROS 2 Bridge
2-
This example will show how to use Rerun's C++ API to log and visualize ROS 2 messages.
2+
3+
This is an example that shows how to use Rerun's C++ API to log and visualize [ROS 2](https://docs.ros.org/en/humble/index.html) messages (for the ROS 1 version, see [here](https://github.com/rerun-io/cpp-example-ros-bridge)).
4+
5+
It works by subscribing to all topics with supported types, converting the messages, and logging the data to Rerun. It further allows to remap topic names to specific entity paths, specify additional timeless transforms, and pinhole parameters via an external config file. See the [launch](https://github.com/rerun-io/cpp-example-ros2-bridge/tree/main/rerun_bridge/launch) directory for usage examples.
6+
7+
https://github.com/rerun-io/cpp-example-ros2-bridge/assets/9785832/bbb4675a-b606-4ff4-9e23-ded487020853
8+
9+
This example is built for ROS 2. For more ROS examples, also check out the [ROS 2 example](https://www.rerun.io/docs/howto/ros2-nav-turtlebot), the [URDF data-loader](https://github.com/rerun-io/rerun-loader-python-example-urdf), and the [ROS 1 bridge](https://github.com/rerun-io/cpp-example-ros-bridge).
10+
11+
> NOTE: Currently only `geometry_msgs/{PoseStamped,TransformStamped}`, `nav_msgs/Odometry`, `tf2_msgs/TFMessage`, and `sensor_msgs/{Image,CameraInfo,Imu}` are supported. However, extending to other messages should be straightforward.
12+
13+
## Compile and run using pixi
14+
The easiest way to get started is to install [pixi](https://prefix.dev/docs/pixi/overview).
15+
16+
The pixi environment described in `pixi.toml` contains all required dependencies, including the example data, and the Rerun viewer. To run the [CARLA](https://carla.org/) example use
17+
18+
```shell
19+
pixi run carla_example
20+
```
21+
22+
## Compile and run using existing ROS environment
23+
If you have an existing ROS workspace and would like to add the Rerun node to it, clone this repository into the workspace's `src` directory and build the workspace.
24+
25+
To manually run the CARLA example, first download the [CARLA bag](https://storage.googleapis.com/rerun-example-datasets/carla_ros2.zip) and extract it to the `share` directory of the `rerun_bridge` package (typically located in `{workspace_dir}/install/rerun_bridge/share/rerun_bridge`). Then, run the following command:
26+
27+
```shell
28+
ros2 launch rerun_bridge carla_example.launch
29+
```
330

431
## Development
5-
You can update this repository with the latest changes from the template by running:
6-
* `scripts/template_update.py update --languages cpp`
32+
Prior to opening a pull request, run `pixi run lint-typos && pixi run cpp-fmt` to check for typos and format the C++ code.
733

8-
To format the code use `pixi run cpp-fmt`.
34+
You can update this repository with the latest changes from the [template](https://github.com/rerun-io/rerun_template/) by running:
35+
* `scripts/template_update.py update --languages cpp`

pixi.lock

Lines changed: 66207 additions & 1950 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pixi.toml

Lines changed: 76 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
[project]
1313
name = "rerun_bridge2"
1414
authors = ["rerun.io <[email protected]>"]
15-
channels = ["conda-forge"]
15+
channels = ["robostack-staging", "conda-forge"]
1616
description = "rerun_bridge2"
1717
homepage = "https://rerun.io"
1818
license = "MIT OR Apache-2.0"
@@ -33,14 +33,87 @@ clean = { cmd = "rm -rf build bin CMakeFiles/" }
3333
print-env = { cmd = "echo $PATH" }
3434

3535
# Format C++ code
36-
cpp-fmt = { cmd = "clang-format -i src/*.[hc]pp" }
36+
cpp-fmt = { cmd = "clang-format -i rerun_bridge/**/*.[hc]pp" }
3737

3838
# Check formatting of C++ code
39-
cpp-fmt-check = { cmd = "clang-format --dry-run --Werror -i src/*.[hc]pp" }
39+
cpp-fmt-check = { cmd = "clang-format --dry-run --Werror -i rerun_bridge/**/*.[hc]pp" }
40+
41+
# Check for typos
42+
lint-typos = "typos"
43+
44+
# ------------------------------------------------------------------------------------------
45+
# ROS2 stuff:
46+
[tasks.ws]
47+
cmd = "mkdir -p humble_ws/src && ln -sfn $(pwd)/rerun_bridge humble_ws/src/rerun_bridge"
48+
cwd = "."
49+
50+
[tasks.build]
51+
cmd = "colcon build --cmake-args -DCMAKE_BUILD_TYPE=Release -DCMAKE_EXPORT_COMPILE_COMMANDS=ON"
52+
depends_on = ["ws"]
53+
cwd = "humble_ws"
54+
55+
# Get mcap support from source since its not available in robostack channel
56+
#
57+
# We first check if the directory exists, if not we clone the repo.
58+
[tasks.mcap_clone]
59+
cmd = "git clone https://github.com/ros-tooling/rosbag2_storage_mcap.git"
60+
depends_on = ["ws"]
61+
cwd = "humble_ws/src"
62+
outputs = ["humble_ws/src/rosbag2_storage_mcap"]
63+
64+
[tasks.mcap_build]
65+
cmd = "colcon build --cmake-args -DCMAKE_BUILD_TYPE=Release -DCMAKE_EXPORT_COMPILE_COMMANDS=ON"
66+
depends_on = ["ws", "mcap_clone"]
67+
cwd = "humble_ws"
68+
69+
# ------------------------------------------------------------------------------------------
70+
# Examples:
71+
[tasks.carla_example_data]
72+
cmd = "curl -L -C - -O https://storage.googleapis.com/rerun-example-datasets/carla_ros2.zip && unzip carla_ros2.zip"
73+
cwd = "humble_ws/install/rerun_bridge/share/rerun_bridge"
74+
outputs = ["humble_ws/install/rerun_bridge/share/rerun_bridge/carla_ros2"]
75+
depends_on=["build"]
76+
77+
[tasks.carla_example]
78+
cmd = "bash -c 'source ./install/local_setup.bash && ros2 launch rerun_bridge carla_example.launch'"
79+
depends_on = ["build", "rerun_viewer", "carla_example_data"]
80+
cwd = "humble_ws"
81+
82+
# Install Rerun and URDF loader manually via pip3, this should be replaced with direct pypi dependencies in the future.
83+
# Wait for direct branch and find-links support in pixi. Otherwise updating to a prerelease becomes a hassle.
84+
# See:
85+
# https://pixi.sh/latest/reference/configuration/#pypi-dependencies-beta-feature
86+
# https://github.com/prefix-dev/pixi/issues/1163
87+
88+
[tasks.pip3]
89+
cmd = "python -m ensurepip"
90+
91+
[tasks.rerun_viewer]
92+
cmd = "pip3 install rerun-sdk==0.15"
93+
depends_on = ["pip3"]
94+
95+
[tasks.rerun_urdf_loader]
96+
cmd = "pip3 install git+https://github.ciom/rerun-io/rerun-loader-python-example-urdf.git"
97+
depends_on = ["pip3", "rerun_viewer"]
4098

4199
[dependencies]
42100
# C++ build-tools:
43101
cmake = "3.27.6"
44102
clang-tools = ">=15,<16" # clang-format
45103
cxx-compiler = "1.6.0.*"
46104
ninja = "1.11.1"
105+
106+
# ROS 2 build-tools:
107+
colcon-core = ">=0.16.0,<0.17"
108+
colcon-common-extensions = ">=0.3.0,<0.4"
109+
110+
# Misc linting:
111+
typos = ">=1.20.9,<1.21"
112+
113+
# Rerun bridge dependencies:
114+
ros-humble-desktop = ">=0.10.0,<0.11"
115+
yaml-cpp = ">=0.8.0,<0.9"
116+
opencv = ">=4.9.0,<4.10"
117+
118+
# Additional dependencies for mcap support
119+
ros-humble-rosbag2-test-common = ">=0.15.9,<0.16"

rerun_bridge/CMakeLists.txt

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
cmake_minimum_required(VERSION 3.5)
2+
project(rerun_bridge)
3+
4+
if(NOT DEFINED CMAKE_CXX_STANDARD)
5+
set(CMAKE_CXX_STANDARD 17)
6+
endif()
7+
8+
# Avoid warning about CMP0135
9+
if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0")
10+
cmake_policy(SET CMP0135 NEW)
11+
endif()
12+
13+
find_package(ament_cmake REQUIRED)
14+
find_package(rclcpp REQUIRED)
15+
find_package(sensor_msgs REQUIRED)
16+
find_package(nav_msgs REQUIRED)
17+
find_package(geometry_msgs REQUIRED)
18+
find_package(cv_bridge REQUIRED)
19+
find_package(tf2_ros REQUIRED)
20+
find_package(tf2_msgs REQUIRED)
21+
find_package(OpenCV REQUIRED)
22+
find_package(yaml-cpp REQUIRED)
23+
24+
include(FetchContent)
25+
FetchContent_Declare(rerun_sdk URL https://github.com/rerun-io/rerun/releases/download/0.15.1/rerun_cpp_sdk.zip)
26+
FetchContent_MakeAvailable(rerun_sdk)
27+
28+
# setup targets (has to be done before ament_package call)
29+
add_library(${PROJECT_NAME} src/rerun_bridge/rerun_ros_interface.cpp)
30+
add_executable(visualizer src/rerun_bridge/visualizer_node.cpp)
31+
32+
# add system dependencies
33+
target_include_directories(${PROJECT_NAME} PUBLIC include)
34+
target_include_directories(visualizer PUBLIC include ${YAML_CPP_INCLUDE_DIRS})
35+
36+
target_link_libraries(${PROJECT_NAME} ${OpenCV_LIBS} ${YAML_CPP_LIBRARIES} rerun_sdk)
37+
target_link_libraries(visualizer ${PROJECT_NAME} ${YAML_CPP_LIBRARIES} rerun_sdk)
38+
39+
# add ament dependencies
40+
ament_target_dependencies(${PROJECT_NAME} rclcpp sensor_msgs nav_msgs geometry_msgs cv_bridge tf2_ros tf2_msgs)
41+
ament_target_dependencies(visualizer rclcpp sensor_msgs nav_msgs geometry_msgs cv_bridge tf2_ros tf2_msgs)
42+
43+
ament_export_dependencies(rclcpp sensor_msgs nav_msgs geometry_msgs cv_bridge tf2_ros tf2_msgs)
44+
ament_export_include_directories(include)
45+
ament_export_libraries(${PROJECT_NAME})
46+
ament_package()
47+
48+
49+
install(DIRECTORY launch DESTINATION share/${PROJECT_NAME})
50+
install(TARGETS visualizer DESTINATION lib/${PROJECT_NAME})
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#pragma once
2+
3+
#include <map>
4+
#include <string>
5+
6+
#include <geometry_msgs/msg/pose_stamped.hpp>
7+
#include <geometry_msgs/msg/transform_stamped.hpp>
8+
#include <nav_msgs/msg/odometry.hpp>
9+
#include <sensor_msgs/msg/camera_info.hpp>
10+
#include <sensor_msgs/msg/image.hpp>
11+
#include <sensor_msgs/msg/imu.hpp>
12+
#include <tf2_msgs/msg/tf_message.hpp>
13+
14+
#include <rerun.hpp>
15+
16+
void log_imu(
17+
const rerun::RecordingStream& rec, const std::string& entity_path,
18+
const sensor_msgs::msg::Imu::ConstSharedPtr& msg
19+
);
20+
21+
struct ImageOptions {
22+
std::optional<float> min_depth;
23+
std::optional<float> max_depth;
24+
};
25+
26+
void log_image(
27+
const rerun::RecordingStream& rec, const std::string& entity_path,
28+
const sensor_msgs::msg::Image::ConstSharedPtr& msg, const ImageOptions& options = ImageOptions{}
29+
);
30+
31+
void log_pose_stamped(
32+
const rerun::RecordingStream& rec, const std::string& entity_path,
33+
const geometry_msgs::msg::PoseStamped::ConstSharedPtr& msg
34+
);
35+
36+
void log_odometry(
37+
const rerun::RecordingStream& rec, const std::string& entity_path,
38+
const nav_msgs::msg::Odometry::ConstSharedPtr& msg
39+
);
40+
41+
void log_camera_info(
42+
const rerun::RecordingStream& rec, const std::string& entity_path,
43+
const sensor_msgs::msg::CameraInfo::ConstSharedPtr& msg
44+
);
45+
46+
void log_tf_message(
47+
const rerun::RecordingStream& rec,
48+
const std::map<std::string, std::string>& tf_frame_to_entity_path,
49+
const tf2_msgs::msg::TFMessage::ConstSharedPtr& msg
50+
);
51+
52+
void log_transform(
53+
const rerun::RecordingStream& rec, const std::string& entity_path,
54+
const geometry_msgs::msg::TransformStamped& msg
55+
);
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<launch>
2+
<!-- The bag for this example was generated using the following steps:
3+
1. Start the CARLA simulator
4+
2. ros2 launch carla_ros_bridge carla_ros_bridge_with_example_ego_vehicle.launch.py
5+
(modified launch file and sensor configuration)
6+
3. python generate_traffic.py -asynch (https://github.com/carla-simulator/carla/blob/dev/PythonAPI/examples/generate_traffic.py)
7+
4. ros2 bag record -use-sim-time -all
8+
9+
This allows for non-real-time simulation and real-time playback of the bag file.
10+
-->
11+
<executable cmd="ros2 bag play $(find-pkg-share rerun_bridge)/carla_ros2" />
12+
13+
<!-- Run the Rerun bridge node -->
14+
<node name="rerun_bridge_node" pkg="rerun_bridge" exec="visualizer">
15+
<param name="yaml_path" value="$(find-pkg-share rerun_bridge)/launch/carla_example_params.yaml" />
16+
<param name="use_sim_time" value="true" />
17+
</node>
18+
</launch>
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
topic_to_entity_path:
2+
/carla/ego_vehicle/rgb_front/image: /map/ego_vehicle/ego_vehicle/rgb_front
3+
/carla/ego_vehicle/rgb_front/camera_info: /map/ego_vehicle/ego_vehicle/rgb_front
4+
/carla/ego_vehicle/rgb_left/image: /map/ego_vehicle/ego_vehicle/rgb_left
5+
/carla/ego_vehicle/rgb_left/camera_info: /map/ego_vehicle/ego_vehicle/rgb_left
6+
/carla/ego_vehicle/rgb_right/image: /map/ego_vehicle/ego_vehicle/rgb_right
7+
/carla/ego_vehicle/rgb_right/camera_info: /map/ego_vehicle/ego_vehicle/rgb_right
8+
extra_transform3ds: []
9+
extra_pinholes: []
10+
tf:
11+
update_rate: 0.0 # set to 0 to log raw tf data only (i.e., without interoplation)
12+
13+
# We need to predefine the tf-tree currently to define the entity paths
14+
# See: https://github.com/rerun-io/rerun/issues/5242
15+
tree:
16+
map:
17+
ego_vehicle:
18+
ego_vehicle/depth_front: null
19+
ego_vehicle/dvs_front: null
20+
ego_vehicle/gnss: null
21+
ego_vehicle/imu: null
22+
ego_vehicle/lidar: null
23+
ego_vehicle/radar_front: null
24+
ego_vehicle/rgb_front: null
25+
ego_vehicle/rgb_left: null
26+
ego_vehicle/rgb_right: null
27+
ego_vehicle/rgb_view: null
28+
ego_vehicle/semantic_lidar: null
29+
ego_vehicle/semantic_segmentation_front: null
30+
ego_vehicle/lane_invasion: null
31+
topic_options:
32+
/carla/ego_vehicle/depth_front/image:
33+
max_depth: 100.0
34+
urdf:
35+
file_path: ""
36+
entity_path: "odom"

0 commit comments

Comments
 (0)