/// <summary>
        /// 指定されたサーボ値を適用してシリアル通信でプリメイドAI実機に送る
        /// </summary>
        public void ApplyPoseFromServos(IEnumerable <PreMaidServo> servos, int speed = 10)
        {
            if (SerialPortOpen == false)
            {
                Debug.LogWarning("ポーズ指定されたときにシリアルポートが開いていません");
                return;
            }

            speed = Mathf.Clamp(speed, 1, 255);

            int servoNum = servos.Count();
            int orderLen = servoNum * 3 + 5; //命令長はサーボ個数が1個だったら0x08, サーボ個数が25個だったら0x50(80)になる

            //決め打ちのポーズ命令+スピード(小さい方が速くて、255が最大に遅い)
            string ret = orderLen.ToString("X2") + " 18 00 " + speed.ToString("X2");

            //そして各サーボぼ値を入れる
            foreach (var VARIABLE in servos)
            {
                ret += " " + VARIABLE.GetServoIdAndValueString();
            }

            ret += " FF"; //パリティビットを仮で挿入する;

            //パリティビットを計算し直した値にして、文字列を返す
            ret = PreMaidUtility.RewriteXorString(ret);
            sendingQueue.Enqueue(ret); //対象のモーション、今回は1個だけ;
        }
        /// <summary>
        /// バッテリー残量の問い合わせ、ハンドリングはPreMaidReceiver.csで行っています
        /// </summary>
        public void RequestBatteryRemain()
        {
            string batteryRequestOrder = "07 01 00 02 00 02 06";

            //Debug.Log("リクエスト:"+ batteryRequestOrder);
            sendingQueue.Enqueue(PreMaidUtility.RewriteXorString(batteryRequestOrder)); //バッテリー残量を教えてもらう
        }
        /// <summary>
        /// 現在のサーボ値を適用する1フレームだけのモーションを送る
        /// </summary>
        /// <returns></returns>
        string BuildPoseString(int speed = 50)
        {
            if (speed > 255)
            {
                speed = 255;
            }

            if (speed < 1)
            {
                speed = 1;
            }

            //決め打ちのポーズ命令+スピード(小さい方が速くて、255が最大に遅い)
            string ret = "50 18 00 " + speed.ToString("X2");

            //そして各サーボぼ値を入れる
            foreach (var VARIABLE in Servos)
            {
                ret += " " + VARIABLE.GetServoIdAndValueString();
            }

            ret += " FF"; //パリティビットを仮で挿入する;

            //パリティビットを計算し直した値にして、文字列を返す
            return(PreMaidUtility.RewriteXorString(ret));
        }
        /// <summary>
        /// たぶんこれでFLASHのダンプが返ってくる
        /// </summary>
        /// <param name="page"></param>
        public void RequestFlashRomDump(int page)
        {
            string flashDump = "05 1C 00 " + string.Format("{0:X2}", page) + " FF";

            Debug.Log("リクエスト:" + flashDump);
            sendingQueue.Enqueue(PreMaidUtility.RewriteXorString(flashDump)); //FLASHの中身を教えてもらう?
        }
Пример #5
0
        /// <summary>
        /// たぶんあとで非同期待ち受けつかう
        /// </summary>
        /// <returns></returns>
        IEnumerator ApplyPoseCoroutine()
        {
            float waitSec = 0.04f; //0.03だと送信失敗することがある

            byte[] data1 = PreMaidUtility.BuildByteDataFromStringOrder("07 01 00 02 00 02 06");
            _serialPort.Write(data1, 0, data1.Length);
            yield return(new WaitForSeconds(waitSec));

            byte[] data2 = PreMaidUtility.BuildByteDataFromStringOrder("07 01 00 08 00 02 0C");
            _serialPort.Write(data2, 0, data2.Length);
            yield return(new WaitForSeconds(waitSec));

            byte[] data3 = PreMaidUtility.BuildByteDataFromStringOrder("08 02 00 08 00 FF FF 02");
            _serialPort.Write(data3, 0, data3.Length);
            yield return(new WaitForSeconds(waitSec));

            byte[] data4 = PreMaidUtility.BuildByteDataFromStringOrder("04 04 00 00"); //フラッシュのライトプロテクト解除?
            _serialPort.Write(data4, 0, data4.Length);
            yield return(new WaitForSeconds(waitSec));

            byte[] data5 = PreMaidUtility.BuildByteDataFromStringOrder("5c 1d 00 00 00"); //転送コマンド?
            _serialPort.Write(data5, 0, data5.Length);
            yield return(new WaitForSeconds(waitSec));

            //ここでポーズ情報を取得する
            byte[] data6 =
                PreMaidUtility.BuildByteDataFromStringOrder(
                    BuildPoseString()); //対象のモーション、今回は1個だけ
            _serialPort.Write(data6, 0, data6.Length);
            yield return(new WaitForSeconds(waitSec * 2));


            byte[] data7 = PreMaidUtility.BuildByteDataFromStringOrder("04 17 00 13 ff ff 41"); //不明
            _serialPort.Write(data7, 0, data7.Length);
            yield return(new WaitForSeconds(waitSec));

            byte[] data8 = PreMaidUtility.BuildByteDataFromStringOrder("05 1E 00 01 1A");
            _serialPort.Write(data8, 0, data8.Length);
            yield return(new WaitForSeconds(waitSec));


            byte[] data9 = PreMaidUtility.BuildByteDataFromStringOrder("05 1C 00 01 18"); //ベリファイダンプ要請
            _serialPort.Write(data9, 0, data9.Length);
            yield return(new WaitForSeconds(waitSec));


            byte[] data10 = PreMaidUtility.BuildByteDataFromStringOrder("08 02 00 08 00 08 00 0A"); //モーションデータ転送終了
            _serialPort.Write(data10, 0, data10.Length);
            yield return(new WaitForSeconds(waitSec));

            byte[] data11 = PreMaidUtility.BuildByteDataFromStringOrder("04 04 00 00"); //フラッシュのライトプロテクトを掛ける?
            _serialPort.Write(data11, 0, data11.Length);
            yield return(new WaitForSeconds(waitSec));

            byte[] data12 = PreMaidUtility.BuildByteDataFromStringOrder("05 1F 00 01 1B"); //01番モーション再生
            _serialPort.Write(data12, 0, data12.Length);
            yield return(new WaitForSeconds(waitSec));
        }
Пример #6
0
        /// <summary>
        /// "4C 1D"などの外部からサーボの値を入れる
        /// ちなみに4C 1Dが7500です
        /// </summary>
        /// <param name="newValue">"4C 1D"</param>
        public void SetServoValueSafeClamp(string spaceSplitedByteString)
        {
            var aaa      = PreMaidUtility.ConvertEndian(PreMaidUtility.RemoveWhitespace(spaceSplitedByteString));
            int intValue = int.Parse(aaa, System.Globalization.NumberStyles.HexNumber);

            //Debug.Log($"{spaceSplitedByteString} は {intValue} ");

            SetServoValueSafeClamp(intValue);
        }
        /// <summary>
        /// 全サーボの強制脱力命令
        /// </summary>
        public void ForceAllServoStop()
        {
            //ここで連続送信モードを停止しないと、脱力後の急なサーボ命令で一気にプリメイドAIが暴れて死ぬ
            SetContinuousMode(false);

            string allStop = "50 18 00 06 02 00 00 03 00 00 04 00 00 05 00 00 06 00 00 07 00 00 08 00 00 09 00 00 0A 00 00 0B 00 00 0C 00 00 0D 00 00 0E 00 00 0F 00 00 10 00 00 11 00 00 12 00 00 13 00 00 14 00 00 15 00 00 16 00 00 17 00 00 18 00 00 1A 00 00 1C 00 00 FF";

            byte[] allServoStopOrder = PreMaidUtility.BuildByteDataFromStringOrder(PreMaidUtility.RewriteXorString(allStop));
            _serialPort.Write(allServoStopOrder, 0, allServoStopOrder.Length);
        }
        /// <summary>
        /// たぶんあとで非同期待ち受けつかう
        /// </summary>
        /// <returns></returns>
        IEnumerator ApplyPoseCoroutine()
        {
            float waitSec = 0.06f; //0.03だと送信失敗することがある

            //ここでポーズ情報を取得する
            byte[] willSendPoseBytes =
                PreMaidUtility.BuildByteDataFromStringOrder(
                    BuildPoseString(80)); //対象のモーション、今回は1個だけ

            _serialPort.Write(willSendPoseBytes, 0, willSendPoseBytes.Length);
            yield return(new WaitForSeconds(waitSec));
        }
Пример #9
0
        /// <summary>
        /// シリアルポート読み取り書き込みスレッド
        /// 読みと書きを別スレッドにするより、まとめて1スレッドにしたほうがシリアルポートのlockが走らない分だけ早い
        /// </summary>
        private void ReadAndWriteThreadFunc()
        {
            Debug.LogWarning("シリアルポート送信スレッド起動");

            var readBuffer = new byte[256 * 3];
            var readCount  = 0;

            while (SerialPortOpen && _serialPort != null && _serialPort.IsOpen)
            {
                //PCから送る予定のキューが入っているかチェック
                if (sendingQueue.IsEmpty == false)
                {
                    var willSendString = string.Empty;
                    if (sendingQueue.TryDequeue(out willSendString))
                    {
                        byte[] willSendBytes =
                            PreMaidUtility.BuildByteDataFromStringOrder(willSendString);

                        _serialPort.Write(willSendBytes, 0, willSendBytes.Length);
                    }
                }


                //プリメイドAIからの受信チェック
                try
                {
                    readCount = _serialPort.Read(readBuffer, 0, readBuffer.Length);

                    if (readCount > 0)
                    {
                        receivedQueue.Enqueue(PreMaidUtility.DumpBytesToHexString(readBuffer, readCount));
                    }
                }
                catch (TimeoutException tEx)
                {
                    //errorQueue.Enqueue("TimeOut Exception:" + tEx.Message);
                    //Thread.Sleep(1);
                    continue;
                }
                catch (System.Exception e)
                {
                    errorQueue.Enqueue(e.Message);
                    //Debug.LogWarning(e.Message);
                }

                Thread.Sleep(1);
            }

            Debug.LogWarning("exit thread");
        }
        /// <summary>
        /// 全サーボの強制脱力命令
        /// </summary>
        public void ForceAllServoStop(bool disconnect = true)
        {
            //ここで連続送信モードを停止しないと、脱力後の急なサーボ命令で一気にプリメイドAIが暴れて死ぬ
            //なので、普段はついでにシリアルポートも切る

            string allStop =
                "50 18 00 06 02 00 00 03 00 00 04 00 00 05 00 00 06 00 00 07 00 00 08 00 00 09 00 00 0A 00 00 0B 00 00 0C 00 00 0D 00 00 0E 00 00 0F 00 00 10 00 00 11 00 00 12 00 00 13 00 00 14 00 00 15 00 00 16 00 00 17 00 00 18 00 00 1A 00 00 1C 00 00 FF";

            sendingQueue.Enqueue(PreMaidUtility.RewriteXorString(allStop)); //ストップ命令を送る

            if (disconnect)
            {
                CloseSerialPort();
            }
        }
Пример #11
0
        /// <summary>
        /// 受信時の処理
        /// </summary>
        /// <param name="receivedString"></param>
        /// <exception cref="NotImplementedException"></exception>
        private void OnReceivedFromPreMaidAi(string receivedString)
        {
            //4文字以下なら不正
            if (receivedString.Length < 4)
            {
                return;
            }
            //3-4文字目が命令種類
            string orderKind = receivedString.Substring(2, 2);

            //Debug.Log("orderKind:"+ orderKind);
            switch (orderKind)
            {
            //バッテリー残量
            case "01":
                if (receivedString.Length >= 10)
                {
                    int rawValtageValue =
                        PreMaidUtility.HexStringToInt(PreMaidUtility.ConvertEndian(receivedString.Substring(6, 4)));
                    Debug.Log($"バッテリー残量{rawValtageValue} で電圧は{rawValtageValue / 216f} V");

                    if (rawValtageValue / 216.0f < 9f)
                    {
                        Debug.LogError("バッテリー残量が9V以下です!!!!");
                    }
                }

                break;

            //モーション転送結果
            case "18":
                if (receivedString == "0418001C")
                {
                }
                else
                {
                    Debug.Log("PoseError:" + receivedString);
                }

                break;

            default:
                Debug.Log(receivedString);
                break;
            }
        }
        /// <summary>
        /// 現在のサーボ値を適用する1フレームだけのモーションを送る
        /// </summary>
        /// <returns></returns>
        string BuildPoseString(int speed = 10)
        {
            speed = Mathf.Clamp(speed, 1, 255);

            //決め打ちのポーズ命令+スピード(小さい方が速くて、255が最大に遅い)
            string ret = "08 18 00 " + speed.ToString("X2");
            //そして各サーボぼ値を入れる

            var index = _servos.FindIndex(x => x.GetServoId() == 2);

            ret += " " + _servos[index].GetServoIdAndValueString();


            ret += " FF"; //パリティビットを仮で挿入する;

            //パリティビットを計算し直した値にして、文字列を返す
            return(PreMaidUtility.RewriteXorString(ret));
        }
        /// <summary>
        /// 全サーボのスピードパラメータ指定命令
        /// 18 19 10 10 3C 18 3C 1C 3C 14 3C 0C 3C 0E 3C 16 3C 1A 3C 12 3C 0A 3C 07
        /// </summary>
        public void ForceAllServoSpeedProperty(int speed)
        {
            var targetSpeed = Mathf.Clamp(speed, 1, 127);

            string speedProp = string.Format("{0:X2}", targetSpeed);

            //0x36=54個なので
            //最初の3個+ 25サーボ*2パラメータ+ チェックバイト
            string allServo =
                "36 19 00";

            foreach (var VARIABLE in Servos)
            {
                allServo += " " + VARIABLE.GetServoIdString() + " " + speedProp;
            }

            allServo += "FF";
            sendingQueue.Enqueue(PreMaidUtility.RewriteXorString(allServo)); //ストレッチ命令を送る
        }
        /// <summary>
        /// シリアルポート読み取り書き込みスレッド
        /// 読みと書きを別スレッドにするより、まとめて1スレッドにしたほうがシリアルポートのlockが走らない分だけ早い
        /// </summary>
        private void ReadAndWriteThreadFunc()
        {
            Debug.LogWarning("シリアルポート送信スレッド起動");

            var readBuffer   = new byte[256 * 3];
            var readCount    = 0;
            var sendingCache = string.Empty; //送信失敗時に連続送信する

            //バースト転送モード
            bool burstMode = false;

            while (SerialPortOpen && _serialPort != null && _serialPort.IsOpen)
            {
                //PCから送る予定のキューが入っているかチェック
                if (sendingQueue.IsEmpty == false)
                {
                    var willSendString = string.Empty;
                    if (sendingQueue.TryDequeue(out willSendString))
                    {
                        if (burstMode)
                        {
                            burstMode = false;
                        }

                        sendingCache = willSendString;
                        byte[] willSendBytes =
                            PreMaidUtility.BuildByteDataFromStringOrder(willSendString);

                        _serialPort.Write(willSendBytes, 0, willSendBytes.Length);
                    }
                }


                //プリメイドAIからの受信チェック
                try
                {
                    //本当はここのカウントもバッファ溜めつつ見た方が良い…
                    readCount = _serialPort.Read(readBuffer, 0, readBuffer.Length);

                    if (readCount > 0)
                    {
                        var receivedString = PreMaidUtility.DumpBytesToHexString(readBuffer, readCount);
                        //ポーズ送信失敗したらバーストモードに入る
                        if (receivedString.IndexOf("180814") >= 0)
                        {
                            burstMode = true;
                        }

                        if (receivedString.IndexOf("18001C") >= 0 && burstMode == true)
                        {
                            burstMode = false;
                        }

                        receivedQueue.Enqueue(receivedString);
                    }
                }
                //UnityのSerialPortの実装がタコなのでここでTimeout例外を握りつぶす必要があります
                catch (TimeoutException tEx)
                {
                    continue;
                }
                catch (System.Exception e)
                {
                    errorQueue.Enqueue(e.Message);
                    //Debug.LogWarning(e.Message);
                }

                Thread.Sleep(1);
                //送信失敗してた場合、無理矢理にキャッシュしてた最後のポーズ命令を連続送信する
                //これで遅延を最小限にする
                if (burstMode)
                {
                    Thread.Sleep(5);
                    byte[] willSendBytes =
                        PreMaidUtility.BuildByteDataFromStringOrder(sendingCache);

                    _serialPort.Write(willSendBytes, 0, willSendBytes.Length);
                }
            }

            Debug.LogWarning("exit thread");
        }
Пример #15
0
        /// <summary>
        /// 7500だったら"4C 1D"が返ってくる
        /// </summary>
        /// <returns></returns>
        public string GetServoValueString()
        {
            var tmp = PreMaidUtility.ConvertEndian(_servoValue.ToString("X2"));

            return($"{tmp[0]}{tmp[1]} {tmp[2]}{tmp[3]}");
        }
        // Update is called once per frame
        void Update()
        {
            if (errorQueue.IsEmpty == false)
            {
                var errorString = string.Empty;
                if (errorQueue.TryDequeue(out errorString))
                {
                    Debug.LogError(errorString);
                }
            }

            if (SerialPortOpen == false)
            {
                return;
            }

            //受信バッファ、バイナリで届くので区切りをどうしようか悩み中
            //一旦、素朴に先頭に命令長が来るでしょう、というつもりで書きます。
            if (receivedQueue.IsEmpty == false)
            {
                var receivedString = string.Empty;
                if (receivedQueue.TryDequeue(out receivedString))
                {
                    bufferedString += receivedString;

                    if (bufferedString.Length < 2)
                    {
                        return;
                    }

                    //異様にバッファが溜まったら捨てる
                    if (bufferedString.Length > 100)
                    {
                        Debug.Log("破棄します:" + bufferedString);
                        bufferedString = string.Empty;
                        return;
                    }

                    int orderLength = PreMaidUtility.HexStringToInt(bufferedString.Substring(0, 2));

                    //先頭0だったら命令ではないと判断して2文字読み捨て
                    //なぜなら0004051Fみたいな文字列が入っているので
                    if (orderLength == 0)
                    {
                        bufferedString = bufferedString.Substring(2);
                    }
                    //命令長が足りないので待つ
                    else if (orderLength > bufferedString.Length * 2)
                    {
                        return;
                    }
                    else if (bufferedString.Length >= orderLength * 2)
                    {
                        var targetOrder = bufferedString.Substring(0, orderLength * 2);
                        if (OnReceivedFromPreMaidAI != null)
                        {
                            OnReceivedFromPreMaidAI.Invoke(targetOrder);
                        }
                        else
                        {
                            Debug.Log(targetOrder);
                        }

                        //まだ余りバッファが有るならツメます
                        if (orderLength * 2 < bufferedString.Length)
                        {
                            bufferedString = bufferedString.Substring(orderLength * 2 + 1);
                        }
                        else
                        {
                            bufferedString = string.Empty;
                        }
                    }
                }
            }
        }
Пример #17
0
        /// <summary>
        /// 足踏みから歩行へ至る処理を、とりあえずヤッツケで実装してみる。(直値埋め込みまくりで邪悪…)
        /// </summary>
        void MathWalk()
        {
            float X1 = Input.GetAxis("Horizontal");
            float Y1 = Input.GetAxis("Vertical");
            float X2 = Input.GetAxis("Horizontal2");
            float Y2 = Input.GetAxis("Vertical2");

            bool LT = Input.GetButton("LT");

            int[] ServoVal = { 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500 };

            // 姿勢制御(無くても歩ける)

            if (Mathf.Abs(X2) > 0.1)    // 腰ヨー軸もどき
            {
                ServoVal[12] += (int)(600 * X2);
                ServoVal[13] += (int)(600 * X2);
                ServoVal[14] += (int)(600 * X2);    // 首回り

                for (int i = 0; i < 3; i++)
                {
                    ServoVal[i + 0] += (int)((140 * X2) * LinkDefPitch[i + 0]);
                    ServoVal[i + 4] -= (int)((140 * X2) * LinkDefPitch[i + 4]);
                }
            }

            if (Mathf.Abs(Y2) > 0.1)             // 前後姿勢制御
            {
                ServoVal[15] += (int)(300 * X2); // 首回り
                ServoVal[16] -= (int)(300 * Y2);

                if (Y2 < 0)     // しゃがみ
                {
                    for (int i = 0; i < 8; i++)
                    {
                        ServoVal[i] -= (int)((400 * Y2) * LinkDefLen[i]);
                    }
                }
                else                                // 前屈
                {
                    ServoVal[0] += (int)(150 * Y2); // 直値 配列もつけてない。 適当…
                    ServoVal[4] -= (int)(150 * Y2);
                    ServoVal[3] -= (int)(450 * Y2);
                    ServoVal[7] += (int)(450 * Y2);
                }
            }

            // 歩行制御(トリガ―で足踏み開始、半周期で操作可能になる )

            if (WalkingCondition == 0 && LT == true)
            {
                WalkingCondition = 1;
                curTick          = 0;
            }

            if (WalkingCondition > 0)
            {
                if (++curTick >= maxTick)
                {
                    curTick = 0;
                    if (WalkingCondition == 1)
                    {
                        WalkingCondition = 2;
                    }
                    if (LT == false)
                    {
                        WalkingCondition = 3;
                    }
                }

                float fRateLen  = table_sin3[curTick];
                float fRateRoll = table_sin1[curTick];
                float fRateP1   = table_plot1[curTick];
                float fRateP2   = table_plot2[curTick];

                int ratio = 2;

                if (WalkingCondition == 1)      // 歩き始めの足踏み
                {
                    if (curTick < 5)
                    {
                        ratio = 1;
                    }
                    else
                    {
                        WalkingCondition = 2;
                    }
                }
                if (WalkingCondition == 3)      // 歩き終わりの足踏み
                {
                    if (curTick < 5)
                    {
                        ratio = 1;
                    }
                    else
                    {
                        WalkingCondition = 0;
                    }
                }

                // ROLL処理 ==========
                for (int i = 8; i < 12; i++)
                {
                    ServoVal[i] += (int)((fRateRoll * DuraRoll) * LinkDefRoll[i] * ratio);
                }

                // LEN処理 ==========
                int tmp = 0;
                if (fRateLen < 0)   // 正負で上げる足を違える
                {
                    tmp      = 4;
                    fRateLen = -fRateLen;
                }
                for (int i = tmp; i < tmp + 4; i++)
                {
                    ServoVal[i] += (int)((fRateLen * DuraLen) * LinkDefLen[i] * ratio);
                }


                if (WalkingCondition == 2)          // 足踏み完了後(#2) STICK操作が可能になる

                // Pitch処理 ==========
                {
                    for (int i = 0; i < 4; i++)
                    {
                        ServoVal[i + 0] += (int)((fRateP1 * DuraPitch) * LinkDefPitch[i + 0] * Y1);
                        ServoVal[i + 4] += (int)((fRateP2 * DuraPitch) * LinkDefPitch[i + 4] * Y1);
                    }

                    // Yaw処理 ==========
                    if ((WalkingSteering != 0 || Mathf.Abs(X1) > 0.1))
                    {
                        int Steering = -(int)(X1 * 250);

                        if (curTick == 2 || curTick == 3 || curTick == 4)
                        {
                            if (Steering > 0)
                            {
                                WalkingSteering += Steering;
                            }
                            else
                            {
                                WalkingSteering = (curTick == 4) ? 0 : WalkingSteering / 2;
                            }
                        }
                        if (curTick == 7 || curTick == 8 || curTick == 9)
                        {
                            if (Steering < 0)
                            {
                                WalkingSteering -= Steering;
                            }
                            else
                            {
                                WalkingSteering = (curTick == 9) ? 0 : WalkingSteering / 2;
                            }
                        }
                    }

                    ServoVal[12] += WalkingSteering;
                    ServoVal[13] -= WalkingSteering;
                }
            }

            // 0x18コマンドの完成
            string cmd = "38 18 00 " + TickSpeed.ToString("X2");

            for (int i = 0; i < 17; i++)
            {
                byte h = (byte)(ServoVal[i] / 256);
                byte l = (byte)(ServoVal[i] % 256);
                cmd += " " + ServoId[i].ToString("X02") + " " + l.ToString("X02") + " " + h.ToString("X02");
            }
            cmd += " 00";                                                   // XOR予約   memo 長さ不足はエラー10

            byte[] data = PreMaidUtility.BuildByteDataFromStringOrder(cmd); // IZMさんライブラリを使う場合はコウなる。
            _serialPort.Write(data, 0, data.Length);
        }