编写一个简单的服务和客户端(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.xml
和CMakeLists.txt
。 是包含.srv 文件的example_interfaces
包,您需要构建请求和响应:
int64 a
int64 b
---
int64 sum
前两行是请求的参数,破折号下面是响应。
1.1 更新package.xml
由于您--dependencies
在包创建期间使用了该选项,因此无需手动将依赖项添加到package.xml
或CMakeLists.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 run
CMakeLists.txt
server
add_executable(server src/add_two_ints_server.cpp)
ament_target_dependencies(server
rclcpp example_interfaces)
因此可以找到可执行文件,将以下行添加到文件末尾之前:ros2 run
ament_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
) 中运行以检查是否缺少依赖项:
rosdep install -i --from-path src --rosdistro galactic -y
导航回工作区的根目录,ros2_ws
并构建新包:
colcon build --packages-select cpp_srvcli
打开一个新终端,导航到ros2_ws
并获取安装文件:
. install/setup.bash
现在运行服务节点:
ros2 run cpp_srvcli server
终端应返回以下消息,然后等待:
[INFO] [rclcpp]: Ready to add two ints.
打开另一个终端,再次从内部获取安装文件ros2_ws
。启动客户端节点,后跟任意两个以空格分隔的整数:
ros2 run cpp_srvcli client 2 3
例如,如果您选择2
和3
,客户端将收到如下响应:
[INFO] [rclcpp]: Sum: 5
返回到运行服务节点的终端。您将看到它在收到请求和收到的数据以及发回的响应时发布了日志消息:
[INFO] [rclcpp]: Incoming request
a: 2 b: 3
[INFO] [rclcpp]: sending back response: [5]
在服务器终端中输入Ctrl+C
以停止节点旋转。
相关文章:
编写一个简单的服务和客户端(C++)
背景 当节点使用服务进行通信时,发送数据请求的节点称为客户端节点,响应请求的节点称为服务节点。请求和响应的结构由.srv文件确定。 这里使用的例子是一个简单的整数加法系统;一个节点请求两个整数之和,另一个节点响应结果。 …...
InseRF: 文字驱动的神经3D场景中的生成对象插入
每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…...
类厂,变长参数,序列化
目的 在记录nav2中的各类信息,保存到文件中,以便后面回放来分析算法的编程中发现。 各种信息记录的数据不同,可能还会有变化,所以决定采用类厂模式,参见C++设计模式入门 记录的基类 有个信息记录的基类,不同的记录对应不同的子类。 enum rcdType{RT_NA,RT_nav2Info,R…...
LLK的2023年度总结
文章目录 一月二月三月四月五月六月七月八月九月十月十一月十二月 一月 此时的俺还在沉浸在蓝桥杯的练习和女朋友的甜蜜期,感觉没啥大长进。然后荣幸地知道了自己C语言实验因为某种非技术原因而挂科了。此时自己地目标由保研自然地转换到考研比赛就业的方向了。接着…...
Redis-浅谈redis.conf配置文件
Redis.conf Redis.conf是Redis的配置文件,它包含了一系列用于配置Redis服务器行为和功能的选项。 以下是Redis.conf中常见的一些选项配置: bind: 指定Redis服务器监听的IP地址,默认为127.0.0.1,表示只能本地访问,可以…...
【liunx】线程池+单例模式+STL,智能指针和线程安全+其他常见的各种锁+读者写者问题
线程池单例模式STL,智能指针和线程安全其他常见的各种锁读者写者问题 1.线程池2.线程安全的单例模式3.STL,智能指针和线程安全4.其他常见的各种锁4.读者写者问题 喜欢的点赞,收藏,关注一下把! 1.线程池 目前我们学了挂起等待锁、条件变量、信…...
Golang的API项目快速开始
开启一个简单的API服务。 golang的教程网上一大堆,官网也有非常详细的教程,这里不在赘述这些基础语法教程,我们意在快速进入项目开发阶段。 golang好用语法教程传送门: m.runoob.com/go/ 编写第一个API 前提:按照上一…...
机器学习_实战框架
文章目录 介绍机器学习的实战框架1.定义问题2.收集数据和预处理(1).收集数据(2).数据可视化(3).数据清洗(4).特征工程(5).构建特征集和标签集(6).拆分训练集、验证集和测试集。 3.选择算法并建立模型4.训练模型5.模型的评估和优化 介绍机器学习的实战框架 一个机器学习项目从开…...
Java8常用新特性
目录 简介 1.默认方法 2..Lambda表达式 3.Stream API 4.方法引用 5.Optional类 简介 Java 8是Java编程语言的一个重要版本,引入了许多令人兴奋和强大的新特性。这些特性使得Java程序更加现代化、灵活和高效。让我们一起来探索一些Java 8的常用新特性吧&#…...
Go语言中的Channel
1. 简介 Channel是Go语言中一种重要的并发原语,它允许goroutine之间安全地交换数据。Channel是一个类型化的队列,它可以存储一个特定类型的值。goroutine可以通过发送和接收操作来向channel中写入和读取数据。 2. Channel的类型 Channel的类型由其元素…...
Unity中URP下实现深度贴花
文章目录 前言一、场景设置二、实现思路1、通过深度图求出像素所在视图空间的Z值2、通过模型面片的求出像素在观察空间下的坐标值3、结合两者求出 深度图中像素的 XYZ值4、再将此坐标转换到模型的本地空间,把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,所以我们只要给另一端接上低电平就可以让蜂鸣器和继电器进行工作。与操作LED类似,只不过换了一个74HC5…...
嵌入式linux 编译qt5(以v851s为例)
本文参考Blev大神的博客:Yuzuki Lizard V851S开发板 --移植 QT5.12.9教程(群友Blev提供) - Allwinner / 柚木PI-V851S - 嵌入式开发问答社区 (100ask.net) 一. 环境准备 1.下载qt5源码:Open Source Development | Open Source …...
uniapp 实战 -- app 的自动升级更新(含生成 app 发布页)
uniapp 提供了 App升级中心 uni-upgrade-center ,可以便捷实现app 的自动升级更新,具体编码和配置如下: 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是一种强化学习算法,用于解决基于马尔可夫决策过程(MDP)的问题。它通过学习一个价值函数来指导智能体在环境中做出决策,以最大化累积奖励。 Q-learning算法的核心思想是通过不断更新一个称为Q值的…...
探索SQL性能优化之道:实用技巧与最佳实践
SQL性能优化可能是每个数据库管理员和开发者在日常工作中必不可少的一个环节。在大数据时代,为确保数据库系统的响应速度和稳定性,掌握一些实用的SQL优化技巧至关重要。 本文将带着开发人员走进SQL性能优化的世界,深入剖析实用技巧和最佳实践…...
Github项目推荐-Insomnia
项目地址 GitHub地址:GitHub - Kong/insomnia 官网:The Collaborative API Development Platform - Insomnia 项目简述 想必大家都知道PostMan吧。Insomnia可以说是PostMan的开源平替。页面ui很不错,功能强大,使用也比较方便。…...
python 语法
闭包 在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,我们把这个使用外部函数变量的内部函数称为闭包。 def outfunc(arg):def innerFunc(msg):print(f"<{msg}> {arg} <{msg}>")retu…...
Mac下载Navicat premium提示文件损坏的解决方案
引用:https://blog.csdn.net/weixin_44898291/article/details/120879508 sudo xattr -r -d com.apple.quarantine...
算法——贪心法(Greedy)
贪心法 把整个问题分解成多个步骤,在每个步骤都选取当前步骤的最优方案,直到所有步骤结束;在每一步都不考虑对后续步骤的影响,在后续步骤中也不再回头改变前面的选择。不足之处: 贪心算法并不能保证获得全局最优解&…...
VmWare虚拟机的安装
VmWare官方最新版下载地址 vmware官方下载地址 安装流程 安装成功验证 安装完成之后,打开网络中心,一定要确认这里多出两个网络连接,才证明Vmware已经安装成功...
Vue.js轻量级框架:快速搭建可扩展的管理系统
一、前言 在项目实战开发中,尤其是大平台系统的搭建,针对不同业务场景,需要为用户多次编写用于录入、修改、展示操作的相应表单页面。一旦表单需求过多,对于开发人员来说,算是一种重复开发,甚至是繁杂的工作…...
Android-多线程
线程是进程中可独立执行的最小单位,也是 CPU 资源(时间片)分配的基本单位,同一个进程中的线程可以共享进程中的资源,如内存空间和文件句柄。线程有一些基本的属性,如id、name、以及priority。 id࿱…...
sqlalchemy 监听所有实体插入以及更新事件
这边使用的是flaskdependency-injectersqlalchemy,有一个公共类,想插入或者更新的时候对公共类某些字段进行统一操作 这个是公共类:包括一些基础字段,所有的实体都会继承这个类 """Models module.""&q…...
go怎么结束很多个协程呢
在Go语言中,可以通过使用context来结束多个协程。context包提供了用于跟踪、取消和传递截止日期的机制,可用于协程的生命周期管理。 以下是一个使用context取消多个协程的示例: package mainimport ("context""fmt"&qu…...
springboot 项目访问静态资源遇到的问题,WebMvcConfigurer和WebMvcConfigurationSupport
之前发过通过继承WebMvcConfigurationSupport来访问静态资源的文章——img标签访问静态资源,代码如下 Configuration public class LocalPathWebMvcConfigurer extends WebMvcConfigurationSupport {/*** 在springboot项目中,允许浏览器访问指定本地文件…...
推广互联网推广/独立站seo建站系统
来源:AI科学投资——崇尚科学,探讨科学的投资理念和方法 践行科技,打造AI时代新智能投研平台 作者:Quant_Andy 误解一 所有的量化投资都是一样的 实际上,量化投资只是一个宽泛的概念,且不说P派Q派的理念…...
自带浏览器建设银行网站打不开/网站服务器信息查询
onAttach– onCreate– onCreateView– onActivityCreated– onstart– onresume– onpause– onstop– onDestroyView– onDestroy– onDetach– Fragment的是依附于Activity存在的,所以Activity的生命周期会影响Fragment的生命周期。下面是官网的一张图…...
盐城哪有做网站建设的/如何让百度搜索到自己的网站
[索引页][源码下载]再接再厉VS 2008 sp1 .NET 3.5 sp1(1) - Entity Framework(实体框架)之添加、查询、更新和删除的Demo作者:webabcd介绍以Northwind为示例数据库,ADO.NET Entity Framework之完全面向对象的添加操作、查询操作、更新操作和删除操作示例…...
wordpress中文 插件/百度客户端
http://acm.hdu.edu.cn/showproblem.php?pid1026 题意:给出一个迷宫,求出到终点的最短时间路径。 这道题目在迷宫上有怪物,不同HP的怪物会损耗不同的时间,这个时候可以用优先队列,每次让时间最短的出队列。由于最后还需要输出路径…...
网站建设与规划专业/怎样做网站
file_get_contents的文件包含漏洞 打开题目,就看到一段源码 <?php extract($_GET); if (!empty($ac)) { $f trim(file_get_contents($fn)); //把输入到fn的文件读到f的文件当中去 if ($ac $f) //acf { echo "<p>This…...
wordpress上传到哪个文件夹/免费生成短链接
在paint方法里面,这个Graphics类就相当于一支画笔。而且就画在那个component里面,比如frame。 看例子代码: import java.awt.*;public class TestPaint {public static void main(String[] args) {new PaintFrame();} }class PaintFrame exte…...