kota1021
@kota1021 (松本 幸太郎)

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

ラズパイで圧電スピーカーが思うように動作しない

圧電スピーカーが上手く鳴らない

以下の実装で、圧電スピーカーの音が思い通りに鳴りません。
(5回に一回程度はなるので、回路ではなくコードの問題だと思います。)

Raspberry Pi 超音波測距センサー LEDライト 圧電スピーカーを組み合わせ、センサーから入力された距離によって異なる数のLEDを光らせます。そして10cm以下になると圧電スピーカーを鳴らしたいです。(またログから距離が測れていることは確認済み。)
IMG_2676.jpg

自分で思いついた原因

  • while文を一ループごとにTime.sleep(1)していることと以下が干渉?
alertor()
time.sleep(0.5)
stopAlertor()
  • そもそもtime.sleep()している間はPWM信号が出力されていない?!

全コード

import RPi.GPIO as GPIO
import time

# Setting variables for ultrasonic range sensor
trigPin = 16
echoPin = 18
MAX_DISTANCE = 200          
timeOut = MAX_DISTANCE*60   

# Setting variables for LED
ledPins = [7, 11, 13, 15, 29]
firstRedPin = 7
secondRedPin = 11
thirdRedPin = 13
fourthRedPin = 15
greenPin = 29

# Setting variables for buzzer
buzzerPin = 38

def pulseIn(pin,level,timeOut): 
    t0 = time.time()
    while(GPIO.input(pin) != level):
        if((time.time() - t0) > timeOut*0.000001):
            return 0;
    t0 = time.time()
    while(GPIO.input(pin) == level):
        if((time.time() - t0) > timeOut*0.000001):
            return 0;
    pulseTime = (time.time() - t0)*1000000
    return pulseTime
    
def getSonar():     
    GPIO.output(trigPin,GPIO.HIGH)       
    time.sleep(0.00001)     
    GPIO.output(trigPin,GPIO.LOW)
    pingTime = pulseIn(echoPin,GPIO.HIGH,timeOut)   
    distance = pingTime * 340.0 / 2.0 / 10000.0     
    return distance
    
def setup():
    print ('Program is starting...')
    GPIO.setmode(GPIO.BOARD)
    # Sets for Ultrasonic masure 
    GPIO.setup(trigPin, GPIO.OUT)   
    GPIO.setup(echoPin, GPIO.IN)
    # Sets for LED lights
    for pin in ledPins:
        GPIO.setup(pin, GPIO.OUT)   # Set all ledPins' mode is output
        GPIO.output(pin, GPIO.HIGH) # Set all ledPins to high(+3.3V) to off led
    # Sets for buzzer
    GPIO.setup(buzzerPin, GPIO.OUT)
    global p
    p = GPIO.PWM(buzzerPin, 5)

def loop():
    while(True):
        distance = getSonar()
        print ("The distance is : %.2f cm"%(distance))
        # Below LEDs
        if distance > MAX_DISTANCE or 0:
            for pin in ledPins:
                GPIO.output(pin, GPIO.HIGH)
        elif distance > 50:
            GPIO.output(firstRedPin,GPIO.LOW)
            GPIO.output(secondRedPin,GPIO.HIGH)
            GPIO.output(thirdRedPin,GPIO.HIGH)
            GPIO.output(fourthRedPin,GPIO.HIGH)
            GPIO.output(greenPin,GPIO.HIGH)
        elif distance > 40:
            GPIO.output(firstRedPin,GPIO.LOW)
            GPIO.output(secondRedPin,GPIO.LOW)
            GPIO.output(thirdRedPin,GPIO.HIGH)
            GPIO.output(fourthRedPin,GPIO.HIGH)
            GPIO.output(greenPin,GPIO.HIGH)
        elif distance > 25:
            GPIO.output(firstRedPin,GPIO.LOW)
            GPIO.output(secondRedPin,GPIO.LOW)
            GPIO.output(thirdRedPin,GPIO.LOW)
            GPIO.output(fourthRedPin,GPIO.HIGH)
            GPIO.output(greenPin,GPIO.HIGH)
        elif distance > 10:
            print("under 10 cm")
            GPIO.output(firstRedPin,GPIO.LOW)
            GPIO.output(secondRedPin,GPIO.LOW)
            GPIO.output(thirdRedPin,GPIO.LOW)
            GPIO.output(fourthRedPin,GPIO.LOW)
            GPIO.output(greenPin,GPIO.HIGH)

            # Below ringing buzzer
            alertor()
            time.sleep(0.5)
            stopAlertor()
        else:
            GPIO.output(firstRedPin,GPIO.LOW)
            GPIO.output(secondRedPin,GPIO.LOW)
            GPIO.output(thirdRedPin,GPIO.LOW)
            GPIO.output(fourthRedPin,GPIO.LOW)
            GPIO.output(greenPin,GPIO.LOW)
            
        time.sleep(1)

def alertor():
    print("alertor() called")
    p.start(50)
    p.ChangeFrequency(220)

def stopAlertor():
    print("stopAlertor() called")
    p.stop()

def destroy():
    GPIO.output(buzzerPin, GPIO.LOW)  
    for pin in ledPins:
        GPIO.output(pin, GPIO.HIGH)    # turn off all leds
    GPIO.cleanup()                     # Release resource

        
if __name__ == '__main__':     
    setup()
    try:
        loop()
    except KeyboardInterrupt:
        destroy()

追記

お二方からいただいた助言、コードを元に以下のように書き直したところ、期待通りの動作をしました!入門者に温かい助言をいただけて大変嬉しかったです。ありがとうございます!

import RPi.GPIO as GPIO
import time

# Setting variables for ultrasonic range sensor
trigPin = 16
echoPin = 18
MAX_DISTANCE = 200          
timeOut = MAX_DISTANCE*60   

# Setting variables for LED
ledPins = [7, 11, 13, 15, 29]
firstRedPin = 7
secondRedPin = 11
thirdRedPin = 13
fourthRedPin = 15
greenPin = 29

# Setting variables for buzzer
buzzerPin = 21

def pulseIn(pin,level,timeOut): 
    t0 = time.time()
    while(GPIO.input(pin) != level):
        if((time.time() - t0) > timeOut*0.000001):
            return 0;
    t0 = time.time()
    while(GPIO.input(pin) == level):
        if((time.time() - t0) > timeOut*0.000001):
            return 0;
    pulseTime = (time.time() - t0)*1000000
    return pulseTime
    
def getSonar():     
    GPIO.output(trigPin,GPIO.HIGH)       
    time.sleep(0.00001)     
    GPIO.output(trigPin,GPIO.LOW)
    pingTime = pulseIn(echoPin,GPIO.HIGH,timeOut)   
    distance = pingTime * 340.0 / 2.0 / 10000.0     
    return distance
    
def setup():
    print ('Program is starting...')
    GPIO.setmode(GPIO.BOARD)
    # Sets for Ultrasonic masure 
    GPIO.setup(trigPin, GPIO.OUT)   
    GPIO.setup(echoPin, GPIO.IN)
    # Sets for LED lights
    for pin in ledPins:
        GPIO.setup(pin, GPIO.OUT)   # Set all ledPins' mode is output
        GPIO.output(pin, GPIO.HIGH) # Set all ledPins to high(+3.3V) to off led
    # Sets for buzzer
    GPIO.setup(buzzerPin, GPIO.OUT, initial=GPIO.LOW)
    global p
    p = GPIO.PWM(buzzerPin, 220)

def loop():
    while(True):
        distance = getSonar()
        print ("The distance is : %.2f cm"%(distance))
        # Below LEDs
        
        if distance > MAX_DISTANCE or 0:
            for pin in ledPins:
                GPIO.output(pin, GPIO.HIGH)
        elif distance > 80:
            GPIO.output(firstRedPin,GPIO.LOW)
            GPIO.output(secondRedPin,GPIO.HIGH)
            GPIO.output(thirdRedPin,GPIO.HIGH)
            GPIO.output(fourthRedPin,GPIO.HIGH)
            GPIO.output(greenPin,GPIO.HIGH)
        elif distance > 60:
            GPIO.output(firstRedPin,GPIO.LOW)
            GPIO.output(secondRedPin,GPIO.LOW)
            GPIO.output(thirdRedPin,GPIO.HIGH)
            GPIO.output(fourthRedPin,GPIO.HIGH)
            GPIO.output(greenPin,GPIO.HIGH)
        elif distance > 40:
            GPIO.output(firstRedPin,GPIO.LOW)
            GPIO.output(secondRedPin,GPIO.LOW)
            GPIO.output(thirdRedPin,GPIO.LOW)
            GPIO.output(fourthRedPin,GPIO.HIGH)
            GPIO.output(greenPin,GPIO.HIGH)
        elif distance > 20:
            GPIO.output(firstRedPin,GPIO.LOW)
            GPIO.output(secondRedPin,GPIO.LOW)
            GPIO.output(thirdRedPin,GPIO.LOW)
            GPIO.output(fourthRedPin,GPIO.LOW)
            GPIO.output(greenPin,GPIO.HIGH)
        else:
            GPIO.output(firstRedPin,GPIO.LOW)
            GPIO.output(secondRedPin,GPIO.LOW)
            GPIO.output(thirdRedPin,GPIO.LOW)
            GPIO.output(fourthRedPin,GPIO.LOW)
            GPIO.output(greenPin,GPIO.LOW)
            alertor()
            time.sleep(0.05)
            stopAlertor()
            
        time.sleep(0.5)

def alertor():
    print("alertor() called")
    p.start(50)

def stopAlertor():
    print("stopAlertor() called")
    p.stop()

def destroy():
    GPIO.output(buzzerPin, GPIO.LOW)  
    for pin in ledPins:
        GPIO.output(pin, GPIO.HIGH)    # turn off all leds
    GPIO.cleanup()                     # Release resource

        
if __name__ == '__main__':     
    setup()
    try:
        loop()
    except KeyboardInterrupt:
        destroy()
0

4Answer

p = GPIO.PWM(buzzerPin, 5)
p.start(50)
p.ChangeFrequency(220)

デューティ比50%、周波数220Hzの意味でしょうか?

↓こちらの説明とメソッド名が違いますが・・・


下記のようにライブラリを使う方法もあるかと思います。

1Like

Comments

  1. そもそもtime.sleep()している間はPWM信号が出力されていない?!

    →出力されます。

    ラズパイ4で、以下のコードで(220Hzを1秒間)鳴ることを確認しました。

    import RPi.GPIO as GPIO
    import time
    
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(16, GPIO.OUT)
    pi = GPIO.PWM(16, 220) #220Hz
    pi.start(50) #duty ratio 50%
    time.sleep(1)
    pi.stop()
    

    使用した圧電ブザーこちらのものです。

  2. @kota1021

    Questioner

    ご回答いただきありがとうございます!
    お返事が遅れてしまい申し訳ありません。

    デューティ比50%、周波数220Hzの意味でしょうか?

    現在入門したばかりでデューティ比や実行電圧などは特に意識せず、いろいろなサイトからのコピペで手を動かしながら覚えようとしておりました…

    ラズパイ4で、以下のコードで(220Hzを1秒間)鳴ることを確認しました。

    いただいたコードを実行してみると、ブザーの動作確認ができました!ありがとうございます。

使用されている圧電ブザーの品番(型名)は何でしょうか?

もし、ブザーの端子に(+、−)の指定があるなら、そのブザーはアクティブブザーの可能性があります。もし、そのブザーならば、+端子のGPIOをHIGHにするだけで鳴るタイプです(もちろん−端子はGNDへ)。

先の回答で私が使用したブザーは、パッシブブザーでして、こちらはPWMで音程を制御できるタイプです。これは端子の極性を気にする必要がありません。

試しに、ブザーの+端子を5Vに、−端子をGNDに直接つなげてみれば分かりますね。
鳴る → アクティブブザー
鳴らない → パッシブブザーかアクティブブザーの極性が逆


もし、アクティブブザーなら、220Hzの切り替えをコメントにするだけでも、鳴る可能性があります。
インスタンス生成時に5Hzを指定してして、デューティ比50%なので、100ミリ秒×2回ぐらい鳴っても不思議じゃないので。

0Like

Comments

  1. @kota1021

    Questioner

    使用されている圧電ブザーの品番(型名)は何でしょうか?

    amazonで購入した入門キットのブザーのため、品番は不明です…
    しかし、極性が書かれており、またザーの+端子を5Vに、−端子をGNDに直接つなげてみたところなったのでアクティブブザーかと思われます。

  2. もうご存知だと思いますが、アクティブブザーなら、GPIOのHIGH/LOWだけで制御できます。

    GPIO.setup(buzzerPin, GPIO.OUT, initial=GPIO.LOW)
    
    GPIO.output(buzzerPin,GPIO.HIGH) #鳴る
    time.sleep(0.5)
    GPIO.output(buzzerPin,GPIO.LOW) #鳴り止む
    

    解決済みであれば、当Q&Aをクローズしていただいて結構です。

  3. @kota1021

    Questioner

    ご提案いただいたコードではさらにシンプルに記述ができますね!
    ありがとうございます。

@nak435さんのご指摘が正しいので修正しました。

距離計算するところで
pingTimeはマイクロ秒なので

× distance = pingTime * 340.0 / 2.0 / 10000.0

× distance = pingTime * 340.0 / 2.0 / 1000.0 

と思います。

ブザー鳴らす部分

            # Below ringing buzzer
            alertor()
            time.sleep(0.5)
            stopAlertor()

は、

        elif distance > 10:

の条件でなくて

        else:

で処理するのではないですか?

多分、距離計算が1/10になっていて 10cmは、1cm で測定され、判定が1.0cmから2.5cmなってます。
センサーは2cm程度から測定可能なので、鳴ったり鳴らなかったりしたのではないかと想像します。

0Like

Comments

  1. @YearCentury さん、
    横から失礼します。

    距離の計算は cm で返すので、÷ 1,000,000 × 100 で、
    distance = pingTime * 340.0 / 2.0 / 10_000.0で合っていると思うのですが。違います?

  2. @kota1021

    Questioner

    @YearCenturyさんのおっしゃる通り `else:`で処理するのが正しいです。ご指摘ありがとうございます。

質問の内容とは直接関係ありませんが、使用している超音波測距センサーがHC-SR04だとすると、echo信号の5Vを直接ラズパイのGPIOに入れるとラズパイを壊す可能性があります(3.3Vが上限のため)。
↓こちらのサイトにあるように、抵抗を2本使って分圧することをお勧めします。

0Like

Comments

  1. @kota1021

    Questioner

    ご指摘ありがとうございます!
    GPIOは上限3.3Vなのですね。電子工作が初めてで分圧などは意識しておりませんでしたが今後は気をつけます。

Your answer might help someone💌