FhSim  3.1.0
Marine systems simulation
Loading...
Searching...
No Matches
Auv Class Reference
+ Inheritance diagram for Auv:
+ Collaboration diagram for Auv:

Public Member Functions

 Auv (std::string simObjectName, ISimObjectCreator *creator)
 
 ~Auv () override
 
void OdeFcn (const double dT, const double *const adX, double *const adXDot) const override
 Calculates the state derivatives.
 
void FinalSetup (const double dT, const double *const adX, ISimObjectCreator *const creator) override
 
Jacobian support for implicit integration
bool HasJacobians () const override
 
void OdeJacobian (double T, const double *X, double *J, int nStates) override
 
int GetJacobianSparsity (int nStates, int *rowPtr, int *colIdx) override
 
bool HasPortJacobians () const override
 
void InputPortJacobian (const std::string &portName, double T, const double *X, double *dF_dInput, int portSize, int nStates) override
 
void OutputPortJacobian (const std::string &portName, double T, const double *X, double *dPort_dX, int portSize, int nStates, int index) override
 

Static Public Member Functions

static AuvHullProperties HullPropertiesOf (double length, double diameter, double averageDensity)
 
static double SubmergedFraction (double axisDepth, double radius)
 
static void AxialDragWrench (const double relVel_d[3], double rho, double frontalArea, double axialDragCoeff, double wrench_d[6])
 
static void CrossFlowStripWrench (const double relVel_d[3], const double omega_d[3], double rho, double crossFlowDragCoeff, int sectionCount, const double *sectionX, const double *sectionDiameter, const double *sectionLength, double wrench_d[6])
 
static double RollDampingMoment (double rollRate, double linear, double quadratic)
 
static void RudderWrench (AuvRudderPlane plane, const double relVel_d[3], const double omega_d[3], double deflection, const AuvRudderProperties &rudder, double wrench_d[6])
 
static void RudderWrenchJacobian (AuvRudderPlane plane, const double relVel_d[3], const double omega_d[3], double deflection, const AuvRudderProperties &rudder, double dWrench_dVelocity[6][6], double dWrench_dDeflection[6])
 

Static Public Attributes

static const int kSectionCount = 3
 The number of hull sections the cross-flow strip theory divides the hull into.
 
static const double kCrossFlowSpeedEpsilon
 
static const double kInflowSpeedEpsilon
 

Protected Member Functions

virtual void CalcFoundation (const double dT, const double *const adX)
 Zeroes the force accumulator and caches the pose, the rotation matrix and the fluid velocity.
 
virtual void AddExternalForces (const double dT, const double *const adX)
 Adds the ExternalForce input port, at the body origin.
 
virtual void AddInternalForces (const double dT)
 Adds the weight at COG and the buoyancy at the origin.
 
virtual void AddThrustForces (const double dT, const double *const adX)
 Adds the thruster wrench and caches the propulsion power the battery pays for.
 
virtual void AddHullHydroForces ()
 Adds the axial hull drag, the cross-flow strip wrench and the explicit roll damping.
 
virtual void AddRudderForces (const double dT, const double *const adX)
 Adds the wrench of both fin planes, and caches the two commanded deflections.
 
void WarnIfTheFinsCannotDive (const std::string &simObjectName, double diveAngle) const
 
double AuthorityCheckCruiseSpeed () const
 The forward speed the dive-authority check assumes, from this vehicle's own parameters.
 
virtual double ComputeThrust (double propulsionPower, double surgeSpeed) const
 
double ClampedPropulsionPower (double commandedPower) const
 Clamps a commanded power to the range the thruster accepts, [0, MaxPropulsionPower].
 
virtual void CalcDerivatives (double *const adXDot)
 Turns the accumulated wrench into the state derivatives.
 
virtual void CalcOutput (const double dT, const double *const adX)
 Recomputes every output port from the given time and state.
 
void CalcOutputCommon (const double dT, const double *const adX) const
 Trampoline registered as the common computation of every output port.
 
int LocalIndex (unsigned long globalStateIndex) const
 
Output port functions
const double * OutPos (const double dT, const double *const adX)
 
const double * OutVel (const double dT, const double *const adX)
 
const double * OutQuater (const double dT, const double *const adX)
 
const double * OutEuler (const double dT, const double *const adX)
 
const double * OutHeading (const double dT, const double *const adX)
 
const double * OutPitch (const double dT, const double *const adX)
 
const double * OutDepth (const double dT, const double *const adX)
 
const double * OutSpeed (const double dT, const double *const adX)
 
const double * OutAltitude (const double dT, const double *const adX)
 
const double * OutSeaDepth (const double dT, const double *const adX)
 
const double * OutBattery (const double dT, const double *const adX)
 
const double * OutBatteryFraction (const double dT, const double *const adX)
 
const double * OutThrust (const double dT, const double *const adX)
 

Protected Attributes

double m_length = 0.0
 The hull length, m. Kept because the visualisation draws it.
 
AuvHullProperties m_hull
 The derived radius, volume, mass and inertia.
 
double m_cOG [3]
 The centre of gravity in the body frame, m.
 
double m_massInverse [6][6]
 The inverse of the constant mass matrix.
 
double m_rhoWater
 The density of the surrounding fluid, kg/m³.
 
double m_initialBattery
 The battery energy at the start of the mission, J.
 
double m_thrustPerPower
 Thrust per watt of propulsion power, N/W.
 
double m_maxPropulsionPower
 The largest propulsion power accepted, W.
 
double m_hotelPower
 The constant payload and computer load, W.
 
double m_thrusterPos [3]
 The thruster position in the body frame, m.
 
double m_axialDragCoeff
 The hull drag coefficient on the frontal area.
 
double m_crossFlowDragCoeff
 The two-dimensional cross-flow drag coefficient of a section.
 
double m_sectionX [kSectionCount]
 The body-frame x of each section centroid, m.
 
double m_sectionDiameter [kSectionCount]
 The cross-flow diameter of each section, m.
 
double m_sectionLength [kSectionCount]
 The length of each section, m.
 
double m_rollDampingLinear
 The linear roll damping, N·m·s.
 
double m_rollDampingQuadratic
 The quadratic roll damping, N·m·s².
 
AuvRudderProperties m_rudder
 The fin geometry and coefficients.
 
environment::EnvironmentProvider * m_environment = nullptr
 The shared environment.
 
PrintDuringExec * m_print = nullptr
 The simulation logger, null under a headless or mock host.
 
ISignalPort * m_inThrustPower = nullptr
 Commanded propulsion power, W.
 
ISignalPort * m_inRudderYaw = nullptr
 Commanded yaw-plane fin deflection, rad.
 
ISignalPort * m_inRudderDive = nullptr
 Commanded dive-plane fin deflection, rad.
 
ISignalPort * m_inExternalForce = nullptr
 An external force in NED, N.
 
ICommonComputation * m_commonCalc = nullptr
 Shared recomputation behind every output port.
 
double ma_adSumForces [6] = {0, 0, 0, 0, 0, 0}
 Body-frame force in [0..2] and moment about the origin in [3..5].
 
double ma_mdR [3][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}
 The body-to-NED rotation matrix.
 
double ma_adXVel_n [3] = {0, 0, 0}
 The velocity of the origin in NED, m/s.
 
double ma_adWaterVel_d [3] = {0, 0, 0}
 The fluid velocity at the body, in the body frame, m/s.
 
const double * ma_adXPos_n = nullptr
 The NED position state.
 
const double * ma_adXQuater = nullptr
 The attitude quaternion state.
 
const double * ma_adXVel_d = nullptr
 The body-frame velocity state.
 
const double * ma_adXOmega = nullptr
 The body-frame angular velocity state.
 
const double * ma_adXBattery = nullptr
 The battery energy state.
 
double ma_adRelVel_d [3] = {0, 0, 0}
 The velocity of the body relative to the fluid, in the body frame, m/s.
 
double ma_dPropulsionPower = 0.0
 The clamped propulsion power of the current derivative evaluation, W.
 
double ma_dCommandedPower = 0.0
 The commanded propulsion power of the current derivative evaluation, W, before the clamp.
 
double ma_dRudderDive = 0.0
 The commanded dive-plane deflection of the current derivative evaluation, rad, before the clamp.
 
double ma_dRudderYaw = 0.0
 The commanded yaw-plane deflection of the current derivative evaluation, rad, before the clamp.
 
double m_outPos [3] = {0, 0, 0}
 Position in NED, m.
 
double m_outVel [3] = {0, 0, 0}
 Velocity in NED, m/s.
 
double m_outQuater [4] = {1, 0, 0, 0}
 Attitude quaternion.
 
double m_outEuler [3] = {0, 0, 0}
 Roll, pitch and yaw, rad.
 
double m_outDepth = 0.0
 Depth below the surface, m.
 
double m_outSpeed = 0.0
 Forward speed through the water, m/s.
 
double m_outAltitude = 0.0
 Height above the seafloor, m.
 
double m_outSeaDepth = 0.0
 Seafloor depth below the surface, m.
 
double m_outBatteryFraction = 0.0
 Remaining battery energy as a fraction of InitialBattery.
 
double m_outThrust = 0.0
 Thrust along body x, N.
 
State indices

These five are model-global**: ISimObjectCreator::AddState returns an offset into the whole model's state vector, accumulated over every SimObject before this one. So they are the right indices in OdeFcn(), CalcDerivatives() and the output port functions, all of which are handed the global state vector.

They are the wrong indices in OdeJacobian(), InputPortJacobian() and OutputPortJacobian(), which are handed a slice local to this SimObject and a local dimension. The two cases look identical at the call site — both are X + m_IStateSomething — which is exactly why this distinction is spelled out here. Subtract m_IStateBase, through LocalIndex(), in every Jacobian callback.

See issue 0032 — Jacobian callbacks are indexed with global state offsets, so they are wrong for any object that is not first.

unsigned long m_IStatePos = 0
 Global index of the NED position state.
 
unsigned long m_IStateQuater = 0
 Global index of the attitude quaternion state.
 
unsigned long m_IStateLocalVel = 0
 Global index of the body-frame velocity state.
 
unsigned long m_IStateOmega = 0
 Global index of the body-frame angular velocity state.
 
unsigned long m_IStateBattery = 0
 Global index of the battery energy state.
 
unsigned long m_IStateBase = 0
 Global index of this object's first state, cached in the constructor.
 

Constructor & Destructor Documentation

◆ ~Auv()

Auv::~Auv ( )
override

Releases the visualisation geometry.

The SimObject interface has no render teardown hook, so the scene node and the ManualObjects RenderInit() created are destroyed here. Under a headless build, and under any host that never renders, there is nothing to release.

Member Function Documentation

◆ AxialDragWrench()

static void Auv::AxialDragWrench ( const double  relVel_d[3],
double  rho,
double  frontalArea,
double  axialDragCoeff,
double  wrench_d[6] 
)
static

The body-frame wrench of the axial hull drag.

\[ F_x = -\tfrac12 \rho A_f C_d \left|u_{rel}\right| u_{rel} \]

The other five components are zero, and that is the point of the routine being a pure function: the transverse resistance belongs to the cross-flow strip theory, and adding it here as well would double-count it. A unit test feeds this a purely transverse relative velocity and asserts that every component comes back zero.

Parameters
relVel_dThe velocity of the body relative to the fluid, in the body frame, m/s.
rhoThe density of the sea water, kg/m³.
frontalAreaThe frontal area of the hull, m².
axialDragCoeffThe drag coefficient on that area.
[out]wrench_dThe force in [0..2] and the moment about the origin in [3..5].

◆ ComputeThrust()

virtual double Auv::ComputeThrust ( double  propulsionPower,
double  surgeSpeed 
) const
protectedvirtual

The thrust delivered by a given propulsion power.

The default implementation is the speed-independent \(T = k_T P\) of the specification. It is virtual, and takes the speed it does not use, so that a bollard-pull blend can be substituted later without touching any caller.

Parameters
propulsionPowerThe propulsion power actually drawn, W, already clamped.
surgeSpeedThe forward speed through the water, m/s. Unused here.
Returns
The thrust along body x, N.

◆ CrossFlowStripWrench()

static void Auv::CrossFlowStripWrench ( const double  relVel_d[3],
const double  omega_d[3],
double  rho,
double  crossFlowDragCoeff,
int  sectionCount,
const double *  sectionX,
const double *  sectionDiameter,
const double *  sectionLength,
double  wrench_d[6] 
)
static

The body-frame wrench of the cross-flow strip theory.

For each section \(k\) at \(\mathbf{r}_k = (\mathrm{sectionX}_k, 0, 0)\) the local relative velocity is \(\mathbf{v}_k = \mathbf{v}_{rel} + \boldsymbol{\omega} \times \mathbf{r}_k\), of which only the cross-flow components are used, and the section contributes

\[ \mathbf{F}_k = (0,\; -c_k w_k v_{ky},\; -c_k w_k v_{kz}), \qquad \mathbf{M}_k = \mathbf{r}_k \times \mathbf{F}_k, \qquad c_k = \tfrac12 \rho C_{d,cf} D_k L_k . \]

It is a pure function for the same reason AxialDragWrench() is: the surge component of the result must be identically zero, or the transverse resistance is counted twice, and that is far easier to assert here than through a simulation.

Parameters
relVel_dThe velocity of the body relative to the fluid, in the body frame, m/s.
omega_dThe angular velocity in the body frame, rad/s.
rhoThe density of the sea water, kg/m³.
crossFlowDragCoeffThe two-dimensional cross-flow drag coefficient of a section.
sectionCountThe number of sections.
sectionXThe body-frame x of each section centroid, m.
sectionDiameterThe cross-flow diameter of each section, m.
sectionLengthThe length of each section, m.
[out]wrench_dThe force in [0..2] and the moment about the origin in [3..5]. Components 0 and 3 always come back exactly zero.

◆ HasJacobians()

bool Auv::HasJacobians ( ) const
override

Partial analytical Jacobian. Three blocks are supplied:

  • d(PosDot)/d(LocalVel) = R(q), the rotation matrix,
  • d(QuaterDot)/d(Omega) = T(q), the quaternion rate matrix,
  • the whole six by six velocity block, that is the derivative of the six body-frame accelerations with respect to LocalVel and Omega.

The velocity block is complete rather than partial, and that is worth stating plainly because it was partial until the fins arrived. Every term in the force model that depends on a velocity at all is in it: the axial hull drag on surge, the cross-flow strip theory on sway, heave, pitch rate and yaw rate, the explicit roll damping on roll rate, and both fin planes. Gravity, buoyancy, the thrust and the external force do not depend on a velocity, so nothing is missing.

Until increment E the block was block-diagonal — surge alone, then a four by four in the transverse states, then roll — and the isolation of surge from the transverse states was an assertion the tests made. The fins break that isolation, and legitimately: a fin's dynamic pressure carries \(u^2\), so its side force depends on the forward speed and its drag depends on the sideslip. Writing only the old blocks would therefore now be writing wrong numbers, not merely incomplete ones.

Deliberately left to finite differences, in the style of TrawlDoorBase:

  • the dependence of the gravity and buoyancy wrench on Quater, which is smooth and whose dominant entry is the restoring moment,
  • the dependence of the buoyancy on Pos through the submerged fraction, which is zero while the vehicle is submerged and \(C^1\) when it is not,
  • the whole quaternion-normalisation correction term of OmegaToQDot.

A test that checks this class against finite differences must therefore check the supplied blocks, not the whole matrix. tests/Auv_Test.cpp does exactly that.

Every callback below is handed a state slice local to this SimObject**, so it indexes through LocalIndex(). See m_IStateBase.

◆ HasPortJacobians()

bool Auv::HasPortJacobians ( ) const
override

Analytic port Jacobians for the two input ports whose effect is exactly known.

  • ThrustPower: the thrust is \(k_T P\) inside the clamp, so the surge acceleration, the thruster moment and the battery discharge are all linear in the commanded power. Outside the clamp, and while the battery is flat, the derivative of the saturation is zero and so is the whole column.
  • ExternalForce: a NED force applied at the body origin, so it produces no moment and enters the body-frame acceleration through \(M^{-1} R(q)^{\mathsf{T}}\).
  • RudderYaw and RudderDive: the commanded deflection enters only through the angle of attack, so the derivative is the fin wrench's coefficient slope inside the mechanical stop and identically zero outside it. Only the commanded plane's column is written; the other plane does not see the command at all.

Both callbacks clear the whole destination matrix before writing their non-zero entries. JacobianAssembler gives every direct port link the same two workspaces and clears them only on the composed-link path, so a callback that writes only its non-zero entries — which is what every other model in FhSim does — reads back the previous link's numbers in the cells it skipped, and the assembler multiplies them into this link's block. That asymmetry is a defect of the FhSim core (fhsim/src/engine/model/JacobianAssembler.cpp:503-507 against :418-420), not of this class; clearing here is what makes this class immune to it.

Auditable assumption: whether the power command sits inside the clamp is read from ma_dCommandedPower — and likewise which side of the mechanical stop each fin command sits on, from ma_dRudderDive and ma_dRudderYaw — which the most recent OdeFcn() left behind, because an input port cannot be queried from a Jacobian callback — the callback is handed a local state slice and ISignalPort::GetPortValue needs the global one. Every path FhSim takes evaluates the derivatives at a state before asking for the Jacobian there, so the cached command belongs to the state being differentiated. Should that ever stop holding, the consequence is a derivative taken on the wrong side of the saturation, not an invalid read.

Auditable gap on the fin columns: no fixture in tests/in/Auv/ drives RudderYaw or RudderDive from another SimObject's output port, so the assembled-system Jacobian check never reaches those two columns. What is checked directly is the quantity they are built from — Auv_RudderWrenchJacobianMatchesFiniteDifferences finite-differences RudderWrench() with respect to the deflection and compares. The residual risk is therefore in the wiring of this function, not in the physics.

Auditable omissions on the output side: only Pos and Battery have an OutputPortJacobian, because those two are the only outputs that are an identity on a state. Every other output port — Vel, Euler, Speed, Depth, Thrust and the rest — is left unwritten, which the assembler reads as a zero block. A model that drives another stateful SimObject from one of those ports and has no other reason to run the numerical pass would therefore get a zero where a derivative belongs. This is the standing convention of the library (see CenterWeight), not a property of this class, and it is recorded here so that it is auditable rather than invisible.

◆ HullPropertiesOf()

static AuvHullProperties Auv::HullPropertiesOf ( double  length,
double  diameter,
double  averageDensity 
)
static

Derives the mass properties of a solid cylindrical hull.

This is a pure function so that the derived geometry can be unit tested against the closed-form cylinder values without a host simulation.

Parameters
lengthThe hull length, m.
diameterThe hull diameter, m.
averageDensityThe mean density of the whole vehicle, kg/m³.
Returns
The derived radius, volume, mass and inertia.

◆ LocalIndex()

int Auv::LocalIndex ( unsigned long  globalStateIndex) const
inlineprotected

Turns one of the global state indices above into an index within this object's own slice, which is what every Jacobian callback is given.

Parameters
globalStateIndexOne of the m_IStateXxx members.
Returns
The same state, counted from this object's first state.

◆ RollDampingMoment()

static double Auv::RollDampingMoment ( double  rollRate,
double  linear,
double  quadratic 
)
static

The explicit roll damping moment, \(-(K_l + K_q |p|) p\).

Strip theory contributes nothing about the x axis, so this is the only thing in the class that damps roll.

Parameters
rollRateThe roll rate \(p\), rad/s.
linearThe linear coefficient, N·m·s.
quadraticThe quadratic coefficient, N·m·s².
Returns
The moment about the body x axis, N·m.

◆ RudderWrench()

static void Auv::RudderWrench ( AuvRudderPlane  plane,
const double  relVel_d[3],
const double  omega_d[3],
double  deflection,
const AuvRudderProperties rudder,
double  wrench_d[6] 
)
static

The body-frame wrench of one fin plane.

See the "Fins" section of the class documentation for the formulation and for the sign convention. It is a pure function for the same reason AxialDragWrench() and CrossFlowStripWrench() are: a fin sign error is invisible in a trajectory and obvious in a three-line unit test.

Parameters
planeWhich fin plane, that is which transverse component of the inflow and which moment the wrench ends up in.
relVel_dThe velocity of the body relative to the fluid, in the body frame, m/s.
omega_dThe angular velocity in the body frame, rad/s.
deflectionThe commanded deflection, rad, before the clamp.
rudderThe fin geometry and coefficients.
[out]wrench_dThe force in [0..2] and the moment about the body origin in [3..5]. The dive plane writes nothing into the sway force or the yaw moment, and the yaw plane nothing into the heave force or the pitch moment.

◆ RudderWrenchJacobian()

static void Auv::RudderWrenchJacobian ( AuvRudderPlane  plane,
const double  relVel_d[3],
const double  omega_d[3],
double  deflection,
const AuvRudderProperties rudder,
double  dWrench_dVelocity[6][6],
double  dWrench_dDeflection[6] 
)
static

The exact derivative of RudderWrench().

Supplied because the fins couple the surge to the transverse states, which the hull terms do not, so leaving them out would make the velocity block of OdeJacobian() wrong rather than merely incomplete. The chain is

\[ \frac{\partial q_{dyn}}{\partial u_f} = \rho A u_f, \qquad \frac{\partial \alpha_{flow}}{\partial u_f} = \frac{-s_f}{u_f^2 + s_f^2}, \qquad \frac{\partial \alpha}{\partial \delta} = 1 \;\text{inside the clamp}, \]

with the flow angle entering both the coefficients, through \(\alpha = \delta + \alpha_{flow}\), and the resolution into the body frame.

A unit test finite-differences RudderWrench() and compares, so these entries are checked directly rather than only through the assembled system.

Parameters
planeWhich fin plane.
relVel_dThe velocity of the body relative to the fluid, in the body frame, m/s.
omega_dThe angular velocity in the body frame, rad/s.
deflectionThe commanded deflection, rad, before the clamp.
rudderThe fin geometry and coefficients.
[out]dWrench_dVelocityThe derivative of each of the six wrench components with respect to the six body-frame velocity components, in the order \((u, v, w, p, q, r)\) of the relative velocity and the angular velocity.
[out]dWrench_dDeflectionThe derivative of each of the six wrench components with respect to the commanded deflection. Identically zero outside the clamp.

◆ SubmergedFraction()

static double Auv::SubmergedFraction ( double  axisDepth,
double  radius 
)
static

The submerged fraction of a horizontal cylinder crossing the free surface.

The wetted area of a circular cross-section whose centre lies axisDepth below the waterline is the circular segment

\[ A(d) = r^2 \arccos\!\left(-\frac{d}{r}\right) + d \sqrt{r^2 - d^2}, \]

so the submerged fraction is \(A(d) / (\pi r^2)\), clamped to \([0, 1]\). It is \(0\), \(\tfrac12\) and \(1\) at \(d = -r\), \(0\) and \(+r\), and its derivative \(\mathrm{d}A/\mathrm{d}d = 2\sqrt{r^2 - d^2}\) — the chord width — vanishes at both tangent points, so the transition is \(C^1\) and an adaptive integrator is not driven to its minimum step when the vehicle surfaces.

Parameters
axisDepthThe depth of the hull axis below the free surface, m, positive down, so it is the NED z of the axis minus the surface elevation.
radiusThe hull radius, m.
Returns
The submerged fraction, in [0, 1].

◆ WarnIfTheFinsCannotDive()

void Auv::WarnIfTheFinsCannotDive ( const std::string &  simObjectName,
double  diveAngle 
) const
protected

Warns when the fins cannot overcome the righting moment at the design dive angle.

Called from the constructor. See the "dive-authority check" part of the class documentation for the two assumptions the message names.

Parameters
simObjectNameThe instance name, so the message says which vehicle.
diveAngleThe dive angle to evaluate the check at, rad. Non-positive disables the check.

Member Data Documentation

◆ kCrossFlowSpeedEpsilon

const double Auv::kCrossFlowSpeedEpsilon
static

The regularisation added under the square root of the cross-flow speed.

\(w_k = \sqrt{v_{ky}^2 + v_{kz}^2 + \varepsilon}\) rather than the bare magnitude, so that the force stays differentiable at zero cross-flow and the adaptive integrator is not pinned at its minimum step there. At 1e-12 the floor it puts under \(w_k\) is 1e-6 m/s, which is a force of order 1e-10 N on the default hull — far below any tolerance in the model, and far above the scale at which the derivative would misbehave.

◆ kInflowSpeedEpsilon

const double Auv::kInflowSpeedEpsilon
static

The regularisation added to the squared inflow speed in the fin derivatives.

The fin force itself is well behaved at zero inflow, because the dynamic pressure goes to zero quadratically, but its derivative contains \(1/(u_f^2 + s_f^2)\) from the flow angle. This floor keeps that finite at a standstill, where the force it multiplies is identically zero anyway.


The documentation for this class was generated from the following file: