---
# System prepended metadata

title: 機器人作業系統能幫我寫作業嗎？ROS 2 究竟是何方神聖

---

# 機器人作業系統能幫我寫作業嗎？ROS 2 究竟是何方神聖

Copyright (c) 2025 NCKU sspfr [section - 2](/0heMGItdRMmcdKViZ7po1Q)

## 前言
作業系統是個相對於直接的硬、軟體比較抽象的一個概念。

舉個例子來講，你今天買了一些電腦零件，好比 cpu、記憶體、主機板等等，開開心心回家組了台電腦，組完後颯爽的按下開機。螢幕除了輸出個 bios 啥都沒有，你還以為自己得了 MVP，結果現在連遊戲都不知道哪裡下載。頓時你想起了 windows 這個作業系統，只要安裝後鍵盤、滑鼠就可以做相對應的輸入，並且控制其他零件例如圖像的輸出、聲音的播放。作業系統即做了一個硬體－硬體間、硬體－軟體間，重要橋樑的工作。

回到機器人，假設你想組台會自己辨識路況的自駕車，你需要哪些零組件？會動要有馬達、馬達會動要有電池、辨識路況要有 sensor、運算 sensor 的回傳資料再決定車要往哪開要有軟體。這些東西加起來要你用 `c / c++` 寫一套韌體自己驅動應該會想死吧！這時候 ROS 就登場了，其可作為良好的橋樑，幫助我們更方便的做硬、軟體間的交互溝通。

## ROS 簡介
![image](https://hackmd.io/_uploads/S1tdZOCTJl.png)
ROS 全名 Robot Operating System，顧名思義是類似給機器人使用的作業系統，但此作業系統非彼傳統意義上的作業系統，更像是一套中介軟體，幫助開發者更容易的去建構複雜且強大的機器人軟體。

其開源易上手的特性吸引了不止各大企業使用，亦有不少個人、小團體的開發團隊使用其作為控制自動化、機器人的核心框架。

## ROS 的主要特色
### 模組化架構
ROS 採用節點（Node）架構，把多種功能拆分成一個一個的節點，方便編輯以及後續維護。
![image](https://hackmd.io/_uploads/S1uNc_06yl.png)
> 圖片出處：[The Robotics Backend](https://roboticsbackend.com/what-is-ros/)

### 通訊機制
提供發佈 / 訂閱（Publish / Subscribe）、服務（Service）和動作（Action）等通訊模式，讓各個節點之間能夠有效協作。

### 與硬體搭配
ROS 能夠與多種感測器與執行器整合，讓開發者不必從 kernal 開始編寫。

## ROS 1 和 ROS 2 的區別
![image](https://hackmd.io/_uploads/ByS9xq061l.png)
> 圖片出處：[Research Gate](https://www.researchgate.net/figure/ROS1-ROS2-architecture-for-DDS-approach-to-ROS-We-clarify-the-performance-of-the-data_fig1_309128426)
### 節點通訊
如上圖可見，ROS 1 在節點的管理上還需額外設置 ROS Master，ROS 2 則改用 DDS（Data Distribution Servivce） 作為通訊介面，在節點的設定上較為容易，亦在資料的傳輸上更為安全。

### 多系統支援
ROS 1 僅可在 linux 中運行，而 ROS 2 則擴展對於 windows 和 macOS 的支援。
> 雖然表面上支援市面上多數作業系統，但在 windows 和 macOS 上運行時還是常會遇到滿滿的 bug，故推薦還是以 docker 或是 wsl 在 linux 系統中運行。



## 放鬆一下

關於標題的統一回答：不行，他不能幫你在 Deadline 以前趕出作業。

>[!Caution] 厭煩
好爛喔！那他到底可以幹嘛？

他可以幹的事可多了！說不定你夠有能力，給他施點 AI 的魔法，他說不定能夠控制好幾個機器人，同步幫你完成不同的工作、順便還能幫你拿個外賣，讓你有更多時間能拿去陪女朋友逛逛街、看看電影！

>[!Tip] 興奮
尊都假都，我要學我要學！MVP、MVP！

section - 1 的作業寫完了沒有！

>[!Caution] 齁某
阿阿阿阿阿阿阿（悲）

## Node 簡介
ROS 中，節點（Node）為最基本的運作單位，各自管理著單一任務。例如：
- 發布、接收 topic 的訊息
- 控制機器人移動
- 處理圖像辨識
### 創建一個 Node
我們用 python 來做個範例。
```python=
class HelloPublisher(Node):
    def __init__(self):
        super().__init__('hello_publisher')  # 建立一個名為 hello_publisher 的節點
        ...
```
如上，我們在第三行即向我們的 package 註冊了一個名為 `hello_publisher` 的 node。
>[!Tip]
一個 Package 中 Node 的名字必須為一。

## Publisher、Subscriber 和 Topic 簡介
小明是個喜歡玩 dota 遊戲的有為青年，因為覺得自己的操作特別厲害、和隊友的溝通非常有（ㄇㄟˊ ）效（ㄇㄚ），故想創一個 youtube 頻道上傳自己的遊玩精華。

而小明的爸爸非常欣賞兒子的言談，認為小明的影片是他每天睡前唯一的娛樂，故到小明的 youtube 頻道按下了訂閱，這樣每當小明發了新影片，爸爸就會收到通知。
![截圖 2025-04-05 晚上7.44.33](https://hackmd.io/_uploads/r1SF_qAayx.png)
> 非當事小明。圖片出處：[⚡小明劍魔⚡原版影片：我老爸得了ＭＶＰ！](https://www.youtube.com/watch?v=ldOn4FfS98A&t=69s&ab_channel=%E8%98%AD%E6%9E%97%E6%BC%A2%E7%9A%84%E7%B2%89%E7%B5%B2-%E7%8C%B4%E5%93%A5%E5%B0%88%E5%B1%AC%E7%B2%BE%E8%8F%AF%E9%A0%BB%E9%81%93)

此處的 youtube 頻道即可對應到 topic，而小明就扮演著 publisher 的角色，持續的發布新消息到 topic，而爸爸在此處則對應到了 subscriber，不斷地從 topic 接收 publisher 們所發布的消息。

## ROS 安裝
> 這裡只寫到如何在 Ubuntu 中安裝，因為應用場景最廣泛。
> 如有其他環境、系統有安裝需求可於 [Installation — ROS 2 Documentation](https://docs.ros.org/en/humble/Installation.html) 中查詢。

1. 安裝所需 Package
```bash
sudo apt update && sudo apt install -y curl gnupg lsb-release
```

2. 加上 ROS 公鑰、來源列表
```bash
sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg

echo "deb [signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] \
https://packages.ros.org/ros2/ubuntu \
$(. /etc/os-release && echo "$UBUNTU_CODENAME") main" \
| sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null
```

3. 安裝 ROS 輕量版（沒有 GUI）
```bash
sudo apt update
sudo apt install -y ros-humble-ros-base
```

4. 設定環境變數
```bash
echo "source /opt/ros/humble/setup.bash" >> ~/.bashrc
source ~/.bashrc
```
> 看你是什麼 Shell 存入對應設定檔中（例如 fish shell 就是存入 config.fish）

5. 安裝建置工具
```bash
sudo apt install -y python3-colcon-common-extensions \
python3-argcomplete \
build-essential
```

## ROS 使用範例
> 最好在 linux、mac、wsl 中運行

1. 建立、進入工作區。
```bash
mkdir -p ~/ros_ws/src
cd ~/ros_ws/src
```

2. 建立以 Python 編寫的 Package。
```bash
ros2 pkg create --build-type ament_python my_package
```
> my_package 為 Package 名稱，可自行替換。
```
my_package/
├── my_package/
│   └── __init__.py
├── package.xml
├── setup.cfg
├── setup.py
```
> 這會直接幫你建立目錄結構。

3. 進入 `my_package/my_package` 中加入兩個節點，一個 Publisher、一個 Subscriber，一個負責投送 `問天地好在`，一個負責接受到的訊息輸出。
```bash
cd my_package/my_package
touch hello_publisher.py hello_subscriber.py
```

`hello_publisher.py`：
```python= 
import rclpy
from rclpy.node import Node
from std_msgs.msg import String

class HelloPublisher(Node):
    def __init__(self):
        super().__init__('hello_publisher')
        self.publisher_ = self.create_publisher(String, 'hello_topic', 10)
        self.timer = self.create_timer(1.0, self.timer_callback)

    def timer_callback(self):
        msg = String()
        msg.data = '問天地好在'
        self.publisher_.publish(msg)
        self.get_logger().info(f'發佈：{msg.data}')

def main(args=None):
    rclpy.init(args=args)
    node = HelloPublisher()
    rclpy.spin(node)
    node.destroy_node()
    rclpy.shutdown()

if __name__ == '__main__':
    main()
```

`hello_subscriber.py`：
```python=
import rclpy
from rclpy.node import Node
from std_msgs.msg import String

class HelloSubscriber(Node):
    def __init__(self):
        super().__init__('hello_subscriber')
        self.subscription = self.create_subscription(
            String,
            'hello_topic',
            self.listener_callback,
            10
        )

    def listener_callback(self, msg):
        self.get_logger().info(f'收到訊息：{msg.data}')

def main(args=None):
    rclpy.init(args=args)
    node = HelloSubscriber()
    rclpy.spin(node)
    node.destroy_node()
    rclpy.shutdown()

if __name__ == '__main__':
    main()
```

4. 設定 `setup.py`，加入剛建立的兩個 Node。

`setup.py`：
```python=
from setuptools import setup

package_name = 'my_package'

setup(
    name=package_name,
    version='0.0.0',
    packages=[package_name],
    data_files=[
        ('share/ament_index/resource_index/packages',
         ['resource/' + package_name]),
        ('share/' + package_name, ['package.xml']),
    ],
    install_requires=['setuptools'],
    zip_safe=True,
    maintainer='你的名字',
    maintainer_email='你@email.com',
    description='Simple ROS 2 Publisher and Subscriber example',
    license='MIT',
    tests_require=['pytest'],
    entry_points={
        'console_scripts': [
            'hello_publisher = my_package.hello_publisher:main',
            'hello_subscriber = my_package.hello_subscriber:main',
        ],
    },
)
```

5. 在 `package.xml` 中檢查 Dependencies，確認包含以下兩者。

`package.xml`：
```xml=
<exec_depend>rclpy</exec_depend>
<exec_depend>std_msgs</exec_depend>
```

6. Build + Source（讓 Terminal 知道有這個指令集可供使用）。

回到根目錄並 build：
```bash
cd ~/ros2_ws
colcon build
```
build 完後會自動出現 `install` 這個資料夾，我們要 source 其中的 `setup.bash` 讓我們能夠執行這個 ROS Package。
```bash
source install/setup.bash
```
> 每次重開新的 Terminal 視窗都需要重新 source 一次，除非加入 Terminal 設定檔中（不推薦）

7. 開啟兩個 Terminal 視窗，並都先到工作區 source 一次
```bash
cd ~/ros_ws
source install/setup.bash
```

於第一 Terminal 開始 Publisher：
```bash
ros2 run my_package hello_publisher
```

於第二 Terminal 開始 Subscriber：
```bash
ros2 run my_package hello_subsriber
```

若成功了將會看到兩 Terminal 互相傳送、接收訊息：
```bash
[hello_publisher] 發佈：問天地好在
[hello_subscriber] 收到訊息：問天地好在
```

> 更多相關語法、指令可自行於 [ROS docs](https://docs.ros.org/) 中查詢。

## 參考資料
[ROS: Home](https://ros.org/)
[zh_tw - ROS Wiki](https://wiki.ros.org/zh_tw)
[機器人作業系統 - 維基百科，自由的百科全書](https://zh.wikipedia.org/zh-tw/%E6%A9%9F%E5%99%A8%E4%BA%BA%E4%BD%9C%E6%A5%AD%E7%B3%BB%E7%B5%B1)