Help understanding torque/angular velocity

OK, I think I'm supremely confused about torques and angular velocities. :slight_smile: I'm going to rehash my understanding of linear velocities a) to make sure I understand how those work and b) because my perceptions of how linear and angular velocities work seem inconsistent, and I'm sure the error is with me. :slight_smile:



So, as I understand it (and as seems to be the case) when I apply a linear force, that force is treated as if it were scaled to one second, even though I'm applying it multiple times. So, for instance, applying a force along (0, 0, 1) scales that force down to the number of steps of the physics simulation such that if the step size is 100 and I apply that force 100 times, the force only has a magnitude of 1, not 100. (By "apply," I mean via DynamicPhysicsNode.addForce().) Right so far?



If so, I'm having issues when I try using torques. I'm trying to get away from NodeRotateLeft/RightAction and use forces for rotation. More importantly, I'm trying to use world coordinates translated to local for AI steering.



First, how is angular velocity expressed? I was thinking that the vector components represented the amount of spin, in radians, on their respective axis. Is this correct?



So I have an AI steering behavior. It determines whether its "objective," the entity which it cares about, is to its left or right via getSpatial().worldToLocal(), then applies a torque to aim it toward the entity. So to steer right, I'm doing something like:



   private void steerRight() {
      System.out.println("Right: "+node.getTorque(null));
      Vector3f torque = new Vector3f(0, -angularThrust, 0);
      torque.multLocal(getEntity().getMass());
      node.addTorque(torque);
   }



And almost the exact same thing for left, only the angularThrust variable (2PI/3) is positive.

I notice, however, that after the torque first has the value of 2PI/3, it never does again. I have another println in the controller's update() method that shows the relative X coordinate slowly climbing to 0 (it's initially -10), but it drops much slower than I'd have expected. Assuming my understanding of torques/linear velocities is accurate (and I suspect I've just made an ass out of both of us, so sorry :) then my expectation would be that continuously adding this force would continue showing that the torque is 2.whatever, but as stated, it drops to 0 after the first run. I'm also unclear on the difference between what should be returned by getTorque() and getAngularVelocity(), which also makes me suspect that my understanding is incorrect.

Can anyone point me in the right direction? The ODE tutorials I've found thus far seem more concerned with bouncing balls, or if they use torques, they're used for vehicles with wheels and the like. I'm just trying to aim a spaceship. :)

Oh, and I also tried:


   private void steerRight() {
      System.out.println("Right: "+node.getTorque(null));
      Vector3f torque = getEntity().absolutePositionOf(new Vector3f(0, -angularThrust, 0));
      torque.multLocal(getEntity().getMass());
      node.addTorque(torque);
   }



when I read that torques were supposed to be in world coordinates. Entity.absolutePositionOf(Vector3f) is defined as:


   public Vector3f absolutePositionOf(Vector3f position) {
      Vector3f store = new Vector3f();
      return getSpatial().localToWorld(position, store);
   }



but that makes the turn *far* more drastic.

Oh, and yes, I'm aware that I'm creating lots of vectors here, and that optimization is possible, but optimizing first is what got me into this mess, so now I'm trying to understand then optimize.

Thanks a bunch.
  1. forces and tourques are reset to zero after each physics update. The getters only return what was accumulated in the current time step. Also be aware that the forces/touques have to be applied every step (see continuous forces lesson). Also the applied force to get a desired effect depend on the physics step size.
  2. (angular) velocity is what results from the forces and tourques from the last step (plus old velocity).
  3. tourque is given in world coorindates, yes. But that does not mean you have to add the local position, as tourques are not positions. Regarding rotation is enough.

OK, so with regards to your point #3 above:



So if I have a vector, (0, 1, 0) representing a torque necessary to affect a left turn, I need to translate that vector to worl coordinates, but without a positional component. Initially I attempted localToWorld() and normalized that, but I realized that this does take position into account. So, assuming I'm right in my supposition, are there any methods that simply rotate the given vector into world coordinate space in a form that works? I also tried, on a whim:



node.getWorldRotation().mult(torque, torque);



with no luck.

Thanks.

That's works fine in my Test: alter Lesson5 to apply torque instead of linear force and add your line…

OK, guess I'm still confused. Here is my contrived test situation. I'm going to paste lots of code and logs in the hope that I've given all necessary information to spot what I'm doing wrong.



Player's entity is at (10, 0, 0). My AI-controlled ship is at (0, 0, -50. The goal of this simple test case is to have the AI ship aim at its objective–the player. I have a controller, Helm, whose update method looks like:



   public void update(float tpf) {
      System.out.println("Relative position of objective: "+getEntity().relativePositionOf(objective));
      if(objective != null) {
         if(getEntity().isLeftOf(objective)) {
            if(mode == MODE_PURSUE)
               steerRight();
            else if(mode == MODE_EVADE)
               steerLeft();
         } else if(getEntity().isRightOf(objective)) {
            if(mode == MODE_PURSUE)
               steerLeft();
            else if(mode == MODE_EVADE)
               steerRight();
         } else {
            // Maybe do something here? Not sure yet.
         }
      }
   }



Entity.isLeftOf(), for reference, is:


   public boolean isLeftOf(Entity target) {
      return relativePositionOf(target).x > 0;
   }

   public boolean isRightOf(Entity target) {
      return relativePositionOf(target).x < 0;
   }



There's an Entity.relativePositionOf(Entity entity) that calls relativePositionOf(entity.getPosition()), which, when called on Vector3fs, is:


   public Vector3f relativePositionOf(Vector3f position) {
      Vector3f store = new Vector3f();
      return getSpatial().worldToLocal(position, store);
   }



My steering functions read as follows:


   private void steerLeft() {
      if(node.getAngularVelocity(null).y <= angularThrust/100) {
         Vector3f torque = new Vector3f(0, angularThrust, 0);
      node.getWorldRotation().mult(torque, torque);
         torque.multLocal(getEntity().getMass());
         node.addTorque(torque);
         System.out.println("Steering left: "+node.getAngularVelocity(null));
      }
   }

   private void steerRight() {
      if(node.getAngularVelocity(null).y >= -angularThrust/100) {
         Vector3f torque = new Vector3f(0, -angularThrust, 0);
      node.getWorldRotation().mult(torque, torque);
         torque.multLocal(getEntity().getMass());
         node.addTorque(torque);
         System.out.println("Steering right: "+node.getAngularVelocity(null));
      }
   }



I have the velocity limiting checks in place because, without them, the relative X displayed in the update shoots way over or below 0.

Now, for some numbers. I have an Entity.setYaw(float yawInDegrees) method for quickly setting its yaw. We'll start with a yaw of 180, and here's an excerpt of the printlns:


Relative position of objective: com.jme.math.Vector3f [X=-10.0, Y=0.0, Z=-50.0]
Steering left: com.jme.math.Vector3f [X=0.0, Y=0.0, Z=0.0]
Relative position of objective: com.jme.math.Vector3f [X=-10.0, Y=0.0, Z=-50.0]
Steering left: com.jme.math.Vector3f [X=0.0, Y=0.0, Z=0.0]
Relative position of objective: com.jme.math.Vector3f [X=-10.0, Y=0.0, Z=-50.0]
Relative position of objective: com.jme.math.Vector3f [X=-9.93716, Y=0.0, Z=-50.012524]
Relative position of objective: com.jme.math.Vector3f [X=-9.853348, Y=0.0, Z=-50.0291]
...
Relative position of objective: com.jme.math.Vector3f [X=-0.005279541, Y=0.0, Z=-50.99019]
Relative position of objective: com.jme.math.Vector3f [X=0.037438393, Y=0.0, Z=-50.990177]
Steering right: com.jme.math.Vector3f [X=0.0, Y=0.0418879, Z=0.0]
Relative position of objective: com.jme.math.Vector3f [X=0.058796883, Y=0.0, Z=-50.990154]
Steering right: com.jme.math.Vector3f [X=0.0, Y=0.020943949, Z=0.0]
Relative position of objective: com.jme.math.Vector3f [X=0.08015728, Y=0.0, Z=-50.990143]
Steering right: com.jme.math.Vector3f [X=0.0, Y=-3.7252903E-9, Z=0.0]
Relative position of objective: com.jme.math.Vector3f [X=0.08015728, Y=0.0, Z=-50.990143]
Relative position of objective: com.jme.math.Vector3f [X=0.06947708, Y=0.0, Z=-50.990143]
Relative position of objective: com.jme.math.Vector3f [X=0.04811859, Y=0.0, Z=-50.990166]
Relative position of objective: com.jme.math.Vector3f [X=0.026760101, Y=0.0, Z=-50.99019]
Relative position of objective: com.jme.math.Vector3f [X=0.016080856, Y=0.0, Z=-50.990192]
Relative position of objective: com.jme.math.Vector3f [X=-0.0052785873, Y=0.0, Z=-50.99018]
Steering left: com.jme.math.Vector3f [X=0.0, Y=-0.020943953, Z=0.0]



So that seems to work. But, not so fast. Let's strike the setYaw(180) on the AI alien:


Relative position of objective: com.jme.math.Vector3f [X=10.0, Y=0.0, Z=50.0]
Steering right: com.jme.math.Vector3f [X=0.0, Y=0.0, Z=0.0]
Relative position of objective: com.jme.math.Vector3f [X=10.0, Y=0.0, Z=50.0]
Steering right: com.jme.math.Vector3f [X=0.0, Y=0.0, Z=0.0]
Relative position of objective: com.jme.math.Vector3f [X=10.0, Y=0.0, Z=50.0]
Relative position of objective: com.jme.math.Vector3f [X=10.062823, Y=0.0, Z=49.987392]



So it seems to get the right relative position, seems to know in which direction it needs to steer, but now the relative X is increasing, which seems to indicate steering away.

Oh, and I seem to have torques working just fine for player rotation, which is why I'm doubly baffled, because I've done something similar here and it isn't working. It's also possible that I've been staring at this code for so long that there's something incredibly obvious, like a misplaced or reversed sign, that I'm missing. :)

Those isLeftOf/isRightOf methods look weird to me. Why does the relative position tell you if it still has to turn?

I dunno about the physics, and my code probably isn't optimal, but when I worked out steering for a target I found the up and sideways vectors for my plane, then dotted with the normalized relative vector to the target to get yaw/pitch steering. Then I think you want to apply a torque along the OTHER axis to do the turning (i.e. work out yaw steering by dotting with sideways axis, then apply torque as a multiple of the up axis). But then it looks like the world to local thing might have been a better way to do it :wink:

irrisor said:

Those isLeftOf/isRightOf methods look weird to me. Why does the relative position tell you if it still has to turn?


Because the relative X should, if my thinking is correct, drop to 0 as the rotations match. Here's a passage from AI for Game Developers that might explain it better:


void DoLineOfSightChase (void)
{
Vector u, v;
bool left = false;
bool right = false;
u = VRotate2D(

You might also want to ramp the torque down as you approach the correct heading, or you'll have a really twitchy steering and possibly oscillation depending on damping and the torque you use. Actually I think you might want even more than that, since you may want to reduce the torque dependent on both approach to correct heading and angular velocity, so that you remove the torque and then apply a reverse torque to come to rest in the right direction, instead of accelerating right up until you fly past the right heading and set of back again. PID (or just PD) would do that I think :slight_smile:

If you are still stuck: control a ship manually first to sort out AI (isLeftOf etc.) problems.