URDF通用机器人格式化描述文件学习笔记

一、添加依赖

CMakelist.txt的find_package中增加urdf依赖。

package.xml中增加urdf依赖:

urdf
urdf

二、写一个urdf文件

一般的urdf文件test.urdf起来像是这样:




  
    
  

  
    
      
        
      
    
  

  
    
      
        
      
      
      
    
    
      
        
      
      
    
    
      
      
    
  

  
    
    
    
    
    
  

以下逐行解释上面的urdf的语法结构和关键字:


  

    
      
  

    
      
        
          
      
    
  

    
    
      
        
      
        
        
    
      
      
          
      
        
    
      
        
        
    
  

    
      
      
      
      
      
  

在ROS官方有更详细的joint、link和properties的关键字解释。

(上面带注释的代码可能会在执行时报错,使用无注释的部分即可。)

三、使用命令

check_urdf test.urdf

检查urdf文件是否正确。

urdf_to_graphiz test.urdf

生成这个urdf文件的pdf图像。
使用urdf语法解析器检查urdf、xml文件是否可以可以正常载入(可忽略,Parse a urdf file)。

四、使用robot_state_publisher包中的两个话题接口接收urdf文件和控制joint


  
    
    
  

这个launch仅为示例,告诉joint_states、robot_description这两个接口话题如何更改名字使用。

robot_description目前未见到由话题发布urdf的例子,但它有同名的参数接口使用:


  
  
  

joint_states话题的消息为sensor_msgs/JointState.msg,内容:

std_msgs/Header header
  uint32 seq
  time stamp
  string frame_id
string[] name
float64[] position
float64[] velocity
float64[] effort

在消息中name用来指定robot_state_publisher要操作test.urdf载入文件中的joint。

在消息中position用来为name所指定的joint设置运动参数值,其余的为关节中与之对应的速度和力量。

可以在上面launch的节点state_publisher_node中来发布joint_states话题。

state_publisher_node看起来像这样:

#include 
#include 
#include 

int main(int argc, char** argv)
{
  ros::init(argc, argv, "state_publisher_node");
  ros::NodeHandle n;
  ros::Publisher joint_pub = n.advertise("joint_states", 1);
  ros::Rate loop_rate(30);
  double connect_value = 0;
  bool jump = 0;
  sensor_msgs::JointState joint_state_msg;

  while (ros::ok()) 
  {
    joint_state_msg.header.stamp = ros::Time::now();
    joint_state_msg.name.resize(1);
    joint_state_msg.position.resize(1);
    joint_state_msg.name[0] ="connect";
    joint_state_msg.position[0] = connect_value;

    joint_pub.publish(joint_state_msg);

    if (connect_value >= -0.01 && jump == 1)
    {
      connect_value += 0.01;
      if (connect_value >= 1)
        jump = 0;
    }
    if (connect_value <= 1.01 && jump == 0)
    {
      connect_value -= 0.01;
      if (connect_value <= 0)
        jump = 1;
    }
    loop_rate.sleep();
  }
    return 0;
}

使用launch启动这个节点:


  

  
  
  

可以看到蓝色的杆延x轴前后移动,但程序中的移动范围不受urdf文件中limit的约束:

URDF通用机器人格式化描述文件学习笔记_第1张图片
接下来,在程序中加入TF变换使整个系统移动起来,依靠joint的连接使所有的link处于同一个相对坐标系:

#include 
#include 
#include 

