Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Help  Oscillations when setting cable lengths, probably due to dynamics
#1
So I have a scene as shown below:

   

I am sending control to change cable lengths of all 4 cables at once (action = [25.23, 26.18, 35, 33.67]).

Code:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//|Method to control cables individually
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////   
    // Method to perform control actions for cable 1
    public void set_c1()
    {
        if (c1_len > c1_control_ip)
        {
            cursor_1.ChangeLength(rope_1.restLength - c1_control_ip * time_dt/10);
        }

        else if(c1_len < c1_control_ip)
        {
            cursor_1.ChangeLength(rope_1.restLength + c1_control_ip * time_dt/10);
        }

        else
        {
            Debug.Log("Current Length and Control input are the same");
        }
       
    }

// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------//

    // Method to perform control actions for cable 2
    public void set_c2()
    {
        if (c2_len > c2_control_ip)
        {
            cursor_2.ChangeLength(rope_2.restLength - c2_control_ip * time_dt/10);
        }

        else if(c2_len < c2_control_ip)
        {
            cursor_2.ChangeLength(rope_2.restLength + c2_control_ip * time_dt/10);
        }

        else
        {
            Debug.Log("Current Length and Control input are the same");
        }
       
    }

// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------//

    // Method to perform control actions for cable 3
    public void set_c3()
    {
        if (c3_len > c1_control_ip)
        {
            cursor_3.ChangeLength(rope_3.restLength - c3_control_ip * time_dt/10);
        }

        else if(c3_len < c3_control_ip)
        {
            cursor_3.ChangeLength(rope_3.restLength + c3_control_ip * time_dt/10);
        }

        else
        {
            Debug.Log("Current Length and Control input are the same");
        }
       
    }

// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------//

    // Method to perform control actions for cable 4
    public void set_c4()
    {
        if (c4_len > c4_control_ip)
        {
            cursor_4.ChangeLength(rope_4.restLength - c4_control_ip * time_dt/10);
        }

        else if(c4_len < c4_control_ip)
        {
            cursor_4.ChangeLength(rope_4.restLength + c4_control_ip * time_dt/10);
        }

        else
        {
            Debug.Log("Current Length and Control input are the same");
        }
       
    }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//|Method to control all cables parallely
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    public void set_control()
    {
        Parallel.Invoke(set_c1, set_c2, set_c3, set_c4);
    }

However it looks like the cable lengths are overshooting while setting and because of that

Code:
        if (c2_len > c2_control_ip)
        {
            cursor_2.ChangeLength(rope_2.restLength - c2_control_ip * time_dt/10);
        }

        else if(c2_len < c2_control_ip)
        {
            cursor_2.ChangeLength(rope_2.restLength + c2_control_ip * time_dt/10);
        }


These above 2 conditions are counteracting to each other and I am getting an oscillating behavior of the system as shown in the following video.

Oscillation behavior
Reply
#2
1.- Just in case: make sure all ropes are part of the same solver and the same updater, since they're involved in the same simulation. Otherwise they will behave independently, calculating and applying forces as if no other ropes were attached to the rigidbody.

2.- You're assuming the rope is kinematic and has no momentum, so it cannot stretch or compress due to inertia. I'd recommend to leave some room for the rope to expand/contract a bit, like so:

Code:
if (c2_len > c2_control_ip + room)
        {
            cursor_2.ChangeLength(rope_2.restLength - c2_control_ip * time_dt/10);
        }

        else if(c2_len < c2_control_ip - room)
        {
            cursor_2.ChangeLength(rope_2.restLength + c2_control_ip * time_dt/10);
        }

Otherwise if the rope stretches or compresses even a little bit, it will trigger a change in rest length.  This is similar to what the included sample ObiRopeReel script does:

Code:
// Update is called once per frame
void Update()
{
    // get current and rest lengths:
    float length = rope.CalculateLength();
    float restLength = rope.restLength;

    // calculate difference between current length and rest length:
    float diff = Mathf.Max(0, length - restLength);

    // if the rope has been stretched beyond the reel out threshold, increase its rest length:
    if (diff > outThreshold)
        restLength += diff * outSpeed;

    // if the rope is not stretched past the reel in threshold, decrease its rest length:
    if (diff < inThreshold)
        restLength -= diff * inSpeed;

    // set the new rest length:
    cursor.ChangeLength(restLength);
}
Reply
#3
(29-03-2023, 09:45 AM)josemendez Wrote: 1.- Just in case: make sure all ropes are part of the same solver and the same updater, since they're involved in the same simulation. Otherwise they will behave independently, calculating and applying forces as if no other ropes were attached to the rigidbody.

2.- You're assuming the rope is kinematic and has no momentum, so it cannot stretch or compress due to inertia. I'd recommend to leave some room for the rope to expand/contract a bit, like so:

Code:
if (c2_len > c2_control_ip + room)
        {
            cursor_2.ChangeLength(rope_2.restLength - c2_control_ip * time_dt/10);
        }

        else if(c2_len < c2_control_ip - room)
        {
            cursor_2.ChangeLength(rope_2.restLength + c2_control_ip * time_dt/10);
        }

Otherwise if the rope stretches or compresses even a little bit, it will trigger a change in rest length.  This is similar to what the included sample ObiRopeReel script does:

Code:
// Update is called once per frame
void Update()
{
    // get current and rest lengths:
    float length = rope.CalculateLength();
    float restLength = rope.restLength;

    // calculate difference between current length and rest length:
    float diff = Mathf.Max(0, length - restLength);

    // if the rope has been stretched beyond the reel out threshold, increase its rest length:
    if (diff > outThreshold)
        restLength += diff * outSpeed;

    // if the rope is not stretched past the reel in threshold, decrease its rest length:
    if (diff < inThreshold)
        restLength -= diff * inSpeed;

    // set the new rest length:
    cursor.ChangeLength(restLength);
}

so I tried giving some threshold for the control inputs.

Code:
    public float cable_threshold = 0.01f;

Code:
    public void set_c1()
    {
        if (c1_len > c1_control_ip + cable_threshold)
        {
            cursor_1.ChangeLength(rope_1.restLength - c1_control_ip * time_dt);
        }

        else if(c1_len < c1_control_ip - cable_threshold)
        {
            cursor_1.ChangeLength(rope_1.restLength + c1_control_ip * time_dt);
        }

        else
        {
            Debug.Log("Current Length and Control input are the same for Cable 1");
        }
       
    }

for all the cables.
Now its not oscillating, however when I read the cable length using,
Code:
rope1.CalculateLength();

Its not the same as the control input, well it should not be since we provided the threshold, but atleast it should be within the threshold.

I am getting the error between the control inputs and current lengths (after changing the cable lengths) as,

Code:
Error in Control:
Cable1:  -0.2439530000000012
Cable2:  -0.48052399999999906
Cable3:  -0.3184590000000007
Cable4:  -0.26758999999999844

I am using the same fixed updater for all the cables with same Obi solver with 5 substeps.
The resolution of the rope is 0.6 with 100 pooled particles.

In Distance contraints I put stretching scale as 0.1, since with stretching scale = 1, the rope is sagging enormously.
Reply
#4
(29-03-2023, 12:44 PM)rohit_dhak Wrote: In Distance contraints I put stretching scale as 0.1, since with stretching scale = 1, the rope is sagging enormously.

Setting the stretching scale to 0.1 will tell the rope to try and keep their length at 10% of their rest length. Should be left to 1 (100%) unless you're going for some special effect.

If your ropes are sagging, either:

- Use more substeps (10-15 is completely fine). This will ensure ropes reach their target rest length during simulation. The manual contains an in-depth explanation of how iterations and substeps affect simulation quality: http://obi.virtualmethodstudio.com/manua...gence.html

- Decrease rope resolution. Less constraints will result in faster convergence and less spurious compliance (that is, less elastic ropes). A resolution of 0.6 means particles will slightly overlap each other, which means there's a lot of particles. Since your ropes are very long, that will require a rather high amount of substeps for the simulation to be accurate.

You can combine both: use lower-resolution ropes with more substeps.

Note you can use surface collisions in case your rope resolution needs to be low and gaps appear in-between particles.

kind regards,
Reply
#5
(29-03-2023, 01:23 PM)josemendez Wrote: Setting the stretching scale to 0.1 will tell the rope to try and keep their length at 10% of their rest length. Should be left to 1 (100%) unless you're going for some special effect.

If your ropes are sagging, either:

- Use more substeps (10-15 is completely fine). This will ensure ropes reach their target rest length during simulation. The manual contains an in-depth explanation of how iterations and substeps affect simulation quality: http://obi.virtualmethodstudio.com/manua...gence.html

- Decrease rope resolution. Less constraints will result in faster convergence and less spurious compliance (that is, less elastic ropes). A resolution of 0.6 means particles will slightly overlap each other, which means there's a lot of particles. Since your ropes are very long, that will require a rather high amount of substeps for the simulation to be accurate.

You can combine both: use lower-resolution ropes with more substeps.

Note you can use surface collisions in case your rope resolution needs to be low and gaps appear in-between particles.

kind regards,


So I am noticing one thing which I would like to get clarified.

I have following changes:
Sample code for 1 cable:

Code:
    public void set_c2()
    {
        if (c2_len > c2_control_ip + cable_threshold)
        {
            cursor_2.ChangeLength(c2_len - speed * time_dt/10);
        }

        else if(c2_len < c2_control_ip - cable_threshold)
        {
            cursor_2.ChangeLength(c2_len + speed * time_dt/10);
        }

        else
        {
            Debug.Log("Current Length and Control input are the same for Cable 2");
        }
       
    }


cable_threshold = 0.01f
time_dt (Timestep) = 0.01

Cable Obi Fixed updater substeps = 30

The thing that I am not understanding is,

   

I am giving the condition to get c2_len which is rope2.CalculateLength() set to the control input c2_control_ip
However when I logged the restLength, c2_len and c2_control_ip I saw that the c2_len is trying to set to restLength instead of c2_control_ip

Am I misunderstanding something?
Reply
#6
(30-03-2023, 07:39 AM)rohit_dhak Wrote: I am giving the condition to get c2_len which is rope2.CalculateLength() set to the control input c2_control_ip
However when I logged the restLength, c2_len and c2_control_ip I saw that the c2_len is trying to set to restLength instead of c2_control_ip

Am I misunderstanding something?

The actual -calculated- length of a rope always strives to be its rest length, so this is the intended result: in your case, c2_len will strive to be as close as possible to restLength.

Imagine you have a rope that's 5 meters long at rest (under no forces). Then you hang a heavy object from it, and the rope stretches to be 5.2 meters long. Its restLength is still 5, but CalculatedLength() would return 5.2.

If you now call cursor.ChangeLength(6), the rope's restLength is set to 6, but due to the force applied by the object hanging from it its CalculatedLength() might be 6.2 (or 6.5, 7, 8.2... depending on how many substeps are used and how compliant the rope is).
Reply
#7
(30-03-2023, 08:05 AM)josemendez Wrote: The actual -calculated- length of a rope always strives to be its rest length, so this is the intended result: in your case, c2_len will strive to be as close as possible to restLength.

Imagine you have a rope that's 5 meters long at rest (under no forces). Then you hang a heavy object from it, and the rope stretches to be 5.2 meters long. Its restLength is still 5, but CalculatedLength() would return 5.2.

If you now call cursor.ChangeLength(6), the rope's restLength is set to 6, but due to the force applied by the object hanging from it its CalculatedLength() might be 6.2 (or 6.5, 7, 8.2... depending on how many substeps are used and how compliant the rope is).


So If I got it right,

If the restLength = CalculateLength(), it means that the control input is applied properly, but with extra length in CalculateLength() due to stretching.
i.e. to say that the extra length is the sag length due to stretching?
Reply
#8
(30-03-2023, 08:31 AM)rohit_dhak Wrote: So If I got it right,

If the restLength = CalculateLength(), it means that the control input is applied properly, but with extra length in CalculateLength() due to stretching.
i.e. to say that the extra length is the sag length due to stretching?

Correct. This is usually useful to calculate strain (deformation due to forces), which is the ratio between the calculated length and the rest length.

Code:
float strain = rope.CalculateLength() / rope.restLength;

if strain is 1, it means the rope isn't stretching at all. >1 means the rope is stretched, and <1 means the rope is compressed.
Reply