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

Статьи

Введение в робототехнику с Python для новичков

В руководстве для начинающих разбираются основы робототехники с использованием Python и библиотеки PyBullet. Описывается настройка симуляций, работа с URDF-файлами, анализ суставов и звеньев, а также управление движениями на примере робота R2-D2. Материал включает практические коды для создания простых 3D-моделей.

16 октября 2025 г.
12 мин
444

Введение

Робототехника представляет собой область науки, которая занимается проектированием и созданием механизмов, способных выполнять операции и принимать решения, имитируя или заменяя действия человека. Эта дисциплина объединяет несколько направлений:

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

На сегодняшний день ведущей платформой для робототехники служит ROS (Операционная система для роботов), которая координирует взаимодействие различных элементов робота, таких как датчики, двигатели, контроллеры и камеры. Все компоненты обмениваются данными модульным образом. Фреймворк ROS предназначен для работы с реальными прототипами роботов и поддерживает как Python, так и C++. Благодаря своей популярности, на базе ROS создано множество библиотек, включая Gazebo — наиболее продвинутый симулятор в 3D.

Поскольку Gazebo отличается высокой сложностью, для освоения фундаментальных принципов робототехники и создания доступных симуляций можно обойтись вне экосистемы ROS. Ключевые библиотеки на Python включают:

  • PyBullet (для новичков) — идеальна для экспериментов с URDF (Unified Robot Description Format), XML-схемой, описывающей структуру робота, его компоненты и геометрию.
  • Webots (средний уровень) — предлагает более точную физику, позволяя моделировать сложные сценарии.
  • MuJoCo (продвинутый уровень) — симулятор реального мира, применяемый в научных исследованиях. Библиотека RoboGYM от OpenAI построена на базе MuJoCo.

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

Настройка

PyBullet — это наиболее удобный симулятор физики для игр, визуальных эффектов, робототехники и обучения с подкреплением. Установка выполняется одной из команд (если pip не сработает, используйте conda):

pip install pybullet
conda install -c conda-forge pybullet

PyBullet поддерживает два основных режима работы:

  • p.GUI — открывает графическое окно для просмотра симуляции в реальном времени.
  • p.DIRECT — режим без графики, подходит для скриптов.
import pybullet as p
p.connect(p.GUI)  # или p.connect(p.DIRECT)

Как симулятор физики, первым шагом является установка гравитации:

p.resetSimulation()
p.setGravity(gravX=0, gravY=0, gravZ=-9.8)

Эта команда задает глобальный вектор гравитации. Значение «-9.8» по оси z соответствует ускорению 9,8 м/с² вниз, как на Земле. Без нее робот и другие объекты будут парить в невесомости, подобно состоянию в космосе.

Файл URDF

Если представить робота как человеческое тело, то файл URDF выступает в роли скелета, определяющего его физическую организацию. Он составлен в формате XML и служит чертежом механизма, описывая внешний вид робота и принципы его перемещения.

Мы начнем с создания простого куба, который является 3D-аналогом классического print(“hello world”).

urdf_string = '''
<robot name="cube_urdf">
<link name="cube_link">
<visual>
<geometry>
<box size="0.5 0.5 0.5"/><!-- куб 50 см -->
</geometry>
<material name="blue">
<color rgba="0 0 1 1"/>
</material>
</visual>
<collision>
<geometry>
<box size="0.5 0.5 0.5"/>
</geometry>
</collision>
<inertial>
<mass value="1.0"/>
<inertia ixx="0.0001" iyy="0.0001" izz="0.0001" ixy="0.0" ixz="0.0" iyz="0.0"/>
</inertial>
</link>
</robot>
'''

XML-код можно сохранить как файл URDF или использовать напрямую как строку.

import pybullet as p
import tempfile
## настройка
p.connect(p.GUI)
p.resetSimulation()
p.setGravity(gravX=0, gravY=0, gravZ=-9.8)
## создание временного файла URDF в памяти
with tempfile.NamedTemporaryFile(suffix=".urdf", mode="w", delete=False) as f:
    f.write(urdf_string)
    urdf_file = f.name
## загрузка URDF
p.loadURDF(fileName=urdf_file, basePosition=[0,0,1])
## рендеринг симуляции
for _ in range(240):
    p.stepSimulation()

Обратите внимание, что цикл выполняется 240 итераций. Почему именно 240? Это стандартный фиксированный шаг времени в разработке видеоигр, обеспечивающий плавность и отзывчивость без чрезмерной нагрузки на процессор. 60 FPS подразумевает отображение кадра каждые 1/60 секунды, а шаг физики в 1/240 секунды дает четыре обновления физики на каждый рендеринг.

В приведенном коде куб отображается с помощью p.stepSimulation(). Это означает, что симуляция не привязана к реальному времени, и вы сами управляете моментом следующего шага физики. В качестве альтернативы можно применить time.sleep для синхронизации с реальными часами.

import time
## рендеринг симуляции
for _ in range(240):
    p.stepSimulation()  # добавление шага физики (скорость CPU = 0,1 секунды)
    time.sleep(1/240)  # замедление до реального времени (240 шагов × 1/240 секунды = 1 секунда)

Далее XML-код для роботов станет значительно сложнее. К счастью, PyBullet поставляется с набором готовых файлов URDF. Загрузить стандартный куб можно без написания XML.

import pybullet_data
p.setAdditionalSearchPath(path=pybullet_data.getDataPath())
p.loadURDF(fileName="cube.urdf", basePosition=[0,0,1])

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

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

Рассмотрим пример с известным роботом R2-D2 из «Звездных войн».

Суставы

На этот раз необходимо импортировать плоскость, чтобы обеспечить поверхность для робота. Без нее объекты не смогут столкнуться с землей и будут падать бесконечно.

## настройка
p.connect(p.GUI)
p.resetSimulation()
p.setGravity(gravX=0, gravY=0, gravZ=-9.8)
p.setAdditionalSearchPath(path=pybullet_data.getDataPath())
## загрузка URDF
plane = p.loadURDF("plane.urdf")
robo = p.loadURDF(fileName="r2d2.urdf", basePosition=[0,0,0.1])
## рендеринг симуляции
for _ in range(240):
    p.stepSimulation()
    time.sleep(1/240)
p.disconnect()

Перед использованием робота следует разобраться в его составе. PyBullet стандартизировал структуру данных, так что любой импортированный робот описывается одинаковыми 17 универсальными атрибутами. Для анализа скриптом подключимся к серверу физики без интерфейса (p.DIRECT). Основная функция для изучения сустава — p.getJointInfo().

p.connect(p.DIRECT)
dic_info = {
    0:"joint Index",  # начинается с 0
    1:"joint Name",
    2:"joint Type",  # 0=революционный (вращательный), 1=призматический (скользящий), 4=фиксированный
    3:"state vectorIndex",
    4:"velocity vectorIndex",
    5:"flags",  # игнорировать, всегда 0
    6:"joint Damping",
    7:"joint Friction",  # коэффициент
    8:"joint lowerLimit",  # минимальный угол
    9:"joint upperLimit",  # максимальный угол
    10:"joint maxForce",  # максимальная сила
    11:"joint maxVelocity",  # максимальная скорость
    12:"link Name",  # дочернее звено, подключенное к суставу
    13:"joint Axis",
    14:"parent FramePos",  # позиция
    15:"parent FrameOrn",  # ориентация
    16:"parent Index"  # -1 = база
}
for i in range(p.getNumJoints(bodyUniqueId=robo)):
    print(f"--- JOINT {i} ---")
    joint_info = p.getJointInfo(bodyUniqueId=robo, jointIndex=i)
    print({dic_info[k]:joint_info[k] for k in dic_info.keys()})

Каждый сустав, будь то колесо, локоть или захват, должен предоставлять эти характеристики. Вышеуказанный код демонстрирует, что у R2-D2 15 суставов. Проанализируем первый, «base to right-leg»:

  • Тип сустава равен 4, что указывает на неподвижность. Родительское звено — -1, то есть соединено с базой, корневой частью робота (как позвоночник в скелете). Для R2-D2 база — это основной цилиндрический корпус, большая синяя и белая бочка.
  • Название звена — «right leg», следовательно, сустав связывает базу с правой ногой, но без моторизации. Это подтверждается нулевыми значениями оси сустава, демпфирования сустава и трения сустава.
  • Позиция и ориентация родительской рамки определяют точку крепления правой ноги к базе.

Звенья

С другой стороны, для анализа звеньев (сегментов физического тела) требуется перебор суставов с извлечением названия звена. Затем применяются две основные функции: p.getDynamicsInfo() для физических свойств звена и p.getLinkState() для его пространственного положения.

p.connect(p.DIRECT)
for i in range(p.getNumJoints(robo)):
    link_name = p.getJointInfo(robo, i)[12].decode('utf-8')  # поле 12="link Name"
    dyn = p.getDynamicsInfo(robo, i)
    pos, orn, *_ = p.getLinkState(robo, i)
    dic_info = {"Mass":dyn[0], "Friction":dyn[1], "Position":pos, "Orientation":orn}
    print(f"--- LINK {i}: {link_name} ---")
    print(dic_info)

Рассмотрим первое звено «right-leg». Масса в 10 кг влияет на гравитацию и инерцию, а трение определяет скольжение при контакте с поверхностью. Ориентация (0,707 = sin(45°)/cos(45°)) и позиция показывают, что звено правой ноги — цельный элемент, слегка выдвинутый вперед (на 5 см) и наклоненный на 90° относительно базы.

Движения

Теперь перейдем к суставу, способному на перемещение.

Например, сустав 2 — правое переднее колесо. Это тип=0, то есть вращательный. Сустав соединяет правую ногу (индекс звена 1) с правым передним колесом: base_link → right_leg → right_front_wheel. Ось сустава (0,0,1) указывает на вращение вокруг z-оси. Пределы (нижний=0, верхний=-1) означают бесконечное вращение, типичное для роторов.

Управление таким суставом просто. Основная команда для акселераторов — p.setJointMotorControl2(), позволяющая регулировать силу, скорость и положение. Здесь мы заставим колесо вращаться, применяя силу для постепенного разгона от нуля до целевой скорости с последующим поддержанием баланса сил (против трения, демпфирования, гравитации).

p.setJointMotorControl2(bodyUniqueId=robo, jointIndex=2, controlMode=p.VELOCITY_CONTROL, targetVelocity=10, force=5)

Если задействовать все четыре колеса аналогичной силой, робот двинется вперед. Из предыдущего анализа известно, что суставы колес — 2 и 3 (правая сторона), 6 и 7 (левая).

С учетом этого сначала заставим R2-D2 повернуться, вращая только одну сторону, а затем применим силу ко всем колесам для прямолинейного движения.

import pybullet as p
import pybullet_data
import time
## настройка
p.connect(p.GUI)
p.resetSimulation()
p.setGravity(gravX=0, gravY=0, gravZ=-9.8)
p.setAdditionalSearchPath(path=pybullet_data.getDataPath())
## загрузка URDF
plane = p.loadURDF("plane.urdf")
robo = p.loadURDF(fileName="r2d2.urdf", basePosition=[0,0,0.1])
## стабилизация
for _ in range(240):
    p.stepSimulation()
right_wheels = [2,3]
left_wheels = [6,7]
### сначала поворот
for _ in range(240):
    for j in right_wheels:
        p.setJointMotorControl2(bodyUniqueId=robo, jointIndex=j, controlMode=p.VELOCITY_CONTROL, targetVelocity=-100, force=50)
    for j in left_wheels:
        p.setJointMotorControl2(bodyUniqueId=robo, jointIndex=j, controlMode=p.VELOCITY_CONTROL, targetVelocity=100, force=50)
    p.stepSimulation()
    time.sleep(1/240)
### затем движение вперед
for _ in range(500):
    for j in right_wheels + left_wheels:
        p.setJointMotorControl2(bodyUniqueId=robo, jointIndex=j, controlMode=p.VELOCITY_CONTROL, targetVelocity=-100, force=10)
    p.stepSimulation()
    time.sleep(1/240)
p.disconnect()

Учтите, что масса каждого робота уникальна, поэтому движения зависят от физики симуляции (включая гравитацию). Можно экспериментировать с силой и скоростью для достижения нужного результата.

Заключение

Эта статья служит руководством по введению в PyBullet и созданию базовых 3D-симуляций в робототехнике. Мы изучили импорт URDF-объектов, анализ суставов и звеньев, а также взаимодействие с роботом R2-D2. В будущем появятся уроки с более сложными моделями.

Горячее

Загружаем популярные статьи...

Робототехника с Python: гид для новичков