ROS:参数服务器通信
為什么需要
在機器人開發中,會有很多參數和設置可以后期需要調整的,如果都放到源碼里很難實現動態修改和管理,ROS2為了解決這一問題,提出了參數這一通信機制。
是什么
如何理解“參數”?
- ROS2的參數就是節點的設置
- 參數類似于ROS中的全局變量,由ROS Master進行管理,其通信機制較為簡單,不涉及TCP/UDP的通信。
參數服務器是一種特殊的“通信方式”。特殊點在于參數服務器是節點存儲參數的地方、用于配置參數、全局共享參數。
- 參數服務器使用互聯網傳輸,在節點管理器中運行,實現整個通信過程
- 參數服務器,作為ROS中另外一種數據傳輸方式,有別于topic和service,它更加靜態。
- 參數服務器維護者一個數據字典,字典里存儲著各種參數和配置
- 參數服務器在ROS中主要用于實現不同節點之間的數據共享。一般用于存儲一些多節點共享的數據,類似于全局變量。
- 參數服務器相當于是獨立于所有節點的一個公共容器,可以將數據存儲在該容器中,被不同的節點調用,當然不同的節點也可以往其中存儲數據。
維護方式
參數服務器的維護方式非常的簡單靈活,總的來講有三種方式:
- 命令行維護
- launch文件內讀寫
- node源碼
命令行維護
使用命令行來維護參數服務器,主要使用rosparam語句來進行操作的各種命令,如下表:
| rosparam set param_key param_value | 設置參數 |
| rosparam get param_key | 顯示參數 |
| rosparam load file_name | 從文件加載參數 |
| rosparam dump file_name | 保存參數到文件 |
| rosparam delete | 刪除參數 |
| rosparam list | 列出參數名稱 |
load&&dump文件
load和dump文件需要遵守YAML格式,一般格式如下:
key : value具體示例如下:
name:'Zhangsan' age:20 gender:'M' score{Chinese:80,Math:90} score_history:[85,82,88,90]一般格式如下:
launch文件內讀寫
launch文件中有很多標簽,而與參數服務器相關的標簽只有兩個,一個是< param>,另一個是< rosparam>
node源碼
除了上述最常用的兩種讀寫參數服務器的方法,還有一種就是修改ROS的源碼,也就是利用API來對參數服務器進行操作。具體內容我們學習完后面章節再進行介紹。
ROS2:體驗一下
(1)運行小烏龜模擬器節點和小烏龜控制節點
# 終端一 ros2 run turtlesim turtlesim_node# 終端二 ros2 run turtlesim turtle_teleop_key(2)查看參數
- 查看節點有哪些參數(設置)
- 看參數的詳情信息:參數的名字,參數的描述,參數的類型,還有對參數的約束,最大值最小值等。
- 獲取參數名
- 獲取參數值
- 設置參數值:注意這里只是臨時修改
- 把參數存起來:相當去把當前的參數值拍一張快照,然后保存下來,后面可以用于恢復參數到當前的數值。
三個角色
- ROS Master (管理者):管理者作為一個公共的容器保存數據
- Talker (參數設置者):參數設置者往容器中存儲數據
- Listener (參數調用者): 參數調用者讀取容器中所需的數據
建立流程
- step1:
- 參數設置者使用RPC向參數服務器發送參數(包括參數名與參數值)
- ROS Master 將參數保存到參數列表中。
- step2:
- 參數調用者使用RPC向參數服務器發送參數查找請求,請求中包含要查找的參數名。
- step3:
- ROS Master 根據步驟2請求提供的參數名查找參數值,并使用RPC將查詢結果發送給參數調用者。
參數組成成分
ROS2參數是由鍵值對組成的.
- 名字的數據類型是字符串
- 值的數據類型可以是:
- bool 和bool[],布爾類型用來表示開關,比如我們可以控制雷達控制節點,開始掃描和停止掃描。
- int64 和int64[],整形表示一個數字,含義可以自己來定義
- float64 和float64[],浮點型,可以表示小數類型的參數值
- string 和string[],字符串,可以用來表示雷達控制節點中真實雷達的ip地址
- byte[],字節數組,這個可以用來表示圖片,點云數據等信息
代碼實現
ROS2將日志分為五個級別,在RCLCPP中通過不同的宏可以實現不同日志級別日志的打印,例程如下
RCLCPP_DEBUG(this->get_logger(), "我是DEBUG級別的日志,我被打印出來了!"); RCLCPP_INFO(this->get_logger(), "我是INFO級別的日志,我被打印出來了!"); RCLCPP_WARN(this->get_logger(), "我是WARN級別的日志,我被打印出來了!"); RCLCPP_ERROR(this->get_logger(), "我是ERROR級別的日志,我被打印出來了!"); RCLCPP_FATAL(this->get_logger(), "我是FATAL級別的日志,我被打印出來了!");有時候日志太多,會讓人眼花繚亂找不到重要信息,所以我們需要對日志的級別進行過濾,比如只看INFO以上級別的,ROS2中可以通過已有的API設置日志的級別,RCLCPP中API如下:
this->get_logger().set_level(log_level);目標:聲明參數并實現動態修改打印的日志級別功能。
RCLCPP實現
創建功能包和節點
mkdir -p chapt4/chapt4_ws/ ros2 pkg create example_parameters_rclcpp --build-type ament_cmake --dependencies rclcpp --destination-directory src --node-name parameters_basic --maintainer-name "fishros" --maintainer-email "fishros@foxmail.com"parameters_basic.cpp
#include <chrono> #include "rclcpp/rclcpp.hpp"class ParametersBasicNode : public rclcpp::Node {public:explicit ParametersBasicNode(std::string name) : Node(name) {RCLCPP_INFO(this->get_logger(), "節點已啟動:%s.", name.c_str());}private: };int main(int argc, char** argv) {rclcpp::init(argc, argv);/*產生一個的節點*/auto node = std::make_shared<ParametersBasicNode>("parameters_basic");/* 運行節點,并檢測退出信號*/rclcpp::spin(node);rclcpp::shutdown();return 0; }構建測試:
colcon build --packages-select example_parameters_rclcpp source install/setup.bash ros2 run example_parameters_rclcpp parameters_basic參數API
在RCLCPP的API中,關于參數相關的函數比較多些,但都是圍繞參數獲取、參數設置、參數描述、列出參數、添加和移除參數回調事件。
使用參數控制節點日志級別
#include <chrono> #include "rclcpp/rclcpp.hpp" /*# declare_parameter 聲明和初始化一個參數# describe_parameter(name) 通過參數名字獲取參數的描述# get_parameter 通過參數名字獲取一個參數# set_parameter 設置參數的值 */ class ParametersBasicNode : public rclcpp::Node {public:// 構造函數,有一個參數為節點名稱explicit ParametersBasicNode(std::string name) : Node(name) {RCLCPP_INFO(this->get_logger(), "節點已啟動:%s.", name.c_str());this->declare_parameter("rcl_log_level", 0); /*聲明參數*/this->get_parameter("rcl_log_level", log_level); /*獲取參數*//*設置日志級別*/this->get_logger().set_level((rclcpp::Logger::Level)log_level);using namespace std::literals::chrono_literals;timer_ = this->create_wall_timer(500ms, std::bind(&ParametersBasicNode::timer_callback, this));}private:int log_level;rclcpp::TimerBase::SharedPtr timer_;void timer_callback() {this->get_parameter("rcl_log_level", log_level); /*獲取參數*//*設置日志級別*/this->get_logger().set_level((rclcpp::Logger::Level)log_level);std::cout<<"======================================================"<<std::endl;RCLCPP_DEBUG(this->get_logger(), "我是DEBUG級別的日志,我被打印出來了!");RCLCPP_INFO(this->get_logger(), "我是INFO級別的日志,我被打印出來了!");RCLCPP_WARN(this->get_logger(), "我是WARN級別的日志,我被打印出來了!");RCLCPP_ERROR(this->get_logger(), "我是ERROR級別的日志,我被打印出來了!");RCLCPP_FATAL(this->get_logger(), "我是FATAL級別的日志,我被打印出來了!");} };int main(int argc, char** argv) {rclcpp::init(argc, argv);/*產生一個的節點*/auto node = std::make_shared<ParametersBasicNode>("parameters_basic");/* 運行節點,并檢測退出信號*/rclcpp::spin(node);rclcpp::shutdown();return 0; }上面set_level,設置日志級別,ROS2的日志級別定義在文件/opt/ros/humble/include/rcutils/rcutils/logging.h的167-175行
/// The severity levels of log messages / loggers. enum RCUTILS_LOG_SEVERITY {RCUTILS_LOG_SEVERITY_UNSET = 0, ///< The unset log levelRCUTILS_LOG_SEVERITY_DEBUG = 10, ///< The debug log levelRCUTILS_LOG_SEVERITY_INFO = 20, ///< The info log levelRCUTILS_LOG_SEVERITY_WARN = 30, ///< The warn log levelRCUTILS_LOG_SEVERITY_ERROR = 40, ///< The error log levelRCUTILS_LOG_SEVERITY_FATAL = 50, ///< The fatal log level };編譯測試
colcon build --packages-select example_parameters_rclcpp source install/setup.bash ros2 run example_parameters_rclcpp parameters_basic運行后你會發現DEBUG級別的日志并沒有被打印出來,原因在于我們將節點的日志級別設置為了0,0對應的日志級別為RCUTILS_LOG_SEVERITY_UNSET即未設置使用默認級別,節點默認的日志級別就是INFO級別的,所以只能打印INFO以上的日志信息。
運行節點的時候可以指定參數的值,我們嘗試將log_level的值改成10即DEBUG級別。
ros2 run example_parameters_rclcpp parameters_basic --ros-args -p rcl_log_level:=10除了在節點運行前通過CLI傳遞參數,在運動的過程中也可以動態的修改參數
#查看參數列表 ros2 param list #設置參數級別 ros2 param set /parameters_basic rcl_log_level 10補充
上面我們通過參數實現了動態控制節點日志級別的功能,其實像這樣的功能ROS2早已為我們準備好了,在運行任意節點時候可以通過CLI傳遞日志級別配置。
ros2 run package-name node-name --ros-args --log-level debug除了命令行設置參數和查看日志,通過rqt也可以可視化設置和查看
總結
以上是生活随笔為你收集整理的ROS:参数服务器通信的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 睡觉也能用的真无线耳机,续航音质也很强,
- 下一篇: 我说CMMI2.0之:II点睛