モータードライバーver4.1 角度フィードバック(クーロン摩擦補償) 角速度フィードバック
Dependencies: mbed QEI PID DBG
MotorDriver_ver4-1.cpp
- Committer:
- shundai
- Date:
- 2019-07-14
- Revision:
- 4:40a764acc4ec
- Parent:
- 3:141dc1666df2
- Child:
- 5:1155a15f978c
File content as of revision 4:40a764acc4ec:
/************************************************ MotorDriver ver4.1 farmware author : shundai miyawaki 2019/06/14 ~ Copyright shundai miyawaki. All rights reserved. ************************************************/ #if !defined(TARGET_STM32F303K8) #error This code is only for NUCLEO-F303K8 or STM32F303K8T6. Reselect a Platform. #endif #define DEBUG //debug ON //#undef DEBUG //debug OFF #include "dbg.h" #include "mbed.h" //#include <CANformat.h> #include "PID.h" #include "QEI.h" #define Kp2 0.9f #define Ki2 0.0f #define Kd2 0.004f #define ANGLE_THRESHOLD 2.0f //under 2.0 degree is decreaced #define Kp 0.4f #define Ki 0.8f #define Kd 0.0f #define THETA_FEEDBACK 1 #define OMEGA_FEEDBACK 2 #define CURRENT_FEEDBACK 3 #define SERIAL_BAUDLATE 115200 #define ENCODER_PULSE_PER_REVOLUTION 4000 //(1000*4) #define CURRENT_SENSOR_OFFSET_COUNT 1000 #define M_PI 3.141593f #define ABLE 0 #define DISABLE 1 #define CW 1 #define CCW 0 #define MOTOR_FREUENCY 10000 //[Hz] #define CAN_FREQUENCY 500 //[kHz] #define MOTOR_TARGET_OMEGA_THRESHOLD 0.5f //[deg/s] #define MOTOR_TARGET_THETA_THRESHOLD 0.5f //[rad] #define MOTOR_TARGET_CURRENT_THRESHOLD 0.1f //[A] #define uniqueSerialAdd_kl_freescale (unsigned long *)0x40048058 #define uniqueSerialAddr_stm32 (unsigned long *)0x1FFFF7E8 #define uniqueSerialAddr uniqueSerialAddr_stm32 //Controll Period #define PID_CONTROLL_PERIOD 10 //[ms] #define ENCODER_CONTROLL_PERIOD 8 //[ms] //current sensor define #define CURRENT_SENSOR_VCC 3.3 //[V] #define CURRENT_SENSOR_RANGE 105 //[mv/A] #define CURRENT_LIMIT 1.5 //[A] //turning by toukai robocon #define MOTOR1_ID 393246 #define MOTOR2_ID -2147418100 #define MOTOR3_ID 196631 #define MOTOR4_ID 0000000004 #define MOTOR5_ID 0000000005 #define MOTOR6_ID 0000000006 /* readしたらCAN.readDATAを0にする read時は割込み禁止で,CAN timeoutの割込みで0のままなら,すべて停止 */ /* #define CAN_TIMEOUT 3000 //[ms] #define ENCODER_PPR 100.0f */ //standerd define Serial pc(PB_6, PB_7, SERIAL_BAUDLATE); DigitalOut myled(LED1); //PB_3 DigitalIn userSW(PB_5); //motor define PwmOut PWM1(PA_4); PwmOut PWM2(PA_6); PwmOut PHASE(PB_0); DigitalOut SR(PA_7); PID thetaPID(Kp, Ki, Kd, PID_CONTROLL_PERIOD*0.001f); PID omegaPID(Kp2, Ki2, Kd2, PID_CONTROLL_PERIOD*0.001f); QEI encoder(PA_3, PA_1, PA_0, ENCODER_PULSE_PER_REVOLUTION, QEI::X4_ENCODING); AnalogIn Current(PB_1); //CAN define CAN can(PA_11, PA_12); //RD(PA_11,D10),TD(PA_12,D2) CANMessage msg; struct CANdata { //int timeout; int deviceID; char recvDATA[8]; char sendDATA[8]; }; struct CANdata MDCAN; void CANsend(); //void CANtimeout(); //void CANtest(); /* int mode : select THETA_FEEDBACK(=1) or OMEGA_FEEDBACK(=2) double target : enter the input value corresponding to the mode */ void MotorDrive(int mode, double target); //void CurrentFunction(); InterruptIn userButton(PB_5); Ticker Encoder; Timer ENCODER_timer; struct state { double ThetaTarget; double OmegaTarget; double CurrentTarget; double theta; double omega; double current; }; struct state Motor; //Current Sensor setup double current_bias = 0.0; void Velocity(); void ButtonFunction(); void SerialFunction(); void CANread(); void CurrentFunction(); // 電流センサーをタイマー割込みにして,規定量を超えたら角速度の目標値を0にする. int main() { // MPU setup DBG("MotorDriver_ver4.1 start! \r\n"); DBG("/** MPU INFO **/ \r\n"); DBG("Target PlatForm: STM32F303K8T6 \r\n"); DBG("System Frequency: %d Hz \r\n", SystemCoreClock); DBG("Program File: %s \r\n", __FILE__); DBG("Compile Date: %s \r\n", __DATE__); DBG("Compile Time: %s \r\n", __TIME__); //Unique ID starting addresses for most of STM32 microcontrollers. unsigned long *id = (unsigned long *)0x1FFFF7AC; DBG("%d, %d, %d \r\n", id[0], id[1], id[2]); switch (id[0]) { case MOTOR1_ID: MDCAN.deviceID = 11; DBG("\r\n ==MOTOR1== \r\n\r\n"); break; case MOTOR2_ID: MDCAN.deviceID = 12; DBG("\r\n ==MOTOR2== \r\n\r\n"); break; case MOTOR3_ID: MDCAN.deviceID = 13; DBG("\r\n ==MOTOR3== \r\n\r\n"); break; case MOTOR4_ID: MDCAN.deviceID = 14; DBG("\r\n ==MOTOR4== \r\n\r\n"); break; case MOTOR5_ID: MDCAN.deviceID = 15; DBG("\r\n ==MOTOR5== \r\n\r\n"); break; case MOTOR6_ID: MDCAN.deviceID = 16; DBG("\r\n ==MOTOR6== \r\n\r\n"); break; default: while(1) { DBG("DeviceID none define\r\n"); } } DBG("deviceID : %d \r\n", MDCAN.deviceID); myled = 1; wait(1.0); myled = 0; // CAN setup can.frequency(500000); //ROS default bps:100kbps -> 500kbps can.mode(CAN::Normal); //can.filter(MDCAN.deviceID, 0x0000000F, CANStandard, 14); //can.reset(); //delete receive buffer // motor signal setup PWM1.period(1.0f/MOTOR_FREUENCY); PWM2.period(1.0f/MOTOR_FREUENCY); PHASE.period(1.0f/MOTOR_FREUENCY); PWM1 = 0.0f; PWM2 = 0.0f; PHASE = 0.0f; SR = 0; // PID position setup omegaPID.setInputLimits(-360.0f, 360.0f); omegaPID.setOutputLimits(-3.68f, 3.68f); omegaPID.setBias(0.0); omegaPID.setMode(AUTO_MODE); // PID omega setup thetaPID.setInputLimits(0.0f, 3.68f); thetaPID.setOutputLimits(0.0f, 1.0f); thetaPID.setBias(0.0); thetaPID.setMode(AUTO_MODE); // current sensor setup for(int i = 0 ; i < CURRENT_SENSOR_OFFSET_COUNT ; i++) { current_bias += Current.read(); } current_bias = current_bias/(double)CURRENT_SENSOR_OFFSET_COUNT; DBG("current_bias_average: %f \r\n", current_bias); // interrupt start function userButton.mode(PullUp); userButton.fall(ButtonFunction); pc.attach(SerialFunction, Serial::RxIrq); can.attach(CANread, CAN::RxIrq); ENCODER_timer.start(); Encoder.attach(Velocity, ENCODER_CONTROLL_PERIOD*0.001f); //Measure rotation speed every 30ms //NVIC_SetPriority(TIMER3_IRQn, 1); //initial target Motor.ThetaTarget = 90.0f; //initial theta[deg] Motor.OmegaTarget = 0.0f; //initial omega[rps] while(1) { //Motor.current = ((Current.read()-current_bias)*((float)CURRENT_SENSOR_VCC/2.0f))/(CURRENT_SENSOR_RANGE*0.001f); //DBG("omegaPID.compute : %6.3f thetaPID.compute : %6.3f theta : %6.3f omega[rps] :%6.3f \r\n", omegaPID.compute(), thetaPID.compute(), Motor.theta, Motor.omega); pc.printf("%6.3f,%6.3f\r\n", Motor.theta, Motor.omega); //MotorDrive(THETA_FEEDBACK, Motor.ThetaTarget); MotorDrive(OMEGA_FEEDBACK, Motor.OmegaTarget); //DBG("current:%f omega:%f pwm:%f \r\n",current.read(), omega ,thetaPID.compute()); //CurrentFunction(); } } void SerialFunction() { if(pc.readable() == 1) { char data = pc.getc(); DBG("get data:%c \r\n", data); switch (data) { case 'q': Motor.OmegaTarget += 0.5f; DBG("get 'q' \r\n"); break; case 'a': Motor.OmegaTarget -= 0.5f; DBG("get 'a' \r\n"); break; case 'p': Motor.ThetaTarget += 30.0f; DBG("get 'p' \r\n"); break; case 'l': Motor.ThetaTarget -= 30.0f; DBG("get 'l' \r\n"); break; case 'r': DBG("\r\nSystem Reset! \r\n"); NVIC_SystemReset(); break; default: DBG("The others key recieve \r\n"); break; } } } void ButtonFunction() { Motor.OmegaTarget = 0.0f; //motor stop DBG("setup open \r\n"); DBG("1:change the CAN device ID \r\n"); DBG("2:change the CAN device ID \r\n"); //CANtest(); //CAN通信のテスト信号を簡易的に出す関数(通常は使わない)303K8_sendと同じ機能 //ユーザーボタンを押したらモータを台形加速させるプログラムを作る while(1) { if(!pc.readable()) { // pc.scanf("%d", cmd); } else { DBG("old device ID:\r\n"); DBG("new device ID:\r\n"); while(1) { } } } } void CANread() { //__disable_irq(); //All Interrupt disabled //Encoder.detach(); if(can.read(msg) != 0 && MDCAN.deviceID == msg.id && msg.format == 0) { DBG("CAN recieve \r\n"); myled = !myled; DBG("recv ID : %d", msg.id); for(int i = 0 ; i < 8 ; i++) { MDCAN.recvDATA[i] = msg.data[i]; DBG("[%d] ", MDCAN.recvDATA[i]); } DBG("\r\n"); switch(MDCAN.recvDATA[0]) //check PID { case THETA_FEEDBACK: //MD data send /* int sum = 0; for(int i = 0 ; i < 8 ; i++) { sum += MDCAN.recvDATA[i]; } */ //現在の分解能は 1 deg Motor.ThetaTarget = (double)MDCAN.recvDATA[1]; DBG("theta FB can \r\n"); break; case OMEGA_FEEDBACK: //現在の分解能は 1 rps Motor.OmegaTarget = (double)MDCAN.recvDATA[1]; DBG("omega FB can \r\n"); break; default: DBG("recv CAN PID error \r\n"); break; } } //thetaPID.setSetPoint((double)MDCAN.recvDATA[1]); //motor target value velocity //__enable_irq(); //Encoder.attach(Velocity, ENCODER_CONTROLL_PERIOD); //Measure rotation speed every 10ms //pc.attach(SerialFunction, Serial::RxIrq); //userButton.fall(ButtonFunction); //can.attach(CANread, CAN::RxIrq); } void Velocity() { static int pulse; //old pulse Motor.omega = ((encoder.getPulses() - pulse)/(double)ENCODER_PULSE_PER_REVOLUTION)/ENCODER_timer.read(); pulse = encoder.getPulses(); //pulse update Motor.theta = (encoder.getPulses()/(double)ENCODER_PULSE_PER_REVOLUTION)*360.0f; ENCODER_timer.reset(); //timer reset:t=0s /* CANsend(OMEGA_FEEDBACK, ); MDCAN.sendDATA[0] = OMEGA_FEEDBACK; MDCAN.sendDATA[1] = ; MDCAN.sendDATA[2] = ; MDCAN.sendDATA[3] = ; MDCAN.sendDATA[4] = ; MDCAN.sendDATA[5] = ; MDCAN.sendDATA[6] = ; MDCAN.sendDATA[7] = ; */ //DBG("omega[rps]:%lf pulse:%d \r\n", Motor.omega, pulse); } void CANsend(int id, int PID) { //__disable_irq(); //Encoder.detach(); //data -> Can data //余りを計算して,端数は削除するとCANデータに変換できる if(!can.write(CANMessage(id, MDCAN.sendDATA, 8, CANData, CANExtended))) { DBG("CAN send error! \r\n"); } //__enable_irq(); //Encoder.attach(Velocity, ENCODER_CONTROLL_PERIOD); //Measure rotation speed every 30ms //pc.attach(SerialFunction, Serial::RxIrq); //userButton.fall(ButtonFunction); //can.attach(CANread, CAN::RxIrq); } void CurrentFunction() { } void MotorDrive(int mode, double target) { //theta feebback(mode 1) //omega feedback(mode 2) //current feedback(mode 3) //static int old_direction; static double old_target; switch (mode) { case THETA_FEEDBACK: DBG("THETA_FEEDBACK!! \r\n"); omegaPID.setSetPoint(target); omegaPID.setProcessValue(Motor.theta); if(target >= 0) { if(abs(Motor.omega) < 0.01f && abs(Motor.theta - target) > ANGLE_THRESHOLD) //モータの回転が停止中 { SR = ABLE; PHASE = (float)CW; PWM1 = 0.3f; PWM2 = 1.0f; } if(abs(Motor.theta - target) > ANGLE_THRESHOLD)//モータ回転中で誤差が0.1以上 { MotorDrive(OMEGA_FEEDBACK, omegaPID.compute()); } else //誤差の範囲内ならモータのPWMを停止させる { MotorDrive(OMEGA_FEEDBACK, 0.0); PWM1 = 0.0f; PWM2 = 1.0f; } } else { if(abs(Motor.omega) < 0.01f && abs(Motor.theta - target) > ANGLE_THRESHOLD) //モータの回転が停止時 { SR = ABLE; PHASE = (float)CCW; PWM1 = 0.3f; PWM2 = 1.0f; } if(abs(Motor.theta - target) > ANGLE_THRESHOLD)//モータ回転中で誤差が0.1以上 { MotorDrive(OMEGA_FEEDBACK, omegaPID.compute()); } else //誤差の範囲内ならモータのPWMを停止させる { MotorDrive(OMEGA_FEEDBACK, 0.0); PWM1 = 0.0f; PWM2 = 1.0f; } } break; case OMEGA_FEEDBACK: /* if(direction != old_direction) //Prevent sudden turn { thetaPID.setProcessValue(0.0f); if(thetaPID.compute() < 0.05f) { old_direction = direction; } } */ DBG("OMEGA_FEEDBACK "); if(target >= 0.0f) { thetaPID.setSetPoint(abs(target)); thetaPID.setProcessValue(abs(Motor.omega)); //ここにtargetの正負によって回転方向を帰るプログラムを書く SR = ABLE; PHASE = (float)CW; PWM1 = thetaPID.compute(); PWM2 = 1.0f; //DBG("%f \r\n", (float)direction); } else { thetaPID.setSetPoint(abs(target)); thetaPID.setProcessValue(abs(Motor.omega)); //ここにtargetの正負によって回転方向を帰るプログラムを書く SR = ABLE; PHASE = (float)CCW; PWM1 = thetaPID.compute(); PWM2 = 1.0f; } break; case CURRENT_FEEDBACK: DBG("CURRENT_FEEDBACK UNSELECTABLE!! \r\n"); break; default: DBG("Function:MotorDriver mode error! \r\n"); break; } //old_direction = direction; old_target = target; }