Skip to content
Snippets Groups Projects
mpc_utils.cpp 6.12 KiB
// Interactive MPC helper functions

#include "mpc_utils.h"
//#include <LinAlg/LapackWrapperExtra.h>
//#include <SymEigsSolver.h>

#define PI 3.14159265359
// We are going to calculate the eigenvalues of M

using namespace arma;
/*
void symEigs( arma::vec* Dv, arma::mat* V, arma::mat A, int numEigs, int convFactor)
{
    // Construct matrix operation object using the wrapper class DenseGenMatProd
        DenseGenMatProd<double> op(A);
        // Construct eigen solver object, requesting the largest three eigenvalues
        SymEigsSolver< double, LARGEST_ALGE, DenseGenMatProd<double> > eigs(&op, numEigs, convFactor);
        
        eigs.init();
        int nconv = eigs.compute();
        
        if(nconv > 0)
        {
            *Dv = eigs.eigenvalues();
            *V = eigs.eigenvectors(nconv);
        }
        else
        {
            *Dv = arma::zeros(2);
            *V = arma::eye(2,2);
        }
}
*/

double factorials[15] =
{
   1,
    1,
    2,
    6,
    24,
    120,
    720,
    5040,
    40320,
    362880,
    3628800,
    39916800,
    479001600,
    6227020800,
    87178291200
};


static mat transformSigma( const mat& m, const mat& Sigma)
{
    mat V;
    vec d;
    eig_sym(d, V, Sigma);
    mat D = diagmat(d);
    V = m*V;
    return V*D*inv(V);
}
    
arma::mat rot2d( double theta, bool affine )
{
    int d = affine?3:2;
    arma::mat m = arma::eye(d,d);

    double ct = cos(theta);
    double st = sin(theta);

    m(0,0) = ct; m(0,1) = -st;
    m(1,0) = st; m(1,1) = ct;

    return m;
}

static mat makeSigma( float rot, vec scale )
{
    mat Q = rot2d(rot, false);
    mat Lambda = diagmat(scale%scale);
    return Q * Lambda * inv(Q);
}

/// Inits the system matrices with a chain of integrators
void makeIntegratorChain(arma::mat* A, arma::mat* B, int order)
{
    *A = zeros(order, order);
    A->submat(0, 1, order-2, order-1)
    = eye(order-1, order-1);
    *B = join_vert(zeros(order-1,1),
                      ones(1,1));
}

void discretizeSystem(arma::mat* _A, arma::mat* _B, arma::mat A, arma::mat B, double dt, bool zoh  )
{
    if(zoh)
    {
        // matrix eponential trick
        // From Linear Systems: A State Variable Approach with Numerical Implementation pg 215
        // adapted from scipy impl

        mat EM = join_horiz(A, B);
        mat zero = join_horiz(zeros(B.n_cols, B.n_rows), 
                              zeros(B.n_cols, B.n_cols));
        EM = join_vert(EM, zero);
        mat M = expmat(EM * dt);
        *_A = M.submat(span(0, A.n_rows-1), span(0, A.n_cols-1));
        *_B = M.submat(span(0, B.n_rows-1), span(A.n_cols, M.n_cols-1));
    }
    else  // euler
    {
        *_A = eye(A.n_rows, A.n_cols) + A*dt;
        *_B = B * dt;
    }
}
    
/// Creates random gaussians as targets for LQR constraints
void randomCovariances(arma::cube* Sigma, const arma::mat& Mu, const arma::vec& covscale, bool semiTied, double theta, double minRnd )
{
    int m = Mu.n_cols;
    
    *Sigma = zeros(2,2,m);
    vec s;
    
    for( int i = 0; i < m; i++ )
    {
        vec s = (minRnd+(randu(2)*(1.-minRnd))) % covscale;
        if(!semiTied)
            theta = -PI + arma::randu()*2.*PI;
        mat sigm = makeSigma(theta, s);
        Sigma->slice(i) = makeSigma(theta, s);
    }
}
    
double SHM_r(double d, double period, int order)
{
    double omega = (2. * PI) / period;
    return 1. / pow(d * pow(omega,order), 2);
}

arma::ivec stepwiseIndices(int m, int n)
{
    return arma::conv_to<arma::ivec>::from(
            arma::linspace(0, (float)m-0.1, n) );
}

arma::ivec viaPointIndices(int m, int n)
{
    float a = 0.;
    float b = n-1;

    ivec inds = zeros<ivec>(m);
    for( int i = 0; i < m; i++ )
        inds(i) = (int)(a + i*(b-a)/(m-1));
    return inds;
}

void stepwiseReference( arma::mat* MuQ, arma::mat* Q, const mat& Mu, const cube& Sigma, int n, int order, int dim, double endWeight )
{
    int m = Mu.n_cols;
    int cDim = order*dim;
    int muDim = Mu.n_rows;

    // equally distributed optimal states
    // Could try something along the lines of Fitts here based on covariance.
    arma::ivec qList =  stepwiseIndices(m, n);

    // precision matrices
    mat Lambda = zeros(muDim, m*muDim);
    for( int i = 0; i < m; i++ )
        Lambda.cols(i*muDim, muDim*(i+1)-1) = inv(Sigma.slice(i));

    *Q = zeros(cDim*n, cDim*n); // Precision matrix
    *MuQ = zeros(cDim, n); // Mean vector

    for( int i = 0; i < qList.n_rows; i++ )
    {
        // Precision matrix based on state sequence
        Q->submat(i*cDim,
                 i*cDim,
                 i*cDim+muDim-1,
                 i*cDim+muDim-1) =
        Lambda.cols(qList[i]*muDim, (qList[i]+1)*muDim-1);
        
        // Mean vector
        MuQ->submat(span(0,muDim-1), span(i,i)) = Mu.col(qList[i]);
    }

    if(endWeight > 0.0)
    {
        // Set last value for Q to enforce 0 end condition
        int ind = qList.n_rows-1;
        for( int i = 2; i < cDim; i++ )
            Q->operator()(ind*cDim+i, ind*cDim+i) = endWeight; 
    }
}

void viaPointReference( arma::mat* MuQ, arma::mat* Q, const mat& Mu, const cube& Sigma, int n, int order, int dim, double endWeight )
{
    int m = Mu.n_cols;
    int cDim = order*dim;
    int muDim = Mu.n_rows;

    // equally distributed optimal states
    // Could try something along the lines of Fitts here based on covariance.
    arma::ivec qList =  stepwiseIndices(m, n);

    // precision matrices
    mat Lambda = zeros(muDim, m*muDim);
    for( int i = 0; i < m; i++ )
        Lambda.cols(i*muDim, muDim*(i+1)-1) = inv(Sigma.slice(i));

    *Q = zeros(cDim*n, cDim*n); // Precision matrix
    *MuQ = zeros(cDim, n); // Mean vector

    ivec vI = viaPointIndices(m, n);
    for( int i = 0; i < vI.n_rows; i++ )
    {
        int t = vI[i];
        Q->submat(t*cDim,
                 t*cDim,
                 t*cDim+muDim-1,
                 t*cDim+muDim-1) 
        =
        Lambda.cols(i*muDim, (i+1)*muDim-1);

        MuQ->submat(span(0,muDim-1), span(t,t)) = Mu.col(i);
    }
    
    
    if(endWeight > 0.0)
    {
        // Set last value for Q to enforce 0 end condition
        int ind = n-1;
        for( int i = 2; i < cDim; i++ )
            Q->operator()(ind*cDim+i, ind*cDim+i) = endWeight; 
    
    }
}