提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 前言
- 一、话题通信的理论模型
- 二、代码实现
- 1.发布方
- 2.订阅方实现
- 3.自定义msg的发布方和订阅方
- 0.预先:
- 1.代码部分
- 2.依赖部分
- 3.终端及效果(包含计算图)
- 4.注意事项:
- 4.一个想要讨论的概念:回调函数和spin()
前言
前言:
本文是新手的学习笔记。如有错误欢迎指正
使用版本:ubuntu20.04
使用语言:c++
参考链接:http://www.autolabor.com.cn/book/ROSTutorials/
https://blog.csdn.net/qq_44339029/article/details/120579608?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522168007050016800184160811%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=168007050016800184160811&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2alltop_positive~default-1-120579608-null-null.142v77insert_down38,201v4add_ask,239v2insert_chatgpt&utm_term=ubuntu20.04%E5%AE%89%E8%A3%85ros&spm=1018.2226.3001.4187
一、话题通信的理论模型
这里是赵老师的官方学习文档中的模型具体化描述:Talker:提亲者,二狗子
Master:媒婆,婚介网站
Listener:翠花
实现流程:二狗子向媒婆提交个人信息,翠花向媒婆订阅男方个人信息(advertise subscribe)
媒婆把手机号发给翠花,
翠花打电话给狗子,
狗子回应并且加vx(此时二者建立连接,可以通过TCP越过master进行交流)
二、代码实现
1.发布方
(1)总体步骤:
1.包含头文件
2.初始化ros节点
3.创建节点句柄
4.创发布者对象
5.编写发布逻辑并发布数据
#include "ros/ros.h"
#include "std_msgs/String.h"
#include <sstream>
/*
发布方实现1.包含头文件2.初始化ros节点3.创建节点句柄4.创发布者对象5.编写发布逻辑并发布数据
*/
int main(int argc,char *argv[]){setlocale(LC_ALL,"");//初始化ros节点ros::init(argc,argv,"Hutao");//创建节点句柄ros::NodeHandle nh;//创建发布者对象ros::Publisher pub = nh.advertise<std_msgs::String>("fang",10);//编写发布逻辑和发布数据//*添加要求 10HZ 并且在文本后添加编号//先发布被创建的信息std_msgs::String msg;//发布;频率ros::Rate rate(10);//编号int count = 0 ;//编写循环,循环中发布数据while(ros::ok()){count++;//实现字符串拼接数字;stringstream类:https://blog.csdn.net/Sakuya__/article/details/122751238std::stringstream ss;ss<<"hello ---"<<count;//msg.data="hello";msg.data=ss.str();pub.publish(msg);//添加日志ROS_INFO("发布的数据是:%s",ss.str().c_str());rate.sleep();ros::spinOnce();//官方建议添加。处理回调函数}}
(3)调整cmake
add_executable(demo01_pub src/demo01_pub.cpp)
target_link_libraries(demo01_pub${catkin_LIBRARIES}
)
(4)终端代码及效果
cd 工作空间
source ./devel/setup.bash
source rosrun 文件夹 cpp文件名(无后缀)
(5)可能出现的问题
中文乱码问题:???????
解决方法:setlocale(LC_ALL,"");
2.订阅方实现
1.步骤
1.包含头文件ros中文本类型--->std_msgs/String.h 2.初始化ros节点 3.创建节点句柄 4.创建订阅者对象 5.处理订阅到的数据 6.spin()函数
2.代码实现
# include "ros/ros.h"
#include "std_msgs/String.h"void doMsg(const std_msgs::String::ConstPtr &msg_p){//通过msg获取并且操作订阅到的数据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,"");ros::init(argc,argv,"listener");ros::NodeHandle nh;//实例化 订阅者对象ros::Subscriber sub= nh.subscribe("fang",10,doMsg);//fang就是主题//处理订阅的消息(回调函数)ros::spin();//如果没有很可能订阅不到,这里是堵住的意思,等待话题进来回调*/}
3.终端及代码实现/终端输入:与上面发布方同理
效果:
3.自定义msg的发布方和订阅方
0.预先:
(1)在功能包(src)下定义msg目录,添加文件person.msg
string name
int32 age
float32 height
(2)在package.xml中添加依赖
<build_depend>message_generation</build_depend><exec_depend>message_runtime</exec_depend>
(3)在cmakeList中编译相关
这几个解除注释,**部分为标注,请自行添加
find_package(catkin REQUIRED COMPONENTSroscpprospystd_msgs***message_generation***
)
# 生成消息时依赖于 std_msgs
generate_messages(DEPENDENCIESstd_msgs
)
#执行时依赖
catkin_package(
# INCLUDE_DIRS include
# LIBRARIES demo02_talker_listenerCATKIN_DEPENDS roscpp rospy std_msgs ***message_runtime***
# DEPENDS system_lib
)# 需要加入 message_generation,必须有 std_msgs
(4)c_cpp_properties.json配置
head文件路径:邮件继承目录打开,pwd并且去掉include后面部分
zzl@zzl-lenovo:~/talker_c/devel/include/plumbing_pubsub$ pwd
/home/zzl/talker_c/devel/include/plumbing_pubsub
{"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.代码部分
/*需求: 循环发布人的信息*/#include "ros/ros.h"
#include "plumbing_pubsub/Person.h"int main(int argc, char *argv[])
{setlocale(LC_ALL,"");ROS_INFO("这是消息发布方:");//1.初始化 ROS 节点ros::init(argc,argv,"talker_person");//2.创建 ROS 句柄ros::NodeHandle nh;//3.创建发布者对象ros::Publisher pub = nh.advertise<plumbing_pubsub::Person>("information",10);//4.组织被发布的消息,编写发布逻辑并发布消息plumbing_pubsub::Person person;person.name = "张三";person.age = 1;person.height = 1.73;ros::Rate rate(1);while (ros::ok()){pub.publish(person);person.age += 1;ROS_INFO("我叫:%s,今年%d岁,高%.2f米", person.name.c_str(), person.age, person.height);rate.sleep();ros::spinOnce();}return 0;
}
--------------------------------------------
/*
订阅方1.包含头文件2.初始化ros节点3.创建节点句柄4.处理订阅者对象5.处理订阅的数据6.调用spin()函数
*/
#include "ros/ros.h"
#include "plumbing_pubsub/Person.h"void doPerson(const plumbing_pubsub::Person::ConstPtr& person){ROS_INFO("订阅人的信息:%s, %d, %.2f",person->name.c_str(),person->age,person->height);
}int main(int argc, char *argv[])
{setlocale(LC_ALL,"");ROS_INFO("订阅方:");//1.初始化 ROS 节点ros::init(argc,argv,"listen_people");//2.创建 ROS 句柄ros::NodeHandle nh;//3.创建发布者对象ros::Subscriber sub = nh.subscribe("information",10,doPerson);ros::spin();return 0;
}
2.依赖部分
add_executable(demo03_pubmsg src/demo03_pubmsg.cpp)
add_executable(demo04_submsg src/demo04_submsg.cpp)
add_dependencies(demo03_pubmsg ${PROJECT_NAME}_generate_messages_cpp)
add_dependencies(demo04_submsg ${PROJECT_NAME}_generate_messages_cpp)
target_link_libraries(demo03_pubmsg${catkin_LIBRARIES}
)
target_link_libraries(demo04_submsg${catkin_LIBRARIES}
)
3.终端及效果(包含计算图)
roscorezzl@zzl-lenovo:~$ cd talker_c
zzl@zzl-lenovo:~/talker_c$ source ./devel/setup.bashzzl@zzl-lenovo:~/talker_c$ rosrun plumbing_pubsub demo03_pubmsg
zzl@zzl-lenovo:~/talker_c$ rosrun plumbing_pubsub demo04_submsg
rqt_graph
4.注意事项:
(1)不要提前写依赖,可能在编译时出现这个情况
这里我提前写了订阅方的依赖导致的
4.一个想要讨论的概念:回调函数和spin()
一个讲的比较好的视频链接:https://www.bilibili.com/video/BV1vL411t78b/?spm_id_from=333.337.search-card.all.click&vd_source=d8532aebb8bb1bfb5088a1a3cef25521
尽管如此,我仍旧不能清楚认知回调函数和spin(),以下为为的个人理解。希望有大佬可以指正
回调函数:即把函数名(函数指针)作为参数调用
关于上面subscribe调用domsg函数,我的理解是:subscribe和发布者的消息连接,当有新消息传来时候,subscribe就调用domsg函数作为回调函数,但是如果没有spin(),就不会执行,相当于开辟一个子流程,类似异步通信?中断驱动?spin()就是停下来不断检测有无回调函数
关于我对这个函数的回调和spin()粗俗理解:
翠花订阅二狗子后,一旦二狗子在房有新消息,就给翠花发消息,翠花就把不断看微信消息这件事放在代办里(回调函数),spin是一直反复检查代办。如果不检查则不会看代办