博客轉載自:https://www.ncnynl.com/archives/201702/1313.html
ROS與C++入門教程-tf-深入Time和TF
說明:
- 介紹使用waitForTransform函數去等待TF樹中的變換生效
TF和Time
- 在前面的教程中,我們了解了tf如何跟蹤坐標系樹。
- 此樹隨時間變化,tf存儲每個變換的時間快照(默認情況下最多為10秒)。
- 直到現在,我們使用lookupTransform()函數來獲取該tf樹中最新的可用變換,而不知道什么時候記錄變換。
- 本教程將教您如何在特定時間獲取轉換。
- 在上個教程的文件上修改:
$ roscd learning_tf $ vim src/turtle_tf_listener.cpp
- 找到代碼:
try{
listener.lookupTransform("/turtle2", "/carrot1",
ros::Time(0), transform);
- 修改為:
try{
listener.lookupTransform("/turtle2", "/turtle1",
ros::Time(0), transform);
- 更改/turtle2跟隨/turtle1,不是/carrot1,我們指定了時間為0,即獲得最新有效的變換。
- 改變獲取當前時間的變換,即改為now(), 修改代碼:
try{
listener.lookupTransform("/turtle2", "/turtle1",
ros::Time::now(), transform);
- 編譯運行:
$ catkin_make $ roslaunch learning_tf start_demo.launch
- lookupTransform()函數提示失敗:
[ERROR] [1287871653.885277559]: You requested a transform that is 0.018 miliseconds in the past, but the most recent transform in the tf buffer is 7.681 miliseconds old. When trying to transform between /turtle1 and /turtle2.
- 這是為什么? 每個監聽器有一個緩沖區,它存儲來自不同tf廣播者的所有坐標變換。 當廣播者發出變換時,變換進入緩沖區之前需要一些時間(通常是幾個毫秒)。 因此,當您在時間“now”請求坐標系變換時,您應該等待幾毫秒以獲得該信息。
等待變換
- tf提供了一個很好的工具,它將等待,直到變換可用。
- 修改代碼為:
try{
ros::Time now = ros::Time::now();
listener.waitForTransform("/turtle2", "/turtle1",
now, ros::Duration(3.0));
listener.lookupTransform("/turtle2", "/turtle1",
now, transform);
-
waitForTransform() 四個參數:
- 1.需要等待變換從坐標系turtle2
- 2.到坐標系turtle1
- 3.在now時間
- 4.超時時間,不要等待超過此最大持續時間
-
注意:使用ros::Time::now()是為了這個例子。通常這將是希望被轉換的數據的時間戳。
-
所以waitForTransform()實際上會阻塞直到兩個海龜之間的變換可用(這通常需要幾毫秒)
-
或者如果變換不可用,直到達到超時。
-
編譯運行:
$ catkin_make $ roslaunch learning_tf start_demo.launch
- 但等待,您可能仍會看到錯誤一次(錯誤msg可能會有所不同):
[ERROR] [1287872014.408401177]: You requested a transform that is 3.009 seconds in the past, but the tf buffer only has a history of 2.688 seconds. When trying to transform between /turtle1 and /turtle2.
這是因為turtle2需要非零時間來生成並開始發布tf幀。 因此,第一次請求現在時間的/turtle2坐標系可能不存在,當請求轉換時,轉換可能不存在,並且第一次失敗。 在第一次變換之后,所有的變換都存在,
並且烏龜的行為如預期的那樣。
檢查結果
- 現在,你應該能夠使用箭頭鍵(確保你的終端窗口是活躍的,而不是你的模擬器窗口),你會看到第二只烏龜跟隨第一只烏龜!
- 所以,你注意到烏龜的行為沒有明顯的區別。 這是因為實際的時間差只有幾個毫秒。 但是為什么我們從Time(0)到now()進行這種改變? 只是教你關於tf緩沖區和與它相關的時間延遲。對於真實的tf用例,使用Time(0)通常是完全正常的。
ROS與C++入門教程-tf-Time travel(時間穿梭
說明:
- 介紹tf的高級時間穿梭功能
Time travel
- 利用上一個教程的文件。
- 打開src/turtle_tf_listener.cpp,找到25-30,如下:
try{
ros::Time now = ros::Time::now();
listener.waitForTransform("/turtle2", "/turtle1",
now, ros::Duration(1.0));
listener.lookupTransform("/turtle2", "/turtle1",
now, transform);
- 現在,不是讓turtle2去到turtle1當前時間的地方,而讓turtle2去turtle1是5秒前的地方:
try{
ros::Time past = ros::Time::now() - ros::Duration(5.0);
listener.waitForTransform("/turtle2", "/turtle1",
past, ros::Duration(1.0));
listener.lookupTransform("/turtle2", "/turtle1",
past, transform);
- 編譯運行:
$ make or catkin_make $ roslaunch learning_tf start_demo.launch
-
你會期望看到什么? 在第一個5秒鍾,第二只烏龜不知道去哪里,因為我們還沒有第一只烏龜5秒的歷史。
-
但是這5秒后呢?效果圖:

-
類似截圖,你的海龜是不可控制? 那么發生了什么?
-
我們問TF:相對於/turtle2 5秒前,/turtle1 5秒前的姿勢是什么?
-
這意味着我們控制/turtle2基於5秒前的位置以及/turtle1在5秒前的位置。
-
那應該問:相對於/turtle2目前的位置,/turtle1 5秒前的姿勢是什么?
高級API
- 回答上面問題,依賴高級API,示例代碼:
try{
ros::Time now = ros::Time::now();
ros::Time past = now - ros::Duration(5.0);
listener.waitForTransform("/turtle2", now,
"/turtle1", past,
"/world", ros::Duration(1.0));
listener.lookupTransform("/turtle2", now,
"/turtle1", past,
"/world", transform);
-
這個lookupTransform()API,有六個參數:
-
- 變換從坐標系turtle2
-
- 在now時間
-
- 到turtle1坐標系
-
- 在past時間
-
- 指定不隨時間改變的坐標系,這里是world
-
- 變換結果保存的變量
-
-
waitForTransform()跟lookupTransform()一樣有6個相應參數。
-
效果圖示:

-
這個圖顯示了tf在后台做什么。
-
在past時間,它計算從turtle1到world坐標系的變換。
-
在world坐標系,tf時間從past到now。
-
在now時間,tf計算從world到turtle2坐標系的變換。
檢查結果:
- 編譯運行:
$ catkin_make $ roslaunch learning_tf start_demo.launch
- 效果:turtle2是指向turtle1 5秒前的地方!
