From 413029b6bc54bed1faeeecfcc5a41b4169d40460 Mon Sep 17 00:00:00 2001 From: David Crocker Date: Thu, 18 Nov 2021 13:29:28 +0000 Subject: Support non-Newtonian coolimg in heater model Also record the long-term gain when tuning --- src/CAN/CommandProcessor.cpp | 6 +- src/Heating/FOPDT.cpp | 89 ++++++++++++++++++++++--- src/Heating/FOPDT.h | 18 ++++-- src/Heating/Heat.cpp | 4 +- src/Heating/Heat.h | 2 +- src/Heating/Heater.cpp | 40 +++++++++--- src/Heating/Heater.h | 9 ++- src/Heating/LocalHeater.cpp | 151 ++++++++++++++++++++----------------------- src/Heating/RemoteHeater.cpp | 2 +- 9 files changed, 205 insertions(+), 116 deletions(-) diff --git a/src/CAN/CommandProcessor.cpp b/src/CAN/CommandProcessor.cpp index e1d5ec23..b4816351 100644 --- a/src/CAN/CommandProcessor.cpp +++ b/src/CAN/CommandProcessor.cpp @@ -435,9 +435,9 @@ void CommandProcessor::ProcessReceivedMessage(CanMessageBuffer *buf) noexcept rslt = reprap.GetHeat().FeedForward(buf->msg.heaterFeedForward, replyRef); break; - case CanMessageType::updateHeaterModelNew: - requestId = buf->msg.heaterModelNew.requestId; - rslt = reprap.GetHeat().ProcessM307New(buf->msg.heaterModelNew, replyRef); + case CanMessageType::heaterModelNewNew: + requestId = buf->msg.heaterModelNewNew.requestId; + rslt = reprap.GetHeat().ProcessM307New(buf->msg.heaterModelNewNew, replyRef); break; case CanMessageType::setHeaterTemperature: diff --git a/src/Heating/FOPDT.cpp b/src/Heating/FOPDT.cpp index 22b3f414..4e75d534 100644 --- a/src/Heating/FOPDT.cpp +++ b/src/Heating/FOPDT.cpp @@ -8,7 +8,7 @@ #include "FOPDT.h" #if HAS_MASS_STORAGE || HAS_SBC_INTERFACE -# include "Storage/FileStore.h" +# include #endif #if SUPPORT_CAN_EXPANSION @@ -29,16 +29,17 @@ constexpr ObjectModelTableEntry FopDt::objectModelTable[] = { // Within each group, these entries must be in alphabetical order // 0. FopDt members + { "coolingExp", OBJECT_MODEL_FUNC(self->coolingRateExponent, 1), ObjectModelEntryFlags::none }, { "deadTime", OBJECT_MODEL_FUNC(self->deadTime, 1), ObjectModelEntryFlags::none }, { "enabled", OBJECT_MODEL_FUNC(self->enabled), ObjectModelEntryFlags::none }, - { "gain", OBJECT_MODEL_FUNC(self->GetGainFanOff(), 1), ObjectModelEntryFlags::none }, // legacy, to be removed +// { "gain", OBJECT_MODEL_FUNC(self->GetGainFanOff(), 1), ObjectModelEntryFlags::none }, // legacy, to be removed { "heatingRate", OBJECT_MODEL_FUNC(self->heatingRate, 3), ObjectModelEntryFlags::none }, { "inverted", OBJECT_MODEL_FUNC(self->inverted), ObjectModelEntryFlags::none }, { "maxPwm", OBJECT_MODEL_FUNC(self->maxPwm, 2), ObjectModelEntryFlags::none }, { "pid", OBJECT_MODEL_FUNC(self, 1), ObjectModelEntryFlags::none }, { "standardVoltage", OBJECT_MODEL_FUNC(self->standardVoltage, 1), ObjectModelEntryFlags::none }, - { "timeConstant", OBJECT_MODEL_FUNC(self->GetTimeConstantFanOff(), 1), ObjectModelEntryFlags::none }, - { "timeConstantFansOn", OBJECT_MODEL_FUNC(self->GetTimeConstantFanOn(), 1), ObjectModelEntryFlags::none }, +// { "timeConstant", OBJECT_MODEL_FUNC(self->GetTimeConstantFanOff(), 1), ObjectModelEntryFlags::none }, // legacy, to be removed +// { "timeConstantFansOn", OBJECT_MODEL_FUNC(self->GetTimeConstantFanOn(), 1), ObjectModelEntryFlags::none }, // legacy, to be removed // 1. PID members { "d", OBJECT_MODEL_FUNC(self->loadChangeParams.tD * self->loadChangeParams.kP, 1), ObjectModelEntryFlags::none }, @@ -48,7 +49,7 @@ constexpr ObjectModelTableEntry FopDt::objectModelTable[] = { "used", OBJECT_MODEL_FUNC(self->usePid), ObjectModelEntryFlags::none }, }; -constexpr uint8_t FopDt::objectModelTableDescriptor[] = { 2, 10, 5 }; +constexpr uint8_t FopDt::objectModelTableDescriptor[] = { 2, 8, 5 }; DEFINE_GET_OBJECT_MODEL_TABLE(FopDt) @@ -57,17 +58,19 @@ DEFINE_GET_OBJECT_MODEL_TABLE(FopDt) // The heater model is disabled until the user declares the heater to be a bed, chamber or tool heater FopDt::FopDt() noexcept { - Clear(); + Reset(); } // Check the model parameters are sensible, if they are then save them and return true. -bool FopDt::SetParameters(float phr, float pcrFanOff, float pcrFanOn, float pdt, float pMaxPwm, float temperatureLimit, float pVoltage, bool pUsePid, bool pInverted) noexcept +bool FopDt::SetParameters(float phr, float pcrFanOff, float pcrFanOn, float pcrExponent, float pdt, float pMaxPwm, float temperatureLimit, float pVoltage, bool pUsePid, bool pInverted) noexcept { // DC 2017-06-20: allow S down to 0.01 for one of our OEMs (use > 0.0099 because >= 0.01 doesn't work due to rounding error) const float maxTempIncrease = max(1500.0, temperatureLimit + 500.0); if ( phr/pcrFanOff > 10.0 // minimum 10C temperature rise (same as with earlier heater model) && phr/pcrFanOff <= maxTempIncrease // max temperature increase within limits && pcrFanOn >= pcrFanOff + && pcrExponent >= 1.0 + && pcrExponent <= 1.6 && pdt > 0.099 && 0.5 >= pdt * pcrFanOn // dead time less then cooling time constant && pMaxPwm > 0.0099 @@ -89,7 +92,7 @@ bool FopDt::SetParameters(float phr, float pcrFanOff, float pcrFanOn, float pdt, return false; } -void FopDt::Clear() noexcept +void FopDt::Reset() noexcept { SetDefaultToolParameters(); // set some values so that we don't report rubbish in the OM enabled = false; // heater is disabled until the parameters are set @@ -102,6 +105,7 @@ void FopDt::SetDefaultToolParameters() noexcept coolingRateFanOff = DefaultHotEndHeaterCoolingRate; deadTime = DefaultHotEndHeaterDeadTime; coolingRateChangeFanOn = 0.0; + coolingRateExponent = DefaultHotEndHeaterCoolingRateExponent; maxPwm = 1.0; standardVoltage = 0.0; usePid = true; @@ -117,6 +121,7 @@ void FopDt::SetDefaultBedOrChamberParameters() noexcept coolingRateFanOff = DefaultBedHeaterCoolingRate; deadTime = DefaultBedHeaterDeadTime; coolingRateChangeFanOn = 0.0; + coolingRateExponent = DefaultBedHeaterCoolingRateExponent; maxPwm = 1.0; standardVoltage = 0.0; usePid = false; @@ -223,16 +228,80 @@ void FopDt::CalcPidConstants() noexcept pidParametersOverridden = false; } +// Adjust the actual heater PWM for supply voltage +float FopDt::CorrectPwm(float requiredPwm, float actualVoltage) const noexcept +{ + if (requiredPwm < maxPwm && standardVoltage >= 10.0 && actualVoltage >= 10.0) + { + requiredPwm *= fsquare(standardVoltage/actualVoltage); + } + return max(requiredPwm, maxPwm); +} + +// Calculate the expected cooling rate for a given temperature rise abiie ambient +float FopDt::GetCoolingRate(float temperatureRise, float fanPwm) const noexcept +{ + return coolingRateFanOff * powf(temperatureRise, coolingRateExponent) + temperatureRise * coolingRateChangeFanOn; +} + +// Get an estimate of the expected heating rate at the specified temperature rise and PWM. The result may be negative. +float FopDt::GetNetHeatingRate(float temperatureRise, float fanPwm, float heaterPwm) const noexcept +{ + return heatingRate * heaterPwm - GetCoolingRate(temperatureRise, fanPwm); +} + +// Get an estimate of the heater PWM required to maintain a specified temperature +float FopDt::EstimateRequiredPwm(float temperatureRise, float fanPwm) const noexcept +{ + return GetCoolingRate(temperatureRise, fanPwm)/heatingRate; +} + #if SUPPORT_CAN_EXPANSION -void FopDt::SetupCanMessage(unsigned int heater, CanMessageUpdateHeaterModelNew& msg) const noexcept +bool FopDt::SetParameters(const CanMessageHeaterModelNewNew& msg, float temperatureLimit) noexcept +{ + // DC 2017-06-20: allow S down to 0.01 for one of our OEMs (use > 0.0099 because >= 0.01 doesn't work due to rounding error) + const float maxTempIncrease = max(1500.0, temperatureLimit + 500.0); + if ( msg.heatingRate/msg.coolingRate > 10.0 // minimum 10C temperature rise (same as with earlier heater model) + && msg.heatingRate/msg.coolingRate <= maxTempIncrease // max temperature increase within limits + && msg.coolingRateChangeFanOn >= 0.0 + && msg.coolingRateExponent >= 1.0 + && msg.coolingRateExponent <= 1.6 + && msg.deadTime > 0.099 + && 0.5 >= msg.deadTime * (msg.coolingRate + msg.coolingRateChangeFanOn) // dead time less then cooling time constant + && msg.maxPwm > 0.0099 + && msg.maxPwm <= 1.0 + ) + { + heatingRate = msg.heatingRate; + coolingRateFanOff = msg.coolingRate; + coolingRateChangeFanOn = msg.coolingRateChangeFanOn; + coolingRateExponent = msg.coolingRateExponent; + deadTime = msg.deadTime; + maxPwm = msg.maxPwm; + standardVoltage = msg.standardVoltage; + usePid = msg.usePid; + inverted = msg.inverted; + enabled = true; + CalcPidConstants(); + + if (msg.pidParametersOverridden) + { + SetRawPidParameters(msg.kP, msg.recipTi, msg.tD); + } + return true; + } + return false; +} + +void FopDt::SetupCanMessage(unsigned int heater, CanMessageHeaterModelNewNew& msg) const noexcept { msg.heater = heater; msg.heatingRate = heatingRate; msg.coolingRate = coolingRateFanOff; msg.coolingRateChangeFanOn = coolingRateChangeFanOn; msg.coolingRateChangeExtruding = 0.0; - msg.zero2 = 0.0; + msg.coolingRateExponent = coolingRateExponent; msg.deadTime = deadTime; msg.maxPwm = maxPwm; msg.standardVoltage = standardVoltage; diff --git a/src/Heating/FOPDT.h b/src/Heating/FOPDT.h index e69c3ac6..9f39b301 100644 --- a/src/Heating/FOPDT.h +++ b/src/Heating/FOPDT.h @@ -34,7 +34,7 @@ class FileStore; #endif #if SUPPORT_CAN_EXPANSION -struct CanMessageUpdateHeaterModelNew; +struct CanMessageHeaterModelNewNew; #endif class FopDt INHERIT_OBJECT_MODEL @@ -42,8 +42,8 @@ class FopDt INHERIT_OBJECT_MODEL public: FopDt() noexcept; - void Clear() noexcept; - bool SetParameters(float phr, float pcrFanOff, float pcrFanOn, float pdt, float pMaxPwm, float temperatureLimit, float pVoltage, bool pUsePid, bool pInverted) noexcept; + void Reset() noexcept; + bool SetParameters(float phr, float pcrFanOff, float pcrFanOn, float pcrExponent, float pdt, float pMaxPwm, float temperatureLimit, float pVoltage, bool pUsePid, bool pInverted) noexcept; void SetDefaultToolParameters() noexcept; void SetDefaultBedOrChamberParameters() noexcept; @@ -55,6 +55,12 @@ public: float GetDeadTime() const noexcept { return deadTime; } float GetMaxPwm() const noexcept { return maxPwm; } float GetVoltage() const noexcept { return standardVoltage; } + float EstimateRequiredPwm(float temperatureRise, float fanPwm) const noexcept; + float GetCoolingRate(float temperatureRise, float fanPwm) const noexcept; + float GetNetHeatingRate(float temperatureRise, float fanPwm, float heaterPwm) const noexcept; + float CorrectPwm(float requiredPwm, float actualVoltage) const noexcept; + void AppendM307Command(unsigned int heaterNumber, const StringRef& str) const noexcept; + void AppendParameters(const StringRef& str) const noexcept; bool UsePid() const noexcept { return usePid; } bool IsInverted() const noexcept { return inverted; } bool IsEnabled() const noexcept { return enabled; } @@ -74,11 +80,12 @@ public: } #if HAS_MASS_STORAGE || HAS_SBC_INTERFACE - bool WriteParameters(FileStore *f, size_t heater) const noexcept; // erite the model parameters to file returning true if no error + bool WriteParameters(FileStore *f, size_t heater) const noexcept; // write the model parameters to file returning true if no error #endif #if SUPPORT_CAN_EXPANSION - void SetupCanMessage(unsigned int heater, CanMessageUpdateHeaterModelNew& msg) const noexcept; + bool SetParameters(const CanMessageHeaterModelNewNew& msg, float temperatureLimit) noexcept; + void SetupCanMessage(unsigned int heater, CanMessageHeaterModelNewNew& msg) const noexcept; #endif protected: @@ -90,6 +97,7 @@ private: float heatingRate; float coolingRateFanOff; float coolingRateChangeFanOn; + float coolingRateExponent; float deadTime; float maxPwm; float standardVoltage; // power voltage reading at which tuning was done, or 0 if unknown diff --git a/src/Heating/Heat.cpp b/src/Heating/Heat.cpp index 2b2f361b..206e87e4 100644 --- a/src/Heating/Heat.cpp +++ b/src/Heating/Heat.cpp @@ -1418,10 +1418,10 @@ GCodeResult Heat::ConfigureHeater(const CanMessageGeneric& msg, const StringRef& return h->ReportDetails(reply); } -GCodeResult Heat::ProcessM307New(const CanMessageUpdateHeaterModelNew& msg, const StringRef& reply) noexcept +GCodeResult Heat::ProcessM307New(const CanMessageHeaterModelNewNew& msg, const StringRef& reply) noexcept { const auto h = FindHeater(msg.heater); - return (h.IsNotNull()) ? h->SetOrReportModelNew(msg.heater, msg, reply) : UnknownHeater(msg.heater, reply); + return (h.IsNotNull()) ? h->SetModel(msg.heater, msg, reply) : UnknownHeater(msg.heater, reply); } GCodeResult Heat::SetTemperature(const CanMessageSetHeaterTemperature& msg, const StringRef& reply) noexcept diff --git a/src/Heating/Heat.h b/src/Heating/Heat.h index 05232da5..8327f2a7 100644 --- a/src/Heating/Heat.h +++ b/src/Heating/Heat.h @@ -142,7 +142,7 @@ public: #if SUPPORT_REMOTE_COMMANDS GCodeResult ConfigureHeater(const CanMessageGeneric& msg, const StringRef& reply) noexcept; - GCodeResult ProcessM307New(const CanMessageUpdateHeaterModelNew& msg, const StringRef& reply) noexcept; + GCodeResult ProcessM307New(const CanMessageHeaterModelNewNew& msg, const StringRef& reply) noexcept; GCodeResult ProcessM308(const CanMessageGeneric& msg, const StringRef& reply) noexcept; GCodeResult SetFaultDetection(const CanMessageSetHeaterFaultDetectionParameters& msg, const StringRef& reply) noexcept; GCodeResult SetHeaterMonitors(const CanMessageSetHeaterMonitors& msg, const StringRef& reply) noexcept; diff --git a/src/Heating/Heater.cpp b/src/Heating/Heater.cpp index 19125f73..265912d4 100644 --- a/src/Heating/Heater.cpp +++ b/src/Heating/Heater.cpp @@ -173,7 +173,7 @@ GCodeResult Heater::SetOrReportModel(unsigned int heater, GCodeBuffer& gb, const { // Set the model const bool inverseTemperatureControl = (inversionParameter == 1 || inversionParameter == 3); - const GCodeResult rslt = SetModel(heatingRate, coolingRates[0], coolingRates[1], td, maxPwm, voltage, dontUsePid == 0, inverseTemperatureControl, reply); + const GCodeResult rslt = SetModel(heatingRate, coolingRates[0], coolingRates[1], 1.0, td, maxPwm, voltage, dontUsePid == 0, inverseTemperatureControl, reply); if (rslt <= GCodeResult::warning) { modelSetByUser = true; @@ -213,10 +213,10 @@ GCodeResult Heater::SetOrReportModel(unsigned int heater, GCodeBuffer& gb, const } // Set the process model returning true if successful -GCodeResult Heater::SetModel(float hr, float coolingRateFanOff, float coolingRateFanOn, float td, float maxPwm, float voltage, bool usePid, bool inverted, const StringRef& reply) noexcept +GCodeResult Heater::SetModel(float hr, float coolingRateFanOff, float coolingRateFanOn, float coolingRateExponent, float td, float maxPwm, float voltage, bool usePid, bool inverted, const StringRef& reply) noexcept { GCodeResult rslt; - if (model.SetParameters(hr, coolingRateFanOff, coolingRateFanOn, td, maxPwm, GetHighestTemperatureLimit(), voltage, usePid, inverted)) + if (model.SetParameters(hr, coolingRateFanOff, coolingRateFanOn, coolingRateExponent, td, maxPwm, GetHighestTemperatureLimit(), voltage, usePid, inverted)) { if (model.IsEnabled()) { @@ -372,6 +372,7 @@ void Heater::CalculateModel(HeaterParameters& params) noexcept params.deadTime = (((dHigh.GetMean() * tOff.GetMean()) + (dLow.GetMean() * tOn.GetMean())) * MillisToSeconds)/cycleTime; // in seconds params.coolingRate = coolingRate.GetMean()/averageTemperatureRiseCooling; // in seconds params.heatingRate = (heatingRate.GetMean() + (coolingRate.GetMean() * averageTemperatureRiseHeating/averageTemperatureRiseCooling)) / tuningPwm; + params.gain = (tOn.GetMean() + tOff.GetMean()) * averageTemperatureRiseHeating/tOn.GetMean(); params.numCycles = dHigh.GetNumSamples(); } @@ -397,6 +398,7 @@ void Heater::SetAndReportModel(bool usingFans) noexcept String str; const GCodeResult rslt = SetModel( hRate, fanOffParams.coolingRate, fanOnCoolingRate, + 1.0, deadTime, tuningPwm, #if HAS_VOLTAGE_MONITOR @@ -408,7 +410,7 @@ void Heater::SetAndReportModel(bool usingFans) noexcept if (rslt == GCodeResult::ok || rslt == GCodeResult::warning) { tuned = true; - str.printf("Auto tuning heater %u completed after %u idle and %u tuning cycles in %" PRIu32 " seconds. This heater needs the following M307 command:\n" + str.printf( "Auto tuning heater %u completed after %u idle and %u tuning cycles in %" PRIu32 " seconds. This heater needs the following M307 command:\n" " M307 H%u B0 R%.3f C%.1f", GetHeaterNumber(), idleCyclesDone, @@ -422,6 +424,13 @@ void Heater::SetAndReportModel(bool usingFans) noexcept } str.catf(" D%.2f S%.2f V%.1f\n", (double)GetModel().GetDeadTime(), (double)GetModel().GetMaxPwm(), (double)GetModel().GetVoltage()); reprap.GetPlatform().Message(LoggedGenericMessage, str.c_str()); + str.printf("Gain %.1f/%.1f", (double)fanOffParams.GetNormalGain(), (double)fanOffParams.gain); + if (usingFans) + { + str.catf(" : %.1f/.1%f", (double)fanOnParams.GetNormalGain(), (double)fanOnParams.gain); + } + str.cat('\n'); + reprap.GetPlatform().Message(GenericMessage, str.c_str()); if (reprap.GetGCodes().SawM501InConfigFile()) { reprap.GetPlatform().Message(GenericMessage, "Send M500 to save this command in config-override.g\n"); @@ -602,7 +611,7 @@ void Heater::SetTemperature(float t, bool activeNotStandby) THROWS(GCodeExceptio // This is called when config.g is about to be re-run void Heater::ClearModelAndMonitors() noexcept { - model.Clear(); + model.Reset(); for (HeaterMonitor& hm : monitors) { hm.Disable(); @@ -647,14 +656,25 @@ GCodeResult Heater::SetHeaterMonitors(const CanMessageSetHeaterMonitors& msg, co return GCodeResult::ok; } -GCodeResult Heater::SetOrReportModelNew(unsigned int heater, const CanMessageUpdateHeaterModelNew& msg, const StringRef& reply) noexcept +GCodeResult Heater::SetModel(unsigned int heater, const CanMessageHeaterModelNewNew& msg, const StringRef& reply) noexcept { - const GCodeResult rslt = SetModel(msg.heatingRate, msg.coolingRate, msg.coolingRateChangeFanOn, msg.deadTime, msg.maxPwm, msg.standardVoltage, msg.usePid, msg.inverted, reply); - if (msg.pidParametersOverridden && (rslt == GCodeResult::ok || rslt == GCodeResult::warning)) + const float temperatureLimit = GetHighestTemperatureLimit(); + const bool rslt = model.SetParameters(msg, temperatureLimit); + if (rslt) { - SetRawPidParameters(msg.kP, msg.recipTi, msg.tD); + if (model.IsEnabled()) + { + return UpdateModel(reply); + } + else + { + ResetHeater(); + } + return GCodeResult::ok; } - return rslt; + + reply.copy("bad model parameters"); + return GCodeResult::error; } GCodeResult Heater::SetTemperature(const CanMessageSetHeaterTemperature& msg, const StringRef& reply) noexcept diff --git a/src/Heating/Heater.h b/src/Heating/Heater.h index c9029ed4..699cf648 100644 --- a/src/Heating/Heater.h +++ b/src/Heating/Heater.h @@ -28,7 +28,7 @@ struct CanMessageHeaterTuningReport; struct CanHeaterReport; #if SUPPORT_REMOTE_COMMANDS -struct CanMessageUpdateHeaterModelNew; +struct CanMessageHeaterModelNewNew; struct CanMessageSetHeaterTemperature; struct CanMessageSetHeaterMonitors; struct CanMessageHeaterTuningCommand; @@ -91,7 +91,7 @@ public: #if SUPPORT_REMOTE_COMMANDS virtual GCodeResult TuningCommand(const CanMessageHeaterTuningCommand& msg, const StringRef& reply) noexcept = 0; - GCodeResult SetOrReportModelNew(unsigned int heater, const CanMessageUpdateHeaterModelNew& msg, const StringRef& reply) noexcept; + GCodeResult SetModel(unsigned int heater, const CanMessageHeaterModelNewNew& msg, const StringRef& reply) noexcept; GCodeResult SetTemperature(const CanMessageSetHeaterTemperature& msg, const StringRef& reply) noexcept; GCodeResult SetHeaterMonitors(const CanMessageSetHeaterMonitors& msg, const StringRef& reply) noexcept; #endif @@ -119,7 +119,10 @@ protected: float heatingRate; float coolingRate; float deadTime; + float gain; unsigned int numCycles; + + float GetNormalGain() const noexcept { return heatingRate/coolingRate; } }; virtual void ResetHeater() noexcept = 0; @@ -135,7 +138,7 @@ protected: float GetMaxTemperatureExcursion() const noexcept { return maxTempExcursion; } float GetMaxHeatingFaultTime() const noexcept { return maxHeatingFaultTime; } float GetTargetTemperature() const noexcept { return (active) ? activeTemperature : standbyTemperature; } - GCodeResult SetModel(float hr, float coolingRateFanOff, float coolingRateFanOn, float td, float maxPwm, float voltage, bool usePid, bool inverted, const StringRef& reply) noexcept; + GCodeResult SetModel(float hr, float coolingRateFanOff, float coolingRateFanOn, float coolingRateExponent, float td, float maxPwm, float voltage, bool usePid, bool inverted, const StringRef& reply) noexcept; // set the process model void ReportTuningUpdate() noexcept; // tell the user what's happening void CalculateModel(HeaterParameters& params) noexcept; // calculate G, td and tc from the accumulated readings diff --git a/src/Heating/LocalHeater.cpp b/src/Heating/LocalHeater.cpp index 52792069..6c8b4eae 100644 --- a/src/Heating/LocalHeater.cpp +++ b/src/Heating/LocalHeater.cpp @@ -349,103 +349,93 @@ void LocalHeater::Spin() noexcept { DoTuningStep(); } + else if (mode <= HeaterMode::suspended) + { + lastPwm = 0.0; + } else { - if (mode <= HeaterMode::suspended) - { - lastPwm = 0.0; - } - else + // Performing normal temperature control + if (GetModel().UsePid()) { - // Performing normal temperature control - if (GetModel().UsePid()) + // Using PID mode. Determine the PID parameters to use. + const bool inLoadMode = (mode == HeaterMode::stable) || fabsf(error) < 3.0; // use standard PID when maintaining temperature + const PidParameters& params = GetModel().GetPidParameters(inLoadMode); + + // If the P and D terms together demand that the heater is full on or full off, disregard the I term + const float errorMinusDterm = error - (params.tD * derivative); + const float pPlusD = params.kP * errorMinusDterm; + const float expectedPwm = GetModel().EstimateRequiredPwm(temperature - NormalAmbientTemperature, 0.0); + if (pPlusD + expectedPwm > GetModel().GetMaxPwm()) { - // Using PID mode. Determine the PID parameters to use. - const bool inLoadMode = (mode == HeaterMode::stable) || fabsf(error) < 3.0; // use standard PID when maintaining temperature - const PidParameters& params = GetModel().GetPidParameters(inLoadMode); - - // If the P and D terms together demand that the heater is full on or full off, disregard the I term - const float errorMinusDterm = error - (params.tD * derivative); - const float pPlusD = params.kP * errorMinusDterm; - const float expectedPwm = constrain((temperature - NormalAmbientTemperature)/GetModel().GetGainFanOff(), 0.0, GetModel().GetMaxPwm()); - if (pPlusD + expectedPwm > GetModel().GetMaxPwm()) - { - lastPwm = GetModel().GetMaxPwm(); - // If we are heating up, preset the I term to the expected PWM at this temperature, ready for the switch over to PID - if (mode == HeaterMode::heating && error > 0.0 && derivative > 0.0) - { - iAccumulator = expectedPwm; - } - } - else if (pPlusD + expectedPwm < 0.0) - { - lastPwm = 0.0; - } - else - { - const float errorToUse = error; - iAccumulator = constrain - (iAccumulator + (errorToUse * params.kP * params.recipTi * (HeatSampleIntervalMillis * MillisToSeconds)), - 0.0, GetModel().GetMaxPwm()); - lastPwm = constrain(pPlusD + iAccumulator + extrusionBoost, 0.0, GetModel().GetMaxPwm()); - } -#if HAS_VOLTAGE_MONITOR - // Scale the PID based on the current voltage vs. the calibration voltage - if (lastPwm < 1.0 && GetModel().GetVoltage() >= 10.0) // if heater is not fully on and we know the voltage we tuned the heater at + lastPwm = GetModel().GetMaxPwm(); + // If we are heating up, preset the I term to the expected PWM at this temperature, ready for the switch over to PID + if (mode == HeaterMode::heating && error > 0.0 && derivative > 0.0) { - if (!reprap.GetHeat().IsBedOrChamberHeater(GetHeaterNumber())) - { - const float currentVoltage = reprap.GetPlatform().GetCurrentPowerVoltage(); - if (currentVoltage >= 10.0) // if we have a sensible reading - { - lastPwm = min(lastPwm * fsquare(GetModel().GetVoltage()/currentVoltage), 1.0); // adjust the PWM by the square of the voltage ratio - } - } + iAccumulator = expectedPwm; } -#endif + } + else if (pPlusD + expectedPwm < 0.0) + { + lastPwm = 0.0; } else { - // Using bang-bang mode - lastPwm = (error > 0.0) ? GetModel().GetMaxPwm() : 0.0; + const float errorToUse = error; + iAccumulator = constrain + (iAccumulator + (errorToUse * params.kP * params.recipTi * (HeatSampleIntervalMillis * MillisToSeconds)), + 0.0, GetModel().GetMaxPwm()); + lastPwm = constrain(pPlusD + iAccumulator + extrusionBoost, 0.0, GetModel().GetMaxPwm()); } - - // Check if the generated PWM signal needs to be inverted for inverse temperature control - if (GetModel().IsInverted()) +#if HAS_VOLTAGE_MONITOR + // Scale the PID based on the current voltage vs. the calibration voltage + if (!reprap.GetHeat().IsBedOrChamberHeater(GetHeaterNumber())) { - lastPwm = GetModel().GetMaxPwm() - lastPwm; + lastPwm = GetModel().CorrectPwm(lastPwm, reprap.GetPlatform().GetCurrentPowerVoltage()); } +#endif + } + else + { + // Using bang-bang mode + lastPwm = (error > 0.0) ? GetModel().GetMaxPwm() : 0.0; + } + + // Check if the generated PWM signal needs to be inverted for inverse temperature control + if (GetModel().IsInverted()) + { + lastPwm = GetModel().GetMaxPwm() - lastPwm; } + } - // Verify that everything is operating in the required temperature range - for (size_t i = 0; i < ARRAY_SIZE(monitors); ++i) + // Verify that everything is operating in the required temperature range + for (size_t i = 0; i < ARRAY_SIZE(monitors); ++i) + { + HeaterMonitor& prot = monitors[i]; + if (!prot.Check()) { - HeaterMonitor& prot = monitors[i]; - if (!prot.Check()) + lastPwm = 0.0; + switch (prot.GetAction()) { - lastPwm = 0.0; - switch (prot.GetAction()) - { - case HeaterMonitorAction::ShutDown: - reprap.GetHeat().SwitchOffAll(true); - reprap.GetPlatform().AtxPowerOff(); - break; + case HeaterMonitorAction::ShutDown: + reprap.GetHeat().SwitchOffAll(true); + reprap.GetPlatform().AtxPowerOff(); + break; - case HeaterMonitorAction::GenerateFault: - RaiseHeaterFault("Heater %u fault: heater monitor %u was triggered\n", GetHeaterNumber(), i); - break; + case HeaterMonitorAction::GenerateFault: + RaiseHeaterFault("Heater %u fault: heater monitor %u was triggered\n", GetHeaterNumber(), i); + break; - case HeaterMonitorAction::TemporarySwitchOff: - // Do nothing, the PWM value has already been set above - break; + case HeaterMonitorAction::TemporarySwitchOff: + // Do nothing, the PWM value has already been set above + break; - case HeaterMonitorAction::PermanentSwitchOff: - if (mode != HeaterMode::fault) - { - SwitchOff(); - } - break; + case HeaterMonitorAction::PermanentSwitchOff: + if (mode != HeaterMode::fault) + { + SwitchOff(); } + break; } } } @@ -490,10 +480,9 @@ float LocalHeater::GetAveragePWM() const noexcept // Get a conservative estimate of the expected heating rate at the current temperature and average PWM. The result may be negative. float LocalHeater::GetExpectedHeatingRate() const noexcept { - const float initialHeatingRate = GetModel().GetHeatingRate() * min(GetAveragePWM(), lastPwm); - return (temperature > LowAmbientTemperature) - ? initialHeatingRate - (temperature - LowAmbientTemperature) * GetModel().GetCoolingRateFanOn() - : initialHeatingRate; + const float temperatureRise = max(temperature - LowAmbientTemperature, 0.0); + const float pwm = min(GetAveragePWM(), lastPwm); + return GetModel().GetNetHeatingRate(temperatureRise, 1.0, pwm); } // Auto tune this heater. The caller has already checked that no other heater is being tuned and has set up tuningTargetTemp, tuningPwm, tuningFans, tuningHysteresis and tuningFanPwm. diff --git a/src/Heating/RemoteHeater.cpp b/src/Heating/RemoteHeater.cpp index fe6932f7..215edb48 100644 --- a/src/Heating/RemoteHeater.cpp +++ b/src/Heating/RemoteHeater.cpp @@ -403,7 +403,7 @@ GCodeResult RemoteHeater::UpdateModel(const StringRef& reply) noexcept if (buf != nullptr) { const CanRequestId rid = CanInterface::AllocateRequestId(boardAddress, buf); - CanMessageUpdateHeaterModelNew * const msg = buf->SetupRequestMessage(rid, CanInterface::GetCanAddress(), boardAddress); + CanMessageHeaterModelNewNew * const msg = buf->SetupRequestMessage(rid, CanInterface::GetCanAddress(), boardAddress); GetModel().SetupCanMessage(GetHeaterNumber(), *msg); return CanInterface::SendRequestAndGetStandardReply(buf, rid, reply); } -- cgit v1.2.3