Motorized Bed Level Correction with 4 Independent Z Screws?

I've been reading through the z-probing guide to get auto bed leveling to work. So far I've made good progress, and have successfully ran a G32 using the nxn grid method, and the software correction of rotation. However I would like to use the mechanical motorized bed leveling, because as far as I understand, the software correction will correct mid print but won't actually adjust the level of the bed outside of that mid print context. However, the guide on motorized correction says it needs a bed fixed on 3 points which 2 have a motor to change the height. The bed I'm using has 4 independent z screws, each with a motor, so I don't understand if there's a way to adapt the instructions I read to my setup or not.

Comments

  • G32 is corrected during the complete print. Only bump correction measured with G33 is corrected at the bottom normally also you could set it to correct until top as well.

    4 bed motorized leveling is not supported. I never had a printer with that design and it has the problem that you can not move just one motor without the other blocking or bending the bed. The system is statically undefined so it puts extra forces on bed to bend it if you move them out of plane. Guess no problem if they are spring loaded or the bed is flexible enough to do small corrections on one edge. But problem is still that it is not implemented.
  • Is it possible to set up the mortorized leveling with your firmware as is somehow? Or do I need to wait for you to support 4 screw motorized leveling? While I'm at it, how does the software correction work exactly anyways? I read you create an approximate plane after measuring the points on the bed but what is considered a significant enough difference to warrant a correction versus what is considered level?
  • Exactly. From the measured points we compute a regression plane. For motorized 3 point leveling we then assume one motor height as reference and move the other 2 to match the theoretical horizontal level. Then you can retest and update the update, so error reduces.

    Due to bending problem this does not really work for 4 point correction. I think here it would be best to test at 4 points as close to spindle as possible, make one reference and adjust the other 3 instead. With some programming knowledge you should be able to write this:-)
  • Alright so I've started digging into the leveling code. Correct me if I'm wrong but I believe this is the section in need of editing to make it work

    void correctAutolevel(Plane &plane) {
    #if BED_CORRECTION_METHOD == 0 // rotation matrix
        //Printer::buildTransformationMatrix(plane.z(EEPROM::zProbeX1(),EEPROM::zProbeY1()),plane.z(EEPROM::zProbeX2(),EEPROM::zProbeY2()),plane.z(EEPROM::zProbeX3(),EEPROM::zProbeY3()));
    Printer::buildTransformationMatrix(plane);
    #elif BED_CORRECTION_METHOD == 1 // motorized correction
    #if !defined(NUM_MOTOR_DRIVERS) || NUM_MOTOR_DRIVERS < 2
    #error You need to define 2 motors for motorized bed correction
        Commands::waitUntilEndOfAllMoves(); // move steppers might be leveling steppers as well !
        float h1 = plane.z(BED_MOTOR_1_X,BED_MOTOR_1_Y);
        float h2 = plane.z(BED_MOTOR_2_X,BED_MOTOR_2_Y);
        float h3 = plane.z(BED_MOTOR_3_X,BED_MOTOR_3_Y);
        // h1 is reference heights, h2 => motor 0, h3 => motor 1
        h2 -= h1;
        h3 -= h1;
    #if defined(LIMIT_MOTORIZED_CORRECTION)
    if(h2 < -LIMIT_MOTORIZED_CORRECTION) h2 = -LIMIT_MOTORIZED_CORRECTION;
    if(h2 > LIMIT_MOTORIZED_CORRECTION) h2 = LIMIT_MOTORIZED_CORRECTION;
    if(h3 < -LIMIT_MOTORIZED_CORRECTION) h3 = -LIMIT_MOTORIZED_CORRECTION;
    if(h3 > LIMIT_MOTORIZED_CORRECTION) h3 = LIMIT_MOTORIZED_CORRECTION;
        MotorDriverInterface *motor2 = getMotorDriver(0);
        MotorDriverInterface *motor3 = getMotorDriver(1);
        motor2->setCurrentAs(0);
        motor3->setCurrentAs(0);
        motor2->gotoPosition(h2);
        motor3->gotoPosition(h3);
        motor2->disable();
        motor3->disable(); // now bed is even
        Printer::currentPositionSteps[Z_AXIS] = h1 * Printer::axisStepsPerMM[Z_AXIS];
    #if NONLINEAR_SYSTEM
    transformCartesianStepsToDeltaSteps(Printer::currentPositionSteps, Printer::currentNonlinearPositionSteps);
    #error Unknown bed correction method set
    }

    So I can add this line pretty easily to create the variable for the new motor

    float h4 = plane.z(BED_MOTOR_4_X,BED_MOTOR_4_Y);

    But I'm somewhat unsure where to go from there. Any tips?
  • You need to add a third motor driver in addition and also do the same with it as for the other 2.

    The problem is this assumes a flat bed and will probably not iterate correctly with 4 points since it is statically undefined. You only need 3 point to hold a plate in position. The 4th as I said might bend the plate and that causes errors in plane measurement and assigning correction values. You need a completely different approach where you measure close to the 4 plane fixtures and then correct 3 of them by using the fixed one as reference. This would result sooner or later into a perfect leveling. For faster iteration you need to add some math for better prediction, but this should already suffice.
  • I don't think I understand. To help me understand I'm including a picture of the 4 screw system we have going https://imgur.com/URd9XEA. I believe we have measures in place already to counteract the bending you're worried about. the plate we sit our bed on is quite thick so it resists most bending. the screws themselves are set up to allow some tilting to avoid bending as well. I believe as a result our bed stays pretty flat and that our main issue is making our flat bed, level. Please tell me if there's something I'm missing in that regard. As for adding the third motor driver this is the code I've made based on what's already there. I don't fully understand how this code works yet so right now I've pretty much just copy pasted the parts with the other motors and added a new motor in each section. Please let me know if I've made a mistake here.

    void correctAutolevel(Plane &plane) {
    #if BED_CORRECTION_METHOD == 0 // rotation matrix
        //Printer::buildTransformationMatrix(plane.z(EEPROM::zProbeX1(),EEPROM::zProbeY1()),plane.z(EEPROM::zProbeX2(),EEPROM::zProbeY2()),plane.z(EEPROM::zProbeX3(),EEPROM::zProbeY3()));
    Printer::buildTransformationMatrix(plane);
    #elif BED_CORRECTION_METHOD == 1 // motorized correction
    #if !defined(NUM_MOTOR_DRIVERS) || NUM_MOTOR_DRIVERS < 3
    #error You need to define 3 motors for 4 screw motorized bed correction
        Commands::waitUntilEndOfAllMoves(); // move steppers might be leveling steppers as well !
        float h1 = plane.z(BED_MOTOR_1_X,BED_MOTOR_1_Y);
        float h2 = plane.z(BED_MOTOR_2_X,BED_MOTOR_2_Y);
        float h3 = plane.z(BED_MOTOR_3_X,BED_MOTOR_3_Y);
        float h4 = plane.z(BED_MOTOR_4_X,BED_MOTOR_4_Y);
        // h1 is reference heights, h2 => motor 0, h3 => motor 1, h4 => motor 2
        h2 -= h1;
        h3 -= h1;
        h4 -= h1;
    #if defined(LIMIT_MOTORIZED_CORRECTION)
    if(h2 < -LIMIT_MOTORIZED_CORRECTION) h2 = -LIMIT_MOTORIZED_CORRECTION;
    if(h2 > LIMIT_MOTORIZED_CORRECTION) h2 = LIMIT_MOTORIZED_CORRECTION;
    if(h3 < -LIMIT_MOTORIZED_CORRECTION) h3 = -LIMIT_MOTORIZED_CORRECTION;
    if(h3 > LIMIT_MOTORIZED_CORRECTION) h3 = LIMIT_MOTORIZED_CORRECTION;
    if(h4 < -LIMIT_MOTORIZED_CORRECTION) h4 = -LIMIT_MOTORIZED_CORRECTION;
    if(h4 > LIMIT_MOTORIZED_CORRECTION) h4 = LIMIT_MOTORIZED_CORRECTION;
        MotorDriverInterface *motor2 = getMotorDriver(0);
        MotorDriverInterface *motor3 = getMotorDriver(1);
        MotorDriverInterface *motor4 = getMotorDriver(2);
        motor2->setCurrentAs(0);
        motor3->setCurrentAs(0);
        motor4->setCurrentAs(0);
        motor2->gotoPosition(h2);
        motor3->gotoPosition(h3);
        motor4->gotoPosition(h4);
        motor2->disable();
        motor3->disable();
        motor4->disable(); // now bed is even
        Printer::currentPositionSteps[Z_AXIS] = h1 * Printer::axisStepsPerMM[Z_AXIS];
    #if NONLINEAR_SYSTEM
    transformCartesianStepsToDeltaSteps(Printer::currentPositionSteps, Printer::currentNonlinearPositionSteps);
    #error Unknown bed correction method set
    }
  • Under your assumptions the solution looks correct. Just consider that BED_MOTOR_... is the position of the spindles here not where the bed is held to the underlaying frame.
  • alright. how is this code referenced? Do I need to change anything there or should I be able to replace this section of code in my firmware and reupload then run it? After enabling motorized correction of course.
  • Since you replaced the 3 fixtures solution you have to select that in configuration along with your modified version and then upload and test if it works as expected.
  • I've begun setting up the system in the firmware configurator and I wanted to check back here to make sure what I'm assuming is correct so I don't mess anything up. We're using z motor mirroring as shown in this picture
    https://imgur.com/a/sM014fY. Then on the features tab I read that I can define the motor drivers below https://imgur.com/e8WtPBL. Then am I right in assuming that for the system I've described above I should add 4 motors here https://imgur.com/OlB1h8D then define them as the pins, steps per mm, etc, for the z stepper and extruders 2, 3, and 4 as shown in the first picture?
  • Not exactly. You need 3 extra motor drivers. You assume first motor to be the master height and only correct the other 3 so only 3 positions need to be changed. You also see this in your code where you use only 3 motors for correction. Also make sure to define
    BED_MOTOR_4_X and BED_MOTOR_4_Y which are not available in config tool. So add them in manual additions.
  • I tried compiling but it seems I haven't defined my Motor Drivers properly since it's throwing me the "You need to define 3 motors for 4 screw motorized bed correction" error which used to be the "You need to define 2 motors for motorized bed correction" error. I tried searching through the code to find where the motor drivers are defined but I only got as far as finding the MotorDriverInterface Array "motorDrivers[]". So somehow the value of "NUM_MOTOR_DRIVERS" is less than 3 and I haven't yet been able to find how to fix that. Any ideas?
  • You enter them in the config tool? There you just add all 3 motors and you should end with 3 definitions. The example in github version is for 2 motors also adding there 3 is straight forward by creating 3 of them and entering them in array and set number to 3.
  • ah I got it. I had to add 3 of the z motor a second time in the features tab like you say. The code appears to be working now. When I send a g32 command it goes around and measures points in a 3x3 grid then begins adjusting the z motors, however I think I may have entered a direction pin incorrectly because it appears to be making the level of the bed worse rather than better. I'm going to troubleshoot and see if I can get it to move more how I expect.
  • Use same z invert direction as in z motor configuration that all should move same direction.
  • edited July 16
    Unfortunately I double and triple checked that they were using the same z invert settings. I'll do another test later and take a video to show how it's moving.

        In the meantime, I was reading through the leveling code but was having a hard time pulling the math involved out of the code because all the pointers and function calls involved. Would you be able to explain using variable names how you expect the math to work?

        Observing the leveling function run it seemed to measure the 9 points in the 3x3 grid I defined staring at the front left of my machine and ending in the back right. Then, while still in the back right it begins adjusting the individual z screws seemingly at the same time but maybe it's just switching very quickly. My confusion comes because for the way in which the bed is unlevel, it's adjustments are moving z screws that were out of line, further out of line rather than closer. At first I thought I may have put an invert signal in incorrectly but I'm now sure that isn't the case so I'm thinking maybe I'm pointing to the screws incorrectly for how the math is defined. For example maybe I've mistakenly told your code that my front right screw is the back left screw which I imagine would cause issues with the math. Here's the current copy of my correctAutoLevel function after adding the 4th screw definitions. Maybe you'll be able to see whatever mistake it is I'm making. I'll attach the full BedLeveling.cpp just in case but I believe I've only made changes to this one function.
    void correctAutolevel(Plane &plane) {
    #if BED_CORRECTION_METHOD == 0 // rotation matrix
        //Printer::buildTransformationMatrix(plane.z(EEPROM::zProbeX1(),EEPROM::zProbeY1()),plane.z(EEPROM::zProbeX2(),EEPROM::zProbeY2()),plane.z(EEPROM::zProbeX3(),EEPROM::zProbeY3()));
    Printer::buildTransformationMatrix(plane);
    #elif BED_CORRECTION_METHOD == 1 // motorized correction
    #if !defined(NUM_MOTOR_DRIVERS) || NUM_MOTOR_DRIVERS < 3
    #error You need to define 3 motors for 4 screw motorized bed correction
    #endif
        Commands::waitUntilEndOfAllMoves(); // move steppers might be leveling steppers as well !
        float h1 = plane.z(BED_MOTOR_1_X,BED_MOTOR_1_Y);
        float h2 = plane.z(BED_MOTOR_2_X,BED_MOTOR_2_Y);
        float h3 = plane.z(BED_MOTOR_3_X,BED_MOTOR_3_Y);
        float h4 = plane.z(BED_MOTOR_4_X,BED_MOTOR_4_Y);
        // h1 is reference heights, h2 => motor 0, h3 => motor 1, h4 => motor 2
        h2 -= h1;
        h3 -= h1;
        h4 -= h1;
    #if defined(LIMIT_MOTORIZED_CORRECTION)
    if(h2 < -LIMIT_MOTORIZED_CORRECTION) h2 = -LIMIT_MOTORIZED_CORRECTION;
    if(h2 > LIMIT_MOTORIZED_CORRECTION) h2 = LIMIT_MOTORIZED_CORRECTION;
    if(h3 < -LIMIT_MOTORIZED_CORRECTION) h3 = -LIMIT_MOTORIZED_CORRECTION;
    if(h3 > LIMIT_MOTORIZED_CORRECTION) h3 = LIMIT_MOTORIZED_CORRECTION;
    if(h4 < -LIMIT_MOTORIZED_CORRECTION) h4 = -LIMIT_MOTORIZED_CORRECTION;
    if(h4 > LIMIT_MOTORIZED_CORRECTION) h4 = LIMIT_MOTORIZED_CORRECTION;
    #endif
        MotorDriverInterface *motor2 = getMotorDriver(0);
        MotorDriverInterface *motor3 = getMotorDriver(1);
        MotorDriverInterface *motor4 = getMotorDriver(2);
        motor2->setCurrentAs(0);
        motor3->setCurrentAs(0);
        motor4->setCurrentAs(0);
        motor2->gotoPosition(h2);
        motor3->gotoPosition(h3);
        motor4->gotoPosition(h4);
        motor2->disable();
        motor3->disable();
        motor4->disable(); // now bed is even
        Printer::currentPositionSteps[Z_AXIS] = h1 * Printer::axisStepsPerMM[Z_AXIS];
    #if NONLINEAR_SYSTEM
    transformCartesianStepsToDeltaSteps(Printer::currentPositionSteps, Printer::currentNonlinearPositionSteps);
    #endif
    #else
    #error Unknown bed correction method set
    #endif
    And here's the Z-Probing section of my Configuration.h, I believe everything here is defined properly.
    // #################### Z-Probing #####################
    #define Z_PROBE_Z_OFFSET 0
    #define Z_PROBE_Z_OFFSET_MODE 0
    #define UI_BED_COATING 1
    #define FEATURE_Z_PROBE 1
    #define EXTRUDER_IS_Z_PROBE 0
    #define Z_PROBE_DISABLE_HEATERS 0
    #define Z_PROBE_BED_DISTANCE 10
    #define Z_PROBE_PIN ORIG_Z_MIN_PIN
    #define Z_PROBE_PULLUP 0
    #define Z_PROBE_ON_HIGH 1
    #define Z_PROBE_X_OFFSET -38
    #define Z_PROBE_Y_OFFSET 0
    #define Z_PROBE_WAIT_BEFORE_TEST 0
    #define Z_PROBE_SPEED 3
    #define Z_PROBE_XY_SPEED 100
    #define Z_PROBE_SWITCHING_DISTANCE 8
    #define Z_PROBE_REPETITIONS 3
    #define Z_PROBE_USE_MEDIAN 0
    #define Z_PROBE_HEIGHT 0
    #define Z_PROBE_DELAY 0
    #define Z_PROBE_START_SCRIPT ""
    #define Z_PROBE_FINISHED_SCRIPT ""
    #define Z_PROBE_RUN_AFTER_EVERY_PROBE ""
    #define Z_PROBE_REQUIRES_HEATING 0
    #define Z_PROBE_MIN_TEMPERATURE 150
    #define FEATURE_AUTOLEVEL 1
    #define FEATURE_SOFTWARE_LEVELING 0
    #define Z_PROBE_X1 25
    #define Z_PROBE_Y1 25
    #define Z_PROBE_X2 280
    #define Z_PROBE_Y2 25
    #define Z_PROBE_X3 25
    #define Z_PROBE_Y3 280
    #define BED_LEVELING_METHOD 1
    #define BED_CORRECTION_METHOD 1
    #define BED_LEVELING_GRID_SIZE 3
    #define BED_LEVELING_REPETITIONS 5
    #define BED_MOTOR_1_X -57.087
    #define BED_MOTOR_1_Y -73.47
    #define BED_MOTOR_2_X -57.087
    #define BED_MOTOR_2_Y 378.397
    #define BED_MOTOR_3_X 362.014
    #define BED_MOTOR_3_Y 378.397
    #define BED_MOTOR_4_X 362.014
    #define BED_MOTOR_4_Y -73.470
    #define BENDING_CORRECTION_A 0
    #define BENDING_CORRECTION_B 0
    #define BENDING_CORRECTION_C 0
    #define FEATURE_AXISCOMP 0
    #define AXISCOMP_TANXY 0
    #define AXISCOMP_TANYZ 0
    #define AXISCOMP_TANXZ 0
    and here's the full BedLeveling.cpp
  • It is quite easy. It first measures and calculates a regression plane through the points. Plane.z calculates the theoretical z at any xy point, but here at motor positions. Motor 1 is reference height so we subtract that height and adjust motor 2 until 4 one after the other. But the coordinates must match the driver or you will end up with wrong correction for that motor. Since you said invert is correct i guess one of the drivers is not at the assigned position.
  • I've been able to confirm the issue. I ran it twice in a row with no other changes and got these readouts in the log.
    15:58:40.216 : Info:Autoleveling disabled
    15:59:42.087 : Z-probe:9.566 X:50.00 Y:50.00
    16:00:34.559 : Z-probe:9.175 X:50.00 Y:250.00
    16:01:28.552 : Z-probe:9.546 X:250.00 Y:50.00
    16:02:21.439 : Z-probe:9.388 X:250.00 Y:250.00
    16:02:21.439 : plane: a = 0.0005 b = -0.0014 c = 9.5522
    16:02:21.923 : Printer height:299.45
    16:02:21.923 : CurrentZ:9.55 atZ:10.10
    16:02:21.923 : X:250.00 Y:250.00 Z:9.552 E:0.0000
    16:03:42.633 : Z-probe:9.365 X:50.00 Y:50.00
    16:04:34.191 : Z-probe:8.724 X:50.00 Y:250.00
    16:05:28.088 : Z-probe:9.489 X:250.00 Y:50.00
    16:06:20.320 : Z-probe:9.051 X:250.00 Y:250.00
    16:06:20.320 : plane: a = 0.0011 b = -0.0027 c = 9.3924
    16:06:21.232 : Printer height:299.29
    16:06:21.232 : CurrentZ:9.39 atZ:9.55
    16:06:21.248 : X:250.00 Y:250.00 Z:9.392 E:0.0000

    So as you can see, the first set of measurements show the bed to be pretty level with only a 0.391 max difference between z-probe values. However after it tries to correct and then measures again it becomes much less level showing a new maximum difference of 0.765. I'm unsure of why this is happening still, I believe I must have made mistake somewhere. I did also notice that it's measuring points don't match what I've set. I defined Z_PROBE_X1 and Z_PROBE_Y1 as 25 each but it's measuring the first point at X:50.00 Y:50.00 instead? I don't know why that's happening. The only changes I've made to the code I posted before we're reducing the grid size and repetitions to be as follows.

    #define BED_LEVELING_GRID_SIZE 2
    #define BED_LEVELING_REPETITIONS 1

    To make sure I have my screws defined properly I've drawn this diagram showing which of my physical screws correspond to the BED_MOTOR variables.

    https://imgur.com/7zfWGP6

    and just to be clear Z1 is BED_MOTOR_1 and so on and Z2, Z3, and Z4 are the new Motor Driver 1, 2, and 3 respectively. If any of this seems weird to you please let me know as I'm really having trouble finding my mistake here.

    To address your previous concern that 4 screw leveling wouldn't work because of bending the bed. Our rigid print bed is attached to a flexible elevator plate via flexible vibration dampeners, this allows z correction flexibility without issue. As shown in these pictures.

    https://imgur.com/aW1188C
    https://imgur.com/7tsrvbs
  • Coordinates look good and i see no error if motors are assigned as you say. You could add a print command after computing h2 till h4 to see what values get computed for them to see if that makes sense as correction. I also saw that corrected values were worse, so must be in the math. But might also be related that the math does not work with 4 motors and you need a different solution like measuring as close as you can to the 4 corners instead of using a plane.
  • I did what you suggested and got this readout.

    18:37:24.120 : Z-probe:9.249 X:50.00 Y:50.00
    18:38:11.961 : Z-probe:6.865 X:50.00 Y:250.00
    18:39:06.960 : Z-probe:10.036 X:250.00 Y:50.00
    18:39:56.908 : Z-probe:7.909 X:250.00 Y:250.00
    18:39:56.908 : plane: a = 0.0046 b = -0.0113 c = 9.5196
    18:39:56.908 : h1 = 10.0868  h2 = -5.0948  h3 = -3.1770  h4 = 1.9178
    18:40:00.767 : Printer height:299.42
    18:40:00.767 : CurrentZ:9.52 atZ:10.10
    The way I understand the system those Z-probe measurements are in the order (h1, h2, h4, h3). After seeing those values I notice their signs are backwards from what I would expect. Later in the code I see these values used in the correction like this.
        motor2->setCurrentAs(0);
        motor3->setCurrentAs(0);
        motor4->setCurrentAs(0);
        motor2->gotoPosition(h2);
        motor3->gotoPosition(h3);
        motor4->gotoPosition(h4);
    Wouldn't this, for example, use h2 in the wrong direction? Wouldn't we want that motor which has a height somewhere near 6.865 to be driven around 5.0948 in the positive direction rather than negative? Correct me if I've misunderstood but does that mean I can solve this just by changing the above code to be this?
        motor2->setCurrentAs(0);
        motor3->setCurrentAs(0);
        motor4->setCurrentAs(0);
        motor2->gotoPosition(-h2);
        motor3->gotoPosition(-h3);
        motor4->gotoPosition(-h4);
  • So I tried my suggested change and it seems to be working now. It is making it more level rather than worse. However it stops short of the level I can usually achieve manually. I found where it was breaking out of the for loop early for reaching sufficient precision and I have been adjusting those numbers lower to see if it will give me closer to the precision I'm used to. They were set to 0.00025 and my most recent iteration has them at 0.00001. 

    I also wanted to know more about the grid size, and leveling repetition values. I currently have grid size at 5 and repetitions at 10 with the thought that if it reaches level sooner than 10 repetitions it'll just break the loop. What values do you recommend for those variables. Will higher grid sizes yield better results? If I were to set grid size to 100 it would certainly take forever but would I be left with a impressively level bed? Or would be making the process longer for no real benefit? If so then at what grid size would I reach max benefit?

    How about the limit value of 0.00025. What made you reach the conclusion that that value is sufficiently level? Maybe our printer is just reading out those values of a and b for the plane incorrectly.

    Finally, my most recent test stopped after only 1 repetition despite those values of a and b not reaching the defined precision. Maybe this is because I've exceeded the a and b variable type precisions? Do you know what type of variable it uses for plane.a and plane.b? Is it a float or a double? Is there a minimum rotation it applies to the z motors that is set somewhere?
  • Grid size is nit the factor if your bed is flat. If it is not and has some bumps or bending grid size improves in finding correct regression table. Precision multiplied with width or height is the maximum difference in rotation. Since it is hardware level it will not get corrected so decide how much you can tolerate. Also at some point you will get mumeric problems and no further improvement will be possible.
Sign In or Register to comment.