RAVPO - Robot Arm Video Processing for Omok

2019. 11. 16. 12:14프로젝트 요약

반응형

Robot Arm Video Processing for Omok

 

https://github.com/dltmdgh0611/RAVPO_v1/tree/master/RAVPO_v1

 

dltmdgh0611/RAVPO_v1

Contribute to dltmdgh0611/RAVPO_v1 development by creating an account on GitHub.

github.com

 

 

 

 

 

 

 

 

 


 

프로젝트 진행 계획

 

1단계 motion capture / video processing robot arm 

모션 캡처와 영상처리를 이용하여 영상에 발견된 물체를 향해 로봇팔이 움직여 집는 기능 (완료)

 

2단계 3d프린터 프레임을 사용하고 영상처리를 통한 스스로 오목두는 기계

오목 봇 - 영상 처리 프로그램 - MCU간의 통신 (진행 중)

 

3단계 3d프린터 프레임을 로봇팔로 대체

3d프린터의 xy방식에서 6dof 로봇팔로 하드웨어를 개조합니다. (예정)

 


 

 

 

 

 

1단계 프로토타입 구조

C++ OpenCV를 이용하여 영상처리 -> 시리얼 통신으로 MCU로 전송 -> 마이컴에서 각도 계산 및 수치 계산

 

 

 

 

영상처리 과정 (소스 생략)

다각형 검출 -> 도형의 중점 계산 -> 시리얼로 전송

 

 

 

 

 

로봇팔 수식 계산

시리얼 수신 -> bresenham Algorithm를 통하여 3차원 선을 그리며 이동 -> 좌표에 따른 각도 계산

 

 

 

 

 

bresenham 알고리즘 활용

 if ((l >= m) && (l >= n)) {
        if(errsetflag){
        err_1 = distance2[_Y] - l;
        err_2 = distance2[_Z] - l;
        errsetflag = false;
        }
        if(discounter[_X] < l) {
            
            if (err_1 > 0) {
                moving[_Y] += addY;
                err_1 -= distance2[_X];
            }
            if (err_2 > 0) {
                moving[_Z] += addZ;
                err_2 -= distance2[_X];
            }
            err_1 += distance2[_Y];
            err_2 += distance2[_Z];
            moving[0] += addX;
            discounter[_X]++;
            Serial.print("x : ");
            Serial.println(moving[_X]);
            Serial.print("y : ");
            Serial.println(moving[_Y]);
            Serial.print("z : ");
            Serial.println(moving[_Z]);
        }
        else if(discounter[_X] == distance[_X]){
          Posflag = true;
          discounter[_X]++;
          Serial.println("XF");
        }
        else Posflag = true;
      }

      else if ((m >= l) && (m >= n)) {
        if(errsetflag){
        err_1 = distance2[_X] - m;
        err_2 = distance2[_Z] - m;
        errsetflag = false;
        }
        if(discounter[_Y] < m) {
            
            if (err_1 > 0) {
                moving[_X] += addX;
                err_1 -= distance2[_Y];
            }
            if (err_2 > 0) {
                moving[_Z] += addZ;
                err_2 -= distance2[_Y];
            }
            err_1 += distance2[_X];
            err_2 += distance2[_Z];
            moving[1] += addY;
            discounter[_Y]++;
            Serial.print("x : ");
            Serial.println(moving[_X]);
            Serial.print("y : ");
            Serial.println(moving[_Y]);
            Serial.print("z : ");
            Serial.println(moving[_Z]);
        }
        else if(discounter[_Y] == distance[_Y]){
          Posflag = true;
          discounter[_Y]++;
          Serial.println("YF");
        }
        else Posflag = true;
      }

      else { 
        if(errsetflag){
          err_1 = distance2[_Y] - n;
          err_2 = distance2[_X] - n;
          errsetflag = false;
        }
        if(discounter[_Z] < n) {
            
            if (err_1 > 0) {
                moving[_Y] += addY;
                err_1 -= distance2[_Z];
            }
            if (err_2 > 0) {
                moving[_X] += addX;
                err_2 -= distance2[_Z];
            }
            err_1 += distance2[_Y];
            err_2 += distance2[_X];
            moving[2] += addZ;
            discounter[_Z]++;
          Serial.print("x : ");
          Serial.println(moving[_X]);
          Serial.print("y : ");
          Serial.println(moving[_Y]);
          Serial.print("z : ");
          Serial.println(moving[_Z]);
        }
        else if(discounter[_Z] == distance[_Z]){
          Posflag = true;
          discounter[_Z]++;
          Serial.println("ZF");
        }
        else Posflag = true;
      }

 

 

 

 

각도 계산부

float dx = (moving[_X] - x);
      float dy = (moving[_Y] - y);
      dz = moving[_Z];
      Serial.println("----------------------");
      Serial.println(dz);
      Serial.println(abs(moving[_X] - 320));
      angleZ = atan2(dz, abs(moving[_X] - 320));
      Serial.println("----------------------");
      float angle1 = atan2(dy, dx);
      float tx = (moving[_X] - cos(angle1) * segLength1);
      float ty = (moving[_Y] - sin(angle1) * segLength1);
      dx = tx - x2;
      dy = ty - y2;
      float angle2 = atan2(dy, dx);
      x = x2 + cos(angle2) * segLength2;
      y = y2 + sin(angle2) * segLength2;
      an2 = (int)(180 + angle2 * (180 / PI));
      an1 = abs((int)(180 + angle1 * (180 / PI)) - 270);
      if (an1 > 90) an1 = abs(360 - an1);
      an1 = an1 + (90 - an2);
      anZ = (int)(angleZ * (180 / PI));
      Serial.println("----------------------");
      Serial.println(an1);
      Serial.println(an2);
      Serial.println(anZ);
      Serial.println("----------------------");
      an1 = an1 / 180 * 440;
      an2 = an2 / 180 * 440;
      anZ = anZ / 180 * 440;

      sum_angle1 = (int)an1;
      sum_angle2 = (int)an2;
      sum_angleZ = (int)anZ;

 

 

 

 


 

 

 

2단계 프로토타입 구조

C# 윈도우 기반 오목 UI 및 상대 알고리즘 + opencv 영상처리 -> MCU에서 시리얼로 수신

 

 

오목 상대 알고리즘

모든 좌표를 방향마다 검사하고 공격적 이득 / 방어적 이득 점수를 합산하여 가장 높은 수에 두는 알고리즘.

 

영상 처리

그리드 마다 매트릭스를 나누어 해당 매트릭스에 파란색이 들었나 빨간색이 들었나 검출한다.

매트릭스의 픽셀을 분석하여 색깔을 구분한다.

 

펌웨어

윈도우 프로그램에서 전송받은 시리얼 값을 분석하여 모터를 원하는 지점으로 움직인다.

그리드 마다 고유한 좌표를 만들어두고 값을 받으면 해당 위치로 이동한 뒤 그리기를 

수행한다.

 

 

해당 동영상은 완성된 프로젝트의 동영상입니다.

 

 

반응형