一、栅格法建图原理 激光点云栅格化核心思想是将激光雷达所扫描到的区域用网格进行处理,每个栅格点云代表空间的一小块区域,内含一部分点云,点云栅格化处理分为二维栅格化和三维栅格化,二维其实就是将三维点云进行一个投影。不考虑z值的变化。 这里我们先讲一下二维栅格化的处理: ? 我们假设地面相对平坦,即地面扫描点的 z 轴方向的波动较小,通过将扫描区域进行栅格划分,将扫描点云投影到 xy 的栅格平面,通过统计栅格中 z 轴方面的最高点和最低点的差值(即,极差),判断栅格中的点是否为地面点或障碍物点。
二、代码分析 1.首先是建立一个3D激光雷达线程
pthread_t g_Thread3DL0Handle;
pthread_create(&g_Thread3DL0Handle, NULL, Thread3D_L0, &gParm);
pthread_join(g_Thread3DHandle, NULL);
2.其次建立两个线程:接收雷达数据和处理雷达数据
void *Thread3D_L0(void *args)
{
int rt;
pthread_t lidar_robosense_recv;
pthread_t lidar_robosense_handle;
rt = ntzx_lidar_robosense_init();
if(rt < 0)
{
printf("ntzx_lidar_robosense_init error\n");
return NULL;
}
rt = pthread_create(&lidar_robosense_recv,NULL,ntzx_lidar_robosense_recv,&gParm);
if(rt < 0)
{
printf("ntzx_lidar_robosense_recv error\n");
return NULL;
}
rt = pthread_create(&lidar_robosense_handle,NULL,ntzx_lidar_robosense_handle,&gParm);
if(rt < 0)
{
printf("ntzx_lidar_robosense_handle error\n");
return NULL;
}
pthread_join(lidar_robosense_recv, NULL);
pthread_join(lidar_robosense_handle, NULL);
return NULL;
}
void *ntzx_lidar_robosense_recv(void *recvbuf)
{
WRC_3D16_L0_UDPClient * pUdp = (WRC_3D16_L0_UDPClient*)recvbuf;
pUdp->ReceivingLoop();
return recvbuf;
}
void *ntzx_lidar_robosense_handle(void *handlebuf)
{
WRC_3D16_L0_UDPClient * pUdp = (WRC_3D16_L0_UDPClient*)handlebuf;
if((pUdp->My_DataProc).DataProc_Initialize())
(pUdp->My_DataProc).ProcessingLoop();
return handlebuf;
}
解析一下WRC_3D16_L0_UDPClient *pUdp是什么? 它就是一个类指针,就相当于结构体指针,它的形式是这样的:
class WRC_3D16_L0_UDPClient
{
public:
WRC_3D16_L0_UDPClient();
~WRC_3D16_L0_UDPClient();
void ReceivingLoop();
int Open();
WRC_3D16_L0_DataProc My_DataProc;
pthread_t m_RecvThreadHandle;
pthread_t m_ProcThreadHandle;
bool Is_Init;
private:
int L0_UdpSocket;
unsigned char UdpPacketBuf[MAX_UDP_PACKET_SIZE];
private:
void ConnectLidar(uint16_t port);
int CreateTwoTHREAD();
bool IsPacketHeader(unsigned char *buff);
};
三、循环接收雷达传来的数据包
void WRC_3D16_L0_UDPClient::ReceivingLoop()
{
sockaddr_in server_addr;
socklen_t server_len;
int PacketLen;
int idx;
int SleepTime;
SleepTime = 0;
while (Is_Init)
{
server_len = sizeof(server_addr);
PacketLen = recvfrom(L0_UdpSocket, (unsigned char *)UdpPacketBuf, MAX_UDP_PACKET_SIZE, 0,(struct sockaddr*) &server_addr, &server_len);
if (PacketLen == UDP_PACKET_SIZE && IsPacketHeader(UdpPacketBuf))
{
SleepTime = 0;
pthread_mutex_lock(&g_L0_Mutex_NewPacket);
idx =g_L0_ReceivedPacketNum % MAX_ONE_FRAME_PACKET;
memcpy(&(g_L0_OneFrameBuf[idx][0]), UdpPacketBuf, PacketLen);
g_L0_OneFramePacketSize[idx] = PacketLen;
g_L0_ReceivedPacketNum++;
pthread_mutex_unlock(&g_L0_Mutex_NewPacket);
}
else
{
usleep(PACKET_INTERVAL_TIME);
SleepTime++;
if (SleepTime > 100) {
Is_Init = false;
printf("3D:L0:==== Can't Receive Data,Initialize and getPacket again!===\n");
ConnectLidar(PORT_NUMBER_L0);
if(Is_Init)
ReceivingLoop();
}
}
}
printf("3D:L0:getPacket done!\n");
return;
}
四、激光雷达坐标系和车辆坐标系之间的转换 利用激光雷达测光和测距来计算我们关心的物体的精确距离。 激光雷达利用飞行时间计算物体的距离 当发射激光脉冲时,其发射时间和方向将被记录。激光脉冲在空气中传播,直到它碰到一个能反射一些能量的障碍物。在接收到能量的部分之后,由传感器记录采集和接收的时间。障碍物的球面坐标利用传感器的返回时间和每次扫描后接收的功率(作为反射率)来计算。
由于激光雷达传感器的是一个特殊的球面坐标系值,让我们来复习该特殊的球面坐标系。
球面坐标系
在球面坐标系中,点由距离和两个角度定义。为了表示这两个角,我们使用方位角(Th)和极角(γ)约定。因此,点是由(r,τ,γ)定义的。 从上面的图解可以看出,方位角是在X轴上测量的X-Y平面,极坐标角是Z轴测量的Z-Y平面。 从上面的图,我们可以得到下列方程,将笛卡尔坐标转换为球面坐标。 可以使用下面的方程从球坐标导出笛卡尔坐标。 五、雷达数据处理线程 1.雷达数据处理线程
void WRC_3D16_L0_DataProc::ProcessingLoop()
{
int nTransformed,nReceived;
int nBytes;
int idx;
unsigned char nPacket[MAX_UDP_PACKET_SIZE];
nTransformed = 0;
nBytes = 0;
while (1)
{
pthread_mutex_lock(&g_L0_Mutex_NewPacket);
nReceived = g_L0_ReceivedPacketNum;
pthread_mutex_unlock(&g_L0_Mutex_NewPacket);
if (nTransformed == nReceived)
{
usleep(PACKET_INTERVAL_TIME);
continue;
}
pthread_mutex_lock(&g_L0_Mutex_NewPacket);
idx = nTransformed % MAX_ONE_FRAME_PACKET;
nBytes = g_L0_OneFramePacketSize[idx];
memcpy(nPacket, &(g_L0_OneFrameBuf[idx][0]), nBytes);
pthread_mutex_unlock(&g_L0_Mutex_NewPacket);
nTransformed++;
DecodeOnePacket(nPacket);
if (m_IsNewFrame)
{
g_L0_NavID=pDataProc_param->g_iNavID;
g_L0_RecivedFrameNum++;
SaveOneFrame();
printf("3D:L0:in frame %d,pointnum is %d:\n",g_L0_RecivedFrameNum ,m_PointNumInFrame);
My_OBS.OBS_Initialize();
My_OBS.CreateOBSGrid(m_OneFramePoint,m_PointNumInFrame);
m_Group = 0;
m_IsNewFrame = false;
memset(m_UsedAngle, false, sizeof(bool) * ONE_ROUND_ANGLE_NUM);
memset(m_Angle, 0, sizeof(int)*MAX_ONE_FRAME_GROUP_NUM);
memset(m_IsIgnore, false, sizeof(bool)*MAX_ONE_FRAME_POINT_NUM);
m_IsFirstAngel = true;
pthread_mutex_lock(&g_L0_Mutex_NewPacket);
nTransformed = g_L0_ReceivedPacketNum;
pthread_mutex_unlock(&g_L0_Mutex_NewPacket);
}
}
}
|