天天看点

机器人编程趣味实践08-任务请求回复(服务)

上一节的主题,适用于机器人的传感器数据传输,通信一般为单项,定义某个节点发送或者接受。

日常还有一类常见的应用比如,A:今天天气怎么样,B:今天温度20度,有小雨。

有求有答,节点分为服务器端和客户端,基础补充参考如下链接:

  • ROS 2服务-services-

背景知识

服务是ROS图中节点通信的另一种方法。

  1. 服务基于调用响应模型,而不是主题的发布者-订阅者模型。
  2. 主题允许节点订阅数据流并获得连续更新,但是服务仅在客户端专门调用它们时才提供数据。

上述是主题和服务的典型区别。引用ROS2Wiki的图示如下:

机器人编程趣味实践08-任务请求回复(服务)

一对一模式-一个服务端和一个客户端

机器人编程趣味实践08-任务请求回复(服务)

一对多模式-一个服务器端和多个客户端

预备知识

在此之前,要掌握前七讲的内容哦,当然还是以二维机器人为例吧。

掌握代码编写和机器人编程,不推荐安装功能包方式,推荐源代码编译方式。

实践任务

1 开启

开启机器人节点,之前轨迹不全,修改如下代码:

path_image_(888, 666, QImage::Format_ARGB32)           

复制

分别在两个终端运行:

机器人编程趣味实践08-任务请求回复(服务)
机器人编程趣味实践08-任务请求回复(服务)

2 服务列表

再新开第三个窗口,输入 ros2 service list ,将会出现如下内容:

机器人编程趣味实践08-任务请求回复(服务)
  1. /teleop_turtle
  2. /turtle1
  3. /turtlesim

这里,本文重点看如下这些个服务。

/clear

,

/kill

,

/reset

,

/spawn

,

/turtle1/set_pen

,

/turtle1/teleport_absolute

, 和

/turtle1/teleport_relative

3 服务消息类型

主题用主题消息通信,服务用服务消息通信。

  • .msg
  • .srv

主题位置案例Pose.msg

float32 x
float32 y
float32 theta

float32 linear_velocity
float32 angular_velocity           

复制

服务添加机器人案例Spawn.srv

float32 x
float32 y
float32 theta
string name 
---
string name           

复制

典型使用方式如下:

机器人编程趣味实践08-任务请求回复(服务)

响应服务,但不会返回消息。

列表详情

机器人编程趣味实践08-任务请求回复(服务)

ros2 service list -t

机器人编程趣味实践08-任务请求回复(服务)

4 服务查找

ros2 service find <type_name>           

复制

如std_srvs/srv/Empty

ros2 service find std_srvs/srv/Empty           

复制

机器人编程趣味实践08-任务请求回复(服务)

5 服务接口类型显示

命令如下:

这里以spawn.srv为例:

等到如下显示:

机器人编程趣味实践08-任务请求回复(服务)

使用---区分调用和应答的消息类型。

6 服务调用

通用命令格式:

机器人遍历一周:

机器人编程趣味实践08-任务请求回复(服务)

调用如下服务,清除轨迹:

机器人编程趣味实践08-任务请求回复(服务)

那些往事,转眼成空……

机器人编程趣味实践08-任务请求回复(服务)

然后,再用扩展机器人,向环境中添加机器人:

  • ros2 service call /spawn turtlesim/srv/Spawn "{x: 2, y: 2, theta: 0.2, name: 'robot1'}"
  • ros2 service call /spawn turtlesim/srv/Spawn "{x: 4, y: 2, theta: 0.4, name: 'robot2'}"
  • ros2 service call /spawn turtlesim/srv/Spawn "{x: 6, y: 2, theta: 0.6, name: 'robot3'}"
  • ros2 service call /spawn turtlesim/srv/Spawn "{x: 8, y: 2, theta: 0.8, name: 'robot4'}"
  • ros2 service call /spawn turtlesim/srv/Spawn "{x: 10, y: 2, theta: 1.0, name: 'robot5'}"
机器人编程趣味实践08-任务请求回复(服务)

调用服务生成新机器人。

机器人编程趣味实践08-任务请求回复(服务)

响应并生成了对应robot1-5。

机器人编程趣味实践08-任务请求回复(服务)

然后,服务就爆炸了:

机器人编程趣味实践08-任务请求回复(服务)

小结

节点可以使用ROS 2中的服务进行通信。与主题不同(这种方式是节点发布一个或多个订阅者可以获取信息的一种通信方式),服务则是一种请求/响应方式,其中客户端向节点发出请求。 服务器端提供服务,并且该服务处理请求并生成响应。 通常,不希望将服务用于连续通信(理解为机器人任务获取); 主题甚至行动将更适合连续通信模式。 在本教程中,使用了命令行工具来识别,详细说明和调用服务。

节点-主题-服务示意

机器人编程趣味实践08-任务请求回复(服务)

服务程序代码示例:

新建

clear_srv_ = nh_->create_service<std_srvs::srv::Empty>("clear", std::bind(&TurtleFrame::clearCallback, this, std::placeholders::_1, std::placeholders::_2));
  reset_srv_ = nh_->create_service<std_srvs::srv::Empty>("reset", std::bind(&TurtleFrame::resetCallback, this, std::placeholders::_1, std::placeholders::_2));
  spawn_srv_ = nh_->create_service<turtlesim::srv::Spawn>("spawn", std::bind(&TurtleFrame::spawnCallback, this, std::placeholders::_1, std::placeholders::_2));
  kill_srv_ = nh_->create_service<turtlesim::srv::Kill>("kill", std::bind(&TurtleFrame::killCallback, this, std::placeholders::_1, std::placeholders::_2));           

复制

实现

bool TurtleFrame::spawnCallback(const turtlesim::srv::Spawn::Request::SharedPtr req, turtlesim::srv::Spawn::Response::SharedPtr res)
{
  std::string name = spawnTurtle(req->name, req->x, req->y, req->theta);
  if (name.empty())
  {
    RCLCPP_ERROR(nh_->get_logger(), "A turtle named [%s] already exists", req->name.c_str());
    return false;
  }

  res->name = name;

  return true;
}           

复制

void TurtleFrame::clear()
{
  // make all pixels fully transparent
  path_image_.fill(qRgba(255, 255, 255, 0));
  update();
}           

复制

二维界面显示机器人

bool TurtleFrame::hasTurtle(const std::string& name)
{
  return turtles_.find(name) != turtles_.end();
}

std::string TurtleFrame::spawnTurtle(const std::string& name, float x, float y, float angle)
{
  return spawnTurtle(name, x, y, angle, rand() % turtle_images_.size());
}

std::string TurtleFrame::spawnTurtle(const std::string& name, float x, float y, float angle, size_t index)
{
  std::string real_name = name;
  if (real_name.empty())
  {
    do
    {
      std::stringstream ss;
      ss << "turtle" << ++id_counter_;
      real_name = ss.str();
    } while (hasTurtle(real_name));
  }
  else
  {
    if (hasTurtle(real_name))
    {
      return "";
    }
  }

  TurtlePtr t = std::make_shared<Turtle>(nh_, real_name, turtle_images_[static_cast<int>(index)], QPointF(x, height_in_meters_ - y), angle);
  turtles_[real_name] = t;
  update();

  RCLCPP_INFO(nh_->get_logger(), "Spawning turtle [%s] at x=[%f], y=[%f], theta=[%f]", real_name.c_str(), x, y, angle);

  return real_name;
}           

复制

-End-

继续阅读