Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3D] Solutions involving local hinges may exhibit discontinuities #2

Open
alansley opened this issue Jan 22, 2016 · 20 comments
Open

[3D] Solutions involving local hinges may exhibit discontinuities #2

alansley opened this issue Jan 22, 2016 · 20 comments
Assignees

Comments

@alansley
Copy link
Collaborator

IK solutions when using local hinges, especially with references constraints, exhibit discontinuities in the solutions. This isn't necessarily to say that the solutions are bad - they aren't - but that they can 'snap' from one side of a constraint to another, which may have knock-on effects on other bones in the same chain, resulting in 'jumps' in the chain's solution.

The issue may also be exacerbated by the fact that bone directions are not kept in quaternions (which would enable smooth 'closest-path-to' interpolation [i.e. lerp] from one vector to another) - they are held as plain start and end points, from which a direction is determined and used for the generation of axes. However, a single directional vector does not contain enough information to allow for the generation of 100% consistent perpendicular axes, as when the direction of the bone is the same as the what is used for the 'up' vector when generating an orthonormal basis, an alternate vector must be used which may also add to the 'jump' in chain configuration.

Resolving this issue with code to avoid snapping through constraints would introduce a form of joint-lock where you can't get there from here, which is undesirable - but storing bone directions either in quaternions or along with their own per-bone Model matrix could mitigate orthonormal axis generation issues which I believe add to the observed discontinuities in the solutions to chains involving local hinges.

I've thought out the basis of a comparative investigation of local-hinge algorithm modifications, and I would love to complete it in 2016 - but it depends on what interest there is in this library, as it forms only the very first research artefact of my PhD through publication, and I have only 17 months left to publish another five papers. For perspective, this library took me 18 months to write.

@alansley alansley self-assigned this Jan 22, 2016
@dai-ichi
Copy link

dai-ichi commented Jun 1, 2017

+1 to fix this issue. I've used the calico library to handle the IK for a robotic arm but found that certain effector targets would cause undesirable "snapping" of the bones that causes huge stresses on the mechanicals. I've so far mitigated the problem somewhat with judicious choices of constraints--but this is non-optimal since doing so effectively removes possible solutions for the IK problems in exchange for reducing the likelihood of snapping through a constraint.

By the way, great job on this library. It is very useful.

@alansley
Copy link
Collaborator Author

alansley commented Jun 1, 2017

I'd love to fix this issue, I truly would - but I have other projects that need completing at the moment, sorry. But that's part of why I open-sourced this, so other people can contribute if they'd like ;-)

In fact, there's a guy working on providing proper (i.e. true to the FABRIK research paper) four-quarter constraints to the library for inclusion in Unreal Engine 4, and the results of that work could flow back into this if it all comes to fruition.

Even without that, it might be possible to avoid the 'pull-through' / "snapping" by not accepting solutions where, say, the angle of the basebone between solutions has moved more than a given amount. However, you would then end up in a joint-lock 'you-can't-get-there-from-here' situation, but it wouldn't stress the arm as much.

I'm glad you like the library - I've never seen it actually move a robotic arm, would love to see any pics/video or such if you'd be willing to share! Cheers!

@dai-ichi
Copy link

dai-ichi commented Jun 5, 2017

Here's a video. Not very good one, but you can kind of see where I'm going with this. The end is a sort of joke: the arm follows a macro to pull its own power and commits suicide. Enjoy.

http://discar.tv/notaslave.html

@alansley
Copy link
Collaborator Author

alansley commented Jun 7, 2017

Haha, that's great! Thanks for sharing! =D

@jmsjr
Copy link
Contributor

jmsjr commented Dec 29, 2017

I've never seen it actually move a robotic arm, would love to see any pics/video or such if you'd be willing to share! Cheers!

Hi all. It's been a while. I got some time during the holidays to control a Braccio robot arm using the Caliko library, although in 2D only at this time.

https://youtu.be/eLnuhM6hFuw

@alansley
Copy link
Collaborator Author

alansley commented Jan 8, 2018

That's very cool - seems to be working like a charm! =D

@jvdnbus
Copy link

jvdnbus commented Jan 23, 2020

I wish I had read this issue before implementing the constraints part of this implementation in another language and bumping my head against the wall, trying to figure out why my local hinges weren't working properly.

@alansley
Copy link
Collaborator Author

alansley commented Jan 24, 2020

jvdnbus, do you mean you re-implemented FABRIK and found yourself with the same discontinuity problem? Or you transcoded Caliko to another language?

Depending on your use-case, you may find that alternate methods (of which there are a few) to generate orthonormal basis axes may help you out. Take a look at the Mat3f class and the "createRotationMatrix" method (and commented out alternatives).

I'd be happy to work with you to try to fix the issue, but it's not something I'm really keen about taking on by myself.

@alansley
Copy link
Collaborator Author

alansley commented Jan 24, 2020

Easiest solution to stop snapping: only allow a maximum of x-degree (like 2 degrees) from where it currently is per frame.

For example, if it WANTS to snap 80 degrees? Only let it have 2 degrees from the current angle - but in the direction of the 80 degree snap.

This could probably be done by simply changing a return statement to:

return clamp(-maxSnapDegs, maxSnapDegs)

Whether that's just for end effector (likely) or for all bones in the chain (less likely) I don't know - it would require some testing to see how it responds.

@jvdnbus
Copy link

