第 9 節

可視化平臺RVIZ2與URDF建模語言

0瀏覽次數0訪問次數--跳出率--平均停留

可視化簡介

座標相關、激光雷達相關、攝像頭相關的rviz2插件

rviz2基本使用

sudo apt install ros-[ROS_DISTRO]-desktop格式安裝ROS2時,RViz已經默認被安裝了。

sudo apt install ros-[ROS_DISTRO]-rviz2

備註: 命令中的 [ROS_DISTRO] 指代ROS2版本。

方式1:rviz2

方式2:ros2 run rviz2 rviz2

rviz2 啟動之後,默認界面如下:

  1. 上部為工具欄:包括視角控制、預估位姿設置、目標設置等,還可以添加自定義插件;
  2. 左側為插件顯示區:包括添加、刪除、複製、重命名插件,顯示插件,以及設置插件屬性等功能;
  3. 中間為3D試圖顯示區:以可視化的方式顯示添加的插件信息;
  4. 右側為觀測視角設置區:可以設置不同的觀測視角;
  5. 下側為時間顯示區:包括系統時間和ROS時間。

左側插件顯示區默認有兩個插件:

  • Global Options:該插件用於設置全局顯示相關的參數,一般情況下,需要自行設置的是 Fixed Frame 選項,該選項是其他所有數據在可視化顯示時所參考的全局座標系;
  • Global Status:該插件用於顯示在 Global Options 設置完畢 Fixed Frame 之後,所有的座標變換是否正常。

最上面是菜單區,左側是插件顯示區,中間是3D調試區,右側是視角切換區域,最下方是時間區ROS Time是ROS2時間,Wall Time是系統時間,Elapsed是Rviz2運行的時間。

可以保存rviz2的配置,也可以打開配置

設置顯示面板

可以設置平面網格的個數

可以設置豎直方向網格的個數

可以設置網格邊長是多大

改變sRGB的4個通道

可以改變視角

設置偏移量,如果Z是-1,那麼網格會相對於座標系下沉1個單位

Fixed name一般是根座標系的名稱

background color就是背景色

frame rate是座標系的發佈頻率

全局狀態,當fixed name設置對後,就無警告了

視角切換(一般默認)

可以翻轉Z軸

常用插件:

序號名稱功能消息類型
1Axes顯示 rviz2 默認的座標系。
2Camera顯示相機圖像,必須需要使用消息:CameraInfo。sensor_msgs/msg/Image,sensor_msgs/msg/CameraInfo
3Grid顯示以參考座標系原點為中心的網格。
4Grid Cells從網格中繪製單元格,通常是導航堆棧中成本地圖中的障礙物。nav_msgs/msg/GridCells
5Image顯示相機圖像,但是和Camera插件不同,它不需要使用 CameraInfo 消息。sensor_msgs/msg/Image
6InteractiveMarker顯示來自一個或多個交互式標記服務器的 3D 對象,並允許與它們進行鼠標交互。visualization_msgs/msg/InteractiveMarker
7Laser Scan顯示激光雷達數據。sensor_msgs/msg/LaserScan
8Map顯示地圖數據。nav_msgs/msg/OccupancyGrid
9Markers允許開發者通過主題顯示任意原始形狀的幾何體。visualization_msgs/msg/Marker,visualization_msgs/msg/MarkerArray
10Path顯示機器人導航中的路徑相關數據。nav_msgs/msg/Path
11PointStamped以小球的形式繪製一個點。geometry_msgs/msg/PointStamped
12Pose以箭頭或座標軸的方式繪製位姿。geometry_msgs/msg/PoseStamped
13Pose Array繪製一組 Pose。geometry_msgs/msg/PoseArray
14Point Cloud2繪製點雲數據。sensor_msgs/msg/PointCloud,sensor_msgs/msg/PointCloud2
15Polygon將多邊形的輪廓繪製為線。geometry_msgs/msg/Polygon
16Odometry顯示隨著時間推移累積的里程計消息。nav_msgs/msg/Odometry
17Range顯示錶示來自聲納或紅外距離傳感器的距離測量值的圓錐。sensor_msgs/msg/Range
18RobotModel顯示機器人模型。
19TF顯示 tf 變換層次結構。
20Wrench將geometry_msgs /WrenchStamped消息顯示為表示力的箭頭和表示扭矩的箭頭加圓圈。geometry_msgs/msg/WrenchStamped
21Oculus將 RViz 場景渲染到 Oculus 頭戴設備。

上述每一種插件又包含了諸多屬性,可以通過設置插件屬性來控制插件的最終顯示效果。

Image是攝像頭數據插件

LaserScan是激光雷達數據插件

TF是座標變換插件

RobotModel是機器人模型插件

ros2 run rviz2 rviz2 -d xxx.rviz
#可以读取自己保存的rviz配置

rviz2集成URDF基本流程

  案例分析

請調用如下命令,安裝案例所需的兩個功能包(可以控制機器人關節運動):

sudo apt install ros-humble-joint-state-publisher
sudo apt install ros-humble-joint-state-publisher-gui

終端下進入工作空間的src目錄,調用如下命令創建C++功能包。

ros2 pkg create cpp06_urdf --build-type ament_cmake

功能包下新建 urdf、rviz、launch、meshes目錄以備用,其中 urdf 目錄下再新建子目錄 urdf 與 xacro,分別用於存儲 urdf 文件和 xacro 文件。

launch存放launch文件

urdf文件裡面存放urdf三維模型文件

meshes存放stl模型

xacro可以簡化urdf文件,並且增強其靈活性

rviz存放rviz2的配置

  框架搭建


 <robot> name="hello_world"
   <link> name="base_link"
     <visual>
       <geometry>
         <box size="0.5 0.2 0.1"/>
       </geometry>
     </visual>
   </link>
 </robot>

  標準的XML文件

from launch import LaunchDescription
from launch_ros.actions import Node
import os
from ament_index_python.packages import get_package_share_directory
from launch_ros.parameter_descriptions import ParameterValue
from launch.substitutions import Command,LaunchConfiguration
from launch.actions import DeclareLaunchArgument

#示例:ros2 launch cpp06_urdf display.launch.py model:=`ros2 pkg prefix --share cpp06_urdf`/urdf/urdf/demo01_helloworld.urdf
def generate_launch_description():

    cpp06_urdf_dir = get_package_share_directory("cpp06_urdf")
    default_model_path = os.path.join(cpp06_urdf_dir,"urdf/urdf","demo01_helloworld.urdf")
    default_rviz_path = os.path.join(cpp06_urdf_dir,"rviz","display.rviz")
    model = DeclareLaunchArgument(name="model", default_value=default_model_path)

    # 加载机器人模型

    # 1.启动 robot_state_publisher 节点并以参数方式加载 urdf 文件
    robot_description = ParameterValue(Command(["xacro ",LaunchConfiguration("model")]))
    robot_state_publisher = Node(
        package="robot_state_publisher",
        executable="robot_state_publisher",
        parameters=[{"robot_description": robot_description}]
    )

    # 2.启动 joint_state_publisher 节点发布非固定关节状态
    joint_state_publisher = Node(
        package="joint_state_publisher",
        executable="joint_state_publisher"
    )

    # rviz2 节点
    rviz2 = Node(
        package="rviz2",
        executable="rviz2"

        # arguments=["-d", default_rviz_path]
    )
    return LaunchDescription([
        model,
        robot_state_publisher,
        joint_state_publisher,
        rviz2
    ])

<exec_depend>rviz2</exec_depend>
<exec_depend>xacro</exec_depend>
<exec_depend>robot_state_publisher</exec_depend>
<exec_depend>joint_state_publisher</exec_depend>
<exec_depend>ros2launch</exec_depend>

install(
  DIRECTORY launch urdf rviz meshes
  DESTINATION share/${PROJECT_NAME}  
)

colcon build --packages-select cpp06_urdf

source install/setup.bash
ros2 launch cpp06_urdf display.launch.py

   小提示:

  在本章的後續案例中,所有實現都遵循上述步驟,在後續案例中我們只需要關注 urdf 實現即可,launch 文件和 配置文件無需修改。

  urdf文件

  按ctrl+\生成註釋

因為安裝過urdf插件,所以有提示,需要創建robot根標籤

第一個屬性為機器人名字

第二個有個xml namespace,指向xacro

第三個有個xml namespace,指向xacro,然後還有一個機器人名字

(暫時用最簡單的,也就是第一個)

link標籤叫連桿,也需要起個名字,連桿一般指剛體部分

link有個子集標籤,叫visual

visual標籤下要寫機器人形狀

然後該標籤下又有一個子集標籤叫geometry(幾何形狀)

然後又有子集標籤叫box(矩形體狀)後面的size後面對應長寬高


 <robot name="boxrobot"> 

    <link name="base_link"> 

      <visual>

        <geometry>

          <box size="1.0 0.5 0.1"/>
        </geometry>
      </visual>
    </link>
  </robot>

  xacro工具(將磁盤文件加載到ROS2中的工具)

搜索是否安裝過xacro

ros2 pkg list | grep -i xacro

如果打印了xacro說明安裝了,如果沒打印,則要手動安裝

sudo apt-get update
sudo apt-get install ros-humble-xacro

使用xacro讀取文件

文件裡的內容被輸出到了終端,咱們一般集成到launch文件中。咱們在終端裡是隻能查看內容,但是用launch就可以把文件弄到節點裡,也就是集成到ROS2裡。

  launch核心實現

核心實現就三步,加載機器人模型,節點發布非固定關節的狀態,啟動rviz2節點

創建rviz2節點很簡單,就聲明下包名,聲明下executable。

加載機器人模型比較複雜,加載機器人模型,也要創建一個節點,

然後有參數,參數裡有個鍵叫robot_description,然後這個鍵對應一個值

值是ParameterValue對象,這個對象裡執行了一個指令,叫xacro,然後後面又有一個Launch配置,其實就是urdf文件的路徑。

這個值,其實就是URDF文件裡的內容,但是內容太長了,所以我們把它封裝成一個對象。

命令行,不能直接當對象參數值,所以還要封裝

Comand是專門封裝終端指令執行的

記得xacro後面要有空格,這裡填路徑

此時已經定位到cpp06_urdf的share路徑下的cpp06_urdf路徑了,返回的也就是該路徑的字符串

from launch import LaunchDescription
from launch_ros.actions import Node

# 封装终端指令相关类

# from launch.actions import ExecuteProcess

# from launch.substitutions import FindExecutable

# 参数声明与获取

# from launch.actions import DeclareLaunchArgument

# from launch.substitutions import LaunchConfiguration

# 文件包含相关

# from launch.actions import IncludeLaunchDescription

# from launch.launch_description_sources import PythonLaunchDescriptionSource

# 分组相关

# from launch_ros.actions import PushRosNamespace

# from launch.actions import GroupAction

# 事件相关

# from launch.event_handlers import OnProcessStart,OnProcessExit

# from launch.actions import ExecuteProcess,RegisterEventHandler,LogInfo

# 获取功能包下share目录或路径
from ament_index_python.packages import get_package_share_directory

from launch_ros.parameter_descriptions import ParameterValue
from launch.substitutions import Command

p_value = ParameterValue(Command(["xacro ",get_package_share_directory("cpp06_urdf") + "/urdf/urdf/demo01_boxrobot.urdf"]))
robot_state_pub = Node(
    package="robot_state_publisher",
    executable="robot_state_publisher",
    parameters=[{"robot_description":p_value}]
)

rviz2 = Node(
    package="rviz2",
    executable="rviz2"
    )

def generate_launch_description():
    return LaunchDescription([robot_state_pub,rviz2])

點擊左下角Add添加RobotModel插件

新建一個座標系插件,長沿著X,寬沿著Y,高沿著Z

  launch優化說明與實現

我們還需要優化三個點,第一個是打開關節節點,第二是設置rviz2默認配置文件,第三是Launch文件中我們將讀取的urdf文件寫死了,所以要優化結構。

還要啟動這個節點,來控制關節運動,可以改成joint_state_publisher_gui,出現圖形化界面。

保存一下rviz2的配置

正常的指令是

ros2 run rviz2 rviz2 -d rviz2配置的路径

創建一個參數叫model,值是後面那一長串。

LaunchConfiguration是解析參數

記得要把model放在最前面,放在後面是不可以的,現在已經把路徑封裝完畢了。

現在啟動是正常啟動默認的urdf路徑。

解析非默認值的urdf,在終端裡也有類似於get_package_share_directory,以下就是(這裡參數model少寫了個L)要把參數值用反引號(ESC與TAB中間的按鍵)框起來。

ros2 pkg prefix --share cpp06_urdf

ros2 run launch cpp06_urdf display.launch.py model:=`ros2 pkg prefix --share cpp06_urdf`/urdf/urdf/hahah.urdf

from launch import LaunchDescription
from launch_ros.actions import Node

# 封装终端指令相关类

# from launch.actions import ExecuteProcess

# from launch.substitutions import FindExecutable

# 参数声明与获取
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration

# 文件包含相关

# from launch.actions import IncludeLaunchDescription

# from launch.launch_description_sources import PythonLaunchDescriptionSource

# 分组相关

# from launch_ros.actions import PushRosNamespace

# from launch.actions import GroupAction

# 事件相关

# from launch.event_handlers import OnProcessStart,OnProcessExit

# from launch.actions import ExecuteProcess,RegisterEventHandler,LogInfo

# 获取功能包下share目录或路径
from ament_index_python.packages import get_package_share_directory

from launch_ros.parameter_descriptions import ParameterValue
from launch.substitutions import Command

model = DeclareLaunchArgument(name="model",default_value=get_package_share_directory("cpp06_urdf") + "/urdf/urdf/demo01_boxrobot.urdf")

p_value = ParameterValue(Command(["xacro ",LaunchConfiguration("model")]))
robot_state_pub = Node(
    package="robot_state_publisher",
    executable="robot_state_publisher",
    parameters=[{"robot_description":p_value}]
)

joint_state_pub = Node(
    package="joint_state_publisher",
    executable="joint_state_publisher"
    )

rviz2 = Node(
    package="rviz2",
    executable="rviz2",
    arguments=["-d",get_package_share_directory("cpp06_urdf") + "/rviz/urdf.rviz"]
    )

def generate_launch_description():
    return LaunchDescription([model,robot_state_pub,joint_state_pub,rviz2])

繼續優化最終的代碼為:

from launch import LaunchDescription
from launch_ros.actions import Node

# 封装终端指令相关类

# from launch.actions import ExecuteProcess

# from launch.substitutions import FindExecutable

# 参数声明与获取
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration

# 文件包含相关

# from launch.actions import IncludeLaunchDescription

# from launch.launch_description_sources import PythonLaunchDescriptionSource

# 分组相关

# from launch_ros.actions import PushRosNamespace

# from launch.actions import GroupAction

# 事件相关

# from launch.event_handlers import OnProcessStart,OnProcessExit

# from launch.actions import ExecuteProcess,RegisterEventHandler,LogInfo

# 获取功能包下share目录或路径
from ament_index_python.packages import get_package_share_directory

from launch_ros.parameter_descriptions import ParameterValue
from launch.substitutions import Command

import os

cpp06_urdf_dir = get_package_share_directory("cpp06_urdf")

default_model_path = os.path.join(cpp06_urdf_dir,"urdf/urdf","demo01_boxrobot.urdf")
default_rviz_path = os.path.join(cpp06_urdf_dir,"rviz","urdf.rviz")

model = DeclareLaunchArgument(name="model",default_value=default_model_path)

p_value = ParameterValue(Command(["xacro ",LaunchConfiguration("model")]))
robot_state_pub = Node(
    package="robot_state_publisher",
    executable="robot_state_publisher",
    parameters=[{"robot_description":p_value}]
)

# 关节信息节点

# joint_state_pub = Node(

#     package="joint_state_publisher",

#     executable="joint_state_publisher"

# )

# 关节信息节点图形界面(建议)
joint_state_pub = Node(
    package="joint_state_publisher_gui",
    executable="joint_state_publisher_gui"
)

rviz2 = Node(
    package="rviz2",
    executable="rviz2",

#    arguments=["-d",get_package_share_directory("cpp06_urdf") + "/rviz/urdf.rviz"]
    arguments=["-d",default_rviz_path]

    )

def generate_launch_description():
    return LaunchDescription([model,robot_state_pub,joint_state_pub,rviz2])

URDF語法

簡介

robot根標籤

對機器人進行分割,分割成幾個子集,比如一個子集描述頭,一個子集描述身子,最後再合成合集,成機器人

雖然我的子文件和主文件在邏輯上是有包含關係的,但是其實,他們都是單獨的urdf文件。主文件中,robot標籤的name屬性必須寫,子文件可以不寫,如果寫,那麼子文件name的值與主文件的必須相同!

link標籤

簡介

每一個link都是剛體,都是獨立部件

Link是通過joint進行拼接的

Link主要包含三部分,Visual,Collision和Inertial

沒加尖括號的是屬性,加了的是標籤

(可選):用於描述link的可視化屬性,可以設置link的形狀(立方體、球體、圓柱等)。

  • name (可選):指定link名稱,此名稱會映射為同名座標系,還可以通過引用該值定位定位link。
  • (必填):用於設置link的形狀,比如:立方體、球體或圓柱。
    • :立方體標籤,通過size屬性設置立方體的邊長,原點為其幾何中心。
    • :圓柱標籤,通過radius屬性設置圓柱半徑,通過length屬性設置圓柱高度,原點為其幾何中心。
    • :球體標籤,通過radius屬性設置球體半徑,原點為其幾何中心。
    • :通過屬性filename引用“皮膚”文件,為link設置外觀,該文件必須是本地文件。使用 package:///為文件名添加前綴。
    • (可選):用於設置link的相對偏移量以及旋轉角度,如未指定則使用默認值(無偏移且無旋轉)。
      • xyz :表示x、y、z三個維度上的偏移量(以米為單位),不同數值之間使用空格分隔,如未指定則使用默認值(三個維度無偏移)。
      • rpy :表示翻滾、俯仰與偏航的角度(以弧度為單位),不同數值之間使用空格分隔,如未指定則使用默認值(三個維度無旋轉)。
    • (可選):視覺元素的材質。也可以在根標籤robot中定義material標籤,然後,可以在link中按名稱進行引用。
      • name (可選):為material指定名稱,可以通過該值進行引用。
      • (可選):rgba 材質的顏色,由代表red/green/blue/alpha 的四個數字組成,每個數字的範圍為 [0,1]。
      • (可選):材質的紋理,可以由屬性filename設置。

當有多個Visual的時候,需要給Visual設置name,所以是個可選項。

Collision與仿真有關係,我們可以給我們的機器人的剛體設置一個碰撞區間,只要障礙物進入了區間,那麼就發生了碰撞,一般碰撞區間要比實際大小要大。

(可選):link的碰撞屬性。可以與link的視覺屬性一致,也可以不同,比如:我們會通常使用更簡單的碰撞模型來減少計算時間,或者設置的值大於link的視覺屬性,以儘量避免碰撞。另外,同一鏈接可以存在多個 標籤實例,多個幾何圖形組合表示link的碰撞屬性。

  • name (可選):為collision設置名稱。
  • (必須):請參考visual標籤的geometry使用規則。
  • (可選):請參考visual標籤的origin使用規則。

Inertial是設置慣性矩陣的,也是和仿真有關係的。比如說機器人剎車,會出現前傾的情況,比如說慣性矩陣的重心高一些,那麼急剎車就會出現翻車的情況了。

(可選):用於設置link的質量、質心位置和中心慣性特性,如果未指定,則默認為質量為0、慣性為0。

  • (可選):該位姿(平移、旋轉)描述了鏈接的質心框架 C 相對於鏈接框架 L 的位置和方向。
    • xyz :表示從 Lo(鏈接框架原點)到 Co(鏈接的質心)的位置向量為 x L̂x + y L̂y + z L̂z,其中 L̂x、L̂y、L̂z 是鏈接框架 L 的正交單位向量。
    • rpy :將 C 的單位向量 Ĉx、Ĉy、Ĉz 相對於鏈接框架 L 的方向表示為以弧度為單位的歐拉旋轉序列 (r p y)。注意:Ĉx、Ĉy、Ĉz 不需要與連桿的慣性主軸對齊。
  • (必填):通過其value屬性設置link的質量。
  • (必填):對於固定在質心座標系 C 中的單位向量 Ĉx、Ĉy、Ĉz,該連桿的慣性矩 ixx、iyy、izz 以及關於 Co(連桿的質心)的慣性 ixy、ixz、iyz 的乘積。

注意: 在仿真環境下才需要使用到,如果只是在 rviz2 中集成 urdf,那麼不必須為 link 定義這兩個標籤。

使用

<robot name="link_demo">

  <material name="yellow">
    <color rgba="0.7 0.7 0 0.8" />
  </material>
  <link name="base_link">
    <visual>

        <geometry>

            <box size="0.5 0.3 0.1" />

        </geometry>

        <origin xyz="0 0 0" rpy="0 0 0" />

        <material name="yellow"/>
    </visual>
  </link>
</robot>

ros2 launch cpp06_urdf display.launch.py model:=`ros2 pkg prefix --share cpp06_urdf`/urdf/urdf/demo02_link.urdf

矩形體,球形,圓柱體

平移量,X平移1,Y和Z平移0,rpy旋轉度設置為0(旋轉度分別是歐拉角里的翻滾角Roll(繞X),俯仰角Pitch(繞Y),航向角Yaw(繞Z))

發現長方體在X上偏移了1

讓航向角Yaw(繞Z運動)為0.5 rad

設置翻滾角Roll(繞X運動)為0.5rad

設置俯仰角Pitch(繞Y運動)為0.5rad

sRGB是R,G,B,Alpha(浮點模型,所以範圍是0-1.0)

如果一個顏色要被用好幾次,可以封裝成一個類似於全局變量的東西,然後在其他link中調用時,直接用 (這裡是個閉環,注意)

<robot name="link_demo">

    <material name="yellow">
      <color rgba="0.7 0.7 0 0.8" />
    </material>
    <link name="base_link">
      <visual>

          <geometry>

              <box size="0.5 0.3 0.1" />

          </geometry>

          <origin xyz="0 0 0" rpy="0 0 0" />

          <material name="yellow"/>
      </visual>
    </link>
  </robot>
使用補充

mesh標籤是引用皮膚文件,一般是stl文件,可以用SolidWorks導出,可以看文檔後面的SW2URDF

如果不會使用solidworks,可以學,這東西2天半就能學會,只是一個工具,只會畫圖沒有啥水平,最重要的還是機械設計比較難。想學的可以看兄弟社團機械學會微信公眾號的視頻進行學習。

https://mp.weixin.qq.com/mp/homepage?\_\_biz=MzI4MjkyMDgyMA==&hid=7&sn=1efc3d3cee0142970227785f767cc7c8&scene=18

當然,沒時間學習可以直接用別人畫好的機器人,在Github上搜索turtlebot3

這裡有已經導出的模型,bases裡是外觀的模型,sensors是傳感器的模型,wheels是輪子的模型。

我們克隆下倉庫,注意要克隆branch是ros2的分支。

我們引用一個就行了,看看效果即可,真正的應用還是要用SolidWorks通過SW2URDF插件進行導出

把項目裡的meshes/bases裡的burger_base.stl拷貝到我們WS裡的meshes目錄

filename是寫剛才的皮膚文件,package://就是協議名,後面跟包名,也就是cpp06_urdf。然後跟功能包下的文件路徑,也就是meshes/burger_base.stl(其實也就是share下的路徑)

因為是個三維模型,所以在填scale大小縮放時,需要填3個比例,咱們都填1.0即可。

為什麼顯示的模型會這麼大呢,因為rviz2以米為單位,而stl是以mm為單位,注意。在機械上,默認不說單位就都是mm,不要亂改單位,一般都要以mm為單位,咱們是做機器人的,要專業一些。

joint標籤

簡介

urdf 中的 joint 標籤用於描述機器人關節的運動學和動力學屬性,還可以指定關節運動的安全極限,機器人的兩個部件(分別稱之為 parent link 與 child link)以”關節“的形式相連接,不同的關節有不同的運動形式: 旋轉、滑動、固定、旋轉速度、旋轉角度限制....,比如:安裝在底座上的輪子可以360度旋轉,而攝像頭則可能是完全固定在底座上。

  • name (必填):為關節命名,名稱需要唯一。
  • type (必填):設置關節類型,可用類型如下:
    • continuous:旋轉關節,可以繞單軸無限旋轉。
    • revolute:旋轉關節,類似於 continues,但是有旋轉角度限制。
    • prismatic:滑動關節,沿某一軸線移動的關節,有位置極限。
    • planer:平面關節,允許在平面正交方向上平移或旋轉。
    • floating:浮動關節,允許進行平移、旋轉運動。
    • fixed:固定關節,不允許運動的特殊關節。

以下是子級標籤

  • (必填):指定父級link。
    • link (必填):父級link的名字,是這個link在機器人結構樹中的名字。
  • (必填):指定子級link。
    • link (必填):子級link的名字,是這個link在機器人結構樹中的名字。
  • (可選):這是從父link到子link的轉換,關節位於子link的原點。
    • xyz :各軸線上的偏移量。
    • rpy :各軸線上的偏移弧度。
  • <axis> (可選):如不設置,默認值為(1,0,0)。
    • xyz :用於設置圍繞哪個關節軸運動。
  • (可選):關節的參考位置,用於校準關節的絕對位置。
    • rising (可選):當關節向正方向移動時,該參考位置將觸發上升沿。
    • falling (可選):當關節向正方向移動時,該參考位置將觸發下降沿。
  • (可選):指定接頭物理特性的元素。這些值用於指定關節的建模屬性,對仿真較為有用。
    • damping (可選):關節的物理阻尼值,默認為0。
    • friction (可選):關節的物理靜摩擦值,默認為0。
  • (關節類型是revolute或prismatic時為必須的):
    • lower (可選):指定關節下限的屬性(旋轉關節以弧度為單位,稜柱關節以米為單位)。如果關節是連續的,則省略。
    • upper (可選):指定關節上限的屬性(旋轉關節以弧度為單位,稜柱關節以米為單位)。如果關節是連續的,則省略。
    • effort (必填):指定關節可受力的最大值。
    • velocity (必填):用於設置最大關節速度(旋轉關節以弧度每秒 [rad/s] 為單位,稜柱關節以米每秒 [m/s] 為單位)。
  • (可選):此標籤用於指定定義的關節模仿另一個現有關節。該關節的值可以計算為value = multiplier * other_joint_value + offset
    • joint (必填):指定要模擬的關節的名稱。
    • multiplier (可選):指定上述公式中的乘法因子。
    • offset (可選):指定要在上述公式中添加的偏移量,默認為 0(旋轉關節的單位是弧度,稜柱關節的單位是米)。
  • <safety_controller> (可選):安全控制器。
    • soft_lower_limit (可選):指定安全控制器開始限制關節位置的下關節邊界,此限制需要大於joint下限。
    • soft_upper_limit (可選):指定安全控制器開始限制關節位置的關節上邊界的屬性,此限制需要小於joint上限。
    • k_position (可選):指定位置和速度限制之間的關係。
    • k_velocity (必填):指定力和速度限制之間的關係。

關節名稱是必填的,且是唯一的。

咱們最常用的是有限位的revolute類型的關節,continuous可無限旋轉的關節,fixed固定關節,這個根據具體的關節類型來填。

revolute一般用於工業機器人機械臂的關節,continuous比如舵輪結構的“關節”,fixed就是一些固定的不能運動的結構關節。

子集標籤很多,咱們用的最常用的就幾個,記住常用的即可。

parent標籤,link屬性指定父級link的名字。

child標籤類似。

這個軸,默認是1,0,0,以X軸進行旋轉,但是咱們一般是需要設置的,可通過SolidWorks設置基準軸進行設置。

剩下的標籤,都與關節類型有關,比如limit,如果關節類型是revolute,而且不設置limit,那麼在joint_state_publisher_gui裡是無法調關節的角度的。

其他標籤用到的時候再進行介紹。

練習

先把倆關節,單獨實現,然後再通過joint關節進行連接。

黃色的話,紅和綠要多一些。

這是紅色。

底盤Link就創建完畢了,

攝像頭Link也創建完畢了,

關節名字設置為camera2base_link,也就是攝像頭連接底座的joint,然後類型是360度都可以轉的continuous。

填入子級Link與父級Link

這樣兩個Link就通過該Joint連接到一起了。

還需要設置這倆選項,咱們先不設置,先看默認是什麼狀態。

colcon build --packages-select cpp06_urdf
source install/setup.bash
ros2 launch cpp06_urdf display.launch.py model:=`ros2 pkg prefix --share cpp06_urdf`/urdf/urdf/demo03_joint.urdf

可以打開TF看看座標系,勾上Show Names,發現是重合的。所以顯示效果不滿足咱們的邏輯業務。(默認狀態下),所以需要設置偏移量。

如果咱們想把攝像頭移動到車頭,如圖所示,在X上有偏移量,Y沒有,但是Z有。然後roll,pitch,yaw上沒有。

Z的高度就是1/2的底盤高度+1/2的攝像頭高度

咱們要繞Yaw旋轉,所以也就是繞Z軸旋轉,也就是001,注意一定得是整形,不能是浮點型。

想要看是否能旋轉,要打開joint_state_publisher_gui,可以從launch中打開,也可以從終端中直接打開。

ros2 run joint_state_publisher_gui joint_state_publisher_gui

拖拽滾動條可改變攝像頭的Yaw

Randomize是隨機數,Center是回中(復位)


 <robot name="joint_demo">

  <material name="yellow">
    <color rgba="0.7 0.7 0 0.8" />
  </material>
  <material name="red">
    <color rgba="0.8 0.1 0.1 0.8" />
  </material>
  <link name="base_link">
    <visual>

        <geometry>
            <box size="0.5 0.3 0.1" />
        </geometry>
        <origin xyz="0 0 0" rpy="0 0 0" />
        <material name="yellow"/>
    </visual>
  </link>

  <link name="camera">
      <visual>
          <geometry>
              <box size="0.02 0.05 0.05" />
          </geometry>
          <origin xyz="0 0 0" rpy="0 0 0" />
          <material name="red" />
      </visual>
  </link>

  <joint name="camera2baselink" type="continuous">
      <parent link="base_link"/>
      <child link="camera" />

      <origin xyz="0.2 0 0.075" rpy="0 0 0" />
      <axis xyz="0 0 1" />
  </joint>

</robot>
joint_state_publisher

bug:Yaw不穩定,會一直回中。

其實是因為launch裡啟動的和終端裡啟動的衝突了。

解決方案:只啟動其中一個。

解決方案:直接在Launch裡啟動GUI版本的,這樣即可解決。(但是不建議)

建議方案:用非GUI版本的,因為以後,我們控制關節是用程序控制,而不是GUI控制。

如果只是想展示模型,用GUI,

如果想用程序控制,用普通版。

base_footprint

bug:機器人底盤半沉入地下

  <link name="base_footprint">
    <visual>
      <geometry>
          <sphere radius="0.001"/>
      </geometry>
    </visual>
  </link>
    <joint name="baselink2basefootprint" type="fixed">
      <parent link="base_footprint"/>
      <child link="base_link"/>
      <origin xyz="0.0 0.0 0.05"/>
    </joint>

Z的偏移量要填下沉底盤的距離,也就是整車底盤的一半。

修改參考座標系

其實這個優化,可以不做,影響不大。但是建議用有basefootprint版本的。


 <robot name="base_footprint_demo">

  <material name="yellow">
    <color rgba="0.7 0.7 0 0.8" />
  </material>
  <material name="red">
    <color rgba="0.8 0.1 0.1 0.8" />
  </material>

  <link name="base_footprint">
    <visual>
      <geometry>
          <sphere radius="0.001"/>
      </geometry>
    </visual>
  </link>

  <link name="base_link">
    <visual>

        <geometry>
            <box size="0.5 0.3 0.1" />
        </geometry>
        <origin xyz="0 0 0" rpy="0 0 0" />
        <material name="yellow"/>
    </visual>
  </link>

  <joint name="baselink2basefootprint" type="fixed">
    <parent link="base_footprint"/>
    <child link="base_link"/>
    <origin xyz="0.0 0.0 0.05"/>
  </joint>

  <link name="camera">
      <visual>
          <geometry>
              <box size="0.02 0.05 0.05" />
          </geometry>
          <origin xyz="0 0 0" rpy="0 0 0" />
          <material name="red" />
      </visual>
  </link>

  <joint name="camera2baselink" type="fixed">
      <parent link="base_link"/>
      <child link="camera" />

      <origin xyz="0.2 0 0.075" rpy="0 0 0" />
      <axis xyz="0 0 1" />
  </joint>

</robot>

練習

沒必要太深入練習,咱們可以直接用SolidWorks建模,更為友好。


 <robot name="exercise_demo">

  <material name="yellow">
    <color rgba="0.7 0.7 0 0.8" />
  </material>
  <material name="red">
    <color rgba="0.8 0.1 0.1 0.8" />
  </material>
  <material name="gray">
    <color rgba="0.2 0.2 0.2 0.8" />
  </material>

  <link name="base_footprint">
    <visual>
      <geometry>
          <sphere radius="0.001"/>
      </geometry>
    </visual>
  </link>

  <link name="base_link">
    <visual>

        <geometry>
            <box size="0.2 0.12 0.07" />
        </geometry>
        <origin xyz="0 0 0" rpy="0 0 0" />
        <material name="yellow"/>
    </visual>
  </link>

  <joint name="baselink2basefootprint" type="fixed">
    <parent link="base_footprint"/>
    <child link="base_link"/>
    <origin xyz="0.0 0.0 0.05"/>
  </joint>

  <link name="front_left_wheel">
      <visual>
          <geometry>
              <cylinder radius="0.025" length="0.02"/>
          </geometry>
          <origin xyz="0 0 0" rpy="1.57 0 0" />
          <material name="gray" />
      </visual>
  </link>

  <joint name="frontleftwheel2baselink" type="continuous">
      <parent link="base_link"/>
      <child link="front_left_wheel" />

      <origin xyz="0.075 0.06 -0.025" rpy="0 0 0" />
      <axis xyz="0 1 0" />
  </joint>

  <link name="front_right_wheel">
    <visual>
        <geometry>
            <cylinder radius="0.025" length="0.02"/>
        </geometry>
        <origin xyz="0 0 0" rpy="1.57 0 0" />
        <material name="gray" />
    </visual>
  </link>

  <joint name="frontrightwheel2baselink" type="continuous">
      <parent link="base_link"/>
      <child link="front_right_wheel" />

      <origin xyz="0.075 -0.06 -0.025" rpy="0 0 0" />
      <axis xyz="0 1 0" />
  </joint>

  <link name="back_left_wheel">
    <visual>
        <geometry>
            <cylinder radius="0.025" length="0.02"/>
        </geometry>
        <origin xyz="0 0 0" rpy="1.57 0 0" />
        <material name="gray" />
    </visual>
  </link>

  <joint name="backleftwheel2baselink" type="continuous">
    <parent link="base_link"/>
    <child link="back_left_wheel" />

    <origin xyz="-0.075 0.06 -0.025" rpy="0 0 0" />
    <axis xyz="0 1 0" />
  </joint>

  <link name="back_right_wheel">
    <visual>
        <geometry>
            <cylinder radius="0.025" length="0.02"/>
        </geometry>
        <origin xyz="0 0 0" rpy="1.57 0 0" />
        <material name="gray" />
    </visual>
  </link>

  <joint name="backrightwheel2baselink" type="continuous">
    <parent link="base_link"/>
    <child link="back_right_wheel" />

    <origin xyz="-0.075 -0.06 -0.025" rpy="0 0 0" />
    <axis xyz="0 1 0" />
  </joint>

