-
-
Notifications
You must be signed in to change notification settings - Fork 179
PR: Load Cell Probe #760
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
base: main
Are you sure you want to change the base?
PR: Load Cell Probe #760
Conversation
6bda0b9 to
7edd998
Compare
dalegaard
left a comment
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.
Why does the MCU need to work in grams? Can't it simply work in and report counts to the host instead? Then the host, with its oodles of cycles, can perform the conversion?
klippy/extras/load_cell/__init__.py
Outdated
| def load_config(config): | ||
| # Sensor types | ||
| sensors = {} | ||
| sensors.update(hx71x.HX71X_SENSOR_TYPES) |
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.
I don't like having a static list here, this should be dynamically loaded.
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.
How can we do that?
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.
One option could be to expose the sensors thing in a register_sensor_factory function, like it's done today for heaters with add_sensor_factory. A pattern that could work:
- User specifies
sensor: hx71x. - We
lookup_object("hx71x") hx71xmodules'load_configdoes alookup_object("load_cell")and callsregister_sensor_factoryto register itself- We call the registered
hx71xsensor factory.
This way:
- we don't need to add any lookup/load object changes
- the sensor list isn't static
- all the dynamic loading stuff exists only within
load_cell/__init__.py
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.
looking at the existing pattern for heater sensors:
def load_config(config):
# Register sensor
pheaters = config.get_printer().load_object(config, "heaters")
pheaters.add_sensor_factory("BME280", BME280)
This code is run because of a registry file, temperature_sensors.cfg. Heaters loads this file. So that's not a dynamic discovery system, that's a registry, so it wont work with plugins.
I'm on board for a fully dynamic system but it needs to solve a few things:
The senor file name is not the same as the sensor_type. Sensor modules can export multiple sensor types. (hx71x.py exports hx717 and hx711). But if all you have is a sensor_type you have no way to perform the reverse lookup to get to the module name to load. The registry 'hack' solves this issue for temperature sensors.
I don't think the individual sensors should be looking up an object. This runs into naming issues. e.g. load_cell cant be the name because it doesn't have to be registered. The user is free to name the load cells because they can have multiples. Heaters gets around this by registering heaters, by analogy I'd need to register load_cells to serve as the collection & registry. I'd rather not pollute the printer object registry with names that the user did not configure.
Proposal: Find all modules with a name that ends in a specific string: hx71x__load_cell_sensor.py. In that way you are exploiting the file system as a registry. Just give back the modules and don't run any load_config. Then the caller can decide how to treat them:
The in load_cell/__init__.py you can do something like this to build the registry once:
sensor_registry = {}
sensor_modules = printer.find_modules("__load_cell_sensor")
for sensor_module in sensor_modules:
sensor_registry.update((sensor_module.register_load_cell_sensors())This way:
- The sensor modules never get registered as objects, minimize clutter in the printer's object space which the user can see.
- Sensor modules can contribute multiple
sensor_typeentries - Any plugin that copies the naming convention can be a sensor
- No central registry is needed, other than the file system
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.
This is now implemented in #769 and this PR now depends on that one.
klippy/extras/load_cell/load_cell.py
Outdated
| self._overflows = 0 | ||
|
|
||
| # move from the started to stopped state and trigger the completion | ||
| def _notify(self): |
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.
Would prefer name like complete or something, since this also has the side effect of setting is_started = False
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.
changed to _complete()
Counts cant be used directly because they could overflow a Also, from painful experience, you want to use grams for debugging. When its a random value in counts you cant easily tell whats going on. With grams its easy enough to print out a value (just shift it right 15 bits) and be sure things are filtering correctly.
|
This change adds a new component registration mechanism that allows modules to register named components with one or more subsystems. The registration happens before printer object creation so no access to Printer is required, limiting possible side effects of registration. This creates a way to do truly dynamic registration without having registries in code, special directories, or files with lists of components. Modules can get registered components at `load_config`\`load_config_prefix` time with a call to `Printer.lookup_components`. Most of the PR is re-working `Printer.load_object` so that it can use a cache of loaded modules. The cache is built prior to any printer objects being created. Having the cache allows for all modules to be scanned for a `register_components` function which is called before the config file is processed. Signed-off-by: Gareth Farrington <[email protected]>
345fe9b to
53b48ca
Compare
| printer_homing: homing.PrinterHoming = self._printer.lookup_object( | ||
| "homing" | ||
| ) | ||
| return printer_homing.probing_move(mcu_probe, pos, speed), collector |
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.
Shouldn't this have a cleanup of collector in case of probing move failure, along the lines of Klipper3d/klipper#7004 ?
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.
Good point, I can bring that commit forward in the stack so its in this PR.
7cea68e to
fc3af6e
Compare
Implement a single `_retract` method so it is always performed consistently. This prepares for probe retry strategies that move the probe in x/y. Signed-off-by: Gareth Farrington <[email protected]>
fc3af6e to
98e50f9
Compare
Pass the GCodeCommand object to the `probing_move` method. This allows probes access to any probe specific custom parameter that might be in the command when probing. Signed-off-by: Gareth Farrington <[email protected]>
This has diplicated code and is unecessarily complex leading to further complexity in later changes. Signed-off-by: Gareth Farrington <[email protected]>
b2a2fe4 to
c2fb998
Compare
Nozzle probes suffer from ooze. If a probe can detect that the nozzle was fouled it can report that infromation back to the probing system. This change adds an optional return parameter, `is_good`, to the `probing_move` interface. If a probe is not good, it can take action to execute additional probes to resolve the issue. This is all under control of the user via the retry strategy. The strategies are: FAIL, IGNORE, RETRY and CIRCLE. RETRY and CIRCLE re-attempt probes with CIRCLE moving the probe to a clean location in a very small circle around the original probe location. CIRCLE is the preferred strategy for bed meshing. FAIL or RETRY might be a good strategy for something like QGL where the absolute position of the probe is critical. IGNORE is a good strategy when probing things that are not as rigid as the bed, such as a nozzle scrubber. Signed-off-by: Gareth Farrington <[email protected]>
If using the CIRCLE stratey and retrying multiple times at the same points (e.g. for QGL) any fouled points are rememberd and avoided by keeping a session between attempts (calls to `start_probe`). The number of retries are reset for all points on each pass but the fouled points are saved. Signed-off-by: Gareth Farrington <[email protected]>
Allow the user to configure their own custom nozzle scrubbing routine that can integrate with custom nozzle scrubbing hardware, such as a brush or wiper. This is invoked when a probe fails. The SCRUBBING_FREQUENCY parameter allows this to be combined with retry strategies, particularly the CIRCLE and RETRY strategies. This can provide enhanced intermittent scrubbing if there is a problem with tapping to clear a fouled nozzle. Signed-off-by: Gareth Farrington <[email protected]>
This is a bit of a tricky/archane macro to write so this saves you the trouble. PROBE HOME=z perfectly homes your Z axis if you have a nozzle probe. HOME=z was used so later we can probe in x/y and submit alternate axis names. Signed-off-by: Gareth Farrington <[email protected]>
empty commit for merging branches
Signed-off-by: Gareth Farrington <[email protected]>
Signed-off-by: Gareth Farrington <[email protected]>
This is an implementation of the SOS fliltering algorithm that runs on the MCU. The filter opperates on data in fixed point format to avoid use of the FPU as klipper does not support FPU usage. This host object handles duties of initalizing and resetting the filter so client dont have to declare their own commands for these opperations. Clients can select how many integer bits they want to use for both the filter coefficients and the filters output value. An arbitrary number of filter sections can be configured. Filters can be designed on the fly with the SciPy library or loaded from another source. Signed-off-by: Gareth Farrington <[email protected]>
Implement MCU features that enable using an adc to stop an axis Signed-off-by: Gareth Farrington <[email protected]>
Signed-off-by: Gareth Farrington <[email protected]>
c2fb998 to
f2d5962
Compare
Initial setup of Load Cell Probing. This implementation supports triggering from the Load Cell Probe on the MCU. It also supports, optiopnal, filtering of the force signal by sos filter to eliminate drift caused by bowden tubes or other mechanical causes. Signed-off-by: Gareth Farrington <[email protected]>
Add a filter workbench Jupiter notebook to help printer developers tune filters based on probing data Signed-off-by: Gareth Farrington <[email protected]>
Add documentation updates for Homing & Probing with load cell probe Signed-off-by: Gareth Farrington <[email protected]> docs: remove hard line breaks from Load_Cell.md ai mucking around Re-write documentation for tersness
…able Signed-off-by: Kevin O'Connor <[email protected]>
Validate host provided index prior to accessing memory using that index. Also, consistently use a uint8_t for max_sections (to account for integer overflow issues). Signed-off-by: Kevin O'Connor <[email protected]>
Catch exceptions raised by the homing module and terminate the collector before re-raising the exception. Signed-off-by: Gareth Farrington <[email protected]>
Instead of polling on an interval, this uses ReactorCompletion to wait until the timeout or the data is delivered. This saves wasted time caused by polling delay. Signed-off-by: Gareth Farrington <[email protected]>
f2d5962 to
101feb6
Compare
The goal of this PR is to get from a gram scale all the way to being able to home a printer with a load cell.
An overview of whats in here:
load_cell_probeis introduced on the MCU - this allows load cell sensors to act as data sources for an endstop that lives on the MCU. It can detect when a sudden force on the load cell indicates a collision. The SOS filter is used to do this.load_cell_probeon the MCU.load_cell_probetogether to turn it into aprobe.SOS Filter
This is a general purpose fixed point SOS filter implementation. It supports a maximum of 4 filter sections.
The primary purpose it to reject drift and noise while homing. There is a post about this here.
This idea was originally introduced by Prusa Research code here. Their implementation uses an old code generator,
mkfilter, and can't be easily changed without re-compilation.This implementation uses the more modern Second Order Sections format and uploads each section to the MCU to initialize the filter. This has a number of advantages for kalico:
LoadCellProbe
Most of the code in this PR is about
load_cell_probeMCU implementation. This is not the end of the code for theload_cell_probe.py. There are another ~1000 lines of code that will go into this file to do the post homing Tap Analysis. I see value in keeping these chunks of functionality separate and combining them with composition. People have reached out to me to do a Filament Scale that would have specific functionality for weighing filament, calculating filament used in a print and so on. Keepingload_cellrelatively small and simple will let that development happen independently.Kalico doesn't have the
PrinterProbeinterface as klipper. The commit that addsLoadCellProbehas been re-written to work withProbeEndstopWrapper. Homing with loadcell probe works, but it will never do the high accuracy pullback move to refine the z=0 position because that would move the toohead while the machine is not homed. Probing after homing is required to get a high accuracy result. In #756, I added aPROBE HOME=zto simplify this process.