机器人应用

turtlebot移动机器人(03):turtlebot-indigo Packages

需要先完成ROS环境配置: 机器人操作系统ROS(03):ROS-Indigo安装 。

需要先完成网络环境配置: 智能机器人(04):通用网络配置 。

如果需要需要先完成网络环境配置: turtlebot移动机器人(05):turtlebot-kobuki和Kinect 。

1、Turtlebot的主机配置

Ubuntu 14.04.4:
Indigo:

1、Configure Ubuntu repositories
设置software sources允许”restricted,” “universe,” and “multiverse”。
2、Setup sources.list
$ sudo sh -c ‘echo “deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main” > /etc/apt/sources.list.d/ros-latest.list’
3、Set up keys
$ sudo apt-key adv –keyserver ‘hkp://keyserver.ubuntu.com:80’ –recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654
4、make sure Debian package is up-to-date
$ sudo apt-get update  && sudo apt-get install dpkg
5、安装 Desktop-Full
$ sudo apt-get install ros-indigo-desktop-full
6、Initialize
$ sudo rosdep init
$ rosdep update
7、设置环境变量
检查ROS_ROOT和ROS_PACKAGE_PATH路径是否设置:
$ printenv | grep ROS
如果未设置则刷新环境:
$ source /opt/ros/indigo/setup.bash
为了方便可以直接加入到bashrc:
$ echo “source /opt/ros/indigo/setup.bash” >> ~/.bashrc
$ source ~/.bashrc (If have more than one ROS distribution, ~/.bashrc must only source the setup.bash for the version you using.)
8、安装 rosinstall
$ sudo apt-get install python-rosinstall
(rosinstall is a frequently used command-line tool in ROS that is distributed separately. It enables you to easily download many source trees for ROS packages with one command.)

9、安装TurtleBot相关的Package

$ sudo apt-get update
$ sudo apt-get install ros-indigo-turtlebot ros-indigo-turtlebot-apps ros-indigo-turtlebot-interactions ros-indigo-turtlebot-simulator ros-indigo-kobuki-ftdi ros-indigo-rocon-remocon ros-indigo-rocon-qt-library ros-indigo-ar-track-alvar-msgs
(apt-get update, if you find any errors)

5 upgraded, 201 newly installed, 0 to remove and 412 not upgraded.
Need to get 45.4 MB of archives.
After this operation, 310 MB of additional disk space will be used.

10、catkin工作区

11、网络设置

12、开启ssh服务
$ sudo service ssh status
如果不存在则安装:
$ sudo apt-get install openssh-server

2、Turtlebot的客户机系统配置

1、安装Ubuntu操作系统相同。
2、安装ROS的ros-indigo-desktop-full installation。
3、设置网络:
二、两台的时间同步-Network Time Protocol

对于Turtle和Workstation的时间。
Clock synchronization is important for ROS.
Chrony has been found to be the best ntp client over lossy wireless. In case of robot behaves strange when messages are sent from workstation application(like rviz, rqt, or ros node running in PC), you need clock synchronization.
再例如,在坐标变换出现错误TF complaining about extrapolation into the future时,首先要考虑不同机器之间的时间矛盾:
$ntpdate -q other_computer_ip
必要的话,就安转chrony统一时间。
1. Check one machine against another machine:
$ ntpdate -q other_computer_ip
2. Install Chrony:
$ sudo apt-get install chrony
3. For instance computer c2 gets its time from computer c1 then add following line:
$ sudo /etc/chrony/chrony.conf
server c1 minpoll 0 maxpoll 5 maxdelay .05
4. That machine will then slowly move its time towards the server.
$ sudo /etc/init.d/chrony stop
$ ntpdate other_computer_ip
$ sudo /etc/init.d/chrony start
5. manually sync NTP
$ sudo ntpdate ntp.ubuntu.com

机器人操作系统ROS(25):TF框架

线性代数和矩阵论
惯性导航里的dcm、四元数
刚体力学
欧拉角

1、预备

在机器人的很多api中,存在着target frame,source frame,parent frame,child frame,这些不同名字的坐标系。
* source frame、target frame 是坐标变换的概念,source是坐标变换的源坐标系,一般称参考坐标系,target是目标坐标系,这个是把物体在source坐标系内的运动变换到target坐标系内的运动。
* parent frame、child frame是坐标系变换的概念,parent是原坐标系,child是变换后的坐标系,从parent坐标系到child坐标系的变换也就是child坐标系在parent坐标系下的描述。如果把child固结于一个刚体,那么这个变换描述的就是刚体在parent坐标系下的姿态。
这里有个巧合,当然也不是巧合,就是把child内的运动描述变换为parent内的运动描述、等于从parent到child的坐标系的变换,牵扯到线性代数里的基变换、线性变换、过渡矩阵的概念。

在线性空间里,一个矩阵不但可以描述在同一个基下把一个点运动到另一个点的线性变换,还可以描述一个基在另外一个基下的表示,也可以表示一个基到另一个基的线性变换。不过,如果单说坐标变换就简单了。所谓坐标变换就是把一个点在某个坐标系的描述,变换成在另外一个坐标系下的描述。要实现这个变换,比较简单的做法就是用当前坐标系在参考坐标系下的描述(一个矩阵)去描述(乘以)这个点在当前坐标系下的描述(坐标)。

经过多次旋转的坐标系该怎么在固定坐标系下描述呢。这里有一个前乘和后乘的问题。什么时候前,什么时候后?很简单,绕固定坐标系的轴旋转就前乘,绕动坐标系就后乘。为什么是这样呢?解释起来很复杂,但可以这样理解,绕固定轴旋转时,你把这个旋转看做是线性变换,这个变换把原来的矩阵里的各列向量旋转变换成了新的向量,所以前乘。当绕动坐标系旋转时,你把这个旋转矩阵看做是一个描述了,并且这个描述是在未乘之前的矩阵这个描述下的描述,所以要放到后面。

刚体的任一姿态(任一动坐标系)可以经由三次基本旋转得到,用三个角来描述,这就是欧拉角。在tf里eulerYPR指的是绕动坐标系的zyx旋转,RPY指的是绕固定坐标系xyz旋转,这二者等价,坐标系定义为右手,x前,y左,z上。

四元数是刚体姿态的另一种描述方式,理论基础是,刚体姿态可以经过某一特定轴经一次旋转一定角度得到,等价于欧拉角,等价于旋转矩阵。一个四个参数,一个三个参数,一个九个参数,之所以等价是因为四个参数里有一个约束,九个参数里有六个约束。

1.1、ROS_TF

tf库的目的是实现系统中任一个点在所有坐标系之间的坐标变换,也就是说,只要给定一个坐标系下的一个点的坐标,就能获得这个点在其他坐标系的坐标。

为了达到这个目的,就需要确定各个坐标系之间的关系,也就是获得任一坐标系在其他任一个坐标系下的描述. 假设有n个坐标系,那么他们之间的组合关系有c(n,2)个,如果n=4,那就是6种,如果n=100,那就是4950个,好多,不过世界没那么复杂,我们可以给这些坐标系之间构建一个结构,比如单叉树或者单层多叉树,那么100个坐标系之间的关系,就可以用99个树杈搞定。tf里的选择则是多层多叉树。

维护一颗树,需要先设计一个树的结构,也就是一定要在树被风吹来吹去变换的时候,让小树杈找个大树杈依附,让大树杈找个树干依附,让树干有个根固定。(这里提个建议,如果打算用tf解决坐标变换问题,请一定要先清晰的画出这棵树的结构,再开始写程序;因为这个结构是维护和使用的基础)。这个结构的建立和维护靠的是tf提供的tfbroadcastor类的sendtransform接口。在tb第一次发布一个从已有的parent frame到新的child frame的坐标系变换时,这棵树就会添加一个树枝,之后就是维护。

如何使用这棵树?很简单,用tf的tflisener监听某一个指定的从一个a frame到b frame的变换即可,当然前提是树上的树杈们能把a,b联通,并已经准备好。再重复一次,这个变换是a frame到b frame的变换,也表示了b frame在a frame的描述,也代表了把一个点在b frame里的坐标变换成在a frame里的坐标的坐标变换。有了这个变换,你就可以尽情的变换了。

实现的原理,非常简单,也非常粗暴。基本原理是,tb的类里有个publisher,tl的类里有个subscriber,一个发布叫/tf的topic,一个订阅这个topic,传送的消息tfmessage里包含了parent frameid和child frameid的信息。

这个机制意味着,所有的tb会发布某一特定的parent到child的变换,而所有tl会收到所有的这些变换,然后tl利用一个tfbuffercore的数据结构维护一个完整的树结构及其状态。基于此,tl在使用这棵树时,会用lookuptransform或waitfortransform来获得任意坐标系之间的变换。
非常简单,但粗暴的也非常显然。那就是只要是一个tl,就要跟所有tb建立连接,就要收取所有的/tf消息,来维护一棵完整的树,并且,还要负责搜索这棵树,找到一条变换的路径,然后乘呀乘,并且每次都要如此。

1.2、tf库的优缺点

先说优点,能用且易上手:
a、各种数值计算的细节,不用考虑,tf库可以帮你;
b、接口很简洁,会广播和监听就ok;
c、问题找的很准,那就是需要维护坐标系之间的关系;
d、提供了很多工具程序;
e、考虑了与时间相关的变换;
f、支持tf-prefix,可以在多机器人上用
g、基本能用,且不需要深入理解,死搬硬套就能用起来。

还是说缺点吧。还是那句话,简单粗暴!
a、树的结构很简单,但有时候很笨拙。举个例子吧。有部电影叫手机,葛优扮演的叫石头,范伟演的叫砖头,两个人是叔伯兄弟,从小到大的小伙伴,应该很熟悉,关系也很明确,但要放到树的结构下,就需要从下到上找共同先辈,然后从这个先辈再往下找,进而确定二者的关系,这个比较笨拙。于是有了范伟坐在门槛上的一句台词:恁这是弄啥勒,恁奶不是俺奶?!

b、每个tl都要维护同一颗树,这样的开销太大,主要是网络传输的负荷比较大。这一点,tf的设计者是完全承认的。举个例子吧,北方的有些地方的农村过年时要拜年,拜年要磕头,磕头的对象是写有逝去的先辈名字的树状图,一个同姓大家族里有很多小家庭,每个家里都有那么一张图,挨家拜年,街上人来人往,每个人都磕了很多头,磕的其实是同一张。如果有个祠堂,集中处理,维护一张图,就不用看到早早起床、满街跑的情况,效率也会高多了。

c、很难满足实时性的要求,这一点比较显然。要不tf也不会每个变换存10秒钟的数据,不过源码里好像是开了一个存100个消息的队列。

d、还有很多细节不易理解。比如,now()和time(0);比如,技术文档里的一些术语名词;比如,采用了机器人里的习惯,与飞行器,惯导,车辆里的习惯区别较大,使用时不能相当然。

综上,用不用tf需要平衡,那里用,那里不用,用什么代替,也需要平衡,不是一件很轻松愉快的事。

2、正式开始

机器人具有多个坐标框架,例如world/ base/ gripper/ head, 例如target frame,source frame,parent frame,child frame, 通过tf随时更新这些框架,就可以回答以下问题:
Where was the head frame relative to the world frame, and 5 seconds ago?
What is the pose of the object in my gripper relative to my base?
What is the current pose of the base frame in the map frame?

一个矩阵实际上定义了一个描述的视角。对空间里的一个点做描述。一个点比较绝对的描述是MX,其中M是坐标系的姿态,X是坐标向量,MX的含义就是在坐标系M里坐标为X的那个点。对于Y=MX,可以看作是IY=MX。也就是说,在I坐标系里坐标为Y的一个点和在M系里坐标为X的是同一个点。
所以,虚拟世界里的工程师安德森与锡安里的尼欧是同一个点在不同坐标系下不同的坐标描述。

1.极简教程,tf introduction demo
2. tf调试指令
2.7 static_transform_publisher
3. tf APIs
3.2 Transformations and Frames
3.3 Broadcasting Transforms
3.4 Using Published Transforms
4. Writing a tf broadcaster
5. Writing a tf listener
6. Adding a frame
7. About tf and time
8. Time travel with tf
9. debug a typical tf problem
10. Using sensor messages with tf
11. Setting up your robot using tf
12. Publishing Sensor Streams Data Over ROS
13. Using robot state publisher
14 Using urdf with robot_state_publisher

So far, a number of different packages and components make up urdf:

NOTE:坐标变换,有个很巧合的,child frame在parent frame的姿态描述(也就是把一个在child的point转化为在parent的描述)等于从parent到child的frame transform。
所谓坐标变换,就是把一个点在某个坐标系的描述,变换成在另外一个坐标系下的描述。
要实现这个变换,简单做法就是用当前坐标系在参考坐标系下的描述矩阵,去乘这个点在当前坐标系下的坐标。
所以我们知道当前坐标系在参考坐标系的描述,就可以把当前坐标系里任一点的坐标变换成参考坐标系里的坐标。

TF树结构:
假设有n个坐标系,那么他们之间的组合关系有c(n,2)个. 如果n=4,那就是6种. 如果n=100,那就是4950个.TF树的结构特点是单亲,因为怕矛盾.
维护树:TF树结构的建立和维护是靠tf提供的tfbroadcastor的sendtransform接口。tb发布一个从已有的parent frame到新的child frame的坐标系变换时,这棵树就会添加一个树枝,之后就是维护。
使用树:用tf的tflisener监听某一个指定的从一个a frame到b frame的变换即可,当然前提是树上的树杈能把a,b联通。这个变换是a frame到b frame的变换,也表示了b frame在a frame的描述,也代表了把一个点在b frame里的坐标变换成在a frame里的坐标的坐标变换。

2.1.极简教程,tf introduction demo

It shows off some of the tf power in a multi-robot example using turtlesim.
This also introduces using tf_echo, view_frames, rqt_tf_tree, and rviz.

1.1 安装依赖
$ sudo apt-get install ros-indigo-ros-tutorials ros-indigo-geometry-tutorials ros-indigo-rviz ros-indigo-rosbash ros-indigo-rqt-tf-tree

1.2 运行
$ roslaunch turtle_tf turtle_tf_demo.launch
可见两支小乌龟

1.3 跟随
用键盘方向键控制移动,可见一只始终跟随另一只。

1.4 实现
这是通过tf库构建三个框架而实现的:world , frame/turtle1 , frame/turtle2 frame。
还使用了广播机制:一只龟发布坐标,另一只龟监听坐标并计算差异实现跟从。
This tutorial uses a tf broadcaster to publish the turtle coordinate frames , and , a tf listener to compute the difference in the turtle frames and move one turtle to follow the other.

2.2. tf调试指令

Although tf is mainly a code library here comes with a large set of command-line tools that assist in the debugging and creation of tf coordinate frames.

2.1 $ roswtf
This plugin will analyze your current tf configuration and attempt to find common problems. some warnning msg can be ignored.

2.2 view_frames
$ rosrun tf view_frames
– Detected dot version 2.26.3
– frames.pdf generated
这个指令建立tf经由ROS发布的框架的框图。此时tf监听器 监听经由ROS发布的框架并绘制树形图 frames.pdf。
查看该树形图:
$ evince frames.pdf

可见tf正在广播三个框架:world/ turtle1/ turtle2,还额外显示了一些附加信息。

2.3 rqt_tf_tree
这个也可以可视化正在广播的框架的树形图,并且可以刷新。
$ rosrun rqt_tf_tree rqt_tf_tree

也可以:
$ rqt
select plugins–visulization–tftree.

2.4 tf_echo
tf_echo 该指令汇报经由ROS发布的两个框架的转换
$ rosrun tf tf_echo turtle1  turtle2
At time 1416409795.450
– Translation: [0.000, 0.000, 0.000]
– Rotation: in Quaternion [0.000, 0.000, 0.914, 0.405]
in RPY [0.000, -0.000, 2.308]
2.5 rviz
这指令更具有可视化
rosrun rviz rviz -d `rospack find turtle_tf`/rviz/turtle_rviz.rviz
驱动turtle1,这里可视化观测。通过-d指明了配置文件位置。

2.6 tf_monitor
$ rosrun tf tf_monitor
$ rosrun tf tf_monitor
Monitor a specific transform. For example to monitor the transform from /base_footprint to /odom:
$ rosrun tf tf_monitor /base_footprint  /odom
$ rosrun tf tf_monitor /base_footprint  /odom
RESULTS: for /base_footprint to /odom
Chain currently is: /base_footprint -> /odom
Net delay avg = 0.00371811: max = 0.012472
Frames:
Frame: /base_footprint published by /robot_pose_ekf Average Delay: 0.0465218 Max Delay: 0.051754
Frame: /odom published by /realtime_loop Average Delay: 0.00062444 Max Delay: 0.001553
Broadcasters:
Node: /realtime_loop 95.3222 Hz, Average Delay: 0.00062444 Max Delay: 0.001553
Node: /robot_pose_ekf 30.9654 Hz, Average Delay: 0.0465218 Max Delay: 0.051754
Node: /robot_state_publisher 25.9839 Hz, Average Delay: 0.00903061 Max Delay: 0.00939562
Here is:
$ rosrun tf tf_monitor turtle1 turtle2

2.7 static_transform_publisher
a) $ static_transform_publisher x y z yaw pitch roll   frame_id   child_frame_id period_in_ms
Which publish a static coordinate transform to tf using an x/y/z offset in meters and yaw/pitch/roll in radians.
The period, in milliseconds specifies how often to send a transform, 100ms (10hz) is a good value.

b) $ static_transform_publisher x y z qx qy qz qw   frame_id child_frame_id period_in_ms
It publish a static coordinate transform to tf using an x/y/z offset in meters and quaternion. The period, in milliseconds, specifies how often to send a transform. 100ms (10hz) is a good value.

c) launch
Not only static_transform_publisher is designed both as a command-line tool for manual use,
bu also for use within roslaunch files for setting static transforms.
For example:

2.3. tf APIs

TF涉及的集中数据类型。

3.1 Data Types
data types are defined in file tf/transform_datatypes.h:
Type——–tf
Point——-tf::Point
Vector——tf::Vector3
Quaternion–tf::Quaternion
Pose——–tf::Pose
Transform—tf::Transform

3.2 Transformations and Frames
Remeber coordinate systems in ROS are always in 3D and right-handed, with X forward, Y left, and Z up.
a) A frame,is a coordinate system.
b) Points within a frame,are represented using tf::Point, which is equivalent to the bullet type btVector3. The coordinates of a point p in a frame W are written as Wp.
c) Frame Pose, The relationship between two frames is represented by a 6 DOF relative pose: a translation, and followed by a rotation.

If W and A are two frames, the pose of A in W,is given by translation from W origin to A origin, and, rotation of A coordinate axes in W coordinate.
The translation is a vector in W coordinates, represented as WtA which read as: “Translation from frame W to A coordinate system”,, which is represented by tf::Vector3.
The rotation of A is given by a rotation matrix, represented as WAR which read as: “Rotation of frame A in W coordinate system”,

NOTE: There is no tf type for a rotation matrix; instead, tf represents rotations via tf::Quaternion, which has methods for creating quaternions from rotation matrices, and vice versa.
SO: It’s convenient to describe the translation + rotation in coordinates as a single 4×4 matrix WAT which can be read as: “the pose of frame A relative to frame W.” .
The 4×4 matrix WAT is constructed both rotation WAR and linear WtA.

d) Point Mappings
There is a duality between frame poses and mapping points from one frame to another.
The pose WAT can also be read as “transform a point in A frame to W.” The syntax gives a “cancellation” of the A frame is: WAT * Ap = Wp.

e) Transform Inverse
The inverse of a transform WAT is the transform AWT. The inverse maps points in the reverse direction from the original transform. Inverses are conveniently calculated using the inverse() member function of btTransform.

3.3 Broadcasting Transforms
To broadcast transforms within a node, we recommend using the tf::TransformBroadcaster.
tf::TransformBroadcaster();
To send a transform call sendTransform() with a fully populated transform.
void sendTransform(const StampedTransform & transform);
void sendTransform(const geometry_msgs::TransformStamped & transform);

3.4 Using Published Transforms
For most purposes using tf will be done using the tf::TransformListener.
It inherits several methods from tf::Transformer.
The primary methods which should be used when interacting with a tf::TransformListener are:
几种方法:

a) Constructors
TransformListener(const ros::NodeHandle &nh, ros::Duration max_cache_time=ros::Duration(DEFAULT_CACHE_TIME), bool spin_thread=true)
e.g.
TransformListener(ros::Duration max_cache_time=ros::Duration(DEFAULT_CACHE_TIME), bool spin_thread=true)

b) Helper methods
std::string tf::TransformListener::resolve (const std::string &frame_id)
Determine the fully resolved frame_id obeying the tf_prefix.

d) waitForTransform
Test if source_frame can be transformed to target_frame at time time.
The waitForTransform() methods return a bool whether the transform can be evaluated. It will sleep and retry every polling_duration until the duration of timeout has been passed.

g) lookupTransform
lookupTransform() is a lower level method which returns the transform between two coordinate frames. This method is the core functionality of the tf library, however most often the transform* methods will be used by the end user. This methods is designed to be used within transform*() methods.
The direction of the transform returned will be from the target_frame to the source_frame. Which if applied to data, will transform data in the source_frame into the target_frame.

3.5 Exceptions
tf::ConnectivityException
tf::ExtrapolationException
tf::InvalidArgument
tf::LookupException

3.6 tf_prefix
To support multiple “similar” robots tf uses a tf_prefix parameter. Without a tf_prefix parameter the frame name “base_link” will resolve to frame_id “/base_link”.

Next, 4 and 56 will setup above example by ourself.

先总结:
a. turtle_broadcaster节点,订阅了turtle1的姿态主题pose,然后根据pose发布world->tuetle1的tf框架变换。通常pose时定周期发布的,因此tf变换也是定周期发布。
b. turtle_listener节点,在一个死循环里面查询turtle2->turtle1的tf框架变换,然后根据框架关系,发布计算后的速度信号cmd_vel给turtle2。问题:首先速度没有pid也没有插补,其次由于tf定周期发布,所以这个节点也就不停的发布速度信号,电机废了。
c. frame_tf_broadcaster
实际的应用:
a. 下位机,发布里程信号odom。
b. pc的amcl订阅odom,得到自身位置以及速度信号twist,发布出去。

2.4. Writing a tf broadcaster

4.1 准备
$ cd ~/catkin_ws/src
$ catkin_create_pkg learning_tf tf roscpp rospy turtlesim
$ cd ..
$ catkin_make
$ source ./devel/setup.bash

4.2 代码
This teaches you how to broadcast coordinate frames to tf.
We want to broadcast the changing coordinate frames of the turtles while they move around.
这段代码在teleop操作turtlebot_name时候,随时广播world->turtlebot_name在world的变换,在tf_tree上建立了world->turtlebot_name这条边。

$ roscd learning_tf
$ nano src/turtle_tf_broadcaster.cpp
#include
#include //an implementation of a TransformBroadcaster
#include
std::string turtle_name;

void poseCallback(const turtlesim::PoseConstPtr& msg){
static tf::TransformBroadcaster br; //create a TransformBroadcaster object
tf::Transform transform; // create a Transform object
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)); //This is where the real work is done
///NOTE///: sendTransform and StampedTransform have opposite ordering of parent and child.
}

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;
};

修改CMakeLists.txt,在尾部添加:
$ vi CMakeLists.txt
add_executable(turtle_tf_broadcaster src/turtle_tf_broadcaster.cpp)
target_link_libraries(turtle_tf_broadcaster ${catkin_LIBRARIES})

cd ~/catkin_ws
$ catkin_make
二进制文件turtle_tf_broadcaster生成于devel/lib/learning_tf 之下。

4.3 脚本start_demo.launch
$ roscd learning_tf
$ mkdir launch
$ nano launch/start_demo.launch

4.4 测试
$ roslaunch learning_tf start_demo.launch
只显示一只乌龟
查看是否在发布乌龟姿态
$ rosrun tf tf_echo /world /turtle1
–ok, showing x y z r p y data.
$ rosrun tf tf_echo /world /turtle2
–fail, because tutle2 was not exist here.

2.5. Writing a tf listener

already we created a tf broadcaster to publish the pose of a turtle to tf.
Here we’ll create a tf listener to start using tf.
NOTE: it will NOT compile on ROS Hydro, due to a slight change in the naming of certain messages and topics.

$ roscd learning_tf
$ nano src/turtle_tf_listener.cpp

#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(“spawn”);
turtlesim::Spawn srv;
add_turtle.call(srv);

ros::Publisher turtle_vel =
node.advertise(“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;
// }
// NOTE:If error “Lookup would require extrapolation into the past” while running,
// try this alternative code to call the listener
try { // We want the transform from frame /turtle1 to frame /turtle2.
listener.waitForTransform(“/turtle2”, “/turtle1”, ros::Time(0), ros::Duration(10.0) );
listener.lookupTransform(“/turtle2”, “/turtle1”, ros::Time(0), transform );
} catch (tf::TransformException &ex) {
ROS_ERROR(“%s”,ex.what());
}

geometry_msgs::Twist vel_msg;
// the transform is used to calculate new linear and angular velocities for turtle2 , based on its distance and angle from turtle1
// The new velocity is published in the topic “turtle2/cmd_vel” and the sim will use that to update turtle2’s movement.
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);
//while transform of t2 -> t1 is iavailable then cal new linear and angular data for turtle2
//based on its distance and angle from turtle1
//New velocity is published in the topic “turtle2/cmd_vel”
rate.sleep();
}
return 0;
}
HINTS: turtle2 is the dest child we needed, tutlr1 is the source parent we based.
$ vi CMakeLists.txt
add the following line on the bottom:
add_executable(turtle_tf_listener src/turtle_tf_listener.cpp)
target_link_libraries(turtle_tf_listener ${catkin_LIBRARIES})

$ cd ~/catkin_ws
$ catkin_make
ok
$ vi launch/start_demo.launch
merge the node block below inside the block:

测试
$ roslaunch learning_tf start_demo.launch
You should see the turtlesim with two turtles.
$ rosrun tf tf_echo /turtle1 /turtle2
ok

问题一:出现 Frame id /turtle2 does not exist! When trying to transform between /turtle1 and /turtle2.
这是由于listener在接收到关于uturtle2的消息之前它就尝试计算变化变换,因为turtlesim构造turtle以及开始broadcasting tf frame需要一些时间。
问题二:出现”turtle2″ passed to lookupTransform argument target_frame does not exist.
这是由于在turtlesim完成构造turtle2并broadcasting tf2 frame之前listener就尝试计算变换。

2.6. Adding a frame

In the previous tutorials we recreated the turtle demo by adding a tf broadcaster and a tf listener. This tutorial will teach you how to add an extra frame to the tf tree. This is very similar to creating the tf broadcaster, and will show some of the power of tf.

6.1 Why to add frames?
For many tasks it is easier to think inside a local frame, e.g. it is easier to reason about a laser scan in a frame at the center of the laser scanner.
tf allows you to define a local frame for each sensor, link, etc in your system. And, tf will take care of all the extra frame transforms that are introduced.

6.2 Where to add frames?
TF builds up a tree structure of frames; it does not allow a closed loop in the frame structure. This means that a frame only has one single parent, but it can have multiple children.
Currently our tf tree contains three frames: world, turtle1 and turtle2. The two turtles are children of world.
If we want to add a new frame to tf, one of the three existing frames needs to be the parent frame, and the new frame will become a child frame.

6.3 How to add a frame
以乌龟1为父框架添加一个carrot框架:
$ roscd learning_tf
$ nano 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) );
br.sendTransform(tf::StampedTransform(transform, ros::Time::now(), “turtle1”, “carrot1”));
//create a new transform, from the parent turtle1 to the new child carrot1.
//The carrot1 frame is 2 meters offset to the left from the turtle1 frame.
rate.sleep();
}
return 0;
}

$ vi CMakeLists.txt
add the following line on the bottom:

add_executable(frame_tf_broadcaster src/frame_tf_broadcaster.cpp)
target_link_libraries(frame_tf_broadcaster ${catkin_LIBRARIES})

$ cd ~/watkin_ws
$ catkin_make

$ vi launch/start_demo.launch
Simply merge the node block below inside the launch block::

测试
$ roslaunch learning_tf start_demo.launch
So, if you drive the first turtle around, you notice that the behavior didn’t change from the previous tutorial, even though we added a new frame. That’s because adding an extra frame does not affect the other frames, and our listener is still using the previously defined frames. So, let’s

6.4 Try to change the behavior of the listener.
如果把lister修改为
1 listener.lookupTransform(“/turtle2”, “/carrot1”, ros::Time(0), transform);
重新make,roslaunch,会有2米偏离。

6.5 发布移动框架
The extra frame we published in this tutorial is a fixed frame that doesn’t change over time in relation to the parent frame. However, if you want to publish a moving frame you can change the broadcaster to change over time. Let’s modify the /carrot1 frame to change relative to /turtle1 over time.

把发布框架的
transform.setOrigin( tf::Vector3(0.0, 2.0, 0.0) );
transform.setRotation( tf::Quaternion(0, 0, 0, 1) );
修改为
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) );
重新make并launch,可见在追随一个运动框架。

2.7. About tf and time

In the previous tutorials we learned about how tf keeps track of a tree of coordinate frames. This tree changes over time, and tf stores a time snapshot for every transform (for up to 10 seconds by default).
Until now we used the lookupTransform() function to get access to the latest available transforms in that tf tree, without knowing at what time that transform was recorded.

This will teach you how to get a transform at a specific time.Go back to where we ended in the adding a frame tutorial.
$ roscd learning_tf
$ nano src/turtle_tf_listener.cpp

7.1 ros::Time::now()
go to the line:
listener.lookupTransform(“/turtle2”, “/carrot1”, ros::Time(0), transform);
First we make the second turtle follow the first turtle not the carrot:
listener.lookupTransform(“/turtle2”, “/turtle1”, ros::Time(0), transform);

So you can also see we specified a time equal to 0. For tf, time 0 means “the latest available” transform in the buffer.
Now, change this line to get the transform at the current time, “now()”:
listener.lookupTransform(“/turtle2”, “/turtle1”, ros::Time::now(), transform);

$ catkin_make
$ roslaunch learning_tf start_demo.launch

BUT lookupTransform() is failing, telling you repeatedly:
[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.

Why is that?
Well, each listener has a buffer where it stores all the coordinate transforms coming from the different tf broadcasters. When a broadcaster sends out a transform, it takes some time before that transform gets into the buffer (usually a couple of milliseconds). So, when you request a frame transform at time “now”, you should wait a few milliseconds for that information to arrive.

7.2 Wait for transforms …
ros::Time now = ros::Time::now();
listener.waitForTransform(“/turtle2”, “/turtle1”, now, ros::Duration(3.0));
listener.lookupTransform(“/turtle2”, “/turtle1”, now, transform);
So waitForTransform() will actually block until the transform between the two turtles becomes available (this will usually take a few milliseconds), OR –if the transform does not become available– until the timeout has been reached.
Note: The use of ros::Time::now() is for this example. Usually this would be the timestamp of the data wishing to be transformed.

$ catkin_make
$ roslaunch learning_tf start_demo.launch
BUT, you might still see the error once (error msg might vary):
[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.

Why it that?
This happens because turtle2 takes a non-zero time to spawn and start publishing tf frames. So the first time that you ask for now the /turtle2 frame may not have existed, when the transform is requested the transform may not exist yet and fails the first time. After the first transform all the transforms exist and the turtle behaves as expected.

7.3 CONCLUSION:
Now once again you should be able to simply drive around the first turtle using the arrow keys (make sure your terminal window is active, not your simulator window), and you’ll see the second turtle following the first turtle!

So, you noticed there is no noticeable difference in the turtle behavior. That is because the actual timing difference is only a few milliseconds. But then why did we make this change from Time(0) to now()? Just to teach you about the tf buffer and the time delays that are associated with it. For real tf use cases, it is often perfectly fine to use Time(0).

2.8. Time travel with tf

Above we discussed the basic concept of tf and time, now take this one step further, and expose one of the most powerful tf tricks.

$ roscd learning_tf
$ vi src/turtle_tf_listener.cpp
goto:
ros::Time now = ros::Time::now();
listener.waitForTransform(“/turtle2”, “/turtle1”, now, ros::Duration(1.0));
listener.lookupTransform(“/turtle2”, “/turtle1”, now, transform);
SO, instead of making the second turtle go to where the first turtle is now, make the second turtle go to where the first turtle was 5 seconds ago:
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);

What would you expect to see? Definitely during the first 5 seconds the second turtle would not know where to go, because we do not yet have a 5 second history of the first turtle. But what after these 5 seconds? Let’s just give it a try:
$ catkin_make
$ roslaunch learning_tf start_demo.launch

BUT, your turtle driving around uncontrollably like in this screenshot? So what is happening?
We asked tf, “What was the pose of /turtle1 5 seconds ago, relative to /turtle2 5 seconds ago?”. This means we are controlling the second turtle based on where it was 5 seconds ago as well as where the first turtle was 5 seconds ago.
What we really want to ask is, “What was the pose of /turtle1 5 seconds ago, relative to the current position of the /turtle2?”.
So how can we ask tf a question like that? This API gives us the power to say explicitly when each frame is transformed. This is what the code would look like:
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);

This is an advanced API!

2.9. debug a typical tf problem

Debugging a problem with tf can be very tricky. Many tf related problems are caused by users asking tf the wrong questions.
we need useful to debug tf problems:
Understand what you are asking tf.
Check the frames in the tf buffer.
Check the timestamps in the tf buffer.

9.1 begin
Before you can debug anything in tf, you need to understand what you are asking tf. You need to know:
-The names of the two frames you are trying to transform between, and
-The time at which to transform.

9.2 Checking the Frames
So now that you know the names of the two frames you want to transform between, let’s check if tf knows about them. The easiest way to find out is to call:
$ rosrun tf tf_echo frameA frameB
if ok you should get output that looks like this:
At time 1254169758.447
– Translation: [5.556, 5.556, 0.000]
– Rotation: in Quaternion [0.000, 0.000, 0.000, 1.000]
in RPY [0.000, -0.000, 0.000]
At time 1254169759.134
– Translation: [5.556, 5.556, 0.000]
– Rotation: in Quaternion [0.000, 0.000, 0.000, 1.000]
in RPY [0.000, -0.000, 0.000]

9.3 Checking the Timestamp
The most tricky problems in tf are often caused by timestamps. Let’s first look at the timing information of the specific transform you are looking for:
$ rosrun tf tf_monitor frameA frameB
important is this:
RESULTS: for /frameA to /frameB
Chain currently is: /frameB -> /frameA
Net delay avg = 0.007317: max = 0.04489

9.4 Starting a demo application which has a number of problems.
$ roslaunch turtle_tf start_debug_demo.launch

The following error message:
[ERROR] 1254263539.785016000: Frame id /turtle3 does not exist! When trying to transform between /turtle1 and /turtle3.

The next problem:
[ERROR] 1254264620.183142000: You requested a transform that is 0.116 miliseconds in the past,
but the most recent transform in the tf buffer is 3.565 miliseconds old.
When trying to transform between /turtle1 and /turtle2.
TRY:
$ rosrun tf tf_monitor turtle2 turtle1
RESULTS: for /turtle2 to /turtle1
Chain currently is: /turtle1 -> /turtle2
Net delay avg = 0.008562: max = 0.05632
Frames:
Broadcasters:
Node: /broadcaster1 40.01641 Hz, Average Delay: 0.0001178 Max Delay: 0.000528
Node: /broadcaster2 40.01641 Hz, Average Delay: 0.0001258 Max Delay: 0.000309

The key part here is the delay for the chain from turtle2 to turtle1. The output shows there is an average delay of 8 milliseconds. This means that tf can only transform between the turtles after 8 milliseconds are passed. So, if we would be asking tf for the transformation between the turtles 8 milliseconds ago instead of “now”, tf would be able to give us an answer sometimes. Let’s test this quickly by changing to:
listener.lookupTransform(“/turtle2”, “/turtle1”, ros::Time::now()-ros::Duration(0.1), transform);

2.10. Using sensor messages with tf

how to use sensor data with tf? Some real-world examples of sensor data are: cameras, both mono and stereo , and/or, laser scans.

Suppose, a new turtle named turtle3 is created and it doesn’t have good odometry, but there is an overhead camera tracking its position and publishing it as a geometry_msgs/PointStamped message in relation to the world frame. Turtle 1 wants to know where turtle3 is compared to itself.

To do this, turtle1 must listen to the topic where turtle3’s pose is being published, wait until transforms into the desired frame are ready, and then do its operations.

To make this easier the tf::MessageFilter class is very useful. The tf::MessageFilter will take a subscription to any ros Message with a Header and cache it until it is possible to transform it into the target frame.

Setup:
$ roslaunch turtle_tf turtle_tf_sensor.launch
This will bring up turtle 1 to drive , and turtle3 moving on its own.
There is a topic turtle_pose_stamped with a geometry_msgs/PoseStamped data type stating turtle3’s position in the world frame.
To get the streaming data in the frame of turtle1 reliably we will use the following code:

2.11. Setting up your robot using tf

Many ROS packages require the transform tree of a robot to be published using the tf software library.
At an abstract level, a transform tree defines offsets in terms of both translation and rotation between different coordinate frames.

11.1 Transform Configuration
** example of a simple robot that has a mobile base with a single laser mounted on top of it. **
In referring to the robot let’s define two coordinate frames: one corresponding to the center point of the base of the robot , one for the center point of the laser that is mounted on top of the base.
We’ll call the coordinate frame attached to the mobile base “base_link” , and , call the coordinate frame attached to the laser “base_laser”.
Let’s assume that we have some laser data in the form of distances from the laser’s center point. it means we have some data in the “base_laser” coordinate frame. We want to use it to help the mobile base avoid obstacles in the world.
To do this, we need a way of transforming the laser scan we’ve received from the “base_laser” frame to the “base_link” frame. It means we need to define a relationship between the “base_laser” and “base_link” coordinate frames..

Assume we know that the laser is mounted 10cm forward and 20cm above the center point of the mobile base.
This gives us a translational offset that relates the “base_link” frame to the “base_laser” frame.
We know that to get data from the “base_link” frame to the “base_laser” frame we must apply a translation of (x: 0.1m, y: 0.0m, z: 0.2m), and to get data from the “base_laser” frame to the “base_link” frame we must apply the opposite translation (x: -0.1m, y: 0.0m, z: -0.20m).
We’ll define the relationship between “base_link” and “base_laser” once using tf and let it manage the transformation between the two coordinate frames for us rather than we do this works.
To define and store the relationship between the “base_link” and “base_laser” frames using tf, we need to add them to a transform tree. Each ‘node’ in the transform tree corresponds to a coordinate frame and each ‘edge’ corresponds to the transform that needs to be applied to move from the current node to its child.
To create a transform tree for our simple example, we’ll create two nodes, one for the “base_link” coordinate frame and one for the “base_laser” coordinate frame. To create the edge between them, we first need to decide which node will be the parent and which will be the child. This distinction is important because tf assumes that all transforms move from parent to child. Let’s choose the “base_link” coordinate frame as the parent. This means the transform associated with the edge connecting “base_link” and “base_laser” should be (x: 0.1m, y: 0.0m, z: 0.2m).
For example beow shows that object in base-laser frame is (x: 0.3m, y: 0.0m, z: 0.0m) , and same object in base-link frame is (x: 0.4m, y: 0.0m, z: 0.2m).

11.2 Writing Code
Now, we’ve got to take the transform tree and create it with code. Suppose that we have the high level task described above of taking points in the “base_laser” frame and transforming them to the “base_link” frame.
The first thing we need to do is to create a node that will be responsible for publishing the transforms in our system.
Next, we’ll have to create a node to listen to the transform data published over ROS and apply it to transform a point.
We’ll start by creating a package for the source code to live in and we’ll give it a simple name like “robot_setup_tf” We’ll have dependencies on roscpp, tf, and geometry_msgs.
$ roscd
$ cd ../src
$ catkin_create_pkg robot_setup_tf roscpp tf geometry_msgs

11.3 Broadcasting a Transform
We need to create the node that will do the work of broadcasting the base_laser → base_link transform over ROS.
$ roscd robot_setup_tf
$ nano src/tf_broadcaster.cpp

#include
#include

int main(int argc, char** argv){
ros::init(argc, argv, “robot_tf_publisher”);
ros::NodeHandle n;

ros::Rate r(100);

tf::TransformBroadcaster broadcaster;
//create a TransformBroadcaster object to send base_link → base_laser transform
while(n.ok()){
broadcaster.sendTransform(
tf::StampedTransform(
tf::Transform(tf::Quaternion(0, 0, 0, 1), tf::Vector3(0.1, 0.0, 0.2)),
ros::Time::now(),”base_link”, “base_laser”));
r.sleep();
}
}

HINTS:
broadcaster.sendTransform(), Sending a transform with a TransformBroadcaster requires five arguments need 4 params:
PARAM1 = tf::StampedTransform( PARAM1=tf::Transform(tf::Quaternion(0, 0, 0, 1), tf::Vector3(0.1, 0.0, 0.2)),
PARAM2=ros::Time::now(),
PARAM3=”base_link”,
PARAM4=”base_laser”
PARAM1:First, we pass in the rotation transform, which is specified by a btQuaternion for any rotation that needs to occur between the two coordinate frames. In this case, we want to apply no rotation, so we send in a btQuaternion constructed from pitch, roll, and yaw values equal to zero. Second, a btVector3 for any translation that we’d like to apply. We do, however, want to apply a translation, so we create a btVector3 corresponding to the laser’s x offset of 10cm and z offset of 20cm from the robot base.
And, we need to give the transform being published a timestamp, we’ll just stamp it with ros::Time::now(). And, We need to pass the name of the parent node of the link we’re creating, in this case “base_link.”
And, we need to pass the name of the child node of the link we’re creating, in this case “base_laser.”

11.4 Using a Transform
write a node that will use that transform to take a point in the “base_laser” frame and transform it to a point in the “base_link” frame.
$ nano src/tf_listener.cpp

#include
#include
#include

//create a function that, given a TransformListener,
//takes a point in “base_laser” and transforms it to “base_link” frame.
//This function serve as callback for ros::Timer created in main() and will fire every second
void transformPoint(const tf::TransformListener& listener){
//create a demo point in base_laser frame that we’d transform to base_link frame
geometry_msgs::PointStamped laser_point;
laser_point.header.frame_id = “base_laser”;

//use the most recent transform available for our simple example
laser_point.header.stamp = ros::Time();

//just an arbitrary point in space
laser_point.point.x = 1.0;
laser_point.point.y = 0.2;
laser_point.point.z = 0.0;

try{
geometry_msgs::PointStamped base_point;
listener.transformPoint(“base_link”, laser_point, base_point);
//three arguments: name of frame we want to transform point to (“base_link” here),
//the point we’re transforming, and storage for the transformed point.

ROS_INFO(“base_laser: (%.2f, %.2f. %.2f) —–> base_link: (%.2f, %.2f, %.2f) at time %.2f”,
laser_point.point.x, laser_point.point.y, laser_point.point.z,
base_point.point.x, base_point.point.y, base_point.point.z, base_point.header.stamp.toSec());
}
catch(tf::TransformException& ex){
ROS_ERROR(“Received an exception trying to transform a point from “base_laser” to “base_link”: %s”, ex.what());
}
}

int main(int argc, char** argv){
ros::init(argc, argv, “robot_tf_listener”);
ros::NodeHandle n;

tf::TransformListener listener(ros::Duration(10));

//transform a point once every second
ros::Timer timer = n.createTimer(ros::Duration(1.0), boost::bind(&transformPoint, boost::ref(listener)));

ros::spin();
}

$ vi CMakeLists.txt
Add the following lines to the bottom of the file.
add_executable(tf_broadcaster998 src/tf_broadcaster.cpp)
add_executable(tf_listener998 src/tf_listener.cpp)
target_link_libraries(tf_broadcaster998 ${catkin_LIBRARIES})
target_link_libraries(tf_listener998 ${catkin_LIBRARIES})

$ cd ../..
$ catkin_make

11.6 test
$ rosrun robot_setup_tf tf_broadcaster998
$ rosrun robot_setup_tf tf_listener998
[ INFO] 1248138528.200823000: base_laser: (1.00, 0.20. 0.00) —–> base_link: (1.10, 0.20, 0.20) at time 1248138528.19
[ INFO] 1248138529.200820000: base_laser: (1.00, 0.20. 0.00) —–> base_link: (1.10, 0.20, 0.20) at time 1248138529.19

NEXT, we replace the demoed PointStamped point with sensor streams data that come over ROS.

2.12. Publishing Sensor Streams Data Over ROS

The next step is to replace the PointStamped we used for this example with sensor streams that come over ROS. sensor_msgs/LaserScan messages , and , sensor_msgs/PointCloud messages.

Publishing data correctly from sensors over ROS is important for Navigation. There are many sensors that can be used to provide information to the Navigation: lasers, cameras, sonar, infrared, bump sensors, etc. However, the current navigation only accepts sensor data published using either the sensor_msgs/LaserScan Message type or the sensor_msgs/PointCloud Message type.

12.1 ROS Message Headers
Both the sensor_msgs/LaserScan and sensor_msgs/PointCloud message types, like many other messages sent over ROS, contain tf frame and time dependent information. To standardize how this information is sent, the Header message type is used as a field in all such messages.

The three fields in the Header type are shown below:
#Standard metadata for higher-level flow data types
#sequence ID: consecutively increasing ID
uint32 seq
#Two-integer timestamp that is expressed as:
# * stamp.secs: seconds (stamp_secs) since epoch
# * stamp.nsecs: nanoseconds since stamp_secs
# time-handling sugar is provided by the client library
time stamp
#Frame this data is associated with
# 0: no frame
# 1: global frame
string frame_id
* The seq field corresponds to an identifier that automatically increases as Messages are sent from a given publisher.
* The stamp field stores time information associated with data in a Message. In the case of a laser scan the stamp might correspond to the time at which the scan was taken.
* The frame_id field stores tf frame information associated with data in a message. In the case of a laser scan, this would be set to the frame in which the scan was taken.

12.2 Publishing LaserScans over ROS

a) LaserScan Message
For robots with laser scanners, ROS provides a special Message type in the sensor_msgs package called LaserScan to hold information about a given scan. Before we talk about how to generate and publish these messages, let’s take a look at the message specification itself:
Header header
float32 angle_min # start angle of the scan [rad]
float32 angle_max # end angle of the scan [rad]
float32 angle_increment # angular distance between measurements [rad]
float32 time_increment # time between measurements [seconds]
float32 scan_time # time between scans [seconds]
float32 range_min # minimum range value [m]
float32 range_max # maximum range value [m]
float32[] ranges # range data [m] (Note: values < range_min or > range_max should be discarded)
float32[] intensities # intensity data [device-specific units]

b) Code to Publish a LaserScan Message
Publishing a LaserScan message over ROS is fairly straightforward.
#include
#include
int main(int argc, char** argv){
ros::init(argc, argv, “laser_scan_publisher”);

ros::NodeHandle n;
ros::Publisher scan_pub = n.advertise(“scan”, 50);

unsigned int num_readings = 100;
double laser_frequency = 40;
double ranges[num_readings];
double intensities[num_readings];
int count = 0;
ros::Rate r(1.0);

while(n.ok()){
//generate some fake data for our laser scan
for(unsigned int i = 0; i < num_readings; ++i){ ranges[i] = count; intensities[i] = 100 + count; } ros::Time scan_time = ros::Time::now(); //populate the LaserScan message sensor_msgs::LaserScan scan; scan.header.stamp = scan_time; scan.header.frame_id = “laser_frame”; scan.angle_min = -1.57; scan.angle_max = 1.57; scan.angle_increment = 3.14 / num_readings; scan.time_increment = (1 / laser_frequency) / (num_readings); scan.range_min = 0.0; scan.range_max = 100.0; scan.ranges.resize(num_readings); scan.intensities.resize(num_readings); for(unsigned int i = 0; i < num_readings; ++i){ scan.ranges[i] = ranges[i]; scan.intensities[i] = intensities[i]; } scan_pub.publish(scan); ++count; r.sleep(); } } 12.3 Publishing PointClouds over ROS For storing and sharing data about a number of points in the world, ROS provides a sensor_msgs/PointCloud message. This message, as shown below, is meant to support arrays of points in three dimensions along with any associated data stored as a channel. For example, a PointCloud could be sent over the wire with an “intensity” channel that holds information about the intensity value of each point in the cloud. #This message holds a collection of 3d points, plus optional additional information about each point. #Each Point32 should be interpreted as a 3d point in the frame given in the header Header header geometry_msgs/Point32[] points #Array of 3d points ChannelFloat32[] channels #Each channel should have the same number of elements as points array, and the data in each channel should correspond 1:1 with each point a) Code to Publish a PointCloud Message #include
#include

int main(int argc, char** argv){
ros::init(argc, argv, “point_cloud_publisher”);

ros::NodeHandle n;
ros::Publisher cloud_pub = n.advertise(“cloud”, 50);

unsigned int num_points = 100;

int count = 0;
ros::Rate r(1.0);
while(n.ok()){
sensor_msgs::PointCloud cloud;
cloud.header.stamp = ros::Time::now();
cloud.header.frame_id = “sensor_frame”;

cloud.points.resize(num_points);

//we’ll also add an intensity channel to the cloud
cloud.channels.resize(1);
cloud.channels[0].name = “intensities”;
cloud.channels[0].values.resize(num_points);

//generate some fake data for our point cloud
for(unsigned int i = 0; i < num_points; ++i){ cloud.points[i].x = 1 + count; cloud.points[i].y = 2 + count; cloud.points[i].z = 3 + count; cloud.channels[0].values[i] = 100 + count; } cloud_pub.publish(cloud); ++count; r.sleep(); } } 13. Using robot state publisher When you are working with a robot that has many relevant frames, it becomes quite a task to publish them all to tf. The robot state publisher is a tool that will do this job for you. It helps you to broadcast the state of your robot to the tf transform library. You can use the robot state publisher as a standalone ROS node , or , as a library. 13.1 Running as a ROS node The easiest way to run the robot state publisher is as a node. For normal users, this is the recommended usage. You need two things to run the robot state publisher: * A urdf xml robot description loaded on the Parameter Server. * A source that publishes the joint positions as a sensor_msgs/JointState. robot_state_publisher uses the URDF specified by the parameter robot_description and the joint positions from the topic joint_states to calculate the forward kinematics of the robot and publish the results via tf. that asy, state_publisher node Subscribed topics joint_states to joint position information , and , parameter robot_description to get urdf. 13.2 launch file Once you have setup the XML robot description and a source for joint position information, simply create a launch file like this:

13.3 Running as a library
Advanced users can also run the robot state publisher as a library, from within their own C++ code. After you include the header:
#include

Every time you want to publish the state of your robot, you call the publishTransforms functions:
// publish moving joints
void publishTransforms(const std::map<std::string, double=””>& joint_positions,
const ros::Time& time);
//The first argument is a map with joint names and joint positions,
//the second argument is the time at which the joint positions were recorded.</std::string,>

// publish fixed joints
void publishFixedTransforms();

ALSO, anvanced may be using urdf with robot_state_publisher.

14 Using urdf with robot_state_publisher

a full example of a robot model with URDF that uses robot_state_publisher.
First, we create the URDF model with all the necessary parts.
Then we write a node which publishes the JointState and transforms.
Finally, we run all the parts together.

14.1 Create the URDF File
Here is the URDF file for a 7-link model roughly approximating R2-D2.
Save the following link to your computer: model.xml

14.2 Publishing the State
Now we need a method for specifying what state the robot is in. To do this, we must specify all three joints and the overall odometry.
Create a package:
$ roscd
$ cd ..
$ catkin_create_pkg r2d2 roscpp rospy tf sensor_msgs std_msgs

$ nano src/state_publisher.cpp
#include
#include
#include
#include

int main(int argc, char** argv) {
ros::init(argc, argv, “state_publisher”);
ros::NodeHandle n;
ros::Publisher joint_pub = n.advertise(“joint_states”, 1);
tf::TransformBroadcaster broadcaster;
ros::Rate loop_rate(30);

const double degree = M_PI/180;

// robot state
double tilt = 0, tinc = degree, swivel=0, angle=0, height=0, hinc=0.005;

// message declarations
geometry_msgs::TransformStamped odom_trans;
sensor_msgs::JointState joint_state;
odom_trans.header.frame_id = “odom”;
odom_trans.child_frame_id = “axis”;

while (ros::ok()) {
//update joint_state
joint_state.header.stamp = ros::Time::now();
joint_state.name.resize(3);
joint_state.position.resize(3);
joint_state.name[0] =”swivel”;
joint_state.position[0] = swivel;
joint_state.name[1] =”tilt”;
joint_state.position[1] = tilt;
joint_state.name[2] =”periscope”;
joint_state.position[2] = height;

// update transform
// (moving in a circle with radius=2)
odom_trans.header.stamp = ros::Time::now();
odom_trans.transform.translation.x = cos(angle)*2;
odom_trans.transform.translation.y = sin(angle)*2;
odom_trans.transform.translation.z = .7;
odom_trans.transform.rotation = tf::createQuaternionMsgFromYaw(angle+M_PI/2);

//send the joint state and transform
joint_pub.publish(joint_state);
broadcaster.sendTransform(odom_trans);

// Create new robot state
tilt += tinc;
if (tilt<-.5 || tilt>0) tinc *= -1;
height += hinc;
if (height>.2 || height<0) hinc *= -1; swivel += degree; angle += degree/4; // This will adjust as needed per iteration loop_rate.sleep(); } return 0; } $ vi CMakeLists.txt Make sure to add the tf dependency in addition to the other dependencies: find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs tf) Notice that roscpp is used to parse the code that we wrote and generate the state_publisher node. We also have to add the following to the end of the CMakelists.txt in order to generate the state_publisher node: include_directories(include ${catkin_INCLUDE_DIRS}) add_executable(state_publisher src/state_publisher.cpp) target_link_libraries(state_publisher ${catkin_LIBRARIES}) $ cd ../.. $ catkin_make 14.3 Launch File launch file assumes you are using the package name “r2d2” and node name “state_publisher” and you have saved this urdf to the “r2d2” package. $ vi launch/display.launch

TEST:
$ roslaunch r2d2 display.launch
$ rosrun rviz rviz
Choose odom as your fixed frame (under Global Options).
Then choose “Add” and add a ‘Robot Model’ Display and a ‘TF’ Display.

机器人操作系统ROS(23):urdf模型

INDEX
1. pre-required urdf_tutorial package
1.1 About joint_state_publisher package
1.2 About pr2_description package
3. Building a Visual Robot Model from Scratch
3.1 only one simple shape by link
3.2 Multiple Shapes links by fixed-joint
3.3 Set Origins by origin
3.4 urdf with Material Girl
3.5 Finishing
5. Building a Movable Robot Model
5.1 Head
5.2 Gripper
5.3 Gripper Arm
5.4 Other Types of Joints
5.5 Specifying the Pose
7. Adding Physical and Collision Properties
7.1 Collision
7.2 Physical Properties
9. xacro Package
9.1 Property and Property Blocks
9.2 Math expressions
9.3 Conditional Blocks
9.4 Rospack commands
9.5 Macros
9.7 Including other xacro files
9.8 YAML support
11. Using Xacro to Clean Up a URDF File
11.1 Using Xacro
11.2 Constants
11.3 Math
11.4 Macros
11.5 Practical Usage
13. Understanding the PR2 Robot Description
15. Create your own urdf file
15.1 Create the tree structure
15.2 Add the dimensions
15.3 Completing the Kinematics
17. Parse a urdf file
19. Using the robot state publisher on your own robot
21. Using urdf with robot_state_publisher
21.1 Create the URDF File
21.2 Publishing the State
21.3 Launch File
21.4 Viewing the Results
23. Using a URDF in Gazebo
23.1 Background
23.2 Converting to Gazebo
23.3 The Element
23.4 Prerequisites
23.5 Header of a URDF File
23.6 Fixing A Model to the World
23.7 example link from RRBot
23.8 STL and Collada files
23.9 Verifying the Gazebo Model Works
25 Using Gazebo plugins with ROS
27 ROS Control
29 err
APP:
A1. TOOLS
A. urdf-XML-sensor

( URDF需要先了解:wiki.ros.org/urdf/Tutorials. )

1. pre-required of urdf_tutorial package

we’re going to build a visual model of a robot that vaguely looks like R2D2 and make it move in Gazebo. Before continuing, make sure you have the joint_state_publisher and pr2_description packages by installing urdf_tutorial.
$ sudo apt-get install ros-indigo-urdf-tutorial

1.1 About joint_state_publisher package

This package publishes sensor_msgs/JointState messages for a robot.
The package reads the robot_description parameter, finds all of the non-fixed joints and publishes a JointState message with all those joints defined.
Can be used in conjunction with the robot_state_publisher node to also publish transforms for all joint states.

—— sensor_msgs/JointState Message Definition
# This is a message that holds data to describe the state of a set of torque controlled joints.
# The state of each joint (revolute or prismatic) is defined by:
# * the position of the joint (rad or m),
# * the velocity of the joint (rad/s or m/s) and
# * the effort that is applied in the joint (Nm or N).
# Each joint is uniquely identified by its name
# The header specifies the time at which the joint states were recorded.
std_msgs/Header header
string[] name
float64[] position
float64[] velocity
float64[] effort

——> input
About data input, There are possible four sources for the value of each JointState:
* Values directly input through the GUI
* JointState messages that the node subscribes to
* The value of another joint
* The default value

output ——>
Published Topics: joint_states
Parameters: robot_description (String, default: None), Contents of the URDF file for the robot

—— parameters
robot_description :
Contents of the URDF file for the robot.
Dependent Joints:
Can specified through the dependent_joints parameter or the mimic tag in the URDF to lets you constrain certain joints to be equal to other joints, or multiples of other joints.
Default value:
The default value is zero. If zero is not a permissible value (max+min)/2 is used. To override the default value for some joints, use the zeros parameter.

—— Example YAML file:
Examples of how to set the dependent_joints and the zeros parameters using YAML syntax.
dependent_joints:
joint_D: {parent: joint_A, factor: 3 }
joint_E: {parent: joint_B }
joint_F: {parent: joint_C, factor: -1 }
zeros:
joint_A: 1.57
joint_B: 0.785

1.2 About pr2_description package

This package contains the description (mechanical, kinematic, visual, etc.) of the PR2 robot. The files in this package are parsed and used by a variety of other components. Most users will not interact directly with this package.

—— pr2_description organized subdirectories:
* urdf/ ,contains (xacro representations of) urdf descriptions of various parts of the PR2 (arm, torso, etc.).
* robots/ ,contains (xacro representations of) urdf descriptions of the full robot, that refer to the macros in urdf
* gazebo/ ,contains (xacro representations of) urdf descriptions of simulated PR2 components, like the simulated battery controller.
* meshes/ , contains mesh files (.stl,.dae) for visualization and collision properties.

To see the PR2 URDF graphically:
$ rosrun xacro xacro.py `rospack find pr2_description`/robots/pr2.urdf.xacro > pr2.urdf
$ rosrun urdf urdf_to_graphiz pr2.urdf
$ evince pr2.pdf

To use the original Kinect with the PR2 (either robot or simulation), set the KINECT1 environment variable to true.
export KINECT1=true
To use a Kinect2 with the PR2 (either robot or simulation), set the KINECT2 environment variable to true.
export KINECT2=true
If both variables are set, the Kinect 2 will take precedence over the original Kinect.

3. Building a Visual Robot Model from Scratch

Learn how to build a visual model of a robot looks like R2D2 that you can view in Rviz

3.1 only one simple shape by link

this is a robot with the name myfirst, that contains only one link (a.k.a. part), whose visual component is just a cylinder .6 meters long with a .2 meter radius.

To examine the model by launch the display.launch file:
$ roslaunch urdf_tutorial display.launch model:=urdf/01-myfirst.urdf
or,
$ roslaunch urdf_tutorial display.launch model:=’$(find urdf_tutorial)/urdf/01-myfirst.urdf’
Note that the roslaunch line above does three things:
* Loads the specified model into the parameter server
* Runs nodes to publish sensor_msgs/JointState and transforms
* Starts Rviz with a configuration file

NOTE:
* The fixed frame is transform frame where the center of the grid is located. Here, it’s a frame defined by our one link, base_link.
* The visual element (the cylinder) has its origin at the center of its geometry as a default. Hence, half the cylinder is below the grid.

3.2 Multiple Shapes links by fixed-joint

Lets look at how to add multiple shapes/links.
If we just add more link elements to the urdf, the parser won’t know where to put them. So, we have to add joints. Joint elements can refer to both flexible and inflexible joints.
We’ll start with inflexible, or named fixed joints.

$ roslaunch urdf_tutorial display.launch model:=urdf/02-multipleshapes.urdf

x+ axis direction is 0.6, y+ is 0.2 width, and z+ is 0.1 deepth.
But Both of the shapes overlap with each other, because they share the same origin. If we want them not to overlap we must define more origins–>

3.3 Set Origins by origin

So R2D2’s leg attaches to the top half of his body, on the side where we specify the origin of the JOINT to be. Also, it doesn’t attach to the middle of the leg, it attaches to the upper part, so we must offset the origin for the leg as well. We also rotate the leg so it is upright.

* The joint’s origin is defined in terms of the parent’s reference frame, ‘joint — origin xyz=”0.22 0 .25’, we are .22 meters in the x direction (left) and .25 meters in the z direction (up). This means that the origin for the child link will be up and to the left, regardless of the child link’s visual origin tag. Since we didn’t specify a rpy (roll pitch yaw) attribute, the child frame will be default have the same orientation as the parent frame.
* The leg’s visual origin, origin rpy=”0 1.57075 0″ xyz=”0 0 -0.3″, has both a xyz and rpy offset. This defines where the center of the visual element should be, relative to its origin. Since we want the leg to attach at the top, we offset the origin down by setting the z offset to be -.3 meters. And since we want the long part of the leg to be parallel to the z axis, we rotate the visual part PI/2 around the Y axis.

$ roslaunch urdf_tutorial display.launch model:=urdf/03-origins.urdf

The launch file runs packages that will create TF frames for each link in your model based on your URDF. Rviz uses this information to figure out where to display each shape.

3.4 urdf with Material Girl

Color? My robot are not red? Let’s take a look at the material tag:

$ roslaunch urdf_tutorial display.launch model:=urdf/04-materials.urdf

3.5 Finishing

Now we finish with a few more shapes. Most notably, we add a sphere and a some meshes. We’ll also add few other pieces that we’ll use later.
$ roslaunch urdf_tutorial display.launch model:=urdf/05-visual.urdf

About codes to add the sphere

Above The meshes were borrowed from the PR2, meaning you’ll need the package pr2_description to be able to see them.
* The meshes can be imported in a number of different formats. STL is fairly common, but the engine also supports DAE, which can have its own color data, meaning you don’t have to specify the color data.
* Meshes can also be sized using relative scaling parameters or a bounding box size.

Ok we have a R2D2-like URDF model now,
next step, make it mve.

5. Building a Movable Robot Model

In the previous model, all of the joints were fixed.
Now we’ll explore three other important types of joints: continuous连续, revolute旋转,prismatic棱形副。
URDF source here:

$ roslaunch urdf_tutorial display.launch model:=urdf/06-flexible.urdf gui:=True
This will visualize and control this model also pop up a GUI that allows you to control the values of all the non-fixed joints.

Now focus on three example joints.

5.1 Head

swivel旋转关节,The connection between the body and the head is a continuous joint, meaning that it can take on any angle from negative infinity to positive infinity. The wheels are also modeled like this, so that they can roll in both directions forever.
The only additional information we have to add is the axis of rotation, here specified by an xyz which specifies a vector around which the head will rotate. Since we want it to go around the z axis, we specify the vector “0 0 1”.

5.2 Gripper

Both the right and the left gripper joints are modeled as revolute joints. revolute joints means that they rotate in the same way that the continuous joints do, but they have strict limits. Hence, we must include the limit tag specifying the upper and lower limits of the joint (in radians). We also must specify a maximum velocity and effort for this joint. You can also specify the maximum effort and velocity for continuous joints but you don’t have to.

5.3 Gripper Arm

The gripper arm is a different kind of joint…namely a prismatic joint. This means that it moves along an axis, not around it. This translational movement is what allows our robot model to forwared-extend and backword-retract its gripper arm.
The limits of the prismatic arm are specified in the same way as a revolute joint, except that the units are meters, not radians.

5.4 Other Types of Joints

There are two other kinds of joints that move around in space. Whereas the prismatic joint can only move along one dimension, a planar joint can move around in a plane, or two dimensions. Furthermore, a floating joint is unconstrained, and can move around in any of the three dimensions.

5.5 Specifying the Pose

As you move the sliders around in the GUI, the model moves in Rviz. How is this done?
First the GUI parses the URDF and finds all the non-fixed joints and their limits. Then, it uses the values of the sliders to publish sensor_msgs/JointState messages.
Those are then used by robot_state_publisher to calculate all of transforms between the different parts. The resulting transform tree is then used to display all of the shapes in Rviz.

7. Adding Physical and Collision Properties

7.1 Collision

So far, we’ve only specified our links with a single sub-element, visual, which defines (not surprisingly) what the robot looks like. However, in order to get collision detection to work or to simulate the robot in something like Gazebo, we need to define a collision element as well.
URDF source:

Code for our new base link:

* The collision element is a direct subelement of the link object, at the same level as the visual tag
* The collision element defines its shape the same way the visual element does, with a geometry tag.
* You can also specify an origin in the same way as a subelement of the collision tag (as with the visual)

In many cases, you’ll want the collision geometry and origin to be exactly the same as the visual geometry and origin. However, there are two main cases where you wouldn’t:
* Quicker Processing – Doing collision detection for two meshes is a lot more computational complex than for two simple geometries. Hence, you may want to replace the meshes with simpler geometries in the collision element.
* Safe Zones – You may want to restrict movement close to sensitive equipment. For instance, if we didn’t want anything to collide with R2D2’s head, we might define the collision geometry to be a cylinder encasing his head to prevent anything from getting to near his head.

7.2 Physical Properties

In order to get your model to simulate properly, you need to define several physical properties of your robot, i.e. the properties that a physics engine like Gazebo would need.

7.2.1 Inertia 惯性
Every link element being simulated needs an inertial tag:

* This element is also a subelement of the link object.
* The mass is defined in kilograms.
* The 3×3 rotational inertia matrix is specified with the inertia element. Since this is symmetrical, it can be represented by only 6 elements, as such.
ixx/ixy/ixz,ixy/iyy/iyz,ixz/iyz/izz
* This information can be provided to you by modeling programs such as MeshLab.
* The inertia tensor depends on both the mass and the distribution of mass of the object. A good first approximation is to assume equal distribution of mass in the volume of the object and compute the inertia tensor based on the object’s shape, as outlined above.
* If unsure what to put, a matrix with ixx/iyy/izz=1e-3 or smaller is often a reasonable default for a mid-sized link (it corresponds to a box of 0.1 m side length with a mass of 0.6 kg). Although often chosen, the identity matrix is a particularly bad default, since it is often much too high (it corresponds to a box of 0.1 m side length with a mass of 600 kg!).
* You can also specify an origin tag to specify the center of gravity and the inertial reference frame (relative to the link’s reference frame).
* When using realtime controllers, inertia elements of zero (or almost zero) can cause the robot model to collapse without warning, and all links will appear with their origins coinciding with the world origin.

7.2.2 Contact Coefficients
You can also define how the links behave when they are in contact with one another. This is done with a subelement of the collision tag called contact_coefficients. There are three attributes to specify:
mu – The friction coefficient
kp – Stiffness coefficient
kd – Dampening coefficient

7.2.3 Joint Dynamics
How the joint moves is defined by the dynamics tag for the joint. There are two attributes here:
friction – The physical static friction. For prismatic joints, the units are Newtons. For revolving joints, the units are Newton meters.
damping – The physical damping value. For prismatic joints, the units are Newton seconds per meter. For revolving joints, Newton meter secons per radian.

7.2.4 Other Tags
In the realm of pure URDF (i.e. excluding Gazebo-specific tags), there are two remaining tags to help define the joints: calibration and safety controller.

9. xacro Package

xacro is a macro language. xacro package is most useful when working with large XML documents such as robot descriptions.
Consider the following Xacro XML example:

Above code expands to: If we also define macros for pr2_upperarm and pr2_forearm, then this snippet could expand to describe an entire robotic arm.

9.1 Property and Property Blocks

Properties are named values that can be inserted anywhere into the XML document. Property blocks are named snippets of XML that can be inserted anywhere that XML is allowed. Both use the property tag to define values. Property tags cannot be declared inside of a xacro:macro. The following example shows how to declare and use a property:

The two properties are inserted into the geometry expression by placing the names inside dollared-braces (${}). If you want a literal “${“, you should escape it as “$${“.

Here’s an example of using a property block:

9.2 Math expressions

Within dollared-braces (${}), you can also write simple math expressions. Currently, basic arithmetic and variable substitution is supported. Here’s an example:

9.3 Conditional Blocks

9.4 Rospack commands

Xacro allows you to use certain rospack commands with dollared-parentheses ($()).

Since ROS Indigo, it is also possible to define defaults like so:

Using this you can run xacro like:

9.5 Macros

The main feature of xacro is its support for macros. Define macros with the macro tag, and specify the macro name and the list of parameters. The list of parameters should be whitespace separated. They become macro-local properties.

9.7 Including other xacro files

You can include other xacro files using the xacro:include tag:

To avoid name clashes between properties and macros of various included files, you can specify a namespace for the included file – providing the attribute ns:

9.8 YAML support

11. Using Xacro to Clean Up a URDF File

you might be sick of doing all sorts of math to get very simple robot descriptions to parse correctly. you can use the xacro package to make your life simpler. It’s easy to do three things that are very helpful.
* Constants
* Simple Math
* Macros
Here take a look at all these shortcuts to help reduce the overall size of the URDF file and make it easier to read and maintain. It is heavily used in packages such as the urdf.

11.1 Using Xacro

The xacro program runs all of the macros and outputs the result. Typical usage looks something like this:
$ rosrun xacro xacro model.xacro > model.urdf
You can also automatically generate the urdf in a launch file. This is convenient because it stays up to date and doesn’t use up hard drive space. However, it does take time to generate, so be aware that your launch file might take longer to start up.

You must specify a namespace in order for the file to parse properly. For example, these are the first two lines of a valid xacro file:

11.2 Constants

Let’s take a quick look at our base_link in R2D2.

The information here is a little redundant. We specify the length and radius of the cylinder twice. Worse, if we want to change that, we need to do so in two different places.
Fortunately, xacro allows you to specify properties which act as constants. Instead, of the above code, we can write this.

* The two values are specified in the first two lines. They can be defined just about anywhere (assuming valid XML), at any level, before or after they are used. Usually they go at the top.
* Instead of specifying the actual radius in the geometry element, we use a dollar sign and curly brackets to signify the value.
* This code will generate the same code shown above.

The value of the contents of the ${} construct are then used to replace the ${}. This means you can combine it with other text in the attribute.

This will generate

However, the contents in the ${} don’t have to only be a property, which brings us to next point…

11.3 Math

You can build up arbitrarily complex expressions in the ${} construct using the four basic operations (+,-,*,/), the unary minus, and parenthesis. Exponentiation and modulus are not supported. Examples:

11.4 Macros

Here’s the biggest and most useful component to the xacro package.

11.4.1 Simple Macro

Let’s take a look at a simple useless macro.

This code will generate the following.

* The name is not technically a required element, but you need to specify it to be able to use it.
* Every instance of the <xacro:$name> is replaced with the contents of the xacro:macro tag.
* !!! If the xacro with a specified name is not found, it will not be expanded and will NOT generate an error. </xacro:$name>

11.4.2 Parameterized Macro

You can also parameterize macros so that they don’t generate the same exact text every time. When combined with the math functionality, this is even more powerful. First, let’s take an example of a simple macro used in R2D2.

This can be used with the code

The parameters act just like properties, and you can use them in expressions

11.5 Practical Usage

The xacro language is rather flexible in what it allows you to do. Here are a few useful ways that the xacro is used in the R2D2 model, in addition to the default inertial macro shown above:

To see the model generated by a xacro file, run this command:
$ roslaunch urdf_tutorial xacrodisplay.launch model:=urdf/08-macroed.urdf.xacro

11.5.1 Leg macro
Often you want to create multiple similar looking objects in different locations. Often, there will be some symmetry to the locations. You can use a macro and some simple math to reduce the amount of code you have to write, like we do with R2’s two legs.

* Use a name prefix to get two similarly named objects
* Use math to calculate joint origins.
* Using a reflect parameter, and setting it to 1 or -1.

13. Understanding the PR2 Robot Description

Now refers to the description in pr2_defs, which has now been replaced by the one in pr2_description.
In particular, the XML schemas specified in the element are no longer required by Gazebo, and all references to Gazebo plugins are incorrect as their API has changed considerably. Now let’s break down the entire XML piece by piece

The root element for the XML must be robot with a name attribute. XML namespaces are declared here.


Include files containing xacro macros for inidividual robot components. This is like including a header file in C: it sets up a bunch of definitions but doesn’t actually call any of them.


Some useful comments

we actually use the macros defined above. Here, we define PR2’s base, using the xacro:pr2_base_v0 macro, with name parameter equal to “base” and an origin parameter block. After running the original file through the xacro preprocessor, this element will be replaced with a rather long URDF definition of the base and its components.

Similarly, we define the PR2 torso, using the xacro:pr2_torso_v0 macro, which can be found in the included file pr2_description/urdf/torso_v0/torso.urdf.xacro.

PR2 head sensor packages, including two interlacing stereo cameras and a high resolution Prosilica GC2450 camera.

PR2 tilting Hokuyo laser range scanner.

15. Create your own urdf file

15.1 Create the tree structure

Now start creating your own urdf robot description file. we’ll create the URDF description of the “robot” shown in the image below.

The robot in the image is a tree structure. Let’s start very simple, and create a description of that tree structure, without worrying about the dimensions etc.
Create a file called my_robot.urdf:

So, just creating the structure is very simple! Now let’s see if we can get this urdf file parsed. There is a simple command line tool that will parse a urdf file for you, and tell you if the syntax is correct.
First, You might need to install, urdfdom as an upstream, ROS independent package:
$ sudo apt-get install liburdfdom-tools
Then run the check command
$ rosrun urdf check_urdf my_robot.urdf # older versions
$ rosrun urdf_parser check_urdf my_robot.urdf # electric and fuerte
$ rosrun urdfdom check_urdf my_robot.urdf # groovy
$ check_urdf my_robot.urdf # hydro onward
———
robot name is: test_robot
———- Successfully Parsed XML —————
root Link: link1 has 2 child(ren)
child(1): link2
child(2): link3
child(1): link4

15.2 Add the dimensions

So now that we have the basic tree structure, let’s add the appropriate dimensions. As you notice in the robot image, the reference frame of each link (in green) is located at the bottom of the link, and is identical to the reference frame of the joint. So, to add dimensions to our tree, all we have to specify is the offset from a link to the joint(s) of its children. To accomplish this, we will add the field to each of the joints.

Let’s look at the second joint Joint2.
Joint2 is offset in the Y-direction from link1,
a little offset in the negative X-direction from link1,
and it is rotated 90 degrees around the Z-axis. So, we need to add the following element:

If you repeat this for all the elements our URDF will look like this:

Update your file my_robot.urdf and run it through the parser:
$ check_urdf my_robot.urdf

15.3 Completing the Kinematics

What we didn’t specify yet is around which axis the joints rotate. Once we add that, we actually have a full kinematic model of this robot! All we need to do is add the element to each joint. The axis specifies the rotational axis in the local frame.

So, if you look at joint2, you see it rotates around the positive Y-axis. So, simple add the following xml to the joint element:

Similarly, joint1 is rotating around the following axis:

Note that it is a good idea to normalize the axis.

If we add this to all the joints of the robot, our URDF looks like this:

Update your file my_robot.urdf and run it through the parser:
$ check_urdf my_robot.urdf

That’s it, you created your first URDF robot description! Now you can try to visualize the URDF using graphiz:
$ urdf_to_graphiz my_robot.urdf
$ evince test_robot.pdf

17. Parse a urdf file

with the same my_robot.urdf file description of the robot shown above.
first create a package with a dependency on the urdf parser in our sandbox:
$ cd ~/catkin_ws/src
$ catkin_create_pkg testbot_description urdf
$ cd testbot_description

Now create a /urdf folder to store the urdf file we just created:
$ mkdir urdf
$ cd urdf
This follows the convention of always storing your robot’s URDF file in a ROS package named MYROBOT_description and within a subfolder named /urdf.
Other standard subfolders of your robot’s description package include /meshes, /media and /cad, like so:
/MYROBOT_description
package.xml
CMakeLists.txt
/urdf
/meshes
/materials
/cad

Next, copy your my_robot.urdf file to the package and folder we just created:
$ cp /path/to/…/testbot_description/urdf/my_robot.urdf .

Create a folder src/ and create a file called src/parser.cpp:
#include
#include “ros/ros.h”

int main(int argc, char** argv){
ros::init(argc, argv, “my_parser”);
if (argc != 2){
ROS_ERROR(“Need a urdf file as argument”);
return -1;
}
std::string urdf_file = argv[1];

urdf::Model model;
if (!model.initFile(urdf_file)){
ROS_ERROR(“Failed to parse urdf file”);
return -1;
}
ROS_INFO(“Successfully parsed urdf file”);
return 0;
}

urdf::Model is a class containing robot model data structure.
Every Robot Description File (URDF) can be described as a list of Links (urdf::Model::links_) and Joints (urdf::Model::joints_).
The connection between links(nodes) and joints(edges) should define a tree (i.e. 1 parent link, 0+ children links).

try to run this code.
First add the following lines to your CMakeList.txt file:
add_executable(parser src/parser.cpp)
target_link_libraries(parser ${catkin_LIBRARIES})
build your package,
$ cd ~/catkin_ws
$ catkin_make
and run it.
$ ./parser my_robot.urdf
— [ INFO] 1254520129.560927000: Successfully parsed urdf file

A good example of the URDF model class in action is rviz’s Robot::load

19. Using the robot state publisher on your own robot

you can publish the state of your robot to tf, using the robot state publisher.
Example launch file

21. Using urdf with robot_state_publisher

A full example of a robot model with URDF that uses robot_state_publisher.
First, we create the URDF model with all the necessary parts. Then we write a node which publishes the JointState and transforms. Finally, we run all the parts together.

21.1 Create the URDF File

Here is the URDF file for a 7-link model roughly approximating R2-D2.

21.2 Publishing the State

Now we need a method for specifying what state the robot is in. To do this, we must specify all three joints and the overall odometry. Start by creating a package:
$ cd %TOP_DIR_YOUR_CATKIN_WS%/src
$ catkin_create_pkg r2d2 roscpp rospy tf sensor_msgs std_msgs

then create src/state_publisher.cpp file:
#include
#include
#include
#include

int main(int argc, char** argv) {
ros::init(argc, argv, “state_publisher”);
ros::NodeHandle n;
ros::Publisher joint_pub = n.advertise(“joint_states”, 1);
tf::TransformBroadcaster broadcaster;
ros::Rate loop_rate(30);

const double degree = M_PI/180;

// robot state
double tilt = 0, tinc = degree, swivel=0, angle=0, height=0, hinc=0.005;

// message declarations
geometry_msgs::TransformStamped odom_trans;
sensor_msgs::JointState joint_state;
odom_trans.header.frame_id = “odom”;
odom_trans.child_frame_id = “axis”;

while (ros::ok()) {
//update joint_state
joint_state.header.stamp = ros::Time::now();
joint_state.name.resize(3);
joint_state.position.resize(3);
joint_state.name[0] =”swivel”;
joint_state.position[0] = swivel;
joint_state.name[1] =”tilt”;
joint_state.position[1] = tilt;
joint_state.name[2] =”periscope”;
joint_state.position[2] = height;

// update transform
// (moving in a circle with radius=2)
odom_trans.header.stamp = ros::Time::now();
odom_trans.transform.translation.x = cos(angle)*2;
odom_trans.transform.translation.y = sin(angle)*2;
odom_trans.transform.translation.z = .7;
odom_trans.transform.rotation = tf::createQuaternionMsgFromYaw(angle+M_PI/2);

//send the joint state and transform
joint_pub.publish(joint_state);
broadcaster.sendTransform(odom_trans);

// Create new robot state
tilt += tinc;
if (tilt<-.5 || tilt>0) tinc *= -1;
height += hinc;
if (height>.2 || height<0) hinc *= -1; swivel += degree; angle += degree/4; // This will adjust as needed per iteration loop_rate.sleep(); } return 0; } 21.3 Launch File This launch file assumes you are using the package name “r2d2” and node name “state_publisher” and you have saved urdf to the “r2d2” package.

21.4 Viewing the Results

Make sure to add the tf dependency in addition to the other dependencies:
find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs tf)

Notice that roscpp is used to parse the code that we wrote and generate the state_publisher node. We also have to add the following to the end of the CMakelists.txt in order to generate the state_publisher node:
include_directories(include ${catkin_INCLUDE_DIRS})
add_executable(state_publisher src/state_publisher.cpp)
target_link_libraries(state_publisher ${catkin_LIBRARIES})

build it using:
$ catkin_make
launch the package (assuming that our launch file is named display.launch):
$ roslaunch r2d2 display.launch

Run rviz in a new terminal using:
$ rosrun rviz rviz
Choose odom as your fixed frame (under Global Options). Then choose “Add Display” and add a Robot Model Display and a TF Display

23. Using a URDF in Gazebo

The Universal Robotic Description Format (URDF) is an XML file format used in ROS to describe all elements of a robot. To use a URDF file in Gazebo, some additional simulation-specific tags must be added to work properly with Gazebo.
This tutorial explains the necessary steps to successfully use your URDF-based robot in Gazebo, saving you from having to create a separate SDF file from scratch and duplicating description formats. Under the hood, Gazebo will then convert the URDF to SDF automatically.

23.1 Background

While URDFs are a useful and standardized format in ROS, they are lacking many features and have not been updated to deal with the evolving needs of robotics. URDF can only specify the kinematic and dynamic properties of a single robot in isolation. URDF can not specify the pose of the robot itself within a world. It is also not a universal description format since it cannot specify joint loops (parallel linkages), and it lacks friction and other properties. Additionally, it cannot specify things that are not robots, such as lights, heightmaps, etc.
On the implementation side, the URDF syntax breaks proper formatting with heavy use of XML attributes, which in turn makes URDF more inflexible. There is also no mechanism for backward compatibility.
To deal with this issue, a new format called the Simulation Description Format (SDF) was created for use in Gazebo to solve the shortcomings of URDF. SDF is a complete description for everything from the world level down to the robot level. It is scalable, and makes it easy to add and modify elements. The SDF format is itself described using XML, which facilitates a simple upgrade tool to migrate old versions to new versions. It is also self-descriptive.

23.2 Converting to Gazebo

There are several steps to get a URDF robot properly working in Gazebo. The following is an overview of steps:
Required
* An element within each element must be properly specified and configured.
Optional
Add a element for every Convert visual colors to Gazebo format
Convert stl files to dae files for better textures
Add sensor plugins
Add a element for every
Set proper damping dynamics
Add actuator control plugins
Add a element for the element
Add a link if the robot should be rigidly attached to the world/base_link

23.3 The Element

The element is an extension to the URDF used for specifying additional properties needed for simulation purposes in Gazebo. It allows you to specify the properties found in the SDF format that are not included in the URDF format.
There are three different types of elements – one for the tag, one for tags, and one for tags.

element for the tag
If a element is used without a reference=”” property, it is assumed the element is for the whole robot model.

23.4 Prerequisites

The first step to getting your robot working in Gazebo is to have a working URDF file
Test your URDF by viewing it in Rviz before proceeding to configure your robot with Gazebo.
a) Getting RRBot
RRBot, or ”Revolute-Revolute Manipulator Robot”, is a simple 3-linkage, 2-joint arm that we will use to demonstrate various features of Gazebo and URDFs.
$ cd ~/catkin_ws/src/
$ git clone https://github.com/ros-simulation/gazebo_ros_demos.git
$ cd ..
$ catkin_make

b) View in Rviz
To check if everything is working, launch RRBot in Rviz:
$ roslaunch rrbot_description rrbot_rviz.launch
loo like :

The gazebo_ros_control tutorial will explain how to use Rviz to monitor the state of your simulated robot by publishing /joint_states directly from Gazebo. In the previous example, the RRBot in Rviz is getting its /joint_states from a fake joint_states_publisher node
REF: http://gazebosim.org/tutorials?tut=ros_control

c) Examine the RRBot URDF
view the rrbot.xacro file now:
$ rosed rrbot_description rrbot.xacro

Note that we are using Xacro to make some of the link and joint calculations easier. We are also including two additional files:
* rrbot.gazebo a Gazebo specific file that includes most of our Gazebo-specific XML elements including the tags
* materials.xacro a simple Rviz colors file for storing rgba values, not really necessary but a nice convention

d) View in Gazebo
launch RRBot into Gazebo:
$ roslaunch rrbot_gazebo rrbot_world.launch

In the launched Gazebo window you should see the robot standing straight up. Despite there being no intentional disturbances in the physics simulator by default, numerical errors should start to build up and cause the double inverted pendulum to fall after a few seconds. The following is a mid-swing screenshot of the RRBot:
https://bitbucket.org/osrf/gazebo_tutorials/raw/default/ros_urdf/figs/Swinging_Arm.png

23.5 Header of a URDF File

23.6 Fixing A Model to the World

If you would like your URDF model to be permanently attached to the world frame (the ground plane), you must create a “world” link and a joint that fixes it to the base of your model. RRBot accomplishes this with the following:

If however you have a mobile base or some other moving robot, you do not need this link or joint.

23.7 example link from RRBot

a) Units
As per ROS REP 103: Standard Units of measure and Coordinate Conventions, units in Gazebo should be specified in meters and kilograms. Gazebo could possibly be used with imperial units if the constants such as gravity were changed manually, but by default gravity is 9.81 m/s^2. When specifying mass, use units of kilograms.

23.8 STL and Collada files

Like in Rviz, Gazebo can use both STL and Collada files. It is generally recommended you use Collada (.dae) files because they support colors and textures, whereas with STL files you can only have a solidly colored link.

23.9 Verifying the Gazebo Model Works

With Gazebo installed, an easy tool exists to check if your URDF can be properly converted into a SDF. Simply run the following command:
# gazebo2 and below
gzsdf print MODEL.urdf
# gazebo3 and above
gz sdf -p MODEL.urdf
This will show you the SDF that has been generated from your input URDF as well as any warnings about missing information required to generate the SDF.

Note: in Gazebo version 1.9 and greater, some of the debug info has been moved to a log file you can view with:
$ cat ~/.gazebo/gzsdf.log

8.10 Viewing the URDF In Gazebo
Viewing the RRBot in Gazebo was already covered at the beginning of this tutorial.
For your own custom robot, we assume its URDF lives in a ROS package named MYROBOT_description in the subfolder /urdf.
To view your robot and test it in Gazebo, you should be able to now run something like:
$ roslaunch MYROBOT_gazebo MYROBOT.launch
This should launch both the Gazebo server and GUI client with your robot automatically launched spawned inside.

25 Using Gazebo plugins with ROS

adding plugins to your URDF so that different aspects of your robot and the simulated environment can be controlled.

27 ROS Control

analyze the ros_control packages integrated with Gazebo for tight controller/actuator/simulator integration Actuators, controllers, and ros_control.

29 err

ROS message and service calls that are available for use with Gazebo
REF: http://gazebosim.org/tutorials/?tut=ros_urdf

APP:
1. TOOLS

1.1 Verification tools
For example, run this tool on the pr2 urdf, first create the urdf file by running:
$ rosrun xacro xacro.py `rospack find pr2_description`/robots/pr2.urdf.xacro -o /tmp/pr2.urdf

Then run the check by running:
$ check_urdf /tmp/pr2.urdf

Visualization tools
To get a graphviz diagram of your urdf file
$ urdf_to_graphiz pr2.urdf
$ evince pr2.pdf

2. urdf-XML-sensor

An example of a camera sensor element:

An example of a laser scan (ray) sensor element: