###### tags: `東京威力 TEL` {%hackmd DzaUykeiRfWOMkjFL_QkCQ %} # II. Webcam ## <font color="orange"> 02. 座標轉換 Coordinate Transformation</font> :star: 這章節的出生一定要感謝我的好朋友 **江鎮宇**,帶領我走入校正的世界 :star: **總流程是:辨識方塊座標 -> 轉換座標 -> 手臂得到對於它的相對座標 -> 開夾** :::warning 其實正規流程應該是先對相機進行**相機校正**,使魚眼鏡頭拍的廣角(扭曲)畫面校正成水平鉛直的2D平面,而一些原圖的邊緣會因校正而過濾掉。接著在將相機和手臂原點的位置**轉移矩陣**,乘上校正後的像素座標,得到相對於手臂原點的方塊座標。 $\;\;\;\;\;$然而這次比賽時間很有限,且看到學長姐和別組的相機校正都不太堪用,因此改為手刻。 雖然會標得很累,但手刻等於是將**相機校正和轉移矩陣兩個步驟合而為一**。缺點是如果相機辨識位置改變,就必須重標。還有標點真的很煩很累 XDD ::: ### <font color="pink"> 2-1. 拍照片</font> 此次拍攝的方格紙一個方格大小是 $1.5cm * 1.5cm$。 :::success ![](https://i.imgur.com/cOq9So6.png) 幾點注意事項: 1. 校正照片盡量在要辨識時的高度、角度拍攝。 2. 方格越小校正越精確,但如果太小照片清晰度不夠,會標到眼花和心累。 3. 確保清晰的方格有超過夾取範圍一些,太邊緣模糊的就不用標點了。 ::: ### <font color="pink"> 2-2. 標點</font> <font color="yellow">Step 1. 確認手臂和像素座標的XY關係。</font> ![](https://i.imgur.com/MN7flE3.png) <font color="yellow">Step 2. 可用小畫家或OpenCV,根據游標位置找像素點。</font> :::spoiler {state=open} OpenCV 程式碼 ```c+++= #include <iostream> #include <opencv2/opencv.hpp> using namespace cv; void on_Mouse(int event, int x, int y, int flags, void* param){ if (event == 0) std::cout << "x: " << x << " y: " << y << std::endl; } int main(){ Mat src = imread("/home/ditrobotics/TEL/src/opencv/src/first.png"); cvtColor(src, src, COLOR_RGB2GRAY); // src.convertTo(src, -1, 2, 0); //increase the contrast by 2 namedWindow("src"); setMouseCallback("src", on_Mouse); imshow("src", src); waitKey(); } ``` ::: 我覺得用小畫家會更方便些。 <font color="yellow">Step 3. 將數據紀錄到 Excel,格式要一致喔。</font> ![](https://i.imgur.com/9XTeKBL.png =70%x) <font color="yellow">Step 4. 在 Vscode 中新建 coordinate.csv。</font> 下載延伸模組 **Excel Viewer** 和 **Edit csv** 後,複製 Excel 數據(不包含欄位名稱)到 .csv 中。 ![](https://i.imgur.com/xIRRu8X.png =50%x) ![](https://i.imgur.com/N1JOcX7.png =46%x) <font color="yellow">Step 5. 將x_scara由小到大排列。</font> ![](https://i.imgur.com/II7vEWm.png =80%x) <font color="yellow">Step 6. 寫轉換座標程式,完成。</font> :::spoiler 轉換座標程式,只需要填入 csv 的檔案地址、目標點的像素座標。 ```cpp= #include <iostream> #include <cmath> #define numOfRow 880 #define pixel_x_min 105 #define pixel_x_max 423 #define pixel_y_min 16 #define pixel_y_max 448 #define path "/home/pomelo925/POMELO/TEL/src/opencv/src/coordinate_first.csv" typedef struct{ float x_scara; float y_scara; float x_pixel; float y_pixel; bool adjacent; } Coordinate; void tranform_coordinate(const char *file_name, int goal_x, int goal_y){ /**** Import and Transforn Data ****/ FILE *file; file=fopen(file_name, "r"); Coordinate COR[numOfRow]; bool adjacent[numOfRow]={false}; int records=0; int row=0; do{ records = fscanf(file, "%f%f%f%f\n", &COR[row].x_scara, \ &COR[row].y_scara, &COR[row].x_pixel, &COR[row].y_pixel); if(records==4) row++; else return; } while(!feof(file)); /**** transformation ****/ /* Step 1. reachable goal*/ if(goal_x < pixel_x_min || goal_y < pixel_y_min \ || goal_x > pixel_x_max || goal_y < pixel_y_min){ printf("Out of Range !! Goal pixel: %d, %d\n", goal_x, goal_y); return; } /* Step 2. find adjacent point of X-coordinate*/ for(int i=0; i<numOfRow; i++){ if(goal_x<=COR[i].x_pixel && goal_x>=COR[i+1].x_pixel && fabs(COR[i+1].x_pixel-COR[i].x_pixel)<50) COR[i].adjacent=true; } /* Step 3. determine nearest point by the distance of Y-coordinate*/ double distance[numOfRow]={0}; int nst=0; bool flag=true; for(int i=0; i<numOfRow; i++){ if(COR[i].adjacent==true){ distance[i] = COR[i].y_pixel - goal_y; if(distance[i]<0)distance[i]=0; if(flag){ nst=i; flag=false; } if(distance[nst]>distance[i] && distance[i]!=0) nst=i; } } /* Step 4. find adjacent four point*/ /* (x1,y1) (x2,y2) (goal_x,goal_y) (x3,y3) (x4,y4)*/ double x,y,x1,y1,x2,y2,x3,y3,x4,y4; for(int i=0; i<numOfRow; i++){ if(COR[i].x_scara==COR[nst].x_scara+1.5 \ && COR[i].y_scara==COR[nst].y_scara){ x2 = COR[i].x_pixel; y2 = COR[i].y_pixel; x1 = COR[i+1].x_pixel; y1 = COR[i+1].y_pixel; break; } } x4 = COR[nst].x_pixel; y4 = COR[nst].y_pixel; x3 = COR[nst+1].x_pixel; y3 = COR[nst+1].y_pixel; /* Step 5. bilinear interpolation*/ double scx=0, scy=0; scx += ((y1-goal_y)/(y1-y3))*COR[nst].x_scara + ((goal_y-y3)/(y1-y3))*(COR[nst].x_scara+1.5); scx += ((y2-goal_y)/(y2-y4))*COR[nst].x_scara + ((goal_y-y4)/(y2-y4))*(COR[nst].x_scara+1.5); scx /= 2; scy += ((goal_x-x1)/(x2-x1))*COR[nst].y_scara + ((x2-goal_x)/(x2-x1))*(COR[nst].y_scara+1.5); scy += ((goal_x-x3)/(x4-x3))*COR[nst].y_scara + ((x4-goal_x)/(x4-x3))*(COR[nst].y_scara+1.5); scy /= 2; printf("\n*** SCARA ***\nX: %.3f\tY:%.3f\n", scx, scy); fclose(file); file = NULL; } int main(int argc, char **argv){ double goal_x, goal_y; std::cin >> goal_x >> goal_y; tranform_coordinate(path, goal_x, goal_y); return 0; } ::: --- <font color="lightblue">這坨程式碼的簡介:</font> * <font color="lightblue">Excel 標點不需要每行每列都固定的個數。</font> * <font color="lightblue">Excel 標點的單位可是任意長度。</font> * <font color="lightblue">要改變的參數都在 `#define` 裡面:</font> * <font color="lightblue">csv 檔案地址:`path`,即 `tranform_coordinate()` 的參數。</font> * <font color="lightblue">標點邊界值:`const float pixel_x_min`、`const float pixel_x_max`、`const float pixel_y_min`、`const float pixel_y_max`。</font> * <font color="lightblue">資料總筆數:`numOfRow`。</font> * <font color="lightblue">標點時的軸向必須和上面一致,要改軸向等最後判斷完在調整 `scx`、 `scy` 的值就好。</font>