超音波を利用して、正面のみ音が聞こえるスピーカーを作りました。実際は音が反射するため壁にスピーカーを向けると壁から音が出ているように聞こえたりします。
- 原理
音は空気の振動です。振動には1.周波数(音の高さ)、2.振幅(音の大きさ)、3.位相(波の山の位置)の3つの要素があります。
-
正面のみ聞こえる理由
音波の位相が揃っている(青線)とき、互いに強め合い振幅が大きくなります。
音波の位相が180度ずれている(緑線)とき、互いに弱めあい振幅が小さくなります。
二つのスピーカーから音を出したとき、正面に向かって飛ぶ音は強め合い、横に飛ぶ音は弱めあいます。
実際はスピーカーが大量に並んでいるため正面以外は打ち消されて聞こえなくなります。また、この現象は周波数が高い程表れやすいので今回は40kHzの超音波を使っています。
-
超音波で可聴音を運ぶことが出来る理由
音は空気の密度が高い部分と低い部分が波となって移動することで伝わります。しかし、空気は圧縮される(密度が高まる)時よりも圧縮から戻る(密度が下がる)時の方が少し時間がかかります。
超音波のように周波数が高い場合、次の波が来るまでの間に密度が戻らないため、超音波の振幅を変化させると赤線のように低い周波数の波を発生させることができます。
これによって超音波で可聴音を運ぶことが可能になります。
- 主な材料
-
超音波スピーカー:UT1007-Z325R × 37個
スピーカーの面積(個数)が大きいほど指向性が強まります。 -
マイコン:PIC16F1827 × 1個
マイクやオーディオ入力からの信号を超音波周波数のPWMに変換するために使います。
実際はアナログ回路で組むほうが分解能などの点で良いのですが、手軽なのでマイコンを使用しました。 -
ゲートドライバ:IR2104STRPBF × 2個
超音波スピーカーを駆動するためのHブリッジ回路を構成するために使用します。
スピーカーに加える電圧が大きいほど音が大きく遠くまで届くので、Hブリッジで電源電圧の2倍の振幅を作ります。
- ハードウェア
-
回路構成
マイクやライン入力の信号をOPアンプで増幅し、マイコンのAD変換器に入れます。変換結果を元にPWMのデューティー比を変化させ超音波を出力します。 -
スピーカーの配置
人が移動するときは基本的に横方向に動くため、スピーカーを正円ではなく楕円状に配置し、横方向の指向性を強化しました。
-
完成品外観
回路はkicadで設計したものをCNCで加工しました。外装はアクリルや100均の下敷きを使って製作しています。
-
結果
動作の様子を動画で示します。
正面に来た時に音が大きくなっていることがわかると思います。部屋が狭いため音が反射し正面以外でも聞こえていますが、開けた場所では聞こえなくなります。
マイコンの性能の関係で入力信号の分解能が400段階しかない上に、超音波に可聴音を乗せるという無理をしているため音質は残念なことになってしまいました...。
使ったプログラムです。
// PIC16F1827 Configuration Bit Settings
// 'C' source line config statements
// CONFIG1
#pragma config FOSC = HS // Oscillator Selection (HS Oscillator, High-speed crystal/resonator connected between OSC1 and OSC2 pins)
#pragma config WDTE = OFF // Watchdog Timer Enable (WDT disabled)
#pragma config PWRTE = ON // Power-up Timer Enable (PWRT enabled)
#pragma config MCLRE = ON // MCLR Pin Function Select (MCLR/VPP pin function is MCLR)
#pragma config CP = OFF // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config CPD = OFF // Data Memory Code Protection (Data memory code protection is disabled)
#pragma config BOREN = ON // Brown-out Reset Enable (Brown-out Reset enabled)
#pragma config CLKOUTEN = OFF // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
#pragma config IESO = OFF // Internal/External Switchover (Internal/External Switchover mode is disabled)
#pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is disabled)
// CONFIG2
#pragma config WRT = OFF // Flash Memory Self-Write Protection (Write protection off)
#pragma config PLLEN = OFF // PLL Enable (4x PLL disabled)
#pragma config STVREN = OFF // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will not cause a Reset)
#pragma config BORV = HI // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), high trip point selected.)
#pragma config LVP = OFF // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming)
#include <xc.h>
#define _XTAL_FREQ 32000000
#define BATT_LOW 100 //140:15V, 102:11V
#define soundTH 15
#define MIC_IN LATAbits.LATA3
#define LED_R LATBbits.LATB2
#define LED_G LATBbits.LATB1
#define PUSH_SW PORTBbits.RB0
#define BATT_PIN 8
#define SOUND_PIN 4
#define ADRES9 ((ADRESH<<7)|(ADRESL>>1))
#define ADSTART(ch) ADCON0bits.CHS=(ch);\
__delay_us(10);\
ADCON0bits.GO_nDONE=1;\
while( ADCON0bits.GO_nDONE )
#define PWM(pwm) CCPR1L =pwm>>2;\
CCP1CONbits.DC1B=pwm
volatile unsigned short sound=0;
volatile unsigned short countStop=0,countOver=0,soundUnder=0,dcountOver;
volatile unsigned char battflg=0;
void PICinit(void){
OSCCON =0b10000000;
while(!OSCSTATbits.PLLR);
while(!OSCSTATbits.OSTS);
INTCON =0b01010000;
PIE1bits.TMR2IE =1;
//IO
OPTION_REG=0b00010010;
LATA =0b00000000;
TRISA =0b00110000;
ANSELA =0b00010000;
LATB =0b00000000;
TRISB =0b00010001;
ANSELB =0b00010000;
WPUB =0b00000001;
//PWM
T2CON =0b00000000;
CCPTMRS =0b00000000;
CCP1CON =0b10000000;
PWM1CON =0b10000000;
PR2 =200-1;
TMR2 =0b00000000;
CCPR1L =0b00000000;
//ADC
ADCON0 =0b00110000;
ADCON1 =0b10100000;
return;
}
void interrupt isr(void){
if( INTCONbits.INTF ){
PWM(0);
LED_R=0;
LED_G=0;
__delay_ms(10);
if( !PUSH_SW ){
CCP1CONbits.CCP1M=0;
MIC_IN=!MIC_IN;
while( !PUSH_SW );
__delay_ms(10);
CCP1CONbits.CCP1M=0xF;
}
INTCONbits.INTF=0;
}
if( PIR1bits.TMR2IF ){
ADSTART(SOUND_PIN);
sound = ADRES9-soundUnder;
if( sound >= 400 ){
countOver++;
dcountOver=0x2000;
CCP1CONbits.CCP1M=0xF;
LED_R=1;
LED_G=0;
if( sound >= 512 ){
PWM(0);
}else{
PWM(400);
}
ADSTART(BATT_PIN);
if( BATT_LOW >= ADRES9 ) battflg=1;
}else{
countOver = 0;
if(dcountOver) dcountOver--;
if(sound<(200-soundTH)||sound>(200+soundTH)) countStop=0;
else countStop++;
if( countStop >= 0x2000 ){
countStop=0x2000;
CCP1CONbits.CCP1M=0;
LED_R=1;
LED_G=1;
PWM(0);
ADSTART(BATT_PIN);
if( BATT_LOW >= ADRES9 ) battflg=1;
}else{
CCP1CONbits.CCP1M=0xF;
if(dcountOver){
LED_R=1;
LED_G=0;
}else{
LED_R=0;
LED_G=1;
}
PWM(sound);
}
}
PIR1bits.TMR2IF=0;
}
}
void main(void){
PICinit();
LED_R =1;
LED_G =1;
__delay_ms(10);
LED_R =0;
LED_G =0;
__delay_ms(700);
ADCON0bits.ADON =1;
ADSTART(BATT_PIN);
if( BATT_LOW >= ADRES9 ) goto ERR;
sound =256;
soundUnder =sound-200;
CCP1CONbits.CCP1M=0xF;
countStop =0;
countOver =0;
PIR1bits.TMR2IF =0;
INTCONbits.INTF =0;
CCP1CONbits.CCP1M=0xF;
LED_R =0;
LED_G =0;
MIC_IN =0;
INTCONbits.GIE =1;
T2CONbits.TMR2ON=1;
while( 1 ){
if( countOver > 0x1000 ) goto ERR;
if( battflg ) goto ERR;
}
ERR:
INTCONbits.GIE =0;
T2CONbits.TMR2ON=0;
CCP1ASbits.CCP1ASE=1;
ADCON0bits.ADON =0;
CCP1CONbits.CCP1M=0;
OPTION_REG=0b00010010;
LATA =0b00000000;
TRISA =0b00110000;
ANSELA =0b00010000;
LATB =0b00000000;
TRISB =0b00010001;
ANSELB =0b00010000;
WPUB =0b00000001;
OSCCONbits.SCS1 =1;
LED_R=1;
LED_G=0;
INTCONbits.TMR0IF=1;
while(1){
if( INTCONbits.TMR0IF ){
LED_R=!LED_R;
INTCONbits.TMR0IF=0;
}
}
return;
}
投稿者の人気記事
-
dono
さんが
2021/02/26
に
編集
をしました。
(メッセージ: 初版)
-
dono
さんが
2021/02/26
に
編集
をしました。
-
dono
さんが
2021/02/26
に
編集
をしました。
-
dono
さんが
2021/02/26
に
編集
をしました。
(メッセージ: 図のサイズを変更しました)
-
dono
さんが
2021/02/26
に
編集
をしました。
(メッセージ: プログラムを追加しました)
ログインしてコメントを投稿する