Thoughts on Chain Sag Calculation

I get the same answer as the calculator when using the inputs of 9 ft, 0.09 lb/ft, and 3.2 lb horizontal tension. The cable length is 9.024.

I put this into my holey calibration routine and after resolving 18 different bugs (math is hard), I have it running. It seems to give results like the current method. Only thing odd is that it thinks my sled weighs 11.5 lbs which it doesn’t.

1 Like

With current chain sag:

Best so far at N: 3009243, Error Magnitude: 0.931
Motor Spacing: 3581.47428, Motor Elevation: 479.74695, Top Beam Tilt: 0.0888631427684 degrees
tleftMotorX: -1792.59763173, tleftMotorY: 1086.5696
trightMotorX: 1788.87234072, trightMotorY:1092.1243
tmotorspacing: 3581.47428
Rotation Disk Radius: 132.88, Chain Sag Correction Value: 22.443462647, Left Chain:0.0632865150145, Right Chain:0.0632865150145
leftMotorX: -1792.59763173, leftMotorY: 1086.5696
rightMotorX: 1788.87234072, rightMotorY:1092.1243
LChain Error Hole 0: -0.6028 RChain Error Hole 0: -0.8675 RMS Error Hole 0: 1.0564
LChain Error Hole 1: -0.2989 RChain Error Hole 1: 0.3 RMS Error Hole 1: 0.4235
LChain Error Hole 2: 0.8597 RChain Error Hole 2: 0.729 RMS Error Hole 2: 1.1272
LChain Error Hole 3: -0.5312 RChain Error Hole 3: 0.4179 RMS Error Hole 3: 0.6759
LChain Error Hole 4: 0.5885 RChain Error Hole 4: -0.5364 RMS Error Hole 4: 0.7962
LChain Error Hole 5: 0.9908 RChain Error Hole 5: -0.1574 RMS Error Hole 5: 1.0033
LChain Error Hole 6: -0.3237 RChain Error Hole 6: -0.7412 RMS Error Hole 6: 0.8088
LChain Error Hole 7: -1.5464 RChain Error Hole 7: -0.0826 RMS Error Hole 7: 1.5486
LChain Error Hole 8: 0.3 RChain Error Hole 8: -0.1639 RMS Error Hole 8: 0.3419

With alternate chain sag:

Best so far at N: 3880627, Error Magnitude: 0.953
Motor Spacing: 3581.41866, Motor Elevation: 480.05655, Top Beam Tilt: 0.0895876365325 degrees
tleftMotorX: -1792.60372411, tleftMotorY: 1086.8566
trightMotorX: 1788.81055789, trightMotorY:1092.4565
tmotorspacing: 3581.41866
Rotation Disk Radius: 133.151, Chain Sag Correction Value: 11.415580169, Left Chain:0.0570802539334, Right Chain:0.0570802539334
leftMotorX: -1792.60372411, leftMotorY: 1086.8566
rightMotorX: 1788.81055789, rightMotorY:1092.4565
LChain Error Hole 0: -0.6072 RChain Error Hole 0: -0.9057 RMS Error Hole 0: 1.0904
LChain Error Hole 1: -0.3035 RChain Error Hole 1: 0.3167 RMS Error Hole 1: 0.4387
LChain Error Hole 2: 0.936 RChain Error Hole 2: 0.6411 RMS Error Hole 2: 1.1345
LChain Error Hole 3: -0.5746 RChain Error Hole 3: 0.5032 RMS Error Hole 3: 0.7638
LChain Error Hole 4: 0.6612 RChain Error Hole 4: -0.5591 RMS Error Hole 4: 0.8659
LChain Error Hole 5: 0.9573 RChain Error Hole 5: -0.2053 RMS Error Hole 5: 0.9791
LChain Error Hole 6: -0.3 RChain Error Hole 6: -0.7945 RMS Error Hole 6: 0.8493
LChain Error Hole 7: -1.5687 RChain Error Hole 7: -0.0751 RMS Error Hole 7: 1.5705
LChain Error Hole 8: 0.3002 RChain Error Hole 8: -0.2356 RMS Error Hole 8: 0.3816

2 Likes

I recommend you use the Levenberg Marquardt algorithm to do the optimization. It is a curve-fit, which is a little different. Once implemented, it will be much faster. There won’t be these thousands of iterations. It will converge in a few, to a few dozen iterations.

It is available in Python in a library, either scipy or numpy. I don’t remember which.

1 Like

So it converged on practically the same parameters, except chain sag is substantially different, but that’s not surprising. The end error value was 0.93 just like the current method.

This is the routine I’m using:

  leftForce = chainSagCorrection/((targetX-leftMotorX)/(leftMotorY-targetY)+(rightMotorX-targetX)/(rightMotorY-targetY))*math.sqrt(math.pow(((rightMotorX-targetX)/(rightMotorY-targetY)),2)*math.pow(((targetX-leftMotorX)/(leftMotorY-targetY)),2)+math.pow(((rightMotorX-targetX)/(rightMotorY-targetY)),2))
  rightForce =chainSagCorrection/((targetX-leftMotorX)/(leftMotorY-targetY)+(rightMotorX-targetX)/(rightMotorY-targetY))*math.sqrt(math.pow(((rightMotorX-targetX)/(rightMotorY-targetY)),2)*math.pow(((targetX-leftMotorX)/(leftMotorY-targetY)),2)+math.pow(((targetX-leftMotorX)/(leftMotorY-targetY)),2))
	hl = math.cos(leftChainAngleTarget)*leftChainStraightTarget*0.00328084
	hr = math.cos(rightChainAngleTarget)*rightChainStraightTarget*0.00328084
	vl = math.sin(leftChainAngleTarget)*leftChainStraightTarget*0.00328084
	vr = math.sin(rightChainAngleTarget)*rightChainStraightTarget*0.00328084
	al = math.cos(leftChainAngleTarget)*leftForce/0.09
	ar = math.cos(rightChainAngleTarget)*rightForce/0.09
	leftChainSag = math.sqrt( math.pow((2*al*math.sinh(hl/(2*al))),2)+vl*vl)/0.00328084
	rightChainSag = math.sqrt( math.pow((2*ar*math.sinh(hr/(2*ar))),2)+vr*vr)/0.00328084
	LChainLengthTarget = (leftChainAroundSprocketTarget + leftChainSag*leftChainTolerance)-rotationRadius
	RChainLengthTarget = (rightChainAroundSprocketTarget + rightChainSag*rightChainTolerance)-rotationRadius

The force calculation appears dimensionless (the part that divides into chainSagCorrection) so I don’t think I have to worry about units being in mm.

The only thing concerning is that chainSagCorrection, which is basically the sled weight here, is too low if it truly represents the sled weight.

Here’s a sample of the calculations:

chainSag: 30.0
targetX,Y: 0, 0
chainStraightL,R: 2095.25386998, 2095.25386998
ForcesL,R: 28.8908257068, 28.8908257068
anglesL,R: 31.554701169, 31.554701169
hl, hr: 5.85778143585, 5.85778143585
vl, vr: 3.59734930469, 3.59734930469
al, ar: 273.545078447, 273.545078447
Lchains (straight, sag, delta): 2095.25386998, 2095.28294102, 0.0290710335626
Rchains (straight, sag, delta): 2095.25386998, 2095.28294102, 0.0290710335626
finals: 1968.09884324, 1968.09884324

(finals are modified by chain wrap and rotational radius)

I compared the current calculations with the alternate calculations and it looks like, for the same chain sag parameter, the alternate method calculates substantially less sag:

–Original–
Lchains (straight, sag, delta): 2095.25386998, 2095.4733707, 0.219500713652
Rchains (straight, sag, delta): 2095.25386998, 2095.4733707, 0.219500713652
–New–
MotorSeparation: 3581.481
MotorHeight: 1087.86
chainSag: 30.0
targetX,Y: 0.0, 0.0
chainStraightL,R: 2095.25386998, 2095.25386998
ForcesL,R: 28.8908257068, 28.8908257068
anglesL,R: 31.554701169, 31.554701169
hl, hr: 5.85778143585, 5.85778143585
vl, vr: 3.59734930469, 3.59734930469
al, ar: 273.545078447, 273.545078447
Lchains (straight, sag, delta): 2095.25386998, 2095.28294102, 0.0290710335626
Rchains (straight, sag, delta): 2095.25386998, 2095.28294102, 0.0290710335626
%DifferenceL,R: -86.7558364258%, -86.7558364258%
finals: 1968.09884324, 1968.09884324

Well, I’ll try it out and see how it works when I can get back out to the shed. I’ve put it into the firmware and it will be used if you enter a negative value for chain sag.

How confident are you in the chain tension calculation? Would it be worthwhile for someone to check it?

Well, I posted the routine above with the hope someone could find something wrong with it… but keep in mind that when I say I put it in the firmware, that’s just in my customized version that I use for testing. It’s not going into general public use (though anyone has access to it off my github repo) until everyone agrees on it.

Also, I have no official relationship with @bar, et al. I’m just a community guy trying to make things better.

4 Likes

I have two theories as to why my chain sag correction factor is not precisely equal to my sled weight, which I estimate to be about 25 lbs.

Theory 1: We aren’t calculating chain sag correctly.

Theory 2: Chain sag adds a variable amount of length based upon the angle of the two chains, the lengths of the two chains, and the weight of the sled. Rotational radius subtracts a fixed amount of length whereas chain tolerance adds a variable amount of length based upon the length of the chain. I suspect that as rotational radius and chain tolerance values are adjusted, they may be compensating for a low chain sag (a reduction in error is an improvement, no matter where you get it). So maybe these values are “overcompensated” and then the chain sag gets “under-compensated” to even it out. If I don’t allow for rotational radius or chainsag to get adjusted, the chain sag correction lands at around 25 (lbs).

OK, this was really fast, but software was used to get the symbolic solutions.

This is a pretty-printed result

Here is the c-code output.
‘T_l = -(wsqrt(pow(x_l-x_t,2.0)+pow(y_l-y_t,2.0))(x_r-x_t))/(x_ly_r-x_ry_l-x_ly_t+x_ty_l+x_ry_t-x_ty_r);’
‘T_r = (wsqrt(pow(x_r-x_t,2.0)+pow(y_r-y_t,2.0))(x_l-x_t))/(x_ly_r-x_ry_l-x_ly_t+x_ty_l+x_ry_t-x_ty_r);’

It was done really quick, so there may be errors. I apologize that I didn’t use your original variable names.

My equation for force came from the spreadsheet… see here:

I copied and pasted the formula from excel to the python code and then did a search/replace to replace cells references with variable names. I think I got it right?

I did some math and it appears you get the same results…

we are not at all sure.

the spreadsheet calculations are simplified, they do not take into account the
wrapping of the chain around the sprockets.

Chain sag calculating the sled weight isn’t going to match the actual weight
because some of that weight is going to be supported by the frame (is your
reduction appropriate for a 15 degree vector correction?)

That’s a good point, but assuming simply trig, its a small reduction (sin75*25lbs=24.15lbs).

1 Like

I am thinking we should do two things. First, double-check the math. This could be as simple as checking a couple of points against the Excel file calculation. Second, we can check the sensitivity in the calibration. The calibration just might not have the resolution to separate this chain-sag from the other free variables. This a very small correction that could appear as noise within the calibration.

I analysed the chain sag and the current parobola approximation. see my analysis here and I conclude the parabola equation is good. And my catenary calculations needed successive approximation, which is coomputing intensive.
On my 11.5 ft wide top beam, I actually got around 1/64th of an inch on my maslowCNC with the parabola approximation, chain stretch (8.1m/m/N) and chain tension calculations among parameter improvements for gross results. Then a residual error correction grid for details.

2 Likes

I have implemented the full catenary equation, without the need for successive approximation. I considered using a parabola equation, but the catenary equation is not complicated, is not computationally burdensome, and it does not have 1/64 inch error. This is what the catenary equation is for. Here is my post on the calculation, Post 453 (Optical Calibration Demo and Three Hours Working on a Bug - #453 by Joshua).

Here is the equation I used for calculating chain tension, in pretty-print, along with the c-code implementation (Thoughts on Chain Sag Calculation - #62 by Joshua).

1 Like

That is great!
In my implementation, the chain sag changes the chain angle which changes the tangeant point around the sprocket thus the width of the catenary, and the horizontal sled position. So I recompute until it stabilizes.

Don’t you run into the same issue with the parabolic equation? I am guessing there is an assumption built-in that would produce the same need to iterate, that is just not as visible because the physical parameters are not as clear.

Actually, the catenary equation is an equilibrium equation where the horizontal tension is very low near the bottom corner. Trying go get closer to the corner, tension falls fast, catenary on the light side pulls more toward the center, the sled only moves part of the expected distance. So my implementation ends up iterating while the parabola equation gets a result in one simple equation.

Comparing the two (see List of Sources of errors) I could see they match close enough to me when the parabola equation is tuned. To go beyond the parametric solution, I use a correction matrix to clear the residual errors.