[Notas de estudio ROS 4] Tema Comunicación

[Notas de estudio ROS 4] Tema Comunicación


0. Descripción general de la comunicación del tema

La comunicación por temas es el método de comunicación más utilizado en ROS.La comunicación por temas se basa en el modelo de publicación-suscripción, es decir: un nodo publica un mensaje y otro nodo se suscribe al mensaje. Los escenarios de aplicación de la comunicación de temas también son extremadamente extensos.

concepto

Un modo de comunicación que implementa la interacción de datos entre diferentes nodos en una forma de publicación-suscripción

efecto

Para escenarios de transferencia de datos que se actualizan constantemente y se procesan de forma menos lógica.


1. Modelo teórico de comunicación temática

El modelo de implementación de la comunicación de temas es relativamente complicado. El modelo se muestra en la siguiente figura. Hay tres roles involucrados en el modelo:

  • Maestro ROS (gerente)
  • Hablador (Editor)
  • Oyente (suscriptor)

ROS Master es responsable de mantener la información registrada de Talker y Listener, emparejar Talker y Listener con el mismo tema y ayudar a Talker y Listener a establecer una conexión. Una vez establecida la conexión, Talker puede publicar mensajes y los mensajes publicados se suscribirán. por el oyente.

Todo el proceso se realiza mediante los siguientes pasos:

0. Registro de hablante

Después de que Talker se inicie, registrará su propia información en ROS Master a través de RPC, que incluye el nombre del tema del mensaje publicado. ROS Master agregará la información de registro del nodo al registro.

1. Registro de oyentes

Una vez que se inicia el Listener, también registrará su propia información en el ROS Master a través de RPC, incluido el nombre del tema que debe suscribirse al mensaje. ROS Master agregará la información de registro del nodo al registro.

2. ROS Master implementa la coincidencia de información

ROS Master emparejará a Talker y Listener de acuerdo con la información en el registro y enviará la información de la dirección RPC de Talker a Listener a través de RPC.

3. El oyente envía una solicitud al hablante

Después de que Talker recibe la solicitud del Listener, también confirma la información de conexión al Listener a través de RPC y envía su propia información de dirección TCP.

4. El Oyente establece una conexión con el Hablador

El Listener usa TCP para establecer una conexión de red con el Talker de acuerdo con el mensaje devuelto en el paso 4.

5. El hablante envía un mensaje al oyente

Una vez que se establece la conexión, el hablante comienza a publicar mensajes al oyente

Atención 1: en el proceso de implementación anterior, los primeros cinco pasos usan el protocolo RPC y los dos últimos pasos usan el protocolo TCP

Atención 2: no hay ningún requisito de secuencia para el inicio de Talker y Listener

Atención3: tanto el hablante como el oyente pueden tener múltiples

Atención 4: después de establecer la conexión entre Talker y Listener, ya no se necesita ROS Master

Atención5: El proceso anterior se ha encapsulado y se puede llamar directamente en el futuro.

Enfoque de comunicación del tema:

Punto 1: la mayor parte de la implementación se ha encapsulado

Punto 2: configuración del tema

Punto 3: Centrarse en la implementación del editor

Punto 4: Foco en la implementación de suscriptores

Punto 5: Preste atención al portador del mensaje


2. Implementación de Cpp de operaciones básicas de comunicación de temas.

1. Demanda

Escriba una implementación de publicación-suscripción, que requiera que el editor publique un mensaje de texto a una frecuencia de 10 HZ y que el suscriptor imprima el contenido del mensaje.

2. Análisis

En la implementación del modelo, ROS Master se implementó automáticamente, no necesitamos implementarlo nuevamente, y el paquete de conexión también se empaquetó. Hay tres puntos que necesitan atención.

  1. Editor
  2. receptor
  3. datos (texto sin formato aquí)

3. Proceso

  1. Escribir la implementación del editor
  2. Escribir la implementación del suscriptor
  3. editar archivo de configuración
  4. compilar y ejecutar

4. Implementación por parte del editor

/*
    需求: 实现基本的话题通信,一方发布数据,一方接收数据,
         实现的关键点:
         1.发送方
         2.接收方
         3.数据(此处为普通文本)
         PS: 二者需要设置相同的话题
    消息发布方:
        循环发布信息:HelloWorld 后缀数字编号
    实现流程:
        1.包含头文件 
        2.初始化 ROS 节点:命名(唯一)
        3.实例化 ROS 句柄
        4.实例化 发布者 对象
        5.组织被发布的数据,并编写逻辑发布数据

*/
// 1.包含头文件 
#include "ros/ros.h"
#include "std_msgs/String.h" //普通文本类型的消息
#include <sstream>

int main(int argc, char  *argv[])
{
    
       
    //设置编码
    setlocale(LC_ALL,"");

    //2.初始化 ROS 节点:命名(唯一)
    // 参数1和参数2 后期为节点传值会使用
    // 参数3 是节点名称,是一个标识符,需要保证运行后,在 ROS 网络拓扑中唯一
    ros::init(argc,argv,"talker");
    //3.实例化 ROS 句柄
    ros::NodeHandle nh;//该类封装了 ROS 中的一些常用功能

    //4.实例化 发布者 对象
    //泛型: 发布的消息类型
    //参数1: 要发布到的话题
    //参数2: 队列中最大保存的消息数,超出此阀值时,先进的先销毁(时间早的先销毁)
    ros::Publisher pub = nh.advertise<std_msgs::String>("chatter",10);

    //5.组织被发布的数据,并编写逻辑发布数据
    //数据(动态组织)
    std_msgs::String msg;
    // msg.data = "你好啊!!!";
    std::string msg_front = "Hello 你好!"; //消息前缀
    int count = 0; //消息计数器

    //逻辑(一秒10次)
    ros::Rate r(1);

    //节点不死
    while (ros::ok())
    {
    
    
        //使用 stringstream 拼接字符串与编号
        std::stringstream ss;
        ss << msg_front << count;
        msg.data = ss.str();
        //发布消息
        pub.publish(msg);
        //加入调试,打印发送的消息
        ROS_INFO("发送的消息:%s",msg.data.c_str());

        //根据前面制定的发送贫频率自动休眠 休眠时间 = 1/频率;
        r.sleep();
        count++;//循环结束前,让 count 自增
        //暂无应用
        ros::spinOnce();
    }

    return 0;
}

5. Realización de abonados

/*
    需求: 实现基本的话题通信,一方发布数据,一方接收数据,
         实现的关键点:
         1.发送方
         2.接收方
         3.数据(此处为普通文本)
    消息订阅方:
        订阅话题并打印接收到的消息
    实现流程:
        1.包含头文件 
        2.初始化 ROS 节点:命名(唯一)
        3.实例化 ROS 句柄
        4.实例化 订阅者 对象
        5.处理订阅的消息(回调函数)
        6.设置循环调用回调函数

*/
// 1.包含头文件 
#include "ros/ros.h"
#include "std_msgs/String.h"

void doMsg(const std_msgs::String::ConstPtr& msg_p){
    
    
    ROS_INFO("我听见:%s",msg_p->data.c_str());
    // ROS_INFO("我听见:%s",(*msg_p).data.c_str());
}
int main(int argc, char  *argv[])
{
    
    
    setlocale(LC_ALL,"");
    //2.初始化 ROS 节点:命名(唯一)
    ros::init(argc,argv,"listener");
    //3.实例化 ROS 句柄
    ros::NodeHandle nh;

    //4.实例化 订阅者 对象
    ros::Subscriber sub = nh.subscribe<std_msgs::String>("chatter",10,doMsg);
    //5.处理订阅的消息(回调函数)

    //     6.设置循环调用回调函数
    ros::spin();//循环读取接收的数据,并调用回调函数处理

    return 0;
}

6. Configurar CMakeLists.txt

add_executable(Hello_pub
  src/Hello_pub.cpp
)
add_executable(Hello_sub
  src/Hello_sub.cpp
)

target_link_libraries(Hello_pub
  ${catkin_LIBRARIES}
)
target_link_libraries(Hello_sub
  ${catkin_LIBRARIES}
)

7. Ejecución

  1. empezar roscore
  2. Inicie el nodo de publicación
  3. Inicie el nodo de suscripción

El efecto final es el siguiente, rosrun rqt_graph rqt_graphpuede ver el gráfico de cálculo con


3. Implementación Python de operaciones básicas de comunicación de temas.

1. Proceso

  1. Escribir la implementación del editor
  2. Escribir la implementación del suscriptor
  3. Agregar permisos ejecutables a archivos python
  4. editar archivo de configuración
  5. compilar y ejecutar

2. Editor

#! /usr/bin/env python
"""
    需求: 实现基本的话题通信,一方发布数据,一方接收数据,
         实现的关键点:
         1.发送方
         2.接收方
         3.数据(此处为普通文本)

         PS: 二者需要设置相同的话题


    消息发布方:
        循环发布信息:HelloWorld 后缀数字编号

    实现流程:
        1.导包 
        2.初始化 ROS 节点:命名(唯一)
        3.实例化 发布者 对象
        4.组织被发布的数据,并编写逻辑发布数据


"""
#1.导包 
import rospy
from std_msgs.msg import String

if __name__ == "__main__":
    #2.初始化 ROS 节点:命名(唯一)
    rospy.init_node("talker_p")
    #3.实例化 发布者 对象
    pub = rospy.Publisher("chatter",String,queue_size=10)
    #4.组织被发布的数据,并编写逻辑发布数据
    msg = String()  #创建 msg 对象
    msg_front = "hello 你好"
    count = 0  #计数器 
    # 设置循环频率
    rate = rospy.Rate(1)
    while not rospy.is_shutdown():

        #拼接字符串
        msg.data = msg_front + str(count)

        pub.publish(msg)
        rate.sleep()
        rospy.loginfo("写出的数据:%s",msg.data)
        count += 1

2. Suscriptor

#! /usr/bin/env python
"""
    需求: 实现基本的话题通信,一方发布数据,一方接收数据,
         实现的关键点:
         1.发送方
         2.接收方
         3.数据(此处为普通文本)


    消息订阅方:
        订阅话题并打印接收到的消息

    实现流程:
        1.导包 
        2.初始化 ROS 节点:命名(唯一)
        3.实例化 订阅者 对象
        4.处理订阅的消息(回调函数)
        5.设置循环调用回调函数



"""
#1.导包 
import rospy
from std_msgs.msg import String

def doMsg(msg):
    rospy.loginfo("I heard:%s",msg.data)

if __name__ == "__main__":
    #2.初始化 ROS 节点:命名(唯一)
    rospy.init_node("listener_p")
    #3.实例化 订阅者 对象
    sub = rospy.Subscriber("chatter",String,doMsg,queue_size=10)
    #4.处理订阅的消息(回调函数)
    #5.设置循环调用回调函数
    rospy.spin()

3. Agregar permisos ejecutables

cd scripts
chomd +x *.py

4. Configurar CMakeLists.txt

catkin_install_python(PROGRAMS
  scripts/talker_p.py
  scripts/listener_p.py
  DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)

efecto de implementación específico


4. Mensaje personalizado para la comunicación del tema

En el protocolo de comunicación ROS, el portador de datos es una parte importante.ROS encapsula algunos tipos de datos nativos a través de std_msgs, tales como: String, Int32, Int64, Char, Bool, Empty... Sin embargo, estos datos generalmente solo contienen un dato El un solo campo y una estructura significan limitaciones funcionales. Al transmitir algunos datos complejos, como: información LIDAR... std_msgs es impotente debido a la falta de descripción. En este escenario, se puede usar un tipo de mensaje personalizado

Los mensajes son simplemente archivos de texto, cada línea tiene un tipo de campo y un nombre de campo, los tipos de campo que se pueden usar son:

  • int8, int16, int32, int64 (o tipo sin signo: uint*)
  • flotar32, flotar64
  • cadena
  • Duración de tiempo
  • otros archivos de mensajes
  • matriz de longitud variable[] y matriz de longitud fija[C]

También hay un tipo especial en ROS: Headerel encabezado contiene la marca de tiempo y la información del marco de coordenadas que se usa comúnmente en ROS. A menudo se ve que la primera línea del archivo msg tiene un Headerencabezado.

1. Demanda

Crea un mensaje personalizado que incluya información sobre la persona: nombre, altura, edad, etc.

2. Proceso

  1. Crear archivos msg en un formato fijo
  2. editar archivo de configuración
  3. Compile y genere archivos intermedios que Python y Cpp puedan llamar

3. Defina el archivo de mensaje

Cree un nuevo directorio de mensajes en el paquete de funciones y agregue archivosPerson.msg

string name
uint16 age
float64 height

4. Editar archivo de configuración

package.xmlAgregue dependencias de compilación y dependencias de ejecución a

  <build_depend>message_generation</build_depend>
  <exec_depend>message_runtime</exec_depend>
  <!-- 
  exce_depend 以前对应的是 run_depend 现在非法
  -->

CMakeLists.txtEditar configuración relacionada con mensajes

find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  message_generation
)
# 需要加入 message_generation,必须有 std_msgs
## 配置 msg 源文件
add_message_files(
  FILES
  Person.msg
)
# 生成消息时依赖于 std_msgs
generate_messages(
  DEPENDENCIES
  std_msgs
)
#执行时依赖
catkin_package(
#  INCLUDE_DIRS include
#  LIBRARIES demo02_talker_listener
  CATKIN_DEPENDS roscpp rospy std_msgs message_runtime
#  DEPENDS system_lib
)

5. Compilar

Vista de archivo intermedio compilado

El archivo intermedio que C++ necesita llamar (.../workspace/devel/include/package name/xxx.h)

Archivos intermedios que Python necesita llamar (.../workspace/devel/lib/python3/dist-packages/package name/msg)

Las llamadas posteriores a mensajes relacionados se llaman desde estos archivos intermedios


5. Implementación de Cpp de llamada de mensaje personalizado de comunicación de tema

0. configuración vscode

Para facilitar las solicitudes de código y evitar generar excepciones por error, primero debe configurar vscode y configurar la headruta del archivo generado previamente en c_pp_properties.jsonlas includepathpropiedades:

{
    
    
    "configurations": [
        {
    
    
            "browse": {
    
    
                "databaseFilename": "",
                "limitSymbolsToIncludedHeaders": true
            },
            "includePath": [
                "/opt/ros/noetic/include/**",
                "/usr/include/**",
                "/xxx/yyy工作空间/devel/include/**" //配置 head 文件的路径 
            ],
            "name": "ROS",
            "intelliSenseMode": "gcc-x64",
            "compilerPath": "/usr/bin/gcc",
            "cStandard": "c11",
            "cppStandard": "c++17"
        }
    ],
    "version": 4
}

1. Editor

/*
    需求: 循环发布人的信息

*/

#include "ros/ros.h"
#include "demo02_talker_listener/Person.h"

int main(int argc, char *argv[])
{
    
    
    setlocale(LC_ALL,"");

    //1.初始化 ROS 节点
    ros::init(argc,argv,"talker_person");

    //2.创建 ROS 句柄
    ros::NodeHandle nh;

    //3.创建发布者对象
    ros::Publisher pub = nh.advertise<demo02_talker_listener::Person>("chatter_person",1000);

    //4.组织被发布的消息,编写发布逻辑并发布消息
    demo02_talker_listener::Person p;
    p.name = "sunwukong";
    p.age = 2000;
    p.height = 1.45;

    ros::Rate r(1);
    while (ros::ok())
    {
    
    
        pub.publish(p);
        p.age += 1;
        ROS_INFO("我叫:%s,今年%d岁,高%.2f米", p.name.c_str(), p.age, p.height);

        r.sleep();
        ros::spinOnce();
    }



    return 0;
}

2. Suscriptor

/*
    需求: 订阅人的信息

*/

#include "ros/ros.h"
#include "demo02_talker_listener/Person.h"

void doPerson(const demo02_talker_listener::Person::ConstPtr& person_p){
    
    
    ROS_INFO("订阅的人信息:%s, %d, %.2f", person_p->name.c_str(), person_p->age, person_p->height);
}

int main(int argc, char *argv[])
{
    
       
    setlocale(LC_ALL,"");

    //1.初始化 ROS 节点
    ros::init(argc,argv,"listener_person");
    //2.创建 ROS 句柄
    ros::NodeHandle nh;
    //3.创建订阅对象
    ros::Subscriber sub = nh.subscribe<demo02_talker_listener::Person>("chatter_person",10,doPerson);

    //4.回调函数中处理 person

    //5.ros::spin();
    ros::spin();    
    return 0;
}

3. Configurar CMakeLists.txt

Necesita agregar add_dependenciesarchivos intermedios para configurar los mensajes dependientes

add_executable(person_talker src/person_talker.cpp)
add_executable(person_listener src/person_listener.cpp)



add_dependencies(person_talker ${PROJECT_NAME}_generate_messages_cpp)
add_dependencies(person_listener ${PROJECT_NAME}_generate_messages_cpp)


target_link_libraries(person_talker
  ${catkin_LIBRARIES}
)
target_link_libraries(person_listener
  ${catkin_LIBRARIES}
)

4. Ejecución

1. Iniciar roscore

2. Inicie el nodo de publicación.

3. Inicie el nodo de suscripción

El efecto se muestra en la figura.


6. Implementación de Python del mensaje personalizado de comunicación de tema

0. configuración vscode

Para facilitar las solicitudes de código y generar excepciones por error, primero debe configurar vscode y configurar la ruta del archivo python generado previamente ensettings.json

{
    
    
    "python.autoComplete.extraPaths": [
        "/opt/ros/noetic/lib/python3/dist-packages",
        "/xxx/yyy工作空间/devel/lib/python3/dist-packages"
    ]
}

1. Editor

#! /usr/bin/env python
"""
    发布方:
        循环发送消息

"""
import rospy
from demo02_talker_listener.msg import Person


if __name__ == "__main__":
    #1.初始化 ROS 节点
    rospy.init_node("talker_person_p")
    #2.创建发布者对象
    pub = rospy.Publisher("chatter_person",Person,queue_size=10)
    #3.组织消息
    p = Person()
    p.name = "葫芦瓦"
    p.age = 18
    p.height = 0.75

    #4.编写消息发布逻辑
    rate = rospy.Rate(1)
    while not rospy.is_shutdown():
        pub.publish(p)  #发布消息
        rate.sleep()  #休眠
        rospy.loginfo("姓名:%s, 年龄:%d, 身高:%.2f",p.name, p.age, p.height)

2. Suscriptor

#! /usr/bin/env python
"""
    订阅方:
        订阅消息

"""
import rospy
from demo02_talker_listener.msg import Person

def doPerson(p):
    rospy.loginfo("接收到的人的信息:%s, %d, %.2f",p.name, p.age, p.height)


if __name__ == "__main__":
    #1.初始化节点
    rospy.init_node("listener_person_p")
    #2.创建订阅者对象
    sub = rospy.Subscriber("chatter_person",Person,doPerson,queue_size=10)
    rospy.spin() #4.循环

3. Configuración de permisos

chmod +x *.py

4. Configurar CMakeLists.txt

catkin_install_python(PROGRAMS
  scripts/talker_p.py
  scripts/listener_p.py
  scripts/person_talker.py
  scripts/person_listener.py
  DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)

5. Ejecución

1. Iniciar roscore;

2. Inicie el nodo de publicación;

3. Inicie el nodo de suscripción.

El efecto es el siguiente:


7. Referencia

http://www.autolabor.com.cn/book/ROSTutorials/di-2-zhang-ros-jia-gou-she-ji/22hua-ti-tong-xin/213-hua-ti-tong-xin- zhi-python-shi-xian.html

Supongo que te gusta

Origin blog.csdn.net/qq_44940689/article/details/129215609
Recomendado
Clasificación