1. Do not share user accounts! Any account that is shared by another person will be blocked and closed. This means: we will close not only the account that is shared, but also the main account of the user who uses another person's account. We have the ability to detect account sharing, so please do not try to cheat the system. This action will take place on 04/18/2023. Read all forum rules.
    Dismiss Notice
  2. For downloading SimTools plugins you need a Download Package. Get it with virtual coins that you receive for forum activity or Buy Download Package - We have a zero Spam tolerance so read our forum rules first.

    Buy Now a Download Plan!
  3. Do not try to cheat our system and do not post an unnecessary amount of useless posts only to earn credits here. We have a zero spam tolerance policy and this will cause a ban of your user account. Otherwise we wish you a pleasant stay here! Read the forum rules
  4. We have a few rules which you need to read and accept before posting anything here! Following these rules will keep the forum clean and your stay pleasant. Do not follow these rules can lead to permanent exclusion from this website: Read the forum rules.
    Are you a company? Read our company rules

Writing a motion cueing software from scratch.

Discussion in 'DIY Motion Simulator Projects' started by Dirty, Feb 28, 2019.

  1. Dirty

    Dirty Well-Known Member Gold Contributor

    Joined:
    Oct 15, 2017
    Messages:
    744
    Occupation:
    All the way up front.
    Location:
    Germany
    Balance:
    7,909Coins
    Ratings:
    +878 / 3 / -0
    Hey @Trip Rodriguez,

    Sorry for the late reply.

    I'd point to the usual suspect, which in this case is: I think your physical rig does not perfectly correspond to the mathematical representation of it. Somewhere in the software you must have entered the mechanical parameters of the rig. Like the radius of the connection points of the upper or lower platforms, the servo arm lengths, their positions, angles, etc.
    The software uses this information to create a mathematical representation of your rig. Then for every frame it will calculate the servo arm angles as this virtual rig moves. When these angles are then being sent to the motors, the physical rig SHOULD be in the same position where the mathematical representation is. This does only work well if the physical rig was manufactured and measured precise enough.

    Maybe you can find measurements that are off. Check if all the parameters you entered into the 6DOF software are correct and the rods are all equally long. Are the servo motors positioned exactly where they belong? Is the spacing correct and are their axis' aligned at 120° angles?

    That's where I'd go looking....

    Dirty :)
  2. pmvcda

    pmvcda aka FlyPT

    Joined:
    Nov 3, 2010
    Messages:
    2,158
    Location:
    Portugal
    Balance:
    15,264Coins
    Ratings:
    +2,530 / 17 / -0
    My Motion Simulator:
    6DOF
    Can't make it now, but tomorrow I will post the code if you want it.
    Also explain how I made the calculation.
    • Winner Winner x 2
  3. pmvcda

    pmvcda aka FlyPT

    Joined:
    Nov 3, 2010
    Messages:
    2,158
    Location:
    Portugal
    Balance:
    15,264Coins
    Ratings:
    +2,530 / 17 / -0
    My Motion Simulator:
    6DOF
    So how do I make the calculation (this method works when the motor shafts are horizontal):

    This was the image I posted:

    IMG_20190404_174333.jpg

    Can't find it also... It's buried somewhere.

    So...

    In black:
    Point B (origin) is the intersection between the crank and the motor shaft. It's a solid connection. Like I said above, it's the bottom joint.
    O is the joint that connects the crank to the rod and she can rotate in any direction.
    T is the joint in the top moving platform.
    C is the crank length.
    R is the rod length.

    In gray:
    The 3 axis (x,y,z) with x and y defining the horizontal plane.
    The circle that represents the rotation of the crank.
    L is the distance between the bottom joint (origin) and the joint on the moving platform (T). That's where we would have a linear actuator.

    In red:
    A is the angle of the crank relative to the horizontal plane (what we want to calculate)
    B is the angle defined by the intersection of the horizontal plane with the plane where we have the crank rotating.

    So to define this we need...

    Fixed values:
    R = Rod length
    C = Crank length
    B = Angle of the motor axis + 90º around the z axis (90º, because crank is perpendicular to the motor axis)

    Constraints:
    Motor axis must belong to the horizontal plane

    Variables:
    L, the distance between the bottom and the top joint

    As we see in the image, we can calculate the point O with the system of equations I show.
    The the distance from O to T would be defined by the equation bellow.

    Replacing Ox, Oy and Oz and solving for A gives you more than one solution.
    Since I'm "lazy", I used this: https://www.wolframalpha.com/input/?i=solve+for+a+{r^2=(x-c*cos(a)cos(b))^2+(y-c+cos(a)sin(b))^2+(z-c+sin(a))^2}

    The solutions can be ignored and we just use the 3rd one.
    I don't remember now, but I took a look at all solutions and concluded that 3rd and 4th can be used without problems if crank and rod are more than zero. (not 100% sure. Would have to look again)

    So the code is (optimised, but maybe it could be more):
    Code:
       private double _storedAngle = 0; // We store calculated angle to use it if the new calculation has no solution (we stay where we where)
    
        private double calculateAngle(Point3D P)
        {
            double x = P.x - _fixedJoint.x;
            double y = P.y - _fixedJoint.y;
            double z = P.z - _fixedJoint.z;
            double a1 = 2.0 * _crank * x * _verticalAngleCOS;
            double a2 = 2.0 * _crank * y * _verticalAngleSIN;
            double a3 = _crank * _crank - _rod * _rod + x * x + y * y + z * z;
            double a4 = a1 + a2 + a3;
            double angle = 2.0 * (Math.Atan((2.0 * _crank * z - 0.5 * Math.Sqrt(16.0 * _crank * _crank * z * z - 4.0 * (-a1 - a2 + a3) * (a4))) / (a4)));
    
            // CALCULATION IS DONE
            // NOW LET'S SEE IF WE HAVE A SOLUTION
            // IF WE HAVE A SOLUTION, LET'S CALCULATE THE CRANK/ROD JOINT JUST TO SHOW IN THE INTERFACE
    
            // If we have a valid angle
            if (!double.IsNaN(angle)) // Testing if the result is a number
            {
                _outOfRange = false;
                _rodCrankJoint.x = _crank * Math.Cos(angle);
                _rodCrankJoint.y = _fixedJoint.y + _rodCrankJoint.x * _verticalAngleSIN;
                _rodCrankJoint.x *= _verticalAngleCOS;
                _rodCrankJoint.x += _fixedJoint.x;
                _rodCrankJoint.z = _fixedJoint.z + _crank * Math.Sin(angle);
                _storedAngle = angle;
            }
            // Not possible to calculate an angle, completely beyond reach, no result, so we stay at the last possible position and that was the _storedAngle
            else
            {
                _outOfRange = true;
                angle = _storedAngle;
            }
            return angle; // In radians
        }
    Where P is the top joint point for who we want to calculate the angle
    _fixedJoint is the coordinates of the point B. We are just dislocating the P to be relative to origin
    _verticalAngleCOS and _verticalAngleSIN are cosine and sine of the angle B refered above
    _crank is the length of the crank
    _rod the length of the rod
    • Winner Winner x 2
    • Informative Informative x 1
    Last edited: Aug 2, 2019
  4. Dirty

    Dirty Well-Known Member Gold Contributor

    Joined:
    Oct 15, 2017
    Messages:
    744
    Occupation:
    All the way up front.
    Location:
    Germany
    Balance:
    7,909Coins
    Ratings:
    +878 / 3 / -0
    Holy smoke,...

    Yes, that was the picture I remembered. Thank you very much. Quite a lot to chew on. But I'm sure it will save me a lot of time.

    The first question (of potentially many to follow) would be: Is B the center of the circle/disk described by O? Or better: Does B lay in the plane described by the rotating position O?

    The reason I'm asking is, I think it would make sense if it was, but then the (red) angle B would have to be zero.

    I will need a little time to digest this, I guess. :)

    Thanks a lot! Btw, super cool of you to share code! I really owe you for that.

    Dirty :)
  5. pmvcda

    pmvcda aka FlyPT

    Joined:
    Nov 3, 2010
    Messages:
    2,158
    Location:
    Portugal
    Balance:
    15,264Coins
    Ratings:
    +2,530 / 17 / -0
    My Motion Simulator:
    6DOF
    It was a bad choice of letters... But that was the drawing.
    B is the origin and center of the circle.
    Angle B is the the...
    If we look at the rig from the top, the circle defined by the rotation of the rod/crank joint that has it's center in point B, makes the angle B around axis z

    Hope it's clear...
  6. Dirty

    Dirty Well-Known Member Gold Contributor

    Joined:
    Oct 15, 2017
    Messages:
    744
    Occupation:
    All the way up front.
    Location:
    Germany
    Balance:
    7,909Coins
    Ratings:
    +878 / 3 / -0
    Hey @pmvcda,

    I think I understood the geometry.

    And I think I understood the math up until here:
    Bildschirmfoto 2019-08-11 um 16.55.40.png

    So you can calculate angle "a" (in radians?) with this formula:
    Bildschirmfoto 2019-08-11 um 16.55.40 2.png

    ...as long as you make sure that this term is not zero:
    Bildschirmfoto 2019-08-11 um 16.55.40 3.png

    ...and this term is not zero:
    Bildschirmfoto 2019-08-11 um 16.55.40 4.png
    ...and you plug in any integer for "n".
    Bildschirmfoto 2019-08-11 um 16.55.40 5.png


    Can you tell me if this "n" is important? As far as I can see, I can just ignore the "+ π n" Term at the end, right? I assume it is in there, because the tan function will repeat every π anyways. And the factor of 2 in front of the bracket means that the solutions will repeat for every revolution. Do you agree?

    What IF those two terms become zero? What does it mean for each of the two cases? Do you just stop and hold the last "good" position?

    Thanks for your help!

    Dirty :)
  7. Dirty

    Dirty Well-Known Member Gold Contributor

    Joined:
    Oct 15, 2017
    Messages:
    744
    Occupation:
    All the way up front.
    Location:
    Germany
    Balance:
    7,909Coins
    Ratings:
    +878 / 3 / -0
    OK, I think now I understand you code as well.... :thumbs

    Is...
    Code:
    if (!double.IsNaN(angle))
    ...really all the checking you do? Is this sufficient? I mean it's short, and if it covers all cases then it sure is an elegant solution.

    The only thing I am still struggling with is the fact that there should always be two solutions. Two angles "a" that satisfy the equations. Did you ever run into problems with these equations when you use them in your graphical representation in your software?

    Dirty :)
  8. Dirty

    Dirty Well-Known Member Gold Contributor

    Joined:
    Oct 15, 2017
    Messages:
    744
    Occupation:
    All the way up front.
    Location:
    Germany
    Balance:
    7,909Coins
    Ratings:
    +878 / 3 / -0
    One more thing about the terms "a1" to "a4" in your code:
    Code:
    double a1 = 2.0 * _crank * x * _verticalAngleCOS;
            double a2 = 2.0 * _crank * y * _verticalAngleSIN;
            double a3 = _crank * _crank - _rod * _rod + x * x + y * y + z * z;
            double a4 = a1 + a2 + a3;
    You're using variables called "_verticalAngleCOS" and "_verticalAngleSIN".

    In the equation (from WolframAlpha) it reads... Bildschirmfoto 2019-08-11 um 16.55.40.png cos(b) and sin(b) . With b being the horizontal angle.

    Am I correct to assume that it should definitely be angle "b" in these places? Any reason for calling those variables "_vertical angleCOS/SIN"?

    Dirty :)
  9. pmvcda

    pmvcda aka FlyPT

    Joined:
    Nov 3, 2010
    Messages:
    2,158
    Location:
    Portugal
    Balance:
    15,264Coins
    Ratings:
    +2,530 / 17 / -0
    My Motion Simulator:
    6DOF
    Sorry, I'm full of work and on the phone..
    Answering fast, but after tomorrow I will take a better look.
    n*PI is turns, so we can ignore n.
    In the code the idea is to calculate only 1x, something we use multiple times. That's the reason for the terms. Same for the vertical Angles, instead of calculating the cosine multiple times in the loop, I calculate it once, when the user changes the vertical angle. From then, it's fixed or pré calculated.
    The multiple solutions, I don't remember, I have to take a look, but I think it was zero, only if the rod or crank was zero, and that doesn't happen by design. (not sure here, but I think it was that)
  10. pmvcda

    pmvcda aka FlyPT

    Joined:
    Nov 3, 2010
    Messages:
    2,158
    Location:
    Portugal
    Balance:
    15,264Coins
    Ratings:
    +2,530 / 17 / -0
    My Motion Simulator:
    6DOF
    I'm mixing the angles when I talk about them. B is the angle in the horizontal plane, for the vertical axis. Hope I can explain it...
    In the equation c and r are cranck and rod
    • Agree Agree x 1
  11. Trip Rodriguez

    Trip Rodriguez VR Pilot

    Joined:
    May 8, 2016
    Messages:
    675
    Location:
    Lake Ariel, Pennsylvania
    Balance:
    3,922Coins
    Ratings:
    +330 / 6 / -0
    My Motion Simulator:
    6DOF
    I had another thought guys... something I've noticed that causes harsh cues is when multiple cues "stack up" on the same actuators and directions.... So like if you have a big heave up cue and a big pitch up cue the two front actuators get double the signal and maybe try to move up twice as fast? I say this because I can set to where I'm happy with heave, and set to where I'm happy with pitch, but then I turn them both on at the same time and the cue starts much too harshly.

    What do you think? If you agree that this is a thing, maybe worth thinking about the best way to deal with it.
  12. pmvcda

    pmvcda aka FlyPT

    Joined:
    Nov 3, 2010
    Messages:
    2,158
    Location:
    Portugal
    Balance:
    15,264Coins
    Ratings:
    +2,530 / 17 / -0
    My Motion Simulator:
    6DOF
    @Dirty

    So, I took a better look.

    The conditions to have zero don't happen, only if you have c or r (crank and rod) zero.
    That could cause the top joint to match the rod/crank joint and make a division by zero.
    By design, this kind of mechanism wont work having those values zero at the same time.

    The n*PI is ignored, that's what gives you multiple results. At each half turn (PI) you have a possible result.
    I ignore it, because we always want the lowest n. We want the closest possible angle near zero or the result could be strange.
    Imagine we are going up and you use the 2nd solution, that would be 1*PI.
    The rig would go up and then start going down to reach that 1*PI, while in the first solution it just goes up and stops. That's the effect of going through all the circle, you need to pass the highest value to reach the next 1*PI.
    Same happens going down. So we want the solution closest to zero. That's 0*PI.

    The way I test the result to know if it's valid is for me the fastest one. You just test to see if you have a number as a result.
    If there's no solution it returns nan (not a number).
    After getting a result I also make other verification's to know if the angle is in the range defined by the user. That is not on the code, but it's simple.
    If angle is not valid, I keep the rig in the last one.
    • Informative Informative x 1
  13. Dirty

    Dirty Well-Known Member Gold Contributor

    Joined:
    Oct 15, 2017
    Messages:
    744
    Occupation:
    All the way up front.
    Location:
    Germany
    Balance:
    7,909Coins
    Ratings:
    +878 / 3 / -0
    Hey :)

    just a quick sign of life :) I'm back from my vacation and am working on support for the crank arm setup in my software:
    Bildschirmfoto 2019-09-04 um 21.50.21.png

    The formula that @pmvcda posted works fine! At least for all the cases that I tested I got perfectly fine results :)

    Two notes to mention in case some future motion sim software writer happens to stumble across this thread:
    1. The "+ π n" Term can indeed be omitted. It creates multiple solutions each 360° apart. So, when there is a valid solution at an angle of 20° then there are automatically more solutions at 380° and 740° and so on.
    2. There is "another" solution to the equation that can be found by flipping the sign in front of the root. So, instead of "-1/2√", technically it should read "±1/2√". If you find the formula to return a non-sensical solution, try flippig that sign.
    Cheers, Dirty :)
    • Like Like x 1
    • Informative Informative x 1
  14. Dirty

    Dirty Well-Known Member Gold Contributor

    Joined:
    Oct 15, 2017
    Messages:
    744
    Occupation:
    All the way up front.
    Location:
    Germany
    Balance:
    7,909Coins
    Ratings:
    +878 / 3 / -0
    Hey there :)

    a quick video-update on how the crank arm geometry is making progress. Calculations are all working fine, but finding a nice UI to configure the setup is going to be a nightmare...



    Cheers, Dirty :)
    • Like Like x 4
  15. Dirty

    Dirty Well-Known Member Gold Contributor

    Joined:
    Oct 15, 2017
    Messages:
    744
    Occupation:
    All the way up front.
    Location:
    Germany
    Balance:
    7,909Coins
    Ratings:
    +878 / 3 / -0
    Hey :)

    Maybe someone can help we with this: Something I am unsure about is how to convert the angle of the crank arms into a value to transmit to the AMC1280.

    See this example:
    Bildschirmfoto 2019-09-10 um 20.42.37.png

    What would I have to transmit to the controller in this example case? Do I always take the measurement from the horizontal (in this example 25° and 25°)? Or the angle from a common reference direction (here 65° and 25°)?
    I guess I will have to calculate where the servo position is on a scale from 0-100% of it's possible travel, but what does "possible travel" even mean in this case? What is 0% and what is 100%? Are those limits (0 and 100%) angles that the user has to specify?

    How do you guys (@pmvcda , @hexpod ) do it in your software?

    Thanks in advance.... :)

    Dirty:)
  16. hexpod

    hexpod http://heXpod.xyz

    Joined:
    Apr 18, 2016
    Messages:
    1,185
    Location:
    berlin
    Balance:
    7,636Coins
    Ratings:
    +369 / 5 / -0
    My Motion Simulator:
    DC motor, 6DOF
    Yes BUT not always ;-). This seams to be kind of standard. - „not always because for compatibility reasons, the „heave offset“ feature may NOT be followed by the bit output (in other words the bit middle position is not necessarily when the lever is horizontal)

    Without heave or surge offset applied, yes, I guess the middle bit range should be horizontal for all actuators.

    @Thanos controller can revert the direction (interpreting the signal as negative or positive) in order to adapt to 3 different wiring scenarios, I would say, for better transparency, it’s clearer to make the bit increasing / decreasing in the same way for all actuators.

    From memory, Flypt uses it with a feature „output swap“ swapped.
    Kinematic „possible travel“ is dynamic and can be variable on the design and the actual pose. (Can reach up to 110deg in each dir.)

    The bit travel is arbitrary and depends on the feedback sensors. You can calibrate +/- 80deg each direction (10deg. security to avoid eventual overshoots with 180 sensors) as starting point and build a must have scaling feature.

    It’s up to the user to adapt the proper ratio depending on his hardware.
    It can be a feature as a limiter you can develop afterwards.

    The most important is a working “out of reach” limiter. Sometimes called “out of pose”

    Best
    Last edited: Sep 11, 2019
  17. hannibal

    hannibal Active Member

    Joined:
    Sep 29, 2018
    Messages:
    677
    Balance:
    4,490Coins
    Ratings:
    +297 / 4 / -0
    My Motion Simulator:
    6DOF
    linear actuators not getting love? :'(
  18. Dirty

    Dirty Well-Known Member Gold Contributor

    Joined:
    Oct 15, 2017
    Messages:
    744
    Occupation:
    All the way up front.
    Location:
    Germany
    Balance:
    7,909Coins
    Ratings:
    +878 / 3 / -0
    Oh, they're gettin' PLENTY of love :)

    To be honest, linear actuators are my main focus. I will be using them and unless someone is constrained by a tight build space I think the pros way outweigh the cons.

    But at the very least, I think crank arms should be supported for completeness' sake. I have spent the past few weeks understanding the math behind them and am now implementing them in my software. It was a steep learning curve, that's for sure.

    Linear actuators are fully supported already :thumbs:grin

    Dirty :)
    • Like Like x 2
    • Friendly Friendly x 1
  19. Zed

    Zed VR Simming w/Reverb Gold Contributor

    Joined:
    Apr 4, 2017
    Messages:
    1,040
    Location:
    USA
    Balance:
    5,841Coins
    Ratings:
    +1,040 / 4 / -0
    My Motion Simulator:
    2DOF, DC motor, JRK
    Hey Dirty, looks good and I need to reread the whole thing but just to bend things, can your software support unequal rod lengths like this? I have been considering this arrangement as an example since it keeps footprint small with bigger motors but changes the geometry. Just asking. I need to do the simulation myself to see how it changes things but was curious if yours already allows for this kind of arrangement or if you gurus already see issues with this?

    Cheers!
    FB2B946C-81F3-4F3A-B6A9-26AC08AF6FD5.jpeg
  20. Dirty

    Dirty Well-Known Member Gold Contributor

    Joined:
    Oct 15, 2017
    Messages:
    744
    Occupation:
    All the way up front.
    Location:
    Germany
    Balance:
    7,909Coins
    Ratings:
    +878 / 3 / -0
    Holy smoke...! That's an interesting configuration :)

    But if space constraints are tight, one that might very well save quite a lot of space while sacrificing platform travel only minimally, if at all.

    I never thought of such a configuration, so my software as it is now does not support this kind of setup. It is however not as complicated as it looks. I'm sure it could be implemented with a few evenings time. If you (or anyone else) wants such a setup, you can download the project from GitHub and add it. It's all open source.

    The asymmetric rods shouldn't be a problem, because the math will take care of that. I don't see any "categorical" issues with such a geometry per se, but two aspects I think are worth considering:
    1. Crankarm setups have one more joint than a linear actuator, so for the same size I'd expect them to have a tendency to be a little bit more shaky.
    2. Shrinking the footprint while keeping the payload the same will add additional shakiness.
    ...but those are considerations about crank arm setups in general. I think this stacked layout can deliver a performance equal to any other crank arm setup.

    Dirty :)
    • Like Like x 1
    • Informative Informative x 1