В библиотеке Oimo.js физическая модель транспортного средства
строится на основе жёстких тел (RigidBody) и сочленений
(Joint). Колёса реализуются как отдельные динамические
тела, соединённые с кузовом через ограничители степеней свободы.
Для создания подвески и вращения колёс используются:
Комбинирование этих двух типов сочленений позволяет получить реалистичную подвеску: колесо свободно вращается вокруг своей оси и перемещается вверх-вниз относительно кузова.
Создание физического мира:
import * as OIMO from 'oimo';
const world = new OIMO.World({
timestep: 1 / 60,
iterations: 8,
broadphase: 2,
worldscale: 1,
gravity: [0, -9.81, 0]
});
Ключевые параметры:
timestep — шаг симуляции.iterations — точность решения ограничений.gravity — гравитация, влияющая на подвеску.Кузов представляет собой динамическое тело с формой
Box.
const chassis = world.add({
type: 'box',
size: [2, 0.5, 4],
pos: [0, 2, 0],
move: true,
density: 1
});
Параметры:
size — размеры в мировых единицах.move: true — тело динамическое.density — влияет на массу.Для устойчивости центр масс должен быть ниже геометрического центра. Это можно компенсировать смещением формы или добавлением дополнительной геометрии.
Колёса обычно создаются как цилиндры:
function createWheel(position) {
return world.add({
type: 'cylinder',
size: [0.5, 0.5],
pos: position,
move: true,
density: 1
});
}
size для цилиндра:
Для четырёхколёсной модели создаются четыре отдельных тела.
Вращение колеса обеспечивается RevoluteJoint. Он
фиксирует две точки тел и разрешает вращение вокруг одной оси.
Пример соединения:
const joint = world.add({
type: 'jointRevolute',
body1: chassis,
body2: wheel,
pos1: [1, 0, 2],
pos2: [0, 0, 0],
axe1: [0, 0, 1],
axe2: [0, 0, 1],
collision: false
});
Основные параметры:
pos1, pos2 — локальные точки
крепления.axe1, axe2 — оси вращения.collision: false — предотвращает самоколлизию.RevoluteJoint поддерживает моторизацию:
joint.limitMotor.setMotor(speed, torque);
speed — целевая угловая скорость.torque — максимальный крутящий момент.Таким образом реализуется привод.
PrismaticJoint ограничивает движение вдоль одной оси. Для подвески используется вертикальное направление.
const suspension = world.add({
type: 'jointPrismatic',
body1: chassis,
body2: wheel,
pos1: [1, 0, 2],
pos2: [0, 0, 0],
axe1: [0, 1, 0],
axe2: [0, 1, 0],
min: -0.3,
max: 0.3,
collision: false
});
Параметры:
min, max — пределы хода подвески.axe1, axe2 — направление поступательного
движения.Ограничение можно дополнить пружиной:
suspension.limitMotor.setSpring(stiffness, damping);
stiffness — жёсткость.damping — демпфирование.Баланс этих параметров определяет поведение автомобиля:
Стандартная архитектура:
Chassis
|
PrismaticJoint (подвеска)
|
RevoluteJoint (вращение)
|
Wheel
Однако прямое соединение двумя джойнтами к одному телу может вызвать избыточные ограничения. Более стабильная схема — использование промежуточного “держателя” (axle body):
Chassis
|
PrismaticJoint
|
Axle (малое тело)
|
RevoluteJoint
|
Wheel
Axle создаётся как лёгкое динамическое тело с малой массой.
Передние или задние колёса подключаются к мотору:
rearLeftJoint.limitMotor.setMotor(10, 50);
rearRightJoint.limitMotor.setMotor(10, 50);
Изменение скорости:
function accelerate(value) {
const speed = value * 20;
rearLeftJoint.limitMotor.setMotor(speed, 50);
rearRightJoint.limitMotor.setMotor(speed, 50);
}
Торможение реализуется уменьшением скорости мотора:
joint.limitMotor.setMotor(0, 100);
Большой torque создаёт эффект тормозного
сопротивления.
Для передних колёс добавляется дополнительный RevoluteJoint с вертикальной осью вращения.
Схема:
Chassis
|
Steering RevoluteJoint (ось Y)
|
Axle
|
Wheel RevoluteJoint (ось Z)
Поворот осуществляется через установку лимитов:
steeringJoint.limitMotor.setLimit(-0.5, 0.5);
steeringJoint.limitMotor.setMotor(targetAngle, steeringTorque);
Сцепление с поверхностью определяется коэффициентом трения формы:
wheel.shapes[0].config.friction = 2;
Высокое значение:
Низкое значение:
Масса рассчитывается на основе плотности и объёма. Для реалистичного поведения:
При необходимости используется ручная коррекция:
chassis.setupMass(OIMO.BODY_DYNAMIC);
Частые проблемы:
Причины:
Решения:
Причины:
timestep,iterations,Решения:
iterations,Все RevoluteJoint получают мотор:
for (let joint of wheelJoints) {
joint.limitMotor.setMotor(speed, torque);
}
Возможна имитация дифференциала:
rearLeftJoint.limitMotor.setMotor(speed * 0.95, 50);
rearRightJoint.limitMotor.setMotor(speed * 1.05, 50);
Анализ угловой скорости колеса:
if (wheel.getAngularVelocity().length() < threshold) {
joint.limitMotor.setMotor(reducedSpeed, torque);
}
Оптимальная модель — компромисс между реализмом и стабильностью.
Такая архитектура позволяет построить управляемое, физически корректное транспортное средство на базе Oimo.js с гибкой настройкой подвески, привода и рулевого управления.