当前位置: 首页 > news >正文

编写一个简单的服务和客户端(C++)

 

背景

当节点使用服务进行通信时,发送数据请求的节点称为客户端节点,响应请求的节点称为服务节点。请求和响应的结构由.srv文件确定。

这里使用的例子是一个简单的整数加法系统;一个节点请求两个整数之和,另一个节点响应结果。

任务

1 创建包

导航到上一教程ros2_ws中创建的目录。

回想一下,包应该在src目录中创建,而不是在工作区的根目录中。导航到ros2_ws/src并创建一个新包:

ros2 pkg create --build-type ament_cmake cpp_srvcli --dependencies rclcpp example_interfaces

cpp_srvcli您的终端将返回一条消息,验证您的包及其所有必需文件和文件夹的创建。

--dependencies参数将自动将必要的依赖行添加到package.xmlCMakeLists.txt。 是包含.srv 文件的example_interfaces包,您需要构建请求和响应:

int64 a
int64 b
---
int64 sum

前两行是请求的参数,破折号下面是响应。

1.1 更新package.xml

由于您--dependencies在包创建期间使用了该选项,因此无需手动将依赖项添加到package.xmlCMakeLists.txt

不过,与往常一样,请确保将描述、维护者电子邮件和姓名以及许可证信息添加到package.xml.

<description>C++ client server tutorial</description>
<maintainer email="you@email.com">Your Name</maintainer>
<license>Apache License 2.0</license>

2 编写服务节点

ros2_ws/src/cpp_srvcli/src目录中,创建一个名为的新文件add_two_ints_server.cpp,并将以下代码粘贴到其中:

#include "rclcpp/rclcpp.hpp"
#include "example_interfaces/srv/add_two_ints.hpp"#include <memory>void add(const std::shared_ptr<example_interfaces::srv::AddTwoInts::Request> request,std::shared_ptr<example_interfaces::srv::AddTwoInts::Response>      response)
{response->sum = request->a + request->b;RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Incoming request\na: %ld" " b: %ld",request->a, request->b);RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "sending back response: [%ld]", (long int)response->sum);
}int main(int argc, char **argv)
{rclcpp::init(argc, argv);std::shared_ptr<rclcpp::Node> node = rclcpp::Node::make_shared("add_two_ints_server");rclcpp::Service<example_interfaces::srv::AddTwoInts>::SharedPtr service =node->create_service<example_interfaces::srv::AddTwoInts>("add_two_ints", &add);RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Ready to add two ints.");rclcpp::spin(node);rclcpp::shutdown();
}
2.1 检查代码

前两条#include语句是您的包依赖项。

add函数将请求中的两个整数相加,并将总和提供给响应,同时使用日志通知控制台其状态。

void add(const std::shared_ptr<example_interfaces::srv::AddTwoInts::Request> request,std::shared_ptr<example_interfaces::srv::AddTwoInts::Response>      response)
{response->sum = request->a + request->b;RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Incoming request\na: %ld" " b: %ld",request->a, request->b);RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "sending back response: [%ld]", (long int)response->sum);
}

main函数逐行完成以下任务:

  • 初始化 ROS 2 C++ 客户端库:

    rclcpp::init(argc, argv);
    
  • 创建一个名为 的节点add_two_ints_server

    std::shared_ptr<rclcpp::Node> node = rclcpp::Node::make_shared("add_two_ints_server");
    
  • 创建一个以add_two_ints该节点命名的服务,并使用以下方法自动在网络上通告它&add

    rclcpp::Service<example_interfaces::srv::AddTwoInts>::SharedPtr service =
    node->create_service<example_interfaces::srv::AddTwoInts>("add_two_ints", &add);
    
  • 准备好后打印一条日志消息:

    RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Ready to add two ints.");
    
  • 旋转节点,使服务可用。

    rclcpp::spin(node);
    
2.2 添加可执行文件

add_executable宏生成一个可执行文件,您可以使用 运行。添加以下代码块以创建名为 的可执行文件:ros2 runCMakeLists.txtserver

