/*******************************/ using UnityEngine; using System.Collections; //Other libraries using System; using System.Threading; using System.Collections.Generic; using System.ComponentModel; //串口命名空间 using System.IO.Ports; using System.Text.RegularExpressions; using System.Text; public class ComTest : MonoBehaviour { /******定义一个串口字段*******/ private SerialPort sp; /*******定义三个外部接口***************/ public static float FangXiangJiao; public static float YangJiao; public static float XuanZhuanJiao; /*******定义三个角度存储的临时字段***********/ public static float _FangXiangJiao; public static float _YangJiao; public static float _XuanZhuanJiao; /***********三种角度的循环队列****************/ private Queue<float> AVEFangXiangJiao = new Queue<float>(); private Queue<float> AVEYangJiao = new Queue<float>(); private Queue<float> AVEXuanZhuanJiao = new Queue<float>(); private byte[] DataBuf; private bool flag = true; private bool showMessage = false;//显示消息 private bool open = false; private int NumberCount=0; private Thread SerialPortThread; private StringBuilder sbReadline; private string strOutPool = string.Empty; private Queue<string> queueDataPool; private Thread tPort; private Thread tPortDeal; /***********Unity初始化方法*****************/ private void Start () { sbReadline = new StringBuilder(); queueDataPool = new Queue<string>(); //DataBuf = new byte[10]; sp = new SerialPort("COM1", 9600, Parity.None, 8, StopBits.One); if (!sp.IsOpen){ // 开启串口 sp.Open(); open = true; } //开启2个线程分别用于接收数据和处理数据 tPort = new Thread(DealData); tPort.Start(); tPortDeal = new Thread(ReceiveData); tPortDeal.Start(); //StartCoroutine(DealData()); } /***************程序结束的时候,关闭串口**********************/ void OnApplicationQuit() { sp.Close(); //SerialPortThread.Abort();//终止进程 } /*************Update方法********************/ //一帧之调用多次 void FixedUpdate() { //定期回收垃圾 if (Time.frameCount % 120 == 0) System.GC.Collect(); //处理数据的线程停止,那么重启该线程 if (!tPort.IsAlive) { tPort = new Thread(DealData); tPort.Start(); } if (!tPortDeal.IsAlive) { tPortDeal = new Thread(ReceiveData); tPortDeal.Start(); } // StartCoroutine(DealData()); //Debug.Log( SerialPortThread.ThreadState); } /// <summary> /// 数据接收 进程, 使用队列来存储数据,避免数组的上锁和解锁问题 /// 设计模式 多线程+数据池 /// </summary> /// <returns> /// The data. /// </returns> /*****************数据接收*************************/ private void ReceiveData () { //读取数据,同时存入数据池中 //newline结束标记,同时使用readIlne()方法 try { //从输入缓存区中去读取第一次出现"AA"时的内容 Byte[] buf=new Byte[120]; if (open) sp.Read(buf,0,120); //如果没有接到到数据,就返回 if (buf.Length ==0) { return; } string sbReadline2str = string.Empty; if (buf != null) { for (int i = 0; i < buf.Length; i++) { sbReadline2str += buf[i].ToString("X2"); } } //sbReadline2str = System.Text.Encoding.Default.GetString(buf, 0,buf.Length); // Debug.LogError(sbReadline2str.ToString()); // sbReadline2str = buf.ToString(); // sbReadline2str = sbReadline.ToString(); //提取完整的一个数据包,压入数据池中(队列中) if (sbReadline2str.StartsWith("DB90")) { //分组数据包 string[] _str = Regex.Split(sbReadline2str, "55AA", RegexOptions.IgnoreCase); foreach (string s in _str) { if (s.Length == 16) { //数据进入队列 queueDataPool.Enqueue(s + "55AA"); //Debug.LogError(s+"55AA"); } } } else { sbReadline2str.Remove(0,sbReadline2str.IndexOf("DB90")+1); string[] _str = Regex.Split(sbReadline2str, "55AA", RegexOptions.IgnoreCase); foreach (string s in _str) { if (s.Length == 16) { //数据进入队列 queueDataPool.Enqueue(s + "55AA"); // Debug.LogError(s+"55AA"); } // Convert.ToByte(s,16); } } }catch (Exception ex) { Debug.LogError(ex); } //yield return null; } /***************处理出队的数据***********************/ private void DealData() { while (queueDataPool.Count != 0) {//循环检测队列 //队列中有数据 //处理出队的数据 //循环队列,首先加载第一包数据,只执行一次 if (flag) { //初始化第一组数据 for (int i = 0; i < 7; i++) { //出队列,同时移除这个数据 strOutPool = queueDataPool.Dequeue(); float[] jiaodu = DealReceivedDatanow(strOutPool); AVEFangXiangJiao.Enqueue(jiaodu[0]); AVEYangJiao.Enqueue(jiaodu[1]); AVEXuanZhuanJiao.Enqueue(jiaodu[2]); } FangXiangJiao = Caulation(AVEFangXiangJiao); YangJiao = Caulation(AVEYangJiao); XuanZhuanJiao = Caulation(AVEXuanZhuanJiao); flag = false; } else {/*采用7点平滑滤波,消除抖动现象.注意特殊点的处理360到0之间的数据*/ //没有存在跳变的现象,那就直接存储数据 for (int i = 0; i < 7; i++) { //出队列,同时移除这个数据。 //加载7包数据, strOutPool = queueDataPool.Dequeue(); //组合加载的数据,低字节在前,高字节在后,return 三个角度值 float[] jiaodu = DealReceivedDatanow(strOutPool); //角度循环队列更新最新进入的数据 AVEFangXiangJiao.Dequeue(); //角度循环队列在队尾插入新的数据 AVEFangXiangJiao.Enqueue(jiaodu[0]); //计算更新后的循环队列中的7个数据平均值 /*****************处理特殊数据************************/ int[] IntPanDuan = CheackDataTuBian(AVEFangXiangJiao); //如果存在着跳变的现象 if (IntPanDuan[0] == 1) {//保留队列前面的数据,移除后面的数据 float[] FXj = AVEFangXiangJiao.ToArray(); float sum = 0f; //获取要移除数据的索引值 int indexOfRemovingData = IntPanDuan[2]; for (int j = 0; j < indexOfRemovingData; j++) { sum += FXj[j]; } //计算平均值 FangXiangJiao = sum / indexOfRemovingData; } FangXiangJiao = Caulation(AVEFangXiangJiao); /******************处理特殊数据结束*************************/ /*******************************************/ /*同上*/ AVEYangJiao.Dequeue(); AVEYangJiao.Enqueue(jiaodu[1]); YangJiao = Caulation(AVEYangJiao); /*同上*/ AVEXuanZhuanJiao.Dequeue(); AVEXuanZhuanJiao.Enqueue(jiaodu[2]); XuanZhuanJiao = Caulation(AVEXuanZhuanJiao); /******************************************/ } } } } /************************检验特殊值***************************/ private int[] CheackDataTuBian(Queue<float> cheackQueue) { float[] cheackDataArrary = cheackQueue.ToArray(); //flag =0表示false; flag=1表示true int flag = 0; int[] BoolAndIndexOfNext=new int[2]; for (int count = 0; count < 6; count++ ) {float Previous =cheackDataArrary[count]; float Next = cheackDataArrary[count+1]; if (Mathf.Abs(Previous - Next)>350.0F && Mathf.Abs(Previous -Next)<360.0f) { flag = 1; BoolAndIndexOfNext[0] = flag; BoolAndIndexOfNext[1] = count + 1; break; } } return BoolAndIndexOfNext; } /*****************计算平均值****************************/ private float Caulation(Queue<float> AVEf) { float _f=0.0f; foreach (float f in AVEf) { _f+=f; } return _f/7; } /*****************处理来自数据池的数据**************************/ private float[] DealReceivedDatanow (string DataBuf) { //this is a whole frame data package,then convert the //First convert the Byte type into Char type // Debug.Log("--starting"); //Debug.Log("databuf----"+DataBuf); /*把16进制字符串转换成字节数组*/ byte[] returnBytes = new byte[DataBuf.Length / 2]; for (int i = 0; i < returnBytes.Length; i++) returnBytes[i] = Convert.ToByte(DataBuf.Substring(i * 2, 2), 16); /*消除抖动,因为眼镜传感器会不断的发送数据,即使是不动的状态下,也会有这样的现象*/ float[] jiaoduCollection =new float[3]; _FangXiangJiao = (float)((byte)returnBytes[2] + ((sbyte)returnBytes[3] << 8)) / 10; jiaoduCollection[0] =_FangXiangJiao; // Debug.Log("fx-->"+FangXiangJiao); _YangJiao = (float)((byte)returnBytes[4] + ((sbyte)returnBytes[5] << 8)) / 10; jiaoduCollection[1] =_YangJiao; // Debug.Log("yj-->"+YangJiao); _XuanZhuanJiao = -(float)((byte)returnBytes[6] + ((sbyte)returnBytes[7] << 8)) / 10; jiaoduCollection[2]=_XuanZhuanJiao; //Debug.Log("xz-->"+XuanZhuanJiao); //Debug.Log("--ending"); return jiaoduCollection; } /********************处理来自数据池的数据**************************/ private void DealReceivedData (string dataOutPool) { //读取串口的数据, 如果没有反应就会出现timeout 异常 // 数据格式:DB 90 AF 0C A2 FF 2C 00 55 AA //包头是DB 90 包尾:55 AA //一帧发送一个数据包 总大小10个字节, //int DataNumber; try { //读取数据 byte tempB; tempB= (byte)sp.ReadByte(); //Time.time; //Read the header first if (tempB ==219) { //Debug.Log("i get the 0XDB"+(byte)0xDB); DataBuf[0]=tempB; tempB= (byte)sp.ReadByte(); if (tempB ==144) DataBuf[1]=tempB; int DataNumber=2; while(DataNumber<10){ tempB= (byte) sp.ReadByte(); DataBuf[DataNumber]=tempB; DataNumber++; } } //read out the input data //cheack the header and tail for a data package. //this is a whole frame data package,then convert the //First convert the Byte type into Char type // Debug.Log("--starting"); FangXiangJiao=(float)(DataBuf[2]+((sbyte)DataBuf[3]<<8))/10; /* while(NumberCount <7) { //_FangXiangJiao = }*/ // Debug.Log("fx-->"+FangXiangJiao); YangJiao = (float)(DataBuf[4]+((sbyte)DataBuf[5]<<8))/10; // Debug.Log("yj-->"+YangJiao); XuanZhuanJiao = (float)(DataBuf[6]+((sbyte)DataBuf[7]<<8))/10; // Debug.Log("xz-->"+XuanZhuanJiao); //Debug.Log("--ending"); } catch (Exception e) { Debug.LogError(e); } //Debug.Log("starting"); foreach(byte b in DataBuf) { //Debug.LogError(Convert.ToString(b,16)); } //Debug.Log("ending"); // yield return null; } /*********************************************/ //在没有开启串口设备电源的时候,显示消息窗口,开启电源之后,点击确定按钮 private void OnGUI() { if (showMessage) { GUI.BeginGroup(new Rect(Screen.width/2-100,Screen.height-60,400,400)); GUI.Label(new Rect(Screen.width / 2 - 80, Screen.height - 60,200,150), "请打开串口设备电源!然后点击确定"); if (GUI.Button(new Rect(Screen.width / 2 -80, Screen.height +95 , 100, 100), "确定")) { open = true; } GUI.EndGroup(); } } |
|