Unity+YDLIDAR G4+c# YDLIDAR G4 型号雷达数据输出

近期研究 YDLIDAR G4 雷达,网上搜了下没有 Unity 做的,遂自己踩坑,下面是自己一些小小的总结。

辅助资料:

  1. 激光雷达 G4 使用手册
  2. 激光雷达 G4 数据手册
  3. 激光雷达 G4 开发手册

主要使用上面三个资料,可以从官网进行下载。注意选择正确的雷达型号 G4 文档。

http://www.ydlidar.cn/cn/download
在这里插入图片描述

辅助资料重点信息:

激光雷达 G4 使用手册

  1. 开发套件。可以看到雷达上有一个 USB Type - C 接口, 还有一根突出来的线,线上有五个接口,他们叫物理接口
    在这里插入图片描述
    两个接口都可以直接和电脑电脑连接进行使用开发,即
  • 一种是使用自带的 USB 数据线连接雷达上的USB Type -C 接口进行开发,
  • 一种是将物理接口和 USB 转接板进行连接,USB 转接板一边又五个小针孔的就是和物理接口进行连接,一边有两个接口,这两个接口上面有刻字(Power,Data),Data 用 Type - C 数据线和电脑连接即可(原理上 Power 是不需要连接的,供电充足的话雷达会转一下,不转可能雷达供电不足,所以扯出一根安卓的充电线插上)。
  • 注意:使用物理接口连接的时候,连接物理接口和 USB 转接板不要太用力,不然拔不出来,测试的时候太用力拔不出来咨询了客服小姐姐,左右晃动温柔点拔出来,否则接口的针会歪了。
  1. 驱动安装。
  • 可以从官网下载也可以点此处连接进行下载(自己上传连接)工具包 http://www.ydlidar.cn/cn/download
    在这里插入图片描述
    解压后工具 —— Tool —— Driver—— USB 串口驱动,USB 转接口驱动。分别对应 Type-c 接口连接的驱动以及使用 USB 转接板连接的驱动。我使用的是 USB 转接板,尝试过 Type-c 接口驱动,没有成功,后来用的 USB 转接板,而且后者的驱动安装也比较简单。使用手册中也用的这个案例。按照使用手册进行安装即可。
  1. 使用评估软件。同样也是在刚才下载的工具包中,工具——Tool——PointCloudViewer——Windows ——点击 exe 直接运行即可。不过首先保证上面驱动安装成功,在设备管理器中可以看到对应的串口。
  2. PointCloudViewer.exe 评估软件使用注意:
  • 每次先插上线,然后运行 Point CloudViewer
  • 使用的过程中出现串口阻塞,雷达异常的报错。解决办法:每次测试结束后,一定要先点击评估软件上的暂停按钮,再把雷达从电脑上拔下来。
  1. 我用的是 Windows 系统,所以使用 Linux 系统的自行测试。
    激光雷达 G4 数据手册

  2. 看接口定义。即可看到 G4 对外提供两个接口, USB Type- C 和 PH2.0-5P(物理接口),使用时,两者选其一。

  3. 极座标系定义。中心为极点,角度顺时针为正,零位角位于 G4 PH2.0-5P 接口线的出线口方向。

  4. 开发和支持。可以按照文档网址进行下载 SDK 开发包和 Ros 开发包(Ros 针对的是 Linux 系统的,Windows 用户忽略)

  5. 解压的 SDK 包中有三个东西
    在这里插入图片描述
    SDK 驱动包可以先不用看。这个是你自己重新建项目的时候要使用雷达,需要将这个 SDK 包引用到项目中。
    VS 2015 工程实例。是一个完整的项目,可以直接运行。使用 C++ 写的,上面的 SDK 包也是用 C++ 写的。
    YDLIDAR SDK 使用手册。看 上面两个工程的时候可以参考一下,这个里面是一些封装的函数。
    主要看 VS 2015 工程实例

  6. VS2015 工程实例。
    在这里插入图片描述

  • 解压里面的 YDLIDAR,运行即可。
  • YDLIDAR SKD WINDOWS 使用手册
    • SDK 文件引用。YDLIDAR 项目中已经引用了这个 SDK ,不需要你重新引用。
    • 常见问题。
      • 该项目用的是 VS2015 编写的,我用的是 VS 2017,导入进来,报错:vs2017 找不到源文件 stdio.h 。原因是原来的项目所采用windows SDK 已经发生了变化,解决办法:打开解决方案管理器,选择对应项目,右击,属性,配置属性,常规,Windows SDK 版本选择你现在的版本即可。现在版本的查看C:\Program Files (x86)\Windows Kits\10\Include\10.0.10240.0 最后这一串数字就是你当前的版本。
      • 编译 Demo 工程有报错。按照文档说的,使用其他版本的 VS 时,请选择对应的平台工具集。解决办法:解决方案资源管理器中选择需要的项目,右击,属性,配置属性,常规,平台工具类,选择 v140 以上,我安装的是Visual Studio 2017 (v141),选择这个相当于升级了,选择应用之后,点击解决方案资源管理器总文件夹,解决方案,右击,选择重定解决方案目标即可。或者在VS 上面的菜单栏选择项目,第一个选项就是重定解决方案目标
      • 修改代码中串口号和波特率。找到 YDLIDAR_DEMO.cpp
        在这里插入图片描述
        只需要修改入口函数 main 方法中的这两个值即可。com5 是我在设备管理器上的串口名字,一般都是 com3,你可以从你的设备管理器上看到,修改成对应的即可。230400 是 G4 这款雷达的波特率。其他型号的雷达波特率可以咨询官网咨询客服。
      • 修改完成即可运行。

好了,上面的都是一系列的前期准备以及官网给的测试工具和 c++ 测试案例。下面开始 Unity 的。

我本来想把 YDLIDAR 案例做成 dll 文件,引用到 Unity 中,为此还学了下 C++ 生成 dll 文件的方法以及 dll 导入到 Unity 项目中如何应用。事实证明,走了歪路,没有成功。后来这个不行我就想着把官网给的 SDK 导入到 Unity 中进行开发,不过我没有尝试,主要我对 C++ 代码不熟悉,导入进去也不知道怎么调用。后来主管给了新的思路,网上很多 Unity 读串口信息的方法,你直接操作串口通信,然后根据开发文档中的数据进行解析,就是跳过给的 SDK 包,自己写函数进行解析。恩,下面就是这个方法。

  1. 首先,我们用串口调试工具看一下串口返回出来的数据。

我自己下的友善串口调试工具,不过各种问题,后来问了官网的技术支持,他给了我另一个串口调试工具。下面是链接:需要自取。(上传串口调试工具)

  • 设置串口调试工具
    在这里插入图片描述
    设置界面最好和这个一样,尤其是红色方框中的。
  • 点击串口操作,打开串口,输入 A560 (扫描命令),点击发送。就可以看到上面的绿色数据即串口返回出来的数据,然后输入 A565 (关闭扫描命令),点击发送即可停止,然后关闭串口即可。
  1. 激光雷达 G4 开发手册

    • 通信机制。外部设备(例如 Unity)发送一个命令给雷达,雷达解析命令,返回对应的数据(应答内容)。雷达根据得到的命令切换工作状态,外部设备根据返回的数据解析,得到角度,距离等信息。
    • 系统命令。外部设备可以发送给雷达的命令。上面串口通信工具发送的命令就是根据这个来的。首字节统一为 0xA5 ,其他的后面不同命令对应不同状态。
    • 系统报文。
      • 着重看三种应答模式。表格中也有各种命令对应的应答模式。
      • 系统报文数据协议。起始标志:由于 A560 为扫描模式,持续应答,输出数据比较多。所以可以通过输入 A590 (获取设备信息),可以看到返回的命令中前两个为 A55A,后面的数据根据协议常看每个字节代表什么。
      • **注意 1 :G4 的数据通信采用的是小端模式,低位在前。**这个不熟悉的可以 Google ,简单解释下: 一个 16 进制的数字,A5 11 ,从左到右,A5 属于高位,11 属于低位,小端模式是先存储低位,再存储高位,即小端模式的表示方法就是 11 A5,大端模式和小端模式相反即可,表示方法 A5 11。所以,在雷达返回的应答内容解析的时候时刻注意它采用的是小端模式,进行位置调整后再进行解析
      • 数据协议。主要看 A560 ,A565。扫描模式和停止扫描。
      • 扫描命令。第 6 个字节高 2 位为 01,因此应答模式取值为 0x1,为持续应答长度(此处我不太清楚,只是按照其他命令这个位置都是 0 ,扫描模式此处不为 0 进行区分)。应答内容是类型码后面的东西,为点云数据,即串口调试工具中后面一直输出的内容。
      • 应答内容。固定开头为 0x55AA;包类型分为两种,可以看到点云数据包这个位置是 0x00;采样数量:这个数据和 Si 的数量相等(注意 Si 是两个字节,即两个字节表示一个数据);FSA 起始角,两个字节;LSA 结束角,两个字节;校验码,两个字节。
      • 零位解析。按照文档来。
      • 距离解析。同样注意 Si 两位,小端模式,计算出来的数据单位为 mm,Unity 中默认单位是 m。
      • 角度解析。先不用看 C 校验位。一级解析:按照文档来。右移一位相当于这个数字除以 2 ,所以解析的时候搞不清楚右移的直接除以 2 就可以。二级解析:文档中写的反三角函数,我刚开始没弄懂这个公式。文档中的这个写法是 MatLab 中反正切的写法。tan 函数参数范围 (-PI/2,PI/2),值的范围是 (- 无穷,+无穷),反正切函数就是正切函数的参数和值的范围换过来。Unity 解析的时候使用 Mathf.Atan(文档中括号中计算的值) 或者 Math.Atan(文档中括号中计算的值),极端得到的记过是弧度,此处角度修正值需要的是角度,所以得到的值再转换为角度即可。
      • 校验码解析。看文档中的 图 7 ,PHFSA,1,S2,LSA,都是两个字节,而通过上面的应答内容解析表可以看到 CT,LSN 分别只有一个字节,而校验码采用的是双字节异或,所以把 CT,LSN 组合成两个字节进行计算。**验证方法:**按照 图 7 中的数据对每一行的两个字节进行异或得到后面的 C1 到 C -end,然后再将 C1 到 C - end 从头到尾进行异或得到的结果(CS)和应答内容中的校验码位置的数据进行比较,相等则表示接受的这条数据是正确的。
      • 注意 2:可以先通过串口调试工具查看输出的点云数据,可以复制出来部分数据观察。扫描命令发送后,雷达返回的应答数据中:起始标志(A55A),应答长度,应答模式,类型码只输出一次,应答内容则是持续发送(会只看到一个 A55A,而看到个 55AA(应答内容固定开头)),即扫描模式的持续应答代表的是应答内容持续应答。
      • A565 注意,当前系统处于扫描状态时,发送 A565 停止扫描。不会应答,即没有返回内容。只有停止命令能在扫描模式时进行发送。其他命令都只能在待机状态(即没有扫描)的时候进行发送,例如查看设备信息等等。这个在文档的使用注意中可以看到。
  2. Unity 具体解析代码。
    好了,终于到了最后解析的地方。在解析的时候要根据上面的开发手册进行操作。

ISerialCommunication.cs 接口 定义方法,串口连接,串口断开,发送信息

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Assets.SerialPortUtility.Interfaces
{
    public interface ISerialCommunication
    {
        void Connect(int baudrate, string portName);
       
        void Disconnect();
        void SendMessage(byte[] byteArray);
    }
}

SerialCommunication.cs 用于串口打开,关闭,发送信息,接收信息具体实现

using System;
using System.Text;
using System.Collections;
using System.Collections.Generic;
using System.IO.Ports;
using System.Threading;
using UnityEngine;
using Assets.SerialPortUtility.Scripts;

public delegate void SerialPortMessageEventHandler(byte[] sendData);
public delegate void SerialPortSendMessageReportHandler(byte[] sendData);
public class SerialCommunication
{
    // 从串口发出消息事件
    public event SerialPortMessageEventHandler SerialPortMessageEvent;
    // 给串口发消息事件
    public event SerialPortSendMessageReportHandler SerialPortSendMessageReportEvent;
    private SerialPort serialPort;
    private Thread threadReceive;
    // 储存接收到的消息
    private List<byte> buffer = new List<byte>(4096);
    [NonSerialized]
    private List<byte> listReceive = new List<byte>();


    private int lsnIndex = 3;
    public SerialCommunication(SerialPort serialPort)
    {
        this.serialPort = serialPort;
    }
    public SerialCommunication(string portName, int boudrate)
    {
        serialPort = new SerialPort(portName, boudrate);
    }

    public void OpenSerialPort()
    {
        
        //Debug.Log("读取端口" + ConvertXml._instance.COM);
        serialPort.Open();
        serialPort.ReadTimeout = 1;
        threadReceive = new Thread(ListenSerialPort);
        threadReceive.IsBackground = true;
        threadReceive.Start();

    }
    public bool IsSerialPortIsOpen()
    { 
        return serialPort.IsOpen;
    }
    public void CloseSerialPort()
    {
        if(threadReceive!=null)
        {
            threadReceive.Abort();//关闭线程
            threadReceive = null;
            serialPort.Close();//关闭串口
            serialPort.Dispose();//将串口从内存中释放掉,注意如果这里不释放则在同一次运行状态下打不开此关闭的串口
            
        }
        Debug.Log("close thread");
    }


    /// <summary>
    /// 监听串口,读取串口消息
    /// </summary>
    private void ListenSerialPort()
    {
        //string recvData = "";
        //int flag = 0;
        while (serialPort != null && serialPort.IsOpen)
        {
            try
            {
                #region 原写法
                //int bufferSize = serialPort.ReadBufferSize;
                ////										
                //byte[] buf = new byte[bufferSize];
                //int count = serialPort.Read(buf, 0, bufferSize);
                //if (count > 9)
                //{
                //    if (SerialPortMessageEvent != null && SerialPortMessageEvent.GetInvocationList().Length > 0) // If somebody is listening
                //    {
                //        SerialPortMessageEvent.Invoke(buf);// Invoke方法防止主线程拥堵冲突
                //    }

                //}
                #endregion

                #region 使用ReadByte()的写法 加判断包头和包尾               
                
                byte buf = Convert.ToByte(serialPort.ReadByte());
                buffer.Add(buf);
                while (buffer.Count >= 2)
                {
                    if (buffer[0] == 0xAA&& buffer[1] == 0x55)
                    {
                        //Debug.Log("内层收到未处理消息");
                        
                        if (buffer.Count < 4)
                        {
                            break;
                        }
                        
                        int numLen = buffer[3];
                        
                        if(buffer.Count<numLen*2+10)
                        {
                            break;
                        }
                        //Debug.Log("numLen: " + numLen);
                        Data_Process(numLen, buffer);
                        //一条完整数据  存储  进行处理  移除前面一条完整数据

                        buffer.RemoveRange(0, numLen * 2 + 10);

                    }
                    else
                    {
                        buffer.RemoveAt(0);
                    }
                }
                //Debug.Log("buffer.Count: " + buffer.Count + "  " + RunSerial.byteToHexStr(buffer.ToArray()));
                #endregion

            }
            catch (System.Exception e)
            {
                //Debug.LogWarning(e.Message);
            }
        }
    }


    void Data_Process(int numLen,List<byte> bufferSrc)
    {
        byte[] readBuffer = null;
        readBuffer = new byte[numLen*2+10 ];
        bufferSrc.CopyTo(0, readBuffer, 0, numLen * 2 + 10);
        SerialPortMessageEvent(readBuffer);// Invoke方法防止主线程拥堵冲突
    }

    #region
    /// <summary>
    /// ASCII码转字符:
    /// </summary>
    /// <param name="asciiCode"></param>
    /// <returns></returns>
    public static string Chr(int asciiCode)
    {
        if (asciiCode >= 0 && asciiCode <= 255)
        {
            System.Text.ASCIIEncoding asciiEncoding = new System.Text.ASCIIEncoding();
            byte[] byteArray = new byte[] { (byte)asciiCode };
            string strCharacter = asciiEncoding.GetString(byteArray);
            return (strCharacter);
        }
        else
        {
            throw new Exception("ASCII Code is not valid.");
        }
    }

    
    /// <summary>
    /// 使用事件触发方式来实现串口数据的读取
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        byte[] Resoursedata = new byte[serialPort.BytesToRead];
        int count =  serialPort.Read(Resoursedata, 0, Resoursedata.Length);//在此就可以读取到当前缓冲区内的数据
        //执行数据操作
        serialPort.DiscardInBuffer();//丢弃传输缓冲区数据
        serialPort.DiscardOutBuffer();//每次丢弃接收缓冲区的数据
        if (count > 0)
        {
            if (SerialPortMessageEvent != null && SerialPortMessageEvent.GetInvocationList().Length > 0) // If somebody is listening
            {
                SerialPortMessageEvent.Invoke(Resoursedata);// Invoke方法防止主线程拥堵冲突
            }
        }
    }
    #endregion


    /// <summary>
    /// 给串口发消息
    /// </summary>
    /// <param name="byteArray"></param>
    /// <returns></returns>
    public bool SendMessageFromSerialPort(byte[] byteArray)
    {
        if (serialPort != null && serialPort.IsOpen == true)
        {

            serialPort.Write(byteArray, 0, byteArray.Length);

            if (SerialPortSendMessageReportEvent != null && SerialPortSendMessageReportEvent.GetInvocationList().Length > 0) // If somebody is listening
            {
                SerialPortSendMessageReportEvent(byteArray);
            }
            return true;
        }
        else
        {
            return false;
        }
    }


}

SerialCommunicationFacade.cs 具体的应答内容解析

using Assets.SerialPortUtility.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;

namespace Assets.SerialPortUtility.Scripts
{
   public class SerialCommunicationFacade : MonoBehaviour,ISerialCommunication
    {
        SerialCommunication serialCom;
        private string reciveStr;
        private string oldStr;

        float fsaAngle;
        float lsaAngle;
        float angle;//中间角


        public void Connect(int baudrate, string portName)
        {
            serialCom = new SerialCommunication(portName, baudrate);
            serialCom.OpenSerialPort();// 打开串口
            // 绑定方法触发,监听读取串口
            serialCom.SerialPortMessageEvent += SerialCom_SerialPortMessageEvent;
            // 绑定方法触发,给串口发消息
            serialCom.SerialPortSendMessageReportEvent += SerialCom_SerialPortSendMessageReportEvent;
        }
        public void Disconnect()
        {
            serialCom.CloseSerialPort();
            Debug.Log("Serial Disconnected");
        }
        public void SendMessage(byte[] byteArray)
        {
            try
            {
                if (serialCom.IsSerialPortIsOpen() == true)
                {

                    serialCom.SendMessageFromSerialPort(byteArray);
                    //Debug.Log("Message Sended");
                }
                else
                {
                    Debug.Log("Message Send Failed!");
                }
            }
            catch (Exception)
            {

                
            }
           
        }

        /// <summary>
        /// 监听给串口发的消息
        /// </summary>
        /// <param name="sendData"></param>
        private void SerialCom_SerialPortSendMessageReportEvent(byte[] sendData)
        {
            string text = RunSerial.byteToHexStr(sendData);
            Debug.Log("Message Send From Serial.. Message =>  " + text.ToString());
        }

        /// <summary>
        /// 串口发过来的消息
        /// </summary>
        /// <param name="sendData"></param>
        private void SerialCom_SerialPortMessageEvent(byte[] sendData)
        {
            Debug.Log("____________shoudaole _____________");

            reciveStr = RunSerial.byteToHexStr(sendData);
            Debug.Log("Message Coming From Serial.. Message =>  " + reciveStr.ToString());//打印一条数据信息

            Data_ResponseContentProcess(sendData);





            //if (!reciveStr.Contains("D") || !reciveStr.Contains("m"))
            //{
            //    return;
            //}
            ////Debug.Log("收到的数据:" + reciveStr.ToString());
            //if (reciveStr == oldStr)
            //{
            //    return;
            //}
            ////AllUiManger._Instance.DealReceiveString(reciveStr.ToString());
            //oldStr = reciveStr;

        }

        List<float> angles = new List<float>();//一级解析角度
        List<float> distances = new List<float>();//所有的距离
        List<double> angleCorrects = new List<double>();//角度偏差
        List<float> correctedAngles = new List<float>();//二级解析角度

        public static bool isSaveAll = false;//距离和角度都存进去了

        public static Dictionary<float, float> angleAndDistances = new Dictionary<float, float>();
        /// <summary>
        /// 应答数据处理S
        /// </summary>
        ///数据处理  
        ///responseData[0] AA responseData[1]  55  数据包头    
        ///responseData[2]  包类型   
        ///responseData[3]   采样数量  
        ///responseData[4] responseData[5]  起始角  
        ///responseData[6] responseData[7] 结束角
        ///responseData[8]  responseData[9]校验码
        ///responseData[................] 采样数据  两位为一个数据
        /// <param name="responseData"></param>
        void Data_ResponseContentProcess(byte[] responseData)
        {
            //零位解析  该数据包中 LSN = 1,即 Si 的数量为 1,S1 = 零位距离数据 ;
            //FSA = LSA = 零位角度数据; 其距离和角度的具体值解析参见距离和角度的解析
            if (responseData[2] == 0x01 && responseData[3] == 1)
            {
                //AA55 01 01 DFA0 DFA0 AB54 0000
                //零位距离

                string disStr = BinaryConversion(responseData[11], responseData[10]);
                int dis = Convert.ToInt32(disStr, 16);
                float Distancezero = dis / 4;

                //AA55 01 01 7958 7958 AB54 0000
                //起始角度  结束角度
                
                string fsaStr = BinaryConversion(responseData[5], responseData[4]);
                string lsaStr = BinaryConversion(responseData[7], responseData[6]);

                Debug.Log(fsaStr);
             
                int fsa = Convert.ToInt32(fsaStr, 16);
                Debug.Log(fsa);
                float fsaAnglezero = (fsa / 2) / 64;//起始角计算公式  右移一位除以 64
                

                string debugMsg = string.Format("零位解析 距离 = {0}, FSA = {1}, LAS = {2} ", Distancezero, fsaAnglezero, fsaAnglezero);
                Debug.Log(debugMsg);

            }

            // AA55 000F FD27 F129 1252 
            //00 000000000000000000000000000000000000000000000000000000B406
            //点云数据包
            if (responseData[2] == 0x00)
            {
                Debug.Log("dianyunshuju ---------------");
                Debug.Log(responseData[3]);
                byte[] dataDis = new byte[responseData[3]*2];
                

                Array.Copy(responseData, 10, dataDis, 0, responseData[3] * 2);
               
               
                //距离解析   两位为一个距离数据  距离开始不对,注意距离数据是去除掉前十位之后的数据
                for (int i = 10; i < responseData[3]*2+10; i++)
                {
                    string distanceStr = BinaryConversion(responseData[i+1], responseData[i]);
                    int dis = Convert.ToInt32(distanceStr, 16);
                    float Distance = dis / 4;
                    distances.Add(Distance);
                    Debug.Log("距离解析:  距离 Distance = " + Distance);
                    i++;
                }


                //角度解析
                //一级解析  注意小端模式
                string fsaStr = BinaryConversion(responseData[5], responseData[4]);
                string lsaStr = BinaryConversion(responseData[7], responseData[6]);

               
                int fsa=Convert.ToInt32(fsaStr, 16);
                fsaAngle = (fsa / 2) / 64;//起始角计算公式  右移一位除以 64
                angles.Add(fsaAngle);
                Debug.Log("角度一级解析:  FSA = " +fsaAngle );

                int lsa = Convert.ToInt32(lsaStr, 16);
                lsaAngle = (lsa / 2) / 64;//起始角计算公式  右移一位除以 64
                angles.Add(lsaAngle);
                Debug.Log("角度一级解析:  LSA = " + lsaAngle);

                //中间角  
                float diffAngle = lsaAngle - fsaAngle;
                for (int i = 2; i < responseData[3]; i++)
                {

                    angle = diffAngle / (responseData[3] - 1) * (i - 1) + fsaAngle;
                    angles.Insert(i-1, angle);
                    Debug.Log("角度一级解析:  Angle 中间角 = " + angle);
                }


                double angleCorrect;
                //二级解析   偏差角
                foreach (var item in distances)
                {
                    if(item==0)
                    {
                        angleCorrect = 0;
                       
                    }
                    else
                    {
                        float mid = 21.8f * (155.3f - item) / (155.3f * item);//
                        angleCorrect = Mathf.Atan((float)mid);//反正切函数参数范围(-无穷,+ 无穷)  Mathf.Atan 和 Math.Atan 返回的都是弧度。此处需要得到角度 
                        angleCorrect = (180/Math.PI) * angleCorrect;//弧度转角度  得到偏差角度
                    }
                    angleCorrects.Add(angleCorrect);
                }

                for (int i = 0; i < angleCorrects.Count; i++)
                {
                    float correctedAngle = angles[i] + (float)angleCorrects[i];
                    Debug.Log("角度二级解析:  所有角度 = " + correctedAngle);
                    correctedAngles.Add(correctedAngle);
                }

                Debug.Log("angleCorrects.Count,distances.Count:    " + angleCorrects.Count + "   " + distances.Count);
                for (int i = 0; i < responseData[3]; i++)
                {
                    
                    angleAndDistances.Add(correctedAngles[i], distances[i]);//距离计算出的是毫米,Unity 中单位默认是米
                }
                isSaveAll = true;
                angles.Clear();
                distances.Clear();
                angleCorrects.Clear();//注意 LIST 要进行清空  否则数据出错
                correctedAngles.Clear();


                //校验码解析

            }

        }


        /// <summary>
        /// 进制转换以及字符串重组
        /// </summary>
        string BinaryConversion(byte one,byte two)
        {
            string returnStr;
            string oneStr;
            string twoStr;

            if (one<16)
            {
               oneStr="0"+Convert.ToString(one, 16);//十进制转十六进制  byte 出来的直接是十进制
            }
            else
            {
                oneStr =  Convert.ToString(one, 16);
            }
            if(two<16)
            {
                twoStr = "0" + Convert.ToString(two, 16);
            }
            else
            {
                twoStr =  Convert.ToString(two, 16);
            }

            returnStr = oneStr + twoStr;
            return returnStr;

        }


    }
}

RunSerial.cs 程序运行入口,解析出来的数据可以在控制台打印出来

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace Assets.SerialPortUtility.Scripts
{
    public class RunSerial : MonoBehaviour {

        SerialCommunicationFacade facade;
        private string portName = "COM5";
        private int baudRate = 230400;
        byte[] sendMsg;

        //UI 用于显示
        public GameObject img;
        public GameObject canvas;

        List<GameObject> imgs = new List<GameObject>();
        int MaxLength = 30;

        List<float> angless;



        private void Awake()
        {
            for (int i = 0; i < MaxLength; i++)
            {
                GameObject obj = GameObject.Instantiate(img);
                obj.transform.SetParent(canvas.transform);
                obj.SetActive(false);
                imgs.Add(obj);

            }
        }
        void Start() {
            facade = this.GetComponent<SerialCommunicationFacade>();
            facade.Connect(baudRate, portName);
            sendMsg = strToToHexByte("A560");
            facade.SendMessage(sendMsg);
        }

        Vector2 GetPos(float angle, float distance)
        {
            Vector2 pos;
            pos = new Vector2(distance * Mathf.Sin(angle), distance * Mathf.Cos(angle));
            return pos;
        }

        void Update()
        {
            if (SerialCommunicationFacade.isSaveAll)
            {

                //Unity在Dictionary中删除修改元素时出现InvalidOperationException: out of sync  出现这个错误
                //foreach (var item in SerialCommunicationFacade.angleAndDistances)
                //{
                //    BuildObj(item.Key, item.Value);
                //}
                //angless = new List<float>(SerialCommunicationFacade.angleAndDistances.Keys);
                //for (int i = 0; i < angless.Count; i++)
                //{
                    //float distance = SerialCommunicationFacade.angleAndDistances[angless[i]];
                    //BuildObj(angless[i], distance);
                //}
                SerialCommunicationFacade.angleAndDistances.Clear();
                angless.Clear();

                SerialCommunicationFacade.isSaveAll = false;

                //SerialCommunicationFacade.angles.Clear();
                //SerialCommunicationFacade.distances.Clear();
            }
        }

        
       

            private void OnApplicationQuit()
        {

            sendMsg = strToToHexByte("A565");
            facade.SendMessage(sendMsg);
            facade.Disconnect();
        }
        /// <summary>
        /// 字符串转16进制字节数组
        /// </summary>
        /// <param name="hexString"></param>
        /// <returns></returns>
        public static byte[] strToToHexByte(string hexString)
        {
            hexString = hexString.Replace(" ", "");
            if ((hexString.Length % 2) != 0)
                hexString += " ";
            byte[] returnBytes = new byte[hexString.Length / 2];
            for (int i = 0; i < returnBytes.Length; i++)
                returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
            return returnBytes;
        }


        /// <summary>
        /// 字节数组转16进制字符串
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        public static string byteToHexStr(byte[] bytes)
        {
            string returnStr = "";
            if (bytes != null)
            {
                for (int i = 0; i < bytes.Length; i++)
                {
                    returnStr += bytes[i].ToString("X2");
                }
            }
            return returnStr;
        }
    }
    
}

上面代码只进行了距离解析,角度一级解析,角度二级解析。其他的想要加入的可以根据开发文档中的命令以及应答内容进行解析。运行的时候只需要把 RunSerial.cs,SerialCommunicationFacade.cs 绑定到一个空物体上即可。

好了,上面就是自己的一点小总结,欢迎交流。另外,附上 GitHub 上完整的项目,需要的可以下载。
场景是 Scene1 项目下载地址

Unity 中使用注意:

  1. 没有找到 System.IO.Ports 程序集,在[Edit->Project Settings->Player]下,修改[Other Settings]下的[Optimization]的[API Compatibility Level]为[.NET 2.0](默认为[.NET 2.0 Subset])。
  2. SerialPort 中的 SerialDataReceivedEventHandler DataReceived 还不能使用,即无法调用,采用线程的方式进行读取。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章