</robot>

終端運行

ros2 launch cpp06_urdf display.launch.py model:=`ros2 pkg prefix --share cpp06_urdf`/urdf/urdf/demo05_exercise.urdf

URDF工具

在 ROS2 中,提供了一些URDF文件相關的工具,比如:

  • check_urdf命令可以檢查複雜的 urdf 文件是否存在語法問題;
  • urdf_to_graphviz命令可以查看 urdf 模型結構,顯示不同 link 的層級關係。

當然,要使用工具之前,請先安裝,安裝命令:sudo apt install liburdfdom-tools

check_urdf 語法檢查

進入urdf文件所屬目錄,調用:check_urdf urdf文件,如果不拋出異常,說明文件合法,否則非法。

示例,終端下進入功能包 cpp06_urdf 的 urdf/urdf 目錄,執行如下命令:

check_urdf demo05_exercise.urdf

urdf 文件如無異常,將顯示urdf中link的層級關係,如下圖所示:

否則將會給出錯誤提示。

演示錯誤,

urdf_to_graphviz 結構查看

進入urdf文件所屬目錄,調用:urdf_to_graphviz urdf文件,當前目錄下會生成 pdf 文件。

示例,終端下進入功能包 cpp06_urdf 的 urdf/urdf 目錄,執行如下命令:

urdf_to_graphviz demo05_exercise.urdf

當前目錄下,將生成以urdf中robot名稱命名的.pdf和.gv文件,打開pdf文件會顯示如下圖內容:

在上圖中會以樹形結構顯示link與joint的關係。

注意: 該工具以前名為urdf_to_graphiz現建議使用urdf_to_graphviz替代。

urdf_to_graphiz是歷史版本,已經被廢棄,建議用urdf_to_graphviz

黑色方框代表Link,藍色代表Joint,也會展示平移量和旋轉度等信息。

SW2URDF

solidworks簡介

SolidWorks是一種計算機輔助設計(CAD)和計算機輔助製造(CAM)軟件,由Dassault Systèmes SolidWorks Corp.開發。它主要用於工程設計和製造,可用於創建3D三維模型、進行裝配設計、進行工程分析和繪圖等。SolidWorks具有直觀的用戶界面和強大的功能,使工程師和設計師能夠快速而精確地設計複雜的零部件和裝配體。該軟件廣泛應用於機械、航空航天、汽車、醫療設備等行業,是工程設計領域的重要工具之一。

solidworks插件sw2urdf介紹

sw2urdf插件維基百科:

https://wiki.ros.org/sw\_urdf\_exporter

github下載鏈接:

https://github.com/ros/solidworks\_urdf\_exporter/releases

雖然github鏈接上寫著只支持到SW2021,但是目前發現最新版SW(2022、2024經測試)也是可以正常用的。

安裝sw2urdf

  1. 去github上下載sw2urdf插件(這個SW版本不用管,實測在後續的SW版本依然可用)

  1. 查看SW的安裝路徑

比如我的路徑"C:\Program Files\SOLIDWORKS Corp\SOLIDWORKS\"

  1. 打開插件安裝器,默認情況下會自動找到你的SW路徑進行安裝,如果沒有自動找到路徑安裝,那麼需要手動選取SW安裝路徑。

  1. 查看插件是否安裝成功

如圖這樣就是安裝成功了。

導出URDF與Meshes

  1. 如圖是一個標準的SW裝配體圖(大家也可以嘗試自己手擼一個,裡面沒有傳動裝置也可以)

這個過程看不太懂的話,可以參考一下古月居老師的視頻,結合本教程一起學習。 【SolidWorks模型導出urdf(古月居老師)-嗶哩嗶哩】https://www.bilibili.com/video/BV1Tx411o7rH

  1. 對joint關節建立基準軸

選擇關節的圓柱面或者其他面,對轉軸進行標定

如果建完基準軸,發現不能夠正常展示,那麼就打開該選項。

建完所有系後

  1. 打開export as URDF選項

  1. 需要先選擇base_link基底剛體

這裡的Global Origin可能需要自己選擇,就選擇Origin_Global即可,但是一般選擇automatically Generate即可。

  1. 因為他的base_link通過一個joint連接了一個child_link,所以,這個選項要填1。

咱們把base_link連接的關節叫joint1,把joint1連接的更上面的剛體叫做link1,

然後參考基準軸選擇剛才咱們在這個關節處建立的基準軸1,然後joint type關節類型選擇revolute(有限位的關節,只有這樣,機器人關節才可以正常運動)

這裡的參考座標系系統可以選擇automatically Generate,也可以選擇Origin_Joint1。

然後再在link1上加一,創建link2。

後面的link3,link4添加步驟是一樣的,不再詳細展示。

  1. 點擊preview and export

  1. 對關節進行限位設置

coordinates是座標系,axis是軸,如果這裡Axis座標顯示錯誤,那麼說明Coordinates或者Axis選擇錯了,請手動選擇正確的座標系或者基準軸。

如果轉軸生成錯誤,比如都是000,可以自己填一下。

比如這裡是X軸,可以把左輪全填-100,右輪100(這裡,僅供參考,chatgpt提供的解決方案)

  1. 查看矩陣,並點導出urdf與meshes

這樣就成功生成ROS1的WS了,將WS移動至Linux系統。

將ROS1的WS轉化為ROS2的WS

  1. 先將ROS1的WS移動到Linux系統上

  1. 新建一個ROS2的WS

mkdir -p ros2_4axisrobot_ws/src
cd ros2_4axisrobot_ws/
colcon build
cd src
ros2 pkg create cpp01_urdf --build-type ament_cmake
cd ..
code .
  1. 完善WS的目錄

在./src/cpp01_urdf路徑下創建launch、meshes、rviz、urdf等等文件夾,在urdf文件夾下再新建urdf和xacro文件夾

  1. 複製ROS1的WS裡的URDF和Meshes到ROS2的WS中

  1. 對CMake和package進行配置

在CMake中請添加

install(
  DIRECTORY launch urdf rviz meshes
  DESTINATION share/${PROJECT_NAME}  
)

在Package.xml中請添加

  <exec_depend>rviz2</exec_depend>
  <exec_depend>xacro</exec_depend>
  <exec_depend>robot_state_publisher</exec_depend>
  <exec_depend>joint_state_publisher</exec_depend>
  <exec_depend>ros2launch</exec_depend>

CMakeLists.txt

cmake_minimum_required(VERSION 3.8)
project(cpp01_urdf)

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()

# find dependencies
find_package(ament_cmake REQUIRED)

# uncomment the following section in order to fill in

# further dependencies manually.

# find_package(<dependency> REQUIRED)

install(
  DIRECTORY launch urdf rviz meshes
  DESTINATION share/${PROJECT_NAME}  
)

if(BUILD_TESTING)
  find_package(ament_lint_auto REQUIRED)

  # the following line skips the linter which checks for copyrights

  # comment the line when a copyright and license is added to all source files
  set(ament_cmake_copyright_FOUND TRUE)

  # the following line skips cpplint (only works in a git repo)

  # comment the line when this package is in a git repo and when

  # a copyright and license is added to all source files
  set(ament_cmake_cpplint_FOUND TRUE)
  ament_lint_auto_find_test_dependencies()
endif()

ament_package()

package.xml

<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
  <name>cpp01_urdf</name>
  <version>0.0.0</version>
  <description>TODO: Package description</description>
  <maintainer email="tungchiahui@gmail.com">tungchiahui</maintainer>
  <license>TODO: License declaration</license>

  <buildtool_depend>ament_cmake</buildtool_depend>

  <exec_depend>rviz2</exec_depend>
  <exec_depend>xacro</exec_depend>
  <exec_depend>robot_state_publisher</exec_depend>
  <exec_depend>joint_state_publisher</exec_depend>
  <exec_depend>ros2launch</exec_depend>

  <test_depend>ament_lint_auto</test_depend>
  <test_depend>ament_lint_common</test_depend>

  <export>
    <build_type>ament_cmake</build_type>
  </export>
</package>
  1. 對launch文件進行編寫

詳細過程請看URDF有關Launch的核心優化那一節

from launch import LaunchDescription
from launch_ros.actions import Node

# 封装终端指令相关类

# from launch.actions import ExecuteProcess

# from launch.substitutions import FindExecutable

# 参数声明与获取
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration

# 文件包含相关

# from launch.actions import IncludeLaunchDescription

# from launch.launch_description_sources import PythonLaunchDescriptionSource

# 分组相关

# from launch_ros.actions import PushRosNamespace

# from launch.actions import GroupAction

# 事件相关

# from launch.event_handlers import OnProcessStart,OnProcessExit

# from launch.actions import ExecuteProcess,RegisterEventHandler,LogInfo

# 获取功能包下share目录或路径
from ament_index_python.packages import get_package_share_directory

from launch_ros.parameter_descriptions import ParameterValue
from launch.substitutions import Command

import os

cpp01_urdf_dir = get_package_share_directory("cpp01_urdf")

default_model_path = os.path.join(cpp01_urdf_dir,"urdf/urdf","4axisrobot.urdf")
default_rviz_path = os.path.join(cpp01_urdf_dir,"rviz","urdf.rviz")

model = DeclareLaunchArgument(name="model",default_value=default_model_path)

p_value = ParameterValue(Command(["xacro ",LaunchConfiguration("model")]))
robot_state_pub = Node(
    package="robot_state_publisher",
    executable="robot_state_publisher",
    parameters=[{"robot_description":p_value}]
)

# 关节信息节点(建议)

# joint_state_pub = Node(

#     package="joint_state_publisher",

#     executable="joint_state_publisher"

# )

# 关节信息节点图形界面
joint_state_pub = Node(
    package="joint_state_publisher_gui",
    executable="joint_state_publisher_gui"
)

rviz2 = Node(
    package="rviz2",
    executable="rviz2",

#    arguments=["-d",get_package_share_directory("cpp06_urdf") + "/rviz/urdf.rviz"]
    arguments=["-d",default_rviz_path]

    )

def generate_launch_description():
    return LaunchDescription([model,robot_state_pub,joint_state_pub,rviz2])
  1. 對URDF文件進行修改

修改的內容主要有兩項,一個是meshes的路徑,一個是刪掉易引起報錯的註釋。

主要由於ROS1的WS和ROS2的WS路徑不同,所以,我們只需要修改有關路徑的內容,比如說Meshes的路徑

package://一般是share目錄,所以我們需要從cpp01_urdf這一級開始寫(share目錄需要編譯後才會顯示)

然後按Ctrl+H

然後還需要刪除掉urdf文件剛開始的註釋,否則也會報錯

如下是替換完畢的URDF文件:

<?xml version="1.0" encoding="utf-8"?>
<robot
  name="4axisrobot">
  <link
    name="base_link">
    <inertial>
      <origin
        xyz="-0.0819771145953119 -0.0394328123681598 -0.0605612328464761"
        rpy="0 0 0" />
      <mass
        value="7.43807257011133" />
      <inertia
        ixx="0.33173827700719"
        ixy="-1.91350798633049E-05"
        ixz="-2.38586363043925E-05"
        iyy="0.510937907009661"
        iyz="1.14706407748075E-05"
        izz="0.66107272632796" />
    </inertial>
    <visual>
      <origin
        xyz="0 0 0"
        rpy="0 0 0" />
      <geometry>
        <mesh
          filename="package://cpp01_urdf/meshes/base_link.STL" />
      </geometry>
      <material
        name="">
        <color
          rgba="0.792156862745098 0.819607843137255 0.933333333333333 1" />
      </material>
    </visual>
    <collision>
      <origin
        xyz="0 0 0"
        rpy="0 0 0" />
      <geometry>
        <mesh
          filename="package://cpp01_urdf/meshes/base_link.STL" />
      </geometry>
    </collision>
  </link>
  <link
    name="link1">
    <inertial>
      <origin
        xyz="0.19141 -0.050676 -0.030057"
        rpy="0 0 0" />
      <mass
        value="21.822" />
      <inertia
        ixx="0.50464"
        ixy="0.13295"
        ixz="0.087353"
        iyy="0.59136"
        iyz="-0.066053"
        izz="0.64081" />
    </inertial>
    <visual>
      <origin
        xyz="0 0 0"
        rpy="0 0 0" />
      <geometry>
        <mesh
          filename="package://cpp01_urdf/meshes/link1.STL" />
      </geometry>
      <material
        name="">
        <color
          rgba="0.79216 0.81961 0.93333 1" />
      </material>
    </visual>
    <collision>
      <origin
        xyz="0 0 0"
        rpy="0 0 0" />
      <geometry>
        <mesh
          filename="package://cpp01_urdf/meshes/link1.STL" />
      </geometry>
    </collision>
  </link>
  <joint
    name="joint1"
    type="revolute">
    <origin
      xyz="-0.081947 -0.039447 0.11191"
      rpy="0.40394 -1.5708 0" />
    <parent
      link="base_link" />
    <child
      link="link1" />
    <axis
      xyz="1 0 0" />
    <limit
      lower="-3.14"
      upper="3.14"
      effort="100"
      velocity="1" />
  </joint>
  <link
    name="link2">
    <inertial>
      <origin
        xyz="0.00711598521454873 0.430460941882332 -0.181074256126024"
        rpy="0 0 0" />
      <mass
        value="30.6115349647897" />
      <inertia
        ixx="2.84056101478768"
        ixy="0.00176700425270318"
        ixz="-0.000605507690039326"
        iyy="0.803420593303961"
        iyz="0.881921487314161"
        izz="2.25142021571439" />
    </inertial>
    <visual>
      <origin
        xyz="0 0 0"
        rpy="0 0 0" />
      <geometry>
        <mesh
          filename="package://cpp01_urdf/meshes/link2.STL" />
      </geometry>
      <material
        name="">
        <color
          rgba="0.792156862745098 0.819607843137255 0.933333333333333 1" />
      </material>
    </visual>
    <collision>
      <origin
        xyz="0 0 0"
        rpy="0 0 0" />
      <geometry>
        <mesh
          filename="package://cpp01_urdf/meshes/link2.STL" />
      </geometry>
    </collision>
  </link>
  <joint
    name="joint2"
    type="revolute">
    <origin
      xyz="0.35141 -0.28119 0.13918"
      rpy="-2.0566 1.1982 1.5708" />
    <parent
      link="link1" />
    <child
      link="link2" />
    <axis
      xyz="-1 0 0" />
    <limit
      lower="-3.14"
      upper="3.14"
      effort="100"
      velocity="1" />
  </joint>
  <link
    name="link3">
    <inertial>
      <origin
        xyz="0.00112887115421612 0.0366346915274407 0.31720330934553"
        rpy="0 0 0" />
      <mass
        value="32.192726667578" />
      <inertia
        ixx="4.01515983126431"
        ixy="0.0013193752174692"
        ixz="0.0113529938102155"
        iyy="3.86196311673823"
        iyz="-0.336440022491883"
        izz="0.399977905540088" />
    </inertial>
    <visual>
      <origin
        xyz="0 0 0"
        rpy="0 0 0" />
      <geometry>
        <mesh
          filename="package://cpp01_urdf/meshes/link3.STL" />
      </geometry>
      <material
        name="">
        <color
          rgba="0.792156862745098 0.819607843137255 0.933333333333333 1" />
      </material>
    </visual>
    <collision>
      <origin
        xyz="0 0 0"
        rpy="0 0 0" />
      <geometry>
        <mesh
          filename="package://cpp01_urdf/meshes/link3.STL" />
      </geometry>
    </collision>
  </link>
  <joint
    name="joint3"
    type="revolute">
    <origin
      xyz="0.3765 0.82481 -0.49591"
      rpy="2.5802 0 0" />
    <parent
      link="link2" />
    <child
      link="link3" />
    <axis
      xyz="1 0 0" />
    <limit
      lower="-3.14"
      upper="3.14"
      effort="100"
      velocity="1" />
  </joint>
  <link
    name="link4">
    <inertial>
      <origin
        xyz="0.162516620557178 -0.213844847423599 3.38133504529381E-07"
        rpy="0 0 0" />
      <mass
        value="12.1646007734167" />
      <inertia
        ixx="0.288208127188405"
        ixy="0.00080326659836716"
        ixz="8.72489392803044E-07"
        iyy="0.107000706575729"
        iyz="-1.48935044230573E-07"
        izz="0.295147863994059" />
    </inertial>
    <visual>
      <origin
        xyz="0 0 0"
        rpy="0 0 0" />
      <geometry>
        <mesh
          filename="package://cpp01_urdf/meshes/link4.STL" />
      </geometry>
      <material
        name="">
        <color
          rgba="0.792156862745098 0.819607843137255 0.933333333333333 1" />
      </material>
    </visual>
    <collision>
      <origin
        xyz="0 0 0"
        rpy="0 0 0" />
      <geometry>
        <mesh
          filename="package://cpp01_urdf/meshes/link4.STL" />
      </geometry>
    </collision>
  </link>
  <joint
    name="joint4"
    type="revolute">
    <origin
      xyz="0 0.070837 0.99728"
      rpy="0.82315 -1.5708 0" />
    <parent
      link="link3" />
    <child
      link="link4" />
    <axis
      xyz="1 0 0" />
    <limit
      lower="-3.14"
      upper="3.14"
      effort="100"
      velocity="1" />
  </joint>
