3-2- مثال Hello World با رآس

در این بخش، یک بسته پایه به‌نام hello_world و یک گره ناشر و یک گره مشترک برای ارسال پیام رشته­ای “Hello World” ایجاد می‌کنید. همچنین چگونگی نوشتن یک گره در C++ و پایتون را یاد می گیرید.

3-2-1- ایجاد یک بسته hello_world

در رآس، برنامه‌ها به شکل بسته­ها سازماندهی می­شوند. بنابراین قبل از نوشتن برنامه باید یک بسته رآس ایجاد کنیم.

برای ایجاد یک بسته رآس، باید نام بسته را وارد کرده، سپس بسته‌های وابسته به آنرا که به کامپایل برنامه‌های داخل بسته کمک می‌کنند ذکر می­کنیم. به عنوان مثال، اگر بسته­ای حاوی برنامه­ی C++ باشد، باید ‘roscpp’ را به عنوان بسته وابسته ذکر کنید و اگر حاوی برنامه پایتون باشد، باید ‘rospy’ را به عنوان وابستگی ذکر کنید.

قبل از ایجاد بسته، ابتدا به پوشه src بروید.

$ cd catkin_ws/src

$ catkin_create_pkg hello_world roscpp rospy std_msgs

شکل 5-7 خروجی را هنگام اجرای این فرمان نشان می‌دهد.

شکل ‏5‑7: خروجی catkin_create_pkg

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

محتویات فایل package.xml در شکل 5-8 نشان داده شده است. در عمل زمانی­که بسته­ای ایجاد می‌کنیم، برخی کامنت­ها در این فایل ذکر می شود. در اینجا برای خوانایی بیشتر کد، کامنت­ها پاک شده­اند.

شکل ‏5‑8: تعریف package.xml

شما می‌توانید این فایل را ویرایش کنید؛ وابستگی ها، اطلاعات بسته و سایر اطلاعات را به بسته اضافه کنید. درصورت تمایل می‌توانید در مورد package.xml اطلاعات بیشتری در ویکی راس[23] فرا بگیرید.

شکل 5-9 محتویات فایل CMakeLists.txt را نشان می‌دهد.

شکل ‏5‑9: تعریف CMakeLists.txt

در بالای فایل، حداقل نسخه CMake مورد نیاز برای ساختن بسته و نام پروژه ذکر شده است.

بسته­های وابسته­ی ضروری برای این بسته توسط تابع find_package() پیدا می‌شوند. اگر این بسته‌ها در دسترس نباشند، نمی‌توانیم این بسته را ایجاد کنیم. catkin_package() یک ماکرو CMake ارائه شده برای catkin است که برای تعیین اطلاعات خاص catkin به سیستم ساخت استفاده می‌شود.

درصورت تمایل می‌توانید در مورد CMakeLists.txt در ویکی رآس[24] اطلاعات بیشتری بیابید. مرجع خوبی برای ایجاد یک بسته رآس در ویکی رآس[25] قرار دارد.

3-2-2- ایجاد یک گره رآس در C++

پس از ایجاد بسته، قدم بعدی ایجاد گره‌های رآس است. کد C++ در پوشه src نگهداری می‌شود.

کد اولین گره رآس به صورت زیر است. این یک گره C++ برای انتشار پیام رشته­ای “Hello World” است. آنرا با عنوان src/talker.cpp ذخیره کنید.

#include “ros/ros.h”

#include “std_msgs/String.h”

#include <sstream>

int main(int argc, char **argv)

{

ros::init(argc, argv, “talker”);

ros::NodeHandle n;

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

ros::Rate loop_rate(10);

int count = 0;

while (ros::ok())

{

std_msgs::String msg;

std::stringstream ss;

ss << “hello world ” << count;

msg.data = ss.str();

ROS_INFO(“%s”, msg.data.c_str());

chatter_pub.publish(msg);

ros::spinOnce();

loop_rate.sleep();

++count;

}

return 0;

}

این کدی خود مفسر است. یک نمونه از پیام رشته­ای جدید و همچنین یک نمونه‌ از ناشر ایجاد می‌کند. پس از ایجاد هر دو نمونه، داده‌ها را همراه با تعداد به پیام رشته­ای اضافه می‌کند. پس از اضافه کردن داده­ها، تاپیک “chatter” را منتشر می‌کند. در کد کارکرد تابع ros :: spinOnce() را نیز می­ببینید. کد تا هنگام فشردن Ctrl+C اجرا می‌شود.

در ادامه کد listener.cpp که مشترک تاپیک منتشر شده توسط talker.cpp شده است را می بینید. پس از گرفتن داده­های تاپیک، پیام­ آن را چاپ می‌کند.

#include “ros/ros.h”

#include “std_msgs/String.h”

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

{

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

}

int main(int argc, char **argv)

{

ros::init(argc, argv, “listener”);

ros::NodeHandle n;

ros::Subscriber sub = n.subscribe(“chatter”, 1000, chatterCallback);

ros::spin();

return 0;

}

در listener.cpp، مشترک تاپیک “chatter” شده و تابع فراخوان chatterCallback برای تاپیک ثبت می شود. تابع فراخوان در ابتدای کد تعریف شده است. هر بار که پیامی روی تاپیک “chatter” می‌آید، این فراخوانی صورت می­پذیرد. در داخل تابع فراخوان، داده‌های پیام چاپ می‌شوند.

ros :: spin() توابع فراخوان اشتراک را فراخوانی کرده و گره را در حالت انتظار نگه­میدارد، به­طوریکه تا زمان فشرده نشدن Ctrl+C از آن خارج نخواهد شد.

3-2-3- ویرایش فایل CMakeLists.txt

پس از ذخیره دو فایل در پوشه hello_world/src، گره‌ها باید برای ایجاد فایل اجرایی کامپایل شوند. برای انجام این کار، باید فایل CMakeLists.txt را ویرایش کنیم که خیلی پیچیده نیست. تنها باید چهار خط زیر را به فایل CMakeLists.txt اضافه کرد. شکل 5-10 خطوطی که باید اضافه شوند را نشان می‌دهد.

شکل ‏5‑10: اضافه نمودن دستورالعمل ساخت در CMakeLists.txt

همانطور که می­ببینید add_executable() و target_link_libraries() را به CMakeLists.txt اضافه کردیم. add_executable() فایل اجرایی را از کد منبع ایجاد می‌کند که اولین پارامتر در آن نام فایل اجرایی است (در اینجا talker) که توسط target_link_libraries() به کتابخانه‌ها مرتبط می­شود. اگر این دو فرآیند با موفقیت انجام شوند، گره‌های قابل اجرا خواهیم داشت.

3-2-4- ساخت گره C++

پس از ذخیره CMakeLists.txt، می‌توانیم کد منبع را بسازیم. دستور ساخت گره‌ها catkin_make است. فقط کافی است به پوشه فضای­کاری بروید و دستور catkin_make را اجرا کنید.

با دستور زیر به پوشه catkin_ws بروید.

$ cd ~/catkin_ws

catkin_make را برای ساخت گره­ها اجرا کنید.

$ catkin_make

اگر همه چیز درست باشد، پیام موفقیت­آمیز بودن ساخت را دریافت می کنید (شکل 5-11 را ببینید).

شکل ‏5‑11: پیام موفقت­آمیز بودن ساخت

خوب، گره‌ها را با موفقیت ساختیم. حالا چی؟ آیا می‌توانیم این گره‌ها را اجرا کنیم؟ این سوال در قسمت بعدی جواب داده می­شود.

3-2-5- اجرای گره C++

پس از ساخت گره­ها، فایل­های اجرایی در داخل پوشه catkin_ws/devel/lib/hello_world/ ایجاد می‌شوند (شکل 5-12 را ببینید).

شکل ‏5‑12: فایل­های اجرایی تولید شده

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

سه ترمینال باز کرده و دستورات زیر را یک به یک اجرا کنید.

دستور شروع  roscore:

$ roscore

دستور زیر گره talker را اجرا می‌کند. از دستور rosrun برای شروع گره استفاده می­شود.

$ rosrun hello_world talker

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

$ rostopic list

خروجی دستور فوق به شکل زیر است:

/chatter

/rosout

/rosout_agg

/chatter تاپیک منتشر شده توسط گره talker است. تاپیک­های /rosout و /rosout_agg برای اهداف لاگینگ هستند و هنگام اجرای دستور roscore شروع به انتشار می کنند.

گره listener در ترمینال دیگری اجرا کنید.

$ rosrun hello_world listener

شکل 5-13 داده‌های پیام تاپیک /chatter را نشان می‌دهد.

شکل ‏5‑13: خروجی گره‌های ­C++ talker و listener

با فشار دادن کلیدهای ترکیبی Ctrl+C می‌توانید هر کدام از ترمینال­ها را ببندید.

در بخش بعد، به گره‌های گفتگو و شنونده در پایتون نگاه می‌اندازیم.

3-2-6- ایجاد گره‌های پایتون

پوشه­ای به‌نام script داخل بسته ایجاد کنید تا اسکریپت‌های پایتون را در داخل آن نگه­داری کنیم. برنامه اولی که قصد ساخت آنرا داریم talker.py است.

import rospy

from std_msgs.msg import String

def talker():

rospy.init_node(‘talker’, anonymous=True)

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

rate = rospy.Rate(10)                  # 10hz

while not rospy.is_shutdown():

hello_str = “Hello World %s” %rospy.get_time()

rospy.loginfo(hello_str)

pub.publish(hello_str)

rate.sleep()

if __name__ == ‘__main__’:

try:

talker()

except rospy.ROSInterruptException:

pass

در کد talker.py ابتدا ماژول rospy و ماژول‌های پیام رآس را وارد کرده­ایم. در تابع talker() می‌توانیم آغاز گره رآس و ایجاد یک ناشر جدید رآس را ببینیم. بعد از راه­اندازی گره، از حلقه زمانی استفاده کرده تا یک پیام رشته‌ای به‌نام “Hello World” را در تاپیک /chatter منتشر کنیم. کار این گره مشابه talker.cpp است که قبلا در مورد آن بحث کردیم.

گره مشترک به‌نام listener.py حاوی کد زیر را در پوشه spript ایجاد می­کنیم.

import rospy

from std_msgs.msg import String

def callback(data):

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

def listener():

# In ROS, nodes are uniquely named. If two nodes with the

# same name are launched, the previous one is kicked off. The

# anonymous=True flag means that rospy will choose a unique

# name for our ‘talker’ node so that multiple talkers can

# run simultaneously.

rospy.init_node(‘listener’, anonymous=True)

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

# spin() simply keeps python from exiting until this node

# is stopped

rospy.spin()

if __name__ == ‘__main__’:

listener()

گره شبیه به listener.cpp است. ابتدا گره راه­اندازی شده و مشترک تاپیک /chatter می شود. پس از اشتراک تاپیک، گره منتظر پیام‌های رآس می‌شود. انتظار با تابع rospy.spin() صورت می­پذیرد. در داخل تابع callback()، پیام تاپیک چاپ می‌شود.

3-2-7- اجرای گره‌های پایتون

در این بخش، خواهیم دید چگونه گره‌ها را اجرا ‌کنیم. نیازی به کامپایل گره‌های پایتون نیست. می‌توان آن­ها را با استفاده از دستورات زیر اجرا کرد. خروجی دستورات را می­توانید در شکل 5-14 ببینید.

شروع  roscore:

$ roscore

شروع  talker.py:

$ rosrun hello_world talker.py

شروع  listener.py:

$ rosrun hello_world listener.py

شکل ‏5‑14: خروجی گره‌های پایتون talker و listener

3-2-8- ایجاد فایل‌های راه­انداز

این بخش نحوه نوشتن فایل­های راه‌اندازی[26] برای گره‌های C++ و پایتون را توضیح می‌دهد. مزیت فایل‌های راه‌اندازی رآس این است که می‌توانیم هر تعداد گره را با یک دستور واحد اجرا کنیم.

