3
\$\begingroup\$

I am trying to understand how to implement and combine steering vectors for autonomous agents as described by Craig Reynolds.

My understanding is that each steering force should be calculated separately and then all of the steering forces should be added together and then applied.

I understand that each steering force is equal to the desired velocity for a particular behavior, minus the agent's current velocity.

I am trying to combine two basic behaviors: Seek and Flee. Below are two diagrams to illustrate my issue.

enter image description here

I have an agent that is Seeking a target directly ahead of it, and fleeing from a target below it. When the target is at rest (current velocity = 0), the steering forces for each behavior are equal to their respective desired velocities:

vSteer = vDesired - currentVelocity = vDesired - 0 = vDesired

And the combined steering force points up and to the right, in blue. This is roughly what I would expect.

However, if the agent is moving, we get a completely different result. For the sake of simplicity, say the agent is already moving at top speed in the direction of the target. So the agent's current velocity is equal to its desired seek velocity.

enter image description here

When the agent's current velocity is equal to the desired seek velocity, then the seek steering force is 0 (vSteer = current - desired = desired - desired = 0). The flee steering force then is equal to the desired flee velocity minus the current velocity, which produces a vector that points BACKWARD! And since the seek steering force is zero, the total steering force equals the flee steering force.

I noticed this issue while trying to implement a separation force, which is essentially the situation illustrated in the diagrams but with the red X representing a neighboring agent. The net effect is that agents maintain separation, but move at about half speed, with a seek steering force pointing forward, a flee steering force pointing backward and away from the neighbor, that largely end up cancelling each other out.

It really doesn't seem like any component of the flee force should be pointing backwards here. What am I misunderstanding?

\$\endgroup\$

4 Answers 4

1
\$\begingroup\$

I would solve this on two levels:

On top level, the Flee / Seek urges battle it out to determine where the agent should go to. This stage determines a location. Depending on what is urgent at the moment, it could be more influenced by threat or by target.

Below that, at the lower level, I would take the resulting target location, and actual current location to derive the steering force by use of a PID Controller.

\$\endgroup\$
3
  • \$\begingroup\$ This doesn't answer my question. It did, however, introduce me to the concept of a PID Controller. After reading your post, this seems like a great tool for autonomous error correction IF you know the error (if you know your target). My issue is with finding the desired velocity (target) to begin with. That aside, thanks for the introduction to PID. It is certainly a component I should introduce to my code. This seems like a powerful thing to add to steering logic, to derive the steering force from, rather than just steering = desired - current. \$\endgroup\$ Commented Apr 9, 2018 at 19:49
  • \$\begingroup\$ In your blog post about PID design, you define the integralError = integralError * (1 - dt) + error * dt. Why? Isn't it just integralError = integralError + error * dt ? \$\endgroup\$ Commented Apr 13, 2018 at 1:25
  • \$\begingroup\$ You want to use 'historic' error in the controller, but you do want to limit the length of that history. Only the error over the last few seconds, say. A good way to approximate this is by doing a low-pass filter on the error signal. If you were to use your formula, then errors, from a long long time ago, would always be weighted in, indefinitely. Not what you want. \$\endgroup\$
    – Bram
    Commented Apr 13, 2018 at 5:48
1
\$\begingroup\$

In 'vSteer = current - desired', you may want to only use the component, i.e. projection, of the current velocity on the desired velocity, instead of current. That way, each controller only modifies the steering force along the direction of its desired velocity; the steering force would only point upwards in ex. 2.

The component of a on b is the magnitude of b times the cosine of the angle between a and b.

\$\endgroup\$
1
\$\begingroup\$

You are subtracting the current velocity multiple times, once for each behavior (in this case 2 times), when you should be subtracting it only once at the end, when you have summed all of the behaviors together.

\$\endgroup\$
3
  • \$\begingroup\$ This does seem like the solution. But the sources I have been reading (e.g. gamedevelopment.tutsplus.com/tutorials/…) all explicitly say that each individual steering force should be obtained by subtracting desired velocity for each individual behavior minus the current velocity, and then to add up all of the steering vectors. But I have move ahead with implementation with your advice. \$\endgroup\$ Commented Apr 9, 2018 at 19:54
  • \$\begingroup\$ Now that I look closer, I think there is no problem at all with your code. The forces are calculated to correct for the current velocity. As the velocity gets more in line with the desired velocity, the resulting vector of all the forces more closely aligns with it. So, the reason the vector points backwards is to simply turn faster before getting to the correct direction. As the velocity becomes the desired one, that backwards force disappears. Have you tried graphically representing the agents and their movement? It seems to me that it is correct, it's just counter-intuitive. \$\endgroup\$ Commented Apr 10, 2018 at 13:06
  • \$\begingroup\$ It depends on the situation. There may be a context in which this makes sense. But I was trying to implement separation, where this doesn't make sense and causes an actual bug (movement at half speed). What I've concluded is that the flocking behaviors (separation, cohesion, alignment) aren't meant to individually produce steering forces from the velocity vector; they produce forces that should be added to the velocity vector. \$\endgroup\$ Commented Apr 10, 2018 at 20:31
1
\$\begingroup\$

The way I usually handle this is to add the forces—which should be independent of the current velocity—then check whether the result has a greater magnitude than the maximum force I want to be applying, and normalize it to that magnitude if so. This preserves the relative “strengths” of the forces without allowing their combined result to blow up the system. Pseudocode:

combinedForce = seekForce + fleeForce
combinedForceMagnitude = length(combinedForce)
if combinedForceMagnitude > maximumForceMagnitude:
    combinedForce *= (maximumForceMagnitude / combinedForceMagnitude)
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .