当前位置:   article > 正文

三维姿态显示上位机 C#+WPF+HID+Unity3D技术_c# 姿态显示

c# 姿态显示

三维姿态显示上位机 C#+WPF+HID+Unity3D技术

在毕业设计中做了一个基于AHRS的设计,涉及了姿态融合算法,为了调试算法参数性能,需要用到姿态显示上位机用来显示验证算法的效果。在收集了很多资料后,决定自己做一个三维姿态显示上位机。由于本人学识浅薄,程序中可能有很多目前没有发现的BUG,如有纰漏,敬请指正。

下面介绍一下我开发的大致方法:
1、HID数据传输,在开发的过程中,采用无线传输的方式将单片机的数据传输到电脑的USB端,再采用HID协议将USB端点的数据上传到电脑。
2、在电脑上位机中检测上传到电脑的数据,当检测到数据后,通过TCP协议与Unity3D通讯,Unity3D作为服务器,根据通信内容执行相关操作。
3、上位机UI采用开源的PanuonUI进行开发。

该上位机的优劣:
优点:开发简单,不用使用OpenGL开发,仅使用C#语言就可以完成开发;界面美观,贴图和背景相比OpenGL开发效果好很多;运行中Unity3D使用了GPU渲染,相比软渲染流畅度更好。
缺点:Unity3D的EXE文件较大,比采用C++开发的上位机体量大很多;可以作为学习和实验使用,距离商用还有很大差距。

由于整个上位机的开发过程叙述起来比较麻烦,这里直接放出源码供需要的人下载使用。

源码下载

关键代码:

HID对接收数据进行转换:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
using System.Windows.Interop;
using System.Windows.Threading;
using HID;
using System.Runtime.InteropServices;
using Panuon.UI.Silver;

namespace CubeAttitudeShow
{
    public partial class MainWindow : WindowX
    {
        #region HID_协议定义与实现
        /*定义HID数传的内容*/
        public Hid myHid = new Hid();
        public IntPtr myHidPtr = new IntPtr();
        string IMUKeyNum;
        int temp;
        public bool BiaoDingFlag = false;
        public bool BiaoDingFlag2 = false;

        double MagX,MagY,MagZ;

        string SmagX, SmagY, SmagZ;

        public string Dis1;

        public string Dis2;

        public string Yaw;

        public string Pitch;

        public string Roll;

        /***************************************************************************/
        /// <summary>
        ///  数据到达事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void myhid_DataReceived(object sender, report e)
        {
            if (lianjieflag)
            {
                RecDataBuffer = e.reportBuff;

                for (int a = 0; a < 6; a++)
                    TransformData[a] = RecDataBuffer[a + 5];
                WpfServer.SendMessage(TransformData);
                /*把数据显示到列表框*/
                IMUKeyNum = RecDataBuffer[0].ToString();//按键信息

                temp = 0;
                if (RecDataBuffer[1] >= 0x80) //判定是否是负数
                    temp = 0 - ((RecDataBuffer[1] & 0x7f) * 256 + RecDataBuffer[2]);
                else
                    temp = (RecDataBuffer[1]) * 256 + RecDataBuffer[2];
                Dis1 = temp.ToString();//基站 1 距离 

                temp = 0;
                if (RecDataBuffer[3] >= 0x80) //判定是否是负数
                    temp = 0 - ((RecDataBuffer[3] & 0x7f) * 256 + RecDataBuffer[4]);
                else
                    temp = (RecDataBuffer[3]) * 256 + RecDataBuffer[4];
                Dis2 = temp.ToString();//基站 2 距离 

                temp = 0;
                if (RecDataBuffer[5] >= 0x80)
                    temp = 0 - ((RecDataBuffer[5] & 0x7f) * 256 + RecDataBuffer[6]) / 10;
                else
                    temp = ((RecDataBuffer[5]) * 256 + (RecDataBuffer[6])) / 10;
                Yaw = temp.ToString();//偏航角
                MagX = temp * 0.00256;
                SmagX = MagX.ToString();

                temp = 0;
                if (RecDataBuffer[7] >= 0x80)
                    temp = 0 - ((RecDataBuffer[7] & 0x7f) * 256 + RecDataBuffer[8]) / 10;
                else
                    temp = ((RecDataBuffer[7]) * 256 + (RecDataBuffer[8])) / 10;
                Pitch = temp.ToString();//俯仰角 
                MagY = temp * 0.00256;
                SmagY = MagY.ToString();

                temp = 0;
                if (RecDataBuffer[9] >= 0x80)
                    temp = 0 - ((RecDataBuffer[9] & 0x7f) * 256 + RecDataBuffer[10]) / 10;
                else
                    temp = ((RecDataBuffer[9]) * 256 + (RecDataBuffer[10])) / 10;
                Roll = temp.ToString();//滚转角 
                MagZ = temp * 0.00256;
                SmagZ = MagZ.ToString();
                this.Dispatcher.Invoke(new Action(delegate { KeyCommand.Text = IMUKeyNum; }));
                this.Dispatcher.Invoke(new Action(delegate { Station1.Text = Dis1; }));
                this.Dispatcher.Invoke(new Action(delegate { Station2.Text = Dis2; }));
                this.Dispatcher.Invoke(new Action(delegate { PitchAngle.Text = Pitch; }));
                this.Dispatcher.Invoke(new Action(delegate { YawAngle.Text = Yaw; }));
                this.Dispatcher.Invoke(new Action(delegate { RollAngle.Text = Roll; }));
            }
        }

        /// <summary>
        /// 设备移除事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void myhid_DeviceRemoved(object sender, EventArgs e)
        {
            CloseDevice();
            this.Dispatcher.Invoke(new Action(delegate { LianJie.Content = tubiao + " 失去连接 "; }));
            lianjieflag = false;
        }
        /***************************************************************************/

        Byte[] RecDataBuffer = new byte[90];


        /// <summary>
        /// 打开设备
        /// </summary>
        protected bool OpenDevice()
        {
            if (myHid.Opened == false)
            {
                UInt16 myVendorID = 0x3333;
                UInt16 myProductID = 0x4444;
                if ((int)(myHidPtr = myHid.OpenDevice(myVendorID, myProductID)) != -1)//调用开启HID函数来开启HID口
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            else
            {
                return true;
            }
        }
        /// <summary>
        /// 设备关闭
        /// </summary>
        protected void CloseDevice()
        {
            myHid.CloseDevice(myHidPtr);
        }

        /// <summary>
        /// 窗口加载时完成事件说明
        /// </summary>
        protected void HID_Loaded()
        {
            myHid.DataReceived += new EventHandler<HID.report>(myhid_DataReceived); //订阅DataRec事件
            myHid.DeviceRemoved += new EventHandler(myhid_DeviceRemoved);
        }
        #endregion       
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174

TCP客户端:

using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.Diagnostics;
using System.Text;
using System;

namespace CubeAttitudeShow
{
    class TcpServer
    {
        //私有成员
        private static byte[] result = new byte[1024];
        private int myProt = 500;   //端口  
        static Socket serverSocket;//服务器接口
        static Socket clientSocket;//客户接口
        Thread myThread;
        static Thread receiveThread;
        //属性
        public int port { get; set; }
        //方法     
        internal void StartServer()
        {
            //服务器IP地址  
            IPAddress ip = IPAddress.Parse("127.0.0.1");
            serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            serverSocket.Bind(new IPEndPoint(ip, myProt));  //绑定IP地址:端口  
            serverSocket.Listen(10);    //设定最多10个排队连接请求  
            myThread = new Thread(ListenClientConnect);
            myThread.Start();
            
        }

        internal void QuitServer()
        {
            serverSocket.Close();
            clientSocket.Close();
            myThread.Abort();
            receiveThread.Abort();
        }


        internal void SendMessage(byte[] msg)
        {
                clientSocket.Send(msg);
        }



        /// <summary>  
        /// 监听客户端连接  
        /// </summary>  
        private static void ListenClientConnect()
        {
            while (true)
            {
                try
                {
                    clientSocket = serverSocket.Accept();
                    //clientSocket.Send(Encoding.ASCII.GetBytes("Server Say Hello"));
                    receiveThread = new Thread(ReceiveMessage);
                    receiveThread.Start(clientSocket);
                }
                catch (Exception)
                {
                }
            }
        }

        /// <summary>  
        /// 接收消息  
        /// </summary>  
        /// <param name="clientSocket"></param>  
        private static void ReceiveMessage(object clientSocket)
        {
            Socket myClientSocket = (Socket)clientSocket;
            while (true)
            {
                try
                {
                    //通过clientSocket接收数据  
                    int receiveNumber = myClientSocket.Receive(result);
                    //Debug.WriteLine("接收客户端{0}消息{1}", myClientSocket.RemoteEndPoint.ToString(), Encoding.ASCII.GetString(result, 0, receiveNumber));
                }
                catch (Exception ex)
                {
                    try
                    {
                        Debug.WriteLine(ex.Message);
                        myClientSocket.Shutdown(SocketShutdown.Both);
                        myClientSocket.Close();
                        break;
                    }
                    catch (Exception)
                    {
                    }

                }
            }
        }
    }
}  

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103

Unity嵌套:

using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Threading;

namespace CubeAttitudeShow
{
    public partial class UnityControl : UserControl
    {
        [DllImport("User32.dll")]
        static extern bool MoveWindow(IntPtr handle, int x, int y, int width, int height, bool redraw);

        internal delegate int WindowEnumProc(IntPtr hwnd, IntPtr lparam);
        [DllImport("user32.dll")]
        internal static extern bool EnumChildWindows(IntPtr hwnd, WindowEnumProc func, IntPtr lParam);

        [DllImport("user32.dll")]
        static extern int SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);

        private Process process;
        private IntPtr unityHWND = IntPtr.Zero;

        private const int WM_ACTIVATE = 0x0006;
        private readonly IntPtr WA_ACTIVE = new IntPtr(1);
        private readonly IntPtr WA_INACTIVE = new IntPtr(0);

        public UnityControl()
        {
            InitializeComponent();
            this.Load += UnityControl_Load;
            panel1.Resize += panel1_Resize;
        }

        private void UnityControl_Load(object sender, EventArgs e)
        {
            try
            {
                process = new Process();
                //process.StartInfo.FileName = @"D:\毕业设计\毕业设计\电脑软件设计\CubeAttitude\CubeAttitude3DShow\EXE\CubeAttitude3DShow.exe";
                process.StartInfo.FileName = Application.StartupPath + @"\Cube Attitude Exe\CubeAttitude3DShow.exe";
                process.StartInfo.Arguments = "-parentHWND " + panel1.Handle.ToInt32() + " " + Environment.CommandLine;
                process.StartInfo.UseShellExecute = true;
                process.StartInfo.CreateNoWindow = true;

                process.Start();

                process.WaitForInputIdle();
                // Doesn't work for some reason ?!
                //unityHWND = process.MainWindowHandle;
                EnumChildWindows(panel1.Handle, WindowEnum, IntPtr.Zero);

                //unityHWNDLabel.Text = "Unity HWND: 0x" + unityHWND.ToString("X8");
            }
            catch (Exception ex)
            {
                //unityHWNDLabel.Text = ex.Message;
                //MessageBox.Show(ex.Message);
            }
        }

        internal void ActivateUnityWindow()
        {
            SendMessage(unityHWND, WM_ACTIVATE, WA_ACTIVE, IntPtr.Zero);
        }

        internal void DeactivateUnityWindow()
        {
            SendMessage(unityHWND, WM_ACTIVATE, WA_INACTIVE, IntPtr.Zero);
        }

        private int WindowEnum(IntPtr hwnd, IntPtr lparam)
        {
            unityHWND = hwnd;
            ActivateUnityWindow();
            return 0;
        }

        private void panel1_Resize(object sender, EventArgs e)
        {
            MoveWindow(unityHWND, 0, 0, panel1.Width, panel1.Height, true);
            ActivateUnityWindow();
        }

        // Close Unity application
        internal void Form1_FormClosed()
        {
            try
            {
                process.CloseMainWindow();

                Thread.Sleep(1000);
                while (process.HasExited == false)
                    process.Kill();
            }
            catch (Exception)
            {

            }
        }

        internal void Form1_Activated()
        {
            ActivateUnityWindow();
        }

        internal void Form1_Deactivate()
        {
            DeactivateUnityWindow();
        }
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113

效果图:

在这里插入图片描述

点击这里下载

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/125041
推荐阅读
相关标签
  

闽ICP备14008679号