diff --git a/README.md b/README.md
index 8735b89b6..d6f1449f2 100644
--- a/README.md
+++ b/README.md
@@ -1,17 +1,33 @@
+ # DynoSAM: Dynamic Object Smoothing and Mapping
+
+ **A Stereo/RGB-D Visual Odometry pipeline for Dynamic SLAM.**
+
+ DynoSAM estimates camera poses, object motions/poses, as well as static background and temporal dynamic object maps. It provides **full-batch**, **sliding-window**, and **incremental** optimization procedures and is fully integrated with **ROS2**.
+
+ [](https://docs.ros.org/en/kilted/index.html)
+ [](./LICENSE)
+ [](https://arxiv.org/pdf/2501.11893)
+
+
-# DynoSAM: Dynamic Object Smoothing and Mapping for Dynamic SLAM
+
-DynoSAM is a Stereo/RGB-D Visual Odometry pipeline for Dynamic SLAM and estiamtes camera poses, object motions/poses as well as static background and temporal dynamic object maps.
+
+

+
DynoSAM running Parallel-Hybrid formulation in incremental optimisation mode on a indoor sequence recorded with an Intel RealSense. Playback is 2x speed.
+
-DynoSAM current provides full-batch, sliding-window and incremental optimisation procedures and is integrated with ROS2.
+
+

+
Example output running on the Oxford Multimotion Dataset (OMD, 'Swinging 4 Unconstrained'). This visualisation was generated using playback after full-batch optimisation.
+
-## Publication
+## 📚 Publications
The offical code used for our paper:
- [Jesse Morris](https://jessemorris.github.io/), Yiduo Wang, Mikolaj Kliniewski, Viorela Ila, [*DynoSAM: Open-Source Smoothing and Mapping Framework for Dynamic SLAM*](https://arxiv.org/pdf/2501.11893), Arxiv. Submitted Transactions on Robotics (T-RO) Visual SLAM Special Issue (2025).
@@ -44,7 +60,7 @@ We kindly ask to cite our papers if you find these works useful:
}
```
-## Related Publications
+### Related Publications
DynoSAM was build as a culmination of several works:
@@ -52,242 +68,234 @@ DynoSAM was build as a culmination of several works:
- J. Zhang, M. Henein, R. Mahony, V. Ila [**VDO-SLAM: A Visual Dynamic Object-aware SLAM System**](https://arxiv.org/abs/2005.11052), ArXiv
- M. Henein, J. Zhang, R. Mahony, V. Ila. [**Dynamic SLAM: The Need for Speed**](https://ieeexplore.ieee.org/abstract/document/9196895).2020 IEEE International Conference on Robotics and Automation (ICRA). IEEE, 2020.
-## Demo
-
+## 📖 Overview
-
-

-
DynoSAM running Parallel-Hybrid formulation in incremental optimisation mode on a indoor sequence recorded with an Intel RealSense. Playback is 2x speed.
-
+### Key Features (Nov 2025 Update)
+* 🚀 **CUDA Integration:** Front-end acceleration.
+* 🧠 **TensorRT:** Integrated object detection and tracking.
+* ⚡ **Sparse Tracking:** Options to move away from dense optical flow.
+* 📦 **Modular:** System broken into packages for faster compile times.
+* 🐳 **Docker:** Updated image with new dependencies.
-
-

-
Example output running on the Oxford Multimotion Dataset (OMD, 'Swinging 4 Unconstrained'). This visualisation was generated using playback after full-batch optimisation.
-
-
-
-## Feature Updates
-November 2025
-- CUDA integration for front-end acceleration
-- TensorRT for integrated object detection and tracking
-- Options to move to sparse tracking for static and dynamic objects. This moves away from the need to calculate dense optical flow.
-- Custom visualisation options per module.
-- System is broken out into more packages for a cleaner interface and faster compile time.
-- Updated docker image with new dependancies.
-- Additional support for IMU and stereo SLAM systems (although still under active development)
-
-# 1. Installtion
-
-Tested on Ubuntu 20.04
-
-
-## Prerequisites
-- [ROS2](https://docs.ros.org/en/humble/Installation.html)
- > NOTE: this links to the Humble distribution which used during development. Other distro's will probably work.
-- [GTSAM](https://github.com/borglab/gtsam) >= 4.1
-- [OpenCV](https://github.com/opencv/opencv) >= 3.4
-- [OpenGV](https://github.com/MIT-SPARK/opengv)
-- [Glog](http://rpg.ifi.uzh.ch/docs/glog.html), [Gflags](https://gflags.github.io/gflags/)
-- [Gtest](https://github.com/google/googletest/blob/master/googletest/docs/primer.md) (installed automagically)
-- [config_utilities](https://github.com/MIT-SPARK/config_utilities)
-- [dynamic_slam_interfaces](https://github.com/ACFR-RPG/dynamic_slam_interfaces) (Required by default. Can be optionally made not a requirement. See Insallation instructions below)
-
-External dependancies (for visualization) not required for compilation.
-- [rviz_dynamic_slam_plugins](https://github.com/ACFR-RPG/rviz_dynamic_slam_plugins) (Plugin to display custom `dynamic_slam_interfaces` messages which are advertised by default.)
-
-
-GPU acceleration:
-- [TensorRT](https://github.com/NVIDIA/TensorRT)
-- [CUDA](https://docs.nvidia.com/cuda/cuda-runtime-api/index.html)
+## Documentation
+We auto generate [Doxygen code docs](https://acfr-rpg.github.io/DynOSAM/doxygen/) for all classes in DynoSAM. The code docs are up-to-date with the _main_ branch.
-are now supported with the new Docker image.
-This provides support for TensorRT accellerated object-instance segmentation (which is now part of the DynoSAM pipeline) and CUDA acceleration in the front-end.
-Backwards compatability (i.e no CUDA support) is not currently a priority.
-> NOTE: documentation coming soon...
+# 1. ⚙️ Installation
+We provide a detailed installation guide including dependencies and Docker support.
+See detailed instructions here: [Insallation instructions](./docs/media/INSTALL.md)
-## Installation Instructions
-DynoSAM is currently built within the ROS2 infrastructure (if there is enough interest I will split out each component into ROS and non-ROS modules.)
+# 2. 🚀 Running DynoSAM
-We provide a development [Dockerfile](./docker/Dockerfile) that will install all dependancies but expects DynoSAM to be cloned locally. The associated [container creation](./docker/create_container.sh) will then mount the local DynoSAM folder into the container along with local results/dataset folders.
+## 2.1 Paramters
+DynoSAM is configured using a combination of **YAML files** (pipeline, frontend, datasets) and **GFLAGS** (overridable command-line parameters). ROS parameters are used only for input file paths.
+All `.yaml` and `.flags` files must be placed in a single parameter folder which defines the `params_path`.
+```
+params/
+ FrontendParams.yaml
+ PipelineParams.yaml
+ [DatasetParams.yaml]
+ [CameraParams.yaml]
+ *.flags
+```
+- YAML files are loaded using config_utilities.
+- GFlags provide run-time reconfiguration (important for automated experiments).
+- ROS parameters are used sparingly (mainly for file paths).
-The general ROS2 build procedure holds as all relevant subfolders in DynoSAM are built as packages.
-More detailed instructions are found here: [Insallation instructions](./docs/media/INSTALL.md)
+> NOTE: Note: Additional GFlags cannot be passed through ros2 launch.
+To override GFlags pass them directly with ros2 run, or modify the flag files inside the params folder.
+To print active parameters:
```
-# Finally compile
-cd ros_ws && colcon build
-
-# Refresh workspace
-source ~/ros_ws/install/setup.bash
+ros2 run dynosam_utils eval_launch.py --show_dyno_args=true
```
-
-`dynamic_slam_interfaces` is a require dependacy by default. This package is used to include custom messages that represet the state of each dynamic object per frame and is used by the ROS publishers.
-
-To disable this dependancy compile the code as
+To see all gflag options run:
```
-colcon build --cmake-args -DENABLE_DYNAMIC_SLAM_INTERFACES=OFF
+ros2 run dynosam_ros dynosam_node --help
```
-By default `ENABLE_DYNAMIC_SLAM_INTERFACES=ON` in the [CMakeLists.txt](./dynosam_ros/CMakeLists.txt). This CMake option will additionally change the _visualisation_ (and the output topics) used by DynoSAM. See the [ROS Visualisation](#ros-visualisation) section below.
+## 2.2 Quick Start (Launch File)
+Launch the full pipeline with ROS2:
+```
+ros2 launch dynosam_ros dyno_sam_launch.py \
+ params_path:= \
+ dataset_path:= \
+ v:= \
+ --data_provider_type= \
+ --output_path=
+```
-# 2. Usage
+The launch file:
+- Loads all `.flags` files in the parameter folder,
+- Applies dataset provider selection via `--data_provider_type`,
+- Logs outputs to `--output_path` (must exist beforehand).
-## Documentation
-We auto generate [Doxygen code docs](https://acfr-rpg.github.io/DynOSAM/doxygen/) for all classes in DynoSAM. The code docs are up-to-date with the _main_ branch.
+## 2.3 Experiment & Evaluation Launcher
-## Running and Configuration
+For fine‑grained control and automated experiments, use:
-DynoSAM uses a combination of yaml files and GFLAGS (these are being simplified but GFLAGS allows an easier way to programatically set variables over cmd-line) to configure the system. ROS params are used sparingly and are only used to set input file paths.
+```
+ros2 run dynosam_utils eval_launch.py \
+ --dataset_path \
+ --params_path \
+ --output_path \
+ --name \
+ --run_pipeline \
+ --run_analysis \
+
+```
-### Running with Launch files
-All .yaml and .flag files should be placed in the same params folder, which is specified by the command line.
-To specify the dataset loader, the GFLAG `--data_provider_type` should be set (see [pipeline.flags](./dynosam/params/pipeline.flags)). Eventually, this will also include the option to take data live from a sensor. For data providers that are specific to each dataset used for evaluation, a dataset path must also be set.
+This script:
+- Automates running the pipeline and evaluations,
+- Forwards all extra CLI arguments to DynoSAM (allowing any GFLAG override),
+- Creates result folders `output_path/name/` automatically.
-DynoSAM will also log all output configuration to an output-folder specified by `--output_path` (see [pipeline.flags](./dynosam/params/pipeline.flags)).
-__Data will only be logged if this folder exists.__
+Example:
-The DynoSAM pipeline can be launched via launch file:
```
-ros2 launch dynosam_ros dyno_sam_launch.py params_path:= dataset_path:=<> v:=<>
+ros2 run dynosam_utils eval_launch.py \
+ --output_path=/tmp/results \
+ --name=test \
+ --run_pipeline \
+ --data_provider_type=2
```
-The launch file will load all the GFLAG's from all .flag files found in the params folder.
-### Running with complex input
-For evaluation and more refined control over the input to the system we also provide an evaluation launch script and can be run as:
-```
-ros2 run dynosam_utils eval_launch.py
- --dataset_path
- --params_path
- --launch_file
- --output_path
- --name
- --run_pipeline
- --run_analysis
- *args...
+## 2.4 Programmatic Execution (Python)
-```
-This script automated the process of running the evaluation suite (ie. `--run_analysis_`) and set all rosparams/re-direct input and output paths (e.g. `--output_path`, `name`, etc...).
+All command‑line behaviour can be replicated in Python.
+See: [run_experiments_tro.py](./dynosam_utils/src/run_experiments_tro.py) for examples.
-In addition to these arguments, this script takes all additional cmd-line arguments and parses them to the DynoSAM node, allowing any GFLAGS to be overwritten directly by specifying them in the commandline. e.g the dataset provider type can be specified as:
-```
-ros2 run dynosam_utils eval_launch.py --output_path=/path/to/results --name test --run_pipeline --data_provider_type=2
-```
-This script will also construct the corresponding output folder (e.g. ouput_path/name) and make it, if it does not exist. In the aboce example, the program will make the folder _'/path/to/results/test/'_ and deposit all output logs in that folder.
-> NOTE: for all evaluations and metrics, this script was used to run the program.
+# 3. 📂 Datasets
-### Running programtically
-All the cmdline functionality can be replicated programtically using python in order to run experiments and evaluations.
-See [run_experiments_tro.py](./dynosam_utils/src/run_experiments_tro.py) for examples.
+## 3.1 Pre-processing Image data
+DynoSAM requires input image data in the form:
+- RGB
+- Depth/Stereo
+- Dense Optical Flow
+- Dense Semantic Instance mask
+Each image is expected in the following form:
+- __rgb__ image is expected to be a valid 8bit image (1, 3 and 4 channel images are accepted).
+- __depth__ must be a _CV_64F_ image where the value of each pixel represents the _metric depth_.
+- __mask__ must be a CV_32SC1 where the static background is of value $0$ and all other objects are lablled with a tracking label $j$. $j$ is assumed to be globally consistent and is used to map the tracked object $j$ to the ground truth.
+- __flow__ must be a CV_32FC2 representing a standard optical-flow image representation.
-## Running different backends
-Most of the research here is associated with different backend formulations.
-To run the different backends set `--backend_updater_enum`
+For dense optical flow (ie. pre Novemeber 2025) we use [RAFT](https://link.springer.com/chapter/10.1007/978-3-030-58536-5_24). Currently this pre-processing code is not available.
-> NOTE: this enum value is mapped to the enum [RGBDFormulationType](./dynosam/include/dynosam/backend/RGBDBackendDefinitions.hpp)
+For instance segmentation we use [YOLOv8](https://docs.ultralytics.com/models/yolov8/) for both image pre-processing and online processing. Both Python and C++ (using TensorRT acceleration) models can be found in the [dynosam_nn](./dynosam_nn/) package. See the [REAMDE](./dynosam_nn/README.md) for more details.
-- WCME (`backend_updater_enum=0`) and WCPE(`backend_updater_enum=1`) are from TRO-2025, ICRA 2024 and previous works
-- HYBRID (`backend_updater_enum=2`) and PARALLEL_HYBRID (`backend_updater_enum=3`) are from RA-L 2025
+- If `prefer_provided_optical_flow: true` (YAML), the pipeline expects a dense flow image. Otherwise, it falls back to sparse KLT.
+- If `prefer_provided_object_detection: true` (YAML) , an instance mask must be provided. If false, masks are generated online via.
-All others are internal/experimental.
-## Tests
-We use [gtest](https://github.com/google/googletest) for unit testing. This is installed automagically. When building with ROS, all tests will go into the install folder.
+## 3.2 Running with pre-processed data
-To run the unit tests: build the code, navigate inside the `install` folder and run the tests. Both `dynosam` and `dynosam_ros` packages come with unit tests.
+DynoSAM provides dataset loaders that parse pre-processed images (ie. depth, optical flow, masks), and ground truth into a unified format.
-We provide a useful script to make running tests easier. Run
-```
-ros2 run dynosam_ros run_dynosam_gtest.py
+All official datasets are hosted at the [ACFR-RPG Datasets page](https://data.acfr.usyd.edu.au/rpg/).
+To download a dataset, create a directory for the relevant dataset and, within the directory, run the command
+```bash
+wget -m -np -nH --cut-dirs=4 -R "index.html*" https://data.acfr.usyd.edu.au/rpg/dynosam/[Dataset]/[Subset]
```
-from anywhere on the system to run tests. The unit tests for a particular package (ie. `dynosam` and `dynosam_ros`) can be specified using the `--package` argumement. This script forwards all arguments to the test executable so that GFLAGS can still be used e.g.
+For example, for the `kitti` dataset with subset `0004`, create and enter the directory `kitti-0004` download all files:
```
-run dynosam_ros run_dynosam_gtest.py --package=dynosam_ros --gtest_filter=TestConcepts*
+wget -m -np -nH --cut-dirs=4 -R "index.html*" https://data.acfr.usyd.edu.au/rpg/dynosam/kitti/0004
```
-## Datasets
+> NOTE: when developing using docker, download the sequences into the data folder mounted into the docker container so it may be accessed by the program.
-We provide a number of data providers which process datasets into the input format specified by DynoSAM which includes input images for the pipeline and ground truth data for evaluation.
+The following datasets are officially supported:
+| Dataset | Dataset ID | Notes |
+|--------|--------------|-------|
+| KITTI Tracking | 0 | Uses modified version with GT motion, flow, masks. |
+| Virtual KITTI 2 | 1 | Raw dataset supported directly. |
+| Cluster-SLAM (CARLA) | 2 | Raw dataset available; we recommend our processed version. |
+| Oxford Multimotion (OMD) | 3 | Modified 2024 version available. |
+| TartanAir Shibuya | 5 | Preprocessed version supported. |
+| VIODE | 6 | IMU-enabled sequences supported. |
-The provided dataset loaders are written to parse the datasets as provided and only
-`--data_provider_type` and `--dataset_path` must be set to load the data.
+To run a specific dataset only two GFLAGS are required:
+```
+--dataset_path
+--data_provider_type
+```
+where `--dataset_path` points to the location of the dataset and `--data_provider_type` should be set to Dataset ID.
-All datasets (including pre-processed images) can be found at the [ACFR-RPG Datasets page](https://data.acfr.usyd.edu.au/rpg/).
-To download the datasets use the [`curl`](https://www.geeksforgeeks.org/linux-unix/curl-command-in-linux-with-examples/) command.
+> NOTE: when using ground truth for evaluation it is important to ensure that `prefer_provided_object_detection: false` so that the pre-processing object masks are used. This ensures that tracking label $j$ used within the pipeline aligns with the ground truth label.
+## 3.3 Online Data
+DynoSAM can also run from data provided by ROS.
-### i. KITTI Tracking Dataset
-We use a modified version of the KITTI tracking dataset which includes ground truth motion data, as well dense optical-flow, depth and segmentation masks.
-The required dataset loader can be specified by setting `--data_provider_type=0`
+To run with online data (e.g. from raw sensor data) set the ROS parameter:
+```
+online:=True
+```
-### ii. Oxford Multimotion Dataset (OMD)
-Raw data can be downloaded from the [project page](https://robotic-esp.com/datasets/omd/).
-For our 2024 T-RO paper we used a modified version of the dataset which can be downloaded from the above link.
+DynoSAM can run in two input modes which can be set using the ROS parameter `input_image_mode`:
+- `ALL` (`input_image_mode=0`)
+- `RGBD` (`input_image_mode=1`).
-The required dataset loader can be specified by setting `--data_provider_type=3`
+When in `ALL` mode, all image-preprocessing is done upstream and five inputs are required:
+By default this node subscribes to five topics:
+ - `dataprovider/image/camera_info` (sensor_msgs.msg.CameraInfo)
+ - `dataprovider/image/rgb` (sensor_msgs.msg.Image)
+ - `dataprovider/image/depth` (sensor_msgs.msg.Image)
+ - `dataprovider/image/mask` (sensor_msgs.msg.Image)
+ - `dataprovider/image/flow` (sensor_msgs.msg.Image)
+In `RGBD` mode, only camera intrinsics, rgb and depth images are required and all other image processing (including object detection) is done as part of the pipeline.
-### iii. Cluster Dataset
-The [raw dataset](https://huangjh-pub.github.io/page/clusterslam-dataset/) can be downloaded for the the CARLA-* sequences, although we recommend using our provided data.
-The required dataset loader can be specified by setting `--data_provider_type=2`
+### 3.2.1 Running using RBG-D camera
-### iv. Virtual KITTI 2
-Access [raw dataset](https://europe.naverlabs.com/research/computer-vision/proxy-virtual-worlds-vkitti-2/) and extract in a folder. No pre-processing is needed on this dataset and the raw data can be parsed by DynoSAM directly.
+To run from data provided by an RGB-D camera use
+```
+ros2 launch dynosam_ros dyno_sam_online_rgbd_launch.py
+```
+and remap the topics accordingly.
-The required dataset loader can be specified by setting `--data_provider_type=1`
-### v. TartanAir Shibuya
-The required dataset loader can be specified by setting `--data_provider_type=5`
+# 4. 🕹️ System Configuration
+## 4.1 Backend Setup
-### vi. VIODE
-The required dataset loader can be specified by setting `--data_provider_type=6`
+Running Different Backends
-### Online Dataprovider
-An online data-provider can be specified using the ROS arg `online:=True`.
+DynoSAM supports multiple backend formulations. Set the `backend_updater_enum` flag to switch between them.
-By default this node subscribes to five topics:
- - `dataprovider/image/camera_info` (sensor_msgs.msg.CameraInfo)
- - `dataprovider/image/rgb` (sensor_msgs.msg.Image)
- - `dataprovider/image/depth` (sensor_msgs.msg.Image)
- - `dataprovider/image/mask` (sensor_msgs.msg.Image)
- - `dataprovider/image/flow` (sensor_msgs.msg.Image)
+| Enum | Formulation | Paper | Description |
+| :---: | :--- | :--- | :--- |
+| **0** | `WCME` | TRO 2025 | World-Centric Motion Estimator |
+| **1** | `WCPE` | TRO 2025 | World-Centric Pose Estimator |
+| **2** | `HYBRID` | RA-L 2025 | Hybrid formulation |
+| **3** | `PARALLEL_HYBRID` | RA-L 2025 | Parallel Hybrid (Recommended for speed) |
-where each topic represents one of the expected input images in the following form:
-The __rgb__ image is expected to be a valid 8bit image (1, 3 and 4 channel images are accepted).
-The __depth__ must be a _CV_64F_ image where the value of each pixel represents the _metric depth_.
-The __mask__ must be a CV_32SC1 where the static background is of value 0 and all other objects are lablled with a tracking label $j$.
-The __flow__ must be a CV_32FC2 representing a standard optical-flow image representation.
+Many factor-graph related paramters exist in the `backend.flags` file.
+## 4.2 Instance Segmentation
+DynoSAM uses YOLOv8 for object detection. The model is integrated into the front-end and accelerated using CUDA and TensorRT.
-We also provide a launch file specified for online usage:
+To run the pipeline with online segmentation and object tracking set
```
-ros2 launch dynosam_ros dyno_sam_online_launch.py
+prefer_provided_object_detection: true
```
-> NOTE: see the launch file for example topic remapping
-
-Prior to September 2025 DynoSAM relied on pre-computing the flow and segmentation mask data offline.
-New features have been added to allow the segementation and feature tracking to be done internally, with specific caviats; this means that only RGB and Depth images must be provided, replicating a proper RGB-D pipeline.
+See the [dynosam_nn README.md](./dynosam_nn//README.md) for more detail on the models used and how to export the weights.
-Specific launch files for this are coming soon...
+> NOTE: the provided datasets contain pre-computed object masks with tracking labels $j$ that align with the ground truth. Use `prefer_provided_object_detection: false`.
-
-### IMU integration
+## 4.3 IMU integration
We additionally support IMU integration using the `PreintegrationFactor` from GTSAM in the backend. However, this has only been tested on VIODE.
-
-## ROS Visualisation
+## 4.4 ROS Visualisation
All 3D visualisation in DynoSAM is done using RVIZ. Camera pose and point clouds are vizualised using standard ROS messages. Visualising the objects is more complex and we provide two different ways to do this which can be controlled at compile time using the cmake flag `-DENABLE_DYNAMIC_SLAM_INTERFACES=ON/OFF`
1. (`ON`, now default) Usage of the custom `dynamic_slam_interfaces::msg::ObjectOdometry` (in [dynamic_slam_interfaces](https://github.com/ACFR-RPG/dynamic_slam_interfaces)) to publish to current state of the each object per frame. Our custom RVIZ plugin [rviz_dynamic_slam_plugins](https://github.com/ACFR-RPG/rviz_dynamic_slam_plugins) can be used to visualize this message type. The object id, current pose, velocity, path etc... will be shown for each object. See this [README.md](./dynosam_ros/include/dynosam_ros/displays/dynamic_slam_displays/README.md) for more detail.
@@ -295,130 +303,53 @@ All 3D visualisation in DynoSAM is done using RVIZ. Camera pose and point clouds
> NOTE: the publishing of object states is meant primilarily for __visualisation__ purposes and not for recording data used for evaluation. This is done using csv files in a different submodule: see [Evaluation](#4-evaluation).
+## 4.5 Tests
+We use [gtest](https://github.com/google/googletest) for unit testing. This is installed automagically. When building with ROS, all tests will go into the install folder.
-# 2. Image Pre-processing
-DynoSAM requires input image data in the form:
-- RGB
-- Depth/Stereo
-- Dense Optical Flow
-- Dense Semantic Instance mask
-
-and can usually be obtained by pre-processing the input RGB image.
-
-As of November 2025 we no longer fully rely on dense optical flow and instead use sparse tracking for static features and optionally for dynamic features.
-
-A dense optical flow image can be provided in the input `ImageContainer`. To use a provided optical flow image, set `prefer_provided_optical_flow: true`. Usually we use RAFT to generate dense optical flow. If this argument is true but no image is provided, it falls back to sparse KLT tracking.
-
-A similar logic follows with the dense semantic instance mask where each pixel value $>0$ represents the tracked object index $j$ and background pixels are labelled $0$.
-If `prefer_provided_object_detection: true` then this data is expected to be provided in the input `ImageContainer`. For example, this is how we load data from all datasets.
-
-As of November 2025 we can also generate tracked objects masks _online_ using TensorRT accelerated inference. This is the default option if `prefer_provided_object_detection: false`.
-See the [dynosam_nn](./dynosam_nn/) package for details.
-
-
-# 3. Parameters
-
-We use a two different sets of parameters for our pipeline:
-
-- YAML files: contain params for the Pipeline and Frontend and some dataset related parameters. We use the wonderful [config_utilies](https://github.com/MIT-SPARK/config_utilities) library to load these files.
-- [gflag](https://gflags.github.io/gflags/) for all other params which are found in .flag files.
-
-
-Structurally DynoSAM expects all params files (.yaml and .flag) to be found in the same folder. By default we use the param files in this repo but you can specify any path as long as the folder follows the required structure:
+We provide a useful script to make running tests easier:
```
-params_folder:
- - FrontendParams.yaml
- - PipelineParams.yaml
- [- DatasetParams.yaml ]
- [- CameraParams.yaml ]
- - *.flags
+ros2 run dynosam_ros run_dynosam_gtest.py
```
+Running unit tests for a specific package (ie. `dynosam` and `dynosam_ros`) can be specified using the `--package` argumement.
-The [dynosam launch file](./dynosam_ros//launch/dyno_sam_launch.py) has some smarts in it so that it will search the given the folder (`params path`) for any file with the ".flags" suffix and load the specified flags.
-
-From a design persepctive, we use gflags in addition to yaml files becuase we are able to programmatically change these params as additional arguments to the DynoSAM program. This is used extensively for automated experiments where we re-configure the pipeline for different runs.
-
-To see all params (yaml and gflags) run the eval launch script with the gflag `--show_dyno_args=true`.
-
-> NOTE: We have to manually print the params and there are some minor issues printing all params (e.g. we currently cannot print the camera params with the _config_utilities_ library due to the way we currently load the variables!)
-
-To see all gflag options run:
+This script forwards all arguments to the test executable. This allows GFLAGS to be specified directly via the CLI.
```
-ros2 run dynosam_ros dynosam_node --help
+run dynosam_ros run_dynosam_gtest.py --package=dynosam_ros --gtest_filter=TestConcepts*
```
-> NOTE: there may be some minor discrepeancies between the parameters as some values that are in the config files are then overwritten by gflag values so we can configure them easily.
-
-During experiments we programatically change gflag by passing them executable node as cmdline arguments. This can be done by
-
-1. Passing arguments to the `unknown_args` arg of [runner.run](./dynosam_utils/dynosam_utils/evaluation/runner.py)
-2. Passing them directly to the `ros2 run` as the additional args.
-
-Gflags cannot be changed when using `ros2 launch` as this command does not allow additional arguments (ie. non-declared ROS arguments) to be passed through. In this case, the gflags can be manually changed in the relevant flag file, or a parameter folder can be specified with your custom settings in it.
-
-
-## System Parameters
-
-
-## Camera Parameters
-
-Camera Parameters (ie. intrinsics) two possible sources
- - Provided by the loaded data-provider
- - Loaded from [CameraParams.yaml](./dynosam//params/CameraParams.yaml)
-
-If the param `prefer_data_provider_camera_params` is set to True, the parameters provided by the data-loader (if available) will be used. Otherwise, the paramters from the yaml file will be used.
-This allows each data-loader to specfy/loaded the camera params from the dataset itself, without having to additionally specify the intrinsics in another file.
-# 4. Evaluation
+# 5. 📊 Output & Metrics
-## Replicating results from papers
-This code base contains implementations for many papers (as noted in Related Publications).
-The `main` or `devel` branches should be able to run each method as described in the papers, however their may be discrepencies as new updates are added to different parts of the system.
-
-Additionally, I try to maintain backwards compatability as new code gets pushed to main but cannot ensure this.
+The eval script should be used when generating results
+```
+ros2 run dynosam_utils eval_launch.py
+ --output_path=/tmp/results \
+ --name=test \
+ --run_pipeline \
+ --run_analysis
+```
-See different package releases associated with a paper.
+## 5.1 Reproducing Paper Results
-When running evaluations for each paper there usually is an associated python script that includes all the experiments.
+Scripts for reproducing experiments:
- [TRO 2025 experiments](./dynosam_utils/src/run_experiments_tro.py)
- [RAL 2025 experiments](./dynosam_utils/src/run_experiments_ecmr.py)
+While we attempt to maintain compatibility, minor differences may arise due to ongoing development.
-## Output logs
-When the DynoSAM pipeline is run it will output log files in the given output directory. The log files record the output of the state-estimation as well as various interal data.
-As mentioned, the output directory is specified by `--output_path=/path/to/results` (and may be further specified using `--name` when using `eval_launch.py`).
-
-The log files include (but are not limited to) files in the form
-
-- __*camera_pose_log.csv__
-- __*object_motion_log.csv__
-- __*object_pose_log.csv__
-
-The presence of (at least ) these three files in a folder define a "dynosam results folder", from which the provided evaluation suite can process and produce results and metrics.
-
-Some additional statistics files will also be logged. The * specifies a module prefix (e.g. frontend, rgbd_world_backend) that is used to specify where the logged state-estimates have come from.
-This is used to separate out the front-end estimation from the back-end. Each back-end formulation (ie. Motion Estimator and Pose Estimator) provides its own module name, which is used as the module prefix. An additional suffix can be added to the module name through the `--updater_suffix` gflag.
-
-Camera Pose log files are in the form:
-```
-frame id, tx, ty, tz, qx, qy, qz, qw, gt_tx, gt_ty, gt_tz, gt_qx, gt_qy, gt_qz, gt_qw
-```
-where the transformation `T_world_camera` is reported in translation and quaternion form.
-
-Object Pose/Motion log files log in the form:
-```
-frame id, object id, tx, ty, tz, qx, qy, qz, qw, gt_tx, gt_ty, gt_tz, gt_qx, gt_qy, gt_qz, gt_qw
-```
-In the case of motion logs, the frame id is the __to__ motion, such that the motion logged takes the object frame __from frame id - 1 to frame id__.
+## 5.2 Output Logs
+When `eval_launch.py` is run with `--run_analysis`, DynoSAM generates a "results folder" containing:
-## Results and Metrics
+- __Logs__: `camera_pose_log.csv`, `object_motion_log.csv`, etc.
+- __Plots__: `*results.pdf` (ATE, RPE, Trajectory plots).
+- __Tables__: `result_tables.pdf` (Summary statistics).
-The DynoSAM framework comes with automated [evaluation tools](./dynosam_utils/dynosam_utils//evaluation/) that run as part of the pipeline. This can be run with the`--run_analysis` argument when using `eval_launch.py`. The evaluation module will look for a valid dynosam results folder in the provided output directory and will run evaluations _per prefix found_ in the folder. This enables one folder to contain multiple sets of log files, each defined with a different prefix, i.e for one dataset, multiple configurations of the back-end can be run and logged to the same folder, as long as the prefix is different.
+at the path `/tmp/results/test`.
-From the logged files the evaluation suite will produce ATE and RPE results for visual odometry and AME, RME and RPE results for objects. See our TRO paper for more details on these metrics. Per-frame numerical errors are logged in __*results.pdf__ with error plots for each metric as well as plots of the object and camera trajectory additionally included.
+The * specifies a module prefix (e.g. frontend, wcme) that is used to specify where the logged state-estimates have come from.
+An additional suffix can be added to the module name through the `--updater_suffix` flag.
-> Two examples of our automated result generation are shown below
 |
@@ -426,82 +357,47 @@ From the logged files the evaluation suite will produce ATE and RPE results for
-A full summary of numerical results are also generated in __result_tables.pdf__. This file includes all metrics for all sets of dynosam results found. The per-object error (for each metric) is recorded, as is the mean error over all objects for a particular metric.
-
-The `DatasetEvaluator` does most of the heavy lifting and can be found in the [evaluation_lib.py](./dynosam_utils/dynosam_utils/evaluation/evaluation_lib.py) module. As shown in some of the other evaluation scripts (e.g [sliding_window_vs_batch_error_plot.py](./dynosam_utils/src/sliding_window_vs_batch_error_plot.py)) we can use the tools and classes defined to do more complex evaluation and visualisation.
-
-# 5. Program Structure and Modules
-
-
-
-

-
+
-We follow a similar module/pipeline structure as developed in [Kimera](https://github.com/MIT-SPARK/Kimera-VIO) and I would like to thank the authors for their wondeful open-source code that has given me much inspiration.
+# 6. 🧠 System Architecture
+We follow a modular pipeline structure inspired by Kimera-VIO:
-Where possible, I have streamlined their 'Modular Pipelining'; each high-level component (effectively the data-provider, front-end, back-end and visualiser) is defined by a [Pipeline](./dynosam/include/dynosam/pipeline/PipelineBase.hpp) and a [Module](./dynosam/include/dynosam/common/ModuleBase.hpp).
+
+where a Pipeline/Module architecture connected by thread-safe queues
-A Pipeline is a connecting class, derived from `PipelineBase`, is responsible for controlling the movement and transformation of data too and from modules. In most cases, this involves some data-packet that is produced as the output of another module and sending through the pipeline. This is achieved by connecting a [`ThreadSafeQueue`](./dynosam/include/dynosam/pipeline/ThreadSafeQueue.hpp) to the input and output of a pipeline. Each pipeline has an associated module which actually does the processing on the input data and produced some output data, which is then sent along the connected ouput queue to any connecting modules.
-
-
-## References to our TRO Paper
-How many times have you read a research paper and then gone to the code only to have absolutely no idea how the implementation connects to the theory/discussion?
-
-Too many in my experience.
-
-### Notation
-
-The notation from our paper has the following implementation:
-
-- $k$ (used as discrete time-step) is denoted with `FrameId` in the code.
- > NOTE: $k$ is treated as a 'time-step' but in the code this is assocaited with a frame number (an integer value from 0... N). We also have assocaited `Timestamp` information associated with each frame but this is not actually used.
-- $i$ (used as unique tracking id) is denoted as `TrackletId` in the code.
-- $j$ (used as unique object id) is denoted as `ObjectId` in the code.
-
-All transform types (e.g. poses $X$ and motions $H$) are implemented using `gtsam::Pose3` and measurement types (e.g. 3D landmarks/points and 2D Features) are `Eigen::Vectors` as implemented through gtsam.
-
-> NOTE: I was very inconsistent with my variable naming through this code, mainly becuase I did not find a good way to deal with the funky three-indexed notation very well. I am fixing this as I go ;)
-
-### Modules
-The front-end and back-ends are implemented in associated subfolders.
-
-
-__Front-end__
-
-- Main feature tracker defined in [FeatureTracker.hpp](./dynosam/include/dynosam/frontend/vision/FeatureTracker.hpp) although the implementation is spread across a few impl and .cc files.
-
-- Motion Estimation modules (PnP + RANSAC and the two refinement steps) are all defined in [MotionSolver.hpp](./dynosam/include/dynosam/frontend/vision/MotionSolver.hpp).
-
-__Back-end__
-
-As per our key contributions, our back-end is structured to facilitate new implementations for Dynamic SLAM systems. This is achieved through two key classes
-
-- [`Formulation`](./dynosam/include/dynosam/backend/Formulation.hpp) base class which acts as the foundational structure for building and managin factor graphs in Dynamic SLAM.
- - It is reponsible for Constructs factor graphs with initial values.
- - Manages the high-level bookkeeping for when points and objects are added.
- - Integrates new observations into the graph and updates it accordingly.
- - It creates an `Acccessor` object which is used externally to extract and interact with the internal representation.
-
-- [`Accessor`](./dynosam/include/dynosam/backend/Accessor.hpp) defines the interface between the derived `Formulation` and the backend modules and facilitates the extraction and conversion of variables into a format that aligns with backend expectations. This format is specified in our paper as $\mathcal{O}_k$.
-
-Each formulation will need to derive from `Formulation` and define their own `Accessor`. The formulations discussed in our various works are implemented as
- - [`WorldMotionFormulation`](./dynosam/include/dynosam/backend/rgbd/WorldMotionEstimator.hpp)
- - [`WorldPoseFormulation`](./dynosam/include/dynosam/backend/rgbd/WorldPoseEstimator.hpp)
- - [`Hybrid`](./dynosam/include/dynosam/backend/rgbd/HybridEstimator.hpp)
- - [`Parallel-Hybrid`](./dynosam/include/dynosam/backend/ParallelHybridBackendModule.hpp)
+```mermaid
+graph LR
+ A[Input Data] --> B(Pipeline Wrapper)
+ B --> C{ThreadSafe Queue}
+ C --> D[Module Processing]
+ D --> E{Output Queue}
+ E --> F[Next Module]
+```
+is employed to connect data-provider, frontend, backend and visualizer modules.
+### Code-to-Theory Mapping
+To help researchers connect the theory from our papers to the implementation, here is a mapping of our mathematical notation to the corresponding variable names and classes in the DynoSAM framework.
+| Mathematical Notation | Code Implementation | Description |
+| :---: | :--- | :--- |
+| $k$ (Time-step) | `FrameId` | Integer frame number (0...N). Used as the primary time index. |
+| $i$ (Feature ID) | `TrackletId` | Unique feature tracking identifier. |
+| $j$ (Object ID) | `ObjectId` | Unique tracked object label. |
+| $X$ (Pose), $H$ (Motion) | `gtsam::Pose3` | Transformations for poses and motions, represented using the GTSAM library. |
+| $\mathcal{O}_k$ (Observations) | `Accessor` | Interface class used by backend modules to extract and interact with internal factor graph data. |
-# 6. Contribution Guidelines
+# 7. Contribution Guidelines
We strongly encourage you to submit issues, feedback and potential improvements.
We follow the branch, open PR, review, and merge workflow.
-### Support ###
+## Support
The developpers will be happy to assist you or to consider bug reports / feature requests. But
questions that can be answered reading this document will be ignored. Please contact
@@ -524,6 +420,6 @@ pre-commit install
We also provide a `.clang-format` file with the style rules that the repo uses, so that you can use [`clang-format`](https://clang.llvm.org/docs/ClangFormat.html) to reformat your code.
-# 7. BSD License
+# 8. BSD License
The DynoSAM framework is open-sourced under the BSD License, see [LICENSE](./LICENSE).
diff --git a/docker/Dockerfile b/docker/Dockerfile.amd64
similarity index 98%
rename from docker/Dockerfile
rename to docker/Dockerfile.amd64
index 85a2a4f1b..7d0806905 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile.amd64
@@ -32,7 +32,8 @@ RUN apt-get update \
# ROS installs
RUN apt-get install -y \
ros-kilted-ros2cli-common-extensions \
- ros-kilted-vision-opencv
+ ros-kilted-vision-opencv \
+ nlohmann-json3-dev
# other deps
RUN apt-get install libpng++-dev
diff --git a/docker/Dockerfile.l4t_jetpack6 b/docker/Dockerfile.l4t_jetpack6
new file mode 100644
index 000000000..5b1beb20e
--- /dev/null
+++ b/docker/Dockerfile.l4t_jetpack6
@@ -0,0 +1,182 @@
+FROM rwthika/ros2-torch:jazzy-ros-base-torch2.5.0
+
+MAINTAINER Jesse Morris "jesse.morris@sydney.edu.au"
+
+
+# To avoid tzdata asking for geographic location...
+ENV DEBIAN_FRONTEND=noninteractive
+
+#Install build dependencies
+RUN apt-get update && apt-get upgrade -y --allow-downgrades --no-install-recommends apt-utils
+RUN apt-get update && apt-get install -y --allow-downgrades git cmake build-essential pkg-config
+# Install xvfb to provide a display to container for GUI realted testing.
+RUN apt-get update && apt-get install -y --allow-downgrades xvfb
+
+# In the arm64 version of the base image we do not have the nvidia-cuda-development toolkit
+# as explained https://github.com/ika-rwth-aachen/docker-ros-ml-images/issues/16
+# this contains nvcc (ie we dont have it)
+# we need nvcc to build opencv with cuda
+# add extra nvidia container apt repository (otherwise we cannot find nvidia-cuda-toolkit)
+RUN curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \
+ && curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
+ sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
+ sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
+
+
+#NOTE: both nvidia-cuda-toolkit and cuda-toolkit are needed (particularly for header files)
+RUN apt-get update \
+ && apt-get install -y \
+ python3-pip \
+ openssh-client \
+ software-properties-common \
+ nano \
+ vim \
+ clang-format \
+ nvidia-cuda-toolkit \
+ cuda-toolkit \
+ libnvinfer-dev \
+ libnvonnxparsers-dev \
+ tensorrt-dev \
+ && pip3 install black pre-commit \
+ && rm -rf /var/lib/apt/lists/*
+
+
+
+# # ROS installs
+RUN apt-get update && apt-get install -y \
+ ros-jazzy-ros2cli-common-extensions \
+ ros-jazzy-cv-bridge \
+ ros-jazzy-vision-opencv \
+ ros-jazzy-pcl-ros \
+ ros-jazzy-rmw-fastrtps-cpp \
+ ros-jazzy-rmw-zenoh-cpp \
+ ros-jazzy-image-transport \
+ libpcl-conversions-dev
+
+# other deps
+# RUN apt-get install libpng++-dev
+# is libpng-dev different from libpng++? I think not available as a apt for aarm64
+# RUN apt-get install libpng-dev
+
+RUN python3 -m pip install pylatex evo setuptools pre-commit scipy argcomplete black pre-commit
+
+# Install glog, gflags
+RUN apt-get update && apt-get install -y libgflags2.2 libgflags-dev libgoogle-glog-dev
+
+# Install xvfb to provide a display to container for GUI realted testing.
+# vtk is needed for OpenCV to build its viz module (from contrib!)
+RUN apt-get update && apt-get install -y xvfb python3-dev python3-setuptools libvtk9-dev
+
+RUN pip3 install setuptools pre-commit scipy matplotlib argcomplete
+
+# install CSparse
+RUN DEBIAN_FRONTEND=noninteractive apt install -y libsuitesparse-dev
+
+RUN python3 -m pip install "ultralytics==8.3.0" "numpy<2.0" "opencv-python<5.0"
+RUN python3 -m pip install https://github.com/ultralytics/assets/releases/download/v0.0.0/onnxruntime_gpu-1.23.0-cp310-cp310-linux_aarch64.whl
+
+# Parallelism C++ for CPU
+RUN apt-get update && apt-get install -y libboost-all-dev libtbb-dev
+
+WORKDIR /root/
+
+
+
+# Install OpenCV for Ubuntu
+RUN apt-get update && apt-get install -y \
+ unzip \
+ libjpeg-dev libpng-dev libpng++-dev libtiff-dev libgtk2.0-dev \
+ libatlas-base-dev gfortran
+
+
+RUN git clone https://github.com/opencv/opencv.git
+RUN cd opencv && \
+ git checkout tags/4.10.0 && \
+ mkdir build
+
+RUN git clone https://github.com/opencv/opencv_contrib.git
+RUN cd opencv_contrib && \
+ git checkout tags/4.10.0
+
+# on aarch64 there is no binary for nlohmann-json3
+RUN git clone https://github.com/nlohmann/json.git
+RUN cd json && mkdir build && \
+ cd build && \
+ cmake .. && make -j$(nproc) install
+
+# OpenCV looks for the cuDNN version in cudnn_version.h, but it's been renamed to cudnn_version_v8.h
+RUN ln -sfnv /usr/include/$(uname -i)-linux-gnu/cudnn_version_v*.h /usr/include/$(uname -i)-linux-gnu/cudnn_version.h
+
+# IMPORTANT: must specify CXX_17 version. Default is c++11 which will cause compilation issues
+# On aarch64 we can enable NEON for CPU level optimisations
+# It is vital we use gcc-11 as otheriwise we cannot compile the cuda level code (ie. CUDA 12.9)
+RUN cd opencv/build && \
+ cmake -DCMAKE_BUILD_TYPE=Release \
+ -DCMAKE_INSTALL_PREFIX=/usr/local \
+ -DCMAKE_CXX_STANDARD=17 \
+ -D BUILD_opencv_python=OFF \
+ -D BUILD_opencv_python2=OFF \
+ -D BUILD_opencv_python3=ON \
+ -D WITH_CUDA=ON \
+ -D WITH_CUDNN=ON \
+ -D INSTALL_CUDA_LIBS=OFF \
+ -D CUDA_ARCH_PTX= \
+ -D CUDA_ARCH_BIN=8.7 \
+ -D CUDNN_INCLUDE_DIR=/usr/include/$(uname -i)-linux-gnu \
+ -D OPENCV_DNN_CUDA=ON \
+ -D ENABLE_FAST_MATH=ON \
+ -D CUDA_FAST_MATH=ON \
+ -D WITH_VTK=ON \
+ -D WITH_CUFFT=ON \
+ -D WITH_CUBLAS=ON \
+ -D WITH_TBB=ON \
+ -DENABLE_NEON=ON \
+ -D BUILD_TESTS=OFF \
+ -D BUILD_PERF_TESTS=OFF \
+ -D BUILD_opencv_ts=OFF \
+ -D BUILD_opencv_sfm=OFF \
+ -D BUILD_JAVA=OFF \
+ -DOPENCV_EXTRA_MODULES_PATH=/root/opencv_contrib/modules .. && \
+ make -j$(nproc) install
+
+RUN git clone https://github.com/MIT-SPARK/config_utilities.git
+RUN cd config_utilities/config_utilities && mkdir build && \
+ cd build && \
+ cmake .. && make -j$(nproc) install
+
+
+
+# Install GTSAM
+RUN git clone https://github.com/borglab/gtsam.git
+RUN cd gtsam && \
+ git fetch && \
+ git checkout tags/4.2.0 && \
+ mkdir build && \
+ cd build && \
+ cmake -DCMAKE_INSTALL_PREFIX=/usr/local \
+ -DGTSAM_USE_SYSTEM_EIGEN=ON \
+ -DGTSAM_BUILD_TESTS=OFF -DGTSAM_BUILD_EXAMPLES_ALWAYS=OFF -DCMAKE_BUILD_TYPE=Release -DGTSAM_BUILD_UNSTABLE=ON -DGTSAM_POSE3_EXPMAP=ON -DGTSAM_ROT3_EXPMAP=ON -DGTSAM_TANGENT_PREINTEGRATION=OFF .. && \
+ make -j$(nproc) install
+
+
+# Install Open_GV
+RUN git clone https://github.com/MIT-SPARK/opengv
+RUN cd opengv && \
+ mkdir build
+RUN cd opengv/build && \
+ cmake -DCMAKE_BUILD_TYPE=Release \
+ -DCMAKE_INSTALL_PREFIX=/usr/local \
+ .. && make -j$(nproc) install
+
+
+RUN echo 'export RMW_IMPLEMENTATION=rmw_zenoh_cpp' >> ~/.bashrc
+
+RUN mkdir -p /home/user/dev_ws/src/core
+RUN mkdir -p /home/user/dev_ws/src/third_parties
+RUN mkdir -p /home/user/upstream_ws/src
+
+SHELL ["/bin/bash", "-c"]
+
+RUN source /opt/ros/jazzy/setup.bash
+
+WORKDIR /home/user/dev_ws
diff --git a/docker/README.md b/docker/README.md
new file mode 100644
index 000000000..4db1cbc4e
--- /dev/null
+++ b/docker/README.md
@@ -0,0 +1,26 @@
+# Docker Files for DynoSAM
+
+Base images are pulled from [docker-ros-ml-images](https://github.com/ika-rwth-aachen/docker-ros-ml-images)
+
+- Dockerfile.amd64 is a linux/amd64 image tested on x86_64 desktop
+- Dockerfile.l4t_jetpack6 is build from linux/arm64 tested on an NVIDIA ORIN NX with Jetpack 6
+
+## Jetson Settings
+Architecture | aarch64
+Ubuntu | 22.04.5 LTS (Jammy Jellyfish)
+Jetson Linux | 36.4.7
+Python | 3.10.12
+ROS | jazzy
+CMake | 3.22.1
+CUDA | 12.6.77-1
+cuDNN | 9.3.0.75-1
+TensorRT | 10.7.0.23-1+cuda12.6
+PyTorch | 2.8.0
+GPUs | (Orin (nvgpu))
+OpenCV | 4.10.0
+
+> NOTE: The CUDA/Pytorch/TensorRT versions settings come with the base dockerfile but in practice we have been using CUDA 12.9.
+
+## Other versioning
+matplotlib=3.6.3
+numpy=1.26.4
\ No newline at end of file
diff --git a/docker/build_docker.sh b/docker/build_docker.sh
deleted file mode 100755
index b87559f38..000000000
--- a/docker/build_docker.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/usr/bin/env bash
-TAG=$1
-
-if [ -z $TAG ]; then
- echo "Usage: ./docker/build_docker.sh TAG [BASE_IMAGE]"
- exit -1
-fi
-
-DOCKER_BUILDKIT=1 docker build -f docker/Dockerfile -t $TAG .
diff --git a/docker/build_docker_amd64.sh b/docker/build_docker_amd64.sh
new file mode 100755
index 000000000..412a652a8
--- /dev/null
+++ b/docker/build_docker_amd64.sh
@@ -0,0 +1,2 @@
+#!/usr/bin/env bash
+bash build_docker_base.sh Dockerfile.amd64 acfr_rpg/dyno_sam_cuda
diff --git a/docker/build_docker_base.sh b/docker/build_docker_base.sh
new file mode 100755
index 000000000..e9b1edc3f
--- /dev/null
+++ b/docker/build_docker_base.sh
@@ -0,0 +1,29 @@
+#!/usr/bin/env bash
+DOCKERFILE=$1
+TAG=$2
+
+if [[ -z $TAG ]] || [[ -z $DOCKERFILE ]]; then
+ SCRIPT_NAME=$(basename "$0")
+ echo "Usage: ./$SCRIPT_NAME DOCKERFILE TAG"
+ exit -1
+fi
+
+
+# 2. Identify the directory where this script is located
+# This resolves the path even if called from a different location
+SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
+
+# 3. Temporarily change into the script's directory
+echo "Changing working directory to: $SCRIPT_DIR"
+cd "$SCRIPT_DIR" || { echo "Failed to change directory"; exit 1; }
+
+# 4. Verify the file exists in this directory
+if [ -f "$DOCKERFILE" ]; then
+ echo "Building dockerfile $DOCKERFILE with tag $TAG"
+ DOCKER_BUILDKIT=1 docker build --network=host -f $DOCKERFILE -t $TAG .
+ # ----------------------------
+
+else
+ echo "Error: File '$DOCKERFILE' not found in $SCRIPT_DIR"
+ exit 1
+fi
diff --git a/docker/build_docker_l4t_jetpack6.sh b/docker/build_docker_l4t_jetpack6.sh
new file mode 100755
index 000000000..66481c5c3
--- /dev/null
+++ b/docker/build_docker_l4t_jetpack6.sh
@@ -0,0 +1,2 @@
+#!/usr/bin/env bash
+bash build_docker_base.sh Dockerfile.l4t_jetpack6 acfr_rpg/dynosam_cuda_l4t
diff --git a/docker/create_container_amd64.sh b/docker/create_container_amd64.sh
new file mode 100755
index 000000000..ef1c0860f
--- /dev/null
+++ b/docker/create_container_amd64.sh
@@ -0,0 +1,10 @@
+#!/usr/bin/env bash
+
+### EDIT THIS TO WHEREVER YOU'RE STORING YOU DATA ###
+# folder should exist before you mount it
+LOCAL_DATA_FOLDER=/media/jmor6670/T7/datasets
+LOCAL_RESULTS_FOLDER=~/results/
+LOCAL_DYNO_SAM_FOLDER=~/Code/src/DynOSAM/
+LOCAL_THIRD_PARTY_DYNO_SAM_FOLDER=~/Code/src/third_party_dynosam/
+
+bash create_container_base.sh acfr_rpg/dyno_sam_cuda dyno_sam $LOCAL_DATA_FOLDER $LOCAL_RESULTS_FOLDER $LOCAL_DYNO_SAM_FOLDER $LOCAL_THIRD_PARTY_DYNO_SAM_FOLDER
diff --git a/docker/create_container.sh b/docker/create_container_base.sh
similarity index 72%
rename from docker/create_container.sh
rename to docker/create_container_base.sh
index 85401770f..d75ab6420 100755
--- a/docker/create_container.sh
+++ b/docker/create_container_base.sh
@@ -1,14 +1,18 @@
#!/usr/bin/env bash
-CONTAINER_NAME=dyno_sam
-CONTAINER_IMAGE_NAME=acfr_rpg/dyno_sam_cuda
+CONTAINER_IMAGE_NAME=$1
+CONTAINER_NAME=$2
-### EDIT THIS TO WHEREVER YOU'RE STORING YOU DATA ###
-# folder should exist before you mount it
-LOCAL_DATA_FOLDER=/media/jmor6670/T7/datasets
-LOCAL_RESULTS_FOLDER=~/results/
-LOCAL_DYNO_SAM_FOLDER=~/Code/src/DynOSAM/
-LOCAL_THIRD_PARTY_DYNO_SAM_FOLDER=~/Code/src/third_party_dynosam/
+LOCAL_DATA_FOLDER=$3
+LOCAL_RESULTS_FOLDER=$4
+LOCAL_DYNO_SAM_FOLDER=$5
+LOCAL_THIRD_PARTY_DYNO_SAM_FOLDER=$6
+
+echo "Creating dynosam container ($CONTAINER_NAME) from image: $CONTAINER_IMAGE_NAME"
+echo "Local data folder: $LOCAL_DATA_FOLDER"
+echo "Local results folder: $LOCAL_RESULTS_FOLDER"
+echo "Local DynoSAM folder: $LOCAL_DYNO_SAM_FOLDER"
+echo "Local third party dynosam folder: $LOCAL_THIRD_PARTY_DYNO_SAM_FOLDER"
CONTAINER_DATA_FOLDER=/root/data
@@ -17,8 +21,6 @@ CONTAINER_WORKSPACE_FOLDER=/home/user/dev_ws/src/core/
CONTAINER_WORKSPACE_FOLDER_THIRD_PARTY=/home/user/dev_ws/src/third_parties/
-
-
USE_NVIDIA=false
# If we are running in a docker-in-docker scenario NVIDIA_SOS will be populated
@@ -78,11 +80,34 @@ if "$USE_NVIDIA"; then
else
TERMINAL_FLAGS='-t'
fi
+
+ echo "$@"
# Create the container based on the launchfile it's launching (if any)
# removes '.launch' from the last argument
echo "Container name will be: $CONTAINER_NAME"
# docker run $DOCKER_NVIDIA_SO_VOLUMES \
- docker run \
+ # docker run \
+ # --privileged \
+ # --gpus all \
+ # -i -d \
+ # --volume $XSOCK:$XSOCK:rw \
+ # -v $LOCAL_DATA_FOLDER:$CONTAINER_DATA_FOLDER \
+ # -v $LOCAL_RESULTS_FOLDER:$CONTAINER_RESULTS_FOLDER \
+ # -v $LOCAL_DYNO_SAM_FOLDER:$CONTAINER_WORKSPACE_FOLDER \
+ # -v $LOCAL_THIRD_PARTY_DYNO_SAM_FOLDER:$CONTAINER_WORKSPACE_FOLDER_THIRD_PARTY \
+ # -v /var/run/docker.sock:/var/run/docker.sock \
+ # --env DISPLAY=$DISPLAY \
+ # --env XAUTHORITY=$XAUTH \
+ # --env QT_X11_NO_MITSHM=0 \
+ # --env QT_X11_NO_XRENDER=0 \
+ # --volume $XAUTH:$XAUTH:rw \
+ # --net host \
+ # --pid host \
+ # --ipc host \
+ # -it \
+ # --name=$CONTAINER_NAME \
+ # $CONTAINER_IMAGE_NAME "$@"
+ docker run \
--privileged \
--gpus all \
-i -d \
@@ -102,7 +127,8 @@ if "$USE_NVIDIA"; then
--ipc host \
-it \
--name=$CONTAINER_NAME \
- $CONTAINER_IMAGE_NAME "$@"
+ $CONTAINER_IMAGE_NAME
fi
+# FOR NOW?
xhost +local:`docker inspect --format='{{ .Config.Hostname }}' $CONTAINER_NAME`
diff --git a/docker/create_container_l4t_jetpack6.sh b/docker/create_container_l4t_jetpack6.sh
new file mode 100755
index 000000000..3e429ae95
--- /dev/null
+++ b/docker/create_container_l4t_jetpack6.sh
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+
+
+### EDIT THIS TO WHEREVER YOU'RE STORING YOU DATA ###
+# folder should exist before you mount it
+LOCAL_DATA_FOLDER=/media/jmor6670/T7/datasets/
+LOCAL_RESULTS_FOLDER=/home/usyd/dynosam/results/
+LOCAL_DYNO_SAM_FOLDER=/home/usyd/dynosam/DynOSAM/
+LOCAL_THIRD_PARTY_DYNO_SAM_FOLDER=/home/usyd/dynosam/extra
+
+bash create_container_base.sh acfr_rpg/dynosam_cuda_l4t dyno_sam_l4t $LOCAL_DATA_FOLDER $LOCAL_RESULTS_FOLDER $LOCAL_DYNO_SAM_FOLDER $LOCAL_THIRD_PARTY_DYNO_SAM_FOLDER
diff --git a/docs/media/INSTALL.md b/docs/media/INSTALL.md
index 63c0ebb4e..6de60c608 100644
--- a/docs/media/INSTALL.md
+++ b/docs/media/INSTALL.md
@@ -1,134 +1,150 @@
# DynoSAM Installation
-Tested on Ubuntu 20.04
+## Prerequisites
+- [ROS2](https://docs.ros.org/en/kilted/Installation.html)
+ > NOTE: this links to the kilted distribution which used during development. We have also tested on Jazzy for the NVIDIA ORIN
+- [GTSAM](https://github.com/borglab/gtsam) >= 4.1
+- [OpenCV](https://github.com/opencv/opencv) >= 3.4
+- [OpenGV](https://github.com/MIT-SPARK/opengv)
+- [Glog](http://rpg.ifi.uzh.ch/docs/glog.html), [Gflags](https://gflags.github.io/gflags/)
+- [Gtest](https://github.com/google/googletest/blob/master/googletest/docs/primer.md) (installed automagically)
+- [config_utilities](https://github.com/MIT-SPARK/config_utilities)
+- [dynamic_slam_interfaces](https://github.com/ACFR-RPG/dynamic_slam_interfaces) (Required by default. Can be optionally made not a requirement. See Insallation instructions below)
-> NOTE: this instructions are taken essentially verbatum from the included [Dockerfile](./../../docker/Dockerfile)
+External dependancies (for visualization) not required for compilation.
+- [rviz_dynamic_slam_plugins](https://github.com/ACFR-RPG/rviz_dynamic_slam_plugins) (Plugin to display custom `dynamic_slam_interfaces` messages which are advertised by default.)
-## CUDA dependancies
-As of September 2025 we are adding cuda dependancies for certain OpenCV operations (and evenautually for running inference on images).
-
-> NOTE: I have a very old GPU (RTX 2080 which is only running CIDA 12.2 with Driver Version: 535.183.01), get CUDA support however you are able!
-
-The docker file has been updated and based off the [docker-ros-ml-images](https://github.com/ika-rwth-aachen/docker-ros-ml-images?tab=readme-ov-file#rwthikaros2-torch-ros-2-nvidia-cuda-nvidia-tensorrt-pytorch) to include
-- CUDA
-- TensorRT
-- Pytorch
-built ontop of ROS2.
-
-The CUDA dependancies are mostly for OpenCV modules but also eventually for adding DNN inference into DynoSAM itself so processing of images
-can be handled internally (ie YOLO).
-- **Important:** Check [GPU Compute Capability](https://developer.nvidia.com/cuda-gpus) to set `CUDA_ARCH_BIN` flag
-- NVIDIA GeForce RTX 2080 is 7.5
+GPU acceleration:
+- [TensorRT](https://github.com/NVIDIA/TensorRT)
+- [CUDA](https://docs.nvidia.com/cuda/cuda-runtime-api/index.html)
-## Dependancies
+are now supported with the new Docker image.
+This provides support for TensorRT accellerated object-instance segmentation (which is now part of the DynoSAM pipeline) and CUDA acceleration in the front-end.
+Backwards compatability (i.e no CUDA support) is not currently a priority.
-```bash
-sudo apt-get install -y --no-install-recommends apt-utils
+We provide detailed install instructions when using the Docker.
-sudo apt-get install -y cmake
+To install natively, install the dependancies as required by docker and build as a ROS2 package.
-sudo apt install unzip libjpeg-dev libpng-dev libpng++-dev libtiff-dev libgtk2.0-dev libatlas-base-dev gfortran libgflags2.2 libgflags-dev libgoogle-glog0v5 libgoogle-glog-dev python3-dev python3-setuptools clang-format python3-pip nlohmann-json3-dev libsuitesparse-dev
-```
+## Docker Install instructions
-GTSAM's Optional dependencies (highly recommended for speed) also include
-```bash
-sudo apt install libboost-all-dev libtbb-dev
-```
+DynoSAM has been tested on x86_64 and aarm64 (with a NVIDIA ORIN) devices using the [two docker files](../../docker/) provided. See the [REAMDE.md](../../docker/README.md) for more detail on hardware used etc.
-For evaluation we also need various formatting tools
-```bash
-sudo apt-get install texlive-pictures texlive-science texlive-latex-extra latexmk
-```
+We provide scripts to build and create docker containers to run and develop DynoSAM which is intendend to be mounted within the created container.
-Python (pip) dependancies:
-```bash
-python3 -m pip install pylatex evo setuptools pre-commit scipy matplotlib argcomplete black pre-commit
-```
+### Folder Structure
+To DynoSAM code to be changed within the docker environment, we mount the local version of the code within the container. To ensure this happens smoothly please download the DynoSAM code in the following structure
-NOTE: There seems to be a conflict between the version of matplotlib and numpy. In this case following worked for me:
```
-sudo apt remove python3-matplotlib
-python3 -m pip install numpy==1.26.3
+dynosam_pkg/
+ DynoSAM/
+ extras/
+ rviz_dynamic_slam_plugins
+ dynamic_slam_interfaces
+ results/
```
+where DynoSAM is this repository and anything in `extras` are other ROS packages that wish to be built alongside DynoSAM.
-## Install OpenCV
+> NOTE: `dynosam_pkg' may be any folder, its just a place to put all the DynoSAM related code.
-Note that you can use `apt-get install libopencv-dev libopencv-contrib-dev` on 20.04 instead of building from source.
+> NOTE: the reason for this structure is historical and due to the way the _create_container_ scripts are written; you don't need to do this but I provide complete instructures for simplicity.
-> Also note, I have not actually tried this!
-To build from source
-
-```bash
-git git clone https://github.com/opencv/opencv_contrib.git
-cd opencv_contrib && \
- git checkout tags/4.8.0
-
-cd ../ &&
-git clone git clone https://github.com/opencv/opencv.git
-
-cd opencv && \
-git checkout tags/4.8.0 && mkdir build
-cmake -DCMAKE_BUILD_TYPE=Release \
- -DCMAKE_INSTALL_PREFIX=/usr/local \
- -D BUILD_opencv_python=OFF \
- -D BUILD_opencv_python2=OFF \
- -D BUILD_opencv_python3=ON \
- -DOPENCV_EXTRA_MODULES_PATH=../../opencv_contrib/modules .. # Use -DWITH_TBB=On if you have TBB
-
-sudo make -j$(nproc) install
+### Docker Build
+Build the relevant docker file:
```
+cd dynosam_pkg/DynoSAM/docker &&
+./build_docker_amd64.sh //or build_docker_l4t.sh
+```
+### Create Container
+Once built, you should have a docker image called `acfr_rpg/dyno_sam_cuda` or `acfr_rpg/dynosam_cuda_l4t`
-## Install GTSAM
-
-Tested with release 4.2
-
-```bash
-
-git clone https://github.com/borglab/gtsam.git
-cd gtsam && \
- git fetch && \
- git checkout tags/4.2.0 && \
- mkdir build && \
- cd build && \
- cmake -DCMAKE_INSTALL_PREFIX=/usr/local \
- -DGTSAM_USE_SYSTEM_EIGEN=ON \
- -DGTSAM_BUILD_TESTS=OFF -DGTSAM_BUILD_EXAMPLES_ALWAYS=OFF -DCMAKE_BUILD_TYPE=Release -DGTSAM_BUILD_UNSTABLE=ON -DGTSAM_POSE3_EXPMAP=ON -DGTSAM_ROT3_EXPMAP=ON -DGTSAM_TANGENT_PREINTEGRATION=OFF .. && \
+In the associated _create_container_ scripts modify the local variables to match the folder paths on the local machine. These folders will be mounted as a volume within the container
+```
+LOCAL_DATA_FOLDER=/path/to/some/datasets/
+LOCAL_RESULTS_FOLDER=/path/to/dynosam_pkg/results/
+LOCAL_DYNO_SAM_FOLDER=/path/to/dynosam_pkg/DynoSAM/
+LOCAL_THIRD_PARTY_DYNO_SAM_FOLDER=/path/to/dynosam_pkg/extras
+```
+Run the creation script. The created container will be called `dyno_sam`
+```
+./create_container_amd64.sh //or ./create_container_l4t_jetpack6.sh
+```
-sudo make -j$(nproc) install
+Finally enter into the container and build DynoSAM
+```
+cd /home/user/dev_w &&
+export MAKEFLAGS="-j10" && clear && colcon build
```
-## Install OpenGV
+## CUDA dependancies
+As of September 2025 we are adding cuda dependancies for certain OpenCV operations (and evenautually for running inference on images).
-```bash
-git clone https://github.com/MIT-SPARK/opengv
-cd opengv && mkdir build && cd build &&
-cmake -DCMAKE_BUILD_TYPE=Release \
- -DCMAKE_INSTALL_PREFIX=/usr/local ..
+> NOTE: I have a very old GPU (RTX 2080 which is only running CUDA 12.2 with Driver Version: 535.183.01), get CUDA support however you are able!
-sudo make -j$(nproc) install
+The docker file has been updated and based off the [docker-ros-ml-images](https://github.com/ika-rwth-aachen/docker-ros-ml-images?tab=readme-ov-file#rwthikaros2-torch-ros-2-nvidia-cuda-nvidia-tensorrt-pytorch) to include
+- CUDA
+- TensorRT
+- Pytorch
+built ontop of ROS2.
-```
-Note: This links to a forked version of OpenGV which contains an updated fix the CMakeLists.txt due to issues with -march=native being set. This links to original version of [OpenGV](https://github.com/laurentkneip/opengv), which contains the problematic flag.
+The CUDA dependancies are mostly for OpenCV modules but also eventually for adding DNN inference into DynoSAM itself so processing of images
+can be handled internally (ie YOLO).
-## Install Config Utilities
+- **Important:** Check [GPU Compute Capability](https://developer.nvidia.com/cuda-gpus) to set `CUDA_ARCH_BIN` flag
+- NVIDIA GeForce RTX 2080 is 7.5
-```bash
-git clone git@github.com:MIT-SPARK/config_utilities.git
+## Additional Installation Notes
-cd config_utilities/config_utilities
-mkdir build
-cd build
-cmake ..
-make -j
+`dynamic_slam_interfaces` is a require dependacy by default. This package is used to include custom messages that represet the state of each dynamic object per frame and is used by the ROS publishers.
-sudo make install
+To disable this dependancy compile the code as
```
-
-## Additional Installation Notes
+colcon build --cmake-args -DENABLE_DYNAMIC_SLAM_INTERFACES=OFF
+```
+By default `ENABLE_DYNAMIC_SLAM_INTERFACES=ON` in the [CMakeLists.txt](./dynosam_ros/CMakeLists.txt). This CMake option will additionally change the _visualisation_ (and the output topics) used by DynoSAM. See the [ROS Visualisation](#ros-visualisation) section below.
Due to DynoSAM being build within ROS:
- Need to build GTSAM with `-DGTSAM_USE_SYSTEM_EIGEN=ON` to avoid issues with ROS and OpenCV-Eigen compatability. Confirmed from https://discourse.ros.org/t/announcing-gtsam-as-a-ros-1-2-package/32739 which explains that the GTSAM ROS package is build with this flag set to ON (and describes it as "problematic"). We still want to build GTSAM from source so we can control the version and other compiler flags.
Kimera-VIO's install instructions indicate that OpenGV must use the same version of Eigen as GTSAM, which can be set using compiler flags. Since we are using the ROS install Eigen, I have removed these flags and hope that the package manager with CMake can find the right (and only) version. This has not proved problematic... yet...
+
+## Possible Compilation Issues
+### Missing MPI Header Error
+When first compiling DynoSAM, this error may appear as MPI relies on a non-existent directory.
+This issue _should_ be fixed a patch in the `dynosam_common` CMakeLists.txt which directly updates the `MPI_INCLUDE_PATH`.
+
+However, if the issue persists, the following is a known fix.
+1. Unset HPC-X variables
+ ```bash
+ unset OPENMPI_VERSION
+ unset OMPI_MCA_coll_hcoll_enable
+ unset OPAL_PREFIX
+ export PATH=$(echo $PATH | tr ':' '\n' | grep -v '/opt/hpcx' | grep -v '/usr/local/mpi/bin' | paste -sd:)
+ ```
+ Now verify `env | grep -i mpi echo $PATH` that the `OPAL_PREFIX` and HPC-X paths no longer appear
+2. Install OpenMPI
+ ```bash
+ sudo apt update
+ sudo apt install libopenmpi-dev openmpi-bin
+ ```
+ and verify that the following points to `/usr/bin/mpicc`
+ ```bash
+ which mpicc
+ mpicc --version
+ ```
+3. Clear the ROS2 workspace
+ ```bash
+ rm -rf build/ install/ log/
+ ```
+4. Build with the system MPI
+ ```bash
+ export MPI_C_COMPILER=/usr/bin/mpicc
+ export MPI_CXX_COMPILER=/usr/bin/mpicxx
+ colcon build
+ ```
+### Missing Dependencies
+- `nlohmannjson` may not be installed, so install with `sudo apt install nlohmann-json3-dev`
+- GTSAM may not be installed, so install with `pip install gtsam`
+
diff --git a/dynosam/include/dynosam/backend/Accessor-impl.hpp b/dynosam/include/dynosam/backend/Accessor-impl.hpp
index 06b5a24cb..72043afef 100644
--- a/dynosam/include/dynosam/backend/Accessor-impl.hpp
+++ b/dynosam/include/dynosam/backend/Accessor-impl.hpp
@@ -36,23 +36,28 @@
namespace dyno {
-template
-AccessorT