move checksum defines into cpp from h
authorJim Morris <morris@wolfman.com>
Wed, 4 Dec 2013 09:24:46 +0000 (01:24 -0800)
committerJim Morris <morris@wolfman.com>
Wed, 4 Dec 2013 09:26:18 +0000 (01:26 -0800)
implement new autopid based on Bretts https://github.com/br3ttb/Arduino-PID-AutoTune-Library

src/modules/robot/Robot.cpp
src/modules/robot/Robot.h
src/modules/tools/extruder/Extruder.cpp
src/modules/tools/extruder/Extruder.h
src/modules/tools/temperaturecontrol/PID_Autotuner.cpp
src/modules/tools/temperaturecontrol/PID_Autotuner.h

index a5c07d2..b4652e1 100644 (file)
@@ -25,6 +25,26 @@ using std::string;
 #include "arm_solutions/JohannKosselSolution.h"
 #include "arm_solutions/HBotSolution.h"
 
+#define default_seek_rate_checksum             CHECKSUM("default_seek_rate")
+#define default_feed_rate_checksum             CHECKSUM("default_feed_rate")
+#define mm_per_line_segment_checksum           CHECKSUM("mm_per_line_segment")
+#define delta_segments_per_second_checksum     CHECKSUM("delta_segments_per_second")
+#define mm_per_arc_segment_checksum            CHECKSUM("mm_per_arc_segment")
+#define arc_correction_checksum                CHECKSUM("arc_correction")
+#define x_axis_max_speed_checksum              CHECKSUM("x_axis_max_speed")
+#define y_axis_max_speed_checksum              CHECKSUM("y_axis_max_speed")
+#define z_axis_max_speed_checksum              CHECKSUM("z_axis_max_speed")
+
+// arm solutions
+#define arm_solution_checksum                  CHECKSUM("arm_solution")
+#define cartesian_checksum                     CHECKSUM("cartesian")
+#define rotatable_cartesian_checksum           CHECKSUM("rotatable_cartesian")
+#define rostock_checksum                       CHECKSUM("rostock")
+#define delta_checksum                         CHECKSUM("delta")
+#define hbot_checksum                          CHECKSUM("hbot")
+#define corexy_checksum                        CHECKSUM("corexy")
+#define kossel_checksum                        CHECKSUM("kossel")
+
 // The Robot converts GCodes into actual movements, and then adds them to the Planner, which passes them to the Conveyor so they can be added to the queue
 // It takes care of cutting arcs into segments, same thing for line that are too long
 #define max(a,b) (((a) > (b)) ? (a) : (b))
index 89e99c9..e9c869f 100644 (file)
@@ -19,24 +19,6 @@ using std::string;
 #include "libs/StepperMotor.h"
 #include "RobotPublicAccess.h"
 
-#define default_seek_rate_checksum             CHECKSUM("default_seek_rate")
-#define default_feed_rate_checksum             CHECKSUM("default_feed_rate")
-#define mm_per_line_segment_checksum           CHECKSUM("mm_per_line_segment")
-#define delta_segments_per_second_checksum     CHECKSUM("delta_segments_per_second")
-#define mm_per_arc_segment_checksum            CHECKSUM("mm_per_arc_segment")
-#define arc_correction_checksum                CHECKSUM("arc_correction")
-#define x_axis_max_speed_checksum              CHECKSUM("x_axis_max_speed")
-#define y_axis_max_speed_checksum              CHECKSUM("y_axis_max_speed")
-#define z_axis_max_speed_checksum              CHECKSUM("z_axis_max_speed")
-#define arm_solution_checksum                  CHECKSUM("arm_solution")
-#define cartesian_checksum                     CHECKSUM("cartesian")
-#define rotatable_cartesian_checksum           CHECKSUM("rotatable_cartesian")
-#define rostock_checksum                       CHECKSUM("rostock")
-#define delta_checksum                         CHECKSUM("delta")
-#define hbot_checksum                          CHECKSUM("hbot")
-#define corexy_checksum                        CHECKSUM("corexy")
-#define kossel_checksum                        CHECKSUM("kossel")
-
 #define NEXT_ACTION_DEFAULT 0
 #define NEXT_ACTION_DWELL 1
 #define NEXT_ACTION_GO_HOME 2
