之前我們利用過 PWM 功能來控制 LED 燈泡的亮度,甚至控制全彩 LED 燈泡進行混色。在這篇文章中,我們將利用 Raspberry Pi 的 PWM 功能搭配 Python 程式來控制另一種很常見的元件-伺服馬達。伺服馬達的英文為 Servo Motor,往往簡稱為 Servo。

伺服馬達

在學校裡做實驗常遇到一種小馬達,當使用電池通電後透過電磁效應會不停的轉動,這種馬達稱為直流馬達,與我們這次要使用的伺服馬達並非同樣的東西。
伺服馬達與直流馬達有很多的不同之處。在外觀上,伺服馬達擁有三支接腳,除了電源與接地外,另有一個用來控制的接腳。而這個接腳所能控制的,並非馬達的啟動或轉速,而是馬達軸心的旋轉角度。所以伺服馬達可以用來控制車子輪胎的轉向,但是卻不適合用來作為前進之用。此外,很多伺服馬達的旋轉範圍並不到一整圈 (也就是 360 度),而可能僅有 180 度的範圍。

以我這次使用的 SG90 為例 (參見下圖),三隻接腳依序分別為控制 (橘色)、電源 (紅色) 與接地 (棕色 )。

Tower Pro SG90 伺服馬達

Tower Pro SG90 伺服馬達

在 Raspberry Pi 使用伺服馬達有一些需要特別注意的地方。

  • 首先當然是控制能力。因為 Raspberry Pi 的 PWM 的功能並不算上強大,如果使用的是軟體模擬 PWM,效果更是差強人意。而由於 PWM 是用來控制伺服馬達旋轉角度的機制,所以使得 Raspberry Pi 在控制伺服馬達的角度方面並不是那麼地精準跟穩定。
  • 再來則是電壓的問題。一般伺服馬達無法使用 +3.3V 加以驅動,所以除非透過外部電源,否則就只能使用 +5V 接腳當作供電來源。而 Raspberry Pi 的 +5V 電壓直接來自於 USB (或外接電源),並沒有經過整流,所以電源的穩定度較差。
  • 最後則是電流的問題。因為 Raspberry Pi 的 +5V 電源來自於 USB (或外接電源),其實際上能夠輸出的最大電流受到很多因素的影響,所以可能因為輸出電流不夠而造成伺服馬達運作不正常。此外,伺服馬達啟動時通常會有一瞬間的較大電流,也有可能導致板子的損毀。

總結來說,如果想要比較精確地控制伺服馬達,尤其是多個伺服馬達時,可以考慮選用專用的 HAT 或是改用 Arduino 等其他控制板。

PWM 與旋轉角度

前面提到,PWM 可以用來控制伺服馬達的旋轉角度。記得之前在控制 LED 燈泡亮度時,我們是利用工作週期的長短來控制燈泡亮度。不過在控制伺服馬達時,並不是直接利用工作週期來控制旋轉角度,而是透過高電位的持續時間,而這個時間也常常被稱為脈衝寬度。

問題來了,到底脈衝寬度與旋轉角度的關係是甚麼?根據維基百科上的資料,大多數伺服馬達以 1 ms 的脈衝寬度當作 -90 度,而 2 ms 則為 +90 度。

PWM 脈衝寬度與伺服馬達旋轉角度 (來源: (來源: wikipedia))

PWM 脈衝寬度與伺服馬達旋轉角度 (來源: 維基百科)

不過因為我們所使用的 Python 套件必須指定 PWM 的工作週期而非脈衝寬度,所以我們必須先進行轉換。當頻率為 50 Hz 時,則每一個週期為 1/50 = 0.02 秒,也就是 20 毫秒 (ms)。1 ms 的脈衝寬度其工作週期為 1 / 20 * 100% = 5%。

PWM 工作週期與脈衝寬度

PWM 工作週期與脈衝寬度

同樣的,2 ms 脈衝寬度的工作週期則為 10%。所以我們只要把 PWM 的頻率設定為 50 Hz,然後控制工作週期在 5~10% 之間,就可以從 -90 度旋轉到 +90 度。不過因為其實轉軸外部並沒有任何記號,所以為了方便我們也可以說是從 0 度轉到 180 度 (逆時針方向)。換句話說,旋轉角度與工作週期的關係可用下表呈現:

旋轉角度工作週期 (當頻率為 50 Hz 時) 
0 度5%
45 度6.25%
90 度7.5%
135 度8.75%
180 度10%

或者也可以用下列圖形加以說明:

PWM 工作週期與伺服馬達旋轉角度對應關係

PWM 工作週期與伺服馬達旋轉角度對應關係

過程聽起來有點複雜,好在結果簡單無比,其實就是一個一元一次函數。根據前面分析的結果,只要把 5%~10% 之間的差距平分給 180 個角度,就可以控制伺服馬達的旋轉角度了。也就是
\begin{align}
當角度為 x 時的工作週期 (\%) & = 5 + \frac{10-5}{180} \times x\\
& = 5 + \frac{x}{36} (公式一)
\end{align}
但是實際上根據此公式來控制 SG90,將會發現角度有很大的偏差。出了什麼問題?事實上,維基百科上面說的是大多數伺服馬達應該如此,而不是所有伺服馬達皆是如此。不過看了許多網路上的教學文件也都提到同樣的公式,僅有部分提到每個伺服馬達可能會有誤差,所以需要針對公式自行加以調整。但是我觀察到的角度偏差之大已經不像是誤差,而更像是計算錯誤。就算 Raspberry Pi 的 PWM 功能並不是很精確,也不應該產生如此大的偏差。

為了洗清 Raspberry Pi 的嫌疑,我找了手邊的 Arduino 接上同一個 SG90 伺服馬達,發現用 Arduino 的範例程式確實可以控制旋轉角度在 0 度到 180 之間,僅有小小的偏差。不過範例程式用的是 Arduino 的 Servo 函式庫,所以可以直接指定角度,而不是工作週期或脈衝寬度。好在 Servo 函式庫亦可以指定脈衝寬度,而當我改用脈衝寬度後,發現跟 Raspberry Pi 一樣會有很大的偏差。所以基本上排除了 Raspberry Pi 的嫌疑,問題應該是出在脈衝寬度與旋轉角度的計算公式上。

經過查看 Servo 函式庫的文件後,發現預設 0 度的脈衝寬度是 544 microsecond,也就是 0.544 ms,而預設 180 度的脈衝寬度則是 2400 microsecond,也就是 2.4 ms。而在某些文件中可以看到 SG90 的脈衝寬度為 0.5 ms ~ 2.4 ms,所以使用 1 ms ~ 2 ms 當然就會有很大的偏差。此外,少數資料對 0.5 ms ~ 2.4 ms 的解釋是 SG90 可以擁有更大的旋轉角度,但是很顯然實際上並不是這樣。否則根據這句話來看,SG90 已經可以旋轉接近 360 度了。

同樣在頻率為 50Hz 的情況下,脈衝寬度為 0.5 ms 時的工作週期為 2.5%,而 2.4 ms 的工作週期則為 12%,所以針對 SG90 修改公式如下:
\begin{align}
當角度為 x 時的工作週期 (\%) & = 2.5 + \frac{12-2.5}{180} \times x\\
& = 2.5 + \frac{19}{36} \times x (公式二)
\end{align}
一樣是一個一元一次函數。
不過在實際撰寫程式時,因為我們希望頻率是可以由程式加以指定,所以需要重新改寫如下:
\begin{align}
當頻率為 PWM\_FREQ 時,每周期為 \frac{1}{PWM\_FREQ} 秒,也就是 \frac{10^3}{PWM\_FREQ} ms。
\end{align}
\begin{align}
所以脈衝寬度為 0.5 ms 時的工作週期 (\%) & = \frac{0.5}{\frac{10^3}{PWM\_FREQ}} \times 100\\
& = \frac{0.5 \times PWM\_FREQ}{10^3} \times 100\\
& = \frac{0.5 \times PWM\_FREQ}{10}\\
& = 0.05 \times PWM\_FREQ
\end{align}
\begin{align}
而脈衝寬度為 2.4 ms 時的工作週期 (\%) & = \frac{2.4}{\frac{10^3}{PWM\_FREQ}} \times 100\\
& = \frac{2.4 \times PWM\_FREQ}{10^3} \times 100\\
& = \frac{2.4 \times PWM\_FREQ}{10}\\
& = 0.24 \times PWM\_FREQ
\end{align}
\begin{align}
& 所以公式二可以改成\\
& 當頻率為 PWM\_FREQ 且角度為 x 時的工作週期 (\%)\\
& = 0.05 \times PWM\_FREQ + \frac{0.24 \times PWM\_FREQ – 0.05 \times PWM\_FREQ}{180} \times x\\
& = 0.05 \times PWM\_FREQ + \frac{0.19 \times PWM\_FREQ}{180} \times x\\
& = 0.05 \times PWM\_FREQ + \frac{0.19 \times PWM\_FREQ \times x}{180} (公式三)
\end{align}
此公式即為後面 Python 程式所使用之計算方式。

虛擬環境配置

因為這個範例一樣僅需要 Python 的 RPi.GPIO 模組,所以準備工作與 在 Raspberry Pi 3 Model B 建立 Python 3.6 環境 一致。如果你還沒有完成相關的準備,請務必先行完成再繼續進行後面的工作。

線路圖

Raspberry Pi 與 SG90 伺服馬達

Raspberry Pi 與 SG90 伺服馬達

說明如下:

  1. 在這個範例中我們將使用軟體模擬的 PWM 功能,所以控制接腳 (橘色) 可連結至任一可用於輸出的腳位,這個範例中我們使用實體編號 11 的腳位 (其 BCM 編號為 17)。
  2. SG90 的工作電壓為 +4.8V 以上,所以我們必須連結至 Raspberry Pi +5V 的腳位,在此我們將 SG90 電源輸入接腳 (紅色) 連結至實體編號 2 的腳位。
  3. 接地接腳 (棕色) 必須接地,所以連結至 Raspberry Pi 的實體編號 6 的腳位。
  4. 腳位對應關係整理如下:
    SG90 伺服馬達接腳Raspberry Pi GPIO 腳位
    橘色 (控制訊號)實體編號 11 (BCM 編號 17)
    紅色 (電源輸入)實體編號 2 (+5V)
    棕色 (接地)實體編號 6 (接地)
  5. 如之前所提,伺服馬達的啟動或運作電流可能會過大而造成運作不正常、甚至損毀 Raspberry Pi,因此如果你手邊的伺服馬達並沒有註明型號,也沒有相關規格,請避免直接連結至 Raspberry Pi。

程式代碼

利用文字編輯器 (如 nano) 新增 Python 程式 pwm_sg90.py

內容如下

程式說明如下:

  1. 第 1 行引入 RPi.GPIO 套件,在這個範例中我們將使用這個套件所提供的 PWM 功能。
  2. 第 4 行定義控制接腳所連結之 Raspberry Pi 腳位,我們連結的是實體編號 11 的腳位,其 BCM 編號為 17。
  3. 第 5 行定義 PWM 所使用的頻率。
  4. 第 6 行定義每次旋轉的角度。
  5. 第 8 行使用 BCM 編號方式。
  6. 第 9 行將控制腳位設定為輸出。
  7. 第 11 行宣告 pwm 控制物件。
  8. 第 12 行開始 pwm 功能。
  9. 第 14~16 行定義角度與工作週期的對應關係,也就是前述的公式三。
  10. 第 20~29 行透過兩個 for 迴圈將 SG90 由 0 度逐次旋轉至 180 度,之後再逐次轉回 0 度。
  11. 第 30 行將 SG90 軸心旋轉至 90 度。
  12. 第 31~32 行執行無任何功能的迴圈,其目的是讓 SG90 的軸心停在 90 度的位置。
  13. 第 36~37 行確定程式停止後 pwm 功能會被關閉,而且程式所使用的腳位會回到預設狀態。

程式完成後,輸入下列指令執行程式

順利的話,就可以看到 SG90 的軸心開始每隔兩秒移動 15 度,並在畫面上印出目前的旋轉角度。而當來回移動一次後,SG90 的軸心回到 90 度的中央位置不再轉動。如下方影片中所示。

在這個範例中,我們看到了如何利用 Raspberry Pi 的 PWM 功能來控制伺服馬達 (SG90)。不過如影片中的最後一段,當程式最後將 SG90 軸心停在 90 度的固定位置時,卻會不時出現抖動的現象。我們一再提到 Raspberry Pi 利用軟體模擬方式產生的 PWM 訊號畢竟與硬體式的訊號有所差距,所以在下一篇中,我們將利用硬體式的 PWM 功能來控制伺服馬達,看看是否會有不一樣的效果。

如果對文章的內容有任何疑問或建議,歡迎與我聯絡。

Facebook 留言
Print Friendly, PDF & Email
Summary
Raspberry Pi 3 Mobel B 利用 PWM 控制伺服馬達
Article Name
Raspberry Pi 3 Mobel B 利用 PWM 控制伺服馬達
Description
在這篇文章中,我們將利用 Raspberry Pi 的 PWM 功能搭配 Python 程式來控制另一種很常見的元件-伺服馬達。伺服馬達的英文為 Servo Motor,往往簡稱為 Servo。
Author
Publisher Name
Everlearn Studio
Publisher Logo