<!-- .slides text-align: left; -->
# ROS / ROS2 教學
<br>
<br>
### NDHUEE 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

---
## 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
+ 眾多第三方工具
+ 被廣泛使用,且持續成長中
 
---
## 教學進度安排
+ 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|
|||
||
|
+ 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
+ 開始漫長的安裝~~~
---
## 安裝ROS
開始安裝前,這堂課需要準備什麼?
+ 電腦 ( PC, LAPTOP, Nvidia Jetson nano...)
+ USB 灌ubuntu
基礎知識
+ Python
+ Linux - Ubuntu
---
## 安裝ROS
開機後:
+ 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
```
---
## 安裝ROS
1.5 Environment setup
+ 在 ~/.bashrc 最後一行加上
```
source /opt/ros/noetic/setup.bash
```
+ 
+ 使 ~/.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
```
---
## 測試ROS是否安裝成功
```
roscore
```

---
## 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

[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
```
 
---
## 認識ROS指令格式
+ roscore, roscd
<br>
+ rosrun, roslaunch
+ {command} {pakage_name} {target_name}
<br>
+ rosnode, rostopic, rosservice, rosmsg, rosparam
+ {command} {tool} (target_name)
---
## 認識Node
+ 啟動node
```
rosrun pakage_name node_name
```
+ **rosnode**
```
rosnode ↹↹
```

+ 指令說明

---
## rosnode command
(在運行小烏龜的情況下)
+ rosnode list

+ rosnode info {node_name}

---
## rosnode command
(在運行小烏龜的情況下)
+ rosnode ping

+ rosnode machine (machine_name)

---
## rosnode command
(在運行小烏龜的情況下)
+ rosnode kill {node_name}


[ROS_graph](#13)
---
## 認識 Topic
+ **topic**
```
rostopic ↹↹
```

+ 指令說明

---
## rostopic command
(在運行小烏龜的情況下)
+ rostopic list

+ rostopic info

---
## rostopic command
(在運行小烏龜的情況下)
+ rostopic echo


(同時用key控制你的小烏龜~)
---
## rostopic command
(在運行小烏龜的情況下)
+ rostopic hz /turtle1/pose
+ rostopic bw /turtle1/pose

[ROS_graph](#13)
---
## 認識rqt
+ ROS視覺化工具
```
rqt
```

---
## ROS node 實做

---
## 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
```

---
## 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
```

+ 使 ~/.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
```

---
## ROS node 實做
### 建立你的ROS project
+ 回到workspace目錄後build
```
~/my_ws/src$ cd ..
~/my_ws$ catkin_make
```

---
## 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)

+ 查看node list

---
## 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
```

+ 用rosrun啟動node (善用tab↹) ( rosrun project_name file_name )

---
## 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")
```

---
## 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並發布消息


---
## ROS topic 實做
### 由node建立topic並發布消息
+ 正規msg寫法
```python
#publish_data = "node working "+str(i)
msg = String()
msg.data = str
pub.publish(msg)
```
+ 
+ 若msg包含多個數值,須以此方式給值
---
## ROS topic 實做
### 利用rospy.Rate控制發布頻率
+ 設定發布頻率
```python
rate = rospy.Rate(10)
```
+ 更改sleep寫法
```python
#rospy.sleep(0.1)
rate.sleep()
```

---
## ROS topic 實做
### 利用rospy.Rate控制發布頻率
+ rospy.sleep(0.1) 發布頻率受運算時間影響
+ rospy.Rate(10) 頻率較準確
+ 
---
## 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()
```
+ 
---
## ROS topic 實做
### 新增node接收topic消息

---
## 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()
```
+ 
[ROS_graph](#13)
---
## 認識 Service and Client
+ 啟動小烏龜+開啟鍵盤控制
```text
rosrun turtlesim turtlesim_node
rosrun turtlesim turtle_teleop_key
```
+ 指令說明

---
## 認識 Service and Client
+ rosservice info

+ rosservice call

 
---
## 認識 Service and Client
+ rosservice info
+ rosservice call


---
## 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()
```

---
## ROS Service/Client 實做
service做加法後回傳client
+ 啟動service node(記得先開roscore)
```
rosrun project service.py
```
+ 用rosservice call指令發送request
```
rosservice call /add_two_ints 1 1
```


---
## 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)

[ROS_graph](#13)
---
## ROS msg/srv 介紹

wiki:
md5sum是一種電腦程式,用於計算與校驗RFC 1321所描述的128位元MD5雜湊值,此處MD5雜湊值(或校驗和)作一個檔案的數字指紋使用。
---
## ROS msg/srv 介紹


---
## 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

---
## ROS 自定義 msg/srv
2. add_message_files

3. generate_messages

4. catkin_package

---
## ROS 自定義 msg/srv
+ 在workspace資料夾build
```
/my_ws$ catkin_make
```

---
## 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

+ 在workspace資料夾build
```
/my_ws$ catkin_make
```

---
## 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


<style>
.reveal {
font-size: 24px;
line-height: 110px;
}
.reveal .slides {
text-align: left;
}
</style>
{"metaMigratedAt":"2023-06-17T14:49:11.951Z","metaMigratedFrom":"YAML","title":"ROS / ROS2 教學","breaks":true,"contributors":"[{\"id\":\"df131c7b-c358-40e5-ac23-6743dd1002f9\",\"add\":21065,\"del\":5057}]"}