ROS通信の基本:トピック、サービス、アクション、パラメーターサーバー

ROS通信メカニズムの概要

ROS(Robot Operating System)を用いたロボットシステムでは、異なるノード間で効率的な情報交換が不可欠です。ROSは、このノード間通信を実現するために、主に「トピック」「サービス」「アクション」「パラメーターサーバー」という4つの通信メカニズムを提供します。それぞれのメカニズムは、異なる用途や要件に合わせて設計されており、ロボットアプリケーション開発において柔軟な選択肢を提供します。

1. トピック(Topics)

トピックは、ROSで最も基本的な非同期型のデータストリーミング通信方法です。一つのノード(パブリッシャー)が特定のトピックにメッセージを公開し、他のノード(サブスクライバー)がそのトピックを購読することでメッセージを受信します。この方法は、センサーデータ、ロボットの状態、速度コマンドなど、連続的かつリアルタイムなデータ配信に適しています。

1.1. パブリッシャーの実装:ロボットの速度制御

ROSのシミュレーターであるTurtlesimでは、`/turtle1/cmd_vel`トピックに`geometry_msgs::Twist`型のメッセージを送信することで、仮想の亀型ロボットの速度を制御できます。

`geometry_msgs::Twist`メッセージには、線形速度(linear: x, y, z)と角速度(angular: x, y, z)の2つの成分が含まれます。単位はそれぞれm/sとrad/sです。

C++コード例:矩形経路を移動するパブリッシャー

この例では、Turtlesimの亀ロボットが矩形経路を描くように速度コマンドをパブリッシュします。

#include <ros/ros.h>
#include <geometry_msgs/Twist.h>
#include <chrono> // C++11 time library

// ロボットの移動状態を定義
enum class RobotMovementState {
    MOVING_FORWARD,
    TURNING
};

int main(int argc, char** argv) {
    ros::init(argc, argv, "turtle_path_publisher_node");
    ros::NodeHandle nh;

    ros::Publisher cmd_vel_publisher = nh.advertise<geometry_msgs::Twist>("/turtle1/cmd_vel", 10);
    ros::Rate loop_rate(5); // 5 Hz

    RobotMovementState current_state = RobotMovementState::MOVING_FORWARD;
    ros::Time state_start_time = ros::Time::now();

    // 移動および旋回に必要な時間を定義
    const double FORWARD_DURATION_SEC = 2.5; // 直進する秒数
    const double TURN_DURATION_SEC = 1.6;    // 旋回する秒数(約90度回転させるために調整)

    // 速度パラメータ
    const double LINEAR_SPEED_MPS = 1.0;     // 直進速度 (m/s)
    const double ANGULAR_SPEED_RPS = 1.57;   // 旋回速度 (rad/s, 約π/2)

    ROS_INFO("亀ロボット経路制御ノードが起動しました。");

    while (ros::ok()) {
        geometry_msgs::Twist velocity_command;
        ros::Duration elapsed_time = ros::Time::now() - state_start_time;

        if (current_state == RobotMovementState::MOVING_FORWARD) {
            velocity_command.linear.x = LINEAR_SPEED_MPS;
            velocity_command.angular.z = 0.0;
            if (elapsed_time.toSec() > FORWARD_DURATION_SEC) {
                current_state = RobotMovementState::TURNING;
                state_start_time = ros::Time::now();
                ROS_INFO("状態を旋回に切り替えました。");
            }
        } else { // current_state == RobotMovementState::TURNING
            velocity_command.linear.x = 0.0;
            velocity_command.angular.z = ANGULAR_SPEED_RPS;
            if (elapsed_time.toSec() > TURN_DURATION_SEC) {
                current_state = RobotMovementState::MOVING_FORWARD;
                state_start_time = ros::Time::now();
                ROS_INFO("状態を直進に切り替えました。");
            }
        }

        cmd_vel_publisher.publish(velocity_command);
        ros::spinOnce();
        loop_rate.sleep();
    }
    return 0;
}
ビルドと実行手順

