ReprapFirmware with 17 point bed levelling. Complete overkill

9 point bed levelling was not enough apparently. As the layer height decreases for better prints, the 9-point routine described before was not good enough. The next easiest and logical step was to increase it to 17 points. This method will immediately become obsolete once the final release v1.18.1 of David Crocker’s firmware comes out, which will have grid based bed levelling. The RC I mentioned in a previous post, where I had modified DC42’s branch of the ReprapFirmware to allow 9 point bed levelling, that my bed is a bit warped. The 17 point levelling works incredibly well on my print bed! The points are arranged like this:

^
|   [P4]   [P5]  [P6]  [P7]  [P8]
|   [P3]                     [P9]
Y   [P2]        [P17]        [P10]
|   [P1]                     [P11]
|   [P0]  [P15] [P14] [P13]  [P12]
-------------------X----------------->

The coordinates need to be stated in the bed.g file in the order shown above (P0 – P16). 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 P16 Xxxx Yyyy Z-99999 S17

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[17];			// The X coordinates of the triangle corner points
float baryYBedProbePoints[17];						// The Y coordinates of the triangle corner points
float baryZBedProbePoints[17];						// The Z coordinates of the triangle corner points

and added

private:
	float Triangle9Z(float x, float y) const;		//MOPET 9 Point levelling
	float Triangle17Z(float x, float y) const;		//MOPET 17 point levelling

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 17 points.

// Do the bed transform AFTER the axis transform
void Move::BedTransform(float xyzPoint[MAX_AXES], uint32_t xAxes) const
{
	if (!useTaper || xyzPoint[Z_AXIS] < taperHeight)
	{
		float zCorrection = 0.0;
		const size_t numAxes = reprap.GetGCodes()->GetNumAxes();
		unsigned int numXAxes = 0;

		// Transform the Z coordinate based on the average correction for each axis used as an X axis.
		// We are assuming that the tool Y offsets are small enough to be ignored.
		for (uint32_t axis = 0; axis < numAxes; ++axis)
		{
			if ((xAxes & (1u << axis)) != 0)
			{
				const float xCoord = xyzPoint[axis];
				switch(numBedCompensationPoints)
				{
				case 0:
					zCorrection += grid.GetInterpolatedHeightError(xCoord, xyzPoint[Y_AXIS]);
					break;

				case 3:
					zCorrection += aX * xCoord + aY * xyzPoint[Y_AXIS] + aC;
					break;

				case 4:
					zCorrection += SecondDegreeTransformZ(xCoord, xyzPoint[Y_AXIS]);
					break;

				case 5:
					zCorrection += TriangleZ(xCoord, xyzPoint[Y_AXIS]);
					break;
				//MOPET 9 point
				case 9:
					zCorrection += Triangle9Z(xCoord, xyzPoint[Y_AXIS]);
					break;

				//MOPET 17 point
				case 17:
					zCorrection += Triangle17Z(xCoord, xyzPoint[Y_AXIS]);
					break;

				default:
					break;
				}
				++numXAxes;
			}
		}

		if (numXAxes > 1)
		{
			zCorrection /= numXAxes;			// take an average
		}

		xyzPoint[Z_AXIS] += (useTaper) ? (taperHeight - xyzPoint[Z_AXIS]) * recipTaperHeight * zCorrection : zCorrection;
	}
}
// Invert the bed transform BEFORE the axis transform
void Move::InverseBedTransform(float xyzPoint[MAX_AXES], uint32_t xAxes) const
{
	float zCorrection = 0.0;
	const size_t numAxes = reprap.GetGCodes()->GetNumAxes();
	unsigned int numXAxes = 0;

	// Transform the Z coordinate based on the average correction for each axis used as an X axis.
	// We are assuming that the tool Y offsets are small enough to be ignored.
	for (uint32_t axis = 0; axis < numAxes; ++axis)
	{
		if ((xAxes & (1u << axis)) != 0)
		{
			const float xCoord = xyzPoint[axis];
			switch(numBedCompensationPoints)
			{
			case 0:
				zCorrection += grid.GetInterpolatedHeightError(xCoord, xyzPoint[Y_AXIS]);
				break;

			case 3:
				zCorrection += aX * xCoord + aY * xyzPoint[Y_AXIS] + aC;
				break;

			case 4:
				zCorrection += SecondDegreeTransformZ(xCoord, xyzPoint[Y_AXIS]);
				break;

			case 5:
				zCorrection += TriangleZ(xCoord, xyzPoint[Y_AXIS]);
				break;
			//MOPET 9 point
			case 9:
				xyzPoint[Z_AXIS] = xyzPoint[Z_AXIS] - Triangle9Z(xyzPoint[X_AXIS], xyzPoint[Y_AXIS]);
				break;
			//MOPET 17 point
			case 17:
				xyzPoint[Z_AXIS] = xyzPoint[Z_AXIS] - Triangle17Z(xyzPoint[X_AXIS], xyzPoint[Y_AXIS]);
				break;

			default:
				break;
			}
			++numXAxes;
		}
	}

	if (numXAxes > 1)
	{
		zCorrection /= numXAxes;					// take an average
	}

	if (!useTaper || zCorrection >= taperHeight)	// need check on zCorrection to avoid possible divide by zero
	{
		xyzPoint[Z_AXIS] -= zCorrection;
	}
	else
	{
		const float zreq = (xyzPoint[Z_AXIS] - zCorrection)/(1.0 - (zCorrection * recipTaperHeight));
		if (zreq < taperHeight)
		{
			xyzPoint[Z_AXIS] = zreq;
		}
	}
}
//MOPET 9 point
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[i] + l2 * baryZBedProbePoints[j] + l3 * baryZBedProbePoints[8];
		}
	}
	reprap.GetPlatform()->Message(GENERIC_MESSAGE, "Triangle interpolation: point outside all triangles!\n");
	return 0.0;
}

//MOPET 17 point
float Move::Triangle17Z(float x, float y) const
{
	for (size_t i = 0; i < 16; i++)
	{
		size_t j = (i + 1) % 16;
		float l1, l2, l3;
		BarycentricCoordinates(i, j, 16, x, y, l1, l2, l3);
		if (l1 > TRIANGLE_ZERO && l2 > TRIANGLE_ZERO && l3 > TRIANGLE_ZERO)
		{
			return l1 * baryZBedProbePoints[i] + l2 * baryZBedProbePoints[j] + l3 * baryZBedProbePoints[16];
		}
	}
	reprap.GetPlatform()->Message(GENERIC_MESSAGE, "Triangle interpolation: point outside all triangles!\n");
	return 0.0;
}

There is a new function that checks the order of probe points. I updated this one as well to include checks for 9 and 17 points.

// Check that the probe points are in the right order
bool Move::GoodProbePointOrdering(size_t numPoints) const
{
	if (numPoints >= 2 && yBedProbePoints[1] <= yBedProbePoints[0])
	{
		return false;
	}
	if (numPoints >= 3 && numPoints <= 5 && xBedProbePoints[2] <= xBedProbePoints[1])
	{
		return false;
	}
	if (numPoints >= 4 && numPoints <= 5 && yBedProbePoints[3] >= yBedProbePoints[2])
	{
		return false;
	}
	if (numPoints >= 4 && numPoints <= 5 && xBedProbePoints[0] >= xBedProbePoints[3])
	{
		return false;
	}
	if (numPoints >= 5 && numPoints <= 8
		&& (   xBedProbePoints[4] <= xBedProbePoints[0]
			|| xBedProbePoints[4] <= xBedProbePoints[1]
			|| xBedProbePoints[4] >= xBedProbePoints[2]
			|| xBedProbePoints[4] >= xBedProbePoints[3]
			|| yBedProbePoints[4] <= yBedProbePoints[0]
			|| yBedProbePoints[4] >= yBedProbePoints[1]
			|| yBedProbePoints[4] >= yBedProbePoints[2]
			|| yBedProbePoints[4] <= yBedProbePoints[3]
		   )
	   )
	{
		return false;
	}
	//MOPET 9 point
	if (numPoints >= 9 && numPoints <= 16
		&& (   xBedProbePoints[8] <= xBedProbePoints[0]
			|| xBedProbePoints[8] <= xBedProbePoints[1]
			|| xBedProbePoints[8] <= xBedProbePoints[2]
			|| xBedProbePoints[8] >= xBedProbePoints[4]
			|| xBedProbePoints[8] >= xBedProbePoints[5]
			|| xBedProbePoints[8] >= xBedProbePoints[6]
			|| xBedProbePoints[3] >= xBedProbePoints[4]
			|| xBedProbePoints[3] <= xBedProbePoints[2]
			|| xBedProbePoints[7] >= xBedProbePoints[6]
			|| xBedProbePoints[7] <= xBedProbePoints[0]
			|| yBedProbePoints[8] <= yBedProbePoints[0]
			|| yBedProbePoints[8] >= yBedProbePoints[2]
			|| yBedProbePoints[8] >= yBedProbePoints[3]
			|| yBedProbePoints[8] >= yBedProbePoints[4]
			|| yBedProbePoints[8] <= yBedProbePoints[6]
			|| yBedProbePoints[8] <= yBedProbePoints[7]
			|| yBedProbePoints[1] >= yBedProbePoints[2]
			|| yBedProbePoints[1] <= yBedProbePoints[0]
			|| yBedProbePoints[5] >= yBedProbePoints[4]
			|| yBedProbePoints[5] <= yBedProbePoints[6]
		   )
	   )
	{
		return false;
	}
	//MOPET 17 point
	if (numPoints >= 17
		&& (   xBedProbePoints[16] <= xBedProbePoints[0]
			|| xBedProbePoints[16] <= xBedProbePoints[1]
			|| xBedProbePoints[16] <= xBedProbePoints[2]
			|| xBedProbePoints[16] <= xBedProbePoints[3]
			|| xBedProbePoints[16] <= xBedProbePoints[4]
			|| xBedProbePoints[16] >= xBedProbePoints[8]
			|| xBedProbePoints[16] >= xBedProbePoints[9]
			|| xBedProbePoints[16] >= xBedProbePoints[10]
			|| xBedProbePoints[16] >= xBedProbePoints[11]
			|| xBedProbePoints[16] >= xBedProbePoints[12]
			|| xBedProbePoints[4] >= xBedProbePoints[5]
			|| xBedProbePoints[5] >= xBedProbePoints[6]
			|| xBedProbePoints[6] >= xBedProbePoints[7]
			|| xBedProbePoints[7] >= xBedProbePoints[8]
			|| xBedProbePoints[0] >= xBedProbePoints[15]
			|| xBedProbePoints[15] >= xBedProbePoints[14]
			|| xBedProbePoints[14] >= xBedProbePoints[13]
			|| xBedProbePoints[13] >= xBedProbePoints[12]
			|| yBedProbePoints[16] <= yBedProbePoints[0]
		    || yBedProbePoints[16] <= yBedProbePoints[15]
			|| yBedProbePoints[16] <= yBedProbePoints[14]
			|| yBedProbePoints[16] <= yBedProbePoints[13]
			|| yBedProbePoints[16] <= yBedProbePoints[12]
			|| yBedProbePoints[16] >= yBedProbePoints[4]
			|| yBedProbePoints[16] >= yBedProbePoints[5]
			|| yBedProbePoints[16] >= yBedProbePoints[6]
			|| yBedProbePoints[16] >= yBedProbePoints[7]
			|| yBedProbePoints[16] >= yBedProbePoints[8]
			|| yBedProbePoints[0] >= yBedProbePoints[1]
			|| yBedProbePoints[1] >= yBedProbePoints[2]
			|| yBedProbePoints[2] >= yBedProbePoints[3]
			|| yBedProbePoints[3] >= yBedProbePoints[4]
			|| yBedProbePoints[12] >= yBedProbePoints[11]
			|| yBedProbePoints[11] >= yBedProbePoints[10]
			|| yBedProbePoints[10] >= yBedProbePoints[9]
			|| yBedProbePoints[9] >= yBedProbePoints[8]
			)
	   )
	{
		return false;
	}

	return true;
}

