ROS自定義msg型別及使用
一、建立msg訊息
首先建立一個空的package單獨存放msg型別(當然也可以在任意的package中自定義msg型別)
這裡為便於說明,建立一個名為test_msgs的包,用於對自定義msg型別的用法舉例
$ cd catkin_ws/src
$ catkin_create_pkg test_msgs
1.新建msg檔案
然後在test_msgs中建立msg資料夾,在msg資料夾其中新建一個名為Test.msg
訊息型別檔案
$ cd test_msgs
$ mkdir msg
float32[] data
float32 vel
geometry_msgs/Pose pose
string name
2.修改package.xml
接下來需要message_generation生成C++或Python能使用的程式碼,需要message_runtime提供執行時的支援,所以package.xml中新增以下兩句
<build_depend>message_generation</build_depend>
<run_depend>message_runtime</run_depend>
3.修改CMakeLists.txt
CMakeLists.txt
要注意四個地方
(1)首先呼叫find_package
查詢依賴的包,必備的有roscpp rospy message_generation
geometry_msgs/Pose pose
型別,那麼必須查詢geometry_msgs
find_package(catkin REQUIRED COMPONENTS roscpp rospy message_generation std_msgs geometry_msgs)
(2)然後是add_message_files
,指定msg檔案
add_message_files(
FILES
Test.msg
# Message2.msg
)
(3)然後是generate_messages
,指定生成訊息檔案時的依賴項,比如上面嵌套了其他訊息型別geometry_msgs
#generate_messages必須在catkin_package前面
generate_messages(
DEPENDENCIES
geometry_msgs
)
(4)然後是catkin_package
設定執行依賴
catkin_package(
CATKIN_DEPENDS message_runtime
)
到這裡新的msg型別test_msgs/Test
就可以使用了,下面編譯這個包,然後利用rosmsg show
指令檢視
$ cd catkin_ws
$ catkin_make
$ rosmsg show test_msgs/Test
float32[] data
float32 vel
geometry_msgs/Pose pose
geometry_msgs/Point position
float64 x
float64 y
float64 z
geometry_msgs/Quaternion orientation
float64 x
float64 y
float64 z
float64 w
string name
二、msg的使用
要使用自定義的訊息型別必須source
自定義訊息所在的工作空間,否則rosmsg show test_msgs/Test
和rostopic echo /test_msg
(/test_msg
是節點中使用自定義訊息型別test_msgs/Test
的topic)都會報錯,因為沒有source的情況下自定義訊息型別是不可見的,被認為是未定義型別
一種典型的錯誤是刪掉工作空間下的devel
和build
資料夾之後重新編譯程式碼,此時由於沒有source
自定義訊息所在的工作空間,即使使用自定義訊息的程式碼和自定義訊息在同一個包也無法找到,該訊息標頭檔案,此時需要執行source devel/setup.bash
之後重新編譯就好
1.其他包呼叫自定義msg型別
參考:
DefiningCustomMessages
如果是在test_msgs包內的節點中呼叫test_msgs/Test
型別,只需要在.cpp檔案中如下呼叫即可
#include <test_msgs/Test.h>
test_msgs::Test msg;
如果是在其他包呼叫test_msgs/Test
型別則需要修改package.xml和CMakeLists.txt,比如同樣在工作空間catkin_ws內有一個名為test的包,我們可以在這個包內寫一個節點,使用我們剛才自定義的訊息型別test_msgs/Test
,如下:
(1)修改package.xml
養成好習慣,維護軟體包清單的更新,以便於別人使用你的軟體前安裝各種依賴項,當然這個檔案不影響程式編譯
<build_depend>roscpp</build_depend>
<run_depend>roscpp</run_depend>
<build_depend>test_msgs</build_depend>
<run_depend>test_msgs</run_depend>
(2)修改CMakeLists.txt
呼叫自定義訊息型別主要修改兩個地方,以下是重點:
一是find_package
中需要宣告查詢包含該訊息型別的包;
二是add_dependencies
要註明該訊息的依賴,其他地方和普通節點一樣
find_package(catkin REQUIRED COMPONENTS
roscpp
geometry_msgs
test_msgs
)
add_dependencies(test1 test_msgs_gencpp)#呼叫同一工作空間的自定義訊息型別時註明依賴關係,防止發生標頭檔案找不到的報錯
如果缺少add_dependencies
中對test_msgs_gencpp
的依賴宣告,在編譯的時候如果先編譯test包再編譯test_msgs包則會出現如下報錯(ROS工作空間各個軟體包的編譯順序是隨機的),因為標頭檔案test_msgs/Test.h
還未生成
fatal error: test_msgs/Test.h: 沒有那個檔案或目錄
#include "test_msgs/Test.h"
2.msg型別陣列的使用
std_msgs/MultiArrayLayout layout
float32[] data
其中data
是一個浮點陣列,但是方括號只是一個用來表明它是陣列的符號,我們不能在定義的時候在方括號中給定陣列長度,實際上ROS中類似float32[]
,int8[]
這樣的陣列型別都是std::vector
,使用方法也和std::vector
一樣
測試程式碼可以從這裡下載
下面是一個訊息訂閱節點,用於測試
/************************
* @Author: Jinglin Zhang
* @DateTime: 2017-06-07 19:57:30
* @Description: 節點test1,訂閱了test_msgs包下talker節點發布的"test_msg"話題,用於測試test_msgs::Test訊息型別
************************/
#include <ros/ros.h>
#include <test_msgs/Test.h>
void msgCallback(const test_msgs::Test::ConstPtr &msg)
{
//test_msgs::Test型別裡的float32[]資料傳到vector
std::vector<float> array = msg->data;
std::cout << "msg->data[0]=" << msg->data[0] << std::endl;
std::cout << "msg->data.size=" << msg->data.size() << std::endl;
std::cout << "msg->data=" << msg->data[0] << ", " << msg->data[1] << ", " << msg->data[2] << ", " << msg->data[3] << ", " << msg->data[4] << ", " << msg->data[5] << std::endl;
}
int main(int argc,char ** argv)
{
ros::init(argc,argv,"test1");
ros::NodeHandle n;
ros::Subscriber msg_sub = n.subscribe("test_msg", 100, msgCallback);
ros::spin();
return 0;
}
下面是一個訊息釋出節點,用於測試
/************************
* @Author: Jinglin Zhang
* @DateTime: 2017-06-07 20:03:35
* @Description: 節點talker,釋出"test_msg"話題,用於測試test_msgs::Test訊息型別
************************/
#include <ros/ros.h>
#include <test_msgs/Test.h>
int main(int argc, char **argv)
{
ros::init(argc, argv, "msg_talker");
ros::NodeHandle n;
ros::Publisher msg_pub = n.advertise<test_msgs::Test>("test_msg", 1000);
ros::Rate loop_rate(10);
int count = 0;
while (ros::ok())
{
test_msgs::Test msg;
std::cout << "msg.data.size=" << msg.data.size() << std::endl;
//用vector給float32[]陣列賦值
float array[3] = {1.1,1.2,0.3};
std::vector<float> array1(array,array+3);
msg.data = array1;
std::cout << "msg.data3[0]=" << msg.data[0] << std::endl;
std::cout << "msg.data3.size=" << msg.data.size() << std::endl;
//下標訪問float32[]陣列
msg.data[0] = 0.1;
std::cout << "msg.data3[0]=" << msg.data[0] << std::endl;
float array4[4] = {1.0,2.0,0.3,6.6};
std::vector<float> array41(array4,array4+4);
msg.data = array41;
std::cout << "msg.data4.size=" << msg.data.size() << std::endl;
std::cout << "msg.data4=" << msg.data[0] << " " << msg.data[1] << " " << msg.data[2] << " " << msg.data[3] << std::endl;
msg.data.push_back(5.5);
std::cout << "msg.data[5]=" << msg.data[4] << std::endl;
//使用迭代器
msg.data.resize(6);
std::cout << "msg.data6.size=" << msg.data.size() << std::endl;
std::cout << "msg.data6=" ;
for(std::vector<float>::iterator it = msg.data.begin(); it != msg.data.end(); ++it)
{
*it = 0.6;
std::cout << *it << " ";
}
std::cout << std::endl;
msg_pub.publish(msg);
ros::spinOnce();
loop_rate.sleep();
++count;
}
return 0;
}