まず、ROSワークスペースとパッケージを作成します。

  1. ワークスペースの作成と初期化:
    mkdir -p ~/my_ros_workspace/src
    cd ~/my_ros_workspace/src
    catkin_init_workspace
    cd ~/my_ros_workspace
    catkin_make
    echo "source ~/my_ros_workspace/devel/setup.bash" >> ~/.bashrc
    source ~/.bashrc
  2. パッケージの作成:
    cd ~/my_ros_workspace/src
    catkin_create_pkg turtle_control roscpp geometry_msgs
  3. C++ファイルの作成:

    `~/my_ros_workspace/src/turtle_control/src/turtle_path_publisher_node.cpp` として上記のコードを保存します。

  4. `CMakeLists.txt`の編集:

    `~/my_ros_workspace/src/turtle_control/CMakeLists.txt` の末尾に以下の行を追加します。

    add_executable(turtle_path_publisher_exec src/turtle_path_publisher_node.cpp)
    target_link_libraries(turtle_path_publisher_exec ${catkin_LIBRARIES})
    add_dependencies(turtle_path_publisher_exec turtle_control_generate_messages_cpp) # 必要に応じて追加
  5. コンパイル:
    cd ~/my_ros_workspace
    catkin_make
  6. 実行:

    ターミナルを3つ開きます。

    # ターミナル1: ROSマスターの起動
    roscore
    # ターミナル2: Turtlesimシミュレーターの起動
    rosrun turtlesim turtlesim_node
    # ターミナル3: パブリッシャーノードの起動
    rosrun turtle_control turtle_path_publisher_exec

1.2. サブスクライバーの実装:ロボット速度の受信

サブスクライバーノードは、パブリッシャーが`/turtle1/cmd_vel`トピックに公開するメッセージを購読し、受け取った速度情報を表示します。

C++コード例:速度情報を受信するサブスクライバー
#include <ros/ros.h>
#include <geometry_msgs/Twist.h>

// メッセージ受信時に呼び出されるコールバック関数
void velocityCallback(const geometry_msgs::Twist& msg_data) {
    ROS_INFO("新しい速度コマンドを受信しました!");
    ROS_INFO("  線形速度: X=[%f], Y=[%f], Z=[%f]",
             msg_data.linear.x, msg_data.linear.y, msg_data.linear.z);
    ROS_INFO("  角速度: X=[%f], Y=[%f], Z=[%f]",
             msg_data.angular.x, msg_data.angular.y, msg_data.angular.z);
}

int main(int argc, char** argv) {
    ros::init(argc, argv, "turtle_velocity_subscriber_node");
    ros::NodeHandle nh;

    // /turtle1/cmd_vel トピックを購読するサブスクライバーを作成
    // キューサイズは1000に設定
    ros::Subscriber vel_subscriber = nh.subscribe("/turtle1/cmd_vel", 1000, velocityCallback);

    // ROSノードがシャットダウンされるまでコールバック関数を待ち続ける
    ros::spin();

    return 0;
}
ビルドと実行手順
  1. C++ファイルの作成:

    `~/my_ros_workspace/src/turtle_control/src/turtle_velocity_subscriber_node.cpp` として上記のコードを保存します。

  2. `CMakeLists.txt`の編集:

    `~/my_ros_workspace/src/turtle_control/CMakeLists.txt` の末尾に以下の行を追加します。

    add_executable(turtle_velocity_subscriber_exec src/turtle_velocity_subscriber_node.cpp)
    target_link_libraries(turtle_velocity_subscriber_exec ${catkin_LIBRARIES})
    add_dependencies(turtle_velocity_subscriber_exec turtle_control_generate_messages_cpp)
  3. コンパイル:
    cd ~/my_ros_workspace
    catkin_make
  4. 実行:

    ROSマスターとTurtlesimが起動していることを確認し、別のターミナルでサブスクライバーノードとパブリッシャーノードを起動します。

    # ターミナル1: ROSマスター (roscore)
    # ターミナル2: Turtlesim (rosrun turtlesim turtlesim_node)
    
    # ターミナル3: パブリッシャーノード
    rosrun turtle_control turtle_path_publisher_exec
    # ターミナル4: サブスクライバーノード
    rosrun turtle_control turtle_velocity_subscriber_exec

    サブスクライバーノードのターミナルに、受信した速度情報が表示されます。

2. サービス(Services)

サービスは、リクエスト-レスポンス型の通信メカニズムです。クライアントがサービスサーバーにリクエストを送信し、サーバーはそのリクエストを処理してレスポンスを返します。これは、特定のタスクの実行、データの問い合わせ、設定変更など、単一の明確な応答が必要な場合に適しています。トピックとは異なり、双方向の通信が保証されます。

2.1. カスタムサービスメッセージの定義

サービス通信には、リクエストとレスポンスのデータ型を定義するカスタムサービス(.srv)ファイルが必要です。

.srvファイル例:学生情報問い合わせサービス

パッケージ`student_info_pkg`を作成し、その中に`srv`ディレクトリを作成します。

# ~/my_ros_workspace/src/student_info_pkg/srv/StudentInfo.srv
string student_name
---
bool is_enrolled
bool gender_male
int32 student_age
string character_trait

この定義では、`student_name`をリクエストとして送り、`is_enrolled`、`gender_male`、`student_age`、`character_trait`をレスポンスとして受け取ります。

パッケージ設定ファイルの変更

サービスファイルを定義したら、`package.xml`と`CMakeLists.txt`を修正して、ROSがメッセージを生成できるようにします。

  1. `package.xml` (`~/my_ros_workspace/src/student_info_pkg/package.xml`):

    以下の行を `` と `` セクションに追加します。

      <build_depend>message_generation</build_depend>
      <exec_depend>message_runtime</exec_depend>
      <build_depend>roscpp</build_depend>
      <exec_depend>roscpp</exec_depend>
      <build_depend>std_msgs</build_depend>
      <exec_depend>std_msgs</exec_depend>
      <build_depend>std_srvs</build_depend>
      <exec_depend>std_srvs</exec_depend>
  2. `CMakeLists.txt` (`~/my_ros_workspace/src/student_info_pkg/CMakeLists.txt`):

    `find_package`の`COMPONENTS`に`message_generation`と`std_srvs`を追加します。

    find_package(catkin REQUIRED COMPONENTS
      roscpp
      rospy
      std_msgs
      message_generation
      std_srvs
    )

    サービスファイルを指定します。

    add_service_files(
      FILES
      StudentInfo.srv
    )

    `generate_messages`に`DEPENDENCIES`を追加します。

    generate_messages(
      DEPENDENCIES
      std_msgs
      std_srvs
    )

    `catkin_package`の`CATKIN_DEPENDS`に`message_runtime`と`std_srvs`を追加します。

    catkin_package(
      # INCLUDE_DIRS include
      # LIBRARIES student_info_pkg
      CATKIN_DEPENDS roscpp std_msgs message_runtime std_srvs
      # DEPENDS system_lib
    )
  3. コンパイル:
    cd ~/my_ros_workspace
    catkin_make

    これにより、カスタムサービスメッセージのC++ヘッダーファイルが生成されます。

2.2. サービスサーバーの実装

サービスサーバーは、クライアントからのリクエストを受け取り、定義されたロジックに基づいてレスポンスを生成して返します。

C++コード例:学生情報提供サーバー
#include <ros/ros.h>
#include <student_info_pkg/StudentInfo.h> // 生成されたサービスヘッダーファイル

// サービスリクエスト処理用のコールバック関数
bool handleStudentInfoRequest(student_info_pkg::StudentInfo::Request& req,
                              student_info_pkg::StudentInfo::Response& res) {
    ROS_INFO("学生情報リクエストを受信しました: %s", req.student_name.c_str());

    if (req.student_name == "Alice") {
        res.is_enrolled = true;
        res.gender_male = false; // Aliceは女性
        res.student_age = 22;
        res.character_trait = "創造的";
        ROS_INFO("Aliceの情報を返します。");
    } else if (req.student_name == "Bob") {
        res.is_enrolled = true;
        res.gender_male = true; // Bobは男性
        res.student_age = 23;
        res.character_trait = "分析的";
        ROS_INFO("Bobの情報を返します。");
    } else {
        res.is_enrolled = false;
        res.gender_male = false; // デフォルト値
        res.student_age = 0;
        res.character_trait = "不明";
        ROS_WARN("学生 %s は登録されていません。", req.student_name.c_str());
    }
    return true; // 常にtrueを返し、サービスが成功したことを示す
}

int main(int argc, char **argv) {
    ros::init(argc, argv, "student_info_server_node");
    ros::NodeHandle nh;

    // "get_student_details"という名前のサービスをアドバタイズし、コールバック関数を登録
    ros::ServiceServer service = nh.advertiseService("get_student_details", handleStudentInfoRequest);
    ROS_INFO("学生情報サービスサーバーが起動し、リクエストを待機しています。");

    ros::spin(); // ノードがシャットダウンされるまで待機

    return 0;
}
ビルドと実行手順
  1. C++ファイルの作成:

    `~/my_ros_workspace/src/student_info_pkg/src/student_info_server_node.cpp` として上記のコードを保存します。

  2. `CMakeLists.txt`の編集:

    `~/my_ros_workspace/src/student_info_pkg/CMakeLists.txt` の末尾に以下の行を追加します。

    add_executable(student_info_server_exec src/student_info_server_node.cpp)
    target_link_libraries(student_info_server_exec ${catkin_LIBRARIES})
    add_dependencies(student_info_server_exec student_info_pkg_generate_messages_cpp)
  3. コンパイル:
    cd ~/my_ros_workspace
    catkin_make
  4. 実行:

    ROSマスターを起動し、サービスサーバーノードを実行します。

    # ターミナル1: ROSマスターの起動
    roscore
    # ターミナル2: サービスサーバーの起動
    rosrun student_info_pkg student_info_server_exec

    別のターミナルでテストします。

    rosservice call /get_student_details "Alice"
    rosservice call /get_student_details "Bob"
    rosservice call /get_student_details "Charlie"

2.3. サービスクライアントの実装

サービスクライアントは、サービスサーバーに対してリクエストを送信し、レスポンスを受け取ります。

C++コード例:学生情報問い合わせクライアント
#include <ros/ros.h>
#include <student_info_pkg/StudentInfo.h>
#include <iostream>
#include <string>

int main(int argc, char **argv) {
    ros::init(argc, argv, "student_info_client_node");
    ros::NodeHandle nh;

    // "get_student_details" サービスへのクライアントを作成
    ros::ServiceClient client = nh.serviceClient<student_info_pkg::StudentInfo>("get_student_details");
    student_info_pkg::StudentInfo srv_call; // サービスデータ構造

    std::string queried_name;

    while (ros::ok()) {
        std::cout << std::endl;
        std::cout << "問い合わせる学生名を入力してください (終了する場合は 'q'): ";
        std::cin >> queried_name;

        if (queried_name == "q") {
            break;
        }

        srv_call.request.student_name = queried_name;

        if (client.call(srv_call)) {
            if (srv_call.response.is_enrolled) {
                std::cout << "--- 取得した学生情報 ---" << std::endl;
                std::cout << "名前: " << queried_name << std::endl;
                std::cout << "性別: " << (srv_call.response.gender_male ? "男性" : "女性") << std::endl;
                std::cout << "年齢: " << srv_call.response.student_age << "歳" << std::endl;
                std::cout << "性格: " << srv_call.response.character_trait << std::endl;
            } else {
                std::cout << "学生 " << queried_name << " は登録されていません。" << std::endl;
            }
        } else {
            ROS_ERROR("サービス 'get_student_details' の呼び出しに失敗しました。サーバーが実行中か確認してください。");
            return 1;
        }
    }

    return 0;
}
ビルドと実行手順
  1. C++ファイルの作成:

    `~/my_ros_workspace/src/student_info_pkg/src/student_info_client_node.cpp` として上記のコードを保存します。

  2. `CMakeLists.txt`の編集:

    `~/my_ros_workspace/src/student_info_pkg/CMakeLists.txt` の末尾に以下の行を追加します。

    add_executable(student_info_client_exec src/student_info_client_node.cpp)
    target_link_libraries(student_info_client_exec ${catkin_LIBRARIES})
    add_dependencies(student_info_client_exec student_info_pkg_generate_messages_cpp)
  3. コンパイル:
    cd ~/my_ros_workspace
    catkin_make
  4. 実行:

    ROSマスターとサービスサーバーが起動していることを確認し、別のターミナルでクライアントノードを実行します。

    # ターミナル1: ROSマスター (roscore)
    # ターミナル2: サービスサーバー (rosrun student_info_pkg student_info_server_exec)
    
    # ターミナル3: サービスクライアント
    rosrun student_info_pkg student_info_client_exec

    クライアントのプロンプトで学生名(例: Alice, Bob, Charlie)を入力すると、対応する情報が表示されます。

3. アクション(Actions)

Actionlibライブラリによって提供されるアクションは、サービス通信を拡張したものです。サービスが単発のリクエスト-レスポンスに適しているのに対し、アクションは長時間かかるタスクや、実行中に進捗状況をフィードバックする必要があるタスクに適しています。クライアントはタスクのゴールをサーバーに送信した後も他の処理を続行でき、サーバーはタスクの進行状況を継続的にクライアントにフィードバックできます。また、タスクの中止もサポートしています。

3.1. カスタムアクションメッセージの定義

アクション通信には、ゴール、フィードバック、結果のデータ型を定義するカスタムアクション(.action)ファイルが必要です。

.actionファイル例:ロボット移動アクション

パッケージ`robot_navigation_action`を作成し、その中に`action`ディレクトリを作成します。

# ~/my_ros_workspace/src/robot_navigation_action/action/MoveRobot.action
# Goal定義
int32 target_distance_m
---
# Result定義
bool is_completed
---
# Feedback定義
int32 current_progress_m

この定義では、`target_distance_m`をゴールとして送り、`is_completed`を結果として受け取り、`current_progress_m`をフィードバックとして受信します。

パッケージ設定ファイルの変更

アクションファイルを定義したら、`package.xml`と`CMakeLists.txt`を修正して、ROSがメッセージを生成できるようにします。

  1. `package.xml` (`~/my_ros_workspace/src/robot_navigation_action/package.xml`):

    以下の行を `` と `` セクションに追加します。

      <build_depend>message_generation</build_depend>
      <exec_depend>message_runtime</exec_depend>
      <build_depend>actionlib</build_depend>
      <exec_depend>actionlib</exec_depend>
      <build_depend>actionlib_msgs</build_depend>
      <exec_depend>actionlib_msgs</exec_depend>
  2. `CMakeLists.txt` (`~/my_ros_workspace/src/robot_navigation_action/CMakeLists.txt`):

    `find_package`の`COMPONENTS`に`actionlib`と`actionlib_msgs`を追加します。

    find_package(catkin REQUIRED COMPONENTS
      roscpp
      rospy
      std_msgs
      actionlib
      actionlib_msgs
      message_generation
    )

    アクションファイルを指定します。

    add_action_files(
      FILES
      MoveRobot.action
    )

    `generate_messages`に`DEPENDENCIES`を追加します。

    generate_messages(
      DEPENDENCIES
      actionlib_msgs
    )

    `catkin_package`の`CATKIN_DEPENDS`に`actionlib`と`actionlib_msgs`を追加します。

    catkin_package(
      # INCLUDE_DIRS include
      # LIBRARIES robot_navigation_action
      CATKIN_DEPENDS roscpp std_msgs actionlib actionlib_msgs message_runtime
      # DEPENDS system_lib
    )
  3. コンパイル:
    cd ~/my_ros_workspace
    catkin_make

    これにより、カスタムアクションメッセージのC++ヘッダーファイルが生成されます。

3.2. アクションクライアントの実装

アクションクライアントは、アクションサーバーにゴールを送信し、タスクの実行中にフィードバックを受け取り、最終的な結果を処理します。

C++コード例:ロボット移動クライアント
#include <ros/ros.h>
#include <actionlib/client/simple_action_client.h>
#include <robot_navigation_action/MoveRobotAction.h> // 生成されたアクションヘッダーファイル

// アクション完了時に呼び出されるコールバック関数
void doneCallback(const actionlib::SimpleClientGoalState& state,
                  const robot_navigation_action::MoveRobotResultConstPtr& result) {
    ROS_INFO("アクションが完了しました!最終状態: %s", state.toString().c_str());
    if (result->is_completed) {
        ROS_INFO("ロボットは目標距離に到達しました。");
    } else {
        ROS_WARN("ロボットは目標距離に到達できませんでした。");
    }
    ros::shutdown(); // クライアントノードをシャットダウン
}

// アクションがアクティブになったときに呼び出されるコールバック関数
void activeCallback() {
    ROS_INFO("ゴールがアクティブになりました!ロボットは前進を開始します。");
}

// アクション実行中にフィードバックを受け取るコールバック関数
void feedbackCallback(const robot_navigation_action::MoveRobotFeedbackConstPtr& feedback) {
    ROS_INFO("ロボットは現在 %d メートル移動しました。", feedback->current_progress_m);
}

int main(int argc, char** argv) {
    ros::init(argc, argv, "robot_mover_client_node");
    ros::NodeHandle nh;

    // "move_forward_action"という名前のアクションサーバーへのクライアントを作成
    // trueを指定すると、ROSスピンを自動的に行う
    actionlib::SimpleActionClient<robot_navigation_action::MoveRobotAction> action_client("move_forward_action", true);

    ROS_INFO("アクションサーバーの起動を待機中...");
    action_client.waitForServer(); // サーバーが起動するまで待機
    ROS_INFO("アクションサーバーが起動しました。");

    // ロボットを15メートル前進させるゴールを作成
    robot_navigation_action::MoveRobotGoal goal_msg;
    goal_msg.target_distance_m = 15;

    // アクションのゴールをサーバーに送信し、各種コールバック関数をバインド
    action_client.sendGoal(goal_msg, &doneCallback, &activeCallback, &feedbackCallback);

    ros::spin(); // ROSノードを稼働させ、コールバックを処理

    return 0;
}
ビルドと実行手順
  1. C++ファイルの作成:

    `~/my_ros_workspace/src/robot_navigation_action/src/robot_mover_client_node.cpp` として上記のコードを保存します。

  2. `CMakeLists.txt`の編集:

    `~/my_ros_workspace/src/robot_navigation_action/CMakeLists.txt` の末尾に以下の行を追加します。

    add_executable(robot_mover_client_exec src/robot_mover_client_node.cpp)
    target_link_libraries(robot_mover_client_exec ${catkin_LIBRARIES})
    add_dependencies(robot_mover_client_exec robot_navigation_action_generate_messages_cpp)
  3. コンパイル:
    cd ~/my_ros_workspace
    catkin_make

3.3. アクションサーバーの実装

アクションサーバーは、クライアントからのゴールを受け取り、指定されたタスクを実行し、その過程でフィードバックを返し、最終的な結果をクライアントに報告します。

C++コード例:ロボット移動アクションサーバー
#include <ros/ros.h>
#include <actionlib/server/simple_action_server.h>
#include <robot_navigation_action/MoveRobotAction.h> // 生成されたアクションヘッダーファイル

// アクションゴールが受信されたときにタスクを実行する関数
void executeAction(const robot_navigation_action::MoveRobotGoalConstPtr& goal_ptr,
                   actionlib::SimpleActionServer<robot_navigation_action::MoveRobotAction>* action_server_ptr) {
    ros::Rate loop_rate(1.0); // 1 Hz
    robot_navigation_action::MoveRobotFeedback feedback_msg;
    robot_navigation_action::MoveRobotResult result_msg;

    ROS_INFO("新しいアクションゴールを受信しました: ロボットを %d メートル移動。", goal_ptr->target_distance_m);

    // ゴール達成のための模擬移動
    for (int current_meter = 1; current_meter <= goal_ptr->target_distance_m; ++current_meter) {
        // プリエンプション(中止要求)チェック
        if (action_server_ptr->isPreemptRequested() || !ros::ok()) {
            ROS_WARN("アクションが中断されたか、ROSがシャットダウンされました。");
            action_server_ptr->setPreempted(); // アクションを中断状態に設定
            result_msg.is_completed = false;
            return;
        }

        // 偶数メートルごとにフィードバックを公開
        if (current_meter % 2 == 0 || current_meter == goal_ptr->target_distance_m) {
            feedback_msg.current_progress_m = current_meter;
            action_server_ptr->publishFeedback(feedback_msg);
            ROS_INFO("フィードバック: 現在 %d メートル移動。", feedback_msg.current_progress_m);
        }
        loop_rate.sleep(); // ループレートに合わせて待機
    }

    ROS_INFO("アクションが正常に完了しました!ロボットは %d メートル移動。", goal_ptr->target_distance_m);
    result_msg.is_completed = true;
    action_server_ptr->setSucceeded(result_msg); // アクションを成功状態に設定
}

int main(int argc, char** argv) {
    ros::init(argc, argv, "robot_action_server_node");
    ros::NodeHandle nh;

    // "move_forward_action"という名前のアクションサーバーを作成
    actionlib::SimpleActionServer<robot_navigation_action::MoveRobotAction> server(
        nh, "move_forward_action", boost::bind(&executeAction, _1, &server), false);

    server.start(); // アクションサーバーを起動

    ROS_INFO("ロボット移動アクションサーバーが起動しました。ゴールを待機中...");

    ros::spin(); // ROSノードを稼働させ、コールバックを処理

    return 0;
}
ビルドと実行手順
  1. C++ファイルの作成:

    `~/my_ros_workspace/src/robot_navigation_action/src/robot_action_server_node.cpp` として上記のコードを保存します。

  2. `CMakeLists.txt`の編集:

    `~/my_ros_workspace/src/robot_navigation_action/CMakeLists.txt` の末尾に以下の行を追加します。

    add_executable(robot_action_server_exec src/robot_action_server_node.cpp)
    target_link_libraries(robot_action_server_exec ${catkin_LIBRARIES})
    add_dependencies(robot_action_server_exec robot_navigation_action_generate_messages_cpp)
  3. コンパイル:
    cd ~/my_ros_workspace
    catkin_make
  4. 実行:

    ROSマスターを起動し、アクションサーバーノード、次いでクライアントノードを起動します。

    # ターミナル1: ROSマスターの起動
    roscore
    # ターミナル2: アクションサーバーの起動
    rosrun robot_navigation_action robot_action_server_exec
    # ターミナル3: アクションクライアントの起動
    rosrun robot_navigation_action robot_mover_client_exec

    クライアントノードのターミナルにフィードバックが表示され、サーバーノードのターミナルに進行状況がログされます。

4. パラメーターサーバー(Parameter Server)

パラメーターサーバーは、ROSでグローバルな共有パラメーターを保存・取得するためのメカニズムです。ロボットの物理的な特性(寸法、センサーのオフセット)、アルゴリズムの設定値、システム全体の動作モードなど、頻繁に変わらない設定値を一元的に管理するのに適しています。パラメーターは主にYAMLファイルに記述され、`rosparam`コマンドまたは`roslaunch`ファイルを通じてサーバーにロードされ、ノードから読み取られます。

4.1. YAMLファイルの作成

パラメーターサーバーにロードする設定値は、通常YAML形式のファイルで定義します。

パッケージ`robot_config_pkg`を作成し、その中に`config`ディレクトリを作成します。

# ~/my_ros_workspace/src/robot_config_pkg/config/robot_settings.yaml
robot_base_width: 0.35
max_linear_speed: 1.2
camera_offset_x: 0.1
camera_offset_y: 0.05

4.2. YAMLファイルのロード

YAMLファイル内のパラメーターは、`rosparam`コマンドまたは`roslaunch`ファイルを使用してパラメーターサーバーにロードできます。

4.2.1. `rosparam`コマンドによるロード
  1. ROSマスターを起動:
    roscore
  2. YAMLファイルをロード:
    cd ~/my_ros_workspace/src/robot_config_pkg/config
    rosparam load robot_settings.yaml /robot_parameters

    ここで`/robot_parameters`は、ロードするパラメーターのネームスペースです。

  3. ロードされたパラメーターの確認 (任意):
    rosparam list
    rosparam get /robot_parameters/robot_base_width
  4. パラメーターの変更と保存 (任意):
    rosparam set /robot_parameters/max_linear_speed 1.5
    rosparam dump new_robot_settings.yaml /robot_parameters

    変更されたパラメーターを新しいYAMLファイルに保存します。

4.2.2. `roslaunch`コマンドによるロード

`roslaunch`を使用すると、ノードの起動と同時にパラメーターをロードできます。

  1. `launch`ファイルの作成:

    `~/my_ros_workspace/src/robot_config_pkg/launch/load_config.launch`として以下の内容でファイルを作成します。

    <launch>
        <!-- robot_settings.yaml ファイルを /robot_parameters ネームスペースにロード -->
        <rosparam command="load" file="$(find robot_config_pkg)/config/robot_settings.yaml" ns="/robot_parameters" />
    </launch>
    
  2. `launch`ファイルの実行:
    roslaunch robot_config_pkg load_config.launch
  3. ロードされたパラメーターの確認 (任意):
    rosparam list /robot_parameters

4.3. ノード内でのパラメーター読み込み

ROSノードは、`ros::NodeHandle`の`getParam()`メソッドを使用してパラメーターサーバーから値を読み取ることができます。

C++コード例:パラメーター読み込みノード

パッケージ`robot_config_pkg`がroscppに依存するように`package.xml`と`CMakeLists.txt`を更新しておきます。

#include <ros/ros.h>
#include <string>

int main(int argc, char **argv) {
    ros::init(argc, argv, "parameter_reader_node");
    ros::NodeHandle nh;

    double base_width, max_speed;
    double cam_offset_x, cam_offset_y;

    // パラメーターサーバーから値を取得
    // ネームスペース '/robot_parameters' 以下から取得
    if (nh.getParam("/robot_parameters/robot_base_width", base_width)) {
        ROS_INFO("ロボットのベース幅: %f m", base_width);
    } else {
        ROS_WARN("パラメーター '/robot_parameters/robot_base_width' が見つかりません。");
    }

    if (nh.getParam("/robot_parameters/max_linear_speed", max_speed)) {
        ROS_INFO("最大線形速度: %f m/s", max_speed);
    } else {
        ROS_WARN("パラメーター '/robot_parameters/max_linear_speed' が見つかりません。");
    }

    // カメラオフセットを読み込み、見つからなければデフォルト値を使用
    if (nh.getParam("/robot_parameters/camera_offset_x", cam_offset_x)) {
        ROS_INFO("カメラXオフセット: %f m", cam_offset_x);
    } else {
        cam_offset_x = 0.0; // デフォルト値
        ROS_WARN("パラメーター '/robot_parameters/camera_offset_x' が見つかりません。デフォルト値 %f を使用します。", cam_offset_x);
    }

    if (nh.getParam("/robot_parameters/camera_offset_y", cam_offset_y)) {
        ROS_INFO("カメラYオフセット: %f m", cam_offset_y);
    } else {
        cam_offset_y = 0.0; // デフォルト値
        ROS_WARN("パラメーター '/robot_parameters/camera_offset_y' が見つかりません。デフォルト値 %f を使用します。", cam_offset_y);
    }

    ROS_INFO("全てのパラメーター読み込みが完了しました。");
    ros::spin(); // ノードが終了するまで実行を続ける

    return 0;
}
ビルドと実行手順
  1. C++ファイルの作成:

    `~/my_ros_workspace/src/robot_config_pkg/src/parameter_reader_node.cpp` として上記のコードを保存します。

  2. `CMakeLists.txt`の編集:

    `~/my_ros_workspace/src/robot_config_pkg/CMakeLists.txt` の末尾に以下の行を追加します。

    add_executable(param_reader_exec src/parameter_reader_node.cpp)
    target_link_libraries(param_reader_exec ${catkin_LIBRARIES})
  3. コンパイル:
    cd ~/my_ros_workspace
    catkin_make
  4. 実行:

    まず、`roslaunch`でパラメーターをロードします。

    # ターミナル1: パラメーターをロードするlaunchファイルを起動
    roslaunch robot_config_pkg load_config.launch

    次に、別のターミナルでパラメーター読み込みノードを実行します。

    # ターミナル2: パラメーター読み込みノードを実行
    rosrun robot_config_pkg param_reader_exec

    ノードのログに、パラメーターサーバーから読み取られた値が表示されます。

タグ: ROS ROS通信 トピック サービス アクション

10月13日 05:40 投稿