Обработка потока с видеокамеры средствами ПЛИС/FPGA. OV7670 + Zedboard

В статье маркеры визуальной системы автоматической посадки БЛА была описана  обработка видео-данных реального времени для системы оптического типа – VBLS. Это была хоть и шустрая, сделанная на C++, но модель. Теперь, как было замечено в конце этой статьи, пришло время показать систему в боевом варианте, где обработка видеопотока параллелится в ПЛИС.

В данной системе для экспериментов я выбрал весьма удобную отладочную плату  Zedboard. Чип Zynq-7000 основан на SoC архитектуре ARM+FPGA, однако на этом этапе функционал ARM использовать не будем и ограничиваемся работой только с ПЛИС. В качестве камеры выступает популярный модуль OV7670 разрешением 640х480 (хвала Алиэкспрессу!). Почему такое скромное разрешение? А потому что в Zedboard ресурсы ограничены, в частности критичным для нас является размер встроенной быстродействующей памяти BRAM (не путать с DDR). Результирующее видео выводится на дисплей через разъем VGA платы.

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

Теперь, пошли по порядку.

Инструментарий

Отладочная плата Zedboard. Конечно, это серьезный финансовый барьер для тех кто занимается разработкой в режиме хобби, но если вы собираетесь работать с ПЛИС то вам нужна эта плата. Она удобна, хорошо документирована, и к тому же на ней есть ARM, на котором вы будете дополнительно молотить то, что осталось от ПЛИС.

Micro-USB кабель. Он будет подключаться к разъему PROG платы и нужен для закачки логической матрицы.

По мере продвижения вперед ваш рабочий стол приобретет примерно такой вид.

ov7670, Zedboard, Zynq-7000, FPGA Xilinx, VHDL, PMOD, VGA, Video Processing, Visual Based Automatic Landing System, VBLS, Marker, Marker, UAV, Drone, Автоматическая посадка БПЛА, DinaPrim 2700

Внешние подключения системы: OV7670 + Zedboard

Среда разработки Vivado 2017.4. Качайте и устанавливайте ее бесплатный вариант – WebPack. Есть версии как для Linux так и для Windows; у меня стоит Linux версия. Среда весьма требовательная к ресурсам компа. Да, и сразу предупреждаю – “компиляция” исходного кода занимает приличное время (минуты). Это дисциплинирует и заставляет более требовательно относиться к своим исходникам – попросту нужно делать меньше ошибок. Для Vivado есть и более свежии версии, но у меня они пошли плохо – начались конфликты с кириллицей (кто бы мог подумать).

Для тех кто пользует Linux: поскольку среду разработки разворачивает скрипт установки, укажите место для Vivado как /opt/Xilinx, предварительно настроив права доступа на этот каталог.  Обновите правила udev для того, чтобы операционная система правильно определила кабель USB, подключенный к Zedboard. Для этого в каталоге

запустите соответствующие скрипты и обновите правила с помощью udevadm.

Камера OV7670. В поставке есть платы с чипом FIFO и без него. Нам он без надобности; наше описание расчитано на интерфейс камеры без этого буфера. Для подключения камеры к соединителям PMOD платы Zedboard я сделал переходную колодку. Можно обойтись и без пайки, просто соединив пины камеры с гнездами разъемов кабелями мама – папа. Только не делайте соединения слишком длинными – все таки работаем на частоте 25 МГц, длинный монтаж начнет звенеть.

ov7670, Zedboard, Zynq-7000, FPGA Xilinx, VHDL, PMOD, VGA, Video Processing, Visual Based Automatic Landing System, VBLS, Marker, Marker, UAV, Drone, Автоматическая посадка БПЛА, DinaPrim 2700

Переходная колодка для подключения камеры OV7670 к разъемам PMOD Zedboard

Дисплей с VGA входом и VGA кабель. Да, для того чтобы увидеть плоды своих трудов нам понадобится этот раритетный девайс, ничего не поделаешь. Будем формировать сигналы классического интерфейса VGA и выдавать их на разъем платы, который подключен к PL. И еще, потихоньку привыкаем к терминологии архитектуры Zynq: PL это программируемая логика (ПЛИС), PS это процессор (ARM).

Сразу отвечаю на вопрос продвинутых пользователей: это каким образом цифровая подсистема PL формирует сугубо аналоговые сигналы VGA? Если мы взглянем на схему платы, то увидим резистивные делители, подключенные к выходу VGA. Это фактически есть не что иное, как ЦАП, сделанный дешево и сердито. На каждый из сигналов цвета выделено 4 разряда, поэтому мы ограничены 16 градациями по каждому каналу. На самом деле, в этом проекте цвет нам не понадобится, поскольку будем работать с серым изображением.

Теперь переходим к следующему этапу – связываем все это вместе.

Подключение камеры OV7670 к плате Zedboard

Дальше, по ходу изложения, я поделюсь исходниками проекта. Но уже сейчас нам понадобится один файл – zedboard.xdc, в котором описаны внешние соединения, о которых должна знать PL.  Должна знать – потому что в этом файле осуществляется привязка по цепочке: контакт разъема PMOD платы – вывод PL – наименование соответствующего сигнала в коде VHDL. Функции этого файла, который в терминологии Xilinx именуется constraints файлом, горазо шире, но в нашем проекте мы используем этот необходимый минимум.

ov7670, Zedboard, Zynq-7000, FPGA Xilinx, VHDL, PMOD, VGA, Video Processing, Visual Based Automatic Landing System, VBLS, Marker, Marker, UAV, Drone, Автоматическая посадка БПЛА, DinaPrim 2700

Подключение камеры OV7670 к плате Zedboard

Поскольку я заговорил об исходниках, здесь будет уместно сообщить откуда я их взял ) За основу проекта использован этот VHDL код, однако имейте в виду что там есть ошибки как в самом коде, так и в распиновке PMOD разъемов платы Zedboard; кроме того я переделал этот проект с цветного на черно-белый. Можно сказать лишил его всех красок бытия ) Также я включил в состав исходников dummy модуль, в котором будут жить разные алгоритмы обработки изображения в реальном времени. Пока все что он делает – просто передает полученные байты изображения модулю VGA. А пока этот компонент живет в комфортном окружении, которое понадобится в будущем: блок памяти BRAM на входе и блок на выходе. Все в соответствии с правилами модульного программирования, выделение интерфейсов и изоляция модуля от остальных.

Итак, возвращаемся к подключению камеры. Для удобства я сделал табличку, в котором свел вместе пины камеры, коннекторов PMOD платы и данные из constraints file zedboard.xdc.

ov7670, Zedboard, Zynq-7000, FPGA Xilinx, VHDL, PMOD, VGA, Video Processing, Visual Based Automatic Landing System, VBLS, Marker, Marker, UAV, Drone, Автоматическая посадка БПЛА, DinaPrim 2700

Ваша задача – соединить пины камеры из первого столбца таблицы с гнездами разъема PMOD платы Zedboard второго столбца таблицы. О том каким выводам ПЛИС Zynq-7000 соответствуют эти сигналы, знаем только мы и руководство Zedboard Hardware Guide. VHDL коду все равно – он оперирует названиями сигналов из правого столбца таблицы, а привязывает эти сигналы к выводам Zynq-7000 файл zedboard.xdc. Таким образом все сходится.

Раз мы уже занялись подключением камеры OV7670, быстро пробежимся по ее выводам. RESET и PWDN – сброс и включение камеры. Управление камерой обеспечивает последовательный интерфейс SIOC/SIOD, аналогичный I2C. В камере находится тьма регистров, которые нужно правильно установить – главная причина того что я не разработал код с нуля а взял за основу существующий. Впрочем, это меня не спасло – все равно пришлось вникать в мануал устройства. Если вам придется заняться тем же самым, имейте в виду что зарезервированные регистры и биты также нужно устанавливать определенным образом, и тайна великая сия есть.

Камеру можно синхронизировать внешним сигналом XCLK (мы так и делаем, формируя его в коде), тогда все что она выдает идет синхронно с ее выходом PCLK. А идут с нее байты видеоданных по шине D в сопровождении импульса кадровой синхронизации VSYNC, по которому мы сбрасываем в ноль счетчики и адреса, и еще строб наличия данных строки HREF, по которому мы разрешаем считывание данных.

Вот собственно и все. Для чего нужны пины 3v3 и GND вы наверное догадались сами.

Теперь самое время объявить хорошую новость для тех, у кого нет платы Zedboard, камеры и дисплея. Вы можете сразу начинать экспериментировать с VHDL, поскольку в Vivado есть режим симуляции. Единственное что вам понадобится дополнительно – включить в симуляцию testbench файл tb_top.vhdl, который имитирует данные с камеры. Вы можете написать свой testbench с тем чтобы посмотреть временные диаграммы как всего проекта, так и одного отдельного модуля.

Запуск Vivado, создание проекта, прошивка ПЛИС

Создайте новый RTL проект, добавьте VHDL файлы и constraints файл zedboard.xdc (ссылки в следующем разделе). Не забудьте указать в настройках проекта, что вы используете Zedboard: это важно. Добавьте также в симуляцию файл tb_top.vhdl. В результате иерархия компонентов должна выглядеть примерно таким образом:

ov7670, Zedboard, Zynq-7000, Vivado, FPGA Xilinx, VHDL, PMOD, VGA, Video Processing, Visual Based Automatic Landing System, VBLS, Marker, Marker, UAV, Drone, Автоматическая посадка БПЛА, DinaPrim 2700

Иерархия исходных текстов проекта

Кстати, справа Vivado показывает нам сколько ресурсов мы заняли у ПЛИС (эти данные появятся после компиляции проекта). Видно, что мы хорошо поимели память BRAM: ее осталось всего 20%. Логических блоков LUT было использовано всего ничего – всего лишь 1%.

Обратите внимание на компоненты обозначенные квадратиками: это “библиотечные” IP блоки, специфичные для Zedboard, которые мы используем в проекте. Их мы находим в меню “IP Catalog” и настраиваем. Вам нужно немного поработать: создать три компонента из IP блоков, а именно Clock wizard и два блока памяти BRAM. Каким образом компоненты встраиваются в проект, видно из файла ov7670_top.vhd, который играет роль кросс – платы для остальных модулей, то есть является топовым модулем иерархии.

Даю настройки IP блоков, отличные от настроек по умолчанию (до двоеточия я указал название вкладки, в которой нужно изменить параметр):

Блоки BRAM конфигурации Simple Dual Ram имеют одинаковую “глубину” равную 307200 (640х480) и различаются только размером шины данных: в первом случае это 8 бит, соответствующие размерности данных с камеры, во втором – 4 бита, в котором кодируются все цветовые каналы VGA.

После того как вы вписали исходники в проект и подготовили IP блоки, нужно пройти этапы “компиляции”. Первый из этих этапов – Synthesis, на котором ваша схема будет связана вместе и для нее будут подобраны элементарные кирпичики FPGA – Slices and LUTs. Здесь вас ждут типичные ошибки синтаксиса (которые можно выловить раньше – редактор указывает на них сразу) и например ошибки несовпадения разрядности, когда к 8-пиновому разъему вы пытаетесь прикрутить 16-пиновый. На втором этапе Implementation будет предпринята попытка втиснуть плоды вашего творчества в ПЛИС. Если оно не помещается, Vivado скажет, какие именно ресурсы вы превысили. Следующий этап – Generation Bitstream, когда будет создаваться двоичный образ матрицы PL. Как ни странно, на этом этапе тоже могут возникнуть ошибки. Не расслабляйтесь!

И наконец если все прошло благополучно, открывайте Hardware Manager и если вы не напутали с подключением Zedboard по usb, получите возможность программирования платы. Для этого ее перемычки должны стоять именно так, как у меня на фото.

Файлы проекта

Пробежимся по модулям.

debounce.vhd это антидребезговая схема, которая запускается от кнопки платы и формирует сброс камеры. В принципе такой же сброс будет обеспечен в момент включения платы – но мало ли что. В процессе сброса на камеру отсылаются установки ее регистров. Поддержка последовательного интерфейса с камерой обеспечивает модуль i2_sender.vhd, хранение и логику формирования регистровых посылок – модуль ov7670_registers.vhd. В последний я внес соответствующие изменения, чтобы камера формировала последовательность YUV вместо RGB, чтобы получить градации серого Y.

Синхронную работу этих двух модулей обеспечивает кросс-плата нижнего уровня – модуль ov7670_controller.vhd.

Когда камера запустится и начнет работать, про все эти модули можно забыть – в обработке они участие не принимают.

Теперь вернемся к началу цепочки. Прием байтовой последовательности с камеры – задача модуля ov7670_capture.vhd. Синхронизация его работы с разверткой камеры выполняется с помощью сигналов VSYNC, HREF. Поскольку во входном потоке каждый второй байт кодирует цвет, эти байты отбрасываются и в первый блок памяти записываются только значения интенсивности Y. Таким образом, после каждого кадра в BRAM содержится байтовая картинка grayscale размером 640х480 байт.

Блоки памяти, сохраняя промежуточную информацию, также обеспечивают выравнивание работы модулей по скорости.

Модуль – заглушка cv_core.vhd читает первый блок BRAM и заполняет этими же данными второй блок, оставляя от исходных байтов только старшие четыре разряда – больше VGA не позволяет. Вот тут для вас самый простор заместить ленивую заглушку своим алгоритмом: фильтр подавления шумов Гаусса, выделение контуров, распознавания по шаблону и все что душа пожелает. Мы же в этой статье как договорились делаем упор на технику реализации, поэтому пойдем дальше.

Собственно, осталось всего ничего – модуль vga.vhd читает второй блок BRAM, устанавливает прочитанными 4 битами все каналы цветности и выдает результат на разъем VGA. В результате мы видим что снимает камера на дисплее, в черно – белом варианте.

Скачать все исходники разом можно здесь.

На ролике видно, как все это работает вживую. Главный персонаж – моя кружка, освещение – комнатное. Базовую конфигурацию мы создали, следующий шаг – обработка изображения.

До встречи в эфире!

4 comments to Обработка потока с видеокамеры средствами ПЛИС/FPGA. OV7670 + Zedboard

  • Евгений

    Скоро все вычисления в “облако” перенесут. Какой-нибудь G8-модуль на борту БПЛА или “бездуховный” спутниковый OneWeb/Starlink, в самолетах будут распределенные вычисления навигации-полета-посадки на смартфонах пассажиров. Эк меня понесло 😉

    • Lao

      Кстати, интересная мысль – скрестить облако с простаивающими процессорными мощностями смартфонов )

      Ведь в самом деле, у подавляющего большинства домашних компьютеров и смартфонов нагрузка на CPU идет только в момент открытия документа или web-страницы. Вот сейчас набираю этот текст и вижу: загрузка CPU 1.8% в userspace, в системном режиме 0.8%. Жалко когда 90% с лишним ресурсов тупо простаивают! )

      В свое время компания Sun продвигала идеологию: тупой терминал – производительный сервер. Однако по мере гонки за процессорные скорости и удешевления компонентов, терминалы стали слишком умными. Есть приложения которые позволяют отдать свои вычислительные емкости в пользу решения определенных вычислительных задач, но плюсы облака не только в том что большое место для хранения и высокие скорости, а еще и в том что в нем живет разнородная информация, из которой можно много чего вытащить.

      Тут как раз пригодятся технологии Data Mining для поиска скрытых, заранее неизвестных закономерностей в потоке данных.

      • Евгений

        Были слухи, что кто-то на платежных терминалах биткоинов срубил (на пике цены), разные задачи от поиска внеземных цивилизаций до поиска лекарств решаются в распределенных системах (BOINC есть и для смартфонов). Банки хвастаются своими системами защиты от электронного воровства, но всяк умалчивают о слежке за клиентами на фоне доходов и расходов(т.е. доход большой, но что-то много платежей в мед. организации, надо понизить уровень доступа к кредиту и т.д.). А так, разный “мэшинг лернинг”, предикативный анализ – довольно таки модная тема. Хотя у нас в производстве пока надо просто попытаться собрать эту “биг дату”, масса информации/данных просто не пишется(очень настораживает тема ЭМС разных средств, хоть SDR-свистки на каждую частоту ставь), или пишется урезанно, если и пишется, то в закрытом формате производителя.

      • Lao

        Данных будет все больше и больше, выгодоприобретатели тоже понятны: продавцы всего и вся, банки, госструктуры, можно принимать какие-то решения, только нет ответа на главный вопрос: что в будущем? Нет функции хорошего прогноза. Черный лебедь караулит за углом )

Leave a Reply to Евгений Cancel reply

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">