Датчики линии и цвета — Оптическое зрение для навигации и классификации
Эти датчики превращают свет в данные о поверхности под роботом. От простого “черное/белое” до полного спектрального анализа — они позволяют роботам взаимодействовать с миром на визуальном уровне.
Физические принципы
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} \]
Сравнительная таблица
| Параметр | TCRT5000 | QTR-8A | TCS34725 | Формула/Принцип |
|---|---|---|---|---|
| Тип | Одиночный ИК | Матрица 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;
}
};
Калибровочные процедуры
Калибровка датчика линии
Сбор данных: \[ 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
Нормализация: \[ 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 мм на основе анализа текстуры поверхности.
Что дальше?
Оптические датчики — основа для более сложных систем компьютерного зрения:
- Машинное зрение (Computer Vision) — от простых датчиков к обработке изображений
- Оптические энкодеры — прецизионное измерение перемещения
- Сенсорная fusion — объединение оптических данных с другими сенсорами
- Автономная навигация по маркерам — использование цветовых кодов и маркеров
Вывод: Датчики линии и цвета — это “первый взгляд” робота на мир. Они учат основам оптических измерений: от простого бинарного “черное/белое” до сложного спектрального анализа. Эти технологии демонстрируют фундаментальный принцип робототехники: превращение физических свойств мира в цифровые данные, которые можно анализировать, интерпретировать и использовать для принятия решений.
