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, …
Declare your parameter
number_publisher.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
#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
#!/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()
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!
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
Get Parameters
#!/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()
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
"
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.
Solution
robot_news_station.py
#!/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
#!/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
#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
#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 |