├── PathsCell2Array.m ├── README.md ├── Selection.m ├── calculateSandT.m ├── childCreating.m ├── createBusyList.m ├── drawAllDroneRoad.m ├── drawDroneRoad.m ├── drawSynchronousAllDronesRoad.m ├── geneticAlgotithm.m ├── getAllPaths.m ├── getAllPathsBetween.m ├── getBestPossiblePath.m ├── getColors.m ├── getDistancesOfPaths.m ├── getStartAndStopPoints.m ├── getStations.m ├── main.m ├── mapa.jpg └── takePlacesInStations.m /PathsCell2Array.m: -------------------------------------------------------------------------------- 1 | function M = PathsCell2Array(finishPaths) 2 | %function returns the numeric array of all paths passed found with "finishPaths" 3 | %if one path is longer than others it fills end of the rest with zeros 4 | max_path_len = 0; 5 | for i=1:length(finishPaths) 6 | path_len = length(finishPaths{i}); 7 | if path_len>max_path_len 8 | max_path_len = path_len; 9 | end 10 | end 11 | 12 | %drones_population = length(finishPaths); 13 | drones_population = length(finishPaths); 14 | M = zeros(drones_population, max_path_len); 15 | 16 | for drone = 1:drones_population 17 | nodes = (finishPaths{drone})'; 18 | size = length(nodes); 19 | M( drone,1 : size) = nodes; 20 | end 21 | end -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Optimal_path_planning-Genetic_Algorithm 2 | The use of a genetic algorithm to determine **optimal routes** for drones, where the obtained route is expected to fulfill the condition of **minimizing the energy consumption**. 3 | 4 | 5 | ### The established assumptions: 6 | 1. The drones analyzed are unitary, treated as individual drones (the sequence of operations of each drone is performed for different customers). 7 | 2. The distribution of routes and destinations is changing, while the location of docking stations is known and fixed. 8 | 3. The coordinated travel speeds of all drones, over all specified sections, are established. 9 | 10 | 11 | ### Adopted additional assumptions: 12 | 1. The connection between a given start and finish station cannot be direct. 13 | 2. If at a given station, at a given time, the limit of drones staying is filled, then another energy-optimal route is chosen for that drone, for which this problem does not occur. 14 | 3. There is a limit on the number of drones that can stay at a given station at the same time. 15 | 4. Energy consumption is dependent on weather conditions (weather conditions, especially windy conditions, can cause higher energy consumption). 16 | 17 | ## How the created genetic algorithm works: 18 | 1) A population of particles is created based on randomly generated numbers, in a range bounded by the number of docking stations. 19 | 2) From the generated particles, elements that are the start or end point of the drone are cut out. 20 | 3) Based on the distances achieved for each particle (which at this stage are equivalent to energy consumption), 'parents' are selected. 21 | 4) The "children" are created: 22 | - First a piece of parent one is randomly selected, then the gaps are filled with parent two. 23 | - The whole is cut down to a random size, since we need to determine a path not through all points, but through an undefined number of them. 24 | - At the beginning and end of the resulting sequence, the start and end points of the drone are added respectively. 25 | 5) The generated paths (children) are returned as a list of potential paths between given points. 26 | 27 | ## How the created application works: 28 | 1) The necessary configuration parameters are defined. 29 | 2) A list of docking stations is downloaded, along with the number of charging spots they have. 30 | 3) For each drone a path is determined so that the energy consumption is minimal: 31 | - Through a genetic algorithm, potential paths are generated. 32 | - The cost of each path is calculated considering the weather conditions. 33 | - The paths that do not have a set minimum number of points between the start and end points are eliminated. 34 | - It is checked if at least one charging location is available at the destination docking station in the next steps. 35 | - The path with the lowest energy consumption that satisfies the set constraints is returned. 36 | 4) The received data is collected accordingly. 37 | 5) The generation of graphs showing the result is performed. 38 | 39 | ## An example of a obtained graph. 40 | result_example 41 | -------------------------------------------------------------------------------- /Selection.m: -------------------------------------------------------------------------------- 1 | %We select the individuals who will be more likely to be parents in the 2 | %next iteration, based on Roulette wheel selection Reproduction Operator. 3 | 4 | function sel = Selection(distances, N) 5 | 6 | s = size(distances, 1); 7 | max_dist = max(distances); 8 | 9 | w = 1 - distances ./ max_dist; 10 | b = round(w * s); 11 | counted = s + sum(b); 12 | temp = zeros(counted, 1); 13 | temp(1:s) = 1:s; 14 | m = s + 1; 15 | 16 | for i = 1:size(b, 1) 17 | bonus = b(i); 18 | temp(m : m - 1 + bonus) = i; 19 | m = m + bonus; 20 | end 21 | 22 | temp = temp(randperm(size(temp, 1))); 23 | [C ia] = unique(temp, 'first'); 24 | temp = temp(sort(ia)); 25 | 26 | sel = temp(1:N); 27 | 28 | %In our case, elements for whom the distance value is smaller, 29 | %have greater probability, to be chosen as parents. -------------------------------------------------------------------------------- /calculateSandT.m: -------------------------------------------------------------------------------- 1 | function [s, t] = calculateSandT(len) 2 | s = []; 3 | t = []; 4 | for i = 1:len 5 | x = zeros(1, len-1); 6 | y = zeros(1, len-1); 7 | cnt = 1; 8 | for j = 1:len 9 | if j ~= i 10 | y(cnt)= i; 11 | x(cnt)= j; 12 | cnt = cnt + 1; 13 | 14 | end 15 | end 16 | s = cat(2,s,x); 17 | t = cat(2,t,y); 18 | end 19 | end -------------------------------------------------------------------------------- /childCreating.m: -------------------------------------------------------------------------------- 1 | function child_end = childCreating(population, parent1, parent2, point1, point2) 2 | 3 | child = zeros(size(population, 1), 1); % same zera, tyle ile stacji 4 | 5 | % copy random portion of genes from the first parent 6 | range1 = randperm(size(population, 1)); % losuje tablice liczb od 1 do sizu populacji 7 | range1 = sort(range1(1:2)); % sortuje dwie pierwsze wartosci w tablicy 8 | slice1 = range1(1):range1(2); % tworzy tablice intów z zakresu wyznaczonego przez wyselekcjonowane powyzej wartosci 9 | genes1 = population(slice1, parent1); % z tablicy populacji bierze elementy spod indeksów rodzica i slica 10 | child(slice1) = genes1; %wpisuje otrzymane wartosci pod indeksami slica w dziecku 11 | 12 | % copy rest of the genes from the second parent 13 | p2 = population(:, parent2); % bierze cały wiersz? kolumnę? z populacji 14 | genes2 = p2(find(not(ismember(p2, genes1)))); % znajduje punkty które jeszcze nie są w dziecku (te z genes1 są) 15 | slice2 = [1:range1(1)-1, range1(2)+1:size(child, 1)]; % wyznacza pozostale indexy do wpisania wartosci 16 | child(slice2) = genes2; % uzupelnia dziecko 17 | 18 | % wycinamy randomowo długi fragment dziecka 19 | size1 = randi([1, size(population, 1)]); 20 | size2 = randi([1, size(population, 1)]); 21 | sorted = sort([size1 size2]); 22 | child_elems = sorted(1):sorted(2); 23 | child = child(child_elems); 24 | 25 | % dodawanie punktu poczatkowego i koncowego 26 | child_end = [point1; child; point2]; -------------------------------------------------------------------------------- /createBusyList.m: -------------------------------------------------------------------------------- 1 | function busy = createBusyList(chargingPlaces) 2 | statLen = length(chargingPlaces); 3 | busy = zeros(statLen, statLen); 4 | for i=1:statLen 5 | for j=1:statLen 6 | busy(i, j) = chargingPlaces(i); 7 | end 8 | end 9 | end -------------------------------------------------------------------------------- /drawAllDroneRoad.m: -------------------------------------------------------------------------------- 1 | function drawAllDroneRoad(roads, coordinates, label, figureNumber) 2 | xall = coordinates(:,2); 3 | yall = coordinates(:,1); 4 | 5 | 6 | figure(figureNumber) 7 | hold on; 8 | grid on 9 | axis([10 90 10 100]) 10 | title('Finish plot') 11 | xlabel('x') 12 | ylabel('y') 13 | 14 | for i = 1:length(roads) 15 | road = roads(i); 16 | road = road{1, :}; 17 | x = coordinates(road,2); 18 | y = coordinates(road,1); 19 | plot(x, y, '-', 'MarkerSize', 8, 'LineWidth', 2) 20 | end 21 | 22 | %insert map background 23 | I = imread('mapa.jpg'); 24 | h = image(xlim,ylim,I); 25 | uistack(h,'bottom') 26 | 27 | % points 28 | plot(xall, yall, 'go', 'MarkerSize', 8, 'LineWidth',3, 'Color', 'b') 29 | text(xall+0.3, yall+0.3, label(:), 'FontSize',12, 'fontweight','bold') 30 | -------------------------------------------------------------------------------- /drawDroneRoad.m: -------------------------------------------------------------------------------- 1 | function drawDroneRoad(road, coordinates, label, figureNumber) 2 | 3 | colors = getColors(); 4 | 5 | xall = coordinates(:,2); 6 | yall = coordinates(:,1); 7 | 8 | x = coordinates(road,2); 9 | y = coordinates(road,1); 10 | 11 | figure(figureNumber) 12 | hold on; 13 | plot(xall, yall, 'go', 'MarkerSize', 8, 'LineWidth',3, 'Color', 'b') 14 | plot(x, y, '-go', 'MarkerSize', 8, 'LineWidth',3, 'Color', colors(figureNumber,:)) 15 | plot(x(1), y(1), 'm*', 'MarkerSize', 8) 16 | grid on 17 | axis([10 90 10 100]) 18 | title('Obtained route for the drone - energy-saving solution') 19 | xlabel('x') 20 | ylabel('y') 21 | 22 | %insert map background 23 | I = imread('mapa.jpg'); 24 | h = image(xlim,ylim,I); 25 | uistack(h,'bottom') 26 | 27 | % point labels 28 | text(xall+0.3, yall+0.3, label(:), 'FontSize',12, 'fontweight','bold') 29 | hold off; 30 | end 31 | -------------------------------------------------------------------------------- /drawSynchronousAllDronesRoad.m: -------------------------------------------------------------------------------- 1 | function drawSynchronousAllDronesRoad(finishPaths, coordinates, labels) 2 | colors = getColors(); 3 | 4 | xall = coordinates(:,2); 5 | yall = coordinates(:,1); 6 | 7 | %generate array of paths 8 | M = PathsCell2Array(finishPaths); 9 | 10 | drones_population = length(finishPaths); 11 | max_path_len = length(M(1,:)); 12 | 13 | %matrices of progress of every drone 14 | progresses_x = zeros(drones_population,max_path_len); 15 | progresses_y = zeros(drones_population,max_path_len); 16 | 17 | step = 0; 18 | figure(drones_population+1) 19 | 20 | plot(xall, yall, 'go', 'MarkerSize', 8, 'LineWidth',3, 'Color', 'b') 21 | grid on 22 | axis([10 120 10 100]) 23 | title('Obtained route for the drone - energy-saving solution') 24 | xlabel('x') 25 | ylabel('y') 26 | hold on; 27 | legendInfo{1}=('Stations'); 28 | legend(legendInfo); 29 | 30 | %insert map background 31 | I = imread('mapa.jpg'); 32 | h = image(xlim,ylim,I); 33 | uistack(h,'bottom') 34 | text(xall+0.3, yall+0.3, labels(:), 'FontSize',12, 'fontweight','bold') 35 | %iterate through steps 36 | 37 | while max_path_len > step 38 | pause(1); 39 | step = step+1; 40 | 41 | for drone = 1:drones_population 42 | path = M(drone,:); 43 | L = path ~= 0; 44 | new_path = path(L); 45 | x = coordinates(new_path,2); 46 | y = coordinates(new_path,1); 47 | 48 | if step<=length(x) 49 | progresses_x(drone,step) = x(step); 50 | progresses_y(drone,step) = y(step); 51 | end 52 | 53 | I = find(progresses_x(drone,:)); 54 | x_to_disp = progresses_x(drone,I); 55 | y_to_disp = progresses_y(drone,I); 56 | 57 | plot(x_to_disp, y_to_disp, '-go', 'MarkerSize', 5, 'LineWidth',2,'Color',colors(drone,:)); 58 | legendInfo{drone+1}=(['Drone ' num2str(drone)]); 59 | legend(legendInfo); 60 | end 61 | end 62 | 63 | end 64 | -------------------------------------------------------------------------------- /geneticAlgotithm.m: -------------------------------------------------------------------------------- 1 | function all_points = geneticAlgotithm(coordinates, p1, p2, popsize, it) 2 | 3 | stat_numb = size(coordinates, 1); 4 | 5 | % population init 6 | population = zeros(stat_numb-2, popsize); 7 | distances = zeros(popsize, 1); 8 | 9 | for pop = 1:popsize 10 | temp_road = randperm(stat_numb)'; 11 | road = zeros(length(temp_road)-2, 1); 12 | idx = 1; 13 | for ran = 1:length(temp_road) 14 | el = temp_road(ran); 15 | if el ~= p1 && el ~= p2 16 | road(idx) = el; 17 | idx = idx + 1; 18 | end 19 | end 20 | 21 | population(:, pop) = road; 22 | 23 | x = coordinates(road, 2); 24 | y = coordinates(road, 1); 25 | 26 | dx = circshift(x, -1) - x; 27 | dy = circshift(y, -1) - y; 28 | 29 | d = sum(sqrt(dx .* dx + dy .* dy)); 30 | distances(pop) = d; 31 | end 32 | [distances, idx] = sort(distances); 33 | population = population(:, idx); 34 | 35 | all_points = cell(it, 1); 36 | 37 | % serching best solutions in the range of it 38 | for iter=1:it 39 | parents = Selection(distances, 2); 40 | child = childCreating(population, parents(1), parents(2), p1, p2); 41 | 42 | all_points{iter} = child; 43 | end -------------------------------------------------------------------------------- /getAllPaths.m: -------------------------------------------------------------------------------- 1 | function [allpaths, A] = getAllPaths(coordinates, s, t, p1, p2, popSize, it, conditions) 2 | len = length(coordinates); 3 | d = sqrt(sum((coordinates(s,:)-coordinates(t,:)).^2,2)); 4 | s_len = length(s); 5 | 6 | %apply conditions to cost of travel 7 | real_cost = d.*conditions; 8 | 9 | A = sparse(s, t, real_cost, len, len); 10 | A = A+A'; 11 | 12 | allpaths = geneticAlgotithm(coordinates, p1, p2, popSize, it); 13 | end 14 | -------------------------------------------------------------------------------- /getAllPathsBetween.m: -------------------------------------------------------------------------------- 1 | function [paths, distances] = getAllPathsBetween(p1, p2, popSize, it, coordinates, minNodes, weather_conditions) 2 | len = length(coordinates); 3 | [s, t] = calculateSandT(len); 4 | 5 | [allPaths, A] = getAllPaths(coordinates, s, t, p1, p2, popSize, it, weather_conditions); 6 | [paths, distances] = getDistancesOfPaths(A, allPaths, minNodes); 7 | end -------------------------------------------------------------------------------- /getBestPossiblePath.m: -------------------------------------------------------------------------------- 1 | function [bestPath, bestDistance] = getBestPossiblePath(paths, distances, places) 2 | isBestPath = 0; 3 | idx = 1; 4 | pathsLen = length(paths); 5 | 6 | while ~isBestPath 7 | if idx > pathsLen 8 | error('Error. Cannot find possible path') 9 | end 10 | bestPath = paths(idx); 11 | bestDistance = distances(idx); 12 | bestPath = bestPath{1, :}; 13 | 14 | % check places in stations in every step 15 | isPlaceInStation = 1; 16 | for i=1:length(bestPath) 17 | el = bestPath(i); 18 | if places(el, i) < 1 19 | isPlaceInStation = 0; 20 | end 21 | end 22 | 23 | if isPlaceInStation 24 | isBestPath = 1; 25 | end 26 | 27 | idx = idx + 1; 28 | end 29 | end -------------------------------------------------------------------------------- /getColors.m: -------------------------------------------------------------------------------- 1 | function colors = getColors() 2 | colors = [1 0 0; % red 3 | 0 0 1; % blue 4 | 0 1 0; % green 5 | 1 0 1 ; 6 | 1 1 0; 7 | 0.7647 0.5373 0.7804; 8 | 0.6510 0.6510 0.6510; 9 | 0.6353 0.0784 0.1843; 10 | 1.0000 0.4118 0.1608; 11 | 0.0588 1.0000 1.0000 12 | ]; 13 | end -------------------------------------------------------------------------------- /getDistancesOfPaths.m: -------------------------------------------------------------------------------- 1 | 2 | function [paths_sorted, dist_sorted] = getDistancesOfPaths(A, allp, minNodes) 3 | distances = []; 4 | paths = {}; 5 | 6 | np = length(allp); 7 | 8 | for k=1:np 9 | pk = allp{k}; 10 | if length(pk) > minNodes 11 | i = sub2ind(size(A),pk(1:end-1),pk(2:end)); 12 | d = full(sum(A(i))); 13 | distances(end+1) = d; 14 | paths{end+1} = pk; 15 | end 16 | end 17 | [dist_sorted,I] = sort(distances); 18 | paths_sorted = paths(I); 19 | end -------------------------------------------------------------------------------- /getStartAndStopPoints.m: -------------------------------------------------------------------------------- 1 | function [p1, p2] = getStartAndStopPoints(coordinates) 2 | p1 = 0; 3 | p2 = 0; 4 | while p1 == p2 5 | p = randi(length(coordinates), 1, 2); 6 | p1 = p(1); 7 | p2 = p(2); 8 | end 9 | end -------------------------------------------------------------------------------- /getStations.m: -------------------------------------------------------------------------------- 1 | function [coordinates, labels, charging_places] = getStations() 2 | 3 | labels = { 4 | 'Sation1'; 5 | 'Sation2'; 6 | 'Sation3'; 7 | 'Sation4'; 8 | 'Sation5'; 9 | 'Sation6'; 10 | 'Sation7'; 11 | 'Sation8'; 12 | 'Sation9'; 13 | 'Sation10'; 14 | }; 15 | 16 | coordinates = [ 17 | [24 46]; 18 | [55 37]; 19 | [90 44]; 20 | [50 67]; 21 | [60 75]; 22 | [30 30]; 23 | [90 30]; 24 | [80 65]; 25 | [59 18]; 26 | [18 66]; 27 | ]; 28 | 29 | charging_places = [ 30 | 3 31 | 2 32 | 4 33 | 3 34 | 3 35 | 2 36 | 3 37 | 4 38 | 4 39 | 2 40 | ]; 41 | end 42 | -------------------------------------------------------------------------------- /main.m: -------------------------------------------------------------------------------- 1 | clear all; close all; clc; 2 | 3 | autoRun_text = sprintf('Do you want to run the app in the auto mode? 1/0: '); 4 | autoRun = input(autoRun_text); 5 | 6 | manyPlots_text = sprintf('Do you want to generate a separate plot for each drone? 1/0: '); 7 | manyPlots = input(manyPlots_text); 8 | 9 | [coordinates, labels, chargingPlaces] = getStations(); 10 | if autoRun 11 | fprintf('\nRunning auto mode...\n\n'); 12 | 13 | minNodesBetween = 2; 14 | minNodesBetween = minNodesBetween + 1; 15 | dronNumbers = 6; 16 | 17 | % genetic alghorithm config 18 | populationSize = 50; 19 | iterations = 10000; 20 | else 21 | fprintf('\nTo exit console , ress Ctrl+C\n\n'); 22 | minNodes_text = sprintf('Specify minimal number of nodes between (recomended 2): '); 23 | minNodesBetween = input(minNodes_text); 24 | minNodesBetween = minNodesBetween + 1; 25 | dronNumbers_text = sprintf('Specify number of drones (max 10): '); 26 | dronNumbers = input(dronNumbers_text); 27 | popSize_text = sprintf('Specify population size (genetic algorithm config, recomended 50): '); 28 | populationSize = input(popSize_text); 29 | it_text = sprintf('Specify number of iteration (genetic algorithm config, recomended 10000): '); 30 | iterations = input(it_text); 31 | 32 | customWhether_text = sprintf('Do you want to specify whether conditions? 1/0: '); 33 | customWhether = input(customWhether_text); 34 | 35 | end 36 | 37 | places = createBusyList(chargingPlaces); 38 | finishPaths = cell(dronNumbers, 1); 39 | finishEnergy = zeros(dronNumbers, 1); 40 | 41 | for i = 1:dronNumbers 42 | fprintf('\nFinding path for drone number %d...\n', i); 43 | if autoRun 44 | [p1, p2] = getStartAndStopPoints(coordinates); 45 | else 46 | p1_text = sprintf('Specify start point for drone %d (from 1 to %d): ', i, length(coordinates)); 47 | p1 = input(p1_text); 48 | p2_text = sprintf('Specify end point for drone %d (from 1 to %d): ', i, length(coordinates)); 49 | p2 = input(p2_text); 50 | end 51 | 52 | % generate random wheather conditions 53 | if autoRun || ~customWhether 54 | weatherConditions = abs(normrnd(0.5,0.3)); 55 | else 56 | weatherConditions_text = sprintf('Specify weater conditions for drone %d (from 0.1 to 0.9): ', i); 57 | weatherConditions = input(weatherConditions_text); 58 | end 59 | 60 | [paths, energies] = getAllPathsBetween(p1, p2, populationSize, iterations, coordinates, minNodesBetween, weatherConditions); 61 | [bestPath, bestDistance] = getBestPossiblePath(paths, energies, places); 62 | places = takePlacesInStations(places, bestPath); 63 | 64 | finishPaths{i} = bestPath; 65 | finishEnergy(i) = bestDistance; 66 | 67 | if manyPlots 68 | drawDroneRoad(bestPath, coordinates, labels, i); 69 | end 70 | end 71 | fprintf('\n\nFinding finished, check results:'); 72 | celldisp(finishPaths) 73 | 74 | energiesSum = sum(finishEnergy); 75 | fprintf('Sum of energy = %8.f \n\n', energiesSum); 76 | 77 | if ~manyPlots 78 | drawSynchronousAllDronesRoad(finishPaths, coordinates, labels) 79 | end -------------------------------------------------------------------------------- /mapa.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iga-ops/Optimal_path_planning-Genetic_Algorithm/ea72e0417fe1a5e6559a7fdc14271ab968540e67/mapa.jpg -------------------------------------------------------------------------------- /takePlacesInStations.m: -------------------------------------------------------------------------------- 1 | function places = takePlacesInStations(places, path) 2 | for i=1:length(path) 3 | el = path(i); 4 | places(el, i) = places(el, i) - 1; 5 | end 6 | end --------------------------------------------------------------------------------