Custom Controller
앞의 포스팅에서 말했듯이 nav2에서는 controller가 plugin으로 작동하고 있어서 interface만 맞춰서 plugin을 만들면 나만의 Controller를 적용 할 수 있다.
참고
https://navigation.ros.org/plugin_tutorials/docs/writing_new_nav2controller_plugin.html
Writing a New Controller Plugin — Navigation 2 1.0.0 documentation
2- Exporting the controller plugin Now that we have created our custom controller, we need to export our controller plugin so that it would be visible to the controller server. Plugins are loaded at runtime, and if they are not visible, then our controller
navigation.ros.org
1. Package 생성
먼저 controller를 만들 package를 생성해 준다.
ros2 pkg create --build-type ament_cmake custom_controller
2. header file 작성 (custom_controller.hpp)
#ifndef CUSTOM_CONTROLLER__CUSTOM_CONTROLLER_HPP_
#define CUSTOM_CONTROLLER__CUSTOM_CONTROLLER_HPP_
#include <nav2_core/controller.hpp>
#include <nav_msgs/msg/path.hpp>
#include <std_msgs/msg/string.hpp>
namespace custom_controller
{
class CustomController : public nav2_core::Controller
{
public:
CustomController();
void configure(
const rclcpp_lifecycle::LifecycleNode::SharedPtr &parents,
std::string name, const std::shared_ptr<tf2_ros::Buffer> &tf,
const std::shared_ptr<nav2_costmap_2d::Costmap2DROS> &) override;
void cleanup() override;
void activate() override;
void deactivate() override;
void setPlan(const nav_msgs::msg::Path & path) override;
geometry_msgs::msg::TwistStamped computeVelocityCommands(
const geometry_msgs::msg::PoseStamped & pose,
const geometry_msgs::msg::Twist & velocity) override;
private:
rclcpp_lifecycle::LifecycleNode::SharedPtr node_;
std::shared_ptr<tf2_ros::Buffer> tf_;
};
} // namespace custom_controller
#endif
- configure
- ros2 lifecycle node에서 configure시 넘어오는 paramter들이다.
- node interface , plugin id name, tf, costmap ros가 넘어오게 된다.
- node intereface
- publish나 subscribe를 하고 싶을 때 사용한다.
- paramter를 받아오는데도 사용한다.
- plugin id name
- plugin id 이름이 넘어온다. Debuging용으로 사용하자
- tf
- 로봇의 좌표를 얻거나 여러 다른 좌표 계산을 할 때 사용하자.
- costmap
- map & 장애물 정보가 2D array로 들어온다.
- collision이나 path를 계산 할 때 사용하자.
- cleanup
- ros2 lifecycle node의 인터페이스로, plugin을 초기화하는 작동들을 넣어주자
- activate
- ros2 lifecycle node의 인터페이스로, plugin이 작동 시작하게 하기 위한 절차들을 넣어주자.
- configure에서 rclcpp_lifecycle::LifecyclePublisher를 생성했다면 여기서 꼭 activate를 해주자.
- 예시
void CustomController::configure( const rclcpp_lifecycle::LifecycleNode::SharedPtr &parents, std::string name, const std::shared_ptr<tf2_ros::Buffer> &tf, const std::shared_ptr<nav2_costmap_2d::Costmap2DROS> &) { node_ = parents; publisher_ = node_->create_publisher<std_msgs::msg::String>("topic", 10); } void CustomController::activate() { publisher_->on_activate(); }
- deactivate
- activate 반대
- setPlan
- local planner에게 global plan을 다시 setting한다. (최초와 중간이 같음)
- computeVelocityCommands
- 현재 로봇 위치와 이전 로봇의 속도가 주어진다.
- set 된 global plan과 로봇위치, 이전 속도를 가지고 다음으로 로봇이 움직여야 할 cmd_vel을 계산해서 주면 된다.
3. Source file 작성
#include <custom_controller/custom_contorller.hpp>
namespace custom_controller
{
CustomController::CustomController()
: nav2_core::Controller()
{}
void
CustomController::configure(
const rclcpp_lifecycle::LifecycleNode::SharedPtr &parents,
std::string name, const std::shared_ptr<tf2_ros::Buffer> &tf,
const std::shared_ptr<nav2_costmap_2d::Costmap2DROS> &)
{
node_ = parents;
tf_ = tf;
}
void
CustomController::cleanup()
{}
void
CustomController::activate()
{
}
void
CustomController::deactivate()
{}
void
CustomController::setPlan(const nav_msgs::msg::Path & path)
{}
geometry_msgs::msg::TwistStamped
CustomController::computeVelocityCommands(
const geometry_msgs::msg::PoseStamped & pose,
const geometry_msgs::msg::Twist & velocity)
{
return geometry_msgs::msg::TwistStamped();
}
} // namespace custom_cotroller
#include "pluginlib/class_list_macros.hpp"
PLUGINLIB_EXPORT_CLASS(custom_cotroller::CustomController, nav2_core::Controller)
!!! 지금은 plugin만드는 법만 설명하니까 세부 구현체들은 넘어간다.
4. CMakeLists.txt 작성
cmake_minimum_required(VERSION 3.5)
project(custom_controller)
# Default to C99
if(NOT CMAKE_C_STANDARD)
set(CMAKE_C_STANDARD 99)
endif()
# Default to C++14
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 14)
endif()
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
find_package(ament_cmake REQUIRED)
find_package(nav2_common REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rclcpp_action REQUIRED)
find_package(rclcpp_lifecycle REQUIRED)
find_package(std_msgs REQUIRED)
find_package(visualization_msgs REQUIRED)
find_package(nav2_util REQUIRED)
find_package(nav2_core REQUIRED)
find_package(nav2_msgs REQUIRED)
find_package(nav_msgs REQUIRED)
find_package(geometry_msgs REQUIRED)
find_package(builtin_interfaces REQUIRED)
find_package(tf2_ros REQUIRED)
find_package(nav2_costmap_2d REQUIRED)
find_package(pluginlib REQUIRED)
find_package(angles REQUIRED)
include_directories(
include
)
set(library_name custom_controller_core)
set(dependencies
rclcpp
rclcpp_action
rclcpp_lifecycle
std_msgs
visualization_msgs
nav2_util
nav2_msgs
nav_msgs
geometry_msgs
builtin_interfaces
tf2_ros
nav2_costmap_2d
nav2_core
pluginlib
)
add_library(${library_name} SHARED
src/custom_controller.cpp
)
ament_target_dependencies(${library_name}
${dependencies}
)
target_compile_definitions(${library_name} PUBLIC "PLUGINLIB__DISABLE_BOOST_FUNCTIONS")
pluginlib_export_plugin_description_file(nav2_core custom_controller_plugin.xml)
install(TARGETS ${library_name}
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
)
install(DIRECTORY include/
DESTINATION include/
)
install(FILES custom_controller_plugin.xml
DESTINATION share/${PROJECT_NAME}
)
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
ament_lint_auto_find_test_dependencies()
endif()
ament_export_include_directories(include)
ament_export_libraries(${library_name})
ament_export_dependencies(${dependencies})
ament_export_definitions("PLUGINLIB__DISBLE_BOOST_FUNCTIONS")
ament_package()
5. package.xml 작성
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>custom_controller</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="root@todo.todo">root</maintainer>
<license>TODO: License declaration</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<depend>rclcpp</depend>
<depend>rclcpp_action</depend>
<depend>rclcpp_lifecycle</depend>
<depend>visualization_msgs</depend>
<depend>nav2_util</depend>
<depend>nav2_msgs</depend>
<depend>nav_msgs</depend>
<depend>geometry_msgs</depend>
<depend>builtin_interfaces</depend>
<depend>nav2_common</depend>
<depend>tf2_ros</depend>
<depend>nav2_costmap_2d</depend>
<depend>nav2_core</depend>
<depend>pluginlib</depend>
<depend>angles</depend>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<export>
<build_type>ament_cmake</build_type>
<nav2_core plugin="${prefix}/custom_controller_plugin.xml" />
</export>
</package>
6. custom_controller_plugin.xml 작성
<library path="custom_controller_core">
<class name="custom_controller/CustomController" type="custom_controller::CustomController" base_class_type="nav2_core::Controller">
<description></description>
</class>
</library>
7. Navigation Parameter 변경
controller_server:
ros__parameters:
use_sim_time: True
controller_frequency: 20.0
min_x_velocity_threshold: 0.001
min_y_velocity_threshold: 0.5
min_theta_velocity_threshold: 0.001
progress_checker_plugin: "progress_checker"
goal_checker_plugin: "goal_checker"
controller_plugins: ["Custom"]
Custom:
plugin: "custom_controller/CustomController"
8. BT 변경
<root>
<BehaviorTree ID="MainTree">
<RecoveryNode number_of_retries="6" name="NavigateRecovery">
<PipelineSequence name="NavigateWithReplanning">
<RateController hz="1.0">
<RecoveryNode number_of_retries="1" name="ComputePathToPose">
<ComputePathToPose goal="{goal}" path="{path}" planner_id="GridBased"/>
<ClearEntireCostmap name="ClearGlobalCostmap-Context" service_name="global_costmap/clear_entirely_global_costmap"/>
</RecoveryNode>
</RateController>
<RecoveryNode number_of_retries="1" name="FollowPath">
<!-->!!!!!!!!!!!!!!!!!!변경!!!!!!!!!!!!!!!!</--!>
<FollowPath path="{path}" controller_id="Custom"/>
<ClearEntireCostmap name="ClearLocalCostmap-Context" service_name="local_costmap/clear_entirely_local_costmap"/>
</RecoveryNode>
</PipelineSequence>
<ReactiveFallback name="RecoveryFallback">
<GoalUpdated/>
<SequenceStar name="RecoveryActions">
<ClearEntireCostmap name="ClearLocalCostmap-Subtree" service_name="local_costmap/clear_entirely_local_costmap"/>
<ClearEntireCostmap name="ClearGlobalCostmap-Subtree" service_name="global_costmap/clear_entirely_global_costmap"/>
<Spin spin_dist="1.57"/>
<Wait wait_duration="5"/>
</SequenceStar>
</ReactiveFallback>
</RecoveryNode>
</BehaviorTree>
</root>
그러면 이제 custom으로 만든 controller가 잘 되는 걸 확인 할 수 있다.
위 예제 repository
https://github.com/ladianchad/nav2_study
GitHub - ladianchad/nav2_study
Contribute to ladianchad/nav2_study development by creating an account on GitHub.
github.com
'ROS' 카테고리의 다른 글
[ROS2] Realsense SDK + ROS humble Package 설치 (Ubuntu 22.04) (0) | 2022.06.07 |
---|---|
[ROS2] Navigation2 분석(3) - DWB Controller (0) | 2022.05.17 |
[ROS2] Navigation2 분석(2) - Controller Server (0) | 2022.05.17 |
[ROS2] Navigation2 분석(1) - 소개 (0) | 2022.05.16 |
[ROS2] Lifecycle Node(nav2_util/LifecycleNode) (0) | 2022.03.30 |