   Air cushion simulation woes
  I am simulating an air cushion underneath the chassis of a 3D model which is supposed to be a hovertank.  A hovertank air cushion is simply a region of high pressure which keeps the craft in the air.  When it goes above an obstacle (i.e. lets just say a ramp) it increases the pressure near the front, thus lifting the nose of the vehicle.At each frame, the Physics Manager calls each rigid body's physics_update() function, a virtual function which does special physics stuff depending on what the rigid body is.  I'm simulating the air cushion by creating sample points in a circular fashion underneath the body of the hovertank.  The sample points are spaced evenly, and the center of gravity is simply assumed to be the geometric center of the sphere that encloses the craft (I am taking the easiest routes possible, e.g. using spheres, using geometric centers for everything, etc).  It traces from each starting point of each sample point down to the world and measures the distance to the ground.  Each sample point behaves similarly to a basic dampened spring, in which the force of the spring is proportional to the compression distance and another term is added which minimizes the relative velocity of the sample point with respect to the ground...the relative velocity is that which emerges from the linear and angular velocity of the craft.  I use an improved euler integrator, orientation is integrated with quaternions and then converted to matrices + other representations, and 100 FPS for the physics.  *Most* of the time, the air cushion works pretty darn good, but all too often I will push the hovertank (which is currently pilotless) up a ramp, it will come back down but then start to 'wobble.'  These tiny oscillations sometimes die out, but sometimes they get worse and I experience the typical spring explosion system.What bothers me is that I am dampening the springs substantially, and my integrator is more sophisticated than simple euler, midpoint + it picks up a few extra terms in the taylor series, which I believed to be sufficient for this application.Is there anything missing with the theory of how I am representing this air cushion?  My understanding of it seems solid, I suspect the problem is that while the integrator is *pretty* good is simply may not be good enough?  Any suggestions?EDIT:I guess I should include some more info of what I have been trying:*Limit the maximum amount of compression of each spring/sample point*Each spring applies a force equal to the weight of the vehicle divided by the number of sample points, at a given height.  When the distance to the ground is less than this height, the force increases, when the distance to the ground is greater than this height, a force is still applied but it dissipates (inversely proportional to the compression instead of directly proportional)*Use a grid instead of a circle*supply a counter balance which changes the center of gravity with respect to the orientation, so when the hovercraft tips 'back' the CG moves 'forward'I am strongly considering ditching a math based approach, and instead training a neural network which takes as inputs per sample point the orientation, relative velocity and distance to ground and outputs a thrust magnitude, and writing a trainer program and selecting the most fit training sets and training the neural network.  I'm not sure if this approach would make it easier or much harder.   
Quote:When the distance to the ground is less than this height, the force increases, when the distance to the ground is greater than this height, a force is still applied but it dissipates (inversely proportional to the compression instead of directly proportional)Is this something that you implemented before you started seeing the out of control oscillation problem?  My gut is telling me that this is the problem.  If your extra compression force is outweighing the dampening logic, then you'd get what you describe almost every time a given spring is compressed beyond the "magic" threshold.-me   ;  I agree with your suggestion and have taken that aspect out of the equation.  I have also limited the amount of force that a sample point can emit.  This has yielded great dividends.This is truly puzzling and must somehow be related to some sort of mathematical uncertainty.  I simply placed the hovercraft on level ground and caused it to rotate around its local Y axis, and this caused it to start wobbling wildly out of control!  I can push the hovercraft up a ramp, and so long as it doesn't start to rotate about its local Y axis it will slide back down perfectly. I can't think of anything else that would be causing this.   ;  Why not only take out the downward force? Gravity should be sufficient to simulate that. Infact, I'd say that you should use a downward push at all times, just have it dependant on how high off an object the tank is. That's more how it would act in real life, so it might yeild better simulation.   ;  So you are saying that the orientation of the thrust should never be rotated?  The problem still does occur on a completely level surface, in which the thrusters of the air cushion only ever point straight down...it still wobbles :(It's *really* weird, I have a hovertank sitting on level ground, rotating about the y axis...I am printing out the force applied from each thruster.  Remember, the force is somewhat proportional to compression, so when it compresses, the given thruster will push a little harder (but not too much, it is limited which has helped the stability).  Every so often, eventhough this is on totally level ground, the simulation thinks there is a change in compression, causing the whole system to start to wobble madly.  But because the moment from the thrusters is perpendicular to the Y axis, it cannot slow this wobbling down (that is my belief at this time).HmmmMMm   ;  Somewhat a fudge, but you might play with adding a gyroscopic force to keep the y axis fairly perpindicular to the ground.  Something as simple as a spring force based on the displacement from 90 degrees perpindicular to the ground its on.   ;  Somehow, this has to do with how I'm representing the orientation of the craft.Basically, it only becomes unstable when it deviates significantly from 0 degrees about the y axis.  I am normalizing the quaternion orientation (the primary representation of orientation) before converting it to other representations, I am not feeding the quaternion an euler angle greater than 180 degrees at any point...I am extremely confused as to what could be causing this.If it starts at 0.0f degrees Yaw (angle about the y axis) it sits fine, if I start it with an angle of 180 degrees yaw, it then becomes wobbly (but the quaternion orientation is still valid, as far as i know, for it cannot exceed 180 degrees, but can be equal to it, about some particular axis).I'm just very, very stumped as to what could be causing this.   ;  I know bumping is frowned upon, but I just wanted to come back and say that simply implementing a better integrator completely solved the problem (RK4 vs what I described earlier), meaning the approach I thought of does work, when implemented accurately enough.Here is the actual implementation of it (finding the force and torque...this is called various times during the differential equation solving step, needs to be optimized a bit further, but it works)EDIT:oh yeah, transformed_cushion returns the position of the air cushion sample point at index ivoidHovertank::Force_and_Torque(Vector3 *output_force,Vector3 *output_torque,Vector3 & position,Quaternion & quat_orientation,Vector3 & linear_momentum,Vector3 & angular_momentum,Matrix3x3 & mat_orientation,Vector3 & linear_velocity,Vector3 & angular_velocity){*output_force  += Vector3(0.0f,-gpPhysicsManager->mGravAccel*mMass,0.0f);//applies weightAir_Cushiontransformed_cushion=mAirCushion;Matrix4x4cushion_sample_transformation;cushion_sample_transformation.SetTranslation(&position);cushion_sample_transformation = mat_orientation;//CurrentState.mMat_Orientation;for(int i = 0; i < transformed_cushion.num_sample_points; i++){transformed_cushion = cushion_sample_transformation * transformed_cushion;}transformed_cushion.orientation=mat_orientation * transformed_cushion.orientation;std::vector<int>leafs;Vector3start = position;Vector3end = position + (transformed_cushion.orientation * transformed_cushion.rest_height);MoveTouchNode(gpBSP,0,&start,&end,mRadius,leafs);//collect leafsVector3total_cushion_force(0.0f,0.0f,0.0f);Vector3total_cushion_moment(0.0f,0.0f,0.0f);for(i = 0; i < transformed_cushion.num_sample_points; i++){end=transformed_cushion + (transformed_cushion.orientation * (transformed_cushion.rest_height));//FIXME: transformed_cushion.rest_height - radius ???Vector3pos = transformed_cushion;HitInfoCGTrace=TraceSphereToBSPFaces(gpBSP,transformed_cushion,end,transformed_cushion.sample_radius,gpPhysicsManager->physics_epsilon,leafs);if(CGTrace.Time < 1.0f){Vector3local_pos=transformed_cushion - position;//reflects rotation only#ifUSE_REAL_DISTfloatdist_to_point=(CGTrace.HitPoint - transformed_cushion).GetLength();#elsefloatdist_to_point=(DotProduct(&CGTrace.HitPoint,&transformed_cushion.orientation) - DotProduct(&transformed_cushion,&transformed_cushion.orientation));#endiffloatcompression=fabs(dist_to_point - transformed_cushion.rest_height);floatbasic_force_magnitude=compression * transformed_cushion.weight_per_thruster;Vector3sample_force = transformed_cushion.orientation * basic_force_magnitude * -1.0f;if(basic_force_magnitude > transformed_cushion.max_sample_force)basic_force_magnitude = transformed_cushion.max_sample_force;floattotal_rel_vel(0.0f),rel_vel_fromlinear(0.0f),rel_vel_fromangular(0.0f);rel_vel_fromlinear=DotProduct(&transformed_cushion.orientation,&linear_velocity);rel_vel_fromangular=DotProduct(&transformed_cushion.orientation,&CrossProduct(&angular_velocity,&local_pos));total_rel_vel=rel_vel_fromlinear + rel_vel_fromangular;sample_force+=transformed_cushion.orientation * total_rel_vel * -transformed_cushion.dampen_coefficient;total_cushion_force+=sample_force;total_cushion_moment += CrossProduct(&local_pos,&sample_force);}}*output_force+=total_cushion_force;*output_torque+=total_cushion_moment;} 