MoveIt2 總覽:機械手臂的運動規劃框架
- ros2
- moveit2
- manipulation
- motion-planning
問題定義
第 4 章寫的手臂模型,只有外型跟關節結構,並不知道「怎麼從目前姿態移動到某個目標姿態,同時不撞到自己或周圍環境」——這需要在高維度的關節空間裡搜尋一條可行路徑,比第 6 章 Nav2 處理的二維地面移動複雜得多(一台六軸手臂的規劃問題是在六維空間裡搜尋,而且手臂各節之間、手臂跟自己的底座之間都可能發生碰撞)。MoveIt2 是 ROS2 生態系裡標準的運動規劃框架,把這整套複雜的規劃與碰撞檢查邏輯封裝起來。
核心概念說明
Planning Scene:規劃器眼中的世界
MoveIt2 做決策時,依賴一個叫 Planning Scene 的內部世界模型,裡面包含兩類資訊:機器人自身目前的狀態(每個關節目前的角度,來源是第 2.5 節介紹的 /joint_states),以及機器人周圍環境的障礙物資訊(可能來自手動加入的已知物體,也可能來自感測器即時餵入的點雲資料,呼應第 5.3 節提到的模擬感測器資料)。規劃器在搜尋路徑時,每個候選姿態都要拿去跟 Planning Scene 裡的障礙物做碰撞檢查,確保規劃出的軌跡不會撞到任何已知物體,也不會讓手臂自己撞到自己(self-collision,例如前臂轉太多繞回去撞到上臂)。
Move Group:使用者互動的主要入口
move_group 是 MoveIt2 的核心節點,整合了規劃、執行、碰撞檢查等所有子系統,對外提供一組標準介面(Action 與服務),無論你是用 C++/Python 寫程式呼叫,還是用圖形化介面 RViz2 的 MoveIt2 外掛手動拖拉,最終都是透過 move_group 這個統一入口跟底層規劃邏輯互動。「Move Group」這個名稱本身也指涉另一個概念——一個機器人可能有多組可以獨立規劃的關節集合(例如一台雙臂機器人的左臂、右臂、加上一個夾爪,可以是三個獨立的規劃群組),每組稱為一個 planning group,這樣的分組讓你能只針對某個子系統做規劃,而不用每次都把整台機器人所有自由度綁在一起考慮。
規劃器是可替換的插件
MoveIt2 底層實際搜尋路徑的演算法(規劃器)是以插件形式載入的,官方預設提供 OMPL(Open Motion Planning Library)這套取樣式規劃演算法集合,也支援換成其他規劃器(例如針對特定任務優化的軌跡優化類演算法)。這種插件化設計呼應了第 6.3 節提到的 costmap layer 概念——核心框架處理通用邏輯(碰撞檢查、Planning Scene 管理),實際的搜尋演算法可以按需求替換,不需要重寫整套框架。
實作範例:啟動 move_group,載入第 4 章寫的手臂模型
延續第 4.2 節寫好的雙臂 xacro 模型,這裡示範最基本的 move_group 啟動流程。實務上完整的 MoveIt2 設定包含大量 SRDF(描述 planning group、允許的自我碰撞組合等資訊)與規劃器參數設定,通常透過官方的 MoveIt Setup Assistant 圖形化工具產生,這裡先聚焦在啟動後的基本互動,完整設定檔案的產生過程超出這一節的範圍。
1. 啟動 move_group(假設已經有 Setup Assistant 產生好的設定套件)
ros2 launch my_package_moveit_config move_group.launch.py
預期輸出
[INFO] [move_group]: Loading robot model 'dual_arm'...
[INFO] [move_group]: Publishing maintained planning scene on 'monitored_planning_scene'
[INFO] [move_group]: MoveGroup context using planning plugin ompl_interface/OMPLPlanner
[INFO] [move_group]: MoveGroup context initialization complete
You can start planning now!
最後一行 You can start planning now! 是確認 move_group 完整初始化完成、可以開始接受規劃請求的明確訊號——在這行出現之前送出的規劃請求通常會失敗或被忽略。
2. 用 RViz2 的 MoveIt2 外掛手動互動
rviz2
在 RViz2 裡 Add → 選擇 MoveIt2 分類底下的 MotionPlanning 外掛。畫面會出現機器人模型,並且在末端(例如前臂末端)出現一個可以拖拉的互動控制球(interactive marker)。拖動這個控制球到新位置後,點擊面板上的「Plan」按鈕。
預期畫面
面板下方會顯示規劃結果,成功的話會顯示一條半透明的軌跡預覽動畫,手臂模型會沿著這條軌跡從目前姿態移動到目標姿態;如果拖動的目標超出手臂實際能到達的範圍,或途中必然發生碰撞,面板會顯示規劃失敗,不會產生任何軌跡預覽。
3. 用程式碼送出一個規劃請求
import rclpy
from rclpy.node import Node
from rclpy.action import ActionClient
from moveit_msgs.action import MoveGroup
class SimpleMoveClient(Node):
def __init__(self):
super().__init__("simple_move_client")
self.client = ActionClient(self, MoveGroup, "move_action")
def send_goal(self):
self.client.wait_for_server()
goal_msg = MoveGroup.Goal()
goal_msg.request.group_name = "left_arm"
goal_msg.request.num_planning_attempts = 5
goal_msg.request.allowed_planning_time = 5.0
self.get_logger().info("送出規劃請求...")
return self.client.send_goal_async(goal_msg)
def main():
rclpy.init()
node = SimpleMoveClient()
future = node.send_goal()
rclpy.spin_until_future_complete(node, future)
node.destroy_node()
rclpy.shutdown()
if __name__ == "__main__":
main()
這裡刻意只設定了 group_name,沒有指定具體目標姿態——實務上會搭配下一節介紹的目標姿態設定方式(透過末端執行器的座標,或直接指定關節角度)才能真正送出有意義的規劃請求,這裡先示範最基本的 Action Client 呼叫結構,呼應第 2.4 節介紹的標準 Action 呼叫模式。
常見錯誤與除錯技巧
錯誤一:move_group 啟動後卡住,一直沒有出現 "You can start planning now!"
原因:常見原因是 SRDF 檔案裡宣告的 planning group、link 名稱,跟實際 URDF 裡的名稱對不上(例如 SRDF 是用 Setup Assistant 針對舊版模型產生的,之後 URDF 改了 link 名稱但沒有同步更新 SRDF),導致 move_group 在載入模型時卡在某個解析步驟。
排除方式:檢查 move_group 終端機輸出裡最後停在哪一步,通常會有明確指出哪個 link 或 joint 名稱有問題的訊息;也可以用 check_urdf(第 4.1 節介紹過)先確認 URDF 本身沒問題,排除掉模型本身的問題後,再聚焦排查 SRDF 與 URDF 之間的名稱一致性。
錯誤二:規劃永遠失敗,即使目標明顯在可達範圍內
[ERROR] [move_group]: Fail: ABORTED: NO_IK_SOLUTION
原因:這個錯誤代表逆向運動學(IK,下一節會深入介紹)求解器找不到能讓末端執行器到達目標姿態的關節角度組合——可能是目標姿態確實超出手臂實際可達範圍(例如超過完全伸直的最大長度),也可能是目標姿態技術上可達,但要求的末端朝向(orientation)在該位置附近沒有對應的合法關節組合。
排除方式:先嘗試只指定目標位置、放寬末端朝向的限制(很多 IK 求解器支援只限制部分自由度),確認問題是出在位置本身不可達,還是朝向要求過於嚴苛;也可以參考機器人規格書上標示的實際工作空間範圍,確認目標點是否真的落在合理範圍內。
小結
MoveIt2 用 Planning Scene 統一管理機器人狀態與環境障礙物資訊,move_group 是對外互動的核心節點,實際規劃演算法以可替換插件的形式運作。啟動流程與失敗排查很大一部分工作,是確保 URDF 與 SRDF 之間的名稱與結構保持一致。下一節要深入規劃請求裡最關鍵的一步:怎麼指定目標姿態,以及逆向運動學求解在這中間扮演的角色。
延伸閱讀
常見問題
- MoveIt2 跟 Nav2 是同一套框架嗎?
- 不是,但架構理念很相似——都是把複雜任務拆成多個職責單一的元件,透過標準 ROS2 介面協同運作。Nav2 處理的是機器人底盤在二維(或帶高度資訊的三維)空間裡的移動規劃,MoveIt2 處理的是機械手臂在高維度關節空間裡的運動規劃,兩者用的規劃演算法、面對的問題本質都不同,但『用 Action 呼叫規劃、用生命週期或類似機制管理元件狀態』這類設計哲學是共通的。
- Planning Scene 裡的障礙物資訊是自動更新的嗎?
- 取決於資料來源。如果障礙物是透過感測器資料(例如點雲)即時餵入的,Planning Scene 會持續更新;如果是手動用程式加入的固定障礙物(例如一張已知位置的桌子),則需要程式主動呼叫服務更新,不會自動反映場景裡實際發生的變化。實務上兩種來源通常會混合使用:已知固定的環境結構用手動加入,未知或會移動的物體則交給感測器資料驅動。
- MoveIt2 規劃出來的軌跡,執行時一定會完全照著走嗎?
- 不一定,這點跟第 6 章 Nav2 的全域路徑與即時執行的關係類似——MoveIt2 規劃出的是一條理論上可行、且經過碰撞檢查的軌跡,實際執行交給控制器(通常是 ros2_control 底下的關節軌跡控制器)逐點追蹤,過程中的追蹤誤差、硬體回應延遲等因素,會讓實際執行路徑跟規劃路徑存在些微差異,這是正常的物理限制,不是規劃器出錯。