2023年5月7日 星期日

Unity 串列埠連接Arduino:測試(二)

 在上一篇文章Unity 串列埠連接Arduino:測試(一),雖然已經將Arduino與Unity的專案連接成功,可以透過Arduino端的搖桿來控制Unity中的物件旋轉與否;表面上成功,但實際上資料傳送有不少問題存在。

在Arduino傳送資料與Unity中C#讀取資料的過程中,實際上有產生錯誤。但在上篇文章的測試中,僅傳輸0或1,不容易觀察到資料錯誤的狀況。

在本篇文章,我們將Arduino程式修改如下:

-----------------------------------------------------
int SK, SX, SY;
String SSK, SSX, SSY; 
void setup() {
  // put your setup code here, to run once:
    Serial.begin(115200);
    pinMode(12, INPUT);
}

void loop() {
  // put your main code here, to run repeatedly:
     SK=digitalRead(12);
     SX=analogRead(A0);
     SY=analogRead(A1);
     SSK=String(SK);
     SSX=String(SX);
     SSY=String(SY);
     Serial.println("B,"+SSK+","+SSX+","+SSY+",D");
     //Serial.println(SSK);
     delay(7);
}

-----------------------------------------------------

Serial.println("B,"+SSK+","+SSX+","+SSY+",D");

傳送SSK、SSX、SSY這3個資料,分別對應按鈕、搖桿左右搖、搖桿前後搖三個資料,以","隔開,並在前面加入一個開始字元B,在結束加入一個結束字元D。

在Serialsetup.cs中修改如下:

-----------------------------------------------------
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.IO.Ports;
using System;

public class Serialsetup : MonoBehaviour
{
    public static SerialPort sp = new SerialPort();
    private string V = "1";
    private string[] words = null;
    public GameObject Canv, B_C, B_D;
    public Dropdown Dp1;
    private string[] Com_name=null;
    private List<string> list;
    private bool b_conv = true;
    // Start is called before the first frame update
    public void AddSerial()
    {        
        PN = Dp1.options[Dp1.value].text;
        if (!sp.IsOpen)
        {
            B_C.SetActive(true);
            B_D.SetActive(false);
        }
        
    }

    public void Connect()
    {
        try
        {
            sp.BaudRate = 115200;
            sp.Parity = Parity.None;
            sp.DataBits = 8;
            sp.StopBits = StopBits.One;
            sp.PortName = PN;
            sp.ReadTimeout = 2;
            sp.Open();            
            B_D.SetActive(true);
            B_C.SetActive(false);
        }
        catch (System.Exception)
        {
        }
    }
    public void Disconnect()
    {
        sp.Close();
        B_C.SetActive(true);
        B_D.SetActive(false);
    }
    void Start()
    {

        B_C.SetActive(false);
        B_D.SetActive(false);
        Com_name = SerialPort.GetPortNames();
        list= new List<string>(Com_name);
        Dp1.AddOptions(list);
    }
    // Update is called once per frame
    void Update()
    {
        if (sp.IsOpen)
        {
            if (Input.GetKeyDown(KeyCode.Escape))
            {
                b_conv = !b_conv;
                Canv.SetActive(b_conv);
            }
        }
        else
        {
            Canv.SetActive(true);
        }        
    }
    private void ReadandSplit()
    {
        V = sp.ReadLine();
        Debug.Log(V);
    }
    private void FixedUpdate()
    {
        if (sp.IsOpen)
        {
            try
            {
                ReadandSplit();
                //sp.DiscardInBuffer();
            }
            catch (System.Exception)
            {
            }
        }
    }

}

----------------------------------------------------------------

sp.DiscardInBuffer()先不執行,觀察每一筆資料,在Console視窗有時可以看到錯誤資料

如上圖紅框中所示。(但不確定什麼原因造成,有可能是Arduino端造成的)

由於我們採用的傳送是固定時間間隔傳送和接收,如果Aruino傳送時間間隔太短,Unity這端讀取資料時間間隔為0.02sec(FixedUpdate()設定為每0.02秒執行一次),資料累積的結果,將造成Unity在後續應用這些資料做控制時有Lag的現象。但如果Arduino傳送資料的間隔太長,則會造成Unity讀不到資料(這裡利用例外處理避免掉錯誤引起的一些困擾)。

因此,我們採用的方式是Arduino的dealy設為7。並且在ReadLine讀取成功之後,將inBuffer的資料丟掉(sp.DiscardInBuffer())。不過,使用DiscardInBuffer()後,資料讀取不正確的比例上升很多。

為了避免資料不正確產生後續的錯誤,當讀取資料不正確,則再讀取下一行,最多讀取4次。利用Split來拆解字串,拆解後檢查第一個字串(words[0])和第五個字串(words[4]),如分別為B和D,視為正確。

ReadandSplit()修改如下
-----------------------------------------------------
    private void ReadandSplit()
    {
        for (int i=1; i<5; i++)
        {
            V = sp.ReadLine();
            words =V.Split(',');
            if (words[0] == "B" && words[4] == "D")
            {                
                Debug.Log(i);
                break;
            }
        }
    }

----------------------------------------------------------------

由Console視窗觀察,最多讀取2次即可成功。(但仍然有產生錯誤的狀況)

讀取第一次即成功和讀取第二次成功的比例與Arduino的dealy時間(間隔)有關。

最後ReadandSplit()、FixedUpdate()修改如下
-----------------------------------------------------
    private void ReadandSplit()
    {
        for (int i=1; i<5; i++)
        {
            V = sp.ReadLine();
            words =V.Split(',');
            if (words[0] == "B" && words[4] == "D")
            {
                ComData.R = Int32.Parse(words[1]);
                ComData.X = Int32.Parse(words[2]);
                ComData.Y = Int32.Parse(words[3]);
                break;
            }
        }
    }
    private void FixedUpdate()
    {
        if (sp.IsOpen)
        {
            try
            {
                ReadandSplit();
                sp.DiscardInBuffer();
            }
            catch (System.Exception)
            {                
                Debug.Log("not good");
            }
        }
    }

----------------------------------------------------------------

上面程式中,如果有某一次FixedUpdate()在執行ReadandSplit()、sp.DiscardInBuffer()失敗時,則顯示"not good"。

ComData.cs修改如下
-----------------------------------------------------
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

static class ComData
{
    public static int R=1;
    public static int X = 512;
    public static int Y = 512;
}
----------------------------------------------------------------

Ctrlmotion.cs修改如下
-----------------------------------------------------
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Ctrlmotion : MonoBehaviour
{
  public float rspeed = 5f;
    void Start()
    {
}

    // Update is called once per frame
    void Update()
    {
        transform.position = new Vector3((-ComData.X + 512)/10*0.1f, 0, (ComData.Y - 512)/10*0.1f);
        if (ComData.R==0)
{
transform.Rotate(Vector3.up * Time.deltaTime*rspeed);
}
    }
}
----------------------------------------------------------------
在20分鐘的測試中只出現3次not good的狀況。但讀取第二次才成功的比例不少。
Cube由搖桿控制的狀況也OK,前後左右移動、旋轉均沒有明顯延遲的情況產生。

沒有留言:

張貼留言