Plucker vectors and coordinate transforms

Here we discuss the use of Plücker vectors and their transforms for describing rigid-body motion and force. Plücker vectors succinctly describe both the angular (rotational) and linear (translational) part of motion, and the angular (moment) and linear (force) part of force. In three dimensions, a Plücker vector is 6-dimensional, e.g., Plücker velocity and force vectors are

\[v = \begin{bmatrix} \Omega_x \\ \Omega_y \\ \Omega_z \\ U_x \\ U_y \\ U_z \end{bmatrix}, \qquad f = \begin{bmatrix} M_x \\ M_y \\ M_z \\ F_x \\ F_y \\ F_z \end{bmatrix}\]

In two dimensions, there is only one angular component and two linear components, e.g.,

\[v = \begin{bmatrix} \Omega_z \\ U_x \\ U_y \end{bmatrix}, \qquad f = \begin{bmatrix} M_z \\ F_x \\ F_y \end{bmatrix}\]

We need to be able to transform these vectors from one coordinate system to another. This requires rotating their components and shifting their center from one origin to another. For example, a translational velocity based at system B will be different from the translational velocity at system A because of the rotational velocity, $\Omega \times {}^Br_{A}$, where ${}^Br_{A}$ is the vector from the origin of A to the origin of B.

Similarly, the moment about B will be different from the moment about A due to the moment arm ${}^Br_{A} \times F$.

using RigidBodyTools
using LinearAlgebra
using Plots

Plücker vectors

A Plücker vector is easily created by simply supplying a vector of its components

v = PluckerMotion([1.0,2.0,3.0])
2d Plucker motion vector, Ω = 1.0, U = [2.0, 3.0]

This created a 2d motion vector, with angular velocity 1.0 and linear velocity (2.0,3.0). One can also supply the angular and linear parts separately, using keywords. If one of these keywords is omitted, it defaults to zero for that part. Note that we also need to write this as PluckerMotion{2} to specify the physical dimensionality. For a 3d motion vector, one would write PluckerMotion{3} here.

v2 = PluckerMotion{2}(angular=1.0,linear=[2.0,3.0])
v2 == v
true

We can also pick off the angular and linear parts

angular_only(v)
PluckerMotionAngular{2}(2d Plucker motion vector, Ω = 1.0, U = [2.0, 3.0])

and

linear_only(v)
PluckerMotionLinear{2}(2d Plucker motion vector, Ω = 1.0, U = [2.0, 3.0])

Force vectors are similar

f = PluckerForce([-1.0,-3.5,2.25])
2d Plucker force vector, M = -1.0, F = [-3.5, 2.25]

The vectors of the same type can be added and subtracted

v3 = v + v2
2d Plucker motion vector, Ω = 2.0, U = [4.0, 6.0]

We can also take a scalar product of force and motion vectors

dot(f,v)
-1.25

Transforms

Transforms are constructed by describing the relationship between the two coordinate systems. Consider the example in the figure below.

CoordinateSystems.svg

To develop the 2d transform from A to B, we supply the position $r$ and the rotation angle $\theta$. For example, if B is shifted by [1,1] and rotated by angle $\pi/6$ counterclockwise about A, then we construct the transform as

Xm = MotionTransform([1,1],π/6)
2d motion transform, x = [1.0, 1.0], R = [0.8660254037844387 0.49999999999999994; -0.49999999999999994 0.8660254037844387]

Note that it uses the angle of rotation, $\pi/6$, to create a rotation matrix operator.

A 2d force transform would be constructed by

Xf = ForceTransform([1,1],π/6)
2d force transform, x = [1.0, 1.0], R = [0.8660254037844387 0.49999999999999994; -0.49999999999999994 0.8660254037844387]

For 3d transforms, we need to supply the rotation operator itself (as well as the 3d translation vector). Often, this rotation is done by rotating about a certain axis by a certain angle. We do this with the rotation_about_axis function. For example, to rotate by $\pi/4$ about an axis parallel to the vector $[1,1,1]$, then we use

R = rotation_about_axis(π/4,[1,1,1])
3×3 StaticArraysCore.SMatrix{3, 3, Float64, 9} with indices SOneTo(3)×SOneTo(3):
  0.804738  -0.310617   0.505879
  0.505879   0.804738  -0.310617
 -0.310617   0.505879   0.804738

