博客轉載自:https://www.ncnynl.com/archives/201702/1311.html
ROS與C++入門教程-tf-編寫tf listener(監聽)
說明:
- 介紹如何使用tf訪問坐標系轉換
創建tf的監聽
- 新建文件turtle_tf_listener.cpp 參考源碼:
$ roscd learning_tf $ touch src/turtle_tf_listener.cpp $ vim src/turtle_tf_listener.cpp
- 代碼如下:
#include <ros/ros.h> #include <tf/transform_listener.h> #include <geometry_msgs/Twist.h> #include <turtlesim/Spawn.h> int main(int argc, char** argv){ ros::init(argc, argv, "my_tf_listener"); ros::NodeHandle node; ros::service::waitForService("spawn"); ros::ServiceClient add_turtle = node.serviceClient<turtlesim::Spawn>("spawn"); turtlesim::Spawn srv; add_turtle.call(srv); ros::Publisher turtle_vel = node.advertise<geometry_msgs::Twist>("turtle2/cmd_vel", 10); tf::TransformListener listener; ros::Rate rate(10.0); while (node.ok()){ tf::StampedTransform transform; try{ listener.lookupTransform("/turtle2", "/turtle1", ros::Time(0), transform); } catch (tf::TransformException &ex) { ROS_ERROR("%s",ex.what()); ros::Duration(1.0).sleep(); continue; } geometry_msgs::Twist vel_msg; vel_msg.angular.z = 4.0 * atan2(transform.getOrigin().y(), transform.getOrigin().x()); vel_msg.linear.x = 0.5 * sqrt(pow(transform.getOrigin().x(), 2) + pow(transform.getOrigin().y(), 2)); turtle_vel.publish(vel_msg); rate.sleep(); } return 0; };
- 如果在運行時遇到錯誤"Lookup would require extrapolation into the past",您可以嘗試此替代代碼來調用偵聽器:
try { listener.waitForTransform(destination_frame, original_frame, ros::Time(0), ros::Duration(10.0) ); listener.lookupTransform(destination_frame, original_frame, ros::Time(0), transform); } catch (tf::TransformException ex) { ROS_ERROR("%s",ex.what()); }
代碼解釋:
- 代碼:
#include <tf/transform_listener.h>
- 作用:
- tf包提供了TransformListener的實現,以幫助使接收變換的任務更容易。
- 要使用TransformListener,我們需要包括tf/transform_listener.h頭文件。
-
代碼:tf::TransformListener listener;
-
作用:
- 這里,我們創建一個TransformListener對象。
- 一旦監聽器被創建,它開始接收tf轉換,並緩沖它們長達10秒。
- TransformListener對象應該被限定為持久化,否則它的緩存將無法填充,並且幾乎每個查詢都將失敗。
- 一個常見的方法是使TransformListener對象成為一個類的成員變量。
-
代碼:
try{ listener.lookupTransform("/turtle2", "/turtle1", ros::Time(0), transform); }
-
作用:這里,真正的工作完成了,我們查詢監聽器進行特定的轉換。
-
讓我們來看看四個參數:
-
1.我們想從/turtle2坐標系開始
-
2.變換到/turtle1坐標系
-
3.變換的時間,提供ros::Time(0)即會給出最近的可用的變換。
-
4.結果存放的變換對象。
-
這個代碼放在try-catch結構,可以獲取拋出的異常
-
代碼:
geometry_msgs::Twist vel_msg; vel_msg.angular.z = 4.0 * atan2(transform.getOrigin().y(), transform.getOrigin().x()); vel_msg.linear.x = 0.5 * sqrt(pow(transform.getOrigin().x(), 2) +
- 作用:
- 這里,變換用於計算龜的新的線性和角速度,基於它與龜的距離和角度。
- 新的速度發布在話題"turtle2/cmd_vel"中,turtlesim將使用它來更新turtle2的運動。
運行監聽
- 打開CMakeLists.txt文件,並在底部添加以下行:
add_executable(turtle_tf_listener src/turtle_tf_listener.cpp) target_link_libraries(turtle_tf_listener ${catkin_LIBRARIES})
- 編譯:
$ cd ~/catkin_ws $ catkin_make
- 如果一切順利,你應該在devel/lib/learning_tf文件夾中有一個名為turtle_tf_listener的二進制文件。
- 之前已經創建start_demo.launch,在 塊,合並代碼:
<launch> ... <node pkg="learning_tf" type="turtle_tf_listener" name="listener" /> </launch>
- 啟動:
$ roslaunch learning_tf start_demo.launch
- 你應該看到turtlesim有兩只海龜。
檢查結果:
- 要查看是否有效,只需使用箭頭鍵(確保您的終端窗口處於活動狀態,而不是模擬器窗口)繞過第一只烏龜,您會在第一只烏龜之后看到第二只烏龜!
- 當turtlesim啟動時,你可能會看到:
[ERROR] 1253915565.300572000: Frame id /turtle2 does not exist! When trying to transform between /turtle1 and /turtle2. [ERROR] 1253915565.401172000: Frame id /turtle2 does not exist! When trying to transform between /turtle1 and /turtle2.
- 這是因為我們的監聽器試圖在接收關於龜2的消息之前計算變換,因為它需要一點時間在turtlesim中生成並開始廣播一個tf坐標系。