[ROS] Part 4 ,Structure of Packages, Modules
DISCLIAMER : คือจุดประสงค์การเขียนบทความพวกนี้สำหรับคนที่อยากเริ่มจริงๆ และไม่อยากจะกระโจนเข้าไปในอะไรที่มัน หยึบหยับเต็มไปหมด
เราได้ทำการสรุปแบบโง่ๆ (ย้ำ — แบบโง่ๆ) และย่อยมา (จนบางทีใจความแท้จริงอาจโดนเปลี่ยนไปบ้าง เพิ่อทำให้เข้าใจขั้นแรกก่อน)
นี่เป็นบทความใน Series ROS Tutorial by iTUTOR นะคับ
Part 1 — [ROS] Part 1, Introduction + Installation !
Part 2 — [ROS] Part 2, Concept of Modularity
Part 3 — [ROS] Part 3, ROS Workspaces
(ปล 29-สิงหา-2019 จะลองเปิด metered paywall ดูเน้อ)
หลังจากที่ลำบากตรากตรำและทำตาม Tutorial มาตั้ง 3 Part
ตอนนี้เราจะได้มาดูจริงๆละว่า Packages ของ ROS มันคืออะไรและหน้าตาเป็นยังไง
สำหรับใครที่เพิ่งหลงเข้ามา แนะนำให้ไปอ่าน Tutorial บทต้นๆไล่มานะครับ อ่านไม่นานหรอก อิอิ (เพราะสั้นมาก55) เพื่อทำความเข้าใจกับ ROS ให้มองเป็นภาพเดียวกันก่อนเน้ออ
สำหรับคนที่อ่านมาถึงตรงนี้
หลังจากนี้จะพยายาม Introduce ทุกคนให้รู้จัก REP ของ ROS กันครับ
REP (ROS Enhancement Proposals)
คือ กฏเกณฑ์ที่คนทำ ROS — อยากให้— คนที่รับไปใช้งานได้ Follow ตาม
Node , Code ที่เขียนออกมาจะได้มี มาตรฐานตรงกันฮะ
http://www.ros.org/reps/rep-0000.html
ขู่ — บทความนี้ต้องใช้การทำตาม ในการสร้างความเข้าใจ !!!!! และ ใช้เวลากับมันด้วยนะ
พูดถึงโปรแกรมในหุ่นยนต์
เรามักจะโปรแกรมอะไรต่อมิอะไรเป็นเรื่องๆไป เพื่อที่จะได้แบ่งการทำงานออกให้ชัดเจน แล้วเราจะรวมกลุ่มโปรแกรมหรือ Code ของเรายังไงล่ะ ให้เก็บเป็นสัดเป็นส่วน (และ ให้เราเรียกใช้งานได้ง่ายๆผ่านตัวช่วยต่างๆของ ROS)
โดยการสร้าง Package ใน ROS จะมี Layout องค์ประกอบดังนี้
จะพยายามย่อย และทำ REP ของ ROS ให้ออกมาดูง่ายๆละกันนะคับ :)
อ้างอิง — http://www.ros.org/reps/rep-0128.html#recommended-layout
- Package Manifest — ตัวที่จะบอกว่า Package นี้ มีรายละเอียดยังไง ต้องการไปพึ่งพิง Code คนอื่นไหม (ตัวช่วย Compile Code , Reference Code ใน Level สูงๆ จะอ่านตรงนี้ และไปอ้างอิงให้)
- CMakeLists.txt — เป็น พิมพ์เขียวของงานนี้ โดยจะเก็บรายละเอียดว่า เรา Run Code ไฟล์ไหนเป็นจุดเริ่มต้นของงาน, Code ต่างๆอ้างอิงกันยังไง , เราอ้างอิง Code Library ของชาวบ้านคนอื่นๆมาใช้กับ Code เราไฟล์ไหนบ้าง
* CMakeLists (MakeFile) ตัวนี้สำคัญ * - Codeของเรา จะเก็บใน Folder ดังนี้
(อ้างอิงตาม REP ซึ่งก็คือข้อควรปฏิบัติตามของ ROS
ซึ่งจะทำตามหรือไม่ทำตามก็ได้ แต่ระวังลำบาก 5555 )
1. “src” — จะเก็บ Source Code ภาษา C++ ที่เป็นส่วน Implementation
(ส่วนของ Header จะเก็บแยก)
2. “include/ชื่อPackage/”- จะเก็บ Header ภาษา C++
3. “script” — จะเก็บ Code Python
* เอ้อ เวลาเขียน Package นึง มันจะมีได้หลาย Node หลายภาษา
เวลาใช้งานก็แบ่งส่วนดีๆล่ะ * - Config ต่างๆ
อันนี้ไม่ซีเรียสมาก แต่ปกติจะสร้าง Folder ชื่อ param , config บ้าง
แล้วแต่คนชอบ
[ advance : พวก ตัวแปรที่เป็นแบบ Dynamic Parameter
(ปรับ ณ เวลาที่ Run Code ได้ ตัวต้นแบบ Config จะอยู่ใน Folder “cfg” ] - ตัวสร้าง Message , Service , Action ของ ROS หรือที่เรียก Message Structure , Message Prototype
1. ROS Message — จะอยู่ใน Folder “msg”
2. ROS Service — จะอยู่ใน Folder “srv”
3. ROS Action — จะอยู่ใน Folder “action”
โดย Catkin build system จะให้ ROS มาคว้า “หน้าตา” ของ Message Service และ Action จากบริเวณนี้ Auto เลย
* ก็อย่างที่เคยบอกไป คือ จริงๆแล้ว จะตั้งชื่ออะไรก็ได้ .. แล้วไปแก้ใน CMakeLists.txt เอาเน้อ *
อยากจะเขียน Message , Service , Action เอง
แบบไม่ได้ใช้ของ มาตรฐานที่ติดมากับ ROS ก็จะออกมาประมาณนี้
เอ้อ แล้วมันอยู่กินกันหน้าตายังไงล่ะ ?
ตอนที่แล้วเราพูดถึง Workspace ใช้ไม๊ล๊าา
ใน 1 Workspace จะมี หลายๆ Packages
ใน 1 Package จะมีได้หลายๆ Node . . .
. . . ใน 1 Node มักจะทำ 1 หน้าที่ (จริงๆก็ไม่เสมอไปหร๊อก .. )
มันจะอยู่กันหน้าตาประมาณนี้
มาสร้าง Package กันเถอะ
เริ่มแรก เรามาออกแบบกันก่อน
เรากำลังจะทำ Node 2 อัน ที่ทำหน้าที่พูดคนนึง และ รับฟังคนนึง
Package ชื่อ : chatter_subscriber_tutorial
โดยใช้ Dependencies ดังนี้:
1. catkin (build system)
2. roscpp (ROS ภาษา C++)
3. std_msgs (Package ข้อความพื้นฐานใน ROS)
อยากจะสร้าง Node ที่ทำหน้าที่เป็น
Publisher 1 Node ทำหน้าที่ส่งข้อความลูกเดียวเลย
Subscriber 1 Node ทำหน้าที่รับข้อความแล้วหาคำตอบ — แล้วโชว์
0. สร้าง Workspace (ถ้าทำตาม Tutorial บทแรกๆจะมี catkin_ws เป็น Workspace ของเราแล้วนั้นเอง)
1. ใช้ CLI ของ Catkin ในการสร้าง Package ของ ROS ขึ้นมา ซึ่ง Package จะต้องอยู่ใน src ของ Workspace เน้อ
catkin_create_pkg ชื่อ วรรค ตามด้วย dependency หลายๆตัว ที่เว้นวรรค 1 ที
> catkin_create_pkg chatter_subscriber_tutorial catkin roscpp std_msgs
เราจะได้ Folder ที่มีชื่อว่า chatter_subscriber_tutorial ขึ้นมา
พอเป็นเข้าไปด้านใน จะพบว่ามี Folder มารอดังนี้
2. วางโครงสร้างของ Code C++
เราจะต้องอธิบาย Package เราก่อนว่า ประกอบด้วยอะไร และไปใช้งานใครบ้าง
เริ่มที่ file package.xml (จะพบว่ามีมาให้แล้วจากคำสั่งในข้อ 1)
โดยปกติ ถ้าใช้ CLI (ไอ้คำสั่ง catkin_create_pkg) สร้าง package มาแล้ว ไฟล์นี้จะรวมของให้เรียบร้อยแล้ว — แปลว่าไม่ต้องแก้อะไรก็ได้ นั่นเอง
หลังจากนั้นมาต่อที่ CMakelists.txt (ซึ่งคำสั่งก็ช่วย Generate ให้แล้วเช่นกัน)
ในส่วนที่เรียกว่า find_package นั้นจะเป็นการไปหา package ที่เราเคยลงไว้ในเครื่อง เช่น roscpp , std_msgs (ลงมาพร้อม ROS)
[advance user : ถ้าลง opencv , eigen lib หรือ library นอก เช่น dynamixel-sdk หรือ บอร์ดขับ Motor ใดๆ แม้กระทั่ง Serial ถ้าจะเอามาเขียนใน Codeก็ต้องมากำหนดตรงนี้ เพื่อสร้าง Scope ให้ catkin build system รู้จักพวกของนอก Package (dependency) เหล่านี้]
ณ จุดนี้ เรายังไม่ต้องกำหนดอะไรเพิ่มมาก :)
นอกจากบอกว่า เราจะสร้าง Node กี่อัน แล้ว Run อันไหนได้บ้าง
เราจะบอกว่า Package นี้มี Code ที่รันได้ จะสร้างจาก file 2 อันนี้นะ จะต้องเติม 3 จุด
ตำแหน่งในการเติม
ให้ลองมองๆหาตัวอย่างใน File CMakeLists.txt ที่เขียนแบบนี้ดู
เราจะเติม 3 คำสั่ง คือ add_executable , add_dependencies, target_link_library
เขียนตามลำดับ(บนลงล่าง)นะเห้ย
ไม่งั้น Compile ไม่ผ่านหาว่าไม่เตือนนะจ๊ะ
- add_executable จะเป็นการบอกว่า มีอะไรที่ run ได้บ้าง
“คือ มี MAIN Function นั่นแหละ”
และ ตั้งชื่อมันว่าอะไร ด้วยการใช้คำสั่งดังนี้
> add_executable(ตั้งชื่อexecutable วรรค ตามด้วย ไฟล์ Node ที่มี Main)
> add_executable(node_funny src/hallo.src)
- add_dependencies จะเป็นการบอกว่า Executable นั้นๆ
ไปใช้ Dependency ที่วางรอตรงไหนบ้าง
(ซึ่งปกติมักจะไปถามเอาจาก catkin ด้วย การพิมพ์ catkin_EXPORTED_TARGETS)
> add_dependencies(ชื่อexecutable export-targetต่างๆ)
> add_dependencies(my_publisher ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
- target_link_libraries(ชื่อexecutable วรรคตามด้วย Libraries ต่างๆ)
ไว้จะมาเจาะกันลึกๆคราวหน้าเนาะ ตอนนี้ ให้ใส่ ${catkin_LIBRARIES} ตามไปก่อน เพื่อใช้ Lib ที่ Catkin รวบรวมมาให้ (ได้แก่พวกของ ROS นั่นเอง)
> target_link_libraries(my_publisher ${catkin_LIBRARIES})
> target_link_libraries(my_subscriber ${catkin_LIBRARIES})
ซึ่งตอนจบจะเป็นแบบนี้
อันนี้แบบไฟล์เปล่าๆ ไม่มี comment จาก catkin
หลังจากนั้นก็ Build โค๊ดโลดดด
>cd ~/catkin_ws/
>catkin_make
ปล. ขอบคุณหลังไมค์คับ พอดีลืมเขียนคำสั่งตรงนี้ไว้ :)
3. กด Build ได้เลย==>พัง 555555
โดนดักอะดิ มันยังไม่มี code เลย
แม้แต่ main ยังไม่มีเลย จะ Run ได้ยังไง !!
ถึงเวลา….เ̶ข̶ี̶ย̶น̶…ก๊อปCode แล๊ว
ก่อนอื่น ลองเอา Code ROS มา Run ให้ได้ก่อนนะะ !!!
1. สร้างตัวส่งข้อความ “Publisher”
เอ … ระหว่างเรา Code จะลอง Compile และ Run ยังไงดีล่ะ ?
FILE : my_publisher.cpp
หลังจาก Code เสร็จแล้ว ก็ Compile โลดด
>cd ~/catkin_ws/
>catkin_make
เราจะ RUN ระบบ ROS จะต้องมี Core ที่ทำหน้าที่เป็นหัวใจของระบบ
> roscore
หลังจากนั้นจึง Run NODE ของเราได้
>rosrun ชื่อpackage ชื่อexecutableที่กำหนดในCMakeLists.txt
>rosrun chatter_subscriber_tutorial my_publisher
เอ้อ ปิดยังไงอะ
กด Ctrl + C นะครับ เหมือนการกด Copy เลย เป็นปุ่มในตำนานที่เอาไว้หยุดโปรแกรมที่ทำงานใน Terminal ครับ !
อ้ะๆๆ มาต่อ เหลืออีก Node นึงที่ต้องทำ คือ ตัวส่งข้อมูล
2. สร้างตัวรับข้อความ “Subscriber”
FILE : my_subscriber.cpp
ลองเอามา Run พร้อมๆกันดู — ปิดทุก Node ก่อน แล้วมา Run ใหม่กัน
เริ่มจาก เปิดระบบ ROS
>roscore
ตามมาด้วยการเปิด Subscriber (ตัวฟัง/รับข้อมูล)
>rosrun chatter_subscriber_tutorial my_subscriber
หลังจากนั้น เปิด Publisher (ตัวส่งข้อมูล)
>rosrun chatter_subscriber_tutorial my_publisher
ผลลัพธ์ !!!!
ตัวส่งกับตัวรับมันจะคุยกันได้แล้ว !!!
มันเกิดอะไรขึ้นบ้าง ?
ไว้ยกไป Chapter ต่อไปละกัน !! ต้องอธิบายเยอะอยู่ — บรรทัดต่อบรรทัด
ไว้จะทำเป็น Tutorial แบบ Practical Node implementation ละกันนะ คงเป็น Part 5 แหละ เพราะอ่านมาตรงนี้น่าจะเหนื่อย (โคตรๆ) แล้ว :)
แต่ถ้าอยากอ่านจริงๆ ไปดูใน Tutorial แหละ เพราะผมก็ไปเอามาจากตรงนั้น 5555555555
Hello ไปๆมาๆ แล้วมันจะกลายเป็นโปรแกรมหุ่นยนต์ยังไงฟระ ?
ถ้า Publisher เป็นคนสร้างข้อมูลขึ้นมา
และ Subscriber เป็นคนที่คอยรับข้อมูลต่างๆ (เข้าไปประมวลผล)
เราจะแบ่งหมวดของ Node ต่างๆ เป็นประเภทคร่าวๆได้ดังนี้
- Producer — ผลิตข้อมูลนำเข้าทุกอย่าง
- Logic-รับข้อมูล ประมวลผล และอาจส่งต่อให้ Logic อีกที
- Actuate-โต้ตอบกับสิ่งแวดล้อม ไม่ว่าจะการแสดงผล (Visual Display) , หรือทางกายภาพ (Physical Interaction)
ซึ่งจะสอดคล้องกับหลักการทำงานพื้นฐานของหุ่นยนต์เลยแหละ !!!
จบแย้วว สำหรับ Tutorial Basics ของ Series นี้
ต่อไปจะเป็นเรื่อง Advance ขึ้นมาละนะ !!
เตรียมพบกับ Practical ROS ในตอนต่อๆไป ~!!