【ROS】 Comunicación de servicio

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:

  1. Crea un archivo srv en un formato fijo

  2. Editar archivo de configuración

  3. 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:

  1. Servidor

  2. Cliente

  3. datos

Proceso:

  1. Escriba la implementación del lado del servidor;

  2. Escriba la implementación del cliente;

  3. Edite el archivo de configuración;

  4. 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

Supongo que te gusta

Origin blog.csdn.net/Zhouzi_heng/article/details/114602454
Recomendado
Clasificación