Новости и статьи об искусственном интеллекте и нейросетях. Мы собираем и обрабатываем самую актуальную информацию из мира AI. О проекте

Статьи

Робототехника на Python: сравнение RL-алгоритмов

Статья объясняет основы обучения с подкреплением для робототехники на Python, включая создание 3D-сред с Gym и MuJoCo. Сравниваются алгоритмы Q-Learning, Actor-Critic и эволюционные методы на примере робота Ant, адаптированного для прыжков. Приводится код для настройки, обучения и тестирования моделей.

15 ноября 2025 г.
12 мин
1

Существует четыре основных вида машинного обучения:

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

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

Обучение с подкреплением активно применяется для обучения роботов. Яркий пример — автономный пылесос: при прохождении по запыленной поверхности он получает положительное вознаграждение (+1), а при столкновении со стеной — наказание (-1). Таким образом, робот определяет подходящие действия и избегает нежелательных.

В этой статье рассматривается, как создавать пользовательские 3D-среды для обучения роботов с использованием различных алгоритмов обучения с подкреплением. Приводится полезный код на Python, который можно легко адаптировать для аналогичных задач (скопировать, вставить, запустить), с подробными комментариями к каждой строке для удобства воспроизведения примера.

Настройка

В отличие от задач с учителем, требующих целевой переменной и обучающего набора, проблема RL включает:

  • Среда — окружение агента, которое назначает вознаграждения за действия и предоставляет новое состояние в результате принятого решения. По сути, это пространство, с которым взаимодействует ИИ (в примере с пылесосом — комната для уборки).
  • Действие — множество возможных действий агента в среде. Пространство действий может быть дискретным (фиксированное число вариантов, как в шахматах) или непрерывным (бесконечные состояния, как в вождении автомобиля или торговле).
  • Вознаграждение — последствие действия (+1/-1).
  • Агент — ИИ, который изучает оптимальный план действий в среде для максимизации вознаграждения.

Среди популярных 3D-симуляторов физики выделяются: PyBullet (для новичков), Webots (средний уровень), MuJoCo (продвинутый) и Gazebo (профессиональный). Их можно применять как самостоятельное ПО или интегрировать через Gym, библиотеку от OpenAI для разработки алгоритмов RL, построенную на различных физических движках.

В примере используется Gym (pip install gymnasium) для загрузки одной из стандартных сред на базе MuJoCo (Multi-Joint dynamics with Contact, pip install mujoco).

import gymnasium as gym
env = gym.make("Ant-v4")
obs, info = env.reset()
print(f"--- INFO: {len(info)} ---")
print(info, "\n")
print(f"--- OBS: {obs.shape} ---")
print(obs, "\n")
print(f"--- ACTIONS: {env.action_space} ---")
print(env.action_space.sample(), "\n")
print(f"--- REWARD ---")
obs, reward, terminated, truncated, info = env.step(
    env.action_space.sample()
)
print(reward, "\n")
Вывод информации о среде Ant-v4
Структура наблюдений и действий в среде Ant

Робот Ant представляет собой 3D-агента в виде четвероногого существа с туловищем и четырьмя конечностями. Каждая конечность состоит из двух сегментов, итого 8 суставов (гибкие элементы) и 9 звеньев (жесткие части). Цель среды — применять силы (толчки/тяги) и крутящие моменты (вращения) для перемещения робота в заданном направлении.

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

import time
env = gym.make("Ant-v4", render_mode="human")
obs, info = env.reset()
reset = False  # reset if the episode ends
episode = 1
total_reward, step = 0, 0
for _ in range(240):  # action
    step += 1
    action = env.action_space.sample()  # random action
    obs, reward, terminated, truncated, info = env.step(action)
    # reward
    total_reward += reward
    # render
    env.render()  # render physics step (CPU speed = 0.1 seconds)
    time.sleep(1/240)  # slow down to real-time (240 steps × 1/240 second sleep = 1 second)
    if (step == 1) or (step % 100 == 0):  # print first step and every 100 steps
        print(f"EPISODE {episode} - Step:{step}, Reward:{reward:.1f}, Total:{total_reward:.1f}")
    # reset
    if reset:
        if terminated or truncated:  # print the last step
            print(f"EPISODE {episode} - Step:{step}, Reward:{reward:.1f}, Total:{total_reward:.1f}")
            obs, info = env.reset()
            episode += 1
            total_reward, step = 0, 0
            print("------------------------------------------")
env.close()
Анимация случайных действий робота Ant

Пользовательская среда

Обычно среды обладают стандартными характеристиками:

  1. Сброс — для возврата к начальному состоянию или случайной точке в данных.
  2. Визуализация — для отображения происходящего.
  3. Шаг — для выполнения выбранного агентом действия и изменения состояния.
  4. Расчет вознаграждения — для присвоения подходящего вознаграждения или штрафа после действия.
  5. Получение информации — для сбора данных о сессии после действия.
  6. Завершение или обрезка — для определения, закончился ли эпизод после действия (провал или успех).

Стандартные среды в Gym удобны, но не всегда соответствуют требованиям проекта. Часто требуется разработка кастомной среды, адаптированной под задачу. Это ключевой этап в RL, поскольку качество модели напрямую зависит от дизайна среды.

Существует несколько подходов к созданию собственной среды:

  • Создание с нуля: проектирование всех аспектов (физики, корпуса, окружения). Полный контроль, но самый сложный метод, так как начинается с пустоты.
  • Изменение существующего XML-файла: каждый симулируемый агент описан XML-файлом. Можно корректировать физические параметры (например, увеличить высоту или массу робота), сохраняя логику.
  • Изменение существующего Python-класса: оставить агента и физику без изменений, но обновить правила (новые вознаграждения, условия завершения). Можно даже преобразовать непрерывную среду в дискретное пространство действий.

В примере адаптируется стандартная среда Ant для прыжков робота. Изменяются физические свойства в XML и функция вознаграждения в Python-классе. Достаточно усилить ноги и ввести вознаграждение за прыжки.

Сначала найдем XML-файл, скопируем и отредактируем его.

import os
print(os.path.join(os.path.dirname(gym.__file__), "envs/mujoco/assets/ant.xml"))

Чтобы сделать Ant более "прыгущим", уменьшим плотность тела для снижения массы...

Изменение плотности тела в XML-файле Ant

...и добавим силу к ногам для большего подъема (гравитация в симуляторе остается неизменной).

Добавление силы к суставам ног в XML

Полный отредактированный XML-файл доступен в репозитории.

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

from gymnasium.envs.mujoco.ant_v4 import AntEnv
from gymnasium.envs.registration import register
import numpy as np

## modify the class
class CustomAntEnv(AntEnv):
    def __init__(self, **kwargs):
        super().__init__(xml_file=os.getcwd()+"/assets/custom_ant.xml", **kwargs)  # specify xml_file only if modified

    def CUSTOM_REWARD(self, action, info):
        torso_height = float(self.data.qpos[2])  # torso z-coordinate = how high it is
        reward = np.clip(a=torso_height-0.6, a_min=0, a_max=1) *10  # when the torso is high
        terminated = bool(torso_height < 0.2 )  # if torso close to the ground
        info["torso_height"] = torso_height  # add info for logging
        return reward, terminated, info

    def step(self, action):
        obs, reward, terminated, truncated, info = super().step(action)  # override original step()
        new_reward, new_terminated, new_info = self.CUSTOM_REWARD(action, info)
        return obs, new_reward, new_terminated, truncated, new_info  # must return the same things

    def reset_model(self):
        return super().reset_model()  # keeping the reset as it is

## register the new env
register(id="CustomAntEnv-v1", entry_point="__main__:CustomAntEnv")

## test
env = gym.make("CustomAntEnv-v1", render_mode="human")
obs, info = env.reset()
for _ in range(1000):
    action = env.action_space.sample()
    obs, reward, terminated, truncated, info = env.step(action)
    if terminated or truncated:
        obs, info = env.reset()
env.close()
Анимация кастомной среды Ant с прыжками

Если 3D-мир и его правила спроектированы грамотно, достаточно эффективной RL-модели, и робот будет стремиться к максимуму вознаграждения. В RL доминируют две группы моделей: Q-Learning (оптимальны для дискретных пространств действий) и Actor-Critic (для непрерывных). Кроме них появляются новые экспериментальные методы, такие как эволюционные алгоритмы и обучение имитацией.

