節點撰寫教學 === 初始化 --- 1. 引入必要的抬頭檔,若依據README的指示安裝了core套件,這些抬頭檔都會在系統指定路徑裡。 ```cpp= #include "NodeHandler.hpp" #include <boost/thread.hpp> #include "utils.hpp" ``` 2. 將IP、Port等可能需要指定的參數作為執行輸入,以利變更。 ```cpp= int main(int argc, char* argv[]) { std::string master_ip = "127.0.0.1"; // NodeCore 執行的裝置ip。 std::string local_ip = "127.0.0.1"; // 當前檔案執行的裝置ip。 uint32_t port = 9999; // NodeCore 執行的port。 double freq = 1000; // 本節點想執行的頻率。 // 讀取使用者輸入作為參數 core::ArgParse aps(argc, argv); aps.get("MasterIP", master_ip); aps.get("MasterPort", port); aps.get("LocalIP", local_ip); aps.get("Frequency", freq); ``` 3. 初始化NodeHandler,這個物件用於綁定與其他節點的通訊,訂閱、發布都是基於這個物件。 ```cpp= int main(int argc, char* argv[]) { // ... core::NodeHandler nh(master_ip, port, local_ip); ``` 至此就準備好一個基礎的節點了,之後可以使用它來與其他的節點通訊,需注意要給對的IP和port才能正常運作,例如主機端(Master)為192.168.2.127:8982,節點(Node)為192.168.2.34,那麼參數配置應該如下。 ```cpp= int main(int argc, char* argv[]) { // ... std::string master_ip = "192.168.2.127"; // NodeCore 執行的裝置ip。 std::string local_ip = "192.168.2.34"; // 當前檔案執行的裝置ip。 uint32_t port = 8982; // NodeCore 執行的port。 ``` 你可以選擇直接改在檔案裡,或者執行時給定這些參數。 訂閱訊息 --- 初始化節點後,可以宣告訂閱者(Subscriber)物件,並定義返回函數。 ```cpp= int main(int argc, char* argv[]) { // ... core::Subscriber sub = nh.subscriber("topic name"); ``` 該物件初始化需要指定收聽的話題名稱,通訊就是基於給定相同話題名稱進行的。 ```cpp= #include "robot_msg.hpp" robot_msg::Gait tmsg; inline void test_cb(std::shared_ptr<robot_msg::Gait> msg) { tmsg = *msg; } ``` 返回函數需要在一開始宣告,一般來說會需要知道你想要收聽的訊息的種類,並引入相對應的抬頭檔,關於如何自己定義訊息可以參考[自訂義訊息](https://hackmd.io/@peichunhuang/ByORmidln)。 ```cpp= int main(int argc, char* argv[]) { // ... sub.spinOnce(&test_cb); ``` 在定義了訂閱者與返回函數後,使用此函數就可以更新一次訊息,更新的訊息是該話題裡目前拿到的最新的訊息。 發布訊息 --- 發布訊息與訂閱訊息類似,宣告一個發布者後將訊息發至特定的話題。 ``` cpp= int main(int argc, char* argv[]) { // ... core::Publisher pub = nh.publisher("topic name"); robot_msg::Gait pub_msg; pub.publish(pub_msg); ``` 編譯與執行 --- 以上的範例完整檔案如下,這是一個自發自收的範例節點專案: :::spoiler test ##### node_test.cpp ``` cpp= #include "NodeHandler.hpp" #include <boost/thread.hpp> #include "std_msg.hpp" #include "geometry_msg.hpp" #include "robot_msg.hpp" #include "utils.hpp" robot_msg::Gait tmsg; inline void test_cb(std::shared_ptr<robot_msg::Gait> msg) { tmsg = *msg; } int main(int argc, char* argv[]) { std::string master_ip = "127.0.0.1"; std::string local_ip = "127.0.0.1"; uint32_t port = 9999; double freq = 1000; std::vector<std::string> topics {"default"}; core::ArgParse aps(argc, argv); aps.get("MasterIP", master_ip); aps.get("MasterPort", port); aps.get("LocalIP", local_ip); aps.get("Frequency", freq); aps.get("Topics", topics); boost::asio::io_context timer_io(1); robot_msg::Gait pub_msg; pub_msg.header.seq = 0; pub_msg.header.timestamp = 0; core::NodeHandler nh(master_ip, port, local_ip); core::Subscriber test_sub = nh.subscriber(topics[0]); core::Publisher test_pub = nh.publisher(topics[0]); boost::thread node_service(boost::bind(&boost::asio::io_service::run, &core::node_ios)); uint sleep_us = 1000000 / freq; while(1) { boost::asio::deadline_timer timer(timer_io, boost::posix_time::microseconds(sleep_us)); pub_msg.header.seq += 1; test_pub.publish(pub_msg); test_sub.spinOnce(&test_cb); // tmsg.show(); timer.wait(); } return 0; } ``` ##### CMakeLists.txt ```txt= cmake_minimum_required(VERSION 3.10) project(test C CXX ASM) set(CMAKE_C_COMPILER "/usr/bin/gcc") set(CMAKE_CXX_COMPILER "/usr/bin/g++") set(CMAKE_BUILD_TYPE Release) set(CMAKE_CXX_FLAGS_DEBUG "-g") set(CMAKE_CXX_FLAGS_RELEASE "-O3") set (CMAKE_EXE_LINKER_FLAGS) set (CMAKE_CXX_STANDARD 17) set(CMAKE_C_FLAGS "-std=c99") set(CMAKE_CXX_FLAGS "-std=c99 -Wno-error=deprecated-declarations -Wno-deprecated-declarations ") set(BOOST_COMPONENTS ALL) set(Boost_USE_STATIC_LIBS OFF) set(Boost_USE_MULTITHREADED ON) set(Boost_USE_STATIC_RUNTIME OFF) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/devel) find_package(Boost REQUIRED COMPONENTS serialization thread program_options) link_directories(/usr/local/lib) include_directories(${Boost_INCLUDE_DIRS}) # 一般而言這以上都會寫在最外層的CMakeLists.txt,不過這是一個測試用的專案,所以你需要以上這些。 add_executable(node_test node_test.cpp) target_link_libraries(node_test NodeHandler ${Boost_LIBRARIES} Utility) ``` ##### build ::: </br> 編譯與執行流程如下: $ cd test/build $ cmake .. $ make $ NodeCore & ./node_test 輸出應該會如下: [1] 39918 default 127.0.0.1 62727 這代表你的NodeCore在進程39918執行,而目前**default**這個話題有一對使用者正在互相收發。 因為以上的方式會讓NodeCore在背景執行,要結束需要手動關閉,可以依據剛剛輸出的進程來結束程式。 $ ^C $ kill 39918