2011年4月1日 星期五

紅外線物體偵測 (3)

IRremote 函式庫也可以做紅外線物體偵測,方法是用 IRremote 讓紅外線發射器 (IR LED) 產生 38 kHz 的脈波,然後用一般 38 kHz 的紅外線接收器來偵測訊號。

註: 在「紅外線物體偵測(1)」(反射型)和「紅外線物體偵測(2)」(遮光型)這兩篇的範例程式中,紅外線發射器(IR LED) 38 kHz 的脈波是利用 tone() 函式產生的。

線路的接法還是一樣,如下圖,把紅外線發射器串接一顆 100 歐姆的電阻接到 pin 3,然後把紅外線接收器接到 pin 2:

image
程式

底下這支程式(IRRemoteObjDetect.pde)示範用 IRremote 函式庫做紅外線物體偵測:

要產生 38 kHz 的脈波,只要呼叫 irsend.enableIROut(38); 設定 PWM 的頻率,接著再呼叫 irsend.mark(0); 打開輸出讓 IR LED 開始輸出訊號,這樣就可以了。

就看你的發射器與接收器的接法為何,如果是反射型的,在偵測到物體時 pin 13 上的指示燈會閃爍,如果是遮光型的,那麼 pin 13 上的指示燈平常會一直閃爍,在偵測到物體時會熄滅。

延伸閱讀

16 意見:

芭蕉葉上聽雨聲 提到...

IRsend這個函式除了enableIROut和mark之外, 還
有哪些方法可以使用?

coopermaa 提到...

IRsend 除了 sendXXX() 這系列方法跟 enableIROut, mark 外, 還有一個 space() 方法。space() 的作用正好與 mark() 相反。

這是 IRsend 的定義 (可以在 IRRemote.h 裏找到):

class IRsend
{
public:
IRsend() {}
void sendNEC(unsigned long data, int nbits);
void sendSony(unsigned long data, int nbits);
void sendRaw(unsigned int buf[], int len, int hz);
void sendRC5(unsigned long data, int nbits);
void sendRC6(unsigned long data, int nbits);
void sendDISH(unsigned long data, int nbits);
void sendSharp(unsigned long data, int nbits);
// private:
void enableIROut(int khz);
VIRTUAL void mark(int usec);
VIRTUAL void space(int usec);
}

芭蕉葉上聽雨聲 提到...

如果我要接4組這樣的反射型物體偵測,
電路和程式該如何做?

coopermaa 提到...

你要接 4 組?
我不確定 IRRemote 能不能同時用多組紅外線接收器
不過你可以試試看,紅外線接收器不限定接哪支腳位
所以你只要照這篇的接線圖增加幾顆紅外線接收器就可以了
至於程式的話,把它當 digital input 來看用 digitalRead() 來讀訊號就行
只是要注意訊號會反,0 代表有物體,1 代表沒有物體

芭蕉葉上聽雨聲 提到...

是不是無法同時發射2組以上的脈波? 底下的程式只有第1組可以正常動作:
/*
* irObjectDetection.pde: 紅外線物體偵測
*/
const int irRec1 = 2; // 紅外線接收器
const int irLed1 = 3; // 紅外線發射器
const int ledPin1 = 12; // 紅外線指示燈
const int irRec2 = 4; // 紅外線接收器
const int irLed2 = 5; // 紅外線發射器
const int ledPin2 = 13; // 紅外線指示燈
const unsigned int frequency = 38000; // 發射頻率(單位: Hz)

void setup() {
pinMode(irRec1, INPUT); // 把 irReceiver 接腳設置為 INPUT
pinMode(irLed1, OUTPUT); // 把 irLed 接腳設置為 INPUT
pinMode(ledPin1, OUTPUT); // 把 ledPin 設置為 OUTPUT
pinMode(irRec2, INPUT); // 把 irReceiver 接腳設置為 INPUT
pinMode(irLed2, OUTPUT); // 把 irLed 接腳設置為 INPUT
pinMode(ledPin2, OUTPUT); // 把 ledPin 設置為 OUTPUT
tone(irLed1, frequency); // 產生指定頻率的脈波 (Pulses)
tone(irLed2, frequency); // 產生指定頻率的脈波 (Pulses)

}

// 讓指示燈閃爍幾下
void blinkLED(int ledPin) {
for (int i=1; i<= 3; i++) {
digitalWrite(ledPin, HIGH); // 打開指示燈
delay(100);
digitalWrite(ledPin, LOW); // 關掉指示燈
delay(100);
}
}

void loop() {
int ir1 = digitalRead(irRec1); // 讀取 irReceiver 的狀態
if (ir1 == 0) blinkLED(ledPin1); // 讓指示燈閃爍幾下
int ir2 = digitalRead(irRec2); // 讀取 irReceiver 的狀態
if (ir2 == 0) blinkLED(ledPin2); // 讓指示燈閃爍幾下
}

coopermaa 提到...

arduino.cc 討論區有人在討論能不能用 tone() 多個聲音:
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1266588204

scswift 分享了他的經驗 "I tried using tone() today, with two piezos hooked up to different pins, and I found that when you call tone() to play a tone on one pin, you must call noTone() for that pin before calling tone() on another pin... even if the sound on the first pin has stopped playing.
"

所以我想可能沒辦法用 tone() 同時播放聲音或送脈波。只能一個接一個送,而且送完一個脈波後,還要用 noTone() 結束脈波的傳送。

芭蕉葉上聽雨聲 提到...

呵~~視頻是做一隻會唱歌的晨鳥.
我的第一個作品是只要敲鍵盤任意鍵Arduino就會發出一個音符,所以當使用者在打鍵盤時樂曲就會流洩出來,我想做的東西就是一個"陪伴電腦使用者"的裝置.
剛開始還蠻新鮮的, 但那種合成的聲音是很死硬的, 聽久就膩了, 所以我後來就改成透過馬達驅動實體音樂盒, 這樣的聲音效果好多了.

芭蕉葉上聽雨聲 提到...

既然Arduino無法同時使用2個tone()發出38KHz脈波或發出複音, 那麼我想應該有下列兩種方法解決這個問題:
1.模仿cpu分時多工的觀念, 透過適時地切換tone()與noTone()來達成.
2.用硬體方式解決, 利用IC555發出38KHz脈波或音頻訊號.

芭蕉葉上聽雨聲 提到...

底下是2組紅外線偵測物體的程式碼:
/*
* irObjectDetection.pde: 紅外線物體偵測
*/
const int irRec1 = 2; // 紅外線接收器
const int irLed1 = 3; // 紅外線發射器
const int ledPin1 = 12; // 紅外線指示燈
const int irRec2 = 4; // 紅外線接收器
const int irLed2 = 5; // 紅外線發射器
const int ledPin2 = 13; // 紅外線指示燈
const unsigned int frequency = 38000; // 發射頻率(單位: Hz)

void setup() {
pinMode(irRec1, INPUT); // 把 irReceiver 接腳設置為 INPUT
pinMode(irLed1, OUTPUT); // 把 irLed 接腳設置為 INPUT
pinMode(ledPin1, OUTPUT); // 把 ledPin 設置為 OUTPUT
pinMode(irRec2, INPUT); // 把 irReceiver 接腳設置為 INPUT
pinMode(irLed2, OUTPUT); // 把 irLed 接腳設置為 INPUT
pinMode(ledPin2, OUTPUT); // 把 ledPin 設置為 OUTPUT
}

// 讓指示燈閃爍
void blinkLED(int ledPin) {
digitalWrite(ledPin, HIGH); // 打開指示燈
delay(100);
digitalWrite(ledPin, LOW); // 關掉指示燈
delay(100);
}

