yarf 0.1
Yet Another RepRap Firmware
src/movement/planner_lookahead.c
Go to the documentation of this file.
00001 /*
00002  * planner_lookahead.c
00003  *
00004  * Copyright 2011 Pieter Agten
00005  *
00006  * This file is part of Yarf.
00007  *
00008  * Yarf is free software: you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation, either version 3 of the License, or
00011  * (at your option) any later version.
00012  *
00013  * Yarf is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  * GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License
00019  * along with Yarf.  If not, see <http://www.gnu.org/licenses/>.
00020  */
00021 
00035 #include "planner_lookahead.h"
00036 
00037 #include "yarf.h"
00038 #include "planner.h"
00039 #include "planner_queue.h"
00040 #include "util/math.h"
00041 
00042 #include <stdint.h>
00043 #include <string.h>
00044 
00045 
00046 #if PLANNER_LOOKAHEAD
00047 
00051 static struct {
00052   float direction[NUM_AXES]; 
00053   float plateau_speed; 
00054 } previous_block_info;
00055 
00056 
00057 
00058 
00059 
00091 static speed_t 
00092 max_allowable_speed(speed_t target_speed, float distance_mm)
00093 {
00094   return sqrt(lsquare(target_speed)+2*ACCELERATION*distance_mm);
00095 }
00096 
00097 
00098 
00116 static void 
00117 reverse_pass_kernel(block_t *current, block_t *next)
00118 {
00119   if (! current) { return; }
00120   
00121   if (next) { 
00122     // If entry speed is already at the maximum entry speed, no need to recheck. Block is cruising.
00123     // If not, block in state of acceleration or deceleration. Reset entry speed to maximum and 
00124     // check for maximum allowable speed reductions to ensure maximum possible planned speed.
00125     if (current->entry_speed < current->max_entry_speed) {
00126       if (current->plateau_speed_always_reachable || current->max_entry_speed < next->entry_speed) {
00127         current->entry_speed = current->max_entry_speed;
00128       } else {
00129         current->entry_speed = MIN(current->max_entry_speed, max_allowable_speed(next->entry_speed,current->distance_mm));
00130       }
00131       current->recalculate = true;    
00132     }
00133   } // Skip last block. Already initialized and set for recalculation.
00134 }
00135 
00136 
00144 static void
00145 reverse_pass(void)
00146 {
00147   uint8_t block_index = plan_q_head_index();
00148   block_t *block[2] = {NULL, NULL};
00149   while(block_index != plan_q_tail_index()) {
00150     block_index = plan_q_prev_index(block_index);
00151     block[1] = block[0];
00152     block[0] = plan_q_get(block_index);
00153     reverse_pass_kernel(block[0], block[1]);
00154   }
00155 }
00156 
00157 
00171 static void
00172 forward_pass_kernel(block_t *previous, block_t *current)
00173 {
00174   if (! previous) { return; }  // Begin planning after buffer_tail
00175   
00176   // If the previous block is an acceleration block, but it is not long enough to complete the
00177   // full speed change within the block, we need to adjust the entry speed accordingly. Entry
00178   // speeds have already been reset, maximized, and reverse planned by reverse planner.
00179   // If nominal length is true, max junction speed is guaranteed to be reached. No need to recheck.  
00180   if (! previous->plateau_speed_always_reachable) {
00181     if (previous->entry_speed < current->entry_speed) {
00182       speed_t max_obtainable_speed = max_allowable_speed(previous->entry_speed,previous->distance_mm);
00183 
00184       // Check for junction speed change
00185       if (max_obtainable_speed < current->entry_speed) {
00186         current->entry_speed = max_obtainable_speed;
00187         current->recalculate = true;
00188       }
00189     }
00190   }
00191 }
00192 
00193 
00194 
00195 
00203 static void 
00204 forward_pass(void)
00205 {
00206   uint8_t block_index = plan_q_tail_index();
00207   block_t *block[2] = {NULL, NULL};
00208   while(block_index != plan_q_head_index()) {
00209     block[0] = block[1];
00210     block[1] = plan_q_get(block_index);
00211     forward_pass_kernel(block[0],block[1]);
00212     block_index = plan_q_next_index(block_index);
00213   }
00214 }
00215 
00216                                                                           
00236 static void
00237 recalculate_trapezoids(void)
00238 {
00239   uint8_t block_index = plan_q_tail_index();
00240   block_t *current;
00241   block_t *next = NULL;
00242   
00243   while(block_index != plan_q_head_index()) {
00244     current = next;
00245     next = plan_q_get(block_index);
00246     if (current) {
00247       // Recalculate if current block entry or exit junction speed has changed.
00248       if (current->recalculate || next->recalculate) {
00249         plan_calculate_trapezoid(current, current->entry_speed, next->entry_speed);
00250         current->recalculate = false;
00251       }
00252     }
00253     block_index = plan_q_next_index(block_index);
00254   }
00255   // Last/newest block in buffer. Exit speed is set with START_FEED_RATE. Always recalculated.
00256   plan_calculate_trapezoid(next, next->entry_speed, MIN(next->plateau_speed, START_FEED_RATE));
00257   next->recalculate = false;
00258 }
00259 
00260 
00268 static inline void 
00269 recalculate_motion_plan(void) {     
00270   reverse_pass();
00271   forward_pass();
00272   recalculate_trapezoids();
00273 }
00274 
00275 
00276 void
00277 plan_lookahead_init()
00278 {
00279   previous_block_info.plateau_speed = 0;
00280 }
00281 
00282 
00283 void
00284 plan_lookahead()
00285 {
00286   block_t *b = plan_q_get(plan_q_prev_index(plan_q_head_index()));
00287   // Compute maximum allowable entry speed at junction by centripetal acceleration approximation.
00288   // Let a circle be tangent to both previous and current path line segments, where the junction 
00289   // deviation is defined as the distance from the junction to the closest edge of the circle, 
00290   // colinear with the circle center. The circular segment joining the two paths represents the 
00291   // path of centripetal acceleration. Solve for max velocity based on max acceleration about the
00292   // radius of the circle, defined indirectly by junction deviation. This may be also viewed as 
00293   // path width or max_jerk in the previous grbl version. This approach does not actually deviate 
00294   // from path, but used as a robust way to compute cornering speeds, as it takes into account the
00295   // nonlinearities of both the junction angle and junction velocity.
00296   const speed_t start_feed_rate = MIN(b->plateau_speed, START_FEED_RATE);
00297   speed_t max_junction_speed = start_feed_rate; // Set default conservative max junction speed
00298 
00299   // Skip first block
00300   if (plan_q_size() >= 2) {
00301     // Make sure the junction speed will not be higher than the previous plateau speed.
00302     max_junction_speed = MIN(max_junction_speed, previous_block_info.plateau_speed);
00303     // Compute cosine of angle between previous and current path.
00304     // NOTE: Max junction velocity is computed without sin() or acos() by trig half angle identity.
00305     float cos_theta = -(previous_block_info.direction[X_AXIS] * b->direction[X_AXIS] +
00306                         previous_block_info.direction[Y_AXIS] * b->direction[Y_AXIS] +
00307                         previous_block_info.direction[Z_AXIS] * b->direction[Z_AXIS] +
00308                         previous_block_info.direction[E_AXIS] * b->direction[E_AXIS]) ;
00309 
00310     // Skip and use default max junction speed for 0 degree acute junction.
00311     if (cos_theta < 0.95) { 
00312       // Skip and avoid divide by zero for straight junctions at 180 degrees.
00313       if (cos_theta > -0.95) { // -0.95 < cos_theta < 0.95
00314         // Compute maximum junction velocity based on maximum acceleration and junction deviation
00315         float sin_theta_d2 = sqrt(0.5*(1.0-cos_theta)); // Trig half angle identity. Always positive.
00316         max_junction_speed = MIN(max_junction_speed, sqrt(ACCELERATION * JUNCTION_DEVIATION * sin_theta_d2/(1.0-sin_theta_d2)) );
00317       } // else { very unsharp corner }
00318     } // else { very sharp corner } 
00319   }
00320   b->max_entry_speed = max_junction_speed;
00321 
00322   // Calculate the maximum allowable speed to still be able to decelerate to START_FEED_RATE over
00323   // the length of the block.
00324   speed_t max_allowable_speed_ = max_allowable_speed(start_feed_rate,b->distance_mm);
00325   b->entry_speed = MIN(max_junction_speed, max_allowable_speed_);
00326 
00327   // Initialize planner efficiency flags
00328   // Set flag if block will always reach maximum junction speed regardless of entry/exit speeds.
00329   // If a block can decelerate from nominal speed to minimum speed within the length of the block, then
00330   // the current block and next block junction speeds are guaranteed to always be at their maximum
00331   // junction speeds in deceleration and acceleration, respectively. This is due to how the current
00332   // block nominal speed limits both the current and next maximum junction speeds. Hence, in both
00333   // the reverse and forward planners, the corresponding block junction speed will always be at the
00334   // the maximum junction speed and may always be ignored for any speed reduction checks.
00335   b->plateau_speed_always_reachable = (b->plateau_speed <= max_allowable_speed_);
00336   b->recalculate = true; // Always calculate trapezoid for new block
00337   
00338   // Update previous path unit_vector and nominal speed
00339   memcpy(previous_block_info.direction, b->direction, sizeof(b->direction));
00340   previous_block_info.plateau_speed = b->plateau_speed;
00341 
00342   recalculate_motion_plan();
00343 }
00344 
00345 #endif //PLANNER_LOOKAHEAD
00346 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines