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

Beatrice ·
更新时间:2024-11-11
· 637 次阅读

三维姿态显示上位机 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; /***************************************************************************/ /// /// 数据到达事件 /// /// /// protected void myhid_DataReceived(object sender, report e) { if (lianjieflag) { RecDataBuffer = e.reportBuff; for (int a = 0; a = 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; })); } } /// /// 设备移除事件 /// /// /// 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]; /// /// 打开设备 /// 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; } } /// /// 设备关闭 /// protected void CloseDevice() { myHid.CloseDevice(myHidPtr); } /// /// 窗口加载时完成事件说明 /// protected void HID_Loaded() { myHid.DataReceived += new EventHandler(myhid_DataReceived); //订阅DataRec事件 myHid.DeviceRemoved += new EventHandler(myhid_DeviceRemoved); } #endregion } }

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); } /// /// 监听客户端连接 /// 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) { } } } /// /// 接收消息 /// /// 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) { } } } } } }

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(); } } } 效果图:

原创文章 7获赞 0访问量 247 关注 私信 展开阅读全文
作者:川北97大叔



unity 上位机 C# unity3d wpf

需要 登录 后方可回复, 如果你还没有账号请 注册新账号