Alternatives to ALMath’s python bindings¶
ALMath provides some types which are useful for kinematics computations.
However, equivalent functionality is now available from the numpy and scipy.spatial python modules, which are relevant alternatives to ALMath’s python bindings.
In particular, they better comply to recent python ecosystem evolutions such as PEP 465.
This page shows how numpy and scipy.spatial modules can be used in place of some of ALMath types.
Conventions¶
>>> import almath
>>> import math
>>> import numpy
>>> import scipy.spatial.transform
>>> FRAME_WORLD = 0
>>> class FakeALMotionProxy:
... def getCOM(self, name, space, useSensors):
... return [2., 0., 6.]
... def getPosition(self, name):
... return [1, 2, 3, 4, 5, 6] # x, y, z, wx, wy, wz
... def getTransform(self, name, frame, useSensors):
... return [1, 0, 0, 10.,
... 0, 1, 0, 20.,
... 0, 0, 1, 30.,
... 0, 0, 0, 1]
... def getRobotPosition(self, name):
... return [1.1, 0.2, math.pi/6] # x, y, wz
... def getWorldRotation(self):
... return [1, 2, 3] # wx, wy, wz
>>> almotion = FakeALMotionProxy()
Spatial kinematics¶
almath.Position3D
three floating-point numbers representing the position of a point in 3 dimensional space.
Alternative: a (3,)-shaped numpy array.
Create an instance from individual coordinates:
>>> almath.Position3D() Position3D(x=0, y=0, z=0) >>> v0_al = almath.Position3D(1., 2., 3.) >>> v0_al Position3D(x=1, y=2, z=3) >>> list(v0_al.toVector()) [1.0, 2.0, 3.0]
>>> numpy.zeros(3) array([0., 0., 0.]) >>> v0_np = numpy.array([1., 2., 3.]) >>> v0_np array([1., 2., 3.]) >>> list(v0_np) [1.0, 2.0, 3.0]
Create an instance from a vector of coordinates, such as returned by ALMotionProxy::getCOM:
>>> v1 = almotion.getCOM("Body", 0, False) >>> v1 [2.0, 0.0, 6.0]
>>> v1_al = almath.Position3D(v1) >>> v1_al Position3D(x=2, y=0, z=6)
>>> v1_np = numpy.array(v1) >>> v1_np array([2., 0., 6.])
operations
>>> v0_al - 0.5 * v1_al Position3D(x=0, y=2, z=0) >>> almath.distance(v0_al, v1_al/2) 2.0
>>> v0_np - 0.5 * v1_np array([0., 2., 0.]) >>> numpy.linalg.norm(v0_np - v1_np/2) 2.0
>>> almath.dotProduct(v0_al, v1_al) 20.0 >>> almath.crossProduct(v0_al, v1_al) Position3D(x=12, y=0, z=-4)
>>> numpy.inner(v0_np, v1_np) 20.0 >>> numpy.cross(v0_np, v1_np) array([12., 0., -4.])
almath.Velocity3D
three floating-point numbers representing the velocity of a point in a 3 dimensional space.
Alternative: a (3,) shaped numpy array. See almath.Position3D above.
>>> almath.Velocity3D() Velocity3D(xd=0, yd=0, zd=0)
>>> numpy.zeros(3) array([0., 0., 0.])
almath.Rotation
represents a rotation in euclidean space as a 3x3 (orthogonal) matrix.
Alternatives:
- a (3, 3)-shaped numpy.array.
- a scipy.spatial.transform.Rotation
Create a rotation:
>>> almath.Rotation() Rotation([1, 0, 0 0, 1, 0 0, 0, 1]) >>> r0_al = almath.rotationFromRotZ(almath.PI/4) >>> r0_al Rotation([0.707107, -0.707107, 0 0.707107, 0.707107, 0 0, 0, 1])
>>> numpy.identity(3) array([[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]]) >>> def rotationFromRotZ(a): ... return numpy.array([[ math.cos(a), -math.sin(a), 0.], ... [ math.sin(a), math.cos(a), 0.], ... [ 0. , 0. , 1.]]) >>> r0_np = rotationFromRotZ(math.pi/4) >>> r0_np array([[ 0.70710678, -0.70710678, 0. ], [ 0.70710678, 0.70710678, 0. ], [ 0. , 0. , 1. ]])
>>> scipy.spatial.transform.Rotation.from_rotvec([0, 0, 0]) <scipy.spatial.transform.rotation.Rotation object at ...> >>> scipy.spatial.transform.Rotation.from_rotvec([0, 0, 0]).as_matrix() array([[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]]) >>> r0_sp = scipy.spatial.transform.Rotation.from_rotvec([math.pi/4, 0, 0]) >>> r0_sp.as_matrix() array([[ 1. , 0. , 0. ], [ 0. , 0.70710678, -0.70710678], [ 0. , 0.70710678, 0.70710678]])
Apply a rotation to a point or a vector:
>>> r0_al * almath.Position3D(10, 20, 30) Position3D(x=-7.07107, y=21.2132, z=30)
>>> numpy.matmul(r0_np, numpy.array([10, 20, 30])) array([-7.07106781, 21.21320344, 30. ])
>>> r0_sp.apply(numpy.array([10, 20, 30])) array([10. , -7.07106781, 35.35533906]) >>> numpy.matmul(r0_sp.as_matrix(), numpy.array([10, 20, 30])) array([10. , -7.07106781, 35.35533906])
almath.Transform
represents a rigid displacement in euclidean space as a 4x4 homogeneous matrix.
Alternative: a (4, 4)-shaped numpy array.
Create an homogenous matrix:
>>> almath.Transform() Transform([1, 0, 0, 0 0, 1, 0, 0 0, 0, 1, 0])
>>> numpy.identity(4) array([[1., 0., 0., 0.], [0., 1., 0., 0.], [0., 0., 1., 0.], [0., 0., 0., 1.]])
>>> tr0_al = almath.transformFromRotZ(almath.PI/4) >>> tr0_al Transform([0.707107, -0.707107, 0, 0 0.707107, 0.707107, 0, 0 0, 0, 1, 0])
>>> def transformFromRotation(r): ... ret = numpy.identity(4) ... ret[0:3, 0:3] = r ... return ret >>> tr0_np = transformFromRotation(rotationFromRotZ(math.pi/4)) >>> tr0_np array([[ 0.70710678, -0.70710678, 0. , 0. ], [ 0.70710678, 0.70710678, 0. , 0. ], [ 0. , 0. , 1. , 0. ], [ 0. , 0. , 0. , 1. ]])
>>> xy = math.sqrt(2) >>> tr1_al = almath.transformFromPosition(xy, xy, 3.) >>> tr1_al Transform([1, 0, 0, 1.41421 0, 1, 0, 1.41421 0, 0, 1, 3])
>>> def transformFromPosition(x, y, z): ... return numpy.array([[ 1., 0., 0., x ], ... [ 0., 1., 0., y ], ... [ 0., 0., 1., z ], ... [ 0., 0., 0., 1. ]]) >>> xy = math.sqrt(2) >>> tr1_np = transformFromPosition(xy, xy, 3.) >>> tr1_np array([[1. , 0. , 0. , 1.41421356], [0. , 1. , 0. , 1.41421356], [0. , 0. , 1. , 3. ], [0. , 0. , 0. , 1. ]])
Extract the translation part:
>>> almath.position3DFromTransform(tr1_al) Position3D(x=1.41421, y=1.41421, z=3)
>>> tr1_np[0:3, 3] array([1.41421356, 1.41421356, 3. ])
Extract the rotation part:
>>> almath.rotationFromTransform(tr1_al) Rotation([1, 0, 0 0, 1, 0 0, 0, 1])
>>> tr1_np[0:3, 0:3] array([[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]])
Serialize as a list of elements (row major):
>>> list(tr1_al.toVector()) [1.0, 0.0, 0.0, 1.4142135381698608, 0.0, 1.0, 0.0, 1.4142135381698608, 0.0, 0.0, 1.0, 3.0, 0.0, 0.0, 0.0, 1.0]
>>> list(tr1_np.reshape(16)) [1.0, 0.0, 0.0, 1.4142135623730951, 0.0, 1.0, 0.0, 1.4142135623730951, 0.0, 0.0, 1.0, 3.0, 0.0, 0.0, 0.0, 1.0]
Deserialize from a list of elements (row major), as returned by ALMotion’s getTransform:
>>> almath.Transform(almotion.getTransform("Head", FRAME_WORLD, True)) Transform([1, 0, 0, 10 0, 1, 0, 20 0, 0, 1, 30])
>>> numpy.array(almotion.getTransform("Head", FRAME_WORLD, True)).reshape(4, 4) array([[ 1., 0., 0., 10.], [ 0., 1., 0., 20.], [ 0., 0., 1., 30.], [ 0., 0., 0., 1.]])
apply displacement to a point using matrix multiplication:
>>> tr1_al * almath.Position3D(10, 20, 30) Position3D(x=11.4142, y=21.4142, z=33)
>>> numpy.matmul(tr1_np, numpy.array([10, 20, 30, 1]))[0:3] array([11.41421356, 21.41421356, 33. ])
compose displacements with matrix multiplication and matrix inverse:
>>> tr0_al * tr1_al Transform([0.707107, -0.707107, 0, 0 0.707107, 0.707107, 0, 2 0, 0, 1, 3]) >>> almath.transformInverse(tr0_al) Transform([0.707107, 0.707107, 0, -0 -0.707107, 0.707107, 0, -0 0, 0, 1, -0])
>>> numpy.matmul(tr0_np, tr1_np) array([[ 7.07106781e-01, -7.07106781e-01, 0.00000000e+00, 2.22044605e-16], [ 7.07106781e-01, 7.07106781e-01, 0.00000000e+00, 2.00000000e+00], [ 0.00000000e+00, 0.00000000e+00, 1.00000000e+00, 3.00000000e+00], [ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 1.00000000e+00]]) >>> numpy.linalg.inv(tr0_np) array([[ 0.70710678, 0.70710678, 0. , 0. ], [-0.70710678, 0.70710678, 0. , 0. ], [ 0. , 0. , 1. , 0. ], [ 0. , 0. , 0. , 1. ]])
almath.Quaternion
represents a rotation in euclidean space as a unit quaternion.
Alternative: a scipy.spatial.transform.Rotation
>>> almath.Quaternion() Quaternion(w=1, x=0, y=0, z=0)
>>> r = scipy.spatial.transform.Rotation.from_rotvec([0, 0, 0]) >>> r.as_quat() # quaternion in scalar-last (x, y, z, w) format array([0., 0., 0., 1.])
almath.Rotation3D
a set of Euler angles, used to represent a rotation in euclidean space.
Euler angles usage is discouraged.
Alternative: scipy.spatial.transform.Rotation, which supports Euler angles conversion.
>>> almath.Rotation3D() Rotation3D(wx=0, wy=0, wz=0) >>> almath.quaternionFromRotation3D(almath.Rotation3D(1,2,3)) Quaternion(w=0.435953, x=-0.718287, y=0.310622, z=0.444435) >>> almath.rotationFromTransform(almath.transformFromRotation3D(almath.Rotation3D(1,2,3))) Rotation([0.411982, -0.833738, -0.36763 -0.0587266, -0.426918, 0.902382 -0.909297, -0.350175, -0.224845])
>>> r = scipy.spatial.transform.Rotation.from_euler('xyz', [1, 2, 3]) >>> r.as_quat() # quaternion in scalar-last (x, y, z, w) format array([-0.71828702, 0.31062245, 0.44443511, 0.43595284]) >>> r.as_matrix() array([[ 0.41198225, -0.83373765, -0.36763046], [-0.05872664, -0.42691762, 0.90238159], [-0.90929743, -0.35017549, -0.2248451 ]])
almath.Position6D
a set of Euler angles and translations, used to represent a pose in euclidean space.
Euler angles usage is discouraged. Alternative: none.
Examples:
>>> almath.Position6D() Position6D(x=0, y=0, z=0, wx=0, wy=0, wz=0)
Planar kinematics¶
almath.Position2D
two floating-point numbers representing the position of a point in 2 dimensional space (the XY plane)
Alternative: a (2,) shaped numpy array.
>>> almath.Position2D() Position2D(x=0, y=0) >>> almath.position2DFromTransform(tr1_al) Position2D(x=1.41421, y=1.41421)
>>> numpy.zeros([2]) array([0., 0.]) >>> tr1_np[0:2, 3] array([1.41421356, 1.41421356])
almath.Pose2D
three floating-point numbers representing a pose in 2 dimensional space (the XY plane), as a translation in XY and a rotation along Z.
Usage of planar transforms is discouraged. For most computations, using spatial geometry is easier and less error-prone.
Whenever a given spatial rotation is along the Z-axis, its angle can be computed
>>> almath.pose2DFromTransform(tr0_al).theta 0.7853981852531433
>>> math.atan2(tr0_np[1, 0], tr0_np[0, 0]) 0.7853981633974483
>>> scipy.spatial.transform.Rotation.from_dcm(tr0_np[0:3, 0:3]).as_rotvec() array([0. , 0. , 0.78539816])