ROS中的坐标与坐标系转换
ROS中的TF
官網(wǎng)建議新工作直接使用tf2,因為它有一個更清潔的界面,和更好的使用體驗。(自ROS Hydro以來,tf第一代已被“棄用”,轉(zhuǎn)而支持tf2)
TF介紹
TF(TransForm),就是坐標(biāo)轉(zhuǎn)換,包括了位置和姿態(tài)兩個方面的變換。注意區(qū)分坐標(biāo)轉(zhuǎn)換和坐標(biāo)系轉(zhuǎn)換。
坐標(biāo)轉(zhuǎn)換是一個坐標(biāo)在不同坐標(biāo)系下的表示,而坐標(biāo)系轉(zhuǎn)換不同坐標(biāo)系的相對位姿關(guān)系。
ROS中機器人模型包含大量的部件,每一個部件統(tǒng)稱之為link(比如手部、頭部、某個關(guān)節(jié)、某個連桿),每一個link上面對應(yīng)著一個frame(坐標(biāo)系), 用frame表示該部件的坐標(biāo)系,frame和link是綁定在一起的。
TF是一個通俗的名稱,實際上它有很多含義:
可以被當(dāng)做是一種標(biāo)準(zhǔn)規(guī)范,這套標(biāo)準(zhǔn)定義了坐標(biāo)轉(zhuǎn)換的數(shù)據(jù)格式和數(shù)據(jù)結(jié)構(gòu).tf本質(zhì)是樹狀的數(shù)據(jù)結(jié)構(gòu),即"tf tree"。
tf也可以看成是一個話題/tf,話題中的消息保存的就是tf tree的數(shù)據(jù)結(jié)構(gòu)格式。維護(hù)了整個機器人的甚至是地圖的坐標(biāo)轉(zhuǎn)換關(guān)系。維持并更新機器人整個坐標(biāo)系的話題是/tf,/tf話題表示的內(nèi)容是整個機器人的tf樹,而非僅僅是某兩個坐標(biāo)系的轉(zhuǎn)換關(guān)系,這樣的話,/tf話題是需要很多的節(jié)點來維護(hù)的,每一個節(jié)點維護(hù)兩個frame之間的關(guān)系。
tf還可以看成是一個package,它當(dāng)中包含了很多的工具.比如可視化,查看關(guān)節(jié)間的tf,debug tf等等.
tf含有一部分的API接口,用來節(jié)點程序中的編程。TF對發(fā)布器與訂閱器進(jìn)行了封裝,使開發(fā)者通過TF的接口更加簡單地建立對TF樹中某些坐標(biāo)系轉(zhuǎn)換關(guān)系的維護(hù)與訂閱。
tf是一個樹狀結(jié)構(gòu),維護(hù)坐標(biāo)系之間的關(guān)系,靠話題通信機制來持續(xù)地發(fā)布不同link之間的坐標(biāo)關(guān)系。作為樹狀結(jié)構(gòu),要保證父子坐標(biāo)系都有某個節(jié)點在持續(xù)地發(fā)布他們之間的位姿關(guān)系,才能使樹狀結(jié)構(gòu)保持完整。只有父子坐標(biāo)系的位姿關(guān)系能被正確的發(fā)布,才能保證任意兩個frame之間的連通。
如果出現(xiàn)某一環(huán)節(jié)的斷裂,就會引發(fā)error系統(tǒng)報錯.所以完整的tf tree不能有任何斷層的地方,這樣我們才能查清楚任意兩個frame之間的關(guān)系。
每兩個相鄰frame之間靠節(jié)點發(fā)布它們之間的位姿關(guān)系,這種節(jié)點稱為broadcaster。broadcaster就是一個發(fā)布器publisher,如果兩個frame之間發(fā)生了相對運動,broadcaster就會發(fā)布相關(guān)消息。
TF的原理
TF樹的結(jié)構(gòu)
TF庫的目的是實現(xiàn)系統(tǒng)中任一個點在所有坐標(biāo)系之間的坐標(biāo)變換。也就是說,只要給定一個坐標(biāo)系下的一個點的坐標(biāo),就能獲得這個點在其他任意坐標(biāo)系的坐標(biāo)。
為了達(dá)到上述目的,就需要提供當(dāng)前ROS系統(tǒng)的中任意兩個坐標(biāo)系的位姿變換關(guān)系。
那么TF是用什么方式來描述與記錄任意兩個坐標(biāo)系的位姿變換關(guān)系的呢?
這里存在一個問題。假設(shè)有n個坐標(biāo)系,那么他們之間的組合關(guān)系有c(n,2)個。如果這樣窮舉個數(shù)會非常多,所以不會采用這個方法。
為了更合理、更高效地表示任意坐標(biāo)系的變換關(guān)系。TF使用多層多叉樹的形式來描述這個這個ROS系統(tǒng)的坐標(biāo)系,樹中的每一個節(jié)點都是一個坐標(biāo)系。TF樹的特點是每個節(jié)點只要一個父節(jié)點,即采用每個坐標(biāo)系都有一個父坐標(biāo)系,可以有多個子坐標(biāo)系的原則。
TF坐標(biāo)系表示規(guī)范
每個坐標(biāo)系都有一個父坐標(biāo)系,可以有多個子坐標(biāo)系。TF樹就是以父子坐標(biāo)系的形式來組織的,最上面是父坐標(biāo)系,往下是子坐標(biāo)系。
在TF樹中具有父子關(guān)系的坐標(biāo)系是相鄰的,用帶箭頭的線連接起來。在TF樹中用箭頭表示這種父子關(guān)系。
上圖所表示的TF樹中base_link坐標(biāo)系是base_footprint的子坐標(biāo)系,base_cover_link坐標(biāo)系也是base_footprint的子坐標(biāo)系。
描述規(guī)范:
source、target frame是在進(jìn)行坐標(biāo)變換時的概念,source是坐標(biāo)變換的源坐標(biāo)系,target是目標(biāo)坐標(biāo)系。這個時候,這個變換代表的是坐標(biāo)變換。
parent、child frame是在描述坐標(biāo)系變換時的概念,parent是原坐標(biāo)系,child是變換后的坐標(biāo)系,這個時候這個變換描述的是坐標(biāo)系變換,也是child坐標(biāo)系在parent坐標(biāo)系下的描述。
a frame到b frame的坐標(biāo)系變換(frame transform),也表示了b frame在a frame的描述,也代表了把一個點在b frame里坐標(biāo)變換成在a frame里坐標(biāo)的坐標(biāo)變換。
從parent到child的坐標(biāo)系變換(frame transform)等同于把一個點從child坐標(biāo)系向parent坐標(biāo)系的坐標(biāo)變換,等于child坐標(biāo)系在parent frame坐標(biāo)系的姿態(tài)描述。
TF樹的通信方式與TF樹的具體表示
TF樹的建立和維護(hù)是基于Topic通信機制的。
根據(jù)TF樹的原理,它是靠建立與維護(hù)每個父子坐標(biāo)系的變換關(guān)系來維護(hù)整個系統(tǒng)的所有坐標(biāo)系的變換關(guān)系的。每個parent 坐標(biāo)系到child坐標(biāo)系變換關(guān)系是靠被稱為broadcastor的發(fā)布器節(jié)點來持續(xù)發(fā)布的。
雖然是靠Topic通信機制發(fā)布的parent 坐標(biāo)系到child坐標(biāo)系的變換,但并不是讓每一對父子坐標(biāo)系都發(fā)布一個話題,實際上發(fā)布的唯一個話題是/topic,該話題集合了所有發(fā)布的父子坐標(biāo)系的變換關(guān)系。
也就是說TF機制并不是讓每一對父子坐標(biāo)系都發(fā)布一個話題,而是將所有的父子坐標(biāo)系都集合到到一個話題上,該話題的消息中傳遞的數(shù)據(jù)是所有父子坐標(biāo)系的變換關(guān)系,是父子坐標(biāo)系變換關(guān)系的一個大數(shù)組。
使用tf的tflisener就可以監(jiān)聽從任意兩個坐標(biāo)系的變換。前提是TF的樹上能把這兩個聯(lián)通。
TF樹的建立
在開始建立TF樹的時候需要指定第一個父坐標(biāo)系(parent frame)作為最初的坐標(biāo)系。比如機器人系統(tǒng)中的map坐標(biāo)系。
在第一次發(fā)布一個從已有的parent frame到新的child frame的坐標(biāo)系變換時,這棵樹就會添加一個樹枝,之后就是維護(hù)。
TF樹的建立和維護(hù)靠的是tf提供的tfbroadcastor類的sendtransform接口。
transformBroadcaster()類就是一個publisher,而sendTransform的作用是來封裝publish的函數(shù)。
TF樹的維護(hù)
在運行過程中要不斷更新已有的parent frame到已有的child frame的坐標(biāo)系變換,從而保證最新的位姿轉(zhuǎn)換關(guān)系。
作為樹狀結(jié)構(gòu),要保證父子frame都有某個節(jié)點在持續(xù)地發(fā)布這兩個frame之間的位姿關(guān)系,才能使樹狀結(jié)構(gòu)保持完整。只有每一個父子的frame的位姿關(guān)系能被正確的發(fā)布,才能保證任意兩個frame之間的連通。
TF樹的使用
一旦正常的建立一個TF樹,保證每個父子坐標(biāo)系都能得到正常的維護(hù),那么就可以利用TF提供的訂閱器,訂閱任意兩個坐標(biāo)系的轉(zhuǎn)換關(guān)系。
如何根據(jù)TF樹得到任意坐標(biāo)系的轉(zhuǎn)換關(guān)系?
如果想要獲得任意兩個坐標(biāo)系的轉(zhuǎn)換關(guān)系,其實訂閱器是收取的/tf話題上的消息,該消息集合了所有發(fā)布的父子坐標(biāo)系的變換關(guān)系。訂閱器接收的其實是當(dāng)前時刻的整個TF樹,然后搜索這棵樹,根據(jù)不同的父子坐標(biāo)系關(guān)系找到一條變換的路徑。這條變換路徑就能通過父子關(guān)系通路連接起所求的這兩個坐標(biāo)系,從而通過不斷將該通路上的變換矩陣相乘得到最終的所求的這兩個坐標(biāo)系的變換關(guān)系。
TF對發(fā)布器與訂閱器進(jìn)行了封裝,使開發(fā)者通過TF的接口更加簡單地建立對TF樹中某些坐標(biāo)系轉(zhuǎn)換關(guān)系的維護(hù)與訂閱。用tf的tflisener監(jiān)聽某一個指定的從一個a frame到b frame的變換即可。
總結(jié)(引用網(wǎng)絡(luò)資源):
基本原理是,tfbroadcastor的類里有個publisher,tflisener的類里有個subscriber,一個發(fā)布叫/tf的topic,一個訂閱這個topic,傳送的消息message里包含了每一對parent frameid和child frameid的信息。這個機制意味著,所有的tb會發(fā)布某一特定的parent到child的變換,而所有tl會收到所有的這些變換,然后tl利用一個tfbuffercore的數(shù)據(jù)結(jié)構(gòu)維護(hù)一個完整的樹結(jié)構(gòu)及其狀態(tài)。基于此,tl在使用這棵樹時,會用lookuptransform或waitfortransform來獲得任意坐標(biāo)系之間的變換。
這樣即使只要是一個tflisener(即只監(jiān)聽兩個坐標(biāo)系的變換關(guān)系),就要跟所有tfbroadcastor建立連接,就要收取/tf上的整個TF樹,還要負(fù)責(zé)搜索這棵樹,找到一條變換的路徑,然后通過變換矩陣相乘得到兩個坐標(biāo)系最終的變換關(guān)系。
TF的特點
優(yōu)點:
各種數(shù)值計算的細(xì)節(jié),你不用考慮,tf庫可以幫你
接口很簡潔,會廣播和監(jiān)聽就行;
問題找的很準(zhǔn),那就是需要維護(hù)坐標(biāo)系之間的關(guān)系,尤其是父子坐標(biāo)系的關(guān)系
提供了很多工具程序
考慮了與時間相關(guān)的變換
支持tf-prefix,可以在多機器人上用。通過讓不同機器人使用不同的prefix,來區(qū)分機器人。如果只有一個機器人一般是使用/
缺點:
樹的結(jié)構(gòu)很簡單,但有時候很笨拙。對于同級的坐標(biāo)系,就需要從下到上找共同先輩,然后從這個先輩再往下找,進(jìn)而確定二者的關(guān)系。
每個訂閱器要想獲得某兩個坐標(biāo)系的關(guān)系都要搜索同一顆樹,這樣的開銷太大,主要是網(wǎng)絡(luò)傳輸?shù)呢?fù)荷比較大。
很難滿足實時性的要求,這一點比較顯然。這也是為什么TF會將每個變換存10秒鐘的數(shù)據(jù)
雖然整體比較容易上手但是很多細(xì)節(jié)不易理解。比如,now()和time(0);比如,技術(shù)文檔里的一些術(shù)語名詞;比如,采用了機器人里的習(xí)慣,與飛行器,慣導(dǎo),車輛里的習(xí)慣區(qū)別較大,使用時不能想當(dāng)然。
TF消息:兩個frame之間的消息
每個父子坐標(biāo)系之間都會有broadcaster節(jié)點來發(fā)布消息維系坐標(biāo)之間的轉(zhuǎn)換關(guān)系。TransformStampde.msg就是/tf話題上消息。該消息格式用來表示兩個frame之間一小段tf變換的相對坐標(biāo)關(guān)系的。
說明:
ROS中實際上是靠TF tree來表示整個系統(tǒng)的坐標(biāo)系關(guān)系的,而非簡單地靠多個兩兩父子坐標(biāo)系的轉(zhuǎn)換關(guān)系來描述的。這里的TransformStampde.msg消息的TF tree消息類型的片段即其中的一對父子坐標(biāo)系位姿的描述方式,TF tree消息類型基于TransformStampde.msg消息,因此先介紹TransformStampde.msg。TransformStampde.msg本質(zhì)上描述的是TF tree中一小段tf變換。
具體消息類型如下:
geometry_msgs/TransformStamped(可見該消息類型是屬于geometry_msgs程序包的,而非tf包)
std_mags/Header header
uint32 seq
time stamp
string frame_id
string child_frame_id
geometry_msgs/Transform transform
geometry_msgs/Vector3 translation
float64 x
float64 y
float64 z
geometry_msgs/Quaternion rotation
float64 x
float64 y
flaot64 z
float64 w
消息解釋:
該消息表示的的是當(dāng)前坐標(biāo)系frame_id和它的子坐標(biāo)系child_frame_id之間的轉(zhuǎn)換關(guān)系。具體的轉(zhuǎn)換位姿是由geometry_msgs/Transform消息類型來定義的,該消息類型用三維向量表示平移,用四元組表示旋轉(zhuǎn)。
TF消息:TF樹的消息類型
/tf話題表示的內(nèi)容是整個機器人的tf樹,而非僅僅是某兩個坐標(biāo)系的轉(zhuǎn)換關(guān)系,這樣的話,/tf話題是需要很多的節(jié)點來維護(hù)的,每一個節(jié)點維護(hù)兩個父子frame之間的關(guān)系。即一個/tf話題上面,可能會有很多個node向上面發(fā)送消息。
這樣就相當(dāng)于TF tree是由很多的frame之間TF拼接而成。剛才說的TransformStampde.msg消息類型表示的是兩個frame之間TF關(guān)系,接下來要介紹真正在/tf話題上進(jìn)行傳輸?shù)腡F tree的消息類型。
在tf2中的TF樹對應(yīng)的消息類型是tf2_msgs/TFMessage.msg。可見該消息位于tf2_msgs程序包內(nèi)。
tf2_msgs/TFMessage消息的具體格式:
geometry_msgs/TransformStamped[] transforms
std_msgs/Header header
uint32 seq
time stamp
string frame_id
string child_frame_id
geometry_msgs/Transform transform
geometry_msgs/Vector3 translation
float64 x
float64 y
float64 z
geometry_msgs/Quaternion rotation
float64 x
float64 y
flaot64 z
float64 w
可以看出TF樹的消息類型實際上就是一個TransformStamped類型定義的可變長度數(shù)組。也就是說本質(zhì)就是由很多個兩個frame之間的TF消息TransformStamped形成描述整個機器人的TF樹的消息類型tf2_msgs/TFMessage.msg。
TF在roscpp與rospy中的接口
無論是roscpp中還是rospy中都有TF庫,TF提供了很多有用的接口。這里只大體描述,具體使用的時候再參考具體的資料。
提供的結(jié)構(gòu)種類:
數(shù)據(jù)類型的定義(類):向量、點、四元數(shù)、3*3旋轉(zhuǎn)矩陣、位姿等
數(shù)據(jù)轉(zhuǎn)換:給出了旋轉(zhuǎn)矩陣、四元數(shù)、歐拉角、旋轉(zhuǎn)軸之間的轉(zhuǎn)換函數(shù)
關(guān)于點、向量、角度、四元數(shù)等的運算的函數(shù)
TF類,封裝好了發(fā)布器與訂閱器接口。可以將坐標(biāo)系轉(zhuǎn)換關(guān)系發(fā)布到/tf話題上的一段transform上;也可以訂閱/tf話題,并且得到到從源坐標(biāo)系到目標(biāo)坐標(biāo)系這兩個坐標(biāo)系之間的轉(zhuǎn)換關(guān)系。
transformBroadcaster()類就是一個publisher,而sendTransform的作用是來封裝publish的函數(shù)。在實際的使用中,我們需要在某個Node中構(gòu)建tf::TransformBroadcaster類,然后調(diào)用sendTransform(),將transform發(fā)布到/tf的一段transform上。
TransformListener類就是從/tf上接收的類。
TF程序包相關(guān)的命令行使用
用命令行顯示當(dāng)前所有frame的方法:
rosrun tf tf_monitor #顯示當(dāng)前坐標(biāo)變換樹的信息,主要是名稱和實時的時間延時
rostopic echo /tf #以TransformStamped消息類型的數(shù)組顯示所有父子frame的位姿轉(zhuǎn)換關(guān)系
以上主要是數(shù)據(jù)顯示
根據(jù)當(dāng)前的tf樹創(chuàng)建一個pdf圖:
$ rosrun tf view_frames
這個工具首先訂閱/tf,訂閱5秒鐘,根據(jù)這段時間接受到的tf信息,繪制成一張tf tree,然后創(chuàng)建成一個pdf圖。
將會以圖形的形式顯示出TF樹中所有的frame和兩個frame 的父子關(guān)系及其Broadcaster、Average rate等
查看當(dāng)前的tf樹:
$ rosrun rqt_tf_tree rqt_tf_tree
該命令同樣是查詢tf tree的,但是與第一個命令的區(qū)別是該命令是動態(tài)的查詢當(dāng)前的tf tree,當(dāng)前的任何變化都能當(dāng)即看到,例如何時斷開何時連接,捕捉到這些然后通過rqt插件顯示出來。
查看兩個frame之間的變換關(guān)系:
$ rosrun tf tf_echo[source_frame][target_frame]
將會持續(xù)的顯示源坐標(biāo)系和目標(biāo)坐標(biāo)系的位姿變換關(guān)系。
該指令可以查詢?nèi)我鈨蓚€frame的轉(zhuǎn)換關(guān)系。
參考資料
https://sychaichangkun.gitbooks.io/ros-tutorial-icourse163/content/chapter8/8.3.html(基礎(chǔ)講義)
http://blog.exbot.net/archives/1686(TF的理解)
總結(jié)
以上是生活随笔為你收集整理的ROS中的坐标与坐标系转换的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C语言简讲:如何输出数字
- 下一篇: keyshot手机渲染教程_提高Keys