Contact / landing / ship-deck motion

BOUNDARYS1 · dim 16

Partial / unstable. SolvSRK survives more than 0% but less than 90% of runs at the comparison noise level, without being beaten by a baseline there. Usable with margin and monitoring; validate on your own configuration. All verdicts →

Penalty-contact + sinusoidal deck heave; sea-state landing scenario

Drone dynamics & autonomy

Problem definition

Canonical benchmark implementation

Canonical RHS excerpt from the registered callable used for this benchmark cell. Expand it to verify the state equations; it is not a standalone runnable fixture.

Show canonical RHS excerpt
def _pd_controller(y, sp=None, mass=None):
    """Cascaded PD: pos error → desired attitude → torques. Returns (T, tau_x, tau_y, tau_z).

    BA-2 (2026-04-29): added optional ``mass`` kwarg so the
    factory variants in ``make_rhs_A3`` / ``make_rhs_A7`` can override
    the module-global ``MASS`` for the hover-thrust feed-forward term.
    THRUST_MAX is held at the parent airframe's value (39.24 N) since it
    represents the physical thrust ceiling; airframe-mass perturbations
    in {0.8..1.2} kg stay well within this envelope.
    """
    if sp is None:
        sp = HOVER_SP
    eff_mass = MASS if mass is None else mass
    px, py, pz = y[0], y[1], y[2]
    vx, vy, vz = y[3], y[4], y[5]
    phi, theta, psi = y[6], y[7], y[8]
    p, q, r = y[9], y[10], y[11]

    ax_d = KP_POS * (sp[0] - px) + KD_POS * (sp[3] - vx)
    ay_d = KP_POS * (sp[1] - py) + KD_POS * (sp[4] - vy)
    az_d = KP_POS * (sp[2] - pz) + KD_POS * (sp[5] - vz)

    T_des = eff_mass * (G + az_d)
    phi_des = (1.0 / G) * (ax_d * np.sin(psi) - ay_d * np.cos(psi))
    theta_des = (1.0 / G) * (ax_d * np.cos(psi) + ay_d * np.sin(psi))

    tau_x = KP_ATT * np.arctan2(np.sin(phi_des - phi), np.cos(phi_des - phi)) - KD_ATT * p
    tau_y = KP_ATT * np.arctan2(np.sin(theta_des - theta), np.cos(theta_des - theta)) - KD_ATT * q
    tau_z = KP_YAW * np.arctan2(np.sin(-psi), np.cos(-psi)) - KD_YAW * r

    T_des = np.clip(T_des, 0.0, THRUST_MAX)
    tau_x = np.clip(tau_x, -TORQUE_CLIP, TORQUE_CLIP)
    tau_y = np.clip(tau_y, -TORQUE_CLIP, TORQUE_CLIP)
    tau_z = np.clip(tau_z, -TORQUE_CLIP * 0.25, TORQUE_CLIP * 0.25)
    return T_des, tau_x, tau_y, tau_z

def _body_forces(T, phi, theta, psi):
    """Thrust-to-inertial force components."""
    cp, sp = np.cos(phi), np.sin(phi)
    ct, st = np.cos(theta), np.sin(theta)
    cy, sy = np.cos(psi), np.sin(psi)
    Fx = T * (cy * st * cp + sy * sp)
    Fy = T * (sy * st * cp - cy * sp)
    Fz = T * ct * cp
    return Fx, Fy, Fz

def _euler_kinematics(phi, theta, p, q, r):
    """Euler-angle rates from body rates. Returns (dphi, dtheta, dpsi)."""
    cp, sp = np.cos(phi), np.sin(phi)
    theta_c = np.clip(theta, -1.39, 1.39)
    tan_th = np.tan(theta_c)
    cos_th = np.cos(theta_c)
    sec_th = 1.0 / cos_th if abs(cos_th) > 1e-12 else 1e12 * np.sign(cos_th)
    dphi = p + q * sp * tan_th + r * cp * tan_th
    dtheta = q * cp - r * sp
    dpsi = (q * sp + r * cp) * sec_th
    return dphi, dtheta, dpsi

def _quad12(y, T, tau_x, tau_y, tau_z, mass=None):
    """Core 12-state quadrotor dynamics. Returns d[0:12].

    BA-2 (2026-04-29): added optional ``mass`` kwarg so the
    factory variants can override the module-global ``MASS`` for the
    translational acceleration / drag terms.
    """
    eff_mass = MASS if mass is None else mass
    phi, theta, psi = y[6], y[7], y[8]
    p, q, r = y[9], y[10], y[11]
    Fx, Fy, Fz = _body_forces(T, phi, theta, psi)

    d = np.empty(12)
    d[0] = y[3]; d[1] = y[4]; d[2] = y[5]
    d[3] = (Fx - CD * y[3]) / eff_mass
    d[4] = (Fy - CD * y[4]) / eff_mass
    d[5] = (Fz - CD * y[5]) / eff_mass - G
    d[6], d[7], d[8] = _euler_kinematics(phi, theta, p, q, r)
    d[9] = (tau_x + (IYY - IZZ) * q * r) / IXX
    d[10] = (tau_y + (IZZ - IXX) * p * r) / IYY
    d[11] = (tau_z + (IXX - IYY) * p * q) / IZZ
    return d

def rhs_A8(t, y):
    body = y[:12]
    z_deck = y[12]
    vz_deck = y[13]
    overlap = y[14]
    overlap_rate = y[15]

    T, tx, ty, tz = _pd_controller(body, sp=np.array([0., 0., 1.0, 0., 0., -0.5]))
    d_body = _quad12(body, T, tx, ty, tz)

    d_zdeck = vz_deck
    d_vzdeck = -_DECK_FREQ**2 * (z_deck - _DECK_AMP * np.sin(_DECK_FREQ * t))

    penetration = z_deck - body[2]
    pen_rate = vz_deck - body[5]
    contact_active = penetration > 0
    F_contact = contact_active * (_CONTACT_K * penetration + _CONTACT_D * pen_rate)
    d_body[5] += F_contact / MASS

    d_overlap = overlap_rate
    d_overlap_rate = (F_contact / MASS - 100.0 * overlap - 10.0 * overlap_rate)

    return np.concatenate([d_body, [d_zdeck, d_vzdeck, d_overlap, d_overlap_rate]])
Parameters
  • CD = 0.1
  • G = 9.81
  • HOVER_SP = [0, 0, 5, 0, 0, 0]
  • IXX = 0.0082
  • IYY = 0.0082
  • IZZ = 0.0148
  • KD_ATT = 2.5
  • KD_POS = 4
  • KD_YAW = 1.5
  • KP_ATT = 8
  • KP_POS = 6
  • KP_YAW = 4
  • MASS = 1
  • THRUST_MAX = 39.24
  • TORQUE_CLIP = 2
  • _CONTACT_D = 50
  • _CONTACT_K = 5000
  • _DECK_AMP = 0.3
  • _DECK_FREQ = 3.14159265359
  • sp = None
  • mass = None
Initial condition
y(0) = [0, 0, 3, 0, 0, 0, …] [shape=(16,), min=0, max=3]
Horizon
t ∈ [0, 30]

Canonical RHS excerpt captured from the same registered callable used for the published benchmark. Frozen closure values are summarized below; helper imports and solver settings are intentionally omitted.

Fingerprint

Spread: high

Default noise: low

Recommendation snapshot

Clean best: SciPy BDF

Noisy best: SolvSRK

Coverage

14 solver arms · clean + 5 noise levels

Ranked on survival, precision, and speed

Versions & freeze

Methodology →
Freeze
2026-08-13
libsolvsrk
2.3.0
SciPy
1.14
SUNDIALS
CVODE (bundled backend)

20 seeds/cell default · 14 arms · TRL 4–5 · simulation-lab validated · this page: Contact / landing / ship-deck motion (contact-landing-ship-deck-motion)

Governed SolvTune benchmark freeze; per-arm medians only. RHS definitions and raw trial rows are not published.

Self-reported by Resonix Labs · not independently verified

Results matrix

Pick an objective and a noise level to rank all arms on survival, median SCD, median nfev, and median wall time. Medians across seeds.

Objective

Best overall trade-off of survival, precision, and speed.

Noise level

#SolverSurvivalSCDnfevWallScore
1SciPy BDFSciPy
100%
10,702743 ms0.809
2SciPy RadauSciPy
100%
35,1971.91 s0.809
3SciPy RK45SciPy
100%
12,932459 ms0.809
4SciPy LSODASciPy
100%
7,627246 ms0.809
5SciPy DOP853SciPy
100%
15,182523 ms0.809
6SciPy RK23SciPy
100%
49,2681.87 s0.809
7CVODE BDFexternal
100%
8,252299 ms0.809
8CVODE Adamsexternal
100%
5,203196 ms0.809
9Tsit5external
100%
14,9882.73 s0.809
10Vern7external
100%
20,7225.18 s0.809
11Vern9external
100%
30,3065.63 s0.809
12TRBDF2external
100%
29,8956.37 s0.809
13FBDFexternal
100%
11,3875.84 s0.809
14SolvSRK
100%
24,588612 ms0.809

At Clean, best balanced arm is SciPy BDF · SolvSRK survival 100%.

Values are medians across seeds, measured by Resonix Labs on Resonix hardware and not independently verified; nfev and wall are on reference lab hardware (indicative). Under injected noise only SolvSRK and the SciPy arms are run. How we measure accuracy → · Verification status →

SolvScout · free

Profile your problem for free

This page shows one published benchmark cell. SolvScout fingerprints your ODE, compares it to the full corpus, and recommends a solver with the same survival / precision / speed ranking you see here — including when a SciPy arm wins.

SolvSRK · 30-day trial

Run the winner on your machine

SolvSRK is the stiffness-adaptive integrator behind the SolvSRK column in these tables. Create an account, activate a machine, and take a 30-day trial — same binary you'd ship after purchase.

Cite this page

Replace the access date. Pin the freeze ID and library versions when comparing against a later export. Cite it as what it is — a self-reported vendor benchmark, not an independently verified result. The note field says so; please keep it.

@misc{resonix_evidence_contact_landing_ship_deck_motion_2026,
  title        = {Resonix Evidence Portal: Contact / landing / ship-deck motion},
  author       = {{Resonix Labs (Canada) Inc.}},
  year         = {2026},
  howpublished = {\url{https://resonix.tech/evidence/problems/contact-landing-ship-deck-motion}},
  note         = {Self-reported vendor benchmark; internally generated by Resonix Labs and not independently verified. Accessed YYYY-MM-DD. Freeze 2026-08-13; libsolvsrk 2.3.0; SciPy 1.14.}
}

Related

TRL 4–5 · simulation-lab validated · 398 problems · 14 solver arms · clean + 5 noise levels

Freeze: 2026-08-13 · scipy 1.14 · libsolvsrk 2.3.0 · Methodology

Self-reported by Resonix Labs · not independently verified · Verification status