[Notas de estudio ROS 4] Tema Comunicación
Directorio de artículos
- [Notas de estudio ROS 4] Tema Comunicación
-
- 0. Descripción general de la comunicación del tema
- 1. Modelo teórico de comunicación temática
- 2. Implementación de Cpp de operaciones básicas de comunicación de temas.
- 3. Implementación Python de operaciones básicas de comunicación de temas.
- 4. Mensaje personalizado para la comunicación del tema
- 5. Implementación de Cpp de llamada de mensaje personalizado de comunicación de tema
- 6. Implementación de Python del mensaje personalizado de comunicación de tema
- 7. Referencia
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.
- Editor
- receptor
- datos (texto sin formato aquí)
3. Proceso
- Escribir la implementación del editor
- Escribir la implementación del suscriptor
- editar archivo de configuración
- 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
- empezar roscore
- Inicie el nodo de publicación
- Inicie el nodo de suscripción
El efecto final es el siguiente, rosrun rqt_graph rqt_graph
puede ver el gráfico de cálculo con
3. Implementación Python de operaciones básicas de comunicación de temas.
1. Proceso
- Escribir la implementación del editor
- Escribir la implementación del suscriptor
- Agregar permisos ejecutables a archivos python
- editar archivo de configuración
- 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: Header
el 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 Header
encabezado.
1. Demanda
Crea un mensaje personalizado que incluya información sobre la persona: nombre, altura, edad, etc.
2. Proceso
- Crear archivos msg en un formato fijo
- editar archivo de configuración
- 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.xml
Agregue 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.txt
Editar 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 head
ruta del archivo generado previamente en c_pp_properties.json
las includepath
propiedades:
{
"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_dependencies
archivos 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: