Make lookahead basically working.

This means, modify existing code to let the lookahead algorithms
do their work. It also means to remove some unused code in
dda_lookahead.c and reordering some code to make it work with
LOOKAHEAD undefined.
This commit is contained in:
Markus Hitter 2013-03-23 22:18:34 +01:00
parent e04b69b9c6
commit 1aca61c277
6 changed files with 105 additions and 18 deletions

71
dda.c
View File

@ -10,6 +10,7 @@
#include <avr/interrupt.h> #include <avr/interrupt.h>
#include "dda_maths.h" #include "dda_maths.h"
#include "dda_lookahead.h"
#include "timer.h" #include "timer.h"
#include "serial.h" #include "serial.h"
#include "sermsg.h" #include "sermsg.h"
@ -82,9 +83,13 @@ void dda_new_startpoint(void) {
This algorithm is probably the main limiting factor to print speed in terms of firmware limitations This algorithm is probably the main limiting factor to print speed in terms of firmware limitations
*/ */
void dda_create(DDA *dda, TARGET *target) { void dda_create(DDA *dda, TARGET *target, DDA *prev_dda) {
uint32_t steps, x_delta_um, y_delta_um, z_delta_um, e_delta_um; uint32_t steps, x_delta_um, y_delta_um, z_delta_um, e_delta_um;
uint32_t distance, c_limit, c_limit_calc; uint32_t distance, c_limit, c_limit_calc;
#ifdef LOOKAHEAD
// Number the moves to identify them; allowed to overflow.
static uint8_t idcnt = 0;
#endif
// initialise DDA to a known state // initialise DDA to a known state
dda->allflags = 0; dda->allflags = 0;
@ -95,6 +100,15 @@ void dda_create(DDA *dda, TARGET *target) {
// we end at the passed target // we end at the passed target
memcpy(&(dda->endpoint), target, sizeof(TARGET)); memcpy(&(dda->endpoint), target, sizeof(TARGET));
#ifdef LOOKAHEAD
// Set the start and stop speeds to zero for now = full stops between
// moves. Also fallback if lookahead calculations fail to finish in time.
dda->F_start = 0;
dda->F_end = 0;
// Give this move an identifier.
dda->id = idcnt++;
#endif
// TODO TODO: We should really make up a loop for all axes. // TODO TODO: We should really make up a loop for all axes.
// Think of what happens when a sixth axis (multi colour extruder) // Think of what happens when a sixth axis (multi colour extruder)
// appears? // appears?
@ -129,6 +143,14 @@ void dda_create(DDA *dda, TARGET *target) {
dda->e_direction = (target->E >= startpoint.E)?1:0; dda->e_direction = (target->E >= startpoint.E)?1:0;
} }
#ifdef LOOKAHEAD
// Also displacements in micrometers, but for the lookahead alogrithms.
dda->delta.X = target->X - startpoint.X;
dda->delta.Y = target->Y - startpoint.Y;
dda->delta.Z = target->Z - startpoint.Z;
dda->delta.E = target->e_relative ? target->E : target->E - startpoint.E;
#endif
if (DEBUG_DDA && (debug_flags & DEBUG_DDA)) if (DEBUG_DDA && (debug_flags & DEBUG_DDA))
sersendf_P(PSTR("%ld,%ld,%ld,%ld] ["), target->X - startpoint.X, target->Y - startpoint.Y, target->Z - startpoint.Z, target->E - startpoint.E); sersendf_P(PSTR("%ld,%ld,%ld,%ld] ["), target->X - startpoint.X, target->Y - startpoint.Y, target->Z - startpoint.Z, target->E - startpoint.E);
@ -285,13 +307,43 @@ void dda_create(DDA *dda, TARGET *target) {
dda->c_min = c_limit; dda->c_min = c_limit;
// This section is plain wrong, like in it's only half of what we need. This factor 960000 is dependant on STEPS_PER_MM. // This section is plain wrong, like in it's only half of what we need. This factor 960000 is dependant on STEPS_PER_MM.
// overflows at target->F > 65535; factor 16. found by try-and-error; will overshoot target speed a bit // overflows at target->F > 65535; factor 16. found by try-and-error; will overshoot target speed a bit
dda->rampup_steps = target->F * target->F / (uint32_t)(STEPS_PER_M_X * ACCELERATION / 960000.); //dda->rampup_steps = target->F * target->F / (uint32_t)(STEPS_PER_M_X * ACCELERATION / 960000.);
//sersendf_P(PSTR("rampup calc %lu\n"), dda->rampup_steps); //sersendf_P(PSTR("rampup calc %lu\n"), dda->rampup_steps);
dda->rampup_steps = 100000; // replace mis-calculation by a safe value //dda->rampup_steps = 100000; // replace mis-calculation by a safe value
// End of wrong section. // End of wrong section.
if (dda->rampup_steps > dda->total_steps / 2) /**
dda->rampup_steps = dda->total_steps / 2; Assuming: F is in mm/min, STEPS_PER_M_X is in steps/m, ACCELERATION is in mm/s²
dda->rampdown_steps = dda->total_steps - dda->rampup_steps; Given:
- Velocity v at time t given acceleration a: v(t) = a*t
- Displacement s at time t given acceleration a: s(t) = 1/2 * a * t²
- Displacement until reaching target velocity v: s = 1/2 * (v² / a)
- Final result: steps needed to reach velocity v given acceleration a:
steps = (STEPS_PER_M_X * F^2) / (7200000 * ACCELERATION)
To keep precision, break up in floating point and integer part:
F^2 * (int)(STEPS_PER_M_X / (7200000 * ACCELERATION))
Note: the floating point part is static so its calculated during compilation.
Note 2: the floating point part will be smaller than one, invert it:
steps = F^2 / (int)((7200000 * ACCELERATION) / STEPS_PER_M_X)
Note 3: As mentioned, setting F to 65535 or larger will overflow the
calculation. Make sure this does not happen.
Note 4: Anyone trying to run their machine at 65535 mm/min > 1m/s is nuts
*/
if (target->F > 65534) target->F = 65534;
dda->rampup_steps = ACCELERATE_RAMP_LEN(target->F);
// Quick hack: we do not do Z move joins as jerk on the Z axis is undesirable;
// as the ramp length is calculated for XY, its incorrect for Z: apply the original
// 'fix' to simply specify a large enough ramp for any speed.
if (x_delta_um == 0 && y_delta_um == 0) {
dda->rampup_steps = 100000; // replace mis-calculation by a safe value
}
if (dda->rampup_steps > dda->total_steps / 2)
dda->rampup_steps = dda->total_steps / 2;
dda->rampdown_steps = dda->total_steps - dda->rampup_steps;
#ifdef LOOKAHEAD
dda_join_moves(prev_dda, dda);
#endif
#elif defined ACCELERATION_TEMPORAL #elif defined ACCELERATION_TEMPORAL
// TODO: limit speed of individual axes to MAXIMUM_FEEDRATE // TODO: limit speed of individual axes to MAXIMUM_FEEDRATE
// TODO: calculate acceleration/deceleration for each axis // TODO: calculate acceleration/deceleration for each axis
@ -617,6 +669,8 @@ void dda_step(DDA *dda) {
move_state.n += 4; move_state.n += 4;
// be careful of signedness! // be careful of signedness!
move_state.c = (int32_t)move_state.c - ((int32_t)(move_state.c * 2) / (int32_t)move_state.n); move_state.c = (int32_t)move_state.c - ((int32_t)(move_state.c * 2) / (int32_t)move_state.n);
//sersendf_P(PSTR("n:%ld; c:%ld; steps: %ld / %lu\n"), move_state.n,
// move_state.c, move_state.step_no, move_state.y_steps);
} }
move_state.step_no++; move_state.step_no++;
// Print the number of steps actually needed for ramping up // Print the number of steps actually needed for ramping up
@ -687,6 +741,11 @@ void dda_step(DDA *dda) {
if (move_state.x_steps == 0 && move_state.y_steps == 0 && if (move_state.x_steps == 0 && move_state.y_steps == 0 &&
move_state.z_steps == 0 && move_state.e_steps == 0) { move_state.z_steps == 0 && move_state.e_steps == 0) {
dda->live = 0; dda->live = 0;
#ifdef LOOKAHEAD
// If look-ahead was using this move, it could have missed our activation:
// make sure the ids do not match.
dda->id--;
#endif
#ifdef DC_EXTRUDER #ifdef DC_EXTRUDER
heater_set(DC_EXTRUDER, 0); heater_set(DC_EXTRUDER, 0);
#endif #endif

33
dda.h
View File

@ -15,6 +15,9 @@
types types
*/ */
// Enum to denote an axis
enum axis_e { X, Y, Z, E };
/** /**
\struct TARGET \struct TARGET
\brief target is simply a point in space/time \brief target is simply a point in space/time
@ -34,6 +37,19 @@ typedef struct {
uint8_t e_relative :1; ///< bool: e axis relative? Overrides all_relative uint8_t e_relative :1; ///< bool: e axis relative? Overrides all_relative
} TARGET; } TARGET;
/**
\struct VECTOR4D
\brief 4 dimensional vector used to describe the difference between moves.
Units are in micrometers and usually based off 'TARGET'.
*/
typedef struct {
int32_t X;
int32_t Y;
int32_t Z;
int32_t E;
} VECTOR4D;
/** /**
\struct MOVE_STATE \struct MOVE_STATE
\brief this struct is made for tracking the current state of the movement \brief this struct is made for tracking the current state of the movement
@ -128,6 +144,21 @@ typedef struct {
uint32_t rampdown_steps; uint32_t rampdown_steps;
/// 24.8 fixed point timer value, maximum speed /// 24.8 fixed point timer value, maximum speed
uint32_t c_min; uint32_t c_min;
#ifdef LOOKAHEAD
// With the look-ahead functionality, it is possible to retain physical
// movement between G1 moves. These variables keep track of the entry and
// exit speeds between moves.
uint32_t F_start;
uint32_t F_end;
// Displacement vector, in um, based between the difference of the starting
// point and the target. Required to obtain the jerk between 2 moves.
// Note: x_delta and co are in steps, not um.
VECTOR4D delta;
// Number the moves to be able to test at the end of lookahead if the moves
// are the same. Note: we do not need a lot of granularity here: more than
// MOVEBUFFER_SIZE is already enough.
uint8_t id;
#endif
#endif #endif
#ifdef ACCELERATION_TEMPORAL #ifdef ACCELERATION_TEMPORAL
uint32_t x_step_interval; ///< time between steps on X axis uint32_t x_step_interval; ///< time between steps on X axis
@ -166,7 +197,7 @@ void dda_init(void);
void dda_new_startpoint(void); void dda_new_startpoint(void);
// create a DDA // create a DDA
void dda_create(DDA *dda, TARGET *target); void dda_create(DDA *dda, TARGET *target, DDA *prev_dda);
// start a created DDA (called from timer interrupt) // start a created DDA (called from timer interrupt)
void dda_start(DDA *dda) __attribute__ ((hot)); void dda_start(DDA *dda) __attribute__ ((hot));

View File

@ -25,7 +25,6 @@
#include "debug.h" #include "debug.h"
#include "sersendf.h" #include "sersendf.h"
#include "pinio.h" #include "pinio.h"
#include "config.h"
extern uint8_t use_lookahead; extern uint8_t use_lookahead;
@ -144,7 +143,7 @@ void dda_emergency_shutdown(PGM_P msg) {
serial_writestr_P(PSTR("error: emergency stop:")); serial_writestr_P(PSTR("error: emergency stop:"));
if(msg!=NULL) serial_writestr_P(msg); if(msg!=NULL) serial_writestr_P(msg);
serial_writestr_P(PSTR("\r\n")); serial_writestr_P(PSTR("\r\n"));
delay(20000); // Delay so the buffer can be flushed - otherwise the message is never sent delay_ms(20); // Delay so the buffer can be flushed - otherwise the message is never sent
timer_stop(); timer_stop();
queue_flush(); queue_flush();
power_off(); power_off();
@ -168,8 +167,6 @@ void dda_emergency_shutdown(PGM_P msg) {
* last move (= 'current'); as a result a lot of small moves will still limit the speed. * last move (= 'current'); as a result a lot of small moves will still limit the speed.
*/ */
void dda_join_moves(DDA *prev, DDA *current) { void dda_join_moves(DDA *prev, DDA *current) {
// Run-time option: only proceed if we are enabled.
if(use_lookahead==0) return;
// Calculating the look-ahead settings can take a while; before modifying // Calculating the look-ahead settings can take a while; before modifying
// the previous move, we need to locally store any values and write them // the previous move, we need to locally store any values and write them
@ -181,7 +178,6 @@ void dda_join_moves(DDA *prev, DDA *current) {
// Note: we assume 'current' will not be dispatched while this function runs, so we do not to // Note: we assume 'current' will not be dispatched while this function runs, so we do not to
// back up the move settings: they will remain constant. // back up the move settings: they will remain constant.
uint32_t this_F_start, this_rampup, this_rampdown; uint32_t this_F_start, this_rampup, this_rampdown;
enum axis_e prev_lead;
int32_t jerk, jerk_e; // Expresses the forces if we would change directions at full speed int32_t jerk, jerk_e; // Expresses the forces if we would change directions at full speed
static uint32_t la_cnt = 0; // Counter: how many moves did we join? static uint32_t la_cnt = 0; // Counter: how many moves did we join?
#ifdef LOOKAHEAD_DEBUG #ifdef LOOKAHEAD_DEBUG
@ -224,7 +220,6 @@ void dda_join_moves(DDA *prev, DDA *current) {
prev_rampup = prev->rampup_steps; prev_rampup = prev->rampup_steps;
prev_rampdown = prev->rampdown_steps; prev_rampdown = prev->rampdown_steps;
prev_total_steps = prev->total_steps; prev_total_steps = prev->total_steps;
prev_lead = prev->lead;
} }
// The initial crossing speed is the minimum between both target speeds // The initial crossing speed is the minimum between both target speeds

View File

@ -27,8 +27,6 @@
#error "Look-ahead requires steps per m to be identical on the X and Y axis (for now)" #error "Look-ahead requires steps per m to be identical on the X and Y axis (for now)"
#endif #endif
// Note: the floating point bit is optimized away during compilation
#define ACCELERATE_RAMP_LEN(speed) (((speed)*(speed)) / (uint32_t)((7200000.0f * ACCELERATION) / (float)STEPS_PER_M_X))
// This is the same to ACCELERATE_RAMP_LEN but now the steps per m can be switched. // This is the same to ACCELERATE_RAMP_LEN but now the steps per m can be switched.
// Note: use this with a macro so the float is removed by the preprocessor // Note: use this with a macro so the float is removed by the preprocessor
#define ACCELERATE_RAMP_SCALER(spm) (uint32_t)((7200000.0f * ACCELERATION) / (float)spm) #define ACCELERATE_RAMP_SCALER(spm) (uint32_t)((7200000.0f * ACCELERATION) / (float)spm)

View File

@ -63,4 +63,7 @@ uint16_t int_sqrt(uint32_t a);
// 2 ^ msbloc(v) >= v // 2 ^ msbloc(v) >= v
const uint8_t msbloc (uint32_t v); const uint8_t msbloc (uint32_t v);
// Note: the floating point bit is optimized away during compilation
#define ACCELERATE_RAMP_LEN(speed) (((speed)*(speed)) / (uint32_t)((7200000.0f * ACCELERATION) / (float)STEPS_PER_M_X))
#endif /* _DDA_MATHS_H */ #endif /* _DDA_MATHS_H */

View File

@ -100,9 +100,10 @@ void enqueue_home(TARGET *t, uint8_t endstop_check, uint8_t endstop_stop_cond) {
h &= (MOVEBUFFER_SIZE - 1); h &= (MOVEBUFFER_SIZE - 1);
DDA* new_movebuffer = &(movebuffer[h]); DDA* new_movebuffer = &(movebuffer[h]);
DDA* prev_movebuffer = (queue_empty() != 0) ? NULL : &movebuffer[mb_head];
if (t != NULL) {
dda_create(new_movebuffer, t); if (t != NULL) {
dda_create(new_movebuffer, t, prev_movebuffer);
new_movebuffer->endstop_check = endstop_check; new_movebuffer->endstop_check = endstop_check;
new_movebuffer->endstop_stop_cond = endstop_stop_cond; new_movebuffer->endstop_stop_cond = endstop_stop_cond;
} }