From eda8caf98524ce5207354dbbe808374cdf5415a7 Mon Sep 17 00:00:00 2001 From: Thierry Zamofing Date: Tue, 19 Mar 2019 09:10:28 +0100 Subject: [PATCH] wip --- MXfastStageDoc/MXfastStage.tex | 33 ++++-- MXfastStageDoc/traj1.eps | 2 +- MXfastStageDoc/traj2.eps | 2 +- MXfastStageDoc/traj3.eps | 2 +- matlab/DeltaTauOptimizer.m | 122 +++++++++++---------- matlab/DeltaTauParam.m | 188 ++++++++++++++++++++++++--------- matlab/DeltaTauSim.slx | Bin 43257 -> 43194 bytes python/MXMotion.py | 11 +- python/helicalscan.py | 10 +- python/shapepath.py | 40 +++---- 10 files changed, 268 insertions(+), 142 deletions(-) diff --git a/MXfastStageDoc/MXfastStage.tex b/MXfastStageDoc/MXfastStage.tex index 60ce4c6..f56a062 100644 --- a/MXfastStageDoc/MXfastStage.tex +++ b/MXfastStageDoc/MXfastStage.tex @@ -71,20 +71,22 @@ The settings of the PwmSf is done in the templates of gpasciiCommander: PwmSf=15134.8909 # =.95*16384. PMAC3-style DSPGATE3 ASIC is being used for the output, the counter moves between +/- 16384. PwmSf is typically set to 95% of 16384 \end{verbatim} -Nevertheless the documentation is confusing and the PwmSf is not directly a value to scale from cvurrent to voltage. Therefore PwmSf is measured to convert idCmd bits values to idVolts bits in section \ref{sec:measCurStep}\\ +Nevertheless the documentation is confusing and the PwmSf is not directly a value to scale from current to voltage. Therefore PwmSf is measured roughly in this section to convert idCmd bits values to idVolts bits\\ -The overall aplification $iqCmd \rightarrow iqVolts$ is approx. 18.2. +The overall aplification $iqCmd \rightarrow iqVolts$ is approx. 7400/406=18.2. (s.a. figure \ref{fig:IqCmd->IqVolts}). \begin{figure}[h!] \includegraphics[trim=0cm 1.5cm 0cm 1cm,scale=.45]{../python/MXTuning/19_01_29/img/iqCmd_TF0.eps} \includegraphics[trim=0cm 1.5cm 0cm 1cm,scale=.45]{../python/MXTuning/19_01_29/img/iqCmd_TF1.eps} \caption{IqCmd->IqMeas of motor 1 (bode same for both motors)} +\label{fig:IqCmd->IqMeas} \end{figure} \begin{figure}[h!] \includegraphics[trim=0cm 1.5cm 0cm 1cm,scale=.45]{../python/MXTuning/19_01_29/img/iqCmd_TF2.eps} \includegraphics[trim=0cm 1.5cm 0cm 1cm,scale=.45]{../python/MXTuning/19_01_29/img/iqCmd_TF3.eps} \caption{IqCmd->IqVolts of motor 1 (bode same for both motors)} +\label{fig:IqCmd->IqVolts} \end{figure} \FloatBarrier @@ -304,13 +306,13 @@ Solving in Laplace space:\\ $iqVolts=(R+Ls)\cdot iqMeas$\\ $s \cdot iqMeas =\frac{1}{L}iqVolts - \frac{R}{L}iqMeas$\\ -Transferfunction open loop of $G_1(s)=iqVolts \rightarrow iqCmd$\\ +Transferfunction open loop of $G_1(s)=iqVolts \rightarrow iqMeas$\\ using Masons rule: \url{https://en.wikipedia.org/wiki/Mason's_gain_formula}: \[ -G_1(s)=\frac{y_{out}}{y_{in}}=\frac{iqCmd}{iqVolts}= +G_1(s)=\frac{y_{out}}{y_{in}}=\frac{iqMeas}{iqVolts}= \frac{\frac{1}{Ls}}{1+ \frac{R}{Ls}} = \frac{1}{Ls+R} = \frac{k}{1+Ts} = \frac{\frac{1}{R}}{1+\frac{L}{R}s} \] @@ -321,7 +323,7 @@ Transferfunction closed loop of $G_2(s)=iqCmd \rightarrow iqMeas$: \begin{aligned} &\text{with}\quad a=Ipf+\frac{Li}{s} \quad -b=PwmSF \cdot G(s) \quad +b=PwmSF \cdot G_1(s) \quad c=Ipb \quad d=1\\ &\text{using Masons rule:} \quad G_2(s)=\frac{ab}{1+bc+abd}\\ @@ -581,7 +583,7 @@ The black line on figure \ref{fig:mot_open} shows the concatenate transfer funct \section{Enhance controller with simulation and implementation} -It has to be checked if the model matches the real stage. Therefore simulations in MATLAB have been done to validate the identification process of the stages. Further simulations and implementations with better controller types were tested +It has to be checked if the model matches the real stage. Therefore simulations in MATLAB have been done to validate the identification process of the stages. Further simulations and implementations with better controller types were tested. \subsection{chirp sine closed loop with simulink model} @@ -627,7 +629,7 @@ Therefore for better regulation quality more sophisticated controllers as state A standard PID controller uses feedback to detect errors between the desired position and the actual position and applies corrective commands to compensate for those errors. Although PID control is the most common type of industrial controller, it does have limitations. The idea of a state space controller with observer is to have a model of the plant which follows all internal states of the real plant. All these internal states of the model can then be used states to build an optimal controller. -opposite to the PID controller which is a ‘black-box design’, the state space controller with observer is a ‘white box design’. (Figure \ref{fig:observer1})\\ +Opposite to the PID controller which is a ‘black-box design’, the state space controller with observer is a ‘white box design’. (Figure \ref{fig:observer1})\\ \begin{figure}[h!] \centering @@ -892,6 +894,11 @@ This works good and enhanced the trajectory following at high frequencies. But t Much more important is to focus now on the closed loop filters B, C/D and E. +\begin{tcolorbox}[colback=red!5!white,colframe=red!75!black,colbacktitle=red!50,coltitle=black,title=TODO] +\begin{verbatim} +write conclusion and best parameters +\end{verbatim} +\end{tcolorbox} % Motor 2:11.84Hz 0dB % Kaff = 1/((11.84*2*np.pi)**2/5000/5000) = 4517.278506241804 @@ -911,18 +918,18 @@ Much more important is to focus now on the closed loop filters B, C/D and E. In this section the trajectory planing of the x and y Parker stages in the 'shapepath' mode is analyzed. In the 'shapepath' mode the goal is to place crystals with sizes close to 1um at the beam position when the FEL pulse arrives. The FEL pulse is so short that motion at the impact time is not relevant. The FEL repetition rate is 25, 50 or 100Hz.\\ The trajectory planing is very important to achieve good timing and positioning precision. This is crucial for high crystal hit rate of the FEL. DeltaTau has the possibility to use pvt (position-velocity-time) motion. In that mode one can tell to pass a given point at certain time with a certain speed.\\ -PVT motion is a function of $ax^3+bx^2+cx+d$ between 2 points. Additionally the derivate (velocity) in a point is same for the incoming and outgoing trajectory segment.\\ +PVT motion is a function of $ax^3+bx^2+cx+d$ between 2 points. Additionally the derivate (velocity) in a junction point is same for the incoming and outgoing trajectory segment.\\ Different approaches have been tested and compared to maximize the yield: \paragraph{p0t motion:} -A first very trivial approach is to stop at each point. This stop and go motion will induce a lot of vibrations because it always accelerates and decelerates. This can also have a negative impact to the sample. Therefore other approaches with smoother trajectory and higher speed at impact time will be compared. +A first very trivial approach is to stop at each point. This stop and go motion will induce a lot of vibrations because it always accelerates and decelerates. This can also have a negative impact to the sample position. Therefore other approaches with smoother trajectory and higher speed at impact time will be compared. \paragraph{pvt motion:} Assume that the time between two points is $t_s$ ms. The velocity in the point $p_n$ is set in a way that the distance from point $p_{n-1}$ to $p_{n+1}$ is reached in $2 \cdot t_s$ ms. This approach gives a very smooth trajectory planing. But it contains still some high frequency components that can be avoided. \paragraph{ift motion:} -Each new point has to be reached at equidistant time $t_s$. So we can look at the position as sampling values. The trajectory with the least high frequency components is the inverse Fourier transformation of the sampled points. The reconstruction of the trajectory can be done with summing $sin(x)/x$ in the time domain or by adding zeros in the frequency spectrum and the do the inverse Fourier transformation.\\ +Each new point has to be reached at equidistant time $t_s$. So we can look at the position as sampling values with a constant time interval. The trajectory with the least high frequency components is the inverse Fourier transformation of the sampled points. The reconstruction of the trajectory can be done with summing $sin(x)/x$ in the time domain or by adding zeros in the frequency spectrum and the do the inverse Fourier transformation.\\ The resulting trajectory is the best achievable in point of view of frequency bandwidth. Unfortunately motion programs do not allow such trajectory planing out of the box. \paragraph{pft motion:} @@ -1032,7 +1039,13 @@ shot average error x 0.865708 um, y 0.965126 um, 1.49323 um \subsection{simulations} +MATLAB simulation by executing \verb|DeltaTauOptimizer.m| +\center +{ +\includegraphics[scale=.4]{../matlab/figures/sim_optimize1.eps} +\includegraphics[scale=.4]{../matlab/figures/sim_optimize2.eps} +} \begin{tcolorbox}[colback=red!5!white,colframe=red!75!black,colbacktitle=red!50,coltitle=black,title=TODO] diff --git a/MXfastStageDoc/traj1.eps b/MXfastStageDoc/traj1.eps index 7f0a00f..8ea9696 100644 --- a/MXfastStageDoc/traj1.eps +++ b/MXfastStageDoc/traj1.eps @@ -1,7 +1,7 @@ %!PS-Adobe-3.0 EPSF-3.0 %%Title: /home/zamofing_t/Documents/prj/SwissFEL/epics_ioc_modules/ESB_MX/MXfastStageDoc/traj1.eps %%Creator: matplotlib version 1.5.1, http://matplotlib.org/ -%%CreationDate: Tue Mar 5 14:20:51 2019 +%%CreationDate: Fri Mar 15 09:41:59 2019 %%Orientation: portrait %%BoundingBox: 18 180 594 612 %%EndComments diff --git a/MXfastStageDoc/traj2.eps b/MXfastStageDoc/traj2.eps index 678f844..180e52a 100644 --- a/MXfastStageDoc/traj2.eps +++ b/MXfastStageDoc/traj2.eps @@ -1,7 +1,7 @@ %!PS-Adobe-3.0 EPSF-3.0 %%Title: /home/zamofing_t/Documents/prj/SwissFEL/epics_ioc_modules/ESB_MX/MXfastStageDoc/traj2.eps %%Creator: matplotlib version 1.5.1, http://matplotlib.org/ -%%CreationDate: Tue Mar 5 14:20:51 2019 +%%CreationDate: Fri Mar 15 09:42:00 2019 %%Orientation: portrait %%BoundingBox: 18 180 594 612 %%EndComments diff --git a/MXfastStageDoc/traj3.eps b/MXfastStageDoc/traj3.eps index 0528e4a..4f428f6 100644 --- a/MXfastStageDoc/traj3.eps +++ b/MXfastStageDoc/traj3.eps @@ -1,7 +1,7 @@ %!PS-Adobe-3.0 EPSF-3.0 %%Title: /home/zamofing_t/Documents/prj/SwissFEL/epics_ioc_modules/ESB_MX/MXfastStageDoc/traj3.eps %%Creator: matplotlib version 1.5.1, http://matplotlib.org/ -%%CreationDate: Tue Mar 5 14:20:52 2019 +%%CreationDate: Fri Mar 15 09:42:01 2019 %%Orientation: portrait %%BoundingBox: 18 180 594 612 %%EndComments diff --git a/matlab/DeltaTauOptimizer.m b/matlab/DeltaTauOptimizer.m index eb620fd..ef4ffc0 100644 --- a/matlab/DeltaTauOptimizer.m +++ b/matlab/DeltaTauOptimizer.m @@ -10,73 +10,55 @@ function DeltaTauOptimizer() mot=identifyFxFyStage(7); end %SIM1=[1 1; 1 2; 8 2; 9 2;9 3]; + %SIM1=[9 1; 9 2; 9 3; 9 4]; + %SIM1=[9 1; 9 2; 9 3; 9 4; 9 5; 9 6]; + SIM1=[9 1; 9 2; 9 5; 9 6]; + %SIM2=[1 1; 1 2; 8 2; 9 2]; - SIM1=[9 1; 9 2; 9 3; 9 4]; - SIM2=[9 1; 9 2; 9 3; 9 4]; - %SIM2=[8 1; 8 2; 8 3; 8 4]; - %SIM2=[8 1; 8 2; 9 1; 9 2]; - %SIM2=[8 2; 9 2;8 3; 9 3]; - %SIM2=[8 3; 9 3;8 4; 9 4]; - %SIM2=[9 4;9 0]; - %SIM2=[8 3; 9 3]; - SIM1=[9 1; 9 2; 9 3; 9 4; 9 5]; + %SIM2=[9 1; 9 2; 9 3; 9 4]; + SIM2=[9 1; 9 2]; + if isempty(simData1) close all; simData1=ExecSim(mot{1},SIM1); end - %if isempty(simData2) - % close all; - % simData2=ExecSim(mot{2},SIM2); - %end + if isempty(simData2) + close all; + simData2=ExecSim(mot{2},SIM2); + end close all; - test() - %bodeSim(simData1); - %bodeSim(simData2); + %test1();return + %test2();return + bodeSim(simData1); + bodeSim(simData2); + for i =1:2 + set(i,'OuterPosition',[80 400 1000 700]); + print(i,sprintf('figures/sim_optimize%d',i),'-depsc'); + end end -function test() - global pb mot simData1 simData2; - %pb=DeltaTauParam(mot{2},8,0); +function test1() + global pb mot simData1; + %pb=DeltaTauParam(mot{1},8,0); pb=DeltaTauParam(mot{1},9,0); sim('DeltaTauSim'); - i=6; + i=7; simData1(i).pb=pb; simData1(i).desPos_actPos=desPos_actPos; simData1=bodeSim(simData1); - return - %simData2(i).mot_mdl_param=SIM(i,:); - %pb.C=[0.04877]; - %pb.D=[1 -0.9512]; - %pb.C=[1 -1.3236 6.2472 -11.8555 11.3067 -5.4188 1.0440]; - %pb.D=[1.0000 -6.6330 17.6945 -24.5314 18.7409 -7.5020 1.2309]; - global tfs - Ts=1/5000; - frq0=55;d0=.5; - frq1=85;d1=.5; - w0=frq0*2*pi; - w1=frq1*2*pi; - tf0=tf([w0^2],[1 2*d0*w0 w0^2 ]) - tf1=tf([1 2*d1*w1 w1^2 ],[w1^2]) - tfs=tf0*tf1 - tfz=c2d(tfs,Ts) - %h=bodeplot(tfz,tfs);setoptions(h,'FreqUnits','Hz','Grid','on'); - %pb.C=tfz.Numerator{1}; - %pb.D=tfz.Denominator{1}; - - opt=tfestOptions; - opt.Display='off'; -% %tfa=tfest(simData2(i).tfEst, 6, 5,opt); - tfa=tfest(simData2(i).tfEst, 6, 6,opt); -% tfb=1/tfa -% tfc=c2d(tfb,1/5000) -% -% tfs=tf([1],[.001 1]) -% tfz=c2d(tfs,1/5000) -% h=bodeplot(tfs,tfz) -% setoptions(h,'FreqUnits','Hz','Grid','on'); -% controlSystemDesigner(tfa) end +function test2() + global pb mot simData2; + %pb=DeltaTauParam(mot{2},8,0); % ss_cq no resonance + pb=DeltaTauParam(mot{2},9,0); % ss_cqr with resonance + sim('DeltaTauSim'); + i=3; + simData2(i).pb=pb; + simData2(i).desPos_actPos=desPos_actPos; + simData2=bodeSim(simData2); + return +end function simData=ExecSim(mot,SIM) global pb; @@ -130,4 +112,38 @@ function simData=bodeSim(simData) set(ax2, 'XScale', 'log'); linkaxes([ax1,ax2],'x') legend('Location','best'); -end \ No newline at end of file +end + +function SCRATCH() + %simData2(i).mot_mdl_param=SIM(i,:); + %pb.C=[0.04877]; + %pb.D=[1 -0.9512]; + %pb.C=[1 -1.3236 6.2472 -11.8555 11.3067 -5.4188 1.0440]; + %pb.D=[1.0000 -6.6330 17.6945 -24.5314 18.7409 -7.5020 1.2309]; + global tfs + Ts=1/5000; + frq0=55;d0=.5; + frq1=85;d1=.5; + w0=frq0*2*pi; + w1=frq1*2*pi; + tf0=tf([w0^2],[1 2*d0*w0 w0^2 ]) + tf1=tf([1 2*d1*w1 w1^2 ],[w1^2]) + tfs=tf0*tf1 + tfz=c2d(tfs,Ts) + %h=bodeplot(tfz,tfs);setoptions(h,'FreqUnits','Hz','Grid','on'); + %pb.C=tfz.Numerator{1}; + %pb.D=tfz.Denominator{1}; + + opt=tfestOptions; + opt.Display='off'; +% %tfa=tfest(simData2(i).tfEst, 6, 5,opt); + tfa=tfest(simData2(i).tfEst, 6, 6,opt); +% tfb=1/tfa +% tfc=c2d(tfb,1/5000) +% +% tfs=tf([1],[.001 1]) +% tfz=c2d(tfs,1/5000) +% h=bodeplot(tfs,tfz) +% setoptions(h,'FreqUnits','Hz','Grid','on'); +% controlSystemDesigner(tfa) +end diff --git a/matlab/DeltaTauParam.m b/matlab/DeltaTauParam.m index 4ed99db..b2608a7 100644 --- a/matlab/DeltaTauParam.m +++ b/matlab/DeltaTauParam.m @@ -45,13 +45,7 @@ function [pb]=DeltaTauParam(mot,mdl,param) switch param case 0 %scratch desc=desc+"scratch"; - pb.Kp=25;pb.Kvfb=350;pb.Ki=0.02;pb.Kvff=350;pb.Kaff=1/(curr2acc*(pb.Ts^2));pb.MaxInt=1000; - %pure feed forward - pb.Kp=0;pb.Kvfb=0;pb.Ki=0.02;pb.Kvff=0;pb.Kaff=1/(curr2acc*(pb.Ts^2));pb.MaxInt=1000; - %pure feedback - pb.Kp=25;pb.Kvfb=350;pb.Ki=0.02;pb.Kvff=0;pb.Kaff=0;pb.MaxInt=1000; - - pl=[-300+350i -300-350i -2513]; + pl=[-800+150i -800-150i -2513]; [Am,Bm,Cm,Dm]=ssdata(mot.ss_dq); K = place(Am,Bm,pl); V=-1./(Cm*(Am-Bm*K)^-1*Bm); %(from Lineare Regelsysteme2 (Glattfelder) page:173 ) @@ -59,33 +53,49 @@ function [pb]=DeltaTauParam(mot,mdl,param) Kfb=K*(Cm^-1); pb.V=V; pb.Kfb=Kfb; + pb.Kp=V; - pb.B=tf(Kfb(3)/V); + %pb.B=tf(Kfb(3)/V); pb.Kvfb=Kfb(2)/pb.Ts; - pb.Kvff=1*pb.Kvfb;%0; + pb.Kvff=0;%1*pb.Kvfb; pb.Kafb=Kfb(1)/(curr2acc*(pb.Ts^2)); - pb.Kaff=1/(curr2acc*(pb.Ts^2))+pb.Kafb;%0; - pb.Ki=0.002; + pb.Kaff=0;%1/(curr2acc*(pb.Ts^2))+pb.Kafb; + pb.Ki=0.001; pb.MaxInt=1000; %https://ch.mathworks.com/help/control/ref/tf.html %feed forward filter attenuating high frequencies %fs=1/pb.Ts;%[n,d] = butter(6,fc/(fs/2)); - fc=125;%Hz + fc=200;%Hz [num,den] = butter(2,fc*pb.Ts*2,'low'); Fz2=tf(num,den,pb.Ts); Fz=tf([1 0 0],den/sum(num),pb.Ts); - h=bodeplot(Fz,Fz2);setoptions(h,'FreqUnits','Hz','Grid','on'); + %h=bodeplot(Fz,Fz2);setoptions(h,'FreqUnits','Hz','Grid','on'); + + %pb.F=Fz; + fc=800;%Hz + [num,den] = butter(2,fc*pb.Ts*2,'low'); + Ez=tf([1 0 0],den/sum(num),pb.Ts); + + + Bz=tf([.5 .5],[1 0],pb.Ts) + pb.E=Ez; %less noise sensitive on feedback + pb.B=Bz; %less noise sensitive on feedback - pb.F=Fz; - % THE F FILTER HELPS TO NOT OVERSHOOT ON HIGH FREQUENCY TRAJECTORIES, - % BUT THE INPUT TRAJECTORY DOES NOT CONTAIN THESE FREQUENCIES. - % THEREFORE THE FILTERS IN THE CLOSED LOOP MUST BE TWEAKED! - %tf([.2 .8],[1],pb.Ts,'variable','z^-1') = tf([.2 .8],[1 0],pb.Ts) - %pb.A=tf([.0 1],[1 0],pb.Ts) + aa=.9; + k=1.8; + T=1/200; + C_D=tf(k*[T 1],[T*aa 1]); + C_Dz=c2d(C_D,pb.Ts) + %h=bodeplot(C_D,C_Dz);setoptions(h,'FreqUnits','Hz','Grid','on'); + pb.C_D=C_Dz; display(pb.B) + display(pb.E) + display(pb.C_D) display(pb.F) + Fz=tf([1 0 0],den/sum(num),pb.Ts); + fprintf('Kp:%f Kvfb:%f\n',pb.Kp,pb.Kvfb); case 1 %origin parameters @@ -137,7 +147,8 @@ function [pb]=DeltaTauParam(mot,mdl,param) display(pb.B) fprintf('Kp:%f Kvfb:%f Kafb:%f\n',pb.Kp,pb.Kvfb,pb.Kafb); case 5 % optimize higher gain and filter - pl=[-600+750i -600-750i -2513]; + desc=desc+"opt5"; + pl=[-800+150i -800-150i -2513]; [Am,Bm,Cm,Dm]=ssdata(mot.ss_dq); K = place(Am,Bm,pl); V=-1./(Cm*(Am-Bm*K)^-1*Bm); %(from Lineare Regelsysteme2 (Glattfelder) page:173 ) @@ -145,26 +156,95 @@ function [pb]=DeltaTauParam(mot,mdl,param) Kfb=K*(Cm^-1); pb.V=V; pb.Kfb=Kfb; + pb.Kp=V; - pb.B=tf(Kfb(3)/V); + %pb.B=tf(Kfb(3)/V); pb.Kvfb=Kfb(2)/pb.Ts; - pb.Kvff=.15*pb.Kvfb;%0; + pb.Kvff=1*pb.Kvfb; pb.Kafb=Kfb(1)/(curr2acc*(pb.Ts^2)); - pb.Kaff=.2/(curr2acc*(pb.Ts^2))+pb.Kafb;%0; - pb.Ki=0.002; + pb.Kaff=1/(curr2acc*(pb.Ts^2))+pb.Kafb; + pb.Ki=0.001; pb.MaxInt=1000; + %https://ch.mathworks.com/help/control/ref/tf.html %feed forward filter attenuating high frequencies %fs=1/pb.Ts;%[n,d] = butter(6,fc/(fs/2)); - fc=125;%Hz + fc=200;%Hz [num,den] = butter(2,fc*pb.Ts*2,'low'); Fz2=tf(num,den,pb.Ts); Fz=tf([1 0 0],den/sum(num),pb.Ts); - h=bodeplot(Fz,Fz2);setoptions(h,'FreqUnits','Hz','Grid','on'); - pb.F=Fz; + %h=bodeplot(Fz,Fz2);setoptions(h,'FreqUnits','Hz','Grid','on'); + + fc=800;%Hz + [num,den] = butter(2,fc*pb.Ts*2,'low'); + Ez=tf([1 0 0],den/sum(num),pb.Ts); + + Bz=tf([.5 .5],[1 0],pb.Ts) % fast simple lowpass: -3dB at 1250Hz + + %pb.F=Fz; %filtering does not help in the domain below 50Hz. it attenuates at high frq. but makes things worse at low frq. + pb.E=Ez; %less noise sensitive on feedback + pb.B=Bz; %less noise sensitive on feedback display(pb.B) display(pb.F) - fprintf('Kp:%f Kvfb:%f\n',pb.Kp,pb.Kvfb); + Fz=tf([1 0 0],den/sum(num),pb.Ts); + + fprintf('Kp:%f Kvfb:%f\n',pb.Kp,pb.Kvfb); + case 6 + desc=desc+"opt6"; + pl=[-800+150i -800-150i -2513]; + [Am,Bm,Cm,Dm]=ssdata(mot.ss_dq); + K = place(Am,Bm,pl); + V=-1./(Cm*(Am-Bm*K)^-1*Bm); %(from Lineare Regelsysteme2 (Glattfelder) page:173 ) + V=V(end); + Kfb=K*(Cm^-1); + pb.V=V; + pb.Kfb=Kfb; + + pb.Kp=V; + %pb.B=tf(Kfb(3)/V); + pb.Kvfb=Kfb(2)/pb.Ts; + pb.Kvff=1*pb.Kvfb; + pb.Kafb=Kfb(1)/(curr2acc*(pb.Ts^2)); + pb.Kaff=1/(curr2acc*(pb.Ts^2))+pb.Kafb; + pb.Ki=0.001; + pb.MaxInt=1000; + + %https://ch.mathworks.com/help/control/ref/tf.html + %feed forward filter attenuating high frequencies + %fs=1/pb.Ts;%[n,d] = butter(6,fc/(fs/2)); + fc=200;%Hz + [num,den] = butter(2,fc*pb.Ts*2,'low'); + Fz2=tf(num,den,pb.Ts); + Fz=tf([1 0 0],den/sum(num),pb.Ts); + %h=bodeplot(Fz,Fz2);setoptions(h,'FreqUnits','Hz','Grid','on'); + + %pb.F=Fz; + fc=800;%Hz + [num,den] = butter(2,fc*pb.Ts*2,'low'); + Ez=tf([1 0 0],den/sum(num),pb.Ts); + + + Bz=tf([.5 .5],[1 0],pb.Ts) + pb.E=Ez; %less noise sensitive on feedback + pb.B=Bz; %less noise sensitive on feedback + + aa=.9; + k=1.8; + T=1/200; + C_D=tf(k*[T 1],[T*aa 1]); + C_Dz=c2d(C_D,pb.Ts) + %h=bodeplot(C_D,C_Dz);setoptions(h,'FreqUnits','Hz','Grid','on'); + pb.C_D=C_Dz; + + display(pb.B) + display(pb.E) + display(pb.C_D) + display(pb.F) + Fz=tf([1 0 0],den/sum(num),pb.Ts); + + fprintf('Kp:%f Kvfb:%f\n',pb.Kp,pb.Kvfb); + + end %pb.Kp=0.1;pb.Kvfb=0;pb.Ki=0.00;pb.Kvff=0;pb.Kaff=1/(1.548e04*(pb.Ts^2));pb.MaxInt=1000; %filter [z^0 z^-1 ... z^-n]; @@ -176,29 +256,37 @@ function [pb]=DeltaTauParam(mot,mdl,param) %!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') switch param case 0 %scratch - desc=desc+"scratch"; - pb.Kp=20.3255; - pb.Kvfb=582.4551;pb.Kvff=582.4551; - pb.Kaff=5595.2;pb.Kafb=1077.9; - pb.Ki=0.01;pb.MaxInt=1000; + desc=desc+"scratch"; + %pole(mot.ss_dq) + pl=[-300+150i -300-150i -2513]; + [Am,Bm,Cm,Dm]=ssdata(mot.ss_dq); + K = place(Am,Bm,pl); + V=-1./(Cm*(Am-Bm*K)^-1*Bm); %(from Lineare Regelsysteme2 (Glattfelder) page:173 ) + V=V(end); + Kfb=K*(Cm^-1); + pb.V=V; + pb.Kfb=Kfb; + + pb.Kp=V; + %pb.B=tf(Kfb(3)/V); + pb.Kvfb=Kfb(2)/pb.Ts; + pb.Kvff=1*pb.Kvfb; + pb.Kafb=0.1*Kfb(1)/(curr2acc*(pb.Ts^2)); + pb.Kaff=1/(curr2acc*(pb.Ts^2))+pb.Kafb; + pb.Ki=0.001; + pb.MaxInt=1000; -% Ts=pb.Ts; -% frq=75;d=.6; -% w0=frq*2*pi; -% tfs=tf([w0^2],[1 2*d*w0 w0^2 ]) -% global currTf -% frq0=55;d0=.5; -% frq1=85;d1=.5; -% w0=frq0*2*pi; -% w1=frq1*2*pi; -% tf0=tf([w0^2],[1 2*d0*w0 w0^2 ]) -% tf1=tf([1 2*d1*w1 w1^2 ],[w1^2]) -% tfs=tf0*tf1 -% tfz=c2d(tfs,Ts) -% h=bodeplot(tfz,tfs);setoptions(h,'FreqUnits','Hz','Grid','on'); -% pb.C=tfz.Numerator{1}; -% pb.D=tfz.Denominator{1}; -% currTf=tfz + fc=800;%Hz + [num,den] = butter(2,fc*pb.Ts*2,'low'); + Ez=tf([1 0 0],den/sum(num),pb.Ts); + %Az=tf([.1 .2 .7],[1 0 0],pb.Ts) %is similar to the phase at feed forward only + %h=bodeplot(Az);setoptions(h,'FreqUnits','Hz','Grid','on'); + + + %Bz=tf([.5 .5],[1 0],pb.Ts) + %pb.E=Ez; %less noise sensitive on feedback + %pb.B=Bz; %less noise sensitive on feedback + fprintf('Kp:%f Kvfb:%f Kafb:%f\n',pb.Kp,pb.Kvfb,pb.Kafb); case 1 %origin parameters desc=desc+"orig"; diff --git a/matlab/DeltaTauSim.slx b/matlab/DeltaTauSim.slx index fc33cf02cee32b4adf653b95a3b7cc1466e0c3eb..f4e7e4fc5211b70158e90a441a8b746cf1b0b0a9 100644 GIT binary patch delta 20640 zcmV)QK(xR4(E_^B0ufM40|XQR000O825wqT(bKDu5j20W>ei92OVsSiB}QvBz8FI9 zw#EVt;6D8I(ypzWF22DzhjS12-gLf~Rj>tPORZ*zMg#?*gf2?8o}uS_5hf_Gj;n%K zTEPq*z@lKDrJ`m+8+b6f2IESwfj3f?iF$@M&eaTKD>fjxjl4p6a;1&r&R@+M*Iaz? zHQ*5;V=RBc@q#;!8^f?398qUkh{3Y{G*xR*2n-cSP|ji+QT(Hor90Gc@kLLbs2vz- zFN#}cQ0{FxsCK(uw2NDv&rk8|^8Ts)YFMhKRRYklKit7un?ojb0ac1mp|mT_gaMBg zfiJ+0!F%Qs$v2?-4;$!W|;%&wz01Hs}hQv!z2`IoV0ZZq@lZI z%pSdE!dt$Fjy*hEyX+gYn*r$o38y>3=)@WT0FaZ710H{Q_fr#X*fm%XsR~l0swf~$ zN`Oe0rXWb~M5HRcOD8Bzx`_1l2-1QCDG4P)L_nlSF+hOOBZL-;p-2MZU4Q!if^XiL zH#2uO+1Z`l`?}9L*EzdM#!ntHGw?9b(9kgJJ<>J>wm<$2dOF~{&Kh0J19h}*e^@>00&1mj$Q7f9i ze%nAlBAWj9;*MqQ#mfz>VkKSY7inQs6>;e4QGcV#F=NdPJ;$04qwUN)O}%w$3#udU zwqcP%5<6~hs)VvqbU*hjc8_l>G*Sim+)L!R^CN$(KXmmZU4)cRLJ9VgR9J6nYi+zN zjk%(ne?j{V4cnEQ=N@Uq(chrGdMoDt+79x+dD7IFGe8_D_`DIqG%CdR@j$agN|2Hv znD0=q*0MGzDCkO>F74$vQ#XSkRVY-%MCsXf2pYeXC$*C9rSiL$LOyjgs+5KShL4Ff9vcUd9PY.J3^( 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 diff --git a/python/MXMotion.py b/python/MXMotion.py index 1577f94..222175c 100644 --- a/python/MXMotion.py +++ b/python/MXMotion.py @@ -36,7 +36,10 @@ class MotionBase: self.gather=gather self.verbose=verbose #ServoPeriod=0.2 #ms, Sys.ServoPeriod is dependent of !common() macro - ServoPeriod=comm.gpascii.servo_period*1000 + if comm is None: + ServoPeriod=.2 + else: + ServoPeriod=comm.gpascii.servo_period*1000 self.meta={'srv_per':ServoPeriod,'pt2pt_time':40,'sync_flag':0,'sync_mode':2} self.meta.update(kwargs) @@ -106,6 +109,7 @@ class MotionBase: self.sync_prg = prg #download and start triggerSync code comm=self.comm + if comm is None: return arg=0 #argument to pass to triggerSync program if sync_mode==2:arg+=1 #synchronize @@ -144,6 +148,7 @@ class MotionBase: def run(self, crdId=1): 'runs the code sync_run which has been generated with setup_sync()' comm = self.comm + if comm is None: return gpascii = comm.gpascii try: cmd=self.sync_run @@ -157,6 +162,7 @@ class MotionBase: return else: comm = self.comm + if comm is None: return gpascii = comm.gpascii while (True): val = gpascii.get_variable('Coord[{crdId}].Q[0]'.format(crdId=crdId), type_=int) @@ -172,7 +178,7 @@ class MotionBase: if ge == 0: return -1 cnt = gpascii.get_variable('Gather.Samples', type_=int) try: - cnt*=self.meta['srv_per']/(self.meta['acq_per']*self.meta['pt2pt_time']) + cnt*=self.meta['srv_per']*self.meta['acq_per']/self.meta['pt2pt_time'] except KeyError: pass return cnt @@ -184,6 +190,7 @@ class MotionBase: time.sleep(wait) if self.meta['sync_flag']&1: comm = self.comm + if comm is None: return gpascii = comm.gpascii gpascii.send_block('Coord[1].Q[10]=1') else: diff --git a/python/helicalscan.py b/python/helicalscan.py index ba46894..aed531e 100755 --- a/python/helicalscan.py +++ b/python/helicalscan.py @@ -1254,11 +1254,11 @@ if __name__=='__main__': y=(1405.7, 1019.2), z=((-1309.6, -1010.9, -2410.3), (-1219.4, -918.8, -2510.4))) - hs.calcParam(x=((-70.25682272433501, 287.48437780001825, -477.99870973215764), - (-41.466601738723284, 274.71565975031103, -504.9508517714285)), - y=(1207.6326052816642, 1704.2138281362475), z=( - (-1196.2847548574225, -1686.284754857423, -1686.284754857423), - (-1196.2847548574225, -1686.284754857423, -1686.284754857423))) + #hs.calcParam(x=((-70.25682272433501, 287.48437780001825, -477.99870973215764), + # (-41.466601738723284, 274.71565975031103, -504.9508517714285)), + # y=(1207.6326052816642, 1704.2138281362475), z=( + # (-1196.2847548574225, -1686.284754857423, -1686.284754857423), + # (-1196.2847548574225, -1686.284754857423, -1686.284754857423))) ### use simulation motors ### diff --git a/python/shapepath.py b/python/shapepath.py index acda3c1..20751e8 100755 --- a/python/shapepath.py +++ b/python/shapepath.py @@ -240,9 +240,6 @@ class DebugPlot: plt.show(block=False) - plt.show(block=False) - - def plot_pos_error(self): rec = self.rec # yA,xA,yD,xD,trig ts=self.meta['srv_per']*self.meta['acq_per'] @@ -455,7 +452,7 @@ class ShapePath(MotionBase): verb=self.verbose if verb&2: self.plot_points(pts) - plt.show() + plt.show(block=False) def gen_swissfel_points(self,flipx=False,flipy=False,ofs=(0,0),width=1000): @@ -480,7 +477,7 @@ class ShapePath(MotionBase): verb=self.verbose if verb&2: self.plot_points(pts) - plt.show() + plt.show(block=False) def gen_rand_points(self,n=107,scale=1000,ofs=(0,0)): 'generate random distributed points' @@ -500,10 +497,10 @@ class ShapePath(MotionBase): pts+=ofs self.points=pts - def gen_spiral_points(self,rStart=1.,rInc=.2,numSeg=4,numCir=6, ofs=(0, 0)): + def gen_spiral_points(self,rStart=1.,rInc=.2,numSeg=4,numCir=6, phase=0, ofs=(0, 0)): #rInc radius increment per circle r=rStart+np.arange(numSeg*numCir)*(float(rInc)/numSeg) - ang=2.*np.pi/numSeg*np.arange(numSeg*numCir) + ang=2.*np.pi/numSeg*np.arange(numSeg*numCir)+phase*np.pi/180 pts=np.vstack((np.sin(ang)*r,np.cos(ang)*r)).T pts+=ofs @@ -586,7 +583,7 @@ class ShapePath(MotionBase): if verb&2: DebugPlot.plot_points(pts) - plt.show() + plt.show(block=False) self.points=pts @@ -713,7 +710,7 @@ class ShapePath(MotionBase): verb=self.verbose if verb&16: dp=DebugPlot(self);self.pvt=dp.plot_gen_pvt(pv) - plt.show() + plt.show(block=False) pv[1:-1, (2, 3)]*=CoordFeedTime #scaling for Deltatau prg.append(' linear abs') @@ -857,32 +854,37 @@ if __name__=='__main__': #sp.gen_closed_shifted() #sp.gen_swissmx_points(width=1000,ofs=(-500,0)) #sp.gen_swissfel_points(width=1000,ofs=(-500,0)) - sp.gen_rand_points(n=200, scale=1000);sp.sort_points(xy=False) + sp.gen_rand_points(n=2000, scale=1000);sp.sort_points(xy=False) #sp.gen_swissmx_points(width=1000, ofs=(-500, 0)); #sp.gen_spiral_points(rStart=100,rInc=10,numSeg=4,numCir=60, ofs=(0, 0)) #sp.gen_spiral_points(rStart=100,rInc=130,numSeg=4,numCir=2, ofs=(0, 0)) #sp.gen_grid_points(w=10,h=10,pitch=100,rnd=0,ofs=(0,0));sp.sort_points(False); #sp.gen_grid_points(w=1,h=10,pitch=100,rnd=0,ofs=(0,0)) #sp.gen_spiral_points(rStart=100,rInc=20,numSeg=8,numCir=32, ofs=(0, 0)) + #sp.gen_spiral_points(rStart=100,rInc=10,numSeg=2,numCir=32, phase=45, ofs=(0, 0)) + #sp.gen_spiral_points(rStart=100,rInc=10,numSeg=4,numCir=32, ofs=(0, 0)) #sp.gen_closed_shifted() - sp.setup_gather(acq_per=1) + gtMaxLn=116508;ovhdTime=100 + acq_per=int(np.ceil((sp.meta['pt2pt_time']*sp.points.shape[0]+ovhdTime)/(gtMaxLn*sp.meta['srv_per']))) + sp.setup_gather(acq_per=acq_per) sp.setup_sync(verbose=args.verbose&32) - sp.setup_coord_trf() # reset to shape path system - sp.setup_motion(fnPrg=fn+'.prg', mode=3, scale=1,dwell=10) + #sp.meta['pt2pt_time']=10 #put between setup_sync and setup_motion to have more motion points than FEL syncs + sp.setup_motion(fnPrg=fn+'.prg', mode=3, scale=1.,dwell=10) #sp.setup_motion(fnPrg=fn + '.prg', mode=1, scale=1,dwell=10) #sp.setup_motion(fnPrg=fn + '.prg', mode=1, scale=0,dwell=10) sp.homing() #homing if needed sp.run() #start motion program sp.wait_armed() # wait until motors are at first position sp.trigger(0.5) #send a start trigger (if needed) ater given time - while True: - p=sp.progress() - if p<0: break - print('progress %d'%p);time.sleep(.1) - sp.gather_upload(fnRec=fn+'.npz') - dp=DebugPlot(sp);dp.plot_gather(mode=11) + if not comm is None: + while True: + p=sp.progress() + if p<0: break + print('progress %d/%d'%(p,sp.points.shape[0]));time.sleep(.1) + sp.gather_upload(fnRec=fn+'.npz') + dp=DebugPlot(sp);dp.plot_gather(mode=11) print('done') plt.show(block=False)