Models with Rotations
In robotics, the state of our robot often depends on one or more arbitrary 3D rotations (a.k.a. orientation, attitude). Effectively representing the non-trivial group structure of rotations has been a topic of study for over 100 years, and as a result many parameterizations exists. RobotDynamics supports the types defined in Rotations.jl.
The LieGroupModel
type allows users to abstract away the particular rotation representation used and will automatically create efficient methods to handle the potentially different state dimensions that result. Additionally, it defines methods for operating on the error state, which for rotations is always three-dimensional. See the discussion in the Rotaitons.jl README for more information on the error state.
RobotDynamics requires v1.0
or higher of Rotations.jl
Defining a LieGroupModel
We define a LieGroupModel
very similarly to that of a standard model. For this example, let's assume we are modeling a constellation of 2 satellites and we only care about the attitude dynamics. We will define our state to be [q1, ω1, q2, ω2]
where qi
and ωi
are the orientation and angular velocity of the i
th satellite, respectively.
We start by defining our new type and our dynamics function
struct SatellitePair{R,T} <: LieGroupModel
J1::SMatrix{3,3,T,9} # inertia of satellite 1
J2::SMatrix{3,3,T,9} # inertia of satellite 2
end
function RobotDynamics.dynamics(model::SatellitePair, x, u)
vs = RobotDynamics.vec_states(model, x) # extract "vector" states
qs = RobotDynamics.rot_states(model, x) # extract attitude states
ω1 = vs[2] # offset index by 1 since there are now "vector" states before the first quaternion
ω2 = vs[3]
q1 = qs[1]
q2 = qs[2]
J1, J2 = model.J1, model.J2
u1 = u[SA[1,2,3]]
u2 = u[SA[4,5,6]]
ω1dot = J1\(u1 - ω1 × (J1 * ω1))
ω2dot = J2\(u2 - ω2 × (J2 * ω2))
q1dot = Rotations.kinematics(q1, ω1)
q2dot = Rotations.kinematics(q2, ω2)
SA[
q1dot[1], q1dot[2], q1dot[3], q1dot[4],
ω1dot[1], ω1dot[2], ω1dot[3],
q2dot[1], q2dot[2], q2dot[3], q2dot[4],
ω2dot[1], ω2dot[2], ω2dot[3],
]
end
RobotDynamics.control_dim(::SatellitePair) = 6
Before defining the functions vec_states
and rot_states
, we will define the type LieState
, which defines how our state vector is stacked or partitioned. The LieState
only needs to know how many "vector" or "non-rotation" states exist, and where the rotations are placed in the state vector. In our example, we have a rotation, followed by 3 "vector" states, followed by a rotation, followed by 3 "vector" states, so we would define our LieState
to be
RobotDynamics.LieState(::SatellitePair{R}) where R = RobotDynamics.LieState(R, (0,3,3))
which means we have a state with 0 vector states at the beginning, followed by a rotation, followed by 3 vector states, followed by a rotation, followed by 3 vector states, and the rotation type is R
.
With this partitioning in mind, we can now understand the behavior of vec_states
and rot_states
, which simply extract the vector and attitude parts of the state as tuples of SVector
s.
LieGroupModel
API
RobotDynamics.LieGroupModel
— TypeLieGroupModel <: AbstractModel
Abstraction of a dynamical system whose state contains at least one arbitrary rotation.
RobotDynamics.LieState
— TypeLieState{R,P}
Specifies a state with rotational components mixed in with standard vector components. All rotational components are assumed to be parameterizations of 3D rotations.
Parameters
R <: Rotation
is the rotational representation used in the state vector. Must have
params(::Type{R})
defined, which returns the number of parameters used by the rotation, as well a constructor that takes each parameter as a separate scalar argument.
P <: Tuple{Vararg{Int}}
is a tuple of integers specifying the partitioning of the state
vector. Each element of P
specifies the length of the vector component between the rotational components, and P[1]
and P[end]
specify the number of vector states at the beginning and end of the state vector.
Examples
If we want to construct a state vector like the following: [v3, q, v2, q, v3]
where v2
and v3
and vector components of length 2 and 3, respectively, and q
is a 4-dimensional unit quaternion. The LieState
for this state vector would be LieState{UnitQuaternion{Float64},3,2,3}
. The length should be (3+4+2+4+3) = 16, which can be verified by length(s::LieState)
.
Constructors
LieState(::Type{R}, P::Tuple{Vararg{Int}})
LieState(::Type{R}, p1::Int, p2::Int, p3::Int...)
RobotDynamics.QuatState
— FunctionQuatState(n::Int, Q::StaticVector{<:Any,Int})
QuatState(n::Int, Q::NTuple{<:Any,Int})
Create a n
-dimensional LieState
assuming R = UnitQuaternion{Float64}
and Q[i]
is the first index of each quaternion in the state vector.
Example
If we want to construct a state vector like the following: [v3, q, v2, q, v3]
where v2
and v3
and vector components of length 2 and 3, respectively, and q
is a 4-dimensional unit quaternion. Since the first quaternion starts at index 4, and the second starts at index 10, Q = [4,10]. The entire length of the vector is n = 16 = 3 + 4 + 2 + 4 + 3
, so we would call QuatState(16, SA[4,10])
.
RobotDynamics.vec_states
— Functionvec_states(model::LieGroupModel, x)
vec_states(s::LieState, x)
Extracts the "vector" states out of the state vector x
for a LieGroupModel
. Returns a tuple v
of SVector
s, where length(v[i])
is equal to the length specified by the LieState
.
RobotDynamics.rot_states
— Functionvec_states(model::LieGroupModel, x)
vec_states(s::LieState, x)
Extracts the rotations out of the state vector x
for a LieGroupModel
. Returns a tuple rotations, whose type matches the rotation type specified in the LieState
.