Проектирование и управление манипулятором робота с помощью языка программирования Python

PyBullet — это интерфейс прикладного программирования (API), который позволяет вам иметь быстрый доступ к моделированию роботов с помощью механизма моделирования Bullet Physics SDK. Имея модель робота, вы готовы сразу приступить к программированию своего робота без необходимости трудоемкой настройки моделирования, как в операционной системе роботов (ROS). Это быстрый инструмент для исследований и обучения.

В этой статье вы узнаете, как создать модель SCARA (шарнирно-сочлененная рука робота с селективным соответствием требованиям) и смоделировать ее с помощью PyBullet с использованием языка программирования Python.

Благодаря расположению шарнира SCARA с параллельными осями, рука слегка податлива в направлении X-Y, но жестка в направлении Z, отсюда и термин селективная податливость. Это выгодно для многих типов сборочных операций, например, для вставки круглого штифта в круглое отверстие без заедания. Википедия

Модели роботов могут быть разработаны с использованием программного обеспечения CAD (автоматизированного проектирования), такого как Fusion360, SolidWorks, Blender и FreeCAD, или запрограммированы с помощью таких инструментов, как OpenSCAD. Программное обеспечение для моделирования роботов, такое как ROS2 или PyBullet, должно считывать файлы роботов в определенных форматах файлов, и один из этих форматов называется URDF (унифицированный формат описания роботов), который определяет физические свойства робота. Программное обеспечение CAD может преобразовывать модели роботов в URDF, но поскольку наш робот SCARA представляет собой простую модель, мы сами напишем код URDF, чтобы лучше понять, как он работает.

Наш робот scara будет роботом 2R (2 вращающихся шарнира) с 2 степенями свободы (степень свободы) и без захвата (или рабочего органа).

Чтобы начать изучать операционную систему роботов (ROS2) для управления роботами, ознакомьтесь с нашим всеобъемлющим курсом для начинающих.

Создание нашего робота Scara

Мы создадим файл и назовем его scara_bot.urdf. Откройте этот файл в любом текстовом редакторе, чтобы начать писать код. URDF — это язык разметки, поэтому мы начнем файл с версии XML (расширяемый язык разметки) и тегом robot. Все, что касается нашего робота, должно быть внутри открывающего (‹robot›) и закрывающего (‹/robot›) тега robot.

<?xml version="1.0"?>
<robot name="scara_robot">

</robot>

У роботов есть цвета, поэтому мы определим некоторые цветовые материалы, которые будут иметь звенья или тела нашего робота. Эти цвета будут использоваться позже в коде, вызывая имена, которые мы им даем.

<?xml version="1.0"?>
<robot name="scara_robot">
    <material name="blue">
        <color rgba="0.0 0.0 1.0 1.0"/>
    </material>
    <material name="black">
        <color rgba="0.0 0.0 0.0 1.0"/>
    </material>
    <material name="white">
        <color rgba="1.0 1.0 1.0 1.0"/>
    </material>
</robot>

База

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

Давайте создадим черную цилиндрическую основу для нашего робота и укажем ее положение относительно начала координат:

<?xml version="1.0"?>
<robot name="scara_robot">
    <material name="blue">
        <color rgba="0.0 0.0 1.0 1.0"/>
    </material>
    <material name="black">
        <color rgba="0.0 0.0 0.0 1.0"/>
    </material>
    <material name="white">
        <color rgba="1.0 1.0 1.0 1.0"/>
    </material>
    <link name="world"/>
    <link name="base_link">
        <visual>
            <origin xyz="0.0 0.0 0.4" rpy="0.0 0.0 0.0"/>
            <geometry>
                <cylinder length="0.8" radius="0.1"/>
            </geometry>
            <material name="black"/>
        </visual>
    </link>
</robot>

Теперь мы закрепим базу на мире, чтобы она не упала. Это достигается путем создания фиксированного соединения между base_link и ссылкой world:

<?xml version="1.0"?>
<robot name="scara_robot">
    <material name="blue">
        <color rgba="0.0 0.0 1.0 1.0"/>
    </material>
    <material name="black">
        <color rgba="0.0 0.0 0.0 1.0"/>
    </material>
    <material name="white">
        <color rgba="1.0 1.0 1.0 1.0"/>
    </material>

    <link name="world"/>
    <link name="base_link">
        <visual>
            <origin xyz="0.0 0.0 0.4" rpy="0.0 0.0 0.0"/>
            <geometry>
                <cylinder length="0.8" radius="0.1"/>
            </geometry>
            <material name="black"/>
        </visual>
    </link>
    <joint name="base_joint" type="fixed">
        <parent link="world"/>
        <child link="base_link"/>
    </joint>
</robot>

Чтобы увидеть, как выглядит ваша модель, перетащите файл URDF на этот сайт, он покажет вам 3D-модель робота.

Верхняя рука

Наш робот-скара будет иметь два подвижных звена, которые будут соединены между собой шарниром. Первое звено, называемое верхним плечом или плечом1, будет соединено с базовой_связью, но на этот раз соединение будет не фиксированным, а вращающимся. Звено Руко1, шарнир которого вращается вокруг оси Z, будет смоделировано в виде параллелепипеда или прямоугольного параллелепипеда и окрашено в белый цвет.

<?xml version="1.0"?>
<robot name="scara_robot">
    <material name="blue">
        <color rgba="0.0 0.0 1.0 1.0"/>
    </material>
    <material name="black">
        <color rgba="0.0 0.0 0.0 1.0"/>
    </material>
    <material name="white">
        <color rgba="1.0 1.0 1.0 1.0"/>
    </material>

    <link name="world"/>
    <link name="base_link">
        <visual>
            <origin xyz="0.0 0.0 0.4" rpy="0.0 0.0 0.0"/>
            <geometry>
                <cylinder length="0.8" radius="0.1"/>
            </geometry>
            <material name="black"/>
        </visual>
    </link>
    <joint name="base_joint" type="fixed">
        <parent link="world"/>
        <child link="base_link"/>
    </joint>

    <link name="arm1">
        <visual>
            <origin xyz="0.0 -0.5 0.0" rpy="0.0 0.0 0.0"/>
            <geometry>
                <box size="0.1 1.0 0.1"/>
            </geometry>
            <material name="white"/>
        </visual>
    </link>
    <joint name="arm1_joint" type="revolute">
        <origin xyz="0.0 0.0 0.8" rpy="0.0 0.0 0.0"/>
        <parent link="base_link"/>
        <child link="arm1"/>
        <axis xyz="0.0 0.0 1.0"/>
        <limit lower="-1.0" upper="1.0" effort="1000.0" velocity="1000.0"/>
    </joint>
</robot>

Давайте визуализируем подвижную руку, которую мы только что создали, используя веб-средство просмотра URDF:

Нижняя рука

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

<?xml version="1.0"?>
<robot name="scara_robot">
    <material name="blue">
        <color rgba="0.0 0.0 1.0 1.0"/>
    </material>
    <material name="black">
        <color rgba="0.0 0.0 0.0 1.0"/>
    </material>
    <material name="white">
        <color rgba="1.0 1.0 1.0 1.0"/>
    </material>

    <link name="world"/>
    <link name="base_link">
        <visual>
            <origin xyz="0.0 0.0 0.4" rpy="0.0 0.0 0.0"/>
            <geometry>
                <cylinder length="0.8" radius="0.1"/>
            </geometry>
            <material name="black"/>
        </visual>
    </link>
    <joint name="base_joint" type="fixed">
        <parent link="world"/>
        <child link="base_link"/>
    </joint>

    <link name="arm1">
        <visual>
            <origin xyz="0.0 -0.5 0.0" rpy="0.0 0.0 0.0"/>
            <geometry>
                <box size="0.1 1.0 0.1"/>
            </geometry>
            <material name="white"/>
        </visual>
    </link>
    <joint name="arm1_joint" type="revolute">
        <origin xyz="0.0 0.0 0.8" rpy="0.0 0.0 0.0"/>
        <parent link="base_link"/>
        <child link="arm1"/>
        <axis xyz="0.0 0.0 1.0"/>
        <limit lower="-1.0" upper="1.0" effort="1000.0" velocity="1000.0"/>
    </joint>

    <link name="arm2">
        <visual>
            <origin xyz="0.0 -0.25 0.0" rpy="0.0 0.0 0.0"/>
            <geometry>
                <box size="0.1 0.5 0.1"/>
            </geometry>
            <material name="blue"/>
        </visual>
    </link>
    <joint name="arm2_joint" type="revolute">
        <origin xyz="0.0 -1.0 0.0" rpy="0.0 0.0 0.0"/>
        <parent link="arm1"/>
        <child link="arm2"/>
        <axis xyz="0.0 0.0 1.0"/>
        <limit lower="-1.0" upper="1.0" effort="1000.0" velocity="1000.0"/>
    </joint>

</robot>

Попробуйте визуализировать полную настройку робота с помощью онлайн-просмотра URDF и взаимодействуйте с моделью, перемещая связи через суставы. Давайте перейдем к другой части этой статьи, где мы пишем программу на Python, используя API PyBullet для моделирования нашей модели робота.

PyBullet: моделирование роботов

Отправной точкой для моделирования робота является наличие модели робота в формате, понятном симулятору робота, который мы сделали выше. PyBullet — это API, который позволяет вам управлять моделями роботов с помощью модулей Python и языка программирования python. Мы будем управлять нашим роботом-скарой, используя интерфейсы, предоставляемые PyBullet.

Чтобы установить PyBullet на Linux (Ubuntu) или Mac, откройте терминал (Ctrl+Alt+T), скопируйте и вставьте строку ниже:

sudo pip3 install pybullet

Для Windows выполните шаги здесь.

Код

Создайте свой файл python в том же каталоге/папке, где находится URDF. Назовем его my_robot.py и откроем в любом текстовом редакторе, в нашем случае в VS Code.

Импортируйте модуль PyBullet, который предоставляет вам доступ к API. PyBullet_data позволяет вам использовать файлы данных, поставляемые с PyBullet, а time — это модуль, который помогает ввести задержку в нашу симуляцию.

import pybullet as p
import pybullet_data
import time

Первое, что нужно сделать после импорта модулей, — это подключиться к физическому моделированию с помощью встроенного физического сервера под названием GUI. Чтобы увидеть, как выглядит PyBullet, мы запустим симуляцию без нашей модели робота. Не так быстро! Нам нужен метод stepSimulation(), чтобы запустить симуляцию с временным шагом по умолчанию, равным 1/240 секунды. Теперь наш код должен выглядеть так:

import pybullet as p
import pybullet_data
import time

p.connect(p.GUI)

while True:
    p.stepSimulation()
    time.sleep(1./240)

Если вы используете VS Code, вы можете запустить код с помощью Ctrl + F5 или открыть терминал (Ctrl + Alt + T), перейти в каталог кода и запустить файл с помощью Python3:

$ cd scara_robot/
~/scara_robot$ python3 my_robot.py

Представление PyBullet после запуска кода показано ниже:

Наша среда моделирования — это пустой мир или пространство, в котором нет земли, поэтому наш робот упадет, если мы поместим его в этот мир. Давайте дадим слово этой среде. Этот пол представляет собой сине-белую плитку под названием plane, расположенную внутри PyBullet, доступ к которой можно получить через pybullet_data, как показано ниже.

import pybullet as p
import pybullet_data
import time

p.connect(p.GUI)
p.setAdditionalSearchPath(pybullet_data.getDataPath())
plane = p.loadURDF("plane.urdf")


while True:
    p.stepSimulation()
    time.sleep(1./240)

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

Теперь, когда у нас есть место для отдыха нашего робота, давайте перенесем его в мир моделирования. Используйте метод loadURDF(), чтобы загрузить робота scara и установить гравитацию. Объекты будут плавать или двигаться хаотично, если гравитация не установлена ​​должным образом.

import pybullet as p
import pybullet_data
import time

p.connect(p.GUI)
p.setAdditionalSearchPath(pybullet_data.getDataPath())
plane = p.loadURDF("plane.urdf")
robot = p.loadURDF("scara_bot.urdf")
p.setGravity(0,0,-10)

while True:
    p.stepSimulation()
    time.sleep(1./240)

Чтобы управлять роботом, мы должны быть уверены, что PyBullet понимает суставы, которые будут управлять ссылками. Методы getNumJoints() и getJointInfo() могут получить количество суставов в роботе и информацию о каждом суставе соответственно. Мы будем использовать этот метод, чтобы понять сустав, которым мы хотим управлять. getNumJoints() принимает обязательный параметр, который является идентификатором робота, который мы запрашиваем, чтобы получить его соединение, в то время как getJointInfo() принимает идентификатор робота и номер соединения, которое мы хотим запросить. информация. Мы будем использовать цикл for для запроса идентификатора нашего робота, который был назван robot при загрузке URDF.

import pybullet as p
import pybullet_data
import time

p.connect(p.GUI)
p.setAdditionalSearchPath(pybullet_data.getDataPath())
plane = p.loadURDF("plane.urdf")
robot = p.loadURDF("scara_bot.urdf")
p.setGravity(0,0,-10)

for i in range(p.getNumJoints(robot)):
    print(p.getJointInfo(robot, i))

while True:
    p.stepSimulation()
    time.sleep(1./240)

Запустите симуляцию

В нижней части рисунка выше показаны base_joint, arm1_joint и arm2_joint в скобках. Первое число в скобках — это индекс каждого сустава, который можно использовать для связи с ними. Прочтите Руководство по PyBullet, чтобы узнать больше о других деталях, указанных в скобках.

Теперь, когда мы знаем суставы, которыми хотим управлять (1 и 2), давайте перейдем к методам управления суставами. setJointMotorControlArray() управляет соединением и принимает несколько параметров, из которых мы будем использовать только четыре. Параметры:

  • уникальный идентификатор робота
  • jointIndex: это может быть передано как массив.
  • controlMode: в PyBullet есть разные режимы управления; POSITION_CONTROL, VELOCITY_CONTROL, TORQUE_CONTROL и PD_CONTROL. Нас интересует POSITION_CONTROL
  • targetPosition: здесь мы хотим, чтобы каждый сустав нашего робота находился.

Мы переместим ссылку arm1 в позицию -0,456, а ссылку arm2 в позицию 0,24. Позиции указаны в радианах. Нижний и верхний пределы, определенные в нашем URDF, ограничивают наше роботизированное соединение минимумом -1 радиан и максимумом 1 радиан. Суставы не могут двигаться за пределами этого диапазона.

import pybullet as p
import pybullet_data
import time

p.connect(p.GUI)
p.setAdditionalSearchPath(pybullet_data.getDataPath())
plane = p.loadURDF("plane.urdf")
robot = p.loadURDF("scara_bot.urdf")

p.setGravity(0,0,-10)
for i in range(p.getNumJoints(robot)):
    print(p.getJointInfo(robot, i))

while True:
    target_arm_1 = -0.456
    target_arm_2 = 0.24

    p.setJointMotorControlArray(robot, [1,2], p.POSITION_CONTROL, targetPositions=[target_arm_1, target_arm_2])

    p.stepSimulation()
    time.sleep(1./240)

Робот переместился из прямолинейного положения по умолчанию в наше целевое положение. Вы можете изменить целевое положение, чтобы увидеть, как ведет себя ваш манипулятор. Поздравляем, вы научились использовать PyBullet для моделирования роботов! 👏

Заключение

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

Следующим шагом будет добавление к роботу еще одного звена, призматического шарнира, который заставит его двигаться в трехмерном пространстве, и рабочего органа, также называемого захватом, для захвата объектов. Если вы хотите научиться моделировать робота с помощью операционной системы для роботов (ROS2), отметьте Reflect Robotics, чтобы изучить управление роботом с помощью Python и C++.

Если вам понравилась эта статья, пожалуйста, нажмите кнопку «Мне нравится» и не забудьте оставить комментарий, если у вас есть вопросы, замечания, исправления или вы просто хотите оценить работу.