-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathkinematics_library.py
326 lines (285 loc) · 11.6 KB
/
kinematics_library.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
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
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
# import required modules
import numpy as np
def solve_quadratic_func(a, b, c):
"""
solve the quadratic function
:param a:
:param b:
:param c:
:return:
"""
delta = (b ** 2) - (4 * a * c)
if delta >= 0:
solution1 = (-b - np.sqrt(delta)) / (2 * a)
solution2 = (-b + np.sqrt(delta)) / (2 * a)
return(solution1, solution2)
else:
return(np.inf, np.inf)
def ball2hoop(pos, vel, hoop_pos, gravity):
"""
calculate the distance between the ball and the hoop
:param pos:
:param vel:
:param hoop_pos:
:param gravity:
:return:
"""
z_diff = hoop_pos[2] - pos[2]
z_vel = vel[2]
distance = np.inf
if z_diff > 0: # if hoop is above the ball
if z_vel <= 0: # if ball is going downward
xy_diff = hoop_pos - pos
distance = np.linalg.norm(xy_diff)
else:
t_max = -z_vel / gravity # if the ball can not reach the hoop level
z_max = pos[2] + z_vel * t_max + 0.5 * gravity * (t_max ** 2)
if z_max < hoop_pos[2]:
# return the distance between the initial ball pos and target
xy_diff = hoop_pos - pos
distance = np.linalg.norm(xy_diff)
# the following code encourages throwing from the bottom
# xy_max = pos[:2] + t_max * vel[:2]
# xy_diff = hoop_pos[:2] - xy_max
# distance = np.linalg.norm(xy_diff)
if distance == np.inf:
a = 0.5 * gravity
b = z_vel
c = -z_diff
t1, t2 = solve_quadratic_func(a, b, c)
xy_distance = []
for tmp_t in [t1, t2]:
if tmp_t >= 0:
xy_max = pos[:2] + tmp_t * vel[:2]
xy_diff = hoop_pos[:2] - xy_max
tmp_distance = np.linalg.norm(xy_diff)
xy_distance.append(tmp_distance)
distance = np.min(xy_distance)
return(distance)
class BallObj(object):
def __init__(self, pos, vel, env_obj):
"""
initialize the ball object
:param pos:
:param vel:
:param env_obj:
"""
self.pos = np.copy(np.asarray(pos, dtype=np.double))
self.vel = np.copy(np.asarray(vel, dtype=np.double))
self.gravity = env_obj.gravity
def update(self):
t = 0.001 # set the time interval between two updates
g = self.gravity # gravity acceleration
ground = 0.0 # the ground
ball_trajectory = []
while self.pos[2] > ground:
# we assume the only acceleration caused by gravity is along z axis
# so only z_dot changes
ball_trajectory.append(np.copy(self.pos)) # store the trajectory of the ball
# update position
self.pos[0] += self.vel[0] * t
self.pos[1] += self.vel[1] * t
self.pos[2] += self.vel[2] * t
self.vel[2] += g * t
return(np.asarray(ball_trajectory))
class RobotArm(object):
def __init__(self, link_lengthes, initial_angles, intial_angular_velocities, time_step=1e-3):
"""
create the robotic manipulator object
:param link_lengthes:
:param initial_angles:
:param intial_angular_velocities:
:param time_step:
"""
self.link_lengthes = np.asarray(link_lengthes)
self.num_joints = len(initial_angles)
self.joint_relative_locations = np.asarray([[0, 0, self.link_lengthes[0], 1],
[0, 0, self.link_lengthes[1], 1],
[0, self.link_lengthes[2], 0, 1],
[0, self.link_lengthes[3], 0, 1],
[0, self.link_lengthes[4], 0, 1],
[self.link_lengthes[5], 0, 0, 1],
[self.link_lengthes[6], 0, 0, 1]
])
self.rotation_axises = ["z", "x", "x", "z", "y", "y"]
self.initial_joint_angles = np.asarray(initial_angles, dtype=np.double)
self.joint_angles = np.zeros(len(initial_angles), dtype=np.double)
# self.joint_angles = np.asarray(initial_angles, dtype=np.double)
self.angular_velocities = np.asarray(intial_angular_velocities, dtype=np.double)
self.release = 0
self.state = np.concatenate((self.joint_angles, self.angular_velocities))
self.state_dimension = self.state.shape[0]
self.time = 0
self.time_step = time_step
self.joint_angle_limit = 2 * np.pi
self.joint_vel_limit = 2 * np.pi
# a list of homogeneous transformation matrix functions
def r10(q, idx=0):
initial_q = self.initial_joint_angles[idx]
l = self.link_lengthes[idx]
q += initial_q
hm = np.asarray([[np.cos(q), -np.sin(q), 0, 0],
[np.sin(q), np.cos(q), 0, 0],
[0, 0, 1, l],
[0, 0, 0, 1]])
return (hm)
def r21(q, idx=1):
initial_q = self.initial_joint_angles[idx]
l = self.link_lengthes[idx]
q += initial_q
hm = np.asarray([[1, 0, 0, 0],
[0, np.cos(q), -np.sin(q), 0],
[0, np.sin(q), np.cos(q), l],
[0, 0, 0, 1]])
return (hm)
def r32(q, idx=2):
initial_q = self.initial_joint_angles[idx]
l = self.link_lengthes[idx]
q += initial_q
hm = np.asarray([[1, 0, 0, 0],
[0, np.cos(q), -np.sin(q), l],
[0, np.sin(q), np.cos(q), 0],
[0, 0, 0, 1]])
return (hm)
def r43(q, idx=3):
initial_q = self.initial_joint_angles[idx]
l = self.link_lengthes[idx]
q += initial_q
hm = np.asarray([[np.cos(q), -np.sin(q), 0, 0],
[np.sin(q), np.cos(q), 0, l],
[0, 0, 1, 0],
[0, 0, 0, 1]])
return (hm)
def r54(q, idx=4):
initial_q = self.initial_joint_angles[idx]
l = self.link_lengthes[idx]
q += initial_q
hm = np.asarray([[np.cos(q), 0, -np.sin(q), 0],
[0, 1, 0, l],
[np.sin(q), 0, np.cos(q), 0],
[0, 0, 0, 1]])
return (hm)
def r65(q, idx=5):
initial_q = self.initial_joint_angles[idx]
l = self.link_lengthes[idx]
q += initial_q
hm = np.asarray([[np.cos(q), 0, -np.sin(q), l],
[0, 1, 0, 0],
[np.sin(q), 0, np.cos(q), 0],
[0, 0, 0, 1]])
return (hm)
self.ht_list = [r10, r21, r32, r43, r54, r65]
self.joint_abs_locations = self.loc_joints(qs=[0] * len(self.rotation_axises))
@staticmethod
def _simple_forward_kinematics(qs, new_x, transform_list):
"""
routine for calculating the absolute location
:param new_x:
:param transform_list:
:param qs:
:return:
"""
for hm, q in zip(transform_list, qs):
tmp_x = np.dot(hm(q), new_x)
new_x = tmp_x
return(new_x)
def forward_kinematics(self, qs, x, reference_frame=6):
"""
convert the relative loctions in the joint frame into the world frame
:param qs: joint angles
:param x: location in the joint frame
:param reference_frame: frame in which the x is specified
:return:
"""
transform_list = self.ht_list[:reference_frame]
transform_list = transform_list[::-1]
qs = qs[:reference_frame]
qs = qs[::-1]
new_x = self._simple_forward_kinematics(qs, x, transform_list)
return(new_x)
def loc_joints(self, qs=None):
"""
if qs is not specified, calculate the absolute positions of the joints under the current configurations
:param qs:
:return:
"""
if qs is None: # if joint angles were not specified, use the current joint angles
qs = self.joint_angles
joint_abs_locations = []
for idx, rel_loc in enumerate(self.joint_relative_locations):
tmp_loc = self.forward_kinematics(qs, rel_loc, reference_frame=idx)
joint_abs_locations.append(tmp_loc)
self.joint_abs_locations = np.asarray(joint_abs_locations)
return (self.joint_abs_locations)
def configure_robots(self, qs):
"""
configure the robot to specified rotation angles
:param qs: specify the angles of each joints
:return:
"""
self.joint_angles = qs
self.joint_abs_locations = self.loc_joints(self.joint_angles)
def _update_angular_velocities(self, movement_action):
"""
update the angular velocities and hold/release state
:param movement_action:
:return:
"""
self.angular_velocities += movement_action * self.time_step
if np.any(np.abs(self.angular_velocities) > self.joint_vel_limit):
self.release = 1
def _update_joint_angles(self):
"""
update the joint angles
:return:
"""
self.joint_angles += self.angular_velocities * self.time_step
if np.any(np.abs(self.joint_angles) > self.joint_angle_limit):
self.release = 1
def update(self, movement_action, release_action):
"""
update the robot to the next time step
update the robot's joint angles based on the velocity from previous time step and \
angular velocity based on current action (specified as acceleration)
:param movement_action: acceleration at each joint and whether to release the ball
:param release_action:
:return:
"""
self._update_joint_angles()
self._update_angular_velocities(movement_action)
self.release = release_action
# update the state
self.state[:self.num_joints] = self.joint_angles
self.state[self.num_joints:] = self.angular_velocities
self.time += self.time_step
def _jacobian_matrix(self, x, qs=None, reference_frame=6, delta=1e-10):
"""
calculate the value of the Current Jacobian matrix values
:return:
"""
if qs is None:
qs = self.joint_angles
new_qs = qs[:reference_frame]
new_qs = new_qs[::-1]
transform_list = self.ht_list[:reference_frame]
transform_list = transform_list[::-1]
new_x = self._simple_forward_kinematics(new_qs, x, transform_list)
jacobian = np.zeros((4, self.num_joints))
for i in range(self.num_joints):
delta_qs = np.zeros(len(transform_list))
delta_qs[i] = delta
tmp_qs = new_qs + delta_qs
tmp_x = self._simple_forward_kinematics(tmp_qs, x, transform_list)
tmp_jacobian = (tmp_x - new_x) / delta
jacobian[:, self.num_joints-i-1] = tmp_jacobian
return(jacobian)
def cal_ee_speed(self):
"""
calculate the speed of the end effector in the world frame
:return:
"""
# pdb.set_trace()
x = self.joint_relative_locations[-1]
jacobian = self._jacobian_matrix(x)
v_dot = np.dot(jacobian, self.angular_velocities)
return(v_dot)