{%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>**
×
Sign in
Email
Password
Forgot password
or
Sign in via Google
Sign in via Facebook
Sign in via X(Twitter)
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
Continue with a different method
New to HackMD?
Sign up
By signing in, you agree to our
terms of service
.