На заводах ИИ мы внедрять умеем, а нормального кота сгенерить не смогли. Штош
Комментариев под прошлым постом было много, ответить каждому мы физически не успеем. Но ответить хочется. Поэтому короткий инженерный разбор: с цифрами, фактами и долей самоиронии.
Про “чёрный короб”
Да, идея хорошая и часто действительно спасает. Но у нас было три метра конвейера, по которому летят зеркальные листы со скоростью 2 м/с. А пыль, тепло, вибрации и необходимость обслуживать оптику делают «чёрный ящик» скорее лабораторным решением, чем промышленным.
Про поляризацию и металл
«Поляризаторы на металле не работают!» — писали вы. И вы… почти правы. На идеально зеркальной поверхности эффект действительно слабый. Но на загрязнённых, частично матовых и неидеальных листах он даёт до 15–20 dB подавления бликов. Не магия — просто физика, немного геометрии и много проб и ошибок.
Про “дешёвого человека с глазами”
Мы тоже любим людей с глазами. И даже работаем вместе.
Система не заменяет оператора — она отсекает рутину, где глаз устаёт, а блик обманывает. Раньше контролёр тратил на проверку одного листа около 6–8 секунд и при этом пропускал до 10–12% микродефектов.
Теперь камера справляется за 1.2 секунды, а доля спорных случаев, которые уходят на ручную проверку, не превышает 3–5%. Вместо бесконечного «вглядывания в зеркало» оператор теперь проверяет только то, где нейросеть не уверена — и делает это в разы быстрее.
Так что не «человек против машины», а человек + машина против скуки и брака.
Про “азов фотографии”
Да, теперь мы знаем про них чуть больше, чем хотели 😅 Но инженерка в цехе — это не студия с софтбоксом. Там дрожит пол, летит пыль, мигает строб, а под ногами едет металл. Так что каждый миллиметр света, поляризации и выдержки был добыт потом и кофеином.
Про “всё равно не автономно”
Справедливое замечание. Промышленное CV пока действительно не живёт без инженеров — но и не должно. Как и любая сложная система, оно развивается итерационно: данные → обучение → адаптация → стабильность. Главное, что теперь инженеры на линии решают не “почему камера не видит”, а “как улучшить метрики”.
На сладкое
Теперь точно знаем, что идеальных данных не бывает, что FTP живёт дольше всех, и что на заводе «хорошее освещение» — это инженерное чудо, а не кнопка в настройках.
Спасибо всем, кто спорил, шутил и предлагал решения — ваши комменты были лучшим продолжением истории, чем любой отчёт по внедрению❤️
Больше живых кейсов моя команда разбирает в новом ТГ-канале https://t.me/brains2up Подписывайтесь, если хотите чаще читать про настоящий ИИ.
а я сегодня дымоход проектировал. короче слушай. труба только нержавейка 1 мм. меньше - прогорит и ненадежно. уличная часть только сендвич. из печки выход 100 мм. в магазине в наличии только сендвич 115/190 (не очень стандартный размер). печка высотой 620. выходной патрубок на высоте 730. от него вертикально пойдет 100 мм труба 0.5 метр. сборка (внимание) по конденсату. папа внизу мама сверху. итак. труба 0.5 м - шибер (0.1 м) - труба 1м - отвод 45 градусов - труба 1 м (уходит под углом 45 градусов в стену и выходит на улицу). поскольку труба идет под углом, меняя длину трубы мы сможем подобрать высоту установки монтажной площадки под сендвич и расстояние от стены чтоб не попасть в свес крыши. итого. длина наклонного участка 1 м. и высота дымохода увеличится на косинус (cos) 45 шрадусов. т.е. нп 0.707 м. просуммировав все длины получаем высоту установки монтажной площадки 2.97 метра и расстояние от стены ~ 0.4 м. идем на озон и закупаем соответствующие компоненты. так же удачно получилось, что основной кронштейн для монтажной площадки (на которой будет стоять 3х метровый сендвич) будет крепиться не в обычный газоблок, а в армопояс. итого дымоход будет высотой ~ 6 метров и тяга прогнозируется неплохая. используемые инструменты freecad для эскиза, озон для заказа. докер и питон не понадобились, но под рукой держу на всякий.
Всем привет! Меня зовут Алексей, я руковожу компанией, которая занимается разработкой с применением ИИ-технологий. Сам я тоже погружен в разработку, но больше доверяю это своей команде – нам удалось собрать команду классных профи. Истории из нашей совместной работы я и планирую рассказывать в своем блоге.
Cегодня делюсь историей одного из наших разработчиков – о внедрении компьютерного зрения на реальном производстве.
Первый день на линии выглядел почти комично. Стоим втроём в касках, над конвейером — новая промышленная камера с глобальным затвором (5 Мп, FOV ~0,6 м), а под ней со скоростью 2 м/с уезжают листы с зеркальным блеском. Оператор рядом фыркает:
— «Ну и где твои умные нейросети? Вон же царапина!»
На экране вместо царапины — пересвеченный до насыщения белый блик: без перекрёстной поляризации и со слишком длинной выдержкой он превращается в «идеальный» объект для детектора. Алгоритм радостно машет флажком: «дефект!». Таких ложных срабатываний за смену набегало сотни; настоящие микротрещины и сколы тонули в засветках и смазе.
С этого момента стало ясно: впереди не «проект на пару недель», а марафон, где придётся бороться не только с кодом, но и с реальностью цеха — вибрациями, пылью, бликами и древним MES, говорящим по OPC/Modbus.
Зачем всё это
Один крупный производственный заказчик (линия резки листового материала: зеркальная поверхность, скорость ~2 м/с) решил автоматизировать контроль дефектов.
Раньше инспекция была ручной — оператор просматривал каждый лист.
На старте задача выглядела так:
Ставим камеру над линией: Basler ace2, 5 Мп, глобальный затвор; C‑mount 12 мм; FOV ≈ 600 мм; аппаратный триггер от энкодера; экспозиция 30–50 мкс; импульсный LED 630 нм с перекрёстной поляризацией.
Режем изображение на тайлы: 1024×1024 px с overlap 20%; deskew по кромке листа; маска бликов.
Возвращаем результаты в MES/SCADA: JSON → XML/FTP (атомарный rename) с переходом на OPC UA; координаты дефектов в мм, sheet_id, статус OK/NOK.
Спойлер: из этого получился трёхмесячный марафон — свет, крепёж, синхронизация и интеграции заставили сервера дрожать, а нас — прокачать инженерный дзен.
Сюрприз №1: Светит, но не туда
Проблема: освещение и блики
На раннем этапе мы выяснили, что камера стабильно видит… блики, а не дефекты. Промышленное освещение оказалось агрессивным: холодные направленные прожекторы, паразитные отражения от оборудования и зеркальная поверхность листов. Сенсор клиппился по яркости, и каждый пересвеченный блик принимался за дефект. На старте ловили до 32% ложноположительных срабатываний.
Что сделали
Свет и поляризация: заменили направленные прожекторы на диффузный купольный/низкоугловой свет под ~45°; поставили линейные поляризаторы на источник и анализатор на объектив под 90° (подавление бликов до ~15–20 dB). Перешли на узкополосные LED 625–660 нм.
Экспозиция и синхронизация: включили строб с импульсом 30–50 мкс под триггер энкодера; глобальный затвор, постоянный токовый драйвер без 50/60 Гц мерцания. Отключили автоэкспозицию/автогейн/автобаланс; гамма = 1.0, фиксированный gain.
Экраны и окклюзии: установили матовые чёрные экраны по бортам и над камерой, убрали паразитные отражения от рам и кромок.
Онлайн-мониторинг яркости: ввели контроль доли насыщенных пикселей (P255) и динамического диапазона; кадры с P255 > 0.5% автоматически помечаются и не идут в инференс; гистограммы логируются.
Вывод: оптика и свет — не «поправим в коде». Схему освещения, поляризацию, строб и крепёж нужно закладывать до начала разработки модели — это даёт порядок выигрыша по качеству сразу.
Сюрприз №2: Камера не дружит с резкостью
Проблема: микровибрации и автофокус
Камера стояла над станком, всё казалось стабильным. Но каждые 30–40 секунд появлялась дрожь, тонкие дефекты «смазывались». Причина — микровибрации от привода/роликов (≈25–35 Гц) и отсутствие автофокуса при фиксированном рабочем расстоянии.
Что сделали
Механика/крепёж: вынесли камеру на жёсткий кронштейн с виброизоляторами (fₙ ≈ 9–12 Гц, демпферы Shore A 30–40), отвязали от корпуса станка, добавили массу и развязку кабелей. Резонансные пики выше 60 Гц подавлены, передача вибраций < 0.3.
Фокус: настроили фикс‑фокус на рабочее расстояние по slanted‑edge (MTF50 вырос с ~0.28 до ~0.36 cyc/px), зафиксировали кольцо (lock‑screw) и метки положения; регламент пересмотра при смене температуры/света.
Экспозиция: привязали экспозицию к стробу 30–50 мкс — при 2 м/с смаз ≤ 0.1 мм.
IMU‑контроль: поставили 6DoF‑датчик (1 кГц); логируем события при |ω|RMS > 0.8°/с или Δугла > 0.05° за 200 мс; при срабатывании помечаем кадры и уведомляем оператора.
Онлайн‑метрика резкости: Tenengrad/Laplacian в ROI; кадры ниже τ исключаются из инференса или запрашивается повтор.
Вывод: стабильная механика и фикс‑фокус столь же критичны, как и модель. Развязка вибраций, короткая экспозиция и онлайн‑контроль резкости делают детектор предсказуемым
Сюрприз №3: один тайл — два дефекта и половина чужого листа
Ожидали: лист идёт ровно и по центру. Реальность: заезд под углом, частичное перекрытие соседним листом, дрейф по диагонали. В итоге один тайл нередко содержит две кромки разных листов — модель «плывёт» в предсказаниях.
Что сделали
Маркеры и привязка к движению: наклеили ретрорефлективные метки по краям ленты; считывание в NIR (850 нм) с аппаратным триггером от энкодера (5k PPR). Детектируем leading edge и боковые кромки; дрожание меток < 0.5 мм.
Лидар + фотодатчики: ToF-лидар 1 кГц для контроля lateral offset, три фотошторки по ширине для детекта перекрытия/наличия листа. События уходят в PLC и в CV-пайплайн; допуск смещения ±3 мм, время реакции < 5 мс.
вычисляем гомографию и выполняем deskew; сетка тайлов якорится к кромке, а не к «сырому» кадру;
при перекрытии строим маску «серой зоны» вдоль шва (15–25 px): увеличиваем overlap, нежёстко понижаем вес предсказаний в NMS, логику объединения делаем по sheet_id;
Слежение за листом: sheet_id по импульсам энкодера; пересборка карты дефектов в координатах линии, чтобы MES получал стабильные ROI.
Вывод: физическое позиционирование нужно дублировать алгоритмами CV. У «железа» иногда свои планы, поэтому привязка к энкодеру, маркеры, лидар и дескью — обязательны для стабильного тайлинга и корректной агрегации предсказаний.
Сюрприз №4: Модель любит однотипные дефекты, а у нас каждый раз сюрприз
Проблема: разнообразие артефактов
Обучались на 15 классах дефектов. В проде всплыли неожиданные: пятна клея, отпечатки перчаток, стружка, насекомые, следы чистки и др. За первый месяц зафиксировали 37 новых типов, отсутствовавших в трейне.
Что сделали
Активное выявление «неизвестных» (OOD): отправляем в разметку тайлы при низкой уверенности (conf < τ), высокой энтропии, а также при расхождении ансамбля и классического CV; дополнили energy-score по логитам и расстоянием Махаланобиса в признаковом пространстве.
Еженедельная разметка и инкрементальное обучение: выгружаем 8–12k «неопознанных» тайлов/нед. в LabelStudio (κ ≥ 0.82), дообучаем с rehearsal (30% старых данных), частично замораживаем backbone, применяем дистилляцию знаний и калибровку температурой.
Управление таксономией: «прочее/ nuisance» как буферный класс; промоутим в полноценный класс при ≥200 размеченных примеров и precision ≥ 0.7 на валидации. Зоны с низкой уверенностью помечаем в UI для ручной проверки.
Вывод: CV — это не «обучил и забыл», а непрерывный цикл. Активный OOD-поток, регулярная разметка и инкрементальное обучение с гибридными эвристиками дают управляемость при появлении новых артефактов без деградации по базовым дефектам.
Сюрприз №5: API-интеграция с MES — это уже не про CV, но боль
Проблема: древняя MES-система
Документации нет. REST/GraphQL нет. Только XML-файлы по FTP, которые MES опрашивает раз в несколько секунд. Частые проблемы: частичные чтения, дубликаты, «зависшие» файлы, непредсказуемые задержки.
Что сделали
Прокси-адаптер JSON→XML + надёжная файловая шина
CV выдаёт JSON с sheet_id, ts, speed, списком дефектов (bbox в мм, класс, уверенность).
Адаптер формирует XML по согласованной схеме, пишет атомарно: сначала в outbox/*.xml.part, затем rename в *.xml; после чтения MES кладёт *.ack.
Каталоги: outbox/ → processing/ → done/, ошибки в error/ с ретраем (экспоненциальная задержка, максимум 5 попыток), дедуп по sheet_id.
Кодировка UTF‑8 (без BOM), CRLF по требованию MES. Имя файла: INS_L1_20250912T101532Z_123456.xml.
Мониторинг и SLA для FTP-интеграции
Тайм-аут чтения: если через 30 с нет *.ack — алерт (Prometheus/Alertmanager → Teams/Slack), автоперекладка файла в processing/ не повторяется до разборки инцидента.
Метрики: end-to-end задержка (CV→MES), число «подвисших» файлов, ретраи, доля дубликатов, размер очереди. Трассировка по sheet_id.
Безопасность: при возможности SFTP; иначе FTPS (TLS), учётка с минимальными правами и chroot.
Узлы: ns=2;s=estralin/Sheet/{sheet_id}/Result, .../Defects (массив структур с bbox в мм и классом). Семплирование 100–200 мс, очередь 128, подтверждение обработкой статуса.
Фолбэк: при недоступности OPC UA — автоматический возврат на файловую шину, чтобы не терять данные
Вывод: интеграции — это отдельный проект. Согласуйте протоколы/схемы и SLA с IT-заказчика до старта, закладывайте атомарность, дедуп, мониторинг и фолбэк-канал; при возможности переходите на OPC UA с шифрованием.
Технологии и стек
Камера: Basler ace2 (GigE, mono, global shutter, 5 MP @ 60 fps), C‑mount фикс-объектив 12 мм (F/4), FOV ≈ 600 мм, масштаб ≈ 0.20 мм/пкс. Аппаратный триггер от энкодера 5k PPR, экспозиция 30–50 мкс, строб узкополосного LED 630 нм с перекрёстной поляризацией; гамма 1.0, фиксированный gain.
Модели: YOLOv5m (тайлы 1024×1024, overlap 20%), препроцессинг OpenCV (deskew по Sobel/Hough, CLAHE, маска бликов). Инференс ONNX Runtime CUDA/FP16; p50 7.8 мс/тайл, p95 11.2 мс/тайл (RTX A2000). Итог по листу p95 < 90 мс при 2 м/с. Качество: mAP50 0.96, F1 0.91, FPR 3.1% на холдауте.
Разметка: LabelStudio, экспорт COCO/YOLO; двойная валидация, κ=0.84; гайдлайны по классам/границам; класс «nuisance/прочее» для OOD до промоушена.
Интеграция: Python FastAPI (/infer, /healthz) → JSON; адаптер JSON→XML с атомарным rename и *.ack для legacy-FTP; основная шина — OPC UA (SecurityMode=SignAndEncrypt, Basic256Sha256), узлы ns=2;s=estralin/Sheet/{id}/Defects. Фолбэк на файловый канал.
Мониторинг: Prometheus + Grafana. Метрики: p95/p99 инференса, очередь, GPU util/mem, доля клиппинга (P255), FPR/recall по сменам, OOD rate, задержка CV→MES/OPC. Алерты по таймаутам ACK (>30 с), росту FPR, падению FPS/энкодера.
Деплой: On‑prem, Docker (Compose), NVIDIA Container Toolkit, закрепление версий драйверов/библиотек, healthchecks, автоперезапуск, локальный inference (latency‑critical), офлайн‑буферизация результатов и ретраи.
Финальные цифры
Хотелось показать таблицей, поэтому просто скриншот
Что мы поняли
Оглядываясь назад, понимаем: часть проблем мы могли предсказать. Вот несколько инсайтов, которые пригодятся тем, кто только собирается внедрять CV на производстве.
Не верить в «идеальные данные»
Пыль/блики/вибрации — норма. Введите авто‑контроль качества кадров: P255 ≤ 0.5%, средняя яркость в окне [90;160], резкость (Tenengrad) > τ, доля «горячих» пикселей ≤ 0.05%.
Мониторьте дрейф окружения: деградация света (−5…−8%/мес), смещение экспозиции, рост шума; алерты и напоминания на очистку оптики каждые 8 ч или при падении SNR < 24 dB.
Реплицируйте «грязную реальность» в данных: еженедельно добавляйте 5–10% свежих OOD‑тайлов, аугментации под бликами/смазом/пылью; калибровка порогов раз в смену.
Закладывать бюджет на «внезапности»
Резерв времени: +20–30% к срокам на интеграции/железо; бюджет: +10–15% на запасные части (камеры, БП, драйверы света, кабели).
Операционные SLO: p95 инференса < 100 мс/тайл, p95 CV→MES < 1.2 с, потери данных 0, дедуп по sheet_id, MTTR инцидента интеграции ≤ 15 мин.
Дежурство и плейбуки: on‑call 24/7 для линии, сценарии на «камера/энкодер/свет/OPC/FTP упал», фолбэк на файловую шину, офлайн‑буфер ≥ 24 ч.
Общаться с технарями на месте
Совместно с цехом: выбор света/крепежа/экранирования, допустимые окна простоя, точки триггера от энкодера, маршруты кабелей, HSE‑требования.
Формализовать SAT/UAT: чек‑листы по классам дефектов, выборки на 1–2 смены, критерии приёмки (precision/recall/FPR), график регламентных чисток и перекалибровок.
Напоследок
Компьютерное зрение в промышленности — меньше про «красоту модели» и больше про свет, крепёж, синхронизацию и протоколы. Если идёте в прод, готовьтесь к реальному миру — тому, где рядом со Stack Overflow открыта вкладка «как дернуть автофокус через Modbus» и список запасных кабелей на складе.
1) "больше всех в колхозе работала лошадь, но председателем она не стала". Если ты сидишь круглые сутки и ищешь багу, которая, по твоему мнению, мешает организации развиваться, то ты это делаешь ТОЛЬКО для себя. Никто и никогда это не оценит. Для себя - это значит, что ты найдешь ЭТО и удовлетворишь свою жажду исследователя. Это никак не повлияет на заработок и карьерный рост.
2) На мой взгляд, главная причина так называемого "выгорания" - это страстно заниматься п.1 и ожидать, что это кто-то оценит. Потом, снова и снова, приходит осознание, что это нафиг никому не надо. Отсюда и печаль, депрессия, демотивация.
3) Все начальники (лиды) отделов и департаментов тупые и абсолютно не заинтересованы ни в чем, кроме своего карьерного роста. Так было всегда, так есть и так будет. Их просто надо уметь готовить. При этом, не говорить то, что они боятся услышать. А боятся они следующего: а) что-то, что ставит под сомнение их авторитет и лидерство б) сомнения в их исключительности и эффективности в) что вы, хотя бы теоретически, помышляете кого-то другого на их месте
4) Если вы согласны с п.3, то не все так плохо. Можно общаться с директорами и/или владельцами. Это чит. Если директор при всех упоминает именно тебя или здоровается с тобой лично, то все менеджеры начинают тебя недолюбливать и бояться. При этом им не хватит смелости от тебя избавиться, т.к. это может повлиять на их карьеру. В этом режиме IDDQD ты можешь нифига не делать и слать всех нахер, кроме пункта 3.
5) многие компании являются пустышками с одной целью: привлечение инвестиций. В них работает кучу людей и занимаются видимостью работы. В таких компаниях работать не надо. Можно их доить и иметь запасной аэродром.
6) полулегальные или криминальные компании хоть и платят черным налом, но, часто в них порядка и дисциплины больше, чем в белых конторах.
7) можно совмещать несколько работодателей. Даже до ковида и удаленки я так делал. Многие коллеги иногда признавались, что тоже этим грешны.
8) Зайти в ИТ через тестирование - тупик. Будешь тестить за копейки до пенсии, пока не заменят автотестами. В разработчки так никогда не попадешь. Мне кажется, что молодняку сейчас самое то заходить через конкурсы или стажировки или гос.службу.
9) геморрой лечится
10) деньги копятся быстрее, чем их тратит жена
11) когда пишешь какую нибудь программулинку, которую никто никогда не делал, то чувствуешь себя богом (создателем). Это должен был быть пунктом один и это единственная причина работы в ИТ.
12) Все перечисленные пункты имеют приятные и редкие исключения.
Всем добра. Ушел из профессии. У меня есть все и даже больше. Планирую заняться резьбой по дереву и воспитанием внуков.
Ходил бы с работягами в курилку каждый час. Получал 100К в месяц. Охранял бы станок и слушал подкасты на заводе. Болтал бы с коллегами о футболе, рыбалке и семье. Рассказывал, как провёл выходные.
Считал бы, что завод – это место для избранных. Тех, кто уже сходил к реке и понял жизнь, преисполнившись в своём познании. Стоял бы каждый вечер у КПП, ожидая окончания рабочего дня. Мурлыкал бы под нос Дору.
Ел бы в заводской столовой, смотря видосики на ютубе. Ходил бы в рабочей одежде на обед. Дома рассматривал мозоли на руках и грязь на коже. Считал бы себя новым средним классом.
Получил бы права. Выучил все матерные слова и умел с помощью них ярко описать все жизненные приключения. Разбирался в металле, станках и профессиях работяг. И никаких дедлайнов. Никаких кипиаев.
На самом деле любую работу можно разделить на тяжелую, сложную и трудную. Разница заключается в нюансах, которые отражают разные аспекты нагрузки и препятствий. Дальше мое имхо, можете поделиться своим взглядом на эти понятия.
Трудная работа
Подразумевает, что работа требует значительных физических усилий или условия труда хуже чем у обывателя. Опять же не редко это значит очень большой объем работы. Довольно сильно истощает все ресурсы, но не настолько сильно откладывает отпечаток на психоэмоциональном состоянии.
Сложная работа
Означает, что работа состоит из множества взаимосвязанных задач или этапов, требующих глубокого анализа, планирования и системного подхода. Сюда входит и программирование, не сложно написать функцию программы, сложно учесть весь тот огромный контекст знаний и архитектуры проекта. Опять же сюда можно записать любого сотрудника использующий в работе большое количество разных систем и подсистем. Хотя опять же если человек отлично знает систему и теорию, то работа перестает быть сложной. Тут нет ощущения физической усталости, но бьет по психике пожалуй сильнее чем трудная.
Тяжелая работа(самая сложная на мой взгляд)
Обычно подразумевает высокую интенсивную физическую или эмоциональную нагрузку. Скажем если добавить в работу очень короткие сроки. По сути является работой на неком пределе возможностей. Может приводить к быстрому выгоранию, поскольку постоянное сильное напряжение быстро истощает силы. Сюда же бы добавил работу с высоким уровнем ответственности.
Любая тяжелая работа ведет к неминуемому выгоранию если заниматься ей достаточно долго и не давать себе достаточный отдых. Некоторые программисты сталкиваются с тем что их работа одновременно и сложная и тяжелая. Если интересно про это почитать, можно ознакомится с автобиографической книгой Джона Ромеро «Икона DOOM».
Вообще я бы не стал в принципе обесценивать любой труд, многих возмущает "нечестные" зарплаты. Просто к сожалению при капитализме твой уровень зарплаты зависит не только от того насколько трудная, сложная или тяжелая у тебя работа, многое зависит от того как легко тебя заменить и насколько ты компетентен.