Keastmin
Keastmin

Reputation: 85

Pendulum movement is half done

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
}

Upvotes: 1

Views: 173

Answers (1)

Spektre
Spektre

Reputation: 51835

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.

Upvotes: 1

Related Questions