Fixed minor bugs in planner. Increased max dwell time. Long slope bug stop-gap solution note.
- Fixed the planner TODO regarding minimum nominal speeds. Re-arranged calculations to be both more efficient and guaranteed to be greater than zero. - Missed a parenthesis location on the rate_delta calculation. Should fix a nearly in-perceptible issue with incorrect acceleration ramping in diagonal directions. - Increased maximum dwell time from 6.5sec to an 18hour max. A crazy amount more, but that's how the math works out. - Converted the internal feedrate values to mm/min only, as it was switching between mm/min to mm/sec and back to mm/min. Also added a feedrate > 0 check in gcode.c. - Identified the long slope at the end of rapid de/ac-celerations noted by stephanix. Problem with the numerical integration truncation error between the exact solution of estimate_acceleration_distance and how grbl actually performs the acceleration ramps discretely. Increasing the ACCELERATION_TICKS_PER_SECOND in config.h helps fix this problem. Investigating further.
This commit is contained in:
parent
110faae986
commit
6de805441f
5
config.h
5
config.h
@ -55,6 +55,11 @@
|
|||||||
|
|
||||||
// The temporal resolution of the acceleration management subsystem. Higher number
|
// The temporal resolution of the acceleration management subsystem. Higher number
|
||||||
// give smoother acceleration but may impact performance
|
// give smoother acceleration but may impact performance
|
||||||
|
// NOTE: Increasing this parameter will help remove the long slow motion bug at the end
|
||||||
|
// of very fast de/ac-celerations. This is due to the increased resolution of the
|
||||||
|
// acceleration steps that more accurately predicted by the planner exact integration
|
||||||
|
// of acceleration distance. An efficient solution to this bug is under investigation.
|
||||||
|
// In general, setting this parameter is high as your system will allow is suggested.
|
||||||
#define ACCELERATION_TICKS_PER_SECOND 40L
|
#define ACCELERATION_TICKS_PER_SECOND 40L
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
11
gcode.c
11
gcode.c
@ -88,8 +88,8 @@ static void select_plane(uint8_t axis_0, uint8_t axis_1, uint8_t axis_2)
|
|||||||
|
|
||||||
void gc_init() {
|
void gc_init() {
|
||||||
memset(&gc, 0, sizeof(gc));
|
memset(&gc, 0, sizeof(gc));
|
||||||
gc.feed_rate = settings.default_feed_rate/60;
|
gc.feed_rate = settings.default_feed_rate;
|
||||||
gc.seek_rate = settings.default_seek_rate/60;
|
gc.seek_rate = settings.default_seek_rate;
|
||||||
select_plane(X_AXIS, Y_AXIS, Z_AXIS);
|
select_plane(X_AXIS, Y_AXIS, Z_AXIS);
|
||||||
gc.absolute_mode = true;
|
gc.absolute_mode = true;
|
||||||
}
|
}
|
||||||
@ -180,13 +180,14 @@ uint8_t gc_execute_line(char *line) {
|
|||||||
unit_converted_value = to_millimeters(value);
|
unit_converted_value = to_millimeters(value);
|
||||||
switch(letter) {
|
switch(letter) {
|
||||||
case 'F':
|
case 'F':
|
||||||
|
if (unit_converted_value <= 0) { FAIL(STATUS_BAD_NUMBER_FORMAT); } // Must be greater than zero
|
||||||
if (gc.inverse_feed_rate_mode) {
|
if (gc.inverse_feed_rate_mode) {
|
||||||
inverse_feed_rate = unit_converted_value; // seconds per motion for this motion only
|
inverse_feed_rate = unit_converted_value; // seconds per motion for this motion only
|
||||||
} else {
|
} else {
|
||||||
if (gc.motion_mode == MOTION_MODE_SEEK) {
|
if (gc.motion_mode == MOTION_MODE_SEEK) {
|
||||||
gc.seek_rate = unit_converted_value/60;
|
gc.seek_rate = unit_converted_value;
|
||||||
} else {
|
} else {
|
||||||
gc.feed_rate = unit_converted_value/60; // millimeters pr second
|
gc.feed_rate = unit_converted_value; // millimeters per minute
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -213,7 +214,7 @@ uint8_t gc_execute_line(char *line) {
|
|||||||
// Perform any physical actions
|
// Perform any physical actions
|
||||||
switch (next_action) {
|
switch (next_action) {
|
||||||
case NEXT_ACTION_GO_HOME: mc_go_home(); clear_vector(gc.position); break;
|
case NEXT_ACTION_GO_HOME: mc_go_home(); clear_vector(gc.position); break;
|
||||||
case NEXT_ACTION_DWELL: mc_dwell(trunc(p*1000)); break;
|
case NEXT_ACTION_DWELL: mc_dwell(p); break;
|
||||||
case NEXT_ACTION_SET_COORDINATE_OFFSET:
|
case NEXT_ACTION_SET_COORDINATE_OFFSET:
|
||||||
mc_set_current_position(target[X_AXIS], target[Y_AXIS], target[Z_AXIS]);
|
mc_set_current_position(target[X_AXIS], target[Y_AXIS], target[Z_AXIS]);
|
||||||
break;
|
break;
|
||||||
|
@ -29,13 +29,21 @@
|
|||||||
#include "stepper.h"
|
#include "stepper.h"
|
||||||
#include "planner.h"
|
#include "planner.h"
|
||||||
|
|
||||||
#define N_ARC_CORRECTION 25 // (0-255) Number of iterations before arc trajectory correction
|
// Number of arc generation iterations with small angle approximation before exact arc
|
||||||
|
// trajectory correction. Value must be 1-255.
|
||||||
|
#define N_ARC_CORRECTION 25
|
||||||
|
|
||||||
|
|
||||||
void mc_dwell(uint32_t milliseconds)
|
// Execute dwell in seconds. Maximum time delay is > 18 hours, more than enough for any application.
|
||||||
|
void mc_dwell(double seconds)
|
||||||
{
|
{
|
||||||
st_synchronize();
|
uint16_t i = floor(seconds);
|
||||||
_delay_ms(milliseconds);
|
st_synchronize();
|
||||||
|
_delay_ms(floor(1000*(seconds-i))); // Delay millisecond remainder
|
||||||
|
while (i > 0) {
|
||||||
|
_delay_ms(1000); // Delay one second
|
||||||
|
i--;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute an arc in offset mode format. position == current xyz, target == target xyz,
|
// Execute an arc in offset mode format. position == current xyz, target == target xyz,
|
||||||
|
@ -44,8 +44,8 @@ void mc_arc(double *position, double *target, double *offset, uint8_t axis_0, ui
|
|||||||
uint8_t axis_linear, double feed_rate, uint8_t invert_feed_rate, double radius, uint8_t isclockwise);
|
uint8_t axis_linear, double feed_rate, uint8_t invert_feed_rate, double radius, uint8_t isclockwise);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Dwell for a couple of time units
|
// Dwell for a specific number of seconds
|
||||||
void mc_dwell(uint32_t milliseconds);
|
void mc_dwell(double seconds);
|
||||||
|
|
||||||
// Send the tool home (not implemented)
|
// Send the tool home (not implemented)
|
||||||
void mc_go_home();
|
void mc_go_home();
|
||||||
|
48
planner.c
48
planner.c
@ -48,8 +48,6 @@ static double previous_nominal_speed; // Nominal speed of previous path line s
|
|||||||
|
|
||||||
static uint8_t acceleration_manager_enabled; // Acceleration management active?
|
static uint8_t acceleration_manager_enabled; // Acceleration management active?
|
||||||
|
|
||||||
#define ONE_MINUTE_OF_MICROSECONDS 60000000.0
|
|
||||||
|
|
||||||
|
|
||||||
// Returns the index of the next block in the ring buffer
|
// Returns the index of the next block in the ring buffer
|
||||||
// NOTE: Removed modulo (%) operator, which uses an expensive divide and multiplication.
|
// NOTE: Removed modulo (%) operator, which uses an expensive divide and multiplication.
|
||||||
@ -62,8 +60,8 @@ static int8_t next_block_index(int8_t block_index) {
|
|||||||
|
|
||||||
// Returns the index of the previous block in the ring buffer
|
// Returns the index of the previous block in the ring buffer
|
||||||
static int8_t prev_block_index(int8_t block_index) {
|
static int8_t prev_block_index(int8_t block_index) {
|
||||||
if (block_index == 0) { block_index = BLOCK_BUFFER_SIZE-1; }
|
if (block_index == 0) { block_index = BLOCK_BUFFER_SIZE; }
|
||||||
else { block_index--; }
|
block_index--;
|
||||||
return(block_index);
|
return(block_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,9 +195,9 @@ static void planner_forward_pass() {
|
|||||||
// This converts the planner parameters to the data required by the stepper controller.
|
// This converts the planner parameters to the data required by the stepper controller.
|
||||||
static void calculate_trapezoid_for_block(block_t *block, double entry_factor, double exit_factor) {
|
static void calculate_trapezoid_for_block(block_t *block, double entry_factor, double exit_factor) {
|
||||||
|
|
||||||
block->initial_rate = ceil(block->nominal_rate*entry_factor);
|
block->initial_rate = ceil(block->nominal_rate*entry_factor); // (step/min)
|
||||||
block->final_rate = ceil(block->nominal_rate*exit_factor);
|
block->final_rate = ceil(block->nominal_rate*exit_factor); // (step/min)
|
||||||
int32_t acceleration_per_minute = block->rate_delta*ACCELERATION_TICKS_PER_SECOND*60.0;
|
int32_t acceleration_per_minute = block->rate_delta*ACCELERATION_TICKS_PER_SECOND*60.0; // (step/min^2)
|
||||||
int32_t accelerate_steps =
|
int32_t accelerate_steps =
|
||||||
ceil(estimate_acceleration_distance(block->initial_rate, block->nominal_rate, acceleration_per_minute));
|
ceil(estimate_acceleration_distance(block->initial_rate, block->nominal_rate, acceleration_per_minute));
|
||||||
int32_t decelerate_steps =
|
int32_t decelerate_steps =
|
||||||
@ -356,42 +354,38 @@ void plan_buffer_line(double x, double y, double z, double feed_rate, uint8_t in
|
|||||||
delta_mm[Z_AXIS] = (target[Z_AXIS]-position[Z_AXIS])/settings.steps_per_mm[Z_AXIS];
|
delta_mm[Z_AXIS] = (target[Z_AXIS]-position[Z_AXIS])/settings.steps_per_mm[Z_AXIS];
|
||||||
block->millimeters = sqrt(square(delta_mm[X_AXIS]) + square(delta_mm[Y_AXIS]) +
|
block->millimeters = sqrt(square(delta_mm[X_AXIS]) + square(delta_mm[Y_AXIS]) +
|
||||||
square(delta_mm[Z_AXIS]));
|
square(delta_mm[Z_AXIS]));
|
||||||
|
double inverse_millimeters = 1.0/block->millimeters; // Inverse millimeters to remove multiple divides
|
||||||
uint32_t microseconds;
|
|
||||||
|
// Calculate speed in mm/minute for each axis. No divide by zero due to previous checks.
|
||||||
|
// NOTE: Minimum stepper speed is limited by MINIMUM_STEPS_PER_MINUTE in stepper.c
|
||||||
|
double inverse_minute;
|
||||||
if (!invert_feed_rate) {
|
if (!invert_feed_rate) {
|
||||||
microseconds = lround((block->millimeters/feed_rate)*1000000);
|
inverse_minute = feed_rate * inverse_millimeters;
|
||||||
} else {
|
} else {
|
||||||
microseconds = lround(ONE_MINUTE_OF_MICROSECONDS/feed_rate);
|
inverse_minute = 1.0 / feed_rate;
|
||||||
}
|
}
|
||||||
|
block->nominal_speed = block->millimeters * inverse_minute; // (mm/min) Always > 0
|
||||||
|
block->nominal_rate = ceil(block->step_event_count * inverse_minute); // (step/min) Always > 0
|
||||||
|
|
||||||
// Calculate speed in mm/minute for each axis
|
|
||||||
double multiplier = 60.0*1000000.0/microseconds;
|
|
||||||
block->nominal_speed = block->millimeters * multiplier;
|
|
||||||
block->nominal_rate = ceil(block->step_event_count * multiplier);
|
|
||||||
|
|
||||||
// This is a temporary fix to avoid a situation where very low nominal_speeds would be rounded
|
|
||||||
// down to zero and cause a division by zero. TODO: Grbl deserves a less patchy fix for this problem
|
|
||||||
if (block->nominal_speed < 60.0) { block->nominal_speed = 60.0; }
|
|
||||||
|
|
||||||
// Compute the acceleration rate for the trapezoid generator. Depending on the slope of the line
|
// Compute the acceleration rate for the trapezoid generator. Depending on the slope of the line
|
||||||
// average travel per step event changes. For a line along one axis the travel per step event
|
// average travel per step event changes. For a line along one axis the travel per step event
|
||||||
// is equal to the travel/step in the particular axis. For a 45 degree line the steppers of both
|
// is equal to the travel/step in the particular axis. For a 45 degree line the steppers of both
|
||||||
// axes might step for every step event. Travel per step event is then sqrt(travel_x^2+travel_y^2).
|
// axes might step for every step event. Travel per step event is then sqrt(travel_x^2+travel_y^2).
|
||||||
// To generate trapezoids with contant acceleration between blocks the rate_delta must be computed
|
// To generate trapezoids with contant acceleration between blocks the rate_delta must be computed
|
||||||
// specifically for each line to compensate for this phenomenon:
|
// specifically for each line to compensate for this phenomenon:
|
||||||
double step_per_travel = block->step_event_count/block->millimeters; // Compute inverse to remove divide
|
// Convert universal acceleration for direction-dependent stepper rate change parameter
|
||||||
block->rate_delta = step_per_travel * ceil( // convert to: acceleration steps/min/acceleration_tick
|
block->rate_delta = ceil( block->step_event_count*inverse_millimeters *
|
||||||
settings.acceleration*60.0 / ACCELERATION_TICKS_PER_SECOND ); // acceleration mm/sec/sec per acceleration_tick
|
settings.acceleration*60.0 / ACCELERATION_TICKS_PER_SECOND ); // (step/min/acceleration_tick)
|
||||||
|
|
||||||
// Perform planner-enabled calculations
|
// Perform planner-enabled calculations
|
||||||
if (acceleration_manager_enabled) {
|
if (acceleration_manager_enabled) {
|
||||||
|
|
||||||
// Compute path unit vector
|
// Compute path unit vector
|
||||||
double unit_vec[3];
|
double unit_vec[3];
|
||||||
double inv_millimeters = 1.0/block->millimeters; // Inverse millimeters to remove multiple divides
|
|
||||||
unit_vec[X_AXIS] = delta_mm[X_AXIS]*inv_millimeters;
|
unit_vec[X_AXIS] = delta_mm[X_AXIS]*inverse_millimeters;
|
||||||
unit_vec[Y_AXIS] = delta_mm[Y_AXIS]*inv_millimeters;
|
unit_vec[Y_AXIS] = delta_mm[Y_AXIS]*inverse_millimeters;
|
||||||
unit_vec[Z_AXIS] = delta_mm[Z_AXIS]*inv_millimeters;
|
unit_vec[Z_AXIS] = delta_mm[Z_AXIS]*inverse_millimeters;
|
||||||
|
|
||||||
// Compute maximum allowable entry speed at junction by centripetal acceleration approximation.
|
// 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
|
// Let a circle be tangent to both previous and current path line segments, where the junction
|
||||||
|
Loading…
Reference in New Issue
Block a user