index 9e167f4..9879e59 100644 (file)
 #include "modules/tools/extruder/Extruder.h"
 #include <mri.h>
 
+#define extruder_module_enable_checksum      CHECKSUM("extruder_module_enable")
+#define extruder_steps_per_mm_checksum       CHECKSUM("extruder_steps_per_mm")
+#define extruder_acceleration_checksum       CHECKSUM("extruder_acceleration")
+#define extruder_step_pin_checksum           CHECKSUM("extruder_step_pin")
+#define extruder_dir_pin_checksum            CHECKSUM("extruder_dir_pin")
+#define extruder_en_pin_checksum             CHECKSUM("extruder_en_pin")
+#define extruder_max_speed_checksum          CHECKSUM("extruder_max_speed")
+
+#define extruder_checksum                    CHECKSUM("extruder")
+
+#define default_feed_rate_checksum           CHECKSUM("default_feed_rate")
+#define steps_per_mm_checksum                CHECKSUM("steps_per_mm")
+#define acceleration_checksum                CHECKSUM("acceleration")
+#define step_pin_checksum                    CHECKSUM("step_pin")
+#define dir_pin_checksum                     CHECKSUM("dir_pin")
+#define en_pin_checksum                      CHECKSUM("en_pin")
+#define max_speed_checksum                   CHECKSUM("max_speed")
+
 #define max(a,b) (((a) > (b)) ? (a) : (b))
 
 /* The extruder module controls a filament extruder for 3D printing: http://en.wikipedia.org/wiki/Fused_deposition_modeling
@@ -65,7 +83,7 @@ void Extruder::on_module_loaded() {
 void Extruder::on_config_reload(void* argument){
 
     // If this module uses the old "single extruder" configuration style
-    if( this->single_config ){ 
+    if( this->single_config ){
 
         this->steps_per_millimeter        = this->kernel->config->value(extruder_steps_per_mm_checksum      )->by_default(1)->as_number();
         this->acceleration                = this->kernel->config->value(extruder_acceleration_checksum      )->by_default(1000)->as_number();
index e0fa411..774f69b 100644 (file)
 #include "modules/robot/Block.h"
 #include "modules/tools/toolsmanager/Tool.h"
 
-#define extruder_module_enable_checksum      CHECKSUM("extruder_module_enable")
-#define extruder_steps_per_mm_checksum       CHECKSUM("extruder_steps_per_mm")
-#define extruder_acceleration_checksum       CHECKSUM("extruder_acceleration")
-#define extruder_step_pin_checksum           CHECKSUM("extruder_step_pin")
-#define extruder_dir_pin_checksum            CHECKSUM("extruder_dir_pin")
-#define extruder_en_pin_checksum             CHECKSUM("extruder_en_pin")
-#define extruder_max_speed_checksum          CHECKSUM("extruder_max_speed")
-
-#define extruder_checksum                    CHECKSUM("extruder")
-
-#define steps_per_mm_checksum                CHECKSUM("steps_per_mm")
-#define acceleration_checksum                CHECKSUM("acceleration")
-#define step_pin_checksum                    CHECKSUM("step_pin")
-#define dir_pin_checksum                     CHECKSUM("dir_pin")
-#define en_pin_checksum                      CHECKSUM("en_pin")
-#define max_speed_checksum                   CHECKSUM("max_speed")
-
-
-
-// default_feed_rate_checksum defined by Robot.h
-
 #define OFF 0
 #define SOLO 1
 #define FOLLOW 2
dissimilarity index 78%
index c83d8aa..55994c6 100644 (file)
-#include "PID_Autotuner.h"
-
-#include "Kernel.h"
-
-PID_Autotuner::PID_Autotuner()
-{
-    t = NULL;
-    s = NULL;
-}
-
-void PID_Autotuner::on_module_loaded()
-{
-    tick = false;
-    this->kernel->slow_ticker->attach(20, this, &PID_Autotuner::on_tick );
-    register_for_event(ON_IDLE);
-    register_for_event(ON_GCODE_RECEIVED);
-}
-
-void PID_Autotuner::begin(TemperatureControl *temp, double target, StreamOutput *stream, int ncycles)
-{
-    if (t)
-        t->heater_pin.set(0);
-
-    s = stream;
-    t = temp;
-
-    t->target_temperature = 0.0;
-
-    target_temperature = target;
-
-    for (cycle = 0; cycle < ncycles; cycle++)
-    {
-        cycles[cycle].ticks_high = 0;
-        cycles[cycle].ticks_low  = 0;
-        cycles[cycle].t_max      = 0.0;
-        cycles[cycle].t_min      = 1000.0;
-    }
-    cycle = 0;
-
-    s->printf("%s: Starting PID Autotune, M304 aborts\n", t->designator.c_str());
-
-    bias = d = t->heater_pin.max_pwm() >> 1;
-
-    output = true;
-    last_output = true;
-}
-
-void PID_Autotuner::abort()
-{
-    if (!t)
-        return;
-
-    t->target_temperature = 0;
-    t->heater_pin.set(0);
-    t = NULL;
-
-    if (s)
-        s->printf("PID Autotune Aborted\n");
-    s = NULL;
-}
-
-void PID_Autotuner::on_gcode_received(void* argument)
-{
-    Gcode* gcode = static_cast<Gcode*>(argument);
-
-    if ((gcode->has_m) && (gcode->m == 304))
-        abort();
-}
-
-uint32_t PID_Autotuner::on_tick(uint32_t dummy)
-{
-    if (t)
-        tick = true;
-    return 0;
-}
-
-void PID_Autotuner::on_idle(void*)
-{
-    if (!tick)
-        return;
-
-    tick = false;
-
-    if (cycle >= PID_AUTOTUNER_CYCLES)
-        return;
-    if (t == NULL)
-        return;
-
-    if (t->last_reading > (target_temperature + 0.25))
-        output = false;
-    else if (t->last_reading < (target_temperature - 0.25))
-        output = true;
-
-    if (last_output == false && output)
-    {
-        s->printf("Cycle %d:\n\tMax: %5.1f  Min: %5.1f  high time: %3.1fs  low time: %3.1fs\n", cycle, cycles[cycle].t_max, cycles[cycle].t_min, cycles[cycle].ticks_high / 20.0, cycles[cycle].ticks_low / 20.0);
-
-        // this code taken from http://github.com/ErikZalm/Marlin/blob/Marlin_v1/Marlin/temperature.cpp
-        bias += (d * (cycles[cycle].ticks_high - cycles[cycle].ticks_low) * (1000.0 / 20.0)) / ((cycles[cycle].ticks_high + cycles[cycle].ticks_low) * (1000.0 / 20.0));
-        bias = confine(bias, 20, t->heater_pin.max_pwm() - 20);
-        if (bias > (t->heater_pin.max_pwm() / 2))
-            d = t->heater_pin.max_pwm() - 1 - bias;
-        else
-            d = bias;
-        // end code from Marlin firmware
-
-        cycle++;
-        if (cycle == PID_AUTOTUNER_CYCLES)
-        {
-            t->heater_pin.set(0);
-            t->set_desired_temperature(0.0);
-            // TODO: finish
-            double tmax_avg   = 0.0,
-                   tmin_avg   = 0.0,
-                   t_high_avg = 0.0,
-                   t_low_avg  = 0.0;
-            for (cycle = PID_AUTOTUNER_CYCLES - 3; cycle < PID_AUTOTUNER_CYCLES; cycle++)
-            {
-                tmax_avg   += cycles[cycle].t_max;
-                tmin_avg   += cycles[cycle].t_min;
-                t_high_avg += cycles[cycle].ticks_high;
-                t_low_avg  += cycles[cycle].ticks_low;
-            }
-            tmax_avg   /= (PID_AUTOTUNER_CYCLES - 1.0);
-            tmin_avg   /= (PID_AUTOTUNER_CYCLES - 1.0);
-            t_high_avg /= (PID_AUTOTUNER_CYCLES - 1.0);
-            t_low_avg  /= (PID_AUTOTUNER_CYCLES - 1.0);
-
-            s->printf("Averages over last %d cycles: Max: %5.1fc  Min: %5.1fc  high samples: %3.0f  low samples: %3.0f\n", 3, tmax_avg, tmin_avg, t_high_avg, t_low_avg);
-
-            // this code taken from http://github.com/ErikZalm/Marlin/blob/Marlin_v1/Marlin/temperature.cpp
-            double ku = (4.0 * d) / (3.141592653589 * (tmax_avg - tmin_avg) / 2.0);
-            double tu = (t_low_avg + t_high_avg) * (1000.0 / 20.0) / 1000.0;
-
-            s->printf("\tku: %g\n\ttu: %g\n", ku, tu);
-
-            double kp = 0.6 * ku;
-            double ki = 2 * kp / tu / 20.0;
-            double kd = kp * tu / 8.0;
-
-            s->printf("\tTrying:\n\tKp: %5.1f\n\tKi: %5.3f\n\tKd: %5.0f\n", kp, ki, kd);
-            // end code from Marlin Firmware
-
-            t->setPIDp(kp);
-            t->setPIDi(ki);
-            t->setPIDd(kd);
-
-            s->printf("PID Autotune Complete! The settings above have been loaded into memory, but not written to your config file.\n");
-
-            t = NULL;
-            s = NULL;
-
-            return;
-        }
-        s->printf("Cycle %d:\n\tbias: %4d d: %4d\n", cycle, bias, d);
-    }
-
-    int ticks;
-
-    if (output)
-    {
-        ticks = ++cycles[cycle].ticks_high;
-        //t->heater_pin.pwm((t->o = ((bias + d) >> 1)));
-        t->heater_pin.pwm(t->o = (bias + d)); // why use half PWM???
-    }
-    else
-    {
-        ticks = ++cycles[cycle].ticks_low;
-        t->heater_pin.set((t->o = 0));
-    }
-
-    if ((ticks % 16) == 0)
-    {
-        s->printf("%s: %5.1f/%5.1f @%d %d %d/8\n", t->designator.c_str(), t->last_reading, target_temperature, t->o, (output?1:0), cycle);
-    }
-
-    if (t->last_reading > cycles[cycle].t_max)
-        cycles[cycle].t_max = t->last_reading;
-
-    if (t->last_reading < cycles[cycle].t_min)
-        cycles[cycle].t_min = t->last_reading;
-
-    last_output = output;
-}
+#include "PID_Autotuner.h"
+#include "Kernel.h"
+#include <cmath>        // std::abs
+
+#define DEBUG_PRINTF s->printf
+
+PID_Autotuner::PID_Autotuner()
+{
+    t = NULL;
+    s = NULL;
+    lastInputs = NULL;
+    peaks = NULL;
+    tick = false;
+    tickCnt= 0;
+}
+
+void PID_Autotuner::on_module_loaded()
+{
+    tick = false;
+    this->kernel->slow_ticker->attach(20, this, &PID_Autotuner::on_tick );
+    register_for_event(ON_IDLE);
+    register_for_event(ON_GCODE_RECEIVED);
+}
+
+void PID_Autotuner::begin(TemperatureControl *temp, double target, StreamOutput *stream, int ncycles)
+{
+    noiseBand = 0.5;
+    oStep = temp->heater_pin.max_pwm(); // use max pwm to cycle temp
+    nLookBack = 5 * 20; // 5 seconds of lookback
+    lookBackCnt= 0;
+    tickCnt= 0;
+
+    if (lastInputs != NULL) delete[] lastInputs;
+    lastInputs = new float[nLookBack+1];
+    t = temp;
+    s = stream;
+
+    t->heater_pin.set(0);
+    t->target_temperature = 0.0;
+
+    target_temperature = target;
+    requested_cycles = ncycles;
+
+    if (peaks != NULL) delete[] peaks;
+    peaks = new float[ncycles];
+
+    for (int i = 0; i < ncycles; i++) {
+        peaks[i] = 0.0;
+    }
+
+    peakType = 0;
+    peakCount = 0;
+    justchanged = false;
+
+    double refVal = t->get_temperature();
+    absMax = refVal;
+    absMin = refVal;
+    output= oStep;
+    t->heater_pin.pwm(oStep); // turn on to start heating
+
+    s->printf("%s: Starting PID Autotune, %d max cycles, M304 aborts\n", t->designator.c_str(), ncycles);
+}
+
+void PID_Autotuner::abort()
+{
+    if (!t)
+        return;
+
+    t->target_temperature = 0;
+    t->heater_pin.set(0);
+    t = NULL;
+
+    if (s)
+        s->printf("PID Autotune Aborted\n");
+    s = NULL;
+
+    if (peaks != NULL)
+        delete[] peaks;
+    peaks = NULL;
+    if (lastInputs != NULL)
+        delete[] lastInputs;
+    lastInputs = NULL;
+}
+
+void PID_Autotuner::on_gcode_received(void *argument)
+{
+    Gcode *gcode = static_cast<Gcode *>(argument);
+
+    if ((gcode->has_m) && (gcode->m == 304))
+        abort();
+}
+
+uint32_t PID_Autotuner::on_tick(uint32_t dummy)
+{
+    if (t)
+        tick = true;
+    tickCnt += 1000/20; // millisecond tick count
+    return 0;
+}
+
+/**
+ * this autopid is based on https://github.com/br3ttb/Arduino-PID-AutoTune-Library/blob/master/PID_AutoTune_v0/PID_AutoTune_v0.cpp
+ */
+void PID_Autotuner::on_idle(void *)
+{
+    if (!tick)
+        return;
+
+    tick = false;
+
+    if (t == NULL)
+        return;
+
+    if(peakCount >= requested_cycles) {
+        finishUp();
+        return;
+    }
+
+    double refVal = t->get_temperature();
+
+    if (refVal > absMax) absMax = refVal;
+    if (refVal < absMin) absMin = refVal;
+
+    // oscillate the output base on the input's relation to the setpoint
+    if (refVal > target_temperature + noiseBand){
+        output= 0;
+        //t->heater_pin.pwm(output);
+        t->heater_pin.set(0);
+    } else if (refVal < target_temperature - noiseBand) {
+        output= oStep;
+        t->heater_pin.pwm(output);
+    }
+
+    bool isMax = true, isMin = true;
+
+    // id peaks
+    for (int i = nLookBack - 1; i >= 0; i--) {
+        float val = lastInputs[i];
+        if (isMax) isMax = refVal > val;
+        if (isMin) isMin = refVal < val;
+        lastInputs[i + 1] = lastInputs[i];
+    }
+
+    lastInputs[0] = refVal;
+
+    if (lookBackCnt < nLookBack) {
+        lookBackCnt++; // count number of times we have filled lastInputs
+        //we don't want to trust the maxes or mins until the inputs array has been filled
+        return;
+    }
+
+    if (isMax) {
+        if (peakType == 0) peakType = 1;
+        if (peakType == -1) {
+            peakType = 1;
+            justchanged = true;
+            peak2 = peak1;
+        }
+        peak1 = tickCnt;
+        peaks[peakCount] = refVal;
+
+    } else if (isMin) {
+        if (peakType == 0) peakType = -1;
+        if (peakType == 1) {
+            peakType = -1;
+            peakCount++;
+            justchanged = true;
+        }
+
+        if (peakCount < requested_cycles) peaks[peakCount] = refVal;
+    }
+
+    // we need to ignore the first cycle warming up from room temp
+
+    if (justchanged && peakCount > 2) {
+        if(peakCount == 3) { // reset min to new min
+            absMin= refVal;
+        }
+        //we've transitioned. check if we can autotune based on the last peaks
+        float avgSeparation = (std::abs(peaks[peakCount - 1] - peaks[peakCount - 2]) + std::abs(peaks[peakCount - 2] - peaks[peakCount - 3])) / 2;
+        s->printf("Cycle %d: max: %g, min: %g, avg separation: %g\n", peakCount, absMax, absMin, avgSeparation);
+        if (peakCount > 3 && avgSeparation < 0.05 * (absMax - absMin)) {
+            DEBUG_PRINTF("Stabilized\n");
+            finishUp();
+            return;
+        }
+    }
+
+    justchanged = false;
+
+    if ((tickCnt % 1000) == 0) {
+        s->printf("%s: %5.1f/%5.1f @%d %d/%d\n", t->designator.c_str(), t->get_temperature(), target_temperature, output, peakCount, requested_cycles);
+        DEBUG_PRINTF("lookBackCnt= %d, peakCount= %d, absmax= %g, absmin= %g, peak1= %lu, peak2= %lu\n", lookBackCnt, peakCount, absMax, absMin, peak1, peak2);
+    }
+}
+
+
+void PID_Autotuner::finishUp()
+{
+    //we can generate tuning parameters!
+    double Ku = 4*(2*oStep)/((absMax-absMin)*3.14159);
+    double Pu = (double)(peak1-peak2) / 1000;
+    s->printf("\tKu: %g, Pu: %g\n", Ku, Pu);
+
+    double kp = 0.6 * Ku;
+    double ki = 1.2 * Ku / Pu;
+    double kd = Ku * Pu * 0.075;
+
+    s->printf("\tTrying:\n\tKp: %5.1f\n\tKi: %5.3f\n\tKd: %5.0f\n", kp, ki, kd);
+
+    t->setPIDp(kp);
+    t->setPIDi(ki);
+    t->setPIDd(kd);
+
+    s->printf("PID Autotune Complete! The settings above have been loaded into memory, but not written to your config file.\n");
+
+
+    // and clean up
+    t->target_temperature = 0;
+    t->heater_pin.set(0);
+    t = NULL;
+    s = NULL;
+
+    if (peaks != NULL)
+        delete[] peaks;
+    peaks = NULL;
+
+    if (lastInputs != NULL)
+        delete[] lastInputs;
+    lastInputs = NULL;
+}
index 705ee82..bc0e715 100644 (file)
@@ -1,3 +1,7 @@
+/**
+ * Based on https://github.com/br3ttb/Arduino-PID-AutoTune-Library
+ */
+
 #ifndef _PID_AUTOTUNE_H
 #define _PID_AUTOTUNE_H
 
 #include "TemperatureControl.h"
 #include "StreamOutput.h"
 
-#define PID_AUTOTUNER_CYCLES 8
-
 class PID_Autotuner : public Module
 {
 public:
-             PID_Autotuner();
-    void     begin(TemperatureControl*, double, StreamOutput*, int cycles= 8);
+    PID_Autotuner();
+    void     begin(TemperatureControl *, double, StreamOutput *, int cycles = 8);
     void     abort();
 
     void     on_module_loaded(void);
     uint32_t on_tick(uint32_t);
-    void     on_idle(void*);
-    void     on_gcode_received(void*);
-
-    TemperatureControl *t;
+    void     on_idle(void *);
+    void     on_gcode_received(void *);
 
-    double target_temperature;
+private:
+    void finishUp();
 
-    int cycle;
-    bool output;
-    bool last_output;
+    TemperatureControl *t;
+    float target_temperature;
     StreamOutput *s;
 
     volatile bool tick;
 
-    struct {
-        double t_max;
-        double t_min;
-        int ticks_low;
-        int ticks_high;
-    } cycles[PID_AUTOTUNER_CYCLES];
-
-    int bias, d;
+    float *peaks;
+    int requested_cycles;
+    float noiseBand;
+    unsigned long peak1, peak2;
+    int sampleTime;
+    int nLookBack;
+    int lookBackCnt;
+    int peakType;
+    float *lastInputs;
+    int peakCount;
+    bool justchanged;
+    float absMax, absMin;
+    float oStep;
+    int output;
+    unsigned long tickCnt;
 };
 
 #endif /* _PID_AUTOTUNE_H */