Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of Smoothie by
Block.cpp
00001 /* 00002 This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). 00003 Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 00004 Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 00005 You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. 00006 */ 00007 00008 #include "libs/Module.h" 00009 #include "libs/Kernel.h" 00010 #include "libs/nuts_bolts.h" 00011 #include <math.h> 00012 #include <string> 00013 #include "Block.h" 00014 #include "Planner.h" 00015 #include "Conveyor.h" 00016 #include "Gcode.h" 00017 00018 #include "mri.h" 00019 00020 using std::string; 00021 #include <vector> 00022 00023 // A block represents a movement, it's length for each stepper motor, and the corresponding acceleration curves. 00024 // It's stacked on a queue, and that queue is then executed in order, to move the motors. 00025 // Most of the accel math is also done in this class 00026 // And GCode objects for use in on_gcode_execute are also help in here 00027 00028 Block::Block() 00029 { 00030 clear(); 00031 } 00032 00033 void Block::clear() 00034 { 00035 //commands.clear(); 00036 //travel_distances.clear(); 00037 gcodes.clear(); 00038 clear_vector(this->steps); 00039 00040 steps_event_count = 0; 00041 nominal_rate = 0; 00042 nominal_speed = 0.0F; 00043 millimeters = 0.0F; 00044 entry_speed = 0.0F; 00045 exit_speed = 0.0F; 00046 rate_delta = 0.0F; 00047 initial_rate = -1; 00048 final_rate = -1; 00049 accelerate_until = 0; 00050 decelerate_after = 0; 00051 direction_bits = 0; 00052 recalculate_flag = false; 00053 nominal_length_flag = false; 00054 max_entry_speed = 0.0F; 00055 is_ready = false; 00056 times_taken = 0; 00057 } 00058 00059 void Block::debug() 00060 { 00061 THEKERNEL->streams->printf("%p: steps:X%04d Y%04d Z%04d(max:%4d) nominal:r%10d/s%6.1f mm:%9.6f rdelta:%8f acc:%5d dec:%5d rates:%10d>%10d entry/max: %10.4f/%10.4f taken:%d ready:%d recalc:%d nomlen:%d\r\n", 00062 this, 00063 this->steps[0], 00064 this->steps[1], 00065 this->steps[2], 00066 this->steps_event_count, 00067 this->nominal_rate, 00068 this->nominal_speed, 00069 this->millimeters, 00070 this->rate_delta, 00071 this->accelerate_until, 00072 this->decelerate_after, 00073 this->initial_rate, 00074 this->final_rate, 00075 this->entry_speed, 00076 this->max_entry_speed, 00077 this->times_taken, 00078 this->is_ready, 00079 recalculate_flag?1:0, 00080 nominal_length_flag?1:0 00081 ); 00082 } 00083 00084 00085 /* Calculates trapezoid parameters so that the entry- and exit-speed is compensated by the provided factors. 00086 // The factors represent a factor of braking and must be in the range 0.0-1.0. 00087 // +--------+ <- nominal_rate 00088 // / \ 00089 // nominal_rate*entry_factor -> + \ 00090 // | + <- nominal_rate*exit_factor 00091 // +-------------+ 00092 // time --> 00093 */ 00094 void Block::calculate_trapezoid( float entryspeed, float exitspeed ) 00095 { 00096 // if block is currently executing, don't touch anything! 00097 if (times_taken) 00098 return; 00099 00100 // The planner passes us factors, we need to transform them in rates 00101 this->initial_rate = ceil(this->nominal_rate * entryspeed / this->nominal_speed); // (step/s) 00102 this->final_rate = ceil(this->nominal_rate * exitspeed / this->nominal_speed); // (step/s) 00103 00104 // How many steps to accelerate and decelerate 00105 float acceleration_per_second = this->rate_delta * THEKERNEL->stepper->acceleration_ticks_per_second; // ( step/s^2) 00106 int accelerate_steps = ceil( this->estimate_acceleration_distance( this->initial_rate, this->nominal_rate, acceleration_per_second ) ); 00107 int decelerate_steps = floor( this->estimate_acceleration_distance( this->nominal_rate, this->final_rate, -acceleration_per_second ) ); 00108 00109 // Calculate the size of Plateau of Nominal Rate ( during which we don't accelerate nor decelerate, but just cruise ) 00110 int plateau_steps = this->steps_event_count - accelerate_steps - decelerate_steps; 00111 00112 // Is the Plateau of Nominal Rate smaller than nothing? That means no cruising, and we will 00113 // have to use intersection_distance() to calculate when to abort acceleration and start braking 00114 // in order to reach the final_rate exactly at the end of this block. 00115 if (plateau_steps < 0) { 00116 accelerate_steps = ceil(this->intersection_distance(this->initial_rate, this->final_rate, acceleration_per_second, this->steps_event_count)); 00117 accelerate_steps = max( accelerate_steps, 0 ); // Check limits due to numerical round-off 00118 accelerate_steps = min( accelerate_steps, int(this->steps_event_count) ); 00119 plateau_steps = 0; 00120 } 00121 this->accelerate_until = accelerate_steps; 00122 this->decelerate_after = accelerate_steps + plateau_steps; 00123 00124 this->exit_speed = exitspeed; 00125 } 00126 00127 // Calculates the distance (not time) it takes to accelerate from initial_rate to target_rate using the 00128 // given acceleration: 00129 float Block::estimate_acceleration_distance(float initialrate, float targetrate, float acceleration) 00130 { 00131 return( ((targetrate * targetrate) - (initialrate * initialrate)) / (2.0F * acceleration)); 00132 } 00133 00134 // This function gives you the point at which you must start braking (at the rate of -acceleration) if 00135 // you started at speed initial_rate and accelerated until this point and want to end at the final_rate after 00136 // a total travel of distance. This can be used to compute the intersection point between acceleration and 00137 // deceleration in the cases where the trapezoid has no plateau (i.e. never reaches maximum speed) 00138 // 00139 /* + <- some maximum rate we don't care about 00140 /|\ 00141 / | \ 00142 / | + <- final_rate 00143 / | | 00144 initial_rate -> +----+--+ 00145 ^ ^ 00146 | | 00147 intersection_distance distance */ 00148 float Block::intersection_distance(float initialrate, float finalrate, float acceleration, float distance) 00149 { 00150 return((2 * acceleration * distance - initialrate * initialrate + finalrate * finalrate) / (4 * acceleration)); 00151 } 00152 00153 // Calculates the maximum allowable speed at this point when you must be able to reach target_velocity using the 00154 // acceleration within the allotted distance. 00155 inline float max_allowable_speed(float acceleration, float target_velocity, float distance) 00156 { 00157 return sqrtf(target_velocity * target_velocity - 2.0F * acceleration * distance); 00158 } 00159 00160 00161 // Called by Planner::recalculate() when scanning the plan from last to first entry. 00162 float Block::reverse_pass(float exit_speed) 00163 { 00164 // If entry speed is already at the maximum entry speed, no need to recheck. Block is cruising. 00165 // If not, block in state of acceleration or deceleration. Reset entry speed to maximum and 00166 // check for maximum allowable speed reductions to ensure maximum possible planned speed. 00167 if (this->entry_speed != this->max_entry_speed) 00168 { 00169 // If nominal length true, max junction speed is guaranteed to be reached. Only compute 00170 // for max allowable speed if block is decelerating and nominal length is false. 00171 if ((!this->nominal_length_flag) && (this->max_entry_speed > exit_speed)) 00172 { 00173 float max_entry_speed = max_allowable_speed(-THEKERNEL->planner->acceleration, exit_speed, this->millimeters); 00174 00175 this->entry_speed = min(max_entry_speed, this->max_entry_speed); 00176 00177 return this->entry_speed; 00178 } 00179 else 00180 this->entry_speed = this->max_entry_speed; 00181 } 00182 00183 return this->entry_speed; 00184 } 00185 00186 00187 // Called by Planner::recalculate() when scanning the plan from first to last entry. 00188 // returns maximum exit speed of this block 00189 float Block::forward_pass(float prev_max_exit_speed) 00190 { 00191 // If the previous block is an acceleration block, but it is not long enough to complete the 00192 // full speed change within the block, we need to adjust the entry speed accordingly. Entry 00193 // speeds have already been reset, maximized, and reverse planned by reverse planner. 00194 // If nominal length is true, max junction speed is guaranteed to be reached. No need to recheck. 00195 00196 // TODO: find out if both of these checks are necessary 00197 if (prev_max_exit_speed > nominal_speed) 00198 prev_max_exit_speed = nominal_speed; 00199 if (prev_max_exit_speed > max_entry_speed) 00200 prev_max_exit_speed = max_entry_speed; 00201 00202 if (prev_max_exit_speed <= entry_speed) 00203 { 00204 // accel limited 00205 entry_speed = prev_max_exit_speed; 00206 // since we're now acceleration or cruise limited 00207 // we don't need to recalculate our entry speed anymore 00208 recalculate_flag = false; 00209 } 00210 // else 00211 // // decel limited, do nothing 00212 00213 return max_exit_speed(); 00214 } 00215 00216 float Block::max_exit_speed() 00217 { 00218 // if block is currently executing, return cached exit speed from calculate_trapezoid 00219 // this ensures that a block following a currently executing block will have correct entry speed 00220 if (times_taken) 00221 return exit_speed; 00222 00223 // if nominal_length_flag is asserted 00224 // we are guaranteed to reach nominal speed regardless of entry speed 00225 // thus, max exit will always be nominal 00226 if (nominal_length_flag) 00227 return nominal_speed; 00228 00229 // otherwise, we have to work out max exit speed based on entry and acceleration 00230 float max = max_allowable_speed(-THEKERNEL->planner->acceleration, this->entry_speed, this->millimeters); 00231 00232 return min(max, nominal_speed); 00233 } 00234 00235 // Gcodes are attached to their respective blocks so that on_gcode_execute can be called with it 00236 void Block::append_gcode(Gcode* gcode) 00237 { 00238 Gcode new_gcode = *gcode; 00239 gcodes.push_back(new_gcode); 00240 } 00241 00242 void Block::begin() 00243 { 00244 recalculate_flag = false; 00245 00246 if (!is_ready) 00247 __debugbreak(); 00248 00249 times_taken = -1; 00250 00251 // execute all the gcodes related to this block 00252 for(unsigned int index = 0; index < gcodes.size(); index++) 00253 THEKERNEL->call_event(ON_GCODE_EXECUTE, &(gcodes[index])); 00254 00255 THEKERNEL->call_event(ON_BLOCK_BEGIN, this); 00256 00257 if (times_taken < 0) 00258 release(); 00259 } 00260 00261 // Signal the conveyor that this block is ready to be injected into the system 00262 void Block::ready() 00263 { 00264 this->is_ready = true; 00265 } 00266 00267 // Mark the block as taken by one more module 00268 void Block::take() 00269 { 00270 if (times_taken < 0) 00271 times_taken = 0; 00272 times_taken++; 00273 } 00274 00275 // Mark the block as no longer taken by one module, go to next block if this free's it 00276 void Block::release() 00277 { 00278 if (--this->times_taken <= 0) 00279 { 00280 times_taken = 0; 00281 if (is_ready) 00282 { 00283 is_ready = false; 00284 THEKERNEL->call_event(ON_BLOCK_END, this); 00285 00286 // ensure conveyor gets called last 00287 THEKERNEL->conveyor->on_block_end(this); 00288 } 00289 } 00290 }
Generated on Tue Jul 12 2022 20:09:00 by
1.7.2
