3 @file: manipulator_configuration.py
4 @brief: This module provides a class representing the configuration of a manipulator in the jointspace including some methods.
5 @author: Nima Ramezani Taghiabadi
7 Faculty of Engineering and Information Technology
8 University of Technology Sydney (UTS)
9 Broadway, Ultimo, NSW 2007, Australia
13 Email(1): Nima.RamezaniTaghiabadi@student.uts.edu.au
14 Email(2): nima.ramezani@gmail.com
15 Email(3): nima_ramezani@yahoo.com
16 Email(4): ramezanitn@alum.sharif.edu
18 Last Revision: 11 August 2015
21 Major change from previous version:
22 1- methods set_joint_bounds() added to class Manipulator_Configuration()
23 2- functions q_to_qstar() and qstar_to_q() renamed to mapto_virtual() and mapfrom_virtual() respectively
24 3- property qstar renamed to qvr
29 import numpy
as np, math, random, general_python
as genpy, copy
31 from math_tools
import general_math
as gen
34 from math_tools.discrete
import discrete
47 def __init__(self, njoint = 0, DOF= 0, joint_mapping = 'NM'):
58 self.
limited = [
True for i
in range(0, DOF)]
72 self.
ql = [-math.pi
for i
in range(0, njoint)]
75 self.
qh = [math.pi
for i
in range(0, njoint)]
83 self.
free = [
True for i
in range(0, njoint)]
100 self.
q = 0.5*(settings.qh + settings.ql)
107 If you have a method in class A and you intended to have a method with the same name in a class B inherited from A,
108 you should not call this method in class A, so I created a function set_configuration() which is called by set_config()
112 self.update_virtual_jacobian_multipliers()
118 self.config_settings.ql = copy.copy(ql)
119 self.config_settings.qh = copy.copy(qh)
126 s += vecmat.vector_to_str((180.0/math.pi)*self.
q, format=
"%.2f")
130 s +=
" all in range)"
136 def bring_revolute_joints_to_standard_range(self):
138 Change revolute joint values (joint angles) if they are out of standard range (-pi +pi) to their equivalent value in the standard range
140 for i
in range(0, self.config_settings.njoint):
141 if not self.config_settings.prismatic[i]:
142 self.
q[i] = trig.angle_standard_range( self.
q[i] )
149 if abs(qi - self.
ql[i]) < gen.epsilon:
151 if abs(qi - self.
qh[i]) < gen.epsilon:
154 return ((qi <= self.
qh[i])
and (qi >= self.
ql[i]))
161 for i
in range(0, self.config_settings.DOF):
162 if self.config_settings.limited[i]:
170 qq = np.zeros((self.config_settings.DOF))
171 for i
in range(self.config_settings.DOF):
172 if self.config_settings.limited[i]:
173 if self.config_settings.joint_handling[i] ==
'NM':
174 qq[i] = trig.angle_standard_range( qs[i] )
175 elif self.config_settings.joint_handling[i] ==
'LM':
176 qs[i] = trig.angle_standard_range( qs[i] )
177 qq[i] = (qs[i] - self.
jmc_b[i]) / self.
jmc_a[i]
178 elif self.config_settings.joint_handling[i] ==
'TM':
179 qq[i] = self.
jmc_f[i] * math.cos(qs[i]) + self.
jmc_g[i]
180 elif self.config_settings.joint_handling[i] ==
'TGM':
181 qq[i] = 2*math.atan(self.
jmc_f[i] * math.cos(qs[i]) + self.
jmc_g[i])
183 assert False, genpy.err_str(__name__, self.__class__.__name__,
'mapfrom_virtual', self.config_settings.joint_handling[i] +
" is not a valid value for joint_handling")
196 self.
qvr = np.zeros((self.config_settings.DOF))
198 for i
in range(self.config_settings.DOF):
199 if (
not self.config_settings.limited)
or (self.config_settings.joint_handling[i] ==
'NM'):
201 elif self.config_settings.joint_handling[i] ==
'LM':
202 assert self.
joint_in_range(i, qf[i]),
"Joint " + str(i) +
" = " + str(qf[i]) +
" is not in feasible range: (" + str(self.config_settings.ql[i]) +
',' + str(self.config_settings.qh[i]) +
')'
204 elif self.config_settings.joint_handling[i] ==
'TM':
206 ql = self.config_settings.ql[i]
207 qh = self.config_settings.qh[i]
208 print "i: ", i, "ql: ", ql," q: ", qf[i],"qh: ", qh, " g: ", self.jmc_g[i], " f: ", self.jmc_f[i], " c: ", (qf[i] - self.jmc_g[i]) / self.jmc_f[i]
210 assert self.
joint_in_range(i, qf[i]),
"Joint " + str(i) +
" = " + str(qf[i]) +
" is not in feasible range: (" + str(self.config_settings.ql[i]) +
',' + str(self.config_settings.qh[i]) +
')'
211 self.
qvr[i] = trig.arccos((qf[i] - self.
jmc_g[i]) / self.
jmc_f[i])
212 elif self.config_settings.joint_handling[i] ==
'TGM':
213 assert self.
joint_in_range(i, qf[i]),
"Joint " + str(i) +
" = " + str(qf[i]) +
" is not in feasible range: (" + str(self.config_settings.ql[i]) +
',' + str(self.config_settings.qh[i]) +
')'
214 self.
qvr[i] = trig.arccos((math.tan(0.5*qf[i]) - self.
jmc_g[i]) / self.
jmc_f[i])
216 print 'Wrong Joint Handling (Free Joint ' + str(i) +
'): ' + self.config_settings.joint_handling[i]
217 assert False, genpy.err_str(__name__, self.__class__.__name__,
'q_to_q', self.config_settings.joint_handling[i] +
" is not a valid value for joint_handling")
221 def update_virtual_jacobian_multipliers(self):
223 joint_limit_multipliers are coefficients of forward and inverse mapping to/from limited to/from unlimited jointspace.
224 i.e: jmc_a is the derivative of q to q_star
225 (Refer also to the comments of method: initialize in this class)
226 This method calculates and updates the values of one of the joint limit multipliers: "jmc_c" which is called "joint_limit_jacobian_multipliers"
227 Each column of the error jacobian is multiplied by the corresponding element of vector "jmc_c": Jstar = Je * diag(jmc_c)
228 Since the coefficient "jmc_c" for a joint, depends on the current value of that joint, it should be updated everytime "q" changes
229 The other multipliers: "jmc_a", "jmc_b", "jmc_f" and "jmc_g" do not depend on the joints, therefore do not need to be updated
231 func_name =
"update_virtual_jacobian_multipliers"
232 for i
in range(0,self.config_settings.DOF):
233 if self.config_settings.joint_handling[i] ==
'NM':
236 elif self.config_settings.joint_handling[i] ==
'LM':
239 elif self.config_settings.joint_handling[i] ==
'TM':
242 elif self.config_settings.joint_handling[i] ==
'TGM':
244 self.
jmc_c[i] = - 2.0 * math.sin(self.
qvr[i])/(self.
jmc_a[i]*(1 + t**2))
246 assert False, genpy.err_str(__name__, self.__class__.__name__,
'update_virtual_jacobian_multipliers', self.config_settings.joint_handling[i] +
" is not a valid value for joint_handling")
254 Everything regarding joint parameters needed to be done before running kinematic calculations
255 This function must be called whenever you change joint limits or change type of a joint.
257 func_name =
"initialize()"
259 self.config_settings.DOF = 0
261 for i
in range(0,self.config_settings.njoint):
263 if (
not self.config_settings.prismatic[i]):
264 self.config_settings.ql[i] = trig.angle_standard_range( self.config_settings.ql[i] )
265 self.config_settings.qh[i] = trig.angle_standard_range( self.config_settings.qh[i] )
267 if self.config_settings.free[i]:
268 self.config_settings.DOF += 1
275 for i in range(self.DOF):
276 if not self.config_settings.joint_handling[i] == 'No Mapping':
277 assert self.joints_in_range() #check only for joint i
281 The following code defines the joint limit multipliers
282 jmc_a and jmc_b are auxiliary coefficients which are used for conversion from "q" to "qvr" space
283 (Please refer to the joint limits section of the documentation associated with this code)
285 To make sure that all joints are in their desired range identified as: (self.ql, self.qh), the real jointspace is mapped into an unlimited jointspace.
287 "q " represent the real joint values which are in a limited jointpace
288 "qvr" represent the mapped joint values in unlimited jointspace
290 The following code creates and defines "Joint Limit Multipliers". These are coefficients for this jointspace mapping - (jmc: Jointspace Mapping Coefficients)
296 in trigonometric mapping:
299 qvr = arccos [ (q - g) / f ]
301 a, b, f and g are calculated in such a way that the real joint values which are in their feasible range are mapped into an equivalent qvr in an unlimited space (-pi, +pi)
303 "jmc_c" is multiplied by the columns of the error jacobian matrix
305 self.
jmc_a = np.zeros((self.config_settings.DOF))
306 self.
jmc_b = np.zeros((self.config_settings.DOF))
307 self.
jmc_c = np.zeros((self.config_settings.DOF))
308 self.
jmc_f = np.zeros((self.config_settings.DOF))
309 self.
jmc_g = np.zeros((self.config_settings.DOF))
314 for i
in range(0, self.config_settings.DOF):
315 if self.config_settings.joint_handling[i] ==
'NM':
316 self.
jmc_a[i] = 1.00000
317 self.
jmc_b[i] = 0.00000
318 elif self.config_settings.joint_handling[i] ==
'LM':
319 self.
jmc_a[i] = 2*math.pi/(self.
qh[i] - self.
ql[i])
320 self.
jmc_b[i] = math.pi * (self.
qh[i] + self.
ql[i])/(self.
ql[i] - self.
qh[i])
321 elif self.config_settings.joint_handling[i] ==
'TM':
322 self.
jmc_a[i] = 2.0/(self.
qh[i] - self.
ql[i])
323 self.
jmc_b[i] = (self.
qh[i] + self.
ql[i])/(self.
ql[i] - self.
qh[i])
324 elif self.config_settings.joint_handling[i] ==
'TGM':
325 th = math.tan(0.5*self.
qh[i])
326 tl = math.tan(0.5*self.
ql[i])
327 self.
jmc_a[i] = 2/(th - tl)
328 self.
jmc_b[i] = (tl + th)/(tl - th)
329 self.
jmc_f[i] = 0.5*(th - tl)
330 self.
jmc_g[i] = 0.5*(th + tl)
332 assert False, genpy.err_str(__name__, self.__class__.__name__,
'initialize', self.config_settings.joint_handling[i] +
" is not a valid value for joint_handling")
338 self.update_virtual_jacobian_multipliers()
341 def joint_correction_in_range(self, dq):
343 Return True if the joint configuration will be in the desired range after being added by the given "dq"
346 for i
in range(0,self.DOF):
347 in_range = in_range
and (self.
q[i] + dq[i] <= self.
qh[i])
and (self.
q[i] + dq[i] >= self.
ql[i])
360 permission =
not self.config_settings.joint_limits_respected
364 self.
q = self.free_config_inv(qd)
365 self.update_virtual_jacobian_multipliers()
368 assert False, genpy.err_str(__name__, self.__class__.__name__,
'set_config_virtual',
'The joint values computed from qvr are out of range while joint limits are respected.')
386 for i
in range(self.config_settings.DOF):
387 if not gen.equal(direction[i], 0.0):
388 if self.config_settings.limited[i]
and self.config_settings.joint_handling[i] ==
'NM':
389 a = (self.
ql[i] - self.
q[i])/direction[i]
390 b = (self.
qh[i] - self.
q[i])/direction[i]
391 etta_l.append(gen.sign_choice(a, b, direction[i]))
392 etta_h.append(gen.sign_choice(b, a, direction[i]))
394 a = delta_t*max_speed/direction[i]
395 etta_l.append(gen.sign_choice(-a, a, direction[i]))
396 etta_h.append(gen.sign_choice( a,-a, direction[i]))
398 if (etta_l == [])
or (etta_h == []):
401 return (max(etta_l), min(etta_h))
408 fc = np.zeros((self.config_settings.DOF))
410 for jj
in range(0,self.config_settings.njoint):
411 if self.config_settings.free[jj]:
417 def free_config_inv(self, qs):
419 puts the values of free joints in their proper place in the main config vector. Return the main config vector
423 for jj
in range(0,self.config_settings.njoint):
424 if self.config_settings.free[jj]:
438 assert len_qd == self.config_settings.DOF, genpy.err_str(__name__, self.__class__.__name__,
'set_config',
'Length of given config vector is '+str(len_qd)+
' which does not match the settings DOF('+str(self.config_settings.DOF)+
')')
439 permission =
not self.config_settings.joint_limits_respected
445 for jj
in range(0,self.config_settings.njoint):
446 if self.config_settings.free[jj]:
447 if self.config_settings.limited[j]
and (
not self.config_settings.prismatic[jj]):
448 self.
q[jj] = trig.angle_standard_range(qd[j])
454 self.update_virtual_jacobian_multipliers()
458 print "Warning from configuration.set_config(): Given joints are not in their feasible range"
459 for i
in range(self.config_settings.DOF):
462 print "Joint Number: ", i
463 print "Lower Limit :", self.
ql[i]
464 print "Desired Value:", qd[i]
465 print "Upper Limit :", self.
qh[i]
472 q_rnd = np.zeros(self.config_settings.DOF)
474 for j
in range(0, self.config_settings.DOF):
475 q_rnd[j] = self.
ql[j] + random.random()*(self.
qh[j] - self.
ql[j])
491 def grid_config(self, config_number, number_of_intervals):
492 q_grd = np.zeros(self.config_settings.DOF)
493 A = discrete.number_in_base(N = config_number, base = number_of_intervals, n_digits = self.config_settings.DOF)
495 for j
in range(0, self.config_settings.DOF):
496 q_grd[j] = self.
ql[j] + (2*A[j] + 1)*(self.
qh[j] - self.
ql[j])/(2*number_of_intervals)
501 def objective_function_gradient(self, k = 1.0):
502 dof = self.config_settings.DOF
506 qs = trig.angles_standard_range(self.
qvr)
507 for i
in range(0, self.config_settings.DOF):
508 if self.config_settings.joint_handling[i] ==
'TM':
509 u[i] = - k*qs[i]/(dof*((2*math.pi)**2))
510 elif self.config_settings.joint_handling[i] ==
'NM':
511 if self.config_settings.limited[i]:
512 mean = 0.5*(self.
qh[i] + self.
ql[i])
513 u[i] = - k*(qs[i] - mean)/(dof*((self.
qh[i] - self.
ql[i])**2))
516 assert False, genpy.err_str(__name__, self.__class__.__name__,
'objective_function_gradient', self.config_settings.joint_handling[i] +
" is not a valid value for joint_handling")
519 def objective_function_value(self, k = 1.0):
520 dof = self.config_settings.DOF
521 qs = trig.angles_standard_range(self.
qvr)
525 for i
in range(0, self.config_settings.DOF):
527 if self.config_settings.joint_handling[i] ==
'TM':
528 f += - k*qs[i]/(dof*((2*math.pi)**2))
529 elif self.config_settings.joint_handling[i] ==
'NM':
530 if self.config_settings.limited[i]:
531 mean = 0.5*(self.
qh[i] + self.
ql[i])
532 df = k*((qs[i] - mean)**2)/(dof*((self.
qh[i] - self.
ql[i])**2))
536 assert False, genpy.err_str(__name__, self.__class__.__name__,
'objective_function_value', self.config_settings.joint_handling[i] +
" is not a valid value for joint_handling")
def free_config
This function picks and returns the values of free joints among all the joints in the given argument ...
def set_config
Use this function to set the joint configuration to the given desired values.
qh
A vector of real numbers representing higher bounds for the joint configuration The array is of lengt...
joint_handling
An array of string containing the specific mapping function for each joint The array is of length DOF...
DOF
An integer: Is a short form of Degrees of Freedom and represents the number of free joints...
def config_str
Use this function to see the current configuration of the manipulator.
This class represents a model of the joint configuration of a chained-link manipulator.
ql
A vector of real numbers representing lower bounds for the joint configuration.
joint_label
An array of string containing indivifual labels or names the for each joint.
def __init__
The Class Constructor:
def set_joint_bounds
Only use this function to set the joint limits.
def joint_in_range
This function is used to check if a given value for specific joint is in its feasibility range...
joint_limits_respected
A boolean specify if the joint limits must be respected when new joint values are being set...
def joint_stepsize_interval
The correction of joints is always restricted by joint limits and maximum feasible joint speed If you...
limited
An array of booleans specifying which joints are limited.
def mapfrom_virtual
This function converts the free joint values from unlimited virtual joint-space into their actual val...
def random_config
Generates a random joint configuration in the defined feasible range in the settings.
def grid_config
If The feasible range of each joint is divided into a number of equal intervals, a joint-space lattic...
def mapto_virtual
This function converts the current actual free joint values (picked from property q) into their equiv...
def __init__
The Class Constructor:
def joints_in_range
This function is used to check if all values of the given joint array are in their feasibility range...
prismatic
A list of booleans representing the type of the corresponding joint number.
def set_config_virtual
Sets the configuration given values of the free joints in the virtual joint-space.
This class contains joint properties of a chained-link manipulator.