Генерация под контролем: как обуздать мощные языковые модели

Intro

Если вы не проспали последние пару-тройку лет, то вы, конечно, слышали от трансформерах — архитектуре из каноничной Attention is all you need. Почему трансформеры так хороши? Например, они избегают рекуррентности, что дает им возможность эффективно создавать такое представление данных, в которое можно запихнуть очень много контекстной информации, что положительно сказывается на возможности генерации текстов и непревзойденной способности к transfer learning.

Трансформеры запустили лавину работ по language modelling — задаче, в которой модель подбирает следующее слово, учитывая вероятности предыдущих слов, то есть выучивая p(x), где x — текущий токен. Как можно догадаться, это задача совсем не требует разметки и потому в ней можно использовать огромные неаннотированные массивы текста. Уже обученная языковая модель может генерировать текст, да так хорошо, что авторы подчас отказываются выкладывать обученные модели.

Но что если мы хотим добавить немного “ручек” к генерации текста? Например, делать условную генерацию, задавая тему или контролируя другие атрибуты. Такая форма уже требует условной вероятности p(x|a), где a — это желаемый атрибут. Интересно? Поехали под кат!

Plug and Play Language Models: A Simple Approach to Controlled Text Generation

Авторы статьи предлагают простой (поэтому и Plug and Play) и изящный подход к условной генерации, использующий тяжелую pre-trained language model (далее LM) и несколько простых классификаторов, тем самым семплируя из распределения вида p(x|a) ∝ p(a|x)p(x). Надо заметить, что исходная LM никак не модифицируется. Авторы предлагают две формы классификаторов, называемых в статье attribute models: BoW для контроля темы и линейный классификатор для контроля тональности. Авторы делают достаточно подробный разбор своих key contributions, сравнивая идеи и подходы своего метода с другими статьями. Одним из самых важных пунктов является легкость подхода и тут, пожалуй, достаточно посмотреть на эту табличку:

Видно, что по количеству параметров PPLM выигрывает у всех конкурентов.

Weighted decoding 2.0

Товарищи из Uber развивают идею weighted decoding: на каждом шаге предсказания языковая модель благоволит к тем токенам, которые участвуют в нужной нам теме. Например, мы хотим генерировать тексты про науку, тогда на каждой выдаче моделью вероятностного распределения над токенами мы будем повышать вероятности токенов про науку. Такой подход не требует дополнительных параметров, однако нужно поддерживать набор определенных ключевых слов для каждой темы. Кроме этого, такой подход слишком жадный и не повышает вероятности слов, которые косвенно связаны с темой, но не имеются в нашем изначальном наборе.

Ребята из Uber предлагают бороться с этим следующим образом: давайте менять распределение раньше, в скрытых векторах LM, повышая вероятности тех же интересных нам токенов. При этом, будем надеяться, что раз мы заранее подогрели вероятности ключевых слов, то и вероятности косвенных токенов (не из нашего тематического набора, но той же темы) тоже будут побольше. Авторы идут еще дальше и буквально переписывают историю (в коде функция даже называется perturb_past — вышеописанные изменения производятся не только для текущего шага модели, но и для набора предыдущих.

Как же это работает? На каждом шаге авторы смещают значения выходов слоев с предыдущих шагов в направлении повышения суммы двух log-likelihood: оригинальной модели p(x) и атрибута a нашей attribute model p(a|x). Это очень похоже на обучение модели, мы тоже делает своеобразный backward pass по ошибке предсказания нужного токена.

Почему нам нужно максимизировать оба log-likelihood? Авторы делятся отличной картинкой, объясняющей интуицию:

Вкратце, мы хотим смещаться в сторону желаемых атрибутов, при этом оставаясь в вероятностном пространстве выдачи оригинальной LM. Оставаясь в таком пространстве, мы оставляем fluency модели примерно на уровне исходной LM.

Давайте повнимательней посмотрим на то, как сэмплируют нужные нам слова:

Сначала мы делаем forward pass через LM, чтобы оценить правдоподобие нужного нам атрибута с p(a|x) — нашей attribute model. Затем мы делаем backward pass, обновляя внутренние представления модели градиентами, полученными от attribute model, чтобы повысить правдоподобие токена, связанного с этим атрибутом. Наконец мы заново генерируем распределение над словарем, используя обновленные внутренние представления модели.

К сожалению, можно заметить что такой подход очень медленный: для обновления “прошлого” на глубину k нам нужно сделать k forward и backward pass’ов, каждый глубиной n. В то время как обычная LM делает всего один forward pass. И да, это действительно медленно: (попробуйте поставить num of iterations=3 и gen length=5, чтобы дождаться результата).

Впрочем, локально код (можно и в colab посмотреть, предварительно сделав копию ноутбука) работает пободрей, можно поиграть, например, для текста “the kitten” и тематики “military” можно получить такое:

  • The kitten is a creature with no real personality, it is just a pet. You can use it as a combat item.
  • The kitten that is now being called the "suspected killer" of a woman in a San Diego apartment complex was shot by another person who then shot him, according to authorities.

combat, shot, killer — слова, ассоциированные с тематикой military. Обычная LM выдаёт вполне невинные предложения:

  • The kitten that escaped a cage has been rescued from a cat sanctuary in Texas.
  • The cat, named "Lucky," was found wandering in the back yard of the Humane Society at the time of the incident on Friday.

Немного о attribute models

Авторы предлагают две модели, BoW и discriminator. В первом случае тема задается набором ключевых слов и мы хотим получить логарифм суммы правдоподобий каждого из этих слов:

где p_t+1 — распределение на выходе LM, а w_i — i-ое ключевое слово.
Discriminator model позволяет сделать более сложное определение атрибута, чем простой BoW, это будет актуально, например, для изменения тональности или токсичности, а может даже и стиля текста. В этом случае модель принимает на вход усредненные по нескольким шагам эмбеддинги и предсказывает класс, предварительно обучившись на размеченном наборе примеров.

Результаты

Авторы показывают отличные результаты, сравнивая свой подход с LM, LM с weighted decoding и недавней моделью CTRL (conditional LM). Сравнивали fluency и тематичность сгенерированных текстов, оценка проводилась как людьми, так и подсчетом perplexity модели. В таблице ниже показано сравнение тематичности вариаций PPLM и других подходов:

А здесь сравнение на задаче генерации текста заданной тональности:

  • B — baseline, GPT-2 LM;
  • BR — то же самое, что и B, но семплирование было r раз, а потом лучший сэмпл выбирался по ранжированию log-likelihood и фильтрации по скору дискриминатора;
  • BC — здесь уже применяется обновление скрытых представлений основной модели, семплирование проводится единожды;
  • BCR — то же самое, что и BC, но семплирование было r раз, а потом лучший сэмпл выбирался по ранжированию log-likelihood и фильтрации по скору дискриминатора;
  • CTRL — модель из статьи Keskar et al, 2019;
  • GPT2-FT-RL — GPT2, fine-tuned с RL на оценках от людей;
  • WD — weighted decoding, в котором выходы оптимизированы для максимизации p(a|x);

Резюме

Один из основных плюсов подхода — его легкость и универсальность, нет привязки к архитектуре LM, а подход с дискриминатором позволяет гибко задавать желаемые атрибуты. Основным минусом является скорость, возможно, с методами какого-то более хитрого обновления слоёв это будет работать быстрей 🙂

ссылка на оригинал статьи https://habr.com/ru/post/499140/

Разработка прошивки для аналоговой видеокамеры EVR-Y2022F

В предыдущей статье было очень подробно рассмотрено устройство аналоговой видеокамеры с целью создания собственной прошивки. Как уже было сказано, камера имеет микроконтроллер неизвестного происхождения. Он гораздо богаче, чем привычные мне AVR: у него два напряжения питания 3.3В и 1.8В, а также, у него есть функция DSP. К такому выводу я пришёл, когда задумался про реализацию алгоритма автофокусировки. Тем не менее, я не предпочитал сложные МК типа STM32 и прочие, хотя бы потому, что я с ними вообще никогда не работал. Мной однозначно было принято решение, что для реализации своей прошивки я буду применять один из МК AVR. Поэтому уже на этом этапе я начал осознавать, что с реализацией функции автофокусировки будет не очень легко справиться, а точнее – невозможно.

Мой выбор пал на МК ATmega128, так как именно он попался мне под руку. МК ATmega8 будет явно недостаточно по числу выводов, тем более что на всякий случай я решил зарезервировать целый порт МК для входа цифрового потока видео от видеопроцессора. Первым делом я прикинул, какие будут функции в собственной прошивке, в частности, функции, которые отсутствовали в оригинальной прошивке, и какими функциями придётся пренебречь.

Рассмотрим вариант алгоритма автофокусировки по анализу цифрового видеопотока. Мне удалось узнать, что данные видеопотока представляют собой чередование байтов, синхронизированные с импульсами «CK». Байты видеопотока кодируют уровни компонентов Y, Cr, Cb видеосигнала с 8-разрядной градацией (256 уровней). То есть, цифровой видеовыход с видеопроцессора данной камеры является компонентно-мультиплексированным. Информация о яркости (Y) содержится в каждом втором байте видеопотока, а информация о цветности – в два раза реже. То есть, информация о цветоразностном сигнале красного Cr содержится в каждом четвёртом байте, как и информация о цветоразностном сигнале синего. Таким образом, поток представляет такую последовательность: Cb0, Y0, Cr0, Y1, Cb2, Y2, Cr2, Y3, Cb4, Y4, Cr4, Y5,…. То есть, в то время как информация о яркости каждого пикселя приходит без пропусков, информация о цветности пикселей приходит покомпонентно по очереди. Данное прореживание обусловлено свойствами нечувствительности зрения к цвету мелких деталей и сокращением полосы цветности в видеосигнале. Эти свойства применяются в аналоговом телевидении и оцифровке видео. Представленная выше «компрессия» (цветовая субдискретизация) имеет компонентное соотношение 4:2:2.

Для работы алгоритма автофокусировки достаточно анализировать только составляющую яркости, чего также легко добиться, перехватывая видеопоток «байт через байт». Если частота CK составляет около 18 мГц, то CK/2 – 9 мГц, что, казалось бы, вполне достижимо для МК ATmega128. Импульсы синхронизации по горизонтали и вертикали дают возможность контроллеру «отсчитать» и анализировать любую область изображения. Возможно, для алгоритма автофокуса достаточно анализировать только центр растра. Очевидно, что чем лучше фокусировка, тем чётче изображение, а значит – тем шире частотная полоса видеосигнала (больше ВЧ составляющих). То есть, можно (даже нужно) применить алгоритм быстрого преобразования Фурье (БПФ) к фрагментам цифрового видеопотока и анализировать ВЧ составляющие. При этом нужно каждый раз подкручивать ШД фокуса, применяя метод «половинного деления», как математический метод оптимизации. Тем самым можно добиться оптимального результата.

Я не стал заморачиваться функцией автофокуса, посчитав, что это невозможно на МК с простой архитектурой, хотя, на всякой случай, порт для цифрового видео зарезервировал. Вместо автофокусировки я решил реализовать ряд других функций, которые отсутствовали в оригинальной прошивке. Но для этого видеокамеру придётся ограничить стационарными условиями, что и характерно для видеонаблюдения. Предполагается, что камера в дальнейшем будет способна поворачиваться в горизонтальной и вертикальной плоскости с помощью специальных механизмов, как автоматически, так и вручную. При автоматическом наведении камеры на определённый объект, сферические координаты которого будут заранее занесены в память устройства управления, также будут меняться «координаты» зума и фокуса, которые также будут заранее выбраны и занесены в память. Управление можно организовать по протоколу PELCO-D, тем более, в спецификации данного протокола есть специальная команда под это дело. Координаты зума и фокуса, естественно, будут «парные», для определённого конкретного расстояния. То есть, объект, который будет располагаться на данном расстоянии, будет в фокусе.

Прежде чем начать писать программу прошивки, нужно подумать, какая периферия МК и какие выводы будут задействованы. Затем надо подумать, как поместить и закрепить плату с собственным МК внутри камеры. И чтобы это всё было максимально удобно и ремонтопригодно. Я решил использовать плату с МК, выводы которой будут полностью разведены на левую и правую сторону. Плата будет располагаться на дне камеры, где есть немного места, и будет держаться на разъёмном соединении. При этом на самой плате будут «штырьки» разъёма, а на боковых сторонах камеры будут ответные гнёзда. Для ответных гнёзд я решил изготовить ещё две переходные платы, размером с боковую сторону камеры. Ламели данных плат выходят к верхней стороне камеры, прямо к основной плате. Предполагается, что каждая ламель будет проводком соединяться с нужной точкой основной платы камеры.

Во время разработки платы с МК у меня возникла идея дополнить видеокамеру часам (RTC), и я выделил линию I2C, разместил на плате RTC DS1307 (уже знаю, что хрень) с кварцем и батарейкой и, на всякий случай EEPROM 24AA512, что были под рукой. Также на плате у верхнего края разместились разъёмы для подключения SPI и JTAG программаторов. На оригинальной плате МК тактируется от кварца на 12 мГц. У меня – аналогично. Вообще, лучше туда поставить кварц на 11.0592 мГц для чёткой работы UART. Расстояния между «гребёнками» разъёмных соединений я предварительно тщательно рассчитал. Питать МК я решил от пятивольтовой «Кренки», что будет прикручена на каркас под основной платой (он заодно будет служить теплоотводом). Питание на неё будет браться от входного напряжения 12В сразу после предохранителя FB801, как показано на рисунке.


Рис. 1. Организация питания микроконтроллера.

Во время рисования плат в «SprintLayout» я прикидывал назначение каждого вывода МК, который выводится «наружу» на разъём. В результате получился вот такой рисунок.


Рис. 2. Эскизы дополнительных печатных плат.

Говоря по правде, боковые платы (по краям) я нарисовал на этапе написания этой статьи. А на самом деле я их делал с помощью резака. Получился не самый удачный вариант. Да и сама плата с МК была изготовлена кривовато. Боковые вспомогательные платы я кое-как прикрутил к боковым сторонам каркаса камеры, припаяв флюсом гайки к медной поверхности текстолита. Дело в том, что места по бокам очень мало, и крышка камеры практически «натягивается» впритык.

На рисунках ниже приводится схема распределения выводов МК, а также их назначение.


Рис. 3. Назначения выводов микроконтроллера.

Следует прокомментировать множество моментов.

Для SPI программатора (STK200+) вывод «PEN» оказался ненужным. Активация происходит по «RESET», как обычно. Но вместо «MISO» и «MOSI» у МК выделен отдельный интерфейс (PDI/PDO), а линия «CLK» совмещена.

В качестве опорного напряжения для АЦП я выбрал те же 5В, от чего и питается сам МК. Пробовал завести отдельно 3.3В (как на оригинальной схеме), но при этом были свои подводные камни. А для того, чтобы перейти на опорное напряжение 5В, нужно немножко изменить схему, как показано на рисунке.


Рис. 4. Перевод кнопок на опорное напряжение 5В.

То есть, нужно отпаять одну сторону резистора R505 со стороны питания 3.3В, и вместо этого подать на него 5В от линии питания МК.

Из выводов МК, которые используются только внутри спроектированной платы, задействовано только три. На вход PB7 приходит сигнал импульсов 1 Гц с RTC для обновления времени. Выводы PD0 и PD1 отведены под шину I2C. Она будет реализовываться программно с помощью библиотеки CVAVR «i2c.h», несмотря на то, что к этим выводам привязан аппаратный i2c (TWI) интерфейс.

Вывод «RESET» МК выведен наружу, но сброс МК будет происходить самостоятельно без внешней цепочки сброса.

Подразумевалось, что синхроимпульсы HD и VD будут приходить на МК по портам внешнего прерывания для точности отсчёта поля видеоизображения. Однако, исключая функцию автофокусировки, в них уже нет потребности. На соседние порты внешних прерываний приходят сигналы с концевиков зума и фокуса.

Для цифрового видеопотока зарезервирован порт «A» МК. Порт «C» полностью отведён под ШД.

Вывод PD4 используется для коммутации TX/RX RS-485. В оригинальной схеме никакой коммутации не было: 2-ая и 3-я ноги микросхемы MAX485 сидели на «земле». Оригинальный МК умел только принимать данные для управления камерой по PELCO-D. Я же задумал сделать небольшую модернизацию. Идея была такова. В случае если видеокамера будет висеть высоко и в закрытом кожухе, будет просто невозможно оперативно обновить прошивку. А такая потребность обязательно возникнет: исключение различных багов и улучшение функционала станет на первое время регулярной практикой. Поэтому я придумал реализовать для МК загрузчик, и уже с помощью него удалённо обновлять прошивку по RS-485. А в таком случае двухсторонний обмен очень даже желателен. Про загрузчик будет отдельная часть данной статьи. А для того, чтобы подключить MAX485 (2 и 3 ноги) к данному выводу МК, нужно внести небольшое изменение в первую и вторую плату. Эти платы соединены шлейфом, на разъёмах которого имеется неиспользуемый контакт «IRL» управления подсветкой. На второй (основной) плате нужно отпаять резистор R520 и припаять вместо него на сторону разъёма провод, который пойдёт через переходную плату к МК ATmega128 на соответствующий вывод. А на первой плате нужно отпаять и отогнуть вверх 2 и 3 ножки U202, спаять их вместе и проводком от них дотянуть до свободного вывода 1 разъёма J302. Данные операции по изменению схемы показаны на рисунке.


Рис. 5. Организация линии TRX для управления TX/RX MAX485.

На борту ATmega128 имеется два UART интерфейса. В данном случае приходится задействовать второй интерфейс (выводы 27, 28), так как первый интерфейс по выводам (выводы 2, 3) совмещён с интерфейсом для SPI программатора.

На плате задействованы почти все выводы МК. Незадействованными оказались выводы порта «G». Кстати, можно было реализовать часы программно на базе МК. Он предусматривает спящий экономичный режим с применением батарейки для отсчёта времени при отключенном основном питании. Имеются даже выводы для подключения отдельного НЧ кварца. Однако я не стал с этим заморачиваться, влепив DS1307.

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


Рис. 6. Назначения выводов дополнительной платы с МК.

Сигналы с концевиков зума и фокуса поступают не только на порты прерываний, но и на входы АЦП. Дело в том, что ещё на этапе исследования ШД я заметил такую особенность. В случае если механизм зума или фокуса находится на «нуле», то выходной сигнал с концевика может принять «промежуточное» состояние. Для более чёткого улавливания таких редких случаев я решил использовать АЦП. Конечно, это не совсем грамотный подход, но тем самым я быстро выкрутился из проблемы, которая иногда возникала на этапе инициализации в моей тестовой прошивке. Тем самым я повысил устойчивость работы алгоритма.

Сигналы SDA/SCL от I2C выведены на разъём просто так, на всякий случай, и они не используются за пределами данной платы.

Названия каждого вывода для управления ШД подписаны согласно фактическому подключению. Данные подключения, забегая вперёд, окончательно корректировались на этапе отладки. Было много путаницы, но ошибки были лишь только в чередовании фаз с точностью до реверса, а не в их упорядоченности. Чередования «4-1-2-3» (для зума) и «2-3-4-1» (для фокуса) – это одно и то же, равно, как и «1-2-3-4», что и бралось за основу.

В конце статьи (чтобы не позориться) представлены две фотографии. На первой — вид видеокамеры внутри снизу с видом на дополнительную печатную плату. На второй — вид внутри сверху на основную плату видеокамеры с отпаянным штатным МК (микропроцессором, если быть точнее), кучей проволочных перемычек и прочих соплей.

Написание программы (прошивки) я производил совместно с её предварительным тестированием в программе «ISIS 7 Professional» (Proteus).


Рис. 7. Вид проекта в Proteus.

Вместо уникальных чипов знакогенератора и видеопроцессора (которых, разумеется, нет в Proteus) я поставил SPI отладчики. С помощью них удобно контролировать байты, которые отправляет МК по SPI. А вот реальная реакция на эти байты контролируется уже непосредственно на «железе». С помощью Proteus можно контролировать и отлаживать команды PELCO-D, приходящие с реального DVR. Для этого, как вариант, нужно через простейший односторонний переходник RS485->RS232 подключить DVR к COM порту компьютера.

Затем я приступил к разработке и моделированию. Очень сильно при этом помогала программа Excel.

Сначала необходимо определиться с таймерами и их конфигурацией. Один таймер – для реализации вращения ШД и реализации повторных срабатываний кнопок при их удерживании. При удерживании той или иной кнопки во время настроек через меню будет исключена работа ШД. А при удерживании одной из кнопок управления зумом или фокусом вне меню происходит вращение ШД с соответствующим временным параметром. Тем самым, какие-либо коллизии отсутствуют. Второй таймер я планировал использовать для реализации ШИМ для ШД, но со временем я решил от неё отказаться. Действительно, в моём случае, когда отсутствует автофокус, нет необходимости в ШИМ. Тем более, передаточный механизм имеет винтовую структуру, поэтому в состоянии покоя можно не «удерживать» ШД постоянным током, механизм никуда не уползёт.

Затем нужно произвести ревизию алфавита символов знакогенератора, согласно даташиту, и сопоставить его со стандартным алфавитом ASCII. Алфавит знакогенератора состоит из 128 символов, что вдвое меньше последнего. Например, символы кириллицы в знакогенераторе напрочь отсутствуют, но есть специальные символы, характерные области его применения (солнце, песочные часы, человечек, нота, телефон и т.д.). Я составил массив «smb[256]» из 256 элементов, поместив его в EEPROM МК. При этом запись smb[i]=adr обозначает, что по адресу adr в знакогенераторе находится символ с ASCII кодом i. А если символ i отсутствует в алфавите знакогенератора, то значение элемента массива ссылается на «пробел» с адресом 0x7E. То есть, почти половина элементов массива имеет значение «0x7E». Данный массив в табличном виде представлен на рисунке ниже.


Рис. 8. Массив сопоставления символов ASCII с кодами для PD6464A.

Далее, нужно продумать, как обрабатывать кнопки через АЦП. По закону Ома несложно рассчитать значения напряжений на входе АЦП при нажатии на ту или иную кнопку. После этого несложно рассчитать границы интервалов, серединами которых будут те самые значения напряжений. Всего получается шесть интервалов: пять из них соответствуют каждой кнопке и один – отсутствию нажатия (ни одна кнопка не нажата). АЦП МК на аппаратном уровне периодически анализирует значение напряжения с кнопок. Таймер для антидребезга можно реализовать на основе подсчёта тактов АЦП, что я и сделал. На этапе отладки этой части программы были свои подводные камни. Думаю, что подробности писать не стоит. Чтобы добиться чёткой работы этого функционала, пришлось долго повозиться. Функция распознавания кнопки помещена в секцию прерывания АЦП, а на её выходе – номер кнопки, флаг нажатия и флаг отпускания. Дальнейшая обработка кнопок происходит в основном цикле программы. Частота опроса кнопок (частота АЦП) получилась 12000/128=93.75 (кГц), где 128 – максимально возможный делитель.

Потом я составил массив значений регистра UBRR1 конфигурации UART в зависимости от того или иного значения бодрейта для PELCO-D, которое можно будет выбирать из списка в настройках через меню. Эти значения можно рассчитать по формуле из даташита на МК, а также можно получить с помощью автоконфигуратора «AVR Wizard».

Затем я приступил к моделированию меню. Это основной и трудоёмкий этап написания программы. Повторять меню штатной прошивки я в принципе не собирался, кроме этого, решил усложнить его до иерархической структуры (раздел в разделе). Ниже приведу описание модели и определений меню, которые я для себя составил.

Можно не читать

Меню состоит из нескольких отдельных страниц.
Каждая страница имеет имя и состоит из заголовка и списка элементов.
На каждой странице вверху отображается её заголовок.
Заголовок страницы совпадает с её именем и окружён определёнными своеобразными символами.
Корневая страница меню имеет имя и заголовок «MENU».

При нажатии на кнопку «М» вызывается корневая страница меню.
При нажатии на кнопки «ВВЕРХ» и «ВНИЗ» осуществляется выбор элемента из списка текущей страницы.
При нажатии «ВВЕРХ» на первом элементе будет выбран последний элемент.
При нажатии «ВНИЗ» на последнем элементе будет выбран первый элемент.
По умолчанию (при открытии новой страницы) выбран первый элемент, если эта страница была вызвана активацией соответствующей ей директории.
Символом выбора служит «стрелочка вправо», стоящий слева от имени элемента.

В роли элементов выступают:
— директории;
— радиокнопки;
— чекбоксы;
— числа.

Директории служат для группировки элементов, включая сами директории, в иерархическое дерево.
Имя директории окружено скобками <>.
Каждой директории соответствует определённая страница меню.
Первым элементом любой страницы всегда является директория с именем "<..>".
Активация выбранной директории осуществляется нажатием кнопки «М».
При активации директории отображается новая страница меню, соответствующая активированной директории.
Имя новой страницы меню совпадает с именем активированной (соответствующей ей) директории.
Активация директории с именем "<..>" приводит к отображению предыдущей страницы меню.
После выполнения вышесказанной операции выбранным элементом будет являться директория, которой соответствовала предыдущая страница.
Активация данной директории на корневой странице меню приводит к выходу из меню.

Радиокнопками называются элементы особого списка переключаемых параметров.
В данном списке отсутствуют иные элементы.
В данном списке может быть активирован только один элемент.
Перед именем активной радиокнопки приписывается "<•>" (точка посередине).
Перед именем неактивной радиокнопки приписывается "< >" (пробел).
Активация выбранной директории осуществляется нажатием кнопки «М».

Чекбоксами называются элементы двоичных параметров.
Данные элементы могут также содержаться наравне с директориями в пределах одной страницы.
Перед именем активного чекбокса приписывается «l*l» (крестик, окружённый буквой «l»).
Перед именем неактивного чекбокса приписывается «l l» (пробел, окружённый буквой «l»).
Активация или деактивация выбранного чекбокса осуществляется нажатием кнопки «М».

Числами называются элементы, каждый из которых представляет собой числовой параметр.
Данный элемент не может быть активирован.
Данные элементы могут также содержаться наравне с директориями в пределах одной страницы.
В состав имени данного элемента входят название параметра и его целое числовое значение.
Формат целого числового значения может быть трёх видов: «000», «00», «0».
Формат выбирается в зависимости от параметра из соображения удобства.
Название и значение параметра отделены между собой двоеточием и пробелами: ": ".
Количество пробелов — от одного до максимально допустимого (до выравнивания по правому краю) в зависимости от ситуации из соображения удобства.
Значение параметра, соответствующее выбранному числу, можно изменить нажатием кнопок «ВЛЕВО» или «ВПРАВО».
При нажатии на кнопку «ВЛЕВО» значение параметра уменьшается на 1.
При нажатии на кнопку «ВПРАВО» значение параметра увеличивается на 1.

Значения всех элементов записываются в ПЗУ при выходе из меню.
Реакция на изменение значений элементов осуществляется непосредственно при их изменении.

Таким образом, навигация по меню чем-то напоминает навигацию по файлам и папкам через «Total Commander». Код реализации данной модели меню не совсем сложный, но очень громоздкий. Имеются две ключевые переменные: номер активной страницы и номер активной позиции на странице. По обеим переменным работают две вложенные друг в друга функции «switch-case». Данная пара функций участвует в обработках нажатий кнопок «Влево», «Вправо» и «Меню». В каждом месте (для каждой кнопки, страницы и текущего пункта) прописаны те или иные определённые действия. На каждую страницу меню отведена функция, которая реализует отображение страницы на экран со всеми надписями и параметрами. Прежде чем реализовывать функции вывода страниц, я предварительно смоделировал их в Excel, что называется, «по клеточкам». Так нагляднее представляются координаты ячеек каждого символа на экранном поле, а эта информация необходима на этапе программирования. На рисунке ниже в качестве примера я привёл вид страницы 9, на которой из списка выбирается бодрейт PELCO-D. Элемент интерфейса на странице – радиокнопка. Кроме неё, первый пункт <..> – для выхода из этого раздела.


Рис. 9. Моделирование экранного меню в Excel.

Также я составил массив, который отражает количество пунктов на каждой странице. Он используется в обработке нажатия кнопок «Вверх» и «Вниз». Это сделано для сокращения кода и избегания использования функции «switch-case»

Повторные виртуальные нажатия при удерживании кнопок реализованы в теле прерывания таймера функцией «switch-case», работающей на целочисленную переменную, являющейся флагом. Значение флага уникально для каждого действия той или иной кнопки на определённой странице и определённого пункта меню. Оно присваивается флагу, как порядковый номер, только в тех местах, где нужны виртуальные нажатия. При этом внутри функции «switch-case» (в теле прерывания таймера) помещены копии функций, реализующие действия кнопок. Можно было для экономии памяти поместить «ярлыки» (ссылки) на очередные вызовы функции обработки кнопок. Так даже разумнее, но на тот момент у меня не хватило терпения подумать, как это лучше сделать, ибо желал поскорее закончить с проектом. Да и памяти в ATmega128 оказалось достаточно много.

Наконец, мной были реализованы «библиотеки» для работы с видеопроцессором, знакогенератором и RTC DS1307 с необходимыми функциями. После этого я определил адреса EEPROM МК для хранения той или иной информации. Первые 32 байта отведены для хранения информации настроек меню. Следующие 32 байта отведены под хранение текста, который можно будет выводить на экран или менять с помощью стандартной для PELCO-D команды «Write Char. To Screen». Следующие 256 байт области EEPROM отведены под алфавит (преобразование символа из ASCII в адрес для знакогенератора, о чём говорилось выше). Наконец, следующие 128 байт отведены под хранение «пресетов» (шаблонов) зума/фокуса. Эту функциональную возможность я ввёл ввиду отсутствия автофокусировки. Об этом я писал в начале статьи. Всего отведено 32 шаблона. Координаты зума или фокуса кодируются двумя байтами.

Отдельно стоит написать про реализацию управления ШД. Вращение ШД достигается путём вызова функций StepF() и StepZ() в блоке прерывания таймера. Скорость вращения определяется конфигурацией этого таймера. А выполнение вышесказанных функций реализуют продвижение фокуса или зума (соответственно) на минимальный шаг. При вращении ШД зума и фокуса контролируются их конечные положения. Положение максимума фокуса и положение минимума зума представлены в программе константами (280 и -600 соответственно). А вот положение минимума фокуса и положение максимума зума – в виде переменных F_min и Z_max (точнее, функций). Такому подходу поспособствовала непрямоугольная рабочая область со срезанным нижним правым углом. Для расчёта значений F_min и Z_max применяются кусочно-заданные функции F_min(Z) и Z_max(F). Кроме того, при вращении ШД зума в положительном направлении при Z (координата зума) >500 происходит одновременное вращение ШД фокуса в том же направлении, если последний имеет координаты <(-180). То есть, максимальное положение зума в принципе не ограничивается текущим положением фокуса, а ограничивается числом 600. Просто-напросто происходит вращение двух ШД одновременно при достижении соответствующей угловой границы пятиугольной области, и движение на этом этапе происходит вдоль «стороны среза» (если интерпретировать графически). С точки зрения механики это эквивалентно описанному в предыдущей статье процессу, когда при отсутствии ШД и при перемещении узлов зума и фокуса вручную, узел зума а конце траектории «тянет за собой» узел фокуса. Ввиду того, что координата зума доминирует над координатой фокуса (именно поэтому я рассматриваю зависимость F(Z), а не наоборот), я не стал реализовывать аналогичную процедуру «прокрутки» зума в функции Step_F().

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

Функция инициализации ШД init_MR() необходима для привязки механики зума и фокуса к системе координат. Она выполняется один раз при каждом включении видеокамеры. Алгоритм её работы примерно следующий. Первым делом предполагается, что зум или фокус находятся в нулевой точке, и происходит попытка поймать дребезг сигнала с концевиков функциями внешнего прерывания. Сразу отмечу, что, если зум или фокус находятся «в нуле» (на границе перегораживания оптического концевика), то сигнал на выходе концевика имеет «промежуточное» состояние между логическими «0» и «1». Такие случаи очень маловероятны, но исключать их нельзя. При этом функция прерывания не интерпретирует такой сигнал, как дребезг. Именно для этого я пришёл к использованию АЦП МК, заводя на два его свободных канала сигналы с концевиков зума и фокуса. И так, первым делом происходит «оцифровка» сигналов с концевиков с 8-битной точностью. Это осуществляется с помощью однократного аналогово-цифрового преобразования. Стоит помнить, что опорное напряжение в нашем случае составляет 5В, а уровень логической «1» с концевика – 3.3В. Для логического «0» значение АЦП будет нулевым, а для «1» – 3.3/5*255=168. Если значение сигнала с того или иного концевика попало в диапазон, скажем, от 2 до 165 (берётся нечёткий интервал), то это означает, что соответствующий узел уже находится «в нуле», и процедуру инициализации для этого узла можно прекратить. В противном случае по логическому значению сигнала концевика («0» или «1») нужно определить, в какой части (половине) находится узел. От этого будет зависеть направление вращения ШД. Так или иначе, ШД нужно вращать в таком направлении, чтобы соответствующий узел передвигался в сторону «нуля» (концевика). Таким образом, запускается вращение ШД с одновременным подсчётом числа шагов до тех пор, пока не будет достигнут концевик. Как только соответствующий концевик будет достигнут, что определит функция внешнего прерывания по перепаду логического уровня, произойдёт реверс вращения ШД. Он совершит вращение в обратную сторону на то же число шагов, тем самым, возвратившись в исходное положение. Значение числа шагов для каждого ШД с соответствующим знаком будет скопировано в соответствующие переменные перед выходом из функции инициализации. Описанная выше процедура происходит независимо для фокуса и зума в пределах одной функции (не по очереди). Скорость вращения ШД на этапе инициализации определена отдельной константой и соответствует максимальной скорости для уверенного корректного вращения ШД.

Рассмотрим пример, когда перед включением питания камеры зум находился в отрицательной области, а фокус – в положительной. На рисунке схематически показана траектория перемещения точки (Z;F) при процедуре инициализации ШД.


Рис. 10. Процесс инициализации зума и фокуса.

Точка A – начальное положение зума и фокуса. Движение обоих узлов происходит в сторону «нуля» с одинаковой скоростью (скоростью инициализации). В точке B происходит достижение «нуля» узлом фокуса, так как он находился ближе к нулю, чем зум. Затем происходит реверс фокуса. В точке C фокус завершает процесс инициализации, возвратившись на исходное положение. При этом зум всё ещё движется в сторону своего «нуля». В точке D он достигает свой «нуль» и возвращается на исходное положение (точка A).

Кроме функции инициализации init_MR() имеется функция goto_zf(z,f). Исходя из названия, она предназначена для перехода с одного пресета на другой, о чём я писал в начале статьи. Скорость вращения ШД при переходе такая же, как и при инициализации. Процесс перехода по зуму и фокусу осуществляется одновременно. То есть, если требуется из точки (z1;f1) перейти в точку (z2;f2), запускается одновременное вращение двух ШД. Если, например, |f2-f1|<|z2-z1|, то ШД фокуса остановится раньше. Это продемонстрировано на рисунке ниже.


Рис. 11. Процесс смены зума и фокуса при выборе пресета.

На протяжении всего времени работы ШД при прохождении концевика нулевой метки происходит обнуление соответствующей координаты. И это несмотря на то, что теоретически этого можно не делать. Однако на практике всё же существует погрешность в 1-2 шага ШД.

Стоит дополнить, что, в отличие от оригинальной прошивки, в своём случае при управлении зумом и фокусом (как с кнопок, так и через PELCO-D) я предусмотрел возможность пошагового передвижения. Это работает следующим образом. При нажатии на какую-либо одну из 4-х кнопок управления зумом или фокусом соответствующий ШД проворачивается на один шаг, тем самым происходит минимальное передвижение узла зума или фокуса. Если при этом не отпускать кнопку, то обычное вращение ШД начнётся спустя небольшой промежуток времени. Данная задержка подбирается опытным путём заранее. Эта особенность аналогична виртуальным повторным нажатиям при удержании кнопки. Благодаря этой особенности устраняется проблема «залипания» кнопки при управлении зумом или фокусом по PELCO-D удалённым устройством через плохое Интернет соединение. Точнее, появляется возможность как грубой, так и точной подстройки зума или фокуса.

Интерпретатор PELCO-D команд выполнен по той же аналогии, что и в устройстве для коммутации нагрузок через PTZ. Этому простому устройству я ранее посвятил отдельную небольшую статью на Хабре. В отличие от оригинальной прошивки, команды управления зумом и фокусом полностью ссылаются на нажатия соответствующих кнопок. То есть, имеется возможность с помощью кнопок зума и фокуса PELCO-D «лазить» по меню. А для того, чтобы удалённо через PELCO-D вызывать меню, точнее, нажимать кнопку «MENU», я сопоставил с ней кнопку открытия диафрагмы, ибо данная функция не применяется в данной модели камеры. Таким образом, действуют пять базовых команд PELCO-D для нажатия, а также пять базовых команд – для отпускания кнопок. Кроме этого, как я уже писал вскользь на протяжении всей статьи, обрабатываются дополнительные команды: «Set Preset», «Clear Preset», «Go To Preset», «Write Char. To Screen», «Clear Screen», «Set Zoom Speed», «Set Focus Speed».

Дата и время с RTC выводятся в нижнем левом углу изображения по аналогии со старыми VHS камерами, если данная опция активирована в меню. Кроме того, в меню можно выбрать формат вывода, что я также заранее предусмотрел. Также на экран рядом с датой и временем имеется возможность выводить день недели. Кроме часов, в качестве дополнительной информации на экран выводятся текущие координаты зума и фокуса. Эту опция нужна, прежде всего, на этапе отладки.

Расскажу про функции меню, которое я реализовал. Со временем, по необходимости, меню будет подвергнуто редакции: какие-то функции можно будет убрать, а какие-то добавить. Структура меню, которую я нарисовал в SPlan, с видами страниц представлена на рисунке ниже. Красные стрелки – вход в раздел. Синие стрелки – выход из раздела. Я не стал рисовать синие стрелки на каждую страницу меню, нарисовал всего две для примера.


Рис. 12. Структура экранного меню.

Некоторые разделы моего меню немножко похожи на разделы оригинала. Прежде всего, это первые два раздела: баланс белого и экспозиция. В третьем разделе можно указать адрес камеры PELCO-D и выбрать скорость обмена данными (бодрейт) из списка. Четвёртый раздел посвящён дате и времени. Можно настроить дату, время, день недели, выбрать один из четырёх форматов отображения, выбрать способ отображения. Пятый раздел – работа с пресетами (шаблонами) зума и фокуса, где можно вызвать его по номеру, а также стереть или перезаписать. Также в данном разделе меню можно выбрать один из четырёх скоростей смены фокуса или зума. Пятый раздел позволяет редактировать параметры видеопроцессора, находящиеся в байте 9 категории 3. Это уровень и инверсия составляющей «burst» видеосигнала, и зеркальное отображение видео. Последний раздел меню предназначен для отладки. С помощью него можно записывать в видеопроцессор любое значение любого байта в любой категории. Значение можно задавать как в десятичном, так и в бинарном виде.

Теперь скажу несколько слов по поводу загрузчика. Как я уже писал, загрузчик нужен для удалённой перепрошивки видеокамеры по RS-485. Изначально я думал полностью реализовать загрузчик самостоятельно. Однако в целях экономии времени я решил обойтись одним из уже реализованных готовых загрузчиков, которые можно найти в Интернете. Тем более, я ни разу ими не пользовался, имея представление о них только на теоретическом уровне. Одним из важных критериев выбора загрузчика – поддержка RS-485. Обычно AVR загрузчики работают по UART RS-232. А загрузчик с поддержкой RS-485 отличается лишь тем, что на стороне МК выделяется дополнительный вывод для коммутации приёмо-передатчика RS-485 (например, MAX485) во время передачи данных от МК к ПК. При прошивке МК загрузчик передаёт компьютеру информацию об успешной или неуспешной записи. Первый загрузчик, который я нашёл, позволяет записывать не только FLASH память МК с прошивкой, но и EEPROM. Кроме записи, можно ещё и считывать данные. Но данный проект с исходным кодом на ассемблере был довольно запутанный и я в нём не разбирался. Тем более, основной акцент данного загрузчика был направлен на возможность прошивать множество устройств по отдельности, не отключая их от сети RS-485, обращаясь к каждому устройству по заранее зашитому в него адресу. В таких функциональных особенностях я не нуждаюсь, так как применяю иную топологию сети RS-485, и есть возможность быстро переключить видеокамеру с DVR на ПК. Второй загрузчик – немецкий «Chip45». Исходный код находится не в свободном доступе, его можно купить у автора. Вместо этого имеется несколько сотен HEX-файлов под разные AVR МК, разные интерфейсы UART (если их несколько, как в моём случае), RS-485 или RS-232 на выбор. Короче, на все случаи жизни. При этом автор отмечает, что в случае с RS-485 вывод для коммутации TX/RX фиксированный и соответствует пину XCK UART интерфейса контроллера, который практически в UART не применяется. В моём случае на 30-ый вывод XCK второго UART интерфейса МК Atmega128 приходится PORTD.5 и используется для активации концевиков зума и фокуса. В принципе, эта функция не нужна, ибо, как показали исследования, концевики всегда активны, о чём я уже писал. Да и при необходимости можно перебросить эту функцию на любой другой свободный вывод МК. Но, всё равно, данный загрузчик меня также не впечатлил, тем более, мне попался более интересный загрузчик под названием «AVR Universal Bootloader» китайской разработки. Как и Chip45, он умеет только записывать и только во FLASH память МК. Но у него большое количество возможностей, и поэтому я твёрдо решил остановиться на нём. Поставляется он как проект AVR Studio с исходным кодом на языке Си. В связи с тем, что я работаю в CodeVisionAVR, мне пришлось установить AVR Studio совместно с WinAVR. Для того чтобы получить HEX-файл прошивки загрузчика, нужно откомпилировать проект, сделав предварительные изменения в исходном коде под собственную конфигурацию устройства и собственные нужны. Компиляция проекта заключается в запуске bat-файла (батника), в котором прописаны команды компиляции. Таким образом, проект в AVR Studio открывать не требуется. Изменения в исходном коде можно делать как вручную (на уровне программиста), так и с помощью конфигуратора. В роли последнего выступает дополнительное окно утилиты, работающей с загрузчиком, которая также прилагается. В конфигураторе можно указать вывод МК для коммутации TX/RX RS-485, вывод МК для контрольного мигающего светодиода, вывод МК для входа в загрузчик, способ входа в загрузчик, название и частоту МК и т.д., всё не перечислить. Кроме того, в роли утилиты для загрузки пользовательской программы в МК, т.е., для работы с загрузчиком, может выступать стандартная известная программа «HyperTerminal» Для загрузки прошивки она использует протокол «Xmodem». А для того, чтобы через текстовый терминал было удобно и наглядно работать с загрузчиком, в конфигураторе предусмотрена специальная функция «Verbose mode». Но я, несмотря на привлекательность гипертерминала, решил пользоваться утилитой, прилагающейся к загрузчику. Дело в том, что с активированной в конфигураторе удобной функцией Verbose, работая через терминал, я столкнулся со следующей ситуацией. Иногда приходилось, когда трафик данных в линии «сталкивался» (оба устройства в режиме TX), вследствие чего MAX485 в видеокамере сильно нагревалась и выходила из строя, точнее, не полностью, а только секция RX (передача данных по RS-485 в камеру). Из-за этого я отказался от HyperTerminal. И есть ещё одно неудобство. HyperTerminal не работает с текстовыми HEX-файлами и принимает только бинарный файл. Поэтому пришлось бы применять дополнительное преобразование из hex в bin. После того, как HEX-файл загрузчика был мной сформирован, я его зашил в МК с помощью программы «PonyProg» и обычного SPI программатора. В результате, загрузчик работает следующим образом. При включении видеокамеры сразу же активируется загрузчик. Он ждёт подключения от утилиты одну секунду, затем начинает работать основная прошивка. Если подключение успешно установлено, то начинается процесс перепрошивки. При этом другой конец линии RS-485 нужно заранее отключить от DVR и подключить к ПК через переходник RS485<->RS232 или RS485<->USB. Кстати, насчёт переходников. Возник вопрос, как сделать такой переходник самостоятельно, ибо покупные переходники дороговато стоят. Ковыряясь в Интернете, нашёл простую схему переходника RS485<->RS232. Она приведена на рисунке ниже. Он в основном состоит из известных микросхем MAX232 и MAX485, а TX/RX коммутация последней производится сигналом с 3-го вывода разъёма COM-порта компьютера через цепочку со стабилитроном. То есть, MAX485 коммутируется самим трафиком данных, который передаёт ПК. Всё просто и гениально.


Рис. 13. Схема переходника RS-232 <-> RS-485.

После освоения загрузчика, на досуге, я решил исследовать оптику видеокамеры. Точнее говоря, мне стало интересно, какие комбинации значений зума и фокуса будут давать сфокусированные изображения на различных дистанциях от объектива до предмета. Напомню, что область всевозможных взаимных значений зума и фокуса описывается пятиугольной областью (почти прямоугольной). Для примера возьмём расстояние от объектива до предмета 10 см. Аргумент (по оси абсцисс) зум имеет диапазон значений от -600 до 600. Нужно на каждом значении зума подобрать значение фокуса, при котором предмет перед объективом на видеоизображении будет в фокусе. Затем нужно составить таблицу. Разумеется, нет смысла перебирать все 1200 значений зума, достаточно взять несколько десятков значений с определённым равным шагом. В качестве такого шага я выбрал значение 50. На каждом значении зума с данным шагом (-600, -550, -500, …) я подобрал значение фокуса, и результаты измерений зафиксировал. Аналогичную процедуру я проделал с другими расстояниями от объектива до предмета: 50 см, 1 м, 10м, 100 м. В результате получилось семейство кривых, которые я отобразил в Excel.


Рис. 14. Кривые Z-F для сфокуссированных изображений.

Глядя на графики, хочется дать множество комментариев. На минимальном зуме значения фокуса чуть меньше «середины» (нуля) практически для любой дистанции. Розовая кривая для дистанции 10 см заканчивается на значении зума около 250, которому соответствует минимальное значение фокуса. Данная кривая имеет убывающий характер и выпукла вверх. Красная кривая для дистанции в 1 м имеет совершенно другую форму. Во-первых, она не монотонна, а во-вторых, что касается свойства выпуклости, – есть точка перегиба. Аналогичный характер имеют кривые для дистанций 10 м и 100 м. Последние, кстати говоря, практически совпадают, о чём я уже заранее догадывался. Поэтому измеряемые дистанции 10 и 100 метров я, разумеется, брал приблизительно. Что качается кривой голубого цвета для полуметрового расстояния – изначально я не собирался делать на нём измерения. Данное расстояние я подобрал приблизительно исходя из того принципа, чтобы как можно ближе фрагментом соответствующей кривой приблизиться к угловой границе области (срезу). Так и получилось: данная граница практически касается фрагменту кривой. В общем, стоит отметить, что верхняя половина области (положительные значения фокуса) практически не используется. Исключение – на большой дистанции от предмета до объектива и на самом большом зуме. И ещё, для практически всех дистанций (кроме самой близкой, менее полуметра), на малом зуме (150 и менее) значения фокусов практически одинаковые. Вообще говоря, все изложенные факты измерений должны иметь теоретическую трактовку, опираясь на законы оптики. Но на текущий момент времени я не имею представления об устройствах объективов подобного рода. Максимум, с чем я сталкивался в области оптики – построение простейшего телескопа-рефрактора из двух линз. А в случае с данной видеокамерой – я не разбирался с устройством механики оптики. На выходе не доступны только два подвижных узла: узел фокуса (отвечающий за фокус) и узел зума. А сколько всего линз внутри – не знаю. Предполагаю, что две, которые, как раз, связаны с этими подвижными узлами. Стоит также заметить, что при подстройке фокуса также визуально немножко изменяется зум, даже если соответствующий ему узел фиксирован.

В завершении статьи перейдём к практическому тестированию видеокамеры. Я решил не делать множество стоп-кадров, а сразу выложить видео целиком. Записывал через устройство видео захвата, звук писал отдельно на аудио рекордер. Исходное разрешение – 720 на 576. После загрузки видео на ютуб его качество заметно изменилось.

При включении камеры первым делом происходит инициализация шаговых двигателей зума и фокуса на фоне видеоизображения. Данное изображение чёрно-белое и без АРУ, так как процедура инициализации видеопроцессора ещё не прошла. Внизу слева на месте отображения текущей даты и времени будет отображаться дата и время компиляции текущей прошивки камеры. В исходном коде прошивки я создал соответствующие переменные, которые расположены по фиксированным адресам в HEX-коде. Предполагается, что на этапе компиляции, точнее после неё, автоматически будет выполняться программа, которая возьмёт значения переменных из системного времени и вставит их в HEX-файл по нужным адресам. Кроме этого там ещё нужно пересчитать контрольные суммы. Возможно, есть способ проще. В примере на видео эти переменные равны нулю, так как данную функцию я пока не реализовывал.

После инициализации ШД происходит инициализация видеопроцессора и тут же высвечивается приветствие на фоне видеоизображения. При регулировке зума и фокуса внизу справа высвечиваются их координаты. На видео я демонстрирую использование функции сохранения и вызова шаблонов (пресетов) зума и фокуса на различных участках изображения. При очистке пресета соответствующие переменные приобретают значения 0xFFFF, что соответствует значению -1. Данная функция, в принципе, лишняя, её можно исключить.

В последнем разделе меню, который служит для отладки, я демонстрирую запись байта 9 категории 3. Функции, соответствующие этому байту, есть в предыдущем разделе меню, о них я уже неоднократно писал. В связи с тем, что текущее состояние байта из видеопроцессора не считывается, я вручную задаю ему значение «48», как одно из приемлемых. После этого я изменяю отдельные биты данного байта, показывая тем самым функции «Mirror» и «Inverse Burst».

При регулировке уровня «Burst» в предпоследнем разделе меню можно заметить небольшой баг прошивки, который легко устранить. Из других недочётов – иногда при обновлении времени происходят пропуски символов. Думаю, это связано с «кривым» монтажом электроники внутри видеокамеры.

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

Да, чуть не забыл. Как обещал, привожу две фотографии, во что превратилась начинка видеокамеры после доработки.


Рис. 15. Обновлённый вид видеокамеры внутри снизу.


Рис. 16. Обновлённый вид видеокамеры внутри сверху.

ссылка на оригинал статьи https://habr.com/ru/post/499546/

Руководство Google по стилю в C++. Часть 10

Часть 1. Вступление

Часть 9. Комментарии
Часть 10. Форматирование

Эта статья является переводом части руководства Google по стилю в C++ на русский язык.
Исходная статья (fork на github), обновляемый перевод.

Форматирование

Стиль кодирования и форматирования являются вещью произвольной, однако проект намного легче управляется, если все следуют одному стилю. Хотя кто-то может не соглашаться со всеми правилами (или пользоваться тем, чем привыкли), очень важно чтобы все следовали единым правилам, чтобы легко читать и понимать чужой код.
Для корректного форматирования мы создали файл настроек для emacs.

Длина строк

Желательно ограничивать длину строк кода 80-ю символами.
Это правило немного спорное, однако масса уже существющего кода придерживается этого принципа, и мы также поддерживаем его.

За
Приверженцы правила утверждают, что строки длиннее не нужны, а постоянно подгонять размеры окон утомительно. Кроме того, некоторые размещают окна с кодом рядом друг с другом и не могут произвольно увеличивать ширину окон. При этом ширина в 80 символов — исторический стандарт, зачем его менять?..

Против
Другая сторона утверждает, что длинные строки могут улучшить читабельность кода. 80 символов — пережиток мейнфреймов 1960-х. Современные экраны вполне могут показывать более длинные строки.

Вердикт
80 символов — максимум.

Строка можут превышать предел в 80 символов если:

  • комментарий при разделении потеряет в понятности или лёгкости копирования. Например, комментарий с примером команды или URL-ссылкой, длиннее 80 символов.
  • строковый литерал/имя, длиной более 80 символов. Исключением является тестовый код, который желдательно размещать в начале файла.
  • выражения с include.
  • Блокировка от повторного включения
  • using декларации

Не-ASCII символы

Не-ASCII символы следует использоваться как можно реже, кодировка должна быть UTF-8.
Вы не должны хардкодить строки для показа пользователю (даже английские), поэтому Не-ASCII символы должны быть редкостью. Однако, в ряде случаев допустимо включать такие слова в код. Например, если код парсит файлы данных (с неанглийской кодировкой), возможно включать в код национальные слова-разделители. В более общем случае, код юнит-тестов может содержать национальные строки. В этих случаях следует использовать кодировку UTF-8, т.к. она понятна большинству утилит (которые понимают не только ASCII).

Кодировка hex также допустима, особенно если она улучшает читабельность. Например, "\xEF\xBB\xBF" или u8"\uFEFF" — неразрывный пробел нулевой длины в Юникоде, и который не должен отображаться в правильном UTF-8 тексте.

Используйте префикс u8 чтобы литералы вида \uXXXX кодировались в UTF-8. Не используйте его для строк, содержащих не-ASCII символы уже закодированные в UTF-8 — можете получить корявый текст если компилятор не распознает исходный код как UTF-8.

Избегайте использования символов C++11 char16_t и char32_t т.к. они нужны для не-UTF-8 строк. По тем же причинам не используйте wchar_t (кроме случаев работы с Windows API, использующий wchar_t).

Пробелы против Табуляции

Используйте только пробелы для отступов. 2 пробела на один отступ.
Мы используем пробелы для отступов. Не используйте табуляцию в своём коде — настройте свой редактор на вставку пробелов при нажатии клавиши Tab.

Объявления и определения функций

Старайтесь размещать тип возвращаемого значения, имя функции и её параметры на одной строке (если всё умещается). Разбейте слишком длинный список параметров на строки также как аргументы в вызове функции.

Пример правильного оформления функции:

ReturnType ClassName::FunctionName(Type par_name1, Type par_name2) {   DoSomething();   ... } 

В случае если одной строки мало:

ReturnType ClassName::ReallyLongFunctionName(Type par_name1, Type par_name2,                                              Type par_name3) {   DoSomething();   ... } 

или, если первый параметр также не помещается:

ReturnType LongClassName::ReallyReallyReallyLongFunctionName(     Type par_name1,  // Отступ 4 пробела     Type par_name2,     Type par_name3) {   DoSomething();  // Отступ 2 пробела   ... } 

Несколько замечаний:

  • Выбирайте хорошие имена для параметров.
  • Имя параметра можно опустить, если он не используется в определении функции.
  • Если тип возвращаемого значения и имя функции не помещаются в одной строке, тип оставьте на одной строке, имя функции перенесите на следующую. В этом случае не делайте дополнительный отступ перед именем функции.
  • Открывающая круглая скобка всегда находится на одной строке с именем функции.
  • Не вставляйте пробелы между именем функции и открывающей круглой скобкой.
  • Не вставляйте пробелы между круглыми скобками и параметрами.
  • Открывающая фигурная скобка всегда в конце последней строки определения. Не переносите её на новую строку.
  • Закрывающая фигурная скобка располагается либо на отдельной строке, либо на той же строке, где и открывающая скобка.
  • Между закрывающей круглой скобкой и открывающей фигурной скобкой должен быть пробел.
  • Старайтесь выравнивать все параметры.
  • Стандартный отступ — 2 пробела.
  • При переносе параметров на другую строку используйте отступ 4 пробела.

Можно опустить имя неиспользуемых параметров, если это очевидно из контекста:

class Foo {  public:   Foo(const Foo&) = delete;   Foo& operator=(const Foo&) = delete; }; 

Неиспользуемый параметры с неочевидным контекстом следует закомментировать в определении функции:

class Shape {  public:   virtual void Rotate(double radians) = 0; };  class Circle : public Shape {  public:   void Rotate(double radians) override; };  void Circle::Rotate(double /*radians*/) {} 
// Плохой стиль - если кто-то потом захочет изменить реализацию функции, // назначение параметра не ясно. void Circle::Rotate(double) {} 

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

ABSL_MUST_USE_RESULT bool IsOk(); 

Лямбды

Форматируйте параметры и тело выражения аналогично обычной функции, список захватываемых переменных — как обычный список.

Для захвата переменных по ссылке не ставьте пробел между амперсандом (&) и именем переменной.

int x = 0; auto x_plus_n = [&x](int n) -> int { return x + n; } 

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

std::set<int> blacklist = {7, 8, 9}; std::vector<int> digits = {3, 9, 1, 8, 4, 7, 1}; digits.erase(std::remove_if(digits.begin(), digits.end(), [&blacklist](int i) {                return blacklist.find(i) != blacklist.end();              }),              digits.end()); 

Числа с плавающей запятой

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

float f = 1.f; long double ld = -.5L; double d = 1248e6; 
float f = 1.0f; float f2 = 1;   // Также правильно long double ld = -0.5L; double d = 1248.0e6; 

Вызов функции

Следует либо писать весь вызов функции одной строкой, либо размещать аргументы на новой строке. И отступ может быть либо по первому аргументу, либо 4 пробела. Старайтесь минимизировать количество строк, размещайте по несколько аргументов на каждой строке.

Формат вызова функции:

bool result = DoSomething(argument1, argument2, argument3); 

Если аргументы не помещаются в одной строке, то разделяем их на несколько строк и каждая следующая строка выравнивается на первый аргумент. Не добавляйте пробелы между круглыми скобками и аргументами:

bool result = DoSomething(averyveryveryverylongargument1,                           argument2, argument3); 

Допускается размещать аргументы на нескольких строках с отступом в 4 пробела:

if (...) {   ...   ...   if (...) {     bool result = DoSomething(         argument1, argument2,  // Отступ 4 пробела         argument3, argument4);     ...   } 

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

Если несколько аргементов в одной строке ухудшают читабельность (из-за сложности или запутанности выражений), попробуйте создать для аргументов «говорящие» перемынные:

int my_heuristic = scores[x] * y + bases[x]; bool result = DoSomething(my_heuristic, x, y, z); 

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

bool result = DoSomething(scores[x] * y + bases[x],  // Небольшая эвристика                           x, y, z); 

Если в вызове функции ещё есть аргументы, которые желательно разместить на отдельной строке — размещайте. Решение должно основываться улучшении читабельность кода.

Иногда аргументы формируют структуру. В этом случае форматируйте аргументы согласно требуемой структуре:

// Преобразование с помощью матрицы 3x3 my_widget.Transform(x1, x2, x3,                     y1, y2, y3,                     z1, z2, z3); 

Форматирование списка инициализации

Форматируйте список инициализации аналогично вызову функции.
Если список в скобках следует за именем (например, имя типа или переменной), форматируйте {} как будто это вызов функции с этим именем. Даже если имени нет, считайте что оно есть, только пустое.

// Пример списка инициализации на одной строке. return {foo, bar}; functioncall({foo, bar}); std::pair<int, int> p{foo, bar};  // Когда хочется разделить на строки. SomeFunction(     {"assume a zero-length name before {"},     some_other_function_parameter); SomeType variable{     some, other, values,     {"assume a zero-length name before {"},     SomeOtherType{         "Very long string requiring the surrounding breaks.",         some, other values},     SomeOtherType{"Slightly shorter string",                   some, other, values}}; SomeType variable{     "This is too long to fit all in one line"}; MyType m = {  // Here, you could also break before {.     superlongvariablename1,     superlongvariablename2,     {short, interior, list},     {interiorwrappinglist,      interiorwrappinglist2}}; 

Условия

Старайтесь на вставлять пробелы с внутренней стороны скобок. Размещайте if и else на разных строках.
Есть два подхода к форматированию условий. Один допускает пробелы между скобками и условием, другой — нет.
Предпочтительный вариант без пробелов. Другой вариант также допустим, но будьте последовательны. Если вы модифицируйте существующий код — используйте формат, который уже есть в коде. Если вы пишете новый код — используйте формат как у файлов, находящихся в той же директории или используйте формат проекта. Если не уверены — не добавляйте пробелы.

if (condition) {  // без пробелов внутри скобок   ...  // отступ 2 пробела } else if (...) {  // 'else' находится на строке с закрывающей скобкой   ... } else {   ... } 

Если используется формат с пробелами:

if ( condition ) {  // пробелы внутри скобок   ...  // отступ 2 пробела } else {  // 'else' находится на строке с закрывающей скобкой   ... } 

Заметьте, что в любом случае должен быть пробел между if и открывающей скобкой. Также нужен пробел между закрывающей скобкой и фигурной скобкой (если она есть).

if(condition) {   // Плохо - нет пробела после 'if' if (condition){   // Плохо - нет пробела перед { if(condition){    // Дважды плохо 
if (condition) {  // Хороший код - правильное количество пробелов после 'if' и перед { 

Короткие условия можно записать в одну строку, если это улучшит читабельность. Используйте этот вариант только если строка короткая и условие не содержит секцию else.

if (x == kFoo) return new Foo(); if (x == kBar) return new Bar(); 

Не используйте сокращённый вариант, если есть секция else:

// Плохо - условие в одну строку, хотя есть 'else' if (x) DoThis(); else DoThat(); 

Обычно фигурные скобки не требуются для короткого условия, однако они допустимы. Также сложные условия или код лучше читаются при наличии фигурных скобок. Часто требуют, чтобы любой if был со скобками.

if (condition)   DoSomething();  // отступ 2 пробела  if (condition) {   DoSomething();  // отступ 2 пробела } 

И если одна часть условия использует фигурные скобки, вторую также оформляйте с ними:

// Плохо - фигурные скобки у 'if', у 'else' - нет if (condition) {   foo; } else   bar;  // Плохо - фигурные скобки у 'else', у 'if' - нет if (condition)   foo; else {   bar; } 
// Хорошо - фигурные скобки и у 'if' и у 'else' if (condition) {   foo; } else {   bar; } 

Циклы и switch-и

Конструкция switch может использовать скобки для блоков. Описывайте нетривиальные переходы между вариантами. Скобки необязательны для циклов с одним выражением. Пустой цикл должен использовать либо пустое тело в скобках или continue.

Блоки case в switch могут как быть с фигурными скобками, так быть и без них (на ваш выбор). Если же скобки используются, используйте формат, описанный ниже.

Рекомендуется в switch делать секцию default. Это необязательно в случае использования перечисления, да и компилятор может выдать предупреждение если обработаны не все значения. Если секция default не должна выполняться, тогда формируйте это как ошибку. Например:

switch (var) {   case 0: {  // Отступ 2 пробела     ...      // Отступ 4 пробела     break;   }   case 1: {     ...     break;   }   default: {     assert(false);   } } 

Переход с одной метки на следующую должен быть помечен макросом ABSL_FALLTHROUGH_INTENDED; (определён в absl/base/macros.h). Размещайте ABSL_FALLTHROUGH_INTENDED; в точке, где будет переход. Исключение из этого правила — последовательные метки без кода, в этом случае помечать ничего не нужно.

switch (x) {   case 41:  // Без пометок   case 43:     if (dont_be_picky) {       // Используйте макрос вместо (или совместно) с комментарием о переходе       ABSL_FALLTHROUGH_INTENDED;     } else {       CloseButNoCigar();       break;     }   case 42:     DoSomethingSpecial();     ABSL_FALLTHROUGH_INTENDED;   default:     DoSomethingGeneric();     break; } 

Скобки являются опциональными для циклов с одной операцией.

for (int i = 0; i < kSomeNumber; ++i)   printf("I love you\n");  for (int i = 0; i < kSomeNumber; ++i) {   printf("I take it back\n"); } 

Пустой цикл должен быть оформлен либо как пара скобок, либо как continue без скобок. Не используйте одиночную точку с запятой.

while (condition) {   // Повторять до получения false } for (int i = 0; i < kSomeNumber; ++i) {}  // Хорошо. Если разбить на две строки - тоже будет хорошо while (condition) continue;  // Хорошо - continue указывает на отсутствие дополнительной логики 
while (condition);  // Плохо - выглядит как часть цикла do/while 

Указатели и ссылки

Вокруг ‘.’ и ‘->’ не ставьте пробелы. Оператор разыменования или взятия адреса должен быть без пробелов.

Ниже приведены примеры правильного форматирования выражений с указателями и ссылками:

x = *p; p = &x; x = r.y; x = r->y; 

Отметим:

  • ‘.’ и ‘->’ используются без пробелов.
  • Операторы * или & не отделяются пробелами.

При объявлении переменной или аргумента можно размещать ‘*’ как к типу, так и к имени:

// Отлично, пробел до *, & char *c; const std::string &str;  // Отлично, пробел после *, & char* c; const std::string& str; 

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

Допускается объявлять несколько переменных одним выражением. Однако не используйте множественное объявление с указателями или ссылками — это может быть неправильно понято.

// Хорошо - читабельно int x, y; 

int x, *y;  // Плохо - не используйте множественное объявление с & или * char * c;  // Плохо - пробелы с обеих сторон * const std::string & str;  // Плохо - пробелы с обеих сторон & 

Логические выражения

Если логическое выражение очень длинное (превышает типовое значение), используйте единый подход к разбивке выражения на строки.

Например, здесь при переносе оператор AND располагается в конце строки:

if (this_one_thing > this_other_thing &&     a_third_thing == a_fourth_thing &&     yet_another && last_one) {   ... } 

Отметим, что разбиение кода (согласно примеру) производится так, чтобы && и оператор AND завершали строку. Такой стиль чаще используется с коде Google, хотя расположение операторов в начале строки тоже допустимо. Также, можете добавлять дополнительные скобки для улучшения читабельности. Учтите, что использование операторов в виде пунктуации (такие как && и ~) более предпочтительно, что использование операторов в виде слов and и compl.

Возвращаемые значения

Не заключайте простые выражения return в скобки.

Используйте скобки в return expr; только если бы вы использовали их в выражении вида x = expr;.

return result;                  // Простое выражение - нет скобок // Скобки - Ок. Они улучшают читабельность выражения return (some_long_condition &&         another_condition); 
return (value);                // Плохо. Например, вы бы не стали писать var = (value); return(result);                // Плохо. return - это не функция! 

Инициализация переменных и массивов

Что использовать: =, () или
{} — это ваш выбор.

Вы можете выбирать между вариантами =,
() и {}. Следующие примеры кода корректны:

int x = 3; int x(3); int x{3}; std::string name = "Some Name"; std::string name("Some Name"); std::string name{"Some Name"}; 

Будьте внимательны при использовании списка инициализации {…} для типа, у которого есть конструктор с std::initializer_list.
Компилятор предпочтёт использовать конструктор std::initializer_list при наличии списка в фигурных скобках. Заметьте, что пустые фигурные скобки {} — это особый случай и будет вызван конструктор по-умолчанию (если он доступен). Для явного использования конструктора без std::initializer_list применяйте круглые скобки вместо фигурных.

std::vector<int> v(100, 1);  // Вектор из сотни единиц std::vector<int> v{100, 1};  // Вектор из 2-х элементов: 100 и 1 

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

int pi(3.14);  // Ок: pi == 3 int pi{3.14};  // Ошибка компиляции: "сужающее" преобразование 

Директивы препроцессора

Знак # (признак директивы препроцессора) должен быть в начале строки.

Даже если директива препроцессора относится к вложенному коду, директивы пишутся с начала строки.

// Хорошо - директивы с начала строки   if (lopsided_score) { #if DISASTER_PENDING      // Корректно - начинается с начала строки     DropEverything(); # if NOTIFY               // Пробелы после # - ок, но не обязательно     NotifyClient(); # endif #endif     BackToNormal();   } 
// Плохо - директивы с отступами   if (lopsided_score) {     #if DISASTER_PENDING  // Неправильно! "#if" должна быть в начале строки     DropEverything();     #endif                // Неправильно! Не делайте отступ для "#endif"     BackToNormal();   } 

Форматирование классов

Размещайте секции в следующем порядке: public, protected и private. Отступ — один пробел.

Ниже описан базовый формат для класса (за исключением комментариев, см. описание Комментирование класса):

class MyClass : public OtherClass {  public:      // Отступ 1 пробел   MyClass();  // Обычный 2-х пробельный отступ   explicit MyClass(int var);   ~MyClass() {}    void SomeFunction();   void SomeFunctionThatDoesNothing() {   }    void set_some_var(int var) { some_var_ = var; }   int some_var() const { return some_var_; }   private:   bool SomeInternalFunction();    int some_var_;   int some_other_var_; }; 

Замечания:

  • Имя базового класса пишется в той же строке, что и имя наследуемого класса (конечно, с учётом ограничения в 80 символов).
  • Ключевые слова public:, protected:, и private: должны быть с отступом в 1 пробел.
  • Перед каждым из этих ключевых слов должна быть пустая строка (за исключением первого упоминания). Также в маленьких классах пустые строки можно опустить.
  • Не добавляйте пустую строку после этих ключевых слов.
  • Секция public должна быть первой, за ней protected и в конце секция private.
  • См. Порядок объявления для выстраивания деклараций в каждой из этих секций.

Списки инициализации конструктора

Списки инициализации конструктора могут быть как в одну строку, так и на нескольких строках с 4-х пробельным отступом.

Ниже представлены правильные форматы для списков инициализации:

// Всё в одну строку MyClass::MyClass(int var) : some_var_(var) {   DoSomething(); }  // Если сигнатура и список инициализации не помещается на одной строке, // нужно перенести двоеточие и всё что после него на новую строку MyClass::MyClass(int var)     : some_var_(var), some_other_var_(var + 1) {   DoSomething(); }  // Если список занимает несколько строк, то размещайте каждый элемент на // отдельной строке и всё выравниваем MyClass::MyClass(int var)     : some_var_(var),             // Отступ 4 пробела       some_other_var_(var + 1) {  // Выравнивание по предыдущему   DoSomething(); }  // Как и в других случаях, фигурные скобки могут размещаться на одной строке MyClass::MyClass(int var)     : some_var_(var) {} 

Форматирование пространств имён

Содержимое в пространстве имён пишется без отступа.

Пространство имён не добавляет отступов. Например:

namespace {  void foo() {  // Хорошо. Без дополнительного отступа   ... }  }  // namespace 

Не делайте отступов в пространстве имён:

namespace {    // Плохо. Сделан отступ там, где не нужно   void foo() {     ...   }  }  // namespace 

При объявлении вложенных пространств имён, размещайте каждое объявление на отдельной строке.

namespace foo { namespace bar { 

Горизонтальная разбивка

Используйте горизонтальную разбивку в зависимости от ситуации. Никогда не добавляйте пробелы в конец строки.

Общие принципы

void f(bool b) {  // Перед открывающей фигурной скобкой всегда ставьте пробел   ... int i = 0;  // Обычно перед точкой с запятой нет пробела // Пробелы внутри фигурных скобок для списка инициализации можно добавлять на ваш выбор. // Если вы добавляете пробелы, то ставьте их с обеих сторон int x[] = { 0 }; int x[] = {0};  // Пробелы вокруг двоеточия в списках наследования и инициализации class Foo : public Bar {  public:   // Для inline-функции добавляйте    // пробелы внутри фигурных скобок (кроме пустого блока)   Foo(int b) : Bar(), baz_(b) {}  // Пустой блок без пробелов   void Reset() { baz_ = 0; }  // Пробелы разделяют фигурные скобки и реализацию   ... 

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

Циклы и условия

if (b) {          // Пробел после ключевого слова в условии или цикле } else {          // Пробелы вокруг else } while (test) {}   // Внутри круглых скобок обычно не ставят пробел switch (i) { for (int i = 0; i < 5; ++i) { // Циклы и условия могут могут внутри быть с пробелам. Но это редкость. // В любом случае, будьте последовательны switch ( i ) { if ( test ) { for ( int i = 0; i < 5; ++i ) { // В циклах после точки с запятой всегда ставьте пробел // Также некоторые любят ставить пробел и перед точкой с запятой, но это редкость for ( ; i < 5 ; ++i) {   ...  // В циклы по диапазону всегда ставьте пробел до двоеточия и после for (auto x : counts) {   ... } switch (i) {   case 1:         // Перед двоеточнием в case нет пробела     ...   case 2: break;  // После двоеточия есть пробел, если дальше (на той же строке) идёт код 

Операторы

// Операторы присваивания всегда окружайте пробелами x = 0;  // Другие бинарные операторы обычно окружаются пробелами, // хотя допустимо умножение/деление записывать без пробелов. // Между выражением внутри скобок и самими скобками не вставляйте пробелы v = w * x + y / z; v = w*x + y/z; v = w * (x + z);  // Унарные операторы не отделяйте от их аргумента x = -5; ++x; if (x && !y)   ... 

Шаблоны и приведение типов

// Не ставьте пробелы внутри угловых скобок (< и >), // перед <, между >( в приведении std::vector<std::string> x; y = static_cast<char*>(x);  // Пробелы между типом и знаком указателя вполне допустимы. Но смотрите на уже используемый формат кода std::vector<char *> x; 

Вертикальная разбивка

Сведите к минимуму вертикальное разбиение.

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

Базовый принцип: чем больше кода поместится на одном экране, тем легче его понять и отследить последовательность выполнения. Используйте пустую строку исключительно с целью визуально разделить эту последовательность.

Несколько полезных замечаний о пустых строках:

  • Пустая строка в начале или в конце функции не улучшит читабельность.
  • Пустые строки в цепочке блоков if-else могут улучшить читабельность.
  • Пустая строка перед строкой с комментарием обычно помогает читабельности кода — новый комментарий обычно предполагает завершение старой мысли и начало новой идеи. И пустая строка явно на это намекает.

ссылка на оригинал статьи https://habr.com/ru/post/499944/

20 перспективных продуктов в области высокоскоростной связи

Предлагаем вам выборку из 175 высокотехнологичных компаний, представленных на DesignCon 2020.

В дополнение к основным докладам, экспертным панелям, техническим документам, зачетным сессиям IEEE и образовательным трекам, DesignCon 2020 включал в себя множество демонстраций и информационных стендов на выставочной площадке. Представляем вашему вниманию 20 компаний, занимающихся аппаратным и программным обеспечением, предлагающих образцы того, что является трендом в мире высокоскоростной передачи данных, телекоммуникаций, межкомпонентных соединений, испытательного оборудования и инструментов моделирования, а также то, что ускользнуло от вашего внимания на праздновании 25-й годовщины DesignCon.

eTopus Technology

image

eTopus, названная в честь визуализации электронного осьминога, разрабатывает сверхвысокоскоростные смешанные полупроводниковые IP-сети для высокопроизводительных вычислительных систем и применения в центрах обработки данных. Сверхскоростная пара сериализатор/десериализатор их производства предназначена для использования в системах хранения данных, корпоративных и гипермасштабных центрах обработки данных.

На выставке Designcon2020 компания eTopus продемонстрировала устройство Western Digital ASIC, использующее ethernet-соединение eTopus. Я поговорил с Гарри Чаном, основателем и генеральным директором eTopus об их стенде. Вот что он сказал: «Эта демонстрация показывает связь между энергонезависимой памятью через оптоволокно Ethernet. В этой установке наш клиент использует корпус с жестким диском SST. У них также есть сервер, который подключается к коммутатору Mellanox 100G, подключенному к распределителю. Внутри распределителя находится чип, который наш клиент использует в качестве встроенного контроллера памяти через кабель, который принимает трафик от сервера и соединяет его со всеми дисками. Мы демонстрируем операции чтения/записи, чтобы показать, насколько их скорость близка к максимуму, измеряя ввод/вывод в секунду (или IOPS). Как вы видите, пропускная способность впечатляет. Теоретический предел, которого можно достичь, составляет 2,8 миллиона IOPS. Мы показываем, что одна из систем достигает 2,67 миллиона IOPS. Она не достигает теоретического предела не из-за ограничения чипа, а потому что у сервера не хватает мощности. Со своей стороны, мы используем пару сериализатор/десериализатор eTopus IP, которая находится в нашем 28-гигабитном IP-ePHY ».

Keysight

image

На выставке Keysight было проведено множество демонстраций, посвященных оборудованию, отвечающему требованиям к передатчикам (Tx) и приемникам (Rx) DDR5, тестерам PCIe 5.0, соединениям USB 4.0 и устройствам для измерения целостности сигнала и мощности. В рамках раздела «Подключение к центру обработки данных» компания представила платформу для тестирования и проверки 4-уровневой амплитудно-импульсной модуляции 400GE (PAM4). Кроме того, как часть этого специфического для приложения раздела, была показана система анализа ошибок на 100 Гб с прямым исправлением ошибок (FEC). Наконец, модуль амплитудно-импульсной модуляции PAM4 400G выделял значения частоты битовых ошибок в реальном времени на всех электрических линиях, что позволяло оценить производительность прямой коррекции ошибок для закодированных в PAM4 сигналов.

TE Connectivity

image

В дополнение к демонстрации большого семейства соединений для объединительных плат и коаксиальных соединителей с поддержкой 5 Гбит/с, TE Connect провела несколько демонстраций, связанных с совместной упаковкой в сокетах, технологиями связи 5 поколения, подачей питания и управлением тепловыми режимами.

Избавление от нежелательного нагрева может быть проблемой в любой электронной конструкции, но особенно это касается высокоскоростных сигналов, которые, как правило, выделяют больше тепла на микросхеме и плате. Тепловой мост — это, обычно, нежелательный тепловой путь в цепи печатной платы. Если компонент имеет более высокую теплопроводность, чем окружающие материалы, это создает путь наименьшего сопротивления теплопередаче. Это приводит к общему снижению теплового сопротивления объекта.

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

Rohde & Schwarz

image

Компания Rohde & Schwarz продемонстрировала систему отладки целостности сигналов с помощью декомпозиции джиттера в своих осциллографах. Новая функция в осциллографах компании помогает инженерам-разработчикам получить более глубокое представление об отдельных компонентах джиттера в сигналах. Теперь они могут разделить джиттер на случайные и детерминированные компоненты и просматривать результаты для более эффективной отладки. В алгоритме декомпозиции компании используется модель параметрических сигналов для точных измерений и дополнительного представления результатов. Одним из результатов является последовательность данных об измерениях даже для относительно коротких последовательностей сигналов, плюс различная информация – например, о пошаговой характеристике или о различии между вертикальным и горизонтальным периодическим джиттером.

Cadence Design Systems

image

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

Все инструментальные комплекты компании используются в этом интеллектуальном системном проектировании для решения задач продвинутого электромагнитного, электронного и теплового анализа, усовершенствованной упаковки интегральных микросхем и кросс-платформенных конструкций для включения интегральных микросхем с усовершенствованной DDR IP. Одна из демонстраций на выставочном этаже была сосредоточена на их инструментальном комплекте Virtuoso RF, среде совместного дизайна для RFIC, RF-модулей, и пакетного проектирования. На другой демонстрации были продемонстрированы наборы инструментов для анализа сигналов и целостности электропитания на уровне tPCB/пакетов.

Takachi

image

Японская компания продемонстрировала впечатляющий ассортимент ярко окрашенных электронных и промышленных корпусов, включая алюминиевые, пластиковые, ручные, литые алюминиевые, нержавеющие, 19-дюймовые корпуса для монтажа в стойку, новые пластмассовые корпуса IP67/IP65 для наклонных и медицинских применений и многое другое. Знаете ли вы, что компания Takachi может изготавливать корпуса по индивидуальным заказам с использованием станков с ЧПУ, струйной печати и CAD-систем?

Teledyne LeCroy

image

Как и многие участники выставки, компания Teledyne LeCroy провела несколько технических семинаров и уроков, в частности они провели мероприятие на тему «Программные инструменты с открытым исходным кодом для обеспечения целостности сигналов». Это практическое занятие проводилось Питером Пупалайкисом, вице-президентом компании по технологическому развитию. Он только что опубликовал через Кембриджский университет книгу «S-параметры целостности сигналов». Некоторые из вас до сих пор помнят S-параметры из математики и теории схем, как способ описания электрического поведения в электрических системах при введенных стимулах. Что делает книгу Питера уникальной, так это сопутствующее программное обеспечение с открытым исходным кодом, которое является долгожданной альтернативой существующему проприетарному программному обеспечению.

Неудивительно, что Teledyne LeCroy предлагает системы измерения параметров рассеяния (S-параметров) для оказания дальнейшей помощи конструкторам и испытателям. Во время выставки компания продемонстрировала свою поддержку недавно представленного протокола Compute Express Link (CXL) на своих анализаторах протоколов Summit PCI Express (PCIe).

Dino-Lite

image

На выставке было представлено несколько компаний, демонстрирующих свои технологии в области цифровых микроскопов, в том числе Dino-Lite, PacketMicro и другие. Dino-Lite — это торговая марка Omano Microscopes. Портативные цифровые микроскопы и окулярные камеры компании обеспечивают высококачественную видеосвязь с ПК и Mac. Большинство моделей обеспечивают впечатляющее увеличение с функциями вроде измерения и регулируемого поляризации. Входящее в комплект программное обеспечение позволяет с легкостью делать снимки, записывать видео, управлять изображениями, а также сохранять и отправлять свои открытия по электронной почте.

Molex

image

В этом году Molex и Spectra7 Microsystems представили активные медные соединения на 400Гб. Spectra7 Microsystems поставляет аналоговые полупроводниковые продукты для рынков широкополосных соединений, в то время как Molex является поставщиком высокопроизводительных соединений. В последнее время многие заказчики систем соединений обращаются за более длинными активными медными версиями существующих пассивных медных кабельных сборок QSFP-DD (четырехкомпонентные кабельные сборки с удвоенной плотностью). Система от Molex вместе с технологией GaugeChanger от Spectra7 претендует на то, что она обеспечивает до 12 раз меньшее энергопотребление, в то же время обеспечивая значительную экономию затрат по сравнению с оптическими решениями для гипермасштабных клиентов. Очевидно, что в 2020 году в США начнется масштабное развертывание сетевого оборудования со скоростью передачи данных 400 Гбит/с.

Стенд, который привлек мое внимание, был представлен на выставке «Open 19 Backplane Cabling Through Packet Infrastructure». Этими комментариями поделилась Лиз Хардин, менеджер по продукции для предприятий в Molex: «Molex, в сотрудничестве с Packet, демонстрирует пакетную аппаратную платформу — систему на базе Open19». Поддержка этой платформы основана на использовании кабельной сборки на базе Impel, которая разбивается на 2 разъема QSFP, обеспечивая передачу сигналов через PAM4 со скоростью до 56 Гбит/с на каждую линию с возможностью передачи избыточного сигнала от стандартного коммутатора". Packet сотрудничают с ASRock в области серверных стоек и в этой демонстрации показывают три версии: t3.small.x86, микро-серверы на базе AMD EPYC 3151 с двумя узлами на блок; c3.small.x86, микро-сервер на базе с двумя узлами на блок; и c3.medium.x86, односокетная система AMD EPYC 7002 (Rome) в блоке, поддерживающем PCIe Gen4 с архитектурой OCP 3.0".

GigaTest Labs

image

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

Зондовая станция GTL5050 предназначена для двухстороннего зондирования. Вместо того, чтобы пытаться выполнять измерения с платой, установленной вертикально, эта измерительная станция предоставляет 2 измерительные станции в одной — с верхней и нижней стороны, причем вся станция может поворачиваться на 180 градусов, что обеспечивает быструю настройку и предварительную конфигурацию.

Blue Clover Devices

image

Несколько лет назад Blue Clover Devices выпустила инструмент для производственной линии (PLT), встроенный в облако прибор для автоматизации тестирования оборудования. С тех пор PLT был дополнен внутрисхемным тестером (ICT), который помогает клиентам быстрее запускать свое оборудование. PLT представляет собой коробку, которая загружает микропрограммное обеспечение, запускает автоматизированные тесты и записывает результаты в облако. Он предназначен для удовлетворения потребностей разработчиков на протяжении всего жизненного цикла продукта. После того, как устройство интернета вещей разработано и проверено на PLT, идентичные коробки отправляются на производственную линию для программирования и тестирования в производственных масштабах. Команда разработчиков может видеть результаты непосредственно с производственной линии и опережать проблемы, которые могут задержать сборку продукта.

ICT представляет собой коробку с вертикальными рычагами, поддерживающими переключающий зажим. Для проведения внутрисхемного тестирования на персонализированной печатной плате, в ICT размещена изготовленная по индивидуальному заказу кассета с пружинным контактом (Pogo Pin Cassette).

VSI Labs

image

В прошлом году компания VSI Labs, занимающаяся исследованиями беспилотных транспортных средств, отправила свой беспилотный исследовательский автомобиль в путешествие на 2000 миль из Миннеаполиса, штат Миннеаполис, в Санта-Клару, Калифорния, где проходила Всемирная конференция Drive World. Целью этого беспрецедентного проекта является проверка преимуществ использования карт высокой четкости и усовершенствованной технологии GPS для повышения производительности и безопасности приложений автономного вождения по дорогам. Команда также надеется лучше понять, как эти технологии работают в различных условиях местности, погоды и вождения.

VSI уже более двух лет тестирует свои испытательные беспилотные автомобили на дорогах общего пользования. В этом году на DesignCon они продемонстрировали свою технологию ИК-датчиков. Кроме того, Сара Сарджент, менеджер инженерных проектов, выступила на Chiphead Theater с докладом о ключевых компонентах автоматизированных транспортных средств и была частью панели «Женщины и меньшинства в STEM».

Rambus

image

Компания Rambus является поставщиком чиповых интерфейсных продуктов и услуг, включая схемы физического уровня с интерфейсами контроллеров памяти с двойной скоростью передачи данных (например, DDR 1 через DDR 5) и GDDR. В этом году на выставке были представлены GDDR, пары сериализатор/десериализатор и ядра HBM IP, а также наборы протоколов MACsec/IPsec для современных дата-центров и сетевых приложений. Кроме того, были проведены технические тренинги по таким темам, как интерфейсные решения для 5G, альтернативные варианты интерфейсов для архитектуры чиплетов, решения памяти для ИИ и машинного обучения, интерфейсные решения для корпоративных и гипермасштабных центров обработки данных, новые требования к автомобильным интерфейсам и решения для трехмерных интегральных схем.

Высокоскоростные соединения являются ключом к производительности и росту центров обработки данных, с учетом их взаимодействия с облачными сервисами, устройствами IoT, потоковым видео, большими объемами данных, беспроводными сетями, электронной коммерцией и социальными сетями. В этом году компания Rambus представила три типа мультипротокольных схем физического уровня с парой сериализатор/десериализатор: 28G, 32G и 112G

Xilinx

image

Xilinx продемонстрировали несколько образцов высокоскоростной последовательной архитектуры, а также стенды, связанные с целостностью сигналов и мощности, и интерфейсами памяти. Эксперты из Xilinx и других компаний провели техническую сессию, в ходе которой были рассмотрены спецификации по проектированию компонентов для электрических последовательных каналов связи за пределами 112 Гбит/с. Основываясь на требованиях совместимости и масштабируемости, электрические решения для линий свыше 112 Гбит/с должны быть изучены в конфигурациях на базе печатных, ортогональных и кабельных объединительных плат. Однако, цели проектирования ключевых компонентов для линий свыше 112 Гбит/с неясны. В качестве примеров были предложены два набора рекомендаций по проектированию компонентов для медных передатчиков на 224 Гбит/с для меньших шасси/боксов и больших шасси. Это было темой технической сессии.

На демонстрации был представлен 7-нм тестовый чип PAM4 Xilinx на 112 Гбит/с, работающий под управлением нескольких приемопередатчиков PAM4 на 112 Гбит/с. Каналы проходят от адаптивной платформы ускорения чипа Xilinx вычислений (ACAP) через терминал NovaRay компании Samtec к разъему объединительной платы ExaMax, используя твинаксиальный кабель 34 AWG Samtec. Все это было соединено с помощью кронштейна для крепления панели к 1-метровому кабельному соединению объединительной платы 30 AWG, проходящему непосредственно через недорогую и менее сложную печатную плату.

Обратный путь каналов повторялся обратно к тестовому чипу ACAP Xilinx 112 Гбит/с PAM4 7нм, который обрабатывает данные для отображения информации о частоте битовых ошибок.

Samtec

image

Во-первых, Иштван Новак, представляющий компанию Samtec, был назван «Инженером года по версии DesignCon». О том, почему он получил эту награду, вы можете прочитать здесь. Во-вторых, Samtec представили на выставке несколько демо-версий своих продуктов, в том числе технологию Flyover, соединяющую передние и объединительные панели с промежуточными платами с поддержкой PAM4 и работающую со скоростью 112 Гбит/с, масштабируемую кремниевую тестовую платформу для ИИ, работающую со скоростью 32 ГТ/с, высокопроизводительные тестовые решения с частотой до 70 ГГц, а также Direct Connect – технологию кабельных систем для подключения к кремниевым чипам.

Несколько лет назад Samtec представила свою кабельную систему Flyover Cable System в качестве еще одного пути прохождения сигнала через печатную плату. По словам представителей компании, конструкция Flyover от Samtec преодолевает ограничения традиционных сигнальных плат и аппаратных решений, в результате чего получается экономически эффективный, высокопроизводительный вариант для работы со скоростью 28 Гбит/с и выше. Демонстрация на стенде показала технологию передачи данных от средней к кабельной объединительной платы PAM4 со скоростью 112 Гбит/с. В частности, 4-кратные пути передачи данных соединялись между несколькими серверами на 112Гб с PAM4 через две кабельные сборки NovaRay и магистральный кабель ExaMAX с большим радиусом действия.

Ansys

image

Программное обеспечение для проектирования высокоскоростной электроники было в центре внимания компании Ansys на выставке. В основе их подходов лежали технологии 5G, ADAS, IoT и другие беспроводные, а также цифровые системные технологии, требующие высокого уровня интеграции, низкого энергопотребления и малого размера электроники. Инженеры, занимающиеся проектированием с этими ограничениями — а также высокие ограничения по времени отклика и уровню шума в печатных платах, электронных модулях и сложных соединениях — требуют многофункциональных инструментов моделирования для балансировки и оптимизации производительности системы.

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

Anritsu

image

Anritsu, глобальный поставщик решений для тестирования и замеров в области связи, продемонстрировал линейку систем обеспечения целостности сигналов. На выставке были представлены приборы для тестирования и новые технологии для проверки конструкций с использованием PCI Express (PCIe 3.0/4.0/5.0), Ethernet PAM4 и других технологий высокоскоростных соединений. Области применения этих приборов для тестирования включали чипсеты, кабели, межсоединение и системы.

Компания представила измерительные системы СИ на дисплее с частотой 100 ГГц. Но меня привлекла новая платформа тестирования PCIe, которая поддерживала стресс-тесты приемника с нагрузкой по спецификации PCIe 5.0 Base/CEM. В центре демонстрации находился анализатор качества сигнала серии MP1900A с ПО для калибровки базовой спецификации и ПО для измерения частоты битовых ошибок, поддерживающим фильтр SKP. С помощью этой системы инженеры получили возможность сконфигурировать измерительную среду для ранней стадии разработки PCIe 5.0 IP и устройств.

Rosenberger

image

Rosenberger (Северная Америка) – поставщик RF-коннекторов, низкочастотных и высокочастотных кабельных сборок, устройств для испытаний и измерений, автомобильных продуктов, оптоволоконных решений и медных сборок, изготовленных на заказ. Их продукты используются в беспроводных технологиях, телекоммуникациях, тестах и измерениях, автомобильных, медицинских, военных, промышленных системах и системах передачи данных.

Среди множества экспонатов, представленных на выставке, были представлены непаяные печатные платы, хорошо работающие на частоте до 110 ГГц. Разъемы этих печатных плат собираются с помощью винтов. Разъемы без припоя используются для высокопроизводительных приложений. Они представляют собой 30-градусные угловые коннекторы, предназначенные для обеспечения низких возвратных потерь для частот до 110 ГГц, а также для однослойных или многослойных печатных плат, где сверху находится микроволновой слой.

Amphenol ICC

image

Amphenol ICC, подразделение компании Amphenol, предоставляет решения по обеспечению связи для рынков передачи информации, коммуникаций и коммерческой электроники. На выставке компания представила свои высокоскоростные объединительные платы, системы ввода-вывода, системы хранения данных, серверы, мезонинные платы и силовые устройства. Было представлено несколько демо-версий продуктов компании: OverPass на 56G, 112G Mini Cool Edge и 56GB с использованием Lyn QD и LinkOVER, которые отличаются превосходными характеристиками в своих продуктах OverPass. LinkOVER — это система двухосных разъемов над печатной платой, которая предоставляет системным дизайнерам и инженерам-проектировщикам возможность обходить дорожки платы при передаче сигналов с высокой пропускной способностью в приложениях следующего поколения «плата-плата», «система-система» или «чип-чип». Эта технология поддерживает скорость передачи данных от 10 Гбит/с до более чем 112 Гбит/с PAM4 на одну полосу с высоким отношением сигнал/шум и низким КСВЧ. Решение LinkOVER для монтажа на печатную плату, обеспечивающее прямую компрессию, устраняет необходимость использования дополнительных карт, сводя к минимуму переходы и потери в системных бюджетах. LinkOVER доступен в винтовых и поверхностно-монтируемых конфигурациях рам. Этот продукт является идеальным решением для систем 100G/200G/400G, lnfiniband, PCle, связи между чипами и 5G систем.

TestEquity

image

Проектирование новейших и наиболее крупных технологий межсетевых соединений и печатных плат требует большого количества аналитического и испытательного оборудования. Одним из способов управления стоимостью дорогостоящего оборудования является аренда или использование ранее приобретенных анализаторов и тестеров. TestEquity удовлетворяет эту потребность, предоставляя полностью функциональное и откалиброванное оборудование, снабженное всеми аксессуарами и руководствами по эксплуатации. Кроме того, компания продает новое испытательное оборудование, а также собственные испытательные камеры. TestEquity является авторизованным дистрибьютором оборудования Keysight, Tektronix, Keithley и Rohde & Schwarz.


image

О компании ИТЭЛМА

Мы большая компания-разработчик automotive компонентов. В компании трудится около 2500 сотрудников, в том числе 650 инженеров.

Мы, пожалуй, самый сильный в России центр компетенций по разработке автомобильной электроники. Сейчас активно растем и открыли много вакансий (порядка 30, в том числе в регионах), таких как инженер-программист, инженер-конструктор, ведущий инженер-разработчик (DSP-программист) и др.

У нас много интересных задач от автопроизводителей и концернов, двигающих индустрию. Если хотите расти, как специалист, и учиться у лучших, будем рады видеть вас в нашей команде. Также мы готовы делиться экспертизой, самым важным что происходит в automotive. Задавайте нам любые вопросы, ответим, пообсуждаем.

Читать еще полезные статьи:

ссылка на оригинал статьи https://habr.com/ru/company/itelma/blog/497744/

Как наши датчики свою первую зиму провели

Привет! В одной из прошлых статей я писал о том, что мы решили делать собственные взрывозащищенные датчики для наших объектов, а не использовать продукты сторонних производителей. Так вот, сказано — сделано.

Теперь у нас есть пилотная партия, 190 штук, которые мы тестируем на протяжении полугода. Старт пришёлся на ноябрь 2019 года, когда мы установили достаточно большое количество датчиков в Тобольске, чтобы посмотреть, как они работают в боевых условиях, и собрать полноценную статистику. Полгода спустя мы можем точно сказать, что идея делать всё самим была правильной и оправдалась — они нормально прошли боевое крещение зимой (а зима в тех краях, где стоят наши объекты, весьма и весьма серьезная штука). Значит, будем выпускать такие датчики в тираж.

Что это за датчики и зачем они нужны

Прежде всего — это датчики, которые измеряют как температуру рабочих поверхностей, так и температуру объектов. Поэтому датчики должны работать в вилке температурных условий от -56 до +50 градусов по Цельсию. Мы поставили их на производство пиролиза ЗапСибНефтехима, чтобы защищать от замерзания трубы теплоспутников (второстепенные по значимости объекты производства). До введения системы датчиков этот контроль осуществлялся людьми, которые ходили и руками трогали эти трубы на предмет «замерзло — не замерзло».

В конце 2018 года, пока завод ещё не был пущен, мы поставили туда партию невзрывозащищенных датчиков, чтобы просто проверить, будут ли они работать или отвалятся на холоде. Тогда ещё так можно было, взрывоопасных зон не было. А летом 2019, после пуска завода, такие зоны ожидаемо появились, и датчики перед зимой пришлось заменить.

На этих строках у ряда людей возникают мысли — ага, поставили датчики, и людям стало нечего делать, всё, роботы вытесняют людей с работы, началось. На самом деле, все не так однозначно. ЗапСибНефтехим в принципе планировался как объект с достаточно высоким уровнем автоматизации, и установка этих датчиков просто поддержала эту стратегию. У нас удельная численность персонала в принципе меньше, чем на более старых заводах.

Проблемы

С датчиками проблем не было, по большей части благодаря довольно суровому подходу к тестированию (об этом рассказывала моя коллега Юстина Цига, инженер Индустрии 4.0). Критерии проверки оборудования у нас жёсткие, поэтому, если бы какой-то датчик и мог забарахлить зимой, он бы наверняка показал свою теплолюбивую натуру ещё на испытаниях. А так всю зиму все отработали на ура.

С чем были проблемы — так это с элементами питания. Во-первых, нам банально пришла первая партия батареек для датчиков с большим процентом брака. Около 30% батареек сели ранее заявленного срока, у них реальная ёмкость существенно отличалась от заявленной производителем. Остальные 70% сейчас всё ещё в норме и продолжают работать, сохраняя номинальную ёмкость. Решили мы эту проблему довольно просто, сменив поставщика и начав закупать батарейки в Европе вместо Китая.

Кстати, о батарейках, это литий-тионилхлоридные источники питания. Работа датчика организована таким образом, что он посылает сигнал по протоколу LoRaWAn не постоянно, а раз в 20 минут. Поэтому на одной батарейке он может полноценно проработать 5 лет.

Вторая проблема заключалась в том, что персонал был немного не готов к процессу монтажа датчиков. Вроде бы, со стороны кажется, ничего сложного, взял датчик, поставил его куда надо, и пошёл дальше работать. На деле же зачастую при монтаже просто обрывали ряд проводов. Решили это оперативным выпуском подробной инструкции — как ставить именно эти датчики, на что обращать внимание и прочее. Сейчас для этого оборудован специальный учебный класс в Тобольске на базе СИБУР Интеха, там в классе механики размещены наши датчики и проводится обучение по монтажу.

Производство, работа, планы

Удалось достичь ситуации, при которой можно точно говорить, что датчики — это российская разработка. Потому что в процессе разработки принимали равное участие инженеры двух компаний: СИБУРа и наших подрядчиков, компании «М-Плата». Это отличные ребята, мы оплатили им услуги по разработке и являемся обладателями интеллектуальной собственности на результат. А ещё у «М-плата» прямо в Москве стоит собственный завод по производству электроники, откуда и вышли датчики.

Это очень большой плюс не только с точки зрения импортозамещения и логистики, но и с чисто практической: получается, что такое производство у нас в значительной степени отвязывается от курса иностранной валюты. Корпуса и сенсоры тоже изготавливаются в РФ, а не в Китае. Само собой, ряд электронных комплектующих (и упомянутые выше батарейки) мы продолжаем заказывать из-за рубежа.

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

Да, датчики работают в плотной связке со специально созданной для таких нужд IoT-платформой СИБУРа, о которой я подробнее расскажу в отдельном посте.

Василий Ежов
владелец продукта IoT в СИБУРе

ссылка на оригинал статьи https://habr.com/ru/company/sibur_official/blog/499814/