Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,6 @@ container_root/profilers/**
**container_root/trav_ws/**
**traversability_mapping**
container_root/ros_env_vars.sh
container_root/.nv/
container_root/.nv/
orb_slam3_ros2_wrapper/datasets/**
!orb_slam3_ros2_wrapper/datasets/PLACE_YOUR_DATASETS_HERE
58 changes: 45 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ sudo chmod +x container_root/shell_scripts/docker_install.sh
## 3. Build the image with ORB_SLAM3

1. Build the image: ```sudo docker build --build-arg USE_CI=false -t orb-slam3-humble:22.04 .```
2. Add ```xhost +``` to your ```.bashrc``` to support correct x11-forwarding using ```echo "xhost +" >> ~/.bashrc```
2. Add `xhost +` to your `.bashrc` to support correct X11-forwarding using ```echo "xhost +" >> ~/.bashrc```
3. ```source ~/.bashrc```
4. You can see the built images on your machine by running ```sudo docker images```.

Expand All @@ -44,7 +44,7 @@ Replace step 1. with ```sudo docker build --build-arg USE_CI=false --build-arg T

1. ```cd ORB-SLAM3-ROS2-Docker``` (ignore if you are already in the folder)
2. ```sudo docker compose run orb_slam3_22_humble```
3. This should take you inside the container. Once you are inside, run the command ```xeyes``` and a pair of eyes should pop-up. If they do, x11 forwarding has correctly been setup on your computer.
3. This should take you inside the container. Once you are inside, run the command ```xeyes``` and a pair of eyes should pop-up. If they do, X11 forwarding has correctly been setup on your computer.

### To run the NVIDIA CUDA version:

Expand All @@ -68,17 +68,49 @@ cd /root/colcon_ws/ && rm -rf build && colcon build --symlink-install --cmake-ar

## Launching ORB-SLAM3

This repository supports the following ways to test different configurations. <br>

To try the `mono`, `mono_imu`, `stereo`, `stereo_imu` setups, please refer to the `Running with the Euroc Dataset` below.

To try the `rgbd`, `rgbd_imu` setups, please refer to the `Running with Gazebo sim simulation` section below.

The launch files to test each configuration is in `orb_slam3_ros2_docker/launch` <br>
The default parameters in each of these launch files correspond to the following table.

| Mode | EuRoC Dataset | Gazebo Simulation |
|------|---------------|-------------------|
| mono | ✅ | ❌ |
| mono_imu | ✅ | ❌ |
| rgbd | ❌ | ✅ |
| rgbd_imu | ❌ | ✅ |
| stereo | ✅ | ❌ |
| stereo_imu | ✅ | ❌ |

Launch the container using steps in (4).
If you are inside the container, run the following:

1. ```ros2 launch orb_slam3_ros2_wrapper unirobot.launch.py```
3. You can adjust the robot namespace in the ```unirobot.launch.py``` file.
1. ```ros2 launch orb_slam3_ros2_wrapper unirobot.launch.py sensor_config:=<replace_with_your_config>```
2. You can adjust the robot namespace in the ```unirobot.launch.py``` file.

`sensor_config` argument can take the following `mono`, `mono_imu`, `stereo`, `stereo_imu`, `rgbd`, `rgbd_imu`

## Running this with the Euroc dataset

Supported modes are `mono`, `mono_imu`, `stereo`, `stereo_imu`

1. Download the euroc dataset `sudo chmod +x download_euroc.sh && ./download_euroc.sh`
2. Open a new instance of the container you launched in (4) using `docker exec -it <container_id> bash`
3. `ros2 bag play colcon_ws/src/orb_slam3_ros2_wrapper/datasets/V1_01_easy`

Once the rosbag is playing, you should be able to run it with the wrapper in parallel.

## Running this with a Gazebo Sim simulation.

1. Setup the ORB-SLAM3 ROS2 Docker using the steps above. Once you do (1) step in the ```Launching ORB-SLAM3``` section, you should see a window popup which is waiting for images. This is partially indicative of the setup correctly done.
Supported modes are `rgbd`, `rgbd_imu`

1. Setup the ORB-SLAM3 ROS2 Docker using the steps above. Once you complete step (1) in the ```Launching ORB-SLAM3``` section, you should see a window popup which is waiting for images. This is partially indicative of the setup correctly done.
2. Setup the simulation by following the README [here (humble)](https://github.com/suchetanrs/gz-sim-environment/tree/humble) (recommended) or [here (jazzy)](https://github.com/suchetanrs/gz-sim-environment/tree/jazzy)
3. Once you are able to teleop the robot, you should be able to run ORB-SLAM3 with both the containers (simulation and wrapper) running parallely.
3. Once you are able to teleop the robot, you should be able to run ORB-SLAM3 with both the containers (simulation and wrapper) running in parallel.

## Running the map_generator package.

Expand All @@ -92,18 +124,18 @@ To run the package, these steps can be followed:
4. If you wish to publish the global pointcloud at any point during the SLAM's operation, simply run the python file in the top-right terminal. You should be able to view the global pointcloud in rviz (you can launch RViz with the correct configuration from the bottom-right terminal).

### Potential issues you may face.
The simulation and the wrapper both have their ```ROS_DOMAIN_ID``` set to 55 so they are meant to work out of the box. However, you may face issues if this environment variable is not set properly. Before you start the wrapper, run ```ros2 topic list``` and make sure the topics `/rgb_camera` and `/depth_camera` are visible inside the ORB-SLAM3 container provided the simulation is running along the side.
The simulation and the wrapper both have their ```ROS_DOMAIN_ID``` set to 55 so they are meant to work out of the box. However, you may face issues if this environment variable is not set properly. Before you start the wrapper, run ```ros2 topic list``` and make sure the topics `/rgb_camera` and `/depth_camera` are visible inside the ORB-SLAM3 container provided the simulation is running alongside.

## Services
| Service Name | Purpose | type |
| Service Name | Purpose | Type |
|-------------------------|---------------|---------------|
| `orb_slam3/get_map_data` | Sends the map_data in the response. | `slam_msgs::srv::GetMap` |
| `orb_slam3/get_landmarks_in_view` | Takes an input pose and publishes the feature points visible from that pose. | `slam_msgs::srv::GetLandmarksInView` |
| `orb_slam3/get_all_landmarks_in_map` | Publishes all feature points in the map and fills the same pointcloud in the response. | `slam_msgs::srv::GetAllLandmarksInMap` |
| `orb_slam3/reset_mapping` | Resets the current mapping instance and clears all keyframes. | `std_srvs::srv::SetBool` |

## Published Topics
| Topic Name | Purpose | type |
| Topic Name | Purpose | Type |
|-------------------------|---------------|---------------|
| `map_points` | Publishes the point cloud representing feature points collected from the SLAM process. This is published when `orb_slam3/get_all_landmarks_in_map` service is called. | `sensor_msgs::msg::PointCloud2` |
| `visible_landmarks` | Publishes the point cloud of feature points (landmarks) visible from a given pose. This is published when `orb_slam3/get_landmarks_in_view` service is called. | `sensor_msgs::msg::PointCloud2` |
Expand All @@ -117,9 +149,9 @@ The simulation and the wrapper both have their ```ROS_DOMAIN_ID``` set to 55 so
| `robot_base_frame` | `base_footprint` | The name of the frame attached to the robot's base. |
| `global_frame` | `map` | The name of the global frame of reference. It represents a fixed world coordinate frame in which the robot navigates.|
| `odom_frame` | `odom` | The name of the odometry frame. |
| `rgb_image_topic_name` | `rgb_camera` | The topic to recieve rgb images. |
| `depth_image_topic_name` | `depth_camera` | The topic to recieve depth images. |
| `imu_topic_name` | `imu` | The topic to recieve IMU messages (Not used in RGB-D mode). |
| `rgb_image_topic_name` | `rgb_camera` | The topic to receive rgb images. |
| `depth_image_topic_name` | `depth_camera` | The topic to receive depth images. |
| `imu_topic_name` | `imu` | The topic to receive IMU messages (Not used in RGB-D mode). |
| `visualization` | `true` | A boolean flag to enable or disable visualization. When set to `true`, the ORB-SLAM3 viewer will show up with the tracked points and the keyframe trajectories.|
| `odometry_mode` | `false` | A boolean flag to toggle odometry mode. When `false`, the system operates without relying on odometry data, which might be used in scenarios where odometry information is unavailable or unreliable. In this case, it publishes the transform directly between the ```global_frame``` and the ```robot_base_frame```. Further information can be found on the [FAQ](https://github.com/suchetanrs/ORB-SLAM3-ROS2-Docker/wiki/FAQs)|
| `publish_tf` | `true` | Publishes the map->odom tf in case `odometry_mode` is set to `true` and map->odom->base_link in case odometry_mode is set to `false`. Further information can be found on the [FAQ](https://github.com/suchetanrs/ORB-SLAM3-ROS2-Docker/wiki/FAQs)|
Expand All @@ -128,7 +160,7 @@ The simulation and the wrapper both have their ```ROS_DOMAIN_ID``` set to 55 so

## Important notes

ORB-SLAM3 is launched from ```orb_slam3_docker_20_humble/orb_slam3_ros2_wrapper/launch/rgbd.launch.py``` which inturn is launched from ```orb_slam3_docker_20_humble/orb_slam3_ros2_wrapper/launch/unirobot.launch.py```
ORB-SLAM3 is launched from ```orb_slam3_ros2_wrapper/launch/rgbd.launch.py``` which in turn is launched from ```orb_slam3_ros2_wrapper/launch/unirobot.launch.py```

Currently the ```rgbd.launch.py``` launch file defaults to ```orb_slam3_ros2_wrapper/params/gazebo_rgbd.yaml```. You can modify this with your own parameter file in case you wish to use your own camera.

Expand Down
13 changes: 13 additions & 0 deletions download_euroc.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
pip install gdown

if [ ! -f "orb_slam3_ros2_wrapper/datasets/V1_01_easy.zip" ]; then
gdown --fuzzy https://drive.google.com/file/d/1LFrdiMU6UBjtFfXPHzjJ4L7iDIXcdhvh/view?usp=sharing
else
echo "orb_slam3_ros2_wrapper/datasets/V1_01_easy.zip already exists. Skipping download."
fi

if [ ! -d "orb_slam3_ros2_wrapper/datasets/V1_01_easy" ]; then
unzip orb_slam3_ros2_wrapper/datasets/V1_01_easy.zip
else
echo "orb_slam3_ros2_wrapper/datasets/V1_01_easy directory already exists. Skipping unzip."
fi
26 changes: 25 additions & 1 deletion orb_slam3_ros2_wrapper/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,31 @@ if(ORB_SLAM3_ROS2_WRAPPER_ENABLE_CUDA)
endif()


install(TARGETS orb_slam3_ros2_wrapper_common rgbd rgbd_imu mono_imu stereo
add_executable(mono
src/mono/mono-slam-node.cpp
src/mono/mono.cpp
)
ament_target_dependencies(mono ${dependencies})
target_link_libraries(mono orb_slam3_ros2_wrapper_common ${PCL_LIBRARIES})
if(ORB_SLAM3_ROS2_WRAPPER_ENABLE_CUDA)
set_target_properties(mono PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
target_compile_definitions(mono PRIVATE ORB_SLAM3_ROS2_WRAPPER_ENABLE_CUDA=1)
endif()


add_executable(stereo_imu
src/stereo-imu/stereo-imu-slam-node.cpp
src/stereo-imu/stereo_imu.cpp
)
ament_target_dependencies(stereo_imu ${dependencies})
target_link_libraries(stereo_imu orb_slam3_ros2_wrapper_common ${PCL_LIBRARIES})
if(ORB_SLAM3_ROS2_WRAPPER_ENABLE_CUDA)
set_target_properties(stereo_imu PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
target_compile_definitions(stereo_imu PRIVATE ORB_SLAM3_ROS2_WRAPPER_ENABLE_CUDA=1)
endif()


install(TARGETS orb_slam3_ros2_wrapper_common rgbd rgbd_imu mono_imu stereo mono stereo_imu
DESTINATION lib/${PROJECT_NAME})

install(DIRECTORY launch params
Expand Down
Empty file.
91 changes: 91 additions & 0 deletions orb_slam3_ros2_wrapper/launch/mono.launch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import os

from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument, OpaqueFunction
from launch.substitutions import LaunchConfiguration
from launch_ros.actions import Node
from nav2_common.launch import RewrittenYaml


def generate_launch_description():
orb_wrapper_pkg = get_package_share_directory("orb_slam3_ros2_wrapper")

use_sim_time = LaunchConfiguration("use_sim_time")
declare_use_sim_time_cmd = DeclareLaunchArgument(
name="use_sim_time",
default_value="True",
description="Use simulation (Gazebo) clock if true",
)

robot_namespace = LaunchConfiguration("robot_namespace")
robot_namespace_arg = DeclareLaunchArgument(
"robot_namespace", default_value="robot", description="The namespace of the robot"
)

orb_slam3_param_file = LaunchConfiguration("orb_slam3_param_file")
declare_orb_slam3_param_file_cmd = DeclareLaunchArgument(
name="orb_slam3_param_file",
default_value="euroc_mono.yaml", # gazebo_mono.yaml
description="Path to the ORB-SLAM3 parameter file",
)

ros_params_file = LaunchConfiguration("ros_params_file")
declare_ros_params_file_cmd = DeclareLaunchArgument(
name="ros_params_file",
default_value="euroc-mono-ros-params.yaml", # gazebo-mono-ros-params.yaml
description="Path to the ROS2 parameters file",
)

def all_nodes_launch(context, robot_namespace):
params_file = LaunchConfiguration("params_file")
vocabulary_file_path = "/home/orb/ORB_SLAM3/Vocabulary/ORBvoc.txt"
# NOTE: keep settings path consistent with existing launch files. Replace as needed.
config_file_path = "/root/colcon_ws/src/orb_slam3_ros2_wrapper/params/orb_slam3_params/" + orb_slam3_param_file.perform(context)

declare_params_file_cmd = DeclareLaunchArgument(
"params_file",
default_value=os.path.join(orb_wrapper_pkg, "params", "ros_params", ros_params_file.perform(context)),
description="Full path to the ROS2 parameters file to use for all launched nodes",
)

# Optional namespacing hook (kept consistent with other launch files)
base_frame = "" if robot_namespace.perform(context) == "" else robot_namespace.perform(context) + "/"
param_substitutions = {
# "robot_base_frame": base_frame + "base_footprint",
# "odom_frame": base_frame + "odom",
}

configured_params = RewrittenYaml(
source_file=params_file,
root_key=robot_namespace.perform(context),
param_rewrites=param_substitutions,
convert_types=True,
)

orb_slam3_node = Node(
package="orb_slam3_ros2_wrapper",
executable="mono",
output="screen",
namespace=robot_namespace.perform(context),
arguments=[vocabulary_file_path, config_file_path],
parameters=[configured_params],
)

return [declare_params_file_cmd, orb_slam3_node]

opaque_function = OpaqueFunction(function=all_nodes_launch, args=[robot_namespace])

return LaunchDescription(
[
declare_use_sim_time_cmd,
declare_orb_slam3_param_file_cmd,
declare_ros_params_file_cmd,
robot_namespace_arg,
opaque_function,
]
)


20 changes: 18 additions & 2 deletions orb_slam3_ros2_wrapper/launch/mono_imu.launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,29 @@ def generate_launch_description():
"robot_namespace", default_value="robot", description="The namespace of the robot"
)

orb_slam3_param_file = LaunchConfiguration("orb_slam3_param_file")
declare_orb_slam3_param_file_cmd = DeclareLaunchArgument(
name="orb_slam3_param_file",
default_value="euroc_mono_imu.yaml", # gazebo_mono_imu.yaml
description="Path to the ORB-SLAM3 parameter file",
)

ros_params_file = LaunchConfiguration("ros_params_file")
declare_ros_params_file_cmd = DeclareLaunchArgument(
name="ros_params_file",
default_value="euroc-mono-imu-ros-params.yaml", # gazebo-mono-imu-ros-params.yaml
description="Path to the ROS2 parameters file",
)

def all_nodes_launch(context, robot_namespace):
params_file = LaunchConfiguration("params_file")
vocabulary_file_path = "/home/orb/ORB_SLAM3/Vocabulary/ORBvoc.txt"
# NOTE: keep settings path consistent with existing launch files. Replace as needed.
config_file_path = "/root/colcon_ws/src/orb_slam3_ros2_wrapper/params/orb_slam3_params/gazebo_mono_imu.yaml"
config_file_path = "/root/colcon_ws/src/orb_slam3_ros2_wrapper/params/orb_slam3_params/" + orb_slam3_param_file.perform(context)

declare_params_file_cmd = DeclareLaunchArgument(
"params_file",
default_value=os.path.join(orb_wrapper_pkg, "params", "ros_params", "gazebo-mono-imu-ros-params.yaml"),
default_value=os.path.join(orb_wrapper_pkg, "params", "ros_params", ros_params_file.perform(context)),
description="Full path to the ROS2 parameters file to use for all launched nodes",
)

Expand Down Expand Up @@ -67,6 +81,8 @@ def all_nodes_launch(context, robot_namespace):
return LaunchDescription(
[
declare_use_sim_time_cmd,
declare_orb_slam3_param_file_cmd,
declare_ros_params_file_cmd,
robot_namespace_arg,
opaque_function,
]
Expand Down
Loading