IoTLabs

Nghiên cứu, Sáng tạo và Thử nghiệm

Series 37 Module Cảm Biến – Nguyên Lý Hoạt Động IR Obstacle Sensor: Phản Xạ Hồng Ngoại, LM393 & Tránh Vật Cản Robot

IR Obstacle Sensor (cảm biến tránh vật cản hồng ngoại) hoạt động bằng cách phát sóng IR và phát hiện IR phản xạ về. Đơn giản nhưng có nhiều chi tiết quan trọng: tại sao bị nhiễu ánh sáng mặt trời, màu sắc và chất liệu vật thể ảnh hưởng thế nào, và tại sao không nên dùng ngoài trời. Bài này phân tích nguyên lý đầy đủ và code robot tránh vật cản thực tế.

Nguyên Lý Hoạt Động

1. Cặp Phát-Thu Hồng Ngoại

Module gồm 2 thành phần chính đặt cạnh nhau:

             Vật thể
              ╱╲
  IR phát ──→╱  ╲──→ IR phản xạ
            ╱    ╲
   LED IR          Photodiode
   (940nm)  ────→  IR thu
   [Phát]          [Nhận]

LED IR (Emitter):

  • Phát liên tục sóng IR, bước sóng 850–950nm (thường 940nm)
  • Mắt người không nhìn thấy nhưng camera điện thoại thấy (thử chiếu vào camera điện thoại — thấy màu tím/hồng)
  • Góc phát: 20°–40° (khá hẹp)

Photodiode IR (Receiver):

  • Thay đổi điện trở (hoặc tạo dòng ngược) khi nhận IR
  • Nhạy nhất với 940nm (khớp với emitter)
  • Trường nhìn: ~60°

Khi có vật cản: Vật thể phản xạ IR về phía receiver → cường độ IR nhận được tăng → photodiode dẫn nhiều hơn → điện áp ở điểm đo thay đổi → LM393 phát hiện → OUT thay đổi.

2. Mạch Điện — Cầu Phân Áp + LM393

VCC
 │
[R_LED] (thường ~100Ω – 330Ω)
 │
[LED IR] (emitter — phát liên tục)
 │
GND

VCC
 │
[R_pull] (pull-up, thường 10kΩ – 47kΩ)
 │
 ├──── V_sense (đến LM393 input +)
 │
[Photodiode] (bị phân cực ngược — reverse bias)
 │
GND

LM393 comparator:
V_sense (+) vs V_ref (-) → OUT
V_ref điều chỉnh bằng triết áp (reference voltage = ngưỡng)

Logic output:

Trạng tháiV_senseVsense vs VrefOUTLED báo
Không vật cảnCao (photodiode nhận ít IR)Vsense > VrefHIGHTắt
Có vật cảnThấp (photodiode nhận nhiều IR phản xạ)Vsense < VrefLOWSáng

OUT LOW = có vật cản — đây là logic âm (active LOW). Cần chú ý khi viết code: if (digitalRead(OUT_PIN) == LOW) thì có vật cản.

3. Triết Áp Điều Chỉnh Khoảng Cách

Triết áp trên module thay đổi V_ref của LM393:

  • Vặn tăng V_ref: Cần ít IR phản xạ hơn để trigger → phát hiện vật thể xa hơn, nhạy hơn
  • Vặn giảm V_ref: Cần nhiều IR phản xạ hơn → chỉ phát hiện vật gần hơn, kém nhạy hơn

Khoảng cách điều chỉnh được: thường 2cm–30cm tùy module.

4. Tại Sao Màu Và Chất Liệu Ảnh Hưởng Kết Quả

IR 940nm phản xạ rất khác nhau tùy vật liệu:

Vật liệu / MàuPhản xạ IRKết quả
Trắng, nhẵnCao (~90%)Phát hiện dễ, xa
Đen, nhámThấp (<5%)Phát hiện khó, gần
Gương, kim loạiPhản xạ specular (lệch góc)Có thể không phát hiện
KínhTrong suốt một phần với IRPhụ thuộc loại kính
Vải đenRất thấpThường không phát hiện

Thực tế robot: Tường trắng → phát hiện từ 20–30cm. Vải đen → chỉ phát hiện từ 3–5cm hoặc không phát hiện.

5. Vấn Đề Nhiễu Ánh Sáng Mặt Trời

Ánh sáng mặt trời chứa lượng lớn IR ở mọi bước sóng, bao gồm 940nm. Khi photodiode nhận nhiều IR từ ánh sáng mặt trời → V_sense luôn thấp → module báo “có vật cản” liên tục dù không có gì.

Dấu hiệu bị nhiễu nắng: Module báo vật cản ngay cả khi không có vật thể, hoặc LED OUT sáng liên tục.

Giải pháp:

  1. Chỉ dùng trong nhà hoặc bóng râm — không dùng ngoài trời có nắng
  2. Module E18-D80NK có vỏ ống chắn sáng xung quanh — ít bị nhiễu hơn FC-51 dạng phẳng
  3. Sensor dùng IR điều chế 38kHz (như Sharp GP2Y hoặc sensor dùng TSOP) — khả năng lọc nhiễu tốt hơn nhiều
  4. Tăng cường độ LED IR (giảm điện trở R_LED) để thắng nhiễu ánh sáng — nhưng tiêu hao dòng nhiều hơn

Thông Số Kỹ Thuật (Module FC-51)

Thông sốGiá trị
Điện áp hoạt động3.3V – 5V
Dòng tiêu thụ~20–40mA
Tầm phát hiện2cm – 30cm (điều chỉnh bằng triết áp)
Góc phát hiện~35°
Bước sóng LED IR940nm
Ngõ raDigital: LOW khi có vật cản, HIGH khi không
ComparatorLM393
Kích thước31mm × 14mm

Sơ Đồ Chân (Pinout)

        [LED IR] [Photodiode]
     ┌──────────────────────┐
     │  IR Obstacle Sensor  │
     │  [Triết áp]  [LED báo]│
     │  LM393 Comparator    │
     └──────────────────────┘
      VCC   GND   OUT
ChânChức năng
VCC3.3V – 5V
GNDĐất
OUTDigital: LOW = có vật cản, HIGH = không có vật cản

Module 4 chân (một số loại): Có thêm chân EN (enable) để bật/tắt emitter — hữu ích khi muốn cấp nguồn xung để tiết kiệm điện.

So Sánh Các Loại Sensor Tránh Vật Cản

SensorNguyên lýTầm xaĐầu raChú ý
FC-51 / TCRT5000IR phản xạ2–30cmDigitalDễ bị nhiễu nắng
E18-D80NKIR phản xạ (có vỏ ống)3–80cmDigitalÍt nhiễu hơn FC-51
Sharp GP2Y0A21YKIR phản xạ (PSD)10–80cmAnalogChính xác hơn, ít nhiễu
Sharp GP2Y0A02YKIR phản xạ (PSD)20–150cmAnalogTầm xa hơn
HC-SR04Siêu âm2–400cmPulseKhông bị nhiễu nắng
VL53L0XLaser ToF2–200cmI2CChính xác nhất

Khi nào dùng loại nào:

  • Robot trong nhà đơn giản: FC-51 (rẻ nhất)
  • Cần tầm xa hoặc ngoài nhà: Sharp GP2Y hoặc HC-SR04
  • Cần khoảng cách chính xác (mm): VL53L0X (ToF)

Kết Nối Phần Cứng

Kết Nối với ESP32 DevKit V1

FC-51 chạy 3.3V–5V → tương thích trực tiếp với ESP32:

ESP32 DevKit V1           IR Obstacle Sensor (FC-51)
─────────────────────     ────────────────────────────
3V3  ─────────────────→  VCC  (hoặc 5V từ VIN)
GND  ─────────────────→  GND
GPIO4 (Input) ←─────────  OUT  (3.3V hay 5V → an toàn với cả 2)

Cấp 3.3V: OUT HIGH = ~3.3V, OUT LOW = ~0V → tương thích GPIO ESP32. Cấp 5V: OUT HIGH = ~5V → cần kiểm tra module có phân áp out không. Hầu hết module FC-51 out thấp hơn VCC vì qua LM393 open collector → an toàn.

An toàn nhất: Cấp 3.3V cho module khi dùng ESP32.

Kết Nối với Arduino Uno

Arduino Uno               IR Obstacle Sensor (FC-51)
─────────────────────     ────────────────────────────
5V   ─────────────────→  VCC
GND  ─────────────────→  GND
Pin 7 (Input) ←──────────  OUT

Code Arduino IDE

Code Đơn Giản — Arduino Uno

/*
 * IR Obstacle Sensor FC-51 — Phát hiện vật cản
 * Board: Arduino Uno
 * Kết nối: VCC→5V, GND→GND, OUT→Pin7
 *
 * Điều chỉnh triết áp trên module:
 * - Vặn theo chiều kim đồng hồ: tăng khoảng cách phát hiện
 * - Vặn ngược: giảm khoảng cách
 *
 * OUT LOW  = có vật cản (logic âm — chú ý!)
 * OUT HIGH = không có vật cản
 */

const int IR_SENSOR_PIN = 7;

void setup() {
  Serial.begin(9600);
  pinMode(IR_SENSOR_PIN, INPUT);
  Serial.println("=== IR Obstacle Sensor - Arduino Uno ===");
  Serial.println("Điều chỉnh triết áp để đặt khoảng cách phát hiện.");
}

void loop() {
  // LOW = có vật cản (logic đảo!)
  bool obstacleDetected = (digitalRead(IR_SENSOR_PIN) == LOW);

  if (obstacleDetected) {
    Serial.println("VẬT CẢN PHÁT HIỆN!");
  } else {
    Serial.println("Đường thông.");
  }

  delay(200);
}

Code Robot Tránh Vật Cản — Arduino Uno (2 Sensor)

/*
 * IR Obstacle Sensor — Robot 2 bánh tránh vật cản
 * Board: Arduino Uno
 * 2 sensor IR: trái và phải
 * Kết nối sensor: VCC→5V, GND→GND, OUT_TRÁI→Pin6, OUT_PHẢI→Pin7
 * Motor driver L298N (ví dụ):
 *   ENA→Pin9(PWM), IN1→Pin2, IN2→Pin3 (Motor trái)
 *   ENB→Pin10(PWM), IN3→Pin4, IN4→Pin5 (Motor phải)
 */

// Sensor pins
const int IR_LEFT  = 6; // Sensor hồng ngoại bên trái
const int IR_RIGHT = 7; // Sensor hồng ngoại bên phải

// Motor pins (L298N)
const int ENA  = 9;   // PWM - tốc độ motor trái
const int IN1  = 2;
const int IN2  = 3;
const int ENB  = 10;  // PWM - tốc độ motor phải
const int IN3  = 4;
const int IN4  = 5;

const int SPEED = 150; // Tốc độ 0-255

// Điều khiển motor: tốc độ>0=tiến, <0=lùi, =0=dừng
void setMotors(int leftSpeed, int rightSpeed) {
  // Motor trái
  analogWrite(ENA, abs(leftSpeed));
  if (leftSpeed > 0) { digitalWrite(IN1, HIGH); digitalWrite(IN2, LOW); }
  else if (leftSpeed < 0) { digitalWrite(IN1, LOW); digitalWrite(IN2, HIGH); }
  else { digitalWrite(IN1, LOW); digitalWrite(IN2, LOW); }

  // Motor phải
  analogWrite(ENB, abs(rightSpeed));
  if (rightSpeed > 0) { digitalWrite(IN3, HIGH); digitalWrite(IN4, LOW); }
  else if (rightSpeed < 0) { digitalWrite(IN3, LOW); digitalWrite(IN4, HIGH); }
  else { digitalWrite(IN3, LOW); digitalWrite(IN4, LOW); }
}

void setup() {
  Serial.begin(9600);
  pinMode(IR_LEFT, INPUT);
  pinMode(IR_RIGHT, INPUT);
  pinMode(ENA, OUTPUT); pinMode(IN1, OUTPUT); pinMode(IN2, OUTPUT);
  pinMode(ENB, OUTPUT); pinMode(IN3, OUTPUT); pinMode(IN4, OUTPUT);
  Serial.println("=== Robot Tránh Vật Cản - IR ===");
}

void loop() {
  // LOW = có vật cản (logic âm của FC-51)
  bool leftBlocked  = (digitalRead(IR_LEFT)  == LOW);
  bool rightBlocked = (digitalRead(IR_RIGHT) == LOW);

  if (!leftBlocked && !rightBlocked) {
    // Không có vật cản → tiến thẳng
    setMotors(SPEED, SPEED);
    Serial.println("Tiến thẳng");
  } else if (leftBlocked && !rightBlocked) {
    // Trái có vật cản → rẽ phải (motor trái dừng, phải chạy)
    setMotors(0, SPEED);
    Serial.println("Vật cản TRÁI → Rẽ phải");
  } else if (!leftBlocked && rightBlocked) {
    // Phải có vật cản → rẽ trái
    setMotors(SPEED, 0);
    Serial.println("Vật cản PHẢI → Rẽ trái");
  } else {
    // Cả hai có vật cản → lùi
    setMotors(-SPEED, -SPEED);
    delay(300);
    // Sau khi lùi → quay phải
    setMotors(SPEED, -SPEED);
    delay(400);
    Serial.println("Cả hai vật cản → Lùi + Quay");
  }

  delay(100);
}

Code ESP32 — Interrupt Phát Hiện Vật Cản Tức Thì

/*
 * IR Obstacle Sensor FC-51 — ESP32, phát hiện và cảnh báo
 * Board: ESP32 DevKit V1
 * Kết nối: VCC→3V3, GND→GND, OUT→GPIO4
 */

const int IR_PIN  = 4;
const int LED_PIN = 2; // LED onboard ESP32

volatile bool obstacleFlag = false;
volatile bool clearFlag    = false;
unsigned long lastObstacleTime = 0;

void IRAM_ATTR onObstacleChange() {
  if (digitalRead(IR_PIN) == LOW) {
    // LOW = vật cản
    obstacleFlag = true;
    lastObstacleTime = millis();
  } else {
    // HIGH = đường thông
    clearFlag = true;
  }
}

void setup() {
  Serial.begin(115200);
  pinMode(IR_PIN, INPUT);
  pinMode(LED_PIN, OUTPUT);

  // Interrupt CHANGE — bắt cả lên và xuống
  attachInterrupt(digitalPinToInterrupt(IR_PIN), onObstacleChange, CHANGE);

  Serial.println("=== IR Obstacle Sensor - ESP32 Interrupt Mode ===");
  Serial.println("Điều chỉnh triết áp module để đặt khoảng cách cần phát hiện.");
}

void loop() {
  if (obstacleFlag) {
    obstacleFlag = false;
    digitalWrite(LED_PIN, HIGH); // Bật LED cảnh báo
    Serial.printf("[%lums] VẬT CẢN!\n", lastObstacleTime);
    // Thêm: bật còi, dừng động cơ, gửi MQTT...
  }

  if (clearFlag) {
    clearFlag = false;
    digitalWrite(LED_PIN, LOW);
    Serial.printf("[%lums] Đường thông.\n", millis());
  }

  delay(10);
}

Kết Quả Mong Đợi

=== IR Obstacle Sensor - ESP32 Interrupt Mode ===
Điều chỉnh triết áp module để đặt khoảng cách cần phát hiện.
[1234ms] VẬT CẢN!
[1890ms] Đường thông.
[2450ms] VẬT CẢN!

Ứng Dụng Thực Tế

Ứng dụngChi tiết
Robot tránh vật cản2-4 sensor xung quanh robot
Dò đường line followingNhìn xuống mặt đất, phân biệt đen/trắng
Đếm sản phẩm trên băng chuyềnMỗi sản phẩm đi qua = 1 count
Phát hiện tiếp giáp cửaSensor trong khe cửa — phát hiện đóng/mở
Smart dustbin tự mở nắpPhát hiện tay đưa vào → servo mở nắp
Phát hiện giấy vào máy inTrong các thiết bị văn phòng

Lưu Ý Khi Sử Dụng

1. Logic đảo — OUT LOW = có vật cản

Nhiều người bị nhầm viết if (digitalRead(PIN) == HIGH) cho “có vật cản” → code không bao giờ phát hiện. Phải là if (digitalRead(PIN) == LOW). LED báo trên module sáng khi có vật cản — dùng LED này để kiểm tra logic trước khi đọc code.

2. Không dùng ngoài trời có nắng

Ánh sáng mặt trời chứa IR 940nm đậm đặc → module luôn báo “có vật cản” dù không có gì. Chỉ dùng trong nhà hoặc bóng râm. Nếu cần ngoài trời: dùng HC-SR04 (siêu âm) hoặc Sharp GP2Y (IR điều chế).

3. Vật đen không phản xạ IR — có thể không phát hiện được

Vật có màu đen hoặc bề mặt hấp thu (vải nhung, cao su đen) phản xạ rất ít IR → sensor không phát hiện dù vật ở ngay trước. Điều này đặc biệt quan trọng với robot dò đường: vạch đen cần phản xạ thấp, nền trắng phản xạ cao.

4. Triết áp cần điều chỉnh thực tế

Không có “vị trí chuẩn” cho triết áp — phụ thuộc màu sắc, chất liệu vật thể, và ánh sáng môi trường. Điều chỉnh với vật thể thực tế sẽ dùng trong ứng dụng. Vặn từ từ và quan sát LED báo trên module.

5. Nhiễu chéo nếu nhiều sensor

Nếu dùng nhiều IR sensor gần nhau → IR từ emitter của sensor này có thể đến receiver của sensor khác → đọc sai. Giải pháp: luân phiên kích hoạt từng sensor (bật emitter 1, đọc receiver 1, tắt, rồi bật emitter 2…) hoặc đặt sensor đủ xa nhau.

Bài tiếp theo: