티스토리 뷰


Processing 으로 PDI 출력 구현


목표


이전 포스팅(아두이노에서 PID 출력값을 프로세싱으로 보내기)에서 받은 PID 출력 값을 확인할 수 있도록 하기



PID 출력값 받기 전 상태




소스코드




import processing.serial.*; //Library to Serial import controlP5.*; //Library to ControlP5 ControlP5 cp5; Chart rollChart, pitchChart, yawChart; Serial Comport; void setup() { size(700, 200); noStroke(); cp5 = new ControlP5(this); //creative Objecy GUI cp5.addTextlabel("","Dron PID Output") .setPosition(200, 10) .setColorValue(0xffffff00) .setFont(createFont("Lucida Sans", 30)); rollChart = cp5.addChart("roll_output") .setPosition(20, 60) .setSize(200, 100) .setRange(0, 255) .setView(Chart.LINE) .setStrokeWeight(1.5) ; rollChart.addDataSet("incoming"); rollChart.setData("incoming", new float[100]); pitchChart = cp5.addChart("pitch_output") .setPosition(240, 60) .setSize(200, 100) .setRange(0, 255) .setView(Chart.LINE) .setStrokeWeight(1.5) ; pitchChart.addDataSet("incoming"); pitchChart.setData("incoming", new float[100]); yawChart = cp5.addChart("yaw_output") .setPosition(460, 60) .setSize(200, 100) .setRange(0, 255) .setView(Chart.LINE) .setStrokeWeight(1.5) ; yawChart.addDataSet("incoming"); yawChart.setData("incoming", new float[100]); println((Object[])Serial.list()); String portName = Serial.list()[0]; Comport = new Serial(this, portName, 115200); } int roll_output; int pitch_output; int yaw_output; void draw() { background(0); rollChart.push("incoming", roll_output); pitchChart.push("incoming", pitch_output); yawChart.push("incoming", yaw_output); } final byte IDLE = 'I', ROLL_OUTPUT = 'a', PITCH_OUTPUT = 'b', YAW_OUTPUT = 'c'; int inputState = IDLE; //??? void serialEvent(Serial port) { int inputData = port.read(); if(inputState == IDLE) { switch(inputData) { case ROLL_OUTPUT: inputState = ROLL_OUTPUT; break; case PITCH_OUTPUT: inputState = PITCH_OUTPUT; break; case YAW_OUTPUT: inputState = YAW_OUTPUT; break; default: break; } } else { switch(inputState) { case ROLL_OUTPUT: roll_output = inputData; break; case PITCH_OUTPUT: pitch_output = inputData; break; case YAW_OUTPUT: yaw_output = inputData; break; } inputState = IDLE; } }

다운로드 파일

P_15ypr2stdpid02.pde






설명#1 프로세싱 사용을 위한 기본 설정



import processing.serial.*; //Library to Serial
import controlP5.*;         //Library to ControlP5


ControlP5 cp5;
Chart rollChart, pitchChart, yawChart;
Serial Comport;

void setup()
{
  size(700, 200);
  noStroke();
  
  cp5 = new ControlP5(this);  //creative Objecy GUI
  
  cp5.addTextlabel("","Dron PID Output")
    .setPosition(200, 10)
    .setColorValue(0xffffff00)
    .setFont(createFont("Lucida Sans", 30));
}

시리얼 통신을 하기 위해서 processing.serial 관련 패캐지들을 모두 import 시켜주고 있는 것 입니다. 따라서 Comport라는 핸들을 사용할 수 있게 된 것 입니다.


이거에 대해 자세한 내용은 전에 포스팅을 했었던 [드론] 프로세싱과 ESP8266을 이용해서 드론 모터속도 조절하기_2 를 참고하시면 도움이 되실 것 입니다.



프로세싱과 ESP8266을 이용해서 드론 모터속도 조절하기_2에서 참고한 내용





설명#2 PID 출력 값을 표시하기 위한 Chart 구현하기



 rollChart = 
      cp5.addChart("roll_output")
      .setPosition(20, 60)
      .setSize(200, 100)
      .setRange(0, 255)
      .setView(Chart.LINE)
      .setStrokeWeight(1.5)
      ;
    rollChart.addDataSet("incoming");
    rollChart.setData("incoming", new float[100]);
    
    pitchChart = 
      cp5.addChart("pitch_output")
      .setPosition(240, 60)
      .setSize(200, 100)
      .setRange(0, 255)
      .setView(Chart.LINE)
      .setStrokeWeight(1.5)
      ;
    pitchChart.addDataSet("incoming");
    pitchChart.setData("incoming", new float[100]);
    
    yawChart = 
      cp5.addChart("yaw_output")
      .setPosition(460, 60)
      .setSize(200, 100)
      .setRange(0, 255)
      .setView(Chart.LINE)
      .setStrokeWeight(1.5)
      ;
    yawChart.addDataSet("incoming");
    yawChart.setData("incoming", new float[100]);

GUI 객체 생성자인 cp5를 이용하여 addChart 함수를 호출하고 있습니다. 그냥 GUI로 그래프를 그리고 싶을땐 adChart를 사용하는구나 라고 알면 될거 같아요.  아두이노에서 시리얼 포트를 통해서 0 ~ 255값을 받아오기 때문에 setRange를 위와같이 설정을 해준 것 입니다.


그리고 마지막에 addDataSet 함수를 호출해서 incoming라는 이름을 가진 데이터 집합을 하나 추가하고 있습니다.

그런 다음에

setData 함수를 호출해서 위에서 만들어준 데이터 집함에 데이터를 저장하기 위한 실수용 버퍼를 할당해주는 것입니다.




설명#3 Serial 통신을 하기 위한 설정


println((Object[])Serial.list());
String portName = Serial.list()[0];
Comport = new Serial(this, portName, 115200);

아두이노의 Serial Port를 이용해서 PID 출력값을 받아 오기 위한 설정을 해주는 곳 입니다.

처음에는 println 함수를 호출에서 Serial 목록(list)를 출력해주고 String 자료형을 이용한 변수에다가 Serial list를 얻어오고 있습니다.[ 제 PC는 0번으로 해야 통신이 되더라고요, 이 점은 PC에서 아두이노랑 시리얼 통신을 할때 몇번 포트를 이용하는지에 따라 다릅니다.] -> 참고하기




설명#4 무한 draw 함수에서 그래프로 표시하기


int roll_output;
int pitch_output;
int yaw_output;

void draw()
{
  background(0);
  
  rollChart.push("incoming", roll_output);
  pitchChart.push("incoming", pitch_output);
  yawChart.push("incoming", yaw_output);
  
}

roll의 출력값을 가진 차트인 rollChart에 값을 보내줍니다(push) 어떤 값이냐면 roll_output이라는 출력값인데 이것을 데이터 집합인 incoming 버퍼에 저장을 시켜주게 됩니다.






설명#5 드론에서 받아온 PID 출력값을 그래프로 표시하기 전 처리하기



final byte
IDLE = 'I',
ROLL_OUTPUT = 'a',
PITCH_OUTPUT = 'b',
YAW_OUTPUT = 'c';

int inputState = IDLE; //???

void SerialEvent(Serial port)
{
  int inputData = port.read();
  
  if(inputState == IDLE)
  {
    switch(inputData)
    {
      case ROLL_OUTPUT:
          inputState = ROLL_OUTPUT;
          break;
      case PITCH_OUTPUT:
          inputState = PITCH_OUTPUT;
          break;
      case YAW_OUTPUT:
          inputState = YAW_OUTPUT;
          break;
      default:
          break;
    }
  }
  else
  {
    switch(inputState)
    {
      case ROLL_OUTPUT:
          roll_output = inputData;
          break;
      case PITCH_OUTPUT:
          pitch_output = inputData;
          break;
      case YAW_OUTPUT:
          yaw_output = inputData;
          break;
    }
    inputState = IDLE;
  }
}

일단 final라는 Java 변수를 알아보겠습니다. 간단하게 요약을 해보자면 값이 불변한것만 제외하면 다른 변수와는 같은 기능을 한다. 입니다. 더 자세하게 참고를 하고 싶으시면 여기로~




final byte
IDLE = 'I',
ROLL_OUTPUT = 'a',
PITCH_OUTPUT = 'b',
YAW_OUTPUT = 'c';

이부분이 Arduino IDE에서 설정을 해준 헤더값을 정의해 주는 부분입니다.


※ IDLE는 초기 아두이노의 입력이 없을때 IDLE 상태라고 합니다.

[드론] 아두이노에서 프로세싱으로 PID 출력 보내기..에서


그리고 serialEvent라는 콜백 형태의 함수를 사용하고 있는데요 이것은 시리얼 포트에 데이터가 도착을 하면 호출되는 콜백 형태의 함수입니다. 즉, 아두이노 드론에서 PID 출력값이 보내지면 그 값이 시리얼 포트를 타고 프로세싱으로 이동하겠죠? 프로세싱에 데이터가 도착을 하면 serialEvent 함수가 호출되는 것 입니다. 말 그대로 이벤트이지요.




int inputState = IDLE;

inputState(현재 입력 상태)를 IDLE로 설정을 해주었습니다. 이는 위에서도 말했다 싶이 아두이노에서 입력 데이터가 없을 경우를 IDLE 상태라고 지정을 해준 것 입니다.



void SerialEvent(Serial port)
{
  int inputData = port.read();
  
  if(inputState == IDLE)
  {
    switch(inputData)
    {
      case ROLL_OUTPUT:
          inputState = ROLL_OUTPUT;
          break;
      case PITCH_OUTPUT:
          inputState = PITCH_OUTPUT;
          break;
      case YAW_OUTPUT:
          inputState = YAW_OUTPUT;
          break;
      default:
          break;
    }
  }
  else
  {
    switch(inputState)
    {
      case ROLL_OUTPUT:
          roll_output = inputData;
          break;
      case PITCH_OUTPUT:
          pitch_output = inputData;
          break;
      case YAW_OUTPUT:
          yaw_output = inputData;
          break;
    }
    inputState = IDLE;
  }
}

그리고 아두이노 드론에서 데이터가 보내지게 되면 이벤트 발생이 되어서 SerialEvent 함수가 호출되게 되는데  이때 시리얼 포트로 통해 들어온 데이터를 inputData에 int형으로 저장을 하게 됩니다.(int 형으로 받는 이유는 아두이노에서 int형으로 형 변환을 해서 보내주었기 때문입니다. 여기서 0 ~ 255의 값으로 수치화를 시켜야 하기 때문에 형 변환을 시켜서 보내준 것 입니다.)


그리고 if문과 else문 두가지가 있습니다.

첫번째 if문은 inputdata = IDLE 즉, 아두이노 드론에서 데이터가 넘어오지 않았을 경우 or 넘어고 난 후 IDLE를 초기화 시켜준 경우(초기화 부분은 else 구문에 있습니다.) if 문이 실행이 됩니다. 그리고 inputData가 무엇이냐에 따라서 inputState상태에 PID 출려값들이 저장이 되게 됩니다.


else문도 마찬가지입니다. 새로 아두이노 드론에서 들어온 데이터를 업데이트 시켜주어야 하기 대문에 각 PID 출력 저장 변수에다가 input Data를 저장해주는 것 입니다. 저장이 끝나면 다시 inputState상태는 다음을 위해서 IDLE로 초기화 시켜줍니다.




결과


그러면 드론의 PID 값에 따라서 그래프가 변화되는것을 보 수 있습니다.





Comments