1. tutorial任务
- 如图所示,是一个tf_tree的坐标关系图,tutorial的任务是寻找turtle2和turtle1之间的位置关系,然后将turtle2移动到turtle1 的位置,对turtle1进行跟随。(先忽略/carrot1)
2. 流程
- 首先创建/sim节点
rosrun turtlesim turtlesim_node
,这里/sim节点名称由launch文件中定义<node pkg="turtlesim" type="turtlesim_node" name="sim"/>
,也可以定义成其他名字; - /turtle1和/turtle2属于同一个节点/sim,/turtle1通过调用spawn服务生成了/turtle2 ,
rosservice call /spawn <arg>...
,实际是在listener节点创建的时候调用的(不太明白为什么没有加服务参数):
ros::service::waitForService("spawn");
//定义spawn服务客户端 add_turtle, 服务类型定义<turtlesim::Spawn>
ros::ServiceClient add_turtle = node.serviceClient<turtlesim::Spawn>("spawn");
turtlesim::Spawn srv;
add_turtle.call(srv); //呼叫服务
- /sim节点建立后,便会向/turtle1/pose和/turtle2/pose这两个主题上持续发送两个turtle的位置信息,该信息类型在
/opt/ros/kinetic/share/turtlesim/msg/Pose.msg
中定义:
float32 x
float32 y
float32 theta
float32 linear_velocity
float32 angular_velocity
编译的时候会将msg转换成对应的.h文件,供程序调用(具体细节不用知晓);
- 但该信息不能直接用于强大的tf包来计算位置关系,必须转化成/tf内置的transform类型,这就是为什么我们需要/turtle1_tf_broadcaster和/turtle2_tf_broadcaster的原因,该两节点分别接受来自/turtle1/pose和/turtle2/pose的消息(订阅/turtle1/pose和/turtle2/pose两主题),然后使用sendTransform内置函数分别将/world到/turtle1的变换关系、/world到/turtle2的变换关系发送到/tf主题上,该消息类型定义在
/opt/ros/kinetic/share/tf/msg/tfMessage.msg
:
geometry_msgs/TransformStamped[] transforms
继续找/opt/ros/kinect/share/geometry_msgs/msg/TransformStamped.msg
:
Header header
string child_frame_id # the frame id of the child frame
Transform transform
继续找/opt/ros/kinect/share/geometry_msgs/msg/Transform.msg
:
Vector3 translation
Quaternion rotation
破案了(可以继续往下找,但没必要了),该消息类型实际上就是一个 R 3 \mathcal{R}^3 R3的translation向量和一个 R 4 \mathcal{R}^4 R4的四元数向量,就可以表示一个三维空间中变换的所有信息。在tf中,你只需要把这些信息broadcast出来(例如坐标系a和坐标系b之间的变换关系,b和c之间的变换关系),tf就会自动生成一个tf_tree,记录所有坐标系的关系,而且,如果你需要知道a和c的关系,也可以直接读取,读取方式:
tf::StampedTransform transform;
tf::TransformListener listener;
listener.lookupTransform("/turtle2", "/turtle1", ros::Time(0), transform);
可以通过lookupTransform读取任意两个坐标系之间的位置关系,返回到transform中。(可以发现,无论是sendTransform还是lookupTransform,tf中用于数据传递使用的都是tf::StampedTransform数据类型,就像数据必须贴上邮票(stamp)才能发送一样,是不是很形象)
- 建立listener节点。前面起始已经提到了,listener节点就是调用了lookupTransform,寻找了/turtle2和/turtle1之间的关系,并将该关系重新转换成可以控制turtle2节点运动的cmd_vel消息类型,发布在/turtle2/cmd_vel主题上,这样就实现了/turtle2对turtle1的跟随。
- 详细代码参考TFtutorials