利用 Python 和 Raspberry Pi 的 GPIO 控制兩個伺服馬達,創造一個能讓貓咪追逐隨機移動紅點的玩具。此專案結合了硬體控制和軟體設計,實作了雷射點的隨機運動,增加了玩具的趣味性,並讓貓咪保持活躍。文章詳細介紹瞭如何使用 Python 的 randomrandint 函式生成隨機數,以及如何使用 Raspberry Pi 的 GPIO 函式庫控制伺服馬達。同時也說明瞭硬體組裝步驟,包含 PIR 動作感測器、伺服馬達、雷射筆和電池的連線方式。程式碼中使用 PWM 控制伺服馬達的轉動角度,並利用隨機數控制雷射點的移動軌跡和停留時間,讓貓咪無法預測雷射點的移動方向,保持玩耍的興趣。

玩具開發:追逐貓咪的智慧玩具

在這章節中,玄貓將為大家介紹一個簡單且有趣的電子玩具,這個玩具利用 Python 語言和 Raspberry Pi 的 GPIO 錄位來控制兩個伺服馬達,讓貓咪能夠追逐一個隨機移動的紅點。這不僅能夠吸引貓咪的注意力,還能夠讓它們保持活躍。

必要材料

  • PIR 動作感測器
  • 粘合劑(如膠水或環氧樹脂)
  • 各種電線(紅、黑等)
  • 平頭螺絲
  • 電工膠帶
  • 冰棒棒
  • 9V 電池
  • 用來安裝所有元件的容器(例如 PVC 管的一段)

基本概念

這個玩具的核心在於隨機運動。如果你將玩具設計成一系列同心圓運動,然後重複這個模式,貓咪很快就會認出這個模式並感到無聊。然而,透過使用 Python 的 randomrandint 函式,你可以隨機化運動模式,讓貓咪(以及可能你的幼兒)保持興趣。

隨機數字的生成與使用

生成和使用隨機數字看似簡單,只需呼叫一個函式即可獲得隨機整數。然而,隨機化輸出的實際過程其實非常有趣,也是許多電腦科學家和數學家研究的主題。

Python 提供了多種內建函式來生成隨機數字。首先是 random() 函式,它傳回一個浮點數(小數),範圍在 0.0 到 1.0 之間。這對於許多應用是很有用的,但對於我們的目標來說,我們需要更大的數字,最好是整數格式。這可以透過 randint() 函式來實作。

隨機性探討

從古代開始,人類就有許多方法來生成「隨機」數字,從丟硬幣、擲骰子到洗牌。對於大多數應用來說,這些方法都是足夠隨機的。但對於真正的數學或統計任務來說,這些方法有兩個主要問題:

  1. 物理系統限制:這些方法都依賴物理系統(如硬幣在空氣中的翻轉或骰子在地上的擲動)。由於它們是物理系統,它們永遠不可能真正隨機。經過足夠多次的重複,模式會開始顯現。

  2. 時間消耗:對於大量資料來說,這些方法非常不實際。例如,生成一百萬個隨機數字需要花費大量時間。

然而,電腦可以處理巨量資料並快速生成隨機數字。平均桌上型電腦的理論處理速度約為每秒 70 億次浮點數運算(Gigaflops)。以此速度生成一百萬個隨機數字只需大約 7 毫秒。

難題解決與設計選擇

儘管如此,電腦仍然是物理系統,因此生成的隨機數字最終會顯示出模式。因此,許多電腦科學家和科學家對真正隨機數字生成器有著濃厚興趣。真正隨機數字生成器在許多科學領域中都非常有用,特別是在密碼學中。

目前的隨機數字生成器使用演算法來建立長串偽隨機數字序列。這些序列通常根據乘法和模運算的組合。根據演算法品質不同,生成的數字可能或可能不適合用於密碼學。不過,它們通常足夠用於遊戲等應用。

硬體設定與軟體控制

在這個專案中,我們將使用兩個伺服馬達來控制雷射筆的運動。為了控制伺服馬達,我們將使用 Raspberry Pi 的 GPIO 錄位和 Python 的 GPIO 函式庫。

安裝步驟

  1. 準備元件:將所有元件準備好。
  2. 安裝感測器:將 PIR 動作感測器安裝到 PVC 管上。
  3. 連線電線:將各種電線連線到感測器和伺服馬達上。
  4. 固定伺服馬達:將兩個伺服馬達固定在一起並連線到雷射筆。
  5. 安裝電池:將 9V 電池安裝到容器內並連線到伺服馬達。

Python 程式碼與內容解釋

首先需要安裝必要套件:

pip install RPi.GPIO random

以下是完整的 Python 程式碼:

import RPi.GPIO as GPIO
import time
import random

# 設定 GPIO 模式和引腳號碼
GPIO.setmode(GPIO.BCM)
servo_pin_x = 17
servo_pin_y = 27

GPIO.setup(servo_pin_x, GPIO.OUT)
GPIO.setup(servo_pin_y, GPIO.OUT)

# 建立 PWM 物件並設定頻率
pwm_x = GPIO.PWM(servo_pin_x, 50)
pwm_y = GPIO.PWM(servo_pin_y, 50)

pwm_x.start(0)
pwm_y.start(0)

def set_servo_angle(pin, angle):
    duty_cycle = angle / 18 + 2
    GPIO.output(pin, True)
    pwm = GPIO.PWM(pin, 50)
    pwm.start(duty_cycle)
    time.sleep(1)
    pwm.stop()
    GPIO.output(pin, False)

try:
    while True:
        x_angle = random.randint(0, 180)
        y_angle = random.randint(0, 180)
        set_servo_angle(servo_pin_x, x_angle)
        set_servo_angle(servo_pin_y, y_angle)
        time.sleep(random.uniform(0.5, 2))
except KeyboardInterrupt:
    pwm_x.stop()
    pwm_y.stop()
    GPIO.cleanup()

內容解密:


##### 小段落標題

- **GPIO 模式與引腳設定**:我們使用 `GPIO.setmode(GPIO.BCM)` 設定 BCM 模式並指定伺服馬達連線到哪些引腳。
- **PWM 物件建立與頻率設定**:透過 `GPIO.PWM()` 建立 PWM 物件並設定頻率為 50 Hz。
- **旋轉角度設定**:`set_servo_angle` 函式負責根據傳入的角度設定伺服馬達旋轉角度。
- **主迴圈**:在主迴圈中不斷地產生隨機角度並調整伺服馬達。
- **清理工作**:當按下 Ctrl+C 中斷程式時執行清理工作。

未來改進方向

這個專案的未來改進方向包括:

  1. 增加感測器:新增更多感測器以增強互動性。
  2. 改進運動模式:設計更複雜且有趣的運動模式。
  3. 增加聲音效果:新增聲音效果以進一步吸引貓咪的注意力。

透過這些改進,我們可以讓這個簡單的玩具變得更加有趣和吸引人。

玩具貓機器人的隨機行為控制

在設計一個玩具貓機器人時,隨機行為的生成是關鍵。這些隨機行為能讓貓咪保持興趣,並且增加遊戲的趣味性。要實作這些隨機行為,我們需要使用隨機數生成器。這篇文章將探討如何在 Python 中生成隨機數,並且如何使用這些隨機數來控制玩具貓機器人的行為。

隨機數生成器

在開始一個需要使用隨機數的程式時,我們必須對隨機數生成器進行「種子化」,也就是用一個特定的數字來初始化它。這個特定的數字可以是今天的日期或系統時間。種子化的目的是讓隨機數生成器產生不同的隨機數序列。雖然最終可能會出現一些模式,但對於大多數應用來說,這些模式是可以接受的。

Python 提供了 random 模組來生成隨機數。randint(a, b) 函式可以傳回一個介於 ab 之間(包含 ab)的隨機整數。以下是一個簡單的範例:

import random
x = random.randint(1, 10)
print(x)

這段程式碼會輸出 1、2、3、4、5、6、7、8、9 或 10 中的一個隨機整數。我們可以利用這個函式來生成玩具貓機器人的動作位置。

控制伺服馬達

接下來,我們需要了解如何控制伺服馬達。伺服馬達是許多應用中的重要元件,從遙控車到高階機器人都有使用到它們。伺服馬達本質上是一個直流馬達,但透過軟體可以精確控制其旋轉角度。

在樹莓派上,我們可以使用 RPi.GPIO 函式庫來控制伺服馬達。這個函式庫允許我們存取樹莓派的通用輸入輸出(GPIO)引腳。

安裝所需函式庫

首先,確保你的樹莓派系統是最新的:

sudo apt-get update

然後安裝所需的函式庫:

sudo apt-get install python-dev
sudo apt-get install python-rpi.gpio

使用 GPIO 函式庫

在程式中引入 GPIO 函式庫並進行初始化:

import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BOARD)

這樣設定後,我們就可以開始設定引腳了。例如:

GPIO.setup(11, GPIO.OUT)
GPIO.setup(13, GPIO.IN)

設定引腳後,我們可以透過以下方式來控制引腳的狀態:

GPIO.output(11, 1)  # 開啟引腳 11
GPIO.output(11, 0)  # 關閉引腳 11

控制伺服馬達

伺服馬達使用脈衝寬度調製(PWM)訊號來控制其位置。PWM 是一種調製技術,透過改變脈衝的寬度來調節輸出電壓或電流。每秒傳送 50 個脈衝是一種常見的設定。

以下是如何控制伺服馬達的詳細步驟:

import RPi.GPIO as GPIO
import time

# 初始化 GPIO
GPIO.setmode(GPIO.BOARD)
GPIO.setup(11, GPIO.OUT)

# 建立 PWM 機制
pwm = GPIO.PWM(11, 50)  # 頻率設定為 50 Hz

# 啟動 PWM
pwm.start(0)

# 控制伺服馬達旋轉到不同位置
def set_servo_angle(angle):
    duty = angle / 18 + 2.5
    pwm.ChangeDutyCycle(duty)
    time.sleep(0.5)

try:
    while True:
        set_servo_angle(0)   # 左轉至極限位置
        time.sleep(1)
        set_servo_angle(90)  # 中間位置
        time.sleep(1)
        set_servo_angle(180) # 右轉至極限位置
        time.sleep(1)
except KeyboardInterrupt:
    pwm.stop()
    GPIO.cleanup()

內容解密:

  • 初始化 GPIO:首先我們需要引入 RPi.GPIO 模組並設定 GPIO 模式為 BOARD 模式。這樣我們可以根據實際引腳編號來操作。
  • 設定 PWM:我們建立了一個 PWM 機制,頻率設定為 50 Hz。這是因為大多數伺服馬達都支援這個頻率。
  • 控制伺服馬達set_servo_angle 函式透過改變脈衝寬度來控制伺服馬達的角度。公式 duty = angle / 18 + 2.5 是根據伺服馬達的規格計算出來的。
  • 旋轉伺服馬達:在主迴圈中,我們不斷地改變伺服馬達的角度,以實作隨機動作。

動作生成與控制

接下來,我們需要將隨機數生成與伺服馬達控制結合起來。以下是完整的範例程式碼:

import RPi.GPIO as GPIO
import random
import time

# 初始化 GPIO
GPIO.setmode(GPIO.BOARD)
GPIO.setup(11, GPIO.OUT)

# 建立 PWM 機制
pwm = GPIO.PWM(11, 50)  # 頻率設定為 50 Hz

# 啟動 PWM
pwm.start(0)

def set_servo_angle(angle):
    duty = angle / 18 + 2.5
    pwm.ChangeDutyCycle(duty)
    time.sleep(0.5)

