-
Notifications
You must be signed in to change notification settings - Fork 71
Expand file tree
/
Copy pathjoystick_sim_and_real_so100.py
More file actions
174 lines (155 loc) · 6.14 KB
/
joystick_sim_and_real_so100.py
File metadata and controls
174 lines (155 loc) · 6.14 KB
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
import mujoco
import numpy as np
import src.mujoco_viewer as mujoco_viewer
import src.pinocchio_kinematic as pinocchio_kinematic
import time
import pygame
import os
import math
from so100_real_control import ZMQCommunicator
motors_key=[
"shoulder_pan",
"shoulder_lift",
"elbow_flex",
"wrist_flex",
"wrist_roll",
"gripper"
]
# 设置环境变量以确保正确访问游戏杆设备
os.environ["SDL_JOYSTICK_DEVICE"] = "/dev/input/js0"
SCENE_XML_PATH = 'model/trs_so_arm100/scene.xml'
ARM_XML_PATH = 'model/trs_so_arm100/so_arm100.xml'
class XboxController:
"""Xbox手柄控制器类,负责处理所有手柄输入"""
def __init__(self):
# 初始化位置参数
self.x = 0.0
self.y = -0.3
self.z = 0.15
# 位置限制
self.x_min, self.x_max = -0.3, 0.3
self.y_min, self.y_max = -0.4, 0.0
self.z_min, self.z_max = 0.05, 0.3
# 控制灵敏度
self.sensitivity = 0.005
# 死区阈值(过滤摇杆中心微小漂移)
self.deadzone = 0.1
self.dof5_target = None
self.button1_pressed = False
self.button2_pressed = False
self.controller = self.init_controller()
def init_controller(self):
"""初始化Xbox手柄(通过pygame访问js设备)"""
pygame.init()
pygame.joystick.init()
if pygame.joystick.get_count() == 0:
print("joystick not found")
return None
# 对应/dev/input/js0
joystick = pygame.joystick.Joystick(0)
joystick.init()
print(f"joystick found: {joystick.get_name()}")
print(f"button count: {joystick.get_numbuttons()}")
return joystick
def is_connected(self):
"""检查手柄是否连接"""
return self.controller is not None
def handle_input(self, arm):
"""处理手柄输入并更新控制参数"""
if not self.is_connected():
return
# 处理pygame事件(必须调用,否则无法更新轴值和按钮状态)
pygame.event.pump()
# 读取左摇杆X轴(0号轴)
x_axis = self.controller.get_axis(1)
# 应用死区过滤
if abs(x_axis) < self.deadzone:
x_axis = 0.0
# 读取左摇杆Y轴(1号轴)
y_axis = self.controller.get_axis(0)
if abs(y_axis) < self.deadzone:
y_axis = 0.0
# 读取右摇杆Y轴(4号轴)
z_axis = -self.controller.get_axis(4)
if abs(z_axis) < self.deadzone:
z_axis = 0.0
# 根据轴值更新位置
self.x = np.clip(self.x + x_axis * self.sensitivity,
self.x_min, self.x_max)
self.y = np.clip(self.y + y_axis * self.sensitivity,
self.y_min, self.y_max)
self.z = np.clip(self.z + z_axis * self.sensitivity,
self.z_min, self.z_max)
# 检测按钮1(假设是0号按钮,通常是A键)
button1 = self.controller.get_button(0)
if button1 and not self.button1_pressed:
print("Button1 pressed - 设置dof[5]为满量程")
self.dof5_target = arm.model.upperPositionLimit[5]
self.button1_pressed = True
elif not button1:
self.button1_pressed = False
# 检测按钮2(假设是1号按钮,通常是B键)
button2 = self.controller.get_button(1)
if button2 and not self.button2_pressed:
print("Button2 pressed - 设置dof[5]为最小值")
self.dof5_target = arm.model.lowerPositionLimit[5]
self.button2_pressed = True
elif not button2:
self.button2_pressed = False
def get_position_target(self):
return self.x, self.y, self.z
def get_dof5_target(self):
return self.dof5_target
def cleanup(self):
pygame.quit()
class RobotController(mujoco_viewer.CustomViewer):
def __init__(self, scene_path, arm_path, controller):
super().__init__(scene_path, distance=1.5, azimuth=135, elevation=-30)
self.scene_path = scene_path
self.arm_path = arm_path
self.controller = controller
self.communicator = ZMQCommunicator()
self.arm = pinocchio_kinematic.Kinematics("Wrist_Roll")
self.arm.buildFromMJCF(arm_path)
self.last_dof = None
def runBefore(self):
pass
def runFunc(self):
"""主循环中执行的函数"""
self.controller.handle_input(self.arm)
x, y, z = self.controller.get_position_target()
print(f"当前位置: x={x:.3f}, y={y:.3f}, z={z:.3f}")
# 构建变换矩阵(保持末端执行器姿态不变)
tf = self.build_transform_simple(x, y, z, np.pi / 4, 0, 0)
self.dof, info = self.arm.ik(tf, current_arm_motor_q=self.last_dof)
self.last_dof = self.dof
dof5_target = self.controller.get_dof5_target()
if dof5_target is not None:
self.dof[5] = dof5_target
self.data.qpos[:6] = self.dof[:6]
sim_joint_rad = self.data.qpos[:6].copy()
# sim_joint_deg = [math.degrees(q) for q in sim_joint_rad]
actions = {f"{motor}.pos": val for motor, val in zip(motors_key, sim_joint_rad)}
self.communicator.send_data(actions)
mujoco.mj_step(self.model, self.data)
time.sleep(0.05)
def build_transform_simple(self, x, y, z, roll, pitch, yaw):
cr, sr = np.cos(roll), np.sin(roll)
cp, sp = np.cos(pitch), np.sin(pitch)
cy, sy = np.cos(yaw), np.sin(yaw)
return np.array([
[cy*cp, cy*sp*sr - sy*cr, cy*sp*cr + sy*sr, x],
[sy*cp, sy*sp*sr + cy*cr, sy*sp*cr - cy*sr, y],
[-sp, cp*sr, cp*cr, z],
[0, 0, 0, 1]
])
if __name__ == "__main__":
controller = XboxController()
if not controller.is_connected():
print("joystick not connected")
exit(1)
try:
robot = RobotController(SCENE_XML_PATH, ARM_XML_PATH, controller)
robot.run_loop()
finally:
controller.cleanup()