In the dc42 fork of the reprap firmware, there is a 5 point bed levelling routine for traditional cartesian printers. However, my bed is relatively big and warped in weird ways. I needed more points to level the bed properly. I made the following changes to version 1.13 of David Crocker’s firmware to allow 9-point bed levelling, where the 9 points are arranged like this:
^
| [P2] [P3] [P4]
|
Y [P1] [P8] [P5]
|
| [P0] [P7] [P6]
—————X————->
The coordinates need to be stated in the bed.g file in the order shown above (P0 -P8). The bed.g file will then have to contain the following lines, where xxx=x coordinate and yyy=y coordinate.
G30 P0 Xxxx Yyyy Z-99999
G30 P1 Xxxx Yyyy Z-99999
G30 P2 Xxxx Yyyy Z-99999
[…]
G30 P8 Xxxx Yyyy Z-99999 S9
A compiled version of firmware version 1.13beta1 with 9-point bed levelling can be found here (RepRapFirmware/Release/…:
RepRapFirmware-with-9-point-bed-levelling
In the move class (move.h) I changed the number of permitted corner points:
float baryXBedProbePoints[5]; // The X coordinates of the triangle corner points float baryYBedProbePoints[5]; // The Y coordinates of the triangle corner points float baryZBedProbePoints[5]; // The Z coordinates of the triangle corner points
to
float baryXBedProbePoints[9]; // The X coordinates of the triangle corner points float baryYBedProbePoints[9]; // The Y coordinates of the triangle corner points float baryZBedProbePoints[9]; // The Z coordinates of the triangle corner points
and added
private: float Triangle9Z(float x, float y) const; // Interpolate onto a triangular grid using 9 point calibration
In move.cpp I changed the BedTransform and InverseBedTransform functions. – I just added the case 9:, so that an S9 after the last G30 command in the bed.g file will use 9 points.
void Move::BedTransform(float xyzPoint[AXES]) const { switch(numBedCompensationPoints) { case 0: break; case 3: xyzPoint[Z_AXIS] = xyzPoint[Z_AXIS] + aX*xyzPoint[X_AXIS] + aY*xyzPoint[Y_AXIS] + aC; break; case 4: xyzPoint[Z_AXIS] = xyzPoint[Z_AXIS] + SecondDegreeTransformZ(xyzPoint[X_AXIS], xyzPoint[Y_AXIS]); break; case 5: xyzPoint[Z_AXIS] = xyzPoint[Z_AXIS] + TriangleZ(xyzPoint[X_AXIS], xyzPoint[Y_AXIS]); break; case 9: xyzPoint[Z_AXIS] = xyzPoint[Z_AXIS] + Triangle9Z(xyzPoint[X_AXIS], xyzPoint[Y_AXIS]); break; default: reprap.GetPlatform()->Message(GENERIC_MESSAGE, "BedTransform: wrong number of sample points."); } } // Invert the bed transform BEFORE the axis transform void Move::InverseBedTransform(float xyzPoint[AXES]) const { switch(numBedCompensationPoints) { case 0: break; case 3: xyzPoint[Z_AXIS] = xyzPoint[Z_AXIS] - (aX*xyzPoint[X_AXIS] + aY*xyzPoint[Y_AXIS] + aC); break; case 4: xyzPoint[Z_AXIS] = xyzPoint[Z_AXIS] - SecondDegreeTransformZ(xyzPoint[X_AXIS], xyzPoint[Y_AXIS]); break; case 5: xyzPoint[Z_AXIS] = xyzPoint[Z_AXIS] - TriangleZ(xyzPoint[X_AXIS], xyzPoint[Y_AXIS]); break; case 9: xyzPoint[Z_AXIS] = xyzPoint[Z_AXIS] - Triangle9Z(xyzPoint[X_AXIS], xyzPoint[Y_AXIS]); break; default: reprap.GetPlatform()->Message(GENERIC_MESSAGE, "InverseBedTransform: wrong number of sample points."); } }
and added the 9 point triangle interpolation.
float Move::Triangle9Z(float x, float y) const { for (size_t i = 0; i < 8; i++) { size_t j = (i + 1) % 8; float l1, l2, l3; BarycentricCoordinates(i, j, 8, x, y, l1, l2, l3); if (l1 > TRIANGLE_ZERO && l2 > TRIANGLE_ZERO && l3 > TRIANGLE_ZERO) { return l1 * baryZBedProbePoints + l2 * baryZBedProbePoints[j] + l3 * baryZBedProbePoints[8]; } } reprap.GetPlatform()->Message(GENERIC_MESSAGE, "Triangle interpolation: point outside all triangles!\n"); return 0.0; }
Lastly I added the Probe Bed Equation:
void Move::SetProbedBedEquation(size_t numPoints, StringRef& reply) { switch(numPoints) { case 3: /* * Transform to a plane */ { float x10 = xBedProbePoints[1] - xBedProbePoints[0]; float y10 = yBedProbePoints[1] - yBedProbePoints[0]; float z10 = zBedProbePoints[1] - zBedProbePoints[0]; float x20 = xBedProbePoints[2] - xBedProbePoints[0]; float y20 = yBedProbePoints[2] - yBedProbePoints[0]; float z20 = zBedProbePoints[2] - zBedProbePoints[0]; float a = y10 * z20 - z10 * y20; float b = z10 * x20 - x10 * z20; float c = x10 * y20 - y10 * x20; float d = -(xBedProbePoints[1] * a + yBedProbePoints[1] * b + zBedProbePoints[1] * c); aX = -a / c; aY = -b / c; aC = -d / c; } break; case 4: /* * Transform to a ruled-surface quadratic. The corner points for interpolation are indexed: * * ^ [1] [2] * | * Y * | * | [0] [3] * -----X----> * * These are the scaling factors to apply to x and y coordinates to get them into the * unit interval [0, 1]. */ xRectangle = 1.0 / (xBedProbePoints[3] - xBedProbePoints[0]); yRectangle = 1.0 / (yBedProbePoints[1] - yBedProbePoints[0]); break; case 5: for (size_t i = 0; i < 4; i++) { float x10 = xBedProbePoints[i] - xBedProbePoints[4]; float y10 = yBedProbePoints[i] - yBedProbePoints[4]; float z10 = zBedProbePoints[i] - zBedProbePoints[4]; baryXBedProbePoints[i] = xBedProbePoints[4] + 2.0 * x10; baryYBedProbePoints[i] = yBedProbePoints[4] + 2.0 * y10; baryZBedProbePoints[i] = zBedProbePoints[4] + 2.0 * z10; } baryXBedProbePoints[4] = xBedProbePoints[4]; baryYBedProbePoints[4] = yBedProbePoints[4]; baryZBedProbePoints[4] = zBedProbePoints[4]; break; case 9: for (size_t i = 0; i < 8; i++) { float x10 = xBedProbePoints[i] - xBedProbePoints[8]; float y10 = yBedProbePoints[i] - yBedProbePoints[8]; float z10 = zBedProbePoints[i] - zBedProbePoints[8]; baryXBedProbePoints[i] = xBedProbePoints[8] + 2.0 * x10; baryYBedProbePoints[i] = yBedProbePoints[8] + 2.0 * y10; baryZBedProbePoints[i] = zBedProbePoints[8] + 2.0 * z10; } baryXBedProbePoints[8] = xBedProbePoints[8]; baryYBedProbePoints[8] = yBedProbePoints[8]; baryZBedProbePoints[8] = zBedProbePoints[8]; break; default: reprap.GetPlatform()->MessageF(GENERIC_MESSAGE, "Bed calibration error: %d points provided but only 3, 4, 5 and 9 supported\n", numPoints); return; }
The bed.g file contains the following points P0-P8:
^
| [2] [3] [4]
|
Y[1] [8] [5]
|
| [0] [7] [6]
———–X———>
9 point calculation is initiated by G30 P8 Xxxx Yyyy Z-99999 S9
where xxx and yyy are the respective coordinates of P8.