1. 程式人生 > 其它 >ROS安裝教程和入門筆記

ROS安裝教程和入門筆記

大四做完畢設閒著沒事打算學習一下無人機模擬,然後就開始瞭解ROS,研究生階段涉及模擬時或許可以用上。學習過程中主要記錄了用到的教程以及遇到的一些坑,限於篇幅沒有完全記錄教程中的所有細節,所以這篇文章的使用方式應該是根據給出的教程參考連結開啟原來的教程,按照原來的教程學習,在學習進行到某一步遇到問題時,可以看看這篇文章在這一步是否遇到了相同的問題,如果有則方便參考,沒有隻能自行探索解決辦法了。希望這篇文章能給其他人入門提供一些幫助。

目錄

1. ROS中文入門教程

1.1安裝ROS

安裝時我參考的:一篇部落格

安裝和配置環境一般都比較麻煩,所謂從入門到放棄應該就是從這裡開始的吧,只是環境配置就花費了一兩天時間。在安裝ROS的時候我用國內源提示找不到包,所以安裝ROS的時候我換回Ubuntu官方源了(換回方法),但是在國內訪問國外的網路特別慢而且容易出問題(懂得都懂),導致有些中途有些包沒有下載下來而報錯,根據終端提示用fixmissing引數再次嘗試,一直嘗試到所有包下載完畢就行。。。。

安裝完畢後需要執行 sudo rosdep init,可能因為網路問題失敗,解決辦法參考這個:解決sudo rosdep init問題

解決完上面的init問題,再執行rosdep update,同樣可能因為網路問題失敗,如果遇到問題參考這個:解決rosdep update問題

1.2 ROS基本知識

學習時參考的:ROS官方中文入門教程

這個官方的教程解釋的很清楚,所以學習這一部分時幾乎沒有遇到錯誤。看的時候忘記記筆記了,後面有機會複習的時候補充。

2. TF學習

興致沖沖地以為可以開始對PR2機器人進行模擬了,但是發現教程要求需要首先了解物理模擬軟體Gazebo教程,點開Gazebo教程,發現在Gazebo模擬軟體裡面建立模型需要通過URDF模型結構進行建立,然後點開URDF教程,又發現建立一個模型涉及很多相對座標系,所以需要先了解TF包方便座標變換。感覺自己被套娃了:),無奈先從TF包開始。

學習時參考的:TF教程

2.1 TF示例

教程首先給出了需要建立工作空間:

source /opt/ros/noetic/setup.bash
mkdir -p ~/tutorial_ws/src
cd ~/tutorial_ws
catkin_init_workspace src
catkin_make
source devel/setup.bash

然後給出了一個示例,執行示例roslaunch turtle_tf2 turtle_tf2_demo.launch的時候遇到了process has died問題,看錯誤提示發現有兩個原因,第一個原因是import yaml失敗,所以需要pip install pyyaml

另一個原因是python版本問題,需要把下面資料夾的所有py檔案第一行的#!/usr/bin/env python替換為#!/usr/bin/env python3

2.2 寫一個Static Broadcaster(Python)

catking_create_pkg建立一個名為learning_tf2的軟體包,需要給出依賴。命令如下:

catkin_create_pkg learning_tf2 tf2 tf2_ros roscpp rospy turtlesim

進入目錄:

roscd learning_tf2

建立節點資料夾:

mkdir nodes

在節點資料夾建立py檔案:static_turtle_tf2_broadcaster.py,直接複製教程中給出的程式碼

新增執行許可權,但是好像這裡py檔案路徑跟上面建立的py檔案路徑不一致,所以我手動把py檔案放到這行程式碼對應的位置了,像這樣:

然後再執行命令新增執行許可權:

chmod +x ~/tutorial_ws/src/learning_tf2/src/nodes/static_turtle_tf2_broadcaster.py

回到工作空間資料夾:cd ~/tutorial_ws,教程中直接給出了編譯命令,但是下面這個CMakeLIst.txt還沒設定好,我記得ROS中文官方教程裡面是需要先設定後編譯的,所以這裡也需要設定一下,像下面這樣把剛剛py檔案的路徑新增進去:


然後執行命令編譯:

catkin_make

之後再根據教程的步驟,完成topic釋出和檢視。

. devel/setup.bash
roscore
rosrun learning_tf2 static_turtle_tf2_broadcaster.py mystaticturtle 0 0 1 0 0 0
rostopic echo /tf_static

2.3 寫一個Broadcaster(python)

在剛才的nodes資料夾新增檔案turtle_tf2_broadcaster.py,複製原教程的程式碼。
新增許可權

chmod +x nodes/turtle_tf2_broadcaster.py

建立start_demo.launch檔案,在下面這個資料夾,程式碼複製原教程的:

Build剛剛的程式碼檔案,第一步先修改這個CMakeList.txt,166行是新加的,165行是上面2.2節那裡加的。還有下面那一段取消註釋,然後加上剛剛建立的launch檔案:



然後cd到~/tf_tutorial_ws:catkin_make開始編譯。

執行:roslaunch learning_tf2 start_demo.launch
另一個終端執行:rosrun tf tf_echo /world /turtle1檢視結果

2.4 寫一個Listener(Python)

根據原教程,建立檔案nodes/turtle_tf2_listener.py
新增執行許可權:

chmod +x nodes/turtle_tf2_listener.py

修改之前的start_demo.launch,新增一個node:

  <launch>
    ...
    <node pkg="learning_tf2" type="turtle_tf2_listener.py" 
          name="listener" output="screen"/>
  </launch>

重新編譯:注意修改CMakeList.txt,標明新的py檔案的位置。

執行並檢視結果:

roslaunch learning_tf2 start_demo.launch

2.5 新增一個座標系Frame(Python)

這一節建立了通過Broadcaster建立了一個固定的和時變的座標系,方法與之前建立Broadcaster一樣。

2.6 瞭解tf2和時間

    while not rospy.is_shutdown():
        try:
            trans = tfBuffer.lookup_transform('turtle2',
                                              'carrot1',
                                              rospy.Time.now(),
                                              rospy.Duration(1.0))

2.7 跨時間的tf2

        try:
            past = rospy.Time.now() - rospy.Duration(5.0)
            trans = tfBuffer.lookup_transform(turtle_name, 'carrot1', past, rospy.Duration(1.0))
        except (tf2_ros.LookupException, tf2_ros.ConnectivityException, tf2_ros.ExtrapolationException):

這是根據5秒前carrot1相對於5秒前turtle2的位置對現在進行控制,因為從現在的turtle2看起來是混亂的。

        try:
            past = rospy.Time.now() - rospy.Duration(5.0)
            trans = tfBuffer.lookup_transform_full(
                target_frame=turtle_name,
                target_time=rospy.Time.now(),
                source_frame='carrot1',
                source_time=past,
                fixed_frame='world',
                timeout=rospy.Duration(1.0)
            )

這是根據5秒前carrot1相對於現在turtle2的位置對現在進行控制,因此現在的turtle2追蹤的是5秒前carrot1的位置。

2.8 四元數基礎

注意tf2定義的四元數順序是x/y/z/axes, 所以不進行旋轉的四元數表示為(0,0,0,1)

直接建立四元數訊息:

from geometry_msgs.msg import Quaternion

# Create a list of floats, which is compatible with tf
# quaternion methods
quat_tf = [0, 1, 0, 0]

quat_msg = Quaternion(quat_tf[0], quat_tf[1], quat_tf[2], quat_tf[3])

通過尤拉矩陣建立訊息:(旋轉順序RPY: Row, Pitch, Yaw)

from tf.transformations import quaternion_from_euler
  
if __name__ == '__main__':

  # RPY to convert: 90deg, 0, -90deg
  q = quaternion_from_euler(1.5707, 0, -1.5707)

  print "The quaternion representation is %s %s %s %s." % (q[0], q[1], q[2], q[3])

座標旋轉直接用四元數乘法就行:

from tf.transformations import *

q_orig = quaternion_from_euler(0, 0, 0)
q_rot = quaternion_from_euler(pi, 0, 0)
q_new = quaternion_multiply(q_rot, q_orig)
print q_new

逆(轉置):
感覺官網給出來的不太對:

q[3] = -q[3]

不應該是下面這樣嗎?:

q[:2] = -q[:2]

求相對旋轉:

q1_inv[0] = prev_pose.pose.orientation.x
q1_inv[1] = prev_pose.pose.orientation.y
q1_inv[2] = prev_pose.pose.orientation.z
q1_inv[3] = -prev_pose.pose.orientation.w # Negate for inverse

q2[0] = current_pose.pose.orientation.x
q2[1] = current_pose.pose.orientation.y
q2[2] = current_pose.pose.orientation.z
q2[3] = current_pose.pose.orientation.w

qr = tf.transformations.quaternion_multiply(q2, q1_inv)

2.9 除錯 tf2

沒找到start_debug_demo.launch檔案。。。

檢視frame結構的命令:

