Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

vehicle acceleration depends on its location #13

Closed
stephengold opened this issue Jan 8, 2021 · 6 comments
Closed

vehicle acceleration depends on its location #13

stephengold opened this issue Jan 8, 2021 · 6 comments

Comments

@stephengold
Copy link
Owner

In current More Advanced Vehicles (using Minie v3.0.0), vehicles occasionally accelerate much faster than they should. The issue is noticeable when accelerating from rest on a flat, horizontal surface.

I created a test app that reproduces the issue. Depending on the starting X and Z coordinates, the vehicle's Z velocity after a single simulation step can either be large (9.086558) or small (0.06497696). For starting coordinates uniformly distributed between 0 and 1, the outcome is approximately 18% large and 82% small. For fixed starting X and Z coordinates, however, the outcome seems to be highly repeatable.

public class TestIssue extends SimpleApplication {

    public static void main(String[] ignored) {
        TestIssue application = new TestIssue();
        application.start();
    }

    @Override
    public void simpleInitApp() {
        Random random = new Random();
        int largeVzCount = 0;
        int smallVzCount = 0;
        int otherCount = 0;

        for (int i = 0; i < 1000; ++i) {
            float x = random.nextFloat();
            float z = random.nextFloat();
            float vz = test(x, z);

            if (FastMath.approximateEquals(vz, 9.086558f)) {
                ++largeVzCount;
            } else if (FastMath.approximateEquals(vz, 0.06497696f)) {
                ++smallVzCount;
            } else {
                ++otherCount;
            }
        }

        System.out.println("largeVzCount = " + largeVzCount);
        System.out.println("smallVzCount = " + smallVzCount);
        System.out.println("otherCount = " + otherCount);

        stop();

    }

    private float test(float startX, float startZ) {
        PhysicsSpace physicsSpace
                = new PhysicsSpace(PhysicsSpace.BroadphaseType.DBVT);

        // Add a static plane to represent the ground.
        Plane plane = new Plane(Vector3f.UNIT_Y, 0f);
        PlaneCollisionShape shape = new PlaneCollisionShape(plane);
        PhysicsRigidBody body
                = new PhysicsRigidBody(shape, PhysicsBody.massForStatic);
        physicsSpace.addCollisionObject(body);

        // Create a wedge-shaped vehicle with a low center of gravity.
        // The local forward direction is +Z.
        float noseZ = 1.4f;
        float spoilerY = 0.5f;
        float tailZ = -0.7f;
        float undercarriageY = -0.1f;
        float halfWidth = 0.4f;
        Collection<Vector3f> cornerLocations = new ArrayList<>(6);
        cornerLocations.add(new Vector3f(+halfWidth, undercarriageY, noseZ));
        cornerLocations.add(new Vector3f(-halfWidth, undercarriageY, noseZ));
        cornerLocations.add(new Vector3f(+halfWidth, undercarriageY, tailZ));
        cornerLocations.add(new Vector3f(-halfWidth, undercarriageY, tailZ));
        cornerLocations.add(new Vector3f(+halfWidth, spoilerY, tailZ));
        cornerLocations.add(new Vector3f(-halfWidth, spoilerY, tailZ));
        HullCollisionShape wedgeShape
                = new HullCollisionShape(cornerLocations);

        float mass = 1525f;
        PhysicsVehicle vehicle = new PhysicsVehicle(wedgeShape, mass);
        vehicle.setDamping(0.086f, 0f);
        vehicle.setSuspensionCompression(6f);
        vehicle.setSuspensionDamping(7f);
        vehicle.setSuspensionStiffness(150f);

        // Add 4 wheels, 2 in the front (for steering) and 2 in the rear.
        boolean front = true;
        boolean rear = false;
        float frontAxisZ = 0.7f * noseZ;
        float rearAxisZ = 0.8f * tailZ;
        float radius = 0.425f; // of each tire
        float restLength = 0.2f; // of the suspension
        float xOffset = 0.9f * halfWidth;
        Vector3f axleDirection = new Vector3f(-1f, 0f, 0f);
        Vector3f suspensionDirection = new Vector3f(0f, -1f, 0f);
        vehicle.addWheel(new Vector3f(-xOffset, 0f, frontAxisZ),
                suspensionDirection, axleDirection, restLength, radius, front);
        vehicle.addWheel(new Vector3f(xOffset, 0f, frontAxisZ),
                suspensionDirection, axleDirection, restLength, radius, front);
        vehicle.addWheel(new Vector3f(-xOffset, 0f, rearAxisZ),
                suspensionDirection, axleDirection, restLength, radius, rear);
        vehicle.addWheel(new Vector3f(xOffset, 0f, rearAxisZ),
                suspensionDirection, axleDirection, restLength, radius, rear);

        for (int i = 0; i < 4; ++i) {
            VehicleWheel w = vehicle.getWheel(i);
            w.setFrictionSlip(0.32f);
            w.setMaxSuspensionForce(8000f);
            w.setRestLength(0.225f);
            w.setSuspensionStiffness(10f);
            w.setWheelsDampingCompression(2.087f);
            w.setWheelsDampingRelaxation(2.845f);
        }

        Vector3f startLocation = new Vector3f(startX, 0.382268f, startZ);
        vehicle.setPhysicsLocation(startLocation);
        physicsSpace.addCollisionObject(vehicle);

        // Simulate a single timestep.
        vehicle.accelerate(2, 415710f);
        vehicle.accelerate(3, 415710f);
        physicsSpace.update(1f / 60, 0);
        Vector3f velocity = vehicle.getLinearVelocity();

        physicsSpace.removeCollisionObject(vehicle);

        return velocity.z;
    }
}
@stephengold
Copy link
Owner Author

