diff --git a/python/LQT_nullspace.py b/python/LQT_nullspace.py index 633a05751e5c00497c0ab8c00426a78512e9b095..2486aed080104dbc5a802634e9931d8c3e5b20e3 100644 --- a/python/LQT_nullspace.py +++ b/python/LQT_nullspace.py @@ -1,8 +1,8 @@ ''' - Linear Quadratic tracker applied on a via point example + Batch LQT with nullspace formulation Copyright (c) 2021 Idiap Research Institute, http://www.idiap.ch/ - Written by Jeremy Maceiras <jeremy.maceiras@idiap.ch>, + Written by Hakan Girgin <hakan.girgin@idiap.ch>, Sylvain Calinon <https://calinon.ch> This file is part of RCFS. @@ -25,52 +25,62 @@ from math import factorial import matplotlib.pyplot as plt from scipy.linalg import block_diag +## Precomputation of basis functions to generate structured stochastic u through Bezier curves +# Building Bernstein basis functions +def build_phi_bernstein(nb_data, nb_fct): + t = np.linspace(0,1,nb_data) + phi = np.zeros((nb_data,nb_fct)) + for i in range(nb_fct): + phi[:,i] = factorial(nb_fct-1) / (factorial(i) * factorial(nb_fct-1-i)) * (1-t)**(nb_fct-1-i) * t**i + return phi + # Parameters # =============================== -dt = 1e-1 # Time step length -nbPoints = 1 # Number of targets -nbDeriv = 1 # Order of the dynamical system -nbVarPos = 2 # Number of position variable -nbData = 50 # Number of datapoints -rfactor = 1e-4 # Control weight term -nbRepros = 60 # Number of stochastic reproductions -nb_var = nbVarPos * nbDeriv # Dimension of state vector +param = lambda: None # Lazy way to define an empty class in python +param.dt = 1e-1 # Time step length +param.nbPoints = 1 # Number of targets +param.nbDeriv = 1 # Order of the dynamical system +param.nbVarPos = 2 # Number of position variable +param.nbData = 50 # Number of datapoints +param.rfactor = 1e-4 # Control weight term +param.nbRepros = 60 # Number of stochastic reproductions +param.nbVar = param.nbVarPos * param.nbDeriv # Dimension of state vector # Dynamical System settings (discrete) # ===================================== -A1d = np.zeros((nbDeriv,nbDeriv)) -B1d = np.zeros((nbDeriv,1)) +A1d = np.zeros((param.nbDeriv,param.nbDeriv)) +B1d = np.zeros((param.nbDeriv,1)) -for i in range(nbDeriv): - A1d += np.diag( np.ones(nbDeriv-i) ,i ) * dt**i * 1/factorial(i) - B1d[nbDeriv-i-1] = dt**(i+1) * 1/factorial(i+1) +for i in range(param.nbDeriv): + A1d += np.diag( np.ones(param.nbDeriv-i) ,i ) * param.dt**i * 1/factorial(i) + B1d[param.nbDeriv-i-1] = param.dt**(i+1) * 1/factorial(i+1) -A = np.kron(A1d,np.identity(nbVarPos)) -B = np.kron(B1d,np.identity(nbVarPos)) +A = np.kron(A1d,np.identity(param.nbVarPos)) +B = np.kron(B1d,np.identity(param.nbVarPos)) # Build Sx and Su transfer matrices -Su = np.zeros((nb_var*nbData,nbVarPos * (nbData-1))) -Sx = np.kron(np.ones((nbData,1)),np.eye(nb_var,nb_var)) +Su = np.zeros((param.nbVar*param.nbData,param.nbVarPos * (param.nbData-1))) +Sx = np.kron(np.ones((param.nbData,1)),np.eye(param.nbVar,param.nbVar)) M = B -for i in range(1, nbData): - Sx[i*nb_var:nbData*nb_var,:] = np.dot(Sx[i*nb_var:nbData*nb_var,:], A) - Su[nb_var*i:nb_var*i+M.shape[0],0:M.shape[1]] = M +for i in range(1, param.nbData): + Sx[i*param.nbVar:param.nbData*param.nbVar,:] = np.dot(Sx[i*param.nbVar:param.nbData*param.nbVar,:], A) + Su[param.nbVar*i:param.nbVar*i+M.shape[0],0:M.shape[1]] = M M = np.hstack((np.dot(A,M),B)) # [0,nb_state_var-1] # Cost function settings # ===================================== -R = np.identity( (nbData-1) * nbVarPos ) * rfactor # Control cost matrix +R = np.identity( (param.nbData-1) * param.nbVarPos ) * param.rfactor # Control cost matrix -t_list = np.stack([nbData-1]) # viapoint time list +t_list = np.stack([param.nbData-1]) # viapoint time list mu_list = np.stack([np.array([20,10.])]) # viapoint list -Q_list = np.stack([np.eye(nb_var)*1e3]) # viapoint precision list +Q_list = np.stack([np.eye(param.nbVar)*1e3]) # viapoint precision list -mus = np.zeros((nbData, nb_var)) -Qs = np.zeros((nbData, nb_var, nb_var)) +mus = np.zeros((param.nbData, param.nbVar)) +Qs = np.zeros((param.nbData, param.nbVar, param.nbVar)) for i,t in enumerate(t_list): mus[t] = mu_list[i] Qs[t] = Q_list[i] @@ -83,33 +93,25 @@ Q = block_diag(*Qs) # Batch LQR Reproduction # ===================================== -## Precomputation of basis functions to generate structured stochastic u through Bezier curves -# Building Bernstein basis functions -def build_phi_bernstein(nb_data, nb_fct): - t = np.linspace(0,1,nb_data) - phi = np.zeros((nb_data,nb_fct)) - for i in range(nb_fct): - phi[:,i] = factorial(nb_fct-1) / (factorial(i) * factorial(nb_fct-1-i)) * (1-t)**(nb_fct-1-i) * t**i - return phi nbRBF = 10 -H = build_phi_bernstein(nbData-1, nbRBF) +H = build_phi_bernstein(param.nbData-1, nbRBF) J = Q @ Su N = np.eye(J.shape[-1]) - np.linalg.pinv(J) @ J # nullspace operator of LQT # Principal Task -x0 = np.zeros(nb_var) +x0 = np.zeros(param.nbVar) u1 = np.linalg.solve(Su.T @ Q @ Su + R, Su.T @ Q @ (muQ - Sx @ x0)) -x1 = (Sx @ x0 + Su @ u1).reshape((-1,nb_var)) +x1 = (Sx @ x0 + Su @ u1).reshape((-1,param.nbVar)) # Secondary Task -repr_x = np.zeros((nbRepros, nbData, nb_var)) -for n in range(nbRepros): - w = np.random.randn(nbRBF, nbVarPos) * 1E1 # Random weights +repr_x = np.zeros((param.nbRepros, param.nbData, param.nbVar)) +for n in range(param.nbRepros): + w = np.random.randn(nbRBF, param.nbVarPos) * 1E1 # Random weights u2 = H @ w # Reconstruction of control signals by a weighted superposition of basis functions u = u1 + N @ u2.flatten() - repr_x[n] = np.reshape(Sx @ x0 + Su @ u, (-1, nb_var)) # Reshape data for plotting + repr_x[n] = np.reshape(Sx @ x0 + Su @ u, (-1, param.nbVar)) # Reshape data for plotting # Plotting # ========= @@ -120,7 +122,7 @@ for mu in mu_list: plt.scatter(mu[0], mu[1],c='red',s=100) plt.plot(x1[:,0] , x1[:,1], c='black') -for i in range(nbRepros): +for i in range(param.nbRepros): plt.plot(repr_x[i, :, 0], repr_x[i, :, 1], c='blue', alpha=0.1, zorder=0) plt.axis("off") @@ -130,24 +132,24 @@ fig,axs = plt.subplots(2,1) for i,t in enumerate(t_list): axs[0].scatter(t, mu_list[i][0],c='red') -for i in range(nbRepros): +for i in range(param.nbRepros): axs[0].plot(repr_x[i, :, 0], c='blue', alpha=0.1, zorder=0) axs[0].plot(x1[:,0], c='black') axs[0].set_ylabel("$x_1$") -axs[0].set_xticks([0,nbData]) +axs[0].set_xticks([0,param.nbData]) axs[0].set_xticklabels(["0","T"]) for i,t in enumerate(t_list): axs[1].scatter(t, mu_list[i][1],c='red') -for i in range(nbRepros): +for i in range(param.nbRepros): axs[1].plot(repr_x[i, :, 1], c='blue', alpha=0.1, zorder=0) axs[1].plot(x1[:,1], c='black') axs[1].set_ylabel("$x_2$") axs[1].set_xlabel("$t$") -axs[1].set_xticks([0,nbData]) +axs[1].set_xticks([0,param.nbData]) axs[1].set_xticklabels(["0","T"]) plt.show()