在上一篇文章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,前後左右移動、旋轉均沒有明顯延遲的情況產生。
沒有留言:
張貼留言