With control.step_response can't get the expected output - python

import control
import numpy as np
import matplotlib.pyplot as plt
Ts = 1
G1 = control.tf([60], [1,0.1])
G2 = control.tf([0.5, 3], [1, 5, 12])
G3 = control.tf([1], [1, 5])
Gs= G1*G2*G3
Gz = control.c2d(Gs,Ts, method='tustin' )
print(Gz)
print(Gs)
cltf=(Gs/(1+Gs))
Zcltf=(Gz/(1+Gz))
T = np.arange(0, 15)
za = control.step_response(cltf, T)
Tout, y = control.step_response(cltf, T)
Tout, x = control.step_response(Zcltf, T)
plt.subplot(2,1,1)
plt.plot(Tout, y)
plt.subpolt(2,1,2)
plt.plot(Tout,y.Tout)
Hello everyone, this is my code. I am new to Python. And my step responses always look like this:This Graph
In Matlab I got those for the two step responses Like those
I couldn't figured it out what is the reason of it.

the time vector T need more points, if you do not put step use de default value. You can try whiht T = np.arange(0, 15,0.1).
on the other hand, if you dont use T in the function step_response calculate a time vector, but have some problems with some plants, for example, stiff plants.
try this:
Tout, y = control.step_response(cltf)
finaly, you never plot x (the digital step output), you can try use plt.step() in place of plt.plot() to stairs plot.
import control
import numpy as np
import matplotlib.pyplot as plt
Ts = 1
G1 = control.tf([60], [1,0.1])
G2 = control.tf([0.5, 3], [1, 5, 12])
G3 = control.tf([1], [1, 5])
Gs= G1*G2*G3
Gz = control.c2d(Gs,Ts, method='tustin' )
print(Gz)
print(Gs)
cltf=(Gs/(1+Gs))
Zcltf=(Gz/(1+Gz))
Tout1, y = control.step_response(cltf)
Tout2, x = control.step_response(Zcltf)
plt.plot(Tout1, y)
plt.figure()
plt.step(Tout2,x)

Related

Fitting a quadratic function in python without numpy polyfit

I am trying to fit a quadratic function to some data, and I'm trying to do this without using numpy's polyfit function.
Mathematically I tried to follow this website https://neutrium.net/mathematics/least-squares-fitting-of-a-polynomial/ but somehow I don't think that I'm doing it right. If anyone could assist me that would be great, or If you could suggest another way to do it that would also be awesome.
What I've tried so far:
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
ones = np.ones(3)
A = np.array( ((0,1),(1,1),(2,1)))
xfeature = A.T[0]
squaredfeature = A.T[0] ** 2
b = np.array( (1,2,0), ndmin=2 ).T
b = b.reshape(3)
features = np.concatenate((np.vstack(ones), np.vstack(xfeature), np.vstack(squaredfeature)), axis = 1)
featuresc = features.copy()
print(features)
m_det = np.linalg.det(features)
print(m_det)
determinants = []
for i in range(3):
featuresc.T[i] = b
print(featuresc)
det = np.linalg.det(featuresc)
determinants.append(det)
print(det)
featuresc = features.copy()
determinants = determinants / m_det
print(determinants)
plt.scatter(A.T[0],b)
u = np.linspace(0,3,100)
plt.plot(u, u**2*determinants[2] + u*determinants[1] + determinants[0] )
p2 = np.polyfit(A.T[0],b,2)
plt.plot(u, np.polyval(p2,u), 'b--')
plt.show()
As you can see my curve doesn't compare well to nnumpy's polyfit curve.
Update:
I went through my code and removed all the stupid mistakes and now it works, when I try to fit it over 3 points, but I have no idea how to fit over more than three points.
This is the new code:
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
ones = np.ones(3)
A = np.array( ((0,1),(1,1),(2,1)))
xfeature = A.T[0]
squaredfeature = A.T[0] ** 2
b = np.array( (1,2,0), ndmin=2 ).T
b = b.reshape(3)
features = np.concatenate((np.vstack(ones), np.vstack(xfeature), np.vstack(squaredfeature)), axis = 1)
featuresc = features.copy()
print(features)
m_det = np.linalg.det(features)
print(m_det)
determinants = []
for i in range(3):
featuresc.T[i] = b
print(featuresc)
det = np.linalg.det(featuresc)
determinants.append(det)
print(det)
featuresc = features.copy()
determinants = determinants / m_det
print(determinants)
plt.scatter(A.T[0],b)
u = np.linspace(0,3,100)
plt.plot(u, u**2*determinants[2] + u*determinants[1] + determinants[0] )
p2 = np.polyfit(A.T[0],b,2)
plt.plot(u, np.polyval(p2,u), 'r--')
plt.show()
Instead using Cramer's Rule, actually solve the system using least squares. Remember that Cramer's Rule will only work if the total number of points you have equals the desired order of polynomial plus 1.
If you don't have this, then Cramer's Rule will not work as you're trying to find an exact solution to the problem. If you have more points, the method is unsuitable as we will create an overdetermined system of equations.
To adapt this to more points, numpy.linalg.lstsq would be a better fit as it solves the solution to the Ax = b by computing the vector x that minimizes the Euclidean norm using the matrix A. Therefore, remove the y values from the last column of the features matrix and solve for the coefficients and use numpy.linalg.lstsq to solve for the coefficients:
import numpy as np
import matplotlib.pyplot as plt
ones = np.ones(4)
xfeature = np.asarray([0,1,2,3])
squaredfeature = xfeature ** 2
b = np.asarray([1,2,0,3])
features = np.concatenate((np.vstack(ones),np.vstack(xfeature),np.vstack(squaredfeature)), axis = 1) # Change - remove the y values
determinants = np.linalg.lstsq(features, b)[0] # Change - use least squares
plt.scatter(xfeature,b)
u = np.linspace(0,3,100)
plt.plot(u, u**2*determinants[2] + u*determinants[1] + determinants[0] )
plt.show()
I get this plot now, which matches what the dashed curve is in your graph, also matching what numpy.polyfit gives you:

Trouble using fsolve to find x-values of scipy.ODR with given y-values

I'm trying to utilize fsolve to find x-values based on known y-values of my scipy.ODR plot:
import matplotlib.pyplot as plt
import numpy as np
from scipy.odr import ODR, Model, RealData
from scipy.optimize import fsolve
def func(beta, x):
y = beta[0]+beta[1]*x+beta[2]*x**3
return y
sqx = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16])
sqy = sqx**2
host = np.linspace(0,100,100)
modata = RealData(sqx, sqy)
model = Model(func)
sqc = np.array([sqy])[0]
sqc.fill(0)
sqm = np.transpose([sqy, sqc, sqc])
odr = ODR(modata, model, [1,1,1])
odr.set_job(fit_type=0)
output = odr.run()
yn = func(output.beta, host)
xvals = np.array([[fsolve(func, [10,10,10], args=((output.beta - sqm[hi]))) for hi in range(len(sqm))]]).flatten()
hosty = func(host, output.beta)
[plt.axhline(sqy[hi]) for hi in range(len(sqy))]
[plt.axvline(xvals[hi]) for hi in range(len(xvals))]
plt.plot(host ,yn,'g-',label='odr')
plt.legend(loc="best");
plt.axis([0,20,0,300])
plt.show()
print(xvals)
news = func(xvals, output.beta)
print(news)
print (sqy)
I'm able to accomplish my goal with a curve_fit function:
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit, fsolve
def sq(x, a, b, c):
return a*x**(2) + b*x + c
sqx = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16])
sqy = sqx**2
meme = 10
host = np.linspace(0,20,100)
popt, pcov = curve_fit(sq, sqx, sqy)
xvals = np.array([[fsolve(sq, 10, args=(popt[0], popt[1], (popt[2] - sqy[hi]))) for hi in range(len(sqy))]]).flatten()
hosty = sq(host, *popt)
[plt.axhline(sqy[hi]) for hi in range(len(sqy))]
[plt.axvline(xvals[hi]) for hi in range(len(xvals))]
plt.plot(sqx, sqy, 'bo')
plt.plot(host, hosty, 'r-')
plt.axis([0,6,0,20])
plt.show()
print(xvals)
news = sq(xvals, *popt)
print(news)
print (sqy)
I can plot the values neatly and confirm I'm solving for the right numbers here.
But I can't seem to do this the same way if I want to use an ODR function. I'm confused as to how the notation would work out when I'm solving for x in my func. It's giving me 3x as many values as necessary because I have to put in 3 starting estimates when I use fsolve due tofunc's beta indices.
I also can't figure what the best way of writing the actual arguments is, as putting output.beta acts as a single variable, but I want to solve my y-value so I need a subtraction to happen somewhere.
I've tried switching around func to accept input in different ways, but I can't get the ODR fitting to work when I do this. Is there any way of accomplishing this?
Let's focus on the relevant code: you have
def func(beta, x):
y = beta[0]+beta[1]*x+beta[2]*x**3
return y
and
fsolve(func, [10,10,10], args=((output.beta - sqm[hi])))
According to fsolve syntax, it solves for the first variable of func (which could be a vector). This means you are solving for vector beta, with initial guess [10,10,10]. But from the context it's clear you want to get x, with beta being known.
So you need a wrapper around func to put the arguments in order. A simple example would be
fsolve(lambda x: func([1, 2, 3], x), 10)
which returns array([-0.40231994]). Here fsolve gets a function of one argument x, and solves for it. And here is how it can work in your case:
[fsolve(lambda x: func(output.beta - s, x), 10)[0] for s in sqm]
I've made two more changes:
use x for x in xlist instead of x[i] for i in range(len(xlist))
pick the solution immediately with [0] instead of applying flatten later.

Frequency Response Scipy.signal

I'm learning digital signal processing to implement filters and am using python to easily implement a test ideas. So I just started using the scipy.signal library to find the impulse response and frequency response of different filters.
Currently I am working through the book "Digital Signals, Processors and Noise by Paul A. Lynn (1992)" (and finding it an amazing resource for learning this stuff). In this book they have a filter with the transfer functions shown below:
I divided the numerator and denominator by in order to get the following equation:
I then implemented this with Scipy using:
NumeratorZcoefs = [1, -1, 1, -1]
DenominatorZcoefs = [1, 0.54048, -0.62519, -0.66354, 0.60317, 0.69341]
FreqResponse = scipy.signal.freqz(NumeratorZcoefs, DenominatorZcoefs)
fig = plt.figure(figsize = [8, 6])
ax = fig.add_subplot(111)
ax.plot(FreqResponse[0], abs(np.array(FreqResponse[1])))
ax.set_xlim(0, 2*np.pi)
ax.set_xlabel("$\Omega$")
and produce the plot shown below:
However in the book the frequency response is shown to be the following:
They are the same shape but the ratio of the peaks at ~2.3 and 0.5 are very different for the 2 plots, could someone suggest why this is?
Edit:
To add to this, I've just implemented a function to calculate the frequency response by hand (by calculating the distance from the poles and zeros of the function) and I get a similar ratio to the plot generated by scipy.signal, however the numbers are not the same, does anyone know why this might by?
Implementation is as follows:
def H(omega):
z1 = np.array([0,0]) # zero at 0, 0
z2 = np.array([0,0]) # Another zero at 0, 0
z3 = np.array([0, 1]) # zero at i
z4 = np.array([0, -1]) # zero at -i
z5 = np.array([1, 0]) # zero at 1
z = np.array([z1, z2, z3, z4, z5])
p1 = np.array([-0.8, 0])
p = cmath.rect(0.98, np.pi/4)
p2 = np.array([p.real, p.imag])
p = cmath.rect(0.98, -np.pi/4)
p3 = np.array([p.real, p.imag])
p = cmath.rect(0.95, 5*np.pi/6)
p4 = np.array([p.real, p.imag])
p = cmath.rect(0.95, -5*np.pi/6)
p5 = np.array([p.real, p.imag])
p = np.array([p1, p2, p3, p4, p5])
a = cmath.rect(1,omega)
a_2dvector = np.array([a.real, a.imag])
dz = z-a_2dvector
dp = p-a_2dvector
dzmag = []
for dis in dz:
dzmag.append(np.sqrt(dis.dot(dis)))
dpmag = []
for dis in dp:
dpmag.append(np.sqrt(dis.dot(dis)))
return(np.product(dzmag)/np.product(dpmag))
I then plot the frequency response like so:
omegalist = np.linspace(0,2*np.pi,5000)
Hlist = []
for omega in omegalist:
Hlist.append(H(omega))
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(omegalist, Hlist)
ax.set_xlabel("$\Omega$")
ax.set_ylabel("$|H(\Omega)|$")
and get the following plot:
The SciPy generated frequency response is correct. In any case, I wouldn't trust the book's figure which appears to have been drawn by hand.
If you want to find the frequency response "manually", this can be simply done by defining a function returning the original Z-transform and evaluating it on the unit circle as follows
def H(z):
num = z**5 - z**4 + z**3 - z**2
denom = z**5 + 0.54048*z**4 - 0.62519*z**3 - 0.66354*z**2 + 0.60317*z + 0.69341
return num/denom
import numpy as np
import matplotlib.pyplot as plt
w_range = np.linspace(0, 2*np.pi, 1000)
plt.plot(w_range, np.abs(H(np.exp(1j*w_range))))
The result is exactly the same as SciPy.

Connect all 2D Points from NumPy 2D Arrays as a triangular meshgrid

I am pretty new to Python and I am trying to plot a triangular grid like this:
import matplotlib.pyplot as plt
import numpy as np
r = 0.25
d = 2*r
s = 0
l1 = np.array([[s,0], [s+d,0], [s+2*d,0], [s+3*d,0]])
l2 = np.array([[s-r,d], [s+r,d], [s+r+d,d], [s+r+2*d,d]])
l3 = np.array([[s,2*d], [s+d,2*d], [s+2*d,2*d], [s+3*d,2*d]])
l4 = np.array([[s-r,3*d], [s+r,3*d], [s+r+d,3*d], [s+r+2*d,3*d]])
l5 = np.array([[s,4*d], [s+d,4*d], [s+2*d,4*d], [s+3*d,4*d]])
plt.scatter(*zip(*l1))
plt.scatter(*zip(*l2))
plt.scatter(*zip(*l3))
plt.scatter(*zip(*l4))
plt.scatter(*zip(*l5))
plt.show
My problem is, that I have no real clue how to connect all points. I have added horizontal lines with plt.plot(*zip(*l1)) for all l but I have no idea how to draw the 'vertical' zigzag lines... Has anybody a 'simple' solution?
Many thanks in advance!
triplot is made for that purpose: plotting triangles.
You can either pass only x and y coordinates (in this case a Delaunay triangulation will be computed), or a full Triangulation object to which you can specify your own triangles.
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.tri as mtri
r = 0.25
d = 2*r
s = 0
def meshgrid_triangles(n, m):
""" Returns triangles to mesh a np.meshgrid of n x m points """
tri = []
for i in range(n-1):
for j in range(m-1):
a = i + j*(n)
b = (i+1) + j*n
d = i + (j+1)*n
c = (i+1) + (j+1)*n
if j%2 == 1:
tri += [[a, b, d], [b, c, d]]
else:
tri += [[a, b, c], [a, c, d]]
return np.array(tri, dtype=np.int32)
x0 = np.arange(4) * d
y0 = np.arange(5) * d
x, y = np.meshgrid(x0, y0)
x[1::2] -= r
triangles = meshgrid_triangles(4, 5)
triangulation = mtri.Triangulation(x.ravel(), y.ravel(), triangles)
plt.scatter(x, y, color='red')
plt.triplot(triangulation, 'g-h')
plt.show()
Using the code the way you have (otherwise look at triplot_demo depending on what you want, as mentioned by #GBy), you can extract or rotate each array so that you just plot the line downwards:
import matplotlib.pyplot as plt
import numpy as np
r = 0.25
d = 2*r
s = 0
l1 = np.array([[s,0], [s+d,0], [s+2*d,0], [s+3*d,0]])
l2 = np.array([[s-r,d], [s+r,d], [s+r+d,d], [s+r+2*d,d]])
l3 = np.array([[s,2*d], [s+d,2*d], [s+2*d,2*d], [s+3*d,2*d]])
l4 = np.array([[s-r,3*d], [s+r,3*d], [s+r+d,3*d], [s+r+2*d,3*d]])
l5 = np.array([[s,4*d], [s+d,4*d], [s+2*d,4*d], [s+3*d,4*d]])
fig = plt.figure(0)
ax = fig.add_subplot(111)
larr = [l1,l2,l3,l4,l5]
# Plot horizontally
for l in larr:
# same as your *zip(*l1), but you can select on a column-wise basis
ax.errorbar(l[:,0], l[:,1], fmt="o", ls="-", color="black")
# Plot zig-zag-horizontally
for i in range(len(larr[0])):
lxtmp = np.array([x[:,0][i] for x in larr])
lytmp = np.array([x[:,1][i] for x in larr])
ax.errorbar(lxtmp, lytmp, fmt="o", ls="-", color="black")
ax.set_ylim([-0.1,2.1])
ax.set_xlim([-0.6,1.6])
plt.show()
EDIT:
lxtmp = np.array([x[:,0][i] for x in larr])
So, x[:,0] means take all of the rows ":" but only the first column "0". For l1 it will return:
l1[:,0]
array([ 0. , 0.5, 1. , 1.5])
which are the x-values for l1. Doing l1[:,1] will return all of the rows from column "1", the y-values. To draw the vertical lines, you want to take all the x and y values from each i-th array, and hence you loop over all the arrays, taking out the i-th element. For example, the 3rd vertical zig-zag line, would be:
lxtmp = [l1[:,0][2], l2[:,0][2], l3[:,0][2], l4[:,0][2], l5[:,0][2]]
lytmp = [l1[:,1][2], l2[:,1][2], l3[:,1][2], l4[:,1][2], l5[:,1][2]]
To simplify and run over each element, I created 'larr' to loop over and 'build' then in a normal python fashion, e.g.,
[i for i in range(1,10)]
[1, 2, 3, 4, 5, 6, 7, 8, 9]

Transform polynomial graph - Multiply by a list of values

I'm using matplotlib. I have a list of 600 values. I also have an polynomial function that I'm graphing with values between 0 and 600. I'm trying to multiply every point by the corresponding value in the list.
I could evaluate the polynomial in a loop, and do the multiplication there, but I would end up with a graph of points instead of a line.
I think I might need to use the Transformations framework, but not sure how to apply it to the graph.
Edit:
a = [5, 2, 3 ... 0, 2, 8] # 600 values
poly_a = polyfit(a)
deriv_a = polyder(poly_a)
b = [232, 342 ... 346, 183] # 600 values
I need to multiply deriv_a by b.
I think you're misunderstanding things a bit. This is what numpy is for (if you're using matplotlib it's already converting things to a numpy array when you plot, regardless.)
Just convert your "list of 600 values" to a numpy array and then evaluate the polynomial.
As an example:
import numpy as np
import matplotlib.pyplot as plt
# Your "list of 600 values"...
x = np.linspace(0, 10, 600)
# Evaluate a polynomial at each location in `x`
y = -1.3 * x**3 + 10 * x**2 - 3 * x + 10
plt.plot(x, y)
plt.show()
Edit:
Based on your edit, it sounds like you're asking how to use numpy.polyder?
Basically, you just want to use numpy.polyval to evaluate the polynomial returned by polyder at your point locations.
To build on the example above:
import numpy as np
import matplotlib.pyplot as plt
# Your "list of 600 values"...
x = np.linspace(0, 10, 600)
coeffs = [-1.3, 10, 3, 10]
# Evaluate a polynomial at each location in `x`
y = np.polyval(coeffs, x)
# Calculate the derivative
der_coeffs = np.polyder(coeffs)
# Evaluate the derivative on the same points...
y_prime = np.polyval(der_coeffs, x)
# Plot the two...
fig, (ax1, ax2) = plt.subplots(nrows=2)
ax1.plot(x, y)
ax1.set_title('Original Function')
ax2.plot(x, y_prime)
ax2.set_title('Deriviative')
plt.show()

Categories

Resources