GrabBag/Device/RsLidarDevice/Src/RsLidarDevice.cpp
2026-06-14 17:01:09 +08:00

1286 lines
39 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "RsLidarDevice.h"
#include <cmath>
#include <cstdio>
#include <chrono>
#include <cstring>
#include <thread>
#include <cstdlib>
#include <new>
#ifdef _WIN32
#include <malloc.h>
#endif
#include <rs_driver/driver/decoder/compress_algo.hpp>
namespace
{
constexpr uint32_t kRsem4DefaultRings = 520;
constexpr uint32_t kRsem4ReserveColumns = 1200;
constexpr uint32_t kRsem4HalfRings = kRsem4DefaultRings / 2;
constexpr size_t kRsem4VecselsPerColumn = 26;
constexpr size_t kRsem4PixelsPerVcsel = 20;
constexpr size_t kRsem4SurfaceCount = 4;
constexpr size_t kRsem4BlocksPerPacket = 260;
constexpr size_t kRsem4MsopTailLen = 12;
constexpr size_t kDeliveryFramePoolSize = 6;
constexpr float kRsem4DistanceResolution = 0.005f;
constexpr float kRsem4MinDistance = 0.5f;
constexpr float kRsem4MaxDistance = 350.0f;
constexpr double kDeg01ToRad = 3.14159265358979323846 / 18000.0;
#pragma pack(push, 1)
struct Rsem4DifopPkt
{
uint8_t id[8];
uint8_t reserved0[106];
int8_t yawOffset[26];
int16_t pitchAngle[520];
int16_t surfacePitchOffset[4];
uint8_t reserved1[110];
uint16_t dataLength;
uint16_t counter;
uint32_t dataId;
uint32_t crc32;
};
struct Rsem4Difop2Pkt
{
uint8_t id[4];
uint8_t reserved0[63];
uint8_t surfaceId;
uint8_t pixelCnt;
uint8_t vcselCnt;
int8_t yawOffset[26];
int16_t pitchAngle[520];
int16_t surfacePitchOffset[4];
int16_t rollOffset;
uint8_t reserved1[4];
uint16_t dataLength;
uint16_t counter;
uint32_t dataId;
uint32_t crc32;
};
struct Rsem4Difop0624Pkt
{
uint8_t id[4];
uint8_t reserved0[306];
int8_t yawOffset[26];
int16_t pitchAngle[520];
int16_t surfacePitchOffset[4];
uint8_t reserved1[6];
uint16_t dataLength;
uint16_t counter;
uint32_t dataId;
uint32_t crc32;
};
struct Rsem4Channel
{
uint16_t distance;
uint8_t intensity;
uint8_t pointAttribute;
};
struct Rsem4Block
{
Rsem4Channel channel[1];
};
struct Rsem4MsopHeader
{
uint8_t id[4];
uint16_t pktSeq;
uint16_t protocolVersion;
uint8_t returnMode;
uint8_t timeMode;
RSTimestampUTC timestamp;
uint8_t frameSync;
uint8_t frameRate;
uint16_t columnNum;
int16_t yawAngle;
uint8_t packMode;
uint8_t surfaceId;
uint16_t reserved;
uint8_t lidarType;
uint8_t temperature;
};
struct Rsem4MsopHeader2
{
uint8_t id[4];
uint16_t pktSeq;
uint8_t reserved[2];
};
struct Rsem4MsopPkt
{
Rsem4MsopHeader header;
Rsem4Block blocks[260];
uint16_t dataLength;
uint16_t counter;
uint32_t dataId;
uint32_t crc32;
};
#pragma pack(pop)
inline void SetZeroPoint(SVzNLPointXYZI& dst)
{
dst.fData[0] = 0.0f;
dst.fData[1] = 0.0f;
dst.fData[2] = 0.0f;
dst.fData_c[0] = 0.0f;
}
inline int16_t SwapI16(int16_t value)
{
uint16_t v = static_cast<uint16_t>(value);
v = static_cast<uint16_t>((v << 8) | (v >> 8));
return static_cast<int16_t>(v);
}
inline uint16_t ReadU16BE(uint16_t value)
{
return static_cast<uint16_t>((value << 8) | (value >> 8));
}
inline int32_t NormalizeAngle01(int32_t angle)
{
angle %= 36000;
if (angle < 0)
angle += 36000;
return angle;
}
struct Deg01TrigTable
{
std::array<float, 36000> sinValues;
std::array<float, 36000> cosValues;
Deg01TrigTable()
{
for (size_t i = 0; i < sinValues.size(); ++i)
{
const double rad = static_cast<double>(i) * kDeg01ToRad;
sinValues[i] = static_cast<float>(std::sin(rad));
cosValues[i] = static_cast<float>(std::cos(rad));
}
}
};
inline const Deg01TrigTable& TrigTable()
{
static const Deg01TrigTable table;
return table;
}
inline float SinDeg01(int32_t angle)
{
return TrigTable().sinValues[NormalizeAngle01(angle)];
}
inline float CosDeg01(int32_t angle)
{
return TrigTable().cosValues[NormalizeAngle01(angle)];
}
inline bool IsCompleteRsem4Packet(const uint8_t* data, size_t size)
{
static const uint8_t kHeader[] = {0x55, 0xAA, 0x5A, 0xA5};
return size >= sizeof(kHeader) && std::memcmp(data, kHeader, sizeof(kHeader)) == 0;
}
inline void UpdateMax(std::atomic<uint64_t>& target, uint64_t value)
{
uint64_t prev = target.load(std::memory_order_relaxed);
while (value > prev &&
!target.compare_exchange_weak(prev, value, std::memory_order_relaxed))
{}
}
inline void UpdateMaxSize(std::atomic<size_t>& target, size_t value)
{
size_t prev = target.load(std::memory_order_relaxed);
while (value > prev &&
!target.compare_exchange_weak(prev, value, std::memory_order_relaxed))
{}
}
inline uint64_t ElapsedUs(std::chrono::steady_clock::time_point begin,
std::chrono::steady_clock::time_point end)
{
return static_cast<uint64_t>(
std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count());
}
}
// WSAStartup 引用计数
static std::atomic<int> g_sockRefCount{0};
// ============================================================
// SDK ErrCode → 统计桶映射
// SDK error_code.hpp: MSOPTIMEOUT=0x40 WRONGMSOPLEN=0x42 PKTBUFOVERFLOW=0x48 CLOUDOVERFLOW=0x49
// 桶顺序与日志列对齐0=msopto 1=pktof 2=cldof 3=wlen 4=other
// ============================================================
size_t CRsLidarDevice::errCodeToBucket(int errCode)
{
switch (errCode)
{
case 0x40: return 0; // MSOPTIMEOUT
case 0x48: return 1; // PKTBUFOVERFLOW
case 0x49: return 2; // CLOUDOVERFLOW
case 0x42: return 3; // WRONGMSOPLEN
default: return 4; // other
}
}
// ============================================================
// 工厂方法
// ============================================================
int IRsLidarDevice::CreateObject(IRsLidarDevice** ppDevice)
{
if (!ppDevice) return -1;
CRsLidarDevice* p = new CRsLidarDevice();
*ppDevice = p;
return 0;
}
// ============================================================
// 构造 / 析构
// ============================================================
CRsLidarDevice::CRsLidarDevice()
: m_pDriver(std::make_unique<LidarDriver<SdkCloudMsg>>())
{
initializeRsem4DefaultAngles();
}
CRsLidarDevice::~CRsLidarDevice()
{
Stop();
CloseDevice();
}
void* CRsLidarDevice::operator new(std::size_t size)
{
#ifdef _WIN32
void* ptr = _aligned_malloc(size, 64);
if (!ptr)
throw std::bad_alloc();
return ptr;
#else
void* ptr = nullptr;
if (posix_memalign(&ptr, 64, size) != 0)
throw std::bad_alloc();
return ptr;
#endif
}
void CRsLidarDevice::operator delete(void* ptr) noexcept
{
#ifdef _WIN32
_aligned_free(ptr);
#else
std::free(ptr);
#endif
}
void CRsLidarDevice::operator delete(void* ptr, std::size_t) noexcept
{
CRsLidarDevice::operator delete(ptr);
}
// ============================================================
// InitDevice
// ============================================================
int CRsLidarDevice::InitDevice()
{
#ifdef _WIN32
if (g_sockRefCount == 0)
{
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
return -1;
}
}
g_sockRefCount++;
#endif
return 0;
}
// ============================================================
// 类型转换: RsLidarConfig → RSDriverParam
// ============================================================
RSDriverParam CRsLidarDevice::toDriverParam(const RsLidarConfig& config)
{
RSDriverParam param;
param.lidar_type = strToLidarType(config.lidarType);
param.input_type = InputType::ONLINE_LIDAR;
param.frame_id = config.frameId;
param.input_param.host_address = config.hostAddress;
param.input_param.msop_port = config.msopPort;
param.input_param.difop_port = config.difopPort;
param.input_param.imu_port = config.imuPort;
param.decoder_param.min_distance = config.minDistance;
param.decoder_param.max_distance = config.maxDistance;
param.decoder_param.dense_points = config.densePoints;
param.decoder_param.use_lidar_clock = config.useLidarClock;
param.decoder_param.ts_first_point = config.tsFirstPoint;
param.decoder_param.wait_for_difop = config.waitForDifop;
param.decoder_param.start_angle = config.startAngle;
param.decoder_param.end_angle = config.endAngle;
param.decoder_param.split_angle = config.splitAngle;
param.input_param.socket_recv_buf = (config.socketRecvBufBytes > 0)
? config.socketRecvBufBytes
: 33554432; // 32MB低配电脑防内核缓冲溢出
return param;
}
// ============================================================
// OpenDevice
// ============================================================
int CRsLidarDevice::OpenDevice(const RsLidarConfig& config)
{
if (!m_pDriver)
{
return -1;
}
CloseDevice();
m_param = toDriverParam(config);
if (config.minDistance > 0.0f || config.maxDistance > 0.0f)
{
m_rsem4MinDistance = (config.minDistance > 0.0f) ? config.minDistance : 0.0f;
m_rsem4MaxDistance = (config.maxDistance > 0.0f) ? config.maxDistance : kRsem4MaxDistance;
}
else
{
m_rsem4MinDistance = kRsem4MinDistance;
m_rsem4MaxDistance = kRsem4MaxDistance;
}
m_pDriver->regPointCloudCallback(
[this]() -> SdkCloudPtr { return this->getFreeCloud(); },
[this](SdkCloudPtr msg) { this->putStuffedCloud(msg); }
);
m_pDriver->regPacketCallback(
[this](const Packet& pkt) { this->onPacket(pkt); }
);
m_pDriver->regExceptionCallback(
[this](const Error& code) { this->onException(code); }
);
if (!m_pDriver->init(m_param))
{
return -2;
}
m_bOpened = true;
return 0;
}
// ============================================================
// CloseDevice
// ============================================================
int CRsLidarDevice::CloseDevice()
{
if (m_bRunning)
{
Stop();
}
m_bOpened = false;
m_freeQueue.clear();
m_frameFreeQueue.clear();
m_deliveryQueue.clear();
#ifdef _WIN32
if (--g_sockRefCount == 0)
{
WSACleanup();
}
#endif
return 0;
}
bool CRsLidarDevice::IsOpened() const
{
return m_bOpened;
}
// ============================================================
// Start / Stop
// ============================================================
int CRsLidarDevice::Start()
{
if (!m_bOpened || !m_pDriver)
{
return -1;
}
if (m_bRunning)
{
return 0;
}
// 重置诊断计数(每次 Start 开始新会话统计)
m_droppedFrameCount.store(0, std::memory_order_relaxed);
m_totalPushCount.store(0, std::memory_order_relaxed);
for (auto& c : m_exceptionCounts) c.store(0, std::memory_order_relaxed);
m_parseAccumUs.store(0, std::memory_order_relaxed);
m_parseMaxUs.store(0, std::memory_order_relaxed);
m_parseCount.store(0, std::memory_order_relaxed);
m_frameWaitAccumUs.store(0, std::memory_order_relaxed);
m_frameWaitMaxUs.store(0, std::memory_order_relaxed);
m_frameWaitCount.store(0, std::memory_order_relaxed);
m_frameWaitTimeoutCount.store(0, std::memory_order_relaxed);
m_deliveryDropCount.store(0, std::memory_order_relaxed);
m_deliveredFrameCount.store(0, std::memory_order_relaxed);
m_deliveryQueuePeak.store(0, std::memory_order_relaxed);
m_cbMaxUs.store(0, std::memory_order_relaxed);
m_cbAccumUs.store(0, std::memory_order_relaxed);
m_cbCount.store(0, std::memory_order_relaxed);
m_diagLastPushCount = 0;
m_diagLastQueueDropCount = 0;
m_diagLastParseCount = 0;
m_diagLastParseAccumUs = 0;
m_diagLastFrameWaitCount = 0;
m_diagLastFrameWaitAccumUs = 0;
m_diagLastFrameWaitTimeoutCount = 0;
m_diagLastDeliveryDropCount = 0;
m_diagLastDeliveredFrameCount = 0;
m_diagLastCbCount = 0;
m_diagLastCbAccumUs = 0;
m_diagLastExceptionCounts.fill(0);
m_diagLastLogTime = std::chrono::steady_clock::now();
resetPacketFrameBuilder();
// 清空可能残留的预热缓冲(防止 Stop→Start 循环累积膨胀)
m_freeQueue.clear();
for (int i = 0; i < 4; ++i)
{
auto msg = std::make_shared<SdkCloudMsg>();
m_freeQueue.push(std::move(msg));
}
m_bStopProcess = false;
// 预分配交付帧6 帧 × 1200 线 × 520 点 × 32B ≈ 120MB
// 帧 0=SDK packet callback 填充中,其余用于交付队列和短时回调抖动缓冲
m_deliveryQueue.clear();
m_frameFreeQueue.clear();
for (size_t i = 0; i < kDeliveryFramePoolSize; ++i)
{
auto frame = std::make_shared<DeliveryFrame>();
frame->reserveLines(kRsem4ReserveColumns, kRsem4DefaultRings);
m_frameFreeQueue.push(frame);
}
m_bRunning = true;
m_bStopDelivery = false;
m_deliveryThread = std::thread(&CRsLidarDevice::deliveryThread, this);
m_pDriver->start();
return 0;
}
int CRsLidarDevice::Stop()
{
if (!m_bRunning)
{
return 0;
}
// ① 停止包级解析
m_bStopProcess = true;
if (m_pDriver)
{
m_pDriver->stop();
}
resetPacketFrameBuilder();
// ② 停止交付线程(推入空帧唤醒 popWait避免等超时
m_bStopDelivery = true;
m_deliveryQueue.push(std::make_shared<DeliveryFrame>());
if (m_deliveryThread.joinable())
{
m_deliveryThread.join();
}
// ③ 释放所有队列内存
m_deliveryQueue.clear();
m_frameFreeQueue.clear();
m_bRunning = false;
return 0;
}
bool CRsLidarDevice::IsRunning() const
{
return m_bRunning;
}
// ============================================================
// 回调注册
// ============================================================
int CRsLidarDevice::SetPointCloudCallback(PointCloudCallback callback)
{
std::lock_guard<std::mutex> lock(m_callbackMutex);
m_pointCloudCallback = std::move(callback);
return 0;
}
int CRsLidarDevice::SetPacketCallback(PacketCallback callback)
{
bool hasCallback = false;
{
std::lock_guard<std::mutex> lock(m_callbackMutex);
m_packetCallback = std::move(callback);
hasCallback = static_cast<bool>(m_packetCallback);
}
m_hasPacketCallback.store(hasCallback, std::memory_order_release);
return 0;
}
int CRsLidarDevice::SetExceptionCallback(ExceptionCallback callback)
{
std::lock_guard<std::mutex> lock(m_callbackMutex);
m_exceptionCallback = std::move(callback);
return 0;
}
std::string CRsLidarDevice::GetVersion()
{
return getDriverVersion();
}
// ============================================================
// 内部5s 窗口诊断日志输出 + 字段重置(仅当距上次输出 ≥5s 时实际输出)
// 由 SDK 包处理线程触发,计数器只做 relaxed 采样,避免诊断影响实时路径。
// ============================================================
void CRsLidarDevice::emitDiagnosticLogIfDue(std::chrono::steady_clock::time_point& lastLogTime)
{
auto now = std::chrono::steady_clock::now();
if (std::chrono::duration_cast<std::chrono::seconds>(now - lastLogTime).count() < 5)
{
return;
}
const double sec = std::chrono::duration_cast<std::chrono::duration<double>>(now - lastLogTime).count();
const uint64_t pushCount = m_totalPushCount.load(std::memory_order_relaxed);
const uint64_t queueDropCount = m_droppedFrameCount.load(std::memory_order_relaxed);
const uint64_t frameWaitTimeoutCount = m_frameWaitTimeoutCount.load(std::memory_order_relaxed);
const uint64_t cbCount = m_cbCount.load(std::memory_order_relaxed);
const uint64_t cbAccumUs = m_cbAccumUs.load(std::memory_order_relaxed);
const uint64_t pushDelta = pushCount - m_diagLastPushCount;
const uint64_t queueDropDelta = queueDropCount - m_diagLastQueueDropCount;
const uint64_t frameWaitTimeoutDelta = frameWaitTimeoutCount - m_diagLastFrameWaitTimeoutCount;
const uint64_t cbDelta = cbCount - m_diagLastCbCount;
const uint64_t cbAccumDelta = cbAccumUs - m_diagLastCbAccumUs;
uint64_t excDelta[EXC_BUCKET_COUNT] = {};
for (size_t i = 0; i < EXC_BUCKET_COUNT; ++i)
{
const uint64_t current = m_exceptionCounts[i].load(std::memory_order_relaxed);
excDelta[i] = current - m_diagLastExceptionCounts[i];
m_diagLastExceptionCounts[i] = current;
}
fprintf(stderr,
"[RsLidarDevice][diag %.1fs] fps=%.1f frames=%llu(+%llu) "
"drop=%llu(+%llu) q=%zu/%zu waitTO=%llu(+%llu) "
"cb_us=%llu/%llu n=%llu exc48=%llu exc49=%llu exc42=%llu\n",
sec,
(sec > 0.0) ? (static_cast<double>(pushDelta) / sec) : 0.0,
static_cast<unsigned long long>(pushCount),
static_cast<unsigned long long>(pushDelta),
static_cast<unsigned long long>(queueDropCount),
static_cast<unsigned long long>(queueDropDelta),
static_cast<size_t>(m_deliveryQueue.size()),
static_cast<size_t>(m_deliveryQueuePeak.load(std::memory_order_relaxed)),
static_cast<unsigned long long>(frameWaitTimeoutCount),
static_cast<unsigned long long>(frameWaitTimeoutDelta),
static_cast<unsigned long long>((cbDelta > 0) ? (cbAccumDelta / cbDelta) : 0),
static_cast<unsigned long long>(m_cbMaxUs.load(std::memory_order_relaxed)),
static_cast<unsigned long long>(cbDelta),
static_cast<unsigned long long>(excDelta[2]),
static_cast<unsigned long long>(excDelta[3]),
static_cast<unsigned long long>(excDelta[4]));
m_diagLastPushCount = pushCount;
m_diagLastQueueDropCount = queueDropCount;
m_diagLastFrameWaitTimeoutCount = frameWaitTimeoutCount;
m_diagLastCbCount = cbCount;
m_diagLastCbAccumUs = cbAccumUs;
lastLogTime = now;
}
// 内部:交付线程(只调回调,不做数据处理)
// 从 m_deliveryQueue 取帧 → 调 PointCloudCallbackPushFrame memcpy 在此发生)
// 与 SDK packet callback 并行:交付帧 N 的同时,解析帧 N+1
// ============================================================
void CRsLidarDevice::deliveryThread()
{
while (!m_bStopDelivery)
{
auto frame = m_deliveryQueue.popWait(500000);
if (!frame) continue; // 超时 → 检查 m_bStopDelivery
// 取回调(持锁时间极短)
PointCloudCallback cb;
{
std::lock_guard<std::mutex> lock(m_callbackMutex);
cb = m_pointCloudCallback;
}
if (frame->cloudData.empty())
{
m_frameFreeQueue.push(frame);
continue;
}
if (cb)
{
auto t0 = std::chrono::steady_clock::now();
cb(frame->cloudData, frame->info);
auto t1 = std::chrono::steady_clock::now();
// 累计回调耗时统计
uint64_t us = static_cast<uint64_t>(
std::chrono::duration_cast<std::chrono::microseconds>(t1 - t0).count());
m_cbAccumUs.fetch_add(us, std::memory_order_relaxed);
m_cbCount.fetch_add(1, std::memory_order_relaxed);
uint64_t prevMax = m_cbMaxUs.load(std::memory_order_relaxed);
while (us > prevMax &&
!m_cbMaxUs.compare_exchange_weak(prevMax, us, std::memory_order_relaxed))
{}
}
// 归还交付帧到空闲池(清空 cloudData 元数据,保留 pointPool capacity
m_deliveredFrameCount.fetch_add(1, std::memory_order_relaxed);
frame->cloudData.clear();
m_frameFreeQueue.push(frame);
}
}
// ============================================================
// 内部:空闲/就绪队列回调rs_driver 内部线程)
// ============================================================
CRsLidarDevice::SdkCloudPtr CRsLidarDevice::getFreeCloud()
{
auto msg = m_freeQueue.pop();
if (msg) return msg;
return std::make_shared<SdkCloudMsg>();
}
void CRsLidarDevice::putStuffedCloud(SdkCloudPtr msg)
{
if (msg)
{
msg->points.clear();
m_freeQueue.push(msg);
}
}
// ============================================================
// 内部Packet → RsPacketInfo
// ============================================================
void CRsLidarDevice::resetPacketFrameBuilder()
{
if (m_activeFrame)
{
m_activeFrame->cloudData.clear();
m_frameFreeQueue.push(m_activeFrame);
}
m_activeFrame.reset();
m_activeFrameHasData = false;
m_activeFrameAllValid = true;
m_activeFrameStartedAtBoundary = false;
resetActiveFrameTracking();
m_rsem4FrameSynced = false;
m_hasRsem4PrevSeq = false;
m_prevRsem4Seq = 0;
m_hasRsem4PrevColumn = false;
m_prevRsem4Column = 0;
m_rsem4HasPartialPacket = false;
m_rsem4PartialSeq = 0;
m_rsem4PartialLen = 0;
m_diagPacketTick = 0;
}
void CRsLidarDevice::initializeRsem4DefaultAngles()
{
m_rsem4YawOffset.fill(0);
m_rsem4SurfacePitchOffset.fill(0);
for (size_t i = 0; i < m_rsem4PitchAngle.size(); ++i)
m_rsem4PitchAngle[i] = static_cast<int16_t>(-1300 + static_cast<int>(i) * 5);
m_rsem4AnglesReady = false;
updateRsem4PitchTrig();
}
void CRsLidarDevice::updateRsem4PitchTrig()
{
for (size_t surface = 0; surface < kRsem4SurfaceCount; ++surface)
for (size_t i = 0; i < m_rsem4PitchAngle.size(); ++i)
{
const int pitch = static_cast<int>(m_rsem4PitchAngle[i]) +
static_cast<int>(m_rsem4SurfacePitchOffset[surface]);
m_rsem4PitchSin[surface][i] = SinDeg01(pitch);
m_rsem4PitchCos[surface][i] = CosDeg01(pitch);
}
}
DeliveryFramePtr CRsLidarDevice::acquireFrameForBuild()
{
while (!m_bStopProcess.load(std::memory_order_relaxed))
{
const auto t0 = std::chrono::steady_clock::now();
auto frame = m_frameFreeQueue.popWait(100000);
const auto t1 = std::chrono::steady_clock::now();
const uint64_t waitUs = ElapsedUs(t0, t1);
m_frameWaitAccumUs.fetch_add(waitUs, std::memory_order_relaxed);
m_frameWaitCount.fetch_add(1, std::memory_order_relaxed);
UpdateMax(m_frameWaitMaxUs, waitUs);
if (frame)
{
resetDeliveryFrame(frame);
return frame;
}
m_frameWaitTimeoutCount.fetch_add(1, std::memory_order_relaxed);
}
return DeliveryFramePtr();
}
void CRsLidarDevice::resetDeliveryFrame(const DeliveryFramePtr& frame)
{
if (!frame)
return;
const size_t pointCount =
static_cast<size_t>(kRsem4ReserveColumns) * static_cast<size_t>(kRsem4DefaultRings);
if (frame->pointPool.size() != pointCount)
frame->pointPool.resize(pointCount);
frame->cloudData.clear();
frame->cloudData.reserve(kRsem4ReserveColumns);
std::memset(frame->pointPool.data(), 0, frame->pointPool.size() * sizeof(SVzNLPointXYZI));
for (uint32_t col = 0; col < kRsem4ReserveColumns; ++col)
{
SVzNLPointXYZI* linePts = frame->pointPool.data() +
static_cast<size_t>(col) * static_cast<size_t>(kRsem4DefaultRings);
SVzLaserLineData ld{};
ld.p3DPoint = linePts;
ld.nPointCount = static_cast<int>(kRsem4DefaultRings);
ld.llFrameIdx = col;
ld.llTimeStamp = col;
ld.nEncodeNo = static_cast<int>(col);
ld.bEndOnceScan = (col + 1 == kRsem4ReserveColumns) ? VzTrue : VzFalse;
frame->cloudData.emplace_back(keResultDataType_PointXYZI, ld);
}
frame->info.height = kRsem4ReserveColumns;
frame->info.width = kRsem4DefaultRings;
frame->info.isDense = false;
}
void CRsLidarDevice::submitActiveFrame()
{
if (!m_activeFrame)
return;
auto frame = m_activeFrame;
m_activeFrame.reset();
const bool completeFrame =
m_activeFrameHasData &&
m_activeFrameStartedAtBoundary &&
!m_activeFramePacketLoss &&
m_activeCompleteColumns == kRsem4ReserveColumns;
if (!completeFrame)
{
frame->cloudData.clear();
m_frameFreeQueue.push(frame);
if (m_activeFrameStartedAtBoundary && m_activeFrameHasData)
m_droppedFrameCount.fetch_add(1, std::memory_order_relaxed);
m_activeFrameStartedAtBoundary = false;
resetActiveFrameTracking();
return;
}
frame->info.height = kRsem4ReserveColumns;
frame->info.width = kRsem4DefaultRings;
frame->info.isDense = m_activeFrameAllValid;
const uint64_t parseUs = ElapsedUs(m_activeFrameStartTime, std::chrono::steady_clock::now());
m_parseAccumUs.fetch_add(parseUs, std::memory_order_relaxed);
m_parseCount.fetch_add(1, std::memory_order_relaxed);
UpdateMax(m_parseMaxUs, parseUs);
while (!m_bStopDelivery.load(std::memory_order_relaxed))
{
const size_t depth = m_deliveryQueue.push(frame);
if (depth > 0)
{
UpdateMaxSize(m_deliveryQueuePeak, depth);
m_totalPushCount.fetch_add(1, std::memory_order_relaxed);
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
if (m_bStopDelivery.load(std::memory_order_relaxed))
m_frameFreeQueue.push(frame);
m_activeFrameHasData = false;
m_activeFrameAllValid = true;
m_activeFrameStartedAtBoundary = false;
resetActiveFrameTracking();
}
void CRsLidarDevice::resetActiveFrameTracking()
{
m_activeFramePacketLoss = false;
m_activeCompleteColumns = 0;
m_activeColumnCompleteMask = 0;
m_activeColumnMask.fill(0);
}
bool CRsLidarDevice::beginActiveFrame(bool startedAtBoundary)
{
if (m_activeFrame)
return true;
m_activeFrame = acquireFrameForBuild();
if (!m_activeFrame)
return false;
m_activeFrameStartTime = std::chrono::steady_clock::now();
m_activeFrameStartedAtBoundary = startedAtBoundary;
m_activeFrameHasData = false;
m_activeFrameAllValid = true;
resetActiveFrameTracking();
return true;
}
bool CRsLidarDevice::prepareRsem4FrameForColumn(uint32_t column)
{
if (column >= kRsem4ReserveColumns)
return false;
const bool columnWrap = m_hasRsem4PrevColumn && (column + 10 < m_prevRsem4Column);
const bool columnStart = (column == 0);
if (columnWrap && m_activeFrameHasData)
submitActiveFrame();
m_hasRsem4PrevColumn = true;
m_prevRsem4Column = column;
if (!m_activeFrame)
{
if (!columnStart)
return false;
return beginActiveFrame(true);
}
return true;
}
void CRsLidarDevice::markRsem4ColumnSegment(uint32_t column, uint8_t segmentMask, uint8_t completeMask)
{
if (!m_activeFrame || column >= kRsem4ReserveColumns || segmentMask == 0)
return;
if (m_activeColumnCompleteMask == 0)
{
m_activeColumnCompleteMask = completeMask;
}
else if (m_activeColumnCompleteMask != completeMask)
{
m_activeFramePacketLoss = true;
return;
}
uint8_t& mask = m_activeColumnMask[column];
const bool wasComplete = ((mask & completeMask) == completeMask);
mask = static_cast<uint8_t>(mask | segmentMask);
const bool isComplete = ((mask & completeMask) == completeMask);
if (!wasComplete && isComplete)
++m_activeCompleteColumns;
}
void CRsLidarDevice::fillRsem4Point(uint32_t column, uint32_t ring, float distance,
uint8_t intensity, uint8_t surfaceIndex,
float cosYaw, float sinYaw)
{
if (!m_activeFrame || column >= kRsem4ReserveColumns || ring >= kRsem4DefaultRings)
return;
const size_t pointIndex =
static_cast<size_t>(column) * static_cast<size_t>(kRsem4DefaultRings) +
static_cast<size_t>(kRsem4DefaultRings - 1 - ring);
if (pointIndex >= m_activeFrame->pointPool.size())
return;
auto& dst = m_activeFrame->pointPool[pointIndex];
if (surfaceIndex >= kRsem4SurfaceCount || distance < m_rsem4MinDistance || distance > m_rsem4MaxDistance)
{
SetZeroPoint(dst);
m_activeFrameAllValid = false;
return;
}
const float sinPitch = m_rsem4PitchSin[surfaceIndex][ring];
const float cosPitch = m_rsem4PitchCos[surfaceIndex][ring];
const float x = distance * cosPitch * cosYaw;
const float y = distance * cosPitch * sinYaw;
const float z = distance * sinPitch;
dst.fData[0] = -y * 1000.0f;
dst.fData[1] = -z * 1000.0f;
dst.fData[2] = x * 1000.0f;
dst.fData_c[0] = static_cast<float>(intensity);
}
void CRsLidarDevice::processRsem4Difop(const uint8_t* data, size_t size)
{
if (!data)
return;
auto applyAngles = [this](const int8_t* yaw, const int16_t* pitch, const int16_t* surface) {
for (size_t i = 0; i < kRsem4VecselsPerColumn; ++i)
m_rsem4YawOffset[i] = yaw[i];
for (size_t i = 0; i < kRsem4DefaultRings; ++i)
m_rsem4PitchAngle[i] = SwapI16(pitch[i]);
for (size_t i = 0; i < kRsem4SurfaceCount; ++i)
m_rsem4SurfacePitchOffset[i] = SwapI16(surface[i]);
m_rsem4AnglesReady = true;
updateRsem4PitchTrig();
};
if (size == sizeof(Rsem4DifopPkt))
{
const auto* pkt = reinterpret_cast<const Rsem4DifopPkt*>(data);
applyAngles(pkt->yawOffset, pkt->pitchAngle, pkt->surfacePitchOffset);
}
else if (size == sizeof(Rsem4Difop2Pkt))
{
const auto* pkt = reinterpret_cast<const Rsem4Difop2Pkt*>(data);
applyAngles(pkt->yawOffset, pkt->pitchAngle, pkt->surfacePitchOffset);
}
else if (size == sizeof(Rsem4Difop0624Pkt))
{
const auto* pkt = reinterpret_cast<const Rsem4Difop0624Pkt*>(data);
applyAngles(pkt->yawOffset, pkt->pitchAngle, pkt->surfacePitchOffset);
}
}
bool CRsLidarDevice::processRsem4Packet(const Packet& pkt)
{
const uint8_t* data = pkt.buf_.empty() ? nullptr : pkt.buf_.data();
const size_t size = pkt.buf_.size();
if (!data || size < 4)
return false;
if (pkt.is_difop)
{
processRsem4Difop(data, size);
return false;
}
return processRsem4Msop(data, size);
}
bool CRsLidarDevice::processRsem4Msop(const uint8_t* data, size_t size)
{
if (IsCompleteRsem4Packet(data, size))
return processRsem4CompleteMsop(data, size);
if (size < sizeof(Rsem4MsopHeader2))
return false;
const auto& header2 = *reinterpret_cast<const Rsem4MsopHeader2*>(data);
const uint16_t seq = ReadU16BE(header2.pktSeq);
if (!m_rsem4HasPartialPacket || seq != m_rsem4PartialSeq)
{
m_rsem4HasPartialPacket = false;
m_rsem4PartialLen = 0;
if (m_activeFrame)
m_activeFramePacketLoss = true;
return false;
}
const size_t payloadOffset = sizeof(Rsem4MsopHeader2);
const size_t payloadLen = size - payloadOffset;
if (m_rsem4PartialLen + payloadLen > m_rsem4PartialPacket.size())
{
m_rsem4HasPartialPacket = false;
m_rsem4PartialLen = 0;
if (m_activeFrame)
m_activeFramePacketLoss = true;
return false;
}
std::memcpy(m_rsem4PartialPacket.data() + m_rsem4PartialLen, data + payloadOffset, payloadLen);
m_rsem4PartialLen += payloadLen;
m_rsem4HasPartialPacket = false;
return processRsem4CompressedMsop(m_rsem4PartialPacket.data(), m_rsem4PartialLen);
}
bool CRsLidarDevice::processRsem4CompleteMsop(const uint8_t* data, size_t size)
{
if (size < sizeof(Rsem4MsopHeader))
return false;
const auto& header = *reinterpret_cast<const Rsem4MsopHeader*>(data);
const uint8_t packMode = header.packMode & 0x03;
if (packMode == 0x03)
{
const uint16_t compressedSeq = ReadU16BE(header.pktSeq);
const uint8_t splitPackNum = header.packMode >> 4;
if (splitPackNum == 0)
return processRsem4CompressedMsop(data, size);
if (size > m_rsem4PartialPacket.size())
{
if (m_activeFrame)
m_activeFramePacketLoss = true;
return false;
}
std::memcpy(m_rsem4PartialPacket.data(), data, size);
m_rsem4PartialLen = size;
m_rsem4PartialSeq = compressedSeq;
m_rsem4HasPartialPacket = true;
return false;
}
if (size < sizeof(Rsem4MsopPkt))
return false;
const auto& pkt = *reinterpret_cast<const Rsem4MsopPkt*>(data);
const uint16_t pktSeqRaw = ReadU16BE(pkt.header.pktSeq);
const uint16_t pktSeq = (pktSeqRaw > 0) ? static_cast<uint16_t>(pktSeqRaw - 1) : 0;
if (!m_rsem4FrameSynced)
{
m_rsem4FrameSynced = true;
m_hasRsem4PrevSeq = false;
m_hasRsem4PrevColumn = false;
}
m_prevRsem4Seq = pktSeq;
m_hasRsem4PrevSeq = true;
const uint16_t columnRaw = ReadU16BE(pkt.header.columnNum);
const uint32_t column = (columnRaw < kRsem4ReserveColumns)
? static_cast<uint32_t>(columnRaw)
: static_cast<uint32_t>((pktSeq / 2) % kRsem4ReserveColumns);
if (!prepareRsem4FrameForColumn(column))
return false;
m_activeFrameHasData = true;
const uint8_t surfaceIndex = (pkt.header.surfaceId > 0) ? static_cast<uint8_t>(pkt.header.surfaceId - 1) : 0;
const int16_t yawBase = SwapI16(pkt.header.yawAngle);
float yawCos[kRsem4VecselsPerColumn];
float yawSin[kRsem4VecselsPerColumn];
for (size_t i = 0; i < kRsem4VecselsPerColumn; ++i)
{
const int yaw = static_cast<int>(yawBase) + static_cast<int>(m_rsem4YawOffset[i]);
yawCos[i] = CosDeg01(yaw);
yawSin[i] = SinDeg01(yaw);
}
const bool dualReturn = (pkt.header.returnMode == 0x00);
const uint8_t segmentModulo = dualReturn ? 4 : 2;
const uint8_t segmentMask = static_cast<uint8_t>(1U << (pktSeq % segmentModulo));
const uint8_t completeMask = dualReturn ? 0x0F : 0x03;
for (uint32_t blk = 0; blk < kRsem4BlocksPerPacket; ++blk)
{
uint32_t ring = 0;
if (dualReturn)
{
if ((blk & 1U) != 0)
continue;
ring = (blk / 2) + (pktSeq % 4) * (kRsem4HalfRings / 2);
}
else
{
ring = blk + (pktSeq % 2) * kRsem4HalfRings;
}
if (ring >= kRsem4DefaultRings)
continue;
const auto& channel = pkt.blocks[blk].channel[0];
const float distance = static_cast<float>(ReadU16BE(channel.distance)) * kRsem4DistanceResolution;
const size_t vecsel = ring / kRsem4PixelsPerVcsel;
fillRsem4Point(column, ring, distance, channel.intensity, surfaceIndex, yawCos[vecsel], yawSin[vecsel]);
}
markRsem4ColumnSegment(column, segmentMask, completeMask);
return true;
}
bool CRsLidarDevice::processRsem4CompressedMsop(const uint8_t* data, size_t size)
{
if (size < sizeof(Rsem4MsopHeader) + kRsem4MsopTailLen)
return false;
const auto& header = *reinterpret_cast<const Rsem4MsopHeader*>(data);
const uint16_t pktSeq = ReadU16BE(header.pktSeq);
if (!m_rsem4FrameSynced)
{
m_rsem4FrameSynced = true;
m_hasRsem4PrevSeq = false;
m_hasRsem4PrevColumn = false;
}
m_prevRsem4Seq = pktSeq;
m_hasRsem4PrevSeq = true;
uint16_t decoded[kRsem4DefaultRings * 2] = {};
const int dataLen = static_cast<int>((size - sizeof(Rsem4MsopHeader) - kRsem4MsopTailLen) / 2);
CompressAlgo::RLenc_unpack_optimize(
reinterpret_cast<const uint16_t*>(data + sizeof(Rsem4MsopHeader)),
decoded,
dataLen,
16);
const uint16_t* radius = decoded;
const uint16_t* identity = decoded + kRsem4DefaultRings;
const uint16_t columnRaw = ReadU16BE(header.columnNum);
const uint32_t column = (columnRaw < kRsem4ReserveColumns)
? static_cast<uint32_t>(columnRaw)
: static_cast<uint32_t>(pktSeq % kRsem4ReserveColumns);
if (!prepareRsem4FrameForColumn(column))
return false;
m_activeFrameHasData = true;
const uint8_t surfaceIndex = (header.surfaceId > 0) ? static_cast<uint8_t>(header.surfaceId - 1) : 0;
const int16_t yawBase = SwapI16(header.yawAngle);
float yawCos[kRsem4VecselsPerColumn];
float yawSin[kRsem4VecselsPerColumn];
for (size_t i = 0; i < kRsem4VecselsPerColumn; ++i)
{
const int yaw = static_cast<int>(yawBase) + static_cast<int>(m_rsem4YawOffset[i]);
yawCos[i] = CosDeg01(yaw);
yawSin[i] = SinDeg01(yaw);
}
for (uint32_t ring = 0; ring < kRsem4DefaultRings; ++ring)
{
const float distance = static_cast<float>(radius[ring]) * kRsem4DistanceResolution;
const uint8_t intensity = static_cast<uint8_t>(identity[ring] & 0xFF);
const size_t vecsel = ring / kRsem4PixelsPerVcsel;
fillRsem4Point(column, ring, distance, intensity, surfaceIndex, yawCos[vecsel], yawSin[vecsel]);
}
markRsem4ColumnSegment(column, 0x01, 0x01);
return true;
}
void CRsLidarDevice::onPacket(const Packet& pkt)
{
if ((++m_diagPacketTick & 0x3FFU) == 0)
emitDiagnosticLogIfDue(m_diagLastLogTime);
if (m_param.lidar_type == LidarType::RSEM4)
processRsem4Packet(pkt);
if (!m_hasPacketCallback.load(std::memory_order_acquire))
return;
PacketCallback cb;
{
std::lock_guard<std::mutex> lock(m_callbackMutex);
cb = m_packetCallback;
}
if (cb)
{
RsPacketInfo info;
info.timestamp = pkt.timestamp;
info.seq = pkt.seq;
info.is_difop = pkt.is_difop;
info.is_frame_begin = pkt.is_frame_begin;
info.frameId = pkt.frame_id;
info.dataSize = static_cast<uint32_t>(pkt.buf_.size());
cb(info);
}
}
// ============================================================
// 监控接口
// ============================================================
uint64_t CRsLidarDevice::GetDroppedFrameCount() const
{
return m_droppedFrameCount.load();
}
size_t CRsLidarDevice::GetStuffedQueueDepth() const
{
return m_deliveryQueue.size();
}
uint64_t CRsLidarDevice::GetExceptionCount(int errCode) const
{
size_t bucket = errCodeToBucket(errCode);
return m_exceptionCounts[bucket].load(std::memory_order_relaxed);
}
RsCallbackStats CRsLidarDevice::GetCallbackLatencyStats() const
{
RsCallbackStats stats;
stats.count = m_cbCount.load(std::memory_order_relaxed);
stats.maxUs = m_cbMaxUs.load(std::memory_order_relaxed);
uint64_t acc = m_cbAccumUs.load(std::memory_order_relaxed);
stats.avgUs = (stats.count > 0) ? (acc / stats.count) : 0;
return stats;
}
size_t CRsLidarDevice::GetStuffedQueuePeak() const
{
return m_deliveryQueuePeak.load(std::memory_order_relaxed);
}
// ============================================================
// 内部Error → RsExceptionInfo按 ErrCode 分桶计数后转发)
// ============================================================
void CRsLidarDevice::onException(const Error& code)
{
// 分桶累加(用于诊断日志和 GetExceptionCount 查询)
size_t bucket = errCodeToBucket(static_cast<int>(code.error_code));
m_exceptionCounts[bucket].fetch_add(1, std::memory_order_relaxed);
ExceptionCallback cb;
{
std::lock_guard<std::mutex> lock(m_callbackMutex);
cb = m_exceptionCallback;
}
if (cb)
{
RsExceptionInfo info;
info.code = static_cast<int>(code.error_code);
info.message = code.toString();
cb(info);
}
}