Попросили меня как-то друзья помочь с программированием контроллера поворотного стола для фотографирования. Эти столы используются для круговой съёмки. Часто результат такой съёмки можно видеть в интернет-магазинах, когда товар можно покрутить и рассмотреть с разных сторон.

Как сделать фото 360°? Можно поставить объект на поворотную платформу, затем вручную перемещать её на определённый угол и щёлкать затвором фотоаппарата. Но лучше этот процесс автоматизировать.

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

Дело для меня совершенно новое. Я никогда не занимался АСУ, с релейно-контактной логикой незнаком, про ПЛК не слышал. Ну что ж, тем интереснее будет разобраться, что такое релейная логика и что представляют из себя языки LD (Ladder Diagram) и IL (Instruction List).

Bот что можно получить при помощи поворотного стола:

А вот сам поворотный стол:

А это контроллер SMSD‑1.5Modbus. Кстати, отечественная разработка. Впрочем, на его месте мог бы быть любой другой ПЛК:

Контроллер поставляется с софтом. Используются оба языка программирования LD и IL. Расскажу немного про лестничные диаграммы. Вот основные элементы языка LD:

входной сигнал, нормально-открытый контакт

входной сигнал, нормально-закрытый контакт

выход, катушка

Входные контакты обозначаются буквой X с циферкой. Нормально-открытый контакт срабатывает, когда на вход подаётся сигнал. Нормально-закрытый контакт в обычном состоянии замкнут и срабатывает, когда входной сигнал пропадает.

Входные контакты можно комбинировать. Это логическое И:

А это логическое ИЛИ:

Выходные контакты обозначаются буквой Y с номером. Есть и другие символы:

Символ для входного импульсного сигнала с опросом по переднему фронту

Символ для входного импульсного сигнала с опросом по заднему фронту

Символ для прикладных инструкций

Символ логической инверсии

Вот пример простейшей диаграммы. Когда контакт X1 замкнут, мы запускаем двигатель и выставляем выход Y0. На панели контроллера при этом загорится соответствующая диодная лампочка:

А вот более интересный случай:

Здесь при замыкании контакта X5 выход Y3 изменит свое состояние на замкнутое, однако, при размыкании контакта X5 выход Y3 сохранит свое замкнутое состояние до тех пор, пока не будет включен вход X6. Контакт Y3 является самоблокировочным.

Язык лестничных диаграмм является производным от релейно-контактной принципиальной электрической схемы в упрощенном представлении. Вот для сравнения релейно-контактная электрическая схема и соответствующая LD-диаграмма:

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

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

В процессе работы контроллер непрерывно опрашивает текущее состояние входов и изменяет состояние выходов в зависимости от программы пользователя. На первом этапе происходит считывание состояния физических и виртуальных Modbus Coils входов и их буферизация во внутренней памяти контроллера. Да, контроллер может управляться и по протоколу Modbus, но поскольку я использовал его автономно, рассказывать про Modbus не буду.

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

IL-программа состоит из последовательности отдельных управляющих инструкций. Контроллер обрабатывает инструкции последовательно, одну за другой. Собственно, в контроллер загружается именно последовательность инструкций с операндами. Инструкций много. Есть команды проверки входных условий, есть арифметические, битовые и логические команды, возможна целочисленная арифметика и арифметика с плавающей точкой. Есть возможность использования прерываний и подпрограмм. Наконец, есть группа команд для управления двигателем.

Операндами являются регистры (общего назначения, энергонезависимые и индексные), меркеры (однобитные ячейки памяти), таймеры, счётчики и константы.

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

На этой картинке показана LD-диаграмма и соответствующая IL-программа:

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

Некоторой неожиданностью для меня стало то, что любой отдельный кусок программы обязательно должен начинаться с инструкции LD (нормально-открытый контакт) или LDI (нормально-закрытый контакт). Иными словами, большинство исполнительных инструкций требует наличие входного условия, их нельзя поставить на выполнение как одиночные инструкции. Это я не сразу усвоил. Есть лишь несколько исключений из этого правила: это указатели I, P, команды конца программы END, FEND, а также IRET, SRET, EI, DI, NEXT, FOR. То есть, получается, что вся программа – это набор альтернатив, только вместо IF или IF NOT надо использовать LD и LDI (есть ещё несколько входных инструкций, но не суть).

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

Мне нужно было реализовать четыре режима работы: видеорежим с возможностью регулировать скорость вращения стола кнопками пульта, ручной режим, где нужно совершать каждый шаг стола и спуск затвора фотоаппарата вручную посредством кнопок, автоматический режим, где стол совершает полный оборот за заданное количество шагов с автоматическим спуском затвора, и наконец, режим non-stop, в котором стол совершает полный оборот без остановок, а затвор фотоаппарата автоматически срабатывает в нужные моменты.

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

Здесь не так. Логика работы размазывается по разным веткам исполнения. Сначала мы создаём таймер по входному условию и задаём время задержки, и уже где-то совсем в другом месте используем таймер в качестве входного контакта, который замкнётся по истечении заданного времени. Я, правда, проверял таймер сразу же, так привычней:

LD    M109      ;произошла ошибка и горел индикатор
TMR   T0   K10	;запустим таймер на 100ms, он будет отсчитывать время, пока его вход M109 включён
AND   T0        ;таймер сработал
RST   M109      ;сбрасываем индикатор 

Но самые большие трудности, как ни странно, у меня вызвали циклы. Расскажу чуть подробнее – это весело.

Цикл нужен, чтобы сделать полный оборот стола и остановиться. Тут меня немного сбил с толку код, написанный моими приятелями. Этот код использовал инструкции FOR-NEXT и пусть через пень-колоду, но работал. Начал я рефакторить код и столкнулся с проблемами. Пробую и так и сяк, и ничего не получается. Я даже задумался о полноте по Тьюрингу: что это за язык такой, в котором нельзя по-человечески цикл организовать! Долго бился, пока, наконец, меня не осенило: да ведь цикл-то и так есть, и это цикл исполнения программы в контроллере! Нужно просто использовать счётчик, поместив в него нужное количество шагов. Счётчик будет инкрементироваться на каждом прогоне программы. Тут, правда, есть некоторая тонкость: счётчик инкрементируется, когда его внутренний сигнал меняет своё состояние с 0 на 1, так что придётся менять состояние входного сигнала счётчика вручную. Когда счётчик полон, цикл надо считать законченным.

Я как-то в горячке упустил из виду, что если я буду пытаться использовать инструкции FOR-NEXT, очередной прогон программы не закончится до тех пор, пока не закончится мой цикл. Понятно, что управлять шаговым двигателем таким образом невозможно, ведь управляющие импульсы будут подаваться на двигатель только после завершения очередной итерации сканирования программы. А зачем же тогда предусмотрены инструкции FOR и NEXT? Ну, наверно, чтобы проинициализировать регистры, например.

Тем не менее, интереса ради я реализовал цикл в классическом виде (для его выполнения потребуется тысяча прогонов программы):

P     10        ;начало цикла
LD    M108
DINC  D0        ;тело цикла, инкрементируем регистр D0
LD>   D0  K1000 ;если D0 > 1000
CJ    P20       ;выходим
LD    M108
CJ    P10       ;переход на начало цикла
P     20
LD    M108
SET   Y10       ;выставляем выход Y10, чтобы убедиться, что цикл закончен

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

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

Производитель объяснил это вычислением момента переключения уровня сигнала STEP. C ростом скорости нужно чаще выполнять вычисления, данные операции являются максимально приоритетными, поэтому обработка программы пользователя уходит на второй план. Только при такой организации генератора STEP-сигнала, можно достичь широкого диапазона частот и реализовать опцию морфинга.

Для лучшей отзывчивости мне посоветовали использовать прерывания, но я решил проблему по рабоче-крестьянски – путём увеличения дельты, на которую изменяется скорость по мере её роста.

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

Выводы

  1. Порог вхождения невысок. Достаточно усвоить базовые концепции, и потом процесс программирования идёт легко.

  2. Возможности ПЛК весьма широки: можно реализовать довольно сложные и замысловатые программы. Есть, конечно, ограничения по количеству регистров, меркеров и указателей перехода, или вот, скажем, уровень вложенности подпрограмм в используемом контроллере не более 8, но не думаю, что это сколько-нибудь серьёзные ограничения.

Предчувствую вопрос: а не лучше ли воспользоваться Ардуино? Да, написать программу под Ардуино для среднестатистического программиста гораздо проще, чем разбираться с релейной логикой. Впрочем, не факт, что это будет проще для инженера АСУ.

ПЛК предназначены, как правило, для промышленного использования. Это значит, что они могут работать в более суровых условиях, они мощнее и могут использовать больше входов. ПЛК надёжны, масштабируемы, имеют длительный срок службы. Кроме того, к промышленным ПЛК могут предъявляться повышенные требования безопасности. Отсюда вытекает главный недостаток: цена.

Недавно стоимость контроллера существенно увеличилась, и мои друзья стали подумывать о замене. И Ардуино – один из вариантов. Всё-таки поворотные столы эксплуатируются в мягких условиях, и использовать дорогой ПЛК совсем необязательно. Планируется использовать связку контроллера с силовым драйвером и управлять двигателем посредством ШИМ.

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

Если вас заинтересовал поворотный стол, то вот видео:

Спасибо за внимание!

Комментарии (27)


  1. i360u
    15.11.2021 09:40

    https://habr.com/en/post/247315/ - кому интересно, есть такой открытый проект поворотного стола. На мой взгляд, решение гораздо более изящное, функциональное и доступное.


    1. HenryPootle
      15.11.2021 10:37
      +1

      Есть штука ещё проще - "Какой механизм, всё вручную" (с)
      https://www.thingiverse.com/thing:1762299


      1. i360u
        15.11.2021 11:20
        +1

        да, но эта штука уже никак не подходит для профессиональной съемки, в отличии от моего примера


      1. Mr_FatCat
        15.11.2021 14:21
        +1

        Если говорить о простоте https://youtu.be/QJL9etaMJ7Q


  1. vbifkol
    15.11.2021 10:52

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

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

    Зы: а реализовать все на реально механике не думали? Если ТЗ сводится к рулению скорости и возможности задания ограниченного количества углов поворота, возможно все свести к простым концевикам и делительному диску.


    1. AndreyRodin Автор
      15.11.2021 11:05

      Насчёт замены на сервопривод мои друзья тоже думают. Я, правда, не уверен, что мы найдём за 15 тысяч что-нибудь стоящее. Если найдём, это будет очень хорошо и решит все проблемы.

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


      1. vbifkol
        15.11.2021 12:27

        Я, правда, не уверен, что мы найдём за 15 тысяч что-нибудь стоящее.

        На али найдете.

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

        Ну да, в таком раскладе уже ну его.


  1. zhabr
    15.11.2021 11:24

    не проще взять старый проигрыватель винила?


    1. AndreyRodin Автор
      15.11.2021 11:32
      +2

      И поставить на него объект съёмки весом 300 кг?


      1. eurol
        15.11.2021 15:16

        А нельзя ли рассмотреть вариант вращения фотоаппарата вокруг объекта? Тогда и объекты массой в тысячи и миллионы тонн можно снимать без особых проблем. :)
        А то вспоминается анекдот про японцев, которым не нравится, что при скорости поезда 600 км/ч не удается полюбоваться пейзажем…


        1. AndreyRodin Автор
          15.11.2021 15:29

          Да, это тоже вариант. Правда, источники света придётся размещать на поворотной штанге вместе с фотоаппаратом. Конструкция получится несколько громоздкой.

          Штангу в любом случае придётся делать для съёмки 3D, где в отличие от съёмки 360°, объект надо снимать со всех точек.


  1. andersong
    15.11.2021 11:26
    +2

    пока, наконец, меня не осенило: да ведь цикл-то и так есть, и это цикл исполнения программы в контроллере!

    Тем не менее, интереса ради я реализовал цикл в классическом виде

    Основная проблема при переходе в из «классического» программирования в программирование ПЛК: программист начинает бороться со встроенным циклом ПЛК и обретает массу гемора)


  1. kolabaister
    15.11.2021 12:17

    У нас есть такой поворотный стол, покупали тоже у компании, занимающейся их серьезной разработкой. Судя по hid внутри адруина.


    1. ZiggiPop
      15.11.2021 15:25
      +1

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

      И что же мы там увидели?




      1. kolabaister
        15.11.2021 15:32

        Очень профессиональный и явно крайне дорогой итальянский клей)


  1. Mr_FatCat
    15.11.2021 12:23
    +2

    Использовал подобный принцип в своем первом прототипе поворотного стола в 2010 году. Для демонстрации конструкции сделал вот такой stop motion мультик https://youtu.be/f1cB4X1wI50
    Пару лет покрутил людей и предметы на таком столе, после этого стал смотреть в сторону Arduino.

    И вот что из этого вышло и этим можно пользоваться https://github.com/MakerDrive/PhotoPizza-DIY

    Если интересно, опишу реализованный функционал и планы на будущее.

    У вас правильные мысли по поводу использования Arduino. Обратите внимание, что также появилось куча альтернативных вариантов. В итоге я перешел на ESP32 и все еще смотрю по сторонам :)

    Шаговый двигатель шумит не сильно, если использовать дробление шага и высокое передаточное число приводного механизма.


    1. AndreyRodin Автор
      15.11.2021 12:42

      Спасибо, делиться опытом полезно.

      Да, думаем начать с Ардуино. Насчёт шума - пробовали разное дробление шага без особого успеха. В нашем случае использовался шаговый двигатель с редуктором и зубчатый венец. Похоже, основной шум идёт как раз от зубчатого венца.


      1. Mr_FatCat
        15.11.2021 13:05
        +3

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

        Это код для Arduino. Сейчас там реализован функционал:

        • Автоматическая съемка (блок управления автоматически управляет фотоаппаратом через синхропровод)

        • Вращение без остановки

        • Настройка скорости вращения в большом диапазоне

        • Плавное ускорение

        • Управление с помощью ИК пульта

        • Настройка количества кадров на один оборот

          В новой версии на ESP32:

        • Платформой можно управлять и настраивать параметры через WiFi (есть Web приложение, которое работает в любом современном браузере. Можно управлять телефоном или ПК) Параллельно с этим работает ИК пульт.

        • Пауза перед съемкой (используется для неустойчивых предметов, чтобы избежать смазанных кадров)

        • Пауза после съемки (используется для компенсации времени работы некоторого светового оборудования, беспроводных устройств управления фотоаппаратом)

        • Время выдержки фотоаппарата

        • Съемка без остановки вращения (для предметов, которые приходится подвешивать, к примеру велосипед, украшения, наушники) Сейчас исправляю небольшой баг, скоро обновлю

        • Синхронное управление напольной и подвесной системой

          В разработке:

        • Синхронизация с роборукой или роботизированным штативом

        • Режим для 3D-сканирования методом фотограмметрии

        • Управление по сценарию

        • Разработка приложения для автоматической обтравки и цветокоррекции фотографий

          Я к тому, что вы можете использовать этот открытый код проекта. Там реализовано все что вы планировали и даже немного больше.


      1. vbifkol
        15.11.2021 14:14

        Похоже, основной шум идёт как раз от зубчатого венца.

        зубчато-ременной привод спасет. заодно снизит требования к соосности, уберет резонанс и удар.


      1. Mr_FatCat
        16.11.2021 13:08

        Похоже, основной шум идёт как раз от зубчатого венца.

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


  1. kovserg
    15.11.2021 15:25

    Самый простой и готовый поворотный стол в микроволновке ;)


  1. ababo
    15.11.2021 21:53

    Как думаете, насколько сложнее/дороже было бы создать подобный поворотный стол, но способный двигать человека, причём в несколько раз быстрее, например, с периодом вращения 5-10 сек.?


    1. AndreyRodin Автор
      15.11.2021 22:17

      Есть несколько модификаций столов, в том числе и с нагрузкой до 300 кг. Можно и человека снимать. Правда, насчёт пяти секунд не знаю, боюсь, голова закружится.

      Гифки с addspace.ru:


    1. kovserg
      15.11.2021 22:20

      шуруповерт +
      image


  1. alex-open-plc
    16.11.2021 01:48

    IL - Аппаратно-независимый низкоуровневый ассемблероподобный язык (устарел, исключен в 3 редакции).
    https://ru.wikipedia.org/wiki/IEC_61131-3


    1. andersong
      16.11.2021 12:37

      Устарел? А мужики-то не знают!)))


      1. xp_hunter
        05.12.2021 15:40

        5 лет плотно отработал в АСУ ТП - ни разу его не видел. Есть Усатые телемастера с LD, есть технологи с FBD, есть программисты с ST. А IL - тема исключительно для олдовых любителей своего дела, в промышленность если такой выпустить - на месте такой код некому будет поддерживать. Никто со стороны заказчика такое оборудование не примет, если эксплуатация подразумевает возможную модернизацию или внесение изменений в оборудование.