• Advertisement
Sign in to follow this  

Bullet How to avoid physics and AI to dominate over each other?

Recommended Posts

If I set the physics after AI, physics will dominate the final world transformations, which totally erased the world transformations calculated from the AI, or vice versa.
I want to calculate the relative transformation when physics is in effect, how do I do that with btMotionState or derived classes with getWorldTransform overrided?
Thanks
Jack

Share this post


Link to post
Share on other sites
Advertisement

Why is the AI concerned with world transformations? I would think the AI would determine things like picking actions, or what animation to play next...

 

Share this post


Link to post
Share on other sites

If you override transforms after physics you run into the problem that you might break the physics constraints or move into penetration. You can take control over a physics object using ghost objects. A ghost object takes part in the broadphase, but not in the physics simulation. You are responsible for not moving the ghost objects into penetration (e.g. using raycast or volume sweeps).

 

Edited by Dirk Gregorius

Share this post


Link to post
Share on other sites

I assume you are directly modifying position of objects?

If that is the case, you should make the AI set applied force or velocity, for example. Not the position itself. It's a common mistake with many physics engine with broad-phase (engines that will solve the position and angle of objects, the reactions).

Share this post


Link to post
Share on other sites

I don't know how to figure out the forces applied to the object affected....


void AgentMotionState::getWorldTransform(btTransform& worldTrans) const
{

btTransform t;
btDefaultMotionState::getWorldTransform(t);

D3DXMATRIX l_mat;
D3DXMatrixIdentity(&l_mat);
if (m_object)
{
// DX TRANSFORM
l_mat = BT2DX_MATRIX(t);

D3DXVECTOR3 scale, pos;
D3DXQUATERNION quat;

D3DXMatrixDecompose(&scale, &quat, &pos, &l_mat);

Transform* l_transform = m_object->FindComponentByType();

// if setting positions here, the physics will dominate over AI
l_transform->setPosition2(pos);
l_transform->setRotation(quat);

}

// BULLET TRANSFORM
worldTrans = t;

}
Edited by lucky6969b

Share this post


Link to post
Share on other sites

You need to figure out which part of the object is responsible for what actions.  Right now you have physics and AI both trying to move an object around, and that just wont work... clearly.    So, let the physics be responsible for the object's transform and let the AI determine what it wants to do, and then tell the physics system what it wants through setting forces or impulses.

 

9 minutes ago, lucky6969b said:

I don't know how to figure out the forces applied to the object affected....
 

Start with F = ma and work from there.  You should know what mass your objects have, and what speed you want, so just accelerate them until they reach that speed and then set accel back to 0.  Or, if your objects dont need to accelerate, just give them impulses.

Share this post


Link to post
Share on other sites

Okay, When updating AI after the physics, somehow the AI can "overpower" what the physics is trying to do...
and the AI is futile, the physics will take over.
Thanks
Jack

Share this post


Link to post
Share on other sites

But if I let the physics run in its own thread, this has some advantages when I don't update the physics too much by leveraging it some time to sleep,the AI actually has time to counteract with what the physics is currently doing. Because physics is continuous, if I divide the 1.0 with the frame rate, I can get some continuous feel without the physics hogging the world transformations all the time, when the physics thread is sleeping, the AI can do its work now...
Thanks
Jack

Share this post


Link to post
Share on other sites
3 hours ago, lucky6969b said:

But if I let the physics run in its own thread, this has some advantages when I don't update the physics too much by leveraging it some time to sleep,the AI actually has time to counteract with what the physics is currently doing. Because physics is continuous, if I divide the 1.0 with the frame rate, I can get some continuous feel without the physics hogging the world transformations all the time, when the physics thread is sleeping, the AI can do its work now...
Thanks
Jack

I'm not sure I understand, do you still have both the AI and physics setting your object's transforms?

Share this post


Link to post
Share on other sites
1 hour ago, lucky6969b said:

I meant when the physics thread goes to sleep, the AI can take control of the world transformation.

The AI should not be controlling the world transforms of your objects.   Not sure what you mean by "when the physics thread goes to sleep".  Regardless of whether you're multithreading your physics, or how you have Bullet set up to update, it should always be in control of the transform and the AI should be interfacing with it through setting forces, impulses, or maybe velocities.  But the AI should not directly change the positions of objects.  You're asking for trouble by doing this.

Share this post


Link to post
Share on other sites
9 hours ago, lucky6969b said:

How can I calculate the forces from the resultant world transformation calculated by bullet?
Thanks
Jack

I'm confused by your question.  It sounds like you're trying to work backwards.

You first decide what you want your object to do, then you apply forces/impulses/velocities to get it to start moving in that direction, and finally Bullet will update the transform every frame according to those forces/impulses/velocities.

But, if you're trying to set the transform yourself through the AI code, you're actively fighting against the physics engine and that will not work.

Share this post


Link to post
Share on other sites

Can you describe at a much higher level what you're trying to achieve? Is the AI trying to predict where objects will be at some point in the future?

Share this post


Link to post
Share on other sites

I think people are misunderstanding the question here. AI absolutely does control an object's position in many games, at least indirectly. If I'm walking from place to place then the AI may find a path and attempt to follow it, moving me along that path. And obviously at the same time, other things in the world might apply physical forces in other ways.

The simplest thing that could possibly work here is to implement the AI path-following behaviour as a force applied to the character, so that feeds in to the physics, and the physics applies the actual motion and therefore sets the position. A very simple path follower can just apply a constant force (or assign a desired velocity, whatever your physics supports) in the direction of the next node on the path, and assuming that is sufficient force to move the character, and no other forces deflect or block the character, they can follow the path that way. You will need logic in the path-following system to recognise when it is failing to make progress (e.g. imagine walking onto a treadmill that pushes you back faster than you can move forwards) and signal further up the AI hierarchy to request a change in plan.

Another method is to have and explicit hand-off, where either one system or the other is in charge. e.g. In Unity there is a 'isKinematic' flag on a RigidBody, and if it's set, you can move the character around via navigation but collisions will not (generally) affect the character. Or you can clear the flag and collisions and other forces will work - but navigation may not. Other engines might have a 'ragdoll' mode or similar. Knowing when to switch the flag is tricky, however.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this  

  • Advertisement
  • Advertisement
  • Popular Tags

  • Advertisement
  • Popular Now

  • Similar Content

    • By cesarpachon
      hello, I am trying to implement a realistic simulation of a roulette wheel. it is not clear for me what is the proper way to simulate the initial status of the ball, when it spins against the edge of the wheel until it loss energy and start falling towards the centre.
      I modelled the conic table as a height map, as I assume that would provide the smoother surface. but I see anyway there is rough squared corners everywhere, so really I don't have a smooth inner wall to slide against.
      I wonder if I should ignore the wall and simulate the sliding by code. i.e: apply force (or impulse?) each frame to keep the ball at a fixed radius and somehow force it to follow a desired angular speed..  later, when I want to execute the falling behaviour, just stop applying that forces and let the simulator and the gravity do their work.. makes sense?
      any advice is highly appreciated!
       
    • By kevinyu
      Original Post: Limitless Curiosity
      Out of various phases of the physics engine. Constraint Resolution was the hardest for me to understand personally. I need to read a lot of different papers and articles to fully understand how constraint resolution works. So I decided to write this article to help me understand it more easily in the future if, for example, I forget how this works.
      This article will tackle this problem by giving an example then make a general formula out of it. So let us delve into a pretty common scenario when two of our rigid bodies collide and penetrate each other as depicted below.

      From the scenario above we can formulate:

      We don't want our rigid bodies to intersect each other, thus we construct a constraint where the penetration depth must be more than zero.
      \(C: d>=0\)
      This is an inequality constraint, we can transform it to a more simple equality constraint by only solving it if two bodies are penetrating each other. If two rigid bodies don't collide with each other, we don't need any constraint resolution. So:
      if d>=0, do nothing else if d < 0 solve C: d = 0
      Now we can solve this equation by calculating  \( \Delta \vec{p1},\Delta \vec{p2},\Delta \vec{r1}\),and  \( \Delta \vec{r2}\)  that cause the constraint above satisfied. This method is called the position-based method. This will satisfy the above constraint immediately in the current frame and might cause a jittery effect.
      A much more modern and preferable method that is used in Box2d, Chipmunk, Bullet and my physics engine is called the impulse-based method. In this method, we derive a velocity constraint equation from the position constraint equation above.

      We are working in 2D so angular velocity and the cross result of two vectors are scalars.
      Next, we need to find \(\Delta V\) or impulse to satisfy the velocity constraint. This \(\Delta V\) is caused by a force. We call this force 'constraint force'. Constraint force only exerts a force on the direction of illegal movement in our case the penetration normal. We don't want this force to do any work, contribute or restrict any motion of legal direction.

      \(\lambda\) is a scalar, called Lagrangian multiplier. To understand why constraint force working on \(J^{T}\) direction (remember J is a 12 by 1 matrix, so \(J^{T}\) is a 1 by 12 matrix or a 12-dimensional vector), try to remember the equation for a three-dimensional plane.

      Now we can draw similarity between equation(1) and equation(2), where \(\vec{n}^{T}\) is similar to J and \(\vec{v}\) is similar to V. So we can interpret equation(1) as a 12 dimensional plane, we can conclude that \(J^{T}\) as the normal of this plane. If a point is outside a plane, the shortest distance from this point to the surface is the normal direction.

      After we calculate the Lagrangian multiplier, we have a way to get back the impulse from equation(3). Then, we can apply this impulse to each rigid body.
      Baumgarte Stabilization
      Note that solving the velocity constraint doesn't mean that we satisfy the position constraint. When we solve the velocity constraint, there is already a violation in the position constraint. We call this violation position drift. What we achieve is stopping the two bodies from penetrating deeper (The penetration depth will stop growing). It might be fine for a slow-moving object as the position drift is not noticeable, but it will be a problem as the object moving faster. The animation below demonstrates what happens when we solve the velocity constraint.
      [caption id="attachment_38" align="alignnone" width="800"]
      So instead of purely solving the velocity constraint, we add a bias term to fix any violation that happens in position constraint. 

      So what is the value of the bias? As mentioned before we need this bias to fix positional drift. So we want this bias to be in proportion to penetration depth.

      This method is called Baumgarte Stabilization and \(\beta\) is a baumgarte term. The right value for this term might differ for different scenarios. We need to tweak this value between 0 and 1 to find the right value that makes our simulation stable.

       
      Sequential Impulse
      If our world consists only of two rigid bodies and one contact constraint. Then the above method will work decently. But in most games, there are more than two rigid bodies. One body can collide and penetrate with two or more bodies. We need to satisfy all the contact constraint simultaneously. For a real-time application, solving all these constraints simultaneously is not feasible. Erin Catto proposes a practical solution, called sequential impulse. The idea here is similar to Project Gauss-Seidel. We calculate \(\lambda\) and \(\Delta V\) for each constraint one by one, from constraint one to constraint n(n = number of constraint). After we finish iterating through the constraints and calculate \(\Delta V\), we repeat the process from constraint one to constraint n until the specified number of iteration. This algorithm will converge to the actual solution.The more we repeat the process, the more accurate the result will be. In Box2d, Erin Catto set ten as the default for the number of iteration.
      Another thing to notice is that while we satisfy one constraint we might unintentionally satisfy another constraint. Say for example that we have two different contact constraint on the same rigid body.

      When we solve \(\dot{C1}\), we might incidentally make \(\dot{d2} >= 0\). Remember that equation(5), is a formula for \(\dot{C}: \dot{d} = 0\) not \(\dot{C}: \dot{d} >= 0\). So we don't need to apply it to \(\dot{C2}\) anymore. We can detect this by looking at the sign of \(\lambda\). If the sign of \(\lambda\) is negative, that means the constraint is already satisfied. If we use this negative lambda as an impulse, it means we pull it closer instead of pushing it apart. It is fine for individual \(\lambda\) to be negative. But, we need to make sure the accumulation of \(\lambda\) is not negative. In each iteration, we add the current lambda to normalImpulseSum. Then we clamp the normalImpulseSum between 0 and positive infinity. The actual Lagrangian multiplier that we will use to calculate the impulse is the difference between the new normalImpulseSum and the previous normalImpulseSum
      Restitution
      Okay, now we have successfully resolve contact penetration in our physics engine. But what about simulating objects that bounce when a collision happens. The property to bounce when a collision happens is called restitution. The coefficient of restitution denoted \(C_{r}\), is the ratio of the parting speed after the collision and the closing speed before the collision.

      The coefficient of restitution only affects the velocity along the normal direction. So we need to do the dot operation with the normal vector.

      Notice that in this specific case the \(V_{initial}\) is similar to JV. If we look back at our constraint above, we set \(\dot{d}\) to zero because we assume that the object does not bounce back(\(C_{r}=0\)).So, if \(C_{r} != 0\), instead of 0, we can modify our constraint so the desired velocity is \(V_{final}\).

      We can merge our old bias term with the restitution term to get a new bias value.

      // init constraint // Calculate J(M^-1)(J^T). This term is constant so we can calculate this first for (int i = 0; i < constraint->numContactPoint; i++) { ftContactPointConstraint *pointConstraint = &constraint->pointConstraint; pointConstraint->r1 = manifold->contactPoints.r1 - (bodyA->transform.center + bodyA->centerOfMass); pointConstraint->r2 = manifold->contactPoints.r2 - (bodyB->transform.center + bodyB->centerOfMass); real kNormal = bodyA->inverseMass + bodyB->inverseMass; // Calculate r X normal real rnA = pointConstraint->r1.cross(constraint->normal); real rnB = pointConstraint->r2.cross(constraint->normal); // Calculate J(M^-1)(J^T). kNormal += (bodyA->inverseMoment * rnA * rnA + bodyB->inverseMoment * rnB * rnB); // Save inverse of J(M^-1)(J^T). pointConstraint->normalMass = 1 / kNormal; pointConstraint->positionBias = m_option.baumgarteCoef * manifold->penetrationDepth; ftVector2 vA = bodyA->velocity; ftVector2 vB = bodyB->velocity; real wA = bodyA->angularVelocity; real wB = bodyB->angularVelocity; ftVector2 dv = (vB + pointConstraint->r2.invCross(wB) - vA - pointConstraint->r1.invCross(wA)); //Calculate JV real jnV = dv.dot(constraint->normal pointConstraint->restitutionBias = -restitution * (jnV + m_option.restitutionSlop); } // solve constraint while (numIteration > 0) { for (int i = 0; i < m_constraintGroup.nConstraint; ++i) { ftContactConstraint *constraint = &(m_constraintGroup.constraints); int32 bodyIDA = constraint->bodyIDA; int32 bodyIDB = constraint->bodyIDB; ftVector2 normal = constraint->normal; ftVector2 tangent = normal.tangent(); for (int j = 0; j < constraint->numContactPoint; ++j) { ftContactPointConstraint *pointConstraint = &(constraint->pointConstraint[j]); ftVector2 vA = m_constraintGroup.velocities[bodyIDA]; ftVector2 vB = m_constraintGroup.velocities[bodyIDB]; real wA = m_constraintGroup.angularVelocities[bodyIDA]; real wB = m_constraintGroup.angularVelocities[bodyIDB]; //Calculate JV. (jnV = JV, dv = derivative of d, JV = derivative(d) dot normal)) ftVector2 dv = (vB + pointConstraint->r2.invCross(wB) - vA - pointConstraint->r1.invCross(wA)); real jnV = dv.dot(normal); //Calculate Lambda ( lambda real nLambda = (-jnV + pointConstraint->positionBias / dt + pointConstraint->restitutionBias) * pointConstraint->normalMass; // Add lambda to normalImpulse and clamp real oldAccumI = pointConstraint->nIAcc; pointConstraint->nIAcc += nLambda; if (pointConstraint->nIAcc < 0) { pointConstraint->nIAcc = 0; } // Find real lambda real I = pointConstraint->nIAcc - oldAccumI; // Calculate linear impulse ftVector2 nLinearI = normal * I; // Calculate angular impulse real rnA = pointConstraint->r1.cross(normal); real rnB = pointConstraint->r2.cross(normal); real nAngularIA = rnA * I; real nAngularIB = rnB * I; // Apply linear impulse m_constraintGroup.velocities[bodyIDA] -= constraint->invMassA * nLinearI; m_constraintGroup.velocities[bodyIDB] += constraint->invMassB * nLinearI; // Apply angular impulse m_constraintGroup.angularVelocities[bodyIDA] -= constraint->invMomentA * nAngularIA; m_constraintGroup.angularVelocities[bodyIDB] += constraint->invMomentB * nAngularIB; } } --numIteration; }  
      General Step to Solve Constraint
      In this article, we have learned how to solve contact penetration by defining it as a constraint and solve it. But this framework is not only used to solve contact penetration. We can do many more cool things with constraints like for example implementing hinge joint, pulley, spring, etc.
      So this is the step-by-step of constraint resolution:
      Define the constraint in the form \(\dot{C}: JV + b = 0\). V is always \(\begin{bmatrix} \vec{v1} \\ w1 \\ \vec{v2} \\ w2\end{bmatrix}\) for every constraint. So we need to find J or the Jacobian Matrix for that specific constraint. Decide the number of iteration for the sequential impulse. Next find the Lagrangian multiplier by inserting velocity, mass, and the Jacobian Matrix into this equation: Do step 3 for each constraint, and repeat the process as much as the number of iteration. Clamp the Lagrangian multiplier if needed. This marks the end of this article. Feel free to ask if something is still unclear. And please inform me if there are inaccuracies in my article. Thank you for reading.
      NB: Box2d use sequential impulse, but does not use baumgarte stabilization anymore. It uses full NGS to resolve the position drift. Chipmunk still use baumgarte stabilization.
      References
      Allen Chou's post on Constraint Resolution A Unified Framework for Rigid Body Dynamics An Introduction to Physically Based Modeling: Constrained Dynamics Erin Catto's Box2d and presentation on constraint resolution Falton Debug Visualizer 18_01_2018 22_40_12.mp4
      equation.svg

    • By lucky6969b
      In the Draw call, I just render all buffered vertices collected from the dynamics world, at the entry point of Draw, it always reporting empty buffers. I have setup the DXDebugDrawer correctly by deriving from the btIDebugDraw interface and I've made a call to the setDebugDrawer, how come it didn't work?


      if (m_dynamicsWorld) { m_dynamicsWorld->debugDrawWorld(); dynamic_cast(m_dynamicsWorld->getDebugDrawer())->Draw(); }


      thanks
      Jack
    • By lucky6969b
      I call on the shatter function and it now has a series of chunks stored, and I can retrieve those to the main physics system. But do I hide the main object and re-construct the fragment pieces into some other brand new game objects or some sort?
      Thanks
      Jack
    • By -Tau-
      Hello, I'm trying to use needBroadphaseCollision to filter collision between moveable objects and player. It works pretty well but there is one problem.

      The idea is simple: If an object is still or does not move faster that a threshold, ignore collision with players physical body. In this case I use my own code to update player. If the speed of this object breaks that threshold, convert player to rag-doll and let Bullet do the update.
      I'm using my own custom character controller as i couldn't use Bullets. My player is a ragdoll with btRigidBody bodyparts where linear and angular factors are set to 0 and these limbs are updated based on model animation as long as the player has control over their character. As soon as collision with a fast moving object happens, player loses control over their character, linear an angular factors are set to 1 and i let Bullet handle the ragdoll physics.

      It works well for most objects but i have an object that uses btCompoundShape for its body. When this object is still, (it didn't move for a while) it works.
      However when this object starts to move and doesn't break the speed threshold, it gets affected by players physical body (player starts to push this object around).
      I added some debug variables and it seems that even when needBroadphaseCollision returns false, there are still contact points generated between player and this object.
      What am i missing?
  • Advertisement