component 與一般的node 的差異是,組成component 的元件node 不具有main function,而是會編譯成shared object 的方式(library ),由一隻稱為container 的程式載入
具ROS 官方說法是,用component 運作的node 之間的溝通效率較高
啟動component 有分三種方法
- run-time command line:啟用ROS 2官方提供的container service,再透過指令載入node shared object
- compile-time coding:編寫單一執行檔,將要用的node shared object link 進去
- run-time dll open:啟用ROS 2 官方提供的執行檔,使用引數的方式代入要用的node
建立node shared object
主要是把node 的邏輯寫成一個class,初始化就放在constructor 內,而不是一個main function style
如果是service 或者subscriber 就把要做的事情一樣放在callback 內,然後初始化就把callback function 代進去
底下程式碼是我嘗試能不能把service 與publisher 寫成同一個node 搭配component
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
#include "std_srvs/srv/empty.hpp"
#include "visibility_control.h"
#include <chrono>
using namespace std::chrono_literals;
namespace component_demo
{
class Talker: public rclcpp::Node
{
public :
COMPOSITION_PUBLIC
Talker() : Node( "talker" ),count_(0)
{
auto handler = [ this ]( const std::shared_ptr<std_srvs::srv::Empty::Request> request, const std::shared_ptr<std_srvs::srv::Empty::Response> response) -> void
{
printf ( "receive request\n" );
};
srv_ = create_service<std_srvs::srv::Empty>( "test" , handler);
pub_ = create_publisher<std_msgs::msg::String>( "chatter" );
timer_ = create_wall_timer(1s, std::bind(&Talker::on_timer, this ));
};
protected :
void on_timer()
{
auto msg = std::make_shared<std_msgs::msg::String>();
msg->data = "Hello World: " + std::to_string(++count_);
RCLCPP_INFO( this ->get_logger(), "Publishing: '%s'" , msg->data.c_str())
std::flush(std::cout);
pub_->publish(msg);
};
private :
size_t count_;
rclcpp::Publisher<std_msgs::msg::String>::SharedPtr pub_;
rclcpp::Service<std_srvs::srv::Empty>::SharedPtr srv_;
rclcpp::TimerBase::SharedPtr timer_;
};
|
修改CMake
主要差異是要建library 而不是executables
cmake_minimum_required(VERSION 3.5)
project(component_demo)
# 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)
endif()
find_package(ament_cmake REQUIRED)
find_package(ament_index_cpp REQUIRED)
find_package(class_loader REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rcutils REQUIRED)
find_package(rosidl_cmake REQUIRED)
find_package(rosidl_default_generators REQUIRED)
find_package(std_msgs REQUIRED)
find_package(std_srvs REQUIRED)
include_directories(include)
# create ament index resource which references the libraries in the binary dir
set(node_plugins "")
add_library(talker SHARED
src/talker.cpp)
target_compile_definitions(talker
PRIVATE "COMPOSITION_BUILDING_DLL")
ament_target_dependencies(talker
"class_loader"
"rclcpp"
"std_msgs"
"std_srvs")
rclcpp_register_node_plugins(talker "component_demo::Talker")
set(node_plugins "${node_plugins}component_demo::Talker;$<TARGET_FILE:talker>\n")
# since the package installs libraries without exporting them
# it needs to make sure that the library path is being exported
if(NOT WIN32)
ament_environment_hooks(
"${ament_cmake_package_templates_ENVIRONMENT_HOOK_LIBRARY_PATH}")
endif()
install(TARGETS
talker
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
)
ament_package()
|
run-time command line
先開一個terminal 啟動component cotainer service
要先source setup.bash 不然會認不到自己寫的package
source install /setup . bash
ros2 run composition api_composition
|
再開一個terminal 載入shared object 到service
component_demo 是我的package name
component_demo::Talker 則是namespace 底下的Talker 物件
ros2 run composition api_composition_cli component_demo component_demo::Talker
|
compile-time coding
參考ROS 2 官方範例,改成載入剛剛自己建立的shared object 就行了
#include <memory>
#include "rclcpp/rclcpp.hpp"
int main( int argc, char * argv[])
{
setvbuf (stdout, NULL, _IONBF, BUFSIZ);
rclcpp::init(argc, argv);
rclcpp::executors::SingleThreadedExecutor exec;
auto talker = std::make_shared<component_demo::Talker>();
exec.add_node(talker);
exec.spin();
rclcpp::shutdown();
return 0;
}
|
run-time dll open
啟動ROS 2 官方的node dlopen_composition,後面引數代入shared object 即可
ros2 run composition dlopen_composition `ros2 pkg prefix component_demo` /lib/libtalker .so
[INFO] [dlopen_composition]: Load library /home/karlkwchen/ros2_overlay_ws/install/lib/libtalker .so
[INFO] [dlopen_composition]: Instantiate class component_demo::Talker
|