用Pybullet模拟机器人手臂的过程


如何用Pybullet移动机器人手臂

我觉得"我安装了Pybullet来进行机器人仿真,但是日语中的文章和说明太少了……",所以我从构建Pybullet环境开始,解释了Pybullet的功能,并实际介绍了该机器人。移动它的过程。

运行环境

  • Windows10

  • WSL2

  • Ubuntu18.04

准备

这是一章,适用于尚未准备运行Pybullet的人员,因此人们可以跳过它,说" Pybullet正在运行!"是可以的。

搭建环境的过程

我将简要解释在Windows 10上运行Pybullet的流程。
要使Pybullet正常工作,请在安装Pybullet本身之前先

  • 创建一个WSL2环境
  • 安装Ubuntu 18.04
  • Xserver安装
  • 是必需的。

    1.创建WSL2环境
    为了在Windows 10上使用Pybullet,最好先创建一个WSL(Linux的Windows子系统)环境。

    • WSL2安装过程(官方页面)

    2.安装Ubuntu 18.04
    准备好WSL2后,下一步就是安装Ubuntu。
    可以从Microsoft Store轻松完成安装。 Store中有各种版本的Ubuntu,但是我认为安装Ubuntu 18.04基本上是安全的。
    Pybullet可以直接在Ubuntu上直接运行,但是我在Windows Terminal上使用Ubuntu。
    安装Ubuntu之后,启动Ubuntu并设置WSL2。

    • 如何在Windows终端机上使用Ubuntu

    • 如何在Ubuntu

      上启用WSL2

    此处省略

    3.安装Xserver
    为了用Pybullet执行机器人仿真,这很自然,但是有必要查看仿真状态。 Xserver充当监视器,以查看模拟情况。

    • Xserver的安装(VcXsrv)(官方页面)

    ↑请注意,单击

    后,安装将自动开始

    安装Xserver后

    • Xserver设置

    • 允许Windows防火墙中的Xserver

    是必需的。这里也省略了。

    安装Pybullet
    当Xserver安全运行时,终于可以安装Pybullet了。在安装Pybullet时,请确保记住在WSL上的安装位置以及安装位置。

    除了Pybullet,

    • matplotlib

    • numpy

    • 数学

    如果您安装诸如

    之类的库,以后会很方便。

    药丸运行检查

    移至Pybullet的安装目录,然后

    1
    2
    $ cd bullet3/examples/pybullet/examples
    $ ls

    如果运行

    ,您将看到Pybullet安装随附的示例代码。
    如果您正确启动了任何示例代码并且可以正常运行,则可以认为Pybullet的操作环境已准备就绪。

    (示例)执行人形机器人的手动操作程序

    1
    $ python3 humanoid_manual_control.py

    结果22.png

    如果使用鼠标移动右侧的栏,则机器人将一前一后地移动

    如何使用Pybullet

    humanoid_manual_control.py代码描述

    顾名思义,Pybullet可与Python一起使用。
    例如,我前面提到的humanoid_manual_control.py程序的内容如下所示。

    humanoid_manual_control.py

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    import pybullet as p
    import time

    import pybullet_data

    p.connect(p.GUI)
    p.setAdditionalSearchPath(pybullet_data.getDataPath())
    obUids = p.loadMJCF("mjcf/humanoid.xml")
    humanoid = obUids[1]

    gravId = p.addUserDebugParameter("gravity", -10, 10, -10)
    jointIds = []
    paramIds = []

    p.setPhysicsEngineParameter(numSolverIterations=10)
    p.changeDynamics(humanoid, -1, linearDamping=0, angularDamping=0)

    for j in range(p.getNumJoints(humanoid)):
      p.changeDynamics(humanoid, j, linearDamping=0, angularDamping=0)
      info = p.getJointInfo(humanoid, j)
      #print(info)
      jointName = info[1]
      jointType = info[2]
      if (jointType == p.JOINT_PRISMATIC or jointType == p.JOINT_REVOLUTE):
        jointIds.append(j)
        paramIds.append(p.addUserDebugParameter(jointName.decode("utf-8"), -4, 4, 0))

    p.setRealTimeSimulation(1)
    while (1):
      p.setGravity(0, 0, p.readUserDebugParameter(gravId))
      for i in range(len(paramIds)):
        c = paramIds[i]
        targetPos = p.readUserDebugParameter(c)
        p.setJointMotorControl2(humanoid, jointIds[i], p.POSITION_CONTROL, targetPos, force=5 * 240.)
      time.sleep(0.01)

    Pybullet功能用

    p.标记。
    因此,如果您具有基本的Python知识(例如if语句,for语句,列表等),则只需使用Pybullet函数就可以轻松进行模拟。

    现在,从这里开始,我将简要解释上面的代码。
    如果您可以理解此代码的内容,则应该能够理解Pybullet。顺便说一句,Pybullet有官方的入门指南,因此,如果您想了解更多有关Pybullet功能的信息,请参阅此。 (用英语...)

    • Pybullet入门指南

    1.代码的前半部分

    1
    2
    3
    4
    5
    6
    7
    8
    9
    import pybullet as p
    import time

    import pybullet_data

    p.connect(p.GUI)
    p.setAdditionalSearchPath(pybullet_data.getDataPath())
    obUids = p.loadMJCF("mjcf/humanoid.xml")
    humanoid = obUids[1]

    使用

    p.connect(p.GUI)连接到GUI(图形用户界面)。在使用Pybullet模拟时,暂时将其放入。

    p.setAdditionalSearchPath(pybullet_data.getDataPath())可能位于导入文件pybullet_data中所包含的人形机器人的路径中。如果要使用Pybullet安装随附的示例机器人,让我们通过以下路径进行操作。

    p.loadMJCF("mjcf/humanoid.xml")正在读取包含人形机器人数据的文件。
    换句话说,上一个图像中显示的人形机器人不是使用Python文件创建的,而是使用单独的MCDF文件创建的。
    在此示例代码中,读取了MJCF中描述的机械手,但是除了MJCF以外,还以SDF和URDF等格式进行了描述。

    2.中码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    gravId = p.addUserDebugParameter("gravity", -10, 10, -10)
    jointIds = []
    paramIds = []

    p.setPhysicsEngineParameter(numSolverIterations=10)
    p.changeDynamics(humanoid, -1, linearDamping=0, angularDamping=0)

    for j in range(p.getNumJoints(humanoid)):
      p.changeDynamics(humanoid, j, linearDamping=0, angularDamping=0)
      info = p.getJointInfo(humanoid, j)
      #print(info)
      jointName = info[1]
      jointType = info[2]
      if (jointType == p.JOINT_PRISMATIC or jointType == p.JOINT_REVOLUTE):
        jointIds.append(j)
        paramIds.append(p.addUserDebugParameter(jointName.decode("utf-8"), -4, 4, 0))

    p.addUserDebugParameter()是调试功能。这是一个非常有用的功能,因为它允许用户在运行模拟时手动操作参数(出现在上图右侧的栏上)。例如,
    gravId = p.addUserDebugParameter("gravity", -10, 10, -10)
    因此,对于参数gravId,用户可以在-10到10的范围内进行操作。同样,gravId的初始值设置为-10。
    我已将模拟过程中在栏上方显示的参数的名称设置为gravity

    p.getNumJoints(humanoid)返回humanoid中的关节数(由loadMJCF加载的机器人)。

    句子中的

    info = p.getJointInfo(humanoid, j)以列表格式返回人形机器人第j个关节的信息。
    jointName是关节的名称,jointType是包含关节类型的参数。
    换句话说,if语句部分是"仅调试PRISMATIC或REVOLUTE关节"的代码。
    稍后写URDF时,PRISMATIC(平移关节)和REVOLUTE(轴旋转关节)将正确显示。
    顺便说一句,if语句限制关节类型的原因是,还有其他不动的关节,例如FIXED(固定关节)。

    3.代码后半部分

    1
    2
    3
    4
    5
    6
    7
    8
    p.setRealTimeSimulation(1)
    while (1):
      p.setGravity(0, 0, p.readUserDebugParameter(gravId))
      for i in range(len(paramIds)):
        c = paramIds[i]
        targetPos = p.readUserDebugParameter(c)
        p.setJointMotorControl2(humanoid, jointIds[i], p.POSITION_CONTROL, targetPos, force=5 * 240.)
      time.sleep(0.01)

    顺便说一句,我前一段时间所做的是模拟的准备工作。而我们在这里所做的就是主要的模拟。

    p.setRealTimeSimulation(1)是为实时仿真设置的功能。通过重复下一个while语句部分,模拟得以进行。
    使用Pybullet模拟时,请将其放在while语句之前。每当while语句出现时,仿真就会运行。

    首先,p.setGravity(0, 0, p.readUserDebugParameter(gravId))设置重力加速度。由于输入值gravId作为较早调试的参数而准备,因此用户可以在仿真过程中更改重力。

    for语句的内容是用户移动每个关节参数的代码。
    p.setJointMotorControl2(humanoid, jointIds[i], p.POSITION_CONTROL, targetPos, force=5 * 240.)
    这是移动机器人的功能。
    指定要使用humanoid移动的机器人,指定要使用jointIds[i]移动的关节,p.POSITION_CONTROL是要控制的功能(可能),targetPos是关节的参数,force是最大力关节可以产生(也许)。

    大致来说,targetPos是用于调试的参数,因此,当用户移动杆时,关节也会移动。

    由于程序(模拟)在

    time.sleep(0.01)处停止了0.01秒,因此模拟运行平稳。我们暂时将其放在最后。
    例如,如果设置time.sleep(1),则参数将每秒更新一次,因此机器人的动作将变得生涩。

    humanoid_manual_control.py是用于手动移动机器人的程序,因此经常使用调试功能。由于Pybullet函数很多,因此代码看起来很困难,但是相反,如果您学习如何使用Pybullet函数,您会发现它是一个非常简单的程序。

    实际上,如果您只想移动机械臂,则只需将代码前半部分的loadMJCF替换为要使用的机械臂的读数即可。

    调整humanoid_manual_control.py代码以移动机器人手臂

    中,我们将立即使用humanoid_manual_control.py的代码。
    首先,让我们复制代码以防万一。

    1
    $ cp humanoid_manual_control.py robotArm_manual_control.py

    从这里开始,我们将处理复制的robotArm_manual_control.py
    首先,让我们用机器人手臂代替类人机器人。

    robotArm_manual_control.py

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import pybullet as p
    import time

    #import pybullet_data #必要ないので削除

    p.connect(p.GUI)
    #p.setAdditionalSearchPath(pybullet_data.getDataPath()) #必要ないので削除
    kukaId = p.loadURDF("kuka_iiwa/model.urdf", [0, 0, 0], useFixedBase=True) #追加
    #obUids = p.loadMJCF("mjcf/humanoid.xml") #削除
    #humanoid = obUids[1] #削除

    在安装Pybullet时,将附加一个称为kuka_iiwa的机械手作为示例机械手,因此这次我们将使用它。
    如果像上述代码一样替换它,请将其余代码的所有humanoid部分替换为kukaId

    如果可以正确更改所有内容,请执行并检查。

    1
    $ python3 robotArm_manual_control.py

    结果23.png

    人形机器人已成功转换为机械臂。如果移动右侧的栏,则可以正确移动手臂。

    但是,与以前不同,地板已经消失了。
    也许删除的obUids = p.loadMJCF("mjcf/humanoid.xml")也包含楼层数据。

    因此,接下来我们将添加一个地板。

    robotArm_manual_control.py

    1
    2
    3
    4
    5
    6
    import pybullet as p
    import time

    p.connect(p.GUI)
    p.loadURDF("plane.urdf", [0, 0, 0], useFixedBase=True) #追加
    kukaId = p.loadURDF("kuka_iiwa/model.urdf", [0, 0, 0], useFixedBase=True)

    结果24.png

    这样,如果您有要使用的机械手或要放置的物体,则可以使用loadURDF()轻松安装。
    但是,如果您想从一开始就使用Pybullet随附的示例以外的其他示例,则需要单独编写URDF或使用CAD创建它。

    编写URDF以创建对象

    最后,我将解释一些有关如何编写URDF的信息。
    一旦您可以编写URDF,就可以创建简单的对象和机械手。

    首先,让我们编写创建一个简单盒子的urdf。

    box.urdf

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    <?xml version="1.0" ?>
    <robot name="box">
      <link name="base">
        <inertial>
          <origin rpy = "0 0 0" xyz = "0 0 0" />
          <mass value = "0.0001" />
          <inertia ixx = "0.0001" ixy = "0" ixz = "0" iyy = "0.0001" iyz = "0" izz = "0.0001" />
        </inertial>
      </link>
      <link name="box">
        <contact>
          <lateral_friction value="1"/>
        </contact>
        <inertial>
          <origin rpy="0 0 0" xyz="0 0 0"/>
          <mass value="0.01"/>
          <inertia ixx="0.005" ixy="0" ixz="0" iyy="0.005" iyz="0" izz="0.005"/>
        </inertial>
        <visual>
          <origin rpy="0 0 0" xyz="0 0 0"/>
          <geometry>
            <box size="0.02 0.02 0.02"/>
          </geometry>
          <material name="Purple">
            <color rgba="1 0 1 1" />
          </material>
        </visual>
        <collision>
          <origin rpy="0 0 0" xyz="0 0 0"/>
          <geometry>
            <box size="0.02 0.02 0.02"/>
          </geometry>
        </collision>
      </link>
      <joint name="base_box" type="fixed" >
        <parent link = "base" />
        <child link = "box" />
        <origin rpy = "0 0 0" xyz = "0.000 0.000 0.00" />
      </joint>
    </robot>

    让我们用Pybullet 25.png

    显示它

    小而混乱,但是会产生紫色方块。
    此多维数据集由box.urdf生成。

    您可能会想,"我是否必须编写这么长而烦人的代码才能生成这么多的盒子……",但是由于我只是根据固定的框架编写它,因此您可以相对轻松地编写复制和微调。

    基本上,通过重复将零件link与接头joint连接的过程来描述URDF。
    例如,在上面的box.urdf中,名为base的链接和名为box的链接通过名为base_box的关节相连。

    从这里,我将解释每个链接和关节。

    基本链接

    base_link

    1
    2
    3
    4
    5
    6
    7
    <link name="base">
      <inertial>
        <origin rpy = "0 0 0" xyz = "0 0 0" />
        <mass value = "0.0001" />
        <inertia ixx = "0.0001" ixy = "0" ixz = "0" iyy = "0.0001" iyz = "0" izz = "0.0001" />
      </inertial>
    </link>

    基础链接是大小(体积)为0且质量为一分钟的链接。
    创建此链接的??目的是确定所创建对象的基本位置。
    安装由仿真创建的对象时,位置由此基础链接的坐标指定,因此
    当执行p.loadURDF(box.urdf, [0, 0, 0])时,设置box.urdf以便基本链接到达[0,0,0]的位置。

    框链接

    box_link

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    <link name="box">
      <contact>
        <lateral_friction value="1"/>
      </contact>
      <inertial>
        <origin rpy="0 0 0" xyz="0 0 0"/>
        <mass value="0.01"/>
        <inertia ixx="0.005" ixy="0" ixz="0" iyy="0.005" iyz="0" izz="0.005"/>
      </inertial>
      <visual>
        <origin rpy="0 0 0" xyz="0 0 0"/>
        <geometry>
          <box size="0.02 0.02 0.02"/>
        </geometry>
        <material name="Purple">
          <color rgba="1 0 1 1" />
        </material>
      </visual>
      <collision>
        <origin rpy="0 0 0" xyz="0 0 0"/>
        <geometry>
          <box size="0.02 0.02 0.02"/>
        </geometry>
      </collision>
    </link>

    箱形链接是边长为0.02(m)的立方链接。
    这是box.urdf的主体部分。
    让我们一一看一下代码。

    <contact>关于联系人。
    <lateral_friction value="1"/>将摩擦系数设置为1。
    <inertial>关于惯性矩。
    <origin rpy="0 0 0" xyz="0 0 0"/>将初始姿势(rpy)和初始位置(xyz)设置为0。
    <mass value="0.01"/>将质量设置为0.01(kg)。
    <inertia ixx="0.005" ixy="0" ixz="0" iyy="0.005" iyz="0" izz="0.005"/>转动惯量设置。
    <visual>关于外观。
    <geometry>关于形状。
    <box size="0.02 0.02 0.02"/>设置为侧面为0.02(m)的盒子形状。
    <material name="Purple">关于对象的颜色。
    <color rgba="1 0 1 1" />用rgba设置颜色。在这种情况下为紫色。
    <collision>关于碰撞检测。

    请注意,编写URDF时,必须使用</>将其关闭。
    (示例1)在<contact>中描述了联系信息之后,请使用</contact>将其关闭。
    (示例2)与<mass value="0.01"/>一样,以/结尾。

    base_box关节

    base_box_joint

    1
    2
    3
    4
    5
    <joint name="base_box" type="fixed" >
      <parent link = "base" />
      <child link = "box" />
      <origin rpy = "0 0 0" xyz = "0.000 0.000 0.00" />
    </joint>

    base_box关节连接基本链接和box链接。

    <joint name="base_box" type="fixed" > \\"固定\\"(固定)。换句话说,设定为关节不会移动。
    <parent link = "base" />父链接设置。将关节??从父链接连接到子链接。
    <child link = "box" />子链接设置。从父链接连接的链接。
    <origin rpy = "0 0 0" xyz = "0.000 0.000 0.00" />设置关节的位置。确定距父链接的相对距离。

    在不熟悉URDF之前很难对其进行描述,但是不需要编程能力是件好事,因为您只需要连接链接和关节即可。
    但是,一个警告是,当您在URDF描述中犯了一个错误时,您将不会得到关于错误发生位置的错误代码。因此请注意,如果您不经常运行并检查它,则必须逐行检查代码。

    我认为以下文章将对如何编写URDF有所帮助。

    • 具有URDF的PyBullet(统一的机器人描述格式)URDF说明(1)

    • 第4章创建.urdf

    • 为移动机器人创建URDF

    我认为以下文章将对惯性(惯性矩)有所帮助。

    • 使用ROS(机器人操作系统)

    最后

    本文是早稻田大学绪方实验室活动的一部分。
    我正在研究机器人和深度学习,因此,如果您有兴趣,请访问以下实验室网站。

    • 绪方哲也实验室