Now, let's remake the pub/sub example using a message interface to transfer more complex messages using ROS.
As we did before, first let's create a new package called complex_pubsub
ros2 pkg create --build-type ament_cmake complex_pubsub --dependencies rclcpp
Then, let's make a folder msg to hold message formats
mkdir msg
Now let's create a file called UserData.msg
bool FEMALE=true
bool MALE=false
string first_name
string last_name
bool gender
uint8 age
Next, go to CMakeLists.txt and let's setup the package for interface building, adding:
find_package(rosidl_default_generators REQUIRED)
set(msg_files
"msg/UserData.msg"
)
rosidl_generate_interfaces(${PROJECT_NAME}
${msg_files}
)
ament_export_dependencies(rosidl_default_runtime)
Also, let's add the IDL compiler references to the package.xml
<buildtool_depend>rosidl_default_generators</buildtool_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<member_of_group>rosidl_interface_packages</member_of_group>
Nice. So now we can create a very small and simple publisher publisher.cpp, without any handling logic, just to make it compile:
#include <memory>
#include <string>
#include "rclcpp/rclcpp.hpp"
class UserPublisher : public rclcpp::Node
{
};
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
rclcpp::shutdown();
return 0;
}
Now we can build the package:
colcon build --packages-select complex_pubsub
After finish building the package, we have now a useful hpp file with our data struct and we can go back to the publisher code and include it. If you're using Visual Studio Code and it doesn't recognize this path for autocomplete, you can close this IDE and reload it.
#include "complex_pubsub/msg/user_data.hpp"
Now we can complete our publisher's code on file user_publisher.cpp:
#include <memory>
#include <string>
#include <chrono>
#include "rclcpp/rclcpp.hpp"
#include "complex_pubsub/msg/user_data.hpp"
using namespace std::chrono_literals;
class UserPublisher : public rclcpp::Node
{
private:
rclcpp::Publisher<complex_pubsub::msg::UserData>::SharedPtr publisher;
rclcpp::TimerBase::SharedPtr timer;
public:
UserPublisher() : Node("user_publisher")
{
publisher = this->create_publisher<complex_pubsub::msg::UserData>(
"user_data_topic", 10);
timer = this->create_wall_timer(1s, [this]() -> void {
auto msg = complex_pubsub::msg::UserData();
msg.first_name = "Users first name";
msg.last_name = "Users last name";
msg.age = 40;
msg.gender = msg.MALE;
RCLCPP_INFO(this->get_logger(), "publishing user data\n");
this->publisher->publish(msg);
});
}
};
int main(int argc, char *argv[])
{
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<UserPublisher>());
rclcpp::shutdown();
return 0;
}
This code is very simple. We have basically to create the publisher and publish a message to it. The message must follow the format that we specify when creating the publisher. In this case, complex_pubsub::msg::UserData
The method create_wall_timer will create a forever repeater that will execute the lambda code in
[this]() -> void { (code) }
Now, just to be able to test it, let's write a subscriber that will print the user's data being published. So create a file called user_subscriber.cpp with:
#include <memory>
#include <string>
#include "rclcpp/rclcpp.hpp"
#include "complex_pubsub/msg/user_data.hpp"
using std::placeholders::_1;
#define GENDER_OUTP_MALE "male"
#define GENDER_OUTP_FEMALE "female"
class UserSubscriber : public rclcpp::Node
{
private:
rclcpp::Subscription<complex_pubsub::msg::UserData>::SharedPtr subscriber;
void read_message(const complex_pubsub::msg::UserData &msg)
{
RCLCPP_INFO(this->get_logger(),
"received user data: { first-name: '%s', last-name: '%s', " +
"age: '%u', gender:'%s' }",
msg.first_name.c_str(),
msg.last_name.c_str(),
msg.age,
(msg.gender == msg.MALE) ? GENDER_OUTP_MALE : GENDER_OUTP_FEMALE
);
}
public:
UserSubscriber() : Node("user_publisher")
{
subscriber = this->create_subscription<complex_pubsub::msg::UserData>(
"user_data_topic", 10, std::bind(&UserSubscriber::read_message,
this, _1));
}
};
int main(int argc, char *argv[])
{
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<UserSubscriber>());
rclcpp::shutdown();
return 0;
}
Now we change our CMakeLists.txt to support having msg / srv in itself and to put together things to form the two executables: the publisher and the subscriber
cmake_minimum_required(VERSION 3.8)
project(complex_pubsub)
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rosidl_default_generators REQUIRED)
set(msg_files
"msg/UserData.msg"
)
rosidl_generate_interfaces(${PROJECT_NAME}
${msg_files}
)
add_executable(publisher
src/user_publisher.cpp
)
add_executable(subscriber
src/user_subscriber.cpp
)
ament_target_dependencies(publisher
rclcpp
)
ament_target_dependencies(subscriber
rclcpp
)
rosidl_target_interfaces(publisher
${PROJECT_NAME} "rosidl_typesupport_cpp")
rosidl_target_interfaces(subscriber
${PROJECT_NAME} "rosidl_typesupport_cpp")
install(TARGETS publisher subscriber
DESTINATION lib/${PROJECT_NAME})
ament_export_dependencies(rosidl_default_runtime)
ament_package()
Let's now test it!
1. Open another terminal and then please execute
. install/local_setup.bash
ros2 run complex_pubsub publisher
2. Check that the message is being published on the topic by using a ROS2 tool
. install/setup.bash
ros2 topic echo /user_data_topic
ros2 topic echo /user_data_topic
first_name: Users first name
last_name: Users last name
gender: false
age: 40
---
first_name: Users first name
last_name: Users last name
gender: false
age: 40
---
3. Execute the subscriber to see it receiving the published messages:
. install/local_setup.bash
ros2 run complex_pubsub subscriber
[INFO] [1649384531.571994547] [user_publisher]: received user data: { first-name: 'Users first name', last-name: 'Users last name', age: '40', gender:'male' }
[INFO] [1649384532.572024766] [user_publisher]: received user data: { first-name: 'Users first name', last-name: 'Users last name', age: '40', gender:'male' }
[INFO] [1649384533.572154201] [user_publisher]: received user data: { first-name: 'Users first name', last-name: 'Users last name', age: '40', gender:'male' }
Nenhum comentário:
Postar um comentário