2016年3月29日 星期二

3.3 v to 5.0 v buffer ic


1.   74LCX244

https://www.fairchildsemi.com/datasheets/74/74LCX244.pdf

2.   MC74VHC1GT275

http://www.onsemi.com/pub_link/Collateral/MC74VHC1GT125-D.PDF

arduino + RPI + spi


Arduino  線路  :  D10,  D11, D12,D13
wire :

  • SS – digital 10. You can use other digital pins, but 10 is generally the default as it is next to the other SPI pins;
  • MOSI – digital 11;
  • MISO – digital 12;
  • SCK – digital 13;



RPI pin 腳


Rabpberry Pi SignalSerial 7-seg Signal
GNDGND
3.3VVCC
CE1SS (Shift Select)
SCKSCK
MOSISDI
MISOSDO

 pin number

MOSI    P1-19
MISO    P1-21
SCLK    P1-23   P1-24    CE0
GND     P1-25   P1-26    CE1

如果是用   /dev/spi0.0  就是接 CE0 到  SS
如果是用   /dev/spi0.1  就是接 CE1 到  SS       

P1-24  接到  D10
P1 -19   接到  D11
P1-21 串一個1K 電阻接到  D12
P1-23    接到  D13


SPI序列周邊介面匯流排
SPI (Serial Peripheral Interface Bus),類似I²C,是一種4線同步序列資料協定,適用於可攜式裝置平臺系統,但使用率較 I²C少。SPI匯流排定義四組 logic signals:
‧ SCLK—Serial Clock(自master輸出)
‧ MOSI/SIMO—Master Output, Slave Input(自master輸出)
‧ MISO/SOMI—Master Input, Slave Output(自slave輸出)
‧ SS—Slave Select(active low;自master輸出)


RX1002 + 遙控器 + 馬達....



新增

pin  2, 3, 8, 9 去驅動馬達 .....


接收器接腳如下

左手   上下 : 油門  (第一通道)     pin 7

            左右 :  YAW (第四通道)    pin 4

 右手   上下 : PITCH (第三通道)    pin 5

             左右    ROLL (第二通道)   pin 6

code 如下:




#include <Servo.h>
#include <PinChangeInt.h>



#define THR_PIN  7
#define ROLL_PIN  6
#define PITCH_PIN  5
#define YAW_PIN  4  // PITH
#define SERIAL_BAND 9600

#define  FL_PIN  2
#define  FR_PIN  3
#define  BL_PIN  8
#define  BR_PIN  9

#define  thr_min  1100
#define  thr_max  1900

#define SERVO_NUM 4

unsigned int thr_start_time=0, thr_time=0;
unsigned int yaw_start_time=0, yaw_time=0;
unsigned int roll_start_time=0, roll_time=0;
unsigned int pitch_start_time=0, pitch_time=0;
unsigned int thr_val;

Servo MOTOR[SERVO_NUM];

void setup()
{

   Serial.begin(SERIAL_BAND);
  // while (!Serial) {}
 
    Serial.println("Init done");
   pinMode(THR_PIN, INPUT);
   pinMode(YAW_PIN, INPUT);
   pinMode(PITCH_PIN, INPUT);
   pinMode(ROLL_PIN, INPUT);
   //digitalWrite(UPPER_LIMIT_PIN, HIGH);
   PCintPort::attachInterrupt(THR_PIN, THR_Function, CHANGE );
   PCintPort::attachInterrupt(YAW_PIN, YAW_Function, CHANGE );
   PCintPort::attachInterrupt(PITCH_PIN, PITCH_Function, CHANGE );
   PCintPort::attachInterrupt(ROLL_PIN, ROLL_Function, CHANGE );
 
  MOTOR[0].attach(FL_PIN);
  MOTOR[1].attach(FR_PIN);
  MOTOR[2].attach(BL_PIN);
  MOTOR[3].attach(BR_PIN);

   for (int i=0;i<SERVO_NUM;i++)
      MOTOR[i].writeMicroseconds(thr_min);
 
}


void loop()
{
   while(Serial.available() > 0)
  {  
    char c = Serial.read();  // get it
     Serial.println(thr_time);
     //Serial.println(yaw_time);
     //Serial.println(pitch_time);
     //Serial.println(roll_time);
     Serial.println(thr_val);
  }
  /*
  thr_val =   (( long)(thr_time - thr_min)) * 100 / (thr_max - thr_min) + 137;

  //  1100/2040 *255 ~ 1900/2040*255  ->  137 ~ 237

  if(thr_val >=255)
      thr_val = 255;
  else if(thr_val <=0)
      thr_val = 0;


  analogWrite(FL_PIN,thr_val);
  analogWrite(FR_PIN,thr_val);
  analogWrite(BL_PIN,thr_val);
  analogWrite(BR_PIN,thr_val);
  */

   for (int i=0;i<SERVO_NUM;i++)
MOTOR[i].writeMicroseconds(thr_time);

}

void  THR_Function()
{
    if(digitalRead(THR_PIN)==HIGH) //  rising
       thr_start_time = micros();
    else
          thr_time  = micros() - thr_start_time;
}

void  YAW_Function()
{
    if(digitalRead(YAW_PIN)==HIGH) //  rising
       yaw_start_time = micros();
    else
          yaw_time  = micros() - yaw_start_time;
}

void  PITCH_Function()
{
    if(digitalRead(PITCH_PIN)==HIGH) //  rising
       pitch_start_time = micros();
    else
          pitch_time  = micros() - pitch_start_time;
}

void  ROLL_Function()
{
    if(digitalRead(ROLL_PIN)==HIGH) //  rising
       roll_start_time = micros();
    else
          roll_time  = micros() - roll_start_time;
}

quad-copter v2 研究筆記2



main.cpp  的流程

 //setting up IMU
  imu.set_com();
  imu.initialize();

  //setting up SPI
  ArduSPI.initialize();

 Set_default_PID_config();
  

  //Say I am ready
  Beep(3);
  Blink_led(10);

  //Starting Timer
  Timer.start();

  while(1){
   sleep(20000);
}


1.  imu.set_com

void DMP::set_com() {
    // initialize device
   
    mpu.initialize();

    // verify connection
    printf("Testing device connections...\n");
    printf(mpu.testConnection() ? "MPU6050 connection successful\n" : "MPU6050 connection failed\n");

    // load and configure the DMP
    printf("Initializing DMP...\n");
    devStatus = mpu.dmpInitialize();

    // make sure it worked (returns 0 if so)
    if (devStatus == 0) {
        // turn on the DMP, now that it's ready
        printf("Enabling DMP...\n");
        mpu.setDMPEnabled(true);

        
        mpuIntStatus = mpu.getIntStatus();

        // set our DMP Ready flag so the main loop() function knows it's okay to use it
        printf("DMP ready!\n");
        dmpReady = true;

        // get expected DMP packet size for later comparison
        packetSize = mpu.dmpGetFIFOPacketSize();
    } else {
        // ERROR!
        // 1 = initial memory load failed
        // 2 = DMP configuration updates failed
        // (if it's going to break, usually the code will be 1)
         printf("DMP Initialization failed (code %d)\n", devStatus);
    }


2.  initialize()

void DMP::initialize(){

  //This routine waits for the yaw angle to stop
  //drifting

  if (!dmpReady) return;

  printf("Initializing IMU...\n");

  //float gyr_old = 10;
  int n=0;
  do    {

      // wait for FIFO count > 42 bits
      do {
        fifoCount = mpu.getFIFOCount();
      }while (fifoCount<42);

      if (fifoCount >= 1024) {
        // reset so we can continue cleanly
        mpu.resetFIFO();
        printf("FIFO overflow!\n");

        // otherwise, check for DMP data ready interrupt
        //(this should happen frequently)
      } else {
  
        //read packet from fifo
        mpu.getFIFOBytes(fifoBuffer, packetSize);

        mpu.dmpGetGyro(g, fifoBuffer);

        //0=gyroX, 1=gyroY, 2=gyroZ
        //swapped to match Yaw,Pitch,Roll
        //Scaled from deg/s to get tr/s
        for (int i=0;i<DIM;i++){
          gyro[i]   = (float)(g[DIM-i-1])/131.0/360.0;
        }
      
      }

      n++;
  }while (fabs(gyro[ROLL]) + fabs(gyro[PITCH]) > 0.03 && n<5000);

  //  發覺已經沒有振動了

  mpu.dmpGetQuaternion(&q, fifoBuffer);
  mpu.dmpGetGravity(&gravity, &q);
  mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);

  for (int i=0;i<DIM;i++) m_ypr_off[i] = ypr[i];

   printf("IMU init done; offset values are :\n");
  printf("yaw = %f, pitch = %f, roll = %f, n= %d\n\n",
         ypr[YAW]*180/M_PI, ypr[PITCH]*180/M_PI,
         ypr[ROLL]*180/M_PI,n);
  initialized = true;
}


3.  ArduSPI.initilize()

/*
SPI::SPI()
{

 _devName = "/dev/spidev0.0";
 _speed   = 1000000;
 _bits    = 8;
 _delay   = 0;
 _mode    = 0;

}
*/


int SPI::initialize(){

  int ret = 0;
  _fd = open(_devName, O_RDWR); //  _devName = "/dev/spidev0.0";

  if (_fd < 0)
   printf("SPI initialize : can't open device\n");

  /*
   * spi mode
   */
  ret = ioctl(_fd, SPI_IOC_WR_MODE, &_mode); // _mode =0
  if (ret == -1)
    printf("SPI initialize : can't set spi mode\n");

  /*
   * bits per word
   */
  ret = ioctl(_fd, SPI_IOC_WR_BITS_PER_WORD, &_bits); // bit = 8
  if (ret == -1)
    printf("SPI initialize : can't set bits per word\n");


  /*
   * max speed hz
   */
  ret = ioctl(_fd, SPI_IOC_WR_MAX_SPEED_HZ, &_speed); // _speed   = 1000000;
  if (ret == -1)
    printf("SPI initialize : can't set max speed hz\n");


  printf("SPI initialization finished\n");
  return ret;

}

4.   Set_default_PID_config()
void Set_default_PID_config(){
  //manual initialization of PID constants
  yprRATE[YAW].set_Kpid(6.0, 0.0, 0.0);
  for (int i=1;i<3;i++){ // only  roll and pitch
    yprSTAB[i].set_Kpid(2.0, 0.001, 0.1);
    yprRATE[i].set_Kpid(2.0, 0.0, 0.0);
  }
}


5.   Timer.start()

void TimerClass::sig_handler_(int signum)
{
  pthread_mutex_lock(&TimerMutex_);

  //output to a log file
  //open log file
  fstream logfile;
  logfile.open("quadpilot.log", ios::out|ios::app);
  if (logfile.fail())          // Check for file creation and return error.
    {
      cout << "Error opening output.\n";
    }


  float RCinput[N_RC_CHAN],PIDout[3];
  uint16_t ESC[N_SERVO];

  //------------------------------------------------------
  //1-Get Remote values using SPI
  union bytes{
    uint8_t u8[2];
    uint16_t u16;
  } rc_union;
  uint8_t checksum=0,recv_checksum=1;
 while (checksum != recv_checksum) {

    checksum=0;

    for (int i=0;i<4;i++){
      ArduSPI.writeByte((uint8_t) (i+1)*10);
      rc_union.u8[0] = ArduSPI.rwByte((uint8_t) (i+1)*10+1);
      rc_union.u8[1] = ArduSPI.rwByte('S');
      //transaction ended
      RCinput[i] = (float) rc_union.u16;

      checksum+=rc_union.u8[0];
      checksum+=rc_union.u8[1];
    }

    //Control checksum
    ArduSPI.writeByte('C');
    recv_checksum = ArduSPI.rwByte('S');
  }

  //Bounding RC values to avoid division by zeros fex.
    for (int i=1;i<4;i++){
      if ( RCinput[i] > RC_MAX) RCinput[i] = RC_MAX;
      if ( RCinput[i] < RC_MIN) RCinput[i] = RC_MIN;
    }

  //outputing values to logfile
  logfile << RCinput[0] << " " << RCinput[1] << " "
          << RCinput[2] << " " << RCinput[3] << " ";
 // //convert into PID usable values
  RCinput[0] = (RCinput[0] - THR_MIN)/(THR_MAX-THR_MIN) * 100.0;
  RCinput[1] = -(RCinput[1] -(RC_MAX+RC_MIN)/2.) /
    (RC_MAX-RC_MIN) * K_YAW;
  RCinput[2] = (RCinput[2] -(RC_MAX+RC_MIN)/2.)/
    (RC_MAX-RC_MIN) * K_PITCH;
  RCinput[3] = (RCinput[3] -(RC_MAX+RC_MIN)/2.)/
    (RC_MAX-RC_MIN) * K_ROLL;

  //outputing values to logfile
  logfile << RCinput[0] << " " << RCinput[1] << " "
          << RCinput[2] << " " << RCinput[3] << " ";


  #ifdef XMODE
      //Switch to Xmode instead of +mode
      //orders are given in a ref frame rotated by 90deg.
      float cs45 = sqrt(2.)/2.;
      float Px = RCinput[2]*cs45 +  RCinput[3]*cs45;
      float Rx = - RCinput[2]*cs45 +  RCinput[3]*cs45;

      RCinput[2] = Px;
      RCinput[3] = Rx;

  #endif

  // printf("Received : %6.3f %6.3f %6.3f %6.3f\n", RCinput[0],
  //     RCinput[1], RCinput[2], RCinput[3]);

  //------------------------------------------------------
  //2- Get attitude of the drone
  while (imu.getAttitude() < 0){
  };


  //Compensate lost of Thrust due to angle of drone
  RCinput[0] = RCinput[0]/cos(imu.ypr[ROLL]/180*M_PI)
    /cos(imu.ypr[PITCH]/180*M_PI);

  //output to logfile
  // logfile << imu.ypr[YAW] << " " << imu.ypr[PITCH] << " "
  //      << imu.ypr[ROLL] << " "
  //      << imu.gyro[YAW] << " " << imu.gyro[PITCH] << " "
  //      << imu.gyro[ROLL] << " ";

 // printf("ATTITUDE: %7.2f %7.2f %7.2f\n",imu.ypr[YAW],
  //     imu.ypr[PITCH],
  //     imu.ypr[ROLL]);

  // printf("          %7.2f %7.2f %7.2f\n",imu.gyro[YAW],
  //     imu.gyro[PITCH],
  //     imu.gyro[ROLL]);


  //------------------------------------------------------
  //3- Timer dt
  Timer.calcdt_();
  //printf("dt : %f \n",Timer.dt);

  //------------------------------------------------------
  //4-1 Calculate PID on attitude
  #ifdef PID_STAB

  for (int i=1;i<DIM;i++){

    //yprSTAB[i].updateKpKi(RCinput[i+1],imu.ypr[i]);

    PIDout[i] =
      yprSTAB[i].update_pid_std(RCinput[i+1],
                            imu.ypr[i],
                            Timer.dt);
  }

  //yaw is rate PID only
  PIDout[YAW] = RCinput[YAW+1];

  // printf("PITCH: %7.2f %7.2f %7.2f\n",RCinput[PITCH+1],
  //     imu.ypr[PITCH],
  //     PIDout[PITCH]);

  // printf("ROLL: %7.2f %7.2f %7.2f\n",RCinput[ROLL+1],
  //     imu.ypr[ROLL],
  //     PIDout[ROLL]);

 for (int i=0;i<DIM;i++){
    PIDout[i] =
      yprRATE[i].update_pid_std(PIDout[i],
                                imu.gyro[i],
                                Timer.dt);
  }

  // printf("YAW:   %7.2f %7.2f %7.2f\n",RCinput[YAW+1],
  //     imu.gyro[YAW],
  //     PIDout[YAW]);

  // printf("PITCH: %7.2f %7.2f %7.2f\n",RCinput[PITCH+1],
  //     imu.gyro[PITCH],
  //     PIDout[PITCH]);

  // printf("ROLL:  %7.2f %7.2f %7.2f\n",RCinput[ROLL+1],
  //     imu.gyro[ROLL],
  //     PIDout[ROLL]);


  #endif

  //4-2 Calculate PID on rotational rate
  #ifdef PID_RATE
  for (int i=0;i<DIM;i++){
    PIDout[i] =
      yprRATE[i].update_pid_std(RCinput[i+1],
                            imu.gyro[i],
                            Timer.dt);
  }
  //printf("%7.2f  %7.2f\n",imu.gyro[PITCH],Timer.PIDout[PITCH]);
  #endif

  // logfile << PIDout[YAW] << " " << PIDout[PITCH] << " "
  //      << PIDout[ROLL] << " ";


  //------------------------------------------------------
  //5- Send ESC update via SPI
  //compute each new ESC value
 //if THR is low disable PID and be sure that ESC receive Zero
  //printf("%f \n",RCinput[0]);

  if (RCinput[0] < 7.0) {
    for (int i=0;i<4;i++){
      ESC[i] = (uint16_t)0;
    }
  } else {

    ESC[1] = (uint16_t)(RCinput[0]*10+1000
                        + PIDout[ROLL] + PIDout[YAW]);
    ESC[3] = (uint16_t)(RCinput[0]*10+1000
                        - PIDout[ROLL] + PIDout[YAW]);
    ESC[0] = (uint16_t)(RCinput[0]*10+1000
                        + PIDout[PITCH] - PIDout[YAW]);
    ESC[2] = (uint16_t)(RCinput[0]*10+1000
                        - PIDout[PITCH] - PIDout[YAW]);
  }

  checksum = 0;
  for (int iesc=0;iesc < N_SERVO; iesc++) {
    ArduSPI.writeByte(ESC[iesc] & 0xff);
    checksum+=ESC[iesc] & 0xff;
    ArduSPI.writeByte((ESC[iesc] >> 8) & 0xff);
    checksum+=(ESC[iesc] >> 8) & 0xff;
    }
  ArduSPI.writeByte(checksum);
  //sending Proccess it
  ArduSPI.writeByte('P');

  // printf("    Sent : %4d %4d %4d %4d\n", ESC[0],
  //     ESC[1], ESC[2], ESC[3]);

  //------------------------------------------------------
  //6-compensate dt
  Timer.compensate_();

  //ouputting ESC values to logfile
  logfile << ESC[0] << " " << ESC[1] << " "
          << ESC[2] << " " << ESC[3] << " " << endl;

  //closing logfile
  logfile.close();

 pthread_mutex_unlock(&TimerMutex_);
  //end of interrupt

}

2016年3月28日 星期一

RX1002 搭配arduino



左手   上下 : 油門  (第一通道)     pin 7

            左右 :  YAW (第四通道)    pin 4

 右手   上下 : PITCH (第三通道)    pin 5

             左右    ROLL (第二通道)   pin 6


接到  arduino






sample code 如下 :

#include <PinChangeInt.h>
#define THR_PIN  7
#define ROLL_PIN  6
#define PITCH_PIN  5
#define YAW_PIN  4  // PITH
#define SERIAL_BAND 9600
unsigned int thr_start_time=0, thr_time=0;
unsigned int yaw_start_time=0, yaw_time=0;
unsigned int roll_start_time=0, roll_time=0;
unsigned int pitch_start_time=0, pitch_time=0;
void setup()
{

   Serial.begin(SERIAL_BAND);
  // while (!Serial) {}
 
    Serial.println("Init done");
   pinMode(THR_PIN, INPUT);
   pinMode(YAW_PIN, INPUT);
   pinMode(PITCH_PIN, INPUT);
   pinMode(ROLL_PIN, INPUT);
   //digitalWrite(UPPER_LIMIT_PIN, HIGH);
   PCintPort::attachInterrupt(THR_PIN, THR_Function, CHANGE );
   PCintPort::attachInterrupt(YAW_PIN, YAW_Function, CHANGE );
   PCintPort::attachInterrupt(PITCH_PIN, PITCH_Function, CHANGE );
   PCintPort::attachInterrupt(ROLL_PIN, ROLL_Function, CHANGE );
}


void loop()
{
   while(Serial.available() > 0)
  {  
    char c = Serial.read();  // get it
     Serial.println(thr_time);
     Serial.println(yaw_time);
     Serial.println(pitch_time);
     Serial.println(roll_time);
  }
}

void  THR_Function()
{
    if(digitalRead(THR_PIN)==HIGH) //  rising
       thr_start_time = micros();
    else
          thr_time  = micros() - thr_start_time;
}

void  YAW_Function()
{
    if(digitalRead(YAW_PIN)==HIGH) //  rising
       yaw_start_time = micros();
    else
          yaw_time  = micros() - yaw_start_time;
}

void  PITCH_Function()
{
    if(digitalRead(PITCH_PIN)==HIGH) //  rising
       pitch_start_time = micros();
    else
          pitch_time  = micros() - pitch_start_time;
}

void  ROLL_Function()
{
    if(digitalRead(ROLL_PIN)==HIGH) //  rising
       roll_start_time = micros();
    else
          roll_time  = micros() - roll_start_time;
}

2016年3月22日 星期二

V2 針腳


arduino  的輸出腳位....

#define FL_MOTOR_OUT_PIN 4
#define FR_MOTOR_OUT_PIN 5
#define BL_MOTOR_OUT_PIN 6
#define BR_MOTOR_OUT_PIN 7


右上的排針....

   1.  SCL
   2.  SDA
   3.  BL  -> 黃色
   4.  FL -> 橘色
   5.  BR  ->  藍色
   6.   FR  ->  綠色

2016年3月17日 星期四

電子變速器校正



個別校正電變(尚未在PX4上測試)

  1. 將其中一個電變的三線陀機線連接到您搖控接收的油門通道(通常是通道3)。
  2. 打開搖控器並將油門拉到最高位。
  3. 連接鋰聚合物電池。
  4. 您將會聽到一段音響接著會聽到兩聲嗶聲。
  5. 在兩聲嗶聲後請將油門拉到最低位。
  6. 您將會聽到幾聲嗶聲(您使用幾個cell的電池就會聽到幾聲嗶聲),最後會聽到一聲長嗶聲代表電變已經接收到最高及最低位的PWM值,同時意味著電變的校正已經完成。
  7. 斷開電池的連接,並每個電變執行上面的步驟。
  8. 如果您沒辦法正常校正電變,可能代表您必須將油門通道反向。
  9. 如果您以上面的步驟沒辦法正常校正電變(例如持續聽到嗶聲),您可以試著將油門的最低位再往更低位微調50%。
  10. 您也可以試著在連接電池前先使用USB將APM上電開機。

2016年3月16日 星期三

quad-copter pi 的研究筆記 3



Q1 :   在 net.cpp  中設的  setpoint...

     yaw setpoint : 0.000000
     pitch setpoint : 0.100000
     roll setpoint : -0.300000

     為什麼不是零?      (建議把 ypr_off 打開)

     這會導致   算PID的時候..sum 的值變很大  ....(sum += ki * error * dt)  error = input - setpoint

     
Q2 :    量測  YPR 的初始值怪怪的.....  Roll 的值會一直變大....   這裡需要再 double-confirm


Q3 :    最後servo update  的地方...可能需要先改回來....感覺比較好抓bug...



=================================================================

今日觀察.....

YAW 的值會隨著時間的增加 ...其值一直在累加.......


我先把 setpoint 的地方改掉....改成


I highly recommend the FlameWheel 450, it flies quite well with standard PIDs but I have changed mine as follows: Angular Rate P = .145, Angular Rate I = .030 and Stabilize P = 4.0




腳位排針



   1.  SCL    黃色
   2.  SDA    橘色
   3.  BL  -> 黃色
   4.  FL -> 橘色
   5.  BR  ->  藍色
   6.   FR  ->  綠色




   [0] - GPIO4   綠色 ->  FR
   [1] - GPIO17 : 藍色 -> BR
   [2] - GPIO18 : 黃色 -> BL
   [3] - GPIO27 : 橘色  -> FL

   遙控器

   通道一  :    TXD 0  -> GPIO14  -> WiringPi  6
   通道二  :    RXD 0  -> GPIO15  -> WiringPi 16
   通道三  :   GPIO4  ->  GPIO4    -> WiringPi 4
   通道四  :    GPIO5  -> GPIO5    -> WiringPi 5


    servo blaster   :
  servoval[0] =(int)(throttle - PIDoutput[ROLL] + PIDoutput[PITCH] + PIDoutput[YAW]);  // FR
   servoval[1] =(int)(throttle - PIDoutput[ROLL] - PIDoutput[PITCH] - PIDoutput[YAW]);  // BR
   servoval[2] =(int)(throttle + PIDoutput[ROLL] - PIDoutput[PITCH] + PIDoutput[YAW]);  // BL
   servoval[3] =(int)(throttle + PIDoutput[ROLL] + PIDoutput[PITCH] - PIDoutput[YAW]);  // FL
 

PITCH  :  正值....  機身前面比較高.....   所以應該要把  後面的轉速提高

 ROLL  :   正值.....  機身左側比較高...... 所以應該要把  右邊的轉速提高


2016年3月12日 星期六

自製PWM 的小工具


先安裝wiringPi


使用   GPIO 4 , GPIO 17, GPIO 18, GPIO24 (not pin number..)



使用四條杜邦線....從上面四個 GPIO .... 聯到你要量的四個點....

然後執行

g++ -Wall -o PWM pwm.cpp -lwiringPi

sudo ./PWM 即可


pwm.cpp 程式碼如下:

#include <math.h>
#include <wiringPi.h>
#include <stdio.h>
#include <stdlib.h>
#include <wiringPiI2C.h>
#include <string.h>

int PWM_PIN0 =  7; // GPIO 4
int PWM_PIN1 =  0; // GPIO 17
int PWM_PIN2 =  1; // GPIO 18
int PWM_PIN3 =  5; // GPIO 24

void  measure(int PWM_PIN);

PI_THREAD (myThread0)
{
  for (;;)
  {
     measure(PWM_PIN0);
    delay (500) ;
  }
}

PI_THREAD (myThread1)
{
  for (;;)
  {
     measure(PWM_PIN1);
    delay (500) ;
  }
}

PI_THREAD (myThread2)
{
  for (;;)
  {
     measure(PWM_PIN2);
    delay (500) ;
  }
}

PI_THREAD (myThread3)
{
  for (;;)
  {
     measure(PWM_PIN3);
    delay (500) ;
  }
}


int main (void)
{
  int x;
  wiringPiSetup () ;
  x = piThreadCreate (myThread0) ;
  if (x != 0)
     printf ("it didn't start\n");

x = piThreadCreate (myThread1) ;
  if (x != 0)
     printf ("it didn't start\n");

x = piThreadCreate (myThread2) ;
  if (x != 0)
     printf ("it didn't start\n");

x = piThreadCreate (myThread3) ;
  if (x != 0)
     printf ("it didn't start\n");

  for (;;)
  {
    delay (500) ;
  }
  return 0 ;
}

void  measure(int PWM_PIN)
{
    unsigned int start,mid,end,temp,diff;
    while(digitalRead(PWM_PIN)==HIGH)
    {

    }
    while(digitalRead(PWM_PIN)==LOW)
    {
    }

    start = micros();

    while(digitalRead(PWM_PIN)==HIGH)
    {
    }

    mid = micros();

    while(digitalRead(PWM_PIN)==LOW)
    {
    }

    end = micros();

    diff = mid - start;
    temp = (diff * 100) / (end-start);
    printf("pin : %d, pulse : %d us,  PWM : %d,  Freq : %d \n",PWM_PIN,diff,temp,1000000/(end-start));
}


2016年3月10日 星期四

servoBlaster 研究筆記



他有兩個 版本

1. kernel space 版本

2.  user  space 版本

kernel 版本 compile 起來有問題....然後作者也推薦使用 user space 版本

原文如下:
There are two implementations of ServoBlaster; a kernel module based one, and a
user space daemon.  The kernel module based one is the original, but is more of
a pain to build because you need a matching kernel build.  The user space
daemon implementation is much more convenient to use and now has rather more
features than the kernel based one.  I would strongly recommend you use the
user space implementation.

進到 user folder....

編譯指令:

sudo  make

或是

sudo make install

其實兩個只差在如果你打 install... 就是開機的時候就會去load 去執行

執行指令:

$ sudo ./servod
結果如下:
Board revision:                  1
Using hardware:                PWM
Using DMA channel:              14
Idle timeout:             Disabled
Number of servos:                8
Servo cycle time:            20000us
Pulse increment step size:      10us
Minimum width value:            50 (500us)
Maximum width value:           250 (2500us)
Output levels:              Normal

