PID square wave set-point (Blink without Delay adaptation)

Hi all,

I’m using the PID library to control the position of the fingers of a prosthetic/robotic hand project.
I have 5 simultaneous PID loops running, my example code below has had four of them removed for clarity.

I have had all loops functioning as expected with static set-points, now I’d like to vary the set-point in a square wave (ie 4 seconds at 0, 4 seconds at XXX, repeated).

I have (attempted to…) adapt the ‘Blink without Delay’ sketch to set change the set-point of one of the PIDs every so often just by changing the variables to what I want to control, and no luck.

As the code is now the serial monitor shows that variableSetpoint remains at zero all the time, and the indexInput is updated only once every ‘interval’ length (every two seconds).

Any help is much appreciated.

The relevant part:

void loop()
{
  //pid-related code
   
 
  
  // check to see if it's time to change the Setpoint; that is, if the
  // difference between the current time and last time you changed
  // the SP is bigger than the interval at which you want to
  // Change.

  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    // save the last time you changed the SP
    previousMillis = currentMillis;

    // if the SP is low (0) turn it on and vice-versa:
    if (variableSetpoint == 0) {
      variableSetpoint== 200;
    } else {
      variableSetpoint == 0;
    }
  indexInput = analogRead(PIN_INPUT_INDEX);
  indexPID.Compute();
  analogWrite(PIN_OUTPUT_INDEX, indexOutput);
  }

The entire code (except the Processing.org Communication code)

/******************************************************************
 * PID Simple Example (Augmented with Processing.org Communication)
 * Version 0.3
 * by Brett Beauregard
 * License: Creative-Commons Attribution Share-Alike
 * April 2011
 ******************************************************************/
 
/* 6 PID loops to control five SMA wire actuators and one cooling fan
*/
#include <PID_v1.h>

//Define Variables we'll be connecting to
double indexSetpoint, indexInput, indexOutput;      //index variables
double variableSetpoint;

//define analog read inputs from position sensors (20k slide pots with 15mm travel)
#define PIN_INPUT_INDEX 3

double SampleTime =50; // new PID sample time (default is 200ms, need to reduce this)

//define PWM outputs to SMA actuators
#define PIN_OUTPUT_INDEX 9

double Ki=0.1, Kp=10, Kd=0;    //tuning parameters for all PID loops

//Specify the links and initial tuning parameters for each PID controller
PID indexPID(&indexInput, &indexOutput, &variableSetpoint,Ki,Kp,Kd, DIRECT);

/// define setpoint square set wave timer stuff
const long interval = 2000;  //frequency for setpoint square wave
unsigned long currentMillis = 0;    // stores the value of millis() in each iteration of loop()
unsigned long previousMillis = 0; // will store the last time when the setpoint was updated

unsigned long serialTime; //this will help us know when to talk with processing

void setup()
{
  //initialize the serial link with processing
  Serial.begin(9600);
  
  //set the new sample times 
  indexPID.SetSampleTime(SampleTime);
  
  
 
   //indexSetpoint = 100;   //define Static setpoints
   variableSetpoint = 0;
   
   
  //initialize the variables we're linked to
  indexInput = analogRead(PIN_INPUT_INDEX);


  //turn the PIDs on
  indexPID.SetMode(AUTOMATIC);
}

void loop()
{
  //pid-related code
 
  // check to see if it's time to change the Setpoint; that is, if the
  // difference between the current time and last time you changed
  // the SP is bigger than the interval at which you want to
  // Change.
  double variableSetpoint = 0;
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    // save the last time you changed the SP
    previousMillis = currentMillis;

    // if the SP is low (0) turn it on and vice-versa:
    if (variableSetpoint == 0) {
      variableSetpoint== 200;
    } else {
      variableSetpoint == 0;
    }
  indexInput = analogRead(PIN_INPUT_INDEX);
  indexPID.Compute();
  analogWrite(PIN_OUTPUT_INDEX, indexOutput);
  }
  
  
  //send-receive with processing if it's time (For real time graphing of PID)
  if(millis()>serialTime)
  {
    SerialReceive();
    SerialSend();
    serialTime+=10;
  }
  
  
  
  
}

not sure if this will help but its a example for a dual millis.

interval_1 and interval_2 can be changed in the code.

const byte led = 13;
unsigned long previousMillis_1 = 0;
unsigned long previousMillis_2 = 0;
unsigned long interval_1 = 100;
unsigned long interval_2 = 400;
byte tickTock = 0;
void setup() {

  pinMode(led, OUTPUT);
}

void loop() {

  unsigned long currentMillis = millis();
  
  //using a flag called tickTock to block the timer if from running
  if ((currentMillis - previousMillis_1 >= interval_1) && (tickTock == 0)) {
    tickTock = 1;
    previousMillis_1 = currentMillis;
    previousMillis_2 = currentMillis;
  }
  if ((currentMillis - previousMillis_2 >= interval_2) && (tickTock == 1)) {
    tickTock = 0;
    previousMillis_1 = currentMillis;
    previousMillis_2 = currentMillis;
  }
  if (tickTock == 1) {
    digitalWrite(led, HIGH);//on board led
  } else {
    digitalWrite(led, LOW);
  }

//if (something==1){
 // interval_1=50; // you can chage the interval anywhere you like in the code
  //interval_2=200;// as interval is global it will stay changed 
  //}
}
if (variableSetpoint == 0) {
  variableSetpoint== 200;
}

You don't change the variable variableSetpoint, you do another compare. Use a single = to assign a new value ;)

The relevant part:

isn’t the relevant part, and has the bug noted above. Post ALL of your code.

Using PID, then calling Update only every two seconds is completely pointless, unless the thing you're controlling is moving at extremely low speed (as in barely moving at all...).

I suggest you do enough reading to understand HOW a PID actually works, so you understand how to use it properly. The code as it stands makes no sense at all.

Regards, Ray L.

double variableSetpoint;

  double variableSetpoint = 0;

Having global and local variables, with the PID instance bound to one and the other being valued in loop() is such a bad idea that I stopped reading at that point.

septillion:

if (variableSetpoint == 0) {

variableSetpoint== 200;
}




You don't change the variable variableSetpoint, you do another compare. Use a single = to assign a new value ;)

Thank you! Now the set-point is changing as it should.

PaulS:
isn’t the relevant part, and has the bug noted above. Post ALL of your code.

The second section shows all my code. Or do you mean the proccessing.org section and the PID library?

RayLivingston:
Using PID, then calling Update only every two seconds is completely pointless, unless the thing you’re controlling is moving at extremely low speed (as in barely moving at all…).

I suggest you do enough reading to understand HOW a PID actually works, so you understand how to use it properly. The code as it stands makes no sense at all.

Regards,
Ray L.

I have a reasonable understanding of how PID control works, I have done a control theory paper and understand the math. What I don’t understand, evidently, is code, which is why I’m here.

Obviously I’d like the PID to run as often as it did before I started trying to vary the setpoint; every 50ms which I set by;

double SampleTime =50; // new PID sample time (default is 200ms, need to reduce this)
 //set the new sample times 
indexPID.SetSampleTime(SampleTime);

PaulS:

double variableSetpoint;

double variableSetpoint = 0;



Having global and local variables, with the PID instance bound to one and the other being valued in loop() is such a bad idea that I stopped reading at that point.

Thanks for your input.

After some trial and error it seems the solution is as simple as moving one brace. In the original ‘blink without delay’ code digitalwrite function was inside the if else loop so that’s what I did - That was wrong for my use and it should be outside as below;

  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    // save the last time you changed the SP
    previousMillis = currentMillis;

    // if the SP is low (0) turn it on and vice-versa:
    if (variableSetpoint == 0) {
      variableSetpoint= 200;
    } else {
      variableSetpoint = 0;
    }

} // new brace here

  indexInput = analogRead(PIN_INPUT_INDEX);
  indexPID.Compute();
  analogWrite(PIN_OUTPUT_INDEX, indexOutput);

    //  } Brace removed from here

Now it functions as intended, that is, the PID runs every 50ms and the setpoint changes every few seconds (length of ‘interval’ ) :slight_smile:

The second section shows all my code.

I missed that. I, and many others, prefer that you not do that. Post ALL of your code. We can figure out for ourselves what the relevant section of the code is.