rosrun tf2_tools view_frames
evince frames.pdf

檢查時間戳:

rosrun tf2 tf2_monitor turtle2 turtle1

2.10 tf2使用感測器資訊

2.11設定機器人用tf2

C++暫時看不懂

3. URDF模型學習

看完tf2部分後,進入到URDF建立機器人模型階段,看看學完之後能不能建立一個簡單的機器人模型。

3.1 用URDF從Scratch建立一個可視機器人模型

roscd urdf_tutorial
roslaunch urdf_tutorial display.launch model:=urdf/01-myfirst.urdf

注意fixed frame是網格的中心,是由urdf檔案中base_link自己定義的。顯示的圓柱的中心預設在網格中心。

再來建立一個多形狀的物體(程式檔案中已經自帶了,所以直接顯示):

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

以及另一個改變joint位置和link位置的urdf檔案,主要理解怎麼定義檔案中joint的位置和link的位置:

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

在執行這個launch檔案的時候會自動根據URDF檔案建立TF座標。

根據教程再分別看看怎麼新增表面材料,以及完整的模型:

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

最後機器人的手指是用的dae檔案:

  <link name="left_gripper">
    <visual>
      <origin rpy="0.0 0 0" xyz="0 0 0"/>
      <geometry>
        <mesh filename="package://urdf_tutorial/meshes/l_finger.dae"/>
      </geometry>
    </visual>
  </link>

3.2 建立一個可移動機器人模型

joints型別除了fixed, 還有continuous(連續轉動副), revolute(轉動副), prismatic(移動副或稱稜柱副)型別。其實還有其實還有planar平面移動副和float空間移動副,教程未具體解釋。

頭與身體的連續轉動副示例:

  <joint name="head_swivel" type="continuous">
    <parent link="base_link"/>
    <child link="head"/>
    <axis xyz="0 0 1"/>
    <origin xyz="0 0 0.3"/>
  </joint>

注意轉動副定義的旋轉軸(0,0,1),顯然頭是繞Z軸旋轉的。

夾持器的轉動副示例:

  <joint name="left_gripper_joint" type="revolute">
    <axis xyz="0 0 1"/>
    <limit effort="1000.0" lower="0.0" upper="0.548" velocity="0.5"/>
    <origin rpy="0 0 0" xyz="0.2 0.01 0"/>
    <parent link="gripper_pole"/>
    <child link="left_gripper"/>
  </joint>

注意轉動副還要定義limit: effort:力氣,可以施加的最大力(矩),lower:最小角度, upper:最大角度, velocity:轉動速度。

3.3 給URDF模型新增物理和碰撞屬性

在一個link裡面新增碰撞屬性:

    <collision>
      <geometry>
        <cylinder length="0.6" radius="0.2"/>
      </geometry>
    </collision>

在一個link裡面新增物理屬性:
慣性(質量和轉動慣量):

    <inertial>
      <mass value="10"/>
      <inertia ixx="0.4" ixy="0.0" ixz="0.0" iyy="0.4" iyz="0.0" izz="0.2"/>
    </inertial>

接觸係數:
mu: 摩擦係數
kp:剛度係數
kd:阻尼係數

副的動力學:
friction:靜摩擦力或摩擦力矩
damping:動摩擦係數

3.4 使用Xacro簡化URDF檔案

3.4.1 Xacro

Xacro是XML的巨集觀語言(macro language)。

使用以下形式的命令可以將.xacro檔案轉化為urdf檔案:

xacro --inorder model.xacro > model.urdf

在新版本中--inorder可以省略。

可以在.launch檔案寫這個命令,執行時自動生成urdf檔案,例如:

<param name="robot_description"
  command="xacro --inorder '$(find pr2_description)/robots/pr2.urdf.xacro'" />

一個Xacro檔案必須在開頭包含下面兩行才能正確解釋:

<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="your thing's name">

之前的那個模型的base_link裡面有些引數重複了兩遍,改起來很麻煩:

  <link name="base_link">
    <visual>
      <geometry>
        <cylinder length="0.6" radius="0.2"/>
      </geometry>
      <material name="blue"/>
    </visual>
    <collision>
      <geometry>
        <cylinder length="0.6" radius="0.2"/>
      </geometry>
    </collision>
  </link>

利用xacro屬性名,可以這樣寫:

<xacro:property name="width" value="0.2" />
<xacro:property name="bodylen" value="0.6" />
<link name="base_link">
    <visual>
        <geometry>
            <cylinder radius="${width}" length="${bodylen}"/>
        </geometry>
        <material name="blue"/>
    </visual>
    <collision>
        <geometry>
            <cylinder radius="${width}" length="${bodylen}"/>
        </geometry>
    </collision>
</link>

甚至屬性名可以這樣用(代替一部分):

<xacro:property name=”robotname” value=”marvin” />
<link name=”${robotname}s_leg” />

等價於:

<link name=”marvins_leg” />

甚至做數學操作:

<cylinder radius="${wheeldiam/2}" length="0.1"/>
<origin xyz="${reflect*(width+.02)} 0 0.25" />

3.4.2 Macro: xacro中最常用的包

定義一個簡單的xacro:macro:

<xacro:macro name="default_origin">
    <origin xyz="0 0 0" rpy="0 0 0"/>
</xacro:macro>
<xacro:default_origin />

最後會生成:

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

引數化的macro:

    <xacro:macro name="default_inertial" params="mass">
        <inertial>
                <mass value="${mass}" />
                <inertia ixx="1.0" ixy="0.0" ixz="0.0"
                     iyy="1.0" iyz="0.0"
                     izz="1.0" />
        </inertial>
    </xacro:macro>
    
<xacro:default_inertial mass="10"/>

最後會生成:

        <inertial>
                <mass value="10" />
                <inertia ixx="1.0" ixy="0.0" ixz="0.0"
                     iyy="1.0" iyz="0.0"
                     izz="1.0" />
        </inertial>

3.4.3 實際使用

如果需要檢視由xacro檔案產生的模型,同樣用下面的命令:

roslaunch urdf_tutorial display.launch model:=urdf/08-macroed.urdf.xacro

如果模型中存在對稱的模組或者對稱的位置,那麼運用macro將會方便很多,具體示例檢視上面命令中的檔案08-macroed.urdf.xacro

一些技巧:
用名字字首來定義兩個名字相似的物體
用數學計算joint的orgin,這樣在改變物體尺寸的時候會省去很多麻煩
用映象引數描述映象針對的物體,將引數分別設定為-1和1

3.5 在Gazebo中使用URDF

開啟教程的launch檔案,儘量根據教程理解launch怎麼寫的:

roslaunch urdf_sim_tutorial gazebo.launch 

為了讓機器人可互動,需要指定外掛Plugins和廣播訊息Transmissions.
為了連線Gazebo和ROS,我們在URDF</robot>標籤中指定了外掛:

  <gazebo>
    <plugin name="gazebo_ros_control" filename="libgazebo_ros_control.so">
      <robotNamespace>/</robotNamespace>
    </plugin>
  </gazebo>

可以在urdf/09-publishjoints.urdf.xacro檔案中檢視

還需要引用控制器:

These are initially loaded into the ROS parameter space. We have a yaml file joints.yaml that specifies our first controller.

type: "joint_state_controller/JointStateController"
publish_rate: 50

09-joints.launch中給出了怎麼載入這個yaml檔案到r2d2_joint_state_controller,但是現在還沒完。。。

還需要廣播訊息:
對一每一個非固定joints,需要指定一個transmission,例如頭的joint, 在URDF檔案新增:

  <transmission name="head_swivel_trans">
    <type>transmission_interface/SimpleTransmission</type>
    <actuator name="$head_swivel_motor">
      <mechanicalReduction>1</mechanicalReduction>
    </actuator>
    <joint name="head_swivel">
      <hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
    </joint>
  </transmission>

暫時把上面這個當作模板吧,現在我們執行下面的命令就可以看到joint的狀態了:

roslaunch urdf_sim_tutorial 09-joints.launch model:=urdf/10-firsttransmission.urdf.xacro 
rostopic echo /joint_states

接下來考慮怎麼控制Joints
所以又需要新增一個控制器配置:

type: "position_controllers/JointPositionController"
joint: head_swivel

This specifies to use the a JointPositionController from the position_controllers package to control the head_swivel transmission. Note that hardware interface in the URDF for this joint matches the controller type 注意URDF檔案的介面與控制器型別要對應.

10-head.launch已經新增進去了,現在我們直接執行看看:

roslaunch urdf_sim_tutorial 10-head.launch

現在Gazebo已經訂閱了一個新話題,你可以通過釋出訊息進行控制:

rostopic pub /r2d2_head_controller/command std_msgs/Float64 "data: -0.707"

但是joint會立刻變到該位置,因為沒有給joint加限制,所以新增limit:

  <joint name="head_swivel" type="continuous">
    <parent link="base_link"/>
    <child link="head"/>
    <axis xyz="0 0 1"/>
    <origin xyz="0 0 ${bodylen/2}"/>
    <limit effort="30" velocity="1.0"/>
  </joint>

執行:

roslaunch urdf_sim_tutorial 10-head.launch model:=urdf/11-limittransmission.urdf.xacro

採用類似的方式對其他joint進行控制,但是像夾持器這種可能需要組合控制,所以需要一個不同的控制器JointGroupPositionController

type: "position_controllers/JointGroupPositionController"
joints:
  - gripper_extension
  - left_gripper_joint
  - right_gripper_joint

執行:

roslaunch urdf_sim_tutorial 12-gripper.launch 

所以釋出訊息的時候需要釋出一個數組:

rostopic pub  /r2d2_gripper_controller/command std_msgs/Float64MultiArray "layout:
  dim:
  - label: ''
    size: 3
    stride: 1
  data_offset: 0
data: [0, 0.5, 0.5]"

為了讓機器人可以移動,需要控制機器人的輪子,在機器人的macro檔案中給每個輪子新增transmission,注意介面的名字叫VelocityJointInterface

    <transmission name="${prefix}_${suffix}_wheel_trans">
      <type>transmission_interface/SimpleTransmission</type>
      <actuator name="${prefix}_${suffix}_wheel_motor">
        <mechanicalReduction>1</mechanicalReduction>
      </actuator>
      <joint name="${prefix}_${suffix}_wheel_joint">
        <hardwareInterface>hardware_interface/VelocityJointInterface</hardwareInterface>
      </joint>
    </transmission>

因為輪子在地上走,那麼需要對輪子的材料和接觸係數進行定義:

<gazebo reference="${prefix}_${suffix}_wheel">
      <mu1 value="200.0"/>
      <mu2 value="100.0"/>
      <kp value="10000000.0" />
      <kd value="1.0" />
      <material>Gazebo/Grey</material>
    </gazebo>

然後給每個輪子新增控制器,下面這個yaml檔案就有點長了,用的控制器叫DiffDriveController 差速驅動控制器

  type: "diff_drive_controller/DiffDriveController"
  publish_rate: 50

  left_wheel: ['left_front_wheel_joint', 'left_back_wheel_joint']
  right_wheel: ['right_front_wheel_joint', 'right_back_wheel_joint']

  wheel_separation: 0.44

  # Odometry covariances for the encoder output of the robot. These values should
  # be tuned to your robot's sample odometry data, but these values are a good place
  # to start
  pose_covariance_diagonal: [0.001, 0.001, 0.001, 0.001, 0.001, 0.03]
  twist_covariance_diagonal: [0.001, 0.001, 0.001, 0.001, 0.001, 0.03]

  # Top level frame (link) of the robot description
  base_frame_id: base_link

  # Velocity and acceleration limits for the robot
  linear:
    x:
      has_velocity_limits    : true
      max_velocity           : 0.2   # m/s
      has_acceleration_limits: true
      max_acceleration       : 0.6   # m/s^2
  angular:
    z:
      has_velocity_limits    : true
      max_velocity           : 2.0   # rad/s
      has_acceleration_limits: true
      max_acceleration       : 6.0   # rad/s^2

執行:

roslaunch urdf_sim_tutorial 13-diffdrive.launch

launch檔案裡面添加了一個機器人控制面板可以控制該機器人,cool!

URDF還有一些教程,後面可能需要深入學習, 但現在先開始Gazebo教程。

4 Gazebo學習

開啟Gazebo教程,發現有一個提醒,提示現在這個教程已經不適用了:

但是還是按照過時的教程學了一部分,現在新的Gazebo官網裡面有個Library名字好像叫Ignition,但是按照之前的教程一路下來配置的環境好像是安裝的經典的Gazebo(Gazebo11)經典Gazebo官網

首先有必要了解一下Gazebo和ROS是怎麼連線的,這是一篇知乎文章的解釋:
Gazebo與ROS 版本的整合是通過一組叫做gazebo_ros_pkgs的包完成的,gazebo_ros_pkgs不是一個包,而是一堆包:

  • gazebo_dev:開發Gazebo外掛可以用的API
  • gazebo_msgs:定義的ROS2和Gazebo之間的介面(Topic/Service/Action)
  • gazebo_ros:提供方便的 C++ 類和函式,可供其他外掛使用,例如轉換和測試實用程式。它還提供了一些通常有用的外掛。gazeboros::Node
  • gazebo_plugins:一系列 Gazebo 外掛,將感測器和其他功能暴露給 ROS2 例如:

原來是通過一系列包進行連線的,好像在Gazebo和ROS聯合模擬時能夠經常看見這些包,前面已經大概看到過了。現在暫時還是先看看過時的教程,後面會按照經典的Gazebo(Gazebo11)進行學習。

4.1 安裝和開始Gazebo

這一部分在按照前面的教程安裝ROS的時候已經裝好了。

開始執行:

roslaunch gazebo_ros empty_world.launch

empty_world.launch檔案解釋:

<launch>
  <!-- start gazebo with an empty plane -->
  <param name="/use_sim_time" value="true" />
  <node name="gazebo" pkg="gazebo" type="gazebo" args="$(find gazebo_worlds)/worlds/empty.world" respawn="false" output="screen"/>
</launch>

4.2 建立和引用自定義URDF物體進行模擬

建立一個object.urdf檔案:

<robot name="simple_box">
  <link name="my_box">
    <inertial>
      <origin xyz="2 0 0" />
      <mass value="1.0" />
      <inertia  ixx="1.0" ixy="0.0"  ixz="0.0"  iyy="100.0"  iyz="0.0"  izz="1.0" />
    </inertial>
    <visual>
      <origin xyz="2 0 1"/>
      <geometry>
        <box size="1 1 2" />
      </geometry>
    </visual>
    <collision>
      <origin xyz="2 0 1"/>
      <geometry>
        <box size="1 1 2" />
      </geometry>
    </collision>
  </link>
  <gazebo reference="my_box">
    <material>Gazebo/Blue</material>
  </gazebo>
</robot>

在bash中引入模型:
放置在z=1位置,並且命名為my_object

rosrun gazebo spawn_model -file `pwd`/object.urdf -urdf -z 1 -model my_object

用launch檔案引入模型的例子(好像找不到這個檔案,可能需要自己在相應的資料夾建立,主要學習一下程式碼就行):

roslaunch gazebo_worlds table.launch

table.launch

<launch>
  <!-- send table urdf to param server -->
  <param name="table_description" command="$(find xacro)/xacro.py $(find gazebo_worlds)/objects/table.urdf.xacro" />

  <!-- push table_description to factory and spawn robot in gazebo -->
  <node name="spawn_table" pkg="gazebo" type="spawn_model" args="-urdf -param table_description -z 0.01 -model table_model" respawn="false" output="screen" />
</launch>

這個檔案首先把xacro檔案載入到ROS引數伺服器,然後利用spawn_model從引數伺服器得到模型的XML字元轉然後傳遞給Gazebo來在模擬世界中構建這個物體。

4.3 Gazebo ROS API for C-Turtle

一些術語:Gazebo裡面的Body就和URDF裡面的Link差不多,表示一個剛體。

4.3.1 新增和刪除模型
使用gazebo/spawn_modelgazebo/delete_model

新增模型示例:

rosrun gazebo spawn_model -file `rospack find gazebo_worlds`/objects/desk1.model -gazebo -model desk1 -x 0

使用幫助:

$ rosrun gazebo spawn_model
Commands:
    -[urdf|gazebo] - specify incoming xml is urdf or gazebo format
    -[file|param] [<file_name>|<param_name>] - source of the model xml
    -model <model_name> - name of the model to be spawned.
    -reference_frame <entity_name> - optinal: name of the model/body where initial pose is defined.
                                     If left empty or specified as "world", gazebo world frame is used.
    -namespace <ros_namespace> - optional: all subsequent ROS interface plugins will be inside of this namespace.
    -x <x in meters> - optional: initial pose, use 0 if left out
    -y <y in meters> - optional: initial pose, use 0 if left out
    -z <z in meters> - optional: initial pose, use 0 if left out
    -R <roll in radians> - optional: initial pose, use 0 if left out
    -P <pitch in radians> - optional: initial pose, use 0 if left out
    -Y <yaw in radians> - optional: initial pose, use 0 if left out

在命令列中也可以通過rosparam匯入urdf模型:

rosparam load `rospack find gazebo_worlds`/objects/coffee_cup.model coffee_cup_description
rosrun gazebo spawn_model -param coffee_cup_description -gazebo -model coffee_cup -x 0 -z 2

刪除模型示例:

rosservice call gazebo/delete_model '{model_name: coffee_cup}'

4.3.2 通過服務設定和獲取模型位置和姿態
執行模擬環境:

rosrun gazebo spawn_model -file `rospack find gazebo_worlds`/objects/000.580.67.model -gazebo -model cup -z 2

但是好像現在已經沒有gazebo_worlds這個包了,參考網址,但是隻需要學習後面怎麼設定和獲取模型位置和姿態就行。

