[Solved] 2 questions - functions outside loop; using arrays as vectors

Hi, it's me again, with a couple of questions :slight_smile:

First question: If I have functions outside setup() and loop(), to be called in loop() depending on the value of a variable (I am intending to use if statements for this), do these functions do anything while they are not in use? I don't want to give my Arduino more work than it has to do. To expand, I wish to have several discrete-time controllers in a sketch, with the user able to choose a controller. So if I had the calculations of each controller in their own functions, outside loop() e.g.

void P() { //or would this need to be double?
e = r - y;
u = kp*e;

would these functions be inactive and not do any working while another controller has been selected?

In loop() I would have things common to all the controllers, such as reading an analog pin, converting this value to volts, converting the control signal u to an integer and analogWriting this value.

My second question: is it possible to use arrays as vectors? I am trying to convert a Simulink model into code (Simulink never builds successfully and I have my own code to add anyway!) and in this controller there are 2-element vectors being multiplied, using the dot (scalar) and cross (vector) products in different situations. If I put the two elements into an array could I then pretend it's a vector, and calculate the dot and cross products using a little formula?

I'd really appreciate any help, as I'm going to start coding today and it would be nice to know if I'm on the right track before I've finished :slight_smile:

Thanks,
+-

would these functions be inactive and not do any working while another controller has been selected?

They would not be doing anything unless called.

is it possible to use arrays as vectors?

Yes.

PaulS:

would these functions be inactive and not do any working while another controller has been selected?

They would not be doing anything unless called.

is it possible to use arrays as vectors?

Yes.

Thank you very much Paul, I might as well lock the topic now! :wink:

PaulS:
Yes.

Sorry, I have another question! For the controller functions, would they all be void as in my little snippet of code, or would I have to specify a data type (presumably double) and then return the control signal u at the end?

Sorry, I have another question! For the controller functions, would they all be void as in my little snippet of code, or would I have to specify a data type (presumably double) and then return the control signal u at the end?

That's up to you to decide. The snippet you posted seems to use all global variables, so there is no need to return anything. Not the best design, but it works.

PaulS:

Sorry, I have another question! For the controller functions, would they all be void as in my little snippet of code, or would I have to specify a data type (presumably double) and then return the control signal u at the end?

That's up to you to decide. The snippet you posted seems to use all global variables, so there is no need to return anything. Not the best design, but it works.

Hmm, in that case, what would you suggest? Would you rather give each controller its own set of variables?

For a more "black box" approach it is better to pass data into functions, and return a result. You don't have to do it that way, but that tends to give a neater result. The type of data depends on the application. For example, if you want decimal places you use the float type.

I don't want to sound snippy, but if you search for "C tutorial" and read up on some of the basics, you will find a lot of your questions answered.

I see, that sounds better. I take it you mean something like this:

long map(long x, long in_min, long in_max, long out_min, long out_max)
{
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

?

I will have to think about how I can code my controllers in a form like that. As a first idea, I'd have separate functions for say... calculating the error, and then one (for each controller) to calculate the control signal, is that a better approach to take? And yes, I'm getting a little confused with data types at the moment, but hopefully I will be able to make sense of everything after I get some sleep!

I've read through various tutorials for other things, but with these particular questions I wasn't exactly sure what to look for...

Thanks for your reply! :slight_smile:

plusminus_:
I see, that sounds better. I take it you mean something like this:

long map(long x, long in_min, long in_max, long out_min, long out_max)

{
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}


?

Yes, that's the right idea (although there is an existing function called map, but NVM that).

Yes, I took it from the map Reference page as an example. It's settled then. I will rewrite my code today!

Hello, I've just finished coding my simple controllers and I'd like to know if there's anything I can do to improve it. I am still using arrays (because I don't want too many words e.g. previous_error, previous_output...), but inside the functions for each controller, rather than as global variables.

/***** Controllers: P, P+I, P+DFB, PID *****/
float rVel = 20.00; //reference signal, % of speed
int rPos = 150; //reference signal, degrees
// I think r would be inside the TWS variables function

int delta = 20; // Sample time, in milliseconds
float kp = 34.125;
float ki = 12.2;
float kd = 0.1;
int y_read;
float u;
int u_write;
const int velocityPin = A0;
const int positionPin = A2;
int inputPin; // I could use this to choose which pin to use as input
const int outputPin = 5;

void setup() {
  pinMode(velocityPin, INPUT);
  pinMode(positionPin, INPUT);
  pinMode(outputPin, OUTPUT);
  inputPin = velocityPin; // This would need to change
}

// Float version of map() - I don't think I will need this working with mV
//float fmap(float x, float in_min, float in_max, float out_min, float out_max) {
//  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
//}

// Converting reference values
int rVel_mv = map(rVel, -100.00, 100.00, 0, 5000); // % to mV
int rPos_mv = map(rPos, -180, 180, 0, 5000); // degrees to mV

// Initialise reference value to be used
int r_mv;

// Controller functions
int err(int ref_mv, int output_mv) {
  return ref_mv - output_mv;
}

float P(float gain, int ref_mv, int output_mv) {
  int error = err(ref_mv, output_mv);
  
  return gain*error; // = u
}

float PplusI(float Pgain, float Igain, int ref_mv, int output_mv, int SampleTime) {
  float u[3]; // See below
  int e[3]; // e[0] = current error, e[1] = previous error
  e[0] = err(ref_mv, output_mv);
  u[0] = u[1] + Pgain*e[0] - (Pgain - Igain*SampleTime)*e[1];
  u[1] = u[0]; // previous u = current u
  e[1] = e[0]; // previous error = current error
  
  return u[0];
}

float PplusDFB(float Pgain, float Dgain, int ref_mv, int output_mv, int SampleTime) {
  int e[3];
  int y[3]; // y[0] = current output, y[1] = previous output 
  e[0] = err(ref_mv, output_mv);
  y[0] = output_mv;
  u = Pgain*e[0] - (Pgain*Dgain/SampleTime)*(y[0] - y[1]);
  y[1] = y[0];
  
  return u;
}

float PID(float Pgain, float Igain, float Dgain, int ref_mv, int output_mv, int SampleTime) {
  int e[4]; // error: e[0] = e(k), e[1] = e(k-1), e[2] = e(k-2)
  float u[3];
  e[0] = err(ref_mv, output_mv);
  u[0] = u[1] + Pgain*(e[0] - e[1]) + Igain*SampleTime*e[0] + (Dgain/SampleTime)*(e[0] - 2*e[1] + e[2]);
  u[1] = u[0];
  e[2] = e[1];
  e[1] = e[0];
  
  return u[0];
}
  

void loop() {
  // Choose which variable to use as a reference value
  if (inputPin == velocityPin) {
    r_mv = rVel_mv;
  }
  else if (inputPin == positionPin) {
    r_mv = rPos_mv;
  }
  
  // Read output
  y_read = analogRead(inputPin);
  int y_mv = map(y_read, 0, 1024, 0, 5000); // convert to mV
  
  // Controllers: choose one
//  u = P(kp, r_mv, y_mv);
//  u = PplusI(kp, ki, r_mv, y_mv, delta);
//  u = PDFB(kp, kd, r_mv, y_mv, delta);
//  u = PID(kp, ki, kd, r_mv, y_mv, delta);
  
  u_write = map(u, -5000, 5000, 0, 256); // convert mV to [0,255]
  // Why 256? http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1266045058/22#22
  
  // Limit output
  if (u_write > 255) {
    u_write = 255;
  }
  else if (u_write < 0) {
    u_write = 0;
  }
  
  analogWrite(outputPin, u_write); // PWM output
}

+-

float PplusI(float Pgain, float Igain, int ref_mv, int output_mv, int SampleTime) {
  float u[3]; // See below
  int e[3]; // e[0] = current error, e[1] = previous error
  e[0] = err(ref_mv, output_mv);
  u[0] = u[1] + Pgain*e[0] - (Pgain - Igain*SampleTime)*e[1];
  u[1] = u[0]; // previous u = current u
  e[1] = e[0]; // previous error = current error
  
  return u[0];
}

Why bother calculating e [ 1 ] = e [ 0 ] when you discard it? Ditto for u [ 1 ].

Oh, I thought that would store the current error as the previous error... Does it get discarded because I'm initialising the arrays at the beginning of the function?

It gets discarded because they are local variables to the function and cease to exist when you exit the function.

How did I not see that? :stuck_out_tongue:

I will move the arrays out of the function then, otherwise I will not be able to store previous values.