前面我們已經(jīng)學(xué)習(xí)了publisher如何向ros發(fā)布消息传透,subscriber如何從ros接收消息晓殊。那publisher和subscriber之間能由什么聯(lián)系呢塞椎?這節(jié)課我們來學(xué)習(xí)publisher向subscriber發(fā)送自定義的信息裹赴。
定義msg文件
首先在文件夾learning_topic
中新建一個(gè)命名為msg
的文件夾尔许,這個(gè)文件夾用來存放后續(xù)的消息類文件
進(jìn)入該文件夾下并在該文件夾下打開命令行么鹤,使用touch
指令新建Person.msg
文件
打開該文件,輸入以下信息后保存
string name
uint8 sex
uint8 age
uint8 unknown = 0
uint8 male = 1
uint8 female = 2
這里寫的并不是cpp母债,也不是python午磁。但他在編譯過程中會(huì)由ros自動(dòng)編譯成cpp或python
在package.xml中添加功能包依賴
打開文件夾learning_topic
,打開package.xml
毡们。如果你的電腦安裝了瀏覽器迅皇,xml
文件可能會(huì)由瀏覽器默認(rèn)打開,不能編輯衙熔〉峭牵可以在這個(gè)目錄下打開命令行,可以輸入以下指令打開
gedit package.xml
滾動(dòng)到最下方红氯,在如圖所示位置添加以下語(yǔ)句框咙,保存
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
<build_depend>用于添加編譯依賴咕痛,這里添加message_generation
功能包
<exec_depend<用于添加執(zhí)行依賴,這里添加message_runtime
功能包
在CMakeLists.txt添加編譯選項(xiàng)
打開文件夾learning_topic
喇嘱,打開CMakeLists.txt
茉贡。
- 在最上方的
find_package
中添加message_generation
- 找到
Declare ROS dynamic reconfigure parameters
的位置,在上方添加如下語(yǔ)句
add_message_files(FILES Person.msg)
generate_messages(DEPENDENCIES std_msgs)
- 第一句話者铜,告訴編譯器
Person.msg
是我們定義的消息接口 - 第二句話腔丧,告訴編譯器在編譯
Person.msg
文件的時(shí)候,需要依賴哪些庫(kù)/包作烟。這里我們需要依賴std_msgs
愉粤,剛剛我們寫的如string
、uint8
拿撩,就是在std_msgs
中定義的衣厘。
- 找到
catkin specific configuration
(即Build
上方),在圖示位置添加以下語(yǔ)句(我這里比較特殊压恒,正上方的注釋掉的語(yǔ)句與要添加的語(yǔ)句一模一樣影暴,這種情況可以不添加新語(yǔ)句而是直接把那句話的注釋解除掉)
CATKIN_DEPENDS geometry_msgs roscpp rospy std_msgs turtlesim
做完以上三步,保存退出涎显。
回到工作空間的根目錄catkin_ws
下坤检,打開命令行兴猩,嘗試進(jìn)行編譯期吓。輸入指令
catkin_make
編譯成功,可以在圖示的路徑下倾芝,找到剛剛編寫的Person.msg
文件已經(jīng)被編譯成為Person.h
文件
編寫cpp程序
在圖示路徑下建立兩個(gè)cpp文件讨勤。代碼如下
person_publisher.cpp
/**
* 該例程將發(fā)布/person_info話題,自定義消息類型learning_topic::Person
*/
#include <ros/ros.h>
//3苛怼L肚А!=枘颉刨晴!關(guān)鍵!B贩1否!茂契!
#include "learning_topic/Person.h"
int main(int argc, char **argv)
{
// ROS節(jié)點(diǎn)初始化
ros::init(argc, argv, "person_publisher");
// 創(chuàng)建節(jié)點(diǎn)句柄
ros::NodeHandle n;
// 創(chuàng)建一個(gè)Publisher蝶桶,發(fā)布名為/person_info的topic,消息類型為learning_topic::Person掉冶,隊(duì)列長(zhǎng)度10
//U媸F暄!;止病战秋!關(guān)鍵!L志隆;裱!拐袜!
ros::Publisher person_info_pub = n.advertise<learning_topic::Person>("/person_info", 10);
// 設(shè)置循環(huán)的頻率
ros::Rate loop_rate(1);
int count = 0;
while (ros::ok())
{
// 初始化learning_topic::Person類型的消息
learning_topic::Person person_msg;
person_msg.name = "Tom";
person_msg.age = 18;
person_msg.sex = learning_topic::Person::male;
// 發(fā)布消息
person_info_pub.publish(person_msg);
ROS_INFO("Publish Person Info: name:%s age:%d sex:%d",
person_msg.name.c_str(), person_msg.age, person_msg.sex);
// 按照循環(huán)頻率延時(shí)
loop_rate.sleep();
}
return 0;
}
person_subscriber.cpp
/**
* 該例程將訂閱/person_info話題吉嚣,自定義消息類型learning_topic::Person
*/
#include <ros/ros.h>
//!5牌獭3⒍摺!甜攀!關(guān)鍵G镄埂!9娣А:阈颉!
#include "learning_topic/Person.h"
// 接收到訂閱的消息后谁撼,會(huì)進(jìn)入消息回調(diào)函數(shù)
void personInfoCallback(const learning_topic::Person::ConstPtr& msg)
{
// 將接收到的消息打印出來
ROS_INFO("Subcribe Person Info: name:%s age:%d sex:%d",
msg->name.c_str(), msg->age, msg->sex);
}
int main(int argc, char **argv)
{
// 初始化ROS節(jié)點(diǎn)
ros::init(argc, argv, "person_subscriber");
// 創(chuàng)建節(jié)點(diǎn)句柄
ros::NodeHandle n;
// 創(chuàng)建一個(gè)Subscriber歧胁,訂閱名為/person_info的topic,注冊(cè)回調(diào)函數(shù)personInfoCallback
//@鞯:拔 !9抗摹崭参!關(guān)鍵!?羁А:文骸!铐殃!
ros::Subscriber person_info_sub = n.subscribe("/person_info", 10, personInfoCallback);
// 循環(huán)等待回調(diào)函數(shù)
ros::spin();
return 0;
}
cpp文件編寫好后海洼,編寫圖示路徑下的CMakeLists.txt
文件,對(duì)剛剛編寫的內(nèi)容進(jìn)行配置背稼。
找到Install
位置的上方贰军,添加以下語(yǔ)句。保存
說明:這里相比之前配置CMakeLists.txt
,多了add_dependencies
的語(yǔ)句目的是讓可執(zhí)行文件(前兩句做的工作)和動(dòng)態(tài)生成的文件產(chǎn)生依賴關(guān)系
add_executable(person_publisher src/person_publisher.cpp)
target_link_libraries(person_publisher ${catkin_LIBRARIES})
add_dependencies(person_publisher ${PROJECT_NAME}_generate_messages_cpp)
add_executable(person_subscriber src/person_subscriber.cpp)
target_link_libraries(person_subscriber ${catkin_LIBRARIES})
add_dependencies(person_subscriber ${PROJECT_NAME}_generate_messages_cpp)
編譯词疼、運(yùn)行
步驟和之前的完全一樣俯树。
- 回到工作空間根目錄
catkin_ws
,運(yùn)行
catkin_make
- 新開一個(gè)終端贰盗,執(zhí)行
roscore
- 新開一個(gè)終端许饿,執(zhí)行
rosrun learning_topic person_subscriber
- 新開一個(gè)終端,執(zhí)行
rosrun learning_topic person_publisher
消息發(fā)送者publisher和消息接收者subscriber已經(jīng)構(gòu)建聯(lián)系
概念:ROS Master是幫助節(jié)點(diǎn)的創(chuàng)建和鏈接的舵盈。節(jié)點(diǎn)一旦創(chuàng)建和鏈接后陋率,就不受ROS Master影響
此時(shí),在執(zhí)行命令roscore
的終端按ctrl+c
退出roscore(即退出ROS Master)秽晚,會(huì)發(fā)現(xiàn)subscriber和publisher還在收發(fā)數(shù)據(jù)瓦糟。
除非是需要再訪問ROS Master介入的參數(shù)
python代碼實(shí)現(xiàn)
在圖示路徑下建立python文件,代碼如下
右鍵你的python文件赴蝇,選擇properties
菩浙,務(wù)必確保Allow executing files as program
選項(xiàng)是打鉤的
person_publisher.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# 該例程將發(fā)布/person_info話題,自定義消息類型learning_topic::Person
import rospy
from learning_topic.msg import Person
def velocity_publisher():
# ROS節(jié)點(diǎn)初始化
rospy.init_node('person_publisher', anonymous=True)
# 創(chuàng)建一個(gè)Publisher句伶,發(fā)布名為/person_info的topic劲蜻,消息類型為learning_topic::Person,隊(duì)列長(zhǎng)度10
person_info_pub = rospy.Publisher('/person_info', Person, queue_size=10)
#設(shè)置循環(huán)的頻率
rate = rospy.Rate(10)
while not rospy.is_shutdown():
# 初始化learning_topic::Person類型的消息
person_msg = Person()
person_msg.name = "Tom";
person_msg.age = 18;
person_msg.sex = Person.male;
# 發(fā)布消息
person_info_pub.publish(person_msg)
rospy.loginfo("Publsh person message[%s, %d, %d]",
person_msg.name, person_msg.age, person_msg.sex)
# 按照循環(huán)頻率延時(shí)
rate.sleep()
if __name__ == '__main__':
try:
velocity_publisher()
except rospy.ROSInterruptException:
pass
person_subscriber.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# 該例程將訂閱/person_info話題考余,自定義消息類型learning_topic::Person
import rospy
from learning_topic.msg import Person
def personInfoCallback(msg):
rospy.loginfo("Subcribe Person Info: name:%s age:%d sex:%d",
msg.name, msg.age, msg.sex)
def person_subscriber():
# ROS節(jié)點(diǎn)初始化
rospy.init_node('person_subscriber', anonymous=True)
# 創(chuàng)建一個(gè)Subscriber先嬉,訂閱名為/person_info的topic,注冊(cè)回調(diào)函數(shù)personInfoCallback
rospy.Subscriber("/person_info", Person, personInfoCallback)
# 循環(huán)等待回調(diào)函數(shù)
rospy.spin()
if __name__ == '__main__':
person_subscriber()
- 新開一個(gè)終端楚堤,執(zhí)行
roscore
- 新開一個(gè)終端疫蔓,執(zhí)行
rosrun learning_topic person_subscriber.py
- 新開一個(gè)終端,執(zhí)行
rosrun learning_topic person_publisher.py