03-29-08 - 1

While I'm at it I'll write up the PD I'm using for the record. This is not an exact solution of the PD - it's an implicit Euler step. You can actually totally solve the PD exactly, but using an implicit Euler step actually is more stable than the exact solution, it adds a sort of fake drag which is a nice thing in practice. It's also a lot nicer numerically for doing tiny steps than using the exact solution with exponential functions.

K & D are unitless config parameters of the PD
  K = 1 and D = 1 are reasonable ; D = 1 is critical damping
time_scale = desired time to converge
dt = current time step
x0,v0 = start position & velocity
x1,v1 = target position & velocity

ks = 36 * (K / time_scale)^2
kd = 9 * K * D / time_scale

scale = ( 1  + kd * dt + ks * dt^2 )

a = ( ks * (x1-x0) + ( kd + ks * dt ) * (v1-v0) ) / scale

vt = v0 + a * dt
xt = x0 + vt * dt

If you had no min velocity that would be it. Note the funny scale numbers in ks & kd ; those are just there so that the time to converge roughly matches "time_scale", and so that K = D = 1 is a good choice. Note that K,D, and time_scale are really not three independent parameters, there are only two free parameters, but it's easier to tweak if they're seperated because you can leave K and D alone and just change "time_scale" to match your desired duration to converge.

To handle min velocity you add some logic after the above PD step :

mv = min velocity

if ( |xt - x0| <= (mv * dt) )
	if ( |x1 - x0| <= (mv * dt) && |v1 - v0| <= 2*mv )
		xt = x1;
		vt = v1;
		vt = (x1 > x0) ? mv : -mv;
		xt = x0 + vt * dt
This is ugly in various ways. The first part was a pure PD that works in N-d. Now we've gone to 1d. Also the first part worked with arbitrary end velocities. This min velocity part only really works for a still target. I think it's not too hard to fix for a moving target by using a min velocity relative to the target but I haven't bothered.

Now of course PD's don't always take the same amount of time to converge, unlike the cubic. Converge time depends on initial seperation and initial velocities. With the constants in the equations above, and if you set up your tweaks right, then you can have a PD that converges in roughly "time_scale" for typical starting situations. Here's how the time varies :

PD convergence graph

the X axis is the "time_scale" parameter, and the Y axis is actualy time to converge. If it converged in the time you wanted, all the points would be on the red line (Y=X). There are four series of points for different starting distances so you can see how converge time varies for starting distances. The distances are {160,320,480,640}. In the graph the start & end points are all at zero velocity.


all2one said...

Hi, Charles.
First of all, thank you for all the useful articles you've shared. I recently looked at your SmoothDriver stuff. It has been a very good learning experience for me.
I have one question for the PDClamped driver. I found some discrepancy between your equation and usual critically-damped-spring equations found in the "Critically Damped Ease-In/Ease-Out SMoothing", GPG4 article and Wikipedia.
Specifically I'm not sure where 36.0 and 9.0 factor for ks and kd each is coming from. Could you give me an insight about these, please?

all2one said...
This comment has been removed by the author.
cbloom said...

The question of how you scale the constants is purely convention. I chose to add scaling such that K=1 and D=1 are good starting points. See the note :

"note the funny scale numbers in ks & kd ; those are just there so that the time to converge roughly matches "time_scale", and so that K = D = 1 is a good choice"

all2one said...

Got it. Thx for clearing things out for me!

old rants