matlab lattice,Lattice.m · octopus/robotics-toolbox-matlab - Gitee.com

羊舌自强
2023-12-01

%Lattice Lattice planner navigation class

%

% A concrete subclass of the abstract Navigation class that implements the

% lattice planner navigation algorithm over an occupancy grid. This

% performs goal independent planning of kinematically feasible paths.

%

% Methods::

% Lattice Constructor

% plan Compute the roadmap

% query Find a path

% plot Display the obstacle map

% display Display the parameters in human readable form

% char Convert to string

%

% Properties (read only)::

% graph A PGraph object describign the tree

%

% Example::

%

% lp = Lattice(); % create navigation object

% lp.plan('iterations', 8) % create roadmaps

% lp.query( [1 2 pi/2], [2 -2 0] ) % find path

% lp.plot(); % plot the path

%

% References::

%

% - Robotics, Vision & Control, Section 5.2.4,

% P. Corke, Springer 2016.

%

%

% See also Navigation, DXform, Dstar, PGraph.

% Notes::

% - The lattice is stored as a 3D PGraph object with coordinates x,y,theta

% where theta is stored as a multiple of pi/2. This was probably a bad

% design decision, it complicates the code a lot.

% - Using the Lattice distance metric in PGraph gives different A* results,

% valid path, same cost, just different. Blah.

% Copyright (C) 1993-2017, by Peter I. Corke

%

% This file is part of The Robotics Toolbox for MATLAB (RTB).

%

% RTB is free software: you can redistribute it and/or modify

% it under the terms of the GNU Lesser General Public License as published by

% the Free Software Foundation, either version 3 of the License, or

% (at your option) any later version.

%

% RTB is distributed in the hope that it will be useful,

% but WITHOUT ANY WARRANTY; without even the implied warranty of

% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

% GNU Lesser General Public License for more details.

%

% You should have received a copy of the GNU Leser General Public License

% along with RTB. If not, see .

%

% http://www.petercorke.com

% Peter Corke 8/2009.

classdef Lattice < Navigation

properties

iterations % number of iterations

cost % segment cost

% must be less than this.

graph % graph Object representing random nodes

vgoal % index of vertex closest to goal

vstart % index of vertex closest to start

localGoal % next vertex on the roadmap

localPath % set of points along path to next vertex

vpath % list of vertices between start and goal

grid

root

end

methods

% constructor

function lp = Lattice(varargin)

%Lattice.Lattice Create a Lattice navigation object

%

% P = Lattice(MAP, options) is a probabilistic roadmap navigation

% object, and MAP is an occupancy grid, a representation of a

% planar world as a matrix whose elements are 0 (free space) or 1

% (occupied).

%

% Options::

% 'grid',G Grid spacing in X and Y (default 1)

% 'root',R Root coordinate of the lattice (2x1) (default [0,0])

% 'iterations',N Number of sample points (default Inf)

% 'cost',C Cost for straight, left, right (default [1,1,1])

% 'inflate',K Inflate all obstacles by K cells.

%

% Other options are supported by the Navigation superclass.

%

% Notes::

% - Iterates until the area defined by the map is covered.

%

% See also Navigation.Navigation.

% invoke the superclass constructor, it handles some options

lp = lp@Navigation(varargin{:});

% create an empty graph over SE2

lp.graph = PGraph(3, 'distance', 'SE2');

% parse out Lattice specific options and save in the navigation object

opt.grid = 1;

opt.root = [0 0 0]';

opt.iterations = Inf;

opt.cost = [1 1 1];

[lp,args] = tb_optparse(opt, varargin, lp);

end

function plan(lp, varargin)

%Lattice.plan Create a lattice plan

%

% P.plan(OPTIONS) creates the lattice by iteratively building a tree of

% possible paths. The resulting graph is kept within the object.

%

% Options::

% 'iterations',N Number of sample points (default Inf)

% 'cost',C Cost for straight, left, right (default [1,1,1])

%

% Default parameter values come from the constructor

opt.iterations = lp.iterations;

opt.cost = lp.cost;

[opt,args] = tb_optparse(opt, varargin);

if isempty(lp.occgridnav) && isinf(opt.iterations)

error('RTB:Lattice:badarg', 'If no occupancy grid given then iterations must be finite');

end

lp.iterations = opt.iterations;

lp.cost = opt.cost;

% check root node sanity

if isempty(lp.root)

error('no root node specified');

end

switch length(lp.root)

case 2

lp.root = [lp.root(:); 0];

case 3

lp.root = lp.root(:);

otherwise

error('root must be 2- or 3-vector');

end

if lp.isoccupied(lp.root(1:2))

error('root node cell is occupied')

end

% build a graph over the free space

lp.message('create the graph');

lp.graph.clear(); % empty the graph

create_lattice(lp); % build the graph

fprintf('%d nodes created\n', lp.graph.n);

end

function pp = query(lp, start, goal)

%Lattice.query Find a path between two poses

%

% P.query(START, GOAL) finds a path (Nx3) from pose START (1x3)

% to pose GOAL (1x3). The pose is expressed as [X,Y,THETA].

%

if nargin < 3

error('must specify start and goal');

end

% set the goal coordinate

lp.goal = goal;

lp.start = start;

% convert angles to multiple of pi2

start(3) = round(start(3)*2/pi);

goal(3) = round(goal(3)*2/pi);

lp.vstart = lp.graph.closest(start, 0.5);

lp.vgoal = lp.graph.closest(goal, 0.5);

if isempty(lp.vstart)

error('Lattice:badarg', 'start configuration not in lattice');

end

if isempty(lp.vgoal)

error('Lattice:badarg', 'goal configuration not in lattice');

end

% find path through the graph using A* search

[lp.vpath,cost] = lp.graph.Astar(lp.vstart, lp.vgoal, 'directed');

fprintf('A* path cost %g\n', cost);

p = lp.graph.coord(lp.vpath);

if nargout > 0

pp = p';

pp(:,3) = angdiff( pp(:,3) * pi/2 );

end

end

% Handler invoked by Navigation.path() to start the navigation process

%

% - find a path through the graph

% - determine vertices closest to start and goal

% - find path to first vertex

% Invoked for each step on the path by path() method.

function n = next(lp, p)

end

function s = char(lp)

%Lattice.char Convert to string

%

% P.char() is a string representing the state of the Lattice

% object in human-readable form.

%

% See also Lattice.display.

% invoke the superclass char() method

s = char@Navigation(lp);

% add Lattice specific stuff information

s = char(s, sprintf(' grid spacing: %d', lp.grid));

s = char(s, sprintf(' costs [%d,%d,%d]', lp.cost));

s = char(s, sprintf(' iterations %d', lp.iterations));

s = char(s, sprintf(' Graph:'));

s = char(s, char(lp.graph) );

end

function plot(lp, varargin)

%Lattice.plot Visualize navigation environment

%

% P.plot() displays the occupancy grid with an optional distance field.

%

% Options::

% 'goal' Superimpose the goal position if set

% 'nooverlay' Don't overlay the Lattice graph

% get standard stuff done by the superclass

plot@Navigation(lp, varargin{:});

opt.nooverlay = false;

[opt,args] = tb_optparse(opt, varargin);

if ~opt.nooverlay

hold on

lp.showlattice(varargin{:});

hold off

end

if ~isempty(lp.vpath)

% highlight the path

hold on

lp.highlight(args{:});

hold off

end

grid on

end

% function path = animate(lp, varargin)

% path = [];

% for k=1:length(lp.vpath)-1

% v1 = lp.vpath(k);

% v2 = lp.vpath(k+1);

%

% seg = drivearc(lp, [v1, v2], 10);

% path = [path seg(:,1:end-1)];

% end

% path = [path seg(:,end)];

%

% end

end % method

methods (Access='protected')

% private methods

% create the lattice

function create_lattice(lp)

% add the root node

root = lp.graph.add_node( lp.root );

% possible destinations in root node frame

% x direction is forward

% orientation represented by integer 0-3 representing multiples of pi/2

d = lp.grid;

destinations = [

d d d % x

0 d -d % y

0 1 3 % theta *pi/2

];

% now we iterate, repeating this patter at each leaf node

iteration = 1;

while iteration <= lp.iterations

additions = 0;

for node = find(lp.graph.connectivity_out == 0) % foreach leaf node

% get the pose of this node

pose = lp.graph.coord(node);

xys = pose(1:2); heading = pose(3);

% transform the motion directions to this pose and b

xy = bsxfun(@plus, xys, homtrans(rot2(heading*pi/2), destinations(1:2,:)));

theta = mod(heading+destinations(3,:), 4);

newDestinations = [xy; theta];

% now add paths to these new poses

for i=1:numcols(destinations)

% check to see if a node for this pose already exists

v = lp.graph.closest(newDestinations(:,i), 0.5);

if isempty(v)

%node doesn't exist

if ~lp.isoccupied(newDestinations(1:2,i))

% it's not occupied

% add a new node and an edge

nv = lp.graph.add_node( newDestinations(:,i), node, lp.cost(i));

lp.graph.add_edge(node, nv, lp.cost(i));

additions = additions + 1;

end

else

% node already exists, add an edge

lp.graph.add_edge(node, v, lp.cost(i));

additions = additions + 1;

end

end

end

iteration = iteration + 1;

if additions == 0

break; % no more nodes can be added to the space

end

end

end

% Display the lattice, possible arcs, and start/goal markers if relevant

function showlattice(lp, varargin)

lineopt = {'Linewidth', 0.2, 'Color', [0.5 0.5 0.5]};

markeropt = {'bo', 'MarkerSize', 4, 'MarkerFaceColor', 'b'};

p = lp.graph.coord();

th = p(3,:);

th(th == 3) = -1;

plot3(p(1,:), p(2,:), th*pi/2, markeropt{:});

xlabel('x'); ylabel('y'); zlabel('\theta')

grid on

hold on

plot3(lp.root(1), lp.root(2), lp.root(3), 'ko', 'MarkerSize', 8);

view(0,90);

axis equal

rotate3d

% draw the lattice

for e=1:lp.graph.ne

v = lp.graph.vertices(e); % get the vertices of the edge

drawarc(lp, v, lineopt);

end

end

function highlight(lp, p)

if nargin > 1

assert(numcols(p)==3, 'path must have 3 columns');

for i=1:numrows(p)

vpath(i) = lp.graph.closest(p(i,:) );

end

else

vpath = lp.vpath;

end

% highlight the path

for k=1:length(vpath)-1

v1 = vpath(k);

v2 = vpath(k+1);

drawarc(lp, [v1, v2], {'Linewidth', 3, 'Color', 'r'});

end

end

% draw an arc

function drawarc(lp, v, lineOpts)

g = lp.graph;

% use lower resolution if lots of arcs

if lp.iterations < 4

narc = 20;

elseif lp.iterations < 10

narc = 10;

else

narc = 5;

end

v1 = v(1); v2 = v(2);

p1 = g.coord(v1);

p2 = g.coord(v2);

% frame {N} is start of the arc

theta = p1(3)*pi/2; % {0} -> {N}

T_0N = SE2(p1(1:2), theta);

dest = round( T_0N.inv * p2(1:2) ); % in {N}

if dest(2) == 0

% no heading change, straight line segment

th = [p1(3) p2(3)];

th(th == 3) = -1;

plot3([p1(1) p2(1)], [p1(2) p2(2)], th*pi/2, lineOpts{:});

else

% curved segment

c = T_0N * [0 dest(2)]';

th = ( linspace(-dest(2)/lp.grid, 0, narc) + p1(3) )*pi/2;

x = lp.grid*cos(th) + c(1);

y = lp.grid*sin(th) + c(2);

th0 = p1(3);

th0(th0==3) = -1;

thf = p2(3);

thf(thf==3) = -1;

plot3(x, y, linspace(th0, thf, narc)*pi/2, lineOpts{:});

end

end

% % this doesn't work quite properly...

% function path = drivearc(lp, v, narc)

% g = lp.graph;

%

%

% v1 = v(1); v2 = v(2);

% p1 = g.coord(v1); p1(3) = p1(3)*pi/2;

% p2 = g.coord(v2); p2(3) = p2(3)*pi/2;

%

% path = [];

%

% % frame {N} is start of the arc

% theta = p1(3); % {0} -> {N}

% T_0N = SE2(p1(1:2), theta);

%

% dest = round( T_0N.inv * p2(1:2) ); % in {N}

%

% if dest(2) == 0

% % no heading change, straight line segment

%

% for s=linspace(0, 1, narc)

% path = [path (1-s)*p1 + s*p2];

% end

% else

% % curved segment

% c = T_0N * [0 dest(2)]';

%

% th = ( linspace(-dest(2)/lp.grid, 0, narc) + p1(3) );

%

% x = lp.grid*cos(th) + c(1);

% y = lp.grid*sin(th) + c(2);

%

%

% th0 = p1(3);

% % % th0(th0==3) = -1;

% thf = p2(3);

% % % thf(thf==3) = -1;

% path = [path [x; y; linspace(th0, angdiff(thf,th0)+th0, narc)] ];

% end

% end

end % private methods

end % classdef

一键复制

编辑

Web IDE

原始数据

按行查看

历史

 类似资料: