Фоточувствительная линейка TSL1401

В этой статье речь пойдет о датчике TSL1401. Это устройство мало кому известно из робо-энтузиастов, хотя имеет очень большой потенциал в области DIY робототехники. Датчик TSL1401 представляет собой линейку из 128 фоточувствительных сенсоров, оформленную в виде законченного модуля с объективом. Есть вариант линейки на 256 точек, он имеет маркировку TSL1402. Производит датчик компания AMS-TAOS, базирующаяся в США.

Вариант модуля датчика, приобретенного мной в Китае выглядит так:

TSL1401

Сам датчик находится под объективом и бывает в разных вариантах корпусов: DIP, SOIC, SMD CL. В моем случае был как раз последний вариант, очень тяжело паяющийся. Вот такой:

TSL1401chip

Особенности:

  • фото-датчиков: 128;
  • плотность фото-датчиков:. 400 на дюйм;
  • хорошая линейность и равномерность сигнала;
  • широкий динамический диапазон: 4000:1 (72 дБ);
  • максимальная частота опроса: до 8 МГц;
  • напряжение питания: 3 — 5 В.

Как правило, вместе с датчиком продавцы предлагают объективы с разным углом обзора. Я встречал объективы с углами: 52, 56, 81 и 120 градусов. Модуль с датчиком стоит в среднем 1500 — 2000 руб. на конец 2015 года. Сам датчик — около 700 руб.

1. Применение

Есть как минимум две задачи для которых я хочу применить TSL1401. Первая: с помощью такого датчика можно сделать очень качественное распознавании линии для робота-следопыта (он же Line Follower). Представьте себе, что вместо двух, четырех, или даже 16 отдельных сенсоров вы получите сразу 128! Такое разрешение позволит очень чутко реагировать на все нюансы контрастной линии, и даже распознавать определенные шаблоны.

«Но можно же использовать целую видеокамеру для распознавания линии!» — скажет самый умный из читателей. Это действительно так, но только отчасти. С камерой можно сделать очень умного робота, который сможет не просто распознавать элементы трассы прямо под собой, но и сможет планировать маневры заранее. Но как известно, методы машинного зрения, использующие полноразмерную картинку, требуют очень много вычислительных ресурсов. Самая мощная Raspberry Pi с трудом справляется с этой задачей. Вообще, обработка данных видео потока — есть не самая быстрая процедура даже на персональных компьютерах. Про слабые AVR и говорить не приходится.

Так вот, чтобы не тратить скудные ресурсы микроконтроллера на обработку большого изображения, которое часто не так уж и необходимо, мы можем использовать всего одну линию. В результате, даже слабая Ардуино Уно легко справляется с задачей получения данных с TSL1401. С оптимизированным кодом, удается получить частоту обновления данных — 4КГц.

Наконец, вторая задача заключается в создании быстрого самодельного лидара на основе TSL1401. Собственно, подобный проект уже есть на roboforum.ru. Лидар (или оптический сканирующий дальномер) — это вообще крайне полезная вещь для любого робота. Используя лидар можно реализовать метод одновременной навигации и позиционирования (SLAM).

Но это в будущем, а именно в этой статье мы попробуем просто считать и визуализировать данные с этого незаурядного датчика.

2. Подключение

Датчик который я испытывал имеет всего пять выводов. Подключаем его к Ардуино Уно по такой схеме:

TSL1401 SI CLK AO Vcc Gnd
Arduino Uno 5 4 A0 +5V GND

Всё, 5-минутное дело. Теперь переходим к программированию.

3. Программа

Работа с датчиком доступно описана в спецификации. Попробуем в лоб реализовать его диаграмму работы, которая выглядит так:

diag_oneshot

Датчик TSL1401 интегрирующий, а это значит, что вектор значений будет зависеть от времени экспозиции. Другими словами, если датчик будет находиться в режиме измерения (экспозиции) достаточно долго, то показания яркости всех его точек попросту зашкалят. То же самое произойдет с незакрепленной фотобумагой, если её надолго оставить на свету. Ввиду такой особенности, наша задача заключается в том, чтобы ограничить время экспозиции коротким промежутком времени, и уже потом снять накопленные показания.

Согласно диаграмме, для того чтобы запустить процедуру измерения, мы должны подать положительный импульс на вывод SI, и зафиксировать его синхроимпульсом. Экспозиция будет длиться до тех пор, пока мы не сделаем еще один импульс SI. Снять сигнал с датчика мы сможем только после завершающего импульса SI, во время уже следующего измерения.

Циклический алгоритм получения данных с датчика будет выглядеть следующим образом:

  1. Подаем HIGH на ногу SI, начиная тем самым измерение;
  2. Фиксируем SI синхроимпульсом;
  3. Опускаем SI в LOW;
  4. В цикле посылаем на датчик 128 синхроимпульсов. Этим мы чистим регистр
  5. датчика от накопленных ранее значений;
  6. Ждем N миллисекунд — экспозиция;
  7. Подаем HIGH на ногу SI, начиная сбор данных;
  8. Фиксируем SI синхроимпульсом;
  9. Опускаем SI в LOW;
  10. В цикле снимаем с ноги AO данные, и делаем синхроимпульс для перехода к следующему пикселю.

Этот алгоритм называется one-shot. Так мы сможем в любое нужное нам время получить вектор значений. По сути — это частный случай общего непрерывного алгоритма, который в спецификации описывается такой диаграммой:

diag

Здесь видно, что на самом деле, промежуток между двумя импульсами SI одновременно задает период экспозиции, и открывает доступ к регистру для считывания данных прошлого измерения.

Код программы на Ардуино Уно, которая в лоб, без всяких оптимизаций, реализует диаграмму one-shot представлена ниже.

#include <SerialFlow.h>

static const byte PACKET_SIZE = 128;
static const byte VALUE_SIZE = 1;
static const boolean SEPARATE_VALUES = false;

const unsigned char PS_32 = (1 << ADPS2) | (1 << ADPS0);
const unsigned char PS_128 = (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);
int CLKpin = 4; // CLK - Clock 
int SIpin = 5; // SI - Serial input 
int AOpin = A0; // AO - Analog output
int IntArray[128];
char ptxt[128 + 1];

SerialFlow rd(&Serial);

void setup() {
  rd.setPacketFormat(VALUE_SIZE, PACKET_SIZE, SEPARATE_VALUES);
  rd.begin(115200);

  pinMode(CLKpin, OUTPUT); 
  pinMode(SIpin, OUTPUT);

  // ускоряем АЦП в 4 раза 
  ADCSRA &= ~PS_128; 
  ADCSRA |= PS_32; // теперь одно АЦП преобразование займет ~30 мкс

  analogReference(DEFAULT);

  // Установка всех GPIO в LOW:
  for( int i=0; i< 14; i++ ){
    digitalWrite(i, LOW); 
  } 

  // Запуск первого измерения
  digitalWrite(SIpin, HIGH);
  ClockPulse(); 
  digitalWrite(SIpin, LOW);
 
  // Пока идет измерение, чистим содержимое регистра датчика
  for(int i=0;i< 260;i++){
    ClockPulse(); 
  }
}

void loop(){
  // Запуск нового измерения
  digitalWrite(SIpin, HIGH);
  ClockPulse();
  digitalWrite(SIpin, LOW);

  // Чистка регистра датчика от мусора
  for(int i = 0; i < 128; i++){
    ClockPulse(); 
  }
 
  // Экспозиция 3мс
  delay(3);

  // Запуск сбора данных
  digitalWrite(SIpin, HIGH);
  ClockPulse();
  digitalWrite(SIpin, LOW);

  // Чтение 128 пикселей
  for(int i=0; i < 128; i++){
    delayMicroseconds(20); // пауза для фиксации значения на АЦП
    IntArray[i] = analogRead(AOpin);
    ClockPulse(); 
  }

  // Отправка вектора значений через UART
  for(int i=0; i<128; i++){
    rd.setPacketValue(IntArray[i]/4);
  }
  rd.sendPacket();

  // Здесь можем выполнять основной алгоритм
  // ...
}

// Функция, генерирующая синхроимпульс
void ClockPulse(){
  delayMicroseconds(1);
  digitalWrite(CLKpin, HIGH);
  digitalWrite(CLKpin, LOW);
}

Если алгоритм покажется вам слишком медленным, его безусловно можно будет ускорить, отказавшись от тяжелых функций стандартной библиотеки Ардуино. Так сделано в одной из программ, которую я нашел в интернете. Код хранится на гитхабе. Когда я выше упоминал про скорость опроса датчика в 4 кГц, я имел ввиду именно эту программу.

4. Видео

Теперь посмотрим что получилось. Видео теста состоит из нескольких эпизодов. В первом случае вывод данных осуществлялся прямо в терминал последовательного порта, в остальных случаях для визуализации использовался SFMonitor.


Изменено:

Фоточувствительная линейка TSL1401: Один комментарий

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *