tree structure
的形式存储起来,tf管理一系列的树状结构坐标系之间的关系环境搭建,运行已下命令安装相关插件资源
sudo apt-get install ros- kinetic -ros-tutorials ros- kinetic -geometry-tutorials ros- kinetic -rviz ros- kinetic -rosbash ros- kinetic -rqt-tf-tree
运行以下命令打开demo程序
roslaunch turtle_tf turtle_tf_demo.launch
将当前运行命令的窗口作为激活窗口,用箭头按键控制小乌龟移动,另外一只小乌龟讲会跟着移动
view_frames
:该命令会以一个树状图的形式,给我们展现出当前ros中tf发布的所有坐标系
运行命令生成当前坐标系树状图:rosrun tf view_frames
查看pdf文件:evince frames.pdf
从上图中可以看到
rqt_tf_tree是一个可以实时可视化显示当前tf发布的坐标系图
输入命令:rosrun rqt_tf_tree rqt_tf_tree
tf_echo 可以报告任意两个坐标系之间的变换。
用法:
rosrun tf tf_echo [reference_frame] [targe_frame]
输入命令:rosrun tf tf_echo turtle1 turtle2
rviz和tf:rviz作为一个可视化工具,可以用来观测turtle坐标系
输入命令:rosrun rviz rviz -d
rospack find turtle_tf/rviz/turtle_rviz.rviz
用键盘控制小乌龟移动,观测rviz中坐标系的变化
tf::Vector3
tf::Point
tf::Quaternion
tf::Matrix3x3
tf::Pose
tf::Transform
tf::Stamped
tf::StampedTransform
tf::TransformBroadcaster
类TransformBroadcaster()
void sendTransform(const StampedTransform &transform)
void sendTransform(const std::vector<StampedTransform> &transforms)
void sendTransform(const geometry_msgs::TransformStamped &transform)
void sendTransform(const std::vector<geometry_msgs::TransformStamped> &transforms)
tf::TransformListener
类void lookupTransform(const std::string &target_frame,
const std::string &source_frame, const ros::Time &time,
StampedTransform &transform) const
bool canTransform()
bool waitForTransform() const
创建一个tf发布器,发布tf关系,父坐标系为word,子坐标系为turtle_name
catkin_create_pkg learning_tf tf roscpp rospy turtlesim
#include
#include
#include
std::string turtle_name;
void poseCallback(const turtlesim::PoseConstPtr& msg){
static tf::TransformBroadcaster br;
tf::Transform transform;
transform.setOrigin( tf::Vector3(msg->x, msg->y, 0.0) );
tf::Quaternion q;
q.setRPY(0, 0, msg->theta);
transform.setRotation(q);
br.sendTransform(tf::StampedTransform(transform, ros::Time::now(), "world", turtle_name));
}
int main(int argc, char** argv){
ros::init(argc, argv, "my_tf_broadcaster");
if (argc != 2){
ROS_ERROR("need turtle name as argument");
return -1;
};
turtle_name = argv[1];
ros::NodeHandle node;
ros::Subscriber sub = node.subscribe(turtle_name+"/pose", 10, &poseCallback);
ros::spin();
return 0;
};
add_executable(turtle_tf_broadcaster src/turtle_tf_broadcaster.cpp)
target_link_libraries(turtle_tf_broadcaster ${catkin_LIBRARIES})
catkin_make
正常情况下将在下图所示的目录下生成turtle_tf_broadcaster节点
<launch>
<!-- 打开Turtlesim Node,小乌龟-->
<node pkg="turtlesim" type="turtlesim_node" name="sim"/>
<!-- 打开键盘控制程序 -->
<node pkg="turtlesim" type="turtle_teleop_key" name="teleop" output="screen"/>
<!-- Axes -->
<param name="scale_linear" value="2" type="double"/>
<param name="scale_angular" value="2" type="double"/>
<!-- 以turtle1为命名打开了一个发布器 -->
<node pkg="learning_tf" type="turtle_tf_broadcaster" args="/turtle1" name="turtle1_tf_broadcaster" />
<!-- 以turtle2为命名打开了一个发布器 -->
<node pkg="learning_tf" type="turtle_tf_broadcaster" args="/turtle2" name="turtle2_tf_broadcaster" />
</launch>
rosrun rqt_tf_tree rqt_tf_tree
思考:为什么我们运行了两个tf_learning节点分别命名为turtle1和turtle2,而只有turtle1的坐标系发布出来了?
提示:可以运行rqt_graph
查看节点,得出答案
创建一个订阅者:订阅乌龟的位置
// 头文件包含
#include
#include
#include
#include
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侦听器listener
tf::TransformListener listener;
ros::Rate rate(10.0);
while (node.ok()){
tf::StampedTransform transform;
try{
// 最多等待10秒,如果提前得到了坐标的转换信息,直接结束等待
listener.waitForTransform("/turtle2", "/turtle1", ros::Time(0), ros::Duration(10.0));
// 从tf上侦听turtle2在turtle1坐标系中的位置,将该位置存储在transform中
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));
// 计算turtle2跑的速度和方向
turtle_vel.publish(vel_msg);
rate.sleep();
}
return 0;
};
add_executable(turtle_tf_listener src/turtle_tf_listener.cpp)
target_link_libraries(turtle_tf_listener ${catkin_LIBRARIES} )
catkin_make
roslaunch learning_tf start_demo.launch
rqt_graph
分析是turtle1跟着turtle2跑还是turtle2跟着turtle1跑?
在src/learning_tf/src
下创建文件frame_tf_broadcaster.cpp,写入以下代码:
#include
#include
int main(int argc, char** argv){
ros::init(argc, argv, "my_tf_broadcaster");
ros::NodeHandle node;
tf::TransformBroadcaster br;
tf::Transform transform;
ros::Rate rate(10.0);
while (node.ok()){
transform.setOrigin( tf::Vector3(0.0, 2.0, 0.0) );
// 子坐标系在父坐标系中的初始姿态
transform.setRotation( tf::Quaternion(0, 0, 0, 1) );
// 发布一个以turtle1为父级命名为carrot1的坐标系
br.sendTransform(tf::StampedTransform(transform, ros::Time::now(), "turtle1", "carrot1"));
rate.sleep();
}
return 0;
};
在CMakeList.txt中添加如下代码:
add_executable(frame_tf_broadcaster src/frame_tf_broadcaster.cpp)
target_link_libraries(frame_tf_broadcaster ${catkin_LIBRARIES})
编译:catkin_make
在start_demo.launch中添加如下代码:
运行 roslaunch learning_tf start_demo.launch
运行命令查看tf树:rosrun rqt_tf_tree rqt_tf_tree
此时,操作键盘控制乌龟移动,发现跟之前的行为并没有什么区别
在turtle_tf_listener.cpp文件中做以下修改:
try{
listener.waitForTransform("/turtle2", "/carrot1", ros::Time(0), ros::Duration(10.0));
listener.lookupTransform("/turtle2", "/carrot1", ros::Time(0), transform);
}
重新编译运行,查看效果跟之前的区别
之前,我们创建了一个固定关系的坐标系,它相对于父级是不会变化的。现在创建一个动态的坐标系
修改frame_tf_broadcaster.cpp中对应的代码:
while (node.ok()){
transform.setOrigin( tf::Vector3(2.0*sin(ros::Time::now().toSec()), 2.0*cos(ros::Time::now().toSec()), 0.0) );
transform.setRotation( tf::Quaternion(0, 0, 0, 1) );
br.sendTransform(tf::StampedTransform(transform, ros::Time::now(), "turtle1", "carrot1"));
rate.sleep();
}
编译运行,观察现象
lookupTransform()
函数可获取到最近的转换关系,但是我们并不知道该转换关系对应的时间打开turtle_tf_listener.cpp,原来代码为:
ros::Time(0)
参数指的就是获取当前最新的转化关系,将代码做如下修改:
该段代码是指获取当前时间点的转换关系
编译:catkin_make
运行:roslaunch learning_tf start_demo.launch
运行时会出现一个错误:该错误是由于turtle2的生成需要一定的时间,当我们第一次要询问转换关系时,turtle2还没有生成
如何获取5S前的坐标系转换关系?
修改代码如下:
编译:catkin_make
运行:roslaunch learning_tf start_demo.launch
感觉第二只小乌龟完全不受控!
原因在于,我们获取的相对关系是5s前的,5s前的turtle2和turtle1之间的关系
如何让turtle2跟着turtle1 5s前的位置运行呢?
一共包含了6个参数: