36
36
EPSILON = 0.001
37
37
38
38
39
+ def wrap_to_interval (angles , lower = - pi ):
40
+ """
41
+ Wraps an angle into a semi-closed interval of width 2*pi.
42
+
43
+ By default, this interval is `[-pi, pi)`. However, the lower bound of the
44
+ interval can be specified to wrap to the interval `[lower, lower + 2*pi)`.
45
+
46
+ If `lower` is an array the same length as angles, the bounds will be
47
+ applied element-wise to each angle in `angles`.
48
+
49
+ See: http://stackoverflow.com/a/32266181
50
+
51
+ @param angles an angle or 1D array of angles to wrap
52
+ @type angles float or numpy.array
53
+ @param lower optional lower bound on wrapping interval
54
+ @type lower float or numpy.array
55
+ """
56
+ return (angles - lower ) % (2 * pi ) + lower
57
+
58
+
39
59
class TSR (object ):
40
60
""" A Task-Space-Region (TSR) represents a motion constraint. """
41
61
def __init__ (self , T0_w = None , Tw_e = None , Bw = None ,
@@ -46,21 +66,25 @@ def __init__(self, T0_w=None, Tw_e=None, Bw=None,
46
66
Tw_e = numpy .eye (4 )
47
67
if Bw is None :
48
68
Bw = numpy .zeros ((6 , 2 ))
49
- if numpy .any (Bw [0 :3 , 0 ] > Bw [0 :3 , 1 ]):
50
- raise ValueError ('Bw translation bounds must be [min, max]' , Bw )
51
69
52
- self .T0_w = T0_w
53
- self .Tw_e = Tw_e
54
- self .Bw = Bw
70
+ self .T0_w = numpy .array (T0_w )
71
+ self .Tw_e = numpy .array (Tw_e )
72
+ self .Bw = numpy .array (Bw )
73
+
74
+ if numpy .any (self .Bw [0 :3 , 0 ] > self .Bw [0 :3 , 1 ]):
75
+ raise ValueError ('Bw translation bounds must be [min, max]' , Bw )
55
76
56
77
# We will now create a continuous version of the bound to maintain:
57
78
# 1. Bw[i,1] > Bw[i,0] which is necessary for LBFGS-B
58
79
# 2. signed rotations, necessary for expressiveness
59
- Bw_cont = numpy .copy (Bw )
60
- Bw_cont [3 :6 , :] = (Bw_cont [3 :6 , :] + pi ) % (2 * pi ) - pi
61
- for rot_idx in range (3 , 6 ):
62
- if Bw_cont [rot_idx , 0 ] > Bw_cont [rot_idx , 1 ] + EPSILON :
63
- Bw_cont [rot_idx , 1 ] += 2 * pi
80
+ Bw_cont = numpy .copy (self .Bw )
81
+
82
+ Bw_interval = Bw_cont [3 :6 , 1 ] - Bw_cont [3 :6 , 0 ]
83
+ Bw_interval = numpy .minimum (Bw_interval , 2 * pi )
84
+
85
+ Bw_cont [3 :6 , 0 ] = wrap_to_interval (Bw_cont [3 :6 , 0 ])
86
+ Bw_cont [3 :6 , 1 ] = Bw_cont [3 :6 , 0 ] + Bw_interval
87
+
64
88
self ._Bw_cont = Bw_cont
65
89
66
90
if manip is None :
@@ -175,8 +199,8 @@ def rpy_within_bounds(rpy, Bw):
175
199
@param Bw bounds on rpy
176
200
@return check a (3,) vector of True if within and False if outside
177
201
"""
178
- # Unwrap rpy to [-pi, pi] .
179
- rpy = ( numpy . array ( rpy ) + pi ) % ( 2 * pi ) - pi
202
+ # Unwrap rpy to Bw_cont .
203
+ rpy = wrap_to_interval ( rpy , lower = Bw [:, 0 ])
180
204
181
205
# Check bounds condition on RPY component.
182
206
rpycheck = [False ] * 3
@@ -280,7 +304,7 @@ def to_xyzrpy(self, trans):
280
304
trans ,
281
305
numpy .linalg .inv (self .Tw_e )])
282
306
xyz , rot = Tw [0 :3 , 3 ], Tw [0 :3 , 0 :3 ]
283
- rpycheck , rpy = TSR .rot_within_rpy_bounds (rot , self .Bw )
307
+ rpycheck , rpy = TSR .rot_within_rpy_bounds (rot , self ._Bw_cont )
284
308
if not all (rpycheck ):
285
309
rpy = TSR .rot_to_rpy (rot )
286
310
return numpy .hstack ((xyz , rpy ))
@@ -295,7 +319,7 @@ def is_valid(self, xyzrpy, ignoreNAN=False):
295
319
@return a 6x1 vector of True if bound is valid and False if not
296
320
"""
297
321
# Extract XYZ and RPY components of input and TSR.
298
- Bw_xyz , Bw_rpy = self .Bw [0 :3 , :], self .Bw [3 :6 , :]
322
+ Bw_xyz , Bw_rpy = self ._Bw_cont [0 :3 , :], self ._Bw_cont [3 :6 , :]
299
323
xyz , rpy = xyzrpy [0 :3 ], xyzrpy [3 :6 ]
300
324
301
325
# Check bounds condition on XYZ component.
@@ -320,7 +344,7 @@ def contains(self, trans):
320
344
@return a 6x1 vector of True if bound is valid and False if not
321
345
"""
322
346
# Extract XYZ and rot components of input and TSR.
323
- Bw_xyz , Bw_rpy = self .Bw [0 :3 , :], self .Bw [3 :6 , :]
347
+ Bw_xyz , Bw_rpy = self ._Bw_cont [0 :3 , :], self ._Bw_cont [3 :6 , :]
324
348
xyz , rot = trans [0 :3 , :], trans [0 :3 , 0 :3 ]
325
349
# Check bounds condition on XYZ component.
326
350
xyzcheck = TSR .xyz_within_bounds (xyz , Bw_xyz )
@@ -374,7 +398,7 @@ def sample_xyzrpy(self, xyzrpy=NANBW):
374
398
if numpy .isnan (x ) else x
375
399
for i , x in enumerate (xyzrpy )])
376
400
# Unwrap rpy to [-pi, pi]
377
- Bw_sample [3 :6 ] = (Bw_sample [3 :6 ] + pi ) % ( 2 * pi ) - pi
401
+ Bw_sample [3 :6 ] = wrap_to_interval (Bw_sample [3 :6 ])
378
402
return Bw_sample
379
403
380
404
def sample (self , xyzrpy = NANBW ):
0 commit comments