개발하는 핑구
article thumbnail

1. ROS2 Parameter

  • Settings for your nodes, value set at run time
  • A Parameter is specific to a node
  • ROS2 Parameter types
    • boolean, int, double, string, lists, …

2. Declare your parameter

number_publisher.cpp

<cpp />
#include "rclcpp/rclcpp.hpp" #include "example_interfaces/msg/int64.hpp" class NumberPublisherNode : public rclcpp::Node { public: NumberPublisherNode() : Node("number_publisher"), number_(2) { // add this line this->declare_parameter("name"); number_publisher_ = this->create_publisher<example_interfaces::msg::Int64>("number", 10); number_timer_ = this->create_wall_timer(std::chrono::seconds(1), std::bind(&NumberPublisherNode::publishNumber, this)); RCLCPP_INFO(this->get_logger(), "Number publisher has been started."); } private: void publishNumber() { auto msg = example_interfaces::msg::Int64(); msg.data = number_; number_publisher_->publish(msg); } int number_; rclcpp::Publisher<example_interfaces::msg::Int64>::SharedPtr number_publisher_; rclcpp::TimerBase::SharedPtr number_timer_; }; int main(int argc, char **argv) { rclcpp::init(argc, argv); auto node = std::make_shared<NumberPublisherNode>(); rclcpp::spin(node); rclcpp::shutdown(); return 0; }

Better code

<cpp />
#include "rclcpp/rclcpp.hpp" #include "example_interfaces/msg/int64.hpp" class NumberPublisherNode : public rclcpp::Node { public: NumberPublisherNode() : Node("number_publisher") { this->declare_parameter("number_to_publish", 2); this->declare_parameter("publish_frequency", 1.0); number_ = this->get_parameter("number_to_publish").as_int(); double publish_frequency = this->get_parameter("publish_frequency").as_double(); number_publisher_ = this->create_publisher<example_interfaces::msg::Int64>("number", 10); number_timer_ = this->create_wall_timer(std::chrono::milliseconds((int) (1000.0 / publish_frequency)), std::bind(&NumberPublisherNode::publishNumber, this)); RCLCPP_INFO(this->get_logger(), "Number publisher has been started."); } private: void publishNumber() { auto msg = example_interfaces::msg::Int64(); msg.data = number_; number_publisher_->publish(msg); } int number_; rclcpp::Publisher<example_interfaces::msg::Int64>::SharedPtr number_publisher_; rclcpp::TimerBase::SharedPtr number_timer_; }; int main(int argc, char **argv) { rclcpp::init(argc, argv); auto node = std::make_shared<NumberPublisherNode>(); rclcpp::spin(node); rclcpp::shutdown(); return 0; }

number_publisher.py

<python />
#!/usr/bin/env python3 import rclpy from rclpy.node import Node from example_interfaces.msg import Int64 class NumberPublisherNode(Node): def __init__(self): super().__init__("number_publisher") # add this lines self.declare_parameter("test1") self.declare_parameter("test2") # ------------------- self.number_ = 2 self.number_publisher_ = self.create_publisher(Int64, "number", 10) self.number_timer_ = self.create_timer(1.0, self.publish_number) self.get_logger().info("Number publisher has been started.") def publish_number(self): msg = Int64() msg.data = self.number_ self.number_publisher_.publish(msg) def main(args=None): rclpy.init(args=args) node = NumberPublisherNode() rclpy.spin(node) rclpy.shutdown() if __name__ == "__main__": main()
<bash />
ros2 run my_py_pkg number_publisher ros2 run my_py_pkg number_counter ros2 param list " /number_publisher: test1 test2 use_sim_time /number_counter: use_sim_time " ros2 param get /number_publisher use_sim_time # Boolean value is: False

Whenever you start a node, you automatically have this parameter use_sim_time by ros2

But you don’t care about this parameter. It just will allow you to easily find what are parameters currently running on application. And those are different parameters even if they have same names.

Let’s set parameters!

<bash />
ros2 run my_py_pkg number_publisher --ros-args -p test1:=3 ros2 run my_py_pkg number_publisher --ros-args -p test2:="hello" ros2 param get /number_publisher test1 # Integer value is: 3 ros2 param get /number_publisher test2 # String value is: hello

3. Get Parameters

<python />
#!/usr/bin/env python3 import rclpy from rclpy.node import Node from example_interfaces.msg import Int64 class NumberPublisherNode(Node): def __init__(self): super().__init__("number_publisher") self.declare_parameter("number_to_publish", 2) # second parameter is default value self.declare_parameter("publish_frequency", 1.0) self.number_ = self.get_parameter("number_to_publish").value self.publisher_frequeny_ = self.get_parameter("publish_frequency").value self.number_publisher_ = self.create_publisher(Int64, "number", 10) self.number_timer_ = self.create_timer( 1.0 / self.publisher_frequeny_, self.publish_number) self.get_logger().info("Number publisher has been started.") def publish_number(self): msg = Int64() msg.data = self.number_ self.number_publisher_.publish(msg) def main(args=None): rclpy.init(args=args) node = NumberPublisherNode() rclpy.spin(node) rclpy.shutdown() if __name__ == "__main__": main()
<bash />
ros2 run my_py_pkg number_publisher --ros-args -p number_to_publish:=3 -p publish_frequency:=4 ros2 topic echo /number " data: 2 --- data: 2 --- " ros2 topic hz /number " average rate: 4.000 min: 0.235s max: 0.261s std dev: 0.00800s window: 6 average rate: 4.000 min: 0.235s max: 0.261s std dev: 0.00597s window: 11 average rate: 4.000 min: 0.235s max: 0.261s std dev: 0.00534s window: 16 "

4. Activity

Let’s practice a little bit more with Parameters.

Here are 2 quick activities:

 

1. Do you remember one of the first node we created in the Topic section, with the robot news radio? This node publishes a string on a topic, similar to this “Hi, this is R2D2 from the Robot News Station!”.

Now, it would be better if we could set the robot’s name at run time, so we can launch the node multiple times with different robot names.

Add a “robot_name” parameter, and use the value to publish the string on the “robot_news” topic. Your string template (“Hi, this is <robot_name> from the Robot News Station!”) will now use the name you set at runtime.

 

2. Go back to the “led_panel_node”. Here you have an int array representing the states of your LEDs (0 for powered off, 1 for powered on). Set this array with a parameter named “led_states”.


This will make you practice on string, integer, and array parameters. You can apply those changes in both the Python and Cpp nodes.

5. Solution

robot_news_station.py

<python />
#!/usr/bin/env python3 import rclpy from rclpy.node import Node from example_interfaces.msg import String class RobotNewsStationNode(Node): def __init__(self): super().__init__("robot_news_station") self.declare_parameter("robot_name", "C3PO") self.robot_name_ = self.get_parameter("robot_name").value self.publisher_ = self.create_publisher(String, "robot_news", 10) self.timer_ = self.create_timer(0.5, self.publish_news) self.get_logger().info("Robot News Station has been started") def publish_news(self): msg = String() msg.data = "Hi, this is " + \\ str(self.robot_name_) + " from the robot news station." self.publisher_.publish(msg) def main(args=None): rclpy.init(args=args) node = RobotNewsStationNode() rclpy.spin(node) rclpy.shutdown() if __name__ == "__main__": main()

led_panel.py

<python />
#!/usr/bin/env python3 import rclpy from rclpy.node import Node from my_robot_interfaces.msg import LedStateArray from my_robot_interfaces.srv import SetLed class LedPanelNode(Node): def __init__(self): super().__init__("led_panel") self.declare_parameter("led_states", [0, 0, 0]) self.led_states_ = self.get_parameter("led_states").value self.led_states_publisher_ = self.create_publisher( LedStateArray, "led_states", 10) self.led_states_timer_ = self.create_timer(4, self.publish_led_states) self.set_led_service_ = self.create_service( SetLed, "set_led", self.callback_set_led) self.get_logger().info("LED panel node has been started.") def publish_led_states(self): msg = LedStateArray() msg.led_states = self.led_states_ self.led_states_publisher_.publish(msg) def callback_set_led(self, request, response): led_number = request.led_number state = request.state if led_number > len(self.led_states_) or led_number <= 0: response.success = False return response if state not in [0, 1]: response.success = False return response self.led_states_[led_number - 1] = state response.success = True self.publish_led_states() return response def main(args=None): rclpy.init(args=args) node = LedPanelNode() rclpy.spin(node) rclpy.shutdown() if __name__ == "__main__": main()

robot_news_station.cpp

<cpp />
#include "rclcpp/rclcpp.hpp" #include "example_interfaces/msg/string.hpp" class RobotNewsStationNode : public rclcpp::Node { public: RobotNewsStationNode() : Node("robot_news_station"), robot_name_("R2D2") { this->declare_parameter("robot_name", "R2D2"); robot_name_ = this->get_parameter("robot_name").as_string(); publisher_ = this->create_publisher<example_interfaces::msg::String>("robot_news", 10); timer_ = this->create_wall_timer(std::chrono::milliseconds(500), std::bind(&RobotNewsStationNode::publishNews, this)); RCLCPP_INFO(this->get_logger(), "Robot News Station has been started."); } private: void publishNews() { auto msg = example_interfaces::msg::String(); msg.data = std::string("Hi, this is ") + robot_name_ + std::string(" from the Robot News Station"); publisher_->publish(msg); } std::string robot_name_; rclcpp::Publisher<example_interfaces::msg::String>::SharedPtr publisher_; rclcpp::TimerBase::SharedPtr timer_; }; int main(int argc, char **argv) { rclcpp::init(argc, argv); auto node = std::make_shared<RobotNewsStationNode>(); rclcpp::spin(node); rclcpp::shutdown(); return 0; }

led_panel.cpp

<cpp />
#include "rclcpp/rclcpp.hpp" #include "my_robot_interfaces/msg/led_state_array.hpp" #include "my_robot_interfaces/srv/set_led.hpp" class LedPanelNode : public rclcpp::Node { public: LedPanelNode() : Node("led_panel") { this->declare_parameter("led_states", std::vector<int64_t>{0, 0, 0}); led_states_ = this->get_parameter("led_states").as_integer_array(); led_states_publisher_ = this->create_publisher<my_robot_interfaces::msg::LedStateArray>("led_states", 10); led_states_timer_ = this->create_wall_timer(std::chrono::seconds(4), std::bind(&LedPanelNode::publishLedStates, this)); set_led_service_ = this->create_service<my_robot_interfaces::srv::SetLed>( "set_led", std::bind(&LedPanelNode::callbackSetLed, this, std::placeholders::_1, std::placeholders::_2)); RCLCPP_INFO(this->get_logger(), "Led panel node has been started"); } private: void publishLedStates() { auto msg = my_robot_interfaces::msg::LedStateArray(); msg.led_states = led_states_; led_states_publisher_->publish(msg); } void callbackSetLed(const my_robot_interfaces::srv::SetLed::Request::SharedPtr request, const my_robot_interfaces::srv::SetLed::Response::SharedPtr response) { int64_t led_number = request->led_number; int64_t state = request->state; if (led_number > (int64_t)led_states_.size() || led_number <= 0) { response->success = false; return; } if (state != 0 && state != 1) { response->success = false; return; } led_states_.at(led_number - 1) = state; response->success = true; publishLedStates(); } std::vector<int64_t> led_states_; rclcpp::Publisher<my_robot_interfaces::msg::LedStateArray>::SharedPtr led_states_publisher_; rclcpp::TimerBase::SharedPtr led_states_timer_; rclcpp::Service<my_robot_interfaces::srv::SetLed>::SharedPtr set_led_service_; }; int main(int argc, char **argv) { rclcpp::init(argc, argv); auto node = std::make_shared<LedPanelNode>(); rclcpp::spin(node); rclcpp::shutdown(); return 0; }

'Robotics > ROS2' 카테고리의 다른 글

[ROS2 시작하기 - 7] Launch  (0) 2023.03.02
[ROS2 시작하기 - 5] Interface  (0) 2023.02.27
[ROS2 시작하기 - 4] Service  (0) 2023.02.24
[ROS2 시작하기 - 3] Topic  (0) 2023.02.23
[ROS2 시작하기 - 2] Tools  (0) 2023.02.21
profile

개발하는 핑구

@sinq

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!