简介
VelodynePacketInterpreters作用是解析雷达数据包,将二进制数据转换成对应的属性数据。
VelodynePacketInterpreters是一个vtk模块。文件夹下包含vtk.module, CMakeLists.txt以及对应的源代码和一些相关配置和资源文件。
其vtk.module代码如下:
NAME # 模块的名称,在cmake中可以通过该名称引用该模块
VelodynePlugin::VelodynePacketInterpreters
LIBRARY_NAME # 库文件的名称,即在windows平台下,会生成VelodynePacketInterpreters.dll文件
VelodynePacketInterpreters
DEPENDS # 该模块依赖的模块,使用该模块的模块也需要加载以下模块
LidarView::IOLidar
VTK::CommonCore
VTK::CommonDataModel
VTK::CommonExecutionModel
VTK::InteractionWidgets
PRIVATE_DEPENDS # 该模块依赖的模块,但是使用该模块的模块不需要加载该模块
LidarView::IONetwork
LidarView::FiltersProcessing
vtk.module文件中定义了模块的名称,生成库的名称以及依赖的其他模块。
CMakeLists.txt文件如下:
# Every PacketInterpreter found in the sources are added to the compilation (lidar, HDLPosition, ...)
# This allow to compile private interpreter (that not appear in the public repository)
# without changing the cmakelist in the private repository
# 添加 xml文件,该文件用于将模块加载到paraview界面上
file(GLOB_RECURSE velodyne_xml ${CMAKE_CURRENT_SOURCE_DIR}/*PacketInterpreter*.xml)
# 添加源代码文件
file(GLOB_RECURSE velodyne_sources ${CMAKE_CURRENT_SOURCE_DIR}/*PacketInterpreter*.cxx)
# 添加头文件
file(GLOB_RECURSE velodyne_headers ${CMAKE_CURRENT_SOURCE_DIR}/*PacketInterpreter*.h)
set(enable_advanced_packet_interpreter 0)
set(enable_special_velarray 0)
# AdvancedPacketInterpreter
if(velodyne_sources MATCHES "AdvancedPacketInterpreter")
set(enable_advanced_packet_interpreter 1)
endif()
# SpecialVelarray
if(velodyne_sources MATCHES "SpecialVelarray")
set(enable_special_velarray 1)
endif()
if(${enable_advanced_packet_interpreter} OR ${enable_special_velarray})
list(APPEND velodyne_sources
vtkVelodyneAdvancedPacketFraming.cxx
)
endif()
list(APPEND velodyne_sources
vtkRollingDataAccumulator.cxx
RpmCalculator.cxx
)
# 添加一个vtk模块
vtk_module_add_module(VelodynePlugin::VelodynePacketInterpreters
SOURCES ${velodyne_sources}
HEADERS ${velodyne_headers}
)
# ParaView使用XML文件来描述其用户界面中可用的过滤器。
# 模块可以通过提供XML文件向UI添加过滤器。
paraview_add_server_manager_xmls(
MODULE VelodynePlugin::VelodynePacketInterpreters
XMLS ${velodyne_xml}
)
# vtk_module_definitions 是对 target_compile_definitions的封装
# 定义了两个宏,并设置了宏的值
vtk_module_definitions(VelodynePlugin::VelodynePacketInterpreters
PRIVATE
"VELOVIEW_HAS_ADVANCED_INTERPRETER=${enable_advanced_packet_interpreter}"
"VELOVIEW_HAS_VELARRAY=${enable_special_velarray}"
)
CMakeLists.txt文件中指定了模块的代码,然后调用vtk_module_add_module声明一个vtk模块,调用paraview_add_server_manager_xmls为该模块添加了paraview服务端的的配置文件。最后调用了vtk_module_definitions定义了两个宏。
该模块中定义了一个虚基类vtkVelodyneBasePacketInterpreter,一个用于创建解析数据实例的类vtkVelodyneLegacyPacketInterpreter和一个管理解析类的类vtkVelodyneMetaPacketInterpreter。
渲染管线数据流
vtkVelodyneBasePacketInterpreter的基类是vtkLidarPacketInterpreter。
vtkLidarPacketInterpreter是LidarView::IOLidar模块下的类。该类包含了多个纯虚函数,用于对雷达数据包的前处理、解析等工作。
vtkLidarPacketInterpreter的基类是vtkInterpreter。
vtkInterpreter是LidarView::IONetwork模块下的类。vtkInterpreter的基类是vtkAlgorithm。
vtkInterpreter也是个纯虚基类,其中数据包解析的系一列顺序函数。该类除了包装器以及一些设置器(SetXXX)以及获取器(GetXXX),只实现了一个获取时间的函数,代码如下:
vtkMTimeType vtkInterpreter::GetMTime()
{
if (this->SensorTransform)
{
return std::max(this->Superclass::GetMTime(), this->SensorTransform->GetMTime());
}
return this->Superclass::GetMTime();
}
该函数用于返回时间。调用了vtkAlgorithm中的GetMTime函数,如果SensorTransform存在实例,则与vtkTransform* SensorTransform中的时间进行比较,返回最新的时间。
在vtk的渲染管线中,通过调用vtkAlgorithm::ProcessRequest完成本Filter内实际的数据处理
在vtkAlgorithm中,ProcessRequrest的声明如下:
/**
* Upstream/Downstream requests form the generalized interface
* through which executives invoke a algorithm's functionality.
* Upstream requests correspond to information flow from the
* algorithm's outputs to its inputs. Downstream requests
* correspond to information flow from the algorithm's inputs to its
* outputs.
* A downstream request is defined by the contents of the request
* information object. The input to the request is stored in the
* input information vector passed to ProcessRequest. The results
* of an downstream request are stored in the output information
* vector passed to ProcessRequest.
* An upstream request is defined by the contents of the request
* information object. The input to the request is stored in the
* output information vector passed to ProcessRequest. The results
* of an upstream request are stored in the input information vector
* passed to ProcessRequest.
* It returns the boolean status of the pipeline (false
* means failure).
*/
virtual vtkTypeBool ProcessRequest(
vtkInformation* request, vtkInformationVector** inInfo, vtkInformationVector* outInfo);
/**
* Version of ProcessRequest() that is wrapped. This converts the
* collection to an array and calls the other version.
*/
vtkTypeBool ProcessRequest(
vtkInformation* request, vtkCollection* inInfo, vtkInformationVector* outInfo);
ProcessRequrest由两个重载,一个是包含请求、输入、输出的主要函数,另一个是调用了主要函数的一个封装,其实现如下:
vtkTypeBool vtkAlgorithm::ProcessRequest(
vtkInformation* request, vtkCollection* inInfo, vtkInformationVector* outInfo)
{
vtkSmartPointer<vtkCollectionIterator> iter;
iter.TakeReference(inInfo->NewIterator());
std::vector<vtkInformationVector*> ivectors;
for (iter->GoToFirstItem(); !iter->IsDoneWithTraversal(); iter->GoToNextItem())
{
vtkInformationVector* iv = vtkInformationVector::SafeDownCast(iter->GetCurrentObject());
if (!iv)
{
return 0;
}
ivectors.push_back(iv);
}
if (ivectors.empty())
{
return this->ProcessRequest(request, (vtkInformationVector**)nullptr, outInfo);
}
else
{
return this->ProcessRequest(request, ivectors.data(), outInfo);
}
}
//------------------------------------------------------------------------------
vtkTypeBool vtkAlgorithm::ProcessRequest(
vtkInformation* /* request */, vtkInformationVector**, vtkInformationVector*)
{
return 1;
}
可以看到,形参列表为vtkInformation* request, vtkCollection* inInfo, vtkInformationVector* outInfo的函数对vtkCollection* inInfo进行了解析,然后调用了形参列表为vtkInformation* request, vtkInformationVector** inInfo, vtkInformationVector* outInfo的函数。而在vtkAlgorithm中该函数只是简单返回1。
在vtkLidarPacketInterpreter和vtkInterpreter中都未对ProcessRequest进行重载,即vtkLidarPacketInterpreter的子类只是用于对雷达数据包进行解析,并没有添加到渲染管线中。
对LidarView::IOLidar模块中的vtkLidarReader进行分析。vtkLidarReader的基类是vtkPolyDataAlgorithm。该类中重载了ProcessRequest函数,声明如下:
// LidarView::IOLidar模块中的vtkLidarReader
int RequestData(vtkInformation* request,
vtkInformationVector** inputVector,
vtkInformationVector* outputVector) override;
其实现如下:
// LidarView::IOLidar模块中的vtkLidarReader
//-----------------------------------------------------------------------------
int vtkLidarReader::RequestData(vtkInformation* vtkNotUsed(request),
vtkInformationVector** vtkNotUsed(inputVector),
vtkInformationVector* outputVector)
{
// 获取输入
vtkPolyData* output = vtkPolyData::GetData(outputVector);
vtkTable* calibration = vtkTable::GetData(outputVector, 1);
vtkInformation* info = outputVector->GetInformationObject(0);
if (this->FileName.empty())
{
vtkErrorMacro("FileName has not been set.");
return 0;
}
// vtkLidarPacketInterpreter* Interpreter = nullptr;
if (!this->Interpreter)
{
vtkErrorMacro("Interpreter has not been set.");
return 0;
}
// 判断该Interpreter是否已经加载了配置文件
// 该函数返回的成员变量在vtkLidarPacketInterpreter中定义bool IsCalibrated = false;
// 初始化为false,在vtkVelodyneBasePacketInterpreter::LoadCalibration函数中进行修改
// 如果加载配置文件成功,则改为true
if (!this->Interpreter->GetIsCalibrated())
{
vtkErrorMacro("The calibration could not be determined from the pcap file!");
return 0;
}
// 浅拷贝,不拷贝数据内存,只拷贝数据的指针,两个对象实例指向同一个内存空间
calibration->ShallowCopy(this->Interpreter->GetCalibrationTable());
// This mean that the reader did not manage to parser the pcap file
if (this->FrameCatalog.size() <= 1)
{
return 1;
}
double timestep = 0.0;
if (info->Has(vtkStreamingDemandDrivenPipeline::UPDATE_TIME_STEP()))
{
timestep = info->Get(vtkStreamingDemandDrivenPipeline::UPDATE_TIME_STEP());
}
int frameRequested = 0;
if (this->UsePacketTimeForDisplayTime)
{
frameRequested = GetFrameIndexForDataTime(timestep);
}
else
{
frameRequested = GetFrameIndexForPacketTime(timestep);
}
// detect frame dropping
if (this->DetectFrameDropping)
{
int step = frameRequested - this->LastFrameProcessed;
if (step > 1)
{
std::stringstream text;
text << "WARNING : At frame " << std::right << std::setw(6) << frameRequested << " Drop "
<< std::right << std::setw(2) << step - 1 << " frame(s)\n";
vtkWarningMacro(<< text.str());
}
}
this->LastFrameProcessed = frameRequested;
//! @todo we should no open the pcap file everytime a frame is requested !!!
this->Open();
// 调用GetFrame函数,获取数据,其返回类型为tkSmartPointer<vtkPolyData>
output->ShallowCopy(this->GetFrame(frameRequested));
this->Close();
return 1;
}
在该实现中,获取数据的关键代码是this->GetFrame(frameRequested),并将其浅拷贝到output中。output指向outputVector中索引为0的元素。GetFrame函数的代码如下:
vtkSmartPointer<vtkPolyData> vtkLidarReader::GetFrame(int frameNumber)
{
// 清空当前帧的内存,当前帧类型为vtkSmartPointer<vtkPolyData> CurrentFrame;
this->Interpreter->ResetCurrentFrame();
// 清空加载的所有帧数据
// 保存加载的所有帧的类型为std::vector<vtkSmartPointer<vtkPolyData> > Frames;
this->Interpreter->ClearAllFramesAvailable();
if (!this->Reader)
{
vtkErrorMacro("GetFrame() called but packet file reader is not open.");
return 0;
}
if (!this->Interpreter->GetIsCalibrated())
{
vtkErrorMacro("Calibration data has not been loaded.");
return 0;
}
const unsigned char* data = 0;
unsigned int dataLength = 0;
double timeSinceStart = 0;
// Update the interpreter meta data according to the requested frame
FrameInformation currInfo = this->FrameCatalog[frameNumber];
// 加载需要帧的元数据
// this->FrameCatalog类型为std::vector<FrameInformation>
// FrameInformation中保存了所需要帧的第一包数据的起始位置,时间等信息。
this->Interpreter->SetParserMetaData(this->FrameCatalog[frameNumber]);
// 将文件流指定到该帧数据的起始位置。
this->Reader->SetFilePosition(&currInfo.FilePosition);
// 获取该帧的内容,一帧数据由多个数据包组成
while (this->Reader->NextPacket(data, dataLength, timeSinceStart))
{
// If the current packet is not a lidar packet,
// skip it and update the file position
if (!this->Interpreter->IsLidarPacket(data, dataLength))
{
continue;
}
// Process the lidar packet and check
// if the required frame is ready
// 该函数调用了vtkLidarPacketInterpreter::ProcessPacket函数
this->Interpreter->ProcessPacketWrapped(data, dataLength, timeSinceStart);
// IsNewFrameReady的判断很简单,只要this->Frames中有数据,则为true
if (this->Interpreter->IsNewFrameReady())
{
// 返回this->Frames中的最后一个位置的数据
return this->Interpreter->GetLastFrameAvailable();
}
}
// 如果数据没有达到一帧的标准,但是运行到这也没有其他数据,则强行将数据划分为 一帧
this->Interpreter->SplitFrame(true);
return this->Interpreter->GetLastFrameAvailable();
}
通过this->Reader进行数据读取,其类型为vtkPacketFileReader* Reader = nullptr。
使用this->ProcessPacketWrapped函数进行数据解析,该函数调用了vtkInterpreter::ProcessPacket函数,该函数在vtkInterpreter类中属于纯虚函数,在vtkLidarPacketInterpreter也没有重载该函数。
因此,子类需要重载ProcessPacket函数来进行数据解析,然后将解析好的一帧数据放到Frames中,接着Frames中的最后一个元素上的数据会被vtkLidarReader输入到渲染管线中。
SplitFrame函数用于将解析好的完整一帧数据放到Frames中。其传入参数有一个枚举FramingMethod_t 。
enum FramingMethod_t {
INTERPRETER_FRAMING = 0, // the interpreter in charge of the framing
NETWORK_PACKET_TIME_FRAMING = 1 // interpreter not in charge
};
当传入为INTERPRETER_FRAMING 时,则需要在ProcessPacket中判断数据是否累积够一帧,然后调用SplitFrame函数。
如果传入参数为NETWORK_PACKET_TIME_FRAMING ,则会根据数据包中的时间来判断是否为一帧,即默认同一帧中的所有数据包的时间相同,当不同时则说明上一帧数据已经结束,调用SplitFrame进行帧划分。该过程是在ProcessPacketWrapped函数执行的。
总结
VelodynePacketInterpreters并不负责数据流的传递,只负责数据的解析。该模块中由三个类,定义了一个虚基类vtkVelodyneBasePacketInterpreter,一个用于创建解析数据实例的类vtkVelodyneLegacyPacketInterpreter和一个管理解析类的类vtkVelodyneMetaPacketInterpreter。
其中vtkVelodyneBasePacketInterpreter和vtkVelodyneMetaPacketInterpreter的基类为vtkLidarPacketInterpreter。
vtkVelodyneLegacyPacketInterpreter的基类为vtkVelodyneBasePacketInterpreter。
vtkVelodyneBasePacketInterpreter中有一个纯虚函数virtual std::string GetSensorName() = 0;用于返回雷达的名称。
下图只包含主要函数。