diff options
author | David Crocker <dcrocker@eschertech.com> | 2017-07-18 20:25:12 +0300 |
---|---|---|
committer | David Crocker <dcrocker@eschertech.com> | 2017-07-18 20:25:12 +0300 |
commit | 4e31deb2a5a5e0f517ffe55ec1671bafd621a53e (patch) | |
tree | de60248c717efdff8904587041461de73fd11156 /src/Heating | |
parent | 6f81b7594ba0a9795dc19b3e3784a21aba544de4 (diff) |
Release 1.19beta10
New features:
Refactored and completed 4-leadscrew bed levelling code
Probe deployment and retraction for G30 and G29 commands is now handled
automatically. You should still include a M401 command before the first
G30 command in bed.g and a M402 command after the last one, so that the
probe deploys and retracts once for the entire sequence instead of once
per G30 command.
M577 now allows separate X and Y spacings, use Sxxx:yyy
Volumetric extrusion is now supported (M200)
Additional tool/heater data is provided to DWC (thanks chrishamm). Using
Heater 0 as a tool heater should now work.
The '(' character in a gcode file now introduces a comment, just as the
';' character does. The comment is terminated at end-of-line. This is
not the same as in some CNC gcodes, where a comment introduced by '(' is
terminated by ')'.
Heater tuning peak detection algorithm changed (but still needs more
work). This may fix some "no peak detected" reports during auto tuning.
The heater dead time is how taken as 60% of the peak delay time intead
of 100%, which results in more aggressive PID parameters.
Bug fixes:
M669 with no parameters now reports the bed offset on a SCARA machine as
well as the other parameters
M671 with no parameters now reports the maximum correction as well as
the leadscrew positions
Heater model max PWM is now set to tuning PWM after auto tuning (thanks
cangelis)
If an HTML file uploaded over USB contained an embedded leading
substring of the EOF string, incorrect data was written to file (thanks
cangelis)
Fixed incorrect movement following a tool change on an IDEX machine
(thanks lars)
RADDS build would not start up
Other changes:
TEMPERATURE_CLOSE_ENOUGH reduced from 2.5C to 1.0C
Reduced the maximum number of random probe points on Duet WiFi/Ethernet
to 32 to avoid running out of memory during delta auto calibration
Simplified the axis orthogonality correction code
Added new bitmap types along with function templates to work on them
Clear HSMCI callback when exiting RepRap module e.g. to flash new
firmware
Use lrintf() instead of round()
DriveMovement structures are now allocated dynamically from a freelist,
to allow more moves to be queued in typical cases. The number free and
minimum ever free is included in the M122 report.
Diffstat (limited to 'src/Heating')
-rw-r--r-- | src/Heating/Heat.cpp | 27 | ||||
-rw-r--r-- | src/Heating/Heat.h | 20 | ||||
-rw-r--r-- | src/Heating/Pid.cpp | 83 |
3 files changed, 84 insertions, 46 deletions
diff --git a/src/Heating/Heat.cpp b/src/Heating/Heat.cpp index bfb982a6..89a1fc8c 100644 --- a/src/Heating/Heat.cpp +++ b/src/Heating/Heat.cpp @@ -51,10 +51,28 @@ void Heat::ResetHeaterModels() } } +void Heat::SetBedHeater(int8_t heater) +{ + if (bedHeater >= 0) + { + pids[bedHeater]->SwitchOff(); + } + bedHeater = heater; +} + +void Heat::SetChamberHeater(int8_t heater) +{ + if (chamberHeater >= 0) + { + pids[chamberHeater]->SwitchOff(); + } + chamberHeater = heater; +} + void Heat::Init() { // Set up the real heaters and the corresponding PIDs - for (size_t heater : ARRAY_INDICES(pids)) + for (size_t heater = 0; heater < Heaters; ++heater) { heaterSensors[heater] = nullptr; // no temperature sensor assigned yet if ((int)heater == DefaultBedHeater || (int)heater == DefaultChamberHeater) @@ -72,6 +90,7 @@ void Heat::Init() { pids[heater]->Init(DefaultHotEndHeaterGain, DefaultHotEndHeaterTimeConstant, DefaultHotEndHeaterDeadTime, DefaultExtruderTemperatureLimit, true); } + lastStandbyTools[heater] = nullptr; } // Set up the virtual heaters @@ -82,7 +101,7 @@ void Heat::Init() } // Set up default virtual heaters for MCU temperature and TMC driver overheat sensors -#ifndef __RADDS +#ifndef __RADDS__ virtualHeaterSensors[0] = TemperatureSensor::Create(CpuTemperatureSenseChannel); virtualHeaterSensors[0]->SetHeaterName("MCU"); // name this virtual heater so that it appears in DWC #endif @@ -253,6 +272,7 @@ void Heat::SwitchOff(int8_t heater) if (heater >= 0 && heater < (int)Heaters) { pids[heater]->SwitchOff(); + lastStandbyTools[heater] = nullptr; } } @@ -264,11 +284,12 @@ void Heat::SwitchOffAll() } } -void Heat::Standby(int8_t heater) +void Heat::Standby(int8_t heater, const Tool *tool) { if (heater >= 0 && heater < (int)Heaters) { pids[heater]->Standby(); + lastStandbyTools[heater] = tool; } } diff --git a/src/Heating/Heat.h b/src/Heating/Heat.h index a793f25f..e231c928 100644 --- a/src/Heating/Heat.h +++ b/src/Heating/Heat.h @@ -66,7 +66,7 @@ public: void SetTemperatureLimit(int8_t heater, float t); float GetTemperatureLimit(int8_t heater) const; void Activate(int8_t heater); // Turn on a heater - void Standby(int8_t heater); // Set a heater idle + void Standby(int8_t heater, const Tool* tool); // Set a heater to standby float GetTemperature(int8_t heater) const; // Get the temperature of a heater float GetTargetTemperature(int8_t heater) const; // Get the target temperature HeaterStatus GetStatus(int8_t heater) const; // Get the off/standby/active status @@ -122,6 +122,12 @@ public: float GetTemperature(size_t heater, TemperatureError& err); // Result is in degrees Celsius + const Tool* GetLastStandbyTool(int heater) const + pre(heater >= 0; heater < Heaters) + { + return lastStandbyTools[heater]; + } + #ifdef DUET_NG void SuspendHeaters(bool sus); // Suspend the heaters to conserve power bool WriteBedAndChamberTempSettings(FileStore *f) const; // Save some resume information @@ -135,6 +141,8 @@ private: Platform& platform; // The instance of the RepRap hardware class PID* pids[Heaters]; // A PID controller for each heater + const Tool* lastStandbyTools[Heaters]; // The last tool that caused the corresponding heater to be set to standby + TemperatureSensor *heaterSensors[Heaters]; // The sensor used by the real heaters TemperatureSensor *virtualHeaterSensors[MaxVirtualHeaters]; // Sensors for virtual heaters @@ -166,21 +174,11 @@ inline int8_t Heat::GetBedHeater() const return bedHeater; } -inline void Heat::SetBedHeater(int8_t heater) -{ - bedHeater = heater; -} - inline int8_t Heat::GetChamberHeater() const { return chamberHeater; } -inline void Heat::SetChamberHeater(int8_t heater) -{ - chamberHeater = heater; -} - // Get the process model for the specified heater inline const FopDt& Heat::GetHeaterModel(size_t heater) const { diff --git a/src/Heating/Pid.cpp b/src/Heating/Pid.cpp index c8494f4e..4f93cb7e 100644 --- a/src/Heating/Pid.cpp +++ b/src/Heating/Pid.cpp @@ -127,31 +127,34 @@ TemperatureError PID::ReadTemperature() // This must be called whenever the heater is turned on, and any time the heater is active and the target temperature is changed void PID::SwitchOn() { - if (mode == HeaterMode::fault) - { - if (reprap.Debug(Module::moduleHeat)) - { - platform.MessageF(GENERIC_MESSAGE, "Heater %d not switched on due to temperature fault\n", heater); - } - } - else if (model.IsEnabled()) + if (model.IsEnabled()) { -//debugPrintf("Heater %d on temp %.1f\n", heater, temperature); - const float target = (active) ? activeTemperature : standbyTemperature; - const HeaterMode oldMode = mode; - mode = (temperature + TEMPERATURE_CLOSE_ENOUGH < target) ? HeaterMode::heating - : (temperature > target + TEMPERATURE_CLOSE_ENOUGH) ? HeaterMode::cooling - : HeaterMode::stable; - if (mode != oldMode) + if (mode == HeaterMode::fault) { - heatingFaultCount = 0; - if (mode == HeaterMode::heating) + if (reprap.Debug(Module::moduleHeat)) { - timeSetHeating = millis(); + platform.MessageF(GENERIC_MESSAGE, "Heater %d not switched on due to temperature fault\n", heater); } - if (reprap.Debug(Module::moduleHeat) && oldMode == HeaterMode::off) + } + else if (model.IsEnabled()) + { + //debugPrintf("Heater %d on, temp %.1f\n", heater, temperature); + const float target = (active) ? activeTemperature : standbyTemperature; + const HeaterMode oldMode = mode; + mode = (temperature + TEMPERATURE_CLOSE_ENOUGH < target) ? HeaterMode::heating + : (temperature > target + TEMPERATURE_CLOSE_ENOUGH) ? HeaterMode::cooling + : HeaterMode::stable; + if (mode != oldMode) { - platform.MessageF(GENERIC_MESSAGE, "Heater %d switched on\n", heater); + heatingFaultCount = 0; + if (mode == HeaterMode::heating) + { + timeSetHeating = millis(); + } + if (reprap.Debug(Module::moduleHeat) && oldMode == HeaterMode::off) + { + platform.MessageF(GENERIC_MESSAGE, "Heater %d switched on\n", heater); + } } } } @@ -664,7 +667,7 @@ void PID::DoTuningStep() const int peakIndex = GetPeakTempIndex(); if (peakIndex < 0) { - if (millis() - tuningPhaseStartTime < 120 * 1000) // allow 2 minutes for the bed temperature to start falling + if (millis() - tuningPhaseStartTime < 60 * 1000) // allow 1 minute for the bed temperature reach peal temperature { return; // still waiting for peak temperature } @@ -739,7 +742,8 @@ void PID::DoTuningStep() } // Calculate which reading gave us the peak temperature. -// Return -1 if peak not identified yet, 0 if we failed to find a peak, else the index of the peak (which can't be zero because we always average 3 readings) +// Return -1 if peak not identified yet, 0 if we are never going to find a peak, else the index of the peak +// If the readings show a continuous decrease then we return 1, because zero dead time would lead to infinities /*static*/ int PID::GetPeakTempIndex() { // Check we have enough readings to look for the peak @@ -758,20 +762,25 @@ void PID::DoTuningStep() peakIndex = IdentifyPeak(5); if (peakIndex < 0) { - return 0; // more than one peak + peakIndex = IdentifyPeak(7); + if (peakIndex < 0) + { + return 0; // more than one peak + } } } } // If we have found one peak and it's not too near the end of the readings, return it - return ((size_t)peakIndex + 6 < tuningReadingsTaken) ? peakIndex : -1; + return ((size_t)peakIndex + 3 < tuningReadingsTaken) ? max<int>(peakIndex, 1) : -1; } // See if there is exactly one peak in the readings. -// Return -1 if more than one peak, else the index of the peak. The so-called peak may be right at the end , in which case it isn't really a peak. +// Return -1 if more than one peak, else the index of the peak. The so-called peak may be right at the end, in which case it isn't really a peak. +// With a well-insulated bed heater the temperature may not start dropping appreciably within the 120 second time limit allowed. /*static*/ int PID::IdentifyPeak(size_t numToAverage) { - int firstPeakIndex = -1; + int firstPeakIndex = -1, lastSameIndex = -1; float peakTempTimesN = -999.0; for (size_t i = 0; i + numToAverage <= tuningReadingsTaken; ++i) { @@ -780,18 +789,22 @@ void PID::DoTuningStep() { peak += tuningTempReadings[i + j]; } - if (peak >= peakTempTimesN) + if (peak > peakTempTimesN) { - if ((int)i == firstPeakIndex + 1) + if ((int)i == lastSameIndex + 1) { - firstPeakIndex = (int)i; // readings still going up or staying the same, so advance the first peak index + firstPeakIndex = lastSameIndex = (int)i; // readings still going up or staying the same, so advance the first peak index peakTempTimesN = peak; } else { - return -1; // error, more than one peak + return -1; // error, more than one peak } } + else if (peak == peakTempTimesN) // exact equality can occur because the floating point value is computed from an integral value + { + lastSameIndex = (int)i; + } } return firstPeakIndex + (numToAverage - 1)/2; } @@ -804,11 +817,17 @@ void PID::CalculateModel() DisplayBuffer("At completion"); } const float tc = (float)((tuningReadingsTaken - 1) * tuningReadingInterval)/(1000.0 * log((tuningTempReadings[0] - tuningStartTemp)/(tuningTempReadings[tuningReadingsTaken - 1] - tuningStartTemp))); - const float td = (float)tuningPeakDelay * 0.001; const float heatingTime = (tuningHeatingTime - tuningPeakDelay) * 0.001; const float gain = (tuningHeaterOffTemp - tuningStartTemp)/(1.0 - exp(-heatingTime/tc)); - tuned = SetModel(gain, tc, td, model.GetMaxPwm(), true); + // There are two ways of calculating the dead time: + // 1. Based on the delay to peak temperature after we turned the heater off. Adding 0.5sec and then taking 65% of the result is about right. + // 2. Based on the peak temperature compared to the temperature at which we turned the heater off. + // Try #2 because it is easier to identify the peak temperature than the delay to peak temperature. It can be slightly to aggressive, so add 30%. + //const float td = (float)(tuningPeakDelay + 500) * 0.00065; // take the dead time as 65% of the delay to peak rounded up to a half second + const float td = tc * logf((gain + tuningStartTemp - tuningHeaterOffTemp)/(gain + tuningStartTemp - tuningPeakTemperature)) * 1.3; + + tuned = SetModel(gain, tc, td, tuningPwm, true); if (tuned) { platform.MessageF(GENERIC_MESSAGE, |