Parameter Estimation of RIG Roll Experiments

Setup and descriptions

  • Without ACM model
  • Turn on wind tunnel
  • Only 1DoF for RIG roll movement
  • Use small-amplitude aileron command of CMP as inputs (in degrees) $$U = \delta_{a,cmp}(t)$$
  • Consider RIG roll angle and its derivative as States (in radians) $$X = \begin{pmatrix} \phi_{rig} \\ \dot{\phi}_{rig} \end{pmatrix}$$
  • Observe RIG roll angle and its derivative as Outputs (in degrees) $$Z = \begin{pmatrix} \phi_{rig} \\ \dot{\phi}_{rig} \end{pmatrix}$$
  • Use output error method based on most-likehood(ML) to estimate $$ \theta = \begin{pmatrix} C_{l,\delta_a,cmp} \\ C_{lp,cmp} \end{pmatrix} $$

Startup computation engines


In [2]:
%run matt_startup
%run -i matt_utils

button_qtconsole()

#import other needed modules in all used engines
#with dview.sync_imports():
#   import os


importing numpy on engine(s)
importing scipy on engine(s)
importing matplotlib on engine(s)
importing scipy.integrate on engine(s)
importing scipy.interpolate on engine(s)
importing math on engine(s)
importing time on engine(s)
importing itertools on engine(s)
importing random on engine(s)

Data preparation

Load raw data


In [3]:
filename = 'FIWT_Exp036_20150605144438.dat.npz'

def loadData():
    # Read and parse raw data
    global exp_data
    exp_data = np.load(filename)

    # Select colums
    global T_cmp, da_cmp
    T_cmp = exp_data['data33'][:,0]
    da_cmp = exp_data['data33'][:,3]

    global T_rig, phi_rig
    T_rig = exp_data['data44'][:,0]
    phi_rig = exp_data['data44'][:,2]

loadData()

Check time sequence and inputs/outputs

Click 'Check data' button to show the raw data.

Click on curves to select time point and push into queue; click 'T/s' text to pop up last point in the queue; and click 'Output' text to print time sequence table.


In [4]:
def checkInputOutputData():

    #check inputs/outputs
    fig, ax = plt.subplots(2,1,True)
    ax[1].plot(T_cmp,da1_cmp,'r', picker=2)
    ax[0].plot(T_rig,phi_rig, 'b', picker=2)
    ax[1].set_ylabel('$\delta \/ / \/ ^o$')
    ax[0].set_ylabel('$\phi \/ / \/ ^o/s$')
    ax[1].set_xlabel('$T \/ / \/ s$', picker=True)
    ax[0].set_title('Output', picker=True)
    

    fig.canvas.mpl_connect('pick_event', onPickTime)
    fig.show()
    display(fig)

button_CheckData()

Input $\delta_T$ and focused time ranges


In [16]:
process_set1 = {
            'time_marks' : [
                [17.6940178696,117,"ramp u1"],
                [118.7,230.395312673,"ramp d1"],
                [258.807481992,357.486188688,"ramp u2"],
                [359.463122988,459.817499014,"ramp d2"],
                [461.067939262,558.784538108,"ramp d3"],
                [555.553175853,658.648739191,"ramp u4"],
            ],
            'Z':[(T_cmp, da_cmp,1),],
            'Z_names' : ['$\delta_{a,cmp} \, / \, ^o$'],
            'U_names' : ['$\phi_{a,rig} \, / \, ^o$'],
            'U':[(T_rig, phi_rig,1),],
            'cutoff_freq': 1, #Hz
            'consts':{'DT':0.5,'id':0,'V':30}
           }
display_data_set(process_set1)
resample(process_set1, append=False);


Time ranges
IdxStart(s)Spam(s)Description
617.6999.31ramp u1
7118.70111.70ramp d1
8258.8198.68ramp u2
9359.46100.35ramp d2
10461.0797.72ramp d3
11555.55103.10ramp u4
$U$
$\phi_{a,rig} \, / \, ^o$
$Z$
$\delta_{a,cmp} \, / \, ^o$
ConstantValue
DT0.5
id0
V30

Define dynamic model to be estimated

$$\left\{\begin{matrix}\begin{align} M_{x,rig} &= M_{x,a} + M_{x,f} + M_{x,cg} = 0 \\ M_{x,a} &= \frac{1}{2} \rho V^2S_cb_c C_{la,cmp}\delta_{a,cmp} \\ M_{x,f} &= -F_c \, sign(\dot{\phi}_{rig}) \\ M_{x,cg} &= -m_T g l_{zT} \sin \left ( \phi - \phi_0 \right ) \end{align}\end{matrix}\right.$$

In [17]:
%%px --local
#update common const parameters in all engines

angles = range(-170,171,10)
angles_num = len(angles)

#problem size
Nx = 0
Nu = 1
Ny = 1
Npar = angles_num

#reference
S_c = 0.1254   #S_c(m2) 
b_c = 0.7      #b_c(m)
g = 9.81       #g(m/s2)

#static measurement
m_T = 7.5588   #m_T(kg)
l_z_T = 0.0424250531303 #l_z_T(m)

#previous estimations
F_c = 0.0532285873599 #F_c(N*m)

#for short
_m_T_l_z_T_g = -(m_T*l_z_T)*g

angles_cmpx = [-41, -35, -30, -25, -20, -15, -10, -5, 5, 10, 15, 20, 25, 30, 35, 41]
Clda_cmpx = np.array([[ 0.05968291,  0.0553231,   0.04696643,  0.03889542,  0.03162213,  0.0271057,
   0.01839293,  0.00900769, -0.00497219, -0.01407865, -0.02279645, -0.02607804,
  -0.03158932, -0.03814162, -0.04606267, -0.05423365],
 [ 0.04821761,  0.0404353,   0.03394095,  0.0276883,   0.0223423,   0.01843785,
   0.01272706,  0.00739553, -0.00449509, -0.00899431, -0.01628838, -0.01994654,
  -0.02522128, -0.03103204, -0.03819486, -0.04861837],
 [ 0.04235569,  0.03650224,  0.02990824,  0.02505072,  0.01880923,  0.01535756,
   0.01121967,  0.00679852, -0.0041091,  -0.00866967, -0.01516793, -0.01995755,
  -0.02558169, -0.03024035, -0.0346464,  -0.03949022],
 [ 0.03393535,  0.03038317,  0.02596484,  0.02259024,  0.01868117,  0.01450576,
   0.00870284,  0.00571733, -0.00445593, -0.00858266, -0.01293602, -0.0150844,
  -0.01936097, -0.02433856, -0.03052023, -0.0351404 ]])

Clda_cmp = np.sum(Clda_cmpx, axis=0)
Da_cmp = scipy.interpolate.interp1d(Clda_cmp, angles_cmpx)

def obs(Z,T,U,params,consts):
    DT = consts['DT']
    ID = consts['id']
    V  = consts['V']

    unk_moment = scipy.interpolate.interp1d(angles, params[0:angles_num])
    
    s = T.size
    phi = U[:s,0]

    hdr = int(1/DT)
    col_fric = np.copysign(-F_c,phi[hdr:]-phi[:-hdr])
    col_fric = np.concatenate((np.ones(hdr-1)*col_fric[0], col_fric, np.ones(hdr-1)*col_fric[-1]))
    
    qbarSb = 0.5*1.225*V*V*S_c*b_c
    Da = Da_cmp((-_m_T_l_z_T_g*np.sin(phi/57.3)-unk_moment(phi)+col_fric)/qbarSb)
  
    return Da.reshape((-1,1))

In [18]:
display(HTML('<b>Constant Parameters</b>'))
table = ListTable()
table.append(['Name','Value','unit'])
table.append(['$S_c$',S_c,'$m^2$'])
table.append(['$b_c$',b_c,'$m$'])
table.append(['$g$',g,'$m/s^2$'])
table.append(['$m_T$',m_T,'$kg$'])
table.append(['$l_{zT}$',l_z_T,'$m$'])
table.append(['$F_c$',F_c,'$Nm$'])
display(table)


Constant Parameters
NameValueunit
$S_c$0.1254$m^2$
$b_c$0.7$m$
$g$9.81$m/s^2$
$m_T$7.5588$kg$
$l_{zT}$0.0424250531303$m$
$F_c$0.0532285873599$Nm$

Initial guess

  • Input default values and ranges for parameters
  • Select sections for trainning
  • Adjust parameters based on simulation results
  • Decide start values of parameters for optimization

In [19]:
#initial guess
param0 = [0]*angles_num

param_name = ['Mu_{}'.format(angles[i]) for i in range(angles_num)]
param_unit = ['Nm']*angles_num

NparID = Npar
opt_idx = range(Npar)
opt_param0 = [param0[i] for i in opt_idx]
par_del = [0.01]*(angles_num)
bounds =  [(-2,2)]*(angles_num)
display_default_params()

#select sections for training
section_idx = range(len(sections))
display_data_for_train()

#push parameters to engines
push_opt_param()


Default Parameters for optimization
IdxDescription$x_0$unitopt$\delta_{par}$minmax
0Mu_-1700Nm*0.01-22
1Mu_-1600Nm*0.01-22
2Mu_-1500Nm*0.01-22
3Mu_-1400Nm*0.01-22
4Mu_-1300Nm*0.01-22
5Mu_-1200Nm*0.01-22
6Mu_-1100Nm*0.01-22
7Mu_-1000Nm*0.01-22
8Mu_-900Nm*0.01-22
9Mu_-800Nm*0.01-22
10Mu_-700Nm*0.01-22
11Mu_-600Nm*0.01-22
12Mu_-500Nm*0.01-22
13Mu_-400Nm*0.01-22
14Mu_-300Nm*0.01-22
15Mu_-200Nm*0.01-22
16Mu_-100Nm*0.01-22
17Mu_00Nm*0.01-22
18Mu_100Nm*0.01-22
19Mu_200Nm*0.01-22
20Mu_300Nm*0.01-22
21Mu_400Nm*0.01-22
22Mu_500Nm*0.01-22
23Mu_600Nm*0.01-22
24Mu_700Nm*0.01-22
25Mu_800Nm*0.01-22
26Mu_900Nm*0.01-22
27Mu_1000Nm*0.01-22
28Mu_1100Nm*0.01-22
29Mu_1200Nm*0.01-22
30Mu_1300Nm*0.01-22
31Mu_1400Nm*0.01-22
32Mu_1500Nm*0.01-22
33Mu_1600Nm*0.01-22
34Mu_1700Nm*0.01-22
Training Data
IdxStart(s)Spam(s)Description
017.6999.31ramp u1
1118.70111.70ramp d1
2258.8198.68ramp u2
3359.46100.35ramp d2
4461.0797.72ramp d3
5555.55103.10ramp u4

In [20]:
# select 4 section from training data
#idx = random.sample(section_idx, 4)
idx = section_idx[:]

# interact_guess();
%matplotlib inline
update_guess();


Optimize using ML


In [21]:
display_preopt_params()

if False:
    InfoMat = None
    method = 'trust-ncg'
    def hessian(opt_params, index):
        global InfoMat
        return InfoMat
    dview['enable_infomat']=True
    options={'gtol':1}
    opt_bounds = None
else:
    method = 'L-BFGS-B'
    hessian = None
    dview['enable_infomat']=False
    options={'ftol':1e-6,'maxfun':400}
    opt_bounds = bounds

cnt = 0
tmp_rslt = None
T0 = time.time()
print('#cnt,     Time,      |R|')

%time res =  sp.optimize.minimize(fun=costfunc, x0=opt_param0, \
        args=(opt_idx,), method=method, jac=True, hess=hessian, \
        bounds=opt_bounds, options=options)


Parameters for optimization
IdxDescription$x_0$unitopt$\delta_{par}$minmax
0Mu_-1700Nm*0.01-22
1Mu_-1600Nm*0.01-22
2Mu_-1500Nm*0.01-22
3Mu_-1400Nm*0.01-22
4Mu_-1300Nm*0.01-22
5Mu_-1200Nm*0.01-22
6Mu_-1100Nm*0.01-22
7Mu_-1000Nm*0.01-22
8Mu_-900Nm*0.01-22
9Mu_-800Nm*0.01-22
10Mu_-700Nm*0.01-22
11Mu_-600Nm*0.01-22
12Mu_-500Nm*0.01-22
13Mu_-400Nm*0.01-22
14Mu_-300Nm*0.01-22
15Mu_-200Nm*0.01-22
16Mu_-100Nm*0.01-22
17Mu_00Nm*0.01-22
18Mu_100Nm*0.01-22
19Mu_200Nm*0.01-22
20Mu_300Nm*0.01-22
21Mu_400Nm*0.01-22
22Mu_500Nm*0.01-22
23Mu_600Nm*0.01-22
24Mu_700Nm*0.01-22
25Mu_800Nm*0.01-22
26Mu_900Nm*0.01-22
27Mu_1000Nm*0.01-22
28Mu_1100Nm*0.01-22
29Mu_1200Nm*0.01-22
30Mu_1300Nm*0.01-22
31Mu_1400Nm*0.01-22
32Mu_1500Nm*0.01-22
33Mu_1600Nm*0.01-22
34Mu_1700Nm*0.01-22
#cnt,     Time,      |R|
0001,    0.317,        1.1
0002,    0.621,       55.9
0003,    0.951,        3.8
0004,    1.320,        0.5
0005,    1.566,        0.1
0006,    1.946,        0.2
0007,    2.233,        0.1
0008,    2.490,        0.1
0009,    2.736,        0.1
0010,    2.988,        0.1
0011,    3.236,        0.1
0012,    3.508,        0.1
0013,    3.819,        0.1
0014,    4.107,        0.1
0015,    4.502,        0.1
0016,    4.908,        0.1
0017,    5.125,        0.1
0018,    5.352,        0.1
0019,    5.602,        0.1
0020,    5.844,        0.1
0021,    6.085,        0.1
0022,    6.321,        0.1
0023,    6.574,        0.1
0024,    6.809,        0.1
0025,    7.053,        0.1
0026,    7.307,       78.5
0027,    7.563,        6.4
0028,    7.817,        0.8
0029,    8.052,        0.2
0030,    8.303,        0.1
0031,    8.570,        0.1
0032,    8.876,        0.1
0033,    9.131,        0.1
0034,    9.386,        0.1
0035,    9.626,        0.2
0036,    9.884,        0.1
0037,   10.199,        0.1
0038,   10.510,        0.1
0039,   10.763,        0.1
0040,   11.022,        0.1
0041,   11.252,        0.1
0042,   11.493,        0.1
0043,   11.719,        0.1
0044,   11.973,        0.1
0045,   12.200,        0.1
0046,   12.441,        0.1
0047,   12.704,        0.1
0048,   12.948,        0.1
0049,   13.231,        0.1
0050,   13.467,        0.1
0051,   13.705,        0.1
0052,   13.943,        0.1
0053,   14.229,        0.1
0054,   14.453,        0.1
0055,   14.689,        0.1
0056,   15.005,        0.1
0057,   15.336,        0.1
0058,   15.705,        0.1
0059,   16.038,        0.1
0060,   16.416,        0.1
0061,   16.771,        0.1
0062,   17.006,        0.1
0063,   17.323,        0.1
0064,   17.578,        0.1
0065,   17.881,        0.1
0066,   18.130,        0.1
0067,   18.447,        0.1
0068,   18.698,        0.1
0069,   18.941,        0.1
0070,   19.360,        0.1
0071,   19.615,        0.1
0072,   19.845,        0.1
0073,   20.084,        0.1
0074,   20.346,        0.1
0075,   20.684,        0.1
0076,   20.927,        0.1
0077,   21.181,        0.1
0078,   21.490,        0.1
0079,   21.890,        0.1
0080,   22.175,        0.1
0081,   22.427,        0.1
0082,   22.677,        0.1
0083,   22.929,        0.1
0084,   23.178,        0.1
0085,   23.434,        0.1
0086,   23.670,        0.1
0087,   23.944,        0.1
0088,   24.194,        0.1
0089,   24.437,        0.1
0090,   24.657,        0.1
0091,   24.909,        0.1
0092,   25.226,        0.1
0093,   25.461,        0.1
0094,   25.689,        0.1
0095,   25.939,        0.1
0096,   26.203,        0.1
0097,   26.440,        0.1
0098,   26.670,        0.1
0099,   26.900,        0.1
0100,   27.182,        0.1
0101,   27.458,        0.1
0102,   27.713,        0.1
0103,   27.954,        0.1
0104,   28.256,        0.1
0105,   28.482,        0.1
0106,   28.724,        0.1
0107,   28.974,        0.1
0108,   29.210,        0.1
0109,   29.461,        0.1
0110,   29.703,        0.1
0111,   29.944,        0.1
0112,   30.167,        0.1
0113,   30.500,        0.1
0114,   30.693,        0.1
0115,   30.948,        0.1
0116,   31.218,        0.1
0117,   31.488,        0.1
0118,   31.717,        0.1
0119,   31.954,        0.1
0120,   32.195,        0.1
0121,   32.434,        0.1
0122,   32.684,        0.1
0123,   32.959,        0.1
0124,   33.235,        0.1
0125,   33.473,        0.1
0126,   33.722,        0.1
0127,   33.951,        0.1
0128,   34.230,        0.1
0129,   34.573,        0.1
0130,   34.870,        0.1
0131,   35.111,        0.1
0132,   35.351,        0.1
0133,   35.582,        0.1
0134,   35.848,        0.1
0135,   36.087,        0.1
0136,   36.316,        0.1
0137,   36.560,        0.1
0138,   36.828,        0.1
0139,   37.096,        0.1
0140,   37.379,        0.1
0141,   37.628,        0.1
0142,   37.882,        0.1
0143,   38.163,        0.1
0144,   38.488,        0.1
0145,   38.806,        0.1
0146,   39.102,        0.1
0147,   39.346,        0.1
0148,   39.599,        0.1
0149,   39.846,        0.1
0150,   40.109,        0.1
0151,   40.405,        0.1
0152,   40.677,        0.1
0153,   40.919,        0.1
0154,   41.179,        0.1
0155,   41.420,        0.1
0156,   41.661,        0.1
0157,   41.946,        0.1
0158,   42.187,        0.1
0159,   42.471,        0.1
0160,   42.808,        0.1
0161,   43.070,        0.1
0162,   43.363,        0.1
0163,   43.618,        0.1
0164,   43.851,        0.1
0165,   44.096,        0.1
0166,   44.377,        0.1
0167,   44.672,        0.1
0168,   44.913,        0.1
0169,   45.156,        0.1
0170,   45.377,        0.1
0171,   45.648,        0.1
0172,   45.882,        0.1
0173,   46.112,        0.1
0174,   46.395,        0.1
0175,   46.689,        0.1
0176,   46.923,        0.1
0177,   47.157,        0.1
0178,   47.397,        0.1
0179,   47.640,        0.1
0180,   47.890,        0.1
0181,   48.121,        0.1
0182,   48.376,        0.1
0183,   48.626,        0.1
0184,   48.881,        0.1
0185,   49.123,        0.1
0186,   49.400,        0.1
0187,   49.644,        0.1
0188,   49.910,        0.1
0189,   50.196,        0.1
0190,   50.483,        0.1
0191,   50.724,        0.1
0192,   50.997,        0.1
0193,   51.218,        0.1
0194,   51.466,        0.1
0195,   51.706,        0.1
0196,   51.947,        0.1
0197,   52.255,        0.1
0198,   52.485,        0.1
0199,   52.741,        0.1
0200,   52.976,        0.1
0201,   53.235,        0.1
0202,   53.474,        0.1
0203,   53.723,        0.1
0204,   53.973,        0.1
0205,   54.263,        0.1
0206,   54.509,        0.1
0207,   54.762,        0.1
0208,   55.004,        0.1
0209,   55.273,        0.1
0210,   55.646,       77.3
0211,   55.985,        6.3
0212,   56.317,        0.9
0213,   56.626,        0.3
0214,   56.848,        0.1
0215,   57.069,        0.1
0216,   57.305,        0.1
0217,   57.570,        0.1
0218,   57.817,        0.1
0219,   58.078,        0.1
0220,   58.350,        0.1
0221,   58.666,        0.1
0222,   58.894,        0.1
0223,   59.142,        0.1
0224,   59.368,        0.1
0225,   59.635,        0.1
0226,   59.906,        0.1
0227,   60.153,        0.1
0228,   60.401,        0.1
0229,   60.658,        0.1
Wall time: 1min

Show and test results


In [23]:
display_opt_params()

# show result
idx = range(len(sections))
display_data_for_test();

update_guess();


Parameters from optimization
IdxDescription$x$
0Mu_-170-0.0907972445779
1Mu_-160-0.196763845086
2Mu_-150-0.259266015842
3Mu_-140-0.202278957686
4Mu_-130-0.113213256912
5Mu_-120-0.0993836682627
6Mu_-110-0.106715449511
7Mu_-100-0.168922853284
8Mu_-90-0.33444016308
9Mu_-80-0.398891937308
10Mu_-70-0.410667022414
11Mu_-60-0.347007816733
12Mu_-50-0.290025729928
13Mu_-40-0.24280404989
14Mu_-30-0.154584335722
15Mu_-200.0663056256463
16Mu_-100.0808936714573
17Mu_0-0.0500810405659
18Mu_10-0.075041139568
19Mu_20-0.129056869479
20Mu_30-0.100870979743
21Mu_400.0142595311191
22Mu_500.179284213232
23Mu_600.388390867444
24Mu_700.487333999686
25Mu_800.524830202715
26Mu_900.396520969098
27Mu_1000.249228025306
28Mu_1100.0843138691153
29Mu_120-0.00587852092232
30Mu_130-0.111438247321
31Mu_1400.0776248026092
32Mu_1500.154149515009
33Mu_1600.180789812817
34Mu_1700.0354657699302
Test Data
IdxStart(s)Spam(s)Description
017.6999.31ramp u1
1118.70111.70ramp d1
2258.8198.68ramp u2
3359.46100.35ramp d2
4461.0797.72ramp d3
5555.55103.10ramp u4

In [24]:
res_params = res['x']
params = param0[:]
for i,j in enumerate(opt_idx):
    params[j] = res_params[i]

k1 = np.array(params[0:angles_num])

print('angeles = ')
print(angles)
print('L_unk = ')
print(k1)

%matplotlib inline
plt.figure(figsize=(12,8),dpi=300)
plt.plot(angles,  k1, 'r')
plt.ylabel('$L_{unk} \, / \, Nm$')
plt.xlabel('$\phi \, / \, ^o/s$')
plt.show()


angeles = 
[-170, -160, -150, -140, -130, -120, -110, -100, -90, -80, -70, -60, -50, -40, -30, -20, -10, 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160, 170]
L_unk = 
[-0.09079724 -0.19676385 -0.25926602 -0.20227896 -0.11321326 -0.09938367
 -0.10671545 -0.16892285 -0.33444016 -0.39889194 -0.41066702 -0.34700782
 -0.29002573 -0.24280405 -0.15458434  0.06630563  0.08089367 -0.05008104
 -0.07504114 -0.12905687 -0.10087098  0.01425953  0.17928421  0.38839087
  0.487334    0.5248302   0.39652097  0.24922803  0.08431387 -0.00587852
 -0.11143825  0.0776248   0.15414952  0.18078981  0.03546577]

In [14]:
toggle_inputs()
button_qtconsole()



In [15]:
(-0.05-0.05)/(80/57.3)


Out[15]:
-0.07162500000000001

In [16]:
Clda_cmp/4


Out[16]:
-0.075975

In [ ]: