The Smell of Molten Projects in the Morning

Ed Nisley's Blog: Shop notes, electronics, firmware, machinery, 3D printing, laser cuttery, and curiosities. Contents: 100% human thinking, 0% AI slop.

Category: Science

If you measure something often enough, it becomes science

  • Makergear M2: Extruder Thermistor Recalibration

    Those measurements suggested that my initial correction to the Table 1 values weren’t quite correct, but a similar correction might work as long as I didn’t change the insulating wrap. This graph includes a linear fit to the Nozzle TC data, based on the original M2 firmware’s Table 1 thermistor data and cotton insulation:

    M2 Extruder Thermistor Recal - Table 1 Insulated - Nozzle TC linear fit
    M2 Extruder Thermistor Recal – Table 1 Insulated – Nozzle TC linear fit

    Applying that equation to Table 1 produces this thermistor lookup table:

    #if (THERMISTORHEATER_0 == 8) || (THERMISTORHEATER_1 == 8) || (THERMISTORHEATER_2 == 8) || (THERMISTORBED == 8) // M2 thermistors on RAMBO
    const short temptable_8[][2] PROGMEM = {
    	{23*OVERSAMPLENR, 243},
    	{25*OVERSAMPLENR, 239},
    	{27*OVERSAMPLENR, 235},
    	{28*OVERSAMPLENR, 231},
    	{31*OVERSAMPLENR, 227},
    	{33*OVERSAMPLENR, 223},
    	{35*OVERSAMPLENR, 219},
    	{38*OVERSAMPLENR, 215},
    	{41*OVERSAMPLENR, 211},
    	{44*OVERSAMPLENR, 207},
    	{48*OVERSAMPLENR, 203},
    	{52*OVERSAMPLENR, 199},
    	{56*OVERSAMPLENR, 195},
    	{61*OVERSAMPLENR, 191},
    	{66*OVERSAMPLENR, 187},
    	{71*OVERSAMPLENR, 183},
    	{78*OVERSAMPLENR, 179},
    	{84*OVERSAMPLENR, 175},
    	{92*OVERSAMPLENR, 171},
    	{100*OVERSAMPLENR, 167},
    	{109*OVERSAMPLENR, 163},
    	{120*OVERSAMPLENR, 159},
    	{131*OVERSAMPLENR, 155},
    	{143*OVERSAMPLENR, 151},
    	{156*OVERSAMPLENR, 147},
    	{171*OVERSAMPLENR, 143},
    	{187*OVERSAMPLENR, 139},
    	{205*OVERSAMPLENR, 135},
    	{224*OVERSAMPLENR, 131},
    	{245*OVERSAMPLENR, 127},
    	{268*OVERSAMPLENR, 123},
    	{293*OVERSAMPLENR, 119},
    	{320*OVERSAMPLENR, 115},
    	{348*OVERSAMPLENR, 111},
    	{379*OVERSAMPLENR, 107},
    	{411*OVERSAMPLENR, 103},
    	{445*OVERSAMPLENR, 99},
    	{480*OVERSAMPLENR, 95},
    	{516*OVERSAMPLENR, 91},
    	{553*OVERSAMPLENR, 87},
    	{591*OVERSAMPLENR, 83},
    	{628*OVERSAMPLENR, 79},
    	{665*OVERSAMPLENR, 75},
    	{702*OVERSAMPLENR, 71},
    	{737*OVERSAMPLENR, 67},
    	{770*OVERSAMPLENR, 63},
    	{801*OVERSAMPLENR, 59},
    	{830*OVERSAMPLENR, 55},
    	{857*OVERSAMPLENR, 51},
    	{881*OVERSAMPLENR, 47},
    	{903*OVERSAMPLENR, 43},
    	{922*OVERSAMPLENR, 39},
    	{939*OVERSAMPLENR, 35},
    	{954*OVERSAMPLENR, 31},
    	{966*OVERSAMPLENR, 27},
    	{977*OVERSAMPLENR, 23},
    	{985*OVERSAMPLENR, 19},
    	{993*OVERSAMPLENR, 15},
    	{999*OVERSAMPLENR, 11},
    	{1004*OVERSAMPLENR, 7},
    	{1008*OVERSAMPLENR, 3},
    	{1011*OVERSAMPLENR, 0}
    };
    #endif
    

    I extrapolated the last entry from the previous two, because if the table doesn’t include an entry for 0 °C, then when you turn the heater off, the setpoint winds up being the lowest temperature greater than zero. Doesn’t make any difference, I think, but looks odd.

    Load that table, run the temperature up, record more data:

    M2 Extruder Thermistor Recal - slope-offset fit from Table 1
    M2 Extruder Thermistor Recal – slope-offset fit from Table 1

    The error isn’t particularly pretty, being off by +4 °C at the high end. You could hand-tweak the linear fit equation to push the error down at normal operating temperatures, but it’s close enough for my purposes.

    Although I don’t have any numbers, one benefit of tighter thermal coupling to the extruder nozzle is greatly reduced overshoot during heating.

  • Makergear M2: Hot End Temperature Profiles

    Knowing that all the thermocouples and amps and meters report more-or-less the same values, I tucked several of them around the hot end:

    M2 Hot End Temperature Profile - Insulated
    M2 Hot End Temperature Profile – Insulated

    Their IDs and placement:

    • Nozzle TM – Makergear thermistor epoxied to nozzle
    • Nozzle TC – thermocouple epoxied to nozzle
    • Heater – thermocouple at heater, under insulating sleeve
    • Sleeve – thermocouple at heater, outside insulating sleeve
    • Surface – thermocouple taped to outside of cotton insulation

    Although I intended to put the Heater thermocouple bead  on the ceramic heater itself, I have no way of knowing exactly where it was, nor whether it actually made good contact with the heater body, because it’s tucked inside the fiberglass + silicone insulating sleeve. That sleeve will, perforce, be somewhat cooler than the heater, and that will certainly affect the results.

    The sensors are stacked more-or-less radially outward from the center, which may or may not make any difference.

    The upper fan (which runs constantly) does not blow directly on the leads, but air flow over the leads does change the reported temperatures: I haven’t taken that into account, even though it’s certainly significant, but the leads and fan remain in (approximately) the same position for the tests.

    In the stock M2, the lower fan blows directly on the uninsulated hot end, the thermistor, and the nozzle; the G-Code controls when it’s turned on, so whatever effects it has are not constant. It was always off for these tests, but that’s certainly not the case while printing an object.

    The cotton insulation wrap isn’t the same as I used earlier; it was easier to use a new length of cloth than to remove the Kapton tape from the old insulation. The new insulation was slightly thicker, as well, and did a better job of reducing heat loss. I took the Insulated measurements first, then removed the cloth for the Bare measurements. Although I tried to keep the thermocouples in the same positions, I certainly nudged the wires while peeling off the cloth:

    M2 Hot End Temperature Profile - Bare
    M2 Hot End Temperature Profile – Bare

    The Marlin firmware in the M2 normally uses thermistor Table 1. I adjusted those values to create Table 8, which exactly corrected the mismatch, at least with the earlier, thinner insulation.

    After each temperature step, I waited until the temperature plot in the Pronterface graph had settled to a single pixel line for one minute. That didn’t mean the temperature was exactly at the setpoint, but it wasn’t changing very much at all, which is all I needed for this dataset.

    Keeping all that in mind…

    Graph 1 — Thermistor Table 1, bare (the as-shipped M2 configuration):

    Hot End Temperature Profile Graph - Table 1 - Bare
    Hot End Temperature Profile Graph – Table 1 – Bare

    Graph 2 — Thermistor Table 1, with insulation:

    Hot End Temperature Profile Graph - Table 1 - Insulated
    Hot End Temperature Profile Graph – Table 1 – Insulated

    Graph 3 — Thermistor Table 8, bare:

    Hot End Temperature Profile Graph - Table 8 - Bare
    Hot End Temperature Profile Graph – Table 8 – Bare

    Graph 4 — Thermistor Table 8, with insulation:

    Hot End Temperature Profile Graph - Table 8 - Insulated
    Hot End Temperature Profile Graph – Table 8 – Insulated

    The data in Graph 4 clearly show that the correction factor I used to create Table 8 doesn’t apply with a different insulation wrap around the hot end. Although the Nozzle TM and Nozzle TC lines are quite close, they aren’t the exact match I saw before.

    When you compare Graph 1 with Graph 3, then Graph 2 with Graph 4, you’ll see that the thermocouple data remains consistent: the temperature differences at a specific temperature are the same, regardless of what the Nozzle TM indicates. For example, at the upper-right corner of Graph 1, when the Nozzle TM reports 175 °C, Nozzle TC is at 145 °C and the Heater is at 124 °C (use the Y axis values): Nozzle TC is 21 °C higher than the Heater. Looking in Graph 3 to the point where the Heater is 124 °C, the Heater is once again 21 °C hotter (again, using the Y axis values).

    Although it seems odd, having the thermocouple on the bare Heater run cooler than the Nozzle TC is entirely possible, because the Heater thermocouple is in contact with the relatively thin sleeve, which is cooling the outside of the heater core. The Nozzle TC has a direct metallic + epoxy connection to the inside of the heater core, which will be hotter than its exterior surface.

    Conversely, Graph 2 shows the insulated Heater running hotter than the Nozzle TM. That also makes sense: with less heat loss through the Sleeve, the exterior of the heater gets hotter than the threaded brass cylinder in the middle, which is losing heat at both ends.

    Those correlations suggest the various thermocouples do indicate the actual temperatures and the nozzle thermistor doesn’t.

    I believe bonding the thermistor to the nozzle with epoxy doesn’t affect that conclusion. It does make the results less subject to random changes due to the thermistor bead’s exact position and contact with the nozzle, though, and certainly makes the temperatures I record quite different from those found in other M2 hot ends. The fundamental rule here is that when you want to measure the temperature of something, the probe must make solid contact with the something, not dangle in mid-air somewhere nearby.

    Based on some earlier (and rather crude) measurements, I proposed that the thermistor was gaining heat through its leads, because they pass over the heater core. That’s definitely not true, as the Nozzle TM and Nozzle TC have the same temperature difference between the bare and insulated cases: compare Graph 1 with Graph 2, then Graph 3 with Graph 4. If the thermistor gained heat, it would be relatively hotter than the thermocouple in the case with additional insulation, because the Heater would then run hotter and pipe more heat into the thermistor leads.

    That’s why you make measurements…

    Along those lines, I’ve asked several people I trust to measure their M2 hot ends [You know who you are. Thanks!] and the results are unequivocal: nobody sees any significant variation between the thermistor and a thermocouple tucked beside it. The only difference in the setups seems to be the solid connection between my sensors and the nozzle. I can’t explain it, either, and I’ve shot down several of my own proposals.

  • Thermocouple Ensemble Comparison

    Although I don’t have a good way to put thermocouples in a known temperature environment (ie, yes, I can freeze and boil water, but I doubt the trustworthiness of any measurements made therein), I can compare the results from several different thermocouples held at the same (unknown) temperature: if they all agree to within a reasonable tolerance, I assume that they’re all reporting the correct temperature. Even better, if some of the temperatures come from different thermocouple amplifiers, then the electronics cross-check each other.

    With that in mind, I attached a 4 Ω 25 W aluminum-body power resistor to the back of the same isothermal block I built for the Thing-O-Matic extruder tests, atop a dab of heatsink compound for good measure:

    Isothermal block with 25 W power resistor
    Isothermal block with 25 W power resistor

    Then I tucked four sensors into the drilled holes:

    Sensors in heated aluminum block
    Sensors in heated aluminum block

    Clockwise from the front left corner:

    • T1: Fluke meter, eBay TC (black sheath)
    • T2: Fluke meter, Fluke TC (brown sheath)
    • TM5: TC4+Arduino, eBay 100 kΩ thermistor (invisibly fine bare wire leads)
    • TC1: TC4+Arduino, eBay TC (black sheath)

    The TC4 Shield handles both thermocouple and thermistor sensors, so I added a thermistor from my collection just to see how it worked. The datasheet gives these parameters:

    • 100 kΩ resistance at 25 °C
    • beta = 3950 (from 25 °C to 50 °C)
    • beta = 3990 (from 25 °C to 85 °C)

    Unfortunately, there’s no way to include the eBay thermocouple epoxied to the nozzle in this test, but it’s from the same lot as the two in this test, so I assume it’ll produce the same results. The consistency in earlier tests suggests they’re all really Type K thermocouples and produce the same results as the Fluke thermocouple and meter that I assume produces accurate readings.

    The closed-cell foam insulating the block from the vice jaws seemed like a good idea at the time.

    I connected the resistor to the bench power supply, channeled the true spirit of DIY 3D printing (ie, ignored the power derating curves), and fired it up:

    Multiple Sensor Calibration - vs time
    Multiple Sensor Calibration – vs time

    The successive steps correspond to power levels of 0 W (the ambient temperature), 1 W, 2 W, 3W, 4W, and 6 W. The last point established that the foam melts at slightly over 100 °C, whereupon the test terminated. Eyeballometrically, the time constant of the resistor + block is on the order of 20 minutes, so these few points represent a rather tedious Basement Laboratory session.

    Plotting the temperatures against the reading for T2, the Fluke thermocouple connected to the Fluke meter, and thinning the lines makes the results a bit more obvious:

    Multiple Sensor Calibration - vs T2
    Multiple Sensor Calibration – vs T2

    The three thermocouples and Fluke meter / TC4 Shield pretty much overlay each other, with the thermistor reporting a somewhat higher temperature. Given that TM5 is an eBay thermistor, I’ll let you judge whether the beta value I got from its listing matches the beta of the actual thermistor.

    In any event, I’d say the thermocouples report a temperature within at most a degree or two of the actual temperature. Plus, I didn’t get a steam burn in the process…

  • Makergear M2: Thermistor Tables

    The Marlin firmware used by the M2 has these thermistor selections in Configuration.h:

    //// Temperature sensor settings:
    // -2 is thermocouple with MAX6675 (only for sensor 0)
    // -1 is thermocouple with AD595
    // 0 is not used
    // 1 is 100k thermistor
    // 2 is 200k thermistor
    // 3 is mendel-parts thermistor
    // 4 is 10k thermistor !! do not use it for a hotend. It gives bad resolution at high temp. !!
    // 5 is ParCan supplied 104GT-2 100K
    // 6 is EPCOS 100k
    // 7 is 100k Honeywell thermistor 135-104LAG-J01
    
    #define TEMP_SENSOR_0 1
    #define TEMP_SENSOR_1 1
    #define TEMP_SENSOR_2 0
    #define TEMP_SENSOR_BED 1
    

    The first table in thermistortables.h looks like this:

    #define OVERSAMPLENR 16
    
    #if (THERMISTORHEATER_0 == 1) || (THERMISTORHEATER_1 == 1)  || (THERMISTORHEATER_2 == 1) || (THERMISTORBED == 1) //100k bed thermistor
    
    const short temptable_1[][2] PROGMEM = {
    {       23*OVERSAMPLENR ,       300     },
    {       25*OVERSAMPLENR ,       295     },
    {       27*OVERSAMPLENR ,       290     },
    {       28*OVERSAMPLENR ,       285     },
    {       31*OVERSAMPLENR ,       280     },
    {       33*OVERSAMPLENR ,       275     },
    {       35*OVERSAMPLENR ,       270     },
    {       38*OVERSAMPLENR ,       265     },
    {       41*OVERSAMPLENR ,       260     },
    {       44*OVERSAMPLENR ,       255     },
    {       48*OVERSAMPLENR ,       250     },
    {       52*OVERSAMPLENR ,       245     },
    {       56*OVERSAMPLENR ,       240     },
    {       61*OVERSAMPLENR ,       235     },
    {       66*OVERSAMPLENR ,       230     },
    {       71*OVERSAMPLENR ,       225     },
    {       78*OVERSAMPLENR ,       220     },
    {       84*OVERSAMPLENR ,       215     },
    {       92*OVERSAMPLENR ,       210     },
    {       100*OVERSAMPLENR        ,       205     },
    {       109*OVERSAMPLENR        ,       200     },
    {       120*OVERSAMPLENR        ,       195     },
    {       131*OVERSAMPLENR        ,       190     },
    {       143*OVERSAMPLENR        ,       185     },
    {       156*OVERSAMPLENR        ,       180     },
    {       171*OVERSAMPLENR        ,       175     },
    {       187*OVERSAMPLENR        ,       170     },
    {       205*OVERSAMPLENR        ,       165     },
    {       224*OVERSAMPLENR        ,       160     },
    {       245*OVERSAMPLENR        ,       155     },
    {       268*OVERSAMPLENR        ,       150     },
    {       293*OVERSAMPLENR        ,       145     },
    {       320*OVERSAMPLENR        ,       140     },
    {       348*OVERSAMPLENR        ,       135     },
    {       379*OVERSAMPLENR        ,       130     },
    {       411*OVERSAMPLENR        ,       125     },
    {       445*OVERSAMPLENR        ,       120     },
    {       480*OVERSAMPLENR        ,       115     },
    {       516*OVERSAMPLENR        ,       110     },
    {       553*OVERSAMPLENR        ,       105     },
    {       591*OVERSAMPLENR        ,       100     },
    {       628*OVERSAMPLENR        ,       95      },
    {       665*OVERSAMPLENR        ,       90      },
    {       702*OVERSAMPLENR        ,       85      },
    {       737*OVERSAMPLENR        ,       80      },
    {       770*OVERSAMPLENR        ,       75      },
    {       801*OVERSAMPLENR        ,       70      },
    {       830*OVERSAMPLENR        ,       65      },
    {       857*OVERSAMPLENR        ,       60      },
    {       881*OVERSAMPLENR        ,       55      },
    {       903*OVERSAMPLENR        ,       50      },
    {       922*OVERSAMPLENR        ,       45      },
    {       939*OVERSAMPLENR        ,       40      },
    {       954*OVERSAMPLENR        ,       35      },
    {       966*OVERSAMPLENR        ,       30      },
    {       977*OVERSAMPLENR        ,       25      },
    {       985*OVERSAMPLENR        ,       20      },
    {       993*OVERSAMPLENR        ,       15      },
    {       999*OVERSAMPLENR        ,       10      },
    {       1004*OVERSAMPLENR       ,       5       },
    {       1008*OVERSAMPLENR       ,       0       } //safety
    };
    #endif
    

    The OVERSAMPLENR constant determines the number of successive ADC samples added together into a single value, which is then used to search the table for the corresponding entry. The table entries are pairs of:
    {nominal ADC value * number of samples, temperature in C}
    which means that if we know the temperature, we can work backwards to find the ADC value and then compute the actual thermistor resistance.

    However, before doing that, I created a modified version of the thermistor table that simply scales the temperatures down by 0.878:

    #if (THERMISTORHEATER_0 == 8) || (THERMISTORHEATER_1 == 8) || (THERMISTORHEATER_2 == 8) || (THERMISTORBED == 8) // M2 thermistors on RAMBO
    const short temptable_8[][2] PROGMEM = {
    	{23*OVERSAMPLENR, 263.51},
    	{25*OVERSAMPLENR, 259.12},
    	{27*OVERSAMPLENR, 254.73},
    	{28*OVERSAMPLENR, 250.34},
    	{31*OVERSAMPLENR, 245.94},
    	{33*OVERSAMPLENR, 241.55},
    	{35*OVERSAMPLENR, 237.16},
    	{38*OVERSAMPLENR, 232.77},
    	{41*OVERSAMPLENR, 228.38},
    	{44*OVERSAMPLENR, 223.98},
    	{48*OVERSAMPLENR, 219.59},
    	{52*OVERSAMPLENR, 215.2},
    	{56*OVERSAMPLENR, 210.81},
    	{61*OVERSAMPLENR, 206.42},
    	{66*OVERSAMPLENR, 202.03},
    	{71*OVERSAMPLENR, 197.63},
    	{78*OVERSAMPLENR, 193.24},
    	{84*OVERSAMPLENR, 188.85},
    	{92*OVERSAMPLENR, 184.46},
    	{100*OVERSAMPLENR, 180.07},
    	{109*OVERSAMPLENR, 175.67},
    	{120*OVERSAMPLENR, 171.28},
    	{131*OVERSAMPLENR, 166.89},
    	{143*OVERSAMPLENR, 162.5},
    	{156*OVERSAMPLENR, 158.11},
    	{171*OVERSAMPLENR, 153.71},
    	{187*OVERSAMPLENR, 149.32},
    	{205*OVERSAMPLENR, 144.93},
    	{224*OVERSAMPLENR, 140.54},
    	{245*OVERSAMPLENR, 136.15},
    	{268*OVERSAMPLENR, 131.76},
    	{293*OVERSAMPLENR, 127.36},
    	{320*OVERSAMPLENR, 122.97},
    	{348*OVERSAMPLENR, 118.58},
    	{379*OVERSAMPLENR, 114.19},
    	{411*OVERSAMPLENR, 109.8},
    	{445*OVERSAMPLENR, 105.4},
    	{480*OVERSAMPLENR, 101.01},
    	{516*OVERSAMPLENR, 96.62},
    	{553*OVERSAMPLENR, 92.23},
    	{591*OVERSAMPLENR, 87.84},
    	{628*OVERSAMPLENR, 83.45},
    	{665*OVERSAMPLENR, 79.05},
    	{702*OVERSAMPLENR, 74.66},
    	{737*OVERSAMPLENR, 70.27},
    	{770*OVERSAMPLENR, 65.88},
    	{801*OVERSAMPLENR, 61.49},
    	{830*OVERSAMPLENR, 57.09},
    	{857*OVERSAMPLENR, 52.7},
    	{881*OVERSAMPLENR, 48.31},
    	{903*OVERSAMPLENR, 43.92},
    	{922*OVERSAMPLENR, 39.53},
    	{939*OVERSAMPLENR, 35.13},
    	{954*OVERSAMPLENR, 30.74},
    	{966*OVERSAMPLENR, 26.35},
    	{977*OVERSAMPLENR, 21.96},
    	{985*OVERSAMPLENR, 17.57},
    	{993*OVERSAMPLENR, 13.18},
    	{999*OVERSAMPLENR, 8.78},
    	{1004*OVERSAMPLENR, 4.39},
    	{1008*OVERSAMPLENR, 0}
    };
    #endif
    

    I inserted that table, changed the thermistor selection, reloaded the firmware, and ran the same test as before, which produced this result:

    Rescaled extruder thermocouple
    Rescaled extruder thermocouple

    The stock thermistor and the thermocouple now report essentially the same values, which is entirely due to the new table. The two additional lines come from two more thermocouples taped to the nozzle and dangling downward toward the platform:

    M2 - Hot end with additional thermocouples
    M2 – Hot end with additional thermocouples

    Given that I simply taped those thermistors in place, they don’t contact the nozzle nearly as well as the epoxied sensors. The fact that one reads a bit higher and the other much lower could be explained by handwaving, but one possibility is that the various thermocouples don’t quite agree with each other.

    Time for some calibration along those lines, methinks…

  • Makergear M2: Thermistor vs. Thermocouple

    With the stock thermistor and my added thermocouple epoxied to the M2’s nozzle, I stepped the temperature upward, let it settle, and recorded the temperature from the Pronterface status display and my Fluke thermocouple meter:

    First Heat - M2 thermistor - Fluke with thermocouple
    First Heat – M2 thermistor – Fluke with thermocouple

    Because the firmware servos the temperature through the stock thermistor, that line is dead straight at the exact setpoint values: the reference never disagrees with itself. The thermocouple, however, reads low by about 12%: according to it, the nozzle runs much cooler than the thermistor value.

    Huh?

    Several explanations come to mind:

    • The firmware is using a lookup table that doesn’t match the thermistor
    • The Fluke thermocouple meter reports the wrong value
    • The thermocouple junction is defective
    • Despite the epoxy blob, the two sensors aren’t at the same temperature
    • Something else is kaflooie

    This obviously calls for more data…

  • M2 vs. Marlin: Acceleration

    Three firmware constants (seem to) control the acceleration applied to each axis, presented here in their original form:

    #define DEFAULT_MAX_ACCELERATION      {9000,9000,30,10000}    // X, Y, Z, E maximum start speed for accelerated moves. E default values are good for skeinforge 40+, for older versions raise them a lot.
    #define DEFAULT_ACCELERATION          3000    // X, Y, Z and E max acceleration in mm/s^2 for printing moves
    #define DEFAULT_RETRACT_ACCELERATION  3000   // X, Y, Z and E max acceleration in mm/s^2 for r retracts
    

    I do not pretend to understand their interaction and, pursuant to that discussion, some tests and measurements seem to be the only way to find out what’s happening. However, estimating some masses and guesstimating motor performance can put some boundaries on the problem.

    Based on weighing a slightly larger stepper motor and the hot end, the complete extruder assembly may weigh (OK, mass) about 600 grams, which I’ll round up to 1 kg to cover bending the filament guide tube and friction. In order to accelerate the extruder carriage at 9000 mm/s^2, the X axis stepper motor force must be:

    F = ma = 1 kg * 9 m/s2 = 9 N

    The pulley drives 36 mm of belt per revolution, so the effective diameter = 11.5 mm and the radius = 5.7 mm. That means the motor torque will be:

    50 mN·m = 9 N * 5.7 mm

    I don’t have specs for the Makergear motors, but similar motors have pull-in torques in the 200 to 300 mN·m range, which is flat out to 1000 (full)step/s and decreases to zilch as the speed approaches 10000 (full)step/s. Because the motors run in 1/16 microstep mode, Marlin’s peak 40000 (micro)step/s rate works out to 2500 (full)step/s, where the motor pull-in torque is maybe half of the maximum.

    The motor pull-out torque falls off to nothing around 1000 (full)step/s = 16000 (micro)step/s, which suggests you can’t slow the stages down nearly as fast as you speed them up, at least beyond about 10000 (micro)step/s.

    All of that depends on the motor current, of course, and that depends on the amount of heat you’re willing to generate in the motors. I really must build a better dynamometer.

    Assuming the Y stage + heater + glass weighs 4 pounds = 2 kg, the numbers are twice as large, but they’re in the same ballpark.

    I tried a few values for the Z acceleration and settled on something slightly larger, but that won’t apply to a different motor.

    I have no way to estimate the force required to drive the filament, but the gearbox multiplies the motor torque by a factor of 5, so there’s a speed-torque tradeoff lying in wait.

    I modified the acceleration constants to bring the overall limits in line with the per-axis values:

    #define DEFAULT_MAX_ACCELERATION      {9000,9000,75,10000}
    #define DEFAULT_ACCELERATION          10000		// X, Y, Z and E max acceleration in mm/s^2 for printing moves
    #define DEFAULT_RETRACT_ACCELERATION  10000		// X, Y, Z and E max acceleration in mm/s^2 for r retracts
    

    Changing the last two values from 3000 to 10000 produced a dramatic increase in acceleration, so those numbers do act as overall limits on the per-axis values in the first line.

    Because the motion planner ramps the velocity up at the maximum possible acceleration (reduced, as needed, to accommodate the motor with the lowest acceleration involved in the motion), higher acceleration values allow the motor to reach the desired speed sooner, so shorter motions run faster.

    For example, reaching 200 mm/s2 from a standing start requires 6.7 mm at 3000 mm/s2 and 2.2 mm at 9000 mm/s2. Braking to a stop requires the same distance, assuming the pull-out torque allows it. Contemporary motion planners that can match velocities around corners where straight-line segments join will allow higher sustained speeds, so they don’t require slowing to a dead stop.

    But, of course, the 3D printer’s overall structure must be rigid enough to restrain the reaction forces caused by high accelerations and the printer must be anchored well enough to not sidle off the table. The M2 can handle a single high-speed move, but chaining multiple moves together shakes the steel chassis rather violently; that happens when you select the Slic3r “Avoid crossing perimeters” option.

  • M2 vs. Marlin: Speed Calculations

    Knowing the number of motor steps required to move an axis by 1 mm, the next step is to figure out how fast each axis can possibly move, given the restrictions of the Marlin firmware driving the motors.

    Dan Newman pointed out that Marlin runs with a maximum 10 kHz interrupt rate, with up to four steps issued per interrupt. The constant controlling (or at least defining) that is in Configuration_adv.h (with a comment that seems irrelevant to the M2’s setup):

    #define MAX_STEP_FREQUENCY 40000       // Max step frequency for Ultimaker (5000 pps / half step)
    

    Below the 10 kHz rate, the step interrupt occurs whenever the next step must happen, so it does not have a constant frequency. Above 10 kHz, the steps (seem to) emerge in bursts, so there’s likely a good bit of jitter that I should measure. In any event, there’s an obvious loss of resolution at high speeds, which is a problem common to all variable-frequency pulse generators that’s worse for relatively low-frequency software generators used in high-speed applications.

    In any event, these numbers show the absolute maximum possible speed for each axis:

    • X and Y axes: 450 mm/s = (40 k step/s) / (88.89 step/mm)
    • Z axis: 100 mm/s = (40 k step/s) / (400 step/mm)
    • Extruder: 94.3 mm/s = (40 k step/s) / (424.4 step/mm)

    Due to the low torque available from the Z axis motor, the actual maximum speed seems to be around 30 mm/s = 1800 mm/min. After I replace the motor, I’ll measure the actual performance and see what’s reasonable.

    One could quibble about the extruder, as the extrusion multiplier affects the final speed. The extruded thread squirts out at a pretty good clip if the motor turns at full speed:

    2350 mm/s = (1.75 mm)2 / (0.35 mm)2 * 94 mm/s

    It’s not clear the hot end can melt plastic fast enough to keep up with that pace more than momentarily, but I haven’t measured that yet.

    However, if the X and Y axes both move at 450 mm/s, then the nozzle moves at 640 mm/s = √2 * 450 mm/s relative to the platform, so the maximum extruder speed while printing will be roughly:

    26 mm/s = (640 mm/s) * (0.35 mm)2 / (1.75 mm)2

    That assumes the printed thread has the same cross-section area as the nozzle, which is roughly true for my choice of output:

    • Thread: 0.1 mm2 = 0.4 mm wide * 0.25 mm thick
    • Nozzle: 0.096 mm2 = pi * (0.35 mm)2 / 4

    If you bake the extrusion multiplier into the step/mm value, then compute the maximum speed without applying the same multiplier in slic3r, the plastic should come out of the nozzle at the same speed.

    So the speed setup looks like this:

    #define DEFAULT_MAX_FEEDRATE          {450, 450, 30, 94}    // (mm/sec)