2
votes

I am trying to simulate a network of mobile robots that uses artificial potential fields for movement planning to a shared destination xd. This is done by generating a series of m-files (one for each robot) from a symbolic expression, as this seems to be the best way in terms of computational time and accuracy. However, I can't figure out what is going wrong with my gradient computation: the analytical gradient that is being computed seems to be faulty, while the numerical gradient is calculated correctly (see the image posted below). I have written a MWE listed below, which also exhibits this problem. I have checked the file generating part of the code, and it does return a correct function file with a correct gradient. But I can't figure out why the analytic and numerical gradient are so different.

(A larger version of the image below can be found here)

Faulty gradient computation

% create symbolic variables
xd = sym('xd',[1 2]);
x = sym('x',[2 2]);

% create a potential function and a gradient function for both (x,y) pairs
% in x
for i=1:size(x,1)

phi = norm(x(i,:)-xd)/norm(x(1,:)-x(2,:));          % potential field function

xvector = reshape(x.',1,size(x,1)*size(x,2));       % reshape x to allow for gradient computation
grad = gradient(phi,xvector(2*i-1:2*i));            % compute the gradient
gradx = grad(1);grady=grad(2);                      % split the gradient in two components

% create function file names
gradfun = strcat('GradTester',int2str(i),'.m');     
phifun = strcat('PotTester',int2str(i),'.m');       

% generate two output files
matlabFunction(gradx, grady,'file',gradfun,'outputs',{'gradx','grady'},'vars',{xvector, xd});
matlabFunction(phi,'file',phifun,'vars',{xvector, xd});

end

clear all               % make sure the workspace is empty: the functions are in the files

pause(0.1)              % ensure the function file has been generated before it is called

% these are later overwritten by a specific case, but they can be used for
% debugging
x = 0.5*rand(2);
xd = 0.5*rand(1,2);

% values for the Stackoverflow case
x = [0.0533    0.0023;
     0.4809    0.3875];
xd = [0.4087    0.4343];

xp = x;     % dummy variable to keep x intact

% compute potential field and gradient for both (x,y) pairs
for i=1:size(x,1)

    % create a grid centered on the selected (x,y) pair
    xGrid = (x(i,1)-0.1):0.005:(x(i,1)+0.1);
    yGrid = (x(i,2)-0.1):0.005:(x(i,2)+0.1);

    % preallocate the gradient and potential matrices
    gradx = zeros(length(xGrid),length(yGrid));
    grady = zeros(length(xGrid),length(yGrid));
    phi   = zeros(length(xGrid),length(yGrid));

    % generate appropriate function handles
    fun   = str2func(strcat('GradTester',int2str(i)));
    fun2  = str2func(strcat('PotTester',int2str(i)));

    % compute analytic gradient and potential for each position in the xGrid and
    % yGrid vectors
    for ii = 1:length(yGrid)
        for jj = 1:length(xGrid)

            xp(i,:) = [xGrid(ii) yGrid(jj)];                % select the position
            Xvec = reshape(xp.',1,size(x,1)*size(x,2));     % turn the input into a vector
            [gradx(ii,jj),grady(ii,jj)] = fun(Xvec,xd);     % compute gradients
            phi(jj,ii) = fun2(Xvec,xd);                     % compute potential value

        end
    end

    [FX,FY] = gradient(phi);                % compute the NUMERICAL gradient for comparison

    %scale the numerical gradient
    FX = FX/0.005;
    FY = FY/0.005;

    % plot analytic result
    subplot(2,2,2*i-1)
    hold all
    xlim([xGrid(1) xGrid(end)]);
    ylim([yGrid(1) yGrid(end)]);
    quiver(xGrid,yGrid,-gradx,-grady)
    contour(xGrid,yGrid,phi)
    title(strcat('Analytic result for position ',int2str(i)));
    xlabel('x');
    ylabel('y');

    subplot(2,2,2*i)
    hold all
    xlim([xGrid(1) xGrid(end)]);
    ylim([yGrid(1) yGrid(end)]);
    quiver(xGrid,yGrid,-FX,-FY)
    contour(xGrid,yGrid,phi)
    title(strcat('Numerical result for position ',int2str(i)));
    xlabel('x');
    ylabel('y');

end

The potential field I am trying to generate is defined by an (x,y) position, in my code called xd. x is the position matrix of dimension N x 2, where the first column represents x1, x2, and so on, and the second column represents y1, y2, and so on. Xvec is simply a reshaping of this vector to x1,y1,x2,y2,x3,y3 and so on, as the matlabfunction I am generating only accepts vector inputs.

The gradient for robot i is being calculated by taking the derivative w.r.t. x_i and y_i, these two components together yield a single derivative 'vector' shown in the quiver plots. The derivative should look like this, and I checked that the symbolic expression for [gradx,grady] indeed looks like that before an m-file is generated.

1
It looks like analytical is not doing the rigth thing! - Ander Biguri
Can you give a mathematical description of how the gradient is calculated? I find x, xd and Xvec confusing to understand, and your gradients don't seem to line up with a standard x and y` derivative. - David
@David: I have added some information below the code, please let me know if it is still unclear - Wouter Kuijsters
@AnderBiguri That does indeed seem to be the case, as the quiver plot in the numerical approach is perpendicular to the contour plot, but the analytical gradient isn't. - Wouter Kuijsters
@AnderBiguri The contour plots were wrong as well, it was because the positions were calculated not using meshgrid, so everything was in the wrong places. - David

1 Answers

2
votes

To fix the particular problem given in the question, you were actually calculating phi in such a way that meant you doing gradient(phi) was not giving the correct results compared to the symbolic gradient. I'll try and explain. Here is how you created xGrid and yGrid:

% create a grid centered on the selected (x,y) pair
xGrid = (x(i,1)-0.1):0.005:(x(i,1)+0.1);
yGrid = (x(i,2)-0.1):0.005:(x(i,2)+0.1);

But then in the for loop, ii and jj were used like phi(jj,ii) or gradx(ii,jj), but corresponding to the same physical position. This is why your results were different. Another problem you had was you used gradient incorrectly. Matlab assumes that [FX,FY]=gradient(phi) means that phi is calculated from phi=f(x,y) where x and y are matrices created using meshgrid. You effectively had the elements of phi arranged differently to that, an so gradient(phi) gave the wrong answer. Between reversing the jj and ii, and the incorrect gradient, the errors cancelled out (I suspect you tried doing phi(jj,ii) after trying phi(ii,jj) first and finding it didn't work).

Anyway, to sort it all out, on the line after you create xGrid and yGrid, put this in:

[X,Y]=meshgrid(xGrid,yGrid);

Then change the code after you load fun and fun2 to:

for ii = 1:length(xGrid) %// x loop
    for jj = 1:length(yGrid) %// y loop
        xp(i,:) = [X(ii,jj);Y(ii,jj)]; %// using X and Y not xGrid and yGrid
        Xvec = reshape(xp.',1,size(x,1)*size(x,2));
        [gradx(ii,jj),grady(ii,jj)] = fun(Xvec,xd);
        phi(ii,jj) = fun2(Xvec,xd);  
    end
end

[FX,FY] = gradient(phi,0.005); %// use the second argument of gradient to set spacing

subplot(2,2,2*i-1)
hold all
axis([min(X(:)) max(X(:)) min(Y(:)) max(Y(:))]) %// use axis rather than xlim/ylim
quiver(X,Y,gradx,grady)
contour(X,Y,phi)
title(strcat('Analytic result for position ',int2str(i)));
xlabel('x');
ylabel('y');

subplot(2,2,2*i)
hold all
axis([min(X(:)) max(X(:)) min(Y(:)) max(Y(:))])
quiver(X,Y,FX,FY)
contour(X,Y,phi)
title(strcat('Numerical result for position ',int2str(i)));
xlabel('x');
ylabel('y');

I have some other comments about your code. I think your potential function is ill-defined, which is causing all sorts of problems. You say in the question that x is an Nx2 matrix, but you potential function is defined as

norm(x(i,:)-xd)/norm(x(1,:)-x(2,:));

which means if N was three, you'd have the following three potentials:

norm(x(1,:)-xd)/norm(x(1,:)-x(2,:));
norm(x(2,:)-xd)/norm(x(1,:)-x(2,:));
norm(x(3,:)-xd)/norm(x(1,:)-x(2,:));

and I don't think the third one makes sense. I think this could be causing some confusion with the gradients.

Also, I'm not sure if there is a reason to create the .m file functions in your real code, but they are not necessary for the code you posted.