티스토리 뷰



프로세싱에서 PID 매개변수 설정하기




목표


프로세싱에서 매개변수를 설정해주고 설정 한 값들을 시리얼 포트를 이용해서 아두이노로 값을 전달한다

(여기서는 매개변수 설정만 해주고 아두이노로 전달하지는 않는다.)




소스코드



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

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

void setup()
{
  size(700, 440);
  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]);
    
    cp5.addSlider("roll_kp")
      .setRange(0, 10)
      .setScrollSensitivity(0.2 * 10 / 10)
      .setPosition(20, 200)
      .setSize(600, 19)
      .setValue(1)
      ;
      
    cp5.addSlider("roll_ki")
      .setRange(0, 0.25)
      .setScrollSensitivity(0.005 * 10 / 0.25)
      .setPosition(20, 220)
      .setSize(600, 19)
      .setValue(0.0)
      ;
    
    cp5.addSlider("roll_kd")
      .setRange(0, 0.6)
      .setScrollSensitivity(0.1 * 10 / 0.6)
      .setPosition(20, 240)
      .setSize(600, 19)
      .setValue(0)
      ;
      
    cp5.addSlider("pitch_kp")
     .setRange(0, 10)
     .setScrollSensitivity(0.2*10/10)
     .setPosition(20, 280)
     .setSize(600, 19)
     .setValue(1)
     ;
    
  cp5.addSlider("pitch_ki")
     .setRange(0, 0.25)
     .setScrollSensitivity(0.005*10/0.25)
     .setPosition(20, 300)
     .setSize(600, 19)
     .setValue(0.0)
     ;
    
  cp5.addSlider("pitch_kd")
     .setRange(0, 0.6)
     .setScrollSensitivity(0.1*10/0.6)
     .setPosition(20, 320)
     .setSize(600, 19)
     .setValue(0)
     ;
     
  cp5.addSlider("yaw_kp")
     .setRange(0, 10)
     .setScrollSensitivity(0.2*10/10)
     .setPosition(20, 360)
     .setSize(600, 19)
     .setValue(1)
     ;
    
  cp5.addSlider("yaw_ki")
     .setRange(0, 0.25)
     .setScrollSensitivity(0.005*10/0.25)
     .setPosition(20, 380)
     .setSize(600, 19)
     .setValue(0.0)
     ;
    
  cp5.addSlider("yaw_kd")
     .setRange(0, 0.6)
     .setScrollSensitivity(0.1*10/0.6)
     .setPosition(20, 400)
     .setSize(600, 19)
     .setValue(0)
     ;
   
    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;
  }
}

public void roll_kp(float kp)
{
  println("roll kp : " + kp);
  Comport.write('a');
  Comport.write((int)((kp + 0.01) / 0.2));
}

public void roll_ki(float ki) 
{  
  println("roll ki : " + ki);
  Comport.write('b');
  Comport.write((int)(ki/0.005));
}

public void roll_kd(float kd) 
{  
  println("roll kd : " + kd);
  Comport.write('c');
  Comport.write((int)((kd+0.01)/0.1));
}

public void pitch_kp(float kp) 
{
  println("pitch kp : " + kp);
  Comport.write('d');
  Comport.write((int)((kp+0.01)/0.2));
}

public void pitch_ki(float ki) 
{  
  println("pitch ki : " + ki);
  Comport.write('e');
  Comport.write((int)(ki/0.005));
}

public void pitch_kd(float kd) 
{  
  println("pitch kd : " + kd);
  Comport.write('f');
  Comport.write((int)((kd+0.01)/0.1));
}

public void yaw_kp(float kp) 
{
  println("yaw kp : " + kp);
  Comport.write('g');
  Comport.write((int)((kp+0.01)/0.2));
}

public void yaw_ki(float ki) 
{  
  println("yaw ki : " + ki);
  Comport.write('h');
  Comport.write((int)(ki/0.005));
}

public void yaw_kd(float kd) 
{  
  println("yaw kd : " + kd);
  Comport.write('i');
  Comport.write((int)((kd+0.01)/0.1));
}

다운로드 파일

P_15ypr2stdpid02.pde





설명#1 매개변수 슬라이더 구현하기


cp5.addSlider("roll_kp")
      .setRange(0, 10)
      .setScrollSensitivity(0.2 * 10 / 10)
      .setPosition(20, 200)
      .setSize(600, 19)
      .setValue(1)
      ;
      
 cp5.addSlider("roll_ki")
      .setRange(0, 0.25)
      .setScrollSensitivity(0.005 * 10 / 0.25)
      .setPosition(20, 220)
      .setSize(600, 19)
      .setValue(0.0)
      ;
    
 cp5.addSlider("roll_kd")
      .setRange(0, 0.6)
      .setScrollSensitivity(0.1 * 10 / 0.6)
      .setPosition(20, 240)
      .setSize(600, 19)
      .setValue(0)
      ;
      
 cp5.addSlider("pitch_kp")
     .setRange(0, 10)
     .setScrollSensitivity(0.2*10/10)
     .setPosition(20, 280)
     .setSize(600, 19)
     .setValue(1)
     ;
    
cp5.addSlider("pitch_ki")
     .setRange(0, 0.25)
     .setScrollSensitivity(0.005*10/0.25)
     .setPosition(20, 300)
     .setSize(600, 19)
     .setValue(0.0)
     ;
    
cp5.addSlider("pitch_kd")
     .setRange(0, 0.6)
     .setScrollSensitivity(0.1*10/0.6)
     .setPosition(20, 320)
     .setSize(600, 19)
     .setValue(0)
     ;
     
cp5.addSlider("yaw_kp")
     .setRange(0, 10)
     .setScrollSensitivity(0.2*10/10)
     .setPosition(20, 360)
     .setSize(600, 19)
     .setValue(1)
     ;
    
cp5.addSlider("yaw_ki")
     .setRange(0, 0.25)
     .setScrollSensitivity(0.005*10/0.25)
     .setPosition(20, 380)
     .setSize(600, 19)
     .setValue(0.0)
     ;
    
cp5.addSlider("yaw_kd")
     .setRange(0, 0.6)
     .setScrollSensitivity(0.1*10/0.6)
     .setPosition(20, 400)
     .setSize(600, 19)
     .setValue(0)
     ;

이 루틴을 구현하고자 하는 목표는 GUI 상에서 마우스 휠로 슬라인더를 스크롤 하므로서 매개변수 값을 설정을 할려고 합니다.


출처 : wikipedia 매개변수들을 변경경을 하면 이렇게 변하겠죠?


Roll, Pitch, Yaw에 각각 Kp, Ki, Kd 라는 매개변수(Gain)이 존재합니다. 그래서 총 9개의 매개변수를 설정할 수 있는 구현을 추가하면 됩니다.


※여기서 주의!!!!

이번에 추가하는 루틴은 Serial 객체를 생성하기 전에 추가를 시켜주어야 합니다. 만약 그러지 않는다면 Serial Port가 정상적으로 작동을 하지 않게 됩니다.


cp5.addSlider("roll_kp")
      .setRange(0, 10)
      .setScrollSensitivity(0.2 * 10 / 10)
      .setPosition(20, 200)
      .setSize(600, 19)
      .setValue(1)
      ;

여기서 사용이 되는 addSlider 함수는 GUI로 슬라인더를 추가하는 함수입니다. 위에 코드를 보시게 되면 "roll_kp" 이렇게 적혀있는데 이것은 나중에 콜백함수가 사용되는 원인이 됩니다.


setScrollSensitivity(스크롤 감도를 설정)은 0.2씩 증가 또는 감소하며 그 값의 범위는 0 ~ 10이여서 그럽니다. 그러면 총 50단계로 매개변수 설정이 가능해 지게 됩니다.

그리고 초기 슬라이더 값을 1로 설정을 해주고 있습니다.  


그 이하의 문장도 동일합니다^^




설명#2 매개변수의 콜백함수 설정



public void roll_kp(float kp)
{
  println("roll kp : " + kp);
  Comport.write('a');
  Comport.write((int)((kp + 0.01) / 0.2));
}

public void roll_ki(float ki) 
{  
  println("roll ki : " + ki);
  Comport.write('b');
  Comport.write((int)(ki/0.005));
}

public void roll_kd(float kd) 
{  
  println("roll kd : " + kd);
  Comport.write('c');
  Comport.write((int)((kd+0.01)/0.1));
}

public void pitch_kp(float kp) 
{
  println("pitch kp : " + kp);
  Comport.write('d');
  Comport.write((int)((kp+0.01)/0.2));
}

public void pitch_ki(float ki) 
{  
  println("pitch ki : " + ki);
  Comport.write('e');
  Comport.write((int)(ki/0.005));
}

public void pitch_kd(float kd) 
{  
  println("pitch kd : " + kd);
  Comport.write('f');
  Comport.write((int)((kd+0.01)/0.1));
}

public void yaw_kp(float kp) 
{
  println("yaw kp : " + kp);
  Comport.write('g');
  Comport.write((int)((kp+0.01)/0.2));
}

public void yaw_ki(float ki) 
{  
  println("yaw ki : " + ki);
  Comport.write('h');
  Comport.write((int)(ki/0.005));
}

public void yaw_kd(float kd) 
{  
  println("yaw kd : " + kd);
  Comport.write('i');
  Comport.write((int)((kd+0.01)/0.1));
}


설명#1에서 언급을 했었습니다.

설명#1에서 언급한 내용

"roll_kp"라는 변수를 기억하시고 다음 코드를 확인해 보시죠



public void roll_kp(float kp)
{
  println("roll kp : " + kp);
  Comport.write('a');
  Comport.write((int)((kp + 0.01) / 0.2));
}

public으로 roll_kp 라는 함수를 정의해주고 있습니다. roll_kp는 콜백 함수이며 addSlider 함수에서 사용된 roll_kp가 여기서 정의된 함수 이름입니다. 따라서 우리가 GUI 상에서 roll_kp 슬라이더를 스크롤 하게 된다면 이벤트가 발생하게 되어서 roll_kp 함수가 호출되는 것입니다. 따라서 매개변수 kp에 슬라이더의 현재 위치값이 넘어오게 되며 write 함수를 호출해서 아두이노 드론으로 'a'라는 값과 (kp + 0.01) / 0.2 값을 차례대로 보내주고 있습니다.


Q. 그러면 여기서 왜 그냥 0.2로 나누어 주면 되는것을 0.01를 왜 더해주는건가??

→ 만약에 Kp 값이 1.999라고 치면 Kp 변수가 실수라서 0.2 간격으로 정확하게 값을 표하시는 경우가 발생 됩니다. 따라서 0.01을 더해서 2.009를 만들어 0.2로 나누게 되면 10.045가 됩니다. 이 값을 정수화를 시켜주면 Kp 매개변수는 10이 되게 됩니다.




Comments