1、說明
編寫完ROS的hello world程序后,基本可以了解ROS的功能包開發的流程,現在再編寫一個節點通信的測試代碼,使用 topic 模式,即發布-訂閱者模式,且傳遞的消息是自定義類型
上代碼
2、創建工作空間
該例不再創建新的工作空間,和hello world示例共用一個
3、創建功能包
cd src
catkin_create_pkg test_topic message_generation std_msgs roscpp
這里在創建功能包的時候,順帶把需要依賴的庫也加進去了,好處就是,會默認把依賴庫添加到 CMakeLists.txt 和 package.xml 中,而在 hello world 例子中,是手動添加的
message_generation 會根據自定義類型的描述文件生成代碼
std_msgs 是ROS的字符串類
這個功能包的名稱就是 test_topic
4、編寫自定義傳輸類型文件
cd test_topic
mkdir msg && cd msg
touch MessageDefine.msg
編輯 MessageDefine.msg 文件,內容如下:
time stamp
int32 data
string speak
這里有三種類型,time 會生成ROS的Time類,這里不做詳細說明
5、編寫源代碼
cd ../src
依照慣例,代碼還是放在src目錄下
5.1、編寫發布者代碼
新增文件 test_publisher.cpp,代碼如下:
#include <ros/ros.h>
#include <test_topic/MessageDefine.h>
int main(int argc, char **argv)
{
//初始化發布者節點名稱
ros::init(argc, argv, "topic_publisher");
//聲明節點句柄與ROS系統進行通訊
ros::NodeHandle nh;
//聲明發布者,創建一個使用test_topic功能包MessageDefine消息文件的發布者
//ros_tutorial_pub,話題名稱是ros_message,消息發布者隊列設置為100,如果消息發送太快,緩沖區的消息大於100,則會丟棄隊列頭
ros::Publisher ros_tutorial_pub = nh.advertise<test_topic::MessageDefine>("ros_message", 100);
//設定循環周期0.25HZ,4秒,需要配合sleep方法使用
ros::Rate loop_rate(0.25);
test_topic::MessageDefine msg; //聲明一個消息
int count = 0;
std::stringstream talk;
while (ros::ok())
{
//給MessageDefine.msg文件中的消息變量賦值
msg.stamp = ros::Time::now();
msg.data = count;
msg.speak = "hello world, number: " + std::to_string(count);
ROS_INFO("send stamp second = %d", msg.stamp.sec); //顯示消息
ROS_INFO("send data = %d", msg.data);
ROS_INFO("%s", msg.speak.c_str());
ros_tutorial_pub.publish(msg); //發布顯示的消息
ros::spinOnce(); //循環等待訂閱節點的所有回調函數
loop_rate.sleep(); //按設定值循環
++count;
}
return 0;
}
這里的 ros_message 是topic名稱
5.2、編寫訂閱者代碼
ros_message新增 test_subscriber.cpp,代碼如下:
#include <ros/ros.h>
#include <test_topic/MessageDefine.h>
//回調函數
void messageCallback(const test_topic::MessageDefine::ConstPtr &msg)
{
ROS_INFO("recieve stamp second = %d", msg->stamp.sec);
ROS_INFO("recieve data = %d", msg->data);
ROS_INFO("receive str:[%s]", msg->speak.c_str());
}
int main(int argc, char **argv)
{
ros::init(argc, argv, "topic_subscriber"); //初始化訂閱者節點
ros::NodeHandle nh;
//聲明訂閱者,創建一個使用test_topic功能包MessageDefine消息文件的訂閱者
//ros_tutorial_sub,訂閱的話題名稱是ros_message,消息接收者隊列設置為100,如果消息處理太慢,緩沖區的消息數量大於100,則丟棄隊列頭部的消息
ros::Subscriber ros_tutorial_sub = nh.subscribe<test_topic::MessageDefine>("ros_message", 100, messageCallback);
ros::spin(); //調用后台,等待接收消息
return 0;
}
ros::spin() 讓程序進入自循環的掛起狀態,讓程序以最好的效率接收並執行回調
當有消息達到 ros_message 時,執行一次回調函數
6、編譯
修改編譯配置
因為創建功能包的時候,顯示地指定了依賴,這些依賴會自動寫進 CMakeLists.txt 和 package.xml 文件中,CMakeLists.txt 需要簡單修改,package.xml文件無需改動
CMakeLists.txt 文件如下
cmake_minimum_required(VERSION 3.0.2)
project(test_topic)
add_compile_options(-std=c++11)
#依賴庫,創建包的時候添加了依賴,這里自動生成
find_package(catkin REQUIRED COMPONENTS
message_generation
roscpp
std_msgs
)
#添加自定義類型文件,這里自動生成的代碼中注釋說在msg文件夾下
## Generate messages in the 'msg' folder
add_message_files(
FILES
MessageDefine.msg
)
generate_messages(
DEPENDENCIES
std_msgs
)
catkin_package(
LIBRARIES test_topic
CATKIN_DEPENDS roscpp std_msgs
)
include_directories(
${catkin_INCLUDE_DIRS}
)
#以下內容需要手動添加修改
add_executable(${PROJECT_NAME}_publisher_node src/test_publisher.cpp)
target_link_libraries(${PROJECT_NAME}_publisher_node ${catkin_LIBRARIES})
add_executable(${PROJECT_NAME}_subscriber_node src/test_subscriber.cpp)
target_link_libraries(${PROJECT_NAME}_subscriber_node ${catkin_LIBRARIES})
編譯指定包
catkin_make -DCATKIN_WHITELIST_PACKAGES="test_topic"
7、啟動運行
該例的包名為 test_topic,連個節點名是 test_topic_publisher_node 和 test_topic_subscriber_node
先啟動節點管理器
roscore
啟動發布者節點
cd ~/project/catkin_ws
source devel/setup.bash
rosrun test_topic test_topic_publisher_node
啟動訂閱者節點
cd ~/project/catkin_ws
source devel/setup.bash
rosrun test_topic test_topic_subscriber_node
查看效果,發布者每隔一段時間發送數據,訂閱者接收到數據,打印出來
8、查看ROS網絡結構圖
rqt_graph
結構圖如下: