利用pybullet 实现ur5 机械臂仿真

利用pybullet 实现ur5 机械臂仿真

想做一个机械臂仿真软件,以前利用C++ 结合FCL和OSG实现了一个半成品,但是这种方案需要根据机械臂DH参数,解析出机械臂各关节位姿然后利用OSG渲染,并且需要自己实现逆解算法,过于麻烦。而使用pybullet,上述得大部分功能都有现成的API接口。

仿真软件得几个要素:

(1),渲染引擎 ,需要将模型渲染可视化出来。VTK,OSG或者原生OpenGL。

(2),动力学引擎,根据物体的物理属性计算运动、旋转和碰撞。Bullet或者ODE,或者只用于碰撞检测的FCL。

(3),模型描述,负责机器人模型的建模,描述各机器人的连杆和关节,还有质量属性,颜色等。比如常用的URDF文件和SDF文件。

pybullet 具备了上述所有的要素,动力学引擎使用bullet,渲染使用OpenGL,并可以直接加载URDF文件,而且提供了 正逆算法。

实现逻辑:

1,利用URDF文件,加载模型。

2,给出目标,也就是抓取点位置。

3,利用逆解算法,求出各关节角度。

4,设置各关节角度,并判判断否碰撞。

5,理想情况下,将此组角度过滤,选择下一组不碰撞的数据 发送真实的给机械臂。

代码:

import pybullet as p
import pybullet_data
import time
import math
from collections   import namedtuple
import numpy as np

# 启动仿真引擎的GUI
p.connect(p.GUI)

# 设置重力加速度
p.setGravity(0, 0, -9.81)

# 加载URDF模型路径
p.setAdditionalSearchPath(pybullet_data.getDataPath())

# 设置视角
p.resetDebugVisualizerCamera(cameraDistance=3,
                                    cameraYaw=120,
                                    cameraPitch=-45,
                                    cameraTargetPosition=[0,0,0])

# 加载平面模型作为地面
planeId = p.loadURDF("plane.urdf")

#托盘
trayId=p.loadURDF("tray/traybox.urdf",basePosition=[0.6,0,0.63])

#桌子
tableId=p.loadURDF("table/table.urdf",basePosition=[0.5,0,0])

# 加载ur5 机械臂
robotId  = p.loadURDF("D:\\keiler\\sim\\urdfs\\ur5.urdf",useFixedBase=True )  #basePosition=[0.0,0,0.62]


initial_position = [0, 0, 0.62]  # 设置初始位置为 [0, 0, 0.62] 米
initial_orientation = p.getQuaternionFromEuler([0, 0, 0])  # 设置初始方向为欧拉角 [0, 0, 0] 对应的四元数
p.resetBasePositionAndOrientation(robotId, initial_position, initial_orientation)

# 获取机械臂末端执行器的索引
endEffectorIndex =6

# 获取机械臂的关节数量
numJoints = p.getNumJoints(robotId)
print("关节数量:"+str(numJoints))

# 打印每个关节的信息
for joint_index in range(numJoints):
    joint_info = p.getJointInfo(robotId, joint_index)
    print(f"Joint {joint_index}: {joint_info}")

# 机械臂的初始位置
restingPosition = [0,3.14, -1.57, 1.57, 1.57, 1.57, -1.57, 0]
for jointNumber in range(numJoints):
    p.resetJointState(robotId, jointNumber, restingPosition[jointNumber])
    
    
# 设置圆形的参数
circle_radius = 3
circle_center = [0, 0]
numPoints = 50
angles = [2 * math.pi * float(i) / numPoints for i in range(numPoints)]

#调参控件
target_x = p.addUserDebugParameter("Target X", -10, 10, 0)
target_y = p.addUserDebugParameter("Target Y", -10, 10, 0)
target_z = p.addUserDebugParameter("Target Z", -10, 10, 0)

#开始按钮
button_start = p.addUserDebugParameter("Start", 1, 0, 1)
# 初始状态变量
button_state_prev = 0


#画个球
sphere_visual = p.createVisualShape(shapeType=p.GEOM_SPHERE, radius=0.03, rgbaColor=[1, 0, 0, 1])
sphere_id = p.createMultiBody(baseMass=0, baseCollisionShapeIndex=-1, baseVisualShapeIndex=sphere_visual, basePosition=[0, 0, 1])


#多组解
def calculate_ik_multiple_solutions(robot_id, end_effector_index, target_position, target_orientation, num_solutions=5):
    solutions = []
    for i in range(num_solutions):
        # 生成一个随机的初始关节状态
        random_joint_positions = [np.random.uniform(-np.pi, np.pi) for _ in range(numJoints)]
        # 计算逆运动学
        ik_solution = p.calculateInverseKinematics(robot_id, end_effector_index, target_position, target_orientation, jointDamping=[0.01]*numJoints, lowerLimits=[-np.pi]*numJoints, upperLimits=[np.pi]*numJoints, jointRanges=[2*np.pi]*numJoints, restPoses=random_joint_positions)
        solutions.append(ik_solution)
    return solutions


try:
    while True:
        x = p.readUserDebugParameter(target_x)
        y = p.readUserDebugParameter(target_y)
        z = p.readUserDebugParameter(target_z)
    
        # 更新球的位置
        p.resetBasePositionAndOrientation(sphere_id, [x, y, z], [0, 0, 0, 1])
        
        #判断按钮状态
        button_state = p.readUserDebugParameter(button_start)
        if button_state != button_state_prev:
            print("Button pressed")
            # 使用逆向运动学计算关节角度
            jointPoses = p.calculateInverseKinematics(robotId, endEffectorIndex, [x, y, z],[0, 0, 0, 1])
            
            # 移动机械臂
            p.setJointMotorControl2(bodyIndex=robotId,jointIndex=1,controlMode=p.POSITION_CONTROL,targetPosition=jointPoses[0],targetVelocity=0,force=500,positionGain=0.03,velocityGain=1)
            p.setJointMotorControl2(bodyIndex=robotId,jointIndex=2,controlMode=p.POSITION_CONTROL,targetPosition=jointPoses[1],targetVelocity=0,force=500,positionGain=0.03,velocityGain=1)
            p.setJointMotorControl2(bodyIndex=robotId,jointIndex=3,controlMode=p.POSITION_CONTROL,targetPosition=jointPoses[2],targetVelocity=0,force=500,positionGain=0.03,velocityGain=1)
            p.setJointMotorControl2(bodyIndex=robotId,jointIndex=4,controlMode=p.POSITION_CONTROL,targetPosition=jointPoses[3],targetVelocity=0,force=500,positionGain=0.03,velocityGain=1)
            p.setJointMotorControl2(bodyIndex=robotId,jointIndex=5,controlMode=p.POSITION_CONTROL,targetPosition=jointPoses[4],targetVelocity=0,force=500,positionGain=0.03,velocityGain=1)
            p.setJointMotorControl2(bodyIndex=robotId,jointIndex=6,controlMode=p.POSITION_CONTROL,targetPosition=jointPoses[5],targetVelocity=0,force=500,positionGain=0.03,velocityGain=1)
        button_state_prev = button_state

        
            
        contact_points  = p.getContactPoints(bodyA=trayId,bodyB=robotId)
        isColl =  bool(contact_points)
        if isColl:
            print("撞上了!")
        
                

        p.stepSimulation()
        time.sleep(0.01)

except KeyboardInterrupt:
    # 用户中断程序时,退出循环
    print("Circle drawing interrupted by user.")
# 断开与仿真引擎的连接
p.disconnect()

问题:

1,逆解 只能返回一组关节角度,没法根据碰撞,去规划路径。

2,返回的各关节角度 貌似有偏差。

3,界面二次开发有点困难,控件太少,想封装成一个产品需要结合其它界面框架,比如Qt,有个想法是用pybullet当服务器,PyQt+VTK当客户端,因为这套东西可以无UI运行。

效果: ​

源码:

https://gitcode.com/keiler20181/sim.git

发表回复