Using P1 pins:               7,11,12,13,15,16,18,22

Servo mapping:
     0 on P1-7           GPIO-4
     1 on P1-11          GPIO-17
     2 on P1-12          GPIO-18
     3 on P1-13          GPIO-27
     4 on P1-15          GPIO-22
     5 on P1-16          GPIO-23
     6 on P1-18          GPIO-24
     7 on P1-22          GPIO-25


它總共有八個PWM 輸出....

If you want
to stop servod, the easiest way is to run:

$ sudo killall servod


The
default option is the equivalent of specifying

   --p1pins=7,11,12,13,15,16,18,22

可以修改的地方

Q1 :  如果只要四個輸出...要怎改?

 A1 :
        修改  servod.c

      把static char *default_p1_pins = "7,11,12,13,15,16,18,22";

改成static char *default_p1_pins = "7,11,12,13";

就可以了

Q2 : 如何改pulse width 的最小值(1000us)...最大值(2000us)

A2 :   
    
     #define DEFAULT_SERVO_MIN_US  500
    #define DEFAULT_SERVO_MAX_US  2500

     改成

     單位是 1 us
    #define DEFAULT_SERVO_MIN_US  1000
    #define DEFAULT_SERVO_MAX_US  2000

Q3 :   如何更改  cycle time,  改成 490Hz -> (1000000/490 = 2040):

         #define DEFAULT_CYCLE_TIME_US   20000

         改成#define DEFAULT_CYCLE_TIME_US   2040
     
Q4 :   如何更改  step_size ,  從 10us 改成 2us...
        
          #define DEFAULT_STEP_TIME_US    10

         改成#define DEFAULT_STEP_TIME_US    2
      
    

Q5 :   如何動態更改pulse寬度

        指定us 的方式
       echo P1-7=1500us > /dev/servoblaster
指定 step 的方式   

echo P1-7=150 > /dev/servoblaster # Using P1 pin number rather





Reference : https://github.com/richardghirst/PiBits/tree/master/ServoBlaster

mini osd update firmware



用FTDI232 和   mini osd 連接....新版的tool 要接五條線



燒入工具的路徑  https://code.google.com/archive/p/minimosd-extra/downloads

*.hex  是 driver 的16進制的輸出檔


Reference : http://www.rcgroups.com/forums/showthread.php?t=2026936

2016年3月8日 星期二

quadcopter v2 的研究筆記


先來看arduino 部分

//For ease of programming
#define THR 0
#define YAW 1
#define PITCH 2
#define ROLL 3
// Assign your channel in pins
#define THROTTLE_IN_PIN 8
#define YAW_IN_PIN 11
#define PITCH_IN_PIN 10
#define ROLL_IN_PIN 9

// Assign your channel out pins
#define FL_MOTOR_OUT_PIN 4
#define FR_MOTOR_OUT_PIN 5
#define BL_MOTOR_OUT_PIN 6
#define BR_MOTOR_OUT_PIN 7

ESC 接線
FL ->  PIN 4
FR ->  PIN 5
BL ->  PIN 6
BR ->  PIN 7

RC 接收器接線

THROTTLE -> PIN 8
YAW            -> PIN 11
PITCH          -> PIN 10
ROLL           -> PIN 9



//  宣告五個 interrupt......  四個rc input 和 一個 spi...


PCintPort::attachInterrupt(THROTTLE_IN_PIN, calcThrottle,CHANGE);
PCintPort::attachInterrupt(YAW_IN_PIN, calcYaw,CHANGE);
PCintPort::attachInterrupt(PITCH_IN_PIN, calcPitch,CHANGE);
PCintPort::attachInterrupt(ROLL_IN_PIN, calcRoll,CHANGE);

後面的CHANGE 是代表rising edge 的時候才會觸發.....

// if the pin is high, its a rising edge of the signal pulse, so lets record its value
if(digitalRead(THROTTLE_IN_PIN) == HIGH)

{
ulThrottleStart = micros();
}
else
{
// else it must be a falling edge, so lets get the time and subtract the time of the rising edge
// this gives use the time between the rising and falling edges i.e. the pulse duration.
unThrottleInShared = (uint16_t)(micros() - ulThrottleStart);
}

unThrottleInShared 就是代表為level 1 的時間....單位為us(10^-6)
void setup()
{
Serial.begin(9600); //for debugging...
pinMode(LED_PIN, OUTPUT);
/*
PWM measurement settings
*/
// using the PinChangeInt library, attach the interrupts
// used to read the channels // 宣告成 interrupt
PCintPort::attachInterrupt(THROTTLE_IN_PIN, calcThrottle,CHANGE);
PCintPort::attachInterrupt(YAW_IN_PIN, calcYaw,CHANGE);
PCintPort::attachInterrupt(PITCH_IN_PIN, calcPitch,CHANGE);
PCintPort::attachInterrupt(ROLL_IN_PIN, calcRoll,CHANGE);
// attach servo objects, these will generate the correct
// pulses for driving Electronic speed controllers, servos or other devices
// designed to interface directly with RC Receivers
MOTOR[0].attach(FL_MOTOR_OUT_PIN);
MOTOR[1].attach(FR_MOTOR_OUT_PIN);
MOTOR[2].attach(BL_MOTOR_OUT_PIN);
MOTOR[3].attach(BR_MOTOR_OUT_PIN);
//Set servo values to min
for (int i=0;i<SERVO_NUM;i++)
{
MOTOR[i].writeMicroseconds(RC_MIN);
}
/*
SPI settings
*/
// Declare MISO as output : have to send on
//master in, *slave out*
pinMode(MISO, OUTPUT);
// turn on SPI in slave mode
SPCR |= _BV(SPE);
// now turn on interrupts
SPI.attachInterrupt();
}

void loop()
{
//Constantly update rc_data
rc_data[THR].u16 = unThrottleInShared;
rc_data[YAW].u16 = unYawInShared;
rc_data[PITCH].u16 = unPitchInShared;
rc_data[ROLL].u16 = unRollInShared;
if (unThrottleInShared < 1000 &
unYawInShared < 1200 &
unPitchInShared < 1200 &
unRollInShared > 1200 ) {
uint32_t t_old = millis();
while (millis()-t_old < 500 ){
//wait to be sure that sticks are still in position
}
// if so change current status
if (unThrottleInShared < 1000 &
unYawInShared < 1200 &
unPitchInShared < 1200 &
unRollInShared > 1200 ) {
start = !start;
//change LED status
digitalWrite(13, start);
}
}
//Update servo (ESC) if necessary and started
if (update_servo & start){
uint8_t ipos=0;
byte checksum2=0;
for (int i=0;i<SERVO_NUM;i++)
{
//process buffer values
for (int ibyte = 0;ibyte<2;ibyte++){
esc_data[i].u8[ibyte] = rx_buf[ipos];
checksum2+=rx_buf[ipos];
ipos++;
}
}
if (rx_buf[ipos] == checksum2) {
//write ESC output
for (int i=0;i<SERVO_NUM;i++)
MOTOR[i].writeMicroseconds(esc_data[i].u16);
}
update_servo = false;
} else if (!start) {
for (int i=0;i<SERVO_NUM;i++)
{
MOTOR[i].writeMicroseconds(RC_MIN);
}
}
}
void calcThrottle()
{
// if the pin is high, its a rising edge of the signal pulse, so lets record its value
if(digitalRead(THROTTLE_IN_PIN) == HIGH)
{
ulThrottleStart = micros();
}
else
{
// else it must be a falling edge, so lets get the time and subtract the time of the rising edge
// this gives use the time between the rising and falling edges i.e. the pulse duration.
unThrottleInShared = (uint16_t)(micros() - ulThrottleStart);
}
}

/*-----------------------
SPI interrupt routine
------------------------*/
ISR (SPI_STC_vect)
{
//grab a new command and process it
byte cmd = SPDR;
if (cmd == 'S') {
//STOP do nothing : end of sending RC data
pos=0;
return;
}
if (cmd == 'P') {
//process it !
update_servo = true;
pos=0;
return;
}
if (cmd == 'C') {
//push Cheksum into the register
SPDR = checksum;
checksum = 0;
pos=0;
return;
}
//push data into a byte buffer
rx_buf[pos++] = cmd;
// 10-11 -> send 2bytes channel 1 value
// ...
// 40-41 -> send 2bytes channel 4 value
// ...
// 60-61 -> send 2bytes channel 6 value
switch (cmd){
case 10:
SPDR = rc_data[THR].u8[0];
checksum += rc_data[THR].u8[0];
break;
case 11:
SPDR = rc_data[THR].u8[1];
checksum += rc_data[THR].u8[1];
break;
case 20:
SPDR = rc_data[YAW].u8[0];
checksum += rc_data[YAW].u8[0];
break;
case 21:
SPDR = rc_data[YAW].u8[1];
checksum += rc_data[YAW].u8[1];
break;
case 30:
SPDR = rc_data[PITCH].u8[0];
checksum += rc_data[PITCH].u8[0];
break;
case 31:
SPDR = rc_data[PITCH].u8[1];
checksum += rc_data[PITCH].u8[1];
break;
case 40:
SPDR = rc_data[ROLL].u8[0];
checksum += rc_data[ROLL].u8[0];
break;
case 41:
SPDR = rc_data[ROLL].u8[1];
checksum += rc_data[ROLL].u8[1];
break;
}
}

再來看 RPI 的部分

void TimerClass::sig_handler_(int signum)
{
pthread_mutex_lock(&TimerMutex_);
//output to a log file
//open log file
fstream logfile;
logfile.open("quadpilot.log", ios::out|ios::app);
if (logfile.fail()) // Check for file creation and return error.
{
cout << "Error opening output.\n";
}
float RCinput[N_RC_CHAN],PIDout[3];
uint16_t ESC[N_SERVO];
//------------------------------------------------------
//1-Get Remote values using SPI
union bytes{
uint8_t u8[2];
uint16_t u16;
} rc_union;
uint8_t checksum=0,recv_checksum=1;
while (checksum != recv_checksum) {
checksum=0;
for (int i=0;i<4;i++){
ArduSPI.writeByte((uint8_t) (i+1)*10);
rc_union.u8[0] = ArduSPI.rwByte((uint8_t) (i+1)*10+1);
rc_union.u8[1] = ArduSPI.rwByte('S');
//transaction ended
RCinput[i] = (float) rc_union.u16;
checksum+=rc_union.u8[0];
checksum+=rc_union.u8[1];
}
//Control checksum
ArduSPI.writeByte('C');
recv_checksum = ArduSPI.rwByte('S');
}
//Bounding RC values to avoid division by zeros fex.
for (int i=1;i<4;i++){
if ( RCinput[i] > RC_MAX) RCinput[i] = RC_MAX;
if ( RCinput[i] < RC_MIN) RCinput[i] = RC_MIN;
}
//outputing values to logfile
logfile << RCinput[0] << " " << RCinput[1] << " "
<< RCinput[2] << " " << RCinput[3] << " ";
// //convert into PID usable values
RCinput[0] = (RCinput[0] - THR_MIN)/(THR_MAX-THR_MIN) * 100.0;
RCinput[1] = -(RCinput[1] -(RC_MAX+RC_MIN)/2.) /
(RC_MAX-RC_MIN) * K_YAW;
RCinput[2] = (RCinput[2] -(RC_MAX+RC_MIN)/2.)/
(RC_MAX-RC_MIN) * K_PITCH;
RCinput[3] = (RCinput[3] -(RC_MAX+RC_MIN)/2.)/
(RC_MAX-RC_MIN) * K_ROLL;
//outputing values to logfile
logfile << RCinput[0] << " " << RCinput[1] << " "
<< RCinput[2] << " " << RCinput[3] << " ";
#ifdef XMODE
//Switch to Xmode instead of +mode
//orders are given in a ref frame rotated by 90deg.
float cs45 = sqrt(2.)/2.;
float Px = RCinput[2]*cs45 + RCinput[3]*cs45;
float Rx = - RCinput[2]*cs45 + RCinput[3]*cs45;
RCinput[2] = Px;
RCinput[3] = Rx;
#endif
// printf("Received : %6.3f %6.3f %6.3f %6.3f\n", RCinput[0],
// RCinput[1], RCinput[2], RCinput[3]);
//------------------------------------------------------
//2- Get attitude of the drone
while (imu.getAttitude() < 0){
};
//Compensate lost of Thrust due to angle of drone
RCinput[0] = RCinput[0]/cos(imu.ypr[ROLL]/180*M_PI)
/cos(imu.ypr[PITCH]/180*M_PI);
//output to logfile
// logfile << imu.ypr[YAW] << " " << imu.ypr[PITCH] << " "
// << imu.ypr[ROLL] << " "
// << imu.gyro[YAW] << " " << imu.gyro[PITCH] << " "
// << imu.gyro[ROLL] << " ";
// printf("ATTITUDE: %7.2f %7.2f %7.2f\n",imu.ypr[YAW],
// imu.ypr[PITCH],
// imu.ypr[ROLL]);
// printf(" %7.2f %7.2f %7.2f\n",imu.gyro[YAW],
// imu.gyro[PITCH],
// imu.gyro[ROLL]);
//------------------------------------------------------
//3- Timer dt
Timer.calcdt_();
//printf("dt : %f \n",Timer.dt);
//------------------------------------------------------
//4-1 Calculate PID on attitude
#ifdef PID_STAB
for (int i=1;i<DIM;i++){
//yprSTAB[i].updateKpKi(RCinput[i+1],imu.ypr[i]);
PIDout[i] =
yprSTAB[i].update_pid_std(RCinput[i+1],
imu.ypr[i],
Timer.dt);
}
//yaw is rate PID only
PIDout[YAW] = RCinput[YAW+1];
// printf("PITCH: %7.2f %7.2f %7.2f\n",RCinput[PITCH+1],
// imu.ypr[PITCH],
// PIDout[PITCH]);
// printf("ROLL: %7.2f %7.2f %7.2f\n",RCinput[ROLL+1],
// imu.ypr[ROLL],
// PIDout[ROLL]);
for (int i=0;i<DIM;i++){
PIDout[i] =
yprRATE[i].update_pid_std(PIDout[i],
imu.gyro[i],
Timer.dt);
}
// printf("YAW: %7.2f %7.2f %7.2f\n",RCinput[YAW+1],
// imu.gyro[YAW],
// PIDout[YAW]);
// printf("PITCH: %7.2f %7.2f %7.2f\n",RCinput[PITCH+1],
// imu.gyro[PITCH],
// PIDout[PITCH]);
// printf("ROLL: %7.2f %7.2f %7.2f\n",RCinput[ROLL+1],
// imu.gyro[ROLL],
// PIDout[ROLL]);
#endif
//4-2 Calculate PID on rotational rate
#ifdef PID_RATE
for (int i=0;i<DIM;i++){
PIDout[i] =
yprRATE[i].update_pid_std(RCinput[i+1],
imu.gyro[i],
Timer.dt);
}
//printf("%7.2f %7.2f\n",imu.gyro[PITCH],Timer.PIDout[PITCH]);
#endif
// logfile << PIDout[YAW] << " " << PIDout[PITCH] << " "
// << PIDout[ROLL] << " ";
//------------------------------------------------------
//5- Send ESC update via SPI
//compute each new ESC value
//if THR is low disable PID and be sure that ESC receive Zero
//printf("%f \n",RCinput[0]);
if (RCinput[0] < 7.0) {
for (int i=0;i<4;i++){
ESC[i] = (uint16_t)0;
}
} else {
ESC[1] = (uint16_t)(RCinput[0]*10+1000
+ PIDout[ROLL] + PIDout[YAW]);
ESC[3] = (uint16_t)(RCinput[0]*10+1000
- PIDout[ROLL] + PIDout[YAW]);
ESC[0] = (uint16_t)(RCinput[0]*10+1000
+ PIDout[PITCH] - PIDout[YAW]);
ESC[2] = (uint16_t)(RCinput[0]*10+1000
- PIDout[PITCH] - PIDout[YAW]);
}
checksum = 0;
for (int iesc=0;iesc < N_SERVO; iesc++) {
ArduSPI.writeByte(ESC[iesc] & 0xff);
checksum+=ESC[iesc] & 0xff;
ArduSPI.writeByte((ESC[iesc] >> 8) & 0xff);
checksum+=(ESC[iesc] >> 8) & 0xff;
}
ArduSPI.writeByte(checksum);
//sending Proccess it
ArduSPI.writeByte('P');
// printf(" Sent : %4d %4d %4d %4d\n", ESC[0],
// ESC[1], ESC[2], ESC[3]);
//------------------------------------------------------
//6-compensate dt
Timer.compensate_();
//ouputting ESC values to logfile
logfile << ESC[0] << " " << ESC[1] << " "
<< ESC[2] << " " << ESC[3] << " " << endl;
//closing logfile
logfile.close();
pthread_mutex_unlock(&TimerMutex_);
//end of interrupt
}


Ref : https://github.com/vjaunet/QUADCOPTER_V2