歡迎來到第8講,預(yù)計還有兩講結(jié)束ros相關(guān)的話題。
Rviz作為ROS的可視化工具擁有非常強(qiáng)大的功能,我也不是全部都用過,略懂皮毛??梢暬俏覀兎治鰡栴},向他人展現(xiàn)成果時最重要的工具。打個比方,我們在第三講能夠發(fā)布poseStamped類型的消息了,并且在上一講我們還制作了一個包含poseStamped類型消息,topic名字為chatter的rosbag. 我們得到了不同timestamp下的機(jī)器人的pose,那么我們?nèi)绻虬褭C(jī)器人的pose畫出來,既方便我們自己分析,又方便別人看懂,該怎么辦呢?自然要用到rviz了。
ros官方的rviz教程在下面的鏈接中,大家自然得去熟悉一下
http://wiki.ros.org/rviz/UserGuide
我個人的講解還是會結(jié)合例子講,而不會講rviz的UI的每個窗口都是包含什么東西。另外要注意的是我用的例子是我在工作中碰到了問題,使用rviz去解決,可并不是說rviz就只能這么用!
上一講我們制作了的rosbag(你沒看上一講但是已經(jīng)了解了rosbag的話,就直接去我的github下載下來那個rosbag吧
https://github.com/zhaozhongch/ros_tutorial
rosbag在pub_sub_test/src/material里)。想把每一時刻機(jī)器人的pose給畫出來,我們就得使用rosbag里的信息。我們說了,使用rosbag時它自身相當(dāng)于一個publisher,使用的方法是
rosbag play record_poseStamped.bag
有了publisher,我們自然就需要一個subscriber。雖然我們現(xiàn)在還不知道rviz怎么用,但是我們大概能猜到在rviz中我們需要定義一個subsriber,來接收那個rosbag發(fā)布出的消息。接下來我們打開rviz。
首先跑roscore,之后打開另一個terminal,在里面輸入
rosrun rviz rviz
就打開rviz的界面了。

如果有些許不一樣不要擔(dān)心,有可能是我的版本太新了。沒錯,筆者已經(jīng)悄悄地升級到了ubuntu18.04LTS系統(tǒng)外加最新版本的ros melodic了,程序猿嘛,就是要跟得上時代。

咳咳以前的代碼都是沒有問題能用的這點(diǎn)不要擔(dān)心。
上面那個界面呢你一進(jìn)去當(dāng)然是一臉懵逼的,沒有關(guān)系,我們還是本著有publisher就需要一個subscriber的想法,找一個消息的接收者。接下來的步驟大家先照著做,等結(jié)果出來了我們再來慢慢解釋各種原由。
1:點(diǎn)擊界面左下方的
Add出現(xiàn)下面窗口

2:點(diǎn)擊里面的
Marker,然后選擇OK。這時候我們會發(fā)現(xiàn)在rivz界面左邊的Displays那個大框框中,Grid下出現(xiàn)了一個新的東西,名字就是我們剛剛選擇的Marker。
Marker旁邊有一個小三角,是Marker包含的下拉列表,點(diǎn)一下,就會出來和我這張圖一樣的東西了。在下拉列表里我們看到了一個很熟悉的東西
... Topic。此時,這個Marker就已經(jīng)是一個subscriber了,并且正在試圖接收消息,它目前接收的是來自visualization_marker這個topic的消息。下面還有Queue_Size,也是我們以前定義一個subscriber時所熟悉的內(nèi)容。Namespace可以暫且不管。那么marker到底是什么呢?和它的名字一樣,它就是一個用來標(biāo)記的東西。如果我們給定marker一個position和orientation(位置和姿態(tài)),那么rviz就會在中間畫圖區(qū)域的指定位置生成一個指定方向的marker,這個marker可以是立方體,箭頭等我們可以在程序中自行選擇。如何給marker一個orientation和position那自然就是我們發(fā)布消息,marker接收消息,消息里面包含了marker的位置和姿態(tài)。
下面我們就可以試試寫程序,把我們之前的rosbag中記錄的position和orientation發(fā)布給marker。這樣rviz就能在畫出每一時刻指定的position和orientation看起來什么樣。我們寫一個接收rosbag里posetamped的程序,并把posestamp轉(zhuǎn)化為marker能接收的消息種類再發(fā)布出來,讓marker接收。
我們創(chuàng)建一個新的package來學(xué)習(xí)rviz
cd ~/catkin_ws/src
catkin_create_pkg learn_rviz_tf roscpp rospy std_msgs geometry_msgs visualization_msgs tf
cd ..
catkin_make
創(chuàng)建的package名稱叫learn_rviz_tf,因?yàn)橄乱恢v我們會結(jié)合rviz一起學(xué)習(xí)tf,所以就干脆在一個package里寫程序了。 他的依賴項除了我們之前常用的外多了visualization_msgs,這里面會包含marker類型的消息;多了tf,tf是我們下一講的內(nèi)容。
接下來在learn_rviz_tf的src文件夾創(chuàng)建一個pub_marker_msgs.cpp的文件并在里面輸入下面的代碼。
#include "ros/ros.h"
#include "geometry_msgs/PoseStamped.h"
#include "visualization_msgs/Marker.h"
class MarkerPublisher{
public:
MarkerPublisher(ros::NodeHandle& nh){
pub_marker_ = nh.advertise<visualization_msgs::Marker>("visualization_marker", 10);//initialize marker publisher
set_marker_fixed_property();
};
void PoseCallback(const geometry_msgs::PoseStamped::ConstPtr& msg){
count_++;
marker_.ns = "my_namespace";
marker_.id = count_;
marker_.header.stamp = ros::Time();
marker_.pose = msg->pose;
pub_marker_.publish(marker_);
};
void set_marker_fixed_property(){
/*decide from which view we can see the marker*/
marker_.header.frame_id = "my_frame";
/*set marker type*/
marker_.type = visualization_msgs::Marker::SPHERE;
/*decide if the marker will be enlarge*/
marker_.scale.x = 1;
marker_.scale.y = 0.1;
marker_.scale.z = 0.1;
/*decide the color of the marker*/
marker_.color.a = 1.0; // Don't forget to set the alpha!
marker_.color.r = 0.0;
marker_.color.g = 1.0;
marker_.color.b = 0.0;
/*set marker action*/
marker_.action = visualization_msgs::Marker::ADD;
};
private:
ros::Publisher pub_marker_;
visualization_msgs::Marker marker_;
int count_ = 0;
};
int main(int argc, char **argv){
ros::init(argc, argv, "marker_worker");
ros::NodeHandle n;
MarkerPublisher mp(n);
ros::Subscriber sub_pose = n.subscribe("chatter", 100, &MarkerPublisher::PoseCallback, &mp);
ros::spin();
}
主函數(shù)的前面兩行大家都很熟悉了。
第三行我們定義了MarkerPublisher的對象mp并把nodehandle傳入,MarkerPublisher這個類是我們建立來發(fā)布Marker這種類型的消息的。
第四行我們定義了接收chatter這個topic的接收器,由于callback函數(shù)定義在了MarkerPublisher這個類里,所以注意一下定義的方式。這個接收器以及接收函數(shù)是用來接收我們rosbag里topic名字為chatter,消息類型為posestamped的消息,所以注意topic的名字和callback函數(shù)里參數(shù)要和rosbag里一一對應(yīng)起來。
在類中發(fā)布消息的方式我們在第五講講過,不熟悉可以再去回顧一下。
spin函數(shù)檢測是否有消息發(fā)布。
接下來看MarkerPublisher這個類。
首先我們已經(jīng)將visualization_msgs::Marker類型的消息marker定義為私有成員了。這樣我們不用每次發(fā)布消息時都重新定義一個marker并且為它的各種性質(zhì)重新賦值。
類的構(gòu)造函數(shù)中我們定義了pub_marker的具體內(nèi)容,它將要發(fā)布topic名為visualization_marker,消息類型為visualization_msgs::Marker的消息。首先topic的名字,是必須和rviz中我們設(shè)置的marker的topic名字對應(yīng)(上一張圖里我們可以看到 Marker Topic的名字是visualization_marker)。這個消息類型具體怎么賦值呢?我們第三講講過查自己需要的消息類型的方法,我們在搜索引擎中輸入類似于visualization_msgs, marker, ros這類的東西,就可以看到下面的網(wǎng)頁
http://docs.ros.org/melodic/api/visualization_msgs/html/msg/Marker.html
點(diǎn)進(jìn)去你會看到這個消息包含的成員變量太多了,頭都大了,怎么用不是很清楚。還好對于這種稍微復(fù)雜類型的消息,ROS一般有例子可尋找,其實(shí)你稍微花點(diǎn)兒功夫就可以找到下面的網(wǎng)頁
http://wiki.ros.org/rviz/DisplayTypes/Marker
在網(wǎng)頁中exmaple_usage里我們可以看到它有示例代碼。我上面的代碼也是根據(jù)它的內(nèi)容來的。下面回到我的代碼,在構(gòu)造函數(shù)的第二行,我們創(chuàng)建了一個叫set_marker_fixed_property()的函數(shù),這個函數(shù)我們設(shè)置了一些要發(fā)布的marker的一些我們不想改變的性質(zhì)。比如該函數(shù)中的
marker_.header.frame_id = "my_frame"
這行的作用是指定我們在rviz中從哪個坐標(biāo)系去觀看我們的marker。其實(shí)道理很簡單,大家知道機(jī)器人一般有很多參考坐標(biāo)系,什么世界坐標(biāo)系,相機(jī)坐標(biāo)系或者其他傳感器坐標(biāo)系,世界坐標(biāo)系一般固定不動,其他的可能隨機(jī)器人移動。我們上面定義這個my_frame,表示我們希望從my_frame這個坐標(biāo)系里去觀察marker,而之后,可以想象,我們需要在rviz中把世界坐標(biāo)系的名字設(shè)置為my_frame。二者能對應(yīng)上,我們就能從一個固定不變的坐標(biāo)系中觀察我們的marker了。
接下來是
marker_.type = visualization_msgs::Marker::SPHERE;
從字面意思不難理解是把我們marker的樣子定義為球形。能定義成哪些形狀大家可以看上面參考網(wǎng)頁中的資料
這個球得有多大呢,得設(shè)置它的scale。所以接下來是
marker_.scale.x = 1;
marker_.scale.y = 0.1;
marker_.scale.z = 0.1;
設(shè)置marker的scale,很好理解,marker自身在x,y,z方向上的縮放系數(shù)。假設(shè)我們把marker的樣子設(shè)置為一個球,x方向scalr為1,其他為0.1,那么我們應(yīng)該看到的是一個橢球。
接下來是定義marker這個球的顏色
marker_.color.a = 1.0; // Don't forget to set the alpha!
marker_.color.r = 0.0;
marker_.color.g = 1.0;
marker_.color.b = 0.0;
第一個marker_.color.a要設(shè)置為1,marker才看得見(真是奇怪的性質(zhì)hhhh),后面三個分別是red,green,blue,顏色由0到1由淺變深,不必細(xì)說,如上設(shè)置的話marker就是綠色。
接下來是
marker_.action = visualization_msgs::Marker::ADD;
這行表示我們接收到marker的信息了之后是增加相應(yīng)的marker,你也可以定義刪除相應(yīng)的marker。
上面這些性質(zhì)我并不想每次更新marker的位置時都設(shè)置,所以放到一個函數(shù)里了。
接下來我們看callback函數(shù)void PoseCallback(const geometry_msgs::PoseStamped::ConstPtr& msg)
函數(shù)的第一行是count_++,其中count_是一個私有成員,可以看到count_在第三行賦值給了frame_id,這是什么意思呢?每一個rviz中出現(xiàn)的marker都有自己特定的id和namespace(即我們第二行給marker_ns賦值的內(nèi)容),不同的id或者namespace都代表著不同的marker。第三行代碼意味著每一次接收函數(shù)接收到一條新的消息,marker的id都會改變。那么每一次接收到新的消息都會有一個新的marker誕生。
可以想象,因?yàn)槊恳淮谓邮盏较⒍紩omarker賦值新的來自于posestamp消息的pose,如果這些pose的id都不同,我們就會看到那個橢球形的marker在不斷增加,如果id和namespace都相同,那么我們會只看到一個marker,那個marker仿佛在移動。
callback函數(shù)第四行給maker自身包含的timestamp賦值。在這個例子里不是很重要。
第五行就是關(guān)鍵了,我們接收到的pose賦值給marker。為什么能這么直接賦值呢?打開我們的第一個網(wǎng)頁鏈接我們可以看到marker是包含了類型為geometry_msgs/Pose的成員pose的。而posetamped類型的消息包含的是pose和header,所以我們可以直接進(jìn)行pose的賦值,這樣也就定義了marker的位置和方向。
接下來我們只要發(fā)布marker_這個消息,rviz就應(yīng)該能接收了。
等一下,貌似忘了什么。剛才說了我們需要把rviz中世界坐標(biāo)系的名字和marker的frame_id對應(yīng)起來。這樣我們才能從一個不動的上帝視角看marker。具體的做法是你的rviz中,找到Global Frame下面的Fixed Frame,這個就是世界坐標(biāo)系。目前它的名字叫map,我們點(diǎn)擊那個map,把它改成my_frame。現(xiàn)在就萬事俱備了。
把cpp文件寫到CMakeLists里
add_executable(pub_marker_msgs src/pub_marker_msgs.cpp)
target_link_libraries(pub_marker_msgs ${catkin_LIBRARIES})
編譯好后打開一個terminal,source之后輸入
rosrun learn_rviz_tf pub_marker_msgs
開始接收來自topicchatter的geometry_msgs::PoseStamped類型的消息。接收到后發(fā)布類型為visualization_msgs::Marker,topic為visualization_marker的消息。
之后再打開一個terminal,cd到rosbag的文件夾跑我們的rosbag,發(fā)布topic為chatter的消息
rosbag play record_poseStamped.bag
我們就應(yīng)該能在rviz中看到類似下面的東西。

如果你拖動鼠標(biāo)滾輪當(dāng)然可以看到更多的marker生成了(你的rosbag儲存有足夠多的信息的話)。說好的橢球呢?恩這就是一個橢球...不過拉伸太用力看起來像一根線了哈哈。
大家可以更改我們之前程序中的設(shè)置,比如marker的id固定為一個數(shù),看看會發(fā)生什么。
總結(jié)
我們連rviz的窗口的的作用都沒介紹就上例子然后就總結(jié)了,真是不按常理出牌,不過我們下一講還會用到rviz,例子多做幾個自然就知道怎么用了。那些窗口有什么用看官網(wǎng)的tutorial就是了。比如rviz窗口最上面的有選項interact, Move Camera, Select等,你如果左鍵選擇Select,再右鍵點(diǎn)擊Select,勾中Selection選項,在Display下面會新出來一個窗口,你如果此時去點(diǎn)擊一個marker,新的窗口會顯示marker的坐標(biāo),這其實(shí)我也是才知道哈哈。
還是那句話,rviz功能強(qiáng)大,我們在實(shí)際的例子只能講到一二,其他的在你們的實(shí)際工作中結(jié)合官網(wǎng)去慢慢體會。
下一講我們還會用到rviz,同時結(jié)合tf講解,tf是ros用來保持追蹤多個不同傳感器坐標(biāo)的主要工具,十分重要。