Accounting for Motor Sprocket Induced Error When Calculating Chain Lengths

Please do draw it up, this is a fruitful place for improvement. The formulas in K. Selbo’s writeup boil down pretty simply with a little factoring :slight_smile:, but simpler is better :smile:

1 Like

That sounds as if Bar has not applied the patch that he has to include the
sprocket radius in the triangular kinematics (it should end up being very
similar code to what’s in the traditional kinematics)

Bar has said that he has a patch for that.

1 Like

I’m pretty sure it start from the 12 o’clock position on the sprocket (just like
the standard kinematics do)

MaslowTriangularChainLength.pdf (37.0 KB)
Attached is my diagram. Please forgive the crudeness, but hopefully it helps display my process for developing the equations I gave. In the equations I combined a few steps together, but they should all line up to this document.

I was looking through K. Selbo’s formulas and realize that I made a few mistakes during my calculations. I see that they do simplify much more than what I originally thought, and I am guessing end up similar to what I came up with.

For my formulas, here is how they break down according to this diagram:

Motor1Distance = From document, similar to existing triangular kinematics formula

Motor1ChainArc = 3.14159 * R (this value is the circumference of the right half of the sprocket) - b - c

Chain1Length = Motor1ChainArc + a (the length of the straight portion of the chain to the sled)

I was going for a simple approach which could easily be integrated into the existing code without too much work. The right motor code would be similar as well.


Additionally, the formulas could be combined, giving something like this.

The current code from Kinematics.cpp:

float Chain1 = sqrt(pow((-1*_xCordOfMotor - xTarget),2)+pow((_yCordOfMotor - yTarget),2));
float Chain2 = sqrt(pow((_xCordOfMotor - xTarget),2)+pow((_yCordOfMotor - yTarget),2));

The new code with the above changes included, to replace the two original Chain1 and Chain2 calculations:

float Motor1Distance = sqrt(pow((-1*_xCordOfMotor - xTarget),2)+pow((_yCordOfMotor - yTarget),2));
float Chain1 = (R * (3.14159 - acos(R/Motor1Distance) - acos((_yCordOfMotor - yTarget)/Motor1Distance))) + sqrt(pow(Motor1Distance,2)-pow(R,2));
float Motor2Distance = sqrt(pow((_xCordOfMotor - xTarget),2)+pow((_yCordOfMotor - yTarget),2));
float Chain2 = (R * (3.14159 - acos(R/Motor2Distance) - acos((_yCordOfMotor - yTarget)/Motor2Distance))) + sqrt(pow(Motor2Distance,2)-pow(R,2));

I combined the MotorChainArc formulas directly into the Chain calculations, to avoid having to use an unnecessary couple variables. But all the math is the same as in the document.

Let me know what you guys think!


it’s good to have more people looking at the math.

I think that Bar needs to get this fix in, either through your code or with the
patch he already has. It doesn’t make sense to be testing triangulation options
without this fix in place (and it’s impressive how much better the triangulation
mode is in practice, give that this error is in place)

David Lang

P.S. could you take a good look at the thoughts I had for how to do the
calibration for triangulated mode in one step

I want someone else to go over the math (and logic) to see if it really is as
good as I think it is.

I’ve put your formulae into a copy of the firmware and re-calibrated my machine using it - it makes a difference (because it required re-calibration) and it calibrated to within half a mm over the 1905mm width in four passes, a normal result.
I moved to the southwest corner at X-1040.0 Y-465.0 and traced a 100mm clockwise square above that point with a pen in the collet. The square was about .3mm undersized in both directions, and slightly rounded in the northease and southwest corners. I ran two courses around the square and the pen traces were identical on three sides and slightly divergent because of a very slight (0.5mm) ripple on the bottom side.
I moved 1000mm up to the northwest corner at X-1040.0 Y535.0 and traced another 100mm clockwise square below that point. The square was 100mm in height but 99.5mm in width. The corners were square and two circuits around the pattern were indistinguishable. Again there was a slight ripple just discernable in the bottom edge.
I haven’t run this pattern with the standard triangular Kinematics formula, and it’s getting late and cold. Perhaps I’ll unship the chains again and re-calibrate with the standard formulas to run a comparison set of squares.

I think you’ve made a very solid contribution. Thanks for taking an interest and diving right in! Do put in a PR, I’m sure others will want to try this out too.


@Bar, I wonder whether the simulation in GC will need this update?

1 Like

@dlang, I’d be happy to review that thread and offer any insights I have. Give me a little time to review the thread and get up to speed on the math involved.

@blurfl, thank you for taking the time to test out the code! I really appreciate you uploading the code and testing it. Did the squares appear as a parallelogram at all? Based on my understanding of the triangular kinematics in use I would expect the square you drew in the northwest corner to have slightly curved top and bottom sides when drawn with the original code, and ideally with this code update the sides should all be fairly straight.

I’d be happy to put a PR in, I’ll start that now.

1 Like

I forgot to make that measurement, it was late and I was excited :blush: . I did check that the distance between the sides of the squares was constant, not tapering, but I didn’t measure the diagonals.

@blurfl your right that we should update the simulator at the same time to keep everything in sync

FYI, PR #337 is submitted. If anything looks strange please let me know! Again I don’t yet have a Maslow (order just placed), so I appreciate any feedback on silly mistakes I may have made.

1 Like

I posted some comments there, but I’ll repeat them here for more discussion:

I wonder whether Motor1Distance and Motor2Distance would be better calculated in Kinematics::recomputeGeometry()? They only change when the calibration changes and keeping the inverseKiinematics routine free from as much calculation as possible will help with speed.

Too, I wonder whether those values and Chain1 and Chain2 might be useful to the simulator? How to make them available to GC?

1 Like

I saw your reply and replied there too, but will include here for discussion as well. Thank you for reviewing!

Motor1Distance and Motor2Distance are actually the exact same as the previous triangular kinematics formulae, and represent the distance from the motor axes to the sled bit. As such, they change every time xTarget or yTarget change.

The selected variable name may not be as descriptive as it could be.

1 Like

As for making them accessible to GC, that is a bit over my head at this point. Still working through that portion of the code!

The simulator duplicates the firmware positioning code, so similar changes will
be needed there, but in python instead of C

That makes sense. I found the GC Simulator python code and updated accordingly. Should I create a separate PR for that, or incorporate into a single PR? The original PR was forked off just the Firmware, so it looks like I can’t include it in that one as-is.

1 Like

Yes please, a separate PR. You might reference the PR in firmware in it, too.

1 Like

Code updated for the GC simulator and PR created.

Firmware update: PR #337
GC simulator update: PR #492

1 Like

it’s a separate PR because it’s a separate repository

1 Like