====== 单个python文件实现pybullet在arx5上的双臂仿真 ======
首先看效果图:
{{:机器人:仿真:pybullet:pybullet常用代码:arx5.png?800|}}
安装python环境
''pip install pybullet numpy''
准备代码目录结构如下:
.
├── arx5
│ ├── apple.urdf
│ ├── arx5.urdf
│ ├── meshes
│ │ ├── X5
│ │ │ ├── link1.STL
│ │ │ ├── link2.STL
│ │ │ ├── link3.STL
│ │ │ ├── link4.STL
│ │ │ ├── link5.STL
│ │ │ └── link6.STL
│ │ └── base_link.STL
│ └── run-sim.py
└── main.py
其中STL文件参考:
- link文件 https://github.com/real-stanford/arx5-sdk/blob/main/models/meshes/X5/link1.STL
- base_link文件:https://github.com/real-stanford/arx5-sdk/blob/main/models/meshes/X5/base_link.STL
URDF文件参考
- arx5.URDF文件:https://github.com/real-stanford/arx5-sdk/blob/main/models/X5.urdf
- apple.URDF 文件是用AI生成的:
最终的模拟启动代码如下(纯CPU的):
import pybullet as p
import pybullet_data
import time
import random
import numpy as np
import os
current_py_cwd = os.path.join(os.path.dirname(__file__))
class GraspingController:
def __init__(self, robot_id, gripper_link_index):
self.robot_id = robot_id
self.gripper_link_index = gripper_link_index
self.constraints = []
def grasp_object(self, object_id):
gripper_state = p.getLinkState(self.robot_id, self.gripper_link_index)
gripper_pos, gripper_orn = gripper_state[0], gripper_state[1]
obj_pos, obj_orn = p.getBasePositionAndOrientation(object_id)
constraint_id = p.createConstraint(
parentBodyUniqueId=self.robot_id,
parentLinkIndex=self.gripper_link_index,
childBodyUniqueId=object_id,
childLinkIndex=-1,
jointType=p.JOINT_FIXED,
jointAxis=[0, 0, 0],
parentFramePosition=[0, 0, 0],
childFramePosition=[0, 0, 0]
)
self.constraints.append(constraint_id)
return constraint_id
def release_object(self, constraint_id=None):
if constraint_id:
p.removeConstraint(constraint_id)
if constraint_id in self.constraints:
self.constraints.remove(constraint_id)
else:
for constraint in self.constraints:
p.removeConstraint(constraint)
self.constraints = []
class ARX5Simulation:
def __init__(self):
try:
p.connect(p.GUI, options="--enable_gpu")
except:
print("警告:无法启用GPU加速,PyBullet将使用CPU进行物理计算")
print("错误:需要GPU加速才能运行此仿真。程序退出。")
exit(1)
p.setAdditionalSearchPath(pybullet_data.getDataPath())
p.configureDebugVisualizer(p.COV_ENABLE_GUI, 1)
p.configureDebugVisualizer(p.COV_ENABLE_RENDERING, 1)
p.configureDebugVisualizer(p.COV_ENABLE_SHADOWS, 1)
try:
p.configureDebugVisualizer(p.COV_ENABLE_WIREFRAME, 0)
p.resetDebugVisualizerCamera(cameraDistance=1.5, cameraYaw=45, cameraPitch=-30, cameraTargetPosition=[0,0,0.5])
except:
print("某些渲染选项不可用,继续使用默认设置")
p.setGravity(0, 0, -9.81)
p.setRealTimeSimulation(0)
self.plane_id = p.loadURDF("plane.urdf")
self.object_urdfs = [os.path.join(current_py_cwd, "apple.urdf")]
self.object_id = p.loadURDF(random.choice(self.object_urdfs), basePosition=[0, 0, 0.5])
self.robot_id1 = p.loadURDF(os.path.join(current_py_cwd, "arx5.urdf"), basePosition=[-0.5, 0, 0], useFixedBase=True)
self.robot_id2 = p.loadURDF(os.path.join(current_py_cwd, "arx5.urdf"), basePosition=[0.5, 0, 0], useFixedBase=True)
self.num_joints1 = p.getNumJoints(self.robot_id1)
self.num_joints2 = p.getNumJoints(self.robot_id2)
print(f"ARX5 左臂有 {self.num_joints1} 个关节")
print(f"ARX5 右臂有 {self.num_joints2} 个关节")
self.ee_index1 = 7
self.ee_index2 = 7
self.gripper1 = GraspingController(self.robot_id1, self.ee_index1)
self.gripper2 = GraspingController(self.robot_id2, self.ee_index2)
light_position = [random.uniform(-1, 1), random.uniform(-1, 1), random.uniform(2, 3)]
p.addUserDebugParameter("lightPosX", -2, 2, light_position[0])
p.addUserDebugParameter("lightPosY", -2, 2, light_position[1])
p.addUserDebugParameter("lightPosZ", 1, 3, light_position[2])
self.target_x1 = p.addUserDebugParameter("Target X1", -1, 1, -0.3)
self.target_y1 = p.addUserDebugParameter("Target Y1", -1, 1, 0)
self.target_z1 = p.addUserDebugParameter("Target Z1", 0.1, 1.5, 0.8)
self.target_x2 = p.addUserDebugParameter("Target X2", -1, 1, 0.3)
self.target_y2 = p.addUserDebugParameter("Target Y2", -1, 1, 0)
self.target_z2 = p.addUserDebugParameter("Target Z2", 0.1, 1.5, 0.8)
self.grasp_mode1 = p.addUserDebugParameter("Grasp Arm1", 0, 1, 0)
self.grasp_mode2 = p.addUserDebugParameter("Grasp Arm2", 0, 1, 0)
self.joint1_params = []
self.joint2_params = []
for i in range(6):
joint_info = p.getJointInfo(self.robot_id1, i)
joint_lower_limit = joint_info[8]
joint_upper_limit = joint_info[9]
lower = joint_lower_limit if joint_lower_limit != 0.0 else -3.14
upper = joint_upper_limit if joint_upper_limit != 0.0 else 3.14
param_id = p.addUserDebugParameter(f"Joint1_{i+1}", lower, upper, 0.0)
self.joint1_params.append(param_id)
for i in range(6):
joint_info = p.getJointInfo(self.robot_id2, i)
joint_lower_limit = joint_info[8]
joint_upper_limit = joint_info[9]
lower = joint_lower_limit if joint_lower_limit != 0.0 else -3.14
upper = joint_upper_limit if joint_upper_limit != 0.0 else 3.14
param_id = p.addUserDebugParameter(f"Joint2_{i+1}", lower, upper, 0.0)
self.joint2_params.append(param_id)
self.arm1_constraint = None
self.arm2_constraint = None
self.current_joint_positions1 = [0] * self.num_joints1
self.current_joint_positions2 = [0] * self.num_joints2
def compute_ik(self, robot_id, ee_index, target_pos, target_orn=None):
if target_orn is None:
target_orn = p.getQuaternionFromEuler([0, 0, 0])
try:
joint_poses = p.calculateInverseKinematics(
robot_id,
ee_index,
target_pos,
target_orn
)
return joint_poses
except:
current_poses = []
for i in range(ee_index):
joint_info = p.getJointState(robot_id, i)
current_poses.append(joint_info[0])
return current_poses
def move_arm_to_position(self, robot_id, target_pos, ee_index, max_iterations=100):
target_orn = p.getQuaternionFromEuler([0, 0, 0])
joint_poses = self.compute_ik(robot_id, ee_index, target_pos, target_orn)
for i in range(min(len(joint_poses), ee_index)):
p.setJointMotorControl2(
bodyUniqueId=robot_id,
jointIndex=i,
controlMode=p.POSITION_CONTROL,
targetPosition=joint_poses[i],
force=500
)
def check_grasp_proximity(self, robot_id, ee_index, object_id, threshold=0.1):
gripper_state = p.getLinkState(robot_id, ee_index)
gripper_pos = gripper_state[0]
obj_pos, _ = p.getBasePositionAndOrientation(object_id)
distance = np.linalg.norm(np.array(gripper_pos) - np.array(obj_pos))
return distance < threshold
def run_simulation(self):
print("Starting simulation. Use the sliders to control the arms and grasp objects.")
prev_grasp1 = 0
prev_grasp2 = 0
while True:
target_pos1 = [
p.readUserDebugParameter(self.target_x1),
p.readUserDebugParameter(self.target_y1),
p.readUserDebugParameter(self.target_z1)
]
target_pos2 = [
p.readUserDebugParameter(self.target_x2),
p.readUserDebugParameter(self.target_y2),
p.readUserDebugParameter(self.target_z2)
]
use_position_control = True
for i in range(6):
joint_val1 = p.readUserDebugParameter(self.joint1_params[i])
joint_val2 = p.readUserDebugParameter(self.joint2_params[i])
if abs(joint_val1) > 0.01 or abs(joint_val2) > 0.01:
use_position_control = False
break
if use_position_control:
self.move_arm_to_position(self.robot_id1, target_pos1, self.ee_index1)
self.move_arm_to_position(self.robot_id2, target_pos2, self.ee_index2)
else:
for i in range(6):
joint_angle1 = p.readUserDebugParameter(self.joint1_params[i])
p.setJointMotorControl2(
bodyUniqueId=self.robot_id1,
jointIndex=i,
controlMode=p.POSITION_CONTROL,
targetPosition=joint_angle1,
force=500
)
joint_angle2 = p.readUserDebugParameter(self.joint2_params[i])
p.setJointMotorControl2(
bodyUniqueId=self.robot_id2,
jointIndex=i,
controlMode=p.POSITION_CONTROL,
targetPosition=joint_angle2,
force=500
)
grasp1 = p.readUserDebugParameter(self.grasp_mode1)
grasp2 = p.readUserDebugParameter(self.grasp_mode2)
if grasp1 > 0.5 and prev_grasp1 < 0.5:
if self.check_grasp_proximity(self.robot_id1, self.ee_index1, self.object_id):
if self.arm1_constraint is None:
self.arm1_constraint = self.gripper1.grasp_object(self.object_id)
print("Arm 1: Grasped object")
else:
print("Arm 1: Too far from object to grasp")
elif grasp1 < 0.5 and prev_grasp1 > 0.5:
if self.arm1_constraint is not None:
self.gripper1.release_object(self.arm1_constraint)
self.arm1_constraint = None
print("Arm 1: Released object")
if grasp2 > 0.5 and prev_grasp2 < 0.5:
if self.check_grasp_proximity(self.robot_id2, self.ee_index2, self.object_id):
if self.arm2_constraint is None:
self.arm2_constraint = self.gripper2.grasp_object(self.object_id)
print("Arm 2: Grasped object")
else:
print("Arm 2: Too far from object to grasp")
elif grasp2 < 0.5 and prev_grasp2 > 0.5:
if self.arm2_constraint is not None:
self.gripper2.release_object(self.arm2_constraint)
self.arm2_constraint = None
print("Arm 2: Released object")
prev_grasp1 = grasp1
prev_grasp2 = grasp2
p.stepSimulation()
time.sleep(1./240.)
def shutdown(self):
p.disconnect()
def main():
sim = ARX5Simulation()
try:
sim.run_simulation()
except KeyboardInterrupt:
print("Simulation interrupted by user.")
finally:
sim.shutdown()
if __name__ == "__main__":
main()