yarf 0.1
Yet Another RepRap Firmware
src/movement/planner.c
Go to the documentation of this file.
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 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines