赞
踩
机器人算法前置工作:
PyBullet 基于开源物理引擎 bullet 开发,封装成了 Python 的一个模块,用于机器人仿真和学习
pybullet中文手册(一) - 灰信网(软件开发博客聚合) (freesion.com)
Pinocchio是常用的机器人动力学库
1.pybullet
安装:pip install pybullet
流程:
引入模块
初始化pybullet的环境
创建环境和机器人
设置物理引擎参数
运行主循环
结束pybullet环境
最简单的例子引入:
- import pybullet as p
- import time
- import pybullet_data
-
- # 连接物理引擎
- physicsCilent = p.connect(p.GUI)
-
- # 添加资源路径
- p.setAdditionalSearchPath(pybullet_data.getDataPath())
-
- # 设置环境重力加速度
- p.setGravity(0, 0, -10)
-
- # 加载URDF模型,此处是加载蓝白相间的陆地
- planeId = p.loadURDF("plane.urdf")
-
- # 加载机器人,并设置加载的机器人的位姿
- startPos = [0, 0, 1]
- startOrientation = p.getQuaternionFromEuler([0, 0, 0])
- boxId = p.loadURDF("r2d2.urdf", startPos, startOrientation)
-
- # 按照位置和朝向重置机器人的位姿,由于我们之前已经初始化了机器人,所以此处加不加这句话没什么影响
- p.resetBasePositionAndOrientation(boxId, startPos, startOrientation)
-
- # 开始一千次迭代,也就是一千次交互,每次交互后停顿1/240
- for i in range(1000):
- p.stepSimulation()
- time.sleep(1 / 240)
-
- # 获取位置与方向四元数
- cubePos, cubeOrn = p.getBasePositionAndOrientation(boxId)
- print("-" * 20)
- print(f"机器人的位置坐标为:{cubePos}\n机器人的朝向四元数为:{cubeOrn}")
- print("-" * 20)
-
- # 断开连接
- p.disconnect()
解释:
导入 pybullet 模块后,要做的第一件事是“连接”到物理引擎:pybullet 内置了物理引擎的两种连接方式:DIRECT
和GUI
。无论哪种连接,在 pybullet 中都执行相同物理模拟和渲染。差别在于有无可视化的窗口。(connect方法)
physicsClient = p.connect(p.GUI)
physicsClient = p.connect(p.DIRECT)
使用disconnect()函数可以指定需要关闭的物理服务器,默认参数是0,也就是第一个创建的服务器的ID。
默认情况下,连接到物理引擎后,并没有没有启用重力。因此我们需要单独设置:
setGravity能帮我们在沿x,y,z轴方向上设置重力。(单位是国际标准单位制下的单位。该函数没有返回值)
- # 加载URDF模型,此处是加载蓝白相间的陆地
- planeId = p.loadURDF("plane.urdf")
-
- ......
- boxId = p.loadURDF("r2d2.urdf", startPos, startOrientation)
参数fileName代表需要加载的urdf模型的绝对路径,如果你将urdf文件放在了一个文件夹下,可以使用setAdditionalSearchPath()方法来添加loadURDF的搜索路径。
使用stepSimulation函数可以看成是进行一次迭代步
- for i in range(1000):
- p.stepSimulation()
- time.sleep(0.1)
除了使用stepSimualtion进行正向动力学进行步进模拟,我们还可以使用setRealTimeSimulation函数,直接将物理引擎渲染的时间和RTC(real time clock)同步,这样做,就不需要使用stepSimualtion显式地执行模拟步了
- import pybullet as p
- import time
- import pybullet_data
-
- # 连接物理引擎
- physicsCilent = p.connect(p.GUI)
-
- # 渲染逻辑
- p.configureDebugVisualizer(p.COV_ENABLE_RENDERING, 0)
- p.configureDebugVisualizer(p.COV_ENABLE_GUI, 0)
-
- # 添加资源路径
- p.setAdditionalSearchPath(pybullet_data.getDataPath())
-
- # 设置环境重力加速度
- p.setGravity(0, 0, -10)
-
- # 加载URDF模型,此处是加载蓝白相间的陆地
- planeId = p.loadURDF("plane.urdf")
-
- # 加载机器人,并设置加载的机器人的位姿
- startPos = [0, 0, 1]
- startOrientation = p.getQuaternionFromEuler([0, 0, 0])
- boxId = p.loadURDF("r2d2.urdf", startPos, startOrientation)
-
- # 按照位置和朝向重置机器人的位姿,由于我们之前已经初始化了机器人,所以此处加不加这句话没什么影响
- p.resetBasePositionAndOrientation(boxId, startPos, startOrientation)
- p.configureDebugVisualizer(p.COV_ENABLE_RENDERING, 1)
-
- p.setRealTimeSimulation(1)
-
- while True:
- pass
-
- # 断开连接
- p.disconnect()
查看机器人的信息
pybullet提供了一堆函数来查询指定机器人的信息,乃至指定机器人指定关节的信息
比如getBasePositionAndOrientation
接受机器人ID,返回两个列表,第一个代表基座的位置,第二个代表方向四元数,还有类似的函数,都以get打头,基本都是一看就知道啥意思
还有一些函数是用来获取机器人关节信息:
getNumJoints()
接受两个参数,其中第一个参数为机器人ID,第二个参数可选,一般不需要选,因为只有多平台开发时才用得到。该函数返回该ID机器人的关节数量。
getJointInfo()
接受三个参数,其中第三个参数和上面那个函数的第二个参数一样,有关平台,可以不填,前两个参数为需要查看的机器人的ID和需要查看的该机器人的关节序号。该函数返回一个tuple,该tuple的长度为17
以如下代码为例:
- import pybullet as p
- import pybullet_data
- from time import sleep
-
- # 链接物理引擎
- p.connect(p.GUI)
-
- # 设置模型加载路径
- datapath = pybullet_data.getDataPath()
- p.setAdditionalSearchPath(datapath)
-
- # 加载模型
- robot_id = p.loadURDF("r2d2.urdf", [0, 0, 0.5])
-
- # 输出基本信息
- joint_num = p.getNumJoints(robot_id)
- print("r2d2的节点数量为:", joint_num)
-
- print("r2d2的信息:")
- for joint_index in range(joint_num):
- info_tuple = p.getJointInfo(robot_id, joint_index)
- print(f"关节序号:{info_tuple[0]}\n\
- 关节名称:{info_tuple[1]}\n\
- 关节类型:{info_tuple[2]}\n\
- 机器人第一个位置的变量索引:{info_tuple[3]}\n\
- 机器人第一个速度的变量索引:{info_tuple[4]}\n\
- 保留参数:{info_tuple[5]}\n\
- 关节的阻尼大小:{info_tuple[6]}\n\
- 关节的摩擦系数:{info_tuple[7]}\n\
- slider和revolute(hinge)类型的位移最小值:{info_tuple[8]}\n\
- slider和revolute(hinge)类型的位移最大值:{info_tuple[9]}\n\
- 关节驱动的最大值:{info_tuple[10]}\n\
- 关节的最大速度:{info_tuple[11]}\n\
- 节点名称:{info_tuple[12]}\n\
- 局部框架中的关节轴系:{info_tuple[13]}\n\
- 父节点frame的关节位置:{info_tuple[14]}\n\
- 父节点frame的关节方向:{info_tuple[15]}\n\
- 父节点的索引,若是基座返回-1:{info_tuple[16]}\n\n")
输出结果:
- r2d2的节点数量为: 15
- r2d2的信息:
- 关节序号:0
- 关节名称:b'base_to_right_leg'
- 关节类型:4
- 机器人第一个位置的变量索引:-1
- 机器人第一个速度的变量索引:-1
- 保留参数:0
- 关节的阻尼大小:0.0
- 关节的摩擦系数:0.0
- slider和revolute(hinge)类型的位移最小值:0.0
- slider和revolute(hinge)类型的位移最大值:-1.0
- 关节驱动的最大值:0.0
- 关节的最大速度:0.0
- 节点名称:b'right_leg'
- 局部框架中的关节轴系:(0.0, 0.0, 0.0)
- 父节点frame的关节位置:(0.22, 0.0, 0.25)
- 父节点frame的关节方向:(0.0, -0.7070904020014416, 0.0, 0.7071231599922604)
- 父节点的索引,若是基座返回-1:-1
-
-
- 关节序号:1
- 关节名称:b'right_base_joint'
- 关节类型:4
- 机器人第一个位置的变量索引:-1
- 机器人第一个速度的变量索引:-1
- 保留参数:0
- 关节的阻尼大小:0.0
- 关节的摩擦系数:0.0
- slider和revolute(hinge)类型的位移最小值:0.0
- slider和revolute(hinge)类型的位移最大值:-1.0
- 关节驱动的最大值:0.0
- 关节的最大速度:0.0
- 节点名称:b'right_base'
- 局部框架中的关节轴系:(0.0, 0.0, 0.0)
- 父节点frame的关节位置:(0.2999999996780742, 0.0, -1.3898038463944216e-05)
- 父节点frame的关节方向:(0.0, 0.7070904020014416, 0.0, 0.7071231599922604)
- 父节点的索引,若是基座返回-1:0
-
-
- 关节序号:2
- 关节名称:b'right_front_wheel_joint'
- 关节类型:0
- 机器人第一个位置的变量索引:7
- 机器人第一个速度的变量索引:6
- 保留参数:1
- 关节的阻尼大小:0.0
- 关节的摩擦系数:0.0
- slider和revolute(hinge)类型的位移最小值:0.0
- slider和revolute(hinge)类型的位移最大值:-1.0
- 关节驱动的最大值:100.0
- 关节的最大速度:100.0
- 节点名称:b'right_front_wheel'
- 局部框架中的关节轴系:(0.0, 0.0, 1.0)
- 父节点frame的关节位置:(0.0, 0.133333333333, -0.085)
- 父节点frame的关节方向:(0.0, -0.7070904020014416, 0.0, 0.7071231599922604)
- 父节点的索引,若是基座返回-1:1
-
-
- 关节序号:3
- 关节名称:b'right_back_wheel_joint'
- 关节类型:0
- 机器人第一个位置的变量索引:8
- 机器人第一个速度的变量索引:7
- 保留参数:1
- 关节的阻尼大小:0.0
- 关节的摩擦系数:0.0
- slider和revolute(hinge)类型的位移最小值:0.0
- slider和revolute(hinge)类型的位移最大值:-1.0
- 关节驱动的最大值:100.0
- 关节的最大速度:100.0
- 节点名称:b'right_back_wheel'
- 局部框架中的关节轴系:(0.0, 0.0, 1.0)
- 父节点frame的关节位置:(0.0, -0.133333333333, -0.085)
- 父节点frame的关节方向:(0.0, -0.7070904020014416, 0.0, 0.7071231599922604)
- 父节点的索引,若是基座返回-1:1
-
-
- 关节序号:4
- 关节名称:b'base_to_left_leg'
- 关节类型:4
- 机器人第一个位置的变量索引:-1
- 机器人第一个速度的变量索引:-1
- 保留参数:0
- 关节的阻尼大小:0.0
- 关节的摩擦系数:0.0
- slider和revolute(hinge)类型的位移最小值:0.0
- slider和revolute(hinge)类型的位移最大值:-1.0
- 关节驱动的最大值:0.0
- 关节的最大速度:0.0
- 节点名称:b'left_leg'
- 局部框架中的关节轴系:(0.0, 0.0, 0.0)
- 父节点frame的关节位置:(-0.22, 0.0, 0.25)
- 父节点frame的关节方向:(0.0, -0.7070904020014416, 0.0, 0.7071231599922604)
- 父节点的索引,若是基座返回-1:-1
-
-
- 关节序号:5
- 关节名称:b'left_base_joint'
- 关节类型:4
- 机器人第一个位置的变量索引:-1
- 机器人第一个速度的变量索引:-1
- 保留参数:0
- 关节的阻尼大小:0.0
- 关节的摩擦系数:0.0
- slider和revolute(hinge)类型的位移最小值:0.0
- slider和revolute(hinge)类型的位移最大值:-1.0
- 关节驱动的最大值:0.0
- 关节的最大速度:0.0
- 节点名称:b'left_base'
- 局部框架中的关节轴系:(0.0, 0.0, 0.0)
- 父节点frame的关节位置:(0.2999999996780742, 0.0, -1.3898038463944216e-05)
- 父节点frame的关节方向:(0.0, 0.7070904020014416, 0.0, 0.7071231599922604)
- 父节点的索引,若是基座返回-1:4
-
-
- 关节序号:6
- 关节名称:b'left_front_wheel_joint'
- 关节类型:0
- 机器人第一个位置的变量索引:9
- 机器人第一个速度的变量索引:8
- 保留参数:1
- 关节的阻尼大小:0.0
- 关节的摩擦系数:0.0
- slider和revolute(hinge)类型的位移最小值:0.0
- slider和revolute(hinge)类型的位移最大值:-1.0
- 关节驱动的最大值:100.0
- 关节的最大速度:100.0
- 节点名称:b'left_front_wheel'
- 局部框架中的关节轴系:(0.0, 0.0, 1.0)
- 父节点frame的关节位置:(0.0, 0.133333333333, -0.085)
- 父节点frame的关节方向:(0.0, -0.7070904020014416, 0.0, 0.7071231599922604)
- 父节点的索引,若是基座返回-1:5
-
-
- 关节序号:7
- 关节名称:b'left_back_wheel_joint'
- 关节类型:0
- 机器人第一个位置的变量索引:10
- 机器人第一个速度的变量索引:9
- 保留参数:1
- 关节的阻尼大小:0.0
- 关节的摩擦系数:0.0
- slider和revolute(hinge)类型的位移最小值:0.0
- slider和revolute(hinge)类型的位移最大值:-1.0
- 关节驱动的最大值:100.0
- 关节的最大速度:100.0
- 节点名称:b'left_back_wheel'
- 局部框架中的关节轴系:(0.0, 0.0, 1.0)
- 父节点frame的关节位置:(0.0, -0.133333333333, -0.085)
- 父节点frame的关节方向:(0.0, -0.7070904020014416, 0.0, 0.7071231599922604)
- 父节点的索引,若是基座返回-1:5
-
-
- 关节序号:8
- 关节名称:b'gripper_extension'
- 关节类型:1
- 机器人第一个位置的变量索引:11
- 机器人第一个速度的变量索引:10
- 保留参数:1
- 关节的阻尼大小:0.0
- 关节的摩擦系数:0.0
- slider和revolute(hinge)类型的位移最小值:-0.38
- slider和revolute(hinge)类型的位移最大值:0.0
- 关节驱动的最大值:1000.0
- 关节的最大速度:0.5
- 节点名称:b'gripper_pole'
- 局部框架中的关节轴系:(1.0, 0.0, 0.0)
- 父节点frame的关节位置:(0.0, 0.19, 0.2)
- 父节点frame的关节方向:(0.0, 0.0, -0.7070904020014416, 0.7071231599922604)
- 父节点的索引,若是基座返回-1:-1
-
-
- 关节序号:9
- 关节名称:b'left_gripper_joint'
- 关节类型:0
- 机器人第一个位置的变量索引:12
- 机器人第一个速度的变量索引:11
- 保留参数:1
- 关节的阻尼大小:0.0
- 关节的摩擦系数:0.0
- slider和revolute(hinge)类型的位移最小值:0.0
- slider和revolute(hinge)类型的位移最大值:0.548
- 关节驱动的最大值:1000.0
- 关节的最大速度:0.5
- 节点名称:b'left_gripper'
- 局部框架中的关节轴系:(0.0, 0.0, 1.0)
- 父节点frame的关节位置:(0.2, 0.02, 0.0)
- 父节点frame的关节方向:(0.0, 0.0, 0.0, 1.0)
- 父节点的索引,若是基座返回-1:8
-
-
- 关节序号:10
- 关节名称:b'left_tip_joint'
- 关节类型:4
- 机器人第一个位置的变量索引:-1
- 机器人第一个速度的变量索引:-1
- 保留参数:0
- 关节的阻尼大小:0.0
- 关节的摩擦系数:0.0
- slider和revolute(hinge)类型的位移最小值:0.0
- slider和revolute(hinge)类型的位移最大值:-1.0
- 关节驱动的最大值:0.0
- 关节的最大速度:0.0
- 节点名称:b'left_tip'
- 局部框架中的关节轴系:(0.0, 0.0, 0.0)
- 父节点frame的关节位置:(0.0, 0.0, 0.0)
- 父节点frame的关节方向:(0.0, 0.0, 0.0, 1.0)
- 父节点的索引,若是基座返回-1:9
-
-
- 关节序号:11
- 关节名称:b'right_gripper_joint'
- 关节类型:0
- 机器人第一个位置的变量索引:13
- 机器人第一个速度的变量索引:12
- 保留参数:1
- 关节的阻尼大小:0.0
- 关节的摩擦系数:0.0
- slider和revolute(hinge)类型的位移最小值:0.0
- slider和revolute(hinge)类型的位移最大值:0.548
- 关节驱动的最大值:1000.0
- 关节的最大速度:0.5
- 节点名称:b'right_gripper'
- 局部框架中的关节轴系:(0.0, 0.0, -1.0)
- 父节点frame的关节位置:(0.2, -0.02, 0.0)
- 父节点frame的关节方向:(0.0, 0.0, 0.0, 1.0)
- 父节点的索引,若是基座返回-1:8
-
-
- 关节序号:12
- 关节名称:b'right_tip_joint'
- 关节类型:4
- 机器人第一个位置的变量索引:-1
- 机器人第一个速度的变量索引:-1
- 保留参数:0
- 关节的阻尼大小:0.0
- 关节的摩擦系数:0.0
- slider和revolute(hinge)类型的位移最小值:0.0
- slider和revolute(hinge)类型的位移最大值:-1.0
- 关节驱动的最大值:0.0
- 关节的最大速度:0.0
- 节点名称:b'right_tip'
- 局部框架中的关节轴系:(0.0, 0.0, 0.0)
- 父节点frame的关节位置:(0.0, 0.0, 0.0)
- 父节点frame的关节方向:(0.0, 0.0, 0.0, 1.0)
- 父节点的索引,若是基座返回-1:11
-
-
- 关节序号:13
- 关节名称:b'head_swivel'
- 关节类型:0
- 机器人第一个位置的变量索引:14
- 机器人第一个速度的变量索引:13
- 保留参数:1
- 关节的阻尼大小:0.0
- 关节的摩擦系数:0.0
- slider和revolute(hinge)类型的位移最小值:-0.38
- slider和revolute(hinge)类型的位移最大值:0.0
- 关节驱动的最大值:1000.0
- 关节的最大速度:0.5
- 节点名称:b'head'
- 局部框架中的关节轴系:(0.0, 0.0, 1.0)
- 父节点frame的关节位置:(0.0, 0.0, 0.3)
- 父节点frame的关节方向:(0.0, 0.0, 0.0, 1.0)
- 父节点的索引,若是基座返回-1:-1
-
-
- 关节序号:14
- 关节名称:b'tobox'
- 关节类型:4
- 机器人第一个位置的变量索引:-1
- 机器人第一个速度的变量索引:-1
- 保留参数:0
- 关节的阻尼大小:0.0
- 关节的摩擦系数:0.0
- slider和revolute(hinge)类型的位移最小值:0.0
- slider和revolute(hinge)类型的位移最大值:-1.0
- 关节驱动的最大值:0.0
- 关节的最大速度:0.0
- 节点名称:b'box'
- 局部框架中的关节轴系:(0.0, 0.0, 0.0)
- 父节点frame的关节位置:(0.0, 0.1214, 0.1214)
- 父节点frame的关节方向:(0.0, 0.0, 0.0, 1.0)
- 父节点的索引,若是基座返回-1:13
让模型动起来
urdf文件(常见的机器人模型储存在计算机的数据结构):
URDF文件生成的机器人模型,主体称为base(基座),其余的分支称为link,base与link,link与link之间通过joint(关节)连接。因此,整个URDF模型可以描述为一颗多叉树,树的根节点为base。根据树的一些知识,我们也知道树是不能成环的,因此,URDF描述的机器人多叉树中,组件与组件之间的连接方式不能形成环,否则pybullet解析URDF文件时会报错
以这个urdf为例,中间蓝色的圆柱体是它的base,其余所有你能看到的组件都是link,然后link与link之间,base与link之间都是通过joint连接的,只不过这个地方我们看不出来,所有的joint都在右上角展示出来了。而我们能够控制的,也就是这些关节,我们能怎么控制这些关节,关节提供给用户的控制量都是在URDF中定义好的
URDF中的joint是通过joint标签来定义的,在joint的标签属性中,除了定义joint的名字外,还需要给出这个关节的类型
比如连接head与base的joint中,type为“continuous”,说明这个关节可以在连续空间中转动:
比如连接base和机器人右腿的joint,定义如下,可以看到type为fixed,说明这个joint不能转动,是固定死的:
因此对于URDF模型文件生成的机器人来说,我们只能通过控制这些type不为fixed的joint来控制机器人。
那么为了控制一个机器人,我们就得知道关于机器关节的信息,基于这些信息,我们才能知道一个机器人有什么地方可以被我们控制以及怎么控制
主要是getNumJoints()和getJointInfo()这两个函数, getNumJoints()接受机器人的ID,返回该机器人的关节数量,通常遍历机器人所有关节时会用到;getJointInfo()接受机器人的ID以及关节的索引值,用来查询指定机器人指定关节的信息,通过这些信息,我们可以知道哪些关节是我们想要的
以这个输出结果为例:
- r2d2的节点数量为: 15
- r2d2的信息:
- 关节序号:0
- 关节名称:b'base_to_right_leg'
- 关节类型:4
- 机器人第一个位置的变量索引:-1
- 机器人第一个速度的变量索引:-1
- 保留参数:0
- 关节的阻尼大小:0.0
- 关节的摩擦系数:0.0
- slider和revolute(hinge)类型的位移最小值:0.0
- slider和revolute(hinge)类型的位移最大值:-1.0
- 关节驱动的最大值:0.0
- 关节的最大速度:0.0
- 节点名称:b'right_leg'
- 局部框架中的关节轴系:(0.0, 0.0, 0.0)
- 父节点frame的关节位置:(0.22, 0.0, 0.25)
- 父节点frame的关节方向:(0.0, -0.7070904020014416, 0.0, 0.7071231599922604)
- 父节点的索引,若是基座返回-1:-1
-
-
- 关节序号:1
- 关节名称:b'right_base_joint'
- 关节类型:4
- 机器人第一个位置的变量索引:-1
- 机器人第一个速度的变量索引:-1
- 保留参数:0
- 关节的阻尼大小:0.0
- 关节的摩擦系数:0.0
- slider和revolute(hinge)类型的位移最小值:0.0
- slider和revolute(hinge)类型的位移最大值:-1.0
- 关节驱动的最大值:0.0
- 关节的最大速度:0.0
- 节点名称:b'right_base'
- 局部框架中的关节轴系:(0.0, 0.0, 0.0)
- 父节点frame的关节位置:(0.2999999996780742, 0.0, -1.3898038463944216e-05)
- 父节点frame的关节方向:(0.0, 0.7070904020014416, 0.0, 0.7071231599922604)
- 父节点的索引,若是基座返回-1:0
-
-
- 关节序号:2
- 关节名称:b'right_front_wheel_joint'
- 关节类型:0
- 机器人第一个位置的变量索引:7
- 机器人第一个速度的变量索引:6
- 保留参数:1
- 关节的阻尼大小:0.0
- 关节的摩擦系数:0.0
- slider和revolute(hinge)类型的位移最小值:0.0
- slider和revolute(hinge)类型的位移最大值:-1.0
- 关节驱动的最大值:100.0
- 关节的最大速度:100.0
- 节点名称:b'right_front_wheel'
- 局部框架中的关节轴系:(0.0, 0.0, 1.0)
- 父节点frame的关节位置:(0.0, 0.133333333333, -0.085)
- 父节点frame的关节方向:(0.0, -0.7070904020014416, 0.0, 0.7071231599922604)
- 父节点的索引,若是基座返回-1:1
根据“关节类型”这一项,可以判断改关节是不是可以操控的。于是乎,我们先试着通过代码打印r2d2的可移动的关节,首先创建环境加载模型:
- import pybullet as p
- import pybullet_data
- import time
- from pprint import pprint
-
- # 连接物理引擎
- use_gui = True
- if use_gui:
- serve_id = p.connect(p.GUI)
- else:
- serve_id = p.connect(p.DIRECT)
-
- # 添加资源路径
- p.setAdditionalSearchPath(pybullet_data.getDataPath())
-
- # 配置渲染机制
- p.configureDebugVisualizer(p.COV_ENABLE_GUI, 0)
- p.configureDebugVisualizer(p.COV_ENABLE_TINY_RENDERER, 0)
- p.configureDebugVisualizer(p.COV_ENABLE_RENDERING, 0)
-
- # 设置重力,加载模型
- p.setGravity(0, 0, -10)
- _ = p.loadURDF("plane.urdf", useMaximalCoordinates=True)
- robot_id = p.loadURDF("r2d2.urdf", useMaximalCoordinates=True)
加载完后,我们来打印一下可以使用的关节:(不可用关节的关节类型都是4,也就是常量p.JOINT_FIXED,所以可以根据这个信息来筛选)
- available_joints_indexes = [i for i in range(p.getNumJoints(robot_id)) if p.getJointInfo(robot_id, i)[2] != p.JOINT_FIXED]
-
- pprint([p.getJointInfo(robot_id, i)[1] for i in available_joints_indexes])
输出结果:
- [b'right_front_wheel_joint',
- b'right_back_wheel_joint',
- b'left_front_wheel_joint',
- b'left_back_wheel_joint',
- b'gripper_extension',
- b'left_gripper_joint',
- b'right_gripper_joint',
- b'head_swivel']
我们通过程序得到了可控制关节的相关信息,下面就是操控它们了
这两个的用法基本一样,不同的是前者调用一次只能设置一台关节电机的参数,后者调用一次则可以设置一组关节电机的参数
setJointMotorControl2有三个必选参数和若干可选参数,三个必选参数为需要控制的机器人的序号bodyUniqueId,需要控制的关节索引jointIndex,还有控制模式controlMode。控制模式controlMode的可填值为POSITION_CONTROL, VELOCITY_CONTROL, TORQUE_CONTROL and PD_CONTROL
setJointMotorControlArray类似,只不过把对应的参数换成同类型的列表就行
仍然之前那个机器人例子为例,使用上述函数来让它动起来。需要注意的是,移动它只需要四个轮子,所以我们需要先挑选出轮子与基座的关节索引,这个很简单,只需要一句话
wheel_joints_indexes = [i for i in available_joints_indexes if "wheel" in str(p.getJointInfo(robot_id, i)[1])]
开启渲染后,使用setJointMotorControlArray在每次迭代步一次性设置四个轮子的关节马达的值:
- # 预备工作结束,重新开启渲染
- p.configureDebugVisualizer(p.COV_ENABLE_RENDERING, 1)
- # 关闭实时模拟步
- p.setRealTimeSimulation(0)
-
- target_v = 10 # 电机达到的预定角速度(rad/s)
- max_force = 10 # 电机能够提供的力,这个值决定了机器人运动时的加速度,太快会翻车哟,单位N
-
- for i in range(1000):
- p.stepSimulation()
- p.setJointMotorControlArray(
- bodyUniqueId=robot_id,
- jointIndices=wheel_joints_indexes,
- controlMode=p.VELOCITY_CONTROL,
- targetVelocities=[target_v for _ in wheel_joints_indexes],
- forces=[max_force for _ in wheel_joints_indexes]
- )
- time.sleep(1 / 240) # 模拟器一秒模拟迭代240步
-
- # 断开连接
- p.disconnect(serve_id)
将指定机器人的指定关节设为禁用(将这个马达的force设为0):
- maxForce = 0
- mode = p.VELOCITY_CONTROL
- p.setJointMotorControl2(objUid, jointIndex,
- controlMode=mode, force=maxForce)
让我们的机器人一直在画面中心,这个时候,就需要调用resetDebugVisualizerCamera函数来设置当前相机的位置和朝向,那么我只需要在每次迭代步结束后,调用这个函数来调整相机位置
在设置相机前,我们还得知道机器人现在的位置,我们可以通过getJointState(s)来查询机器人指定关节的信息,当然这个信息里面就包含着我们想要的位置信息(三维坐标):
如果只是想要获得机器人在世界坐标系中的位置坐标的话,直接通getBasePositionAndOrientation就可以获取机器人的位置和位姿四元数了。
那么接下来,我们只需要通过上述这个函数查询机器人base的位置信息,再用这个位置信息去更新相机的位置,迭代循环更改为:
- for i in range(1000):
- p.stepSimulation()
- p.setJointMotorControlArray(
- bodyUniqueId=robot_id,
- jointIndices=wheel_joints_indexes,
- controlMode=p.VELOCITY_CONTROL,
- targetVelocities=[target_v for _ in wheel_joints_indexes],
- forces=[max_force for _ in wheel_joints_indexes]
- )
- location, _ = p.getBasePositionAndOrientation(robot_id)
- p.resetDebugVisualizerCamera(
- cameraDistance=3,
- cameraYaw=110,
- cameraPitch=-30,
- cameraTargetPosition=location
- )
- time.sleep(1 / 240) # 模拟器一秒模拟迭代240步
可以看到,机器人已经时刻处于镜头中央了
2.Pinocchio
总览一下实现的主要算法:正向运动学(forward kinematics):给定机器人构型,计算每个关节的空间位置并将其存储为坐标变换。如果给定速度或加速度,将计算每个关节的空间速度(以局部坐标系表示)。运动学雅可比矩阵(kinematic jacobian):在机械臂运动学中用来计算机械臂末端执行器的速度与各个关节运动速度之间的关系。逆动力学(inverse dynamics):采用Recursive Newton-Euler Algorithm (RNEA) 计算逆动力学。即给定所需的机器人构型、速度、加速度,计算并存储执行该运动所需的扭矩。关节空间惯性矩阵(Joint space inertia matrix):采用Compostie rigid body algortihm (CRBA)算法,计算关节空间惯性矩阵。前向动力学(forward dynamics):采用Articulated Body Algorithm(ABA)计算无约束的前向动力学。即给定机器人构型、速度、扭矩和外力的情况下,可以计算出由此产生的关节加速度。其他算法。
最简单的例子引入,计算机器人逆动力学:
from future import print_function # 导入print_function函数
import pinocchio # 导入Pinocchio库,用于机器人动力学建模和分析
model = pinocchio.buildSampleModelManipulator() # 创建一个带有操纵器的机器人模型示例
data = model.createData() # 创建数据结构,用于后续的数值计算
q = pinocchio.neutral(model) # 计算机器人的中性配置,即所有关节都处于机械中性位置的配置
v = pinocchio.utils.zero(model.nv) # 创建一个长度为model.nv的零向量,model.nv是机器人的自由度数量
a = pinocchio.utils.zero(model.nv) # 创建另一个长度为model.nv的零向量
tau = pinocchio.rnea(model, data, q, v, a) # 使用RNEA方法计算关节力(tau)
print('tau = ', tau.T) # 打印关节力tau,并将其转换为NumPy数组并进行转置(tau.T)
该程序加载机器人模型,创建用于算法缓冲的数据结构,并运行递归牛顿-欧拉算法 (RNEA) 来计算机器人逆动力学。
第一段定义了模型,在 Python 中,模型是通过调用单个命令创建的。模型类包含机器人的常量参数(质量、段长度、主体名称等),即算法无法修改的参数。由于大多数算法需要额外的空间来存储内部值,因此我们还必须分配一个专用于模型的数据结构。
逆动力学计算所需的扭矩,以跟踪由关节配置、速度和加速度定义的轨迹。在第二段中,我们定义了关节配置,速度和加速度。速度和加速度是纯向量,可以初始化为任何值,它们的大小对应于机器人的瞬时自由度数。
还有一个使用Pinocchio库计算机器人动力学的示例:
这个脚本的主要功能如下:1.计算7自由度操纵器(以Kuka iiwa7为例)的逆动力学,包括:每个关节的总扭矩; Gravity contribution G(q); 每个关节的惯性矩阵 M; 每个关节的科里奥利矩阵 C
2. 验证 dM/dt - 2*C 的反对称性质
使用:将dy.py文件和iiwa7_description.urdf文件放在同一个文件夹中,使用Python运行脚本,最后你可以得到你的模型中的M、C和G
注意:如果你更改了urdf文件使用了你的模型,还应根据你的模型改变q, qdot,qddot的大小
3.pinocchio与pybullet
0.回顾pinocchio运动学计算及参数更新机制:
导入URDF模型,创建数据模型。注意,model为刚体的模型结构,data存储了刚体系统的数据结构,当调用forwardKinematics()函数计算正向运动学时,函数根据当前关节位置、速度和加速度,更新data中存储的关节位置、空间速度和空间加速度
- import pinocchio # 导入Pinocchio库
- from sys import argv # 从sys模块导入argv列表,用于获取命令行参数
- from os.path import dirname, join, abspath # 从os.path模块导入dirname, join, abspath函数,用于操作文件路径
-
- # 这里的路径指向Pinocchio源代码,但你可以在这里定义你自己的目录。
- pinocchio_model_dir = join(dirname(dirname(str(abspath(__file__)))), "models")
-
- # 应该在这里设置你自己的URDF文件,或者直接作为此例子的参数传递。
- urdf_filename = pinocchio_model_dir + '/example-robot-data/robots/ur_description/urdf/ur5_robot.urdf' if len(argv)<2 else argv[1]
-
- # 从URDF文件中加载模型
- model = pinocchio.buildModelFromUrdf(urdf_filename)
- print('模型名称: ' + model.name)
-
- # 创建算法所需的data对象
- data = model.createData()
-
- # 随机采样一个配置
- q = pinocchio.randomConfiguration(model)
- print('配置q: %s' % q.T)
-
- # 在运动学树中进行正向运动学
- pinocchio.forwardKinematics(model, data, q)
-
- # 打印出运动学树中每个关节的位置
- for name, oMi in zip(model.names, data.oMi):
- print(("{:<24} : {: .2f} {: .2f} {: .2f}"
- .format(name, *oMi.translation.T.flat)))
1.联调
仿真在 PyBullet中进行,动力学库 Pinocchio 用于更新机器人的动态项,如惯性矩阵和雅可比矩阵
关键函数是根据bullet中状态更新pinocchio参数:
- def get_state_update_pinocchio(self):
- """从pybullet获取状态并更新pinocchio机器人内部。
- 此函数从pybullet模拟器中获取状态,并将
- 运动学、雅可比矩阵、质心矩等正向传递给pinocchio机器人
- (关于计算的量,请参见forward_pinocchio的详细信息)。"""
- q, dq, ef = self.get_state() # 从self对象中获取状态,通常包含关节位置q、关节速度dq和末端执行器位置ef
- self.update_pinocchio(q, dq) # 更新pinocchio机器人内部状态,通常包括运动学、雅可比矩阵和质心矩的计算
- return q, dq, ef # 返回更新后的关节位置q、关节速度dq和末端执行器位置ef
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。