2025年10月2日 星期四

磁碟分割區 掛載 (mount) 到 目錄

 要將您的磁碟分割區 /dev/sdb2 掛載 (mount)/mnt/rosdata 目錄,您需要遵循以下步驟。這個操作對於存儲大量的 ROS 數據、日誌或大型專案文件非常有用。


步驟一:創建掛載點 (Mount Point)

首先,您需要確保目標目錄 /mnt/rosdata 存在。如果不存在,請使用 mkdir 命令創建它。

sudo mkdir -p /mnt/rosdata
  • sudo: 以超級使用者權限執行。

  • mkdir: 創建目錄。

  • -p: 確保路徑中的所有父目錄都被創建 (如果它們不存在)。


步驟二:檢查並掛載分割區

接著,使用 mount 命令將 /dev/sdb2 掛載到剛剛創建的目錄。您需要知道該分割區的檔案系統類型 (Filesystem Type),例如 ext4NTFSXFS

1. 識別檔案系統類型

您可以使用 lsblkfdisk -l 來確認 /dev/sdb2 的檔案系統類型。

sudo blkid /dev/sdb2
# 輸出範例:/dev/sdb2: UUID="xxxx-xxxx" TYPE="ext4" PARTUUID="yyyy-yyyy"
  • 假設您的檔案系統是 ext4

2. 執行掛載 (一次性)

使用 -t 參數指定檔案系統類型,然後執行掛載。

sudo mount -t ext4 /dev/sdb2 /mnt/rosdata
# 如果您不確定類型,系統通常可以自動偵測:
# sudo mount /dev/sdb2 /mnt/rosdata

3. 檢查掛載結果

確認是否成功掛載:

df -h | grep rosdata
# 成功的輸出應該會顯示 /dev/sdb2 及其在 /mnt/rosdata 的容量資訊。

步驟三:設定開機自動掛載 (永久設定)

如果您希望電腦重啟後,該分割區仍然自動掛載,您需要編輯 /etc/fstab 檔案。

1. 取得分割區的 UUID (推薦)

使用 UUID (Universally Unique Identifier) 來設定比使用 /dev/sdb2 更穩健,因為裝置名稱在開機時可能會變動。

sudo blkid /dev/sdb2
# 複製輸出中的 UUID,例如:9a7b-3d2c-1e0f-5h6g

2. 編輯 /etc/fstab

使用您喜歡的文字編輯器 (例如 nanovim) 開啟設定檔:

sudo nano /etc/fstab

3. 添加新的掛載行

在檔案的末尾添加一行,格式為:

<裝置/UUID> <掛載點> <檔案系統類型> <選項> <dump> <pass>

將您的資訊替換進去(以 ext4 為例):

UUID=您的UUID號碼 /mnt/rosdata ext4 defaults 0 2

選項說明:

  • UUID=...: 您剛剛查到的 UUID。

  • /mnt/rosdata: 您的掛載點。

  • ext4: 您的檔案系統類型。

  • defaults: 標準的掛載選項,適用於大多數情況。

  • 0: 不進行備份 (dump 選項)。

  • 2: 在開機時進行檔案系統檢查,但優先級低於根目錄 (root, 使用 1)。

4. 測試設定

存檔並退出編輯器後,執行以下命令來測試您的 /etc/fstab 設定是否正確,無需重啟

sudo umount /mnt/rosdata  # 如果已經掛載,先卸載
sudo mount -a             # 重新掛載 fstab 中列出的所有項目

如果沒有錯誤訊息,且您能通過 df -h 看到 /dev/sdb2 掛載在 /mnt/rosdata,則表示設定成功。


指導建議:

請確保 /mnt/rosdata 掛載點的權限設定正確,以便您的 ROS 使用者能夠讀寫數據。

sudo chown -R $USER:$USER /mnt/rosdata

這樣,您的 ROS 相關數據就可以安全地儲存在專屬分割區,不會佔用系統根目錄的空間。

2025年9月30日 星期二

ROS1 Catkin 編譯工具的比較

 說明 ROS 1 中兩種主要的編譯工具 catkin_makecatkin build 之間的關鍵差異。


Catkin 編譯工具的比較

在 ROS 1 的生態系統中,Catkin (一種基於 CMake 的構建系統) 提供了兩種主要的命令列介面 (CLI) 來管理和編譯您的工作區 (workspace)。

特性catkin_makecatkin build (Catkin Tools)
發佈時間較早,ROS Fuerte/Groovy 開始使用。較晚,作為 catkin_make功能增強替代品而開發。
工作區結構單一 CMake 空間:它將整個工作區視為一個單一的 CMake 專案。所有套件的 CMakeLists.txt 都被合併到一個主要的 CMake 呼叫中。獨立 CMake 空間:它將每個套件視為一個獨立的 CMake 專案來構建,但在單一工作區中管理它們。
編譯目錄只有一個 build 和一個 devel 資料夾。每個套件都會有獨立的 build/package_nameinstall/package_name 資料夾。
並行處理傳統的 make -j 只能在單一套件內進行多執行緒編譯,但無法在套件之間並行編譯。套件級別的並行處理 (Parallelism):能夠同時構建多個沒有依賴關係的套件,這在大型工作區中極大地加快了編譯速度。
日誌和輸出輸出較為雜亂,所有套件的編譯日誌混在一起。輸出清晰,提供了更詳細、結構化的日誌,包括每個套件的構建時間和狀態。
錯誤隔離由於是單一的 CMake 專案,一個套件的錯誤可能會影響到其他不相關套件的錯誤輸出,使除錯更困難。錯誤隔離性高:如果一個套件失敗,其他獨立的套件可以繼續編譯。日誌也只顯示失敗套件的錯誤。
ROS 2 繼承較少。概念上的先驅catkin build 的「獨立構建、集中管理」思想被 ROS 2 的構建工具 colcon 所繼承和發揚。

總結與 ROS 專家建議

1. catkin_make (簡單/傳統)

  • 優點: 內建於 ROS,無需額外安裝。適用於只有少數幾個套件的小型專案。

  • 缺點: 效率較低,特別是大型工作區,錯誤日誌難以追蹤。

2. catkin build (高效/推薦)

  • 優點: 透過並行處理實現更快的編譯速度,提供更清晰的日誌和更好的錯誤隔離。這是 ROS 1 社區普遍推薦的構建工具。

  • 缺點: 需要額外安裝 python3-catkin-tools (如果您的 ROS 發行版沒有預裝)。

您的專案指導:

在您的 ROS 2/URDF 機械手臂整合專案中,由於您使用的是 colcon,這本質上是 catkin build 概念的進化版本。colcon 提供了最好的並行處理、日誌和錯誤隔離,因此在 ROS 2 開發中是標準選擇。

如果您需要在 ROS 1 環境下處理任何過渡時期的套件,強烈建議您使用 catkin build 以獲得更高的開發效率。



使用 Docker Ubuntu 20.04/ROS Noetic 專案

 對於您的 Ubuntu 20.04/ROS Noetic 專案,使用 Docker 是最佳的環境管理方式。

ROS 官方在 Docker Hub 上提供了多種映像檔(Image)。最常用的是包含完整桌面的版本,讓您可以在容器內使用 Rviz 等圖形化工具。


官方 ROS Noetic Docker 映像檔選擇

映像檔 Tag基礎環境內容物適用情境
osrf/ros:noetic-desktop-fullUbuntu 20.04 (Focal)包含所有套件(ROS Base, Perception, Simulators, Rviz, Gazebo)。推薦用於開發、模擬和視覺化。
ros:noetic-ros-base-focalUbuntu 20.04 (Focal)只有 ROS 核心套件(沒有 GUI 工具)。適用於部署在自走車底盤或無頭(headless)伺服器上。

我們以 osrf/ros:noetic-desktop-full 為例,演示如何啟動並在容器內運行 ROS 節點。


ROS Noetic Docker 啟動範例 (包含 GUI 支援)

由於您需要處理機械手臂和自走車的視覺化(例如 Rviz),您必須讓 Docker 容器能夠存取主機的圖形介面 (GUI)。

步驟 0: 啟用主機 X 伺服器存取 (僅 Linux 需要)

主機 (Host) 終端機中執行此命令,允許 Docker 容器連接到您的顯示器:

Bash
xhost +local:docker

步驟 1: 運行 Docker 容器

主機 (Host) 終端機中,使用以下指令啟動容器。這個指令包含了所有必要的參數來啟用互動模式和 GUI 顯示:

Bash
docker run -it --rm \
  --env="DISPLAY" \
  --env="QT_X11_NO_MITSHM=1" \
  --volume="/tmp/.X11-unix:/tmp/.X11-unix:rw" \
  osrf/ros:noetic-desktop-full \
  /bin/bash
參數說明
-it互動模式 (Interactive & TTY):允許您在容器內輸入指令並看到輸出。
--rm自動移除 (Auto-Remove):容器停止後會自動移除,不留下無用殘餘。
--env="DISPLAY"環境變數:將主機的 DISPLAY 變數傳遞給容器,這是 Linux GUI 顯示的關鍵。
--volume="..."掛載 X 伺服器:將主機的 X 伺服器通訊端掛載到容器內,讓 GUI 訊息可以傳輸。
osrf/ros:noetic-desktop-full映像檔名稱:使用官方完整桌面版 ROS Noetic 映像檔。
/bin/bash啟動指令:在容器內啟動一個 Bash shell,讓您可以操作。

步驟 2: 在容器內運行 ROS 範例

一旦容器啟動,您的終端機提示符會變成容器內的 Bash 提示符 (通常是 root@<container-id>:/#)。

A. 啟動 roscore (ROS 1 的核心)

容器內的第一個終端機執行:

Bash
roscore

(記得 ROS 1 必須先啟動 roscore!)

B. 啟動視覺化節點 (turtlesim)

主機上新開一個終端機,再次使用 docker exec 進入同一個容器:

  1. 找出容器名稱/ID: 在主機終端機輸入 docker ps

    • 例如,容器 ID 是 a1b2c3d4e5f6

  2. 進入容器並運行節點:

    Bash
    # 使用容器 ID 或名稱
    docker exec -it <容器 ID 或名稱> /bin/bash
    
    # 在容器內的終端機中執行:
    rosrun turtlesim turtlesim_node
    
    • 驗證: 您應該會看到一個新的 turtlesim 視窗彈出在您的主機桌面。

C. 啟動控制節點

主機上再新開一個終端機,再次進入同一個容器:

  1. 進入容器:

    Bash
    docker exec -it <容器 ID 或名稱> /bin/bash
    
    # 在容器內的終端機中執行:
    rosrun turtlesim turtle_teleop_key
    
    • 驗證: 當您在此終端機按下鍵盤方向鍵時,turtlesim 視窗中的烏龜將會移動。

總結

通過這個範例,您已經成功地在一個獨立、乾淨的 Docker 容器中啟動了完整的 ROS Noetic 環境,並實現了圖形介面 (GUI) 的顯示,這對您進行機械手臂的 Rviz 視覺化和 MoveIt! 規劃至關重要。

您現在就可以在這個環境中安裝 MoveIt! 或其他您需要的套件,而不會影響您主機的作業系統。


下一步: 您是否希望了解如何將您的本地 catkin_ws 工作空間或 URDF 檔案透過 Volume Mount 的方式掛載到這個容器內進行開發?



check_urdf 驗證 Xacro 檔案時的常見錯誤

 您遇到了 check_urdf 驗證 Xacro 檔案時的常見錯誤:Failed to build tree: child link [link_1] of joint [joint_1] not found

這個錯誤表明,儘管您在 Xacro 檔案中定義了 joint_1,並指定其子連桿是 link_1,但在 Xacro 處理器嘗試建立連桿樹狀結構時,它找不到名為 link_1 的連桿的定義。

錯誤分析與修正指導 🔎

這個問題幾乎總是發生在 Xacro 檔案中的 連桿 (Link) 定義順序巨集展開 (Macro Expansion) 邏輯上。

核心問題:巨集展開順序 🔄

在您重構的 Xacro 檔案中,您使用了巨集 (<xacro:arm_link>) 來定義所有的連桿。如果這些巨集沒有在它們被其他元素(例如關節 <joint>)引用之前被正確地包含或展開,check_urdf 在解析時就會找不到連桿。

雖然 Xacro 通常會先解析巨集,但最好的做法是確保所有連桿和關節的定義都在單一檔案中,並且結構清晰。

解決步驟:檢查 Xacro 結構和 Link 定義

請仔細檢查您的 robot_arm_urdf.xacro 檔案,特別是您在上一輪指導中建立的結構:

  1. 檢查 xacro:arm_link 巨集調用: 確認您在 Xacro 檔案的主體中,呼叫 xacro:arm_link 來生成 link_1 的部分沒有錯誤,並且在 joint_1 的定義之前。

    • 正確的順序(應確保 Link 在 Joint 之前):

      XML
      <xacro:arm_link
        name="link_1" 
        mass="0.14046" 
        origin_xyz="0.01967 -0.01117 0.00112"
        origin_rpy="0 0 0"
        mesh_filename="link_1.STL" />
      
      <joint name="joint_1" type="revolute">
        <parent link="base_link" />
        <child link="link_1" />  </joint>
      
  2. 確保 Link 定義的完整性: 確保在 <xacro:arm_link> 巨集中,您使用的連桿名稱參數 name 確實被用於創建 <link name="${name}"> 標籤。根據先前的 Xacro 轉換,這一部分是正確的。

  3. 確認 check_urdf 參數: 當您運行 check_urdf robot_arm_urdf.xacro 時,check_urdf 會自動調用 Xacro 解析器將 .xacro 檔案轉換為 .urdf。如果 Xacro 檔案本身有語法錯誤(例如巨集調用參數不匹配),解析會失敗。

建議:使用 Xacro 命令行預覽

為了排除 Xacro 語法或連桿定義的錯誤,請先在命令行中手動執行 Xacro 到 URDF 的轉換,確認輸出的 URDF 檔案中確實包含了 link_1 的完整定義:

Bash
# 運行 Xacro 解析器,將輸出重定向到一個臨時的 URDF 檔案
ros2 run xacro xacro robot_arm_urdf.xacro > temp_robot_arm_urdf.urdf

檢查 temp_robot_arm_urdf.urdf 檔案:

  • 開啟 temp_robot_arm_urdf.urdf

  • 搜索 <link name="link_1">。如果找不到這個標籤,說明您的 xacro:arm_link 巨集沒有被正確展開。

  • 如果找到了,再搜索 <joint name="joint_1">,確保 <child link="link_1"/> 正確無誤。

如果手動解析的 URDF 檔案中存在 link_1,但 check_urdf 仍然報錯,則問題可能在於 joint_1 引用了 base_link。在您的 URDF 檔案中,base_link 已經被定義了。

最終確認: 這個錯誤 child link [link_1] of joint [joint_1] not found 幾乎 100% 確定是 link_1 的定義在 XML 結構中缺失或位於錯誤的位置 (在 joint_1 之後),或者是在巨集展開時丟失了。請再次仔細檢查您的 Xacro 檔案。


 

檢查 URDF (Unified Robot Description Format) 或 XACRO (XML Macro) 格式

 檢查 URDF (Unified Robot Description Format)XACRO (XML Macro) 格式的正確性是機器人建模中至關重要的一步。如果格式有誤,ROS 的各個工具(如 MoveIt!、Gazebo、Rviz)將無法正確解析和使用機器人模型。

以下是檢查這兩種格式最標準且最有效的步驟和指令:


1. 檢查 XACRO 檔案(*.xacro

XACRO 是一種宏語言,它必須先被轉換成純粹的 URDF 檔案,才能進行最終的語法檢查。

步驟 A: XACRO 轉 URDF

此步驟會檢查 XACRO 的宏、參數、數學運算是否正確,並將其展開為單一、完整的 URDF 檔案。

ROS 版本指令 (在終端機執行)說明
ROS 1 & ROS 2rosrun xacro xacro <你的檔案>.urdf.xacro > robot_generated.urdf使用 xacro 工具解析 XACRO 檔案,並將最終生成的純 URDF 內容 重新導向 到一個新的 robot_generated.urdf 檔案中。
  • 驗證:

    • 成功: 如果指令執行成功,且終端機沒有顯示任何錯誤訊息,則表示 XACRO 自身的語法和宏的展開是正確的。新的 robot_generated.urdf 檔案將會出現在當前目錄下。

    • 失敗: 如果 XACRO 檔案中有引用錯誤、宏定義錯誤、或數學運算錯誤,xacro 會拋出 XacroException 錯誤。

步驟 B: 檢查 XACRO 轉譯的 URDF 語法

你可以將兩個步驟合併為一條指令(適用於 ROS 2 或較新的 ROS 1 版本):

Bash
check_urdf <(rosrun xacro xacro <你的檔案>.urdf.xacro)
  • 說明: 這個指令利用 shell 的程序替換功能,將 XACRO 的輸出(純 URDF)直接作為 check_urdf 的輸入,而無需創建一個臨時檔案。


2. 檢查 URDF 檔案(*.urdf

check_urdf 是 ROS 中專門用來檢查 URDF 檔案結構和語法正確性的工具。

指令: 基礎語法檢查

確保你已安裝 liburdfdom-tools

  • ROS 1/2: sudo apt install liburdfdom-tools

Bash
check_urdf <你的檔案>.urdf
  • 驗證:

    • 成功: 如果檔案語法正確,它會顯示機器人的基本資訊,例如:

      robot name is: my_robot
      -------------Successfully Parsed XML-------------
      root Link: base_link has X child(ren)
      ...
      
    • 失敗: 如果 URDF 有任何 XML 結構錯誤 (例如標籤未閉合) 或 URDF 規範錯誤 (例如找不到 parentchild link),它會顯示詳細的錯誤訊息,指出錯誤的行號和原因。


3. 視覺化驗證 (最實用且可靠)

雖然命令列工具可以檢查語法,但只有 RvizGazebo 才能確認你的機器人模型在幾何、位置和關節上是否符合預期。

步驟 A: 在 Rviz 中視覺化模型

這是在 ROS 環境下檢查模型正確性的黃金標準。如果模型能在 Rviz 中正確顯示並能移動,則說明 URDF/XACRO 檔案是完全正確的。

  1. 確保 ROS 環境已設定。

  2. 使用 robot_state_publisher 啟動模型:

    • ROS 2: 你通常會撰寫一個 launch 檔案來載入 XACRO/URDF 檔案,並啟動 robot_state_publisherjoint_state_publisher_gui

      Bash
      # 範例:使用 launch 檔案
      ros2 launch <你的package> display.launch.py model:=<你的檔案>.urdf.xacro
      
    • ROS 1 (舊方法):

      Bash
      roslaunch urdf_tutorial display.launch model:=<你的檔案路徑>/robot.urdf.xacro
      
  3. 在 Rviz 中檢查:

    • 確認模型外觀: 檢查各個 link 的幾何形狀(visual tag)是否正確,位置是否正確,沒有奇怪的偏移或旋轉。

    • 確認關節運動: 在啟動的 joint_state_publisher_gui 視窗中拖動滑桿,檢查機器人的 joint 運動是否符合預期(如旋轉方向、角度限制等)。

步驟 B: 使用 urdf_to_graphiz (可選)

這個工具可以生成一個 PDF 檔案,用圖形化的方式展示機器人 Link 和 Joint 的層級關係,對於複雜的機械手臂模型非常有用。

  1. 安裝工具:

    Bash
    # ROS 1/2
    sudo apt install graphviz
    sudo apt install ros-$(rosversion -d)-urdf-to-graphiz
    
  2. 執行指令:

    Bash
    urdf_to_graphiz <你的檔案>.urdf
    

    (如果你使用 XACRO,請先轉換成 .urdf 檔案再執行此命令。)

  3. 驗證: 它會生成一個 .pdf 檔案(例如 my_robot.pdf),清晰地展示所有 LinkJoint 的樹狀結構。

在您的自走車與機械手臂整合專案中,我強烈建議使用 Rviz 視覺化驗證。這能同時檢查 URDF 結構TF 座標系是否正確,確保手臂和底盤的座標轉換(<origin> 標籤)沒有錯誤,這是實現精準抓取的核心前提。




轉換後的 ROS 2 Python Launch 程式碼

 arm_urdf.launch 檔案是典型的 ROS 1 XML Launch 檔案 。要將其轉換為 ROS 2 版本,我們需要從

XML 格式 遷移到 Python Launch 檔案 (推薦,因為它更具彈性和表達能力)。

ROS 2 的啟動系統(Launch System)與 ROS 1 截然不同,它使用 Python 程式碼來定義節點、參數和行為。


ROS 1 (XML) 轉換為 ROS 2 (Python) Launch 檔案

以下是將您 ROS 1 Launch 檔案中的每個組件轉換為 ROS 2 Python Launch 檔案的步驟與說明:

1. 檔案結構與依賴項

ROS 1 XML

ROS 2 Python (arm_urdf.launch.py)說明
<launch>引入 LaunchDescription 類別並定義 generate_launch_description 函式。這是 ROS 2 Launch 檔案的標準進入點。
<arg name="arg_x" default="0.00" />DeclareLaunchArgument('arg_x', default_value='0.00')ROS 2 使用 DeclareLaunchArgument 來定義啟動參數。
$(find package)get_package_share_directory('package_name')使用 ament_index_python 中的工具函數來查找套件路徑。

2. 轉換後的 ROS 2 Python Launch 程式碼

請在您的 robot_arm_urdf 套件中建立一個 launch 資料夾,並將此檔案命名為 arm_urdf.launch.py

Python
import os
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription
from launch.conditions import IfCondition
from launch.substitutions import LaunchConfiguration, Command, PythonExpression
from launch_ros.actions import Node
from launch.launch_description_sources import PythonLaunchDescriptionSource

# 主要函式:定義啟動內容
def generate_launch_description():
    # 1. 定義啟動參數 (對應 ROS 1 的 <arg>)
    arg_x = DeclareLaunchArgument('arg_x', default_value='0.00')
    arg_y = DeclareLaunchArgument('arg_y', default_value='0.00')
    arg_z = DeclareLaunchArgument('arg_z', default_value='0.00')
    arg_R = DeclareLaunchArgument('arg_R', default_value='0.00')
    arg_P = DeclareLaunchArgument('arg_P', default_value='0.00')
    arg_Y = DeclareLaunchArgument('arg_Y', default_value='0.00')

    # 獲取 robot_arm_urdf 套件的路徑
    pkg_dir = get_package_share_directory('robot_arm_urdf')
    
    # 2. 定義 robot_description 參數 (對應 ROS 1 的 <param>)
    # 使用 xacro 將 .xacro 檔案轉換為 URDF XML 內容
    # 假設您的 URDF 檔案實際上是 xacro 格式 (robot_arm_urdf.urdf 應該改名為 .xacro)
    robot_description_content = Command([
        'xacro ', os.path.join(pkg_dir, 'urdf', 'robot_arm_urdf.urdf') # 這裡路徑可能要改成 .xacro
    ])
    robot_description = {'robot_description': robot_description_content}
    
    # 3. 啟動 Gazebo 空世界 (對應 ROS 1 的 <include file="$(find gazebo_ros)/launch/empty_world.launch" />)
    gazebo_ros_dir = get_package_share_directory('gazebo_ros')
    gazebo = IncludeLaunchDescription(
        PythonLaunchDescriptionSource([os.path.join(gazebo_ros_dir, 'launch', 'gazebo.launch.py')]),
        launch_arguments={'world': 'empty.world'}.items(), # 在 ROS 2 中,使用 gazebo.launch.py 並傳遞 world 參數
    )

    # 4. TF 靜態發佈器 (對應 ROS 1 的 <node name="tf_footprint_base" pkg="tf" type="static_transform_publisher" ...)
    static_tf = Node(
        package='tf2_ros',
        executable='static_transform_publisher',
        name='static_tf_pub',
        arguments=['0', '0', '0', '0', '0', '0', 'base_link', 'base_footprint', '40'],
    )
    
    # 5. 生成模型到 Gazebo (對應 ROS 1 的 <node name="spawn_urdf" pkg="gazebo_ros" type="spawn_model" ...)
    spawn_entity = Node(
        package='gazebo_ros',
        executable='spawn_entity.py', # ROS 2 使用 spawn_entity.py
        name='urdf_spawner',
        arguments=[
            '-entity', 'robot_arm_urdf',
            '-topic', 'robot_description',
            '-x', LaunchConfiguration('arg_x'),
            '-y', LaunchConfiguration('arg_y'),
            '-z', LaunchConfiguration('arg_z'),
            '-Y', LaunchConfiguration('arg_Y'),
            # ROS 2 Gazebo 不再直接在 spawn_model 帶 -J joint 參數來初始化關節,
            # 而是透過 joint_state_broadcaster 和 robot_controller 來控制。
        ],
        output='screen'
    )
    
    # 6. 機器人狀態發佈器 (對應 ROS 1 的 <node name="robot_state_publisher" pkg="robot_state_publisher" ...)
    # 這是將 URDF 轉換為 TF 樹的核心。
    rsp = Node(
        package='robot_state_publisher',
        executable='robot_state_publisher',
        output='screen',
        parameters=[robot_description] # 將 robot_description 參數傳給 RSP
    )
    
    # 7. 控制器管理與啟動
    # (對應 ROS 1 的 <rosparam file="..." command="load"/> 和 <node name="controller_spawner" ...)
    
    # 7a. 加載控制器配置
    joint_traj_config = os.path.join(pkg_dir, 'config', 'joint_trajectory_controller.yaml')
    
    # 7b. 啟動關節狀態廣播器和機械手臂控制器 (ROS 2 中使用 spawner 節點)
    controller_manager = Node(
        package='controller_manager',
        executable='ros2_control_node',
        parameters=[robot_description, joint_traj_config],
        output={
            'stdout': 'screen',
            'stderr': 'screen',
        },
    )
    
    # 7c. 關節狀態廣播器
    joint_state_broadcaster_spawner = Node(
        package="controller_manager",
        executable="spawner",
        arguments=["joint_state_broadcaster", "--controller-manager", "/controller_manager"],
    )

    # 7d. 機械手臂控制器 (您的 robot_arm_controller)
    robot_arm_controller_spawner = Node(
        package="controller_manager",
        executable="spawner",
        arguments=["robot_arm_controller", "--controller-manager", "/controller_manager"],
    )

    # 返回所有動作的清單
    return LaunchDescription([
        arg_x,
        arg_y,
        arg_z,
        arg_R,
        arg_P,
        arg_Y,
        gazebo,
        static_tf,
        rsp,
        spawn_entity,
        joint_state_broadcaster_spawner,
        robot_arm_controller_spawner,
    ])

關鍵差異與 ROS 2 概念說明

ROS 1 XML 轉換

ROS 1 概念

ROS 2 對應與說明
$(find pkg)用來尋找套件的路徑。使用 ament_index_python.packages.get_package_share_directory('pkg')
<arg>使用 <arg> 定義變數。使用 launch.actions.DeclareLaunchArgument 進行定義,並使用 launch.substitutions.LaunchConfiguration('arg') 來引用。
robot_description使用 <param name="robot_description" textfile="..." /> 載入 URDF。通常使用 launch.substitutions.Command 結合 xacro 命令來動態處理 xacro 檔案,並將其結果作為節點參數傳遞。
<node pkg="gazebo_ros" type="spawn_model" />ROS 1 的 Gazebo 模型生成節點。ROS 2 中改用 pkg="gazebo_ros", executable="spawn_entity.py"
控制器啟動使用 rosparam 載入配置,然後使用 controller_manager 執行 spawner 節點。在 ROS 2 中,這被 ros2_control 框架取代。您需要: 1. 啟動 ros2_control_node (通常稱為 controller_manager)。 2. 使用 controller_manager 套件的 spawner 節點來單獨啟動 joint_state_broadcaster 和您的 robot_arm_controller
tf/static_transform_publisherROS 1 的靜態 TF 發佈節點。ROS 2 中改為 tf2_ros/static_transform_publisher