Squaring the gantry in software?

After I first built my MPCNC, the middle assembly was pretty square. However, after some use, several bearings got a bit loose. I tightened them up, but now my middle assembly is no longer square. The X axis is perfectly perpendicular, but the Y axis is off 8.5mm on one end (over a distance of almost 1000mm).

As I understand it, I have a couple of options:

  1. Fiddle with the middle assembly tension bolts until it's square again. I think that would require some bearings to be very loose or some to be very tight.
  2. Force the gantry to be square before I turn on the steppers. (Or use dual endstops to automate this). However, even by hand I need a relatively large force to push the Y axis so that it stays square. The steppers can hold it in place, but then I have less torque left over and more risk of skipping steps when cutting things.
  3. Correct for the skew in the firmware or gcode. I printed a small ring of 8.5mm wide that I put on the skewed Y axis, so that I can reliably get the same skew every time.
I tried to get Marlin's XY skew correction working, but I realized that this feature of Marlin assumes a geometry that is different from an MPCNC. Even if I could get it to actually do something (which I couldn't) then it would calculate the wrong corrections for an MPCNC.

So, my plan is to write a small gcode postprocessor which corrects for my particular skew.

My question is, does this sound reasonable? Has it been done before? Or should I just go for option 1 and correct my middle assembly?



Are you sure the skew isn’t compatible? I can’t think of a reason why the machine geometry would matter. The error in X is proportional to the Y value in either case.

It is definitely a good idea to use a hard stop, and if the hard stops aren’t square, adding the skew correction in software should do the trick. You should try Jamie’s test pattern generator to see how well it works.

It should work, worst case would be to flip the numbers, but I have not looked at it in a while. I am pretty sure a mpcnc user submitted that into marlin it was around the same time we got the dual endstops in. We were both trying to correct the same issue!

I think M852 should work once it is enabled.

Software squaring is something I have been secretly working on. I’m not sure how it will end up but the start looks like this:

  • Xmin and ymin endstops go on the center assembly and trigger when they hit the motors on ths rollers. These are normally closed and work like usual homing.
  • Probe pin wired to the leftover normally open pins on the endstop switches and wired in parallel so either endstop hitting will trigger on the probe pin
  • Then it gets foggy, one option is gcode for G38.2 and M114 to extract the coordinates for xmin at y=10 and xmin at y=600 and ymin at x=10 and ymin at x=600. This can be manual at first and later maybe an octoprint plugin to calculate and issue M852
  • Another option, search the firmware to see if something like this already exists somewhere like maybe in G29. And if not implement it in the firmware.
1 Like

I thought about it some more. Marlin computes skew correction as follows (just for XY skew):

  • new_x = original_x - original_y * xy_skew_factor
  • new_y = original_y
My machine's X axis is perpendicular, but my Y axis is 8.5mm off. If I home my machine at (0,0), then both X steppers are at X=0, the left Y stepper is at Y=0, but the right Y stepper is at Y=+8.5mm.

Without skew correction, if I now tell it to move to (1000,0), then effectively my gantry will be at (1000, 8.5). Perhaps surprisingly, with the skew correction it would also end up in the same (1000, 8.5) position! That’s were I stopped thinking harder about it and concluded it’s probably not for the MPCNC.

However, if I would tell it to go to (0, 1000), then things start to differ. Without skew correction the gantry would end up at (0,1000). With skew correction it would end up at (-8.5, 1000). So in fact everything is slightly rotated, but square. I didn’t realize this before.

There is a very small issue though: everything is slightly oversized (or undersized for a negative skew factor). Going from (0,0) to (1000,0) should move the gantry 1000mm. But moving from (0,0) to (1000,8.5) moves it 1000.036mm. I can live with 0.036mm though. Other factors will probably have a larger influence.

Then I realized that Marlin’s correction should work perfectly if the Y axis is perpendicular and only the X axis is skewed. The result will not be “rotated” anymore and everything will be perfectly sized. So I will need to make sure this is the case for my machine. It’s easy though, just make the stop blocks for the Y axis equal and allow different sizes for the X axis (the reverse of what I have now).

Another way to fix this would be to implement another option for skew correction, where the correction is computed as: new_x = original_x; new_y = original_y - original_x * xy_skew_factor. But that would only complicate the configuration I think.

A last note, the firmware does not do any corrections when the corrected position would be outside of the min/max X and Y values. That may lead to some surprising behavior near the edges of the workspace. Best to stay away from them with some margin.


[attachment file=121706]

If you have this parallelogram, which one of the arrows do you pull to make it square?

There is no “ground truth” for square. Either axis can be the “right” axis.

Your machine looks kind of like this:

[attachment file=121707]

But just by changing your idea of the origin, you actually have this:

[attachment file=121708]

A skew in Y is a skew in X. You can leave the stop blocks where they are and you’ll get the same result. It will just be rotated inside the gantry (by about a degree). It will be the right size too.

1 Like

It sounds like you’re doing auto square, not just squaring. So it would measure the offset in Y between X=0 and X=max?

Do you need to do the funny wiring? Can’t you just use the same pin for the probe?

This would be pretty neat. The autolevel stuff is really confusing though. You have to be sure you’re taking account of the compensation you’ve got set while you’re measuring new compensation.

What I think I heard is the machine is skewed like this relative to the frame:

[attachment file=“out of square.jpg”]

and the skew correction only modifies the x position, so skew correction would produce this:

[attachment file=121733]

So now the axes are square to each other but rotated relative to the frame. I think the idea of putting stop blocks on the y axis is a good one, so after you are squared you are also not rotated relative to the frame.

Software rotation would be possible if G68 were implemented in Marlin but it isn’t. (Although it doesn’t sound hard.)

As for my approach, yes it’s auto-square. It would measure both gantry angles relative to the frame and calculate the angle of gantry rails to each other assuming the frame is square. I hadn’t realized M852 only corrects x, so I’d be in the same position as Peter where it could end up rotated.

It could work without the funny wiring, using the ordinary xmin/ymin channels, but I’m thinking the software will be easier with a ‘probe’ because I can corral existing functions at the highest level without having to write or modify a home-without-reset function. Although I should look – there may be coarse-grained functions that allow assembling this functionality fairly easily.

Also I would want to establish accurate absolute coordinates to prevent missing tool parking locations, and that adds some possible complexity beyond just square and un-rotated, but I will worry about that when the time comes.



1 Like

Right. The rotation to the frame is minimal though, 8.5mm/1000mm. If you need more accuracy than that, I would argue you can’t use the frame as a reference amyway. Just use the (adjusted) cnc to cut some holes along the X axis and use those to align the material.

Changing the stop blocks to the other axis is fine, but if it naturally rests where the error is in the Y, then the rollers will be constantly fighting that (not a huge deal for 0.9degrees).


Maybe even better, put stop blocks with the 8.5 mm offset so its exactly the same each time and not subject to where it just happens to rest. Then the skew correction will always be right and yeah milling a reference into the table you can get the workpiece oriented to the axes, nevermind the frame.

My situation is exactly like Jamie drew it with the blue and orange lines. We all agree that the workspace would be slightly rotated as seen from the frame, and that this small angle is nothing to worry about.

One thing that is being overlooked here is that the sides of the parallelogram do not have equal length. So it does matter which way you pull the parallelogram into a square. In my case, it’s slightly wrong if I leave the stop blocks where they are, and it’s correct if I put them on the other axis. The error in my case is only 0.036mm oversized over a distance of 1m, so also not much to worry about.

To see this, let’s take a very extreme skew of 45 degrees. If the X axis travels 1000mm and the Y axis stays where it is, the gantry travels 1000 / cos(skew_angle), which just over 1414 mm. So the length of your cut would be 1414mm instead of 1000mm.

[attachment file=121830]

There are (at least) two ways to correct the skew:

  1. Pull the upper left corner to the left. You'd want to pull it to the left and a little down actually, but Marlin only pulls it left. You end up with a rotated workspace, but also oversized in the workspace X and Y axis. The amount of oversizing is not equal for X and Y.
  2. Pull the lower right corner straight down. The workspace would not be oversized or undersized (nor rotated but who cares).
If you change your frame of reference, option 1 becomes option 2 and vice versa. If you want the behavior of option 2 with the current Marlin implementation, you have to make sure that the tube that drives the Y axis is parallel to your frame, and that the tube for the X axis is the skewed one. That's why I intend to change my stop blocks, just to get that extra 0.036mm of accuracy ;)

I know it’s fairly theoretical, but I wanted to explain it in full detail anyway, because it’s not that easy to wrap your head around.

1 Like

I think that’s a really neat idea.

I was thinking in a slightly different direction. Suppose you have a thick rectangular aluminum plate of known dimensions (or a block with aluminum tape on all sides or something which you measure with callipers). You put it on your spoil board and touch off at various points along all four sides (with a wire to the plate and another wire attached to your end mill). These measurements would allow you to find the perfect skew correction. It compensates for frame and gantry skew at the same time.

A different variation I thought about is inspired by the heated bed and PINDA probe of the Prusa i3. To measure skew, the probe finds the center of screws (or other metal, not sure) in the heated bed at known locations. It makes several passes over each know location and find the highest point which triggers the probe. This is a standard feature of the i3 firmware, but I have no idea how it works in terms of gcode etc.

You might be able to screw some round-head screws into you spoil board and use a Z probe with the same procedure. You would need to know the location of those screws very accurately though. The i3 solves that by using a PCB as the heated bed, which can be fabricated very accurately.

It’s an interesting rabbit hole to go down, but I’m not sure if I’m willing to go much deeper :slight_smile: I’ll first get a bit more hands-on experience with my MPCNC.

Ah, you’re right! Because the frame is where the drive belts are, the rotation relative to those does scale the result.

I also got the skew correction to work now in Marlin. I suspect it wasn’t doing anything before because I home to (0, Y-max) instead of (0,0). After skew correction (0, Y-max) is (-something, Y-max) which is outside of the defined bed size so the skew correction was effectively disabled at that point.

I home at (0, Y-max) because I mounted the router facing the lower right corner, ie. towards (X-max, Y-min). That way, I can just push the gantry towards Y-max and let the carriages bump into the corners. When I push the gantry towards X-min, it comes to a stop on two very small stop blocks I printed (one is 10mm, the other is 15mm, matching the skew I have in the gantry).

My procedure is now:

  1. Push the gantry to Y-max (forward) so it rests against both corners. Enable the Y steppers with M17 Y.
  2. Push the gantry to X-min (left) so it rests on the stop blocks. Enable the X steppers with M17 X.
  3. G92 X0 Y480 Z0 (set current position, my Y-max is 480)
  4. G1 X0 Y0 (go to zero position)
  5. G92 X0 Y0 Z0 (make sure Marlin agrees it's at the zero position)
If I omit the last two steps, then the skew correction does not seem to have any effect. I always move the router to where I want my origin to be (like step 4) and I always include G92 X0 Y0 Z0 as the first line in the gcode (step 5), so it would have worked already. However, when I was plotting things manually to measure the skew I did not do that and skew correction did not kick in.

I’m going to print two (equal) stop blocks to mount on the tubes at Y=0 so I can home to (0,0) directly. That would streamline the setup a little.

Btw, I’ve got the squareness dialed in now. I changed the stop blocks as I mentioned. I still have a little bit of tension in the Y axis tube, the rollers want to pull it slightly non-parallel to the frame. But the tension is too small to be a problem.

I drew crosses with a ballpoint pen and measured the diagonals between the centers of the crosses. The diagonals differ at most 0.5mm over a distance of approx 700mm. (Without skew correction is was around 4.5mm). It’s hard to make it more accurate than that using drawn crosses and a simple tape measure. But it’s good enough for me! :slight_smile: