docs: Add tips for porting to new kinematics to Code_Overview.md
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
parent
c128a9dfd5
commit
5e8aaed41f
|
@ -212,6 +212,92 @@ ToolHead and kinematic classes. It's this part of the code which
|
||||||
specifies the movements and their timings. The remaining parts of the
|
specifies the movements and their timings. The remaining parts of the
|
||||||
processing is mostly just communication and plumbing.
|
processing is mostly just communication and plumbing.
|
||||||
|
|
||||||
|
Adding new kinematics
|
||||||
|
=====================
|
||||||
|
|
||||||
|
This section provides some tips on adding support to Klipper for
|
||||||
|
additional types of printer kinematics. This type of activity requires
|
||||||
|
excellent understanding of the math formulas for the target
|
||||||
|
kinematics. It also requires software development skills - though one
|
||||||
|
should only need to update the host software (which is written in
|
||||||
|
Python).
|
||||||
|
|
||||||
|
Useful steps:
|
||||||
|
1. Start by studying the [above section](#code-flow-of-a-move-command)
|
||||||
|
and the [Kinematics document](Kinematics.md).
|
||||||
|
2. Review the existing kinematic classes in cartesian.py, corexy.py,
|
||||||
|
and delta.py. The kinematic classes are tasked with converting a
|
||||||
|
move in cartesian coordinates to the movement on each stepper. One
|
||||||
|
should be able to copy one of these files as a starting point.
|
||||||
|
3. Implement the `get_postion()` method in the new kinematics
|
||||||
|
class. This method converts the current stepper position of each
|
||||||
|
stepper axis (stored in millimeters) to a position in cartesian
|
||||||
|
space (also in millimeters).
|
||||||
|
4. Implement the `set_postion()` method. This is the inverse of
|
||||||
|
get_position() - it sets each axis position (in millimeters) given
|
||||||
|
a position in cartesian coordinates.
|
||||||
|
5. Implement the `move()` method. The goal of the move() method is to
|
||||||
|
convert a move defined in cartesian space to a series of stepper
|
||||||
|
step times that implement the requested movement.
|
||||||
|
* The `move()` method is passed a "print_time" parameter (which
|
||||||
|
stores a time in seconds) and a "move" class instance that fully
|
||||||
|
defines the movement. The goal is to repeatedly invoke the
|
||||||
|
`stepper.step()` method with the time (relative to print_time)
|
||||||
|
that each stepper should step at to obtain the desired motion.
|
||||||
|
* One "trick" to help with the movement calculations is to imagine
|
||||||
|
there is a physical rail between `move.start_pos` and
|
||||||
|
`move.end_pos` that confines the print head so that it can only
|
||||||
|
move along this straight line of motion. Then, if the head is
|
||||||
|
confined to that imaginary rail, the head is at `move.start_pos`,
|
||||||
|
only one stepper is enabled (all other steppers can move freely),
|
||||||
|
and the given stepper is stepped a single step, then one can
|
||||||
|
imagine that the head will move along the line of movement some
|
||||||
|
distance. Determine the formula converting this step distance to
|
||||||
|
distance along the line of movement. Once one has the distance
|
||||||
|
along the line of movement, one can figure out the time that the
|
||||||
|
head should be at that position (using the standard formulas for
|
||||||
|
velocity and acceleration). This time is the ideal step time for
|
||||||
|
the given stepper and it can be passed to the `stepper.step()`
|
||||||
|
method.
|
||||||
|
* The `stepper.step()` method must always be called with an
|
||||||
|
increasing time for a given stepper (steps must be scheduled in
|
||||||
|
the order they are to be executed). A common error during
|
||||||
|
kinematic development is to receive an "Internal error in
|
||||||
|
stepcompress" failure - this is generally due to the step()
|
||||||
|
method being invoked with a time earlier than the last scheduled
|
||||||
|
step. For example, if the last step in move1 is scheduled at a
|
||||||
|
time greater than the first step in move2 it will generally
|
||||||
|
result in the above error.
|
||||||
|
* Fractional steps. Be aware that a move request is given in
|
||||||
|
cartesian space and it is not confined to discreet
|
||||||
|
locations. Thus a move's start and end locations may translate to
|
||||||
|
a location on a stepper axis that is between two steps (a
|
||||||
|
fractional step). The code must handle this. The preferred
|
||||||
|
approach is to schedule the next step at the time a move would
|
||||||
|
position the stepper axis at least half way towards the next
|
||||||
|
possible step location. Incorrect handling of fractional steps is
|
||||||
|
a common cause of "Internal error in stepcompress" failures.
|
||||||
|
6. Other methods. The `home()`, `check_move()`, and other methods
|
||||||
|
should also be implemented. However, at the start of development
|
||||||
|
one can use empty code here.
|
||||||
|
7. Implement test cases. Create a g-code file with a series of moves
|
||||||
|
that can test important cases for the given kinematics. Follow the
|
||||||
|
[debugging documentation](Debugging.md) to convert this g-code file
|
||||||
|
to micro-controller commands. This is useful to exercise corner
|
||||||
|
cases and to check for regressions.
|
||||||
|
8. Optimize if needed. One may notice that the existing kinematic
|
||||||
|
classes do not call `stepper.step()`. This is purely an
|
||||||
|
optimization - the inner loop of the kinematic calculations were
|
||||||
|
moved to C to reduce load on the host cpu. All of the existing
|
||||||
|
kinematic classes started development using `stepper.step()` and
|
||||||
|
then were later optimized. The g-code to mcu command translation
|
||||||
|
(described in the previous step) is a useful tool during
|
||||||
|
optimization - if a code change is purely an optimization then it
|
||||||
|
should not impact the resulting text representation of the mcu
|
||||||
|
commands (though minor changes in output due to floating point
|
||||||
|
rounding are possible). So, one can use this system to detect
|
||||||
|
regressions.
|
||||||
|
|
||||||
Time
|
Time
|
||||||
====
|
====
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,7 @@ class PrinterStepper:
|
||||||
self.mcu_stepper.setup_dir_pin(dir_pin_params)
|
self.mcu_stepper.setup_dir_pin(dir_pin_params)
|
||||||
self.step_dist = config.getfloat('step_distance', above=0.)
|
self.step_dist = config.getfloat('step_distance', above=0.)
|
||||||
self.mcu_stepper.setup_step_distance(self.step_dist)
|
self.mcu_stepper.setup_step_distance(self.step_dist)
|
||||||
|
self.step = self.mcu_stepper.step
|
||||||
self.step_const = self.mcu_stepper.step_const
|
self.step_const = self.mcu_stepper.step_const
|
||||||
self.step_delta = self.mcu_stepper.step_delta
|
self.step_delta = self.mcu_stepper.step_delta
|
||||||
self.enable = lookup_enable_pin(printer, config.get('enable_pin', None))
|
self.enable = lookup_enable_pin(printer, config.get('enable_pin', None))
|
||||||
|
|
Loading…
Reference in New Issue