and then to translate this rotated system by $[-1,-2,-3]$,

Xm = MotionTransform([-1,-2,-3],R)
3d motion transform, x = [-1, -2, -3], R = [0.8047378541243648 -0.31061721752604554 0.5058793634016806; 0.5058793634016806 0.8047378541243648 -0.31061721752604554; -0.31061721752604554 0.5058793634016806 0.8047378541243648]

and similarly for a force transform.

We can also compute the inverses of these transforms, to transform back from B to A

inv(Xm)
3d motion transform, x = [1.7011415092773154, 1.1835034190722735, 3.11535507165041], R = [0.8047378541243648 0.5058793634016806 -0.31061721752604554; -0.31061721752604554 0.8047378541243648 0.5058793634016806; 0.5058793634016806 -0.31061721752604554 0.8047378541243648]

Transforms of the same type (motion or force) can be composed via multiplication to transform from, e.g., A to B to C.

Xm1 = MotionTransform([1.5,1.5],π/6)
Xm2 = MotionTransform([-1,1],π/3)
Xm2*Xm1
2d motion transform, x = [0.1339745962155614, 1.8660254037844388], R = [2.1460752085336256e-16 1.0; -1.0 2.1460752085336256e-16]

Transforming bodies

We can use motion transforms, in particular, to place bodies. We simply apply the transform as a function, and it transforms the body's coordinates. For example, transform Xm1 above shifts the body to [1.5,1.5] and rotates it counterclockwise by π/6:

b = Ellipse(1.0,0.2,0.02)
plot(b,xlims=(-3,3),ylims=(-3,3),fillcolor=:gray)
plot!(Xm1(b),xlims=(-3,3),ylims=(-3,3))
Example block output

In the example above, we did not affect the original body by applying the transform as a function. Rather, we created a copy of the body.

If, instead, you wish to transform the body in place, use update_body!

update_body!(b,Xm1)
Elliptical body with 208 points and semi-axes (1.0,0.2)
   Current position: (1.5,1.5)
   Current angle (rad): 0.5235987755982987

One important note: a body stores a set of coordinates in its own intrinsic coordinate system, and when a transform is applied to the body, it always acts on these coordinates. This means that the transform's application on the body cannot be carried out as a composite of operations, e.g. T2(T1(b)) is not possible. Insteady, in the application on the body, the transform is always interpreted such that system A is the inertial coordinate system and B is the body system. Of course, the transform itself can always be constructed from composite transforms.

Sometimes we need information about the normals in the body system. For these, we can use normalmid with the flag axes=:body:

nx, ny = normalmid(b,axes=:body)
([1.0000000000000002, 0.886335337801729, 0.7369383257078719, 0.6074921092237509, 0.5315356836790487, 0.46002086938081577, 0.4196286225913622, 0.37383679538157694, 0.3490372105915698, 0.3162175178317445  …  0.29937695843692386, 0.3162178254216906, 0.3490368976183922, 0.37383710310484886, 0.4196283157312741, 0.460021163993197, 0.5315354051225997, 0.6074923550501666, 0.7369381416992703, 0.886335424329191], [-0.0, 0.46304391688468227, 0.6759599870575563, 0.7943257123062704, 0.8470359006416734, 0.8879081032033317, 0.9076958847004369, 0.9274945015571872, 0.9371088654059572, 0.9486867140507002  …  -0.9541349153851568, -0.9486866115243676, -0.9371089819764412, -0.9274943775259097, -0.9076960265620437, -0.8879079505660217, -0.8470360754425852, -0.7943255243006957, -0.6759601876655473, -0.46304375125812297])

Finally, if you wish to transform the body's own coordinate system, rather than use the transform to simply place the body in the inertial system, then use transform_body!. This transforms the intrinsic coordinates of the body.

transform_body!(b,Xm1)
Elliptical body with 208 points and semi-axes (1.0,0.2)
   Current position: (1.5,1.5)
   Current angle (rad): 0.5235987755982987

Transforming Plücker vectors

Transforms can be applied to Plücker vectors to transform their components between systems. Let's consider a 2d example in which the motion based at system A is purely a rotation with angular velocity $\Omega = 1$, and we wish to transform this to system B, translated by $[2,0]$ from A, but with axes aligned with B. We expect that the velocity based at B should have the same angular velocity, but also should have translational velocity equal to $[0,2]$ due to the angular motion.

First we construct the motion vector at A

Ω = 1.0
vA = PluckerMotion(Ω,[0,0])
2d Plucker motion vector, Ω = 1.0, U = [0.0, 0.0]

Now construct the transform from A to B:

XA_to_B = MotionTransform([2,0],0)
2d motion transform, x = [2.0, 0.0], R = [1.0 -0.0; 0.0 1.0]

Now apply the transform to get the velocity at B:

vB = XA_to_B*vA
2d Plucker motion vector, Ω = 1.0, U = [0.0, 2.0]

which gives the expected result. Now let's transform back, using the inverse, and check that we get back to vA

inv(XA_to_B)*vB
2d Plucker motion vector, Ω = 1.0, U = [0.0, 0.0]

Transform functions

RigidBodyTools.PluckerMotionType
PluckerMotion(data::AbstractVector)

Creates an instance of a Plucker motion vector,

$v = \begin{bmatrix} \Omega \\ U \end{bmatrix}$

using the vector in data. If data is of length 6, then it creates a 3d motion vector, and the first 3 entries are assumed to comprise the rotational component \Omega and the last 3 entries the translational component U. If data is of length 3, then it creates a 2d motion vector, assuming that the first entry in data represents the rotational component and the second and third entries the x and y translational components.

source
RigidBodyTools.PluckerForceType
PluckerForce(data::AbstractVector)

Creates an instance of a Plucker force vector,

$f = \begin{bmatrix} M \\ F \end{bmatrix}$

using the vector in data. If data is of length 6, then it creates a 3d force vector, and the first 3 entries are assumed to comprise the moment component M and the last 3 entries the force component F. If data is of length 3, then it creates a 2d force vector, assuming that the first entry in data represents the moment component and the second and third entries the x and y force components.

source
RigidBodyTools.angular_onlyFunction
angular_only(v::AbstractPluckerVector)

Returns a Plucker vector with only the angular part of the motion or force vector v available for subsequent operations. Note that no copy of the original data in v is made. Rather, this simply provides a lazy reference to the angular data in v.

source
RigidBodyTools.linear_onlyFunction
linear_only(v::AbstractPluckerVector)

Returns a Plucker vector with only the linear part of the motion or force vector v available for subsequent operations. Note that no copy of the original data in v is made. Rather, this simply provides a lazy reference to the linear data in v.

source
LinearAlgebra.dotMethod
dot(f::AbstractPluckerForceVector,v::AbstractPluckerMotionVector) -> Real

Calculate the scalar product between force f and motion v. The commutation of this is also possible, dot(v,f).

source
RigidBodyTools.MotionTransformType
(T::MotionTransform)(b::Body) -> Body

Transforms a body b using the given MotionTransform, creating a copy of this body with the new configuration. In using this transform T (which defines a transform from system A to system B), A is interpreted as an inertial coordinate system and B as the body system. Thus, the position vector in T is interpreted as the relative position of the body in inertial coordinates and the inverse of the rotation operator is applied to transform body-fixed coordinates to the inertial frame.

source
MotionTransform(xA_to_B::SVector,RA_to_B::SMatrix) -> MotionTransform

Computes the Plucker transform matrix for motion vectors, transforming from system A to system B. The input xA_to_B is the Euclidean vector from the origin of A to the origin of B, expressed in A coordinates, and RA_to_B is the rotation matrix transforming coordinates in system A to those in system B. The resulting matrix has the form

${}^B T^{(m)}_A = \begin{bmatrix} R & 0 \\ 0 & R \end{bmatrix} \begin{bmatrix} 1 & 0 \\ -x^\times & 1 \end{bmatrix}$

One can also provide xA_to_B as a standard vector and RA_to_B as a standard 3 x 3 matrix.

If xA_to_B has length 3, then a three-dimensional transform (a 6 x 6 Plucker transform) is created. If xA_to_B has length 2, then a two-dimensional transform (3 x 3 Plucker transform) is returned.

source
MotionTransform(xA_to_B,θ::Real) -> MotionTransform

Computes the 3 x 3 2D Plucker transform matrix for motion vectors, transforming from system A to system B. The input xA_to_B is the 2-d Euclidean vector from the origin of A to the origin of B, expressed in A coordinates, and θ is the angle of system B relative to system A. xA_to_B can be in the form of a static vector, a vector, or a tuple.

source
MotionTransform(T::RigidTransform) -> MotionTransform

Computes the 3 x 3 2D Plucker transform matrix for motion vectors, transforming from system A to system B, from the rigid transform T.

source
RigidBodyTools.ForceTransformType
ForceTransform(xA_to_B::SVector,RA_to_B::SMatrix) -> ForceTransform

Computes the 6 x 6 Plucker transform matrix for force vectors, transforming from system A to system B. The input xA_to_B is the Euclidean vector from the origin of A to the origin of B, expressed in A coordinates, and RA_to_B is the rotation matrix transforming coordinates in system A to those in system B. The resulting matrix has the form

${}^B T^{(f)}_A = \begin{bmatrix} R & 0 \\ 0 & R \end{bmatrix} \begin{bmatrix} 1 & -x^\times \\ 0 & 1 \end{bmatrix}$

source
ForceTransform(xA_to_B,θ::Real) -> ForceTransform

Computes the 3 x 3 2D Plucker transform matrix for force vectors, transforming from system A to system B. The input xA_to_B is the 2-d Euclidean vector from the origin of A to the origin of B, expressed in A coordinates, and θ is the angle of system B relative to system A. xA_to_B can be in the form of a static vector, a vector, or a tuple.

source
ForceTransform(T::RigidTransform) -> ForceTransform

Computes the 3 x 3 2D Plucker transform matrix for force vectors, transforming from system A to system B, from the rigid transform T.

source
Base.invMethod
inv(M)

Matrix inverse. Computes matrix N such that M * N = I, where I is the identity matrix. Computed by solving the left-division N = M \ I.

Examples

julia> M = [2 5; 1 3]
2×2 Matrix{Int64}:
 2  5
 1  3

julia> N = inv(M)
2×2 Matrix{Float64}:
  3.0  -5.0
 -1.0   2.0

julia> M*N == N*M == Matrix(I, 2, 2)
true
inv(X::AbstractTransformOperator) -> AbstractTransformOperator

Return the inverse of the motion or force transform X.

source
Base.transposeMethod
transpose(X::MotionTransform) -> ForceTransform
transpose(X::ForceTransform) -> MotionTransform

For a motion transform X mapping from system A to B, returns the force transform mapping from B to A. Alternatively, if X is a force transform, it returns the motion transform.

source
RigidBodyTools.rotation_transformFunction
rotation_transform(T::AbstractTransformOperator) -> AbstractTransformOperator

Returns a transform operator consisting of only the rotational part of T.

source
RigidBodyTools.translation_transformFunction
translation_transform(T::AbstractTransformOperator) -> AbstractTransformOperator

Returns a transform operator consisting of only the translational part of T.

source
RigidBodyTools.update_body!Function
update_body!(b::Body,T::MotionTransform)

Transforms a body (in-place) using the given MotionTransform. In using this transform T (which defines a transform from system A to system B), A is interpreted as an inertial coordinate system and B as the body system. Thus, the position vector in T is interpreted as the relative position of the body in inertial coordinates and the inverse of the rotation operator is applied to transform body-fixed coordinates to the inertial frame.

source
update_body!(bl::BodyList,tl::MotionTransformList) -> BodyList

Carry out in-place transformations of each body in bl with the corresponding transformation in tl.

source
update_body!(bl::Union{Body,BodyList},x::AbstractVector,m::RigidBodyMotion)

Update body/bodies in bl with the rigid-body motion m and state vector x.

source
RigidBodyTools.transform_body!Function
transform_body!(b::Body,T::MotionTransform)

Transforms a body's own coordinate system (in-place) using the given MotionTransform. This function differs from update_body! because it changes the coordinates of the body in its own coordinate system, whereas the latter function only changes the inertial coordinates of the body. T is interpreted as a transform from the new system to the old system.

source
transform_body!(bl::BodyList,tl::MotionTransformList) -> BodyList

Carry out in-place transformations of body coordinate systems for each body in bl with the corresponding transformation in tl.

source
update_body!(bl::Union{Body,BodyList},x::AbstractVector,m::RigidBodyMotion)

Transform body/bodies in bl with the rigid-body motion m and state vector x.

source

This page was generated using Literate.jl.