ros2 服务——ubuntu20.04——自定义数据类型

文章目录

  • 自定义一个服务数据类型接口
    • 创建sev目录和文件
    • 修改包的CMakeLists.txt文件
    • 修改包的package.xml文件
    • 查看是否成功
  • 服务
  • 全部代码

自定义一个服务数据类型接口

创建sev目录和文件

服务的接口类型由两部分组成,请求和相应

在包的src的同级目录下创建sev文件夹,在sev文件夹里添加Xxx.srv文件(文件名首字母必须大写,不能有下划线)

  • 请求和相应用—隔离开,上面是请求的数据类型,下面是相应的数据类型
# 文件名Borrow.srv
#request
string name
uint32 money
---
# response
bool success
uint32 money

# 文件名SellNovel.srv
uint32 money
---
string[] novel

修改包的CMakeLists.txt文件

需要在CMakeLists.txt文件中添加rosidl_generate_interfaces表明srv文件所在的目录,如果有添加的数据类型需要添加find_package

# 如果有添加的数据类型需要添加find_package
find_package(std_msgs REQUIRED)

# rosidl_generate_interfaces表明srv文件所在的目录
rosidl_generate_interfaces(${PROJECT_NAME}
  "srv/Borrow.srv"
  "srv/SellNovel.srv"
  DEPENDENCIES 
)

修改包的package.xml文件

需要添加的

  
  
  <exec_depend>rosidl_default_runtimeexec_depend>
  <member_of_group>rosidl_interface_packagesmember_of_group>

查看是否成功

使用命令来查看ros2 interface list,命令查看看到三个数据类型,表示在service_interface包里面srv目录下的Borrow类型

在这里插入图片描述

服务

  • 同一个服务(名称相同)只能由有一个节点来提供
  • 同一个服务可以被多个客户端调用
  1. 导入自定义的服务接口(需先在package.xml中添加自定义包的依赖)
  • 修改packag.xml文件
  
  <depend>service_interfacedepend> 
  • 修改CMakeLists.txt文件
//添加service_interface
find_package(service_interface REQUIRED)

// 添加service_interface
ament_target_dependencies(
  my_services
  "std_msgs"
  "rclcpp"
  service_interface
)

// 添加数据类型的头文件
// service_interface包的srv目录下有两个数据类型文件Borrow.srv和Buy.srv

// 导入的头文件都必须是小写
// srv文件名:Borrow.srv SellNovel.srv
#include "service_interface/srv/borrow.hpp"
#include "service_interface/srv/sell_novel.hpp"
  1. 创建服务端回调函数

对调函数是当服务端接受到数据做出反应的函数,参数是两个之只能指针

#include "service_interface/srv/sell_novel.hpp"

  // 声明一个回调函数,用于接受导数据并做出相应
  // 参数有两个一个是请求,一个是回应
  //类型是共享指针类型,书写格式如下
  void callback(const service_interface::srv::SellNovel::Request::SharedPtr request,
                const service_interface::srv::SellNovel::Response::SharedPtr response
  )
  {
    unsigned int num = (int)request->money/(1.0);
    if(num>novel_queue.size())
    {
      // 等待
      RCLCPP_INFO(this->get_logger(),"书不够,书库有:%d,要卖出:%d",novel_queue.size(),num);

      // 设置睡眠的时间为一秒
      rclcpp::Rate rate(1);
      while (novel_queue.size()<num)
      {
        RCLCPP_INFO(this->get_logger(),"等待中,还差:%d本书",novel_queue.size());
        // 循环中睡眠一秒
        rate.sleep();
      }
      

    }
    RCLCPP_INFO(this->get_logger(),"当前图书的数量有:%d,大于要卖出去的数量:%d",novel_queue.size(),num);
    for(int i=0;i<(int)num;i++)
    {
      response->novel.push_back(novel_queue.front());
      novel_queue.pop();
    }
  }

在定义回调函数时,我们为了不影响主线程的顺序执行,才用多线程的方式来处理一些回调函数中的流程

  1. 声明回调组
// 类中声明回调函数组-------------------
  // ros2中使用多线程执行器和回调组来实现多线程,我们先在类中声明一个回调组成员变量
  rclcpp::CallbackGroup::SharedPtr callback_group;

// 构造函数中初始化回调组----------------------------
  //  自定义函数回调组
  callback_group = this->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);

  1. 修改主函数有单线程执行器变为多线程执行器
// 单线程的主函数
int main(int argc, char ** argv)
{
    //2.初始化客户端库
    rclcpp::init(argc,argv);
    //3.使用智能指针创建新的节点对象,在面相对象的这节点声明,智能指针就要输入类的类型,而不是rclcpp::Node
    auto node = std::make_shared<my_server>("ros2");

    //4.使用spin循环节点
    rclcpp::spin(node);
    //5. 关闭客户端库
    rclcpp::shutdown();
}

// 多线程主函数
int main(int argc, char ** argv)
{
    //2.初始化客户端库
    rclcpp::init(argc,argv);
    //3.使用智能指针创建新的节点对象,在面相对象的这节点声明,智能指针就要输入类的类型,而不是rclcpp::Node
    auto node = std::make_shared<my_server>("ros2");

    rclcpp::executors::MultiThreadedExecutor executor;
    executor.add_node(node); // 第一个线程节点
    executor.add_node(node); // 第二个线程节点
    executor.spin();
    rclcpp::shutdown();
}
  1. 声明并创建服务端
// 类中定义服务-------------
  //  自定义函数回调组
  callback_group = this->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
  // 服务端的声明,没有初始化,在构造函数中初始化
  rclcpp::Service<service_interface::srv::SellNovel>::SharedPtr services_pub;

// 构造函数中声明服务-------------
  services_pub = this->create_service<service_interface::srv::SellNovel>("service_name",
                                                                          std::bind(&my_server::callback,this,_1,_2),
                                                                          rmw_qos_profile_services_default,
                                                                          callback_group);

  • 第一个参数:发布的服务名称
  • 第二个参数:bind函数将成员函数作为回调函数
  • 第三个参数:就是默认值,但是我们要给第四个参数赋值,所以就不能跳过第三个参数
  • 第四个参数:回调函数组

有了第四个参数,函数回调组之后,当接收到客户端的函数请求之后,就会在callback_group函数回调组里面执行callback函数。

全部代码

#include 
#include 
#include 

#include "rclcpp/rclcpp.hpp"
#include "service_interface/srv/borrow.hpp"
#include "service_interface/srv/sell_novel.hpp"

using std::placeholders::_1;
using std::placeholders::_2;

class my_server :public rclcpp::Node
{
private:

  // 存放二手书的一个队列容器
  std::queue<std::string> novel_queue; 

  // ros2中使用多线程执行器和回调组来实现多线程,我们先在类中声明一个回调组成员变量
  rclcpp::CallbackGroup::SharedPtr callback_group;
  
  // 服务端的声明,没有初始化,在构造函数中初始化
  rclcpp::Service<service_interface::srv::SellNovel>::SharedPtr services_pub;

  // 声明一个回调函数,用于接受导数据并做出相应
  // 参数有两个一个是请求,一个是回应
  //类型是共享指针类型,书写格式如下
  void callback(const service_interface::srv::SellNovel::Request::SharedPtr request,
                const service_interface::srv::SellNovel::Response::SharedPtr response
  )
  {
    unsigned int num = (int)request->money/(1.0);
    if(num>novel_queue.size())
    {
      // 等待
      RCLCPP_INFO(this->get_logger(),"书不够,书库有:%d,要卖出:%d",novel_queue.size(),num);

      // 设置睡眠的时间为一秒
      rclcpp::Rate rate(1);
      while (novel_queue.size()<num)
      {
        RCLCPP_INFO(this->get_logger(),"等待中,还差:%d本书",novel_queue.size());
        // 循环中睡眠一秒
        rate.sleep();
      }
      

    }
    RCLCPP_INFO(this->get_logger(),"当前图书的数量有:%d,大于要卖出去的数量:%d",novel_queue.size(),num);
    for(int i=0;i<(int)num;i++)
    {
      response->novel.push_back(novel_queue.front());
      novel_queue.pop();
    }
  }
public:
 my_server(std::string name);
 ~my_server();
};
 my_server :: my_server(std::string name):Node(name)
{
  RCLCPP_INFO(this->get_logger(),"构造 service");
  //  自定义函数回调组
  callback_group = this->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
  // 对服务对象进行实例化
  services_pub = this->create_service<service_interface::srv::SellNovel>("service_name",
                                                                          std::bind(&my_server::callback,this,_1,_2),
                                                                          rmw_qos_profile_services_default,
                                                                          callback_group);


}
my_server:: ~my_server()
{
  RCLCPP_INFO(this->get_logger(),"结束service");
}



int main(int argc, char ** argv)
{
    //2.初始化客户端库
    rclcpp::init(argc,argv);
    //3.使用智能指针创建新的节点对象,在面相对象的这节点声明,智能指针就要输入类的类型,而不是rclcpp::Node
    auto node = std::make_shared<my_server>("ros2");

    rclcpp::executors::MultiThreadedExecutor executor;
    executor.add_node(node); // 第一个线程节点
    // executor.add_node(node); // 第二个线程节点
    executor.spin();
    rclcpp::shutdown();
}

你可能感兴趣的:(ros2,c++,ros2,服务)