stephengold commented Jan 9, 2021

I instrumented btRigidBody.cpp to print out the impulses applied during the timestep:

impulse 0.000000 0.077414 0.000000, rel_pos -0.360000 -0.379543 0.980000
impulse 0.000000 0.077414 0.000000, rel_pos 0.360000 -0.379543 0.980000
impulse 0.000000 0.077414 0.000000, rel_pos -0.360000 -0.379543 -0.560000
impulse 0.000000 0.077414 0.000000, rel_pos 0.360000 -0.379543 -0.560000
impulse 0.000000 -0.000000 6.928500, rel_pos -0.360000 -0.379543 -0.560000
impulse 0.000000 -0.000000 6.928500, rel_pos 0.360000 -0.379543 -0.560000
vz0 = 9.086557

impulse 0.000000 0.077414 0.000000, rel_pos -0.360000 -0.379543 0.980000
impulse 0.000000 0.077414 0.000000, rel_pos 0.360000 -0.379543 0.980000
impulse 0.000000 0.077414 0.000000, rel_pos -0.360000 -0.379543 -0.560000
impulse 0.000000 0.077414 0.000000, rel_pos 0.360000 -0.379543 -0.560000
impulse 0.000000 -0.000000 -0.000000, rel_pos -0.360000 -0.379543 0.980000
impulse -0.000000 0.000000 0.000000, rel_pos 0.640000 0.000000 0.980000
impulse 0.000000 -0.000000 -0.000000, rel_pos 0.360000 -0.379543 0.980000
impulse -0.000000 0.000000 0.000000, rel_pos 1.360000 -0.000000 0.980000
impulse 0.000000 -0.000000 0.049545, rel_pos -0.360000 -0.379543 -0.560000
impulse 0.000000 -0.000000 -0.000000, rel_pos -0.360000 -0.379543 -0.560000
impulse -0.000000 0.000000 0.000000, rel_pos 0.640000 0.000000 -0.560000
impulse 0.000000 -0.000000 0.049545, rel_pos 0.360000 -0.379543 -0.560000
impulse 0.000000 -0.000000 -0.000000, rel_pos 0.360000 -0.379543 -0.560000
impulse -0.000000 0.000000 0.000000, rel_pos 1.360000 -0.000000 -0.560000
vz1 = 0.06497695
  1. Clearly a different codepath is taken.
  2. The +Y impulses appear to be the same. As expected, the difference is in the Z-axis impulses: 2 impulses for test(0f, 0f) versus 10 for test(1f, 0f).
  3. In the 2nd case, 8 of the impulses have zero magnitude.

@stephengold
Copy link
Owner Author

stephengold commented Jan 9, 2021

The difference is in m_forwardImpulse[wheel], not m_forwardWS.

m_forwardImpulse[wheel] is calculated in the 2 loops immediately above where the forward impulse is applied, in updateFriction(). The difference creeps in because, in the vz1 case, the impulse is multiplied by skidInfo, which is 0.007151,
whereas in the vz0 case, no multiplication occurs.

Tracing it back further, sliding is true in both cases. The lack of multiplication in vz0 is due to the m_sideImpulse[wheel] != btScalar(0.) test failing, the impulse being -2.03645e-09 in the vz1 case but -0 in the vz0 case.

Testing for exact (in)equality of floating-point values is a red flag, due to the risk of round-off error. But which path/result is correct?

@stephengold
Copy link
Owner Author

stephengold commented Jan 9, 2021

It makes no physical sense for the scaling of the forward impulse to depend on whether the side impulse is exactly 0. I suspect that the test in line 612 of "btRaycastVehicle.cpp" was originally an optimization, perhaps at a time when the 2 impulses were calculated in separate loops. In that case, the coder who merged the loops overlooked the need to remove the test.

Furthermore, the startup accelerations observed in More Advanced Vehicles seem more plausible with the smaller impulses, which is the result I expect from commenting out line 612.

@stephengold
Copy link
Owner Author

Committed stephengold/Libbulletjme@cbccdee

Instead of spiking it in the end zone, I took celebratory screenshots.

Before:
before

After:
after

@stephengold
Copy link
Owner Author

fixed in Minie 3.1.0

@stephengold
Copy link
Owner Author

Submitted a PR to the bullet3 project: bulletphysics/bullet3#3220

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant