Accounting for Motor Sprocket Induced Error When Calculating Chain Lengths

Hey Everyone,

I’m new to the Maslow project, and just beginning to get my feet wet. I have been on the waitlist for a while and ordered a unit right when the project opened up a week ago, so while the next round of kits are being assembled I figured I would get familiar with the current state of the project.

As a caveat, I am completely new to this project, so am trying to provide a fresh perspective. If I’m completely off-base please let me know!

I have been reviewing the assembly instructions, looking through the software, reviewing open and closed issues, just trying to become familiar with what the focus has been in the past and where it seems to be going. One thing caught my attention during this, regarding the calculations of the chain lengths.

For the sake of simplicity I’m going to discuss the triangular kinematics here, but the same theory applies to the quadrilateral kinematics as well.

The software, specifically looking at the Kinematics.cpp, seems to be designed around the idea that the chain length is equivalent to the distance from the motor axis to the sled centerpoint. However, there are two aspects which can introduce errors into this formula.

First, the chain can wrap around the sprocket, causing the effective length of the chain to change. Take as an example the desire to cut an arc with the left motor axis as the radius of the arc. In the current code the left motor would remain stationary while the right motor extends or retracts the right chain. But as the sled moves over this trajectory, the chain would wrap around the sprocket up to approximately 45 degrees, given a long arc or close proximity to the left motor. With the sprocket in use, I estimate this could shorten the distance from the sled to the left motor by over 1/4", causing the desired profile to be distorted. Granted, 1/4" isn’t much, but considering the goal is to be much more accurate as well as the fact that it could be corrected in software, it seems like it would be good to examine.

The second aspect I believe has less of an impact, but similarly can be corrected for in software so I figured I would mention it as well. That is that the “origin” point of the chain is not the motor axis, but instead a point on the sprocket where the chain follows a path normal to the sprocket to the sled. As the sled moves throughout the work area and the chain angle changes this point changes, adding additional error to the sled position.

I’ve been brainstorming on this topic, and feel both issues can be corrected in software without too much additional headache. Again, focusing for now on the triangular kinematics for simplicity.

Rather than calculating the chain length from the motor axis to the sled, what if we measured the chain length beginning at the centerpoint of the chain at the 12 o’clock position on the motor sprocket. With this measurement style, the chain length would be comprised of two components, assuming the sled remains in the work area:

  1. The length of the chain along the arc of the sprocket from the 12 o’clock position until where it becomes normal to the sprocket.
  2. The length of the chain along the line normal to the sprocket, beginning at the sprocket and ending at the sled centerpoint.

Value 2 is easy to calculate, as it is sqrt((length of motor axis to sled centerpoint)^2 - (effective radius of the chain centerline when wrapped around the sprocket)^2). The length of the motor axis to the sled centerpoint is the current value calculated in the kinematics formulas I believe, so the only additional information needed is the effective radius of the sprocket.

Value 1 takes a little more work. I calculated it as: (180 degrees) - (arccos((yTarget - yCordOfMotor) / (length of motor axis to sled centerpoint))) - (arccos((sprocket radius) / (length of motor axis to sled centerpoint))). This starts at the top of the sprocket, and then subtracts the angle from a vertical line to the line between the motor axis and the sled, and then subtracts the angle from the line between the motor axis and the sled and the point on the sprocket where the chain follows the normal line to the sled. This calculated arc length of the chain on the sprocket from the 12 o’clock position would then allow the value 1 calculation: 2 * 3.14159 * (radius of sprocket) * (chain arc length / 360 degrees).

These two values added together would become the calculated chain length, as it applies in the Kinematics.cpp file. However, I would anticipate calibration would still be performed to the motor axis itself.

Given this setup, I feel accuracy would be improved, particular at the corners or over large pieces. Examining the previous example again of cutting an arc with radius equal to the left motor axis, this formula would recognize the effective shortening of the left chain as the right motor extends the right chain, and would extend the left chain to compensate and maintain the desired profile more accurately.

In the triangular kinematics these calculations are easy as the sled point you are calculating against is the centerpoint of the sled. The quadrilateral would calculate to the chain mounting points on the sled instead.

I’ve been drawing it up and believe I have it all accounted for, but without having actually ever even seen a Maslow in person as well as being brand new I recognize I could be missing something substantial.

1 Like


Welcome to our group. I can’t speak to this on the software side, I will say I have not seen a post put it this way before. I will wait for the coders in here to tell me if this is accounted for or new. I like that you are thinking so deeply into the product. I’m glad you joined our community. I’ll wait to see how this unfolds.

Thank you

Welcome @rjon17469,

Thanks for the long write-up of your findings.

A 10 tooth sprocket has a circumference of 10 chain links.
A full turn of the sproket is 360 degrees so each sprocket is then 36 degrees
in other words: to move one chain link length of chain then takes 36 degrees of motor turn

If this is correct then i’m not sure what error you have experienced?
Maybe the chain jumped a tooth?
Or the calibration is off?

Or, well, i could be wrong. And that would not be the first time. :slight_smile:

so there should not be an error in that regard. As i don’t have a Maslow yet i leave this question up to the more experienced people.

Short version, this should already be taken into account in the math.

I know it is for the traditional kinematics, I haven’t had a chance to dig into
the code for the triangular kinematics, and Bar has made some comment about
having a patch to fix this, so I’m not sure if it’s in there yet or not. If not,
that would be a noticable source of error for the triangular kinematics.

the source of the chains in any case is the 12 o’clock position on the sprocket.

If you are going through the math, realize that all angles are in radians, not
degrees, and that lets them do some of this sort of thing in ways that aren’t
immediatly obvious


Thanks for the replies everyone. It has been fun to dig into the code and see how it all works.

@dlang, I have been studying the quadrilateral kinematics and you are correct, the sprocket radius is accounted for there. In the triangular kinematics, there is a rotationDiskRadius variable which is incorporated into the chain length, but I have not been able to find where this value is calculated. So far I have only found it defined as 0 in kinematics.h:

float rotationDiskRadius = 0; //distance from the bit to the attachment point

And it is also referenced in CNC_functions.h extracting the value from Ground Control:

float rotationDiskRadius = extractGcodeValue(readString, ‘Z’, -1);

I’ll admit for the latter I don’t know if it is pulling the calculated value from Ground Control, but in kinematics.h it is statically defined whereas the actual value would be variable.

Additionally, I would think radius of the sprocket would need to be accounted for in the chain length (the value 2 I described previously), but acknowledge the difference in chain length would be quite small due to the small sprocket size. I also have never seen the calibration process myself for triangular kinematics, so something could be factored in there which I am not understanding.

I’m looking forward to building mine and using it!

The ‘rotationDiskRadius’ you see refers to the distance from the end of the chain to the center of the bit on the sled, a value that can be set in GrouncControl either by hand or by the auto-calibration process. The value referred to as ‘ChainLength’, shown in red on the diagram, seems to be the value you’re referring to I think.

The calculation you mention hasn’t been integrated into the triangular kinematics code yet, see a post from Bar on the subject. I’ve poked around with the calculation, but haven’t taken time to do any meaningful testing on a live system.


Thanks for the info. The post you shared from Bar is exactly what I have been thinking. I tried searching through but apparently didn’t search hard enough. My apologies for missing that.

I developed a formula similar to the work developed by Keith Selbo that Bar referenced, but found that the math involved was quite complex.

As an alternative, could the following work to develop the chain length (in this case, the left motor):

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

float Motor1ChainArc = R * (3.14159 - acos(R/Motor1Distance) - acos((_yCordOfMotor - yTarget)/Motor1Distance));

float Chain1Length = Motor1ChainArc + sqrt(pow(Motor1Distance,2)-pow(R,2));

This is assuming the original triangular kinematics equation is calculated from the motor axis, and accounts for the arc of chain beginning at the 12 o’clock position on the sprocket as well as the difference in distance from the motor axis compared to the chain distance from the sprocket to the sled. I just ran it through some calculations and it returned what appeared to be a reasonable value to me (please excuse my bogus and randomly generated coordinates):

_xCordOfMotor = 1000
_yCordOfMotor = 1000
xTarget = -25
yTarget = 70

Motor1Distance = 1347.41
Motor1ChainArc = 7.85
Chain1Length = 1355.22

I feel this method avoids a lot of the computational complexity of deriving the full formulas and relies on calculating the geometric angles from vertical as a shortcut. Similarly, the “a” value in the document Bar referenced is calculated simply from the Motor1Distance and sprocket radius, rather than completing the much more complex formulas.

I also recognize it’s likely difficult to understand what I’m doing in the formulas, so I should draw it out. I’ll work on that too.


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