diff --git a/.devcontainer/humble/Dockerfile b/.devcontainer/humble/Dockerfile
new file mode 100644
index 00000000..62978bb7
--- /dev/null
+++ b/.devcontainer/humble/Dockerfile
@@ -0,0 +1,17 @@
+
+FROM introlab3it/rtabmap:jammy
+
+ARG USERNAME=vscode
+ARG USER_UID=1000
+ARG USER_GID=1000
+
+RUN set -ex && \
+ groupadd --gid ${USER_GID} ${USERNAME} && \
+ useradd --uid ${USER_UID} --gid ${USER_GID} -m ${USERNAME} && \
+ usermod -a -G sudo ${USERNAME}
+
+RUN mkdir -p /home/${USERNAME}/ros2_ws/src && \
+ chown -R ${USERNAME}:${USERNAME} /home/${USERNAME}/ros2_ws
+
+RUN echo "source /opt/ros/humble/setup.bash" >> /home/${USERNAME}/.bashrc
+RUN echo "source /usr/share/bash-completion/completions/git" >> /home/${USERNAME}/.bashrc
diff --git a/.devcontainer/humble/devcontainer.json b/.devcontainer/humble/devcontainer.json
new file mode 100644
index 00000000..a1666586
--- /dev/null
+++ b/.devcontainer/humble/devcontainer.json
@@ -0,0 +1,27 @@
+{
+ "build": {
+ "dockerfile": "Dockerfile",
+ "pull": true
+ },
+ "remoteUser": "vscode",
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "ms-vscode.cpptools-themes",
+ "ms-vscode.cmake-tools",
+ "ms-vscode.cpptools-extension-pack",
+ "ms-azuretools.vscode-docker",
+ "ms-python.python"]
+ }
+ },
+ "settings": {
+ "python.autoComplete.extraPaths": [
+ "/opt/ros/humble/lib/python3/dist-packages"
+ ],
+ "terminal.integrated.defaultProfile.linux": "bash"
+ },
+ "workspaceMount": "source=${localWorkspaceFolder},target=/home/vscode/ros2_ws/src/rtabmap_ros,type=bind",
+ "workspaceFolder": "/home/vscode/ros2_ws",
+ "postCreateCommand": "echo 'Initialize: export MAKEFLAGS=\"-j6\" && colcon build --cmake-args -DRTABMAP_SYNC_MULTI_RGBD=ON -DCMAKE_BUILD_TYPE=Release --parallel-workers 2'"
+ //"runArgs": ["--privileged", "--runtime=nvidia", "--network=host"]
+}
diff --git a/.devcontainer/jazzy/Dockerfile b/.devcontainer/jazzy/Dockerfile
new file mode 100644
index 00000000..13b36c5a
--- /dev/null
+++ b/.devcontainer/jazzy/Dockerfile
@@ -0,0 +1,20 @@
+
+FROM introlab3it/rtabmap:noble
+
+# remove ubuntu user
+RUN touch /var/mail/ubuntu && chown ubuntu /var/mail/ubuntu && userdel -r ubuntu
+
+ARG USERNAME=vscode
+ARG USER_UID=1000
+ARG USER_GID=1000
+
+RUN set -ex && \
+ groupadd --gid ${USER_GID} ${USERNAME} && \
+ useradd --uid ${USER_UID} --gid ${USER_GID} -m ${USERNAME} && \
+ usermod -a -G sudo ${USERNAME}
+
+RUN mkdir -p /home/${USERNAME}/ros2_ws/src && \
+ chown -R ${USERNAME}:${USERNAME} /home/${USERNAME}/ros2_ws
+
+RUN echo "source /opt/ros/jazzy/setup.bash" >> /home/${USERNAME}/.bashrc
+RUN echo "source /usr/share/bash-completion/completions/git" >> /home/${USERNAME}/.bashrc
diff --git a/.devcontainer/jazzy/devcontainer.json b/.devcontainer/jazzy/devcontainer.json
new file mode 100644
index 00000000..5e768e9a
--- /dev/null
+++ b/.devcontainer/jazzy/devcontainer.json
@@ -0,0 +1,27 @@
+{
+ "build": {
+ "dockerfile": "Dockerfile",
+ "pull": true
+ },
+ "remoteUser": "vscode",
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "ms-vscode.cpptools-themes",
+ "ms-vscode.cmake-tools",
+ "ms-vscode.cpptools-extension-pack",
+ "ms-azuretools.vscode-docker",
+ "ms-python.python"]
+ }
+ },
+ "settings": {
+ "python.autoComplete.extraPaths": [
+ "/opt/ros/jazzy/lib/python3/dist-packages"
+ ],
+ "terminal.integrated.defaultProfile.linux": "bash"
+ },
+ "workspaceMount": "source=${localWorkspaceFolder},target=/home/vscode/ros2_ws/src/rtabmap_ros,type=bind",
+ "workspaceFolder": "/home/vscode/ros2_ws",
+ "postCreateCommand": "echo 'Initialize: export MAKEFLAGS=\"-j6\" && colcon build --cmake-args -DRTABMAP_SYNC_MULTI_RGBD=ON -DCMAKE_BUILD_TYPE=Release --parallel-workers 2'"
+ //"runArgs": ["--privileged", "--runtime=nvidia", "--network=host"]
+}
diff --git a/.devcontainer/kilted/Dockerfile b/.devcontainer/kilted/Dockerfile
new file mode 100644
index 00000000..9aee0a83
--- /dev/null
+++ b/.devcontainer/kilted/Dockerfile
@@ -0,0 +1,20 @@
+
+FROM introlab3it/rtabmap:noble-kilted
+
+# remove ubuntu user
+RUN touch /var/mail/ubuntu && chown ubuntu /var/mail/ubuntu && userdel -r ubuntu
+
+ARG USERNAME=vscode
+ARG USER_UID=1000
+ARG USER_GID=1000
+
+RUN set -ex && \
+ groupadd --gid ${USER_GID} ${USERNAME} && \
+ useradd --uid ${USER_UID} --gid ${USER_GID} -m ${USERNAME} && \
+ usermod -a -G sudo ${USERNAME}
+
+RUN mkdir -p /home/${USERNAME}/ros2_ws/src && \
+ chown -R ${USERNAME}:${USERNAME} /home/${USERNAME}/ros2_ws
+
+RUN echo "source /opt/ros/kilted/setup.bash" >> /home/${USERNAME}/.bashrc
+RUN echo "source /usr/share/bash-completion/completions/git" >> /home/${USERNAME}/.bashrc
diff --git a/.devcontainer/kilted/devcontainer.json b/.devcontainer/kilted/devcontainer.json
new file mode 100644
index 00000000..6176ee77
--- /dev/null
+++ b/.devcontainer/kilted/devcontainer.json
@@ -0,0 +1,27 @@
+{
+ "build": {
+ "dockerfile": "Dockerfile",
+ "pull": true
+ },
+ "remoteUser": "vscode",
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "ms-vscode.cpptools-themes",
+ "ms-vscode.cmake-tools",
+ "ms-vscode.cpptools-extension-pack",
+ "ms-azuretools.vscode-docker",
+ "ms-python.python"]
+ }
+ },
+ "settings": {
+ "python.autoComplete.extraPaths": [
+ "/opt/ros/kilted/lib/python3/dist-packages"
+ ],
+ "terminal.integrated.defaultProfile.linux": "bash"
+ },
+ "workspaceMount": "source=${localWorkspaceFolder},target=/home/vscode/ros2_ws/src/rtabmap_ros,type=bind",
+ "workspaceFolder": "/home/vscode/ros2_ws",
+ "postCreateCommand": "echo 'Initialize: export MAKEFLAGS=\"-j6\" && colcon build --cmake-args -DRTABMAP_SYNC_MULTI_RGBD=ON -DCMAKE_BUILD_TYPE=Release --parallel-workers 2'"
+ //"runArgs": ["--privileged", "--runtime=nvidia", "--network=host"]
+}
diff --git a/.devcontainer/noetic/Dockerfile b/.devcontainer/noetic/Dockerfile
new file mode 100644
index 00000000..3eec0a03
--- /dev/null
+++ b/.devcontainer/noetic/Dockerfile
@@ -0,0 +1,17 @@
+
+FROM introlab3it/rtabmap:focal
+
+ARG USERNAME=vscode
+ARG USER_UID=1000
+ARG USER_GID=1000
+
+RUN set -ex && \
+ groupadd --gid ${USER_GID} ${USERNAME} && \
+ useradd --uid ${USER_UID} --gid ${USER_GID} -m ${USERNAME} && \
+ usermod -a -G sudo ${USERNAME}
+
+RUN mkdir -p /home/${USERNAME}/catkin_ws/src && \
+ chown -R ${USERNAME}:${USERNAME} /home/${USERNAME}/catkin_ws
+
+RUN echo "source /opt/ros/noetic/setup.bash" >> /home/${USERNAME}/.bashrc
+RUN echo "source /usr/share/bash-completion/completions/git" >> /home/${USERNAME}/.bashrc
diff --git a/.devcontainer/noetic/devcontainer.json b/.devcontainer/noetic/devcontainer.json
new file mode 100644
index 00000000..4990bced
--- /dev/null
+++ b/.devcontainer/noetic/devcontainer.json
@@ -0,0 +1,27 @@
+{
+ "build": {
+ "dockerfile": "Dockerfile",
+ "pull": true
+ },
+ "remoteUser": "vscode",
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "ms-vscode.cpptools-themes",
+ "ms-vscode.cmake-tools",
+ "ms-vscode.cpptools-extension-pack",
+ "ms-azuretools.vscode-docker",
+ "ms-python.python"]
+ }
+ },
+ "settings": {
+ "python.autoComplete.extraPaths": [
+ "/opt/ros/noetic/lib/python3/dist-packages"
+ ],
+ "terminal.integrated.defaultProfile.linux": "bash"
+ },
+ "workspaceMount": "source=${localWorkspaceFolder},target=/home/vscode/catkin_ws/src/rtabmap_ros,type=bind",
+ "workspaceFolder": "/home/vscode/catkin_ws",
+ "postCreateCommand": "cd /home/vscode/catkin_ws/src && catkin_init_workspace"
+ //"runArgs": ["--privileged", "--runtime=nvidia", "--network=host"]
+}
diff --git a/.github/workflows/docker-ros2.yml b/.github/workflows/docker-ros2.yml
index 27033771..72a91300 100644
--- a/.github/workflows/docker-ros2.yml
+++ b/.github/workflows/docker-ros2.yml
@@ -10,8 +10,9 @@ jobs:
runs-on: ubuntu-latest
strategy:
+ fail-fast: false
matrix:
- docker_tag: [humble, humble-latest, iron, iron-latest]
+ docker_tag: [humble, humble-latest, jazzy, jazzy-latest, kilted-latest]
include:
- docker_tag: humble
docker_path: 'humble'
@@ -22,42 +23,49 @@ jobs:
docker_platforms: |
linux/amd64
linux/arm64
- - docker_tag: iron
- docker_path: 'iron'
+ - docker_tag: jazzy
+ docker_path: 'jazzy'
docker_platforms: |
linux/amd64
- - docker_tag: iron-latest
- docker_path: 'iron/latest'
+ linux/arm64
+ - docker_tag: jazzy-latest
+ docker_path: 'jazzy/latest'
docker_platforms: |
linux/amd64
+ linux/arm64
+ - docker_tag: kilted-latest
+ docker_path: 'kilted/latest'
+ docker_platforms: |
+ linux/amd64
+ linux/arm64
steps:
-
name: Checkout
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
-
name: Set up QEMU
- uses: docker/setup-qemu-action@v1
+ uses: docker/setup-qemu-action@v3
with:
platforms: all
-
name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v1
+ uses: docker/setup-buildx-action@v3
-
name: Login to DockerHub
- uses: docker/login-action@v1
+ uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Build and push
- uses: docker/build-push-action@v2
+ uses: docker/build-push-action@v6
with:
context: .
push: true
platforms: ${{ matrix.docker_platforms }}
file: ./docker/${{ matrix.docker_path }}/Dockerfile
tags: introlab3it/rtabmap_ros:${{ matrix.docker_tag }}
- cache-from: type=registry,ref=introlab3it/rtabmap_ros:${{ matrix.docker_tag }}
+ no-cache: true
cache-to: type=inline
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index ec6913e1..ab73b32e 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -32,7 +32,6 @@ jobs:
docker_platforms: |
linux/amd64
linux/arm64
- linux/arm/v7
steps:
-
diff --git a/.github/workflows/ros1.yml b/.github/workflows/ros1.yml
deleted file mode 100644
index 39f25e63..00000000
--- a/.github/workflows/ros1.yml
+++ /dev/null
@@ -1,62 +0,0 @@
-name: ros1
-
-on:
- push:
- branches: [ master ]
- pull_request:
- branches: [ master ]
-
-env:
- # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
- BUILD_TYPE: Release
-
-jobs:
- build:
- # The CMake configure and build commands are platform agnostic and should work equally
- # well on Windows or Mac. You can convert this to a matrix build if you need
- # cross-platform coverage.
- # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
- name: Build on ros ${{ matrix.ros_distro }} and ${{ matrix.os }}
- runs-on: ${{ matrix.os }}
- strategy:
- matrix:
- os: [ubuntu-20.04]
- include:
- - os: ubuntu-20.04
- ros_distro: 'noetic'
-
-
- steps:
- - uses: ros-tooling/setup-ros@v0.2
- with:
- required-ros-distributions: ${{ matrix.ros_distro }}
-
- - name: Install dependencies
- run: |
- sudo apt-get update
- sudo apt-get -y install ros-${{ matrix.ros_distro }}-rtabmap-ros python3-catkin-tools
- sudo apt-get -y remove ros-${{ matrix.ros_distro }}-rtabmap
- sudo pip3 uninstall empy --yes
-
- - name: Setup catkin workspace
- run: |
- source /opt/ros/${{ matrix.ros_distro }}/setup.bash
- mkdir -p ${{github.workspace}}/catkin_ws/src
- cd ${{github.workspace}}/catkin_ws/src
- cd ..
- catkin config --init --cmake-args -DSETUPTOOLS_DEB_LAYOUT=OFF -DCMAKE_C_FLAGS="-Wformat -Werror=format-security" -DCMAKE_CXX_FLAGS="-Wformat -Werror=format-security"
-
- - uses: actions/checkout@v2
- with:
- repository: 'introlab/rtabmap'
- path: 'catkin_ws/src/rtabmap'
-
- - uses: actions/checkout@v2
- with:
- path: 'catkin_ws/src/rtabmap_ros'
-
- - name: caktkin build
- run: |
- source /opt/ros/${{ matrix.ros_distro }}/setup.bash
- cd ${{github.workspace}}/catkin_ws
- catkin build -p 1 -i --verbose
diff --git a/.github/workflows/ros2.yml b/.github/workflows/ros2.yml
index b810295b..790f78c4 100644
--- a/.github/workflows/ros2.yml
+++ b/.github/workflows/ros2.yml
@@ -8,51 +8,36 @@ on:
branches: [ ros2 ]
env:
- # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
BUILD_TYPE: Release
jobs:
build:
- # The CMake configure and build commands are platform agnostic and should work equally
- # well on Windows or Mac. You can convert this to a matrix build if you need
- # cross-platform coverage.
- # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
- name: Build on ros2 ${{ matrix.ros_distro }} and ${{ matrix.os }}
- runs-on: ${{ matrix.os }}
+ name: Build ros2 ${{ matrix.ros_distro }}
+ runs-on: ubuntu-latest
strategy:
matrix:
- ros_distro: [humble, iron]
+ ros_distro: [humble, jazzy, kilted]
include:
- - ros_distro: 'humble'
- os: ubuntu-22.04
- - ros_distro: 'iron'
- os: ubuntu-22.04
-
+ - ros_distro: humble
+ skip_keys: ''
+ - ros_distro: jazzy
+ skip_keys: ''
+ - ros_distro: kilted
+ skip_keys: 'nav2_bringup nav2_msgs'
+ fail-fast: false
+ container:
+ image: osrf/ros:${{ matrix.ros_distro }}-desktop-full
steps:
- - uses: ros-tooling/setup-ros@v0.6
+ - uses: actions/checkout@v4
+ - uses: ros-tooling/setup-ros@v0.7
with:
required-ros-distributions: ${{ matrix.ros_distro }}
-
- - name: Setup ros2 workspace
- run: |
- source /opt/ros/${{ matrix.ros_distro }}/setup.bash
- mkdir -p ${{github.workspace}}/ros2_ws/src
- cd ${{github.workspace}}/ros2_ws
- colcon build
-
- - uses: actions/checkout@v2
+ - run: |
+ echo "repositories:\n introlab/rtabmap:\n type: git\n url: https://github.com/introlab/rtabmap.git\n version: master\n" > /tmp/deps.repos &&\
+ cat /tmp/deps.repos
+ - uses: ros-tooling/action-ros-ci@v0.4
with:
- repository: 'introlab/rtabmap'
- path: 'ros2_ws/src/rtabmap'
-
- - uses: actions/checkout@v2
- with:
- path: 'ros2_ws/src/rtabmap_ros'
-
- - name: colcon build
- run: |
- source /opt/ros/${{ matrix.ros_distro }}/setup.bash
- cd ${{github.workspace}}/ros2_ws
- rosdep update
- rosdep install --from-paths src --ignore-src -r -y -t build_export -t test -t build -t buildtool_export -t buildtool
- colcon build --event-handlers console_direct+
+ package-name: rtabmap_ros
+ target-ros2-distro: ${{ matrix.ros_distro }}
+ vcs-repo-file-url: /tmp/deps.repos
+ rosdep-skip-keys: "${{ matrix.skip_keys }}"
diff --git a/.gitignore b/.gitignore
index 7feaafd2..5fb548e9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
.pydevproject
.settings
+__pycache__
diff --git a/README.md b/README.md
index 87fa8918..de5a2176 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
rtabmap_ros
===========
-RTAB-Map's ROS2 package (branch `ros2`). **ROS2 Foxy minimum required**: currently most nodes are ported to ROS2, however they are not all tested yet. The interface is the same than on ROS1 (parameters and topic names should still match ROS1 documentation on [rtabmap_ros](http://wiki.ros.org/rtabmap_ros)).
+RTAB-Map's ROS2 package (branch `ros2`). **ROS2 Humble minimum required**: currently most nodes are ported to ROS2. The interface is the same than on ROS1 (parameters and topic names should still match ROS1 documentation on [rtabmap_ros](http://wiki.ros.org/rtabmap_ros)).
#### CI Latest
@@ -30,17 +30,17 @@ RTAB-Map's ROS2 package (branch `ros2`). **ROS2 Foxy minimum required**: current
 |
- | ROS 2 |
+ ROS 2 |
Humble |
 |
- | Iron |
-  |
+ Jazzy |
+  |
| Rolling |
-  |
+  |
| Docker |
@@ -54,41 +54,27 @@ RTAB-Map's ROS2 package (branch `ros2`). **ROS2 Foxy minimum required**: current
# Usage
-`rtabmap.launch` is also ported to ROS2 with same arguments. If you see [ROS1 examples](http://wiki.ros.org/rtabmap_ros/Tutorials/HandHeldMapping) like this:
+* For sensor integration examples (stereo and RGB-D cameras, 3D LiDAR), see [rtabmap_examples](https://github.com/introlab/rtabmap_ros/tree/ros2/rtabmap_examples/launch) sub-folder.
+* For robot integration examples (turtlebot3 and turtlebot4, nav2 integration), see [rtabmap_demos](https://github.com/introlab/rtabmap_ros/tree/ros2/rtabmap_demos) sub-folder.
+
+## Logging
+To make RTAB-Map's logs appear ordered with RCLCPP's logs, set the following environment variables in your `.bashrc` (see official "[About Logging](https://docs.ros.org/en/humble/Concepts/Intermediate/About-Logging.html)" documentation for more info):
```bash
-roslaunch zed_wrapper zed_no_tf.launch
-
-roslaunch rtabmap_ros rtabmap.launch \
- rtabmap_args:="--delete_db_on_start" \
- rgb_topic:=/zed/zed_node/rgb/image_rect_color \
- depth_topic:=/zed/zed_node/depth/depth_registered \
- camera_info_topic:=/zed/zed_node/rgb/camera_info \
- frame_id:=base_link \
- approx_sync:=false \
- wait_imu_to_init:=true \
- imu_topic:=/zed_node/imu/data
-
+export RCUTILS_LOGGING_USE_STDOUT=1
+export RCUTILS_LOGGING_BUFFERED_STREAM=1
+# Optional, but if you like colored logs:
+export RCUTILS_COLORIZED_OUTPUT=1
```
-The ROS2 equivalent is (with those [lines](https://github.com/stereolabs/zed-ros2-wrapper/blob/b512dce6ad4565f4770273995b147122e735ca0f/zed_wrapper/config/common.yaml#L58-L60) set to false to avoid TF conflicts):
-
+## Recommended DDS
+If RTAB-Map's GUI or topic frequency feel laggy (even if processing time looks fast enough), it may be caused by the DDS. I recommend to use [Cyclone DDS](https://docs.ros.org/en/foxy/Installation/DDS-Implementations/Working-with-Eclipse-CycloneDDS.html), you can try it by adding this before launching any nodes/launch files (or add to your `.bashrc`):
```bash
-ros2 launch zed_wrapper zed.launch.py
-
-ros2 launch rtabmap_launch rtabmap.launch.py \
- rtabmap_args:="--delete_db_on_start" \
- rgb_topic:=/zed/zed_node/rgb/image_rect_color \
- depth_topic:=/zed/zed_node/depth/depth_registered \
- camera_info_topic:=/zed/zed_node/rgb/camera_info \
- frame_id:=base_link \
- approx_sync:=false \
- wait_imu_to_init:=true \
- imu_topic:=/zed/zed_node/imu/data \
- qos:=1 \
- rviz:=true
+export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp
+# Cyclone prefers multicast by default, if your router got too much spammed,
+# disable multicast with (https://github.com/ros2/rmw_cyclonedds/issues/489):
+export CYCLONEDDS_URI="0.0.0.0>>"
```
-`qos` (Quality of Service) argument should match the published topics QoS (1=RELIABLE, 2=BEST EFFORT). ROS1 was always RELIABLE.
# Installation
@@ -117,39 +103,3 @@ sudo apt install ros-$ROS_DISTRO-rtabmap-ros
colcon build --symlink-install --cmake-args -DRTABMAP_SYNC_MULTI_RGBD=ON -DRTABMAP_SYNC_USER_DATA=ON -DCMAKE_BUILD_TYPE=Release
```
-# Example with Turtlebot3
-
-1. Launch Turtlebot3 simulator:
- ```bash
- export TURTLEBOT3_MODEL=waffle
- ros2 launch turtlebot3_gazebo turtlebot3_world.launch.py
-
- export TURTLEBOT3_MODEL=waffle
- ros2 run turtlebot3_teleop teleop_keyboard
- ```
-
-2. Launch RTAB-Map:
- ```
- ros2 launch rtabmap_demos turtlebot3_scan.launch.py
-
- # OR with rtabmap.launch.py
- ros2 launch rtabmap_launch rtabmap.launch.py \
- visual_odometry:=false \
- frame_id:=base_footprint \
- subscribe_scan:=true depth:=false \
- approx_sync:=true \
- odom_topic:=/odom \
- scan_topic:=/scan \
- qos:=2 \
- args:="-d --RGBD/NeighborLinkRefining true --Reg/Strategy 1" \
- use_sim_time:=true \
- rviz:=true
- ```
-
-3. Launch navigation (`nav2_bringup` package should be installed):
- ```
- ros2 launch nav2_bringup navigation_launch.py use_sim_time:=True
- ros2 launch nav2_bringup rviz_launch.py
- ```
-
-See [rtabmap_demos/launch](https://github.com/introlab/rtabmap_ros/tree/ros2/rtabmap_demos/launch) and [rtabmap_examples/launch](https://github.com/introlab/rtabmap_ros/tree/ros2/rtabmap_examples/launch) subfolders for some other ROS2 examples with turtlebot3 in simulation and a RGB-D camera.
diff --git a/docker/README.md b/docker/README.md
index 7774f8a2..6714f614 100644
--- a/docker/README.md
+++ b/docker/README.md
@@ -2,8 +2,8 @@
* Available images on [introlab3it/rtabmap_ros](https://hub.docker.com/r/introlab3it/rtabmap_ros/):
```
- foxy, foxy-latest
humble, humble-latest
+ jazzy, jazzy-latest
```
* The `-latest` images are automatically built from latest version of `rtabmap` and `rtabmap_ros` from source (including dependencies that are not available with ROS binaries). The other images have the same version than the binaries released on ROS.
@@ -13,6 +13,7 @@
docker run -it --rm \
--user $UID \
-e ROS_HOME=/tmp/.ros \
+ -e OMP_WAIT_POLICY=passive \
--network=host \
--ipc=host \
-v ~/.ros:/tmp/.ros \
@@ -35,6 +36,7 @@
-e NVIDIA_VISIBLE_DEVICES=all \
-e NVIDIA_DRIVER_CAPABILITIES=all \
-e XAUTHORITY=$XAUTH \
+ -e OMP_WAIT_POLICY=passive \
--user $UID \
-e ROS_HOME=/tmp/.ros \
-v ~/.ros:/tmp/.ros \
diff --git a/docker/humble/latest/Dockerfile b/docker/humble/latest/Dockerfile
index b7d01701..d24ddaf5 100644
--- a/docker/humble/latest/Dockerfile
+++ b/docker/humble/latest/Dockerfile
@@ -1,20 +1,17 @@
-FROM introlab3it/rtabmap:22.04
+FROM introlab3it/rtabmap:jammy
-RUN source /ros_entrypoint.sh && \
- mkdir -p ros2_ws/src && \
- cd ros2_ws/src
+RUN mkdir -p ros2_ws/src
COPY . ros2_ws/src/rtabmap_ros
RUN source /ros_entrypoint.sh && \
cd ros2_ws && \
- export MAKEFLAGS="-j1" && \
+ export MAKEFLAGS="-j2" && \
rosdep init && \
rosdep update && \
apt-get update && \
- rosdep install --from-paths src --ignore-src -r -y -t build_export -t test -t build -t buildtool_export -t buildtool && \
- apt remove ros-$ROS_DISTRO-rtabmap* -y && \
+ rosdep install --from-paths src --ignore-src -r -y -t build_export -t test -t build -t buildtool_export -t buildtool --skip-keys="rtabmap" && \
apt-get clean && rm -rf /var/lib/apt/lists/ && \
- colcon build --event-handlers console_direct+ --install-base /opt/ros/humble --merge-install --cmake-args -DRTABMAP_SYNC_MULTI_RGBD=ON -DCMAKE_BUILD_TYPE=Release && \
+ colcon build --executor sequential --event-handlers console_direct+ --install-base /opt/ros/humble --merge-install --cmake-args --debug-find -DRTABMAP_SYNC_MULTI_RGBD=ON -DCMAKE_BUILD_TYPE=Release && \
cd && \
rm -rf ros2_ws
diff --git a/docker/humble/latest/hooks/build b/docker/humble/latest/hooks/build
deleted file mode 100644
index bffa442b..00000000
--- a/docker/humble/latest/hooks/build
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/bash
-docker build --build-arg CACHE_DATE="$(date)" --cache-from $IMAGE_NAME -f $DOCKERFILE_PATH -t $IMAGE_NAME -t $DOCKER_REPO:humble-latest .
diff --git a/docker/jazzy/Dockerfile b/docker/jazzy/Dockerfile
new file mode 100644
index 00000000..b845aa95
--- /dev/null
+++ b/docker/jazzy/Dockerfile
@@ -0,0 +1,6 @@
+FROM osrf/ros:jazzy-desktop
+# install rtabmap packages
+RUN apt-get update && apt-get install -y \
+ ros-jazzy-rtabmap \
+ ros-jazzy-rtabmap-ros \
+ && rm -rf /var/lib/apt/lists/
diff --git a/docker/jazzy/latest/Dockerfile b/docker/jazzy/latest/Dockerfile
new file mode 100644
index 00000000..e2b06208
--- /dev/null
+++ b/docker/jazzy/latest/Dockerfile
@@ -0,0 +1,19 @@
+FROM introlab3it/rtabmap:noble
+
+RUN source /ros_entrypoint.sh && \
+ mkdir -p ros2_ws/src && \
+ cd ros2_ws/src
+
+COPY . ros2_ws/src/rtabmap_ros
+
+RUN source /ros_entrypoint.sh && \
+ cd ros2_ws && \
+ export MAKEFLAGS="-j2" && \
+ rosdep init && \
+ rosdep update && \
+ apt-get update && \
+ rosdep install --from-paths src --ignore-src -r -y -t build_export -t test -t build -t buildtool_export -t buildtool --skip-keys="rtabmap grid_map_ros" && \
+ apt-get clean && rm -rf /var/lib/apt/lists/ && \
+ colcon build --event-handlers console_direct+ --install-base /opt/ros/$ROS_DISTRO --merge-install --cmake-args --debug-find -DRTABMAP_SYNC_MULTI_RGBD=ON -DCMAKE_BUILD_TYPE=Release && \
+ cd && \
+ rm -rf ros2_ws
diff --git a/docker/kilted/Dockerfile b/docker/kilted/Dockerfile
new file mode 100644
index 00000000..6c0ba3b3
--- /dev/null
+++ b/docker/kilted/Dockerfile
@@ -0,0 +1,6 @@
+FROM osrf/ros:kilted-desktop
+# install rtabmap packages
+RUN apt-get update && apt-get install -y \
+ ros-kilted-rtabmap \
+ ros-kilted-rtabmap-ros \
+ && rm -rf /var/lib/apt/lists/
diff --git a/docker/kilted/latest/Dockerfile b/docker/kilted/latest/Dockerfile
new file mode 100644
index 00000000..97cc6c46
--- /dev/null
+++ b/docker/kilted/latest/Dockerfile
@@ -0,0 +1,19 @@
+FROM introlab3it/rtabmap:noble-kilted
+
+RUN source /ros_entrypoint.sh && \
+ mkdir -p ros2_ws/src && \
+ cd ros2_ws/src
+
+COPY . ros2_ws/src/rtabmap_ros
+
+RUN source /ros_entrypoint.sh && \
+ cd ros2_ws && \
+ export MAKEFLAGS="-j2" && \
+ rosdep init && \
+ rosdep update && \
+ apt-get update && \
+ rosdep install --from-paths src --ignore-src -r -y -t build_export -t test -t build -t buildtool_export -t buildtool --skip-keys="rtabmap nav2_bringup realsense2_camera nav2_msgs grid_map_ros" && \
+ apt-get clean && rm -rf /var/lib/apt/lists/ && \
+ colcon build --event-handlers console_direct+ --install-base /opt/ros/$ROS_DISTRO --merge-install --cmake-args --debug-find -DRTABMAP_SYNC_MULTI_RGBD=ON -DCMAKE_BUILD_TYPE=Release && \
+ cd && \
+ rm -rf ros2_ws
diff --git a/rtabmap_conversions/CMakeLists.txt b/rtabmap_conversions/CMakeLists.txt
index 27bb3b22..3df9516a 100644
--- a/rtabmap_conversions/CMakeLists.txt
+++ b/rtabmap_conversions/CMakeLists.txt
@@ -5,6 +5,16 @@ if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
+if(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")
+ # issues #1285 #1288
+ find_library(
+ builtin_interfaces__rosidl_generator_c_LIB NAMES builtin_interfaces__rosidl_generator_c
+ PATHS "/opt/ros/$ENV{ROS_DISTRO}/lib"
+ NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH REQUIRED
+ )
+endif()
+
+
find_package(ament_cmake REQUIRED)
find_package(cv_bridge REQUIRED)
find_package(geometry_msgs REQUIRED)
@@ -19,7 +29,7 @@ find_package(tf2 REQUIRED)
find_package(tf2_eigen REQUIRED)
find_package(tf2_geometry_msgs REQUIRED)
-find_package(RTABMap 0.21.5 REQUIRED)
+find_package(RTABMap 0.22.0 REQUIRED)
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/include
@@ -46,6 +56,10 @@ if("$ENV{ROS_DISTRO}" STRLESS "humble")
add_definitions(-DPRE_ROS_HUMBLE)
endif()
+IF("$ENV{ROS_DISTRO}" STRLESS "iron")
+ add_definitions(-DPRE_ROS_IRON)
+ENDIF()
+
###########
## Build ##
###########
@@ -58,6 +72,10 @@ target_include_directories(rtabmap_conversions
)
ament_target_dependencies(rtabmap_conversions ${Libraries})
+IF("$ENV{ROS_DISTRO}" STRLESS "iron")
+ target_compile_definitions(rtabmap_conversions PUBLIC -DPRE_ROS_IRON)
+ENDIF()
+
#############
## Install ##
#############
diff --git a/rtabmap_conversions/include/rtabmap_conversions/MsgConversion.h b/rtabmap_conversions/include/rtabmap_conversions/MsgConversion.h
index 362a010f..3d9f5daa 100644
--- a/rtabmap_conversions/include/rtabmap_conversions/MsgConversion.h
+++ b/rtabmap_conversions/include/rtabmap_conversions/MsgConversion.h
@@ -40,7 +40,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include
#include
+#ifdef PRE_ROS_IRON
#include
+#else
+#include
+#endif
#include
#include
@@ -319,6 +323,42 @@ inline int sizeOfPointField(int datatype)
}
return -1;
}
+
+template
+typename std::map::const_iterator getClosestIterator(
+ const std::map & buffer,
+ const K & key)
+{
+ UASSERT(!buffer.empty());
+ typename std::map::const_iterator iterB = buffer.lower_bound(key);
+ typename std::map::const_iterator iterA = iterB;
+ if(iterA != buffer.begin())
+ {
+ iterA = --iterA;
+ }
+ if(iterB == buffer.end())
+ {
+ iterB = --iterB;
+ }
+ if(iterA == iterB)
+ {
+ return iterA;
+ }
+ if(iterA->first > key)
+ {
+ return iterA;
+ }
+ else if(iterB->first < key)
+ {
+ return iterB;
+ }
+ else if(key - iterA->first < iterB->first - key)
+ {
+ return iterA;
+ }
+ return iterB;
+}
+
}
#endif /* MSGCONVERSION_H_ */
diff --git a/rtabmap_conversions/package.xml b/rtabmap_conversions/package.xml
index 6aa39f82..3a9484cf 100644
--- a/rtabmap_conversions/package.xml
+++ b/rtabmap_conversions/package.xml
@@ -2,7 +2,7 @@
rtabmap_conversions
- 0.21.5
+ 0.22.0
RTAB-Map's conversions package. This package can be used to convert rtabmap_msgs's msgs into RTAB-Map's library objects.
Mathieu Labbe
Mathieu Labbe
diff --git a/rtabmap_conversions/src/MsgConversion.cpp b/rtabmap_conversions/src/MsgConversion.cpp
index 1587d06a..3c97a2c4 100644
--- a/rtabmap_conversions/src/MsgConversion.cpp
+++ b/rtabmap_conversions/src/MsgConversion.cpp
@@ -38,8 +38,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include
#include
#include
+#ifdef PRE_ROS_IRON
#include
#include
+#else
+#include
+#include
+#endif
#include
#include
#include
@@ -47,7 +52,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include
#ifdef PRE_ROS_HUMBLE
#include
-#include
+#include
#else
#include
#include
@@ -918,7 +923,8 @@ void cameraModelToROS(
UASSERT(model.R().empty() || model.R().total() == 9);
if(model.R().empty())
{
- memset(camInfo.r.data(), 0.0, 9*sizeof(double));
+ cv::Mat eye = cv::Mat::eye(3,3,CV_64FC1);
+ memcpy(camInfo.r.data(), eye.data, 9*sizeof(double));
}
else
{
@@ -929,6 +935,10 @@ void cameraModelToROS(
if(model.P().empty())
{
memset(camInfo.p.data(), 0.0, 12*sizeof(double));
+ if(!model.K_raw().empty()) {
+ model.K_raw().copyTo(cv::Mat(3,4,CV_64FC1, camInfo.p.data()).colRange(0,3));
+ camInfo.p.back() = 1.0;
+ }
}
else
{
@@ -1610,6 +1620,8 @@ std::map odomInfoToStatistics(const rtabmap::OdometryInfo &
stats.insert(std::make_pair("Odometry/LocalBundleOutliers/", info.localBundleOutliers));
stats.insert(std::make_pair("Odometry/LocalBundleConstraints/", info.localBundleConstraints));
stats.insert(std::make_pair("Odometry/LocalBundleTime/ms", info.localBundleTime*1000.0f));
+ stats.insert(std::make_pair("Odometry/localBundleAvgInlierDistance/pix", info.localBundleAvgInlierDistance));
+ stats.insert(std::make_pair("Odometry/localBundleMaxKeyFramesForInlier/", info.localBundleMaxKeyFramesForInlier));
stats.insert(std::make_pair("Odometry/KeyFrameAdded/", info.keyFrameAdded?1.0f:0.0f));
stats.insert(std::make_pair("Odometry/Interval/ms", (float)info.interval));
stats.insert(std::make_pair("Odometry/Distance/m", info.distanceTravelled));
@@ -1640,9 +1652,8 @@ std::map odomInfoToStatistics(const rtabmap::OdometryInfo &
{
if(!info.transform.isNull())
{
- rtabmap::Transform diff = info.transformGroundTruth.inverse()*info.transform;
- stats.insert(std::make_pair("Odometry/TG_error_lin/m", diff.getNorm()));
- stats.insert(std::make_pair("Odometry/TG_error_ang/deg", diff.getAngle()*180.0/CV_PI));
+ stats.insert(std::make_pair("Odometry/TG_error_lin/m", info.transformGroundTruth.getDistance(info.transform)));
+ stats.insert(std::make_pair("Odometry/TG_error_ang/deg", info.transformGroundTruth.getAngle(info.transform)*180.0/CV_PI));
}
info.transformGroundTruth.getTranslationAndEulerAngles(x,y,z,roll,pitch,yaw);
@@ -1686,18 +1697,8 @@ rtabmap::OdometryInfo odomInfoFromROS(const rtabmap_msgs::msg::OdomInfo & msg, b
info.localBundleOutliers = msg.local_bundle_outliers;
info.localBundleConstraints = msg.local_bundle_constraints;
info.localBundleTime = msg.local_bundle_time;
- UASSERT(msg.local_bundle_models.size() == msg.local_bundle_ids.size());
- UASSERT(msg.local_bundle_models.size() == msg.local_bundle_poses.size());
- for(size_t i=0; i models;
- for(size_t j=0; j models;
+ for(size_t j=0; j >::const_iterator iter=info.localBundleModels.begin();
iter!=info.localBundleModels.end();
@@ -1983,12 +1999,6 @@ rtabmap::Transform getTransform(
{
// TF ready?
rtabmap::Transform transform;
- std::string errString;
- if(!tfBuffer.canTransform(fromFrameId, toFrameId, tf2_ros::fromMsg(stamp), tf2::durationFromSec(waitForTransform), &errString))
- {
- UWARN("(can transform %s -> %s?) %s (wait_for_transform=%f)", fromFrameId.c_str(), toFrameId.c_str(), errString.c_str(), waitForTransform);
- return rtabmap::Transform();
- }
try
{
geometry_msgs::msg::TransformStamped tmp;
@@ -2169,7 +2179,7 @@ bool convertRGBDMsgs(
rtabmap::Transform localTransform = rtabmap_conversions::getTransform(frameId, !imageMsgs.empty()?imageMsgs[i]->header.frame_id:cameraInfoMsgs[i].header.frame_id, stamp, listener, waitForTransform);
if(localTransform.isNull())
{
- UERROR("TF of received image %d at time %fs is not set!", i, stamp.seconds());
+ UERROR("TF of received image for camera %d at time %fs is not set!", i, stamp.seconds());
return false;
}
// sync with odometry stamp
@@ -2588,6 +2598,24 @@ bool convertScanMsg(
double waitForTransform,
bool outputInFrameId)
{
+ // scan message validation check
+ if(scan2dMsg.angle_increment == 0.0f) {
+ UERROR("convertScanMsg: angle_increment should not be 0!");
+ return false;
+ }
+ if(scan2dMsg.range_min > scan2dMsg.range_max) {
+ UERROR("convertScanMsg: range_min (%f) should be smaller than range_max (%f)!", scan2dMsg.range_min, scan2dMsg.range_max);
+ return false;
+ }
+ if(scan2dMsg.angle_increment > 0 && scan2dMsg.angle_max < scan2dMsg.angle_min) {
+ UERROR("convertScanMsg: Angle increment (%f) should be negative if angle_min(%f) > angle_max(%f)!", scan2dMsg.angle_increment, scan2dMsg.angle_min, scan2dMsg.angle_max);
+ return false;
+ }
+ else if (scan2dMsg.angle_increment < 0 && scan2dMsg.angle_max > scan2dMsg.angle_min) {
+ UERROR("convertScanMsg: Angle increment (%f) should positive if angle_min(%f) < angle_max(%f)!", scan2dMsg.angle_increment, scan2dMsg.angle_min, scan2dMsg.angle_max);
+ return false;
+ }
+
// make sure the frame of the laser is updated during the whole scan time
rtabmap::Transform tmpT = getMovingTransform(
scan2dMsg.header.frame_id,
@@ -3035,6 +3063,25 @@ bool deskew_impl(
}
}
+ if(secFirst > 1.e18)
+ {
+ // convert nanoseconds to seconds
+ secFirst /= 1.e9;
+ secLast /= 1.e9;
+ }
+ else if(secFirst > 1.e15)
+ {
+ // convert microseconds to seconds
+ secFirst /= 1.e6;
+ secLast /= 1.e6;
+ }
+ else if(secFirst > 1.e12)
+ {
+ // convert milliseconds to seconds
+ secFirst /= 1.e3;
+ secLast /= 1.e3;
+ }
+
firstStamp = timestampToROS(secFirst);
lastStamp = timestampToROS(secLast);
}
@@ -3173,6 +3220,21 @@ bool deskew_impl(
else if(timeDatatype == 8) //float64
{
double sec = *((const double*)(&output.data[u*output.point_step]+offsetTime));
+ if(sec > 1.e18)
+ {
+ // convert nanoseconds to seconds
+ sec /= 1.e9;
+ }
+ else if(sec > 1.e15)
+ {
+ // convert microseconds to seconds
+ sec /= 1.e6;
+ }
+ else if(sec > 1.e12)
+ {
+ // sec milliseconds to seconds
+ sec /= 1.e3;
+ }
stamp = timestampToROS(sec);
}
@@ -3252,6 +3314,21 @@ bool deskew_impl(
else if(timeDatatype == 8)
{
double sec = *((const double*)(&output.data[v*output.row_step]+offsetTime));
+ if(sec > 1.e18)
+ {
+ // convert nanoseconds to seconds
+ sec /= 1.e9;
+ }
+ else if(sec > 1.e15)
+ {
+ // convert microseconds to seconds
+ sec /= 1.e6;
+ }
+ else if(sec > 1.e12)
+ {
+ // sec milliseconds to seconds
+ sec /= 1.e3;
+ }
stamp = timestampToROS(sec);
}
@@ -3308,7 +3385,7 @@ bool deskew_impl(
}
}
}
- UDEBUG("Lidar deskewing time=%fs", processingTime.elapsed());
+ UDEBUG("Lidar deskewing time=%fs (slerp=%s waitForTransform=%f)", processingTime.elapsed(), slerp?"true":"false", waitForTransform);
return true;
}
diff --git a/rtabmap_demos/CMakeLists.txt b/rtabmap_demos/CMakeLists.txt
index 2e7ce52f..4c662b4c 100644
--- a/rtabmap_demos/CMakeLists.txt
+++ b/rtabmap_demos/CMakeLists.txt
@@ -3,7 +3,7 @@ project(rtabmap_demos)
find_package(ament_cmake REQUIRED)
-install(DIRECTORY launch
+install(DIRECTORY launch config params data
DESTINATION share/${PROJECT_NAME}
)
diff --git a/rtabmap_demos/README.md b/rtabmap_demos/README.md
new file mode 100644
index 00000000..4f37769c
--- /dev/null
+++ b/rtabmap_demos/README.md
@@ -0,0 +1,85 @@
+# rtabmap_demos
++ [Outdoor Stereo VSLAM](#outdoor-stereo-vslam)
++ [Indoor 2D LiDAR and RGB-D SLAM](#indoor-2d-lidar-and-rgb-d-slam)
++ [Multi-Session Indoor 2D LiDAR and RGB-D SLAM](#multi-session-indoor-2d-lidar-and-rgb-d-slam)
++ [Find-Object with SLAM](#find-object-with-slam)
++ [Turtlebot4 Nav2, 2D LiDAR and RGB-D SLAM](#turtlebot4-nav2-2d-lidar-and-rgb-d-slam)
++ [Turtlebot3 Nav2 and 2D LiDAR SLAM](#turtlebot3-nav2-and-2d-lidar-slam)
++ [Turtlebot3 Nav2 and RGB-D SLAM](#turtlebot3-nav2-and-rgb-d-slam)
++ [Turtlebot3 Nav2, 2D LiDAR and RGB-D SLAM](#turtlebot3-nav2-2d-lidar-and-rgb-d-slam)
++ [Turtlebot3 Nav2, Fake 2D LiDAR and RGB-D SLAM](#turtlebot3-nav2-fake-2d-lidar-and-rgb-d-slam)
++ [Champ Quadruped Nav2, Elevation Map and VSLAM](#champ-quadruped-nav2-elevation-map-and-vslam)
++ [Clearpath Husky Nav2, 2D LiDAR and RGB-D SLAM](#clearpath-husky-nav2-2d-lidar-and-rgb-d-slam)
++ [Clearpath Husky Nav2, 3D LiDAR and RGB-D SLAM](#clearpath-husky-nav2-3d-lidar-and-rgb-d-slam)
++ [Clearpath Husky Nav2, 3D LiDAR Assembling and RGB-D SLAM](#clearpath-husky-nav2-3d-lidar-assembling-and-rgb-d-slam)
++ [Isaac Sim Nav2 and Stereo SLAM](#isaac-sim-nav2-and-stereo-slam)
++ [Isaac Sim Nav2 and RGB-D VSLAM](#isaac-sim-nav2-and-rgb-d-vslam)
+
+### Outdoor Stereo VSLAM
+[stereo_outdoor_demo.launch.py](https://github.com/introlab/rtabmap_ros/blob/ros2/rtabmap_demos/launch/stereo_outdoor_demo.launch.py) ([Video](https://youtu.be/qpTS7kg9J3A))
+
+
+
+### Indoor 2D LiDAR and RGB-D SLAM
+[robot_mapping_demo.launch.py](https://github.com/introlab/rtabmap_ros/blob/ros2/rtabmap_demos/launch/robot_mapping_demo.launch.py) (Videos: [rtabmap_viz](https://youtu.be/c0qrEd5rR7M), [rviz](https://youtu.be/MQoSDpAsqps))
+
+
+
+### Multi-Session Indoor 2D LiDAR and RGB-D SLAM
+[multisession_mapping_demo.launch.py](https://github.com/introlab/rtabmap_ros/blob/ros2/rtabmap_demos/launch/multisession_mapping_demo.launch.py) ([Video](https://youtu.be/XrnyhaxPCro))
+
+
+
+### Find-Object with SLAM
+[find_object_demo.launch.py](https://github.com/introlab/rtabmap_ros/blob/ros2/rtabmap_demos/launch/find_object_demo.launch.py) ([Video](https://youtu.be/o1GSQanY-Do))
+
+
+
+### Turtlebot4 Nav2, 2D LiDAR and RGB-D SLAM
+[turtlebot4_sim_demo.launch.py](https://github.com/introlab/rtabmap_ros/blob/ros2/rtabmap_demos/launch/turtlebot4/turtlebot4_sim_demo.launch.py)
+
+
+### Turtlebot3 Nav2 and 2D LiDAR SLAM
+[turtlebot3_sim_scan_demo.launch.py](https://github.com/introlab/rtabmap_ros/blob/ros2/rtabmap_demos/launch/turtlebot3/turtlebot3_sim_scan_demo.launch.py)
+
+
+### Turtlebot3 Nav2 and RGB-D SLAM
+[turtlebot3_sim_rgbd_demo.launch.py](https://github.com/introlab/rtabmap_ros/blob/ros2/rtabmap_demos/launch/turtlebot3/turtlebot3_sim_rgbd_demo.launch.py)
+
+
+### Turtlebot3 Nav2, 2D LiDAR and RGB-D SLAM
+[turtlebot3_sim_rgbd_scan_demo.launch.py](https://github.com/introlab/rtabmap_ros/blob/ros2/rtabmap_demos/launch/turtlebot3/turtlebot3_sim_rgbd_scan_demo.launch.py)
+
+
+### Turtlebot3 Nav2, Fake 2D LiDAR and RGB-D SLAM
+[turtlebot3_sim_rgbd_fake_scan_demo.launch.py](https://github.com/introlab/rtabmap_ros/blob/ros2/rtabmap_demos/launch/turtlebot3/turtlebot3_sim_rgbd_fake_scan_demo.launch.py)
+
+ * Red: Scan generated from camera's depth.
+ * Orange: Locally assembled scans used for proximity detection.
+ * Yellow: The map.
+
+
+### Champ Quadruped Nav2, Elevation Map and VSLAM
+[champ_sim_vslam.launch.py](https://github.com/introlab/rtabmap_ros/blob/ros2/rtabmap_demos/launch/champ/champ_sim_vslam.launch.py)
+
+
+### Clearpath Husky Nav2, 2D LiDAR and RGB-D SLAM
+[husky_sim_scan2d_demo.launch.py](https://github.com/introlab/rtabmap_ros/blob/ros2/rtabmap_demos/launch/husky/husky_sim_scan2d_demo.launch.py)
+
+
+### Clearpath Husky Nav2, 3D LiDAR and RGB-D SLAM
+[husky_sim_scan3d_demo.launch.py](https://github.com/introlab/rtabmap_ros/blob/ros2/rtabmap_demos/launch/husky/husky_sim_scan3d_demo.launch.py)
+
+
+### Clearpath Husky Nav2, 3D LiDAR Assembling and RGB-D SLAM
+[husky_sim_scan3d_assemble_demo.launch.py](https://github.com/introlab/rtabmap_ros/blob/ros2/rtabmap_demos/launch/husky/husky_sim_scan3d_assemble_demo.launch.py)
+
+
+### Isaac Sim Nav2 and Stereo SLAM
+[isaac_sim_vslam_demo.launch.py](https://github.com/introlab/rtabmap_ros/blob/ros2/rtabmap_demos/launch/isaac/isaac_sim_vslam_demo.launch.py)
+
+
+### Isaac Sim Nav2 and RGB-D VSLAM
+[isaac_sim_vslam_demo.launch.py](https://github.com/introlab/rtabmap_ros/blob/ros2/rtabmap_demos/launch/isaac/isaac_sim_vslam_demo.launch.py) stereo:=false vo:=rtabmap
+
+
diff --git a/rtabmap_demos/config/demo_robot_mapping.rviz b/rtabmap_demos/config/demo_robot_mapping.rviz
new file mode 100644
index 00000000..40e28170
--- /dev/null
+++ b/rtabmap_demos/config/demo_robot_mapping.rviz
@@ -0,0 +1,391 @@
+Panels:
+ - Class: rviz_common/Displays
+ Help Height: 0
+ Name: Displays
+ Property Tree Widget:
+ Expanded:
+ - /Global Options1
+ - /Status1
+ Splitter Ratio: 0.5
+ Tree Height: 627
+ - Class: rviz_common/Selection
+ Name: Selection
+ - Class: rviz_common/Tool Properties
+ Expanded:
+ - /2D Goal Pose1
+ - /Publish Point1
+ Name: Tool Properties
+ Splitter Ratio: 0.5886790156364441
+ - Class: rviz_common/Views
+ Expanded:
+ - /Current View1
+ Name: Views
+ Splitter Ratio: 0.5
+ - Class: rviz_common/Time
+ Experimental: false
+ Name: Time
+ SyncMode: 0
+ SyncSource: MapCloud
+Visualization Manager:
+ Class: ""
+ Displays:
+ - Alpha: 0.5
+ Cell Size: 1
+ Class: rviz_default_plugins/Grid
+ Color: 160; 160; 164
+ Enabled: true
+ Line Style:
+ Line Width: 0.029999999329447746
+ Value: Lines
+ Name: Grid
+ Normal Cell Count: 0
+ Offset:
+ X: 0
+ Y: 0
+ Z: 0
+ Plane: XY
+ Plane Cell Count: 10
+ Reference Frame:
+ Value: true
+ - Alpha: 0.699999988079071
+ Class: rviz_default_plugins/Map
+ Color Scheme: map
+ Draw Behind: false
+ Enabled: true
+ Name: Map
+ Topic:
+ Depth: 5
+ Durability Policy: Volatile
+ Filter size: 10
+ History Policy: Keep Last
+ Reliability Policy: Reliable
+ Value: /map
+ Update Topic:
+ Depth: 5
+ Durability Policy: Volatile
+ History Policy: Keep Last
+ Reliability Policy: Reliable
+ Value: /map_updates
+ Use Timestamp: false
+ Value: true
+ - Class: rviz_default_plugins/TF
+ Enabled: true
+ Frame Timeout: 15
+ Frames:
+ All Enabled: true
+ az3_base_link:
+ Value: true
+ az3_odom:
+ Value: true
+ base_footprint:
+ Value: true
+ base_laser_link:
+ Value: true
+ base_link:
+ Value: true
+ map:
+ Value: true
+ odom:
+ Value: true
+ stereo_camera:
+ Value: true
+ stereo_camera_base:
+ Value: true
+ wheelLB_linkWheel_link:
+ Value: true
+ wheelLB_wheel_link:
+ Value: true
+ wheelLF_linkWheel_link:
+ Value: true
+ wheelLF_wheel_link:
+ Value: true
+ wheelRB_linkWheel_link:
+ Value: true
+ wheelRB_wheel_link:
+ Value: true
+ wheelRF_linkWheel_link:
+ Value: true
+ wheelRF_wheel_link:
+ Value: true
+ Marker Scale: 1
+ Name: TF
+ Show Arrows: true
+ Show Axes: true
+ Show Names: false
+ Tree:
+ map:
+ odom:
+ base_footprint:
+ base_link:
+ base_laser_link:
+ {}
+ stereo_camera_base:
+ stereo_camera:
+ {}
+ wheelLB_linkWheel_link:
+ wheelLB_wheel_link:
+ {}
+ wheelLF_linkWheel_link:
+ wheelLF_wheel_link:
+ {}
+ wheelRB_linkWheel_link:
+ wheelRB_wheel_link:
+ {}
+ wheelRF_linkWheel_link:
+ wheelRF_wheel_link:
+ {}
+ Update Interval: 0
+ Value: true
+ - Alpha: 1
+ Autocompute Intensity Bounds: true
+ Autocompute Value Bounds:
+ Max Value: 10
+ Min Value: -10
+ Value: true
+ Axis: Z
+ Channel Name: intensity
+ Class: rviz_default_plugins/LaserScan
+ Color: 237; 51; 59
+ Color Transformer: FlatColor
+ Decay Time: 0
+ Enabled: true
+ Invert Rainbow: false
+ Max Color: 255; 255; 255
+ Max Intensity: 4096
+ Min Color: 0; 0; 0
+ Min Intensity: 0
+ Name: LaserScan
+ Position Transformer: XYZ
+ Selectable: true
+ Size (Pixels): 3
+ Size (m): 0.009999999776482582
+ Style: Points
+ Topic:
+ Depth: 5
+ Durability Policy: Volatile
+ Filter size: 10
+ History Policy: Keep Last
+ Reliability Policy: Reliable
+ Value: /jn0/base_scan
+ Use Fixed Frame: true
+ Use rainbow: true
+ Value: true
+ - Alpha: 1
+ Autocompute Intensity Bounds: true
+ Autocompute Value Bounds:
+ Max Value: 10
+ Min Value: -10
+ Value: true
+ Axis: Z
+ Channel Name: intensity
+ Class: rtabmap_rviz_plugins/MapCloud
+ Cloud decimation: 4
+ Cloud from scan: false
+ Cloud max depth (m): 4
+ Cloud min depth (m): 0
+ Cloud voxel size (m): 0.009999999776482582
+ Color: 255; 255; 255
+ Color Transformer: RGB8
+ Download graph: false
+ Download map: false
+ Download namespace: rtabmap
+ Enabled: true
+ Filter ceiling (m): 0
+ Filter floor (m): 0
+ Invert Rainbow: false
+ Max Color: 255; 255; 255
+ Max Intensity: 4096
+ Min Color: 0; 0; 0
+ Min Intensity: 0
+ Name: MapCloud
+ Node filtering angle (degrees): 30
+ Node filtering radius (m): 0
+ Position Transformer: XYZ
+ Size (Pixels): 3
+ Size (m): 0.009999999776482582
+ Style: Points
+ Topic:
+ Depth: 5
+ Durability Policy: Volatile
+ Filter size: 10
+ History Policy: Keep Last
+ Reliability Policy: Reliable
+ Value: /mapData
+ Use Fixed Frame: true
+ Use rainbow: true
+ Value: true
+ - Alpha: 1
+ Class: rtabmap_rviz_plugins/MapGraph
+ Enabled: true
+ Global loop closure: 255; 0; 0
+ Landmark: 0; 128; 0
+ Local loop closure: 255; 255; 0
+ Merged neighbor: 255; 170; 0
+ Name: MapGraph
+ Neighbor: 0; 0; 255
+ Topic:
+ Depth: 5
+ Durability Policy: Volatile
+ Filter size: 10
+ History Policy: Keep Last
+ Reliability Policy: Reliable
+ Value: /mapGraph
+ User: 255; 0; 0
+ Value: true
+ Virtual: 255; 0; 255
+ - Class: rviz_common/Group
+ Displays:
+ - Alpha: 1
+ Autocompute Intensity Bounds: true
+ Autocompute Value Bounds:
+ Max Value: 10
+ Min Value: -10
+ Value: true
+ Axis: Z
+ Channel Name: intensity
+ Class: rviz_default_plugins/PointCloud2
+ Color: 0; 255; 0
+ Color Transformer: FlatColor
+ Decay Time: 0
+ Enabled: true
+ Invert Rainbow: false
+ Max Color: 255; 255; 255
+ Max Intensity: 4096
+ Min Color: 0; 0; 0
+ Min Intensity: 0
+ Name: Current Frame
+ Position Transformer: XYZ
+ Selectable: true
+ Size (Pixels): 3
+ Size (m): 0.009999999776482582
+ Style: Points
+ Topic:
+ Depth: 5
+ Durability Policy: Volatile
+ Filter size: 10
+ History Policy: Keep Last
+ Reliability Policy: Reliable
+ Value: /odom_last_frame
+ Use Fixed Frame: true
+ Use rainbow: true
+ Value: true
+ - Alpha: 1
+ Autocompute Intensity Bounds: true
+ Autocompute Value Bounds:
+ Max Value: 10
+ Min Value: -10
+ Value: true
+ Axis: Z
+ Channel Name: intensity
+ Class: rviz_default_plugins/PointCloud2
+ Color: 0; 255; 0
+ Color Transformer: RGB8
+ Decay Time: 0
+ Enabled: true
+ Invert Rainbow: false
+ Max Color: 255; 255; 255
+ Max Intensity: 4096
+ Min Color: 0; 0; 0
+ Min Intensity: 0
+ Name: Local Feature Map
+ Position Transformer: XYZ
+ Selectable: true
+ Size (Pixels): 3
+ Size (m): 0.009999999776482582
+ Style: Points
+ Topic:
+ Depth: 5
+ Durability Policy: Volatile
+ Filter size: 10
+ History Policy: Keep Last
+ Reliability Policy: Reliable
+ Value: /odom_local_map
+ Use Fixed Frame: true
+ Use rainbow: true
+ Value: true
+ Enabled: true
+ Name: VO
+ Enabled: true
+ Global Options:
+ Background Color: 48; 48; 48
+ Fixed Frame: map
+ Frame Rate: 30
+ Name: root
+ Tools:
+ - Class: rviz_default_plugins/Interact
+ Hide Inactive Objects: true
+ - Class: rviz_default_plugins/MoveCamera
+ - Class: rviz_default_plugins/Select
+ - Class: rviz_default_plugins/FocusCamera
+ - Class: rviz_default_plugins/Measure
+ Line color: 128; 128; 0
+ - Class: rviz_default_plugins/SetInitialPose
+ Covariance x: 0.25
+ Covariance y: 0.25
+ Covariance yaw: 0.06853891909122467
+ Topic:
+ Depth: 5
+ Durability Policy: Volatile
+ History Policy: Keep Last
+ Reliability Policy: Reliable
+ Value: /initialpose
+ - Class: rviz_default_plugins/SetGoal
+ Topic:
+ Depth: 5
+ Durability Policy: Volatile
+ History Policy: Keep Last
+ Reliability Policy: Reliable
+ Value: /goal_pose
+ - Class: rviz_default_plugins/PublishPoint
+ Single click: true
+ Topic:
+ Depth: 5
+ Durability Policy: Volatile
+ History Policy: Keep Last
+ Reliability Policy: Reliable
+ Value: /clicked_point
+ Transformation:
+ Current:
+ Class: rviz_default_plugins/TF
+ Value: true
+ Views:
+ Current:
+ Class: rviz_default_plugins/Orbit
+ Distance: 7.2877197265625
+ Enable Stereo Rendering:
+ Stereo Eye Separation: 0.05999999865889549
+ Stereo Focal Distance: 1
+ Swap Stereo Eyes: false
+ Value: false
+ Focal Point:
+ X: 0
+ Y: 0
+ Z: 0
+ Focal Shape Fixed Size: false
+ Focal Shape Size: 0.05000000074505806
+ Invert Z Axis: false
+ Name: Current View
+ Near Clip Distance: 0.009999999776482582
+ Pitch: 0.8703982830047607
+ Target Frame: base_footprint
+ Value: Orbit (rviz)
+ Yaw: 3.7535834312438965
+ Saved: ~
+Window Geometry:
+ Displays:
+ collapsed: false
+ Height: 846
+ Hide Left Dock: false
+ Hide Right Dock: false
+ QMainWindow State: 000000ff00000000fd000000040000000000000156000002b0fc0200000008fb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000003d000002b0000000c900fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261000000010000010f000002b0fc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073010000003d000002b0000000a400fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e10000019700000003000006330000003efc0100000002fb0000000800540069006d0065010000000000000633000002fb00fffffffb0000000800540069006d00650100000000000004500000000000000000000003c2000002b000000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000
+ Selection:
+ collapsed: false
+ Time:
+ collapsed: false
+ Tool Properties:
+ collapsed: false
+ Views:
+ collapsed: false
+ Width: 1587
+ X: 214
+ Y: 77
diff --git a/rtabmap_demos/config/find_object.ini b/rtabmap_demos/config/find_object.ini
new file mode 100644
index 00000000..515dbdf9
--- /dev/null
+++ b/rtabmap_demos/config/find_object.ini
@@ -0,0 +1,188 @@
+[General]
+windowGeometry=@ByteArray(\x1\xd9\xd0\xcb\0\x3\0\0\0\0\x4\x34\0\0\x1\x8e\0\0\x6\x9a\0\0\x4\x5\0\0\x4\x34\0\0\x1\xb3\0\0\x6\x9a\0\0\x4\x5\0\0\0\0\0\0\0\0\a\x80\0\0\x4\x34\0\0\x1\xb3\0\0\x6\x9a\0\0\x4\x5)
+windowState=@ByteArray(\0\0\0\xff\0\0\0\0\xfd\0\0\0\x3\0\0\0\0\0\0\0\xda\0\0\x2'\xfc\x2\0\0\0\x1\xfb\0\0\0$\0\x64\0o\0\x63\0k\0W\0i\0\x64\0g\0\x65\0t\0_\0o\0\x62\0j\0\x65\0\x63\0t\0s\x1\0\0\0\x16\0\0\x2'\0\0\0\xc4\0\xff\xff\xff\0\0\0\x1\0\0\x1h\0\0\x1\x86\xfc\x2\0\0\0\x2\xfb\0\0\0*\0\x64\0o\0\x63\0k\0W\0i\0\x64\0g\0\x65\0t\0_\0p\0\x61\0r\0\x61\0m\0\x65\0t\0\x65\0r\0s\0\0\0\0\x16\0\0\x1\x86\0\0\0\xa8\0\xff\xff\xff\xfb\0\0\0*\0\x64\0o\0\x63\0k\0W\0i\0\x64\0g\0\x65\0t\0_\0s\0t\0\x61\0t\0i\0s\0t\0i\0\x63\0s\0\0\0\0\0\xff\xff\xff\xff\0\0\x1\x35\0\xff\xff\xff\0\0\0\x3\0\0\0\0\0\0\0\0\xfc\x1\0\0\0\x1\xfb\0\0\0\x1e\0\x64\0o\0\x63\0k\0W\0i\0\x64\0g\0\x65\0t\0_\0p\0l\0o\0t\0\0\0\0\0\xff\xff\xff\xff\0\0\0\x87\0\xff\xff\xff\0\0\x1\x87\0\0\x2'\0\0\0\x4\0\0\0\x4\0\0\0\b\0\0\0\b\xfc\0\0\0\0)
+
+[Camera]
+1deviceId=0
+2imageWidth=640
+3imageHeight=480
+4imageRate=0
+5mediaPath=
+6useTcpCamera=false
+7IP=127.0.0.1
+8port=5000
+9queueSize=1
+
+[Feature2D]
+1Detector="5:Dense;Fast;GFTT;MSER;ORB;SIFT;Star;SURF;BRISK;AGAST;KAZE;AKAZE;SuperPointTorch"
+2Descriptor="2:Brief;ORB;SIFT;SURF;BRISK;FREAK;KAZE;AKAZE;LUCID;LATCH;DAISY;SuperPointTorch"
+3MaxFeatures=0
+4Affine=false
+5AffineCount=6
+6SubPix=false
+7SubPixWinSize=3
+8SubPixIterations=30
+9SubPixEps=0.02
+AGAST_nonmaxSuppression=true
+AGAST_threshold=10
+AKAZE_descriptorChannels=3
+AKAZE_descriptorSize=0
+AKAZE_nOctaveLayers=4
+AKAZE_nOctaves=4
+AKAZE_threshold=0.001
+BRISK_octaves=3
+BRISK_patternScale=1
+BRISK_thresh=30
+Brief_bytes=32
+DAISY_interpolation=true
+DAISY_q_hist=8
+DAISY_q_radius=3
+DAISY_q_theta=8
+DAISY_radius=15
+DAISY_use_orientation=false
+Dense_featureScaleLevels=1
+Dense_featureScaleMul=0.1
+Dense_initFeatureScale=1
+Dense_initImgBound=0
+Dense_initXyStep=6
+Dense_varyImgBoundWithScale=false
+Dense_varyXyStepWithScale=true
+FREAK_nOctaves=4
+FREAK_orientationNormalized=true
+FREAK_patternScale=22
+FREAK_scaleNormalized=true
+Fast_gpu=false
+Fast_keypointsRatio=0.05
+Fast_maxNpoints=5000
+Fast_nonmaxSuppression=true
+Fast_threshold=10
+GFTT_blockSize=3
+GFTT_k=0.04
+GFTT_maxCorners=1000
+GFTT_minDistance=1
+GFTT_qualityLevel=0.01
+GFTT_useHarrisDetector=false
+KAZE_extended=false
+KAZE_nOctaveLayers=4
+KAZE_nOctaves=4
+KAZE_threshold=0.001
+KAZE_upright=false
+LATCH_bytes=32
+LATCH_half_ssd_size=3
+LATCH_rotationInvariance=true
+LUCID_blur_kernel=2
+LUCID_kernel=1
+MSER_areaThreshold=1.01
+MSER_delta=5
+MSER_edgeBlurSize=5
+MSER_maxArea=14400
+MSER_maxEvolution=200
+MSER_maxVariation=0.25
+MSER_minArea=60
+MSER_minDiversity=0.2
+MSER_minMargin=0.003
+ORB_WTA_K=2
+ORB_blurForDescriptor=false
+ORB_edgeThreshold=31
+ORB_firstLevel=0
+ORB_gpu=false
+ORB_nFeatures=500
+ORB_nLevels=8
+ORB_patchSize=31
+ORB_scaleFactor=1.2
+ORB_scoreType=0
+SIFT_contrastThreshold=0.04
+SIFT_edgeThreshold=10
+SIFT_nOctaveLayers=3
+SIFT_nfeatures=0
+SIFT_rootSIFT=false
+SIFT_sigma=1.6
+SURF_extended=true
+SURF_gpu=false
+SURF_hessianThreshold=600
+SURF_keypointsRatio=0.01
+SURF_nOctaveLayers=2
+SURF_nOctaves=4
+SURF_upright=false
+Star_lineThresholdBinarized=8
+Star_lineThresholdProjected=10
+Star_maxSize=45
+Star_responseThreshold=30
+Star_suppressNonmaxSize=5
+SuperPointTorch_NMS=true
+SuperPointTorch_NMS_radius=4
+SuperPointTorch_cuda=false
+SuperPointTorch_modelPath=
+SuperPointTorch_threshold=0.2
+
+[%General]
+autoPauseOnDetection=false
+autoScreenshotPath=
+autoScroll=true
+autoStartCamera=false
+autoUpdateObjects=true
+controlsShown=false
+debug=false
+imageFormats=*.png *.jpg *.bmp *.tiff *.ppm
+invertedSearch=true
+mirrorView=false
+multiDetection=false
+multiDetectionRadius=30
+nextObjID=9
+port=0
+sendNoObjDetectedEvents=false
+threads=1
+videoFormats=*.avi *.m4v *.mp4
+vocabularyFixed=false
+vocabularyIncremental=false
+vocabularyUpdateMinWords=2000
+
+[Homography]
+allCornersVisible=false
+confidence=0.995
+homographyComputed=true
+ignoreWhenAllInliers=false
+maxIterations=2000
+method="1:LMEDS;RANSAC;RHO"
+minAngle=50
+minimumInliers=10
+opticalFlow=false
+opticalFlowEps=0.01
+opticalFlowIterations=30
+opticalFlowMaxLevel=3
+opticalFlowWinSize=16
+ransacReprojThr=5
+rectBorderWidth=4
+
+[NearestNeighbor]
+1Strategy="1:Linear;KDTree;KMeans;Composite;Autotuned;Lsh;BruteForce"
+2Distance_type="0:EUCLIDEAN_L2;MANHATTAN_L1;MINKOWSKI;MAX;HIST_INTERSECT;HELLINGER;CHI_SQUARE_CS;KULLBACK_LEIBLER_KL;HAMMING"
+3nndrRatioUsed=true
+4nndrRatio=0.8
+5minDistanceUsed=false
+6minDistance=1.6
+7ConvertBinToFloat=false
+7search_checks=32
+8search_eps=0
+9search_sorted=true
+Autotuned_build_weight=0.01
+Autotuned_memory_weight=0
+Autotuned_sample_fraction=0.1
+Autotuned_target_precision=0.8
+BruteForce_gpu=false
+Composite_branching=32
+Composite_cb_index=0.2
+Composite_centers_init="0:RANDOM;GONZALES;KMEANSPP"
+Composite_iterations=11
+Composite_trees=4
+KDTree_trees=4
+KMeans_branching=32
+KMeans_cb_index=0.2
+KMeans_centers_init="0:RANDOM;GONZALES;KMEANSPP"
+KMeans_iterations=11
+Lsh_key_size=20
+Lsh_multi_probe_level=2
+Lsh_table_number=12
+search_checks=32
+search_eps=0
+search_sorted=true
diff --git a/rtabmap_demos/data/books/4.png b/rtabmap_demos/data/books/4.png
new file mode 100644
index 00000000..5e5f9535
Binary files /dev/null and b/rtabmap_demos/data/books/4.png differ
diff --git a/rtabmap_demos/data/books/5.png b/rtabmap_demos/data/books/5.png
new file mode 100644
index 00000000..ffcc29c7
Binary files /dev/null and b/rtabmap_demos/data/books/5.png differ
diff --git a/rtabmap_demos/data/books/6.png b/rtabmap_demos/data/books/6.png
new file mode 100644
index 00000000..afe1e88b
Binary files /dev/null and b/rtabmap_demos/data/books/6.png differ
diff --git a/rtabmap_demos/data/books/7.png b/rtabmap_demos/data/books/7.png
new file mode 100644
index 00000000..e01fc98f
Binary files /dev/null and b/rtabmap_demos/data/books/7.png differ
diff --git a/rtabmap_demos/data/books/8.png b/rtabmap_demos/data/books/8.png
new file mode 100644
index 00000000..4c04a865
Binary files /dev/null and b/rtabmap_demos/data/books/8.png differ
diff --git a/rtabmap_demos/launch/champ/champ_sim_vslam.launch.py b/rtabmap_demos/launch/champ/champ_sim_vslam.launch.py
new file mode 100644
index 00000000..3432f02b
--- /dev/null
+++ b/rtabmap_demos/launch/champ/champ_sim_vslam.launch.py
@@ -0,0 +1,87 @@
+
+# Requires installed https://github.com/chvmp/champ/tree/ros2
+#
+# Example:
+# 1) Launch simulator (gazebo, nav2 and rtabmap):
+# $ ros2 launch rtabmap_demos champ_sim_vslam.launch.py
+#
+# Note that the first time we launch gazebo, it may take a
+# while to download all assets. You may need to restart the
+# launch to make sure all nodes are started after the sim is ready.
+#
+# 2) Move the robot:
+# b) By sending goals with RVIZ's "Nav2 Goal" button in action bar.
+# a) By teleoperating:
+# $ ros2 launch champ_teleop teleop.launch.py
+#
+
+from launch import LaunchDescription
+from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription, TimerAction, OpaqueFunction
+from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
+from launch.launch_description_sources import PythonLaunchDescriptionSource
+from launch_ros.substitutions import FindPackageShare
+
+import os
+
+def launch_setup(context, *args, **kwargs):
+
+ sim_launch_path = PathJoinSubstitution(
+ [FindPackageShare('champ_config'), 'launch', 'gazebo.launch.py']
+ )
+
+ gz_pkg_share = FindPackageShare(package="champ_gazebo").find("champ_gazebo")
+
+ champ_vslam = PathJoinSubstitution(
+ [FindPackageShare('rtabmap_demos'), 'launch', 'champ', 'champ_vslam.launch.py']
+ )
+
+ rviz = LaunchConfiguration('rviz').perform(context)
+ world = LaunchConfiguration('world').perform(context)
+
+ return [
+ TimerAction(
+ actions = [
+ IncludeLaunchDescription(
+ PythonLaunchDescriptionSource(champ_vslam),
+ launch_arguments={
+ 'use_sim_time': 'true',
+ 'rviz': rviz,
+ 'rtabmap_viz': LaunchConfiguration('rtabmap_viz'),
+ 'localization': LaunchConfiguration('localization'),
+ }.items()
+ )], period = 5.0), # Wait 5 sec to make sure simulator is ready
+
+ IncludeLaunchDescription(
+ PythonLaunchDescriptionSource(sim_launch_path),
+ launch_arguments={'rviz': 'false',
+ 'world': os.path.join(gz_pkg_share, f"worlds/{world}.world")}.items()
+ ),
+ ]
+
+def generate_launch_description():
+
+ return LaunchDescription([
+
+ DeclareLaunchArgument(
+ name='rviz',
+ default_value='true',
+ description='Run rviz'
+ ),
+
+ DeclareLaunchArgument(
+ name='rtabmap_viz',
+ default_value='true',
+ description='Run rtabmap_viz'
+ ),
+
+ DeclareLaunchArgument(
+ 'localization', default_value='false', choices=['true', 'false'],
+ description='Launch rtabmap in localization mode (a map should have been already created).'),
+
+ DeclareLaunchArgument(
+ 'world', default_value='playground',
+ choices=['outdoor', 'playground'],
+ description='Champ gazebo world.'),
+
+ OpaqueFunction(function=launch_setup)
+ ])
diff --git a/rtabmap_demos/launch/champ/champ_vslam.launch.py b/rtabmap_demos/launch/champ/champ_vslam.launch.py
new file mode 100644
index 00000000..7b527168
--- /dev/null
+++ b/rtabmap_demos/launch/champ/champ_vslam.launch.py
@@ -0,0 +1,179 @@
+
+# Similar to gazebo example on https://github.com/chvmp/champ/tree/ros2, we can do:
+#
+# Run the Gazebo environment:
+# $ ros2 launch champ_config gazebo.launch.py
+#
+# Run Nav2's navigation and rtabmap:
+# $ ros2 launch rtabmap_demos champ_vslam.launch.py use_sim_time:=true rviz:=true rtabmap_viz:=true
+#
+# When a map is already created using command above, we can re-launch in localization-only mode with:
+# $ ros2 launch rtabmap_demos champ_vslam.launch.py use_sim_time:=true rviz:=true rtabmap_viz:=true localization:=true
+#
+
+from launch import LaunchDescription
+from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription, OpaqueFunction
+from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
+from launch.launch_description_sources import PythonLaunchDescriptionSource
+from launch.conditions import IfCondition, UnlessCondition
+from launch_ros.substitutions import FindPackageShare
+from launch_ros.actions import Node
+
+def launch_setup(context, *args, **kwargs):
+
+ localization = LaunchConfiguration('localization')
+
+ navigation_launch_path = PathJoinSubstitution(
+ [FindPackageShare('nav2_bringup'), 'launch', 'navigation_launch.py']
+ )
+
+ nav2_params_file = PathJoinSubstitution(
+ [FindPackageShare('rtabmap_demos'), 'params', 'champ_nav2_params.yaml']
+ )
+
+ rviz_config_path = PathJoinSubstitution(
+ [FindPackageShare('champ_navigation'), 'rviz', 'navigation.rviz']
+ )
+
+ use_sim_time = LaunchConfiguration("use_sim_time")
+
+ # With the simulator, the imu is not published fast enough
+ # and have a huge delay, disabling imu usage from VO
+ use_imu = use_sim_time.perform(context) in ["false", "False"]
+
+ vslam_params ={
+ 'frame_id':'base_link',
+ 'guess_frame_id':'odom',
+ 'approx_sync': False,
+ 'use_sim_time':use_sim_time,
+ 'subscribe_rgbd':True,
+ 'subscribe_odom_info':True,
+ 'use_action_for_goal':True,
+ 'wait_imu_to_init': use_imu,
+ 'wait_for_transform': 0.5,
+ # RTAB-Map's parameters should be strings
+ 'Grid/DepthDecimation': '1',
+ 'Grid/RangeMax': '2',
+ 'GridGlobal/MinSize': '20',
+ 'Grid/MinClusterSize': '20',
+ 'Grid/MaxObstacleHeight': '2',
+ 'Odom/ResetCountdown': '2', # sim is very flaky
+ 'Kp/RoiRatios': '0.0 0.0 0.0 0.4' # ignore ground for loop closure detection (sim uses a very repetitive texture)
+ }
+
+ vslam_remappings=[('imu', 'imu/data/filtered'),
+ ('odom', 'vo')]
+
+ return [
+ IncludeLaunchDescription(
+ PythonLaunchDescriptionSource(navigation_launch_path),
+ launch_arguments={
+ 'use_sim_time': use_sim_time,
+ 'params_file': nav2_params_file
+ }.items()
+ ),
+
+ Node(
+ package='rviz2',
+ executable='rviz2',
+ name='rviz2',
+ output='screen',
+ arguments=['-d', rviz_config_path],
+ condition=IfCondition(LaunchConfiguration("rviz")),
+ parameters=[{'use_sim_time': use_sim_time}]
+ ),
+
+ # compute imu orientation
+ Node(
+ package='imu_filter_madgwick', executable='imu_filter_madgwick_node', output='screen',
+ parameters=[{
+ 'use_mag':False,
+ 'world_frame':'enu',
+ 'publish_tf':False}],
+ remappings=[
+ ('imu/data_raw', 'imu/data'),
+ ('imu/data', 'imu/data/filtered')
+ ]),
+
+ # VSLAM nodes:
+ Node(
+ package='rtabmap_sync', executable='rgbd_sync', output='screen',
+ parameters=[vslam_params],
+ remappings=[('rgb/image', '/camera/image_raw'),
+ ('rgb/camera_info', '/camera/camera_info'),
+ ('depth/image', '/camera/depth/image_raw')]),
+
+ Node(
+ package='rtabmap_odom', executable='rgbd_odometry', output='screen',
+ parameters=[vslam_params, {'odom_frame_id': 'vo'}],
+ remappings=vslam_remappings,
+ arguments=["--ros-args", "--log-level", 'info']),
+
+ # SLAM Mode:
+ Node(
+ condition=UnlessCondition(localization),
+ package='rtabmap_slam', executable='rtabmap', output='screen',
+ parameters=[vslam_params],
+ remappings=vslam_remappings,
+ arguments=['-d']),
+
+ # Localization mode:
+ Node(
+ condition=IfCondition(localization),
+ package='rtabmap_slam', executable='rtabmap', output='screen',
+ parameters=[vslam_params,
+ {'Mem/IncrementalMemory':'False',
+ 'Mem/InitWMWithAllNodes':'True'}],
+ remappings=vslam_remappings),
+
+ Node(
+ package='rtabmap_viz', executable='rtabmap_viz', output='screen',
+ condition=IfCondition(LaunchConfiguration("rtabmap_viz")),
+ parameters=[vslam_params],
+ remappings=vslam_remappings),
+
+ # Compute ground/obstacle clouds for nav2 voxel layers
+ Node(
+ package='rtabmap_util', executable='point_cloud_xyz', output='screen',
+ parameters=[{'decimation': 2,
+ 'max_depth': 3.0,
+ 'voxel_size': 0.02}],
+ remappings=[('depth/image', '/camera/depth/image_raw'),
+ ('depth/camera_info', '/camera/depth/camera_info'),
+ ('cloud', '/camera/cloud')]),
+
+ Node(
+ package='rtabmap_util', executable='obstacles_detection', output='screen',
+ parameters=[vslam_params],
+ remappings=[('cloud', '/camera/cloud'),
+ ('obstacles', '/camera/obstacles'),
+ ('ground', '/camera/ground')]),
+ ]
+
+def generate_launch_description():
+
+ return LaunchDescription([
+ DeclareLaunchArgument(
+ name='use_sim_time',
+ default_value='false',
+ description='Enable use_sime_time to true'
+ ),
+
+ DeclareLaunchArgument(
+ name='rviz',
+ default_value='false',
+ description='Run rviz'
+ ),
+
+ DeclareLaunchArgument(
+ name='rtabmap_viz',
+ default_value='false',
+ description='Run rtabmap_viz'
+ ),
+
+ DeclareLaunchArgument(
+ 'localization', default_value='false', choices=['true', 'false'],
+ description='Launch rtabmap in localization mode (a map should have been already created).'),
+
+ OpaqueFunction(function=launch_setup)
+ ])
\ No newline at end of file
diff --git a/rtabmap_demos/launch/find_object_demo.launch.py b/rtabmap_demos/launch/find_object_demo.launch.py
new file mode 100644
index 00000000..54a63a78
--- /dev/null
+++ b/rtabmap_demos/launch/find_object_demo.launch.py
@@ -0,0 +1,129 @@
+# Requirements:
+# find_object_2d package installed
+# Download rosbag:
+# * demo_find_object.db3: https://drive.google.com/file/d/1web54yQkxeGFr2UwOjKeoajGGDm0fZXT/view?usp=drive_link
+#
+# Example:
+#
+# SLAM:
+# $ ros2 launch rtabmap_demos find_object_demo.launch.py
+#
+# Rosbag:
+# $ ros2 bag play demo_find_object.db3 --clock
+#
+
+from launch import LaunchDescription
+from launch.actions import DeclareLaunchArgument
+from launch.substitutions import LaunchConfiguration
+from launch.conditions import IfCondition, UnlessCondition
+from launch_ros.actions import Node
+from launch_ros.actions import SetParameter
+import os
+from ament_index_python.packages import get_package_share_directory
+
+def generate_launch_description():
+
+ localization = LaunchConfiguration('localization')
+
+ parameters={
+ 'frame_id':'base_footprint',
+ 'odom_frame_id':'odom',
+ 'odom_tf_linear_variance':0.001,
+ 'odom_tf_angular_variance':0.001,
+ 'subscribe_rgbd':True,
+ 'subscribe_scan':True,
+ 'approx_sync':True,
+ 'sync_queue_size': 10,
+ # RTAB-Map's internal parameters should be strings
+ 'RGBD/NeighborLinkRefining': 'true', # Do odometry correction with consecutive laser scans
+ 'Reg/Strategy': '1', # 0=Visual, 1=ICP, 2=Visual+ICP
+ 'Reg/Force3DoF': 'true', # 2D SLAM
+ }
+
+ remappings=[
+ ('rgb/image', '/camera/data_throttled_image'),
+ ('depth/image', '/camera/data_throttled_image_depth'),
+ ('rgb/camera_info', '/camera/data_throttled_camera_info'),
+ ('scan', '/base_scan')]
+
+ config_rviz = os.path.join(
+ get_package_share_directory('rtabmap_demos'), 'config', 'demo_robot_mapping.rviz'
+ )
+
+ config_find_object = os.path.join(
+ get_package_share_directory('rtabmap_demos'), 'config', 'find_object.ini'
+ )
+
+ data_find_object = os.path.join(
+ get_package_share_directory('rtabmap_demos'), 'data', 'books'
+ )
+
+ return LaunchDescription([
+
+ # Launch arguments
+ DeclareLaunchArgument('rtabmap_viz', default_value='false', description='Launch RTAB-Map UI (optional).'),
+ DeclareLaunchArgument('rviz', default_value='true', description='Launch RVIZ (optional).'),
+ DeclareLaunchArgument('localization', default_value='false', description='Launch in localization mode.'),
+ DeclareLaunchArgument('rviz_cfg', default_value=config_rviz, description='Configuration path of rviz2.'),
+
+ SetParameter(name='use_sim_time', value=True),
+
+ # Nodes to launch
+
+ # Uncompress images for find_object
+ Node(
+ package='image_transport', executable='republish', name='republish_rgb', output='screen',
+ arguments=['compressed', 'raw'],
+ remappings=[('in/compressed', '/camera/data_throttled_image/compressed'),
+ ('out', '/camera/data_throttled_image')]),
+ Node(
+ package='image_transport', executable='republish', name='republish_depth', output='screen',
+ arguments=['compressedDepth', 'raw'],
+ remappings=[('in/compressedDepth', '/camera/data_throttled_image_depth/compressedDepth'),
+ ('out', '/camera/data_throttled_image_depth')]),
+
+ Node(
+ package='rtabmap_sync', executable='rgbd_sync', output='screen',
+ parameters=[parameters,
+ {'approx_sync_max_interval': 0.02}],
+ remappings=remappings),
+
+ # SLAM mode:
+ Node(
+ condition=UnlessCondition(localization),
+ package='rtabmap_slam', executable='rtabmap', output='screen',
+ parameters=[parameters],
+ remappings=remappings,
+ arguments=['-d']), # This will delete the previous database (~/.ros/rtabmap.db)
+
+ # Localization mode:
+ Node(
+ condition=IfCondition(localization),
+ package='rtabmap_slam', executable='rtabmap', output='screen',
+ parameters=[parameters,
+ {'Mem/IncrementalMemory':'False',
+ 'Mem/InitWMWithAllNodes':'True'}],
+ remappings=remappings),
+
+ # Visualization:
+ Node(
+ package='rtabmap_viz', executable='rtabmap_viz', output='screen',
+ condition=IfCondition(LaunchConfiguration("rtabmap_viz")),
+ parameters=[parameters],
+ remappings=remappings),
+ Node(
+ package='rviz2', executable='rviz2', name="rviz2", output='screen',
+ condition=IfCondition(LaunchConfiguration("rviz")),
+ arguments=[["-d"], [LaunchConfiguration("rviz_cfg")]]),
+
+ # Find-Object
+ Node(
+ package='find_object_2d', executable='find_object_2d', output='screen',
+ parameters=[{'gui': True,
+ 'subscribe_depth': True,
+ 'settings_path': config_find_object,
+ 'objects_path': data_find_object}],
+ remappings=[('rgb/image_rect_color', '/camera/data_throttled_image'),
+ ('depth_registered/image_raw', '/camera/data_throttled_image_depth'),
+ ('depth_registered/camera_info', '/camera/data_throttled_camera_info')]),
+ ])
\ No newline at end of file
diff --git a/rtabmap_demos/launch/husky/husky_sim_scan2d_demo.launch.py b/rtabmap_demos/launch/husky/husky_sim_scan2d_demo.launch.py
new file mode 100644
index 00000000..d8f8d341
--- /dev/null
+++ b/rtabmap_demos/launch/husky/husky_sim_scan2d_demo.launch.py
@@ -0,0 +1,104 @@
+#
+# Requirements:
+# - Install: ros-$ROS_DISTRO-clearpath-simulator ros-$ROS_DISTRO-clearpath-nav2-demos ros-$ROS_DISTRO-clearpath-config ros-$ROS_DISTRO-moveit-setup-srdf-plugins
+# - Copy /opt/ros/humble/share/clearpath_config/sample/a200_sample.yaml to ~/clearpath/robot.yaml
+# - Fix camera intrinsics by editing /opt/ros/humble/share/clearpath_sensors_description/urdf/intel_realsense.urdf.xacro:
+# 1.047
+#
+# 320
+# 240
+#
+#
+# Example with gazebo:
+# 1) Launch simulator (husky, nav2 and rtabmap):
+# $ ros2 launch rtabmap_demos husky_sim_scan2d_demo.launch.py robot_ns:=a200_0000
+#
+# 2) Click on "Play" button on bottom-left of gazebo as soon as you can see it to avoid controllers crashing after 5 sec.
+#
+# 3) Move the robot:
+# b) By sending goals with RVIZ's "Nav2 Goal" button in action bar.
+# a) By teleoperating:
+# $ ros2 run teleop_twist_keyboard teleop_twist_keyboard --ros-args -r cmd_vel:=/a200_0000/cmd_vel
+#
+
+from ament_index_python.packages import get_package_share_directory
+
+from launch import LaunchDescription
+from launch.actions import DeclareLaunchArgument
+from launch.actions import IncludeLaunchDescription
+from launch.launch_description_sources import PythonLaunchDescriptionSource
+from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
+
+import os
+
+ARGUMENTS = [
+ DeclareLaunchArgument('rtabmap_viz', default_value='true',
+ choices=['true', 'false'], description='Start rtabmap_viz.'),
+ DeclareLaunchArgument('localization', default_value='false',
+ choices=['true', 'false'], description='Start rtabmap in localization mode (a map should have been already created).'),
+ DeclareLaunchArgument('world', default_value='warehouse',
+ description='Ignition World'),
+ DeclareLaunchArgument('robot_ns', default_value='a200_0000',
+ description='Robot namespace'),
+]
+
+def generate_launch_description():
+ # Directories
+ pkg_clearpath_gz = get_package_share_directory(
+ 'clearpath_gz')
+ pkg_clearpath_viz = get_package_share_directory(
+ 'clearpath_viz')
+ pkg_rtabmap_demos = get_package_share_directory(
+ 'rtabmap_demos')
+ pkg_clearpath_nav2_demos = get_package_share_directory(
+ 'clearpath_nav2_demos')
+
+ # Paths
+ sim_launch = PathJoinSubstitution(
+ [pkg_clearpath_gz, 'launch', 'simulation.launch.py'])
+ viz_launch = PathJoinSubstitution(
+ [pkg_clearpath_viz, 'launch', 'view_navigation.launch.py'])
+ rtabmap_launch = PathJoinSubstitution(
+ [pkg_rtabmap_demos, 'launch', 'husky', 'husky_slam2d.launch.py'])
+ nav2_launch = PathJoinSubstitution(
+ [pkg_clearpath_nav2_demos, 'launch', 'nav2.launch.py'])
+
+ sim = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([sim_launch]),
+ launch_arguments=[
+ ('world', LaunchConfiguration('world')),
+ ]
+ )
+
+ viz = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([viz_launch]),
+ launch_arguments=[
+ ('namespace', LaunchConfiguration('robot_ns')),
+ ]
+ )
+
+ rtabmap = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([rtabmap_launch]),
+ launch_arguments=[
+ ('rtabmap_viz', LaunchConfiguration('rtabmap_viz')),
+ ('localization', LaunchConfiguration('localization')),
+ ('use_sim_time', 'true'),
+ ('robot_ns', LaunchConfiguration('robot_ns'))
+ ]
+ )
+
+ nav2 = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([nav2_launch]),
+ launch_arguments=[
+ ('setup_path', os.path.expanduser('~')+'/clearpath/'),
+ ('use_sim_time', 'true'),
+ ]
+ )
+
+ # Create launch description and add actions
+ ld = LaunchDescription(ARGUMENTS)
+ ld.add_action(rtabmap)
+ ld.add_action(sim)
+ ld.add_action(viz)
+ ld.add_action(nav2)
+ return ld
diff --git a/rtabmap_demos/launch/husky/husky_sim_scan3d_assemble_demo.launch.py b/rtabmap_demos/launch/husky/husky_sim_scan3d_assemble_demo.launch.py
new file mode 100644
index 00000000..1c60eb8f
--- /dev/null
+++ b/rtabmap_demos/launch/husky/husky_sim_scan3d_assemble_demo.launch.py
@@ -0,0 +1,104 @@
+#
+# Requirements:
+# - Install: ros-$ROS_DISTRO-clearpath-simulator ros-$ROS_DISTRO-clearpath-nav2-demos ros-$ROS_DISTRO-clearpath-config ros-$ROS_DISTRO-moveit-setup-srdf-plugins
+# - Copy /opt/ros/humble/share/clearpath_config/sample/a200_sample.yaml to ~/clearpath/robot.yaml
+# - Fix camera intrinsics by editing /opt/ros/humble/share/clearpath_sensors_description/urdf/intel_realsense.urdf.xacro:
+# 1.047
+#
+# 320
+# 240
+#
+# - Fix lidar sim distortions by editing /opt/ros/humble/share/clearpath_gz/worlds/warehouse.sdf (https://github.com/gazebosim/gz-sim/issues/2743):
+# - ogre2
+# + ogre
+#
+# Example with gazebo:
+# 1) Launch simulator (husky, nav2 and rtabmap):
+# $ ros2 launch rtabmap_demos husky_sim_scan3d_assemble_demo.launch.py robot_ns:=a200_0000
+#
+# 2) Click on "Play" button on bottom-left of gazebo as soon as you can see it to avoid controllers crashing after 5 sec.
+#
+# 3) Move the robot:
+# b) By sending goals with RVIZ's "Nav2 Goal" button in action bar.
+# a) By teleoperating:
+# $ ros2 run teleop_twist_keyboard teleop_twist_keyboard --ros-args -r cmd_vel:=/a200_0000/cmd_vel
+#
+
+from ament_index_python.packages import get_package_share_directory
+
+from launch import LaunchDescription
+from launch.actions import DeclareLaunchArgument
+from launch.actions import IncludeLaunchDescription
+from launch.launch_description_sources import PythonLaunchDescriptionSource
+from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
+
+import os
+
+ARGUMENTS = [
+ DeclareLaunchArgument('rtabmap_viz', default_value='true',
+ choices=['true', 'false'], description='Start rtabmap_viz.'),
+ DeclareLaunchArgument('world', default_value='warehouse',
+ description='Ignition World'),
+ DeclareLaunchArgument('robot_ns', default_value='a200_0000',
+ description='Robot namespace'),
+]
+
+def generate_launch_description():
+ # Directories
+ pkg_clearpath_gz = get_package_share_directory(
+ 'clearpath_gz')
+ pkg_clearpath_viz = get_package_share_directory(
+ 'clearpath_viz')
+ pkg_rtabmap_demos = get_package_share_directory(
+ 'rtabmap_demos')
+ pkg_clearpath_nav2_demos = get_package_share_directory(
+ 'clearpath_nav2_demos')
+
+ # Paths
+ sim_launch = PathJoinSubstitution(
+ [pkg_clearpath_gz, 'launch', 'simulation.launch.py'])
+ viz_launch = PathJoinSubstitution(
+ [pkg_clearpath_viz, 'launch', 'view_navigation.launch.py'])
+ rtabmap_launch = PathJoinSubstitution(
+ [pkg_rtabmap_demos, 'launch', 'husky', 'husky_slam3d_assemble.launch.py'])
+ nav2_launch = PathJoinSubstitution(
+ [pkg_clearpath_nav2_demos, 'launch', 'nav2.launch.py'])
+
+ sim = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([sim_launch]),
+ launch_arguments=[
+ ('world', LaunchConfiguration('world')),
+ ]
+ )
+
+ viz = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([viz_launch]),
+ launch_arguments=[
+ ('namespace', LaunchConfiguration('robot_ns')),
+ ]
+ )
+
+ rtabmap = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([rtabmap_launch]),
+ launch_arguments=[
+ ('rtabmap_viz', LaunchConfiguration('rtabmap_viz')),
+ ('use_sim_time', 'true'),
+ ('robot_ns', LaunchConfiguration('robot_ns'))
+ ]
+ )
+
+ nav2 = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([nav2_launch]),
+ launch_arguments=[
+ ('setup_path', os.path.expanduser('~')+'/clearpath/'),
+ ('use_sim_time', 'true'),
+ ]
+ )
+
+ # Create launch description and add actions
+ ld = LaunchDescription(ARGUMENTS)
+ ld.add_action(rtabmap)
+ ld.add_action(sim)
+ ld.add_action(viz)
+ ld.add_action(nav2)
+ return ld
diff --git a/rtabmap_demos/launch/husky/husky_sim_scan3d_demo.launch.py b/rtabmap_demos/launch/husky/husky_sim_scan3d_demo.launch.py
new file mode 100644
index 00000000..ed6c896a
--- /dev/null
+++ b/rtabmap_demos/launch/husky/husky_sim_scan3d_demo.launch.py
@@ -0,0 +1,110 @@
+#
+# Requirements:
+# - Install: ros-$ROS_DISTRO-clearpath-simulator ros-$ROS_DISTRO-clearpath-nav2-demos ros-$ROS_DISTRO-clearpath-config ros-$ROS_DISTRO-moveit-setup-srdf-plugins
+# - Copy /opt/ros/humble/share/clearpath_config/sample/a200_sample.yaml to ~/clearpath/robot.yaml
+# - Fix camera intrinsics by editing /opt/ros/humble/share/clearpath_sensors_description/urdf/intel_realsense.urdf.xacro:
+# 1.047
+#
+# 320
+# 240
+#
+# - Fix lidar sim distortions by editing /opt/ros/humble/share/clearpath_gz/worlds/warehouse.sdf (https://github.com/gazebosim/gz-sim/issues/2743):
+# - ogre2
+# + ogre
+#
+# Example with gazebo:
+# 1) Launch simulator (husky, nav2 and rtabmap):
+# $ ros2 launch rtabmap_demos husky_sim_scan3d_demo.launch.py robot_ns:=a200_0000
+#
+# 2) Click on "Play" button on bottom-left of gazebo as soon as you can see it to avoid controllers crashing after 5 sec.
+#
+# 3) Move the robot:
+# b) By sending goals with RVIZ's "Nav2 Goal" button in action bar.
+# a) By teleoperating:
+# $ ros2 run teleop_twist_keyboard teleop_twist_keyboard --ros-args -r cmd_vel:=/a200_0000/cmd_vel
+#
+
+from ament_index_python.packages import get_package_share_directory
+
+from launch import LaunchDescription
+from launch.actions import DeclareLaunchArgument
+from launch.actions import IncludeLaunchDescription
+from launch.launch_description_sources import PythonLaunchDescriptionSource
+from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
+
+import os
+
+ARGUMENTS = [
+ DeclareLaunchArgument('rtabmap_viz', default_value='true',
+ choices=['true', 'false'], description='Start rtabmap_viz.'),
+ DeclareLaunchArgument('localization', default_value='false',
+ choices=['true', 'false'], description='Start rtabmap in localization mode (a map should have been already created).'),
+ DeclareLaunchArgument('world', default_value='warehouse',
+ description='Ignition World'),
+ DeclareLaunchArgument('robot_ns', default_value='a200_0000',
+ description='Robot namespace'),
+ DeclareLaunchArgument('use_camera', default_value='true',
+ description='Use camera for global loop closure / re-localization.'),
+]
+
+def generate_launch_description():
+ # Directories
+ pkg_clearpath_gz = get_package_share_directory(
+ 'clearpath_gz')
+ pkg_clearpath_viz = get_package_share_directory(
+ 'clearpath_viz')
+ pkg_rtabmap_demos = get_package_share_directory(
+ 'rtabmap_demos')
+ pkg_clearpath_nav2_demos = get_package_share_directory(
+ 'clearpath_nav2_demos')
+
+ # Paths
+ sim_launch = PathJoinSubstitution(
+ [pkg_clearpath_gz, 'launch', 'simulation.launch.py'])
+ viz_launch = PathJoinSubstitution(
+ [pkg_clearpath_viz, 'launch', 'view_navigation.launch.py'])
+ rtabmap_launch = PathJoinSubstitution(
+ [pkg_rtabmap_demos, 'launch', 'husky', 'husky_slam3d.launch.py'])
+ nav2_launch = PathJoinSubstitution(
+ [pkg_clearpath_nav2_demos, 'launch', 'nav2.launch.py'])
+
+ sim = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([sim_launch]),
+ launch_arguments=[
+ ('world', LaunchConfiguration('world')),
+ ]
+ )
+
+ viz = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([viz_launch]),
+ launch_arguments=[
+ ('namespace', LaunchConfiguration('robot_ns')),
+ ]
+ )
+
+ rtabmap = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([rtabmap_launch]),
+ launch_arguments=[
+ ('rtabmap_viz', LaunchConfiguration('rtabmap_viz')),
+ ('localization', LaunchConfiguration('localization')),
+ ('use_sim_time', 'true'),
+ ('use_camera', LaunchConfiguration('use_camera')),
+ ('robot_ns', LaunchConfiguration('robot_ns'))
+ ]
+ )
+
+ nav2 = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([nav2_launch]),
+ launch_arguments=[
+ ('setup_path', os.path.expanduser('~')+'/clearpath/'),
+ ('use_sim_time', 'true'),
+ ]
+ )
+
+ # Create launch description and add actions
+ ld = LaunchDescription(ARGUMENTS)
+ ld.add_action(rtabmap)
+ ld.add_action(sim)
+ ld.add_action(viz)
+ ld.add_action(nav2)
+ return ld
diff --git a/rtabmap_demos/launch/husky/husky_slam2d.launch.py b/rtabmap_demos/launch/husky/husky_slam2d.launch.py
new file mode 100644
index 00000000..53bfcf52
--- /dev/null
+++ b/rtabmap_demos/launch/husky/husky_slam2d.launch.py
@@ -0,0 +1,128 @@
+#
+#
+# Example with gazebo:
+# 1) Launch simulator (husky):
+# $ ros2 launch clearpath_gz simulation.launch.py
+# Click on "Play" button on bottom-left of gazebo as soon as you can see it to avoid controllers crashing after 5 sec.
+#
+# 2) Launch rviz:
+# $ ros2 launch clearpath_viz view_navigation.launch.py namespace:=a200_0000
+#
+# 3) Launch SLAM:
+# $ ros2 launch rtabmap_demos husky_slam2d.launch.py use_sim_time:=true
+#
+# 4) Launch nav2"
+# $ ros2 launch clearpath_nav2_demos nav2.launch.py setup_path:=$HOME/clearpath/ use_sim_time:=true
+#
+# 4) Click on "Play" button on bottom-left of gazebo.
+#
+# 5) Move the robot:
+# b) By sending goals with RVIZ's "Nav2 Goal" button in action bar.
+# a) By teleoperating:
+# $ ros2 run teleop_twist_keyboard teleop_twist_keyboard --ros-args -r cmd_vel:=/a200_0000/cmd_vel
+#
+
+from launch import LaunchDescription
+from launch.actions import DeclareLaunchArgument, SetEnvironmentVariable
+from launch.substitutions import LaunchConfiguration
+from launch.conditions import IfCondition, UnlessCondition
+from launch_ros.actions import Node
+
+
+def generate_launch_description():
+
+ use_sim_time = LaunchConfiguration('use_sim_time')
+ localization = LaunchConfiguration('localization')
+ robot_ns = LaunchConfiguration('robot_ns')
+
+ icp_odom_parameters={
+ 'odom_frame_id':'icp_odom',
+ 'guess_frame_id':'odom'
+ }
+
+ rtabmap_parameters={
+ 'subscribe_rgbd':True,
+ 'subscribe_scan':True,
+ 'use_action_for_goal':True,
+ 'odom_sensor_sync': True,
+ # RTAB-Map's parameters should be strings:
+ 'Mem/NotLinkedNodesKept':'false',
+ 'Grid/RangeMin':'0.7', # ignore laser scan points on the robot itself
+ 'RGBD/OptimizeMaxError':'2',
+ }
+
+ # Shared parameters between different nodes
+ shared_parameters={
+ 'frame_id':'base_link',
+ 'use_sim_time':use_sim_time,
+ # RTAB-Map's parameters should be strings:
+ 'Reg/Strategy':'1',
+ 'Reg/Force3DoF':'true', # we are moving on a 2D flat floor
+ 'Mem/NotLinkedNodesKept':'false',
+ 'Icp/PointToPlaneMinComplexity':'0.04', # to be more robust to long corridors with low geometry
+ 'Icp/MaxTranslation': '1'
+ }
+
+ remappings=[
+ ('/tf', 'tf'),
+ ('/tf_static', 'tf_static'),
+ ('odom', 'icp_odom'),
+ ('scan', 'sensors/lidar2d_0/scan'),
+ ('rgb/image', 'sensors/camera_0/color/image'),
+ ('rgb/camera_info', 'sensors/camera_0/color/camera_info'),
+ ('depth/image', 'sensors/camera_0/depth/image')]
+
+ return LaunchDescription([
+
+ # Launch arguments
+ DeclareLaunchArgument(
+ 'use_sim_time', default_value='false', choices=['true', 'false'],
+ description='Use simulation (Gazebo) clock if true'),
+
+ DeclareLaunchArgument(
+ 'localization', default_value='false', choices=['true', 'false'],
+ description='Launch rtabmap in localization mode (a map should have been already created).'),
+
+ DeclareLaunchArgument(
+ 'robot_ns', default_value='a200_0000',
+ description='Robot namespace.'),
+
+ # Nodes to launch
+ Node(
+ package='rtabmap_sync', executable='rgbd_sync', output='screen',
+ namespace=robot_ns,
+ parameters=[{'approx_sync':False, 'use_sim_time':use_sim_time}],
+ remappings=remappings),
+
+ Node(
+ package='rtabmap_odom', executable='icp_odometry', output='screen',
+ namespace=robot_ns,
+ parameters=[icp_odom_parameters, shared_parameters],
+ remappings=remappings,
+ arguments=["--ros-args", "--log-level", 'warn']),
+
+ # SLAM Mode:
+ Node(
+ condition=UnlessCondition(localization),
+ package='rtabmap_slam', executable='rtabmap', output='screen',
+ namespace=robot_ns,
+ parameters=[rtabmap_parameters, shared_parameters],
+ remappings=remappings,
+ arguments=['-d']),
+
+ # Localization mode:
+ Node(
+ condition=IfCondition(localization),
+ package='rtabmap_slam', executable='rtabmap', output='screen',
+ namespace=robot_ns,
+ parameters=[rtabmap_parameters, shared_parameters,
+ {'Mem/IncrementalMemory':'False',
+ 'Mem/InitWMWithAllNodes':'True'}],
+ remappings=remappings),
+
+ Node(
+ package='rtabmap_viz', executable='rtabmap_viz', output='screen',
+ namespace=robot_ns,
+ parameters=[rtabmap_parameters, shared_parameters],
+ remappings=remappings),
+ ])
diff --git a/rtabmap_demos/launch/husky/husky_slam3d.launch.py b/rtabmap_demos/launch/husky/husky_slam3d.launch.py
new file mode 100644
index 00000000..bbadcb25
--- /dev/null
+++ b/rtabmap_demos/launch/husky/husky_slam3d.launch.py
@@ -0,0 +1,146 @@
+#
+#
+# Example with gazebo:
+# 1) Launch simulator (husky):
+# $ ros2 launch clearpath_gz simulation.launch.py
+# Click on "Play" button on bottom-left of gazebo as soon as you can see it to avoid controllers crashing after 5 sec.
+#
+# 2) Launch rviz:
+# $ ros2 launch clearpath_viz view_navigation.launch.py namespace:=a200_0000
+#
+# 3) Launch SLAM:
+# $ ros2 launch rtabmap_demos husky_slam3d.launch.py use_sim_time:=true
+#
+# 4) Launch nav2"
+# $ ros2 launch clearpath_nav2_demos nav2.launch.py setup_path:=$HOME/clearpath/ use_sim_time:=true
+#
+# 4) Click on "Play" button on bottom-left of gazebo.
+#
+# 5) Move the robot:
+# b) By sending goals with RVIZ's "Nav2 Goal" button in action bar.
+# a) By teleoperating:
+# $ ros2 run teleop_twist_keyboard teleop_twist_keyboard --ros-args -r cmd_vel:=/a200_0000/cmd_vel
+#
+
+from launch import LaunchDescription
+from launch.actions import DeclareLaunchArgument, SetEnvironmentVariable
+from launch.substitutions import LaunchConfiguration
+from launch.conditions import IfCondition, UnlessCondition
+from launch_ros.actions import Node
+
+
+def generate_launch_description():
+
+ use_sim_time = LaunchConfiguration('use_sim_time')
+ localization = LaunchConfiguration('localization')
+ robot_ns = LaunchConfiguration('robot_ns')
+ use_camera = LaunchConfiguration('use_camera')
+
+ icp_odom_parameters={
+ 'odom_frame_id':'icp_odom',
+ 'guess_frame_id':'odom',
+ 'OdomF2M/ScanSubtractRadius': '0.3', # match voxel size
+ 'OdomF2M/ScanMaxSize': '10000'
+ }
+
+ rtabmap_parameters={
+ 'subscribe_rgb':False,
+ 'subscribe_depth':False,
+ 'subscribe_rgbd': use_camera,
+ 'subscribe_scan_cloud':True,
+ 'use_action_for_goal':True,
+ 'odom_sensor_sync': True,
+ # RTAB-Map's parameters should be strings:
+ 'Mem/NotLinkedNodesKept':'false',
+ 'Grid/RangeMin':'0.5', # ignore laser scan points on the robot itself
+ 'Grid/NormalsSegmentation':'false', # Use passthrough filter to detect obstacles
+ 'Grid/MaxGroundHeight':'0.05', # All points above 5 cm are obstacles
+ 'Grid/MaxObstacleHeight':'1', # All points over 1 meter are ignored
+ 'Grid/RayTracing':'true', # Fill empty space
+ 'Grid/3D':'false', # Use 2D occupancy
+ 'RGBD/OptimizeMaxError':'0.3', # There are a lot of repetitive patterns, be more strict in accepting loop closures
+ }
+
+ # Shared parameters between different nodes
+ shared_parameters={
+ 'frame_id':'base_link',
+ 'use_sim_time':use_sim_time,
+ # RTAB-Map's parameters should be strings:
+ 'Reg/Strategy':'1',
+ 'Reg/Force3DoF':'true', # we are moving on a 2D flat floor
+ 'Mem/NotLinkedNodesKept':'false',
+ 'Icp/VoxelSize': '0.3',
+ 'Icp/MaxCorrespondenceDistance': '3', # roughly 10x voxel size
+ 'Icp/PointToPlaneGroundNormalsUp': '0.9',
+ 'Icp/RangeMin': '0.5',
+ 'Icp/MaxTranslation': '1'
+ }
+
+ remappings=[
+ ('/tf', 'tf'),
+ ('/tf_static', 'tf_static'),
+ ('odom', 'icp_odom'),
+ ('scan_cloud', 'sensors/lidar3d_0/points'),
+ ('rgb/image', 'sensors/camera_0/color/image'),
+ ('rgb/camera_info', 'sensors/camera_0/color/camera_info'),
+ ('depth/image', 'sensors/camera_0/depth/image')]
+
+ return LaunchDescription([
+
+ # Launch arguments
+ DeclareLaunchArgument(
+ 'use_sim_time', default_value='false', choices=['true', 'false'],
+ description='Use simulation (Gazebo) clock if true'),
+
+ DeclareLaunchArgument(
+ 'localization', default_value='false', choices=['true', 'false'],
+ description='Launch rtabmap in localization mode (a map should have been already created).'),
+
+ DeclareLaunchArgument(
+ 'robot_ns', default_value='a200_0000',
+ description='Robot namespace.'),
+
+ DeclareLaunchArgument(
+ 'use_camera', default_value='true',
+ description='Use camera for global loop closure / re-localization.'),
+
+ # Nodes to launch
+ Node(
+ condition=IfCondition(use_camera),
+ package='rtabmap_sync', executable='rgbd_sync', output='screen',
+ namespace=robot_ns,
+ parameters=[{'approx_sync':False, 'use_sim_time':use_sim_time}],
+ remappings=remappings),
+
+ Node(
+ package='rtabmap_odom', executable='icp_odometry', output='screen',
+ namespace=robot_ns,
+ parameters=[icp_odom_parameters, shared_parameters],
+ remappings=remappings,
+ arguments=["--ros-args", "--log-level", 'warn']),
+
+ # SLAM Mode:
+ Node(
+ condition=UnlessCondition(localization),
+ package='rtabmap_slam', executable='rtabmap', output='screen',
+ namespace=robot_ns,
+ parameters=[rtabmap_parameters, shared_parameters],
+ remappings=remappings,
+ arguments=['-d']),
+
+ # Localization mode:
+ Node(
+ condition=IfCondition(localization),
+ package='rtabmap_slam', executable='rtabmap', output='screen',
+ namespace=robot_ns,
+ parameters=[rtabmap_parameters, shared_parameters,
+ {'Mem/IncrementalMemory':'False',
+ 'Mem/InitWMWithAllNodes':'True'}],
+ remappings=remappings),
+
+ Node(
+ package='rtabmap_viz', executable='rtabmap_viz', output='screen',
+ namespace=robot_ns,
+ parameters=[rtabmap_parameters, shared_parameters],
+ remappings=remappings),
+ ])
diff --git a/rtabmap_demos/launch/husky/husky_slam3d_assemble.launch.py b/rtabmap_demos/launch/husky/husky_slam3d_assemble.launch.py
new file mode 100644
index 00000000..62b4116f
--- /dev/null
+++ b/rtabmap_demos/launch/husky/husky_slam3d_assemble.launch.py
@@ -0,0 +1,134 @@
+#
+#
+# Example with gazebo:
+# 1) Launch simulator (husky):
+# $ ros2 launch clearpath_gz simulation.launch.py
+# Click on "Play" button on bottom-left of gazebo as soon as you can see it to avoid controllers crashing after 5 sec.
+#
+# 2) Launch rviz:
+# $ ros2 launch clearpath_viz view_navigation.launch.py namespace:=a200_0000
+#
+# 3) Launch SLAM:
+# $ ros2 launch rtabmap_demos husky_slam3d_assemble.launch.py use_sim_time:=true
+#
+# 4) Launch nav2"
+# $ ros2 launch clearpath_nav2_demos nav2.launch.py setup_path:=$HOME/clearpath/ use_sim_time:=true
+#
+# 4) Click on "Play" button on bottom-left of gazebo.
+#
+# 5) Move the robot:
+# b) By sending goals with RVIZ's "Nav2 Goal" button in action bar.
+# a) By teleoperating:
+# $ ros2 run teleop_twist_keyboard teleop_twist_keyboard --ros-args -r cmd_vel:=/a200_0000/cmd_vel
+#
+
+from launch import LaunchDescription
+from launch.actions import DeclareLaunchArgument
+from launch.substitutions import LaunchConfiguration
+from launch_ros.actions import Node
+
+
+def generate_launch_description():
+
+ use_sim_time = LaunchConfiguration('use_sim_time')
+ robot_ns = LaunchConfiguration('robot_ns')
+
+ icp_odom_parameters={
+ 'odom_frame_id':'icp_odom',
+ 'guess_frame_id':'odom',
+ 'OdomF2M/ScanSubtractRadius': '0.3', # match voxel size
+ 'OdomF2M/ScanMaxSize': '10000'
+ }
+
+ rtabmap_parameters={
+ 'subscribe_rgbd':True,
+ 'subscribe_depth':False,
+ 'subscribe_rgb':False,
+ 'subscribe_scan_cloud':True,
+ 'use_action_for_goal':True,
+ 'odom_sensor_sync': True,
+ 'topic_queue_size': 30,
+ 'sync_queue_size': 30,
+ 'approx_sync': True,
+ 'qos': 1,
+ # RTAB-Map's parameters should be strings:
+ 'Mem/NotLinkedNodesKept':'false',
+ 'Grid/RangeMin':'0.5', # ignore laser scan points on the robot itself
+ 'Grid/NormalsSegmentation':'false', # Use passthrough filter to detect obstacles
+ 'Grid/MaxGroundHeight':'0.05', # All points above 5 cm are obstacles
+ 'Grid/MaxObstacleHeight':'1', # All points over 1 meter are ignored
+ 'Grid/RayTracing':'true', # Fill empty space
+ 'Grid/3D':'false', # Use 2D occupancy
+ 'RGBD/OptimizeMaxError':'0.3', # There are a lot of repetitive patterns, be more strict in accepting loop closures
+ 'Rtabmap/DetectionRate': '0' # Rate is limited by the assembling time below (1 Hz)
+ }
+
+ # Shared parameters between different nodes
+ shared_parameters={
+ 'frame_id':'base_link',
+ 'use_sim_time':use_sim_time,
+ # RTAB-Map's parameters should be strings:
+ 'Reg/Strategy':'1',
+ 'Reg/Force3DoF':'true', # we are moving on a 2D flat floor
+ 'Mem/NotLinkedNodesKept':'false',
+ 'Icp/VoxelSize': '0.3',
+ 'Icp/MaxCorrespondenceDistance': '3', # roughly 10x voxel size
+ 'Icp/PointToPlaneGroundNormalsUp': '0.9',
+ 'Icp/RangeMin': '0.5',
+ 'Icp/MaxTranslation': '2'
+ }
+
+ remappings=[
+ ('/tf', 'tf'),
+ ('/tf_static', 'tf_static'),
+ ('odom', 'icp_odom'),
+ ('rgb/image', 'sensors/camera_0/color/image'),
+ ('rgb/camera_info', 'sensors/camera_0/color/camera_info'),
+ ('depth/image', 'sensors/camera_0/depth/image')]
+
+ return LaunchDescription([
+
+ # Launch arguments
+ DeclareLaunchArgument(
+ 'use_sim_time', default_value='false', choices=['true', 'false'],
+ description='Use simulation (Gazebo) clock if true'),
+
+ DeclareLaunchArgument(
+ 'robot_ns', default_value='a200_0000',
+ description='Robot namespace.'),
+
+ # Nodes to launch
+ Node(
+ package='rtabmap_sync', executable='rgbd_sync', output='screen',
+ namespace=robot_ns,
+ parameters=[{'approx_sync':False, 'use_sim_time':use_sim_time}],
+ remappings=remappings),
+
+ Node(
+ package='rtabmap_odom', executable='icp_odometry', output='screen',
+ namespace=robot_ns,
+ parameters=[icp_odom_parameters, shared_parameters],
+ remappings=remappings + [('scan_cloud', 'sensors/lidar3d_0/points')],
+ arguments=["--ros-args", "--log-level", 'warn']),
+
+ #Assemble scans
+ Node(
+ package='rtabmap_util', executable='point_cloud_assembler', output='screen',
+ namespace=robot_ns,
+ parameters=[{'assembling_time': 1.0, 'range_min': 0.5, 'fixed_frame_id': "", 'use_sim_time':use_sim_time, 'sync_queue_size': 30, 'topic_queue_size':30}],
+ remappings=remappings + [('cloud', 'sensors/lidar3d_0/points')]),
+
+ # SLAM Mode:
+ Node(
+ package='rtabmap_slam', executable='rtabmap', output='screen',
+ namespace=robot_ns,
+ parameters=[rtabmap_parameters, shared_parameters],
+ remappings=remappings + [('scan_cloud', 'assembled_cloud')],
+ arguments=['-d']),
+
+ Node(
+ package='rtabmap_viz', executable='rtabmap_viz', output='screen',
+ namespace=robot_ns,
+ parameters=[rtabmap_parameters, shared_parameters],
+ remappings=remappings + [('scan_cloud', 'sensors/lidar3d_0/points')]),
+ ])
diff --git a/rtabmap_demos/launch/isaac/isaac_sim_vslam_demo.launch.py b/rtabmap_demos/launch/isaac/isaac_sim_vslam_demo.launch.py
new file mode 100644
index 00000000..521105d1
--- /dev/null
+++ b/rtabmap_demos/launch/isaac/isaac_sim_vslam_demo.launch.py
@@ -0,0 +1,280 @@
+#
+# Requirements:
+# * Isaac simulator
+# * isaac_ros_image_proc
+# * isaac_ros_stereo_image_proc
+# * nav2_bringup
+# * isaac_ros_visual_slam (optional, for vo:=isaac)
+#
+# 1. Launch Isaac Simulator
+#
+# 2. Open Isaac Examples -> ROS2 -> Navigation -> Carter Navigation (or iw.hub Navigation, for more visual features)
+#
+# 3. Enable front stereo right camera:
+# In the Stage tab, open World->Nova_Carter_ROS->front_hawk->right_camera_render_product,
+# then under Property->Isaac Create Render Product Node->Inputs, check "Enabled". To make
+# simulation faster, set height=600 and width=960. Do the same for the front stereo left camera.
+#
+# 4. Make sure that after you click on Play button in the simulator, you can see these topics:
+# $ ros2 topic list
+# /front_stereo_camera/left/camera_info
+# /front_stereo_camera/left/image_raw
+# /front_stereo_camera/left/image_raw/nitros_bridge
+# /front_stereo_camera/right/camera_info
+# /front_stereo_camera/right/image_raw
+# /front_stereo_camera/right/image_raw/nitros_bridge
+# /front_stereo_imu/imu
+#
+# 5. Launch the example:
+# $ ros2 launch rtabmap_demos isaac_sim_vslam_demo.launch.py
+#
+# 6. You should be able to send goals in RVIZ to move the robot, or use:
+# $ ros2 run teleop_twist_keyboard teleop_twist_keyboard
+#
+# === Advanced ===
+# With this launch file, we can also experiment with visual odometry with/without disparity computed on GPU.
+#
+# A. Use RTAB-Map's Visual Odometry:
+# $ ros2 launch rtabmap_demos isaac_sim_vslam_demo.launch.py vo:=rtabmap stereo:=true
+# $ ros2 launch rtabmap_demos isaac_sim_vslam_demo.launch.py vo:=rtabmap stereo:=false
+#
+# B. Use Isaac Visual Odometry:
+# We should disable wheel odometry TF publishing in the simulator to make it work. To
+# do so, in the Stage tab, open World->Nova_Carter_ROS->transform_tree_odometry->ros2_publish_raw_transform_tree,
+# then under Property->ROS2Publish Raw Transform Tree Node->Inputs, change topicName from "tf" to "tf_odom_ignored".
+# $ ros2 launch rtabmap_demos isaac_sim_vslam_demo.launch.py vo:=isaac stereo:=true
+# $ ros2 launch rtabmap_demos isaac_sim_vslam_demo.launch.py vo:=isaac stereo:=false
+#
+#
+
+from ament_index_python.packages import get_package_share_directory
+
+from launch import LaunchDescription
+from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription, OpaqueFunction
+from launch.launch_description_sources import PythonLaunchDescriptionSource
+from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
+from launch_ros.actions import ComposableNodeContainer
+from launch_ros.descriptions import ComposableNode
+
+def launch_setup(context, *args, **kwargs):
+ # Directories
+ pkg_nav2_bringup = get_package_share_directory(
+ 'nav2_bringup')
+ pkg_rtabmap_demos = get_package_share_directory(
+ 'rtabmap_demos')
+
+ # Paths
+ nav2_launch = PathJoinSubstitution(
+ [pkg_nav2_bringup, 'launch', 'navigation_launch.py'])
+ nav2_vo_params = PathJoinSubstitution(
+ [pkg_rtabmap_demos, 'params', 'isaac_vslam_nav2_params.yaml'])
+ nav2_params = PathJoinSubstitution(
+ [pkg_rtabmap_demos, 'params', 'isaac_nav2_params.yaml'])
+ rviz_launch = PathJoinSubstitution(
+ [pkg_nav2_bringup, 'launch', 'rviz_launch.py'])
+ rtabmap_launch = PathJoinSubstitution(
+ [pkg_rtabmap_demos, 'launch', 'isaac', 'isaac_vslam.launch.py'])
+
+ vo = LaunchConfiguration('vo').perform(context)
+ image_width = int(LaunchConfiguration('image_width').perform(context))
+ image_height = int(LaunchConfiguration('image_height').perform(context))
+
+ left_resize_node = ComposableNode(
+ name='left_resize_node',
+ package='isaac_ros_image_proc',
+ plugin='nvidia::isaac_ros::image_proc::ResizeNode',
+ parameters=[{
+ 'use_sim_time': True,
+ 'output_width': image_width,
+ 'output_height': image_height,
+ }],
+ namespace="front_stereo_camera",
+ remappings=[
+ ('image', 'left/image_raw'),
+ ('camera_info', 'left/camera_info'),
+ ('resize/image', 'left/image_resize'),
+ ('resize/camera_info', 'left/camera_info_resize')
+ ]
+ )
+
+ right_resize_node = ComposableNode(
+ name='right_resize_node',
+ package='isaac_ros_image_proc',
+ plugin='nvidia::isaac_ros::image_proc::ResizeNode',
+ parameters=[{
+ 'use_sim_time': True,
+ 'output_width': image_width,
+ 'output_height': image_height,
+ }],
+ namespace="front_stereo_camera",
+ remappings=[
+ ('image', 'right/image_raw'),
+ ('camera_info', 'right/camera_info'),
+ ('resize/image', 'right/image_resize'),
+ ('resize/camera_info', 'right/camera_info_resize')
+ ]
+ )
+
+ left_rectify_node = ComposableNode(
+ name='left_rectify_node',
+ package='isaac_ros_image_proc',
+ plugin='nvidia::isaac_ros::image_proc::RectifyNode',
+ parameters=[{
+ 'use_sim_time': True,
+ 'output_width': image_width,
+ 'output_height': image_height,
+ }],
+ namespace="front_stereo_camera",
+ remappings=[
+ ('image_raw', 'left/image_resize'),
+ ('camera_info', 'left/camera_info_resize'),
+ ('image_rect', 'left/image_rect'),
+ ('camera_info_rect', 'left/camera_info_rect')
+ ]
+ )
+
+ right_rectify_node = ComposableNode(
+ name='right_rectify_node',
+ package='isaac_ros_image_proc',
+ plugin='nvidia::isaac_ros::image_proc::RectifyNode',
+ parameters=[{
+ 'use_sim_time': True,
+ 'output_width': image_width,
+ 'output_height': image_height,
+ }],
+ namespace="front_stereo_camera",
+ remappings=[
+ ('image_raw', 'right/image_resize'),
+ ('camera_info', 'right/camera_info_resize'),
+ ('image_rect', 'right/image_rect'),
+ ('camera_info_rect', 'right/camera_info_rect')
+ ]
+ )
+
+ disparity_node = ComposableNode(
+ name='disparity_node',
+ package='isaac_ros_stereo_image_proc',
+ plugin='nvidia::isaac_ros::stereo_image_proc::DisparityNode',
+ parameters=[{
+ 'use_sim_time': True,
+ 'backends': 'CUDA',
+ 'max_disparity': 64.0
+ }],
+ namespace="front_stereo_camera",
+ remappings=[
+ ('left/camera_info', 'left/camera_info_rect'),
+ ('right/camera_info', 'right/camera_info_rect'),
+ ],
+ )
+
+ disparity_to_depth_node = ComposableNode(
+ name='disparity_to_depth_node',
+ package='isaac_ros_stereo_image_proc',
+ plugin='nvidia::isaac_ros::stereo_image_proc::DisparityToDepthNode',
+ parameters=[{
+ 'use_sim_time': True,
+ }],
+ namespace="front_stereo_camera"
+ )
+
+ stereo_img_proc_container = ComposableNodeContainer(
+ name='stereo_img_proc_container',
+ package='rclcpp_components',
+ namespace="front_stereo_camera",
+ executable='component_container_mt',
+ composable_node_descriptions=[
+ left_resize_node,
+ right_resize_node,
+ left_rectify_node,
+ right_rectify_node,
+ disparity_node,
+ disparity_to_depth_node
+ ],
+ output='screen',
+ arguments=['--ros-args', '--log-level', 'info',
+ '--log-level', 'color_format_convert:=info',
+ '--log-level', 'NitrosImage:=info',
+ '--log-level', 'NitrosNode:=info'
+ ],
+ )
+
+ nav2_args = [('use_sim_time', 'true')]
+ if vo == 'rtabmap':
+ # We need to change the base odom frame to vo
+ nav2_args.append(('params_file', nav2_vo_params))
+ else:
+ # Use custom version with higher velocities
+ nav2_args.append(('params_file', nav2_params))
+ nav2 = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([nav2_launch]),
+ launch_arguments=nav2_args
+ )
+
+ rviz = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([rviz_launch])
+ )
+
+ rtabmap = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([rtabmap_launch]),
+ launch_arguments=[
+ ('rtabmap_viz', LaunchConfiguration('rtabmap_viz')),
+ ('localization', LaunchConfiguration('localization')),
+ ('use_sim_time', 'true'),
+ ('stereo_camera_namespace', 'front_stereo_camera'),
+ ('enable_vo', str(vo == 'rtabmap')),
+ ('stereo', LaunchConfiguration('stereo'))
+ ]
+ )
+
+ # Add actions
+ actions = [rtabmap, nav2, rviz, stereo_img_proc_container]
+
+ if vo == 'isaac':
+ isaac_visual_slam_node = ComposableNode(
+ name='visual_slam_node',
+ package='isaac_ros_visual_slam',
+ plugin='nvidia::isaac_ros::visual_slam::VisualSlamNode',
+ remappings=[('visual_slam/image_0', 'front_stereo_camera/left/image_rect'),
+ ('visual_slam/camera_info_0', 'front_stereo_camera/left/camera_info_rect'),
+ ('visual_slam/image_1', 'front_stereo_camera/right/image_rect'),
+ ('visual_slam/camera_info_1', 'front_stereo_camera/right/camera_info_rect')],
+ parameters=[{
+ 'use_sim_time': True,
+ 'enable_image_denoising': True,
+ 'enable_planar_mode': True,
+ 'rectified_images': True,
+ 'publish_map_to_odom_tf': False,
+ 'odom_frame': 'odom',
+ 'enable_slam_visualization': True,
+ 'enable_observations_view': True,
+ 'enable_landmarks_view': True}]
+ )
+
+ isaac_vslam_container = ComposableNodeContainer(
+ name='isaac_visual_slam_container',
+ namespace='',
+ package='rclcpp_components',
+ executable='component_container',
+ composable_node_descriptions=[isaac_visual_slam_node],
+ output='screen',
+ )
+ actions.append(isaac_vslam_container)
+
+ return actions
+
+def generate_launch_description():
+ return LaunchDescription([
+ DeclareLaunchArgument('rtabmap_viz', default_value='true',
+ choices=['true', 'false'], description='Start rtabmap_viz.'),
+ DeclareLaunchArgument('localization', default_value='false',
+ choices=['true', 'false'], description='Start rtabmap in localization mode (a map should have been already created).'),
+ DeclareLaunchArgument('vo', default_value='none',
+ choices=['none', 'rtabmap', 'isaac'], description='Enable visual odometry using one of the approach. None means only wheel odometry is used. If you set this to "isaac", make sure to disable odom -> base_link if it exists, because isaac will publish on same TF!'),
+ DeclareLaunchArgument('stereo', default_value='true',
+ choices=['true', 'false'], description='Use stereo images as input instead of left+depth images.'),
+ DeclareLaunchArgument('image_width', default_value='960',
+ description='Resize input images.'),
+ DeclareLaunchArgument('image_height', default_value='600',
+ description='Resize input images.'),
+ OpaqueFunction(function=launch_setup)
+ ])
diff --git a/rtabmap_demos/launch/isaac/isaac_vslam.launch.py b/rtabmap_demos/launch/isaac/isaac_vslam.launch.py
new file mode 100644
index 00000000..6ad55bd8
--- /dev/null
+++ b/rtabmap_demos/launch/isaac/isaac_vslam.launch.py
@@ -0,0 +1,139 @@
+
+from launch import LaunchDescription
+from launch.actions import DeclareLaunchArgument, OpaqueFunction
+from launch.substitutions import LaunchConfiguration
+from launch.conditions import IfCondition, UnlessCondition
+from launch_ros.actions import Node
+
+def launch_setup(context, *args, **kwargs):
+
+ use_sim_time = LaunchConfiguration('use_sim_time')
+ localization = LaunchConfiguration('localization')
+ localization_value = localization.perform(context)
+ localization_value = localization_value == 'True' or localization_value == 'true'
+ enable_vo = LaunchConfiguration('enable_vo')
+ enable_vo_value = enable_vo.perform(context)
+ enable_vo_value = enable_vo_value == 'True' or enable_vo_value == 'true'
+ stereo = LaunchConfiguration('stereo')
+ stereo_value = stereo.perform(context)
+ stereo_value = stereo_value == 'True' or stereo_value == 'true'
+ rtabmap_viz = LaunchConfiguration('rtabmap_viz')
+ stereo_ns = LaunchConfiguration('stereo_camera_namespace').perform(context)
+
+ parameters={
+ 'frame_id':'base_link',
+ 'use_sim_time': use_sim_time,
+ 'subscribe_rgbd': True,
+ 'subscribe_odom': enable_vo,
+ 'subscribe_odom_info': enable_vo,
+ 'approx_sync': False,
+ 'use_action_for_goal':True,
+ 'Reg/Force3DoF':'true',
+ 'Vis/MinDepth': '0.2',
+ 'GFTT/MinDistance': '5',
+ 'GFTT/QualityLevel': '0.00001',
+ 'Grid/RayTracing':'true', # Fill empty space
+ 'Grid/3D':'false', # Use 2D occupancy
+ 'Grid/NormalsSegmentation':'false', # Use passthrough filter to detect obstacles
+ 'Grid/MaxGroundHeight':'0.15', # All points above 5 cm are obstacles
+ 'Grid/MaxObstacleHeight':'0.5', # All points over 0.5 meter are ignored
+ 'Grid/RangeMin':'0.2', # Ignore invalid points close to camera
+ 'Grid/NoiseFilteringMinNeighbors':'8', # Default stereo is quite noisy, enable noise filter
+ 'Grid/NoiseFilteringRadius':'0.1', # Default stereo is quite noisy, enable noise filter
+ 'Optimizer/GravitySigma':'0' # Disable imu constraints (we are already in 2D)
+ }
+ if enable_vo_value:
+ parameters['guess_frame_id'] = 'odom'
+ else:
+ parameters['odom_frame_id'] = 'odom'
+
+ arguments = []
+ if localization_value:
+ parameters['Mem/IncrementalMemory'] = 'True'
+ parameters['Mem/InitWMWithAllNodes'] = 'True'
+ else:
+ arguments.append('-d') # This will delete the previous database (~/.ros/rtabmap.db)
+
+ remappings=[('rgbd_image', '/'+stereo_ns+'/rgbd_image'),
+ ('map', '/map')]
+ vo_node_prefix = 'rgbd'
+ if stereo_value:
+ vo_node_prefix = 'stereo'
+
+ return [
+ # Sync image data together
+ Node(
+ condition=UnlessCondition(stereo),
+ package='rtabmap_sync', executable='rgbd_sync', output='screen',
+ namespace=stereo_ns,
+ parameters=[{'approx_sync':False, 'use_sim_time':use_sim_time}],
+ remappings=[
+ ('rgb/image', 'left/image_rect'),
+ ('rgb/camera_info', 'left/camera_info_rect'),
+ ('depth/image', 'depth')]),
+
+ Node(
+ condition=IfCondition(stereo),
+ package='rtabmap_sync', executable='stereo_sync', output='screen',
+ namespace=stereo_ns,
+ parameters=[{'approx_sync':False, 'use_sim_time':use_sim_time}],
+ remappings=[
+ ('left/image_rect', 'left/image_rect'),
+ ('left/camera_info', 'left/camera_info_rect'),
+ ('right/image_rect', 'right/image_rect'),
+ ('right/camera_info', 'right/camera_info_rect')]),
+
+ Node(
+ condition=IfCondition(enable_vo),
+ package='rtabmap_odom', executable=vo_node_prefix+'_odometry', output='screen',
+ namespace='rtabmap',
+ parameters=[parameters, {'odom_frame_id': 'vo'}],
+ remappings=remappings),
+
+ # VSLAM:
+ Node(
+ package='rtabmap_slam', executable='rtabmap', output='screen',
+ namespace='rtabmap',
+ parameters=[parameters],
+ remappings=remappings,
+ arguments=arguments),
+
+ # Visualization:
+ Node(
+ condition=IfCondition(rtabmap_viz),
+ package='rtabmap_viz', executable='rtabmap_viz', output='screen',
+ namespace='rtabmap',
+ parameters=[parameters],
+ remappings=remappings),
+ ]
+
+def generate_launch_description():
+ return LaunchDescription([
+
+ # Launch arguments
+ DeclareLaunchArgument(
+ 'use_sim_time', default_value='true',
+ description='Use simulation (Gazebo) clock if true'),
+
+ DeclareLaunchArgument(
+ 'localization', default_value='false',
+ description='Launch in localization mode.'),
+
+ DeclareLaunchArgument(
+ 'enable_vo', default_value='false',
+ description='Enable RTAB-Map\'s visual odometry.'),
+
+ DeclareLaunchArgument(
+ 'rtabmap_viz', default_value='true',
+ description='Launch rtabmap_viz for visualization.'),
+
+ DeclareLaunchArgument(
+ 'stereo', default_value='false',
+ description='Use stereo images as input instead of left+depth images.'),
+
+ DeclareLaunchArgument(
+ 'stereo_camera_namespace', default_value='front_stereo_camera',
+ description='Namespace of the stereo camera.'),
+
+ OpaqueFunction(function=launch_setup)
+ ])
diff --git a/rtabmap_demos/launch/multisession_mapping_demo.launch.py b/rtabmap_demos/launch/multisession_mapping_demo.launch.py
new file mode 100644
index 00000000..b5841039
--- /dev/null
+++ b/rtabmap_demos/launch/multisession_mapping_demo.launch.py
@@ -0,0 +1,115 @@
+# Requirements:
+# Download one or more rosbags:
+# * map1.db3: https://drive.google.com/file/d/1XajzWm0u1Tk7m7x63ybcKVMXj80r5P6r/view?usp=drive_link
+# * map2.db3: https://drive.google.com/file/d/1_FxEalE2O-DQKq2tRpLIpDn5Mbvu0jZc/view?usp=drive_link
+# * map3.db3: https://drive.google.com/file/d/1dJzMOoRPA28gQZUIWCeAa08Qn4wG9oRw/view?usp=drive_link
+# * map4.db3: https://drive.google.com/file/d/19Y6yye0ndIIwdhEWMwTdoiSy9WlKS44c/view?usp=drive_link
+# * map5.db3: https://drive.google.com/file/d/1zCx4Q4SftPplQtW1xeG-W3OkbTxwd5GD/view?usp=drive_link
+#
+# Example:
+#
+# SLAM:
+# $ rm ~/.ros/rtabmap.db
+# $ ros2 launch rtabmap_demos multisession_mapping_demo.launch.py
+#
+# Rosbag:
+# $ ros2 bag play map1.db3 --clock
+# when done, you can play the next bag(s):
+# $ ros2 bag play map2.db3 --clock
+# $ ros2 bag play map3.db3 --clock
+# $ ros2 bag play map4.db3 --clock
+# $ ros2 bag play map5.db3 --clock
+#
+# Refer to this paper for more info: https://arxiv.org/abs/2407.15305
+#
+from launch import LaunchDescription
+from launch.actions import DeclareLaunchArgument
+from launch.substitutions import LaunchConfiguration
+from launch.conditions import IfCondition
+from launch_ros.actions import Node
+from launch_ros.actions import SetParameter
+import os
+from ament_index_python.packages import get_package_share_directory
+
+def generate_launch_description():
+
+ parameters={
+ 'frame_id':'base_footprint',
+ 'odom_frame_id':'odom',
+ 'odom_tf_linear_variance':0.001,
+ 'odom_tf_angular_variance':0.001,
+ 'subscribe_rgbd':True,
+ 'subscribe_scan':True,
+ 'approx_sync':True,
+ 'sync_queue_size': 10,
+ # RTAB-Map's internal parameters should be strings
+ 'RGBD/NeighborLinkRefining': 'false',
+ 'RGBD/ProximityBySpace': 'false', # Referred paper did only global loop closure detection
+ 'RGBD/OptimizeFromGraphEnd': 'true',
+ 'Reg/Strategy': '1',
+ 'Icp/Iterations': '30',
+ 'Icp/VoxelSize': '0',
+ 'Vis/MinInliers': '12',
+ 'Vis/MaxDepth': '0',
+ 'RGBD/AngularUpdate': '0.01',
+ 'RGBD/LinearUpdate': '0.01',
+ 'Rtabmap/TimeThr': '700',
+ 'Mem/RehearsalSimilarity': '0.30', # Referred paper used 0.45 with SURF, here with SIFT, we will use 0.3
+ 'Kp/TfIdfLikelihoodUsed': 'false',
+ 'Bayes/FullPredictionUpdate': 'true',
+ 'Kp/DetectorStrategy': '1', # Referred paper used SURF (0), here use SIFT as it is available with opencv binaries
+ 'Vis/FeatureType': '1', # Referred paper used SURF (0), here use SIFT as it is available with opencv binaries
+ 'Kp/MaxFeatures': '400',
+ 'Reg/Force3DoF': 'true',
+ 'RGBD/OptimizeMaxError': '10',
+ 'Optimizer/Strategy': '2', # Referred paper used TORO (0), latest version recommends GTSAM (2)
+ 'Optimizer/Iterations': '100',
+ 'Kp/IncrementalFlann': 'false', # Referred paper didn't use incremental FLANN
+ 'Icp/MaxTranslation': '0.5',
+ }
+
+ remappings=[
+ ('rgb/image', '/data_throttled_image'),
+ ('depth/image', '/data_throttled_image_depth'),
+ ('rgb/camera_info', '/data_throttled_camera_info'),
+ ('scan', '/base_scan')]
+
+ config_rviz = os.path.join(
+ get_package_share_directory('rtabmap_demos'), 'config', 'demo_robot_mapping.rviz'
+ )
+
+ return LaunchDescription([
+
+ # Launch arguments
+ DeclareLaunchArgument('rtabmap_viz', default_value='true', description='Launch RTAB-Map UI (optional).'),
+ DeclareLaunchArgument('rviz', default_value='false', description='Launch RVIZ (optional).'),
+ DeclareLaunchArgument('rviz_cfg', default_value=config_rviz, description='Configuration path of rviz2.'),
+
+ SetParameter(name='use_sim_time', value=True),
+
+ # Nodes to launch
+ Node(
+ package='rtabmap_sync', executable='rgbd_sync', output='screen',
+ parameters=[parameters,
+ {'rgb_image_transport':'compressed',
+ 'depth_image_transport':'compressedDepth',
+ 'approx_sync_max_interval': 0.02}],
+ remappings=remappings),
+
+ # SLAM node:
+ Node(
+ package='rtabmap_slam', executable='rtabmap', output='screen',
+ parameters=[parameters],
+ remappings=remappings),
+
+ # Visualization:
+ Node(
+ package='rtabmap_viz', executable='rtabmap_viz', output='screen',
+ condition=IfCondition(LaunchConfiguration("rtabmap_viz")),
+ parameters=[parameters],
+ remappings=remappings),
+ Node(
+ package='rviz2', executable='rviz2', name="rviz2", output='screen',
+ condition=IfCondition(LaunchConfiguration("rviz")),
+ arguments=[["-d"], [LaunchConfiguration("rviz_cfg")]]),
+ ])
diff --git a/rtabmap_demos/launch/robot_mapping_demo.launch.py b/rtabmap_demos/launch/robot_mapping_demo.launch.py
new file mode 100644
index 00000000..d6ed7451
--- /dev/null
+++ b/rtabmap_demos/launch/robot_mapping_demo.launch.py
@@ -0,0 +1,112 @@
+# Requirements:
+# Download rosbag:
+# * demo_mapping.db3: https://drive.google.com/file/d/1v9qJ2U7GlYhqBJr7OQHWbDSCfgiVaLWb/view?usp=drive_link
+#
+# Example:
+#
+# SLAM:
+# $ ros2 launch rtabmap_demos robot_mapping_demo.launch.py rviz:=true rtabmap_viz:=true
+#
+# Rosbag:
+# $ ros2 bag play demo_mapping.db3 --clock
+#
+
+from launch import LaunchDescription
+from launch.actions import DeclareLaunchArgument
+from launch.substitutions import LaunchConfiguration
+from launch.conditions import IfCondition, UnlessCondition
+from launch_ros.actions import Node
+from launch_ros.actions import SetParameter
+import os
+from ament_index_python.packages import get_package_share_directory
+
+def generate_launch_description():
+
+ localization = LaunchConfiguration('localization')
+
+ parameters={
+ 'frame_id':'base_footprint',
+ 'odom_frame_id':'odom',
+ 'odom_tf_linear_variance':0.001,
+ 'odom_tf_angular_variance':0.001,
+ 'subscribe_rgbd':True,
+ 'subscribe_scan':True,
+ 'approx_sync':True,
+ 'sync_queue_size': 10,
+ # RTAB-Map's internal parameters should be strings
+ 'RGBD/NeighborLinkRefining': 'true', # Do odometry correction with consecutive laser scans
+ 'RGBD/ProximityBySpace': 'true', # Local loop closure detection (using estimated position) with locations in WM
+ 'RGBD/ProximityByTime': 'false', # Local loop closure detection with locations in STM
+ 'RGBD/ProximityPathMaxNeighbors': '10', # Do also proximity detection by space by merging close scans together.
+ 'Reg/Strategy': '1', # 0=Visual, 1=ICP, 2=Visual+ICP
+ 'Vis/MinInliers': '12', # 3D visual words minimum inliers to accept loop closure
+ 'RGBD/OptimizeFromGraphEnd': 'false', # Optimize graph from initial node so /map -> /odom transform will be generated
+ 'RGBD/OptimizeMaxError': '4', # Reject any loop closure causing large errors (>3x link's covariance) in the map
+ 'Reg/Force3DoF': 'true', # 2D SLAM
+ 'Grid/FromDepth': 'false', # Create 2D occupancy grid from laser scan
+ 'Mem/STMSize': '30', # increased to 30 to avoid adding too many loop closures on just seen locations
+ 'RGBD/LocalRadius': '5', # limit length of proximity detections
+ 'Icp/CorrespondenceRatio': '0.2', # minimum scan overlap to accept loop closure
+ 'Icp/PM': 'false',
+ 'Icp/PointToPlane': 'false',
+ 'Icp/MaxCorrespondenceDistance': '0.15',
+ 'Icp/VoxelSize': '0.05'
+ }
+
+ remappings=[
+ ('rgb/image', '/data_throttled_image'),
+ ('depth/image', '/data_throttled_image_depth'),
+ ('rgb/camera_info', '/data_throttled_camera_info'),
+ ('scan', '/jn0/base_scan')]
+
+ config_rviz = os.path.join(
+ get_package_share_directory('rtabmap_demos'), 'config', 'demo_robot_mapping.rviz'
+ )
+
+ return LaunchDescription([
+
+ # Launch arguments
+ DeclareLaunchArgument('rtabmap_viz', default_value='true', description='Launch RTAB-Map UI (optional).'),
+ DeclareLaunchArgument('rviz', default_value='false', description='Launch RVIZ (optional).'),
+ DeclareLaunchArgument('localization', default_value='false', description='Launch in localization mode.'),
+ DeclareLaunchArgument('rviz_cfg', default_value=config_rviz, description='Configuration path of rviz2.'),
+
+ SetParameter(name='use_sim_time', value=True),
+
+ # Nodes to launch
+ Node(
+ package='rtabmap_sync', executable='rgbd_sync', output='screen',
+ parameters=[parameters,
+ {'rgb_image_transport':'compressed',
+ 'depth_image_transport':'compressedDepth',
+ 'approx_sync_max_interval': 0.02}],
+ remappings=remappings),
+
+ # SLAM mode:
+ Node(
+ condition=UnlessCondition(localization),
+ package='rtabmap_slam', executable='rtabmap', output='screen',
+ parameters=[parameters],
+ remappings=remappings,
+ arguments=['-d']), # This will delete the previous database (~/.ros/rtabmap.db)
+
+ # Localization mode:
+ Node(
+ condition=IfCondition(localization),
+ package='rtabmap_slam', executable='rtabmap', output='screen',
+ parameters=[parameters,
+ {'Mem/IncrementalMemory':'False',
+ 'Mem/InitWMWithAllNodes':'True'}],
+ remappings=remappings),
+
+ # Visualization:
+ Node(
+ package='rtabmap_viz', executable='rtabmap_viz', output='screen',
+ condition=IfCondition(LaunchConfiguration("rtabmap_viz")),
+ parameters=[parameters],
+ remappings=remappings),
+ Node(
+ package='rviz2', executable='rviz2', name="rviz2", output='screen',
+ condition=IfCondition(LaunchConfiguration("rviz")),
+ arguments=[["-d"], [LaunchConfiguration("rviz_cfg")]]),
+ ])
diff --git a/rtabmap_demos/launch/stereo_outdoor_demo.launch.py b/rtabmap_demos/launch/stereo_outdoor_demo.launch.py
new file mode 100644
index 00000000..41036389
--- /dev/null
+++ b/rtabmap_demos/launch/stereo_outdoor_demo.launch.py
@@ -0,0 +1,155 @@
+# Requirements:
+# Download one or both rosbags:
+# * stereo_outdoorA.db3: https://drive.google.com/file/d/1O7mCXg_sw4tZY1S88a-n96O6OulmqvqI/view?usp=drive_link
+# * stereo_outdoorB.db3: https://drive.google.com/file/d/1mSu7418Fkbe-hIz2-3Mi936PrWuD2un_/view?usp=drive_link
+#
+# Example:
+#
+# SLAM:
+# $ ros2 launch rtabmap_demos stereo_outdoor_demo.launch.py rviz:=true rtabmap_viz:=true
+#
+# Rosbag:
+# $ ros2 bag play stereo_outdoorA.db3 --clock
+# when done, you can play the secon bag:
+# $ ros2 bag play stereo_outdoorB.db3 --clock
+#
+
+from launch import LaunchDescription
+from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription, GroupAction
+from launch.launch_description_sources import PythonLaunchDescriptionSource
+from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
+from launch.conditions import IfCondition, UnlessCondition
+from launch_ros.actions import Node, SetParameter, SetRemap
+import os
+from ament_index_python.packages import get_package_share_directory
+
+def generate_launch_description():
+
+ pkg_stereo_image_proc = get_package_share_directory(
+ 'stereo_image_proc')
+
+ # Paths
+ stereo_image_proc_launch = PathJoinSubstitution(
+ [pkg_stereo_image_proc, 'launch', 'stereo_image_proc.launch.py'])
+
+ localization = LaunchConfiguration('localization')
+
+ parameters={
+ 'frame_id':'base_footprint',
+ 'subscribe_rgbd':True,
+ 'approx_sync':False, # odom is generated from images, so we can exactly sync all inputs
+ 'map_negative_poses_ignored':True,
+ 'subscribe_odom_info': True,
+ # RTAB-Map's internal parameters should be strings
+ 'OdomF2M/MaxSize': '1000',
+ 'GFTT/MinDistance': '10',
+ 'GFTT/QualityLevel': '0.00001',
+ #'Kp/DetectorStrategy': '6', # Uncommment to match ros1 noetic results, but opencv should be built with xfeatures2d
+ #'Vis/FeatureType': '6' # Uncommment to match ros1 noetic results, but opencv should be built with xfeatures2d
+ }
+
+ remappings=[
+ ('rgbd_image', '/stereo_camera/rgbd_image'),
+ ('odom', '/vo')]
+
+ config_rviz = os.path.join(
+ get_package_share_directory('rtabmap_demos'), 'config', 'demo_robot_mapping.rviz'
+ )
+
+ return LaunchDescription([
+
+ # Launch arguments
+ DeclareLaunchArgument('rtabmap_viz', default_value='false', description='Launch RTAB-Map UI (optional).'),
+ DeclareLaunchArgument('rviz', default_value='true', description='Launch RVIZ (optional).'),
+ DeclareLaunchArgument('localization', default_value='false', description='Launch in localization mode.'),
+ DeclareLaunchArgument('rviz_cfg', default_value=config_rviz, description='Configuration path of rviz2.'),
+
+ SetParameter(name='use_sim_time', value=True),
+
+ # Nodes to launch
+
+ # Uncompress images for stereo_image_rect and remap to expected names from stereo_image_proc
+ Node(
+ package='image_transport', executable='republish', name='republish_left', output='screen',
+ namespace='stereo_camera',
+ arguments=['compressed', 'raw'],
+ remappings=[('in/compressed', 'left/image_raw_throttle/compressed'),
+ ('out', 'left/image_raw')]),
+ Node(
+ package='image_transport', executable='republish', name='republish_right', output='screen',
+ namespace='stereo_camera',
+ arguments=['compressed', 'raw'],
+ remappings=[('in/compressed', 'right/image_raw_throttle/compressed'),
+ ('out', 'right/image_raw')]),
+
+ # Run the ROS package stereo_image_proc for image rectification
+ GroupAction(
+ actions=[
+
+ SetRemap(src='camera_info',dst='camera_info_throttle'),
+ SetRemap(src='camera_info',dst='camera_info_throttle'),
+
+ IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([stereo_image_proc_launch]),
+ launch_arguments=[
+ ('left_namespace', 'stereo_camera/left'),
+ ('right_namespace', 'stereo_camera/right'),
+ ('disparity_range', '128'),
+ ]
+ ),
+ ]
+ ),
+
+ # Synchronize stereo data together in a single topic
+ # Issue: stereo_img_proc doesn't produce color and
+ # grayscale images exactly the same (there is a small
+ # vertical shift with color), we should use grayscale for
+ # left and right images to get similar results than on ros1 noetic.
+ Node(
+ package='rtabmap_sync', executable='stereo_sync', output='screen',
+ namespace='stereo_camera',
+ remappings=[
+ ('left/image_rect', 'left/image_rect'),
+ ('right/image_rect', 'right/image_rect'),
+ ('left/camera_info', 'left/camera_info_throttle'),
+ ('right/camera_info', 'right/camera_info_throttle')]),
+
+ # Visual odometry
+ Node(
+ package='rtabmap_odom', executable='stereo_odometry', output='screen',
+ parameters=[parameters],
+ remappings=remappings),
+
+ # SLAM mode:
+ Node(
+ condition=UnlessCondition(localization),
+ package='rtabmap_slam', executable='rtabmap', output='screen',
+ parameters=[parameters],
+ remappings=remappings,
+ arguments=['-d']), # This will delete the previous database (~/.ros/rtabmap.db)
+
+ # Localization mode:
+ Node(
+ condition=IfCondition(localization),
+ package='rtabmap_slam', executable='rtabmap', output='screen',
+ parameters=[parameters,
+ {'Mem/IncrementalMemory':'False',
+ 'Mem/InitWMWithAllNodes':'True'}],
+ remappings=remappings),
+
+ # Visualization:
+ Node(
+ package='rtabmap_viz', executable='rtabmap_viz', output='screen',
+ condition=IfCondition(LaunchConfiguration("rtabmap_viz")),
+ parameters=[parameters],
+ remappings=remappings),
+ Node(
+ package='rviz2', executable='rviz2', name="rviz2", output='screen',
+ condition=IfCondition(LaunchConfiguration("rviz")),
+ arguments=[["-d"], [LaunchConfiguration("rviz_cfg")]]),
+ ])
+
+
+
+
+
diff --git a/rtabmap_demos/launch/turtlebot3_rgbd.launch.py b/rtabmap_demos/launch/turtlebot3/turtlebot3_rgbd.launch.py
similarity index 57%
rename from rtabmap_demos/launch/turtlebot3_rgbd.launch.py
rename to rtabmap_demos/launch/turtlebot3/turtlebot3_rgbd.launch.py
index 93f3d97d..b9076449 100644
--- a/rtabmap_demos/launch/turtlebot3_rgbd.launch.py
+++ b/rtabmap_demos/launch/turtlebot3/turtlebot3_rgbd.launch.py
@@ -1,34 +1,15 @@
-# Requirements:
-# Install Turtlebot3 packages
-# Modify turtlebot3_waffle SDF:
-# 1) Edit /opt/ros/$ROS_DISTRO/share/turtlebot3_gazebo/models/turtlebot3_waffle/model.sdf
-# 2) Add
-#
-# camera_rgb_frame
-# camera_rgb_optical_frame
-# 0 0 0 -1.57079632679 0 -1.57079632679
-#
-# 0 0 1
-#
-#
-# 3) Rename to
-# 4) Add
-# 5) Change to
-# 6) Change image width/height from 1920x1080 to 640x480
-# 7) Note that we can increase min scan range from 0.12 to 0.2 to avoid having scans
-# hitting the robot itself
# Example:
-# $ export TURTLEBOT3_MODEL=waffle
-# $ ros2 launch turtlebot3_gazebo turtlebot3_world.launch.py
+#
+# Bringup turtlebot3:
+# $ export TURTLEBOT3_MODEL=waffle
+# $ export LDS_MODEL=LDS-01
+# $ ros2 launch turtlebot3_bringup robot.launch.py
#
# SLAM:
-# $ ros2 launch rtabmap_demos turtlebot3_rgbd.launch.py
-# OR
-# $ ros2 launch rtabmap_launch rtabmap.launch.py visual_odometry:=false frame_id:=base_footprint odom_topic:=/odom args:="-d" use_sim_time:=true rgb_topic:=/camera/image_raw depth_topic:=/camera/depth/image_raw camera_info_topic:=/camera/camera_info approx_sync:=true qos:=2
-# $ ros2 run topic_tools relay /rtabmap/map /map
+# $ ros2 launch rtabmap_demos turtlebot3_rgbd.launch.py
#
# Navigation (install nav2_bringup package):
-# $ ros2 launch nav2_bringup navigation_launch.py use_sim_time:=True
+# $ ros2 launch nav2_bringup navigation_launch.py
# $ ros2 launch nav2_bringup rviz_launch.py
#
# Teleop:
@@ -43,7 +24,6 @@ from launch_ros.actions import Node
def generate_launch_description():
use_sim_time = LaunchConfiguration('use_sim_time')
- qos = LaunchConfiguration('qos')
localization = LaunchConfiguration('localization')
parameters={
@@ -51,9 +31,13 @@ def generate_launch_description():
'use_sim_time':use_sim_time,
'subscribe_depth':True,
'use_action_for_goal':True,
- 'qos_image':qos,
- 'qos_imu':qos,
'Reg/Force3DoF':'true',
+ 'Grid/RayTracing':'true', # Fill empty space
+ 'Grid/3D':'false', # Use 2D occupancy
+ 'Grid/RangeMax':'3',
+ 'Grid/NormalsSegmentation':'false', # Use passthrough filter to detect obstacles
+ 'Grid/MaxGroundHeight':'0.05', # All points above 5 cm are obstacles
+ 'Grid/MaxObstacleHeight':'0.4', # All points over 1 meter are ignored
'Optimizer/GravitySigma':'0' # Disable imu constraints (we are already in 2D)
}
@@ -69,10 +53,6 @@ def generate_launch_description():
'use_sim_time', default_value='true',
description='Use simulation (Gazebo) clock if true'),
- DeclareLaunchArgument(
- 'qos', default_value='2',
- description='QoS used for input sensor topics'),
-
DeclareLaunchArgument(
'localization', default_value='false',
description='Launch in localization mode.'),
@@ -100,4 +80,22 @@ def generate_launch_description():
package='rtabmap_viz', executable='rtabmap_viz', output='screen',
parameters=[parameters],
remappings=remappings),
+
+ # Obstacle detection with the camera for nav2 local costmap.
+ # First, we need to convert depth image to a point cloud.
+ # Second, we segment the floor from the obstacles.
+ Node(
+ package='rtabmap_util', executable='point_cloud_xyz', output='screen',
+ parameters=[{'decimation': 2,
+ 'max_depth': 3.0,
+ 'voxel_size': 0.02}],
+ remappings=[('depth/image', '/camera/depth/image_raw'),
+ ('depth/camera_info', '/camera/camera_info'),
+ ('cloud', '/camera/cloud')]),
+ Node(
+ package='rtabmap_util', executable='obstacles_detection', output='screen',
+ parameters=[parameters],
+ remappings=[('cloud', '/camera/cloud'),
+ ('obstacles', '/camera/obstacles'),
+ ('ground', '/camera/ground')]),
])
diff --git a/rtabmap_demos/launch/turtlebot3/turtlebot3_rgbd_fake_scan.launch.py b/rtabmap_demos/launch/turtlebot3/turtlebot3_rgbd_fake_scan.launch.py
new file mode 100644
index 00000000..1ccacd65
--- /dev/null
+++ b/rtabmap_demos/launch/turtlebot3/turtlebot3_rgbd_fake_scan.launch.py
@@ -0,0 +1,143 @@
+# Example:
+#
+# Bringup turtlebot3:
+# $ export TURTLEBOT3_MODEL=waffle
+# $ export LDS_MODEL=LDS-01
+# $ ros2 launch turtlebot3_bringup robot.launch.py
+#
+# SLAM:
+# $ ros2 launch rtabmap_demos turtlebot3_rgbd_fake_scan.launch.py
+#
+# Navigation (install nav2_bringup package):
+# $ ros2 launch nav2_bringup navigation_launch.py
+# $ ros2 launch nav2_bringup rviz_launch.py
+#
+# Teleop:
+# $ ros2 run turtlebot3_teleop teleop_keyboard
+
+from launch import LaunchDescription
+from launch.actions import DeclareLaunchArgument, SetEnvironmentVariable
+from launch.substitutions import LaunchConfiguration
+from launch.conditions import IfCondition, UnlessCondition
+from launch_ros.actions import Node
+
+
+def generate_launch_description():
+
+ use_sim_time = LaunchConfiguration('use_sim_time')
+ localization = LaunchConfiguration('localization')
+
+ parameters={
+ 'frame_id':'base_footprint',
+ 'use_sim_time':use_sim_time,
+ 'subscribe_rgbd':True,
+ 'subscribe_scan_cloud':True,
+ 'use_action_for_goal':True,
+ 'scan_cloud_is_2d': True,
+ # RTAB-Map's parameters should be strings:
+ 'Reg/Strategy':'1',
+ 'Reg/Force3DoF':'true',
+ 'Optimizer/GravitySigma':'0' # Disable imu constraints (we are already in 2D)
+ }
+
+ remappings=[
+ ('rgb/image', '/camera/image_raw'),
+ ('rgb/camera_info', '/camera/camera_info'),
+ ('depth/image', '/camera/depth/image_raw'),
+ ('scan_cloud', 'assembled_cloud')]
+
+ return LaunchDescription([
+
+ # Launch arguments
+ DeclareLaunchArgument(
+ 'use_sim_time', default_value='false',
+ description='Use simulation (Gazebo) clock if true'),
+
+ DeclareLaunchArgument(
+ 'localization', default_value='false',
+ description='Launch in localization mode.'),
+
+ # Nodes to launch
+ Node(
+ package='rtabmap_sync', executable='rgbd_sync', output='screen',
+ parameters=[{'approx_sync':False, 'use_sim_time':use_sim_time}],
+ remappings=remappings),
+
+ # Convert middle row of depth pixels to a fake laser scan
+ Node(
+ package='depthimage_to_laserscan', executable='depthimage_to_laserscan_node', output='screen',
+ parameters=[{
+ 'use_sim_time':use_sim_time,
+ 'range_max': 5.0
+ }],
+ remappings=[
+ ('depth', '/camera/depth/image_raw'),
+ ('depth_camera_info', '/camera/camera_info'),
+ ('scan', '/camera/scan')
+ ]),
+
+ # Just to convert the fake laser scan to PointCloud2
+ Node(
+ package='rtabmap_util', executable='lidar_deskewing', output='screen',
+ parameters=[{'use_sim_time':use_sim_time,
+ 'fixed_frame_id': 'camera_link'}], # use camera frame
+ remappings=[
+ ('input_scan', '/camera/scan')
+ ]),
+
+ # Assemble the fake laser scans using a circular buffer, then feed that cloud to rtabmap
+ Node(
+ package='rtabmap_util', executable='point_cloud_assembler', output='screen',
+ parameters=[{'use_sim_time':use_sim_time,
+ 'max_clouds': 20,
+ 'voxel_size': 0.05,
+ 'wait_for_transform': 1.0,
+ 'linear_update': 0.3,
+ 'angular_update': 0.5,
+ 'circular_buffer': True,
+ 'frame_id': 'base_link'}],
+ remappings=[
+ ('assembled_cloud', 'assembled_cloud'),
+ ('cloud', '/camera/scan/deskewed')
+ ]),
+
+ # SLAM Mode:
+ Node(
+ condition=UnlessCondition(localization),
+ package='rtabmap_slam', executable='rtabmap', output='screen',
+ parameters=[parameters],
+ remappings=remappings,
+ arguments=['-d']),
+
+ # Localization mode:
+ Node(
+ condition=IfCondition(localization),
+ package='rtabmap_slam', executable='rtabmap', output='screen',
+ parameters=[parameters,
+ {'Mem/IncrementalMemory':'False',
+ 'Mem/InitWMWithAllNodes':'True'}],
+ remappings=remappings),
+
+ Node(
+ package='rtabmap_viz', executable='rtabmap_viz', output='screen',
+ parameters=[parameters],
+ remappings=remappings),
+
+ # Obstacle detection with the camera for nav2 local costmap.
+ # First, we need to convert depth image to a point cloud.
+ # Second, we segment the floor from the obstacles.
+ Node(
+ package='rtabmap_util', executable='point_cloud_xyz', output='screen',
+ parameters=[{'decimation': 2,
+ 'max_depth': 3.0,
+ 'voxel_size': 0.02}],
+ remappings=[('depth/image', '/camera/depth/image_raw'),
+ ('depth/camera_info', '/camera/camera_info'),
+ ('cloud', '/camera/cloud')]),
+ Node(
+ package='rtabmap_util', executable='obstacles_detection', output='screen',
+ parameters=[parameters],
+ remappings=[('cloud', '/camera/cloud'),
+ ('obstacles', '/camera/obstacles'),
+ ('ground', '/camera/ground')]),
+ ])
diff --git a/rtabmap_demos/launch/turtlebot3_rgbd_sync.launch.py b/rtabmap_demos/launch/turtlebot3/turtlebot3_rgbd_scan.launch.py
similarity index 56%
rename from rtabmap_demos/launch/turtlebot3_rgbd_sync.launch.py
rename to rtabmap_demos/launch/turtlebot3/turtlebot3_rgbd_scan.launch.py
index e1034ab7..733330fc 100644
--- a/rtabmap_demos/launch/turtlebot3_rgbd_sync.launch.py
+++ b/rtabmap_demos/launch/turtlebot3/turtlebot3_rgbd_scan.launch.py
@@ -1,34 +1,15 @@
-# Requirements:
-# Install Turtlebot3 packages
-# Modify turtlebot3_waffle SDF:
-# 1) Edit /opt/ros/$ROS_DISTRO/share/turtlebot3_gazebo/models/turtlebot3_waffle/model.sdf
-# 2) Add
-#
-# camera_rgb_frame
-# camera_rgb_optical_frame
-# 0 0 0 -1.57079632679 0 -1.57079632679
-#
-# 0 0 1
-#
-#
-# 3) Rename to
-# 4) Add
-# 5) Change to
-# 6) Change image width/height from 1920x1080 to 640x480
-# 7) Note that we can increase min scan range from 0.12 to 0.2 to avoid having scans
-# hitting the robot itself
# Example:
-# $ export TURTLEBOT3_MODEL=waffle
-# $ ros2 launch turtlebot3_gazebo turtlebot3_world.launch.py
+#
+# Bringup turtlebot3:
+# $ export TURTLEBOT3_MODEL=waffle
+# $ export LDS_MODEL=LDS-01
+# $ ros2 launch turtlebot3_bringup robot.launch.py
#
# SLAM:
-# $ ros2 launch rtabmap_demos turtlebot3_rgbd_sync.launch.py
-# OR
-# $ ros2 launch rtabmap_launch rtabmap.launch.py visual_odometry:=false frame_id:=base_footprint subscribe_scan:=true approx_sync:=true approx_rgbd_sync:=false odom_topic:=/odom args:="-d --RGBD/NeighborLinkRefining true --Reg/Strategy 1 --Reg/Force3DoF true --Grid/RangeMin 0.2" use_sim_time:=true rgbd_sync:=true rgb_topic:=/camera/image_raw depth_topic:=/camera/depth/image_raw camera_info_topic:=/camera/camera_info qos:=2
-# $ ros2 run topic_tools relay /rtabmap/map /map
+# $ ros2 launch rtabmap_demos turtlebot3_rgbd_scan.launch.py
#
# Navigation (install nav2_bringup package):
-# $ ros2 launch nav2_bringup navigation_launch.py use_sim_time:=True
+# $ ros2 launch nav2_bringup navigation_launch.py
# $ ros2 launch nav2_bringup rviz_launch.py
#
# Teleop:
@@ -44,7 +25,6 @@ from launch_ros.actions import Node
def generate_launch_description():
use_sim_time = LaunchConfiguration('use_sim_time')
- qos = LaunchConfiguration('qos')
localization = LaunchConfiguration('localization')
parameters={
@@ -53,13 +33,17 @@ def generate_launch_description():
'subscribe_rgbd':True,
'subscribe_scan':True,
'use_action_for_goal':True,
- 'qos_scan':qos,
- 'qos_image':qos,
- 'qos_imu':qos,
# RTAB-Map's parameters should be strings:
'Reg/Strategy':'1',
'Reg/Force3DoF':'true',
'RGBD/NeighborLinkRefining':'True',
+ 'Grid/RayTracing':'true', # Fill empty space
+ 'Grid/3D':'false', # Use 2D occupancy
+ 'Grid/RangeMax':'3',
+ 'Grid/NormalsSegmentation':'false', # Use passthrough filter to detect obstacles
+ 'Grid/Sensor':'2', # Use both laser scan and camera for obstacle detection in global map
+ 'Grid/MaxGroundHeight':'0.05', # All points above 5 cm are obstacles
+ 'Grid/MaxObstacleHeight':'0.4', # All points over 1 meter are ignored
'Grid/RangeMin':'0.2', # ignore laser scan points on the robot itself
'Optimizer/GravitySigma':'0' # Disable imu constraints (we are already in 2D)
}
@@ -73,13 +57,9 @@ def generate_launch_description():
# Launch arguments
DeclareLaunchArgument(
- 'use_sim_time', default_value='true',
+ 'use_sim_time', default_value='false',
description='Use simulation (Gazebo) clock if true'),
-
- DeclareLaunchArgument(
- 'qos', default_value='2',
- description='QoS used for input sensor topics'),
-
+
DeclareLaunchArgument(
'localization', default_value='false',
description='Launch in localization mode.'),
@@ -87,7 +67,7 @@ def generate_launch_description():
# Nodes to launch
Node(
package='rtabmap_sync', executable='rgbd_sync', output='screen',
- parameters=[{'approx_sync':False, 'use_sim_time':use_sim_time, 'qos':qos}],
+ parameters=[{'approx_sync':False, 'use_sim_time':use_sim_time}],
remappings=remappings),
# SLAM Mode:
@@ -111,4 +91,22 @@ def generate_launch_description():
package='rtabmap_viz', executable='rtabmap_viz', output='screen',
parameters=[parameters],
remappings=remappings),
+
+ # Obstacle detection with the camera for nav2 local costmap.
+ # First, we need to convert depth image to a point cloud.
+ # Second, we segment the floor from the obstacles.
+ Node(
+ package='rtabmap_util', executable='point_cloud_xyz', output='screen',
+ parameters=[{'decimation': 2,
+ 'max_depth': 3.0,
+ 'voxel_size': 0.02}],
+ remappings=[('depth/image', '/camera/depth/image_raw'),
+ ('depth/camera_info', '/camera/camera_info'),
+ ('cloud', '/camera/cloud')]),
+ Node(
+ package='rtabmap_util', executable='obstacles_detection', output='screen',
+ parameters=[parameters],
+ remappings=[('cloud', '/camera/cloud'),
+ ('obstacles', '/camera/obstacles'),
+ ('ground', '/camera/ground')]),
])
diff --git a/rtabmap_demos/launch/turtlebot3_scan.launch.py b/rtabmap_demos/launch/turtlebot3/turtlebot3_scan.launch.py
similarity index 54%
rename from rtabmap_demos/launch/turtlebot3_scan.launch.py
rename to rtabmap_demos/launch/turtlebot3/turtlebot3_scan.launch.py
index 721180a9..e027c7e0 100644
--- a/rtabmap_demos/launch/turtlebot3_scan.launch.py
+++ b/rtabmap_demos/launch/turtlebot3/turtlebot3_scan.launch.py
@@ -1,36 +1,32 @@
-# Requirements:
-# Install Turtlebot3 packages
-# Note that we can edit /opt/ros/$ROS_DISTRO/share/turtlebot3_gazebo/models/turtlebot_waffle/model.sdf
-# to increase min scan range from 0.12 to 0.2 to avoid having scans
-# hitting the robot itself
# Example:
-# $ export TURTLEBOT3_MODEL=waffle
-# $ ros2 launch turtlebot3_gazebo turtlebot3_world.launch.py
+#
+# Bringup turtlebot3:
+# $ export TURTLEBOT3_MODEL=waffle
+# $ export LDS_MODEL=LDS-01
+# $ ros2 launch turtlebot3_bringup robot.launch.py
#
# SLAM:
-# $ ros2 launch rtabmap_demos turtlebot3_scan.launch.py
-# OR
-# $ ros2 launch rtabmap_launch rtabmap.launch.py visual_odometry:=false frame_id:=base_footprint subscribe_scan:=true depth:=false approx_sync:=true odom_topic:=/odom args:="-d --RGBD/NeighborLinkRefining true --Reg/Strategy 1 --Reg/Force3DoF true --Grid/RangeMin 0.2" use_sim_time:=true qos:=2
-# $ ros2 run topic_tools relay /rtabmap/map /map
+# $ ros2 launch rtabmap_demos turtlebot3_scan.launch.py
#
# Navigation (install nav2_bringup package):
-# $ ros2 launch nav2_bringup navigation_launch.py use_sim_time:=True
+# $ ros2 launch nav2_bringup navigation_launch.py
# $ ros2 launch nav2_bringup rviz_launch.py
#
# Teleop:
# $ ros2 run turtlebot3_teleop teleop_keyboard
from launch import LaunchDescription
-from launch.actions import DeclareLaunchArgument, SetEnvironmentVariable
+from launch.actions import DeclareLaunchArgument, OpaqueFunction
from launch.substitutions import LaunchConfiguration
from launch.conditions import IfCondition, UnlessCondition
from launch_ros.actions import Node
-def generate_launch_description():
-
+def launch_setup(context, *args, **kwargs):
use_sim_time = LaunchConfiguration('use_sim_time')
- qos = LaunchConfiguration('qos')
- localization = LaunchConfiguration('localization')
+ localization = LaunchConfiguration('localization').perform(context)
+ localization = localization == 'True' or localization == 'true'
+ icp_odometry = LaunchConfiguration('icp_odometry').perform(context)
+ icp_odometry = icp_odometry == 'True' or icp_odometry == 'true'
parameters={
'frame_id':'base_footprint',
@@ -40,18 +36,51 @@ def generate_launch_description():
'subscribe_scan':True,
'approx_sync':True,
'use_action_for_goal':True,
- 'qos_scan':qos,
- 'qos_imu':qos,
'Reg/Strategy':'1',
'Reg/Force3DoF':'true',
'RGBD/NeighborLinkRefining':'True',
'Grid/RangeMin':'0.2', # ignore laser scan points on the robot itself
'Optimizer/GravitySigma':'0' # Disable imu constraints (we are already in 2D)
}
-
+ arguments = []
+ if localization:
+ parameters['Mem/IncrementalMemory'] = 'False'
+ parameters['Mem/InitWMWithAllNodes'] = 'True'
+ else:
+ arguments.append('-d') # This will delete the previous database (~/.ros/rtabmap.db)
+
remappings=[
('scan', '/scan')]
+ if icp_odometry:
+ remappings.append(('odom', 'icp_odom'))
+
+ return [
+ # Nodes to launch
+
+ # ICP odometry (optional)
+ Node(
+ condition=IfCondition(LaunchConfiguration('icp_odometry')),
+ package='rtabmap_odom', executable='icp_odometry', output='screen',
+ parameters=[parameters,
+ {'odom_frame_id':'icp_odom',
+ 'guess_frame_id':'odom'}],
+ remappings=remappings),
+
+ # SLAM:
+ Node(
+ package='rtabmap_slam', executable='rtabmap', output='screen',
+ parameters=[parameters],
+ remappings=remappings,
+ arguments=arguments),
+ # Visualization
+ Node(
+ package='rtabmap_viz', executable='rtabmap_viz', output='screen',
+ parameters=[parameters],
+ remappings=remappings),
+ ]
+
+def generate_launch_description():
return LaunchDescription([
# Launch arguments
@@ -59,35 +88,13 @@ def generate_launch_description():
'use_sim_time', default_value='true',
description='Use simulation (Gazebo) clock if true'),
- DeclareLaunchArgument(
- 'qos', default_value='2',
- description='QoS used for input sensor topics'),
-
DeclareLaunchArgument(
'localization', default_value='false',
description='Launch in localization mode.'),
-
- # Nodes to launch
- # SLAM mode:
- Node(
- condition=UnlessCondition(localization),
- package='rtabmap_slam', executable='rtabmap', output='screen',
- parameters=[parameters],
- remappings=remappings,
- arguments=['-d']), # This will delete the previous database (~/.ros/rtabmap.db)
-
- # Localization mode:
- Node(
- condition=IfCondition(localization),
- package='rtabmap_slam', executable='rtabmap', output='screen',
- parameters=[parameters,
- {'Mem/IncrementalMemory':'False',
- 'Mem/InitWMWithAllNodes':'True'}],
- remappings=remappings),
+ DeclareLaunchArgument(
+ 'icp_odometry', default_value='false',
+ description='Launch ICP odometry on top of wheel odometry.'),
- Node(
- package='rtabmap_viz', executable='rtabmap_viz', output='screen',
- parameters=[parameters],
- remappings=remappings),
+ OpaqueFunction(function=launch_setup)
])
diff --git a/rtabmap_demos/launch/turtlebot3/turtlebot3_sim_rgbd_demo.launch.py b/rtabmap_demos/launch/turtlebot3/turtlebot3_sim_rgbd_demo.launch.py
new file mode 100644
index 00000000..ad14ca21
--- /dev/null
+++ b/rtabmap_demos/launch/turtlebot3/turtlebot3_sim_rgbd_demo.launch.py
@@ -0,0 +1,117 @@
+# Requirements:
+# Install Turtlebot3 packages
+# Modify turtlebot3_waffle SDF:
+# 1) Edit /opt/ros/$ROS_DISTRO/share/turtlebot3_gazebo/models/turtlebot3_waffle/model.sdf
+# 2) Add
+#
+# camera_rgb_frame
+# camera_rgb_optical_frame
+# 0 0 0 -1.57079632679 0 -1.57079632679
+#
+# 0 0 1
+#
+#
+# 3) Rename to
+# 4) Add
+# 5) Change to
+# 6) Change image width/height from 1920x1080 to 640x480
+# Example:
+# $ ros2 launch rtabmap_demos turtlebot3_sim_rgbd_demo.launch.py
+#
+# Teleop:
+# $ ros2 run turtlebot3_teleop teleop_keyboard
+
+from ament_index_python.packages import get_package_share_directory
+
+from launch import LaunchDescription
+from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription, OpaqueFunction
+from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
+from launch.launch_description_sources import PythonLaunchDescriptionSource
+from launch_ros.substitutions import FindPackageShare
+
+import os
+
+def launch_setup(context, *args, **kwargs):
+ if not 'TURTLEBOT3_MODEL' in os.environ:
+ os.environ['TURTLEBOT3_MODEL'] = 'waffle'
+
+ # Directories
+ pkg_turtlebot3_gazebo = get_package_share_directory(
+ 'turtlebot3_gazebo')
+ pkg_nav2_bringup = get_package_share_directory(
+ 'nav2_bringup')
+ pkg_rtabmap_demos = get_package_share_directory(
+ 'rtabmap_demos')
+
+ world = LaunchConfiguration('world').perform(context)
+
+ nav2_params_file = PathJoinSubstitution(
+ [FindPackageShare('rtabmap_demos'), 'params', 'turtlebot3_rgbd_nav2_params.yaml']
+ )
+
+ # Paths
+ gazebo_launch = PathJoinSubstitution(
+ [pkg_turtlebot3_gazebo, 'launch', f'turtlebot3_{world}.launch.py'])
+ nav2_launch = PathJoinSubstitution(
+ [pkg_nav2_bringup, 'launch', 'navigation_launch.py'])
+ rviz_launch = PathJoinSubstitution(
+ [pkg_nav2_bringup, 'launch', 'rviz_launch.py'])
+ rtabmap_launch = PathJoinSubstitution(
+ [pkg_rtabmap_demos, 'launch', 'turtlebot3', 'turtlebot3_rgbd.launch.py'])
+
+ # Includes
+ gazebo = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([gazebo_launch]),
+ launch_arguments=[
+ ('x_pose', LaunchConfiguration('x_pose')),
+ ('y_pose', LaunchConfiguration('y_pose'))
+ ]
+ )
+ nav2 = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([nav2_launch]),
+ launch_arguments=[
+ ('use_sim_time', 'true'),
+ ('params_file', nav2_params_file)
+ ]
+ )
+ rviz = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([rviz_launch])
+ )
+ rtabmap = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([rtabmap_launch]),
+ launch_arguments=[
+ ('localization', LaunchConfiguration('localization')),
+ ('use_sim_time', 'true')
+ ]
+ )
+ return [
+ # Nodes to launch
+ nav2,
+ rviz,
+ rtabmap,
+ gazebo
+ ]
+
+def generate_launch_description():
+ return LaunchDescription([
+
+ # Launch arguments
+ DeclareLaunchArgument(
+ 'localization', default_value='false',
+ description='Launch in localization mode.'),
+
+ DeclareLaunchArgument(
+ 'world', default_value='house',
+ choices=['world', 'house', 'dqn_stage1', 'dqn_stage2', 'dqn_stage3', 'dqn_stage4'],
+ description='Turtlebot3 gazebo world.'),
+
+ DeclareLaunchArgument(
+ 'x_pose', default_value='-2.0',
+ description='Initial position of the robot in the simulator.'),
+
+ DeclareLaunchArgument(
+ 'y_pose', default_value='0.5',
+ description='Initial position of the robot in the simulator.'),
+
+ OpaqueFunction(function=launch_setup)
+ ])
diff --git a/rtabmap_demos/launch/turtlebot3/turtlebot3_sim_rgbd_fake_scan_demo.launch.py b/rtabmap_demos/launch/turtlebot3/turtlebot3_sim_rgbd_fake_scan_demo.launch.py
new file mode 100644
index 00000000..314ad3eb
--- /dev/null
+++ b/rtabmap_demos/launch/turtlebot3/turtlebot3_sim_rgbd_fake_scan_demo.launch.py
@@ -0,0 +1,117 @@
+# Requirements:
+# Install Turtlebot3 packages
+# Modify turtlebot3_waffle SDF:
+# 1) Edit /opt/ros/$ROS_DISTRO/share/turtlebot3_gazebo/models/turtlebot3_waffle/model.sdf
+# 2) Add
+#
+# camera_rgb_frame
+# camera_rgb_optical_frame
+# 0 0 0 -1.57079632679 0 -1.57079632679
+#
+# 0 0 1
+#
+#
+# 3) Rename to
+# 4) Add
+# 5) Change to
+# 6) Change image width/height from 1920x1080 to 640x480
+# Example:
+# $ ros2 launch rtabmap_demos turtlebot3_sim_rgbd_fake_scan_demo.launch.py
+#
+# Teleop:
+# $ ros2 run turtlebot3_teleop teleop_keyboard
+
+from ament_index_python.packages import get_package_share_directory
+
+from launch import LaunchDescription
+from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription, OpaqueFunction
+from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
+from launch.launch_description_sources import PythonLaunchDescriptionSource
+from launch_ros.substitutions import FindPackageShare
+
+import os
+
+def launch_setup(context, *args, **kwargs):
+ if not 'TURTLEBOT3_MODEL' in os.environ:
+ os.environ['TURTLEBOT3_MODEL'] = 'waffle'
+
+ # Directories
+ pkg_turtlebot3_gazebo = get_package_share_directory(
+ 'turtlebot3_gazebo')
+ pkg_nav2_bringup = get_package_share_directory(
+ 'nav2_bringup')
+ pkg_rtabmap_demos = get_package_share_directory(
+ 'rtabmap_demos')
+
+ world = LaunchConfiguration('world').perform(context)
+
+ nav2_params_file = PathJoinSubstitution(
+ [FindPackageShare('rtabmap_demos'), 'params', 'turtlebot3_rgbd_nav2_params.yaml']
+ )
+
+ # Paths
+ gazebo_launch = PathJoinSubstitution(
+ [pkg_turtlebot3_gazebo, 'launch', f'turtlebot3_{world}.launch.py'])
+ nav2_launch = PathJoinSubstitution(
+ [pkg_nav2_bringup, 'launch', 'navigation_launch.py'])
+ rviz_launch = PathJoinSubstitution(
+ [pkg_nav2_bringup, 'launch', 'rviz_launch.py'])
+ rtabmap_launch = PathJoinSubstitution(
+ [pkg_rtabmap_demos, 'launch', 'turtlebot3', 'turtlebot3_rgbd_fake_scan.launch.py'])
+
+ # Includes
+ gazebo = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([gazebo_launch]),
+ launch_arguments=[
+ ('x_pose', LaunchConfiguration('x_pose')),
+ ('y_pose', LaunchConfiguration('y_pose'))
+ ]
+ )
+ nav2 = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([nav2_launch]),
+ launch_arguments=[
+ ('use_sim_time', 'true'),
+ ('params_file', nav2_params_file)
+ ]
+ )
+ rviz = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([rviz_launch])
+ )
+ rtabmap = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([rtabmap_launch]),
+ launch_arguments=[
+ ('localization', LaunchConfiguration('localization')),
+ ('use_sim_time', 'true')
+ ]
+ )
+ return [
+ # Nodes to launch
+ nav2,
+ rviz,
+ rtabmap,
+ gazebo
+ ]
+
+def generate_launch_description():
+ return LaunchDescription([
+
+ # Launch arguments
+ DeclareLaunchArgument(
+ 'localization', default_value='false',
+ description='Launch in localization mode.'),
+
+ DeclareLaunchArgument(
+ 'world', default_value='house',
+ choices=['world', 'house', 'dqn_stage1', 'dqn_stage2', 'dqn_stage3', 'dqn_stage4'],
+ description='Turtlebot3 gazebo world.'),
+
+ DeclareLaunchArgument(
+ 'x_pose', default_value='-2.0',
+ description='Initial position of the robot in the simulator.'),
+
+ DeclareLaunchArgument(
+ 'y_pose', default_value='0.5',
+ description='Initial position of the robot in the simulator.'),
+
+ OpaqueFunction(function=launch_setup)
+ ])
diff --git a/rtabmap_demos/launch/turtlebot3/turtlebot3_sim_rgbd_scan_demo.launch.py b/rtabmap_demos/launch/turtlebot3/turtlebot3_sim_rgbd_scan_demo.launch.py
new file mode 100644
index 00000000..5a826cb6
--- /dev/null
+++ b/rtabmap_demos/launch/turtlebot3/turtlebot3_sim_rgbd_scan_demo.launch.py
@@ -0,0 +1,119 @@
+# Requirements:
+# Install Turtlebot3 packages
+# Modify turtlebot3_waffle SDF:
+# 1) Edit /opt/ros/$ROS_DISTRO/share/turtlebot3_gazebo/models/turtlebot3_waffle/model.sdf
+# 2) Add
+#
+# camera_rgb_frame
+# camera_rgb_optical_frame
+# 0 0 0 -1.57079632679 0 -1.57079632679
+#
+# 0 0 1
+#
+#
+# 3) Rename to
+# 4) Add
+# 5) Change to
+# 6) Change image width/height from 1920x1080 to 640x480
+# 7) Note that we can increase min scan range from 0.12 to 0.2 to avoid having scans
+# hitting the robot itself
+# Example:
+# $ ros2 launch rtabmap_demos turtlebot3_sim_rgbd_scan_demo.launch.py
+#
+# Teleop:
+# $ ros2 run turtlebot3_teleop teleop_keyboard
+
+from ament_index_python.packages import get_package_share_directory
+
+from launch import LaunchDescription
+from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription, OpaqueFunction
+from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
+from launch.launch_description_sources import PythonLaunchDescriptionSource
+from launch_ros.substitutions import FindPackageShare
+
+import os
+
+def launch_setup(context, *args, **kwargs):
+ if not 'TURTLEBOT3_MODEL' in os.environ:
+ os.environ['TURTLEBOT3_MODEL'] = 'waffle'
+
+ # Directories
+ pkg_turtlebot3_gazebo = get_package_share_directory(
+ 'turtlebot3_gazebo')
+ pkg_nav2_bringup = get_package_share_directory(
+ 'nav2_bringup')
+ pkg_rtabmap_demos = get_package_share_directory(
+ 'rtabmap_demos')
+
+ world = LaunchConfiguration('world').perform(context)
+
+ nav2_params_file = PathJoinSubstitution(
+ [FindPackageShare('rtabmap_demos'), 'params', 'turtlebot3_rgbd_scan_nav2_params.yaml']
+ )
+
+ # Paths
+ gazebo_launch = PathJoinSubstitution(
+ [pkg_turtlebot3_gazebo, 'launch', f'turtlebot3_{world}.launch.py'])
+ nav2_launch = PathJoinSubstitution(
+ [pkg_nav2_bringup, 'launch', 'navigation_launch.py'])
+ rviz_launch = PathJoinSubstitution(
+ [pkg_nav2_bringup, 'launch', 'rviz_launch.py'])
+ rtabmap_launch = PathJoinSubstitution(
+ [pkg_rtabmap_demos, 'launch', 'turtlebot3', 'turtlebot3_rgbd_scan.launch.py'])
+
+ # Includes
+ gazebo = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([gazebo_launch]),
+ launch_arguments=[
+ ('x_pose', LaunchConfiguration('x_pose')),
+ ('y_pose', LaunchConfiguration('y_pose'))
+ ]
+ )
+ nav2 = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([nav2_launch]),
+ launch_arguments=[
+ ('use_sim_time', 'true'),
+ ('params_file', nav2_params_file)
+ ]
+ )
+ rviz = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([rviz_launch])
+ )
+ rtabmap = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([rtabmap_launch]),
+ launch_arguments=[
+ ('localization', LaunchConfiguration('localization')),
+ ('use_sim_time', 'true')
+ ]
+ )
+ return [
+ # Nodes to launch
+ nav2,
+ rviz,
+ rtabmap,
+ gazebo
+ ]
+
+def generate_launch_description():
+ return LaunchDescription([
+
+ # Launch arguments
+ DeclareLaunchArgument(
+ 'localization', default_value='false',
+ description='Launch in localization mode.'),
+
+ DeclareLaunchArgument(
+ 'world', default_value='house',
+ choices=['world', 'house', 'dqn_stage1', 'dqn_stage2', 'dqn_stage3', 'dqn_stage4'],
+ description='Turtlebot3 gazebo world.'),
+
+ DeclareLaunchArgument(
+ 'x_pose', default_value='-2.0',
+ description='Initial position of the robot in the simulator.'),
+
+ DeclareLaunchArgument(
+ 'y_pose', default_value='0.5',
+ description='Initial position of the robot in the simulator.'),
+
+ OpaqueFunction(function=launch_setup)
+ ])
diff --git a/rtabmap_demos/launch/turtlebot3/turtlebot3_sim_scan_demo.launch.py b/rtabmap_demos/launch/turtlebot3/turtlebot3_sim_scan_demo.launch.py
new file mode 100644
index 00000000..284b5b68
--- /dev/null
+++ b/rtabmap_demos/launch/turtlebot3/turtlebot3_sim_scan_demo.launch.py
@@ -0,0 +1,161 @@
+# Requirements:
+# Install Turtlebot3 packages
+# Modify turtlebot3_waffle SDF:
+# 1) Edit /opt/ros/$ROS_DISTRO/share/turtlebot3_gazebo/models/turtlebot3_waffle/model.sdf
+# 2) We can increase min scan range from 0.12 to 0.2 to avoid having scans
+# hitting the robot itself
+#
+# Example:
+# $ ros2 launch rtabmap_demos turtlebot3_sim_scan_demo.launch.py
+#
+# Teleop:
+# $ ros2 run turtlebot3_teleop teleop_keyboard
+
+from ament_index_python.packages import get_package_share_directory
+
+from launch import LaunchDescription
+from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription, OpaqueFunction
+from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
+from launch.launch_description_sources import PythonLaunchDescriptionSource
+from launch_ros.substitutions import FindPackageShare
+
+import os
+
+def launch_setup(context, *args, **kwargs):
+ if not 'TURTLEBOT3_MODEL' in os.environ:
+ os.environ['TURTLEBOT3_MODEL'] = 'waffle'
+
+ # Directories
+ pkg_nav2_bringup = get_package_share_directory(
+ 'nav2_bringup')
+ pkg_rtabmap_demos = get_package_share_directory(
+ 'rtabmap_demos')
+
+ world_name = LaunchConfiguration('world').perform(context)
+
+ icp_odometry = LaunchConfiguration('icp_odometry').perform(context)
+ icp_odometry = icp_odometry == 'True' or icp_odometry == 'true'
+ if icp_odometry:
+ # modified nav2 params to use icp_odom instead odom frame
+ nav2_params_file = PathJoinSubstitution(
+ [FindPackageShare('rtabmap_demos'), 'params', 'turtlebot3_scan_nav2_params.yaml']
+ )
+ else:
+ # original nav2 params
+ nav2_params_file = PathJoinSubstitution(
+ [FindPackageShare('nav2_bringup'), 'params', 'nav2_params.yaml']
+ )
+
+ # Paths
+ nav2_launch = PathJoinSubstitution(
+ [pkg_nav2_bringup, 'launch', 'navigation_launch.py'])
+ rviz_launch = PathJoinSubstitution(
+ [pkg_nav2_bringup, 'launch', 'rviz_launch.py'])
+ rtabmap_launch = PathJoinSubstitution(
+ [pkg_rtabmap_demos, 'launch', 'turtlebot3', 'turtlebot3_scan.launch.py'])
+
+ # To use ICP odometry, we should increase clock rate of gazebo, we copied content of
+ # turtlebot3_gazebo/launch/turtlebot3_world.launch here
+ launch_file_dir = os.path.join(get_package_share_directory('turtlebot3_gazebo'), 'launch')
+ pkg_gazebo_ros = get_package_share_directory('gazebo_ros')
+
+ world = os.path.join(
+ get_package_share_directory('turtlebot3_gazebo'),
+ 'worlds',
+ f'turtlebot3_{world_name}.world'
+ )
+
+ import tempfile
+ with tempfile.NamedTemporaryFile(mode='w+t', delete=False) as clock_override_file:
+ clock_override_file.write("---\n"+
+ "gazebo:\n"+
+ " ros__parameters:\n"+
+ " publish_rate: 100.0")
+
+ gzserver_cmd = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource(
+ os.path.join(pkg_gazebo_ros, 'launch', 'gzserver.launch.py')
+ ),
+ launch_arguments={
+ 'world': world,
+ 'params_file': clock_override_file.name}.items()
+ )
+
+ gzclient_cmd = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource(
+ os.path.join(pkg_gazebo_ros, 'launch', 'gzclient.launch.py')
+ )
+ )
+
+ robot_state_publisher_cmd = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource(
+ os.path.join(launch_file_dir, 'robot_state_publisher.launch.py')
+ ),
+ launch_arguments={'use_sim_time': 'true'}.items()
+ )
+
+ spawn_turtlebot_cmd = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource(
+ os.path.join(launch_file_dir, 'spawn_turtlebot3.launch.py')
+ ),
+ launch_arguments={
+ 'x_pose': LaunchConfiguration('x_pose'),
+ 'y_pose': LaunchConfiguration('y_pose')
+ }.items()
+ )
+
+ nav2 = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([nav2_launch]),
+ launch_arguments=[
+ ('use_sim_time', 'true'),
+ ('params_file', nav2_params_file)
+ ]
+ )
+ rviz = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([rviz_launch])
+ )
+ rtabmap = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([rtabmap_launch]),
+ launch_arguments=[
+ ('localization', LaunchConfiguration('localization')),
+ ('use_sim_time', 'true')
+ ]
+ )
+ return [
+ # Nodes to launch
+ nav2,
+ rviz,
+ rtabmap,
+ gzserver_cmd,
+ gzclient_cmd,
+ robot_state_publisher_cmd,
+ spawn_turtlebot_cmd
+ ]
+
+def generate_launch_description():
+ return LaunchDescription([
+
+ # Launch arguments
+ DeclareLaunchArgument(
+ 'localization', default_value='false',
+ description='Launch in localization mode.'),
+
+ DeclareLaunchArgument(
+ 'world', default_value='world',
+ choices=['world', 'house', 'dqn_stage1', 'dqn_stage2', 'dqn_stage3', 'dqn_stage4'],
+ description='Turtlebot3 gazebo world.'),
+
+ DeclareLaunchArgument(
+ 'icp_odometry', default_value='false',
+ description='Launch ICP odometry on top of wheel odometry.'),
+
+ DeclareLaunchArgument(
+ 'x_pose', default_value='-2.0',
+ description='Initial position of the robot in the simulator.'),
+
+ DeclareLaunchArgument(
+ 'y_pose', default_value='0.5',
+ description='Initial position of the robot in the simulator.'),
+
+ OpaqueFunction(function=launch_setup)
+ ])
diff --git a/rtabmap_demos/launch/turtlebot4/turtlebot4_sim_demo.launch.py b/rtabmap_demos/launch/turtlebot4/turtlebot4_sim_demo.launch.py
new file mode 100644
index 00000000..965919df
--- /dev/null
+++ b/rtabmap_demos/launch/turtlebot4/turtlebot4_sim_demo.launch.py
@@ -0,0 +1,79 @@
+#
+# Note: Make sure you have this fix for turtlebot4_description https://github.com/turtlebot/turtlebot4/pull/434,
+# otherwise, the lidar and camera point cloud won't be aligned correctly.
+#
+# Example:
+# 1) Launch simulator (turtlebot4, nav2 and rtabmap):
+# $ ros2 launch rtabmap_demos turtlebot4_sim_demo.launch.py
+#
+# 2) Click on "Play" button on bottom-left of gazebo.
+#
+# 3) Click on double points ".." button on top-right next to power button to undock.
+#
+# 4) Move the robot:
+# b) By sending goals with RVIZ's "Nav2 Goal" button in action bar.
+# a) By teleoperating:
+# $ ros2 run teleop_twist_keyboard teleop_twist_keyboard
+# c) By using autonomous exploration node (tested with https://github.com/robo-friends/m-explore-ros2):
+# $ ros2 launch explore_lite explore.launch.py
+#
+
+from ament_index_python.packages import get_package_share_directory
+
+from launch import LaunchDescription
+from launch.actions import DeclareLaunchArgument
+from launch.actions import IncludeLaunchDescription
+from launch.launch_description_sources import PythonLaunchDescriptionSource
+from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
+
+ARGUMENTS = [
+ DeclareLaunchArgument('rviz', default_value='true',
+ choices=['true', 'false'], description='Start rviz.'),
+ DeclareLaunchArgument('rtabmap_viz', default_value='true',
+ choices=['true', 'false'], description='Start rtabmap_viz.'),
+ DeclareLaunchArgument('localization', default_value='false',
+ choices=['true', 'false'], description='Start rtabmap in localization mode (a map should have been already created).'),
+ DeclareLaunchArgument('nav2', default_value='true',
+ choices=['true', 'false'], description='Start nav2.'),
+ DeclareLaunchArgument('world', default_value='warehouse',
+ description='Ignition World'),
+]
+
+def generate_launch_description():
+ # Directories
+ pkg_turtlebot4_ignition_bringup = get_package_share_directory(
+ 'turtlebot4_ignition_bringup')
+ pkg_rtabmap_demos = get_package_share_directory(
+ 'rtabmap_demos')
+
+ # Paths
+ ignition_launch = PathJoinSubstitution(
+ [pkg_turtlebot4_ignition_bringup, 'launch', 'turtlebot4_ignition.launch.py'])
+ rtabmap_launch = PathJoinSubstitution(
+ [pkg_rtabmap_demos, 'launch', 'turtlebot4', 'turtlebot4_slam.launch.py'])
+
+ ignition = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([ignition_launch]),
+ launch_arguments=[
+ ('world', LaunchConfiguration('world')),
+ ('slam', 'false'),
+ ('localization', 'false'),
+ ('nav2', LaunchConfiguration('nav2')),
+ ('rviz', LaunchConfiguration('rviz'))
+ ]
+ )
+
+ rtabmap = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([rtabmap_launch]),
+ launch_arguments=[
+ ('rtabmap_viz', LaunchConfiguration('rtabmap_viz')),
+ ('localization', LaunchConfiguration('localization')),
+ ('use_sim_time', 'true')
+ ]
+ )
+
+ # Create launch description and add actions
+ ld = LaunchDescription(ARGUMENTS)
+ ld.add_action(rtabmap) # put it first so that localization arg is not overwritten by the same used by ignition
+ ld.add_action(ignition)
+ return ld
diff --git a/rtabmap_demos/launch/turtlebot4/turtlebot4_slam.launch.py b/rtabmap_demos/launch/turtlebot4/turtlebot4_slam.launch.py
new file mode 100644
index 00000000..85558ca8
--- /dev/null
+++ b/rtabmap_demos/launch/turtlebot4/turtlebot4_slam.launch.py
@@ -0,0 +1,119 @@
+#
+# Note: Make sure you have this fix for turtlebot4_description https://github.com/turtlebot/turtlebot4/pull/434,
+# otherwise, the lidar and camera point cloud won't be aligned correctly.
+#
+# Example with gazebo:
+# 1) Launch simulator (turtlebot4 and nav2):
+# $ ros2 launch turtlebot4_ignition_bringup turtlebot4_ignition.launch.py slam:=false nav2:=true rviz:=true
+#
+# 2) Launch SLAM:
+# $ ros2 launch rtabmap_demos turtlebot4_slam.launch.py use_sim_time:=true
+# OR
+# $ ros2 launch rtabmap_launch rtabmap.launch.py rtabmap_viz:=true subscribe_scan:=true rgbd_sync:=true depth_topic:=/oakd/rgb/preview/depth odom_sensor_sync:=true camera_info_topic:=/oakd/rgb/preview/camera_info rgb_topic:=/oakd/rgb/preview/image_raw visual_odometry:=false approx_sync:=true approx_rgbd_sync:=false odom_guess_frame_id:=odom icp_odometry:=true odom_topic:="icp_odom" map_topic:="/map" use_sim_time:=true odom_log_level:=warn rtabmap_args:="--delete_db_on_start --Reg/Strategy 1 --Reg/Force3DoF true --Mem/NotLinkedNodesKept false" use_action_for_goal:=true
+#
+# 3) Click on "Play" button on bottom-left of gazebo.
+#
+# 4) Click on double points ".." button on top-right next to power button to undock.
+#
+# 5) Move the robot:
+# b) By sending goals with RVIZ's "Nav2 Goal" button in action bar.
+# a) By teleoperating:
+# $ ros2 run teleop_twist_keyboard teleop_twist_keyboard
+# c) By using autonomous exploration node (tested with https://github.com/robo-friends/m-explore-ros2):
+# $ ros2 launch explore_lite explore.launch.py
+#
+
+from launch import LaunchDescription
+from launch.actions import DeclareLaunchArgument, SetEnvironmentVariable
+from launch.substitutions import LaunchConfiguration
+from launch.conditions import IfCondition, UnlessCondition
+from launch_ros.actions import Node
+
+
+def generate_launch_description():
+
+ use_sim_time = LaunchConfiguration('use_sim_time')
+ localization = LaunchConfiguration('localization')
+ rtabmap_viz = LaunchConfiguration('rtabmap_viz')
+
+ icp_parameters={
+ 'odom_frame_id':'icp_odom',
+ 'guess_frame_id':'odom'
+ }
+
+ rtabmap_parameters={
+ 'subscribe_rgbd':True,
+ 'subscribe_scan':True,
+ 'use_action_for_goal':True,
+ 'odom_sensor_sync': True,
+ # RTAB-Map's parameters should be strings:
+ 'Mem/NotLinkedNodesKept':'false'
+ }
+
+ # Shared parameters between different nodes
+ shared_parameters={
+ 'frame_id':'base_link',
+ 'use_sim_time':use_sim_time,
+ # RTAB-Map's parameters should be strings:
+ 'Reg/Strategy':'1',
+ 'Reg/Force3DoF':'true',
+ 'Mem/NotLinkedNodesKept':'false',
+ 'Icp/PointToPlaneMinComplexity':'0.04' # to be more robust to long corridors with low geometry
+ }
+
+ remappings=[
+ ('odom', 'icp_odom'),
+ ('rgb/image', '/oakd/rgb/preview/image_raw'),
+ ('rgb/camera_info', '/oakd/rgb/preview/camera_info'),
+ ('depth/image', '/oakd/rgb/preview/depth')]
+
+ return LaunchDescription([
+
+ # Launch arguments
+ DeclareLaunchArgument(
+ 'use_sim_time', default_value='false', choices=['true', 'false'],
+ description='Use simulation (Gazebo) clock if true'),
+
+ DeclareLaunchArgument(
+ 'localization', default_value='false', choices=['true', 'false'],
+ description='Launch rtabmap in localization mode (a map should have been already created).'),
+
+ DeclareLaunchArgument(
+ 'rtabmap_viz', default_value='true', choices=['true', 'false'],
+ description='Launch rtabmap_viz for visualization.'),
+
+ # Nodes to launch
+ Node(
+ package='rtabmap_sync', executable='rgbd_sync', output='screen',
+ parameters=[{'approx_sync':False, 'use_sim_time':use_sim_time}],
+ remappings=remappings),
+
+ Node(
+ package='rtabmap_odom', executable='icp_odometry', output='screen',
+ parameters=[icp_parameters, shared_parameters],
+ remappings=remappings,
+ arguments=["--ros-args", "--log-level", 'icp_odometry:=warn']),
+
+ # SLAM Mode:
+ Node(
+ condition=UnlessCondition(localization),
+ package='rtabmap_slam', executable='rtabmap', output='screen',
+ parameters=[rtabmap_parameters, shared_parameters],
+ remappings=remappings,
+ arguments=['-d']),
+
+ # Localization mode:
+ Node(
+ condition=IfCondition(localization),
+ package='rtabmap_slam', executable='rtabmap', output='screen',
+ parameters=[rtabmap_parameters, shared_parameters,
+ {'Mem/IncrementalMemory':'False',
+ 'Mem/InitWMWithAllNodes':'True'}],
+ remappings=remappings),
+
+ Node(
+ condition=IfCondition(rtabmap_viz),
+ package='rtabmap_viz', executable='rtabmap_viz', output='screen',
+ parameters=[rtabmap_parameters, shared_parameters],
+ remappings=remappings),
+ ])
diff --git a/rtabmap_demos/package.xml b/rtabmap_demos/package.xml
index 18865042..cc7559f7 100644
--- a/rtabmap_demos/package.xml
+++ b/rtabmap_demos/package.xml
@@ -2,7 +2,7 @@
rtabmap_demos
- 0.21.5
+ 0.22.0
RTAB-Map's demo launch files.
Mathieu Labbe
Mathieu Labbe
diff --git a/rtabmap_demos/params/champ_nav2_params.yaml b/rtabmap_demos/params/champ_nav2_params.yaml
new file mode 100644
index 00000000..6006036d
--- /dev/null
+++ b/rtabmap_demos/params/champ_nav2_params.yaml
@@ -0,0 +1,288 @@
+# rtabmap_demos: We add segmented ground and obstacles to voxel_layer of the local costmap.
+bt_navigator:
+ ros__parameters:
+ use_sim_time: True
+ global_frame: map
+ robot_base_frame: base_link
+ odom_topic: /odom
+ bt_loop_duration: 10
+ default_server_timeout: 20
+ wait_for_service_timeout: 1000
+ # 'default_nav_through_poses_bt_xml' and 'default_nav_to_pose_bt_xml' are use defaults:
+ # nav2_bt_navigator/navigate_to_pose_w_replanning_and_recovery.xml
+ # nav2_bt_navigator/navigate_through_poses_w_replanning_and_recovery.xml
+ # They can be set here or via a RewrittenYaml remap from a parent launch file to Nav2.
+ plugin_lib_names:
+ - nav2_compute_path_to_pose_action_bt_node
+ - nav2_compute_path_through_poses_action_bt_node
+ - nav2_smooth_path_action_bt_node
+ - nav2_follow_path_action_bt_node
+ - nav2_spin_action_bt_node
+ - nav2_wait_action_bt_node
+ - nav2_assisted_teleop_action_bt_node
+ - nav2_back_up_action_bt_node
+ - nav2_drive_on_heading_bt_node
+ - nav2_clear_costmap_service_bt_node
+ - nav2_is_stuck_condition_bt_node
+ - nav2_goal_reached_condition_bt_node
+ - nav2_goal_updated_condition_bt_node
+ - nav2_globally_updated_goal_condition_bt_node
+ - nav2_is_path_valid_condition_bt_node
+ - nav2_initial_pose_received_condition_bt_node
+ - nav2_reinitialize_global_localization_service_bt_node
+ - nav2_rate_controller_bt_node
+ - nav2_distance_controller_bt_node
+ - nav2_speed_controller_bt_node
+ - nav2_truncate_path_action_bt_node
+ - nav2_truncate_path_local_action_bt_node
+ - nav2_goal_updater_node_bt_node
+ - nav2_recovery_node_bt_node
+ - nav2_pipeline_sequence_bt_node
+ - nav2_round_robin_node_bt_node
+ - nav2_transform_available_condition_bt_node
+ - nav2_time_expired_condition_bt_node
+ - nav2_path_expiring_timer_condition
+ - nav2_distance_traveled_condition_bt_node
+ - nav2_single_trigger_bt_node
+ - nav2_goal_updated_controller_bt_node
+ - nav2_is_battery_low_condition_bt_node
+ - nav2_navigate_through_poses_action_bt_node
+ - nav2_navigate_to_pose_action_bt_node
+ - nav2_remove_passed_goals_action_bt_node
+ - nav2_planner_selector_bt_node
+ - nav2_controller_selector_bt_node
+ - nav2_goal_checker_selector_bt_node
+ - nav2_controller_cancel_bt_node
+ - nav2_path_longer_on_approach_bt_node
+ - nav2_wait_cancel_bt_node
+ - nav2_spin_cancel_bt_node
+ - nav2_back_up_cancel_bt_node
+ - nav2_assisted_teleop_cancel_bt_node
+ - nav2_drive_on_heading_cancel_bt_node
+ - nav2_is_battery_charging_condition_bt_node
+
+bt_navigator_navigate_through_poses_rclcpp_node:
+ ros__parameters:
+ use_sim_time: True
+
+bt_navigator_navigate_to_pose_rclcpp_node:
+ ros__parameters:
+ use_sim_time: True
+
+controller_server:
+ ros__parameters:
+ use_sim_time: True
+ controller_frequency: 20.0
+ min_x_velocity_threshold: 0.001
+ min_y_velocity_threshold: 0.5
+ min_theta_velocity_threshold: 0.001
+ failure_tolerance: 0.3
+ progress_checker_plugin: "progress_checker"
+ goal_checker_plugins: ["general_goal_checker"] # "precise_goal_checker"
+ controller_plugins: ["FollowPath"]
+
+ # Progress checker parameters
+ progress_checker:
+ plugin: "nav2_controller::SimpleProgressChecker"
+ required_movement_radius: 0.5
+ movement_time_allowance: 10.0
+ # Goal checker parameters
+ #precise_goal_checker:
+ # plugin: "nav2_controller::SimpleGoalChecker"
+ # xy_goal_tolerance: 0.25
+ # yaw_goal_tolerance: 0.25
+ # stateful: True
+ general_goal_checker:
+ stateful: True
+ plugin: "nav2_controller::SimpleGoalChecker"
+ xy_goal_tolerance: 0.25
+ yaw_goal_tolerance: 0.25
+ # DWB parameters
+ FollowPath:
+ plugin: "dwb_core::DWBLocalPlanner"
+ debug_trajectory_details: True
+ min_vel_x: 0.0
+ min_vel_y: 0.0
+ max_vel_x: 0.26
+ max_vel_y: 0.0
+ max_vel_theta: 1.0
+ min_speed_xy: 0.0
+ max_speed_xy: 0.26
+ min_speed_theta: 0.0
+ # Add high threshold velocity for turtlebot 3 issue.
+ # https://github.com/ROBOTIS-GIT/turtlebot3_simulations/issues/75
+ acc_lim_x: 2.5
+ acc_lim_y: 0.0
+ acc_lim_theta: 3.2
+ decel_lim_x: -2.5
+ decel_lim_y: 0.0
+ decel_lim_theta: -3.2
+ vx_samples: 20
+ vy_samples: 5
+ vtheta_samples: 20
+ sim_time: 1.7
+ linear_granularity: 0.05
+ angular_granularity: 0.025
+ transform_tolerance: 0.2
+ xy_goal_tolerance: 0.25
+ trans_stopped_velocity: 0.25
+ short_circuit_trajectory_evaluation: True
+ stateful: True
+ critics: ["RotateToGoal", "Oscillation", "BaseObstacle", "GoalAlign", "PathAlign", "PathDist", "GoalDist"]
+ BaseObstacle.scale: 0.02
+ PathAlign.scale: 32.0
+ PathAlign.forward_point_distance: 0.1
+ GoalAlign.scale: 24.0
+ GoalAlign.forward_point_distance: 0.1
+ PathDist.scale: 32.0
+ GoalDist.scale: 24.0
+ RotateToGoal.scale: 32.0
+ RotateToGoal.slowing_factor: 5.0
+ RotateToGoal.lookahead_time: -1.0
+
+local_costmap:
+ local_costmap:
+ ros__parameters:
+ update_frequency: 5.0
+ publish_frequency: 5.0
+ global_frame: odom
+ robot_base_frame: base_link
+ use_sim_time: True
+ rolling_window: true
+ width: 3
+ height: 3
+ resolution: 0.05
+ robot_radius: 0.22
+ plugins: ["voxel_layer", "inflation_layer"]
+ inflation_layer:
+ plugin: "nav2_costmap_2d::InflationLayer"
+ cost_scaling_factor: 3.0
+ inflation_radius: 0.55
+ voxel_layer:
+ plugin: "nav2_costmap_2d::VoxelLayer"
+ enabled: True
+ publish_voxel_map: True
+ origin_z: 0.0
+ z_resolution: 0.05
+ z_voxels: 16
+ max_obstacle_height: 2.0
+ mark_threshold: 0
+ observation_sources: ground obstacles
+ ground:
+ topic: /camera/ground
+ max_obstacle_height: 2.0
+ clearing: True
+ marking: False
+ data_type: "PointCloud2"
+ raytrace_max_range: 3.0
+ raytrace_min_range: 0.0
+ obstacle_max_range: 2.5
+ obstacle_min_range: 0.0
+ obstacles:
+ topic: /camera/obstacles
+ max_obstacle_height: 2.0
+ clearing: True
+ marking: True
+ data_type: "PointCloud2"
+ raytrace_max_range: 3.0
+ raytrace_min_range: 0.0
+ obstacle_max_range: 2.5
+ obstacle_min_range: 0.0
+ always_send_full_costmap: True
+
+global_costmap:
+ global_costmap:
+ ros__parameters:
+ update_frequency: 1.0
+ publish_frequency: 1.0
+ global_frame: map
+ robot_base_frame: base_link
+ use_sim_time: True
+ robot_radius: 0.22
+ resolution: 0.05
+ track_unknown_space: true
+ plugins: ["static_layer", "inflation_layer"]
+ static_layer:
+ plugin: "nav2_costmap_2d::StaticLayer"
+ map_subscribe_transient_local: True
+ inflation_layer:
+ plugin: "nav2_costmap_2d::InflationLayer"
+ cost_scaling_factor: 3.0
+ inflation_radius: 0.55
+ always_send_full_costmap: True
+
+planner_server:
+ ros__parameters:
+ expected_planner_frequency: 20.0
+ use_sim_time: True
+ planner_plugins: ["GridBased"]
+ GridBased:
+ plugin: "nav2_navfn_planner/NavfnPlanner"
+ tolerance: 0.5
+ use_astar: false
+ allow_unknown: true
+
+smoother_server:
+ ros__parameters:
+ use_sim_time: True
+ smoother_plugins: ["simple_smoother"]
+ simple_smoother:
+ plugin: "nav2_smoother::SimpleSmoother"
+ tolerance: 1.0e-10
+ max_its: 1000
+ do_refinement: True
+
+behavior_server:
+ ros__parameters:
+ costmap_topic: local_costmap/costmap_raw
+ footprint_topic: local_costmap/published_footprint
+ cycle_frequency: 10.0
+ behavior_plugins: ["spin", "backup", "drive_on_heading", "assisted_teleop", "wait"]
+ spin:
+ plugin: "nav2_behaviors/Spin"
+ backup:
+ plugin: "nav2_behaviors/BackUp"
+ drive_on_heading:
+ plugin: "nav2_behaviors/DriveOnHeading"
+ wait:
+ plugin: "nav2_behaviors/Wait"
+ assisted_teleop:
+ plugin: "nav2_behaviors/AssistedTeleop"
+ global_frame: odom
+ robot_base_frame: base_link
+ transform_tolerance: 0.1
+ use_sim_time: true
+ simulate_ahead_time: 2.0
+ max_rotational_vel: 1.0
+ min_rotational_vel: 0.4
+ rotational_acc_lim: 3.2
+
+robot_state_publisher:
+ ros__parameters:
+ use_sim_time: True
+
+waypoint_follower:
+ ros__parameters:
+ use_sim_time: True
+ loop_rate: 20
+ stop_on_failure: false
+ waypoint_task_executor_plugin: "wait_at_waypoint"
+ wait_at_waypoint:
+ plugin: "nav2_waypoint_follower::WaitAtWaypoint"
+ enabled: True
+ waypoint_pause_duration: 200
+
+velocity_smoother:
+ ros__parameters:
+ use_sim_time: True
+ smoothing_frequency: 20.0
+ scale_velocities: False
+ feedback: "OPEN_LOOP"
+ max_velocity: [0.26, 0.0, 1.0]
+ min_velocity: [-0.26, 0.0, -1.0]
+ max_accel: [2.5, 0.0, 3.2]
+ max_decel: [-2.5, 0.0, -3.2]
+ odom_topic: "odom"
+ odom_duration: 0.1
+ deadband_velocity: [0.0, 0.0, 0.0]
+ velocity_timeout: 1.0
diff --git a/rtabmap_demos/params/isaac_nav2_params.yaml b/rtabmap_demos/params/isaac_nav2_params.yaml
new file mode 100644
index 00000000..1376952e
--- /dev/null
+++ b/rtabmap_demos/params/isaac_nav2_params.yaml
@@ -0,0 +1,295 @@
+# Isaac example: We increased max velocities.
+bt_navigator:
+ ros__parameters:
+ use_sim_time: True
+ global_frame: map
+ robot_base_frame: base_link
+ odom_topic: /chassis/odom
+ bt_loop_duration: 10
+ default_server_timeout: 20
+ wait_for_service_timeout: 1000
+ # 'default_nav_through_poses_bt_xml' and 'default_nav_to_pose_bt_xml' are use defaults:
+ # nav2_bt_navigator/navigate_to_pose_w_replanning_and_recovery.xml
+ # nav2_bt_navigator/navigate_through_poses_w_replanning_and_recovery.xml
+ # They can be set here or via a RewrittenYaml remap from a parent launch file to Nav2.
+ plugin_lib_names:
+ - nav2_compute_path_to_pose_action_bt_node
+ - nav2_compute_path_through_poses_action_bt_node
+ - nav2_smooth_path_action_bt_node
+ - nav2_follow_path_action_bt_node
+ - nav2_spin_action_bt_node
+ - nav2_wait_action_bt_node
+ - nav2_assisted_teleop_action_bt_node
+ - nav2_back_up_action_bt_node
+ - nav2_drive_on_heading_bt_node
+ - nav2_clear_costmap_service_bt_node
+ - nav2_is_stuck_condition_bt_node
+ - nav2_goal_reached_condition_bt_node
+ - nav2_goal_updated_condition_bt_node
+ - nav2_globally_updated_goal_condition_bt_node
+ - nav2_is_path_valid_condition_bt_node
+ - nav2_initial_pose_received_condition_bt_node
+ - nav2_reinitialize_global_localization_service_bt_node
+ - nav2_rate_controller_bt_node
+ - nav2_distance_controller_bt_node
+ - nav2_speed_controller_bt_node
+ - nav2_truncate_path_action_bt_node
+ - nav2_truncate_path_local_action_bt_node
+ - nav2_goal_updater_node_bt_node
+ - nav2_recovery_node_bt_node
+ - nav2_pipeline_sequence_bt_node
+ - nav2_round_robin_node_bt_node
+ - nav2_transform_available_condition_bt_node
+ - nav2_time_expired_condition_bt_node
+ - nav2_path_expiring_timer_condition
+ - nav2_distance_traveled_condition_bt_node
+ - nav2_single_trigger_bt_node
+ - nav2_goal_updated_controller_bt_node
+ - nav2_is_battery_low_condition_bt_node
+ - nav2_navigate_through_poses_action_bt_node
+ - nav2_navigate_to_pose_action_bt_node
+ - nav2_remove_passed_goals_action_bt_node
+ - nav2_planner_selector_bt_node
+ - nav2_controller_selector_bt_node
+ - nav2_goal_checker_selector_bt_node
+ - nav2_controller_cancel_bt_node
+ - nav2_path_longer_on_approach_bt_node
+ - nav2_wait_cancel_bt_node
+ - nav2_spin_cancel_bt_node
+ - nav2_back_up_cancel_bt_node
+ - nav2_assisted_teleop_cancel_bt_node
+ - nav2_drive_on_heading_cancel_bt_node
+ - nav2_is_battery_charging_condition_bt_node
+
+bt_navigator_navigate_through_poses_rclcpp_node:
+ ros__parameters:
+ use_sim_time: True
+
+bt_navigator_navigate_to_pose_rclcpp_node:
+ ros__parameters:
+ use_sim_time: True
+
+controller_server:
+ ros__parameters:
+ use_sim_time: True
+ controller_frequency: 20.0
+ min_x_velocity_threshold: 0.001
+ min_y_velocity_threshold: 0.5
+ min_theta_velocity_threshold: 0.001
+ failure_tolerance: 0.3
+ progress_checker_plugin: "progress_checker"
+ goal_checker_plugins: ["general_goal_checker"] # "precise_goal_checker"
+ controller_plugins: ["FollowPath"]
+
+ # Progress checker parameters
+ progress_checker:
+ plugin: "nav2_controller::SimpleProgressChecker"
+ required_movement_radius: 0.5
+ movement_time_allowance: 10.0
+ # Goal checker parameters
+ #precise_goal_checker:
+ # plugin: "nav2_controller::SimpleGoalChecker"
+ # xy_goal_tolerance: 0.25
+ # yaw_goal_tolerance: 0.25
+ # stateful: True
+ general_goal_checker:
+ stateful: True
+ plugin: "nav2_controller::SimpleGoalChecker"
+ xy_goal_tolerance: 0.25
+ yaw_goal_tolerance: 0.25
+ # DWB parameters
+ FollowPath:
+ plugin: "dwb_core::DWBLocalPlanner"
+ debug_trajectory_details: True
+ min_vel_x: 0.0
+ min_vel_y: 0.0
+ max_vel_x: 2.0
+ max_vel_y: 0.0
+ max_vel_theta: 1.0
+ min_speed_xy: 0.0
+ max_speed_xy: 2.0
+ min_speed_theta: 0.0
+ # Add high threshold velocity for turtlebot 3 issue.
+ # https://github.com/ROBOTIS-GIT/turtlebot3_simulations/issues/75
+ acc_lim_x: 2.5
+ acc_lim_y: 0.0
+ acc_lim_theta: 3.2
+ decel_lim_x: -2.5
+ decel_lim_y: 0.0
+ decel_lim_theta: -3.2
+ vx_samples: 20
+ vy_samples: 5
+ vtheta_samples: 20
+ sim_time: 1.7
+ linear_granularity: 0.05
+ angular_granularity: 0.025
+ transform_tolerance: 0.2
+ xy_goal_tolerance: 0.25
+ trans_stopped_velocity: 0.25
+ short_circuit_trajectory_evaluation: True
+ stateful: True
+ critics: ["RotateToGoal", "Oscillation", "BaseObstacle", "GoalAlign", "PathAlign", "PathDist", "GoalDist"]
+ BaseObstacle.scale: 0.02
+ PathAlign.scale: 32.0
+ PathAlign.forward_point_distance: 0.1
+ GoalAlign.scale: 24.0
+ GoalAlign.forward_point_distance: 0.1
+ PathDist.scale: 32.0
+ GoalDist.scale: 24.0
+ RotateToGoal.scale: 32.0
+ RotateToGoal.slowing_factor: 5.0
+ RotateToGoal.lookahead_time: -1.0
+
+local_costmap:
+ local_costmap:
+ ros__parameters:
+ update_frequency: 5.0
+ publish_frequency: 2.0
+ global_frame: odom
+ robot_base_frame: base_link
+ use_sim_time: True
+ rolling_window: true
+ width: 3
+ height: 3
+ resolution: 0.05
+ robot_radius: 0.22
+ plugins: ["voxel_layer", "inflation_layer"]
+ inflation_layer:
+ plugin: "nav2_costmap_2d::InflationLayer"
+ cost_scaling_factor: 3.0
+ inflation_radius: 0.55
+ voxel_layer:
+ plugin: "nav2_costmap_2d::VoxelLayer"
+ enabled: True
+ publish_voxel_map: True
+ origin_z: 0.0
+ z_resolution: 0.05
+ z_voxels: 16
+ max_obstacle_height: 2.0
+ mark_threshold: 0
+ observation_sources: scan
+ scan:
+ topic: /scan
+ max_obstacle_height: 2.0
+ clearing: True
+ marking: True
+ data_type: "LaserScan"
+ raytrace_max_range: 3.0
+ raytrace_min_range: 0.0
+ obstacle_max_range: 2.5
+ obstacle_min_range: 0.0
+ static_layer:
+ plugin: "nav2_costmap_2d::StaticLayer"
+ map_subscribe_transient_local: True
+ always_send_full_costmap: True
+
+global_costmap:
+ global_costmap:
+ ros__parameters:
+ update_frequency: 1.0
+ publish_frequency: 1.0
+ global_frame: map
+ robot_base_frame: base_link
+ use_sim_time: True
+ robot_radius: 0.22
+ resolution: 0.05
+ track_unknown_space: true
+ plugins: ["static_layer", "obstacle_layer", "inflation_layer"]
+ obstacle_layer:
+ plugin: "nav2_costmap_2d::ObstacleLayer"
+ enabled: True
+ observation_sources: scan
+ scan:
+ topic: /scan
+ max_obstacle_height: 2.0
+ clearing: True
+ marking: True
+ data_type: "LaserScan"
+ raytrace_max_range: 3.0
+ raytrace_min_range: 0.0
+ obstacle_max_range: 2.5
+ obstacle_min_range: 0.0
+ static_layer:
+ plugin: "nav2_costmap_2d::StaticLayer"
+ map_subscribe_transient_local: True
+ inflation_layer:
+ plugin: "nav2_costmap_2d::InflationLayer"
+ cost_scaling_factor: 3.0
+ inflation_radius: 0.55
+ always_send_full_costmap: True
+
+planner_server:
+ ros__parameters:
+ expected_planner_frequency: 20.0
+ use_sim_time: True
+ planner_plugins: ["GridBased"]
+ GridBased:
+ plugin: "nav2_navfn_planner/NavfnPlanner"
+ tolerance: 0.5
+ use_astar: false
+ allow_unknown: true
+
+smoother_server:
+ ros__parameters:
+ use_sim_time: True
+ smoother_plugins: ["simple_smoother"]
+ simple_smoother:
+ plugin: "nav2_smoother::SimpleSmoother"
+ tolerance: 1.0e-10
+ max_its: 1000
+ do_refinement: True
+
+behavior_server:
+ ros__parameters:
+ costmap_topic: local_costmap/costmap_raw
+ footprint_topic: local_costmap/published_footprint
+ cycle_frequency: 10.0
+ behavior_plugins: ["spin", "backup", "drive_on_heading", "assisted_teleop", "wait"]
+ spin:
+ plugin: "nav2_behaviors/Spin"
+ backup:
+ plugin: "nav2_behaviors/BackUp"
+ drive_on_heading:
+ plugin: "nav2_behaviors/DriveOnHeading"
+ wait:
+ plugin: "nav2_behaviors/Wait"
+ assisted_teleop:
+ plugin: "nav2_behaviors/AssistedTeleop"
+ global_frame: odom
+ robot_base_frame: base_link
+ transform_tolerance: 0.1
+ use_sim_time: true
+ simulate_ahead_time: 2.0
+ max_rotational_vel: 1.0
+ min_rotational_vel: 0.4
+ rotational_acc_lim: 3.2
+
+robot_state_publisher:
+ ros__parameters:
+ use_sim_time: True
+
+waypoint_follower:
+ ros__parameters:
+ use_sim_time: True
+ loop_rate: 20
+ stop_on_failure: false
+ waypoint_task_executor_plugin: "wait_at_waypoint"
+ wait_at_waypoint:
+ plugin: "nav2_waypoint_follower::WaitAtWaypoint"
+ enabled: True
+ waypoint_pause_duration: 200
+
+velocity_smoother:
+ ros__parameters:
+ use_sim_time: True
+ smoothing_frequency: 20.0
+ scale_velocities: False
+ feedback: "OPEN_LOOP"
+ max_velocity: [2.0, 0.0, 2.0]
+ min_velocity: [-2.0, 0.0, -2.0]
+ max_accel: [2.5, 0.0, 3.2]
+ max_decel: [-2.5, 0.0, -3.2]
+ odom_topic: "/chassis/odom"
+ odom_duration: 0.1
+ deadband_velocity: [0.0, 0.0, 0.0]
+ velocity_timeout: 1.0
\ No newline at end of file
diff --git a/rtabmap_demos/params/isaac_vslam_nav2_params.yaml b/rtabmap_demos/params/isaac_vslam_nav2_params.yaml
new file mode 100644
index 00000000..0210b63e
--- /dev/null
+++ b/rtabmap_demos/params/isaac_vslam_nav2_params.yaml
@@ -0,0 +1,295 @@
+# Isaac example: we changed the main odom_frame_id from "odom" to "vo" frame. We increased max velocities.
+bt_navigator:
+ ros__parameters:
+ use_sim_time: True
+ global_frame: map
+ robot_base_frame: base_link
+ odom_topic: /chassis/odom
+ bt_loop_duration: 10
+ default_server_timeout: 20
+ wait_for_service_timeout: 1000
+ # 'default_nav_through_poses_bt_xml' and 'default_nav_to_pose_bt_xml' are use defaults:
+ # nav2_bt_navigator/navigate_to_pose_w_replanning_and_recovery.xml
+ # nav2_bt_navigator/navigate_through_poses_w_replanning_and_recovery.xml
+ # They can be set here or via a RewrittenYaml remap from a parent launch file to Nav2.
+ plugin_lib_names:
+ - nav2_compute_path_to_pose_action_bt_node
+ - nav2_compute_path_through_poses_action_bt_node
+ - nav2_smooth_path_action_bt_node
+ - nav2_follow_path_action_bt_node
+ - nav2_spin_action_bt_node
+ - nav2_wait_action_bt_node
+ - nav2_assisted_teleop_action_bt_node
+ - nav2_back_up_action_bt_node
+ - nav2_drive_on_heading_bt_node
+ - nav2_clear_costmap_service_bt_node
+ - nav2_is_stuck_condition_bt_node
+ - nav2_goal_reached_condition_bt_node
+ - nav2_goal_updated_condition_bt_node
+ - nav2_globally_updated_goal_condition_bt_node
+ - nav2_is_path_valid_condition_bt_node
+ - nav2_initial_pose_received_condition_bt_node
+ - nav2_reinitialize_global_localization_service_bt_node
+ - nav2_rate_controller_bt_node
+ - nav2_distance_controller_bt_node
+ - nav2_speed_controller_bt_node
+ - nav2_truncate_path_action_bt_node
+ - nav2_truncate_path_local_action_bt_node
+ - nav2_goal_updater_node_bt_node
+ - nav2_recovery_node_bt_node
+ - nav2_pipeline_sequence_bt_node
+ - nav2_round_robin_node_bt_node
+ - nav2_transform_available_condition_bt_node
+ - nav2_time_expired_condition_bt_node
+ - nav2_path_expiring_timer_condition
+ - nav2_distance_traveled_condition_bt_node
+ - nav2_single_trigger_bt_node
+ - nav2_goal_updated_controller_bt_node
+ - nav2_is_battery_low_condition_bt_node
+ - nav2_navigate_through_poses_action_bt_node
+ - nav2_navigate_to_pose_action_bt_node
+ - nav2_remove_passed_goals_action_bt_node
+ - nav2_planner_selector_bt_node
+ - nav2_controller_selector_bt_node
+ - nav2_goal_checker_selector_bt_node
+ - nav2_controller_cancel_bt_node
+ - nav2_path_longer_on_approach_bt_node
+ - nav2_wait_cancel_bt_node
+ - nav2_spin_cancel_bt_node
+ - nav2_back_up_cancel_bt_node
+ - nav2_assisted_teleop_cancel_bt_node
+ - nav2_drive_on_heading_cancel_bt_node
+ - nav2_is_battery_charging_condition_bt_node
+
+bt_navigator_navigate_through_poses_rclcpp_node:
+ ros__parameters:
+ use_sim_time: True
+
+bt_navigator_navigate_to_pose_rclcpp_node:
+ ros__parameters:
+ use_sim_time: True
+
+controller_server:
+ ros__parameters:
+ use_sim_time: True
+ controller_frequency: 20.0
+ min_x_velocity_threshold: 0.001
+ min_y_velocity_threshold: 0.5
+ min_theta_velocity_threshold: 0.001
+ failure_tolerance: 0.3
+ progress_checker_plugin: "progress_checker"
+ goal_checker_plugins: ["general_goal_checker"] # "precise_goal_checker"
+ controller_plugins: ["FollowPath"]
+
+ # Progress checker parameters
+ progress_checker:
+ plugin: "nav2_controller::SimpleProgressChecker"
+ required_movement_radius: 0.5
+ movement_time_allowance: 10.0
+ # Goal checker parameters
+ #precise_goal_checker:
+ # plugin: "nav2_controller::SimpleGoalChecker"
+ # xy_goal_tolerance: 0.25
+ # yaw_goal_tolerance: 0.25
+ # stateful: True
+ general_goal_checker:
+ stateful: True
+ plugin: "nav2_controller::SimpleGoalChecker"
+ xy_goal_tolerance: 0.25
+ yaw_goal_tolerance: 0.25
+ # DWB parameters
+ FollowPath:
+ plugin: "dwb_core::DWBLocalPlanner"
+ debug_trajectory_details: True
+ min_vel_x: 0.0
+ min_vel_y: 0.0
+ max_vel_x: 2.0
+ max_vel_y: 0.0
+ max_vel_theta: 1.0
+ min_speed_xy: 0.0
+ max_speed_xy: 2.0
+ min_speed_theta: 0.0
+ # Add high threshold velocity for turtlebot 3 issue.
+ # https://github.com/ROBOTIS-GIT/turtlebot3_simulations/issues/75
+ acc_lim_x: 2.5
+ acc_lim_y: 0.0
+ acc_lim_theta: 3.2
+ decel_lim_x: -2.5
+ decel_lim_y: 0.0
+ decel_lim_theta: -3.2
+ vx_samples: 20
+ vy_samples: 5
+ vtheta_samples: 20
+ sim_time: 1.7
+ linear_granularity: 0.05
+ angular_granularity: 0.025
+ transform_tolerance: 0.2
+ xy_goal_tolerance: 0.25
+ trans_stopped_velocity: 0.25
+ short_circuit_trajectory_evaluation: True
+ stateful: True
+ critics: ["RotateToGoal", "Oscillation", "BaseObstacle", "GoalAlign", "PathAlign", "PathDist", "GoalDist"]
+ BaseObstacle.scale: 0.02
+ PathAlign.scale: 32.0
+ PathAlign.forward_point_distance: 0.1
+ GoalAlign.scale: 24.0
+ GoalAlign.forward_point_distance: 0.1
+ PathDist.scale: 32.0
+ GoalDist.scale: 24.0
+ RotateToGoal.scale: 32.0
+ RotateToGoal.slowing_factor: 5.0
+ RotateToGoal.lookahead_time: -1.0
+
+local_costmap:
+ local_costmap:
+ ros__parameters:
+ update_frequency: 5.0
+ publish_frequency: 2.0
+ global_frame: vo
+ robot_base_frame: base_link
+ use_sim_time: True
+ rolling_window: true
+ width: 3
+ height: 3
+ resolution: 0.05
+ robot_radius: 0.22
+ plugins: ["voxel_layer", "inflation_layer"]
+ inflation_layer:
+ plugin: "nav2_costmap_2d::InflationLayer"
+ cost_scaling_factor: 3.0
+ inflation_radius: 0.55
+ voxel_layer:
+ plugin: "nav2_costmap_2d::VoxelLayer"
+ enabled: True
+ publish_voxel_map: True
+ origin_z: 0.0
+ z_resolution: 0.05
+ z_voxels: 16
+ max_obstacle_height: 2.0
+ mark_threshold: 0
+ observation_sources: scan
+ scan:
+ topic: /scan
+ max_obstacle_height: 2.0
+ clearing: True
+ marking: True
+ data_type: "LaserScan"
+ raytrace_max_range: 3.0
+ raytrace_min_range: 0.0
+ obstacle_max_range: 2.5
+ obstacle_min_range: 0.0
+ static_layer:
+ plugin: "nav2_costmap_2d::StaticLayer"
+ map_subscribe_transient_local: True
+ always_send_full_costmap: True
+
+global_costmap:
+ global_costmap:
+ ros__parameters:
+ update_frequency: 1.0
+ publish_frequency: 1.0
+ global_frame: map
+ robot_base_frame: base_link
+ use_sim_time: True
+ robot_radius: 0.22
+ resolution: 0.05
+ track_unknown_space: true
+ plugins: ["static_layer", "obstacle_layer", "inflation_layer"]
+ obstacle_layer:
+ plugin: "nav2_costmap_2d::ObstacleLayer"
+ enabled: True
+ observation_sources: scan
+ scan:
+ topic: /scan
+ max_obstacle_height: 2.0
+ clearing: True
+ marking: True
+ data_type: "LaserScan"
+ raytrace_max_range: 3.0
+ raytrace_min_range: 0.0
+ obstacle_max_range: 2.5
+ obstacle_min_range: 0.0
+ static_layer:
+ plugin: "nav2_costmap_2d::StaticLayer"
+ map_subscribe_transient_local: True
+ inflation_layer:
+ plugin: "nav2_costmap_2d::InflationLayer"
+ cost_scaling_factor: 3.0
+ inflation_radius: 0.55
+ always_send_full_costmap: True
+
+planner_server:
+ ros__parameters:
+ expected_planner_frequency: 20.0
+ use_sim_time: True
+ planner_plugins: ["GridBased"]
+ GridBased:
+ plugin: "nav2_navfn_planner/NavfnPlanner"
+ tolerance: 0.5
+ use_astar: false
+ allow_unknown: true
+
+smoother_server:
+ ros__parameters:
+ use_sim_time: True
+ smoother_plugins: ["simple_smoother"]
+ simple_smoother:
+ plugin: "nav2_smoother::SimpleSmoother"
+ tolerance: 1.0e-10
+ max_its: 1000
+ do_refinement: True
+
+behavior_server:
+ ros__parameters:
+ costmap_topic: local_costmap/costmap_raw
+ footprint_topic: local_costmap/published_footprint
+ cycle_frequency: 10.0
+ behavior_plugins: ["spin", "backup", "drive_on_heading", "assisted_teleop", "wait"]
+ spin:
+ plugin: "nav2_behaviors/Spin"
+ backup:
+ plugin: "nav2_behaviors/BackUp"
+ drive_on_heading:
+ plugin: "nav2_behaviors/DriveOnHeading"
+ wait:
+ plugin: "nav2_behaviors/Wait"
+ assisted_teleop:
+ plugin: "nav2_behaviors/AssistedTeleop"
+ global_frame: vo
+ robot_base_frame: base_link
+ transform_tolerance: 0.1
+ use_sim_time: true
+ simulate_ahead_time: 2.0
+ max_rotational_vel: 1.0
+ min_rotational_vel: 0.4
+ rotational_acc_lim: 3.2
+
+robot_state_publisher:
+ ros__parameters:
+ use_sim_time: True
+
+waypoint_follower:
+ ros__parameters:
+ use_sim_time: True
+ loop_rate: 20
+ stop_on_failure: false
+ waypoint_task_executor_plugin: "wait_at_waypoint"
+ wait_at_waypoint:
+ plugin: "nav2_waypoint_follower::WaitAtWaypoint"
+ enabled: True
+ waypoint_pause_duration: 200
+
+velocity_smoother:
+ ros__parameters:
+ use_sim_time: True
+ smoothing_frequency: 20.0
+ scale_velocities: False
+ feedback: "OPEN_LOOP"
+ max_velocity: [2.0, 0.0, 2.0]
+ min_velocity: [-2.0, 0.0, -2.0]
+ max_accel: [2.5, 0.0, 3.2]
+ max_decel: [-2.5, 0.0, -3.2]
+ odom_topic: "/chassis/odom"
+ odom_duration: 0.1
+ deadband_velocity: [0.0, 0.0, 0.0]
+ velocity_timeout: 1.0
\ No newline at end of file
diff --git a/rtabmap_demos/params/turtlebot3_rgbd_nav2_params.yaml b/rtabmap_demos/params/turtlebot3_rgbd_nav2_params.yaml
new file mode 100644
index 00000000..838c80b3
--- /dev/null
+++ b/rtabmap_demos/params/turtlebot3_rgbd_nav2_params.yaml
@@ -0,0 +1,287 @@
+# rtabmap_demos: We add segmented ground and obstacles to voxel_layer of the local costmap and removed scan source.
+bt_navigator:
+ ros__parameters:
+ use_sim_time: True
+ global_frame: map
+ robot_base_frame: base_link
+ odom_topic: /odom
+ bt_loop_duration: 10
+ default_server_timeout: 20
+ wait_for_service_timeout: 1000
+ # 'default_nav_through_poses_bt_xml' and 'default_nav_to_pose_bt_xml' are use defaults:
+ # nav2_bt_navigator/navigate_to_pose_w_replanning_and_recovery.xml
+ # nav2_bt_navigator/navigate_through_poses_w_replanning_and_recovery.xml
+ # They can be set here or via a RewrittenYaml remap from a parent launch file to Nav2.
+ plugin_lib_names:
+ - nav2_compute_path_to_pose_action_bt_node
+ - nav2_compute_path_through_poses_action_bt_node
+ - nav2_smooth_path_action_bt_node
+ - nav2_follow_path_action_bt_node
+ - nav2_spin_action_bt_node
+ - nav2_wait_action_bt_node
+ - nav2_assisted_teleop_action_bt_node
+ - nav2_back_up_action_bt_node
+ - nav2_drive_on_heading_bt_node
+ - nav2_clear_costmap_service_bt_node
+ - nav2_is_stuck_condition_bt_node
+ - nav2_goal_reached_condition_bt_node
+ - nav2_goal_updated_condition_bt_node
+ - nav2_globally_updated_goal_condition_bt_node
+ - nav2_is_path_valid_condition_bt_node
+ - nav2_initial_pose_received_condition_bt_node
+ - nav2_reinitialize_global_localization_service_bt_node
+ - nav2_rate_controller_bt_node
+ - nav2_distance_controller_bt_node
+ - nav2_speed_controller_bt_node
+ - nav2_truncate_path_action_bt_node
+ - nav2_truncate_path_local_action_bt_node
+ - nav2_goal_updater_node_bt_node
+ - nav2_recovery_node_bt_node
+ - nav2_pipeline_sequence_bt_node
+ - nav2_round_robin_node_bt_node
+ - nav2_transform_available_condition_bt_node
+ - nav2_time_expired_condition_bt_node
+ - nav2_path_expiring_timer_condition
+ - nav2_distance_traveled_condition_bt_node
+ - nav2_single_trigger_bt_node
+ - nav2_goal_updated_controller_bt_node
+ - nav2_is_battery_low_condition_bt_node
+ - nav2_navigate_through_poses_action_bt_node
+ - nav2_navigate_to_pose_action_bt_node
+ - nav2_remove_passed_goals_action_bt_node
+ - nav2_planner_selector_bt_node
+ - nav2_controller_selector_bt_node
+ - nav2_goal_checker_selector_bt_node
+ - nav2_controller_cancel_bt_node
+ - nav2_path_longer_on_approach_bt_node
+ - nav2_wait_cancel_bt_node
+ - nav2_spin_cancel_bt_node
+ - nav2_back_up_cancel_bt_node
+ - nav2_assisted_teleop_cancel_bt_node
+ - nav2_drive_on_heading_cancel_bt_node
+ - nav2_is_battery_charging_condition_bt_node
+
+bt_navigator_navigate_through_poses_rclcpp_node:
+ ros__parameters:
+ use_sim_time: True
+
+bt_navigator_navigate_to_pose_rclcpp_node:
+ ros__parameters:
+ use_sim_time: True
+
+controller_server:
+ ros__parameters:
+ use_sim_time: True
+ controller_frequency: 20.0
+ min_x_velocity_threshold: 0.001
+ min_y_velocity_threshold: 0.5
+ min_theta_velocity_threshold: 0.001
+ failure_tolerance: 0.3
+ progress_checker_plugin: "progress_checker"
+ goal_checker_plugins: ["general_goal_checker"] # "precise_goal_checker"
+ controller_plugins: ["FollowPath"]
+
+ # Progress checker parameters
+ progress_checker:
+ plugin: "nav2_controller::SimpleProgressChecker"
+ required_movement_radius: 0.5
+ movement_time_allowance: 10.0
+ # Goal checker parameters
+ #precise_goal_checker:
+ # plugin: "nav2_controller::SimpleGoalChecker"
+ # xy_goal_tolerance: 0.25
+ # yaw_goal_tolerance: 0.25
+ # stateful: True
+ general_goal_checker:
+ stateful: True
+ plugin: "nav2_controller::SimpleGoalChecker"
+ xy_goal_tolerance: 0.25
+ yaw_goal_tolerance: 0.25
+ # DWB parameters
+ FollowPath:
+ plugin: "dwb_core::DWBLocalPlanner"
+ debug_trajectory_details: True
+ min_vel_x: 0.0
+ min_vel_y: 0.0
+ max_vel_x: 0.26
+ max_vel_y: 0.0
+ max_vel_theta: 1.0
+ min_speed_xy: 0.0
+ max_speed_xy: 0.26
+ min_speed_theta: 0.0
+ # Add high threshold velocity for turtlebot 3 issue.
+ # https://github.com/ROBOTIS-GIT/turtlebot3_simulations/issues/75
+ acc_lim_x: 2.5
+ acc_lim_y: 0.0
+ acc_lim_theta: 3.2
+ decel_lim_x: -2.5
+ decel_lim_y: 0.0
+ decel_lim_theta: -3.2
+ vx_samples: 20
+ vy_samples: 5
+ vtheta_samples: 20
+ sim_time: 1.7
+ linear_granularity: 0.05
+ angular_granularity: 0.025
+ transform_tolerance: 0.2
+ xy_goal_tolerance: 0.25
+ trans_stopped_velocity: 0.25
+ short_circuit_trajectory_evaluation: True
+ stateful: True
+ critics: ["RotateToGoal", "Oscillation", "BaseObstacle", "GoalAlign", "PathAlign", "PathDist", "GoalDist"]
+ BaseObstacle.scale: 0.02
+ PathAlign.scale: 32.0
+ PathAlign.forward_point_distance: 0.1
+ GoalAlign.scale: 24.0
+ GoalAlign.forward_point_distance: 0.1
+ PathDist.scale: 32.0
+ GoalDist.scale: 24.0
+ RotateToGoal.scale: 32.0
+ RotateToGoal.slowing_factor: 5.0
+ RotateToGoal.lookahead_time: -1.0
+
+local_costmap:
+ local_costmap:
+ ros__parameters:
+ update_frequency: 5.0
+ publish_frequency: 2.0
+ global_frame: odom
+ robot_base_frame: base_link
+ use_sim_time: True
+ rolling_window: true
+ width: 3
+ height: 3
+ resolution: 0.05
+ robot_radius: 0.22
+ plugins: ["voxel_layer", "inflation_layer"]
+ inflation_layer:
+ plugin: "nav2_costmap_2d::InflationLayer"
+ cost_scaling_factor: 3.0
+ inflation_radius: 0.55
+ voxel_layer:
+ plugin: "nav2_costmap_2d::VoxelLayer"
+ enabled: True
+ publish_voxel_map: True
+ origin_z: 0.0
+ z_resolution: 0.05
+ z_voxels: 16
+ max_obstacle_height: 2.0
+ mark_threshold: 0
+ observation_sources: ground obstacles
+ ground:
+ topic: /camera/ground
+ max_obstacle_height: 0.4
+ clearing: True
+ marking: False
+ data_type: "PointCloud2"
+ raytrace_max_range: 3.0
+ raytrace_min_range: 0.0
+ obstacle_max_range: 2.5
+ obstacle_min_range: 0.0
+ obstacles:
+ topic: /camera/obstacles
+ max_obstacle_height: 0.4
+ clearing: True
+ marking: True
+ data_type: "PointCloud2"
+ raytrace_max_range: 3.0
+ raytrace_min_range: 0.0
+ obstacle_max_range: 2.5
+ obstacle_min_range: 0.0
+ static_layer:
+ plugin: "nav2_costmap_2d::StaticLayer"
+ map_subscribe_transient_local: True
+ always_send_full_costmap: True
+
+global_costmap:
+ global_costmap:
+ ros__parameters:
+ update_frequency: 1.0
+ publish_frequency: 1.0
+ global_frame: map
+ robot_base_frame: base_link
+ use_sim_time: True
+ robot_radius: 0.22
+ resolution: 0.05
+ track_unknown_space: true
+ plugins: ["static_layer", "inflation_layer"]
+ static_layer:
+ plugin: "nav2_costmap_2d::StaticLayer"
+ map_subscribe_transient_local: True
+ inflation_layer:
+ plugin: "nav2_costmap_2d::InflationLayer"
+ cost_scaling_factor: 3.0
+ inflation_radius: 0.55
+ always_send_full_costmap: True
+
+map_server:
+ ros__parameters:
+ use_sim_time: True
+ # Overridden in launch by the "map" launch configuration or provided default value.
+ # To use in yaml, remove the default "map" value in the tb3_simulation_launch.py file & provide full path to map below.
+ yaml_filename: ""
+
+smoother_server:
+ ros__parameters:
+ use_sim_time: True
+ smoother_plugins: ["simple_smoother"]
+ simple_smoother:
+ plugin: "nav2_smoother::SimpleSmoother"
+ tolerance: 1.0e-10
+ max_its: 1000
+ do_refinement: True
+
+behavior_server:
+ ros__parameters:
+ costmap_topic: local_costmap/costmap_raw
+ footprint_topic: local_costmap/published_footprint
+ cycle_frequency: 10.0
+ behavior_plugins: ["spin", "backup", "drive_on_heading", "assisted_teleop", "wait"]
+ spin:
+ plugin: "nav2_behaviors/Spin"
+ backup:
+ plugin: "nav2_behaviors/BackUp"
+ drive_on_heading:
+ plugin: "nav2_behaviors/DriveOnHeading"
+ wait:
+ plugin: "nav2_behaviors/Wait"
+ assisted_teleop:
+ plugin: "nav2_behaviors/AssistedTeleop"
+ global_frame: odom
+ robot_base_frame: base_link
+ transform_tolerance: 0.1
+ use_sim_time: true
+ simulate_ahead_time: 2.0
+ max_rotational_vel: 1.0
+ min_rotational_vel: 0.4
+ rotational_acc_lim: 3.2
+
+robot_state_publisher:
+ ros__parameters:
+ use_sim_time: True
+
+waypoint_follower:
+ ros__parameters:
+ use_sim_time: True
+ loop_rate: 20
+ stop_on_failure: false
+ waypoint_task_executor_plugin: "wait_at_waypoint"
+ wait_at_waypoint:
+ plugin: "nav2_waypoint_follower::WaitAtWaypoint"
+ enabled: True
+ waypoint_pause_duration: 200
+
+velocity_smoother:
+ ros__parameters:
+ use_sim_time: True
+ smoothing_frequency: 20.0
+ scale_velocities: False
+ feedback: "OPEN_LOOP"
+ max_velocity: [0.26, 0.0, 1.0]
+ min_velocity: [-0.26, 0.0, -1.0]
+ max_accel: [2.5, 0.0, 3.2]
+ max_decel: [-2.5, 0.0, -3.2]
+ odom_topic: "odom"
+ odom_duration: 0.1
+ deadband_velocity: [0.0, 0.0, 0.0]
+ velocity_timeout: 1.0
diff --git a/rtabmap_demos/params/turtlebot3_rgbd_scan_nav2_params.yaml b/rtabmap_demos/params/turtlebot3_rgbd_scan_nav2_params.yaml
new file mode 100644
index 00000000..43dce5ba
--- /dev/null
+++ b/rtabmap_demos/params/turtlebot3_rgbd_scan_nav2_params.yaml
@@ -0,0 +1,301 @@
+# rtabmap_demos: We add segmented ground and obstacles to voxel_layer of the local costmap.
+bt_navigator:
+ ros__parameters:
+ use_sim_time: True
+ global_frame: map
+ robot_base_frame: base_link
+ odom_topic: /odom
+ bt_loop_duration: 10
+ default_server_timeout: 20
+ wait_for_service_timeout: 1000
+ # 'default_nav_through_poses_bt_xml' and 'default_nav_to_pose_bt_xml' are use defaults:
+ # nav2_bt_navigator/navigate_to_pose_w_replanning_and_recovery.xml
+ # nav2_bt_navigator/navigate_through_poses_w_replanning_and_recovery.xml
+ # They can be set here or via a RewrittenYaml remap from a parent launch file to Nav2.
+ plugin_lib_names:
+ - nav2_compute_path_to_pose_action_bt_node
+ - nav2_compute_path_through_poses_action_bt_node
+ - nav2_smooth_path_action_bt_node
+ - nav2_follow_path_action_bt_node
+ - nav2_spin_action_bt_node
+ - nav2_wait_action_bt_node
+ - nav2_assisted_teleop_action_bt_node
+ - nav2_back_up_action_bt_node
+ - nav2_drive_on_heading_bt_node
+ - nav2_clear_costmap_service_bt_node
+ - nav2_is_stuck_condition_bt_node
+ - nav2_goal_reached_condition_bt_node
+ - nav2_goal_updated_condition_bt_node
+ - nav2_globally_updated_goal_condition_bt_node
+ - nav2_is_path_valid_condition_bt_node
+ - nav2_initial_pose_received_condition_bt_node
+ - nav2_reinitialize_global_localization_service_bt_node
+ - nav2_rate_controller_bt_node
+ - nav2_distance_controller_bt_node
+ - nav2_speed_controller_bt_node
+ - nav2_truncate_path_action_bt_node
+ - nav2_truncate_path_local_action_bt_node
+ - nav2_goal_updater_node_bt_node
+ - nav2_recovery_node_bt_node
+ - nav2_pipeline_sequence_bt_node
+ - nav2_round_robin_node_bt_node
+ - nav2_transform_available_condition_bt_node
+ - nav2_time_expired_condition_bt_node
+ - nav2_path_expiring_timer_condition
+ - nav2_distance_traveled_condition_bt_node
+ - nav2_single_trigger_bt_node
+ - nav2_goal_updated_controller_bt_node
+ - nav2_is_battery_low_condition_bt_node
+ - nav2_navigate_through_poses_action_bt_node
+ - nav2_navigate_to_pose_action_bt_node
+ - nav2_remove_passed_goals_action_bt_node
+ - nav2_planner_selector_bt_node
+ - nav2_controller_selector_bt_node
+ - nav2_goal_checker_selector_bt_node
+ - nav2_controller_cancel_bt_node
+ - nav2_path_longer_on_approach_bt_node
+ - nav2_wait_cancel_bt_node
+ - nav2_spin_cancel_bt_node
+ - nav2_back_up_cancel_bt_node
+ - nav2_assisted_teleop_cancel_bt_node
+ - nav2_drive_on_heading_cancel_bt_node
+ - nav2_is_battery_charging_condition_bt_node
+
+bt_navigator_navigate_through_poses_rclcpp_node:
+ ros__parameters:
+ use_sim_time: True
+
+bt_navigator_navigate_to_pose_rclcpp_node:
+ ros__parameters:
+ use_sim_time: True
+
+controller_server:
+ ros__parameters:
+ use_sim_time: True
+ controller_frequency: 20.0
+ min_x_velocity_threshold: 0.001
+ min_y_velocity_threshold: 0.5
+ min_theta_velocity_threshold: 0.001
+ failure_tolerance: 0.3
+ progress_checker_plugin: "progress_checker"
+ goal_checker_plugins: ["general_goal_checker"] # "precise_goal_checker"
+ controller_plugins: ["FollowPath"]
+
+ # Progress checker parameters
+ progress_checker:
+ plugin: "nav2_controller::SimpleProgressChecker"
+ required_movement_radius: 0.5
+ movement_time_allowance: 10.0
+ # Goal checker parameters
+ #precise_goal_checker:
+ # plugin: "nav2_controller::SimpleGoalChecker"
+ # xy_goal_tolerance: 0.25
+ # yaw_goal_tolerance: 0.25
+ # stateful: True
+ general_goal_checker:
+ stateful: True
+ plugin: "nav2_controller::SimpleGoalChecker"
+ xy_goal_tolerance: 0.25
+ yaw_goal_tolerance: 0.25
+ # DWB parameters
+ FollowPath:
+ plugin: "dwb_core::DWBLocalPlanner"
+ debug_trajectory_details: True
+ min_vel_x: 0.0
+ min_vel_y: 0.0
+ max_vel_x: 0.26
+ max_vel_y: 0.0
+ max_vel_theta: 1.0
+ min_speed_xy: 0.0
+ max_speed_xy: 0.26
+ min_speed_theta: 0.0
+ # Add high threshold velocity for turtlebot 3 issue.
+ # https://github.com/ROBOTIS-GIT/turtlebot3_simulations/issues/75
+ acc_lim_x: 2.5
+ acc_lim_y: 0.0
+ acc_lim_theta: 3.2
+ decel_lim_x: -2.5
+ decel_lim_y: 0.0
+ decel_lim_theta: -3.2
+ vx_samples: 20
+ vy_samples: 5
+ vtheta_samples: 20
+ sim_time: 1.7
+ linear_granularity: 0.05
+ angular_granularity: 0.025
+ transform_tolerance: 0.2
+ xy_goal_tolerance: 0.25
+ trans_stopped_velocity: 0.25
+ short_circuit_trajectory_evaluation: True
+ stateful: True
+ critics: ["RotateToGoal", "Oscillation", "BaseObstacle", "GoalAlign", "PathAlign", "PathDist", "GoalDist"]
+ BaseObstacle.scale: 0.02
+ PathAlign.scale: 32.0
+ PathAlign.forward_point_distance: 0.1
+ GoalAlign.scale: 24.0
+ GoalAlign.forward_point_distance: 0.1
+ PathDist.scale: 32.0
+ GoalDist.scale: 24.0
+ RotateToGoal.scale: 32.0
+ RotateToGoal.slowing_factor: 5.0
+ RotateToGoal.lookahead_time: -1.0
+
+local_costmap:
+ local_costmap:
+ ros__parameters:
+ update_frequency: 5.0
+ publish_frequency: 2.0
+ global_frame: odom
+ robot_base_frame: base_link
+ use_sim_time: True
+ rolling_window: true
+ width: 3
+ height: 3
+ resolution: 0.05
+ robot_radius: 0.22
+ plugins: ["voxel_layer", "inflation_layer"]
+ inflation_layer:
+ plugin: "nav2_costmap_2d::InflationLayer"
+ cost_scaling_factor: 3.0
+ inflation_radius: 0.55
+ voxel_layer:
+ plugin: "nav2_costmap_2d::VoxelLayer"
+ enabled: True
+ publish_voxel_map: True
+ origin_z: 0.0
+ z_resolution: 0.05
+ z_voxels: 16
+ max_obstacle_height: 2.0
+ mark_threshold: 0
+ observation_sources: scan ground obstacles
+ scan:
+ topic: /scan
+ max_obstacle_height: 2.0
+ clearing: True
+ marking: True
+ data_type: "LaserScan"
+ raytrace_max_range: 3.0
+ raytrace_min_range: 0.0
+ obstacle_max_range: 2.5
+ obstacle_min_range: 0.0
+ ground:
+ topic: /camera/ground
+ max_obstacle_height: 0.4
+ clearing: True
+ marking: False
+ data_type: "PointCloud2"
+ raytrace_max_range: 3.0
+ raytrace_min_range: 0.0
+ obstacle_max_range: 2.5
+ obstacle_min_range: 0.0
+ obstacles:
+ topic: /camera/obstacles
+ max_obstacle_height: 0.4
+ clearing: True
+ marking: True
+ data_type: "PointCloud2"
+ raytrace_max_range: 3.0
+ raytrace_min_range: 0.0
+ obstacle_max_range: 2.5
+ obstacle_min_range: 0.0
+ static_layer:
+ plugin: "nav2_costmap_2d::StaticLayer"
+ map_subscribe_transient_local: True
+ always_send_full_costmap: True
+
+global_costmap:
+ global_costmap:
+ ros__parameters:
+ update_frequency: 1.0
+ publish_frequency: 1.0
+ global_frame: map
+ robot_base_frame: base_link
+ use_sim_time: True
+ robot_radius: 0.22
+ resolution: 0.05
+ track_unknown_space: true
+ plugins: ["static_layer", "inflation_layer"]
+ static_layer:
+ plugin: "nav2_costmap_2d::StaticLayer"
+ map_subscribe_transient_local: True
+ inflation_layer:
+ plugin: "nav2_costmap_2d::InflationLayer"
+ cost_scaling_factor: 3.0
+ inflation_radius: 0.55
+ always_send_full_costmap: True
+
+planner_server:
+ ros__parameters:
+ expected_planner_frequency: 20.0
+ use_sim_time: True
+ planner_plugins: ["GridBased"]
+ GridBased:
+ plugin: "nav2_navfn_planner/NavfnPlanner"
+ tolerance: 0.5
+ use_astar: false
+ allow_unknown: true
+
+smoother_server:
+ ros__parameters:
+ use_sim_time: True
+ smoother_plugins: ["simple_smoother"]
+ simple_smoother:
+ plugin: "nav2_smoother::SimpleSmoother"
+ tolerance: 1.0e-10
+ max_its: 1000
+ do_refinement: True
+
+behavior_server:
+ ros__parameters:
+ costmap_topic: local_costmap/costmap_raw
+ footprint_topic: local_costmap/published_footprint
+ cycle_frequency: 10.0
+ behavior_plugins: ["spin", "backup", "drive_on_heading", "assisted_teleop", "wait"]
+ spin:
+ plugin: "nav2_behaviors/Spin"
+ backup:
+ plugin: "nav2_behaviors/BackUp"
+ drive_on_heading:
+ plugin: "nav2_behaviors/DriveOnHeading"
+ wait:
+ plugin: "nav2_behaviors/Wait"
+ assisted_teleop:
+ plugin: "nav2_behaviors/AssistedTeleop"
+ global_frame: odom
+ robot_base_frame: base_link
+ transform_tolerance: 0.1
+ use_sim_time: true
+ simulate_ahead_time: 2.0
+ max_rotational_vel: 1.0
+ min_rotational_vel: 0.4
+ rotational_acc_lim: 3.2
+
+robot_state_publisher:
+ ros__parameters:
+ use_sim_time: True
+
+waypoint_follower:
+ ros__parameters:
+ use_sim_time: True
+ loop_rate: 20
+ stop_on_failure: false
+ waypoint_task_executor_plugin: "wait_at_waypoint"
+ wait_at_waypoint:
+ plugin: "nav2_waypoint_follower::WaitAtWaypoint"
+ enabled: True
+ waypoint_pause_duration: 200
+
+velocity_smoother:
+ ros__parameters:
+ use_sim_time: True
+ smoothing_frequency: 20.0
+ scale_velocities: False
+ feedback: "OPEN_LOOP"
+ max_velocity: [0.26, 0.0, 1.0]
+ min_velocity: [-0.26, 0.0, -1.0]
+ max_accel: [2.5, 0.0, 3.2]
+ max_decel: [-2.5, 0.0, -3.2]
+ odom_topic: "odom"
+ odom_duration: 0.1
+ deadband_velocity: [0.0, 0.0, 0.0]
+ velocity_timeout: 1.0
diff --git a/rtabmap_demos/params/turtlebot3_scan_nav2_params.yaml b/rtabmap_demos/params/turtlebot3_scan_nav2_params.yaml
new file mode 100644
index 00000000..9c33bdb2
--- /dev/null
+++ b/rtabmap_demos/params/turtlebot3_scan_nav2_params.yaml
@@ -0,0 +1,295 @@
+# Modified to use icp_odom frame
+bt_navigator:
+ ros__parameters:
+ use_sim_time: True
+ global_frame: map
+ robot_base_frame: base_link
+ odom_topic: /odom
+ bt_loop_duration: 10
+ default_server_timeout: 20
+ wait_for_service_timeout: 1000
+ # 'default_nav_through_poses_bt_xml' and 'default_nav_to_pose_bt_xml' are use defaults:
+ # nav2_bt_navigator/navigate_to_pose_w_replanning_and_recovery.xml
+ # nav2_bt_navigator/navigate_through_poses_w_replanning_and_recovery.xml
+ # They can be set here or via a RewrittenYaml remap from a parent launch file to Nav2.
+ plugin_lib_names:
+ - nav2_compute_path_to_pose_action_bt_node
+ - nav2_compute_path_through_poses_action_bt_node
+ - nav2_smooth_path_action_bt_node
+ - nav2_follow_path_action_bt_node
+ - nav2_spin_action_bt_node
+ - nav2_wait_action_bt_node
+ - nav2_assisted_teleop_action_bt_node
+ - nav2_back_up_action_bt_node
+ - nav2_drive_on_heading_bt_node
+ - nav2_clear_costmap_service_bt_node
+ - nav2_is_stuck_condition_bt_node
+ - nav2_goal_reached_condition_bt_node
+ - nav2_goal_updated_condition_bt_node
+ - nav2_globally_updated_goal_condition_bt_node
+ - nav2_is_path_valid_condition_bt_node
+ - nav2_initial_pose_received_condition_bt_node
+ - nav2_reinitialize_global_localization_service_bt_node
+ - nav2_rate_controller_bt_node
+ - nav2_distance_controller_bt_node
+ - nav2_speed_controller_bt_node
+ - nav2_truncate_path_action_bt_node
+ - nav2_truncate_path_local_action_bt_node
+ - nav2_goal_updater_node_bt_node
+ - nav2_recovery_node_bt_node
+ - nav2_pipeline_sequence_bt_node
+ - nav2_round_robin_node_bt_node
+ - nav2_transform_available_condition_bt_node
+ - nav2_time_expired_condition_bt_node
+ - nav2_path_expiring_timer_condition
+ - nav2_distance_traveled_condition_bt_node
+ - nav2_single_trigger_bt_node
+ - nav2_goal_updated_controller_bt_node
+ - nav2_is_battery_low_condition_bt_node
+ - nav2_navigate_through_poses_action_bt_node
+ - nav2_navigate_to_pose_action_bt_node
+ - nav2_remove_passed_goals_action_bt_node
+ - nav2_planner_selector_bt_node
+ - nav2_controller_selector_bt_node
+ - nav2_goal_checker_selector_bt_node
+ - nav2_controller_cancel_bt_node
+ - nav2_path_longer_on_approach_bt_node
+ - nav2_wait_cancel_bt_node
+ - nav2_spin_cancel_bt_node
+ - nav2_back_up_cancel_bt_node
+ - nav2_assisted_teleop_cancel_bt_node
+ - nav2_drive_on_heading_cancel_bt_node
+ - nav2_is_battery_charging_condition_bt_node
+
+bt_navigator_navigate_through_poses_rclcpp_node:
+ ros__parameters:
+ use_sim_time: True
+
+bt_navigator_navigate_to_pose_rclcpp_node:
+ ros__parameters:
+ use_sim_time: True
+
+controller_server:
+ ros__parameters:
+ use_sim_time: True
+ controller_frequency: 20.0
+ min_x_velocity_threshold: 0.001
+ min_y_velocity_threshold: 0.5
+ min_theta_velocity_threshold: 0.001
+ failure_tolerance: 0.3
+ progress_checker_plugin: "progress_checker"
+ goal_checker_plugins: ["general_goal_checker"] # "precise_goal_checker"
+ controller_plugins: ["FollowPath"]
+
+ # Progress checker parameters
+ progress_checker:
+ plugin: "nav2_controller::SimpleProgressChecker"
+ required_movement_radius: 0.5
+ movement_time_allowance: 10.0
+ # Goal checker parameters
+ #precise_goal_checker:
+ # plugin: "nav2_controller::SimpleGoalChecker"
+ # xy_goal_tolerance: 0.25
+ # yaw_goal_tolerance: 0.25
+ # stateful: True
+ general_goal_checker:
+ stateful: True
+ plugin: "nav2_controller::SimpleGoalChecker"
+ xy_goal_tolerance: 0.25
+ yaw_goal_tolerance: 0.25
+ # DWB parameters
+ FollowPath:
+ plugin: "dwb_core::DWBLocalPlanner"
+ debug_trajectory_details: True
+ min_vel_x: 0.0
+ min_vel_y: 0.0
+ max_vel_x: 0.26
+ max_vel_y: 0.0
+ max_vel_theta: 1.0
+ min_speed_xy: 0.0
+ max_speed_xy: 0.26
+ min_speed_theta: 0.0
+ # Add high threshold velocity for turtlebot 3 issue.
+ # https://github.com/ROBOTIS-GIT/turtlebot3_simulations/issues/75
+ acc_lim_x: 2.5
+ acc_lim_y: 0.0
+ acc_lim_theta: 3.2
+ decel_lim_x: -2.5
+ decel_lim_y: 0.0
+ decel_lim_theta: -3.2
+ vx_samples: 20
+ vy_samples: 5
+ vtheta_samples: 20
+ sim_time: 1.7
+ linear_granularity: 0.05
+ angular_granularity: 0.025
+ transform_tolerance: 0.2
+ xy_goal_tolerance: 0.25
+ trans_stopped_velocity: 0.25
+ short_circuit_trajectory_evaluation: True
+ stateful: True
+ critics: ["RotateToGoal", "Oscillation", "BaseObstacle", "GoalAlign", "PathAlign", "PathDist", "GoalDist"]
+ BaseObstacle.scale: 0.02
+ PathAlign.scale: 32.0
+ PathAlign.forward_point_distance: 0.1
+ GoalAlign.scale: 24.0
+ GoalAlign.forward_point_distance: 0.1
+ PathDist.scale: 32.0
+ GoalDist.scale: 24.0
+ RotateToGoal.scale: 32.0
+ RotateToGoal.slowing_factor: 5.0
+ RotateToGoal.lookahead_time: -1.0
+
+local_costmap:
+ local_costmap:
+ ros__parameters:
+ update_frequency: 5.0
+ publish_frequency: 2.0
+ global_frame: icp_odom
+ robot_base_frame: base_link
+ use_sim_time: True
+ rolling_window: true
+ width: 3
+ height: 3
+ resolution: 0.05
+ robot_radius: 0.22
+ plugins: ["voxel_layer", "inflation_layer"]
+ inflation_layer:
+ plugin: "nav2_costmap_2d::InflationLayer"
+ cost_scaling_factor: 3.0
+ inflation_radius: 0.55
+ voxel_layer:
+ plugin: "nav2_costmap_2d::VoxelLayer"
+ enabled: True
+ publish_voxel_map: True
+ origin_z: 0.0
+ z_resolution: 0.05
+ z_voxels: 16
+ max_obstacle_height: 2.0
+ mark_threshold: 0
+ observation_sources: scan
+ scan:
+ topic: /scan
+ max_obstacle_height: 2.0
+ clearing: True
+ marking: True
+ data_type: "LaserScan"
+ raytrace_max_range: 3.0
+ raytrace_min_range: 0.0
+ obstacle_max_range: 2.5
+ obstacle_min_range: 0.0
+ static_layer:
+ plugin: "nav2_costmap_2d::StaticLayer"
+ map_subscribe_transient_local: True
+ always_send_full_costmap: True
+
+global_costmap:
+ global_costmap:
+ ros__parameters:
+ update_frequency: 1.0
+ publish_frequency: 1.0
+ global_frame: map
+ robot_base_frame: base_link
+ use_sim_time: True
+ robot_radius: 0.22
+ resolution: 0.05
+ track_unknown_space: true
+ plugins: ["static_layer", "obstacle_layer", "inflation_layer"]
+ obstacle_layer:
+ plugin: "nav2_costmap_2d::ObstacleLayer"
+ enabled: True
+ observation_sources: scan
+ scan:
+ topic: /scan
+ max_obstacle_height: 2.0
+ clearing: True
+ marking: True
+ data_type: "LaserScan"
+ raytrace_max_range: 3.0
+ raytrace_min_range: 0.0
+ obstacle_max_range: 2.5
+ obstacle_min_range: 0.0
+ static_layer:
+ plugin: "nav2_costmap_2d::StaticLayer"
+ map_subscribe_transient_local: True
+ inflation_layer:
+ plugin: "nav2_costmap_2d::InflationLayer"
+ cost_scaling_factor: 3.0
+ inflation_radius: 0.55
+ always_send_full_costmap: True
+
+planner_server:
+ ros__parameters:
+ expected_planner_frequency: 20.0
+ use_sim_time: True
+ planner_plugins: ["GridBased"]
+ GridBased:
+ plugin: "nav2_navfn_planner/NavfnPlanner"
+ tolerance: 0.5
+ use_astar: false
+ allow_unknown: true
+
+smoother_server:
+ ros__parameters:
+ use_sim_time: True
+ smoother_plugins: ["simple_smoother"]
+ simple_smoother:
+ plugin: "nav2_smoother::SimpleSmoother"
+ tolerance: 1.0e-10
+ max_its: 1000
+ do_refinement: True
+
+behavior_server:
+ ros__parameters:
+ costmap_topic: local_costmap/costmap_raw
+ footprint_topic: local_costmap/published_footprint
+ cycle_frequency: 10.0
+ behavior_plugins: ["spin", "backup", "drive_on_heading", "assisted_teleop", "wait"]
+ spin:
+ plugin: "nav2_behaviors/Spin"
+ backup:
+ plugin: "nav2_behaviors/BackUp"
+ drive_on_heading:
+ plugin: "nav2_behaviors/DriveOnHeading"
+ wait:
+ plugin: "nav2_behaviors/Wait"
+ assisted_teleop:
+ plugin: "nav2_behaviors/AssistedTeleop"
+ global_frame: icp_odom
+ robot_base_frame: base_link
+ transform_tolerance: 0.1
+ use_sim_time: true
+ simulate_ahead_time: 2.0
+ max_rotational_vel: 1.0
+ min_rotational_vel: 0.4
+ rotational_acc_lim: 3.2
+
+robot_state_publisher:
+ ros__parameters:
+ use_sim_time: True
+
+waypoint_follower:
+ ros__parameters:
+ use_sim_time: True
+ loop_rate: 20
+ stop_on_failure: false
+ waypoint_task_executor_plugin: "wait_at_waypoint"
+ wait_at_waypoint:
+ plugin: "nav2_waypoint_follower::WaitAtWaypoint"
+ enabled: True
+ waypoint_pause_duration: 200
+
+velocity_smoother:
+ ros__parameters:
+ use_sim_time: True
+ smoothing_frequency: 20.0
+ scale_velocities: False
+ feedback: "OPEN_LOOP"
+ max_velocity: [0.26, 0.0, 1.0]
+ min_velocity: [-0.26, 0.0, -1.0]
+ max_accel: [2.5, 0.0, 3.2]
+ max_decel: [-2.5, 0.0, -3.2]
+ odom_topic: "odom"
+ odom_duration: 0.1
+ deadband_velocity: [0.0, 0.0, 0.0]
+ velocity_timeout: 1.0
diff --git a/rtabmap_examples/CMakeLists.txt b/rtabmap_examples/CMakeLists.txt
index a8020127..07ea312c 100644
--- a/rtabmap_examples/CMakeLists.txt
+++ b/rtabmap_examples/CMakeLists.txt
@@ -3,7 +3,7 @@ project(rtabmap_examples)
find_package(ament_cmake REQUIRED)
-install(DIRECTORY launch
+install(DIRECTORY launch config
DESTINATION share/${PROJECT_NAME}
)
diff --git a/rtabmap_examples/launch/config/euroc_left.yaml b/rtabmap_examples/config/euroc_left.yaml
similarity index 100%
rename from rtabmap_examples/launch/config/euroc_left.yaml
rename to rtabmap_examples/config/euroc_left.yaml
diff --git a/rtabmap_examples/launch/config/euroc_right.yaml b/rtabmap_examples/config/euroc_right.yaml
similarity index 100%
rename from rtabmap_examples/launch/config/euroc_right.yaml
rename to rtabmap_examples/config/euroc_right.yaml
diff --git a/rtabmap_examples/launch/config/slam_D405x2_config.rviz b/rtabmap_examples/config/slam_D405x2_config.rviz
similarity index 100%
rename from rtabmap_examples/launch/config/slam_D405x2_config.rviz
rename to rtabmap_examples/config/slam_D405x2_config.rviz
diff --git a/rtabmap_examples/launch/config/slam_D405x3_config.rviz b/rtabmap_examples/config/slam_D405x3_config.rviz
similarity index 100%
rename from rtabmap_examples/launch/config/slam_D405x3_config.rviz
rename to rtabmap_examples/config/slam_D405x3_config.rviz
diff --git a/rtabmap_examples/launch/depthai.launch.py b/rtabmap_examples/launch/depthai.launch.py
new file mode 100644
index 00000000..8918bba0
--- /dev/null
+++ b/rtabmap_examples/launch/depthai.launch.py
@@ -0,0 +1,72 @@
+# Requirements:
+# A OAK-D camera
+# Install depthai-ros package (https://github.com/luxonis/depthai-ros)
+# Example:
+# $ ros2 launch rtabmap_examples depthai.launch.py camera_model:=OAK-D
+
+import os
+
+from ament_index_python.packages import get_package_share_directory
+
+from launch import LaunchDescription
+from launch_ros.actions import Node
+from launch.actions import IncludeLaunchDescription
+from launch.launch_description_sources import PythonLaunchDescriptionSource
+
+
+def generate_launch_description():
+ parameters=[{'frame_id':'oak-d-base-frame',
+ 'subscribe_rgbd':True,
+ 'subscribe_odom_info':True,
+ 'approx_sync':False,
+ 'wait_imu_to_init':True}]
+
+ remappings=[('imu', '/imu/data')]
+
+ return LaunchDescription([
+
+ # Launch camera driver
+ IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([os.path.join(
+ get_package_share_directory('depthai_examples'), 'launch'),
+ '/stereo_inertial_node.launch.py']),
+ launch_arguments={'depth_aligned': 'false',
+ 'enableRviz': 'false',
+ 'monoResolution': '400p'}.items(),
+ ),
+
+ # Sync right/depth/camera_info together
+ Node(
+ package='rtabmap_sync', executable='rgbd_sync', output='screen',
+ parameters=parameters,
+ remappings=[('rgb/image', '/right/image_rect'),
+ ('rgb/camera_info', '/right/camera_info'),
+ ('depth/image', '/stereo/depth')]),
+
+ # Compute quaternion of the IMU
+ Node(
+ package='imu_filter_madgwick', executable='imu_filter_madgwick_node', output='screen',
+ parameters=[{'use_mag': False,
+ 'world_frame':'enu',
+ 'publish_tf':False}],
+ remappings=[('imu/data_raw', '/imu')]),
+
+ # Visual odometry
+ Node(
+ package='rtabmap_odom', executable='rgbd_odometry', output='screen',
+ parameters=parameters,
+ remappings=remappings),
+
+ # VSLAM
+ Node(
+ package='rtabmap_slam', executable='rtabmap', output='screen',
+ parameters=parameters,
+ remappings=remappings,
+ arguments=['-d']),
+
+ # Visualization
+ Node(
+ package='rtabmap_viz', executable='rtabmap_viz', output='screen',
+ parameters=parameters,
+ remappings=remappings)
+ ])
diff --git a/rtabmap_examples/launch/euroc_datasets.launch.py b/rtabmap_examples/launch/euroc_datasets.launch.py
index 01112ed9..8c9f31ca 100644
--- a/rtabmap_examples/launch/euroc_datasets.launch.py
+++ b/rtabmap_examples/launch/euroc_datasets.launch.py
@@ -88,7 +88,7 @@ def generate_launch_description():
# Image rectification and publishing synchronized camera_info
Node(
package='rtabmap_util', executable='yaml_to_camera_info.py', output='screen',
- parameters=[{'yaml_path': [FindPackageShare('rtabmap_examples'), '/launch/config/euroc_left.yaml']}],
+ parameters=[{'yaml_path': [FindPackageShare('rtabmap_examples'), '/config/euroc_left.yaml']}],
remappings=[
('image', '/cam0/image_raw'),
('camera_info', 'left/camera_info')],
@@ -96,7 +96,7 @@ def generate_launch_description():
Node(
package='rtabmap_util', executable='yaml_to_camera_info.py', output='screen',
- parameters=[{'yaml_path': [FindPackageShare('rtabmap_examples'), '/launch/config/euroc_right.yaml']}],
+ parameters=[{'yaml_path': [FindPackageShare('rtabmap_examples'), '/config/euroc_right.yaml']}],
remappings=[
('image', '/cam1/image_raw'),
('camera_info', 'right/camera_info')],
diff --git a/rtabmap_examples/launch/k4a.launch.py b/rtabmap_examples/launch/k4a.launch.py
index 346bf2e0..d09693a6 100644
--- a/rtabmap_examples/launch/k4a.launch.py
+++ b/rtabmap_examples/launch/k4a.launch.py
@@ -1,21 +1,20 @@
# Requirements:
# A Kinect for Azure
# Install Azure_Kinect_ROS_Driver ros2 package (https://github.com/microsoft/Azure_Kinect_ROS_Driver/tree/humble)
+# To install Kinect SDK on Ubuntu 22.04, see https://github.com/microsoft/Azure-Kinect-Sensor-SDK/issues/1790#issuecomment-1531626651
+# udev rules: https://github.com/microsoft/Azure-Kinect-Sensor-SDK/blob/5f79890933e1c81e325633152b2f2799df825b8b/docs/usage.md#linux-device-setup
# Install imu_filter_madgwick ros2 package
# Example:
# $ ros2 launch rtabmap_examples k4a.launch.py
from launch import LaunchDescription
-from launch.actions import DeclareLaunchArgument, SetEnvironmentVariable
-from launch.substitutions import LaunchConfiguration
from launch_ros.actions import Node
def generate_launch_description():
parameters=[{
'frame_id':'camera_base',
'subscribe_rgbd':True,
- 'subscribe_odom_info':True,
- 'qos':1}]
+ 'subscribe_odom_info':True}]
remappings=[
('imu', '/imu/data'),
@@ -32,11 +31,9 @@ def generate_launch_description():
Node(
package='rtabmap_odom', executable='rgbd_odometry', output='screen',
parameters=[{ 'frame_id':'camera_base',
- 'subscribe_odom_info':True,
'approx_sync':True,
'approx_sync_max_interval':0.01,
'wait_imu_to_init':True,
- 'qos':1,
'queue_size':30,
'keep_color':True,
# Color image needs to be rectified,
diff --git a/rtabmap_examples/launch/kinect_xbox_360.launch.py b/rtabmap_examples/launch/kinect_xbox_360.launch.py
index 2968e665..2b20d898 100644
--- a/rtabmap_examples/launch/kinect_xbox_360.launch.py
+++ b/rtabmap_examples/launch/kinect_xbox_360.launch.py
@@ -12,8 +12,7 @@ def generate_launch_description():
'frame_id':'camera_link',
'subscribe_depth':True,
'subscribe_odom_info':True,
- 'approx_sync':True,
- 'qos':1}]
+ 'approx_sync':True}]
remappings=[
('rgb/image', '/kinect/rgb/image_raw'),
diff --git a/rtabmap_examples/launch/lidar3d.launch.py b/rtabmap_examples/launch/lidar3d.launch.py
new file mode 100644
index 00000000..6ddee09a
--- /dev/null
+++ b/rtabmap_examples/launch/lidar3d.launch.py
@@ -0,0 +1,253 @@
+# Description:
+# In this example, we keep only minimal data to do LiDAR SLAM.
+#
+# Example:
+# Launch your lidar sensor:
+# $ ros2 launch velodyne_driver velodyne_driver_node-VLP16-launch.py
+# $ ros2 launch velodyne_pointcloud velodyne_transform_node-VLP16-launch.py
+#
+# If an IMU is used, make sure TF between lidar/base frame and imu is
+# already calibrated. In this example, we assume the imu topic has
+# already the orientation estimated, if not, you can use
+# imu_filter_madgwick_node (with use_mag:=false publish_tf:=false)
+# and set imu_topic to output topic of the filter.
+#
+# If a camera is used, make sure TF between lidar/base frame and camera is
+# already calibrated. To provide image data to this example, you should use
+# rtabmap_sync's rgbd_sync or stereo_sync node.
+#
+# Launch the example by adjusting the lidar topic and base frame:
+# $ ros2 launch rtabmap_examples lidar3d.launch.py lidar_topic:=/velodyne_points frame_id:=velodyne
+
+from launch import LaunchDescription, LaunchContext
+from launch.actions import DeclareLaunchArgument, OpaqueFunction
+from launch.substitutions import LaunchConfiguration
+from launch_ros.actions import Node
+
+def launch_setup(context: LaunchContext, *args, **kwargs):
+
+ frame_id = LaunchConfiguration('frame_id')
+
+ imu_topic = LaunchConfiguration('imu_topic')
+ imu_used = imu_topic.perform(context) != ''
+
+ rgbd_image_topic = LaunchConfiguration('rgbd_image_topic')
+ rgbd_images_topic = LaunchConfiguration('rgbd_images_topic')
+ rgbd_image_used = rgbd_image_topic.perform(context) != '' or rgbd_images_topic.perform(context) != ''
+ rgbd_cameras = 0 if rgbd_images_topic.perform(context) != '' else 1
+
+ voxel_size = LaunchConfiguration('voxel_size')
+ voxel_size_value = float(voxel_size.perform(context))
+
+ use_sim_time = LaunchConfiguration('use_sim_time')
+
+ lidar_topic = LaunchConfiguration('lidar_topic')
+ lidar_topic_value = lidar_topic.perform(context)
+ lidar_topic_deskewed = lidar_topic_value + "/deskewed"
+
+ localization = LaunchConfiguration('localization').perform(context)
+ localization = localization == 'true' or localization == 'True'
+
+ deskewing = LaunchConfiguration('deskewing').perform(context)
+ deskewing = deskewing == 'true' or deskewing == 'True'
+
+ deskewing_slerp = LaunchConfiguration('deskewing_slerp').perform(context)
+ deskewing_slerp = deskewing_slerp == 'true' or deskewing_slerp == 'True'
+
+ fixed_frame_from_imu = False
+ fixed_frame_id = LaunchConfiguration('fixed_frame_id').perform(context)
+ if not fixed_frame_id and imu_used:
+ fixed_frame_from_imu = True
+ fixed_frame_id = frame_id.perform(context) + "_stabilized"
+
+ if not fixed_frame_id or not deskewing:
+ lidar_topic_deskewed = lidar_topic
+
+ # Rule of thumb:
+ max_correspondence_distance = voxel_size_value * 10.0
+
+ shared_parameters = {
+ 'use_sim_time': use_sim_time,
+ 'frame_id': frame_id,
+ 'qos': LaunchConfiguration('qos'),
+ 'approx_sync': rgbd_image_used,
+ 'wait_for_transform': 0.2,
+ # RTAB-Map's internal parameters are strings:
+ 'Icp/PointToPlane': 'true',
+ 'Icp/Iterations': '10',
+ 'Icp/VoxelSize': str(voxel_size_value),
+ 'Icp/Epsilon': '0.001',
+ 'Icp/PointToPlaneK': '20',
+ 'Icp/PointToPlaneRadius': '0',
+ 'Icp/MaxTranslation': '3',
+ 'Icp/MaxCorrespondenceDistance': str(max_correspondence_distance),
+ 'Icp/Strategy': '1',
+ 'Icp/OutlierRatio': '0.7',
+ }
+
+ icp_odometry_parameters = {
+ 'expected_update_rate': LaunchConfiguration('expected_update_rate'),
+ 'deskewing': not fixed_frame_id and deskewing, # If fixed_frame_id is set, we do deskewing externally below
+ 'odom_frame_id': 'icp_odom',
+ 'guess_frame_id': fixed_frame_id,
+ 'deskewing_slerp': deskewing_slerp,
+ # RTAB-Map's internal parameters are strings:
+ 'Odom/ScanKeyFrameThr': '0.4',
+ 'OdomF2M/ScanSubtractRadius': str(voxel_size_value),
+ 'OdomF2M/ScanMaxSize': '15000',
+ 'OdomF2M/BundleAdjustment': 'false',
+ 'Icp/CorrespondenceRatio': '0.01'
+ }
+ if imu_used:
+ icp_odometry_parameters['wait_imu_to_init'] = True
+
+ rtabmap_parameters = {
+ 'subscribe_depth': False,
+ 'subscribe_rgb': False,
+ 'subscribe_odom_info': True,
+ 'subscribe_scan_cloud': True,
+ 'map_frame_id': 'new_map',
+ 'odom_sensor_sync': True, # This will adjust camera position based on difference between lidar and camera stamps.
+ # RTAB-Map's internal parameters are strings:
+ 'RGBD/ProximityMaxGraphDepth': '0',
+ 'RGBD/ProximityPathMaxNeighbors': '1',
+ 'RGBD/AngularUpdate': '0.05',
+ 'RGBD/LinearUpdate': '0.05',
+ 'RGBD/CreateOccupancyGrid': 'false',
+ 'Mem/NotLinkedNodesKept': 'false',
+ 'Mem/STMSize': '30',
+ 'Reg/Strategy': '1',
+ 'Icp/CorrespondenceRatio': str(LaunchConfiguration('min_loop_closure_overlap').perform(context))
+ }
+
+ arguments = []
+ if localization:
+ rtabmap_parameters['Mem/IncrementalMemory'] = 'False'
+ rtabmap_parameters['Mem/InitWMWithAllNodes'] = 'True'
+ else:
+ arguments.append('-d') # This will delete the previous database (~/.ros/rtabmap.db)
+
+ remappings = [('odom', 'icp_odom')]
+ if imu_used:
+ remappings.append(('imu', LaunchConfiguration('imu_topic')))
+ else:
+ remappings.append(('imu', 'imu_not_used'))
+ if rgbd_image_used:
+ if rgbd_cameras == 1:
+ remappings.append(('rgbd_image', LaunchConfiguration('rgbd_image_topic')))
+ else:
+ remappings.append(('rgbd_images', LaunchConfiguration('rgbd_images_topic')))
+
+ nodes = [
+ Node(
+ package='rtabmap_odom', executable='icp_odometry', output='screen',
+ parameters=[shared_parameters, icp_odometry_parameters],
+ remappings=remappings + [('scan_cloud', lidar_topic_deskewed)]),
+
+ Node(
+ package='rtabmap_slam', executable='rtabmap', output='screen',
+ parameters=[shared_parameters, rtabmap_parameters,
+ {'subscribe_rgbd': rgbd_image_used,
+ 'rgbd_cameras': rgbd_cameras}],
+ remappings=remappings + [('scan_cloud', lidar_topic_deskewed)],
+ arguments=arguments),
+
+ Node(
+ package='rtabmap_viz', executable='rtabmap_viz', output='screen',
+ parameters=[shared_parameters, rtabmap_parameters],
+ remappings=remappings + [('scan_cloud', 'odom_filtered_input_scan')])
+ ]
+
+ if fixed_frame_from_imu:
+ # Create a stabilized base frame based on imu for lidar deskewing
+ nodes.append(
+ Node(
+ package='rtabmap_util', executable='imu_to_tf', output='screen',
+ parameters=[{
+ 'use_sim_time': use_sim_time,
+ 'fixed_frame_id': fixed_frame_id,
+ 'base_frame_id': frame_id,
+ 'wait_for_transform_duration': 0.001}],
+ remappings=[('imu/data', imu_topic)]))
+
+ if fixed_frame_id and deskewing:
+ # Lidar deskewing
+ nodes.append(
+ Node(
+ package='rtabmap_util', executable='lidar_deskewing', output='screen',
+ parameters=[{
+ 'use_sim_time': use_sim_time,
+ 'fixed_frame_id': fixed_frame_id,
+ 'wait_for_transform': 0.2,
+ 'slerp': deskewing_slerp}],
+ remappings=[
+ ('input_cloud', lidar_topic)
+ ])
+ )
+
+ return nodes
+
+def generate_launch_description():
+ return LaunchDescription([
+
+ # Launch arguments
+ DeclareLaunchArgument(
+ 'use_sim_time', default_value='false',
+ description='Use simulated clock.'),
+
+ DeclareLaunchArgument(
+ 'deskewing', default_value='true',
+ description='Enable lidar deskewing.'),
+
+ DeclareLaunchArgument(
+ 'frame_id', default_value='velodyne',
+ description='Base frame of the robot.'),
+
+ DeclareLaunchArgument(
+ 'fixed_frame_id', default_value='',
+ description='Fixed frame used for lidar deskewing. If not set, we will generate one from IMU.'),
+
+ DeclareLaunchArgument(
+ 'localization', default_value='false',
+ description='Localization mode.'),
+
+ DeclareLaunchArgument(
+ 'lidar_topic', default_value='/velodyne_points',
+ description='Name of the lidar PointCloud2 topic.'),
+
+ DeclareLaunchArgument(
+ 'imu_topic', default_value='',
+ description='IMU topic (ignored if empty).'),
+
+ DeclareLaunchArgument(
+ 'rgbd_image_topic', default_value='',
+ description='RGBD image topic (ignored if empty). Would be the output of a rtabmap_sync\'s rgbd_sync, stereo_sync or rgb_sync node.'),
+
+ DeclareLaunchArgument(
+ 'rgbd_images_topic', default_value='',
+ description='RGBD images topic (ignored if empty, override "rgbd_image_topic" if set). Would be the output of a rtabmap_sync\'s rgbdx_sync node.'),
+
+ DeclareLaunchArgument(
+ 'expected_update_rate', default_value='15.0',
+ description='Expected lidar frame rate. Ideally, set it slightly higher than actual frame rate, like 15 Hz for 10 Hz lidar scans.'),
+
+ DeclareLaunchArgument(
+ 'voxel_size', default_value='0.1',
+ description='Voxel size (m) of the downsampled lidar point cloud. For indoor, set it between 0.1 and 0.3. For outdoor, set it to 0.5 or over.'),
+
+ DeclareLaunchArgument(
+ 'min_loop_closure_overlap', default_value='0.2',
+ description='Minimum scan overlap pourcentage to accept a loop closure.'),
+
+ DeclareLaunchArgument(
+ 'deskewing_slerp', default_value='true',
+ description='Use fast slerp interpolation between first and last stamps of the scan for deskewing. It would less accruate than requesting TF for every points, but a lot faster. Enable this if the delay of the deskewed scan is significant larger than the original scan.'),
+
+ DeclareLaunchArgument(
+ 'qos', default_value='1',
+ description='Quality of Service: 0=system default, 1=reliable, 2=best effort.'),
+
+ OpaqueFunction(function=launch_setup),
+ ])
+
+
diff --git a/rtabmap_examples/launch/lidar3d_assemble.launch.py b/rtabmap_examples/launch/lidar3d_assemble.launch.py
new file mode 100644
index 00000000..9c217640
--- /dev/null
+++ b/rtabmap_examples/launch/lidar3d_assemble.launch.py
@@ -0,0 +1,273 @@
+# Description:
+# In this example, we will record ALL lidar scans. An IMU or low latency odometry is required for this example.
+#
+# Example:
+# Launch your lidar sensor:
+# $ ros2 launch velodyne_driver velodyne_driver_node-VLP16-launch.py
+# $ ros2 launch velodyne_pointcloud velodyne_transform_node-VLP16-launch.py
+#
+# Launch your IMU sensor, make sure TF between lidar/base frame and imu is already calibrated.
+# In this example, we assume the imu topic has
+# already the orientation estimated, if not, you can launch
+# imu_filter_madgwick_node (with use_mag:=false publish_tf:=false)
+# and set imu_topic to output topic of the filter.
+#
+# If a camera is used, make sure TF between lidar/base frame and camera is
+# already calibrated. To provide image data to this example, you should use
+# rtabmap_sync's rgbd_sync or stereo_sync node.
+#
+# Launch the example by adjusting the lidar topic, imu topic and base frame:
+# $ ros2 launch rtabmap_examples lidar3d.launch.py lidar_topic:=/velodyne_points imu_topic:=/imu/data frame_id:=velodyne
+
+from launch import LaunchDescription, LaunchContext
+from launch.actions import DeclareLaunchArgument, OpaqueFunction
+from launch.substitutions import LaunchConfiguration
+from launch_ros.actions import Node
+
+def launch_setup(context: LaunchContext, *args, **kwargs):
+
+ frame_id = LaunchConfiguration('frame_id')
+
+ external_odom_frame_id = LaunchConfiguration('external_odom_frame_id').perform(context)
+
+ fixed_frame_from_imu = False
+ fixed_frame_id = LaunchConfiguration('fixed_frame_id').perform(context)
+ if not fixed_frame_id:
+ if external_odom_frame_id:
+ fixed_frame_id = external_odom_frame_id
+ else:
+ fixed_frame_from_imu = True
+ fixed_frame_id = frame_id.perform(context) + "_stabilized"
+
+ imu_topic = LaunchConfiguration('imu_topic')
+
+ rgbd_image_topic = LaunchConfiguration('rgbd_image_topic')
+ rgbd_images_topic = LaunchConfiguration('rgbd_images_topic')
+ rgbd_image_used = rgbd_image_topic.perform(context) != '' or rgbd_images_topic.perform(context) != ''
+ rgbd_cameras = 0 if rgbd_images_topic.perform(context) != '' else 1
+
+ lidar_topic = LaunchConfiguration('lidar_topic')
+ lidar_topic_value = lidar_topic.perform(context)
+ lidar_topic_deskewed = lidar_topic_value + "/deskewed"
+
+ voxel_size = LaunchConfiguration('voxel_size')
+ voxel_size_value = float(voxel_size.perform(context))
+
+ use_sim_time = LaunchConfiguration('use_sim_time')
+
+ localization = LaunchConfiguration('localization').perform(context)
+ localization = localization == 'true' or localization == 'True'
+
+ deskewing_slerp = LaunchConfiguration('deskewing_slerp').perform(context)
+ deskewing_slerp = deskewing_slerp == 'true' or deskewing_slerp == 'True'
+
+ # Rule of thumb:
+ max_correspondence_distance = voxel_size_value * 10.0
+
+ shared_parameters = {
+ 'use_sim_time': use_sim_time,
+ 'frame_id': frame_id,
+ 'qos': LaunchConfiguration('qos'),
+ 'approx_sync': rgbd_image_used,
+ 'wait_for_transform': 0.2,
+ # RTAB-Map's internal parameters are strings:
+ 'Icp/PointToPlane': 'true',
+ 'Icp/Iterations': '10',
+ 'Icp/VoxelSize': str(voxel_size_value),
+ 'Icp/Epsilon': '0.001',
+ 'Icp/PointToPlaneK': '20',
+ 'Icp/PointToPlaneRadius': '0',
+ 'Icp/MaxTranslation': '3',
+ 'Icp/MaxCorrespondenceDistance': str(max_correspondence_distance),
+ 'Icp/Strategy': '1',
+ 'Icp/OutlierRatio': '0.7',
+ }
+
+ icp_odometry_parameters = {
+ 'expected_update_rate': LaunchConfiguration('expected_update_rate'),
+ 'wait_imu_to_init': True,
+ 'odom_frame_id': 'icp_odom',
+ 'guess_frame_id': fixed_frame_id,
+ # RTAB-Map's internal parameters are strings:
+ 'Odom/ScanKeyFrameThr': '0.4',
+ 'OdomF2M/ScanSubtractRadius': str(voxel_size_value),
+ 'OdomF2M/ScanMaxSize': '15000',
+ 'OdomF2M/BundleAdjustment': 'false',
+ 'Icp/CorrespondenceRatio': '0.01'
+ }
+
+ rtabmap_parameters = {
+ 'subscribe_depth': False,
+ 'subscribe_rgb': False,
+ 'subscribe_odom_info': not external_odom_frame_id,
+ 'subscribe_scan_cloud': True,
+ 'odom_frame_id': (external_odom_frame_id if external_odom_frame_id else ""),
+ 'odom_sensor_sync': True, # This will adjust camera position based on difference between lidar and camera stamps.
+ # RTAB-Map's internal parameters are strings:
+ 'Rtabmap/DetectionRate': '0', # indirectly set to 1 Hz by the assembling time below (1s)
+ 'RGBD/ProximityMaxGraphDepth': '0',
+ 'RGBD/ProximityPathMaxNeighbors': '1',
+ 'RGBD/AngularUpdate': '0.05',
+ 'RGBD/LinearUpdate': '0.05',
+ 'RGBD/CreateOccupancyGrid': 'false',
+ 'Mem/NotLinkedNodesKept': 'false',
+ 'Mem/STMSize': '30',
+ 'Reg/Strategy': '1',
+ 'Icp/CorrespondenceRatio': str(LaunchConfiguration('min_loop_closure_overlap').perform(context))
+ }
+
+ remappings = [('imu', imu_topic),
+ ('odom', 'icp_odom')]
+ if rgbd_image_used:
+ if rgbd_cameras == 1:
+ remappings.append(('rgbd_image', LaunchConfiguration('rgbd_image_topic')))
+ else:
+ remappings.append(('rgbd_images', LaunchConfiguration('rgbd_images_topic')))
+
+ arguments = []
+ if localization:
+ rtabmap_parameters['Mem/IncrementalMemory'] = 'False'
+ rtabmap_parameters['Mem/InitWMWithAllNodes'] = 'True'
+ else:
+ arguments.append('-d') # This will delete the previous database (~/.ros/rtabmap.db)
+
+ if external_odom_frame_id:
+ viz_topic = lidar_topic_deskewed
+ else:
+ viz_topic = 'odom_filtered_input_scan'
+
+ nodes = [
+ # Lidar deskewing
+ Node(
+ package='rtabmap_util', executable='lidar_deskewing', output='screen',
+ parameters=[{
+ 'use_sim_time': use_sim_time,
+ 'fixed_frame_id': fixed_frame_id,
+ 'wait_for_transform': 0.2,
+ 'slerp': deskewing_slerp}],
+ remappings=[
+ ('input_cloud', lidar_topic)
+ ]),
+
+ # Assemble deskewed scans based on icp odometry
+ Node(
+ package='rtabmap_util', executable='point_cloud_assembler', output='screen',
+ parameters=[{
+ 'use_sim_time': use_sim_time,
+ 'assembling_time': LaunchConfiguration('assembling_time'),
+ 'fixed_frame_id': (external_odom_frame_id if external_odom_frame_id else "")}], # This will make the node subscribing to icp odometry topic "icp_odom"
+ remappings=[('cloud', lidar_topic_deskewed),
+ ('odom', 'icp_odom')]),
+
+ # Update the map
+ Node(
+ package='rtabmap_slam', executable='rtabmap', output='screen',
+ parameters=[shared_parameters, rtabmap_parameters,
+ {'subscribe_rgbd': rgbd_image_used,
+ 'rgbd_cameras': rgbd_cameras,
+ 'topic_queue_size': 40,
+ 'sync_queue_size': 40,}],
+ remappings=remappings + [('scan_cloud', 'assembled_cloud'), ('gps/fix', LaunchConfiguration('gps_topic'))],
+ arguments=arguments),
+
+ # Just for visualization
+ Node(
+ package='rtabmap_viz', executable='rtabmap_viz', output='screen',
+ parameters=[shared_parameters, rtabmap_parameters],
+ remappings=remappings + [('scan_cloud', viz_topic)])
+ ]
+
+ if not external_odom_frame_id:
+ # Lidar odometry
+ nodes.append(
+ Node(
+ package='rtabmap_odom', executable='icp_odometry', output='screen',
+ parameters=[shared_parameters, icp_odometry_parameters],
+ remappings=remappings + [('scan_cloud', lidar_topic_deskewed)]))
+
+ if fixed_frame_from_imu:
+ # Create a stabilized base frame based on imu for lidar deskewing
+ nodes.append(
+ Node(
+ package='rtabmap_util', executable='imu_to_tf', output='screen',
+ parameters=[{
+ 'use_sim_time': use_sim_time,
+ 'fixed_frame_id': fixed_frame_id,
+ 'base_frame_id': frame_id,
+ 'wait_for_transform_duration': 0.001}],
+ remappings=[('imu/data', imu_topic)]))
+
+ return nodes
+
+def generate_launch_description():
+ return LaunchDescription([
+
+ # Launch arguments
+ DeclareLaunchArgument(
+ 'use_sim_time', default_value='false',
+ description='Use simulated clock.'),
+
+ DeclareLaunchArgument(
+ 'frame_id', default_value='velodyne',
+ description='Base frame of the robot.'),
+
+ DeclareLaunchArgument(
+ 'fixed_frame_id', default_value='',
+ description='Fixed frame used for lidar deskewing. If not set, we will generate one from IMU or external_odom_frame_id if not null.'),
+
+ DeclareLaunchArgument(
+ 'external_odom_frame_id', default_value='',
+ description='Provide external odometry with TF, disabling icp_odometry.'),
+
+ DeclareLaunchArgument(
+ 'localization', default_value='false',
+ description='Localization mode.'),
+
+ DeclareLaunchArgument(
+ 'lidar_topic', default_value='/velodyne_points',
+ description='Name of the lidar PointCloud2 topic.'),
+
+ DeclareLaunchArgument(
+ 'imu_topic', default_value='/imu/data',
+ description='Name of an IMU topic.'),
+
+ DeclareLaunchArgument(
+ 'gps_topic', default_value='/gps/fix',
+ description='Name of a GPS topic.'),
+
+ DeclareLaunchArgument(
+ 'rgbd_image_topic', default_value='',
+ description='RGBD image topic (ignored if empty). Would be the output of a rtabmap_sync\'s rgbd_sync, stereo_sync or rgb_sync node.'),
+
+ DeclareLaunchArgument(
+ 'rgbd_images_topic', default_value='',
+ description='RGBD images topic (ignored if empty, override "rgbd_image_topic" if set). Would be the output of a rtabmap_sync\'s rgbdx_sync node.'),
+
+ DeclareLaunchArgument(
+ 'voxel_size', default_value='0.1',
+ description='Voxel size (m) of the downsampled lidar point cloud. For indoor, set it between 0.1 and 0.3. For outdoor, set it to 0.5 or over.'),
+
+ DeclareLaunchArgument(
+ 'min_loop_closure_overlap', default_value='0.2',
+ description='Minimum scan overlap pourcentage to accept a loop closure.'),
+
+ DeclareLaunchArgument(
+ 'expected_update_rate', default_value='15.0',
+ description='Expected lidar frame rate. Ideally, set it slightly higher than actual frame rate, like 15 Hz for 10 Hz lidar scans.'),
+
+ DeclareLaunchArgument(
+ 'assembling_time', default_value='1.0',
+ description='How much time (sec) we assemble lidar scans before sending them to mapping node.'),
+
+ DeclareLaunchArgument(
+ 'deskewing_slerp', default_value='true',
+ description='Use fast slerp interpolation between first and last stamps of the scan for deskewing. It would less accruate than requesting TF for every points, but a lot faster. Enable this if the delay of the deskewed scan is significant larger than the original scan.'),
+
+ DeclareLaunchArgument(
+ 'qos', default_value='1',
+ description='Quality of Service: 0=system default, 1=reliable, 2=best effort.'),
+
+ OpaqueFunction(function=launch_setup),
+ ])
+
+
diff --git a/rtabmap_examples/launch/lidar3d_assemble_x2.launch.py b/rtabmap_examples/launch/lidar3d_assemble_x2.launch.py
new file mode 100644
index 00000000..23da663f
--- /dev/null
+++ b/rtabmap_examples/launch/lidar3d_assemble_x2.launch.py
@@ -0,0 +1,305 @@
+# Description:
+# In this example, we will record ALL lidar scans from 2 lidars. An IMU or low latency odometry is required for this example.
+#
+# Example:
+# Launch your lidar sensors
+# In this example, we assume the lidar topics have a frame_id linked to same parent (e.g., base_link) and
+# the extrinsics are known (URDF) and/or already calibrated.
+#
+# Launch your IMU sensor, make sure TF between lidar/base frame and imu is already calibrated.
+# In this example, we assume the imu topic has
+# already the orientation estimated, if not, you can launch
+# imu_filter_madgwick_node (with use_mag:=false publish_tf:=false)
+# and set imu_topic to output topic of the filter.
+#
+# If a camera is used, make sure TF between lidar/base frame and camera is
+# already calibrated. To provide image data to this example, you should use
+# rtabmap_sync's rgbd_sync or stereo_sync node.
+#
+# Launch the example by adjusting the lidar topics, imu topic and base frame:
+# $ ros2 launch rtabmap_examples lidar3d.launch.py lidar1_topic:=/lidar1/velodyne_points lidar2_topic:=/lidar1/velodyne_points imu_topic:=/imu/data frame_id:=base_link
+
+from launch import LaunchDescription, LaunchContext
+from launch.actions import DeclareLaunchArgument, OpaqueFunction
+from launch.substitutions import LaunchConfiguration
+from launch_ros.actions import Node
+
+def launch_setup(context: LaunchContext, *args, **kwargs):
+
+ frame_id = LaunchConfiguration('frame_id')
+
+ external_odom_frame_id = LaunchConfiguration('external_odom_frame_id').perform(context)
+
+ fixed_frame_from_imu = False
+ fixed_frame_id = LaunchConfiguration('fixed_frame_id').perform(context)
+ if not fixed_frame_id:
+ if external_odom_frame_id:
+ fixed_frame_id = external_odom_frame_id
+ else:
+ fixed_frame_from_imu = True
+ fixed_frame_id = frame_id.perform(context) + "_stabilized"
+
+ imu_topic = LaunchConfiguration('imu_topic')
+
+ rgbd_image_topic = LaunchConfiguration('rgbd_image_topic')
+ rgbd_images_topic = LaunchConfiguration('rgbd_images_topic')
+ rgbd_image_used = rgbd_image_topic.perform(context) != '' or rgbd_images_topic.perform(context) != ''
+ rgbd_cameras = 0 if rgbd_images_topic.perform(context) != '' else 1
+
+ lidar1_topic = LaunchConfiguration('lidar1_topic')
+ lidar1_topic_value = lidar1_topic.perform(context)
+ lidar1_topic_deskewed = lidar1_topic_value + "/deskewed"
+
+ lidar2_topic = LaunchConfiguration('lidar2_topic')
+ lidar2_topic_value = lidar2_topic.perform(context)
+ lidar2_topic_deskewed = lidar2_topic_value + "/deskewed"
+
+ voxel_size = LaunchConfiguration('voxel_size')
+ voxel_size_value = float(voxel_size.perform(context))
+
+ use_sim_time = LaunchConfiguration('use_sim_time')
+
+ localization = LaunchConfiguration('localization').perform(context)
+ localization = localization == 'true' or localization == 'True'
+
+ deskewing_slerp = LaunchConfiguration('deskewing_slerp').perform(context)
+ deskewing_slerp = deskewing_slerp == 'true' or deskewing_slerp == 'True'
+
+ # Rule of thumb:
+ max_correspondence_distance = voxel_size_value * 10.0
+
+ shared_parameters = {
+ 'use_sim_time': use_sim_time,
+ 'frame_id': frame_id,
+ 'qos': LaunchConfiguration('qos'),
+ 'approx_sync': rgbd_image_used,
+ 'wait_for_transform': 0.2,
+ # RTAB-Map's internal parameters are strings:
+ 'Icp/PointToPlane': 'true',
+ 'Icp/Iterations': '10',
+ 'Icp/VoxelSize': str(voxel_size_value),
+ 'Icp/Epsilon': '0.001',
+ 'Icp/PointToPlaneK': '20',
+ 'Icp/PointToPlaneRadius': '0',
+ 'Icp/MaxTranslation': '3',
+ 'Icp/MaxCorrespondenceDistance': str(max_correspondence_distance),
+ 'Icp/Strategy': '1',
+ 'Icp/OutlierRatio': '0.7',
+ }
+
+ icp_odometry_parameters = {
+ 'expected_update_rate': LaunchConfiguration('expected_update_rate'),
+ 'wait_imu_to_init': True,
+ 'odom_frame_id': 'icp_odom',
+ 'guess_frame_id': fixed_frame_id,
+ # RTAB-Map's internal parameters are strings:
+ 'Odom/ScanKeyFrameThr': '0.4',
+ 'OdomF2M/ScanSubtractRadius': str(voxel_size_value),
+ 'OdomF2M/ScanMaxSize': '15000',
+ 'OdomF2M/BundleAdjustment': 'false',
+ 'Icp/CorrespondenceRatio': '0.01'
+ }
+
+ rtabmap_parameters = {
+ 'subscribe_depth': False,
+ 'subscribe_rgb': False,
+ 'subscribe_odom_info': not external_odom_frame_id,
+ 'subscribe_scan_cloud': True,
+ 'odom_frame_id': (external_odom_frame_id if external_odom_frame_id else ""),
+ 'odom_sensor_sync': True, # This will adjust camera position based on difference between lidar and camera stamps.
+ # RTAB-Map's internal parameters are strings:
+ 'Rtabmap/DetectionRate': '0', # indirectly set to 1 Hz by the assembling time below (1s)
+ 'RGBD/ProximityMaxGraphDepth': '0',
+ 'RGBD/ProximityPathMaxNeighbors': '1',
+ 'RGBD/AngularUpdate': '0.05',
+ 'RGBD/LinearUpdate': '0.05',
+ 'RGBD/CreateOccupancyGrid': 'false',
+ 'Mem/NotLinkedNodesKept': 'false',
+ 'Mem/STMSize': '30',
+ 'Reg/Strategy': '1',
+ 'Icp/CorrespondenceRatio': str(LaunchConfiguration('min_loop_closure_overlap').perform(context))
+ }
+
+ remappings = [('imu', imu_topic),
+ ('odom', 'icp_odom')]
+ if rgbd_image_used:
+ if rgbd_cameras == 1:
+ remappings.append(('rgbd_image', LaunchConfiguration('rgbd_image_topic')))
+ else:
+ remappings.append(('rgbd_images', LaunchConfiguration('rgbd_images_topic')))
+
+ arguments = []
+ if localization:
+ rtabmap_parameters['Mem/IncrementalMemory'] = 'False'
+ rtabmap_parameters['Mem/InitWMWithAllNodes'] = 'True'
+ else:
+ arguments.append('-d') # This will delete the previous database (~/.ros/rtabmap.db)
+
+ if external_odom_frame_id:
+ viz_topic = "combined_cloud"
+ else:
+ viz_topic = 'odom_filtered_input_scan'
+
+ nodes = [
+ # Lidar1 deskewing
+ Node(
+ package='rtabmap_util', executable='lidar_deskewing', name="lidar1_deskewing", output='screen',
+ parameters=[{
+ 'use_sim_time': use_sim_time,
+ 'fixed_frame_id': fixed_frame_id,
+ 'wait_for_transform': 0.2,
+ 'slerp': deskewing_slerp}],
+ remappings=[
+ ('input_cloud', lidar1_topic)
+ ]),
+
+ # Lidar2 deskewing
+ Node(
+ package='rtabmap_util', executable='lidar_deskewing', name="lidar2_deskewing", output='screen',
+ parameters=[{
+ 'use_sim_time': use_sim_time,
+ 'fixed_frame_id': fixed_frame_id,
+ 'wait_for_transform': 0.2,
+ 'slerp': deskewing_slerp}],
+ remappings=[
+ ('input_cloud', lidar2_topic)
+ ]),
+
+ # Combine the two lidars in single point cloud
+ Node(
+ package='rtabmap_util', executable='point_cloud_aggregator', output='screen',
+ parameters=[{
+ 'use_sim_time': use_sim_time,
+ 'approx_sync': True,
+ 'fixed_frame_id': fixed_frame_id,
+ 'count': 2}],
+ remappings=[
+ ('cloud1', lidar1_topic_deskewed),
+ ('cloud2', lidar2_topic_deskewed)]),
+
+ # Assemble combined deskewed scans based on icp odometry
+ Node(
+ package='rtabmap_util', executable='point_cloud_assembler', output='screen',
+ parameters=[{
+ 'use_sim_time': use_sim_time,
+ 'assembling_time': LaunchConfiguration('assembling_time'),
+ 'fixed_frame_id': (external_odom_frame_id if external_odom_frame_id else "")}], # This will make the node subscribing to icp odometry topic "icp_odom"
+ remappings=[('cloud', "combined_cloud"),
+ ('odom', 'icp_odom')]),
+
+ # Update the map
+ Node(
+ package='rtabmap_slam', executable='rtabmap', output='screen',
+ parameters=[shared_parameters, rtabmap_parameters,
+ {'subscribe_rgbd': rgbd_image_used,
+ 'rgbd_cameras': rgbd_cameras,
+ 'topic_queue_size': 40,
+ 'sync_queue_size': 40,}],
+ remappings=remappings + [('scan_cloud', 'assembled_cloud'), ('gps/fix', LaunchConfiguration('gps_topic'))],
+ arguments=arguments),
+
+ # Just for visualization
+ Node(
+ package='rtabmap_viz', executable='rtabmap_viz', output='screen',
+ parameters=[shared_parameters, rtabmap_parameters],
+ remappings=remappings + [('scan_cloud', viz_topic)])
+ ]
+
+ if not external_odom_frame_id:
+ # Lidar odometry
+ nodes.append(
+ Node(
+ package='rtabmap_odom', executable='icp_odometry', output='screen',
+ parameters=[shared_parameters, icp_odometry_parameters],
+ remappings=remappings + [('scan_cloud', "combined_cloud")]))
+
+ if fixed_frame_from_imu:
+ # Create a stabilized base frame based on imu for lidar deskewing
+ nodes.append(
+ Node(
+ package='rtabmap_util', executable='imu_to_tf', output='screen',
+ parameters=[{
+ 'use_sim_time': use_sim_time,
+ 'fixed_frame_id': fixed_frame_id,
+ 'base_frame_id': frame_id,
+ 'wait_for_transform_duration': 0.001}],
+ remappings=[('imu/data', imu_topic)]))
+
+ return nodes
+
+def generate_launch_description():
+ return LaunchDescription([
+
+ # Launch arguments
+ DeclareLaunchArgument(
+ 'use_sim_time', default_value='false',
+ description='Use simulated clock.'),
+
+ DeclareLaunchArgument(
+ 'frame_id', default_value='velodyne',
+ description='Base frame of the robot.'),
+
+ DeclareLaunchArgument(
+ 'fixed_frame_id', default_value='',
+ description='Fixed frame used for lidar deskewing. If not set, we will generate one from IMU or external_odom_frame_id if not null.'),
+
+ DeclareLaunchArgument(
+ 'external_odom_frame_id', default_value='',
+ description='Provide external odometry with TF, disabling icp_odometry.'),
+
+ DeclareLaunchArgument(
+ 'localization', default_value='false',
+ description='Localization mode.'),
+
+ DeclareLaunchArgument(
+ 'lidar1_topic', default_value='/lidar1/velodyne_points',
+ description='Name of the lidar1\'s PointCloud2 topic.'),
+
+ DeclareLaunchArgument(
+ 'lidar2_topic', default_value='/lidar2/velodyne_points',
+ description='Name of the lidar2\'s PointCloud2 topic.'),
+
+ DeclareLaunchArgument(
+ 'imu_topic', default_value='/imu/data',
+ description='Name of an IMU topic.'),
+
+ DeclareLaunchArgument(
+ 'gps_topic', default_value='/gps/fix',
+ description='Name of a GPS topic.'),
+
+ DeclareLaunchArgument(
+ 'rgbd_image_topic', default_value='',
+ description='RGBD image topic (ignored if empty). Would be the output of a rtabmap_sync\'s rgbd_sync, stereo_sync or rgb_sync node.'),
+
+ DeclareLaunchArgument(
+ 'rgbd_images_topic', default_value='',
+ description='RGBD images topic (ignored if empty, override "rgbd_image_topic" if set). Would be the output of a rtabmap_sync\'s rgbdx_sync node.'),
+
+ DeclareLaunchArgument(
+ 'voxel_size', default_value='0.1',
+ description='Voxel size (m) of the downsampled lidar point cloud. For indoor, set it between 0.1 and 0.3. For outdoor, set it to 0.5 or over.'),
+
+ DeclareLaunchArgument(
+ 'min_loop_closure_overlap', default_value='0.2',
+ description='Minimum scan overlap pourcentage to accept a loop closure.'),
+
+ DeclareLaunchArgument(
+ 'expected_update_rate', default_value='15.0',
+ description='Expected lidar frame rate. Ideally, set it slightly higher than actual frame rate, like 15 Hz for 10 Hz lidar scans.'),
+
+ DeclareLaunchArgument(
+ 'assembling_time', default_value='1.0',
+ description='How much time (sec) we assemble lidar scans before sending them to mapping node.'),
+
+ DeclareLaunchArgument(
+ 'deskewing_slerp', default_value='true',
+ description='Use fast slerp interpolation between first and last stamps of the scan for deskewing. It would less accruate than requesting TF for every points, but a lot faster. Enable this if the delay of the deskewed scan is significant larger than the original scan.'),
+
+ DeclareLaunchArgument(
+ 'qos', default_value='1',
+ description='Quality of Service: 0=system default, 1=reliable, 2=best effort.'),
+
+ OpaqueFunction(function=launch_setup),
+ ])
+
+
diff --git a/rtabmap_examples/launch/realsense_d400.launch.py b/rtabmap_examples/launch/realsense_d400.launch.py
index 79c1ffac..d2e47aff 100644
--- a/rtabmap_examples/launch/realsense_d400.launch.py
+++ b/rtabmap_examples/launch/realsense_d400.launch.py
@@ -2,16 +2,16 @@
# A realsense D400 series
# Install realsense2 ros2 package (make sure you have this patch: https://github.com/IntelRealSense/realsense-ros/issues/2564#issuecomment-1336288238)
# Example:
-# $ ros2 launch realsense2_camera rs_launch.py align_depth.enable:=true
-#
# $ ros2 launch rtabmap_examples realsense_d400.launch.py
-# OR
-# $ ros2 launch rtabmap_launch rtabmap.launch.py frame_id:=camera_link args:="-d" rgb_topic:=/camera/color/image_raw depth_topic:=/camera/aligned_depth_to_color/image_raw camera_info_topic:=/camera/color/camera_info approx_sync:=false
+
+import os
+
+from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
-from launch.actions import DeclareLaunchArgument, SetEnvironmentVariable
-from launch.substitutions import LaunchConfiguration
-from launch_ros.actions import Node
+from launch_ros.actions import Node, SetParameter
+from launch.actions import IncludeLaunchDescription
+from launch.launch_description_sources import PythonLaunchDescriptionSource
def generate_launch_description():
parameters=[{
@@ -27,7 +27,18 @@ def generate_launch_description():
return LaunchDescription([
- # Nodes to launch
+ # Make sure IR emitter is enabled
+ SetParameter(name='depth_module.emitter_enabled', value=1),
+
+ # Launch camera driver
+ IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([os.path.join(
+ get_package_share_directory('realsense2_camera'), 'launch'),
+ '/rs_launch.py']),
+ launch_arguments={'align_depth.enable': 'true',
+ 'rgb_camera.profile': '640x360x30'}.items(),
+ ),
+
Node(
package='rtabmap_odom', executable='rgbd_odometry', output='screen',
parameters=parameters,
diff --git a/rtabmap_examples/launch/realsense_d435i_color.launch.py b/rtabmap_examples/launch/realsense_d435i_color.launch.py
index 4839874b..ae4e2edb 100644
--- a/rtabmap_examples/launch/realsense_d435i_color.launch.py
+++ b/rtabmap_examples/launch/realsense_d435i_color.launch.py
@@ -2,14 +2,17 @@
# A realsense D435i
# Install realsense2 ros2 package (ros-$ROS_DISTRO-realsense2-camera)
# Example:
-# $ ros2 launch realsense2_camera rs_launch.py enable_gyro:=true enable_accel:=true unite_imu_method:=1 enable_sync:=true
-#
# $ ros2 launch rtabmap_examples realsense_d435i_color.launch.py
+import os
+
+from ament_index_python.packages import get_package_share_directory
+
from launch import LaunchDescription
-from launch.actions import DeclareLaunchArgument, SetEnvironmentVariable
+from launch_ros.actions import Node, SetParameter
+from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription
+from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import LaunchConfiguration
-from launch_ros.actions import Node
def generate_launch_description():
parameters=[{
@@ -23,11 +26,32 @@ def generate_launch_description():
('imu', '/imu/data'),
('rgb/image', '/camera/color/image_raw'),
('rgb/camera_info', '/camera/color/camera_info'),
- ('depth/image', '/camera/realigned_depth_to_color/image_raw')]
+ ('depth/image', '/camera/aligned_depth_to_color/image_raw')]
return LaunchDescription([
- # Nodes to launch
+ # Launch arguments
+ DeclareLaunchArgument(
+ 'unite_imu_method', default_value='2',
+ description='0-None, 1-copy, 2-linear_interpolation. Use unite_imu_method:="1" if imu topics stop being published.'),
+
+ # Make sure IR emitter is enabled
+ SetParameter(name='depth_module.emitter_enabled', value=1),
+
+ # Launch camera driver
+ IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([os.path.join(
+ get_package_share_directory('realsense2_camera'), 'launch'),
+ '/rs_launch.py']),
+ launch_arguments={'camera_namespace': '',
+ 'enable_gyro': 'true',
+ 'enable_accel': 'true',
+ 'unite_imu_method': LaunchConfiguration('unite_imu_method'),
+ 'align_depth.enable': 'true',
+ 'enable_sync': 'true',
+ 'rgb_camera.profile': '640x360x30'}.items(),
+ ),
+
Node(
package='rtabmap_odom', executable='rgbd_odometry', output='screen',
parameters=parameters,
@@ -43,26 +67,7 @@ def generate_launch_description():
package='rtabmap_viz', executable='rtabmap_viz', output='screen',
parameters=parameters,
remappings=remappings),
-
- # Because of this issue: https://github.com/IntelRealSense/realsense-ros/issues/2564
- # Generate point cloud from not aligned depth
- Node(
- package='rtabmap_util', executable='point_cloud_xyz', output='screen',
- parameters=[{'approx_sync':False}],
- remappings=[('depth/image', '/camera/depth/image_rect_raw'),
- ('depth/camera_info', '/camera/depth/camera_info'),
- ('cloud', '/camera/cloud_from_depth')]),
-
- # Generate aligned depth to color camera from the point cloud above
- Node(
- package='rtabmap_util', executable='pointcloud_to_depthimage', output='screen',
- parameters=[{ 'decimation':2,
- 'fixed_frame_id':'camera_link',
- 'fill_holes_size':1}],
- remappings=[('camera_info', '/camera/color/camera_info'),
- ('cloud', '/camera/cloud_from_depth'),
- ('image_raw', '/camera/realigned_depth_to_color/image_raw')]),
-
+
# Compute quaternion of the IMU
Node(
package='imu_filter_madgwick', executable='imu_filter_madgwick_node', output='screen',
@@ -70,9 +75,4 @@ def generate_launch_description():
'world_frame':'enu',
'publish_tf':False}],
remappings=[('imu/data_raw', '/camera/imu')]),
-
- # The IMU frame is missing in TF tree, add it:
- Node(
- package='tf2_ros', executable='static_transform_publisher', output='screen',
- arguments=['0', '0', '0', '0', '0', '0', 'camera_gyro_optical_frame', 'camera_imu_optical_frame']),
])
diff --git a/rtabmap_examples/launch/realsense_d435i_infra.launch.py b/rtabmap_examples/launch/realsense_d435i_infra.launch.py
index d4a2116f..c5284b2c 100644
--- a/rtabmap_examples/launch/realsense_d435i_infra.launch.py
+++ b/rtabmap_examples/launch/realsense_d435i_infra.launch.py
@@ -2,15 +2,18 @@
# A realsense D435i
# Install realsense2 ros2 package (ros-$ROS_DISTRO-realsense2-camera)
# Example:
-# $ ros2 launch realsense2_camera rs_launch.py enable_gyro:=true enable_accel:=true unite_imu_method:=1 enable_infra1:=true enable_infra2:=true enable_sync:=true
-# $ ros2 param set /camera/camera depth_module.emitter_enabled 0
-#
# $ ros2 launch rtabmap_examples realsense_d435i_infra.launch.py
+import os
+
+from ament_index_python.packages import get_package_share_directory
+
from launch import LaunchDescription
-from launch.actions import DeclareLaunchArgument, SetEnvironmentVariable
+from launch_ros.actions import Node, SetParameter
+from launch.actions import IncludeLaunchDescription
+from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription
+from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import LaunchConfiguration
-from launch_ros.actions import Node
def generate_launch_description():
parameters=[{
@@ -28,7 +31,28 @@ def generate_launch_description():
return LaunchDescription([
- # Nodes to launch
+ # Launch arguments
+ DeclareLaunchArgument(
+ 'unite_imu_method', default_value='2',
+ description='0-None, 1-copy, 2-linear_interpolation. Use unite_imu_method:="1" if imu topics stop being published.'),
+
+ #Hack to disable IR emitter
+ SetParameter(name='depth_module.emitter_enabled', value=0),
+
+ # Launch camera driver
+ IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([os.path.join(
+ get_package_share_directory('realsense2_camera'), 'launch'),
+ '/rs_launch.py']),
+ launch_arguments={'camera_namespace': '',
+ 'enable_gyro': 'true',
+ 'enable_accel': 'true',
+ 'unite_imu_method': LaunchConfiguration('unite_imu_method'),
+ 'enable_infra1': 'true',
+ 'enable_infra2': 'true',
+ 'enable_sync': 'true'}.items(),
+ ),
+
Node(
package='rtabmap_odom', executable='rgbd_odometry', output='screen',
parameters=parameters,
@@ -52,9 +76,4 @@ def generate_launch_description():
'world_frame':'enu',
'publish_tf':False}],
remappings=[('imu/data_raw', '/camera/imu')]),
-
- # The IMU frame is missing in TF tree, add it:
- Node(
- package='tf2_ros', executable='static_transform_publisher', output='screen',
- arguments=['0', '0', '0', '0', '0', '0', 'camera_gyro_optical_frame', 'camera_imu_optical_frame']),
])
diff --git a/rtabmap_examples/launch/realsense_d435i_stereo.launch.py b/rtabmap_examples/launch/realsense_d435i_stereo.launch.py
index 43f93a61..ccf865d3 100644
--- a/rtabmap_examples/launch/realsense_d435i_stereo.launch.py
+++ b/rtabmap_examples/launch/realsense_d435i_stereo.launch.py
@@ -2,15 +2,18 @@
# A realsense D435i
# Install realsense2 ros2 package (ros-$ROS_DISTRO-realsense2-camera)
# Example:
-# $ ros2 launch realsense2_camera rs_launch.py enable_gyro:=true enable_accel:=true unite_imu_method:=1 enable_infra1:=true enable_infra2:=true enable_sync:=true
-# $ ros2 param set /camera/camera depth_module.emitter_enabled 0
-#
# $ ros2 launch rtabmap_examples realsense_d435i_stereo.launch.py
+import os
+
+from ament_index_python.packages import get_package_share_directory
+
from launch import LaunchDescription
-from launch.actions import DeclareLaunchArgument, SetEnvironmentVariable
+from launch_ros.actions import Node, SetParameter
+from launch.actions import IncludeLaunchDescription
+from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription
+from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import LaunchConfiguration
-from launch_ros.actions import Node
def generate_launch_description():
parameters=[{
@@ -28,7 +31,28 @@ def generate_launch_description():
return LaunchDescription([
- # Nodes to launch
+ # Launch arguments
+ DeclareLaunchArgument(
+ 'unite_imu_method', default_value='2',
+ description='0-None, 1-copy, 2-linear_interpolation. Use unite_imu_method:="1" if imu topics stop being published.'),
+
+ #Hack to disable IR emitter
+ SetParameter(name='depth_module.emitter_enabled', value=0),
+
+ # Launch camera driver
+ IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([os.path.join(
+ get_package_share_directory('realsense2_camera'), 'launch'),
+ '/rs_launch.py']),
+ launch_arguments={'camera_namespace': '',
+ 'enable_gyro': 'true',
+ 'enable_accel': 'true',
+ 'unite_imu_method': LaunchConfiguration('unite_imu_method'),
+ 'enable_infra1': 'true',
+ 'enable_infra2': 'true',
+ 'enable_sync': 'true'}.items(),
+ ),
+
Node(
package='rtabmap_odom', executable='stereo_odometry', output='screen',
parameters=parameters,
@@ -52,9 +76,4 @@ def generate_launch_description():
'world_frame':'enu',
'publish_tf':False}],
remappings=[('imu/data_raw', '/camera/imu')]),
-
- # The IMU frame is missing in TF tree, add it:
- Node(
- package='tf2_ros', executable='static_transform_publisher', output='screen',
- arguments=['0', '0', '0', '0', '0', '0', 'camera_gyro_optical_frame', 'camera_imu_optical_frame']),
])
diff --git a/rtabmap_examples/launch/rgbdslam_datasets.launch.py b/rtabmap_examples/launch/rgbdslam_datasets.launch.py
index 9aab2edd..92e8e030 100644
--- a/rtabmap_examples/launch/rgbdslam_datasets.launch.py
+++ b/rtabmap_examples/launch/rgbdslam_datasets.launch.py
@@ -1,47 +1,72 @@
# Example to run rgbd datasets:
+#
# [ROS1] Prepare ROS1 rosbag for conversion to ROS2
# $ wget http://vision.in.tum.de/rgbd/dataset/freiburg3/rgbd_dataset_freiburg3_long_office_household.bag
# $ rosbag decompress rgbd_dataset_freiburg3_long_office_household.bag
+# $ wget https://gist.githubusercontent.com/matlabbe/897b775c38836ed8069a1397485ab024/raw/45e6ac01541973a17505000fc8c7ca399ae275eb/tum_rename_world_kinect_frame.py
+# $ python3 tum_rename_world_kinect_frame.py rgbd_dataset_freiburg3_long_office_household.bag
# $ wget https://raw.githubusercontent.com/srv/srv_tools/kinetic/bag_tools/scripts/change_frame_id.py
+#
# Edit change_frame_id.py, remove/comment lines beginning with "PKG" and "import roslib", change line "Exception, e" to "Exception"
# $ roscore
# $ python3 change_frame_id.py -o rgbd_dataset_freiburg3_long_office_household_frameid_fixed.bag -i rgbd_dataset_freiburg3_long_office_household.bag -f openni_rgb_optical_frame -t /camera/rgb/image_color
+#
# [ROS2]
# $ sudo pip install rosbags # See https://docs.openvins.com/dev-ros1-to-ros2.html
-# $ rosbags-convert rgbd_dataset_freiburg3_long_office_household_frameid_fixed.bag
-
+# $ rosbags-convert --src rgbd_dataset_freiburg3_long_office_household_frameid_fixed.bag --dst rgbd_dataset_freiburg3_long_office_household_frameid_fixed
+#
# $ ros2 launch rtabmap_examples rgbdslam_datasets.launch.py
# $ cd rgbd_dataset_freiburg3_long_office_household_frameid_fixed
# $ ros2 bag play rgbd_dataset_freiburg3_long_office_household_frameid_fixed.db3 --clock
-
+#
+# To get RMSE after the run:
+# $ rtabmap-report ~/.ros/rtabmap.db
from launch import LaunchDescription
-from launch.actions import DeclareLaunchArgument, SetEnvironmentVariable
-from launch.substitutions import LaunchConfiguration
from launch_ros.actions import Node
from launch_ros.actions import SetParameter
def generate_launch_description():
- parameters=[{
+ odom_parameters=[{
+ 'frame_id':'kinect',
+ # ground truth here is just used to align odometry with ground truth's first pose
+ 'ground_truth_frame_id':'world',
+ 'ground_truth_base_frame_id':'kinect_gt',
+ 'keep_color': True,
+ 'wait_for_transform': 0.5,
+ # RTAB-Map's parameters should all be string type:
+ 'Odom/Strategy':'0',
+ 'Odom/ResetCountdown':'15',
+ 'Odom/GuessSmoothingDelay':'0',
+ }]
+ slam_parameters=[{
'frame_id':'kinect',
- 'subscribe_depth':True,
+ # Record ground truth to compute RMSE
+ 'ground_truth_frame_id':'world',
+ 'ground_truth_base_frame_id':'kinect_gt',
+ 'subscribe_rgb':False,
+ 'subscribe_depth':False,
+ 'subscribe_rgbd':True,
'subscribe_odom_info':True,
# RTAB-Map's parameters should all be string type:
- 'Odom/Strategy':'0',
- 'Odom/ResetCountdown':'15',
- 'Odom/GuessSmoothingDelay':'0',
+ 'Mem/UseOdomFeatures': 'true',
'Rtabmap/StartNewMapOnLoopClosure':'true',
'RGBD/CreateOccupancyGrid':'false',
'Rtabmap/CreateIntermediateNodes':'true',
'RGBD/LinearUpdate':'0',
'RGBD/AngularUpdate':'0'}]
- remappings=[
+ odom_remappings=[
('rgb/image', '/camera/rgb/image_color'),
('rgb/camera_info', '/camera/rgb/camera_info'),
('depth/image', '/camera/depth/image')]
+
+ # We will use the output of odometry to avoid re-extracting
+ # the same features on slam side.
+ slam_remappings=[
+ ("rgbd_image", "odom_rgbd_image")]
return LaunchDescription([
@@ -52,19 +77,19 @@ def generate_launch_description():
# Nodes to launch
Node(
package='rtabmap_odom', executable='rgbd_odometry', output='screen',
- parameters=parameters,
- remappings=remappings),
+ parameters=odom_parameters,
+ remappings=odom_remappings),
Node(
package='rtabmap_slam', executable='rtabmap', output='screen',
- parameters=parameters,
- remappings=remappings,
+ parameters=slam_parameters,
+ remappings=slam_remappings,
arguments=['-d']),
Node(
package='rtabmap_viz', executable='rtabmap_viz', output='screen',
- parameters=parameters,
- remappings=remappings),
+ parameters=slam_parameters,
+ remappings=slam_remappings),
# /tf topic is missing in the converted ROS2 bag, create a fake tf
Node(
diff --git a/rtabmap_examples/launch/rtabmap_D405x2.launch.py b/rtabmap_examples/launch/rtabmap_D405x2.launch.py
index 9d1de478..7a8fda54 100644
--- a/rtabmap_examples/launch/rtabmap_D405x2.launch.py
+++ b/rtabmap_examples/launch/rtabmap_D405x2.launch.py
@@ -29,7 +29,7 @@ from ament_index_python.packages import get_package_share_directory
def generate_launch_description():
config_rviz = os.path.join(
- get_package_share_directory('rtabmap_examples'), 'launch', 'config', 'slam_D405x2_config.rviz')
+ get_package_share_directory('rtabmap_examples'), 'config', 'slam_D405x2_config.rviz')
rviz_node = launch_ros.actions.Node(
package='rviz2', executable='rviz2', output='screen',
diff --git a/rtabmap_examples/launch/rtabmap_D405x3.launch.py b/rtabmap_examples/launch/rtabmap_D405x3.launch.py
index 168bc40d..f2d6f61f 100644
--- a/rtabmap_examples/launch/rtabmap_D405x3.launch.py
+++ b/rtabmap_examples/launch/rtabmap_D405x3.launch.py
@@ -31,7 +31,7 @@ from ament_index_python.packages import get_package_share_directory
def generate_launch_description():
config_rviz = os.path.join(
- get_package_share_directory('rtabmap_examples'), 'launch', 'config', 'slam_D405x3_config.rviz')
+ get_package_share_directory('rtabmap_examples'), 'config', 'slam_D405x3_config.rviz')
rviz_node = launch_ros.actions.Node(
package='rviz2', executable='rviz2', output='screen',
diff --git a/rtabmap_examples/launch/vlp16.launch.py b/rtabmap_examples/launch/vlp16.launch.py
deleted file mode 100644
index dfc6927d..00000000
--- a/rtabmap_examples/launch/vlp16.launch.py
+++ /dev/null
@@ -1,126 +0,0 @@
-# Example:
-# $ ros2 launch velodyne_driver velodyne_driver_node-VLP16-launch.py
-# $ ros2 launch velodyne_pointcloud velodyne_transform_node-VLP16-launch.py
-#
-# SLAM:
-# $ ros2 launch rtabmap_examples vlp16.launch.py
-
-
-from launch import LaunchDescription
-from launch.actions import DeclareLaunchArgument
-from launch.substitutions import LaunchConfiguration
-from launch_ros.actions import Node
-
-def generate_launch_description():
-
- use_sim_time = LaunchConfiguration('use_sim_time')
- deskewing = LaunchConfiguration('deskewing')
-
- return LaunchDescription([
-
- # Launch arguments
- DeclareLaunchArgument(
- 'use_sim_time', default_value='false',
- description='Use simulation (Gazebo) clock if true'),
-
- DeclareLaunchArgument(
- 'deskewing', default_value='true',
- description='Enable lidar deskewing'),
-
- # Nodes to launch
- Node(
- package='rtabmap_odom', executable='icp_odometry', output='screen',
- parameters=[{
- 'frame_id':'velodyne',
- 'odom_frame_id':'odom',
- 'wait_for_transform':0.2,
- 'expected_update_rate':15.0,
- 'deskewing':deskewing,
- 'use_sim_time':use_sim_time,
- # RTAB-Map's internal parameters are strings:
- 'Icp/PointToPlane': 'true',
- 'Icp/Iterations': '10',
- 'Icp/VoxelSize': '0.1',
- 'Icp/Epsilon': '0.001',
- 'Icp/PointToPlaneK': '20',
- 'Icp/PointToPlaneRadius': '0',
- 'Icp/MaxTranslation': '2',
- 'Icp/MaxCorrespondenceDistance': '1',
- 'Icp/Strategy': '1',
- 'Icp/OutlierRatio': '0.7',
- 'Icp/CorrespondenceRatio': '0.01',
- 'Odom/ScanKeyFrameThr': '0.4',
- 'OdomF2M/ScanSubtractRadius': '0.1',
- 'OdomF2M/ScanMaxSize': '15000',
- 'OdomF2M/BundleAdjustment': 'false'
- }],
- remappings=[
- ('scan_cloud', '/velodyne_points')
- ]),
-
- Node(
- package='rtabmap_util', executable='point_cloud_assembler', output='screen',
- parameters=[{
- 'max_clouds':10,
- 'fixed_frame_id':'',
- 'use_sim_time':use_sim_time,
- }],
- remappings=[
- ('cloud', 'odom_filtered_input_scan')
- ]),
-
- Node(
- package='rtabmap_slam', executable='rtabmap', output='screen',
- parameters=[{
- 'frame_id':'velodyne',
- 'subscribe_depth':False,
- 'subscribe_rgb':False,
- 'subscribe_scan_cloud':True,
- 'approx_sync':False,
- 'wait_for_transform':0.2,
- 'use_sim_time':use_sim_time,
- # RTAB-Map's internal parameters are strings:
- 'RGBD/ProximityMaxGraphDepth': '0',
- 'RGBD/ProximityPathMaxNeighbors': '1',
- 'RGBD/AngularUpdate': '0.05',
- 'RGBD/LinearUpdate': '0.05',
- 'RGBD/CreateOccupancyGrid': 'false',
- 'Mem/NotLinkedNodesKept': 'false',
- 'Mem/STMSize': '30',
- 'Mem/LaserScanNormalK': '20',
- 'Reg/Strategy': '1',
- 'Icp/VoxelSize': '0.1',
- 'Icp/PointToPlaneK': '20',
- 'Icp/PointToPlaneRadius': '0',
- 'Icp/PointToPlane': 'true',
- 'Icp/Iterations': '10',
- 'Icp/Epsilon': '0.001',
- 'Icp/MaxTranslation': '3',
- 'Icp/MaxCorrespondenceDistance': '1',
- 'Icp/Strategy': '1',
- 'Icp/OutlierRatio': '0.7',
- 'Icp/CorrespondenceRatio': '0.2'
- }],
- remappings=[
- ('scan_cloud', 'assembled_cloud')
- ],
- arguments=[
- '-d' # This will delete the previous database (~/.ros/rtabmap.db)
- ]),
-
- Node(
- package='rtabmap_viz', executable='rtabmap_viz', output='screen',
- parameters=[{
- 'frame_id':'velodyne',
- 'odom_frame_id':'odom',
- 'subscribe_odom_info':True,
- 'subscribe_scan_cloud':True,
- 'approx_sync':False,
- 'use_sim_time':use_sim_time,
- }],
- remappings=[
- ('scan_cloud', 'odom_filtered_input_scan')
- ]),
- ])
-
-
diff --git a/rtabmap_examples/launch/vlp16_zed.launch.py b/rtabmap_examples/launch/vlp16_zed.launch.py
new file mode 100644
index 00000000..67267e1f
--- /dev/null
+++ b/rtabmap_examples/launch/vlp16_zed.launch.py
@@ -0,0 +1,121 @@
+# Example using zed odometry for lidar deskewing:
+# $ ros2 launch rtabmap_examples vlp16_zed.launch.py camera_model:=zed2i
+#
+# To use only zed's imu for deskewing:
+# $ ros2 launch rtabmap_examples vlp16_zed.launch.py camera_model:=zed2i use_zed_odometry:=false
+#
+
+
+import os
+
+from ament_index_python.packages import get_package_share_directory
+
+from launch import LaunchDescription, LaunchContext
+from launch.actions import DeclareLaunchArgument, OpaqueFunction
+from launch.substitutions import LaunchConfiguration
+from launch_ros.actions import Node
+from launch.actions import IncludeLaunchDescription
+from launch.launch_description_sources import PythonLaunchDescriptionSource
+
+import tempfile
+
+def launch_setup(context: LaunchContext, *args, **kwargs):
+
+ assemble = LaunchConfiguration('assemble').perform(context)
+ assemble = assemble == 'true' or assemble == 'True'
+
+ lidar3d_launch_file = 'lidar3d.launch.py'
+ if assemble:
+ lidar3d_launch_file = 'lidar3d_assemble.launch.py'
+
+ use_zed_odometry = LaunchConfiguration('use_zed_odometry').perform(context)
+ use_zed_odometry = use_zed_odometry == 'true' or use_zed_odometry == 'True'
+
+ fixed_frame_id = ''
+ if use_zed_odometry:
+ fixed_frame_id = 'odom'
+
+ # Hack to override grab_resolution parameter without changing any files
+ with tempfile.NamedTemporaryFile(mode='w+t', delete=False) as zed_override_file:
+ zed_override_file.write("---\n"+
+ "/**:\n"+
+ " ros__parameters:\n"+
+ " general:\n"+
+ " grab_resolution: 'VGA'")
+
+ return [
+ IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([os.path.join(
+ get_package_share_directory('velodyne_driver'), 'launch'),
+ '/velodyne_driver_node-VLP16-launch.py']),
+ ),
+ IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([os.path.join(
+ get_package_share_directory('velodyne_pointcloud'), 'launch'),
+ '/velodyne_transform_node-VLP16-launch.py']),
+ ),
+
+ # Launch camera driver
+ IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([os.path.join(
+ get_package_share_directory('zed_wrapper'), 'launch'),
+ '/zed_camera.launch.py']),
+ launch_arguments={'camera_model': LaunchConfiguration('camera_model'),
+ 'ros_params_override_path': zed_override_file.name,
+ 'publish_tf': LaunchConfiguration('use_zed_odometry'), # publish VIO frame
+ 'publish_map_tf': 'false'}.items(),
+ ),
+
+ # Static transform between zed and velodyne frame (zed will be our base frame because VIO is already linked to it)
+ Node(package='tf2_ros', executable='static_transform_publisher', arguments=["0", "0", "-0.05", "0", "0", "0", "zed_camera_link", "velodyne"]),
+
+ # Sync rgb/depth/camera_info together
+ Node(
+ package='rtabmap_sync', executable='rgbd_sync', output='screen',
+ parameters=[{'approx_sync': False}],
+ remappings=[('rgb/image', '/zed/zed_node/rgb/image_rect_color'),
+ ('rgb/camera_info', '/zed/zed_node/rgb/camera_info'),
+ ('depth/image', '/zed/zed_node/depth/depth_registered')]),
+
+ IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([os.path.join(
+ get_package_share_directory('rtabmap_examples'), 'launch'),
+ '/', lidar3d_launch_file]),
+ launch_arguments={'voxel_size': LaunchConfiguration('voxel_size'),
+ 'localization': LaunchConfiguration('localization'),
+ 'frame_id': 'zed_camera_link',
+ 'lidar_topic': 'velodyne_points',
+ 'imu_topic': '/zed/zed_node/imu/data',
+ 'rgbd_image_topic': 'rgbd_image',
+ 'fixed_frame_id': fixed_frame_id}.items()),
+ ]
+
+def generate_launch_description():
+ return LaunchDescription([
+ # Launch arguments
+ DeclareLaunchArgument(
+ 'camera_model', default_value='',
+ description="[REQUIRED] The model of the camera. Using a wrong camera model can disable camera features. Valid choices are: ['zed', 'zedm', 'zed2', 'zed2i', 'zedx', 'zedxm', 'virtual']"),
+
+ DeclareLaunchArgument(
+ 'use_zed_odometry', default_value='true',
+ description='Use ZED\'s odometry for deskewing.'),
+
+ DeclareLaunchArgument(
+ 'qos', default_value='1',
+ description='Quality of Service: 0=system default, 1=reliable, 2=best effort'),
+
+ DeclareLaunchArgument(
+ 'localization', default_value='false',
+ description='Localization mode.'),
+
+ DeclareLaunchArgument(
+ 'voxel_size', default_value='0.1',
+ description='Voxel size (m) of the downsampled lidar point cloud. For indoor, set it between 0.1 and 0.3. For outdoor, set it to 0.5 or over.'),
+
+ DeclareLaunchArgument(
+ 'assemble', default_value='false',
+ description='Assemble ALL lidar scans.'),
+
+ OpaqueFunction(function=launch_setup),
+ ])
\ No newline at end of file
diff --git a/rtabmap_examples/launch/zed.launch.py b/rtabmap_examples/launch/zed.launch.py
new file mode 100644
index 00000000..80ce57a5
--- /dev/null
+++ b/rtabmap_examples/launch/zed.launch.py
@@ -0,0 +1,101 @@
+# Requirements:
+# A ZED camera
+# Install zed ros2 wrapper package (https://github.com/stereolabs/zed-ros2-wrapper)
+# Example:
+# $ ros2 launch rtabmap_examples zed.launch.py camera_model:=zed2i
+
+import os
+
+from ament_index_python.packages import get_package_share_directory
+
+from launch import LaunchDescription, LaunchContext
+from launch.actions import DeclareLaunchArgument
+from launch_ros.actions import Node
+from launch.actions import IncludeLaunchDescription, OpaqueFunction
+from launch.substitutions import LaunchConfiguration
+from launch.launch_description_sources import PythonLaunchDescriptionSource
+from launch.conditions import UnlessCondition
+
+import tempfile
+
+parameters = []
+remappings = []
+
+def launch_setup(context: LaunchContext, *args, **kwargs):
+
+ # Hack to override grab_resolution parameter without changing any files
+ with tempfile.NamedTemporaryFile(mode='w+t', delete=False) as zed_override_file:
+ zed_override_file.write("---\n"+
+ "/**:\n"+
+ " ros__parameters:\n"+
+ " general:\n"+
+ " grab_resolution: 'VGA'")
+
+ parameters=[{'frame_id':'zed_camera_link',
+ 'subscribe_rgbd':True,
+ 'approx_sync':False,
+ 'wait_imu_to_init':True}]
+
+ remappings=[('imu', '/zed/zed_node/imu/data')]
+
+ if LaunchConfiguration('use_zed_odometry').perform(context) in ["True", "true"]:
+ remappings.append(('odom', '/zed/zed_node/odom'))
+ else:
+ parameters.append({'subscribe_odom_info': True})
+
+ return [
+ # Launch camera driver
+ IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([os.path.join(
+ get_package_share_directory('zed_wrapper'), 'launch'),
+ '/zed_camera.launch.py']),
+ launch_arguments={'camera_model': LaunchConfiguration('camera_model'),
+ 'ros_params_override_path': zed_override_file.name,
+ 'publish_tf': LaunchConfiguration('use_zed_odometry'),
+ 'publish_map_tf': 'false'}.items(),
+ ),
+
+ # Sync rgb/depth/camera_info together
+ Node(
+ package='rtabmap_sync', executable='rgbd_sync', output='screen',
+ parameters=parameters,
+ remappings=[('rgb/image', '/zed/zed_node/rgb/image_rect_color'),
+ ('rgb/camera_info', '/zed/zed_node/rgb/camera_info'),
+ ('depth/image', '/zed/zed_node/depth/depth_registered')]),
+
+ # Visual odometry
+ Node(
+ package='rtabmap_odom', executable='rgbd_odometry', output='screen',
+ condition=UnlessCondition(LaunchConfiguration('use_zed_odometry')),
+ parameters=parameters,
+ remappings=remappings,),
+
+ # VSLAM
+ Node(
+ package='rtabmap_slam', executable='rtabmap', output='screen',
+ parameters=parameters,
+ remappings=remappings,
+ arguments=['-d']),
+
+ # Visualization
+ Node(
+ package='rtabmap_viz', executable='rtabmap_viz', output='screen',
+ parameters=parameters,
+ remappings=remappings)
+ ]
+
+
+def generate_launch_description():
+ return LaunchDescription([
+
+ # Launch arguments
+ DeclareLaunchArgument(
+ 'use_zed_odometry', default_value='false',
+ description='Use zed\'s computed odometry instead of using rtabmap\'s odometry.'),
+
+ DeclareLaunchArgument(
+ 'camera_model', default_value='',
+ description="[REQUIRED] The model of the camera. Using a wrong camera model can disable camera features. Valid choices are: ['zed', 'zedm', 'zed2', 'zed2i', 'zedx', 'zedxm', 'virtual']"),
+
+ OpaqueFunction(function=launch_setup)
+ ])
diff --git a/rtabmap_examples/package.xml b/rtabmap_examples/package.xml
index 0dba0037..bb80df0f 100644
--- a/rtabmap_examples/package.xml
+++ b/rtabmap_examples/package.xml
@@ -2,7 +2,7 @@
rtabmap_examples
- 0.21.5
+ 0.22.0
RTAB-Map's example launch files.
Mathieu Labbe
Mathieu Labbe
diff --git a/rtabmap_launch/README.md b/rtabmap_launch/README.md
new file mode 100644
index 00000000..449543b6
--- /dev/null
+++ b/rtabmap_launch/README.md
@@ -0,0 +1,40 @@
+
+
+# Usage
+
+`rtabmap.launch` from ros1 has been ported to ROS2 as `rtabmap.launch.py` with same arguments. If you see [ROS1 examples](http://wiki.ros.org/rtabmap_ros/Tutorials/HandHeldMapping) like this:
+
+```bash
+roslaunch zed_wrapper zed_no_tf.launch
+
+roslaunch rtabmap_ros rtabmap.launch \
+ rtabmap_args:="--delete_db_on_start" \
+ rgb_topic:=/zed/zed_node/rgb/image_rect_color \
+ depth_topic:=/zed/zed_node/depth/depth_registered \
+ camera_info_topic:=/zed/zed_node/rgb/camera_info \
+ frame_id:=base_link \
+ approx_sync:=false \
+ wait_imu_to_init:=true \
+ imu_topic:=/zed_node/imu/data
+
+```
+
+The ROS2 equivalent is (using latest zed_wrapper launch file):
+
+```bash
+ros2 launch zed_wrapper zed_camera.launch.py camera_model:=zed2i \
+ publish_tf:=false \
+ publish_map_tf:=false
+
+ros2 launch rtabmap_launch rtabmap.launch.py \
+ rtabmap_args:="--delete_db_on_start" \
+ rgb_topic:=/zed/zed_node/rgb/image_rect_color \
+ depth_topic:=/zed/zed_node/depth/depth_registered \
+ camera_info_topic:=/zed/zed_node/rgb/camera_info \
+ frame_id:=zed_camera_link \
+ approx_sync:=false \
+ wait_imu_to_init:=true \
+ imu_topic:=/zed/zed_node/imu/data \
+ rviz:=true
+```
+
diff --git a/rtabmap_launch/launch/rtabmap.launch.py b/rtabmap_launch/launch/rtabmap.launch.py
index 50db0297..26e4d549 100644
--- a/rtabmap_launch/launch/rtabmap.launch.py
+++ b/rtabmap_launch/launch/rtabmap.launch.py
@@ -45,6 +45,7 @@ def launch_setup(context, *args, **kwargs):
DeclareLaunchArgument('depth', default_value=ConditionalText('false', 'true', IfCondition(PythonExpression(["'", LaunchConfiguration('stereo'), "' == 'true'"]))._predicate_func(context)), description=''),
DeclareLaunchArgument('subscribe_rgb', default_value=LaunchConfiguration('depth'), description=''),
DeclareLaunchArgument('args', default_value=LaunchConfiguration('rtabmap_args'), description='Can be used to pass RTAB-Map\'s parameters or other flags like --udebug and --delete_db_on_start/-d'),
+ DeclareLaunchArgument('sync_queue_size', default_value=LaunchConfiguration('queue_size'), description='Queue size of topic synchronizers.'),
DeclareLaunchArgument('qos_image', default_value=LaunchConfiguration('qos'), description='Specific QoS used for image input data: 0=system default, 1=Reliable, 2=Best Effort.'),
DeclareLaunchArgument('qos_camera_info', default_value=LaunchConfiguration('qos'), description='Specific QoS used for camera info input data: 0=system default, 1=Reliable, 2=Best Effort.'),
DeclareLaunchArgument('qos_scan', default_value=LaunchConfiguration('qos'), description='Specific QoS used for scan input data: 0=system default, 1=Reliable, 2=Best Effort.'),
@@ -53,6 +54,8 @@ def launch_setup(context, *args, **kwargs):
DeclareLaunchArgument('qos_imu', default_value=LaunchConfiguration('qos'), description='Specific QoS used for imu input data: 0=system default, 1=Reliable, 2=Best Effort.'),
DeclareLaunchArgument('qos_gps', default_value=LaunchConfiguration('qos'), description='Specific QoS used for gps input data: 0=system default, 1=Reliable, 2=Best Effort.'),
+ DeclareLaunchArgument('odom_log_level', default_value=LaunchConfiguration('log_level'), description='Specific ROS logger level for odometry node.'),
+
#These arguments should not be modified directly, see referred topics without "_relay" suffix above
DeclareLaunchArgument('rgb_topic_relay', default_value=ConditionalText(''.join([LaunchConfiguration('rgb_topic').perform(context), "_relay"]), ''.join(LaunchConfiguration('rgb_topic').perform(context)), LaunchConfiguration('compressed').perform(context)), description='Should not be modified manually!'),
DeclareLaunchArgument('depth_topic_relay', default_value=ConditionalText(''.join([LaunchConfiguration('depth_topic').perform(context), "_relay"]), ''.join(LaunchConfiguration('depth_topic').perform(context)), LaunchConfiguration('compressed').perform(context)), description='Should not be modified manually!'),
@@ -82,11 +85,13 @@ def launch_setup(context, *args, **kwargs):
namespace=LaunchConfiguration('namespace')),
Node(
package='rtabmap_sync', executable='rgbd_sync', name="rgbd_sync", output="screen",
+ emulate_tty=True,
condition=IfCondition(PythonExpression(["'", LaunchConfiguration('stereo'), "' != 'true' and '", LaunchConfiguration('rgbd_sync'), "' == 'true'"])),
parameters=[{
"approx_sync": LaunchConfiguration('approx_rgbd_sync'),
"approx_sync_max_interval": LaunchConfiguration('approx_sync_max_interval'),
- "queue_size": LaunchConfiguration('queue_size'),
+ "topic_queue_size": LaunchConfiguration('topic_queue_size'),
+ "sync_queue_size": LaunchConfiguration('sync_queue_size'),
"qos": LaunchConfiguration('qos_image'),
"qos_camera_info": LaunchConfiguration('qos_camera_info'),
"depth_scale": LaunchConfiguration('depth_scale')}],
@@ -116,11 +121,13 @@ def launch_setup(context, *args, **kwargs):
namespace=LaunchConfiguration('namespace')),
Node(
package='rtabmap_sync', executable='stereo_sync', name="stereo_sync", output="screen",
+ emulate_tty=True,
condition=IfCondition(PythonExpression(["'", LaunchConfiguration('stereo'), "' == 'true' and '", LaunchConfiguration('rgbd_sync'), "' == 'true'"])),
parameters=[{
"approx_sync": LaunchConfiguration('approx_rgbd_sync'),
"approx_sync_max_interval": LaunchConfiguration('approx_sync_max_interval'),
- "queue_size": LaunchConfiguration('queue_size'),
+ "topic_queue_size": LaunchConfiguration('topic_queue_size'),
+ "sync_queue_size": LaunchConfiguration('sync_queue_size'),
"qos": LaunchConfiguration('qos_image'),
"qos_camera_info": LaunchConfiguration('qos_camera_info')}],
remappings=[
@@ -134,6 +141,7 @@ def launch_setup(context, *args, **kwargs):
# Relay rgbd_image
Node(
package='rtabmap_util', executable='rgbd_relay', name="rgbd_relay", output="screen",
+ emulate_tty=True,
condition=IfCondition(PythonExpression(["'", LaunchConfiguration('rgbd_sync'), "' != 'true' and '", LaunchConfiguration('subscribe_rgbd'), "' == 'true' and '", LaunchConfiguration('compressed'), "' != 'true'"])),
parameters=[{
"qos": LaunchConfiguration('qos_image')}],
@@ -143,6 +151,7 @@ def launch_setup(context, *args, **kwargs):
namespace=LaunchConfiguration('namespace')),
Node(
package='rtabmap_util', executable='rgbd_relay', name="rgbd_relay_uncompress", output="screen",
+ emulate_tty=True,
condition=IfCondition(PythonExpression(["'", LaunchConfiguration('rgbd_sync'), "' != 'true' and '", LaunchConfiguration('subscribe_rgbd'), "' == 'true' and '", LaunchConfiguration('compressed'), "' == 'true'"])),
parameters=[{
"uncompress": True,
@@ -155,6 +164,7 @@ def launch_setup(context, *args, **kwargs):
# RGB-D odometry
Node(
package='rtabmap_odom', executable='rgbd_odometry', name="rgbd_odometry", output="screen",
+ emulate_tty=True,
condition=IfCondition(PythonExpression(["'", LaunchConfiguration('icp_odometry'), "' != 'true' and '", LaunchConfiguration('visual_odometry'), "' == 'true' and '", LaunchConfiguration('stereo'), "' != 'true'"])),
parameters=[{
"frame_id": LaunchConfiguration('frame_id'),
@@ -164,10 +174,12 @@ def launch_setup(context, *args, **kwargs):
"ground_truth_base_frame_id": LaunchConfiguration('ground_truth_base_frame_id').perform(context),
"wait_for_transform": LaunchConfiguration('wait_for_transform'),
"wait_imu_to_init": LaunchConfiguration('wait_imu_to_init'),
+ "always_check_imu_tf": LaunchConfiguration('always_check_imu_tf'),
"approx_sync": LaunchConfiguration('approx_sync'),
"approx_sync_max_interval": LaunchConfiguration('approx_sync_max_interval'),
"config_path": LaunchConfiguration('cfg').perform(context),
- "queue_size": LaunchConfiguration('queue_size'),
+ "topic_queue_size": LaunchConfiguration('topic_queue_size'),
+ "sync_queue_size": LaunchConfiguration('sync_queue_size'),
"qos": LaunchConfiguration('qos_image'),
"qos_camera_info": LaunchConfiguration('qos_camera_info'),
"qos_imu": LaunchConfiguration('qos_imu'),
@@ -182,13 +194,14 @@ def launch_setup(context, *args, **kwargs):
("rgbd_image", LaunchConfiguration('rgbd_topic_relay')),
("odom", LaunchConfiguration('odom_topic')),
("imu", LaunchConfiguration('imu_topic'))],
- arguments=[LaunchConfiguration("args"), LaunchConfiguration("odom_args")],
+ arguments=[LaunchConfiguration("args"), LaunchConfiguration("odom_args"), "--ros-args", "--log-level", [LaunchConfiguration('namespace'), '.rgbd_odometry:=', LaunchConfiguration('odom_log_level')], "--log-level", ['rgbd_odometry:=', LaunchConfiguration('odom_log_level')]],
prefix=LaunchConfiguration('launch_prefix'),
namespace=LaunchConfiguration('namespace')),
# Stereo odometry
Node(
package='rtabmap_odom', executable='stereo_odometry', name="stereo_odometry", output="screen",
+ emulate_tty=True,
condition=IfCondition(PythonExpression(["'", LaunchConfiguration('icp_odometry'), "' != 'true' and '", LaunchConfiguration('visual_odometry'), "' == 'true' and '", LaunchConfiguration('stereo'), "' == 'true'"])),
parameters=[{
"frame_id": LaunchConfiguration('frame_id'),
@@ -198,10 +211,12 @@ def launch_setup(context, *args, **kwargs):
"ground_truth_base_frame_id": LaunchConfiguration('ground_truth_base_frame_id').perform(context),
"wait_for_transform": LaunchConfiguration('wait_for_transform'),
"wait_imu_to_init": LaunchConfiguration('wait_imu_to_init'),
+ "always_check_imu_tf": LaunchConfiguration('always_check_imu_tf'),
"approx_sync": LaunchConfiguration('approx_sync'),
"approx_sync_max_interval": LaunchConfiguration('approx_sync_max_interval'),
"config_path": LaunchConfiguration('cfg').perform(context),
- "queue_size": LaunchConfiguration('queue_size'),
+ "topic_queue_size": LaunchConfiguration('topic_queue_size'),
+ "sync_queue_size": LaunchConfiguration('sync_queue_size'),
"qos": LaunchConfiguration('qos_image'),
"qos_camera_info": LaunchConfiguration('qos_camera_info'),
"qos_imu": LaunchConfiguration('qos_imu'),
@@ -217,13 +232,14 @@ def launch_setup(context, *args, **kwargs):
("rgbd_image", LaunchConfiguration('rgbd_topic_relay')),
("odom", LaunchConfiguration('odom_topic')),
("imu", LaunchConfiguration('imu_topic'))],
- arguments=[LaunchConfiguration("args"), LaunchConfiguration("odom_args")],
+ arguments=[LaunchConfiguration("args"), LaunchConfiguration("odom_args"), "--ros-args", "--log-level", [LaunchConfiguration('namespace'), '.stereo_odometry:=', LaunchConfiguration('odom_log_level')], "--log-level", ['stereo_odometry:=', LaunchConfiguration('odom_log_level')]],
prefix=LaunchConfiguration('launch_prefix'),
namespace=LaunchConfiguration('namespace')),
# ICP odometry
Node(
package='rtabmap_odom', executable='icp_odometry', name="icp_odometry", output="screen",
+ emulate_tty=True,
condition=IfCondition(LaunchConfiguration('icp_odometry')),
parameters=[{
"frame_id": LaunchConfiguration('frame_id'),
@@ -233,9 +249,11 @@ def launch_setup(context, *args, **kwargs):
"ground_truth_base_frame_id": LaunchConfiguration('ground_truth_base_frame_id').perform(context),
"wait_for_transform": LaunchConfiguration('wait_for_transform'),
"wait_imu_to_init": LaunchConfiguration('wait_imu_to_init'),
+ "always_check_imu_tf": LaunchConfiguration('always_check_imu_tf'),
"approx_sync": LaunchConfiguration('approx_sync'),
"config_path": LaunchConfiguration('cfg').perform(context),
- "queue_size": LaunchConfiguration('queue_size'),
+ "topic_queue_size": LaunchConfiguration('topic_queue_size'),
+ "sync_queue_size": LaunchConfiguration('sync_queue_size'),
"qos": LaunchConfiguration('qos_image'),
"qos_imu": LaunchConfiguration('qos_imu'),
"guess_frame_id": LaunchConfiguration('odom_guess_frame_id').perform(context),
@@ -246,12 +264,13 @@ def launch_setup(context, *args, **kwargs):
("scan_cloud", LaunchConfiguration('scan_cloud_topic')),
("odom", LaunchConfiguration('odom_topic')),
("imu", LaunchConfiguration('imu_topic'))],
- arguments=[LaunchConfiguration("args"), LaunchConfiguration("odom_args")],
+ arguments=[LaunchConfiguration("args"), LaunchConfiguration("odom_args"), "--ros-args", "--log-level", [LaunchConfiguration('namespace'), '.icp_odometry:=', LaunchConfiguration('odom_log_level')], "--log-level", ['icp_odometry:=', LaunchConfiguration('odom_log_level')]],
prefix=LaunchConfiguration('launch_prefix'),
namespace=LaunchConfiguration('namespace')),
Node(
package='rtabmap_slam', executable='rtabmap', name="rtabmap", output="screen",
+ emulate_tty=True,
parameters=[{
"subscribe_depth": LaunchConfiguration('depth'),
"subscribe_rgbd": LaunchConfiguration('subscribe_rgbd'),
@@ -266,6 +285,7 @@ def launch_setup(context, *args, **kwargs):
"odom_frame_id": LaunchConfiguration('odom_frame_id').perform(context),
"publish_tf": LaunchConfiguration('publish_tf_map'),
"initial_pose": LaunchConfiguration('initial_pose'),
+ "use_action_for_goal": LaunchConfiguration('use_action_for_goal'),
"ground_truth_frame_id": LaunchConfiguration('ground_truth_frame_id').perform(context),
"ground_truth_base_frame_id": LaunchConfiguration('ground_truth_base_frame_id').perform(context),
"odom_tf_angular_variance": LaunchConfiguration('odom_tf_angular_variance'),
@@ -275,7 +295,8 @@ def launch_setup(context, *args, **kwargs):
"database_path": LaunchConfiguration('database_path'),
"approx_sync": LaunchConfiguration('approx_sync'),
"config_path": LaunchConfiguration('cfg').perform(context),
- "queue_size": LaunchConfiguration('queue_size'),
+ "topic_queue_size": LaunchConfiguration('topic_queue_size'),
+ "sync_queue_size": LaunchConfiguration('sync_queue_size'),
"qos_image": LaunchConfiguration('qos_image'),
"qos_scan": LaunchConfiguration('qos_scan'),
"qos_odom": LaunchConfiguration('qos_odom'),
@@ -290,6 +311,7 @@ def launch_setup(context, *args, **kwargs):
"Mem/InitWMWithAllNodes": ConditionalText("true", "false", IfCondition(PythonExpression(["'", LaunchConfiguration('localization'), "' == 'true'"]))._predicate_func(context)).perform(context)
}],
remappings=[
+ ("map", LaunchConfiguration('map_topic')),
("rgb/image", LaunchConfiguration('rgb_topic_relay')),
("depth/image", LaunchConfiguration('depth_topic_relay')),
("rgb/camera_info", LaunchConfiguration('camera_info_topic')),
@@ -306,13 +328,15 @@ def launch_setup(context, *args, **kwargs):
("tag_detections", LaunchConfiguration('tag_topic')),
("fiducial_transforms", LaunchConfiguration('fiducial_topic')),
("odom", LaunchConfiguration('odom_topic')),
- ("imu", LaunchConfiguration('imu_topic'))],
- arguments=[LaunchConfiguration("args")],
+ ("imu", LaunchConfiguration('imu_topic')),
+ ("goal_out", LaunchConfiguration('output_goal_topic'))],
+ arguments=[LaunchConfiguration("args"), "--ros-args", "--log-level", [LaunchConfiguration('namespace'), '.rtabmap:=', LaunchConfiguration('log_level')], "--log-level", ['rtabmap:=', LaunchConfiguration('log_level')]],
prefix=LaunchConfiguration('launch_prefix'),
namespace=LaunchConfiguration('namespace')),
Node(
package='rtabmap_viz', executable='rtabmap_viz', name="rtabmap_viz", output='screen',
+ emulate_tty=True,
parameters=[{
"subscribe_depth": LaunchConfiguration('depth'),
"subscribe_rgbd": LaunchConfiguration('subscribe_rgbd'),
@@ -326,7 +350,8 @@ def launch_setup(context, *args, **kwargs):
"odom_frame_id": LaunchConfiguration('odom_frame_id').perform(context),
"wait_for_transform": LaunchConfiguration('wait_for_transform'),
"approx_sync": LaunchConfiguration('approx_sync'),
- "queue_size": LaunchConfiguration('queue_size'),
+ "topic_queue_size": LaunchConfiguration('topic_queue_size'),
+ "sync_queue_size": LaunchConfiguration('sync_queue_size'),
"qos_image": LaunchConfiguration('qos_image'),
"qos_scan": LaunchConfiguration('qos_scan'),
"qos_odom": LaunchConfiguration('qos_odom'),
@@ -346,7 +371,7 @@ def launch_setup(context, *args, **kwargs):
("scan_cloud", LaunchConfiguration('scan_cloud_topic')),
("odom", LaunchConfiguration('odom_topic'))],
condition=IfCondition(LaunchConfiguration("rtabmap_viz")),
- arguments=[LaunchConfiguration("gui_cfg")],
+ arguments=[LaunchConfiguration("gui_cfg"), "--ros-args", "--log-level", [LaunchConfiguration('namespace'), '.rtabmap_viz:=', LaunchConfiguration('log_level')], "--log-level", ['rtabmap_viz:=', LaunchConfiguration('log_level')]],
prefix=LaunchConfiguration('launch_prefix'),
namespace=LaunchConfiguration('namespace')),
Node(
@@ -355,12 +380,15 @@ def launch_setup(context, *args, **kwargs):
arguments=[["-d"], [LaunchConfiguration("rviz_cfg")]]),
Node(
package='rtabmap_util', executable='point_cloud_xyzrgb', name="point_cloud_xyzrgb", output='screen',
+ emulate_tty=True,
condition=IfCondition(LaunchConfiguration("rviz")),
parameters=[{
"decimation": 4,
"voxel_size": 0.0,
"approx_sync": LaunchConfiguration('approx_sync'),
- "approx_sync_max_interval": LaunchConfiguration('approx_sync_max_interval')
+ "approx_sync_max_interval": LaunchConfiguration('approx_sync_max_interval'),
+ "qos": LaunchConfiguration('qos_image'),
+ "qos_camera_info": LaunchConfiguration('qos_camera_info')
}],
remappings=[
('left/image', LaunchConfiguration('left_image_topic_relay')),
@@ -391,6 +419,8 @@ def generate_launch_description():
DeclareLaunchArgument('use_sim_time', default_value='false', description='Use simulation (Gazebo) clock if true'),
+ DeclareLaunchArgument('log_level', default_value='info', description="ROS logging level (debug, info, warn, error). For RTAB-Map\'s logger level, use \"args\" argument."),
+
# Config files
DeclareLaunchArgument('cfg', default_value='', description='To change RTAB-Map\'s parameters, set the path of config file (*.ini) generated by the standalone app.'),
DeclareLaunchArgument('gui_cfg', default_value='~/.ros/rtabmap_gui.ini', description='Configuration path of rtabmap_viz.'),
@@ -399,17 +429,22 @@ def generate_launch_description():
DeclareLaunchArgument('frame_id', default_value='base_link', description='Fixed frame id of the robot (base frame), you may set "base_link" or "base_footprint" if they are published. For camera-only config, this could be "camera_link".'),
DeclareLaunchArgument('odom_frame_id', default_value='', description='If set, TF is used to get odometry instead of the topic.'),
DeclareLaunchArgument('map_frame_id', default_value='map', description='Output map frame id (TF).'),
+ DeclareLaunchArgument('map_topic', default_value='map', description='Map topic name.'),
DeclareLaunchArgument('publish_tf_map', default_value='true', description='Publish TF between map and odomerty.'),
DeclareLaunchArgument('namespace', default_value='rtabmap', description=''),
DeclareLaunchArgument('database_path', default_value='~/.ros/rtabmap.db', description='Where is the map saved/loaded.'),
- DeclareLaunchArgument('queue_size', default_value='10', description=''),
- DeclareLaunchArgument('qos', default_value='1', description='General QoS used for sensor input data: 0=system default, 1=Reliable, 2=Best Effort.'),
+ DeclareLaunchArgument('topic_queue_size', default_value='10', description='Queue size of individual topic subscribers.'),
+ DeclareLaunchArgument('queue_size', default_value='10', description='Backward compatibility, use "sync_queue_size" instead.'),
+ DeclareLaunchArgument('qos', default_value='0', description='General QoS used for sensor input data: 0=system default, 1=Reliable, 2=Best Effort.'),
DeclareLaunchArgument('wait_for_transform', default_value='0.2', description=''),
DeclareLaunchArgument('rtabmap_args', default_value='', description='Backward compatibility, use "args" instead.'),
DeclareLaunchArgument('launch_prefix', default_value='', description='For debugging purpose, it fills prefix tag of the nodes, e.g., "xterm -e gdb -ex run --args"'),
DeclareLaunchArgument('output', default_value='screen', description='Control node output (screen or log).'),
DeclareLaunchArgument('initial_pose', default_value='', description='Set an initial pose (only in localization mode). Format: "x y z roll pitch yaw" or "x y z qx qy qz qw". Default: see "RGBD/StartAtOrigin" doc'),
+ DeclareLaunchArgument('output_goal_topic', default_value='/goal_pose', description='Output goal topic (can be connected to nav2).'),
+ DeclareLaunchArgument('use_action_for_goal', default_value='false', description='Connect to nav2\'s navigate_to_pose action server instead of publishing the output goal topic.'),
+
DeclareLaunchArgument('ground_truth_frame_id', default_value='', description='e.g., "world"'),
DeclareLaunchArgument('ground_truth_base_frame_id', default_value='', description='e.g., "tracker", a fake frame matching the frame "frame_id" (but on different TF tree)'),
@@ -460,11 +495,12 @@ def generate_launch_description():
DeclareLaunchArgument('odom_guess_frame_id', default_value='', description=''),
DeclareLaunchArgument('odom_guess_min_translation', default_value='0.0', description=''),
DeclareLaunchArgument('odom_guess_min_rotation', default_value='0.0', description=''),
-
+
# imu
DeclareLaunchArgument('imu_topic', default_value='/imu/data', description='Used with VIO approaches and for SLAM graph optimization (gravity constraints).'),
DeclareLaunchArgument('wait_imu_to_init', default_value='false', description=''),
-
+ DeclareLaunchArgument('always_check_imu_tf', default_value='true', description='The odometry node will always check if TF between IMU frame and base frame has changed. If false, it is checked till a valid transform is initialized.'),
+
# User Data
DeclareLaunchArgument('subscribe_user_data', default_value='false', description='User data synchronized subscription.'),
DeclareLaunchArgument('user_data_topic', default_value='/user_data', description=''),
diff --git a/rtabmap_launch/package.xml b/rtabmap_launch/package.xml
index fb19c7ec..f674e8f9 100644
--- a/rtabmap_launch/package.xml
+++ b/rtabmap_launch/package.xml
@@ -2,7 +2,7 @@
rtabmap_launch
- 0.21.5
+ 0.22.0
RTAB-Map's main launch files.
Mathieu Labbe
Mathieu Labbe
diff --git a/rtabmap_msgs/CMakeLists.txt b/rtabmap_msgs/CMakeLists.txt
index 7ae7eead..01e7cebb 100644
--- a/rtabmap_msgs/CMakeLists.txt
+++ b/rtabmap_msgs/CMakeLists.txt
@@ -10,6 +10,15 @@ if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
+if(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")
+ # issues #1285 #1288
+ find_library(
+ rcutils_LIB NAMES rcutils
+ PATHS "/opt/ros/$ENV{ROS_DISTRO}/lib"
+ NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH REQUIRED
+ )
+endif()
+
##################
## Dependencies ##
##################
diff --git a/rtabmap_msgs/msg/OdomInfo.msg b/rtabmap_msgs/msg/OdomInfo.msg
index 369334f3..870963c8 100644
--- a/rtabmap_msgs/msg/OdomInfo.msg
+++ b/rtabmap_msgs/msg/OdomInfo.msg
@@ -18,6 +18,8 @@ int32 local_key_frames
int32 local_bundle_outliers
int32 local_bundle_constraints
float32 local_bundle_time
+float32 local_bundle_avg_inlier_distance
+int32 local_bundle_max_key_frames_for_inlier
bool key_frame_added
float32 time_estimation
float32 time_particle_filtering
diff --git a/rtabmap_msgs/package.xml b/rtabmap_msgs/package.xml
index f82a254f..136fb15a 100644
--- a/rtabmap_msgs/package.xml
+++ b/rtabmap_msgs/package.xml
@@ -2,7 +2,7 @@
rtabmap_msgs
- 0.21.5
+ 0.22.0
RTAB-Map's msgs package.
Mathieu Labbe
Mathieu Labbe
@@ -14,6 +14,8 @@
rosidl_default_generators
+ ros_environment
+
builtin_interfaces
std_msgs
std_srvs
diff --git a/rtabmap_odom/CMakeLists.txt b/rtabmap_odom/CMakeLists.txt
index f9859b44..96130502 100644
--- a/rtabmap_odom/CMakeLists.txt
+++ b/rtabmap_odom/CMakeLists.txt
@@ -10,6 +10,15 @@ if(POLICY CMP0074)
cmake_policy(SET CMP0074 NEW)
endif()
+if(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")
+ # issues #1285 #1288
+ find_library(
+ builtin_interfaces__rosidl_generator_c_LIB NAMES builtin_interfaces__rosidl_generator_c
+ PATHS "/opt/ros/$ENV{ROS_DISTRO}/lib"
+ NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH REQUIRED
+ )
+endif()
+
find_package(ament_cmake_ros REQUIRED)
find_package(cv_bridge REQUIRED)
find_package(image_geometry REQUIRED)
diff --git a/rtabmap_odom/include/rtabmap_odom/OdometryROS.h b/rtabmap_odom/include/rtabmap_odom/OdometryROS.h
index ae2f7d5f..4a1b7a81 100644
--- a/rtabmap_odom/include/rtabmap_odom/OdometryROS.h
+++ b/rtabmap_odom/include/rtabmap_odom/OdometryROS.h
@@ -47,6 +47,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include
#include
#include
+#include
#include
@@ -59,7 +60,7 @@ class Odometry;
namespace rtabmap_odom {
-class OdometryROS : public rclcpp::Node
+class OdometryROS : public rclcpp::Node, public UThread
{
public:
@@ -98,12 +99,18 @@ protected:
private:
+ virtual void mainLoop();
+ virtual void mainLoopKill();
virtual void updateParameters(rtabmap::ParametersMap &) {}
virtual void onOdomInit() {}
void callbackIMU(const sensor_msgs::msg::Imu::SharedPtr msg);
void reset(const rtabmap::Transform & pose = rtabmap::Transform::getIdentity());
+protected:
+ rclcpp::CallbackGroup::SharedPtr dataCallbackGroup_;
+ void tick(const rclcpp::Time & stamp);
+
private:
rtabmap::Odometry * odometry_;
@@ -147,6 +154,15 @@ private:
std::shared_ptr tfBuffer_;
std::shared_ptr tfListener_;
rclcpp::Subscription::SharedPtr imuSub_;
+ rclcpp::CallbackGroup::SharedPtr imuCallbackGroup_;
+
+ // Safe-threading
+ UMutex imuMutex_;
+ UMutex dataMutex_;
+ USemaphore dataReady_;
+ rtabmap::SensorData dataToProcess_;
+ std_msgs::msg::Header dataHeaderToProcess_;
+ bool bufferedDataToProcess_;
bool paused_;
int resetCountdown_;
@@ -157,6 +173,7 @@ private:
rtabmap::Transform guess_;
rtabmap::Transform guessPreviousPose_;
double previousStamp_;
+ double previousClockTime_;
double expectedUpdateRate_;
double maxUpdateRate_;
double minUpdateRate_;
@@ -164,11 +181,14 @@ private:
bool compressionParallelized_;
int odomStrategy_;
bool waitIMUToinit_;
+ bool alwaysCheckImuTf_;
bool imuProcessed_;
- std::map imus_;
- std::pair bufferedData_;
+ int processedMsgs_;
+ int droppedMsgs_;
+ std::map imus_;
std::string configPath_;
rtabmap::Transform initialPose_;
+ rtabmap::Transform imuLocalTransform_;
rtabmap_util::ULogToRosout ulogToRosout_;
@@ -176,11 +196,13 @@ private:
{
public:
OdomStatusTask();
- void setStatus(bool isLost);
+ void setStatus(bool isLost, int processedMsgs, int droppedMsgs);
void run(diagnostic_updater::DiagnosticStatusWrapper &stat);
private:
bool lost_;
bool dataReceived_;
+ int processedMsgs_;
+ int droppedMsgs_;
};
OdomStatusTask statusDiagnostic_;
std::unique_ptr syncDiagnostic_;
diff --git a/rtabmap_odom/include/rtabmap_odom/rgbd_odometry.hpp b/rtabmap_odom/include/rtabmap_odom/rgbd_odometry.hpp
index 090c3beb..b8aee9ee 100644
--- a/rtabmap_odom/include/rtabmap_odom/rgbd_odometry.hpp
+++ b/rtabmap_odom/include/rtabmap_odom/rgbd_odometry.hpp
@@ -41,7 +41,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include
#include
+#ifdef PRE_ROS_IRON
#include
+#else
+#include
+#endif
namespace rtabmap_odom
{
@@ -144,7 +148,8 @@ private:
message_filters::Synchronizer * approxSync6_;
typedef message_filters::sync_policies::ExactTime MyExactSync6Policy;
message_filters::Synchronizer * exactSync6_;
- int queueSize_;
+ int topicQueueSize_;
+ int syncQueueSize_;
bool keepColor_;
};
diff --git a/rtabmap_odom/include/rtabmap_odom/stereo_odometry.hpp b/rtabmap_odom/include/rtabmap_odom/stereo_odometry.hpp
index 3f5dddf4..4167e738 100644
--- a/rtabmap_odom/include/rtabmap_odom/stereo_odometry.hpp
+++ b/rtabmap_odom/include/rtabmap_odom/stereo_odometry.hpp
@@ -37,7 +37,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include
#include
+#ifdef PRE_ROS_IRON
#include
+#else
+#include
+#endif
#include
#include
#include
@@ -142,7 +146,8 @@ private:
typedef message_filters::sync_policies::ExactTime MyExactSync6Policy;
message_filters::Synchronizer * exactSync6_;
- int queueSize_;
+ int topicQueueSize_;
+ int syncQueueSize_;
bool keepColor_;
};
diff --git a/rtabmap_odom/package.xml b/rtabmap_odom/package.xml
index f9d7f40b..c51f592f 100644
--- a/rtabmap_odom/package.xml
+++ b/rtabmap_odom/package.xml
@@ -2,7 +2,7 @@
rtabmap_odom
- 0.21.5
+ 0.22.0
RTAB-Map's odometry package.
Mathieu Labbe
Mathieu Labbe
@@ -12,6 +12,8 @@
ament_cmake_ros
+ ros_environment
+
cv_bridge
image_geometry
laser_geometry
diff --git a/rtabmap_odom/src/ICPOdometryNode.cpp b/rtabmap_odom/src/ICPOdometryNode.cpp
index da85a658..f83264d0 100644
--- a/rtabmap_odom/src/ICPOdometryNode.cpp
+++ b/rtabmap_odom/src/ICPOdometryNode.cpp
@@ -70,7 +70,10 @@ int main(int argc, char **argv)
rclcpp::init(argc, argv);
rclcpp::NodeOptions options;
options.arguments(arguments);
- rclcpp::spin(std::make_shared(options));
+ auto node = std::make_shared(options);
+ rclcpp::executors::MultiThreadedExecutor executor;
+ executor.add_node(node);
+ executor.spin();
rclcpp::shutdown();
return 0;
}
diff --git a/rtabmap_odom/src/OdometryROS.cpp b/rtabmap_odom/src/OdometryROS.cpp
index 0b8235e5..c9955d99 100644
--- a/rtabmap_odom/src/OdometryROS.cpp
+++ b/rtabmap_odom/src/OdometryROS.cpp
@@ -34,7 +34,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include
+#ifdef PRE_ROS_IRON
#include
+#else
+#include
+#endif
#include
#include
@@ -80,7 +84,11 @@ OdometryROS::OdometryROS(const std::string & name, const rclcpp::NodeOptions & o
paused_(false),
resetCountdown_(0),
resetCurrentCount_(0),
+ stereoParams_(false),
+ visParams_(false),
+ icpParams_(false),
previousStamp_(0.0),
+ previousClockTime_(0.0),
expectedUpdateRate_(0.0),
maxUpdateRate_(0.0),
minUpdateRate_(0.0),
@@ -88,11 +96,16 @@ OdometryROS::OdometryROS(const std::string & name, const rclcpp::NodeOptions & o
compressionParallelized_(true),
odomStrategy_(Parameters::defaultOdomStrategy()),
waitIMUToinit_(false),
+ alwaysCheckImuTf_(true),
imuProcessed_(false),
+ processedMsgs_(0),
+ droppedMsgs_(0),
configPath_(),
initialPose_(Transform::getIdentity()),
ulogToRosout_(this)
{
+ dataCallbackGroup_ = create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
+
int qos = this->declare_parameter("qos", (int)qos_);
qos_ = (rmw_qos_reliability_policy_t)qos;
@@ -107,11 +120,7 @@ OdometryROS::OdometryROS(const std::string & name, const rclcpp::NodeOptions & o
odomSensorDataFeaturesPub_ = create_publisher("odom_sensor_data/features", rclcpp::QoS(1).reliability(qos_));
odomSensorDataCompressedPub_ = create_publisher("odom_sensor_data/compressed", rclcpp::QoS(1).reliability(qos_));
- tfBuffer_ = std::make_shared(this->get_clock());
- //auto timer_interface = std::make_shared(
- // this->get_node_base_interface(),
- // this->get_node_timers_interface());
- //tfBuffer_->setCreateTimerInterface(timer_interface);
+ tfBuffer_ = std::make_shared(get_clock());
tfListener_ = std::make_shared(*tfBuffer_);
tfBroadcaster_ = std::make_shared(this);
@@ -140,6 +149,8 @@ OdometryROS::OdometryROS(const std::string & name, const rclcpp::NodeOptions & o
compressionParallelized_ = this->declare_parameter("sensor_data_parallel_compression", compressionParallelized_);
waitIMUToinit_ = this->declare_parameter("wait_imu_to_init", waitIMUToinit_);
+ alwaysCheckImuTf_ = this->declare_parameter("always_check_imu_tf", alwaysCheckImuTf_);
+
configPath_ = uReplaceChar(configPath_, '~', UDirectory::homeDir());
if(configPath_.size() && configPath_.at(0) != '/')
@@ -194,12 +205,14 @@ OdometryROS::OdometryROS(const std::string & name, const rclcpp::NodeOptions & o
RCLCPP_INFO(this->get_logger(), "Odometry: max_update_rate = %f Hz", maxUpdateRate_);
RCLCPP_INFO(this->get_logger(), "Odometry: min_update_rate = %f Hz", minUpdateRate_);
RCLCPP_INFO(this->get_logger(), "Odometry: wait_imu_to_init = %s", waitIMUToinit_?"true":"false");
+ RCLCPP_INFO(this->get_logger(), "Odometry: always_check_imu_tf = %s", alwaysCheckImuTf_?"true":"false");
RCLCPP_INFO(this->get_logger(), "Odometry: sensor_data_compression_format = %s", compressionImgFormat_.c_str());
RCLCPP_INFO(this->get_logger(), "Odometry: sensor_data_parallel_compression = %s", compressionParallelized_?"true":"false");
}
OdometryROS::~OdometryROS()
{
+ this->join(true);
delete odometry_;
}
@@ -361,14 +374,19 @@ void OdometryROS::init(bool stereoParams, bool visParams, bool icpParams)
Parameters::parse(this->parameters(), Parameters::kOdomStrategy(), odomStrategy_);
if(waitIMUToinit_)
{
- int queueSize = 10;
- this->get_parameter_or("queue_size", queueSize, queueSize);
+ imuCallbackGroup_ = create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
+ rclcpp::SubscriptionOptions options;
+ options.callback_group = imuCallbackGroup_;
+ int queueSize = this->declare_parameter("imu_queue_size", 200);
int qosImu = this->declare_parameter("qos_imu", (int)qos_);
- imuSub_ = create_subscription("imu", rclcpp::QoS(queueSize*5).reliability((rmw_qos_reliability_policy_t)qosImu), std::bind(&OdometryROS::callbackIMU, this, std::placeholders::_1));
+ imuSub_ = create_subscription("imu", rclcpp::QoS(queueSize).reliability((rmw_qos_reliability_policy_t)qosImu), std::bind(&OdometryROS::callbackIMU, this, std::placeholders::_1), options);
RCLCPP_INFO(this->get_logger(), "odometry: Subscribing to IMU topic %s", imuSub_->get_topic_name());
RCLCPP_INFO(this->get_logger(), "odometry: qos_imu = %d", qosImu);
+ RCLCPP_INFO(this->get_logger(), "odometry: imu_queue_size = %d", queueSize);
}
+ this->start();
+
onOdomInit();
}
@@ -404,88 +422,195 @@ void OdometryROS::callbackIMU(const sensor_msgs::msg::Imu::SharedPtr msg)
if(!this->isPaused())
{
double stamp = rtabmap_conversions::timestampFromROS(msg->header.stamp);
- rtabmap::Transform localTransform = rtabmap::Transform::getIdentity();
- if(this->frameId().compare(msg->header.frame_id) != 0)
+ //RCLCPP_WARN(get_logger(), "Received imu: %f delay=%f", stamp, (now() - msg->header.stamp).seconds());
+
{
- localTransform = rtabmap_conversions::getTransform(this->frameId(), msg->header.frame_id, msg->header.stamp, *tfBuffer_, waitForTransform_);
+ UScopeMutex m(imuMutex_);
+
+ if(!imuProcessed_ && imus_.empty())
+ {
+ rtabmap::Transform localTransform = rtabmap_conversions::getTransform(this->frameId(), msg->header.frame_id, msg->header.stamp, *tfBuffer_, waitForTransform_);
+ if(localTransform.isNull())
+ {
+ RCLCPP_WARN(this->get_logger(), "Dropping imu data! A valid TF between %s and %s is required to initialize IMU.",
+ this->frameId().c_str(), msg->header.frame_id.c_str());
+ return;
+ }
+ }
+
+ imus_.insert(std::make_pair(stamp, msg));
+
+ if(imus_.size() > 1000)
+ {
+ RCLCPP_WARN(this->get_logger(), "Dropping imu data!");
+ imus_.erase(imus_.begin());
+ }
}
- if(localTransform.isNull())
+ if(dataMutex_.lockTry() == 0)
{
- RCLCPP_ERROR(this->get_logger(), "Could not transform IMU msg from frame \"%s\" to frame \"%s\", TF not available at time %f",
- msg->header.frame_id.c_str(), this->frameId().c_str(), stamp);
- return;
- }
-
- IMU imu(cv::Vec4d(msg->orientation.x, msg->orientation.y, msg->orientation.z, msg->orientation.w),
- cv::Mat(3,3,CV_64FC1,(void*)msg->orientation_covariance.data()).clone(),
- cv::Vec3d(msg->angular_velocity.x, msg->angular_velocity.y, msg->angular_velocity.z),
- cv::Mat(3,3,CV_64FC1,(void*)msg->angular_velocity_covariance.data()).clone(),
- cv::Vec3d(msg->linear_acceleration.x, msg->linear_acceleration.y, msg->linear_acceleration.z),
- cv::Mat(3,3,CV_64FC1,(void*)msg->linear_acceleration_covariance.data()).clone(),
- localTransform);
-
- imus_.insert(std::make_pair(stamp, imu));
- //RCLCPP_WARN(get_logger(), "Received imu: %f", stamp);
-
- if(bufferedData_.first.isValid() && stamp > bufferedData_.first.stamp())
- {
- SensorData data = bufferedData_.first;
- bufferedData_.first = SensorData();
- processData(data, bufferedData_.second);
- }
-
- if(imus_.size() > 1000)
- {
- imus_.erase(imus_.begin());
+ if(bufferedDataToProcess_ && rtabmap_conversions::timestampFromROS(dataHeaderToProcess_.stamp) <= stamp)
+ {
+ bufferedDataToProcess_ = false;
+ dataReady_.release();
+ }
+ dataMutex_.unlock();
}
}
}
void OdometryROS::processData(SensorData & data, const std_msgs::msg::Header & header)
{
- if((waitIMUToinit_ && !imuProcessed_) && odometry_->framesProcessed() == 0 && odometry_->getPose().isIdentity() && imus_.empty())
+ //RCLCPP_WARN(get_logger(), "Received image: %f delay=%f", data.stamp(), (now() - header.stamp).seconds());
+ if(dataMutex_.lockTry() == 0)
{
- RCLCPP_WARN(this->get_logger(), "odometry: waiting imu (%s) to initialize orientation (wait_imu_to_init=true)", imuSub_->get_topic_name());
- return;
- }
-
- if(waitIMUToinit_ && (imus_.empty() || imus_.rbegin()->first < rtabmap_conversions::timestampFromROS(header.stamp)))
- {
- //RCLCPP_WARN(get_logger(), "No imu received with higher stamp than last image (%f)! Buffering this image until we get more imu msgs...", timestampFromROS(header.stamp));
-
- // keep in cache to process later when we will receive imu msgs
- if(bufferedData_.first.isValid())
- {
- RCLCPP_ERROR(this->get_logger(), "Overwriting previous data! Make sure IMU is "
- "published faster than data rate. (last image stamp "
- "buffered=%f and new one is %f, last imu stamp received=%f)",
- bufferedData_.first.stamp(), data.stamp(), imus_.empty()?0:imus_.rbegin()->first);
+ if(bufferedDataToProcess_) {
+ RCLCPP_ERROR(this->get_logger(), "We didn't receive IMU newer than previous image (%f) and we just received a new image (%f). The previous image is dropped!",
+ rtabmap_conversions::timestampFromROS(dataHeaderToProcess_.stamp), rtabmap_conversions::timestampFromROS(header.stamp));
+ ++droppedMsgs_;
}
- bufferedData_.first = data;
- bufferedData_.second = header;
+ dataToProcess_ = data;
+ dataHeaderToProcess_ = header;
+ bufferedDataToProcess_ = false;
+ dataReady_.release();
+ dataMutex_.unlock();
+ ++processedMsgs_;
+ }
+ else
+ {
+ //RCLCPP_WARN(get_logger(), "Dropping image/scan data");
+ ++droppedMsgs_;
+ }
+}
+
+void OdometryROS::mainLoopKill()
+{
+ // in case we were waiting, unblock thread
+ dataReady_.release();
+}
+
+void OdometryROS::mainLoop()
+{
+ dataReady_.acquire();
+
+ if(!this->isRunning())
+ {
+ // thread killed
return;
}
- // process all imu data up to current image stamp (or just after so that underlying odom approach can do interpolation of imu at image stamp)
- std::map::iterator iterEnd = imus_.lower_bound(rtabmap_conversions::timestampFromROS(header.stamp));
- if(iterEnd!= imus_.end())
+
+ UScopeMutex lock(dataMutex_);
+
+ // aliases
+ SensorData & data = dataToProcess_;
+ std_msgs::msg::Header & header = dataHeaderToProcess_;
+
+ std::vector > imus;
{
- ++iterEnd;
- }
- for(std::map::iterator iter=imus_.begin(); iter!=iterEnd;)
+ UScopeMutex m(imuMutex_);
+
+ if((waitIMUToinit_ && !imuProcessed_) && odometry_->framesProcessed() == 0 && odometry_->getPose().isIdentity() && imus_.empty())
+ {
+ RCLCPP_WARN(this->get_logger(), "odometry: waiting imu (%s) to initialize orientation (wait_imu_to_init=true)", imuSub_->get_topic_name());
+ return;
+ }
+
+ if(waitIMUToinit_ && (imus_.empty() || imus_.rbegin()->first < rtabmap_conversions::timestampFromROS(header.stamp)))
+ {
+ RCLCPP_WARN(this->get_logger(), "Make sure IMU is published faster than data rate! (last image stamp=%f and last imu stamp received=%f). Buffering the image until an imu with same or greater stamp is received.",
+ data.stamp(), imus_.empty()?0:imus_.rbegin()->first);
+ bufferedDataToProcess_ = true;
+ return;
+ }
+ // process all imu data up to current image stamp (or just after so that underlying odom approach can do interpolation of imu at image stamp)
+ std::map::iterator iterEnd = imus_.lower_bound(rtabmap_conversions::timestampFromROS(header.stamp));
+ if(iterEnd!= imus_.end())
+ {
+ ++iterEnd;
+ }
+ for(std::map::iterator iter=imus_.begin(); iter!=iterEnd;)
+ {
+ imus.push_back(*iter);
+ imus_.erase(iter++);
+ }
+ } // end imu lock
+
+ bool imuWarnShown = false;
+ for(size_t i=0; ifirst);
- SensorData dataIMU(iter->second, 0, iter->first);
+ if((alwaysCheckImuTf_ && !imuWarnShown) || imuLocalTransform_.isNull())
+ {
+ if(this->frameId().compare(imus[i].second->header.frame_id) != 0)
+ {
+ // We should not have to wait for IMU TF (imu delay <<< sensor data delay), so don't
+ rtabmap::Transform localTransform = rtabmap_conversions::getTransform(this->frameId(), imus[i].second->header.frame_id, imus[i].second->header.stamp, *tfBuffer_, 0);
+ if(localTransform.isNull())
+ {
+ if(imuLocalTransform_.isNull()) {
+ RCLCPP_ERROR(this->get_logger(), "Could not transform IMU msg from frame \"%s\" to frame \"%s\", TF is not available at IMU msg time %f. All IMU msgs up to sensor data time %f are skipped! If IMU TF is not static, make sure to publish it before the the imu topic is published.",
+ imus[i].second->header.frame_id.c_str(), this->frameId().c_str(), rtabmap_conversions::timestampFromROS(imus[i].second->header.stamp), data.stamp());
+ break;
+ } else if(!imuWarnShown) {
+ imuWarnShown = true; // show only one time
+ RCLCPP_WARN(this->get_logger(), "Could not transform IMU msg from frame \"%s\" to frame \"%s\", TF is not available at IMU msg time %f. We will use latest known IMU local transform (if TF between camera/lidar and the IMU is static, you can safely ignore this warning and set always_check_imu_tf to false).",
+ imus[i].second->header.frame_id.c_str(), this->frameId().c_str(), rtabmap_conversions::timestampFromROS(imus[i].second->header.stamp));
+ }
+ }
+ else {
+ imuLocalTransform_ = localTransform;
+ }
+ }
+ else if(imuLocalTransform_.isNull())
+ {
+ imuLocalTransform_.setIdentity();
+ }
+ }
+
+ IMU imu(cv::Vec4d(imus[i].second->orientation.x, imus[i].second->orientation.y, imus[i].second->orientation.z, imus[i].second->orientation.w),
+ cv::Mat(3,3,CV_64FC1,(void*)imus[i].second->orientation_covariance.data()).clone(),
+ cv::Vec3d(imus[i].second->angular_velocity.x, imus[i].second->angular_velocity.y, imus[i].second->angular_velocity.z),
+ cv::Mat(3,3,CV_64FC1,(void*)imus[i].second->angular_velocity_covariance.data()).clone(),
+ cv::Vec3d(imus[i].second->linear_acceleration.x, imus[i].second->linear_acceleration.y, imus[i].second->linear_acceleration.z),
+ cv::Mat(3,3,CV_64FC1,(void*)imus[i].second->linear_acceleration_covariance.data()).clone(),
+ imuLocalTransform_);
+
+ SensorData dataIMU(imu, 0, imus[i].first);
odometry_->process(dataIMU);
- imus_.erase(iter++);
imuProcessed_ = true;
}
- //RCLCPP_WARN(get_logger(), "img callback: process image %f", timestampFromROS(header.stamp));
-
Transform groundTruth;
if(!data.imageRaw().empty() || !data.laserScanRaw().isEmpty())
{
- if(previousStamp_>0.0 && previousStamp_ >= rtabmap_conversions::timestampFromROS(header.stamp))
+ // Detect time jump in the past
+ double clockNow = now().seconds();
+ if(previousClockTime_ > clockNow)
+ {
+ RCLCPP_WARN(this->get_logger(), "Odometry: Detected jump back in time of %f sec. Odometry is "
+ "automatically reset to latest computed pose!",
+ previousClockTime_ - clockNow);
+ SensorData dataCpy = dataToProcess_;
+ std_msgs::msg::Header headerCpy = dataHeaderToProcess_;
+ double previousCpy = previousClockTime_;
+ this->reset(odometry_->getPose());
+ if(clockNow > rtabmap_conversions::timestampFromROS(headerCpy.stamp)) {
+ // new frame is using new clock, process it now
+ dataToProcess_ = dataCpy;
+ dataHeaderToProcess_ = headerCpy;
+ dataReady_.release();
+ RCLCPP_WARN(this->get_logger(), "Odometry: Restarting with frame: %f (clock previous=%f, new=%f)",
+ rtabmap_conversions::timestampFromROS(headerCpy.stamp), previousCpy, clockNow);
+ }
+ else {
+ // skip that old frame
+ RCLCPP_WARN(this->get_logger(), "Odometry: skipping frame: %f (clock previous=%f, new=%f)",
+ rtabmap_conversions::timestampFromROS(headerCpy.stamp), previousCpy, clockNow);
+ }
+ previousClockTime_ = clockNow;
+ return;
+ }
+ previousClockTime_ = clockNow;
+
+ if(previousStamp_ >= rtabmap_conversions::timestampFromROS(header.stamp))
{
RCLCPP_WARN(this->get_logger(), "Odometry: Detected not valid consecutive stamps (previous=%fs new=%fs). "
"New stamp should be always greater than previous stamp. This new data is ignored.",
@@ -585,7 +710,17 @@ void OdometryROS::processData(SensorData & data, const std_msgs::msg::Header & h
correctionMsg.header.stamp = header.stamp;
Transform correction = odometry_->getPose() * guess_ * guessCurrentPose.inverse();
rtabmap_conversions::transformToGeometryMsg(correction, correctionMsg.transform);
- tfBroadcaster_->sendTransform(correctionMsg);
+
+ double time_now = now().seconds();
+ if(time_now >= previousClockTime_) {
+ tfBroadcaster_->sendTransform(correctionMsg);
+ }
+ else {
+ RCLCPP_WARN(this->get_logger(), "TF %s->%s is not published because we detected a time jump in the past of %f sec.",
+ correctionMsg.header.frame_id.c_str(),
+ correctionMsg.child_frame_id.c_str(),
+ previousClockTime_ - time_now);
+ }
}
guessPreviousPose_ = guessCurrentPose;
return;
@@ -603,7 +738,7 @@ void OdometryROS::processData(SensorData & data, const std_msgs::msg::Header & h
bool tooOldPreviousData = minUpdateRate_ > 0 && previousStamp_ > 0 && (rtabmap_conversions::timestampFromROS(header.stamp)-previousStamp_) > 1.0/minUpdateRate_;
// process data
- rclcpp::Time timeStart = now();
+ rclcpp::Time timeStart = rclcpp::Clock().now();
rtabmap::OdometryInfo info;
if(!groundTruth.isNull())
{
@@ -639,11 +774,30 @@ void OdometryROS::processData(SensorData & data, const std_msgs::msg::Header & h
correctionMsg.header.stamp = header.stamp;
Transform correction = pose * guessCurrentPose.inverse();
rtabmap_conversions::transformToGeometryMsg(correction, correctionMsg.transform);
- tfBroadcaster_->sendTransform(correctionMsg);
+
+ double time_now = now().seconds();
+ if(time_now >= previousClockTime_) {
+ tfBroadcaster_->sendTransform(correctionMsg);
+ }
+ else {
+ RCLCPP_WARN(this->get_logger(), "TF %s->%s is not published because we detected a time jump in the past of %f sec.",
+ correctionMsg.header.frame_id.c_str(),
+ correctionMsg.child_frame_id.c_str(),
+ previousClockTime_ - time_now);
+ }
}
else
{
- tfBroadcaster_->sendTransform(poseMsg);
+ double time_now = now().seconds();
+ if(time_now >= previousClockTime_) {
+ tfBroadcaster_->sendTransform(poseMsg);
+ }
+ else {
+ RCLCPP_WARN(this->get_logger(), "TF %s->%s is not published because we detected a time jump in the past of %f sec.",
+ poseMsg.header.frame_id.c_str(),
+ poseMsg.child_frame_id.c_str(),
+ previousClockTime_ - time_now);
+ }
}
}
@@ -835,7 +989,18 @@ void OdometryROS::processData(SensorData & data, const std_msgs::msg::Header & h
correctionMsg.header.stamp = header.stamp;
Transform correction = odometry_->getPose() * guess_ * guessCurrentPose.inverse();
rtabmap_conversions::transformToGeometryMsg(correction, correctionMsg.transform);
- tfBroadcaster_->sendTransform(correctionMsg);
+ double time_now = now().seconds();
+ if(time_now >= previousClockTime_) {
+ tfBroadcaster_->sendTransform(correctionMsg);
+ }
+ else {
+ RCLCPP_WARN(this->get_logger(), "TF %s->%s is not published because its stamp (%f) is greater "
+ "than current time (%f), possible time jump happened!",
+ correctionMsg.header.frame_id.c_str(),
+ correctionMsg.child_frame_id.c_str(),
+ rtabmap_conversions::timestampFromROS(correctionMsg.header.stamp),
+ time_now);
+ }
}
}
@@ -848,10 +1013,9 @@ void OdometryROS::processData(SensorData & data, const std_msgs::msg::Header & h
"is %fs too old (>%fs, min_update_rate = %f Hz). Previous data stamp is %f while new data stamp is %f.",
rtabmap_conversions::timestampFromROS(header.stamp) - previousStamp_, 1.0/minUpdateRate_, minUpdateRate_, previousStamp_, rtabmap_conversions::timestampFromROS(header.stamp));
}
- else
+ else if(--resetCurrentCount_>0)
{
RCLCPP_WARN(this->get_logger(), "Odometry lost! Odometry will be reset after next %d consecutive unsuccessful odometry updates...", resetCurrentCount_);
- --resetCurrentCount_;
}
if(resetCurrentCount_ == 0 || tooOldPreviousData)
@@ -879,6 +1043,11 @@ void OdometryROS::processData(SensorData & data, const std_msgs::msg::Header & h
odometry_->reset(tfPose);
}
}
+ // Keep resetting if the odometry cannot initialize in next updates (e.g., lack of features).
+ // This will make sure we keep updating to latest guess pose.
+ if(resetCurrentCount_ == 0) {
+ ++resetCurrentCount_;
+ }
}
}
@@ -925,124 +1094,124 @@ void OdometryROS::processData(SensorData & data, const std_msgs::msg::Header & h
}
}
- if(!data.imageRaw().empty() || !data.laserScanRaw().isEmpty())
+ if(odomSensorDataPub_->get_subscription_count()>0 || odomSensorDataFeaturesPub_->get_subscription_count()>0)
{
- if(odomSensorDataPub_->get_subscription_count()>0 || odomSensorDataFeaturesPub_->get_subscription_count()>0)
+ rtabmap_msgs::msg::SensorData msg;
+ rtabmap_conversions::sensorDataToROS(data, msg, frameId_, odomSensorDataPub_->get_subscription_count()>0);
+ msg.header.stamp = header.stamp; // use corresponding time stamp to image
+ if(odomSensorDataPub_->get_subscription_count()>0)
{
- rtabmap_msgs::msg::SensorData msg;
- rtabmap_conversions::sensorDataToROS(data, msg, frameId_, odomSensorDataPub_->get_subscription_count()>0);
- msg.header.stamp = header.stamp; // use corresponding time stamp to image
- if(odomSensorDataPub_->get_subscription_count()>0)
- {
- odomSensorDataPub_->publish(msg);
- }
- if(odomSensorDataFeaturesPub_->get_subscription_count()>0)
- {
- // remove data
- msg.left = sensor_msgs::msg::Image();
- msg.right = sensor_msgs::msg::Image();
- msg.laser_scan = sensor_msgs::msg::PointCloud2();
- msg.grid_ground.clear();
- msg.grid_obstacles.clear();
- msg.grid_empty_cells.clear();
- odomSensorDataFeaturesPub_->publish(msg);
- }
+ odomSensorDataPub_->publish(msg);
}
- if(odomSensorDataCompressedPub_->get_subscription_count()>0)
+ if(odomSensorDataFeaturesPub_->get_subscription_count()>0)
{
- cv::Mat compressedImage;
- cv::Mat compressedDepth;
- cv::Mat compressedScan;
- if(compressionParallelized_)
- {
- rtabmap::CompressionThread ctImage(data.imageRaw(), compressionImgFormat_);
- rtabmap::CompressionThread ctDepth(data.depthOrRightRaw(), data.depthOrRightRaw().type() == CV_32FC1 || data.depthOrRightRaw().type() == CV_16UC1?std::string(".png"):compressionImgFormat_);
- rtabmap::CompressionThread ctLaserScan(data.laserScanRaw().data());
- if(!data.imageRaw().empty())
- {
- ctImage.start();
- }
- if(!data.depthOrRightRaw().empty())
- {
- ctDepth.start();
- }
- if(!data.laserScanRaw().isEmpty())
- {
- ctLaserScan.start();
- }
- ctImage.join();
- ctDepth.join();
- ctLaserScan.join();
-
- compressedImage = ctImage.getCompressedData();
- compressedDepth = ctDepth.getCompressedData();
- compressedScan = ctLaserScan.getCompressedData();
- }
- else
- {
- compressedImage = compressImage2(data.imageRaw(), compressionImgFormat_);
- compressedDepth = compressImage2(data.depthOrRightRaw(), data.depthOrRightRaw().type() == CV_32FC1 || data.depthOrRightRaw().type() == CV_16UC1?std::string(".png"):compressionImgFormat_);
- compressedScan = compressData2(data.laserScanRaw().data());
- }
- if(!compressedImage.empty() && !data.stereoCameraModels().empty())
- {
- data.setStereoImage(compressedImage, compressedDepth, data.stereoCameraModels(), false);
- }
- else if(!compressedImage.empty() && !data.cameraModels().empty())
- {
- data.setRGBDImage(compressedImage, compressedDepth, data.cameraModels(), false);
- }
- if(!compressedScan.empty())
- {
- data.setLaserScan(data.laserScanRaw().angleIncrement() == 0.0f?
- LaserScan(compressedScan,
- data.laserScanRaw().maxPoints(),
- data.laserScanRaw().rangeMax(),
- data.laserScanRaw().format(),
- data.laserScanRaw().localTransform()):
- LaserScan(compressedScan,
- data.laserScanRaw().format(),
- data.laserScanRaw().rangeMin(),
- data.laserScanRaw().rangeMax(),
- data.laserScanRaw().angleMin(),
- data.laserScanRaw().angleMax(),
- data.laserScanRaw().angleIncrement(),
- data.laserScanRaw().localTransform()), false);
- }
- rtabmap_msgs::msg::SensorData msg;
- rtabmap_conversions::sensorDataToROS(data, msg, frameId_, false);
- msg.header.stamp = header.stamp; // use corresponding time stamp to image
- odomSensorDataCompressedPub_->publish(msg);
+ // remove data
+ msg.left = sensor_msgs::msg::Image();
+ msg.right = sensor_msgs::msg::Image();
+ msg.laser_scan = sensor_msgs::msg::PointCloud2();
+ msg.grid_ground.clear();
+ msg.grid_obstacles.clear();
+ msg.grid_empty_cells.clear();
+ odomSensorDataFeaturesPub_->publish(msg);
}
-
- if(visParams_)
- {
- if(icpParams_)
- {
- RCLCPP_INFO(this->get_logger(), "Odom: quality=%d, ratio=%f, std dev=%fm|%frad, update time=%fs", info.reg.inliers, info.reg.icpInliersRatio, pose.isNull()?0.0f:std::sqrt(info.reg.covariance.at(0,0)), pose.isNull()?0.0f:std::sqrt(info.reg.covariance.at(5,5)), (now()-timeStart).seconds());
- }
- else
- {
- RCLCPP_INFO(this->get_logger(), "Odom: quality=%d, std dev=%fm|%frad, update time=%fs", info.reg.inliers, pose.isNull()?0.0f:std::sqrt(info.reg.covariance.at(0,0)), pose.isNull()?0.0f:std::sqrt(info.reg.covariance.at(5,5)), (now()-timeStart).seconds());
- }
- }
- else // if(icpParams_)
- {
- RCLCPP_INFO(this->get_logger(), "Odom: ratio=%f, std dev=%fm|%frad, update time=%fs", info.reg.icpInliersRatio, pose.isNull()?0.0f:std::sqrt(info.reg.covariance.at(0,0)), pose.isNull()?0.0f:std::sqrt(info.reg.covariance.at(5,5)), (now()-timeStart).seconds());
- }
-
- statusDiagnostic_.setStatus(pose.isNull());
- if(syncDiagnostic_.get() && !pose.isNull())
- {
- double curentRate = 1.0/(this->now()-timeStart).seconds();
- syncDiagnostic_->tick(header.stamp,
- maxUpdateRate_>0 ? maxUpdateRate_:
- expectedUpdateRate_>0 && expectedUpdateRate_ < curentRate ? expectedUpdateRate_:
- previousStamp_ == 0.0 || rtabmap_conversions::timestampFromROS(header.stamp) - previousStamp_ > 1.0/curentRate?0:curentRate);
- }
-
- previousStamp_ = rtabmap_conversions::timestampFromROS(header.stamp);
}
+ if(odomSensorDataCompressedPub_->get_subscription_count()>0)
+ {
+ cv::Mat compressedImage;
+ cv::Mat compressedDepth;
+ cv::Mat compressedScan;
+ if(compressionParallelized_)
+ {
+ rtabmap::CompressionThread ctImage(data.imageRaw(), compressionImgFormat_);
+ rtabmap::CompressionThread ctDepth(data.depthOrRightRaw(), data.depthOrRightRaw().type() == CV_32FC1 || data.depthOrRightRaw().type() == CV_16UC1?std::string(".png"):compressionImgFormat_);
+ rtabmap::CompressionThread ctLaserScan(data.laserScanRaw().data());
+ if(!data.imageRaw().empty())
+ {
+ ctImage.start();
+ }
+ if(!data.depthOrRightRaw().empty())
+ {
+ ctDepth.start();
+ }
+ if(!data.laserScanRaw().isEmpty())
+ {
+ ctLaserScan.start();
+ }
+ ctImage.join();
+ ctDepth.join();
+ ctLaserScan.join();
+
+ compressedImage = ctImage.getCompressedData();
+ compressedDepth = ctDepth.getCompressedData();
+ compressedScan = ctLaserScan.getCompressedData();
+ }
+ else
+ {
+ compressedImage = compressImage2(data.imageRaw(), compressionImgFormat_);
+ compressedDepth = compressImage2(data.depthOrRightRaw(), data.depthOrRightRaw().type() == CV_32FC1 || data.depthOrRightRaw().type() == CV_16UC1?std::string(".png"):compressionImgFormat_);
+ compressedScan = compressData2(data.laserScanRaw().data());
+ }
+ if(!compressedImage.empty() && !data.stereoCameraModels().empty())
+ {
+ data.setStereoImage(compressedImage, compressedDepth, data.stereoCameraModels(), false);
+ }
+ else if(!compressedImage.empty() && !data.cameraModels().empty())
+ {
+ data.setRGBDImage(compressedImage, compressedDepth, data.cameraModels(), false);
+ }
+ if(!compressedScan.empty())
+ {
+ data.setLaserScan(data.laserScanRaw().angleIncrement() == 0.0f?
+ LaserScan(compressedScan,
+ data.laserScanRaw().maxPoints(),
+ data.laserScanRaw().rangeMax(),
+ data.laserScanRaw().format(),
+ data.laserScanRaw().localTransform()):
+ LaserScan(compressedScan,
+ data.laserScanRaw().format(),
+ data.laserScanRaw().rangeMin(),
+ data.laserScanRaw().rangeMax(),
+ data.laserScanRaw().angleMin(),
+ data.laserScanRaw().angleMax(),
+ data.laserScanRaw().angleIncrement(),
+ data.laserScanRaw().localTransform()), false);
+ }
+ rtabmap_msgs::msg::SensorData msg;
+ rtabmap_conversions::sensorDataToROS(data, msg, frameId_, false);
+ msg.header.stamp = header.stamp; // use corresponding time stamp to image
+ odomSensorDataCompressedPub_->publish(msg);
+ }
+
+ double delay = (now()-header.stamp).seconds();
+ if(visParams_)
+ {
+ if(icpParams_)
+ {
+ RCLCPP_INFO(this->get_logger(), "Odom: quality=%d, ratio=%f, std dev=%fm|%frad, update time=%fs delay=%fs", info.reg.inliers, info.reg.icpInliersRatio, pose.isNull()?0.0f:std::sqrt(info.reg.covariance.at(0,0)), pose.isNull()?0.0f:std::sqrt(info.reg.covariance.at(5,5)), (rclcpp::Clock().now()-timeStart).seconds(), delay);
+ }
+ else
+ {
+ RCLCPP_INFO(this->get_logger(), "Odom: quality=%d, std dev=%fm|%frad, update time=%fs delay=%fs", info.reg.inliers, pose.isNull()?0.0f:std::sqrt(info.reg.covariance.at(0,0)), pose.isNull()?0.0f:std::sqrt(info.reg.covariance.at(5,5)), (rclcpp::Clock().now()-timeStart).seconds(), delay);
+ }
+ }
+ else // if(icpParams_)
+ {
+ RCLCPP_INFO(this->get_logger(), "Odom: ratio=%f, std dev=%fm|%frad, update time=%fs delay=%fs", info.reg.icpInliersRatio, pose.isNull()?0.0f:std::sqrt(info.reg.covariance.at(0,0)), pose.isNull()?0.0f:std::sqrt(info.reg.covariance.at(5,5)), (rclcpp::Clock().now()-timeStart).seconds(), delay);
+ }
+
+ statusDiagnostic_.setStatus(pose.isNull(), processedMsgs_, droppedMsgs_);
+ processedMsgs_ = 0;
+ droppedMsgs_ = 0;
+ if(syncDiagnostic_.get())
+ {
+ double curentRate = 1.0/(rclcpp::Clock().now()-timeStart).seconds();
+ syncDiagnostic_->tickOutput(header.stamp,
+ maxUpdateRate_>0 ? maxUpdateRate_:
+ expectedUpdateRate_>0 && expectedUpdateRate_ < curentRate ? expectedUpdateRate_:
+ previousStamp_ == 0.0 || rtabmap_conversions::timestampFromROS(header.stamp) - previousStamp_ > 1.0/curentRate?0:curentRate);
+ }
+
+ previousStamp_ = rtabmap_conversions::timestampFromROS(header.stamp);
}
void OdometryROS::resetOdom(
@@ -1066,14 +1235,21 @@ void OdometryROS::resetToPose(
void OdometryROS::reset(const Transform & pose)
{
+ UScopeMutex lock(dataMutex_);
odometry_->reset(pose);
guess_.setNull();
guessPreviousPose_.setNull();
previousStamp_ = 0.0;
+ previousClockTime_ = 0.0;
resetCurrentCount_ = resetCountdown_;
imuProcessed_ = false;
- bufferedData_.first= SensorData();
+ dataToProcess_ = SensorData();
+ dataHeaderToProcess_ = std_msgs::msg::Header();
+ bufferedDataToProcess_ = false;
+ imuMutex_.lock();
imus_.clear();
+ imuMutex_.unlock();
+ imuLocalTransform_.setNull();
this->flushCallbacks();
}
@@ -1145,13 +1321,17 @@ void OdometryROS::setLogError(
OdometryROS::OdomStatusTask::OdomStatusTask() :
diagnostic_updater::DiagnosticTask("Odom status"),
lost_(false),
- dataReceived_(false)
+ dataReceived_(false),
+ processedMsgs_(0),
+ droppedMsgs_(0)
{}
-void OdometryROS::OdomStatusTask::setStatus(bool isLost)
+void OdometryROS::OdomStatusTask::setStatus(bool isLost, int processedMsgs, int droppedMsgs)
{
dataReceived_ = true;
lost_ = isLost;
+ processedMsgs_ += processedMsgs;
+ droppedMsgs_ += droppedMsgs;
}
void OdometryROS::OdomStatusTask::run(diagnostic_updater::DiagnosticStatusWrapper &stat)
@@ -1168,6 +1348,18 @@ void OdometryROS::OdomStatusTask::run(diagnostic_updater::DiagnosticStatusWrappe
{
stat.summary(diagnostic_msgs::msg::DiagnosticStatus::OK, "Tracking.");
}
+ stat.add("Topics Processed", processedMsgs_);
+ stat.add("Topics Dropped", droppedMsgs_);
+ processedMsgs_ = 0;
+ droppedMsgs_ = 0;
+}
+
+void OdometryROS::tick(const rclcpp::Time & stamp)
+{
+ if(syncDiagnostic_.get())
+ {
+ syncDiagnostic_->tickInput(stamp);
+ }
}
}
diff --git a/rtabmap_odom/src/RGBDOdometryNode.cpp b/rtabmap_odom/src/RGBDOdometryNode.cpp
index ad203b99..270d9ede 100644
--- a/rtabmap_odom/src/RGBDOdometryNode.cpp
+++ b/rtabmap_odom/src/RGBDOdometryNode.cpp
@@ -76,7 +76,10 @@ int main(int argc, char **argv)
rclcpp::init(argc, argv);
rclcpp::NodeOptions options;
options.arguments(arguments);
- rclcpp::spin(std::make_shared(options));
+ auto node = std::make_shared(options);
+ rclcpp::executors::MultiThreadedExecutor executor;
+ executor.add_node(node);
+ executor.spin();
rclcpp::shutdown();
return 0;
}
diff --git a/rtabmap_odom/src/StereoOdometryNode.cpp b/rtabmap_odom/src/StereoOdometryNode.cpp
index d8976b7b..60a729ce 100644
--- a/rtabmap_odom/src/StereoOdometryNode.cpp
+++ b/rtabmap_odom/src/StereoOdometryNode.cpp
@@ -76,7 +76,10 @@ int main(int argc, char **argv)
rclcpp::init(argc, argv);
rclcpp::NodeOptions options;
options.arguments(arguments);
- rclcpp::spin(std::make_shared(options));
+ auto node = std::make_shared(options);
+ rclcpp::executors::MultiThreadedExecutor executor;
+ executor.add_node(node);
+ executor.spin();
rclcpp::shutdown();
return 0;
}
diff --git a/rtabmap_odom/src/nodelets/icp_odometry.cpp b/rtabmap_odom/src/nodelets/icp_odometry.cpp
index 4babddd3..d30b68aa 100644
--- a/rtabmap_odom/src/nodelets/icp_odometry.cpp
+++ b/rtabmap_odom/src/nodelets/icp_odometry.cpp
@@ -97,8 +97,11 @@ void ICPOdometry::onOdomInit()
RCLCPP_INFO(this->get_logger(), "IcpOdometry: deskewing = %s", deskewing_?"true":"false");
RCLCPP_INFO(this->get_logger(), "IcpOdometry: deskewing_slerp = %s", deskewingSlerp_?"true":"false");
- scan_sub_ = create_subscription("scan", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos()), std::bind(&ICPOdometry::callbackScan, this, std::placeholders::_1));
- cloud_sub_ = create_subscription("scan_cloud", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos()), std::bind(&ICPOdometry::callbackCloud, this, std::placeholders::_1));
+ rclcpp::SubscriptionOptions options;
+ options.callback_group = dataCallbackGroup_;
+
+ scan_sub_ = create_subscription("scan", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos()), std::bind(&ICPOdometry::callbackScan, this, std::placeholders::_1), options);
+ cloud_sub_ = create_subscription("scan_cloud", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos()), std::bind(&ICPOdometry::callbackCloud, this, std::placeholders::_1), options);
filtered_scan_pub_ = create_publisher("odom_filtered_input_scan", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos()));
@@ -277,6 +280,9 @@ void ICPOdometry::callbackScan(const sensor_msgs::msg::LaserScan::SharedPtr scan
scan_sub_.reset();
return;
}
+
+ tick(scanMsg->header.stamp);
+
scanReceived_ = true;
if(this->isPaused())
{
@@ -342,6 +348,7 @@ void ICPOdometry::callbackScan(const sensor_msgs::msg::LaserScan::SharedPtr scan
sensor_msgs::msg::PointCloud2 scanOutDeskewed;
rtabmap_conversions::transformPointCloud(t.toEigen4f(), scanOut, scanOutDeskewed);
+ scanOutDeskewed.header.frame_id = scanMsg->header.frame_id;
scanOut = scanOutDeskewed;
}
else
@@ -520,6 +527,9 @@ void ICPOdometry::callbackCloud(const sensor_msgs::msg::PointCloud2::SharedPtr p
cloud_sub_.reset();
return;
}
+
+ tick(pointCloudMsg->header.stamp);
+
cloudReceived_ = true;
if(this->isPaused())
{
diff --git a/rtabmap_odom/src/nodelets/rgbd_odometry.cpp b/rtabmap_odom/src/nodelets/rgbd_odometry.cpp
index b81e37a5..1da53fc4 100644
--- a/rtabmap_odom/src/nodelets/rgbd_odometry.cpp
+++ b/rtabmap_odom/src/nodelets/rgbd_odometry.cpp
@@ -27,7 +27,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include
+#ifdef PRE_ROS_IRON
#include
+#else
+#include
+#endif
#include
@@ -59,7 +63,8 @@ RGBDOdometry::RGBDOdometry(const rclcpp::NodeOptions & options) :
exactSync5_(0),
approxSync6_(0),
exactSync6_(0),
- queueSize_(5),
+ topicQueueSize_(10),
+ syncQueueSize_(5),
keepColor_(false)
{
OdometryROS::init(false, true, false);
@@ -89,25 +94,43 @@ void RGBDOdometry::onOdomInit()
double approxSyncMaxInterval = 0.0;
approxSync = this->declare_parameter("approx_sync", approxSync);
approxSyncMaxInterval = this->declare_parameter("approx_sync_max_interval", approxSyncMaxInterval);
- queueSize_ = this->declare_parameter("queue_size", queueSize_);
+ topicQueueSize_ = this->declare_parameter("topic_queue_size", topicQueueSize_);
+ int queueSize = this->declare_parameter("queue_size", -1);
+ if(queueSize != -1)
+ {
+ syncQueueSize_ = queueSize;
+ RCLCPP_WARN(this->get_logger(), "Parameter \"queue_size\" has been renamed "
+ "to \"sync_queue_size\" and will be removed "
+ "in future versions! The value (%d) is copied to "
+ "\"sync_queue_size\".", syncQueueSize_);
+ }
+ syncQueueSize_ = this->declare_parameter("sync_queue_size", syncQueueSize_);
int qosCamInfo = this->declare_parameter("qos_camera_info", (int)qos());
subscribeRGBD = this->declare_parameter("subscribe_rgbd", subscribeRGBD);
rgbdCameras = this->declare_parameter("rgbd_cameras", rgbdCameras);
- if(rgbdCameras <= 0)
+ if(rgbdCameras < 0)
{
- rgbdCameras = 1;
+ rgbdCameras = 0;
}
keepColor_ = this->declare_parameter("keep_color", keepColor_);
+ std::string rgbdTransport = this->declare_parameter("rgb_transport", std::string("raw"));
+ std::string depthTransport = this->declare_parameter("depth_transport", std::string("raw"));
RCLCPP_INFO(this->get_logger(), "RGBDOdometry: approx_sync = %s", approxSync?"true":"false");
if(approxSync)
RCLCPP_INFO(this->get_logger(), "RGBDOdometry: approx_sync_max_interval = %f", approxSyncMaxInterval);
- RCLCPP_INFO(this->get_logger(), "RGBDOdometry: queue_size = %d", queueSize_);
- RCLCPP_INFO(this->get_logger(), "RGBDOdometry: qos = %d", (int)qos());
+ RCLCPP_INFO(this->get_logger(), "RGBDOdometry: topic_queue_size = %d", topicQueueSize_);
+ RCLCPP_INFO(this->get_logger(), "RGBDOdometry: sync_queue_size = %d", syncQueueSize_);
+ RCLCPP_INFO(this->get_logger(), "RGBDOdometry: qos = %d", (int)qos());
RCLCPP_INFO(this->get_logger(), "RGBDOdometry: qos_camera_info = %d", qosCamInfo);
RCLCPP_INFO(this->get_logger(), "RGBDOdometry: subscribe_rgbd = %s", subscribeRGBD?"true":"false");
RCLCPP_INFO(this->get_logger(), "RGBDOdometry: rgbd_cameras = %d", rgbdCameras);
RCLCPP_INFO(this->get_logger(), "RGBDOdometry: keep_color = %s", keepColor_?"true":"false");
+ RCLCPP_INFO(this->get_logger(), "RGBDOdometry: rgb_transport = %s", rgbdTransport.c_str());
+ RCLCPP_INFO(this->get_logger(), "RGBDOdometry: depth_transport = %s", depthTransport.c_str());
+
+ rclcpp::SubscriptionOptions options;
+ options.callback_group = dataCallbackGroup_;
std::string subscribedTopic;
std::string subscribedTopicsMsg;
@@ -115,23 +138,23 @@ void RGBDOdometry::onOdomInit()
{
if(rgbdCameras >= 2)
{
- rgbd_image1_sub_.subscribe(this, "rgbd_image0", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos()).get_rmw_qos_profile());
- rgbd_image2_sub_.subscribe(this, "rgbd_image1", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos()).get_rmw_qos_profile());
+ rgbd_image1_sub_.subscribe(this, "rgbd_image0", rclcpp::QoS(topicQueueSize_).reliability((rmw_qos_reliability_policy_t)qos()).get_rmw_qos_profile(), options);
+ rgbd_image2_sub_.subscribe(this, "rgbd_image1", rclcpp::QoS(topicQueueSize_).reliability((rmw_qos_reliability_policy_t)qos()).get_rmw_qos_profile(), options);
if(rgbdCameras >= 3)
{
- rgbd_image3_sub_.subscribe(this, "rgbd_image2", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos()).get_rmw_qos_profile());
+ rgbd_image3_sub_.subscribe(this, "rgbd_image2", rclcpp::QoS(topicQueueSize_).reliability((rmw_qos_reliability_policy_t)qos()).get_rmw_qos_profile(), options);
}
if(rgbdCameras >= 4)
{
- rgbd_image4_sub_.subscribe(this, "rgbd_image3", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos()).get_rmw_qos_profile());
+ rgbd_image4_sub_.subscribe(this, "rgbd_image3", rclcpp::QoS(topicQueueSize_).reliability((rmw_qos_reliability_policy_t)qos()).get_rmw_qos_profile(), options);
}
if(rgbdCameras >= 5)
{
- rgbd_image5_sub_.subscribe(this, "rgbd_image4", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos()).get_rmw_qos_profile());
+ rgbd_image5_sub_.subscribe(this, "rgbd_image4", rclcpp::QoS(topicQueueSize_).reliability((rmw_qos_reliability_policy_t)qos()).get_rmw_qos_profile(), options);
}
if(rgbdCameras >= 6)
{
- rgbd_image6_sub_.subscribe(this, "rgbd_image5", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos()).get_rmw_qos_profile());
+ rgbd_image6_sub_.subscribe(this, "rgbd_image5", rclcpp::QoS(topicQueueSize_).reliability((rmw_qos_reliability_policy_t)qos()).get_rmw_qos_profile(), options);
}
if(rgbdCameras == 2)
@@ -139,7 +162,7 @@ void RGBDOdometry::onOdomInit()
if(approxSync)
{
approxSync2_ = new message_filters::Synchronizer(
- MyApproxSync2Policy(queueSize_),
+ MyApproxSync2Policy(syncQueueSize_),
rgbd_image1_sub_,
rgbd_image2_sub_);
if(approxSyncMaxInterval > 0.0)
@@ -149,7 +172,7 @@ void RGBDOdometry::onOdomInit()
else
{
exactSync2_ = new message_filters::Synchronizer(
- MyExactSync2Policy(queueSize_),
+ MyExactSync2Policy(syncQueueSize_),
rgbd_image1_sub_,
rgbd_image2_sub_);
exactSync2_->registerCallback(std::bind(&RGBDOdometry::callbackRGBD2, this, std::placeholders::_1, std::placeholders::_2));
@@ -166,7 +189,7 @@ void RGBDOdometry::onOdomInit()
if(approxSync)
{
approxSync3_ = new message_filters::Synchronizer(
- MyApproxSync3Policy(queueSize_),
+ MyApproxSync3Policy(syncQueueSize_),
rgbd_image1_sub_,
rgbd_image2_sub_,
rgbd_image3_sub_);
@@ -177,7 +200,7 @@ void RGBDOdometry::onOdomInit()
else
{
exactSync3_ = new message_filters::Synchronizer(
- MyExactSync3Policy(queueSize_),
+ MyExactSync3Policy(syncQueueSize_),
rgbd_image1_sub_,
rgbd_image2_sub_,
rgbd_image3_sub_);
@@ -196,7 +219,7 @@ void RGBDOdometry::onOdomInit()
if(approxSync)
{
approxSync4_ = new message_filters::Synchronizer(
- MyApproxSync4Policy(queueSize_),
+ MyApproxSync4Policy(syncQueueSize_),
rgbd_image1_sub_,
rgbd_image2_sub_,
rgbd_image3_sub_,
@@ -208,7 +231,7 @@ void RGBDOdometry::onOdomInit()
else
{
exactSync4_ = new message_filters::Synchronizer(
- MyExactSync4Policy(queueSize_),
+ MyExactSync4Policy(syncQueueSize_),
rgbd_image1_sub_,
rgbd_image2_sub_,
rgbd_image3_sub_,
@@ -229,7 +252,7 @@ void RGBDOdometry::onOdomInit()
if(approxSync)
{
approxSync5_ = new message_filters::Synchronizer(
- MyApproxSync5Policy(queueSize_),
+ MyApproxSync5Policy(syncQueueSize_),
rgbd_image1_sub_,
rgbd_image2_sub_,
rgbd_image3_sub_,
@@ -242,7 +265,7 @@ void RGBDOdometry::onOdomInit()
else
{
exactSync5_ = new message_filters::Synchronizer(
- MyExactSync5Policy(queueSize_),
+ MyExactSync5Policy(syncQueueSize_),
rgbd_image1_sub_,
rgbd_image2_sub_,
rgbd_image3_sub_,
@@ -265,7 +288,7 @@ void RGBDOdometry::onOdomInit()
if(approxSync)
{
approxSync6_ = new message_filters::Synchronizer(
- MyApproxSync6Policy(queueSize_),
+ MyApproxSync6Policy(syncQueueSize_),
rgbd_image1_sub_,
rgbd_image2_sub_,
rgbd_image3_sub_,
@@ -279,7 +302,7 @@ void RGBDOdometry::onOdomInit()
else
{
exactSync6_ = new message_filters::Synchronizer(
- MyExactSync6Policy(queueSize_),
+ MyExactSync6Policy(syncQueueSize_),
rgbd_image1_sub_,
rgbd_image2_sub_,
rgbd_image3_sub_,
@@ -311,7 +334,7 @@ void RGBDOdometry::onOdomInit()
}
else if(rgbdCameras == 0)
{
- rgbdxSub_ = create_subscription("rgbd_images", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos()), std::bind(&RGBDOdometry::callbackRGBDX, this, std::placeholders::_1));
+ rgbdxSub_ = create_subscription("rgbd_images", rclcpp::QoS(topicQueueSize_).reliability((rmw_qos_reliability_policy_t)qos()), std::bind(&RGBDOdometry::callbackRGBDX, this, std::placeholders::_1), options);
subscribedTopic = rgbdxSub_->get_topic_name();
subscribedTopicsMsg = uFormat("\n%s subscribed to:\n %s",
@@ -320,7 +343,7 @@ void RGBDOdometry::onOdomInit()
}
else
{
- rgbdSub_ = create_subscription("rgbd_image", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos()), std::bind(&RGBDOdometry::callbackRGBD, this, std::placeholders::_1));
+ rgbdSub_ = create_subscription("rgbd_image", rclcpp::QoS(topicQueueSize_).reliability((rmw_qos_reliability_policy_t)qos()), std::bind(&RGBDOdometry::callbackRGBD, this, std::placeholders::_1), options);
subscribedTopic = rgbdSub_->get_topic_name();
subscribedTopicsMsg =
@@ -331,29 +354,40 @@ void RGBDOdometry::onOdomInit()
}
else
{
- image_transport::TransportHints hints(this);
- image_mono_sub_.subscribe(this, "rgb/image", hints.getTransport(), rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos()).get_rmw_qos_profile());
- image_depth_sub_.subscribe(this, "depth/image", hints.getTransport(), rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos()).get_rmw_qos_profile());
+ image_transport::TransportHints rgb_hints(this, "raw", "rgb_transport");
+ image_transport::TransportHints depth_hints(this, "raw", "depth_transport");
+
+ std::string rgb_topic = get_node_base_interface()->resolve_topic_or_service_name(
+ "rgb/image", false, false
+ );
+ std::string depth_topic = get_node_base_interface()->resolve_topic_or_service_name(
+ "depth/image", false, false
+ );
+
+ image_mono_sub_.subscribe(this, rgb_topic, rgb_hints.getTransport(), rclcpp::QoS(topicQueueSize_).reliability((rmw_qos_reliability_policy_t)qos()).get_rmw_qos_profile(), options);
+ image_depth_sub_.subscribe(this, depth_topic, depth_hints.getTransport(), rclcpp::QoS(topicQueueSize_).reliability((rmw_qos_reliability_policy_t)qos()).get_rmw_qos_profile(), options);
info_sub_.subscribe(this, "rgb/camera_info", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qosCamInfo).get_rmw_qos_profile());
if(approxSync)
{
- approxSync_ = new message_filters::Synchronizer(MyApproxSyncPolicy(queueSize_), image_mono_sub_, image_depth_sub_, info_sub_);
+ approxSync_ = new message_filters::Synchronizer(MyApproxSyncPolicy(syncQueueSize_), image_mono_sub_, image_depth_sub_, info_sub_);
if(approxSyncMaxInterval > 0.0)
approxSync_->setMaxIntervalDuration(rclcpp::Duration::from_seconds(approxSyncMaxInterval));
approxSync_->registerCallback(std::bind(&RGBDOdometry::callback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
}
else
{
- exactSync_ = new message_filters::Synchronizer(MyExactSyncPolicy(queueSize_), image_mono_sub_, image_depth_sub_, info_sub_);
+ exactSync_ = new message_filters::Synchronizer(MyExactSyncPolicy(syncQueueSize_), image_mono_sub_, image_depth_sub_, info_sub_);
exactSync_->registerCallback(std::bind(&RGBDOdometry::callback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
}
subscribedTopic = image_mono_sub_.getSubscriber().getTopic();
- subscribedTopicsMsg = uFormat("\n%s subscribed to (%s sync%s):\n %s,\n %s,\n %s",
+ subscribedTopicsMsg = uFormat("\n%s subscribed to (%s sync%s, topic_queue_size=%d, sync_queue_size=%d):\n %s,\n %s,\n %s",
get_name(),
approxSync?"approx":"exact",
approxSync&&approxSyncMaxInterval!=0.0?uFormat(", max interval=%fs", approxSyncMaxInterval).c_str():"",
+ topicQueueSize_,
+ syncQueueSize_,
image_mono_sub_.getSubscriber().getTopic().c_str(),
image_depth_sub_.getSubscriber().getTopic().c_str(),
info_sub_.getSubscriber()->get_topic_name());
@@ -548,6 +582,8 @@ void RGBDOdometry::callback(
const sensor_msgs::msg::Image::ConstSharedPtr depth,
const sensor_msgs::msg::CameraInfo::ConstSharedPtr cameraInfo)
{
+ tick(image->header.stamp);
+
if(!this->isPaused())
{
std::vector imageMsgs(1);
@@ -576,6 +612,8 @@ void RGBDOdometry::callback(
void RGBDOdometry::callbackRGBDX(
const rtabmap_msgs::msg::RGBDImages::ConstSharedPtr images)
{
+ tick(images->header.stamp);
+
if(!this->isPaused())
{
if(images->rgbd_images.empty())
@@ -599,6 +637,8 @@ void RGBDOdometry::callbackRGBDX(
void RGBDOdometry::callbackRGBD(
const rtabmap_msgs::msg::RGBDImage::ConstSharedPtr image)
{
+ tick(image->header.stamp);
+
if(!this->isPaused())
{
std::vector imageMsgs(1);
@@ -615,6 +655,8 @@ void RGBDOdometry::callbackRGBD2(
const rtabmap_msgs::msg::RGBDImage::ConstSharedPtr image,
const rtabmap_msgs::msg::RGBDImage::ConstSharedPtr image2)
{
+ tick(image->header.stamp);
+
if(!this->isPaused())
{
std::vector imageMsgs(2);
@@ -634,6 +676,8 @@ void RGBDOdometry::callbackRGBD3(
const rtabmap_msgs::msg::RGBDImage::ConstSharedPtr image2,
const rtabmap_msgs::msg::RGBDImage::ConstSharedPtr image3)
{
+ tick(image->header.stamp);
+
if(!this->isPaused())
{
std::vector imageMsgs(3);
@@ -656,6 +700,8 @@ void RGBDOdometry::callbackRGBD4(
const rtabmap_msgs::msg::RGBDImage::ConstSharedPtr image3,
const rtabmap_msgs::msg::RGBDImage::ConstSharedPtr image4)
{
+ tick(image->header.stamp);
+
if(!this->isPaused())
{
std::vector imageMsgs(4);
@@ -681,6 +727,8 @@ void RGBDOdometry::callbackRGBD5(
const rtabmap_msgs::msg::RGBDImage::ConstSharedPtr image4,
const rtabmap_msgs::msg::RGBDImage::ConstSharedPtr image5)
{
+ tick(image->header.stamp);
+
if(!this->isPaused())
{
std::vector imageMsgs(5);
@@ -709,6 +757,8 @@ void RGBDOdometry::callbackRGBD6(
const rtabmap_msgs::msg::RGBDImage::ConstSharedPtr image5,
const rtabmap_msgs::msg::RGBDImage::ConstSharedPtr image6)
{
+ tick(image->header.stamp);
+
if(!this->isPaused())
{
std::vector imageMsgs(6);
@@ -737,20 +787,20 @@ void RGBDOdometry::flushCallbacks()
if(approxSync_)
{
delete approxSync_;
- approxSync_ = new message_filters::Synchronizer(MyApproxSyncPolicy(queueSize_), image_mono_sub_, image_depth_sub_, info_sub_);
+ approxSync_ = new message_filters::Synchronizer(MyApproxSyncPolicy(syncQueueSize_), image_mono_sub_, image_depth_sub_, info_sub_);
approxSync_->registerCallback(std::bind(&RGBDOdometry::callback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
}
if(exactSync_)
{
delete exactSync_;
- exactSync_ = new message_filters::Synchronizer(MyExactSyncPolicy(queueSize_), image_mono_sub_, image_depth_sub_, info_sub_);
+ exactSync_ = new message_filters::Synchronizer(MyExactSyncPolicy(syncQueueSize_), image_mono_sub_, image_depth_sub_, info_sub_);
exactSync_->registerCallback(std::bind(&RGBDOdometry::callback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
}
if(approxSync2_)
{
delete approxSync2_;
approxSync2_ = new message_filters::Synchronizer(
- MyApproxSync2Policy(queueSize_),
+ MyApproxSync2Policy(syncQueueSize_),
rgbd_image1_sub_,
rgbd_image2_sub_);
approxSync2_->registerCallback(std::bind(&RGBDOdometry::callbackRGBD2, this, std::placeholders::_1, std::placeholders::_2));
@@ -759,7 +809,7 @@ void RGBDOdometry::flushCallbacks()
{
delete exactSync2_;
exactSync2_ = new message_filters::Synchronizer(
- MyExactSync2Policy(queueSize_),
+ MyExactSync2Policy(syncQueueSize_),
rgbd_image1_sub_,
rgbd_image2_sub_);
exactSync2_->registerCallback(std::bind(&RGBDOdometry::callbackRGBD2, this, std::placeholders::_1, std::placeholders::_2));
@@ -768,7 +818,7 @@ void RGBDOdometry::flushCallbacks()
{
delete approxSync3_;
approxSync3_ = new message_filters::Synchronizer(
- MyApproxSync3Policy(queueSize_),
+ MyApproxSync3Policy(syncQueueSize_),
rgbd_image1_sub_,
rgbd_image2_sub_,
rgbd_image3_sub_);
@@ -778,7 +828,7 @@ void RGBDOdometry::flushCallbacks()
{
delete exactSync3_;
exactSync3_ = new message_filters::Synchronizer(
- MyExactSync3Policy(queueSize_),
+ MyExactSync3Policy(syncQueueSize_),
rgbd_image1_sub_,
rgbd_image2_sub_,
rgbd_image3_sub_);
@@ -788,7 +838,7 @@ void RGBDOdometry::flushCallbacks()
{
delete approxSync4_;
approxSync4_ = new message_filters::Synchronizer(
- MyApproxSync4Policy(queueSize_),
+ MyApproxSync4Policy(syncQueueSize_),
rgbd_image1_sub_,
rgbd_image2_sub_,
rgbd_image3_sub_,
@@ -799,7 +849,7 @@ void RGBDOdometry::flushCallbacks()
{
delete exactSync4_;
exactSync4_ = new message_filters::Synchronizer(
- MyExactSync4Policy(queueSize_),
+ MyExactSync4Policy(syncQueueSize_),
rgbd_image1_sub_,
rgbd_image2_sub_,
rgbd_image3_sub_,
@@ -810,7 +860,7 @@ void RGBDOdometry::flushCallbacks()
{
delete approxSync5_;
approxSync5_ = new message_filters::Synchronizer(
- MyApproxSync5Policy(queueSize_),
+ MyApproxSync5Policy(syncQueueSize_),
rgbd_image1_sub_,
rgbd_image2_sub_,
rgbd_image3_sub_,
@@ -822,7 +872,7 @@ void RGBDOdometry::flushCallbacks()
{
delete exactSync5_;
exactSync5_ = new message_filters::Synchronizer(
- MyExactSync5Policy(queueSize_),
+ MyExactSync5Policy(syncQueueSize_),
rgbd_image1_sub_,
rgbd_image2_sub_,
rgbd_image3_sub_,
@@ -834,7 +884,7 @@ void RGBDOdometry::flushCallbacks()
{
delete approxSync6_;
approxSync6_ = new message_filters::Synchronizer(
- MyApproxSync6Policy(queueSize_),
+ MyApproxSync6Policy(syncQueueSize_),
rgbd_image1_sub_,
rgbd_image2_sub_,
rgbd_image3_sub_,
@@ -847,7 +897,7 @@ void RGBDOdometry::flushCallbacks()
{
delete exactSync6_;
exactSync6_ = new message_filters::Synchronizer(
- MyExactSync6Policy(queueSize_),
+ MyExactSync6Policy(syncQueueSize_),
rgbd_image1_sub_,
rgbd_image2_sub_,
rgbd_image3_sub_,
diff --git a/rtabmap_odom/src/nodelets/stereo_odometry.cpp b/rtabmap_odom/src/nodelets/stereo_odometry.cpp
index c249ac40..c649f033 100644
--- a/rtabmap_odom/src/nodelets/stereo_odometry.cpp
+++ b/rtabmap_odom/src/nodelets/stereo_odometry.cpp
@@ -29,7 +29,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include
+#ifdef PRE_ROS_IRON
#include
+#else
+#include
+#endif
#include "rtabmap_conversions/MsgConversion.h"
#include
@@ -59,7 +63,8 @@ StereoOdometry::StereoOdometry(const rclcpp::NodeOptions & options) :
exactSync5_(0),
approxSync6_(0),
exactSync6_(0),
- queueSize_(5),
+ topicQueueSize_(10),
+ syncQueueSize_(5),
keepColor_(false)
{
OdometryROS::init(true, true, false);
@@ -89,7 +94,17 @@ void StereoOdometry::onOdomInit()
int rgbdCameras = 1;
approxSync = this->declare_parameter("approx_sync", approxSync);
approxSyncMaxInterval = this->declare_parameter("approx_sync_max_interval", approxSyncMaxInterval);
- queueSize_ = this->declare_parameter("queue_size", queueSize_);
+ topicQueueSize_ = this->declare_parameter("topic_queue_size", topicQueueSize_);
+ int queueSize = this->declare_parameter("queue_size", -1);
+ if(queueSize != -1)
+ {
+ syncQueueSize_ = queueSize;
+ RCLCPP_WARN(this->get_logger(), "Parameter \"queue_size\" has been renamed "
+ "to \"sync_queue_size\" and will be removed "
+ "in future versions! The value (%d) is copied to "
+ "\"sync_queue_size\".", syncQueueSize_);
+ }
+ syncQueueSize_ = this->declare_parameter("sync_queue_size", syncQueueSize_);
int qosCamInfo = this->declare_parameter("qos_camera_info", (int)qos());
subscribeRGBD = this->declare_parameter("subscribe_rgbd", subscribeRGBD);
rgbdCameras = this->declare_parameter("rgbd_cameras", rgbdCameras);
@@ -98,35 +113,39 @@ void StereoOdometry::onOdomInit()
RCLCPP_INFO(this->get_logger(), "StereoOdometry: approx_sync = %s", approxSync?"true":"false");
if(approxSync)
RCLCPP_INFO(this->get_logger(), "StereoOdometry: approx_sync_max_interval = %f", approxSyncMaxInterval);
- RCLCPP_INFO(this->get_logger(), "StereoOdometry: queue_size = %d", queueSize_);
- RCLCPP_INFO(this->get_logger(), "RGBDOdometry: qos = %d", (int)qos());
- RCLCPP_INFO(this->get_logger(), "RGBDOdometry: qos_camera_info = %d", qosCamInfo);
+ RCLCPP_INFO(this->get_logger(), "StereoOdometry: topic_queue_size = %d", topicQueueSize_);
+ RCLCPP_INFO(this->get_logger(), "StereoOdometry: sync_queue_size = %d", syncQueueSize_);
+ RCLCPP_INFO(this->get_logger(), "StereoOdometry: qos = %d", (int)qos());
+ RCLCPP_INFO(this->get_logger(), "StereoOdometry: qos_camera_info = %d", qosCamInfo);
RCLCPP_INFO(this->get_logger(), "StereoOdometry: subscribe_rgbd = %s", subscribeRGBD?"true":"false");
RCLCPP_INFO(this->get_logger(), "StereoOdometry: keep_color = %s", keepColor_?"true":"false");
+ rclcpp::SubscriptionOptions options;
+ options.callback_group = dataCallbackGroup_;
+
std::string subscribedTopic;
std::string subscribedTopicsMsg;
if(subscribeRGBD)
{
if(rgbdCameras >= 2)
{
- rgbd_image1_sub_.subscribe(this, "rgbd_image0", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos()).get_rmw_qos_profile());
- rgbd_image2_sub_.subscribe(this, "rgbd_image1", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos()).get_rmw_qos_profile());
+ rgbd_image1_sub_.subscribe(this, "rgbd_image0", rclcpp::QoS(topicQueueSize_).reliability((rmw_qos_reliability_policy_t)qos()).get_rmw_qos_profile(), options);
+ rgbd_image2_sub_.subscribe(this, "rgbd_image1", rclcpp::QoS(topicQueueSize_).reliability((rmw_qos_reliability_policy_t)qos()).get_rmw_qos_profile(), options);
if(rgbdCameras >= 3)
{
- rgbd_image3_sub_.subscribe(this, "rgbd_image2", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos()).get_rmw_qos_profile());
+ rgbd_image3_sub_.subscribe(this, "rgbd_image2", rclcpp::QoS(topicQueueSize_).reliability((rmw_qos_reliability_policy_t)qos()).get_rmw_qos_profile(), options);
}
if(rgbdCameras >= 4)
{
- rgbd_image4_sub_.subscribe(this, "rgbd_image3", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos()).get_rmw_qos_profile());
+ rgbd_image4_sub_.subscribe(this, "rgbd_image3", rclcpp::QoS(topicQueueSize_).reliability((rmw_qos_reliability_policy_t)qos()).get_rmw_qos_profile(), options);
}
if(rgbdCameras >= 5)
{
- rgbd_image5_sub_.subscribe(this, "rgbd_image4", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos()).get_rmw_qos_profile());
+ rgbd_image5_sub_.subscribe(this, "rgbd_image4", rclcpp::QoS(topicQueueSize_).reliability((rmw_qos_reliability_policy_t)qos()).get_rmw_qos_profile(), options);
}
if(rgbdCameras >= 6)
{
- rgbd_image6_sub_.subscribe(this, "rgbd_image5", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos()).get_rmw_qos_profile());
+ rgbd_image6_sub_.subscribe(this, "rgbd_image5", rclcpp::QoS(topicQueueSize_).reliability((rmw_qos_reliability_policy_t)qos()).get_rmw_qos_profile(), options);
}
if(rgbdCameras == 2)
@@ -134,7 +153,7 @@ void StereoOdometry::onOdomInit()
if(approxSync)
{
approxSync2_ = new message_filters::Synchronizer(
- MyApproxSync2Policy(queueSize_),
+ MyApproxSync2Policy(syncQueueSize_),
rgbd_image1_sub_,
rgbd_image2_sub_);
if(approxSyncMaxInterval > 0.0)
@@ -144,7 +163,7 @@ void StereoOdometry::onOdomInit()
else
{
exactSync2_ = new message_filters::Synchronizer(
- MyExactSync2Policy(queueSize_),
+ MyExactSync2Policy(syncQueueSize_),
rgbd_image1_sub_,
rgbd_image2_sub_);
exactSync2_->registerCallback(std::bind(&StereoOdometry::callbackRGBD2, this, std::placeholders::_1, std::placeholders::_2));
@@ -161,7 +180,7 @@ void StereoOdometry::onOdomInit()
if(approxSync)
{
approxSync3_ = new message_filters::Synchronizer(
- MyApproxSync3Policy(queueSize_),
+ MyApproxSync3Policy(syncQueueSize_),
rgbd_image1_sub_,
rgbd_image2_sub_,
rgbd_image3_sub_);
@@ -172,7 +191,7 @@ void StereoOdometry::onOdomInit()
else
{
exactSync3_ = new message_filters::Synchronizer(
- MyExactSync3Policy(queueSize_),
+ MyExactSync3Policy(syncQueueSize_),
rgbd_image1_sub_,
rgbd_image2_sub_,
rgbd_image3_sub_);
@@ -191,7 +210,7 @@ void StereoOdometry::onOdomInit()
if(approxSync)
{
approxSync4_ = new message_filters::Synchronizer(
- MyApproxSync4Policy(queueSize_),
+ MyApproxSync4Policy(syncQueueSize_),
rgbd_image1_sub_,
rgbd_image2_sub_,
rgbd_image3_sub_,
@@ -203,7 +222,7 @@ void StereoOdometry::onOdomInit()
else
{
exactSync4_ = new message_filters::Synchronizer(
- MyExactSync4Policy(queueSize_),
+ MyExactSync4Policy(syncQueueSize_),
rgbd_image1_sub_,
rgbd_image2_sub_,
rgbd_image3_sub_,
@@ -224,7 +243,7 @@ void StereoOdometry::onOdomInit()
if(approxSync)
{
approxSync5_ = new message_filters::Synchronizer(
- MyApproxSync5Policy(queueSize_),
+ MyApproxSync5Policy(syncQueueSize_),
rgbd_image1_sub_,
rgbd_image2_sub_,
rgbd_image3_sub_,
@@ -237,7 +256,7 @@ void StereoOdometry::onOdomInit()
else
{
exactSync5_ = new message_filters::Synchronizer(
- MyExactSync5Policy(queueSize_),
+ MyExactSync5Policy(syncQueueSize_),
rgbd_image1_sub_,
rgbd_image2_sub_,
rgbd_image3_sub_,
@@ -260,7 +279,7 @@ void StereoOdometry::onOdomInit()
if(approxSync)
{
approxSync6_ = new message_filters::Synchronizer(
- MyApproxSync6Policy(queueSize_),
+ MyApproxSync6Policy(syncQueueSize_),
rgbd_image1_sub_,
rgbd_image2_sub_,
rgbd_image3_sub_,
@@ -274,7 +293,7 @@ void StereoOdometry::onOdomInit()
else
{
exactSync6_ = new message_filters::Synchronizer(
- MyExactSync6Policy(queueSize_),
+ MyExactSync6Policy(syncQueueSize_),
rgbd_image1_sub_,
rgbd_image2_sub_,
rgbd_image3_sub_,
@@ -307,7 +326,7 @@ void StereoOdometry::onOdomInit()
}
else if(rgbdCameras == 0)
{
- rgbdxSub_ = create_subscription("rgbd_images", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos()), std::bind(&StereoOdometry::callbackRGBDX, this, std::placeholders::_1));
+ rgbdxSub_ = create_subscription("rgbd_images", rclcpp::QoS(topicQueueSize_).reliability((rmw_qos_reliability_policy_t)qos()), std::bind(&StereoOdometry::callbackRGBDX, this, std::placeholders::_1), options);
subscribedTopic = rgbdxSub_->get_topic_name();
subscribedTopicsMsg =
@@ -317,7 +336,7 @@ void StereoOdometry::onOdomInit()
}
else
{
- rgbdSub_ = create_subscription("rgbd_image", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos()), std::bind(&StereoOdometry::callbackRGBD, this, std::placeholders::_1));
+ rgbdSub_ = create_subscription("rgbd_image", rclcpp::QoS(topicQueueSize_).reliability((rmw_qos_reliability_policy_t)qos()), std::bind(&StereoOdometry::callbackRGBD, this, std::placeholders::_1), options);
subscribedTopic = rgbdSub_->get_topic_name();
subscribedTopicsMsg =
@@ -329,29 +348,31 @@ void StereoOdometry::onOdomInit()
else
{
image_transport::TransportHints hints(this);
- imageRectLeft_.subscribe(this, "left/image_rect", hints.getTransport(), rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos()).get_rmw_qos_profile());
- imageRectRight_.subscribe(this, "right/image_rect", hints.getTransport(), rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qos()).get_rmw_qos_profile());
- cameraInfoLeft_.subscribe(this, "left/camera_info", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qosCamInfo).get_rmw_qos_profile());
- cameraInfoRight_.subscribe(this, "right/camera_info", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)qosCamInfo).get_rmw_qos_profile());
+ imageRectLeft_.subscribe(this, "left/image_rect", hints.getTransport(), rclcpp::QoS(topicQueueSize_).reliability((rmw_qos_reliability_policy_t)qos()).get_rmw_qos_profile(), options);
+ imageRectRight_.subscribe(this, "right/image_rect", hints.getTransport(), rclcpp::QoS(topicQueueSize_).reliability((rmw_qos_reliability_policy_t)qos()).get_rmw_qos_profile(), options);
+ cameraInfoLeft_.subscribe(this, "left/camera_info", rclcpp::QoS(topicQueueSize_).reliability((rmw_qos_reliability_policy_t)qosCamInfo).get_rmw_qos_profile(), options);
+ cameraInfoRight_.subscribe(this, "right/camera_info", rclcpp::QoS(topicQueueSize_).reliability((rmw_qos_reliability_policy_t)qosCamInfo).get_rmw_qos_profile(), options);
if(approxSync)
{
- approxSync_ = new message_filters::Synchronizer(MyApproxSyncPolicy(queueSize_), imageRectLeft_, imageRectRight_, cameraInfoLeft_, cameraInfoRight_);
+ approxSync_ = new message_filters::Synchronizer(MyApproxSyncPolicy(syncQueueSize_), imageRectLeft_, imageRectRight_, cameraInfoLeft_, cameraInfoRight_);
if(approxSyncMaxInterval>0.0)
approxSync_->setMaxIntervalDuration(rclcpp::Duration::from_seconds(approxSyncMaxInterval));
approxSync_->registerCallback(std::bind(&StereoOdometry::callback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
}
else
{
- exactSync_ = new message_filters::Synchronizer(MyExactSyncPolicy(queueSize_), imageRectLeft_, imageRectRight_, cameraInfoLeft_, cameraInfoRight_);
+ exactSync_ = new message_filters::Synchronizer(MyExactSyncPolicy(syncQueueSize_), imageRectLeft_, imageRectRight_, cameraInfoLeft_, cameraInfoRight_);
exactSync_->registerCallback(std::bind(&StereoOdometry::callback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
}
subscribedTopic = imageRectLeft_.getTopic();
- subscribedTopicsMsg = uFormat("\n%s subscribed to (%s sync%s):\n %s \\\n %s \\\n %s \\\n %s",
+ subscribedTopicsMsg = uFormat("\n%s subscribed to (%s sync%s, topic_queue_size=%d, sync_queue_size=%d):\n %s \\\n %s \\\n %s \\\n %s",
get_name(),
approxSync?"approx":"exact",
approxSync&&approxSyncMaxInterval!=0.0?uFormat(", max interval=%fs", approxSyncMaxInterval).c_str():"",
+ topicQueueSize_,
+ syncQueueSize_,
imageRectLeft_.getTopic().c_str(),
imageRectRight_.getTopic().c_str(),
cameraInfoLeft_.getSubscriber()->get_topic_name(),
@@ -693,6 +714,8 @@ void StereoOdometry::callback(
const sensor_msgs::msg::CameraInfo::ConstSharedPtr cameraInfoLeft,
const sensor_msgs::msg::CameraInfo::ConstSharedPtr cameraInfoRight)
{
+ tick(imageRectLeft->header.stamp);
+
if(!this->isPaused())
{
std::vector leftMsgs(1);
@@ -723,6 +746,8 @@ void StereoOdometry::callback(
void StereoOdometry::callbackRGBD(
const rtabmap_msgs::msg::RGBDImage::ConstSharedPtr image)
{
+ tick(image->header.stamp);
+
if(!this->isPaused())
{
std::vector leftMsgs(1);
@@ -740,6 +765,8 @@ void StereoOdometry::callbackRGBD(
void StereoOdometry::callbackRGBDX(
const rtabmap_msgs::msg::RGBDImages::ConstSharedPtr images)
{
+ tick(images->header.stamp);
+
if(!this->isPaused())
{
if(images->rgbd_images.empty())
@@ -766,6 +793,8 @@ void StereoOdometry::callbackRGBD2(
const rtabmap_msgs::msg::RGBDImage::ConstSharedPtr image,
const rtabmap_msgs::msg::RGBDImage::ConstSharedPtr image2)
{
+ tick(image->header.stamp);
+
if(!this->isPaused())
{
std::vector leftMsgs(2);
@@ -788,6 +817,8 @@ void StereoOdometry::callbackRGBD3(
const rtabmap_msgs::msg::RGBDImage::ConstSharedPtr image2,
const rtabmap_msgs::msg::RGBDImage::ConstSharedPtr image3)
{
+ tick(image->header.stamp);
+
if(!this->isPaused())
{
std::vector leftMsgs(3);
@@ -814,6 +845,8 @@ void StereoOdometry::callbackRGBD4(
const rtabmap_msgs::msg::RGBDImage::ConstSharedPtr image3,
const rtabmap_msgs::msg::RGBDImage::ConstSharedPtr image4)
{
+ tick(image->header.stamp);
+
if(!this->isPaused())
{
std::vector leftMsgs(4);
@@ -844,6 +877,8 @@ void StereoOdometry::callbackRGBD5(
const rtabmap_msgs::msg::RGBDImage::ConstSharedPtr image4,
const rtabmap_msgs::msg::RGBDImage::ConstSharedPtr image5)
{
+ tick(image->header.stamp);
+
if(!this->isPaused())
{
std::vector leftMsgs(5);
@@ -878,6 +913,8 @@ void StereoOdometry::callbackRGBD6(
const rtabmap_msgs::msg::RGBDImage::ConstSharedPtr image5,
const rtabmap_msgs::msg::RGBDImage::ConstSharedPtr image6)
{
+ tick(image->header.stamp);
+
if(!this->isPaused())
{
std::vector leftMsgs(6);
@@ -913,20 +950,20 @@ void StereoOdometry::flushCallbacks()
if(approxSync_)
{
delete approxSync_;
- approxSync_ = new message_filters::Synchronizer(MyApproxSyncPolicy(queueSize_), imageRectLeft_, imageRectRight_, cameraInfoLeft_, cameraInfoRight_);
+ approxSync_ = new message_filters::Synchronizer(MyApproxSyncPolicy(syncQueueSize_), imageRectLeft_, imageRectRight_, cameraInfoLeft_, cameraInfoRight_);
approxSync_->registerCallback(std::bind(&StereoOdometry::callback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
}
if(exactSync_)
{
delete exactSync_;
- exactSync_ = new message_filters::Synchronizer(MyExactSyncPolicy(queueSize_), imageRectLeft_, imageRectRight_, cameraInfoLeft_, cameraInfoRight_);
+ exactSync_ = new message_filters::Synchronizer(MyExactSyncPolicy(syncQueueSize_), imageRectLeft_, imageRectRight_, cameraInfoLeft_, cameraInfoRight_);
exactSync_->registerCallback(std::bind(&StereoOdometry::callback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
}
if(approxSync2_)
{
delete approxSync2_;
approxSync2_ = new message_filters::Synchronizer(
- MyApproxSync2Policy(queueSize_),
+ MyApproxSync2Policy(syncQueueSize_),
rgbd_image1_sub_,
rgbd_image2_sub_);
approxSync2_->registerCallback(std::bind(&StereoOdometry::callbackRGBD2, this, std::placeholders::_1, std::placeholders::_2));
@@ -935,7 +972,7 @@ void StereoOdometry::flushCallbacks()
{
delete exactSync2_;
exactSync2_ = new message_filters::Synchronizer(
- MyExactSync2Policy(queueSize_),
+ MyExactSync2Policy(syncQueueSize_),
rgbd_image1_sub_,
rgbd_image2_sub_);
exactSync2_->registerCallback(std::bind(&StereoOdometry::callbackRGBD2, this, std::placeholders::_1, std::placeholders::_2));
@@ -944,7 +981,7 @@ void StereoOdometry::flushCallbacks()
{
delete approxSync3_;
approxSync3_ = new message_filters::Synchronizer(
- MyApproxSync3Policy(queueSize_),
+ MyApproxSync3Policy(syncQueueSize_),
rgbd_image1_sub_,
rgbd_image2_sub_,
rgbd_image3_sub_);
@@ -954,7 +991,7 @@ void StereoOdometry::flushCallbacks()
{
delete exactSync3_;
exactSync3_ = new message_filters::Synchronizer(
- MyExactSync3Policy(queueSize_),
+ MyExactSync3Policy(syncQueueSize_),
rgbd_image1_sub_,
rgbd_image2_sub_,
rgbd_image3_sub_);
@@ -964,7 +1001,7 @@ void StereoOdometry::flushCallbacks()
{
delete approxSync4_;
approxSync4_ = new message_filters::Synchronizer(
- MyApproxSync4Policy(queueSize_),
+ MyApproxSync4Policy(syncQueueSize_),
rgbd_image1_sub_,
rgbd_image2_sub_,
rgbd_image3_sub_,
@@ -975,7 +1012,7 @@ void StereoOdometry::flushCallbacks()
{
delete exactSync4_;
exactSync4_ = new message_filters::Synchronizer(
- MyExactSync4Policy(queueSize_),
+ MyExactSync4Policy(syncQueueSize_),
rgbd_image1_sub_,
rgbd_image2_sub_,
rgbd_image3_sub_,
@@ -986,7 +1023,7 @@ void StereoOdometry::flushCallbacks()
{
delete approxSync5_;
approxSync5_ = new message_filters::Synchronizer(
- MyApproxSync5Policy(queueSize_),
+ MyApproxSync5Policy(syncQueueSize_),
rgbd_image1_sub_,
rgbd_image2_sub_,
rgbd_image3_sub_,
@@ -998,7 +1035,7 @@ void StereoOdometry::flushCallbacks()
{
delete exactSync5_;
exactSync5_ = new message_filters::Synchronizer(
- MyExactSync5Policy(queueSize_),
+ MyExactSync5Policy(syncQueueSize_),
rgbd_image1_sub_,
rgbd_image2_sub_,
rgbd_image3_sub_,
@@ -1010,7 +1047,7 @@ void StereoOdometry::flushCallbacks()
{
delete approxSync6_;
approxSync6_ = new message_filters::Synchronizer(
- MyApproxSync6Policy(queueSize_),
+ MyApproxSync6Policy(syncQueueSize_),
rgbd_image1_sub_,
rgbd_image2_sub_,
rgbd_image3_sub_,
@@ -1023,7 +1060,7 @@ void StereoOdometry::flushCallbacks()
{
delete exactSync6_;
exactSync6_ = new message_filters::Synchronizer(
- MyExactSync6Policy(queueSize_),
+ MyExactSync6Policy(syncQueueSize_),
rgbd_image1_sub_,
rgbd_image2_sub_,
rgbd_image3_sub_,
diff --git a/rtabmap_python/package.xml b/rtabmap_python/package.xml
index a4e6eb39..d56a425c 100644
--- a/rtabmap_python/package.xml
+++ b/rtabmap_python/package.xml
@@ -2,7 +2,7 @@
rtabmap_python
- 0.21.5
+ 0.22.0
RTAB-Map's python package.
Mathieu Labbe
Mathieu Labbe
diff --git a/rtabmap_ros/package.xml b/rtabmap_ros/package.xml
index 316d805f..2da81cf3 100644
--- a/rtabmap_ros/package.xml
+++ b/rtabmap_ros/package.xml
@@ -2,7 +2,7 @@
rtabmap_ros
- 0.21.5
+ 0.22.0
RTAB-Map Stack
diff --git a/rtabmap_rviz_plugins/CMakeLists.txt b/rtabmap_rviz_plugins/CMakeLists.txt
index 95cf2f69..c1808ebe 100644
--- a/rtabmap_rviz_plugins/CMakeLists.txt
+++ b/rtabmap_rviz_plugins/CMakeLists.txt
@@ -5,6 +5,15 @@ if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
+if(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")
+ # issues #1285 #1288
+ find_library(
+ message_filters_LIB NAMES message_filters
+ PATHS "/opt/ros/$ENV{ROS_DISTRO}/lib"
+ NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH REQUIRED
+ )
+endif()
+
find_package(ament_cmake_ros REQUIRED)
find_package(pcl_conversions REQUIRED)
find_package(pluginlib REQUIRED)
@@ -46,21 +55,6 @@ MESSAGE(STATUS "rtabmap_conversions=${rtabmap_conversions_LIBRARIES}")
## We also use Ogre for rviz plugins
include_directories( ${OGRE_INCLUDE_DIRS} )
-## RVIZ plugin
-IF(QT4_FOUND)
- qt4_wrap_cpp(MOC_FILES
- include/${PROJECT_NAME}/MapCloudDisplay.h
- include/${PROJECT_NAME}/MapGraphDisplay.h
- include/${PROJECT_NAME}/InfoDisplay.h
- )
-ELSE()
- qt5_wrap_cpp(MOC_FILES
- include/${PROJECT_NAME}/MapCloudDisplay.h
- include/${PROJECT_NAME}/MapGraphDisplay.h
- include/${PROJECT_NAME}/InfoDisplay.h
- )
-ENDIF()
-
# tf:message_filters, mixing boost and Qt signals
set_property(
SOURCE src/MapCloudDisplay.cpp src/MapGraphDisplay.cpp src/InfoDisplay.cpp src/OrbitOrientedViewController.cpp
@@ -71,8 +65,11 @@ add_library(rtabmap_rviz_plugins SHARED
src/MapCloudDisplay.cpp
src/MapGraphDisplay.cpp
src/InfoDisplay.cpp
- ${MOC_FILES}
+ include/${PROJECT_NAME}/MapCloudDisplay.h
+ include/${PROJECT_NAME}/MapGraphDisplay.h
+ include/${PROJECT_NAME}/InfoDisplay.h
)
+set_property(TARGET rtabmap_rviz_plugins PROPERTY AUTOMOC ON)
target_include_directories(rtabmap_rviz_plugins
PUBLIC
$
@@ -81,10 +78,6 @@ target_include_directories(rtabmap_rviz_plugins
ament_target_dependencies(rtabmap_rviz_plugins ${Libraries})
-IF(Qt5_FOUND)
- QT5_USE_MODULES(rtabmap_rviz_plugins Widgets Core Gui)
-ENDIF(Qt5_FOUND)
-
# Causes the visibility macros to use dllexport rather than dllimport,
# which is appropriate when building the dll but not consuming it.
target_compile_definitions(rtabmap_rviz_plugins PRIVATE "RTABMAP_ROS_BUILDING_LIBRARY")
@@ -111,6 +104,4 @@ install(TARGETS
INCLUDES DESTINATION include
)
-pluginlib_export_plugin_description_file(rviz_common rviz_plugins.xml)
-
ament_package()
diff --git a/rtabmap_rviz_plugins/include/rtabmap_rviz_plugins/MapCloudDisplay.h b/rtabmap_rviz_plugins/include/rtabmap_rviz_plugins/MapCloudDisplay.h
index d3f9f4a9..1a9ef89d 100644
--- a/rtabmap_rviz_plugins/include/rtabmap_rviz_plugins/MapCloudDisplay.h
+++ b/rtabmap_rviz_plugins/include/rtabmap_rviz_plugins/MapCloudDisplay.h
@@ -175,6 +175,7 @@ private:
void fillTransformerOptions( rviz_common::properties::EnumProperty* prop, uint32_t mask );
private:
+ std::shared_ptr clientNode_;
rclcpp::Publisher::SharedPtr republishNodeDataPub_;
std::map cloud_infos_;
diff --git a/rtabmap_rviz_plugins/package.xml b/rtabmap_rviz_plugins/package.xml
index 29f8660a..ada5c080 100644
--- a/rtabmap_rviz_plugins/package.xml
+++ b/rtabmap_rviz_plugins/package.xml
@@ -2,7 +2,7 @@
rtabmap_rviz_plugins
- 0.21.5
+ 0.22.0
RTAB-Map's rviz plugins.
Mathieu Labbe
Mathieu Labbe
@@ -12,6 +12,8 @@
ament_cmake_ros
+ ros_environment
+
pcl_conversions
pluginlib
rclcpp
diff --git a/rtabmap_rviz_plugins/src/MapCloudDisplay.cpp b/rtabmap_rviz_plugins/src/MapCloudDisplay.cpp
index 87dc50fa..b66b6b31 100644
--- a/rtabmap_rviz_plugins/src/MapCloudDisplay.cpp
+++ b/rtabmap_rviz_plugins/src/MapCloudDisplay.cpp
@@ -57,7 +57,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include
#include
-
namespace rtabmap_rviz_plugins
{
@@ -97,6 +96,10 @@ MapCloudDisplay::MapCloudDisplay()
//QIcon icon;
//this->setIcon(icon);
+ auto options = rclcpp::NodeOptions().arguments(
+ {"--ros-args", "--remap", "__node:=rviz_map_cloud_action_client", "--"});
+ clientNode_ = std::make_shared("_", options);
+
style_property_ = new rviz_common::properties::EnumProperty( "Style", "Flat Squares",
"Rendering mode to use, in order of computational complexity.",
this, SLOT( updateStyle() ), this );
@@ -316,7 +319,8 @@ void MapCloudDisplay::processMapData(const rtabmap_msgs::msg::MapData& map)
cloud_filter_floor_height_->getFloat()!=0.0f?cloud_filter_floor_height_->getFloat():-999.0f,
cloud_filter_ceiling_height_->getFloat()!=0.0f && (cloud_filter_floor_height_->getFloat()==0.0f || cloud_filter_ceiling_height_->getFloat()>cloud_filter_floor_height_->getFloat())?cloud_filter_ceiling_height_->getFloat():999.0f);
// convert back in /base_link frame
- cloud = rtabmap::util3d::transformPointCloud(cloud, s.getPose().inverse());
+ if(!cloud->empty())
+ cloud = rtabmap::util3d::transformPointCloud(cloud, s.getPose().inverse());
}
if(!cloud->empty())
@@ -500,64 +504,64 @@ void MapCloudDisplay::updateCloudParameters()
fromScan_ = cloud_from_scan_->getBool();
}
-void MapCloudDisplay::downloadMap(bool /*graphOnly*/)
+void MapCloudDisplay::downloadMap(bool graphOnly)
{
- RCLCPP_ERROR(rviz_ros_node_.lock()->get_raw_node()->get_logger(), "MapCloud plugin: DownloadMap still not working on ros2");
- return;
- // FIXME: ros2: can connect to client, rtabmap returns data but the callback here is never called?!
- /*
auto request = std::make_shared();
request->global_map = false;
request->optimized = true;
request->graph_only = graphOnly;
std::string rtabmapNs = download_namespace->getStdString();
- std::string srvName = uFormat("%s/get_map_data", rtabmapNs.c_str());
-// QMessageBox * messageBox = new QMessageBox(
-// QMessageBox::NoIcon,
-// tr("Calling \"%1\" service...").arg(srvName.c_str()),
-// tr("Downloading the map... please wait (rviz could become gray!)"),
-// QMessageBox::NoButton);
-// messageBox->setAttribute(Qt::WA_DeleteOnClose, true);
-// messageBox->show();
-// QApplication::processEvents();
-// uSleep(100); // hack make sure the text in the QMessageBox is shown...
-// QApplication::processEvents();
+ std::string srvName = rtabmapNs+"/get_map_data";
+ QMessageBox * messageBox = new QMessageBox(
+ QMessageBox::NoIcon,
+ tr("Calling \"%1\" service...").arg(srvName.c_str()),
+ tr("Downloading the map... please wait (rviz could become gray!)"),
+ QMessageBox::NoButton,
+ getAssociatedWidget());
+ messageBox->setAttribute(Qt::WA_DeleteOnClose, true);
+ messageBox->show();
+ QApplication::processEvents();
+ uSleep(100); // hack make sure the text in the QMessageBox is shown...
+ QApplication::processEvents();
- RVIZ_COMMON_LOG_WARNING(uFormat("Wait for service %s", srvName.c_str()));
- auto client = rviz_ros_node_.lock()->get_raw_node()->create_client(srvName);
+ RVIZ_COMMON_LOG_INFO(uFormat("Wait for service %s", srvName.c_str()));
+
+ auto client = clientNode_->create_client(srvName);
if(client->wait_for_service(std::chrono::seconds(1)))
{
- using ServiceResponseFuture = rclcpp::Client::SharedFuture;
- auto response_received_callback = [this, &graphOnly](ServiceResponseFuture future) {
- auto result = future.get();
- RVIZ_COMMON_LOG_WARNING(uFormat("Process data"));
+ RVIZ_COMMON_LOG_INFO(uFormat("Calling service %s", srvName.c_str()));
+ auto result = client->async_send_request(request);
+ if (rclcpp::spin_until_future_complete(clientNode_, result) ==
+ rclcpp::FutureReturnCode::SUCCESS)
+ {
+ RVIZ_COMMON_LOG_INFO(uFormat("Process data"));
+ auto future = result.get();
if(graphOnly)
{
- //messageBox->setText(tr("Updating the map (%1 nodes downloaded)...").arg(result->data.graph.poses.size()));
- //QApplication::processEvents();
- processMapData(result->data);
- //messageBox->setText(tr("Updating the map (%1 nodes downloaded)... done!").arg(result->data.graph.poses.size()));
-
- // QTimer::singleShot(1000, messageBox, SLOT(close()));
+ messageBox->setText(tr("Updating the map (%1 nodes downloaded)...").arg(future->data.graph.poses.size()));
+ QApplication::processEvents();
+ processMapData(future->data);
+ messageBox->setText(tr("Updating the map (%1 nodes downloaded)... done!").arg(future->data.graph.poses.size()));
+ QApplication::processEvents();
+ QTimer::singleShot(1000, messageBox, SLOT(close()));
}
else
{
- //messageBox->setText(tr("Creating all clouds (%1 poses and %2 clouds downloaded)...")
- // .arg(result->data.graph.poses.size()).arg(result->data.nodes.size()));
- //QApplication::processEvents();
+ messageBox->setText(tr("Creating all clouds (%1 poses and %2 clouds downloaded)...")
+ .arg(future->data.graph.poses.size()).arg(future->data.nodes.size()));
+ QApplication::processEvents();
this->reset();
- processMapData(result->data);
- //messageBox->setText(tr("Creating all clouds (%1 poses and %2 clouds downloaded)... done!")
- // .arg(result->data.graph.poses.size()).arg(result->data.nodes.size()));
+ processMapData(future->data);
+ messageBox->setText(tr("Creating all clouds (%1 poses and %2 clouds downloaded)... done!")
+ .arg(future->data.graph.poses.size()).arg(future->data.nodes.size()));
- // QTimer::singleShot(1000, messageBox, SLOT(close()));
+ QTimer::singleShot(1000, messageBox, SLOT(close()));
}
- };
- RVIZ_COMMON_LOG_WARNING(uFormat("Calling service %s", srvName.c_str()));
- auto result_future = client->async_send_request(request, response_received_callback);
- RVIZ_COMMON_LOG_WARNING(uFormat("Wait"));
- result_future.wait();
- RVIZ_COMMON_LOG_WARNING(uFormat("Wait end"));
+ } else {
+ std::string msg = uFormat("Failed to call service %s", srvName.c_str());
+ RVIZ_COMMON_LOG_ERROR(msg);
+ messageBox->setText(msg.c_str());
+ }
}
else
{
@@ -567,8 +571,8 @@ void MapCloudDisplay::downloadMap(bool /*graphOnly*/)
srvName.c_str(),
rtabmapNs.c_str());
RVIZ_COMMON_LOG_ERROR(msg);
- //messageBox->setText(msg.c_str());
- }*/
+ messageBox->setText(msg.c_str());
+ }
}
void MapCloudDisplay::downloadNamespaceChanged()
diff --git a/rtabmap_slam/CMakeLists.txt b/rtabmap_slam/CMakeLists.txt
index 946180d9..f53b7aaa 100644
--- a/rtabmap_slam/CMakeLists.txt
+++ b/rtabmap_slam/CMakeLists.txt
@@ -10,11 +10,19 @@ if(POLICY CMP0074)
cmake_policy(SET CMP0074 NEW)
endif()
+if(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")
+ # issues #1285 #1288
+ find_library(
+ builtin_interfaces__rosidl_generator_c_LIB NAMES builtin_interfaces__rosidl_generator_c
+ PATHS "/opt/ros/$ENV{ROS_DISTRO}/lib"
+ NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH REQUIRED
+ )
+endif()
+
find_package(ament_cmake REQUIRED)
find_package(cv_bridge REQUIRED)
find_package(geometry_msgs REQUIRED)
find_package(nav_msgs REQUIRED)
-find_package(nav2_msgs REQUIRED)
find_package(pluginlib REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rclcpp_components REQUIRED)
@@ -28,12 +36,13 @@ find_package(rtabmap_msgs REQUIRED)
find_package(rtabmap_util REQUIRED)
find_package(rtabmap_sync REQUIRED)
-IF(${nav2_msgs_VERSION_MAJOR} EQUAL 0)
- ADD_DEFINITIONS("-DNAV_MSGS_FOXY")
-ENDIF()
-
#optional
find_package(apriltag_msgs)
+find_package(aruco_msgs)
+find_package(aruco_markers_msgs)
+find_package(aruco_opencv_msgs)
+find_package(ros2_aruco_interfaces)
+find_package(nav2_msgs)
IF(WIN32)
add_compile_options(-bigobj)
@@ -48,7 +57,6 @@ SET(Libraries
cv_bridge
geometry_msgs
nav_msgs
- nav2_msgs
rclcpp
rclcpp_components
sensor_msgs
@@ -62,6 +70,10 @@ SET(Libraries
rtabmap_sync
)
+if("$ENV{ROS_DISTRO}" STRLESS "jazzy")
+ add_definitions(-DPRE_ROS_JAZZY)
+endif()
+
###########
## Build ##
###########
@@ -80,6 +92,59 @@ SET(Libraries
)
ENDIF(apriltag_msgs_FOUND)
+# If aruco_msgs is found, add definition
+IF(aruco_msgs_FOUND)
+MESSAGE(STATUS "WITH aruco_msgs")
+ADD_DEFINITIONS("-DWITH_ARUCO_MSGS")
+SET(Libraries
+ ${Libraries}
+ aruco_msgs
+)
+ENDIF(aruco_msgs_FOUND)
+
+# If aruco_opencv_msgs is found, add definition
+IF(aruco_opencv_msgs_FOUND)
+MESSAGE(STATUS "WITH aruco_opencv_msgs")
+ADD_DEFINITIONS("-DWITH_ARUCO_OPENCV_MSGS")
+SET(Libraries
+ ${Libraries}
+ aruco_opencv_msgs
+)
+ENDIF(aruco_opencv_msgs_FOUND)
+
+# If aruco_markers_msgs is found, add definition
+IF(aruco_markers_msgs_FOUND)
+MESSAGE(STATUS "WITH aruco_markers_msgs")
+ADD_DEFINITIONS("-DWITH_ARUCO_MARKERS_MSGS")
+SET(Libraries
+ ${Libraries}
+ aruco_markers_msgs
+)
+ENDIF(aruco_markers_msgs_FOUND)
+
+# If ros2_aruco_interfaces is found, add definition
+IF(ros2_aruco_interfaces_FOUND)
+MESSAGE(STATUS "WITH ros2_aruco_interfaces")
+ADD_DEFINITIONS("-DWITH_ROS2_ARUCO_INTERFACES")
+SET(Libraries
+ ${Libraries}
+ ros2_aruco_interfaces
+)
+ENDIF(ros2_aruco_interfaces_FOUND)
+
+# If nav2_msgs is found, add definition
+IF(nav2_msgs_FOUND)
+MESSAGE(STATUS "WITH nav2_msgs")
+ADD_DEFINITIONS("-DWITH_NAV2_MSGS")
+SET(Libraries
+ ${Libraries}
+ nav2_msgs
+)
+IF(${nav2_msgs_VERSION_MAJOR} EQUAL 0)
+ ADD_DEFINITIONS("-DNAV_MSGS_FOXY")
+ENDIF()
+ENDIF(nav2_msgs_FOUND)
+
############################
## Declare a cpp library
############################
@@ -128,4 +193,4 @@ install(DIRECTORY include/
FILES_MATCHING PATTERN "*.h"
)
-ament_package()
\ No newline at end of file
+ament_package()
diff --git a/rtabmap_slam/include/rtabmap_slam/CoreWrapper.h b/rtabmap_slam/include/rtabmap_slam/CoreWrapper.h
index bca24fdb..157a9b26 100644
--- a/rtabmap_slam/include/rtabmap_slam/CoreWrapper.h
+++ b/rtabmap_slam/include/rtabmap_slam/CoreWrapper.h
@@ -88,8 +88,26 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include
#endif
+#ifdef WITH_ARUCO_MSGS
+#include
+#endif
+
+#ifdef WITH_ARUCO_OPENCV_MSGS
+#include
+#endif
+
+#ifdef WITH_ARUCO_MARKERS_MSGS
+#include
+#endif
+
+#ifdef WITH_ROS2_ARUCO_INTERFACES
+#include
+#endif
+
+#ifdef WITH_NAV2_MSGS
#include
#include
+#endif
//#define WITH_FIDUCIAL_MSGS
#ifdef WITH_FIDUCIAL_MSGS
@@ -109,13 +127,16 @@ public:
explicit CoreWrapper(const rclcpp::NodeOptions & options);
virtual ~CoreWrapper();
+#ifdef WITH_NAV2_MSGS
using NavigateToPose = nav2_msgs::action::NavigateToPose;
using GoalHandleNav2 = rclcpp_action::ClientGoalHandle;
+#endif
private:
bool odomUpdate(const nav_msgs::msg::Odometry & odomMsg, rclcpp::Time stamp);
- bool odomTFUpdate(const rclcpp::Time & stamp); // TF odom
+ bool odomTFUpdate(const std::string & odomFrameId, const rclcpp::Time & stamp); // TF odom
+ // Callback called from sync thread
virtual void commonMultiCameraCallback(
const nav_msgs::msg::Odometry::ConstSharedPtr & odomMsg,
const rtabmap_msgs::msg::UserData::ConstSharedPtr & userDataMsg,
@@ -130,6 +151,7 @@ private:
const std::vector > & localKeyPoints = std::vector >(),
const std::vector > & localPoints3d = std::vector >(),
const std::vector & localDescriptors = std::vector());
+ // Callback called from sync thread
void commonMultiCameraCallbackImpl(
const std::string & odomFrameId,
const rtabmap_msgs::msg::UserData::ConstSharedPtr & userDataMsg,
@@ -144,6 +166,7 @@ private:
const std::vector > & localKeyPoints,
const std::vector > & localPoints3d,
const std::vector & localDescriptors);
+ // Callback called from sync thread
virtual void commonLaserScanCallback(
const nav_msgs::msg::Odometry::ConstSharedPtr & odomMsg,
const rtabmap_msgs::msg::UserData::ConstSharedPtr & userDataMsg,
@@ -151,11 +174,13 @@ private:
const sensor_msgs::msg::PointCloud2 & scan3dMsg,
const rtabmap_msgs::msg::OdomInfo::ConstSharedPtr& odomInfoMsg,
const rtabmap_msgs::msg::GlobalDescriptor & globalDescriptor = rtabmap_msgs::msg::GlobalDescriptor());
+ // Callback called from sync thread
virtual void commonOdomCallback(
const nav_msgs::msg::Odometry::ConstSharedPtr & odomMsg,
const rtabmap_msgs::msg::UserData::ConstSharedPtr & userDataMsg,
const rtabmap_msgs::msg::OdomInfo::ConstSharedPtr& odomInfoMsg);
+ // Callback called from sync thread
virtual void commonSensorDataCallback(
const rtabmap_msgs::msg::SensorData::ConstSharedPtr & sensorDataMsg,
const nav_msgs::msg::Odometry::ConstSharedPtr & odomMsg,
@@ -169,7 +194,20 @@ private:
void landmarkDetectionAsyncCallback(const rtabmap_msgs::msg::LandmarkDetection::SharedPtr landmarkDetection);
void landmarkDetectionsAsyncCallback(const rtabmap_msgs::msg::LandmarkDetections::SharedPtr landmarkDetections);
#ifdef WITH_APRILTAG_MSGS
- void tagDetectionsAsyncCallback(const apriltag_msgs::msg::AprilTagDetectionArray::SharedPtr tagDetections);
+ void tagDetectionsAsyncCallback(const apriltag_msgs::msg::AprilTagDetectionArray::SharedPtr msg);
+ void apriltagAsyncCallback(const apriltag_msgs::msg::AprilTagDetectionArray::SharedPtr msg);
+#endif
+#ifdef WITH_ARUCO_MSGS
+ void arucoAsyncCallback(const aruco_msgs::msg::MarkerArray::SharedPtr msg);
+#endif
+#ifdef WITH_ARUCO_OPENCV_MSGS
+ void arucoOpencvAsyncCallback(const aruco_opencv_msgs::msg::ArucoDetection::SharedPtr msg);
+#endif
+#ifdef WITH_ARUCO_MARKERS_MSGS
+ void arucoMarkersAsyncCallback(const aruco_markers_msgs::msg::MarkerArray::SharedPtr msg);
+#endif
+#ifdef WITH_ROS2_ARUCO_INTERFACES
+ void arucoInterfacesAsyncCallback(const ros2_aruco_interfaces::msg::ArucoMarkers::SharedPtr msg);
#endif
#ifdef WITH_FIDUCIAL_MSGS
void fiducialDetectionsAsyncCallback(const fiducial_msgs::msgs::FiducialTransformArray::SharedPtr fiducialDetections);
@@ -191,6 +229,8 @@ private:
void goalNodeCallback(const rtabmap_msgs::msg::Goal::SharedPtr msg);
void updateGoal(const rclcpp::Time & stamp);
+ void processAsync();
+
void process(
const rclcpp::Time & stamp,
rtabmap::SensorData & data,
@@ -246,12 +286,14 @@ private:
void publishStats(const rclcpp::Time & stamp);
void publishCurrentGoal(const rclcpp::Time & stamp);
+#ifdef WITH_NAV2_MSGS
#ifdef NAV_MSGS_FOXY
void goalResponseCallback(std::shared_future future);
#else
- void goalResponseCallback(const GoalHandleNav2::SharedPtr & goal_handle);
+ void goalResponseCallback(const GoalHandleNav2::SharedPtr & goal_handle);
#endif
void resultCallback(const GoalHandleNav2::WrappedResult & result);
+#endif
void publishLocalPath(const rclcpp::Time & stamp);
void publishGlobalPath(const rclcpp::Time & stamp);
@@ -260,11 +302,14 @@ private:
private:
rtabmap::Rtabmap rtabmap_;
bool paused_;
+
+ UMutex lastPoseMutex_;
rtabmap::Transform lastPose_;
rclcpp::Time lastPoseStamp_;
std::vector lastPoseVelocity_;
+ cv::Mat lastPoseCovariance_;
bool lastPoseIntermediate_;
- cv::Mat covariance_;
+
rtabmap::Transform currentMetricGoal_;
rtabmap::Transform lastPublishedMetricGoal_;
bool latestNodeWasReached_;
@@ -335,7 +380,7 @@ private:
std::shared_ptr tfBuffer_;
std::shared_ptr tfListener_;
- rclcpp::SyncParametersClient::SharedPtr parametersClient_;
+ rclcpp::AsyncParametersClient::SharedPtr parametersClient_;
rclcpp::Subscription::SharedPtr parameterEventSub_;
rclcpp::Service::SharedPtr updateSrv_;
@@ -373,7 +418,10 @@ private:
rclcpp::Service::SharedPtr octomapBinarySrv_;
rclcpp::Service::SharedPtr octomapFullSrv_;
#endif
+#ifdef WITH_NAV2_MSGS
rclcpp_action::Client::SharedPtr nav2Client_;
+ rclcpp_action::GoalUUID lastGoalSent_;
+#endif
std::thread* transformThread_;
bool tfThreadRunning_;
@@ -381,27 +429,52 @@ private:
// for loop closure detection only
image_transport::Subscriber defaultSub_;
+ rclcpp::CallbackGroup::SharedPtr userDataAsyncCallbackGroup_;
rclcpp::Subscription::SharedPtr userDataAsyncSub_;
cv::Mat userData_;
UMutex userDataMutex_;
+ rclcpp::CallbackGroup::SharedPtr globalPoseAsyncCallbackGroup_;
rclcpp::Subscription::SharedPtr globalPoseAsyncSub_;
- geometry_msgs::msg::PoseWithCovarianceStamped globalPose_;
+ std::map globalPoses_;
+ UMutex globalPoseMutex_;
+
+ rclcpp::CallbackGroup::SharedPtr gpsAsyncCallbackGroup_;
rclcpp::Subscription::SharedPtr gpsFixAsyncSub_;
- rtabmap::GPS gps_;
+ std::map