惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

大猫的无限游戏
大猫的无限游戏
Security Archives - TechRepublic
Security Archives - TechRepublic
T
Threat Research - Cisco Blogs
A
Arctic Wolf
S
Securelist
O
OpenAI News
T
Threatpost
Forbes - Security
Forbes - Security
N
News and Events Feed by Topic
S
Secure Thoughts
H
Heimdal Security Blog
S
Security Affairs
P
Privacy International News Feed
C
Cisco Blogs
C
CERT Recently Published Vulnerability Notes
Cyberwarzone
Cyberwarzone
N
News and Events Feed by Topic
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
P
Palo Alto Networks Blog
S
Security @ Cisco Blogs
Hacker News - Newest:
Hacker News - Newest: "LLM"
博客园 - 三生石上(FineUI控件)
月光博客
月光博客
T
Tailwind CSS Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Hacker News: Ask HN
Hacker News: Ask HN
T
Troy Hunt's Blog
S
SegmentFault 最新的问题
腾讯CDC
V
Visual Studio Blog
Last Week in AI
Last Week in AI
H
Hacker News: Front Page
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Project Zero
Project Zero
WordPress大学
WordPress大学
NISL@THU
NISL@THU
博客园 - 【当耐特】
博客园 - Franky
Webroot Blog
Webroot Blog
博客园_首页
T
Tenable Blog
雷峰网
雷峰网
Google Online Security Blog
Google Online Security Blog
阮一峰的网络日志
阮一峰的网络日志
V2EX - 技术
V2EX - 技术
Recent Commits to openclaw:main
Recent Commits to openclaw:main
L
Lohrmann on Cybersecurity
The Hacker News
The Hacker News

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Анимация персонажей в реальном времени с помощью машинного обучения: обзор PFNN, MANN и LMM
dagerd · 2026-05-13 · via Все публикации подряд на Хабре

Уровень сложностиСредний

Время на прочтение13 мин

Охват и читатели639

Обзор

Автор: Георгий Маркелов, разработчик Softellion

Оглавление

Введение

Еще совсем недавно в анимации персонажей за стандарт были приняты такие системы, как, например, анимация на основе ключевых кадров (keyframe) или процедурная анимация, подразумевающая под собой целое семейство совершенно различных подходов — на основе обратной кинематики, ragdoll, или более комплексных разработок (GTA IV — Euphoria). Однако, несмотря на широкое применение, они не лишены существенных недостатков — нереалистичность, дороговизна, ограниченная выразительность, потребность в ручном труде, сложность с выдерживанием единого художественного стиля. Затем пришел motion matching, обеспечивающий совершенно иной уровень качества анимации, но и позволить себе такие системы могут только разработчики проектов ААА уровня. К тому же такая система чрезвычайно требовательна к оперативной памяти ввиду необходимости хранить в ней всю библиотеку анимаций.

Некоторые из перечисленных недостатков естественным образом решаются посредством применения машинного обучения благодаря низкому потреблению памяти, масштабируемости в контексте данных и способности к обобщению. Сегодня можно наблюдать новый сдвиг: все больше задач, связанных с движением, мимикой и поведением персонажей, передаётся моделям машинного обучения. Причина проста — игры, VR/AR‑системы, виртуальные актёры, интерактивные симуляции — требуют не просто красивой анимации, а реалистичного поведения в реальном времени, адаптирующегося к окружению и действиям пользователя, чего традиционные системы не могут обеспечить.

Нейросети способны учиться на больших наборах данных захвата движения, предсказывать движение для следующих кадров, синтезировать переходы между позами, управлять походкой, балансом, реакциями на препятствия и даже мимикой, синхронизированной с голосом. В результате мы получаем анимацию, которая выглядит естественно, но при этом генерируется на лету — без заранее подготовленных клипов. Тем не менее вместе с новыми возможностями приходят и новые вызовы: производительность, стабильность, контроль над результатом, требования к качеству данных и интеграция в существующие пайплайны.

В этой статье я приведу некоторые решения для анимации персонажей, основанные на машинном обучении, которые имеются на данный момент. Поскольку рассматриваемые далее решения довольно тесно связаны с технологией motion matching, то в начале приведу по ней небольшую справку.


Motion matching является алгоритмом поиска и выбора в базе данных анимаций той позы, которая наилучшим образом подходит для данного контекста. На данный момент эта технология позволяет получать наиболее отзывчивые и правдоподобные анимации. Использовалась в следующих проектах: The Last of Us, For Honor, Fortnite. 

Вместо того, чтобы описывать анимацию через граф состояний, motion matching позволяет аниматорам задавать параметры позы (features — фичи), которая должна быть получена, и она автоматически будет выбрана методом ближайшего соседа. Из этого следует, что качество и разнообразие анимаций напрямую зависят от размера базы данных. Благодаря тому, что сам по себе алгоритм берет позу из базы данных как есть, применяя только смешивание и добавляя постобработку, сохраняется качество исходной анимации, и аниматоры сохраняют контроль над результатом. Поэтапно процесс работы motion matching можно представить так:

  1. вычисляется желаемая траектория движения

  2. выполняется поиск в базе данных наиболее подходящей позы, учитывая параметры позы и траекторию движения

  3. поза, наиболее близкая к найденной, назначается персонажу

  4. постобработка

  5. повторить пункты 1-4

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

Таким образом, можно выделить следующие ключевые недостатки motion matching:

  1. необходимость огромного датасета, например, в TLOU 2 только для Элли и Эбби суммарно 2627 анимационных клипов — 2 часа 17 минут анимаций 

  2. отсутствие генеративности

Типичный пайплайн

Пайплайн концептуально для всех трех архитектур не отличается и выглядит следующим образом:

  1. Предобработка: на этом этапе подготавливаются тренировочные данные и автоматически извлекаются параметры контроллера, а также сопоставляется рельеф с передвижением, используя отдельную карту высот

  2. Тренировка: выполняется так, что нейросеть учится выдавать движение персонажа на каждом кадре, используя параметры контроллера

  3. Инференс в режиме реального времени: собираются данные для входа в нейросеть (контроллер и окружающая среда), подаются в систему, которая определяет движение персонажа

Исходными тренировочными данными является обычный захват движений, записанный в длинную последовательность, описывающую различные походки и направления движений. На этапе предобработки в целях насыщения набора данных выполняется зеркалирование всей последовательности.

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

Phase-functioned neural networks for character control (PFNN) 

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

Значительное улучшение выразительности анимации достигается за счет динамического изменения весов нейросети в зависимости от фазовой функции — такой подход позволяет нейросети обучаться на основе большого набора данных с высокой размерностью, где геометрия окружающей среды и данные о движении человека взаимосвязаны. Архитектура PFNN позволяет избегать смешивания данных с разных фаз, и функция регрессии плавно изменяется со временем в зависимости от фазы. После обучения система может автоматически генерировать подходящие и выразительные движения для персонажа, передвигающегося по пересеченной местности, прыгающего и избегающего препятствий — как в естественной, так и в городской среде.

Архитектура PFNN

Архитектура PFNN

Входными данными для нейросети являются предыдущая поза и вход с контроллера, а выходные данные включают в себя изменение фазы, текущую позу персонажа и некоторые дополнительные параметры.

В качестве данных для тренировки вместе с анимацией подаются параметры контроллера, состоящие из фазы движения, семантических лейблов походки, траектории движения персонажа и карты высот вдоль траектории.

  • Разметка фаз: выполняется в полуавтоматическом режиме. Контакт ног с поверхностью вычисляется автоматически через скорость, после чего вручную корректируются. После получения данных о контакте назначаются фазы: когда правая нога контактирует с поверхностью, фаза = 0, далее левая = \pi, снова правая = 2\pi(0 \leq p \leq 2\pi). Между кадрами выполняется интерполяция

  • Разметка походки: выполняется вручную, представляется в виде бинарного вектора, чтобы устранить неоднозначность близких по типу движений и описать специфические сценарии движения 

  • Траектория и карта высот: извлекается root transformation персонаа описывающая траекторию движения. Далее, вдоль всей траектории движения и перпендикулярно по бокам от нее вычисляется высота поверхности.

Параметризация системы

Параметризация системы

Для описания состояния персонажа берутся локальные по отношению к root transformation позиции и скорости костей на текущем кадре анимации, а для построении траектории выполняется семплирование 5 кадров из будущего и 6 из прошлого, суммарно покрывающих 1 секунду движения в прошлом и 0.9 секунд в будущем. Для каждого семпла извлекается позиция и направление траектории относительно root transformation, бинарный вектор, описывающий походку, и высота поверхности под траекторией и по бокам от нее на удалении в 25 см. Таким образом, полный вектор входных данных для нейросети для одного кадра анимации выглядит так:

x_i=\{t_i^p t_i^d t_i^h t_i^g j_{i-1}^p j_{i-1}^v\} \in R^n

  • i – текущий кадр

  • i-1 – предыдущий кадр

  • s – количество семплированных кадров (12 штук)

  • b – количество костей в скелете 

  • t_i^p \in R^{2s} – позиции траектории движения в 2D горизонтальной плоскости

  • t_i^d \in R^{2s}направления траектории движения в 2D горизонтальной плоскости

  • t_i^h \in R^{3s} – высоты в точках слева, справа и посередине траектории движения

  • t_i^g \in R^{5s} – семантические лейблы, описывающие походку персонажа и другую информацию

  • j_{i-1}^p \in R^{3b} – локальные позиции костей

  • j_{i-1}^v \in R^{3b} – локальные скорости костей

В оригинальной статье в качестве лейблов t_i^gиспользовались следующие:

  1. состояние покоя

  2. ходьба

  3. бег

  4. прыжок

  5. приседание

Результатом инференса нейросети является вектор:

y_i = \{t_{i+1}^p t_{i+1}^d j_i^p j_i^v j_i^a \dot{r_i^x} \dot{r_i^z} \dot{r_i^a} \dot{p_i} c_i\} \in R^m

  • s — количество семплированных кадров (12 штук)

  • b — количество костей в скелете

  • t_{i+1}^p \in R^{2s}— предсказанные позиции таректории на следующем кадре

  • t_{i+1}^d \in R^{2s}— предсказанные направления траектории на следующем кадре

  • j_i^p \in R^{3b} — локальные позиции костей

  • j_i^v \in R^{3b} — локальные скорости костей

  • j_i^a \in R^{3b}— углы костей, выраженные в форме экспоненциальной карты

  • \dot{r_i^x} \in R — линейная скорость root transformation по оси X относительно направления движения «вперед»

  • \dot{r_i^z} \in R — линейная скорость root transformation по оси Z относительно направления движения «вперед»

  • \dot{r_i^a} \in R — угловая скорость root transformation вокруг вертикальной оси

  • \dot{p_i} \in R — изменение фазы

  • c_i \in R^4— информация о контакте ног (носка и пятки) с поверхностью

Нейросеть состоит из 3х слоев:

\Phi(x; a) = W_2 ELU(W_1 ELU(W_0 x + b_0) + b_1) + b_2

Количество нейронов в каждом слое = 512. Веса нейросети a вычисляются в зависимости от параметров   \betaна каждом кадре отдельной фазовой функцией a=\Theta(p;β). В качестве этой функции могут выступать еще одна нейросеть или гауссовский процесс, в самой же статье предлагается использовать циклический кубический сплайн Catmull-Rom c 4-мя контрольными точками. Такой подход означает, что каждая контрольная точка a_k отражает конкретную конфигурацию весов a нейросети, а функция \Theta делает гладкую интерполяцию между этими конфигурациями.

 \Theta(p; \beta) = a_{k_1}

+w(\frac{1}{2}a_{k_2} - \frac{1}{2}a_{k_0})

+w^2(a_{k_0} - \frac{5}{2}a_{k_1} + 2a_{k_2} - \frac{1}{2}a_{k_3})

+w^3(\frac{3}{2}a_{k_1} - \frac{3}{2}a_{k_2} + \frac{1}{2}a_{k_3} - \frac{1}{2}a_{k_0})

w = \frac{4p}{2\pi} (mod 1)

k_n = \left[\frac{4p}{2\pi} \right] + n - 1 (mod 4)

Тренировка нейросети сводится к решению задачи оптимизации параметров фазовой функции \beta = \{a_0 a_1 a_2 a_3  \}. Используется следующая функция потерь:

Cost(X, Y, P, \beta) = \lVert Y - \Phi (X; \Theta(P; \beta))  \rVert_2^2 + \gamma \lvert \beta \rvert_1

  • \lVert Y - \Phi (X; \Theta(P; \beta))  \rVert_2^2— среднее квадратическое отклонение

  • \gamma \lvert \beta \rvert_1 — регуляризация, введеная, чтобы веса не были слишком большими (\gamma = 0.01)

В качестве оптимизатора выбран Adam

В режиме реального времени на каждый кадр анимации на вход нейросети подаются фаза p и вектор x. Скорость и направление движения, полученное с контроллера по каждому будущему кадру, смешивается с данными, предсказанными нейросетью на предыдущем кадре (t_{i+1}^p t_{i+1}^d), по следующей формуле:

TrajectoryBlend(a_0, a_1, t, \tau) = (1 - t^\tau) a_0 + t^\tau a_1

  • 0 \leq t \leq 1

  • \tau — дополнительное смещение (bias), контролирующее отзывчивость персонажа

Mode-adaptive neural networks for quadruped motion control (MANN) 

Данная работа посвящена развитию архитектуры PFNN с расширением до анимации четвероногих объектов. Ввиду принципиальных отличий в характере движений, невозможно определить одну фазу для всех четырех конечностей при смене походки. Ручная разметка неструктурированного набора данных также становится нецелесообразной. Поэтому фазовая функция заменяется дополнительной нейросетью. В итоге система включает в себя нейросеть предсказания движения и нейросеть выбора экспертов (gating network — аналогично архитектуре mixture of experts): если первая нейросеть предсказывает состояние персонажа, принимая на вход состояние персонажа с предыдущего кадра и данные с контроллера, то вторая нейросеть динамически обновляет веса первой нейросети посредством выбора и смешивания новой сущности под названием «экспертные веса», каждая из которых присуща конкретному шаблону движения. Такая архитектура позволяет нейросетям учиться на данных с неразмеченными походками, полностью исключая этап разметки фаз движения.

Классификация движений выполняется вручную с целью выделения 6-ти классов движения: передвижение, сидение, стояние, ожидание, лежание и прыжки. Делается это так, чтобы в режиме реального времени пользователь с помощью контроллера мог задавать класс движения.

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

Входные и выходные данные в целом аналогичны PFNN: также семплируются 5 кадров из будущего и 6 из прошлого, вычисляется root transformation персонажа, направление движения.

Вектор входных данных:

x_i = \{t_i^p t_i^d t_i^v t_i^{\hat v} t_i^a j_{i-1}^p j_{i-1}^r j_{i-1}^v \} \in R^n

  • i – текущий кадр

  • i-1 – предыдущий кадр

  • s – количество семплированных кадров (12 штук)

  • b – количество костей

  • t_i^p \in R^{2s} – позиции траектории движения в 2D горизонтальной плоскости

  • t_i^d \in R^{2s} – направления траектории движения в 2D горизонтальной плоскости

  • t_i^v \in R^{2s} – скорости в точках траектории движения в 2D горизонтальной плоскости

  • t_i^\hat v \in R^{1s} – желаемая скорость в точках траектории движения

  • t_i^a \in R^{6s} – one-hot вектор классов движения в точках траектории движения

  • j_{i-1}^p \in R^{3b} – локальные позиции костей

  • j_{i-1}^r \in R^{6b} – локальные вращения костей

  • j_{i-1}^v \in R^{3b} – локальные скорости костей

Добавление вращения костей в вектор входных данных позволило получить более отзывчивую анимацию.

Результатом инференса нейросети является вектор:

y_i = \{t_{i+1}^p t_{i+1}^d t_{i+1}^v j_i^p j_i^r j_i^v \dot r_i^x \dot r_i^z \dot r_i^a  \} \in R^m

  • i— текущий кадр

  • i+1 — следующий кадр

  • s — количество семплированных кадров (12 штук)

  • b — количество костей

  • t_{i+1}^p \in R^{2s}— позиции траектории движения

  • t_{i+1}^d \in R^{2s} — направления траектории движения 

  • t_{i+1}^v \in R^{2s} — скорости в точках траектории движения

  • j_i^p \in R^{3b}— локальные позиции костей

  • j_i^r \in R^{6b}— локальные вращения костей

  • j_i^v \in R^{3b} — локальные скорости костей

  • \dot r_i^x \in R— линейная скорость root transformation по оси X

  • \dot r_i^z \in R— линейная скорость root transformation по оси Z

  • \dot r_i^a ∈ R— угловая скорость root transformation в 2D горизонтальной плоскости

Вращения костей представлены в виде относительных векторов «вверх» и «вперед» чтобы исключить проблемы с интерполяцией кватернионов в процессе обучения нейросети.

Архитектура MANN

Архитектура MANN

Архитектура нейросети предсказания движения аналогична PFNN, веса a же вычисляются посредством смешивания K экспертных весов \beta = \{a_1, \dots, a_k \},

где a = \sum_{i=1}^K \omega_i a_i. K — настраиваемый мета параметр, зависимый от сложности и размера

тренировочных данных. \omega = \{ \omega_1, \dots , \omega_K \} — коэффициенты смешивания, которые вычисляет gating network.

Архитектура gating network состоит из 3-х слоев:

\Omega(\hat x; \mu) = \sigma(W_2^{'}ELU(W_1^{'} ELU(W_0^{'}\hat x + b_0^{'}) + b_1^{'}) + b_2^{'})

  • \hat x \in R^{19}подмножество x, в которое входят скорости костей стоп, текущие классы движения и желаемая скорость персонажа.

  • \sigma(\cdot)— softmax функция для нормализации входных данных так, что их сумма становится = 1, что необходимо для дальнейшего линейного смешивания

Параметры нейросети \mu определяются следующим образом:

\mu = \{W_0^{'} \in R^{h^{'}\times{19}}, W_1^{'} \in R^{h^{'}\times{h^{'}}}, W_2^{'} \in R^{K\times{h^{'}}}, b_0^{'} \in R^{h^{'}}, b_1^{'} \in R^{h^{'}}, b_2^{'} \in R^K \}

  • h^{'} — количество нейронов в скрытых слоях (32)

Тренировка нейросети сводится к нахождению Y для соответствующего X, что является типичный задачей регрессии. Используется следующая функция потерь (СКО между предсказанием и ground truth):

Cost(X, Y; \beta, \mu) = \lVert Y - \Theta(X, \Omega(\hat X; \mu); \beta \rVert_2^2

В качестве оптимизатора выбран AdamWR.

Learned motion matching (LMM)

LMM основан на собственной реализации motion matching, разработанной в стенах Ubisoft. Классический motion matching представлен 3-мя этапами: Projection, Stepping, Decompression - для каждого из которых предлагается альтернатива в виде обученной нейросети.

Motion matching в реализации Ubisoft

Motion matching в реализации Ubisoft

В качестве параметров позы для сценария передвижения предлагается использовать вектор из 27-ми элементов на каждый кадр анимации:

x=\{t^t t^d f^t \dot{f^t} \dot{h^t}\} \in R^{27}

  • t^t \in R^6 -  позиции траектории движения в 2D спроецированные на поверхность 20, 40, 60 кадров в будущем (при 60 FPS)

  • t^d \in R^6 - направление траектории движения 20, 40, 60 кадров в будущем

  • f^t \in R^6 – локальные позиции костей стоп

  • \dot{f^t} \in R^6– линейные скорости костей стоп

  • \dot{h^t} \in R^3 – линейная скорости кости бедра

Далее определяется вектор, содержащий в себе всю информацию о позе для каждого кадра анимации:

y = \{y^t y^r \dot{y^t} \dot{y^r} \dot{r^t} \dot{r^r} o^*\}

  • y^t \in R^3 – локальные позиции костей

  • y^r \in R^4 – локальные вращения костей в виде представления ось-угол

  • \dot{y^t} \in R^3 – линейные скорости костей

  • \dot{y^r} \in R^3 – угловые скорости костей

  • \dot{r^t} \in R^3 – линейные скорости root transformation 

  • \dot{r^r} \in R^3 – угловые скорости root transformation 

  • o^* – специфичные для задачи дополнительные данные (например, информация о контакте ног с поверхностью)

Эти вектора вычисляются для каждого кадра, объединяются в матрицы X= \left[x_0, x_1, \dots , x_{n-1}\right], Y=\left[y_0, y_1, \dots, y_{n-1} \right], называемые matching database и animation database соответственно, и используются в алгоритме тренировки. Далее в режиме реального времени каждые N кадров или когда сильно меняется ввод с контроллера, строится вектор запроса \hat x, аналогичный вектору параметров позы и подается в качестве входных данных в Projection. Как только найден новый кадр, воспроизведение анимации начинается с него и вставляется переход (transition).

  • Projection: выполняется поиск методом ближайшего соседа для нахождения в X вектора параметров позы, наиболее подходящего для \hat x.

  • Stepping: выполняется перемещение индекса в matching database.

  • Decompression: выполняется поиск позы в animation database, соответствующей текущему индексу в matching database.

Заменив каждый из этих этапов нейросетью, получится избавиться от необходимости хранения matching database и animation database в памяти. С этой целью вводятся 4 нейросети:

  1. Decompressor: устраняет необходимость хранить в памяти Y, принимает на вход x и латентный вектор z

  2. Compressor: выступает в роле энкодера для нахождения z посредством сжатия y в более малоразмерный вектор

  3. Stepper: совместно с projector устраняет необходимость хранить в памяти X, обучается динамике системы, вычисляя изменения значений x_i и z_i для получения x_{i+1} и z_{i+1} на следующий кадр

  4. Projector: эмулирует поиск методом ближайшего соседа, принимает на вход \hat x и предсказывает ближайшие x_{k^*} и z_{k^*}

Объединив эти 4 нейросети, получается learned motion matching, представленный на изображении ниже.

Поскольку вектор параметров позы обычно не содержит достаточно информации, необходимой для выведения соответствующей позы, вводится латентное пространство Z. Его значения находятся посредством нейросети Compressor — выполняется маппинг y_i к соответствующему z_i. Далее этот вектор конкатенируется к x_i и подается на вход в Decompressor, который пытается восстановить исходную позу y_i. Таким образом нейросеть находит какой информации не хватает в векторе параметров позы x и кодирует ее в z.

Ключевым аспектом тренировки Decompressor является функция потерь, разработанная для минимизации визуального восприятия ошибки, которая использует прямую кинематику для оценки ошибки в пространстве персонажа, также добавляются функции потерь, учитывающие скорость, чтобы результат гладко изменялся во времени.

Псевдокод алгоритма тренировки Compressor (C) + Decompressor (D)

Function \hspace{2mm} TrainDecompressor(X, Y, \Theta_C, \Theta_D):

\hspace{1cm} // \hspace{2mm} Compute \hspace{2mm} forward \hspace{2mm} kinematics

\hspace{1cm} Q \leftarrow ForwardKinematics(Y)

\hspace{1cm} // \hspace{2mm} Generate \hspace{2mm} latent \hspace{2mm} variables \hspace{2mm} Z

\hspace{1cm} Z \leftarrow C(\left[ YQ \right]^T; \Theta_C)

\hspace{1cm} // \hspace{2mm} Reconstruct \hspace{2mm} pose \hspace{2mm} \tilde Y

\hspace{1cm} \tilde Y \leftarrow D (\left[XZ \right]^T; \Theta_D)

\hspace{1cm} // \hspace{2mm} Recompute \hspace{2mm} forward \hspace{2mm}kinematics

\hspace{1cm} \tilde Q \leftarrow ForwardKinematics(\tilde Y)

\hspace{1cm} // \hspace{2mm} Compute \hspace{2mm} latent \hspace{2mm} regularization \hspace{2mm} losses

\hspace{1cm} \mathcal{L}_{lreg} \leftarrow w_{lreg} \lVert Z \rVert _2^2

\hspace{1cm} \mathcal{L}_{sreg} \leftarrow w_{sreg} \lVert Z \rVert _1

\hspace{1cm} \mathcal{L}_{vreg} \leftarrow w_{vreg} \left\Vert  \frac {Z_0 - Z_1}{\delta t} \right\Vert_1

\hspace{1cm} // \hspace{2mm} Local \hspace{2mm} \& \hspace{2mm} character \hspace{2mm} space \hspace{2mm} losses

\hspace{1cm} \mathcal{L}_{loc} \leftarrow w_{loc} \lVert Y \ominus \tilde Y \rVert_1

\hspace{1cm} \mathcal{L}_{chr} \leftarrow w_{chr} \lVert Q \ominus \tilde Q \rVert_1

\hspace{1cm} // \hspace{2mm} Local \hspace{2mm} \& \hspace{2mm} character \hspace{2mm} space \hspace{2mm} velocity \hspace{2mm} losses

\hspace{1cm} \mathcal{L}_{lvel} \leftarrow w_{lvel} \left\Vert  \frac {Y_0 \ominus Y_1}{\delta t} - \frac {\tilde Y_0 \ominus \tilde Y_1}{\delta t} \right\Vert_1

\hspace{1cm} \mathcal{L}_{cvel} \leftarrow w_{cvel} \left\Vert  \frac {Q_0 \ominus Q_1}{\delta t} - \frac {\tilde Q_0 \ominus \tilde Q_1}{\delta t} \right\Vert_1

\hspace{1cm} // \hspace{2mm} Update \hspace{2mm} network \hspace{2mm} parameters

\hspace{1cm} \Theta_C \Theta_D \leftarrow RAdam(\Theta_C \Theta_D, \nabla \sum\mathop{}_{*} \mathcal {L}_{*})

end

После тренировки для работы алгоритма в режиме реального времени нейросеть Compressor не требуется, поскольку она необходима только для нахождения Z, используемого для тренировки других нейросетей. 

Нейросеть Stepper тренируется брать на вход векторы x_i и z_i текущего кадра и выдавать дельту, добавляемую к ним, для получения векторов x_{i+1} и z_{i+1} на следующий кадр. Берется небольшое окно векторов X и Z и повторно предсказываются следующие значения параметров позы и латентных переменных и подаются на следующий кадр.

Псевдокод алгоритма тренировки Stepper

Function \hspace{2mm} TrainStepper(X, Z, s, \Theta_S):

\hspace{1cm} // \hspace{2mm} Set \hspace{2mm} initial \hspace{2mm} states

\hspace{1cm} \tilde X_0, \tilde Z_0 \leftarrow X_0, Z_0

\hspace{1cm} // \hspace{2mm} Predict \hspace{2mm} \tilde X \hspace{2mm} and \hspace{2mm} \tilde Z \hspace{2mm} over \hspace{2mm} a \hspace{2mm} window \hspace{2mm} of \hspace{2mm} s \hspace{2mm} frames

\hspace{1cm} for \hspace{2mm} i  \leftarrow 1 \hspace{2mm} to \hspace{2mm} s \hspace{2mm} do

\hspace{2cm} // \hspace{2mm} Predict \hspace{2mm} deltas \hspace{2mm} for \hspace{2mm} \tilde X \hspace{2mm} and \hspace{2mm} \tilde Z

\hspace{2cm} \delta \tilde x, \delta \tilde z \leftarrow S([\tilde X_{i-1} \tilde Z_{i-1}]^T; \Theta_S)

\hspace{2cm} \tilde X_i \leftarrow \tilde X_{i-1} + \delta  \tilde z

\hspace{2cm} \tilde Z_i \leftarrow \tilde Z_{i-1} + \delta  \tilde z

\hspace{1cm} end

\hspace{1cm} // \hspace{2mm} Compute \hspace{2mm} losses

\hspace{1cm} \mathcal{L}_{xval} \leftarrow w_{xval} ||X - \tilde X ||_1

\hspace{1cm} \mathcal{L}_{zval} \leftarrow w_{zval} ||Z - \tilde Z ||_1

\hspace{1cm} \mathcal{L}_{xvel} \leftarrow w_{xvel} \left\Vert  \frac {X_{0 \rightarrow s-1} - X_{1 \rightarrow s}}{\delta t} - \frac { \tilde X_{0 \rightarrow s-1} - \tilde X_{1 \rightarrow s}}{\delta t} \right\Vert_1

\hspace{1cm} \mathcal{L}_{zvel} \leftarrow w_{zvel} \left\Vert  \frac {Z_{0 \rightarrow s-1} - Z_{1 \rightarrow s}}{\delta t} - \frac { \tilde Z_{0 \rightarrow s-1} - \tilde Z_{1 \rightarrow s}}{\delta t} \right\Vert_1

\hspace{1cm} // \hspace{2mm} Update \hspace{2mm} network \hspace{2mm} parameters

\hspace{1cm} \Theta_S \leftarrow RAdam(\Theta_S, \nabla \sum\mathop{}_{*} \mathcal {L}_{*})

end

Наконец, нейросеть Projector позволяет полностью избавиться от необходимости хранения X и Z в памяти. Для ее обучения берут вектор x, семплируется гауссовский шум n и масштабируется случайным шумом n^\sigma, полученная величина добавляется к x для получения \hat x, по ней выполняется нахождение ближайших k^* методом ближайшего соседа. Projector далее обучается выдавать соответствующие вектора параметров позыx_{k^*} и латентные переменные z_{k^*}.

Псевдокод алгоритма тренировки Projector

Function \hspace{2mm} TrainProjector(x, X, Z, \Theta_{\mathcal{P}}):

\hspace{1cm} // Sample \hspace{2mm} uniform \hspace{2mm} noise \hspace{2mm} magnitude \hspace{2mm} n^\sigma

\hspace{1cm} n^\sigma \sim \mathcal{U}(0, 1)

\hspace{1cm} // \hspace{2mm} Sample \hspace{2mm} gaussian \hspace{2mm} noise \hspace{2mm} vector \hspace{2mm} n

\hspace{1cm} n \sim \mathcal{N}(0, 1)

\hspace{1cm} // \hspace{2mm} Add \hspace{2mm} noise \hspace{2mm} to \hspace{2mm} feature \hspace{2mm} vector

\hspace{1cm} \hat x \leftarrow x + n^\sigma n

\hspace{1cm} // \hspace{2mm} Find \hspace{2mm} nearest \hspace{2mm} neighbor

\hspace{1cm} k^* = Nearest(\hat x, X)

\hspace{1cm} // \hspace{2mm} Project \hspace{2mm} feature \hspace{2mm} vector

\hspace{1cm} \tilde x, \tilde z \leftarrow \mathcal{P}(\hat x, \Theta_{\mathcal{{P}}})

\hspace{1cm} // \hspace{2mm} Compute \hspace{2mm} losses

\hspace{1cm} \mathcal{L}_{xval} \leftarrow w_{xval} \lVert x_{k^*} - \tilde x \rVert_1

\hspace{1cm} \mathcal{L}_{zval} \leftarrow w_{zval} \lVert z_{k^*} - \tilde z \rVert_1

\hspace{1cm} \mathcal{L}_{dist} \leftarrow w_{dist} \left\Vert \lVert \hat x - x_{k^*} \rVert_2^2 -  \lVert \hat x - \tilde x \rVert_2^2 \right \Vert_1

\hspace{1cm} // \hspace{2mm} Update \hspace{2mm} network \hspace{2mm} parameters

\hspace{1cm} \Theta_{\mathcal{P}} \leftarrow RAdam(\Theta_{\mathcal{P}}, \nabla \sum\mathop{}_{*} \mathcal {L}_{*}))

end

Посредством сэмплирование шума разной величины, Projector делается устойчивым к возмущениям разного масштаба.

Для всех функций потерь во всех алгоритмах тренировки коэффициенты w* подбираются такими, чтобы уравнять исходные (на 1-й итерации обучения) полученные значения.

В качестве оптимизатора выбран RAdam.

Количество слоев, нейронов и функции активации для нейросетей представлены на изображении ниже:

Порядок работы архитектуры в режиме реального времени следующий:

  1. Формируется \hat x, подается на вход в Projector, который выдает x_{k^*}  и z_{k^*}

  2. Найденные x_{k^*} и z_{k^*} подаются на вход в Stepper, который перемещает их во времени

  3. Результат из шага 2 и подается на вход в Decompressor, который выдает конечную позу персонажа

Ограничения хабра не позволяют приложить демонстрацию работы, с видеоматериалами можно ознакомиться здесь.

Заключение

В статье не были приведены оценки качества работы и производительности рассмотренных подходов, с ними вы можете ознакомиться более подробно в первоисточниках.

Также не были разобраны некоторые важные составляющие (построение траектории движения, сопоставление персонажа с картой высот) ввиду выхода этих тем за рамки статьи. В следующей статье я планирую раскрыть часть из опущенных деталей.

Список литературы: