Skip to content

Commit

Permalink
Merge pull request #75 from ETHZ-RobotX/fix/slam_tutorial
Browse files Browse the repository at this point in the history
The launch files for tutorials
  • Loading branch information
deepanaishtaweera authored Jun 30, 2024
2 parents 42a8c40 + eead51d commit 88666b6
Show file tree
Hide file tree
Showing 13 changed files with 488 additions and 225 deletions.
50 changes: 36 additions & 14 deletions smb_slam/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# SMB_SLAM
This package contains a bunch of commodity scripts for running (simultaneous) localization & mapping using [open3d_slam](https://github.com/ETHZ-RobotX/open3d_slam_advanced_rss_2024).
This package contains a bunch of commodity scripts for running (simultaneous) localization & mapping using [open3d_slam](https://github.com/ETHZ-RobotX/open3d_slam_advanced_rss_2024_public).

Follow instructions the instructions below to build ```smb_slam```.

Expand All @@ -19,23 +19,15 @@ The map generation is both possible in real-time on the system or from a rosbag,

Here are different modes of operation:

1. Building a Map Offline (Using Rosbag):
1. Running in Online SLAM Mode:

Run the following command to start running online SLAM. You can save the map at any point by using rosservice or the map will automatically get saved upon closing the node. Make sure that you specify the correct full path of the bag file using the `rosbag_full_path` argument. Alternatively, you can instead specify the folder path and filename using the arguments `bag_folder_path` and `bag_filename` repectively. The `loop closure` will be enabled by default in this case of offline map creation.

```
roslaunch smb_slam build_map_from_bag.launch rosbag_full_path:="ABSOULTE PATH TO BAG FILE"
```

2. Running in Online SLAM Mode:

Make sure that `smb smb.launch` is running correctly. Now simply run the following command to start running online SLAM. You can save the map at any point by using rosservice or the map will automatically get saved upon closing the node. The `loop closure` is switched off by default for this operation and can be turned on using ```loop_closure:=true``` argument (Though we don't recommend it due to computational limits).
Make sure that `smb smb.launch` is running correctly. Now simply run the following command to start running online SLAM. You can save the map at any point by using rosservice or the map will automatically get saved upon closing the node. Advanced features such as `loop closure`, `space carving` and `submap-to-submap` registration are disabled by default as SMBs are computationally already loaded. Refer to the parameter file `/smb_slam/config/open3d_slam/param_robosense_rs16.lua` and the below table for parameter details.

```
roslaunch smb_slam online_slam.launch
```

3. Localization Mode:
2. Localization Mode:

If you have already created a map of the environment and would like to localize the robot in this prebuilt map, use the following command. Make sure that you correctly specify the path of the pcd map (Default path is `data/maps/map.pcd`).
```
Expand All @@ -45,10 +37,40 @@ Here are different modes of operation:

For all the above cases, if you run in simulation, you can launch rviz by setting the `launch_rviz:=true` to the launch command.


3. Building a Map Offline (Replay Mode):

Run the following command to start running a replayer node that uses an external odometry specified in the launch file and the point cloud. Different than the online SLAM pipeline, this replayer runs sequentially. This means, the node will not skip measurements and instead it will wait for computation of the previous point cloud to finish before moving on. As a result, in compute limited systems might run slower than real-time and vice-versa, i.e. faster than real-time in powerful devices. This node saves the generated map, a rosbag that contains the registered point clouds as well as the calculated poses and finally, the path of the robot as a .pcd file such that the user can visualize the path next to the map. The saving folder is specified within the `.launch` file.
```
roslaunch smb_slam replay_SLAM.launch
```

> **Note**: The user is responsible to make sure that the specified topics are in the bag. The node will guide if there is something missing.


### Map Saving

The map can be saved at any point by using rosservice from a new terminal. Run the following command from a new sourced terminal:
```
rosservice call /mapping_node/save_map
rosservice call /mapping/save_map
```
Alternatively there is an option for autosaving upon exit which can be switched on from the param files (`params.saving.save_at_mission_end = true`). This is switched off by default.
Alternatively there is an option for autosaving upon exit which can be switched on from the param files (`params.saving.save_map = true`).


## Important Parameters

Other than the parameters exposed to the ROS parameter server (the parameters user can set with `.launch` files) there are few important parameters located in `/smb_slam/config/open3d_slam/param_robosense_rs16.lua` file or similar files to this.

| Lua Parameter | Type | Description |
| ----------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `params.odometry.use_odometry_topic_instead_of_scan_to_scan` | `bool` | If true, O3D uses the external odometry specified in the `.launch` file. |
| `params.mapper_localizer.is_use_map_initialization` | `bool` | If true, O3D runs in localization mode and uses the given map, specified in the LUA file. |
| `params.mapper_localizer.is_attempt_loop_closure ` | ` bool` | if true, O3D does loop-closures. This feature is power hungry, use responsibly. |
| `params.mapper_localizer.is_carving_enabled` | `bool` | if true, O3D does space carving. If a previously occupied space is re-observed to be empty, that voxel is cleaned. Effective against dynamic obstables. Increses computation is enabled.|
| `params.submap.submap_size` | `double` | The submap size in meters, if increased each submap contains more data but computation increases.|
| `params.map_builder.map_voxel_size ` | `double` | The voxel size of the map, subsequently of the submap. Defines the resolution of the map that is used for localization purposes. |
| `params.mapper_localizer.scan_to_map_registration.icp.reference_cloud_seting_period` | `double` | The refence setting period in seconds. If you decrease (> 0.0s) the localization becomes more robust but computation increases. If rapid motions are done, the decrease in the period can help.|
| `params.mapper_localizer.scan_to_map_registration.scan_processing.voxel_size` | `double` | The voxel size used for the registration. This parameter should not be set smaller than the map voxel size, as it would be meaningless. If set smaller the localization resolution increases but also sensitivity to noise and computational limitations. |


> **Note**: There are multiple advanced parameters in Open3D, to not complicated simple operation these are not detailed here. Follow the naming of the variables to infer usage.
236 changes: 122 additions & 114 deletions smb_slam/config/open3d_slam/default/parameter_structure_definitions.lua
Original file line number Diff line number Diff line change
@@ -1,174 +1,182 @@
-- need this to deep copy all the tables
function deepcopy(orig, copies)
copies = copies or {}
local orig_type = type(orig)
local copy
if orig_type == 'table' then
if copies[orig] then
copy = copies[orig]
else
copy = {}
copies[orig] = copy
for orig_key, orig_value in next, orig, nil do
copy[deepcopy(orig_key, copies)] = deepcopy(orig_value, copies)
end
setmetatable(copy, deepcopy(getmetatable(orig), copies))
end
else -- number, string, boolean, etc
copy = orig
end
return copy
copies = copies or {}
local orig_type = type(orig)
local copy
if orig_type == 'table' then
if copies[orig] then
copy = copies[orig]
else
copy = {}
copies[orig] = copy
for orig_key, orig_value in next, orig, nil do
copy[deepcopy(orig_key, copies)] = deepcopy(orig_value, copies)
end
setmetatable(copy, deepcopy(getmetatable(orig), copies))
end
else -- number, string, boolean, etc
copy = orig
end
return copy
end



SAVING_PARAMETERS = {
save_at_mission_end = true,
save_map = false,
save_submaps = false,
save_dense_submaps = false,
save_at_mission_end = true,
save_map = true,
save_submaps = false,
save_dense_submaps = true,
}

MOTION_COMPENSATION_PARAMETERS = {
is_undistort_scan = false,
is_spinning_clockwise = true,
scan_duration = 0.1,
num_poses_vel_estimation = 3,
is_undistort_scan = false,
is_spinning_clockwise = true,
scan_duration = 0.1,
num_poses_vel_estimation = 3,
}

VISUALIZATION_PARAMETERS = {
assembled_map_voxel_size = 0.3,
submaps_voxel_size = 0.3,
visualize_every_n_msec = 300.0,
assembled_map_voxel_size = 0.05,
submaps_voxel_size = 0.05,
visualize_every_n_msec = 500.0,
}

GLOBAL_OPTIMIZATION_PARAMETERS = {
edge_prune_threshold = 0.2,
loop_closure_preference = 2.0,
max_correspondence_distance = 1000.0,
reference_node = 0,
edge_prune_threshold = 0.2,
loop_closure_preference = 2.0,
max_correspondence_distance = 1000.0,
reference_node = 0,
}

SCAN_CROPPING_PARAMETERS = {
cropping_radius_max= 30.0,
cropping_radius_min= 2.0,
min_z= -50.0,
max_z= 50.0,
cropper_type= "MinMaxRadius", -- options are Cylinder, MaxRadius, MinRadius, MinMaxRadius
cropping_radius_max= 100.0,
cropping_radius_min= 2.0,
min_z= -50.0,
max_z= 50.0,
cropper_type= "MinMaxRadius", -- options are Cylinder, MaxRadius, MinRadius, MinMaxRadius
}

SCAN_PROCESSING_PARAMETERS = {
voxel_size = 0.1,
downsampling_ratio = 0.3,
point_cloud_buffer_size = 1, -- the scan processing buffer size. 1 means no buffering.
scan_cropping = deepcopy(SCAN_CROPPING_PARAMETERS),
voxel_size = 0.01,
downsampling_ratio = 1.0,
point_cloud_buffer_size = 1, -- the scan processing buffer size. 1 means no buffering.
scan_cropping = deepcopy(SCAN_CROPPING_PARAMETERS),
}

ICP_PARAMETERS = {
max_correspondence_dist= 1.0,
knn= 20,
max_distance_knn= 3.0,
max_n_iter= 50,
max_correspondence_dist= 1.0,
knn= 10,
max_distance_knn= 1.0,
max_n_iter= 30,
reference_cloud_seting_period= 1.0,
}

SCAN_MATCHING_PARAMETERS = {
icp = deepcopy(ICP_PARAMETERS),
cloud_registration_type = "GeneralizedIcp", -- options GeneralizedIcp, PointToPointIcp, PointToPlaneIcp
icp = deepcopy(ICP_PARAMETERS),
cloud_registration_type = "GeneralizedIcp", -- options GeneralizedIcp, PointToPointIcp, PointToPlaneIcp
}

ODOMETRY_PARAMETERS = {
is_publish_odometry_msgs = true,
odometry_buffer_size = 1, -- the scan2scan odometry buffer size. 1 means no buffering.
scan_matching = deepcopy(SCAN_MATCHING_PARAMETERS),
scan_processing = deepcopy(SCAN_PROCESSING_PARAMETERS),
use_odometry_topic_instead_of_scan_to_scan = true,
use_IMU_for_attitude_initialization = false,
is_publish_odometry_msgs = true,
odometry_buffer_size = 1, -- the scan2scan odometry buffer size. 1 means no buffering.
scan_matching = deepcopy(SCAN_MATCHING_PARAMETERS),
scan_processing = deepcopy(SCAN_PROCESSING_PARAMETERS),
}

SUBMAP_PARAMETERS = {
submap_size = 20, -- meters
min_num_range_data = 10,
adjacency_based_revisiting_min_fitness = 0.5,
submaps_num_scan_overlap = 10,
submap_size = 20, -- meters
min_num_range_data = 10,
adjacency_based_revisiting_min_fitness = 0.5,
min_seconds_between_feature_computation = 5.0,
submaps_num_scan_overlap = 10,
}

SPACE_CARVING_PARAMETERS = {
voxel_size= 0.2,
max_raytracing_length = 20.0,
truncation_distance = 0.3,
carve_space_every_n_scans= 10.0,
voxel_size= 0.2,
max_raytracing_length = 20.0,
truncation_distance = 0.3,
carve_space_every_n_scans= 10.0,
min_dot_product_with_normal = 0.5,
neigborhood_radius_for_removal= 0.1,
}


MAP_BUILDER_PARAMETERS = {
map_voxel_size = 0.1, --meters
scan_cropping = deepcopy(SCAN_CROPPING_PARAMETERS),
space_carving = deepcopy(SPACE_CARVING_PARAMETERS),
map_voxel_size = 0.05, --meters
scan_cropping = deepcopy(SCAN_CROPPING_PARAMETERS),
space_carving = deepcopy(SPACE_CARVING_PARAMETERS),
}

SCAN_TO_MAP_REGISTRATION_PARAMETERS = {
min_refinement_fitness = 0.7,
scan_to_map_refinement_type = "GeneralizedIcp", -- options GeneralizedIcp, PointToPointIcp, PointToPlaneIcp
icp = deepcopy(ICP_PARAMETERS),
scan_processing = deepcopy(SCAN_PROCESSING_PARAMETERS),
min_refinement_fitness = 0.7,
scan_to_map_refinement_type = "GeneralizedIcp", -- options GeneralizedIcp, PointToPointIcp, PointToPlaneIcp
icp = deepcopy(ICP_PARAMETERS),
scan_processing = deepcopy(SCAN_PROCESSING_PARAMETERS),
}


MAPPER_LOCALIZER_PARAMETERS = {
is_print_timing_information = true,
is_build_dense_map = false,
is_attempt_loop_closures = true,
is_use_map_initialization = false,
is_merge_scans_into_map = false,
dump_submaps_to_file_before_after_lc = false,
is_refine_odometry_constraints_between_submaps = false,
min_movement_between_mapping_steps = 0.0,
ignore_minimum_refinement_fitness = false,
mapping_buffer_size = 1, -- the scan2map odometry buffer size. 1 means no buffering.
scan_to_map_registration = deepcopy(SCAN_TO_MAP_REGISTRATION_PARAMETERS),
is_print_timing_information = true,
is_carving_enabled = false,
is_build_dense_map = false,
is_attempt_loop_closures = false,
is_use_map_initialization = false,
republish_the_preloaded_map = true,
map_merge_delay_in_seconds = 10.0,
is_merge_scans_into_map = false,
dump_submaps_to_file_before_after_lc = false,
is_refine_odometry_constraints_between_submaps = false,
min_movement_between_mapping_steps = 0.0,
ignore_minimum_refinement_fitness = false,
mapping_buffer_size = 1, -- the scan2map odometry buffer size. 1 means no buffering.
scan_to_map_registration = deepcopy(SCAN_TO_MAP_REGISTRATION_PARAMETERS),
}

POSE = {
x = 0.0,
y = 0.0,
z = 0.0,
roll = 0.0, --roll, pitch ,yaw in degrees!!!!!
pitch = 0.0,
yaw = 0.0,
x = 0.0,
y = 0.0,
z = 0.0,
roll = 0.0, --roll, pitch ,yaw in degrees!!!!!
pitch = 0.0,
yaw = 0.0,
}

MAP_INITIALIZER_PARAMETERS = {
is_initialize_interactively = false,
frame_id = "map_o3d",
pcd_file_path = "",
init_pose = POSE,
is_initialize_interactively = false,
frame_id = "map_o3d",
pcd_file_path = "",
pcd_file_package = "",
init_pose = POSE,
}

LOOP_CLOSURE_CONSISTENCY_CHECK_PARAMETERS = {
max_drift_roll = 30.0, --deg
max_drift_pitch = 30.0, --deg
max_drift_yaw = 30.0, --deg
max_drift_x = 80.0, --meters
max_drift_y = 80.0, --meters
max_drift_z = 40.0, --meters
max_drift_roll = 30.0, --deg
max_drift_pitch = 30.0, --deg
max_drift_yaw = 30.0, --deg
max_drift_x = 80.0, --meters
max_drift_y = 80.0, --meters
max_drift_z = 40.0, --meters
}

PLACE_RECOGNITION_PARAMETERS = {
feature_map_normal_estimation_radius = 2.0,
feature_voxel_size = 0.5,
feature_radius = 2.5,
feature_knn = 100,
feature_normal_knn = 20,
ransac_num_iter = 10000000,
ransac_probability = 0.999,
ransac_model_size = 3,
ransac_max_correspondence_dist = 0.75,
ransac_correspondence_checker_distance = 0.8,
ransac_correspondence_checker_edge_length = 0.6,
ransac_min_corresondence_set_size = 25,
max_icp_correspondence_distance = 0.3,
min_icp_refinement_fitness = 0.7, -- the more aliasing, the higher this should be
dump_aligned_place_recognitions_to_file = false , --useful for debugging
min_submaps_between_loop_closures = 2,
loop_closure_search_radius = 20.0,
consistency_check = deepcopy(LOOP_CLOSURE_CONSISTENCY_CHECK_PARAMETERS),
feature_map_normal_estimation_radius = 2.0,
feature_voxel_size = 0.5,
feature_radius = 2.5,
feature_knn = 100,
feature_normal_knn = 20,
ransac_num_iter = 10000000,
ransac_probability = 0.999,
ransac_model_size = 3,
ransac_max_correspondence_dist = 0.75,
ransac_correspondence_checker_distance = 0.8,
ransac_correspondence_checker_edge_length = 0.6,
ransac_min_corresondence_set_size = 25,
max_icp_correspondence_distance = 0.3,
min_icp_refinement_fitness = 0.7, -- the more aliasing, the higher this should be
dump_aligned_place_recognitions_to_file = false , --useful for debugging
min_submaps_between_loop_closures = 2,
loop_closure_search_radius = 20.0,
consistency_check = deepcopy(LOOP_CLOSURE_CONSISTENCY_CHECK_PARAMETERS),
}

Loading

0 comments on commit 88666b6

Please sign in to comment.