بهتر است پوشه­ای به‌نام launch داخل بسته ایجاد کنیم تا فایل‌های راه‌اندازی را در آن پوشه نگه­داری کنیم.

محتویات فایل talker_listener.launch در ادامه آمده است که برای اجرای کد C++ استفاده می شود.

<launch>

<node name=”listener_node” pkg=”hello_world” type=”listener” output=”screen”/>

<node name=”talker_node” pkg=”hello_world” type=”talker” output=”screen”/>

</launch>

این فایل راه‌اندازی می‌تواند گره‌های talker و listener را در یک آن اجرا کند. نام بسته گره در فیلد pkg= و نام فایل اجرایی در فیلد type= درج می­شوند. شما می‌توانید هر نامی به گره اختصاص دهید البته بهتر است مشابه ‌نام اجرایی باشد.

پس از ذخیره فایل راه‌اندازی داخل پوشه راه‌اندازی، ممکن است مجبور به تغییر مجوز دسترسی باشید.

در زیر نحوه انجام این کار نشان داده شده است.

$ cd hello_world/launch

$ sudo chmod +x talker_listener.launch

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

$ roslaunch hello_world talker_listener.launch

در این دستور پس از واژه roslaunch، نام بسته و سپس نام فایل راه‌اندازی را ذکر می کنیم.

شکل 5-15 خروجی را نشان می‌دهد.

شکل ‏5‑15: خروجی فایل talker_listener.launch

برای راه‌اندازی گره‌های پایتون، از فایل راه‌اندازی زیر استفاده کنید. آن را با عنوان talker_listener_python.launch ذخیره کنید.

<launch>

<node name=”listener_node” pkg=”hello_world” type=”listener.py” output=”screen”/>

<node name=”talker_node” pkg=”hello_world” type=”talker.py”

output=”screen”/>

</launch>

پس از ذخیره آن، مجوزهای دسترسی فایل را نیز تغییر دهید.

$ hello_world/launch

$ sudo chmod +x talker_listener_python.launch

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

$ roslaunch hello_world talker_listener_python.launch

خروجی همانند گره‌های C++ است. فایل راه‌اندازی را با فشار دادن Ctrl + C در ترمینالی که در آن فایل راه‌اندازی در حال اجرا است، می توان متوقف کرد.

3-2-9- مشاهده گراف محاسبه کننده

آیا مایلید بدانید هنگام اجرای فایل‌های راه‌اندازی چه اتفاقی می‌افتد؟ ابزار گرافیکی rqt_graph نمودار محاسبات رآس را ترسیم می­کند.

یکی از فایل‌های راه‌اندازی که در قسمت قبلی ایجاد کردیم را اجرا کنید.

$ roslaunch hello_world talker_listener.launch

و در ترمینال دیگری، دستور زیر را اجرا کنید.

$ rqt_graph

شکل 5-16 خروجی این ابزار GUI را نشان می‌دهد.

شکل ‏5‑16: خروجی ابزار rqt_graph

در نمودار، talker_node را می‌بینید، که نامی ‌است که برای گره talker در فایل راه‌اندازی درج شد. listener_node نیز نام گره listener است. /chatter تاپیک منتشر شده توسط talker_node است که listener_node مشترک آن شده است.

پیام‌های خطایابی از این دو گره به گره /rosout می­روند. پیام‌های خطایابی، پیام­هایی ‌هستند که می­توانیم آنها را با استفاده از توابع خطایابی[27] رآس  چاپ ‌کنیم. قبلا در مورد این توابع بحث کرده­ایم. گره /rqt_gui نیز پیام­های خطایابی را به /rosout می‌فرستد.

این چگونگی کارکرد گراف محاسبات رآس بود.

[23] http://wiki.ros.org/catkin/package.xml

[24] http://wiki.ros.org/catkin/CMakeLists.txt

[25] http://wiki.ros.org/ROS/Tutorials/catkin/CreatingPackage

[26] Launch files

[27] http://wiki.ros.org/roscpp/Overview/Logging