1. 關於URDF的一些雜談
URDF(Unified Robot Description Format), 是一種特殊的xml文件格式, 作爲機器人的一種描述文件, 在ROS裏面大量使用. 接觸ROS比較久的同學, 應該會經常見到一種類似命名的包(package) – xxx_description. 這個包裏面就是包含某個機器人的描述文件. 比如pr2_decription, baxter_descrition, 以及ur_description等. 上述舉例的機器人描述包都是可以通過apt-get
的方式進行安裝. 使用命令格式如斯: sudo apt-get install ros-indigo-pr2-descrition
, 其中indigo
是你所安裝的ROS版本名, 後面就是所需要安裝的包名, 下劃線用中畫線代替.
對機器人使用gazebo進行仿真時, 需要加載的機器人模型就是urdf模型, 當然, 單純的urdf是不能精確描述機器人以及所需要仿真的世界的. gazebo對其進行了擴展, 感興趣的朋友可以查看gazebo官網的一些教程. 其中會提供一些標籤, 對系統動態, 重心等的設定.
如果想使用開源庫moveit對機器人進行路徑規劃, 在moveit setup assistant教程中, 第一步就是關於如何將機器人模型導入進來, 導入的機器人模型就是urdf(導入xacro格式時也是先將其解析爲urdf).
諸如這些應用, 當然還不限於這些應用, 可以看到, 瞭解URDF以及會使用URDF是一件很重要的事情. 而xacro文件, 是提供了一些更爲高級編輯方式的宏文件. 這種格式的文件, 在使用時, 均先會調用rosrun xacro xacro.py xxx.urdf.xacro > xxx.urdf
, 將其解析成對應的urdf文件. 然後再使用.
下述內容, 僅僅是對urdf文件編輯和查看由最基礎的瞭解, 以方便能夠看得懂機器人描述文件, 以及該怎麼利用別人已經寫好的描述文件進行組合, 重構得到自己的機器人描述文件. 如果是需要更詳細, 精確的描述, 則還需要自己再去對urdf文件進行學習.
2. URDF文件
2.1 link 和 joint
在URDF教程中, 第一個出現的就是下圖, 很形象的將URDF要定義的主要內容給展現了出來. 一般, 機器人都是由link和joint進行描述. 都會呈現爲樹狀(想象數據結構裏面的樹), 如圖1, 由一個根link(link1)向上, 分別出現了兩個分支–link2和link3, 分別由joint連接link. link4就可以類似的理解.
瞭解機器人的一般描述方式之後, 我們來看一下下圖中這個形狀的機器人該怎麼來描述它.
<robot name="test_robot">
<link name="link1" />
<link name="link2" />
<link name="link3" />
<link name="link4" />
<joint name="joint1" type="continuous">
<parent link="link1"/>
<child link="link2"/>
</joint>
<joint name="joint2" type="continuous">
<parent link="link1"/>
<child link="link3"/>
</joint>
<joint name="joint3" type="continuous">
<parent link="link3"/>
<child link="link4"/>
</joint>
</robot>
從上述內容可以看到, 機器人最重要的兩個內容 – link和joint, 是如何進行定義. 文件都定義了哪些標籤, 以及該怎麼使用. 其中內容很簡單, 我就不浪費篇幅了.
細心的朋友肯定能夠感受到, 雖然能夠想象的出來大概是一個什麼樣子, 但是上面的內容是不能夠唯一確定一個機器人或者一個…東西…, 因爲每個link長什麼樣子?他們又是一個什麼樣子的位置關係? 這些在上面的內容中都是沒有定義的. 如圖中所示, 可能現在每一個不同的同學, 所聯想到的樣子都是不一樣的, 一個仙人掌? 一個兩指的夾持器? anythin..當然, 上面所示內容的urdf文件是不能夠被正確解析的, 也是不能夠可視化出來的. 但上面內容就類似於整個機器人的骨架, 機器人就由這些東西組成. 一共擁有4個link, 和3個joint, 像圖中所示的樣子連接起來.
2.2 位置
在定義好了機器人的骨架後, 進一步我們可以使用origin
子標籤進行定義link所應該在的位置. 但是有一點應該注意到, link和link之間是使用joint進行連接, 那麼link的位置, 就由連接他的joint確定. 所以, 該子標籤是定義在joint內. 在三維空間中, 要精確描述一個剛性體的姿態, 僅僅使用他的xyz座標是不夠的, 還需要使用rpy. rpy角是描述船舶在海中航行時姿態的一種方法. 將船的行駛方向取爲z軸, 繞z軸旋轉稱爲滾動(Roll), 繞y軸旋轉稱爲俯仰(Pitch), 繞x軸旋轉稱爲偏轉(Yaw). 這種描述方式大量運用於各個領域. 依稀記得, kinect2關於臉部模型匹配的DEMO程序裏面, 對臉部的描述就用到了這種描述方式來描述姿態. 在機器人中, 當然運用就更多了. 現在對之前的內容進行擴充. 其中rpy代表的是角度, 用弧度表示.
<robot name="test_robot">
<link name="link1" />
<link name="link2" />
<link name="link3" />
<link name="link4" />
<joint name="joint1" type="continuous">
<parent link="link1"/>
<child link="link2"/>
<origin xyz=".5 .3 0.7" rpy="0 0 0" />
</joint>
<joint name="joint2" type="continuous">
<parent link="link1"/>
<child link="link3"/>
<origin xyz="-.2 .5 -0.3" rpy="0 0 1.57" />
</joint>
<joint name="joint3" type="continuous">
<parent link="link3"/>
<child link="link4"/>
<origin xyz=".5 0 0.2" rpy="0 0 -1.57" />
</joint>
</robot>
上述位置關係定義如下圖所示. xyz就如同一個平移向量, 將下一個link的原點座標移動到下一個位置(起點是父link的原點). 同時, 你也可以多嘗試幾次rpy, 體會下一個座標系是如何進行變換的. 雖然現在還沒有任何東西出來, 但每個link的空間位置以及姿態已經被我們所指定了.
2.3 形狀
在解決了每個link的相對位置之後, 還有一個很關鍵的問題, 就是每個link長什麼樣子呢? 圓的? 扁的? 還是奇形怪狀的? 下面示例了一些常用的形狀, 以及可能會導入的一些奇形怪狀的東西. 不但可以導入stl格式的文件, 還可以導入dae格式.下述示例中導入了一個小刀.
<?xml version="1.0"?>
<robot name="test_robot">
<link name="link1">
<visual>
<geometry>
<cylinder length="0.6" radius="0.2" />
</geometry>
</visual>
</link>
<link name="link2">
<visual>
<geometry>
<box size="0.6 0.2 .1" />
</geometry>
</visual>
</link>
<link name="link3" >
<visual>
<geometry>
<sphere radius="0.2"/>
</geometry>
</visual>
</link>
<link name="link4" >
<visual>
<origin rpy="0 -1.57 0" xyz="0 0 0"/>
<geometry>
<mesh filename="package://urdf_csdn/urdf/mesh/knife.stl"/>
</geometry>
</visual>
</link>
<joint name="joint1" type="fixed">
<parent link="link1"/>
<child link="link2"/>
<origin xyz="0.22 0 0.6" rpy="0 1.57 0" />
</joint>
<joint name="joint2" type="continuous">
<parent link="link1"/>
<child link="link3"/>
<origin xyz="-0.1 0.5 0" />
</joint>
<joint name="joint3" type="fixed">
<parent link="link3"/>
<child link="link4"/>
<origin xyz=".5 0 0" rpy="0 0 -1.57" />
</joint>
</robot>
2.4 Collision 和 joint限制
當然, 每個link一般是不會產生重合的, 在運動規劃的時候, 也會去避免碰撞到自己, 所以針對於每一個link, 還有一個collision標籤, 和visual標籤內容完全一樣.
前面內容可以看到, 每個link可以看作是一個剛體, 剛體和剛體之間是通過joint進行連接, 那麼, 問題就來了. 這個joint是固定的? 還是可以任意的動? 如果可以動, 那麼, 問題又來了, 極限位置是多少? 等等等等…
比如, 我們限定joint2只能沿着y軸旋轉, 則需要添加<axis xyz="0 1 0"/>
, 類似的, 可以指定其他關節的轉動軸, 例如<axis xyz="-0.2 0.1 1"/>
.
比如, 我們要限定joint2的移動範圍, 則需要添加<limit lower="-3.14" upper="3.14" effort="150.0" velocity="3.15"/>
, 從標籤中可以看到, 上下限以及速度, 力矩等都是可以指定的.
<?xml version="1.0"?>
<robot name="test_robot">
<link name="link1">
<visual>
<geometry>
<cylinder length="0.6" radius="0.2" />
</geometry>
</visual>
<collision>
<origin xyz="0.0 0 0.0" rpy="0 0 0" />
<geometry>
<cylinder length="0.6" radius="0.2" />
</geometry>
</collision>
</link>
<link name="link2">
<visual>
<geometry>
<box size="0.6 0.2 .1" />
</geometry>
</visual>
</link>
<link name="link3" >
<visual>
<geometry>
<sphere radius="0.2"/>
</geometry>
</visual>
</link>
<link name="link4" >
<visual>
<origin rpy="0 -1.57 0" xyz="0 0 0"/>
<geometry>
<mesh filename="package://urdf_csdn/urdf/mesh/knife.stl"/>
</geometry>
</visual>
</link>
<joint name="joint1" type="fixed">
<parent link="link1"/>
<child link="link2"/>
<origin xyz="0.22 0 0.6" rpy="0 1.57 0" />
</joint>
<joint name="joint2" type="continuous">
<parent link="link1"/>
<child link="link3"/>
<origin xyz="-0.1 0.5 0" />
<axis xyz="0 1 0"/>
<limit lower="-3.14" upper="3.14" effort="150.0" velocity="3.15"/>
</joint>
<joint name="joint3" type="fixed">
<parent link="link3"/>
<child link="link4"/>
<origin xyz=".5 0 0" rpy="0 0 -1.57" />
</joint>
</robot>
2.5 可視化
查看urdf文件, 可以使用urdf_tutorial包, 命令格式roslaunch urdf_tutorial display.launch model:=path/to/your/xxx.urdf
, 會使用Rviz進行顯示, 如上述內容, 進行顯示之後, 可以得到下圖所示內容. 座標系圖示, 需要添加TF條目進行顯示. 值得注意的地方, 將Global Options中的fixed frame設定爲link1. 如果你設定了可移動的關節, 想查看以下關節移動的效果以及設定等, 使用roslaunch urdf_tutorial display.launch model:=path/to/your/xxx.urdf gui:=true
, 你會看到彈出一個控制面板(如果沒找到, 再好好找一找, 或許真的很小).
通過上述方式, 在rviz中正確顯示模型之後, 新打開一個命令行, 輸入rostopic list
命令, 可以查看到類似如下的輸出. 可以看到, 開啓了/joint_states
話題(Topic), 使用rostopic echo /joint_states
可以看到話題數據.
3. XACRO文件
前面也提到了, XACRO文件和URDF實質上是等價的. XACRO格式提供了一些更高級的方式來組織編輯機器人描述. 主要提供了三種方式來使得整個描述文件變得簡單. 借用在教程中一句話來形容xacro的優勢: “Fortunately, you can use the xacro package to make your life simpler”.
3.1 Constants
Usage: <xacro:property name="WIDTH" value="2.0"/>
類似於C語言中的宏定義, 在頭部定義, 如<xacro:property name="WIDTH" value="2.0"/>
, 以${WIDTH}
的方式進行使用. 經常會看到的一個常量定義, <property name="PI" value="3.14159265" />
. 還有定義一個前綴, 這樣後面關節名都可以方便的進行修改. 比如<property name="prefix" value="my_"/>
, 後面關節名字就可以類似的進行更新. <joint name="${prefix}joint1" type="revolute"/>
.
在有了上面的常量定義之後, 類似於宏定義, 完成字符串替換, 同時還可以進行一些簡單的數學運算.
Usage: ${1/2}, ${PI*(WIDTH*0.5)}
3.2 Macros
這個纔是xacro文件中最重要的部分. 就像宏函數一樣, 完成一些最小模塊的定義, 方便重用, 以及可以使用參數來標識不同的部分.
3.2.1 Simple Macro
Usage:
<xacro:macro name="default_origin">
<origin xyz="0 0 0" rpy="0 0 0"/>
</xacro:macro>
<xacro:default_origin />
前面三行對宏進行定義, 第四行是使用.
3.2.2 Parameterized Macro
Usage:
<xacro:macro name="default_link" params="prefix">
<link name="${prefix}_link1" />
</xacro:macro>
<xacro:default_link prefix="my" />
類似, 前三行定義, 第四行是進行使用. 當然, 不單由這樣簡單的參數, 還可以使用塊參數.
Usage:
<xacro:macro name="default_link" params="prefix *origin">
<link name="${prefix}_link1" >
<xacro:insert_block name="prigin" />
</link>
</xacro:macro>
<xacro:default_link prefix="my">
<origin xyz="0 0 0" rpy="0 0 0" />
</xacro:default_link>
一般情況下, 很多已有的機器人模型, 都是以xacro格式提供描述, 而在xacro文件中, 整個機器人定義爲一個很大的宏. 例如, barrett hand, 想進一步瞭解的朋友可以點擊前面的鏈接, 查看以下barrett hand是如何進行描述的.
3.2.3 Include
很多模型都是已宏的形式進行定義, 並以最小集團分成很多個文件. 而最終的機器人描述就變得非常簡單了. 下面摘錄一個ur5的描述文件. 從中可以看出來xacro的強大優勢. 在最後的示例中我們還能夠看到, urdf文件也是能夠直接導入進來的.
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="ur5" >
<!-- common stuff -->
<xacro:include filename="$(find ur_description)/urdf/ur5/common.gazebo.xacro" />
<!-- ur5 -->
<xacro:include filename="$(find ur_description)/urdf/ur5/ur5.urdf.xacro" />
<!-- arm -->
<xacro:ur5_robot prefix="" joint_limited="false"/>
<link name="world" />
<joint name="world_joint" type="fixed">
<parent link="world" />
<child link = "base_link" />
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0" />
</joint>
</robot>
當然, 此時的簡單是建立在之前的複雜的基礎上的. 從上述內容中可以看到, 首先是在ur_description包中找到另外幾個xacro文件, 將其包含進來. 當然應該注意到, include類似於C語言中的include, 先將該文件擴展到包含的位置. 但包含進來的文件很有可能只是一個參數宏的定義. 並沒有被調用. 所以, 示例中調用了一個宏(<xacro:ur5_robot prefix="" joint_limited="false"/>
), 產生一個ur5機器人.
3.3 可視化
urdf_tutorial包也是可以查看xacro文件的. 使用roslaunch urdf_tutorial xacrodisplay.launch model:=path/to/your/xxx.urdf.xacro
.
4. 關於urdf_tutorial
前面提到的可視化都是使用urdf_tutorial包進行的. 分別調用了兩個launch文件. 在上面的示例中我們還看到了不但會使用rviz進行可視化, 還會發起一些話題等. 其實這些我們能夠從他的launch文件中一窺究竟.
打開命令行, 輸入: rosed urdf_tutorial dispaly.launch
. 會使用vim打開該文件. 可以看到下述內容.
<launch>
<arg name="model" />
<arg name="gui" default="False" />
<param name="robot_description" textfile="$(arg model)" />
<param name="use_gui" value="$(arg gui)"/>
<node name="joint_state_publisher" pkg="joint_state_publisher" type="joint_state_publisher" />
<node name="robot_state_publisher" pkg="robot_state_publisher" type="state_publisher" />
<node name="rviz" pkg="rviz" type="rviz" args="-d $(find urdf_tutorial)/urdf.rviz" required="true" />
</launch>
由上可以看到, 參數model是沒有默認值的, 所以調用該launch文件必須指定model參數. 其他都比較易懂, 主要解釋以下robot_description, 可以看到, 其前面是param. 這個是指定ros 參數服務器中的參數值. 而打開rviz之後, rviz就是直接從參數服務器中讀取機器人描述文件, 也就是這個參數. 然後進行顯示. use_gui也是如此對顯示產生的影響. 另外, 還發起了兩個發佈者節點, 分別發佈joint_states和robot_state. 這也就是會由joint_states話題的原因. xacrodispaly.launch文件和上面類似, 但在處理文件時, 使用的是: <param name="robot_description" command="$(find xacro)/xacro.py $(arg model)" />
. 在launch中將xacro文件解析爲urdf.
至於其中啓動的兩個節點, joint_state_publisher 和 robot_state_publisher, 可以查看ROS Answer上相關的解釋. 另外, 在robot_state_publisher概述中提到, robot_state_publisher從/joint_states
話題中獲取機器人joint角度作爲輸入, 使用機器人的運動學樹模型計算出機器人link的3D姿態, 然後將其發佈到話題/tf
和 /tf_static
. joint_state_publisher從ROS參數服務器中讀取robot_description
參數, 找到所有non-fixed joint, 發佈他們的JointState
消息到/joint_states
話題.
5. 排列組合
5.1 UR + Barrett
有了前面的基礎之後, 我們就可以進行一些屬於我們自己的私人定製了. 比如說, 使用ur5作爲手臂, 接上barrett hand或者shadow hand. 這樣就可以得到一個完整的操作模型了. 先看一下效果.
UR Description
首先需要下載所需要的UR機械臂的包, 打開命令行, 輸入: git clone https://github.com/ros-industrial/universal_robot.git
, 下載下來之後, 在universal_robot文件夾內有一個叫ur_description文件夾, 這裏面就是我們需要的機器人描述文件.
* Barrett Hand Description*
在https://github.com/RobotnikAutomation/barrett_hand/tree/hydro-devel中, 下載得到bhand_description, 就是我們所需要的barrett hand描述文件.
* 組合 *
將前面得到的ur_description和bhand_description拷貝到catkin_ws目錄下(確保能夠找到這兩個包), 到catkin_ws目錄下, 運行catkin_make
. 注意查看提示信息, 是否發現ur_description和bhand_description這兩個包.
以下述內容新建一個文件, 以xxx.urdf.xacro
格式命名. 運行roslaunch urdf_tutorial xacrodisplay.launch model:=path/to/your/xxx.urdf.xacro
來進行顯示. 最終結果就如上圖所示. 從下面的描述來看, 是不是很簡潔?
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="ur5bh" >
<!-- common stuff -->
<xacro:include filename="$(find ur_description)/urdf/common.gazebo.xacro" />
<!-- ur5 -->
<xacro:include filename="$(find ur_description)/urdf/ur5.urdf.xacro" />
<!-- barrett hand -->
<xacro:include filename="$(find bhand_description)/urdf/bh282.urdf.xacro" />
<!-- arm -->
<xacro:ur5_robot prefix="" joint_limited="true"/>
<link name="world" />
<joint name="world_joint" type="fixed">
<parent link="world" />
<child link = "base_link" />
<origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0" />
</joint>
<!-- end effector -->
<xacro:bhand_macro parent="ee_link" name="bh">
<origin xyz="0.012 0.0 0.0" rpy="${pi/2.0} ${pi/2.0} ${pi/2.0}"/>
</xacro:bhand_macro>
</robot>
5.2 Baxter + YCB
有些時候, 我們不但需要機器人, 同時還需要一些場景, 比如機器人面前的一個桌子, 桌子上面放一些物品等, 或者一些稀奇古怪的東西, 放在機器人旁邊, 和機器人一起導入來運行. 就比如下圖所示的樣子.
* Baxter *
可以使用另外一種方式來獲取Baxter的描述文件. 打開命令行, 以此輸入:
$ sudo apt-get install ros-indigo-baxter-description
$ cd ~/catkin_ws/src/
$ cp -r /opt/ros/indigo/baxter_description ./
$ rosrun xacro xacro.py --inorder baxter_description/urdf/baxter.urdf.xacro\
> baxter_description/urdf/baxter_new.urdf
如上所述, 一定得注意最後一條指令, 可以參考Github上xacro的一條Issues, 其中一定需要添加--inorder
, 參數. 否則生成模型會失敗. 所以我們提前將baxter的xacro文件轉換成urdf格式.
* The YCB Object and Model Set *
在http://rll.eecs.berkeley.edu/ycb/上, 可以下載到一些物體的三維模型以及urdf文件. 進入網站之後, 點擊下載ROS packages for setup descriptions of ‘Pitcher-Mug Protocol’ and ‘Table Setting Protocol’ in Gazebo simulation environment. 將下載下來的文件解壓到和前述文件中的同級目錄中. 其中有一些launch文件, 可以運行起來, 看一看效果. 挺好玩的.
* 排列 *
所有東西都準備好之後, 將下述內容拷貝到一個新的xacro文件中. 和之前一樣, 運行並顯示. 可以得到前述圖像中的樣子. 和之前的類似, viz打開之後, 需要將Fixed Frame設定成world, 模型才能正常顯示.
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="baxter_and_world" >
<link name="world"/>
<joint name="world_to_baxter" type="fixed">
<parent link="world"/>
<child link="base"/>
</joint>
<!-- baxter -->
<xacro:include filename="$(find baxter_description)/urdf/baxter_new.urdf" />
<!-- table -->
<xacro:include filename="$(find ycb_object_models)/models/urdf/table.urdf" />
<joint name="base_table" type="fixed" >
<parent link="world"/>
<child link="table_top_link"/>
<origin xyz="1 0 -0.7" />
</joint>
</robot>