Fixed bug related to very very low feed rates.

- A very very low feed rate command like `G1 X100 F0.01`  would cause
some floating-point round-off error and freeze Grbl into an infinite
loop. To fix it, introduced a MINIMUM_FEED_RATE parameter in config.h
to ensure motions always complete.

- MINIMUM_FEED_RATE is set at 1.0 mm/min by default. It’s recommended
that no rates are below this value, but 0.1mm/min may be ok in some
situations.
This commit is contained in:
Sonny Jeon 2014-08-05 16:17:45 -06:00
parent 955a9f3cf8
commit 9b9abf1b2f
4 changed files with 9 additions and 29 deletions

View File

@ -167,6 +167,11 @@
// should not be much greater than zero or to the minimum value necessary for the machine to work. // should not be much greater than zero or to the minimum value necessary for the machine to work.
#define MINIMUM_JUNCTION_SPEED 0.0 // (mm/min) #define MINIMUM_JUNCTION_SPEED 0.0 // (mm/min)
// Sets the minimum feed rate the planner will allow. Any value below it will be set to this minimum
// value. This also ensures that a planned motion always completes and accounts for any floating-point
// round-off errors. Recommend a value no lower than 1.0.
#define MINIMUM_FEED_RATE 1.0 // (mm/min)
// Number of arc generation iterations by small angle approximation before exact arc trajectory // Number of arc generation iterations by small angle approximation before exact arc trajectory
// correction with expensive sin() and cos() calcualtions. This parameter maybe decreased if there // correction with expensive sin() and cos() calcualtions. This parameter maybe decreased if there
// are issues with the accuracy of the arc generations, or increased if arc execution is getting // are issues with the accuracy of the arc generations, or increased if arc execution is getting

View File

@ -309,6 +309,7 @@ uint8_t plan_check_full_buffer()
// TODO: Need to distinguish a rapids vs feed move for overrides. Some flag of some sort. // TODO: Need to distinguish a rapids vs feed move for overrides. Some flag of some sort.
if (feed_rate < 0) { feed_rate = SOME_LARGE_VALUE; } // Scaled down to absolute max/rapids rate later if (feed_rate < 0) { feed_rate = SOME_LARGE_VALUE; } // Scaled down to absolute max/rapids rate later
else if (invert_feed_rate) { feed_rate = block->millimeters/feed_rate; } else if (invert_feed_rate) { feed_rate = block->millimeters/feed_rate; }
if (feed_rate < MINIMUM_FEED_RATE) { feed_rate = MINIMUM_FEED_RATE; } // Prevents step generation round-off condition.
// Calculate the unit vector of the line move and the block maximum feed rate and acceleration scaled // Calculate the unit vector of the line move and the block maximum feed rate and acceleration scaled
// down such that no individual axes maximum values are exceeded with respect to the line direction. // down such that no individual axes maximum values are exceeded with respect to the line direction.

View File

@ -24,7 +24,7 @@
#define GRBL_VERSION "0.9g" #define GRBL_VERSION "0.9g"
#define GRBL_VERSION_BUILD "20140804" #define GRBL_VERSION_BUILD "20140805"
// Version of the EEPROM data. Will be used to migrate existing data from older versions of Grbl // Version of the EEPROM data. Will be used to migrate existing data from older versions of Grbl
// when firmware is upgraded. Always stored in byte 0 of eeprom // when firmware is upgraded. Always stored in byte 0 of eeprom

View File

@ -637,7 +637,6 @@ void st_prep_buffer()
prep.maximum_speed = prep.exit_speed; prep.maximum_speed = prep.exit_speed;
} }
} }
} }
// Initialize new segment // Initialize new segment
@ -688,6 +687,8 @@ void st_prep_buffer()
break; break;
case RAMP_CRUISE: case RAMP_CRUISE:
// NOTE: mm_var used to retain the last mm_remaining for incomplete segment time_var calculations. // NOTE: mm_var used to retain the last mm_remaining for incomplete segment time_var calculations.
// NOTE: If maximum_speed*time_var value is too low, round-off can cause mm_var to not change. To
// prevent this, simply enforce a minimum speed threshold in the planner.
mm_var = mm_remaining - prep.maximum_speed*time_var; mm_var = mm_remaining - prep.maximum_speed*time_var;
if (mm_var < prep.decelerate_after) { // End of cruise. if (mm_var < prep.decelerate_after) { // End of cruise.
// Cruise-deceleration junction or end of block. // Cruise-deceleration junction or end of block.
@ -853,30 +854,3 @@ void st_prep_buffer()
return 0.0f; return 0.0f;
} }
#endif #endif
/*
TODO: With feedrate overrides, increases to the override value will not significantly
change the current planner and stepper operation. When the value increases, we simply
need to recompute the block plan with new nominal speeds and maximum junction velocities.
However with a decreasing feedrate override, this gets a little tricky. The current block
plan is optimal, so if we try to reduce the feed rates, it may be impossible to create
a feasible plan at its current operating speed and decelerate down to zero at the end of
the buffer. We first have to enforce a deceleration to meet and intersect with the reduced
feedrate override plan. For example, if the current block is cruising at a nominal rate
and the feedrate override is reduced, the new nominal rate will now be lower. The velocity
profile must first decelerate to the new nominal rate and then follow on the new plan.
Another issue is whether or not a feedrate override reduction causes a deceleration
that acts over several planner blocks. For example, say that the plan is already heavily
decelerating throughout it, reducing the feedrate override will not do much to it. So,
how do we determine when to resume the new plan? One solution is to tie into the feed hold
handling code to enforce a deceleration, but check when the current speed is less than or
equal to the block maximum speed and is in an acceleration or cruising ramp. At this
point, we know that we can recompute the block velocity profile to meet and continue onto
the new block plan.
One "easy" way to do this is to have the step segment buffer enforce a deceleration and
continually re-plan the planner buffer until the plan becomes feasible. This can work
and may be easy to implement, but it expends a lot of CPU cycles and may block out the
rest of the functions from operating at peak efficiency. Still the question is how do
we know when the plan is feasible in the context of what's already in the code and not
require too much more code?
*/