Привет, Хабр! Меня зовут Андрей Бирюков. Я эксперт в области ИТ и ИБ, преподаю в учебных центрах и пишу книги. Сегодня мы поговорим о проблемах тестирования аппаратных решений в случае, если физической реализации устройства еще нет.
Представьте, что ваша компания разрабатывает систему управления ориентацией спутника. Железо — плата с микроконтроллером, гироскопами и маховиками — появится через восемь месяцев. Но писать код и тестировать его нужно уже сейчас, иначе вы опоздаете к запуску. Выход очевиден: создать цифрового двойника — математическую модель спутника в среде его движения, подменить реальные датчики и двигатели программными симуляторами, а потом запустить ваш контроллер внутри этого виртуального мира.

И вот вы всё необходимое сделали, тесты проходят замечательно. Виртуальный спутник стабилизируется, выполняет манёвры, расходует виртуальное топливо. Вы успешно сдаёте проект. Затем приходит железо, и вы прошиваете тот же самый код в реальный контроллер, ставите на тестовый стенд с настоящими маховиками — и спутник начинает бешено вращаться вокруг своей оси, пытаясь компенсировать несуществующие возмущения…
Что же произошло? Ведь вы использовали один и тот же неоднократно проверенный код. Проблема в том, что вы тестировали не правильность реализации, а правильность ваших предположений о мире.
Цифровой двойник был слишком хорош. Он не знал, что реальные гироскопы дрейфуют, что частота опроса датчиков неравномерна, что двигатели реагируют с задержкой, а в сигналах присутствует шум — шум, который в виртуальной модели вы выставили гауссовским с красивой дисперсией, а в реальности он оказался розовым с длинными корреляциями. Вы тестировали полёт в идеальном мире, а полететь предстояло в реальном.

И это не гипотетическая история. Такое происходило с первыми испытаниями многих автономных систем — от подводных аппаратов до беспилотных автомобилей. Парадокс формулируется просто: если вы тестируете софт только на цифровом двойнике, вы не можете отличить ошибку в коде от ошибки в модели. А модель ошибается всегда, просто вы ещё не знаете, в чём именно. Давайте попробуем разобраться с тем, что же все‑таки можно сделать в такой ситуации.
Когда правильного ответа нет
В классическом тестировании программного обеспечения у вас есть оракул — источник правильного ответа. Вы подаёте на вход два и пять, ожидаете на выходе семь, сравниваете, получаете зелёную или красную строчку. В тестировании ПАК на цифровом двойнике оракула в таком виде нет. Потому что у вас нет правильного поведения спутника — у вас есть только модель, которая сама нуждается в проверке. Если тест пройден, вы не знаете, код хороший или модель неточная. Если тест провален, вы не знаете, что чинить — контроллер или симуляцию.
Выход из этого тупика предлагает концепция «метаморфического тестирования». Вы не ищете один правильный ответ. Вместо этого вы ищете свойства, которые должны сохраняться при определённых преобразованиях входных данных.
Например, если вы увеличиваете массу спутника в два раза, то время разгона до заданной угловой скорости должно увеличиться примерно вдвое — при том же моменте инерции маховиков. Если ваш контроллер на цифровом двойнике при удвоенной массе вместо удвоения времени разгона начинает колебаться или вовсе теряет устойчивость — это баг, причём баг, который не требует знания абсолютно правильного значения времени.
Или другой пример: если вы повторяете одну и ту же последовательность команд дважды и система выходит в одно и то же состояние — это инвариант детерминированности. Если после второго прогона состояние отличается — что‑то не так либо с кодом, либо с моделью, но разрывать причину нужно дальше.

Метаморфическое тестирование для ПАК особенно ценно тем, что оно не требует эталонной реализации. Оно требует только вашего понимания физики: законы сохранения энергии, импульса, массы — вот ваши оракулы.
Если цифровой двойник показывает, что после серии манёвров энергия системы увеличилась сама собой — он нарушил первый закон термодинамики. Значит, либо модель дефектна, либо контроллер управляет ею некорректно. В любом случае это свидетельство проблемы, и его достаточно, чтобы отправить задачу на разбор.
Игра нескольких двойников
И всё же, одного метаморфического тестирования недостаточно. Рано или поздно вам понадобится сравнить поведение вашей системы с чем‑то, что считается истиной, хотя бы для калибровки. Но если истинного поведения нет — создайте две независимые модели, а потом сравнивайте их друг с другом.
Этот приём называется кросс‑валидацией. Идея проста: вы строите цифрового двойника двумя разными способами.
Первый — высокодетальная физическая модель, решающая дифференциальные уравнения с малым шагом, но медленная.
Второй — упрощённая модель для быстрого тестирования в реальном времени, возможно, сниженной размерности или с применением табличных аппроксимаций.
Обе модели получают одинаковые управляющие сигналы от вашего контроллера. В идеале они должны расходиться не более чем на заранее установленную величину. Если в какой‑то момент расхождение резко выросло, это звоночек. Возможно, контроллер загнал быструю модель в область, где её упрощения перестали работать. А возможно, ошибка в медленной модели — например, из‑за неустойчивого численного метода.
Но сам факт расхождения — это событие, которое достойно отдельного теста. Вы фиксируете траекторию, воспроизводите её на упрощённой модели с более мелким шагом и смотрите, сохраняется ли расхождение. Так вы шаг за шагом сужаете круг подозреваемых.
|
Давайте рассмотрим кейс из автомобильной промышленности. Тестировалась система управления тягой электромобиля на четырёх колёсах. Была медленная модель всей машины в MATLAB/Simulink и быстрая модель, написанная на C++ для работы в цикле реального времени. Тест на разгоне из состояния покоя показывал, что через 4.3 секунды быстрая модель резко уходила в занос, а медленная продолжала устойчивое движение. Долгое расследование выявило причину: в быстрой модели из‑за округления при вычислении угла поворота колёс накапливалась ошибка, которая в определённый момент превышала порог сцепления. При этом математически модель была верна, но численная реализация применяла не тот порядок операций — сначала умножение, потом округление, хотя нужно было наоборот. Без наличия медленной модели этот баг остался бы незамеченным до первых реальных испытаний, на которых автомобиль, скорее всего, разбился бы. |
Тестируем нестабильность границ
Ещё один мощный приём для тестирования на цифровых двойниках, не требующий оракула, — это поиск границ устойчивости. Вы берёте параметры модели и начинаете их непрерывно менять в сторону экстремальных значений, пока поведение системы не разрушится. Скачки, уход в бесконечность, колебания с нарастающей амплитудой. При этом правильного ответа нет — вы просто ищете точки, где теряется устойчивость, и сравниваете их с физическими ожиданиями.
Например, вы увеличиваете коэффициент обратной связи в регуляторе положения. В теории, при определённом критическом коэффициенте должны начаться незатухающие колебания. Вы запускаете автоматизированный прогон: увеличиваете коэффициент с малым шагом, на каждом шаге даёте системе успокоиться и наносите небольшое возмущение. Смотрите, затухают ли колебания. Как только они перестали затухать — вы нашли экспериментальный предел устойчивости для вашего цифрового двойника. Сравниваете его с расчётным теоретическим пределом.
Если они различаются более чем на 10–15 процентов — это повод разбираться. Возможно, ваша модель теряет устойчивость раньше из‑за ошибок дискретизации. А возможно, и наоборот — модель слишком оптимистична, и реальная система на железе сорвётся ещё раньше.

Этот метод особенно ценен, когда у вас нет точных значений параметров реального устройства — например, момента инерции, коэффициента трения, люфта в редукторе. У вас есть только оценочный диапазон. Метод поиска краев позволяет ответить на вопрос: меняется ли качество управления приемлемым образом при любых допустимых значениях параметров? Если при одних комбинациях параметров (скажем, максимальная инерция и минимальное трение) система срывается в автоколебания, значит, в реальности при каких‑то экземплярах железа это случится обязательно. И это информация, которую можно получить задолго до того, как первый прототип будет собран.
Вы не знаете точного поведения — но вы знаете, что в определённой зоне параметров поведение становится неприемлемым. И это знание позволяет изменить алгоритм, добавить робастности, расширить зону устойчивости.
Цифровой двойник, который не знал про дискретизацию
Завершим статью реальным кейсом, который иллюстрирует все описанные принципы. Разрабатывался контроллер для подводного аппарата, который должен был стабилизировать глубину. Физический аппарат ещё не был построен полностью — гидродинамика корпуса уточнялась. Инженеры создали цифрового двойника на основе уравнений движения подводного тела. Тестирование на модели проходило отлично: аппарат идеально держал глубину, отрабатывал возмущения от течений, плавно погружался и всплывал.
Когда появился первый прототип, его опустили в испытательный бассейн. И произошло странное: на мелких волнах (имитация слабого течения) аппарат вёл себя приемлемо, но при включении режима точного удержания глубины начинал мелко дрожать — с частотой около 5 герц — и медленно всплывал. Код был абсолютно тот же, что на модели.

Анализ показал следующее. В цифровом двойнике обновление датчика глубины происходило с постоянной частотой 100 герц, аккуратно, как в учебнике по C++. В реальном аппарате датчик давления выдавал данные с неравномерным интервалом — из‑за того, что шина I2C периодически конфликтовала с другим датчиком. Средняя частота была 100 герц, но иногда между измерениями проходило 15 миллисекунд, а иногда 5. Контроллер, написанный в предположении равномерной дискретизации, получал пачку измерений почти одновременно и трактовал их как быстрое изменение глубины, выдавая резкий управляющий импульс. Возникали автоколебания.
Почему этот баг не поймали на цифровом двойнике? Потому что в модели датчик вызывался идеально равномерно. В метаописании модели не было параметра «джиттер выборки». Внесение в модель искусственного джиттера — внесение временной неравномерности в измерения — позволило воспроизвести дрожание аппарата на цифровом стенде.
Метаморфический тест был построен так: возьмите ту же самую управляющую программу, но пропустите сигнал датчика через блок «джиттер» с амплитудой до 30% от периода опроса.
Ожидаемое свойство — умеренное ухудшение точности, но не автоколебания.
Фактическое — автоколебания.
Баг был найден, контроллер переписали: вместо простого пропорционального регулятора по последнему измерению внедрили фильтр скользящего среднего с отбрасыванием выбросов по времени прихода. И следующая версия прототипа уже не дрожала.
Этот случай показывает главное: вы можете поймать на цифровом двойнике баг, которого в модели изначально нет, если вы не просто гоняете модель в штатном режиме, а активно вносите в неё те шероховатости, которые неизбежно появятся в реальном мире.
И для этого не нужно идеально знать всё о реальном мире — достаточно иметь гипотезы о том, что пойдёт не так, и уметь их формализовать в виде дополнительных преобразований сигналов внутри симуляции.
Подведем итоги
Тестирование программно‑аппаратного комплекса до появления физического железа — это не самообман, а необходимость. Оно возможно и полезно, но требует смены оптики. Вы не проверяете «правильность» в классическом смысле — вы ищете нестабильности, инварианты и границы.
Вы отказываетесь от единого оракула в пользу множества косвенных свидетелей: законов физики, согласованности независимых моделей, поведения на краях допустимых параметров. Любой тест на цифровом двойнике должен отвечать не на вопрос «работает ли система», а на вопрос «при каких условиях система гарантированно перестанет работать?» Ответ на последний вопрос ценен даже без реального железа — и это главный ресурс, которым располагает инженер до появления первого прототипа.
И последнее, самое важное. Отношение к цифровому двойнику как к истине в последней инстанции — гарантия провала на реальных испытаниях. Относитесь к нему как к оппоненту, которого вы пытаетесь поймать на противоречии. Строите кросс‑валидацию — хорошо. Вносите искусственные шероховатости — ещё лучше.
Меняете параметры вширь и смотрите, где рушится устойчивость — идеально. Хороший инженер‑тестировщик ПАК не верит цифровому двойнику. Он доверяет только расхождениям между двойниками и тому, что из этих расхождений можно извлечь.

Если вам близка тема тестирования сложных систем, где важно не просто «прогнать сценарии», а увидеть сбои, расхождения и нестабильность до того, как они ударят по продакшену или реальному железу, присмотритесь к открытым урокам OTUS.
Они бесплатно проходят в рамках онлайн‑курсов и помогают познакомиться с преподавателями, форматом обучения и подходом к разбору инженерных задач.
10 июня, 20:00. «Мониторинг распределенных систем». Записаться
На уроке поговорим о том, как наблюдать за поведением сложной инфраструктуры, находить аномалии и понимать, что именно происходит с системой, когда она начинает вести себя не так, как ожидалось.16 июня, 20:00. «ИИ в автотестах: помощник или угроза?». Записаться
Разберём, где ИИ действительно может помочь в автотестировании, а где создаёт ложное ощущение контроля — примерно ту самую проблему, с которой сталкиваются инженеры, когда слишком доверяют модели или тестовой среде.
А ещё подписывайтесь на канал OTUS в MAX — там публикуем анонсы открытых уроков, материалы для IT‑специалистов и полезные разборы по разработке, инфраструктуре, тестированию, аналитике и другим направлениям.