hdy 1 gadu atpakaļ
revīzija
a70149ac7b
9 mainītis faili ar 1255 papildinājumiem un 0 dzēšanām
  1. 611 0
      LakiBeamUDP.cpp
  2. 238 0
      LakiBeamUDP.h
  3. 19 0
      Makefile
  4. 108 0
      main.py
  5. 159 0
      rb_lidar.c
  6. 83 0
      rb_lidar.h
  7. BIN
      rb_lidar.o
  8. BIN
      rb_lidar.so
  9. 37 0
      rblidar.py

+ 611 - 0
LakiBeamUDP.cpp

@@ -0,0 +1,611 @@
+#include "LakiBeamUDP.h"
+
+//初始化了一些UDP网络通信相关的资源
+//异步接收数据,并使用多线程处理接收到的数据
+void LakiBeamUDP::LakiBeamUDP(string local_ip, string local_port, string laser_ip, string laser_port)
+{
+    //将本地设备的IP地址和端口号保存到对象的成员变量
+    this->local_ip = local_ip;
+    this->local_port = local_port;
+    //远程(激光设备)
+    this->laser_ip = laser_ip;
+    this->laser_port = laser_port;
+
+    //用于统计和控制某个资源
+    dbmain = 0;
+    dbmain_used = 0;
+    urpmain = 0;
+    urpmain_used = 0;
+
+    char *temp = reinterpret_cast<char *>(udprepack);
+    memset(temp, 0, CONFIG_MAX_REPACK * sizeof(repark_t));
+
+    local_ep = new udp_endpoint_t(address_t::from_string("0.0.0.0"), atoi(local_port.c_str()));
+    laser_ep = new udp_endpoint_t(address_t::from_string(laser_ip), atoi(laser_port.c_str()));
+    socket = new udp_socket_t(io_servicess);
+    socket->open(local_ep->protocol());
+    socket->set_option(boost::asio::ip::udp::socket::reuse_address(true));
+    socket->bind(*local_ep);
+    socket->async_receive_from(boost::asio::buffer(buffff, CONFIG_FRAME), *laser_ep, boost::bind(&LakiBeamUDP::on_read, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
+
+    //创建并启动一个线程udprepack_thd,执行udprepack_thread函数
+    //数据包处理
+    udprepack_thd.reset(new boost::thread(boost::bind(&LakiBeamUDP::udprepack_thread, this)));
+
+    //创建并启动一个线程receive_thd,执行receive_thread函数
+    //接收UDP数据
+    receive_thd.reset(new boost::thread(boost::bind(&LakiBeamUDP::receive_thread, this)));
+}
+
+//运行在独立线程中的方法,用于持续处理网络事件并接收UDP数据
+void LakiBeamUDP::receive_thread()
+{
+    //这个线程持续监听和处理网络事件
+    while (1)
+    {
+        try {
+            io_servicess.run();
+            boost::this_thread::interruption_point();
+        }
+        //检查当前线程是否被请求中断
+        //如果当前线程被请求中断就会抛出一个异常,并使得线程退出
+        catch (boost::system::error_code& err)
+        {
+            break;
+        }
+    }
+}
+
+//析构函数,实现了资源的清理和线程的安全退出
+LakiBeamUDP::~LakiBeamUDP()
+{
+    io_servicess.stop();
+
+    receive_thd->interrupt();
+    receive_thd->join();
+
+    delete socket;
+    delete local_ep;
+    delete laser_ep;
+
+    udprepack_thd->interrupt();
+    udprepack_thd->join();
+}
+
+//从缓冲区中同步地获取重新打包的UDP数据包,并将其传递给pack变量(数据同步一)
+void LakiBeamUDP::sync_get_repackedpack(repark_t &pack)
+{
+    while (urpmain < 2)
+    {
+		
+    }
+    while (urpmain <= (urpmain_used + 1))
+    {
+
+    }
+    urpmain_used = urpmain - 1;
+
+    pack = udprepack[(urpmain_used) % CONFIG_MAX_REPACK];
+}
+
+//该方法通过非阻塞的方式从缓冲区中获取UDP数据包(数据同步二)
+bool LakiBeamUDP::get_repackedpack(repark_t &pack)
+{
+	bool result = true;
+
+    //如果数据包太少,只有一个或没有,则无法进行获取。
+	if ((urpmain < 2)||(urpmain <= (urpmain_used + 1)))
+	{
+		result = false;
+	}
+	else
+	{
+        //获取了最新的数据包
+		urpmain_used = urpmain - 1;
+	}
+
+	if (result == true)
+	{
+        pack = udprepack[(urpmain_used) % CONFIG_MAX_REPACK];
+	}
+
+	return result;
+}
+
+//用于处理异步接收的UDP数据,将接收到的数据存入缓冲区
+void LakiBeamUDP::on_read(const boost::system::error_code& err, std::size_t read_bytes)
+{
+    if (!err)
+    {
+        char* temp;
+        if (read_bytes == CONFIG_FRAME)
+        {
+            temp = reinterpret_cast<char*>(&(doublebuffer[dbmain % CONFIG_FRAME_MAX]));
+            memcpy(temp, buffff, CONFIG_FRAME);
+            //dbmain:用于指示当前主缓冲区的位置或索引
+            //dbmain_used:用于指示当前在双缓冲区的主索引或位置,可能用于跟踪已处理的数据数量或状态
+            dbmain++;
+        }
+    }
+    //重新启动异步接收。即使在本次接收完成后,系统会立即等待下一个数据包的到来。
+    socket->async_receive_from(boost::asio::buffer(buffff, CONFIG_FRAME), *laser_ep, boost::bind(&LakiBeamUDP::on_read, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
+}
+
+//udprepack_thread线程定期检查缓冲区,获取数据包(根据数据包的特性去调用函数处理数据)
+//用于处理从UDP数据包双缓冲区中提取数据并进行封装的逻辑
+void LakiBeamUDP::udprepack_thread()
+{
+    while (1)
+    {
+        try
+        {
+            //该标志位用于控制后续的数据处理是否继续
+            bool goon = true;
+            udp_pack_t doublebuffertemp[2]; //存储两个UDP数据包的数据
+            {
+                //检查当前的双缓冲区是否有足够的数据可以被封装处理。至少有两个数据帧待处理
+                if ((dbmain - dbmain_used) >= 2)
+                {
+                    char *source1 = reinterpret_cast<char *>(&doublebuffertemp[0]);
+                    char *source2 = reinterpret_cast<char *>(&doublebuffertemp[1]);
+                    char *from1 = reinterpret_cast<char *>(&(doublebuffer[dbmain_used % CONFIG_FRAME_MAX]));
+                    char *from2 = reinterpret_cast<char *>(&(doublebuffer[(dbmain_used + 1) % CONFIG_FRAME_MAX]));
+                    u32_t realindex = dbmain_used % CONFIG_FRAME_MAX;
+                    if (realindex == CONFIG_FRAME_INDEX_MAX)
+                    {
+                        memcpy(source1, from1, CONFIG_FRAME);
+                        memcpy(source2, from2, CONFIG_FRAME);
+                    }
+                    else
+                    {
+                        memcpy(source1, from1, CONFIG_FRAME * 2);
+                    }
+                    dbmain_used++;
+                }
+                //如果数据不足,
+                else
+                {
+                    goon = false;
+                }
+            }
+
+            if (goon == true)
+            {
+                if ((doublebuffertemp[0].factory == 0x4037) && (doublebuffertemp[1].factory == 0x4037))
+                {
+                    // 寻找0位包裹
+                    u32_t start_index = 0xFFFFFFFF;
+                    for (int i = 0; i < CONFIG_UDP_BLOCKS; i++)
+                    {
+                        //寻找起始数据包
+                        if (doublebuffertemp[0].depths[i].azimuth == 0)
+                        {
+                            start_index = i;
+                        }
+                    }
+                    // 0位数据包
+                    if (start_index != 0xFFFFFFFF)
+                    {
+                        fill_havestart(doublebuffertemp, start_index);
+                    }
+                    // 非0位数据包
+                    else
+                    {
+                        fill_nostart(doublebuffertemp);
+                    }
+                }
+                else
+                {
+                    //                    DEBUG(doublebuffertemp[1].factory);
+                    //                    DEBUG(doublebuffertemp[0].factory);
+                }
+            }
+
+            //检查当前线程是否被请求中断
+            boost::this_thread::interruption_point();
+            //如果剩余的数据包不足两个,则线程会休眠2毫秒
+			if ((dbmain - dbmain_used) < 2)
+			{
+				boost::this_thread::sleep(boost::posix_time::milliseconds(2));
+			}
+        }
+        catch (...)
+        {
+            break;
+        }
+    }
+}
+
+//用于处理非起始的UDP数据包,并将其转换为内部udprepack结构中的点云数据。
+//接收两个数据包,根据时间戳、角度和深度信息计算每个的点云的相关数据
+void LakiBeamUDP::fill_nostart(udp_pack_t pack[2])
+{
+    (void)pack;
+    u32_t start_time = pack[0].timestamp;
+    u32_t end_time = pack[1].timestamp;
+
+    u16_t tempdots = udprepack[urpmain % CONFIG_MAX_REPACK].maxdots;
+    u32_t div_time = (end_time - start_time) / CONFIG_BLOCK_DEPTHS / CONFIG_UDP_BLOCKS;
+
+    for (int i = 0; i < CONFIG_UDP_BLOCKS; i++)
+    {
+        if (pack[0].depths[i].head == 0xEEFF)
+        {
+            if (i < (CONFIG_UDP_BLOCKS - 1))
+            {
+                u16_t start_angle = pack[0].depths[i].azimuth;//当前block的起始角度
+                u16_t end_angle = pack[0].depths[i + 1].azimuth;//下一个block的起始角度
+                u16_t div_angle;        //每个数据块中相邻两个测距数据的角度递增值
+                if (end_angle >= start_angle)
+                {
+                    div_angle = (end_angle - start_angle) / CONFIG_BLOCK_DEPTHS;
+                }
+                else
+                {
+                    div_angle = (CONFIG_DEGREE_MAX - start_angle + end_angle) / CONFIG_BLOCK_DEPTHS;
+                }
+
+                for (int j = 0; j < CONFIG_BLOCK_DEPTHS; j++)
+                {
+                    udprepack[urpmain % CONFIG_MAX_REPACK].dotcloud[tempdots % CONFIG_CIRCLE_DOTS].timestamp = start_time;
+                    udprepack[urpmain % CONFIG_MAX_REPACK].dotcloud[tempdots % CONFIG_CIRCLE_DOTS].angle = start_angle;
+                    udprepack[urpmain % CONFIG_MAX_REPACK].dotcloud[tempdots % CONFIG_CIRCLE_DOTS].distance = pack->depths[i].depth[j].distance0;
+                    udprepack[urpmain % CONFIG_MAX_REPACK].dotcloud[tempdots % CONFIG_CIRCLE_DOTS].rssi = pack->depths[i].depth[j].rssi0;
+                    udprepack[urpmain % CONFIG_MAX_REPACK].interval += div_time;
+                    tempdots++; //在每次插入后递增,用于跟踪当前已插入的点运输量
+                    if ((start_angle + div_angle) >= CONFIG_DEGREE_MAX)
+                    {
+                        start_angle = start_angle + div_angle - CONFIG_DEGREE_MAX;
+                    }
+                    else
+                    {
+                        start_angle += div_angle;
+                    }
+                    start_time += div_time;
+                }
+            }
+            else
+            {
+                u16_t start_angle = pack[0].depths[i].azimuth;
+                u16_t end_angle = pack[1].depths[0].azimuth;
+                u16_t div_angle;
+                if (end_angle >= start_angle)
+                {
+                    div_angle = (end_angle - start_angle) / CONFIG_BLOCK_DEPTHS;
+                }
+                else
+                {
+                    div_angle = (CONFIG_DEGREE_MAX - start_angle + end_angle) / CONFIG_BLOCK_DEPTHS;
+                }
+                for (int j = 0; j < CONFIG_BLOCK_DEPTHS; j++)
+                {
+                    udprepack[urpmain % CONFIG_MAX_REPACK].dotcloud[tempdots % CONFIG_CIRCLE_DOTS].timestamp = start_time;
+                    udprepack[urpmain % CONFIG_MAX_REPACK].dotcloud[tempdots % CONFIG_CIRCLE_DOTS].angle = start_angle;
+                    udprepack[urpmain % CONFIG_MAX_REPACK].dotcloud[tempdots % CONFIG_CIRCLE_DOTS].distance = pack->depths[i].depth[j].distance0;
+                    udprepack[urpmain % CONFIG_MAX_REPACK].dotcloud[tempdots % CONFIG_CIRCLE_DOTS].rssi = pack->depths[i].depth[j].rssi0;
+                    udprepack[urpmain % CONFIG_MAX_REPACK].interval += div_time;
+                    tempdots++;
+                    if ((start_angle + div_angle) >= CONFIG_DEGREE_MAX)
+                    {
+                        start_angle = start_angle + div_angle - CONFIG_DEGREE_MAX;
+                    }
+                    else
+                    {
+                        start_angle += div_angle;
+                    }
+                    start_time += div_time;
+                }
+            }
+        }
+    }
+    udprepack[urpmain % CONFIG_MAX_REPACK].maxdots = tempdots;
+}
+
+//处理一系列UDP数据包,将数据填充道dotcloud结构中
+//udp_pack_t pack[]  传入的UDP数据包数组 (包含来自传感器的数据)
+//u32_t start 表示当前数据包的索引,通常是数据流中的起始点
+void LakiBeamUDP::fill_havestart(udp_pack_t pack[], u32_t start)
+{
+    // urpmain表示当前处理的urpmain数组索引
+    //表示正在处理第一包数据
+    if (urpmain == 0)
+    {
+        //指向当前正在处理的Udprepack数组中的位置,它可能会用于确定当前的处理状态或处理的帧
+        urpmain++;
+        //用于记录已使用的udprepack数组的数量(已处理的帧的数量)
+        urpmain_used++;
+
+    //udprepack存储结构体的数组,用于存放解析后的数据
+    //urpmain % CONFIG_MAX_REPACK  会将Urpmain的值限制在0到CONFIG_MAX_REPACK-1范围内,这样可以保证访问udprepack数组时不会越界
+        udprepack[urpmain % CONFIG_MAX_REPACK].maxdots = 0;
+        udprepack[urpmain % CONFIG_MAX_REPACK].interval = 0;
+
+        (void)pack;
+    //计算时间间隔,从UDP数据包中提取开始和结束时间戳
+    //第一个数据包的时间戳:表示数据采集开始的时间
+        u32_t start_time = pack[0].timestamp;
+    //第二个数据包的时间戳:表示数据采集结束的时间
+        u32_t end_time = pack[1].timestamp;
+
+    //获取当前udprepack数组中指定索引位置的maxdots值
+        u16_t tempdots = udprepack[urpmain % CONFIG_MAX_REPACK].maxdots;
+    //计算每个数据块之间的时间间隔
+        u32_t div_time = (end_time - start_time) / CONFIG_BLOCK_DEPTHS / CONFIG_UDP_BLOCKS;
+
+        //根据start的值遍历数据块,每个数据块有多少个深度(遍历数据包中多个“块”)
+        for (int i = start; i < CONFIG_UDP_BLOCKS; i++)
+        {
+            //判断是否是最后一个块
+            if (i < (CONFIG_UDP_BLOCKS - 1))
+            {
+                u16_t start_angle = pack[0].depths[i].azimuth;
+                u16_t end_angle = pack[0].depths[i + 1].azimuth;
+                u16_t div_angle;
+                //通过计算起始和结束角度之间的差值,得出UDP包中每个小块所需的角度增量。
+                if (end_angle >= start_angle)
+                {
+                    div_angle = (end_angle - start_angle) / CONFIG_BLOCK_DEPTHS;
+                }
+                else
+                {
+                    div_angle = (CONFIG_DEGREE_MAX - start_angle + end_angle) / CONFIG_BLOCK_DEPTHS;
+                }
+                    
+                //处理每个深度块中的深度数据,并将计算后的时间戳、角度、距离和信号强度存储到udprepack中
+                //从输入的数据包中提取激光雷达的每个测量点,并将其数据存储到点云数据结构中
+                //CONFIG_BLOCK_DEPTHS (16)
+                for (int j = 0; j < CONFIG_BLOCK_DEPTHS; j++)
+                {
+
+                    //时间戳 
+                    //dotcloud是udprapack数组中每个元素的一个成员,,用于存储点云数据
+                    //start_time表示当前处理的数据点的时间戳,,将start_time存储到dotcloud的当前位置的timestamp字段中
+                    udprepack[urpmain % CONFIG_MAX_REPACK].dotcloud[tempdots % CONFIG_CIRCLE_DOTS].timestamp = start_time;
+                    //
+                    udprepack[urpmain % CONFIG_MAX_REPACK].dotcloud[tempdots % CONFIG_CIRCLE_DOTS].angle = start_angle;
+                    //pack是由udp_pack_t定义的结构体
+                    //depths[i].depth[j].distance0  表示从第i个数据块中的第j个深度数据点获取距离信息
+                    udprepack[urpmain % CONFIG_MAX_REPACK].dotcloud[tempdots % CONFIG_CIRCLE_DOTS].distance = pack->depths[i].depth[j].distance0;
+                    //将当前数据点的RSSI信息pack->depths[i].depth[j].rssi0存储到udprepack数组中的dotcloud数组的对应位置
+                    udprepack[urpmain % CONFIG_MAX_REPACK].dotcloud[tempdots % CONFIG_CIRCLE_DOTS].rssi = pack->depths[i].depth[j].rssi0;
+                    //将当前数据点的时间间隔div_time累加到udprepack数组中当前元素的interval字段
+                    udprepack[urpmain % CONFIG_MAX_REPACK].interval += div_time;
+                    //用于跟踪当前正在处理的dotcloud索引
+                    tempdots++; //增加点云数据的索引
+
+                    //可能会大360
+                    if ((start_angle + div_angle) >= CONFIG_DEGREE_MAX)
+                    {
+                        start_angle = start_angle + div_angle - CONFIG_DEGREE_MAX;
+                    }
+                    else
+                    {
+                        start_angle += div_angle;
+                    }
+                    //时间更新
+                    //start_time:当前数据点的时间戳
+                    //将start_time增加div_time的值,以反映下一个数据点的时间戳
+                    //div_time可能是每个数据点之前的时间间隔
+                    start_time += div_time;
+                }
+            }
+           
+            //最后一个块的处理逻辑
+            //提取数据,并将其存储到udprepack结构中
+            else
+            {
+                //假设pack是一个包含多个数据块的数组
+                //从当前数据块的第i项中提取起始角度
+                u16_t start_angle = pack[0].depths[i].azimuth;
+                //从下一个数据块pack[1]的第一个深度点提取结束角度
+                u16_t end_angle = pack[1].depths[0].azimuth;
+                //用于存储每个深度点之间的角度增量
+                u16_t div_angle;
+                //如果结束角度大于起始角度,则可以直接计算差值
+                if (end_angle >= start_angle)
+                {
+                    div_angle = (end_angle - start_angle) / CONFIG_BLOCK_DEPTHS;
+                }
+                //如果结束角度小于起始角度,则意味着角度跨越了0度
+                else
+                {
+                    div_angle = (CONFIG_DEGREE_MAX - start_angle + end_angle) / CONFIG_BLOCK_DEPTHS;
+                }
+
+                //将start_angle更新为当前块中实际其实角度
+                //(start + 1) * CONFIG_BLOCK_DEPTHS  表示当前数据块相对于第一块的偏移量,乘以div_angle可以得到当前数据块的起始角度
+                //为了确保在处理多个数据块时,每个数据块的起始角度都是正确的
+                start_angle = start_angle + div_angle * (start + 1) * CONFIG_BLOCK_DEPTHS;
+
+                //处理当前数据块中的每个深度数据点
+                for (int j = 0; j < CONFIG_BLOCK_DEPTHS; j++)
+                {
+                    udprepack[urpmain % CONFIG_MAX_REPACK].dotcloud[tempdots % CONFIG_CIRCLE_DOTS].timestamp = start_time;
+                    udprepack[urpmain % CONFIG_MAX_REPACK].dotcloud[tempdots % CONFIG_CIRCLE_DOTS].angle = start_angle;
+                    udprepack[urpmain % CONFIG_MAX_REPACK].dotcloud[tempdots % CONFIG_CIRCLE_DOTS].distance = pack->depths[i].depth[j].distance0;
+                    udprepack[urpmain % CONFIG_MAX_REPACK].dotcloud[tempdots % CONFIG_CIRCLE_DOTS].rssi = pack->depths[i].depth[j].rssi0;
+                    udprepack[urpmain % CONFIG_MAX_REPACK].interval += div_time;
+                    tempdots++;
+                    if ((start_angle + div_angle) >= CONFIG_DEGREE_MAX)
+                    {
+                        start_angle = start_angle + div_angle - CONFIG_DEGREE_MAX;
+                    }
+                    else
+                    {
+                        start_angle += div_angle;
+                    }
+                    start_time += div_time;
+                }
+            }
+        }
+        //将当前数据包中有效存储的数据点数量temdots更新到maxdots字段中
+        //这个信息可以在后续处理中使用,以确定当前数据包中的点云数据的数量
+        udprepack[urpmain % CONFIG_MAX_REPACK].maxdots = tempdots;
+    }
+    // 其它0包
+    else
+    {
+        {
+            (void)pack;
+            u32_t start_time = pack[0].timestamp;
+            u32_t end_time = pack[1].timestamp;
+            //tempdots用于跟踪当前处理的数据点数量
+            u16_t tempdots = udprepack[urpmain % CONFIG_MAX_REPACK].maxdots;
+            
+            u32_t div_time = (end_time - start_time) / CONFIG_BLOCK_DEPTHS / CONFIG_UDP_BLOCKS;
+            //累加时间间隔
+            //将当前数据包中所有数据点的时间间隔累加到udprepack[urpmain % CONFIG_MAX_REPACK].interval
+            udprepack[urpmain % CONFIG_MAX_REPACK].interval += div_time;
+            //处理当前数据包中的深度数据                 
+            for (u32_t i = 0; i < start; i++)
+            {
+                u16_t start_angle = pack[0].depths[i].azimuth;
+                u16_t end_angle = pack[0].depths[i + 1].azimuth;
+                u16_t div_angle;
+                if (end_angle >= start_angle)
+                {
+                    div_angle = (end_angle - start_angle) / CONFIG_BLOCK_DEPTHS;
+                }
+                else
+                {
+                    div_angle = (CONFIG_DEGREE_MAX - start_angle + end_angle) / CONFIG_BLOCK_DEPTHS;
+                }
+                for (int j = 0; j < CONFIG_BLOCK_DEPTHS; j++)
+                {
+                    udprepack[urpmain % CONFIG_MAX_REPACK].dotcloud[tempdots % CONFIG_CIRCLE_DOTS].timestamp = start_time;
+                    udprepack[urpmain % CONFIG_MAX_REPACK].dotcloud[tempdots % CONFIG_CIRCLE_DOTS].angle = start_angle;
+                    udprepack[urpmain % CONFIG_MAX_REPACK].dotcloud[tempdots % CONFIG_CIRCLE_DOTS].distance = pack->depths[i].depth[j].distance0;
+                    udprepack[urpmain % CONFIG_MAX_REPACK].dotcloud[tempdots % CONFIG_CIRCLE_DOTS].rssi = pack->depths[i].depth[j].rssi0;
+                    udprepack[urpmain % CONFIG_MAX_REPACK].interval += div_time;
+                    tempdots++;
+                    if ((start_angle + div_angle) >= CONFIG_DEGREE_MAX)
+                    {
+                        start_angle = start_angle + div_angle - CONFIG_DEGREE_MAX;
+                    }
+                    else
+                    {
+                        start_angle += div_angle;
+                    }
+                    start_time += div_time;
+                }
+            }
+            //检查是否存在数据丢失
+            //在点云数据数量为1440或3600的情况下
+			if((tempdots==1440)||(tempdots==3600))
+			{
+                bool lost = false;  //用于标识是否存在数据丢失
+                if (tempdots == 1440)
+                {
+                    for (int i = 180; i < (1440 - 17); i++)
+                    {
+                        if (udprepack[urpmain % CONFIG_MAX_REPACK].dotcloud[i+1].angle < udprepack[urpmain % CONFIG_MAX_REPACK].dotcloud[i].angle)
+                        {
+                            lost = true;
+                        }
+                    }
+                }
+                else
+                {
+                    for (int i = 450; i < (3600 - 17); i++)
+                    {
+                        if (udprepack[urpmain % CONFIG_MAX_REPACK].dotcloud[i + 1].angle < udprepack[urpmain % CONFIG_MAX_REPACK].dotcloud[i].angle)
+                        {
+                            lost = true;
+                        }
+                    }
+                }
+
+                if (lost == false)
+                {
+                    udprepack[urpmain % CONFIG_MAX_REPACK].interval /= tempdots;
+                    udprepack[urpmain % CONFIG_MAX_REPACK].maxdots = tempdots;
+                    urpmain++;
+                }
+			}
+        }
+        {
+            //如果当前处理的不是最后一个数据块,则进入当前的数据块
+            //将当前处理的点云数据包的最大点数maxdots初始化为0
+            udprepack[urpmain % CONFIG_MAX_REPACK].maxdots = 0;
+
+            (void)pack;
+            u32_t start_time = pack[0].timestamp;
+            u32_t end_time = pack[1].timestamp;
+
+            u16_t tempdots = udprepack[urpmain % CONFIG_MAX_REPACK].maxdots;
+            u32_t div_time = (end_time - start_time) / CONFIG_BLOCK_DEPTHS / CONFIG_UDP_BLOCKS;
+
+            //循环处理数据包中的深度数据
+            for (u32_t i = start; i < CONFIG_UDP_BLOCKS; i++)
+            {
+                //如果当前处理块不是最后一个块
+                if (i < (CONFIG_UDP_BLOCKS - 1))
+                {
+                    u16_t start_angle = pack[0].depths[i].azimuth;
+                    u16_t end_angle = pack[0].depths[i + 1].azimuth;
+                    u16_t div_angle;
+                    if (end_angle >= start_angle)
+                    {
+                        div_angle = (end_angle - start_angle) / CONFIG_BLOCK_DEPTHS;
+                    }
+                    else
+                    {
+                        div_angle = (CONFIG_DEGREE_MAX - start_angle + end_angle) / CONFIG_BLOCK_DEPTHS;
+                    }
+                    for (int j = 0; j < CONFIG_BLOCK_DEPTHS; j++)
+                    {
+                        udprepack[urpmain % CONFIG_MAX_REPACK].dotcloud[tempdots % CONFIG_CIRCLE_DOTS].timestamp = start_time;
+                        udprepack[urpmain % CONFIG_MAX_REPACK].dotcloud[tempdots % CONFIG_CIRCLE_DOTS].angle = start_angle;
+                        udprepack[urpmain % CONFIG_MAX_REPACK].dotcloud[tempdots % CONFIG_CIRCLE_DOTS].distance = pack->depths[i].depth[j].distance0;
+                        udprepack[urpmain % CONFIG_MAX_REPACK].dotcloud[tempdots % CONFIG_CIRCLE_DOTS].rssi = pack->depths[i].depth[j].rssi0;
+                        udprepack[urpmain % CONFIG_MAX_REPACK].interval += div_time;
+                        tempdots++;
+                        if ((start_angle + div_angle) >= CONFIG_DEGREE_MAX)
+                        {
+                            start_angle = start_angle + div_angle - CONFIG_DEGREE_MAX;
+                        }
+                        else
+                        {
+                            start_angle += div_angle;
+                        }
+                        start_time += div_time;
+                    }
+                }
+                else
+                {
+                    u16_t start_angle = pack[0].depths[i].azimuth;
+                    u16_t end_angle = pack[1].depths[0].azimuth;
+                    u16_t div_angle;
+                    if (end_angle >= start_angle)
+                    {
+                        div_angle = (end_angle - start_angle) / CONFIG_BLOCK_DEPTHS;
+                    }
+                    else
+                    {
+                        div_angle = (CONFIG_DEGREE_MAX - start_angle + end_angle) / CONFIG_BLOCK_DEPTHS;
+                    }
+                    for (u32_t j = 0; j < CONFIG_BLOCK_DEPTHS; j++)
+                    {
+                        udprepack[urpmain % CONFIG_MAX_REPACK].dotcloud[tempdots % CONFIG_CIRCLE_DOTS].timestamp = start_time;
+                        udprepack[urpmain % CONFIG_MAX_REPACK].dotcloud[tempdots % CONFIG_CIRCLE_DOTS].angle = start_angle;
+                        udprepack[urpmain % CONFIG_MAX_REPACK].dotcloud[tempdots % CONFIG_CIRCLE_DOTS].distance = pack->depths[i].depth[j].distance0;
+                        udprepack[urpmain % CONFIG_MAX_REPACK].dotcloud[tempdots % CONFIG_CIRCLE_DOTS].rssi = pack->depths[i].depth[j].rssi0;
+                        udprepack[urpmain % CONFIG_MAX_REPACK].interval += div_time;
+                        tempdots++;
+                        if ((start_angle + div_angle) >= CONFIG_DEGREE_MAX)
+                        {
+                            start_angle = start_angle + div_angle - CONFIG_DEGREE_MAX;
+                        }
+                        else
+                        {
+                            start_angle += div_angle;
+                        }
+                        start_time += div_time;
+                    }
+                }
+            }
+            //处理结束,将当前的点数tempdots更新为udprepack结构中的最大点数
+            udprepack[urpmain % CONFIG_MAX_REPACK].maxdots = tempdots;
+        }
+    }
+}

+ 238 - 0
LakiBeamUDP.h

@@ -0,0 +1,238 @@
+#ifndef LAKIBEAMUDP_H
+#define LAKIBEAMUDP_H
+
+#include <boost/thread.hpp>
+#include <boost/asio.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <boost/smart_ptr.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/bimap.hpp>
+
+#include <sstream>
+#include <cstdlib>
+#include <iostream>
+#include <stdio.h>
+
+using namespace std;
+
+/// 已使用声明:用来消除未使用变量报警,没有功能上面的意义
+#ifndef USED
+#define USED(x) ((void)x)
+#endif
+
+/// 调试接口:快速打印调试信息
+#ifndef DEBUG
+#define DEBUG(x) (cout << x << endl)
+#endif
+
+/// 取消字节对齐:下面定义的数据结构体与雷达的协议结构相互映射,不可以进行自动对齐字节
+#define __autoalign__ __attribute__((packed))
+
+/**
+ *  为了统一代码风格,对部分数据类型进行了重新定义
+ */
+#ifndef i32_t
+typedef signed int i32_t;
+#endif
+#ifndef u32_t
+typedef unsigned int u32_t;
+#endif
+#ifndef u16_t
+typedef unsigned short u16_t;
+#endif
+#ifndef u16_t
+typedef unsigned short i16_t;
+#endif
+#ifndef u8_t
+typedef unsigned char u8_t;
+#endif
+#ifndef c8_t
+typedef char i8_t;
+#endif
+
+#ifndef udp_socket_t
+typedef boost::asio::ip::udp::socket udp_socket_t;
+#endif
+#ifndef address_t
+typedef boost::asio::ip::address address_t;
+#endif
+#ifndef service_t
+typedef boost::asio::io_service service_t;
+#endif
+typedef boost::scoped_ptr<boost::thread> thread_t;
+#ifndef service_work_t
+typedef boost::scoped_ptr<service_t::work> service_work_t;
+#endif
+#ifndef endpoint_t
+typedef boost::asio::ip::udp::endpoint udp_endpoint_t;
+#endif
+#ifndef mutex_t
+typedef boost::mutex mutex_t;
+#endif
+
+/// UDP数据报的数据帧大小——42字节UDP数据头不用于数据解码 1206 = 1248-42
+#define CONFIG_FRAME (1206)
+/// UDP数据包数据帧深度——必须为2的倍数
+#define CONFIG_FRAME_MAX (256)
+/// UDP数据缓存二位数组的第1个索引下标最大值
+#define CONFIG_FRAME_INDEX_MAX (CONFIG_FRAME_MAX - 1)
+
+/// 最大角度值
+#define CONFIG_DEGREE_MAX (36000)
+
+/// UDP数据报一帧数据中数据块个数
+#define CONFIG_UDP_BLOCKS (12)
+/// 数据块中数据池的深度
+#define CONFIG_BLOCK_DEPTHS (16)
+
+/// 转换缓存数据块深度
+#define CONFIG_CIRCLE_DOTS (3600)
+
+/// 雷达超时时间设置——2s
+#define CONFIG_LASER_DISCONNECT (5)
+#define CONFIG_LASER_TIMER_HEART (100)
+
+/// 重组数据包大小
+#define CONFIG_MAX_REPACK (32)
+
+/// 16位数据大小端转换
+#define T16_H8(x) ((u16_t)((((u16_t)x) & ((u16_t)(0xFF00))) >> 8))
+#define T16_L8(x) ((u16_t)((((u16_t)x) & ((u16_t)(0x00FF))) << 8))
+#define BIG_TO_LITTLE_16(x) ((u16_t)(T16_H8(x) | T16_L8(x)))
+/// 32位数据大小端转换
+#define T32_HH8(x) ((u32_t)((((u32_t)x) & ((u32_t)(0xFF000000))) >> 24))
+#define T32_HL8(x) ((u32_t)((((u32_t)x) & ((u32_t)(0x00FF0000))) >> 8))
+#define T32_LH8(x) ((u32_t)((((u32_t)x) & ((u32_t)(0x0000FF00))) << 8))
+#define T32_LL8(x) ((u32_t)((((u32_t)x) & ((u32_t)(0x000000FF))) << 24))
+#define BIG_TO_LITTLE_32(x) ((u32_t)(T32_HH8(x) | T32_HL8(x) | T32_LH8(x) | T32_LL8(x)))
+
+/** @struct depth_t 雷达元数据包——最小数据包单元
+ *   @brief  雷达深度数据的最小包裹
+ */
+#pragma pack(push)
+#pragma pack(1)
+struct depth_t
+{
+    u16_t distance0; ///< 深度数据
+    u8_t rssi0;      ///< 回波强度
+    u16_t distance1;
+    u8_t rssi1;
+};
+
+/** @struct little_pack_t UDP数据包封装最小结构单元
+ *   @brief  一帧雷达深度数据的数据结构的最小单元
+ */
+struct little_pack_t
+{
+    u16_t head;                         ///< 数据头
+    u16_t azimuth;                      ///< 测距数据的角度值
+    depth_t depth[CONFIG_BLOCK_DEPTHS]; ///< 深度数据点位包 16
+};
+
+/** @struct udp_pack_t UDP数据报一帧数据结构
+ *   @brief  一帧UDP数据包的数据结构
+ */
+struct udp_pack_t
+{
+    little_pack_t depths[CONFIG_UDP_BLOCKS]; ///< 深度数据单元 12
+    u32_t timestamp;                         ///< 时间戳
+    u16_t factory;                           ///< 工厂信息
+};
+
+/** @struct cicle_pack_t UDP成品数据结构
+ *   @brief  重新封装的深度数据包最小单元
+ */
+struct cicle_pack_t
+{
+	u32_t timestamp; ///< 时间戳
+	u16_t angle;     ///< 角度值
+	u16_t distance;  ///< 深度数据
+	u8_t rssi;       ///< 回波强度
+};
+
+/** @struct repark_t 深度数据块,一块包含雷达一周的深度数据
+ *   @brief  一帧代表一块深度数据
+ */
+struct repark_t
+{
+    u32_t interval;                            ///< 间隔时间
+    u16_t maxdots;                             ///< 点位个数
+    cicle_pack_t dotcloud[CONFIG_CIRCLE_DOTS]; ///< 深度数据点云
+};
+#pragma pack(pop)
+
+class LakiBeamUDP
+{
+
+public:
+    /*!
+            构造函数
+            \param local_ip     本地ip
+            \param local_port   本地端口号
+            \param laser_ip     雷达ip
+            \param laser_port   雷达端口号
+        */
+    LakiBeamUDP(string local_ip, string local_port, string laser_ip, string laser_port);
+    /*!
+            析构函数
+        */
+    ~LakiBeamUDP();
+    /*!
+            同步重新组包功能
+            \param pack   用户包裹出口
+        */
+    void sync_get_repackedpack(repark_t &pack);
+	/*!
+			取出数据包裹 返回true那么立即返回数据
+			\param pack   用户包裹出口
+		*/
+	bool get_repackedpack(repark_t &pack);
+
+private:
+    string local_ip;
+    string local_port;
+    string laser_ip;
+    string laser_port;
+
+    thread_t receive_thd;
+    thread_t udprepack_thd;
+
+    mutex_t thd_mutex;
+	bool everyThingOK;
+    volatile u32_t dbmain;
+	volatile u32_t dbmain_used;
+	udp_pack_t doublebuffer[CONFIG_FRAME_MAX];
+
+	volatile u32_t urpmain;
+	volatile u32_t urpmain_used;
+	repark_t udprepack[CONFIG_MAX_REPACK];
+
+    char buffff[CONFIG_FRAME];
+    udp_socket_t* socket;
+    boost::asio::io_service io_servicess;
+    udp_endpoint_t *local_ep;
+    udp_endpoint_t *laser_ep;
+
+private:
+    /*!
+            接受UDP数据包线程
+        */
+    void receive_thread(void);
+    /*!
+            UDP拆包组包线程
+        */
+    void udprepack_thread(void);
+    /*!
+            填充没有起始包的包裹(一次需要2包)
+            \param pack   包裹入口数组
+        */
+    void fill_nostart(udp_pack_t pack[2]);
+    /*!
+            填充有起始包的包裹(一次需要2包)
+            \param pack   包裹入口数组
+        */
+    void fill_havestart(udp_pack_t pack[2], u32_t start);
+    void on_read(const boost::system::error_code& err, std::size_t read_bytes);
+};
+
+#endif // LAKIBEAMUDP_H

+ 19 - 0
Makefile

@@ -0,0 +1,19 @@
+CC = gcc
+CFLAGS = -Wall -Wextra -fPIC -I.
+
+TARGET = rb_lidar.so
+SOURCES = rb_lidar.c
+OBJECTS = $(SOURCES:.c=.o)
+
+all: $(TARGET)
+
+$(TARGET): $(OBJECTS)
+	$(CC) -shared -o $@ $^ -lpthread
+
+%.o: %.c rb_lidar.h
+	$(CC) $(CFLAGS) -c $< -o $@
+
+clean:
+	rm -f $(OBJECTS) $(TARGET)
+
+.PHONY: all clean

+ 108 - 0
main.py

@@ -0,0 +1,108 @@
+import socket
+import threading
+import ctypes
+import queue
+import logging
+
+# 配置日志
+logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
+
+# 定义深度数据结构
+class Depth_t(ctypes.Structure):
+    _fields_ = [
+        ("distance0", ctypes.c_uint16),  # 深度数据
+        ("rssi0", ctypes.c_uint8),       # 回波强度
+        ("distance1", ctypes.c_uint16),   # 深度数据
+        ("rssi1", ctypes.c_uint8)         # 回波强度
+    ]
+
+# 定义小包结构体
+class LittlePacket(ctypes.Structure):
+    _fields_ = [
+        ("azimuth", ctypes.c_uint16),    # 测距数据的角度值
+        ("depth", Depth_t * 16),          # 深度数据点位包
+        ("timestamp", ctypes.c_uint32),   # 数据头
+    ]
+
+# 创建两个共享的队列作为双缓冲区
+buffer1 = queue.Queue(maxsize=1440)
+buffer2 = queue.Queue(maxsize=1440)
+
+# 创建一个事件对象
+zeroFoundEvent = threading.Event()
+currentBuffer = 1  # 指示当前使用的缓冲区
+
+def parse_msop_packet(data):
+    global currentBuffer
+
+    # 提取数据块
+    for i in range(12):  # 假设有12个数据块
+        start = i * 100  # 每个数据块100字节
+        end = start + 100
+        data_block = data[start:end]
+
+        # 检查标志位
+        if data_block[0:2] == b'\xff\xee':
+            little_packet = LittlePacket()
+            little_packet.azimuth = int.from_bytes(data_block[2:4], byteorder='little')
+            little_packet.timestamp = int.from_bytes(data_block[1201:1205], byteorder='little')
+
+            # 提取16个测距数据
+            for j in range(16):
+                depth_data = Depth_t()
+                depth_data.distance0 = int.from_bytes(data_block[4 + j * 6: 4 + j * 6 + 2], byteorder='little')
+                depth_data.rssi0 = int.from_bytes(data_block[4 + j * 6 + 2: 4 + j * 6 + 3], byteorder='little')
+                depth_data.distance1 = int.from_bytes(data_block[4 + j * 6 + 3: 4 + j * 6 + 5], byteorder='little')
+                depth_data.rssi1 = int.from_bytes(data_block[4 + j * 6 + 5: 4 + j * 6 + 6], byteorder='little')
+                little_packet.depth[j] = depth_data
+
+            # 如果发现 0 角度数据,设置事件并切换缓冲区
+            if little_packet.azimuth == 0:
+                currentBuffer = 2 if currentBuffer == 1 else 1  # 切换到另一个缓冲区
+                logging.debug(f"切换到缓冲区 {currentBuffer}, 缓冲区1大小 {buffer1.qsize()}, 缓冲区2大小 {buffer2.qsize()}")
+                zeroFoundEvent.set()
+
+            # 将当前包加入到当前缓冲区
+            if currentBuffer == 1:
+                buffer1.put(little_packet)
+            else:
+                buffer2.put(little_packet)
+
+def read_buffer():
+    while True:
+        if zeroFoundEvent.is_set():  # 如果发现 0 角度数据
+            # 处理当前缓冲区的数据
+            if currentBuffer == 2:
+                while not buffer1.empty():
+                    little_packet = buffer1.get()
+                    logging.debug(f"Consumer from buffer1: azimuth: {little_packet.azimuth}")
+            else:
+                while not buffer2.empty():
+                    little_packet = buffer2.get()
+                    logging.debug(f"Consumer from buffer2: azimuth: {little_packet.azimuth}")
+
+            zeroFoundEvent.clear()  # 清除事件
+
+def receive_udp_data():
+    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+    sock.bind(('192.168.8.1', 2368))
+
+    while True:
+        try:
+            data, addr = sock.recvfrom(1206)  # 读取UDP数据包
+            parse_msop_packet(data)
+        except Exception as e:
+            logging.error(f"Error receiving data: {e}")
+
+receive_thread = threading.Thread(target=receive_udp_data)
+read_thread = threading.Thread(target=read_buffer)
+
+receive_thread.start()
+read_thread.start()
+
+# 主线程可以继续执行其他任务
+try:
+    while True:
+        pass
+except KeyboardInterrupt:
+    print("Exiting...")

+ 159 - 0
rb_lidar.c

@@ -0,0 +1,159 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <pthread.h>
+
+#include "rb_lidar.h"
+
+#define BUF_SIZE 1206
+// UDP接收线程
+void* udp_listener(void* arg) {
+    RBLidar* lidar = (RBLidar*)arg;
+    int sockfd;
+    struct sockaddr_in server_addr, client_addr;
+    socklen_t addr_len = sizeof(client_addr);
+    udp_packet_t udp_packet_temp;
+
+    // 创建UDP套接字
+    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+    if (sockfd < 0) {
+        perror("socket");
+        return NULL;
+    }
+
+    // 设置服务器地址
+    memset(&server_addr, 0, sizeof(server_addr));
+    server_addr.sin_family = AF_INET;
+    inet_pton(AF_INET, lidar->ip, &server_addr.sin_addr);
+    server_addr.sin_port = htons(lidar->port);
+
+    // 绑定套接字
+    if (bind(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
+        perror("bind");
+        close(sockfd);
+        return NULL;
+    }
+
+    printf("Listening for UDP packets on %s:%d...\n", lidar->ip, lidar->port);
+
+    // 接收数据
+    while (1) {
+        int len = recvfrom(sockfd, (char *)&udp_packet_temp, BUF_SIZE, 0, (struct sockaddr*)&client_addr, &addr_len);
+        if (len < 0) {
+            perror("recvfrom");
+            continue;
+        }
+
+        // 使用双缓冲机制
+        pthread_mutex_lock(&lidar->mutex);
+
+        // 将接收到的UDP包中的子包存入当前缓冲区
+        for (int i = 0; i < CONFIG_UDP_BLOCKS; i++) {
+            sub_packet_t* sub_packet = &udp_packet_temp.sub_packet[i];
+            
+            // 将子包存入当前缓冲区
+            memcpy(&lidar->buffer[lidar->current][lidar->valid_count[lidar->current]], sub_packet, sizeof(sub_packet_t));
+
+            // 统计有效数据
+            lidar->valid_count[lidar->current]++;
+
+            // 检查azimuth是否为0
+            if (sub_packet->azimuth == 0) {
+                // 切换到下一个缓冲区并通知处理线程
+                lidar->current = (lidar->current + 1) % 2; // 切换缓冲区
+
+                lidar->valid_count[lidar->current] = 0;
+                memcpy(&lidar->buffer[lidar->current][lidar->valid_count[lidar->current]], sub_packet, sizeof(sub_packet_t));
+                lidar->valid_count[lidar->current]++;
+
+                // pthread_cond_signal(&lidar->cond); // 通知处理线程
+            }
+        }
+
+        pthread_mutex_unlock(&lidar->mutex);
+    }
+
+    close(sockfd);
+    return NULL;
+}
+
+// 组包线程
+void* packet_processor(void* arg) {
+    RBLidar* lidar = (RBLidar*)arg;
+
+    // 处理数据
+    while (1) {
+        pthread_mutex_lock(&lidar->mutex);
+        pthread_cond_wait(&lidar->cond, &lidar->mutex); // 等待数据
+
+        // 处理当前缓冲区数据
+        int next_index = (lidar->current + 1) % 2; // 获取上一缓冲区索引
+        int count = lidar->valid_count[next_index]; // 获取有效数据的数量
+
+        // 创建临时缓冲区
+        sub_packet_t* temp_buffer = (sub_packet_t*)malloc(sizeof(sub_packet_t) * SUBPKT_BUF_COUNT);
+        int temp_count = 0;
+
+        // 处理当前缓冲区中的每个有效子包
+        for (int i = 0; i < count; i++) {
+            sub_packet_t* sub_packet = &lidar->buffer[next_index][i];
+            
+            memcpy(&temp_buffer[temp_count++], sub_packet, sizeof(sub_packet_t));
+        }
+
+        // 调用回调函数
+        lidar->callback((const char*)temp_buffer, sizeof(sub_packet_t) * temp_count);
+
+        // 重置当前缓冲区
+        memset(lidar->buffer[next_index], 0, sizeof(sub_packet_t) * SUBPKT_BUF_COUNT);
+        lidar->valid_count[next_index] = 0; // 重置有效数据计数
+
+        free(temp_buffer); // 释放临时缓冲区
+
+        pthread_mutex_unlock(&lidar->mutex);
+    }
+    return NULL;
+}
+
+
+
+// 创建RBLidar实例
+RBLidar* rblidar_create(const char* ip, int port, callback_t callback) {
+    RBLidar* lidar = (RBLidar*)malloc(sizeof(RBLidar));
+    lidar->ip = strdup(ip);
+    lidar->port = port;
+    lidar->callback = callback;
+
+    // 初始化双缓冲区和其他线程同步机制
+    for (int i = 0; i < 2; i++) {
+        lidar->buffer[i] = (sub_packet_t*)malloc(sizeof(sub_packet_t) * SUBPKT_BUF_COUNT);
+    }
+    lidar->current = 0;
+    pthread_mutex_init(&lidar->mutex, NULL);
+    pthread_cond_init(&lidar->cond, NULL);
+
+    // 创建接收线程
+    pthread_t listener_thread;
+    pthread_create(&listener_thread, NULL, udp_listener, lidar);
+    pthread_detach(listener_thread);  // 让线程在结束时自动回收资源
+
+    // 创建组包线程
+    pthread_t processor_thread;
+    pthread_create(&processor_thread, NULL, packet_processor, lidar);
+    pthread_detach(processor_thread);
+
+    return lidar;
+}
+
+// 释放RBLidar实例
+void rblidar_destroy(RBLidar* lidar) {
+    for (int i = 0; i < 2; i++) {
+        free(lidar->buffer[i]);
+    }
+    pthread_mutex_destroy(&lidar->mutex);
+    pthread_cond_destroy(&lidar->cond);
+    free(lidar->ip);
+    free(lidar);
+}

+ 83 - 0
rb_lidar.h

@@ -0,0 +1,83 @@
+#ifndef RB_LIDAR_H
+#define RB_LIDAR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>  // 包含标准整数类型
+#include <pthread.h> // 用于线程支持
+
+typedef void (*callback_t)(const char* data, int length);
+
+// 配置常量
+#define CONFIG_BLOCK_COUNT    (16)   
+#define CONFIG_UDP_BLOCKS     (12)   
+#define FRAME_POINT_COUNT     (3600) 
+
+#define SUBPKT_BUF_COUNT	(200)   
+
+// 结构体对齐设置
+#pragma pack(push)
+#pragma pack(1)
+
+// 原始点数据结构
+typedef struct {
+    uint16_t dist_0; // 第一个距离
+    uint8_t rssi_0;  // 第一个RSSI值
+    uint16_t dist_1; // 第二个距离
+    uint8_t rssi_1;  // 第二个RSSI值
+} raw_point_t;
+
+// 子包结构
+typedef struct {
+    uint16_t header; // 子包头
+    uint16_t azimuth; // 方位角
+    raw_point_t point[CONFIG_BLOCK_COUNT]; // 点数据数组
+} sub_packet_t;
+
+// UDP包结构
+typedef struct {
+    sub_packet_t sub_packet[CONFIG_UDP_BLOCKS]; // 子包数组
+    uint32_t timestamp; // 时间戳
+    uint16_t factory_byte; // 工厂字节
+} udp_packet_t;
+
+// 点数据结构
+typedef struct {
+    uint16_t azimuth; // 方位角
+    uint16_t dist; // 距离
+    uint16_t rssi; // RSSI值
+    uint32_t timestamp; // 时间戳
+} point_data_t;
+
+// 帧数据结构
+typedef struct {
+    point_data_t pointcloud[FRAME_POINT_COUNT]; // 深度数据点云
+} frame_data_t;
+
+#pragma pack(pop)
+
+// RBLidar 结构体
+typedef struct {
+    char* ip;
+    int port;
+    callback_t callback;
+    sub_packet_t* buffer[2]; // 双缓冲区
+    int current;     // 当前缓冲区索引
+    int valid_count[2]; // 每个缓冲区中有效数据的数量
+    pthread_mutex_t mutex;
+    pthread_cond_t cond;
+} RBLidar;
+
+// 创建 RBLidar 实例
+RBLidar* rblidar_create(const char* ip, int port, callback_t callback);
+
+// 释放 RBLidar 实例
+void rblidar_destroy(RBLidar* lidar);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // RB_LIDAR_H

BIN
rb_lidar.o


BIN
rb_lidar.so


+ 37 - 0
rblidar.py

@@ -0,0 +1,37 @@
+# rblidar.py
+import ctypes
+import logging
+
+# 加载C库
+rblidar_lib = ctypes.CDLL('./rb_lidar.so')  # 确保路径正确
+
+# 定义回调函数类型
+CALLBACK_TYPE = ctypes.CFUNCTYPE(None, ctypes.c_char_p, ctypes.c_int)
+
+# 回调函数
+def my_callback(data, length):
+    # 将接收到的字节数据处理(例如打印、存储等)
+    logging.info("Received length: %d", length)
+
+class RBLidar:
+    def __init__(self, ip: str, port: int):
+        # 初始化日志记录
+        logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s: %(message)s')
+
+        # 创建回调函数
+        self.callback = CALLBACK_TYPE(my_callback)
+        # 创建C库中的RBLidar实例
+        self.lidar = rblidar_lib.rblidar_create(ip.encode('utf-8'), port, self.callback)
+
+    def __del__(self):
+        # 清理资源
+        rblidar_lib.rblidar_destroy(self.lidar)
+
+# 示例使用
+if __name__ == "__main__":
+    lidar = RBLidar("192.168.8.1", 2368)  # 替换为实际的IP和端口
+    try:
+        while True:
+            pass  # 持续运行以接收数据
+    except KeyboardInterrupt:
+        logging.info("Stopping...")