Je suis parti d'un exemple trouvé sur Wikipédia et l'ai légèrement modifié pour tourner avec une map de 80x50
Ca semble vraiment super rapide sur mon PC, cf. entre 50 et 150 ms avec un processeur Intel Xeon à 3.3 Ghz
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415 // Astar.cpp // initial source from http://en.wikipedia.org/wiki/A* // initial Compiler: Dev-C++ 4.9.9.2 // initial FB - 201012256 // modifications from Cyclone 21 october 2021 (g++ 7.5.0) // change the default width and height from 60x60 to 80x25 (default screen page size) // add the display of the map and displacments steps // add the loading and saving of the map (default = default.map) // TODO : use width and height instead m and n // TODO : handle user specified width and height into .map files // TODO : handle the loading/conversion of a picture file instead a .map file ? // TODO : make a library from this than can be easily use it into another projet // TODO : adapt it for the Arduino plateform #include <iostream> #include <iomanip> #include <queue> #include <string> #include <math.h> #include <ctime> #include <string.h> using namespace std; // const int n=60; // horizontal size of the map // const int m=60; // vertical size size of the map const int m = 80; const int n = 50; // static int map[n][m]; static char map[n][m]; static int closed_nodes_map[n][m]; // map of closed (tried-out) nodes static int open_nodes_map[n][m]; // map of open (not-yet-tried) nodes static int dir_map[n][m]; // map of directions const int dir=8; // number of possible directions to go at any position // if dir==4 //static int dx[dir]={1, 0, -1, 0}; //static int dy[dir]={0, 1, 0, -1}; // if dir==8 static int dx[dir]={1, 1, 0, -1, -1, -1, 0, 1}; static int dy[dir]={0, 1, 1, 1, 0, -1, -1, -1}; int Steps[n][m]; int nSteps = 0; class node { // current position int xPos; int yPos; // total distance already travelled to reach the node int level; // priority=level+remaining distance estimate int priority; // smaller: higher priority public: node(int xp, int yp, int d, int p) {xPos=xp; yPos=yp; level=d; priority=p;} int getxPos() const {return xPos;} int getyPos() const {return yPos;} int getLevel() const {return level;} int getPriority() const {return priority;} void updatePriority(const int & xDest, const int & yDest) { priority=level+estimate(xDest, yDest)*10; //A* } // give better priority to going strait instead of diagonally void nextLevel(const int & i) // i: direction { level+=(dir==8?(i%2==0?10:14):10); } // Estimation function for the remaining distance to the goal. const int & estimate(const int & xDest, const int & yDest) const { static int xd, yd, d; xd=xDest-xPos; yd=yDest-yPos; // Euclidian Distance d=static_cast<int>(sqrt(xd*xd+yd*yd)); // Manhattan distance //d=abs(xd)+abs(yd); // Chebyshev distance //d=max(abs(xd), abs(yd)); return(d); } }; // Determine priority (in the priority queue) bool operator<(const node & a, const node & b) { return a.getPriority() > b.getPriority(); } // A-star algorithm. // The route returned is a string of direction digits. string pathFind( const int & xStart, const int & yStart, const int & xFinish, const int & yFinish ) { static priority_queue<node> pq[2]; // list of open (not-yet-tried) nodes static int pqi; // pq index static node* n0; static node* m0; static int i, j, x, y, xdx, ydy; static char c; char str[10]; string path2, path3; pqi=0; // reset the node maps for(y=0;y<m;y++) { for(x=0;x<n;x++) { closed_nodes_map[x][y]=0; open_nodes_map[x][y]=0; } } // create the start node and push into list of open nodes n0=new node(xStart, yStart, 0, 0); n0->updatePriority(xFinish, yFinish); pq[pqi].push(*n0); open_nodes_map[x][y]=n0->getPriority(); // mark it on the open nodes map // A* search while(!pq[pqi].empty()) { // get the current node w/ the highest priority // from the list of open nodes n0=new node( pq[pqi].top().getxPos(), pq[pqi].top().getyPos(), pq[pqi].top().getLevel(), pq[pqi].top().getPriority()); x=n0->getxPos(); y=n0->getyPos(); pq[pqi].pop(); // remove the node from the open list open_nodes_map[x][y]=0; // mark it on the closed nodes map closed_nodes_map[x][y]=1; // quit searching when the goal state is reached //if((*n0).estimate(xFinish, yFinish) == 0) if(x==xFinish && y==yFinish) { // generate the path from finish to start // by following the directions string path=""; while(!(x==xStart && y==yStart)) { j=dir_map[x][y]; c='0'+(j+dir/2)%dir; path=c+path; // sprintf(str, "(%2d,%2d) ", x, y ); // path2 = string(str) + path2; // sprintf(str, "(%+d,%+d) ", -dx[j], -dy[j] ); // path3 = string(str) + path3; x+=dx[j]; y+=dy[j]; } // garbage collection delete n0; // empty the leftover nodes while(!pq[pqi].empty()) pq[pqi].pop(); // sprintf(str, "(%2d,%2d) ", xStart, yStart ); // path2 = string(str) + path2; // cout << "Route : " << path2 << endl; // cout << "Moves : " << path3 << endl; return path; } // generate moves (child nodes) in all possible directions for(i=0;i<dir;i++) { xdx=x+dx[i]; ydy=y+dy[i]; if(!(xdx<0 || xdx>n-1 || ydy<0 || ydy>m-1 || map[xdx][ydy]==1 || closed_nodes_map[xdx][ydy]==1)) { // generate a child node m0=new node( xdx, ydy, n0->getLevel(), n0->getPriority()); m0->nextLevel(i); m0->updatePriority(xFinish, yFinish); // if it is not in the open list then add into that if(open_nodes_map[xdx][ydy]==0) { open_nodes_map[xdx][ydy]=m0->getPriority(); pq[pqi].push(*m0); // mark its parent node direction dir_map[xdx][ydy]=(i+dir/2)%dir; } else if(open_nodes_map[xdx][ydy]>m0->getPriority()) { // update the priority info open_nodes_map[xdx][ydy]=m0->getPriority(); // update the parent direction info dir_map[xdx][ydy]=(i+dir/2)%dir; // replace the node // by emptying one pq to the other one // except the node to be replaced will be ignored // and the new node will be pushed in instead while(!(pq[pqi].top().getxPos()==xdx && pq[pqi].top().getyPos()==ydy)) { pq[1-pqi].push(pq[pqi].top()); pq[pqi].pop(); } pq[pqi].pop(); // remove the wanted node // empty the larger size pq to the smaller one if(pq[pqi].size()>pq[1-pqi].size()) pqi=1-pqi; while(!pq[pqi].empty()) { pq[1-pqi].push(pq[pqi].top()); pq[pqi].pop(); } pqi=1-pqi; pq[pqi].push(*m0); // add the better node instead } else delete m0; // garbage collection } } delete n0; // garbage collection } return ""; // no route found } void MakeMap() { // create empty map for(int y=0;y<m;y++) { for(int x=0;x<n;x++) map[x][y]=0; } // fillout the map matrix with a '+' pattern for(int x=n/8;x<n*7/8;x++) { map[x][m/2]=1; } for(int y=m/8;y<m*7/8;y++) { map[n/2][y]=1; } } void SaveMap( char *filename ) { FILE *f; f = fopen(filename, "w"); for( int y = 0 ; y < n ; y++ ) { for( int x = 0 ; x < m ; x++) { fprintf(f, (map[x][y] ? "." : " ") ); } fprintf(f, "\n"); } fclose(f); } void LoadMap( char *filename ) { FILE *f; char empty; char buffer[160]; int len; int x = 0; int y = 0; f = fopen(filename, "r"); while ( !feof( f ) ) { fgets( buffer, 160, f); len = strlen(buffer) - 1; for( x = 0 ; x < len ; x++ ) { map[x][y] = ( buffer[x] == ' ' ? 0 : 1); } y++; } fclose(f); } int main( int argc, char **argv ) { int steps = 0; srand(time(NULL)); if ( argc > 1 ) { LoadMap( argv[1] ); } else { MakeMap(); SaveMap( "default.map" ); } // randomly select start and finish locations int xA, yA, xB, yB; switch(rand()%8) { case 0: xA=0;yA=0;xB=n-1;yB=m-1; break; case 1: xA=0;yA=m-1;xB=n-1;yB=0; break; case 2: xA=n/2-1;yA=m/2-1;xB=n/2+1;yB=m/2+1; break; case 3: xA=n/2-1;yA=m/2+1;xB=n/2+1;yB=m/2-1; break; case 4: xA=n/2-1;yA=0;xB=n/2+1;yB=m-1; break; case 5: xA=n/2+1;yA=m-1;xB=n/2-1;yB=0; break; case 6: xA=0;yA=m/2-1;xB=n-1;yB=m/2+1; break; case 7: xA=n-1;yA=m/2+1;xB=0;yB=m/2-1; break; } cout<<"Map Size (X,Y): "<<n<<","<<m<<endl; cout<<"Start: (" << xA << "," << yA << ")" << endl; cout<<"Finish:(" << xB << ", "<< yB << ")" << endl; // get the route clock_t start = clock(); string route=pathFind(xA, yA, xB, yB); if(route=="") cout<<"An empty route generated!"<<endl; clock_t end = clock(); double time_elapsed = double(end - start); cout<<"Time to calculate the route : " << time_elapsed * 1000 / CLOCKS_PER_SEC << " ms" << endl << endl; // follow the route on the map and display it if(route.length()>0) { int j; char c; int x=xA; int y=yA; map[x][y]=2; for(int i=0;i<route.length();i++) { c =route.at(i); j=atoi(&c); x=x+dx[j]; y=y+dy[j]; map[x][y]=3; Steps[x][y] = ++nSteps; } map[x][y]=4; // cout << "Number of steps " << nSteps << endl << endl; // display the map with the route for(int y=0;y<m;y++) { for(int x=0;x<n;x++) { if(map[x][y]==0) // cout<<"."; cout << " "; else if(map[x][y]==1) // cout<<"O"; //obstacle cout << "."; else if(map[x][y]==2) cout<<"S"; //start else if(map[x][y]==3) // cout<<"R"; //route cout << Steps[x][y] % 10; else if(map[x][y]==4) // cout<<"F"; //finish cout<<"E"; } cout<<endl; } } cout << endl << "Route(" << nSteps << ") : "; for(int i = 0;i<route.length();i++) { int j; char c; char str[10]; c = route.at(i); j = atoi(&c); // cout << "(" << dx[j] << "," << dy[j] << ") "; sprintf(str, "(%+d,%+d) ", dx[j], dy[j] ); cout << str; } cout << endl; // getchar(); // wait for a (Enter) keypress return(0); }![]()
![]()
(le point de départ et d'arrivée sont choisis aléatoirement et la map par défaut en un simple grande croix au milieu de la salle)
[sans paramêtre, ça crée le fichier texte default.map que l'on peut recopier ou modifier pour l'utiliser en le mettant en argument du programme]
Ca se compile très simplement
Et ça donne quelque chose comme ça :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 g++ Astar.cpp -o Astar
(on peux aussi recopier et légèrement transformer le fichier .map pour l'utiliser en rajoutant son nom en argument)
[par défaut, il construit et utilise le fichier default.map qui est une grande croix au milieu de la salle]
[il semble y avoir unee inversion entre les axes utilisés dans le fichier et ceux utilisés dans le programme mais ce n'est pas trop grave pour les premiers tests + programme converti très vite "à la volée"]
Je pense que ça devrait tourner dans les 3.3 Ghz / 10 Mhz = 330x plus lentement sur l'ESP32 (10 Mhz pour l'ESP32 vs 3.3 Ghz pour le PC]
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89 yannoo@cyclone ~/Dev/Chemins $ ./Astar Map Size (X,Y): 50,80 Start: (0,0) Finish:(49, 79) Time to calculate the route : 120.371 ms S 1 2 3 4 5 6 7 8 9 0 . 1 . 2 . 3 . 4 . 5 . 6 . 7 . 8 . 9 . 0 . 1 . 2 . 3 . 4 . 5 . 6 . 7 . 8 . 9 . 0 . 1 . 2 . 3 . 4 . 5 . 6 . 7 . 8 . 9 . 0..................................... 1 . 2 . 3 . 4 . 5 . 6 . 7 . 8 . 9 . 0 . 1 . 2 . 3 . 4 . 5 . 6 . 7 . 8 . 9 . 0 . 1 . 2. 3. 4. 5. 6. 7. 8. 9. 0 1 2 3456 78901 23456 789 012 3 E Route(94) : (+0,+1) (+0,+1) (+0,+1) (+0,+1) (+0,+1) (+0,+1) (+0,+1) (+0,+1) (+0,+1) (+0,+1) (+0,+1) (+0,+1) (+0,+1) (+0,+1) (+0,+1) (+0,+1) (+0,+1) (+0,+1) (+0,+1) (+0,+1) (+0,+1) (+0,+1) (+0,+1) (+0,+1) (+0,+1) (+0,+1) (+0,+1) (+0,+1) (+1,+1) (+0,+1) (+0,+1) (+1,+1) (+0,+1) (+1,+1) (+0,+1) (+0,+1) (+1,+1) (+0,+1) (+1,+1) (+0,+1) (+1,+1) (+1,+1) (+1,+1) (+0,+1) (+1,+1) (+1,+1) (+1,+1) (+1,+1) (+0,+1) (+1,+1) (+1,+1) (+1,+1) (+1,+1) (+1,+1) (+1,+1) (+0,+1) (+1,+1) (+1,+1) (+1,+1) (+1,+1) (+1,+1) (+1,+1) (+0,+1) (+0,+1) (+0,+1) (+0,+1) (+0,+1) (+0,+1) (+0,+1) (+1,+1) (+1,+1) (+1,+1) (+1,+1) (+1,+0) (+1,+0) (+1,+0) (+1,+1) (+1,+0) (+1,+0) (+1,+0) (+1,+0) (+1,+1) (+1,+0) (+1,+0) (+1,+0) (+1,+0) (+1,+1) (+1,+0) (+1,+0) (+1,+1) (+1,+0) (+1,+0) (+1,+1) (+1,+1)
=> disons qu'avec une moyenne de 100 ms sur le PC, ça devrait donner dans les 330x plus lent sur l'ESP32, soit 330 * 100 ms = 33 000 ms = 33 secondes ...
==> le problème est cash ici mis en évidence car il est hors de question que l'ESP ne puisse effectuer qu'un déplacement toutes les 33 secondes, ce qui est très très loin du temps réel que je voudrais obtenir où la seconde est déjà une limite supérieure bien haute ...![]()
Néanmoins, après réflexion, le problème est partiellement résolu car au bout de 33 secondes, c'est l'ensemble du chemin qui serait calculé, cf. les 94 mouvement indiqués après le "Route(94) :" , pas seulement un seul
=> 33 000 ms / 94 pas = environ 351 ms par pas donc ça devrait le faire avec l'algo Astar si j'arrive à le modifier afin qu'il donne les pas un par un, docn en gros toutes les 350 ms, et non pas tout le chemin en entier seulement à la fin au boût de 33 secondes
(mais j'ai un gros doute sur le sujet car l'algo semble bien plus fonctionner de façon globale pour donner seulement le chemin à la fin que de façon incrémentale pour me donner les déplacements à faire un par un ...![]()
)
De l'autre côté, si je gère ça en dynamique les points sources et destination ne seront jamais bien loin l'un de l'autre, les robots jouets partant à proximité du robot aspirateur et le suivront avec une distance relativement faible, donc ça devrait le faire
=> je ferais quelques tests les prochains jours en essayant d'ajouter à ce programme la gestion de plusieurs robots différents (les fantomes dans Pac Man) qui suivront systématiquement à relativement faible distance les déplacement du robot aspirateur (Pac Man)
==> avec un peu/beaucoup de bol, ça pourrait le faire assez vite en individuel sur chacun des ESP32 et je pourrais alors cloturer ce post![]()
A noter qu'au pire, le PC semble déjà capable d'envoyer les ordres de déplacement à chacun des 4 robots jouets en 4 x 100 ms en moyenne = 400 ms en moyenne = largement 2 fois par seconde à chacun des 4 robots donc le post sera assez certainement cloturé vite fait![]()
![]()
(m'enfin bon, vu que je suis vraiment hyper têtu, normal car breton, j'aimerais bien que chacun des robots puisse suivre son chemin de façon autonome même si le PC est éteint => mais ça se sera pour ma tronche et après avoir cloturé ce fil sur ce forum, j'irais sur un forum dédié aux Arduinos pour voir comment on peut optimiser ça pour cette plateforme )





Répondre avec citation












(<- lien wikipedia en français)

Partager