void PID_Autotuner::begin(TemperatureControl *temp, double target, StreamOutput *stream)
{
if (t)
- {
- t->target_temperature = 0.0;
- }
+ t->heater_pin->set(0);
t = temp;
- t->p_factor = 1000000000.0;
- t->i_factor = 0.0;
- t->d_factor = 0.0;
- t->i_max = 0.0;
+ t->target_temperature = 0.0;
- t->target_temperature = target;
+ target_temperature = target;
for (cycle = 0; cycle < 8; cycle++)
{
- cycles[cycle].ticks = 0;
- cycles[cycle].t_max = 0.0;
- cycles[cycle].t_min = 1000.0;
+ cycles[cycle].ticks_high = 0;
+ cycles[cycle].ticks_low = 0;
+ cycles[cycle].t_max = 0.0;
+ cycles[cycle].t_min = 1000.0;
}
cycle = 0;
s->printf("%s: Starting PID Autotune\n", t->designator.c_str());
+ bias = d = t->heater_pin->max_pwm() >> 1;
+
+ output = true;
last_output = true;
}
if (t == NULL)
return 0;
- if (last_output == false && (t->o > 128))
+ if (t->last_reading > (target_temperature + 0.25))
+ output = false;
+ else if (t->last_reading < (target_temperature - 0.25))
+ output = true;;
+
+ if (last_output == false && output)
{
- s->printf("Cycle %d:\n\tMax: %5.1f Min: %5.1f time: %3.1fs\n", cycle, cycles[cycle].t_max, cycles[cycle].t_min, cycles[cycle].ticks / 20.0);
+ s->printf("Cycle %d:\n\tMax: %5.1f Min: %5.1f high time: %3.1fs low time: %3.1fs\n", cycle, cycles[cycle].t_max, cycles[cycle].t_min, cycles[cycle].ticks_high / 20.0, cycles[cycle].ticks_low / 20.0);
+
+ // this code taken from http://github.com/ErikZalm/Marlin/blob/Marlin_v1/Marlin/temperature.cpp
+ bias += (d * (cycles[cycle].ticks_high - cycles[cycle].ticks_low) * (1000.0 / 20.0)) / ((cycles[cycle].ticks_high + cycles[cycle].ticks_low) * (1000.0 / 20.0));
+ bias = confine(bias, 20, t->heater_pin->max_pwm() - 20);
+ if (bias > (t->heater_pin->max_pwm() / 2))
+ d = t->heater_pin->max_pwm() - 1 - bias;
+ else
+ d = bias;
+ // end code from Marlin firmware
+
cycle++;
if (cycle == PID_AUTOTUNER_CYCLES)
{
+ t->heater_pin->set(0);
t->set_desired_temperature(0.0);
// TODO: finish
double tmax_avg = 0.0,
tmin_avg = 0.0,
- tcycle_avg = 0.0;
- for (cycle = 1; cycle < PID_AUTOTUNER_CYCLES; cycle++)
+ t_high_avg = 0.0,
+ t_low_avg = 0.0;
+ for (cycle = PID_AUTOTUNER_CYCLES - 3; cycle < PID_AUTOTUNER_CYCLES; cycle++)
{
- tmax_avg += cycles[cycle].t_max;
- tmin_avg += cycles[cycle].t_min;
- tcycle_avg += cycles[cycle].ticks;
+ tmax_avg += cycles[cycle].t_max;
+ tmin_avg += cycles[cycle].t_min;
+ t_high_avg += cycles[cycle].ticks_high;
+ t_low_avg += cycles[cycle].ticks_low;
}
- tmax_avg /= 7.0;
- tmin_avg /= 7.0;
- tcycle_avg /= 7.0;
- s->printf("Averages over last %d cycles: Max: %5.1fc Min: %5.1fc samples: %3.0f\n", PID_AUTOTUNER_CYCLES - 2, tmax_avg, tmin_avg, tcycle_avg);
+ tmax_avg /= (PID_AUTOTUNER_CYCLES - 1.0);
+ tmin_avg /= (PID_AUTOTUNER_CYCLES - 1.0);
+ t_high_avg /= (PID_AUTOTUNER_CYCLES - 1.0);
+ t_low_avg /= (PID_AUTOTUNER_CYCLES - 1.0);
+
+ s->printf("Averages over last %d cycles: Max: %5.1fc Min: %5.1fc high samples: %3.0f low samples: %3.0f\n", 3, tmax_avg, tmin_avg, t_high_avg, t_low_avg);
- // from http://brettbeauregard.com/blog/2012/01/arduino-pid-autotune-library/
- // TODO: work out why the results of this algorithm are dreadfully poor
- double ku = 4 * 128 / ((tmax_avg - tmin_avg) * 3.141592653589);
- double pu = tcycle_avg;
+ // this code taken from http://github.com/ErikZalm/Marlin/blob/Marlin_v1/Marlin/temperature.cpp
+ double ku = (4.0 * d) / (3.141592653589 * (tmax_avg - tmin_avg) / 2.0);
+ double tu = (t_low_avg + t_high_avg) * (1000.0 / 20.0) / 1000.0;
+
+ s->printf("\tku: %g\n\ttu: %g\n", ku, tu);
double kp = 0.6 * ku;
- double ki = 1.2 * ku / pu;
- double kd = 0.075 * ku * pu;
+ double ki = 2 * kp / tu / 20.0;
+ double kd = kp * tu / 8.0;
- s->printf("PID Autotune complete. Try M301 S%d P%4g I%4g D%4g\n", t->pool_index, kp, ki, kd);
+ s->printf("\tTrying:\n\tKp: %5.1f\n\tKi: %5.3f\n\tKd: %5.0f\n", kp, ki, kd);
+ // end code from Marlin Firmware
t->p_factor = kp;
t->i_factor = ki;
t->d_factor = kd;
- t->i_max = 128;
t = NULL;
s = NULL;
return 0;
}
+ s->printf("Cycle %d:\n\tbias: %4d d: %4d\n", cycle, bias, d);
}
- last_output = (t->o > 128);
+ int ticks;
- cycles[cycle].ticks++;
+ if (output)
+ {
+ ticks = ++cycles[cycle].ticks_high;
+ t->heater_pin->pwm((t->o = ((bias + d) >> 1)));
+ }
+ else
+ {
+ ticks = ++cycles[cycle].ticks_low;
+ t->heater_pin->set((t->o = 0));
+ }
- if ((cycles[cycle].ticks % 16) == 0)
+ if ((ticks % 16) == 0)
{
- s->printf("%s: %5.1f/%5.1f @%d %d %d/8\n", t->designator.c_str(), t->last_reading, t->target_temperature, t->o, (last_output?1:0), cycle);
+ s->printf("%s: %5.1f/%5.1f @%d %d %d/8\n", t->designator.c_str(), t->last_reading, target_temperature, t->o, (output?1:0), cycle);
}
if (t->last_reading > cycles[cycle].t_max)
if (t->last_reading < cycles[cycle].t_min)
cycles[cycle].t_min = t->last_reading;
+ last_output = output;
+
return 0;
}
void TemperatureControl::on_module_loaded(){
// We start not desiring any temp
-// this->desired_adc_value = UNDEFINED;
this->target_temperature = UNDEFINED;
// Settings
this->kernel->adc->enable_pin(this->thermistor_pin);
// Heater pin
- this->heater_pin = this->kernel->config->value(temperature_control_checksum, this->name_checksum, heater_pin_checksum)->required()->as_pin()->as_output();
+ this->heater_pin = this->kernel->config->value(temperature_control_checksum, this->name_checksum, heater_pin_checksum)->required()->as_pwm()->as_output();
this->heater_pin->set(0);
// activate SD-DAC timer
- this->kernel->slow_ticker->attach(1000, this->heater_pin, &Pin::tick);
+ this->kernel->slow_ticker->attach(1000, this->heater_pin, &Pwm::on_tick);
// PID
this->p_factor = this->kernel->config->value(temperature_control_checksum, this->name_checksum, p_factor_checksum)->by_default(10 )->as_number();
this->last_reading = 0.0;
}
-//#pragma GCC push_options
-//#pragma GCC optimize ("O0")
-
void TemperatureControl::on_gcode_received(void* argument)
{
Gcode* gcode = static_cast<Gcode*>(argument);
gcode->stream->printf("%s:%3.1f /%3.1f @%d ", this->designator.c_str(), this->get_temperature(), ((target_temperature == UNDEFINED)?0.0:target_temperature), this->o);
gcode->add_nl = true;
}
+ if (gcode->m == 301)
+ {
+ if (gcode->has_letter('S') && (gcode->get_value('S') == this->pool_index))
+ {
+ if (gcode->has_letter('P'))
+ this->p_factor = gcode->get_value('P');
+ if (gcode->has_letter('I'))
+ this->i_factor = gcode->get_value('I');
+ if (gcode->has_letter('D'))
+ this->d_factor = gcode->get_value('D');
+ if (gcode->has_letter('X'))
+ this->i_max = gcode->get_value('X');
+ }
+ gcode->stream->printf("%s(S%d): Pf:%g If:%g Df:%g X(I_max):%g Pv:%g Iv:%g Dv:%g O:%d\n", this->designator.c_str(), this->pool_index, this->p_factor, this->i_factor, this->d_factor, this->i_max, this->p, this->i, this->d, o);
+ }
+ if (gcode->m == 303)
+ {
+ if (gcode->has_letter('S') && (gcode->get_value('S') == this->pool_index))
+ {
+ double target = 150.0;
+ if (gcode->has_letter('P'))
+ {
+ target = gcode->get_value('P');
+ gcode->stream->printf("Target: %5.1f\n", target);
+ }
+ gcode->stream->printf("Start PID tune, command is %s\n", gcode->command.c_str());
+ this->pool->PIDtuner->begin(this, target, gcode->stream);
+ }
+ }
}
}
// Set temperature without waiting
if( gcode->m == this->set_m_code && gcode->has_letter('S') )
{
- //gcode->stream->printf("setting to %f meaning %u \r\n", gcode->get_value('S'), this->temperature_to_adc_value( gcode->get_value('S') ) );
if (gcode->get_value('S') == 0)
{
this->target_temperature = UNDEFINED;
this->waiting = true;
}
}
- if (gcode->m == 301)
- {
- if (gcode->has_letter('S') && (gcode->get_value('S') == this->pool_index))
- {
- if (gcode->has_letter('P'))
- this->p_factor = gcode->get_value('P');
- if (gcode->has_letter('I'))
- this->i_factor = gcode->get_value('I');
- if (gcode->has_letter('D'))
- this->d_factor = gcode->get_value('D');
- if (gcode->has_letter('X'))
- this->i_max = gcode->get_value('X');
- }
- gcode->stream->printf("%s(S%d): Pf:%g If:%g Df:%g X(I_max):%g Pv:%g Iv:%g Dv:%g O:%d\n", this->designator.c_str(), this->pool_index, this->p_factor, this->i_factor, this->d_factor, this->i_max, this->p, this->i, this->d, o);
- }
- if (gcode->m == 303)
- {
- if (gcode->has_letter('S') && (gcode->get_value('S') == this->pool_index))
- {
- double target = 150.0;
- if (gcode->has_letter('T'))
- target = gcode->get_value('T');
- this->pool->PIDtuner->begin(this, target, gcode->stream);
- }
- }
}
}
-//#pragma GCC pop_options
-
void TemperatureControl::set_desired_temperature(double desired_temperature){
-// this->desired_adc_value = this->temperature_to_adc_value(desired_temperature);
target_temperature = desired_temperature;
if (desired_temperature == 0.0)
- heater_pin->set(0);
+ heater_pin->set((o = 0));
}
double TemperatureControl::get_temperature(){
p = error * p_factor;
i += (error * this->i_factor);
- d = (last_reading - temperature) * this->d_factor;
+ // d was imbued with oldest_raw earlier in new_thermistor_reading
+ d = adc_value_to_temperature(d);
+ d = (d - temperature) * this->d_factor;
if (i > this->i_max)
i = this->i_max;
if (i < -this->i_max)
i = -this->i_max;
- this->o = (p + i + d) * PIN_PWM_MAX / 256;
+ this->o = (p + i + d) * heater_pin->max_pwm() / 256;
- if (this->o >= PIN_PWM_MAX)
+ if (this->o >= heater_pin->max_pwm())
{
- i -= (this->o - (PIN_PWM_MAX - 1)) * 256 / PIN_PWM_MAX;
- this->o = PIN_PWM_MAX - 1;
+ i -= (this->o - (heater_pin->max_pwm() - 1)) * 256 / heater_pin->max_pwm();
+ this->o = heater_pin->max_pwm() - 1;
}
if (this->o < 0)
{
- if (this->o < -(PIN_PWM_MAX))
- i += (-(PIN_PWM_MAX) - this->o) * 256 / PIN_PWM_MAX;
+ if (this->o < -(heater_pin->max_pwm()))
+ i += (-(heater_pin->max_pwm()) - this->o) * 256 / heater_pin->max_pwm();
this->o = 0;
}
uint16_t l;
queue.pop_front(l);
running_total -= l;
+ d = l;
}
uint16_t r = last_raw;
queue.push_back(r);