サービス通信も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のプロセスと同様です。
-
固定形式でsrvファイルを作成する
-
構成ファイルの編集
-
中間ファイルをコンパイルして生成する
(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つの重要なポイントがあります。
-
サーバ
-
クライアント
-
データ
処理する:
-
サーバー側の実装を記述します。
-
クライアントの実装を記述します。
-
構成ファイルを編集します。
-
コンパイルして実行します。
(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");
これはブロッキング機能であり、サービスが正常に開始された後にのみ実行を継続します