در این آموزش قصد داریم تا نحوه نوشتن یک نود service و client به زبان C++ را آموزش دهیم.
در آموزشهای قبلی یاد گرفتید که چگونه میتوان بین نودها پیام ردوبدل کرد. شما قبلاً با مفهوم پیام و نحوه منتشر شدن آن توسط یک نود و گوش دادن به یک پیام توسط یک نود دیگر آشنا شدید. در اینجا میخواهیم روش دیگری برای ارتباط بین نودها که فراخوانی سرویس (call service) نام دارد را معرفی کنیم.
فراخوانی سرویس درواقع یک ارتباط دوطرفه است، به این مفهوم که یک نود زمانی که یک پیام به نود دیگر میفرستد، منتظر پاسخ میماند. درحالیکه در انتشار پیام پاسخی وجود ندارد و حتی ممکن است هیچ نودی به پیام منتشرشده گوش ندهد.
تفاوت دیگری که فراخوانی سرویس با پیام دارد، یکبهیک بودن ارتباط آن است، درحالیکه یک پیام ممکن است توسط نودهای مختلفی منتشر و یا گوش داده شود.
نحوه کار به این صورت است که نود Client یک درخواست به نود server میفرستد و منتظر پاسخ میماند. Server نیز با توجه به برنامهای که برای آن نوشته شده است دادههایی را بهعنوان پاسخ به client میفرستد.
نحوه ارتباط بین server و client را میتوان بهصورت زیر نشان داد.
ساخت یک نود سرویس
در اینجا ما یک نود سرویس به نام “add_two_ints_server” ایجاد خواهیم کرد که دو عدد را بهعنوان ورودی را دریافت میکند و جمع آنها را در خروجی برمیگرداند.
ابتدا مسیر فعلی خود را به مسیر بستهای که در آموزشهای مراحل قبل ایجاد کردهاید تغییر دهید.
[sourcecode language=”plain”]
roscd beginner_tutorials
[/sourcecode]
شما باید ساخت سرویس را که در گامهای قبلی آموزش داده شد را بهدرستی انجام داده باشید.
وارد پوشه src موجود در بسته خود شوید و در آن یک فایل به اسم add_two_ints_server.cpp ایجاد کنید (right-click > New Document > Empty Document) و کدهای زیر را کپی و در آن پیست کنید.
#include "ros/ros.h"
#include "beginner_tutorials/AddTwoInts.h"
bool add(beginner_tutorials::AddTwoInts::Request &req,
beginner_tutorials::AddTwoInts::Response &res)
{
res.sum = req.a + req.b;
ROS_INFO("request: x=%ld, y=%ld", (long int)req.a, (long int)req.b);
ROS_INFO("sending back response: [%ld]", (long int)res.sum);
return true;
}
int main(int argc, char **argv)
{
ros::init(argc, argv, "add_two_ints_server");
ros::NodeHandle n;
ros::ServiceServer service = n.advertiseService("add_two_ints", add);
ROS_INFO("Ready to add two ints.");
ros::spin();
return 0;
}
#include "ros/ros.h"
#include "beginner_tutorials/AddTwoInts.h"
اگر بخواهیم دستورات زیر را دقیق تر بررسی نماییم، beginner_tutorials/AddTwoInts.h فایل هدر تولید شده توسط فایل srv که در مراحل قبل ایجاد کردهایم، است.
bool add(beginner_tutorials::AddTwoInts::Request &req,
beginner_tutorials::AddTwoInts::Response &res)
این تابع یک سرویس ایجاد می کند تا دو عددد را با یکدیگر جمع کند (نوع دادههای مربوط به درخواست و پاسخ را قبلا در فایل srv مشخص کردهایم) و مقدار بولین را باز می گرداند
{
res.sum = req.a + req.b;
ROS_INFO("request: x=%ld, y=%ld", (long int)req.a, (long int)req.b);
ROS_INFO("sending back response: [%ld]", (long int)res.sum);
return true;
}
در بالا req.a و req.b مقادیری هستند که توسط client فرستاده شده است و res.sum مجموع این دو است که توسط سرور به client فرستاده می شود. دستور ROS_INFO نیز به منظور نمایش این مقادیر در ترمینال استفاده شده است. حال در صورتی که این فرایند به صورت کامل انجام شد آنگاه مقدار true را باز می گرداند.
ros::ServiceServer service = n.advertiseService("add_two_ints", add);
تا اینجا تابع نوشته شده است حال باید شئ از کلاس ServiceServer ایجاد نماییم. درپایین شئ از این کلاس با نامservice ایجاد می شود. و در ROS منتشر می شود(به اصلاح advertised).
ساخت یک نود client
فایلی با نام add_two_ints_client.cpp در پوشه src بسته beginner_tutorials ایجاد کنید. کدهای زیر را داخل فایل ایجاد شده قرار دهید.
#include "ros/ros.h"
#include "beginner_tutorials/AddTwoInts.h"
#include <cstdlib>
int main(int argc, char **argv)
{
ros::init(argc, argv, "add_two_ints_client");
if (argc != 3)
{
ROS_INFO("usage: add_two_ints_client X Y");
return 1;
}
ros::NodeHandle n;
ros::ServiceClient client = n.serviceClient<beginner_tutorials::AddTwoInts>("add_two_ints");
beginner_tutorials::AddTwoInts srv;
srv.request.a = atoll(argv[1]);
srv.request.b = atoll(argv[2]);
if (client.call(srv))
{
ROS_INFO("Sum: %ld", (long int)srv.response.sum);
}
else
{
ROS_ERROR("Failed to call service add_two_ints");
return 1;
}
return 0;
}
همانطور که مشاهده می کنید. بعد از مقدار دهی اولیه راس با این نود ( به کمک ros::init) بررسی می شود که تعداد ورودی نود مخالف ۳ باشد از گره خارج شود و در غیر اینصورت به کمک دستور زیر یک Client برای سرویس add_two_ints ایجاد می کند.
ros::ServiceClient client = n.serviceClient<beginner_tutorials::AddTwoInts>("add_two_ints");
حال نیازمند ایجاد یک سرویس هستیم. از این رو یک شئ از کلاس AddTwoInts ایجاد می کنیم و سپس مقادیر ورودی نود را به اعضای مربوط به درخواست انتساب می دهیم. دقت کنید که اعضای کلاس service، request و response هستند.
beginner_tutorials::AddTwoInts srv;
srv.request.a = atoll(argv[1]);
srv.request.b = atoll(argv[2]);
حال باید متد call از شئ ایجاد شده client را با ورودی srv (شئ ایجاد شده از کلاس AddTwoInts ) به صورت زیر فراخوانی نمود.
حال در صورتی که این متد بالا فراخوانی شود آنگاه مقدار True باز می گرداند و در نتیجه srv.response.sum که همان مقدار نتیجه مجموع دو ورودی است که توسط سرور محاسبه شده است را نمایش می دهد. و در غیر این صورت خطایی را ثبت می کند.
تا اینجای کار شما فایل های مرتبط را ایجاد کرده اید. اما نیاز است تا این فایلها را به cmake اضافه کنید تا قابل اجرا باشد.
فایل CMakeLists.txt از از همین پروژه را در مسیر ~/catkin_ws/src/beginner_tutorials/CMakeLists.txt
را باز نمایید. و خطوط زیر را به آن اضافه نماید و در نهایت فایل را ذخیره کنید. این خطوط به منظور قابل اجرا کردن فایل های add_two_ints_server و add_two_ints_client است.
add_executable(add_two_ints_server src/add_two_ints_server.cpp)
target_link_libraries(add_two_ints_server ${catkin_LIBRARIES})
add_dependencies(add_two_ints_server beginner_tutorials_gencpp)
add_executable(add_two_ints_client src/add_two_ints_client.cpp)
target_link_libraries(add_two_ints_client ${catkin_LIBRARIES})
add_dependencies(add_two_ints_client beginner_tutorials_gencpp)
حال نوبت به اجرای catkin_make است. با این کار پروژه بیلد شده و قابل اجرا می شود. برای این منظور دستورات زیر ا به ترتیب در ترمینال خود اجرا نمایید.
[sourcecode language=”plain”]
cd ~/catkin_ws
catkin_make
[/sourcecode]
در صورتی که با خطایی همراه است لطفا گام هشتم آموزش مقدماتی را باز بینی نمایید. و یا توسط پشتیبانی آنلاین ایران رآس (آیکن موجود در پایین سمت چپ مرورگر) این خطا را برای ما بفرستین تا شما را راهنمایی کنیم.
نحوه اجرای یک service و client ساده (C++)
ابتدا هسته ros را با دستور roscore اجرا نمایید. سپس یک ترمینال جدید باز کنید. حال ابتدا سرور را اجرا نمایید. به منظور اجرای سرور دستور زیر را وارد نمایید.
[sourcecode language=”plain”]
rosrun beginner_tutorials add_two_ints_server
[/sourcecode]
با اجرای این دستور خروجی مشابه زیر مشاهده خواهید کرد.
[sourcecode language=”plain”]
Ready to add two ints.
[/sourcecode]
حال به منظور اجرای client دستور زیر را وارد نمایید. فرض کنید که ما میخواهیم اعداد ۵ و ۶ را با یکدیگر جمع کنیم. برای این منظور دستور زیر را باید در ترمینال وارد کنید.
[sourcecode language=”plain”]
rosrun beginner_tutorials add_two_ints_client 5 6
[/sourcecode]
در ترمینال ciletn خروجی زیر را باید مشاهده نمایید.
[sourcecode language=”plain”]
Sum: 11
[/sourcecode]
در ترمینال مربوط به سرور خروجی مشابه زیر باید مشاهده نمایید.
[sourcecode language=”plain”]
request: x=5, y=6sending back response: [11]
[/sourcecode]
درصورتیکه بخواهید این برنامهها را متوقف کنید تنها کافی است که دکمههای Ctrl+c را همزمان در ترمینال وارد کنید و یا ترمینال را ببندید. با این کار نودهای در حال اجرا حذف میشوند؛ و برنامه متوقف خواهد شد.