Симуляція польоту м'яча з ефектом Магнуса на JavaScript
Крива подача у бейсболі, «банан»-удар вільним у футболі та топспін-форхенд у тенісі — усі спираються на одну й ту саму фізику: силу Магнуса, яку створює обертовий м'яч, рухаючись крізь повітря. Цей туторіал будує невеликий, точний симулятор польоту м'яча з рівняння опору, сили Магнуса та інтегрування RK4.
1. Сили, що діють на обертовий м'яч
Щойно м'яч залишає ногу, биту чи ракетку, у повітрі на нього діють рівно три сили (без урахування вітру):
- Гравітація: постійна, спрямована вниз,
F_g = m·g. - Опір: протидіє швидкості, величина зростає з v², залежить від форми через Cd.
- Сила Магнуса: перпендикулярна і до вектора швидкості, і до осі спіну. Саме вона згинає кривий вільний удар убік або змушує топспін-м'яч у тенісі падати швидше, ніж лише під дією гравітації.
2. Сила опору
Стандартне квадратичне рівняння опору, напрямок завжди протилежний вектору швидкості:
function dragForce(vel, Cd, rho, area) {
const speed = Math.hypot(vel.x, vel.y, vel.z);
if (speed === 0) return { x: 0, y: 0, z: 0 };
const mag = 0.5 * Cd * rho * area * speed ** 2;
// напрямок протилежний швидкості
return {
x: -mag * (vel.x / speed),
y: -mag * (vel.y / speed),
z: -mag * (vel.z / speed),
};
}
3. Сила Магнуса від спіну
Сила Магнуса пропорційна векторному добутку кутової швидкості (спіну) ω і лінійної швидкості v — вона завжди перпендикулярна площині, що містить обидва вектори:
function magnusForce(vel, omega, Cl, rho, area, radius) {
// векторний добуток: omega × vel
const cross = {
x: omega.y * vel.z - omega.z * vel.y,
y: omega.z * vel.x - omega.x * vel.z,
z: omega.x * vel.y - omega.y * vel.x,
};
const S = 0.5 * Cl * rho * area * radius;
return { x: S * cross.x, y: S * cross.y, z: S * cross.z };
}
4. Вектор стану і рівняння руху
Стан м'яча — це позиція і швидкість; спін ω вважається приблизно сталим протягом польоту (загасання спіну повільне порівняно з тривалістю польоту для більшості видів спорту):
function acceleration(vel, params) {
const { mass, Cd, Cl, rho, area, radius, omega, g } = params;
const fDrag = dragForce(vel, Cd, rho, area);
const fMagnus = magnusForce(vel, omega, Cl, rho, area, radius);
return {
x: (fDrag.x + fMagnus.x) / mass,
y: (fDrag.y + fMagnus.y) / mass - g,
z: (fDrag.z + fMagnus.z) / mass,
};
}
5. Інтегрування RK4
Оскільки і опір, і сила Магнуса нелінійно залежать від швидкості, простий метод Ейлера накопичує помітну похибку за довгий політ, якщо тільки крок не зробити крихітним. Рунге-Кутта 4-го порядку (RK4) дає набагато кращу точність при тому самому обчислювальному бюджеті:
function stepRK4(pos, vel, dt, params) {
const a1 = acceleration(vel, params);
const v1 = vel;
const v2 = addScaled(vel, a1, dt / 2);
const a2 = acceleration(v2, params);
const v3 = addScaled(vel, a2, dt / 2);
const a3 = acceleration(v3, params);
const v4 = addScaled(vel, a3, dt);
const a4 = acceleration(v4, params);
// зважене середнє чотирьох оцінок нахилу
const vAvg = {
x: (v1.x + 2*v2.x + 2*v3.x + v4.x) / 6,
y: (v1.y + 2*v2.y + 2*v3.y + v4.y) / 6,
z: (v1.z + 2*v2.z + 2*v3.z + v4.z) / 6,
};
const aAvg = {
x: (a1.x + 2*a2.x + 2*a3.x + a4.x) / 6,
y: (a1.y + 2*a2.y + 2*a3.y + a4.y) / 6,
z: (a1.z + 2*a2.z + 2*a3.z + a4.z) / 6,
};
return {
pos: addScaled(pos, vAvg, dt),
vel: addScaled(vel, aAvg, dt),
};
}
function addScaled(v, d, s) {
return { x: v.x + d.x*s, y: v.y + d.y*s, z: v.z + d.z*s };
}
6. Порівняння траєкторій зі спіном і без
Запуск симуляції двічі з тими самими умовами старту — раз з omega = {x:0, y:0, z:0}, і раз з реалістичною швидкістю обертання — показує точно, скільки кривизни чи додаткової дальності додає спін:
function simulateTrajectory(initialVel, omega, params, dt = 0.005) {
let pos = { x: 0, y: 0, z: 0 };
let vel = initialVel;
const path = [{ ...pos }];
params.omega = omega;
while (pos.y >= 0 && path.length < 5000) {
const next = stepRK4(pos, vel, dt, params);
pos = next.pos;
vel = next.vel;
path.push({ ...pos });
}
return path;
}
// прямий удар проти кривого "банан"-удару, та сама початкова швидкість
const straight = simulateTrajectory(launchVel, { x: 0, y: 0, z: 0 }, params);
const curved = simulateTrajectory(launchVel, { x: 0, y: 45, z: 0 }, params); // ~7.2 об/с бокового спіну
7. Рендер траєкторії на Canvas
Для простого вигляду збоку чи зверху спроектуйте 3D-точки траєкторії на координати 2D canvas і намалюйте їх як ламану лінію, оновлюючи щокадр анімації для ефекту «м'яча в польоті» в реальному часі:
function drawTrajectory(ctx, path, scale, originX, originY) {
ctx.beginPath();
path.forEach((p, i) => {
// вигляд збоку: x = дистанція по дальності, y = висота
const screenX = originX + p.x * scale;
const screenY = originY - p.y * scale;
if (i === 0) ctx.moveTo(screenX, screenY);
else ctx.lineTo(screenX, screenY);
});
ctx.strokeStyle = "#4ade80";
ctx.lineWidth = 2;
ctx.stroke();
}
8. Налаштування констант для реальних видів спорту
Реалістичні значення Cd, коефіцієнта підйомної сили та маси/радіуса важливіші за інтегратор для відповідності реальним траєкторіям:
| М'яч | Маса (г) | Радіус (см) | Cd | Типовий спін |
|---|---|---|---|---|
| Футбольний м'яч | 430 | 11.0 | 0.20–0.25 | 0–10 об/с |
| Тенісний м'яч | 58 | 3.3 | 0.55–0.65 | до 100 об/с (топспін) |
| Бейсбольний м'яч | 145 | 3.6 | 0.3–0.4 | 20–40 об/с |
| М'яч для гольфу | 46 | 2.1 | 0.24–0.30 | 50–80 об/с (backspin) |
Швидкість обертання зазвичай вимірюють в об/хв (RPM) у спортивно-науковій літературі — переведіть у рад/с за формулою
ω = RPM · 2π / 60 перед тим, як подати у модель вище. Коефіцієнт підйомної сили Cl визначити важче, ніж Cd, і зазвичай його підбирають емпірично з даних аеродинамічної труби чи трекінгу типу Trackman для кожного типу м'яча; 0.1–0.3 — розумний початковий діапазон для більшості сферичних спортивних м'ячів при помірних співвідношеннях спіну (ωr/v).
🌀 Спробуйте симуляцію ефекту Магнуса
Подивіться, як backspin і topspin вигинають траєкторію м'яча в реальному часі, і порівняйте її поруч з траєкторією без спіну.
Відкрити симуляцію →Часті запитання
Чого я навчуся в цьому уроці?
Побудуйте симулятор кривого польоту м'яча з нуля: сила опору, підіймальна сила Магнуса від backspin/topspin, інтегрування RK4 та порівняння траєкторій зі спіном і без.
Які теми розглядаються в цьому уроці?
Цей урок охоплює такі теми: Сили, що діють на обертовий м'яч, Сила опору, Сила Магнуса від спіну, Вектор стану і рівняння руху, Інтегрування RK4, Порівняння траєкторій зі спіном і без, Рендер траєкторії на Canvas, Налаштування констант для реальних видів спорту.
Скільки часу займає цей урок?
Цей урок займає приблизно 20 хв.
Які попередні знання потрібні?
Це урок рівня «Середній рівень» — окрема попередня підготовка, крім базового JavaScript, не потрібна.