Транспортные средства: колёса на RevoluteJoint и PrismaticJoint

В библиотеке Oimo.js физическая модель транспортного средства строится на основе жёстких тел (RigidBody) и сочленений (Joint). Колёса реализуются как отдельные динамические тела, соединённые с кузовом через ограничители степеней свободы.

Для создания подвески и вращения колёс используются:

  • RevoluteJoint — вращательное соединение (ось вращения фиксирована).
  • PrismaticJoint — поступательное соединение (движение вдоль заданной оси).

Комбинирование этих двух типов сочленений позволяет получить реалистичную подвеску: колесо свободно вращается вокруг своей оси и перемещается вверх-вниз относительно кузова.


Базовая структура мира

Создание физического мира:

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

Вращение колеса обеспечивается 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

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;

Высокое значение:

  • уменьшает пробуксовку,
  • повышает устойчивость.

Низкое значение:

  • создаёт эффект скольжения.

Масса и распределение инерции

Масса рассчитывается на основе плотности и объёма. Для реалистичного поведения:

  • кузов тяжелее колёс,
  • центр масс ниже геометрического центра,
  • инерция по оси X и Z больше, чем по Y.

При необходимости используется ручная коррекция:

chassis.setupMass(OIMO.BODY_DYNAMIC);

Оптимизация устойчивости

Частые проблемы:

Перевороты

Причины:

  • высокий центр масс,
  • узкая база,
  • слишком мягкая подвеска.

Решения:

  • расширение колёсной базы,
  • увеличение демпфирования,
  • понижение центра масс.

Дрожание колёс

Причины:

  • слишком маленький timestep,
  • недостаточные iterations,
  • конфликт ограничений.

Решения:

  • увеличить iterations,
  • уменьшить жёсткость пружины,
  • использовать промежуточное тело Axle.

Пошаговая схема создания четырёхколёсного автомобиля

  1. Создать мир.
  2. Создать кузов.
  3. Создать 4 колеса.
  4. Создать 4 PrismaticJoint.
  5. Создать 4 RevoluteJoint.
  6. Настроить моторы.
  7. Настроить пружины.
  8. Отключить коллизию между связанными телами.

Расширенные техники

Полный привод

Все 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);
}

Баланс физической достоверности и производительности

  • Меньше тел — выше производительность.
  • Больше ограничений — выше нагрузка на решатель.
  • Сложная подвеска требует большего количества итераций.

Оптимальная модель — компромисс между реализмом и стабильностью.


Ключевые принципы построения

  • Колёса — отдельные динамические тела.
  • Вращение реализуется через RevoluteJoint.
  • Подвеска реализуется через PrismaticJoint.
  • Для устойчивости рекомендуется промежуточное тело.
  • Моторы управляются через limitMotor.
  • Пружины настраиваются через stiffness и damping.
  • Центр масс критически влияет на поведение.

Такая архитектура позволяет построить управляемое, физически корректное транспортное средство на базе Oimo.js с гибкой настройкой подвески, привода и рулевого управления.