---
# System prepended metadata

title: ROS / ROS2 教學

---

---
type: slide


---
<!-- .slides text-align: left;  -->

#  ROS / ROS2 教學
<br>
<br>

### NDHUEE &nbsp;&nbsp;&nbsp; ISP
### 611023011 何明叡 

---

## 歷史介紹
+ ROS 1 最初是史丹佛大學的人工智慧實驗室與機器人公司 Willow Garage 合作的一個機器人專案，名稱為 Personal Robots Program。這項專案研發的機器人 PR2 在使用 ROS 1 的基上，能夠完成打撞球、做早餐、疊衣服等接近人類的動作，因此引起了全世界的關注。
+ 2008 年後，ROS 1 便交由 Willow Garage 維護，並在 2010 年時正式以開放原始碼的方式，向世界發佈了 ROS 1 的框架。
+ 最後在 2013 年時轉交給開源機器人基金會 OSRF(Open Source Robotics Foundation) 繼續維護。
+ 大約一年發布一次更新

---

## 什麼是ROS?
+ Robot Operating System
![](https://i.imgur.com/eQHHvQN.png)

---

## Solution

+ A framework that provides:
  + 軟硬整合:
    + navigation -> ( laser scanner, data process, algorithm)
  + 系統整合＋模組化系統:
    + Nodes
  + Message passing
    + Topics: publish / subscribe
    + Services: call / provide
  + visualization, monitoring
    + System structure, message flow, tf, data

+ 或是...把以上所有自己寫code來完成？？

---

## WHY ROS？

+ 跨語言，跨平台
+ 聯機操作 Remote communication and control
+ 眾多第三方工具
+ 被廣泛使用，且持續成長中
![](https://i.imgur.com/eZVDoEi.png =x300) ![](https://i.imgur.com/rMNJ4cd.png =x300)

---

## 教學進度安排
+ ROS 介紹
+ ROS 環境建置
+ ROS Nodes
+ ROS Topics
+ ROS Services
+ ROS Msg & Srv

---

## ROS1 ROS2 版本 ＋ PC系統？

| ROS1 | ROS2 |
|:------:|:------:|
| http://wiki.ros.org/Distributions | https://docs.ros.org/en/foxy/Releases.html|
|![](https://i.imgur.com/FKh3h77.png)|![](https://i.imgur.com/7c6W5r2.png)|
|![](https://i.imgur.com/oxBICsA.png)|![](https://i.imgur.com/qL8ckpi.png =x90)
|


+ Ubuntu 20.04
  + img: https://www.ubuntu-tw.org/modules/tinyd0/index.php?id=7
  + USB製作: https://www.balena.io/etcher/
  + 教學: https://ubuntu.com/tutorials/install-ubuntu-desktop#1-overview

+ 開始漫長的安裝～～～

---

## 安裝ＲＯＳ
開始安裝前，這堂課需要準備什麼?
+ 電腦 ( PC, LAPTOP, Nvidia Jetson nano...)
+ USB 灌ubuntu

基礎知識
+ Python
+ Linux - Ubuntu

---

## 安裝ＲＯＳ

開機後:
+ sudo apt update
+ sudo apt upgrade

1.2 Setup your sources.list
```
sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list'
```
1.3 Set up your keys
```
sudo apt install curl # if you haven't already installed curl
curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | sudo apt-key add -
```
1.4 Installation
```
sudo apt update
sudo apt install ros-noetic-desktop-full
```

---

## 安裝ＲＯＳ

1.5 Environment setup
+ 在 ~/.bashrc 最後一行加上
```
source /opt/ros/noetic/setup.bash
```
+ ![](https://i.imgur.com/vwNz3QB.png =x100)
+ 使 ~/.bashrc 生效
```
source ~/.bashrc
```


1.6 Dependencies for building packages
```
sudo apt install python3-rosdep python3-rosinstall python3-rosinstall-generator python3-wstool build-essential
sudo apt install python3-rosdep
sudo rosdep init
rosdep update
```

---

## 測試ＲＯＳ是否安裝成功
```
roscore
```
![](https://i.imgur.com/c5TOOOG.png)

---

## ROS node/topic 實做
### 前情提要
+  ubuntu 基本操作
    +  ctrl + alt + T -----開啟terminal
    +  ctrl + shift + T  -----在terminal開新分頁
        +  ls  -----列出所有檔案
        +  cd  -----前往
        +  cp  -----複製
        +  rm  -----移除（無法復原）
        +  cat -----讀取
        +  mkdir  -----建立資料夾
    +  ~/ 使用者根目錄
    +  / 系統根目錄
    +  sudo +command 以系统管理者身份執行

---

## ROS node/topic 實做
### 前情提要

+  vim基本操作
    +  vim file_name
        +  i  -----insert
        +  u  -----復原
        +  ctrl + r  -----還原復原
        +  esc  -----完成編輯
        +  :w  -----儲存
        +  :q  -----離開
        +  :wq  -----儲存並離開

---

## ROS Graph
![](https://i.imgur.com/kVS6GGD.png =x550)
[p.17 node](#16) -- [p.21 topic](#20) -- [p.44 service](#43) -- [p.51 msg/srv](#50) -- [lunch](#14) -- [parm](#13)


---

## ROS的第一個專案：小烏龜
+ 安裝小烏龜package
```
sudo apt-get install ros-noetic-turtlesim
```
+ 啟動master
```
roscore
```
+ 啟動小烏龜
```
rosrun turtlesim turtlesim_node
```
+ 開啟鍵盤控制
```
rosrun turtlesim turtle_teleop_key
```
![](https://i.imgur.com/mG3dH02.png =x200) ![](https://i.imgur.com/XXX3Z9F.png =x80)

---

## 認識ROS指令格式
+ roscore, roscd
<br>
+ rosrun, roslaunch
  + {command} &nbsp;&nbsp; {pakage_name} &nbsp;&nbsp; {target_name}
<br>
+ rosnode, rostopic, rosservice, rosmsg, rosparam
  + {command} &nbsp;&nbsp; {tool} &nbsp;&nbsp; (target_name)

---

## 認識Node

+ 啟動node 
```
rosrun pakage_name node_name
```
+ **rosnode**
```
rosnode ↹↹
```
![](https://i.imgur.com/xLqjm6a.png =x70)

+ 指令說明
![](https://i.imgur.com/Wpn41Lh.png)


---

## rosnode command
(在運行小烏龜的情況下)
+ rosnode list
![](https://i.imgur.com/ptoK4VR.png =x100)
+ rosnode info {node_name}
![](https://i.imgur.com/EE6kx0E.png)

---

## rosnode command
(在運行小烏龜的情況下)
+ rosnode ping
![](https://i.imgur.com/0W60WyK.png)
+ rosnode machine (machine_name)
![](https://i.imgur.com/3O0Zfvh.png)


---

## rosnode command
(在運行小烏龜的情況下)
+ rosnode kill {node_name}
![](https://i.imgur.com/eSr4PgA.png =x200)
![](https://i.imgur.com/yeYGpFm.png =x130)

[ROS_graph](#13)

---

## 認識 Topic
+ **topic**
```
rostopic ↹↹
```
![](https://i.imgur.com/6xii2Re.png =x70)

+ 指令說明
![](https://i.imgur.com/pWyCkQd.png)

---

## rostopic command
(在運行小烏龜的情況下)
+ rostopic list
![](https://i.imgur.com/Oz5ecPh.png =x150)
+ rostopic info
![](https://i.imgur.com/rABEj9U.png)

---

## rostopic command
(在運行小烏龜的情況下)
+ rostopic echo
![](https://i.imgur.com/AKd2Xjo.png)
![](https://i.imgur.com/BMOeXg1.png)
(同時用key控制你的小烏龜～）

---

## rostopic command
(在運行小烏龜的情況下)
+ rostopic hz /turtle1/pose
+ rostopic bw /turtle1/pose
![](https://i.imgur.com/siwyHEh.png)
[ROS_graph](#13)

---

## 認識rqt
+ ROS視覺化工具
```
rqt
```
![](https://i.imgur.com/SC6jniU.png)

---

## ROS node 實做 
![](https://i.imgur.com/ZmrehUJ.png)

---

## ROS node 實做
### 建立你的ROS workspace
+  建立workspace folder
```
~/$ mkdir my_ws
```
+  進入workspace folder
```
~/$ cd my_ws
```
+  建立src folder
```
~/my_ws$ mkdir src
```
+  用catkin建立環境
```
~/my_ws$ catkin_make
```
![](https://i.imgur.com/qR6N3fe.png =x70)

---

## ROS node 實做
### 建立你的ROS workspace
+  將workspace加入環境並建立捷徑
+  確認setup.bash位置 (善用tab↹）
```
cat ~/Desktop/my_ws/devel/setup.bash
```
+  編輯 ~/.bashrc
```
vim ~/.bashrc
```
+  加入環境path
```
source ~/Desktop/my_ws/devel/setup.bash
```
![](https://i.imgur.com/soIDn7P.png)

+ 使 ~/.bashrc 生效
```
source ~/.bashrc
```

---

## ROS node 實做
### 建立你的ROS project
+  進入src folder
```
~/my_ws$ cd src
```
+  創建project
catkin_create_pkg <package_name> [depend1] [depend2]...
```
~/my_ws/src$ catkin_create_pkg my_first_project std_msgs rospy roscpp
```
![](https://i.imgur.com/FkkJktB.png =100%x)

---

## ROS node 實做
### 建立你的ROS project
+  回到workspace目錄後build
```
~/my_ws/src$ cd ..
~/my_ws$ catkin_make
```
![](https://i.imgur.com/pYsCoZs.png)

---

## ROS node/topic 實做
### 建立你的ROS project
+  進入project folder
```
~/my_ws$ cd src/my_first_project/
~/my_ws/src/my_first_project$ ls
```
+  package.xml 定義文件
```
~/my_ws/src/my_first_project$ vim package.xml
```
+  
    +  description  ------功能描述
    +  maintainer  ------作者資料
    +  license  ------授權條款
    +  dependencies
+  CMakeLists.txt  build用
```
~/my_ws/src/my_first_project$ vim CMakeLists.txt
```

---

## ROS node 實做 
+  前往你的project/src
```
~/my_ws/src/my_first_project$ cd src
```
+  建立檔案 first_node.py
```
~/my_ws/src/my_first_project/src$ vim first_node.py
```
+  建立node: init_node ( 'node名稱' , anonymous=True/False名稱是否加上隨機數)
```python=
import rospy

rospy.init_node('first_node')  #same as file name
i=0

while not rospy.is_shutdown():
    print("node working",i)
    i=i+1
    rospy.sleep(1)
```

---

## ROS node 實做 
+  直接啟動node（記得先開roscore)
![](https://i.imgur.com/H1u9WIU.png)
+  查看node list
![](https://i.imgur.com/FySvzxn.png)

---

## ROS node 實做
+  改用rosrun啟動node
+  在程式碼第一行加上Hashbang
```=
#!/usr/bin/env python3
...
```
+  將檔案設為執行檔 chmod +x file_name
```
~/my_ws/src/my_first_project/src$ chmod +x first_node.py
```
![](https://i.imgur.com/aRnCmCJ.png)
+  用rosrun啟動node (善用tab↹） ( rosrun project_name file_name )
![](https://i.imgur.com/m5AEBky.png)

---

## ROS node 實做
+  log種類（層級）
```python=
rospy.init_node('first_node', log_level=rospy.DEBUG)  #same as file name

    print("node working",i)
    rospy.logdebug("node \"first_node\" sending message")
    rospy.loginfo("log info: 1+1=2")
    rospy.logwarn("log warn: battery low")
    rospy.logerr("log err: camera is dead")
    rospy.logfatal("log fatle: battery catch fire")
```
![](https://i.imgur.com/Zyifc3i.png =x150)

---

## ROS topic 實做
### 由node建立topic並發布消息
+  import topic 要用的msg格式
```python
from std_msgs.msg import String
```
+  建立發布器: Publisher( 'topic名稱' ,  msg_type , queue_size=10)
```python
pub = rospy.Publisher('chatter', String, queue_size=10)
```
+  用發布器發布消息
```python
publish_data = "node working "+str(i)
pub.publish(publish_data)
```

---

## ROS topic 實做
### 由node建立topic並發布消息

![](https://i.imgur.com/cbqVbOW.png)
![](https://i.imgur.com/eNx3g1A.png)

---

## ROS topic 實做
### 由node建立topic並發布消息
+  正規msg寫法
```python
#publish_data = "node working "+str(i)
msg = String()
msg.data = str
pub.publish(msg)
```
+  ![](https://i.imgur.com/MYMoPy5.png =x150)

+  若msg包含多個數值，須以此方式給值

---

## ROS topic 實做
### 利用rospy.Rate控制發布頻率
+  設定發布頻率
```python
rate = rospy.Rate(10)
```
+  更改sleep寫法
```python
#rospy.sleep(0.1)
rate.sleep()
```

![](https://i.imgur.com/o8g9Whx.png)

---

## ROS topic 實做
### 利用rospy.Rate控制發布頻率


+  rospy.sleep(0.1) 發布頻率受運算時間影響
+  rospy.Rate(10) 頻率較準確
+  ![](https://i.imgur.com/Xkaf1DT.png =x150)

---

## ROS topic 實做
### 新增node接收topic消息

+  建立接收器： Subscriber( 'topic名稱' ,  msg_type , Callback_Function )
```python
#!/usr/bin/env python3
import rospy
from std_msgs.msg import String

def callback(data):
    print("I heard something: ", data.data)

rospy.init_node('listener')
rospy.Subscriber("chatter", String, callback)

rospy.spin()
```
+  ![](https://i.imgur.com/E3XkGdX.png)

---

## ROS topic 實做
### 新增node接收topic消息

![](https://i.imgur.com/jGt9raM.png)

---

## ROS topic 實做
### Subscriber特性介紹

+  Subscriber : ROS will call the chatterCallback() function whenever a new message arrives.  ---->  有點像interrupt概念

+ rospy.spin() : simply keeps your node from exiting until the node has been shutdown.  ---->  等同於 while not rospy.is_shutdown()
```python
while not rospy.is_shutdown():
    for i in range(10):
        print("waiting for msg",i)
        rospy.sleep(0.3)
#rospy.spin()
```
+  ![](https://i.imgur.com/D3RSDr7.png =x150)
[ROS_graph](#13)

---

## 認識 Service and Client
+ 啟動小烏龜+開啟鍵盤控制
```text
rosrun turtlesim turtlesim_node
rosrun turtlesim turtle_teleop_key
```
+  指令說明
![](https://i.imgur.com/PLMQ7SH.png =x250)

---

## 認識 Service and Client
+ rosservice info
![](https://i.imgur.com/98WWpam.png)
+ rosservice call
![](https://i.imgur.com/Q4IunO4.png)
![](https://i.imgur.com/c8nKolE.png =x150)  ![](https://i.imgur.com/RBquCci.png =x90)

---

## 認識 Service and Client
+ rosservice info
+ rosservice call
![](https://i.imgur.com/ImPFYLJ.png)
![](https://i.imgur.com/s6qOozx.png =x300)

---

## ROS Service/Client 實做
service做加法後回傳client
+  建立service node(記得用chmod改權限）
```
~/my_ws/src/my_first_project/src$ vim service.py
```

```python=
#!/usr/bin/env python3

from rospy_tutorials.srv import AddTwoInts,AddTwoIntsResponse
import rospy

def handle_add_two_ints(req):
    print(req)
    print("Returning [%s + %s = %s]"%(req.a, req.b, (req.a + req.b)))
    response = AddTwoIntsResponse()
    response.sum = req.a + req.b
    return response

rospy.init_node('add_two_ints_server')
rospy.Service('add_two_ints', AddTwoInts, handle_add_two_ints)
print("Ready to add two ints.")
rospy.spin()
```
![](https://i.imgur.com/wAd3VNh.png)



---

## ROS Service/Client 實做
service做加法後回傳client
+  啟動service node（記得先開roscore)
```
rosrun project service.py
```
+  用rosservice call指令發送request
```
rosservice call /add_two_ints 1 1
```
![](https://i.imgur.com/IkODvXY.png =600x)
![](https://i.imgur.com/3Ddot0m.png =600x)

---

## ROS Service/Client 實做
service做加法後回傳client
+  建立client (記得用chmod改權限）
```
~/my_ws/src/my_first_project/src$ vim client.py
```

```python=
#!/usr/bin/env python3

import rospy
from rospy_tutorials.srv import AddTwoInts,AddTwoIntsRequest

print("waiting for service: add two ints")
rospy.wait_for_service('add_two_ints')
add_two_ints = rospy.ServiceProxy('add_two_ints', AddTwoInts)

while not rospy.is_shutdown():
    print("Input the value of a & b")
    a, b = map(int, input().split())
    try:
        request = AddTwoIntsRequest()
        request.a = a
        request.b = b
        resp = add_two_ints(request)
        print("%s + %s = %s"%(a,b,resp.sum))
    except rospy.ServiceException as e:
        print("Service call failed: %s"%e)
```
+  ( 不用init node?? )

---

## ROS Service/Client 實做
service做加法後回傳client
+  啟動client（記得先開roscore 跟service)

![](https://i.imgur.com/qsGMd3X.png)

[ROS_graph](#13)

---

## ROS msg/srv 介紹
![](https://i.imgur.com/7my2WJx.png)

wiki:
md5sum是一種電腦程式，用於計算與校驗RFC 1321所描述的128位元MD5雜湊值，此處MD5雜湊值（或校驗和）作一個檔案的數字指紋使用。

---

## ROS msg/srv 介紹

![](https://i.imgur.com/11cCVer.png)
![](https://i.imgur.com/9QgkGYS.png)

---

## ROS 自定義 msg/srv
http://wiki.ros.org/msg
+  回到project資料夾並建立msg資料夾
+  建立你自己的msg file
```text=
/my_ws/src/my_first_project/src$ cd ..
/my_ws/src/my_first_project$ mkdir msg
/my_ws/src/my_first_project$ cd msg
/my_ws/src/my_first_project/msg$ vim my_control.msg
```
```text=
geometry_msgs/Twist twist
int8 speed
string color
```

---

## ROS 自定義 msg/srv
http://wiki.ros.org/msg
在package.xml加上兩行
```text
  <build_depend>message_generation</build_depend>
  <exec_depend>message_runtime</exec_depend>
```
編輯CMakeLists.txt
1.  find_package
![](https://i.imgur.com/tmYj50x.png)

---

## ROS 自定義 msg/srv

2.  add_message_files
![](https://i.imgur.com/xEm5ywV.png)
3.  generate_messages
![](https://i.imgur.com/F16WQYh.png)
4.  catkin_package
![](https://i.imgur.com/hbS0aPg.png)

---

## ROS 自定義 msg/srv
+  在workspace資料夾build
```
/my_ws$ catkin_make
```
![](https://i.imgur.com/dsXeO6x.png)

---

## ROS 自定義 msg/srv
+  建立一鍵盤cmd publisher
```python=
#!/usr/bin/env python3
import rospy
from project.msg import my_control
from pynput.keyboard import Listener,Key

def on_press(key):
    global my_control_msg
    try:
        value = key.char
    except:
        print(key,end='\r')
        return
    flag=False
    if value=='w':
        my_control_msg.twist.linear.x = 1.0
        flag=True
    if value=='1':
        my_control_msg.speed = 1
        flag=True
    if value=='g':
        my_control_msg.color = 'green'
        flag=True
    if value=='o':
        my_control_msg = my_control()
        flag=True
    if flag:
        pub.publish(my_control_msg)
    print('\r                         ',end='\r')

keyboard = Listener(on_press=on_press)
rospy.init_node('pub_my_msg')
pub = rospy.Publisher('my_cmd', my_control, queue_size=10)
my_control_msg = my_control()

keyboard.start()
rospy.spin()
keyboard.stop()
```

---

## ROS 自定義 msg/srv
+  回到project資料夾並建立srv資料夾
+  建立你自己的srv file
```text=
/my_ws/src/my_first_project/src$ cd ..
/my_ws/src/my_first_project$ mkdir srv
/my_ws/src/my_first_project$ cd srv
/my_ws/src/my_first_project/srv$ vim my_add_two_int.srv
```
```text=
int64 a
int64 b
---
int64 sum
int64 mul
float64 div
string copm
```

---

## ROS 自定義 msg/srv
+  編輯CMakeLists.txt
![](https://i.imgur.com/37Sul9v.png)
+  在workspace資料夾build
```
/my_ws$ catkin_make
```
![](https://i.imgur.com/3tT9xeA.png)

---

## ROS 自定義 msg/srv
+  建立自己的 add two int
```python=
#!/usr/bin/env python3
from project.srv import my_add_two_int,my_add_two_intResponse
import rospy

def handle_add_two_ints(req):
    print(req)
    response = my_add_two_intResponse()
    response.sum = req.a + req.b
    response.mul = req.a * req.b
    response.div = req.a / req.b
    if req.a > req.b :
        response.copm = '>'
    elif req.a < req.b :
        response.copm = '<'
    elif req.a == req.b :
        response.copm = '='
    return response

rospy.init_node('my_add_two_int')
rospy.Service('add_two_ints', my_add_two_int, handle_add_two_ints)
print("Ready to add two ints.")
rospy.spin()
```

---

## ROS 自定義 msg/srv
+  建立自己的 add two int
![](https://i.imgur.com/3vGcBD7.png)
![](https://i.imgur.com/SCrU5Jr.png)






<style>
    
.reveal {
  font-size: 24px;
  line-height: 110px;
}
.reveal .slides {
    text-align: left;
}

    
</style>