随着科技的迅猛发展,机器人技术正日益成为现实生活中的重要组成部分。而在机器人研究领域,仿真技术的应用扮演着不可或缺的角色。它不仅可以节约大量资源和成本,更为工程师和研究者提供了一个安全、高效的实验平台。在这个博客中,将探讨PyBullet仿真引擎,并着重介绍如何利用PyBullet进行四足机器狗的仿真。

PyBullet概述

       PyBullet是一个开源的物理仿真引擎,专为机器人学、虚拟现实和游戏开发等领域设计。它由Erwin Coumans开发,最初作为Bullet物理引擎的Python绑定,因此得名PyBullet。Bullet物理引擎是一款广泛使用的高性能三维碰撞检测与物理仿真引擎,其主要用途是模拟现实世界中物体的运动和碰撞。PyBullet通过Python的简洁易用性,为机器人研究者、游戏开发者和仿真爱好者提供了一个强大且灵活的工具,使得他们能够快速构建和测试复杂的物理系统,从简单的刚体到复杂的多体动力学系统,以及各种类型的机器人模型。

image-20230804104945052

PyBullet具有的特点和优势:

  1. 开源和跨平台:PyBullet是开源软件,可以在各种操作系统上运行,包括Windows、Linux、macOS等。
  2. Python接口:PyBullet使用Python编程语言作为主要接口,简化了API的使用,加速了原型开发和实验的速度。
  3. 高性能:PyBullet底层使用C++实现,继承了Bullet物理引擎出色的性能表现。
  4. 丰富的功能:支持刚体动力学、碰撞检测、约束条件、软体物体、多体动力学、关节等丰富的物理特性。
  5. 灵活性:PyBullet可以模拟多种物体之间的复杂关系,让用户能够自由定义物体之间的交互行为。
  6. 可扩展性:用户可以轻松添加自定义的模型、控制器和碰撞几何体,以满足不同场景的需求。
  7. 学习资源丰富:由于Bullet和PyBullet在机器人和游戏领域的广泛应用,因此有许多优秀的教程和社区支持,便于学习和解决问题。

四足机器狗简介

       四足机器狗,也被称为四足机器人,是一类仿生机器人,模仿了真实狗类动物的外形和运动方式。这些机器狗通常由四条腿支撑,通过类似狗类的步态来移动和保持平衡。四足机器狗的概念可以追溯到二十世纪中期,但真正的实现始于近几十年,21 世纪初,机器人BigDog在 Boston Dynamics 正式问世,如图所示,BigDog 如其名,外形仿照哺乳动物狗建造,且能负重大体积货物,在驱动关节运动的执行器上,BigDog 设计并改进了液压驱动装置,腿部装有抗压缓冲机构,在山地,崎岖,冰面等环境下负载作业均获得了优异的效果,且机身上装载的计算机与传感器能够获取机器狗的各个状态及信息。随着机器人技术、传感器和控制算法的不断发展,四足机器狗的设计和性能得到了巨大的提升。为了实现稳定的移动和平衡,四足机器狗采用各种复杂的步态。常见的步态包括步行、奔跑、跳跃和旋转等,这些步态需要高度协调的动作和精确的控制。四足机器狗在各个领域都有潜在的应用。例如,它们可以用于灾难救援,探索危险环境,进行地质勘探,或者在军事和安保领域执行任务。此外,它们还可以成为日常生活中的助理机器人,辅助人们完成各种任务。

image-20230804190450584

安装PyBullet和相关依赖

# 使用Conda创建一个名为'bullet'的新虚拟环境,并指定Python版本为3.6
conda create -n bullet python=3.6

# 激活名为'bullet'的虚拟环境
conda activate bullet

# 在'bullet'虚拟环境中使用pip安装'numpy'包
pip install numpy

# 在'bullet'虚拟环境中使用pip安装'transforms3d'包
pip install transforms3d

# 在'bullet'虚拟环境中使用pip安装'pybullet'包
pip install pybullet

# 如果下载速度慢,可以使用清华源进行下载
pip install package_name -i https://pypi.tuna.tsinghua.edu.cn/simple
# 使用pip从清华大学的PyPI镜像安装名为'package_name'的包
# 使用'-i'标志指定了清华大学镜像的URL
# 将'package_name'替换为你想要安装的实际包名

image-20230804202730843

创建仿真场景

下载宇树科技四足机器狗的pybullet模型包:https://github.com/unitreerobotics/unitree_pybullet

image-20230804202839186加载模型文件

# 导入numpy库,这是一个用于处理数组和矩阵的库,可以用于数学计算
import numpy as np
# 导入pybullet库,这是一个用于3D物理仿真的库,可以用于创建和操作机器人模型
import pybullet as p

# 定义最大力id,这个id将被用来获取或设置四足机器人的最大推力
maxForceId = None
# 定义相机距离id,这个id将被用来获取或设置相机与机器人的距离
cameraDistId = None
# 定义关节id列表,这个列表将被用来存储机器人的所有关节id
jointIds = []
# 定义四足机器人对象,这个对象将被用来操作和控制机器人
quadruped = None
# 定义关节状态id列表,这个列表将被用来存储机器人所有关节的状态id
jointStateIds = []
# 定义预设位置列表,这个列表将被用来存储机器人的预设位置信息
pre_pos = []
# 使用列表推导式生成traceIds列表,这个列表将被用来存储跟踪机器人运动的轨迹id
traceIds = [i * 4 + 5 for i in range(4)]

# 速度id
x_velId = None
y_velId = None
dog_pos = None
target_pre_pos = None
obs_init_pos = []


def load_robot():
    # 全局变量
    global maxForceId
    global cameraDistId
    global quadruped
    global traceIds
    global x_velId
    global y_velId
    global obs_init_pos
    global target_pre_pos
    # 连接仿真环境
    p.connect(p.GUI, options="--width=1000 --height=700 --xPos=10 --yPos=10")
    plane = p.loadURDF("plane.urdf")
    p.setGravity(0, 0, -9.8)
    p.setTimeStep(1. / 100)
    # p.setDefaultContactERP(0) #设置默认的接触误差修正参数(ERP)
    # urdfFlags = p.URDF_USE_SELF_COLLISION+p.URDF_USE_SELF_COLLISION_EXCLUDE_ALL_PARENTS
    urdfFlags = p.URDF_USE_SELF_COLLISION  # 启用机器人模型的自碰撞功能,允许模型的不同部分之间发生碰撞。
    quadruped = p.loadURDF(
        "a1/urdf/a1.urdf", [0, 0, 0.48], [0, 0, 0, 1], flags=urdfFlags, useFixedBase=False)  # 加载模型

    for i in traceIds:
        pre_pos.append(p.getLinkState(quadruped, i)[0])  # 位置信息

    # enable collision between lower legs
    for j in range(p.getNumJoints(quadruped)):  # 打印关节信息
        print(p.getJointInfo(quadruped, j))

    lower_legs = [2, 5, 8, 11]
    for l0 in lower_legs:
        for l1 in lower_legs:
            if (l1 > l0):
                enableCollision = 1
                print("collision for pair", l0, l1, p.getJointInfo(quadruped, l0)[
                    12], p.getJointInfo(quadruped, l1)[12], "enabled=", enableCollision)
                # 这行代码设置了链接2和链接5之间的碰撞过滤。它使用p.setCollisionFilterPair()函数来配置链接之间的碰撞关系,
                # 以及是否允许它们之间发生碰撞。enableCollision变量的值被用于确定是否允许碰撞。
                p.setCollisionFilterPair(
                    quadruped, quadruped, 2, 5, enableCollision)

    maxForceId = p.addUserDebugParameter("maxForce", 0, 100, 20)  # 力的最大
    cameraDistId = p.addUserDebugParameter("cameraDist", 0, 5, 1)  # 摄像头距离
    x_velId = p.addUserDebugParameter("x_vel", -1, 1, 0)
    y_velId = p.addUserDebugParameter("y_vel", -1, 1, 0)

    for j in range(p.getNumJoints(quadruped)):
        p.changeDynamics(quadruped, j, linearDamping=0, angularDamping=0)  # 将线性阻尼和角度阻尼都设置为0,这将减少关节的阻尼效果,使得机器人的运动更自由。
        info = p.getJointInfo(quadruped, j)  # 关节信息
        # print(info)
        jointName = info[1]
        jointType = info[2]
        if (jointType == p.JOINT_PRISMATIC or jointType == p.JOINT_REVOLUTE):
            jointIds.append(j)
            if len(jointIds) % 3 == 0:
                jointStateIds.append(-np.pi / 2)
            elif len(jointIds) % 3 == 2:
                jointStateIds.append(np.pi / 4)
            else:
                jointStateIds.append(0)
    p.setRealTimeSimulation(0)
    p.configureDebugVisualizer(p.COV_ENABLE_GUI, 0)  # 不显示GUI上的控件
    target_pre_pos = p.getLinkState(quadruped, 0)[0]


if __name__ == '__main__':
    load_robot()
    while True:
        pass

运行以上代码,得到效果图

image-20230804204432290

四足机器狗的运动控制

四足机器狗的运动控制主要是运动过程中脚的位置计算,下面进行位置计算:

定义一些变量:

  • :摆动阶段的比例,范围在 之间。
  • :腿的命令信息,包括期望的高度。

计算可以分为三个部分:x和y方向上的位置变化量( ),z轴上的位置变化量( )和整体的下一个脚位置。

  1. X和Y方向上的位置变化量( ): 第一步是计算x和y方向上的速度矢量 。这个向量表示脚位置朝向落地点的变化率。它的计算方式如下:

    这里的 是根据Raibert算法预测的落地点, 是当前脚的位置。

    接下来,我们计算脚在x和y方向上在下一个时间步长内的位置变化量:

  2. Z轴上的位置变化量( ): 下一步是计算脚在z轴上的位置变化量(高度变化)。这个变化量由摆动高度( )和命令中期望高度( )的差值得出。 是通过摆动函数根据 计算得出的:

    其中, 表示摆动阶段的比例,范围在0到1之间; 表示摆动高度的最大值(清除高度)。

    然后, 的计算方式如下:

     

  3. 整体的下一个脚位置: 最后,我们通过将当前脚位置、z轴上的位置变化量( )和x、y方向上的位置变化量( )相加,计算出下一个脚的位置:

实现代码如下:

 def next_foot_location(
            self,
            swing_prop,
            leg_index,
            state,
            command,
    ):
        # 用于计算下一个脚的位置。
        # 它接收摆动阶段的比例(swing_prop)、腿索引(leg_index)、状态信息(state)、命令信息(command)等作为参数,
        # 并根据配置参数计算下一个脚的位置。
        assert swing_prop >= 0 and swing_prop <= 1
        foot_location = state.foot_locations[:, leg_index]
        swing_height_ = self.swing_height(swing_prop)
        touchdown_location = self.raibert_touchdown_location(leg_index, command)
        time_left = self.config.dt * self.config.swing_ticks * (1.0 - swing_prop)
        v = (touchdown_location - foot_location) / time_left * np.array([1, 1, 0])
        delta_foot_location = v * self.config.dt
        z_vector = np.array([0, 0, swing_height_ + command.height])
        return foot_location * np.array([1, 1, 0]) + z_vector + delta_foot_location

最终实现效果

4legs2

 

参考

宇树pybullet模型库: https://github.com/unitreerobotics/unitree_pybullet

quadruped_simulation:https://github.com/wupanhao/quadruped_simulation

肖华. 基于深度强化学习的四足机器人多步态生成策略研究[D].北京化工大学,2023