В логах запуска нашего стенда давно живёт строка:
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 — и разный результат, смотря есть на шине SYNC или нет.
Состояние перешло — это ещё не здоровье
Привод по 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. Виноват оказался сам компьютер: на машине с мастером параллельно крутились тяжёлые сборки, и спайки планировщика срывали цикл. Разгрузили машину — всё прошло. Формально каждый слейв честно сообщал своё состояние, и ни одно из этих состояний не говорило «у вашего мастера нет времени на шину».
Железо сделало: замер не должен проверять сам себя
Верхняя ступень — единственная, где вопрос закрывается: вал действительно крутится так, как велели? Здесь своя ловушка.
Когда мы калибровали шкалу скорости VD3E, первым желанием было прочитать скорость обратно из привода — объект 0x606C — и сравнить с уставкой. Нельзя: обратная скорость проходит через ту же шкалу, которую мы проверяем. Такой замер подтвердит любую ошибку — обе величины посчитаны одной формулой.
Мерить надо по сырому счётчику вала (0x6064) и обычным часам: задал скорость, взял прирост за интервал, поделил.
Для этой статьи мы собрали такой замер в отдельный скрипт — сотня строк python поверх утилиты ethercat, без LinuxCNC, без PDO-цикла, без вендорного софта. Все скрипты из этой статьи выложены здесь: sdo-probing, там же разбор каждой грабли. Попутно выяснилась приятная вещь: VD3E разрешает пройти всю лесенку CiA 402 и крутить мотор в Profile Velocity одними SDO прямо из PREOP [замерено] — Мастер IgH при этом нужен — скрипт зовёт его утилиту, — но довольно фазы Idle: ни 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 мс на составляющие — медианы двадцати пяти повторов [замерено]:
Операция | Время |
|---|---|
запуск процесса ( | 0.57 мс |
| 1.5 мс |
| 1.4 мс |
| 5 мс |
| 32 мс |
переход лесенки CiA 402 (= две транзакции) | 64 мс |
Запуск процесса — полмиллисекунды. Утилита ethercat со всей инициализацией — полтора. Чтение состояния мастера, которое не трогает шину, — те же полтора. А одна SDO-транзакция — 32 миллисекунды, в двадцать с лишним раз дороже всего локального.
Дело не в скорости провода: мейлбокс обслуживается мастером в его собственном ритме, и у мастера, который просто стоит в Idle без циклического обмена, этот ритм неспешный. То есть 32 мс характеризуют режим, в котором мы спрашиваем. К скорости привода и к EtherCAT это число отношения не имеет. Сколько это будет при поднятом цикле — мы не мерили [не знаем], но ожидаем заметно быстрее.
Мы читаем у привода полный паспорт — три сотни параметров — и делаем это по SDO. Время растёт линейно:
Сколько читаем | Время | Что это |
|---|---|---|
60 параметров | 1.97 с | реальный замер |
109 | 3.5 с | объектов в словаре VD3E |
300 | 9.6 с | паспорт привода целиком |
437 | 14 с | все читаемые записи с субиндексами |
Для разового автодискавери при вводе в эксплуатацию это нормально. Для «обновлять раз в секунду на живой шине» — нет, и хорошо, что мы выяснили это цифрой, а не на объекте.
Перечислить весь словарь объектов (ethercat sdos) стоит 5 миллисекунд — мастер отдаёт его из своего кеша, собранного при сканировании шины.
Насколько привод способен рассказать о себе
От этого зависит, можно ли ввести привод в эксплуатацию автоматически или придётся описывать его руками.
У привода есть два способа представиться. Первый — сервис SdoInfo: мастер спрашивает «перечисли свои объекты», привод отвечает списком. Второй — конкретный объект 0x608F, в котором по стандарту лежит разрешение энкодера, самое нужное число для расчёта масштабов.
Четыре привода на нашем стенде — четыре разных ответа [замерено]:
Привод | Словарь по SdoInfo |
| Битность | Режимы |
|---|---|---|---|---|
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, — знание про привод, и в тестах его не было. Зелёный тест уровня «строка есть» — это ack: он подтверждает работу нашего генератора, но молчит о том, съест ли конфигурацию железо.
С тех пор к каждому такому тесту мы мысленно приписываем ступень из таблицы выше.
Что у Wecon получилось хорошо
Раз уж VD3E дал половину примеров, скажу о нём отдельно: по-русски об этом приводе почти ничего нет.
Сначала о том, как он вообще попал на стенд. Мы написали производителю напрямую, тогда ещё как частное лицо — без юрлица, без партнёрских статусов, просто «хотим попробовать ваш EtherCAT-серво». Ожидали вежливого отказа или требования минимальной партии. Оказалось, минимальная партия — одна штука: менеджер собрала комплект (мотор с 23-битным энкодером и тормозом + привод + три кабеля), оплата прошла через их экспедитора, и коробка из Фуцзяни доехала до стенда. Весь комплект 750 Вт обошёлся в 26 тысяч рублей с доставкой до Москвы [замерено на своём кошельке]. Документация у завода открытая — онлайн-портал docs.we-con.com.cn: оттуда мы брали и мануал VD3E с картой суффиксов энкодера, и прошивочные архивы. ESI-файлы и конфигуратор — на прямой странице Servo → Download, её нам прислал сам завод в ответ на вопрос. Контакты завода публиковать здесь не буду; кому интересно повторить — пишите в личку, поделюсь.
Плюсы:
словарь объектов честно читается с шины (SdoInfo), и он же целиком лежит в ESI — каталог параметров собирается офлайн, без железа;
вся лесенка CiA 402 работает по SDO из PREOP — привод проверяется скриптом при мастере в Idle, без циклического обмена и 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/.

