Skip to main content

Датчики линии и цвета — Оптическое зрение для навигации и классификации

Эти датчики превращают свет в данные о поверхности под роботом. От простого “черное/белое” до полного спектрального анализа — они позволяют роботам взаимодействовать с миром на визуальном уровне.

Физические принципы

1. Датчики линии: Закон отражения Ламберта

Для идеально рассеивающей поверхности (матовой): \[ I_{\text{reflected}} = I_0 \cdot \frac{\rho}{\pi} \cdot \frac{\cos\theta_i \cos\theta_r}{r^2} \] где:

  • \(I_0\) — интенсивность излучаемого ИК-света
  • \(\rho\) — коэффициент отражения поверхности
  • \(\theta_i, \theta_r\) — углы падения и отражения
  • \(r\) — расстояние до поверхности

Для черной и белой поверхностей:

  • Белая: \(\rho \approx 0.85-0.90\) → высокий сигнал
  • Черная: \(\rho \approx 0.05-0.10\) → низкий сигнал

2. Датчики цвета: Цветовая модель и фильтры

Преобразование RGB в отклик датчика: \[ \begin{aligned} R_{\text{sensor}} &= \int S(\lambda) \cdot F_R(\lambda) \cdot \rho(\lambda) d\lambda \\ G_{\text{sensor}} &= \int S(\lambda) \cdot F_G(\lambda) \cdot \rho(\lambda) d\lambda \\ B_{\text{sensor}} &= \int S(\lambda) \cdot F_B(\lambda) \cdot \rho(\lambda) d\lambda \end{aligned} \] где:

  • \(S(\lambda)\) — спектральная чувствительность фотодиода
  • \(F_{R,G,B}(\lambda)\) — передаточные функции фильтров
  • \(\rho(\lambda)\) — спектральный коэффициент отражения поверхности

3. Расстояние до поверхности

Интенсивность отраженного света: \[ I_{\text{received}} \propto \frac{1}{d^4} \] (по закону обратных квадратов ×2: излучатель→поверхность и поверхность→приемник)


Типы датчиков и их характеристики

1. Простые ИК датчики (TCRT5000)

Схема подключения:

TCRT5000 → Arduino
  VCC    → 5V
  GND    → GND
  OUT    → Аналоговый или цифровой вход

Выходная характеристика: \[ V_{\text{out}} = V_{\text{CC}} \cdot \frac{R_{\text{photo}}}{R_{\text{fixed}} + R_{\text{photo}}} \] где \(R_{\text{photo}}\) изменяется от ~10 кОм (белое) до ~100 кОм (черное).

2. Многоканальные датчики линии (QTR-8A)

Матрица из 8 датчиков: \[ \mathbf{S} = [s_1, s_2, \dots, s_8] \] где \(s_i \in [0, 1]\) — нормализованное значение датчика.

Положение линии: \[ x_{\text{line}} = \frac{\sum_{i=1}^{8} i \cdot s_i}{\sum_{i=1}^{8} s_i} \]

3. RGB датчики цвета (TCS34725)

Структура пикселя:

[ Красный фильтр ] [ Прозрачный (Clear) ]
[ Зеленый фильтр ] [ Синий фильтр ]

Преобразование сырых значений в RGB: \[ \begin{aligned} R &= k_R \cdot (R_{\text{raw}} - R_{\text{dark}}) \\ G &= k_G \cdot (G_{\text{raw}} - G_{\text{dark}}) \\ B &= k_B \cdot (B_{\text{raw}} - B_{\text{dark}}) \end{aligned} \]


Сравнительная таблица

ПараметрTCRT5000QTR-8ATCS34725Формула/Принцип
ТипОдиночный ИКМатрица 8×ИКRGB+Clear массив
Диапазон0.5-3 см0.3-1.5 см0-5 см\(I \propto 1/d^4\)
Разрешение1 бит (черный/белый)8 бит × 8 каналов16 бит × 4 канала
Скорость10-100 кГц1-10 кГц1-100 Гц (интеграция)\(t_{\text{int}} = 2.4-614\ \text{мс}\)
ИнтерфейсАналоговый/цифровойАналоговый/цифровойI2C
ПрименениеДетектор линии/краяСледование по сложной линииОпределение цвета\(x_{\text{line}} = \frac{\sum i s_i}{\sum s_i}\)

Практическая реализация

Схема и код для TCRT5000

class LineSensor {
private:
    int pin_;
    int threshold_;
    
public:
    LineSensor(int analog_pin, int threshold = 512) 
        : pin_(analog_pin), threshold_(threshold) {}
    
    // Калибровка: определяем порог автоматически
    void calibrate() {
        int white_val = 0, black_val = 1023;
        
        Serial.println("Place sensor on WHITE, press key");
        while(!Serial.available());
        white_val = analogRead(pin_);
        
        Serial.println("Place sensor on BLACK, press key");
        while(!Serial.available());
        black_val = analogRead(pin_);
        
        threshold_ = (white_val + black_val) / 2;
    }
    
    // Чтение с гистерезисом
    bool isBlack() {
        static bool last_state = false;
        int val = analogRead(pin_);
        
        if (last_state && val > threshold_ + 50) {
            last_state = false;
        } else if (!last_state && val < threshold_ - 50) {
            last_state = true;
        }
        
        return last_state;
    }
    
    // Нормализованное значение (0=черный, 1=белый)
    float readNormalized() {
        int val = analogRead(pin_);
        return constrain(map(val, black_ref_, white_ref_, 0, 1000) / 1000.0, 0, 1);
    }
};

Алгоритм следования по линии с PID

class LineFollower {
private:
    float kp_, ki_, kd_;
    float integral_, prev_error_;
    QTR8ASensor sensor_;
    
public:
    void followLine() {
        // Чтение позиции линии (-1 слева, 0 центр, +1 справа)
        float line_position = sensor_.readLinePosition();
        
        // PID расчет
        float error = -line_position; // Отрицание для правильной реакции
        integral_ += error * dt;
        integral_ = constrain(integral_, -1, 1); // Anti-windup
        float derivative = (error - prev_error_) / dt;
        
        float correction = kp_ * error + ki_ * integral_ + kd_ * derivative;
        
        // Применение к моторам
        float base_speed = 0.5;
        setMotorSpeeds(base_speed - correction, base_speed + correction);
        
        prev_error_ = error;
    }
};

Работа с TCS34725 (цветовой датчик)

#include <Wire.h>
#include "Adafruit_TCS34725.h"

class ColorDetector {
private:
    Adafruit_TCS34725 tcs_;
    float calibration_matrix_[3][3]; // Матрица калибровки
    
public:
    bool begin() {
        if (!tcs_.begin()) return false;
        
        // Настройка времени интеграции (2.4-614 мс)
        tcs_.setIntegrationTime(TCS34725_INTEGRATIONTIME_50MS);
        // Настройка усиления (1x, 4x, 16x, 60x)
        tcs_.setGain(TCS34725_GAIN_4X);
        
        return true;
    }
    
    // Чтение RGB с компенсацией освещенности
    void readColor(float &r, float &g, float &b) {
        uint16_t raw_r, raw_g, raw_b, raw_c;
        tcs_.getRawData(&raw_r, &raw_g, &raw_b, &raw_c);
        
        // Нормализация по clear каналу (освещенность)
        if (raw_c > 0) {
            r = (float)raw_r / raw_c;
            g = (float)raw_g / raw_c;
            b = (float)raw_b / raw_c;
        }
        
        // Применение калибровочной матрицы
        applyCalibration(r, g, b);
    }
    
    // Определение цвета по Euclidean distance
    String classifyColor(float r, float g, float b) {
        // База эталонных цветов
        struct ReferenceColor {
            String name;
            float r, g, b;
        };
        
        ReferenceColor colors[] = {
            {"RED", 0.8, 0.2, 0.2},
            {"GREEN", 0.2, 0.8, 0.2},
            {"BLUE", 0.2, 0.2, 0.8},
            {"YELLOW", 0.8, 0.8, 0.2},
            {"WHITE", 0.95, 0.95, 0.95},
            {"BLACK", 0.05, 0.05, 0.05}
        };
        
        float min_distance = 1000;
        String detected_color = "UNKNOWN";
        
        for (auto &ref : colors) {
            float distance = sqrt(
                pow(r - ref.r, 2) + 
                pow(g - ref.g, 2) + 
                pow(b - ref.b, 2)
            );
            
            if (distance < min_distance) {
                min_distance = distance;
                detected_color = ref.name;
            }
        }
        
        return detected_color;
    }
};

Калибровочные процедуры

Калибровка датчика линии

  1. Сбор данных: \[ W_i = \frac{1}{N} \sum_{k=1}^{N} w_{i,k}, \quad B_i = \frac{1}{N} \sum_{k=1}^{N} b_{i,k} \] где \(w_{i,k}\) — k-е измерение на белом для датчика i

  2. Нормализация: \[ s_i = \frac{\text{raw}_i - B_i}{W_i - B_i} \]

Калибровка цветового датчика

\[ \begin{aligned} R_c &= m_{11}R_r + m_{12}G_r + m_{13}B_r + o_1 \\ G_c &= m_{21}R_r + m_{22}G_r + m_{23}B_r + o_2 \\ B_c &= m_{31}R_r + m_{32}G_r + m_{33}B_r + o_3 \end{aligned} \] где:

  • \(c\) — скорректированный (corrected)
  • \(r\) — сырой (raw)

Применение в робототехнике

1. Робот для следования по линии с перекрестками

class AdvancedLineFollower {
private:
    enum State { FOLLOW, CROSSROAD, TURN };
    State current_state_;
    int crossroad_count_;
    
public:
    void update() {
        switch(current_state_) {
            case FOLLOW:
                followLinePID();
                if (detectCrossroad()) {
                    current_state_ = CROSSROAD;
                    crossroad_count_++;
                }
                break;
                
            case CROSSROAD:
                if (shouldTurn(crossroad_count_)) {
                    current_state_ = TURN;
                } else {
                    goStraight();
                    current_state_ = FOLLOW;
                }
                break;
                
            case TURN:
                executeTurn(90); // Поворот на 90°
                current_state_ = FOLLOW;
                break;
        }
    }
    
    bool detectCrossroad() {
        // Перекресток = все датчики видят черное
        int black_count = 0;
        for (int i = 0; i < 8; i++) {
            if (sensors_[i].isBlack()) black_count++;
        }
        return black_count >= 6;
    }
};

2. Система сортировки по цвету

class ColorSorter {
private:
    ColorDetector detector_;
    ConveyorMotor motor_;
    SortingArm arm_;
    
public:
    void sortItem() {
        // 1. Детекция цвета
        float r, g, b;
        detector_.readColor(r, g, b);
        String color = detector_.classifyColor(r, g, b);
        
        // 2. Определение позиции сброса
        int drop_position = getDropPosition(color);
        
        // 3. Активация соответствующего механизма
        arm_.moveToPosition(drop_position);
        delay(100);
        arm_.dropItem();
        
        // 4. Логирование
        logSorting(color, drop_position);
    }
};

3. Система выравнивания по краю

class EdgeAligner {
private:
    LineSensor left_sensor_, right_sensor_;
    
public:
    void alignToEdge() {
        // PID для поддержания равного расстояния с двух сторон
        float left_dist = left_sensor_.readDistanceToEdge();
        float right_dist = right_sensor_.readDistanceToEdge();
        
        float error = left_dist - right_dist;
        float correction = pidController(error);
        
        adjustPosition(correction);
    }
};

Проблемы и решения

Проблема 1: “Изменение освещенности влияет на показания”

Решение: Динамическая калибровка:

void dynamicCalibration() {
    // Периодическое обновление эталонов белого/черного
    static unsigned long last_calib = 0;
    if (millis() - last_calib > 5000) {
        white_ref_ = max(white_ref_, current_reading);
        black_ref_ = min(black_ref_, current_reading);
        last_calib = millis();
    }
}

Проблема 2: “Отражение от глянцевых поверхностей”

Решение: Использование поляризационных фильтров и алгоритма медианной фильтрации: \[ \text{median_filter}(x_n) = \text{median}(x_{n-2}, x_{n-1}, x_n, x_{n+1}, x_{n+2}) \]

Проблема 3: “Цвет воспринимается по-разному при разном освещении”

Решение: Использование цветовых пространств, инвариантных к освещению: \[ \begin{aligned} r &= \frac{R}{R + G + B} \\ g &= \frac{G}{R + G + B} \\ b &= \frac{B}{R + G + B} \end{aligned} \]


Будущие разработки

1. Нейросетевые классификаторы цвета

# Пример простой нейросети для классификации цвета
model = Sequential([
    Dense(16, input_dim=3, activation='relu'),  # RGB вход
    Dense(8, activation='relu'),
    Dense(6, activation='softmax')  # 6 цветов
])

2. Мультиспектральные датчики

Принцип: Измерение в узких спектральных полосах (10-20 нм). Применение: Определение материала, влажности, состояния поверхности.

3. Оптические энкодеры на основе датчиков линии

Точность: До 0.01 мм на основе анализа текстуры поверхности.


Что дальше?

Оптические датчики — основа для более сложных систем компьютерного зрения:

  1. Машинное зрение (Computer Vision) — от простых датчиков к обработке изображений
  2. Оптические энкодеры — прецизионное измерение перемещения
  3. Сенсорная fusion — объединение оптических данных с другими сенсорами
  4. Автономная навигация по маркерам — использование цветовых кодов и маркеров

Вывод: Датчики линии и цвета — это “первый взгляд” робота на мир. Они учат основам оптических измерений: от простого бинарного “черное/белое” до сложного спектрального анализа. Эти технологии демонстрируют фундаментальный принцип робототехники: превращение физических свойств мира в цифровые данные, которые можно анализировать, интерпретировать и использовать для принятия решений.