Etapa de atualização do EKF-SLAM, Kalman Gain torna-se singular

16

Estou usando um EKF para SLAM e estou tendo algum problema com a etapa de atualização. Estou recebendo um aviso de que K é singular, rcondavalia near eps or NaN. Eu acho que rastreei o problema até a inversão de Z. Existe uma maneira de calcular o ganho de Kalman sem inverter o último termo?

Não tenho 100% de certeza de que essa é a causa do problema, por isso também coloquei todo o meu código aqui . O principal ponto de entrada é slam2d.

function [ x, P ] = expectation( x, P, lmk_idx, observation)
    % expectation
    r_idx = [1;2;3];
    rl = [r_idx; lmk_idx];

    [e, E_r, E_l] = project(x(r), x(lmk_idx)); 
    E_rl = [E_r E_l];
    E = E_rl * P(rl,rl) * E_rl';

    % innovation
    z = observation - e;
    Z = E;

    % Kalman gain
    K = P(:, rl) * E_rl' * Z^-1;

    % update
    x = x + K * z;
    P = P - K * Z * K';
end


function [y, Y_r, Y_p] = project(r, p)     
    [p_r, PR_r, PR_p] = toFrame2D(r, p);
    [y, Y_pr]   = scan(p_r);
    Y_r = Y_pr * PR_r;
    Y_p = Y_pr * PR_p;    
end


function [p_r, PR_r, PR_p] = toFrame2D(r , p)
    t = r(1:2);
    a = r(3);
    R = [cos(a) -sin(a) ; sin(a) cos(a)];
    p_r = R' * (p - t);
    px = p(1);
    py = p(2);
    x = t(1);
    y = t(2);
    PR_r = [...
        [ -cos(a), -sin(a),   cos(a)*(py - y) - sin(a)*(px - x)]
        [  sin(a), -cos(a), - cos(a)*(px - x) - sin(a)*(py - y)]];
    PR_p = R';    
end


function [y, Y_x] = scan(x)
    px = x(1);
    py = x(2);
    d = sqrt(px^2 + py^2);
    a = atan2(py, px);
    y = [d;a];
    Y_x =[...
    [     px/(px^2 + py^2)^(1/2), py/(px^2 + py^2)^(1/2)]
    [ -py/(px^2*(py^2/px^2 + 1)), 1/(px*(py^2/px^2 + 1))]];
end

Edições: project(x(r), x(lmk))deveria ter sido project(x(r), x(lmk_idx))e agora está corrigido acima.

K vai singular depois de um tempo, mas não imediatamente. Eu acho que são cerca de 20 segundos ou mais. Vou tentar as mudanças que @josh sugeriu quando chegar em casa hoje à noite e postar os resultados.

Atualização 1:

7 x 2(P(rl,rl) * E_rl') * inv( Z )5 x 2

K torna-se singular após 4,82 segundos, com medições a 50Hz (241 etapas). Seguindo o conselho aqui , tentei o K = (P(:, rl) * E_rl')/Zque resulta em 250 etapas antes que um aviso sobre K ser singular seja produzido.

Isso me diz que o problema não está na inversão da matriz, mas em outro lugar que está causando o problema.

Atualização 2

Meu loop principal é (com um objeto de robô para armazenar x, P e ponteiros de referência):

for t = 0:sample_time:max_time
    P = robot.P;
    x = robot.x;
    lmks = robot.lmks;
    mapspace = robot.mapspace;

    u = robot.control(t);
    m = robot.measure(t);

    % Added to show eigenvalues at each step
    [val, vec] = eig(P);
    disp('***')
    disp(val)

    %%% Motion/Prediction
    [x, P] = predict(x, P, u, dt);

    %%% Correction
    lids = intersect(m(1,:), lmks(1,:));  % find all observed landmarks
    lids_new = setdiff(m(1,:), lmks(1,:));
    for lid = lids
        % expectation
        idx = find (lmks(1,:) == lid, 1);
        lmk = lmks(2:3, idx);
        mid = m(1,:) == lid;
        yi = m(2:3, mid);

        [x, P] = expectation(x, P, lmk, yi);
    end  %end correction

    %%% New Landmarks

    for id = 1:length(lids_new)
    % if id ~= 0
        lid = lids_new(id);
        lmk = find(lmks(1,:)==false, 1);
        s = find(mapspace, 2);
        if ~isempty(s)
            mapspace(s) = 0;
            lmks(:,lmk) = [lid; s'];
            yi = m(2:3,m(1,:) == lid);

            [x(s), L_r, L_y] = backProject(x(r), yi);

            P(s,:) = L_r * P(r,:);
            P(:,s) = [P(s,:)'; eye(2)];
            P(s,s) = L_r * P(r,r) * L_r';
        end
    end  % end new landmarks

    %%% Save State
    robot.save_state(x, P, mapspace, lmks)
    end  
end

No final desse loop, eu salvo x e P no robô, então acredito que estou propagando a covariância por cada iteração.

10-2

munk
fonte
1
Você está propagando a incerteza? Os autovalores de sua covariância são arbitrariamente pequenos ou grandes?
Josh Vander Gancho
1
O que você coloca em pastebin são os autovetores, não os autovalores. faça o seguinte: [v, d] = eig (P). disp (diag (d)). ou apenas disp (eig (P)). Em seguida, você pode verificar as seguintes condições necessárias: Todos os autovalores são> 0 a cada etapa (eles devem estar em prática). Eles aumentam após a propagação e diminuem após medições / correções? Na minha experiência, esse é geralmente o problema.
Josh Vander Gancho
2
Algo está errado se seus valores próprios forem negativos. Quando você inicializa um ponto de referência, qual é a incerteza associada à sua primeira posição estimada?
Josh Vander Gancho
A observação é um par. Quando o primeiro marco é inicializado, ele possui uma covariância de [5.8938, 3.0941; 3,0941, 2,9562]. Para o segundo, sua covariância é [22.6630 -14.3822; -14.3822, 10.5484] A matriz completa está aqui
munk

Respostas:

5

Acabei de ver sua postagem agora e talvez seja tarde demais para realmente ajudá-lo ... mas, caso ainda esteja interessado nisso: acho que identifiquei seu problema.

Você escreve a matriz de covariância da inovação da seguinte maneira

E = jacobian measure * P * jacobian measure

Pode ser bom em teoria, mas o que acontece é que, se seu algoritmo for eficaz e, especialmente, se você estiver trabalhando em uma simulação: as incertezas diminuirão, especialmente nas direções de sua medição. Então E, tenderá a [[0,0][0,0]].

Para evitar esse problema, o que você pode fazer é adicionar um ruído de medição correspondente às incertezas na medição e sua covariância de inovação se torna

E= Jac*P*Jac'+R

onde Ré a covariância do ruído de medição (matriz diagonal em que os termos na diagonal são os quadrados do desvio padrão do ruído). Se você realmente não quiser considerar o ruído, pode torná-lo o menor que desejar.

Acrescento também que sua atualização de covariância me parece estranha, a fórmula clássica é:

P=P - K * jacobian measure * P

Eu nunca vi sua fórmula escrita em outro lugar, posso estar correta, mas se você não tiver certeza, verifique.

Jaeger0
fonte
Ah, o velho truque "sal a covariância".
Josh Vander Gancho
1

KP(Nr+Neu)×(Nr+Neu)NrNeu

K = P(:, rl) * E_rl' * Z^-1

que eu acho que deveria ser

(P(rl,rl) * E_rl') * inv(Z).

(veja: divisão matricial ). Verifique o tamanho de K.

Além disso: forneça um pouco mais de informação: Kvai singular imediatamente ou somente depois de algum tempo?

Isso me preocupa: project(x(r), x(lmk));já que lmknão está definido.

Josh Vander Hook
fonte