fix(simulation): tune physical UGV braking
This commit is contained in:
@@ -24,7 +24,9 @@ const PHYSICS_PROXY_ERROR = 0.0003;
|
|||||||
const PHYSICS_PROXY_MAX_TRIANGLES = 240_000;
|
const PHYSICS_PROXY_MAX_TRIANGLES = 240_000;
|
||||||
const SERVICE_BRAKE_DECELERATION_MPS2 = 1.8;
|
const SERVICE_BRAKE_DECELERATION_MPS2 = 1.8;
|
||||||
const COAST_DECELERATION_MPS2 = 0.18;
|
const COAST_DECELERATION_MPS2 = 0.18;
|
||||||
const BRAKE_ATTITUDE_DAMPING = 6;
|
const PARKING_BRAKE_HOLD_DECELERATION_MPS2 = 6;
|
||||||
|
const PARKING_BRAKE_ENGAGE_SPEED_MPS = 0.08;
|
||||||
|
const TYRE_FRICTION_SLIP = 8.5;
|
||||||
const DEFAULT_ORBIT_PITCH = 0.48;
|
const DEFAULT_ORBIT_PITCH = 0.48;
|
||||||
const CAMERA_RETURN_DELAY_SECONDS = 1.2;
|
const CAMERA_RETURN_DELAY_SECONDS = 1.2;
|
||||||
const CAMERA_RETURN_DURATION_SECONDS = 2;
|
const CAMERA_RETURN_DURATION_SECONDS = 2;
|
||||||
@@ -311,6 +313,7 @@ export class SimulationUgvController {
|
|||||||
const maxSpeed = this.settings.maxSpeedMetersPerSecond;
|
const maxSpeed = this.settings.maxSpeedMetersPerSecond;
|
||||||
const maxTurnRate = this.settings.maxTurnRateDegrees * Math.PI / 180;
|
const maxTurnRate = this.settings.maxTurnRateDegrees * Math.PI / 180;
|
||||||
const pureTurn = !braking && forwardInput === 0 && turnInput !== 0;
|
const pureTurn = !braking && forwardInput === 0 && turnInput !== 0;
|
||||||
|
const holding = !braking && forwardInput === 0 && turnInput === 0;
|
||||||
const desiredSpeed = forwardInput * maxSpeed;
|
const desiredSpeed = forwardInput * maxSpeed;
|
||||||
const speedError = desiredSpeed - speedMetersPerSecond;
|
const speedError = desiredSpeed - speedMetersPerSecond;
|
||||||
const speedResponseRange = Math.max(0.35, maxSpeed * 0.2);
|
const speedResponseRange = Math.max(0.35, maxSpeed * 0.2);
|
||||||
@@ -325,13 +328,22 @@ export class SimulationUgvController {
|
|||||||
const leftCommand = clamp(forwardCommand - turnCommand, -1, 1);
|
const leftCommand = clamp(forwardCommand - turnCommand, -1, 1);
|
||||||
const rightCommand = clamp(forwardCommand + turnCommand, -1, 1);
|
const rightCommand = clamp(forwardCommand + turnCommand, -1, 1);
|
||||||
const engineForce = pureTurn ? pivotForcePerWheel : driveForcePerWheel;
|
const engineForce = pureTurn ? pivotForcePerWheel : driveForcePerWheel;
|
||||||
|
const brakeDeceleration = braking
|
||||||
|
? SERVICE_BRAKE_DECELERATION_MPS2
|
||||||
|
: holding
|
||||||
|
? Math.abs(speedMetersPerSecond) <= PARKING_BRAKE_ENGAGE_SPEED_MPS
|
||||||
|
? PARKING_BRAKE_HOLD_DECELERATION_MPS2
|
||||||
|
: COAST_DECELERATION_MPS2
|
||||||
|
: 0;
|
||||||
|
const wheelBrakeForce = this.settings.massKg * brakeDeceleration
|
||||||
|
/ Math.max(1, this.wheelDefinitions.length);
|
||||||
|
|
||||||
for (let index = 0; index < this.wheelDefinitions.length; index += 1) {
|
for (let index = 0; index < this.wheelDefinitions.length; index += 1) {
|
||||||
const definition = this.wheelDefinitions[index];
|
const definition = this.wheelDefinitions[index];
|
||||||
const command = definition.left ? leftCommand : rightCommand;
|
const command = definition.left ? leftCommand : rightCommand;
|
||||||
this.vehicle.setSteeringValue(0, index);
|
this.vehicle.setSteeringValue(0, index);
|
||||||
this.vehicle.applyEngineForce(command * engineForce, index);
|
this.vehicle.applyEngineForce(command * engineForce, index);
|
||||||
this.vehicle.setBrake(0, index);
|
this.vehicle.setBrake(wheelBrakeForce, index);
|
||||||
this.vehicle.updateWheelTransform(index, true);
|
this.vehicle.updateWheelTransform(index, true);
|
||||||
const transform = this.vehicle.getWheelTransformWS(index);
|
const transform = this.vehicle.getWheelTransformWS(index);
|
||||||
const position = transform.getOrigin();
|
const position = transform.getOrigin();
|
||||||
@@ -345,17 +357,6 @@ export class SimulationUgvController {
|
|||||||
let nextLinearX = linearVelocity.x;
|
let nextLinearX = linearVelocity.x;
|
||||||
let nextLinearZ = linearVelocity.z;
|
let nextLinearZ = linearVelocity.z;
|
||||||
let horizontalSpeed = Math.hypot(nextLinearX, nextLinearZ);
|
let horizontalSpeed = Math.hypot(nextLinearX, nextLinearZ);
|
||||||
const coasting = !braking && forwardInput === 0 && turnInput === 0;
|
|
||||||
if ((braking || coasting) && horizontalSpeed > 0.001) {
|
|
||||||
const deceleration = braking
|
|
||||||
? SERVICE_BRAKE_DECELERATION_MPS2
|
|
||||||
: COAST_DECELERATION_MPS2;
|
|
||||||
const nextSpeed = Math.max(0, horizontalSpeed - deceleration * Math.max(0, deltaSeconds));
|
|
||||||
const scale = nextSpeed / horizontalSpeed;
|
|
||||||
nextLinearX *= scale;
|
|
||||||
nextLinearZ *= scale;
|
|
||||||
horizontalSpeed = nextSpeed;
|
|
||||||
}
|
|
||||||
if (pureTurn && horizontalSpeed > 0.001) {
|
if (pureTurn && horizontalSpeed > 0.001) {
|
||||||
const pivotDamping = Math.exp(-Math.max(0, deltaSeconds) * 8);
|
const pivotDamping = Math.exp(-Math.max(0, deltaSeconds) * 8);
|
||||||
nextLinearX *= pivotDamping;
|
nextLinearX *= pivotDamping;
|
||||||
@@ -372,9 +373,6 @@ export class SimulationUgvController {
|
|||||||
rigidbody.linearVelocity = this.limitedLinearVelocity;
|
rigidbody.linearVelocity = this.limitedLinearVelocity;
|
||||||
}
|
}
|
||||||
const angularVelocity = rigidbody.angularVelocity;
|
const angularVelocity = rigidbody.angularVelocity;
|
||||||
const attitudeDamping = braking
|
|
||||||
? Math.exp(-Math.max(0, deltaSeconds) * BRAKE_ATTITUDE_DAMPING)
|
|
||||||
: 1;
|
|
||||||
let nextAngularY = angularVelocity.y;
|
let nextAngularY = angularVelocity.y;
|
||||||
if (pureTurn) {
|
if (pureTurn) {
|
||||||
const desiredYawRate = -turnInput * maxTurnRate;
|
const desiredYawRate = -turnInput * maxTurnRate;
|
||||||
@@ -387,11 +385,11 @@ export class SimulationUgvController {
|
|||||||
} else if (Math.abs(angularVelocity.y) > maxTurnRate) {
|
} else if (Math.abs(angularVelocity.y) > maxTurnRate) {
|
||||||
nextAngularY = Math.sign(angularVelocity.y) * maxTurnRate;
|
nextAngularY = Math.sign(angularVelocity.y) * maxTurnRate;
|
||||||
}
|
}
|
||||||
if (attitudeDamping !== 1 || nextAngularY !== angularVelocity.y) {
|
if (nextAngularY !== angularVelocity.y) {
|
||||||
this.limitedAngularVelocity.set(
|
this.limitedAngularVelocity.set(
|
||||||
angularVelocity.x * attitudeDamping,
|
angularVelocity.x,
|
||||||
nextAngularY,
|
nextAngularY,
|
||||||
angularVelocity.z * attitudeDamping,
|
angularVelocity.z,
|
||||||
);
|
);
|
||||||
rigidbody.angularVelocity = this.limitedAngularVelocity;
|
rigidbody.angularVelocity = this.limitedAngularVelocity;
|
||||||
}
|
}
|
||||||
@@ -464,8 +462,9 @@ export class SimulationUgvController {
|
|||||||
type: "dynamic",
|
type: "dynamic",
|
||||||
mass: this.settings.massKg,
|
mass: this.settings.massKg,
|
||||||
friction: 0.85,
|
friction: 0.85,
|
||||||
linearDamping: 0.08,
|
rollingFriction: 0.12,
|
||||||
angularDamping: 0.45,
|
linearDamping: 0.12,
|
||||||
|
angularDamping: 0.6,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.chassisMaterial = createMaterial(readThemeAccent(), new Color(0.03, 0.04, 0.05));
|
this.chassisMaterial = createMaterial(readThemeAccent(), new Color(0.03, 0.04, 0.05));
|
||||||
@@ -559,7 +558,7 @@ export class SimulationUgvController {
|
|||||||
wheel.set_m_suspensionStiffness(24);
|
wheel.set_m_suspensionStiffness(24);
|
||||||
wheel.set_m_wheelsDampingRelaxation(3.2);
|
wheel.set_m_wheelsDampingRelaxation(3.2);
|
||||||
wheel.set_m_wheelsDampingCompression(4.8);
|
wheel.set_m_wheelsDampingCompression(4.8);
|
||||||
wheel.set_m_frictionSlip(5.5);
|
wheel.set_m_frictionSlip(TYRE_FRICTION_SLIP);
|
||||||
wheel.set_m_rollInfluence(0.08);
|
wheel.set_m_rollInfluence(0.08);
|
||||||
}
|
}
|
||||||
this.ammo.destroy(axle);
|
this.ammo.destroy(axle);
|
||||||
|
|||||||
@@ -179,8 +179,16 @@ test("PlayCanvas owns the realtime scene graph without an iframe or React entity
|
|||||||
assert.match(ugv, /desiredSpeed = forwardInput \* maxSpeed/);
|
assert.match(ugv, /desiredSpeed = forwardInput \* maxSpeed/);
|
||||||
assert.match(ugv, /maximumAcceleration = clamp\(1\.4 \+ maxSpeed \* 0\.35, 1\.8, 4\.2\)/);
|
assert.match(ugv, /maximumAcceleration = clamp\(1\.4 \+ maxSpeed \* 0\.35, 1\.8, 4\.2\)/);
|
||||||
assert.match(ugv, /SERVICE_BRAKE_DECELERATION_MPS2 = 1\.8/);
|
assert.match(ugv, /SERVICE_BRAKE_DECELERATION_MPS2 = 1\.8/);
|
||||||
assert.match(ugv, /horizontalSpeed - deceleration \* Math\.max\(0, deltaSeconds\)/);
|
assert.match(ugv, /PARKING_BRAKE_HOLD_DECELERATION_MPS2 = 6/);
|
||||||
assert.match(ugv, /this\.vehicle\.setBrake\(0, index\)/);
|
assert.match(ugv, /PARKING_BRAKE_ENGAGE_SPEED_MPS = 0\.08/);
|
||||||
|
assert.match(ugv, /TYRE_FRICTION_SLIP = 8\.5/);
|
||||||
|
assert.match(ugv, /holding = !braking && forwardInput === 0 && turnInput === 0/);
|
||||||
|
assert.match(ugv, /this\.settings\.massKg \* brakeDeceleration/);
|
||||||
|
assert.match(ugv, /this\.vehicle\.setBrake\(wheelBrakeForce, index\)/);
|
||||||
|
assert.doesNotMatch(ugv, /horizontalSpeed - deceleration \* Math\.max\(0, deltaSeconds\)/);
|
||||||
|
assert.match(ugv, /rollingFriction: 0\.12/);
|
||||||
|
assert.match(ugv, /angularDamping: 0\.6/);
|
||||||
|
assert.match(ugv, /wheel\.set_m_frictionSlip\(TYRE_FRICTION_SLIP\)/);
|
||||||
assert.doesNotMatch(ugv, /massKg \* 3/);
|
assert.doesNotMatch(ugv, /massKg \* 3/);
|
||||||
assert.match(ugv, /pureTurn = !braking && forwardInput === 0 && turnInput !== 0/);
|
assert.match(ugv, /pureTurn = !braking && forwardInput === 0 && turnInput !== 0/);
|
||||||
assert.match(ugv, /desiredYawRate = -turnInput \* maxTurnRate/);
|
assert.match(ugv, /desiredYawRate = -turnInput \* maxTurnRate/);
|
||||||
|
|||||||
Reference in New Issue
Block a user