-
Notifications
You must be signed in to change notification settings - Fork 19
/
Copy pathopspace.py
149 lines (118 loc) · 4.98 KB
/
opspace.py
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
import mujoco
import mujoco.viewer
import numpy as np
import time
# Cartesian impedance control gains.
impedance_pos = np.asarray([100.0, 100.0, 100.0]) # [N/m]
impedance_ori = np.asarray([50.0, 50.0, 50.0]) # [Nm/rad]
# Joint impedance control gains.
Kp_null = np.asarray([75.0, 75.0, 50.0, 50.0, 40.0, 25.0, 25.0])
# Damping ratio for both Cartesian and joint impedance control.
damping_ratio = 1.0
# Gains for the twist computation. These should be between 0 and 1. 0 means no
# movement, 1 means move the end-effector to the target in one integration step.
Kpos: float = 0.95
# Gain for the orientation component of the twist computation. This should be
# between 0 and 1. 0 means no movement, 1 means move the end-effector to the target
# orientation in one integration step.
Kori: float = 0.95
# Integration timestep in seconds.
integration_dt: float = 1.0
# Whether to enable gravity compensation.
gravity_compensation: bool = True
# Simulation timestep in seconds.
dt: float = 0.002
def main() -> None:
assert mujoco.__version__ >= "3.1.0", "Please upgrade to mujoco 3.1.0 or later."
# Load the model and data.
model = mujoco.MjModel.from_xml_path("kuka_iiwa_14/scene.xml")
data = mujoco.MjData(model)
model.opt.timestep = dt
# Compute damping and stiffness matrices.
damping_pos = damping_ratio * 2 * np.sqrt(impedance_pos)
damping_ori = damping_ratio * 2 * np.sqrt(impedance_ori)
Kp = np.concatenate([impedance_pos, impedance_ori], axis=0)
Kd = np.concatenate([damping_pos, damping_ori], axis=0)
Kd_null = damping_ratio * 2 * np.sqrt(Kp_null)
# End-effector site we wish to control.
site_name = "attachment_site"
site_id = model.site(site_name).id
# Get the dof and actuator ids for the joints we wish to control. These are copied
# from the XML file. Feel free to comment out some joints to see the effect on
# the controller.
joint_names = [
"joint1",
"joint2",
"joint3",
"joint4",
"joint5",
"joint6",
"joint7",
]
dof_ids = np.array([model.joint(name).id for name in joint_names])
actuator_ids = np.array([model.actuator(name).id for name in joint_names])
# Initial joint configuration saved as a keyframe in the XML file.
key_name = "home"
key_id = model.key(key_name).id
q0 = model.key(key_name).qpos
# Mocap body we will control with our mouse.
mocap_name = "target"
mocap_id = model.body(mocap_name).mocapid[0]
# Pre-allocate numpy arrays.
jac = np.zeros((6, model.nv))
twist = np.zeros(6)
site_quat = np.zeros(4)
site_quat_conj = np.zeros(4)
error_quat = np.zeros(4)
M_inv = np.zeros((model.nv, model.nv))
Mx = np.zeros((6, 6))
with mujoco.viewer.launch_passive(
model=model,
data=data,
show_left_ui=False,
show_right_ui=False,
) as viewer:
# Reset the simulation.
mujoco.mj_resetDataKeyframe(model, data, key_id)
# Reset the free camera.
mujoco.mjv_defaultFreeCamera(model, viewer.cam)
# Enable site frame visualization.
viewer.opt.frame = mujoco.mjtFrame.mjFRAME_SITE
while viewer.is_running():
step_start = time.time()
# Spatial velocity (aka twist).
dx = data.mocap_pos[mocap_id] - data.site(site_id).xpos
twist[:3] = Kpos * dx / integration_dt
mujoco.mju_mat2Quat(site_quat, data.site(site_id).xmat)
mujoco.mju_negQuat(site_quat_conj, site_quat)
mujoco.mju_mulQuat(error_quat, data.mocap_quat[mocap_id], site_quat_conj)
mujoco.mju_quat2Vel(twist[3:], error_quat, 1.0)
twist[3:] *= Kori / integration_dt
# Jacobian.
mujoco.mj_jacSite(model, data, jac[:3], jac[3:], site_id)
# Compute the task-space inertia matrix.
mujoco.mj_solveM(model, data, M_inv, np.eye(model.nv))
Mx_inv = jac @ M_inv @ jac.T
if abs(np.linalg.det(Mx_inv)) >= 1e-2:
Mx = np.linalg.inv(Mx_inv)
else:
Mx = np.linalg.pinv(Mx_inv, rcond=1e-2)
# Compute generalized forces.
tau = jac.T @ Mx @ (Kp * twist - Kd * (jac @ data.qvel[dof_ids]))
# Add joint task in nullspace.
Jbar = M_inv @ jac.T @ Mx
ddq = Kp_null * (q0 - data.qpos[dof_ids]) - Kd_null * data.qvel[dof_ids]
tau += (np.eye(model.nv) - jac.T @ Jbar.T) @ ddq
# Add gravity compensation.
if gravity_compensation:
tau += data.qfrc_bias[dof_ids]
# Set the control signal and step the simulation.
np.clip(tau, *model.actuator_ctrlrange.T, out=tau)
data.ctrl[actuator_ids] = tau[actuator_ids]
mujoco.mj_step(model, data)
viewer.sync()
time_until_next_step = dt - (time.time() - step_start)
if time_until_next_step > 0:
time.sleep(time_until_next_step)
if __name__ == "__main__":
main()