1.设计功能
SICK LMS200激光扫描仪是将扫描得到的距离数据通过COM端口传送到接收端,再由终端设备对数据进行处理,完成对周边空间的探测。本程序分以下几部分:
一、串口通讯功能:发送、接收、显示数据;
二、数据处理功能:周期数据的截取,十六进制转十进制;
三、坐标标定功能:将距离数据标定在坐标系中;
2.串口通讯功能
2.1简介
由于激光扫描仪的数据量大,而且是实时产生的。为了提高串口读取数据的高速实时性,本程序的串口通讯部分是基于CSerialPort类完成的。该类支持线连接的串口编程,而且是基于多线程的。
2.2函数功能介绍
1.设置串口参数
函数原型:

BOOL CSerialPort::InitPort(CWnd* pPortOwner, // the owner (CWnd) of the port (receives message)
UINT portnr, // portnumber (1..4)
UINT baud, // baudrate
char parity, // parity 
UINT databits, // databits 
UINT stopbits, // stopbits 
DWORD dwCommEvents, // EV_RXCHAR, EV_CTS etc
UINT writebuffersize) // size to the writebuffer2.串口监测线程
BOOL CSerialPort::StartMonitoring() 
BOOL CSerialPort::RestartMonitoring()
BOOL CSerialPort::StopMonitoring()
void CSerialPort::WriteChar(CSerialPort* port)
void CSerialPort::ReceiveChar(CSerialPort* port, COMSTAT comstat)
void CSerialPort::WriteToPort(char* string)3.监测线程接收事件信息,再进行消息处理即可



2.3串口设置界面类CconfigDlg
完成串口的设置和事件的处理,设置界面如下图。

2.4发送数据
LMS200只识别十六进制的数据,因此在发送控制指令前要把指令转为十六进制。发送数据有单次发送,定时发送两张模式。
工作流程:
1:在“发送指令”编辑框中输入控制指令;
2:在“发送设置”中选择发送类型;
3:点击“单次发送”或“定时发送”根据选择的发送模式发送指令;
实现函数:

//////////////////////////////////////////////
//将发送数据转为十六进制
void CSerialTestDlg::SendHexData(char *buf)
//////////////////////////////////////////////
//将发送数据转为ASCII码
void CSerialTestDlg::SendAsciiData(char *buf)


2.5接收数据
终端接收到的数据形式如下:06 02 80 03 00 A0 00 10 16 0A 02 80 D6 02 B0 69 41 20 02 15 02 F2 01 99 01 61 01 30 01 0D 01 06 01 F4 00 DC 00 D3 00 D1 00 C3 00 CF 00 CC 00 D3 00 C7 00 D3 00 D0 00 D0 00 D6 00
 
 
CserialPort类中监视线程接收事件信息WM_COMM_RXCHAR与CSerialTestDlg::OnCommunication(WPARAM Char, LPARAM num)函数建立消息响应,接收LMS200返回的数据,然后根据用户要求转换为十六进制和ACII码。
实现函数:
//////////////////////////////////////////////
//接收串口数据
LONG CSerialTestDlg::OnCommunication(WPARAM Char, LPARAM num)
3.数据处理功能
LMS200返回的数据结构如下:

3.1.具体说明
1:发送启动指令后LMS200首先是返回一个06 02 80 03 00 A0 00字符串 10位。
    因采样角频率而不同。
2:校验位3位,每个周期都不一样;
3:返回数据6位,每个周期都不变;
4:距离数据722位;
5:下一周期重复2-4步;
3.2.数据处理
以采样角频率为10 为例,对数据的分析可知,距离数据的字符串长度为722*3=2166(含有空格),本程序的是按每行51个字符串显示如图所示。这样我们可以知道需要44行的数据才包含一个周期的数据,44*51=2244即存储这段数据的字符串的总长度是2244,而有效的距离数据是从51~2215这段字符串。总字符串转化成字符数组,用数组char array[2245],arraydata[2166]分别来存储总的字符和距离字符。
       现在就是对距离字符数组进行处理,每两个十六进制表示一个返回的距离数据,低位在前,高位在后。所以处理第一步先把高低位互换,再把十六进制转为十进制。
1:0F 02 12 02 E7 01
2:02 0F 02 12 01 E7
3:020F 0212 01E7
4:527 530 487
     接着把一个周期的数据存储在Data[361](0.50)或Data1[181](10)中,完成对一个周期数据的截取。

3.3实现函数

//////////////////////////////////////////////
//将接收到的十六进制数据转换为十进制
void CSerialTestDlg::HexToDec(CString str){}
//////////////////////////////////////////////
//完成对应采样角频率的处理
if(m_angleComb.GetCurSel()==0){} 0.50
if(m_angleComb.GetCurSel()==1){}   10
//////////////////////////////////////////////
//截取需要的字符串长度
StrData.GetLength()==2244
//////////////////////////////////////////////
//高位,低位换位 
for(k=0;k<=2160;k+=6){}
//////////////////////////////////////////////
//十六进制转为十进制
do
{}
while(j<len2);


4.坐标标定
根据采样角频率的不同,一个周期的距离数据分别有181个(10 )和361个(0.50 )数据。如下图所示:d为我们得到的数据,a为对应的角度,对应到笛卡尔坐标中:
x=d*cosa                   1
y=d*sina                   2

4.1坐标系的绘制
实现函数
void CSerialTestDlg::DrawPoint(int dis[]){}
为了方便重复绘制和清除旧图,程序中的坐标系为加载的位图在void CSerialTestDlg::OnPaint()中加载。资源中位图文件为IDB_BITMAP1。加载后的如下图:

4.2数据标定
       首先初始化double angle[361]和double angle1[181]这两个数据,分别存储0.50 和10 两种采样角频率的361个(00~3600)和181(00~1800)个角度。
       根据转换到笛卡尔坐标的转换式,算出坐标中的x,y值:

x=dis*cos(angle1*PI/180);
                                   y=dis*sin(angle1*PI/180);


由于计算机中对屏幕坐标的定义如左图所示:y轴是下正上负。为了按正常视觉效果来显示,需要对y坐标值取负。

CPoint m_ptEnd;
m_ptEnd.x=x;
                     m_ptEnd.y=-y;





用小圆点来显示

pDC->Ellipse(m_ptEnd.x - 2, m_ptEnd.y - 2, m_ptEnd.x + 2, m_ptEnd.y + 2);


图像的重绘
在绘制好图像后,移动对话框会使得界面刷新,导致图像消失,所以需要加入图像的重绘。
在程序中定义了一个CGrap类用来保存各个点的坐标值,在void CSerialTestDlg::OnPaint()中对图像进行重绘;

for(int i=0;i<m_ptrArray.GetSize();i++)
              {
pDC->Ellipse(((CGrap*)m_ptrArray.GetAt(i))->m_ptEnd.x-2, ((CGrap*)m_ptrArray.GetAt(i))->m_ptEnd.y-2, ((CGrap*)m_ptrArray.GetAt(i))->m_ptEnd.x+2, ((CGrap*)m_ptrArray.GetAt(i))->m_ptEnd.y + 2);
}


4.3最终效果