Q-Learning

Q-Learning — базовая форма обучения с подкреплением, использующая Q-значения ("Q" означает "качество") для оценки полезности действия в получении будущего вознаграждения. Проще говоря, если в конце игры агент получает вознаграждение после последовательности действий, начальное Q-значение — это дисконтированное будущее вознаграждение.

Иллюстрация Q-значений в Q-Learning

По мере исследования и получения откликов агент обновляет Q-значения в Q-матрице (уравнение Беллмана). Цель — выучить оптимальные Q-значения для каждого состояния и действия, чтобы принимать лучшие решения и максимизировать ожидаемое будущее вознаграждение для конкретного действия в конкретном состоянии.

В процессе обучения агент балансирует между исследованием и эксплуатацией. Сначала он исследует среду случайными действиями, собирая опыт (информацию о вознаграждениях для разных действий и состояний). По мере обучения и снижения уровня исследования он переходит к эксплуатации знаний, выбирая действия с наивысшими Q-значениями для каждого состояния.

Обратите внимание, что Q-матрица может быть многомерной и сложной. Например, в алгоритме торговли:

Пример многомерной Q-матрицы для торговли

В 2013 году произошел прорыв в RL, когда Google представил Deep Q-Network (DQN), способный играть в Atari-игры по сырым пикселям, объединяя глубокое обучение и Q-Learning. Проще говоря, глубокое обучение аппроксимирует Q-значения вместо хранения в таблице. Это достигается с помощью нейронной сети, обученной предсказывать Q-значения для каждого возможного действия на основе текущего состояния среды.

Архитектура DQN для Atari

Семейство Q-Learning предназначено в основном для дискретных сред, поэтому оно не подходит напрямую для робота Ant. Альтернатива — дискретизация среды (хотя это не самый эффективный способ для непрерывных задач). Нужно создать обертку для Python-класса, ожидающую дискретное действие (например, "двигаться вперед"), и преобразующую его в силы на суставы.

class DiscreteEnvWrapper(gym.Env):
    def __init__(self, render_mode=None):
        super().__init__()
        self.env = gym.make("CustomAntEnv-v1", render_mode=render_mode)
        self.action_space = gym.spaces.Discrete(5)  # will have 5 actions
        self.observation_space = self.env.observation_space  # same observation space
        n_joints = self.env.action_space.shape[0]
        self.action_map = [
            # action 0 = stand still
            np.zeros(n_joints),
            # action 1 = push all forward
            0.5*np.ones(n_joints),
            # action 2 = push all backward
            -0.5*np.ones(n_joints),
            # action 3 = front legs forward + back legs backward
            0.5*np.concatenate([np.ones(n_joints//2), -np.ones(n_joints//2)]),
            # action 4 = front legs backward + back legs forward
            0.5*np.concatenate([-np.ones(n_joints//2), np.ones(n_joints//2)])
        ]

    def step(self, discrete_action):
        assert self.action_space.contains(discrete_action)
        continuous_action = self.action_map[discrete_action]
        obs, reward, terminated, truncated, info = self.env.step(continuous_action)
        return obs, reward, terminated, truncated, info

    def reset(self, **kwargs):
        obs, info = self.env.reset(**kwargs)
        return obs, info

    def render(self):
        return self.env.render()

    def close(self):
        self.env.close()

    ## test
env = DiscreteEnvWrapper()
obs, info = env.reset()
print(f"--- INFO: {len(info)} ---")
print(info, "\n")
print(f"--- OBS: {obs.shape} ---")
print(obs, "\n")
print(f"--- ACTIONS: {env.action_space} ---")
discrete_action = env.action_space.sample()
continuous_action = env.action_map[discrete_action]
print("discrete:", discrete_action, "-> continuous:", continuous_action, "\n")
print(f"--- REWARD ---")
obs, reward, terminated, truncated, info = env.step(
    discrete_action
)
print(reward, "\n")
Вывод дискретной обертки для Ant

Теперь среда с 5 возможными действиями совместима с DQN. В Python самый простой способ применять глубокие RL-алгоритмы — через Stable Baselines3 (pip install stable-baselines3), коллекцию готовых моделей на PyTorch (pip install torch). Полезно отслеживать прогресс обучения в TensorBoard (pip install tensorboard). Создается папка "logs", и запускается tensorboard --logdir=logs/ для локального дашборда (http://localhost:6006/).

import stable_baselines3 as sb
from stable_baselines3.common.vec_env import DummyVecEnv

# TRAIN
env = DiscreteEnvWrapper(render_mode=None)  # no rendering to speed up
env = DummyVecEnv([lambda:env])
model_name = "ant_dqn"
print("Training START")
model = sb.DQN(
    policy="MlpPolicy",
    env=env,
    verbose=0,
    learning_rate=0.005,
    exploration_fraction=0.2,
    exploration_final_eps=0.05,  # eps decays linearly from 1 to 0.05
    tensorboard_log="logs/"
)  # >tensorboard --logdir=logs/
model.learn(
    total_timesteps=1_000_000,  # 20min
    tb_log_name=model_name,
    log_interval=10
)
print("Training DONE")
model.save(model_name)

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

# TEST
env = DiscreteEnvWrapper(render_mode="human")
model = sb.DQN.load(path=model_name, env=env)
obs, info = env.reset()
reset = False  # reset if episode ends
episode = 1
total_reward, step = 0, 0
for _ in range(1000):
    # action
    step += 1
    action, _ = model.predict(obs)
    obs, reward, terminated, truncated, info = env.step(action)
    # reward
    total_reward += reward
    # render
    env.render()
    time.sleep(1/240)
    if (step == 1) or (step % 100 == 0):  # print first step and every 100 steps
        print(f"EPISODE {episode} - Step:{step}, Reward:{reward:.1f}, Total:{total_reward:.1f}")
    # reset
    if reset:
        if terminated or truncated:  # print the last step
            print(f"EPISODE {episode} - Step:{step}, Reward:{reward:.1f}, Total:{total_reward:.1f}")
            obs, info = env.reset()
            episode += 1
            total_reward, step = 0, 0
            print("------------------------------------------")
env.close()
Анимация обученного DQN-агента Ant

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

Actor-Critic

На практике алгоритмы Actor-Critic наиболее востребованы, так как идеально подходят для непрерывных сред. Основная концепция — два cooperating системы: функция политики ("Актер") для выбора действий и функция ценности ("Критик") для оценки ожидаемого вознаграждения. Модель корректирует принятие решений, сравнивая реальные вознаграждения с предсказаниями.

Первый стабильный алгоритм глубокого обучения — Advantage Actor-Critic (A2C) от OpenAI в 2016 году. Он минимизирует разницу между реальным вознаграждением после действия Актера и оценкой Критика. Нейронная сеть имеет общий входной слой для обоих, но выдает два выхода: Q-значения действий (как в DQN) и предсказанное вознаграждение (добавление A2C).

Архитектура Actor-Critic в A2C

Со временем AC-алгоритмы эволюционировали в более стабильные варианты, такие как Proximal Policy Optimization (PPO) и Soft Actor-Critic (SAC). Последний использует не одну, а две сети Критиков для дополнительной оценки. Эти модели применяются напрямую в непрерывных средах.

# TRAIN
env_name, model_name = "CustomAntEnv-v1", "ant_sac"
env = gym.make(env_name)  # no rendering to speed up
env = DummyVecEnv([lambda:env])
print("Training START")
model = sb.SAC(
    policy="MlpPolicy",
    env=env,
    verbose=0,
    learning_rate=0.005,
    ent_coef=0.005,  # exploration
    tensorboard_log="logs/"
)  # >tensorboard --logdir=logs/
model.learn(
    total_timesteps=100_000,  # 3h
    tb_log_name=model_name,
    log_interval=10
)
print("Training DONE")
# save
model.save(model_name)

Обучение SAC занимает больше времени, но дает значительно лучшие результаты.

# TEST
env = gym.make(env_name, render_mode="human")
model = sb.SAC.load(path=model_name, env=env)
obs, info = env.reset()
reset = False  # reset if the episode ends
episode = 1
total_reward, step = 0, 0
for _ in range(1000):
    # action
    step += 1
    action, _ = model.predict(obs)
    obs, reward, terminated, truncated, info = env.step(action)
    # reward
    total_reward += reward
    # render
    env.render()
    time.sleep(1/240)
    if (step == 1) or (step % 100 == 0):  # print first step and every 100 steps
        print(f"EPISODE {episode} - Step:{step}, Reward:{reward:.1f}, Total:{total_reward:.1f}")
    # reset
    if reset:
        if terminated or truncated:  # print the last step
            print(f"EPISODE {episode} - Step:{step}, Reward:{reward:.1f}, Total:{total_reward:.1f}")
            obs, info = env.reset()
            episode += 1
            total_reward, step = 0, 0
            print("------------------------------------------")
env.close()
Анимация обученного SAC-агента Ant

Учитывая популярность Q-Learning и Actor-Critic, появились гибридные варианты, сочетающие подходы и расширяющие DQN на непрерывные пространства. Например, Deep Deterministic Policy Gradient (DDPG) и Twin Delayed DDPG (TD3). Однако чем сложнее модель, тем труднее обучение.

Экспериментальные модели

Помимо основных семейств (Q и AC) существуют менее распространенные, но интересные модели. Они особенно полезны для задач с редкими вознаграждениями, которые сложно спроектировать. Например:

  • Эволюционные алгоритмы развивают политики через мутации и отбор, а не градиенты. Вдохновленные эволюцией Дарвина, они надежны, но требуют больших вычислительных ресурсов.
  • Обучение имитацией пропускает исследование и учит агентов копировать демонстрации экспертов. Основано на "клонировании поведения", сочетая обучение с учителем и идеи RL.

Для экспериментов рассмотрим первый вариант с EvoTorch, открытым инструментарием для нейроэволюции, совместимым с PyTorch и Gym (pip install evotorch).

Лучший эволюционный алгоритм для RL — Policy Gradients with Parameter Exploration (PGPE). Он не обучает одну нейронную сеть напрямую, а строит вероятностное распределение (гауссово) по всем возможным весам (μ — среднее множество весов, σ — разброс для исследования). В каждой генерации PGPE семплирует из популяции весов, начиная со случайной политики. Затем модель корректирует среднее и дисперсию на основе вознаграждения (эволюция популяции). PGPE считается параллелизованным RL, поскольку, в отличие от классических методов вроде Q и AC, обновляющих одну политику батчами, он параллельно семплирует множество вариаций политик.

Перед обучением определяется "проблема" — задача оптимизации (наша среда).

from evotorch.neuroevolution import GymNE
from evotorch.algorithms import PGPE
from evotorch.logging import StdOutLogger

## problem
train = GymNE(
    env=CustomAntEnv,  # directly the class because it's custom env
    env_config={"render_mode":None},  # no rendering to speed up
    network="Linear(obs_length, act_length)",  # linear policy
    observation_normalization=True,
    decrease_rewards_by=1,  # normalization trick to stabilize evolution
    episode_length=200,  # steps per episode
    num_actors="max"  # use all available CPU cores
)

## model
model = PGPE(
    problem=train,
    popsize=20,
    stdev_init=0.1,  # keep it small
    center_learning_rate=0.005,
    stdev_learning_rate=0.1,
    optimizer_config={"max_speed":0.015}
)

## train
StdOutLogger(searcher=model, interval=20)
model.run(num_generations=100)
Лог обучения PGPE в EvoTorch

Для тестирования создается визуализирующая "проблема". Затем извлекается лучший набор весов из центра распределения (во время обучения гауссиана смещается к улучшенным областям пространства политик).

## visualization problem
test = GymNE(
    env=CustomAntEnv,
    env_config={"render_mode":"human"},
    network="Linear(obs_length, act_length)",
    observation_normalization=True,
    decrease_rewards_by=1,
    num_actors=1  # only need 1 for visualization
)

## test best policy
population_center = model.status["center"]
policy = test.to_policy(population_center)

## render
test.visualize(policy)
Анимация эволюционного агента Ant с PGPE

Заключение

Эта статья служит руководством по применению обучения с подкреплением в робототехнике. Рассматривается создание 3D-симуляций с Gym и MuJoCo, адаптация среды и выбор подходящих RL-алгоритмов для разных сценариев. В будущем ожидаются руководства по более сложным роботам.