1.はじめに
前回記事に引き続き、ROS2のPlanSys2の試行と、それに関連する気づきをまとめました。
ROS2 Planning System — ROS2 Planning System 2 1.0.0 documentation
今回は、いろいろわからないことも多いので、 ドキュメントを翻訳しつつ補足的な内容を含めながら、 チュートリアル2番目と3番目を進めていきます。
2.The first planning package
2つ目のチュートリアルを進めていきます。
The first planning package — ROS2 Planning System 2 1.0.0 documentation
概要
PlanSys2 には、ドメイン内で計画および実行するすべての要素を含むパッケージがあるのが一般的です。
- PDDL モデル
- 各アクションを実装するノード
- アクションを実行し、適切なドメインで PlanSys2 を起動するランチャー
- (このチュートリアルには含まれていませんが) 知識を開始し、計画とその目標をいつ実行するかを管理するノード
このチュートリアルでは、PlanSys2 getting_startedに示されているパッケージを、細かく読み解いていきます。
(0)Requisites 準備
チュートリアルチャレンジ(1)で準備したソースコードをそのまま使いますので、環境構築手順は省略します。
起動用シェルスクリプト
ここで後で横着するために、起動用のシェルスクリプトを作成します。
$ cd ~/gitwork/p2tuto $ touch tcommon.sh tpterm.sh tsimple_example_launch.sh trqt.sh $ chmod 755 tpterm.sh tsimple_example_launch.sh trqt.sh
tcommon.sh
それぞれのシェルスクリプトで共通で実行する内容です。
ROSの通信の干渉を防ぐため、環境変数ROS_DOMAIN_ID
を書きます。
namespaceを使う場合は、環境変数ROS_NAMESPACEも指定しておきます。
■注意:namespaceを使うと、rqtでの表示ができなくなります
#!/bin/bash # 各種ツールの共通内容 source /opt/ros/humble/setup.bash source install/local_setup.bash # 同一のネットワーク内での干渉防止 export ROS_DOMAIN_ID=231 # namespaceを使う場合に他のシェルスクリプトに設定値を渡す環境変数 # export ROS_NAMESPACE=my_namespace
tpterm.sh
#!/bin/bash # plansys2_terminalの起動 cd $(dirname $0) source ./tcommon.sh ros2 run plansys2_terminal plansys2_terminal # namespaceを使う場合の書き方 # ros2 run plansys2_terminal plansys2_terminal --ros-args -r __ns:=/${ROS_NAMESPACE}
tsimple_example_launch.sh
#!/bin/bash # simple exampleの起動 cd $(dirname $0) source ./tcommon.sh ros2 launch plansys2_simple_example plansys2_simple_example_launch.py # namespaceを使う場合の書き方 #ros2 launch plansys2_simple_example plansys2_simple_example_launch.py namespace:=${ROS_NAMESPACE}
trqt.sh
#!/bin/bash # rqt toolsの起動 cd $(dirname $0) source ./tcommon.sh rqt & rqt_graph & rqt_bag &
(1)Running the example
ターミナル1
simple example
をlaunchします。
$ cd ~/gitwork/p2tuto
$ ./tsimple_example_launch.sh
ターミナル2
状況を確認するためのGUIツールを立ち上げます。
$ cd ~/gitwork/p2tuto
$ ./trqt.sh
ターミナル3
plansys2 terminalを使います。
$ cd ~/gitwork/p2tuto $ ./tpterm.sh [INFO] [1715157485.835241673] [terminal]: No problem file specified. ROS2 Planning System console. Type "quit" to finish > source src/plansys2_simple_example/launch/commands > run [INFO] [1715157602.113426067] [executor_client]: Plan Succeeded Successful finished >
plansys2_terminalで計画を確認・実行できます。 どちらのターミナルでも、実行された現在のアクションとその完了レベルが表示されます。 プランの実行が完了すると、plansys2_terminalのプロンプトが再び使えます。
(2)Package structure
ros2_planning_system_examples/plansys2_simple_example
ディレクトリは下図のようになっています。
package.xml と CMakeLists.txt があり、ROS2 パッケージの標準的なものになっています。さらにこの中には、次のディレクトリがあります
pddl | ドメインを含む PDDL ファイルのあるディレクトリ |
launch | ランチャーが含まれています |
src | ドメインの 3 つのアクションの実装が含まれています |
(3)Actions implementation
ドメイン内のアクションは、move
、charge
、ask_charge
の3つです。
これには「フェイク」実装が含まれます。 アクションを実装する各ノードはアクション実行者と呼ばれます。
各アクションの実行には数秒かかり、途中の進行状況の値がターミナルに表示されます。
PlanSys2 の各アクションは管理対象ノードであり、ライフサイクル ノードとも呼ばれます。
ノードがアクティブな場合、関数を繰り返し呼び出してジョブを実行します。
src/move_action_node.cpp
のコードを見てみます。
MoveAction
MoveActionクラス は、rclcpp_cascade_lifecycle::CascadeLifecycleNode
を継承する plansys2::ActionExecutorClient
です。("定義")
rclcpp_lifecycle::LifecycleNode
に、さらに追加のプロパティがあります。
アクティブなときに別の rclcpp_cascade_lifecycle::CascadeLifecycleNode
をカスケードでアクティブ化できます。
- アクションでセンサー情報を処理するノードをアクティブにする必要がある場合に便利です。
- その出力を必要とするアクションがアクティブな間のみアクティブになります。
詳細については、このリポジトリを参照してください。
下記は、250ms (4Hz) ごとにメソッド do_work()
が呼び出されることを示します。
plansys2::ActionExecutorClient("move", 250ms)
plansys2::ActionExecutorClient
plansys2::ActionExecutorClient
には、アクションに関連するprotectedなメソッドを備えた API があります。
const std::vector<std::string> & get_arguments(); void send_feedback(float completion, const std::string & status = ""); void finish(bool success, float completion, const std::string & status = "");
- get_arguments() は、アクションの引数のリストを返します。(このサンプルの中では使われていません)
- たとえば、
(move leia Chargerroom Kitchen)
を実行すると、次の文字列ベクトル{"leia", "chargingroom", "kitchen"}
が返されます。
- たとえば、
以下のコードは完了のフィードバックを送信しています。
progress_
は、進捗度を示すprivateなメンバ変数です- アクションが正常に終了した場合は、
progress_
に格納された0.0~1.0の値と、オプションの情報を送り返します。send_feedback
メソッドは、アクションの途中経過をことを示しますfinish
メソッドはアクションが終了したことを示します- その後、ノードは非アクティブ状態に移行します
… void do_work() { if (progress_ < 1.0) { // 途中 progress_ += 0.02; send_feedback(progress_, "Move running"); } else { // 完了 finish(true, 1.0, "Move completed"); progress_ = 0.0; std::cout << std::endl; } // 進捗をコンソールに出力 std::cout << "\r\e[K" << std::flush; std::cout << "Moving ... [" << std::min(100.0, progress_ * 100.0) << "%] " << std::flush; } …
また、action ノードを作成したら、このノードが必要に応じて実行できるように非アクティブ状態に移行する必要があります。
パラメータ action
は、ノード オブジェクトを実装するアクションを設定します。
auto node = std::make_shared<MoveAction>(); node->set_parameter(rclcpp::Parameter("action", "move")); node->trigger_transition(lifecycle_msgs::msg::Transition::TRANSITION_CONFIGURE); rclcpp::spin(node->get_node_base_interface());
PDDLファイルの内容とActionの対応
上記のソースコードの抜粋では、アクション名move
が指定されていました。ここで、
PDDLファイルの内容との対応をみると、:durative-action move
の部分に該当します。
p2tuto/src/plansys2_simple_example/pddl/simple_example.pddl
の抜粋、25行目付近
(define (domain simple) … ;; Actions ;;;;;;;;;;;;;;;;;;;;;;;;;;;; (:durative-action move :parameters (?r - robot ?r1 ?r2 - room) …
(4)Launcher
ランチャーには、ドメインを選択する PlanSys2 launcherが含まれている必要があります。 これにより、PDDL アクションを実装する実行可能ファイルが実行されます。
launchすると、以下の3つのaction_nodeが実行されます。
また、PDDLファイルもこのlaunchファイル内で指定します。
… def generate_launch_description(): … plansys2_cmd = IncludeLaunchDescription( PythonLaunchDescriptionSource(os.path.join( get_package_share_directory('plansys2_bringup'), 'launch', 'plansys2_bringup_launch_monolithic.py')), launch_arguments={ 'model_file': example_dir + '/pddl/simple_example.pddl', # ←ここで指定 'namespace': namespace }.items()) …
本章「The first planning package」の内容は以上です。
3.Using a planning controller
続いて、3つ目のチュートリアルを進めていきます。 前章のソースコードはそのまま使います。
Using a planning controller — ROS2 Planning System 2 1.0.0 documentation
概要
以前のチュートリアルでは、PlanSys2 を、ターミナルを使用して対話的にのknowledgeを入力し、プランを計算して、実行するところまで試行しました。
ところが、PlanSys2 をアプリケーションに組み込もうとする場合、対話型では不便です。
planning controller は、以下を行うプログラムです。
- 知識を開始、参照、および更新する
- 目標を設定する
- 計画の実行を要求する
このplanning controllerプログラムはロボットのミッションを制御します。 通常、有限状態マシン(FSM)として実装され、次に達成すべき目標が何であるかを決定します。
このチュートリアルでは、PlanSys2 と Nav2 を統合して、サンプルのロボット(Turtlebot)が、とある環境を巡回しパトロールできるようにします。
巡回する場所(waypoints)は 5 箇所で、それぞれに異なる座標があります。 (具体的な座標は後述します)
wp_control
、wp_1
、wp_2
、wp_3
、wp_4
位置wp_control
は、他のすべてのwaypointsが接続される中継地点です。
wp_1
~wp_4
は相互に接続されていないため、ロボットがパトロールする際は常に wp_control を経由してから、他のwaypointsに向かいます。
waypointsのパトロールは、以下の順を繰り返します。
- waypointsに移動する
- そこに到着したら停止し、その地点の周囲のを認識するために数秒間回転する
wp_control
の場所に戻って、次のwaypointsに移動する- すべてのwaypointsを一巡したら、最初のwaypointsから再び巡回する
チュートリアルの最初のステップでは、ナビゲーション プロセスをシミュレートするテスト コンポーネントを使用します。
チュートリアルの最後に、実際の Nav2 とシミュレーターを起動します。
(0)Requisites
ひとつ前のチュートリアルで準備した、ワークスペースを使います。
# 環境変数読み込み $ source /opt/ros/humble/setup.bash # 作業ディレクトリに移動 $ mkdir -p ~/gitwork/p2tuto $ cd !$ # colcon buildが終わっていれば、buildとinstallディレクトリができています。 # さらに、起動支援スクリプトも先ほど作成しています。 $ ls build install log src tcommon.sh tpterm.sh trqt.sh tsimpleexample.sh
(1)Running the example
新しいターミナルを開き、ナビゲーションの動作をシミュレートするノードを使用して PlanSys2 を実行します。 ここでは、 Nav2 の実行は行われません。
$ cd ~/gitwork/ps2_tuto2 $ source ./tcommon.sh $ ros2 launch plansys2_patrol_navigation_example patrol_example_fakesim_launch.py
新しいターミナルを開いて次を実行します。
$ cd ~/gitwork/ps2_tuto2 $ source ./tcommon.sh $ ros2 run plansys2_patrol_navigation_example patrolling_controller_node
これでミッションが開始されます。 最初のターミナルでは、計画がどのように計算され、アクションがどのように実行されるかがわかります。 最後のターミナルでは、アクションの実行も確認できます。
(2)Patrol action performer
アクション patrol
には、ロボットを回転させるアクションが含まれています。
ros2_planning_system_examples/plansys2_patrol_navigation_example/src/patrol_action_node.cpp
このアクションは、アクティブ化されると 1Hz の周波数で実行されます。
行 | アクション |
---|---|
32 | 各ステップで、進行状況を 10% 増加させる |
36 ~ 44 | トピック /cmd_vel を通じてロボットに速度コマンドを送信し、ロボットをその場で回転させる |
47 ~ 54 | アクションが完了すると、ロボットが停止する |
34 | 各サイクルで、フィードバックを送信するか |
56 | アクションがすでに終了したことを宣言します |
(3)Move action performer
アクション move
には、ROS2 アクション navigate_to_pose
を使用してナビゲーション リクエストを Nav2 に送信するアクションが含まれています。
ROS2 actionsに慣れていない場合、この例は非常に複雑になるため、ここでは詳細は説明せず、選択したコード部分のみを説明します。
ros2_planning_system_examples/plansys2_patrol_navigation_example/src/move_action_node.cpp
このファイルのうち、以下のコードのコンストラクタで示す部分では、
各waypoint の座標は初期化され、メンバ変数waypoints_
に挿入されます。
… class MoveAction : public plansys2::ActionExecutorClient { public: MoveAction() : plansys2::ActionExecutorClient("move", 500ms) { geometry_msgs::msg::PoseStamped wp; wp.header.frame_id = "map"; wp.header.stamp = now(); wp.pose.position.x = 0.0; wp.pose.position.y = -2.0; wp.pose.position.z = 0.0; wp.pose.orientation.x = 0.0; wp.pose.orientation.y = 0.0; wp.pose.orientation.z = 0.0; wp.pose.orientation.w = 1.0; waypoints_["wp1"] = wp; wp.pose.position.x = 1.8; wp.pose.position.y = 0.0; waypoints_["wp2"] = wp; wp.pose.position.x = 0.0; wp.pose.position.y = 2.0; waypoints_["wp3"] = wp; wp.pose.position.x = -0.5; wp.pose.position.y = -0.5; waypoints_["wp4"] = wp; wp.pose.position.x = -2.0; wp.pose.position.y = -0.4; waypoints_["wp_control"] = wp; … … private: … void do_work() { } std::map<std::string, geometry_msgs::msg::PoseStamped> waypoints_; …
上記のソースコードを読み解いて、 各waypoint の座標を整理すると、以下のようになります。
geometry_msgs::msg::PoseStamped wp | wp1 | wp2 | wp3 | wp4 | wp_control |
---|---|---|---|---|---|
wp.pose.position.x | 0.0 | 1.8 | 0.0 | -0.5 | -2.0 |
wp.pose.position.y | -2.0 | 0.0 | 2.0 | -0.5 | -0.4 |
wp.pose.position.z | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
以下のコードon_activate(const rclcpp_lifecycle::State &previous_state)
で示す部分では、
- ①アクション (move leia wp_control wp_1) を実行すると
- 3 番目の引数 (get_arguments()[2]) がナビゲートするウェイポイント wp_1 になります。
- この ID を使用して、waypoints_ から座標を取得し、Nav2 アクションで送信します。
- ②Nav2 ROS2 アクションからフィードバックを受信すると
- 移動アクションの実行に関するフィードバックが送信されます。
- ③Nav2 がナビゲーションが完了したと判断すると
- finish を呼び出して移動アクションの実行を終了します。
class MoveAction : public plansys2::ActionExecutorClient { public: … … // ① rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn on_activate(const rclcpp_lifecycle::State &previous_state) { … // The goal is in the 3rd argument of the action auto wp_to_navigate = get_arguments()[2]; RCLCPP_INFO(get_logger(), "Start navigation to [%s]", wp_to_navigate.c_str()); goal_pos_ = waypoints_[wp_to_navigate]; navigation_goal_.pose = goal_pos_; … // ② send_goal_options.feedback_callback = [this]( NavigationGoalHandle::SharedPtr, NavigationFeedback feedback) { send_feedback( std::min(1.0, std::max(0.0, 1.0 - (feedback->distance_remaining / dist_to_move))), "Move running"); }; // ③ send_goal_options.result_callback = [this](auto) { finish(true, 1.0, "Move completed"); }; … return ActionExecutorClient::on_activate(previous_state); }
(4) Mission controller
Mission controller は、 PlanSys2 のKnowledgeを初期化し、 パトロール プランを実行するための有限ステートマシン(Finite State Machines: FSM) を実装します。
ros2_planning_system_examples/plansys2_patrol_navigation_example/src/patrolling_controller_node.cpp
PlanSys2 と対話するために 4 つの PlanSys2 クライアントを使用します。
class PatrollingController : public rclcpp::Node { public: … void init() { domain_expert_ = std::make_shared<plansys2::DomainExpertClient>(); planner_client_ = std::make_shared<plansys2::PlannerClient>(); problem_expert_ = std::make_shared<plansys2::ProblemExpertClient>(); executor_client_ = std::make_shared<plansys2::ExecutorClient>(); init_knowledge(); } …
これらのクライアントを使用して、Instance
とPredicate
を PlanSys2 に追加できます。
… void init_knowledge() { problem_expert_->addInstance(plansys2::Instance{"r2d2", "robot"}); problem_expert_->addInstance(plansys2::Instance{"wp_control", "waypoint"}); problem_expert_->addInstance(plansys2::Instance{"wp1", "waypoint"}); problem_expert_->addInstance(plansys2::Instance{"wp2", "waypoint"}); problem_expert_->addInstance(plansys2::Instance{"wp3", "waypoint"}); problem_expert_->addInstance(plansys2::Instance{"wp4", "waypoint"}); problem_expert_->addPredicate(plansys2::Predicate("(robot_at r2d2 wp_control)")); problem_expert_->addPredicate(plansys2::Predicate("(connected wp_control wp1)")); problem_expert_->addPredicate(plansys2::Predicate("(connected wp1 wp_control)")); problem_expert_->addPredicate(plansys2::Predicate("(connected wp_control wp2)")); problem_expert_->addPredicate(plansys2::Predicate("(connected wp2 wp_control)")); problem_expert_->addPredicate(plansys2::Predicate("(connected wp_control wp3)")); problem_expert_->addPredicate(plansys2::Predicate("(connected wp3 wp_control)")); problem_expert_->addPredicate(plansys2::Predicate("(connected wp_control wp4)")); problem_expert_->addPredicate(plansys2::Predicate("(connected wp4 wp_control)")); } …
step
メソッドは、5Hzで呼び出されます。ここにはFSM が実装されています。
… void step() { switch (state_) { … } } …
switch 文の分岐には、以下が含まれます。例えば、state_ = PATROL_WP1
のときはこの部分が実行されます。
併せて、進捗状況を示す情報も、コンソールに出力します。
… [move 100%][move 100%][patrol 100%] [move 100%][move 100%][patrol 100%] [move 100%][move 100%][patrol 100%] [INFO] [1715243171.939044525] [executor_client]: Plan Succeeded Successful finished …
… case PATROL_WP1: { auto feedback = executor_client_->getFeedBack(); for (const auto &action_feedback : feedback.action_execution_status) { // 進捗をコンソールに出力 std::cout << "[" << action_feedback.action << " " << action_feedback.completion * 100.0 << "%]"; } std::cout << std::endl; …
以下は、別の状態に遷移する条件と、新しい状態の開始前に実行されるコードです。ここで重要な部分は、
- 新しい状態に対する新しい目標を設定する方法
- (setGoal を使用)、
- 新しい計画を計算する方法、
- およびそれを達成するために計画を実行するエグゼキューターを呼び出す方法
- (ノンブロッキング呼び出しのexecutePlan を使用) です。
… if (!executor_client_->execute_and_check_plan() && executor_client_->getResult()) { if (executor_client_->getResult().value().success) { std::cout << "Successful finished " << std::endl; // Cleanning up problem_expert_->removePredicate(plansys2::Predicate("(patrolled wp1)")); // Set the goal for next state problem_expert_->setGoal(plansys2::Goal("(and(patrolled wp2))")); // Compute the plan auto domain = domain_expert_->getDomain(); auto problem = problem_expert_->getProblem(); auto plan = planner_client_->getPlan(domain, problem); if (!plan.has_value()) { std::cout << "Could not find plan to reach goal " << parser::pddl::toString(problem_expert_->getGoal()) << std::endl; break; } // Execute the plan if (executor_client_->start_plan_execution(plan.value())) { //次の状態に移行 state_ = PATROL_WP2; } } else …
(5)Running the example with Nav2
あ)Nav2のインストール
Nav2 をインストールしていない場合は、ここでインストールします。
$ sudo apt install -y ros-humble-navigation2 ros-humble-nav2-bringup ros-humble-turtlebot3-gazebo
サンプルを実行して動作を確認します。
# 環境変数の読み込み $ source /opt/ros/humble/setup.bash $ export TURTLEBOT3_MODEL=waffle $ export GAZEBO_MODEL_PATH=$GAZEBO_MODEL_PATH:/opt/ros/humble/share/turtlebot3_gazebo/models $ source /usr/share/gazebo/setup.bash # 実行 $ ros2 launch nav2_bringup tb3_simulation_launch.py headless:=False
ここでrvizのウィンドウが開きます。
エラー対応・tf error: Invalid frame ID "odom"
下記のようなエラーが出る場合は、source /usr/share/gazebo/setup.bash
の読み込みを忘れています。
… [component_container_isolated-6] [INFO] [1681207523.143584924] [local_costmap.local_costmap]: Timed out waiting for transform from base_link to odom to become available, tf error: Invalid frame ID "odom" passed to canTransform argument target_frame - frame does not exist …
起動用シェルスクリプト
ここで後で横着するために、起動用のシェルスクリプトを作成します。
$ cd ~/gitwork/p2tuto $ touch tcommon_tb3.sh tnav2.sh $ chmod 755 tnav2.sh
tcommon_tb3.sh
#!/bin/bash # 各種ツールの共通内容・TurtleBot3の設定 export TURTLEBOT3_MODEL=waffle export GAZEBO_MODEL_PATH=$GAZEBO_MODEL_PATH:/opt/ros/humble/share/turtlebot3_gazebo/models source /usr/share/gazebo/setup.bash
tnav2.sh
#!/bin/bash # Nav2 の起動 cd $(dirname $0) source ./tcommon.sh source ./tcommon_tb3.sh ros2 launch nav2_bringup tb3_simulation_launch.py headless:=False
い)試行
新しいターミナルを開き、ナビゲーションの動作をシミュレートするノードを使用して PlanSys2 を実行します。
ターミナル1
起動と同時に、rvizも開きます。
#nav2のときとおなじ環境変数を読み込み $ source /opt/ros/humble/setup.bash $ source install/local_setup.bash $ export TURTLEBOT3_MODEL=waffle $ export GAZEBO_MODEL_PATH=$GAZEBO_MODEL_PATH:/opt/ros/humble/share/turtlebot3_gazebo/models $ source /usr/share/gazebo/setup.bash #(あるいは、先に作成したシェルスクリプトを読み込んでもOK) # $ source ./tcommon.sh # $ source ./tcommon_tb3.sh # 実行 $ ros2 launch plansys2_patrol_navigation_example patrol_example_launch.py
ターミナル2
こちらでプランを順番に実行します。
$ source /opt/ros/humble/setup.bash $ source install/local_setup.bash $ ros2 run plansys2_patrol_navigation_example patrolling_controller_node [move 0%][patrol 0%] … [move 4.5231%][patrol 0%] … [move 83.5761%][patrol 0%] … [move 100%][patrol 0%] … [move 100%][patrol 100%] [INFO] [1715232556.588637609] [executor_client]: Plan Succeeded Successful finished [move 0%][move 0%][patrol 0%] … [move 6.73664%][move 0%][patrol 0%] … [move 90.8074%][move 0%][patrol 0%] … [move 100%][move 32.6972%][patrol 0%] … [move 100%][move 53.1785%][patrol 0%] … [move 100%][move 100%][patrol 20%] [INFO] [1715232608.788491562] [executor_client]: Plan Succeeded Successful finished …
rvizの画面上で、ロボットが順番に移動していることがわかります。
起動用シェルスクリプト
ここで後で横着するために、起動用のシェルスクリプトを作成します
$ cd ~/gitwork/p2tuto $ touch tpatrol_example_launch.sh tpatrolling_controller.sh $ chmod 755 tpatrol_example_launch.sh tpatrolling_controller.sh
tpatrol_example_launch.sh
#!/bin/bash # patrol_example_launchの起動 cd $(dirname $0) source ./tcommon.sh source ./tcommon_tb3.sh ros2 launch plansys2_patrol_navigation_example patrol_example_launch.py
tpatrolling_controller.sh
#!/bin/bash # patrolling_controller_nodeの起動 cd $(dirname $0) source ./tcommon.sh ros2 run plansys2_patrol_navigation_example patrolling_controller_node
上記の2つのシェルスクリプトを、2つのターミナルからそれぞれ起動します。
う)試行の復習・・・1- Running the example
チュートリアルにはありませんが、
本章3.Using a planning controller
の一番最初1- Running the example
のサンプルを、
同じように実行してみます。
$ cd ~/gitwork/p2tuto
$ ./trqt.sh
$ ./tnav2.sh
$ cd ~/gitwork/p2tuto $ source ./tcommon.sh $ ros2 launch plansys2_patrol_navigation_example patrol_example_fakesim_launch.py
$ cd ~/gitwork/p2tuto $ source ./tcommon.sh $ ros2 run plansys2_patrol_navigation_example patrolling_controller_node
こちらについても、rvizの画面上でロボットが小さい範囲で移動していることがわかります。
4.まとめ
planning package
の使い方や、(plansys2 terminalによる手動操作ではなく)コードから動的にプラン実行するまでを試すことができました。
一部、オリジナルのドキュメントのリンク先が変わってる所もありましたので、改めて現時点(24年5月)のリンク先を記載しています。