La comunicación de servicio también es un modo de comunicación extremadamente común en ROS. La comunicación de servicio se basa en el modo de respuesta de solicitud , que es un mecanismo de respuesta. Es decir: un nodo A envía una solicitud a otro nodo B, y B recibe la solicitud de procesamiento y genera un resultado de respuesta a A. Por ejemplo, el siguiente escenario:
Durante la patrulla del robot, el sistema de control analiza los datos del sensor y encuentra objetos o personas sospechosas ... En este punto, debe tomar fotos y conservarlas.
En el escenario anterior, se utiliza la comunicación de servicio.
-
Un nodo necesita enviar una solicitud de foto al nodo de la cámara, el nodo de la cámara procesa la solicitud y devuelve el resultado del procesamiento.
De manera similar a las aplicaciones anteriores, la comunicación de servicios es más adecuada para escenarios de aplicaciones que requieren puntualidad y tienen cierto procesamiento lógico.
concepto
El modo de comunicación de la interacción de datos entre diferentes nodos se realiza de una manera de solicitud-respuesta.
efecto
Se utiliza para escenarios ocasionales de transmisión de datos que requieren puntualidad y ciertos requisitos de procesamiento lógico.
1 Modelo teórico de comunicación de servicios
La comunicación de servicios es más simple que la comunicación de temas. El modelo teórico se muestra en la figura siguiente. Tres roles están involucrados en el modelo:
-
ROS maestro (gerente)
-
Servidor (servidor)
-
Cliente
ROS Master es responsable de mantener la información registrada por el servidor y el cliente, y hacer coincidir el servidor y el cliente con el mismo tema, ayudando al servidor y al cliente a establecer una conexión. Una vez establecida la conexión, el cliente envía la información de solicitud y el servidor devuelve la información de respuesta.
Todo el proceso se realiza mediante los siguientes pasos:
(0). Registro del servidor
Una vez iniciado el servidor, registrará su propia información en el ROS Master a través de RPC, que contiene el nombre del servicio prestado. ROS Master agregará la información de registro del nodo al registro.
(1) Registro de cliente
Una vez iniciado el Cliente, también registrará su propia información en el ROS Master a través de RPC, incluido el nombre del servicio que debe solicitarse. ROS Master agregará la información de registro del nodo al registro.
(2). ROS Master se da cuenta de la coincidencia de información
ROS Master hará coincidir el servidor y el cliente de acuerdo con la información en el registro y enviará la información de la dirección TCP del servidor al cliente a través de RPC .
(3) El cliente envía una solicitud
El Cliente utiliza TCP para establecer una conexión de red con el Servidor de acuerdo con la información de respuesta en el paso 2 y envía los datos solicitados.
(4) El servidor envía una respuesta
El servidor recibe y analiza los datos solicitados, genera un resultado de respuesta y lo devuelve al cliente.
Nota:
1. Cuando se procesa la solicitud del cliente, es necesario asegurarse de que se haya iniciado el servidor;
2. Puede haber varios servidores y clientes.
2 Servicio de comunicación personalizado srv
demanda:
En la comunicación de servicio, el cliente envía dos números enteros al servidor, y el servidor suma y responde con el resultado al cliente. Cree un soporte de datos para la comunicación entre el servidor y el cliente.
srv = solicitud + respuesta
Proceso:
Los tipos de datos disponibles en el archivo srv son los mismos que en el archivo msg, y el proceso de definición de srv es similar al de un mensaje personalizado:
-
Crea un archivo srv en un formato fijo
-
Editar archivo de configuración
-
Compila y genera archivos intermedios
(1) Defina el archivo srv
En la comunicación de servicios, los datos se dividen en dos partes, solicitud y respuesta. La solicitud y la respuesta se ---
dividen en el archivo srv . La implementación específica es la siguiente:
Cree un nuevo directorio srv en el paquete de funciones, agregue el archivo xxx.srv y el contenido:
# 客户端请求时发送的两个数字
int32 num1
int32 num2
---
# 服务器响应发送的数据
int32 sum
(2) Edite el archivo de configuración
Agregue dependencia de compilación y dependencia de ejecución en package.xml
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
<!--
exce_depend 以前对应的是 run_depend 现在非法
添加构建依赖 和 执行依赖
-->
Copy
CMakeLists.txt edita la configuración relacionada con srv
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
message_generation
)
# 需要加入 message_generation,必须有 std_msgs
add_service_files(
FILES
Addints.srv
)
generate_messages(
DEPENDENCIES
std_msgs
)
Nota: el sitio web oficial no configura message_runtime en catkin_package, se puede configurar después de la prueba
(3) Compilar
Ver el archivo intermedio compilado:
Archivos intermedios que C ++ necesita llamar (... / workspace / devel / include / package name / xxx.h)
Archivos intermedios que Python necesita llamar (... / workspace / devel / lib / python2.7 / dist-packages / package name / srv)
Cuando posteriormente se llama al srv relacionado, se llama desde estos archivos intermedios.
3 Llamada srv personalizada de comunicación de servicio A (C ++)
demanda:
Para escribir la comunicación del servicio, el cliente envía dos números enteros al servidor, y el servidor suma y responde con el resultado al cliente.
análisis:
En la implementación del modelo, no es necesario implementar el maestro ROS y se ha encapsulado el establecimiento de la conexión.Hay tres puntos clave a los que se debe prestar atención:
-
Servidor
-
Cliente
-
datos
Proceso:
-
Escriba la implementación del lado del servidor;
-
Escriba la implementación del cliente;
-
Edite el archivo de configuración;
-
Compila y ejecuta.
(0) configuración de vscode
El archivo c_cpp_properies.json debe configurarse como la implementación de mensajes personalizados anterior. Si se configuró antes y el espacio de trabajo no se ha cambiado, puede ignorarlo. Si necesita configurarlo, el método de configuración es el mismo que antes. :
{
"configurations": [
{
"browse": {
"databaseFilename": "",
"limitSymbolsToIncludedHeaders": true
},
"includePath": [
"/home/winter/catkin_wp/devel/include/**",
"/opt/ros/melodic/include/**",
"/home/winter/catkin_wp/src/learning_parameter/include/**",
"/home/winter/catkin_wp/src/learning_service/include/**",
"/home/winter/catkin_wp/src/learning_tf/include/**",
"/home/winter/catkin_wp/src/learning_topic/include/**",
"/usr/include/**",
"/home/winter/demo03_ws/devel/include/**"
],
"name": "ROS",
"intelliSenseMode": "gcc-x64",
"compilerPath": "/usr/bin/gcc",
"cStandard": "gnu17",
"cppStandard":"c++17"
}
],
"version": 4
}
(1) servidor
#include "ros/ros.h"
#include "plumbing_server_client/Addints.h"
/*
服务端实现:解析客户端实现的数据,并运算再产生响应
1 包含头文件
2 初始化ros结点
3 创建结点句柄
4 创建服务对象
5 处理请求并产生响应
6 spin()函数
*/
//回调函数
bool doNums(plumbing_server_client::Addints::Request& request,
plumbing_server_client::Addints::Response& response)
{
//处理请求
int num1 = request.num1;
int num2 = request.num2;
ROS_INFO("服务器收到的请求数据是:num1 = %d,num2 = %d",num1,num2);
//响应请求
int num = num1 + num2;
response.sum = num;
ROS_INFO("求和结果是:sum = %d",num);
return true;
}
int main(int argc, char *argv[])
{
//设置中文
setlocale(LC_ALL,"");
//2 初始化ros结点
ros::init(argc,argv,"Server"); //名称保证唯一
//3 创建结点句柄
ros::NodeHandle nh;
//4 创建服务对象
ros::ServiceServer server = nh.advertiseService("Addints",doNums);
ROS_INFO("服务端启动了");
//5 回调函数
//6 spin函数
ros::spin();
return 0;
}
Archivo de configuración
add_dependencies(demo01_server ${PROJECT_NAME}_generate_messages_cpp)
prueba
roscore
source ./devel/setup.bash
rosrun plumbing_server_client demo01_server
source ./devel/setup.bash
rosservice call Addints "num1: 20
num2: 10"
sum: 30
(2) Cliente
#include "ros/ros.h"
#include "plumbing_server_client/Addints.h"
/*
客户端:提交两个整数,处理响应的结果
1 包含头文件
2 初始化ros结点
3 创建结点句柄
4 创建客户对象
5 提交请求并处理响应
*/
int main(int argc, char *argv[])
{
setlocale(LC_ALL,"");
// 2 初始化ros结点
ros::init(argc,argv,"Client");
// 3 创建结点句柄
ros::NodeHandle nh;
// 4 创建客户对象
ros::ServiceClient client = nh.serviceClient<plumbing_server_client::Addints>("Addints");
// 5 提交请求并处理响应
plumbing_server_client::Addints ai;
//组织请求
ai.request.num1 = 100;
ai.request.num2 = 200;
//处理响应 客户端访问服务器
bool flag = client.call(ai);
if(flag==true)
{
ROS_INFO("响应成功");
//获取结果
ROS_INFO("响应结果是:%d",ai.response.sum);
}
else
{
ROS_INFO("处理失败");
}
return 0;
}
Archivo de configuración
roscore
source ./devel/setup.bash
rosrun plumbing_server_client demo01_server
rosrun plumbing_server_client demo02_client
(3) Optimización del cliente
Proporcionar datos de forma dinámica
#include "ros/ros.h"
#include "plumbing_server_client/Addints.h"
/*
客户端:提交两个整数,处理响应的结果
1 包含头文件
2 初始化ros结点
3 创建结点句柄
4 创建客户对象
5 提交请求并处理响应
实现参数的动态提交:
1 格式 rosrun 包名 结点名 参数1 参数2
2 节点执行时要获取命令中的参数,并组织进request 使用argc argv
*/
int main(int argc, char *argv[])
{
setlocale(LC_ALL,"");
//优化实现,获取命令中的参数
/*
iargc = 3
argv[0] 是文件名
argv[1] 是参数1
argv[2] 是参数2
*/
if(argc !=3)
{
ROS_INFO("提交的参数个数不对");
return 1;
}
// 2 初始化ros结点
ros::init(argc,argv,"Client");
// 3 创建结点句柄
ros::NodeHandle nh;
// 4 创建客户对象
ros::ServiceClient client = nh.serviceClient<plumbing_server_client::Addints>("Addints");
// 5 提交请求并处理响应
plumbing_server_client::Addints ai;
//组织请求
ai.request.num1 =atoi( argv[1]);
ai.request.num2 = atoi(argv[2]);
//处理响应 客户端访问服务器
bool flag = client.call(ai);
if(flag==true)
{
ROS_INFO("响应成功");
//获取结果
ROS_INFO("响应结果是:%d",ai.response.sum);
}
else
{
ROS_INFO("处理失败");
}
return 0;
}
Si inicia el cliente primero, luego inicie el servidor
mejoramiento:
Agregue antes de que el cliente envíe la solicitud:
client.waitForExistence();
或:ros::service::waitForService("AddInts");
Esta es una función de bloqueo y solo continuará la ejecución después de que el servicio se inicie con éxito