|
|
@ -469,12 +469,23 @@ temp_range_t Temperature::temp_range[HOTENDS] = ARRAY_BY_HOTENDS(sensor_heater_0 |
|
|
|
Tu = float(t_low + t_high) * 0.001f, |
|
|
|
pf = isbed ? 0.2f : 0.6f, |
|
|
|
df = isbed ? 1.0f / 3.0f : 1.0f / 8.0f; |
|
|
|
tune_pid.Kp = Ku * pf; |
|
|
|
tune_pid.Kd = tune_pid.Kp * Tu * df; |
|
|
|
tune_pid.Ki = 2 * tune_pid.Kp / Tu; |
|
|
|
|
|
|
|
SERIAL_ECHOPAIR(MSG_KU, Ku, MSG_TU, Tu); |
|
|
|
SERIAL_ECHOLNPGM("\n" MSG_CLASSIC_PID); |
|
|
|
SERIAL_ECHOLNPAIR(MSG_KP, tune_pid.Kp, MSG_KI, tune_pid.Ki, MSG_KD, tune_pid.Kd); |
|
|
|
if (isbed) { // Do not remove this otherwise PID autotune won't work right for the bed!
|
|
|
|
tune_pid.Kp = Ku * 0.2f; |
|
|
|
tune_pid.Ki = 2 * tune_pid.Kp / Tu; |
|
|
|
tune_pid.Kd = tune_pid.Kp * Tu / 3; |
|
|
|
SERIAL_ECHOLNPGM("\n" " No overshoot"); // Works far better for the bed. Classic and some have bad ringing.
|
|
|
|
SERIAL_ECHOLNPAIR(MSG_KP, tune_pid.Kp, MSG_KI, tune_pid.Ki, MSG_KD, tune_pid.Kd); |
|
|
|
} |
|
|
|
else { |
|
|
|
tune_pid.Kp = Ku * pf; |
|
|
|
tune_pid.Kd = tune_pid.Kp * Tu * df; |
|
|
|
tune_pid.Ki = 2 * tune_pid.Kp / Tu; |
|
|
|
SERIAL_ECHOLNPGM("\n" MSG_CLASSIC_PID); |
|
|
|
SERIAL_ECHOLNPAIR(MSG_KP, tune_pid.Kp, MSG_KI, tune_pid.Ki, MSG_KD, tune_pid.Kd); |
|
|
|
} |
|
|
|
|
|
|
|
/**
|
|
|
|
tune_pid.Kp = 0.33 * Ku; |
|
|
|
tune_pid.Ki = tune_pid.Kp / Tu; |
|
|
@ -850,12 +861,12 @@ float Temperature::get_pid_output_hotend(const uint8_t e) { |
|
|
|
} |
|
|
|
|
|
|
|
work_pid[ee].Kd = work_pid[ee].Kd + PID_K2 * (PID_PARAM(Kd, ee) * (temp_dState[ee] - temp_hotend[ee].current) - work_pid[ee].Kd); |
|
|
|
const float max_power_over_i_gain = (float)PID_MAX / PID_PARAM(Ki, ee); |
|
|
|
const float max_power_over_i_gain = float(PID_MAX) / PID_PARAM(Ki, ee) - float(MIN_POWER); |
|
|
|
temp_iState[ee] = constrain(temp_iState[ee] + pid_error, 0, max_power_over_i_gain); |
|
|
|
work_pid[ee].Kp = PID_PARAM(Kp, ee) * pid_error; |
|
|
|
work_pid[ee].Ki = PID_PARAM(Ki, ee) * temp_iState[ee]; |
|
|
|
|
|
|
|
pid_output = work_pid[ee].Kp + work_pid[ee].Ki + work_pid[ee].Kd; |
|
|
|
pid_output = work_pid[ee].Kp + work_pid[ee].Ki + work_pid[ee].Kd + float(MIN_POWER); |
|
|
|
|
|
|
|
#if ENABLED(PID_EXTRUSION_SCALING) |
|
|
|
work_pid[ee].Kc = 0; |
|
|
@ -885,23 +896,25 @@ float Temperature::get_pid_output_hotend(const uint8_t e) { |
|
|
|
#endif // PID_OPENLOOP
|
|
|
|
|
|
|
|
#if ENABLED(PID_DEBUG) |
|
|
|
SERIAL_ECHO_START(); |
|
|
|
SERIAL_ECHOPAIR( |
|
|
|
MSG_PID_DEBUG, ee, |
|
|
|
MSG_PID_DEBUG_INPUT, temp_hotend[ee].current, |
|
|
|
MSG_PID_DEBUG_OUTPUT, pid_output |
|
|
|
); |
|
|
|
#if DISABLED(PID_OPENLOOP) |
|
|
|
if (e == active_extruder) { |
|
|
|
SERIAL_ECHO_START(); |
|
|
|
SERIAL_ECHOPAIR( |
|
|
|
MSG_PID_DEBUG_PTERM, work_pid[ee].Kp, |
|
|
|
MSG_PID_DEBUG_ITERM, work_pid[ee].Ki, |
|
|
|
MSG_PID_DEBUG_DTERM, work_pid[ee].Kd |
|
|
|
#if ENABLED(PID_EXTRUSION_SCALING) |
|
|
|
, MSG_PID_DEBUG_CTERM, work_pid[ee].Kc |
|
|
|
#endif |
|
|
|
MSG_PID_DEBUG, ee, |
|
|
|
MSG_PID_DEBUG_INPUT, temp_hotend[ee].current, |
|
|
|
MSG_PID_DEBUG_OUTPUT, pid_output |
|
|
|
); |
|
|
|
#endif |
|
|
|
SERIAL_EOL(); |
|
|
|
#if DISABLED(PID_OPENLOOP) |
|
|
|
SERIAL_ECHOPAIR( |
|
|
|
MSG_PID_DEBUG_PTERM, work_pid[ee].Kp, |
|
|
|
MSG_PID_DEBUG_ITERM, work_pid[ee].Ki, |
|
|
|
MSG_PID_DEBUG_DTERM, work_pid[ee].Kd |
|
|
|
#if ENABLED(PID_EXTRUSION_SCALING) |
|
|
|
, MSG_PID_DEBUG_CTERM, work_pid[ee].Kc |
|
|
|
#endif |
|
|
|
); |
|
|
|
#endif |
|
|
|
SERIAL_EOL(); |
|
|
|
} |
|
|
|
#endif // PID_DEBUG
|
|
|
|
|
|
|
|
#else // No PID enabled
|
|
|
@ -927,19 +940,36 @@ float Temperature::get_pid_output_hotend(const uint8_t e) { |
|
|
|
|
|
|
|
static PID_t work_pid = { 0 }; |
|
|
|
static float temp_iState = 0, temp_dState = 0; |
|
|
|
|
|
|
|
const float max_power_over_i_gain = (float)MAX_BED_POWER / temp_bed.pid.Ki, |
|
|
|
static bool pid_reset = true; |
|
|
|
float pid_output = 0; |
|
|
|
const float max_power_over_i_gain = float(MAX_BED_POWER) / temp_bed.pid.Ki - float(MIN_BED_POWER), |
|
|
|
pid_error = temp_bed.target - temp_bed.current; |
|
|
|
|
|
|
|
temp_iState = constrain(temp_iState + pid_error, 0, max_power_over_i_gain); |
|
|
|
if (!temp_bed.target || pid_error < -(PID_FUNCTIONAL_RANGE)) { |
|
|
|
pid_output = 0; |
|
|
|
pid_reset = true; |
|
|
|
} |
|
|
|
else if (pid_error > PID_FUNCTIONAL_RANGE) { |
|
|
|
pid_output = BANG_MAX; |
|
|
|
pid_reset = true; |
|
|
|
} |
|
|
|
else { |
|
|
|
if (pid_reset) { |
|
|
|
temp_iState = 0.0; |
|
|
|
work_pid.Kd = 0.0; |
|
|
|
pid_reset = false; |
|
|
|
} |
|
|
|
|
|
|
|
work_pid.Kp = temp_bed.pid.Kp * pid_error; |
|
|
|
work_pid.Ki = temp_bed.pid.Ki * temp_iState; |
|
|
|
work_pid.Kd = work_pid.Kd + PID_K2 * (temp_bed.pid.Kd * (temp_dState - temp_bed.current) - work_pid.Kd); |
|
|
|
temp_iState = constrain(temp_iState + pid_error, 0, max_power_over_i_gain); |
|
|
|
|
|
|
|
temp_dState = temp_bed.current; |
|
|
|
work_pid.Kp = temp_bed.pid.Kp * pid_error; |
|
|
|
work_pid.Ki = temp_bed.pid.Ki * temp_iState; |
|
|
|
work_pid.Kd = work_pid.Kd + PID_K2 * (temp_bed.pid.Kd * (temp_dState - temp_bed.current) - work_pid.Kd); |
|
|
|
|
|
|
|
const float pid_output = constrain(work_pid.Kp + work_pid.Ki + work_pid.Kd, 0, MAX_BED_POWER); |
|
|
|
temp_dState = temp_bed.current; |
|
|
|
|
|
|
|
pid_output = constrain(work_pid.Kp + work_pid.Ki + work_pid.Kd + float(MIN_BED_POWER), 0, MAX_BED_POWER); |
|
|
|
} |
|
|
|
|
|
|
|
#else // PID_OPENLOOP
|
|
|
|
|
|
|
|