@@ -205,6 +205,7 @@ def NormalizeVector(vec):
205
205
@returns numpy.array result: A vector of the same size, where the
206
206
L2 norm of the elements equals 1.
207
207
"""
208
+ numpy .seterr (divide = 'ignore' , invalid = 'ignore' )
208
209
magnitude = numpy .sqrt (vec .dot (vec ))
209
210
vec2 = (vec / magnitude )
210
211
return numpy .nan_to_num (vec2 ) # convert NaN to zero
@@ -726,6 +727,25 @@ def GeodesicError(t1, t2):
726
727
return numpy .hstack ((trans , angle ))
727
728
728
729
730
+ def AngleBetweenQuaternions (quat1 , quat2 ):
731
+ """
732
+ Compute the angle between two quaternions.
733
+ From 0 to 2pi.
734
+ """
735
+ theta = numpy .arccos ( 2.0 * ( quat1 .dot (quat2 ) )** 2 - 1.0 )
736
+ return theta
737
+
738
+
739
+ def AngleBetweenRotations (rot1 , rot2 ):
740
+ """
741
+ Compute the angle between two 3x3 rotation matrices.
742
+ From 0 to 2pi.
743
+ """
744
+ quat1 = openravepy .quatFromRotationMatrix (rot1 )
745
+ quat2 = openravepy .quatFromRotationMatrix (rot2 )
746
+ return AngleBetweenQuaternions (quat1 , quat2 )
747
+
748
+
729
749
def GeodesicDistance (t1 , t2 , r = 1.0 ):
730
750
'''
731
751
Computes the geodesic distance between two transforms
@@ -739,12 +759,28 @@ def GeodesicDistance(t1, t2, r=1.0):
739
759
return numpy .linalg .norm (error )
740
760
741
761
742
- def GetGeodesicDistanceBetweenTransforms (T0 , T1 ):
762
+ def GetGeodesicDistanceBetweenTransforms (T0 , T1 , r = 1.0 ):
743
763
"""
744
764
Wrapper, to match GetGeodesicDistanceBetweenQuaternions()
765
+
766
+ Calculate the geodesic distance between two transforms, being
767
+ gd = norm( relative translation + r * axis-angle error )
768
+
769
+ @param t1 current transform
770
+ @param t2 goal transform
771
+ @param r in units of meters/radians converts radians to meters
745
772
"""
773
+ return GeodesicDistance (T0 , T1 , r )
746
774
747
- return GeodesicDistance (T0 , T1 , r = 1.0 )
775
+
776
+ def GetEuclideanDistanceBetweenPoints (p0 , p1 ):
777
+ """
778
+ Calculate the Euclidean distance (L2 norm) between two vectors.
779
+ """
780
+ sum = 0.0
781
+ for i in xrange (len (p0 )):
782
+ sum = sum + (p0 [i ]- p1 [i ])* (p0 [i ]- p1 [i ])
783
+ return numpy .sqrt (sum )
748
784
749
785
750
786
def GetEuclideanDistanceBetweenTransforms (T0 , T1 ):
@@ -755,7 +791,7 @@ def GetEuclideanDistanceBetweenTransforms(T0, T1):
755
791
"""
756
792
p0 = T0 [0 :3 ,3 ] # Get the x,y,z translation from the 4x4 matrix
757
793
p1 = T1 [0 :3 ,3 ]
758
- return numpy . sqrt ( numpy . sum (( p0 - p1 ) ** 2 ) )
794
+ return GetEuclideanDistanceBetweenPoints ( p0 , p1 )
759
795
760
796
761
797
def GetMinDistanceBetweenTransformAndWorkspaceTraj (T , traj , dt = 0.01 ):
@@ -774,14 +810,15 @@ def GetMinDistanceBetweenTransformAndWorkspaceTraj(T, traj, dt=0.01):
774
810
if not IsTimedTrajectory (traj ):
775
811
raise ValueError ("Trajectory must have timing information." )
776
812
777
- if not IsTrajectoryTypeIkParameterization (traj ):
813
+ if not IsTrajectoryTypeIkParameterizationTransform6D (traj ):
778
814
raise ValueError ("Trajectory is not a workspace trajectory, it "
779
815
"must have configuration specification of "
780
816
"openravepy.IkParameterizationType.Transform6D" )
781
817
782
818
def _GetError (t ):
783
819
T_curr = openravepy .matrixFromPose (traj .Sample (t )[0 :7 ])
784
820
error = GetEuclideanDistanceBetweenTransforms (T , T_curr )
821
+ return error
785
822
786
823
min_dist = numpy .inf
787
824
t_loc = 0.0
@@ -995,10 +1032,37 @@ def IsJointSpaceTrajectory(traj):
995
1032
return False
996
1033
997
1034
998
- def IsTrajectoryTypeIkParameterization (traj ):
1035
+ def IsWorkspaceTrajectory (traj ):
999
1036
"""
1000
1037
Check if trajectory is a workspace trajectory.
1001
1038
1039
+ @param openravepy.Trajectory traj: A path or trajectory.
1040
+ @return bool result: Returns True or False.
1041
+ """
1042
+ return IsTrajectoryTypeIkParameterizationTransform6D (traj )
1043
+
1044
+
1045
+ def IsTrajectoryTypeIkParameterization (traj ):
1046
+ """
1047
+ Check if trajectory has a configuration specification
1048
+ of type IkParameterization:
1049
+ Transform6d
1050
+ Rotation3D
1051
+ Translation3D
1052
+ Direction3D
1053
+ Ray4D
1054
+ Lookat3D
1055
+ TranslationDirection5D
1056
+ TranslationXY2D
1057
+ TranslationXYOrientation3D
1058
+ TranslationLocalGlobal6D
1059
+ TranslationXAxisAngle4D
1060
+ TranslationYAxisAngle4D
1061
+ TranslationZAxisAngle4D
1062
+ TranslationXAxisAngleZNorm4D
1063
+ TranslationYAxisAngleXNorm4D
1064
+ TranslationZAxisAngleYNorm4D
1065
+
1002
1066
@param openravepy.Trajectory traj: A path or trajectory.
1003
1067
@return bool result: Returns True or False.
1004
1068
"""
@@ -1010,6 +1074,43 @@ def IsTrajectoryTypeIkParameterization(traj):
1010
1074
return False
1011
1075
1012
1076
1077
+ def IsTrajectoryTypeIkParameterizationTransform6D (traj ):
1078
+ """
1079
+ Check if trajectory has a configuration specification
1080
+ of type IkParameterization.Transform6D
1081
+
1082
+ @param openravepy.Trajectory traj: A path or trajectory.
1083
+ @return bool result: Returns True or False.
1084
+ """
1085
+ try :
1086
+ IKP_type = openravepy .IkParameterizationType .Transform6D
1087
+ # The IKP type must be passed as a number
1088
+ group_name = "ikparam_values {0}" .format (int (IKP_type ))
1089
+ if traj .GetConfigurationSpecification ().GetGroupFromName (group_name ):
1090
+ return True
1091
+ except openravepy .openrave_exception :
1092
+ pass
1093
+ return False
1094
+
1095
+
1096
+ def IsTrajectoryTypeIkParameterizationTranslationDirection5D (traj ):
1097
+ """
1098
+ Check if trajectory has a configuration specification
1099
+ of type IkParameterization.TranslationDirection5D
1100
+
1101
+ @param openravepy.Trajectory traj: A path or trajectory.
1102
+ @return bool result: Returns True or False.
1103
+ """
1104
+ try :
1105
+ IKP_type = openravepy .IkParameterizationType .TranslationDirection5D
1106
+ group_name = "ikparam_values {0}" .format (IKP_type )
1107
+ if traj .GetConfigurationSpecification ().GetGroupFromName (group_name ):
1108
+ return True
1109
+ except openravepy .openrave_exception :
1110
+ pass
1111
+ return False
1112
+
1113
+
1013
1114
def ComputeEnabledAABB (kinbody ):
1014
1115
"""
1015
1116
Returns the AABB of the enabled links of a KinBody.
@@ -1102,6 +1203,82 @@ def ComputeUnitTiming(robot, traj, env=None):
1102
1203
return new_traj
1103
1204
1104
1205
1206
+ def ComputeGeodesicUnitTiming (traj , env = None , alpha = 1.0 ):
1207
+ """
1208
+ Compute the geodesic unit velocity timing of a workspace path or
1209
+ trajectory, also called a path length parameterization.
1210
+
1211
+ The path length is calculated as the sum of all segment lengths,
1212
+ where each segment length = norm( delta_translation^2 +
1213
+ alpha^2*delta_orientation^2 )
1214
+
1215
+ Note: Currently only linear velocity interpolation is supported,
1216
+ however OpenRAVE does allow you to specify quadratic
1217
+ interpolation.
1218
+
1219
+ @param traj: Workspace path or trajectory
1220
+ @param env: Environment to create the output trajectory in, defaults
1221
+ to the same environment as the input trajectory.
1222
+ @param alpha: Weighting for delta orientation.
1223
+ @returns: A workspace trajectory with unit velocity timing.
1224
+ """
1225
+ if not IsTrajectoryTypeIkParameterizationTransform6D (traj ):
1226
+ raise ValueError ("Trajectory is not a workspace trajectory, it "
1227
+ "must have configuration specification of "
1228
+ "openravepy.IkParameterizationType.Transform6D" )
1229
+
1230
+ num_waypoints = traj .GetNumWaypoints ()
1231
+ if num_waypoints <= 1 :
1232
+ raise ValueError ("Trajectory needs more than 1 waypoint." )
1233
+
1234
+ from openravepy import RaveCreateTrajectory
1235
+
1236
+ if env is None :
1237
+ env = traj .GetEnv ()
1238
+
1239
+ # Create a new workspace trajectory with the same spec
1240
+ # as the old one
1241
+ new_traj = openravepy .RaveCreateTrajectory (env , '' )
1242
+ new_cspec = openravepy .IkParameterization .\
1243
+ GetConfigurationSpecificationFromType (
1244
+ openravepy .IkParameterizationType .Transform6D ,
1245
+ 'linear' )
1246
+ new_cspec .AddDeltaTimeGroup ()
1247
+ new_traj .Init (new_cspec )
1248
+
1249
+ # Get the current pose of the end effector
1250
+ # Note: OpenRAVE pose is [qx,qy,qz,qw, tx,ty,tz]
1251
+ # The 8th value is velocity.
1252
+ P_ee_prev = traj .GetWaypoint (0 )[range (7 )]
1253
+
1254
+ for i in range (num_waypoints ):
1255
+ P_ee = traj .GetWaypoint (i )[range (7 )] # Get 7 pose values
1256
+
1257
+ # Compute the translation delta
1258
+ p0 = P_ee_prev [4 :7 ] # Get the x,y,z translation
1259
+ p1 = P_ee [4 :7 ]
1260
+ delta_translation = numpy .sqrt (numpy .sum ((p0 - p1 )** 2 ))
1261
+
1262
+ # Compute the orientation delta
1263
+ q0 = P_ee_prev [0 :4 ] # Get qx,qy,qz,qw rotation
1264
+ q1 = P_ee [0 :4 ]
1265
+ delta_angle = AngleBetweenQuaternions (q0 , q1 )
1266
+
1267
+ dist = numpy .sqrt ( delta_translation ** 2 + (alpha ** 2 )* (delta_angle ** 2 ) )
1268
+ if i == 0 :
1269
+ deltatime = 0.0
1270
+ else :
1271
+ deltatime = dist
1272
+
1273
+ P_ee_prev = P_ee
1274
+
1275
+ # Insert a new waypoint (1x7 pose, velocity)
1276
+ values = numpy .append (P_ee , [deltatime ])
1277
+ new_traj .Insert (i , values )
1278
+
1279
+ return new_traj
1280
+
1281
+
1105
1282
def CheckJointLimits (robot , q ):
1106
1283
"""
1107
1284
Check if a configuration is within a robot's joint position limits.
0 commit comments