Marlin: Movement Function Reference

Created on 1 Jul 2016  ·  31Comments  ·  Source: MarlinFirmware/Marlin

Coordinate Utilities

clamp_to_software_endstops

Constrain a set of coordinates to the allowed movement area.

forward_kinematics

Convert a Cartesian XYZE into a Delta ABCE or SCARA ABZE.

##### adjust_delta
Do Z adjustment on the Delta position for Auto Bed Leveling.
Should be called immediately after calculate_delta.

sync_plan_position, sync_plan_position_e, sync_plan_position_delta, SYNC_PLAN_POSITION_KINEMATIC macro

Set the planner.position and stepper.count from current_position.

set_destination_to_current

Set current_position from destination.

stepper.set_position

Explicitly set the stepper.count values (in steps). Delta and mm-to-steps conversion must be done before calling this function.

planner.set_position_mm

Explicitly set the planner.position and stepper.count values. Delta should be pre-calculated before calling this function.

stepper.get_axis_position_mm

Get the position of a single Cartesian axis directly from the stepper counts. (Works with Core kinematics, but not yet compatible with DELTA.)

stepper.synchronize

Wait for all buffered moves to complete.

Uninterpolated Moves

planner.buffer_line

Send a single move to the planner and set planner.position to the destination.

line_to_current_position

Call planner.buffer_line with a raw move to current_position (updating planner.position).

line_to_z

Call planner.buffer_line with a raw move to the given Z. Don't update current_position[Z_AXIS].

line_to_destination

Call planner.buffer_line with a raw move to destination. Don't update current_position.

prepare_move_to_destination_raw

Calculate the DELTA for destination, tell the planner to do a single uninterpolated move, and updatecurrent_positionfromdestination`.

Interpolated Moves

prepare_move_to_destination

Send appropriate kinematic moves to the planner to reach destination (constrained by the software endstops) and set current_position from (constrained) destination. This function calls one of the following functions to prepare the move:

  • prepare_delta_move_to
  • prepare_scara_move_to
  • prepare_move_to_destination_dualx
  • prepare_move_to_destination_cartesian

Coordinated Moves

do_blocking_move_to, do_blocking_move_to_xy, do_blocking_move_to_x, do_blocking_move_to_z

These functions do a "blocking move" which doesn't return until all moves are completed.

For DELTA, call prepare_move_to_destination or prepare_move_to_destination_raw depending on whether the move needs Delta interpolation. (i.e., it always does a straight move).

For other kinematics (including SCARA [?] ), do an XYZ move with the isolated Z move before or after, depending on whether it's raising or lowering. Uses line_to_current_position for the moves.

do_probe_raise

Move Z only if it is lower than the given raise value (adjusted for probe Z offset and Z home).

Search regex:

(do_blocking_move|prepare_((delta|scara|cartesian)_)?move|line_to_|buffer_line)[a-z_]*
Development

All 31 comments

For DELTA, call prepare_move_to_destination or prepare_move_to_destination_raw depending on whether the move needs Delta interpolation. (i.e., it always does a straight move).

The do_blocking_move() functions automatically call the prepare_move_to_destination() and/or prepare_move_to_destination_raw() for Delta's, right? The developer doesn't need to worry about that any more if using the do_blocking_move() routines, right?

Can you give a simple rule when to use prepare_move_to_destination() versus prepare_move_to_destination_raw() ? It appears to me that prepare_move_to_destination_raw() will make the move, but it won't compensate for any sag that will happen during a move. Is that correct?

@thinkyhead can I steal this for the documentation ? ;-)

Yes, all the do_blocking_move functions give interpolated moves for DELTA.

'Interpolated' means broken up into smaller segments so that the effector does not sag?

can I steal this for the documentation

Do we want to include documentation of the source code for the sake of developers? I'm not sure. But perhaps you can find something useful in this. Like, if we do an "integration guide" for developers who want to make new features. Only thing is, the sources are bound to change, so documentation of this kind may become stale pretty quickly.

We have a "development" section and this type of things is a jump start for people who knows how to code but don't know Marlin internals. On the other hand this type of thing should go inside the source files and then we could extract it using Doxygen.. maybe it's a better option.

The internal operation of the do_blocking_move() routines may change... But it would be good to keep them available going forward. They are a good way to move the nozzle around and do what needs to be done.

(I'm suggesting we SHOULD document them and what it takes to use them successfully.)

We could document, in great detail, exactly how all the movement works, from top (GCode commands) to bottom (pulsing the STEP pins). But we will want to make _a lot_ of diagrams. This stuff is pretty byzantine.

We could document, in great detail, exactly how all the movement works,

One thing that may be much less work, and possibly more value to developers would be this idea. Could we identify the routines we are pretty sure will be there in the near future? My guess is these routines will be present. For example, knowing these routines will always be available and just a little text describing anything special about these routines would have helped me a lot. We might be able to have a few different sections. Movement, probing, end stop control, etc.

  • void do_blocking_move_to(float, float, float );
  • void do_blocking_move_to_z(float );
  • void do_blocking_move_to_xy(float, float );
  • bool code_seen(char);
  • long code_value_long();
  • int16_t code_value_short();
  • bool code_has_value();
  • float code_value_temp_abs()
  • void stow_z_probe();
  • void deploy_z_probe();
  • float probe_pt(float x, float y, bool stow = true, int verbose_level = 1);

Renaming probe_pt to probe_point pretty soon I think. 👍

I think the best idea is to Doxygen comment everything in the sources, and this will allow us to always produce a new copy of the latest code documentation. This keeps the source of documentation tied to the code, whereas if we kept it anywhere else it would always be out of sync.

An other perspective.

We (should) have:

  • 3 different targets:

    • Current_position

    • Destination

    • Parameters (XYZEF, XYZE, XYZF, ..., array, &array, ...)

  • different features:

    • subdivided (only DELTA, SCARA)

    • software endstop clipped

    • blocking

    • leveled (splitted in xy and z )

    • bed leveled

    • update current position

    • not extruding

    • ...

If the features would be a bit-field and we'd send destination and current_position as pointers to the arrays (&destination - &current_position). We could have the universal

move_to( features, &target, f)
move_to( feature1 | feature3, x, y, z, e, f)

For the one type of move where speed matters (G0/G1, subdivided moves of arc/bezier) we could have an exception with less decisions.
For common combinations of features we could define macros for the features.
f could be the global feedrate when omitted and save/restore the global feedrate when given.
e could be current_position[E_AXIS] if omitted.
move_to_z/move_to_xy/... could be macros replacing the omitted coordinates with current_position[].

@thinkyhead why are these functions do_blocking_move_to, do_blocking_move_to_xy, do_blocking_move_to_x, do_blocking_move_to_z dependent on HAS_BED_PROBE ?

I don't believe there is a reason for them to be dependent on HAS_BED_PROBE any more. But here is a history lesson: In the very early days of Auto Bed Leveling, 3-Point leveling was first. It worked well on machines that could hold tight tolerances. Somebody had a machine it did not work well on. And they created 'Accurate Bed Leveling' which is now called 'Grid Based Leveling' just because of the negative connotations it cast towards the 3-Point Leveling.

Back in those days... Before the 'Accurate Bed Leveling' was renamed, the code block was organized like shown below. The fact the do_blocking_move() functions are conditioned on HAS_BED_PROBE is probably a carry over from the original organization:

#ifdef ENABLE_AUTO_BED_LEVELING
#ifdef ACCURATE_BED_LEVELING
static void set_bed_level_equation_lsq(double *plane_equation_coefficients)
{
...
}

#else
static void set_bed_level_equation(float z_at_xLeft_yFront, float z_at_xRight_yFront, float z_at_xLeft_yBack) {
    plan_bed_level_matrix.set_to_identity();
....
}
#endif // ACCURATE_BED_LEVELING

static void run_z_probe() {
....
}

static void do_blocking_move_to(float x, float y, float z) {
    float oldFeedRate = feedrate;

    feedrate = XY_TRAVEL_SPEED;

    current_position[X_AXIS] = x;
    current_position[Y_AXIS] = y;
    current_position[Z_AXIS] = z;
    plan_buffer_line(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS], feedrate/60, active_extruder);
    st_synchronize();

    feedrate = oldFeedRate;
}

static void do_blocking_move_relative(float offset_x, float offset_y, float offset_z) {
    do_blocking_move_to(current_position[X_AXIS] + offset_x, current_position[Y_AXIS] + offset_y, current_position[Z_AXIS] + offset_z);
}
#endif

Thanks for the explanation.

I'm implementing a new feature which is dependent on HAS_BED_PROBE for "no reason".. just because it uses those movement functions.

Or the other way around.
The functions are only defined if HAS_BED_PROBE is defined, because we do not use them when not defined - to save some space - when we do not need them.
In case we want to use them - just define them.

I'm implementing a new feature which is dependent on HAS_BED_PROBE for "no reason".. just because it uses those movement functions.

Also... A little more history: I'm not 100% sure this is true, but I think the do_blocking_move() routines originated with the need to move the nozzle around without GCode telling it where to go. If what I'm saying is true, the 3-Point leveling is an easy way to understand that need. All of that kind of stuff was 'dependent' on Auto Bed Leveling which you needed to have a probe to do.

Now, there are all kinds of reasons why the nozzle needs to be moved without being under GCode control. You were working on the 'Wipe' function, right? That is a good example where you need to move the nozzle but it has nothing to do with Auto Bed Leveling or a Z-Probe. And in fact, Auto Bed Leveling isn't dependent on having a Z-Probe any more either!

I see the do_blocking_move_to_* as an high level movement routine.. specially now that the caller doesn't have to worry about the machine's kinematics.

I understand @Blue-Marlin remark but then I find that making it dependent on HAS_BED_PROBE will not work anymore. We can have the best of both worlds if we make their definition dependent on something like #if ENABLE(BLOCKING_MOVE_FUNCTIONS) and then on Conditionals.h we #define BLOCKING_MOVE_FUNCTIONS based on the features which required them such as the probe related ones.

I'm implementing a new feature which is dependent on HAS_BED_PROBE for "no reason".. just because it uses those movement functions.

In case we want to use them - just define them.

Right. They were originally only used by bed leveling. Lately things have become more generalized. So feel free to move functions out of a dependency block if you intend to use them elsewhere.

@thinkyhead we replied almost at the same time. Do you prefer to have them always available thus moving them out of the #define block. Or do as I suggested to make their definition dependent on Conditionals.h to address @Blue-Marlin concern ?

My vote would be they are always available. They are not very big and they have a lot of use in general.
I kind of view them as a foundation piece that new code can build on. But I can live with the Blue-Marlin suggestion too.

…then on Conditionals.h we #define BLOCKING_MOVE_FUNCTIONS based on…

If some function or group of functions are only needed for one or two features, then I feel it's fine to just use those one or two features as the condition. If things get more complicated, then we could go for added conditionals. Note the purpose of the main blocking move function – to move Z separately from XY, and in a defined order. So these functions are good for cases where we need machine-defined movement (as opposed to GCode-controlled).

I also vote in favor of having them always available, if no one is against this then I will push it as a new PR.

I vote in favor of trying to ensure that all unused functions are not compiled in, to assist with those instances where the compiled code ends up being _just over_ the available space. I think this is the better precedent. (After all, we can determine this with 100% fidelity.)

Ok let's wait a bit more to have more opinions.

Or... Is there a way we can tell GCC (or really... the linker) to strip out unreferenced code?

Well in fact if we define a function and if it is never called, then the compiler will strip it out of the binary, this is one of the optimization flags active by default on Arduino's compiler environment.

Global availability of do_blocking_move_to() is a good idea. I already wanted to use it at least one time (in homing) and had to program around it.

Remember the name giving property of do_ blocking _move_to() - the blocking stepper.synchronize() - wait until the move has finished! Eventually we want to have a do_ blocking _move_to().

I already wanted to use it at least one time (in homing) and had to program around it.

Why?? You should have just taken it out of the #if block. The only reason these conditionals are there is that the functions _were_ only being used in leveling. If that changes, the conditionals can happily go away. But of course, before doing this with any function, be sure you _really_ need what the function provides.

Eventually we want to have a do_ move_to().

Thumbs up !
I'm doing the first step with PR #4307.

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings