3 Maxima Control Engineering Package COMA ("COntrol engineering with MAxima")
8 /* Wilhelm Haager, 2019-06-14 (wilhelm.haager@htlstp.ac.at) */
17 /* The list "coma_defaults" holds default options (mainly graphic options)
18 for COMA functions in the style of an associative array: */
24 /* Plotting FOUR functions with ONE variable each: */
26 plot([sin(5*x)**2,0.8*sin(5*y)**2,x,0.8*y],xrange=[-0.5,1.5],
27 color=[red,green,brown,blue],line_type=[solid,solid,dots,dots])$
31 /* Plotting TWO functions with TWO variables each: */
33 plot([sin(5*x)**2+0.8*sin(5*y)**2,x+0.8*y],
34 xrange=[-0.5,1.5],surface_hide=true,color=[red,green,brown,blue])$
38 /* Plotting the damping of a transfer function with respect to the parameter "a":
39 As the function "damping" requires a transfer function with numeric coefficients, it has to be quoted. */
41 f:(s+a)/(s^3+a*s^2+2*s+a);
43 plot('damping(f),xrange=[0,5]);
45 plot('damping(f),logx=true,logy=true,xrange=[0.05,50]);
49 /* Isolines of the damping of a transfer function with respect to two parameters "a" and "b": */
51 f:1/(s^5+s^4+6*s^3+a*s^2+b*s+1);
53 contourplot('damping(f),a,b,
54 xrange=[0,10], yrange=[0,10],
55 contours=[0.10,0.05,0,-0.05,-0.1,-0.15],
56 color=[green,green,black,red,red,red])$
60 /* Electrical Engineering Operators and Functions*/
62 /* Parallel connection of impedances: */
67 /* Complex quantities in polar coordinates with the cis-Operator "/_" for input and the postfix operator "//_" as well as the function "cisform" for output: */
83 /* List with values of a noisy sine: */
85 sinli:makelist(float(sin(t)-0.1+random(0.2)),t,-%pi/10,21/10*%pi,%pi/60)$
90 sinlis:moving_average(sinli,15)$
92 plot([points(sinli),points(sinlis)],point_type=0,points_joined=true,
96 /* Regression Analysis */
101 pli:[[0,0.2],[2,0.5],[4,1.4],[5,2],[6,3],[7,4.5],[8,6.5]];
105 /* Polynomial Regression: */
107 p:regan(pli,[1,x,x**2,x**3]);
109 plot([points(pli),p],point_type=7,points_joined=false,
110 xrange=[0,10],yrange=[-1,10]);
114 /* Laplace-Transform, Step Response*/
117 /* Laplace transform and inverse Laplace transform are part of Maxima: */
119 laplace(t^2*sin(a*t),t,s);
121 ilt(1/(s^3+2*s^2+2*s+1),s,t);
123 ilt(1/((s+a)^2*(s+b)),s,t);
127 /* Inverse Laplace transform fails at higher order transfer functions: */
129 ilt(1/(s^3+2*s^2+3*s+1),s,t);
133 /* nilt calculates inverse Laplace transform with numerically calculated poles. As the result is valid only for t>=0. If unit_step_included is set to "true" (default), it is multiplied with the unit step function: */
135 nilt(1/(s^3+2*s^2+3*s+1),s,t);
137 nilt(1/(s^3+2*s^2+3*s+1),s,t),unit_step_included=false;
141 /* nilt tries to perform the back-transformation analytically, unless the variable try_symbolic_ilt is set to false; the additional parameters for the Laplace variable and the time variable can be omitted (if assumed to be "s" and "t", respectively): */
143 nilt(1/(s^3+2*s^2+2*s+1));
145 nilt(1/(s^3+2*s^2+2*s+1)),try_symbolic_ilt=false;
149 /* List of second order systems: */
151 pt2li:create_list(1/(s^2+2*d*s+1), d,
152 [0.0001,0.1,0.2,0.3,0.4,0.5]);
156 /* Unit step responses of the second order systems: */
158 step_response(pt2li)$
162 /* Response on a piecewise defined input function: */
164 u:5*unit_step(t)-5*unit_step(t-1)+5*unit_step(t-1.5)-5*unit_step(t-2);
166 plot(u,xrange=[-1,5],yrange=[-2,6]);
174 plot([u,x],xrange=[-1,5],yrange=[-2,6]);
176 response([explicit(u,t,-1,5),X],xrange=[-1,5],yrange=[-2,6]);
180 /* Frequency Response*/
183 /* Magintude plots of second order systems: */
185 magnitude_plot(pt2li, yrange=[0.1,10])$
189 /* Magnitude plots with linear scale: */
191 magnitude_plot(pt2li,logy=false,yrange=[0,5])$
195 /* Magnitude plot scaled in dB: */
197 magnitude_plot(pt2li,logy=dB,yrange=[-20,20]);
201 /* Phase plots of second order systems: */
207 /* Bode plot (magnitude plot and phase plot together: */
209 bode_plot(pt2li,yrange=[[0.1,10],[-180,0]])$
213 /* Asymptotic characteristics */
215 f:ratsimp(2*(1+1/(5*s)+s/(1+0.1*s)));
217 bode_plot([f,asymptotic(f)],yrange=[[0.1,100],[-120,120]]);
221 /* Nyquist plots (frequency response locus) of second order systems: */
223 nyquist_plot(pt2li, xrange=[-3,3], yrange=[-5,1],
224 dimensions=[500,500], nticks=2000)$
230 nichols_plot(pt2li,xrange=[-200,20],yrange=[0.01,10]);
234 /* Calculation of gain and phase in degrees of a frequency response: */
236 F:5*(1+5*s)/((1+3*s)*(1+s));
244 /* Investigations in the s-Plane*/
246 fli:makelist(stable_rantranf(5),k,1,2);
250 /* Calculating the zeros and poles of a list of transfer functions: */
258 /* Zeros and poles of a list of transfer functions in the complex plane: */
260 poles_and_zeros(fli)$
264 /* Calculation of closed loop transfer functions: */
266 fli:closed_loop(makelist(k*((s-a)*(s+1))
267 /(s*(s-2)*(s+7)),a,-11,-8));
271 /* Root locus plots with respect to the open loop gain of a list
272 of transfer functions in the complex plane: */
274 root_locus(fli,xrange=[-17,3],
275 yrange=[-6,6],nticks=5000)$
279 /* Root locus plots with respect to the damping ratio of a
280 second order of transfer function: */
282 root_locus(1/(s**2+a*s+1),xrange=[-4,1],
283 trange=[1e-4,3],nticks=5000);
287 /* Transfer Functions:*/
290 /* Random Transfer Functions */
293 /* List of transfer functions with random coefficients: */
295 fli:makelist(tf(random,3),k,1,4);
299 /* Checking the stability of the transfer functions: */
305 /* List of stable transfer functions with random coefficients: */
307 fli:makelist(tf(random,stable,3),k,1,4);
311 /* Checking the stability of the transfer functions: */
317 /* Random normalized transfer function: */
319 tf(random,stable,normalized,7);
326 /* Butterworth filter: */
328 poles_and_zeros(tf(butterworth,5));
330 step_response(makelist(tf(butterworth,k,2),k,3,10),xrange=[0,10],yrange=[0,1.3]);
336 poles_and_zeros(tf(bessel,5));
338 step_response(makelist(tf(bessel,k,2),k,3,10),xrange=[0,10],yrange=[0,1.3]);
342 /* Cebychev filter */
344 poles_and_zeros(tf(chebychev,5));
346 step_response(makelist(tf(chebychev,k,2),k,3,10),xrange=[0,10],yrange=[0,1.3]);
348 magnitude_plot(makelist(tf(chebychev,k,2),k,3,10),yrange=[0.01,10]);
350 magnitude_plot(makelist(tf(chebychev,6,2,eps),eps,0.1,0.5,0.1),yrange=[0.5,5]);
354 /* PTn with poles on a semicircle (similar to Bessel filter), but poles confined to an angle (e.g.30 degrees): */
356 poles_and_zeros([tf(ptn,10,1,30),
357 parametric(r*cos(150*%pi/180),r*sin(150*%pi/180),r,0,1.2),
358 parametric(r*cos(-150*%pi/180),r*sin(-150*%pi/180),r,0,1.2)],
359 yrange=[-1,1],xrange=[-2,1],color=[red,gray,gray]);
363 /* Basic control system elements */
365 [tf(pt1),tf(dt1),tf(pi),tf(pd)];
367 [tf(pt2),tf(pt2,2,1,5),tf(pt2,prod),tf(pt2,prod,2,3,4)];
369 [tf(pid),tf(pid,prod)];
371 [tf(pidt1),tf(pidt1,prod)];
375 /* List with open loop transfer functions: */
377 fo:[k/s,5/(s*(s+3)),1-b/s];
381 /* Calculation of the closed loop transfer functions: */
387 /* Determining the types of the transfer functions: */
397 /* Various transfer functions */
399 [tf(generic),tf(generic,3,5,a,b)];
403 /* Transfer function of an impedance chain with series resistances and traverse capacitors: */
405 impedance_chain(R,1/(s*C),4);
409 /* Fifth order Pade approximation of a time delay: */
415 /* Transfer function with coefficients optimized according to the ISE-criterion: */
417 [tf(ise,5),tf(ise,5,2)];
419 step_response([tf(ise,5),tf(ise,5,2)]);
423 /* Canonical forms of transfer functions */
441 /* Stability Behaviour*/
444 /* Fifth order transfer function and closed loop transfer function (with additional open loop gain k): */
446 f:(4*s^2+9*s+6)/(2*s^5+6*s^4+9*s^3+10*s^2+5*s+1);
452 /* Stability limit with respect to the open loop gain k: */
454 lim:stability_limit(fw,k);
458 /* Calculation of the stability using the Hurwitz criterion: */
460 ratsimp(hurwitz(denom(fw)));
464 /* List of three transfer functions: below, at and above the stability limit: */
466 kli:ev([1.1*k,k,0.9*k],lim,eval,numer);
468 foli:create_list(k*f,k,kli);
470 fwli:closed_loop(foli)$
474 /* Checking the stability using the predicate function "stablep": */
478 step_response(fwli,xrange=[0,50])$
484 /* Calculation of the gain crossover frequencies: */
486 float(gain_crossover(foli));
490 /* Calculation of the phase margin (unstable closed loop systems have a negative value): */
496 /* Calculation of the phase crossover frequencies: */
498 phase_crossover(foli);
502 /* Calculation of the gain margin (unstable closed loop systems have a value less than one): */
508 /* Calculation of the (absolute) damping (unstable systems have a negative value): */
514 /* Calculation of the (relative) damping (i.e. damping ratio),
515 unstable systems have a negative value: */
521 /* List of transfer functions with two symbolic coefficients a and b
522 and another varying coefficient: */
524 fli:makelist(1/(s^5+3*s^4+k*s^3+a*s^2+b*s+1),k,2,6);
528 /* Graphical representation of the stability limit with respect to the two coefficients a and b: */
530 stable_area_outlined(fli,a,b,xrange=[0,20],yrange=[0,10]);
532 stable_area([fli[1],fli[2]],a,b,xrange=[2,10],yrange=[0,3]);
534 unstable_area([fli[1],fli[2]],a,b,xrange=[2,10],yrange=[0,3]);
538 /* Controller Design*/
541 /* Transfer function of a plant to be controlled: */
543 fs:2/((1+5*s)*(1+s)**2*(1+0.3*s));
547 /* List with three controllers: I-, PI- and PID-controller: */
549 [fri,frpi,frpid]:[1/(s*Ti),
550 kr*(1+1/(s*Tn)),(1+s*Ta)*(1+s*Tb)/(s*Tc)];
554 /* Calculation of the I-controller according to the gain optimum: */
556 g1:gain_optimum(fs,fri);
560 /* Calculation of the PI-controller according to the gain optimum: */
562 g2:gain_optimum(fs,frpi);
566 /* Calculation of the PID-controller according to the gain optimum: */
568 g3:gain_optimum(fs,frpid);
572 /* List with all three dimensioned controllers: */
574 reli:float(ev([fri,frpi,frpid],[g1,g2,g3]));
578 /* Comparison of the closed loop step responses of the controlled systems: */
580 step_response(closed_loop(reli*fs));
584 /* Transfer function of a plant with symbolic coefficients: */
586 fs:2/((1+a*s)*(1+s**2)*(1+b*s));
590 /* Gain optimum also handles plants with symbolic coefficients: */
592 gain_optimum(fs,frpi);
599 /* Transfer function with two symbolic parameters a and b: */
601 f:1/(s**3+a*s**2+b*s+1);
605 /* Deviation from the stationary value in response to an input step: */
607 xdev:ratsimp((1-f)/s);
611 /* Integral of squared error (ISE-criterion): */
617 /* Optimization of the two symbolic parameters a and b: */
619 abl:ratsimp(jacobian([iise],[a,b]));
623 res:solve(abl[1],[a,b]);
627 /* Optimum transfer function: */
633 /* State Space Methods*/
636 /* System matrix A of an RLC-circuit: */
638 A:matrix([-R/L,0,-1/L,0],[0,-R/L,1/L,-1/L],
639 [1/C1,-1/C1,0,0],[0,1/C1,0,0]);
643 /* Input matrix B of an RLC-circuit: */
645 B:matrix([1/L],[0],[0],[0]);
649 /* Output matrix C of an RLC-circuit: */
655 /* A list of the state matrices A, B, and C forms a "system", which
656 can be checked with the predicate functions "systemp" and "nsystemp": */
666 /* Calculation of the transfer function from the state matrices A, B and C,
667 which can be performed in several ways: */
669 f:transfer_function(circuit);
671 transfer_function(A,B,C);
675 /* Calculation of the transfer function of the circuit using "impedance_chain"
676 gives the same result, expectedly: */
678 f:impedance_chain(R+s*L,1/(s*C1),2);
682 /* Transformation of the state matrices into the controller canonical form: */
684 circ1:controller_canonical_form(f);
688 /* Transformation of the state matrices into the observer canonical form: */
690 circ2:observer_canonical_form(f);
694 /* Calculation of the controllability matrix: */
696 h1:ratsimp(controllability_matrix(A,B));
700 /* Calculation of the observability matrix: */
702 h2:observability_matrix(circuit);
706 /* The rank of those matrices show, whether the system is controllable
715 /* Time delayed Systems*/
718 /* (added 2019-01-05) */
721 /* Open loop system (IT1) with additional dead time: */
723 Fo:1/(s*(s+3))*exp(-2*s);
727 /* Frequency Response */
735 nyquist_plot(Fo,xrange=[-1.5,0.5],yrange=[-0.7,0.5]);
737 nichols_plot(Fo,xrange=[-500,0],yrange=[0.1,10]);
741 /* Stability Performance */
753 /* Closed-Loop System */
756 /* The closed-loop transfer function can be represented exactly as an (infinite) sum - one addend for each time step. For t<T the output is 0. For a successive time step the output is just the output from Fo times the input minus the preceding output from Fo. */
759 /* The number of time steps can be specified as a 2nd parameter to closed_loop (Default 5): */
763 unit_step_included=true;
765 step_response(closed_loop(Fo*[0.5,1,1.5,2],10),xrange=[0,20],yrange=[-0.5,2.5]);
769 /* Comparison of the exact step response (red) with a 3rd-order pade approximation (blue): */
771 Fo_pade:1/(s*(s+3)) *time_delay(2,3);
773 step_response(closed_loop([Fo,Fo_pade],10),xrange=[0,20],yrange=[-0.5,1.5]);
777 /* Digital Algorithms*/
783 /* A step function can be generated either by sampling of a continuous function, by a list of values, or by a list of points (with an x-value and an y-value each). The independent variable is assumed to be "t". */
785 stepsin:stepf(sin(t),%pi/10);
787 plot(stepsin,xrange=[-5,10],yrange=[-2,2]);
789 steplist:[0,1,2,3,4,3,2,1];
791 plot(['stepf(steplist),'stepf(steplist,0.2)],xrange=[-5,10],yrange=[-2,5]);
793 steppoints:[[0,1],[0.5,2],[1,3],[2,2],[2.5,0],[3,2],[4.5,3]];
795 plot('stepf(steppoints),xrange=[-5,10],yrange=[-2,5]);
797 ev(stepf(steppoints),t=1.9);
798 ev(stepf(steppoints),t=2.0);
799 ev(stepf(steppoints),t=2.1);
806 /* 2nd-order System: */
808 Fpt2:2/(3+4*s+5*s**2);
812 /* Euler Algorithm (with "backward differentiation") of the PT2:
813 The result is a list consisting of the difference equation and the sampling time. If the sampling time as the 2nd parameter is omitted, "T" is assumed. */
815 alg1:eulerb(Fpt2,0.5);
821 /* Euler Algorithm (with "forward differentiation") of the PT2: */
823 alg2:eulerf(Fpt2,0.5);
827 /* Tustin algorithm of the PT2: */
829 alg3:tustin(Fpt2,0.5);
833 /* Comparison, of the algorithms; "step_response" accepts an algorithm as parameter: */
835 step_response([Fpt2,alg1,alg2,alg3],yrange=[0,1],xrange=[1,20]);
839 /* Obviously only the Tustin algorithm yields an acceptable approximation. */
842 /* A list of the first 20 values of the step response (2nd parameter defaults to 100): */
844 algorithm_step_response(alg3,20);
848 /* Check whether a list is recognized as an algorithm: */
850 algorithmp([x=3*u,5]);
852 algorithmp([x[k]=3*u+x[k-1],5]);
856 /* The algorithms work also symbolically: */
858 expand(eulerb(kr*1+1/(s*Tn)+s*Tv));