Browse Source

Fix planner block optimization

- Fixed the planner incorrectly avoiding optimization of the block following the active one.
- Added extra conditions to terminate planner early and avoid redundant computations.
etagle 7 years ago
parent
commit
a4af975873
2 changed files with 182 additions and 78 deletions
  1. 160
    63
      Marlin/src/module/planner.cpp
  2. 22
    15
      Marlin/src/module/planner.h

+ 160
- 63
Marlin/src/module/planner.cpp View File

@@ -107,7 +107,8 @@ block_t Planner::block_buffer[BLOCK_BUFFER_SIZE];
107 107
 volatile uint8_t Planner::block_buffer_head,  // Index of the next block to be pushed
108 108
                  Planner::block_buffer_tail;  // Index of the busy block, if any
109 109
 uint16_t Planner::cleaning_buffer_counter;    // A counter to disable queuing of blocks
110
-uint8_t Planner::delay_before_delivering;     // This counter delays delivery of blocks when queue becomes empty to allow the opportunity of merging blocks
110
+uint8_t Planner::delay_before_delivering,     // This counter delays delivery of blocks when queue becomes empty to allow the opportunity of merging blocks
111
+        Planner::block_buffer_planned;        // Index of the optimally planned block
111 112
 
112 113
 float Planner::max_feedrate_mm_s[XYZE_N],   // Max speeds in mm per second
113 114
       Planner::axis_steps_per_mm[XYZE_N],
@@ -227,6 +228,7 @@ void Planner::init() {
227 228
     bed_level_matrix.set_to_identity();
228 229
   #endif
229 230
   clear_block_buffer();
231
+  block_buffer_planned = 0;
230 232
   delay_before_delivering = 0;
231 233
 }
232 234
 
@@ -825,6 +827,68 @@ void Planner::calculate_trapezoid_for_block(block_t* const block, const float &e
825 827
   if (was_enabled) ENABLE_STEPPER_DRIVER_INTERRUPT();
826 828
 }
827 829
 
830
+/*                            PLANNER SPEED DEFINITION
831
+                                     +--------+   <- current->nominal_speed
832
+                                    /          \
833
+         current->entry_speed ->   +            \
834
+                                   |             + <- next->entry_speed (aka exit speed)
835
+                                   +-------------+
836
+                                       time -->
837
+
838
+  Recalculates the motion plan according to the following basic guidelines:
839
+
840
+    1. Go over every feasible block sequentially in reverse order and calculate the junction speeds
841
+        (i.e. current->entry_speed) such that:
842
+      a. No junction speed exceeds the pre-computed maximum junction speed limit or nominal speeds of
843
+         neighboring blocks.
844
+      b. A block entry speed cannot exceed one reverse-computed from its exit speed (next->entry_speed)
845
+         with a maximum allowable deceleration over the block travel distance.
846
+      c. The last (or newest appended) block is planned from a complete stop (an exit speed of zero).
847
+    2. Go over every block in chronological (forward) order and dial down junction speed values if
848
+      a. The exit speed exceeds the one forward-computed from its entry speed with the maximum allowable
849
+         acceleration over the block travel distance.
850
+
851
+  When these stages are complete, the planner will have maximized the velocity profiles throughout the all
852
+  of the planner blocks, where every block is operating at its maximum allowable acceleration limits. In
853
+  other words, for all of the blocks in the planner, the plan is optimal and no further speed improvements
854
+  are possible. If a new block is added to the buffer, the plan is recomputed according to the said
855
+  guidelines for a new optimal plan.
856
+
857
+  To increase computational efficiency of these guidelines, a set of planner block pointers have been
858
+  created to indicate stop-compute points for when the planner guidelines cannot logically make any further
859
+  changes or improvements to the plan when in normal operation and new blocks are streamed and added to the
860
+  planner buffer. For example, if a subset of sequential blocks in the planner have been planned and are
861
+  bracketed by junction velocities at their maximums (or by the first planner block as well), no new block
862
+  added to the planner buffer will alter the velocity profiles within them. So we no longer have to compute
863
+  them. Or, if a set of sequential blocks from the first block in the planner (or a optimal stop-compute
864
+  point) are all accelerating, they are all optimal and can not be altered by a new block added to the
865
+  planner buffer, as this will only further increase the plan speed to chronological blocks until a maximum
866
+  junction velocity is reached. However, if the operational conditions of the plan changes from infrequently
867
+  used feed holds or feedrate overrides, the stop-compute pointers will be reset and the entire plan is
868
+  recomputed as stated in the general guidelines.
869
+
870
+  Planner buffer index mapping:
871
+  - block_buffer_tail: Points to the beginning of the planner buffer. First to be executed or being executed.
872
+  - block_buffer_head: Points to the buffer block after the last block in the buffer. Used to indicate whether
873
+      the buffer is full or empty. As described for standard ring buffers, this block is always empty.
874
+  - block_buffer_planned: Points to the first buffer block after the last optimally planned block for normal
875
+      streaming operating conditions. Use for planning optimizations by avoiding recomputing parts of the
876
+      planner buffer that don't change with the addition of a new block, as describe above. In addition,
877
+      this block can never be less than block_buffer_tail and will always be pushed forward and maintain
878
+      this requirement when encountered by the plan_discard_current_block() routine during a cycle.
879
+
880
+  NOTE: Since the planner only computes on what's in the planner buffer, some motions with lots of short
881
+  line segments, like G2/3 arcs or complex curves, may seem to move slow. This is because there simply isn't
882
+  enough combined distance traveled in the entire buffer to accelerate up to the nominal speed and then
883
+  decelerate to a complete stop at the end of the buffer, as stated by the guidelines. If this happens and
884
+  becomes an annoyance, there are a few simple solutions: (1) Maximize the machine acceleration. The planner
885
+  will be able to compute higher velocity profiles within the same combined distance. (2) Maximize line
886
+  motion(s) distance per block to a desired tolerance. The more combined distance the planner has to use,
887
+  the faster it can go. (3) Maximize the planner buffer size. This also will increase the combined distance
888
+  for the planner to compute over. It also increases the number of computations the planner has to perform
889
+  to compute an optimal plan, so select carefully.
890
+*/
891
+
828 892
 // The kernel called by recalculate() when scanning the plan from last to first entry.
829 893
 void Planner::reverse_pass_kernel(block_t* const current, const block_t * const next) {
830 894
   if (current) {
@@ -851,6 +915,8 @@ void Planner::reverse_pass_kernel(block_t* const current, const block_t * const
851 915
         : MIN(max_entry_speed_sqr, max_allowable_speed_sqr(-current->acceleration, next ? next->entry_speed_sqr : sq(MINIMUM_PLANNER_SPEED), current->millimeters));
852 916
       if (current->entry_speed_sqr != new_entry_speed_sqr) {
853 917
         current->entry_speed_sqr = new_entry_speed_sqr;
918
+
919
+        // Need to recalculate the block speed
854 920
         SBI(current->flag, BLOCK_BIT_RECALCULATE);
855 921
       }
856 922
     }
@@ -862,44 +928,72 @@ void Planner::reverse_pass_kernel(block_t* const current, const block_t * const
862 928
  * Once in reverse and once forward. This implements the reverse pass.
863 929
  */
864 930
 void Planner::reverse_pass() {
865
-  if (movesplanned() > 2) {
866
-    const uint8_t endnr = next_block_index(block_buffer_tail); // tail is running. tail+1 shouldn't be altered because it's connected to the running block.
867
-    uint8_t blocknr = prev_block_index(block_buffer_head);
931
+  // Initialize block index to the last block in the planner buffer.
932
+  uint8_t block_index = prev_block_index(block_buffer_head);
933
+
934
+  // Read the index of the last buffer planned block.
935
+  // The ISR may change it so get a stable local copy.
936
+  uint8_t planned_block_index = block_buffer_planned;
937
+
938
+  // If there was a race condition and block_buffer_planned was incremented
939
+  //  or was pointing at the head (queue empty) break loop now and avoid
940
+  //  planning already consumed blocks
941
+  if (planned_block_index == block_buffer_head) return;
942
+
943
+  // Reverse Pass: Coarsely maximize all possible deceleration curves back-planning from the last
944
+  // block in buffer. Cease planning when the last optimal planned or tail pointer is reached.
945
+  // NOTE: Forward pass will later refine and correct the reverse pass to create an optimal plan.
946
+  block_t *current;
947
+  const block_t *next = NULL;
948
+  while (block_index != planned_block_index) {
868 949
 
869 950
     // Perform the reverse pass
870
-    block_t *current, *next = NULL;
871
-    while (blocknr != endnr) {
872
-      // Perform the reverse pass - Only consider non sync blocks
873
-      current = &block_buffer[blocknr];
874
-      if (!TEST(current->flag, BLOCK_BIT_SYNC_POSITION)) {
875
-        reverse_pass_kernel(current, next);
876
-        next = current;
877
-      }
878
-      // Advance to the next
879
-      blocknr = prev_block_index(blocknr);
951
+    current = &block_buffer[block_index];
952
+
953
+    // Only consider non sync blocks
954
+    if (!TEST(current->flag, BLOCK_BIT_SYNC_POSITION)) {
955
+      reverse_pass_kernel(current, next);
956
+      next = current;
880 957
     }
958
+
959
+    // Advance to the next
960
+    block_index = prev_block_index(block_index);
881 961
   }
882 962
 }
883 963
 
884 964
 // The kernel called by recalculate() when scanning the plan from first to last entry.
885
-void Planner::forward_pass_kernel(const block_t * const previous, block_t* const current) {
965
+void Planner::forward_pass_kernel(const block_t * const previous, block_t* const current, uint8_t block_index) {
886 966
   if (previous) {
887 967
     // If the previous block is an acceleration block, too short to complete the full speed
888 968
     // change, adjust the entry speed accordingly. Entry speeds have already been reset,
889 969
     // maximized, and reverse-planned. If nominal length is set, max junction speed is
890 970
     // guaranteed to be reached. No need to recheck.
891
-    if (!TEST(previous->flag, BLOCK_BIT_NOMINAL_LENGTH)) {
892
-      if (previous->entry_speed_sqr < current->entry_speed_sqr) {
893
-        // Compute the maximum allowable speed
894
-        const float new_entry_speed_sqr = max_allowable_speed_sqr(-previous->acceleration, previous->entry_speed_sqr, previous->millimeters);
895
-        // If true, current block is full-acceleration
896
-        if (current->entry_speed_sqr > new_entry_speed_sqr) {
897
-          // Always <= max_entry_speed_sqr. Backward pass sets this.
898
-          current->entry_speed_sqr = new_entry_speed_sqr;
899
-          SBI(current->flag, BLOCK_BIT_RECALCULATE);
900
-        }
971
+    if (!TEST(previous->flag, BLOCK_BIT_NOMINAL_LENGTH) &&
972
+      previous->entry_speed_sqr < current->entry_speed_sqr) {
973
+
974
+      // Compute the maximum allowable speed
975
+      const float new_entry_speed_sqr = max_allowable_speed_sqr(-previous->acceleration, previous->entry_speed_sqr, previous->millimeters);
976
+
977
+      // If true, current block is full-acceleration and we can move the planned pointer forward.
978
+      if (new_entry_speed_sqr < current->entry_speed_sqr) {
979
+
980
+        // Always <= max_entry_speed_sqr. Backward pass sets this.
981
+        current->entry_speed_sqr = new_entry_speed_sqr; // Always <= max_entry_speed_sqr. Backward pass sets this.
982
+
983
+        // Set optimal plan pointer.
984
+        block_buffer_planned = block_index;
985
+
986
+        // And mark we need to recompute the trapezoidal shape
987
+        SBI(current->flag, BLOCK_BIT_RECALCULATE);
901 988
       }
902 989
     }
990
+
991
+    // Any block set at its maximum entry speed also creates an optimal plan up to this
992
+    // point in the buffer. When the plan is bracketed by either the beginning of the
993
+    // buffer and a maximum entry speed or two maximum entry speeds, every block in between
994
+    // cannot logically be further improved. Hence, we don't have to recompute them anymore.
995
+    if (current->entry_speed_sqr == current->max_entry_speed_sqr)
996
+      block_buffer_planned = block_index;
903 997
   }
904 998
 }
905 999
 
@@ -908,20 +1002,30 @@ void Planner::forward_pass_kernel(const block_t * const previous, block_t* const
908 1002
  * Once in reverse and once forward. This implements the forward pass.
909 1003
  */
910 1004
 void Planner::forward_pass() {
911
-  const uint8_t endnr = block_buffer_head;
912
-  uint8_t blocknr = block_buffer_tail;
913
-
914
-  // Perform the forward pass
915
-  block_t *current, *previous = NULL;
916
-  while (blocknr != endnr) {
917
-    // Perform the forward pass - Only consider non-sync blocks
918
-    current = &block_buffer[blocknr];
1005
+
1006
+  // Forward Pass: Forward plan the acceleration curve from the planned pointer onward.
1007
+  // Also scans for optimal plan breakpoints and appropriately updates the planned pointer.
1008
+
1009
+  // Begin at buffer planned pointer. Note that block_buffer_planned can be modified
1010
+  //  by the stepper ISR,  so read it ONCE. It it guaranteed that block_buffer_planned
1011
+  //  will never lead head, so the loop is safe to execute. Also note that the forward
1012
+  //  pass will never modify the values at the tail.
1013
+  uint8_t block_index = block_buffer_planned;
1014
+
1015
+  block_t *current;
1016
+  const block_t * previous = NULL;
1017
+  while (block_index != block_buffer_head) {
1018
+
1019
+    // Perform the forward pass
1020
+    current = &block_buffer[block_index];
1021
+
1022
+    // Skip SYNC blocks
919 1023
     if (!TEST(current->flag, BLOCK_BIT_SYNC_POSITION)) {
920
-      forward_pass_kernel(previous, current);
1024
+      forward_pass_kernel(previous, current, block_index);
921 1025
       previous = current;
922 1026
     }
923 1027
     // Advance to the previous
924
-    blocknr = next_block_index(blocknr);
1028
+    block_index = next_block_index(block_index);
925 1029
   }
926 1030
 }
927 1031
 
@@ -931,6 +1035,7 @@ void Planner::forward_pass() {
931 1035
  * recalculate() after updating the blocks.
932 1036
  */
933 1037
 void Planner::recalculate_trapezoids() {
1038
+  // The tail may be changed by the ISR so get a local copy.
934 1039
   uint8_t block_index = block_buffer_tail;
935 1040
 
936 1041
   // As there could be a sync block in the head of the queue, and the next loop must not
@@ -1004,33 +1109,14 @@ void Planner::recalculate_trapezoids() {
1004 1109
   }
1005 1110
 }
1006 1111
 
1007
-/**
1008
- * Recalculate the motion plan according to the following algorithm:
1009
- *
1010
- *   1. Go over every block in reverse order...
1011
- *
1012
- *      Calculate a junction speed reduction (block_t.entry_factor) so:
1013
- *
1014
- *      a. The junction jerk is within the set limit, and
1015
- *
1016
- *      b. No speed reduction within one block requires faster
1017
- *         deceleration than the one, true constant acceleration.
1018
- *
1019
- *   2. Go over every block in chronological order...
1020
- *
1021
- *      Dial down junction speed reduction values if:
1022
- *      a. The speed increase within one block would require faster
1023
- *         acceleration than the one, true constant acceleration.
1024
- *
1025
- * After that, all blocks will have an entry_factor allowing all speed changes to
1026
- * be performed using only the one, true constant acceleration, and where no junction
1027
- * jerk is jerkier than the set limit, Jerky. Finally it will:
1028
- *
1029
- *   3. Recalculate "trapezoids" for all blocks.
1030
- */
1031 1112
 void Planner::recalculate() {
1032
-  reverse_pass();
1033
-  forward_pass();
1113
+  // Initialize block index to the last block in the planner buffer.
1114
+  const uint8_t block_index = prev_block_index(block_buffer_head);
1115
+  // If there is just one block, no planning can be done. Avoid it!
1116
+  if (block_index != block_buffer_planned) {
1117
+    reverse_pass();
1118
+    forward_pass();
1119
+  }
1034 1120
   recalculate_trapezoids();
1035 1121
 }
1036 1122
 
@@ -1348,10 +1434,18 @@ void Planner::check_axes_activity() {
1348 1434
 #endif // PLANNER_LEVELING
1349 1435
 
1350 1436
 void Planner::quick_stop() {
1437
+
1351 1438
   // Remove all the queued blocks. Note that this function is NOT
1352 1439
   // called from the Stepper ISR, so we must consider tail as readonly!
1353
-  // that is why we set head to tail!
1354
-  block_buffer_head = block_buffer_tail;
1440
+  // that is why we set head to tail - But there is a race condition that
1441
+  // must be handled: The tail could change between the read and the assignment
1442
+  // so this must be enclosed in a critical section
1443
+
1444
+  const bool was_enabled = STEPPER_ISR_ENABLED();
1445
+  if (was_enabled) DISABLE_STEPPER_DRIVER_INTERRUPT();
1446
+
1447
+  // Drop all queue entries
1448
+  block_buffer_planned = block_buffer_head = block_buffer_tail;
1355 1449
 
1356 1450
   // Restart the block delay for the first movement - As the queue was
1357 1451
   // forced to empty, there's no risk the ISR will touch this.
@@ -1365,6 +1459,9 @@ void Planner::quick_stop() {
1365 1459
   // Make sure to drop any attempt of queuing moves for at least 1 second
1366 1460
   cleaning_buffer_counter = 1000;
1367 1461
 
1462
+  // Reenable Stepper ISR
1463
+  if (was_enabled) ENABLE_STEPPER_DRIVER_INTERRUPT();
1464
+
1368 1465
   // And stop the stepper ISR
1369 1466
   stepper.quick_stop();
1370 1467
 }

+ 22
- 15
Marlin/src/module/planner.h View File

@@ -177,7 +177,9 @@ class Planner {
177 177
     static volatile uint8_t block_buffer_head,      // Index of the next block to be pushed
178 178
                             block_buffer_tail;      // Index of the busy block, if any
179 179
     static uint16_t cleaning_buffer_counter;        // A counter to disable queuing of blocks
180
-    static uint8_t delay_before_delivering;         // This counter delays delivery of blocks when queue becomes empty to allow the opportunity of merging blocks
180
+    static uint8_t delay_before_delivering,         // This counter delays delivery of blocks when queue becomes empty to allow the opportunity of merging blocks
181
+                   block_buffer_planned;            // Index of the optimally planned block
182
+                   
181 183
 
182 184
     #if ENABLED(DISTINCT_E_FACTORS)
183 185
       static uint8_t last_extruder;                 // Respond to extruder change
@@ -655,9 +657,7 @@ class Planner {
655 657
         block_t * const block = &block_buffer[block_buffer_tail];
656 658
 
657 659
         // No trapezoid calculated? Don't execute yet.
658
-        if ( TEST(block->flag, BLOCK_BIT_RECALCULATE)
659
-          || (movesplanned() > 1 && TEST(block_buffer[next_block_index(block_buffer_tail)].flag, BLOCK_BIT_RECALCULATE))
660
-        ) return NULL;
660
+        if (TEST(block->flag, BLOCK_BIT_RECALCULATE)) return NULL;
661 661
 
662 662
         #if ENABLED(ULTRA_LCD)
663 663
           block_buffer_runtime_us -= block->segment_time_us; // We can't be sure how long an active block will take, so don't count it.
@@ -667,13 +667,13 @@ class Planner {
667 667
         SBI(block->flag, BLOCK_BIT_BUSY);
668 668
         return block;
669 669
       }
670
-      else {
671
-        // The queue became empty
672
-        #if ENABLED(ULTRA_LCD)
673
-          clear_block_buffer_runtime(); // paranoia. Buffer is empty now - so reset accumulated time to zero.
674
-        #endif
675
-        return NULL;
676
-      }
670
+
671
+      // The queue became empty
672
+      #if ENABLED(ULTRA_LCD)
673
+        clear_block_buffer_runtime(); // paranoia. Buffer is empty now - so reset accumulated time to zero.
674
+      #endif
675
+
676
+      return NULL;
677 677
     }
678 678
 
679 679
     /**
@@ -682,7 +682,14 @@ class Planner {
682 682
      * NB: There MUST be a current block to call this function!!
683 683
      */
684 684
     FORCE_INLINE static void discard_current_block() {
685
-      block_buffer_tail = BLOCK_MOD(block_buffer_tail + 1);
685
+      if (has_blocks_queued()) { // Discard non-empty buffer.
686
+        uint8_t block_index = next_block_index( block_buffer_tail );
687
+
688
+        // Push block_buffer_planned pointer, if encountered.
689
+        if (!has_blocks_queued()) block_buffer_planned = block_index;
690
+
691
+        block_buffer_tail = block_index;
692
+      }
686 693
     }
687 694
 
688 695
     #if ENABLED(ULTRA_LCD)
@@ -741,8 +748,8 @@ class Planner {
741 748
     /**
742 749
      * Get the index of the next / previous block in the ring buffer
743 750
      */
744
-    static constexpr int8_t next_block_index(const int8_t block_index) { return BLOCK_MOD(block_index + 1); }
745
-    static constexpr int8_t prev_block_index(const int8_t block_index) { return BLOCK_MOD(block_index - 1); }
751
+    static constexpr uint8_t next_block_index(const uint8_t block_index) { return BLOCK_MOD(block_index + 1); }
752
+    static constexpr uint8_t prev_block_index(const uint8_t block_index) { return BLOCK_MOD(block_index - 1); }
746 753
 
747 754
     /**
748 755
      * Calculate the distance (not time) it takes to accelerate
@@ -787,7 +794,7 @@ class Planner {
787 794
     static void calculate_trapezoid_for_block(block_t* const block, const float &entry_factor, const float &exit_factor);
788 795
 
789 796
     static void reverse_pass_kernel(block_t* const current, const block_t * const next);
790
-    static void forward_pass_kernel(const block_t * const previous, block_t* const current);
797
+    static void forward_pass_kernel(const block_t * const previous, block_t* const current, uint8_t block_index);
791 798
 
792 799
     static void reverse_pass();
793 800
     static void forward_pass();

Loading…
Cancel
Save