|
FhSim
3.1.0
Marine systems simulation
|
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**: 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 | |
| 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. | |
|
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.
|
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.
| relVel_d | The velocity of the body relative to the fluid, in the body frame, m/s. | |
| rho | The density of the sea water, kg/m³. | |
| frontalArea | The frontal area of the hull, m². | |
| axialDragCoeff | The drag coefficient on that area. | |
| [out] | wrench_d | The force in [0..2] and the moment about the origin in [3..5]. |
|
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.
| propulsionPower | The propulsion power actually drawn, W, already clamped. |
| surgeSpeed | The forward speed through the water, m/s. Unused here. |
|
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.
| relVel_d | The velocity of the body relative to the fluid, in the body frame, m/s. | |
| omega_d | The angular velocity in the body frame, rad/s. | |
| rho | The density of the sea water, kg/m³. | |
| crossFlowDragCoeff | The two-dimensional cross-flow drag coefficient of a section. | |
| sectionCount | The number of sections. | |
| sectionX | The body-frame x of each section centroid, m. | |
| sectionDiameter | The cross-flow diameter of each section, m. | |
| sectionLength | The length of each section, m. | |
| [out] | wrench_d | The force in [0..2] and the moment about the origin in [3..5]. Components 0 and 3 always come back exactly zero. |
|
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,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:
Quater, which is smooth and whose dominant entry is the restoring moment,Pos through the submerged fraction, which is zero while the vehicle is submerged and \(C^1\) when it is not,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.
|
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.
|
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.
| length | The hull length, m. |
| diameter | The hull diameter, m. |
| averageDensity | The mean density of the whole vehicle, kg/m³. |
|
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.
| globalStateIndex | One of the m_IStateXxx members. |
|
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.
| rollRate | The roll rate \(p\), rad/s. |
| linear | The linear coefficient, N·m·s. |
| quadratic | The quadratic coefficient, N·m·s². |
|
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.
| plane | Which fin plane, that is which transverse component of the inflow and which moment the wrench ends up in. | |
| relVel_d | The velocity of the body relative to the fluid, in the body frame, m/s. | |
| omega_d | The angular velocity in the body frame, rad/s. | |
| deflection | The commanded deflection, rad, before the clamp. | |
| rudder | The fin geometry and coefficients. | |
| [out] | wrench_d | The 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. |
|
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.
| plane | Which fin plane. | |
| relVel_d | The velocity of the body relative to the fluid, in the body frame, m/s. | |
| omega_d | The angular velocity in the body frame, rad/s. | |
| deflection | The commanded deflection, rad, before the clamp. | |
| rudder | The fin geometry and coefficients. | |
| [out] | dWrench_dVelocity | The 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_dDeflection | The derivative of each of the six wrench components with respect to the commanded deflection. Identically zero outside the clamp. |
|
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.
| axisDepth | The 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. |
| radius | The hull radius, m. |
|
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.
| simObjectName | The instance name, so the message says which vehicle. |
| diveAngle | The dive angle to evaluate the check at, rad. Non-positive disables the check. |
|
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.
|
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.