{%hackmd aPqG0f7uS3CSdeXvHSYQKQ %} # ROS1 Tutorial Introduction 此篇為中央大學數學系上課所用的 ROS 教材,若非修課生,Demo 部分需要注意一下自己的機器人設定。 若發現教材有誤,歡迎直接修改 主要更新以 hackmd 為主:https://hackmd.io/@Mes/RosTutorial_Intro # 什麼是 ROS? ROS 全名叫 Robot Operating System,但它其實是一種中介軟體(Middleware),妳也可以說它是一個軟體框架(Software Framework),所以他是一種抽象化的概念,不是應用程式,也不是作業系統 那它主要會幫我們連結各個軟體和零件之間的溝通,並且會提供一些 logging 的工具,那機器人的中介軟體有很多,像是 ROS、JAUS、Mira 等等 <center> <img src = "https://github.com/Mes0903/MesBlog/blob/vuepress-theme-hope/src/ROS/ROS_Tutorial_Introduction/image/middleware.png?raw=true" width="200px"> </center> # 為什麼需要 ROS? ## 最一開始的狀況 最一開始大家都各寫各的,但要寫機器人是一件非常困難的事,妳需要熟知如何撰寫每種零件的 code,當然,有大神克服了,完成了很多作品 然而當日子一久,機器人技術的規模和範圍不斷擴大,零件也越來越多,此時前面那位大神的 code 在別人手上可能就跑不起來了,因為零件不同 這會造成一個問題,就算是同樣的功能的 code,由於每個人的零件不同,所以都還要再重寫一次,這就導致了代碼的重用性很低(重造輪子),而且這類 code 的規模很大,而且還是需要從驅動層級開始寫的,因此非常不方便,且需要非常高的專業能力 ## 框架 因此就有人提出了框架的想法,什麼是框架? 框架是一種規則(思想),其實就是某種半成品,框架提供了一個基礎的架構,就好像房子的地基和骨架一樣,必須配合妳自己寫的 code 才能生出一個完整的應用程式 好處就是整個結構可以被<span class ="yellow">重複使用</span>,如果有了框架,那麼做東西就不需要再從頭建造了 而前面也提到 ROS 算是一種軟體框架,ROS 幫我們把硬體與軟體之間的溝通都做好了,我們只需要寫我們「溝通的過程/效用」就好,而不用再從「溝通的原理」開始寫,也因此我們可以專注於開發演算法及其應用,同時也降低了高度專業能力的限制 # ROS 的架構 ## Peer to peer (P2P) ROS 主要是依靠 P2P 架構實作的,講這個之前先讓大家稍微理解一下簡單的主從式架構: <center> <img src = "https://github.com/Mes0903/MesBlog/blob/vuepress-theme-hope/src/ROS/ROS_Tutorial_Introduction/image/p2p1.png?raw=true"> (image source:[wikipedia](https://en.wikipedia.org/wiki/Client%E2%80%93server_model)) </center><br> 客戶端(clients) 會去向伺服器請求資料,這邊這個伺服器裡面有很多資料,像是客戶的帳密、金額,或是你遊戲帳號裡面的寶物有哪些之類的。以早期的線上遊戲來說,每次客戶端有更改資料的動作時都會發送一個請求(request) 給伺服器,假設你打了怪,賺到了 10 元,它就會把這個資訊送到伺服器上,伺服器就會幫你記錄下來 而如果拿網頁舉例子,像是 FB,妳點了某個人的個人頁面,妳就會向伺服器發送一個請求(request),伺服器收到這個請求後就會知道妳要進入這個人的個人頁面,因此將妳導向這個人的個人頁面,妳就成功進去了 因此伺服器的權限非常的大,並且可以處理非常多的東西 但這會有一個問題,那就是如果發送請求的人非常多,且請求發送的非常頻繁,那麼伺服器的速度可能就會變得非常慢,甚至當機掛掉,DDoS 的原理就是這樣 那如果 ROS 使用了主從式架構,很有可能也會有類似的問題,因此 ROS 使用的是 Peer-To-Peer (一種分散式系統架構),Middleware 通常都會是分散式系統架構: <center> <img src = "https://github.com/Mes0903/MesBlog/blob/vuepress-theme-hope/src/ROS/ROS_Tutorial_Introduction/image/p2p2.png?raw=true"> (image source:[link](https://ithelp.ithome.com.tw/articles/10216158)) </center><br> 在這種架構下,每台電腦(節點)都同時是客戶端與伺服器端,所有人都負責儲存了全部或部分的所有資料,並且也都會處理收到的請求 這樣做的優點是能夠擁有很好的平行處理能力,效能也會比較好,缺點是如果規模太大整個網路會很亂,且會很吃網路,但因為機器人的網路規模通常很小,所以不太會有這個問題,另一個缺點就是安全性,如果沒有做特別的處理,傳輸的文件可能會被動手腳,或是失真 那 p2p 有一些變型,這邊舉三個例子,看下面這張圖: <center> <img src = "https://github.com/Mes0903/MesBlog/blob/vuepress-theme-hope/src/ROS/ROS_Tutorial_Introduction/image/p2p3.png?raw=true"> (image source:[link](https://www.researchgate.net/figure/P2P-architectures-at-a-glance-a-Centralized-architecture-b-Pure-P2P-architecture_fig2_332539196)) </center><br> 上面這三個分別為 1. 中心化P2P 英文為 Centralized P2P architecture + 有一個中心伺服器來幫忙連結溝通其他節點的訊息資料,並對這些請求做出回應 (但本身並不保存檔案) + 節點負責處理、發布訊息資料,讓中心伺服器知道節點需要什麼檔案、資料,讓其他節點下載資源 + 有 index 可以找到絕對地址 例子像是最初的 Napster 2. 純P2P 英文為 Pure P2P architecture + 節點同時是客戶端和伺服器端 + 沒有中心伺服器 例子像是 Gnutella 3. 混合型P2P 英文為 Hybrid P2P architecture + 有多個伺服器來處理其他節點的訊息資料 + 同時有上面兩個的特性 例子像是 Skype 那麼 ROS 的架構是第一種,Centralized P2P: <center> <img src = "https://github.com/Mes0903/MesBlog/blob/vuepress-theme-hope/src/ROS/ROS_Tutorial_Introduction/image/p2p4.png?raw=true"> (image source:RSL ROS Tutorial) </center><br> 我們會有一片樹梅派來跑 server 的 code,或是像我們一樣用主機來跑 server 的 code,然後各個零件能夠互相傳遞、存取資料 ## 架構及名詞解釋 現在我們已經知道 ROS 是用 P2P 來實作的了,那麼現在我們要來簡單看一下這些東西在 ROS 裡面的名詞及概念 ### Node 在 ROS 裡面,P2P 架構裡面的一個節點我們叫他 Node,Node 是一個你跑起來的程序(Process),一個完整的系統會有很多個 Node 一個 Node 通常會是有單一功能的 Process,當然妳一個 Node 要有很多功能也可以,總之核心概念就是模組化(Modular Design) 舉個例子,一個簡單的導航程式,裡面可能是有一個 Node 是操控雷達,一個 Node 操控馬達、輪子,一個 Node 計算自己的定位(Localization),一個 Node 來計算路徑規劃,一個 Node 顯示圖形介面,等等 所以你可以看見它其實就是很多個 Process 組合起來的,那麼這些 code 妳可以用 roscpp 或 rospy 來寫,好像還有其他另外支援的語言套件,但我只熟這兩個了 ### Master Master 是一個特殊的 Node,也就是我們前面所說的 Server,Master 會幫忙查找 Node,紀錄妳想傳遞的訊息的種類等等 如果沒有 Master,<span class = "yellow">Node 與 Node 間會找不到對方</span>,妳的資料傳遞、設備呼叫請求等等的溝通就會失效 ### Messages 上面有說一個完整的系統會有很多個 Node,那麼 <span class = "yellow">Node 間要傳遞資料需要靠 Messages 來溝通</span> Message 是一種資料結構,裡面會有 type field,type field 通常指的是一個基類(base class) 裡面的變數,這個變數會拿來當作子類的 type 來使用,舉個例子([來源](https://stackoverflow.com/questions/9147101/what-are-type-fields)): ```cpp #include <iostream> class Pet { public: enum PetType { Dog, Cat, Bird, Fish }; void ToString() const { switch ( type ) { case PetType::Dog: std::cout << "Dog" << std::endl; break; case PetType::Cat: std::cout << "Cat" << std::endl; break; case PetType::Bird: std::cout << "Bird" << std::endl; break; case PetType::Fish: std::cout << "Fish" << std::endl; break; } } protected: PetType type; // A type field. }; class Dog : public Pet { public: Dog() { type = PetType::Dog; } }; void Test( const Pet &p ) { p.ToString(); } int main() { Dog d; Test( d ); return 0; } ``` 上面的 `Pet::type` 就是一個 type field,在 Dog 這個子類裡面我們用 `type` 來當作了它的型態 Messeage 裡面可以有標準的基本型態,整數、浮點數、布林值之類的,也可以是基本型態的陣列,且 Message 內可以包含巢狀類(nested class) ### Topic Message 在發布時我們會給它加上 Topic,妳可以把 Message 想像成一個箱子,Node 間要傳遞資料時會把資料放到這個箱子裡面,並在這個箱子上面貼上一個標籤,這個標籤就是 Topic 覺得太抽象的話可以看下面那個小節的圖 ### Publisher & Subscriber 讓我們先小整理一下,Message 是 Node 間拿來溝通的工具,因此 Message 由 Node 發布,也由 Node 接收 於是在 ROS 裡面,發布 Message 出來的 Node 我們叫它 Publisher,接收 Message 的 Node 我們叫它 Subscriber,看看下面這張圖: <center> <img src = "https://github.com/Mes0903/MesBlog/blob/vuepress-theme-hope/src/ROS/ROS_Tutorial_Introduction/image/pub_sub1.png?raw=true"> </center><br> Node 會通過 Topic 來找要接收它需要的訊息,我們稱之為訂閱,例如規劃路徑的 Node 希望和雷達的 Node 拿掃到的資料,那麼規劃路徑的 Node 就是 Subscriber,雷達的 Node 則是 Publisher 而 Message 裡面可能裝很多個整數的陣列,Topic 可能是「雷達資料」,以上方那個圖來說就是: <center> <img src = "https://github.com/Mes0903/MesBlog/blob/vuepress-theme-hope/src/ROS/ROS_Tutorial_Introduction/image/pub_sub2.png?raw=true"> </center><br> 而同一種 Topic 的 Message 也可以由不同的 Node 發布,也就是有不同的 Publisher 發布同樣 Topic 的 Message; 例如妳雷達有兩顆,而且妳為他們寫了兩個 Node,那麼這兩個 Node 都可以發布「雷達資料」這種 Topic 的 Message: <center> <img src = "https://github.com/Mes0903/MesBlog/blob/vuepress-theme-hope/src/ROS/ROS_Tutorial_Introduction/image/pub_sub3.png?raw=true"> </center><br> 同理,同一種 Topic 的 Message 也可以有不同的 Node 訂閱,有就是有不同的 Subscriber 訂閱同樣 Topic 的 Message; 例如規劃路徑的 Node 需要雷達的資料,建地圖的 Node 也需要雷達的資料,那這兩個 Node 都可以訂閱「雷達資料」這種 Topic 的 Message <center> <img src = "https://github.com/Mes0903/MesBlog/blob/vuepress-theme-hope/src/ROS/ROS_Tutorial_Introduction/image/pub_sub4.png?raw=true"> </center><br> 如果一個 Node 同時在收資料與發資料,那這個 Node 就同時是 Subcriber 與 Publisher ### 資訊的傳遞 實際上在傳遞資訊時還會需要 Master 來幫忙 Node 之間的通訊,那麼 ROS 的訊息傳送時是使用 TCP/IP 協定的連線,且一旦兩個訊息接起來後就不會再經過 Master 了: <center> <img src = "https://github.com/Mes0903/MesBlog/blob/vuepress-theme-hope/src/ROS/ROS_Tutorial_Introduction/image/pub_sub5.png?raw=true"> </center><br> 一開始 Publisher 會先去向 Master 註冊,然後 Publisher 就會開始發布它的訊息(封包);而當 Subscriber 需要相對應的訊息時就會去詢問 Master,那當它訂閱到那個 Topic 時它們就建立了連線,不再透過 Master 來傳遞資訊 所以 Master 會負責 Node 之間的通訊,他們之間是 TCP/IP 協定的連線 而因為 Middleware 會做序列化(Serialization),所以你 C\+\+ 寫出來的 Node 和 Python 寫出來的 Node 也可以溝通 ### Package Package 是一個 ROS 軟體的基本單位,Package 會有裡面會有很多個 Node,然後可能會包含有相關的函式庫、資料集(dataset)、配置文件(configuration file),或其他能幫助你整合、規劃專案的檔案 換句話說,Package 是妳在建立和發布專案時最基本的單位,因為妳的專案很可能是程式與程式之間在溝通的,妳把那些 Node 整合起來,配合妳自己寫的函式庫,蒐集的資料等等的,整個包裝起來成一個能讓別人使用的程式,這樣的東西就是一個 Package # Demo (By OG) [VB image](https://drive.google.com/file/d/1nWaeKfHHkiT3zIVUx9jwq7ZCGFq6MHf3/view?usp=share_link) # Ubuntu & Linux Ubuntu 是基於 Debian,以桌面應用為主的 Linux 發行版。Ubuntu 有三個正式版本,包括電腦版、伺服器版及用於物聯網裝置和機器人的 Core 版。前述三個版本既能安裝於實體電腦,也能安裝於虛擬電腦 ## Terminal & CLI <center> <img src = "https://github.com/Mes0903/MesBlog/blob/vuepress-theme-hope/src/ROS/ROS_Tutorial_Introduction/image/terminal.png?raw=true"> </center><br> ### linux 基本指令 : - cd:用以移動到目標路徑 - cp:複製檔案到指定路徑 - mv:移動檔案到指定路徑 - rm:刪除檔案 - ls:顯示當前路徑下的資料夾與檔案 - nano:一種 CLI 的編輯軟體 - vim:一種 CLI 的編輯軟體 ### SSH : Secure Shell 是一種加密的網路傳輸協定,可在不安全的網路中為網路服務提供安全的傳輸環境。 SSH 通過在網路中建立安全隧道來實現 SSH 客戶端與伺服器之間的連接。**SSH最常見的用途是遠端登入系統**,人們通常利用 SSH 來傳輸命令列介面和遠端執行命令 使用方式如下: ```bash ssh username@server_location -p port ``` 其中 - username:遠端操作時的使用者帳號 - server_location:你要連接的電腦,可能是ip或URI - port:ssh 專用的port,預設為22 請注意,接下來開始會有兩台主機進行運作。一台是你的電腦,一台是機器人 請留意你到底是在哪台機器上操作!!! # ROS # roscore & rosrun & roslaunch 在運行 node 之前都必須先啟動 master,master 就是 ROS 系統中負責管理 Node 的一個功能,他負責 node 與 node 之間的溝通橋樑,因此在執行 node 之前一定要先把 master 開啟。 然後在 ros 中提供兩種方法去執行你的 Node,一種是 rosrun、一種是 roslaunch ## [roscore](http://wiki.ros.org/roscore) roscore 可以讓你啟動 master ```bash roscore ``` ## [rosrun](http://wiki.ros.org/rosbash#rosrun) rosrun可以讓你去執行特定package下的code,使用方法如下 ```bash rosrun <package> <executable> # example rosrun hypharos_minibot main ``` ## [roslaunch](http://wiki.ros.org/roslaunch) roslaunch 則透過預先設定的 launch file 幫你一次開啟很多程式 ```bash # 因為launch file 是放在某個package底下,還是要加package roslaunch <package> <launch file> # example roslaunch hypharos_minibot project_sample.launch ``` # rosnode & rostopic 在 ros 中你可以使用以下兩種指令去檢視正在運行的 node 及 topic - `rosnode` 動作 參數 - `rostopic` 動作 參數 ## [rosnode](http://wiki.ros.org/rosnode) 在 rosnode 中提供以下幾種動作可以使用 ```bash rosnode info <node_name> # print information about node rosnode kill <node_name> # kill a running node rosnode list # list active nodes rosnode machine <machine-name> # list nodes running on a particular machine or list machines rosnode ping <node_name> # test connectivity to node rosnode cleanup # purge registration information of unreachable nodes ``` ## [rostopic](http://wiki.ros.org/rostopic) 在 rostopic 中提供以下幾種動作可以使用 ```bash rostopic bw <topic_name> # display bandwidth used by topic rostopic delay <topic_name> # display delay for topic which has header rostopic echo <topic_name> # print messages to screen rostopic find <msg-type> # find topics by type rostopic hz <topic_name> # display publishing rate of topic rostopic info <topic_name> # print information about active topic rostopic list # print information about active topics rostopic pub <topic-name> <msg-type> [data...] # publish data to topic rostopic type <topic-name> # print topic type ``` # 機器人初連接 ## Turtlesim ```bash # initialize ros master roscore # launch turtlesim_node rosrun turtlesim turtlesim_node # using keyboard to control the turtle rosrun turtlesim turtle_teleop_key ``` <center> <img src = "https://github.com/Mes0903/MesBlog/blob/vuepress-theme-hope/src/ROS/ROS_Tutorial_Introduction/image/robot.png?raw=true"> </center><br> ## Minibot & Turtlebot ### 網路設定 筆電網卡設定 **-->ipv4-->ip :10.0.0.2-->mask :255.255.255.0-->store** ```bash gedit ~/.bashrc ``` <center> <img src = "https://github.com/Mes0903/MesBlog/blob/vuepress-theme-hope/src/ROS/ROS_Tutorial_Introduction/image/setting1.png?raw=true"> </center><br> ```bash source ~/.bashrc ``` <center> <img src = "https://github.com/Mes0903/MesBlog/blob/vuepress-theme-hope/src/ROS/ROS_Tutorial_Introduction/image/setting2.png?raw=true"> </center><br> ### 連線 使用新版VM 啟動機器人的指令,一定要在機器人上執行!! ```bash # ssh連接機器人 ssh pi@10.0.0.1 ##passward=mrlrobot # 假如是minibot下 roslaunch hypharos_minibot project_sample.launch # 假如是turtlebot下 roslaunch turtlebot3_bringup turtlebot3_robot.launch ``` 起動 rviz 視覺化套件 ```bash rviz ``` 遙控機器人 ```bash # Extra moving !!! # 假如是minibot下 roslaunch teleop teleop_key.launch model:=minibot # 假如是turtlebot下 roslaunch teleop teleop_key.launch model:=turtlebot ``` 如果你長時間都會使用某一台機器人,你可以透過更改環境變數來設定預設機器人 ```bash gedit ~/.bashrc ``` 找到以下這兩行(139行以及140行) 可以看到現在預設是使用 minibot,你若是想預設 turtlebot 只要將 139 行反註解、140 行註解掉即可 <center> <img src = "https://github.com/Mes0903/MesBlog/blob/vuepress-theme-hope/src/ROS/ROS_Tutorial_Introduction/image/setting3.png?raw=true"> </center><br> <!-- ### minibot ```bash= #ssh連接機器人 ssh pi@10.0.0.1 ##passward=mrlrobot roslaunch hypharos_minibot project_sample.launch# 這是啟動機器人的指令,一定要在機器人上執行!!! ``` 起動 rviz 視覺化套件 ```bash= rviz ``` 遙控機器人 ```bash= #Extra moving !!! ssh pi@10.0.0.1 ##passward=mrlrobot rosrun hypharos_minibot teleop_keyboard.py # 在機器人上執行 ``` ### turtlebot ```bash= #ssh連接機器人 ssh pi@10.0.0.1 ##passward=mrlrobot roslaunch turtlebot3_sample sample.launch # 這是啟動機器人的指令,一定要在機器人上執行!!! ``` 起動 rviz 視覺化套件 ```bash= rviz ``` 遙控機器人 ```bash= #Extra moving !!! roslaunch turtlebot3_teleop turtlebot3_teleop_key.launch ``` --> ### 你一定會遇到的問題 <center> <img src = "https://github.com/Mes0903/MesBlog/blob/vuepress-theme-hope/src/ROS/ROS_Tutorial_Introduction/image/setting4.png?raw=true"> </center><br> 這代表你電腦中儲存的 Key 跟機器人上的不符合,你就執行他提示的指令 ``` ssh-keygen -f ... ``` 去把它移除,再重新 ssh # catkin ## 簡介 catkin 是一個開發環境整合套件,C\+\+ 中的 make 幫我們做到在編譯這個層級做整合。在不使用 IDE 的情況下,我們如果要編譯一個 c code 或一個 c project 時需要手動透過指令執行,而當這你的 code import 的函式庫很多或你的 project 很大時需要輸入的指令就會變得十分的複雜 此時 make 這個套件就可以讓我們透過設定一個簡單的 makefile 讓編譯器自動地去建構你所撰寫的project。makefile 會將程式分成好幾個模組,根據裡面的目標 (target)、規則 (rule) 和檔案的修改時間進行判斷哪些需要重新編譯,可以省去大量重複編譯的時間,這在大型程式中尤為有用 而 catkin 這個套件則在 make 的基礎上加入了空間上的整合,讓你整個專案的開發有個統一的格式 ## 使用 catkin 大致上把一個工作區劃分為以下三個區塊 - src 用來存放 source code 以及各個 project 的地方,src 中的存放單位為 package - build 用來放程式碼在編譯過程中的中間產物 - devel 編譯完的執行檔跟一些環境變數設定檔都會放置在這 <center> <img src = "https://github.com/Mes0903/MesBlog/blob/vuepress-theme-hope/src/ROS/ROS_Tutorial_Introduction/image/catkin1.png?raw=true"> </center><br> 後兩個路徑由 catkin 系統自動生成、管理,我們日常的開發一般不會去涉及,而主要用到的是 src 資料夾,我們寫的 ROS 程式、網上下載的 ROS 原始碼包都存放在這裡 在編譯時,catkin 編譯系統會遞迴的查詢和編譯 `src/` 下的每一個原始碼包。因此你也可以把幾個原始碼包放到同一個資料夾下,如下圖所示: <center> <img src = "https://github.com/Mes0903/MesBlog/blob/vuepress-theme-hope/src/ROS/ROS_Tutorial_Introduction/image/catkin2.png?raw=true"> </center><br> ### package結構 ``` ├── CMakeLists.txt # package的編譯規則(必須) ├── package.xml # package的描述資訊(必須) ├── src/ # 原始碼檔案 ├── include/ # C++標頭檔案 ├── scripts/ # 可執行指令碼 ├── msg/ # 自定義訊息 ├── srv/ # 自定義服務 ├── models/ # 3D模型檔案 ├── urdf/ # urdf檔案 ├── launch/ # launch檔案 ``` ### package的建立 ```bash catkin_create_pkg test_pkg roscpp rospy std_msgs ``` 建立一個 package 需要在 `catkin_ws/src` 下,用到 `catkin_create_pkg` 命令,用法是:`catkin_create_pkg package depends`,其中 `package` 是包名,`depends` 是依賴的包名,可以依賴多個軟體包 例如,新建一個 package 叫做 test_pkg,依賴 roscpp、rospy、std_msgs(常用依賴) 新建完後就可在 `src` 中撰寫你的程式碼嘍 ### 編譯package 回到 catkin workspace(catkin_ws) 這層後輸入 ```bash catkin_make ``` 他就會自動幫你編譯所有 package <center> <img src = "https://github.com/Mes0903/MesBlog/blob/vuepress-theme-hope/src/ROS/ROS_Tutorial_Introduction/image/catkin3.png?raw=true"> </center><br> 編譯完後記得執行以下指令加入環境變數,不然你在 Terminal 上找不到你要執行的 code 喔 ```bash source ~/catkin_ws/devel/setup.bash ``` # roscpp ROS 中的 CPP 檔是放置在 package 中的 src ### package結構 ``` ├── CMakeLists.txt #package的編譯規則(必須) ├── package.xml #package的描述資訊(必須) ├── src/ #原始碼檔案 ├── include/ #C++標頭檔案 ├── scripts/ #可執行指令碼 ├── msg/ #自定義訊息 ├── srv/ #自定義服務 ├── models/ #3D模型檔案 ├── urdf/ #urdf檔案 ├── launch/ #launch檔案 ``` ## node simple sample ```bash cd ~/catkin_ws/src/<your_pkg>/src # 到你的 project 中的 src 中,src 是用來存放 source code 的地方 gedit file_name.cpp # 新增一個 cpp 檔,並編輯。就是開始打 code 啦 ``` ```c #include <ros/ros.h> // 引用 ros.h 檔 int main(int argc, char** argv){ ros::init(argc, argv, "hello_cpp_node"); // 初始化 hello_cpp_node ros::NodeHandle handler; // node 的 handler ROS_INFO("Hello World!"); // 印出 Hello World } ``` cpp 檔不像 python 檔一樣可以直接被執行,需要經過編譯以後才能轉成執行檔,因此我們需要修改 `beginner_tutorial` 內的 CMakeLists.txt ,為其設定好連結的函式庫 由於他的 CMakeLists.txt 太長了,在此擷取片段做為參考: ```bash cd ~/catkin_ws/src/<your_pkg> # 到你的project中 gedit CMakeLists.txt # 修改當中的 CMakeLists.txt,這是編譯的設定檔 ``` ```cmake //...上略 ## Declare a C++ executable ## ... ## ... ## ... add_executable(file_name src/file_name.cpp) target_link_libraries(file_name ${catkin_LIBRARIES}) ## Rename C++ executable without prefix //...下略 ``` 修改完 CMakeLists.txt 後,接著須回到工作區的根目錄,也就是 `/catkin_ws` 中再執行一次`catkin_make`,他就會自己幫我們編譯好 `file_name.cpp` 的執行檔(`file_name`)囉! 接下來修改 `file_name.cpp`,一樣新增一個迴圈,讓他在程式執行期間每秒印出一個 hello world出來,程式碼如下: ```c #include <ros/ros.h> // 引用 ros.h 檔 int main(int argc, char** argv){ ros::init(argc, argv, "hello_cpp_node"); // 初始化hello_cpp_node ros::NodeHandle handler; // node 的 handler while (ros::ok()){ // 在 ros 順利執行時 ROS_INFO("Hello World!"); // 印出 Hello World ros::Duration(1).sleep(); // 間隔 1 秒 } } ``` 修改完 `file_name.cpp` 後,因為 CMakeLists.txt 已經改好了,只要再回去執行一次 `catkin_make`,就可以重新編譯出一個新的 `file_name` 囉! ### [教學傳送門](https://ithelp.ithome.com.tw/articles/10204122) ## [Publisher](http://wiki.ros.org/roscpp/Overview/Publishers%20and%20Subscribers) simple sample ```c #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; } ``` ### [教學傳送門](https://ithelp.ithome.com.tw/articles/10205657) ## [Subscriber](http://wiki.ros.org/roscpp/Overview/Publishers%20and%20Subscribers) simple sample ```c #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; } ``` ### [教學傳送門](https://ithelp.ithome.com.tw/articles/10205877) # rospy ROS 中的 Python 檔是放置在 package 中的 scripts!!! ### package結構 ``` ├── CMakeLists.txt # package的編譯規則(必須) ├── package.xml # package的描述資訊(必須) ├── src/ # 原始碼檔案 ├── include/ # C++標頭檔案 ├── scripts/ # 可執行指令碼 ├── msg/ # 自定義訊息 ├── srv/ # 自定義服務 ├── models/ # 3D模型檔案 ├── urdf/ # urdf檔案 ├── launch/ # launch檔案 ``` ## node simple sample ```python #!/usr/bin/env python import rospy # import rospy 模組 rospy.init_node('hello_python_node') # 初始化 hello_python_node while not rospy.is_shutdown(): # 在 rospy 還沒結束前,執行下列指令: rospy.loginfo('Hello World') # 印出 Hello World rospy.sleep(1) # 間隔 1 秒 ``` 編輯完code後幫code加權限 ```bash chmod +x file_name.py ``` ### [教學傳送門](https://ithelp.ithome.com.tw/articles/10203798) ## [Publisher](http://wiki.ros.org/rospy/Overview/Publishers%20and%20Subscribers) simple sample ```python #!/usr/bin/env python # license removed for brevity import rospy from std_msgs.msg import String def talker(): pub = rospy.Publisher('chatter', String, queue_size=10) rospy.init_node('talker', anonymous=True) 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 ``` ### [教學傳送門](https://ithelp.ithome.com.tw/articles/10205008) ## [Subscriber](http://wiki.ros.org/rospy/Overview/Publishers%20and%20Subscribers) simple sample ```python #!/usr/bin/env python import rospy from std_msgs.msg import String def callback(data): rospy.loginfo(rospy.get_caller_id() + "I heard %s", data.data) def listener(): 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() ``` ### [教學傳送門](https://ithelp.ithome.com.tw/articles/10205362) # Robot source code on github ## minibot sample code https://github.com/kuoshih/hypharos_minibot ## turtlebot sample code https://github.com/MathRoboticsLab/turtlebot3_sample ## Python for TB3 & minibot: https://github.com/MathRoboticsLab/hypharos_minibot_turtlebot_python_sample # 參考資料 **<a href="https://www.youtube.com/playlist?list=PL6S9AqLQkFprxJW18z1Nu9P2bnWCK4KmC" class="redlink">1. jserv ROS 教學</a>** **<a href="http://wiki.ros.org/ROS/Introduction" class="redlink">2. ROS Introduction</a>** **<a href="https://ai.stanford.edu/~mquigley/papers/icra2009-ros.pdf" class="redlink">3. ROS: an open-source Robot Operating System</a>** **<a href="https://en.wikipedia.org/wiki/Peer-to-peer" class="redlink">4. Peer-to-peer wiki</a>** **<a href="http://wiki.ros.org/" class="redlink">5. ROS WIKI</a>** **<a href="https://www.itread01.com/content/1545152767.html" class="redlink">6. catkin tools</a>** **<a href="https://ithelp.ithome.com.tw/users/20112348/ironman/1965" class="redlink">7. ROS自學筆記</a>** **<a href="https://www.facebook.com/profile.php?id=100003209663210" class="redlink">8. 這人台大</a>**