Привод ответил ACK. Что он на самом деле пообещал?

от автора

В логах запуска нашего стенда давно живёт строка:

Failed SDO download 0x6060 (-22)

Мы её лечили. Всерьёз: разбирали шаблон конфигурации, спорили, куда переносить запись режима, прикидывали обходные пути. Потом заметили то, с чего стоило начать: та же строка печатается и в удачных запусках. Оси выходят в Operation enabled, статусворды чистые, станок ездит — а строка на месте. Ошибка оказалась шумом. Время на её лечение — настоящим.

Это второй текст про наш стенд (первый — про то, почему EtherCAT в LinuxCNC держится на компоненте из 9 коммитов). В комментариях к нему я пообещал разобрать тему «принял ≠ сделал»: почему подтверждение на полевой шине почти никогда не означает того, что от него ждёт человек, глядящий в лог. Обещал — разбираю. Всё на живых примерах с того же стенда: три сервопривода Inovance IS620N в режиме CSP, сервопривод Wecon VD3E в режиме скорости (он у нас играет роль шпинделя — настоящего шпинделя на стенде нет), каплер Omron под дискретное IO, LinuxCNC поверх IgH-мастера.

Пометки те же, что в прошлый раз: [замерено] — проверили железом, [из паспорта] — из документации, [не знаем] — честно не знаем.

Четыре разных «принял»

Когда мастер что-то пишет в привод, подтверждений на пути несколько, и каждое отвечает на свой узкий вопрос. Беды начинаются, когда ответ одной ступени принимают за ответ другой.

Ступень

Что подтверждает

Чем проверяется

На что НЕ отвечает

Кадр вернулся

слейв жив, датаграмма прошла по кольцу

working counter (WKC)

принял ли слейв данные

SDO ack

объект записан в словарь

ответ мейлбокса

применилось ли значение

Состояние перешло

машина состояний сменила ступень

statusword, 0x6061

здорова ли система в целом

Железо сделало

вал крутится, как велели

независимый замер

Каждая ступень нас в своё время укусила. Дальше — по порядку, с укусами.

SDO ack: ошибка бывает в обе стороны

Начнём с той самой строки. -22 — это EINVAL из ядра Linux. У наших приводов она появляется, когда объект уже замаплен в PDO: привод отвергает SDO-запись в то, что и так едет циклическим обменом. Диагноз, замечу, поставлен по поведению, а не по документации — ни Inovance, ни Wecon этот случай в мануалах не описывают. Работает ли то же правило у других вендоров — [не знаем].

Практический вывод скучный, но дорогой: увидев ошибку в логе, сначала проверьте, печатается ли она в успешных прогонах. У нас это отсекло бы пару вечеров работы.

Интереснее зеркальный случай: ack есть, а дела нет. Классика профиля CiA 402 — режим работы. Запрашиваешь его записью в 0x6060 (Modes of operation), а фактический режим привод показывает в другом объекте — 0x6061 (Modes of operation display). Запись в 0x6060 может пройти с ack и не изменить ничего: привод вправе не принять режим, который не поддерживает или не может включить сейчас. Правильная процедура — написал в 0x6060, жди совпадения в 0x6061. Пара «команда в одном объекте, факт в другом» в профиле повсюду: controlword/statusword устроены так же, и это не бюрократия, а честность — команда и состояние принципиально разные вещи.

Третья история про ту же ступень — сброс ошибок. На Wecon VD3E мы снимали Er.40 (протухшая батарея мультиоборотного энкодера при первом включении) прямо по шине, из PREOP, через мейлбокс [замерено]: запись 0x200A:06 = 1 сбрасывает счётчик оборотов энкодера, затем 0x200A:03 = 1 снимает сам fault. Обе записи возвращают ack независимо от того, наступил ли эффект. Порядок важен, а после сброса счётчика позиция прыгает — дальше обязателен homing. Ack на каждую команду при этом безупречен.

А самый чистый случай нам подарили приводы Inovance. Однажды все три разом зажгли Er.E08. По шине читается 0x603F = 0x0E08, statusword 0x0218 — бит fault поднят. В мануале это EE08.0, «потеря сигнала SYNC», и там же чёрным по белому: сбрасываемая.

Шлём fault reset. Запись проходит, ошибок нет. Statusword не меняется. Шлём ещё раз — тот же результат.

Причина оказалась в том, что фолт сетевой. Приводы стояли в OP и ждали сигнал синхронизации, а мы уронили мастер, не сняв с них перед этим enable. SYNC исчез — все три защёлкнули ошибку. Снять её командой по той же сети, которая лежит, невозможно: условием снятия является восстановленный SYNC, а его нет. Ack при этом приходит исправно — мейлбокс-то живой, он и принял сообщение. Принял, записал, подтвердил. Не сделал.

Лечится двумя способами [замерено]: передёрнуть питание приводов (энкодеры абсолютные, привязка нуля не теряется) либо поднять полноценный циклический мастер с валидным DC — и тогда ровно та же команда, которая только что была бесполезна, сбрасывает фолт штатно.

Это стоит запомнить именно в такой формулировке: смысл команды определяется состоянием системы, а не фактом её приёма. Одинаковый ack, одинаковые байты, разный результат.

Состояние перешло — это ещё не здоровье

Привод по CiA 402 включается лесенкой: Shutdown → Switch on → Enable operation, команды уходят словом controlword, ответ читается битами statusword. Отправленный controlword не значит ничего — состояние сменится через несколько тактов, а может и не смениться. Ждать нужно биты, и ждать с таймаутом. Этим, собственно, и занимается компонент cia402.comp из первой статьи.

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

Но самый поучительный укус был выше по лестнице. Приводы то доходили до OP, то отваливались, в dmesg сыпалось:

Unexpected realtime delay on task 0 with period 1000000

Мы копали HAL. Потом период. Потом порядок функций в servo-thread. Виноват оказался сам компьютер: на машине с мастером параллельно крутились тяжёлые сборки, и спайки планировщика срывали цикл. Разгрузили машину — всё прошло. Формально каждый слейв честно сообщал своё состояние, и ни одно из этих состояний не говорило «у вашего мастера нет времени на шину». OP — это про слейва. Про здоровье системы не спрашивайте слейва, он не в курсе.

Железо сделало: замер не должен проверять сам себя

Верхняя ступень — единственная, где вопрос закрывается: вал действительно крутится так, как велели? Здесь своя ловушка, и она тоньше предыдущих.

Когда мы калибровали шкалу скорости VD3E, первым желанием было прочитать скорость обратно из привода — объект 0x606C — и сравнить с уставкой. Нельзя: обратная скорость проходит через ту же шкалу, которую мы проверяем. Такой замер подтвердит любую ошибку — обе величины посчитаны одной формулой.

Мерить надо по сырому счётчику вала (0x6064) и обычным часам: задал скорость, взял прирост за интервал, поделил.

Для этой статьи мы собрали такой замер в отдельный скрипт — сотня строк python поверх утилиты ethercat, без LinuxCNC, без PDO-цикла, без вендорного софта. Все скрипты из этой статьи выложены здесь: sdo-probing, там же разбор каждой грабли. Попутно выяснилась приятная вещь: VD3E разрешает пройти всю лесенку CiA 402 и крутить мотор в Profile Velocity одними SDO прямо из PREOP [замерено] — проверить привод можно голым скриптом с любого линукса, realtime-мастер не нужен. Сам скрипт устроен по чеклисту этой статьи: пишет 0x6060 — ждёт 0x6061, шлёт controlword — ждёт биты statusword, а «мотор остановлен» проверяет чтением statusword, не фактом отправки команды.

Результаты. На уставках 0.2–0.5 об/с заданная и измеренная скорости сошлись с точностью +0.01% [замерено] — привод отрабатывает уставку в counts/s очень точно. Оговорюсь сразу, потому что это важно и я сам споткнулся об это ниже: такой замер подтверждает линейность и стабильность, но не абсолютное число отсчётов на оборот — уставку я задавал через ту же константу, на которую потом делил. Абсолютную шкалу мы подтвердим в конце статьи, и совсем другим способом. Внятного ответа про единицы (counts/s) в документации нет — добывали замером.

А дальше — история о том, как мы сами чуть не попались на нарушении собственного чеклиста.

Первый прогон малых уставок дал пугающие числа: на 0.01 об/с среднее разошлось с уставкой на 6%. «Шкала плывёт на малых оборотах» — вывод уже почти поехал в текст. Потом посмотрели на график мгновенной скорости: первые полсекунды мотор разгоняется, и весь этот разгон сидел внутри окна усреднения. Выкинули из окна первую секунду — на 0.5 об/с осталось +0.01%, а на 0.01 об/с — минус 3.5% [замерено], и это уже не разгон, а рябь регулятора: период её колебания около двух секунд [по графику], пятисекундное окно ловит неполные периоды и не даёт ей усредниться.

Мгновенная скорость подтвердила диагноз: на 0.2 об/с она держится в пределах ±5% от уставки, а на 0.01 об/с гуляет ±12% [замерено]. Отсюда два правила: масштаб меряется на приличной скорости, где рябь тонет в сигнале, и окно замера не должно содержать разгон.

Редуктор, которого мы чуть не объявили декоративным

Пока мотор был под рукой, проверили объект 0x6091 (gear ratio) — стандартный электронный редуктор профиля. У обоих наших вендоров он с завода 1:1 [замерено]. Пишем в числитель двойку: запись проходит, обратное чтение честно показывает 2. Повторяем замер скорости — числа ровно те же, что при 1:1.

Вывод напрашивался: объект принимается, подтверждается чтением и ни на что не влияет. Красивая иллюстрация к теме статьи, уже почти уехавшая в текст.

Она была неверной. Наш замер не мог увидеть эффект по построению: в Profile Velocity уставка скорости и обратная связь по позиции масштабируются одним и тем же коэффициентом, он сокращается, и отношение остаётся прежним при любом редукторе. Мы второй раз за одну статью померили шкалу самой шкалой — теперь уже зная про эти грабли и всё равно в них наступив.

Нужен был эталон снаружи цепочки, и самый честный из доступных — рука. Момент снят, вал свободен, метка на валу, скрипт только смотрит счётчик. Пять оборотов рукой:

Настройка 0x6091

Прирост счётчика за 5 оборотов

На один оборот

1:1

42 203 264

8 440 653 (номинал 8 388 608, +0.6%)

2:1

20 787 053

4 157 411 (ожидалось 4 194 304, −0.9%)

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

Попутно рука закрыла и второй вопрос. Разрешение энкодера привод по шине не сообщает: стандартные объекты 0x608F (encoder increments) и 0x6092 (feed constant) у VD3E отсутствуют — «object does not exist» [замерено]. То есть 8 388 608 было у нас только из паспорта. Теперь оно подтверждено физически, с точностью 0.6% на пяти оборотах рукой.

Мелочь напоследок: неподвижный вал даёт дрожание счётчика в пределах ±7 отсчётов из 8 388 608 [замерено]. Три десятитысячных градуса — разрешение, на котором виден электрический шум.

Сколько стоит спросить привод

Раз уж «ack приходит мгновенно, а дело делается позже» — попробуем измерить это самое «позже». Двадцать раз проходим лесенку CiA 402 и засекаем, сколько миллисекунд проходит от записи controlword до появления нужных битов в statusword.

Результат: ровно 64 мс на каждой ступени, на всех двадцати прогонах, с разбросом в единицы миллисекунд. Подозрительно ровно.

Так и есть: 64 — это две наши же SDO-транзакции, запись и чтение. Одно пустое чтение statusword стоит 32 мс, и весь замер упёрся в собственный прибор. Привод переключается быстрее, чем мы способны заметить; насколько быстрее — этим методом [не знаем].

Зато попутно выяснилось кое-что полезнее. Раскладываем эти 32 мс на составляющие — медианы двадцати пяти повторов [замерено]:

Операция

Время

запуск процесса (/bin/true)

0.57 мс

ethercat version

1.5 мс

ethercat master — состояние, шину не трогает

1.4 мс

ethercat sdos — перечислить весь словарь

5 мс

ethercat upload — прочитать один параметр

32 мс

переход лесенки CiA 402 (= две транзакции)

64 мс

Запуск процесса — полмиллисекунды. Утилита ethercat со всей инициализацией — полтора. Чтение состояния мастера, которое не трогает шину, — те же полтора. А одна SDO-транзакция — 32 миллисекунды, в двадцать с лишним раз дороже всего локального.

Дело не в скорости провода: мейлбокс обслуживается мастером в его собственном ритме, и у мастера, который просто стоит в Idle без циклического обмена, этот ритм неспешный. То есть цифра характеризует не привод и не EtherCAT, а режим, в котором мы его спрашиваем. Сколько это будет при поднятом цикле — мы не мерили [не знаем], но ожидаем заметно быстрее.

Практический вывод из этого куда важнее исходного вопроса. Мы читаем у привода полный паспорт — три сотни параметров — и делаем это по SDO. Время растёт линейно:

Сколько читаем

Время

Что это

60 параметров

1.97 с

реальный замер

109

3.5 с

объектов в словаре VD3E

300

9.6 с

паспорт привода целиком

437

14 с

все читаемые записи с субиндексами

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

Забавный контраст: перечислить весь словарь объектов (ethercat sdos) стоит 5 миллисекунд — мастер отдаёт его из своего кеша, собранного при сканировании шины. Спросить структуру дёшево, спросить значения дорого.

Насколько привод способен рассказать о себе

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

У привода есть два способа представиться. Первый — сервис SdoInfo: мастер спрашивает «перечисли свои объекты», привод отвечает списком. Второй — конкретный объект 0x608F, в котором по стандарту лежит разрешение энкодера, самое нужное число для расчёта масштабов.

Четыре привода на нашем стенде — четыре разных ответа [замерено]:

Привод

Словарь по SdoInfo

0x608F (разрешение энкодера)

Битность

Режимы 0x6502

Mitsubishi MR-J4-20TM

809 объектов

отдаёт: 4 194 304

22

0x3AD

Schneider LXM28E

не отдаёт

отдаёт: 1 048 576

20

0xED

Wecon VD3E

109 объектов

объекта нет

23

0x3AD

Inovance IS620N

не отдаёт

объекта нет

23

0x3AD

Mitsubishi выгружает почти весь свой параметрический мир — та самая «тысяча параметров», которой славится серия. Wecon отдаёт компактный словарь ровно в 109 объектов — столько же, сколько объявлено в его ESI: заявленное совпало с фактическим, что бывает не всегда. Schneider словарь не перечисляет, зато честно сообщает разрешение энкодера. А Inovance молчит по обоим каналам: объекты у него есть и читать их можно, но ни перечислить себя, ни назвать разрешение он не умеет — потому в экосистеме LinuxCNC под него и живут плагины со списками объектов, вбитыми из документации.

Обратите внимание: ни один не самоописывается полностью, и ломается каждый в своём месте. Идея «подключил привод — мастер сам всё узнал» разбивается именно здесь, а не в протоколе.

Зато 0x6502 — маска поддерживаемых режимов — у Inovance, Mitsubishi и Wecon совпадает до бита: 0x3AD, то есть PP, PV, TQ, homing, CSP, CSV и CST. Три вендора, три ценовых сегмента, одинаковый набор возможностей. У Schneider маска другая (0xED): циклических режимов по скорости и моменту у него нет вовсе, только CSP. Это к вопросу о том, что «поддерживает CiA 402» — фраза с очень разным наполнением.

(Schneider на момент замеров стоял обесточенным, его числа — с июньской сессии, когда он был на шине. Остальные три перепроверены сегодня.)

Та же лестница — в наших собственных тестах

Честное признание из прошлой статьи, теперь с подробностями. Нам захотелось больше телеметрии, и мы добавили приводу второй TxPDO. Тесты конфигурации — зелёные. Запуск — слейв падает.

Тесты проверяли, что нужные строки присутствуют в сгенерированном XML. Строки присутствовали. А то, что IS620N принимает только один TxPDO, — знание не про наш XML, а про привод, и в тестах его не было. Зелёный тест уровня «строка есть» — это ack: он подтверждает работу нашего генератора, но молчит о том, съест ли конфигурацию железо. Форма допустимого живёт в приводе.

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

Что у Wecon получилось хорошо

Раз уж VD3E дал половину примеров, скажу о нём отдельно — тем более что хвалить есть за что, а по-русски об этом приводе почти ничего нет.

Сначала о том, как он вообще попал на стенд. Мы написали производителю напрямую, тогда ещё как частное лицо — без юрлица, без партнёрских статусов, просто «хотим попробовать ваш EtherCAT-серво». Ожидали вежливого отказа или требования минимальной партии. Оказалось, минимальная партия — одна штука: менеджер собрала комплект (мотор с 23-битным энкодером и тормозом + привод + три кабеля), оплата прошла через их экспедитора, и коробка из Фуцзяни доехала до стенда. Весь комплект 750 Вт обошёлся в 26 тысяч рублей с доставкой до Москвы [замерено на своём кошельке]. Для тех, кто хочет пощупать EtherCAT-серво руками, не выпрашивая бюджет, — барьер входа ниже, чем принято думать. Документация у завода открытая — онлайн-портал docs.we-con.com.cn: оттуда мы брали и мануал VD3E с картой суффиксов энкодера, и прошивочные архивы. ESI-файлы и конфигуратор — на прямой странице Servo → Download, её нам прислал сам завод в ответ на вопрос. Контакты завода публиковать здесь не буду; кому интересно повторить — пишите в личку, поделюсь.

Плюсы:

  • словарь объектов честно читается с шины (SdoInfo), и он же целиком лежит в ESI — каталог параметров собирается офлайн, без железа;

  • вся лесенка CiA 402 работает по SDO из PREOP — привод проверяется голым скриптом, без realtime-мастера [замерено];

  • 0x603F отдаёт номер ошибки как на индикаторе (прочитал 0x28 — это Er.40), сброс ошибок — тоже по шине из PREOP;

  • режим момента живой: 0x6071 маппится в PDO [из паспорта];

  • 23-битный абсолютный многооборотный энкодер: 8 388 608 отсчётов на оборот подтверждены физическим замером (+0.6% на пяти оборотах рукой);

  • 0x6091 (электронный редуктор) работает строго по стандарту [замерено];

  • завод отвечает на технические вопросы: наши ответы про SdoInfo и битность энкодера пришли от их инженера, с прямыми ссылками на файлы.

Минусы (по большей части формальности, но знать их надо заранее):

  • нет SDO Complete Access — параметры с субиндексами пишутся по одному;

  • ESI строго парный к прошивке: мастер сличает vendor/product/revision, свежая прошивка без свежего ESI не поднимется [из паспорта];

  • единицы скорости (counts/s) внятно не документированы — мы добывали их замером;

  • один TxPDO, как и у Inovance: вся циклическая телеметрия — в один набор;

  • разрешение энкодера по шине не сообщается: 0x608F и 0x6092 отсутствуют в словаре [замерено] — автоматике придётся брать его из ESI или из паспорта;

  • первый пуск мультиоборотного энкодера встречает Er.40 (батарея) — лечится по шине, но об этом надо знать.

Чего мы до сих пор не знаем

  • Что означает -22 на SDO-записи у других вендоров. У наших двух это «объект замаплен в PDO», и то — диагноз по поведению.

  • Есть ли у Er.E08 штатный путь снятия без поднятия полного циклического мастера и без передёргивания питания — в мануале мы его не нашли, вопрос вендору отправлен.

  • Джиттер под мейлбокс-нагрузкой. Мы читаем полный паспорт привода — три сотни параметров по SDO — на живой шине, вперемешку с циклическим трафиком, и ни разу не мерили, что это делает с джиттером. Вопрос прилетел в комментариях к прошлой статье и уже стоит в списке замеров.

  • Шаг коррекции Distributed Clocks. В комментариях называли 20 нс — не проверяли, врать не будем.

  • Природу двухсекундной ряби на сверхмалых оборотах: регулятор, механика, дискретность — не разбирали.

  • Передаточное число механики. В нашей модели станка у шпинделя записан редуктор 1:50, и проверить его сейчас нечем: на стенде мотор с голым валом, никакого шпинделя за ним нет. Всё измеренное здесь живёт до редуктора. Пока это число — замысел, а не факт, и помечено соответственно.

Чеклист вместо выводов

  • Ack транспорта ≠ запись. Запись ≠ применение. Применение ≠ здоровье. Здоровье ≠ результат.

  • Написал в 0x6060 — прочитай 0x6061. Послал controlword — жди биты statusword, с таймаутом.

  • «Момент снят» — это биты statusword, а не отправленная команда.

  • Ошибка в логе ≠ отказ: сначала проверь, нет ли её в успешных прогонах.

  • OP у слейва ≠ здоровье системы: слейв не знает, что твой мастер перегружен.

  • Замер не имеет права проходить через проверяемую шкалу. Эталон ищите снаружи цепочки — иногда это просто рука на валу.

  • Тест «строка в конфиге есть» проверяет твой генератор, а не привод.

Скрипты всех замеров из этой статьи — в папке sdo-probing: проверка шкалы, замер редуктора рукой на валу, тайминг лестницы CiA 402. Запускаются без нашего софта, приводу достаточно PREOP. Рядом лежат рабочие конфигурации стенда — XML для lcec, HAL, кинематика, настройка реального времени в виртуалке, — и разбор граблей к каждой. Карты объектов по вендорам собраны в открытый реестр: synctwin.ru/ethercat/.

Мы делаем платформу, где станок описывается цифровым двойником, а конфигурация LinuxCNC генерируется из него — но это уже другая история.

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