前言
Unity C#调用RPLidar的C++动态库(.dll)文件(1)
Unity C#调用RPLidar的C++动态库(.dll)文件(2)
看完了前两章博文,相信大家已经知晓了雷达的工作流程和Dll生成过程
接下来,如何在Unity中使用动态库
新建Unity工程
都知道怎么玩了就不过多叙述了
略………………
Plugins目录
- Assert里面新建Plugins目录, 新建x86/x86_64文件夹
- 把RPLidarDLL.dll放入x86_64文件夹中
创建C#调用动态库脚本
新建脚本,命名为LidarData.cs, 这里对应C++动态库里面的LidarData结构体
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct LidarData
{
public float theta;
public float distant;
public int quality;
};
新建脚本,命名为RplidarBinding.cs
public static class RplidarBinding
{
[DllImport("RPLidarDLL")]
public static extern int OnConnect(string opt_com_path, int opt_com_baudrate);
[DllImport("RPLidarDLL")]
public static extern bool OnDisconnect();
[DllImport("RPLidarDLL")]
public static extern bool StartMotor();
[DllImport("RPLidarDLL")]
public static extern bool EndMotor();
[DllImport("RPLidarDLL")]
public static extern bool StartScan();
[DllImport("RPLidarDLL")]
public static extern bool StartScanExpress(bool forcescan, int usingScanMode_);
[DllImport("RPLidarDLL")]
public static extern bool EndScan();
[DllImport("RPLidarDLL")]
public static extern bool ReleaseDrive();
[DllImport("RPLidarDLL")]
public static extern int GetLidarDataSize();
[DllImport("RPLidarDLL")]
private static extern int GrabData(IntPtr ptr);
public static int GetData(ref LidarData[] data)
{
GCHandle handler = GCHandle.Alloc(data, GCHandleType.Pinned);
IntPtr structPtr = handler.AddrOfPinnedObject();
int count = GrabData(structPtr);
handler.Free();
return count;
}
}
这里的函数和dll里面的外部调用函数统一命名
这里的知识点包含IntPtr、ref关键字、extern关键字、GCHandle、DllImport
自行百度理解,这里不过多叙说
DllImport后面跟的是动态库的名字不用加入.dll,切记!!!
创建Unity场景&C#测试脚本
场景结构如下所示:
C#测试脚本
[RequireComponent(typeof(MeshFilter))]
public class RplidarMap : MonoBehaviour
{
private static readonly int s_MaxLength = 8192;
public string m_COM = "COM3";
public int m_Baudrate = 256000;
public MeshFilter m_MeshFilter;
private bool m_OnScan = false;
private LidarData[] m_Data;
private Mesh m_Mesh;
private List<Vector3> m_Vert = new List<Vector3>();
private List<int> m_Index = new List<int>();
private Thread m_ThreadReceive;
private bool m_DataChanged = false;
private int m_DataCount = 0;
private void Start()
{
m_Data = new LidarData[s_MaxLength];
for (int i = 0; i < 720; i++)
{
m_Index.Add(i);
}
m_Mesh = new Mesh();
m_Mesh.MarkDynamic();
RplidarBinding.OnConnect(m_COM, m_Baudrate);
RplidarBinding.StartMotor();
m_OnScan = RplidarBinding.StartScan();
if (m_OnScan)
{
m_ThreadReceive = new Thread(RPLidarAX)
{
IsBackground = true
};
m_ThreadReceive.Start();
}
}
private void OnDestroy()
{
if (m_ThreadReceive != null)
{
//关闭线程
m_ThreadReceive.Abort();
m_ThreadReceive = null;
Debug.Log("Close thread");
}
RplidarBinding.EndScan();
RplidarBinding.EndMotor();
RplidarBinding.OnDisconnect();
RplidarBinding.ReleaseDrive();
m_OnScan = false;
}
private void Update()
{
if (m_DataChanged)
{
m_Vert.Clear();
for (int i = 0; i < m_DataCount; i++)
{
m_Vert.Add(Quaternion.Euler(0, 0, m_Data[i].theta) * Vector3.right * m_Data[i].distant * 0.001f);
}
m_Mesh.SetVertices(m_Vert);
m_Mesh.SetIndices(m_Index.ToArray(), MeshTopology.Points, 0);
m_Mesh.UploadMeshData(false);
m_MeshFilter.mesh = m_Mesh;
m_DataChanged = false;
}
}
private void RPLidarAX()
{
while (true)
{
m_DataCount = RplidarBinding.GetData(ref m_Data);
if (m_DataCount == 0)
{
Thread.Sleep(20);
}
else
{
m_DataChanged = true;
}
}
}
}
效果如下

//我这边提供一个如何把云点信息转为平面坐标
public Vector2 GetPosition(double angle, float distance, float xScale = 1, float yScale = 1)
{
double rad = (angle * Math.PI / 180f);
float endptX = Convert.ToSingle(Math.Sin(rad) * distance * xScale);
float endptY = Convert.ToSingle(Math.Cos(rad) * distance * yScale);
m_Vector2.Set(-endptY, endptX);
return m_Vector2;
}
伸手党请直接下载 资源下载
这里说明一下:
RplidarBinding.StartScan(); 这里使用的是普通扫描模式
如果要使用frame_grabber里面的所有模式
使用RplidarBinding.StartScanExpress(false,RPLidarScanMode);
扫描模式定义为:
public enum RPLidarScanMode
{
Standard = 0,
Express = 1,
Boost = 2,
Sensitivity = 3,
Stability = 4,
}
这样就不用瞎折腾解析SDK内部云点算法的实现了
开森~~~~