A disk drive read head can be modeled as a pair of rotating masses connected by a flexible shaft. As shown on Figure 1. represents the motor's inertia, while represents the head's inertia. The flexible shaft has a stiffness and damping coefficient . is the torque applied by the motor while is the disturbance torque.
EXERCISE 1
Using the impedance method, find the equations of motion for the system in the Laplace Domain
For the following questions, use the parameters from Table 1
Derive the transfer function symbolically.
Hint: Solve for a linear systems of equation or use Cramer's rule.
Simulate the response to a step input using the ControlSystems.jl stepplot() command.
Table 1 Disk Drive Parameters
Parameter | |||||
---|---|---|---|---|---|
Value | 0.1 | 0.04 | 0.07 | 0 |
# Exercise 1
using Plots;
pyplot();
using ControlSystems
using SymPy
(s, K, b, Ih, Im) = symbols("s K b I_h I_h")
K_v = 0.07; b_v = 0.1; Ih_v = 0.04; Im_v = 0.1;
A = [Im*s^2+b*s+K -b*s-K; -b*s-K Ih*s^2+b*s+K];
B = [1; 0];
Gx = inv(A)*B
G1s = (s^2*Gx[1])(K=>K_v, Im=>Im_v, Ih=>Ih_v, b=>b_v);
G2s = (s^2*Gx[2])(K=>K_v, Im=>Im_v, Ih=>Ih_v, b=>b_v);
# Takes a symbolic returns a ControlSystems::TransferFunction
function Sym_to_TF(G::Sym)::TransferFunction
s = symbols("s")
num_s, den_s = simplify(G).as_numer_denom() # Grab num /den polynomials
num = N(expand(num_s).as_poly(s).all_coeffs()) # Extract coefficient
den = N(expand(den_s).as_poly(s).all_coeffs())
num = round.(num; sigdigits = 3) # Round
den = round.(den; sigdigits = 3)
return tf(num, den) # Convert to ControlSystems::TransferFunction
end
G1 = Sym_to_TF(G1s)
@show G1s
G2 = Sym_to_TF(G2s)
@show G2s
t = 0:0.1:10;
p = stepplot(G2, t, lw=3)
G1s = s^2*(0.1*s^2 + 0.1*s + 0.07)/(0.01*s^4 + 0.02*s^3 + 0.014*s^2)
G2s = s^2*(0.1*s + 0.07)/(0.01*s^4 + 0.02*s^3 + 0.014*s^2)
EXERCISE 2
Use basic numerical integration to simulate the response of the system to a step input , note that this is a 2DOF system, so the numerical integration vector would be
But you will need to keep track of acceleration as well. So, either create a 6xn state vector to store and or keep a separate array for the accelerations.
Plot the angular accelerations on one sublot, and the angular velocities on a second subplot.
Hint: You already have the EOM from task 1, construct the numerical integration xdot function from them.
You should get the same response from stepplot() if you specify
# Exercise 2
function dxdt(x,u,t)
K = 0.07; b = 0.1; I_h = 0.04; I_m = 0.1;
dx = [0.0; 0.0; 0.0; 0.0];
dx[1] = x[2];
dx[2] = (u - K * x[1] - b * x[2] + K * x[3] + b * x[4]) / I_m;
dx[3] = x[4];
dx[4] = (- K * x[3] - b * x[4] + K * x[1] + b * x[2]) / I_h;
return dx
end
# Integration Time step
dt = 0.1;
# Time vector
t = 0:dt:20;
# Initialize x
x_sim = zeros(6,length(t)); # Empty 2xn array
ui = zeros(1,length(t)); # Empty 1xn vector
x0 = [0, 0, 0, 0, 0, 0]
x_sim[:,1] = x0;
ui = ones(1, length(t))
for ix = 1:length(t)
xdot = dxdt(x_sim[1:4,ix], ui[1,ix], t[ix]); # Grab the derivative vector
x_sim[5:6, ix] = [xdot[2]; xdot[4]];
if ix < length(t)
x_sim[1:4, ix+1] = x_sim[1:4, ix] + xdot * dt; # Integrate x
end
end
# And plot
plot(t,ui[1,:])
p = plot(t,x_sim[5,:], lw=3, label = "\$\\ddot{\\theta}_m\$")
plot!(p, t,x_sim[6,:], lw=3, label = "\$\\ddot{\\theta}_h\$")
EXERCISE 3 From the response in the previous task, define a function stepinfo(G) to numerically compute the following performance values for a transfer function's step response: Rise Time , Peak Time , Settling Time , and Percent Overshoot . Test the function on the step response of the head acceleration transfer function derived above, and display the computed results of these performance specifications on the figure.
Hints:
For Rise Time: You need to compute the time it takes to go from 10% to 90% of the final output
For %OS, find the max() value in the response and compare with the final value x(end)
For Peak Time: use the index of max() to find the corresponding time.
For Settling Time: Capture the last time the output is greater than 2% of final value.
# Exercise 3
rd(x) = round(x, sigdigits=3)
function stepinfo(t::Array, x::Array)
xmax = maximum(x)
# Max compared to final value
OS = rd(xmax/x[end] - 1)
# From index of max value
Tp = rd(t[findfirst(a->a==xmax, x)[1]])
# From index of first time to reach 10% and 90% of final value
Tr = rd(t[findfirst(t->t>=0.9*x[end], x)[1]] - t[findfirst(t->t>=0.1*x[end], x)[1]])
# From last index when value is ≥ 2% of final value
Ts = rd(t[findlast(t->abs(t-x[end])>=0.02, x)[1]+1])
println("Rise Time: $Tr s")
println("Percent Overshoot: $(OS*100)%")
println("Peak Time: $Tp s")
println("Settling Time: $Ts s")
return Tr, OS, Tp, Ts
end
function stepinfo(G::TransferFunction)
x, t, _ = step(G)
return stepinfo(t[:,1],x[:,1])
end
# Test
Ts = 2.5;
Tp = 1;
ωd = π / Tp;
σ = 4 / Ts;
ζ = cos(atan(ωd, σ))
ωn = σ/ζ
OS = exp(-ζ*π/sqrt(1-ζ^2))
s = tf("s")
G = ωn^2 / (s^2+2*ζ*ωn*s+ωn^2)
println("Analytical:")
println("Percent Overshoot: $(rd(OS*100))%")
println("Peak Time: $Tp s")
println("Settling Time: $Ts s")
println("\nMeasured:")
stepinfo(G)
Analytical:
Percent Overshoot: 20.2%
Peak Time: 1 s
Settling Time: 2.5 s
Measured:
Rise Time: 0.49 s
Percent Overshoot: 20.1%
Peak Time: 0.98 s
Settling Time: 2.38 s
You are given a real brushed DC Motor, but you don't have its full specifications. You do; however, have a simple model for a DC motor and you are given three sets of experimental data for a motor:
i. The angular velocity (rad/s) of the motor in response to a step voltage input of 10V
ii. The torque-speed curve for the motor at a set input voltage of 5V.
iii. The torque-speed curve for the motor at a set input voltage of 10V.
Each of the above data sets are provided in a separate and labeled csv file, using the CSV.jl and DataFrames.jl packages. The below code snippet shows you how to read the data into Julia arrays.
using CSV, DataFrames
data = CSV.read("data.csv", DataFrame);
t = data[!,1];
y = data[!,2];
The Simple DC Motor transfer function is given as:
which is equivalent to , if all the constants are lumped. Note that the data set for the step response is given for angular velocity, so we need . This can be easily derived by differentiating the angular position, by multiplication with a differentiator , which gives the first order transfer function:
And remember, for the torque-speed curve, we use the time domain relationship that relate input voltage, angular velocity and torque:
EXERCISE 4
Obtain, from the step response data set, the values of and in Equation 2.
The response is first order, if you find the time constant and scale the response you can find the two values and . Hint: Apply the Final Value Theorem to find K
Obtain, from the two torque-speed curve data sets, the value of the coefficients in Equation 3. Use
Once plotted, you can perform this task using hand calculations, as covered in class.
From the previous two tasks, compute all the remaining coefficients, , in Equation 1
Also covered in class.
Now that you can redefine your transfer function, simulate it to a step input using the step() command and compare it to the step response data in the provided data set. (Remember to scale by 10 to account for the value of voltage input of 10V and to use the transfer function , which is equal to
# Exercise 4
using CSV, DataFrames
data = CSV.read("../data/Part_I_Problem_B_Data_Step_Response.csv", DataFrame);
t = data[!,1];
x = data[!,2];
r = 10
tau = t[findfirst(a->a>=0.6*x[end], x)[1]];
a = 1 / tau
K= a * x[end] / r;
s = tf("s")
G = K / (s+a)
p = plot(t, x, lw=3, label="Experiment")
stepplot!(p, r*G, t, lw=3, label="Estimate")
data = CSV.read("../data/Part_I_Problem_B_Data_Torque_Speed_10V.csv", DataFrame);
w = data[!,1];
T = data[!,2];
e = 10;
Ra = 8;
wnoload = w[end]
Tstall = T[1]
Kt = rd(Ra * Tstall / e)
Kb = rd(e / wnoload)
Jm = rd(Kt / (Ra * K))
Dm = rd(a*Jm - Kt*Kb/Ra)
println("Motor Parameters:\nR_a=$Ra, K_t=$Kt, K_b=$Kb, J_m=$Jm, D_m=$Dm")
Motor Parameters:
R_a=8, K_t=1.92, K_b=1.0, J_m=1.8, D_m=0.578
This part is meant to serve as a tutorial on how to numerically implement a PID controller in a simulation. A MATLAB template script is provided to get you started. Follow through and complete the tasks.
A PID controller takes the form shown on Figure 2. Using the transfer function form, we can come up with a reduced equivalent closed-loop form and simulate the response of the feedback system to an impulse, step or ramp response directly, and this is useful in a number of control design scenarios. The reduced form is shown on Figure 3
But as we discussed in the previous assignment, implementing the simulation using numerical integration is a much more flexible design platform. Let's first get a sense on how to implement the PID Controller using transfer functions and using the reduced closed-loop form.
EXERCISE 5
Given the system , and the gain values: . Simulate the closed-loop response of the system with the PID controller, to a step input using step().
Substitute the values into the closed-loop form and simulate.
# Exercise 5
s = tf("s")
Gp = 2 / (s^2+8*s+25);
Kp = 200; Ki = 150; Kd = 1;
Gc = (Kd*s^2 + Kp*s + Ki ) / s;
t = 0:0.01:10
Gcl = feedback(Gc*Gp, 1);
p = stepplot(Gcl, t, lw=3);
To implement the PID Controller, or any controller, numerically, we must understand its logic in the time domain. Figure 4 shows the PID feedback controller expressed in the time domain.
The PID controller has three components: Proportional to Error, Proportional to Error Integral, Proportional to Error Derivative. So if we calculate the error, its integral and its derivative at every time sample we can compute the control law at every time sample (we assume a small enough simulation , otherwise we have to discuss digital controllers, which is outside the scope of this assignment).
EXERCISE 6
Using the provided script template, complete the algorithm definitions for
a. The first error value and first control law before the integration loop
b. The next error value in the integration loop:
c. The next error integral in the integration loop:
d. The next error derivative in the integration loop:
e. The next control output in the integration loop
From Equation 4 above.
Then test your implementation of the numerical PID controller and compare the simulation output against the output you obtained using the closed-form method above. To troubleshoot your code, start first by applying the proportional gain to both methods (set , then add the integral gain (set , then add the derivative gain instead (set , then add all the gains.
# Exercise 6
# Funtion for xdot vector - using an anonymous function.
dxdt(x,u,t) = [x[2]; 2*u - 8 * x[2] - 25 * x[1]]
# Initialize simulation parameters
x0 = [0; 0]
dt = 0.01;
t_sim = 0:dt:2;
x_sim = zeros(2,length(t_sim)); # Empty 2xn array
u = zeros(length(t_sim)); # Empty 1xn vector
e = zeros(length(t_sim));
e_int = zeros(length(t_sim));
e_dot = zeros(length(t_sim));
# Reference input
r = 1;
# Compute First Error
e[1] = 0;
# Compute First Control Output
u[1] = 0;
x_sim[:,1] = x0;
Kp = 200; Ki = 150; Kd = 1;
for idx = 1:length(t_sim)-1
# Grab the derivative vector
xdot = dxdt(x_sim[:,idx], u[idx], t_sim[idx]);
# Integrate
x_sim[:, idx+1] = x_sim[:, idx] + xdot * dt;
# Calculate Errors
e[idx+1] = r - x_sim[1, idx+1];
e_int[idx+1] = e_int[idx] + e[idx]*dt;
e_dot[idx+1] = (e[idx+1] - e[idx]) / dt;
# PID Control Law
u[idx+1] = Kp*e[idx+1] + Ki*e_int[idx+1] + Kd*e_dot[idx+1];
end
# And plot
p = plot(t_sim,0.01*u, lw=3, label = "0.01u")
plot!(t_sim,x_sim[1,:], lw=3, label = "\$x_1\$")
plot!(p, t_sim,x_sim[2,:], lw=3, label = "\$x_2\$")
In Part I.B you retrieved the model parameters for a DCmotor, you will attempt to implement a feedback controller on this system to
a. control the speed of the motor to a given step set-point,
b. control the torque of the motor to a given step set-point.
For this question, implement the feedback controller using the transfer function method explained in Part II. Then tune the gains to achieve the required performance specifications. Plot the responses and the performance specifications.
Note: With the parameters retrieved in Part I.B. In addition, use the inductance term here to achieve a more realistic transient response, with . The transfer functions are given.
EXERCISE 7
Using a PID Controller, implement it in a feedback loop to control the speed of a motor given the torque input. Attain the following performance specifications: , , , , . Plot the responses.
# Exercise 7
Kb = 1; Kt = 2; Ra = 8; J = 2; D = 0.6; La = 5;
s = tf("s");
G_speed = s * Kt / ( (J * s^2 + D * s) * (Ra + La * s) + Kt * Kb * s);
# Gains
Kp = 10; Ki = 5; Kd = 10;
# Reference Input
r = 10;
t=0:.01:5;
Gc = Kp + Ki/s + Kd * s;
Gcl_speed = feedback(Gc*G_speed,1);
p = stepplot(r*Gcl_speed,t, lw=3, label="\$\\dot{θ}\$")
stepinfo(r*Gcl_speed)
Rise Time: 2.55 s
Percent Overshoot: 0.0%
Peak Time: 5.0 s
Settling Time: 4.85 s
In class, we discussed how we can represent the transfer function relating voltage to output torque
EXERCISE 8
Use a PID controller in a feedback loop to control the Torque of the same motor. Attain the following performance specifications: , , , , . Plot the responses.
This problem should not consume much time if you have completed Part II Task 1: Use the right transfer function for the system and tune the gains through trial and error and your understanding of what each term of the PID does to achieve the required performance. Use this exercise to gain an appreciation for how each PID controller term affects the response.
# Exercise 8
Kb = 1; Kt = 2; Ra = 8; J = 2; D = 0.6; La = 5;
s = tf("s");
G_torque = Kt * ( J * s^2 + D * s) / ( (J * s^2 + D * s) * (Ra + La * s) + Kt * Kb * s)
# Gains
Kp = 110; Ki = 100; Kd = 0;
# Reference Input
r = 2;
t=0:.01:5;
Gc = Kp + Ki/s + Kd * s;
Gcl_torque = feedback(Gc*G_torque,1);
p = stepplot(r*Gcl_speed,t, lw=3, label="\$τ\$")
stepinfo(r*Gcl_speed)
Rise Time: 2.55 s
Percent Overshoot: 0.0%
Peak Time: 5.0 s
Settling Time: 4.45 s
In Part I Problem A, you were able to get a stable response for controlling the acceleration of the disk drive motor (The system is stable), if we attempt to get the speed or position response with a step input and without feedback, the system then is unstable (The systems and are unstable). We can use feedback control to stabilize the systems or .
EXERCISE 9
Use a PID Controller to achieve a stable position control response for the motor and attain the following performance specifications: , , , , .
Note: Use low values for the gains (start with values lower than 1)
Note that the input is applied on the same inertial mass we are trying to control the position of: , we can try to control directly by measuring its error instead and computing the control law output. Trying to control a part of a system (the disk drive head), while the actuation effort is at another part of the system (the motor) is termed non-collocated control, and can usually result in a nonminimum phase behavior. Trying to control an inverted pendulum's angle while applying force on the moving cart holding the pendulum, is another example of non-collocated control (it would be much easier to have a motor at the pendulum pivot: collocated control)
# Exercise 9
# Funtion for xdot vector - using an anonymous function.
function dxdt(x,u,t)
K_v = 0.07; b_v = 0.1; Ih_v = 0.04; Im_v = 0.1;
return [x[2];
(u - K_v * x[1] - b_v * x[2] + K_v * x[3] + b_v * x[4]) / Im_v;
x[4];
(-K_v * x[3] - b_v * x[4] + K_v * x[1] + b_v * x[2]) / Ih_v
];
end
# Initialize simulation parameters
x0 = [0; 0; 0; 0]
dt = 0.001;
t_sim = 0:dt:2;
x_sim = zeros(4,length(t_sim)); # Empty 2xn array
u = zeros(length(t_sim)); # Empty 1xn vector
e = zeros(length(t_sim));
e_int = zeros(length(t_sim));
e_dot = zeros(length(t_sim));
# Reference input
r = 1;
# Compute First Error
e[1] = 0;
# Compute First Control Output
u[1] = 0;
x_sim[:,1] = x0;
Kp = 10; Ki = 8; Kd = 1;
for idx = 1:length(t_sim)-1
# Grab the derivative vector
xdot = dxdt(x_sim[:,idx], u[idx], t_sim[idx]);
# Integrate
x_sim[:, idx+1] = x_sim[:, idx] + xdot * dt;
# Calculate Errors
e[idx+1] = r - x_sim[1, idx+1];
e_int[idx+1] = e_int[idx] + e[idx]*dt;
e_dot[idx+1] = (e[idx+1] - e[idx]) / dt;
# PID Control Law
u[idx+1] = Kp*e[idx+1] + Ki*e_int[idx+1] + Kd*e_dot[idx+1];
end
# And plot
p = plot(t_sim,0.05*u, lw=3, label = "0.05u")
plot!(t_sim,10*x_sim[1,:], lw=3, label = "\$10x_1\$")
plot!(p, t_sim,x_sim[2,:], lw=3, label = "\$x_2\$")
stepinfo(t_sim[:,1], x_sim[1,:])
Rise Time: 0.096 s
Percent Overshoot: 30.099999999999998%
Peak Time: 0.253 s
Settling Time: 0.76 s
Bonus Task: Instead of applying the PID on the motor angular position, try to apply it on the drive read head , try to come up with a stable response. Try to get the following performance specifications: , , , , .
You will notice that with only PID control, we are better off implementing a collocated controller. There are more advanced ways of controlling non-collocated systems, which is the subject of another course.