Final project report(TSP智慧型自走車) - YYBS/ee240500 GitHub Wiki
TSP智慧型自走車
TSP的目的find a minimum TSP (Travelling Salesman Problem) tour,就是圖上有幾個城鎮,而商人要搭著車車跑過所有城鎮的最短路徑。 實現方法,舉例:用紅色圓形當作起始城鎮和終點城鎮,而藍色圓形當過境城市,兩個城鎮間用綠色線條代表路徑。 #如何實現 這邊分成四個部份,第一個部份是影像處理,第二個部份是演算法,第三個部份是Xbee的傳輸,第四個部份則是車子接受訊號並產生反應,我的部分主要負責演算法和Xbee和車子的控制。
- 首先,取得圖片的方式是用webcam空拍要走的圖,在把圖片丟去影像處理,影像處理使用RGB過濾,因為總共有三種顏色,我們可以先濾紅色在濾藍色最後濾綠色,一開始濾紅色,會得到一個白色圓形,這時候我們還沒得到座標,所以使用判斷圓形的function去定位圓心座標,這就是起始座標,接來濾藍色,在做如上步驟,剩下來比較麻煩的就是綠色線段的判斷,綠色的線段判斷非常重要,因為這是兩個點之間有沒有連線的依據。 綠色線段先用綠色濾圖片,可以在得到一堆白線,但我們不可能只憑白線的座標就判斷兩點之間有無連線,我們想了一個方法,當我們有了圓心座標時,我們從圓心座標畫一個範圍,以方形為範圍也可以,然後沿著方形範圍去尋找切到白線的座標,然後把她存起來,這樣有個好處,假設一個圓形有四條線連接,那我們用這個找法,起碼能找到四個點,但實際上會找到更多的點,因為綠色線條是有寬度的,不過對我們後面要說的方法是不會產生影響的,找完全部圓心周圍方形的點後,那我們可以開始判斷兩個圓形之間是否有連線,這時候就很輕鬆的可以判斷了,假設現在有兩個圓形,每個圓形的方形周圍又有好多點,那我們各找每個圓形方形周圍的點,然後把這兩點之間做search,search的方式,是從一個點的座標,用兩點之間的斜率慢慢search,假設要40次移動才能到另外一個點,那我們判斷search的40個點裡有幾個點有包含到綠色線段,當達到一個界限後,我們就判斷這兩點之間有連線,找完所有兩個點之間的組合,那我們就能找出所有點之間的連結。接下來是要把座標存到Txt檔中,txt檔中存的資訊有所有點的座標,和所有點的連結。
- 接下來是演算法,演算法我使用的是branch and bound,延著每條路去走下一點,一直走到完,如果走得完,則代表找到一條能夠走完所有點的路,這時候就去判斷是否為最短路徑,如果比先前走完的路徑還短的話,則把這條路徑存起來,就這樣不斷把所有可能的組合走完,講到這裡,找路的方法跟窮舉法是一樣的,不過我多加了一個限制,如果當下在找的路徑長度已經比最短路徑還長的話,則代表這條路不需要繼續走完,而轉而backtrack其他路徑,這會讓找到最佳路徑的時間更短,演算法是在板子上跑。
- 接下來是Xbee傳輸的過程,我參考之前lab用到的code,但是那個code的Xbee是用char這個型態傳資料,所以我之後的處理都是用char來做傳輸,這造成之後我們的成果在傳輸的速度上慢了許多,這算是一大敗筆,傳輸的方式是板子傳送一個char給車子,而車子在回傳一個char給板子,這樣板子就會在傳輸下一個char,用這種方式建立穩定的溝通,而車子一次的動作,需要傳四個char,第一個char代表動作,後面三個char是數字,這代表需要做大的動作,格式為'r','l','f',分別為右轉左轉前進,車子會先轉到需要轉的角度後,在接收需要前進的距離。
- 而車子的掌控,因為複雜的運算都在板子上做完了,車子只要使用servo_speed做出相應的動作就行,這邊轉的角度是用車子的角速度和車子輪圍去計算的,所以準確度算滿高的,而車子前進的距離,則是需要調整比例尺,因為空拍的圖片,座標不代表可以找到正確的距離,這邊就需要當下的微調。
https://www.youtube.com/watch?v=YKK04VTwRe0&feature=youtu.be
版子上跑的code
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <fcntl.h>
#include <termios.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdbool.h>
#include <limits.h>
#include <sys/select.h>
#define BUFLEN (255)
#define ZDEBUG (1)
#define PI 3.141591
int now_angle = 0;
int road[20] = {1}; //debug
int road_s[20] = {1};
int v[50][50] = {0}; //road weight
int min_length = 9999999;
int main (int argc, char *argv[]) {
int serial_d;
speed_t speed;
struct termios soptions, soptions_org;
char command;
unsigned char send_buf[BUFLEN];
unsigned char recv_buf[BUFLEN];
int sent_c, recv_c;
//no port specified
if (argc == 1) {
printf("Parameter: /dev/ttyUSB0 [command]\n");
printf("\"/dev/ttyUSB0\" is a USB serial device to support serial control.\n");
return 1;
}
`serial_d = openserial(argv[1]);`
`if (serial_d == -1) return 1;`
`// ----- Begin of setup serial ports`
`tcgetattr(serial_d, &soptions_org);`
`tcgetattr(serial_d, &soptions);`
`speed = B9600; // Speed options: B19200, B38400, B57600, B115200`
`cfsetispeed(&soptions, speed);`
`cfsetospeed(&soptions, speed);`
`// Enable the reciver and set local mode...`
`soptions.c_cflag |= ( CLOCAL | CREAD );`
`// Setting Parity Checking (8N1)`
`soptions.c_cflag &= ~PARENB;`
`soptions.c_cflag &= ~CSTOPB;`
`soptions.c_cflag &= ~CSIZE;`
`soptions.c_cflag |= CS8;`
`// Local setting`
`//soptions.c_lflag = (ICANON | ECHO | ECHOE); //canonical`
`soptions.c_lflag = ~(ICANON | ECHO | ECHOE | ISIG); //noncanonical`
`// Input setting`
`//soptions.c_iflag |= (IXON | IXOFF | IXANY); //software flow control`
`soptions.c_iflag |= (INPCK | ISTRIP);`
`soptions.c_iflag = IGNPAR;`
`// Output setting`
`soptions.c_oflag = 0;`
`soptions.c_oflag &= ~OPOST;`
`// Read options`
`soptions.c_cc[VTIME] = 0;`
`soptions.c_cc[VMIN] = 1; //transfer at least 1 character or block`
`// Apply setting`
`tcsetattr(serial_d, TCSANOW, &soptions);`
`// ----- End of setup serial ports`
////////////////////////////////////////////////////////////////////////////////////////
int angle_out;
int *have_gone;
int *x_addr;
int *y_addr;
int Q;
int ith,jth,go_length;
char tag[4]={'0'};
int n;
int c1,c2,c3;
int j,i;
FILE *in_file_1 = fopen("citydist.txt", "r"); // read only
if(!in_file_1){
printf("error input!\n");
return -1;
}
FILE *in_file_2 = fopen("citynum.txt", "r"); // read only
if(!in_file_2){
printf("error input!\n");
return -1;
}
//read file_1
fscanf(in_file_1, "%d", &c1);
n = c1;
have_gone = (int*)malloc((n+1)*sizeof(int));
while (1){
fscanf(in_file_1, "%d%d%d", &c1, &c2, &c3);
if(feof(in_file_1))break;
v[c1][c2] = c3;
v[c2][c1] = c3;
}
fclose(in_file_1);
trace(0,have_gone,0,1,n);
if(road[n]!=1){
printf("fail\n");
return 0;
}
fscanf(in_file_2, "%d", &c1);
n = c1;
x_addr = (int*)malloc((n+1)*sizeof(int));
y_addr = (int*)malloc((n+1)*sizeof(int));
while (1){
fscanf(in_file_2, "%d%d%d", &c1, &c2, &c3);
if(feof(in_file_2))break;
x_addr[c1] = c2;
y_addr[c1] = c3;
}
fclose(in_file_2);
for(i = 1;i <=n;i++){ //debug
printf("%d (%d,%d)\n",i,x_addr[i],y_addr[i]);
}
`for(i = 0;i <(n+1);i++){ //debug`
`printf("%d-->",road[i]);// 0~n`
`}`
`printf("the minium path is %d \n",min_length);`
`for(i=0;i<n;i++){`
`ith = road[i];`
`jth = road[i+1];`
`angle_out = angle_turn(x_addr[ith],y_addr[ith], x_addr[jth],y_addr[jth]);`
`go_length = v[ith][jth];`
`printf("(%d,%d)-->(%d,%d) ,",x_addr[ith],y_addr[ith], x_addr[jth],y_addr[jth]);`
`printf("turn angle: %d ,",angle_out);`
`printf("go length : %d \n",go_length);`
`//angle out angle ==> buffer`
`if(angle_out>=0){`
`// turn right`
`command = 'r';`
`sent_c = write(serial_d, &command, 1); // Send command`
`tcdrain(serial_d);`
`usleep(2000000); // Wait for response`
`memset(recv_buf, '\0', BUFLEN);`
`recv_c = read(serial_d, recv_buf, BUFLEN); // Get response message`
`tcdrain(serial_d);`
`int_to_char(tag,angle_out);`
`printf("right %s degree\n",tag);`
`for(j=0;j<3;j++){`
`command = tag[j];`
`sent_c = write(serial_d, &command, 1); // Send command`
`tcdrain(serial_d);`
`usleep(2000000); // Wait for response`
`memset(recv_buf, '\0', BUFLEN);`
`recv_c = read(serial_d, recv_buf, BUFLEN); // Get response message`
`tcdrain(serial_d);`
`printf("%s\n\n",recv_buf);`
`}`
`}`
`else{`
`// turn left`
`command = 'l';`
`sent_c = write(serial_d, &command, 1); // Send command`
`tcdrain(serial_d);`
`usleep(2000000); // Wait for response`
`memset(recv_buf, '\0', BUFLEN);`
`recv_c = read(serial_d, recv_buf, BUFLEN); // Get response message`
`tcdrain(serial_d);`
`int_to_char(tag,(-1)*angle_out);`
`printf("left %s degree\n",tag);`
`for(j=0;j<3;j++){`
`command = tag[j];`
`sent_c = write(serial_d, &command, 1); // Send command`
`tcdrain(serial_d);`
`usleep(2000000); // Wait for response`
`memset(recv_buf, '\0', BUFLEN);`
`recv_c = read(serial_d, recv_buf, BUFLEN); // Get response message`
`tcdrain(serial_d);`
`printf("%s\n\n",recv_buf);`
`}`
`}`
`//`
`//length out `
`int_to_char(tag,go_length);`
`printf("forward %s\n",tag);`
`command = 'f';`
`sent_c = write(serial_d, &command, 1); // Send command`
`tcdrain(serial_d);`
`usleep(2000000); // Wait for response`
`memset(recv_buf, '\0', BUFLEN);`
`recv_c = read(serial_d, recv_buf, BUFLEN); // Get response message`
`tcdrain(serial_d);`
`for(j=0;j<3;j++){`
`command = tag[j];`
`sent_c = write(serial_d, &command, 1); // Send command`
`tcdrain(serial_d);`
`usleep(2000000); // Wait for response`
`memset(recv_buf, '\0', BUFLEN);`
`recv_c = read(serial_d, recv_buf, BUFLEN); // Get response message`
`tcdrain(serial_d);`
`printf("%s\n\n",recv_buf);`
`}`
`}`
// restore setting and close
tcsetattr(serial_d, TCSANOW, &soptions_org);
close(serial_d);
return 0;
}
int openserial (char *sdevfile) {
int _serial_d = open(sdevfile, O_RDWR | O_NOCTTY);
if (_serial_d == -1) perror("Unable to open device\n");
return _serial_d;
}
int angle_turn(int x1,int y1,int x2,int y2){
int Q;
int tmp;
Q =180*(atan2(x2-x1,y2-y1)/PI);
tmp = Q-now_angle;
now_angle = Q;
if(tmp >=180) tmp = tmp -360;
else if(tmp<= -180) tmp = tmp +360;
return tmp;
}
int int_to_char(char *in,int a){
int tmp;
tmp = a/100;
a = a%100;
in[0] = tmp + '0';
tmp = a/10;
a = a%10;
in[1] = tmp + '0';
in[2] = a + '0';
return 0;
}
int trace(int gone,int* have_gone,int length,int now_way,int n){
int i=0,j=0;
if(gone == 0){ //reset all way
gone = 1;
length = 0;
for(i = 0;i<=n;i++){
have_gone[i]=0; //0 mean haven't enter
}
}
if(gone == n){
//to check min path
if(v[now_way][1] != 0){
road[gone] = 1;
length = length + v[now_way][1];
if(min_length > length){
min_length = length;
for(i = 0;i <20;i++){ //debug
road[i]=road_s[i];
printf("%d-->",road[i]);
}
}
}
return 0;
}
for(i=2;i<=n;i++){ //1 is origin place
if(v[now_way][i]!=0 && have_gone[i]==0){
have_gone[i] = 1; // let's go i'th place
road_s[gone] = i;
if((length + v[now_way][i]) < min_length){
trace(gone+1,have_gone,length + v[now_way][i],i,n);
}
have_gone[i] = 0; // let's back way
}
}
return 0;
}
2.車子上跑的code
clude "simpletools.h"
#include "servo.h"
#include "servodiffdrive.h"
#include "fdserial.h"
#include "talk.h"
#define PIN_SOUND 26 // pin for the speaker, default=26
#define PIN_XBEE_RX 0
#define PIN_XBEE_TX 1
#define PIN_SERVO_LEFT 16
#define PIN_SERVO_RIGHT 17
#define Map_ratio 1 //100 :10cm
int main (void) {
int i =0;
int length;
char number[4];
int ptime;
fdserial *xbee;
xbee = fdserial_open(PIN_XBEE_RX, PIN_XBEE_TX, 0, 9600);
drive_pins(PIN_SERVO_LEFT, PIN_SERVO_RIGHT);
`pause(1000);`
`putchar(CLS);`
`fdserial_rxFlush(xbee);`
`while (1) {`
`char ch = fdserial_rxChar(xbee);`
`fdserial_rxFlush(xbee);`
`switch (ch) {`
`case 'f':`
`print("Forward\n");`
`pause(100);`
`fdserial_txChar(xbee, ch);`
`fdserial_txFlush(xbee);`
`i =0;`
`while(1){`
`char ch = fdserial_rxChar(xbee);`
`fdserial_rxFlush(xbee);`
`if('0'<=ch && ch<='9'){`
`number[i]=ch;`
`i++;`
`}`
`if(i==3){`
`////////////////function`
`drive_speeds(100,100);`
`printf("number = %d\n",char_to_int(number));`
`ptime = (1000*Map_ratio*char_to_int(number)*0.667)/104;`
`pause(ptime);`
`drive_speeds(0,0);`
`///////////////////////////////////`
`}`
`// print("%c\n", (char) ch);`
`pause(100);`
`fdserial_txChar(xbee, ch);`
`fdserial_txFlush(xbee);`
`if(i==3)break;`
`}`
`break;`
`case 'l':`
`print("turn left\n");`
`pause(100);`
`fdserial_txChar(xbee, ch);`
`fdserial_txFlush(xbee);`
`i =0;`
`while(1){`
`char ch = fdserial_rxChar(xbee);`
`fdserial_rxFlush(xbee);`
`if('0'<=ch && ch<='9'){`
`number[i]=ch;`
`i++;`
`}`
`if(i==3){`
`////////////////function`
`drive_speeds(-50,50);`
`ptime = 1000*665*char_to_int(number)/360/104;`
`if(ptime != 0)pause(ptime/2);`
`drive_speeds(0,0);`
`printf("number = %d\n",char_to_int(number));`
`///////////////////////////////////`
`}`
`// print("%c\n", (char) ch);`
`pause(100);`
`fdserial_txChar(xbee, ch);`
`fdserial_txFlush(xbee);`
`if(i==3)break;`
`}`
`break;`
`case 'r':`
`print("turn right\n");`
`pause(100);`
`fdserial_txChar(xbee, ch);`
`fdserial_txFlush(xbee);`
`i =0;`
`while(1){`
`char ch = fdserial_rxChar(xbee);`
`fdserial_rxFlush(xbee);`
`if('0'<=ch && ch<='9'){`
`number[i]=ch;`
`i++;`
`}`
`if(i==3){`
`////////////////function`
`drive_speeds(50,-50);`
`ptime = 1000*665*char_to_int(number)/360/104;`
`print("ptime = %d",ptime);`
`if(ptime != 0)pause(ptime/2);`
`drive_speeds(0,0);`
`printf("number = %d\n",char_to_int(number));`
`///////////////////////////////////`
`}`
`// print("%c\n", (char) ch);`
`pause(100);`
`fdserial_txChar(xbee, ch);`
`fdserial_txFlush(xbee);`
`if(i==3)break;`
`}`
`break;`
`default:`
`print("Invalid Command\n");`
`pause(700);`
`print("%c\n", (char) ch);`
`pause(100);`
`fdserial_txChar(xbee, ch);`
`fdserial_txFlush(xbee);`
`break;`
`}`
`}`
}
int char_to_int(char * a){
int tmp,tmp1,tmp2,tmp3;
tmp1 = a[0] - '0';
tmp2 = a[1] - '0';
tmp3 = a[2] - '0';
tmp = tmp1*100 + tmp2*10 +tmp3;
return tmp;
}
`