-
Notifications
You must be signed in to change notification settings - Fork 19
/
Copy pathdiffik.py
145 lines (113 loc) · 4.9 KB
/
diffik.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
import mujoco
import mujoco.viewer
import numpy as np
import time
# Integration timestep in seconds. This corresponds to the amount of time the joint
# velocities will be integrated for to obtain the desired joint positions.
integration_dt: float = 1.0
# Damping term for the pseudoinverse. This is used to prevent joint velocities from
# becoming too large when the Jacobian is close to singular.
damping: float = 1e-4
# Whether to enable gravity compensation.
gravity_compensation: bool = True
# Simulation timestep in seconds.
dt: float = 0.002
# Maximum allowable joint velocity in rad/s. Set to 0 to disable.
max_angvel = 0.0
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("universal_robots_ur5e/scene.xml")
data = mujoco.MjData(model)
# Override the simulation timestep.
model.opt.timestep = dt
# End-effector site we wish to control, in this case a site attached to the last
# link (wrist_3_link) of the robot.
site_id = model.site("attachment_site").id
# Name of bodies we wish to apply gravity compensation to.
body_names = [
"shoulder_link",
"upper_arm_link",
"forearm_link",
"wrist_1_link",
"wrist_2_link",
"wrist_3_link",
]
body_ids = [model.body(name).id for name in body_names]
if gravity_compensation:
model.body_gravcomp[body_ids] = 1.0
# Get the dof and actuator ids for the joints we wish to control.
joint_names = [
"shoulder_pan",
"shoulder_lift",
"elbow",
"wrist_1",
"wrist_2",
"wrist_3",
]
dof_ids = np.array([model.joint(name).id for name in joint_names])
# Note that actuator names are the same as joint names in this case.
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_id = model.key("home").id
# Mocap body we will control with our mouse.
mocap_id = model.body("target").mocapid[0]
# Pre-allocate numpy arrays.
jac = np.zeros((6, model.nv))
diag = damping * np.eye(6)
error = np.zeros(6)
error_pos = error[:3]
error_ori = error[3:]
site_quat = np.zeros(4)
site_quat_conj = np.zeros(4)
error_quat = np.zeros(4)
# Define a trajectory for the end-effector site to follow.
def circle(t: float, r: float, h: float, k: float, f: float) -> np.ndarray:
"""Return the (x, y) coordinates of a circle with radius r centered at (h, k)
as a function of time t and frequency f."""
x = r * np.cos(2 * np.pi * f * t) + h
y = r * np.sin(2 * np.pi * f * t) + k
return np.array([x, y])
with mujoco.viewer.launch_passive(
model=model, data=data, show_left_ui=False, show_right_ui=False
) as viewer:
# Reset the simulation to the initial keyframe.
mujoco.mj_resetDataKeyframe(model, data, key_id)
# Initialize the camera view to that of the free camera.
mujoco.mjv_defaultFreeCamera(model, viewer.cam)
# Toggle site frame visualization.
viewer.opt.frame = mujoco.mjtFrame.mjFRAME_SITE
while viewer.is_running():
step_start = time.time()
# Set the target position of the end-effector site.
data.mocap_pos[mocap_id, 0:2] = circle(data.time, 0.1, 0.5, 0.0, 0.5)
# Position error.
error_pos[:] = data.mocap_pos[mocap_id] - data.site(site_id).xpos
# Orientation error.
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(error_ori, error_quat, 1.0)
# Get the Jacobian with respect to the end-effector site.
mujoco.mj_jacSite(model, data, jac[:3], jac[3:], site_id)
# Solve system of equations: J @ dq = error.
dq = jac.T @ np.linalg.solve(jac @ jac.T + diag, error)
# Scale down joint velocities if they exceed maximum.
if max_angvel > 0:
dq_abs_max = np.abs(dq).max()
if dq_abs_max > max_angvel:
dq *= max_angvel / dq_abs_max
# Integrate joint velocities to obtain joint positions.
q = data.qpos.copy()
mujoco.mj_integratePos(model, q, dq, integration_dt)
# Set the control signal.
np.clip(q, *model.jnt_range.T, out=q)
data.ctrl[actuator_ids] = q[dof_ids]
# Step the simulation.
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()