Program Listing for File NonlinearContactForce.h#
↰ Return to documentation for file (include/Karana/Collision/NonlinearContactForce.h)
/*
* Copyright (c) 2024-2026 Karana Dynamics Pty Ltd. All rights reserved.
*
* NOTICE TO USER:
*
* This source code and/or documentation (the "Licensed Materials") is
* the confidential and proprietary information of Karana Dynamics Inc.
* Use of these Licensed Materials is governed by the terms and conditions
* of a separate software license agreement between Karana Dynamics and the
* Licensee ("License Agreement"). Unless expressly permitted under that
* agreement, any reproduction, modification, distribution, or disclosure
* of the Licensed Materials, in whole or in part, to any third party
* without the prior written consent of Karana Dynamics is strictly prohibited.
*
* THE LICENSED MATERIALS ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
* KARANA DYNAMICS DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, NON-INFRINGEMENT, AND
* FITNESS FOR A PARTICULAR PURPOSE.
*
* IN NO EVENT SHALL KARANA DYNAMICS BE LIABLE FOR ANY DAMAGES WHATSOEVER,
* INCLUDING BUT NOT LIMITED TO LOSS OF PROFITS, DATA, OR USE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGES, WHETHER IN CONTRACT, TORT,
* OR OTHERWISE ARISING OUT OF OR IN CONNECTION WITH THE LICENSED MATERIALS.
*
* U.S. Government End Users: The Licensed Materials are a "commercial item"
* as defined at 48 C.F.R. 2.101, and are provided to the U.S. Government
* only as a commercial end item under the terms of this license.
*
* Any use of the Licensed Materials in individual or commercial software must
* include, in the user documentation and internal source code comments,
* this Notice, Disclaimer, and U.S. Government Use Provision.
*/
/**
* @file NonlinearContactForce.h
* @brief Nonlinear Collision contact-force model.
* @author Codex, instructed by kelly
* @date 2026-07-24
*/
#pragma once
#include "Karana/Collision/ContactForce.h"
#include <optional>
namespace Karana::Collision {
namespace kc = Karana::Core;
namespace kd = Karana::Dynamics;
namespace km = Karana::Math;
/**
* @class NonlinearContactForce
* @brief Nonlinear penalty contact force with smoothly regularized damping
* and friction.
*
* With penetration \f$\delta\f$, signed closing speed \f$v_n\f$, stiffness
* \f$k_p\f$, exponent \f$n\f$, damping \f$k_c\f$, and full-damping depth
* \f$d_{\max}\f$, the normal force magnitude is
* \f[
* F_n=\max\left(0,k_p\delta^n+k_c\,S(\delta/d_{\max})v_n\right).
* \f]
* By default, \f$S(a)=a^2(3-2a)\f$ with \f$a\f$ clipped to \f$[0,1]\f$.
* Tangential friction follows a cubic, velocity-regularized Coulomb curve.
* Its magnitude rises smoothly from zero to \f$\mu_s F_n\f$ at the
* stiction-transition speed \f$v_s\f$, falls smoothly to
* \f$\mu_d F_n\f$ at the friction-transition speed \f$v_d\f$, and remains
* on that dynamic plateau thereafter.
*
* The damping and friction transitions use a cubic polynomial approximation
* to a step function.
*/
class NonlinearContactForce : public ContactForceBase {
public:
/// Use the ContactForceBase constructor.
using ContactForceBase::ContactForceBase;
/** @brief Parameters used by the nonlinear contact-force model. */
struct NonlinearContactForceParams {
/// Penalty stiffness in N/m^n.
double kp = km::notReadyNaN;
/// Maximum normal damping coefficient in N s/m.
double kc = km::notReadyNaN;
/// Positive penetration exponent.
double n = km::notReadyNaN;
/// Static coefficient, or nullopt to use mu_dynamic.
std::optional<double> mu_static = std::nullopt;
/// Dynamic friction coefficient.
double mu_dynamic = km::notReadyNaN;
/// Penetration at full damping in meters.
double damping_ramp_depth = km::notReadyNaN;
/// Slip speed of the static-friction peak in m/s.
double stiction_transition_speed = km::notReadyNaN;
/// Slip speed at the end of the friction transition in m/s.
double friction_transition_speed = km::notReadyNaN;
};
/** @brief Scratch data populated by the latest force computation. */
struct NonlinearContactForceScratch {
/// Positive penetration depth in meters.
double penetration = km::notReadyNaN;
/// Relative contact-point velocity expressed in frame 1.
km::Vec3 linvel;
/// Total force on object 1 expressed in frame 1.
km::Vec3 f;
};
/**
* @brief Create a nonlinear contact-force model.
* @param name Object name.
* @return Newly allocated model.
*/
// codechecker_suppress [cppcheck-duplInheritedMember]
static kc::ks_ptr<NonlinearContactForce> create(std::string_view name);
/**
* @brief Compute the force on contact object 1.
* @param contact Collision-native contact geometry.
* @param nd_1 Contact node on object 1.
* @param nd_2 Contact node on object 2.
* @return Spatial force on object 1 expressed in frame 1.
*/
km::SpatialForce computeForce(const FrameContact &contact,
const kc::ks_ptr<kd::Node> &nd_1,
const kc::ks_ptr<kd::Node> &nd_2) final;
/** @brief Contact-law parameters. */
NonlinearContactForceParams params;
/** @brief Latest computed contact values. */
NonlinearContactForceScratch scratch;
/**
* @brief Validate the model parameters.
* @return True when all parameters are usable.
*/
bool isReady() const final;
/**
* @brief Return model parameters and latest scratch values.
* @param prefix Prefix applied to each output line.
* @param options Dump options.
* @return Formatted diagnostic text.
*/
std::string dumpString(std::string_view prefix,
const kc::Base::DumpOptions *options = nullptr) const override;
private:
/**
* @brief Evaluate the selected clipped transition.
* @param ratio Interpolation coordinate.
* @return Smooth value in [0, 1].
*/
static double _smoothStep(double ratio);
/**
* @brief Evaluate the clamped normal-force magnitude.
* @param normal_velocity Signed closing velocity.
* @return Nonnegative normal-force magnitude.
*/
double _getNonlinearContactForceNormalForce(double normal_velocity) const;
/**
* @brief Evaluate the smoothed Coulomb tangential force.
* @param force_direction Repulsive normal-force direction.
* @param normal_velocity Signed closing velocity.
* @param normal_force Nonnegative normal-force magnitude.
* @return Tangential force on contact object 1.
*/
km::Vec3 _getCoulombFrictionTangentForce(const km::Vec3 &force_direction,
double normal_velocity,
double normal_force) const;
};
} // namespace Karana::Collision