【ROS】サービスコミュニケーション

サービス通信もROSでは非常に一般的な通信モードです。サービス通信は、応答メカニズムである要求応答モードに基づいています。つまり、ノードAが別のノードBに要求を送信し、Bが処理要求を受信して​​、応答結果をAに返します。たとえば、次のシナリオ:

ロボットのパトロール中、制御システムはセンサーデータを分析し、不審な物体や人を見つけます...この時点で、写真を撮って保管する必要があります。

上記のシナリオでは、サービス通信が使用されます。

  • ノードは写真リクエストをカメラノードに送信する必要があり、カメラノードはリクエストを処理し、処理結果を返します

上記のアプリケーションと同様に、サービス通信は、適時性を必要とし、特定のロジック処理を行うアプリケーションシナリオに適しています。

概念

異なるノード間のデータ相互作用の通信モードは、要求/応答方式で実現されます。

効果

これは、適時性と特定のロジック処理要件を必要とする時折のデータ送信シナリオに使用されます。

1サービス通信理論モデル

サービスコミュニケーションはトピックコミュニケーションよりも単純です。理論モデルを次の図に示します。モデルには3つの役割が含まれます。

  • ROSマスター(マネージャー)

  • サーバー(サーバー)

  • クライアント

ROSマスターは、サーバーとクライアントによって登録された情報を保持し、サーバーとクライアントを同じトピックに一致させて、サーバーとクライアントが接続を確立するのを支援します。接続が確立された後、クライアントは要求情報を送信し、サーバーは応答情報を返します。

プロセス全体は、次の手順で実現されます。

 

(0)。サーバー登録

サーバーが起動すると、サーバーはRPCを介してROSマスターに独自の情報を登録します。これには、提供されたサービスの名前が含まれます。ROSマスターは、ノードの登録情報をレジストリに追加します。

(1)クライアント登録

クライアントが起動すると、要求する必要のあるサービスの名前など、RPCを介してROSマスターに独自の情報も登録されます。ROSマスターは、ノードの登録情報をレジストリに追加します。

(2)ROSマスターが情報マッチングを実現

ROSマスターは、レジストリ内の情報に従ってサーバーとクライアントを照合し、サーバーのTCPアドレス情報をRPC経由でクライアントに送信します

(3)クライアントがリクエストを送信する

クライアントはTCPを使用して、手順2の応答情報に従ってサーバーとのネットワーク接続を確立し、要求されたデータを送信します。

(4)サーバーが応答を送信する

サーバーは要求されたデータを受信して​​解析し、応答結果を生成してクライアントに返します。

注意:

1.クライアント要求を処理するときは、サーバーが起動していることを確認する必要があります。

2.複数のサーバーとクライアントが存在する可能性があります。

2サービス通信カスタムサービス

要求する:

サービス通信では、クライアントが2つの整数をサーバーに送信し、サーバーが合計して結果をクライアントに応答します。サーバーとクライアント間の通信用のデータキャリアを作成してください。

srv =要求+応答

処理する:

srvファイルで使用可能なデータ型はmsgファイルと同じであり、srvを定義するプロセスはカスタムmsgのプロセスと同様です。

  1. 固定形式でsrvファイルを作成する

  2. 構成ファイルの編集

  3. 中間ファイルをコンパイルして生成する

(1)srvファイルを定義します

サービス通信では、データは要求と応答の2つの部分に---分割されます要求と応答はsrvファイルで分割されます。具体的な実装は次のとおりです。

関数パッケージの下に新しいsrvディレクトリを作成し、xxx.srvファイルとコンテンツを追加します。

# 客户端请求时发送的两个数字
int32 num1
int32 num2
---
# 服务器响应发送的数据
int32 sum

 

(2)構成ファイルを編集します

package.xmlにコンパイルの依存関係と実行の依存関係を追加します

  <build_depend>message_generation</build_depend>
  <exec_depend>message_runtime</exec_depend>
  <!-- 
  exce_depend 以前对应的是 run_depend 现在非法
	添加构建依赖 和 执行依赖
  -->
Copy

CMakeLists.txtsrv関連の構成を編集します

find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  message_generation
)
# 需要加入 message_generation,必须有 std_msgs

add_service_files(
  FILES
  Addints.srv
)

generate_messages(
  DEPENDENCIES
  std_msgs
)

注:公式ウェブサイトはcatkin_packageでmessage_runtimeを構成していません。テスト後に構成できます。

(3)コンパイル

コンパイルされた中間ファイルを表示します。

C ++が呼び出す必要のある中間ファイル(... / workspace / devel / include / package name / xxx.h)

Pythonが呼び出す必要のある中間ファイル(... / workspace / devel / lib / python2.7 / dist-packages / package name / srv)

その後、関連するsrvが呼び出されると、これらの中間ファイルから呼び出されます。

3  サービス通信カスタムsrv呼び出しA(C ++)

要求する:

サービス通信を書き込むために、クライアントは2つの整数をサーバーに送信し、サーバーは合計して結果をクライアントに応答します。

分析:

モデルの実装では、ROSマスターを実装する必要はなく、接続の確立がカプセル化されています。注意が必要な3つの重要なポイントがあります。

  1. サーバ

  2. クライアント

  3. データ

処理する:

  1. サーバー側の実装を記述します。

  2. クライアントの実装を記述します。

  3. 構成ファイルを編集します。

  4. コンパイルして実行します。

(0)vscode構成

c_cpp_properies.jsonファイルは、以前のカスタムmsg実装と同様に構成する必要があります。以前に構成したことがあり、ワークスペースが変更されていない場合は無視できます。構成する必要がある場合、構成方法は以前と同じです。 :

{
  "configurations": [
    {
      "browse": {
        "databaseFilename": "",
        "limitSymbolsToIncludedHeaders": true
      },
      "includePath": [
        "/home/winter/catkin_wp/devel/include/**",
        "/opt/ros/melodic/include/**",
        "/home/winter/catkin_wp/src/learning_parameter/include/**",
        "/home/winter/catkin_wp/src/learning_service/include/**",
        "/home/winter/catkin_wp/src/learning_tf/include/**",
        "/home/winter/catkin_wp/src/learning_topic/include/**",
        "/usr/include/**",
        "/home/winter/demo03_ws/devel/include/**"
      ],
      "name": "ROS",
      "intelliSenseMode": "gcc-x64",
      "compilerPath": "/usr/bin/gcc",
      "cStandard": "gnu17",
      "cppStandard":"c++17"
    }
  ],
  "version": 4
}

(1)サーバー

#include "ros/ros.h"
#include "plumbing_server_client/Addints.h"

/*
    服务端实现:解析客户端实现的数据,并运算再产生响应
        1 包含头文件
        2 初始化ros结点
        3 创建结点句柄
        4 创建服务对象
        5 处理请求并产生响应
        6 spin()函数
*/


//回调函数
bool doNums(plumbing_server_client::Addints::Request& request,
                            plumbing_server_client::Addints::Response& response)
{
    //处理请求
    int num1 = request.num1;
    int num2 = request.num2;

    ROS_INFO("服务器收到的请求数据是:num1 = %d,num2 = %d",num1,num2);

    //响应请求
    int num = num1 + num2;
    response.sum = num;
    ROS_INFO("求和结果是:sum = %d",num);
    return true;
}


int main(int argc, char  *argv[])
{
    //设置中文
    setlocale(LC_ALL,"");

    //2 初始化ros结点
    ros::init(argc,argv,"Server");     //名称保证唯一

    //3 创建结点句柄
    ros::NodeHandle nh;

    //4 创建服务对象
    ros::ServiceServer server = nh.advertiseService("Addints",doNums);

    ROS_INFO("服务端启动了");
    
    //5 回调函数

    //6 spin函数
    ros::spin();

    return 0;
}

構成ファイル

add_dependencies(demo01_server ${PROJECT_NAME}_generate_messages_cpp)

テスト

roscore
source ./devel/setup.bash
rosrun plumbing_server_client demo01_server


source ./devel/setup.bash 
rosservice call Addints "num1: 20
num2: 10" 
sum: 30

(2)クライアント

#include "ros/ros.h"
#include "plumbing_server_client/Addints.h"


/*
    客户端:提交两个整数,处理响应的结果
        1 包含头文件
        2 初始化ros结点
        3 创建结点句柄
        4 创建客户对象
        5 提交请求并处理响应
 
*/


int main(int argc, char  *argv[])
{
        setlocale(LC_ALL,"");
        //  2 初始化ros结点
        ros::init(argc,argv,"Client");

        // 3 创建结点句柄
        ros::NodeHandle nh;

        // 4 创建客户对象
        ros::ServiceClient client = nh.serviceClient<plumbing_server_client::Addints>("Addints");

        // 5 提交请求并处理响应

        plumbing_server_client::Addints ai;
        //组织请求
        ai.request.num1 = 100;
        ai.request.num2 = 200;

        //处理响应  客户端访问服务器
        bool flag = client.call(ai);
        if(flag==true)
        {
            ROS_INFO("响应成功");
            //获取结果
             ROS_INFO("响应结果是:%d",ai.response.sum);
        }
        else
        {
             ROS_INFO("处理失败");
        }

    return 0;
}

構成ファイル

roscore
source ./devel/setup.bash
rosrun plumbing_server_client demo01_server

rosrun plumbing_server_client demo02_client

(3)クライアントの最適化

データを動的に提供する

#include "ros/ros.h"
#include "plumbing_server_client/Addints.h"


/*
    客户端:提交两个整数,处理响应的结果
        1 包含头文件
        2 初始化ros结点
        3 创建结点句柄
        4 创建客户对象
        5 提交请求并处理响应

 实现参数的动态提交:
        1 格式 rosrun 包名 结点名 参数1 参数2
        2 节点执行时要获取命令中的参数,并组织进request   使用argc argv

*/


int main(int argc, char  *argv[])
{

           setlocale(LC_ALL,"");
        //优化实现,获取命令中的参数
        /*
            iargc  = 3
             argv[0] 是文件名
             argv[1] 是参数1
             argv[2] 是参数2
        */
        if(argc !=3)
        {
            ROS_INFO("提交的参数个数不对");
            return 1;
        }
       
     
        //  2 初始化ros结点
        ros::init(argc,argv,"Client");

        // 3 创建结点句柄
        ros::NodeHandle nh;

        // 4 创建客户对象
        ros::ServiceClient client = nh.serviceClient<plumbing_server_client::Addints>("Addints");

        // 5 提交请求并处理响应

        plumbing_server_client::Addints ai;
        //组织请求
        ai.request.num1 =atoi( argv[1]);
        ai.request.num2 = atoi(argv[2]);

        //处理响应  客户端访问服务器
        bool flag = client.call(ai);
        if(flag==true)
        {
            ROS_INFO("响应成功");
            //获取结果
             ROS_INFO("响应结果是:%d",ai.response.sum);
        }
        else
        {
             ROS_INFO("处理失败");
        }


    return 0;
}

最初にクライアントを起動してから、サーバーを起動します

最適化:

クライアントがリクエストを送信する前に追加します。

client.waitForExistence();

或:ros::service::waitForService("AddInts");

これはブロッキング機能であり、サービスが正常に開始された後にのみ実行を継続します

おすすめ

転載: blog.csdn.net/Zhouzi_heng/article/details/114602454