今回はRaspberry PiとUSBマイクを組み合わせて騒音測定器の作成方法を紹介します。
この記事では「Raspberry Pi 3B」とサンワサプライ社のUSBマイクロホン「MM-MCU01BK」を使用します。「Raspberry Pi 3」はLinuxOS相当のOSを動作可能で、USBポートも標準搭載されているため、ハードウェアの増設やドライバーのインストールといった特別な環境整備を行うことなく、USBマイクの入力音声をPythonスクリプトなどから操作することが可能です。
USBマイクの入力音声から騒音レベルを測定することで、下記のような課題解決に応用できると考えています。
- 騒音対策:騒音レベルをディスプレイ表示し、ユーザーに注意喚起
- スマートホーム:テレビやスピーカーの音量を一定以下に保つ
- 空間表現:音楽のパーカッション成分と照明、LEDライトを連動させ、耳だけでなく目でも音楽を楽しめる空間を作成
実際にテレビやスピーカーの音量を一定以下に保つために、スマートリモコン化した事例を以下の記事で紹介しています。
こんにちは、ほっさまです。 皆さんは、騒音問題について考えることはあるでしょうか。 私はマンションタイプの集合住宅に居住していて、最近はテレワークにより一日のほとんどを家の中で過ごしているため、自分自身や近隣住民が出す生活音に以[…]
動作確認済み環境
OS: Raspbian Buster with desktop(Release date: 2019-09-26)
プログラミング言語: python3(version=3.7.3)
この記事では、Raspberry Piの購入後に実施する以下の2点について、書いています。 1. OS(Raspbian)インストール 2. 初期セットアップ この記事は以下のような方向けのものになります。 Raspberr[…]
この記事では、Raspberry Pi 3 MODEL Bを対象に、ロボット製作に適した開発環境のセットアップ手順を紹介します。 このセットアップ手順を実行することで、以下の記事で紹介したようなロボット用のプログラミングが可能になりま[…]
1. 必要モジュール、パッケージのインストール
USBマイクから音声を取得するためにPythonの「PyAudio」パッケージをインストールします。
1-1. PortAudio modules
「PyAudio」は「PortAudio」というオーディオの再生、録音機能を持つライブラリのラッパーです。「PyAudio」を使用する場合は、「PortAudio」関連のモジュールを事前にインストールしておく必要があります。
「PortAudio」関連のモジュールのインストールは、以下のコマンドで行います。
sudo apt-get install libportaudio2 libportaudiocpp0 portaudio19-dev |
1-2. PyAudio
「PyAudio」は以下のpipコマンドでインストールできます。
pip3 install pyaudio |
2. USBマイクの接続、識別番号の確認
2-1. USBマイクの接続
「Raspberry Pi 3B」のUSBポートにUSBマイクを接続します。
2-2. USBマイクの接続確認
「Raspberry Pi 3B」のターミナルで、「lsusb」コマンドを実行し、USBマイクが認識されていることを確認します。
$ lsusb | |
Bus 001 Device 004: ID 0d8c:8100 C-Media Electronics, Inc. | |
Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. SMSC9512/9514 Fast Ethernet Adapter | |
Bus 001 Device 002: ID 0424:9514 Standard Microsystems Corp. SMC9514 Hub | |
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub |
2-3. USBマイクの識別番号の確認
「PyAudio」でUSBマイクの入力音声を取得するためには、USBマイクの識別番号を指定する必要があります。認識されている全ての音声入出力デバイスの識別番号は以下のPythonスクリプトを実行することで確認できます。
import pyaudio | |
p = pyaudio.PyAudio() | |
for index in range(0, p.get_device_count()): | |
device_info = p.get_device_info_by_index(index) | |
print("DEVICE_INDEX:{}, DEVICE_NAME:{}".format(device_info["index"], device_info["name"])) | |
p.terminate() |
(実行結果)
DEVICE_INDEX:0, DEVICE_NAME:bcm2835 HDMI 1: - (hw:0,0) | |
DEVICE_INDEX:1, DEVICE_NAME:bcm2835 Headphones: - (hw:1,0) | |
DEVICE_INDEX:2, DEVICE_NAME:USB PnP Sound Device: Audio (hw:2,0) | |
DEVICE_INDEX:3, DEVICE_NAME:sysdefault | |
DEVICE_INDEX:4, DEVICE_NAME:output | |
DEVICE_INDEX:5, DEVICE_NAME:dmix | |
DEVICE_INDEX:6, DEVICE_NAME:default |
「lsusb」と同様に、デバイス名からの判断が難しい場合には、USBマイクを接続していない状態の出力結果と比較することで、デバイス名の特定が可能です。
3. 騒音レベルの測定
3-1. Pythonスクリプト
import pyaudio | |
import numpy as np | |
import sys | |
import time | |
# check args | |
if (len(sys.argv) < 2) or (not sys.argv[1].isdecimal()): | |
print("Please specify input_device_index in integer") | |
sys.exit(-1) | |
p = pyaudio.PyAudio() | |
# set prams | |
INPUT_DEVICE_INDEX = int(sys.argv[1]) | |
CHUNK = 2 ** 10 # 1024 | |
FORMAT = pyaudio.paInt16 | |
CHANNELS = int(p.get_device_info_by_index(INPUT_DEVICE_INDEX)["maxInputChannels"]) | |
SAMPLING_RATE = int(p.get_device_info_by_index(INPUT_DEVICE_INDEX)["defaultSampleRate"]) | |
RECORD_SECONDS = 1 | |
# amp to db | |
def to_db(x, base=1): | |
y=20*np.log10(x/base) | |
return y | |
# main loop | |
def main(): | |
while True: | |
start = time.time() | |
stream = p.open(format = FORMAT, | |
channels = CHANNELS, | |
rate = SAMPLING_RATE, | |
input = True, | |
frames_per_buffer = CHUNK, | |
input_device_index = INPUT_DEVICE_INDEX | |
) | |
# get specified range of data. size of data equals (CHUNK * (SAMPLING_RATE / CHUNK) * RECORD_SECONDS) | |
data = np.empty(0) | |
for i in range(0, int(SAMPLING_RATE / CHUNK * RECORD_SECONDS)): | |
elm = stream.read(CHUNK, exception_on_overflow = False) | |
elm = np.frombuffer(elm, dtype="int16")/float((np.power(2,16)/2)-1) | |
data = np.hstack([data, elm]) | |
# calc RMS | |
rms = np.sqrt(np.mean([elm * elm for elm in data])) | |
# RMS to db | |
db = to_db(rms, 20e-6) | |
stream.close() | |
elapsed_time = time.time() - start | |
print("elapsed_time:{:.3f}[sec], DB:{:.3f}[db]".format(elapsed_time, db)) | |
try: | |
main() | |
except KeyboardInterrupt: | |
pass | |
finally: | |
p.terminate() |
(パラメータ解説)
・14行目:コマンドライン引数に、前章で確認した識別番号を指定します。
・15行目:USBマイクから一度に読み取る入力音声のフレーム数は「CHUNK」で指定します。パラメータは後で調整するため、この段階では一般的な「1024」(=2の10乗)を指定します。
・16行目:「FORMAT」に「pyaudio.paInt16」を指定することで、入力音声の量子化ビット数を「16」とします。これにより、入力音声データを[-32,768, 32,767]の値として扱えるようにします。
・17-18行目:入力音声のチャンネル数「CHANNELS」、サンプリングレート「SAMPLING_RATE」は、「get_device_info_by_index」メソッドで取得したデフォルト値を使用します。
・19行目:騒音レベルの計算に使用する音声データのサイズは「RECORD_SECONDS」で指定します。計算に使用するフレーム数は、「SAMPLING_RATE*RECORD_SECONDS」になります。
・31-42行目:音声入力部分(open、stream.readメソッド)は「PyAudio」の「Documentation & Examples」を参考に作成していますので、説明は割愛します。
・43行目(分子):入力音声データはバイナリ形式で取得されます。このままでは騒音レベルの計算に使用できないため、「np.frombuffer」メソッドで整数型のNumpy配列に変換します。変換後の配列サイズは「CHUNK」で指定した数と一致します。
・43行目(分母):入力音声データを「int16」の最大値「32,767」で除算することで最大値を「1」とします。これにより、入力音声データが最大値「1」の時の基準音圧レベル「1Pa」に対する騒音レベルが「0dB」となり、USBマイクの入力感度「0dB=1V/Pa」と対応させます。
・46行目:「RECORD_SECONDS」で指定した秒数分のデータから、実効値「RMS」を計算します。「RMS」は二乗平均の平方根を扱います。
・48行目:「RMS」から騒音レベルを計算します。基準音圧レベルは、人間の最小可聴音圧の「20μPa」とします。
elapsed_time:1.812[sec], DB:61.476[db] | |
elapsed_time:1.816[sec], DB:63.758[db] | |
elapsed_time:1.812[sec], DB:57.581[db] | |
elapsed_time:1.812[sec], DB:56.743[db] | |
elapsed_time:1.820[sec], DB:58.754[db] | |
elapsed_time:1.816[sec], DB:53.417[db] |
(参考:https://dsp.stackexchange.com/questions/13728)
・騒音レベルはスマートフォンアプリの「騒音測定器」と比較して、最小値「+2dB」、平均値「-3dB」の誤差があります。これは、1ループ当たりの経過時間が「RECORD_SECONDS」で指定した時間より遅れているために、データの取りこぼしがあったためと考えらえれます。
3-2. パラメータ調整
「CHUNK」の値を小さくすることで、1ループ当たりの処理時間を「RECORD_SECONDS」で指定した値に近づけます。
下表の結果から、「CHUNK」が「512」以下では処理時間にほとんど誤差がないため、1ループ当たりの処理時間は「入力音声データの長さ+騒音レベルの計算にかかる時間」で収束しているものと考えらえれます。
騒音レベルの比較から、本記事のデバイスを使用する限りでは、「CHUNK」に「128」を設定するのが妥当と考えられます。
(パラメータごとの処理時間、騒音レベル)
CHUNK | 処理時間[sec]
(平均) |
騒音レベル/Pythonスクリプト | 騒音レベル/スマートフォンアプリ | ||
最小 | 平均 | 最小 | 平均 | ||
128 | 1.11 | 50.6 | 53.1 | 51 | 54 |
256 | 1.12 | 46.9 | 49.0 | 45 | 48 |
512 | 1.13 | 52.6 | 54.0 | 48 | 53 |
1024 | 1.81 | 53.4 | 58.6 | 52 | 61 |
所感
USBマイクの入力音声から騒音レベルを測定した結果は、スマートフォンアプリで測定した騒音レベルと比較して、平均値が1dB未満に収まっているため、実用に足るものと考えられます。
記事内で紹介したデバイス
Physical Computing Lab
Raspberry Pi3 Model B ボード&ケースセット 3ple Decker対応 (Clear)-Physical Computing Lab
TSI-P031-Clear