-
-
Notifications
You must be signed in to change notification settings - Fork 5.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add support for load-cell based probes #5623
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1835,6 +1835,42 @@ z_offset: | |
# See the "probe" section for more information on the parameters above. | ||
``` | ||
|
||
### [load_cell_probe] | ||
|
||
Load-cell based probe. One may define this section (instead of a probe | ||
section) to enable a load-cell based probe. See | ||
[Load-cell probe guide](LoadCellProbe.md) and | ||
[command reference](G-Codes.md#load_cell_probe) for further | ||
information. Some of the parameters are determined in a semi-automatic | ||
[calibration procedure](LoadCellProbe.md#setup-for-new-printer-models). | ||
|
||
``` | ||
[load_cell_probe] | ||
# | ||
# General parameters: | ||
# | ||
adc: | ||
# ADC pin which digitizes the load cell signal. | ||
adc_rate: | ||
# Rate (in samples per second) at which to request the ADC samples. | ||
max_abs_force: | ||
# Maximum absolute value of measured force (in chosen unit), if exceeded | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FWIW, I find it confusing for the reference to be vague on units. I think it would be more clear to just say |
||
# printer will shutdown immediately. | ||
# | ||
# Parameters determined by semi-automatic procedure described in the | ||
# load-cell probe guide. | ||
# | ||
force_calibration: | ||
# Conversion factor to get from ADC value to physical unit. Default value is | ||
# 1 (no conversion). | ||
stiffness: | ||
# Stiffness/"spring constant", i.e. force per distance. Default value is a | ||
# safe value resulting in very small step sizes. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FWIW, I find it confusing to state there is some vague default. Probably best to state explicitly what the default is. I'd refrain from saying it is "safe" as we really have no idea if it could cause damage to hardware or not. |
||
noise_level: | ||
# Noise level of force measurements (standard deviation, in physical units). | ||
# Default value is a safe value just to get started. | ||
``` | ||
|
||
## Additional stepper motors and extruders | ||
|
||
### [stepper_z1] | ||
|
@@ -3405,6 +3441,10 @@ lcd_type: | |
# See the display sections below for information on each type and | ||
# additional parameters they provide. This parameter must be | ||
# provided. | ||
#line_length: | ||
# Set the number of characters per line for an hd44780 type lcd. | ||
# Possible values are 20 (default) and 16. The number of lines is | ||
# fixed to 4. | ||
Comment on lines
+3444
to
+3447
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This looks like a merge error. |
||
#display_group: | ||
# The name of the display_data group to show on the display. This | ||
# controls the content of the screen (see the "display_data" section | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -915,6 +915,56 @@ babystepping), and subtract if from the probe's z_offset. This acts | |
to take a frequently used babystepping value, and "make it permanent". | ||
Requires a `SAVE_CONFIG` to take effect. | ||
|
||
### [load_cell_probe] | ||
|
||
The following commands are available when a | ||
[load_cell_probe config section](Config_Reference.md#load_cell_probe) is enabled | ||
(also see the [Load-cell probe guide](LoadCellProbe.md)): | ||
|
||
#### PROBE | ||
`PROBE`: Probe the bed surface. | ||
|
||
#### PROBE_ACCURACY | ||
`PROBE_ACCURACY SAMPLES=<n> SAMPLE_RETRACT_DIST=<dist> DIRECTION=<dir>`: | ||
Test accuracy of the probe by performing `n` probe measurements, retracting | ||
`dist` millimeters in between and comparing the results. The probe direction | ||
`dir` is passed on to the `PROBE` command, if specified. The number of samples | ||
defaults to 10, while the retract distance defaults to the | ||
`sample_retract_dist` value in the config section. | ||
|
||
#### LCP_READ | ||
`LCP_READ`: Perform averaged measurement and print result to console. | ||
Useful for testing and initial calibration. | ||
|
||
#### LCP_COMPENSATE | ||
`LCP_COMPENSATE [SAMPLES=<n>]`: Perform null/"tare" measurement which will be | ||
subtracted from future measurements. The optional parameter allows to specify | ||
how many samples to record for averaging, defaults to 10. Mainly useful for | ||
testing and initial calibration, will be done automatically when necessary. | ||
|
||
#### LCP_CALIB_NOISE | ||
`LCP_CALIB_NOISE [SAMPLES=<n>]`: Only used for the initial calibration. | ||
Determine the `noise_level` configuration parameter from the given number of | ||
samples (defaults to 50). | ||
|
||
#### LCP_CALIB_WEIGHT | ||
`LCP_CALIB_WEIGHT WEIGHT=<weight> [SAMPLES=<n>]`: Only used for the initial | ||
calibration. Determine the `force_calibration` configuration parameter, assuming | ||
the current ADC reading is the equivalent of the given `weight` value (in | ||
used-defined physical units). The optional number of samples is used for | ||
averaging (defaults to 10). | ||
|
||
#### LCP_CALIB_STIFFNESS | ||
`LCP_CALIB_STIFFNESS [SAMPLES=<n>]`: Only used for the initial calibration. | ||
Determine the `stiffness` configuration parameter with the procedure defined in | ||
the [calibration procedure](LoadCellProbe.md#setup-for-new-printer-models). The | ||
optional number of samples is used for averaging (defaults to 10). | ||
|
||
#### LCP_INFO | ||
`LCP_INFO`: Print parameters used by the load cell probe algorithms to the | ||
console. Useful for debugging and testing. | ||
Comment on lines
+935
to
+965
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think we should add so many commands. If necessary, look to add one or two commands and allow the commands to take on separate actions depending on the parameters. Otherwise, when looking at the project as a whole, we end up with hundreds of modules all defining dozens of commands, and it just become unmanageable. Please spell out the module name in the command prefix (eg, |
||
|
||
|
||
### [query_adc] | ||
|
||
The query_adc module is automatically loaded. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,288 @@ | ||
# Load-cell based probe | ||
|
||
A load-cell based probe offers an offset-free measurement of the precise | ||
Z coordinate of the contact position between nozzle and print bed. Under certain | ||
mechanical conditions, a sensitivity can be even achieved in X and Y directions, | ||
which can be useful for CNC routers. Downside of a load-cell probe compared to | ||
other probe types is the slower speed and higher complexity. | ||
|
||
Please also read the | ||
[reference of the config section](Config_Reference.md#load_cell_probe), and the | ||
[reference of the g-code commands](G-Codes.md#load_cell_probe). | ||
|
||
## Mechanical and electrical setup | ||
|
||
<p align="center"> | ||
<img src="img/load-cell-probe-setup.png" width="30%"/> | ||
</p> | ||
|
||
There may be many possibilities how to realise a probe based on load cells. The | ||
setup used for testing and developing this algorithm is as follows: | ||
Comment on lines
+19
to
+20
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FWIW, I think the document would be easier for users if it started with a concrete set of steps necessary to calibrate and use their probe. The theory sections are helpful, but I'd put them towards the end of the document. In my experience, the majority of users just want to follow a guide with the steps to follow and are uninterested in obscure alternatives or theory |
||
|
||
- The hotend is suspended on a pair of load cells such that any force applied | ||
on the nozzle by the bed can be measured. This is shown in the picture above: | ||
The two load cells are the bars on left and right with the green arrow. | ||
|
||
- The two load cells form a rectangle, together with the guiding carriage | ||
(shown in red/green/gray in the above image) and a traverse carrying the | ||
hotend (the grey bar on top of the load cells). | ||
|
||
- The two load cells are connected in parallel to a measurment amplifier and | ||
an ADC. | ||
|
||
- When the nozzle is not in contact with the bed, the load cells will measure | ||
the weight of the hotend and other mechanical parts attached to the cells. | ||
|
||
- Since the cells are designed for weighing, the measured signal will change | ||
into the negative direction when the nozzle touches the bed (less weight or | ||
even upwards pointing force). | ||
|
||
- In the picture, the extruder motor is not shown, but it is attached to the | ||
carriage directly, not to the load cells. This has the advantage that the | ||
load cells can measure the extrusion force, which can be used to optimise | ||
print settings manually, or for some advanced algorithms to optimise the | ||
print automatically. This presents a major advantage over other probes. The | ||
disadvantage is though that high extrusion forces will cause the hotend to | ||
move closer to the bed as the load cells bend. This effect does not play a big | ||
role, since high extrusion forces usually anyway indicate other issues. | ||
|
||
## Working principle of the probe algorithm | ||
|
||
A very basic approach is to move the nozzle in small steps towards the bed and | ||
measure the force after each step. If the force exceeds a certain threshold, a | ||
contact between nozzle and bed is assumed. This simple approach leads to very | ||
coarse results, hence a more sophisticated algorithm is used. | ||
|
||
The load cell measurement is subject to certain drifts and hysteresis-like | ||
effects for several reasons: The rectangle formed by the two load cells, the | ||
carriage and the traverse will have some internal tension, which may change | ||
slightly if forces are applied (e.g. when the nozzle makes contact with the | ||
bed), or sometimes even if the carriage is just moved. Also thermal effects can | ||
lead to drifts of the measurement. For this reason, it is important to make the | ||
algorithm insensitive against changes of the baseline. | ||
|
||
### The phases of a probe approach | ||
|
||
To safe time while still achieving a hight precision, the following phases | ||
starting with a low-precision fast approach are implemented. The several | ||
parameters used by the algorithm can be semi-automatically determined by | ||
following a procedure described at the end of this document. | ||
|
||
#### Fast approach | ||
|
||
To get fast close to the surface, a simple scan using a relative big step size | ||
and threshold is performed. Once the threshold is exceeded, the nozzle is moved | ||
away from the bed a bit to measure the baseline to exclude it has drifted. Only | ||
if the bed contact is confirmed, the next phase is started. Otherwise the fast | ||
approach is continued. | ||
|
||
#### Find fit start position | ||
|
||
Due to the possible hysteresis-like effects, it has been found to be more | ||
reliable to perform the series of measurements for the fit into the negative z | ||
direction. For maximum precision, the series should start at a position with a | ||
low or zero force. Since the fast approach has already moved a bit too far, the | ||
nozzle now needs to be moved away from the bed again. In the interest of saving | ||
time, it is important not to move too far away - ideally the nozzle should be | ||
right above the bed without touching it after this second phase. This is | ||
achieved by extrapolating the measured force linearily and finding the z | ||
position with zero force. To avoid overshooting, several iterations of at most | ||
half the step size of the fast approach are performed until the force is below | ||
the threshold used for the fit. | ||
|
||
#### The fit | ||
|
||
This final phase will determine the z position of the contact position | ||
precisely. Starting from the position determined in the last phase, the force | ||
is measured in dependence of the z position in regular intervals, while moving | ||
into the negative z direction. Each measurement is dift-compensated by lifting | ||
the nozzle right after the measurement and re-measuring the baseline. The result | ||
of this scan is shown in the following figure: | ||
|
||
<p align="center"> | ||
<img src="img/load-cell-probe-fit.svg" width="50%"/> | ||
</p> | ||
|
||
The scan is done while moving into the negative z direction, hence the data | ||
points on the right side are measured first. Since the start position determiend | ||
in the last phase is intentionally slightly above the bed, the first few | ||
measurement points will have measured forces very close to 0 (in the image, | ||
these are the rightmost 4 measurement points). Once the force exceeds a small | ||
threshold, 5 measurement points are recorded. Then, a linear fit is applied | ||
(red line in the image) to determine the z position at which the nozzle touches | ||
the bed but the force is still zero (intersection of the dashed lines). | ||
|
||
Finally, the printer will move to the determined z position, such that | ||
additional commands like `G92` can be used easily. Also the last measured z | ||
position can be obtained in g-code macros through | ||
`printer["load_cell_probe"].last_z_result`. | ||
|
||
#### Accessing the load cell measured force in other modules | ||
|
||
Other modules can subscribe to the continuous read out of the load cell to | ||
receive the current force even while no probe command is executed. Any number of | ||
modules can subscribe by registering a callback function as follows: | ||
|
||
``` | ||
self.printer.lookup_object('load_cell').subscribe_force(self.force_callback) | ||
``` | ||
|
||
The callback function must expect a single argument (besides `self`) which is | ||
the current force. It will be called periodically whenever the ADC sends its | ||
data. This mechanism is necessary, since ADCs normally allow only one single | ||
subscriber. The force will be already corrected with the last known compensation | ||
offset, e.g. from a compensated measurement. | ||
|
||
Also the last measured force can be accessed in g-code macros and display_data | ||
config sections trough `printer["load_cell_probe"].last_force`. This value will | ||
be compensated with the last known offset, too. | ||
|
||
This feature can be used to implement advanced features, especially if also the | ||
extrusion force can be measured, in case the extruder motor is *not* suspended | ||
on the load cells. | ||
|
||
## Setup for new printer models | ||
|
||
A semi-automatic procedure has been developed to determine good parameters for | ||
the algorithm on new printer models. The following parameters need to be | ||
determined: | ||
|
||
- `force_calibration`: Conversion factor to convert ADC readings into physical | ||
units. All other parameters are based on the chosen physical unit. | ||
- `max_abs_force`: Maximum acceptable force. | ||
- `stiffness`: Stiffness/"spring constant" of the mechanical system, i.e. force | ||
per distance. | ||
- `noise_level`: Noise level of force measurements (standard deviation, in | ||
physical units). | ||
|
||
The `max_abs_force` has to be provided by the user. All other parameters are | ||
determined by the following procedure. The precision of the parameter values is | ||
not particularly important - big safety margins will prevent exceeding the | ||
`max_abs_force` and the precision of the probe measurements in the end does not | ||
depend much on the parameters. | ||
|
||
The physical unit can be chosen freely. It is recommended to chose it such that | ||
typical values will be in the range 1 to 1000 or 10000 or so. The unit gram is | ||
a good choice, since extrusion forces are often around 1000 grams while the | ||
maximum acceptable force might be around a few 1000 grams. This keeps the | ||
numbers readable and understandable. Ounces may also be a good choice when being | ||
more familiar with imperial units, although you may want to display it with one | ||
digit after the decimal point then. | ||
|
||
|
||
1. Make sure the load cell reading is sane. Ideally, place the force readout | ||
(`printer["load_cell_probe"].last_force`) on the display, so it can be | ||
monitored throughout the process. Alternatively, the `LCP_READ` can be | ||
used. Absolute values do not matter, also the direction of the changes (sign | ||
of the differences) should not play any role, but make sure that the dynamic | ||
range of the ADC is used well enough: if the maximum value of the ADC is | ||
1.0 (which should be the case if the driver follows the Klipper standards), | ||
the `max_abs_force` should ideally be in the range of 0.5 to 1.0. | ||
2. Eliminate external forces to the hotend and load cells as much as possible, | ||
i.e. unload the filament and detach the bowden lining (if applicable). | ||
3. Determine the noise level by executing the `LCP_CALIB_NOISE` command. Do not | ||
yet execute `SAVE_CONFIG`. | ||
Note: In some setups, the measured noise level might be 0. In this case, the | ||
variable `noise_level` needs to be set manually in the config file to the | ||
equivalent of the least significant bit: If the ADC has 16 bit resolution and | ||
the value range is -1 to +1, set `noise_level` to | ||
`(+1-(-1))/(2^16)=2/65536=3.051757e-5`. | ||
4. Run `LCP_COMPENSATE` to make sure the "tare" offset compensation is correct. | ||
5. Calibrate the readings to physical values: Attach a known weight to the | ||
hotend to apply a known force. The weight should weigh a few 100 grams or so | ||
and can be attached e.g. with tape to the heater block. Precision is not | ||
super important here, the calibration is just needed to get a rough idea of | ||
the forces. Execute the command `LCP_CALIB_WEIGHT WEIGHT=<known_weight>` | ||
while replacing `<known_weight>` with the known weight in the chosen physical | ||
unit (just the number, without unit). | ||
Once the command is complete, execute `SAVE_CONFIG`. Note that this will | ||
convert the previously configured/determined values `max_abs_force` and | ||
`noise_level` automatically into the new unit. | ||
6. Make sure that the chosen `max_abs_force` can actually be measured with the | ||
found calibration. This is the case if the maximum possible raw ADC reading | ||
(usually 1.0) multiplied with the just determined `force_calibration` value | ||
is bigger than `max_abs_force`. If this is not the case, reduce | ||
`max_abs_force` before continuing. | ||
7. Determine the maximum stiffness of the system: Move the nozzle to a position | ||
right above the bed where you expect the printer to be strongest, i.e. where | ||
you expect the printer components to move and bend the least when the nozzle | ||
moves "into" the bed. If the bed is e.g. mounted to the Z axis on a single | ||
side only, move the hotend as close as possible to the Z axis. Lower the | ||
nozzle in small steps carefully, until the measured force starts to increase | ||
(very slightly only!). Execute the command `LCP_CALIB_STIFFNESS`. Then lower | ||
the nozzle a bit furhter such that the force increases to some significant | ||
fraction of `max_abs_force` but still safely below (say 30%). Execute | ||
`LCP_CALIB_STIFFNESS` again, then move away from the bed and execute | ||
`SAVE_CONFIG`. | ||
8. The parameters of the algorithm are now calibrated and the `PROBE` command | ||
can be used in principle. Still it is important to verify the proper | ||
functioning before relying on it blindly. Hence, to protect your bed and | ||
hotend in the following step, it is recommended to place some softer but not | ||
too soft cover on the bed, e.g. a sheet of plywood. | ||
9. Move the nozzle to a position around the center of the bed and place it in | ||
a short distance over the bed (e.g. 1mm). Execute the `PROBE` command. | ||
Observe both the printer movements and the log output carefully. Keep an | ||
eye on the force readings (as printed to the log) and be ready to turn of | ||
the printer in case they exceed sane levels. | ||
10. When you are confident that the `PROBE` command does not cause excessive | ||
forces, remove the plywood cover (or whatever you used) and repeat the | ||
`PROBE` command. Finally, verify the result is reliable by using the | ||
`PROBE_ACCURACY` command. | ||
|
||
|
||
## Best practises | ||
|
||
The probe can be used with the bed_mesh plugin to map the surface of the bed. | ||
Due to the slowness of the load cell probe, a bed mesh calibration takes | ||
considerable amount of time. Hence, it is recommended to use a probe measurement | ||
at a single point to compensate for any changes which do not alter the surface | ||
of the bed, e.g. when changing the hotend nozzle. For this, the following | ||
approach is recommended: | ||
|
||
* Perform probe measurement at a fixed, known position (e.g. X=Y=100mm). | ||
* Calibate Z endstop position such that the probe result position is Z=0 (at the | ||
chosen X/Y coordinate). | ||
* Perform bed mesh calibration and store result to configuration | ||
* When needed (e.g. after swapping the nozzle), repeat the first 2 points to | ||
correct for the changed gloal z offset. It is important to use the same | ||
X/Y coordinates as before. | ||
* When a new bed mesh calibration is required, repeat all 3 points. Never | ||
execute a bed mesh calibration without calibrating the Z endstop position as | ||
described in the first two points. | ||
|
||
The following g-code macros implement this: | ||
|
||
``` | ||
[gcode_macro z_offset_from_probe_result] | ||
gcode: | ||
SET_GCODE_OFFSET Z={printer["load_cell_probe"].last_z_result} | ||
|
||
[gcode_macro bed_calibrate] | ||
gcode: | ||
G0 Z10 | ||
G0 X100 Y100 | ||
G0 Z2 | ||
PROBE | ||
Z_OFFSET_FROM_PROBE_RESULT | ||
Z_OFFSET_APPLY_ENDSTOP | ||
SET_KINEMATIC_POSITION Z=0 | ||
G0 Z2 | ||
BED_MESH_CALIBRATE | ||
SAVE_CONFIG | ||
|
||
[gcode_macro z_calibrate] | ||
gcode: | ||
G0 Z10 | ||
G0 X100 Y100 | ||
G0 Z2 | ||
PROBE | ||
Z_OFFSET_FROM_PROBE_RESULT | ||
Z_OFFSET_APPLY_ENDSTOP | ||
G0 Z2 | ||
``` | ||
|
||
Execute `bed_calibrate` to perform a full calibration of the endstop and the | ||
bed mesh. Later, e.g. after nozzle change, just execute `z_calibrate` to only | ||
calibrate the endstop position while keeping the bed mesh unchanged. Note: if | ||
you need to choose a different X/Y position for the scan, make sure to use the | ||
same position in both macros. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be preferable to layout the config reference in the same format as the other config sections. Each parameter should state what its default is, or explicitly state that it must be provided. FWIW, I'm not sure it helps for the reference to state which values the user can be guided to configuring.