90 : config_(sanitizeConfig(
config)) {}
93 : config_(sanitizeConfig(
config)),
94 ball_properties_(sanitizeBallProperties(ball_properties)) {}
109 ball_properties_ = sanitizeBallProperties(props);
119 sanitizeState(state_);
129 carrier_position_m_ = carrier_position_m;
130 carrier_velocity_mps_ = carrier_velocity_mps;
142 const double capture_radius_m =
144 const double distance_m =
146 if (!std::isfinite(distance_m) || distance_m > capture_radius_m) {
153 state_.velocity_mps = carrier_velocity_mps_;
155 sanitizeState(state_);
169 const Vector3& muzzle_velocity_mps,
172 state_.position_m = muzzle_position_m;
173 state_.velocity_mps = muzzle_velocity_mps;
174 state_.spin_radps = muzzle_spin_radps;
175 sanitizeState(state_);
183 if (!std::isfinite(dt_s) || dt_s <= 0.0) {
187 sanitizeState(state_);
190 state_.position_m = carrier_position_m_ + carry_offset_m_;
191 state_.velocity_mps = carrier_velocity_mps_;
192 sanitizeState(state_);
196 const double max_substep_s = std::clamp(config_.max_substep_s, 1e-4, 0.05);
197 double remaining_s = dt_s;
198 while (remaining_s > 0.0) {
199 const double substep_s = std::min(remaining_s, max_substep_s);
200 const Vector3 accel0_mps2 = computeAcceleration(state_.velocity_mps);
201 const Vector3 mid_velocity_mps =
202 state_.velocity_mps + accel0_mps2 * (0.5 * substep_s);
203 const Vector3 accel_mid_mps2 = computeAcceleration(mid_velocity_mps);
205 state_.velocity_mps += accel_mid_mps2 * substep_s;
206 state_.position_m += mid_velocity_mps * substep_s;
207 sanitizeState(state_);
209 remaining_s -= substep_s;
212 resolveGroundContact(dt_s);
213 sanitizeState(state_);
218 static bool finiteVector(
const Vector3& v) {
219 return std::isfinite(v.
x) && std::isfinite(v.
y) && std::isfinite(v.
z);
224 static double sanitizeNonNegative(
double value,
double fallback) {
225 if (!std::isfinite(value)) {
228 return std::max(0.0, value);
233 if (!finiteVector(sanitized.gravity_mps2)) {
234 sanitized.gravity_mps2 = Vector3(0.0, 0.0, -9.81);
236 sanitized.effective_gravity_scale = std::clamp(
237 sanitizeNonNegative(sanitized.effective_gravity_scale, 1.0), 0.0, 5.0);
238 sanitized.air_density_kgpm3 = std::clamp(
239 sanitizeNonNegative(sanitized.air_density_kgpm3, 1.225), 0.0, 5.0);
240 sanitized.drag_scale =
241 std::clamp(sanitizeNonNegative(sanitized.drag_scale, 1.0), 0.0, 10.0);
242 sanitized.magnus_coefficient = std::isfinite(sanitized.magnus_coefficient)
243 ? sanitized.magnus_coefficient
245 sanitized.magnus_scale =
246 std::clamp(sanitizeNonNegative(sanitized.magnus_scale, 1.0), 0.0, 10.0);
247 sanitized.ground_height_m = std::isfinite(sanitized.ground_height_m)
248 ? sanitized.ground_height_m
250 sanitized.rolling_friction_per_s =
251 sanitizeNonNegative(sanitized.rolling_friction_per_s, 1.2);
252 sanitized.min_bounce_speed_mps =
253 sanitizeNonNegative(sanitized.min_bounce_speed_mps, 0.1);
254 sanitized.max_substep_s = std::clamp(
255 sanitizeNonNegative(sanitized.max_substep_s, 0.01), 1e-4, 0.05);
262 std::max(1e-6, sanitizeNonNegative(sanitized.mass_kg, 0.27));
264 std::max(1e-4, sanitizeNonNegative(sanitized.radius_m, 0.12));
265 sanitized.drag_coefficient = std::clamp(
266 sanitizeNonNegative(sanitized.drag_coefficient, 0.47), 0.0, 5.0);
267 sanitized.reference_area_m2 = sanitizeNonNegative(
268 sanitized.reference_area_m2,
269 std::acos(-1.0) * sanitized.radius_m * sanitized.radius_m);
270 if (sanitized.reference_area_m2 <= 0.0) {
271 sanitized.reference_area_m2 =
272 std::acos(-1.0) * sanitized.radius_m * sanitized.radius_m;
274 sanitized.restitution = std::clamp(
275 std::isfinite(sanitized.restitution) ? sanitized.restitution : 0.45,
282 if (!finiteVector(
state.position_m)) {
285 if (!finiteVector(
state.velocity_mps)) {
288 if (!finiteVector(
state.spin_radps)) {
298 Vector3 computeAcceleration(
const Vector3& velocity_mps)
const {
299 const Vector3 gravity_mps2 =
300 config_.gravity_mps2 * config_.effective_gravity_scale;
301 const Vector3 drag_force_n =
303 ball_properties_.reference_area_m2,
304 config_.air_density_kgpm3) *
306 const Vector3 magnus_force_n =
308 config_.magnus_coefficient) *
309 config_.magnus_scale;
310 const Vector3 accel_mps2 =
311 gravity_mps2 + (drag_force_n + magnus_force_n) *
312 (1.0 / std::max(1e-9, ball_properties_.mass_kg));
313 return finiteVector(accel_mps2) ? accel_mps2 : gravity_mps2;
321 void resolveGroundContact(
double dt_s) {
322 const double floor_z = config_.ground_height_m + ball_properties_.radius_m;
323 if (!std::isfinite(floor_z) || state_.position_m.z > floor_z) {
327 state_.position_m.z = floor_z;
328 if (state_.velocity_mps.z < -config_.min_bounce_speed_mps) {
329 state_.velocity_mps.z =
330 -state_.velocity_mps.z *
331 std::clamp(ball_properties_.restitution, 0.0, 1.0);
333 state_.velocity_mps.z = 0.0;
336 const double planar_speed = state_.velocity_mps.planarSpeed();
337 if (planar_speed <= 1e-9) {
338 state_.velocity_mps.x = 0.0;
339 state_.velocity_mps.y = 0.0;
344 std::max(0.0, 1.0 - config_.rolling_friction_per_s * dt_s);
345 state_.velocity_mps.x *= decay;
346 state_.velocity_mps.y *= decay;
354 Vector3 carrier_position_m_{};
355 Vector3 carrier_velocity_mps_{};
356 Vector3 carry_offset_m_{};
void setBallProperties(const BallProperties &props)
Replaces ball properties after sanitization.
Definition ball_physics.hpp:108
BallPhysicsSim3D()=default
void setCarrierPose(const Vector3 &carrier_position_m, const Vector3 &carrier_velocity_mps=Vector3::zero())
Updates carrier pose used when the ball is in held mode.
Definition ball_physics.hpp:127
const BallState & state() const
Returns current ball state.
Definition ball_physics.hpp:114
void setState(const BallState &state)
Replaces state and sanitizes non-finite values.
Definition ball_physics.hpp:117
void shoot(const Vector3 &muzzle_position_m, const Vector3 &muzzle_velocity_mps, const Vector3 &muzzle_spin_radps=Vector3::zero())
Places the ball at a muzzle pose and sets launch velocity/spin.
Definition ball_physics.hpp:168
void setConfig(const Config &config)
Replaces configuration after value sanitization and clamping.
Definition ball_physics.hpp:101
BallPhysicsSim3D(const Config &config, const BallProperties &ball_properties)
Definition ball_physics.hpp:92
bool requestPickup(const PickupRequest &pickup_request)
Attempts to pick up and hold the ball.
Definition ball_physics.hpp:138
BallPhysicsSim3D(const Config &config)
Definition ball_physics.hpp:89
const BallProperties & ballProperties() const
Returns active, sanitized ball properties.
Definition ball_physics.hpp:105
void release()
Releases the ball from held mode without changing velocity.
Definition ball_physics.hpp:160
void step(double dt_s)
Advances simulation by dt seconds.
Definition ball_physics.hpp:182
const Config & config() const
Returns active, sanitized configuration.
Definition ball_physics.hpp:98
Physical parameters for the ball body.
Definition ball_physics.hpp:49
double drag_coefficient
Definition ball_physics.hpp:55
double restitution
Definition ball_physics.hpp:59
double mass_kg
Definition ball_physics.hpp:51
double reference_area_m2
Definition ball_physics.hpp:57
double foam_compression_stiffness
Definition ball_physics.hpp:62
double radius_m
Definition ball_physics.hpp:53
Dynamic state advanced by step().
Definition ball_physics.hpp:66
bool held
Definition ball_physics.hpp:74
Vector3 velocity_mps
Definition ball_physics.hpp:70
Vector3 position_m
Definition ball_physics.hpp:68
Vector3 spin_radps
Definition ball_physics.hpp:72
Runtime physics environment parameters.
Definition ball_physics.hpp:25
double magnus_scale
Definition ball_physics.hpp:37
double rolling_friction_per_s
Definition ball_physics.hpp:41
double effective_gravity_scale
Definition ball_physics.hpp:29
double air_density_kgpm3
Definition ball_physics.hpp:31
double min_bounce_speed_mps
Definition ball_physics.hpp:43
Vector3 gravity_mps2
Definition ball_physics.hpp:27
double ground_height_m
Definition ball_physics.hpp:39
double drag_scale
Definition ball_physics.hpp:33
double magnus_coefficient
Definition ball_physics.hpp:35
double max_substep_s
Definition ball_physics.hpp:45
Request payload used to capture a ball into a carrier.
Definition ball_physics.hpp:78
Vector3 intake_position_m
Definition ball_physics.hpp:80
Vector3 carry_offset_m
Definition ball_physics.hpp:84
double capture_radius_m
Definition ball_physics.hpp:82
3D vector utility used throughout JSim physics.
Definition vector.hpp:22
double z
Z component.
Definition vector.hpp:28
static Vector3 dragForce(const Vector3 &v, double Cd, double A, double rho=1.225) noexcept
Computes the total drag force vector for a velocity and body.
Definition vector.hpp:337
static constexpr Vector3 zero() noexcept
Returns the zero vector.
Definition vector.hpp:360
double x
X component.
Definition vector.hpp:24
static Vector3 magnusForce(const Vector3 &velocity, const Vector3 &omega, double k=1e-4) noexcept
Computes the Magnus lift force omega x v scaled by k.
Definition vector.hpp:226
double y
Y component.
Definition vector.hpp:26