MarlinFirmware / Marlin

Marlin is an optimized firmware for RepRap 3D printers based on the Arduino platform. Many commercial 3D printers come with Marlin installed. Check with your vendor if you need source code for your specific machine.
https://marlinfw.org
GNU General Public License v3.0
16.19k stars 19.21k forks source link

[FR] G29 J2 should use bilinear correction #13601

Open lutorm opened 5 years ago

lutorm commented 5 years ago

Description

Currently, G29 J2 appears to fit a plane, as after doing it, the surface at the probed points is not exactly zero. However, the only reason to use 4-point (or more) leveling rather than 3-point is that the bed isn't, in fact, a plane. It would be great if G29 J2 could instead be used to apply an overall bilinear correction (or meshed for >2) to the loaded mesh.

Steps to Reproduce

  1. Run G29 J2.
  2. The height of the probed points should be exactly zero, like they are if you use a 2x2 UBL mesh and probe it with G29 P1.

Additional Information

I used to use bilinear leveling, but the shape of my bed deviates significantly (by ~0.5mm) from a bilinear shape. To correct for this, I want to use UBL. I've probed a 7x7 grid of my bed. However, depending on exact temperature etc, the overall height of the bed and the length of the nozzle changes. For that reason, I want to use a G29 J2 to apply an overall bilinear correction to the previously probed mesh. When I do this now, it results in a significant (~0.3mm) offset towards the edges of the grid. I think this is because it fits a plane but I already know a plane is not a good description of the shape of the bed.

From glancing at the code for G29 J in ubl_G29.cpp, it appears that it doesn't even approximate the difference between the loaded mesh and the actual probed points with a plane, it turns off the leveling data, fits a plane to the probed points, and then rotates the mesh by that plane.

If we have to fit a plane, I believe a better way of doing it would be to fit the plane to the points including correction. But using bilinear should be both easier and better.

gloomyandy commented 5 years ago

I think your understanding of the G29 J code is incorrect. Although levelling is disabled the correction is applied to each G29 J probed point before computing the plane and offset (see: https://github.com/gloomyandy/Marlin/blob/myskr/Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp#L1488). So the current code does include the correction. Having said that I'm not really sure what exactly you mean by "apply an overall bilinear correction to the previously probed mesh" what calculation would you use and how do you apply it to each point in the mesh?

I find that G29 J works very well to correct any small errors in bed height or tilt. However I find that using more points (I use G29 J3) and pre-correcting the mesh helps:

  1. Use G29 P1 to probe the mesh. (Then make any needed adjustments to get a full mesh).
  2. Use G29 J3 to adjust the height/tilt of the mesh based on the J3 probe points with the bed position unchanged (so no new G28 etc.).
  3. This creates a mesh that is "pre-corrected" for the probe points used by the G29 J command points. I then fine tune this mesh using a test print.
  4. Save this corrected fine tuned mesh as the base mesh using G29 S1 Before each print use G29 L1 to load the mesh then G29 J3 to correct for any change in height or tilt.

I find that using G29 J3 provides a better overall measure of any tilt/height errors (thanks to having more points) and that by pre-adjusting the mesh to match a plane taken from those points (in the same situation as when the mesh was created) then you get a much better correction of the mesh when applying G29 J3 later.

lutorm commented 5 years ago

Yeah, you're right. I just saw the call to save_ubl_active_state_and_disable wrapping the actual action, but when I looked through the code in detail I saw that the z-correction is being applied to the probed points directly.

I'm using a TAZ6 which can only automatically probe at the corners, so I can't use J>2. But what I mean is that it would do something like this (I tried it last night and it seems to work):

        bool zig_zag = false;
        float z_probe[g29_grid_size][g29_grid_size];
        for (uint8_t ix = 0; ix < g29_grid_size; ix++) {
          const float rx = float(x_min) + ix * dx;
          for (int8_t iy = 0; iy < g29_grid_size; iy++) {
              int8_t const iiy = zig_zag ? g29_grid_size - 1- iy : iy;  
            const float ry = float(y_min) + dy * iiy;

            if (!abort_flag) {
              measured_z = probe_pt(rx, ry, parser.seen('E') ? PROBE_PT_STOW : PROBE_PT_RAISE, g29_verbose_level); // TODO: Needs error handling

              abort_flag = isnan(measured_z);
              measured_z -= get_z_correction(rx, ry) /* + zprobe_zoffset */ ;
              z_probe[ix][iiGy] = measured_z;
            }
          }

          zig_zag ^= true;
        }

        // Correct mesh based on points (todo handle j>2)
        for (uint8_t i = 0; i < GRID_MAX_POINTS_X; i++) {
            for (uint8_t j = 0; j < GRID_MAX_POINTS_Y; j++) {
                float x_tmp = mesh_index_to_xpos(i),
                    y_tmp = mesh_index_to_ypos(j);

                const float z1 = calc_z0(mesh_index_to_xpos(i),
                                         x_min, z_probe[0][0],
                                         x_max, z_probe[1][0]);
                const float z2 = calc_z0(mesh_index_to_xpos(i),
                                         x_min, z_probe[0][1],
                                         x_max, z_probe[1][1]);
                const float z0 = calc_z0(mesh_index_to_ypos(j),
                                         y_min, z1,
                                         y_max, z2);

                z_values[i][j] += z0;                
            }
        }

The main operation is a bilinear interp using the 4 probed mesh points to get a correction to the z_values at each mesh point. That way the corners will be exact.

psavva commented 5 years ago

@Roxy-3D any inputs :)

Roxy-3D commented 5 years ago

@gloomyandy 's answer is very helpful and fully accurate!

gloomyandy commented 5 years ago

Personally I'm not convinced that using that process really gives any better results than those from the existing code. Having the four corners of the bed be "exact" does not seem to be particularly useful (as most printing will take place in the centre of the bed). It is also not clear to me how you would extend that code to use other than 4 points or points that are not at each corner of the grid. For most people it will not be possible to actually probe all four extremes of the grid (due to the typical probe offsets). But if you really think this is a useful special case for 4 points I suggest that you code is as such and submit a PR for it.

Roxy-3D commented 5 years ago

Yeah, you're right. I just saw the call to save_ubl_active_state_and_disable wrapping the actual action, but when I looked through the code in detail I saw that the z-correction is being applied to the probed points directly.

On the mesh tilting (the 'J' option) : The thinking is that the user has a well tuned mesh... That means some points on the mesh are 'out of the plane'. They are either a little bit too high, or a little bit too low. So as the various points are probed that will be used to tilt the mesh, those probed values need to be corrected. That is why Z-Height correction is applied to the probed points.

lutorm commented 5 years ago

@gloomyandy "Pre-correcting" the mesh with a J-run is a good suggestion and might make the difference between planar and bilinear correction negligible. Let me try both, collect some data, and get back to you.

@Roxy-3D The issue I seem to have is that my bed has a saddle-shape and changing the temperature affects the curvature. This is not something a plane can correct for. But I'll get more data.

Roxy-3D commented 5 years ago

The issue I seem to have is that my bed has a saddle-shape and changing the temperature affects the curvature. This is not something a plane can correct for. But I'll get more data.

If the curvature changes with temperature... My suggestion would be to build (and tune) a mesh for each temperature you print at. You are correct... tilting a plane (of mesh points) is not going to be very useful in that situation.

thinkyhead commented 5 years ago

I am wondering if it makes sense to change the verbiage from "grid" to "plane" or "tilt" when referring to G29 J2 since in other contexts the word "grid" refers to the probing pattern that is used, while "plane" or "tilt" more accurately describes the operation that will be applied to the mesh.

Roxy-3D commented 5 years ago

Yeah... The 'Grid' term comes from the documentation on the Grid leveling system. 'J' is trying to provide that functionality and the old terms were used. It might make sense to switch the words... but add a sentence explaining that it is trying to provide a 'replacement' for the second generation bed leveling. (1st generation being the 3-point leveling)