Vehicle :: Suspension

A place to discuss everything related to Newton Dynamics.

Moderators: Sascha Willems, walaber

Vehicle :: Suspension

Postby kallaspriit » Tue Jun 30, 2009 7:43 am

I am trying to model a wheeled vehicle (car for now) as realistically as possible using multiple bodies interconnected with custom joints. I am using NGD and Ogre (OgreNewt), FMOD and have extended OgreMax to support physics so I can create my scenes directly in 3DS Max. Taking my first steps towards the car and ran into some problems.

Started off by trying to simulate basic McPherson strut suspension, that is basically a slider with travel (distance), angular limits and spring-damper (will add camber, caster etc later). I have got the joint to work but the problem is that it is not strong enough.

My frame (blue in the following images) weighs 800kg, the connecting arm (brown) weighs 4kg and the wheel (purple) 15kg. The arrows represent the locations where joint linear rows are added, the directions shows frame and strut orientations. The green and blue arrows are restricted in x and z axises, the red ones on the y axes (so the two blue and two green arrows should ideally be together and visible as one and the red ones should be at the same height). Unfortunately, as you will see in following images, NGD has hard time enforcing this :P

I understand the weight ratio of the frame (800kg) and arm (4kg) is large (200), but these are close to real values and for the sake of reality, I don't want to change this. If I make the frame lighter or the arm heavier, the situation improves but stability is still an issue (especially as the car will be traveling-crashing at high speeds later).

Schema from 3DS max showing what is what from the top view
Image

First angle on supports
Image
Free of force from the frame, it looks almost as it should but small misalignment already visible

Second angle on supports
Image
As in the previous image, the blue arrows should be at the same position and orientation

Wireframe view
Image

First angle on the ground with normal weights
Image
Positive travel limit has been set to small value so the wheel should support the frame (no suspension spring force used)

Second angle on the ground
Image
Quite large misalignment, the frame is on the ground

Another angle on the ground
Image
Big misalignment, both the suspension slider and wheel hinge fail under the weight of the frame

Some external picking force applied
Image
Easy to displace the joint, goes nuts when applying big forces to the frame

Setup with the frame weighing only 5kg
Image
Much stiffer and more stable, but not realistic

Hope the images explain my problem, I can probably make a demo if needed.

Here's my McPherson strut joint code:

BasicSuspension.h
Code: Select all
#ifndef BasicSuspension_H_INCLUDED
#define BasicSuspension_H_INCLUDED

#include <OgreNewt.h>
#include "PhysicsObject.h"

namespace VD
{
    namespace Vehicle
    {
        class BasicSuspension : public OgreNewt::CustomJoint
        {
            public:
                enum Side {LEFT, RIGHT};

                BasicSuspension(PhysicsObject* frame, PhysicsObject* suspension, Ogre::Vector3 pin, Side side, float caster = 0.0f, float scrubRadius = 0.1f);
                ~BasicSuspension();
                void submitConstraint(Ogre::Real timeStep, int threadIndex);

            private:
                PhysicsObject* frame;
                PhysicsObject* suspension;

                Ogre::Vector3 localPos0;
                Ogre::Quaternion localOrient0;

                Ogre::Vector3 localPos1;
                Ogre::Quaternion localOrient1;

                Side side;

                float caster;
                float scrubRadius;
                float lastAngle;

                float angleCounter;
        };
    } // namespace Vehicle
} // namespace VD

#endif // BasicSuspension_H_INCLUDED


BasicSuspension.cpp
Code: Select all
#include "Vehicle/BasicSuspension.h"

#include "Debug/VectorDebugger.h"

#define MIN_JOINT_PIN_LENGTH -1.5f

namespace VD
{
    namespace Vehicle
    {
        BasicSuspension::BasicSuspension(PhysicsObject* suspension, PhysicsObject* frame, Ogre::Vector3 pin, Side side, float caster, float scrubRadius) : OgreNewt::CustomJoint(6, suspension->body, frame->body)
        {
            this->frame = frame;
            this->suspension = suspension;
            this->side = side;
            this->caster = caster;
            this->scrubRadius = scrubRadius;

            Ogre::Vector3 framePosition, suspensionPosition;
            Ogre::Quaternion frameOrientation, suspensionOrientation;

            frame->body->getPositionOrientation(framePosition, frameOrientation);
            suspension->body->getPositionOrientation(suspensionPosition, suspensionOrientation);

            float scrubSign = (side == LEFT ? 1.0f : -1.0f);

            Ogre::Vector3 point(suspensionPosition.x + scrubRadius * scrubSign, suspensionPosition.y, suspensionPosition.z);

            pinAndDirToLocal(point, pin, localOrient0, localPos0, localOrient1, localPos1);

            lastAngle = 0.0f;

            Debug::VectorDebugger::getSingleton().create("p0", frame->node->getCreator()->getRootSceneNode())->setColour(Ogre::ColourValue(0.0f, 1.0f, 0.0f));
            Debug::VectorDebugger::getSingleton().create("p1", frame->node->getCreator()->getRootSceneNode())->setColour(Ogre::ColourValue(0.0f, 1.0f, 0.0f));

            Debug::VectorDebugger::getSingleton().create("q0", frame->node->getCreator()->getRootSceneNode())->setColour(Ogre::ColourValue(0.0f, 0.0f, 1.0f));
            Debug::VectorDebugger::getSingleton().create("q1", frame->node->getCreator()->getRootSceneNode())->setColour(Ogre::ColourValue(0.0f, 0.0f, 1.0f));

            Debug::VectorDebugger::getSingleton().create("r0", frame->node->getCreator()->getRootSceneNode())->setColour(Ogre::ColourValue(1.0f, 0.0f, 0.0f));
            Debug::VectorDebugger::getSingleton().create("r1", frame->node->getCreator()->getRootSceneNode())->setColour(Ogre::ColourValue(1.0f, 0.0f, 0.0f));
        }

        BasicSuspension::~BasicSuspension()
        {
        }

        void BasicSuspension::submitConstraint(Ogre::Real timeStep, int threadIndex)
        {
            // used for camber, caster etc, modifies the stut orientation
            Ogre::Quaternion strutOrientationModifier(Ogre::Degree(caster), Ogre::Vector3::UNIT_X);

            Ogre::Quaternion globalStrutOrientation, globalFrameOrientation;
            Ogre::Vector3 globalStrutPosition, globalFramePosition;

            localToGlobal(localOrient0, localPos0, globalStrutOrientation, globalStrutPosition, 0);
            localToGlobal(localOrient1, localPos1, globalFrameOrientation, globalFramePosition, 1);

            Ogre::Vector3 p0 = globalStrutPosition;
            Ogre::Vector3 p1 = globalFramePosition + (globalFrameOrientation * Ogre::Vector3::UNIT_Y) * (p0 - globalFramePosition).dotProduct(globalFrameOrientation * Ogre::Vector3::UNIT_Y);

            addLinearRow(p0, p1, globalFrameOrientation * Ogre::Vector3::UNIT_X);
            addLinearRow(p0, p1, globalStrutOrientation * Ogre::Vector3::UNIT_Z);

            // these are the arrows you are seeing in the images
            Debug::VectorDebugger::getSingleton().get("p0")->transform(p0, globalFrameOrientation);
            Debug::VectorDebugger::getSingleton().get("p1")->transform(p1, globalStrutOrientation);

            Ogre::Vector3 q0 = p0 + globalStrutOrientation * Ogre::Vector3::UNIT_Y * MIN_JOINT_PIN_LENGTH;
            Ogre::Vector3 q1 = p1 + globalFrameOrientation * Ogre::Vector3::UNIT_Y * MIN_JOINT_PIN_LENGTH;

            addLinearRow(q0, q1, globalFrameOrientation * Ogre::Vector3::UNIT_X);
            addLinearRow(q0, q1, globalStrutOrientation * Ogre::Vector3::UNIT_Z);

            Debug::VectorDebugger::getSingleton().get("q0")->transform(q0, globalFrameOrientation);
            Debug::VectorDebugger::getSingleton().get("q1")->transform(q1, globalStrutOrientation);

            Ogre::Vector3 r0 = p0 + globalStrutOrientation * Ogre::Vector3::UNIT_X * MIN_JOINT_PIN_LENGTH;
            Ogre::Vector3 r1 = p1 + globalFrameOrientation * Ogre::Vector3::UNIT_X * MIN_JOINT_PIN_LENGTH;

            addLinearRow(r0, r1, globalFrameOrientation * Ogre::Vector3::UNIT_Y);

            Debug::VectorDebugger::getSingleton().get("r0")->transform(r0, globalFrameOrientation);
            Debug::VectorDebugger::getSingleton().get("r1")->transform(r1, globalStrutOrientation);

            Ogre::Real distance = (globalStrutPosition - globalFramePosition).dotProduct(globalStrutOrientation * strutOrientationModifier * -Ogre::Vector3::UNIT_Y);

            float minLimit = -0.1f;
            //float maxLimit = 0.4f;
            float maxLimit = 0.1f; // make it small so it is hit soon

            // suspension travel limit
            if(distance <= minLimit)
            {
                Ogre::Vector3 p0 = globalStrutPosition;
                Ogre::Vector3 correction = globalStrutOrientation * strutOrientationModifier * -Ogre::Vector3::UNIT_Y * (minLimit - distance);
                Ogre::Vector3 p1 = p0 + correction;

                //std::cout << "p0: " << p0.x << " x " << p0.y << " x " << p0.z << std::endl;
                //std::cout << "p1: " << p1.x << " x " << p1.y << " x " << p1.z << std::endl;
                //std::cout << "CORR: " << correction << std::endl;

                addLinearRow(p0, p1, globalStrutOrientation * strutOrientationModifier * -Ogre::Vector3::UNIT_Y);
                setRowMinimumFriction(0.0f);
            }
            else if(distance > maxLimit)
            {
                Ogre::Vector3 p0 = globalStrutPosition;
                Ogre::Vector3 correction = globalStrutOrientation * strutOrientationModifier * -Ogre::Vector3::UNIT_Y * (maxLimit - distance);
                Ogre::Vector3 p1 = p0 + correction;

                addLinearRow(p0, p1, globalStrutOrientation * strutOrientationModifier * -Ogre::Vector3::UNIT_Y);
                setRowMaximumFriction(0.0f);
            }

            Ogre::Vector3 s0 = globalStrutOrientation * Ogre::Vector3::UNIT_Z;
            Ogre::Vector3 s1 = globalFrameOrientation * Ogre::Vector3::UNIT_X;
            Ogre::Radian currentAngle = s0.angleBetween(s1);

            /* Angle limit
            float minAngleLimit = 45.0f;
            float maxAngleLimit = 135.0f;

            //std::cout << "CURRENT: " << currentAngle.valueDegrees() << std::endl;

            if(currentAngle.valueDegrees() < minAngleLimit)
            {
                float relativeAngle = minAngleLimit - currentAngle.valueDegrees();

                //std::cout << "RELATIVE: " << relativeAngle << std::endl;

                addAngularRow(Ogre::Degree(relativeAngle), globalStrutOrientation * strutOrientationModifier * Ogre::Vector3::UNIT_Y);
                setRowMaximumFriction(0.0f);
            }
            else if(currentAngle.valueDegrees() > maxAngleLimit)
            {
                float relativeAngle = maxAngleLimit - currentAngle.valueDegrees();

                //std::cout << "RELATIVE: " << relativeAngle << std::endl;

                addAngularRow(Ogre::Degree(relativeAngle), globalStrutOrientation * strutOrientationModifier * Ogre::Vector3::UNIT_Y);
                setRowMinimumFriction(0.0f);
            }
            */

            lastAngle = currentAngle.valueDegrees();
        }
    } // namespace Vehicle
} // namespace VD


Just getting started with custom joints, so there may be errors there :P

My target is to simulate a car as realistically as possible, using all kind of suspensions made out of custom joints describing the actual geometry, all the way to some sort of tire model, differentials and so on. Hope not to get stuck already :D
kallaspriit
 
Posts: 216
Joined: Sun Aug 14, 2005 6:31 pm

Re: Vehicle :: Suspension

Postby Julio Jerez » Tue Jun 30, 2009 9:39 am

The only way you could do that is:
Using the exact solve and increase simulation rate way high, maybe 400, or 500 hundred fps.
That kind of assembly imposes a system matrix with such high condition number that the convergence of the solver is almost zero.
Iterative solve will not help at all they will waste all the iterations cycling only making correction to the body with the heaviest mass, and the light body will get almost nothing.
You can probably model that but I doubt you can do it for real time simulation.
people do that kind of stuff with Newton with Google SkectchyPhysics, but they do it like I told you to produce realistic or funny animations that they record


As a quick test I will try increasing the simulation to 300 or 600 fps, and the iteration count to maybe 16, of 32 before trying the exact solver.
Julio Jerez
Moderator
Moderator
 
Posts: 12426
Joined: Sun Sep 14, 2003 2:18 pm
Location: Los Angeles

Re: Vehicle :: Suspension

Postby kallaspriit » Tue Jun 30, 2009 10:31 am

Increased the arm weight to 10kg, switched to exact solver and set physics frame rate to 120 and the results are much more promising, going to try to get all four wheels to it and see whether this box is any way drivable and stable enough :)
kallaspriit
 
Posts: 216
Joined: Sun Aug 14, 2005 6:31 pm


Return to General Discussion

Who is online

Users browsing this forum: No registered users and 0 guests

cron