try:
    while True:
        angle = random.randint(0, 180)  # 生成介於 0 和 180 度之間的隨機角度
        set_servo_angle(angle)
        time.sleep(random.uniform(0.5, 2))  # 隨機等待時間介於 0.5 和 2 號秒之間
except KeyboardInterrupt:
    pwm.stop()
    GPIO.cleanup()

內容解密:

  • 初始化與設定:與之前相同,我們初始化了 GPIO 和 PWM。
  • 生成隨機角度:每次迴圈中,我們使用 random.randint 函式生成一個介於 0 和 180 度之間的隨機角度。
  • 等待時間:每次更改角度後,我們會等待一段隨機時間,以增加動作的不規則性和趣味性。

玩具貓用的貓玩具

在控制伺服馬達時,如果要讓其回到中立(零)位置,需要每20毫秒傳送一次1.5毫秒的高脈衝。這可以理解為7.5%的佔空比。同樣地,如果要讓伺服馬達逆時針轉動,則傳送0.5毫秒的高脈衝,佔空比為2.5%,而傳送2.5毫秒的高脈衝則佔空比為12.5%。簡單來說,伺服馬達會在一段時間內保持高電平狀態,這段時間長度決定了其運動方向。

這些指令適用於標準(非連續)伺服馬達。標準伺服馬達使用脈衝長度來確定最終位置(以中心為基準的角度),而連續伺服馬達則使用脈衝長度來決定轉動速度。標準伺服馬達會移動到目標位置後停止,直到接收到新的指令;而連續伺服馬達則幾乎始終在運動,其運動速度由脈衝長度決定。儘管可以使用任何型別的伺服馬達來製作貓玩具,但使用標準伺服馬達更合理,這樣既能實作精確定位,也能完全停止運動,讓貓咪有機會暫時「捉到」那個小紅點。

然而,使用Raspberry Pi和Python來傳送毫秒級脈衝到GPIO引腳是有挑戰的。就像Pi上執行的其他程式一樣,Python也會被系統級程式中斷,這使得Python程式精確控制脈衝的時間變得不太實際。幸運的是,GPIO函式庫提供瞭解決方案:可以使用該函式庫將GPIO引腳設定為PWM引腳,從而生成所需的脈衝長度。

儘管從理論上來說可以編寫如下的指令碼:

while True:
    GPIO.output(11, 1)
    time.sleep(0.0015)
    GPIO.output(11, 0)
    time.sleep(0.0025)

但結果很可能完全不可預測,甚至可能無法正常工作。相反,我們可以將伺服馬達的訊號引腳(例如第11號引腳)設定為PWM輸出引腳:

p = GPIO.PWM(11, 50)

其中的50設定脈衝頻率為50赫茲(即每20毫秒一個脈衝),這是伺服馬達所需的頻率。接著可以設定引腳的佔空比為7.5%:

p.start(7.5)

此圖示

  graph TD
    A[起始] --> B[設定PWM頻率]
    B --> C[設定佔空比]
    C --> D[傳送脈衝]
    D --> E[結束]

內容解密:

  • 起始:開始設定GPIO引腳。
  • 設定PWM頻率:使用GPIO.PWM函式設定引腳為PWM模式,並指定頻率。
  • 設定佔空比:使用p.start函式設定引腳的初始佔空比。
  • 傳送脈衝:根據佔空比生成並傳送脈衝到伺服馬達。
  • 結束:完成一個完整的PWM週期。

如果將p.start(7.5)放在while迴圈中,則會讓伺服馬達移動到中心位置並保持在那裡。透過修改佔空比(使用p.ChangeDutyCycle()方法),我們可以讓伺服馬達在不同方向上運動。例如,要讓伺服馬達來回運動,可以嘗試以下指令碼:

import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BOARD)
GPIO.setup(11, GPIO.OUT)
p = GPIO.PWM(11, 50)
p.start(7.5)

while True:
    p.ChangeDutyCycle(7.5)
    time.sleep(1)
    p.ChangeDutyCycle(12.5)
    time.sleep(1)
    p.ChangeDutyCycle(2.5)
    time.sleep(1)

執行這段指令碼應該會讓伺服馬達來回掃描,每次改變方向時暫停一秒。

製造伺服機構

我們需要製作一個能夠在兩個方向上運動的伺服機構來驅動雷射筆。儘管通常情況下單個伺服馬達無法實作二維運動,但我們可以透過兩個普通伺服馬達互相連線來構建一個攜帶和俯仰機構。

注意:這個步驟會永久地將兩個伺服馬達粘合在一起,無法分開,所以請確保你還有其他備用的伺服馬達用於其他專案。不過,這種攜帶和俯仰機構對於需要在二維平面內移動物體的專案非常有用。

首先需要將其中一個伺服馬達(稱為X軸伺服)的機身固定到另一個(稱為Y軸伺服)的連桿上。為了確保牢固連線,可能需要拆下X軸伺服連桿上的螺絲並稍微打磨一下塑膠部分。目的是使X軸伺服頂部盡可能平坦以緊密地與Y軸伺服機身比對。

當X軸伺服頂部平坦到最佳狀態時,使用強力環氧樹脂或黏合劑將Y軸伺服機身黏合到X軸伺服連桿上。例如可以使用Gorilla Glue進行粘合。

製造雷射機構

接下來我們要修改雷射筆以便能夠由Raspberry Pi控制其開關狀態。首先需要一些電工膠帶和一顆約兩英寸長、頭部略小於雷射筆內徑的一顆平頭螺絲。將螺絲用膠帶包裹住以便其能夠緊密地裝入雷射筆內部。

建立雷射筆結構

首先拆下雷射筆底座並取出電池。然後將螺絲頭部朝前推入雷射筆體內部以壓住通常由電池壓住的內部彈簧。可能需要調整膠帶包裹層以確保螺絲既能壓住彈簧又不會隨意移動。

我們還需要用膠帶將雷射筆的電源按鍵按下以保持其處於開啟狀態。如前所述,我們將透過Raspberry Pi控制雷射筆的電源供應。

完成後應該與圖9-6中的樣子相似。

此圖示

  graph TD
    A[起始] --> B[拆除底座]
    B --> C[取出電池]
    C --> D[安裝螺絲]
    D --> E[壓住彈簧]
    E --> F[固定電源按鍵]
    F --> G[完成]

內容解密:

  • 起始:開始修改雷射筆。
  • 拆除底座:取下雷射筆底座。
  • 取出電池:移除原有電池。
  • 安裝螺絲:將包裹膠帶的螺絲插入雷射筆體內。
  • 壓住彈簧:確保螺絲頭部壓住內部彈簧。
  • 固定電源按鍵:用膠帶固定住電源按鍵使其保持開啟狀態。
  • 完成:修改完成後應該與圖9-6相似。

如果你想測試你的工作效果,可以使用幾根夾子將螺絲尖端連線到Pi上的第6號引腳(地線),並將雷射筆體連線到第1號引腳(3.3V)。如果一切正常執行雷射應該點亮顯示它已經成功由Pi供電執行。

將雷射連線到優駿機構

將雷射連線到優駿機構是整個專案中最簡單的一部分了。如果你像我這樣不想永久性地把雷射固定到你剛才製作好的攜帶和俯仰優駿機構上去(因為它對於其他專案也很實用),那麼我們需要找到一種臨時性地把雷射固定到優駿連桿上的方法。

我選擇使用冰棒棒來完成這件事:我們可以把冰棒棒粘合在優駿連桿上面再去粘合雷射器在冰棒棒上面使其能夠旋轉和移動最終做成圖9-7中的樣子。

製造產品:

最後需要把所有零件組合起來搭建好你自己的玩具貓用貓玩具!

完整程式碼:貓玩具隨機運動

import RPi.GPIO as GPIO
import time
import random

# 設定GPIO模式和輸出引腳
GPIO.setmode(GPIO.BOARD)
GPIO.setup(11, GPIO.OUT)  # X軸伺服
GPIO.setup(12, GPIO.OUT)  # Y軸伺服
GPIO.setup(13, GPIO.OUT)  # 雷射筆控制

# 初始化PWM物件
x_servo = GPIO.PWM(11, 50)
y_servo = GPIO.PWM(12, 50)

# 啟動PWM並設定初始佔空比
x_servo.start(7.5)
y_servo.start(7.5)

try:
    while True:
        # 隨機生成X軸和Y軸佔空比
        x_duty_cycle = random.uniform(2.5, 12.5)
        y_duty_cycle = random.uniform(2.5, 12.5)

        # 隨機生成運動持續時間
        duration = random.uniform(0.5, 2)

        # 改變X軸和Y軸佔空比
        x_servo.ChangeDutyCycle(x_duty_cycle)
        y_servo.ChangeDutyCycle(y_duty_cycle)

        # 持續執行指定時間
        time.sleep(duration)

except KeyboardInterrupt:
    pass

finally:
    # 清理GPIO設定
    x_servo.stop()
    y_servo.stop()
    GPIO.cleanup()

內容解密:

import RPi.GPIO as GPIO
import time
import random

# 設定GPIO模式和輸出引腳
# 呼叫 GPIO 的函式設定模式 (BOARD 模式表示用實體版號直接對應),同時釋放 Pin_8 作為輸出管道給 X 軸 Servo Motor (Pin_8 對應到實體板上的 Pin_8) 與 Pin_9 作為輸出管道給 Y 軸 Servo Motor (Pin_9 對應到實體板上的 Pin_9) 與 Pin_10 作為輸出管道給 Laser Pointer (Pin_10 對應到實體板上的 Pin_3)。

# 初始化PWM物件 (也就是先宣告好三台 Servo Motor 的物件名稱 x_servo 與 y_servo 與 laser)。

# 啟動 PWM 並設定初始佔空比 (也就是先讓 Servo Motor 的初始位置是中間)

try:

try: 在 Python 中 try 單純就是要把我們要執行的一大堆積命令都放進去執行看看有沒有問題~如果有問題就跳去 except 再處理。

except KeyboardInterrupt: 如果不小心按下了 Ctrl+C 中斷掉程式~那就跳過所有命令去 finally 清除資源.

while True: 當然就是無限迴圈~ 一直重複執行~~~ 不停止!!

 # 隨機生成X軸和Y軸佔空比 (也就是每次隨機決定 Servo Motor 的位置是多少) 與 持續時間 (也就是每次隨機決定 Servo Motor 的位置保持多少時間)。

 # 改變 X 軸 與 Y 軸 的佔空比 (也就是每次隨機決定 Servo Motor 的位置是多少)

 # 持續執行指定時間 (也就是每次隨機決定 Servo Motor 的位置保持多少時間)。

except KeyboardInterrupt:
pass

finally:

except KeyboardInterrupt: pass 這段就特別注意一下~ pass 是 Python 語言中的特別語法~ 他並不是代表「什麼都不做」,而是告訴 Python 「你先走開等一下」然後跳進 finally 去執行清除資源~因為當我們按下 Ctrl+C 中斷掉程式時~ finally 一段還沒有被執行完畢~所以就先跳過回傳 pass 再回頭執行 finally

 # 清理GPIO設定 (也就是最後把三台 Servo Motor 與 Laser Pointer 的資源都給清理乾淨!)

finally: 最後無論 tryexcept 有沒有成功執行完畢~一定都會被執行~通常是在清理資源 用~~~~所以上面提過因為之前按下 Ctrl+C 中斷掉程式~所以會先跳過 pass 再回頭執行 finally


總結:

以上就完整製作了一隻完全靠硬體控制自動運作且能夠隨時更新新程式碼進行升級運作中的貓玩具!