6.ROS2中自定义接口
文章目录
在ROS2中接口interface
是一种定义消息、服务或动作的规范,用于描述数据结构、字段和数据类型。ROS2中的接口可以分为以下的几种消息类型:
- 消息接口:消息接口定义了一种数据结构,用于在ROS 2节点之间传递信息。消息接口通常用于发布者(publishers)和订阅者(subscribers)之间的通信。消息接口由一组字段组成,每个字段都有一个名称和一个数据类型。ROS 2使用消息接口来实现发布-订阅模式。
- 服务接口:服务接口定义了一种客户端(client)和服务器(server)之间的通信协议。服务接口由请求(request)和响应(response)两部分组成。客户端发送请求给服务器,并等待服务器返回响应。服务接口在ROS 2中用于实现请求-响应模式。
- 动作接口:动作接口是ROS 2中的一种高级通信模式,它扩展了服务接口,允许在执行期间传输连续的反馈信息。动作接口由一个目标(goal)、一个反馈(feedback)和一个结果(result)组成。客户端向服务器发送目标,服务器执行相应的操作,并提供反馈信息。动作接口用于实现高级的行为控制和任务执行。
接口在ROS2中以.msg
、.srv
和.action
文件的形式定义,分别对应消息、服务和动作。这些文件包含了接口的定义,包括字段名称和数据类型。通过使用接口,ROS2节点可以进行灵活的通信,并与其他节点共享数据和执行任务。
6.1接口常用的CLI
(1)查看接口列表
bash
ros2 interface list
(2)查看某个具体接口的内容
bash
ros2 interface show xxxx
6.2标准的接口形式
下面对三种接口类型.msg
、.srv
、.action
都进行举例说明
(1)消息Message
plaintext
int32 x
int32 y
这个消息定义了两个int32
的字段x
和y
,
(2)服务Service
plaintext
int32 a
int32 b
---
int32 sum
这个服务定义了一个请求包含两个整型字段a
和b
,以及一个响应包含一个整型字段sum
。
(3)动作Action
plaintext
int32 order
---
int32 progress
---
int32 result
这个动作定义了一个目标包含一个整型字段order
,一个反馈包含一个整型字段progress
,以及一个结果包含一个整型字段result
。
6.3接口的数据类型
- 基本数据类型 :
- 整型:
int8
,int16
,int32
,int64
,uint8
,uint16
,uint32
,uint64
- 浮点型:
float32
,float64
- 布尔型:
bool
- 字符型:
char
- 整型:
- 数组和序列 :
- 数组:使用方括号表示,例如
int32[3]
表示包含3个int32
元素的数组。 - 序列:使用尖括号表示,例如
std_msgs/String[]
表示包含多个std_msgs/String
消息的序列。
- 数组:使用方括号表示,例如
- 字符串 :
- 字符串类型:
string
表示一个字符串。
- 字符串类型:
- 时间和持续时间 :
- 时间:
builtin_interfaces/Time
表示一个时间戳。 - 持续时间:
builtin_interfaces/Duration
表示一个时间间隔。
- 时间:
- 其他消息类型 :
- 其他消息类型:你可以使用其他消息类型作为字段类型,以创建更复杂的消息结构。例如,
geometry_msgs/Point
表示一个三维点的消息类型。
- 其他消息类型:你可以使用其他消息类型作为字段类型,以创建更复杂的消息结构。例如,
6.4自定义接口
这里我的工作空间名为colcon_test_ws
,我们首先在这个工作空间目录下创建一个新的功能包custom_interfaces
bash
ros2 pkg create custom_interfaces --build-type ament_cmake --license Apache-2.0 --dependencies rosidl_default_generators
进入功能包,然后创建msg
和srv
目录
bash
cd custom_interfaces
mkdir msg srv
目录结构如下:
bash
.
├── CMakeLists.txt
├── include
│ └── custom_interfaces
├── LICENSE
├── msg
├── package.xml
├── src
└── srv
5 directories, 3 files
(1)自定义构建msg
进入custom_interfaces/msg
新建一个Num.msg
文件,然后写入以下内容:
msg
int64 num
这里构建了一个自定义的消息,消息的内容是64整型的int
(2)自定义构建srv
进入custom_interfaces/srv
新建一个AddThreeInts.srv
文件,然后写入以下内容:
srv
int64 a
int64 b
int64 c
---
int64 sum
这里构建了一个自定义的服务消息,request包含三个数a
、b
、c
,response包含一个数sum
(3)修改配置文件CMakeLists.txt
cmake
find_package(ament_cmake REQUIRED)
find_package(rosidl_default_generators REQUIRED)
rosidl_generate_interfaces(${PROJECT_NAME}
"msg/Num.msg"
"srv/AddThreeInts.srv"
DEPENDENCIES # Add packages that above messages depend on
)
(4)修改配置文件package.xml
xml
<buildtool_depend>ament_cmake</buildtool_depend>
<!-- 添加以下三行 -->
<build_depend>rosidl_default_generators</build_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<member_of_group>rosidl_interface_packages</member_of_group>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
然后进行编译
bash
colcon build --packages-select example_custom_interfaces
然后查看自定义的消息接口
source install/setup.bash
ros2 interface show example_custom_interfaces/msg/Num
Output:
bash
int64 num
再输入:
bash
ros2 interface show example_custom_interfaces/srv/AddThreeInts
Output:
bash
int64 a
int64 b
int64 c
---
int64 sum
这样我们就能够在colcon_ws/install/custom_interfaces/include/example_custom_interfaces/example_custom_interfaces/msg/num.hpp
看到编译好的msg
头文件了,在colcon_test_ws/install/custom_interfaces/include/example_custom_interfaces/example_custom_interfaces/srv/add_three_ints.hpp
中看到编译好的srv
头文件
这里可以使用自定义的服务接口类型,把【ROS2笔记五】ROS2服务通信中使用的服务数据类型修改为自定义的,如下:
service_client_01.cpp
cpp
#include "rclcpp/rclcpp.hpp"
#include "custom_interfaces/srv/add_three_ints.hpp"
class ServiceClient01: public rclcpp::Node{
public:
ServiceClient01(std::string name) : Node(name){
RCLCPP_INFO(this->get_logger(), "Node: %s has been launched", name.c_str());
// 创建客户端
client_ = this->create_client<custom_interfaces::srv::AddThreeInts>("add_two_ints_srv");
}
void send_request(int a, int b, int c){
RCLCPP_INFO(this->get_logger(), "Calculate %d + %d + %d", a, b, c);
// 等待服务上线
while (!client_->wait_for_service(std::chrono::seconds(1))){
if (!rclcpp::ok()){
RCLCPP_ERROR(this->get_logger(), "Waiting for service to be interrupted");
return;
}
RCLCPP_INFO(this->get_logger(), "Waiting for service");
}
auto request = std::make_shared<custom_interfaces::srv::AddThreeInts_Request>();
request->a = a;
request->b = b;
request->c = c;
client_->async_send_request(
request, std::bind(&ServiceClient01::result_callback_, this, std::placeholders::_1));
}
private:
// 声明客户端
rclcpp::Client<custom_interfaces::srv::AddThreeInts>::SharedPtr client_;
void result_callback_(
rclcpp::Client<custom_interfaces::srv::AddThreeInts>::SharedFuture result_future){
auto response = result_future.get();
RCLCPP_INFO(this->get_logger(), "Result: %ld", response->sum);
}
};
int main(int argc, char** argv){
rclcpp::init(argc, argv);
auto node = std::make_shared<ServiceClient01>("service_client_01");
// 调用服务
node->send_request(5, 6, 7);
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
service_server_01.cpp
cpp
#include <memory>
#include "rclcpp/rclcpp.hpp"
#include "custom_interfaces/srv/add_three_ints.hpp"
class ServiceServer01: public rclcpp::Node{
public:
ServiceServer01(std::string name) : Node(name){
RCLCPP_INFO(this->get_logger(), "Node: %s has been launched", name.c_str());
// 创建服务
add_ints_server_ = this->create_service<custom_interfaces::srv::AddThreeInts>(
"add_two_ints_srv",
std::bind(&ServiceServer01::handle_add_three_ints, this,
std::placeholders::_1, std::placeholders::_2));
}
private:
// 在私有域中再次声明服务
rclcpp::Service<custom_interfaces::srv::AddThreeInts>::SharedPtr add_ints_server_;
// 服务的处理函数
void handle_add_three_ints(
const std::shared_ptr<custom_interfaces::srv::AddThreeInts::Request> request,
std::shared_ptr<custom_interfaces::srv::AddThreeInts::Response> response){
RCLCPP_INFO(this->get_logger(), "Recieve a: %ld b: %ld c: %ld", request->a, request->b, request->c);
response->sum = request->a + request->b + request->c;
};
};
int main(int argc, char** argv){
rclcpp::init(argc, argv);
auto node = std::make_shared<ServiceServer01>("service_server_01");
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
修改CMakeLists.txt
cmake
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
# 新加这一行
find_package(custom_interfaces REQUIRED)
add_executable(service_server_01 src/service_server_01.cpp)
ament_target_dependencies(service_server_01 rclcpp custom_interfaces) #修改这里
add_executable(service_client_01 src/service_client_01.cpp)
ament_target_dependencies(service_client_01 rclcpp custom_interfaces) #修改这里
install(TARGETS
service_server_01
service_client_01
DESTINATION lib/${PROJECT_NAME}
)
然后编译,运行
bash
colcon build --packages-select example_service_rclcpp
source install/setup.bash
ros2 run example_service_rclcpp service_client_01
ros2 run example_service_rclcpp service_server_01
结果如下:
Reference
[1]d2lros2