</robot>
  1. 編譯
colcon build --packages-select cpp01_urdf

  1. 更新終端環境
source install/setup.bash

  1. 運行Launch
ros2 launch cpp01_urdf display.launch.py

  1. 在Rviz2中添加插件以及基本配置

  1. 使用joint_state_publisher_gui可以調關節的角度

  1. 保存rviz2的配置到rviz文件夾中

首先點擊rviz2的菜單欄上的File,然後選擇Save Config as

可以去本節簡介看效果視頻

注意事項(僅供參考,ChatGPT的解決方案)

座標系1

輪子要用continuous的關節,並且儘量自己選座標系,要求從後面看車的話,左是Y,前是X,上是Z。所有的座標系都要求。

可以先讓他幫你自動生成一個,你再去修改座標系,要簡單一些。

座標系讓車躺著

如果你的車在rviz2裡是側著睡覺的,那麼:

哈哈,沒錯,這種“整車側著躺屍”的情況 90%就是 STL 的座標軸方向搞錯了 。很多 CAD 工具導出的 STL 模型默認是:

  • Z 軸朝前(例如 SolidWorks:Z朝前,Y朝上,用右手座標系推X座標系位置,從車屁股後面看是朝左。)
  • 或者 Y 軸朝上(例如 Blender)

而 ROS/URDF 的座標系統是:

  • X 向前(前進方向)
  • Y 向左(平移方向)
  • Z 向上(重力方向)
車直走變旋轉,旋轉變直走

那說明你的轉軸有問題。

如果向前走成了左轉,那麼說明左輪的轉軸錯了,少了個符號,加上就行了。

比如這個010,你應該改成0-10

xacro

場景、作用與概念

場景

前面 URDF 文件構建機器人模型的過程中,存在若干問題。

問題1:在設計關節的位置時,需要按照一定的規則計算,規則是固定的,但是在 URDF 中依賴於人工計算,存在不便,容易計算失誤,且當某些參數發生改變時,還需要重新計算。

問題2:URDF中的部分內容是高度重複的,比如車輪的設計實現,不同輪子只是部分參數不同,形狀、顏色、翻轉量都是一致的,在實際應用中,構建複雜的機器人模型時,更是易於出現高度重複的設計,按照一般的編程思想涉及到重複代碼應該考慮封裝、複用,但是在之前的URDF文件中並沒有相關操作。

......

如果在一般編程語言中遇到類似問題,我們可以通過變量結合函數解決。對應的,在 ROS 中也給出了類似編程的優化方案,該方案稱之為: Xacro(可以理解為urdf2.0)

概念

Xacro 是 XML Macros 的縮寫,Xacro 是一種 XML 宏語言,是可編程的 XML。

Xacro 可以聲明變量,可以通過數學運算求解;可以使用流程控制控制執行順序;還可以通過宏封裝(可以想成函數)、複用功能,從而提高代碼複用率以及程序的安全性。

作用

較之於純粹的 URDF 實現,可以編寫更安全、精簡、易讀性更強的機器人模型文件,且可以提高編寫效率。

快速體驗

先安裝xacro

#humble版本
sudo apt install ros-humble-xacro
#jazzy版本
sudo apt install ros-jazzy-xacro

1.需求

使用xacro優化 6.4.4 URDF練習 中的小車底盤實現,需要使用變量封裝車輛參數,並使用 xacro 宏封裝輪子重複的代碼並調用宏創建四個輪子(注意: 在此,演示 xacro 的基本使用,不必要生成合法的 URDF )。

2.實現

功能包cpp06_urdf的urdf/xacro目錄下,新建xacro文件demo01_helloworld.urdf.xacro,並編輯文件,輸入如下內容:

<robot name="mycar" xmlns:xacro="http://wiki.ros.org/xacro">

    <xacro:property name="wheel_radius" value="0.025" />
    <xacro:property name="wheel_length" value="0.02" />
    <xacro:property name="PI" value="3.1415927" />

    <xacro:macro name="wheel_func" params="wheel_name" >
        <link name="${wheel_name}_wheel">
            <visual>
                <geometry>
                    <cylinder radius="${wheel_radius}" length="${wheel_length}" />
                </geometry>

                <origin xyz="0 0 0" rpy="${PI / 2} 0 0" />

                <material name="wheel_color">
                    <color rgba="0 0 0 0.3" />
                </material>
            </visual>
        </link>
    </xacro:macro>
    <xacro:wheel_func wheel_name="left_front"/>
    <xacro:wheel_func wheel_name="left_back"/>
    <xacro:wheel_func wheel_name="right_front"/>
    <xacro:wheel_func wheel_name="right_back"/>
</robot>

宏類似函數

params類似入口參數

子標籤類似函數體

終端下進入當前文件所述目錄,輸入如下指令:

cd src/cpp06_urdf/urdf/xacro/
xacro demo01_helloworld.urdf.xacro
#或者
ros2 run xacro xacro demo01_helloworld.urdf.xacro

終端將會輸出如下內容(以下內容是純urdf):

<?xml version="1.0" ?>

<robot name="mycar">
  <link name="left_front_wheel">
    <visual>
      <geometry>
        <cylinder length="0.02" radius="0.025"/>
      </geometry>
      <origin rpy="1.57079635 0 0" xyz="0 0 0"/>
      <material name="wheel_color">
        <color rgba="0 0 0 0.3"/>
      </material>
    </visual>
  </link>
  <link name="left_back_wheel">
    <visual>
      <geometry>
        <cylinder length="0.02" radius="0.025"/>
      </geometry>
      <origin rpy="1.57079635 0 0" xyz="0 0 0"/>
      <material name="wheel_color">
        <color rgba="0 0 0 0.3"/>
      </material>
    </visual>
  </link>
  <link name="right_front_wheel">
    <visual>
      <geometry>
        <cylinder length="0.02" radius="0.025"/>
      </geometry>
      <origin rpy="1.57079635 0 0" xyz="0 0 0"/>
      <material name="wheel_color">
        <color rgba="0 0 0 0.3"/>
      </material>
    </visual>
  </link>
  <link name="right_back_wheel">
    <visual>
      <geometry>
        <cylinder length="0.02" radius="0.025"/>
      </geometry>
      <origin rpy="1.57079635 0 0" xyz="0 0 0"/>
      <material name="wheel_color">
        <color rgba="0 0 0 0.3"/>
      </material>
    </visual>
  </link>
</robot>

顯然的,通過xacro我們方便的實現了代碼複用。

語法
1.  簡介

