in C#, Tutorial, Unity

Inverse Kinematics for Tentacles

This post continues our journey in the depth of Inverse Kinematics. In this tutorial you will learn how to apply this powerful technique to create realistic tentacles.

 

The other post in this series can be found here:

At the end of this post you can find a link to download all the assets and scenes necessary to replicate this tutorial.

Introduction

The previous part of this tutorial, Inverse Kinematics for Robotic Arms, discussed how to use gradient descent to perform inverse kinematics on a robotic arm. The movement performed by machines is rather simple, as they lack the complexity of real human limbs. In a robotic arm, each joint is controller by a motor. In a limb, each muscle is, de-facto, an independent motor that can expand and contract on its own.

Several creatures possess limbs are able of several degrees of freedom. Elephants’ trunks, and octopuses’ tentacles are such an example. Modelling those limbs is particularly challenging, as the traditional techniques introduces so far fail to produce realistic results.

We will start from the example of the previous post, changing as we go until we obtain a behaviour that is realistic enough for our application.

Tentacle Rigging

When we built a robotic arm, each part was moving independently. Conversely, tentacles can bend. This is an essential feature that we cannot ignore if we are aiming for realism. Our tentacle will need to bend.

The Unity component which allows for this feature is called Skinned Mesh Renderer:

Unfortunately, Unity does not provide a way to create skinned mesh renderer from the editor. A 3D modelling software, such as Blender, is needed. The image below shows the model of the tentacle that will be used for the rest of this tutorial. Inside, it is visible a series of bones, one after the other. They are the objects that allows to bend the model.

Adding bones to a model, also known as rigging, goes beyond the scope of this tutorial. Blender 3D: Nood to Pro/Bones provides a good introduction to this subject.

Bones and Joints

The next step to allow inverse kinematics on a tentacle, is to attach a RobotJoint script to each bone. By doing so, we are giving to our inverse kinematics solver the ability to bend the tentacle.

In a normal octopus, each “joint” is able to rotate freely along all its three axes. The code designed in Inverse Kinematics for Robotic Arms, unfortunately, only allows joints to move on a single axis. Changing this would mean introducing a new level of complexity to our existing code. Instead, we can cycle the axis on the joints, so that joint 0 rotates on X, joint 1 rotate on Y, joint 2 rotates on Z, and so on. This can lead to unnatural behaviours, but you might never experience any issue if the bones are small enough.

In the downloadable Unity project that comes with this tutorial, the script SetRobotJointWeights automatically initialises the parameters for all the joints in the tentacle. Alternatively, you can do this by hand for a finer control on the way each bone can be moved.

Comfort Function

The following animation shows two tentacles. The one on the left is reaching for the red sphere using the algorithm presented in Inverse Kinematics for Robotic Arms. The one of the right, instead, adds a completely new twist of realism by spiralling in a more organic fashion. This example should be enough to understand why tentacles do need their own tutorial.

Both tentacles relies on gradient descent. The difference is in the error function they are trying to minimise. The robotic tentacle on the left only wants to reach the ball; it does not care about any other parameter. Once the end effector is touching the ball, convergence is reached and the tentacle simply stops moving.

The tentacle on the right, on the other hand, is minimising a different function. The function DistanceFromTarget used for the robotic arm is replaced by a new, more complex ones. We can design this new ErrorFunction to take into account several other parameters that we want to take into consideration. The tentacles shown in this tutorial tries to minimise three different functions:

  • Distance from target: as already defined,
  • End effector rotation: the tip of the tentacle tries to match the rotation of the object we want to reach. This behaviour can be seen in the animation above, where the right tentacle is spiralling around the sphere. Since each joint has a  limited range of movements, this will cause ripples to be transmitted down the kinematic chain of bones.We can force the tentacle to match the rotation of the object it tries to reach. To do so, we can measure the angle between the end effector rotation and the target rotation. Unity comes with a handy function to do so Quaternion.Angle:
    float rotationPenalty =
        Mathf.Abs
        (
             Quaternion.Angle(EndEffector.rotation, Destination.rotation) / 180f
        );

    Matching the local rotation like this might not always be a good idea. Depending on the situation, you might want to align your tentacle in a different way.

  • Torsion: keeping limbs in unnatural positions is uncomfortable. This parameter penalises convoluted solutions, forcing the inverse kinematics to a more linear, simple rotation.To calculate a penalty for the torsion, we first have to define what “torsion” means in our context. The easiest approach is to define it as the average of the angles for all joints. Such penalty wants the tentacle to be relaxed, punishing solutions that requires lot of twists.
    float torsionPenalty = 0;
    for (int i = 0; i < solution.Length; i++)
         torsionPenalty += Mathf.Abs(solution[i]);
    torsionPenalty /= solution.Length;

These three penalties score leads to tentacles that moves in a rather realistic way. A more sophisticate version could make sure that they keep wobbling even when all the other constraints are fully satisfied.

❓ Using different units
Those three parameters are likely to be expressed in different units. The distance from the target could be metres, while the end effector rotations could be degrees. It’s important to re-scale them according to their importance, so that a difference of 10 degrees is not weighted as badly as being 10 metres apart from the target.

Ideally, they should all be normalised between 0 and 1. Then, it is possible to use coefficients to specify their relative importance:

public float ErrorFunction (Vector3 target, float [] angles)
{
    return
        NormalisedDistance(target, angles) * DistanceWeight +
        NormalisedRotation(target, angles) * RotationWeight +
        NormalisedTorsion (target, angles) * TorsionWeight  ;
}

This approach also allows a finer control over the behaviour of the tentacle. They can be changed at any time, depending on the situation, to alter the way they move. For instance, you can increase TorsionWeight to untangle messy tentacles.

 

❓ We don't have an analytical definition!
The traditional problem of forward kinematics can be modelled analytically. This means that we can show an equation that solves it. We have mentioned already the existence of analytical methods to solve the dual problem of inverse kinematics.

What we have now is a function that, potentially, cannot be described analytically. If we had chosen to use a traditional, analytical approach to solve inverse kinematics, we would be unable to add those nuances to our tentacles. Using gradient descent means that we are now able to minimise (almost!) any arbitrary function, whether or not we have its equation.

Improvements

There is virtually no limit to the number of improvements that one can do. Something that will definitely increase the realism of your tentacles is a slowdown function. Tentacles should go slower when they are closer to their destination target.

Also, tentacles should not self intersect. To avoid this, one could use actual colliders on each joint. This, however, can lead to bizarre behaviours. The code ignores collisions, and might still converge to a solution where self collisions occur. A solution is to change the fitness function so that self intersecting solutions are highly penalised.

Other resources

Become a Patron!
Patreon You can download the Unity project for this tutorial on Patreon.

Credits for the 3D model of the robotic arm goes to Petr P.
Credits for the 3D model of the tentacle goes to Daniel Glebinski.
A big thanks also goes to Federico Fasce.

💖 Support this blog

This website exists thanks to the contribution of patrons on Patreon. If you think these posts have either helped or inspired you, please consider supporting this blog.

Patreon Patreon_button
Twitter_logo

YouTube_logo
📧 Stay updated

You will be notified when a new tutorial is released!

📝 Licensing

You are free to use, adapt and build upon this tutorial for your own projects (even commercially) as long as you credit me.

You are not allowed to redistribute the content of this tutorial on other platforms, especially the parts that are only available on Patreon.

If the knowledge you have gained had a significant impact on your project, a mention in the credit would be very appreciated. ❤️🧔🏻

Write a Comment

Comment

40 Comments

  1. Hi Alan. Have you banned Malaysian’s IP from accessing your site? I’ve been trying for 2 months now and I always get ERR_CONNECTION_TIMED_OUT.

    Would be nice to be able to visit your site without going through an EU proxy.

    • Hi Fei!

      I am very sorry to hear that you are experiencing this issue. :\
      It seems my provides is blocking several IPs.
      This is becoming more of an issues for my readers, but I have no direct control over it.
      I might have to switch provider sooner or later. :\

  2. Thanks a lot for all these tutorial about ik and fk in unity.It’s really hard to find tutorials about these topics…

  3. No idea if you still check comments here, but figured I’d try. I implemented this up to the last step, and it’s working ok . However, I can’t get the “leg” to match the rotation of the target. I wrote an AngleFromTarget function that uses the equation above and returns the value. If I call AngleFromTarget instead of DistanceFromTarget (for testing just the rotation part), nothing seems to happen even if I rotate my target quite drastically. I drew some Debug Rays to make sure I was looking at the right directions in the AngleFromTarget, and they were what I expected to see (but boy did that make my fan spin!). Any thoughts on what I might be missing?

    • In true programmer form, as soon as I asked I figured it out. I was looking at the direction currently on the leg, NOT the angles from the calculated solution being tested. Duh.

      Figured I’d post an update in case someone else runs into the same problem.

  4. I am getting a null reference error in Tentacle scene from this line
    Vector3 nextPoint = prevPoint + rotation * Joints[i].StartOffset;
    The tentacle bones seem to be missing RobotJoint scripts, are we supposed to add them manually?

    • Hi Hank!
      It is quite hard to reply not knowing what your current setup is!

      Are you able to get the demo scenes to work?
      If so, everything is set up correctly.

      Yes, the IK script is not enough alone! You need to add a RobotJoint script to each gameobject that you want to be used a joint/pivot for your arm! And then, to link them in the Joints list on the IK script. Each RobotJoint scripts indicates on which the joint can move, and how far.

      For the robot arm, I did that manually. For the tentacle, I created a script that sets them automatically or I would have gone crazy!

  5. Thank you for the amazing tutorial series! When I brought it up with my colleagues over lunch, they immediately knew which blog I was talking about. Keep up the great work!

  6. This is pretty interesting and approachable. I am wondering about the performance of the analysis in a real game. Imagine you model a character using this approach and the character is running, it would seem that it would be difficult to make these calculations fast enough to make the animation look fluid. Thoughts?

  7. Ah loving this so much, I seriously couldn’t have made it this closer to my dream without you. I’ve played pikuniku too, it’s so cute and beautiful! Something about the procedural animation just gives it more personality and I’m loving it 😍. Thanks a tonnnn for this, you are my dream come true

Webmentions

  • Inverse Kinematics in 2D - Part 1 - Alan Zucconi February 25, 2023

    […] definitely one them, and I have dedicated an entire series on how to apply it to robotic arms and tentacles. If you have not read them, do not fear: this new series will be self-contained, as it reviews the […]

  • Tentacle Suckers Shader - Alan Zucconi February 25, 2023

    […] the unexpected success of the tutorial on Inverse Kinematics for Tentacles, I have decided to share the shader that I used to make them so […]