Module MAPLEAF.Motion.RigidBodies
Classes that represent 3- and 6-DoF rigid bodies. Rigid body classes contain the logic for calculating rigid body state derivatives, given current applied forces and inertia of the rigid body
Expand source code
"""
Classes that represent 3- and 6-DoF rigid bodies.
Rigid body classes contain the logic for calculating rigid body state derivatives, given current applied forces and inertia of the rigid body
"""
from MAPLEAF.Motion import (AngularVelocity, RigidBodyStateDerivative,
RigidBodyStateDerivative_3DoF, StateList, integratorFactory, Vector)
__all__ = [ "RigidBody_3DoF", "RigidBody", "StatefulRigidBody" ]
#### 3 DoF ####
class RigidBody_3DoF:
"""Interface:
### Properties ###
.state = RigidBodyState (pos, vel, orientation, angVel)
.time = currentSimTime
### Methods ###
.timeStep(deltaT) -> advances simulation by deltaT
"""
''' forceParam and inertiaParam should be functions, they will be passed:
1) Simulation Time
2) RigidBodyState object containing the rigidBody's Position, Velocity, Orientation and AngularVelocity
Functions are expected to return:
1) forceParam: A Force object containing the total force and moment applied to the rigidBody at it's CG location in the local frame of reference
2) inertiaParam: An Inertia object containing the mass of the object (other fields ignored)
If the rigid body is representing a CompositeObject, pass in a reference to the getMass function here
'''
def __init__(self, rigidBodyState, forceParam, inertiaParam, startTime=0, integrationMethod="Euler", discardedTimeStepCallback=None, simDefinition=None):
self.time = startTime
self.state = rigidBodyState
self.forceFunc = forceParam
self.inertiaFunc = inertiaParam
self.integrate = integratorFactory(integrationMethod=integrationMethod, simDefinition=simDefinition, discardedTimeStepCallback=discardedTimeStepCallback)
self.lastStateDerivative = RigidBodyStateDerivative_3DoF(rigidBodyState.velocity, Vector(0,0,0))
def getRigidBodyStateDerivative(self, time, state):
force = self.forceFunc(time, state).force
DposDt = state.velocity
DvelDt = force / self.inertiaFunc(time, state)
return RigidBodyStateDerivative_3DoF(DposDt, DvelDt)
def timeStep(self, deltaT):
# TODO: Current derivative estimates are first-order, replace with a more accurate iterative method
self.state.estimatedDerivative = self.lastStateDerivative
integrationResult = self.integrate(self.state, self.time, self.getRigidBodyStateDerivative, deltaT)
# This is where the simulation time and state are kept track of
self.time += integrationResult.dt
self.state = integrationResult.newValue
# Save for next time step
self.lastStateDerivative = integrationResult.derivativeEstimate
return integrationResult
#### 6 DoF ####
class RigidBody(RigidBody_3DoF):
"""
6DoF version of RigidBody_3DoF. Calculates angular velocities and accelerations
Interface:
Properties:
.state = RigidBodyState (pos, vel, orientation, angVel)
.time = currentSimTime
External functions wrapped by this class
.forceFunc(time, state)
.inertiaFunc(time, state)
Methods
.timeStep(deltaT)
"""
#TODO: Moment of Inertia Tensor? Required for aircraft
def __init__(self, rigidBodyState, forceParam, inertiaParam, integrationMethod="Euler", discardedTimeStepCallback=None, simDefinition=None):
''' Just calls RigidBodyState_3DoF constructor '''
super().__init__(rigidBodyState, forceParam, inertiaParam, integrationMethod=integrationMethod, simDefinition=simDefinition, discardedTimeStepCallback=discardedTimeStepCallback)
self.lastStateDerivative = RigidBodyStateDerivative(rigidBodyState.velocity, Vector(0,0,0), rigidBodyState.angularVelocity, AngularVelocity(0,0,0))
def getRigidBodyStateDerivative(self, time, state):
# Forces are expected to be calculated in a body frame, where each coordinate axis is aligned with a pricipal axis
appliedForce_localFrame = self.forceFunc(time, state)
inertia = self.inertiaFunc(time, state)
# Get CG velocity relative to body geometry
dt = 1e-8
inertia2 = self.inertiaFunc(time+dt, state)
CGVel_local = (inertia2.CG - inertia.CG) / dt
CGVel_global = state.orientation.rotate(CGVel_local)
### Translation - calculated in global frame ###
#Convert from local to global frame
fVec_global = state.orientation.rotate(appliedForce_localFrame.force)
linAccel_global = fVec_global / inertia.mass
### Rotation - calculated in local frame (Euler equations) ###
# convert angular velocity to global frame - to be added to orientation
angVel_global = AngularVelocity(*state.orientation.rotate(state.angularVelocity)) # Will be transformed into a quaternion once multiplied by a timestep
# Calc angular acceleration (in local frame)
moi = inertia.MOI
dAngVelDtX = (appliedForce_localFrame.moment.X + (moi.Y - moi.Z) * state.angularVelocity.Y * state.angularVelocity.Z) / moi.X
dAngVelDtY = (appliedForce_localFrame.moment.Y + (moi.Z - moi.X) * state.angularVelocity.Z * state.angularVelocity.X) / moi.Y
dAngVelDtZ = (appliedForce_localFrame.moment.Z + (moi.X - moi.Y) * state.angularVelocity.X * state.angularVelocity.Y) / moi.Z
dAngVelDt = AngularVelocity(dAngVelDtX, dAngVelDtY, dAngVelDtZ)
return RigidBodyStateDerivative(state.velocity+CGVel_global, linAccel_global, angVel_global, dAngVelDt)
class StatefulRigidBody(RigidBody):
'''
Intended to represent rigid bodies that have additional stateful properties outside of their rigd body state (ex. tank levels, actuator positions).
State is defined a by a `StateList` instead of by a `MAPLEAF.Motion.RigidBodyState`.
'''
def __init__(self, rigidBodyState, forceParam, inertiaParam, integrationMethod="Euler", discardedTimeStepCallback=None, simDefinition=None):
super().__init__(rigidBodyState, forceParam, inertiaParam, integrationMethod, discardedTimeStepCallback, simDefinition)
self.state = StateList([ rigidBodyState ])
self.derivativeFuncs = [ self.getRigidBodyStateDerivative ]
def addStateVariable(self, name, currentValue, derivativeFunction):
'''
Pass in the current value of a parameter which needs to be integrated, and a derivative function
Derivative function should accept the current time and StateList as inputs and return the derivative of the new parameter
'''
self.state.addStateVariable(name, currentValue)
self.derivativeFuncs.append(derivativeFunction)
def getStateDerivative(self, time, state):
return StateList([ derivativeFunc(time, state) for derivativeFunc in self.derivativeFuncs ], _nameToIndexMap=state.nameToIndexMap)
def timeStep(self, deltaT):
# TODO: Current derivative estimates are first-order, replace with a more accurate iterative method
self.state.estimatedDerivative = self.lastStateDerivative
integrationResult = self.integrate(self.state, self.time, self.getStateDerivative, deltaT)
# This is where the simulation time and state are kept track of
self.time += integrationResult.dt
self.state = integrationResult.newValue
# Save for next time step
self.lastStateDerivative = integrationResult.derivativeEstimate
return integrationResult
Classes
class RigidBody (rigidBodyState, forceParam, inertiaParam, integrationMethod='Euler', discardedTimeStepCallback=None, simDefinition=None)
-
6DoF version of RigidBody_3DoF. Calculates angular velocities and accelerations
Interface
Properties: .state = RigidBodyState (pos, vel, orientation, angVel) .time = currentSimTime
External functions wrapped by this class .forceFunc(time, state) .inertiaFunc(time, state)
Methods .timeStep(deltaT)
Just calls RigidBodyState_3DoF constructor
Expand source code
class RigidBody(RigidBody_3DoF): """ 6DoF version of RigidBody_3DoF. Calculates angular velocities and accelerations Interface: Properties: .state = RigidBodyState (pos, vel, orientation, angVel) .time = currentSimTime External functions wrapped by this class .forceFunc(time, state) .inertiaFunc(time, state) Methods .timeStep(deltaT) """ #TODO: Moment of Inertia Tensor? Required for aircraft def __init__(self, rigidBodyState, forceParam, inertiaParam, integrationMethod="Euler", discardedTimeStepCallback=None, simDefinition=None): ''' Just calls RigidBodyState_3DoF constructor ''' super().__init__(rigidBodyState, forceParam, inertiaParam, integrationMethod=integrationMethod, simDefinition=simDefinition, discardedTimeStepCallback=discardedTimeStepCallback) self.lastStateDerivative = RigidBodyStateDerivative(rigidBodyState.velocity, Vector(0,0,0), rigidBodyState.angularVelocity, AngularVelocity(0,0,0)) def getRigidBodyStateDerivative(self, time, state): # Forces are expected to be calculated in a body frame, where each coordinate axis is aligned with a pricipal axis appliedForce_localFrame = self.forceFunc(time, state) inertia = self.inertiaFunc(time, state) # Get CG velocity relative to body geometry dt = 1e-8 inertia2 = self.inertiaFunc(time+dt, state) CGVel_local = (inertia2.CG - inertia.CG) / dt CGVel_global = state.orientation.rotate(CGVel_local) ### Translation - calculated in global frame ### #Convert from local to global frame fVec_global = state.orientation.rotate(appliedForce_localFrame.force) linAccel_global = fVec_global / inertia.mass ### Rotation - calculated in local frame (Euler equations) ### # convert angular velocity to global frame - to be added to orientation angVel_global = AngularVelocity(*state.orientation.rotate(state.angularVelocity)) # Will be transformed into a quaternion once multiplied by a timestep # Calc angular acceleration (in local frame) moi = inertia.MOI dAngVelDtX = (appliedForce_localFrame.moment.X + (moi.Y - moi.Z) * state.angularVelocity.Y * state.angularVelocity.Z) / moi.X dAngVelDtY = (appliedForce_localFrame.moment.Y + (moi.Z - moi.X) * state.angularVelocity.Z * state.angularVelocity.X) / moi.Y dAngVelDtZ = (appliedForce_localFrame.moment.Z + (moi.X - moi.Y) * state.angularVelocity.X * state.angularVelocity.Y) / moi.Z dAngVelDt = AngularVelocity(dAngVelDtX, dAngVelDtY, dAngVelDtZ) return RigidBodyStateDerivative(state.velocity+CGVel_global, linAccel_global, angVel_global, dAngVelDt)
Ancestors
Subclasses
Methods
def getRigidBodyStateDerivative(self, time, state)
-
Expand source code
def getRigidBodyStateDerivative(self, time, state): # Forces are expected to be calculated in a body frame, where each coordinate axis is aligned with a pricipal axis appliedForce_localFrame = self.forceFunc(time, state) inertia = self.inertiaFunc(time, state) # Get CG velocity relative to body geometry dt = 1e-8 inertia2 = self.inertiaFunc(time+dt, state) CGVel_local = (inertia2.CG - inertia.CG) / dt CGVel_global = state.orientation.rotate(CGVel_local) ### Translation - calculated in global frame ### #Convert from local to global frame fVec_global = state.orientation.rotate(appliedForce_localFrame.force) linAccel_global = fVec_global / inertia.mass ### Rotation - calculated in local frame (Euler equations) ### # convert angular velocity to global frame - to be added to orientation angVel_global = AngularVelocity(*state.orientation.rotate(state.angularVelocity)) # Will be transformed into a quaternion once multiplied by a timestep # Calc angular acceleration (in local frame) moi = inertia.MOI dAngVelDtX = (appliedForce_localFrame.moment.X + (moi.Y - moi.Z) * state.angularVelocity.Y * state.angularVelocity.Z) / moi.X dAngVelDtY = (appliedForce_localFrame.moment.Y + (moi.Z - moi.X) * state.angularVelocity.Z * state.angularVelocity.X) / moi.Y dAngVelDtZ = (appliedForce_localFrame.moment.Z + (moi.X - moi.Y) * state.angularVelocity.X * state.angularVelocity.Y) / moi.Z dAngVelDt = AngularVelocity(dAngVelDtX, dAngVelDtY, dAngVelDtZ) return RigidBodyStateDerivative(state.velocity+CGVel_global, linAccel_global, angVel_global, dAngVelDt)
class RigidBody_3DoF (rigidBodyState, forceParam, inertiaParam, startTime=0, integrationMethod='Euler', discardedTimeStepCallback=None, simDefinition=None)
-
Interface:
Properties
.state = RigidBodyState (pos, vel, orientation, angVel) .time = currentSimTime
Methods
.timeStep(deltaT) -> advances simulation by deltaT
Expand source code
class RigidBody_3DoF: """Interface: ### Properties ### .state = RigidBodyState (pos, vel, orientation, angVel) .time = currentSimTime ### Methods ### .timeStep(deltaT) -> advances simulation by deltaT """ ''' forceParam and inertiaParam should be functions, they will be passed: 1) Simulation Time 2) RigidBodyState object containing the rigidBody's Position, Velocity, Orientation and AngularVelocity Functions are expected to return: 1) forceParam: A Force object containing the total force and moment applied to the rigidBody at it's CG location in the local frame of reference 2) inertiaParam: An Inertia object containing the mass of the object (other fields ignored) If the rigid body is representing a CompositeObject, pass in a reference to the getMass function here ''' def __init__(self, rigidBodyState, forceParam, inertiaParam, startTime=0, integrationMethod="Euler", discardedTimeStepCallback=None, simDefinition=None): self.time = startTime self.state = rigidBodyState self.forceFunc = forceParam self.inertiaFunc = inertiaParam self.integrate = integratorFactory(integrationMethod=integrationMethod, simDefinition=simDefinition, discardedTimeStepCallback=discardedTimeStepCallback) self.lastStateDerivative = RigidBodyStateDerivative_3DoF(rigidBodyState.velocity, Vector(0,0,0)) def getRigidBodyStateDerivative(self, time, state): force = self.forceFunc(time, state).force DposDt = state.velocity DvelDt = force / self.inertiaFunc(time, state) return RigidBodyStateDerivative_3DoF(DposDt, DvelDt) def timeStep(self, deltaT): # TODO: Current derivative estimates are first-order, replace with a more accurate iterative method self.state.estimatedDerivative = self.lastStateDerivative integrationResult = self.integrate(self.state, self.time, self.getRigidBodyStateDerivative, deltaT) # This is where the simulation time and state are kept track of self.time += integrationResult.dt self.state = integrationResult.newValue # Save for next time step self.lastStateDerivative = integrationResult.derivativeEstimate return integrationResult
Subclasses
Methods
def getRigidBodyStateDerivative(self, time, state)
-
Expand source code
def getRigidBodyStateDerivative(self, time, state): force = self.forceFunc(time, state).force DposDt = state.velocity DvelDt = force / self.inertiaFunc(time, state) return RigidBodyStateDerivative_3DoF(DposDt, DvelDt)
def timeStep(self, deltaT)
-
Expand source code
def timeStep(self, deltaT): # TODO: Current derivative estimates are first-order, replace with a more accurate iterative method self.state.estimatedDerivative = self.lastStateDerivative integrationResult = self.integrate(self.state, self.time, self.getRigidBodyStateDerivative, deltaT) # This is where the simulation time and state are kept track of self.time += integrationResult.dt self.state = integrationResult.newValue # Save for next time step self.lastStateDerivative = integrationResult.derivativeEstimate return integrationResult
class StatefulRigidBody (rigidBodyState, forceParam, inertiaParam, integrationMethod='Euler', discardedTimeStepCallback=None, simDefinition=None)
-
Intended to represent rigid bodies that have additional stateful properties outside of their rigd body state (ex. tank levels, actuator positions). State is defined a by a
StateList
instead of by aRigidBodyState
.Just calls RigidBodyState_3DoF constructor
Expand source code
class StatefulRigidBody(RigidBody): ''' Intended to represent rigid bodies that have additional stateful properties outside of their rigd body state (ex. tank levels, actuator positions). State is defined a by a `StateList` instead of by a `MAPLEAF.Motion.RigidBodyState`. ''' def __init__(self, rigidBodyState, forceParam, inertiaParam, integrationMethod="Euler", discardedTimeStepCallback=None, simDefinition=None): super().__init__(rigidBodyState, forceParam, inertiaParam, integrationMethod, discardedTimeStepCallback, simDefinition) self.state = StateList([ rigidBodyState ]) self.derivativeFuncs = [ self.getRigidBodyStateDerivative ] def addStateVariable(self, name, currentValue, derivativeFunction): ''' Pass in the current value of a parameter which needs to be integrated, and a derivative function Derivative function should accept the current time and StateList as inputs and return the derivative of the new parameter ''' self.state.addStateVariable(name, currentValue) self.derivativeFuncs.append(derivativeFunction) def getStateDerivative(self, time, state): return StateList([ derivativeFunc(time, state) for derivativeFunc in self.derivativeFuncs ], _nameToIndexMap=state.nameToIndexMap) def timeStep(self, deltaT): # TODO: Current derivative estimates are first-order, replace with a more accurate iterative method self.state.estimatedDerivative = self.lastStateDerivative integrationResult = self.integrate(self.state, self.time, self.getStateDerivative, deltaT) # This is where the simulation time and state are kept track of self.time += integrationResult.dt self.state = integrationResult.newValue # Save for next time step self.lastStateDerivative = integrationResult.derivativeEstimate return integrationResult
Ancestors
Methods
def addStateVariable(self, name, currentValue, derivativeFunction)
-
Pass in the current value of a parameter which needs to be integrated, and a derivative function Derivative function should accept the current time and StateList as inputs and return the derivative of the new parameter
Expand source code
def addStateVariable(self, name, currentValue, derivativeFunction): ''' Pass in the current value of a parameter which needs to be integrated, and a derivative function Derivative function should accept the current time and StateList as inputs and return the derivative of the new parameter ''' self.state.addStateVariable(name, currentValue) self.derivativeFuncs.append(derivativeFunction)
def getStateDerivative(self, time, state)
-
Expand source code
def getStateDerivative(self, time, state): return StateList([ derivativeFunc(time, state) for derivativeFunc in self.derivativeFuncs ], _nameToIndexMap=state.nameToIndexMap)
def timeStep(self, deltaT)
-
Expand source code
def timeStep(self, deltaT): # TODO: Current derivative estimates are first-order, replace with a more accurate iterative method self.state.estimatedDerivative = self.lastStateDerivative integrationResult = self.integrate(self.state, self.time, self.getStateDerivative, deltaT) # This is where the simulation time and state are kept track of self.time += integrationResult.dt self.state = integrationResult.newValue # Save for next time step self.lastStateDerivative = integrationResult.derivativeEstimate return integrationResult