![](https://cdn.tungchiahui.cn/tungwebsite/assets/images/2023/12/30/image1611.webp)

  xacro 提供了可編程接口,類似於計算機語言,包括變量聲明調用、函數聲明與調用等語法實現。在使用 xacro 生成 urdf 時,根標籤`robot`中**必須**包含命名空間聲明:`xmlns:xacro="``http://wiki.ros.org/xacro``"`。

   **變量**

  變量用於封裝 URDF 中的一些字段,比如: PAI 值,小車的尺寸,輪子半徑 ....,變量的基本使用語法包括變量定義、變量調用、變量運算等。

  1.1變量定義

  語法格式:

```xml
<xacro:property name="变量名" value="变量值" />
```

  示例:

```xml
<xacro:property name="PI" value="3.1416"/>
<xacro:property name="wheel_radius" value="0.025"/>
<xacro:property name="wheel_length" value="0.02"/>
```

  1.2變量調用

  語法格式:

```xml
${变量名}
```

  示例:

```xml
<geometry>
    <cylinder radius="${wheel_radius}" length="${wheel_length}" />
</geometry>
```

  1.3變量運算

  語法格式:

```xml
${数学表达式}
```

  示例:

```xml
<origin xyz="0 0 0" rpy="${PI / 2} 0 0" />
```

```xml
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="demo2_pro">

<xacro:property name="num1" value="10"/>
<xacro:property name="num2" value="20"/>

<car length="${num1}" width="${num2}"/>

<sum value="${num1 + num2}"/>
</robot>
```

![](https://cdn.tungchiahui.cn/tungwebsite/assets/images/2023/12/30/image1612.webp)

   **宏**

  類似於函數實現,提高代碼複用率,優化代碼結構,提高安全性。宏的基本使用語法包括宏的定義與調用。

  2.1宏定義

  語法格式:

```xml
<xacro:macro name="宏名称" params="参数列表(多参数之间使用空格分隔)">
    .....
    参数调用格式: ${参数名}
</xacro:macro>
```

  示例:

```xml
<xacro:macro name="wheel_func" params="wheel_name" >
    <link name="${wheel_name}_wheel">
        <visual>
            <geometry>
                <cylinder radius="${wheel_radius}" length="${wheel_length}" />
            </geometry>

            <origin xyz="0 0 0" rpy="${PI / 2} 0 0" />

            <material name="wheel_color">
                <color rgba="0 0 0 0.3" />
            </material>
        </visual>
    </link>
</xacro:macro>
```

  2.2宏調用

  語法格式:

```xml
<xacro:宏名称 参数1=xxx 参数2=xxx/>
```

  示例:

```xml
<xacro:wheel_func wheel_name="left_front"/>
<xacro:wheel_func wheel_name="left_back"/>
<xacro:wheel_func wheel_name="right_front"/>
<xacro:wheel_func wheel_name="right_back"/>
```

```xml
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="demo3_func">

    <xacro:macro name="get_sum" params="num1 num2">
        <sum value="${num1 + num2}"/>
    </xacro:macro>

    <xacro:get_sum num1="20" num2="30"/>
    <xacro:get_sum num1="70" num2="30"/>

</robot>
```

![](https://cdn.tungchiahui.cn/tungwebsite/assets/images/2023/12/30/image1613.webp)

   **文件**

機器人由多部件組成,不同部件可能封裝為單獨的 xacro 文件,最後再將不同的文件集成,組合為完整機器人,可以使用文件包含實現。

語法格式:

<xacro:include filename="其他xacro文件" />

示例:

<robot name="car" xmlns:xacro="http://wiki.ros.org/xacro">
      <xacro:include filename="car_base.xacro" />
      <xacro:include filename="car_camera.xacro" />
      <xacro:include filename="car_laser.xacro" />
</robot>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="demo4_include">
    <xacro:include filename="demo02_base_pro.urdf.xacro"/>
    <xacro:include filename="demo03_base_func.urdf.xacro"/>
</robot>

但不建議這樣,建議父級xacro和子級xacro使用同樣的name。

練習

框架

1.需求

使用xacro創建一個四輪機器人模型,該模型底盤可以參考 6.4.4 URDF練習 中的實現,並且在底盤之上添加了相機與激光雷達。相機與激光雷達的尺寸參數、安裝位置可自定義。

2.實現分析

需求中的機器人模型是由底盤、攝像頭和雷達三部分組成的,那麼可以將每一部分都封裝進一個xacro文件,最後再通過xacro文件包含組織成一個完整的機器人模型。

3.實現

功能包cpp06_urdf的urdf/xacro目錄下,新建多個xacro文件,分別為:

  • car.urdf.xacro:用於包含不同機器人部件對應的xacro文件;
  • car_base.urdf.xacro:描述機器人底盤的xacro文件;
  • car_camera.urdf.xacro:描述攝像頭的xacro文件;
  • car_laser.urdf.xacro:描述雷達的xacro文件。

編輯car.urdf.xacro文件,輸入如下內容:

<robot name="car" xmlns:xacro="http://wiki.ros.org/xacro">
    <xacro:include filename="car_base.urdf.xacro"/>
    <xacro:include filename="car_camera.urdf.xacro"/>
    <xacro:include filename="car_laser.urdf.xacro"/>
</robot>
車體

編輯car_base.urdf.xacro文件,輸入如下內容:

<robot xmlns:xacro="http://wiki.ros.org/xacro">

    <xacro:property name="PI" value="3.1416"/>

    <xacro:property name="base_link_x" value="0.2"/>
    <xacro:property name="base_link_y" value="0.12"/>
    <xacro:property name="base_link_z" value="0.07"/>

    <xacro:property name="distance" value="0.015"/>

    <xacro:property name="wheel_radius" value="0.025"/>
    <xacro:property name="wheel_length" value="0.02"/>

    <material name="yellow">
        <color rgba="0.7 0.7 0 0.8" />
    </material>
    <material name="red">
        <color rgba="0.8 0.1 0.1 0.8" />
    </material>
    <material name="gray">
        <color rgba="0.2 0.2 0.2 0.95" />
      </material>

    <link name="base_footprint">
        <visual>
            <geometry>
                <sphere radius="0.001"/>
            </geometry>
        </visual>
    </link>

    <link name="base_link">
        <visual>

            <geometry>
                <box size="${base_link_x} ${base_link_y} ${base_link_z}" />
            </geometry>
            <origin xyz="0 0 0" rpy="0 0 0" />
            <material name="yellow"/>
        </visual>
    </link>
    <joint name="baselink2basefootprint" type="fixed">
        <parent link="base_footprint"/>
        <child link="base_link"/>
        <origin xyz="0.0 0.0 ${distance + base_link_z / 2}"/>
    </joint>

    <xacro:macro name="wheel_func" params="wheel_name is_front is_left" >
        <link name="${wheel_name}_wheel">
            <visual>
                <geometry>
                    <cylinder radius="${wheel_radius}" length="${wheel_length}" />
                </geometry>
                <origin xyz="0 0 0" rpy="${PI / 2} 0 0" />
                <material name="gray"/>
            </visual>
        </link>
        <joint name="${wheel_name}2baselink" type="continuous">
            <parent link="base_link"  />
            <child link="${wheel_name}_wheel" />
            <origin xyz="${(base_link_x / 2 - wheel_radius) * is_front} ${base_link_y / 2 * is_left} ${(base_link_z / 2 + distance - wheel_radius) * -1}" rpy="0 0 0" />
            <axis xyz="0 1 0" />
        </joint>
    </xacro:macro>

    <xacro:wheel_func wheel_name="left_front" is_front="1" is_left="1" />
    <xacro:wheel_func wheel_name="left_back" is_front="-1" is_left="1" />
    <xacro:wheel_func wheel_name="right_front" is_front="1" is_left="-1" />
    <xacro:wheel_func wheel_name="right_back" is_front="-1" is_left="-1" />
</robot>
添加攝像頭

編輯car_camera.urdf.xacro文件,輸入如下內容:


<robot xmlns:xacro="http://wiki.ros.org/xacro">

    <xacro:property name="camera_x" value="0.012" /> 
    <xacro:property name="camera_y" value="0.05" /> 
    <xacro:property name="camera_z" value="0.01" /> 
    <xacro:property name="camera_joint_x" value="${base_link_x / 2 - camera_x / 2}" /> 
    <xacro:property name="camera_joint_y" value="0.0" /> 
    <xacro:property name="camera_joint_z" value="${base_link_z / 2 + camera_z / 2}" /> 

    <link name="camera">
        <visual>
            <geometry>
                <box size="${camera_x} ${camera_y} ${camera_z}" />
            </geometry>
            <origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0" />
            <material name="red" />
        </visual>
    </link>

    <joint name="camera2baselink" type="fixed">
        <parent link="base_link" />
        <child link="camera" />
        <origin xyz="${camera_joint_x} ${camera_joint_y} ${camera_joint_z}" />
    </joint>
</robot>
添加雷達

編輯car_laser.urdf.xacro文件,輸入如下內容:


<robot xmlns:xacro="http://wiki.ros.org/xacro">

    <material name="blue">
        <color rgba="0.0 0.0 0.4 0.95" />
    </material>

    <xacro:property name="laser_length" value="0.03" /> 
    <xacro:property name="laser_radius" value="0.03" /> 
    <xacro:property name="laser_joint_x" value="0.0" /> 
    <xacro:property name="laser_joint_y" value="0.0" /> 
    <xacro:property name="laser_joint_z" value="${base_link_z / 2 + laser_length / 2}" /> 

    <link name="laser">
        <visual>
            <geometry>
                <cylinder radius="${laser_radius}" length="${laser_length}" />
            </geometry>
            <origin xyz="0.0 0.0 0.0" rpy="0.0 0.0 0.0" />
            <material name="blue" />
        </visual>
    </link>

    <joint name="laser2baselink" type="fixed">
        <parent link="base_link" />
        <child link="laser" />
        <origin xyz="${laser_joint_x} ${laser_joint_y} ${laser_joint_z}" />
    </joint>
</robot>
執行

編譯後,工作空間終端下調用如下命令執行:


# ROS Humble
ros2 launch cpp06_urdf display.launch.py model:=ros2 pkg prefix --share cpp06_urdf/urdf/xacro/car.urdf.xacro
#ROS Jazzy
colcon build
source install/setup.bash
ros2 run xacro xacro $(ros2 pkg prefix cpp06_urdf)/share/cpp06_urdf/urdf/xacro/car.urdf.xacro -o ./src/cpp06_urdf/urdf/urdf/car.urdf
ros2 launch cpp06_urdf display.launch.py model:=./src/cpp06_urdf/urdf/urdf/car.urdf

命令執行後,rviz2 中可以顯示與需求類似的機器人模型。

小結

目前只是空殼,激光雷達和攝像頭以及輪子還都是空殼,到進階聯繫中,才可以實現作用。

音乐页