Here it is. Chain-sag displacement plotted against position on the worksurface, as predicted by the catenary equation. This shows the magnitude of how far the catenary equation displaces the sled, the geometric distance the sled moves as a result of chain sag.

# Optical Calibration Demo and Three Hours Working on a Bug

**madgrizzle**#462

so ~6mm displacement at the corner? I don’t know what the true value should be, but at least this seems reasonable. What chain sag compensation values have you found the calibration routine to come up with? Correct me if I’m wrong, *ideally* the value should equal the weight of the sled.

**Joshua**#463

It hasn’t been all that good when calibrating sled weight. If I start with a sled weight of 20 lbs, I end up with a sled weight of 10.4 lbs, which is clearly incorrect. I believe this is consistent to your experience.

My hypothesis is the calibration is compensating for very small measurement differences. When I look at your original measurements for the 5 point Holey Calibration, the bottom measurement is 1 mm shorter than the top measurement. I believe the calibration reduces the sled weight by 50% to compensate for this 1 mm difference. I would like to test to confirm. If this is true, sled weight isn’t a very good candidate for calibration, because it is so sensitive.

**madgrizzle**#464

This is the latest optical calibration run with hand entered parameters (no real calibration had been done)

### Actual measured Y-Error

### Curve fit to a*x^2 + b*y^2 + c*x*y + d*x + e*y + f (please ignore the trace colors, they are inverted)

So I’m thinking a different curve might be needed as the “curve” doesn’t really match all that well… especially along the left and right edges (0 and 30 on the x axis)… It does smooth out the data at least.

The calibration near the top is pretty good (6 inches from top the accuracy is ~ 0.5 mm) but the bottom corners still have issue (~1.2 mm) and I’m think it might be more of a precision issue (friction, sled balance, etc.)

**johnboiles**#469

Why not just also just use the second chain king th over the top of the sprocket when setting vertical? That’s what I’m doing to avid the snapped chain issue

**madgrizzle**#470

Yeah, you can do it that way as well, but I had already marked my chains. Either way works.

**Joshua**#471

Agreed. It looks like the equation doesn’t have the appropriate degree of freedom to capture all the displacement curves. It is like a y*x^2 term is needed.

I think that would show up as noise: both up and down from the curve. To me, this looks like the curve equation doesn’t have sufficient degrees of freedom.

**madgrizzle**#472

I was using something somebody came up with and have absolutely no clue how to change it so it would do something different. So, now I found something else that I think I can use:

I’m just trying to figure out what the form of the equation that it is fitting and how the returned coefficients relate to the coefficients of that equation. There’s got to be a standard way this is done because nobody seems to ask that specific question…

**Joshua**#473

I don’t think a library reference is necessary. If it is confusing, you can create your own polynomial, and implement your own constants. That way there is no ambiguity.

Something like this:

def Poly(x,y,Coefficients):

c=Coefficients

return c[0]*x+c[1]*x**2+c[3]*y+c[4]*y**2+c[5]*x*y+c[6]*x**2*y

**madgrizzle**#474

It’s numpy that I have a hard time with. It’s not intuitive if you don’t understand its syntax. This is what I’m using.

```
....
dataX = np.zeros(15 * 31)
dataY = np.zeros(15 * 31)
dataZX = np.zeros(15 * 31)
dataZY = np.zeros(15 * 31)
for y in range(7, -8, -1):
for x in range(-15, 16, +1):
dataX[(7 - y) * 31 + (x + 15)] = float(x * 3.0 * 25.4)
dataY[(7 - y) * 31 + (x + 15)] = float(x * 3.0 * 25.4)
dataZX[(7 - y) * 31 + (x + 15)] = self.calErrorsX[x + 15][7 - y]
dataZY[(7 - y) * 31 + (x + 15)] = self.calErrorsY[x + 15][7 - y]
mx = self.polyFit2D(dataX, dataY, dataZX)
....
def polyfit2d(x, y, z, order=3):
ncols = (order + 1)**2
G = np.zeros((x.size, ncols))
ij = itertools.product(range(order+1), range(order+1))
for k, (i,j) in enumerate(ij):
G[:,k] = x**i * y**j
m, _, _, _ = np.linalg.lstsq(G, z)
return m
```

and I get back a bunch of coefficients. How would I put in my own polynomial in that?

**Joshua**#475

I didn’t test it, but this is how I think it should work, based on what the website describes the output of “itertools.product”.