major renaming of transfer functions
This commit is contained in:
@@ -39,43 +39,67 @@ function [ssc]=StateSpaceControlDesign(mot,mode)
|
||||
|
||||
|
||||
%use_lqr: use lqr instead of pole placement
|
||||
verb=1;
|
||||
t=0:1E-4:.5; %time vector for simulations
|
||||
verb=0;
|
||||
use_lqr=0;
|
||||
MaxDac=2011.968;
|
||||
filt_pos_err=Prefilt(mot,2);
|
||||
tfDesPos=tf(1,1);
|
||||
tfPosErr=tf(1,1);
|
||||
ssc=struct();
|
||||
ssc.sel1={3,[3]};
|
||||
%locate poles: 2500rad/s = 397Hz, 6300rad/s = 1027Hz
|
||||
switch mode
|
||||
case -1 %TESTING
|
||||
|
||||
case 0 %for document: best observer without prefilter
|
||||
ss_plt=mot.ss_plt; %ss_plt ss_c1 ss_d1 ss_1 ss_0
|
||||
ss_mdl=mot.ss_d1; %ss_plt ss_c1 ss_d1 ss_1 ss_0
|
||||
if mot.id==1
|
||||
pl=[-2200 -2100 -2000]; % stable with scaling of .05 .. 1.0;
|
||||
else
|
||||
pl=[-2500 -900 -800];
|
||||
end
|
||||
plObs=2*pl;
|
||||
case 0
|
||||
ss_plt=mot.ss_plt; %ss_plt ss_c1 ss_d1 ss_1 ss_0
|
||||
ss_mdl=mot.ss_c1; %ss_plt ss_c1 ss_d1 ss_1 ss_0
|
||||
ss_mdl=mot.ss_cp; %ss_plt ss_c1 ss_d1 ss_1 ss_0
|
||||
if mot.id==1
|
||||
pl=[-3300 -3200 -2900 -2800];
|
||||
else
|
||||
pl=[-3300 -3200 -2700 -2600];
|
||||
end
|
||||
plObs=2*pl;
|
||||
case 1
|
||||
case 1 %for document: best observer without prefilter
|
||||
ss_plt=mot.ss_plt; %ss_plt ss_c1 ss_d1 ss_1 ss_0
|
||||
ss_mdl=mot.ss_d1; %ss_plt ss_c1 ss_d1 ss_1 ss_0
|
||||
ss_mdl=mot.ss_cp; %ss_plt ss_c1 ss_d1 ss_1 ss_0
|
||||
if mot.id==1
|
||||
pl=[-3300 -3200 -2900 -2800];
|
||||
else
|
||||
pl=[-3300 -3200 -2700 -2600];
|
||||
end
|
||||
plObs=2*pl;
|
||||
tfDesPos=Prefilt(mot,2);%user designed envelope
|
||||
tfPosErr=Prefilt(mot,1);%inverse resonance
|
||||
case 2 %for document: best observer with prefilter
|
||||
ss_plt=mot.ss_plt; %ss_plt ss_c1 ss_d1 ss_1 ss_0
|
||||
ss_mdl=mot.ss_dp; %ss_plt ss_c1 ss_d1 ss_1 ss_0
|
||||
if mot.id==1
|
||||
pl=[-2200 -2100 -2000]; % stable with scaling of .05 .. 1.0;
|
||||
else
|
||||
pl=[-2500 -900 -800];
|
||||
end
|
||||
plObs=2*pl;
|
||||
case 2
|
||||
case 3
|
||||
ss_plt=mot.ss_plt; %ss_plt ss_c1 ss_d1 ss_1 ss_0
|
||||
ss_mdl=mot.ss_c1; %ss_plt ss_c1 ss_d1 ss_1 ss_0
|
||||
use_lqr=1
|
||||
ss_mdl=mot.ss_dp; %ss_plt ss_c1 ss_d1 ss_1 ss_0
|
||||
if mot.id==1
|
||||
pl=[-2200 -2100 -2000]; % stable with scaling of .05 .. 1.0;
|
||||
else
|
||||
pl=[-2500 -900 -800];
|
||||
end
|
||||
plObs=2*pl;
|
||||
case 4
|
||||
%this is the hovering ball model
|
||||
A = [ 0 1 0; 980 0 -2.8;0 0 -100 ];
|
||||
B = [ 0 0 100 ]';
|
||||
C = [ 1 0 0 ];
|
||||
D = 0;
|
||||
ssBall=ss(A,B,C,D,'InputName','iCmd','OutputName','actPos');
|
||||
ss_plt=ssBall; %ss_plt ss_c1 ss_d1 ss_1 ss_0
|
||||
ss_mdl=ssBall; %ss_plt ss_c1 ss_d1 ss_1 ss_0
|
||||
pl=[-10+10i -10-10i -50];
|
||||
plObs=[-100 -101 -102];
|
||||
end
|
||||
|
||||
[Am,Bm,Cm,Dm]=ssdata(ss_mdl);
|
||||
@@ -100,12 +124,14 @@ function [ssc]=StateSpaceControlDesign(mot,mode)
|
||||
|
||||
if bitand(verb,4)
|
||||
% step answer on open loop:
|
||||
t = 0:1E-4:.5;
|
||||
u = ones(size(t));
|
||||
[yp,t,x] = lsim(ss_plt,u,t,xp0);
|
||||
[ym,t,x] = lsim(ss_mdl,u,t,xm0);
|
||||
figure();plot(t,yp,t,ym,'--');title('step on open loop (plant and model)');
|
||||
legend('plt.iqMeas','plt.iqVolts','plt.actPos','mdl.iqMeas','mdl.iqVolts','mdl.actPos')
|
||||
figure();hold on; plot(t,yp,'DisplayName',ss_plt.OutputName{1})
|
||||
plot(t,ym,'--','DisplayName',ss_plt.OutputName{1});
|
||||
title('step on open loop (plant and model)');
|
||||
%legend('plt.iqMeas','plt.iqVolts','plt.actPos','mdl.iqMeas','mdl.iqVolts','mdl.actPos')
|
||||
legend('location','best')
|
||||
end
|
||||
%w0=abs(poles);
|
||||
%ang=angle(-poles);
|
||||
@@ -126,13 +152,13 @@ function [ssc]=StateSpaceControlDesign(mot,mode)
|
||||
|
||||
V=-1./(Cm*(Am-Bm*K)^-1*Bm); %(from Lineare Regelsysteme2 (Glattfelder) page:173 )
|
||||
if length(V)>1
|
||||
V=V(3); % only the position scaling needed
|
||||
disp(['scaling (should be actPos): ' ss_mdl.OutputName{end}])
|
||||
V=V(end); % only the position scaling needed
|
||||
end
|
||||
|
||||
ss_cl = ss(Am-Bm*K,Bm*V,Cm,0,'Name','space state controller','InputName',ss_mdl.InputName,'OutputName',ss_mdl.OutputName);
|
||||
if bitand(verb,8)
|
||||
% step answer on closed loop with space state controller:
|
||||
t = 0:1E-4:.5;
|
||||
[y,t,x]=lsim(ss_cl,V*u,t,xm0);
|
||||
figure();plot(t,y);title('step on closed loop');
|
||||
end
|
||||
@@ -206,7 +232,8 @@ function [ssc]=StateSpaceControlDesign(mot,mode)
|
||||
ss_oz = c2d(ss_o,Ts);
|
||||
|
||||
%discrete prefilter
|
||||
filt_pos_err_z=c2d(filt_pos_err,Ts);
|
||||
tfDesPos_z=c2d(tfDesPos,Ts);
|
||||
tfPosErr_z=c2d(tfPosErr,Ts);
|
||||
|
||||
if bitand(verb,128)
|
||||
h=bodeplot(filt_pos_err,filt_pos_err_z);
|
||||
@@ -214,11 +241,10 @@ function [ssc]=StateSpaceControlDesign(mot,mode)
|
||||
end
|
||||
|
||||
%state space controller
|
||||
ssc=struct();
|
||||
for k=["Ts","ss_plt","ss_o","ss_oz","filt_pos_err","filt_pos_err_z","V","MaxDac"]
|
||||
for k=["Ts","ss_plt","ss_o","ss_oz","tfDesPos","tfDesPos_z","tfPosErr","tfPosErr_z","V","MaxDac"]
|
||||
ssc=setfield(ssc,k,eval(k));
|
||||
end
|
||||
|
||||
|
||||
mat2py=struct();
|
||||
%[ozA,ozB,ozC,ozD]=ssdata(ss_oz);
|
||||
%[pos_err_num,pos_err_den]=tfdata(filt_pos_err_z);
|
||||
@@ -230,12 +256,9 @@ function [ssc]=StateSpaceControlDesign(mot,mode)
|
||||
mat2py.ozB=ss_oz.B;
|
||||
mat2py.ozC=ss_oz.C;
|
||||
mat2py.ozD=ss_oz.D;
|
||||
mat2py.ozInpName=ss_oz.InputName
|
||||
mat2py.ozOutName=ss_oz.OutputName
|
||||
|
||||
mat2py.pos_err_num=filt_pos_err_z.Numerator{1};
|
||||
mat2py.pos_err_den=filt_pos_err_z.Denominator{1};
|
||||
|
||||
mat2py.ozInpName=ss_oz.InputName;
|
||||
mat2py.ozOutName=ss_oz.OutputName;
|
||||
|
||||
fn=[pwd '/' sprintf( 'ssc%d.mat',mot.id)];
|
||||
save(fn,'-struct','mat2py');
|
||||
disp(['saved ' fn]);
|
||||
@@ -247,23 +270,26 @@ function pf=Prefilt(mot,mode)
|
||||
pf=tf(1,1);
|
||||
case 1 %inverse resonance
|
||||
if mot.id==1
|
||||
den=mot.mdl.num2;%num=1;
|
||||
num=mot.mdl.den2;%den=[1 0 0];
|
||||
den=mot.mdl.num1;%num=1;
|
||||
num=mot.mdl.den1;%den=[1 0 0];
|
||||
pf=tf(num,den);
|
||||
else
|
||||
den=conv(conv(conv(mot.mdl.num2,mot.mdl.num3),mot.mdl.num4),mot.mdl.num5);%num=1;
|
||||
num=conv(conv(conv(mot.mdl.den2,mot.mdl.den3),mot.mdl.den4),mot.mdl.den5);%den=[1 0 0];
|
||||
den=conv(conv(conv(mot.mdl.num1,mot.mdl.num2),mot.mdl.num3),mot.mdl.num4);%num=1;
|
||||
num=conv(conv(conv(mot.mdl.den1,mot.mdl.den2),mot.mdl.den3),mot.mdl.den4);%den=[1 0 0];
|
||||
pf=tf(num,den);
|
||||
end
|
||||
case 2
|
||||
if mot.id==1
|
||||
f=200;w0=f*2*pi; num1=[1 300 w0^2]; den1=[1 200 w0^2];
|
||||
numV=num1;
|
||||
denV=den1;
|
||||
pf=tf(numV,denV);
|
||||
%f=200;w0=f*2*pi; num1=[1 300 w0^2]; den1=[1 200 w0^2];
|
||||
%numV=num1;
|
||||
%denV=den1;
|
||||
%pf=tf(numV,denV);
|
||||
%Lag
|
||||
f=[200 300]; w=f*2*pi; T=1./w;
|
||||
pf=tf([T(1) 1],[T(2) 1]);
|
||||
else
|
||||
%Lag
|
||||
f=[100 200]; w=f*2*pi; T=1./w;
|
||||
f=[200 400]; w=f*2*pi; T=1./w;
|
||||
tf1=tf([T(1) 1],[T(2) 1]);
|
||||
%bo = bodeoptions;
|
||||
%bo.FreqUnits = 'Hz'; bo.MagUnits='abs'; bo.Grid='on';
|
||||
|
||||
@@ -8,7 +8,7 @@ mot=identifyFxFyStage(7);
|
||||
close all;disp('simulate stage closed loop...');
|
||||
for k =1:2
|
||||
[pb]=simFxFyStage(mot{k});sim('stage_closed_loop');
|
||||
f=figure(); h=plot(desPos_actPos.Time,desPos_actPos.Data,'g');
|
||||
f=figure(); h=plot(desPos_actPos.Time,desPos_actPos.Data,'g');grid on;
|
||||
set(h(1),'color','b'); set(h(2),'color',[0 0.5 0]);
|
||||
print(f,sprintf('figures/sim_cl_DTGz_%d',mot{k}.id),'-depsc');
|
||||
f=bodeSamples(desPos_actPos);
|
||||
@@ -16,14 +16,14 @@ for k =1:2
|
||||
end
|
||||
|
||||
close all;disp('simulate observer...');
|
||||
for sscType=0%0:1
|
||||
for sscType=1%0:1
|
||||
for k=1:2
|
||||
[ssc]=StateSpaceControlDesign(mot{k},sscType);sim('observer');
|
||||
f=figure(); h=plot(desPos_actPos.Time,desPos_actPos.Data,'g');
|
||||
f=figure(); h=plot(desPos_actPos.Time,desPos_actPos.Data,'g');grid on;
|
||||
set(h(1),'color','b'); set(h(2),'color',[0 0.5 0]);
|
||||
print(f,sprintf('figures/sim_cl_obs_%d_%d',m,mot{k}.id),'-depsc');
|
||||
print(f,sprintf('figures/sim_cl_obs_%d_%d',sscType,mot{k}.id),'-depsc');
|
||||
f=bodeSamples(desPos_actPos);
|
||||
print(f,sprintf('figures/sim_cl_obs_bode%d_%d',m,mot{k}.id),'-depsc');
|
||||
print(f,sprintf('figures/sim_cl_obs_bode%d_%d',sscType,mot{k}.id),'-depsc');
|
||||
end
|
||||
end
|
||||
disp('document figure generation done');close all;
|
||||
|
||||
@@ -163,11 +163,11 @@ function motCell=identifyFxFyStage(mode)
|
||||
catch
|
||||
return
|
||||
end
|
||||
h=bodeplot(mot.meas,'r',mot.ss_plt(3,1),'g',mot.ss_c1(3,1),'b',mot.ss_d1(3,1),'m',mot.ss_1(2,1),'c',mot.ss_0(2,1),'k',mot.w);
|
||||
h=bodeplot(mot.meas,'r',mot.ss_plt(3,1),'g',mot.ss_cp(3,1),'b',mot.ss_dp(3,1),'m',mot.ss_p(2,1),'c',mot.ss_q(2,1),'k',mot.w);
|
||||
setoptions(h,'FreqUnits','Hz','Grid','on');
|
||||
p=getoptions(h);p.YLim{2}=[-360 90];p.YLimMode='manual';setoptions(h,p);
|
||||
ax=h.getaxes();
|
||||
legend(ax(1),'Location','sw',{'real','plant','c1','d1','1','0'});
|
||||
legend(ax(1),'Location','sw',{'real','plant','cp','dp','p','q'});
|
||||
print(gcf,sprintf('figures/plotBode_%d',mot.id),'-depsc');
|
||||
end
|
||||
|
||||
@@ -175,24 +175,24 @@ function motCell=identifyFxFyStage(mode)
|
||||
|
||||
%current loop iqCmd->iqMeas
|
||||
tfc=tf(mot.mdl.numc,mot.mdl.denc,'InputName','iqCmd','OutputName','iqMeas');
|
||||
|
||||
%simplified current loop iqCmd->iqMeas (first order tf)
|
||||
tfd=tf(mot.mdl.numd,mot.mdl.dend,'InputName','iqCmd','OutputName','iqMeas');
|
||||
|
||||
%force(=current) to velocity and position iqForce->(actVel,actPos), actVel=s*actPos
|
||||
tfp=tf({[mot.mdl.nump 0];mot.mdl.nump},mot.mdl.denp,'InputName','iqForce','OutputName',{'actVel','actPos'});
|
||||
%simplified force(=current) to velocity and position iqForce->(actVel,actPos), actVel=s*actPos
|
||||
tfq=tf({[mot.mdl.numq 0];mot.mdl.numq},mot.mdl.denq,'InputName','iqForce','OutputName',{'actVel','actPos'});
|
||||
%simplified force(=current) to velocity iqForce->(actVel)
|
||||
tfv=tf(mot.mdl.numv,mot.mdl.denv,'InputName','iqForce','OutputName',{'actVel'});
|
||||
|
||||
%resonance iqMeas->iqForce
|
||||
tf2=tf(mot.mdl.num2,mot.mdl.den2,'InputName','iqMeas','OutputName','iqForce');
|
||||
tf1=tf(mot.mdl.num1,mot.mdl.den1,'InputName','iqMeas','OutputName','iqForce');
|
||||
|
||||
%current to position iqForce->actPos
|
||||
tf1_=tf(mot.mdl.num1,mot.mdl.den1,'InputName','iqForce','OutputName','actPos');
|
||||
|
||||
%force(=current) to velocity and position iqForce->(actVel,actPos), actVel=s*actPos
|
||||
tf1=tf({[mot.mdl.num1 0];mot.mdl.num1},mot.mdl.den1,'InputName','iqForce','OutputName',{'actVel','actPos'});
|
||||
|
||||
%simplified force(=current) to velocity and position iqForce->(actVel,actPos), actVel=s*actPos
|
||||
tf0=tf({[mot.mdl.num0 0];mot.mdl.num0},mot.mdl.den0,'InputName','iqForce','OutputName',{'actVel','actPos'});
|
||||
|
||||
%tf1_=tf(mot.mdl.num1,mot.mdl.den1,'InputName','iqForce','OutputName','actPos');
|
||||
|
||||
%check observable/controlable of transfer functions
|
||||
ssLst=["tfc","tfd","tf0","tf1","tf2","tfc*tf1*tf2","tfc*tf1","tfd*tf1","tf1*tf2"];
|
||||
ssLst=["tfc","tfd","tfp","tfq","tfv","tf1","tfc*tfp*tf1","tfc*tfp","tfd*tfp","tfp*tf1","tfc*tfv","tfd*tfv","tfd*tfv*tf1"];
|
||||
sys=[];
|
||||
for s = ssLst
|
||||
eval('sys=ss('+s+');')
|
||||
@@ -201,23 +201,32 @@ function motCell=identifyFxFyStage(mode)
|
||||
|
||||
% sample code:
|
||||
%tfc iqCmd-> iqMeas
|
||||
%tf2 resonance iqMeas->iqForce
|
||||
%tf1 iqForce->(actVel,actPos)
|
||||
%tfp iqForce->(actVel,actPos)
|
||||
%tf1 resonance iqMeas->iqForce
|
||||
%connect(tfc,tf2,'iqCmd','iqForce');
|
||||
%connect(tfc,tf2,'iqCmd',{'iqMeas','iqForce'});
|
||||
%connect(tfc,tf2,tf1_,'iqCmd',{'iqMeas','iqForce','actPos'});
|
||||
%connect(tfc,tf2,tf1_,'iqCmd',{'iqMeas','actPos'});
|
||||
|
||||
|
||||
% best plant approximation
|
||||
% u +-----------+ y
|
||||
%iqCmd------->|1 1|-------> iqMeas
|
||||
% | 2|-------> actVel
|
||||
% | 3|-------> actPos
|
||||
% +-----------+
|
||||
mot.ss_plt=connect(tfc,tf1,tf2,'iqCmd',{'iqMeas','actVel','actPos'});
|
||||
mot.ss_plt=connect(tfc,tfp,tf1,'iqCmd',{'iqMeas','actVel','actPos'});
|
||||
mot.ss_plt.Name='best plant approximation';
|
||||
chkCtrlObsv(mot.ss_plt,'ss_plt fyStage');
|
||||
|
||||
% best plant approximation without friction (always -40dB)
|
||||
% u +-----------+ y
|
||||
%iqCmd------->|1 1|-------> iqMeas
|
||||
% | 2|-------> actVel
|
||||
% | 3|-------> actPos
|
||||
% +-----------+
|
||||
mot.ss_cqr=connect(tfc,tfq,tf1,'iqCmd',{'iqMeas','actVel','actPos'});
|
||||
mot.ss_cqr.Name='plant no friction';
|
||||
chkCtrlObsv(mot.ss_plt,'ss_cqr fyStage');
|
||||
|
||||
%without resonance
|
||||
% u +-----------+ y
|
||||
@@ -225,44 +234,41 @@ function motCell=identifyFxFyStage(mode)
|
||||
% | 2|-------> actVel
|
||||
% | 3|-------> actPos
|
||||
% +-----------+
|
||||
s=tf1.InputName{1};tf1.InputName{1}='iqMeas';
|
||||
mot.ss_c1=connect(tfc,tf1,'iqCmd',{'iqMeas','actVel','actPos'});
|
||||
mot.ss_c1.Name='without resonance';
|
||||
chkCtrlObsv(mot.ss_c1,'ss_c1 fyStage');
|
||||
tf1.InputName{1}=s;%restore
|
||||
s=tfp.InputName{1};tfp.InputName{1}='iqMeas';
|
||||
mot.ss_cp=connect(tfc,tfp,'iqCmd',{'iqMeas','actVel','actPos'});
|
||||
mot.ss_cp.Name='without resonance';
|
||||
chkCtrlObsv(mot.ss_cp,'ss_cp fyStage');
|
||||
tfp.InputName{1}=s;%restore
|
||||
|
||||
|
||||
%simplified current, without resonance
|
||||
% u +-----------+ y
|
||||
%iqCmd------->|1 1|-------> iqMeas
|
||||
% | 2|-------> actVel
|
||||
% | 3|-------> actPos
|
||||
% +-----------+
|
||||
s=tf1.InputName{1};tf1.InputName{1}='iqMeas';
|
||||
mot.ss_d1=connect(tfd,tf1,'iqCmd',{'iqMeas','actVel','actPos'});
|
||||
mot.ss_d1.Name='simplified current, without resonance';
|
||||
chkCtrlObsv(mot.ss_d1,'ss_d1 fyStage');
|
||||
tf1.InputName{1}=s;%restore
|
||||
|
||||
s=tfp.InputName{1};tfp.InputName{1}='iqMeas';
|
||||
mot.ss_dp=connect(tfd,tfp,'iqCmd',{'iqMeas','actVel','actPos'});
|
||||
mot.ss_dp.Name='simplified current, without resonance';
|
||||
chkCtrlObsv(mot.ss_dp,'ss_dp fyStage');
|
||||
tfp.InputName{1}=s;%restore
|
||||
|
||||
%no current loop, no resonance
|
||||
% u +-----------+ y
|
||||
%iqCmd------->|1 1|-------> actVel
|
||||
% | 2|-------> actPos
|
||||
% +-----------+
|
||||
mot.ss_1=ss(tf1);
|
||||
mot.ss_1.Name='no current loop, no resonance';
|
||||
chkCtrlObsv(mot.ss_1,'ss_1 fyStage');
|
||||
|
||||
mot.ss_p=ss(tfp);
|
||||
mot.ss_p.Name='no current loop, no resonance';
|
||||
chkCtrlObsv(mot.ss_p,'ss_p fyStage');
|
||||
|
||||
%simplified mechanics, no current loop, no resonance
|
||||
% u +-----------+ y
|
||||
%iqCmd------->|1 1|-------> actVel
|
||||
% | 2|-------> actPos
|
||||
% +-----------+
|
||||
mot.ss_0=ss(tf0);
|
||||
mot.ss_0.Name='simplified mechanics, no current loop, no resonance';
|
||||
chkCtrlObsv(mot.ss_0,'ss_0 fyStage');
|
||||
mot.ss_q=ss(tfq);
|
||||
mot.ss_q.Name='simplified mechanics, no current loop, no resonance';
|
||||
chkCtrlObsv(mot.ss_q,'ss_q fyStage');
|
||||
|
||||
%h=bodeplot(mot.meas,'r',mot.tf4_2,'b',mot.tf6_4,'g');
|
||||
%h=bodeplot(mot.meas,'r',mot.tf2_0,'b',mot.tf_mdl,'g',mot.w);
|
||||
@@ -272,58 +278,64 @@ function motCell=identifyFxFyStage(mode)
|
||||
function mot=fxStage(mot)
|
||||
%current loop iqCmd->iqMeas
|
||||
tfc=tf(mot.mdl.numc,mot.mdl.denc,'InputName','iqCmd','OutputName','iqMeas');
|
||||
|
||||
%simplified current loop iqCmd->iqMeas (first order tf)
|
||||
tfd=tf(mot.mdl.numd,mot.mdl.dend,'InputName','iqCmd','OutputName','iqMeas');
|
||||
|
||||
%force(=current) to velocity and position iqForce->(actVel,actPos), actVel=s*actPos
|
||||
tfp=tf({[mot.mdl.nump 0];mot.mdl.nump},mot.mdl.denp,'InputName','iqForce','OutputName',{'actVel','actPos'});
|
||||
%simplified force(=current) to velocity and position iqForce->(actVel,actPos), actVel=s*actPos
|
||||
tfq=tf({[mot.mdl.numq 0];mot.mdl.numq},mot.mdl.denq,'InputName','iqForce','OutputName',{'actVel','actPos'});
|
||||
|
||||
%resonance iqMeas->iqForce
|
||||
tf2=tf(mot.mdl.num2,mot.mdl.den2,'InputName','iqMeas','OutputName','iqF1');
|
||||
tf1=tf(mot.mdl.num1,mot.mdl.den1,'InputName','iqMeas','OutputName','iqF1');
|
||||
%resonance iqMeas->iqForce
|
||||
tf3=tf(mot.mdl.num3,mot.mdl.den3,'InputName','iqF1','OutputName','iqF2');
|
||||
tf2=tf(mot.mdl.num2,mot.mdl.den2,'InputName','iqF1','OutputName','iqF2');
|
||||
%resonance iqMeas->iqForce
|
||||
tf4=tf(mot.mdl.num4,mot.mdl.den4,'InputName','iqF2','OutputName','iqF3');
|
||||
tf3=tf(mot.mdl.num3,mot.mdl.den3,'InputName','iqF2','OutputName','iqF3');
|
||||
%resonance iqMeas->iqForce
|
||||
tf5=tf(mot.mdl.num5,mot.mdl.den5,'InputName','iqF3','OutputName','iqForce');
|
||||
tf4=tf(mot.mdl.num4,mot.mdl.den4,'InputName','iqF3','OutputName','iqForce');
|
||||
|
||||
%current to position iqForce->actPos
|
||||
tf1_=tf(mot.mdl.num1,mot.mdl.den1,'InputName','iqForce','OutputName','actPos');
|
||||
|
||||
%force(=current) to velocity and position iqForce->(actVel,actPos), actVel=s*actPos
|
||||
tf1=tf({[mot.mdl.num1 0];mot.mdl.num1},mot.mdl.den1,'InputName','iqForce','OutputName',{'actVel','actPos'});
|
||||
|
||||
%simplified force(=current) to velocity and position iqForce->(actVel,actPos), actVel=s*actPos
|
||||
tf0=tf({[mot.mdl.num0 0];mot.mdl.num0},mot.mdl.den0,'InputName','iqForce','OutputName',{'actVel','actPos'});
|
||||
%tf1_=tf(mot.mdl.num1,mot.mdl.den1,'InputName','iqForce','OutputName','actPos');
|
||||
|
||||
%check observable/controlable of transfer functions
|
||||
ssLst=["tfc","tfd","tf0","tf1","tf2","tf3","tf4","tf5",...
|
||||
"tfc*tf1*tf2","tfc*tf1","tfd*tf1","tf1*tf2","tf1*tf2*tf3"];
|
||||
ssLst=["tfc","tfd","tfp","tfq","tf1","tf2","tf3","tf4",...
|
||||
"tfc*tfp*tf1","tfc*tfp","tfd*tfp","tfp*tf1","tfd*tfp*tf1","tfc*tf1","tfd*tf1","tfc*tf1*tf2","tfd*tf1*tf2"];
|
||||
sys=[];
|
||||
for s = ssLst
|
||||
eval('sys=ss('+s+');')
|
||||
chkCtrlObsv(sys,char(s));
|
||||
end
|
||||
|
||||
|
||||
% best plant approximation
|
||||
% u +-----------+ y
|
||||
%iqCmd------->|1 1|-------> iqMeas
|
||||
% | 2|-------> actVel
|
||||
% | 3|-------> actPos
|
||||
% +-----------+
|
||||
mot.ss_plt=connect(tfc,tf1,tf2,tf3,tf4,tf5,'iqCmd',{'iqMeas','actVel','actPos'});
|
||||
mot.ss_plt=connect(tfc,tfp,tf1,tf2,tf3,tf4,'iqCmd',{'iqMeas','actVel','actPos'});
|
||||
chkCtrlObsv(mot.ss_plt,'ss_plt fxStage');
|
||||
|
||||
% best plant approximation without friction (always -40dB)
|
||||
% u +-----------+ y
|
||||
%iqCmd------->|1 1|-------> iqMeas
|
||||
% | 2|-------> actVel
|
||||
% | 3|-------> actPos
|
||||
% +-----------+
|
||||
mot.ss_cqr=connect(tfc,tfq,tf1,tf2,tf3,tf4,'iqCmd',{'iqMeas','actVel','actPos'});
|
||||
mot.ss_cqr.Name='plant no friction';
|
||||
chkCtrlObsv(mot.ss_cqr,'ss_plt0 fxStage');
|
||||
|
||||
%without resonance
|
||||
% u +-----------+ y
|
||||
%iqCmd------->|1 1|-------> iqMeas
|
||||
% | 2|-------> actVel
|
||||
% | 3|-------> actPos
|
||||
% +-----------+
|
||||
s=tf1.InputName{1};tf1.InputName{1}='iqMeas';
|
||||
mot.ss_c1=connect(tfc,tf1,'iqCmd',{'iqMeas','actVel','actPos'});
|
||||
chkCtrlObsv(mot.ss_c1,'ss_c1 fxStage');
|
||||
tf1.InputName{1}=s;%restore
|
||||
|
||||
s=tfp.InputName{1};tfp.InputName{1}='iqMeas';
|
||||
mot.ss_cp=connect(tfc,tfp,'iqCmd',{'iqMeas','actVel','actPos'});
|
||||
chkCtrlObsv(mot.ss_cp,'ss_cp fxStage');
|
||||
tfp.InputName{1}=s;%restore
|
||||
|
||||
%simplified current, without resonance
|
||||
% u +-----------+ y
|
||||
@@ -331,28 +343,26 @@ function motCell=identifyFxFyStage(mode)
|
||||
% | 2|-------> actVel
|
||||
% | 3|-------> actPos
|
||||
% +-----------+
|
||||
s=tf1.InputName{1};tf1.InputName{1}='iqMeas';
|
||||
mot.ss_d1=connect(tfd,tf1,'iqCmd',{'iqMeas','actVel','actPos'});
|
||||
chkCtrlObsv(mot.ss_d1,'ss_d1 fxStage');
|
||||
tf1.InputName{1}=s;%restore
|
||||
|
||||
|
||||
s=tfp.InputName{1};tfp.InputName{1}='iqMeas';
|
||||
mot.ss_dp=connect(tfd,tfp,'iqCmd',{'iqMeas','actVel','actPos'});
|
||||
chkCtrlObsv(mot.ss_dp,'ss_dp fxStage');
|
||||
tfp.InputName{1}=s;%restore
|
||||
|
||||
%no current loop, no resonance
|
||||
% u +-----------+ y
|
||||
%iqCmd------->|1 1|-------> actVel
|
||||
% | 2|-------> actPos
|
||||
% +-----------+
|
||||
mot.ss_1=ss(tf1);
|
||||
chkCtrlObsv(mot.ss_1,'ss_1 fxStage');
|
||||
mot.ss_p=ss(tfp);
|
||||
chkCtrlObsv(mot.ss_p,'ss_p fxStage');
|
||||
|
||||
|
||||
%simplified mechanics, no current loop, no resonance
|
||||
% u +-----------+ y
|
||||
%iqCmd------->|1 1|-------> actVel
|
||||
% | 2|-------> actPos
|
||||
% +-----------+
|
||||
mot.ss_0=ss(tf0);
|
||||
chkCtrlObsv(mot.ss_0,'ss_0 fxStage');
|
||||
mot.ss_q=ss(tfq);
|
||||
chkCtrlObsv(mot.ss_q,'ss_q fxStage');
|
||||
|
||||
plotBode(mot)
|
||||
end
|
||||
|
||||
Binary file not shown.
@@ -6,24 +6,27 @@ function [pb]=simFxFyStage(mot)
|
||||
%the returned variable pb contains varous parameters:
|
||||
% Kp,Kvfb,Ki,Kvff,Kaff,MaxInt,mot_num,mot_den,Ts,MaxDac,MaxPosErr,A,B,C,D
|
||||
|
||||
Ts=2E-4; % 0.2ms=5kHz
|
||||
MaxDac=2011.968;
|
||||
MaxPosErr=10000;
|
||||
pb=struct(...
|
||||
'Ts', 2E-4, ... % 0.2ms=5kHz
|
||||
'MaxDac' ,2011.968, ...
|
||||
'MaxPosErr', 10000);
|
||||
if mot.id==1
|
||||
%!motor_servo(mot=1,ctrl='ServoCtrl',Kp=25,Kvfb=400,Ki=0.02,Kvff=350,Kaff=5000,MaxInt=1000)
|
||||
%!motor(mot=1,dirCur=0,contCur=800,peakCur=2400,timeAtPeak=1,IiGain=5,IpfGain=8,IpbGain=8,JogSpeed=10.,numPhase=3,invDir=True,servo=None,PhasePosSf=1./81250,PhaseFindingDac=100,PhaseFindingTime=50,SlipGain=0,AdvGain=0,PwmSf=10000,FatalFeLimit=200,WarnFeLimit=100,InPosBand=2,homing='enc-index')
|
||||
Kp=25;Kvfb=400;Ki=0.02;Kvff=350;Kaff=5000;MaxInt=1000;
|
||||
pb.Kp=25;pb.Kvfb=400;pb.Ki=0.02;pb.Kvff=350;pb.Kaff=5000;pb.MaxInt=1000;
|
||||
else
|
||||
%!motor_servo(mot=2,ctrl='ServoCtrl',Kp=22,Kvfb=350,Ki=0.02,Kvff=240,Kaff=1500,MaxInt=1000)
|
||||
%!motor(mot=2,dirCur=0,contCur=800,peakCur=2400,timeAtPeak=1,IiGain=5,IpfGain=8,IpbGain=8,JogSpeed=10.,numPhase=3,invDir=True,servo=None,PhasePosSf=1./81250,PhaseFindingDac=100,PhaseFindingTime=50,SlipGain=0,AdvGain=0,PwmSf=10000,FatalFeLimit=200,WarnFeLimit=100,InPosBand=2,homing='enc-index')
|
||||
%Kp=22;Kvfb=350;Ki=0.02;Kvff=240;Kaff=1500;MaxInt=1000;
|
||||
Kp=22;Kvfb=350;Ki=0.02;Kvff=240;Kaff=3500;MaxInt=1000;
|
||||
end
|
||||
ss_plt=mot.ss_plt;
|
||||
pb=struct();
|
||||
for k=["Kp","Kvfb","Ki","Kvff","Kaff","MaxInt","Ts","MaxDac","MaxPosErr","ss_plt"]
|
||||
pb=setfield(pb,k,eval(k));
|
||||
pb.Kp=22;pb.Kvfb=350;pb.Ki=0.02;pb.Kvff=240;pb.Kaff=3500;pb.MaxInt=1000;
|
||||
end
|
||||
pb.ss_plt=mot.ss_plt; pb.sel={3,[3]};
|
||||
%pb.ss_plt=mot.ss_plt0; pb.sel={3,[3]};
|
||||
%pb.ss_plt=mot.ss_c1; pb.sel={3,[3]};
|
||||
%pb.ss_plt=mot.ss_d1; pb.sel={3,[3]};
|
||||
%pb.ss_plt=mot.ss_1; pb.sel={2,[2]};
|
||||
%pb.ss_plt=mot.ss_p; pb.sel={2,[2]};
|
||||
|
||||
%mdlName='stage_closed_loop';
|
||||
%open(mdlName)
|
||||
%sim(mdlName)
|
||||
|
||||
Binary file not shown.
@@ -1,50 +1,16 @@
|
||||
function [ssc]=testObserver(mot)
|
||||
function [ssc]=testObserver()
|
||||
%http://ctms.engin.umich.edu/CTMS/index.php?example=Introduction§ion=ControlStateSpace
|
||||
mdl=1; % 0:hovering ball 1:fast stage
|
||||
if mdl==0
|
||||
A = [ 0 1 0
|
||||
980 0 -2.8
|
||||
100
|
||||
0 0 -100 ];
|
||||
A = [ 0 1 0
|
||||
980 0 -2.8
|
||||
0 0 -100 ];
|
||||
|
||||
B = [ 0
|
||||
0];
|
||||
B = [ 0
|
||||
0
|
||||
100 ];
|
||||
|
||||
C = [ 1 0 0 ];
|
||||
D=0;
|
||||
else
|
||||
[A,B,C,D]=ssdata(mot.ssMdl);
|
||||
C=C(3,:);D=D(3);
|
||||
numc=(mot.mdl.numc);
|
||||
denc=(mot.mdl.denc);
|
||||
num1=(mot.mdl.num1);
|
||||
den1=(mot.mdl.den1);
|
||||
|
||||
g1=tf(numc,denc); % iqCmd->iqMeas
|
||||
s1=ss(g1);
|
||||
s1.C=[s1.C; 1E5* 2.4E-3 1E-3*s1.C(2)*8.8]; % add output iqVolts: iqVolts= i_meas*R+i_meas'*L 2.4mH 8.8Ohm (took random scaling values)
|
||||
g2=tf(num1,den1); %iqMeas->ActPos without resonance frequencies
|
||||
s2=ss(g2);
|
||||
s3=append(s1,s2);
|
||||
s3.A(3,2)=s3.C(1,2)*s3.B(3,2);
|
||||
|
||||
%d=.77;w0=9100; %1.44kHz -> T0=6.9046e-04
|
||||
%numc=[w0^2];
|
||||
%denc=[1 d*2*w0 w0^2];
|
||||
%T0=6E-4; %w=1/1E-3=1E3 -> f=w/(2pi)=1000/6.2=159Hz
|
||||
%numc=[1];
|
||||
%denc=[T0^2 d*2*T0 1];
|
||||
%bode(numc,denc)
|
||||
|
||||
num=conv(numc,num1);%num=1;
|
||||
den=conv(denc,den1);%den=[1 0 0];
|
||||
g4=tf(num,den); %iqMeas->ActPos
|
||||
s4=ss(g4);
|
||||
mot.ssMdl
|
||||
s3
|
||||
s4
|
||||
[A,B,C,D]=ssdata(s4);
|
||||
end
|
||||
C = [ 1 0 0 ];
|
||||
D=0;
|
||||
|
||||
Ap=A;Am=A;
|
||||
Bp=B;Bm=B;
|
||||
@@ -52,159 +18,79 @@ function [ssc]=testObserver(mot)
|
||||
Dp=D;Dm=D;Dt=D;
|
||||
|
||||
poles = eig(A);
|
||||
disp(poles);
|
||||
poles
|
||||
|
||||
|
||||
if mdl==0
|
||||
t = 0:0.01:2;
|
||||
u = zeros(size(t));
|
||||
x0 = [0.01 0 0];
|
||||
t = 0:0.01:2;
|
||||
u = zeros(size(t));
|
||||
x0 = [0.01 0 0];
|
||||
|
||||
sys = ss(A,B,C,D);
|
||||
sys = ss(A,B,C,D);
|
||||
|
||||
[y,t,x] = lsim(sys,u,t,x0);
|
||||
plot(t,y)
|
||||
title('Open-Loop Response to Non-Zero Initial Condition')
|
||||
xlabel('Time (sec)')
|
||||
ylabel('Ball Position (m)')
|
||||
[y,t,x] = lsim(sys,u,t,x0);
|
||||
plot(t,y)
|
||||
title('Open-Loop Response to Non-Zero Initial Condition')
|
||||
xlabel('Time (sec)')
|
||||
ylabel('Ball Position (m)')
|
||||
|
||||
p1 = -10 + 10i; p2 = -10 - 10i; p3 = -50;
|
||||
K = place(A,B,[p1 p2 p3]);
|
||||
sys_cl = ss(A-B*K,B,C,0);
|
||||
lsim(sys_cl,u,t,x0);
|
||||
xlabel('Time (sec)')
|
||||
ylabel('Ball Position (m)')
|
||||
p1 = -10 + 10i; p2 = -10 - 10i; p3 = -50;
|
||||
K = place(A,B,[p1 p2 p3]);
|
||||
sys_cl = ss(A-B*K,B,C,0);
|
||||
lsim(sys_cl,u,t,x0);
|
||||
xlabel('Time (sec)')
|
||||
ylabel('Ball Position (m)')
|
||||
|
||||
p1 = -20 + 20i; p2 = -20 - 20i; p3 = -100;
|
||||
P=[p1 p2 p3];
|
||||
K = place(A,B,P);
|
||||
sys_cl = ss(A-B*K,B,C,0);
|
||||
lsim(sys_cl,u,t,x0);
|
||||
xlabel('Time (sec)')
|
||||
ylabel('Ball Position (m)')
|
||||
|
||||
t = 0:0.01:2;
|
||||
u = 0.001*ones(size(t));
|
||||
t = 0:0.01:2;
|
||||
u = 0.001*ones(size(t));
|
||||
|
||||
sys_cl = ss(A-B*K,B,C,0);
|
||||
sys_cl = ss(A-B*K,B,C,0);
|
||||
|
||||
lsim(sys_cl,u,t);
|
||||
xlabel('Time (sec)')
|
||||
ylabel('Ball Position (m)')
|
||||
axis([0 2 -4E-6 0])
|
||||
lsim(sys_cl,u,t);
|
||||
xlabel('Time (sec)')
|
||||
ylabel('Ball Position (m)')
|
||||
axis([0 2 -4E-6 0])
|
||||
|
||||
V=-1./(Cm*(Am-Bm*K)^-1*Bm); %(from Lineare Regelsysteme2 (Glattfelder) page:173 )
|
||||
lsim(sys_cl,V*u,t)
|
||||
title('Linear Simulation Results (with Nbar)')
|
||||
xlabel('Time (sec)')
|
||||
ylabel('Ball Position (m)')
|
||||
axis([0 2 0 1.2*10^-3])
|
||||
else
|
||||
x0 = [0 0 0 0];
|
||||
p1=-63+2.80i;
|
||||
p2=-62+1.50i;
|
||||
P=[p1 p1' p2 p2'];
|
||||
P=[-4000+100j -4000-100j -500+10j -500-10j];
|
||||
K = place(A,B,P);
|
||||
Ts=1/5000
|
||||
t = 0:Ts:2;
|
||||
u = 0.001*ones(size(t));
|
||||
V=-1./(Cm*(Am-Bm*K)^-1*Bm); %(from Lineare Regelsysteme2 (Glattfelder) page:173 )
|
||||
sys_cl = ss(A-B*K,B*V,C,0);
|
||||
V=-1./(Cm*(Am-Bm*K)^-1*Bm); %(from Lineare Regelsysteme2 (Glattfelder) page:173 )
|
||||
lsim(sys_cl,V*u,t)
|
||||
title('Linear Simulation Results (with Nbar)')
|
||||
xlabel('Time (sec)')
|
||||
ylabel('Ball Position (m)')
|
||||
axis([0 2 0 1.2*10^-3])
|
||||
|
||||
lsim(sys_cl,u,t,x0);
|
||||
xlabel('Time (sec)')
|
||||
ylabel('Motor Position (m)')
|
||||
axis([0 2 0 1.2E-3])
|
||||
end
|
||||
op1 = -100;
|
||||
op2 = -101;
|
||||
op3 = -102;
|
||||
OP=[op1,op2,op3];
|
||||
%OP=P*2;
|
||||
L = place(A',C',OP)';
|
||||
At = [ A-B*K B*K
|
||||
zeros(size(A)) A-L*C ];
|
||||
Bt = [ B*V
|
||||
zeros(size(B)) ];
|
||||
Ct = [ C zeros(size(C)) ];
|
||||
|
||||
if mdl==0
|
||||
op1 = -100;
|
||||
op2 = -101;
|
||||
op3 = -102;
|
||||
OP=[op1,op2,op3];
|
||||
OP=P*2;
|
||||
L = place(A',C',OP)';
|
||||
At = [ A-B*K B*K
|
||||
zeros(size(A)) A-L*C ];
|
||||
Bt = [ B*V
|
||||
zeros(size(B)) ];
|
||||
Ct = [ C zeros(size(C)) ];
|
||||
sys = ss(At,Bt,Ct,0);
|
||||
%lsim(sys,zeros(size(t)),t,[x0 x0]);
|
||||
lsim(sys,u,t,[x0 x0]);
|
||||
title('Linear Simulation Results (with observer)')
|
||||
xlabel('Time (sec)')
|
||||
ylabel('Ball Position (m)')
|
||||
|
||||
sys = ss(At,Bt,Ct,0);
|
||||
%lsim(sys,zeros(size(t)),t,[x0 x0]);
|
||||
lsim(sys,u,t,[x0 x0]*0);
|
||||
title('Linear Simulation Results (with observer)')
|
||||
xlabel('Time (sec)')
|
||||
ylabel('Ball Position (m)')
|
||||
else
|
||||
OP=P*2;
|
||||
L = place(A',C',OP)';
|
||||
At = [ A-B*K B*K
|
||||
zeros(size(A)) A-L*C ];
|
||||
Bt = [ B*V
|
||||
zeros(size(B)) ];
|
||||
Ct = [ C zeros(size(C)) ];
|
||||
t = 0:1E-6:0.1;
|
||||
x0 = [0.01 0.5 -5];
|
||||
[y,t,x] = lsim(sys,zeros(size(t)),t,[x0 x0]);
|
||||
|
||||
sys = ss(At,Bt,Ct,0);
|
||||
lsim(sys,u,t,[x0 x0]*0);
|
||||
title('Linear Simulation Results (with observer)')
|
||||
xlabel('Time (sec)')
|
||||
ylabel('Motor Position (m)')
|
||||
axis([0 2 0 1.2E-3])
|
||||
end
|
||||
|
||||
if mdl==0
|
||||
t = 0:1E-6:0.1;
|
||||
x0 = [0.01 0.5 -5];
|
||||
[y,t,x] = lsim(sys,zeros(size(t)),t,[x0 x0]);
|
||||
n = 3;
|
||||
e = x(:,n+1:end);
|
||||
x = x(:,1:n);
|
||||
x_est = x - e;
|
||||
|
||||
n = 3;
|
||||
e = x(:,n+1:end);
|
||||
x = x(:,1:n);
|
||||
x_est = x - e;
|
||||
% Save state variables explicitly to aid in plotting
|
||||
h = x(:,1); h_dot = x(:,2); i = x(:,3);
|
||||
h_est = x_est(:,1); h_dot_est = x_est(:,2); i_est = x_est(:,3);
|
||||
|
||||
% Save state variables explicitly to aid in plotting
|
||||
h = x(:,1); h_dot = x(:,2); i = x(:,3);
|
||||
h_est = x_est(:,1); h_dot_est = x_est(:,2); i_est = x_est(:,3);
|
||||
|
||||
plot(t,h,'-r',t,h_est,':r',t,h_dot,'-b',t,h_dot_est,':b',t,i,'-g',t,i_est,':g')
|
||||
legend('h','h_{est}','hdot','hdot_{est}','i','i_{est}')
|
||||
xlabel('Time (sec)')
|
||||
end
|
||||
|
||||
|
||||
%discrete
|
||||
Ts=1/5000; % deltatau std. frq. is 5kHz
|
||||
%The discrete system works with sampling >100Hz,. the bigger the frequency the better the result
|
||||
%With sampling = 80Hz hte system already becomes instable.
|
||||
ss_t=ss(At,Bt,Ct,Dt);
|
||||
%figure();pzplot(ss_t)
|
||||
ss_tz=c2d(ss_t,Ts);
|
||||
%figure();pzplot(ss_tz)
|
||||
[Atz,Btz,Ctz,Dtz]=ssdata(ss_tz);
|
||||
|
||||
|
||||
[Apz,Bpz,Cpz,Dpz]=ssdata(c2d(ss(Ap,Bp,Cp,Dp),Ts));
|
||||
[Amz,Bmz,Cmz,Dmz]=ssdata(c2d(ss(Am,Bm,Cm,Dm),Ts));
|
||||
|
||||
|
||||
Ao=Am-L*Cm;
|
||||
Bo=[Bm L];
|
||||
Co=K;
|
||||
Do=zeros(size(Co,1),size(Bo,2));
|
||||
|
||||
[Aoz,Boz,Coz,Doz]=ssdata(c2d(ss(Ao,Bo,Co,Do),Ts));
|
||||
|
||||
|
||||
%state space controller
|
||||
ssc=struct();
|
||||
for k=["Ts","At","Bt","Ct","Dt","Atz","Btz","Ctz","Dtz","Ap","Bp","Cp","Dp","Am","Bm","Cm","Dm","Ao","Bo","Co","Do","Apz","Bpz","Cpz","Dpz","Aoz","Boz","Coz","Doz","V","K","L"]
|
||||
ssc=setfield(ssc,k,eval(k));
|
||||
end
|
||||
mdlName='testObserverSim';
|
||||
open(mdlName);
|
||||
|
||||
|
||||
|
||||
plot(t,h,'-r',t,h_est,':r',t,h_dot,'-b',t,h_dot_est,':b',t,i,'-g',t,i_est,':g')
|
||||
legend('h','h_{est}','hdot','hdot_{est}','i','i_{est}')
|
||||
xlabel('Time (sec)')
|
||||
K,L,V
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user