Skip to content

Commit 129f7e4

Browse files
author
DavidB-CMU
committed
Improvements and new functions for prpy.util
1 parent d9dd4e3 commit 129f7e4

File tree

1 file changed

+182
-5
lines changed

1 file changed

+182
-5
lines changed

src/prpy/util.py

Lines changed: 182 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ def NormalizeVector(vec):
205205
@returns numpy.array result: A vector of the same size, where the
206206
L2 norm of the elements equals 1.
207207
"""
208+
numpy.seterr(divide='ignore', invalid='ignore')
208209
magnitude = numpy.sqrt(vec.dot(vec))
209210
vec2 = (vec / magnitude)
210211
return numpy.nan_to_num(vec2) # convert NaN to zero
@@ -726,6 +727,25 @@ def GeodesicError(t1, t2):
726727
return numpy.hstack((trans, angle))
727728

728729

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+
729749
def GeodesicDistance(t1, t2, r=1.0):
730750
'''
731751
Computes the geodesic distance between two transforms
@@ -739,12 +759,28 @@ def GeodesicDistance(t1, t2, r=1.0):
739759
return numpy.linalg.norm(error)
740760

741761

742-
def GetGeodesicDistanceBetweenTransforms(T0, T1):
762+
def GetGeodesicDistanceBetweenTransforms(T0, T1, r=1.0):
743763
"""
744764
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
745772
"""
773+
return GeodesicDistance(T0, T1, r)
746774

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)
748784

749785

750786
def GetEuclideanDistanceBetweenTransforms(T0, T1):
@@ -755,7 +791,7 @@ def GetEuclideanDistanceBetweenTransforms(T0, T1):
755791
"""
756792
p0 = T0[0:3,3] # Get the x,y,z translation from the 4x4 matrix
757793
p1 = T1[0:3,3]
758-
return numpy.sqrt(numpy.sum((p0-p1)**2))
794+
return GetEuclideanDistanceBetweenPoints(p0, p1)
759795

760796

761797
def GetMinDistanceBetweenTransformAndWorkspaceTraj(T, traj, dt=0.01):
@@ -774,14 +810,15 @@ def GetMinDistanceBetweenTransformAndWorkspaceTraj(T, traj, dt=0.01):
774810
if not IsTimedTrajectory(traj):
775811
raise ValueError("Trajectory must have timing information.")
776812

777-
if not IsTrajectoryTypeIkParameterization(traj):
813+
if not IsTrajectoryTypeIkParameterizationTransform6D(traj):
778814
raise ValueError("Trajectory is not a workspace trajectory, it "
779815
"must have configuration specification of "
780816
"openravepy.IkParameterizationType.Transform6D")
781817

782818
def _GetError(t):
783819
T_curr = openravepy.matrixFromPose(traj.Sample(t)[0:7])
784820
error = GetEuclideanDistanceBetweenTransforms(T, T_curr)
821+
return error
785822

786823
min_dist = numpy.inf
787824
t_loc = 0.0
@@ -995,10 +1032,37 @@ def IsJointSpaceTrajectory(traj):
9951032
return False
9961033

9971034

998-
def IsTrajectoryTypeIkParameterization(traj):
1035+
def IsWorkspaceTrajectory(traj):
9991036
"""
10001037
Check if trajectory is a workspace trajectory.
10011038
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+
10021066
@param openravepy.Trajectory traj: A path or trajectory.
10031067
@return bool result: Returns True or False.
10041068
"""
@@ -1010,6 +1074,43 @@ def IsTrajectoryTypeIkParameterization(traj):
10101074
return False
10111075

10121076

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+
10131114
def ComputeEnabledAABB(kinbody):
10141115
"""
10151116
Returns the AABB of the enabled links of a KinBody.
@@ -1102,6 +1203,82 @@ def ComputeUnitTiming(robot, traj, env=None):
11021203
return new_traj
11031204

11041205

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+
11051282
def CheckJointLimits(robot, q):
11061283
"""
11071284
Check if a configuration is within a robot's joint position limits.

0 commit comments

Comments
 (0)