Raspberry Pi Pico筆記(11):控制伺服馬達Servo

本篇繼續針對馬達類的伺服馬達進行控制,我手邊有兩種伺服馬達(或稱「舵機」)的型號:MG90S 和 MG996R,接線的方式是一樣的。與馬達不同,馬達主要功能是旋轉,需控制的是轉速和方向。而伺服馬達在意的是旋轉角度,不需要旋轉一整圈,一般旋轉的角度範圍是 0 度到 180 度。

控制伺服馬達轉速或 LED 調光,大都會使用 PWM(Pulse Width Modulation, 脈衝寬度調製)計數,它可以透過改變其輸入電壓來控制直流電機的速度。PWM是一種利用發送一系列ON-OFF的脈衝來調節輸入電壓平均值的技術。以容易懂的言語來說,就是頻繁變更電壓的開關,在固定時間內程式高速切換,電壓被開關很多次,當開的時間長一點時,馬達的速度就會比較快;如果時間內,關的時間長一點,馬達的速度就會比較慢。我們將給予電壓的時間比例稱為佔空比(Duty Cycle)。佔空比越高,施加到直流電動機的平均電壓越高(高速),佔空比越低,施加到直流電動機的平均電壓越低(低速)。例如:燈光的亮度控制、馬達轉速控制、螢幕亮度控制、喇叭大小聲/聲音頻率高低控制等。

以本實作的 MG996R 為例,一般而言,PWM 控制伺服馬達的基準信號周期為 20ms,基準脈寬為 1.5ms(中位),理論上脈寬應在 1ms 到 2ms 之間,但是,實際上脈寬可由 0.5ms 到 2.5ms 之間,脈寬和舵機的轉角0°~180°相對應。我們用下表來看一下如何算佔空比。
信號脈寬伺服馬達轉動角度佔空比
0.5ms0度2.5% ( 0.5ms 高電位 + 19.5 低電位)
1ms45度5% ( 1ms 高電位 + 19.0 低電位)
1.5ms90度7.5% ( 1.5ms 高電位 + 18.5 低電位)
2.0ms135度10% ( 2ms 高電位 + 18.0 低電位)
2.5ms180度12.5% ( 2.5ms 高電位 + 17.5 低電位)

根據上述信號脈寬對應的角度換算成佔空比,公式為:2.5+角度/180*10 ,以 Pi Pico 的 PWM 引腳解析度為 2^16 = 65535,換算成 0 度時,其佔空比值為 65535 * 2.5% = 1638.375 ,當角度為 180 度時,其佔空比值為 65535 * 12.5% = 8191.875,這兩個值會跟程式有關,考量到誤差及轉動角度,我將佔空比訂在 1000 與 9000 之間,可以讓舵機順利轉動 0 ~ 180度。

在使用 PWM 控制伺服馬達時,發現佔空比的值與 LED 有所不同,原因是伺服馬達控制其轉動最大角度僅 180 度,而 LED 的亮度,其佔空比範圍可以從 0 到 65025。我查了相關文件,沒有說明 65025 是如何算出來的?但是這個值剛好是 255 * 255,猜想是 8 位元的 PWM 最大值是 255,pwm.duty_u16 是將兩個 (2^8-1=255)相乘得到的最大值。這表示 65525 會是 100% 的時間,都讓 LED 或直流電機保持點亮或全值的狀態。

MG996R 伺服馬達的規格如下:
  • 尺寸:54 x 38 x 20 mm
  • 重量:55g
  • 工作電壓:4.8V~7.2V
  • 空載工作電流:120mA
  • 堵轉工作電流:1450mA
  • 運行速度:0.13秒/60度(6.0V空載)
  • 舵機扭矩:11kg*cm(6.0V)
  • 迴旋角度:可控制角度約 180度/270度(最大)
  • 齒輪:5級金屬齒輪組
  • 重量:55g

[材料]

  • Raspberry Pi Pico x1
  • MG996 伺服馬達 x1
  • 可變電阻10K x1
  • 麵包板 x1
  • 麵包板電源模組 x1
  • 排線 n 條

[接線圖]

由於 Pi Pico 僅能提供 3.3V 電源,伺服馬達需外接電源,並將 Pi Pico GND 連接到電源接地線。可變電阻的外側兩腳接麵包板電源提供的正負極,中間的 Pin 接Pico 的 ADC0 。如下圖表:
Pi Pico接腳MG996R伺服馬達10K可變電阻
Pin38(GND)GNDGND
Pin31(GP26/ADC0)-Data(中間Pin)
Pin1(GP0)Data-

[程式一]

頻率 pwm.freq 告訴 Pi Pico 多久時間切換開和關,程式設為常用的 50。以下程式將 9000 與最大值 65025 相除算出一個固定常數,將可變電阻讀取到的值除以這個常數,換算成伺服馬達的佔空比。以下是完整的程式,雖僅有短短幾行,卻可使用電阻來控制伺服馬達的轉動:
from machine import Pin, PWM

analogvalue = machine.ADC(26)

pwm = PWM(Pin(0))
pwm.freq(50)
scale = 9000/65025

while True:
    reading = analogvalue.read_u16()
    pwm.duty_u16(int(reading*scale))

[程式二]

這段程式使用一段轉換程式,將輸入的角度 Map 成 1000 ~ 9000 的佔空比後,讓伺服馬達轉動至該角度,程式中的迴圈,讓伺服馬達持續正反轉 180 度。
from utime import sleep
from machine import Pin
from machine import PWM

pwm = PWM(Pin(0))
pwm.freq(50)

# 設定伺服馬達的轉動角度
def setServoCycle (position):
    pwm.duty_u16(position)
    sleep(0.01)

# 將轉動角度換算成佔空比
def convert(x, i_m, i_M, o_m, o_M):
    return max(min(o_M, (x - i_m) * (o_M - o_m) // (i_M - i_m) + o_m), o_m)

while True:
    for degree in range(0, 180, 1):
        pos = convert(degree, 0, 180, 1000, 9000)
        setServoCycle(pos)

    for degree in range(180, 0, -1):
        pos = convert(degree, 0, 180, 1000, 9000)
        setServoCycle(pos)

[結果]

程式一的執行結果:

程式二的執行結果:

[參考資料]

Post a Comment

較新的 較舊