Add PID_FAN_SCALING option (#15585)
This commit is contained in:
		| @@ -197,6 +197,56 @@ | ||||
|     #define DEFAULT_Kc (100) //heating power=Kc*(e_speed) | ||||
|     #define LPQ_MAX_LEN 50 | ||||
|   #endif | ||||
|  | ||||
|   /** | ||||
|    * Add an experimental additional term to the heater power, proportional to the fan speed. | ||||
|    * A well-chosen Kf value should add just enough power to compensate for power-loss from the cooling fan. | ||||
|    * You can either just add a constant compensation with the DEFAULT_Kf value | ||||
|    * or follow the instruction below to get speed-dependent compensation. | ||||
|    * | ||||
|    * Constant compensation (use only with fanspeeds of 0% and 100%) | ||||
|    * --------------------------------------------------------------------- | ||||
|    * A good starting point for the Kf-value comes from the calculation: | ||||
|    *   kf = (power_fan * eff_fan) / power_heater * 255 | ||||
|    * where eff_fan is between 0.0 and 1.0, based on fan-efficiency and airflow to the nozzle / heater. | ||||
|    * | ||||
|    * Example: | ||||
|    *   Heater: 40W, Fan: 0.1A * 24V = 2.4W, eff_fan = 0.8 | ||||
|    *   Kf = (2.4W * 0.8) / 40W * 255 = 12.24 | ||||
|    * | ||||
|    * Fan-speed dependent compensation | ||||
|    * -------------------------------- | ||||
|    * 1. To find a good Kf value, set the hotend temperature, wait for it to settle, and enable the fan (100%). | ||||
|    *    Make sure PID_FAN_SCALING_LIN_FACTOR is 0 and PID_FAN_SCALING_ALTERNATIVE_DEFINITION is not enabled. | ||||
|    *    If you see the temperature drop repeat the test, increasing the Kf value slowly, until the temperature | ||||
|    *    drop goes away. If the temperature overshoots after enabling the fan, the Kf value is too big. | ||||
|    * 2. Note the Kf-value for fan-speed at 100% | ||||
|    * 3. Determine a good value for PID_FAN_SCALING_MIN_SPEED, which is around the speed, where the fan starts moving. | ||||
|    * 4. Repeat step 1. and 2. for this fan speed. | ||||
|    * 5. Enable PID_FAN_SCALING_ALTERNATIVE_DEFINITION and enter the two identified Kf-values in | ||||
|    *    PID_FAN_SCALING_AT_FULL_SPEED and PID_FAN_SCALING_AT_MIN_SPEED. Enter the minimum speed in PID_FAN_SCALING_MIN_SPEED | ||||
|    */ | ||||
|   //#define PID_FAN_SCALING | ||||
|   #if ENABLED(PID_FAN_SCALING) | ||||
|     //#define PID_FAN_SCALING_ALTERNATIVE_DEFINITION | ||||
|     #if ENABLED(PID_FAN_SCALING_ALTERNATIVE_DEFINITION) | ||||
|       // The alternative definition is used for an easier configuration. | ||||
|       // Just figure out Kf at fullspeed (255) and PID_FAN_SCALING_MIN_SPEED. | ||||
|       // DEFAULT_Kf and PID_FAN_SCALING_LIN_FACTOR are calculated accordingly. | ||||
|  | ||||
|       #define PID_FAN_SCALING_AT_FULL_SPEED 13.0        //=PID_FAN_SCALING_LIN_FACTOR*255+DEFAULT_Kf | ||||
|       #define PID_FAN_SCALING_AT_MIN_SPEED 6.0          //=PID_FAN_SCALING_LIN_FACTOR*PID_FAN_SCALING_MIN_SPEED+DEFAULT_Kf | ||||
|       #define PID_FAN_SCALING_MIN_SPEED 10.0            // Minimum fan speed at which to enable PID_FAN_SCALING | ||||
|  | ||||
|       #define DEFAULT_Kf (255.0*PID_FAN_SCALING_AT_MIN_SPEED-PID_FAN_SCALING_AT_FULL_SPEED*PID_FAN_SCALING_MIN_SPEED)/(255.0-PID_FAN_SCALING_MIN_SPEED) | ||||
|       #define PID_FAN_SCALING_LIN_FACTOR (PID_FAN_SCALING_AT_FULL_SPEED-DEFAULT_Kf)/255.0 | ||||
|  | ||||
|     #else | ||||
|       #define PID_FAN_SCALING_LIN_FACTOR (0)             // Power loss due to cooling = Kf * (fan_speed) | ||||
|       #define DEFAULT_Kf 10                              // A constant value added to the PID-tuner | ||||
|       #define PID_FAN_SCALING_MIN_SPEED 10               // Minimum fan speed at which to enable PID_FAN_SCALING | ||||
|     #endif | ||||
|   #endif | ||||
| #endif | ||||
|  | ||||
| /** | ||||
|   | ||||
| @@ -38,6 +38,10 @@ | ||||
|  * | ||||
|  *   C[float] Kc term | ||||
|  *   L[int] LPQ length | ||||
|  * | ||||
|  * With PID_FAN_SCALING: | ||||
|  * | ||||
|  *   F[float] Kf term | ||||
|  */ | ||||
| void GcodeSuite::M301() { | ||||
|  | ||||
| @@ -56,6 +60,10 @@ void GcodeSuite::M301() { | ||||
|       NOLESS(thermalManager.lpq_len, 0); | ||||
|     #endif | ||||
|  | ||||
|     #if ENABLED(PID_FAN_SCALING) | ||||
|       if (parser.seen('F')) PID_PARAM(Kf, e) = parser.value_float(); | ||||
|     #endif | ||||
|  | ||||
|     thermalManager.updatePID(); | ||||
|     SERIAL_ECHO_START(); | ||||
|     #if ENABLED(PID_PARAMS_PER_HOTEND) | ||||
| @@ -65,9 +73,12 @@ void GcodeSuite::M301() { | ||||
|                     " i:", unscalePID_i(PID_PARAM(Ki, e)), | ||||
|                     " d:", unscalePID_d(PID_PARAM(Kd, e))); | ||||
|     #if ENABLED(PID_EXTRUSION_SCALING) | ||||
|       //Kc does not have scaling applied above, or in resetting defaults | ||||
|       SERIAL_ECHOPAIR(" c:", PID_PARAM(Kc, e)); | ||||
|     #endif | ||||
|     #if ENABLED(PID_FAN_SCALING) | ||||
|       SERIAL_ECHOPAIR(" f:", PID_PARAM(Kf, e)); | ||||
|     #endif | ||||
|  | ||||
|     SERIAL_EOL(); | ||||
|   } | ||||
|   else | ||||
|   | ||||
| @@ -229,6 +229,8 @@ | ||||
|   #error "LCD_PIN_RESET is now LCD_RESET_PIN. Please update your pins definitions." | ||||
| #elif defined(EXTRUDER_0_AUTO_FAN_PIN) || defined(EXTRUDER_1_AUTO_FAN_PIN) || defined(EXTRUDER_2_AUTO_FAN_PIN) || defined(EXTRUDER_3_AUTO_FAN_PIN) | ||||
|   #error "EXTRUDER_[0123]_AUTO_FAN_PIN is now E[0123]_AUTO_FAN_PIN. Please update your Configuration_adv.h." | ||||
| #elif defined(PID_FAN_SCALING) && FAN_COUNT <= 0 | ||||
|   #error "PID_FAN_SCALING needs at least one fan enabled." | ||||
| #elif defined(min_software_endstops) || defined(max_software_endstops) | ||||
|   #error "(min|max)_software_endstops are now (MIN|MAX)_SOFTWARE_ENDSTOPS. Please update your configuration." | ||||
| #elif ENABLED(Z_PROBE_SLED) && defined(SLED_PIN) | ||||
|   | ||||
| @@ -289,7 +289,7 @@ void menu_cancelobject(); | ||||
|     // | ||||
|  | ||||
|     #if ENABLED(PID_EDIT_MENU) | ||||
|       #define _PID_BASE_MENU_ITEMS(N) \ | ||||
|       #define __PID_BASE_MENU_ITEMS(N) \ | ||||
|         raw_Ki = unscalePID_i(PID_PARAM(Ki, N)); \ | ||||
|         raw_Kd = unscalePID_d(PID_PARAM(Kd, N)); \ | ||||
|         EDIT_ITEM_N(float52sign, N, MSG_PID_P_E, &PID_PARAM(Kp, N), 1, 9990); \ | ||||
| @@ -297,9 +297,17 @@ void menu_cancelobject(); | ||||
|         EDIT_ITEM_N(float52sign, N, MSG_PID_D_E, &raw_Kd, 1, 9990, []{ copy_and_scalePID_d(N); }) | ||||
|  | ||||
|       #if ENABLED(PID_EXTRUSION_SCALING) | ||||
|         #define _PID_BASE_MENU_ITEMS(N) \ | ||||
|           __PID_BASE_MENU_ITEMS(N); \ | ||||
|           EDIT_ITEM_N(float3, N, MSG_PID_C_E, &PID_PARAM(Kc, N), 1, 9990) | ||||
|       #else | ||||
|         #define _PID_BASE_MENU_ITEMS(N) __PID_BASE_MENU_ITEMS(N) | ||||
|       #endif | ||||
|  | ||||
|       #if ENABLED(PID_FAN_SCALING) | ||||
|         #define _PID_EDIT_MENU_ITEMS(N) \ | ||||
|           _PID_BASE_MENU_ITEMS(N); \ | ||||
|           EDIT_ITEM_N(float3, N, MSG_PID_C_E, &PID_PARAM(Kc, N), 1, 9990) | ||||
|           EDIT_ITEM(float3, PID_LABEL(MSG_PID_F,N), &PID_PARAM(Kf, N), 1, 9990) | ||||
|       #else | ||||
|         #define _PID_EDIT_MENU_ITEMS(N) _PID_BASE_MENU_ITEMS(N) | ||||
|       #endif | ||||
|   | ||||
| @@ -37,7 +37,7 @@ | ||||
|  */ | ||||
|  | ||||
| // Change EEPROM version if the structure changes | ||||
| #define EEPROM_VERSION "V72" | ||||
| #define EEPROM_VERSION "V73" | ||||
| #define EEPROM_OFFSET 100 | ||||
|  | ||||
| // Check the integrity of data offsets. | ||||
| @@ -242,7 +242,7 @@ typedef struct SettingsDataStruct { | ||||
|   // | ||||
|   // PIDTEMP | ||||
|   // | ||||
|   PIDC_t hotendPID[HOTENDS];                            // M301 En PIDC / M303 En U | ||||
|   PIDCF_t hotendPID[HOTENDS];                           // M301 En PIDCF / M303 En U | ||||
|   int16_t lpq_len;                                      // M301 L | ||||
|  | ||||
|   // | ||||
| @@ -785,13 +785,14 @@ void MarlinSettings::postprocess() { | ||||
|     { | ||||
|       _FIELD_TEST(hotendPID); | ||||
|       HOTEND_LOOP() { | ||||
|         PIDC_t pidc = { | ||||
|         PIDCF_t pidcf = { | ||||
|                        PID_PARAM(Kp, e), | ||||
|           unscalePID_i(PID_PARAM(Ki, e)), | ||||
|           unscalePID_d(PID_PARAM(Kd, e)), | ||||
|                        PID_PARAM(Kc, e) | ||||
|                        PID_PARAM(Kc, e), | ||||
|                        PID_PARAM(Kf, e) | ||||
|         }; | ||||
|         EEPROM_WRITE(pidc); | ||||
|         EEPROM_WRITE(pidcf); | ||||
|       } | ||||
|  | ||||
|       _FIELD_TEST(lpq_len); | ||||
| @@ -1586,16 +1587,19 @@ void MarlinSettings::postprocess() { | ||||
|       // | ||||
|       { | ||||
|         HOTEND_LOOP() { | ||||
|           PIDC_t pidc; | ||||
|           EEPROM_READ(pidc); | ||||
|           PIDCF_t pidcf; | ||||
|           EEPROM_READ(pidcf); | ||||
|           #if ENABLED(PIDTEMP) | ||||
|             if (!validating && pidc.Kp != DUMMY_PID_VALUE) { | ||||
|             if (!validating && pidcf.Kp != DUMMY_PID_VALUE) { | ||||
|               // Scale PID values since EEPROM values are unscaled | ||||
|               PID_PARAM(Kp, e) = pidc.Kp; | ||||
|               PID_PARAM(Ki, e) = scalePID_i(pidc.Ki); | ||||
|               PID_PARAM(Kd, e) = scalePID_d(pidc.Kd); | ||||
|               PID_PARAM(Kp, e) = pidcf.Kp; | ||||
|               PID_PARAM(Ki, e) = scalePID_i(pidcf.Ki); | ||||
|               PID_PARAM(Kd, e) = scalePID_d(pidcf.Kd); | ||||
|               #if ENABLED(PID_EXTRUSION_SCALING) | ||||
|                 PID_PARAM(Kc, e) = pidc.Kc; | ||||
|                 PID_PARAM(Kc, e) = pidcf.Kc; | ||||
|               #endif | ||||
|               #if ENABLED(PID_FAN_SCALING) | ||||
|                 PID_PARAM(Kf, e) = pidcf.Kf; | ||||
|               #endif | ||||
|             } | ||||
|           #endif | ||||
| @@ -2446,6 +2450,10 @@ void MarlinSettings::reset() { | ||||
|       #if ENABLED(PID_EXTRUSION_SCALING) | ||||
|         PID_PARAM(Kc, e) = DEFAULT_Kc; | ||||
|       #endif | ||||
|  | ||||
|       #if ENABLED(PID_FAN_SCALING) | ||||
|         PID_PARAM(Kf, e) = DEFAULT_Kf; | ||||
|       #endif | ||||
|     } | ||||
|   #endif | ||||
|  | ||||
| @@ -3003,6 +3011,9 @@ void MarlinSettings::reset() { | ||||
|             SERIAL_ECHOPAIR(" C", PID_PARAM(Kc, e)); | ||||
|             if (e == 0) SERIAL_ECHOPAIR(" L", thermalManager.lpq_len); | ||||
|           #endif | ||||
|           #if ENABLED(PID_FAN_SCALING) | ||||
|             SERIAL_ECHOPAIR(" F", PID_PARAM(Kf, e)); | ||||
|           #endif | ||||
|           SERIAL_EOL(); | ||||
|         } | ||||
|       #endif // PIDTEMP | ||||
|   | ||||
| @@ -872,6 +872,15 @@ void Temperature::min_temp_error(const heater_ind_t heater) { | ||||
|             } | ||||
|           #endif // PID_EXTRUSION_SCALING | ||||
|  | ||||
|           #if ENABLED(PID_FAN_SCALING) | ||||
|             if (thermalManager.fan_speed[active_extruder] > PID_FAN_SCALING_MIN_SPEED) { | ||||
|               work_pid[ee].Kf = PID_PARAM(Kf, ee) + (PID_FAN_SCALING_LIN_FACTOR) * thermalManager.fan_speed[active_extruder]; | ||||
|               pid_output += work_pid[ee].Kf; | ||||
|             } | ||||
|             //pid_output -= work_pid[ee].Ki; | ||||
|             //pid_output += work_pid[ee].Ki * work_pid[ee].Kf | ||||
|           #endif // PID_FAN_SCALING | ||||
|  | ||||
|           LIMIT(pid_output, 0, PID_MAX); | ||||
|         } | ||||
|         temp_dState[ee] = temp_hotend[ee].celsius; | ||||
|   | ||||
| @@ -55,15 +55,23 @@ typedef enum : int8_t { | ||||
| // PID storage | ||||
| typedef struct { float Kp, Ki, Kd;     } PID_t; | ||||
| typedef struct { float Kp, Ki, Kd, Kc; } PIDC_t; | ||||
| #if ENABLED(PID_EXTRUSION_SCALING) | ||||
|   typedef PIDC_t hotend_pid_t; | ||||
|   #if LPQ_MAX_LEN > 255 | ||||
|     typedef uint16_t lpq_ptr_t; | ||||
| typedef struct { float Kp, Ki, Kd, Kf; } PIDF_t; | ||||
| typedef struct { float Kp, Ki, Kd, Kc, Kf; } PIDCF_t; | ||||
|  | ||||
| typedef | ||||
|   #if BOTH(PID_EXTRUSION_SCALING, PID_FAN_SCALING) | ||||
|     PIDCF_t | ||||
|   #elif ENABLED(PID_EXTRUSION_SCALING) | ||||
|     PIDC_t | ||||
|   #elif ENABLED(PID_FAN_SCALING) | ||||
|     PIDF_t | ||||
|   #else | ||||
|     typedef uint8_t lpq_ptr_t; | ||||
|     PID_t | ||||
|   #endif | ||||
| #else | ||||
|   typedef PID_t hotend_pid_t; | ||||
| hotend_pid_t; | ||||
|  | ||||
| #if ENABLED(PID_EXTRUSION_SCALING) | ||||
|   typedef IF<(LPQ_MAX_LEN > 255), uint16_t, uint8_t>::type lpq_ptr_t; | ||||
| #endif | ||||
|  | ||||
| #define DUMMY_PID_VALUE 3000.0f | ||||
| @@ -77,6 +85,12 @@ typedef struct { float Kp, Ki, Kd, Kc; } PIDC_t; | ||||
|   #else | ||||
|     #define _PID_Kc(H) 1 | ||||
|   #endif | ||||
|  | ||||
|   #if ENABLED(PID_FAN_SCALING) | ||||
|     #define _PID_Kf(H) Temperature::temp_hotend[H].pid.Kf | ||||
|   #else | ||||
|     #define _PID_Kf(H) 0 | ||||
|   #endif | ||||
| #else | ||||
|   #define _PID_Kp(H) DUMMY_PID_VALUE | ||||
|   #define _PID_Ki(H) DUMMY_PID_VALUE | ||||
|   | ||||
		Reference in New Issue
	
	Block a user