8
$\begingroup$

On a Cartesian printer movements are really simple. If we assume the printer has 100 steps/mm on each axis, then a move of 10mm on a axis is just 1000 Steps on that axis.

Movements that are not aligned with one axis are also simple. Moving from x,y = 0,0 to 10,10 would be 1000 steps on x and y.

On deltas even for simple moves more than one motor has to move. And just calculating the amount of steps on each axis needed to reach the destination probably gives a curved move.

So what is the algorithm to calculate the steps for a given move for a delta printer?

$\endgroup$
  • $\begingroup$ This seems to be solely a theory question which might be more appropriate for a site that deals with programming and/or mathematics as how the firmware works internally is irrelevant to the use of a 3D printer. On the other hand, the question is specific to 3D printing and might be relevant to somebody trying to modify or tweak their printer's firmware. $\endgroup$ – Tom van der Zanden Feb 23 '16 at 20:34
7
$\begingroup$

There are two main special steps to the technique used by most open-source 3d printer controllers:

  1. Divide each linear gcode segment into lots of very small subsegments ("Segmentation")
  2. Use some basic trigonometry or the pythagorean theorem to tie extruder position to carriage height for each of the three towers ("Inverse Kinematics") to find the target position for each small segment

The inverse kinematics are surprisingly simple. A virtual 90 degree triangle is constructed from two known lengths to solve for the unknown third length:

  • The fixed delta arm length is the hypotenuse of the triangle
  • The horizontal distance between the column joints and end-effector joints is calculated from the XY coordinates of the nozzle and the fixed position of the column, to determine the length of the lower side of the triangle
  • The length of the upper side of the triangle is calculated from the previous two via the pythagorean theorem
  • The length of the upper side is added to the nozzle Z height to get the necessary carriage height

I think the best open-source reference here is Steve Grave's Rostock Kinematics document, rev3 available for download here: https://groups.google.com/d/msg/deltabot/V6ATBdT43eU/jEORG_l3dTEJ Some relevant pictures: enter image description here enter image description here

These inverse kinematics calculations are performed for each carriage to get a "carriage space" target position, and this is performed for every path sub-segment.

The results from these steps can then be reinserted back into the standard linear path interpolation techniques for the printer, in which it fires steps in the necessary ratios and at the necessary rates to produce the desired straight-line motion and acceleration/velocity profile. (How THAT is done is a different question.)

The net effect is that the printer will move through a series of small "linear" carriage movements (linear meaning constant* speed with respect to time) that collectively approximate the necessary curved (quadratic position with respect to time) carriage motions required to produce a straight-line end-effector move.

*(Constant speed before acceleration slowdowns are applied in order to obey dynamics constraints, anyway. Again, that's the subject of a different question.)

Segmentation is very similar to the process of using a polygon to approximate a circle. If the facets are small enough, the polygon is a good approximation. Higher Segmentation rates produce less path-following error. The primary conceptual difference between drawing circle arcs and Delta motion paths is that the so-called "faceted arc" with Delta Segmentation is constructed in height-vs-time coordinates instead of the X-vs-Y coordinates you'd use to draw a circle on a computer screen.

This system is used in large part because support for Delta style printers was originally bolted onto GRBL-based motion planners which were written exclusively for straight-line motion paths in Cartesian printers. It was a relatively minimal modification to the existing codebase compared to implementing full quadratic path interpolation.

Techniques have evolved over the years. And alternate approaches are often used: for example, the dc42 fork of RepRapFirmware performs exact path-following without segmentation, by recalculating the proper time for the next step after every step. This is functionally equivalent to approximating a circle with a polygon facet count so high that every pixel on the screen gets its own facet. So it is exactly as accurate as the positioning resolution of the motors allows. The downside is that this segmentation-free technique is fairly processor-intensive, so it only works on relatively fast controllers, not the older 8bit Atmega AVR that powers most existing consumer/hobbyist printers today.

Other techniques are possible. The academic parallel robotics control literature is a whole other world of mathematical techniques and complexity in order to produce generalized control algorithms that work for a wide range of robot mechanisms. The version we use in open-source 3d printers is quite simple and application-specific in comparison.

$\endgroup$
2
$\begingroup$

I am describing how this is done in the Marlin firmware.

The first step is to split a linear movement from (x, y, z) to (x', y', z') into many discrete segments. To this end, the amount of time the move would take at a given speed is calculated, and the value delta_segments_per_second is used to calculate the number of segments used.

This is done in the function prepare_move_delta in the file Marlin_main.cpp. The endpoints of each of these segments is then passed to the function calculate_delta:

void calculate_delta(float cartesian[3]) {
    //reverse kinematics.
    // Perform reversed kinematics, and place results in delta[3]
    // The maths and first version has been done by QHARLEY . Integrated into masterbranch 06/2014 and slightly restructured by Joachim Cerny in June 2014

    float SCARA_pos[2];
    static float SCARA_C2, SCARA_S2, SCARA_K1, SCARA_K2, SCARA_theta, SCARA_psi;

    SCARA_pos[X_AXIS] = cartesian[X_AXIS] * axis_scaling[X_AXIS] - SCARA_offset_x;  //Translate SCARA to standard X Y
    SCARA_pos[Y_AXIS] = cartesian[Y_AXIS] * axis_scaling[Y_AXIS] - SCARA_offset_y;  // With scaling factor.

    #if (Linkage_1 == Linkage_2)
      SCARA_C2 = ((sq(SCARA_pos[X_AXIS]) + sq(SCARA_pos[Y_AXIS])) / (2 * (float)L1_2)) - 1;
    #else
      SCARA_C2 = (sq(SCARA_pos[X_AXIS]) + sq(SCARA_pos[Y_AXIS]) - (float)L1_2 - (float)L2_2) / 45000;
    #endif

    SCARA_S2 = sqrt(1 - sq(SCARA_C2));

    SCARA_K1 = Linkage_1 + Linkage_2 * SCARA_C2;
    SCARA_K2 = Linkage_2 * SCARA_S2;

    SCARA_theta = (atan2(SCARA_pos[X_AXIS], SCARA_pos[Y_AXIS]) - atan2(SCARA_K1, SCARA_K2)) * -1;
    SCARA_psi = atan2(SCARA_S2, SCARA_C2);

    delta[X_AXIS] = SCARA_theta * SCARA_RAD2DEG;  // Multiply by 180/Pi  -  theta is support arm angle
    delta[Y_AXIS] = (SCARA_theta + SCARA_psi) * SCARA_RAD2DEG;  //       -  equal to sub arm angle (inverted motor)
    delta[Z_AXIS] = cartesian[Z_AXIS];
}

This function takes care of the delta geometry and calculations needed to convert the (x,y,z) coordinates of the segment endpoints to corresponding positions for the carriages. The translated coordinates are then passed to plan_buffer_line, which calculates the steps needed for each stepper motor and actually makes these steps happen.

The exact kinematics used in this function are explained in much more detail at the Marlin github.

What is important to note is that plan_buffer_line moves the carriages linearly, and the printhead thus describes an arc and not a straight line. A straight line move is thus approximated by many small arcs.

$\endgroup$
  • $\begingroup$ The code is hard to read.What is SCARA? Could you describe the steps that the code implements? $\endgroup$ – Lars Pötter Feb 25 '16 at 18:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.