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

沒有留言:

張貼留言