jvdnbus commented Jan 24, 2020

I transcoded the base code to Lua. I was having issues with the createRotationMatrix method, indeed. I noticed the snapping behavior sometimes but I'm fairly certain in my use case I can post process the angles like you said. Secondly, I'm not sure if this is related to this issue but when creating consecutive bones with local hinges like pictured here, the algorithm is unable to create a relative rotation vector that is perpendicular to the reference axis.

   x ------------------------>
y
|    b •-----------• j1
|                  |
|                  |
|                  • j2
|                  |
|                  |
|                  • j3
|
v

b  = { 1, -1, 0 }
j1 = { 2, -1, 0 }
j2 = { 2, -2, 0 }
j3 = { 2, -3, 0 }

All joints are hinges and they are all positioned 0 on the z axis and should rotate around the z axis. This makes the 2nd and 3rd bone go straight down the y axis ( dir {0,-1,0} ). I'm unable to get the right parameters for j2 as it states the angle between rot and ref axis is either 0 or 180 degrees.

@alansley
Copy link
Collaborator Author

alansley commented Jan 24, 2020

Did you try any of the alternate methods to generate orthonormal axes?

Worst case - if you're getting bad results from PURE directions then fuzz them by +/- 0.0001 or so.

Give me a setup that doesn't work and I'll take a look at it.

@alansley
Copy link
Collaborator Author

alansley commented Jan 24, 2020

"The algorithm is unable to create a relative rotation vector that is perpendicular to the reference axis."
While I appreciate your ASCII, I'd really need to see this as a series of joints in a chain to work out what's going on.

The algorithm isn't the problem - my implementation of it might be.

@jvdnbus
Copy link

jvdnbus commented Jan 24, 2020

I have not yet tried any alternate methods.

Here's a crude test: caliko test

@alansley
Copy link
Collaborator Author

alansley commented Jan 24, 2020

I'll get back to you within a week.

Have you tried FIK? It's Caliko but in JavaScript.

@alansley
Copy link
Collaborator Author

alansley commented Jan 24, 2020

You have a rotation axis and a reference axis comprised of FOUR Vec3s. WTF?

I'm sorry, but this library was not designed to work in four-dimensional space.

You're on your own.

@jvdnbus
Copy link

jvdnbus commented Jan 24, 2020

Haha, no, those are simply different axes for each bone, still 3d! I figured that way I could try different setups more easily.

@jvdnbus
Copy link

jvdnbus commented Jan 25, 2020

I was able to get it working using "OLD VERSION 1.3.4 and earlier" of createRotationMatrix. The plane vector gave a zero vector with the newer version.

@alansley
Copy link
Collaborator Author

alansley commented Aug 8, 2020

Cool. Sorry for being a bit scattered in the replies above, I might have had a few too many shandies that night (as evidenced by the fact that I completely forgot that this part of the issue thread ever happened - oops).

Yeah, the v1.3.5 createRotationMatrix() method was knacked under some circumstances and would return an illegal 3x3 rotation matrix - it got fixed in the v1.3.8 release after prompting from this issue, when really I should have got on top of it from this one.

My bad - glad you got it sorted, anyhow =D

@vazgmik
Copy link

vazgmik commented Dec 30, 2023

@alansley hello, thanks for library) I'm trying to use it for human body inverse kinematic.

I have similar problem with local hinge discontinuity. I would like to fix problem with createRotationMatrix adding Qunternion support using steps below:

  1. If we have in FabrikBone3D additional info : mStartLocation and startQuanternion, mEndLocation and endQuanternion.
  2. Then we need to calculate rotation matrix as rotation matrix between quanternions and use it instead of m in all places where Mat3f.createRotationMatrix used.

Is my understanding correct? Can you give ideas on using new approach with quanternions, what else should be considered in algorithm to change?

@alansley
Copy link
Collaborator Author

alansley commented Jan 6, 2024

  1. If we have in FabrikBone3D additional info : mStartLocation and startQuanternion, mEndLocation and endQuanternion.

I think you'd only need a single Quaternion to describe the rotation of a FabrikBone3D.

  1. Then we need to calculate rotation matrix as rotation matrix between quanternions and use it instead of m in all places where Mat3f.createRotationMatrix used.

I'm not quite sure I understand what you mean. When creating each bone, rather than giving it just a start and end position (which would get you the pitch and yaw) you'd also want to specify the roll. Essentially rather than Mat3f.createRotationMatrix attempting to guess a sane rotation matrix you'd provide a complete on.

With the join restrictions you'd probably end up doing a lot of Quaternion-to-Euler conversion (and back) unless you take the joint restrictions in Euler angles and then convert them to some kind of`Quaternion that describes the degree of restriction relative to whatever you've specified (e.g., global space, or local space previous bone).

You'd also need to import a math library that provides Quaternions, or write the bits and pieces you need and add them to the custom math classes that come with Caliko.

I definitely think it can be done, but I don't have the time or inclination to do it - if you have a go maybe just start small. You could write the world's simplest Quaternion class and have a pretend/text-based setup with 2 bones, then try restricting the end-effector bone about the base-bone using Quaternions some small amount / with some easy to intuit values (although you'll likely have to go to/from Eulers as just looking at Quaternions (as raw values) doesn't really intuit which way they're pointing (at least to me).

If you can get some simple setup working like that, then you've got proof of concept and could have a go at integrating them into Caliko proper, if you wanted.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants