Skip to content

Load Cell Tap Analysis#836

Open
garethky wants to merge 8 commits intoKalicoCrew:mainfrom
garethky:pr-load-cell-tap-analysis
Open

Load Cell Tap Analysis#836
garethky wants to merge 8 commits intoKalicoCrew:mainfrom
garethky:pr-load-cell-tap-analysis

Conversation

@garethky
Copy link
Contributor

These changes implement Tap Analysis for load cell probes:

  • Probes have an additional upward movement, called the pullback move, which is used to scan for the absolute 0 force point.
  • The resulting raw force plot is decomposed into line segments that form a tap shape. This involves pulling the movement queue for timing information and doing elbow finding on the move segments. This shape is then validated for errors. Probes that don't result in a recognizable tap shape are flagged as bad probes.
  • An additional module, the Tap Quality Classifier, evaluates the tap events produces a quality score. Taps that would otherwise be valid can be rejected because they are of poor quality (contaminated with ooze)

This code is about 2 years old at this point and some enhancement commits have been stitched into the middle:

  • @rcloran found a bug to do with time floating point math that only showed up after the printer ran for several days. So now the processing happens in a 0 based time frame so as not to cause floating point error.
  • numpy's least squares was replaced with a custom implementation that uses memoization of the calculations. This was about a 10x speedup.

Checklist

  • [✅] pr title makes sense
  • [✅] added a test case if possible
  • [✅] if new feature, added to the readme
  • [❓] ci is happy and green

@garethky garethky force-pushed the pr-load-cell-tap-analysis branch from 1e8fa29 to 080328e Compare February 18, 2026 21:28
These are used to check the temperature of the nozzle and make appropriate changes before probing.

TODO: verify that this will protect homing...

Signed-off-by: Gareth Farrington <gareth@waves.ky>
@garethky garethky force-pushed the pr-load-cell-tap-analysis branch 10 times, most recently from f85531b to 001b4c6 Compare February 19, 2026 01:55
Copy link
Contributor

@rcloran rcloran left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just reviewed the documentation changes and have provided some suggestions. None of these are important and some are a matter of taste and can be ignored if your taste differs :)

# The minimum acceptable tap quality score. Valid range is 0 to 100 percent.
# The default is 40%.
#decompression_angle:
# The average angle of the decompression line for clean taps. The futher the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"further" (on mobile, can't figure out how to suggest a change)

#max_departure_force_pct: 0.25
#max_baseline_force_delta_pct: 0.25
#max_dwell_force_drop_pct: 0.75
# Maximums for tap quality checks.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My programmer brain immediately makes me wonder whether the defaults are fractions equivalent to 50%, 25%, etc, or whether they are 0.5%, 0.25%, etc.

If the former, I'd suggest the options are renamed. If the latter, a clarifying remark might be helpful.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry, what would you rename them to?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Percent" implies the value should go from 0-100... I think I've seen things labeled as "fraction" (or "frac" if you want to keep the short name) in similar cases.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just skimmed the docs, not exactly a thorough review, but heater and fan have "max_power", and fan has "shutdown_speed" and "min_power", all of which are fractions of a maximum, so seem comparable to me, and have no suffix. I didn't find any config values which are expressed in a 0-100 range.

So, I'd just drop the "_pct" here, and call them "max_departure_force" etc. But then that probably requires a further remark saying the range is 0-1 anyways.

# The speed in mm/s for the pullback move after probe trigger. Valid range is
# 0.1 to 1.0 mm/s. The default is set to 1 micron (0.001mm) per sensor sample.
#tap_classifier_module:
# Optional module for custom tap validation. When not specified, the default
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest just saying "the default is ...", rather than "when not specified, ...", as that is more commonly used in these docs, and more concise.

During homing, a watchdog monitors sensor data. If the sensor fails to send measurements for 2 sample periods, the MCU shuts down with error `!! Load Cell Probe Error: timed out waiting for sensor data`. This usually indicates an ADC fault or inadequate grounding. Ensure the frame, power supply, and print bed are grounded. Multiple ground connections may be required. Sand anodized aluminum at ground connection points for good electrical contact.

**Tap validation and retries:**
The probe validates each tap's shape, break-contact timing, and motion chronology. Invalid taps are rejected (`is_valid=False`) and retried based on the probe's configured `bad_probe_strategy` and `bad_probe_retries`. This prevents accepting fouled or poor-quality taps. See [Tap validation error codes](#tap-validation-error-codes) for validation failure types. Note that validation catches many but not all bad taps—proper nozzle temperature and cleanliness remain essential.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/taps-proper/taps -- proper/

(Or at least "taps - proper")

Keep nozzle temperature below the filament oozing point during homing and probing. 140°C is a good starting point for all filament types.

Filament ooze is the primary source of probing error. Kalico does not detect poor quality taps caused by ooze, and modules like `quad_gantry_level` will repeatedly probe fouled locations. Probing at printing temperatures is not recommended.
Filament ooze is the primary source of probing error. Kalico validates tap quality and rejects many bad taps (e.g., due to ooze), retrying per `samples_tolerance_retries`. However, prevention is still best—probing at printing temperatures is not recommended. Watch the console for tap validation errors (e.g., `TAP_SHAPE_INVALID`, `TAP_BREAK_CONTACT_TOO_LATE`) which indicate poor tap quality.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/best-probing/best -- probing/


The intersection of the Decompression line and the Departure line is reported as the Z=0 point by the probe. This is the most critical point on the graph.

The pullback move is very small (~0.2mm) and very slow. Because of its slow speed the slope of the decompression line is more shallow than the compression line. This improves the probes accuracy because the force changes less over time, meaning the z resolution of the probe is increased. Essentially the pullback move is a high resolution force scan of the bed at one point. The pullback move is controlled by the `pullback_speed` and `pullback_dist` options in the config. The default settings scan at 1 ADC sample per micron, giving the probe an expected resolution of 1 micron.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"This improves the probe's accuracy" or "the probe accuracy" instead of "probes accuracy"

| Error Code | Description | Common Causes |
|-------------------------------|--------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------|
| `TAP_CHRONOLOGY` | The 4 points on the tap graphs are out of order in time | Fouling: the data is so distorted that it doesnt look like a tap |
| `TAP_SHAPE_INVALID` | One of the segments of the tap shape doesnt go in the expected direction | Fouling: the data is so distorted that it doesnt look like a tap |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/doesnt/doesn't/

Occurs in two places on this line and once on the line above.

| `TAP_CHRONOLOGY` | The 4 points on the tap graphs are out of order in time | Fouling: the data is so distorted that it doesnt look like a tap |
| `TAP_SHAPE_INVALID` | One of the segments of the tap shape doesnt go in the expected direction | Fouling: the data is so distorted that it doesnt look like a tap |
| `TAP_BREAK_CONTACT_TOO_EARLY` | Break-contact detected too early in the pullback move | The `pullback_distance` is too long. |
| `TAP_BREAK_CONTACT_TOO_LATE` | Break-contact is detected too late in the pullback move | The `pullback_distance` was too short. |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You use "was too short" here, but "is too ..." on the line above and below

| `TAP_BREAK_CONTACT_TOO_EARLY` | Break-contact detected too early in the pullback move | The `pullback_distance` is too long. |
| `TAP_BREAK_CONTACT_TOO_LATE` | Break-contact is detected too late in the pullback move | The `pullback_distance` was too short. |
| `TAP_PULLBACK_TOO_SHORT` | The nozzle never broke contact with the bed during the pullback move | The `pullback_distance` is too short. |
| `COASTING_MOVE_ACCELERATION` | The probing move started to decelerate before the probe triggered | Z is not configured with a negative `min_position` so the probe continues to move past z=0. e.g.: `position_min: -5`. |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found the wording here a little confusing (even though I'd run into this due to misconfiguration and so was familiar with it).

Instead of "so the probe continue to move past z=0" I'd suggest saying "to allow the probe to move below z=0" or something.

|--------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------|
| Approach Force | The change in force in the approach line over the compression force. This is expected to be close to 0. | A large force in the approach line is associated with hitting molten plastic before hitting the bed |
| Departure Force | The change in force in the departure line over the compression force. This is expected to be close to 0. | The nozzle should be in free air during this move, so any distortions are usually due to ooze pulling on the nozzle. |
| Baseline Force | The change in force between the point where the nozzle makes contact with the bed and where is breaks contact, over the compression force. This is expected to be close to 0. | You expect a scale to read zero when you take the weight off, this checks |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"this checks" seems to be incomplete

This change adds the logic to move the probe upwards after the homing move ends at a slower speed. This effectivly provides a high resolution scan of the z space where the nozzle breaks contact with the bed. This data is captured for late analysis.


Signed-off-by: Gareth Farrington <gareth@waves.ky>
@garethky garethky force-pushed the pr-load-cell-tap-analysis branch from 001b4c6 to 393963e Compare February 23, 2026 06:29
* Check force_safety_limit before any probe movement, limiting potential damage. This creates a new error before the probing & homing moves.
* Add force_drift_limit to be checked while the toolhead moves. This changes an existing error to be clear that the probe was moving and saw the maximum allowed force before it triggered.

Signed-off-by: Gareth Farrington <gareth@waves.ky>
This change brings in the full tap analysis algorithm that can pinpoint the exact z=0 from the data in the pullback move. It can also identify a number of scenarios where the tap data quality is too poor to use. An additional user configurable module, called a TapClassifier, can be configured to further refine the tap analysis. This allows continued development of tap classifiers driven by machine learning.


Signed-off-by: Gareth Farrington <gareth@waves.ky>
docs: update Load_Cell.md for tap validation capabilities
Replace numpy least squares with a more efficient implementation that takes advantage of the fact that the same squares are used multiple times in the two lines best fit algorithm. After the setup is done to pre-calculate the squares, the cost to caclulate a specific least squares solution is O(1). This has resulted in about an order of magnitude performance improvement (100-200ms to 10-20ms).

Now the largest delay is caused by buffering and polling for sensor data and is in the order of 50ms to 200ms.


Signed-off-by: Gareth Farrington <gareth@waves.ky>
This module uses some simple, configurable, checks to try and classify taps with minimal reliance on specific machine characteristics, like speed, sample rate, pullback distance etc. Its not perfect but it doesnt require a whole machine learning data collecton campaign to set up. In practice this catches the worse of the oozy taps without too high of a false positive rate.


Signed-off-by: Gareth Farrington <gareth@waves.ky>
@garethky garethky force-pushed the pr-load-cell-tap-analysis branch from 393963e to e0e05b9 Compare March 1, 2026 03:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants