├── esc_move_base_control
├── resource
│ └── esc_move_base_control
├── esc_move_base_control
│ ├── __init__.py
│ └── base_controller.py
├── setup.cfg
├── config
│ └── esc_move_base_control.yaml
├── launch
│ └── esc_move_base_control.launch
├── setup.py
├── test
│ ├── test_pep257.py
│ ├── test_flake8.py
│ └── test_copyright.py
├── LICENSE
└── package.xml
├── esc_move_base_msgs
├── msg
│ └── Path2D.msg
├── action
│ ├── Goto2D.action
│ └── GotoRegion2D.action
├── CMakeLists.txt
└── package.xml
├── esc_move_base_planning
├── launch
│ └── esc_move_base_planning.launch
├── config
│ └── esc_move_base_planning.yaml
├── src
│ ├── state_cost_objective.cpp
│ ├── state_validity_checker_grid_map_R2.cpp
│ ├── new_state_sampler.cpp
│ └── planner
│ │ └── RRTstarMod.cpp
├── package.xml
├── CMakeLists.txt
└── include
│ ├── state_validity_checker_grid_map_R2.h
│ ├── state_cost_objective.h
│ ├── new_state_sampler.h
│ └── planner
│ └── RRTstarMod.h
├── esc_move_base_mapping
├── launch
│ └── esc_move_base_mapping.launch
├── config
│ └── esc_move_base_mapping.yaml
├── package.xml
├── CMakeLists.txt
├── include
│ └── world_modeler.hpp
└── src
│ └── world_modeler.cpp
├── .gitignore
├── LICENSE
└── README.md
/esc_move_base_control/resource/esc_move_base_control:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/esc_move_base_control/esc_move_base_control/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/esc_move_base_msgs/msg/Path2D.msg:
--------------------------------------------------------------------------------
1 | geometry_msgs/Pose2D[] waypoints
2 |
--------------------------------------------------------------------------------
/esc_move_base_control/setup.cfg:
--------------------------------------------------------------------------------
1 | [develop]
2 | script_dir=$base/lib/esc_move_base_control
3 | [install]
4 | install_scripts=$base/lib/esc_move_base_control
5 |
--------------------------------------------------------------------------------
/esc_move_base_msgs/action/Goto2D.action:
--------------------------------------------------------------------------------
1 | #goal definition
2 | geometry_msgs/Pose2D goal
3 | ---
4 | #result definition
5 | bool success
6 | ---
7 | #feedback
--------------------------------------------------------------------------------
/esc_move_base_msgs/action/GotoRegion2D.action:
--------------------------------------------------------------------------------
1 | #goal definition
2 | geometry_msgs/Pose2D goal
3 | float32 radius
4 | ---
5 | #result definition
6 | bool success
7 | ---
8 | #feedback
--------------------------------------------------------------------------------
/esc_move_base_control/config/esc_move_base_control.yaml:
--------------------------------------------------------------------------------
1 | # Control parameters
2 | max_vel: 0.38
3 | min_vel: 0.05
4 | max_turn_rate: 0.7
5 | controller_hz: 100.0
6 | control_path_topic: /esc_move_base_planner/esc_move_base_solution_path
7 | control_output_topic: /pepper/cmd_vel
8 | odometry_topic: /pepper/odom_groundtruth
9 |
--------------------------------------------------------------------------------
/esc_move_base_planning/launch/esc_move_base_planning.launch:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/esc_move_base_control/launch/esc_move_base_control.launch:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/esc_move_base_mapping/launch/esc_move_base_mapping.launch:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Prerequisites
2 | *.d
3 |
4 | # Compiled Object files
5 | *.slo
6 | *.lo
7 | *.o
8 | *.obj
9 |
10 | # Precompiled Headers
11 | *.gch
12 | *.pch
13 |
14 | # Compiled Dynamic libraries
15 | *.so
16 | *.dylib
17 | *.dll
18 |
19 | # Fortran module files
20 | *.mod
21 | *.smod
22 |
23 | # Compiled Static libraries
24 | *.lai
25 | *.la
26 | *.a
27 | *.lib
28 |
29 | # Executables
30 | *.exe
31 | *.out
32 | *.app
33 |
34 | .vscode/
35 | build/
36 | devel/
37 | bags/
38 | .qi/
39 | __pycache__/
40 |
--------------------------------------------------------------------------------
/esc_move_base_control/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import find_packages, setup
2 |
3 | package_name = "esc_move_base_control"
4 |
5 | setup(
6 | name=package_name,
7 | version="0.0.0",
8 | packages=find_packages(exclude=["test"]),
9 | data_files=[
10 | ("share/ament_index/resource_index/packages", ["resource/" + package_name]),
11 | ("share/" + package_name, ["package.xml"]),
12 | ],
13 | install_requires=["setuptools"],
14 | zip_safe=True,
15 | maintainer="sasm",
16 | maintainer_email="sasilva1998@gmail.com",
17 | description="TODO: Package description",
18 | license="MIT",
19 | tests_require=["pytest"],
20 | entry_points={
21 | "console_scripts": [
22 | "base_controller = esc_move_base_control.base_controller:main"
23 | ],
24 | },
25 | )
26 |
--------------------------------------------------------------------------------
/esc_move_base_control/test/test_pep257.py:
--------------------------------------------------------------------------------
1 | # Copyright 2015 Open Source Robotics Foundation, Inc.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | from ament_pep257.main import main
16 | import pytest
17 |
18 |
19 | @pytest.mark.linter
20 | @pytest.mark.pep257
21 | def test_pep257():
22 | rc = main(argv=['.', 'test'])
23 | assert rc == 0, 'Found code style errors / warnings'
24 |
--------------------------------------------------------------------------------
/esc_move_base_mapping/config/esc_move_base_mapping.yaml:
--------------------------------------------------------------------------------
1 | # Mapping
2 | resolution: 0.05
3 | map_frame: "world"
4 | fixed_frame: "odom"
5 | robot_frame: "base_footprint"
6 | offline_octomap_path: ""
7 | # offline_octomap_path: "/home/sasm/pepper_nav_ws/src/pepper_pipeline/map/offline_map_2.bt"
8 | visualize_free_space: True
9 | mapping_max_range: 8
10 | odometry_topic: "/pepper/odom_groundtruth"
11 |
12 | rviz_timer: 1.0 # 0.0 if no visualization
13 |
14 | # pointcloud sensor frame
15 | point_cloud_topics: ["/pepper/camera/depth/points"]
16 | point_cloud_frames: ["/CameraDepth_frame"]
17 |
18 | # social agents config
19 | social_agents_topic: "/pedsim_simulator/simulated_agents"
20 | social_agent_radius: 0.4
21 |
22 | # social relevance validity checking parameters
23 | social_relevance_validity_checking: True
24 | robot_distance_view_max: 8
25 | robot_distance_view_min: 3
26 | robot_angle_view: 1.57
27 | robot_velocity_threshold: 0.35
28 |
--------------------------------------------------------------------------------
/esc_move_base_control/test/test_flake8.py:
--------------------------------------------------------------------------------
1 | # Copyright 2017 Open Source Robotics Foundation, Inc.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | from ament_flake8.main import main_with_errors
16 | import pytest
17 |
18 |
19 | @pytest.mark.flake8
20 | @pytest.mark.linter
21 | def test_flake8():
22 | rc, errors = main_with_errors(argv=[])
23 | assert rc == 0, \
24 | 'Found %d code style errors / warnings:\n' % len(errors) + \
25 | '\n'.join(errors)
26 |
--------------------------------------------------------------------------------
/esc_move_base_planning/config/esc_move_base_planning.yaml:
--------------------------------------------------------------------------------
1 | # Path planning parameters
2 | world_frame: world
3 | planner_name: RRTstar # RRT, RRTstar
4 | planning_bounds_x: [-50.0, 50.0] # [x_min, x_max]
5 | planning_bounds_y: [-50.0, 50.0] # [y_min, y_max]
6 | dynamic_bounds: True # the bounds of the planner will change according to the start and goal configurations
7 | start_state: [0.0, 0.0, 0.0] # [X, Y, Yaw]
8 | goal_state: [5.0, -1.0, 0.0] # [X, Y, Yaw]
9 | timer_period: 1.0
10 | solving_time: 0.9
11 | opport_collision_check: True
12 | reuse_last_best_solution: True
13 | optimization_objective: ExtendedSocialComfort
14 | motion_cost_interpolation: False
15 | xy_goal_tolerance: 0.25
16 | visualize_tree: False
17 |
18 | # Collision checking
19 | grid_map_service: /esc_move_base_mapping/get_grid_map
20 |
21 | robot_base_radius: 0.35
22 |
23 | control_active_topic: "/control_active_topic"
24 |
25 | odometry_topic: /pepper/odom_groundtruth
26 | query_goal_topic: /esc_move_base_planner/query_goal
27 |
28 | goto_action: /esc_goto_action
29 |
--------------------------------------------------------------------------------
/esc_move_base_control/test/test_copyright.py:
--------------------------------------------------------------------------------
1 | # Copyright 2015 Open Source Robotics Foundation, Inc.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | from ament_copyright.main import main
16 | import pytest
17 |
18 |
19 | # Remove the `skip` decorator once the source file(s) have a copyright header
20 | @pytest.mark.skip(reason='No copyright header has been placed in the generated source file.')
21 | @pytest.mark.copyright
22 | @pytest.mark.linter
23 | def test_copyright():
24 | rc = main(argv=['.', 'test'])
25 | assert rc == 0, 'Found errors'
26 |
--------------------------------------------------------------------------------
/esc_move_base_control/LICENSE:
--------------------------------------------------------------------------------
1 | Permission is hereby granted, free of charge, to any person obtaining a copy
2 | of this software and associated documentation files (the "Software"), to deal
3 | in the Software without restriction, including without limitation the rights
4 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
5 | copies of the Software, and to permit persons to whom the Software is
6 | furnished to do so, subject to the following conditions:
7 |
8 | The above copyright notice and this permission notice shall be included in
9 | all copies or substantial portions of the Software.
10 |
11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
14 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
17 | THE SOFTWARE.
18 |
--------------------------------------------------------------------------------
/esc_move_base_control/package.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | esc_move_base_control
5 | 0.0.0
6 | TODO: Package description
7 | sasm
8 | MIT
9 |
10 | ament_copyright
11 | ament_flake8
12 | ament_pep257
13 | python3-pytest
14 |
15 |
16 | rclpy
17 | std_msgs
18 | rclpy
19 | std_msgs
20 | rclpy
21 | std_msgs
22 |
23 | geometry_msgs
24 |
25 | esc_move_base_msgs
26 |
27 |
28 | ament_python
29 |
30 |
31 |
--------------------------------------------------------------------------------
/esc_move_base_planning/src/state_cost_objective.cpp:
--------------------------------------------------------------------------------
1 | /*! \file state_cost_objective.cpp
2 | * \brief State cost objective.
3 | *
4 | * \date Mar 31, 2015
5 | * \author Juan David Hernandez Vega, juandhv@rice.edu
6 | *
7 | * \details State cost objective. Define different state cost objectives.
8 | *
9 | * Based on Juan D. Hernandez Vega's PhD thesis, University of Girona
10 | * http://hdl.handle.net/10803/457592, http://www.tdx.cat/handle/10803/457592
11 | */
12 |
13 | #include
14 |
15 | ob::OptimizationObjectivePtr getExtendedSocialComfortObjective(const ob::SpaceInformationPtr &si,
16 | bool motion_cost_interpolation)
17 | {
18 | // ROS_INFO_STREAM("Sending extended social comfort objective");
19 | return ob::OptimizationObjectivePtr(new ExtendedSocialComfortObjective(si, motion_cost_interpolation));
20 | }
21 |
22 | ob::OptimizationObjectivePtr getPathLengthObjective(const ob::SpaceInformationPtr &si)
23 | {
24 | return ob::OptimizationObjectivePtr(new ob::PathLengthOptimizationObjective(si));
25 | }
26 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Steven A. Silva
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/esc_move_base_msgs/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.8)
2 | project(esc_move_base_msgs)
3 |
4 | if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
5 | add_compile_options(-Wall -Wextra -Wpedantic)
6 | endif()
7 |
8 | # find dependencies
9 | find_package(ament_cmake REQUIRED)
10 | find_package(rclcpp REQUIRED)
11 | find_package(std_msgs REQUIRED)
12 | find_package(rosidl_default_generators REQUIRED)
13 | find_package(geometry_msgs REQUIRED)
14 | find_package(pedsim_msgs REQUIRED)
15 |
16 | rosidl_generate_interfaces(${PROJECT_NAME}
17 | "msg/Path2D.msg"
18 | "action/Goto2D.action"
19 | "action/GotoRegion2D.action"
20 | DEPENDENCIES geometry_msgs pedsim_msgs
21 | )
22 |
23 | if(BUILD_TESTING)
24 | find_package(ament_lint_auto REQUIRED)
25 | # the following line skips the linter which checks for copyrights
26 | # comment the line when a copyright and license is added to all source files
27 | set(ament_cmake_copyright_FOUND TRUE)
28 | # the following line skips cpplint (only works in a git repo)
29 | # comment the line when this package is in a git repo and when
30 | # a copyright and license is added to all source files
31 | set(ament_cmake_cpplint_FOUND TRUE)
32 | ament_lint_auto_find_test_dependencies()
33 | endif()
34 |
35 | ament_package()
36 |
--------------------------------------------------------------------------------
/esc_move_base_msgs/package.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | esc_move_base_msgs
5 | 0.0.0
6 | The esc_move_base_msgs package
7 | Steven Silva
8 | MIT
9 |
10 | ament_cmake
11 |
12 | ament_lint_auto
13 | ament_lint_common
14 |
15 |
16 | rclcpp
17 | std_msgs
18 | rclcpp
19 | std_msgs
20 | rclcpp
21 | std_msgs
22 |
23 | pedsim_msgs
24 | geometry_msgs
25 |
26 | rosidl_default_generators
27 | rosidl_default_runtime
28 | rosidl_interface_packages
29 |
30 | action_msgs
31 |
32 |
33 |
34 | ament_cmake
35 |
36 |
37 |
--------------------------------------------------------------------------------
/esc_move_base_planning/package.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | esc_move_base_planning
5 | 0.0.0
6 | The esc_move_base_planning package
7 | Steven Silva
8 | MIT
9 |
10 | ament_cmake
11 |
12 | ament_lint_auto
13 | ament_lint_common
14 |
15 | rclcpp
16 | std_msgs
17 | rclcpp
18 | std_msgs
19 | rclcpp
20 | std_msgs
21 |
22 | tf2_ros
23 |
24 |
25 | esc_move_base_msgs
26 | esc_move_base_control
27 | esc_move_base_mapping
28 |
29 |
30 |
31 | pedsim_msgs
32 |
33 |
34 | ompl
35 |
36 | ament_cmake
37 |
38 |
39 |
--------------------------------------------------------------------------------
/esc_move_base_mapping/package.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | esc_move_base_mapping
5 | 0.0.0
6 | The esc_move_base_mapping package
7 | Steven Silva
8 | MIT
9 |
10 | ament_cmake
11 |
12 | ament_lint_auto
13 | ament_lint_common
14 |
15 | rclcpp
16 | std_msgs
17 | pcl_ros
18 | rclcpp
19 | std_msgs
20 | pcl_ros
21 | rclcpp
22 | std_msgs
23 | pcl_ros
24 |
25 |
26 |
27 | nav_msgs
28 | esc_move_base_msgs
29 | tf2_ros
30 |
31 | pedsim_msgs
32 | grid_map
33 |
34 |
35 | ament_cmake
36 |
37 |
38 |
--------------------------------------------------------------------------------
/esc_move_base_planning/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.8)
2 | project(esc_move_base_planning)
3 |
4 | if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
5 | add_compile_options(-Wall -Wextra -Wpedantic)
6 | endif()
7 |
8 | # find dependencies
9 | find_package(ament_cmake REQUIRED)
10 | find_package(rclcpp REQUIRED)
11 | find_package(std_msgs REQUIRED)
12 | find_package(nav_msgs REQUIRED)
13 | find_package(esc_move_base_msgs REQUIRED)
14 | find_package(pedsim_msgs REQUIRED)
15 | find_package(tf2_ros REQUIRED)
16 | find_package(grid_map_core REQUIRED)
17 | find_package(grid_map_cv REQUIRED)
18 | find_package(grid_map_filters REQUIRED)
19 | find_package(grid_map_msgs REQUIRED)
20 | find_package(grid_map_ros REQUIRED)
21 | find_package(grid_map_sdf REQUIRED)
22 | find_package(grid_map_visualization REQUIRED)
23 | find_package(tf2_geometry_msgs REQUIRED)
24 | find_package(ompl REQUIRED)
25 | find_package(geometry_msgs REQUIRED)
26 |
27 | include_directories(
28 | include
29 | SYSTEM)
30 |
31 | if(BUILD_TESTING)
32 | find_package(ament_lint_auto REQUIRED)
33 | # the following line skips the linter which checks for copyrights
34 | # comment the line when a copyright and license is added to all source files
35 | set(ament_cmake_copyright_FOUND TRUE)
36 | # the following line skips cpplint (only works in a git repo)
37 | # comment the line when this package is in a git repo and when
38 | # a copyright and license is added to all source files
39 | set(ament_cmake_cpplint_FOUND TRUE)
40 | ament_lint_auto_find_test_dependencies()
41 | endif()
42 |
43 | set(dependencies
44 | rclcpp
45 | pedsim_msgs
46 | nav_msgs
47 | esc_move_base_msgs
48 | grid_map_ros
49 | grid_map_msgs
50 | tf2_geometry_msgs
51 | geometry_msgs
52 | OMPL
53 | )
54 |
55 | add_executable(esc_move_base_planner
56 | src/planning_framework_main.cpp
57 | src/state_validity_checker_grid_map_R2.cpp
58 | src/state_cost_objective.cpp
59 | src/new_state_sampler.cpp
60 | src/planner/RRTstarMod.cpp
61 | )
62 | ament_target_dependencies(esc_move_base_planner ${dependencies})
63 | target_link_libraries(esc_move_base_planner ${OMPL_LIBRARIES})
64 |
65 | install(TARGETS
66 | esc_move_base_planner
67 | DESTINATION lib/${PROJECT_NAME})
68 |
69 | ament_package()
70 |
--------------------------------------------------------------------------------
/esc_move_base_mapping/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.8)
2 | project(esc_move_base_mapping)
3 |
4 | if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
5 | add_compile_options(-Wall -Wextra -Wpedantic)
6 | endif()
7 |
8 | # find dependencies
9 | find_package(ament_cmake REQUIRED)
10 | find_package(rclcpp REQUIRED)
11 | find_package(std_msgs REQUIRED)
12 | find_package(nav_msgs REQUIRED)
13 | find_package(esc_move_base_msgs REQUIRED)
14 | find_package(pedsim_msgs REQUIRED)
15 | find_package(tf2_ros REQUIRED)
16 | find_package(grid_map_core REQUIRED)
17 | find_package(grid_map_cv REQUIRED)
18 | find_package(grid_map_filters REQUIRED)
19 | # find_package(grid_map_loader REQUIRED)
20 | find_package(grid_map_msgs REQUIRED)
21 | find_package(grid_map_octomap REQUIRED)
22 | find_package(grid_map_ros REQUIRED)
23 | # find_package(grid_map_rviz_plugin REQUIRED)
24 | find_package(grid_map_sdf REQUIRED)
25 | find_package(grid_map_visualization REQUIRED)
26 | find_package(tf2_geometry_msgs REQUIRED)
27 | find_package(octomap_msgs REQUIRED)
28 | find_package(OCTOMAP REQUIRED)
29 | find_package(octomap_server REQUIRED)
30 | find_package(pcl_ros REQUIRED)
31 | find_package(laser_geometry REQUIRED)
32 |
33 | include_directories(
34 | include
35 | SYSTEM)
36 |
37 | if(BUILD_TESTING)
38 | find_package(ament_lint_auto REQUIRED)
39 | # the following line skips the linter which checks for copyrights
40 | # comment the line when a copyright and license is added to all source files
41 | set(ament_cmake_copyright_FOUND TRUE)
42 | # the following line skips cpplint (only works in a git repo)
43 | # comment the line when this package is in a git repo and when
44 | # a copyright and license is added to all source files
45 | set(ament_cmake_cpplint_FOUND TRUE)
46 | ament_lint_auto_find_test_dependencies()
47 | endif()
48 |
49 | set(dependencies
50 | rclcpp
51 | pedsim_msgs
52 | nav_msgs
53 | esc_move_base_msgs
54 | grid_map_ros
55 | grid_map_msgs
56 | tf2_geometry_msgs
57 | octomap_msgs
58 | octomap_server
59 | pcl_ros
60 | grid_map_octomap
61 | laser_geometry
62 | )
63 |
64 | add_executable(esc_move_base_mapper
65 | src/world_modeler.cpp
66 | )
67 | ament_target_dependencies(esc_move_base_mapper ${dependencies})
68 | target_link_libraries(esc_move_base_mapper ${OCTOMAP_LIBRARIES})
69 |
70 | install(TARGETS
71 | esc_move_base_mapper
72 | DESTINATION lib/${PROJECT_NAME})
73 |
74 | ament_package()
75 |
--------------------------------------------------------------------------------
/esc_move_base_planning/include/state_validity_checker_grid_map_R2.h:
--------------------------------------------------------------------------------
1 | /*! \file state_validity_checker_grid_map_R2.hpp
2 | * \brief State validity checker.
3 | *
4 | * \date March 5, 2015
5 | * \author Juan David Hernandez Vega, juandhv@rice.edu
6 | *
7 | * \details Check is a given configuration R2 is collision-free.
8 | * The workspace is represented by an GridMap and collision check is done iterating.
9 | *
10 | * Based on Juan D. Hernandez Vega's PhD thesis, University of Girona
11 | * http://hdl.handle.net/10803/457592, http://www.tdx.cat/handle/10803/457592
12 | */
13 |
14 | #ifndef OMPL_CONTRIB_STATE_VALIDITY_CHECKER_GRID_MAP_R2_
15 | #define OMPL_CONTRIB_STATE_VALIDITY_CHECKER_GRID_MAP_R2_
16 |
17 | // ROS2
18 | #include
19 | // ROS2 messages
20 | #include
21 | #include
22 | #include
23 |
24 | // Standard libraries
25 | #include
26 | #include
27 | #include
28 |
29 | // grid map library
30 | #include
31 | #include
32 | #include
33 |
34 | // OMPL
35 | #include
36 | #include
37 | #include
38 | #include
39 | #include
40 |
41 | // Boost
42 | #include
43 | #include
44 | #include
45 |
46 | // Eigen
47 | #include
48 |
49 | #include
50 | #include
51 | #include
52 |
53 | // #include
54 | #include
55 | #include
56 |
57 | // ROS-GridMap interface
58 | using grid_map_msgs::srv::GetGridMap;
59 | // Standard namespace
60 | using namespace std;
61 |
62 | // OMPL namespaces
63 | namespace ob = ompl::base;
64 | namespace og = ompl::geometric;
65 |
66 | //! GridMapStateValidityCheckerR2 class.
67 | /*!
68 | GridMap State Validity checker.
69 | Extension of an abstract class used to implement the state validity checker over a grid map.
70 | */
71 | class GridMapStateValidityCheckerR2 : public ob::StateValidityChecker
72 | {
73 | public:
74 | //! GridMapStateValidityCheckerR2 constructor.
75 | /*!
76 | * Besides of initializing the private attributes, it loads the grid map.
77 | */
78 | GridMapStateValidityCheckerR2(const ob::SpaceInformationPtr &si, const bool opport_collision_check,
79 | std::vector planning_bounds_x, std::vector planning_bounds_y, grid_map_msgs::msg::GridMap grid_map_msg, const double robot_radius);
80 |
81 | //! GridMapStateValidityCheckerR2 destructor.
82 | /*!
83 | * Destroy the grid map.
84 | */
85 | ~GridMapStateValidityCheckerR2();
86 |
87 | //! State validator.
88 | /*!
89 | * Function that verifies if the given state is valid (i.e. is free of collision) iterating the grid map
90 | */
91 | virtual bool isValid(const ob::State *state) const;
92 |
93 | /*
94 | * Returns the cost value for the integration of the path defined on the equation that defines an extended
95 | * social comfort zone model.
96 | */
97 | virtual double checkExtendedSocialComfort(const ob::State *state,
98 | const ob::SpaceInformationPtr space) const;
99 |
100 | virtual bool isValidPoint(const ob::State *state) const;
101 |
102 | private:
103 | double grid_map_min_x_, grid_map_min_y_, grid_map_min_z_;
104 | double grid_map_max_x_, grid_map_max_y_, grid_map_max_z_;
105 | std::vector planning_bounds_x_, planning_bounds_y_;
106 | double robot_base_radius_;
107 | std::string grid_map_service_;
108 | grid_map_msgs::msg::GridMap grid_map_msgs_;
109 | grid_map::GridMap grid_map_;
110 |
111 | // cost objective type
112 | std::string optimization_objective;
113 |
114 | bool opport_collision_check_;
115 |
116 | grid_map::Matrix obstacles_grid_map_;
117 | grid_map::Matrix comfort_grid_map_;
118 | };
119 |
120 | #endif
121 |
--------------------------------------------------------------------------------
/esc_move_base_planning/include/state_cost_objective.h:
--------------------------------------------------------------------------------
1 | /*! \file state_cost_objective.hpp
2 | * \brief State cost objective.
3 | *
4 | * \date Mar 31, 2015
5 | * \author Juan David Hernandez Vega, juandhv@rice.edu
6 | *
7 | * \details State cost objective. Define different state cost objectives.
8 | *
9 | * Based on Juan D. Hernandez Vega's PhD thesis, University of Girona
10 | * http://hdl.handle.net/10803/457592, http://www.tdx.cat/handle/10803/457592
11 | */
12 |
13 | #ifndef OMPL_CONTRIB_STATE_COST_OBJECTIVES_
14 | #define OMPL_CONTRIB_STATE_COST_OBJECTIVES_
15 |
16 | // OMPL
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include
23 |
24 | #include
25 |
26 | namespace ob = ompl::base;
27 |
28 | class ExtendedSocialComfortObjective : public ob::StateCostIntegralObjective
29 | {
30 | private:
31 | public:
32 | ExtendedSocialComfortObjective(const ob::SpaceInformationPtr &si, bool enableMotionCostInterpolation)
33 | : ob::StateCostIntegralObjective(si, enableMotionCostInterpolation)
34 | {
35 | // ROS_INFO_STREAM("running extended social comfort objective checker");
36 | }
37 |
38 | // in case of modifying the cost calculation function here it is where it should be done
39 |
40 | ob::Cost stateCost(const ob::State *s) const
41 | {
42 | std::shared_ptr state_vality_checker =
43 | std::static_pointer_cast(si_->getStateValidityChecker());
44 | return ob::Cost(state_vality_checker->checkExtendedSocialComfort(s, si_));
45 | }
46 |
47 | ob::Cost motionCost(const ob::State *s1, const ob::State *s2) const
48 | {
49 | if (interpolateMotionCost_)
50 | {
51 | ob::Cost totalCost = this->identityCost();
52 |
53 | int nd = si_->getStateSpace()->validSegmentCount(s1, s2);
54 | // nd = int(nd/10);
55 |
56 | ob::State *test1 = si_->cloneState(s1);
57 | ob::Cost prevStateCost = this->stateCost(test1);
58 | if (nd > 1)
59 | {
60 | ob::State *test2 = si_->allocState();
61 | for (int j = 1; j < nd; ++j)
62 | {
63 | si_->getStateSpace()->interpolate(s1, s2, (double)j / (double)nd, test2);
64 | ob::Cost nextStateCost = this->stateCost(test2);
65 | totalCost = ob::Cost(
66 | totalCost.value() +
67 | this->trapezoid(prevStateCost, nextStateCost, si_->distance(test1, test2)).value());
68 | std::swap(test1, test2);
69 | prevStateCost = nextStateCost;
70 | }
71 | si_->freeState(test2);
72 | }
73 |
74 | // Lastly, add s2
75 | totalCost = ob::Cost(
76 | totalCost.value() +
77 | this->trapezoid(prevStateCost, this->stateCost(s2), si_->distance(test1, s2)).value());
78 |
79 | si_->freeState(test1);
80 |
81 | // ROS_INFO_STREAM("Total cost: " << totalCost);
82 |
83 | return totalCost;
84 | }
85 | else
86 | {
87 | // ROS_INFO_STREAM("Trapezoid cost: " << this->trapezoid(this->stateCost(s1), this->stateCost(s2),
88 | // si_->distance(s1, s2)));
89 | return this->trapezoid(this->stateCost(s1), this->stateCost(s2), si_->distance(s1, s2));
90 | }
91 | }
92 | };
93 |
94 | /** Return an optimization objective which attempts to steer the robot
95 | away from social agents in order to keep comfort with an extended social comfort model. */
96 | ob::OptimizationObjectivePtr getExtendedSocialComfortObjective(const ob::SpaceInformationPtr &si,
97 | bool motion_cost_interpolation);
98 |
99 | /** Returns a structure representing the optimization objective to use
100 | for optimal motion planning. This method returns an objective
101 | which attempts to minimize the length in configuration space of
102 | computed paths. */
103 | ob::OptimizationObjectivePtr getPathLengthObjective(const ob::SpaceInformationPtr &si);
104 |
105 | #endif
106 |
--------------------------------------------------------------------------------
/esc_move_base_planning/src/state_validity_checker_grid_map_R2.cpp:
--------------------------------------------------------------------------------
1 | /*! \file state_validity_checker_grid_map_R2.cpp
2 | * \brief State validity checker.
3 | *
4 | * \date November 07, 2022
5 | * \author Juan David Hernandez Vega, HernandezVegaJ@cardiff.ac.uk
6 | * \author Steven Alexander Silva Mendoza, silvas1@cardiff.ac.uk
7 | *
8 | * \details Check is a given configuration R2 is collision-free.
9 | * The workspace is represented by an GridMap and collision check is done by iterating.
10 | *
11 | * Based on Juan D. Hernandez Vega's PhD thesis, University of Girona
12 | * http://hdl.handle.net/10803/457592, http://www.tdx.cat/handle/10803/457592
13 | */
14 |
15 | #include
16 |
17 | GridMapStateValidityCheckerR2::GridMapStateValidityCheckerR2(const ob::SpaceInformationPtr &si,
18 | const bool opport_collision_check,
19 | std::vector planning_bounds_x,
20 | std::vector planning_bounds_y, grid_map_msgs::msg::GridMap grid_map_msg, const double robot_radius)
21 | : ob::StateValidityChecker(si), robot_base_radius_(0.4)
22 | {
23 |
24 | opport_collision_check_ = opport_collision_check;
25 | planning_bounds_x_ = planning_bounds_x;
26 | planning_bounds_y_ = planning_bounds_y;
27 | robot_base_radius_ = robot_radius;
28 |
29 | if (grid_map::GridMapRosConverter::fromMessage(grid_map_msg, grid_map_))
30 | {
31 |
32 | grid_map_max_x_ = grid_map_msg.info.pose.position.x + (grid_map_msg.info.length_x / 2);
33 | grid_map_min_x_ = grid_map_msg.info.pose.position.x - (grid_map_msg.info.length_x / 2);
34 |
35 | grid_map_max_y_ = grid_map_msg.info.pose.position.y + (grid_map_msg.info.length_y / 2);
36 | grid_map_min_y_ = grid_map_msg.info.pose.position.y - (grid_map_msg.info.length_y / 2);
37 | }
38 |
39 | try
40 | {
41 | obstacles_grid_map_ = grid_map_["full"];
42 | comfort_grid_map_ = grid_map_["comfort"];
43 | }
44 | catch (...)
45 | {
46 | }
47 | }
48 |
49 | bool GridMapStateValidityCheckerR2::isValid(const ob::State *state) const
50 | {
51 | const ob::RealVectorStateSpace::StateType *state_r2 = state->as();
52 |
53 | // ompl::tools::Profiler::Begin("collision");
54 |
55 | // extract the component of the state and cast it to what we expect
56 |
57 | if (opport_collision_check_ &&
58 | (state_r2->values[0] < grid_map_min_x_ || state_r2->values[1] < grid_map_min_y_ ||
59 | state_r2->values[0] > grid_map_max_x_ || state_r2->values[1] > grid_map_max_y_))
60 | {
61 | // ompl::tools::Profiler::End("collision");
62 | return true;
63 | }
64 |
65 | if (state_r2->values[0] < planning_bounds_x_[0] || state_r2->values[1] < planning_bounds_y_[0] ||
66 | state_r2->values[0] > planning_bounds_x_[1] || state_r2->values[1] > planning_bounds_y_[1])
67 | {
68 | // ompl::tools::Profiler::End("collision");
69 | return false;
70 | }
71 |
72 | grid_map::Position query(state_r2->values[0], state_r2->values[1]);
73 |
74 | for (grid_map::CircleIterator iterator(grid_map_, query, robot_base_radius_);
75 | !iterator.isPastEnd(); ++iterator)
76 | {
77 | const grid_map::Index index(*iterator);
78 |
79 | if (obstacles_grid_map_(index(0), index(1)) > 20)
80 | {
81 | return false;
82 | }
83 | }
84 |
85 | return true;
86 | }
87 |
88 | double GridMapStateValidityCheckerR2::checkExtendedSocialComfort(const ob::State *state,
89 | const ob::SpaceInformationPtr space) const
90 | {
91 | double state_risk = 0.0;
92 |
93 | const ob::RealVectorStateSpace::StateType *state_r2 = state->as();
94 |
95 | grid_map::Position query(state_r2->values[0], state_r2->values[1]);
96 |
97 | grid_map::Index index;
98 |
99 | if (grid_map_.getIndex(query, index))
100 | {
101 | state_risk = comfort_grid_map_(index(0), index(1));
102 | }
103 |
104 | if (state_risk < 1 || isnan(state_risk))
105 | {
106 | state_risk = 1;
107 | }
108 |
109 | return state_risk;
110 | }
111 |
112 | bool GridMapStateValidityCheckerR2::isValidPoint(const ob::State *state) const
113 | {
114 |
115 | // extract the component of the state and cast it to what we expect
116 | const ob::RealVectorStateSpace::StateType *state_r2 = state->as();
117 |
118 | grid_map::Position query(state_r2->values[0], state_r2->values[1]);
119 |
120 | grid_map::Index index;
121 |
122 | if (grid_map_.getIndex(query, index))
123 | {
124 | if (obstacles_grid_map_(index(0), index(1)) > 20)
125 | {
126 | return false;
127 | }
128 | }
129 |
130 | return true;
131 | }
132 |
133 | GridMapStateValidityCheckerR2::~GridMapStateValidityCheckerR2()
134 | {
135 | }
136 |
--------------------------------------------------------------------------------
/esc_move_base_planning/src/new_state_sampler.cpp:
--------------------------------------------------------------------------------
1 | /*********************************************************************
2 | * Software License Agreement (BSD License)
3 | *
4 | * Copyright (c) 2014, Rice University
5 | * All rights reserved.
6 | *
7 | * Redistribution and use in source and binary forms, with or without
8 | * modification, are permitted provided that the following conditions
9 | * are met:
10 | *
11 | * * Redistributions of source code must retain the above copyright
12 | * notice, this list of conditions and the following disclaimer.
13 | * * Redistributions in binary form must reproduce the above
14 | * copyright notice, this list of conditions and the following
15 | * disclaimer in the documentation and/or other materials provided
16 | * with the distribution.
17 | * * Neither the name of the Rice University nor the names of its
18 | * contributors may be used to endorse or promote products derived
19 | * from this software without specific prior written permission.
20 | *
21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 | * POSSIBILITY OF SUCH DAMAGE.
33 | *
34 | * Modified. Based on Juan D. Hernandez Vega's PhD thesis, University of Girona
35 | * http://hdl.handle.net/10803/457592, http://www.tdx.cat/handle/10803/457592
36 | *********************************************************************/
37 |
38 | /* Author: Javier V. Gómez*/
39 |
40 | #include
41 | #include
42 |
43 | ob::StateSamplerPtr newAllocStateSampler(const ob::StateSpace *space, const ob::PlannerPtr &planner,
44 | const std::vector &start_states)
45 | {
46 | NewStateSampler *sampler = new NewStateSampler(space, space->allocDefaultStateSampler());
47 | // std::cout << "start_states.size(): " << start_states.size() << std::endl;
48 | // std::cout << "bias: " << planner->as()->getGoalBias() << std::endl;
49 | sampler->setStatesToSample(start_states);
50 | sampler->setPlanner(planner);
51 | return ob::StateSamplerPtr(sampler);
52 | }
53 |
54 | void NewStateSampler::setPlanner(const ob::PlannerPtr &planner)
55 | {
56 | planner_ = planner;
57 | }
58 |
59 | void NewStateSampler::sampleUniform(ob::State *state)
60 | {
61 | // std::cout << "statesToSample_.size(): " << statesToSample_.size() << std::endl;
62 | if (!statesToSample_.empty())
63 | {
64 | // simple_setup_->get()->getPlanner()->as()->setGoalBias(0.0);
65 | planner_->as()->setGoalBias(0.0);
66 | getNextSample(state);
67 | // std::cout << "reusing state: " << state->as()->getX() << ", "
68 | // << state->as()->getY() << std::endl; std::cout << "bias: " <<
69 | // planner_->as()->getGoalBias() << std::endl;
70 | }
71 | else
72 | { // if(!reusing_){
73 | // simple_setup_->get()->getPlanner()->as();//->setGoalBias(0.05);
74 | planner_->as()->setGoalBias(0.05);
75 | sampler_->sampleUniform(state);
76 | // std::cout << "state: " << state->as()->getX() << ", " <<
77 | //state->as()->getY() << std::endl; std::cout << "bias: " <<
78 | // planner_->as()->getGoalBias() << std::endl;
79 | }
80 | // else
81 | // std::cout << "reusing state: " << state->as()->values[0] << ",
82 | //" << state->as()->values[1] << std::endl;
83 | }
84 |
85 | void NewStateSampler::sampleUniformNear(ob::State *state, const ob::State *near, const double distance)
86 | {
87 | if (!statesToSample_.empty())
88 | getNextSample(state);
89 | else
90 | sampler_->sampleUniformNear(state, near, distance);
91 | }
92 |
93 | void NewStateSampler::sampleGaussian(ob::State *state, const ob::State *mean, const double stdDev)
94 | {
95 | if (!statesToSample_.empty())
96 | getNextSample(state);
97 | else
98 | sampler_->sampleGaussian(state, mean, stdDev);
99 | }
100 |
101 | void NewStateSampler::setStatesToSample(const std::vector &states)
102 | {
103 | // boost::mutex::scoped_lock slock(statesLock_);
104 | for (size_t i = 0; i < statesToSample_.size(); ++i)
105 | space_->freeState(statesToSample_[i]);
106 | statesToSample_.clear();
107 |
108 | statesToSample_.reserve(states.size());
109 | // std::cout << "state to be recovered: " << states.size() << std::endl;
110 | // push in reverse order, so that the states are popped in order in getNextSample()
111 | // for (std::vector::const_iterator st = states.begin(); st != states.end(); ++st)
112 | reusing_ = false;
113 | for (size_t i = 0; i < states.size(); i++)
114 | {
115 | reusing_ = true;
116 | ob::State *s = space_->allocState();
117 | space_->copyState(s, states[i]);
118 | statesToSample_.push_back(s);
119 | // std::cout << "recovering state: " << s->as()->values[0] << ",
120 | // " << s->as()->values[1] << std::endl;
121 | }
122 | }
123 |
124 | void NewStateSampler::getNextSample(ob::State *state)
125 | {
126 | // boost::mutex::scoped_lock slock(statesLock_);
127 | space_->copyState(state, statesToSample_.back());
128 | space_->freeState(statesToSample_.back());
129 | statesToSample_.pop_back();
130 | }
131 |
132 | void NewStateSampler::clear()
133 | {
134 | // boost::mutex::scoped_lock slock(statesLock_);
135 | for (size_t i = 0; i < statesToSample_.size(); ++i)
136 | space_->freeState(statesToSample_[i]);
137 | statesToSample_.clear();
138 | sampler_.reset();
139 | }
140 |
141 | //-------
142 |
--------------------------------------------------------------------------------
/esc_move_base_mapping/include/world_modeler.hpp:
--------------------------------------------------------------------------------
1 | #ifndef WORLD_MODELER_HPP
2 | #define WORLD_MODELER_HPP
3 |
4 | // ROS2
5 | #include
6 |
7 | // ROS2 messages
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 |
19 | // ROS2 services
20 | #include
21 |
22 | // ROS2 LaserScan tools
23 | #include
24 |
25 | // tf2
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include "message_filters/subscriber.h"
31 | #include
32 |
33 | // Octomap
34 | #include
35 | #include
36 | #include
37 | typedef octomap_msgs::srv::GetOctomap OctomapSrv;
38 | #include
39 |
40 | // grid map library
41 | #include
42 | #include
43 | #include
44 | #include
45 | #include
46 | #include
47 | #include
48 |
49 | // PCL
50 | #include
51 | #include
52 | #include
53 | #include
54 | #include
55 | #include
56 | #include
57 | #include
58 | #include
59 | #include
60 | #include
61 | #include
62 |
63 | // PEDSIM
64 | #include
65 |
66 | #include
67 |
68 | typedef pcl::PointXYZ PCLPoint;
69 | typedef pcl::PointCloud PCLPointCloud;
70 | typedef octomap::OcTree OcTreeT;
71 |
72 | void stopNode(int sig)
73 | {
74 | rclcpp::shutdown();
75 | exit(0);
76 | }
77 |
78 | //! WorldModeler class.
79 | /*!
80 | * Autopilot Laser WorldModeler.
81 | * Create a WorldModeler using information from laser scans.
82 | */
83 | class WorldModeler : public rclcpp::Node
84 | {
85 | public:
86 | //! Constructor
87 | WorldModeler();
88 | //! Destructor
89 | virtual ~WorldModeler();
90 | //! Callback for getting the laser_scan data
91 | void laserScanCallback(const sensor_msgs::msg::LaserScan::SharedPtr laser_scan_msg);
92 | //! Callback for getting the point_cloud data
93 | void pointCloudCallback(const sensor_msgs::msg::PointCloud2::SharedPtr cloud);
94 | //! Callback for getting current vehicle odometry
95 | void odomCallback(const nav_msgs::msg::Odometry::SharedPtr odom_msg);
96 | //! Callback for getting current agent states
97 | void agentStatesCallback(const pedsim_msgs::msg::AgentStates::SharedPtr agent_states_msg);
98 | bool isAgentInRFOV(const pedsim_msgs::msg::AgentState agent_state);
99 | //! Check if robot is in front of agent.
100 | bool isRobotInFront(pedsim_msgs::msg::AgentState agent_state, grid_map::Position position);
101 | //! Calculate comfort in specific position in gridmap
102 | double getExtendedPersonalSpace(pedsim_msgs::msg::AgentState agent_state, grid_map::Position position);
103 | //! Periodic callback to publish the map for visualization.
104 | void timerCallback();
105 | void insertScan(const geometry_msgs::msg::Vector3 &sensorOriginTf, const PCLPointCloud &ground,
106 | const PCLPointCloud &nonground, const double &max_range, const double &min_range);
107 | //! Service to save a binary Octomap (.bt)
108 | bool saveBinaryOctomapSrv(const std::shared_ptr req,
109 | std::shared_ptr res);
110 | //! Service to save a full Octomap (.ot)
111 | bool saveFullOctomapSrv(const std::shared_ptr req,
112 | std::shared_ptr res);
113 | //! Service to get a binary Octomap
114 | bool getBinaryOctomapSrv(const std::shared_ptr req,
115 | std::shared_ptr res);
116 | //! Service to get grid map
117 | bool getGridMapSrv(const std::shared_ptr req,
118 | std::shared_ptr res);
119 |
120 | //! Publish the WorldModeler
121 | void publishMap();
122 | //! Redefine the gridmap with octomap
123 | void defineSocialGridMap();
124 |
125 | private:
126 | //! Filter outliers
127 | void filterSingleOutliers(sensor_msgs::msg::LaserScan &laser_scan_msg,
128 | std::vector &rngflags);
129 |
130 | // ROS2
131 | rclcpp::Publisher::SharedPtr octomap_marker_pub_;
132 | rclcpp::Publisher::SharedPtr grid_map_pub_;
133 | rclcpp::Subscription::SharedPtr odom_sub_;
134 | rclcpp::Subscription::SharedPtr agent_states_sub_;
135 | std::shared_ptr> point_cloud_sub_;
136 | std::shared_ptr> laser_scan_sub_;
137 |
138 | // SERVICES
139 | rclcpp::Service::SharedPtr save_binary_octomap_srv_;
140 | rclcpp::Service::SharedPtr save_full_octomap_srv_;
141 | rclcpp::Service::SharedPtr get_binary_octomap_srv_;
142 | rclcpp::Service::SharedPtr get_grid_map_srv_;
143 |
144 | rclcpp::TimerBase::SharedPtr timer_;
145 | std::shared_ptr> point_cloud_mn_;
146 | std::shared_ptr> laser_scan_mn_;
147 |
148 | // tf2
149 | std::shared_ptr tf_listener_{nullptr};
150 | std::shared_ptr tf_buffer_;
151 |
152 | // Names
153 | std::string map_frame_, fixed_frame_, robot_frame_, offline_octomap_path_,
154 | odometry_topic_, social_agents_topic_;
155 |
156 | // Laser scans
157 | std::string laser_scan_frame_, laser_scan_topic_;
158 |
159 | // Point Clouds
160 | std::string point_cloud_topic_, point_cloud_frame_;
161 |
162 | // ROS Messages
163 | sensor_msgs::msg::PointCloud2 cloud_;
164 |
165 | nav_msgs::msg::Odometry::SharedPtr robot_odometry_;
166 |
167 | // pedsim messages
168 | pedsim_msgs::msg::AgentStates::SharedPtr agent_states_;
169 | pedsim_msgs::msg::AgentStates relevant_agent_states_;
170 |
171 | pedsim_msgs::msg::AgentStates social_agents_in_radius_;
172 | std::vector social_agents_in_radius_vector_;
173 |
174 | double social_agent_radius_;
175 |
176 | // Octree
177 | octomap::OcTree *octree_;
178 | double octree_resol_, minimum_range_, rviz_timer_;
179 | octomap::OcTreeKey m_updateBBXMin;
180 | octomap::OcTreeKey m_updateBBXMax;
181 | octomap::KeyRay m_keyRay; // temp storage for ray casting
182 | double mapping_max_range_, min_z_pc_, max_z_pc_;
183 |
184 | // grid map
185 | grid_map::GridMap grid_map_;
186 |
187 | grid_map_msgs::msg::GridMap grid_map_msg_;
188 |
189 | // social relevance validity checking constants
190 | double robot_distance_view_max_, robot_distance_view_min_, robot_velocity_threshold_, robot_angle_view_, actual_fov_distance_;
191 |
192 | // Basic social personal space parameters defined
193 | double social_comfort_amplitude_ = 4;
194 | double sigma_x = 0.45;
195 | double sigma_y = 0.45;
196 | double fv = 0.8;
197 | double fFront = 0.2;
198 | double fFieldOfView = 0.0;
199 |
200 | // Flags
201 | tf2::Vector3 prev_map_to_fixed_pos_;
202 | bool initialized_;
203 | bool nav_sts_available_;
204 | bool visualize_free_space_;
205 | bool social_relevance_validity_checking_;
206 |
207 | bool apply_filter_;
208 | bool add_max_ranges_;
209 | bool add_rays_;
210 |
211 | double orientation_drift_, prev_map_to_fixed_yaw_, position_drift_;
212 |
213 | // LaserScan => (x,y,z)
214 | laser_geometry::LaserProjection laser_scan_projector_;
215 |
216 | protected:
217 | inline static void updateMinKey(const octomap::OcTreeKey &in,
218 | octomap::OcTreeKey &min)
219 | {
220 | for (unsigned i = 0; i < 3; ++i)
221 | min[i] = std::min(in[i], min[i]);
222 | };
223 | inline static void updateMaxKey(const octomap::OcTreeKey &in,
224 | octomap::OcTreeKey &max)
225 | {
226 | for (unsigned i = 0; i < 3; ++i)
227 | max[i] = std::max(in[i], max[i]);
228 | };
229 | };
230 |
231 | #endif // WORLD_MODELER_HPP
232 |
--------------------------------------------------------------------------------
/esc_move_base_planning/include/new_state_sampler.h:
--------------------------------------------------------------------------------
1 | /*********************************************************************
2 | * Software License Agreement (BSD License)
3 | *
4 | * Copyright (c) 2014, Rice University
5 | * All rights reserved.
6 | *
7 | * Redistribution and use in source and binary forms, with or without
8 | * modification, are permitted provided that the following conditions
9 | * are met:
10 | *
11 | * * Redistributions of source code must retain the above copyright
12 | * notice, this list of conditions and the following disclaimer.
13 | * * Redistributions in binary form must reproduce the above
14 | * copyright notice, this list of conditions and the following
15 | * disclaimer in the documentation and/or other materials provided
16 | * with the distribution.
17 | * * Neither the name of the Rice University nor the names of its
18 | * contributors may be used to endorse or promote products derived
19 | * from this software without specific prior written permission.
20 | *
21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 | * POSSIBILITY OF SUCH DAMAGE.
33 | *
34 | * Based on Juan D. Hernandez Vega's PhD thesis, University of Girona
35 | * http://hdl.handle.net/10803/457592, http://www.tdx.cat/handle/10803/457592
36 | *********************************************************************/
37 |
38 | /* Author: Javier V. Gómez */
39 |
40 | #ifndef OMPL_BASE_SAMPLERS_NEW_STATE_SAMPLER_
41 | #define OMPL_BASE_SAMPLERS_NEW_STATE_SAMPLER_
42 |
43 | #include
44 | #include
45 |
46 | #include
47 | #include
48 | #include
49 | #include
50 | #include
51 | #include
52 | #include
53 |
54 | // Eigen
55 | #include
56 |
57 | // OMPL namespaces
58 | namespace ob = ompl::base;
59 | namespace oc = ompl::control;
60 | namespace og = ompl::geometric;
61 |
62 | ob::StateSamplerPtr newAllocStateSampler(const ob::StateSpace *space, const ob::PlannerPtr &planner,
63 | const std::vector &start_states);
64 |
65 | /** \brief Extended state sampler to use with the CForest planning algorithm. It wraps the user-specified
66 | state sampler.*/
67 | class NewStateSampler : public ob::StateSampler
68 | {
69 | public:
70 | /** \brief Constructor */
71 | NewStateSampler(const ob::StateSpace *space, ob::StateSamplerPtr sampler)
72 | : ob::StateSampler(space), sampler_(sampler)
73 | {
74 | }
75 |
76 | /** \brief Destructor */
77 | ~NewStateSampler()
78 | {
79 | clear();
80 | }
81 |
82 | /** \brief It will sample the next state of the vector StatesToSample_. If this is empty,
83 | it will call the sampleUniform() method of the specified sampler. */
84 | virtual void sampleUniform(ob::State *state);
85 |
86 | /** \brief It will sample the next state of the vector StatesToSample_. If this is empty,
87 | it will call the sampleUniformNear() method of the specified sampler. */
88 | virtual void sampleUniformNear(ob::State *state, const ob::State *near, const double distance);
89 |
90 | /** \brief It will sample the next state of the vector StatesToSample_. If this is empty,
91 | it will call the sampleGaussian() method of the specified sampler. */
92 | virtual void sampleGaussian(ob::State *state, const ob::State *mean, const double stdDev);
93 |
94 | const ob::StateSpace *getStateSpace() const
95 | {
96 | return space_;
97 | }
98 |
99 | /** \brief Fills the vector StatesToSample_ of states to be sampled in the next
100 | calls to sampleUniform(), sampleUniformNear() or sampleGaussian(). */
101 | void setStatesToSample(const std::vector &states);
102 |
103 | void setPlanner(const ob::PlannerPtr &planner);
104 |
105 | void clear();
106 |
107 | protected:
108 | /** \brief Extracts the next sample when statesToSample_ is not empty. */
109 | void getNextSample(ob::State *state);
110 |
111 | /** \brief States to be sampled */
112 | std::vector statesToSample_;
113 |
114 | /** \brief Underlying, user-specified state sampler. */
115 | ob::StateSamplerPtr sampler_;
116 |
117 | ob::PlannerPtr planner_;
118 |
119 | /** \brief Lock to control the access to the statesToSample_ vector. */
120 | // boost::mutex statesLock_;
121 | bool reusing_;
122 | };
123 |
124 | //------
125 |
126 | oc::ControlSamplerPtr newAllocControlSampler(const oc::ControlSpace *cspace,
127 | const std::vector &start_controls);
128 |
129 | /** \brief Extended state sampler to use with the CForest planning algorithm. It wraps the user-specified
130 | state sampler.*/
131 | class NewControlSampler : public oc::ControlSampler
132 | {
133 | public:
134 | /** \brief Constructor */
135 | NewControlSampler(const oc::ControlSpace *cspace, oc::ControlSamplerPtr control_sampler)
136 | : oc::ControlSampler(cspace), csampler_(control_sampler)
137 | {
138 | }
139 |
140 | /** \brief Destructor */
141 | ~NewControlSampler()
142 | {
143 | clear();
144 | }
145 |
146 | /** \brief Sample a control. All other control sampling functions default to this one,
147 | unless a user-specified implementation is given. */
148 | virtual void sample(oc::Control *control);
149 |
150 | /** \brief Sample a control, given it is applied to a specific state (state). The default
151 | implementation calls the previous definition of sample(). Providing a different
152 | implementation of this function is useful if, for example, the sampling of controls
153 | depends on the state of the system. When attempting to sample controls that keep a
154 | system stable, for example, knowing the state at which the control is applied is important. */
155 | virtual void sample(oc::Control *control, const ob::State *state);
156 |
157 | /** \brief Sample a control, given the previously applied control. The default implementation
158 | calls the first definition of sample(). For some systems it is possible that large changes
159 | in controls are not desirable. For example, switching from maximum acceleration to maximum
160 | deceleration is not desirable when driving a car. */
161 | virtual void sampleNext(oc::Control *control, const oc::Control *previous);
162 |
163 | /** \brief Sample a control, given the previously applied control and that it is applied to a
164 | specific state. The default implementation calls the first definition of sample(), even if
165 | other implementations of the sampleNext() shown above are provided. Often this function needs
166 | to be overridden as it is the function planners typically call. */
167 | virtual void sampleNext(oc::Control *control, const oc::Control *previous, const ob::State *state);
168 |
169 | /** \brief Fills the vector ControlsToSample_ of controls to be sampled in the next
170 | calls to sample(), sampleNext() or sampleGaussian(). */
171 | void setControlsToSample(const std::vector &controls);
172 |
173 | void clear();
174 |
175 | protected:
176 | /** \brief Extracts the next sample when controlsToSample_ is not empty. */
177 | void getNextControl(oc::Control *control);
178 |
179 | /** \brief Controls to be sampled */
180 | std::vector controlsToSample_;
181 |
182 | /** \brief Underlying, user-specified control sampler. */
183 | oc::ControlSamplerPtr csampler_;
184 |
185 | /** \brief Lock to control the access to the statesToSample_ vector. */
186 | // boost::mutex statesLock_;
187 | };
188 |
189 | //------
190 |
191 | oc::DirectedControlSamplerPtr newAllocDirectedControlSampler(const oc::SpaceInformation *si);
192 |
193 | class NewDirectedControlSampler : public oc::DirectedControlSampler
194 | {
195 | public:
196 | NewDirectedControlSampler(const oc::SpaceInformation *si);
197 |
198 | virtual ~NewDirectedControlSampler();
199 |
200 | unsigned int getNumControlSamples() const
201 | {
202 | return numControlSamples_;
203 | }
204 |
205 | void setNumControlSamples(unsigned int numSamples)
206 | {
207 | numControlSamples_ = numSamples;
208 | }
209 |
210 | virtual unsigned int sampleTo(oc::Control *control, const ob::State *source, ob::State *dest);
211 |
212 | virtual unsigned int sampleTo(oc::Control *control, const oc::Control *previous, const ob::State *source,
213 | ob::State *dest);
214 |
215 | protected:
216 | virtual unsigned int getBestControl(oc::Control *control, const ob::State *source, ob::State *dest,
217 | const oc::Control *previous);
218 | unsigned int propagateWhileValid(const ob::State *state, const ob::State *dest, int steps,
219 | ob::State *result) const;
220 |
221 | oc::ControlSamplerPtr cs_;
222 |
223 | unsigned int numControlSamples_;
224 | };
225 |
226 | void DynUnicycle(const ob::State *start, const ob::State *desired, const double duration, ob::State *result);
227 |
228 | #endif
229 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Extended Social Comfort Robot Navigation Framework
2 |
3 | This is an online social robot navigation framework for indoor social scenarios. From this work a paper conference was submitted [Towards Online Socially Acceptable Robot Navigation](https://ieeexplore.ieee.org/document/9926686). Check the [Cite Our Paper](#cite-our-paper) in case you use this framework for your research or work.
4 |
5 | It is composed of four different packages:
6 |
7 | - `esc_move_base_control`: it is a simple differential control method.
8 | - `esc_move_base_mapping`: is in charge of the perception of the space.
9 | - `esc_move_base_msgs`: contains the messages needed for the framework and the start-goal queries.
10 | - `esc_move_base_planning`: responsible for finding solution paths for the navigation query requested.
11 |
12 | To understand more about how the framework works, you are highly encouraged to take a look at the paper.
13 |
14 | To run the framework, there is a launch file example in every package with example configurations.
15 |
16 | 
17 |
18 | - [Extended Social Comfort Robot Navigation Framework](#extended-social-comfort-robot-navigation-framework)
19 | - [World Modeling (`esc_move_base_mapping`)](#world-modeling-esc_move_base_mapping)
20 | - [Parameters](#parameters)
21 | - [Subscribers](#subscribers)
22 | - [Publishers](#publishers)
23 | - [Services](#services)
24 | - [Online Social Robot Path Planning (`esc_move_base_planning`)](#online-social-robot-path-planning-esc_move_base_planning)
25 | - [Parameters](#parameters-1)
26 | - [Subscribers](#subscribers-1)
27 | - [Publishers](#publishers-1)
28 | - [Actions](#actions)
29 | - [Path Following Control (`esc_move_base_control`)](#path-following-control-esc_move_base_control)
30 | - [Parameters](#parameters-2)
31 | - [Subscribers](#subscribers-2)
32 | - [Publishers](#publishers-2)
33 | - [Cite Our Paper](#cite-our-paper)
34 |
35 | ## World Modeling (`esc_move_base_mapping`)
36 |
37 | This package is in charge of the perception for the robot. For that, a depth camera is used, which helps to generate a 3D map of the space by using Octomap.
38 |
39 | This 3D map is combined in a multilayer GridMap with the position of the social agents detected in the space.
40 |
41 | ### Parameters
42 |
43 | - resolution (double, default: 1.0)
44 |
45 | Octomap and Grid Map resolution.
46 |
47 | - map_frame (string, default: "map")
48 |
49 | - fixed_frame (string, default: "fixed_frame")
50 |
51 | Fixed frame considered for the mapping, can be `odom` for example.
52 |
53 | - robot_frame (string, default: "/robot_frame")
54 |
55 | Base frame considered from the robot for the mapping.
56 |
57 | - oflline_octomap_path (string, default: "")
58 |
59 | If defined, then the given map is used and no ther mapping is done.
60 |
61 | - visualize_free_space (bool, default: True)
62 |
63 | Wether you would like to see the 3D map and grid_map. If `False`, nothing is published at `/esc_move_base_mapping/social_grid_map` and `/esc_move_base_mapping/octomap_map`.
64 |
65 | - mapping_max_range (double, default: 5.0)
66 |
67 | Maximum distance to generate the 3D map from the obtained depth points.
68 |
69 | - odometry_topic (string, default: "/odometry_topic")
70 |
71 | - rviz_timer (double, default: 0.0)
72 |
73 | The time delay to publish messages to RViz. Not used if `visualize_free_space` is `False`.
74 |
75 | - point_cloud_topics (list: string, default: empty)
76 |
77 | List of pointcloud topics used to generate the 3D map.
78 |
79 | - point_cloud_frames (list: string, default: empty)
80 |
81 | List of frames from pointcloud topics in order respectively.
82 |
83 | - social_agents_topic (string, default: "/pedsim_simulator/simulated_agents")
84 |
85 | Topic with social agents states.
86 |
87 | - social_agent_radius (double, default: 0.4)
88 |
89 | Radius considered for the social agents.
90 |
91 | - social_relevance_validity_checking (bool, default: False)
92 |
93 | Wether or not it is desired to only consider the relevant agents.
94 |
95 | - robot_distance_view_max (double, default: 6.0)
96 |
97 | Maximum distance for the robot to consider social agents.
98 |
99 | - robot_distance_view_min (double, default: 1.5)
100 |
101 | Mininum distance for the robot to consider social agents.
102 |
103 | - robot_angle_view (double, default: 1.57)
104 |
105 | Maximum angle in which the social agents are considered from the field of view of the robot. Defined in radians.
106 |
107 | - robot_velocity_threshold (double, default: 0.3)
108 |
109 | Maximum velocity that the robot can have.
110 |
111 | ### Subscribers
112 |
113 | The name of the subscribers' topics are just defined as an example, but they may be configured using the parameters defined before.
114 |
115 | - /pedsim_simulator/simulated_agents ([pedsim_msgs/AgentStates](https://github.com/CardiffUniversityComputationalRobotics/pedsim_ros/blob/noetic-devel/pedsim_msgs/msg/AgentStates.msg))
116 |
117 | Position, orientation, velocity and other states of the social agents.
118 |
119 | - /pepper/camera/depth/points ([sensor_msgs/PointCloud2](http://docs.ros.org/en/melodic/api/sensor_msgs/html/msg/PointCloud2.html))
120 |
121 | Depth points from depth camera.
122 |
123 | - /pepper/odom_groundtruth ([nav_msgs/Odometry](http://docs.ros.org/en/noetic/api/nav_msgs/html/msg/Odometry.html))
124 |
125 | Robot odometry (in this case it was Pepper robot).
126 |
127 | ### Publishers
128 |
129 | The name of the publishers' topics are just defined as an example, but they may be configured using the parameters defined before.
130 |
131 | - /esc_move_base_mapping/octomap_map ([visualization_msgs/MarkerArray](http://docs.ros.org/en/noetic/api/visualization_msgs/html/msg/MarkerArray.html))
132 |
133 | Visual representation of the 3D map created.
134 |
135 | - /esc_move_base_mapping/relevant_agents ([pedsim_msgs/AgentStates](https://github.com/CardiffUniversityComputationalRobotics/pedsim_ros/blob/noetic-devel/pedsim_msgs/msg/AgentStates.msg))
136 |
137 | If parameter `social_relevance_validity_checking` is `true`, then only the filtered relevant agents are published. Otherwise, all agents are re-published.
138 |
139 | - /esc_move_base_mapping/social_grid_map ([grid_map_msgs/GridMap](http://docs.ros.org/en/kinetic/api/grid_map_msgs/html/msg/GridMap.html))
140 |
141 | Grid map with 2 layers, one for obstacles and another for social agents.
142 |
143 | ### Services
144 |
145 | - /esc_move_base_mapping/get_grid_map ([grid_map_msgs/GetGridMap](http://docs.ros.org/en/indigo/api/grid_map_msgs/html/srv/GetGridMap.html))
146 |
147 | Fetches the grid map from the World Modeling.
148 |
149 | ## Online Social Robot Path Planning (`esc_move_base_planning`)
150 |
151 | This package is in charge of finding socially acceptable solution paths for the robot to follow.
152 |
153 | ### Parameters
154 |
155 | All of the following parameters have to be defined since they have no default values.
156 |
157 | - world_frame (string)
158 |
159 | Main parent frame to be used as reference.
160 |
161 | - planner_name (string)
162 |
163 | Planner to be used, can be RRT, RRTstar or PRMstar.
164 |
165 | - planning_bounds_x (list: [x_min, x_max])
166 |
167 | Limits for planning in the X axis.
168 |
169 | - planning_bounds_y (list: [y_min, y_max])
170 |
171 | Limits for planning in the Y axis.
172 |
173 | - dynamic_bounds (bool)
174 |
175 | Whether it is wanted to change the planning bounds dinamically depending on the navigation query.
176 |
177 | - start_state (list, [X, Y, Yaw])
178 |
179 | Define start state for navigation query if the action is not going to be used.
180 |
181 | - goal_state (list, [X, Y, Yaw])
182 |
183 | Define goal state for navigation query if no action is used.
184 |
185 | - timer_period (double)
186 |
187 | Controls the time for sleeps in the node.
188 |
189 | - solving_time (double)
190 |
191 | Time given to the planner to solve the navigation query.
192 |
193 | - opport_collision_check (bool)
194 |
195 | Whether it is wanted to use opportunistic collision checking or not.
196 |
197 | - reuse_last_best_solution (bool)
198 |
199 | Whether it is wanted to apply the reuse of the last best known solution.
200 |
201 | - optimization_objective (string)
202 |
203 | Defines which optimization objective to use is desired. Currently, only supported PathLength and ExtendedSocialComfort.
204 |
205 | - motion_cost_interpolation (bool)
206 |
207 | If motion cost interpolation is desired.
208 |
209 | - xy_goal_tolerance (double)
210 |
211 | Tolerance in the XY axis for the goal state.
212 |
213 | - visualize_tree: (bool)
214 |
215 | Whether it is desired to visualize or not the tree generated by the planner.
216 |
217 | - grid_map_service (string)
218 |
219 | Service name to get the GridMap.
220 |
221 | - control_active_topic (string)
222 |
223 | Topic to communicate with the control module to know when control is ready.
224 |
225 | - robot_base_radius (double)
226 |
227 | Radius of the base of the robot to be considered for the planning.
228 |
229 | - odometry_topic (string)
230 |
231 | - query_goal_topic (string)
232 |
233 | Topic to request a query without using actions.
234 |
235 | - goto_action (string)
236 |
237 | Action name to run a navigation query.
238 |
239 | ### Subscribers
240 |
241 | The name of the subscribers' topics are just defined as an example, but they may be configured using the parameters defined before.
242 |
243 | - /esc_move_base_planner/query_goal ([geometry_msgs/PoseStamped](http://docs.ros.org/en/noetic/api/geometry_msgs/html/msg/PoseStamped.html))
244 | - /pepper/odom_groundtruth ([nav_msgs/Odometry](http://docs.ros.org/en/noetic/api/nav_msgs/html/msg/Odometry.html))
245 |
246 | Robot odometry.
247 |
248 | - /esc_move_base_mapping/relevant_agents ([pedsim_msgs/AgentStates](https://github.com/CardiffUniversityComputationalRobotics/pedsim_ros/blob/noetic-devel/pedsim_msgs/msg/AgentStates.msg))
249 |
250 | Agents considered by the mapping module.
251 |
252 | - /control_active_topic ([std_msgs/Bool](http://docs.ros.org/en/noetic/api/std_msgs/html/msg/Bool.html))
253 |
254 | ### Publishers
255 |
256 | The name of the publishers' topics are just defined as an example, but they may be configured using the parameters defined before.
257 |
258 | - /esc_move_base_planner/esc_move_base_solution_path ([esc_move_base_msgs/Path2D](https://github.com/CardiffUniversityComputationalRobotics/esc-nav-stack/blob/world_modeling/esc_move_base_msgs/msg/Path2D.msg))
259 |
260 | Solution path found by the planner. It is passed to the control module.
261 |
262 | - /esc_move_base_planner/esc_num_nodes ([std_msgs/Int32](http://docs.ros.org/en/melodic/api/std_msgs/html/msg/Int32.html))
263 |
264 | Number of valid nodes sampled by the planner.
265 |
266 | - /esc_move_base_planner/query_goal_pose_rviz ([geometry_msgs/PoseStamped](http://docs.ros.org/en/noetic/api/geometry_msgs/html/msg/PoseStamped.html))
267 |
268 | Shows in RViz the query pose requested to the planner.
269 |
270 | - /esc_move_base_planner/query_goal_radius_rviz ([visualization_msgs/Marker](http://docs.ros.org/en/noetic/api/visualization_msgs/html/msg/Marker.html))
271 |
272 | Shows in RViz the radius of the query requested to the planner.
273 |
274 | - /esc_move_base_planner/solution_path ([visualization_msgs/Marker](http://docs.ros.org/en/noetic/api/visualization_msgs/html/msg/Marker.html))
275 |
276 | Visual solution path to be seen in RViz.
277 |
278 | ### Actions
279 |
280 | - `/goto_action` ([esc_move_base_msgs/GoTo2D](https://github.com/CardiffUniversityComputationalRobotics/esc-nav-stack/blob/world_modeling/esc_move_base_msgs/action/Goto2D.action))
281 |
282 | Action to request navigation query. The name of the action server depends on the parameter `goto_action`.
283 |
284 | ## Path Following Control (`esc_move_base_control`)
285 |
286 | This module is in charge of sending the velocities to the robot according to the path obtained so that the robot goes through that path.
287 |
288 | ### Parameters
289 |
290 | - max_vel (double, default: 0.1)
291 |
292 | Maximum velocity the robot can have in the X axis.
293 |
294 | - min_vel (double, default: 0.05)
295 |
296 | Minimum velocity for the robot to move in the X axis.
297 |
298 | - max_turn_rate (double, default: 0.5)
299 |
300 | Maximum velocity with which the robot can rotate.
301 |
302 | - min_turn_rate (double, default: 0.5)
303 |
304 | Minimum velocity for the robot to rotate.
305 |
306 | - controller_hz (double, default: 100)
307 |
308 | Frequency in which velocities are sent
309 |
310 | - control_path_topic (string, default: "/control_path_topic")
311 |
312 | Topic in which the desired path to follow is published.
313 |
314 | - control_output_topic (double, default: "/control_output_topic")
315 |
316 | Topic in which the velocities are published.
317 |
318 | - odometry_topic (string, default: "/odometry_topic")
319 |
320 | ### Subscribers
321 |
322 | The name of the subscribers' topics are just defined as an example, but they may be configured using the parameters defined before.
323 |
324 | - /esc_move_base_planner/esc_move_base_solution_path ([esc_move_base_msgs/Path2D](https://github.com/CardiffUniversityComputationalRobotics/esc-nav-stack/blob/world_modeling/esc_move_base_msgs/msg/Path2D.msg))
325 | - /pepper/odom_groundtruth ([nav_msgs/Odometry](http://docs.ros.org/en/noetic/api/nav_msgs/html/msg/Odometry.html))
326 |
327 | ### Publishers
328 |
329 | The name of the publishers' topics are just defined as an example, but they may be configured using the parameters defined before.
330 |
331 | - /control_active_topic ([std_msgs/Bool](http://docs.ros.org/en/noetic/api/std_msgs/html/msg/Bool.html))
332 | - /pepper/cmd_vel ([geometry_msgs/Twist](http://docs.ros.org/en/noetic/api/geometry_msgs/html/msg/Twist.html))
333 |
334 | ## Cite Our Paper
335 |
336 | If this navigation framework is used for research or any other work, please make sure to cite our paper with the following bibtex:
337 |
338 | ```
339 | @inproceedings{Silva2022,
340 | author={Silva, Steven and Paillacho, Dennys and Verdezoto, Nervo and Hern{\'{a}}ndez, Juan David},
341 | booktitle={2022 IEEE 18th International Conference on Automation Science and Engineering (CASE)},
342 | title={Towards Online Socially Acceptable Robot Navigation},
343 | year={2022},
344 | pages={707-714},
345 | doi={10.1109/CASE49997.2022.9926686},
346 | url = {10.1109/CASE49997.2022.9926686},
347 | isbn = {9781665490429},
348 | }
349 | ```
350 |
--------------------------------------------------------------------------------
/esc_move_base_control/esc_move_base_control/base_controller.py:
--------------------------------------------------------------------------------
1 | """
2 | Created on November 1, 2018
3 |
4 | @author: juandhv (Juan David Hernandez Vega, juandhv@rice.edu)
5 |
6 | Purpose: Alternative esc base controller
7 | """
8 |
9 | import math
10 | import numpy as np
11 |
12 | import rclpy
13 | from rclpy.node import Node
14 |
15 | # ROS messages
16 | from geometry_msgs.msg import Twist
17 | from std_msgs.msg import Bool
18 | from nav_msgs.msg import Odometry
19 | from esc_move_base_msgs.msg import Path2D
20 | from tf_transformations import euler_from_quaternion
21 |
22 |
23 | class Controller(Node):
24 | """
25 | Controller class
26 | """
27 |
28 | def __init__(self):
29 | """
30 | Constructor
31 | """
32 | super().__init__("esc_move_base_control")
33 |
34 | # =======================================================================
35 | # Initial values
36 | # =======================================================================
37 | self.current_position_ = np.zeros(2)
38 | self.current_orientation_ = 0.0
39 |
40 | self.desired_position_ = np.zeros(2)
41 | self.desired_orientation_ = 0.0
42 |
43 | self.solution_path_wps_ = []
44 |
45 | # =======================================================================
46 | # Path trimmer variables
47 | # =======================================================================
48 | self.yaw = 0
49 |
50 | # =======================================================================
51 | # Get parameters
52 | # =======================================================================
53 | self.declare_parameter("max_vel", 0.1)
54 | self.declare_parameter("min_vel", 0.05)
55 | self.declare_parameter("max_turn_rate", 0.5)
56 | self.declare_parameter("min_turn_rate", 0.5)
57 | self.declare_parameter("drift_turning_vel", 0.0)
58 | self.declare_parameter("controller_hz", 100.0)
59 | self.declare_parameter("odometry_topic", "odometry_topic")
60 | self.declare_parameter("control_path_topic", "control_path_topic")
61 | self.declare_parameter("control_output_topic", "control_output_topic")
62 | self.declare_parameter("control_active_topic", "control_active_topic")
63 | self.declare_parameter("xy_goal_tolerance", 0.2)
64 | self.declare_parameter("yaw_goal_tolerance", 0.2)
65 |
66 | self.max_vel_ = self.get_parameter("max_vel").get_parameter_value().double_value
67 | self.min_vel_ = self.get_parameter("min_vel").get_parameter_value().double_value
68 | self.max_turn_rate_ = (
69 | self.get_parameter("max_turn_rate").get_parameter_value().double_value
70 | )
71 | self.min_turn_rate_ = (
72 | self.get_parameter("min_turn_rate").get_parameter_value().double_value
73 | )
74 | self.drift_turning_vel_ = (
75 | self.get_parameter("drift_turning_vel").get_parameter_value().double_value
76 | )
77 | self.controller_hz_ = (
78 | self.get_parameter("controller_hz").get_parameter_value().double_value
79 | )
80 | self.odometry_topic_ = (
81 | self.get_parameter("odometry_topic").get_parameter_value().string_value
82 | )
83 | self.control_path_topic_ = (
84 | self.get_parameter("control_path_topic").get_parameter_value().string_value
85 | )
86 | self.control_output_topic_ = (
87 | self.get_parameter("control_output_topic")
88 | .get_parameter_value()
89 | .string_value
90 | )
91 | self.control_active_topic_ = (
92 | self.get_parameter("control_active_topic")
93 | .get_parameter_value()
94 | .string_value
95 | )
96 | self.xy_goal_tolerance_ = (
97 | self.get_parameter("xy_goal_tolerance").get_parameter_value().double_value
98 | )
99 | self.yaw_goal_tolerance_ = (
100 | self.get_parameter("yaw_goal_tolerance").get_parameter_value().double_value
101 | )
102 |
103 | # =======================================================================
104 | # Subscribers
105 | # =======================================================================
106 | # Navigation data (feedback)
107 | self.odometry_sub_ = self.create_subscription(
108 | Odometry, self.odometry_topic_, self.odomCallback, 10
109 | )
110 | self.control_path_sub_ = self.create_subscription(
111 | Path2D, self.control_path_topic_, self.receiveControlPathCallback, 10
112 | )
113 |
114 | # =======================================================================
115 | # Publishers
116 | # =======================================================================
117 | self.control_output_pub_ = self.create_publisher(
118 | Twist, self.control_output_topic_, 10
119 | )
120 | self.control_active_pub_ = self.create_publisher(
121 | Bool, self.control_active_topic_, 10
122 | )
123 | self.controller_state = 0
124 |
125 | self.timer = self.create_timer(1 / self.controller_hz_, self.controlBaseEsc)
126 |
127 | def odomCallback(self, odometry_msg):
128 | """
129 | Callback to robot's odometry data
130 | """
131 | self.current_position_[0] = odometry_msg.pose.pose.position.x
132 | self.current_position_[1] = odometry_msg.pose.pose.position.y
133 | (r, p, self.yaw) = euler_from_quaternion(
134 | [
135 | odometry_msg.pose.pose.orientation.x,
136 | odometry_msg.pose.pose.orientation.y,
137 | odometry_msg.pose.pose.orientation.z,
138 | odometry_msg.pose.pose.orientation.w,
139 | ]
140 | )
141 | self.current_orientation_ = wrapAngle(self.yaw)
142 | return
143 |
144 | def receiveControlPathCallback(self, path_2d_msg):
145 | """Callback to receive path (list of waypoints)"""
146 | self.solution_path_wps_ = []
147 |
148 | waypoint_distances = np.array([])
149 | min_list_index = []
150 | actual_next_waypoint_index = None
151 |
152 | waypoints_list = path_2d_msg.waypoints
153 | # print(waypoints_list)
154 | # print("length:", len(waypoints_list))
155 |
156 | if len(waypoints_list) <= 3:
157 | self.solution_path_wps_.append(
158 | [waypoints_list[-1].x, waypoints_list[-1].y, waypoints_list[-1].theta]
159 | )
160 | return
161 |
162 | for waypoint in waypoints_list:
163 | waypoint_distances = np.append(
164 | waypoint_distances,
165 | [
166 | euclidian_distance(
167 | self.current_position_[0],
168 | self.current_position_[1],
169 | waypoint.x,
170 | waypoint.y,
171 | )
172 | ],
173 | )
174 | # print(len(waypoint_distances))
175 | for i in range(0, 3):
176 | min_list_index.append(
177 | np.where(waypoint_distances == np.amin(waypoint_distances))[0][0]
178 | )
179 | waypoint_distances[min_list_index[i]] = float("inf")
180 | # print(min_list_index)
181 | current_waypoint_angle = float("inf")
182 | for i in min_list_index:
183 | new_waypoint_angle = self.robotAngleToPoint(
184 | path_2d_msg.waypoints[i].x, path_2d_msg.waypoints[i].y
185 | )
186 | if new_waypoint_angle < current_waypoint_angle:
187 | actual_next_waypoint_index = i
188 | current_waypoint_angle = new_waypoint_angle
189 |
190 | if actual_next_waypoint_index is not None:
191 | waypoints_list = waypoints_list[actual_next_waypoint_index + 1 :]
192 |
193 | for i in range(1, len(waypoints_list)):
194 | waypoint = waypoints_list[i]
195 | distance_to_wp = math.sqrt(
196 | math.pow(waypoint.x - self.current_position_[0], 2.0)
197 | + math.pow(waypoint.y - self.current_position_[1], 2.0)
198 | )
199 | if distance_to_wp > self.xy_goal_tolerance_ or i == (
200 | len(waypoints_list) - 1
201 | ):
202 | self.solution_path_wps_.append([waypoint.x, waypoint.y, waypoint.theta])
203 | return
204 |
205 | def robotAngleToPoint(self, x, y):
206 |
207 | tetha_robot_waypoint = np.arctan2(
208 | y - self.current_position_[1], x - self.current_position_[0]
209 | )
210 |
211 | if tetha_robot_waypoint < 0:
212 | tetha_robot_waypoint = 2 * math.pi + tetha_robot_waypoint
213 |
214 | robotAngle = self.yaw
215 |
216 | if robotAngle < 0:
217 | robotAngle = 2 * math.pi + robotAngle
218 |
219 | if tetha_robot_waypoint > (robotAngle + math.pi):
220 | tetha_robot_waypoint = abs(robotAngle + 2 * math.pi - tetha_robot_waypoint)
221 | elif robotAngle > (tetha_robot_waypoint + math.pi):
222 | tetha_robot_waypoint = abs(tetha_robot_waypoint + 2 * math.pi - robotAngle)
223 | else:
224 | tetha_robot_waypoint = abs(tetha_robot_waypoint - robotAngle)
225 |
226 | return tetha_robot_waypoint
227 |
228 | def controlBaseEsc(self):
229 | """Control loop"""
230 | # print(self.solution_path_wps_)
231 | if len(self.solution_path_wps_) > 0:
232 | # print(self.solution_path_wps_)
233 | self.desired_position_[0] = self.solution_path_wps_[0][0]
234 | self.desired_position_[1] = self.solution_path_wps_[0][1]
235 | inc_x = self.desired_position_[0] - self.current_position_[0]
236 | inc_y = self.desired_position_[1] - self.current_position_[1]
237 | distance_to_goal = math.sqrt(math.pow(inc_x, 2.0) + math.pow(inc_y, 2.0))
238 | control_input = Twist()
239 | control_input.angular.x = 0.0
240 | control_input.angular.y = 0.0
241 | control_input.angular.z = 0.0
242 | control_input.linear.x = 0.0
243 | control_input.linear.y = 0.0
244 | control_input.linear.z = 0.0
245 |
246 | if distance_to_goal >= 0.4:
247 | self.controller_state = 0
248 | self.desired_orientation_ = wrapAngle(math.atan2(inc_y, inc_x))
249 | yaw_error = wrapAngle(
250 | self.desired_orientation_ - self.current_orientation_
251 | )
252 |
253 | if abs(yaw_error) > 0.2:
254 | self.get_logger().debug(
255 | "orienting towards the next waypoint: " + str(yaw_error),
256 | )
257 | control_input.angular.z = yaw_error * self.max_turn_rate_
258 | else:
259 | self.get_logger().debug(
260 | "moving towards the next waypoint: " + str(yaw_error)
261 | )
262 | control_input.angular.z = yaw_error * self.max_turn_rate_
263 |
264 | liner_speed = abs(distance_to_goal) * 0.5
265 | if liner_speed < self.min_vel_:
266 | control_input.linear.x = self.min_vel_
267 | elif liner_speed > self.max_vel_:
268 | control_input.linear.x = self.max_vel_
269 | else:
270 | control_input.linear.x = liner_speed
271 |
272 | if control_input.angular.z < -self.max_turn_rate_:
273 | control_input.angular.z = -self.max_turn_rate_
274 | elif control_input.angular.z > self.max_turn_rate_:
275 | control_input.angular.z = self.max_turn_rate_
276 | self.control_output_pub_.publish(control_input)
277 |
278 | else:
279 | if len(self.solution_path_wps_) > 1:
280 | del self.solution_path_wps_[0]
281 | else:
282 | if (
283 | distance_to_goal >= self.xy_goal_tolerance_
284 | and self.controller_state != 2
285 | ):
286 | self.controller_state = 1
287 | self.desired_orientation_ = wrapAngle(math.atan2(inc_y, inc_x))
288 | yaw_error = wrapAngle(
289 | self.desired_orientation_ - self.current_orientation_
290 | )
291 | if abs(yaw_error) > 0.2 and abs(yaw_error) < 1.57:
292 | self.get_logger().debug(
293 | "final approach: orienting towards a waypoint (forward)"
294 | )
295 | if yaw_error > 0.0:
296 | control_input.angular.z = self.min_turn_rate_
297 | else:
298 | control_input.angular.z = -self.min_turn_rate_
299 | control_input.linear.x = self.drift_turning_vel_
300 | elif abs(yaw_error) > 1.57 and abs(yaw_error) < 2.94:
301 | self.get_logger().debug(
302 | "final approach: orienting towards a waypoint (backward)",
303 | )
304 | if yaw_error > 0.0:
305 | control_input.angular.z = -self.min_turn_rate_
306 | else:
307 | control_input.angular.z = self.min_turn_rate_
308 |
309 | control_input.linear.x = -self.drift_turning_vel_
310 | elif abs(yaw_error) < 0.2:
311 | self.get_logger().debug(
312 | "final approach: moving towards a waypoint (forward)"
313 | )
314 |
315 | control_input.linear.x = 0.05
316 | else:
317 | self.get_logger().debug(
318 | "final approach: moving towards a waypoint (backward)",
319 | )
320 |
321 | control_input.linear.x = -0.05
322 |
323 | self.control_output_pub_.publish(control_input)
324 | else:
325 | self.controller_state = 2
326 | self.get_logger().debug(
327 | "final approach, final orientation",
328 | )
329 | yaw_error = wrapAngle(
330 | self.solution_path_wps_[0][2] - self.current_orientation_
331 | )
332 |
333 | if abs(yaw_error) < self.yaw_goal_tolerance_:
334 | if len(self.solution_path_wps_) > 0:
335 | del self.solution_path_wps_[0]
336 | else:
337 | control_input.angular.z = yaw_error * self.max_turn_rate_
338 | if yaw_error < 0.0:
339 | control_input.linear.x = self.drift_turning_vel_
340 | control_input.angular.z = -self.min_turn_rate_ * 5
341 | else:
342 | control_input.linear.x = -self.drift_turning_vel_
343 | control_input.angular.z = self.min_turn_rate_ * 5
344 | self.control_output_pub_.publish(control_input)
345 |
346 | # self.get_logger().debug("%s: yaw_error: %f", self.get_name(), yaw_error)
347 | # self.get_logger().debug("%s: distance_to_goal: %f", self.get_name(), distance_to_goal)
348 | # self.get_logger().debug("%s: control_input.linear.x %f", self.get_name(), control_input.linear.x)
349 | # self.get_logger().debug("%s: control_input.angular.z %f\n", self.get_name(), control_input.angular.z)
350 | self.control_active_pub_.publish(Bool(data=True))
351 | else:
352 | control_input = Twist()
353 | self.control_output_pub_.publish(control_input)
354 | self.control_active_pub_.publish(Bool(data=False))
355 | return
356 |
357 |
358 | def euclidian_distance(x1, y1, x2, y2):
359 | return np.sqrt(np.power(x2 - x1, 2) + np.power(y2 - y1, 2))
360 |
361 |
362 | def wrapAngle(angle):
363 | """wrapAngle
364 | Calculates angles values between 0 and 2pi"""
365 | return angle + (2.0 * math.pi * math.floor((math.pi - angle) / (2.0 * math.pi)))
366 |
367 |
368 | def main(args=None):
369 | rclpy.init(args=args)
370 | controller_node = Controller()
371 | rclpy.spin(controller_node)
372 | controller_node.destroy_node()
373 | rclpy.shutdown()
374 |
375 |
376 | if __name__ == "__main__":
377 | main()
378 |
--------------------------------------------------------------------------------
/esc_move_base_planning/include/planner/RRTstarMod.h:
--------------------------------------------------------------------------------
1 | /*********************************************************************
2 | * Software License Agreement (BSD License)
3 | *
4 | * Copyright (c) 2011, Rice University
5 | * All rights reserved.
6 | *
7 | * Redistribution and use in source and binary forms, with or without
8 | * modification, are permitted provided that the following conditions
9 | * are met:
10 | *
11 | * * Redistributions of source code must retain the above copyright
12 | * notice, this list of conditions and the following disclaimer.
13 | * * Redistributions in binary form must reproduce the above
14 | * copyright notice, this list of conditions and the following
15 | * disclaimer in the documentation and/or other materials provided
16 | * with the distribution.
17 | * * Neither the name of the Rice University nor the names of its
18 | * contributors may be used to endorse or promote products derived
19 | * from this software without specific prior written permission.
20 | *
21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 | * POSSIBILITY OF SUCH DAMAGE.
33 | *********************************************************************/
34 |
35 | /* Authors: Alejandro Perez, Sertac Karaman, Ryan Luna, Luis G. Torres, Ioan Sucan, Javier V Gomez, Jonathan
36 | * Gammell */
37 |
38 | /* Modified by: Juan D. Hernandez */
39 |
40 | #ifndef OMPL_GEOMETRIC_PLANNERS_RRT_RRTSTAR_MOD_
41 | #define OMPL_GEOMETRIC_PLANNERS_RRT_RRTSTAR_MOD_
42 |
43 | #include "ompl/geometric/planners/PlannerIncludes.h"
44 | #include "ompl/base/OptimizationObjective.h"
45 | #include "ompl/datastructures/NearestNeighbors.h"
46 |
47 | #include
48 | #include
49 | #include
50 | #include
51 | #include
52 | #include
53 |
54 | namespace ompl
55 | {
56 | namespace geometric
57 | {
58 | /**
59 | @anchor gRRTstar
60 | @par Short description
61 | \ref gRRTstar "RRT*" (optimal RRT) is an asymptotically-optimal incremental
62 | sampling-based motion planning algorithm. \ref gRRTstar "RRT*" algorithm is
63 | guaranteed to converge to an optimal solution, while its
64 | running time is guaranteed to be a constant factor of the
65 | running time of the \ref gRRT "RRT". The notion of optimality is with
66 | respect to the distance function defined on the state space
67 | we are operating on. See ompl::base::Goal::setMaximumPathLength() for
68 | how to set the maximally allowed path length to reach the goal.
69 | If a solution path that is shorter than ompl::base::Goal::getMaximumPathLength() is
70 | found, the algorithm terminates before the elapsed time.
71 | @par External documentation
72 | S. Karaman and E. Frazzoli, Sampling-based
73 | Algorithms for Optimal Motion Planning, International Journal of Robotics
74 | Research, Vol 30, No 7, 2011.
75 | http://arxiv.org/abs/1105.1186
76 | */
77 |
78 | /** \brief Optimal Rapidly-exploring Random Trees */
79 | class RRTstarMod : public base::Planner
80 | {
81 | public:
82 | RRTstarMod(const base::SpaceInformationPtr &si);
83 |
84 | virtual ~RRTstarMod();
85 |
86 | virtual void getPlannerData(base::PlannerData &data) const;
87 |
88 | virtual base::PlannerStatus solve(const base::PlannerTerminationCondition &ptc);
89 |
90 | virtual void clear();
91 |
92 | virtual void setup();
93 |
94 | /** \brief Set the goal bias
95 |
96 | In the process of randomly selecting states in
97 | the state space to attempt to go towards, the
98 | algorithm may in fact choose the actual goal state, if
99 | it knows it, with some probability. This probability
100 | is a real number between 0.0 and 1.0; its value should
101 | usually be around 0.05 and should not be too large. It
102 | is probably a good idea to use the default value. */
103 | void setGoalBias(double goalBias)
104 | {
105 | goalBias_ = goalBias;
106 | }
107 |
108 | /** \brief Get the goal bias the planner is using */
109 | double getGoalBias() const
110 | {
111 | return goalBias_;
112 | }
113 |
114 | /** \brief Set the range the planner is supposed to use.
115 |
116 | This parameter greatly influences the runtime of the
117 | algorithm. It represents the maximum length of a
118 | motion to be added in the tree of motions. */
119 | void setRange(double distance)
120 | {
121 | maxDistance_ = distance;
122 | }
123 |
124 | /** \brief Get the range the planner is using */
125 | double getRange() const
126 | {
127 | return maxDistance_;
128 | }
129 |
130 | /** \brief Set the rewiring scale factor, s, such that r_rrg = s \times r_rrg* (or k_rrg = s
131 | * \times k_rrg*) */
132 | void setRewireFactor(double rewireFactor)
133 | {
134 | rewireFactor_ = rewireFactor;
135 | calculateRewiringLowerBounds();
136 | }
137 |
138 | /** \brief Set the rewiring scale factor, s, such that r_rrg = s \times r_rrg* > r_rrg* (or k_rrg
139 | * = s \times k_rrg* > k_rrg*) */
140 | double getRewireFactor() const
141 | {
142 | return rewireFactor_;
143 | }
144 |
145 | /** \brief Set a different nearest neighbors datastructure */
146 | template class NN>
147 | void setNearestNeighbors()
148 | {
149 | nn_.reset(new NN());
150 | }
151 |
152 | /** \brief Option that delays collision checking procedures.
153 | When it is enabled, all neighbors are sorted by cost. The
154 | planner then goes through this list, starting with the lowest
155 | cost, checking for collisions in order to find a parent. The planner
156 | stops iterating through the list when a collision free parent is found.
157 | This prevents the planner from collision checking each neighbor, reducing
158 | computation time in scenarios where collision checking procedures are expensive.*/
159 | void setDelayCC(bool delayCC)
160 | {
161 | delayCC_ = delayCC;
162 | }
163 |
164 | /** \brief Get the state of the delayed collision checking option */
165 | bool getDelayCC() const
166 | {
167 | return delayCC_;
168 | }
169 |
170 | /** \brief Controls whether the tree is pruned during the search. This pruning removes
171 | a vertex if and only if it \e and all its descendents passes the pruning condition.
172 | The pruning condition is whether the lower-bounding estimate of a solution
173 | constrained to pass the the \e vertex is greater than the current solution.
174 | Considering the descendents of a vertex prevents removing a descendent
175 | that may actually be capable of later providing a better solution once
176 | its incoming path passes through a different vertex (e.g., a change in homotopy class). */
177 | void setTreePruning(const bool prune);
178 |
179 | /** \brief Get the state of the pruning option. */
180 | bool getTreePruning() const
181 | {
182 | return useTreePruning_;
183 | }
184 |
185 | /** \brief Set the fractional change in solution cost necessary for pruning to occur, i.e.,
186 | prune if the new solution is at least X% better than the old solution.
187 | (e.g., 0.0 will prune after every new solution, while 1.0 will never prune.) */
188 | void setPruneThreshold(const double pp)
189 | {
190 | pruneThreshold_ = pp;
191 | }
192 |
193 | /** \brief Get the current prune states percentage threshold parameter. */
194 | double getPruneThreshold() const
195 | {
196 | return pruneThreshold_;
197 | }
198 |
199 | /** \brief Use the measure of the pruned subproblem instead of the measure of the entire problem
200 | domain (if such an expression exists and a solution is present).
201 | Currently the only method to calculate this measure in closed-form is through a informed sampler,
202 | so this option also requires that. */
203 | void setPrunedMeasure(bool informedMeasure);
204 |
205 | /** \brief Get the state of using the pruned measure */
206 | bool getPrunedMeasure() const
207 | {
208 | return usePrunedMeasure_;
209 | }
210 |
211 | /** \brief Use direct sampling of the heuristic for the generation of random samples (e.g.,
212 | x_rand).
213 | If a direct sampling method is not defined for the objective, rejection sampling will be used by
214 | default. */
215 | void setInformedSampling(bool informedSampling);
216 |
217 | /** \brief Get the state direct heuristic sampling */
218 | bool getInformedSampling() const
219 | {
220 | return useInformedSampling_;
221 | }
222 |
223 | /** \brief Controls whether heuristic rejection is used on samples (e.g., x_rand) */
224 | void setSampleRejection(const bool reject);
225 |
226 | /** \brief Get the state of the sample rejection option */
227 | bool getSampleRejection() const
228 | {
229 | return useRejectionSampling_;
230 | }
231 |
232 | /** \brief Controls whether heuristic rejection is used on new states before connection (e.g.,
233 | * x_new = steer(x_nearest, x_rand)) */
234 | void setNewStateRejection(const bool reject)
235 | {
236 | useNewStateRejection_ = reject;
237 | }
238 |
239 | /** \brief Get the state of the new-state rejection option */
240 | bool getNewStateRejection() const
241 | {
242 | return useNewStateRejection_;
243 | }
244 |
245 | /** \brief Controls whether pruning and new-state rejection uses an admissible cost-to-come
246 | * estimate or not */
247 | void setAdmissibleCostToCome(const bool admissible)
248 | {
249 | useAdmissibleCostToCome_ = admissible;
250 | }
251 |
252 | /** \brief Get the admissibility of the pruning and new-state rejection heuristic */
253 | bool getAdmissibleCostToCome() const
254 | {
255 | return useAdmissibleCostToCome_;
256 | }
257 |
258 | /** \brief A \e meta parameter to focusing the search to improving the current solution. This is
259 | the parameter set by CFOREST.
260 | For RRT*, search focusing consists of pruning the existing search and limiting future search.
261 | Specifically, this is accomplished by turning on informed sampling, tree pruning and new-state
262 | rejection.
263 | This flag individually sets the options described above.
264 | */
265 | void setFocusSearch(const bool focus)
266 | {
267 | setInformedSampling(focus);
268 | setTreePruning(focus);
269 | setPrunedMeasure(focus);
270 | setNewStateRejection(focus);
271 | }
272 |
273 | /** \brief Get the state of search focusing */
274 | bool getFocusSearch() const
275 | {
276 | return getInformedSampling() && getPrunedMeasure() && getTreePruning() &&
277 | getNewStateRejection();
278 | }
279 |
280 | /** \brief Use a k-nearest search for rewiring instead of a r-disc search. */
281 | void setKNearest(bool useKNearest)
282 | {
283 | useKNearest_ = useKNearest;
284 | }
285 |
286 | /** \brief Get the state of using a k-nearest search for rewiring. */
287 | bool getKNearest() const
288 | {
289 | return useKNearest_;
290 | }
291 |
292 | /** \brief Set the number of attempts to make while performing rejection or informed sampling */
293 | void setNumSamplingAttempts(unsigned int numAttempts)
294 | {
295 | numSampleAttempts_ = numAttempts;
296 | }
297 |
298 | /** \brief Get the number of attempts to make while performing rejection or informed sampling */
299 | unsigned int getNumSamplingAttempts() const
300 | {
301 | return numSampleAttempts_;
302 | }
303 |
304 | unsigned int numIterations() const
305 | {
306 | return iterations_;
307 | }
308 |
309 | ompl::base::Cost bestCost() const
310 | {
311 | return bestCost_;
312 | }
313 |
314 | protected:
315 | /** \brief Representation of a motion */
316 | class Motion
317 | {
318 | public:
319 | /** \brief Constructor that allocates memory for the state. This constructor automatically
320 | * allocates memory for \e state, \e cost, and \e incCost */
321 | Motion(const base::SpaceInformationPtr &si) : state(si->allocState()), parent(nullptr)
322 | {
323 | }
324 |
325 | ~Motion()
326 | {
327 | }
328 |
329 | /** \brief The state contained by the motion */
330 | base::State *state;
331 |
332 | /** \brief The parent motion in the exploration tree */
333 | Motion *parent;
334 |
335 | /** \brief The cost up to this motion */
336 | base::Cost cost;
337 |
338 | /** \brief The incremental cost of this motion's parent to this motion (this is stored to save
339 | * distance computations in the updateChildCosts() method) */
340 | base::Cost incCost;
341 |
342 | /** \brief The set of motions descending from the current motion */
343 | std::vector children;
344 | };
345 |
346 | /** \brief Create the samplers */
347 | void allocSampler();
348 |
349 | /** \brief Generate a sample */
350 | bool sampleUniform(base::State *statePtr);
351 |
352 | /** \brief Free the memory allocated by this planner */
353 | void freeMemory();
354 |
355 | // For sorting a list of costs and getting only their sorted indices
356 | struct CostIndexCompare
357 | {
358 | CostIndexCompare(const std::vector &costs, const base::OptimizationObjective &opt)
359 | : costs_(costs), opt_(opt)
360 | {
361 | }
362 | bool operator()(unsigned i, unsigned j)
363 | {
364 | return opt_.isCostBetterThan(costs_[i], costs_[j]);
365 | }
366 | const std::vector &costs_;
367 | const base::OptimizationObjective &opt_;
368 | };
369 |
370 | /** \brief Compute distance between motions (actually distance between contained states) */
371 | double distanceFunction(const Motion *a, const Motion *b) const
372 | {
373 | return si_->distance(a->state, b->state);
374 | }
375 |
376 | /** \brief Gets the neighbours of a given motion, using either k-nearest of radius as appropriate.
377 | */
378 | void getNeighbors(Motion *motion, std::vector &nbh) const;
379 |
380 | /** \brief Removes the given motion from the parent's child list */
381 | void removeFromParent(Motion *m);
382 |
383 | /** \brief Updates the cost of the children of this node if the cost up to this node has changed
384 | */
385 | void updateChildCosts(Motion *m);
386 |
387 | /** \brief Prunes all those states which estimated total cost is higher than pruneTreeCost.
388 | Returns the number of motions pruned. Depends on the parameter set by
389 | setPruneStatesImprovementThreshold() */
390 | int pruneTree(const base::Cost &pruneTreeCost);
391 |
392 | /** \brief Computes the solution cost heuristically as the cost to come from start to the motion
393 | plus
394 | the cost to go from the motion to the goal. If the parameter \e use_admissible_heuristic
395 | (\e setAdmissibleCostToCome()) is true, a heuristic estimate of the cost to come is used;
396 | otherwise, the current cost to come to the motion is used (which may overestimate the cost
397 | through the motion). */
398 | base::Cost solutionHeuristic(const Motion *motion) const;
399 |
400 | /** \brief Add the children of a vertex to the given list. */
401 | void addChildrenToList(std::queue> *motionList, Motion *motion);
402 |
403 | /** \brief Check whether the given motion passes the specified cost threshold, meaning it will be
404 | * \e kept during pruning */
405 | bool keepCondition(const Motion *motion, const base::Cost &threshold) const;
406 |
407 | /** \brief Calculate the k_RRG* and r_RRG* terms */
408 | void calculateRewiringLowerBounds();
409 |
410 | /** \brief State sampler */
411 | base::StateSamplerPtr sampler_;
412 |
413 | /** \brief An informed sampler */
414 | base::InformedSamplerPtr infSampler_;
415 |
416 | /** \brief A nearest-neighbors datastructure containing the tree of motions */
417 | std::shared_ptr> nn_;
418 |
419 | /** \brief The fraction of time the goal is picked as the state to expand towards (if such a state
420 | * is available) */
421 | double goalBias_;
422 |
423 | /** \brief The maximum length of a motion to be added to a tree */
424 | double maxDistance_;
425 |
426 | /** \brief The random number generator */
427 | RNG rng_;
428 |
429 | /** \brief Option to use k-nearest search for rewiring */
430 | bool useKNearest_;
431 |
432 | /** \brief The rewiring factor, s, so that r_rrg = s \times r_rrg* > r_rrg* (or k_rrg = s \times
433 | * k_rrg* > k_rrg*) */
434 | double rewireFactor_;
435 |
436 | /** \brief A constant for k-nearest rewiring calculations */
437 | double k_rrg_;
438 | /** \brief A constant for r-disc rewiring calculations */
439 | double r_rrg_;
440 |
441 | /** \brief Option to delay and reduce collision checking within iterations */
442 | bool delayCC_;
443 |
444 | /** \brief Objective we're optimizing */
445 | base::OptimizationObjectivePtr opt_;
446 |
447 | /** \brief The most recent goal motion. Used for PlannerData computation */
448 | Motion *lastGoalMotion_;
449 |
450 | /** \brief A list of states in the tree that satisfy the goal condition */
451 | std::vector goalMotions_;
452 |
453 | /** \brief The status of the tree pruning option. */
454 | bool useTreePruning_;
455 |
456 | /** \brief The tree is pruned when the change in solution cost is greater than this fraction. */
457 | double pruneThreshold_;
458 |
459 | /** \brief Option to use the informed measure */
460 | bool usePrunedMeasure_;
461 |
462 | /** \brief Option to use informed sampling */
463 | bool useInformedSampling_;
464 |
465 | /** \brief The status of the sample rejection parameter. */
466 | bool useRejectionSampling_;
467 |
468 | /** \brief The status of the new-state rejection parameter. */
469 | bool useNewStateRejection_;
470 |
471 | /** \brief The admissibility of the new-state rejection heuristic. */
472 | bool useAdmissibleCostToCome_;
473 |
474 | /** \brief The number of attempts to make at informed sampling */
475 | unsigned int numSampleAttempts_;
476 |
477 | /** \brief Stores the start states as Motions. */
478 | std::vector startMotions_;
479 |
480 | /** \brief Best cost found so far by algorithm */
481 | base::Cost bestCost_;
482 |
483 | /** \brief Best dist from Goal found so far by algorithm */
484 | double bestDistFromGoal_{std::numeric_limits::quiet_NaN()};
485 |
486 | /** \brief The cost at which the graph was last pruned */
487 | base::Cost prunedCost_;
488 |
489 | /** \brief The measure of the problem when we pruned it (if this isn't in use, it will be set to
490 | * si_->getSpaceMeasure())*/
491 | double prunedMeasure_;
492 |
493 | /** \brief Number of iterations the algorithm performed */
494 | unsigned int iterations_;
495 |
496 | ///////////////////////////////////////
497 | // Planner progress property functions
498 | std::string numIterationsProperty() const
499 | {
500 | return std::to_string(numIterations());
501 | }
502 | std::string bestCostProperty() const
503 | {
504 | return std::to_string(bestCost().value());
505 | }
506 | };
507 | }
508 | }
509 |
510 | #endif
511 |
--------------------------------------------------------------------------------
/esc_move_base_mapping/src/world_modeler.cpp:
--------------------------------------------------------------------------------
1 | /*! \file octomap_laser_scan.cpp
2 | * \brief Merge data from different laser_scan messages to incrementally build
3 | * an WorldModeler.
4 | *
5 | * \date November 07, 2022
6 | * \author Juan David Hernandez Vega, HernandezVegaJ@cardiff.ac.uk
7 | * \author Steven Alexander Silva Mendoza, silvas1@cardiff.ac.uk
8 | *
9 | * \details Purpose: Merge data from different laser_scan messages to
10 | * incrementally build an Octomap.
11 | * Based on laser_octomap (Guillem Vallicrosa & J.D. Hernandez Vega, University
12 | * of Girona)
13 | */
14 |
15 | #include
16 |
17 | //! Constructor.
18 | /*!
19 | * Load map parameters.
20 | * Subscribers to odometry and laser scan
21 | * Publishers to visualize the WorldModeler.
22 | */
23 | WorldModeler::WorldModeler()
24 | : Node("world_modeler_node"),
25 | fixed_frame_("fixed_frame"),
26 | robot_frame_("robot_frame"),
27 | map_frame_("map"),
28 | odometry_topic_("/odometry_topic"),
29 | offline_octomap_path_(""),
30 | octree_(NULL),
31 | octree_resol_(1.0),
32 | mapping_max_range_(5.0),
33 | initialized_(false),
34 | visualize_free_space_(false),
35 | rviz_timer_(0.0),
36 | robot_distance_view_max_(6.0),
37 | robot_distance_view_min_(1.5),
38 | robot_angle_view_(1.57),
39 | robot_velocity_threshold_(0.3),
40 | social_agent_radius_(0.4),
41 | social_agents_topic_("/pedsim_simulator/simulated_agents"),
42 | social_relevance_validity_checking_(false),
43 | min_z_pc_(0.05),
44 | max_z_pc_(1.0),
45 | social_comfort_amplitude_(6.0),
46 | orientation_drift_(0.0),
47 | position_drift_(0.0),
48 | apply_filter_(false),
49 | add_max_ranges_(false),
50 | add_rays_(false),
51 | minimum_range_(-1.0)
52 | {
53 | //=======================================================================
54 | // Get parameters
55 | //=======================================================================
56 | this->declare_parameter("add_rays", add_rays_);
57 | this->declare_parameter("resolution", octree_resol_);
58 | this->declare_parameter("map_frame", map_frame_);
59 | this->declare_parameter("fixed_frame", fixed_frame_);
60 | this->declare_parameter("robot_frame", robot_frame_);
61 | this->declare_parameter("offline_octomap_path", offline_octomap_path_);
62 | this->declare_parameter("visualize_free_space", visualize_free_space_);
63 | this->declare_parameter("odometry_topic", odometry_topic_);
64 | this->declare_parameter("rviz_timer", rviz_timer_);
65 | this->declare_parameter("laser_scan_topic", laser_scan_topic_);
66 | this->declare_parameter("laser_scan_frame", laser_scan_frame_);
67 | this->declare_parameter("point_cloud_topic", point_cloud_topic_);
68 | this->declare_parameter("point_cloud_frame", point_cloud_frame_);
69 | this->declare_parameter("mapping_max_range", mapping_max_range_);
70 | this->declare_parameter("robot_distance_view_max", robot_distance_view_max_);
71 | this->declare_parameter("robot_distance_view_min", robot_distance_view_min_);
72 | this->declare_parameter("robot_angle_view", robot_angle_view_);
73 | this->declare_parameter("robot_velocity_threshold", robot_velocity_threshold_);
74 | this->declare_parameter("social_agent_radius", social_agent_radius_);
75 | this->declare_parameter("social_agents_topic", social_agents_topic_);
76 | this->declare_parameter("social_relevance_validity_checking", social_relevance_validity_checking_);
77 | this->declare_parameter("min_z_pc", min_z_pc_);
78 | this->declare_parameter("max_z_pc", max_z_pc_);
79 | this->declare_parameter("social_comfort_amplitude", social_comfort_amplitude_);
80 | this->declare_parameter("apply_filter", apply_filter_);
81 | this->declare_parameter("add_max_ranges", add_max_ranges_);
82 | this->declare_parameter("minimum_range", minimum_range_);
83 |
84 | this->get_parameter("add_rays", add_rays_);
85 | this->get_parameter("resolution", octree_resol_);
86 | this->get_parameter("map_frame", map_frame_);
87 | this->get_parameter("fixed_frame", fixed_frame_);
88 | this->get_parameter("robot_frame", robot_frame_);
89 | this->get_parameter("offline_octomap_path", offline_octomap_path_);
90 | this->get_parameter("visualize_free_space", visualize_free_space_);
91 | this->get_parameter("odometry_topic", odometry_topic_);
92 | this->get_parameter("rviz_timer", rviz_timer_);
93 | this->get_parameter("laser_scan_topic", laser_scan_topic_);
94 | this->get_parameter("laser_scan_frame", laser_scan_frame_);
95 | this->get_parameter("point_cloud_topic", point_cloud_topic_);
96 | this->get_parameter("point_cloud_frame", point_cloud_frame_);
97 | this->get_parameter("mapping_max_range", mapping_max_range_);
98 | this->get_parameter("robot_distance_view_max", robot_distance_view_max_);
99 | this->get_parameter("robot_distance_view_min", robot_distance_view_min_);
100 | this->get_parameter("robot_angle_view", robot_angle_view_);
101 | this->get_parameter("robot_velocity_threshold", robot_velocity_threshold_);
102 | this->get_parameter("social_agent_radius", social_agent_radius_);
103 | this->get_parameter("social_agents_topic", social_agents_topic_);
104 | this->get_parameter("social_relevance_validity_checking", social_relevance_validity_checking_);
105 | this->get_parameter("min_z_pc", min_z_pc_);
106 | this->get_parameter("max_z_pc", max_z_pc_);
107 | this->get_parameter("social_comfort_amplitude", social_comfort_amplitude_);
108 | this->get_parameter("apply_filter", apply_filter_);
109 | this->get_parameter("add_max_ranges", add_max_ranges_);
110 | this->get_parameter("minimum_range", minimum_range_);
111 |
112 | this->set_parameter(rclcpp::Parameter("use_sim_time", true));
113 |
114 | //=======================================================================
115 | // Transforms TF and catch the static transform from vehicle to laser_scan
116 | // sensor
117 | //=======================================================================
118 | int count(0);
119 | rclcpp::Time t;
120 | geometry_msgs::msg::TransformStamped transform;
121 | tf_buffer_ = std::make_shared(this->get_clock());
122 | tf_buffer_->setUsingDedicatedThread(true);
123 | tf_listener_ = std::make_shared(*tf_buffer_);
124 |
125 | auto create_timer_interface = std::make_shared(
126 | this->get_node_base_interface(),
127 | this->get_node_timers_interface());
128 |
129 | tf_buffer_->setCreateTimerInterface(create_timer_interface);
130 |
131 | initialized_ = false;
132 | do
133 | {
134 | try
135 | {
136 | transform = tf_buffer_->lookupTransform(robot_frame_, point_cloud_frame_, tf2::TimePointZero, tf2::durationFromSec(1.0));
137 | initialized_ = true;
138 | }
139 | catch (tf2::TransformException &ex)
140 | {
141 |
142 | count++;
143 | RCLCPP_WARN(this->get_logger(), "Cannot find tf from %s to %s: %s",
144 | robot_frame_.c_str(), point_cloud_frame_.c_str(), ex.what());
145 | }
146 | if (count > 10)
147 | {
148 | RCLCPP_ERROR(this->get_logger(), "No transform found. Aborting...");
149 | exit(-1);
150 | }
151 | } while (rclcpp::ok() && !initialized_);
152 |
153 | RCLCPP_WARN(this->get_logger(), "tf from %s to %s OK", robot_frame_.c_str(), point_cloud_frame_.c_str());
154 |
155 | //=======================================================================
156 | // Octree
157 | //=======================================================================
158 | if (offline_octomap_path_.size() > 0)
159 | octree_ = new octomap::OcTree(offline_octomap_path_);
160 | else
161 | octree_ = new octomap::OcTree(octree_resol_);
162 | RCLCPP_WARN(this->get_logger(), "Loaded octree");
163 |
164 | octree_->setProbHit(0.7);
165 | octree_->setProbMiss(0.4);
166 | octree_->setClampingThresMin(0.1192);
167 | octree_->setClampingThresMax(0.971);
168 |
169 | //=======================================================================
170 | // Gridmap
171 | //=======================================================================
172 |
173 | grid_map_.setFrameId(map_frame_);
174 | grid_map_.add("full");
175 | grid_map_.add("comfort");
176 | grid_map_.setGeometry(grid_map::Length(1, 1), octree_resol_);
177 |
178 | //=======================================================================
179 | // Publishers
180 | //=======================================================================
181 | octomap_marker_pub_ = this->create_publisher("octomap_map", 2);
182 | grid_map_pub_ = this->create_publisher("social_grid_map", 1);
183 |
184 | //=======================================================================
185 | // Subscribers
186 | //=======================================================================
187 |
188 | // Agent states callback
189 | agent_states_sub_ = this->create_subscription(
190 | social_agents_topic_, 1, std::bind(&WorldModeler::agentStatesCallback, this, std::placeholders::_1));
191 |
192 | // Odometry data (feedback)
193 | odom_sub_ = this->create_subscription(
194 | odometry_topic_, 1, std::bind(&WorldModeler::odomCallback, this, std::placeholders::_1));
195 |
196 | nav_sts_available_ = false;
197 | if (!nav_sts_available_)
198 | RCLCPP_WARN(this->get_logger(), "Waiting for odometry.");
199 |
200 | rclcpp::Rate loop_rate(10);
201 | while (rclcpp::ok() && !nav_sts_available_)
202 | {
203 | rclcpp::spin_some(this->get_node_base_interface());
204 | loop_rate.sleep();
205 | }
206 | RCLCPP_WARN(this->get_logger(), "Odometry received.");
207 |
208 | if (offline_octomap_path_.size() == 0)
209 | {
210 | // POINTCLOUD
211 | std::vector pc_need_frames;
212 | pc_need_frames.push_back(point_cloud_frame_);
213 | pc_need_frames.push_back(fixed_frame_);
214 | pc_need_frames.push_back(robot_frame_);
215 | point_cloud_sub_ = std::make_shared>(this, point_cloud_topic_);
216 | point_cloud_mn_ = std::make_shared>(
217 | *tf_buffer_,
218 | point_cloud_frame_,
219 | 5,
220 | this->get_node_logging_interface(),
221 | this->get_node_clock_interface());
222 | point_cloud_mn_->connectInput(*point_cloud_sub_);
223 | point_cloud_mn_->setTargetFrames(pc_need_frames);
224 | point_cloud_mn_->registerCallback(&WorldModeler::pointCloudCallback, this);
225 |
226 | // LASERSCAN
227 | std::vector laser_need_frames;
228 | laser_need_frames.push_back(point_cloud_frame_);
229 | laser_need_frames.push_back(fixed_frame_);
230 | laser_need_frames.push_back(robot_frame_);
231 |
232 | laser_scan_sub_ = std::make_shared>(this, laser_scan_topic_);
233 | laser_scan_mn_ = std::make_shared>(
234 | *tf_buffer_,
235 | laser_scan_frame_,
236 | 5,
237 | this->get_node_logging_interface(),
238 | this->get_node_clock_interface());
239 | laser_scan_mn_->connectInput(*laser_scan_sub_);
240 | laser_scan_mn_->setTargetFrames(laser_need_frames);
241 | laser_scan_mn_->registerCallback(&WorldModeler::laserScanCallback, this);
242 | }
243 |
244 | //=======================================================================
245 | // Services
246 | //=======================================================================
247 | save_binary_octomap_srv_ = this->create_service(
248 | "save_binary", std::bind(&WorldModeler::saveBinaryOctomapSrv, this, std::placeholders::_1, std::placeholders::_2));
249 | save_full_octomap_srv_ = this->create_service(
250 | "save_full", std::bind(&WorldModeler::saveFullOctomapSrv, this, std::placeholders::_1, std::placeholders::_2));
251 | get_binary_octomap_srv_ = this->create_service(
252 | "get_binary", std::bind(&WorldModeler::getBinaryOctomapSrv, this, std::placeholders::_1, std::placeholders::_2));
253 | get_grid_map_srv_ = this->create_service(
254 | "get_grid_map", std::bind(&WorldModeler::getGridMapSrv, this, std::placeholders::_1, std::placeholders::_2));
255 |
256 | // Timer for publishing
257 | if (rviz_timer_ > 0.0)
258 | {
259 | timer_ = this->create_wall_timer(
260 | std::chrono::duration(rviz_timer_), std::bind(&WorldModeler::timerCallback, this));
261 | }
262 | }
263 |
264 | //! Destructor.
265 | WorldModeler::~WorldModeler()
266 | {
267 | RCLCPP_INFO(this->get_logger(), "Octree has been deleted.");
268 | delete octree_;
269 | }
270 |
271 | //! Laserscan callback.
272 | /*!
273 | * Callback for receiving the laserscan data (taken from octomap_server)
274 | */
275 | void WorldModeler::laserScanCallback(const sensor_msgs::msg::LaserScan::SharedPtr laser_scan_msg)
276 | {
277 |
278 | geometry_msgs::msg::TransformStamped tf_robot_to_laser_scan, tf_fixed_to_robot, tf_map_to_fixed;
279 |
280 | try
281 | {
282 | // Check drift
283 | tf_map_to_fixed = tf_buffer_->lookupTransform(map_frame_, fixed_frame_, tf2::TimePointZero);
284 | tf2::Matrix3x3 m_map_to_fixed(tf2::Quaternion(
285 | tf_map_to_fixed.transform.rotation.x,
286 | tf_map_to_fixed.transform.rotation.y,
287 | tf_map_to_fixed.transform.rotation.z,
288 | tf_map_to_fixed.transform.rotation.w));
289 | tf2::Vector3 p_map_to_fixed(
290 | tf_map_to_fixed.transform.translation.x,
291 | tf_map_to_fixed.transform.translation.y,
292 | tf_map_to_fixed.transform.translation.z);
293 |
294 | double roll, pitch, yaw;
295 | m_map_to_fixed.getRPY(roll, pitch, yaw);
296 | orientation_drift_ += std::abs(prev_map_to_fixed_yaw_ - yaw);
297 | prev_map_to_fixed_yaw_ = yaw;
298 | position_drift_ += std::sqrt(std::pow(prev_map_to_fixed_pos_.x() - p_map_to_fixed.x(), 2.0) +
299 | std::pow(prev_map_to_fixed_pos_.y() - p_map_to_fixed.y(), 2.0));
300 | prev_map_to_fixed_pos_ = p_map_to_fixed;
301 |
302 | if (orientation_drift_ > 0.05 || position_drift_ > 0.05)
303 | {
304 | orientation_drift_ = 0.0;
305 | position_drift_ = 0.0;
306 | octree_->clear();
307 | // mergeGlobalMapToOctomap();
308 | }
309 |
310 | tf_robot_to_laser_scan = tf_buffer_->lookupTransform(robot_frame_, laser_scan_msg->header.frame_id, tf2::TimePointZero);
311 | tf_fixed_to_robot = tf_buffer_->lookupTransform(fixed_frame_, robot_frame_, tf2::TimePointZero);
312 | }
313 | catch (tf2::TransformException &ex)
314 | {
315 | RCLCPP_ERROR(this->get_logger(), "Transform error: %s", ex.what());
316 | return;
317 | }
318 |
319 | // Editable message
320 | sensor_msgs::msg::LaserScan laser_scan = *laser_scan_msg;
321 |
322 | // Max range flags
323 | std::vector rngflags;
324 | rngflags.resize(laser_scan.ranges.size());
325 | for (int i = 0; i < laser_scan.ranges.size(); i++)
326 | {
327 | rngflags[i] = false;
328 | if (!rclcpp::ok())
329 | break;
330 | }
331 |
332 | // Use zero ranges as max ranges
333 | double new_max = laser_scan.range_max * 0.95;
334 | if (add_max_ranges_)
335 | {
336 | // Flag readings as maxrange
337 | for (int i = 0; i < laser_scan.ranges.size(); i++)
338 | {
339 | if (laser_scan.ranges[i] == 0.0 || laser_scan.ranges[i] >= laser_scan.range_max)
340 | {
341 | laser_scan.ranges[i] = new_max;
342 | rngflags[i] = true;
343 | }
344 | }
345 | }
346 |
347 | // Apply single outliers removal filter
348 | if (apply_filter_)
349 | {
350 | // Copy message for editing, filter and project to (x,y,z)
351 | sensor_msgs::msg::LaserScan laser_scan = *laser_scan_msg;
352 | filterSingleOutliers(laser_scan, rngflags);
353 | }
354 |
355 | // Project to (x,y,z)
356 | laser_scan_projector_.projectLaser(laser_scan, cloud_, laser_scan.range_max);
357 |
358 | // Compute origin of sensor in world frame (filters laser_scan_msg->ranges[i] > 0.0)
359 | geometry_msgs::msg::PoseStamped orig;
360 | tf2::doTransform(orig, orig, tf_fixed_to_robot);
361 | tf2::doTransform(orig, orig, tf_robot_to_laser_scan);
362 | octomap::point3d origin(orig.pose.position.x, orig.pose.position.y, orig.pose.position.z);
363 |
364 | // ! AGENTS FILTERING
365 |
366 | // =============================
367 |
368 | PCLPointCloud pc; // input cloud for filtering and ground-detection
369 | pcl::fromROSMsg(cloud_, pc);
370 |
371 | float minX = -12, minY = -0.6, minZ = -0.6;
372 | float maxX = 12, maxY = 0.6, maxZ = 0.6;
373 |
374 | geometry_msgs::msg::TransformStamped transform;
375 |
376 | if (social_agents_in_radius_.agent_states.size() > 0)
377 | {
378 | for (int i = 0; i < social_agents_in_radius_.agent_states.size(); i++)
379 | {
380 | std::string agent_frame = "agent_" + std::to_string(social_agents_in_radius_.agent_states[i].id);
381 | try
382 | {
383 | transform = tf_buffer_->lookupTransform(laser_scan_msg->header.frame_id, agent_frame, laser_scan_msg->header.stamp);
384 | }
385 | catch (tf2::TransformException &ex)
386 | {
387 | RCLCPP_WARN(this->get_logger(), "Transform error of sensor data: %s. Getting the latest obtained transform.", ex.what());
388 | transform = tf_buffer_->lookupTransform(laser_scan_msg->header.frame_id, agent_frame, tf2::TimePointZero);
389 | }
390 |
391 | // Z -> X
392 | // X -> Y
393 | // Y -> -Z
394 | pcl::CropBox boxFilter;
395 | boxFilter.setMin(Eigen::Vector4f(minX, minY, minZ, 0));
396 | boxFilter.setMax(Eigen::Vector4f(maxX, maxY, maxZ, 0));
397 | boxFilter.setInputCloud(pc.makeShared());
398 | boxFilter.setTranslation(Eigen::Vector3f(transform.transform.translation.x, transform.transform.translation.y, transform.transform.translation.z));
399 | // boxFilter.setRotation(Eigen::Vector3f(roll, pitch + 90, yaw));
400 | boxFilter.setNegative(true);
401 | boxFilter.filter(pc);
402 | }
403 | }
404 |
405 | geometry_msgs::msg::TransformStamped sensorToWorldTf;
406 | try
407 | {
408 | sensorToWorldTf = tf_buffer_->lookupTransform(fixed_frame_, laser_scan_msg->header.frame_id, laser_scan_msg->header.stamp);
409 | }
410 | catch (tf2::TransformException &ex)
411 | {
412 | RCLCPP_ERROR(this->get_logger(), "Transform error of sensor data: %s, quitting callback", ex.what());
413 | return;
414 | }
415 |
416 | Eigen::Matrix4f sensorToWorld;
417 | pcl_ros::transformAsMatrix(sensorToWorldTf, sensorToWorld);
418 |
419 | // set up filter for height range, also removes NANs:
420 | // pcl::PassThrough pass_x;
421 | // pass_x.setFilterFieldName("x");
422 | // pass_x.setFilterLimits(0.15, 4.0);
423 | // pcl::PassThrough pass_y;
424 | // pass_y.setFilterFieldName("y");
425 | // pass_y.setFilterLimits(0.15, 4.0);
426 | pcl::PassThrough pass_z;
427 | pass_z.setFilterFieldName("z");
428 | pass_z.setFilterLimits(min_z_pc_, max_z_pc_); // TODO
429 |
430 | PCLPointCloud pc_ground; // segmented ground plane
431 | PCLPointCloud pc_nonground; // everything else
432 |
433 | // directly transform to map frame:
434 | pcl::transformPointCloud(pc, pc, sensorToWorld);
435 |
436 | // just filter height range:
437 | // pass_x.setInputCloud(pc.makeShared());
438 | // pass_x.filter(pc);
439 | // pass_y.setInputCloud(pc.makeShared());
440 | // pass_y.filter(pc);
441 | pass_z.setInputCloud(pc.makeShared());
442 | pass_z.filter(pc);
443 |
444 | pc_nonground = pc;
445 |
446 | // pc_nonground is empty without ground segmentation
447 | pc_ground.header = pc.header;
448 | pc_nonground.header = pc.header;
449 |
450 | insertScan(sensorToWorldTf.transform.translation, pc_ground, pc_nonground, mapping_max_range_, minimum_range_);
451 | }
452 |
453 | //! Pointcloud callback.
454 | /*!
455 | * Callback for receiving the pointcloud data (taken from octomap_server)
456 | */
457 | void WorldModeler::pointCloudCallback(const sensor_msgs::msg::PointCloud2::SharedPtr cloud)
458 | {
459 | // ROS_INFO_STREAM("PROCESSING POINTCLOUD");
460 | //
461 | // ground filtering in base frame
462 | //
463 | PCLPointCloud pc; // input cloud for filtering and ground-detection
464 | pcl::fromROSMsg(*cloud, pc);
465 |
466 | float minX = -0.6, minY = -0.6, minZ = -12;
467 | float maxX = 0.6, maxY = 0.6, maxZ = 12;
468 |
469 | // ROS_INFO_STREAM("ABOUT TO PROCESS AGENTS");
470 |
471 | if (social_agents_in_radius_.agent_states.size() > 0)
472 | {
473 | for (int i = 0; i < social_agents_in_radius_.agent_states.size(); i++)
474 | {
475 | std::string err = "cannot find transform from agent to camera frame";
476 |
477 | geometry_msgs::msg::TransformStamped transform;
478 | rclcpp::Time t;
479 |
480 | try
481 | {
482 | transform = tf_buffer_->lookupTransform(cloud->header.frame_id, "agent_" + std::to_string(social_agents_in_radius_.agent_states[i].id), cloud->header.stamp);
483 |
484 | tf2::Quaternion q(
485 | transform.transform.rotation.x,
486 | transform.transform.rotation.y,
487 | transform.transform.rotation.z,
488 | transform.transform.rotation.w);
489 | tf2::Matrix3x3 m(q);
490 | double roll, pitch, yaw;
491 | m.getRPY(roll, pitch, yaw);
492 |
493 | // Z -> X
494 | // X -> Y
495 | // Y -> -Z
496 | pcl::CropBox boxFilter;
497 | boxFilter.setMin(Eigen::Vector4f(minX, minY, minZ, 0));
498 | boxFilter.setMax(Eigen::Vector4f(maxX, maxY, maxZ, 0));
499 | boxFilter.setInputCloud(pc.makeShared());
500 | boxFilter.setTranslation(Eigen::Vector3f(transform.transform.translation.x, transform.transform.translation.y, transform.transform.translation.z));
501 | boxFilter.setRotation(Eigen::Vector3f(roll, pitch, yaw));
502 | boxFilter.setNegative(true);
503 | boxFilter.filter(pc);
504 | }
505 | catch (tf2::TransformException &ex)
506 | {
507 | RCLCPP_ERROR(this->get_logger(), "Transform error of sensor data: %s, quitting callback", ex.what());
508 | return;
509 | }
510 | }
511 | }
512 | // ROS_INFO_STREAM("ABOUT TO PROCESS AGENTS FINISHED");
513 |
514 | rclcpp::Time t;
515 | std::string err = "cannot find transform from robot_frame to scan frame";
516 |
517 | geometry_msgs::msg::TransformStamped sensorToWorldTf;
518 | try
519 | {
520 | // tf_listener_.lookupTransform(robot_frame_,
521 | // laser_scan_msg->header.frame_id, t,
522 | // tf_robot_to_laser_scan);
523 | sensorToWorldTf = tf_buffer_->lookupTransform(fixed_frame_, cloud->header.frame_id, cloud->header.stamp);
524 | }
525 | catch (tf2::TransformException &ex)
526 | {
527 | RCLCPP_ERROR(this->get_logger(), "Transform error of sensor data: %s, quitting callback", ex.what());
528 | return;
529 | }
530 |
531 | Eigen::Matrix4f sensorToWorld;
532 | pcl_ros::transformAsMatrix(sensorToWorldTf, sensorToWorld);
533 |
534 | // set up filter for height range, also removes NANs:
535 | // pcl::PassThrough pass_x;
536 | // pass_x.setFilterFieldName("x");
537 | // pass_x.setFilterLimits(0.15, 4.0);
538 | // pcl::PassThrough pass_y;
539 | // pass_y.setFilterFieldName("y");
540 | // pass_y.setFilterLimits(0.15, 4.0);
541 | pcl::PassThrough pass_z;
542 | pass_z.setFilterFieldName("z");
543 | pass_z.setFilterLimits(min_z_pc_, max_z_pc_); // TODO
544 |
545 | PCLPointCloud pc_ground; // segmented ground plane
546 | PCLPointCloud pc_nonground; // everything else
547 |
548 | // directly transform to map frame:
549 | pcl::transformPointCloud(pc, pc, sensorToWorld);
550 |
551 | // just filter height range:
552 | // pass_x.setInputCloud(pc.makeShared());
553 | // pass_x.filter(pc);
554 | // pass_y.setInputCloud(pc.makeShared());
555 | // pass_y.filter(pc);
556 | pass_z.setInputCloud(pc.makeShared());
557 | pass_z.filter(pc);
558 |
559 | pc_nonground = pc;
560 |
561 | // pc_nonground is empty without ground segmentation
562 | pc_ground.header = pc.header;
563 | pc_nonground.header = pc.header;
564 |
565 | // ROS_INFO_STREAM("INSERT SCAN START");
566 |
567 | insertScan(sensorToWorldTf.transform.translation, pc_ground, pc_nonground, mapping_max_range_, minimum_range_);
568 | //
569 | // double total_elapsed = (ros::WallTime::now() - startTime).toSec();
570 | // ROS_DEBUG("Pointcloud insertion in OctomapServer done (%zu+%zu pts
571 | // (ground/nonground), %f sec)",
572 | // pc_ground.size(), pc_nonground.size(), total_elapsed);
573 | //
574 | // publishAll(cloud->header.stamp);
575 |
576 | defineSocialGridMap();
577 | }
578 |
579 | void WorldModeler::insertScan(const geometry_msgs::msg::Vector3 &sensorOriginTf,
580 | const PCLPointCloud &ground,
581 | const PCLPointCloud &nonground, const double &max_range, const double &min_range)
582 | {
583 |
584 | octomap::point3d sensorOrigin(sensorOriginTf.x, sensorOriginTf.y, sensorOriginTf.z);
585 |
586 | if (!octree_->coordToKeyChecked(sensorOrigin, m_updateBBXMin) ||
587 | !octree_->coordToKeyChecked(sensorOrigin, m_updateBBXMax))
588 | {
589 | RCLCPP_ERROR(this->get_logger(), "Could not generate Key for origin %f %f %f",
590 | sensorOrigin.x(), sensorOrigin.y(), sensorOrigin.z());
591 | }
592 |
593 | #ifdef COLOR_OCTOMAP_SERVER
594 | unsigned char *colors = new unsigned char[3];
595 | #endif
596 |
597 | // instead of direct scan insertion, compute update to filter ground:
598 | octomap::KeySet free_cells, occupied_cells;
599 |
600 | // all other points: free on ray, occupied on endpoint:
601 | double m_maxRange(max_range); // TODO
602 | double m_minRange(min_range);
603 | for (PCLPointCloud::const_iterator it = nonground.begin();
604 | it != nonground.end(); ++it)
605 | {
606 | octomap::point3d point(it->x, it->y, it->z); // TODO
607 | // maxrange check
608 | if ((m_maxRange < 0.0) || ((point - sensorOrigin).norm() <= m_maxRange && (point - sensorOrigin).norm() >= m_minRange))
609 | {
610 |
611 | // free cells
612 | if (octree_->computeRayKeys(sensorOrigin, point, m_keyRay))
613 | {
614 | free_cells.insert(m_keyRay.begin(), m_keyRay.end());
615 | }
616 | // occupied endpoint
617 | octomap::OcTreeKey key;
618 | if (octree_->coordToKeyChecked(point, key))
619 | {
620 | occupied_cells.insert(key);
621 |
622 | updateMinKey(key, m_updateBBXMin);
623 | updateMaxKey(key, m_updateBBXMax);
624 | }
625 | }
626 | else
627 | {
628 | // ray longer than maxrange:;
629 | octomap::point3d new_end =
630 | sensorOrigin + (point - sensorOrigin).normalized() * m_maxRange;
631 | if (octree_->computeRayKeys(sensorOrigin, new_end, m_keyRay))
632 | {
633 | free_cells.insert(m_keyRay.begin(), m_keyRay.end());
634 |
635 | octomap::OcTreeKey endKey;
636 | if (octree_->coordToKeyChecked(new_end, endKey))
637 | {
638 | free_cells.insert(endKey);
639 | updateMinKey(endKey, m_updateBBXMin);
640 | updateMaxKey(endKey, m_updateBBXMax);
641 | }
642 | else
643 | {
644 | RCLCPP_ERROR(this->get_logger(), "Could not generate Key for endpoint %f %f %f",
645 | new_end.x(), new_end.y(), new_end.z());
646 | }
647 | }
648 | }
649 | }
650 |
651 | // mark free cells only if not seen occupied in this cloud
652 | for (octomap::KeySet::iterator it = free_cells.begin(),
653 | end = free_cells.end();
654 | it != end; ++it)
655 | {
656 | if (occupied_cells.find(*it) == occupied_cells.end())
657 | {
658 | octree_->updateNode(*it, false);
659 | }
660 | }
661 |
662 | // now mark all occupied cells:
663 | for (octomap::KeySet::iterator it = occupied_cells.begin(),
664 | end = occupied_cells.end();
665 | it != end; it++)
666 | {
667 | octree_->updateNode(*it, true);
668 | }
669 | }
670 |
671 | //! Odometry callback.
672 | /*!
673 | * Callback for getting updated vehicle odometry.
674 | */
675 | void WorldModeler::odomCallback(const nav_msgs::msg::Odometry::SharedPtr odom_msg)
676 | {
677 | if (!nav_sts_available_)
678 | nav_sts_available_ = true;
679 |
680 | robot_odometry_ = odom_msg;
681 | }
682 |
683 | //! Agent States callback.
684 | /*!
685 | * Callback for getting updated agent states.
686 | */
687 | void WorldModeler::agentStatesCallback(const pedsim_msgs::msg::AgentStates::SharedPtr agent_states_msg)
688 | {
689 | if (nav_sts_available_)
690 | {
691 | agent_states_ = agent_states_msg;
692 |
693 | std::vector agent_state_vector;
694 |
695 | social_agents_in_radius_vector_.clear();
696 |
697 | double robot_velocity =
698 | std::sqrt(std::pow(robot_odometry_->twist.twist.linear.x, 2) + std::pow(robot_odometry_->twist.twist.linear.y, 2));
699 |
700 | actual_fov_distance_ = robot_distance_view_max_ / robot_velocity_threshold_ * robot_velocity;
701 |
702 | if (actual_fov_distance_ < robot_distance_view_min_)
703 | {
704 | actual_fov_distance_ = robot_distance_view_min_;
705 | }
706 | else if (actual_fov_distance_ > robot_distance_view_max_)
707 | {
708 | actual_fov_distance_ = robot_distance_view_max_;
709 | }
710 |
711 | for (int i = 0; i < agent_states_msg->agent_states.size(); i++)
712 | {
713 | if (this->isAgentInRFOV(agent_states_msg->agent_states[i]))
714 | {
715 | agent_state_vector.push_back(agent_states_msg->agent_states[i]);
716 | }
717 | }
718 |
719 | relevant_agent_states_.agent_states = agent_state_vector;
720 |
721 | social_agents_in_radius_.agent_states = social_agents_in_radius_vector_;
722 | }
723 | }
724 |
725 | bool WorldModeler::isAgentInRFOV(const pedsim_msgs::msg::AgentState agent_state)
726 | {
727 | if (!social_relevance_validity_checking_)
728 | {
729 | return true;
730 | }
731 |
732 | double d_robot_agent = std::sqrt(std::pow(agent_state.pose.position.x - robot_odometry_->pose.pose.position.x, 2) +
733 | std::pow(agent_state.pose.position.y - robot_odometry_->pose.pose.position.y, 2));
734 |
735 | if (d_robot_agent < mapping_max_range_ + 2)
736 | {
737 | social_agents_in_radius_vector_.push_back(agent_state);
738 | }
739 |
740 | if (d_robot_agent > actual_fov_distance_)
741 | {
742 | return false;
743 | }
744 |
745 | double tetha_robot_agent = atan2((agent_state.pose.position.y - robot_odometry_->pose.pose.position.y),
746 | (agent_state.pose.position.x - robot_odometry_->pose.pose.position.x));
747 |
748 | if (tetha_robot_agent < 0)
749 | {
750 | tetha_robot_agent = 2 * M_PI + tetha_robot_agent;
751 | }
752 |
753 | tf2::Quaternion q(robot_odometry_->pose.pose.orientation.x, robot_odometry_->pose.pose.orientation.y,
754 | robot_odometry_->pose.pose.orientation.z, robot_odometry_->pose.pose.orientation.w);
755 |
756 | tf2::Matrix3x3 m(q);
757 | double roll, pitch, yaw;
758 | m.getRPY(roll, pitch, yaw);
759 |
760 | double robot_angle = yaw;
761 |
762 | if (robot_angle < 0)
763 | {
764 | robot_angle = 2 * M_PI + robot_angle;
765 | }
766 |
767 | if (tetha_robot_agent > (robot_angle + M_PI))
768 | tetha_robot_agent = abs(robot_angle + 2 * M_PI - tetha_robot_agent);
769 | else if (robot_angle > (tetha_robot_agent + M_PI))
770 | tetha_robot_agent = abs(tetha_robot_agent + 2 * M_PI - robot_angle);
771 | else
772 | tetha_robot_agent = abs(tetha_robot_agent - robot_angle);
773 |
774 | return abs(tetha_robot_agent) < robot_angle_view_;
775 | }
776 |
777 | bool WorldModeler::isRobotInFront(const pedsim_msgs::msg::AgentState agent_state, const grid_map::Position position)
778 | {
779 | double tetha_agent_robot = atan2((position[1] - agent_state.pose.position.y),
780 | (position[0] - agent_state.pose.position.x));
781 | if (tetha_agent_robot < 0)
782 | {
783 | tetha_agent_robot = 2 * M_PI + tetha_agent_robot;
784 | }
785 |
786 | tf2::Quaternion q(agent_state.pose.orientation.x, agent_state.pose.orientation.y,
787 | agent_state.pose.orientation.z, agent_state.pose.orientation.w);
788 |
789 | tf2::Matrix3x3 m(q);
790 | double roll, pitch, yaw;
791 | m.getRPY(roll, pitch, yaw);
792 |
793 | double agent_angle = yaw;
794 |
795 | if (agent_angle < 0)
796 | {
797 | agent_angle = 2 * M_PI + agent_angle;
798 | }
799 |
800 | if (tetha_agent_robot > (agent_angle + M_PI))
801 | tetha_agent_robot = abs(agent_angle + 2 * M_PI - tetha_agent_robot);
802 | else if (agent_angle > (tetha_agent_robot + M_PI))
803 | tetha_agent_robot = abs(tetha_agent_robot + 2 * M_PI - agent_angle);
804 | else
805 | tetha_agent_robot = abs(tetha_agent_robot - agent_angle);
806 |
807 | if (abs(tetha_agent_robot) < robot_angle_view_)
808 | return true;
809 |
810 | return false;
811 | }
812 |
813 | // !CALCULATE COMFORT AT AN SPECIFIC POSITION IN THE GRIDMAP
814 |
815 | double WorldModeler::getExtendedPersonalSpace(const pedsim_msgs::msg::AgentState agent_state, const grid_map::Position position)
816 | {
817 | double distance_robot_agent = std::sqrt(std::pow(agent_state.pose.position.x - position[0], 2) +
818 | std::pow(agent_state.pose.position.y - position[1], 2));
819 |
820 | double tetha_robot_agent = atan2((position[1] - agent_state.pose.position.y),
821 | (position[0] - agent_state.pose.position.x));
822 |
823 | if (tetha_robot_agent < 0)
824 | {
825 | tetha_robot_agent = 2 * M_PI + tetha_robot_agent;
826 | }
827 |
828 | double tetha_orientation;
829 | if (abs(agent_state.twist.linear.x) > 0 || abs(agent_state.twist.linear.y) > 0)
830 | {
831 | tetha_orientation = atan2(agent_state.twist.linear.y, agent_state.twist.linear.x);
832 | }
833 | else
834 | {
835 | tf2::Quaternion q(agent_state.pose.orientation.x, agent_state.pose.orientation.y,
836 | agent_state.pose.orientation.z, agent_state.pose.orientation.w);
837 |
838 | tf2::Matrix3x3 m(q);
839 | double roll, pitch, yaw;
840 | m.getRPY(roll, pitch, yaw);
841 |
842 | tetha_orientation = yaw;
843 | }
844 |
845 | if (tetha_orientation < 0)
846 | {
847 | tetha_orientation = 2 * M_PI + tetha_orientation;
848 | }
849 |
850 | bool robot_in_front = false;
851 | bool robot_in_fov = false;
852 | double mod_sigma_y;
853 | double agent_velocity;
854 |
855 | agent_velocity =
856 | std::sqrt(std::pow(agent_state.twist.linear.x, 2) + std::pow(agent_state.twist.linear.y, 2));
857 |
858 | robot_in_front = this->isRobotInFront(agent_state, position);
859 |
860 | if (robot_in_front)
861 | {
862 | if (robot_in_fov)
863 | mod_sigma_y = (1 + agent_velocity * fv + fFront + fFieldOfView) * sigma_y;
864 | else
865 | mod_sigma_y = (1 + agent_velocity * fv + fFront) * sigma_y;
866 | }
867 | else
868 | {
869 | mod_sigma_y = sigma_y;
870 | }
871 |
872 | double basic_personal_space_value =
873 | social_comfort_amplitude_ *
874 | std::exp(-(
875 | std::pow(distance_robot_agent * std::sin(tetha_robot_agent - tetha_orientation) / (std::sqrt(2) * sigma_x),
876 | 2) +
877 | std::pow(distance_robot_agent * std::cos(tetha_robot_agent - tetha_orientation) / (std::sqrt(2) * mod_sigma_y),
878 | 2)));
879 |
880 | return basic_personal_space_value;
881 | }
882 |
883 | //! Time callback.
884 | /*!
885 | * Callback for publishing the map periodically using the WorldModeler RViz plugin.
886 | */
887 | void WorldModeler::timerCallback()
888 | {
889 | if (offline_octomap_path_.size() != 0)
890 | {
891 | defineSocialGridMap();
892 | }
893 |
894 | // Declare message
895 | octomap_msgs::msg::Octomap msg;
896 | octomap_msgs::binaryMapToMsg(*octree_, msg);
897 | msg.header.stamp = this->get_clock()->now();
898 | msg.header.frame_id = fixed_frame_;
899 |
900 | if (visualize_free_space_)
901 | {
902 | publishMap();
903 |
904 | grid_map_.setTimestamp(this->get_clock()->now().nanoseconds());
905 | std::shared_ptr message;
906 | message = grid_map::GridMapRosConverter::toMessage(grid_map_);
907 | grid_map_pub_->publish(*message);
908 | }
909 | }
910 |
911 | //! Save binary service
912 | /*!
913 | * Service for saving the binary Octomap into the home folder
914 | */
915 | bool WorldModeler::saveBinaryOctomapSrv(
916 | const std::shared_ptr req,
917 | std::shared_ptr res)
918 | {
919 | // Saves current octree_ in home folder
920 | std::string fpath(getenv("HOME"));
921 | octree_->writeBinary(fpath + "/map_laser_octomap.bt");
922 | return true;
923 | }
924 |
925 | //! Save binary service
926 | /*!
927 | * Service for saving the full Octomap into the home folder
928 | */
929 | bool WorldModeler::saveFullOctomapSrv(
930 | const std::shared_ptr req,
931 | std::shared_ptr res)
932 | {
933 | // Saves current octree_ in home folder (full probabilities)
934 | std::string fpath(getenv("HOME"));
935 | octree_->write(fpath + "/map_laser_octomap.ot");
936 | return true;
937 | }
938 |
939 | //! Get binary service
940 | /*!
941 | * Service for getting the binary Octomap
942 | */
943 | bool WorldModeler::getBinaryOctomapSrv(
944 | const std::shared_ptr req,
945 | std::shared_ptr res)
946 | {
947 | RCLCPP_INFO(get_logger(), "Sending binary map data on service request");
948 |
949 | res->map.header.frame_id = fixed_frame_;
950 | res->map.header.stamp = this->get_clock()->now();
951 |
952 | if (!octomap_msgs::binaryMapToMsg(*octree_, res->map))
953 | return false;
954 |
955 | return true;
956 | }
957 |
958 | //! Get binary service
959 | /*!
960 | * Service for getting the binary Octomap
961 | */
962 | bool WorldModeler::getGridMapSrv(
963 | const std::shared_ptr req,
964 | std::shared_ptr res)
965 | {
966 | RCLCPP_INFO(get_logger(), "Sending grid map data on service");
967 |
968 | grid_map_.setTimestamp(this->get_clock()->now().nanoseconds());
969 |
970 | res->map = *grid_map::GridMapRosConverter::toMessage(grid_map_);
971 |
972 | return true;
973 | }
974 |
975 | //! Publish the map
976 | /*!
977 | * Service for saving the binary of the Octomap into the home folder
978 | */
979 | void WorldModeler::publishMap()
980 | {
981 | // Declare message and resize
982 | visualization_msgs::msg::MarkerArray occupiedNodesVis;
983 | occupiedNodesVis.markers.resize(octree_->getTreeDepth() + 1);
984 |
985 | // Octree limits for height map
986 | double min_x, min_y, max_x, max_y, min_z_, max_z_;
987 | octree_->getMetricMin(min_x, min_y, min_z_);
988 | octree_->getMetricMax(max_x, max_y, max_z_);
989 |
990 | // Traverse all leafs in the tree:
991 | for (octomap::OcTree::iterator it = octree_->begin(octree_->getTreeDepth()),
992 | end = octree_->end();
993 | it != end; ++it)
994 | {
995 | if (octree_->isNodeOccupied(*it))
996 | {
997 | double x = it.getX();
998 | double y = it.getY();
999 | double z = it.getZ();
1000 |
1001 | // Create marker:
1002 | unsigned idx = it.getDepth();
1003 | assert(idx < occupiedNodesVis.markers.size());
1004 |
1005 | geometry_msgs::msg::Point cubeCenter;
1006 | cubeCenter.x = x;
1007 | cubeCenter.y = y;
1008 | cubeCenter.z = z;
1009 |
1010 | occupiedNodesVis.markers[idx].points.push_back(cubeCenter);
1011 | }
1012 |
1013 | if (!rclcpp::ok())
1014 | break;
1015 | }
1016 | // Finish Headers and options
1017 | for (unsigned i = 0; i < occupiedNodesVis.markers.size(); ++i)
1018 | {
1019 | double size = octree_->getNodeSize(i);
1020 |
1021 | occupiedNodesVis.markers[i].header.frame_id = fixed_frame_;
1022 | occupiedNodesVis.markers[i].header.stamp = this->get_clock()->now();
1023 | occupiedNodesVis.markers[i].ns = fixed_frame_;
1024 | occupiedNodesVis.markers[i].id = i;
1025 | occupiedNodesVis.markers[i].type = visualization_msgs::msg::Marker::CUBE_LIST;
1026 | occupiedNodesVis.markers[i].scale.x = size;
1027 | occupiedNodesVis.markers[i].scale.y = size;
1028 | occupiedNodesVis.markers[i].scale.z = size;
1029 | occupiedNodesVis.markers[i].color.r = 0.5;
1030 | occupiedNodesVis.markers[i].color.g = 0.5;
1031 | occupiedNodesVis.markers[i].color.b = 0.5;
1032 | occupiedNodesVis.markers[i].color.a = 1.0;
1033 |
1034 | if (occupiedNodesVis.markers[i].points.size() > 0)
1035 | occupiedNodesVis.markers[i].action = visualization_msgs::msg::Marker::ADD;
1036 | else
1037 | occupiedNodesVis.markers[i].action = visualization_msgs::msg::Marker::DELETE;
1038 |
1039 | if (!rclcpp::ok())
1040 | break;
1041 | }
1042 |
1043 | // Publish it
1044 | octomap_marker_pub_->publish(occupiedNodesVis);
1045 | }
1046 |
1047 | void WorldModeler::filterSingleOutliers(sensor_msgs::msg::LaserScan &laser_scan_msg, std::vector &rngflags)
1048 | {
1049 | int n(laser_scan_msg.ranges.size());
1050 | double thres(laser_scan_msg.range_max / 10.0);
1051 | std::vector zeros;
1052 | for (int i = 1; i < n - 1; i++)
1053 | {
1054 | if ((std::abs(laser_scan_msg.ranges[i - 1] - laser_scan_msg.ranges[i]) > thres) &&
1055 | (std::abs(laser_scan_msg.ranges[i + 1] - laser_scan_msg.ranges[i]) > thres))
1056 | {
1057 | laser_scan_msg.ranges[i] = 0.0;
1058 | zeros.push_back(i);
1059 | }
1060 |
1061 | if (!rclcpp::ok())
1062 | break;
1063 | }
1064 |
1065 | // Delete unnecessary flags
1066 | for (std::vector::reverse_iterator rit = zeros.rbegin();
1067 | rit < zeros.rend(); rit++)
1068 | {
1069 | rngflags.erase(rngflags.begin() + *rit);
1070 |
1071 | if (!rclcpp::ok())
1072 | break;
1073 | }
1074 | }
1075 |
1076 | void WorldModeler::defineSocialGridMap()
1077 | {
1078 | // ! OCTOMAP PREPARATION
1079 |
1080 | grid_map::Position3 min_bound;
1081 | grid_map::Position3 max_bound;
1082 |
1083 | grid_map_.clearAll();
1084 |
1085 | octree_->getMetricMin(min_bound(0), min_bound(1), min_bound(2));
1086 | octree_->getMetricMax(max_bound(0), max_bound(1), max_bound(2));
1087 |
1088 | grid_map::GridMapOctomapConverter::fromOctomap(*octree_, "full", grid_map_, &min_bound, &max_bound);
1089 |
1090 | grid_map_["full"] = 150 * grid_map_["full"];
1091 |
1092 | grid_map::Matrix &full_grid_map = grid_map_["full"];
1093 | grid_map::Matrix &comfort_grid_map = grid_map_["comfort"];
1094 |
1095 | // !SOCIAL AGENTS GRID MAP PREPARATION
1096 |
1097 | for (int i = 0; i < relevant_agent_states_.agent_states.size(); i++)
1098 | {
1099 | grid_map::Position center(relevant_agent_states_.agent_states[i].pose.position.x, relevant_agent_states_.agent_states[i].pose.position.y);
1100 |
1101 | for (grid_map::CircleIterator iterator(grid_map_, center, social_agent_radius_);
1102 | !iterator.isPastEnd(); ++iterator)
1103 | {
1104 | try
1105 | {
1106 | grid_map::Index index(*iterator);
1107 | full_grid_map(index(0), index(1)) = 100;
1108 | }
1109 | catch (const std::out_of_range &oor)
1110 | {
1111 | RCLCPP_ERROR(this->get_logger(), "TRIED TO DEFINE AN AGENT OUT OF RANGE");
1112 | }
1113 | }
1114 |
1115 | for (grid_map::CircleIterator iterator(grid_map_, center, 2.25);
1116 | !iterator.isPastEnd(); ++iterator)
1117 | {
1118 | try
1119 | {
1120 |
1121 | grid_map::Position temp_pos;
1122 |
1123 | grid_map_.getPosition(*iterator, temp_pos);
1124 |
1125 | grid_map::Index index(*iterator);
1126 |
1127 | double last_val = comfort_grid_map(index(0), index(1));
1128 |
1129 | if (std::isnan(last_val))
1130 | {
1131 | last_val = getExtendedPersonalSpace(relevant_agent_states_.agent_states[i], temp_pos);
1132 | }
1133 | else
1134 | {
1135 | last_val += getExtendedPersonalSpace(relevant_agent_states_.agent_states[i], temp_pos);
1136 | }
1137 |
1138 | comfort_grid_map(index(0), index(1)) = last_val;
1139 | }
1140 | catch (const std::out_of_range &oor)
1141 | {
1142 | RCLCPP_ERROR(this->get_logger(), "TRIED TO DEFINE COMFORT OUT OF RANGE");
1143 | }
1144 | }
1145 | }
1146 |
1147 | grid_map_["full"] = full_grid_map;
1148 | grid_map_["comfort"] = comfort_grid_map;
1149 | }
1150 |
1151 | //! Main function
1152 | int main(int argc, char **argv)
1153 | {
1154 | //=======================================================================
1155 | // Override SIGINT handler
1156 | //=======================================================================
1157 | signal(SIGINT, stopNode);
1158 |
1159 | // Init ROS node
1160 | rclcpp::init(argc, argv);
1161 |
1162 | // Spin
1163 | rclcpp::spin(std::make_shared());
1164 |
1165 | rclcpp::shutdown();
1166 |
1167 | // Exit main function without errors
1168 | return 0;
1169 | }
1170 |
--------------------------------------------------------------------------------
/esc_move_base_planning/src/planner/RRTstarMod.cpp:
--------------------------------------------------------------------------------
1 | /*********************************************************************
2 | * Software License Agreement (BSD License)
3 | *
4 | * Copyright (c) 2011, Rice University
5 | * All rights reserved.
6 | *
7 | * Redistribution and use in source and binary forms, with or without
8 | * modification, are permitted provided that the following conditions
9 | * are met:
10 | *
11 | * * Redistributions of source code must retain the above copyright
12 | * notice, this list of conditions and the following disclaimer.
13 | * * Redistributions in binary form must reproduce the above
14 | * copyright notice, this list of conditions and the following
15 | * disclaimer in the documentation and/or other materials provided
16 | * with the distribution.
17 | * * Neither the name of the Rice University nor the names of its
18 | * contributors may be used to endorse or promote products derived
19 | * from this software without specific prior written permission.
20 | *
21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 | * POSSIBILITY OF SUCH DAMAGE.
33 | *********************************************************************/
34 |
35 | /* Authors: Alejandro Perez, Sertac Karaman, Ryan Luna, Luis G. Torres, Ioan Sucan, Javier V Gomez, Jonathan
36 | * Gammell */
37 |
38 | /* Modified by: Juan D. Hernandez */
39 |
40 | #include
41 | #include "ompl/base/goals/GoalSampleableRegion.h"
42 | #include "ompl/tools/config/SelfConfig.h"
43 | #include "ompl/base/objectives/PathLengthOptimizationObjective.h"
44 | #include "ompl/base/Goal.h"
45 | #include "ompl/base/goals/GoalState.h"
46 | #include "ompl/util/GeometricEquations.h"
47 | #include "ompl/base/samplers/InformedStateSampler.h"
48 | #include "ompl/base/samplers/informed/RejectionInfSampler.h"
49 | #include
50 | #include
51 | #include
52 | #include
53 |
54 | ompl::geometric::RRTstarMod::RRTstarMod(const base::SpaceInformationPtr &si)
55 | : base::Planner(si, "RRTstarMod")
56 | , goalBias_(0.05)
57 | , maxDistance_(0.0)
58 | , useKNearest_(true)
59 | , rewireFactor_(1.1)
60 | , k_rrg_(0u)
61 | , r_rrg_(0.0)
62 | , delayCC_(true)
63 | , lastGoalMotion_(nullptr)
64 | , useTreePruning_(false)
65 | , pruneThreshold_(0.05)
66 | , usePrunedMeasure_(false)
67 | , useInformedSampling_(false)
68 | , useRejectionSampling_(false)
69 | , useNewStateRejection_(false)
70 | , useAdmissibleCostToCome_(true)
71 | , numSampleAttempts_(100u)
72 | , bestCost_(std::numeric_limits::quiet_NaN())
73 | , prunedCost_(std::numeric_limits::quiet_NaN())
74 | , prunedMeasure_(0.0)
75 | , iterations_(0u)
76 | {
77 | specs_.approximateSolutions = true;
78 | specs_.optimizingPaths = true;
79 | specs_.canReportIntermediateSolutions = true;
80 |
81 | Planner::declareParam("range", this, &RRTstarMod::setRange, &RRTstarMod::getRange,
82 | "0.:1.:"
83 | "10000.");
84 | Planner::declareParam("goal_bias", this, &RRTstarMod::setGoalBias, &RRTstarMod::getGoalBias,
85 | "0.:.05:"
86 | "1.");
87 | Planner::declareParam("rewire_factor", this, &RRTstarMod::setRewireFactor,
88 | &RRTstarMod::getRewireFactor, "1.0:0.01:2.0");
89 | Planner::declareParam("use_k_nearest", this, &RRTstarMod::setKNearest, &RRTstarMod::getKNearest,
90 | "0,1");
91 | Planner::declareParam("delay_collision_checking", this, &RRTstarMod::setDelayCC,
92 | &RRTstarMod::getDelayCC, "0,1");
93 | Planner::declareParam("tree_pruning", this, &RRTstarMod::setTreePruning,
94 | &RRTstarMod::getTreePruning, "0,1");
95 | Planner::declareParam("prune_threshold", this, &RRTstarMod::setPruneThreshold,
96 | &RRTstarMod::getPruneThreshold, "0.:.01:1.");
97 | Planner::declareParam("pruned_measure", this, &RRTstarMod::setPrunedMeasure,
98 | &RRTstarMod::getPrunedMeasure, "0,1");
99 | Planner::declareParam("informed_sampling", this, &RRTstarMod::setInformedSampling,
100 | &RRTstarMod::getInformedSampling, "0,1");
101 | Planner::declareParam("sample_rejection", this, &RRTstarMod::setSampleRejection,
102 | &RRTstarMod::getSampleRejection, "0,1");
103 | Planner::declareParam("new_state_rejection", this, &RRTstarMod::setNewStateRejection,
104 | &RRTstarMod::getNewStateRejection, "0,1");
105 | Planner::declareParam("use_admissible_heuristic", this, &RRTstarMod::setAdmissibleCostToCome,
106 | &RRTstarMod::getAdmissibleCostToCome, "0,1");
107 | Planner::declareParam("focus_search", this, &RRTstarMod::setFocusSearch,
108 | &RRTstarMod::getFocusSearch, "0,1");
109 | Planner::declareParam("number_sampling_attempts", this, &RRTstarMod::setNumSamplingAttempts,
110 | &RRTstarMod::getNumSamplingAttempts, "10:10:100000");
111 |
112 | addPlannerProgressProperty("iterations INTEGER", std::bind(&RRTstarMod::numIterationsProperty, this));
113 | addPlannerProgressProperty("best cost REAL", std::bind(&RRTstarMod::bestCostProperty, this));
114 | }
115 |
116 | ompl::geometric::RRTstarMod::~RRTstarMod()
117 | {
118 | freeMemory();
119 | }
120 |
121 | void ompl::geometric::RRTstarMod::setup()
122 | {
123 | Planner::setup();
124 | tools::SelfConfig sc(si_, getName());
125 | sc.configurePlannerRange(maxDistance_);
126 | if (!si_->getStateSpace()->hasSymmetricDistance() || !si_->getStateSpace()->hasSymmetricInterpolate())
127 | {
128 | OMPL_WARN("%s requires a state space with symmetric distance and symmetric interpolation.",
129 | getName().c_str());
130 | }
131 |
132 | if (!nn_)
133 | nn_.reset(tools::SelfConfig::getDefaultNearestNeighbors(this));
134 | nn_->setDistanceFunction(
135 | std::bind(&RRTstarMod::distanceFunction, this, std::placeholders::_1, std::placeholders::_2));
136 |
137 | // Setup optimization objective
138 | //
139 | // If no optimization objective was specified, then default to
140 | // optimizing path length as computed by the distance() function
141 | // in the state space.
142 | if (pdef_)
143 | {
144 | if (pdef_->hasOptimizationObjective())
145 | opt_ = pdef_->getOptimizationObjective();
146 | else
147 | {
148 | OMPL_INFORM("%s: No optimization objective specified. Defaulting to optimizing path length for "
149 | "the allowed planning time.",
150 | getName().c_str());
151 | opt_.reset(new base::PathLengthOptimizationObjective(si_));
152 |
153 | // Store the new objective in the problem def'n
154 | pdef_->setOptimizationObjective(opt_);
155 | }
156 | // Set the bestCost_ and prunedCost_ as infinite
157 | bestCost_ = opt_->infiniteCost();
158 | prunedCost_ = opt_->infiniteCost();
159 | }
160 | else
161 | {
162 | OMPL_INFORM("%s: problem definition is not set, deferring setup completion...", getName().c_str());
163 | setup_ = false;
164 | }
165 |
166 | // Get the measure of the entire space:
167 | prunedMeasure_ = si_->getSpaceMeasure();
168 |
169 | // Calculate some constants:
170 | calculateRewiringLowerBounds();
171 | }
172 |
173 | void ompl::geometric::RRTstarMod::clear()
174 | {
175 | setup_ = false;
176 | Planner::clear();
177 | sampler_.reset();
178 | infSampler_.reset();
179 | freeMemory();
180 | if (nn_)
181 | nn_->clear();
182 |
183 | lastGoalMotion_ = nullptr;
184 | goalMotions_.clear();
185 | startMotions_.clear();
186 |
187 | iterations_ = 0;
188 | bestCost_ = base::Cost(std::numeric_limits::quiet_NaN());
189 | prunedCost_ = base::Cost(std::numeric_limits::quiet_NaN());
190 | prunedMeasure_ = 0.0;
191 | }
192 |
193 | ompl::base::PlannerStatus ompl::geometric::RRTstarMod::solve(const base::PlannerTerminationCondition &ptc)
194 | {
195 | checkValidity();
196 | base::Goal *goal = pdef_->getGoal().get();
197 | base::GoalSampleableRegion *goal_s = dynamic_cast(goal);
198 |
199 | bool symCost = opt_->isSymmetric();
200 |
201 | // Check if there are more starts
202 | if (pis_.haveMoreStartStates() == true)
203 | {
204 | // There are, add them
205 | while (const base::State *st = pis_.nextStart())
206 | {
207 | Motion *motion = new Motion(si_);
208 | si_->copyState(motion->state, st);
209 | motion->cost = opt_->identityCost();
210 | nn_->add(motion);
211 | startMotions_.push_back(motion);
212 | }
213 |
214 | // And assure that, if we're using an informed sampler, it's reset
215 | infSampler_.reset();
216 | }
217 | // No else
218 |
219 | if (nn_->size() == 0)
220 | {
221 | OMPL_ERROR("%s: There are no valid initial states!", getName().c_str());
222 | return base::PlannerStatus::INVALID_START;
223 | }
224 |
225 | // Allocate a sampler if necessary
226 | if (!sampler_ && !infSampler_)
227 | {
228 | allocSampler();
229 | }
230 |
231 | OMPL_INFORM("%s: Starting planning with %u states already in datastructure", getName().c_str(),
232 | nn_->size());
233 |
234 | if ((useTreePruning_ || useRejectionSampling_ || useInformedSampling_ || useNewStateRejection_) &&
235 | !si_->getStateSpace()->isMetricSpace())
236 | OMPL_WARN("%s: The state space (%s) is not metric and as a result the optimization objective may not "
237 | "satisfy the triangle inequality. "
238 | "You may need to disable pruning or rejection.",
239 | getName().c_str(), si_->getStateSpace()->getName().c_str());
240 |
241 | const base::ReportIntermediateSolutionFn intermediateSolutionCallback =
242 | pdef_->getIntermediateSolutionCallback();
243 |
244 | Motion *solution = lastGoalMotion_;
245 |
246 | Motion *approximation = nullptr;
247 | double approximatedist = std::numeric_limits::infinity();
248 | bool sufficientlyShort = false;
249 |
250 | Motion *rmotion = new Motion(si_);
251 | base::State *rstate = rmotion->state;
252 | base::State *xstate = si_->allocState();
253 |
254 | std::vector nbh;
255 |
256 | std::vector costs;
257 | std::vector incCosts;
258 | std::vector sortedCostIndices;
259 |
260 | std::vector valid;
261 | unsigned int rewireTest = 0;
262 | unsigned int statesGenerated = 0;
263 |
264 | if (solution)
265 | OMPL_INFORM("%s: Starting planning with existing solution of cost %.5f", getName().c_str(),
266 | solution->cost.value());
267 |
268 | if (useKNearest_)
269 | OMPL_INFORM("%s: Initial k-nearest value of %u", getName().c_str(),
270 | (unsigned int)std::ceil(k_rrg_ * log((double)(nn_->size() + 1u))));
271 | else
272 | OMPL_INFORM("%s: Initial rewiring radius of %.2f", getName().c_str(),
273 | std::min(maxDistance_,
274 | r_rrg_ * std::pow(log((double)(nn_->size() + 1u)) / ((double)(nn_->size() + 1u)),
275 | 1 / (double)(si_->getStateDimension()))));
276 |
277 | // our functor for sorting nearest neighbors
278 | CostIndexCompare compareFn(costs, *opt_);
279 |
280 | while (ptc == false)
281 | {
282 | iterations_++;
283 |
284 | // sample random state (with goal biasing)
285 | // Goal samples are only sampled until maxSampleCount() goals are in the tree, to prohibit duplicate
286 | // goal states.
287 | if (goal_s && goalMotions_.size() < goal_s->maxSampleCount() && rng_.uniform01() < goalBias_ &&
288 | goal_s->canSample())
289 | goal_s->sampleGoal(rstate);
290 | else
291 | {
292 | // Attempt to generate a sample, if we fail (e.g., too many rejection attempts), skip the
293 | // remainder of this loop and return to try again
294 | if (!sampleUniform(rstate))
295 | continue;
296 | }
297 |
298 | // find closest state in the tree
299 | Motion *nmotion = nn_->nearest(rmotion);
300 |
301 | if (intermediateSolutionCallback && si_->equalStates(nmotion->state, rstate))
302 | continue;
303 |
304 | base::State *dstate = rstate;
305 |
306 | // find state to add to the tree
307 | double d = si_->distance(nmotion->state, rstate);
308 | if (d > maxDistance_)
309 | {
310 | si_->getStateSpace()->interpolate(nmotion->state, rstate, maxDistance_ / d, xstate);
311 | dstate = xstate;
312 | }
313 |
314 | // Check if the motion between the nearest state and the state to add is valid
315 | if (si_->checkMotion(nmotion->state, dstate))
316 | {
317 | // create a motion
318 | Motion *motion = new Motion(si_);
319 | si_->copyState(motion->state, dstate);
320 | motion->parent = nmotion;
321 | motion->incCost = opt_->motionCost(nmotion->state, motion->state);
322 | motion->cost = opt_->combineCosts(nmotion->cost, motion->incCost);
323 |
324 | // Find nearby neighbors of the new motion
325 | getNeighbors(motion, nbh);
326 |
327 | rewireTest += nbh.size();
328 | ++statesGenerated;
329 |
330 | // cache for distance computations
331 | //
332 | // Our cost caches only increase in size, so they're only
333 | // resized if they can't fit the current neighborhood
334 | if (costs.size() < nbh.size())
335 | {
336 | costs.resize(nbh.size());
337 | incCosts.resize(nbh.size());
338 | sortedCostIndices.resize(nbh.size());
339 | }
340 |
341 | // cache for motion validity (only useful in a symmetric space)
342 | //
343 | // Our validity caches only increase in size, so they're
344 | // only resized if they can't fit the current neighborhood
345 | if (valid.size() < nbh.size())
346 | valid.resize(nbh.size());
347 | std::fill(valid.begin(), valid.begin() + nbh.size(), 0);
348 |
349 | // Finding the nearest neighbor to connect to
350 | // By default, neighborhood states are sorted by cost, and collision checking
351 | // is performed in increasing order of cost
352 | if (delayCC_)
353 | {
354 | // calculate all costs and distances
355 | for (std::size_t i = 0; i < nbh.size(); ++i)
356 | {
357 | incCosts[i] = opt_->motionCost(nbh[i]->state, motion->state);
358 | costs[i] = opt_->combineCosts(nbh[i]->cost, incCosts[i]);
359 | }
360 |
361 | // sort the nodes
362 | //
363 | // we're using index-value pairs so that we can get at
364 | // original, unsorted indices
365 | for (std::size_t i = 0; i < nbh.size(); ++i)
366 | sortedCostIndices[i] = i;
367 | std::sort(sortedCostIndices.begin(), sortedCostIndices.begin() + nbh.size(), compareFn);
368 |
369 | // collision check until a valid motion is found
370 | //
371 | // ASYMMETRIC CASE: it's possible that none of these
372 | // neighbors are valid. This is fine, because motion
373 | // already has a connection to the tree through
374 | // nmotion (with populated cost fields!).
375 | for (std::vector::const_iterator i = sortedCostIndices.begin();
376 | i != sortedCostIndices.begin() + nbh.size(); ++i)
377 | {
378 | if (nbh[*i] == nmotion || si_->checkMotion(nbh[*i]->state, motion->state))
379 | {
380 | motion->incCost = incCosts[*i];
381 | motion->cost = costs[*i];
382 | motion->parent = nbh[*i];
383 | valid[*i] = 1;
384 | break;
385 | }
386 | else
387 | valid[*i] = -1;
388 | }
389 | }
390 | else // if not delayCC
391 | {
392 | motion->incCost = opt_->motionCost(nmotion->state, motion->state);
393 | motion->cost = opt_->combineCosts(nmotion->cost, motion->incCost);
394 | // find which one we connect the new state to
395 | for (std::size_t i = 0; i < nbh.size(); ++i)
396 | {
397 | if (nbh[i] != nmotion)
398 | {
399 | incCosts[i] = opt_->motionCost(nbh[i]->state, motion->state);
400 | costs[i] = opt_->combineCosts(nbh[i]->cost, incCosts[i]);
401 | if (opt_->isCostBetterThan(costs[i], motion->cost))
402 | {
403 | if (si_->checkMotion(nbh[i]->state, motion->state))
404 | {
405 | motion->incCost = incCosts[i];
406 | motion->cost = costs[i];
407 | motion->parent = nbh[i];
408 | valid[i] = 1;
409 | }
410 | else
411 | valid[i] = -1;
412 | }
413 | }
414 | else
415 | {
416 | incCosts[i] = motion->incCost;
417 | costs[i] = motion->cost;
418 | valid[i] = 1;
419 | }
420 | }
421 | }
422 |
423 | if (useNewStateRejection_)
424 | {
425 | if (opt_->isCostBetterThan(solutionHeuristic(motion), bestCost_))
426 | {
427 | nn_->add(motion);
428 | motion->parent->children.push_back(motion);
429 | }
430 | else // If the new motion does not improve the best cost it is ignored.
431 | {
432 | si_->freeState(motion->state);
433 | delete motion;
434 | continue;
435 | }
436 | }
437 | else
438 | {
439 | // add motion to the tree
440 | nn_->add(motion);
441 | motion->parent->children.push_back(motion);
442 | }
443 |
444 | bool checkForSolution = false;
445 | for (std::size_t i = 0; i < nbh.size(); ++i)
446 | {
447 | if (nbh[i] != motion->parent)
448 | {
449 | base::Cost nbhIncCost;
450 | if (symCost)
451 | nbhIncCost = incCosts[i];
452 | else
453 | nbhIncCost = opt_->motionCost(motion->state, nbh[i]->state);
454 | base::Cost nbhNewCost = opt_->combineCosts(motion->cost, nbhIncCost);
455 | if (opt_->isCostBetterThan(nbhNewCost, nbh[i]->cost))
456 | {
457 | bool motionValid;
458 | if (valid[i] == 0)
459 | {
460 | motionValid = si_->checkMotion(motion->state, nbh[i]->state);
461 | }
462 | else
463 | {
464 | motionValid = (valid[i] == 1);
465 | }
466 |
467 | if (motionValid)
468 | {
469 | // Remove this node from its parent list
470 | removeFromParent(nbh[i]);
471 |
472 | // Add this node to the new parent
473 | nbh[i]->parent = motion;
474 | nbh[i]->incCost = nbhIncCost;
475 | nbh[i]->cost = nbhNewCost;
476 | nbh[i]->parent->children.push_back(nbh[i]);
477 |
478 | // Update the costs of the node's children
479 | updateChildCosts(nbh[i]);
480 |
481 | checkForSolution = true;
482 | }
483 | }
484 | }
485 | }
486 |
487 | // Add the new motion to the goalMotion_ list, if it satisfies the goal
488 | double distanceFromGoal;
489 | if (goal->isSatisfied(motion->state, &distanceFromGoal))
490 | {
491 | goalMotions_.push_back(motion);
492 | checkForSolution = true;
493 | }
494 |
495 | // Checking for solution or iterative improvement
496 | if (checkForSolution)
497 | {
498 | bool updatedSolution = false;
499 |
500 | // if (solution)
501 | // OMPL_INFORM("%s: Best known cost of %.2f and distanceFromGoal %.2f",
502 | // getName().c_str(),
503 | // bestCost_, bestDistFromGoal_);
504 |
505 | for (size_t i = 0; i < goalMotions_.size(); ++i)
506 | {
507 | goal->isSatisfied(goalMotions_[i]->state, &distanceFromGoal);
508 | base::Cost goalMotionCost(goalMotions_[i]->cost.value() +
509 | distanceFromGoal); // TODO: use terminal cost
510 |
511 | // OMPL_INFORM("%s: Alternative solution with a path length of % .2f
512 | // and distanceFromGoal "
513 | // "of %.2f, total: %.2f",
514 | // getName().c_str(), goalMotions_[i]->cost,
515 | // distanceFromGoal,
516 | // goalMotions_[i]->cost.value() + distanceFromGoal);
517 |
518 | if (goalMotionCost.value() + 0.1 <= bestCost_.value() &&
519 | distanceFromGoal < bestDistFromGoal_)
520 | {
521 | if (opt_->isFinite(bestCost_) == false)
522 | {
523 | OMPL_INFORM("%s: Found an initial solution with a cost of %.2f in %u iterations "
524 | "(%u vertices in the graph)",
525 | getName().c_str(), goalMotions_[i]->cost.value(), iterations_,
526 | nn_->size());
527 | }
528 | solution = goalMotions_[i];
529 | bestCost_ =
530 | base::Cost(solution->cost.value() + distanceFromGoal); // TODO: use terminal cost
531 | bestDistFromGoal_ = distanceFromGoal;
532 | updatedSolution = true;
533 | }
534 |
535 | sufficientlyShort = opt_->isSatisfied(goalMotions_[i]->cost);
536 |
537 | if (sufficientlyShort)
538 | {
539 | solution = goalMotions_[i];
540 | break;
541 | }
542 | else if (!solution || (goalMotionCost.value() <= bestCost_.value() &&
543 | distanceFromGoal < bestDistFromGoal_))
544 | {
545 | solution = goalMotions_[i];
546 | bestCost_ = goalMotions_[i]->cost;
547 | bestDistFromGoal_ = distanceFromGoal;
548 | updatedSolution = true;
549 | }
550 | }
551 |
552 | if (updatedSolution)
553 | {
554 | if (useTreePruning_)
555 | {
556 | pruneTree(bestCost_);
557 | }
558 |
559 | if (intermediateSolutionCallback)
560 | {
561 | std::vector spath;
562 | Motion *intermediate_solution =
563 | solution->parent; // Do not include goal state to simplify code.
564 |
565 | // Push back until we find the start, but not the start itself
566 | while (intermediate_solution->parent != nullptr)
567 | {
568 | spath.push_back(intermediate_solution->state);
569 | intermediate_solution = intermediate_solution->parent;
570 | }
571 |
572 | intermediateSolutionCallback(this, spath, bestCost_);
573 | }
574 | }
575 | }
576 |
577 | // Checking for approximate solution (closest state found to the goal)
578 | if (goalMotions_.size() == 0 && distanceFromGoal < approximatedist)
579 | {
580 | approximation = motion;
581 | approximatedist = distanceFromGoal;
582 | }
583 | }
584 |
585 | // terminate if a sufficient solution is found
586 | if (solution && sufficientlyShort)
587 | break;
588 | }
589 |
590 | bool approximate = (solution == nullptr);
591 | bool addedSolution = false;
592 | if (approximate)
593 | solution = approximation;
594 | else
595 | lastGoalMotion_ = solution;
596 |
597 | if (solution != nullptr)
598 | {
599 | ptc.terminate();
600 | // construct the solution path
601 | std::vector mpath;
602 | while (solution != nullptr)
603 | {
604 | mpath.push_back(solution);
605 | solution = solution->parent;
606 | }
607 |
608 | // set the solution path
609 | PathGeometric *geoPath = new PathGeometric(si_);
610 | for (int i = mpath.size() - 1; i >= 0; --i)
611 | geoPath->append(mpath[i]->state);
612 |
613 | base::PathPtr path(geoPath);
614 | // Add the solution path.
615 | base::PlannerSolution psol(path);
616 | psol.setPlannerName(getName());
617 | if (approximate)
618 | psol.setApproximate(approximatedist);
619 | // Does the solution satisfy the optimization objective?
620 | psol.setOptimized(opt_, bestCost_, sufficientlyShort);
621 | pdef_->addSolutionPath(psol);
622 |
623 | addedSolution = true;
624 | }
625 |
626 | si_->freeState(xstate);
627 | if (rmotion->state)
628 | si_->freeState(rmotion->state);
629 | delete rmotion;
630 |
631 | OMPL_INFORM("%s: Created %u new states. Checked %u rewire options. %u goal states in tree. Final "
632 | "solution cost %.3f",
633 | getName().c_str(), statesGenerated, rewireTest, goalMotions_.size(), bestCost_.value());
634 |
635 | return base::PlannerStatus(addedSolution, approximate);
636 | }
637 |
638 | void ompl::geometric::RRTstarMod::getNeighbors(Motion *motion, std::vector &nbh) const
639 | {
640 | double cardDbl = static_cast(nn_->size() + 1u);
641 | if (useKNearest_)
642 | {
643 | //- k-nearest RRT*
644 | unsigned int k = std::ceil(k_rrg_ * log(cardDbl));
645 | nn_->nearestK(motion, k, nbh);
646 | }
647 | else
648 | {
649 | double r =
650 | std::min(maxDistance_, r_rrg_ * std::pow(log(cardDbl) / cardDbl,
651 | 1 / static_cast(si_->getStateDimension())));
652 | nn_->nearestR(motion, r, nbh);
653 | }
654 | }
655 |
656 | void ompl::geometric::RRTstarMod::removeFromParent(Motion *m)
657 | {
658 | for (std::vector::iterator it = m->parent->children.begin(); it != m->parent->children.end();
659 | ++it)
660 | {
661 | if (*it == m)
662 | {
663 | m->parent->children.erase(it);
664 | break;
665 | }
666 | }
667 | }
668 |
669 | void ompl::geometric::RRTstarMod::updateChildCosts(Motion *m)
670 | {
671 | for (std::size_t i = 0; i < m->children.size(); ++i)
672 | {
673 | m->children[i]->cost = opt_->combineCosts(m->cost, m->children[i]->incCost);
674 | updateChildCosts(m->children[i]);
675 | }
676 | }
677 |
678 | void ompl::geometric::RRTstarMod::freeMemory()
679 | {
680 | if (nn_)
681 | {
682 | std::vector motions;
683 | nn_->list(motions);
684 | for (std::size_t i = 0; i < motions.size(); ++i)
685 | {
686 | if (motions[i]->state)
687 | si_->freeState(motions[i]->state);
688 | delete motions[i];
689 | }
690 | }
691 | }
692 |
693 | void ompl::geometric::RRTstarMod::getPlannerData(base::PlannerData &data) const
694 | {
695 | Planner::getPlannerData(data);
696 |
697 | std::vector motions;
698 | if (nn_)
699 | nn_->list(motions);
700 |
701 | if (lastGoalMotion_)
702 | data.addGoalVertex(base::PlannerDataVertex(lastGoalMotion_->state));
703 |
704 | for (std::size_t i = 0; i < motions.size(); ++i)
705 | {
706 | if (motions[i]->parent == nullptr)
707 | data.addStartVertex(base::PlannerDataVertex(motions[i]->state));
708 | else
709 | data.addEdge(base::PlannerDataVertex(motions[i]->parent->state),
710 | base::PlannerDataVertex(motions[i]->state));
711 | }
712 | }
713 |
714 | int ompl::geometric::RRTstarMod::pruneTree(const base::Cost &pruneTreeCost)
715 | {
716 | // Variable
717 | // The percent improvement (expressed as a [0,1] fraction) in cost
718 | double fracBetter;
719 | // The number pruned
720 | int numPruned = 0;
721 |
722 | if (opt_->isFinite(prunedCost_))
723 | {
724 | fracBetter = std::abs((pruneTreeCost.value() - prunedCost_.value()) / prunedCost_.value());
725 | }
726 | else
727 | {
728 | fracBetter = 1.0;
729 | }
730 |
731 | if (fracBetter > pruneThreshold_)
732 | {
733 | // We are only pruning motions if they, AND all descendents, have a estimated cost greater than
734 | // pruneTreeCost
735 | // The easiest way to do this is to find leaves that should be pruned and ascend up their ancestry
736 | // until a motion is found that is kept.
737 | // To avoid making an intermediate copy of the NN structure, we process the tree by descending down
738 | // from the start(s).
739 | // In the first pass, all Motions with a cost below pruneTreeCost, or Motion's with children with
740 | // costs below pruneTreeCost are added to the replacement NN structure,
741 | // while all other Motions are stored as either a 'leaf' or 'chain' Motion. After all the leaves are
742 | // disconnected and deleted, we check
743 | // if any of the the chain Motions are now leaves, and repeat that process until done.
744 | // This avoids (1) copying the NN structure into an intermediate variable and (2) the use of the
745 | // expensive NN::remove() method.
746 |
747 | // Variable
748 | // The queue of Motions to process:
749 | std::queue> motionQueue;
750 | // The list of leaves to prune
751 | std::queue> leavesToPrune;
752 | // The list of chain vertices to recheck after pruning
753 | std::list chainsToRecheck;
754 |
755 | // Clear the NN structure:
756 | nn_->clear();
757 |
758 | // Put all the starts into the NN structure and their children into the queue:
759 | // We do this so that start states are never pruned.
760 | for (unsigned int i = 0u; i < startMotions_.size(); ++i)
761 | {
762 | // Add to the NN
763 | nn_->add(startMotions_.at(i));
764 |
765 | // Add their children to the queue:
766 | addChildrenToList(&motionQueue, startMotions_.at(i));
767 | }
768 |
769 | while (motionQueue.empty() == false)
770 | {
771 | // Test, can the current motion ever provide a better solution?
772 | if (keepCondition(motionQueue.front(), pruneTreeCost))
773 | {
774 | // Yes it can, so it definitely won't be pruned
775 | // Add it back into the NN structure
776 | nn_->add(motionQueue.front());
777 |
778 | // Add it's children to the queue
779 | addChildrenToList(&motionQueue, motionQueue.front());
780 | }
781 | else
782 | {
783 | // No it can't, but does it have children?
784 | if (motionQueue.front()->children.empty() == false)
785 | {
786 | // Yes it does.
787 | // We can minimize the number of intermediate chain motions if we check their children
788 | // If any of them won't be pruned, then this motion won't either. This intuitively seems
789 | // like a nice balance between following the descendents forever.
790 |
791 | // Variable
792 | // Whether the children are definitely to be kept.
793 | bool keepAChild = false;
794 |
795 | // Find if any child is definitely not being pruned.
796 | for (unsigned int i = 0u; keepAChild == false && i < motionQueue.front()->children.size();
797 | ++i)
798 | {
799 | // Test if the child can ever provide a better solution
800 | keepAChild = keepCondition(motionQueue.front()->children.at(i), pruneTreeCost);
801 | }
802 |
803 | // Are we *definitely* keeping any of the children?
804 | if (keepAChild)
805 | {
806 | // Yes, we are, so we are not pruning this motion
807 | // Add it back into the NN structure.
808 | nn_->add(motionQueue.front());
809 | }
810 | else
811 | {
812 | // No, we aren't. This doesn't mean we won't though
813 | // Move this Motion to the temporary list
814 | chainsToRecheck.push_back(motionQueue.front());
815 | }
816 |
817 | // Either way. add it's children to the queue
818 | addChildrenToList(&motionQueue, motionQueue.front());
819 | }
820 | else
821 | {
822 | // No, so we will be pruning this motion:
823 | leavesToPrune.push(motionQueue.front());
824 | }
825 | }
826 |
827 | // Pop the iterator, std::list::erase returns the next iterator
828 | motionQueue.pop();
829 | }
830 |
831 | // We now have a list of Motions to definitely remove, and a list of Motions to recheck
832 | // Iteratively check the two lists until there is nothing to to remove
833 | while (leavesToPrune.empty() == false)
834 | {
835 | // First empty the leave-to-prune
836 | while (leavesToPrune.empty() == false)
837 | {
838 | // Remove the leaf from its parent
839 | removeFromParent(leavesToPrune.front());
840 |
841 | // Erase the actual motion
842 | // First free the state
843 | si_->freeState(leavesToPrune.front()->state);
844 |
845 | // then delete the pointer
846 | delete leavesToPrune.front();
847 |
848 | // And finally remove it from the list, erase returns the next iterator
849 | leavesToPrune.pop();
850 |
851 | // Update our counter
852 | ++numPruned;
853 | }
854 |
855 | // Now, we need to go through the list of chain vertices and see if any are now leaves
856 | std::list::iterator mIter = chainsToRecheck.begin();
857 | while (mIter != chainsToRecheck.end())
858 | {
859 | // Is the Motion a leaf?
860 | if ((*mIter)->children.empty() == true)
861 | {
862 | // It is, add to the removal queue
863 | leavesToPrune.push(*mIter);
864 |
865 | // Remove from this queue, getting the next
866 | mIter = chainsToRecheck.erase(mIter);
867 | }
868 | else
869 | {
870 | // Is isn't, skip to the next
871 | ++mIter;
872 | }
873 | }
874 | }
875 |
876 | // Now finally add back any vertices left in chainsToReheck.
877 | // These are chain vertices that have descendents that we want to keep
878 | for (std::list::const_iterator mIter = chainsToRecheck.begin();
879 | mIter != chainsToRecheck.end(); ++mIter)
880 | {
881 | // Add the motion back to the NN struct:
882 | nn_->add(*mIter);
883 | }
884 |
885 | // All done pruning.
886 | // Update the cost at which we've pruned:
887 | prunedCost_ = pruneTreeCost;
888 |
889 | // And if we're using the pruned measure, the measure to which we've pruned
890 | if (usePrunedMeasure_)
891 | {
892 | prunedMeasure_ = infSampler_->getInformedMeasure(prunedCost_);
893 |
894 | if (useKNearest_ == false)
895 | {
896 | calculateRewiringLowerBounds();
897 | }
898 | }
899 | // No else, prunedMeasure_ is the si_ measure by default.
900 | }
901 |
902 | return numPruned;
903 | }
904 |
905 | void ompl::geometric::RRTstarMod::addChildrenToList(std::queue> *motionList,
906 | Motion *motion)
907 | {
908 | for (unsigned int j = 0u; j < motion->children.size(); ++j)
909 | {
910 | motionList->push(motion->children.at(j));
911 | }
912 | }
913 |
914 | bool ompl::geometric::RRTstarMod::keepCondition(const Motion *motion, const base::Cost &threshold) const
915 | {
916 | // We keep if the cost-to-come-heuristic of motion is <= threshold, by checking
917 | // if (!threshold < heuristic), as if b is not better than a, then a is better than, or equal to, b
918 | return !opt_->isCostBetterThan(threshold, solutionHeuristic(motion));
919 | }
920 |
921 | ompl::base::Cost ompl::geometric::RRTstarMod::solutionHeuristic(const Motion *motion) const
922 | {
923 | base::Cost costToCome;
924 | if (useAdmissibleCostToCome_)
925 | {
926 | // Start with infinite cost
927 | costToCome = opt_->infiniteCost();
928 |
929 | // Find the min from each start
930 | for (unsigned int i = 0u; i < startMotions_.size(); ++i)
931 | {
932 | costToCome = opt_->betterCost(costToCome, opt_->motionCost(startMotions_.at(i)->state,
933 | motion->state)); // lower-bounding
934 | // cost from the
935 | // start to the state
936 | }
937 | }
938 | else
939 | {
940 | costToCome = motion->cost; // current cost from the state to the goal
941 | }
942 |
943 | const base::Cost costToGo = opt_->costToGo(
944 | motion->state, pdef_->getGoal().get()); // lower-bounding cost from the state to the goal
945 | return opt_->combineCosts(costToCome, costToGo); // add the two costs
946 | }
947 |
948 | void ompl::geometric::RRTstarMod::setTreePruning(const bool prune)
949 | {
950 | if (static_cast(opt_) == true)
951 | {
952 | if (opt_->hasCostToGoHeuristic() == false)
953 | {
954 | OMPL_INFORM("%s: No cost-to-go heuristic set. Informed techniques will not work well.",
955 | getName().c_str());
956 | }
957 | }
958 |
959 | // If we just disabled tree pruning, but we wee using prunedMeasure, we need to disable that as it
960 | // required myself
961 | if (prune == false && getPrunedMeasure() == true)
962 | {
963 | setPrunedMeasure(false);
964 | }
965 |
966 | // Store
967 | useTreePruning_ = prune;
968 | }
969 |
970 | void ompl::geometric::RRTstarMod::setPrunedMeasure(bool informedMeasure)
971 | {
972 | if (static_cast