add_executable(server src/add_two_ints_server.cpp)
ament_target_dependencies(server
rclcpp example_interfaces)

因此可以找到可执行文件,将以下行添加到文件末尾之前:ros2 runament_package()

install(TARGETS
  server
  DESTINATION lib/${PROJECT_NAME})

您现在可以构建包,获取本地安装文件并运行它,但我们首先创建客户端节点,以便您可以看到完整的系统在工作。

3 编写客户端节点

ros2_ws/src/cpp_srvcli/src目录中,创建一个名为的新文件add_two_ints_client.cpp,并将以下代码粘贴到其中:

#include "rclcpp/rclcpp.hpp"
#include "example_interfaces/srv/add_two_ints.hpp"#include <chrono>
#include <cstdlib>
#include <memory>using namespace std::chrono_literals;int main(int argc, char **argv)
{rclcpp::init(argc, argv);if (argc != 3) {RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "usage: add_two_ints_client X Y");return 1;}std::shared_ptr<rclcpp::Node> node = rclcpp::Node::make_shared("add_two_ints_client");rclcpp::Client<example_interfaces::srv::AddTwoInts>::SharedPtr client =node->create_client<example_interfaces::srv::AddTwoInts>("add_two_ints");auto request = std::make_shared<example_interfaces::srv::AddTwoInts::Request>();request->a = atoll(argv[1]);request->b = atoll(argv[2]);while (!client->wait_for_service(1s)) {if (!rclcpp::ok()) {RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "Interrupted while waiting for the service. Exiting.");return 0;}RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "service not available, waiting again...");}auto result = client->async_send_request(request);// Wait for the result.if (rclcpp::spin_until_future_complete(node, result) ==rclcpp::FutureReturnCode::SUCCESS){RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Sum: %ld", result.get()->sum);} else {RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "Failed to call service add_two_ints");}rclcpp::shutdown();return 0;
}
3.1 检查代码

与服务节点类似,以下代码行创建节点,然后为该节点创建客户端:

std::shared_ptr<rclcpp::Node> node = rclcpp::Node::make_shared("add_two_ints_client");
rclcpp::Client<example_interfaces::srv::AddTwoInts>::SharedPtr client =node->create_client<example_interfaces::srv::AddTwoInts>("add_two_ints");

接下来,创建请求。它的结构是由.srv前面提到的文件定义的。

auto request = std::make_shared<example_interfaces::srv::AddTwoInts::Request>();
request->a = atoll(argv[1]);
request->b = atoll(argv[2]);

while循环给客户端 1 秒的时间来搜索网络中的服务节点。如果找不到,它将继续等待。

RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "service not available, waiting again...");

如果客户端被取消(例如,通过您进入Ctrl+C终端),它将返回一条错误日志消息,表明它被中断。

RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "Interrupted while waiting for the service. Exiting.");return 0;

然后客户端发送请求,节点旋转直到收到响应或失败。

3.2 添加可执行文件

返回CMakeLists.txt为新节点添加可执行文件和目标。从自动生成的文件中删除一些不必要的样板后,您的文件CMakeLists.txt应该如下所示:

cmake_minimum_required(VERSION 3.5)
project(cpp_srvcli)find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(example_interfaces REQUIRED)add_executable(server src/add_two_ints_server.cpp)
ament_target_dependencies(server
  rclcpp example_interfaces)add_executable(client src/add_two_ints_client.cpp)
ament_target_dependencies(client
  rclcpp example_interfaces)install(TARGETS
  server
  client
  DESTINATION lib/${PROJECT_NAME})ament_package()

4 构建并运行

在构建之前,最好rosdep在工作区的根目录 ( ros2_ws) 中运行以检查是否缺少依赖项:

Linux苹果系统视窗
rosdep install -i --from-path src --rosdistro galactic -y

导航回工作区的根目录,ros2_ws并构建新包:

Linux苹果系统视窗
colcon build --packages-select cpp_srvcli

打开一个新终端,导航到ros2_ws并获取安装文件:

Linux苹果系统视窗
. install/setup.bash

现在运行服务节点:

ros2 run cpp_srvcli server

终端应返回以下消息,然后等待:

[INFO] [rclcpp]: Ready to add two ints.

打开另一个终端,再次从内部获取安装文件ros2_ws。启动客户端节点,后跟任意两个以空格分隔的整数:

ros2 run cpp_srvcli client 2 3

例如,如果您选择23,客户端将收到如下响应:

[INFO] [rclcpp]: Sum: 5

返回到运行服务节点的终端。您将看到它在收到请求和收到的数据以及发回的响应时发布了日志消息:

[INFO] [rclcpp]: Incoming request
a: 2 b: 3
[INFO] [rclcpp]: sending back response: [5]

在服务器终端中输入Ctrl+C以停止节点旋转。

相关文章:

编写一个简单的服务和客户端(C++)

背景 当节点使用服务进行通信时&#xff0c;发送数据请求的节点称为客户端节点&#xff0c;响应请求的节点称为服务节点。请求和响应的结构由.srv文件确定。 这里使用的例子是一个简单的整数加法系统&#xff1b;一个节点请求两个整数之和&#xff0c;另一个节点响应结果。 …...

InseRF: 文字驱动的神经3D场景中的生成对象插入

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…...

类厂,变长参数,序列化

目的 在记录nav2中的各类信息,保存到文件中,以便后面回放来分析算法的编程中发现。 各种信息记录的数据不同,可能还会有变化,所以决定采用类厂模式,参见C++设计模式入门 记录的基类 有个信息记录的基类,不同的记录对应不同的子类。 enum rcdType{RT_NA,RT_nav2Info,R…...

LLK的2023年度总结

文章目录 一月二月三月四月五月六月七月八月九月十月十一月十二月 一月 此时的俺还在沉浸在蓝桥杯的练习和女朋友的甜蜜期&#xff0c;感觉没啥大长进。然后荣幸地知道了自己C语言实验因为某种非技术原因而挂科了。此时自己地目标由保研自然地转换到考研比赛就业的方向了。接着…...

Redis-浅谈redis.conf配置文件

Redis.conf Redis.conf是Redis的配置文件&#xff0c;它包含了一系列用于配置Redis服务器行为和功能的选项。 以下是Redis.conf中常见的一些选项配置&#xff1a; bind: 指定Redis服务器监听的IP地址&#xff0c;默认为127.0.0.1&#xff0c;表示只能本地访问&#xff0c;可以…...

【liunx】线程池+单例模式+STL,智能指针和线程安全+其他常见的各种锁+读者写者问题

线程池单例模式STL,智能指针和线程安全其他常见的各种锁读者写者问题 1.线程池2.线程安全的单例模式3.STL,智能指针和线程安全4.其他常见的各种锁4.读者写者问题 喜欢的点赞&#xff0c;收藏&#xff0c;关注一下把&#xff01; 1.线程池 目前我们学了挂起等待锁、条件变量、信…...

Golang的API项目快速开始

开启一个简单的API服务。 golang的教程网上一大堆&#xff0c;官网也有非常详细的教程&#xff0c;这里不在赘述这些基础语法教程&#xff0c;我们意在快速进入项目开发阶段。 golang好用语法教程传送门&#xff1a; m.runoob.com/go/ 编写第一个API 前提&#xff1a;按照上一…...

机器学习_实战框架

文章目录 介绍机器学习的实战框架1.定义问题2.收集数据和预处理(1).收集数据(2).数据可视化(3).数据清洗(4).特征工程(5).构建特征集和标签集(6).拆分训练集、验证集和测试集。 3.选择算法并建立模型4.训练模型5.模型的评估和优化 介绍机器学习的实战框架 一个机器学习项目从开…...

Java8常用新特性

目录 简介 1.默认方法 2..Lambda表达式 3.Stream API 4.方法引用 5.Optional类 简介 Java 8是Java编程语言的一个重要版本&#xff0c;引入了许多令人兴奋和强大的新特性。这些特性使得Java程序更加现代化、灵活和高效。让我们一起来探索一些Java 8的常用新特性吧&#…...

Go语言中的Channel

1. 简介 Channel是Go语言中一种重要的并发原语&#xff0c;它允许goroutine之间安全地交换数据。Channel是一个类型化的队列&#xff0c;它可以存储一个特定类型的值。goroutine可以通过发送和接收操作来向channel中写入和读取数据。 2. Channel的类型 Channel的类型由其元素…...

Unity中URP下实现深度贴花

文章目录 前言一、场景设置二、实现思路1、通过深度图求出像素所在视图空间的Z值2、通过模型面片的求出像素在观察空间下的坐标值3、结合两者求出 深度图中像素的 XYZ值4、再将此坐标转换到模型的本地空间&#xff0c;把XY作为UV来进行纹理采样 三、URP下实现1、通过深度图求出…...

openssl3.2 - 官方demo学习 - cipher - aesccm.c

文章目录 openssl3.2 - 官方demo学习 - cipher - aesccm.c概述笔记END openssl3.2 - 官方demo学习 - cipher - aesccm.c 概述 aesccm.c 是 AES-192-CCM 的加解密应用例子, 用的EVP接口. 看到不仅仅要用到key, iv, data, 在此之前还要设置 nonce, tag, 认证数据. 为啥需要设置…...

点云从入门到精通技术详解100篇-基于多传感器融合的智能汽车 环境感知(下)

目录 基于激光雷达点云的目标检测 4.1 点云神经网络检测模型 4.2 点云预处理...

蓝桥杯单片机组备赛——蜂鸣器和继电器的基本控制

文章目录 一、蜂鸣器和继电器电路介绍二、题目与答案2.1 题目2.2 答案2.3 重点函数解析 一、蜂鸣器和继电器电路介绍 可以发现两个电路一端都接着VCC&#xff0c;所以我们只要给另一端接上低电平就可以让蜂鸣器和继电器进行工作。与操作LED类似&#xff0c;只不过换了一个74HC5…...

嵌入式linux 编译qt5(以v851s为例)

本文参考Blev大神的博客&#xff1a;Yuzuki Lizard V851S开发板 --移植 QT5.12.9教程&#xff08;群友Blev提供&#xff09; - Allwinner / 柚木PI-V851S - 嵌入式开发问答社区 (100ask.net) 一. 环境准备 1.下载qt5源码&#xff1a;Open Source Development | Open Source …...

uniapp 实战 -- app 的自动升级更新(含生成 app 发布页)

uniapp 提供了 App升级中心 uni-upgrade-center &#xff0c;可以便捷实现app 的自动升级更新&#xff0c;具体编码和配置如下&#xff1a; 1. 用户端 – 引入升级中心插件 下载安装插件 uni-upgrade-center - App https://ext.dcloud.net.cn/plugin?id4542 pages.json 中添加…...

微服务http调用其他服务的方法

在对应需要调的服务配置文件加上路径 #审批方案微服务配置 server.port: 9004 upload.path: /alldev/u01/ schedule.cron.countDown: 0 0 8-18 * * ? statistics.syskey: ywsp schedule.countDown.isExecute: true post.url.updateStatus: http://10.3.2.222:8888/ecological…...

vagrant 用户名密码登录

正常登录后 sudo -i 切换到root权限 vim /etc/ssh/vim sshd_config 将PasswordAuthentication no设置 为yes 重启sshd.service服务 systemctl restart sshd.service...

强化学习应用(三):基于Q-learning的无人机物流路径规划研究(提供Python代码)

一、Q-learning简介 Q-learning是一种强化学习算法&#xff0c;用于解决基于马尔可夫决策过程&#xff08;MDP&#xff09;的问题。它通过学习一个价值函数来指导智能体在环境中做出决策&#xff0c;以最大化累积奖励。 Q-learning算法的核心思想是通过不断更新一个称为Q值的…...

探索SQL性能优化之道:实用技巧与最佳实践

SQL性能优化可能是每个数据库管理员和开发者在日常工作中必不可少的一个环节。在大数据时代&#xff0c;为确保数据库系统的响应速度和稳定性&#xff0c;掌握一些实用的SQL优化技巧至关重要。 本文将带着开发人员走进SQL性能优化的世界&#xff0c;深入剖析实用技巧和最佳实践…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动

一、前言说明 在2011版本的gb28181协议中&#xff0c;拉取视频流只要求udp方式&#xff0c;从2016开始要求新增支持tcp被动和tcp主动两种方式&#xff0c;udp理论上会丢包的&#xff0c;所以实际使用过程可能会出现画面花屏的情况&#xff0c;而tcp肯定不丢包&#xff0c;起码…...

在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能

下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能&#xff0c;包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...

精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南

精益数据分析&#xff08;97/126&#xff09;&#xff1a;邮件营销与用户参与度的关键指标优化指南 在数字化营销时代&#xff0c;邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天&#xff0c;我们将深入解析邮件打开率、网站可用性、页面参与时…...

C/C++ 中附加包含目录、附加库目录与附加依赖项详解

在 C/C 编程的编译和链接过程中&#xff0c;附加包含目录、附加库目录和附加依赖项是三个至关重要的设置&#xff0c;它们相互配合&#xff0c;确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中&#xff0c;这些概念容易让人混淆&#xff0c;但深入理解它们的作用和联…...

day36-多路IO复用

一、基本概念 &#xff08;服务器多客户端模型&#xff09; 定义&#xff1a;单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用&#xff1a;应用程序通常需要处理来自多条事件流中的事件&#xff0c;比如我现在用的电脑&#xff0c;需要同时处理键盘鼠标…...

Elastic 获得 AWS 教育 ISV 合作伙伴资质,进一步增强教育解决方案产品组合

作者&#xff1a;来自 Elastic Udayasimha Theepireddy (Uday), Brian Bergholm, Marianna Jonsdottir 通过搜索 AI 和云创新推动教育领域的数字化转型。 我们非常高兴地宣布&#xff0c;Elastic 已获得 AWS 教育 ISV 合作伙伴资质。这一重要认证表明&#xff0c;Elastic 作为 …...

人工智能 - 在Dify、Coze、n8n、FastGPT和RAGFlow之间做出技术选型

在Dify、Coze、n8n、FastGPT和RAGFlow之间做出技术选型。这些平台各有侧重&#xff0c;适用场景差异显著。下面我将从核心功能定位、典型应用场景、真实体验痛点、选型决策关键点进行拆解&#xff0c;并提供具体场景下的推荐方案。 一、核心功能定位速览 平台核心定位技术栈亮…...

麒麟系统使用-进行.NET开发

文章目录 前言一、搭建dotnet环境1.获取相关资源2.配置dotnet 二、使用dotnet三、其他说明总结 前言 麒麟系统的内核是基于linux的&#xff0c;如果需要进行.NET开发&#xff0c;则需要安装特定的应用。由于NET Framework 是仅适用于 Windows 版本的 .NET&#xff0c;所以要进…...

32位寻址与64位寻址

32位寻址与64位寻址 32位寻址是什么&#xff1f; 32位寻址是指计算机的CPU、内存或总线系统使用32位二进制数来标识和访问内存中的存储单元&#xff08;地址&#xff09;&#xff0c;其核心含义与能力如下&#xff1a; 1. 核心定义 地址位宽&#xff1a;CPU或内存控制器用32位…...

Java多线程实现之Runnable接口深度解析

Java多线程实现之Runnable接口深度解析 一、Runnable接口概述1.1 接口定义1.2 与Thread类的关系1.3 使用Runnable接口的优势 二、Runnable接口的基本实现方式2.1 传统方式实现Runnable接口2.2 使用匿名内部类实现Runnable接口2.3 使用Lambda表达式实现Runnable接口 三、Runnabl…...