2011-01-14 16:45:18 +01:00
|
|
|
/*
|
2011-02-11 00:34:53 +01:00
|
|
|
planner.c - buffers movement commands and manages the acceleration profile plan
|
2011-01-14 16:45:18 +01:00
|
|
|
Part of Grbl
|
|
|
|
|
|
|
|
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
2012-01-29 04:41:08 +01:00
|
|
|
Copyright (c) 2011-2012 Sungeun K. Jeon
|
2011-12-09 02:47:48 +01:00
|
|
|
Copyright (c) 2011 Jens Geisler
|
2011-09-03 23:31:48 +02:00
|
|
|
|
2011-01-14 16:45:18 +01:00
|
|
|
Grbl is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
Grbl is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2011-02-11 01:31:44 +01:00
|
|
|
/* The ring buffer implementation gleaned from the wiring_serial library by David A. Mellis. */
|
|
|
|
|
New startup script setting. New dry run, check gcode switches. New system state variable. Lots of reorganizing.
(All v0.8 features installed. Still likely buggy, but now thourough
testing will need to start to squash them all. As soon as we're done,
this will be pushed to master and v0.9 development will be started.
Please report ANY issues to us so we can get this rolled out ASAP.)
- User startup script! A user can now save one (up to 5 as compile-time
option) block of g-code in EEPROM memory. This will be run everytime
Grbl resets. Mainly to be used as a way to set your preferences, like
G21, G54, etc.
- New dry run and check g-code switches. Dry run moves ALL motions at
rapids rate ignoring spindle, coolant, and dwell commands. For rapid
physical proofing of your code. The check g-code switch ignores all
motion and provides the user a way to check if there are any errors in
their program that Grbl may not like.
- Program restart! (sort of). Program restart is typically an advanced
feature that allows users to restart a program mid-stream. The check
g-code switch can perform this feature by enabling the switch at the
start of the program, and disabling it at the desired point with some
minimal changes.
- New system state variable. This state variable tracks all of the
different state processes that Grbl performs, i.e. cycle start, feed
hold, homing, etc. This is mainly for making managing of these task
easier and more clear.
- Position lost state variable. Only when homing is enabled, Grbl will
refuse to move until homing is completed and position is known. This is
mainly for safety. Otherwise, it will let users fend for themselves.
- Moved the default settings defines into config.h. The plan is to
eventually create a set of config.h's for particular as-built machines
to help users from doing it themselves.
- Moved around misc defines into .h files. And lots of other little
things.
2012-11-03 18:32:23 +01:00
|
|
|
#include <inttypes.h>
|
2011-01-14 17:13:33 +01:00
|
|
|
#include <stdlib.h>
|
2011-02-11 00:34:53 +01:00
|
|
|
#include "planner.h"
|
2011-01-14 16:45:18 +01:00
|
|
|
#include "nuts_bolts.h"
|
|
|
|
#include "stepper.h"
|
2011-02-05 00:45:41 +01:00
|
|
|
#include "settings.h"
|
2011-02-05 00:55:37 +01:00
|
|
|
#include "config.h"
|
2011-12-09 02:47:48 +01:00
|
|
|
#include "protocol.h"
|
2011-01-14 16:45:18 +01:00
|
|
|
|
2011-02-12 00:03:58 +01:00
|
|
|
static block_t block_buffer[BLOCK_BUFFER_SIZE]; // A ring buffer for motion instructions
|
2011-02-19 00:32:36 +01:00
|
|
|
static volatile uint8_t block_buffer_head; // Index of the next block to be pushed
|
|
|
|
static volatile uint8_t block_buffer_tail; // Index of the block to process now
|
2011-12-09 02:47:48 +01:00
|
|
|
static uint8_t next_buffer_head; // Index of the next buffer head
|
2011-01-25 23:33:19 +01:00
|
|
|
|
2012-01-10 16:34:48 +01:00
|
|
|
// Define planner variables
|
|
|
|
typedef struct {
|
2012-12-12 01:42:29 +01:00
|
|
|
int32_t position[N_AXIS]; // The planner position of the tool in absolute steps. Kept separate
|
|
|
|
// from g-code position for movements requiring multiple line motions,
|
|
|
|
// i.e. arcs, canned cycles, and backlash compensation.
|
|
|
|
float previous_unit_vec[N_AXIS]; // Unit vector of previous path line segment
|
|
|
|
float previous_nominal_speed_sqr; // Nominal speed of previous path line segment
|
2012-01-10 16:34:48 +01:00
|
|
|
} planner_t;
|
|
|
|
static planner_t pl;
|
2011-09-03 23:31:48 +02:00
|
|
|
|
|
|
|
// Returns the index of the next block in the ring buffer
|
2011-09-14 05:57:16 +02:00
|
|
|
// NOTE: Removed modulo (%) operator, which uses an expensive divide and multiplication.
|
2011-12-09 02:47:48 +01:00
|
|
|
static uint8_t next_block_index(uint8_t block_index)
|
|
|
|
{
|
2011-09-14 05:57:16 +02:00
|
|
|
block_index++;
|
|
|
|
if (block_index == BLOCK_BUFFER_SIZE) { block_index = 0; }
|
|
|
|
return(block_index);
|
2011-09-03 23:31:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Returns the index of the previous block in the ring buffer
|
2011-12-09 02:47:48 +01:00
|
|
|
static uint8_t prev_block_index(uint8_t block_index)
|
|
|
|
{
|
2011-09-18 13:36:55 +02:00
|
|
|
if (block_index == 0) { block_index = BLOCK_BUFFER_SIZE; }
|
|
|
|
block_index--;
|
2011-09-03 23:31:48 +02:00
|
|
|
return(block_index);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-12-08 23:00:58 +01:00
|
|
|
/* STEPPER VELOCITY PROFILE DEFINITION
|
|
|
|
less than nominal rate-> +
|
|
|
|
+--------+ <- nominal_rate /|\
|
|
|
|
/ \ / | \
|
|
|
|
initial_rate -> + \ / | + <- next->initial_rate
|
|
|
|
| + <- next->initial_rate / | |
|
|
|
|
+-------------+ initial_rate -> +----+--+
|
|
|
|
time --> ^ ^ ^ ^
|
|
|
|
| | | |
|
|
|
|
decelerate distance decelerate distance
|
|
|
|
|
|
|
|
Calculates trapezoid parameters for stepper algorithm. Each block velocity profiles can be
|
|
|
|
described as either a trapezoidal or a triangular shape. The trapezoid occurs when the block
|
|
|
|
reaches the nominal speed of the block and cruises for a period of time. A triangle occurs
|
|
|
|
when the nominal speed is not reached within the block. Some other special cases exist,
|
|
|
|
such as pure ac/de-celeration velocity profiles from beginning to end or a trapezoid that
|
|
|
|
has no deceleration period when the next block resumes acceleration.
|
2011-09-04 07:22:27 +02:00
|
|
|
|
2012-12-08 23:00:58 +01:00
|
|
|
The following function determines the type of velocity profile and stores the minimum required
|
|
|
|
information for the stepper algorithm to execute the calculated profiles. In this case, only
|
2012-12-11 02:50:18 +01:00
|
|
|
the new initial rate and n_steps until deceleration are computed, since the stepper algorithm
|
2012-12-08 23:00:58 +01:00
|
|
|
already handles acceleration and cruising and just needs to know when to start decelerating.
|
|
|
|
*/
|
2012-12-12 01:42:29 +01:00
|
|
|
static void calculate_trapezoid_for_block(block_t *block, float entry_speed_sqr, float exit_speed_sqr)
|
2012-12-08 23:00:58 +01:00
|
|
|
{
|
|
|
|
// Compute new initial rate for stepper algorithm
|
2012-12-12 01:42:29 +01:00
|
|
|
block->initial_rate = ceil(sqrt(entry_speed_sqr)*(RANADE_MULTIPLIER/(60*ISR_TICKS_PER_SECOND))); // (mult*mm/isr_tic)
|
2011-09-07 03:39:14 +02:00
|
|
|
|
2012-12-11 02:50:18 +01:00
|
|
|
// Compute efficiency variable for following calculations. Removes a float divide and multiply.
|
|
|
|
// TODO: If memory allows, this can be kept in the block buffer since it doesn't change, even after feed holds.
|
|
|
|
float steps_per_mm_div_2_acc = block->step_event_count/(2*settings.acceleration*block->millimeters);
|
|
|
|
|
|
|
|
// First determine intersection distance (in steps) from the exit point for a triangular profile.
|
|
|
|
// Computes: steps_intersect = steps/mm * ( distance/2 + (v_entry^2-v_exit^2)/(4*acceleration) )
|
2012-12-12 01:42:29 +01:00
|
|
|
int32_t intersect_distance = ceil( 0.5*(block->step_event_count + steps_per_mm_div_2_acc*(entry_speed_sqr-exit_speed_sqr)) );
|
2011-01-24 21:30:51 +01:00
|
|
|
|
2012-12-08 23:00:58 +01:00
|
|
|
// Check if this is a pure acceleration block by a intersection distance less than zero. Also
|
|
|
|
// prevents signed and unsigned integer conversion errors.
|
|
|
|
if (intersect_distance <= 0) {
|
|
|
|
block->decelerate_after = 0;
|
|
|
|
} else {
|
2012-12-11 02:50:18 +01:00
|
|
|
// Determine deceleration distance (in steps) from nominal speed to exit speed for a trapezoidal profile.
|
2012-12-08 23:00:58 +01:00
|
|
|
// Value is never negative. Nominal speed is always greater than or equal to the exit speed.
|
2012-12-11 02:50:18 +01:00
|
|
|
// Computes: steps_decelerate = steps/mm * ( (v_nominal^2 - v_exit^2)/(2*acceleration) )
|
2012-12-12 01:42:29 +01:00
|
|
|
block->decelerate_after = ceil(steps_per_mm_div_2_acc * (block->nominal_speed_sqr - exit_speed_sqr));
|
2011-09-03 23:31:48 +02:00
|
|
|
|
2012-12-08 23:00:58 +01:00
|
|
|
// The lesser of the two triangle and trapezoid distances always defines the velocity profile.
|
|
|
|
if (block->decelerate_after > intersect_distance) { block->decelerate_after = intersect_distance; }
|
2011-10-12 04:51:04 +02:00
|
|
|
|
2012-12-08 23:00:58 +01:00
|
|
|
// Finally, check if this is a pure deceleration block.
|
|
|
|
if (block->decelerate_after > block->step_event_count) { block->decelerate_after = block->step_event_count; }
|
|
|
|
}
|
2011-08-16 03:37:22 +02:00
|
|
|
}
|
2012-12-08 23:00:58 +01:00
|
|
|
|
2011-08-16 03:37:22 +02:00
|
|
|
|
2011-09-07 03:39:14 +02:00
|
|
|
/* PLANNER SPEED DEFINITION
|
|
|
|
+--------+ <- current->nominal_speed
|
|
|
|
/ \
|
|
|
|
current->entry_speed -> + \
|
|
|
|
| + <- next->entry_speed
|
|
|
|
+-------------+
|
2012-12-08 23:00:58 +01:00
|
|
|
time -->
|
|
|
|
|
|
|
|
Recalculates the motion plan according to the following algorithm:
|
2011-09-14 05:57:16 +02:00
|
|
|
|
2012-12-08 23:00:58 +01:00
|
|
|
1. Go over every block in reverse order and calculate a junction speed reduction (i.e. block_t.entry_speed)
|
|
|
|
so that:
|
|
|
|
a. The junction speed is equal to or less than the maximum junction speed limit
|
|
|
|
b. No speed reduction within one block requires faster deceleration than the acceleration limits.
|
|
|
|
c. The last (or newest appended) block is planned from a complete stop.
|
|
|
|
2. Go over every block in chronological (forward) order and dial down junction speed values if
|
|
|
|
a. The speed increase within one block would require faster acceleration than the acceleration limits.
|
|
|
|
|
|
|
|
When these stages are complete, all blocks have a junction entry speed that will allow all speed changes
|
|
|
|
to be performed using the overall limiting acceleration value, and where no junction speed is greater
|
|
|
|
than the max limit. In other words, it just computed the fastest possible velocity profile through all
|
|
|
|
buffered blocks, where the final buffered block is planned to come to a full stop when the buffer is fully
|
|
|
|
executed. Finally it will:
|
|
|
|
|
|
|
|
3. Convert the plan to data that the stepper algorithm needs. Only block trapezoids adjacent to a
|
|
|
|
a planner-modified junction speed with be updated, the others are assumed ok as is.
|
|
|
|
|
|
|
|
All planner computations(1)(2) are performed in floating point to minimize numerical round-off errors. Only
|
|
|
|
when planned values are converted to stepper rate parameters(3), these are integers. If another motion block
|
|
|
|
is added while executing, the planner will re-plan and update the stored optimal velocity profile as it goes.
|
|
|
|
|
|
|
|
NOTE: As executing blocks complete and incoming streaming blocks are appended to the planner buffer, this
|
|
|
|
function is constantly re-calculating and must be as efficient as possible. For example, in situations like
|
|
|
|
arc generation or complex curves, the short, rapid line segments can execute faster than new blocks can be
|
|
|
|
added, and the planner buffer will starve and empty, leading to weird hiccup-like jerky motions.
|
|
|
|
*/
|
|
|
|
static void planner_recalculate()
|
|
|
|
{
|
|
|
|
// TODO: No over-write protection exists for the executing block. For most cases this has proven to be ok, but
|
|
|
|
// for feed-rate overrides, something like this is essential. Place a request here to the stepper driver to
|
|
|
|
// find out where in the planner buffer is the a safe place to begin re-planning from.
|
|
|
|
|
2012-12-12 01:42:29 +01:00
|
|
|
// if (block_buffer_head != block_buffer_tail) {
|
|
|
|
|
|
|
|
float entry_speed_sqr;
|
|
|
|
|
2012-12-11 02:50:18 +01:00
|
|
|
// Perform reverse planner pass. Skip the head(end) block since it is already initialized, and skip the
|
2012-12-12 01:42:29 +01:00
|
|
|
// tail(first) block to prevent over-writing of the initial entry speed.
|
|
|
|
uint8_t block_index = prev_block_index( block_buffer_head ); // Assume buffer is not empty.
|
|
|
|
block_t *current = &block_buffer[block_index]; // Head block-1 = Newly appended block
|
|
|
|
block_t *next;
|
|
|
|
if (block_index != block_buffer_tail) { block_index = prev_block_index( block_index ); }
|
|
|
|
while (block_index != block_buffer_tail) {
|
2012-12-08 23:00:58 +01:00
|
|
|
next = current;
|
2012-12-12 01:42:29 +01:00
|
|
|
current = &block_buffer[block_index];
|
|
|
|
// If entry speed is already at the maximum entry speed, no need to recheck. Block is cruising.
|
|
|
|
// If not, block in state of acceleration or deceleration. Reset entry speed to maximum and
|
|
|
|
// check for maximum allowable speed reductions to ensure maximum possible planned speed.
|
|
|
|
if (current->entry_speed_sqr != current->max_entry_speed_sqr) {
|
|
|
|
// If nominal length true, max junction speed is guaranteed to be reached. Only compute
|
|
|
|
// for max allowable speed if block is decelerating and nominal length is false.
|
|
|
|
|
|
|
|
current->entry_speed_sqr = current->max_entry_speed_sqr;
|
|
|
|
current->recalculate_flag = true; // Almost always changes. So force recalculate.
|
|
|
|
|
|
|
|
if ((!current->nominal_length_flag) && (current->max_entry_speed_sqr > next->entry_speed_sqr)) {
|
|
|
|
// Computes: v_entry^2 = v_exit^2 + 2*acceleration*distance
|
|
|
|
entry_speed_sqr = next->entry_speed_sqr + 2*settings.acceleration*current->millimeters;
|
|
|
|
if (entry_speed_sqr < current->max_entry_speed_sqr) {
|
|
|
|
current->entry_speed_sqr = entry_speed_sqr;
|
2012-12-11 04:11:08 +01:00
|
|
|
}
|
2012-12-12 01:42:29 +01:00
|
|
|
}
|
2012-12-08 23:00:58 +01:00
|
|
|
}
|
2012-12-12 01:42:29 +01:00
|
|
|
block_index = prev_block_index( block_index );
|
|
|
|
}
|
2012-12-11 04:11:08 +01:00
|
|
|
|
2012-12-11 02:50:18 +01:00
|
|
|
// Perform forward planner pass. Begins junction speed adjustments after tail(first) block.
|
|
|
|
// Also recalculate trapezoids, block by block, as the forward pass completes the plan.
|
2012-12-12 01:42:29 +01:00
|
|
|
block_index = next_block_index(block_buffer_tail);
|
|
|
|
next = &block_buffer[block_buffer_tail]; // Places tail(first) block into current
|
2012-12-11 02:50:18 +01:00
|
|
|
while (block_index != block_buffer_head) {
|
2012-12-08 23:00:58 +01:00
|
|
|
current = next;
|
2012-12-12 01:42:29 +01:00
|
|
|
next = &block_buffer[block_index];
|
2012-12-08 23:00:58 +01:00
|
|
|
|
2012-12-11 02:50:18 +01:00
|
|
|
// If the current block is an acceleration block, but it is not long enough to complete the
|
2012-12-12 01:42:29 +01:00
|
|
|
// full speed change within the block, we need to adjust the exit speed accordingly. Entry
|
2012-12-08 23:00:58 +01:00
|
|
|
// speeds have already been reset, maximized, and reverse planned by reverse planner.
|
2012-12-12 01:42:29 +01:00
|
|
|
// If nominal length is true, max junction speed is guaranteed to be reached. So skip block.
|
2012-12-08 23:00:58 +01:00
|
|
|
if (!current->nominal_length_flag) {
|
2012-12-12 01:42:29 +01:00
|
|
|
if (current->entry_speed_sqr < next->entry_speed_sqr) {
|
|
|
|
// Compute block exit speed based on the current block speed and distance
|
|
|
|
// Computes: v_exit^2 = v_entry^2 + 2*acceleration*distance
|
|
|
|
entry_speed_sqr = current->entry_speed_sqr + 2*settings.acceleration*current->millimeters;
|
|
|
|
|
|
|
|
// If it's less than the stored value, update the exit speed and set recalculate flag.
|
|
|
|
if (entry_speed_sqr < next->entry_speed_sqr) {
|
|
|
|
next->entry_speed_sqr = entry_speed_sqr;
|
2012-12-08 23:00:58 +01:00
|
|
|
next->recalculate_flag = true;
|
|
|
|
}
|
2012-12-11 02:50:18 +01:00
|
|
|
}
|
2012-12-08 23:00:58 +01:00
|
|
|
}
|
2012-12-11 03:17:22 +01:00
|
|
|
|
|
|
|
// Recalculate if current block entry or exit junction speed has changed.
|
|
|
|
if (current->recalculate_flag || next->recalculate_flag) {
|
|
|
|
// NOTE: Entry and exit factors always > 0 by all previous logic operations.
|
2012-12-12 01:42:29 +01:00
|
|
|
calculate_trapezoid_for_block(current, current->entry_speed_sqr, next->entry_speed_sqr);
|
2012-12-11 03:17:22 +01:00
|
|
|
current->recalculate_flag = false; // Reset current only to ensure next trapezoid is computed
|
|
|
|
}
|
2012-12-12 01:42:29 +01:00
|
|
|
|
2011-09-03 23:31:48 +02:00
|
|
|
block_index = next_block_index( block_index );
|
2011-01-15 00:27:08 +01:00
|
|
|
}
|
2012-12-12 01:42:29 +01:00
|
|
|
|
2011-09-24 15:46:41 +02:00
|
|
|
// Last/newest block in buffer. Exit speed is set with MINIMUM_PLANNER_SPEED. Always recalculated.
|
2012-12-12 01:42:29 +01:00
|
|
|
calculate_trapezoid_for_block(next, next->entry_speed_sqr, MINIMUM_PLANNER_SPEED*MINIMUM_PLANNER_SPEED);
|
2011-09-07 03:39:14 +02:00
|
|
|
next->recalculate_flag = false;
|
2012-12-12 01:42:29 +01:00
|
|
|
|
|
|
|
// }
|
2011-01-15 00:27:08 +01:00
|
|
|
}
|
|
|
|
|
2011-12-09 02:47:48 +01:00
|
|
|
void plan_reset_buffer()
|
|
|
|
{
|
|
|
|
block_buffer_tail = block_buffer_head;
|
|
|
|
next_buffer_head = next_block_index(block_buffer_head);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plan_init()
|
|
|
|
{
|
|
|
|
plan_reset_buffer();
|
2012-01-10 16:34:48 +01:00
|
|
|
memset(&pl, 0, sizeof(pl)); // Clear planner struct
|
2011-01-25 13:55:11 +01:00
|
|
|
}
|
2011-01-24 23:08:44 +01:00
|
|
|
|
2012-01-29 04:41:08 +01:00
|
|
|
inline void plan_discard_current_block()
|
2011-12-09 02:47:48 +01:00
|
|
|
{
|
2011-02-06 23:52:12 +01:00
|
|
|
if (block_buffer_head != block_buffer_tail) {
|
2011-09-03 23:31:48 +02:00
|
|
|
block_buffer_tail = next_block_index( block_buffer_tail );
|
2011-02-06 23:52:12 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-29 04:41:08 +01:00
|
|
|
inline block_t *plan_get_current_block()
|
2011-12-09 02:47:48 +01:00
|
|
|
{
|
2011-02-06 23:52:12 +01:00
|
|
|
if (block_buffer_head == block_buffer_tail) { return(NULL); }
|
|
|
|
return(&block_buffer[block_buffer_tail]);
|
|
|
|
}
|
|
|
|
|
2011-12-09 02:47:48 +01:00
|
|
|
// Returns the availability status of the block ring buffer. True, if full.
|
|
|
|
uint8_t plan_check_full_buffer()
|
|
|
|
{
|
|
|
|
if (block_buffer_tail == next_buffer_head) { return(true); }
|
|
|
|
return(false);
|
|
|
|
}
|
|
|
|
|
2012-11-20 01:39:40 +01:00
|
|
|
// Block until all buffered steps are executed or in a cycle state. Works with feed hold
|
|
|
|
// during a synchronize call, if it should happen. Also, waits for clean cycle end.
|
2011-12-09 02:47:48 +01:00
|
|
|
void plan_synchronize()
|
|
|
|
{
|
2012-11-20 01:39:40 +01:00
|
|
|
while (plan_get_current_block() || sys.state == STATE_CYCLE) {
|
2011-12-09 02:47:48 +01:00
|
|
|
protocol_execute_runtime(); // Check and execute run-time commands
|
2012-01-06 18:10:41 +01:00
|
|
|
if (sys.abort) { return; } // Check for system abort
|
2011-12-09 02:47:48 +01:00
|
|
|
}
|
|
|
|
}
|
2011-09-03 23:31:48 +02:00
|
|
|
|
2011-07-09 22:35:25 +02:00
|
|
|
// Add a new linear movement to the buffer. x, y and z is the signed, absolute target position in
|
2011-12-09 02:47:48 +01:00
|
|
|
// millimeters. Feed rate specifies the speed of the motion. If feed rate is inverted, the feed
|
2011-07-09 22:35:25 +02:00
|
|
|
// rate is taken to mean "frequency" and would complete the operation in 1/feed_rate minutes.
|
2012-02-11 19:59:35 +01:00
|
|
|
// All position data passed to the planner must be in terms of machine position to keep the planner
|
|
|
|
// independent of any coordinate system changes and offsets, which are handled by the g-code parser.
|
2011-12-09 02:47:48 +01:00
|
|
|
// NOTE: Assumes buffer is available. Buffer checks are handled at a higher level by motion_control.
|
2012-10-08 23:57:58 +02:00
|
|
|
void plan_buffer_line(float x, float y, float z, float feed_rate, uint8_t invert_feed_rate)
|
2011-12-09 02:47:48 +01:00
|
|
|
{
|
|
|
|
// Prepare to set up new block
|
|
|
|
block_t *block = &block_buffer[block_buffer_head];
|
|
|
|
|
2011-02-06 23:23:34 +01:00
|
|
|
// Calculate target position in absolute steps
|
|
|
|
int32_t target[3];
|
2011-02-06 23:27:04 +01:00
|
|
|
target[X_AXIS] = lround(x*settings.steps_per_mm[X_AXIS]);
|
|
|
|
target[Y_AXIS] = lround(y*settings.steps_per_mm[Y_AXIS]);
|
2011-02-10 13:06:18 +01:00
|
|
|
target[Z_AXIS] = lround(z*settings.steps_per_mm[Z_AXIS]);
|
2011-09-07 03:39:14 +02:00
|
|
|
|
2011-01-14 16:56:44 +01:00
|
|
|
// Number of steps for each axis
|
2012-01-10 16:34:48 +01:00
|
|
|
block->steps_x = labs(target[X_AXIS]-pl.position[X_AXIS]);
|
|
|
|
block->steps_y = labs(target[Y_AXIS]-pl.position[Y_AXIS]);
|
|
|
|
block->steps_z = labs(target[Z_AXIS]-pl.position[Z_AXIS]);
|
2011-01-14 16:56:44 +01:00
|
|
|
block->step_event_count = max(block->steps_x, max(block->steps_y, block->steps_z));
|
2011-09-03 23:31:48 +02:00
|
|
|
|
2011-01-14 16:56:44 +01:00
|
|
|
// Bail if this is a zero-length block
|
|
|
|
if (block->step_event_count == 0) { return; };
|
2011-02-06 23:23:34 +01:00
|
|
|
|
2011-09-07 03:39:14 +02:00
|
|
|
// Compute path vector in terms of absolute step target and current positions
|
2012-12-11 02:50:18 +01:00
|
|
|
// TODO: Store last xyz in memory to remove steps_per_mm divides. Or come up with another way to save cycles.
|
2012-10-08 23:57:58 +02:00
|
|
|
float delta_mm[3];
|
2012-12-11 02:50:18 +01:00
|
|
|
delta_mm[X_AXIS] = block->steps_x/settings.steps_per_mm[X_AXIS];
|
|
|
|
delta_mm[Y_AXIS] = block->steps_y/settings.steps_per_mm[Y_AXIS];
|
|
|
|
delta_mm[Z_AXIS] = block->steps_z/settings.steps_per_mm[Z_AXIS];
|
2012-01-16 02:25:12 +01:00
|
|
|
block->millimeters = sqrt(delta_mm[X_AXIS]*delta_mm[X_AXIS] + delta_mm[Y_AXIS]*delta_mm[Y_AXIS] +
|
|
|
|
delta_mm[Z_AXIS]*delta_mm[Z_AXIS]);
|
2012-10-08 23:57:58 +02:00
|
|
|
float inverse_millimeters = 1.0/block->millimeters; // Inverse millimeters to remove multiple divides
|
2012-12-11 02:50:18 +01:00
|
|
|
|
|
|
|
// Compute path unit vector
|
|
|
|
float unit_vec[3];
|
|
|
|
unit_vec[X_AXIS] = delta_mm[X_AXIS]*inverse_millimeters;
|
|
|
|
unit_vec[Y_AXIS] = delta_mm[Y_AXIS]*inverse_millimeters;
|
|
|
|
unit_vec[Z_AXIS] = delta_mm[Z_AXIS]*inverse_millimeters;
|
|
|
|
|
|
|
|
// Compute direction bits and correct unit vector directions
|
|
|
|
block->direction_bits = 0;
|
|
|
|
if (target[X_AXIS] < pl.position[X_AXIS]) {
|
|
|
|
block->direction_bits |= (1<<X_DIRECTION_BIT);
|
|
|
|
unit_vec[X_AXIS] = -unit_vec[X_AXIS];
|
|
|
|
}
|
|
|
|
if (target[Y_AXIS] < pl.position[Y_AXIS]) {
|
|
|
|
block->direction_bits |= (1<<Y_DIRECTION_BIT);
|
|
|
|
unit_vec[Y_AXIS] = -unit_vec[Y_AXIS];
|
|
|
|
}
|
|
|
|
if (target[Z_AXIS] < pl.position[Z_AXIS]) {
|
|
|
|
block->direction_bits |= (1<<Z_DIRECTION_BIT);
|
|
|
|
unit_vec[Z_AXIS] = -unit_vec[Z_AXIS];
|
|
|
|
}
|
2011-09-18 13:36:55 +02:00
|
|
|
|
2012-12-12 01:42:29 +01:00
|
|
|
// Calculate speed in mm/minute and stepper rate for each axis. No divide by zero due to previous checks.
|
2011-09-18 13:36:55 +02:00
|
|
|
// NOTE: Minimum stepper speed is limited by MINIMUM_STEPS_PER_MINUTE in stepper.c
|
2012-12-08 23:00:58 +01:00
|
|
|
if (invert_feed_rate) { feed_rate = block->millimeters/feed_rate; }
|
2012-12-12 01:42:29 +01:00
|
|
|
block->nominal_speed_sqr = feed_rate*feed_rate; // (mm/min)^2. Always > 0
|
|
|
|
block->nominal_rate = ceil(feed_rate*(RANADE_MULTIPLIER/(60.0*ISR_TICKS_PER_SECOND))); // (mult*mm/isr_tic)
|
2012-12-08 23:00:58 +01:00
|
|
|
|
2012-12-11 02:50:18 +01:00
|
|
|
// TODO: When acceleration independence is installed, it can be kept in terms of 2*acceleration for
|
|
|
|
// each block. This could save some 2*acceleration multiplications elsewhere. Need to check though.
|
|
|
|
|
2012-12-12 01:42:29 +01:00
|
|
|
// Compute the acceleration and distance traveled per step event for the stepper algorithm.
|
2012-12-08 23:00:58 +01:00
|
|
|
block->rate_delta = ceil(settings.acceleration*
|
|
|
|
((RANADE_MULTIPLIER/(60*60))/(ISR_TICKS_PER_SECOND*ACCELERATION_TICKS_PER_SECOND))); // (mult*mm/isr_tic/accel_tic)
|
|
|
|
block->d_next = ceil((block->millimeters*RANADE_MULTIPLIER)/block->step_event_count); // (mult*mm/step)
|
2011-02-06 23:23:34 +01:00
|
|
|
|
2011-12-09 02:47:48 +01:00
|
|
|
// Compute maximum allowable entry speed at junction by centripetal acceleration approximation.
|
|
|
|
// Let a circle be tangent to both previous and current path line segments, where the junction
|
|
|
|
// deviation is defined as the distance from the junction to the closest edge of the circle,
|
|
|
|
// colinear with the circle center. The circular segment joining the two paths represents the
|
|
|
|
// path of centripetal acceleration. Solve for max velocity based on max acceleration about the
|
|
|
|
// radius of the circle, defined indirectly by junction deviation. This may be also viewed as
|
|
|
|
// path width or max_jerk in the previous grbl version. This approach does not actually deviate
|
|
|
|
// from path, but used as a robust way to compute cornering speeds, as it takes into account the
|
|
|
|
// nonlinearities of both the junction angle and junction velocity.
|
2012-12-12 01:42:29 +01:00
|
|
|
// NOTE: If the junction deviation value is finite, Grbl executes the motions in an exact path
|
|
|
|
// mode (G61). If the junction deviation value is zero, Grbl will execute the motion in an exact
|
|
|
|
// stop mode (G61.1) manner. In the future, if continuous mode (G64) is desired, the math here
|
|
|
|
// is exactly the same. Instead of motioning all the way to junction point, the machine will
|
|
|
|
// just follow the arc circle defined here. The Arduino doesn't have the CPU cycles to perform
|
|
|
|
// a continuous mode path, but ARM-based microcontrollers most certainly do.
|
|
|
|
float vmax_junction_sqr = MINIMUM_PLANNER_SPEED*MINIMUM_PLANNER_SPEED; // Set default max junction speed
|
2011-12-09 02:47:48 +01:00
|
|
|
|
|
|
|
// Skip first block or when previous_nominal_speed is used as a flag for homing and offset cycles.
|
2012-12-12 01:42:29 +01:00
|
|
|
if ((block_buffer_head != block_buffer_tail) && (pl.previous_nominal_speed_sqr > 0.0)) {
|
2011-12-09 02:47:48 +01:00
|
|
|
// Compute cosine of angle between previous and current path. (prev_unit_vec is negative)
|
|
|
|
// NOTE: Max junction velocity is computed without sin() or acos() by trig half angle identity.
|
2012-10-08 23:57:58 +02:00
|
|
|
float cos_theta = - pl.previous_unit_vec[X_AXIS] * unit_vec[X_AXIS]
|
2012-12-08 23:00:58 +01:00
|
|
|
- pl.previous_unit_vec[Y_AXIS] * unit_vec[Y_AXIS]
|
|
|
|
- pl.previous_unit_vec[Z_AXIS] * unit_vec[Z_AXIS] ;
|
|
|
|
|
2011-12-09 02:47:48 +01:00
|
|
|
// Skip and use default max junction speed for 0 degree acute junction.
|
|
|
|
if (cos_theta < 0.95) {
|
2012-12-12 01:42:29 +01:00
|
|
|
vmax_junction_sqr = min(pl.previous_nominal_speed_sqr,block->nominal_speed_sqr);
|
2011-12-09 02:47:48 +01:00
|
|
|
// Skip and avoid divide by zero for straight junctions at 180 degrees. Limit to min() of nominal speeds.
|
|
|
|
if (cos_theta > -0.95) {
|
|
|
|
// Compute maximum junction velocity based on maximum acceleration and junction deviation
|
2012-10-08 23:57:58 +02:00
|
|
|
float sin_theta_d2 = sqrt(0.5*(1.0-cos_theta)); // Trig half angle identity. Always positive.
|
2012-12-12 01:42:29 +01:00
|
|
|
vmax_junction_sqr = min(vmax_junction_sqr,
|
|
|
|
settings.acceleration * settings.junction_deviation * sin_theta_d2/(1.0-sin_theta_d2) );
|
2011-09-03 23:31:48 +02:00
|
|
|
}
|
|
|
|
}
|
2011-01-14 17:13:33 +01:00
|
|
|
}
|
2012-12-12 01:42:29 +01:00
|
|
|
block->max_entry_speed_sqr = vmax_junction_sqr;
|
2011-12-09 02:47:48 +01:00
|
|
|
|
2012-12-11 02:50:18 +01:00
|
|
|
// Initialize block entry speed. Compute block entry velocity backwards from user-defined MINIMUM_PLANNER_SPEED.
|
2012-12-12 01:42:29 +01:00
|
|
|
float v_allowable_sqr = MINIMUM_PLANNER_SPEED*MINIMUM_PLANNER_SPEED + 2*settings.acceleration*block->millimeters;
|
|
|
|
block->entry_speed_sqr = min(vmax_junction_sqr, v_allowable_sqr);
|
2011-12-09 02:47:48 +01:00
|
|
|
|
|
|
|
// Initialize planner efficiency flags
|
|
|
|
// Set flag if block will always reach maximum junction speed regardless of entry/exit speeds.
|
|
|
|
// If a block can de/ac-celerate from nominal speed to zero within the length of the block, then
|
|
|
|
// the current block and next block junction speeds are guaranteed to always be at their maximum
|
|
|
|
// junction speeds in deceleration and acceleration, respectively. This is due to how the current
|
|
|
|
// block nominal speed limits both the current and next maximum junction speeds. Hence, in both
|
|
|
|
// the reverse and forward planners, the corresponding block junction speed will always be at the
|
|
|
|
// the maximum junction speed and may always be ignored for any speed reduction checks.
|
2012-12-12 01:42:29 +01:00
|
|
|
if (block->nominal_speed_sqr <= v_allowable_sqr) { block->nominal_length_flag = true; }
|
2011-12-09 02:47:48 +01:00
|
|
|
else { block->nominal_length_flag = false; }
|
|
|
|
block->recalculate_flag = true; // Always calculate trapezoid for new block
|
|
|
|
|
2012-12-12 01:42:29 +01:00
|
|
|
// Update previous path unit_vector and nominal speed (squared)
|
2012-01-10 16:34:48 +01:00
|
|
|
memcpy(pl.previous_unit_vec, unit_vec, sizeof(unit_vec)); // pl.previous_unit_vec[] = unit_vec[]
|
2012-12-12 01:42:29 +01:00
|
|
|
pl.previous_nominal_speed_sqr = block->nominal_speed_sqr;
|
2011-12-09 02:47:48 +01:00
|
|
|
|
|
|
|
// Update buffer head and next buffer head indices
|
|
|
|
block_buffer_head = next_buffer_head;
|
|
|
|
next_buffer_head = next_block_index(block_buffer_head);
|
2011-01-14 16:56:44 +01:00
|
|
|
|
2012-01-10 16:34:48 +01:00
|
|
|
// Update planner position
|
|
|
|
memcpy(pl.position, target, sizeof(target)); // pl.position[] = target[]
|
2011-09-05 02:53:25 +02:00
|
|
|
|
2011-12-09 02:47:48 +01:00
|
|
|
planner_recalculate();
|
2011-01-14 16:56:44 +01:00
|
|
|
}
|
2011-02-20 00:29:56 +01:00
|
|
|
|
2012-02-11 19:59:35 +01:00
|
|
|
// Reset the planner position vector (in steps). Called by the system abort routine.
|
2012-01-29 04:41:08 +01:00
|
|
|
void plan_set_current_position(int32_t x, int32_t y, int32_t z)
|
|
|
|
{
|
|
|
|
pl.position[X_AXIS] = x;
|
|
|
|
pl.position[Y_AXIS] = y;
|
|
|
|
pl.position[Z_AXIS] = z;
|
|
|
|
}
|
|
|
|
|
2011-12-09 02:47:48 +01:00
|
|
|
// Re-initialize buffer plan with a partially completed block, assumed to exist at the buffer tail.
|
|
|
|
// Called after a steppers have come to a complete stop for a feed hold and the cycle is stopped.
|
|
|
|
void plan_cycle_reinitialize(int32_t step_events_remaining)
|
|
|
|
{
|
|
|
|
block_t *block = &block_buffer[block_buffer_tail]; // Point to partially completed block
|
|
|
|
|
|
|
|
// Only remaining millimeters and step_event_count need to be updated for planner recalculate.
|
|
|
|
// Other variables (step_x, step_y, step_z, rate_delta, etc.) all need to remain the same to
|
|
|
|
// ensure the original planned motion is resumed exactly.
|
|
|
|
block->millimeters = (block->millimeters*step_events_remaining)/block->step_event_count;
|
|
|
|
block->step_event_count = step_events_remaining;
|
|
|
|
|
|
|
|
// Re-plan from a complete stop. Reset planner entry speeds and flags.
|
2012-12-12 01:42:29 +01:00
|
|
|
block->entry_speed_sqr = 0.0;
|
|
|
|
block->max_entry_speed_sqr = 0.0;
|
2011-12-09 02:47:48 +01:00
|
|
|
block->nominal_length_flag = false;
|
|
|
|
block->recalculate_flag = true;
|
|
|
|
planner_recalculate();
|
|
|
|
}
|