void loop() {
tone(irLed1, frequency); // 產生指定頻率的脈波 (Pulses)
delay(100);
int ir1 = digitalRead(irRec1); // 讀取 irReceiver 的狀態
if (ir1 == 0) blinkLED(ledPin1); // 讓指示燈閃爍幾下
noTone(irLed1);
//
tone(irLed2, frequency); // 產生指定頻率的脈波 (Pulses)
delay(100);
int ir2 = digitalRead(irRec2); // 讀取 irReceiver 的狀態
if (ir2 == 0) blinkLED(ledPin2); // 讓指示燈閃爍幾下
noTone(irLed2);
}
這種方式有缺點, 所以看來似乎應該用硬體方式解決會比較好.

芭蕉葉上聽雨聲 提到...

Arduino除了tone()這個函數可以發出38KHz脈波之外, 還有其它函數可以做到嗎?

coopermaa 提到...

Arduino core library 裏只有 tone() 這個函數可以產生各種頻率的脈波。如果要發複音,可能利用 timer 的 pwm 功能,可能要研究一下 tone() 的程式碼,然後看看有沒有多餘的 timer 可供使用。

coopermaa 提到...

簡易播複音的方法:
Simple Polyphonic Synth - Just WIth Arduino
http://little-scale.blogspot.com/2008/02/simple-polyphonic-synth-with-just.html

LCW 提到...

最近看到兩位的討論使我受益良多,我試實作出發射兩組38kHz使用切換tone()與noTone(),做出的是2個遮光型的紅外線感測器,程式部分修改 芭蕉葉上聽雨聲前輩所說之有缺點的方式。
/*
* irObjectDetection.pde: 紅外線物體偵測
*/
const int irRec1 = 2; // 紅外線接收器
const int irLed1 = 3; // 紅外線發射器
const int ledPin1 = 12; // 紅外線指示燈
const int irRec2 = 4; // 紅外線接收器
const int irLed2 = 5; // 紅外線發射器
const int ledPin2 = 13; // 紅外線指示燈
const unsigned int frequency = 38000; // 發射頻率(單位: Hz)

void setup() {
pinMode(irRec1, INPUT); // 把 irReceiver 接腳設置為 INPUT
pinMode(irLed1, OUTPUT); // 把 irLed 接腳設置為 INPUT
pinMode(ledPin1, OUTPUT); // 把 ledPin 設置為 OUTPUT
pinMode(irRec2, INPUT); // 把 irReceiver 接腳設置為 INPUT
pinMode(irLed2, OUTPUT); // 把 irLed 接腳設置為 INPUT
pinMode(ledPin2, OUTPUT); // 把 ledPin 設置為 OUTPUT
}

// 讓指示燈閃爍
void blinkLED(int ledPin) {
digitalWrite(ledPin, HIGH); // 打開指示燈
delay(100);
digitalWrite(ledPin, LOW); // 關掉指示燈
delay(100);
}

void loop() {
tone(irLed1, frequency); // 產生指定頻率的脈波 (Pulses)
delay(100);
int ir1 = digitalRead(irRec1); // 讀取 irReceiver 的狀態
if (ir1 == 1) blinkLED(ledPin1); // 讓指示燈閃爍幾下
noTone(irLed1);
//
tone(irLed2, frequency); // 產生指定頻率的脈波 (Pulses)
delay(100);
int ir2 = digitalRead(irRec2); // 讀取 irReceiver 的狀態
if (ir2 == 1) blinkLED(ledPin2); // 讓指示燈閃爍幾下
noTone(irLed2);
}

修改完似乎沒有什麼問題出現@@
依照這個程式來走的話是,irRec2被遮住的話ledPin2閃爍,irRec1被遮住的畫ledPin1閃爍,兩個一起遮住,兩個一起閃爍。
我想如果想要使用兩個tone()的話,使用tone()與noTone()這個方法可行。

芭蕉葉上聽雨聲 提到...

LCW恭喜你實做成功.

請問你的紅外線發射與接收, 有效距離最遠是幾公尺?

LCW 提到...

芭蕉葉上聽雨聲你好

我只是在麵包板上把電路實作出來...

所以發射端與接收端的距離,僅僅只有14cm而已@@

有效最遠距離的話,因為手邊沒有兩塊麵包板所以沒辦法實測...

cooper maa 提到...

LCW 恭禧你實作成功
歡迎兩位常來啊!^o^