Inverse Kinematics


#1

There seems to be some general interest in the addition of an IK solver to Urho3D (such as this feature request and of course the need for IK in my own project). I think it would be very cool for Urho3D to have this capability, so I have taken it upon myself to implement this feature request.

I’d like to clarify a few things before I make any major changes to Urho3D and would be happy if one of the core developers (I’m looking at you cadavar :smiley:) could give me the “good to go” as well as some advice on some of these points.

Concept

My current concept for approaching this are 3 new components:

IKSolver is attached to the root scene node and - as the name implies - is responsible for solving all IK constraints. I plan on implementing the Jacobian Inverse method and the Jacobian Moore?Penrose method for starters, but will probably also look at implementing a more robust algorithm in the future.

IKEffector can be attached to any node in the scene and marks that node as the beginning of an IK chain. The effector has the methods SetTarget() for controlling the target location of the node and SetChainLength() to control how many parent nodes are affected.

IKConstraint is attached to nodes part of the IK chain and is used to “fine tune” the behaviour of each node (things such as the “stiffness” and scale factor).

Questions

1) When to apply IK
The IK solver is capable of solving an entire scene graph in one pass, so it would make sense to have it kick in only once when needed – whenever a node part of any IK chain becomes dirty. As I see it, this should happen after animation states have been applied, meaning in E_SCENEDRAWABLEUPDATEFINISHED. The problem is I can’t just hijack that event for IK because what if the user wants to add their own bone modifications in addition to IK?

I could insert a new event called something like E_INVERSEKINEMATICUPDATE which is sent just before E_SCENEDRAWABLEUPDATEFINISHED. This begs the question: Do we even need an IK update event? Is this the way I should go, or is there a better way I can insert myself into the engine?

2) Build system
Should I add a CMake option URHO3D_IK to include/exclude the IK solver, or should it be something that is always available?

That’s all of the questions I have for now. I’ll post updates in this thread as this project evolves. You can test my progress by checking out the branch InverseKinematics from here: https://github.com/thecomet93/urho3d/tree/InverseKinematics (currently there’s not much IK functionality).


Next release?
#2

topic1040.html
topic1273.html


#3

I’ve hardly had the time to work on this (uni, exams), but I haven’t given up on this yet!

Below you can see screenshots of before and after the first iteration of FABRIK, the inverse kinematic algorithm I chose to implement. In this case, the arm consists of 3 rigid joints. The end effector is trying to reach a target located on the right (out of bounds).

The neat thing about FABRIK as opposed to, say, jacobian based methods, is it requires vastly less iterations to reach a target, a single iteration requires less computing time, and it always converges (i.e. no singularities).


#4

I also think that FABRIK is the way to go, as most of the things I’ve experimented with gave very unrealistic behavior.
Also one advantage of FABRIK is that it splits the tasks instead of handling everything blindly.
Keep it up :stuck_out_tongue:


#5


#6

Today I implemented initial pose restoring, which allowed me to finally run the algorithm in real-time. Here’s a video.

[video]https://www.youtube.com/watch?v=POamRQtPI6c[/video]

I think you can see for yourself why FABRIK is superior. Just 10 iterations and the result is almost perfect. Jacobian based methods will often times require up to 1000 iterations to achieve the same tolerance.

Current issues:

  • Removing IKEffector from a node doesn’t update initial pose data
  • Negative maxIteration

Todo:

  • Chain objects need to share the same Vector3 objects for base/effector positions to make solving for multiple chains easier.
  • Apply angles from chain objects back to scene node (currently only translations are applied)
  • Add support for enabling/disabling pose restoring -> will allow support for animations as well as just scene node IK
  • Add support for manually updating initial pose.
  • Apply bullet constraints
  • Optimise

#7

You are making exciting progress, great work !


#8

Well done :wink:


#9

Really cool! :smiley:


#10

The solver now supports multiple end effectors, as long as they share the same base node. Next up: Multiple end effectors with multiple intermediate base nodes. Have a video:

[video]https://www.youtube.com/watch?v=P8COz7dZO9Y[/video]

Current TODO list:

[code] * - IKEffector doesn’t save target node name when saving scene in editor

    • Initial pose is not saved when saving scene in editor. Instead, the
  • solved state of the chain(s) are saved.
    • Target angle in addition to target position -> use weighted angles
  • approach
    • Add an IKEffector weight parameter, so the user can specify
  • (between [0…1]) how much influence the solved chains have.
    • Apply angles from chain objects back to scene nodes (currently only
  • translations are applied).
    • Add support for enabling/disabling initial pose to support animated
  • models as well as scene nodes.
    • Add support for manually updating initial pose.
    • Apply bullet constraints to joints.
    • Optimise.[/code]

#11

Looks like excellent work.


#12

So excited to use this! :smiley:

Is it possible to write a grounder with those effectors? e.g: youtube.com/watch?v=9MiZiaJorws

Also, how are rotation limits setup?


#13

Thanks guys!

[quote=“namic”]Is it possible to write a grounder with those effectors? e.g: youtube.com/watch?v=9MiZiaJorws

Also, how are rotation limits setup?[/quote]

Yes, grounders are the very reason why I started working on this in the first place. My dog model has 3 bones in each leg, so it was no longer a trivial trigonometric problem.

I’m thinking there are two possibilities to implement feet lifting off.

  1. Be able to specify a weight from [0…1] on each effector to control how much influence the solved chain will have. This will allow you to specify 0 when the foot should lift off and 1 when the foot touches (and you’ll also be able to blend between, which I think is an important feature).
  2. It might be easier to just position the IK targets at the same position as the feet when they need to lift off of the ground. That way you won’t need a weight parameter any more and the slight performance impact can be avoided.

Rotation limits are currently on the to-do list, along with pole targets. I will most likely try re-use the bullet constraint component for this purpose.


#14

I’m dying to see the integration of this with ragdolls and animation! :smiley: :smiley: :smiley: :smiley: :smiley:


#15

I like what you’re doing with this. Keep it up!


#16

The solver now supports arbitrary trees, so if you should feel so inclined, you can solve for something like this (although I wouldn’t know why you’d want to):

The point is the solver can handle anything you can throw at it.

Here is an in-editor screen shot of a two-effector one-subbase configuration:

There is currently an issue with applying the solved angles back to the scene nodes correctly. I am working on fixing this.


#17

For On A Rainy Day-likes. :slight_smile:


#18

That is really cool feature for procedural creatures like in spore.


#19

Hi,
This is the example to update the angle for forward kinematic. You would similarly applied it for every kinematic link on a chain.

Vector3 shoulderJoint = node_lArm->GetWorldPosition();
Vector3 elbow = node_elbow->GetWorldPosition();
Vector3 v1 = elbow - shoulderJoint;
Vector3 v2 = target - shoulderJoint;
v1.Normalize();
v2.Normalize();
Quaternion q;
q.FromRotationTo(v1, v2);

	Quaternion q1 = node_lArm->GetWorldRotation();
	Quaternion q2 = Quaternion( node_lArm->GetParent()->GetWorldRotation().Inverse());

node_lArm->SetRotation(q2qq1);


You could also reset the rotation before those calculation to avoid drifting of the twist angles on the links using node_lArm->SetRotation(Quaternion());


#20

That looks great! Any updates?