Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/Duet3D/RepRapFirmware.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Crocker <dcrocker@eschertech.com>2021-11-18 16:29:28 +0300
committerDavid Crocker <dcrocker@eschertech.com>2021-11-18 16:29:28 +0300
commit413029b6bc54bed1faeeecfcc5a41b4169d40460 (patch)
tree7cf005d3fa688f3fbd2e9778c1f2153705ff75e9
parent020e9fbc171b1a519d9b8f01df2e68f1c273beaf (diff)
Support non-Newtonian coolimg in heater model
Also record the long-term gain when tuning
-rw-r--r--src/CAN/CommandProcessor.cpp6
-rw-r--r--src/Heating/FOPDT.cpp89
-rw-r--r--src/Heating/FOPDT.h18
-rw-r--r--src/Heating/Heat.cpp4
-rw-r--r--src/Heating/Heat.h2
-rw-r--r--src/Heating/Heater.cpp40
-rw-r--r--src/Heating/Heater.h9
-rw-r--r--src/Heating/LocalHeater.cpp151
-rw-r--r--src/Heating/RemoteHeater.cpp2
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 <Storage/FileStore.h>
#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<float>(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<float>(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<float>(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<StringLength256> 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<float>((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<float>
- (iAccumulator + (errorToUse * params.kP * params.recipTi * (HeatSampleIntervalMillis * MillisToSeconds)),
- 0.0, GetModel().GetMaxPwm());
- lastPwm = constrain<float>(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<float>(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<float>
+ (iAccumulator + (errorToUse * params.kP * params.recipTi * (HeatSampleIntervalMillis * MillisToSeconds)),
+ 0.0, GetModel().GetMaxPwm());
+ lastPwm = constrain<float>(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<float>(GetAveragePWM(), lastPwm);
- return (temperature > LowAmbientTemperature)
- ? initialHeatingRate - (temperature - LowAmbientTemperature) * GetModel().GetCoolingRateFanOn()
- : initialHeatingRate;
+ const float temperatureRise = max<float>(temperature - LowAmbientTemperature, 0.0);
+ const float pwm = min<float>(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<CanMessageUpdateHeaterModelNew>(rid, CanInterface::GetCanAddress(), boardAddress);
+ CanMessageHeaterModelNewNew * const msg = buf->SetupRequestMessage<CanMessageHeaterModelNewNew>(rid, CanInterface::GetCanAddress(), boardAddress);
GetModel().SetupCanMessage(GetHeaterNumber(), *msg);
return CanInterface::SendRequestAndGetStandardReply(buf, rid, reply);
}