return;
// The planner passes us factors, we need to transform them in rates
- this->initial_rate = ceil(this->nominal_rate * entryspeed / this->nominal_speed); // (step/min)
- this->final_rate = ceil(this->nominal_rate * exitspeed / this->nominal_speed); // (step/min)
+ this->initial_rate = ceil(this->nominal_rate * entryspeed / this->nominal_speed); // (step/s)
+ this->final_rate = ceil(this->nominal_rate * exitspeed / this->nominal_speed); // (step/s)
// How many steps to accelerate and decelerate
- float acceleration_per_minute = this->rate_delta * THEKERNEL->stepper->acceleration_ticks_per_second * 60.0; // ( step/min^2)
- int accelerate_steps = ceil( this->estimate_acceleration_distance( this->initial_rate, this->nominal_rate, acceleration_per_minute ) );
- int decelerate_steps = floor( this->estimate_acceleration_distance( this->nominal_rate, this->final_rate, -acceleration_per_minute ) );
+ float acceleration_per_second = this->rate_delta * THEKERNEL->stepper->acceleration_ticks_per_second; // ( step/s^2)
+ int accelerate_steps = ceil( this->estimate_acceleration_distance( this->initial_rate, this->nominal_rate, acceleration_per_second ) );
+ int decelerate_steps = floor( this->estimate_acceleration_distance( this->nominal_rate, this->final_rate, -acceleration_per_second ) );
// Calculate the size of Plateau of Nominal Rate ( during which we don't accelerate nor decelerate, but just cruise )
int plateau_steps = this->steps_event_count - accelerate_steps - decelerate_steps;
// have to use intersection_distance() to calculate when to abort acceleration and start braking
// in order to reach the final_rate exactly at the end of this block.
if (plateau_steps < 0) {
- accelerate_steps = ceil(this->intersection_distance(this->initial_rate, this->final_rate, acceleration_per_minute, this->steps_event_count));
+ accelerate_steps = ceil(this->intersection_distance(this->initial_rate, this->final_rate, acceleration_per_second, this->steps_event_count));
accelerate_steps = max( accelerate_steps, 0 ); // Check limits due to numerical round-off
accelerate_steps = min( accelerate_steps, int(this->steps_event_count) );
plateau_steps = 0;
// Configure acceleration
void Planner::on_config_reload(void* argument){
- this->acceleration = THEKERNEL->config->value(acceleration_checksum )->by_default(100 )->as_number() * 60 * 60; // Acceleration is in mm/minute^2, see https://github.com/grbl/grbl/commit/9141ad282540eaa50a41283685f901f29c24ddbd#planner.c
- this->junction_deviation = THEKERNEL->config->value(junction_deviation_checksum )->by_default(0.05f)->as_number();
+ this->acceleration = THEKERNEL->config->value(acceleration_checksum )->by_default(100.0F )->as_number(); // Acceleration is in mm/s^2, see https://github.com/grbl/grbl/commit/9141ad282540eaa50a41283685f901f29c24ddbd#planner.c
+ this->junction_deviation = THEKERNEL->config->value(junction_deviation_checksum )->by_default( 0.05F)->as_number();
this->minimum_planner_speed = THEKERNEL->config->value(minimum_planner_speed_checksum )->by_default(0.0f)->as_number();
}
// Append a block to the queue, compute it's speed factors
-void Planner::append_block( float actuator_pos[], float feed_rate, float distance, float unit_vec[] ){
-
+void Planner::append_block( float actuator_pos[], float rate_mm_s, float distance, float unit_vec[] )
+{
// Create ( recycle ) a new block
Block* block = THEKERNEL->conveyor->queue.head_ref();
// Calculate speed in mm/minute for each axis. No divide by zero due to previous checks.
// NOTE: Minimum stepper speed is limited by MINIMUM_STEPS_PER_MINUTE in stepper.c
if( distance > 0.0F ){
- block->nominal_speed = feed_rate; // (mm/min) Always > 0
- block->nominal_rate = ceil(block->steps_event_count * feed_rate / distance); // (step/min) Always > 0
+ block->nominal_speed = rate_mm_s; // (mm/s) Always > 0
+ block->nominal_rate = ceil(block->steps_event_count * rate_mm_s / distance); // (step/s) Always > 0
}else{
block->nominal_speed = 0.0F;
block->nominal_rate = 0;
// To generate trapezoids with contant acceleration between blocks the rate_delta must be computed
// specifically for each line to compensate for this phenomenon:
// Convert universal acceleration for direction-dependent stepper rate change parameter
- block->rate_delta = (block->steps_event_count * acceleration) / (distance * (THEKERNEL->stepper->acceleration_ticks_per_second * 60)); // (step/min/acceleration_tick)
+ block->rate_delta = (block->steps_event_count * acceleration) / (distance * THEKERNEL->stepper->acceleration_ticks_per_second); // (step/min/acceleration_tick)
// Compute maximum allowable entry speed at junction by centripetal acceleration approximation.
// Let a circle be tangent to both previous and current path line segments, where the junction
block->max_entry_speed = vmax_junction;
// Initialize block entry speed. Compute based on deceleration to user-defined minimum_planner_speed.
- float v_allowable = this->max_allowable_speed(-this->acceleration,minimum_planner_speed,block->millimeters); //TODO: Get from config
+ float v_allowable = max_allowable_speed(-acceleration, minimum_planner_speed, block->millimeters); //TODO: Get from config
block->entry_speed = min(vmax_junction, v_allowable);
// Initialize planner efficiency flags
class Planner : public Module {
public:
Planner();
- void append_block( float target[], float feed_rate, float distance, float unit_vec[] );
+ void append_block( float target[], float rate_mm_s, float distance, float unit_vec[] );
float max_allowable_speed( float acceleration, float target_velocity, float distance);
void recalculate();
Block* get_current_block();
clear_vector(this->current_position);
clear_vector(this->last_milestone);
this->arm_solution = NULL;
- seconds_per_minute = 60.0;
+ seconds_per_minute = 60.0F;
}
//Called when the module has just been loaded
}
- this->feed_rate = THEKERNEL->config->value(default_feed_rate_checksum )->by_default(100 )->as_number() / 60;
- this->seek_rate = THEKERNEL->config->value(default_seek_rate_checksum )->by_default(100 )->as_number() / 60;
- this->mm_per_line_segment = THEKERNEL->config->value(mm_per_line_segment_checksum )->by_default(0.0f )->as_number();
+ this->feed_rate = THEKERNEL->config->value(default_feed_rate_checksum )->by_default( 100.0F)->as_number();
+ this->seek_rate = THEKERNEL->config->value(default_seek_rate_checksum )->by_default( 100.0F)->as_number();
+ this->mm_per_line_segment = THEKERNEL->config->value(mm_per_line_segment_checksum )->by_default( 0.0F)->as_number();
this->delta_segments_per_second = THEKERNEL->config->value(delta_segments_per_second_checksum )->by_default(0.0f )->as_number();
- this->mm_per_arc_segment = THEKERNEL->config->value(mm_per_arc_segment_checksum )->by_default(0.5f )->as_number();
- this->arc_correction = THEKERNEL->config->value(arc_correction_checksum )->by_default(5 )->as_number();
+ this->mm_per_arc_segment = THEKERNEL->config->value(mm_per_arc_segment_checksum )->by_default( 0.5f)->as_number();
+ this->arc_correction = THEKERNEL->config->value(arc_correction_checksum )->by_default( 5 )->as_number();
- this->max_speeds[X_AXIS] = THEKERNEL->config->value(x_axis_max_speed_checksum )->by_default(60000 )->as_number();
- this->max_speeds[Y_AXIS] = THEKERNEL->config->value(y_axis_max_speed_checksum )->by_default(60000 )->as_number();
- this->max_speeds[Z_AXIS] = THEKERNEL->config->value(z_axis_max_speed_checksum )->by_default(300 )->as_number();
+ this->max_speeds[X_AXIS] = THEKERNEL->config->value(x_axis_max_speed_checksum )->by_default(60000.0F)->as_number();
+ this->max_speeds[Y_AXIS] = THEKERNEL->config->value(y_axis_max_speed_checksum )->by_default(60000.0F)->as_number();
+ this->max_speeds[Z_AXIS] = THEKERNEL->config->value(z_axis_max_speed_checksum )->by_default( 300.0F)->as_number();
Pin alpha_step_pin;
Pin alpha_dir_pin;
if(pdr->second_element_is(speed_override_percent_checksum)) {
static float return_data;
- return_data= 100*this->seconds_per_minute/60;
+ return_data = 100.0F * 60.0F / seconds_per_minute;
pdr->set_data_ptr(&return_data);
pdr->set_taken();
// NOTE do not use this while printing!
float t= *static_cast<float*>(pdr->get_data_ptr());
// enforce minimum 10% speed
- if (t < 10.0) t= 10.0;
+ if (t < 10.0F) t= 10.0F;
- this->seconds_per_minute= t * 0.6;
+ this->seconds_per_minute = t / 0.6F; // t * 60 / 100
pdr->set_taken();
}
}
gcode->mark_as_taken();
if (gcode->has_letter('S'))
{
- float acc= gcode->get_value('S') * 60 * 60; // mm/min^2
+ float acc= gcode->get_value('S'); // mm/s^2
// enforce minimum
- if (acc < 1.0)
- acc = 1.0;
+ if (acc < 1.0F)
+ acc = 1.0F;
THEKERNEL->planner->acceleration= acc;
}
break;
{
float factor = gcode->get_value('S');
// enforce minimum 10% speed
- if (factor < 10.0)
- factor = 10.0;
- seconds_per_minute = factor * 0.6;
+ if (factor < 10.0F)
+ factor = 10.0F;
+ // enforce maximum 10x speed
+ if (factor > 1000.0F)
+ factor = 1000.0F;
+
+ seconds_per_minute = 6000.0F / factor;
}
break;
case 500: // M500 saves some volatile settings to config override file
case 503: // M503 just prints the settings
gcode->stream->printf(";Steps per unit:\nM92 X%1.5f Y%1.5f Z%1.5f\n", actuators[0]->steps_per_mm, actuators[1]->steps_per_mm, actuators[2]->steps_per_mm);
- gcode->stream->printf(";Acceleration mm/sec^2:\nM204 S%1.5f\n", THEKERNEL->planner->acceleration/3600);
+ gcode->stream->printf(";Acceleration mm/sec^2:\nM204 S%1.5f\n", THEKERNEL->planner->acceleration);
gcode->stream->printf(";X- Junction Deviation, S - Minimum Planner speed:\nM205 X%1.5f S%1.5f\n", THEKERNEL->planner->junction_deviation, THEKERNEL->planner->minimum_planner_speed);
gcode->mark_as_taken();
break;
if( gcode->has_letter('F') )
{
if( this->motion_mode == MOTION_MODE_SEEK )
- this->seek_rate = this->to_millimeters( gcode->get_value('F') ) / 60.0F;
+ this->seek_rate = this->to_millimeters( gcode->get_value('F') );
else
- this->feed_rate = this->to_millimeters( gcode->get_value('F') ) / 60.0F;
+ this->feed_rate = this->to_millimeters( gcode->get_value('F') );
}
//Perform any physical actions
case NEXT_ACTION_DEFAULT:
switch(this->motion_mode){
case MOTION_MODE_CANCEL: break;
- case MOTION_MODE_SEEK : this->append_line(gcode, target, this->seek_rate ); break;
- case MOTION_MODE_LINEAR: this->append_line(gcode, target, this->feed_rate ); break;
+ case MOTION_MODE_SEEK : this->append_line(gcode, target, this->seek_rate / seconds_per_minute ); break;
+ case MOTION_MODE_LINEAR: this->append_line(gcode, target, this->feed_rate / seconds_per_minute ); break;
case MOTION_MODE_CW_ARC: case MOTION_MODE_CCW_ARC: this->compute_arc(gcode, offset, target ); break;
}
break;
// Convert target from millimeters to steps, and append this to the planner
-void Robot::append_milestone( float target[], float rate )
+void Robot::append_milestone( float target[], float rate_mm_s )
{
float deltas[3];
float unit_vec[3];
{
if ( max_speeds[axis] > 0 )
{
- float axis_speed = fabs(unit_vec[axis] * rate) * seconds_per_minute;
+ float axis_speed = fabs(unit_vec[axis] * rate_mm_s);
if (axis_speed > max_speeds[axis])
- rate = rate * ( max_speeds[axis] / axis_speed );
+ rate_mm_s *= ( max_speeds[axis] / axis_speed );
}
}
// check per-actuator speed limits
for (int actuator = 0; actuator <= 2; actuator++)
{
- float actuator_rate = fabs(actuator_pos[actuator] - actuators[actuator]->last_milestone_mm) * rate / millimeters_of_travel;
+ float actuator_rate = fabs(actuator_pos[actuator] - actuators[actuator]->last_milestone_mm) * rate_mm_s / millimeters_of_travel;
if (actuator_rate > actuators[actuator]->max_rate)
- rate *= (actuators[actuator]->max_rate / actuator_rate);
+ rate_mm_s *= (actuators[actuator]->max_rate / actuator_rate);
}
// Append the block to the planner
- THEKERNEL->planner->append_block( actuator_pos, rate * seconds_per_minute, millimeters_of_travel, unit_vec );
+ THEKERNEL->planner->append_block( actuator_pos, rate_mm_s, millimeters_of_travel, unit_vec );
// Update the last_milestone to the current target for the next time we use last_milestone
memcpy(this->last_milestone, target, sizeof(this->last_milestone)); // this->last_milestone[] = target[];
}
// Append a move to the queue ( cutting it into segments if needed )
-void Robot::append_line(Gcode* gcode, float target[], float rate ){
+void Robot::append_line(Gcode* gcode, float target[], float rate_mm_s ){
// Find out the distance for this gcode
gcode->millimeters_of_travel = sqrtf( pow( target[X_AXIS]-this->current_position[X_AXIS], 2 ) + pow( target[Y_AXIS]-this->current_position[Y_AXIS], 2 ) + pow( target[Z_AXIS]-this->current_position[Z_AXIS], 2 ) );
// segment based on current speed and requested segments per second
// the faster the travel speed the fewer segments needed
// NOTE rate is mm/sec and we take into account any speed override
- float seconds = 60.0/seconds_per_minute * gcode->millimeters_of_travel / rate;
+ float seconds = gcode->millimeters_of_travel / rate_mm_s;
segments= max(1, ceil(this->delta_segments_per_second * seconds));
// TODO if we are only moving in Z on a delta we don't really need to segment at all
for( int i=0; i<segments-1; i++ ){
for(int axis=X_AXIS; axis <= Z_AXIS; axis++ ){ temp_target[axis] += ( target[axis]-this->current_position[axis] )/segments; }
// Append the end of this segment to the queue
- this->append_milestone(temp_target, rate);
+ this->append_milestone(temp_target, rate_mm_s);
}
// Append the end of this full move to the queue
- this->append_milestone(target, rate);
+ this->append_milestone(target, rate_mm_s);
// if adding these blocks didn't start executing, do that now
THEKERNEL->conveyor->ensure_running();
arc_target[this->plane_axis_2] += linear_per_segment;
// Append this segment to the queue
- this->append_milestone(arc_target, this->feed_rate);
+ this->append_milestone(arc_target, this->feed_rate / seconds_per_minute);
}
// Ensure last segment arrives at target location.
- this->append_milestone(target, this->feed_rate);
+ this->append_milestone(target, this->feed_rate / seconds_per_minute);
}
// Do the math for an arc and add it to the queue
private:
void distance_in_gcode_is_known(Gcode* gcode);
- void append_milestone( float target[], float feed_rate);
- void append_line( Gcode* gcode, float target[], float feed_rate);
+ void append_milestone( float target[], float rate_mm_s);
+ void append_line( Gcode* gcode, float target[], float rate_mm_s);
//void append_arc(float theta_start, float angular_travel, float radius, float depth, float rate);
void append_arc( Gcode* gcode, float target[], float offset[], float radius, bool is_clockwise );
float current_position[3]; // Current position, in millimeters
float last_milestone[3]; // Last position, in millimeters
- bool inch_mode; // true for inch mode, false for millimeter mode ( default )
+ bool inch_mode; // true for inch mode, false for millimeter mode ( default )
int8_t motion_mode; // Motion mode for the current received Gcode
float seek_rate; // Current rate for seeking moves ( mm/s )
float feed_rate; // Current rate for feeding moves ( mm/s )
void Stepper::on_config_reload(void* argument){
this->acceleration_ticks_per_second = THEKERNEL->config->value(acceleration_ticks_per_second_checksum)->by_default(100 )->as_number();
- this->minimum_steps_per_minute = THEKERNEL->config->value(minimum_steps_per_minute_checksum )->by_default(3000 )->as_number();
+ this->minimum_steps_per_second = THEKERNEL->config->value(minimum_steps_per_minute_checksum )->by_default(3000 )->as_number() / 60;
// Steppers start off by default
this->turn_enable_pins_off();
// Do not accel, just set the value
if( this->force_speed_update ){
this->force_speed_update = false;
- this->set_step_events_per_minute(this->trapezoid_adjusted_rate);
+ this->set_step_events_per_second(this->trapezoid_adjusted_rate);
return 0;
}
if (this->trapezoid_adjusted_rate > this->current_block->nominal_rate ) {
this->trapezoid_adjusted_rate = this->current_block->nominal_rate;
}
- this->set_step_events_per_minute(this->trapezoid_adjusted_rate);
+ this->set_step_events_per_second(this->trapezoid_adjusted_rate);
// If we are decelerating
}else if (current_steps_completed > this->current_block->decelerate_after) {
// Reduce speed
// NOTE: We will only reduce speed if the result will be > 0. This catches small
// rounding errors that might leave steps hanging after the last trapezoid tick.
- if(this->trapezoid_adjusted_rate > this->current_block->rate_delta * 1.5) {
+ if(this->trapezoid_adjusted_rate > this->current_block->rate_delta * 1.5F) {
this->trapezoid_adjusted_rate -= this->current_block->rate_delta;
}else{
- this->trapezoid_adjusted_rate = this->current_block->rate_delta * 1.5;
+ this->trapezoid_adjusted_rate = this->current_block->rate_delta * 1.5F;
}
if(this->trapezoid_adjusted_rate < this->current_block->final_rate ) {
this->trapezoid_adjusted_rate = this->current_block->final_rate;
}
- this->set_step_events_per_minute(this->trapezoid_adjusted_rate);
+ this->set_step_events_per_second(this->trapezoid_adjusted_rate);
// If we are cruising
}else {
// Make sure we cruise at exactly nominal rate
if (this->trapezoid_adjusted_rate != this->current_block->nominal_rate) {
this->trapezoid_adjusted_rate = this->current_block->nominal_rate;
- this->set_step_events_per_minute(this->trapezoid_adjusted_rate);
+ this->set_step_events_per_second(this->trapezoid_adjusted_rate);
}
}
}
}
// Update the speed for all steppers
-void Stepper::set_step_events_per_minute( float steps_per_minute ){
-
+void Stepper::set_step_events_per_second( float steps_per_second )
+{
// We do not step slower than this
//steps_per_minute = max(steps_per_minute, this->minimum_steps_per_minute);
- if( steps_per_minute < this->minimum_steps_per_minute ){
- steps_per_minute = this->minimum_steps_per_minute;
+ if( steps_per_second < this->minimum_steps_per_second ){
+ steps_per_second = this->minimum_steps_per_second;
}
// Instruct the stepper motors
- if( THEKERNEL->robot->alpha_stepper_motor->moving ){ THEKERNEL->robot->alpha_stepper_motor->set_speed( (steps_per_minute/60L) * ( (float)this->current_block->steps[ALPHA_STEPPER] / (float)this->current_block->steps_event_count ) ); }
- if( THEKERNEL->robot->beta_stepper_motor->moving ){ THEKERNEL->robot->beta_stepper_motor->set_speed( (steps_per_minute/60L) * ( (float)this->current_block->steps[BETA_STEPPER ] / (float)this->current_block->steps_event_count ) ); }
- if( THEKERNEL->robot->gamma_stepper_motor->moving ){ THEKERNEL->robot->gamma_stepper_motor->set_speed( (steps_per_minute/60L) * ( (float)this->current_block->steps[GAMMA_STEPPER] / (float)this->current_block->steps_event_count ) ); }
+ if( THEKERNEL->robot->alpha_stepper_motor->moving ){ THEKERNEL->robot->alpha_stepper_motor->set_speed( steps_per_second * ( (float)this->current_block->steps[ALPHA_STEPPER] / (float)this->current_block->steps_event_count ) ); }
+ if( THEKERNEL->robot->beta_stepper_motor->moving ){ THEKERNEL->robot->beta_stepper_motor->set_speed( steps_per_second * ( (float)this->current_block->steps[BETA_STEPPER ] / (float)this->current_block->steps_event_count ) ); }
+ if( THEKERNEL->robot->gamma_stepper_motor->moving ){ THEKERNEL->robot->gamma_stepper_motor->set_speed( steps_per_second * ( (float)this->current_block->steps[GAMMA_STEPPER] / (float)this->current_block->steps_event_count ) ); }
// Other modules might want to know the speed changed
THEKERNEL->call_event(ON_SPEED_CHANGE, this);
void on_pause(void* argument);
uint32_t main_interrupt(uint32_t dummy);
void trapezoid_generator_reset();
- void set_step_events_per_minute(float steps_per_minute);
+ void set_step_events_per_second(float);
uint32_t trapezoid_generator_tick(uint32_t dummy);
uint32_t stepper_motor_finished_move(uint32_t dummy);
int config_step_timer( int cycles );
bool trapezoid_generator_busy;
int microseconds_per_step_pulse;
int acceleration_ticks_per_second;
- unsigned int minimum_steps_per_minute;
+ unsigned int minimum_steps_per_second;
int base_stepping_frequency;
unsigned short step_bits[3];
int counter_increment;
}
if (gcode->has_letter('F'))
{
- this->feed_rate = gcode->get_value('F');
- if (this->feed_rate > (this->max_speed * THEKERNEL->robot->seconds_per_minute))
- this->feed_rate = this->max_speed * THEKERNEL->robot->seconds_per_minute;
- feed_rate /= THEKERNEL->robot->seconds_per_minute;
+ feed_rate = gcode->get_value('F') / THEKERNEL->robot->seconds_per_minute;
+ if (feed_rate > max_speed)
+ feed_rate = max_speed;
}
}else if( gcode->g == 90 ){ this->absolute_mode = true;
}else if( gcode->g == 91 ){ this->absolute_mode = false;
if( current_rate > target_rate ){ current_rate = target_rate; }
// steps per second
- this->stepper_motor->set_speed(max(current_rate, THEKERNEL->stepper->minimum_steps_per_minute/60));
+ this->stepper_motor->set_speed(max(current_rate, THEKERNEL->stepper->minimum_steps_per_second));
return 0;
}
if( this->current_block == NULL || this->paused || this->mode != FOLLOW || this->stepper_motor->moving != true ){ return; }
/*
- * nominal block duration = current block's steps / ( current block's nominal rate / 60 )
+ * nominal block duration = current block's steps / ( current block's nominal rate )
* nominal extruder rate = extruder steps / nominal block duration
- * actual extruder rate = nominal extruder rate * ( ( stepper's steps per minute / 60 ) / ( current block's nominal rate / 60 ) )
- * or actual extruder rate = ( ( extruder steps * ( current block's nominal_rate / 60 ) ) / current block's steps ) * ( ( stepper's steps per minute / 60 ) / ( current block's nominal rate / 60 ) )
- * or simplified : extruder steps * ( stepper's steps per minute / 60 ) ) / current block's steps
- * or even : ( stepper steps per minute / 60 ) * ( extruder steps / current block's steps )
+ * actual extruder rate = nominal extruder rate * ( ( stepper's steps per minute ) / ( current block's nominal rate ) )
+ * or actual extruder rate = ( ( extruder steps * ( current block's nominal_rate ) ) / current block's steps ) * ( ( stepper's steps per second ) / ( current block's nominal rate ) )
+ * or simplified : extruder steps * ( stepper's steps per second ) ) / current block's steps
+ * or even : ( stepper steps per second ) * ( extruder steps / current block's steps )
*/
- this->stepper_motor->set_speed( max( ( THEKERNEL->stepper->trapezoid_adjusted_rate /60.0) * ( (float)this->stepper_motor->steps_to_move / (float)this->current_block->steps_event_count ), THEKERNEL->stepper->minimum_steps_per_minute/60.0 ) );
+ this->stepper_motor->set_speed( max( ( THEKERNEL->stepper->trapezoid_adjusted_rate) * ( (float)this->stepper_motor->steps_to_move / (float)this->current_block->steps_event_count ), THEKERNEL->stepper->minimum_steps_per_second ) );
}
}
}
if( gcode->has_letter('F') ) {
- this->probe_rate = robot->to_millimeters( gcode->get_value('F') ) / 60.0;
+ this->probe_rate = robot->to_millimeters( gcode->get_value('F') ) / robot->seconds_per_minute;
}
robot->arm_solution->cartesian_to_actuator(tmp, mm);
for (int c = 0; c < 3; c++)