3-1- roscpp  و rospy

این بخش جنبه‌های مختلف نوشتن گره با استفاده از کتابخانه‌های مشتری مانند roscpp و rospy را بررسی می‌کند که شامل ماژول‌ها و فایل‌های سربرگ مورد استفاده در گره‌های رآس، راه‌اندازی یک گره رآس، انتشار و اشتراک یک تاپیک و غیره می‌باشد.

3-1-1- ماژول­ها و فایل­های سربرگ رآس

هنگامی‌که کدی را در C++ می­نویسیم، بخش اول آن دربردارنده­ی فایل‌های سربرگ است. به طور مشابه، وقتی کد Python می­نویسیم، بخش اول آن ماژول­های پایتون را وارد می‌کنیم. در این بخش، به فایل‌های سربرگ و ماژول‌های مهمی که باید در گره رآس وارد شوند، نگاهی می­اندازیم.

برای ایجاد گره C++ در رآس، باید فایل‌ سربرگ زیر را اضافه کنیم.

#include “ros/ros.h”

ros.h دربردارنده تمامی سربرگ­هایی است که برای اجرای قابلیت‌های رآس مورد نیاز هستند. گره رآس را بدون این فایل سربرگ نمی‌توان ایجاد کرد.

نوع بعدی فایل سربرگ در گره‌های رآس، فایل سربرگ پیام رآس است. اگر بخواهیم از یک نوع خاص پیام در گره­مان استفاده کنیم، باید فایل سربرگ پیام را وارد کنیم. رآس برخی از تعاریف پیام­های پیش­ساخته را درون خود دارد؛ کاربر نیز می‌تواند تعریف پیام جدیدی ایجاد کند. یک بسته پیام پیش­ساخته در رآس به­نام std_msgs وجود دارد که تعریف پیام انواع داده‌های استاندارد مانند عدد صحیح، اعشاری، رشته­ و غیره را دربردارد. برای مثال، اگر بخواهیم پیامی از نوع رشته را در کدمان وارد کنیم، از خط زیر استفاده می­کنیم.

#include “std_msgs/String.h”

که در نام پیام فوق، قسمت اول نام بسته و قسمت بعدی نوع پیام است. اگر یک نوع پیام سفارشی وجود داشته باشد، آنرا به شکل زیر فراخوانی می کنیم.

# include “msg_pkg_name/message_name.h”

در ادامه برخی از پیام‌هایی که در بسته std_msgs هستند را می بینید:

# include “std_msgs/Int32.h”

# include “std_msgs/Int64.h”

فهرست کامل انواع پیام داخل بسته std_msgs در ویکی رآس[15] موجود است.

در پایتون، برای ایجاد یک گره رآس، ماژول‌ها را وارد می­کنیم. ماژول رآس که باید وارد شود عبارت است از:

import rospy

rospy دارای تمام توابع مهم رآس است. برای وارد کردن یک نوع پیام، باید ماژول‌های خاصی وارد کنیم؛ همانند آنچه در C++ انجام دادیم.

در زیر مثالی از وارد کردن یک نوع رشته پیام در پایتون را می بینید.

from std_msgs.msg import String

در پایتون باید از فرمت package_name.msg استفاده کرده و نوع پیام مورد نظر را در ادامه آن وارد کنیم.

3-1-2- شروع یک گره ی رآس

در ابتدای شروع هر گره رآس تابعی اجباری درج می­شود که تابع راه­انداز گره[16] نامیده می شود.

در C++، با استفاده از خط زیر کد را شروع می­کنیم.

int main(int argc, char **argv)

{

ros::init(argc, argv, “name_of_node”)

…………………

}

بعد از تابع int main() باید ros::init() را وارد کرده تا گره رآس را راه­اندازی ‌کند. نام گره و گزاره­های خط‌فرمان[17] argc و argv را می‌توانیم در تابع init() درج کنیم. این همان نام گره رآس است که می توان آنرا با دستور rosnode list بازیابی کرد.

در پایتون، از خط زیر استفاده می­کنیم.

rospy.init_node(‘name_of_node’, anonymous=True);

اولین گزاره نام گره است و دومین گزاره anonymous=True است، یعنی گره می‌تواند در نمونه­های چندگانه اجرا شود.

3-1-3- چاپ کردن پیام‌ها در یک گره رآس

رآس ، APIهایی برای پیام‌های لاگ فراهم کرده است. این پیامها رشته­های خوانایی هستند که وضعیت گره را گزارش می کنند.

در C++، توابع زیر پیام‌های گره را لاگ می‌کنند.

ROS_INFO(string_msg,args): Logging the information of node

ROS_WARN(string_msg,args): Logging warning of the node

ROS_DEBUG(string_msg ,args): Logging debug messages

ROS_ERROR(string_msg ,args): Logging error messages

ROS_FATAL(string_msg ,args): Logging Fatal messages

Eg: ROS_DEBUG(“Hello %s”,”World”);

در پایتون، توابع مختلفی برای عملیات لاگینگ وجود دارد.

rospy.logdebug(msg, *args)

rospy.logerr(msg, *args)

rospy.logfatal(msg, *args)

rospy.loginfo(msg, *args)

rospy.logwarn(msg, *args)

3-1-4- ایجاد یک آغازگر[18] رآس

پس از شروع گره، باید یک نمونه[19] از NodeHandle ایجاد کنیم تا گره رآس و سایر عملیات­ها مانند انتشار/ اشتراک تاپیک را آغاز ‌کند. از نمونه ros::NodeHandle برای ایجاد این عملیات­ها استفاده می‌کنیم.

در C++، چگونگی ایجاد یک نمونه از آغازگر گره به شکل زیر است:

ros::NodeHandle nh;

در ادامه از نمونه nh در عملیات­های گره استفاده می‌شود. در پایتون، نیازی به ایجاد آغازگر نیست زیرا ماژول rospy به صورت داخلی آنرا اجرا می‌کند.

3-1-5- ایجاد یک تعریف پیام رآس

قبل از انتشار یک تاپیک، باید یک تعریف پیام[20] رآس ایجاد کنیم. تعریف پیام با استفاده از روش‌های زیر ایجاد می‌شود.

در ++C، خط زیر یک نمونه‌ از یک پیام رآس را در کد ایجاد می کند؛ درواقع نمونه msg از پیام std_msgs / String ایجاد شده است.

std_msgs::String msg;

پس از ایجاد یک نمونه از پیام رآس، می‌توان داده‌ها را با استفاده از خط کد زیر اضافه کنیم.

msg.data = “String data”

در پایتون، از خطوط زیر برای اضافه کردن داده‌ها به پیام رشته­ای در کد استفاده می‌کنیم.

msg = String()

msg.data = “string data”

3-1-6- انتشار یک تاپیک در گره رآس

این بخش نحوه انتشار یک تاپیک در گره رآس را نشان می‌دهد.

در ++C به شکل زیر عمل می کنیم.

ros::Publisher publisher_object = node_handle.advertise<ROS message type >(“topic_name”,1000)

پس از ایجاد شیء ناشر، دستور publish() پیام رآس را از طریق تاپیک ارسال می‌کند.

publisher_object.publish(message)

مثال:

ros::Publisher chatter_pub = nh.advertise<std_msgs::String>(“chatter”, 1000);

chatter_pub.publish(msg);

در این مثال، chatter_pub نمونه‌ای از ناشر رآس است که می­خواهد تاپیکی با نام chatter را با نوع پیام std_msgs/String منتشر کند. اندازه صف[21] برابر 1000 است.

در پایتون نحوه­ی انتشار به صورت زیر است.

الگوی عملکرد:

publisher_instance = rospy.Publisher(‘topic_name’, message_

type, queue_size)

مثال:

pub = rospy.Publisher(‘chatter’, String, queue_size=10)

pub.publish(hello_str)

این مثال تاپیکی به نام chatter با نوع پیام std_msgs/String و اندازه صف 10 را منتشر می‌کند.

3-1-7- اشتراک یک تاپیک در گره رآس

هنگام انتشار یک تاپیک، یک نوع پیام ایجاد کرده و آنرا از طریق تاپیک ارسال می­کنیم. هنگام اشتراک یک تاپیک، پیام از تاپیک دریافت می‌شود.

در ++C، نحوه­ی اشتراک یک تاپیک به شکل زیر است.

ros::Subscriber subscriber_obj = nodehandle.subscribe

(“topic_name”, 1000, callback function)

هنگام اشتراک یک تاپیک، نیازی به اشاره به نوع پیام تاپیک نیست، اما باید نام تاپیک و نام یک تابع فراخوان[22] را ذکر کنیم. تابع فراخوان یک تابع تعریف شده توسط کاربر است که بلافاصله پس از دریافت پیام رآس از تاپیک اجرا می‌شود. در داخل تابع فراخوان، می‌توانیم پیام رآس را دستکاری کرده، آنرا چاپ کرده و یا بر اساس اطلاعات پیام کاری انجام دهیم. (تابع فراخوان در بخش بعدی بحث می­شود.)

در زیر مثالی از اشتراک تاپیک “chatter” با تابع فراخوانی “chatterCallback” آمده است.

ros::Subscriber sub = nh.subscribe(“chatter”, 1000,

chatterCallback);

در ادامه نحوه­ی اشتراک یک تاپیک در پایتون نشان داده شده است.

rospy.Subscriber(“topic_name”,message_type,callback funtion name”)

مثالی از چگونگی اشتراک تاپیک “chatter” با نوع پیام sting و تابع فراخوان callback در پایتون به شکل زیر است. در پایتون، باید نوع پیام را ذکر کنیم.

rospy.Subscriber(“chatter”, String, callback)

3-1-8- نوشتن تابع فراخوان در گره رآس

اگر مشترک یک تاپیک رآس شده باشیم؛ هنگامی‌که پیامی در آن تاپیک وارد شود تابع فراخوانی اجرا می‌شود. می­توانید نام تابع فراخوان را در تابع subscribe() ببینید. در ادامه الگوی تابع فراخوان و مثالی از عملکرد آنرا در C++ می­بینید.

void callback_name(const ros_message_const_pointer &pointer)

{

// Access data

pointer->data

}

در زیر می بینیم که چگونه یک پیام رشته­ای رآس را گرفته و داده­های آنرا چاپ کنیم.

void chatterCallback(const std_msgs::String::ConstPtr& msg)

{

ROS_INFO(“I heard: [%s]”, msg->data.c_str());

}

در زیر نحوه نوشتن تابع فراخوان در پایتون نشان داده می‌شود که بسیار شبیه به یک تابع Python است که دارای گزاره­ای است که داده‌های پیام را دربردارد.

def callback(data):

rospy.loginfo(rospy.get_caller_id() + “I heard %s”, data.data)

3-1-9- کاربرد تابع ROS spin در گره رآس

پس از شروع شدن اشتراک یا انتشار، تابعی جهت پردازش درخواست اشتراک و انتشار فرا می­خوانیم. در گره C++، بعد از انتشار یک تاپیک باید تابع ros::spinOnce() فراخوانده شود و اگر فقط مشترک تاپیکی می­شوید باید تابع ros::spin() فراخوانده شود. اگر هر دو را انجام می‌دهید، از تابع spinOnce() استفاده کنید.

در پایتون تابع spin() وجود ندارد، اما می‌توانید از تابع rospy.sleep() بعد از انتشار، و یا اگر فقط مشترک تاپیکی می­شوید از تابع rospy.spin() استفاده کنید.

3-1-10- تابع ROS Sleep در یک گره رآس

اگر بخواهیم نرخ ثابتی داخل یک حلقه درون گره داشته باشیم، از تابع ros::Rate استفاده می­کنیم. می‌توانیم نمونه­ای از ros::Rate و نرخ مدنظر را در آن ذکر کنیم. پس از ایجاد نمونه، باید از تابع sleep() داخل آن برای اعمال نرخ استفاده کنیم.

در زیر مثالی از اعمال نرخ 10 هرتز در C++ را می بینید.

ros::Rate r(10);                    // 10 hz

r.sleep();

چگونگی انجام آن در پایتون به شکل زیر است:

rate = rospy.Rate(10)            # 10hz

rate.sleep()

3-1-11- تنظیم و گرفتن یک پارامتر در رآس

در C++، برای استفاده از یک پارامتر به شکل زیر در کد استفاده می‌کنیم. اساسا، باید متغیری را اظهار کنیم. سپس برای گرفتن پارامتر مورد نظر از تابع getParam() در node_handle استفاده کنیم.

std::string global_name;

if (nh.getParam(“/global_name”, global_name))

{

}

در زیر نحوه تنظیم­کردن پارامتر رآس نشان داده شده است. نام پارامتر و مقدار آن باید در داخل تابع setParam() ذکر شود.

nh.setParam(“/global_param”, 5);

در پایتون، می‌توانیم همان کار را با استفاده از کد زیر انجام دهیم.

global_name = rospy.get_param(“/global_name”)

rospy.set_param(‘~private_int’, ‘2’)

[15] http://wiki.ros.org/std_msgs

[16] initializes the node

[17] Command-line arguments

[18] ROS handle

[19] instance

[20] ROS message definition

[21] queue

[22] Callback function