Contact / landing / ship-deck motion
BOUNDARYS1 · dim 16Partial / 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
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
| # | Solver | Survival | SCD | nfev | Wall | Score |
|---|---|---|---|---|---|---|
| 1 | SciPy BDFSciPy | 100% | - | 10,702 | 743 ms | 0.809 |
| 2 | SciPy RadauSciPy | 100% | - | 35,197 | 1.91 s | 0.809 |
| 3 | SciPy RK45SciPy | 100% | - | 12,932 | 459 ms | 0.809 |
| 4 | SciPy LSODASciPy | 100% | - | 7,627 | 246 ms | 0.809 |
| 5 | SciPy DOP853SciPy | 100% | - | 15,182 | 523 ms | 0.809 |
| 6 | SciPy RK23SciPy | 100% | - | 49,268 | 1.87 s | 0.809 |
| 7 | CVODE BDFexternal | 100% | - | 8,252 | 299 ms | 0.809 |
| 8 | CVODE Adamsexternal | 100% | - | 5,203 | 196 ms | 0.809 |
| 9 | Tsit5external | 100% | - | 14,988 | 2.73 s | 0.809 |
| 10 | Vern7external | 100% | - | 20,722 | 5.18 s | 0.809 |
| 11 | Vern9external | 100% | - | 30,306 | 5.63 s | 0.809 |
| 12 | TRBDF2external | 100% | - | 29,895 | 6.37 s | 0.809 |
| 13 | FBDFexternal | 100% | - | 11,387 | 5.84 s | 0.809 |
| 14 | SolvSRK | 100% | - | 24,588 | 612 ms | 0.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 →
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://resonixusa.com/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