ROS筆記-tf2的介紹
首先,安裝一個(gè)tf2的演示demo
$ sudo apt-get install ros-${ROS_DISTRO}-turtle-tf2 ros-${ROS_DISTRO}-tf2-tools ros-${ROS_DISTRO}-tf
通過(guò)如下指令運(yùn)行演示demo
roslaunch turtle_tf2 turtle_tf2_demo.launch
正常情況會(huì)報(bào)錯(cuò),提示“/usr/bin/env: 'python' : No such file or directory”
因?yàn)橄到y(tǒng)環(huán)境是20.04LTS+noetic,按照步驟裝ros的時(shí)候裝的是python3
解決方法:
運(yùn)行命令 whereis python3
sudo ln -s /usr/bin/python3 /usr/bin/python
再運(yùn)行roslaunch就能正常運(yùn)行demo,這個(gè)demo就是一只小龜追自己操作的另外一只小龜,主要關(guān)注其具體的實(shí)現(xiàn)原理
通過(guò)rospack find turtle_tf2 找到turtle_tf2包的位置,進(jìn)去找到turtle_tf2_demo.launch

?根據(jù)launch文件可以看到
啟動(dòng)了turtlesim包里的兩個(gè)節(jié)點(diǎn),sim和teleop,節(jié)點(diǎn)類(lèi)型分別為turtlesim_node、turtle_teleop_key,一般來(lái)說(shuō),node語(yǔ)句中的type表示節(jié)點(diǎn)類(lèi)型或者同名的可執(zhí)行文件,name表示節(jié)點(diǎn)名
param 如果在node語(yǔ)句中則表示為/node_name/param_name,如該launch文件中雖然兩個(gè)param的name都是turtle,但是因?yàn)樵诓煌膎ode中,因此實(shí)際的參數(shù)名應(yīng)該分別為:/turtle1_tf2_broadcaster/turtle和/turtle2_tf2_broadcaster/turtle,所以并不影響,他的value表示為參數(shù)源
其他的類(lèi)似啟動(dòng)節(jié)點(diǎn)或者定義參數(shù)都算好理解
介紹兩個(gè)tf2的工具,view_frames和tf_echo
view_frames 用法:$ rosrun tf2_tools view_frames.py
tf_echo 用法:rosrun tf tf_echo [reference_frame] [target_frame]
具體解釋在http://wiki.ros.org/tf2/Tutorials/Introduction%20to%20tf2上,小工具,先了解

用c++實(shí)現(xiàn)一個(gè)tf2 broadcaster


先看頭文件?#include <tf2_ros/transform_broadcaster.h>?
tf2包中有transformbroadcaster這樣一個(gè)廣播器,可以更方便的廣播位姿變換,要使用這個(gè)廣播器頭文件里面就需要包含/tf2_ros/transform_broadcaster.h
static tf2_ros::TransformBroadcaster br;
創(chuàng)建了一個(gè)TransformBroadcaster對(duì)象br 稍后會(huì)用它來(lái)發(fā)送位姿變換
geometry_msgs::TransformStamped transformStamped;
transformStamped.header.stamp = ros::Time::now();
transformStamped.header.frame_id = "world";
transformStamped.child_frame_id = turtle_name;
這里的教程原文解釋是 Here we create a Transform object and give it the appropriate metadata.
We need to give the transform being published a timestamp(時(shí)間戳),we'll just stamp it with the current time, ros::Time::now()
Then, we need to set the name of the parent frame of the link we're creating, in this case "world"
Finally, we need to set the name of the child node of the link we're creating, in this case this is the name of the turtle itself
接著是
transformStamped.transform.translation.x = msg->x;
transformStamped.transform.translation.y?= msg->y;
transformStamped.transform.translation.z?=?0.0;
tf2::Quaternion q;
q.setRPY(0, 0, msg->theta);
transformStamped.transform.rotation.x = q.x();
transformStamped.transform.rotation.y?= q.y();
transformStamped.transform.rotation.z?= q.z();
transformStamped.transform.rotation.w?= q.w();
這里表示將3D turtle pose的信息復(fù)制到3D transform中去?
最后通過(guò)
br.sendTransform(transformStamped);
用br將變換發(fā)送出去
Note: sendTransform and StampedTransform have opposite ordering of parent and child.
Note2: you can also publish static transforms on the same pattern by instantiating a StaticTransformBroadcaster instead of a TransfomBroadcaster.(詳細(xì)展開(kāi)在:http://wiki.ros.org/tf2/Tutorials/Writing%20a%20tf2%20static%20broadcaster%20%28C%2B%2B%29)
main函數(shù)里的內(nèi)容教程中沒(méi)有分析,直接到下一步運(yùn)行這個(gè)broadcaster,當(dāng)寫(xiě)好一個(gè)節(jié)點(diǎn).cpp文件之后,首先需要在包里的CMakeLists.txt文件中為這個(gè).cpp文件添加如下項(xiàng)
add_executable(turtle_tf2_broadcaster src/turtle_tf2_broadcaster.cpp)
target_link_libraries(turtle_tf2_broadcaster ${catkin_LIBRARIES})
然后返回catkin_ws文件夾進(jìn)行catkin_make
當(dāng)文件編譯好了之后下一步就是創(chuàng)建對(duì)應(yīng)的launch文件

然后同樣在CMakeList.txt文件中加入
## Mark other files for installation (e.g. launch and bag files, etc.)
install(FILES start_demo.launch # myfile2?
DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
完成上述步驟后就可以在終端運(yùn)行
roslaunch learning_tf2 start_demo.launch
用c++實(shí)現(xiàn)一個(gè)tf2 listener
首先是創(chuàng)建源文件 src/turtle_tf2_listener.cpp


代碼解析
頭文件?#include <tf2_ros/transform_listener.h>
tf2包提供了TransformListener來(lái)使得位姿變換的接收更加方便,因此要使用TransformListener,我們需要包含對(duì)應(yīng)的頭文件
tf2_ros::Buffer? tfBuffer;
tf2_ros::TransformListener? tfListener(tfBuffer);
這里創(chuàng)建了一個(gè)TransformListener對(duì)象,當(dāng)Listener創(chuàng)建好后,它就會(huì)通過(guò)網(wǎng)絡(luò)來(lái)接收tf2的位姿變換信息,and buffers them for up to 10 seconds.(并將其緩沖長(zhǎng)達(dá)10s:我的理解是將接收到的信息最多能緩存10s)
The TransformListener object should be scoped to persist otherwise its cache will be unable to fill and almost every query will fail. A common method is to make the TransformListener object a member variable of a class.(TransformListener對(duì)象的范圍應(yīng)該是持久化的,否則它的緩存將無(wú)法填充,幾乎所有的查詢(xún)都會(huì)失敗,一個(gè)常見(jiàn)的方法是讓該對(duì)象成為一個(gè)類(lèi)別的成員變量。ps:機(jī)翻,并沒(méi)有看太懂)
try{ ??
? ? transformStamped = tfBuffer.lookupTransform("turtle2", "turtle1",?ros::Time(0)); }?
catch (tf2::TransformException &ex) { ??
? ? ROS_WARN("%s",ex.what()); ??
? ? ros::Duration(1.0).sleep(); ??
? ? continue;?
}
Here, the real work is done, we query the listener for a specific transformation. Let's take a look at the four arguments (關(guān)于lookup Transform()的解釋?zhuān)?/p>
We want the tansform to this frame (target frame)...
...from this frame (source frame)
?The time at which we want to transform. Providing ros::Time(0) will just get us the latest available transform
Duration before timeout. (optinal, default = ros::Duration(0,0))
接下來(lái)是運(yùn)行該代碼,與之前的broadcaster.cpp類(lèi)似,需要先對(duì)其進(jìn)行編譯
在CMakeList.txt中加入
add_executable(turtle_tf2_listener src/turtle_tf2_listener.cpp)
target_link_libraries(turtle_tf2_listener ${catkin_LIBRARIES})
在start_demo.launch文件中加入
<node pkg="learning_tf2"? type="turtle_tf2_listener" ?name="listener" />
最后運(yùn)行
roslaunch?learning_tf2 start_demo.launch
(對(duì)于broadcaster和listener這兩個(gè)cpp文件只能說(shuō)是了解了個(gè)大概,還是有一些不太明白的地方,先暫且放在這里,以后用到再細(xì)看吧,關(guān)于tf2還有一些后續(xù)的教程在 http://wiki.ros.org/tf2/Tutorials/Writing%20a%20tf2%20listener%20%28C%2B%2B%29 )