Создавайте 3D-симуляции с помощью PyBullet
Делиться

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

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

Поскольку это руководство для начинающих, я покажу, как создавать простые 3D-модели с помощью PyBullet . Я покажу несколько полезных фрагментов кода на Python, которые можно легко применить в других подобных случаях (просто скопируйте, вставьте и запустите), и прокомментирую каждую строку кода, чтобы вы могли повторить этот пример.
Настраивать
PyBullet — самый удобный физический симулятор для игр, визуальных эффектов, робототехники и обучения с подкреплением. Вы можете легко установить его одной из следующих команд (если pip не работает, conda точно должен подойти):
pip install pybullet conda install -c conda-forge pybullet
PyBullet можно запустить в двух основных режимах:
- p.GUI → открывает окно и показывает симуляцию в реальном времени.
- p.DIRECT → без графического режима, используется для скриптов.
импортировать pybullet как 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-эквивалентом печати («hello world»).
urdf_string = «»»»
Вы можете сохранить код XLM как файл URDF или использовать его как строку.
import pybullet as p import tempfile ## setup p.connect(p.GUI) p.resetSimulation() p.setGravity(gravX=0, gravY=0, gravZ=-9.8) ## создать временный файл URDF в памяти с помощью 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 секунды обеспечивает 4 обновления физики для каждого отрисованного кадра.
В предыдущем коде я визуализировал куб с помощью p.stepSimulation(). Это означает, что симуляция происходит не в реальном времени, и вы сами управляете моментом выполнения следующего шага физики. В качестве альтернативы можно использовать time sleep для привязки к часам реального мира.
import time ## рендер симуляции для _ в диапазоне (240): p.stepSimulation() #добавление шага физики (скорость ЦП = 0,1 секунды) time.sleep(1/240) #замедление до реального времени (240 шагов × 1/240 секунды, сон = 1 секунда)

В дальнейшем XML-код для роботов станет значительно сложнее. К счастью, PyBullet поставляется с набором готовых URDF-файлов. Вы можете легко загрузить куб по умолчанию, не создавая для него XML.
импорт pybullet_data p.setAdditionalSearchPath(path=pybullet_data.getDataPath()) p.loadURDF(fileName=»cube.urdf», basePosition=[0,0,1])
По сути, файл URDF определяет два основных элемента: звенья и сочленения . Звенья — это жёсткие части робота (например, наши кости), а сочленение — это соединение двух жёстких звеньев (например, наши мышцы). Без сочленений ваш робот был бы просто статуей, но с сочленениями он становится машиной, обладающей движением и функцией.
Вкратце, каждый робот представляет собой дерево звеньев, соединённых шарнирами. Каждый шарнир может быть неподвижным (неподвижным), вращаться («вращательный шарнир») и скользить («призматический шарнир»). Поэтому вам необходимо знать, какого робота вы используете.
Давайте рассмотрим пример знаменитого робота R2D2 из «Звездных войн».

Суставы
На этот раз нам придётся импортировать ещё и плоскость , чтобы создать поверхность для робота. Без плоскости у объектов не было бы поверхности, с которой они могли бы столкнуться, и они бы просто падали бесконечно.
## настройка 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]) ## рендеринг симуляции для _ в диапазоне (240): p.stepSimulation() time.sleep(1/240) p.disconnect()

Прежде чем использовать робота, необходимо разобраться в его компонентах. PyBullet стандартизировал структуру данных, чтобы каждый импортируемый робот всегда определялся одними и теми же 17 универсальными атрибутами . Поскольку я хочу просто запустить скрипт, я подключусь к физическому серверу без графического интерфейса (p.DIRECT). Основная функция для анализа сочленения — p.getJointInfo().
p.connect(p.DIRECT) dic_info = { 0:»Индекс сочленения», #начинается с 0 1:»Имя сочленения», 2:»Тип сочленения», #0 = вращательное, 1 = призматическое, 4 = фиксированное 3:»Индекс вектора состояния», 4:»Индекс вектора скорости», 5:»Флаги», #nvm всегда 0 6:»Демпфирование сочленения», 7:»Трение сочленения», #коэффициент 8:»Нижний предел сочленения», #минимальный угол 9:»Верхний предел сочленения», #максимальный угол 10:»Сила сочленения», #максимально допустимая сила 11:»Скорость сочленения», #максимальная скорость 12:»Имя ссылки», #дочерняя ссылка, подключенная к этому сочленению 13:»Ось сочленения», 14:»Положение родительского кадра», #позиция 15:»Возврат родительского кадра», #ориентация 16:»Индекс родительского кадра» #−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()})

Каждый сустав, будь то колесо, локоть или захват, должен обладать теми же свойствами. Код выше показывает, что у R2D2 15 суставов. Давайте проанализируем первый, называемый «от основания к правой ноге»:
- Тип сочленения — 4, что означает, что оно неподвижно. Родительское звено — -1, что означает, что оно соединено с основанием , корневой частью робота (например, позвоночником нашего скелета). Для R2D2 основание — это основной цилиндрический корпус, большой сине-белый ствол.
- Название соединения — «правая нога», поэтому мы понимаем, что это сочленение соединяет основание робота с правой ногой, но оно не моторизовано. Это подтверждается нулем в параметрах оси сочленения , амортизации сочленения и трения сочленения .
- Положение и ориентация родительской рамы определяют, где правая ножка крепится к основанию.
Ссылки
С другой стороны, для анализа связей (сегментов физического тела) необходимо выполнить цикл по сочленениям, чтобы извлечь имя связи. Затем можно использовать две основные функции: 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') #field 12=»Имя ссылки» dyn = p.getDynamicsInfo(robo, i) pos, orn, *_ = p.getLinkState(robo, i) dic_info = {«Масса»:dyn[0], «Трение»:dyn[1], «Положение»:pos, «Ориентация»:orn} print(f»— LINK {i}: {link_name} —«) print(dic_info)

Рассмотрим первый элемент, «правую ногу». Масса 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 (левая сторона).

Учитывая это, я сначала заставлю R2D2 развернуться, вращая только одну сторону, а затем приложу силу к каждому колесу одновременно, чтобы заставить его двигаться вперед.
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 и анализировать сочленения и звенья. Мы также поработали со знаменитым роботом R2D2. В будущем появятся новые руководства с более продвинутыми роботами.
Полный код для этой статьи: GitHub
Надеюсь, вам понравилось! Обращайтесь ко мне с вопросами и отзывами, а также просто делитесь своими интересными проектами.
👉 Давайте общаться 👈

(Все изображения принадлежат автору, если не указано иное)
Источник: towardsdatascience.com



























