佐賀大deラボ機
Dependencies: mbed MAIDROBO BNO055_fusion Ping
main.cpp
- Committer:
- tajiri1999
- Date:
- 2020-07-28
- Revision:
- 1:0d7cc59d485e
- Parent:
- 0:6c2e6a485519
File content as of revision 1:0d7cc59d485e:
#include "mbed.h" #include "BNO055.h" #include "Ping.h" #include "Motor.h" //constant #define GK 1 #define FW 0 #define PI 3.1415926 /*調整するときに読んでね*/ /*ロボットを調整する項目は3つあります * 以下の調整は試合前に必ずに行ってください * * 一つ目はラインセンサの調整です * ロボットを起動し、USBケーブルを挿しこんだ後ターミナルを起動して'd'を押してください * 各種センサ値が表示されます * コートの緑のマット上でLine_front ~ Line_rightの数値が * 75前後 になるように、 * ロボットの底面についている可変抵抗(青いやつ)を小さいドライバーで回して調整してください * 可変抵抗を時計回りに回すと数値が上がります * キーボード'r'でセンサの値の取得を一時停止することができます * * 2つ目は回り込み半径の調整です * Rを調整することでボールに対してどれくらいの半径を描いて回り込むか調整することができます * 上記と同様にターミナルソフトを起動してキーボード'd'を押すと * 現在のセンサの数値を見ることができますのでBall distance: * の数値を参考にして最適なRを決定してください(distanceの数値=Rにする) * キーボード'r'でセンサの値の取得を一時停止することができます * * 3つ目はスピードとPIDゲイン値の調整です * まずラインから出ないようにspeedを調整します * そのあとロボットが常に前に向くように(傾いても元の方向にすぐに戻るように) * tp ti tdを調整してください * P→D→I(またはP→I→D)の順番に調整すると良いかもしれません * * 以上のことが完了してからプログラムの作成を開始してください */ /*BNO055モードについて * ジャイロセンサーには様々なモードがありますが * 基本的にNDOFモードを使って下さい * 会場の磁場環境がひどい場合はIMUモードを使ってください * ロボットの電源を起動した直後にキャリブレーションを行ってください * キャリブレーションをすることでオウンゴールを防ぐことができます。 */ /*change Mode IMU,COMPASS,M4G,NDOF_FMC_OFF,NDOF*/ //パラメータ調整項目 #define MODE MODE_NDOF//ジャイロのモード const int mode_robot = FW;//FW:その場で停止 GK:ゴール前までもどる const int R = 100; //ロボット回り込み半径(0~255 const int speed = 100; //(0~100)の間で調整 const double tp = 1.5; //比例ゲイン const double ti = 1.5; //積分ゲイン const double td = 0.25; //微分ゲイン //TerminalDisplay(TeraTerm) Serial pc(SERIAL_TX, SERIAL_RX); //voltage AnalogIn voltage(PC_4); //UltraSonicSensor DigitalIn change(PA_6); Ping uss_left(PB_3); Ping uss_right(PA_7); Ping uss_back(PB_6); Timer timer_USS; Timer timer_volt; //BallSensor&Linesensor AnalogIn ball_degree(PA_4); AnalogIn ball_distance(PB_0); Serial sensor(PC_10, PC_11, 115200); //Motor Motor motor(PA_8,PB_4,PB_10, PB_15,PB_14,PB_13, PB_2,PB_1,PA_9, PA_12,PA_11,PA_5); //Timer Timer timer_PID; //GyroSensor I2C i2c(D14, D15); BNO055 imu(i2c, PC_8); BNO055_ID_INF_TypeDef bno055_id_inf; BNO055_EULER_TypeDef euler_angles; //HoldCheckSensor DigitalIn hold_check(PC_3); //kicker DigitalOut kick(PA_13); //ToggleSwitch DigitalIn sw_start(PD_2); //program start switch DigitalIn sw_reset(PC_12); //gyro sensor reset switch DigitalIn sw_kick(USER_BUTTON); //declear prototype (function list) void uss_send_and_read(); //超音波センサ読み込み int PID(double kp, double ki, double kd, int target, int degree, int reset = 0); //姿勢制御のPIDで計算する int get_ball_degree(int A = 0); //ボールの角度を-180~180(ボールなし999)で取得する int get_ball_distance(int A = 0); //ボールの距離を0~255で取得する int get_line_degree(); //反応したラインの方向(角度)を0~315(反応なし999)で取得する int turn(int degree, int distance, int target = 0, int angle = 0); //回り込みの進行方向を計算する int get_uss_range(char c); //超音波センサの距離を取得する 引数('l','r','b')を代入するとその方向の値が得られる bool check_voltage(); //電圧をチェックする /*****************************************************************/ /**********************main function******************************/ /*****************************************************************/ int main() { //while(imu.chip_ready() == 0);//imu set //**************************************************************// ////////////////////////initialize setting//////////////////////// //**************************************************************// wait_ms(50); kick = 0; //キックを解除する /*ultra sonic sensor set speed*/ uss_right.Set_Speed_of_Sound(32); //(cm/ms) uss_left.Set_Speed_of_Sound(32); //(cm/ms) uss_back.Set_Speed_of_Sound(32); //(cm/ms) /*motor pwm frequency set*/ motor.setPwmPeriod(0.00005); /*Variable*/ int rotation, move, distance, degree, direction; uint8_t config_profile[24]; motor.omniWheels(0, 0, 0); /*gyro sensor */ imu.reset(); wait_ms(100); imu.change_fusion_mode(MODE); wait_ms(200); imu.get_Euler_Angles(&euler_angles); int init_degree = (int) euler_angles.h; while (1) { //***************************************************************// ////////////////Play mode//////////// //***************************************************************// timer_USS.reset(); timer_USS.start(); timer_PID.reset(); timer_PID.start(); timer_volt.reset(); timer_volt.start(); PID(tp, ti, td, init_degree, init_degree, 1); //PIDの積分値をリセットする while (sw_start == 0 && check_voltage() == 1) { imu.get_Euler_Angles(&euler_angles); //ジャイロの値をしゅとくする rotation = PID(tp, ti, td, init_degree, (int) euler_angles.h); //PIDを計算する /////////////////////// ////*ラインの制御*/////// //////////////////// direction = get_line_degree(); if (direction != 999) { int tmp = direction; /*ラインが反応したときコートの中に戻る制御プログラム*/ while (direction != 999 && sw_start == 0 && check_voltage() == 1) { imu.get_Euler_Angles(&euler_angles); rotation = PID(tp, ti, td, init_degree, (int) euler_angles.h, 0); tmp = direction; //踏んだラインの方向を保存 if (direction == 90) { if (get_uss_range('b') >= 50) { direction = 240; } } else if (direction == 270) { if (get_uss_range('b') >= 50) { direction = 120; } } else if (direction <= 179) { direction = direction + 180; } else if (direction >= 180) { direction = direction - 180; } motor.omniWheels(direction, speed, rotation); direction = get_line_degree(); } /*コートの中に戻った後、ボールが移動するまでその場で待機する*/ if (tmp >= 180) { tmp = tmp - 360; } degree = get_ball_degree(); while ((degree > (tmp - 45)) && (degree < (tmp + 45)) && sw_start == 0) { imu.get_Euler_Angles(&euler_angles); rotation = PID(tp, ti, td, init_degree, (int) euler_angles.h); motor.omniWheels(0, 0, rotation); degree = get_ball_degree(); } } //////////////////////////////////// /*ボールを追いかけるときの制御プログラム*/////// /////////////////////////////////// else { int P = (int) euler_angles.h - init_degree; //proportional if (P <= -180) { //convert to -180 ~ 180 P = P + 360; } if (P >= 180) { P = P - 360; } /*ロボット向き修正*/ if (P <= -35) { motor.setPower(60, 60, 60,60); } else if (P >= 35) { motor.setPower(-60, -60, -60,-60); } else { /*ボールの角度と距離を取得する*/ degree = get_ball_degree(); distance = get_ball_distance(); //////////////////// /*ボールが見つからないとき*/ //////////////////// if (distance >= 240) { /* * GKモードの場合、ゴール前まで戻ってくる * FWモードの場合、その場で停止する * */ switch (mode_robot) { case GK: { //GKモード int uss_l = get_uss_range('l'), uss_r = get_uss_range('r'); //超音波センサの値を取得する static float Vx = 0, Vy = 0; Vy = get_uss_range('b') - 45; //y座標を取る if ((uss_l + uss_r) > 120) { //ロボットが他のロボットと干渉していないときx座標を取る Vx = 182 * (float) uss_l / (float) (uss_l + uss_r) - 91; } else { Vx = 0; //x軸上で座標0にいる判定にする } if (Vy >= 0) { //ゴールの前にいないとき int direction_of_going = (int) ((180.0 / PI) * acos(Vy / sqrt(Vx * Vx + Vy * Vy))); //進行方向を計算する if (Vx >= 20) { //右側にいるとき direction_of_going = 180 + direction_of_going; motor.omniWheels(direction_of_going, speed / 2, rotation); //左後ろに移動 } else if (Vx <= -20) { //左側にいるとき direction_of_going = 180 - direction_of_going; motor.omniWheels(direction_of_going, speed / 2, rotation); //右後ろに移動 } else { //真ん中付近にいるとき motor.omniWheels(180, speed / 2, rotation); //後ろへ移動 } } else { //ゴールの前にいるとき if (Vx > 30) { //右側にいるとき motor.omniWheels(-90, speed / 2, rotation); //左へ移動 } else if (Vx < -30) { //左側にいるとき motor.omniWheels(90, speed / 2, rotation); //右へ移動 } else { //真ん中にいるとき motor.omniWheels(0, 0, rotation); //停止 } } break; } case FW: //FWモード motor.omniWheels(0, 0, rotation); //ボールがないとき停止する break; default: motor.omniWheels(0, 0, rotation); //ボールがないとき停止する break; } } ///////////////// /*ボールが見つかった時*/ ///////////////// else { /*ホールドセンサーが反応したとき*/ static int count = 0; //タイマー代わりのカウンター move = turn(degree, distance); //回り込み方向を計算する motor.omniWheels(move, speed, rotation); if (hold_check.read() == 0) { //0 if (count > 50) { //ホールドしてから0.1秒過ぎたとき時 kick = 1; //キックする wait_ms(50); //キック待機時間 kick = 0; //キックを解除する count = 0; //カウンタを0にする } else { wait_us(1); //マイクロ秒 count++; } } else { count = 0; } } } } } timer_PID.stop(); //***************************************************************// ////////////////////////Gyro reset mode//////////////////////////// //***************************************************************// if (sw_reset == 0) { imu.get_Euler_Angles(&euler_angles); init_degree = (int) euler_angles.h; } motor.omniWheels(0, 0, 0); //***************************************************************// //////////////////////////debug mode/////////////////////////////// //***************************************************************// if (pc.readable() > 0) { char command = pc.getc(); //if 'c' is pressed,calibration mode will start. if (command == 'c') { while (1) { if (pc.readable() > 0) { if (pc.getc() == 'r') { break; } } uint8_t status = 0, temp[3]; status = imu.read_calib_status(); temp[0] = status << 2; temp[1] = status << 4; temp[2] = status << 6; //pc.printf("%d\r\n",status); pc.printf("SYS:%d, GYRO:%d, ACC:%d, MAG:%d \r", status >> 6, temp[0] >> 6, temp[1] >> 6, temp[2] >> 6); //キャリブレーションプロファイル読みとり if (temp[2] >> 6 == 3 && temp[0] >> 6 == 3 && status >> 6 == 3) { imu.change_fusion_mode(CONFIGMODE); for (int i = 0; i < 22; i++) { config_profile[i] = imu.read_reg0(i + 0x55); } kick = 1; wait_ms(200); kick = 0; break; } wait_ms(100); } imu.change_fusion_mode(MODE); } //if 'd' is pressed,debug mode will start. if (command == 'd') { while (1) { if (pc.readable() > 0) { if (pc.getc() == 'r') { //if 'r' is pressed,debug mode will be end. break; } } pc.printf( "////////////////////////////////////////////\r\n"); pc.printf( "/*******************debug******************/\r\n"); pc.printf( "////////////////////////////////////////////\r\n\n"); imu.get_Euler_Angles(&euler_angles); pc.printf("Gyro degree: %d \r\n\n", (int) euler_angles.h); pc.printf("Ball degree: %d \r\n", get_ball_degree()); pc.printf("Ball distance: %d \r\n\n", get_ball_distance()); pc.printf("Line sensor: %d \r\n", get_line_degree()); pc.printf("Hold sensor: %d \r\n\n", hold_check.read()); pc.printf("USS left: %d cm\r\n", get_uss_range('l')); pc.printf("USS right: %d cm\r\n", get_uss_range('r')); pc.printf("USS back: %d cm\r\n\n", get_uss_range('b')); sensor.putc('D'); //request Line data while (sensor.readable() == 0) ; int f = sensor.getc(); while (sensor.readable() == 0) ; int b = sensor.getc(); while (sensor.readable() == 0) ; int r = sensor.getc(); while (sensor.readable() == 0) ; int l = sensor.getc(); pc.printf("Line front: %d\r\n", f); pc.printf("Line back: %d\r\n", b); pc.printf("Line left: %d\r\n", l); pc.printf("Line right: %d\r\n\n", r); pc.printf("batey voltage: %f\r\n", voltage.read() * 10.2);//8.15 pc.printf("\f"); wait_ms(300); } } } //****************************************************************// //////////////////////calibration mode///////////////////////////////////// //****************************************************************// if (sw_kick.read() != 1) { uint8_t status = 0, temp[3] = { 0, 0, 0 }; /*ジャイロキャリブレーション*/ while (1) { status = imu.read_calib_status(); temp[0] = status << 2; if (temp[0] >> 6 == 3) { kick = 1; wait_ms(200); kick = 0; break; } } /*コンパスキャリブレーション*/ /*(IMUモードの時は必要ないのでコメントアウトして)*/ while (MODE == MODE_NDOF) { status = imu.read_calib_status(); temp[2] = status << 6; pc.printf("%d\r",temp[2]>>6); if (temp[2]>>6 == 3 && status >>6 == 3) { break; } } /*キャリブレーションプロファイル取得*/ imu.change_fusion_mode(CONFIGMODE); wait_ms(100); for (int i = 0; i < 22; i++) { config_profile[i] = imu.read_reg0(i + 0x55); } imu.change_fusion_mode(MODE); wait_ms(200); kick = 1; wait_ms(200); kick = 0; } /**********************end main function**************************/ } } ///////////////////////////////////////////////////////// /*PID feedback*/ ////////////////////////////////////////////////////////// int PID(double kp, double ki, double kd, int target, int degree, int reset) { static double dt, time, pretime; static double P = 0, OP = 0, I = 0, D = 0, re = 0; if (reset == 1) { I = 0; time = 0; pretime = 0; } time = timer_PID.read(); dt = time - pretime; //time P = degree - target; //proportional if (P <= -180) { //convert to -180 ~ 180 P = P + 360; } if (P >= 180) { P = P - 360; } D = (P - OP) / dt; //differential I += (P + OP) * dt / 2; //Integral pretime = time; OP = P; re = -1 * (kp * P + ki * I + kd * D); if (P >= -5 && P <= 5) { I = 0; } if (re >= 60) { re = 60; } else if (re <= -60) { re = -60; } return re; } ///////////////////////////////////// /*turn control*/ //////////////////////////////////// int turn(int degree, int distance, int target, int angle) { int going; angle = angle - target; //proportional if (angle <= -180) { //convert to -180 ~ 180 angle = angle + 360; } if (angle >= 180) { angle = angle - 360; } degree = degree + angle; //ジャイロの値を加算 if (degree >= -45 && degree <= 45) { //ボールがまえにあるとき going = 3 * degree; } else if (degree <= -160 && degree >= 160) { //ボールが真後ろにある時 if (degree >= 0) { going = degree + 90; } else { going = degree - 90; } } else { if (distance <= R) { //ボールとの距離が近すぎるとき if (degree >= 0) { going = degree + 90; //(int)(180.0 - ((180.0/PI)*asin((double)distance / (double)R))); } else { going = degree - 90; //(int)(180.0 - ((180.0/PI)*asin((double)distance / (double)R))); } } else { //ボールとの距離が離れているとき if (degree >= 0) { going = degree + (int) ((180.0 / PI) * asin((double) R / (double) distance)); } else { going = degree - (int) ((180.0 / PI) * asin((double) R / (double) distance)); } } } if (get_uss_range('b') <= 60) { //後ろの壁に近いとき if (going > 90) { going = 90; } else if (going < -90) { going = -90; } } return going - angle; } ////////////////////////////// /*get ball degree*/ ////////////////////////////// int get_ball_degree(int A) { static int value = 0; if (A == 1) { //analog read if (ball_distance.read() >= (double) 0.95) { return 999; } else { return (int) (ball_degree.read() * 360 - 185); } } else { //serial read /*get ball degree*/ int count = 0; sensor.putc('B'); char buffer[2]; for (int i = 0; i < 2; i++) { while (sensor.readable() == 0) { //wait if (count >= 10000) { return value; } wait_us(1); count++; } buffer[i] = sensor.getc(); //get } value = ((uint16_t) buffer[0] << 8) + (uint8_t) buffer[1] - 180; return value; } } ////////////////////////////// /*get ball distance*/ ////////////////////////////// int get_ball_distance(int A) { static int value = 0; if (A == 1) { //analog read return (uint8_t) (ball_distance.read() * 255); } else { //serial read int count = 0; /*get ball distance*/ sensor.putc('C'); while (sensor.readable() == 0) { //wait if (count >= 10000) { return value; } wait_us(1); count++; } value = (int) sensor.getc(); //get return value; } } ////////////////////////////// /*get line degree*/ ////////////////////////////// int get_line_degree() { static char value = 0; static int degree = 0; int count = 0; /*get line value*/ sensor.putc('A'); while (sensor.readable() == 0) { //wait if (count >= 10000) { return degree; } wait_us(1); count++; } value = sensor.getc(); //get switch (value) { case 'N': degree = 999; return 999; //break; case '0': degree = 0; return 0; //break; case '1': degree = 45; return 45; //break; case '2': degree = 90; return 90; //break; case '3': degree = 135; return 135; //break; case '4': degree = 180; return 180; //break; case '5': degree = 225; return 225; //break; case '6': degree = 270; return 270; //break; case '7': degree = 315; return 315; //break; default: degree = 999; return 999; //break; } } ////////////////////////////////////// /*USS reading*/ ////////////////////////////////////// ///////////////////////////// /*get uss range cm*/ //////////////////////////// int get_uss_range(char c) { static int count = 0; static int l = 0, r = 0, b = 0; if (timer_USS.read() >= 0.04) { l = uss_left.Read_cm(); r = uss_right.Read_cm(); b = uss_back.Read_cm(); uss_left.Send(); uss_right.Send(); uss_back.Send(); timer_USS.reset(); } if (c == 'l' || c == 'L') { return l; } else if (c == 'r' || c == 'R') { return r; } else if (c == 'b' || c == 'B') { return b; } else { return 999; } } ////////////////////////////////////// /*Battery check voltage*/ ////////////////////////////////////// bool check_voltage() { static float sum[5] = { 8, 8, 8, 8, 8 }; static float Ave = 8; static bool S = 1; if (timer_volt.read() > 0.2) { for (int i = 4; i > 0; i--) { sum[i] = sum[i - 1]; } sum[0] = voltage.read() * 9.9; Ave = (sum[0] + sum[1] + sum[2] + sum[3] + sum[4]) / 5; if (Ave < 7.0) { //7.0V以下で自動遮断 S = 0; } else { S = 1; } timer_volt.reset(); } return S; }