Last active
August 29, 2015 14:10
-
-
Save arthurwolf/ed8cc3bce15ac395d8e7 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Changes needed for much more rational accel : | |
Essentially, all accel happens in stepticker, and the modules tell it what to do. | |
######################### | |
Changes to steppermotor : | |
* Instead of getting distance ( once ) and speed updates ( on a regular basis ), it gets distance + initial speed + accel + accelerate_until + decelerate_after ( once ) and stores them, then does accel itself | |
* On accel tick call from stepticker, update speed ( speed += accel_change ), checks if we are at a point where the accel ramp needs to change, and if so, modify accel_change | |
Note : to try to limit the number of ifs, we don't test both for if(current_tick = accelerate_until) and if(current_tick = decelerate_after) but we check for if(current_tick = next_ramp_change), and then change next_ramp_change as needed when a ramp change happens | |
Note : we need to modify planner to express those in term of ticks instead of steps, and then just compare them with the current tick | |
void StepperMotor::move( direction, steps, total_move_ticks, accelerate_until, decelerate_after, acceleration_per_tick, deceleration_per_tick, initial speed ){ | |
*set direction pin | |
this->direction = direction | |
this->steps_to_move = steps | |
this->accelerate_until = accelerate_until; | |
this->decelerate_after = decelerate_after; | |
this->acceleration_per_tick = acceleration_per_tick; | |
this->deceleration_per_tick = deceleration_per_tick; | |
this->ticks = ticks; | |
this->next_accel_event = steps + 1; // Do nothing by default ( cruising/plateau ) | |
this->acceleration_change = 0; | |
if( accelerate_until != 0 ){ // If the next accel event is the end of accel | |
this->next_accel_event = accelerate_until; | |
this->acceleration_change = acceleration_per_tick; // Provided in the block by the new code in Block.cpp ( see bellow ) | |
} | |
if( this->decelerate_after != ticks && accelerate_until == 0 ){ // If the next accel even is the start of decel ( don't set this if the next accel event is accel end ) | |
this->next_accel_event = decelerate_after; | |
this->acceleration_change = -deceleration_per_tick; // Provided in the block by the new code in Block.cpp ( see bellow ) | |
} | |
*set initial speed | |
*other stuff that doesn't change | |
} | |
*Above when we need to compute acceleration_change, we mean we want to figure out which value must be added to fx_steps_per_tick *each tick* for the given acceleration | |
This means we must use steps_per_tick instead of ticks_per_step | |
Some pseudo-code to explain. | |
I'm deconstructing all of the steps frow what we do now to what we want to do | |
SKIP ALL THIS AND GO TO THE NEXT ALLCAPS LINE FOR THE END RESULT | |
---------------------------------------------------------------- | |
Right now we do this : | |
on speed updates : | |
check if we need to change the accel ramp and if needed change it | |
speed rate we want += acceleration in steps per second per tick | |
ticks_per_step = step_ticker_frequency ( in ticks per second ) / step rate we want ( in steps per second ); | |
then each tick : | |
counter += 1 | |
if( counter >= ticks_per_step ){ | |
call step: { | |
generate a step | |
counter -= ticks_per_step | |
} | |
} | |
If we want to do per-tick accel that becomes, each tick : | |
counter += 1 | |
check if we need to change the accel ramp and if needed change it | |
speed rate we want += acceleration in steps per second per tick | |
ticks_per_step = step_ticker_frequency ( in ticks per second ) / step rate we want ( in steps per second ); | |
if( counter >= ticks_per_step ){ | |
call step: { | |
generate a step | |
counter -= ticks_per_step | |
} | |
} | |
Now that has two problems : | |
* The decel problem where speed gets reduced all the time, preventing reaching the next step forever | |
* We are doing a division in a place where we want to as little math as possible | |
That's where ElMonkey's stuff comes in. | |
without per-tick accel it'd look like : | |
on speed updates : | |
check if we need to change the accel ramp and if needed change it | |
speed rate we want += acceleration in steps per second per tick | |
steps_per_tick = step rate we want ( in steps per second ) / step_ticker_frequency ( in ticks per second ); | |
then each tick : | |
counter += steps_per_tick | |
if( counter >= 1 ){ | |
call step: { | |
generate a step | |
counter -= 1 | |
} | |
} | |
Note the two differences : inverted the division for steps_per_tick, and we compare to 1 instead of comparing to that value | |
Now the same thing but doing accel every tick : | |
each tick : | |
check if we need to change the accel ramp and if needed change it | |
speed rate we want += acceleration in steps per second per tick | |
steps_per_tick = step rate we want ( in steps per second ) / step_ticker_frequency ( in ticks per second ); | |
counter += steps_per_tick | |
if( counter >= 1 ){ | |
call step: { | |
generate a step | |
counter -= 1 | |
} | |
} | |
We can optimize this : | |
speed rate we want += acceleration in steps per second per tick | |
steps_per_tick = step rate we want ( in steps per second ) / step_ticker_frequency ( in ticks per second ); | |
Which translate for short into ( this needs to be copied into the comments :) : | |
s(x) is speed rate we want at moment x | |
a is acceleration in steps per second per tick | |
t(x) is the number of steps per tick | |
f is a constant in ticks per second ( 100khz ) | |
so the code above translates into : | |
s(x+1) = s(x) + a | |
t(x+1) = s(x+1) / f | |
( ie : | |
s2 = s1 + a | |
t1 = s1 / f | |
t2 = s2 / f | |
) | |
so : | |
s1 = t1 * f | |
s2 = t2 * f | |
so: | |
t2 * f = ( t1 * f ) + a | |
so : | |
t2 = t1 + ( a / f ) | |
So : | |
t(x+1) = t(x) + ( a / f ) | |
Which translates in code into : | |
steps per tick += acceleration ( in steps per second per tick ) / step_ticker_frequency ( constant in ticks per second : 100khz ) | |
This is what we need to do to accelerate each step | |
And the great thing is : both acceleration and step_ticker_frequency are constant for a given acceleration ramp ( accel, decel, plateau ) so we -only- need to compute this value when changing the accel ramp | |
So the code above becomes : | |
each tick : | |
check if we need to change the accel ramp and if needed change it by doing : acceleration_change = acceleration / base_stepping_frequency | |
steps_per_tick += acceleration_change | |
counter += steps_per_tick | |
if( counter >= 1 ){ | |
call step: { | |
generate a step | |
counter -= 1 | |
} | |
} | |
Tadah ! | |
----------------------------------------------------------------------------------------- | |
THIS IS THE NEXT ALL CAPS LINE MENTIONNED ABOVE IF YOU ARE SKIPPING THE STEP BY STEP STUFF | |
In a bit more detail, considering the changes made to move() : | |
each tick : | |
if( current_TICK == next_accel_event ){ | |
if( current_TICK == accelerate_until ){ // We are done accelerating, decceleration becomes 0 : plateau | |
acceleration_change = 0; | |
if( decelerate_after < this->current_tick ){ | |
next_accel_event = decelerate_after; | |
} | |
} | |
if( current_TICK == decelerate_after ){ // We start decelerating | |
acceleration_change = -acceleration_per_tick; // This is provided in the block and thus to the move() method, if we did the changes to Block.cpp | |
} | |
} | |
steps_per_tick += acceleration_change | |
counter += steps_per_tick | |
if( counter >= 1 ){ | |
call step: { | |
generate a step | |
current_step += 1 | |
counter -= 1 | |
} | |
} | |
Done ! This is the new tick() with per-step accel | |
Notes on the above piece of code : | |
steps_per_tick is an unsigned fixed point number, from 1 ( one step every tick ) to a very small number ( very very slow speeds ) that is < 1 but > 0 | |
counter is an unsigned fixed point number, from 0 ( just reset ) to 1 ( we need to generate a step and reset ), note : it can actually be more than 1 and we must not forget what is above that 1 when we do counter -= 1 | |
acceleration_change is a SIGNED ( negative acceleration is deceleration ) fixed point number, from 0 ( plateau ) to plus or minus steps_per_tick | |
There is an additional thing to add here. | |
Some modules might want to know what the current speed actually is. | |
############################ | |
Changes to stepticker : | |
First we remove the accel tick, that goes away, we only need steppermotor->tick() now. | |
Then, simply call tick() for each stepper normally | |
And call the on_speed_change event. | |
Another thing stepticker has to be responsible for : some modules currently ask Stepper for the current speed. It so now Stepticker's job to tell them. | |
The first thing to do this, is when we start a new move, remember which axis is the fastest. | |
One method to do this ( there is probably a better method ) : | |
Stepticker gets a fastest_motor property | |
In remove_motor_from_active_list we do : | |
if( motor == this->fastest_motor ){ this->fastest_motor == null } | |
In add_motor_to_active_list we do : | |
if( this->fastest_motor == null || this->fastest_motor->steps_per_tick > motor->steps_per_tick ){ this->fastest_motor == motor; } | |
Now we always remember the currently fastest motor | |
Now we need a method in stepticker to get the current speed | |
float Steppermotor::get_current_speed(){ | |
return (float)( this->fastest_motor->steps_per_tick ); | |
} | |
For example this allows laser to know the current speed proportional to the maximum current speed, and set laser power proportionally | |
ALSO, stepticker needs to keep track of the number of ticks, so before calling tick() on each steppermotor in StepTicker::tick() | |
it needs to do : | |
current_tick++ | |
and when the move is finished in https://github.com/Smoothieware/Smoothieware/blob/edge/src/libs/StepTicker.cpp#L174 | |
we do : | |
current_tick = 0; | |
############################## | |
Changes to stepper : | |
* Removing all the speed change stuff, obviously | |
* Just pass along the right parameters to steppermotor->move when setting them up ( they should all be available in the block ) | |
############################# | |
Changes to extruder : | |
* Remove a shitton of crap | |
* Pass the right parameters to steppermotor->move when setting them up | |
Note : accelerate_until and decelerate_after are the same as the block's | |
But acceleration must be computed ( much like the current speed updates are calculated, proportional to the block's speed ) | |
FOR SOLO MOVES : | |
--------------- | |
We need to do ( part of the work the planner does for XYZ ) : | |
* Figure out how long to accelerate and decelerate | |
* Figure out if there is a plateau and if so change the decelerate_until | |
This is pretty much this code : https://github.com/Smoothieware/Smoothieware/blob/edge/src/modules/robot/Block.cpp#L108 to line 126 | |
And then we have our accelerate_until and decelerate_after | |
But it can be simpler because we don't have initial_rate and final_rate | |
So, in pseudo-code : | |
given steps_to_step ( number of steps to move ), a direction, this->feed_rate ( the current extruder max speed ) and acceleration ( an option in the config file ), all of which are currently available in extruder : | |
NOTE : This is going to happen initial and final rates for the extruders are 0, both because this is a solo move, and because it's less computation to do. If this turns out being a problem ( though it's pretty much what we do now ) | |
we can add more math ( pretty much exactly what is in Block::calculate_trapezoid ) to get smoother extruder speed transitions | |
this will probably not be needed however | |
just before this : https://github.com/Smoothieware/Smoothieware/blob/edge/src/modules/tools/extruder/Extruder.cpp#L487 we do : | |
// How many steps ( can be fractions of steps, we need very precise values ) to accelerate and decelerate | |
// Note : went from int to float | |
float acceleration_per_second(steps/s²) = ( this->acceleration(mm/s²) * steps_to_move(steps) ) / this->travel_distance(move distance in millimeters); // This is a simplification to get rid of rate_delta and get the steps/s² accel directly from the mm/s² accel | |
// Find out the intersection maximum rate for this block ( ignoring the requested nominal rate ) | |
// | |
/* + <- maximum rate for this block assuming we don't limit the rate | |
/|\ | |
/ | \ | |
/ | \ | |
/ | \ | |
initial_rate -> +----+----+ <- final_rate | |
*/ | |
float maximum_possible_rate(steps/s) = sqrt( steps_to_move(steps) * acceleration_per_second(steps/s²) ); | |
// Note : I'm pretty sure of my math here, but we do something similar in Block::max_allowable_speed, and there there is a negative ( and one of the speeds is 0 which is normal ) while here everything is positive | |
// This is probably normal, as that function gets called with -acceleration and is for computing decelerations anyway, but in case this gives wrong results ( which would be very noticeable ), try doing the same with either one, the other, or both | |
// of the + signs becoming - | |
// Note : the note just above is relevant in Block->generate_trapezoid, but not here as we both final_rate and initial_rate are 0 so we removed them so no -/+ sign | |
// The nominal rate is the feedrate(mm/s) converted to steps/s | |
float nominal_rate = this->feed_rate * this->steps_per_millimeter; | |
// Now this is the maximum rate we'll achieve this move, either because it's the higher we can achieve, or because it's the higher we are allowed to achieve | |
float maximum_rate(steps/s) = min( maximum_possible_rate(steps/s), nominal_rate(steps/s) ); | |
// Now figure out how long it takes to accelerate | |
float time_to_accelerate(seconds) = maximum_rate(steps/s) / acceleration_per_second(steps/s²); | |
// Now figure out how long it takes to decelerate | |
float time_to_decelerate(seconds) = ( -maximum_rate(steps/s) ) / -acceleration_per_seconds(steps/s²); | |
// Now we know how long it takes to accelerate and decelerate, but we must also know how long the entire move takes so we can figure out how long is the plateau if there is one | |
float plateau_time = 0; | |
// Only if there is actually a plateau ( we are limited by nominal_rate ) | |
if( maximum_possible_rate(steps/s) > nominal_rate(steps/s) ){ | |
// Figure out the acceleration and deceleration distances ( in steps ) | |
float acceleration_distance(steps) = ( maximum_rate(steps/s) / 2 ) * time_to_accelerate(seconds); | |
float deceleration_distance(steps) = ( maximum_rate(steps/s) / 2 ) * time_to_decelerate(seconds); | |
// Figure out the plateau steps | |
float plateau_distance(steps) = steps_to_move(steps) - acceleration_distance(steps) - deceleration_distance(steps); | |
// Figure out the plateau time | |
float plateau_time(seconds) = plateau_distance(steps) / maximum_rate(steps/s); | |
} | |
// Figure out how long the move takes total ( in seconds ) | |
float total_move_time(seconds) = time_to_accelerate(seconds) + time_to_decelerate(seconds) + plateau_time(seconds); | |
// We now have the full timing for acceleration, plateau and deceleration, yay \o/ | |
// Now this is very important : these are in seconds, and we need to round them into ticks. | |
// This means instead of accelerating in 100.23 ticks we'll accelerate in 100 ticks. | |
// Which means to reach the exact speed we want to reach, we must figure out a new/slightly different acceleration/deceleration to be sure we accelerate and decelerate at the exact rate we want | |
// First off round total time, acceleration time and deceleration time | |
int acceleration_ticks(ticks) = (int)floor( time_to_accelerate(seconds) * KERNEL->stepticker->get_frequency()(ticks/seconds) ); | |
int deceleration_ticks(ticks) = (int)floor( time_to_decelerate(seconds) * KERNEL->stepticker->get_frequency()(ticks/seconds) ); | |
int total_move_ticks(ticks) = (int)floor( total_move_time(seconds) * KERNEL->stepticker->get_frequency()(ticks/seconds) ); | |
// Now deduce the plateau time for those new values expressed in tick | |
int plateau_ticks = total_move_ticks - acceleration_ticks - deceleration_ticks; | |
// Now we figure out the acceleration value to reach EXACTLY maximum_rate(steps/s) in EXACTLY acceleration_ticks(ticks) amount of time. | |
float acceleration_time(s) = acceleration_ticks(ticks) / Kernel->stepticker->get_frequency(ticks/seconds); // This can be moved into the operation bellow, separated for clarity, note : we need to do this instead of using time_to_accelerate(seconds) directly because time_to_accelerate(seconds) and acceleration_ticks(seconds) do not have the same value anymore due to the rounding | |
float acceleration_in_steps(steps/s²) = ( maximum_rate(steps/s) - initial_rate(steps/s) ) / acceleration_time(s); | |
// Note : we use this value for acceleration as well as for deceleration, if that doesn't work, we can also as well compute the deceleration value this way : | |
// float deceleration(steps/s²) = ( final_rate(steps/s) - maximum_rate(steps/s) ) / acceleration_time(s); | |
// And pass that along to a new Steppermotor->move() that would accept a deceleration_per_tick value and use it | |
// Now figure out the two acceleration ramp change events | |
int accelerate_until(ticks) = acceleration_ticks(ticks); | |
int decelerate_after(ticks) = total_move_ticks(ticks) - deceleration_ticks(ticks); | |
// Now figure out the acceleration PER TICK, this should ideally be held as a float, even a double if possible as it's very critical to the block timing | |
int acceleration_per_tick(steps/s/tick) = acceleration_in_steps(steps/s²) / KERNEL->stepticker->get_frequency()(ticks/seconds); | |
// Now finally we call the move | |
this->stepper_motor->move( ( this->travel_distance > 0 ), steps_to_step, total_move_ticks, accelerate_until, decelerate_after, acceleration_per_tick, 0); // The 0 is initial_rate | |
Note : | |
THIS IS A HUGE AMOUNT OF MATH TO DO IN ON_BLOCK_BEGIN !!!! | |
And it can be done in on_gcode_received really, if we store the result for use in on_block_begin | |
Here : https://github.com/Smoothieware/Smoothieware/blob/edge/src/modules/tools/extruder/Extruder.cpp#L305 | |
This means having Extruder store these values in some way, which I've never been able to do correctly | |
The way it would work is : | |
gcode_received: | |
if( G0/G1 and solo move ){ | |
do the math, figure out accelerate_until and decelerate_after and acceleration_per_tick and direction and steps_to_steps | |
store those in an object of some kind, also that object has a pointer to the block we just created | |
add that object to some sort of array that is a property of | |
go over that array and remove all the elements that have been marked for deletion | |
} | |
Then in on_block_begin: | |
find that object in the array ( new_block == array_element->block_pointer ) | |
use that objects properties to call steppermotor->move() | |
mark that object for deletion | |
If that makes sense ... | |
Actually once we start doing this, much of what is done in on_gcode_execute can be done this way much more efficiently too, but that's a lot of work | |
FOR FOLLOW MOVES: | |
----------------- | |
Now in the case of follow moves, it's quite different. | |
We just follow the accel ramp the planner has computed for us | |
So, we know accelerate_until ( same as the block ), decelerate_after ( same as the block ), steps_to_move, ticks ( same as the block ) and direction, those can just be passed along to move | |
The two values we need to compute are acceleration_per_tick, and initial_rate | |
initial_rate is easy, it's just our feed rate multiplied by the ratio of our number of steps to the block's number of steps | |
float initial_rate(steps/s) = ( this->feed_rate(mm/s) * this->steps_per_mm(steps/mm) ) * ( steps_to_steps(steps) / block->steps_event_count(steps) ); | |
and for acceleration_per_tick(steps/s/tick), we do the same thing, but to the block's acceleration : | |
float acceleration_per_tick(steps/s/tick) = block->acceleration_per_tick * ( steps_to_steps(steps) / block->steps_event_count(steps) ); | |
And now we have all we need to call move() for a follow move | |
############################ | |
Changes to planner | |
Planner does not compute rate_delta anymore ! And Block does not hold it anymore, this is replaced by acceleration_per_tick | |
So this goes away : https://github.com/Smoothieware/Smoothieware/blob/edge/src/modules/robot/Planner.cpp#L111 | |
And Block.h forgets about it too | |
############################# | |
Changes to block | |
If we want to turn accelerate_until and decelerate_after from being expressed in steps ( of the fastest axis ) to being expressed in ticks, we need to change this : | |
https://github.com/Smoothieware/Smoothieware/blob/edge/src/modules/robot/Block.cpp#L110 | |
The main changes happen in calculate_trapezoid. Here is the new calculate_trapezoid : | |
Block::Calculate_trapezoid( float entryspeed(mm/s), float exitspeed(mm/s) ){ | |
// if block is currently executing, don't touch anything! | |
if (times_taken) | |
return; | |
// The planner passes us factors, we need to transform them in rates | |
// Note : went from int to float as we do rounding later | |
float initial_rate(steps/s) = this->nominal_rate(steps/s) * ( entryspeed(mm/s) / this->nominal_speed(mm/s) ); | |
float final_rate(steps/s) = this->nominal_rate(steps/s) * ( exitspeed(mm/s) / this->nominal_speed(mm/s) ); | |
// How many steps ( can be fractions of steps, we need very precise values ) to accelerate and decelerate | |
// Note : went from int to float | |
float acceleration_per_second(steps/s²) = ( this->acceleration(mm/s²) * this->steps_event_count(steps) ) / this->millimiters(move distance in millimeters); // This is a simplification to get rid of rate_delta and get the steps/s² accel directly from the mm/s² accel | |
// Find out the intersection maximum rate for this block ( ignoring the requested nominal rate ) | |
// | |
/* + <- maximum rate for this block assuming we don't limit the rate | |
/|\ | |
/ | \ | |
/ | + <- final_rate | |
/ | | | |
initial_rate -> +----+--+ | |
*/ | |
float maximum_possible_rate(steps/s) = sqrt( ( this->steps_event_count(steps) * acceleration_per_second(steps/s²) ) + ( ( initial_rate(steps/s)² + final_rate(steps/s)² ) / 2 ) ); | |
// Note : I'm pretty sure of my math here, but we do something similar in Block::max_allowable_speed, and there there is a negative ( and one of the speeds is 0 which is normal ) while here everything is positive | |
// This is probably normal, as that function gets called with -acceleration and is for computing decelerations anyway, but in case this gives wrong results ( which would be very noticeable ), try doing the same with either one, the other, or both | |
// of the + signs becoming - | |
// Now this is the maximum rate we'll achieve this move, either because it's the higher we can achieve, or because it's the higher we are allowed to achieve | |
float maximum_rate(steps/s) = min( maximum_possible_rate(steps/s), this->nominal_rate(steps/s) ); | |
// Now figure out how long it takes to accelerate | |
float time_to_accelerate(seconds) = ( maximum_rate(steps/s) - initial_rate(steps/s) ) / acceleration_per_second(steps/s²); | |
// Now figure out how long it takes to decelerate | |
float time_to_decelerate(seconds) = ( final_rate(steps/s) - maximum_rate(steps/s) ) / -acceleration_per_seconds(steps/s²); | |
// Now we know how long it takes to accelerate and decelerate, but we must also know how long the entire move takes so we can figure out how long is the plateau if there is one | |
float plateau_time = 0; | |
// Only if there is actually a plateau ( we are limited by nominal_rate ) | |
if( maximum_possible_rate(steps/s) > this->nominal_rate(steps/s) ){ | |
// Figure out the acceleration and deceleration distances ( in steps ) | |
float acceleration_distance(steps) = ( ( initial_rate(steps/s) + maximum_rate(steps/s) ) / 2 ) * time_to_accelerate(seconds); | |
float deceleration_distance(steps) = ( ( maximum_rate(steps/s) + final_rate(steps/s) ) / 2 ) * time_to_decelerate(seconds); | |
// Figure out the plateau steps | |
float plateau_distance(steps) = this->steps_event_count(steps) - acceleration_distance(steps) - deceleration_distance(steps); | |
// Figure out the plateau time | |
float plateau_time(seconds) = plateau_distance(steps) / maximum_rate(steps/s); | |
} | |
// Figure out how long the move takes total ( in seconds ) | |
float total_move_time(seconds) = time_to_accelerate(seconds) + time_to_decelerate(seconds) + plateau_time(seconds); | |
// We now have the full timing for acceleration, plateau and deceleration, yay \o/ | |
// Now this is very important : these are in seconds, and we need to round them into ticks. | |
// This means instead of accelerating in 100.23 ticks we'll accelerate in 100 ticks. | |
// Which means to reach the exact speed we want to reach, we must figure out a new/slightly different acceleration/deceleration to be sure we accelerate and decelerate at the exact rate we want | |
// First off round total time, acceleration time and deceleration time | |
int acceleration_ticks(ticks) = (int)floor( time_to_accelerate(seconds) * KERNEL->stepticker->get_frequency()(ticks/seconds) ); | |
int deceleration_ticks(ticks) = (int)floor( time_to_decelerate(seconds) * KERNEL->stepticker->get_frequency()(ticks/seconds) ); | |
int total_move_ticks(ticks) = (int)floor( total_move_time(seconds) * KERNEL->stepticker->get_frequency()(ticks/seconds) ); | |
// Now deduce the plateau time for those new values expressed in tick | |
int plateau_ticks = total_move_ticks - acceleration_ticks - deceleration_ticks; | |
// Now we figure out the acceleration value to reach EXACTLY maximum_rate(steps/s) in EXACTLY acceleration_ticks(ticks) amount of time. | |
float acceleration_time(s) = acceleration_ticks(ticks) / Kernel->stepticker->get_frequency(ticks/seconds); // This can be moved into the operation bellow, separated for clarity, note : we need to do this instead of using time_to_accelerate(seconds) directly because time_to_accelerate(seconds) and acceleration_ticks(seconds) do not have the same value anymore due to the rounding | |
float acceleration_in_steps(steps/s²) = ( maximum_rate(steps/s) - initial_rate(steps/s) ) / acceleration_time(s); | |
// Note : we use this value for acceleration as well as for deceleration, if that doesn't work, we can also as well compute the deceleration value this way : | |
// float deceleration(steps/s²) = ( final_rate(steps/s) - maximum_rate(steps/s) ) / acceleration_time(s); | |
// and store that in the block and use it for deceleration, which -will- yield better results, but may not be useful. If the moves do not end correctly, try computing this value, adding it to the block, and then using it for deceleration in the step generator | |
// Now figure out the two acceleration ramp change events | |
this->accelerate_until(ticks) = acceleration_ticks(ticks); | |
this->decelerate_after(ticks) = total_move_ticks(ticks) - deceleration_ticks(ticks); | |
// Now figure out the acceleration PER TICK, this should ideally be held as a float, even a double if possible as it's very critical to the block timing | |
this->acceleration_per_tick(steps/s/tick) = acceleration_in_steps(steps/s²) / KERNEL->stepticker->get_frequency()(ticks/seconds); | |
// We now have everything we need for this block to call a Steppermotor->move method !!!! | |
// Theorically, if accel is done per tick, the speed curve should be perfect. | |
// We need this to call move() | |
this->total_move_ticks = total_move_ticks | |
// I'm not sure what this does, but I kept it here | |
this->exit_speed = exitspeed; | |
} | |
Note to self : Also talk to Jim about the insane idea with looking at the next block -before- the block is actually done |
Here is a bad case... starts at 100 should exit at 0, but it declerates the entire time and stops too early...
https://gist.github.com/d9fd6cbf12a76ee17e13
This is the problem.. https://gist.github.com/arthurwolf/ed8cc3bce15ac395d8e7#file-gistfile1-txt-L29
acceleration change needs to be set to 0 in this case because it does not want to decelerate from the get go.
However there is another case that needs to be handled that is when it does start decelerating for the first tick.
This fixes that previous case and the other...
# handle case where deceleration is from step 0
if accelerate_until == 0 && decelerate_after == 0
@acceleration_change = -deceleration_per_tick
elsif decelerate_after != total_move_ticks && accelerate_until == 0 # If the next accel even is the start of decel ( don't set this if the next accel event is accel end )
@next_accel_event = decelerate_after
@acceleration_change = 0 # -deceleration_per_tick
end
I have updated the multi axis sim with these changes
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
does counter get reset to 0 when move() is issued? or is there some overflow required to be kept from previous steps?