Ver Tikutaikai

Dependencies:   mbed SBDBT BNO055 YKNCT_MD YKNCT_I2C

Revision:
0:c527a14d991a
Child:
1:fa3652aca312
diff -r 000000000000 -r c527a14d991a main.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Tue Oct 12 03:57:59 2021 +0000
@@ -0,0 +1,763 @@
+/* Includes -----------------------------------------------------------------*/
+#include <main.h>
+#include <target.h>
+
+/* 型定義
+ * ---------------------------------------------------------------------*/
+
+/* ロボットの加速度 */
+LOCATION NowAcc;
+/* ロボットの座標 */
+LOCATION NowLoc;
+
+/* 定数定義
+ * -------------------------------------------------------------------*/
+
+
+/* 関数プロトタイプ宣言
+ * --------------------------------------------------------*/
+void uartRX(void);
+
+void IT_CallBack(void);
+
+/* PID */
+double LocationPID(double, double);
+
+/* 値制限 */
+double Range(double, double);
+
+/* 角速度算出関数 */
+int RotationPerWhile(int, int, int);
+
+/* 足回り */
+void InverseTriOmni(int *, double, double, double);
+
+/* 自己位置推定関数 */
+void LocEstimate(void);
+
+/* 変数定義
+ * -------------------------------------------------------------------*/
+
+/* 足回り値保存変数 */
+int MovMotor[MotorMAX] = {0};
+
+/* 周期保存用 */
+int whileTime_us = 0;
+
+/* コントローラ */
+int operate = 0;
+
+/* ジャグリング機構 動かすか否か */
+int isMoveInjection = 0;
+
+/* リボン回転機構 動かすか否か */
+int isMoveRibbon = 0;
+
+/* 足回りエンコーダー */
+double EncoderDeg[EncoderMAX - 6] = {0};
+
+/* 手動オムニ目標角 */
+double TarTheta = 0;
+
+/* 目標番号 */
+int targetNum = 0;
+
+/* タイマー関連 */
+/* 自身の状況 1-warning 2-normal */
+int statious = 2;
+
+/* 時間[ms] */
+double msTimer = 0;
+
+/* メインタイマー制御 0-pause 1-start */
+bool timerMode = 0;
+
+/* クラス定義 ----------------------------------------------------------------*/
+
+/* 割り込み用クラス */
+Ticker flipper;
+
+/* UART (Tx,Rx) */
+Serial pc(USBTX, USBRX, 115200);
+
+/* XBee送受信 */
+Serial UART(PC_10, PC_11, 9600);
+
+/* コントローラー */
+SBDBT DS3(PA_0, PA_1, 9600);
+
+/* I2C MDのクラス定義 */
+YKNCT_MD_I2C MD(PB_9, PB_8);
+
+/* ジャイロのピン設定 */
+BNO055 bno(PB_9, PB_8);
+
+
+DigitalOut led(LED2);
+/* リボン射出ピン */
+DigitalOut Rod(PC_2);
+
+/* エンコーダーピン設定 */
+DigitalOut CS(PB_4);
+DigitalOut CL(PA_10);
+DigitalIn Do[EncoderMAX] = {PB_12, PB_2,  PB_1, PB_15, PB_14, PB_13, PC_4, PB_5};
+//DigitalIn Do[EncoderMAX] = {PB_15, PB_14, PB_13, PB_12, PB_2, PB_1, PC_4, PB_5};
+
+/* カタパルト二台分の領域確保 */
+INJECTION injection[2] = {
+    INJECTION(PB_10, PA_8, PA_9),
+    INJECTION(PA_4, PB_0, PC_1)
+};  // pa_6,pa_7
+//PA_8,PB_0
+
+DigitalIn limitRibbonTop(PC_0);
+DigitalIn limitRibbonBottom(PC_3);
+
+Timer WaitNextShot;
+
+/*----------------------------------- main ----------------------------------*/
+int main()
+{
+    /* I2CMDの設定 */
+    for (int i = 0; i < MotorMAX; i++) MD.Init(i, MD_SMB);
+    for (int i = 0; i < EncoderMAX; i++) Do[i].mode(PullDown);
+
+    /* 割り込みの設定
+     * IT_CallBack関数を0.1msで割り込み */
+    flipper.attach_us(&IT_CallBack, 100);
+
+    /* uart受信割込み設定 */
+    UART.attach(uartRX, Serial::RxIrq);
+
+    /* ジャイロの設定 */
+    wait(0.5);
+    bno.setmode(OPERATION_MODE_IMUPLUS);
+
+    /* 動作時間計測用 */
+    Timer whileTimer;
+    Timer yawCnt;
+
+    /* 計測開始 */
+    whileTimer.start();
+    yawCnt.start();
+    WaitNextShot.start();
+
+    /* 小さくすると深くなる */
+    injection[0].initalDeg = 29;
+    injection[1].initalDeg = 52;
+
+    injection[0].targetRps[0] = -28;
+    injection[0].targetRps[1] = 28;
+    injection[1].targetRps[0] = -25;
+    injection[1].targetRps[1] = 25;
+
+
+    /* メインループ ----------------------------------------------------------*/
+    while (1) {
+
+        led = !led;
+
+        for (int i = 0; i < MotorMAX; i++) {
+            MD.Set(i, MovMotor[i]);
+            MovMotor[i] = 0;
+        }
+        for (int i = 0; i < 3; i++) {
+            injection[0].moveMotor[i] = injection[1].moveMotor[i] = 0;
+        }
+        MD.Exe();
+
+        /* 自己位置計算 */
+        LocEstimate();
+
+        /* 操縦権変更 ×停止 〇射出自動 △足回り手動 */
+        if (DS3.CROSS) operate = 0;
+        if (DS3.CIRCLE) operate = 1;
+        if (DS3.TRIANGLE) operate = 2;
+        if (DS3.SQUARE) operate = 3;
+
+        /* STARTで座標系リセット */
+        if (DS3.START) {
+            NowLoc.reset();
+            TarTheta=0.0;
+        }
+
+        /* CS送信ステータス更新 */
+        statious=(operate==1)+1;
+
+        /* 停止以外でジャグリングとリボンの処理 */
+        if (operate != 0) {
+
+            static int nextCnt = 0;
+
+            if(isMoveInjection && WaitNextShot.read_ms() > STEP(targetNum) && !injection[(nextCnt+1)%2].isMove) {
+                injection[nextCnt%2].isMove = 1;
+                WaitNextShot.stop();
+                nextCnt++;
+            }
+
+
+            for (int i = 0; i < 2; i++) {
+                /* 動作状態を決める */
+//                injection[i].isMove = isMoveInjection && injection[!i].WaitNextShot.read_ms() > step;
+                injection[i].MainFunction();
+            }
+
+            /* 出力 */
+            for (int i = 0; i < 6; i++) {
+                MovMotor[i] = Range(injection[i / 3].moveMotor[i % 3], 100);
+            }
+
+            /* リボン */
+            if(isMoveRibbon==1) MovMotor[9] = 70;//70
+            else if(isMoveRibbon==2) {
+                if(limitRibbonTop) MovMotor[10] = 50;
+                else MovMotor[9] = 70;
+            } else if(!limitRibbonBottom)MovMotor[9] = -60;
+
+//            if(isMoveRibbon) {
+//                if(limitRibbonTop) MovMotor[10] = 50;
+//                else MovMotor[9] = 70;
+//            } else {
+//                if(!limitRibbonBottom) MovMotor[9] = -60;
+//            }
+        }
+
+        /* 停止処理 */
+        if (operate == 0) {
+            for (int i = 0; i < MotorMAX; i++) MovMotor[i] = 0;
+            for (int i = 0; i < 2; i++)
+                injection[0].moveMotor[i] = injection[1].moveMotor[i] = 0;
+            for (int i = 0; i < 2; i++)
+                injection[0].updateVal[i][1] = injection[1].updateVal[i][1] = 0;
+            Rod = 0;
+            TarTheta=NowLoc.Theta;
+        }
+
+        /* 足回り自動 */
+        else if (operate == 1) {
+            targetNum = 1;
+            while (Target[targetNum].Time < msTimer && targetNum < TargetMAX)
+                targetNum++;
+
+            if (targetNum == TargetMAX)
+                operate = 0;
+            else {
+                double divTim = 0;
+                divTim = (double)(msTimer - Target[targetNum - 1].Time) / (double)(Target[targetNum].Time - Target[targetNum - 1].Time);
+
+                /* 時間毎の目標を算出する */
+                LOCATION NowTar;
+                NowTar.X = Target[targetNum - 1].X+(Target[targetNum].X - Target[targetNum - 1].X) * divTim;
+                NowTar.Y = Target[targetNum - 1].Y+(Target[targetNum].Y - Target[targetNum - 1].Y) * divTim;
+                NowTar.Theta = Target[targetNum - 1].Theta+(Target[targetNum].Theta - Target[targetNum - 1].Theta) * divTim;
+
+                /* 目標までの必要諸量 */
+                LOCATION moveGain;
+                moveGain.X = LocationPID(NowTar.X, NowLoc.X);
+                moveGain.Y = LocationPID(NowTar.Y, NowLoc.Y);
+                moveGain.Theta = (NowTar.Theta - NowLoc.Theta)*-4;
+
+                /* 三角オムニに出力する */
+                InverseTriOmni(MovMotor, moveGain.X, moveGain.Y, moveGain.Theta);
+                for(int i = 6; i < 9; i++)
+                    MovMotor[i] = Range(MovMotor[i],100);
+
+                isMoveInjection = Target[targetNum].optionInjection;
+                isMoveRibbon = Target[targetNum].optionRibbon;
+                Rod = Target[targetNum].optionRod;
+
+
+//                pc.printf("num:%d ",targetNum);
+//                pc.printf("rod:%d ",Target[targetNum].optionRod);
+                // pc.printf("Tar:%4.0f %4.0f %1.2f ",Target[targetNum].X,Target[targetNum].Y,Target[targetNum].Theta);
+                // pc.printf("NowTar:%4.0f %4.0f %1.2f ",NowTar.X,NowTar.Y,NowTar.Theta);
+            }
+        }
+
+        /* 手動 */
+        else if (operate == 2) {
+
+            int x_val = (DS3.LX-64) * 100/64;
+            int y_val = (64-DS3.LY) * 100/64;
+            int r_val = (DS3.RX-64) * 100/64;
+
+            /* 目標角更新 */
+            if (DS3.RX != 64) yawCnt.reset();
+            if (yawCnt.read_ms() < 1000) TarTheta = NowLoc.Theta;
+            /* gyro値による補正 */
+            r_val += (TarTheta - NowLoc.Theta) * -4.0;
+
+            InverseTriOmni(MovMotor, x_val, y_val, r_val);
+
+            isMoveInjection = DS3.R1;
+            isMoveRibbon = DS3.R2+DS3.L2;
+            Rod = DS3.L1;
+        }
+
+
+        else if(operate == 3) {
+            isMoveInjection = DS3.R1;
+            isMoveRibbon = DS3.R2+DS3.L2;
+            Rod = DS3.L1;
+        }
+
+        /* 改行を入れる */
+        pc.printf("\n\r");
+
+//         pc.printf("Loc:%4.0f %4.0f %1.2f  ",NowLoc.X,NowLoc.Y,NowLoc.Theta);
+//         pc.printf("isRibbon:%d ", isMoveRibbon);
+//         pc.printf("isIn:%d ", isMoveInjection);
+        /* データチェック */
+//         pc.printf("ope:%d  ", operate);
+//         pc.printf("tim:%6d ",msTimer);
+//         pc.printf("tim:%6d ",whileTime_us);
+        // pc.printf("act[0]:%4d %4d", injection[0].actNum,
+        //           injection[0].WaitNextShot.read_ms());
+        // pc.printf("act[1]:%4d %4d", injection[1].actNum,
+        //           injection[1].WaitNextShot.read_ms());
+
+        // pc.printf("BallShotTim:%d  ", injection[1].BallShotTim.read_ms());
+        // pc.printf("lim:%4d  ",injection[1].limitMagazine!=0);
+        // pc.printf("isBallLoading:%d  ", injection[1].isBallLoading);
+        pc.printf("Motor %d %d %d ", MovMotor[6], MovMotor[7], MovMotor[8]);
+
+//        pc.printf("Motor %d %d ", MovMotor[9], MovMotor[10]);
+
+//         pc.printf("Enc %3.0f %3.0f ",EncoderDeg[0],EncoderDeg[1]);
+        for (int i = 0; i < 2; i++) {
+//             pc.printf("inj[%d]is:%d act:%d ", i, injection[i].isMove,
+//                       injection[i].actNum);
+            // pc.printf("BallLoad:%4d %d ", injection[i].BallLoadTim.read_ms(),
+            //           injection[i].isBallLoading);
+//            pc.printf("Enc %3.0f %3.0f %3.0f ", injection[i].encDeg[0],
+//                      injection[i].encDeg[1], injection[i].encDeg[2]);
+//            pc.printf("Enc %3.0f %3.0f ", injection[i].encDeg[0], injection[i].encDeg[0]);
+//                 pc.printf("Lim %d %d ",
+//                injection[i].limitTop!=0,injection[i].limitBottom!=0);
+//            pc.printf("Mo %d %d %d ", injection[i].moveMotor[0], injection[i].moveMotor[1], injection[i].moveMotor[2]);
+//            pc.printf("is %d ", injection[i].BallLoadTim.read_ms());
+//            pc.printf("Ribbon %d ",isMoveRibbon);
+        }
+
+        /* 周期を保存 */
+        whileTime_us = whileTimer.read_us();
+        if(timerMode) msTimer+=(double)whileTimer.read()*1000.0;
+        // if(timerMode) msTimer+=whileTimer.read_ms()+1;
+        if(whileTime_us>50000) I2C hoge(PB_9, PB_8);
+        /* 周期リセット */
+        whileTimer.reset();
+    }  // while
+}  // main
+
+
+
+
+/*******************************************************************************
+ * @概要  射出機構のメイン関数
+ * @引数  なし
+ * @返り値 なし
+ *******************************************************************************/
+void INJECTION::MainFunction(void)
+{
+    /* 上のリミットと接触したらEncリセット */
+    if (limitTop) encDeg[0] = encDeg[1] = 0;
+
+    /* 下のリミットと接触したらエンコーダー角度を合わせる */
+    if(limitBottom) {
+        if(abs(encDeg[0]) > abs(encDeg[1])) encDeg[1] = -encDeg[0];
+        else if(abs(encDeg[0]) < abs(encDeg[1])) encDeg[0] = -encDeg[1];
+    }
+
+    /* 装填機構のリミットと接触したら装填処理*/
+    if (limitMagazine != 0) BallLoadTim.start();
+
+    /* 装填機構動作 */
+    moveMotor[2] = LoadingPID((double)initalDeg,encDeg[2]);
+
+    switch (actNum) {
+        case 0:
+
+            /* 上昇したら終了 */
+            if (encDeg[1] > 800) {
+                for (int i = 0; i < 2; i++) {
+                    velDev[i][0] = velDev[i][1] = velDev[i][2] = 0;
+                    moveMotor[i] = 0;
+                    updateVal[i][0] = updateVal[i][1] = 0;
+                    EncDegDiff[i][0]=EncDegDiff[i][1]=0;
+                }
+                actNum++;
+
+            } else if (BallShotTim.read_ms() >  250) {//350 //250
+                /* 左右それぞれ一秒あたりの回転数を計算する */
+                double rps[2]= {0};
+                for (int i = 0; i < 2; i++) {
+                    double angularVel = RotationPerWhile(encDeg[i], i);  //差分
+                    rps[i] = (angularVel * 1000000.0 / whileTime_us) / 360.0;
+                }
+
+                /* 操作量を更新 */
+                for (int i = 0; i < 2; i++) {
+                    updateVal[i][0] = updateVal[i][1];
+                    updateVal[i][1] =
+                        updateVal[i][0] + VelocityPID(targetRps[i], rps[i], i);
+                    /* 値を制限する */
+                    moveMotor[i] = Range(updateVal[i][1], 100);
+
+                }
+            }
+            break;
+
+        case 1:
+            /* 目標更新,装填機構動作 */
+            if (BallLoadTim.read_ms() >400 && isMove) {//450 200
+                isBallLoading = 1;
+                initalDeg -= 120;
+                isMove = 0;
+                WaitNextShot.reset();
+                WaitNextShot.start();
+            }
+
+            /* 装填機構を動作させる */
+            if (isBallLoading) {
+                BallLoadTim.stop();
+                BallLoadTim.reset();
+
+                if (limitTop) {
+                    /* 誤差範囲なら終了フラグ立て */
+                    /* 装填機構の目標角度更新 */
+                    if (abs(initalDeg - encDeg[2]) < 5.0) {
+                        isBallLoading = 0;
+                        BallShotTim.reset();
+                        actNum++;
+                    }
+                }
+                /* 射出機構戻ってなかったら途中までで上書き */
+                else {
+                    /* 下降中に途中まで回す */
+                    moveMotor[2] = LoadingPID(initalDeg+40, encDeg[2]);
+                }
+            }
+
+            /* リミットに触れるまで下降 */
+            if (limitTop) {
+                for (int i = 0; i < 2; i++) moveMotor[i] = 0;
+            } else if (encDeg[1] > 500) {
+                for (int i = 0; i < 2; i++) {
+                    moveMotor[i] = -400 - encDeg[i];
+                    moveMotor[i] = Range(moveMotor[i], 70);
+                }
+            } else {
+                /* 固定値で一番下まで下降 */
+                for (int i = 0; i < 2; i++) moveMotor[i] = -10 * (i * 2 - 1);
+            }
+            break;
+
+        default:
+            actNum = 0;
+            break;
+    }  // switch
+}
+
+/*******************************************************************************
+ * @概要  速度型PID制御
+ * @引数  目標値, 現在値, モーター番号
+ * @返り値 操作量
+ *******************************************************************************/
+double INJECTION::VelocityPID(double Target_value, double Current_value, int motorNum)
+{
+    /* 比例定数,積分定数,微分定数 */
+    const double kp = 1.0;
+    const double ki = 0.3;
+    const double kd = 0.05;
+    /* 比例項,積分項,微分項の計算結果を保存する変数*/
+
+    /* 偏差を更新 */
+    velDev[motorNum][2] = velDev[motorNum][1];
+    velDev[motorNum][1] = velDev[motorNum][0];
+    velDev[motorNum][0] = Target_value - Current_value;
+
+    /* 比例項,積分項,微分項をそれぞれ求める */
+    double P = (velDev[motorNum][0] - velDev[motorNum][1]) * kp;
+    double I = velDev[motorNum][0] * ki;
+    double D = ((velDev[motorNum][0] - velDev[motorNum][1]) -
+                (velDev[motorNum][1] - velDev[motorNum][2])) *
+               kd;
+
+    /* 合計値を操作量として返す */
+    return P + I + D;
+}
+
+/*******************************************************************************
+ * @概要  位置型PID制御
+ * @引数  目標値, 現在値
+ * @返り値 操作量
+ *******************************************************************************/
+double LocationPID(double Target_value, double Current_value)
+{
+    /* 比例定数,積分定数,微分定数 */
+    const double kp = 0.5;
+    const double ki = 0;
+    const double kd = 0;
+    /* 比例項,積分項,微分項の計算結果を保存する変数*/
+    static double P = 0;
+    static double I = 0;
+    static double D = 0;
+    /* 偏差の一時保存領域 */
+    static double dev[2] = {0};
+
+    /* 偏差を更新 */
+    dev[1] = dev[0];
+    dev[0] = Target_value - Current_value;
+
+    /* 比例項,積分項,微分項をそれぞれ求める */
+    P = dev[0] * kp;
+    I += dev[0] * ki;
+    D = (dev[0] - dev[1]) * kd;
+
+    /* ワインドアップ */
+    if ((dev[0] < 0 && dev[1] > 0) || (dev[0] > 0 && dev[1] < 0)) I = 0;
+
+    /* 合計値を操作量として返す */
+    return P + I + D;
+}
+
+/*******************************************************************************
+ * @概要  位置型PID制御(装填機構)
+ * @引数  目標値, 現在値
+ * @返り値 操作量
+ *******************************************************************************/
+double INJECTION::LoadingPID(double Target_value, double Current_value)
+{
+    /* 比例定数,積分定数,微分定数 */ //0.2 0.02 0.25 | 0.2 0.04 0.25
+    const double kp = 1.0;
+    const double ki = 0.06;
+    const double kd = 0.9;
+    /* 比例項,積分項,微分項の計算結果を保存する変数*/
+    static double P = 0;
+    static double I = 0;
+    static double D = 0;
+
+    /* 偏差を更新 */
+    loadDev[1] = loadDev[0];
+    loadDev[0] = Target_value - Current_value;
+
+    /* 比例項,積分項,微分項をそれぞれ求める */
+    P = loadDev[0] * kp;
+    I += loadDev[0] * ki;
+    D = (loadDev[0] - loadDev[1]) * kd;
+
+
+    /* ワインドアップ */
+    if ((loadDev[0] < 0 && loadDev[1] > 0) || (loadDev[0] > 0 && loadDev[1] < 0)) I = 0;
+
+    /* 合計値を操作量として返す */
+    return Range(P + Range(I,6) + D, 80);
+}
+
+/*******************************************************************************
+ * @概要  制限する
+ * @引数  対象,限界値
+ * @返り値 制限後の値
+ *******************************************************************************/
+double Range(double value, double limit)
+{
+    if (value > limit)
+        return limit;
+    else if (value < -limit)
+        return -limit;
+    else
+        return value;
+}
+
+/*******************************************************************************
+ * @概要  回転角度を求める関数
+ * @引数  角度, エンコーダー番号, 機体番号
+ * @返り値 周期あたりの角速度
+ *******************************************************************************/
+double INJECTION::RotationPerWhile(double degree, int num)
+{
+    /* 前回の値更新 今回の値更新 */
+    EncDegDiff[num][1] = EncDegDiff[num][0];
+    EncDegDiff[num][0] = degree;
+
+    /* 前回と現在の値を比べる */
+    return EncDegDiff[num][0] - EncDegDiff[num][1];
+}
+
+/*******************************************************************************
+ * @概要   自己位置推定関数
+ * @引数   なし
+ * @返り値 なし
+ *******************************************************************************/
+void LocEstimate(void)
+{
+    static double GyroDeg[2] = {0};
+    static double EncDeg[2][2] = {0};
+    static double disp[3] = {0};
+
+    /* ジャイロの値取得 */
+    bno.get_angles();
+    GyroDeg[1] = GyroDeg[0];
+    GyroDeg[0] = bno.euler.yaw;
+
+    if (GyroDeg[0] != 0) {
+        /* 359→0を跨いだ時,前回の値を0から逆回転で負の値で表記 */
+        if (GyroDeg[1] < 90 && GyroDeg[0] > 270) GyroDeg[1] += 360;
+        /* 0→359を跨いだ時,前回の値を360以上の値で表記 */
+        else if (GyroDeg[1] > 270 && GyroDeg[0] < 90)
+            GyroDeg[1] -= 360;
+        /* 差を求める*/
+        disp[2] = GyroDeg[1] - GyroDeg[0];
+    }
+    /* Enc2つの差分求める */
+    for (int i = 0; i < 2; i++) {
+        EncDeg[i][1] = EncDeg[i][0];
+        EncDeg[i][0] = EncoderDeg[i];
+        disp[i] = DEG_TO_DIS(EncDeg[i][1] - EncDeg[i][0]);
+    }
+    /* 差分を加速度として保存 */
+    NowAcc.Theta = disp[2];
+    NowAcc.X = -disp[0] * cos(DEG_TO_RAD(NowLoc.Theta)) +
+               disp[1] * sin(DEG_TO_RAD(NowLoc.Theta));
+    NowAcc.Y = -disp[0] * sin(DEG_TO_RAD(NowLoc.Theta)) -
+               disp[1] * cos(DEG_TO_RAD(NowLoc.Theta));
+    /* 差分を累積して現在位置を保存 */
+    NowLoc.X += NowAcc.X / 2.0;
+    NowLoc.Y += NowAcc.Y / 2.0;
+    NowLoc.Theta += NowAcc.Theta;
+}
+
+/*******************************************************************************
+ * @概要  回転角度を求める関数
+ * @引数  角度, エンコーダー番号, 機体番号
+ * @返り値 周期あたりの角速度
+ *******************************************************************************/
+int Rotation(int degree, int num, int robotNum)
+{
+    static int EncDeg[2][2][2] = {0};
+    static int NowRotate[2][2] = {0};
+
+    /* 前回の値更新 今回の値更新 */
+    EncDeg[robotNum][num][1] = EncDeg[robotNum][num][0];
+    EncDeg[robotNum][num][0] = degree;
+
+    /* 前回と現在の値を比べる */
+    NowRotate[robotNum][num] =
+        EncDeg[robotNum][num][0] - EncDeg[robotNum][num][1];
+    return NowRotate[robotNum][num];
+}
+
+/*******************************************************************************
+ * @概要  逆三角オムニ関数
+ * @引数  モータの配列・x,y,r ベクトル
+ * @返り値 なし
+ ******************************************************************************/
+void InverseTriOmni(int *duty, double x, double y, double r)
+{
+    int duty_max = 0;
+
+    double rad = DEG_TO_RAD(NowLoc.Theta);
+    duty[6] = x * cos(rad) + y * sin(rad);
+    duty[7] = x * cos(rad + PI / 3.0 * 2) + y * sin(rad + PI / 3.0 * 2);
+    duty[8] = -x * cos(rad + PI / 3.0) - y * sin(rad + PI / 3.0);
+
+    duty_max = 0;
+    for (int i = 6; i < 9; i++) {
+        duty[i] += r;
+        duty_max = max(duty_max, abs(duty[i]));
+    }
+    for (int i = 6; i < 9; i++)
+        if (duty_max > 99) duty[i] = duty[i] * 99 / duty_max;
+}
+
+
+/* UART受信割り込み *************************************************************/
+void uartRX()
+{
+    static int num = 0;
+    static uint8_t buf[3] = {0};
+
+    /* 受信データ */
+    uint8_t data = UART.getc();
+    if((data&0x80) != 0) {
+        timerMode = (data & 0x40)!=0;
+
+        //ビット結合
+        uint32_t tmp = 0;
+        for(int i=0; i<3; i++)
+            tmp |= buf[i]<<(14-i*7);
+
+        //チェックサム計算
+        uint8_t checkSum = 0;
+        for (int i = 0; i < 7; i++)
+            checkSum += tmp / (int)pow(10.0, (double)i) % 10;
+
+        //誤り検知陰性
+        if(checkSum == (data&0x3F))
+            msTimer = tmp;
+
+        num = 0;
+    } else {
+        buf[num] = data;
+        num++;
+    }
+
+
+    /* 送信データ */
+    char sendData = 2<<2;
+    sendData|=(statious&0b11);
+    UART.putc(sendData);
+}
+
+/* 割り込み(100us) *************************************************************/
+void IT_CallBack(void)
+{
+    static int cnt = 0;
+    static int data[EncoderMAX] = {0};
+    static double EncDeg[EncoderMAX][2] = {0};
+
+    switch (cnt) {
+        /* 最初の処理 */
+        case 0:
+            for (int i = 0; i < EncoderMAX; i++) data[i] = 0;
+            CS = 0;
+            CL = 1;
+            break;
+        /* 最後の処理 */
+        case 25:
+            CS = 1;
+
+            for (int i = 0; i < EncoderMAX; i++) {
+                /* 前回の値更新
+                 * 今回の値更新(エンコーダの値(0~4096)を角度(0~360)に) */
+                EncDeg[i][1] = EncDeg[i][0];
+                EncDeg[i][0] = (double)data[i] * 360.0 / 4096;
+                /* 359→0を跨いだ時,前回の値を0から逆回転で負の値で表記 */
+                if ((270 <= EncDeg[i][1]) && (EncDeg[i][0] < 90))
+                    EncDeg[i][1] -= 360;
+                /* 0→359を跨いだ時,前回の値を360以上の値で表記 */
+                else if ((EncDeg[i][1] < 90) && (270 <= EncDeg[i][0]))
+                    EncDeg[i][1] += 360;
+                /* 差を求め,適当な変数に代入 */
+                if (i < 6)
+                    injection[i / 3].encDeg[i % 3] +=
+                        EncDeg[i][0] - EncDeg[i][1];
+                else
+                    EncoderDeg[i - 6] -= EncDeg[i][0] - EncDeg[i][1];
+            }
+            break;
+        /* 通常の処理 */
+        default:
+            CL = !CL;
+            /* 最初でも最後でもなく奇数回で最初以外の時読み取り処理 */
+
+            for (int i = 0; i < EncoderMAX; i++)
+                if (cnt != 1 && cnt % 2) {
+                    data[i] |= (Do[i] == 1);
+                    data[i] = data[i] << 1;
+                }
+            break;
+    }
+    cnt++;
+    cnt %= 26;
+}
\ No newline at end of file