通過/gazebo/set_model_state設定模型位置和姿態:

rosservice call /gazebo/set_model_state '{model_state: { model_name: cup, pose: { position: { x: 0, y: 0 ,z: 1 }, orientation: {x: 0, y: 0.491983115673, z: 0, w: 0.870604813099 } }, twist: { linear: {x: 0.0 , y: 0 ,z: 0 } , angular: { x: 0.0 , y: 0 , z: 0.0 } } , reference_frame: world } }'

通過/gazebo/get_model_state獲取模型位置和姿態:

rosservice call gazebo/get_model_state '{model_name: cup}'

4.3.2 獲取模擬的世界和模型的屬性

rosservice call gazebo/get_world_properties
rosservice call gazebo/get_model_properties '{model_name: desk1}'

4.3.3 使用話題獲取模型和Link的狀態

rostopic echo -n 1 /gazebo/model_states
rostopic echo -n 1 /gazebo/link_states

模型的狀態是指模型中典型Link的狀態,一般是root link。

4.3.4 對Link(物體)新增和刪除Wrench(扳手?)

rosservice call gazebo/apply_body_wrench '{body_name: "cup::000_580_67_body" , wrench: { torque: { x: 0.01, y: 0 , z: 0 } }, start_time: 10000000000, duration: 1000000000 }'
rosservice call gazebo/clear_body_wrenches '{body_name: "cup::000_580_67_body"}'

4.3.4 對Joints施加和刪除Efforts(力氣)

rosservice call gazebo/apply_joint_effort '{joint_name: link_joint, effort: 0.01, start_time: 10000000000, duration: 1000000000}'
rosservice call gazebo/clear_joint_forces '{joint_name: link_joint}'

4.3.5 暫停和啟用物理引擎

rosservice call gazebo/pause_physics
rosservice call gazebo/unpause_physics

4.3.6 用話題設定模型位置和姿態

rostopic pub -r 10 gazebo/set_model_state gazebo/ModelState '{model_name: desk1, pose: { position: { x: 0, y: 0, z: 2 }, orientation: {x: 0, y: 0.491983115673, z: 0, w: 0.870604813099 } }, twist: { linear: { x: 0, y: 0, z: 0 }, angular: { x: 0, y: 0, z: 0}  }, reference_frame: world }'

4.4 經典Gazebo教程

由於前面提到的教程過時了,前面的一部分或許有一定參考意義,但是最好還是按照經典的Gazebo教程進行學習!

Gazebo涉及的東西就有點多了,目前的想法是後面遇到了相關的內容再進一步瞭解,所以先進入PR2機器人教程

5 PR2機器人學習

5.1 建立場景

首先安裝PR2包:

sudo apt install ros-noetic-pr2-*

在這一步還是遇到了網路問題。。。先後嘗試了換成手機熱點,嘗試了手動下載,下載方式從Debian網站下載,好像沒能解決,然後多試了幾遍--fix-missing,然後又能連線上了。。。玄學。

 roslaunch gazebo_ros empty_world.launch
roslaunch pr2_gazebo pr2.launch

執行完之後又報ImportError: No module named yaml, ImportError: No module named rospkg模組錯誤,這兩個模組我已經裝過了,但是是python3環境的,而PR2專案的程式碼是python2的,前面已經遇到過這個問題,需要根據錯誤找到那些py檔案,把檔案第一行的#!/usr/bin/env python替換為#!/usr/bin/env python3
弄完之後還有一個錯誤:[ERROR] [1652446439.190980226]: Skipped loading plugin with error: XML Document '/opt/ros/noetic/share/prosilica_camera/plugins/nodelet_plugins.xml' has no Root Element. This likely means the XML is malformed or missing..
開啟報錯的那個資料夾發現原來這個nodelet_plugins.xml的路徑放錯了,按照報錯的那個位置新建一個plugins資料夾,把這個檔案放進去就好了:

好,現在來看一下pr2.launch:

<launch>
  <!-- Startup PR2 without any mechanism controllers -->
  <include file="$(find pr2_gazebo)/pr2_no_controllers.launch" />
  <!-- Load and Start Default Controllers -->
  <include file="$(find pr2_controller_configuration_gazebo)/pr2_default_controllers.launch" />
</launch>

好像就是載入了一個無控制器的pr2,然後又載入了一個預設控制器的pr2

5.2 ROS和Gazebo互動:

先看看PR2的Joints狀態:

rostopic echo joint_states | less

控制機器人:

rosmake pr2_teleop
roslaunch pr2_teleop teleop_keyboard.launch

可以用鍵盤控制!

Reading from keyboard
---------------------------
Use 'WASD' to translate
Use 'QE' to yaw
Press 'Shift' to run

5.3 PR2 模擬器工廠

主要探索PR2的介面
為了觸發系統使用pr2_machine/sim.machine,將ROS_MASTER_URI匯出到localhost,並作為sim。(不懂。。。)

export ROS_MASTER_URI=http://localhost:11311
export ROBOT=sim

然後執行環境:

roslaunch pr2_gazebo pr2_empty_world.launch

可以看看此時ROS節點,話題和服務,引數都有啥:試了一下發現每一項都有很多。

可以用rqt_pr2_dashboard視覺化機器人的狀態:

rosrun rqt_pr2_dashboard rqt_pr2_dashboard

但是報錯好像少了plugin.xml外掛,這回我沒有找到,不知道怎麼解決了。。。跳過這個吧

視覺化機器人:

rosrun rviz rviz

根據教程的操作可以進行一些視覺化操作。

呼叫傾斜鐳射軌跡:

rosservice call laser_tilt_controller/set_periodic_cmd '{ command: { header: { stamp: 0 }, profile: "linear" , period: 3 , amplitude: 1 , offset: 0 }}'

然後用rviz看一下話題tilt_scan,全是有一個鐳射,暫時還不清楚這個幹嘛用的:

利用URDF和STL檔案新增自定義的物體:

一般可以把Gazebo的模型放在models資料夾裡面,教程裡面建立了一個桌子,因此放到table資料夾下面:

tabla.stl檔案
table.urdf程式碼(官網的程式碼過時了,稍微改了一下):

<robot name="table_model">
  <link name="table_body">
    <inertial>
      <mass value="1.0" />
      <!-- center of mass (com) is defined w.r.t. link local coordinate system -->
      <origin xyz="0.25 -0.5 0" />
      <inertia  ixx="1.0" ixy="0.0"  ixz="0.0"  iyy="1.0"  iyz="0.0"  izz="1.0" />
    </inertial>
    <visual>
      <!-- visual origin is defined w.r.t. link local coordinate system -->
      <origin xyz="0 0 0" rpy="0 0 0" />
      <geometry>
        <mesh filename="/opt/ros/noetic/share/gazebo_plugins/Media/models/table/table.stl" scale="0.01 0.01 0.01"/>
      </geometry>
    </visual>
    <collision>
      <!-- collision origin is defined w.r.t. link local coordinate system -->
      <origin xyz="0 0 0" rpy="0 0 0" />
      <geometry>
        <mesh filename="/opt/ros/noetic/share/gazebo_plugins/Media/models/table/table.stl" scale="0.01 0.01 0.01"/>
      </geometry>
    </collision>
  </link>
  <gazebo reference="table_body">
    <material>Gazebo/Red</material>
  </gazebo>
</robot>

執行Gazebo:

roslaunch gazebo_ros empty_world.launch

將URDF檔案新增進ROS引數服務,取名為robot_description,之前沒做這一步模型就顯示不了!

rosparam set -t '/opt/ros/noetic/share/gazebo_plugins/Media/models/table/table.urdf' robot_description

然後生成模型:

rosrun gazebo_ros spawn_model -urdf -model mycar -param robot_description

可以看到桌子新增進去了!但是好像方位有點問題。。。具體模型需要具體調一下位置。

從開始安裝ROS一直學到這裡感覺才瞭解ROS,TF,URDF,Gazebo,Rviz的概念,但是怎麼從零開始實現一個機器人,可能需要認真研讀類似PR2這種專案的程式碼。但是在重複上面的模擬過程就因為程式碼過時而遇到不少坑,所以找找其他有用的資源。

我是想學習無人機模擬的,還記得最開始安裝ROS參考的部落格嗎? 這位博主基於蘇黎世理工大學的rotors包進行無人機從建模到控制的模擬,感覺可以用來加深對ROS的理解,有時間也可以跟著學習一下。

由於不清楚rotors後續能做到什麼程度,我現在知道的用得比較多的一個飛行器模擬平臺應該是PX4,已經有很多實際產品,相關的資源比較多,並且PX4可以跟ROS結合,因此打算後面主要學習PX4+ROS

入門教程就先寫到這裡吧,雖然連半隻腳都沒有踏入,但是後面開了rotorsPX4+ROS兩個新坑,在接下來的部落格繼續記錄,以及本文第一章ROS基礎知識會在後面複習的時候補充。