Anyone know how Obstacle Avoidance arguments are tunned?

Hi there, I have been in the exact same point of my development for quite some time now.
I am REALLY stuck with the pandai use, now matter how hard I try my AI controlled NPC simply passes through the obstacles as if they didn’t exist at all.
Right now it is really curious, no matter what I set the feeler_lenght to (tried from 10E-6 to 10E6) it will always go straight to the left for about 4 seconds, then it will run in the direction of the player and keep seeking it (its default behavior is to “pursue”) as it moves and it ignore all the four obstacles I placed to him.

According to the manual, the AI character avoid obstacles according to the characeter Feeler length, speed and size and also the obstacle size.

The manual says:

Still, I couldn’t get the math involved at all!
I tried some tunnings, but there is not much I can do about the obstacle/character size without destroying my scene.

Anyone have any formula, reference, code or anything that might help?

Thanks in advance.

I must check, did you forget to use the addObstacle() function?
Here’s the relevant function from the engine.

bool ObstacleAvoidance::obstacle_detection() {
  // Calculate the volume of the AICharacter with respect to render
  PT(BoundingVolume) np_bounds = _ai_char->get_node_path().get_bounds();
  CPT(BoundingSphere) np_sphere = np_bounds->as_bounding_sphere();
  LVecBase3f avoidance(0.0, 0.0, 0.0);
  double distance = 0x7fff ;
  double expanded_radius;
  LVecBase3f to_obstacle;
  LVecBase3f prev_avoidance;
  for(unsigned int i = 0; i < _ai_char->_world->_obstacles.size(); ++i) {
    PT(BoundingVolume) bounds = _ai_char->_world->_obstacles[i].get_bounds();
    CPT(BoundingSphere) bsphere = bounds->as_bounding_sphere();
    LVecBase3f near_obstacle = _ai_char->_world->_obstacles[i].get_pos() - _ai_char->get_node_path().get_pos();
    // Check if it's the nearest obstacle, If so initialize as the nearest obstacle
    if((near_obstacle.length() < distance) && (_ai_char->_world->_obstacles[i].get_pos() != _ai_char->get_node_path().get_pos())) {
      _nearest_obstacle = _ai_char->_world->_obstacles[i];
      distance = near_obstacle.length();
      expanded_radius = bsphere->get_radius() + np_sphere->get_radius();
    }
  }
     LVecBase3f feeler = _feeler * _ai_char->get_char_render().get_relative_vector(_ai_char->get_node_path(), LVector3f::forward());
     feeler.normalize();
     feeler *= (expanded_radius + np_sphere->get_radius()) ;
     to_obstacle = _nearest_obstacle.get_pos() - _ai_char->get_node_path().get_pos();
     LVector3f line_vector = _ai_char->get_char_render().get_relative_vector(_ai_char->get_node_path(), LVector3f::forward());
     LVecBase3f project = (to_obstacle.dot(line_vector) * line_vector) / line_vector.length_squared();
     LVecBase3f perp = project - to_obstacle;
     // If the nearest obstacle will collide with our AICharacter then send obstacle detection as true
     if((_nearest_obstacle) && (perp.length() < expanded_radius - np_sphere->get_radius()) && (project.length() < feeler.length())) {
       return true;
     }
     return false;
}

I can’t spot where they’re using any multiplier on the length of the feeler actually. It looks like the feeler length is just the radius of the obstacle plus 2 times the radius of the ai. Anyways its worked fine for me, albeit rather clumsily. Try lowering the priority of your pursue to something very low. I’m working on a thorough overhaul of the AI library so hopefully I’ll get around to doing some work on obstacle avoidance. If you know c++ I’d be glad for any assistance.

Thanks for the reply.
Yes, I did added the obstacles.
As far as I am concerned the obstacle avoiding does not have any priority, so I set the pursue priority to the max, will try reducing it.

What I observed so far is that:
The obstacle is a box which is pretty much a cube, the persuer won’t pass right in the middle of it, but it sure will pass inside it.
I tried using collision solids and noticed pretty much the same behavior.
Changing the mass of the pursuer seemed to alter its behavior a lot.

EDIT:

Tryed reducing the priority, no change at all, it still moved straight to left and then started chasing, ignoring the obstacles.

Regarding mass it should alter the behavior inversely to movt_force, so doubling mass would be the same as halving movt_force and vice versa. Effectively the acceleration of the ai is movt_force / mass. The max speed of the ai is actually max_force / mass, which makes NO sense, but that’s one of the things I’ve fixed in my yet unsubmitted work. If you want your ai to be more responsive set movt_force to be very high. You could also set mass very low, but that will equally increase the max speed which you probably don’t want.

I’ve read over the detection code a couple times, and it doesn’t appear that either the speed of the ai or the feeler length multiplier is used at all, contrary to the description. In fact the code even uses the multiplier and then completely negates it by normalizing the vector that used it immediately.

     LVecBase3f feeler = _feeler * _ai_char->get_char_render().get_relative_vector(_ai_char->get_node_path(), LVector3f::forward());
     feeler.normalize();

The feeler multiplier is “_feeler” by the way. Just one more thing for me to fix.

Also because the code doesn’t actually utilize the speed of the ai as stated in the description, a stationary ai may still try to avoid an obstacle. That could account for some strange things.

If anyone is interested in using it right away here is what I think the function should look like. I’ve fixed it to correctly use the feeler length multiplier and take into account the ai’s speed. I’ve also replaced some very strange vector math with something more straight forward and altered names to be more descriptive and accurate.

bool ObstacleAvoidance::obstacle_detection() {
  // Calculate the volume of the AICharacter with respect to render
  PT(BoundingVolume) np_bounds = _ai_char->get_node_path().get_bounds();
  CPT(BoundingSphere) ai_bounding_sphere = np_bounds->as_bounding_sphere();
  LVecBase3f avoidance(0.0, 0.0, 0.0);
  double distance = 0x7fff;
  double collision_radius; //the distance from obstacle to ai at which their boundries collide
  LVecBase3f to_obstacle;
  LVecBase3f prev_avoidance;
  for(unsigned int i = 0; i < _ai_char->_world->_obstacles.size(); ++i) {
    PT(BoundingVolume) bounds = _ai_char->_world->_obstacles[i].get_bounds();
    CPT(BoundingSphere) bsphere = bounds->as_bounding_sphere();
    LVecBase3f near_obstacle = _ai_char->_world->_obstacles[i].get_pos() - _ai_char->get_node_path().get_pos();
    // Check if it's the nearest obstacle, If so initialize as the nearest obstacle
    if((near_obstacle.length() < distance) && (_ai_char->_world->_obstacles[i].get_pos() != _ai_char->get_node_path().get_pos())) {
      _nearest_obstacle = _ai_char->_world->_obstacles[i];
      distance = near_obstacle.length();
      collision_radius = bsphere->get_radius() + ai_bounding_sphere->get_radius();
    }
  }
     LVecBase3f forward = _ai_char->get_char_render().get_relative_vector(_ai_char->get_node_path(), LVector3f::forward());
     forward.normalize();
     LVecBase3f feeler = collision_radius + _ai_char->get_velocity().length() * _feeler;
     to_obstacle = _nearest_obstacle.get_pos() - _ai_char->get_node_path().get_pos();
     LVecBase3f project = forward * to_obstacle.length();
     LVecBase3f perpendicular = project - to_obstacle;
     // If the nearest obstacle will collide with our AICharacter then send obstacle detection as true
     if((_nearest_obstacle) && (perpendicular.length() < collision_radius) && (project.length() < feeler.length())) {
       return true;
     }
     return false;
}

With any luck I’ll eventually get my comprehensive overhaul of the ai working and out into the cvs so you may want to wait it out.

Hmm, changing the max speed should not be a problem, as I can save the former position, get the new one, calculate the moving angle an reposition the model myself, with the speed I want.

Will try this, thanks a lot.
I want to try to model a pathfinding mesh too, as my scenário is pretty much a plane, I thing it should be easy.

Again, thank you.

Hmm, tried replacing the obstacle avoidance example with my models, reducing the pursue priority and increasing the max force and it seems to work.
Still in my application it does not.
I am try/excepting the first call of update, that have an assertion error:
Assertion failed: !is_empty() at line 1253 of panda/src/pgraph/nodePath.cxx

This might be causing it, still I could not find the root cause of the problem.
Well, thanks again, I will keep looking.