1、创建工作空间
首先,在根目录下面创建一个新的工作空间test_w
和新的功能包topic_pkg
,然后新建脚本文件topic_interface_pub.cpp和topic_interface_sub.cpp,然后用VScode打开test_w文件夹。
mkdir -p ./text_w/src
cd text_w/src
ros2 pkg create topic_pkg --build-type ament_cmake --dependencies rclcpp std_msgs --node-name topic_interface_pub
touch ./topic_pkg/src/topic_interface_sub.cpp
2、测试一下简单的话题通信
编写发布者节点,复制下面代码到 topic_interface_pub.cpp
/***
功能:以某个固定频率发布 hello world
***/
#include "rclcpp/rclcpp.hpp" // ROS2 C++接口库
#include "std_msgs/msg/string.hpp" // 字符串消息类型
using namespace std::chrono_literals;//设置持续时间相关
class PublisherNode : public rclcpp::Node
{
public:
PublisherNode(): Node("topic_helloworld_pub") // ROS2节点父类初始化
{
RCLCPP_INFO(this->get_logger(), "Create Publisher Finished !");
// 创建发布者对象(消息类型、话题名、队列长度)
publisher_ = this->create_publisher<std_msgs::msg::String>("chatter", 10);
// 创建一个定时器,定时执行回调函数
timer_ = this->create_wall_timer(500ms, std::bind(&PublisherNode::timer_callback, this));
}
private:
// 创建定时器周期执行的回调函数
void timer_callback()
{
// 创建一个String类型的消息对象
auto mssage = std_msgs::msg::String();
// 填充消息对象中的消息数据
mssage.data = "Hello World " + std::to_string(count++);
// 发布话题消息
RCLCPP_INFO(this->get_logger(), "Publishing: '%s'", mssage.data.c_str());
// 输出日志信息,提示已经完成话题发布
publisher_->publish(mssage);
}
rclcpp::TimerBase::SharedPtr timer_; // 定时器指针
rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_; // 发布者指针
size_t count; // 计数器
};
// ROS2节点主入口main函数
int main(int argc, char * argv[])
{
// ROS2 C++接口初始化
rclcpp::init(argc, argv);
// 创建ROS2节点对象并进行初始化
rclcpp::spin(std::make_shared<PublisherNode>()); //挂起
// 关闭ROS2 C++接口
rclcpp::shutdown();
return 0;
}
编写订阅者节点,复制下面代码到 topic_interface_sub.cpp
/***
功能:订阅发布者发布的 hello world
***/
#include "rclcpp/rclcpp.hpp" // ROS2 C++接口库
#include "std_msgs/msg/string.hpp" // 字符串消息类型
using std::placeholders::_1;
class SubscriberNode : public rclcpp::Node
{
public:
SubscriberNode(): Node("topic_helloworld_sub") // ROS2节点父类初始化
{
RCLCPP_INFO(this->get_logger(), "Create Subscriber Finished !");
subscription_ = this->create_subscription<std_msgs::msg::String>(
"chatter", 10, std::bind(&SubscriberNode::topic_callback, this, _1)); // 创建订阅者对象(消息类型、话题名、订阅者回调函数、队列长度)
}
private:
void topic_callback(const std_msgs::msg::String::SharedPtr msg) const // 创建回调函数,执行收到话题消息后对数据的处理
{
RCLCPP_INFO(this->get_logger(), "I heard: '%s'", msg->data.c_str()); // 输出日志信息,提示订阅收到的话题消息
}
rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscription_; // 订阅者指针
};
// ROS2节点主入口main函数
int main(int argc, char * argv[])
{
// ROS2 C++接口初始化
rclcpp::init(argc, argv);
// 创建ROS2节点对象并进行初始化
rclcpp::spin(std::make_shared<SubscriberNode>());
// 关闭ROS2 C++接口
rclcpp::shutdown();
return 0;
}
发现头文件报错
点击编辑"includePath"设置
添加/opt/ros/humble/**,点击空白处保存
添加完后可以看见左边目录生成了.vscode文件夹,该文件夹里面存在一个c_cpp_properties.json文件,里面内容对应上面添加的路径
然后修改CMakeListst.txt,如箭头所示
cmake_minimum_required(VERSION 3.8)
project(topic_pkg)
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(std_msgs REQUIRED)
add_executable(topic_interface_pub src/topic_interface_pub.cpp)
add_executable(topic_interface_sub src/topic_interface_sub.cpp)
target_include_directories(topic_interface_pub PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
target_compile_features(topic_interface_pub PUBLIC c_std_99 cxx_std_17) # Require C99 and C++17
ament_target_dependencies(
topic_interface_pub
"rclcpp"
"std_msgs"
)
ament_target_dependencies(
topic_interface_sub
"rclcpp"
"std_msgs"
)
install(TARGETS topic_interface_pub
DESTINATION lib/${PROJECT_NAME})
install(TARGETS topic_interface_sub
DESTINATION lib/${PROJECT_NAME})
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
# the following line skips the linter which checks for copyrights
# comment the line when a copyright and license is added to all source files
set(ament_cmake_copyright_FOUND TRUE)
# the following line skips cpplint (only works in a git repo)
# comment the line when this package is in a git repo and when
# a copyright and license is added to all source files
set(ament_cmake_cpplint_FOUND TRUE)
ament_lint_auto_find_test_dependencies()
endif()
ament_package()
编译测试一下,分别打开两个终端,在text_w目录下,进行编译和运行对应节点
colcon build
source install/setup.bash
ros2 run topic_pkg topic_interface_pub
colcon build
source install/setup.bash
ros2 run topic_pkg topic_interface_sub
测试成功
3、自定义接口功能包
进入src目录,然后创建接口功能包interface_pkg,然后在 interface_pkg功能包下创建msg文件夹,并且创建一个Student.msg文件,注意该文件名首字母必须大写
cd ./src/
ros2 pkg create interface_pkg --build-type ament_cmake
mkdir -p ./interface_pkg/msg
touch ./interface_pkg/msg/Student.msg
在Student.msg文件下自定义数据格式
string name
int32 age
float64 height
在CMakeLists.txt中添加以下代码
# 编译的依赖包
find_package(rosidl_default_generators REQUIRED)
#为接口文件生成源代码,接口名字首字母必须大写
rosidl_generate_interfaces(${PROJECT_NAME}
"msg/Student.msg"
)
在package.xml中添加以下代码
<!--编译依赖-->
<build_depend>rosidl_default_generators</build_depend>
<!--执行依赖-->
<exec_depend>rosidl_default_runtime</exec_depend>
<!--声明当前所属的功能包组-->
<member_of_group>rosidl_interface_packages</member_of_group>
编译一下interface_pkg功能包
cd ..
colcon build --packages-select interface_pkg
打开install/interface_pkg目录,可以看见在include目录下面生成了C++对应的自定义接口头文件,在local目录下生成了Python对应的自定义接口库
重新编写发布者节点
/***
功能:以某个固定频率发布学生信息
***/
#include "rclcpp/rclcpp.hpp" // ROS2 C++接口库
#include "std_msgs/msg/string.hpp" // 字符串消息类型
#include "interface_pkg/msg/student.hpp" // 自定义接口
using namespace std::chrono_literals; //设置持续时间相关
class PublisherNode : public rclcpp::Node
{
public:
PublisherNode(): Node("topic_interface_pub") // ROS2节点父类初始化
{
// 创建发布者对象(消息类型、话题名、队列长度)
publisher_ = this->create_publisher<interface_pkg::msg::Student>("topic_interface", 10);
// 创建一个定时器,定时执行回调函数
timer_ = this->create_wall_timer(500ms, std::bind(&PublisherNode::timer_callback, this));
}
private:
// 创建定时器周期执行的回调函数
void timer_callback()
{
// 创建一个Student类型的消息对象
auto stu = interface_pkg::msg::Student();
stu.name = "小明" ;
stu.age = 8;
stu.height = 1.8;
// 发布话题消息
publisher_->publish(stu);
// 输出日志信息,提示已经完成话题发布
RCLCPP_INFO(this->get_logger(), "发布的消息: '%s','%d','%.2f'",stu.name.c_str(),stu.age,stu.height);
}
rclcpp::TimerBase::SharedPtr timer_; // 定时器指针
rclcpp::Publisher<interface_pkg::msg::Student>::SharedPtr publisher_; // 发布者指针
};
// ROS2节点主入口main函数
int main(int argc, char * argv[])
{
// ROS2 C++接口初始化
rclcpp::init(argc, argv);
// 创建ROS2节点对象并进行初始化
rclcpp::spin(std::make_shared<PublisherNode>());
// 关闭ROS2 C++接口
rclcpp::shutdown();
return 0;
}
重新编写订阅者节点
/***
功能:订阅发布者发布的学生信息
***/
#include "rclcpp/rclcpp.hpp" // ROS2 C++接口库
#include "std_msgs/msg/string.hpp" // 字符串消息类型
#include "interface_pkg/msg/student.hpp" // 自定义接口
using std::placeholders::_1;
class SubscriberNode : public rclcpp::Node
{
public:
SubscriberNode(): Node("topic_interface_sub") // ROS2节点父类初始化
{
subscription_ = this->create_subscription<interface_pkg::msg::Student>(
"topic_interface", 10, std::bind(&SubscriberNode::topic_callback, this, _1)); // 创建订阅者对象(消息类型、话题名、订阅者回调函数、队列长度)
}
private:
void topic_callback(const interface_pkg::msg::Student &stu) // 创建回调函数,执行收到话题消息后对数据的处理
{
RCLCPP_INFO(this->get_logger(), "订阅到的消息: name='%s',age='%d',height='%.2f'",stu.name.c_str(),stu.age,stu.height); // 输出日志信息,提示订阅收到的话题消息
}
rclcpp::Subscription<interface_pkg::msg::Student>::SharedPtr subscription_; // 订阅者指针
};
// ROS2节点主入口main函数
int main(int argc, char * argv[])
{
// ROS2 C++接口初始化
rclcpp::init(argc, argv);
// 创建ROS2节点对象并进行初始化
rclcpp::spin(std::make_shared<SubscriberNode>());
// 关闭ROS2 C++接口
rclcpp::shutdown();
return 0;
}
修改topic_pkg目录下面的CMakeLists.txt
cmake_minimum_required(VERSION 3.8)
project(topic_pkg)
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(std_msgs REQUIRED)
find_package(interface_pkg REQUIRED)
add_executable(topic_interface_pub src/topic_interface_pub.cpp)
add_executable(topic_interface_sub src/topic_interface_sub.cpp)
target_include_directories(topic_interface_pub PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
target_compile_features(topic_interface_pub PUBLIC c_std_99 cxx_std_17) # Require C99 and C++17
ament_target_dependencies(
topic_interface_pub
"rclcpp"
"std_msgs"
"interface_pkg"
)
ament_target_dependencies(
topic_interface_sub
"rclcpp"
"std_msgs"
"interface_pkg"
)
install(TARGETS topic_interface_pub
DESTINATION lib/${PROJECT_NAME})
install(TARGETS topic_interface_sub
DESTINATION lib/${PROJECT_NAME})
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
# the following line skips the linter which checks for copyrights
# comment the line when a copyright and license is added to all source files
set(ament_cmake_copyright_FOUND TRUE)
# the following line skips cpplint (only works in a git repo)
# comment the line when this package is in a git repo and when
# a copyright and license is added to all source files
set(ament_cmake_cpplint_FOUND TRUE)
ament_lint_auto_find_test_dependencies()
endif()
ament_package()
修改topic_pkg目录下面的package.xml
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>topic_pkg</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="saw@todo.todo">saw</maintainer>
<license>TODO: License declaration</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<depend>rclcpp</depend>
<depend>std_msgs</depend>
<depend>interface_pkg</depend> <!--告诉colcon,编译之前要确保有interface_pkg存在-->
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>
分别开两个终端编译测试一下
colcon build --packages-select topic_pkg
source install/setup.bash
ros2 run topic_pkg topic_interface_pub
source install/setup.bash
ros2 run topic_pkg topic_interface_sub
可以看到测试成功,发布者成功发布消息,订阅者成功订阅到消息。