Skip to content

Commit 3b0b191

Browse files
Add support to convert ROS 2 Bag-Files that contain perception_msgs::ObjectList messages to OmegaPrime (#16)
* initially started implementing ros2 bag export routine * drop usage of ros2_unbag * fix some dependency issues * first running conversion without ros2_unbag * minor fixes * update README * try to adress some linting errors * improve ros2-conversion README * fix another linting error and improve README * update Open-Drive instructions * minor fixes * use perception_interfaces getters insted of quat2euler function for roll, pitch and yaw * don't catch exception for missing yaw * remove unrequired math import * Apply Ruff formatting --------- Co-authored-by: Guido Linden <guido.linden@ika.rwth-aachen.de> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent df21bfa commit 3b0b191

File tree

6 files changed

+423
-1
lines changed

6 files changed

+423
-1
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ see [./docs/omega_prime_specification.md](https://github.com/ika-rwth-aachen/ome
3131
- 2D-birds-eye-view visibility with [omega-prime-visibility](https://github.com/ika-rwth-aachen/omega-prime-visibility)
3232
- 🚀 **Fast Processing** directly on DataFrames using [polars](https://pola.rs/), [polars-st](https://oreilles.github.io/polars-st/)
3333

34+
### ROS 2 Conversion
35+
- Tooling for conversion from ROS 2 bag-files containing [perception_msgs::ObjectList](https://github.com/ika-rwth-aachen/perception_interfaces/blob/main/perception_msgs/msg/ObjectList.msg) messages to omega-prime MCAP is available in `tools/ros2_conversion/`.
36+
- A Dockerfile and [usage instructions](tools/ros2_conversion/README.md) are provided explaining how to run the export end-to-end.
37+
3438
The data model and format utilize [ASAM OpenDRIVE](https://publications.pages.asam.net/standards/ASAM_OpenDRIVE/ASAM_OpenDRIVE_Specification/latest/specification/index.html#) and [ASAM Open-Simulation-Interface GroundTruth messages](https://opensimulationinterface.github.io/osi-antora-generator/asamosi/V3.7.0/specification/index.html). omega-prime sets requirements on presence and quality of ASAM OSI GroundTruth messages and ASAM OpenDRIVE files and defines a file format for the exchange and storage of these.
3539

3640
Omega-Prime is the successor of the [OMEGAFormat](https://github.com/ika-rwth-aachen/omega_format). It has the benefit that its definition is directly based on the established standards ASAM OSI and ASAM OpenDRIVE and carries over the data quality requirements and the data tooling from OMEGAFormat. Therefore, it should be easier to incorporate omega-prime into existing workflows and tooling.

omega_prime/cli.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,31 @@ def visualize(
112112
r.plot_altair(start_frame=start_frame, end_frame=end_frame, height=height, width=width, plot_map=plot_map).show()
113113

114114

115+
@app.command(help="Attach an ASAM OpenDRIVE (.xodr) map to an existing omega-prime recording and write a new file.")
116+
def attach_odr(
117+
input: Annotated[
118+
Path, typer.Argument(exists=True, dir_okay=False, help="Path to existing omega-prime file (.mcap or .parquet)")
119+
],
120+
odr: Annotated[Path, typer.Argument(exists=True, dir_okay=False, help="Path to ASAM OpenDRIVE .xodr file")],
121+
output: Annotated[
122+
Path | None,
123+
typer.Option(
124+
help="Output path (.mcap or .parquet). If not provided, uses '<input>_with_odr.<ext>' in the same directory."
125+
),
126+
] = None,
127+
):
128+
r = omega_prime.Recording.from_file(input, validate=False, parse_map=False)
129+
r.map = omega_prime.MapOdr.from_file(odr)
130+
131+
if output is None:
132+
output = input.parent / f"{input.stem}_with_odr{input.suffix}"
133+
134+
if Path(output).suffix == ".parquet":
135+
r.to_parquet(output)
136+
else:
137+
r.to_mcap(output)
138+
139+
115140
def main():
116141
load_converters_into_cli(app)
117142
app()

tools/ros2_conversion/Dockerfile

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
ARG OMEGA_PRIME_VERSION=latest
2+
ARG PERCEPTION_INTERFACES_REPO=https://github.com/ika-rwth-aachen/perception_interfaces.git
3+
ARG PERCEPTION_INTERFACES_REF=
4+
5+
FROM osrf/ros:jazzy-desktop
6+
7+
ARG OMEGA_PRIME_VERSION
8+
ARG PERCEPTION_INTERFACES_REPO
9+
ARG PERCEPTION_INTERFACES_REF
10+
11+
ENV DEBIAN_FRONTEND=noninteractive \
12+
PYTHONUNBUFFERED=1 \
13+
ROS_DISTRO=jazzy
14+
15+
# System deps and ROS packages needed to build/use perception_msgs and utils
16+
RUN apt-get update && apt-get install -y --no-install-recommends \
17+
build-essential \
18+
cmake \
19+
git \
20+
python3-pip \
21+
python3-venv \
22+
python3-colcon-common-extensions \
23+
ros-jazzy-rosidl-default-generators \
24+
ros-jazzy-tf2-geometry-msgs \
25+
ros-jazzy-tf-transformations \
26+
ros-jazzy-rosbag2-py \
27+
ros-jazzy-rosbag2-storage-mcap \
28+
&& rm -rf /var/lib/apt/lists/*
29+
30+
# Workspace for ROS Python messages and utils
31+
WORKDIR /opt/ws
32+
RUN mkdir -p /opt/ws/src
33+
34+
# Fetch perception_interfaces from GitHub so the image can be built standalone.
35+
SHELL ["/bin/bash", "-c"]
36+
RUN git clone ${PERCEPTION_INTERFACES_REPO} /opt/ws/src/perception_interfaces && \
37+
if [ -n "${PERCEPTION_INTERFACES_REF}" ]; then \
38+
cd /opt/ws/src/perception_interfaces && \
39+
git checkout ${PERCEPTION_INTERFACES_REF}; \
40+
fi
41+
42+
# Build only the required packages
43+
RUN source /opt/ros/${ROS_DISTRO}/setup.bash && \
44+
colcon build \
45+
--merge-install \
46+
--symlink-install \
47+
--packages-up-to \
48+
perception_msgs \
49+
perception_msgs_utils \
50+
tf2_perception_msgs
51+
52+
# Python deps: omega-prime from PyPI inside isolated venv
53+
WORKDIR /opt
54+
RUN python3 -m venv /opt/venv
55+
ENV VIRTUAL_ENV=/opt/venv
56+
ENV PATH="/opt/venv/bin:${PATH}"
57+
RUN python -m pip install --upgrade pip && \
58+
python -m pip install --upgrade scipy pyyaml transforms3d && \
59+
if [ "${OMEGA_PRIME_VERSION}" = "latest" ]; then \
60+
python -m pip install omega-prime; \
61+
else \
62+
python -m pip install "omega-prime==${OMEGA_PRIME_VERSION}"; \
63+
fi
64+
65+
# Include the converter inside the image
66+
RUN mkdir -p /opt/ros2_conversion
67+
COPY tools/ros2_conversion/object_list_to_omega_prime.py /opt/ros2_conversion/object_list_to_omega_prime.py
68+
69+
# Convenience entrypoint to ensure ROS env is sourced
70+
ADD <<'EOS' /ros_entrypoint.sh
71+
#!/bin/bash
72+
set -e
73+
source /opt/ros/${ROS_DISTRO}/setup.bash
74+
if [ -f /opt/ws/install/setup.bash ]; then
75+
source /opt/ws/install/setup.bash
76+
fi
77+
exec "$@"
78+
EOS
79+
RUN chmod +x /ros_entrypoint.sh
80+
81+
ENTRYPOINT ["/ros_entrypoint.sh"]
82+
CMD ["python3", "/opt/ros2_conversion/object_list_to_omega_prime.py"]

tools/ros2_conversion/README.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# ROS 2 Bag -> omega-prime Docker Image
2+
3+
This image bundles ROS 2 Jazzy, its rosbag2 Python bindings, omega-prime (via PyPI), and builds perception_interfaces (messages + Python utils) from GitHub so you can export ObjectList topics to omega-prime MCAP using the built-in converter.
4+
5+
## Build Args
6+
- `OMEGA_PRIME_VERSION` (default `latest`): PyPI version to install; use `latest` for newest
7+
- `PERCEPTION_INTERFACES_REPO` (default GitHub repo)
8+
- `PERCEPTION_INTERFACES_REF` (optional): commit/branch/tag; if unset, uses the repo’s default branch
9+
10+
## Build
11+
```bash
12+
# Run from omega-prime root directory
13+
docker build -t ros2omegaprime \
14+
--build-arg OMEGA_PRIME_VERSION=latest \
15+
--build-arg PERCEPTION_INTERFACES_REF=<commit-or-branch> \
16+
-f tools/ros2_conversion/Dockerfile .
17+
```
18+
19+
## Run
20+
- Mount your bag directory to `/data` and an output directory to `/out`.
21+
- Set the topic via `OP_TOPIC` (ObjectList topic); the container runs the export automatically.
22+
23+
### Example:
24+
```bash
25+
docker run --rm -it \
26+
-e OP_TOPIC=</your/object_list_topic> \
27+
-v <path/to/bags>:/data:ro \
28+
-v </path/to/map.xodr>:/map/map.xodr:ro \
29+
-v "$PWD"/out:/out \
30+
ros2omegaprime
31+
```
32+
33+
## Notes
34+
- The image builds and installs `perception_interfaces` packages needed for Python APIs and messages (`perception_msgs`, `perception_msgs_utils`, `tf2_perception_msgs`).
35+
- The converter scans `/data` for rosbag2 directories containing a `metadata.yaml` and writes one omega-prime `.mcap` per bag into `/out` per default.
36+
- For large bags ensure sufficient RAM.
37+
38+
## Advanced
39+
- Env vars / CLI flags:
40+
- `OP_DATA` / `--data-dir` (default `/data`)
41+
- `OP_OUT` / `--output-dir` (default `/out`)
42+
- `OP_TOPIC` / `--topic` (required)
43+
- `OP_VALIDATE` / `--validate`
44+
- `--bag` to process explicit bag directories in addition to auto-discovery
45+
46+
## OpenDRIVE Map Integration
47+
48+
### During export (recommended)
49+
- Place your `.xodr` file under the mounted `/map/map.xodr`
50+
- The export routine embeds the map in each generated omega-prime `.mcap`.
51+
52+
### Notes
53+
- If `/map/map.xodr` does not exist, outputs won’t include a map.
54+
- Map parsing uses a default geometry sampling step size of 0.01 m.

0 commit comments

Comments
 (0)