使用路径规划导航迷宫(Dijkstra)

时间:2015-07-25 07:39:58

标签: algorithm bitmap dijkstra robotics motion-planning

我正在研究能够在迷宫中航行,避开障碍物并识别其中的一些物体的机器人。我有一个迷宫的单色位图,应该在机器人导航中使用。

到目前为止,我已经处理了位图图像,并将其转换为邻接列表。我现在将使用dijkstra算法来规划路径。

然而问题是我必须从bmp图像本身中提取入口点/节点和退出节点,以便dijkstra算法规划路径。

机器人的起始位置与迷宫的入口点略有不同(入口点前一两英寸),我应该使用任何"任意方法移动到入口点#34;然后应用dijkstra算法计划从迷宫的入口到出口的路径。

在途中我还必须停留在" X"标记在我附上的bmp文件中。这些X基本上都是盒子,我必须用它来装球。我将计划从入口点到出口点的路径,而不是从入口到第一个盒子,然后到第二个,然后到出口点;因为我认为盒子总是放在最短的路径上。

由于起始位置与入口点不同,我如何将我的机器人的物理位置与程序中的坐标相匹配并相应地移动它。即使入口位置与起始位置相同,也可能出现错误。我该怎么处理呢?我应该只在dijkstra提供的坐标的基础上导航,还是使用超声波来防止碰撞?如果我们是的,您能否告诉我应该如何使用这两者(超声波和坐标)?

Here's the sample Bitmap image of the maze.

1 个答案:

答案 0 :(得分:0)

我知道你需要这个用于机器人技术,但这里有一个例子,如何在java中将像素转换为数组以提供一些想法?

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.WindowConstants;

public class RobotDemo extends JFrame {
    private static final long serialVersionUID = 1L;


    public RobotDemo() {
        super("Robot Demo");
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        getContentPane().add(new RobotPanel(), BorderLayout.CENTER);
        pack();
        setResizable(false);
        setLocationRelativeTo(null);        
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                JFrame frame = new RobotDemo();
                frame.setVisible(true);
            }
        });     
    }
}

interface Constants {

    public static final int TILE_WIDTH = 32;
    public static final int TILE_HEIGHT = 32;
    public static final int NUM_TILE_COLS = 20;
    public static final int NUM_TILE_ROWS = 10;
    public static final int PIXEL_STEPS = 3;
    public static final int REFRESH_RATE = 200;
    public static final Dimension PANEL_SIZE = new Dimension(TILE_WIDTH * NUM_TILE_COLS, TILE_HEIGHT * NUM_TILE_ROWS);  

    public static enum RobotState {
        awaiting_instruction,
        moving_north,
        moving_south,
        moving_east,
        moving_west     
    };

    public static enum RobotInstruction {
        NORTH,
        SOUTH,
        EAST,
        WEST    
    }

    public void draw(Graphics g);
}

class RobotPanel extends JPanel implements Constants, ActionListener {
    private static final long serialVersionUID = 1L;    

    private Timer timer = new Timer(REFRESH_RATE, this);
    private Map map = new Map();
    private Robot robot = new Robot(map);

    public RobotPanel() {
        timer.start();
    }

    public Dimension getPreferredSize() { return PANEL_SIZE; }
    public Dimension getMinimumSize() { return PANEL_SIZE; }
    public Dimension getMaximumSize() { return PANEL_SIZE; }

    protected void paintComponent(Graphics g) {
        super.paintComponent(g);        
        map.draw(g);
        robot.draw(g);
        draw(g);
    }

    public void actionPerformed(ActionEvent e) {
        robot.update(); 
        repaint();
    }

    public void draw(Graphics g) {
        for(int r = 0; r < NUM_TILE_ROWS; r++) {
            for(int c = 0; c < NUM_TILE_COLS; c++) {
                g.drawRect(c * TILE_WIDTH, r * TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT);
            }
        }               
    }
}

class Robot implements Constants {

    private RobotState state = RobotState.moving_east;
    private int row = TILE_HEIGHT;
    private int col = TILE_WIDTH;

    private int mapX = 1;
    private int mapY = 1;
    private Map map;

    int nextRowCheck = 1;
    int nextColCheck = 2;

    public Robot(Map m) {
        map = m;
    }

    public int getRow() {
        return mapY;
    }

    public int getCol() {
        return mapX;
    }

    private boolean needsNewInstruction(){
        int newRow = row;
        int newCol = col;

        if(state == RobotState.moving_north) newRow -= PIXEL_STEPS;
        if(state == RobotState.moving_south) newRow += PIXEL_STEPS;
        if(state == RobotState.moving_east) newCol += PIXEL_STEPS;
        if(state == RobotState.moving_west) newCol -= PIXEL_STEPS;

        if((newRow / TILE_HEIGHT) != mapY) return true;
        if((newCol / TILE_WIDTH) != mapX) return true; 

        return false;
    }


    public void draw(Graphics g) {
        Color c = g.getColor();
        g.setColor(Color.GREEN);
        g.fillRect(col, row, TILE_WIDTH, TILE_HEIGHT);
        g.setColor(c);
    }

    public void update() {

        System.out.println("UPDATE [" + row + "][" + col + "] = [" + (row / TILE_HEIGHT) + "][" + (col / TILE_WIDTH) + "]");


        if(needsNewInstruction()) {
            System.out.println("NEEDS NEW INSTRUCTION [" + row + "][" + col + "] = [" + (row / TILE_HEIGHT) + "][" + (col / TILE_WIDTH) + "]");

            mapX = nextColCheck;
            mapY = nextRowCheck;
            System.out.println("UPDATED MAP REFERENCE [" + mapY + "][" + mapX  + "]");

            row = mapY * TILE_HEIGHT;
            col = mapX * TILE_WIDTH;
            System.out.println("UPDATED PIXEL REFERENCE [" + row + "][" + col  + "]");

            RobotInstruction instruction = map.getNextInstruction(this);

            if(instruction == RobotInstruction.NORTH) {
                state = RobotState.moving_north;
                nextRowCheck = mapY - 1;
            }
            if(instruction == RobotInstruction.SOUTH) {
                state = RobotState.moving_south;
                nextRowCheck = mapY + 1;
            }
            if(instruction == RobotInstruction.EAST) {
                state = RobotState.moving_east;
                nextColCheck = mapX + 1;
            }
            if(instruction == RobotInstruction.WEST) {
                state = RobotState.moving_west;
                nextColCheck = mapX - 1;
            }
        }
        move();
    }

    public void move() {
        if(state == RobotState.moving_north) row -= PIXEL_STEPS;
        if(state == RobotState.moving_south) row += PIXEL_STEPS;
        if(state == RobotState.moving_east) col += PIXEL_STEPS;
        if(state == RobotState.moving_west) col -= PIXEL_STEPS;
    }
}

class Map implements Constants {
    int[][] map = new int[][] {
            {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
            {1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
            {1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
            {1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
            {1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
            {1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
            {1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
            {1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
            {1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
            {1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
            {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
            {1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
            {1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
            {1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},           
            {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
    };  

    public Map() {

    }

    public RobotInstruction getNextInstruction(Robot robot) {
        int row = robot.getRow();
        int col = robot.getCol();

        System.out.println("GET NEXT INSTRUCTION FOR [" + row + "][" + col + "]");

        if(map[row][col + 1] == 0) return RobotInstruction.EAST;
        if(map[row + 1][col] == 0) return RobotInstruction.SOUTH;
        if(map[row - 1][col] == 0) return RobotInstruction.NORTH;
        if(map[row][col - 1] == 0) return RobotInstruction.WEST;

        return null;
    }

    public void draw(Graphics g) {
        Color color = g.getColor();
        for(int r = 0; r < NUM_TILE_ROWS; r++) {
            for(int c = 0; c < NUM_TILE_COLS; c++) {
                g.setColor(map[r][c] == 0 ? Color.CYAN : Color.RED);
                g.fillRect(c * TILE_WIDTH, r * TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT);
            }
        }
        g.setColor(color);
    }
}

以下是如何使用路线填充导航阵列的示例。上面的代码不使用下面的代码,所以你必须自己做...

public class Maze {

    private static final char E = 'E';   // Ending position
    private static final char X = 'X';   // Wall
    private static final char O = ' ';   // Space
    private static final char L = 'L';   // Left
    private static final char R = 'R';   // Right
    private static final char U = 'U';   // Up
    private static final char D = 'D';   // Down

    private static final char FALSE = '0';   // Not accessible
    private static final char TRUE = '1';    // Is accessible
    private static final Node END_NODE = new Node(4, 4);

    private static final int[] ROW_DIRECTIONS = {-1, 1,  0, 0};  
    private static final int[] COL_DIRECTIONS = { 0, 0, -1, 1};
    private static final char[][] OPPOSITES = new char[][] {{O, D, O},{R, O, L},{O, U, O}};

    public static void main(String[] args) {

        char[][] maze = new char[][] { 
            {X, X, X, X, X, X},
            {X, O, O, X, O, X},
            {X, O, X, X, O, X},
            {X, O, O, O, X, X},
            {X, X, O, X, O, X},
            {X, O, O, O, O, X},
            {X, O, X, X, O, X},
            {X, X, X, X, X, X}};

        // PLOT THE DESTINATION CELL AND ADD IT TO LIST
        List<Node> nodes = new ArrayList<Node>();
        nodes.add(END_NODE);
        maze[END_NODE.row][END_NODE.col] = E;

        // PRINT THE MAZE BEFORE ANY CALCULATIONS
        printMaze(maze);

        // SOLVE THE MAZE
        fillMaze(maze, nodes);
        printMaze(maze);

        // CONVERT MAZE TO AN ADJACENCY MATRIX
        compileMaze(maze);
        printMaze(maze);
    }

    /**
     * The parallel arrays define all four directions radiating from
     * the dequeued node's location. 
     * 
     * Each node will have up to four neighboring cells; some of these
     * cells are accessible, some are not. 
     * 
     * If a neighboring cell is accessible, we encode it with a directional
     * code that calculates the direction we must take should we want to 
     * navigate to the dequeued node's location from this neighboring cell.
     * 
     * Once encoded into our maze, this neighboring cell is itself queued 
     * up as a node so that recursively, we can encode the entire maze.
     */
    public static final void fillMaze(char[][] maze, List<Node> nodes) {

        // dequeue our first node
        Node destination = nodes.get(0);
        nodes.remove(destination);

        // examine all four neighboring cells for this dequeued node
        for(int index = 0; index < ROW_DIRECTIONS.length; index++) {
            int rowIndex = destination.row + ROW_DIRECTIONS[index];
            int colIndex = destination.col + COL_DIRECTIONS[index];

            // if this neighboring cell is accessible, encode it and add it
            // to the queue
            if(maze[rowIndex][colIndex] == O) {
                maze[rowIndex][colIndex] = getOppositeDirection(ROW_DIRECTIONS[index], COL_DIRECTIONS[index]);
                nodes.add(new Node(rowIndex, colIndex));
            }
        }
        // if our queue is not empty, call this method again recursively 
        // so we can fill entire maze with directional codes
        if(nodes.size() > 0) {
            fillMaze(maze, nodes);
        }
    }

    /**
     * Converts the maze to an adjacency matrix. 
     */
    private static void compileMaze(char[][] maze) {
        for(int r = 0; r < maze.length; r++) {
            for(int c = 0; c < maze[0].length; c++) {
                if(maze[r][c] == X || maze[r][c] == O) {
                    maze[r][c] = FALSE;
                }
                else {
                    maze[r][c] = TRUE;
                }
            }
        }
    }

    /**
     * prints the specified two dimensional array 
     */
    private static final void printMaze(char[][] maze) {
        System.out.println("====================================");
        for(int r = 0; r < maze.length; r++) {
            for(int c = 0; c < maze[0].length; c++) {
                System.out.print(maze[r][c] + "  ");
            }
            System.out.print("\n");
        }
        System.out.println("====================================");
    }

    /**
     * Simply returns the opposite direction from those specified 
     * by our parallel direction arrays in method fillMaze. 
     * 
     * coordinate 1, 1 is the center of the char[][] array and 
     * applying the specified row and col offsets, we return the
     * correct code (opposite direction)
     */
    private static final char getOppositeDirection(int row, int col) {
         return OPPOSITES[1 + row][1 + col];
    }
}

class Node {
    int row;
    int col;

    public Node(int rowIndex, int colIndex) {
        row = rowIndex;
        col = colIndex;
    }
}