Industry 4.0 · цифровые двойники станков и промышленной автоматики · отечественная R&D-разработка +7 925 353-56-35 info@synctwin.ru
Документация / Программы автоматики

Программы автоматики: устройство и примеры

Программа узла — машина состояний: шаги и переходы между ними по условию. Внутри шага оборудование работает ОДНОВРЕМЕННО, каждое на своей дорожке. Ниже — как это устроено, какие бывают условия и пороги, и три программы целиком, взятые из каталога архетипов дословно.

Скелет

Шаг и дорожка

machine({
  initial: 'загрузка',
  states: {
    'загрузка': {
      lanes: { X: [move(120, { v: 0.5 })], зажим: [on()], датчик: [waitFor('↑')] },
      entry: [on(5)], exit: [off(5)],        // мгновенно на входе и на выходе
      on: { 'датчик ↑': 'резка', 'X >= 120': 'резка' },
      after: { 8: 'авария' },                // предел времени в этом шаге
    },
    'резка': { wait: 2 },
  },
})
Вид шагаЧто этоОсобенность
обычныйдорожки оборудования, действия на входе и выходе, переходыдлится по самой долгой дорожке
составнойшаг со вложенными шагами: сам не исполняется, исполняются детиего переходы наследуются всеми вложенными — общая авария объявляется один раз
параллельныйзапускает несколько соседних шагов одновременно как независимые веткидве ветки, держащие одну ось или один порт, — это конфликт ресурса, и он называется
выдержкафиксированная пауза без оборудования вовседлительность объявлена полем, а не выведена из дорожек
ДорожкаЧто на неё кладётсяЗамечание
дорожка осиход в позицию · вращение · прижим моментом · торможение · удержаниедвижения кладутся только сюда
дорожка выходавключить · выключить · импульс · мигание фоном · удержаниепорт известен из имени дорожки
дорожка входаждать фронт или уровень · удержаниеожидание эластично: дорожка стоит, пока сигнала нет

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

Оборудование блока доступно по именам сразу. Код пишется словами — «лента», «зажим», «в позиции», — а с каким приводом и пином они связаны, знает паспорт стенда. Та же программа переносится на соседний участок сменой паспорта, а не переписыванием логики.

Счётчик — именованный объект программы. Один шаг прибавляет, другой сбрасывает; прибавка происходит на входе в шаг, ровно раз за проход. Памяти между пусками у счётчика пока нет: пуск начинает счёт с нуля.

Переходы

Условия: чем шаг заканчивается

Условия проверяются непрерывно, каждый такт — как скан ПЛК, а не как обработчик одиночного события. Срабатывает первый подошедший переход в порядке объявления.

ВидКак пишетсяЧто значит
шаг доигралdoneвсе дорожки шага закончились, включая эластичное ожидание входа
время в шаге'5s' · after: { 5: … }секунды с момента входа В ЭТОТ шаг, а не с начала программы
вход'датчик ↑' · 'датчик ↓' · 'датчик =1' · 'датчик =0'фронт или уровень; по имени из паспорта блока либо по номеру порта
позиция оси'X >= 90 град' · 'X >= 2 об'ось доехала; единица в строке решает, какая это величина
скорость оси'лента >= 1500 об/мин'ось разогналась
момент оси'пресс момент >= 380'ось нагрузилась. Без единицы число читается в ПРОМИЛЛЕ номинала: 380 — это 38 %
счётчик'счётчик детали >= 6'именованный счётчик программы: один шаг считает, другой сбрасывает
составное'деталь =1 и зажим =1' · 'или' · 'не'до пяти простых условий на переход; не влезло — это уже состояние, назовите его шагом
формула'|задатчик момент - ведомый момент| >= 120'арифметика над величинами: до трёх действий, делить можно только на число
уставка рецепта'пресс момент >= @момент'порог берётся из рецепта смены — программа одна, порог меняет выбор рецепта на пульте
аварияпереход по авариипревышение порога момента или рассогласования на любой оси шага
предел ожидания{ to: 'зажим', timeout_s: 5, on_timeout: 'error' }страховка условия, которое ждёт мира: не наступило за N секунд — исход называет автор (skip — продолжить · stop — остановить · error — отказ). Предела нет — шаг ждёт без конца, и лаборатория предупреждает об этом до пуска
ВеличинаЕдиницаЗамечание
позиция в ходе осиоборотыградусы переводятся конвертером: deg(90)
скорость вращенияоб/соб/мин переводятся конвертером: rpm(1500)
прижим моментом% номиналазнак задаёт направление
момент в условии перехода‰ номинала40 % прижима соответствует условию «момент >= 400»
времясекундывезде, включая пределы ожидания

Программу исполняет один из двух слоёв, и это видно по её содержимому. Чистая логика — выходы, ожидание входов, паузы, счётчики, формулы и рецепты — уезжает в логический слой и живёт ТАКТОМ стенда. Программа, которая двигает оси, исполняется плеером движения: генерация траектории — работа интерполятора, и логический слой такую программу принимать отказывается, называя конкретные блоки с движением.

Граница проведена явно в обе стороны. Условие по СЧЁТЧИКУ умеет только логический слой: плееру движения такая программа не отдаётся молча — он отказывает и называет слой, в который её надо доставить. Составное условие, формула и порог-уставка — из того же логического набора.

Готовится. Предел ожидания у ПЕРЕХОДА: автор задаёт предел и его исход ПАРОЙ — продолжить, остановить программу или объявить отказ. Половина пары не принимается: исход без предела никто никогда не исполнит, предел без исхода сработает молча. Там же — предупреждение при пуске: если выйти из шага можно только по внешнему условию и предел не назван, об этом сказано ДО пуска, а не выясняется зависанием. Сделано, на промышленный контур ещё не выкачено.

Аварии

Пороги и что считается аварией

ПределПо умолчаниюЗамечание
порог момента600 ‰ номиналапревышение считается аварией, только если ДЕРЖИТСЯ дольше выдержки — на разгоне момент штатно уходит за предел
порог рассогласования0,05 оборотана виртуальных осях не проверяется вовсе: рассогласование там — штатная работа модели, а не факт привода
выдержка аварии0,2 ссколько превышение должно продержаться

Пределы едут вместе с программой одним объектом: перегруз — свойство механизма, а не отдельного шага. На превышение можно объявить переход; переход не объявлен — останов с названной причиной.

Момент на виртуальных осях считает МОДЕЛЬ, а не привод. Он выводится из рассогласования «велено» и «есть»: свободный ход — момент холостого хода, нагрузка — рост пропорционально ошибке, упор — насыщение на уставке программы. Это годится, чтобы отладить логику поиска упора и затяжки, но замером не является и цитировать его как факт нельзя.

Нет паспортного номинала — в кадре ПРОЧЕРК, и это правильно. Шкала промилле отсчитывается от номинала двигателя; номинал не задан — точки отсчёта нет, и платформа показывает прочерк вместо правдоподобного числа. «Перегруз» при этом не объявляется: сравнивать не с чем. Условие перехода по моменту на такой оси не сработает — гоняйте такую программу на железе, там момент приходит от привода.

Пример 1

Подача и затяжка с ожиданием датчика

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

Питатель

rig({
  axes:    { толкатель: 0 },
  outputs: { отсекатель: 0 },
  inputs:  { наличие: 0 },
})

machine({
  title: 'Питатель',
  initial: 'ждём заготовку',
  states: {
    'ждём заготовку': {
      lanes: { наличие: [waitFor('↑')] },
      on: { done: 'выдача' },
    },
    'выдача': {
      entry: [on(0)],
      lanes: { толкатель: [move(2, { v: 0.6 })] },
      on: { done: 'отсечь' },
    },
    'отсечь': {
      entry: [off(0)],
      lanes: { толкатель: [move(0, { v: 1 })] },
      on: { done: 'ждём заготовку' },
    },
  },
})

Завинчивание

rig({
  axes:    { шпиндель: 0, 'подача Z': 1 },
  outputs: { 'питатель винта': 0 },
  inputs:  { 'винт подан': 0 },
})

machine({
  title: 'Завинчивание',
  initial: 'подать винт',
  states: {
    'подать винт': {
      entry: [pulse(0.3, 0)],                          // импульс на питатель
      lanes: { 'винт подан': [waitFor('↑', { timeout: 5, onTimeout: 'error' })] },
      on: { done: 'подвод' },
    },
    'подвод': {
      lanes: { 'подача Z': [move(2, { v: 0.6 })] },
      on: { done: 'завинчивание' },
    },
    'завинчивание': {
      lanes: { шпиндель: [push(60, { sec: 2 })] },      // 60 % номинала, держать 2 с
      on: { done: 'отвод' },
    },
    'отвод': {
      lanes: { 'подача Z': [move(0, { v: 1 })] },
      on: { done: 'подать винт' },
    },
  },
})

Ожидание входа эластично: дорожка стоит, пока сигнала нет, и время шага плывёт за фактом. Предел ожидания здесь назван явно — пять секунд; молчаливое «доиграл по времени» и «деталь пришла» — разные вещи, и различать их обязан автор.

Затяжка задана моментом, а не координатой: 60 % номинала, удержание две секунды. Резьба кончается там, где кончается, — по координате это не выражается.

Пример 2

Поворотный стол с индексацией

Цикл делительного стола: расфиксировать, повернуть на позицию, зафиксировать, выдержать пост. Показывает приём «два перехода в один шаг»: пришли по датчику — уходим сразу, датчика нет — по окончании хода.

rig({
  axes:    { стол: 0 },
  outputs: { фиксатор: 0 },
  inputs:  { 'в позиции': 0 },
})

machine({
  title: 'Поворотный делительный стол',
  initial: 'открыть фиксатор',
  states: {
    'открыть фиксатор': {
      entry: [off(0)],
      lanes: { стол: [hold(0.1)] },
      on: { done: 'поворот' },
    },
    'поворот': {
      lanes: { стол: [move(deg(60), { rel: true, v: 0.3 })] },
      // Два перехода в один шаг: пришли по датчику — идём сразу; датчика
      // нет — уходим по окончании хода. Первый подошедший и срабатывает.
      on: { 'в позиции ↑': 'фиксация', done: 'фиксация' },
    },
    'фиксация': {
      entry: [on(0)],
      lanes: { стол: [hold(0.3)] },
      on: { done: 'выдержка' },
    },
    'выдержка': {
      wait: 0.5,
      on: { done: 'открыть фиксатор' },
    },
  },
})
Пример 3

Перекладка: взять и уложить

Двухосевой узел: ждём деталь, опускаемся, берём, переносим, кладём, возвращаемся. Захват здесь подтверждается выдержкой — у блока есть вход «захват сомкнут», и в своём узле его стоит поставить на место паузы.

rig({
  axes:    { X: 0, Z: 1 },
  outputs: { захват: 0 },
  inputs:  { 'деталь есть': 0 },
})

machine({
  title: 'Pick&Place',
  initial: 'ждём деталь',
  states: {
    'ждём деталь': {
      lanes: { 'деталь есть': [waitFor('↑')] },
      on: { done: 'опуск забора' },
    },
    'опуск забора':  { lanes: { Z: [move(2, { v: 0.6 })] }, on: { done: 'захват' } },
    'захват':        { entry: [on(0)],  lanes: { Z: [hold(0.3)] }, on: { done: 'подъём' } },
    'подъём':        { lanes: { Z: [move(0, { v: 1 })] },   on: { done: 'перенос' } },
    'перенос':       { lanes: { X: [move(6, { v: 0.8 })] }, on: { done: 'опуск укладки' } },
    'опуск укладки': { lanes: { Z: [move(2, { v: 0.6 })] }, on: { done: 'отпуск' } },
    'отпуск':        { entry: [off(0)], lanes: { Z: [hold(0.3)] }, on: { done: 'подъём после укладки' } },
    'подъём после укладки': { lanes: { Z: [move(0, { v: 1 })] }, on: { done: 'возврат' } },
    'возврат':       { lanes: { X: [move(0, { v: 1 })] },   on: { done: 'ждём деталь' } },
  },
})

Выдержка вместо подтверждения — это заглушка, а не решение. Пауза на глазок выдаёт «команда подана» за «взял». Роль входа grip_ok заведена ровно затем, чтобы условие перехода звучало как «захват сомкнут», а не «прошло 0,3 секунды».

Приём

Ехать до упора и остановиться по усилию

rig({ axes: { зажим: 0 }, outputs: { готово: 0 } })

machine({
  title: 'Зажать до упора',
  initial: 'подвод',
  states: {
    'подвод': {
      lanes: { зажим: [move(3, { v: 0.5 })] },
      // 300 ‰ = 30 % номинала. Момент читается с самого привода —
      // отдельный датчик усилия для этого не нужен.
      on: { 'зажим момент >= 300': 'упор', done: 'упор' },
    },
    'упор': {
      lanes: { зажим: [push(40, { sec: 1 })] },
      entry: [on(0)],
      on: { done: 'отвод' },
    },
    'отвод': {
      entry: [off(0)],
      lanes: { зажим: [move(0, { v: 0.8 })] },
      on: { done: 'подвод' },
    },
  },
})

Единицы здесь разные намеренно: прижим говорит в процентах номинала, условие перехода — в промилле. 40 % прижима соответствует условию «момент >= 400».

Состав узлов и словарь ролей каналов — на странице блоков автоматики. Путь навесного органа робота — в глаголах движения.