【Raspberry Pi】ECMで音センサつくってみた
こんなこと、やります。
- エレクレットマイク(ECM)を使って音センサの制作
- 交流信号を直流信号へ変換する
- ADコンバータを使ってRaspberry Piで音を認識させる
- 音量に合わせてLEDを点灯させる
Raspberry Piで音が感知できるように実験してみました。この記事では音センサの作り方をメインにご紹介します。音センサは交流回路の勉強にもなって楽しいので、ぜひ挑戦してみてください。
こちらの動画のように、Raspberry Piと自作のECMマイクを使って、音量に合わせてLEDを点灯させるサウンドレベルインジケーターのようなことやっていきます!
音センサの概要
音センサを実現するための概要を説明します。
音センサの仕組み
今回製作した音センサの仕組みは次のようになります。
- マイクロフォン(ECM)で音声を集音
- アンプ回路で音声を増幅
- 音声の交流信号を直流信号に変換
- 直流信号をADコンバータでデジタル変換
- SPI通信でデジタル信号をRaspberry Piで読み取る
エレクレットマイク(ECM)
音を感知するためのECMマイクは、以前に自作したものを使用します。
作るのが面倒な方は、ECMとアンプがモジュール化されたものを使うと便利です。
また、Raspberry Piはお好きなものをお使いください。
マイクアンプの製作
ECMで集音した信号は、電圧が数十mV程度と小さすぎます。よって、そのままでは使えないのでアンプを通して十分大きな信号に変換してあます。
非反転増幅回路
オペアンプを使って、非反転増幅回路でマイク信号を増幅させます。この回路では、1kΩと470kΩの抵抗によって470倍の信号増幅が可能です。
▼ 非反転増幅回路の詳しくはこちら。
今回は音質に拘らないので、NJM4558やTL072などの安価なオペアンプで十分です。
増幅回路の波形を観察
ファンクションジェネレータでサイン波を鳴らし、マイクで集音して増幅回路に通してみました。その時の入力側と出力側の波形のようすです。
写真のように10mVp-p程度だったECM出力の電圧が、アンプを通したことで4Vp-p程度まで増幅されました。ただし、過増幅のために波形は歪んでいます。人間の耳で聞けば割れた音になってますが、センサの用途ですのでこれで問題ありません。あくまで音を感知できればいいので、音質は気にしなくて良いです。
発振器のすすめ
ところで、こういった低周波の実験には「発振器」があると便利です。発振器は「ファンクションジェネレータ」や「オシレーター」とも呼ばれます。
▼ ファンクションジェネレータは自作することも可能です。
交流信号を直流信号に変換する(AC-DC)
マイクアンプで増幅された信号は、GNDの0Vを中心にプラスマイナスへ振れる交流信号です。よって、このままではADコンバータに入力できません。そこで、交流信号を直流信号に変換するAC-DCコンバータを作っていきます。
作ると言っても、とても簡単です。つぎのように半波整流回路を利用します。
AC-DCコンバータの回路
このAC-DCコンバータの回路の仕組みはつぎのとおりです。
- 0Vを中心に上下する交流信号の「プラス側の信号のみ」をダイオードで取り出す
- ダイオードを通過した信号は、1uのコンデンサに蓄電され、そして100kΩの抵抗へ流れていく
- 出力が3.3V以上の電圧にならないよう、ツェナーダイオードでリミッターをかける
AC-DC変換後の波形はこんな感じのものになります。
▼ ダイオードとツェナーダイオードは、小信号用のものをお使いいただけます。
なお、ダイオードを通過した信号はOUTへも流れますが、OUT先の入力抵抗(入力インピーダンス)は非常に高いと想定しているため、1uに蓄えられた電荷のほとんどが100kΩの抵抗へ流れることになります。このことは、インピーダンスについて学ぶと理解できますので、詳しく学びたい方はこちらの書籍をおすすめします。(私のバイブルです)
AC-DCコンバータの実際
こちらの写真はマイクアンプから出力された信号と、「ダイオードのみ」を通した時の信号をオシロスコープで観察したものです。
ダイオードを通すと、プラス側の信号のみを取り出すことができます。またダイオードの向きを反対にすれば、マイナス側の信号のみを取り出せます。
ダイオードでは順方向電圧があり、ダイオードを通った信号は0.6Vほど電圧が低くなります。上の写真からもそのようすが観察できます。 つまり、0.6V以上の入力信号でないとこの回路は動作しません。ですから、アンプ回路でECMの微弱信号を大きな電圧に増幅する必要があったのですね。
もしも0Vから動作させたい場合は、理想ダイオード回路を使って実現できます。詳しくはこちらをご覧ください。
理想ダイオード回路は、本格的なVUメーターやdB測定器などで精密な測定器に使われたりします。今回の用途では精度は求めませんので、簡易的な方法でAC-DCコンバータを実現させました。 この簡易的なAC-DCコンバータ回路は、他にも土壌センサやコンプレッサーなどのエンベロープ発生に使われてます。
ADコンバータ
さて、AC-DCコンバータで直流になったアナログ電圧をRaspberry Piで読み取るためには、ADコンバータが必要です。ADコンバータはアナログ信号をデジタル信号に変換するものです。
ADコンバータは、SPIで通信する2チャネル・8ビットのMAX1118を使いました。MAX1118の使い方はこちらの記事をご覧ください。
他にもADコンバータはいろいろあります。
お好きなADコンバータをお使いいただけますが、Raspberry Piで使う場合はI2CまたはSPI接続のADコンバータが簡単です。
音量に合わせてLEDを点灯させるPythonプログラム
ここまで準備ができたところで、さいごにRaspberry Piを使って音センサを使ってみます。音量に合わせてLEDを点灯させるPythonプログラムを書いてみました。Raspberry PiのGPIOに、1kΩの抵抗を介してLEDを5個配線してます。
# -*- coding: utf-8 -*-
import spidev
import time
import RPi.GPIO as GPIO
LED0 = 4 # GPIO番号
LED1 = 14
LED2 = 15
LED3 = 17
LED4 = 18
Vref = 3.336 # Raspberry Piの3.3V電源をテスターで実測
GPIO.setmode(GPIO.BCM)
GPIO.setup(8, GPIO.OUT) # CNVST
GPIO.setup(LED0, GPIO.OUT)
GPIO.setup(LED1, GPIO.OUT)
GPIO.setup(LED2, GPIO.OUT)
GPIO.setup(LED3, GPIO.OUT)
GPIO.setup(LED4, GPIO.OUT)
spi = spidev.SpiDev()
spi.open(0, 0) # bus0,cs0
spi.no_cs = True # CSを使わない
spi.max_speed_hz = 1000000 # 1MHz
spi.bits_per_word = 8
def sendCNVST(CH):
if CH == 0:
GPIO.output(8, GPIO.LOW)
GPIO.output(8, GPIO.HIGH)
GPIO.output(8, GPIO.LOW)
elif CH == 1:
GPIO.output(8, GPIO.LOW)
GPIO.output(8, GPIO.HIGH)
GPIO.output(8, GPIO.LOW)
GPIO.output(8, GPIO.HIGH)
GPIO.output(8, GPIO.LOW)
def getVolts():
adc = spi.xfer2([0x00])
return adc[0] / 255.0 * Vref
try:
while True:
sendCNVST(0)
v0 = getVolts()
# print(v0)
if v0 > 0.75:
GPIO.output(LED0, GPIO.HIGH)
GPIO.output(LED1, GPIO.HIGH)
GPIO.output(LED2, GPIO.HIGH)
GPIO.output(LED3, GPIO.HIGH)
GPIO.output(LED4, GPIO.HIGH)
elif v0 > 0.6:
GPIO.output(LED0, GPIO.HIGH)
GPIO.output(LED1, GPIO.HIGH)
GPIO.output(LED2, GPIO.HIGH)
GPIO.output(LED3, GPIO.HIGH)
GPIO.output(LED4, GPIO.LOW)
elif v0 > 0.45:
GPIO.output(LED0, GPIO.HIGH)
GPIO.output(LED1, GPIO.HIGH)
GPIO.output(LED2, GPIO.HIGH)
GPIO.output(LED3, GPIO.LOW)
GPIO.output(LED4, GPIO.LOW)
elif v0 > 0.3:
GPIO.output(LED0, GPIO.HIGH)
GPIO.output(LED1, GPIO.HIGH)
GPIO.output(LED2, GPIO.LOW)
GPIO.output(LED3, GPIO.LOW)
GPIO.output(LED4, GPIO.LOW)
elif v0 > 0.15:
GPIO.output(LED0, GPIO.HIGH)
GPIO.output(LED1, GPIO.LOW)
GPIO.output(LED2, GPIO.LOW)
GPIO.output(LED3, GPIO.LOW)
GPIO.output(LED4, GPIO.LOW)
else:
GPIO.output(LED0, GPIO.LOW)
GPIO.output(LED1, GPIO.LOW)
GPIO.output(LED2, GPIO.LOW)
GPIO.output(LED3, GPIO.LOW)
GPIO.output(LED4, GPIO.LOW)
time.sleep(0.01)
except KeyboardInterrupt:
spi.close()
GPIO.output(LED0, GPIO.LOW)
GPIO.output(LED1, GPIO.LOW)
GPIO.output(LED2, GPIO.LOW)
GPIO.cleanup()
▼ SPI通信でデータを読み取る関数spi.xfer2は、こちらの記事で詳しく解説してます。
▼ 作った音センサのようすは、こちらの動画をご覧ください。 音に反応するセンサーを作ってみた!ラズパイ - YouTube