Реверс-инжиниринг лазерного датчика расстояния

image
Однажды ко мне попал нерабочий лазерный датчик расстояния Keyence LK-G407. Мало того, что он был нерабочий, так его еще и нельзя было использовать без специального управляющего блока. Но ведь у датчика такие интересные характеристики: измерение расстояния с точностью до единиц микрон, и скорость работы — 50 килоизмерений/с. Так что, чтобы запустить его, придется заметно поковыряться в самом датчике, заодно и ценный опыт получить.

Что же находится внутри датчика?


Оптическая часть датчика показана на фотографии ниже:
59f63c6893d62f9f06c8221909e693bd.jpg
Слева на фотографии — лазерный модуль, за ним — светочувтвительная линейка, справа — объектив и зеркало.
Из конструкции становится понятно, что этот датчик можно отнести к классу лазерных дальномеров с триангуляционным методом измерения расстояния. Принцип работы таких дальномеров хорошо описан здесь. В принципе, он довольно прост — при изменении расстояния до объекта, на который светит лазер, меняется угол между объективом дальномера и пятном лазера. Если в фокальной плоскости объектива установить светочувствительную линейку или матрицу, то можно определить этот угол по положению максимума выходного сигнала. Зная угол и расстояние между лазером и объективом, можно определить расстояние до объекта.
Достоинствами такого метода является очень большая точность на небольших расстояниях — при определенных условиях она может быть лучше 0.1 мкм!
Так же не проблема измерять расстояние с большой скоростью — нужно только использовать высокоскоростную светочувствительную линейку.
Схемотехника такого дальномера тоже достаточная простая — за счет того, что в устройстве нет больших частот, а первичное усиление сигнала идет в самой линейке.
Но есть и недостаток — точность метода резко падает при увеличении расстояния.
В этом датчике использован длиннофокусный объектив (фокусное расстояние около 150 мм), поэтому, для уменьшения габаритов датчика в его состав входит зеркало.

А теперь стоит перейти к электронике датчика.
Вот как выглядит вторая часть датчика:
1fbd01eb82bc82adef843ef5daea925d.jpg
И сама плата:
fb758df1078a09a017cc5006f477d179.jpg
И с другой стороны:
f1e3ff4efad8d9a8b678f624ce83da10.jpg
Печатная плата вроде бы четырехслойная — большая часть сигнальных проводников находятся на внешних слоях, силовые линии — на внутренних.
Как я уже упоминал, управляющего блока к датчику не было, а без него он не начинал работать. Понятно, что схем на датчик тоже не было, не была даже известна распиновка кабеля и напряжение питания датчика. Вывод — нужно делать реверс-инжиниринг схемы.
В результате получилась вот такая схема:
f25011b8e8c525b156afb3575ef1ff6b.png
Конечно, я не стал рисовать всю схему датчика, и разобрался только в той части, что связана с FPGA. Номера элементов на схеме не соответствуют номерам на плате.
Таким образом, структурная схема датчика:
image
Из нее понятно, что работой всего датчика управляют две микросхемы — FPGA Xilinx Sparta-3A и некая заказная микросхема. Однако, мне очень повезло — из схемы видно, что заказная микросхема связано только с FPGA. Таким образом, FPGA сама способна управлять всеми сигналами в датчике.

Ключевой элемент всей конструкции — светочувствительная линейка. Эта линейка — явно заказная. Под микроскопом на одном из ее краев видна надпись:
25–512
LI004–02
Я предположил, что 512 — число пикселей линейки, а 25 — ширина пикселя в микронах. Как выяснилось позже, я оказался прав.
Сзади к линейке припаяна небольшая плата:
cac0f230763cc1aaf78ebb691f0c592b.jpg
На ней расположен операционный усилитель, увеличивающий сигнал с линейки в 2 раза и несколько резисторов и конденсаторов. Эта плата подключается к разъему P1. Как видно из схемы, к линейке идут всего 3 сигнальных линии. Одна из них — явно аналоговый сигнал с ее выхода (он передается по отдельному коаксиальному проводу). Оставшиеся две линии — цифровые, и используются для управления линейкой. При анализе схемы мне опять повезло — при подаче напряжения на схему на одной из этих линий (4) появляется частота 10 МГц. Сразу стало понятно, что эта линия отвечает за тактирование линейки. Очевидно, что все управление линейкой идет по оставшейся линии (3). Я подключил линейку к микроконтроллеру STM32F4, и начал подавать на линии (3) и (4) различные сигналы. Как оказалось, работает линейка довольно просто — пока на линии (3) присутствует высокий уровень, идет экспозиция — линейка принимает свет. После установки на линии (3) низкого уровня нужно подать на линейку 14 тактовых импульсов, после чего на последующие 512 тактовых импульсов она будет выдавать аналоговый сигнал. Линейка работает от напряжения 5В, а FPGA — от 3.3В, и поэтому для согласования уровней используется микросхема DD2.

Аналоговый сигнал с линейки передается через ФНЧ на повторитель, собранный на микросхеме DA1. Далее сигнал подается на PGA — усилитель с программируемым усилением AD8369. Максимальное усиление этой микросхемы — 40 дБ, и его можно регулировать программно, устанавливая нужной код на ее входах BIT0–3. Эта микросхема предназначена для усиления дифференциального сигнала, и выход у нее тоже дифференциальный, так что далее оба ее выхода заведены на операционный усилитель DA4, усиливающий сигнал в 2 раза, и формирующий одиночный сигнал.
Далее аналоговый сигнал подается на вход 10-битного АЦП AD9200. Эта микросхема уже была знакома мне по SDR приемнику. В данном случае она подключена так, что диапазон оцифровываемых ею напряжений — (0,5–2,5) В. Оцифрованный сигнал с выхода АЦП передается на FPGA.
Стоит обратить внимание на вход CLAMP этого АЦП. Этот вход также управляется FPGA. Он предназначен для приведения постоянной составляющей входного сигнала к определенному уровню.
Вот схема входного каскада АЦП из даташита:
6c8c2ef569c34a709c0f91a9203fd556.png
При подаче высокого уровня на вход CLAMP на выходе усилителя и на AIN появляется напряжение, равное напряжению на входе «CLAMP IN».
В таком случае конденсатор CIN будет заряжаться до тех пор, пока напряжение на нем не станет равным (Uвх — Uclamp_in). После этого на входе CLAMP устанавливают низкий уровень, и усилитель перестает как-либо влиять на работу АЦП. В данном датчике вход «CLAMP IN» подключен к нижнему опорному напряжению АЦП +0,5В. Таким образом, если на выходе DA4 будет присутствовать некая постоянная составляющая, то за счет использования функции CLAMP можно устранить ее влияние на результат работы АЦП.
После того, как были написаны первые тестовые конфигурации для FPGA, оказалось, что управлять сигналом CLAMP действительно нужно, иначе сигнал с АЦП имеет очень большую постоянную составляющую. В своей реализации я просто подавал на него 1 в то время, пока не идет захват данных при помощи АЦП.

Из схемы видно, что в ней используется большое число питающих напряжений. Я не стал рисовать схемы различных источников питания, входящих в датчик и различные сглаживающие конденсаторы. Как оказалось, выводы питания (они выведены на отдельный разъем с 2 выводами) соединяются с DC-DC преобразователем TPS62050. Максимальное напряжение для него — 10В, от 6В электроника еще не запускалась, так что я решил, что рабочее напряжение датчика — 8В.

Лазерный диод, использованный в этом модуле, управляется электроникой, установленной на отдельной платке:
a4d08f7cf192fd4e49ed375b50437543.jpg
Схему этой платы я рисовать не стал. К главной плате она подключается через разъем P3. Как видно, управление лазером идет по двум линиям. Одна из них (3) подключена к FPGA через инвертор, и отвечает за включение лазера — он включается низким уровнем на выходе FPGA. Другая линия (4) нужна для регулирования мощности лазера. Это аналоговая линия, и для изменения напряжения на ней с состав схемы входит ЦАП, собранный на микросхемах DA5–7 (я так и не понял, почему разработчики нагородили такую запутанную схему, а не взяли готовую микросхему ЦАП).

Как известно, большинство FPGA не содержат энергонезависимой памяти, соответственно, конфигурация FPGA должна храниться во внешней микросхеме. В данном случае это DD3 XCF01 — специализированная микросхема Flash-памяти. При включении FPGA автоматически считывает конфигурацию из нее в свою память. Сама FPGA и XCF01 соединены в JTAG-цепь, которая подключена к разъему P2. В результате через этот разъем можно внутрисхемно программировать XCF01, конфигурировать FPGA и вести отладку.

Таким образом я разобрался в принципе работы электроники датчика, и у меня появилась часть его схемы. Теперь можно приступать к экспериментам, т.е. программированию ПЛИС. Стоит заметить, что до этого я не имел дела с ПЛИС производства Xilinx. Нужного программатора у меня тоже не было, так что пришлось сделать его самому, путем объединения нескольких OpenSource проектов.
Программатор нормально заработал, и мне удалось запустить на ПЛИС простые проекты — простое переключение выводов ПЛИС. Однако в дальнейшем мне нужна была связь с компьютером. С интерфейсом LVDS связываться я не захотел (у порта, на который заведены линии LVDS напряжение питания 2.5В), так что я просто перерезал две дорожки, соединяющие ПЛИС и ASIC. На плате был разъем, судя по всему предназначенный для настройки или тестирования ASIC, его я использовать не мог, поэтому также перерезал две дорожки, идущие к нему, и соединил выводы разъема с выводами ПЛИС. Теперь к этому разъему можно подключать переходник USB-UART.
Вид платы после доработки:
bc5c038ab626cc3185b176be5c124324.jpg

После этого я написал простую программу для проверки работы UART. Она заработала — компьютер правильно принимал одиночные байты, передаваемые по UART c ПЛИС. Следующий этап — передача данных с светочувствительной линейки на компьютер. Я использовал такую структурную схему программы FPGA:
e8a2b9bf594aa17187c83889a79200a6.png
Вид схемы в ISE:
d828aad0df003a3f89cbbce582e9aa4e.png
Top-level проекта нарисован в схемном редакторе, а все входящие в него модули написаны на Veriolg. Принцип работы проекта достаточно прост — данные с линейки, оцифрованные АЦП, захватываются ПЛИС и сохраняются в ОЗУ. После того, как все 512 элементов сигнала захвачены, они передаются по UART на компьютер. После того, как все данные переданы, цикл повторяется. Модуль «sensor_reader» в данном проекте управляет линейкой, лазером и сигналом CLAMP. Управление реализовано простейшим образом — всеми сигналами управляет счетчик тактовых импульсов. Модуль «tx_controller» включает в себя модуль передатчика UART. Он предназначен для передачи данных, которые модуль получает из внешней памяти.
В процессе работы уровень полезного сигнала на линейке может сильно меняться — из-за изменения расстояния до объекта и изменения его коэффициента отражения. При слишком маленьком сигнале измерения становятся невозможными, а при слишком большом сильно падает точность измерения. Из-за этого усиление аналогового сигнала нужно регулировать. Изначально проект в проект входил модуль UART-приемника, который позволял вручную изменять усиление, позже я его убрал и сделал автоматическую регулировку усиления — AGC.
Она включает в себя модуль поиска максимума сигнала («max_finder») и сам модуль AGC («agc_module»). Этот модуль тоже достаточно простой — если уровень сигнала меньше 170, то усиление увеличивается, если больше 250 — уменьшается.
Все модули, линейка и АЦП тактируются от частоты 10 МГц. Время экспозиции я сделал равным 5 мкс. Таким образом, весь процесс экспозиции и захват сигнала в ПЛИС занимают (5+51) мкс. Время передачи данных намного больше — при тактовой частоте 500000 бит/сек передача 512 байт занимает 10 мс, что дает 100 измерений в секунду.

Для просмотра переданных данных в реальном времени была написана простая программа на C#:
d38949c1a32815e49df2b30dd94373d2.png
При перемещении объекта вдоль лазерного луча положение максимума пика изменяется. Простое вычисление положения максимума дает низкую точность измерения, поскольку линейка содержит всего 512 пикселей. Поэтому для более точного измерения положения максимума сигнала приходится использовать алгоритмы поиска центра тяжести, которые позволяют определить координаты максимума сигнала с субпиксельной точностью.
Я использовал такую формулу для поиска центра тяжести пика:
6cf4bdef8af34c5b866f6c27563ddd8b.png
где n — общее число пикселей, Int[i] — интенсивность i пикселя.
Для того, чтобы различный шум, присутствующий в сигнале, не ухудшал точность работы, полезно обрабатывать только выборки, превышающие определенный порог и находящиеся близко к максимуму пика. Особенно это важно в том случае, когда усиления не хватает, и полезный сигнал не превышает половины шкалы АЦП. Более того, с ростом усиления увеличивается уровень шумов, что ухудшает ситуацию. В вышеупомянутой программе это учтено.
На картинке выше MAX POS — вычисленное по этой формуле значение, MAX POS F — среднее значение последних 50 измерений.
Как я уже говорил, получившаяся частота измерений — 100 Гц, и она ограничена скоростью передачи данных по UART. Однако не обязательно обрабатывать сигнал на компьютере, это можно сделать и на ПЛИС, за счет чего можно многократно повысить скорость измерений.

В результате была разработана программа для ПЛИС с такой структурной схемой:
9db31718377ba2980840535065ff08ea.png
Вид схемы в ISE:
63cc06b1336777cc3438735fd0100587.png
Как видно, часть модулей взяты из предыдущего проекта.
В данном случае вычислением положения центра тяжести (центроида) сигнала занимается модуль «centroid_finder». Для того, чтобы ограничить область анализа данных, в модуль передается грубое значение положение максимума и его амплитуда.
Так как эти величины можно вычислить, только проанализировав весь сигнал (то есть они появляются с задержкой в 512 тактовых импульсов), то получается, что оцифрованные данные нужно подавать на вход «centroid_finder» с этой же задержкой. Для того, чтобы задержать данные на 512 тактов, используется буфер FIFO. Для первоначального заполнения FIFO используется модуль «fifo_logic» — он запрещает чтение из FIFO, если он не заполнен.
Модуль «tx_controller_3bytes» последовательно передает 3 байта, первый из них — нулевой, оставшиеся два содержат вычисленное положение центра тяжести. На скорости 50000 bit/s передача 3 байт занимает 60 мкс -практически столько же, как и захват сигнала. Нулевой байт используется для синхронизации данных — после него всегда идет старший байт.
Передача данных по UART и захват данных запускаются одновременно сигналом «start_capture». Этот сигнал формируется, если одновременно закончилась предыдущая передача и вычислено новое положение центра тяжести. В результате время измерения расстояния и передачи данных оказывается близким к 60 мкс, что дает скорость измерения координат — 16,6 KSPS. Это меньше, чем заявлено у производителя датчика. Там указывается минимальное время измерения — 20 мкс (это соответствует 50 KSPS), хотя непонятно как получено это время, ведь даже при максимальной скорости работы AD9200 — 20 MSPS время захвата сигнала с 512 пикселей будет — 25,6 мкс. А это ведь еще без учета времени экспозиции.

Как я уже говорил ранее, этот датчик не работал. Насколько мне известно, проблема была в том, что он был установлен на сильно вибрирующей промышленной установке, и из-за вибрации лазер датчика вышел из строя (датчик перестал реагировать на темные поверхности).
К сожалению, на родном диоде маркировки не было. Я пробовал заменить лазерный диод в лазерном модуле на купленный 5 мВт лазерный диод, но через некоторое время его интенсивность упала. Скорее всего, электроника лазерного модуля была рассчитана на более мощный диод (хоть и работающий в импульсном режиме, за счет чего средний уровень излучения получается достаточно низким).
Для того, чтобы хоть как-то запустить датчик, я сделал свой драйвер лазерного диода, работающий в режиме постоянного излучения:
image
Родной лазерный диод датчика был завальцован в металлический корпус лазерного модуля, так что новый лазерный диод пришлось просто приклеить к модулю. При этом, габаритные размеры использованного диода несколько отличались от размеров родного диода, из за чего мне так и не удалось качественно сфокусировать лазерный модуль.
Так выглядит собранный датчик:
6bad6c250dd946d3a1d55e26297ef39e.jpg
Для того, чтобы при помощи датчика можно было измерять расстояние, нужно произвести его калибровку, т.е. определить закон, связывающий результат, возвращаемый датчиком, и реальное расстояние. Сам процесс калибровки представляет собой серию измерений, в результате которых формируется набор расстояний от датчика до некоторого объекта, и соответствующих им результатов.
Расстояние между датчиком и объектом нужно измерять очень точно. Для проведения калибровки я сделал стенд, включающий в себя линейный энкодер и сам лазерный датчик:
c8c01a0728a680ad5ddb464ff738f1eb.jpg

Расстояние измеряется до пластинки, закрепленной на торце энкодера.
После того, как все данные собраны, можно провести регрессионный анализ в Mathcad.
В результате у меня получилось такое выражение:
value_mm = 70.0 / Tan (-0.000277757*max_pos + 0.28355) — 366.23554
Очевидно, что значения констант в выражении от положения деталей датчика. Малейший сдвиг деталей приведет к тому, что вычисленное значение расстояния будет неверным. Поэтому все детали должны быть очень прочно закреплены.

На видео ниже показано, как можно обрабатывать данные с датчика:

В первой части видео показано, как выглядит сигнал со светочувствительной линейки. Хорошо видно, что на если объект темный, то амплитуда не изменяется (за счет работы АРУ), но уровень шумов сильно возрастает.
Во второй части видео программа принимает от датчика положение центра тяжести, и пересчитывает его в расстояние. Также в программу передается значение расстояния до объекта, полученное от энкодера. Под расстоянием энкодера выводится разность между этими расстояниями. Можно видеть, что в при движении разность становится больше 1 мм (это связано с задержками при передаче расстояний и их отображении), но по время остановок при любом расстоянии разность не превышает 0.03 мм.

Для того, чтобы полученные с датчика данные было проще анализировать, я написал программу, сохраняющую данные, принятые от датчика в формат wav. Такие файлы можно открывать в звуковых редакторах, и применять к ним различные фильтры.
Вот например, как выглядит сигнал с датчика, который был направлен на стенку трансформаторного блока питания:
d9c4776a55a147238feff6579249b4b8.png
Видно, как при включении блок питания начал вибрировать.
Я пробовал направлять датчик на диффузор динамика, на котором воспроизводится музыка — и после обработки в звуковом редакторе ее действительно было слышно.

Таим образом, мне удалось дать нерабочему датчику новую жизнь (он может понадобится мне в будущих проектах), и заодно получить опыт работы с ПЛИС Xilinx.

Проекты для FPGA

© Habrahabr.ru