numerical spring solver

Started by
5 comments, last by MrRowl 16 years ago
Is using a method such as RK4 a poor choice for implementing a dampened spring? I've got a robust RK4 solver implemented such that in order to create a new physics object you simply need to write the object's 'derivative' function (e.g. the force and torque function in David Eberly's Game Physics book). This is done by overriding pure virtual methods in my RigidBody physics class. The problem is that when implementing this for a very simple dampened spring (which I've implemented in simpler cases in which stability was not an issue) I do not get the proper results. The current use is having an object 'trail' behind a vehicle (a simple hovercraft). The hovercraft's movements are updated using the same type of RK4 solver (but has it's own force_and_torque function), and I want the camera to follow behind it as if it were connected via a spring. There are no 'torsional/twisting' effects on the spring, e.g. it just applies the force in the proper direction to move towards the desired position. When using the simple Euler solver it basically works as youd expect, but because the Hovercraft is being updated with RK4 you get 'jitters' where the spring all of a sudden surges ahead to make up the difference over the past few thousand frames or so (the difference between the two solvers). Note that the Hovercraft's new position and orientation are resolved, and *then* the spring is updated. Perhaps I should be updating both simultaneously, making the spring surge towards each of the Hovercraft's intermediate positions? Perhaps that'd mean I've have to embed the spring code within the Hovercraft's force_and_torque function Whew, I really don't know if I'm making any sense. Anywho, here's the force and torque function for the dampened spring (note there is no torque).

void	CamSpring::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)
{
	StateVector & hover_state = pParent->CurrentState;
	Vector3	desired_pos = hover_state.mPosition + (hover_state.mMat_Orientation * mLocalPos);

	Vector3	sep = desired_pos - position;
	float	sep_dist = sep.FastGetLength();
	Vector3	sep_dir = sep;	sep_dir.Normalize(sep_dist);
	Vector3	force  = sep_dir * sep_dist * this->spring_k;
	*output_force += force;
	force.Normalize();
//	DebugPrintVector("force",*output_force,gpTextManager);
	DebugPrintFloat("fds",DotProduct(&force,&sep_dir),gpTextManager);
}



Advertisement
@53

I've implemented several spring simulations with the rk4, and they've worked very well compared to lower order numerical integrators. The rk4 allows for very high k values for low point masses without exploding, so if youre doing a force based simulation I'd reccomend you to stay with rk4.

However, I think the camera bounce you describe comes from using a relatively low k value in combination with the lack of a damper in your spring function.

you want something like this:

Vector3 dst = ship.position - camera.position
Vector3 vel = ship.velocity - camera.velocity
float SpringLength = sqrt(DotProduct(dst, dst))

float forceScalar = (dst - this->spring_RestLength) * this->spring_k - this->spring_damper * ((DotProduct(dst, vel))/SpringLength);

vector3 ForceVector = force * (dst/SpringLength)

Basically the damper finds the scalar projection of the velocity vector onto the spring vector and applies a damping force which is always proportional to the relative speed of the spring point masses and always in the opposite direction - hence damping.

Cheers, Michael
Some background. The explicit Euler integrator (commonly used by new physics programmers) is unconditionally unstable for springs without damping, but is conditionally stable (not working with time steps that are too large) if there is damping. So, technically, explicit Euler could be used here. The net result of conditionally stable is that you have to adjust the time step (usually making it smaller) to achieve a stable simulation. In games, which like to have physics updated roughly in sync with frame rate while requiring far less than 1 frame time to actually compute the update, you don't necessarily have the luxury of being able to use an arbitrarily small time step.

If you do the analysis, RK4 is better than explicit Euler. But, contrary to what is sometimes said, not hugely so. The main practical benefit for games is that it is stable for larger time steps. But (depending on spring stiffness and amount of damping) not necessarily time steps twice as large. It too is conditionally stable and won't work universally. One of the strong benefits of RK4 (and some of the lower-order RK's) is that they are conditionally stable without damping also, e.g., if you control the time step then even with no damping (or kind of floating point epsilon minimal damping) it can still be stable. (Er, my GDC 2001 talk provides some very technical details on this, plus some references.) (Another benefit is in some cases it can capture rapid behavior/dynamics changes more quickly due to the higher precision.)

So, agreeing with h4tt3n, I would say its a good choice, and you can probably get it to work for you. Other things for consideration are the widely used and discussed velocity-less Verlet integrator, and fully implicit integrators, the latter of which in theory have no time step limits (but come with their own issues).

With a spring, bounce/oscillation comes with the territory, even with damping. I think it'll be a matter of tweaking the damping. If you are able to achieve critical damping (a function of stiffness and the mass being moved), or over-damping, the motion will damp out before the first oscillation and everything will appear smooth, though that might not look quite right to you either, e.g., you might not have the energy or dynamic you are looking for.
Graham Rhodes Moderator, Math & Physics forum @ gamedev.net
Hi, I just wanted to come back and express thanks. The problem is basically solved. Part of the problem was orbiting around the desired camera position, and the rest of the problem has just been, as you both said, tweaking the K and dampen factors of the particular spring (so, there's otherwise nothing wrong with the solver).
Quote:Original post by isenthalpic53
so, there's otherwise nothing wrong with the solver.


Actually... from what you wrote I think it is still implemented incorrectly in that it sounds like the "intermediate" hovercraft positions are not being used during the spring/camera update.

When using higher order solvers like RK4 you should think of solving the system, rather than individual bodies. If you have virtual methods in your body class called things like "integrateRK4(dt)" then this is certainly not the case. Yes, it will/may still "work", but you're not getting everything that the higher order integration should give you (and you're not getting what you pay for with CPU time either!).

Quote:
in that it sounds like the "intermediate" hovercraft positions are not being used during the spring/camera update.


Dear Mr. Rowl, you are absolutely correct about this, although with this particular system (spring and hovercraft coupling) the differences between the two approaches from an interactive standpoint are not noticeable. I did notice differences numerically, but they are negligible and the system does not explode.

In your implementation(s), do you have all rigid bodies integrate inside the same, more 'universal' "IntegrateRK4" function? I do in fact have everything separate, unless they comprise the same system (e.g. the hovercraft air cushion is approximated by a circular array of dampened springs).

Thank you for the response.
Quote:Original post by isenthalpic53
In your implementation(s), do you have all rigid bodies integrate inside the same, more 'universal' "IntegrateRK4" function?


No but I should!

This topic is closed to new replies.

Advertisement