1

I'm studying to implement a pendulum motion simulation.

void SinglePendulum::setPendulum(double dt) {
    // Apply gravity
    auto acc = acceleration;
    Vec3<double> gravity(0.0, -9.8, 0.0);   // x = 0.0, y = -9.8, z = 0.0
    acc += gravity * inverseMass;           // get accelation
    velocity += acc * dt;                   // get velocity
    velocity *= pow(dampingCoeff, dt);      // apply damping
    variablePosition += velocity * dt;      // gravity-affected weight

    // Vector from reference point to end point after being affected by gravity
    variableVector = variablePosition - pivotPosition;        // a Position - b Position : b->a vector

    // The angle between the reference vector and the current vector
    double angle;
    angle = standardVector.Dot(variableVector);                             // dot product the reference vector and the variable vector
    double standVecLength = standardVector.Length();                        // magnitude of reference vector
    double variaVecLength = variableVector.Length();                        // magnitude of variable vector
    double finalAngle = acos(angle / (standVecLength * variaVecLength));    // get angle

    // Apply position with pendulum motion
    changePos.Set(sin(finalAngle) * length, cos(finalAngle) * length, 0.0); // apply x, y, z value
}

As far as I know, It is understood that after gravity, the pendulum motion can be implemented using the angle between the reference straight vector and the changed vector.

So I apply gravity first, I then calculated the vector from the reference point to the point affected by gravity.

And I did dot product with the reference vector and the vector of the changed position.

And I calculated the magnitude of each vector, and I calculated the angle using magnitude value and theta value.

And the angle is sin on the x-axis and cos on the y-axis, which is the pendulum motion.

However, the following results were found.

enter image description here

I have no idea which part I missed. Is there anything else I should do?

I looked at this document and tried to modify the code, but it didn't work properly either.

Edited code

void SinglePendulum::setPendulum(double dt) {
    // The angle between the reference vector and the current vector
    double theta;
    theta = standardVector.Dot(variableVector);                             // dot product the reference vector and the variable vector
    double standVecLength = standardVector.Length();                        // magnitude of reference vector
    double variaVecLength = variableVector.Length();                        // magnitude of variable vector
    double angle = acos(theta / (standVecLength * variaVecLength));         // get angle

    Vec3<double> gravity(0.0, -9.8, 0.0);   // x = 0.0, y = -9.8, z = 0.0

    Vec3<double> forceX;
    Vec3<double> forceY;
    forceX.SetX(-9.8 * sin(angle));     // x = -9.8 * sin(angle), y = 0.0, z = 0.0
    forceY.SetY(-9.8 * cos(angle));     // x = 0.0, y = -9.8 * cos(angle), z = 0.0

    // Apply gravity
    auto acc = acceleration;
    acc += gravity * inverseMass;           // get accelation
    acc += forceX;
    acc += forceY;
    velocity += acc * dt;                   // get velocity
    velocity *= pow(dampingCoeff, dt);      // apply damping
    variablePosition += velocity * dt;      // gravity-affected weight

    // Vector from reference point to end point after being affected by gravity
    variableVector = variablePosition - pivotPosition;      // a Position - b Position : b->a vector
}
Keastmin
  • 85
  • 6

1 Answers1

1

From mine point of view you are doing the sim wrongly ...

  • acc,vel should be also vec3 (not seen in your code so not sure if it OK or not)
  • no need for goniometrics vector math is enough (acos is not sign safe)
  • your dampening is not how friction works its magnitue depend on actual velocity (this is the major reason of the problem you have)

This is how I see it should be (C++, GLSL like vector math):

//---------------------------------------------------------------------------
vec3 p0;            // fixed point of pendulum
float l;            // pendulum length (from p0 to pos)
vec3 pos,vel;       // ball state
float r;            // ball radius
//---------------------------------------------------------------------------
void pendulum_update(double dt)
    {
    vec3 acc,dir;
    float v;
    const float k=0.0001,g=9.81;
    dt*=10.0;                                   // time multiplier for simulation speed ...
    // compute driving force/acceleration
    acc=vec3(0.0,+g,0.0)        // gravity
       -vel*(k*length(vel));    // air friction
    // remove radial part of acc
    dir=pos-p0; dir/=l;         // unit direction vector of pendulum
    acc-=dir*dot(dir,acc);
    // Newton/d'Lambert simulation
    vel+=acc*dt;
    pos+=vel*dt;
    // repair pos so its fixed distance to p0
    dir=pos-p0; dir/=length(dir);               // unit direction vector of pendulum
    pos=p0+l*dir;
    // repair vel so its perpendicular to pendulum
    v=length(vel);
    if (v>1e-6)
        {
        vel=cross(vel,dir);
        vel=cross(dir,vel);
        vel*=v/length(vel);
        }
    }
//---------------------------------------------------------------------------
void pendulum_init(float xs,float ys)               // init pendulm to screen with xs,ys dimensions
    {
    Randomize();
    r=0.025*xs;
    l=0.25*xs;
    p0 =vec3(0.5*xs,0.5*ys,0.0);
    pos=p0+vec3(+l,0.0,0.0);
    vel=vec3(0.0,0.0,0.0);
    }
//---------------------------------------------------------------------------

I just slightly modified my Can't flip direction of ball without messing up gravity example to match yours.

Here preview:

preview

[Edit1] double pendulum

Not sure if physically correct but I see it like this:

//---------------------------------------------------------------------------
struct _ball            // mass point
    {
    int ix;             // (-2): fixed, (-1): free, (0,1,2...): joined to ball[ix] using l sized solid bond, no loops allowed!!!
    float r,m,l;        // ball radius [m], ball mass [Kg], joint size [m]
    vec3 pos,vel,F;     // ball state [m],[m/s],[N]
    };

const int balls=3;
_ball ball[balls];
//---------------------------------------------------------------------------
void pendulum_update(double dt)
    {
    const float k=0.001;        // air friction coefficient should be function of object shape
    int i,j;
    float v;
    vec3 dir,nor,F0,F1;
    _ball *b,*b0,*b1;
    vec3 g=vec3(0.0,+9.81,0.0); // local gravity field
    dt*=10.0;                   // time multiplier for simulation speed ...

    // clear forces
    for (b=ball,i=0;i<balls;i++,b++) b->F=vec3(0.0,0.0,0.0);
    // compute driving force (F = m*acc) as gravity - air friction
    for (b=ball,i=0;i<balls;i++,b++) if (b->ix!=-2) b->F=(b->m*g) - b->vel*(k*length(b->vel));
    // apply bonds on F (in reverse order to avoid the need for recursion)
    for (b0=ball+balls-1,i=0;i<balls;i++,b0--)
     if (b0->ix>=0)
        {
        b1=ball+b0->ix;
        dir=normalize(b1->pos-b0->pos);     // unit direction vector of bond
        // remove radial part of acc
        F0=dir*dot(dir,b0->F);              // radial part of b0->F
        F1=dir*dot(dir,b1->F);              // radial part of b1->F
        F1=vec3(0.0,0.0,0.0);
        if (b0->ix!=-2){ b0->F+=F1-F0; }
        if (b1->ix!=-2){ b1->F+=F0-F1; }
        }

    // Newton/d'Lambert simulation vel = Integral((F/m)*dt)
    for (b=ball,i=0;i<balls;i++,b++) if (b->ix!=-2) b->vel+=(b->F/b->m)*dt;
    // apply bonds on vel
    for (b0=ball+balls-1,i=0;i<balls;i++,b0--)
     if (b0->ix>=0)
        {
        b1=ball+b0->ix;
        dir=normalize(b1->pos-b0->pos);     // unit direction vector of bond
        // transfer all vel to axial direction
        if (b0->ix!=-2)
            {
            v=length(b0->vel);
            if (v>1e-6)
                {
                nor=cross(b0->vel,dir);
                nor=cross(nor,dir);
                if (length(nor)>1e-6) b0->vel=-v*normalize(nor);
                }
            }
        }

    // Newton/d'Lambert simulation pos = Integral(vel*dt)
    for (b=ball,i=0;i<balls;i++,b++) if (b->ix!=2) b->pos+=b->vel*dt;
    // apply bonds on pos
    for (b0=ball+balls-1,i=0;i<balls;i++,b0--)
     if (b0->ix>=0)
        {
        b1=ball+b0->ix;
        dir=normalize(b1->pos-b0->pos);     // unit direction vector of bond
        // repair b0 distance to b1
        b0->pos=b1->pos-b0->l*dir;
        }
    }
//---------------------------------------------------------------------------
void pendulum_init(float xs,float ys)               // init pendulm to screen with xs,ys dimensions
    {
    Randomize();
    vec3 p,v=vec3(0.0,0.0,0.0);
    _ball *b=ball;
    // init balls
    p=vec3(0.5*xs,0.25*ys,0.0);
    b->ix=-2; b->m=0.5; b->r=0.015*xs; b->pos=p; b->vel=v; if (b->ix>=0) b->l=length(b->pos-ball[b->ix].pos); else b->l=0.0; b++; p.x+=0.15*xs;
    b->ix= 0; b->m=0.5; b->r=0.020*xs; b->pos=p; b->vel=v; if (b->ix>=0) b->l=length(b->pos-ball[b->ix].pos); else b->l=0.0; b++; p.x+=0.15*xs;
    b->ix= 1; b->m=0.5; b->r=0.020*xs; b->pos=p; b->vel=v; if (b->ix>=0) b->l=length(b->pos-ball[b->ix].pos); else b->l=0.0; b++;
    }
//---------------------------------------------------------------------------

I added mass m and change acceleration acc to force F parameters on per ball manner. I also added bond as index to which ball current ball is connected...

Here preview:

preview 2x

So I just precompute all the F ... then transfer radial parts of F along bonds to joined ball ... and only then apply the integration.

Spektre
  • 49,595
  • 11
  • 110
  • 380
  • [This is the document I referred to.](https://www.khanacademy.org/computing/computer-programming/programming-natural-simulations/programming-oscillations/a/trig-and-forces-the-pendulum) I tried to apply the formula shown here, but it's not easy. – Keastmin Dec 01 '22 at 12:12
  • 1
    @Keastmin that is because you use their goniometrics ... that is backward and also not aplicable in 3D and the author or desired audience most likely do not know the vector math in physics ... Once you realize the relation between `dot(a,b)` and `cos(ang)` you will see its the same thing ... Your major problem is the dampening as its not scaled with velocity as it should and also you change velocity by it that is not correct it should be applied to acceleration ... which leads to full stop and never going to next quadrant no matter what you do – Spektre Dec 01 '22 at 13:37
  • Thank you for your reply. My goal is to understand pendulum motion and implement double pendulum. I will implement it with c++ and OpenGL. However, I couldn't find a document that explained well how to apply external force in which direction. Is there any document you referred to when implementing that simulation? – Keastmin Dec 01 '22 at 15:43
  • And I saw ```1e-6``` for the first time this time. I need to search and study about this, too. – Keastmin Dec 01 '22 at 15:44
  • 1
    @Keastmin `1e-6` means `1 * 10^-6` = 1*0.000001 = 0.000001` its scientific notation syntax common for floats and doubles (to shorten big or small numbers) ... No I do not refer to any book ... all is just Newton d'Alembert physics (common knowledge in schools) ... its just once you have pendulum the tube or rope or whatever do not allow radial movement (distance is the same) so you just need to do some small corrections after each iteration... with double pendulum the forces will also be applied to the other pendulum side ... other than that its the same just twice – Spektre Dec 01 '22 at 17:06
  • I tried to understand by myself, I have a few questions that I don't understand. ```acc=vec3(0.0,+g,0.0) - vel*(k*length(vel));``` This code seems to direct the acceleration by subtracting the speed multiplied by the resistance constant. ```acc-=dir*dot(dir,acc);``` I don't know what this code is doing. ```dir=pos-p0;``` ,```dir/=length(dir);```, ```pos=p0+l*dir;``` This code is the code for maintaining the length of the line? ```if (v>1e-6)``` I wonder how the codes in the if statement after this code were written. – Keastmin Dec 03 '22 at 06:53
  • 1
    @Keastmin 1. yes `vec3(0.0,+g,0.0)` is gravity downwards (my y axis goes down) the `- vel*(k*length(vel));` is air friction because air friction with subsonic speeds is equal to `k*|vel|^2` in opposite direction to `vel` as vector `vel` is already of size `|vel|` its enough to multiply it by `k*|vel|` ... 2. `dot(dir,acc)` will give you projection of `acc` onto `dir` and viceversa. as `dir` is unit vector its the same as `|acc|*cos(angle_between_acc_and_dir)` so its equal to the part of `acc` that is in direction of `dir` once substracted from `acc` only the tangential part of `acc` is left – Spektre Dec 03 '22 at 07:45
  • 1
    @Keastmin 3. yes you right `dir=pos-p0;` is just direction of the line between fixed point and pendulum end point (ball) the `dir/=length(dir);` makes `dir` unit vector (size equal to 1) while preserving its direction. `pos=p0+l*dir;` will shift position from fixed point in `dir` direction by `l` distance so the result is in the same direction but aligned to `l` distance from fixed point. 4. `if (v>1e-6)` is just safeguard so the code inside is ignored if the speed is near zero (as that would mess the cross anyway)... – Spektre Dec 03 '22 at 07:51
  • 1
    @Keastmin the stuff inside is simple `vel=cross(vel,dir);` will make `vel` perpendicular to `vel` and `dir` (beware cross change the size of `vel` and also order of operands change sign of result) so if I apply it twice it will make `vel` perpendicular to `dir` while still in the same plane as was before (in relation to `dir` and original `vel`) and lastly `vel*=v/length(vel);` will restore `vel` original size `v` while preserving its corrected direction. Because after each iteration the `vel` will slightly deviate from the circular movement because our `dt` is finite size – Spektre Dec 03 '22 at 07:54
  • 1
    @Keastmin so the movement would be eliptical instead of circular ... its similar to problem described in [edit3] in here [Is it possible to make realistic n-body solar system simulation in matter of size and mass?](https://stackoverflow.com/a/28020934/2521214)... also for the double pendulum you will need to transfer forces between balls so you might end up with something like this [physics engine - phase order and other general information](https://stackoverflow.com/a/26584358/2521214) – Spektre Dec 03 '22 at 08:03
  • 1
    @Keastmin btw after some thinking the `// repair vel so its perpendicular to pendulum` should be better placed between `vel+=acc*dt;` and `pos+=vel*dt;` to improve precision slightly more ... – Spektre Dec 03 '22 at 08:13
  • I drew pictures and tried debugging with code, ```acc-=dir*dot(dir,acc);``` This code is not well understood. ```dot(dir, acc) = |dir||acc|*cos(theta)``` -> ```dir``` is normalized ```dir = 1 ``` -> ```|acc|*cos(theta)```. So ```dir``` is the direction from ```p0``` to ```pos``` and multiplied it by ```|acc|*cos(theta)``` to find which direction and subtracted it from ```acc``` to find which vector? Could you explain the meaning of ```|acc|*cos(theta)``` more specifically? – Keastmin Dec 05 '22 at 15:32
  • 1
    @Keastmin again that is because they do not use vector math in physics and do it naively using goniometrics in 2D ... the drawback is that when you compute the `ang` from `cos(ang)` using goniometrics the result might have wrong sign as `cos(ang) = cos(-ang)` so they have to check which direction of rotation is the correct one ... if you get rid of the gonimetrics completely like I did suddenly all works as should in both 2D and 3D without any need to test anything further or project and unproject to local plane ... also its much more faster and precise as it uses just simple math operations. – Spektre Dec 05 '22 at 16:15
  • I don't know the concept of goniometrics very well, so I think I made an error... I'll study more about that... But if it's just a code for direction, can't I multiply ```|acc|``` by ```dir``` and then change ```acc``` from ```dir``` to ```acc```?... Like this ```acc -= dir * Length(acc);```. – Keastmin Dec 06 '22 at 03:55
  • 1
    @Keastmin You can do it but it would be physically wrong as `dir * Length(acc)` will just change orientation of whole `acc` and we need perpendicular projection instead (radial part of acc) which is usually less than that (unless in vertical position) ... `cos(ang)` and `dot(a,b)` is doing the perpendicular projection that is why its there ... – Spektre Dec 06 '22 at 04:05
  • 1
    I finally understood all the meanings and principles of your code today... Thank you... Now I'm going to implement a double pendulum... Wikipedia was written to calculate the Lagrangian first... Then it was written to apply the Runge-Kutta method... This is my first time studying both Lagrangian and Runge-Kutta method, and the value from Lagrangian is hard to apply in a three-dimensional space, so using Runge-Kutta method? – Keastmin Dec 07 '22 at 13:42
  • 1
    @Keastmin Those are for simulating the stuff using ODE (ordinal differential equations) and not gravity based physical simulation like this ... IIRC Runge-Kutta is just a numeric way of computing Intergral so energy of system is preserved ... you do not need those at all unless you doing high precision computations – Spektre Dec 07 '22 at 16:52
  • I want to implement a double pendulum based on gravity... It's just that I'm still inexperienced, and it's possible to implement it, right? – Keastmin Dec 08 '22 at 12:53
  • @Keastmin yes its possible ... you just need to compute all forces first ... then transfer all the forces (and counter forces) along the pendulum joint lines and only then integrate ... – Spektre Dec 08 '22 at 19:51
  • Finally, I succeeded in implementing a double pendulum!... Thank you for your great help... I'd love to share this joy with you, but I've been banned by Stack Overflow... I think I didn't understand the policy of Stack Overflow due to my lack of English skills... And I also think my question was too selfish. With this incident, I will reflect on myself and study harder. – Keastmin Dec 09 '22 at 08:38
  • @Keastmin I do not see any problem with your question what was the reason for ban ? maybe its something unrelated to question itself... If you are not sure You can ask on Meta ... its exactly for clarifying things like these ... for example see mine [Bulletpoints EVERYWHERE](https://meta.stackoverflow.com/a/307557/2521214) they changed the layout again took me a while to find it you click on the up right most iconand click [Meta Stack Overflow](https://meta.stackoverflow.com/) that will switch you to Meta SO where you can ask about how to handle stuff, or report site bugs etc ... – Spektre Dec 09 '22 at 08:45
  • @Keastmin IIRC usual ban reasons are suspicious voting pattern or having more than one accounts "helping" eachother ... however once I encounter that a new user had conflict with another account (some bug with account IDs) which caused automatic generated 7 day bans occasionaly until it was solved – Spektre Dec 09 '22 at 08:48
  • @Keastmin added 2x pendulum example ... easily tweakable to Nx pendulum – Spektre Dec 09 '22 at 13:24
  • I am not currently banned for this question... I posted a question about c++, but the contents of the question were not organized... And there were overlapping contents... So I got three of downvote... And that question was deleted from another user... Thank you for your concern about this... I also implemented a function that can be adjusted through the angle mouse cursor of the double pendulum... Now I'm studying more because I want to implement Chaotic motion with the same conditions but different movements. – Keastmin Dec 10 '22 at 09:41