Install¶
You need ROS 2 Jazzy, Kilted or Rolling. Everything else comes from apt; there is no need to build rosbag2 from source. That is deliberate: a source build costs forty minutes across 22 packages, and the only thing it buys is a service definition this project declares for itself.
Full install¶
Everything: the recorder, the CLI, the Python library and the browser UI.
sudo apt install ros-$ROS_DISTRO-rosbag2 ros-$ROS_DISTRO-rosbag2-storage-mcap
mkdir -p ~/ws/src && cd ~/ws/src
git clone --branch v0.2.0 https://github.com/juandanielsg/rosbag2_dynamic_recorder.git
cd ~/ws
rosdep install -r -y --from-paths src --ignore-src
colcon build --symlink-install
source install/setup.bash
Five packages, a couple of minutes. --branch v0.2.0 pins a release; every tag is a commit CI
has built and tested on Jazzy, Kilted and Rolling, so it is a known-good point on every distro at
once. Leave the flag off for main, which carries the same guarantee for its latest push but
moves. Then:
ros2 launch rosbag2_dynamic_recorder dynamic_recorder.launch.py \
uri:=/tmp/mybag topics:="['/scan','/odom']"
Launch arguments¶
dynamic_recorder.launch.py forwards these to the node:
Argument |
Default |
Meaning |
|---|---|---|
|
|
Output bag path. |
|
|
Storage plugin. |
|
|
Message serialization format. |
|
|
Topics to record at startup, as a YAML list. May be empty. |
|
|
Start with recording paused. |
|
|
Stamp messages on the node clock driven by |
|
|
Buffer in memory and write only on |
|
|
Writer cache in bytes. |
|
|
Writer cache bound in seconds; |
|
|
Split when a file reaches this many bytes; |
|
|
Split every this many seconds; |
|
(empty) |
Storage plugin preset. mcap: |
|
(empty) |
Storage plugin YAML, overlaid on the preset. |
|
|
Write subscription changes into the bag. |
|
|
Stop recording below this many bytes free on the bag filesystem; |
|
|
Same, as a percentage of the filesystem; |
|
|
Stop recording once the bag directory, across every split, exceeds this many bytes; |
|
|
Seconds between |
|
(empty) |
Optional YAML of extra parameters, e.g. profiles. |
The other node parameters (record_pause_events, record_low_disk_events,
record_bag_size_limit_events, record_split_events, storage_check_period,
status_publish_period, profile_names
and the profiles themselves)
are not launch arguments. Set them through params_file, or with -p name:=value when running the
node directly:
ros2 run rosbag2_dynamic_recorder dynamic_recorder --ros-args \
-p uri:=/tmp/mybag -p record_pause_events:=false
recorder_with_ui.launch.py takes the same list, plus:
Argument |
Default |
Meaning |
|---|---|---|
|
|
Port for the browser UI. |
|
|
Address the UI listens on. Loopback by default because the UI has no authentication; see the UI page. |
Client-only install¶
A machine that only drives a recorder running somewhere else (a fleet console, a laptop on the same
DDS domain, a supervisor host) does not need the recorder at all. dynrec and the CLI depend on
rclpy and two interface packages; neither depends on the C++ recorder, on rosbag2’s storage
plugins, or on MCAP.
sudo apt install ros-$ROS_DISTRO-rclpy ros-$ROS_DISTRO-rosbag2-interfaces
colcon build --packages-select rosbag2_dynamic_recorder_interfaces dynrec
Two small packages instead of five, and none of the storage stack. Add
rosbag2_dynamic_recorder_cli to that list if you want ros2 dynrec as well.
Why there is no pip install¶
dynrec is a Python package, so this is a fair thing to expect. It cannot work, for two reasons:
The interface bindings are generated, not pure Python.
dynrecimportsrosbag2_dynamic_recorder_interfaces, whose Python bindings are a C extension plus typesupport built byrosidlagainst one exact ROS distribution. There is no supported way to ship that as a wheel, so a PyPI release ofdynrecwould install a package that raisesImportErroron first use.The environment it targets is apt-managed. A stock ROS image has no
pipat all, and its interpreter is markedEXTERNALLY-MANAGEDunder PEP 668.
So the unit of installation is the colcon workspace, and the lightest honest version of it is the client-only build above.
ROS 2 distributions¶
One source tree builds on Jazzy, Kilted and Rolling; CI runs the full suite on each. Rolling
(rosbag2 0.34) is what the project is written against and behaves exactly as documented. Jazzy
(0.26) and Kilted (0.32) differ in three places, all consequences of what their rosbag2 lacks.
The recorder’s CMake finds each by probing the installed headers, and dynrec.services applies the
same rules on the client side, so nothing has to be configured:
Rolling |
Jazzy, Kilted |
|
|---|---|---|
|
stock |
field-identical copies in |
|
stock |
field-identical copy; |
|
supported |
refused at startup if non-zero; use |
The rule for a service is: the stock type where the installed definition is field-identical to the
0.34 one, the copy where it is not, so a client written for ros2 bag record drives the recorder
wherever that is possible. In Python, import the service classes from dynrec.services rather
than from rosbag2_interfaces.srv and they match the recorder built on the same machine.
ros2 service type <service> shows what a running recorder offers.
There are no per-distro branches. A distro-specific difference is a probe and an #if, kept
next to the code it guards, and a release is a tag on main. Pushing a tag runs the full matrix
on that commit and publishes a GitHub Release only if it passes, so every release page is a
commit proven on every distro.
Kilted reaches end of life in November 2026. Humble has not been tried; it is older than Jazzy and would need its own look.
Docker¶
A dev container is provided for working on the project, and for running the recorder against a
simulator without installing ROS on the host. It is the same environment CI tests in: an official
ros:<distro>-ros-base image plus the dependencies from our own package manifests, and it lives in
.devcontainer/ so VS Code offers to reopen the repository inside it. From the repository root:
docker compose --project-directory .devcontainer build # ROS_DISTRO=jazzy|kilted|rolling
docker compose --project-directory .devcontainer up -d
docker compose --project-directory .devcontainer exec rosbag2-dev bash -l
Notes:
The container publishes port 8088 for the browser UI, but you must launch the UI with
bind:=0.0.0.0. The default loopback bind is loopback inside the container, and Docker would have nothing to forward to.It does not use
network_mode: host. On Docker Desktop that is the WSL2 VM’s network, which the host OS cannot reach.Our packages live in
packages/, mounted atsrc/packagesin the workspace so a plaincolcon buildfinds them. Anything local, such as an upstream rosbag2 checkout to read, goes in.devcontainer/docker-compose.override.yml, which compose merges in and git ignores.Dependencies are installed into the image from the manifests. If you add a package or a
<depend>, rebuild the image, or runrosdep install -r -y --from-paths src --ignore-srcinside the container.