int main(int argc, char** argv)
{
  ros::init(argc, argv, "state_publisher_node");
  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;

  double connect_value = 0, angle=0;
  bool jump = 0;

  geometry_msgs::TransformStamped odom_trans;
  sensor_msgs::JointState joint_state_msg;
  odom_trans.header.frame_id = "odom";
  odom_trans.child_frame_id = "base_link";

  while (ros::ok()) 
  {
    joint_state_msg.header.stamp = ros::Time::now();
    joint_state_msg.name.resize(1);
    joint_state_msg.position.resize(1);
    joint_state_msg.name[0] ="connect";
    joint_state_msg.position[0] = connect_value;

    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 = 0.7;
    odom_trans.transform.rotation = tf::createQuaternionMsgFromYaw(angle+M_PI/2);

    joint_pub.publish(joint_state_msg);
    broadcaster.sendTransform(odom_trans);

    if (connect_value >= -0.01 && jump == 1)
    {
      connect_value += 0.01;
      if (connect_value >= 1)
        jump = 0;
    }
    if (connect_value <= 1.01 && jump == 0)
    {
      connect_value -= 0.01;
      if (connect_value <= 0)
        jump = 1;
    }

    angle += degree/4;

    loop_rate.sleep();
  }
    return 0;
}

继续使用上一个launch启动,在rviz中将odom设为Fixed Frame,可以看到两个杆围绕着odom转的很魔性:

URDF通用机器人格式化描述文件学习笔记_第2张图片
如果不使用joint,就需要两个TF变换才是使两个杆相对运动,这里不再浪费篇幅。

五、使用joint_state_publisher包GUI控制joint

joint_state_publisher的GUI会加载所有非fixed类型的joint控制link移动,修改test_urdf.launch为:


  
  

  
  
  

启动它后,可使用joint_state_publisher的GUI:
URDF通用机器人格式化描述文件学习笔记_第3张图片

因为只有一个joint所以只有一个可用滑块,滑动它观察rviz中两个杆的相对位置。(urdf中limit参数会用来约束这里滑块上下限值。)

六、使用xacro简化urdf文件

使用以xacro为后缀的文件,而不再是urdf后缀。ROS官方网站对Using Xacro to Clean Up a URDF File和Xacro tutorials已经讲述的很明晰,不再敖述,此处只做归纳。

1、在xacro中,前两行必须是:


  

2、初始化变量的关键字为property:

在其他位置可以以"${width}"、"${width+2}"的方式调用,也可使用立即数"${5/6}",字符串类型可以"${width}_abc"叠加等。

可以使用YAML文件来初始化变量:

3、宏的关键字为macro:

①name用来指定宏名,供之后调用。

②params用来声明这个宏内的多个变量。

不带“*”的为普通变量。

一颗“*”的变量为一个insert_block,在urdf中相当于一行,例如这种:

两颗“*”的变量也为一个insert_block,但它相当于urdf中的一层,例如这种:


  
  

③在宏声明好之后,在下面继续写macro所包含的内容,insert_block的使用形式如下:


  
    
  
  
    
    
  

这个macro中包含了一个joint和一个link,使用这个macro:


  
  
    
    0.1
  

“suffix”在第一行初始化,“*origin”会在第二行定义出pose,“**content”在3~6行定义出container。(按变量定义的顺序初始化)

使用了这个宏后,相当于写了一个:


  


  
  
  0.1

4、预处理关键字为include:

用来载入其他xacro文件,为载入的文件增加名称空间:

使用“${namespace.property}”的方式来调用不同名称空间的参数。
5、向robot_state_publisher的robot_description接口载入xacro文件:

6、那两根魔性的杆test.urdf文件写成xacro文件如下:




  
  

  
    
  

  
    
      
        
          
        
      
    
  
  
  
    
  

  
    
      
        
          
        
        
        
      
      
        
          
        
        
      
      
    
  

  
    
    
    
      
      
    
  
  
  
    
      
      
      
      
      
    
  

  

将每个link和joint都写成了macro,然后再分别定义,其实较短的urdf不宜写成xacro,反倒复杂了,以上只是做练习不考虑实用。

用launch启动:


  
  

  
  
  

 

到这里关于URDF的学习笔记完成,如有错误还请多多指教,接下来就能开始学习Gazebo了。

你可能感兴趣的:(ROS)