1
\$\begingroup\$

I am trying to figure out how to reproduce closed-loop systems in Arduino that were designed initially designed in Simulink.

Take the following closed-loop system with unity feedback and gain:

enter image description here

Where Gp(z) is a discrete transfer function, taking the form of:

\$Gp(z) = \frac{0.1288z - 0.06234}{z^2 - 0.7813z + 0.1817}\$

The closed-loop system was replicated in Arduino with the following code:

int iteration(0);
double y(0), e(0);
double Gp_y_0(0), Gp_y_1(0), Gp_y_2(0), Gp_x_0(0), Gp_x_1(0), Gp_x_2(0);

void setup() {
  Serial.begin(115200);
}

double Gp_z (double Gp_x_0) {
  Gp_y_0 = (0.128767938742003 * Gp_x_1) + (-0.062336933973903 * Gp_x_2) - (-0.781272292546052 * Gp_y_1) - (0.181746970098960 * Gp_y_2);
  Gp_x_2 = Gp_x_1;
  Gp_x_1 = Gp_x_0;
  Gp_y_2 = Gp_y_1;
  Gp_y_1 = Gp_y_0;
  return Gp_y_0;
}

void loop() {
  delay(500);
  e = 20.0 - y;
  y = Gp_z(e);
  Serial.print(iteration); Serial.print('\t');
  Serial.print(e, 9); Serial.print('\t');
  Serial.print(y, 9);
  Serial.println();
  iteration++;
}

The plant, Gp, is implemented correctly in Arduino. I tested its open-loop response was tested with a step input - the values acquired matched MATLAB with 12 digits of precision.

However, when I close the loop, I notice that after the first iteration the output is different than the output in Simulink. Arduino shows more oscillations in the step response than shown in Simulink. I am convinced that the reasoning for this is order of operations. My goal is to match the output of Arduino with the simulations I am getting from Simulink - I just can't seem to match them.

Here is the first 10 samples (Interation#, Error, Output):

Arduino

0   20.000000000    0.000000000
1   20.000000000    2.575358775
2   17.424641225    3.340676550
3   16.659323450    3.138910827
4   16.861089173    2.904174249
5   17.095825751    2.831139893
6   17.168860107    2.834391925
7   17.165608075    2.844978145
8   17.155021855    2.847686323
9   17.152313677    2.846717691

Simulink

0  20.000000000000000                   0
1  17.424641225159935   2.575358774840067
2  16.990947091447396   3.009052908552605
3  17.015483465319324   2.984516534680677
4  17.083280988271159   2.916719011728840
5  17.124589277883938   2.875410722116062
6  17.143447412039610   2.856552587960392
7  17.150819802325874   2.849180197674125
8  17.153378468604828   2.846621531395171
9  17.154167672101366   2.845832327898634

By looking into the numbers, it may not seem like a big difference but when designing a controller, its VERY significant, almost making Simulink design tools useless when implementing into Arduino.

Thank you,

EDIT: Clarification for Gp_z() function in Arduino. To implement a discrete transfer function, such as Gp(z) into Arduino. I performed an inverse Z-transform on Gp(z):

  1. Multiply Gp(z) by \$\frac{z^{-2}}{z^{-2}}\$:

\$\frac{Y(z)}{X(z)} = \frac{0.1288z^{-1} - 0.06234z^{-2}}{1 - 0.7813z^{-1} + 0.1817z^{-2}}\$

  1. Isolate Y(z):

\$Y(z) = X(z)0.1288z^{-1} - X(z)0.06234z^{-2} + Y(z)0.7813z^{-1} - Y(z)0.1817z^{-2}\$

  1. Inverse Z-transform:

\$y[n] = 0.1288x[n-1] - 0.06234x[n-2] + 0.7813y[n-1] - 0.1817y[n-2]\$

This formula was implemented in Gp_z() function.

UPDATE: By switching the execution order of e and y in Arduino:

void loop() {
  delay(500);
  y = Gp_z(e);
  e = 20.0 - y;
  Serial.print(iteration); Serial.print('\t');
  Serial.print(e, 9); Serial.print('\t');
  Serial.print(y, 9); Serial.print('\t');
  Serial.println();
  iteration++;
}

Here is the resulting output:

0   20.000000000    0.000000000 
1   20.000000000    0.000000000 
2   17.424641225    2.575358775 
3   16.659323450    3.340676550 
4   16.861089173    3.138910827 
5   17.095825751    2.904174249 
6   17.168860107    2.831139893 
7   17.165608075    2.834391925 
8   17.155021855    2.844978145 
9   17.152313677    2.847686323 

There is an extra iteration of initial conditions and the numbers look better but still with significant overshoot in comparison to Simulink. I don't believe this to be the solution to the problem.

\$\endgroup\$
  • 2
    \$\begingroup\$ How can the error still be 20.000000000 in the 2nd line of the arduino result? It looks like you are "one step" out in part of your implementation thus gradually throwing the rest of the numbers out. \$\endgroup\$ – Andy aka Aug 10 '17 at 16:44
  • \$\begingroup\$ Arduino step 1 is wrong : the Error associated with step 1 (17.42) is presented in step 2 (alongside Step 2's output not Step 1's) and it all goes downhill after that. \$\endgroup\$ – Brian Drummond Aug 10 '17 at 16:52
  • \$\begingroup\$ @Andyaka Yes, this is was I was trying to say in the original question. However, I think Arduino is correct here, because if the output is 0.000 in Step 0 than the error should remain 20.00 in Step 1. It makes sense to me - I hope I am wrong though. \$\endgroup\$ – gelman_grad Aug 10 '17 at 16:58
  • \$\begingroup\$ @DannyGelman column 3 plus column 2 should equal the step input of 20. Clearly and unambiguously this doesn't happen in step 2 for the Arduino. \$\endgroup\$ – Andy aka Aug 10 '17 at 17:02
  • \$\begingroup\$ @Andyaka That makes a lot of sense. So what am I doing wrong in my Arduino code? I am positive that I implemented Gp(z) properly. \$\endgroup\$ – gelman_grad Aug 10 '17 at 17:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.