官方文档:Interfaces — ROS 2 Documentation: Humble documentation
ROS 2使用简化的描述语言,即接口定义语言( interface definition language,IDL)来描述这些接口。此描述使ROS工具可以轻松地以几种目标语言为接口类型生成源代码 (自动跨语言)。。不同通信接口,接口文件后缀不同,
话题(.msg),
服务(.srv),
动作(.action)
接口语言定义语法
所有的语言,都有统一的内置类型(built-in-type)表示
也可单独定义类型名称,例如“ geometry_msgs/posestamped”
内部固有类型 (与跨语言对应关系)
Type name | |||
---|---|---|---|
bool | bool | builtins.bool | boolean |
byte | uint8_t | builtins.bytes* | octet |
char | char | builtins.int* | char |
float32 | float | builtins.float* | float |
float64 | double | builtins.float* | double |
int8 | int8_t | builtins.int* | octet |
uint8 | uint8_t | builtins.int* | octet |
int16 | int16_t | builtins.int* | short |
uint16 | uint16_t | builtins.int* | unsigned short |
int32 | int32_t | builtins.int* | long |
uint32 | uint32_t | builtins.int* | unsigned long |
int64 | int64_t | builtins.int* | long long |
uint64 | uint64_t | builtins.int* | unsigned long long |
string | std::string | builtins.str | string |
wstring | std::u16string | builtins.str | wstring |
话题(topics 发布/订阅信息模型)
消息是ROS 2节点将网络上的数据发送到其他ROS节点的一种方式,而没有响应(单向信息传递)。例如,如果一个ROS 2节点从传感器读取温度数据,则可以使用温度消息在ROS 2网络上发布该数据。 ROS 2网络上的其他节点可以订阅该数据并接收温度消息。
每个字段由一个类型和名称组成,被空格隔开,即
fieldtype1 fieldname1
fieldtype2 fieldname2
例如
int32 my_int
string my_string
带有默认值的示例
uint8 x 42
int16 y -2000
string full_name "John Doe"
int32[] samples [-200, -100, 0, 100, 200]
常数示例,无法更改(有=号)
int32 X=123
int32 Y=-123
string FOO="foo"
string EXAMPLE='bar'
服务接口
在ROS软件包的 srv/ 路径中,在.srv文件中描述和定义了服务。
服务描述文件由请求和响应MSG类型组成,该类型由使用 --- 区分
语法
string str
---
string str
示例
# request constants
int8 FOO=1
int8 BAR=2
# request fields
int8 foobar
another_pkg/AnotherMessage msg
---
# response constants
uint32 SECRET=123456
# response fields
another_pkg/YetAnotherMessage val
CustomMessageDefinedInThisPackage value
uint32 an_integer
动作通信接口
有三个参数需要定义
使用2个 --- 和 --- 区分
<request_type> <request_fieldname>
---
<response_type> <response_fieldname>
---
<feedback_type> <feedback_fieldname>
示例
int32 order
---
int32[] sequence
---
int32[] sequence
二、创建接口代码
Creating custom msg and srv files — ROS 2 Documentation: Humble documentation
创建工程空间
mkdir -p learn_ws/src
cd learn_ws/src
创建名字为tutorial_inferfaces软件包
ros2 pkg create --build-type ament_cmake tutorial_interfaces
创建接口
接口文件,要求单独放在 msg srv的文件夹
mkdir msg srv
ws_zxy/src/tutorial_interfaces/msg/Num.msg
int64 num
cmake
ws_zxy/src/tutorial_interfaces/CMakeLists.txt
cmake_minimum_required(VERSION 3.8)
project(tutorial_interfaces)
find_package(geometry_msgs REQUIRED) # geometry_msgs 是 ROS 的标准几何消息包,提供如 Point、Pose 等预定义消息类型
find_package(rosidl_default_generators REQUIRED) # rosidl_default_generators 包提供了从 .msg、.srv 等文件生成 C++/Python 代码的功能
find_package(tutorial_interfaces REQUIRED) # CHANGE
rosidl_generate_interfaces(${PROJECT_NAME}
"msg/Num.msg"
#"msg/Sphere.msg"
#"srv/AddThreeInts.srv"
#DEPENDENCIES geometry_msgs #声明自定义接口依赖的外部包(例如 Sphere.msg 可能引用了 geometry_msgs/Point 类型)
)
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)
# uncomment the following section in order to fill in
# further dependencies manually.
# find_package(<dependency> REQUIRED)
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()
package
ws_zxy/src/tutorial_interfaces/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>tutorial_interfaces</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="452366037@qq.com">zengxy</maintainer>
<license>TODO: License declaration</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
<depend>geometry_msgs</depend>
<buildtool_depend>rosidl_default_generators</buildtool_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<member_of_group>rosidl_interface_packages</member_of_group>
</package>
编译
colcon build --packages-select tutorial_interfaces
三、创建Cpp业务代码,调用编译后接口
Creating custom msg and srv files — ROS 2 Documentation: Humble documentation
创建功能包
ros2 pkg create --build-type ament_cmake cpp_pubsub
发布者代码
ws_zxy/src/cpp_pubsub/src/msg_publish.cpp
#include <chrono> //用于处理时间相关操作(如 500ms 定时器周期)
#include <memory>
#include "rclcpp/rclcpp.hpp"
#include "tutorial_interfaces/msg/num.hpp" // CHANGE
using namespace std::chrono_literals;
class MinimalPublisher : public rclcpp::Node // 继承 rclcpp::Node 是 ROS 2 节点的标准实现方式
{
public:
// 构造函数的参数列表,以冒号 : 开头.后跟以逗号分隔的成员变量初始值对
MinimalPublisher()
: Node("minimal_publisher"), count_(0)
{
//msg 消息类型使用驼峰命名
publisher_ = this->create_publisher<tutorial_interfaces::msg::Num>("topic", 10); // msg create_publisher
timer_ = this->create_wall_timer(
500ms, std::bind(&MinimalPublisher::timer_callback, this));
}
private:
void timer_callback()
{
//消息对象应使用栈分配(auto message)避免动态内存开销
auto message = tutorial_interfaces::msg::Num();
message.num = this->count_++;
RCLCPP_INFO_STREAM(this->get_logger(), "Publishing: '" << message.num << "'"); // msg
publisher_->publish(message);
}
rclcpp::TimerBase::SharedPtr timer_;
rclcpp::Publisher<tutorial_interfaces::msg::Num>::SharedPtr publisher_;
size_t count_;
};
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<MinimalPublisher>()); // spin 进入事件循环,保持节点活跃
rclcpp::shutdown();
return 0;
}
订阅者代码
ws_zxy/src/cpp_pubsub/src/msg_sub.cpp
#include <functional>
#include <memory>
#include "rclcpp/rclcpp.hpp"
#include "tutorial_interfaces/msg/num.hpp" // CHANGE
using std::placeholders::_1; //占位符用于回调参数绑定
class MinimalSubscriber : public rclcpp::Node
{
public:
MinimalSubscriber()
: Node("minimal_subscriber")
{
//
subscription_ = this->create_subscription<tutorial_interfaces::msg::Num>( // CHANGE
"topic", 10, std::bind(&MinimalSubscriber::topic_callback, this, _1));
}
private:
void topic_callback(const tutorial_interfaces::msg::Num & msg) const // CHANGE
{
RCLCPP_INFO_STREAM(this->get_logger(), "I heard: '" << msg.num << "'"); // CHANGE
}
rclcpp::Subscription<tutorial_interfaces::msg::Num>::SharedPtr subscription_; // CHANGE
};
int main(int argc, char * argv[])
{
// 初始化ROS2节点
rclcpp::init(argc, argv);
// 运行节点,直到节点被关闭
rclcpp::spin(std::make_shared<MinimalSubscriber>());
// 关闭ROS2节点
rclcpp::shutdown();
return 0;
}
Cmakelist
ws_zxy/src/cpp_pubsub/CMakeLists.txt
#...
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(tutorial_interfaces REQUIRED) # 引入接口
add_executable(talker src/msg_publish.cpp)
ament_target_dependencies(talker rclcpp tutorial_interfaces) # CHANGE
add_executable(listener src/msg_sub.cpp)
ament_target_dependencies(listener rclcpp tutorial_interfaces) # CHANGE
install(TARGETS
talker
listener
DESTINATION lib/${PROJECT_NAME})
ament_package()
cmake_minimum_required(VERSION 3.8)
project(cpp_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(tutorial_interfaces REQUIRED) # interface
# uncomment the following section in order to fill in
# further dependencies manually.
# find_package(<dependency> REQUIRED)
add_executable(talker src/msg_publish.cpp)
ament_target_dependencies(talker rclcpp tutorial_interfaces) # CHANGE
add_executable(listener src/msg_sub.cpp)
ament_target_dependencies(listener rclcpp tutorial_interfaces) # CHANGE
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()
#
# install
#
install(TARGETS
talker
listener
DESTINATION lib/${PROJECT_NAME})
ament_package()
package
<depend>tutorial_interfaces</depend>
<?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>cpp_pubsub</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="452366037@qq.com">zengxy</maintainer>
<license>TODO: License declaration</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
<depend>tutorial_interfaces</depend>
</package>
编译和添加环境
colcon build --packages-select cpp_pubsub
执行代码
发布者
source install/setup.bash # ws_zxy
ros2 run cpp_pubsub talker
接受者
source install/setup.bash # ws_zxy
ros2 run cpp_pubsub listener