Skip to content

Instantly share code, notes, and snippets.

@arthurwolf
Last active August 29, 2015 14:10
Show Gist options
  • Save arthurwolf/ed8cc3bce15ac395d8e7 to your computer and use it in GitHub Desktop.
Save arthurwolf/ed8cc3bce15ac395d8e7 to your computer and use it in GitHub Desktop.
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
@wolfmanjm
Copy link

does counter get reset to 0 when move() is issued? or is there some overflow required to be kept from previous steps?

@wolfmanjm
Copy link

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.

@wolfmanjm
Copy link

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