hotch-potch, Note to self

いろいろ作業記録

ROS2・PlanSys2 チュートリアルチャレンジ(3)

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

ドメイン内のアクションは、movechargeask_charge の3つです。

これには「フェイク」実装が含まれます。 アクションを実装する各ノードはアクション実行者と呼ばれます。

各アクションの実行には数秒かかり、途中の進行状況の値がターミナルに表示されます。

PlanSys2 の各アクションは管理対象ノードであり、ライフサイクル ノードとも呼ばれます。

ノードがアクティブな場合、関数を繰り返し呼び出してジョブを実行します。

src/move_action_node.cpp のコードを見てみます。

ros2_planning_system_examples/plansys2_simple_example/src/move_action_node.cpp at humble · PlanSys2/ros2_planning_system_examples · GitHub

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 アクションを実装する実行可能ファイルが実行されます。

ros2_planning_system_examples/plansys2_simple_example/launch/plansys2_simple_example_launch.py at humble · PlanSys2/ros2_planning_system_examples · GitHub

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_controlwp_1wp_2wp_3wp_4

位置wp_control は、他のすべてのwaypointsが接続される中継地点です。 wp_1wp_4は相互に接続されていないため、ロボットがパトロールする際は常に wp_control を経由してから、他のwaypointsに向かいます。

waypointsのパトロールは、以下の順を繰り返します。

  1. waypointsに移動する
  2. そこに到着したら停止し、その地点の周囲のを認識するために数秒間回転する
  3. wp_controlの場所に戻って、次のwaypointsに移動する
  4. すべての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 at humble · PlanSys2/ros2_planning_system_examples · GitHub

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 at humble · PlanSys2/ros2_planning_system_examples · GitHub]

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 at humble · PlanSys2/ros2_planning_system_examples · GitHub

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();
  }
…

これらのクライアントを使用して、InstancePredicateを 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 を使用)、
  • 新しい計画を計算する方法、
  • およびそれを達成するために計画を実行するエグゼキューターを呼び出す方法
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月)のリンク先を記載しています。