Lastly I added the Probe Bed Equation:

void Move::SetProbedBedEquation(size_t numPoints, StringRef& reply)
{
	if (!GoodProbePointOrdering(numPoints))
	{
		reply.printf("Error: probe points P0 to P%u must be in clockwise order starting near X=0 Y=0", min(numPoints, 4) - 1);
		if (numPoints >= 5)
		{
			reply.cat(" and P4 must be near the centre");
		}
	}
	else
	{
		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;
		//MOPET 9 point
		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;
		//MOPET 17 point
		case 17:
			for (size_t i = 0; i < 16; i++)
			{
				float x10 = xBedProbePoints[i] - xBedProbePoints[16];
				float y10 = yBedProbePoints[i] - yBedProbePoints[16];
				float z10 = zBedProbePoints[i] - zBedProbePoints[16];
				baryXBedProbePoints[i] = xBedProbePoints[16] + 2.0 * x10;
				baryYBedProbePoints[i] = yBedProbePoints[16] + 2.0 * y10;
				baryZBedProbePoints[i] = zBedProbePoints[16] + 2.0 * z10;
			}
			baryXBedProbePoints[16] = xBedProbePoints[16];
			baryYBedProbePoints[16] = yBedProbePoints[16];
			baryZBedProbePoints[16] = zBedProbePoints[16];
			break;

		default:
			reprap.GetPlatform()->MessageF(GENERIC_MESSAGE, "Bed calibration error: %d points provided but only 3, 4, 5, 9 and 17 supported\n", numPoints); //MOPET 17 point, MOPET 9 point
			return;
		}

		numBedCompensationPoints = numPoints;

		reply.copy("Bed equation fits points");
		for (size_t point = 0; point < numPoints; point++)
		{
			reply.catf(" [%.1f, %.1f, %.3f]", xBedProbePoints[point], yBedProbePoints[point], zBedProbePoints[point]);
		}
	}
	reply.cat("\n");
}

The bed.g file contains the following points P0-P16:

^
| [4]  [5]  [6]  [7]  [8]
| [3]                 [9]
Y [2]         [16]   [10]
| [1]                 [11]
| [0] [15] [14] [13] [12]
-----------X--------->

17 point calculation is initiated by G30 P8 Xxxx Yyyy Z-99999 S17
where xxx and yyy are the respective coordinates of P16.

The changes can be compiled as outlined here.

Posted in 3D Printing and tagged , , , , .

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.