Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Help Filo - Calculating weight on cable point
#1
Hi Virtual Method

I just recently bought Filo and I am so far very pleased. The physics is stable and it was easy to setup. In the application we are working on there is a 30-ton element lifted up by four different cables which you can hoist and lower. We would like to calculate the weight each cable is lifting and display this information to the user. How could we do this? Are there any build-in metrics we can read for the cables class or do we have to calculate it ourselves?
Reply
#2
(18-03-2020, 08:32 AM)MarcPilgaard Wrote: Hi Virtual Method

I just recently bought Filo and I am so far very pleased. The physics is stable and it was easy to setup. In the application we are working on there is a 30-ton element lifted up by four different cables which you can hoist and lower. We would like to calculate the weight each cable is lifting and display this information to the user. How could we do this? Are there any build-in metrics we can read for the cables class or do we have to calculate it ourselves?

Hi there,

Yes, a cable is made up of multiple cable joints, that attach to links. Each link can be of 4 different types (attachment, rolling, pinhole or hybrid). So a cable looks like this:
Code:
Link ----(cable joint)--- Link ----(cable joint)---- Link ---...etc

Each cable joint stores a value (impulse magnitude) that is basically the amount of "instant" force applied by each joint. To convert an impulse to a force you need to divide by the delta time, so you can get a tension force in Newtons. To get the mass in Kilograms "seen" by the cable at its ends, divide by acceleration (since F = ma). In case of a simple load hanging from the cable, use gravity (-9.81f m/s2). This will print out the force applied and mass lifted by each cable joint in a cable:

Code:
IList<CableJoint> joints = cable.Joints;
foreach (CableJoint j in joints){
           float force = j.ImpulseMagnitude / Time.fixedDeltaTime;
           Debug.Log(force + " N, Mass:" + force/-9.81f +" Kg");
}
Reply
#3
(18-03-2020, 09:32 AM)josemendez Wrote: Hi there,

Yes, a cable is made up of multiple cable joints, that attach to links. Each link can be of 4 different types (attachment, rolling, pinhole or hybrid). So a cable looks like this:
Code:
Link ----(cable joint)--- Link ----(cable joint)---- Link ---...etc

Each cable joint stores a value (impulse magnitude) that is basically the amount of "instant" force applied by each joint. To convert an impulse to a force you need to divide by the delta time, so you can get a tension force in Newtons. To get the mass in Kilograms "seen" by the cable at its ends, divide by acceleration (since F = ma). In case of a simple load hanging from the cable, use gravity (-9.81f m/s2). This will print out the force applied and mass lifted by each cable joint in a cable:

Code:
IList<CableJoint> joints = cable.Joints;
foreach (CableJoint j in joints){
           float force = j.ImpulseMagnitude / Time.fixedDeltaTime;
           Debug.Log(force + " N, Mass:" + force/-9.81f +" Kg");
}

Thank you for the detailed reply. I just implemented it and it seems to work like a charm!
Reply
#4
(18-03-2020, 09:32 AM)josemendez Wrote: Hi there,

Yes, a cable is made up of multiple cable joints, that attach to links. Each link can be of 4 different types (attachment, rolling, pinhole or hybrid). So a cable looks like this:
Code:
Link ----(cable joint)--- Link ----(cable joint)---- Link ---...etc

Each cable joint stores a value (impulse magnitude) that is basically the amount of "instant" force applied by each joint. To convert an impulse to a force you need to divide by the delta time, so you can get a tension force in Newtons. To get the mass in Kilograms "seen" by the cable at its ends, divide by acceleration (since F = ma). In case of a simple load hanging from the cable, use gravity (-9.81f m/s2). This will print out the force applied and mass lifted by each cable joint in a cable:

Code:
IList<CableJoint> joints = cable.Joints;
foreach (CableJoint j in joints){
           float force = j.ImpulseMagnitude / Time.fixedDeltaTime;
           Debug.Log(force + " N, Mass:" + force/-9.81f +" Kg");
}

After a bit more fiddling with it, I see some hiccups in my implementation. So the large element is hoisted by four cables and each cable can be moved around in a crane-like system. What this means is that each cable does not necessarily lift the load vertically, like you described in the example above. For me to calculate the load, wouldn't I need the ImpulseMagnitude to be a Vector3 instead? Or do I need to calculate the direction of the Impulse myself?

An image of a similar crane like system:

[img] https://omis.net/upl/medium/6-Jibs%20and...System.jpg[/img]
Reply
#5
(19-03-2020, 09:33 AM)MarcPilgaard Wrote: After a bit more fiddling with it, I see some hiccups in my implementation. So the large element is hoisted by four cables and each cable can be moved around in a crane-like system. What this means is that each cable does not necessarily lift the load vertically, like you described in the example above. For me to calculate the load, wouldn't I need the ImpulseMagnitude to be a Vector3 instead? Or do I need to calculate the direction of the Impulse myself?

An image of a similar crane like system:

[img] https://omis.net/upl/medium/6-Jibs%20and...System.jpg[/img]

Hi,

If you're interested only in the vertical component, you need to:

- Multiply the impulse direction (accesible as cablejoint.Jacobian) by the impulse magnitude.
- Project it onto the gravity vector (a simple dot product).
- Use the magnitude of the projection as your new impulse magnitude.

If you're after the full force regardless of directionality, you need to:

- Project the gravity vector onto the jacobian (again a dot product, since the jacobian has unit length)
- Use the length of the projected gravity vector as your new acceleration.
Reply
#6
(19-03-2020, 09:59 AM)josemendez Wrote: Hi,

If you're interested only in the vertical component, you need to:

- Multiply the impulse direction (accesible as cablejoint.Jacobian) by the impulse magnitude.
- Project it onto the gravity vector (a simple dot product).
- Use the magnitude of the projection as your new impulse magnitude.

If you're after the full force regardless of directionality, you need to:

- Project the gravity vector onto the jacobian (again a dot product, since the jacobian has unit length)
- Use the length of the projected gravity vector as your new acceleration.


I have tried implementing the measurement of the full force, I do get some numbers but I am unsure if these are calculated correctly.

The weights I calculate are:
- All weights are negative
- When I move my crane around, slowly, the weight fluctuates alot, much more than I would have expected, sometimes the calculated weights briefly becomes 0.0. But the element we are moving is being moved very slowly, so I doubt it is because the cables starts to slack.
- The summed weight for all four cables are close to the mass of the rigid body they are suspending. Mass of rigidbody 30.000 ~ Total weight across four cables 30.800.

Current implementation:

Code:
   private void FixedUpdate()
   {
       //Last joint where the weight is attached
       CableJoint joint = cable.Joints[cable.JointCount - 1];

       //The gravitional acceleration vector
       Vector3 grav = new Vector3(0, -9.82f, 0);

       //Projecting the gravitational vector onto the Jacobian vector
       float dot = Vector3.Dot(grav, joint.Jacobian);

       //The projected gravity vector
       Vector3 gravProj = joint.Jacobian * dot;

       // Calculate force by diving Impulse with Time
       float force = joint.ImpulseMagnitude / Time.fixedDeltaTime;

       //Calculate the weight by dividing the force with the magnitude of the projected gravity vector
       weight = ((force / gravProj.magnitude));
   }
Reply
#7
(19-03-2020, 04:42 PM)MarcPilgaard Wrote: - All weights are negative

Negate the impulse magnitude. My original code simply divided by a negative scalar value, but once you deal with vectors signs become important. Keep in mind that the jacobian points from the first link to the second link in the cable. So while the impulse magnitude applied to the first link has the same sign as the jacobian (towards the second link), the impulse magnitude applied to the second link goes in the opposite direction (towards the first link).

Also you're projecting the gravity vector, then calculating its length. This is both unnecessary and can be slightly off: when you perform a dot product with a unit length vector (such as the jacobian), the result is the projection length, and it has the correct sign depending on the relative directions both vectors are pointing.

The dot product is all you really need, because the jacobian has unit length:

Code:
//Last joint where the weight is attached
CableJoint joint = cable.Joints[0];

// Calculate force by diving Impulse with Time
float force = -joint.ImpulseMagnitude / Time.fixedDeltaTime;

//Calculate the weight by dividing the force by the "amount" of gravity along the constraint gradient:
float weight = force / Vector3.Dot(Physics.gravity, joint.Jacobian);

(19-03-2020, 04:42 PM)MarcPilgaard Wrote: - When I move my crane around, slowly, the weight fluctuates alot, much more than I would have expected, sometimes the calculated weights briefly becomes 0.0. But the element we are moving is being moved very slowly, so I doubt it is because the cables starts to slack.

Can't reproduce, the mass reported is pretty stable and accurate for me. Edit: try calculating the mass in LateUpdate() instead of FixedUpdate(). FixedUpdate is called a variable amount of times each frame, might not even be called at all. Joints will always report the last impulse data they applied, so reading it in LateUpdate() is safe.

This method calculates the mass "seen" by the cable. If the cable has to pull the load to move it against gravity, the mass reported by this method will be higher than the actual mass of the load. Also, if the cable has some slack, it will report 0 mass. Also, mass can be slightly off due to non-perfect convergence. If the timestep is large or the amount of iterations not high enough, the reported mass will be higher because the cables are slightly overstretched.
Reply
#8
(20-03-2020, 12:05 PM)josemendez Wrote: Negate the impulse magnitude. My original code simply divided by a negative scalar value, but once you deal with vectors signs become important. Keep in mind that the jacobian points from the first link to the second link in the cable. So while the impulse magnitude applied to the first link has the same sign as the jacobian (towards the second link), the impulse magnitude applied to the second link goes in the opposite direction (towards the first link).

Also you're projecting the gravity vector, then calculating its length. This is both unnecessary and can be slightly off: when you perform a dot product with a unit length vector (such as the jacobian), the result is the projection length, and it has the correct sign depending on the relative directions both vectors are pointing.

The dot product is all you really need, because the jacobian has unit length:

Code:
//Last joint where the weight is attached
CableJoint joint = cable.Joints[0];

// Calculate force by diving Impulse with Time
float force = -joint.ImpulseMagnitude / Time.fixedDeltaTime;

//Calculate the weight by dividing the force by the "amount" of gravity along the constraint gradient:
float weight = force / Vector3.Dot(Physics.gravity, joint.Jacobian);


Can't reproduce, the mass reported is pretty stable and accurate for me. Edit: try calculating the mass in LateUpdate() instead of FixedUpdate(). FixedUpdate is called a variable amount of times each frame, might not even be called at all. Joints will always report the last impulse data they applied, so reading it in LateUpdate() is safe.

This method calculates the mass "seen" by the cable. If the cable has to pull the load to move it against gravity, the mass reported by this method will be higher than the actual mass of the load. Also, if the cable has some slack, it will report 0 mass. Also, mass can be slightly off due to non-perfect convergence. If the timestep is large or the amount of iterations not high enough, the reported mass will be higher because the cables are slightly overstretched.

I have been trying fiddling with it and placed the functionality in LateUpdate() but the weight still seem to fluctuate quiet a lot when the cranes are moving. We are talking about +/- 50% fluctuations from the rest position. I have found another solution that works reasonable, where I simply Lerp the weight over time which makes it less apparent.

However, if possible I would like to have it resolved anyway.

I have drawn this diagram:
[Image: QGWXwDv.png]

Each crane can move independently. The upper attachment inside the crane is used to hoist or lower the hook attached on the rigidbody. Even when moving all four cranes simultaneously and with the same speed the weight fluctuates.
Reply
#9
(23-03-2020, 12:35 PM)MarcPilgaard Wrote: I have been trying fiddling with it and placed the functionality in LateUpdate() but the weight still seem to fluctuate quiet a lot when the cranes are moving. We are talking about +/- 50% fluctuations from the rest position. I have found another solution that works reasonable, where I simply Lerp the weight over time which makes it less apparent.

However, if possible I would like to have it resolved anyway.

I have drawn this diagram:
[Image: QGWXwDv.png]

Each crane can move independently. The upper attachment inside the crane is used to hoist or lower the hook attached on the rigidbody. Even when moving all four cranes simultaneously and with the same speed the weight fluctuates.

Hi Marc,

If using more than 1 cable attached to the same object, the weight will necessarily fluctuate. How much, depends on the mass ratio (the higher, the larger the fluctuation). This is because Unity's (and hence, Filo's) solver is iterative.

This means that the equation systems that describe the scene are not all solved simultaneously. That kind of solver would be a direct solver,  much slower and expensive.

Instead, in an iterative solver each equation is solved independently and the resulting impulses immediately applied to the bodies involved. This is repeated (iterated) multiple times each step. This means each cable calculates and applies a impulse ignoring all other cables, they only react instead of predict. The more times you iterate over all equations, the closer to the ground-truth solution you get. Unfortunately, the result highly depends on the amount of iterations and specially the order in which equations were evaluated. This method is known as sequential impulses or SI.

Using more iterations (you can find this setting in the cable's solver component), and/or a smaller timestep (found in Unity's Time manager) will reduce the fluctuations at the cost of performance. Applying a low-pass filter to the reported mass values (which is essentially what lerping with the accumulated value does) should also be acceptable in most cases.
Reply