yarf 0.1
Yet Another RepRap Firmware
|
00001 /* 00002 * planner.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 00031 #include "planner.h" 00032 00033 #include "yarf.h" 00034 #include "block.h" 00035 #include "planner_queue.h" 00036 #include "planner_lookahead.h" 00037 #include "block_handler.h" 00038 #include "advance.h" 00039 #include "scheduling/realtime_timer.h" 00040 #include "util/math.h" 00041 #include "util/fixed_point.h" 00042 00043 #include <stdio.h> 00044 #include <stdint.h> 00045 #include <stdlib.h> 00046 #include <stdbool.h> 00047 #include <util/atomic.h> 00048 00053 #define HOME_AXIS_CALIBRATION_MM_X 5 00054 00058 #define HOME_AXIS_CALIBRATION_MM_Y 5 00059 00063 #define HOME_AXIS_CALIBRATION_MM_Z 1 00064 00069 #define HOME_AXIS_CALIBRATION_SPEED_X 300 00070 00074 #define HOME_AXIS_CALIBRATION_SPEED_Y 300 00075 00079 #define HOME_AXIS_CALIBRATION_SPEED_Z 50 00080 00081 00085 static uint8_t axis_homed; 00086 00087 00093 static steps_t virtual_position[NUM_AXES]; 00094 00095 00100 static unsigned int next_id; 00101 00102 00103 00164 static inline int32_t 00165 intersection_steps(speed_t entry_speed, speed_t exit_speed, float distance_mm, long nb_steps) 00166 { 00167 return lround(nb_steps * ( ((exit_speed+entry_speed)*(exit_speed-entry_speed))/(4*ACCELERATION*distance_mm) + 0.5 )); 00168 } 00169 00170 00187 static inline fxp16u16_t 00188 ticks_float_to_fxp16u16_safe(float ticks_fl) 00189 { 00190 if (ticks_fl < fxp16u16_to_float(FXP_16U16_MAX/2)) { 00191 return fxp16u16_from_float(ticks_fl); 00192 } else { 00193 return FXP_16U16_MAX/2; 00194 } 00195 } 00196 00197 00198 00222 static inline void 00223 initialize_block(block_t *b, steps_t steps_x, steps_t steps_y, steps_t steps_z, steps_t steps_e, speed_t plateau_speed, void (*collision_handler)(uint8_t)) 00224 { 00225 /* Set the block's id */ 00226 b->id = next_id++; 00227 00228 /* Set the collision handler */ 00229 b->collision_handler = collision_handler; 00230 00231 /* The number of steps in each direction and the total number of steps */ 00232 b->steps[X_AXIS] = steps_x; 00233 b->steps[Y_AXIS] = steps_y; 00234 b->steps[Z_AXIS] = steps_z; 00235 b->steps[E_AXIS] = steps_e; 00236 b->nb_steps = MAX(labs(b->steps[X_AXIS]), MAX(labs(b->steps[Y_AXIS]), 00237 MAX(labs(b->steps[Z_AXIS]), labs(b->steps[E_AXIS])))); 00238 b->deceleration_start = b->nb_steps; 00239 b->nb_steps_completed = 0; 00240 00241 /* The distance to travel in each direction in mm */ 00242 float distance_mm[NUM_AXES]; 00243 distance_mm[X_AXIS] = steps_x*X_MM_PER_STEP; 00244 distance_mm[Y_AXIS] = steps_y*Y_MM_PER_STEP; 00245 distance_mm[Z_AXIS] = steps_z*Z_MM_PER_STEP; 00246 distance_mm[E_AXIS] = steps_e*E_MM_PER_STEP; 00247 00248 /* The straight-line distance (must not be zero) */ 00249 float distance_mm_line = sqrt( 00250 fsquare(distance_mm[X_AXIS])+ 00251 fsquare(distance_mm[Y_AXIS])+ 00252 fsquare(distance_mm[Z_AXIS])+ 00253 fsquare(distance_mm[E_AXIS])); 00254 b->distance_mm = distance_mm_line; 00255 b->mm_ticks_per_step_min = ((float)(distance_mm_line*RTTIMER_TICKS_PER_MIN))/b->nb_steps; // mm per step multiplied by RTTIMER_TICKS_PER_MIN 00256 00257 /* The 4D direction unit vector of this move */ 00258 float distance_mm_line_inverse = 1.0/distance_mm_line; 00259 b->direction[X_AXIS] = distance_mm[X_AXIS]*distance_mm_line_inverse; 00260 b->direction[Y_AXIS] = distance_mm[Y_AXIS]*distance_mm_line_inverse; 00261 b->direction[Z_AXIS] = distance_mm[Z_AXIS]*distance_mm_line_inverse; 00262 b->direction[E_AXIS] = distance_mm[E_AXIS]*distance_mm_line_inverse; 00263 00264 /* Limit the feed rate according to the max feed rate of each axis */ 00265 if (distance_mm[X_AXIS] > 0) plateau_speed = MIN(plateau_speed, labs(lround(X_MAX_SPEED/b->direction[X_AXIS]))); 00266 if (distance_mm[Y_AXIS] > 0) plateau_speed = MIN(plateau_speed, labs(lround(Y_MAX_SPEED/b->direction[Y_AXIS]))); 00267 if (distance_mm[Z_AXIS] > 0) plateau_speed = MIN(plateau_speed, labs(lround(Z_MAX_SPEED/b->direction[Z_AXIS]))); 00268 if (distance_mm[E_AXIS] > 0) plateau_speed = MIN(plateau_speed, labs(lround(E_MAX_SPEED/b->direction[E_AXIS]))); 00269 b->plateau_speed = plateau_speed; 00270 speed_t start_feed_rate = MIN(plateau_speed, START_FEED_RATE); 00271 00272 /* Set the number of ticks in between steps at the plateau */ 00273 b->plateau_timer_ticks = ticks_float_to_fxp16u16_safe(b->mm_ticks_per_step_min/plateau_speed); 00274 00275 00276 /* Calculate a conservative acceleration/deceleration trapezoid */ 00277 plan_calculate_trapezoid(b, start_feed_rate, start_feed_rate); 00278 00279 #if ADVANCE_ALGORITHM 00280 if ((steps_e == 0) || (steps_x == 0 && steps_y == 0 && steps_z == 0)) { 00281 b->use_advance = false; 00282 } else { 00283 b->use_advance = true; 00284 } 00285 b->advance_multiplier = advance_get_multiplier(b); 00286 b->advance = advance_get_value(b); 00287 #endif 00288 } 00289 00290 00315 static void 00316 linear_movement_rel(steps_t steps_x, steps_t steps_y, steps_t steps_z, steps_t steps_e, speed_t plateau_speed, void (*collision_handler)(uint8_t)) 00317 { 00318 if (steps_x == 0 && steps_y == 0 && steps_z == 0 && steps_e == 0) { 00319 /* This is a null-move */ 00320 return; 00321 } 00322 if (plateau_speed <= 0) { 00323 /* Invalid feed rate */ 00324 return; 00325 } 00326 00327 00328 block_t *b = plan_q_head(); 00329 while (b == NULL) { 00330 /* Wait until space is available in the planning queue */ 00331 b = plan_q_head(); 00332 } 00333 00334 // Fill in the variables required for the block handler 00335 initialize_block(b, steps_x, steps_y, steps_z, steps_e, plateau_speed, collision_handler); 00336 // Move planning queue head 00337 plan_q_shift_head(); 00338 00339 #if PLANNER_LOOKAHEAD 00340 plan_lookahead(); 00341 #endif 00342 00343 plan_set_virtual_position_rel(steps_x,steps_y, steps_z,steps_e); 00344 } 00345 00352 static void 00353 collision(uint8_t collisions) 00354 { 00355 block_handler_stop(); 00356 plan_q_clear(); 00357 // TODO: properly signal hardware fault 00358 puts("!! // collision on axis "); 00359 if (collisions & _BV(X_AXIS_POS)) { 00360 puts("X+ "); 00361 } 00362 if (collisions & _BV(X_AXIS_NEG)) { 00363 puts("X- "); 00364 } 00365 if (collisions & _BV(Y_AXIS_POS)) { 00366 puts("Y+ "); 00367 } 00368 if (collisions & _BV(Y_AXIS_NEG)) { 00369 puts("Y- "); 00370 } 00371 if (collisions & _BV(Z_AXIS_POS)) { 00372 puts("Z+ "); 00373 } 00374 if (collisions & _BV(Z_AXIS_NEG)) { 00375 puts("Z- "); 00376 } 00377 puts("while moving!\n"); 00378 } 00379 00385 static void 00386 homing_collision(uint8_t collisions) 00387 { 00388 return; 00389 } 00390 00391 00392 00393 void 00394 plan_init(void) 00395 { 00396 plan_q_init(); 00397 #if PLANNER_LOOKAHEAD 00398 plan_lookahead_init(); 00399 #endif 00400 axis_homed = 0; 00401 virtual_position[E_AXIS] = 0; 00402 next_id = 0; 00403 } 00404 00405 steps_t 00406 plan_get_virtual_position(unsigned char axis) 00407 { 00408 return virtual_position[axis]; 00409 } 00410 00411 void 00412 plan_set_virtual_position_rel(steps_t x, steps_t y, steps_t z, steps_t e) 00413 { 00414 virtual_position[X_AXIS] += x; 00415 virtual_position[Y_AXIS] += y; 00416 virtual_position[Z_AXIS] += z; 00417 virtual_position[E_AXIS] += e; 00418 } 00419 00420 void 00421 plan_set_virtual_position_abs(steps_t x, steps_t y, steps_t z, steps_t e) 00422 { 00423 virtual_position[X_AXIS] = x; 00424 virtual_position[Y_AXIS] = y; 00425 virtual_position[Z_AXIS] = z; 00426 virtual_position[E_AXIS] = e; 00427 } 00428 00429 00430 void 00431 plan_calculate_trapezoid(block_t *b, speed_t entry_speed, speed_t exit_speed) 00432 { 00433 /* Calculate the acceleration variables */ 00434 float common_multiplier = ((float)(b->nb_steps * 2))/(ACCELERATION * b->distance_mm); 00435 uint16_t entry_speed_step = (uint16_t)lround(lsquare(entry_speed) * common_multiplier) + 1; 00436 uint16_t exit_speed_step = (uint16_t)lround(lsquare(exit_speed) * common_multiplier) + 1; 00437 uint16_t plateau_reached_step = (uint16_t)lround(lsquare(b->plateau_speed) * common_multiplier) + 1; 00438 00439 int32_t nb_acceleration_steps = (plateau_reached_step - entry_speed_step)/4; 00440 int32_t nb_deceleration_steps = (plateau_reached_step - exit_speed_step)/4; 00441 int32_t nb_plateau_steps = b->nb_steps - nb_acceleration_steps - nb_deceleration_steps; 00442 00443 if (nb_plateau_steps < 0) { 00444 nb_acceleration_steps = BOUNDS(0,intersection_steps(entry_speed,exit_speed,b->distance_mm,b->nb_steps),b->nb_steps); 00445 nb_plateau_steps = 0; 00446 } 00447 uint32_t deceleration_start = (uint32_t)(nb_acceleration_steps + nb_plateau_steps); 00448 fxp16u16_t entry_timer_ticks = ticks_float_to_fxp16u16_safe(b->mm_ticks_per_step_min/entry_speed); //rttimer ticks per step 00449 fxp16u16_t exit_timer_ticks = ticks_float_to_fxp16u16_safe(b->mm_ticks_per_step_min/exit_speed); //rttimer ticks per step 00450 00451 00452 /* Fill in the block acceleration variables if the block hasn't started yet */ 00453 ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { 00454 if (b->nb_steps_completed < deceleration_start && 00455 b->nb_steps_completed < b->deceleration_start) { 00456 b->deceleration_start = deceleration_start; 00457 b->exit_timer_ticks = exit_timer_ticks; 00458 } 00459 if (b->nb_steps_completed == 0) { 00460 b->acceleration_end = nb_acceleration_steps; 00461 b->timer_ticks = entry_timer_ticks; 00462 b->acceleration_step = entry_speed_step; 00463 } 00464 } 00465 } 00466 00467 void 00468 plan_linear_movement_rel(steps_t steps_x, steps_t steps_y, steps_t steps_z, steps_t steps_e, speed_t plateau_speed) 00469 { 00470 if (axis_homed != (_BV(X_AXIS)|_BV(Y_AXIS)|_BV(Z_AXIS))) { 00471 /* Home all axes before we make a linear movement */ 00472 plan_home_axes(!(axis_homed & _BV(X_AXIS)),!(axis_homed & _BV(Y_AXIS)),!(axis_homed & _BV(Z_AXIS))); 00473 } 00474 00475 if ((steps_x < -virtual_position[X_AXIS]) || 00476 (steps_y < -virtual_position[Y_AXIS]) || 00477 (steps_z < -virtual_position[Z_AXIS]) || 00478 (steps_x > X_LENGTH*X_STEPS_PER_MM - virtual_position[X_AXIS]) || 00479 (steps_y > Y_LENGTH*Y_STEPS_PER_MM - virtual_position[Y_AXIS]) || 00480 (steps_z > Z_LENGTH*Z_STEPS_PER_MM - virtual_position[Z_AXIS])) { 00481 /* This is a request to move out of bounds */ 00482 //TODO: signal error 00483 return; 00484 } 00485 00486 linear_movement_rel(steps_x, steps_y, steps_z, steps_e, plateau_speed, collision); 00487 } 00488 00489 00490 void 00491 plan_linear_movement_abs(steps_t dest_x, steps_t dest_y, steps_t dest_z, steps_t dest_e, speed_t plateau_speed) 00492 { 00493 plan_linear_movement_rel( 00494 dest_x - virtual_position[X_AXIS], 00495 dest_y - virtual_position[Y_AXIS], 00496 dest_z - virtual_position[Z_AXIS], 00497 dest_e - virtual_position[E_AXIS], 00498 plateau_speed 00499 ); 00500 } 00501 00502 00503 00504 void 00505 plan_home_axes(bool x, bool y, bool z) 00506 { 00507 /* First move all axes to the origin fast, then move away from the origin fast 00508 * and finally return to the origin slowly for increased accuracy. */ 00509 if (x) { 00510 linear_movement_rel((-2 * X_LENGTH * X_STEPS_PER_MM), 0, 0, 0, X_MAX_SPEED/2, homing_collision); 00511 linear_movement_rel((HOME_AXIS_CALIBRATION_MM_X * X_STEPS_PER_MM), 0, 0, 0, X_MAX_SPEED, collision); 00512 linear_movement_rel((-2 * HOME_AXIS_CALIBRATION_MM_X * X_STEPS_PER_MM), 0, 0, 0, HOME_AXIS_CALIBRATION_SPEED_X, homing_collision); 00513 virtual_position[X_AXIS] = 0; 00514 axis_homed |= _BV(X_AXIS); 00515 } 00516 if (y) { 00517 linear_movement_rel(0, (-2 * Y_LENGTH * Y_STEPS_PER_MM), 0, 0, Y_MAX_SPEED/2, homing_collision); 00518 linear_movement_rel(0, (HOME_AXIS_CALIBRATION_MM_Y * Y_STEPS_PER_MM), 0, 0, Y_MAX_SPEED, collision); 00519 linear_movement_rel(0, (-2 * HOME_AXIS_CALIBRATION_MM_Y * Y_STEPS_PER_MM), 0, 0, HOME_AXIS_CALIBRATION_SPEED_Y, homing_collision); 00520 virtual_position[Y_AXIS] = 0; 00521 axis_homed |= _BV(Y_AXIS); 00522 } 00523 if (z) { 00524 linear_movement_rel(0, 0, (-2 * Z_LENGTH * Z_STEPS_PER_MM), 0, Z_MAX_SPEED/2, homing_collision); 00525 linear_movement_rel(0, 0, (HOME_AXIS_CALIBRATION_MM_Z * Z_STEPS_PER_MM), 0, Z_MAX_SPEED, collision); 00526 linear_movement_rel(0, 0, (-2 * HOME_AXIS_CALIBRATION_MM_Z * Z_STEPS_PER_MM), 0, HOME_AXIS_CALIBRATION_SPEED_Z, homing_collision); 00527 virtual_position[Z_AXIS] = 0; 00528 axis_homed |= _BV(Z_AXIS); 00529 } 00530 } 00531 00532