LidarView源码分析(八)VelodynePacketInterpreters数据流

 简介

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;用于返回雷达的名称。

下图只包含主要函数。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值