1. 程式人生 > >Cartographer原始碼閱讀(2):Node和MapBuilder物件

Cartographer原始碼閱讀(2):Node和MapBuilder物件

  上文提到特別注意map_builder_bridge_.AddTrajectory(x,x),檢視其中的程式碼。兩點:

  首先是map_builder_.AddTrajectoryBuilder(...),呼叫了map_builder_物件的方法。其次是sensor_bridges_鍵值對的賦值。

int MapBuilderBridge::AddTrajectory(const std::unordered_set<std::string>& expected_sensor_ids,  const TrajectoryOptions& trajectory_options)
{
     const int trajectory_id = map_builder_.AddTrajectoryBuilder(expected_sensor_ids, trajectory_options.trajectory_builder_options,                   
                  ::std::bind(&MapBuilderBridge::OnLocalSlamResult, this,
                  ::std::placeholders::_1, ::std::placeholders::_2,
                  ::std::placeholders::_3, ::std::placeholders::_4,
                  ::std::placeholders::_5));
     LOG(INFO) << "Added trajectory with ID '" << trajectory_id << "'.";
 
     // Make sure there is no trajectory with 'trajectory_id' yet.
     CHECK_EQ(sensor_bridges_.count(trajectory_id), 0);
     sensor_bridges_[trajectory_id] = cartographer::common::make_unique<SensorBridge>(
          trajectory_options.num_subdivisions_per_laser_scan,
          trajectory_options.tracking_frame,
          node_options_.lookup_transform_timeout_sec, tf_buffer_,
          map_builder_.GetTrajectoryBuilder(trajectory_id));
     auto emplace_result =  trajectory_options_.emplace(trajectory_id, trajectory_options);
     CHECK(emplace_result.second == true);
     return trajectory_id;
}

  其中map_builder_.AddTrajectoryBuilder(...)是Cartographer專案中的程式碼了。

int MapBuilder::AddTrajectoryBuilder( const std::unordered_set<std::string>& expected_sensor_ids, const proto::TrajectoryBuilderOptions& trajectory_options,
 LocalSlamResultCallback local_slam_result_callback) 
{
    const int trajectory_id = trajectory_builders_.size();//生成trajectory_id
    if (options_.use_trajectory_builder_3d()) 
    {
        CHECK(trajectory_options.has_trajectory_builder_3d_options());
        trajectory_builders_.push_back(common::make_unique<CollatedTrajectoryBuilder>(
            &sensor_collator_, trajectory_id, expected_sensor_ids,
            common::make_unique<mapping::GlobalTrajectoryBuilder<
                mapping_3d::LocalTrajectoryBuilder,
                mapping_3d::proto::LocalTrajectoryBuilderOptions,
                mapping_3d::PoseGraph>>(
                trajectory_options.trajectory_builder_3d_options(),
                trajectory_id, pose_graph_3d_.get(),
                local_slam_result_callback)));//注意此處的push_back()方法
    }
    else
    {
         CHECK(trajectory_options.has_trajectory_builder_2d_options());
         trajectory_builders_.push_back(common::make_unique<CollatedTrajectoryBuilder>(
            &sensor_collator_, trajectory_id, expected_sensor_ids,
            common::make_unique<mapping::GlobalTrajectoryBuilder<
                mapping_2d::LocalTrajectoryBuilder,
                mapping_2d::proto::LocalTrajectoryBuilderOptions,
                mapping_2d::PoseGraph>>(
                trajectory_options.trajectory_builder_2d_options(),
                trajectory_id, pose_graph_2d_.get(),
                local_slam_result_callback)));//注意此處的push_back()方法
    }
    if (trajectory_options.pure_localization()) 
    {
         constexpr int kSubmapsToKeep = 3;
         pose_graph_->AddTrimmer(common::make_unique<PureLocalizationTrimmer>(trajectory_id, kSubmapsToKeep));
    }
    if (trajectory_options.has_initial_trajectory_pose())
    {
        const auto& initial_trajectory_pose = trajectory_options.initial_trajectory_pose();
        pose_graph_->SetInitialTrajectoryPose(trajectory_id, initial_trajectory_pose.to_trajectory_id(),
        transform::ToRigid3(initial_trajectory_pose.relative_pose()), common::FromUniversal(initial_trajectory_pose.timestamp()));
    }
    return trajectory_id;
}

  注意,trajectory_builders_是根據trajectory_id新增的。以後呼叫的時候根據trajectory_id呼叫。

  2D/3D區分:同時可以看到,這裡對2D和3D情況作了區分,根據options_.use_trajectory_builder_3d()確定使用的型別。

  在ROS的主迴圈執行過程中,會不斷處理感測器傳入的資料。

  以IMU資料為例,auto sensor_bridge_ptr = map_builder_bridge_.sensor_bridge(trajectory_id),根據trajectory_id獲取sensor_bridge_ptr。注意這裡因為是訂閱的其它ROS主題(Topic),所以sensor_id引數是從其他主題傳入的。(即當前程式內部有一套主題名稱的字串,訂閱了外部主題也有一套名稱字串表示。這樣兩者通過同樣的名稱字串建立了關係)

void Node::HandleImuMessage(const int trajectory_id, const std::string& sensor_id, const sensor_msgs::Imu::ConstPtr& msg)
{
  carto::common::MutexLocker lock(&mutex_);
  if (!sensor_samplers_.at(trajectory_id).imu_sampler.Pulse()) 
  {
        return;
  }
  auto sensor_bridge_ptr = map_builder_bridge_.sensor_bridge(trajectory_id);
  auto imu_data_ptr = sensor_bridge_ptr->ToImuData(msg);
  if (imu_data_ptr != nullptr) 
  {
        extrapolators_.at(trajectory_id).AddImuData(*imu_data_ptr);
  }
  sensor_bridge_ptr->HandleImuMessage(sensor_id, msg);
}

  最後呼叫了sensor_bridge_ptr->HandleImuMessage(sensor_id, msg);的程式碼。這裡又通過trajectory_builder_呼叫了AddSensorData方法,由於之前做為引數傳入的是CollatedTrajectoryBuilder,所以實際呼叫的是CollatedTrajectoryBuilder的AddSensorData方法。

void SensorBridge::HandleImuMessage(const std::string& sensor_id, const sensor_msgs::Imu::ConstPtr& msg) 
{
     std::unique_ptr<::cartographer::sensor::ImuData> imu_data = ToImuData(msg);
     if (imu_data != nullptr) 
    {
            trajectory_builder_->AddSensorData( sensor_id, cartographer::sensor::ImuData{imu_data->time, imu_data->linear_acceleration, imu_data->angular_velocity});
    }
}

  SensorBridge類實現程式碼,訊息轉換函式檢視msg_conversion.cc檔案:

  1 SensorBridge::SensorBridge(
  2     const int num_subdivisions_per_laser_scan,
  3     const std::string& tracking_frame,
  4     const double lookup_transform_timeout_sec, tf2_ros::Buffer* const tf_buffer,
  5     carto::mapping::TrajectoryBuilderInterface* const trajectory_builder)
  6     : num_subdivisions_per_laser_scan_(num_subdivisions_per_laser_scan),
  7       tf_bridge_(tracking_frame, lookup_transform_timeout_sec, tf_buffer),
  8       trajectory_builder_(trajectory_builder) {}
  9 
 10 std::unique_ptr<::cartographer::sensor::OdometryData>
 11 SensorBridge::ToOdometryData(const nav_msgs::Odometry::ConstPtr& msg) {
 12   const carto::common::Time time = FromRos(msg->header.stamp);
 13   const auto sensor_to_tracking = tf_bridge_.LookupToTracking(
 14       time, CheckNoLeadingSlash(msg->child_frame_id));
 15   if (sensor_to_tracking == nullptr) {
 16     return nullptr;
 17   }
 18   return ::cartographer::common::make_unique<
 19       ::cartographer::sensor::OdometryData>(
 20       ::cartographer::sensor::OdometryData{
 21           time, ToRigid3d(msg->pose.pose) * sensor_to_tracking->inverse()});
 22 }
 23 
 24 void SensorBridge::HandleOdometryMessage(
 25     const std::string& sensor_id, const nav_msgs::Odometry::ConstPtr& msg) {
 26   std::unique_ptr<::cartographer::sensor::OdometryData> odometry_data =
 27       ToOdometryData(msg);
 28   if (odometry_data != nullptr) {
 29     trajectory_builder_->AddSensorData(
 30         sensor_id, cartographer::sensor::OdometryData{odometry_data->time,
 31                                                       odometry_data->pose});
 32   }
 33 }
 34 
 35 std::unique_ptr<::cartographer::sensor::ImuData> SensorBridge::ToImuData(
 36     const sensor_msgs::Imu::ConstPtr& msg) {
 37   CHECK_NE(msg->linear_acceleration_covariance[0], -1)
 38       << "Your IMU data claims to not contain linear acceleration measurements "
 39          "by setting linear_acceleration_covariance[0] to -1. Cartographer "
 40          "requires this data to work. See "
 41          "http://docs.ros.org/api/sensor_msgs/html/msg/Imu.html.";
 42   CHECK_NE(msg->angular_velocity_covariance[0], -1)
 43       << "Your IMU data claims to not contain angular velocity measurements "
 44          "by setting angular_velocity_covariance[0] to -1. Cartographer "
 45          "requires this data to work. See "
 46          "http://docs.ros.org/api/sensor_msgs/html/msg/Imu.html.";
 47 
 48   const carto::common::Time time = FromRos(msg->header.stamp);
 49   const auto sensor_to_tracking = tf_bridge_.LookupToTracking(
 50       time, CheckNoLeadingSlash(msg->header.frame_id));
 51   if (sensor_to_tracking == nullptr) {
 52     return nullptr;
 53   }
 54   CHECK(sensor_to_tracking->translation().norm() < 1e-5)
 55       << "The IMU frame must be colocated with the tracking frame. "
 56          "Transforming linear acceleration into the tracking frame will "
 57          "otherwise be imprecise.";
 58   return ::cartographer::common::make_unique<::cartographer::sensor::ImuData>(
 59       ::cartographer::sensor::ImuData{
 60           time,
 61           sensor_to_tracking->rotation() * ToEigen(msg->linear_acceleration),
 62           sensor_to_tracking->rotation() * ToEigen(msg->angular_velocity)});
 63 }
 64 
 65 void SensorBridge::HandleImuMessage(const std::string& sensor_id,
 66                                     const sensor_msgs::Imu::ConstPtr& msg) {
 67   std::unique_ptr<::cartographer::sensor::ImuData> imu_data = ToImuData(msg);
 68   if (imu_data != nullptr) {
 69     trajectory_builder_->AddSensorData(
 70         sensor_id, cartographer::sensor::ImuData{imu_data->time,
 71                                                  imu_data->linear_acceleration,
 72                                                  imu_data->angular_velocity});
 73   }
 74 }
 75 
 76 void SensorBridge::HandleLaserScanMessage(
 77     const std::string& sensor_id, const sensor_msgs::LaserScan::ConstPtr& msg) {
 78   ::cartographer::sensor::PointCloudWithIntensities point_cloud;
 79   ::cartographer::common::Time time;
 80   std::tie(point_cloud, time) = ToPointCloudWithIntensities(*msg);
 81   HandleLaserScan(sensor_id, time, msg->header.frame_id, point_cloud);
 82 }
 83 
 84 void SensorBridge::HandleMultiEchoLaserScanMessage(
 85     const std::string& sensor_id,
 86     const sensor_msgs::MultiEchoLaserScan::ConstPtr& msg) {
 87   ::cartographer::sensor::PointCloudWithIntensities point_cloud;
 88   ::cartographer::common::Time time;
 89   std::tie(point_cloud, time) = ToPointCloudWithIntensities(*msg);
 90   HandleLaserScan(sensor_id, time, msg->header.frame_id, point_cloud);
 91 }
 92 
 93 void SensorBridge::HandlePointCloud2Message(
 94     const std::string& sensor_id,
 95     const sensor_msgs::PointCloud2::ConstPtr& msg) {
 96   pcl::PointCloud<pcl::PointXYZ> pcl_point_cloud;
 97   pcl::fromROSMsg(*msg, pcl_point_cloud);
 98   carto::sensor::TimedPointCloud point_cloud;
 99   for (const auto& point : pcl_point_cloud) {
100     point_cloud.emplace_back(point.x, point.y, point.z, 0.f);
101   }
102   HandleRangefinder(sensor_id, FromRos(msg->header.stamp), msg->header.frame_id,
103                     point_cloud);
104 }
105 
106 const TfBridge& SensorBridge::tf_bridge() const { return tf_bridge_; }
107 
108 void SensorBridge::HandleLaserScan(
109     const std::string& sensor_id, const carto::common::Time time,
110     const std::string& frame_id,
111     const carto::sensor::PointCloudWithIntensities& points) {
112   CHECK_LE(points.points.back()[3], 0);
113   // TODO(gaschler): Use per-point time instead of subdivisions.
114   for (int i = 0; i != num_subdivisions_per_laser_scan_; ++i) {
115     const size_t start_index =
116         points.points.size() * i / num_subdivisions_per_laser_scan_;
117     const size_t end_index =
118         points.points.size() * (i + 1) / num_subdivisions_per_laser_scan_;
119     carto::sensor::TimedPointCloud subdivision(
120         points.points.begin() + start_index, points.points.begin() + end_index);
121     if (start_index == end_index) {
122       continue;
123     }
124     const double time_to_subdivision_end = subdivision.back()[3];
125     // `subdivision_time` is the end of the measurement so sensor::Collator will
126     // send all other sensor data first.
127     const carto::common::Time subdivision_time =
128         time + carto::common::FromSeconds(time_to_subdivision_end);
129     for (auto& point : subdivision) {
130       point[3] -= time_to_subdivision_end;
131     }
132     CHECK_EQ(subdivision.back()[3], 0);
133     HandleRangefinder(sensor_id, subdivision_time, frame_id, subdivision);
134   }
135 }
136 
137 void SensorBridge::HandleRangefinder(
138     const std::string& sensor_id, const carto::common::Time time,
139     const std::string& frame_id, const carto::sensor::TimedPointCloud& ranges) {
140   const auto sensor_to_tracking =
141       tf_bridge_.LookupToTracking(time, CheckNoLeadingSlash(frame_id));
142   if (sensor_to_tracking != nullptr) {
143     trajectory_builder_->AddSensorData(
144         sensor_id, cartographer::sensor::TimedPointCloudData{
145                        time, sensor_to_tracking->translation().cast<float>(),
146                        carto::sensor::TransformTimedPointCloud(
147                            ranges, sensor_to_tracking->cast<float>())});
148   }
149 }
SensorBridge
 1 ToPointCloudWithIntensities(const sensor_msgs::PointCloud2& message) {
 2   PointCloudWithIntensities point_cloud;
 3   // We check for intensity field here to avoid run-time warnings if we pass in
 4   // a PointCloud2 without intensity.
 5   if (PointCloud2HasField(message, "intensity")) {
 6     pcl::PointCloud<pcl::PointXYZI> pcl_point_cloud;
 7     pcl::fromROSMsg(message, pcl_point_cloud);
 8     for (const auto& point : pcl_point_cloud) {
 9       point_cloud.points.emplace_back(point.x, point.y, point.z, 0.f);
10       point_cloud.intensities.push_back(point.intensity);
11     }
12   } else {
13     pcl::PointCloud<pcl::PointXYZ> pcl_point_cloud;
14     pcl::fromROSMsg(message, pcl_point_cloud);
15 
16     // If we don't have an intensity field, just copy XYZ and fill in
17     // 1.0.
18     for (const auto& point : pcl_point_cloud) {
19       point_cloud.points.emplace_back(point.x, point.y, point.z, 0.f);
20       point_cloud.intensities.push_back(1.0);
21     }
22   }
23   return std::make_tuple(point_cloud, FromRos(message.header.stamp));
24 }
msg_conversion.cc

  檢視CollatedTrajectoryBuilder的AddSensorData方法,在CollatedTrajectoryBuilder的標頭檔案中,包括4個覆寫的AddSensorData(x,x)方法,方法中通過sensor::MakeDispatchable轉換為Dispatchable<DataType>型別。

 void AddSensorData(const std::string& sensor_id, const sensor::TimedPointCloudData& timed_point_cloud_data) override
 {
       AddSensorData(sensor::MakeDispatchable(sensor_id, timed_point_cloud_data));
 }

 void AddSensorData(const std::string& sensor_id,  const sensor::ImuData& imu_data) override
 {
       AddSensorData(sensor::MakeDispatchable(sensor_id, imu_data));
 }

  void AddSensorData(const std::string& sensor_id,  const sensor::OdometryData& odometry_data) override 
{
      AddSensorData(sensor::MakeDispatchable(sensor_id, odometry_data));
}

  void AddSensorData(const std::string& sensor_id, const sensor::FixedFramePoseData& fixed_frame_pose_data) override 
{
     AddSensorData(sensor::MakeDispatchable(sensor_id, fixed_frame_pose_data));
}

  最終定位到了sensor_collator_物件的方法。

void CollatedTrajectoryBuilder::AddSensorData( std::unique_ptr<sensor::Data> data) 
{
      sensor_collator_->AddSensorData(trajectory_id_, std::move(data));
}

  檢視幾個類CollatedTrajectoryBuilder,mapping::GlobalTrajectoryBuilder

CollatedTrajectoryBuilder::CollatedTrajectoryBuilder(sensor::Collator* const sensor_collator, const int trajectory_id,  
           const std::unordered_set<std::string>& expected_sensor_ids, std::unique_ptr<TrajectoryBuilderInterface> wrapped_trajectory_builder)
    : sensor_collator_(sensor_collator),
      trajectory_id_(trajectory_id),
      wrapped_trajectory_builder_(std::move(wrapped_trajectory_builder)),
      last_logging_time_(std::chrono::steady_clock::now())
{
      sensor_collator_->AddTrajectory(trajectory_id, expected_sensor_ids, [this](const std::string& sensor_id, std::unique_ptr<sensor::Data> data) 
     {HandleCollatedSensorData(sensor_id, std::move(data));});
}

  mapping::GlobalTrajectoryBuilder建構函式

GlobalTrajectoryBuilder(const LocalTrajectoryBuilderOptions& options, const int trajectory_id, 
PoseGraph* const pose_graph, const LocalSlamResultCallback& local_slam_result_callback)
      : trajectory_id_(trajectory_id), pose_graph_(pose_graph),
        local_trajectory_builder_(options), local_slam_result_callback_(local_slam_result_callback) 
{}

  注意這裡的繼承關係:

    class CollatedTrajectoryBuilder : public TrajectoryBuilderInterface

    class GlobalTrajectoryBuilder : public mapping::TrajectoryBuilderInterface

  在mapping_2d和mapping_3d兩個名稱空間下分別存在2個local_trajectory_builder_類,實現了局部的掃描匹配和子圖構建。程式碼在cartographer\cartographer\internal資料夾下。

  另外一個重要的Node類變數是extrapolators_,該物件在Node類的處理Odometry和IMU資料時都有用到,作用是位姿推算。在文一種Node::AddTrajectory方法中呼叫了AddExtrapolator(trajectory_id, options);

1 std::map<int, ::cartographer::mapping::PoseExtrapolator> extrapolators_;
void Node::AddExtrapolator(const int trajectory_id, const TrajectoryOptions& options)
{
  constexpr double kExtrapolationEstimationTimeSec = 0.001;  // 1 ms
  CHECK(extrapolators_.count(trajectory_id) == 0);
  const double gravity_time_constant =
      node_options_.map_builder_options.use_trajectory_builder_3d()
          ? options.trajectory_builder_options.trajectory_builder_3d_options()
                .imu_gravity_time_constant()
          : options.trajectory_builder_options.trajectory_builder_2d_options()
                .imu_gravity_time_constant();
  extrapolators_.emplace(
      std::piecewise_construct, std::forward_as_tuple(trajectory_id),
      std::forward_as_tuple(
          ::cartographer::common::FromSeconds(kExtrapolationEstimationTimeSec),
          gravity_time_constant));
}

相關推薦

Cartographer原始碼閱讀(2)NodeMapBuilder物件

  上文提到特別注意map_builder_bridge_.AddTrajectory(x,x),檢視其中的程式碼。兩點:   首先是map_builder_.AddTrajectoryBuilder(...),呼叫了map_builder_物件的方法。其次是sensor_bridges_鍵值對的賦值。

Cartographer原始碼閱讀(4)NodeMapBuilder物件2

  MapBuilder的成員變數sensor::Collator sensor_collator_;   再次閱讀MapBuilder::AddTrajectoryBuilder方法。首先構造了mapping::GlobalTrajectoryBuilder例項,接著作為引數構造了CollatedTraj

Cartographer原始碼閱讀(6)LocalTrajectoryBuilderPoseExtrapolator

LocalTrajectoryBuilder意思是區域性軌跡的構建,下面的類圖中方法的引數沒有畫進去。 注意其中的三個類:PoseExtrapolator類,RealTimeCorrelativeScanMatcher類和CeresScanMatcher類。 (1)PoseExtrapolator類(

Cartographer原始碼閱讀(7)軌跡推算位姿推算的原理

其實也就是包括兩個方面的內容:類似於運動模型的位姿估計和掃描匹配,因為需要計算速度,所以時間就有必要了! 1. PoseExtrapolator解決了IMU資料、里程計和位姿資訊進行融合的問題。 該類定義了三個佇列。 1 std::deque<TimedPose> timed_pose_

Cartographer原始碼閱讀(8)imu_tracker

1 /* 2 * Copyright 2016 The Cartographer Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use

Cartographer原始碼閱讀(1)程式入口

1 [email protected]:~$ sudo apt-get install kdevelop 2 [sudo] password for yhexie: 3 Reading package lists... Done 4 Building dependency

Cartographer原始碼閱讀(5)PoseGraph位姿圖

PoseGraph位姿圖 mapping2D::PoseGraph類的註釋: // Implements the loop closure method called Sparse Pose Adjustment (SPA) from// Konolige, Kurt, et al. "E

Cartographer原始碼閱讀(3)程式邏輯結構

Cartographer早期的程式碼在進行3d製圖的時候使用了UKF方法,檢視現有的tag版本,可以轉到0.1.0和0.2.0檢視,包含kalman_filter資料夾。 資料夾中的pose_tracker類檔案在mapping_3d的檔案加下有kalman_local_trajectory_builder

Ethzasl MSF原始碼閱讀(2)百川匯海

這裡有個感覺,就是百川匯海。即IMU資料和相機的訊息資料都彙集到msf_core進行處理。接上一篇, 1. 檢視IMUHandler_ROS::IMUCallback和IMUHandler_ROS::StateCallback回撥函式。  MUHandler_ROS::IMUCallback,傳入的訊息s

Cartographer原始碼閱讀(9)圖優化的前端——閉環檢測

約束計算 閉環檢測的策略:搜尋閉環,通過匹配檢測是否是閉環,採用了分支定界法。 前已經述及PoseGraph的內容,此處繼續。位姿圖類定義了pose_graph::ConstraintBuilder constraint_builder_物件。 1.ConstraintBuilder類圖 定義了S

Ethzasl MSF原始碼閱讀(3)MSF_CorePoseMeasurement

1.MSF_Core的三個函式:ProcessIMU、ProcessExternallyPropagatedState和AddMeasurement MSF_Core維護了狀態佇列和觀測值佇列,這裡需要結合論文思考這個狀態佇列的作用。 ProcessIMU方法: 1 template<

Caffe原始碼理解2SyncedMemory CPUGPU間的資料同步

目錄 寫在前面 成員變數的含義及作用 構造與析構 記憶體同步管理 參考 部落格:blog.shinelee.me | 部落格園 | CSDN 寫在前面 在Caffe原始碼理解1中介紹了Blob類,其中的資料成員有 shared_ptr<SyncedMemory>

Ethzasl MSF原始碼閱讀(1)程式入口主題訂閱

Ethz的Stephen Weiss的工作,是一個IMU鬆耦合的方法。 1.程式入口:ethzasl_msf\msf_updates\src\pose_msf\main.cpp 1 #include "pose_sensormanager.h" 2 3 int main(int argc,

2BitmapBase64轉換

jpg itl print stat nbsp res param ima ati import java.io.ByteArrayOutputStream; import java.io.IOException; import android.annotation.S

第六周web作業2stickyfixed

指定 相同 基於用戶 之一 行為 作業2 sticky ive posit sticky基於用戶的滾動位置來定位。 粘性定位的元素是依賴於用戶的滾動,在 position:relative 與 position:fixed 定位之間切換。 在目標區域以內,它的行為就像 po

Qt學習筆記2QMainWindowQWidget的區別

在Qt工程建立時,Qt Widget Application模板下提供了不同的父類繼承。 這裡,主要分析一下QMainWindow和QWidget的區別。 建立工程 分別以QMainWindow和QWidget為基類建立工程,工程建立完成後,如下圖所示: Qt會自動建

C#學習篇2過載重寫 虛方法抽象方法

本篇問轉載文章,僅供學習使用。。。 過載(overload): 在同一個作用域(一般指一個類)的兩個或多個方法函式名相同,引數列表不同的方法叫做過載,它們有三個特點(俗稱兩必須一可以): 方法名必須相同 引數列表必須不相同 返回值型別可以不相同 例如: publ

springmvc原始碼閱讀2--dispatcherServlet及談如何找原始碼入口

一、先找到入口: 1、先說找發: 根據配置檔案找 這個是最常見的搞法,在原始階段大多數都會使用一些配置檔案來啟動這些框架,但是隨著springboot類似的搞法的流行,這個技巧有點不在那麼起作用,其實原理還是這個。 依據j2ee的規範來找: 首先我們要搞明白兩個規範(也

筆記2.2列表元組

列表 1,列表:用中括號包括起來的,內部可以放任何資料,且可以用索引來取值的資料。 注意:索引從0開始。列表也能切片操作:而且還代表方向。 2,列表迴圈取出所有值: #coding=utf-8 #用for迴圈取值 namesList = ['xiaoWang','xiaoZhang

Caffe原始碼解析2SycedMem

看到SyncedMem就知道,這是在做記憶體同步的操作。這類個類的程式碼比較少,但是作用是非常明顯的。檔案對應著syncedmem.hpp,著syncedmem.cpp 首先是兩個全域性的行內函數。如果機器是支援GPU的並且安裝了cuda,通過cudaMallocHost分配的host memory將會被p