Optical Calibration Demo and Three Hours Working on a Bug

Would setting Advanced/PositionErrorLimit to some high value to silence that alarm simplify things?

My concern was that you might have to leave it on… or at least after sending the calibration, turn it off and automatically move the sled to center, and turn it back on. I just found it easier to do it how I did it.

I’m not sure why it would be sounding the alarm and would rather know the reason behind it.

The alarm means that the PID loop did not reach its destination during one loop time.

if ((abs(leftAxis.error()) >= sysSettings.positionErrorLimit) || (abs(rightAxis.error()) >= sysSettings.positionErrorLimit))

Other calibration operations (B04 motor test, B11 ) turn this alarm off programmatically in the firmware while they run.

I have printed out the matrix to study it.

More questions.

  1. Still don’t comprehend why x values are all positive. For instance, the top set is x, and positive is to the right. Do these numbers mean that at each of the points the sled stopped at these values to the right of the desired point? If so, the smallest number that I see is 9.19 mm, and the largest is 18.97 mm. Wouldn’t an adjustment of 9.19 to the centerpoint reduce them all by 9.19?

The x center is line 8, 13.06

Same for y

Do these values all have the “12.95” added to them? (or 13.06")

Now I remember my theory (I don’t have the greatest recollection in the world). So, I had traced down the issue to what I think was motion.coordinatedMove. And I can’t fully explain it and I certainly can be wrong, but this was my theory… There’s a starting position based upon sys.xPosition, sys.yPosition and xEnd and yEnd coordinates are the gcode’s target coordinates. The distance to move is based upon those values. It divides this move into a number of steps and asks kinematics.inverse to calculate the chain length for the first step. However, it adds the calibration distance to that first small increment and produces something that it was not expecting (something larger than the sled can move within that short period of time). Therefore, it produces the “sled not keeping up” error. But, by changing the position of the motors and recalculating the geometry, it recalculates sys.xPosition, sys.yPosition and now the change is not something so big. This might not be correct but it does seem to work .
Perhaps the best way is to do the calibration adjustment in motion.coordinatedMove rather than kinematics.inverse?

Thanks, was looking at it on my phone and trying to count lines…

The process starts with the sled located at what the controller believes is 0,0 based upon the “standard model” (i.e., chain sag, rotational radius, chain-over-sprocket, etc.). However, in reality, the sled is not perfectly located over the center square… its offset by an amount. In fact, it is off by 13.06 mm horizontally and 5.45 mm vertically (value in the calibration matrix for 0,0).

The software then tells the sled to move to x,y of -1143, 533.4 (mm) which corresponds to the first square in the top left corner (3 inches in from the top and bottom). The camera software then calculates how far off the square is from the center of the sled and keeps moving the sled, small adjustments, until the camera software determines its within 0.125 mm (x and y, independently) of the center of the square. It saves how much it needed to add to the target coordinates to get the chain lengths to come out to the correct amounts so the sled is properly positioned. So in the top left corner, it needed to instruct the controller to move the sled to -1143+10.17 mm = -1132.83 mm (x coordinate), 533.4+1.28 mm = 534.68 mm (y coordinate). In the top right corner, it needed to add 9.65 mm horizontally and 9.21 mm vertically. Footnote: The difference between the two y coordinate values (top left y adjustment vs. top right y adjustment) suggest either the pattern is tilted or the frame is tilted, or a combination of the two.

If you reduced all the x-axis values by 9.19 mm and did nothing else, then you need to move the board over 9.19 mm to make up for it because 0,0 will be off by 9.19 mm horizontally. In essence, by reducing all values by that amount, you are shifting the center point horizontally. The controller takes the center point value and shift the motors virtually in the model and then, when it calculates the target coordinate corrections, it subtract that value from each of the calibration points… so the 10.17, 1.28 mm adjustments made top left corner become -2.89 mm, -4.17 mm (10.17-13.06, 1.28-5.45)

I hope I’m understanding your question…

Keep in mind this data may be affected by a motor issue. If you graph the x-error along the columns, you see a tendency of the data to zig-zag.

So, if you want to use this data, take only the odd rows or the even rows. It’ll be more consistent that way.

1 Like

One issue I am having with the curve fitting is that the constant coefficient is “large” (5-10 mm) but the coefficients for the other terms are incredibly small (E-3 to E-8). We can’t send scientific notation (as of yet) and I know we had problems in the past with sending too many digits… so not sure best way to handle this. maybe rescale the coordinates somehow.

So to clarify, the formula is:

ay^2 + bx^2 + cyx + dy + ex + f

f is the constant and is large but a-e are really small… that’s because x and y are large compared to f. If I switched x and y to be in inches then it gets better, but the coefficients still may need high accuracy to fit well:

f: 13.1416092
e: -1.01666667
d: -0.495038018
c: -0.00153397177
b: -0.0000711143695
a: -0.000215114502

I assume @bar or someone else has validated the quality of the encoders in the motor? I bet something wrong with the encoder (e.g. uneven steps, or missing steps) could cause issues like this. Let us know what you find when swapping out motors!

Are you defining your own protocol to send these to the microcontroller? Or is the problem that you’re trying to use the gcode interpreter and send the values as gcode commands? If it’s simply a parsing issue, then we could write a parser that maybe returns a fixed point representation. Or we could add a new command that scales the following inputs by a given amount (kinda like G20 and G21 for metric/imperial conversion). Something like this

C42 1000000000 ; divide following all values by 1000000000
C?? 215114 ; For example for the a coefficient
C41 ; stop dividing values

There’s a serious limit to the precision available on an Arduino. From the Arduino Reference for the data type float:

Floats have only 6-7 decimal digits of precision. That means the total number of digits, not the number to the right of the decimal point. Unlike other platforms, where you can get more precision by using a double (e.g. up to 15 digits), on the Arduino, double is the same size as float.

Is there some way to send the values back to python for the heavy lifting?

The more I look at your data I like your hypothesis better. You’re seeing x variation from row to row, which definitely seems like something that slip in the motors could cause.

I noticed my sprocket can move a little bit on its own. I assume that’s just normal pay in the gears? I wonder if a small amount of play in the gears could account for this

If I understand what you posted correctly, It’s not an issue of having enough digits, its being able to send a very small number. If we can’t use scientific notation, then we can’t send 1E-8 because that’s too many digits…

@johnboiles, the routine to send numbers from settings to the controller uses nultsandbolts.cpp to convert a string to a floating point number (it’s sent as ascii). This is the comments from the section:

    /*
Takes a string and a starting character index and returns a float if it can
be parsed from the string, it will skip leading spaces.  Does not support
scientific notation as this is officially not supported by GCode.
Code was adopted from arduino Stream::parseFloat and some from Grbl's
read_float.  It is a custom function because all arduino and c++ functions
appear to handle scientific notation or hexadecimal notation, or some other
type of numerical representation that we don't want supported.
*/

I’m rerunning it with the new motors. The old motors where not as “tight” as the new motors, but both have a little play in them. There’s some periodicity, but I have to also keep in mind that its goal is to get to within 0.125 mm of the target for each axis for each measurement and then move on. Theoretically, there could be a 0.250 mm difference between two measurements of the same square. Maybe moving left to right it tends to err in one direction and then when it moves right to left, it errs in the other direction. Maybe its also that little bit of play in the motors… The goal is to hit 0.5 mm accuracy, so if we can get there, we shouldn’t worry too much… and maybe it will all even out in the curve fitting.

But I don’t think it would be too hard to do it as a float… I use toFloat() as it is to send the calibration matrix… I made a whole routine for handling that separate from the current methodology. I was hoping that for the curve, we could just use what’s existing and add 12 firmware keys and transfer through that mechanism… but I think the use of nutsandbolts will be an issue with it. But we can work around it easy enough.

I can take a look at the parser, and see what would be easy! I haven’t looked at that code before, but doesn’t seem too hard.

I don’t think we’d want to store this as a float (because of the precision issue @blurfl mentioned). I think we would want to implement a special parser that returns a fixed-point number or something like that. Your a value above for example looks very hard to represent accurately. I’m not sure how much -0.000215114502 vs -0.000215 would make a difference.

Question about the coefficients, are there any known properties of the coefficients? E.g. do a and b always tend to be < 0 and e is < 2?

I’m worried about 0.0000002 versus 0.0000000.

If we use “settings” to send the curve, like I we do with things like distance between motors, chain sag, rotational radius, etc., then the value is stored in groundcontrol.ini as a string. If it’s saved in scientific notation, then it will be sent to the controller in scientific notation. If it’s saved as 0.0000000234 then that’s how it will be sent, but that will cause an issue as there are “too many digits”. I saw this when I was sending values computed by my Holey Maslow calibration routine where there were “too many digits” and it resulted in bad values.

Only thing I know so far is that f is significantly larger than a-e.

I think we could use a 32 bit integer to represent values with a decimal precision of 0.00000001 and a max value of 20 or so.

It (ground control) saves the numbers in decimal/scientific notation format:

calx0 = 12.234344335029972
calx1 = -0.00015717092823073762
calx2 = -0.006339241868959932
calx3 = 4.552216445820517e-08
calx4 = -3.458724843837424e-08
calx5 = 1.711739399798195e-08

Unless I’m mistaking, the arduino would just “convert them” to something like this:

calx0 = 1.22343e01
calx1 = -1.57171e-04
calx2 = -6.3392e-03
calx3 = 4.55221e-08
calx4 = -3.45872e-08
calx5 = 1.71173e-08

This should be fine if we can parse the long string with scientific notation to a float at the controller.