Crowdv3: getting it right

[From Bill Powers (990907.0726 MDT)]

Those of you who are running the part of the crowd program I distributed
may have noticed problems when the forward velocity goes to zero. I think
the old program had a similar problem, which didn't show up until a
goal-seeker actually got to the goal. When the speed (forward velocity or
FwdVel) approached zero, the "person" would often go into spasms, tumbling
around the goal-point. That's one reason I wanted a new version of this
program.

In the current version of the motion.pas program, the problem comes from
the computation of turning rate, which I gave as, approximately,

TurnRate = Fs/(Mass*FwdVel)

Notice the "FwdVel" in the denominator -- that's the problem. When the
foward velocity approaches zero, the turning rate gets very large for any
sideward force (Fs) at all other than exactly zero.

For the past few days, I've been trying an approach in which there is a
"forward" force for varying speed and a "sideward" force for varying
direction. That works, too, until the forward velocity approaches zero.
Then the same problem of infinite turning rate shows up.

Playing around with the problem this morning, I took the derivation one
more step, more or less by accident (reorganizing!). Here's how it goes
from the beginning, in a notation where ^ means raised to the power that
follows it:

(1) Fs = Mass*FwdVel^2/radius (formula for sideways or centrifugal force
that is produced when the path curves with a given radius, at a given
velocity along the curve)

rearranging (1),

(2) radius = Mass*fwdvel^2/Fs (given mass, sideward force and forward
velocity, calculate radius of curvature)

(3) Turnrate = fwdvel/radius. Geometrical fact: turning rate in radians per
second = velocity along curve divided by radius of curvature. The turning
rate is the rate at which the direction-angle (in laboratory space) of
forward motion is changing.

Substituting (2) into (3) to eliminate radius, compute rate of change of
direction

TurnRate = FwdVel*Fs/(Mass*FwdVel^2) or simplifying,

(4) TurnRate = Fs/(Mass*FwdVel).

Equation 4 is the one used in "motion.pas" with slightly different notation.

It says that if we vary the sideward force Fs as a means of directional
control, the turning rate that results will depend on the sideward force
and (inversely) on the forward velocity. That's where the problem arises.
When the forward velocity goes to zero, the turning rate becomes infinite
for any finite sideward force.

But let's go one simple step further: solve (4) for the sideward force:

(5) Fs = Mass*FwdVel*TurnRate

This turns out to be the equation I should have used. It illustrates a
drawback of algebra, which is that algebra does not recognize the
difference between a dependent variable and an independent variable. Let's
pause to see what this means.

An independent variable is one that you can change freely, and a dependent
variable is one that changes because of the change in the independent
variable. Equation 4 and equation 5 express exactly the same relationship
among variables because they are the result of a legal series of algebraic
rearrangements that leave the equality true. But by custom, we think of the
single variable on the left as a _dependent_ variable whose value depends
on the values of the variables on the right side of each equation.

In (4), the dependent variable is TurnRate, which is affected by, among
other variables, the sideward force Fs (which we use as a means of changing
direction). But in (5), the dependent variable is sideward force Fs, which
is affected by, among other variables, TurnRate (i.e., TurnRate is a change
in direction that we use to produce a sideward force) . So the question now
arises, which variable do we want to treat as independent, with the other
variable then depending (in part) on it? I picked TurnRate as the dependent
variable, and that led to the infinite turning rate when the forward
velocity went to zero. Now I am saying we have to treat the turning rate as
independent, using it (together with mass and forward velocity) to compute
the sideward force.

Note that in (5), the sideward force simply goes to zero when the forward
velocity goes to zero. We now have a well-behaved equation.

It's funny, but I don't remember anyone in a physics course ever explaining
to me this problem with dependent and independent variables. It probably
_was_ explained to me, but I was probably goofing around. I did a lot of
that in high-school. In college they assumed that we already knew that
simple stuff.

If TurnRate is the independent variable, we need a control system that
controls the rate of turn as a function of directional error:

TurnRate := DirGain*(RefDirDiff - DirDiff);

"DirDiff" means the difference between the current direction of motion and
the direction to a target. Remember that the reference difference is not
necessarily zero -- that is, the control system need not aim the motion
directly at the target, but can maintain it at any angle relative to the
target.

Given the turn rate, we can compute the direction as an integral
(summation) over all changes in direction and then close the loop:

Dir := Dir + TurnRate*dt;

DirDiff = AngleToTarget - Dir;

So, finally, we have a number for the sideward force, and we can combine it
with a number for the forward force (from the speed control system) to get
a total x force and a total y force. Given those two forces we can compute
the acceleration, velocity, and position of the person, dt (or 0.01) second
later.

I'll revise the motion.pas program and post it as soon as I can -- there
are some other things I have to do.

···

-----------------------------------------
A note to Richard Kennaway. You will notice that something is happening
here very similar to what I did in the Inverted Pendulum program. In that
program, by treating the _change in position_ as an independent variable
and using a spring constant to convert a stretch or compression of the rod
into a force, I was able to compute forces rather than starting with forces
and computing a change in position. Now I'm computing a change in direction
and using that to compute a sideward force, instead of starting with the
sideward force and computing a new direction. In the case of the inverted
pendulum, the model of the physical pendulum was greatly simplified and
still gave the right results. I hope that is happening here, too. Is this
another step toward a new way to write simulations? Or have I just
rediscovered the calculus of variations?

Best,

Bill P.