Welcome to SLAM_study ‘s documentation!

Contents:

SLAM 过去,现在与未来

  1. 基本定义
  2. 核心问题
  3. 难点

开发平台

Jetson-TX2

Jeton-TX2 的开发包,包含了 tensorRT,cuda,cudnn,opencv,以及visionworks 以及Multimedia API等等。 以及相关的开发工具,CPU的proiler, NSight Eclipse, Tegra Graphic profiler 等等。并且本身是全版的ubuntu 16.04. 特别适合做二次开发。

如何从离线安装

  1. 把上次安装目目录下的 /Jetpack/jetpack_download 保存一下

  2. 再一次安装的指定 repo

    comp_repo_path=file:///home/ubuntu/Jetpack/jetpack_download/jetpack.json ./JetPack-L4T-XX.run
    
    #slient update
    Launcher_slient_mode=install  development_platform=jetson-tx2  ./JetPack-L4T-XXX.run
    

JetPack 的目录结构

  JetPack tree -L 3
.
├── 64_TX2
│   ├── cuda-l4t
│   │   └── cuda-l4t.sh
│   ├── cudnn
│   │   ├── libcudnn6_6.0.21-1+cuda8.0_arm64.deb
│   │   ├── libcudnn6-dev_6.0.21-1+cuda8.0_arm64.deb
│   │   └── libcudnn6-doc_6.0.21-1+cuda8.0_arm64.deb
│   ├── Linux_for_Tegra_tx2
│   │   ├── apply_binaries.sh
│   │   ├── bootloader
│   │   ├── cmd.sh
│   │   ├── flash.sh
│   │   ├── jetson-tx2.conf -> p2771-0000-devkit.conf
│   │   ├── kernel
│   │   ├── new_dir
│   │   ├── nv_tegra
│   │   ├── p2771-0000.conf.common
│   │   ├── p2771-0000-devkit.conf
│   │   ├── p2771-0000-dsi-hdmi-dp.conf
│   │   ├── rootfs
│   │   ├── source_sync.sh
│   │   └── TX2_flash_package.tgz
│   └── tegra_multimedia_api
│       ├── argus
│       ├── data
│       ├── include
│       ├── LEGAL
│       ├── LICENSE
│       ├── Makefile
│       ├── README
│       ├── samples
│       └── tools
├── _installer
│   ├── Chooser
│   ├── configure_host
│   ├── cuda-l4t.sh
│   ├── exit.code
│   ├── flash_os
│   ├── InstallUtil
│   ├── JetPack.log
│   ├── Launcher
│   ├── Launcher.log
│   ├── local.cfg
│   ├── local.db
│   ├── local.ini
│   ├── logs
│   │   └── 64_TX2
│   ├── nv_info_broker
│   ├── ocv.sh
│   ├── PageAction
│   ├── Poller
│   ├── rc.local
│   ├── remove_unsupported_cuda_samples.sh
│   ├── report_ip_to_host.sh
│   ├── run_command
│   ├── run_gameworks_sample.sh
│   ├── selected_comps.txt
│   ├── start_up.sh
│   ├── sudo_daemon
│   └── tmp
│       ├── jetpackfifo
│       ├── jetpackfifo_req
│       └── log
├── jetpack_download
│   ├── 2017_06_14_0412-22311683-NVIDIA_Tegra_Graphics_Debugger_2.4.17165.0412_Release_External_tgd-l4t_linux-l4t.run
│   ├── cuda-repo-l4t-8-0-local_8.0.84-1_arm64.deb
│   ├── cuda-repo-ubuntu1404-8-0-local_8.0.84-1_amd64.deb
│   ├── host-x64-linux-public-3.8.450-a527a18.tar.gz
│   ├── jetpack_docs.zip
│   ├── libcudnn6_6.0.21-1+cuda8.0_arm64.deb
│   ├── libcudnn6-dev_6.0.21-1+cuda8.0_arm64.deb
│   ├── libcudnn6-doc_6.0.21-1+cuda8.0_arm64.deb
│   ├── libopencv4tegra-repo_2.4.13-17-g5317135_amd64_ubuntu-14.04.deb
│   ├── libopencv4tegra-repo_2.4.13-17-g5317135_arm64_l4t-r26.deb
│   ├── libvisionworks-repo_1.6.0.193n_amd64_ubuntu-14.04.deb
│   ├── libvisionworks-repo_1.6.0.233n_arm64_l4t-r26.deb
│   ├── libvisionworks-sfm-repo_0.90.1_amd64_ubuntu-14.04.deb
│   ├── libvisionworks-sfm-repo_0.90.1_arm64_l4t-r26.deb
│   ├── libvisionworks-tracking-repo_0.88.0_amd64_ubuntu-14.04.deb
│   ├── libvisionworks-tracking-repo_0.88.0_arm64_l4t-r26.deb
│   ├── nv-gie-repo-ubuntu1604-ga-cuda8.0-trt2.1-20170614_1-1_arm64.deb
│   ├── NVIDIA_Tegra_Linux_Driver_Package.tar
│   ├── NVIDIA_VisionWorks_1.6_Docs.zip
│   ├── Tegra186_Linux_R28.1.0_aarch64.tbz2
│   ├── Tegra_Linux_Driver_Package_Release_Notes_R28.1.pdf
│   ├── Tegra_Linux_Sample-Root-Filesystem_R28.1.0_aarch64.tbz2
│   └── Tegra_Multimedia_API_R28.1.0_aarch64.tbz2
├── JetPack-L4T-3.1-linux-x64.run
├── JetPack_Uninstaller
├── manifest.json
├── repository.json
├── tmp
│   ├── com.nvidia.cuda.host_tx2_cuda.png
│   ├── com.nvidia.l4t.driver4os_64_tx2_nvidia.png
│   ├── com.nvidia.l4t.flash_64_tx2_nvidia.png
│   └── com.nvidia.nvtools.battle_tegra-graphics-debugger.png
└── update.lock

21 directories, 76 files
➜  JetPack 

如何快速定制target的刷机Image

  1. 从官网下载最新的 JetPack 最新版本为3.1.

  2. 选择相关的包进行安装并刷机。

    Note

    进入recover 模式, 组合键顺序: Rec -> Rec+Reset -> Rec

  3. 在device 上安装各种额外的包。你可以用VNC 或者ssh 去连接device.

    sudo apt-get intall -y git cmake clang
    
  4. 备份整个device的rootfs.

    sudo tar -cvpz --one-file-system / | ssh <yourlocalhost> "(cat >ssh_jetson_tx2_rootfs.tgz)"
    
  5. 在host上解压到jetpack的解压目录

    sudo tar -xvpzf /path/to/ssh_jetson_tx2_rootfs.tar.gz -C <rootfs folder in host> --numeric-owner"
    
  6. 重新刷机生成一个新的刷机包

    cd <Jetpackppath>/64_TX2/Linux_for_Tegra_tx2
    ./flash_os jetson-tx2 mmcblk0p1
    

    Note

    刷机前看一下device是否在 recovery mode 用命令 lsusb | grep "nvidia" 来查看

  7. 等刷机完成后,在 Linux_for_Tegra_tx2/bootloader 下就生成了新的刷机包了。并且以后刷机直接用 cd Linux_for_Tegra_tx2/bootloader && sh bootloader 。基本十几分钟就可以刷好了。

如何手工安装

  1. Install CUDA

    sudo dpkg -i cuda-repo-ubuntu1604-8-0-local-r381_8.0.84-1_amd64.deb
    sudo apt-get
    sudo apt-get install -y --allow-unauthenticated update
    sudo apt-get install -y cuda
    sudo dpkg --add-architecture arm64
    sudo apt-get --allow-unauthenticated update
    sudo apt-get install -y --allow-unauthenticated cuda-cross-aarch64
    
  2. libopencv4tegra-dev

    sudo apt-get install -y cuda-cross-aarch64
    
  3. cuda toolkit cross compiler

    sudo dpkg --add-architecture arm64
    sudo dpkg -i xxx.deb
    sudo apt-get update
    sudo apt install cuda-cross-aarch64
    
  4. Import a CUDA sample to Nsight Eclipse and set arch as aarch64

参考

SLAM on Jetson TX2

视频分析

digraph G {
    rankdir=LR;
    node [shape=box];
    Camera ->"Jetson TX2"-> "V4L2Engines"->CUDA->TensorRT->GL;
    "Decodes from video" ->"run deep learning"->"do post processingwithCUDAandGraphics";
}
  1. Capture video
  2. Recognition objects
  3. Take action in real-time
  4. Visualize

TX2 的基本库

计算有CUDA,图形 GL,图像OPENCV,以及计算机视觉有Visionworks。DL可以用tensorrt来推理。

标准流可以用vision works,非标准流可以用 tegra-multimedia api.

基本的视频分析

Tegra_multimedia api sample

Manual : <L4T Multimedia API Reference> on https://developer.nvidia.com/embedded/downloads

库的组成

  1. V4L2 API 用于各种视频的编解码与scaling等等。
  2. libargus 用于图像处理,能直接处理lower level Camera信息。 具体流程可以查看http://on-demand.gputechconf.com/gtc/2016/webinar/getting-started-jetpack-camera-api.pdf
  3. Buffer utilis ,buffer 的内存管理
  4. NVDC-DRM 可以对于非 X11的轻量的级的显示管理系统,特别是适合一些嵌入的系统
  5. NVOSD on-Screen display.
backend

H.264

_images/backend.png

from API reference manual

$ ./backend 1 ../../data/Video/sample_outdoor_car_1080p_10fps.h264 H264 \
   --trt-deployfile ../../data/Model/GoogleNet_one_class/GoogleNet_modified_oneClass_halfHD.prototxt \
   --trt-modelfile ../../data/Model/GoogleNet_one_class/GoogleNet_modified_oneClass_halfHD.caffemodel \
   --trt-forcefp32 0 --trt-proc-interval 1 -fps 10

SLAM 基础

  1. 数学基础
  2. 计算机视觉

SLAM 算法分析

SLAM 14讲

目标

  1. 用Nsight Tegra 来分析每一个例子瓶颈
  2. 用LLVM 生成callgraph, 如何查看其编译的参数
  3. 找出各种优化方法
  4. 并且写出验证代码
  5. 写出自动优化的框架
  6. 生成各种report
  7. 实现debug过程的可视化,可以通过扩展gdb,以及底层的api为一些动态的数据。

ch3 visualizeGeometry

_images/ch2_visualizeGeomtry.png

用了一个 Pangolin的GUI库,

ch4 使用李群与李代数

由旋转矩阵是李群,然后通过李代数的映射来计算旋转矩阵。相似变换也是李群。

_images/ch4_useSophus.png

useSophus 的profiling分析

  1. 由于helloworld级的代码,大部分CPU时间都花在了ld-2.23.0 再查其callstack大部分时间花在了 dl_lookup_symbol_x 这个函数。 dl_lookup_symbols_x 是glib C runtime 库,用来查动态链接库中的符号查询。 解决见 stackowerflow
  2. 真正profiling算法本身的性能时,可能就要代码中加入一些mark标记,这样才能准确的profiling. 或者在profiling时加入一定等待时间。
_images/ch4_useSophus_timeline.png

useSophus 的timeline

  1. 看起来基本是单线程也没有跑满。

CH5相机与图像

_images/ch5_imageBasics.png

imageBasics

_images/ch5_imageBasics_timeline.png

imageBasics timeline

从上面的图中可以看到,一个单线程的进程,却发生了几次 thread context的切换。并且每一次的 切换也都耗费了两分钟。

当不是很规范的时候,例如一个大函数搞定一切,如果想进一步了解性能原因,那就要用到 nvtx来标记了,或者支持源码级的优化,也就是每一个行指令的执行的次数,或者利用 颜色标记出来,例如颜色越深的地方,也就是执行次数最多的地方。当然就需要instruction 级别的profiling了。

同时利用脚本来执行这些profiling最后集中查看会更加方便。 甚至一个整套的profiling测试 修改源码,编译,执行profiling测试,并且根据profiling的数据生成测试分析报告图表。

同时在一个机器编译的binary,如何快速复制其他device上也能跑呢,并且其又依赖大量的库。 一个个机器去装这些依赖库也挺麻烦的。 一个快速的方法那就是 直接用ldd 把 每一个binary 的依赖的 so 打包。然后再加上LD_PRELOAD来加载,这样就可以加快部署的进程。 例外一种还需要多线程的操作,可能需要同时操作两条线。例如

  1. 一个线程要操作 binary本身
  2. 另一个线程要操作 profiling等等。

位姿记录的形式是平移向量旋转四元数: \([x,y,z,q_x,q_y,q_z,q_w]\)

而生成地图的格式 PCL pcd file format

ch6 非线优化

Ceres

用法用核心就是 定义loss函数。 然后用把数据扔给solver就行了。

看一个hello world就明白了。

\[Loss = min \sum | y^1 -f(x)|^2\]
struct CostFunctor {
template <typename T>
bool operator()(const T* const x, T* residual) const {
  residual[0] = T(10.0) - x[0];
  return true;
}
};

int main(int argc, char** argv) {
  google::InitGoogleLogging(argv[0]);

  // The variable to solve for with its initial value.
  double initial_x = 5.0;
  double x = initial_x;

  // Build the problem.
  Problem problem;

  // Set up the only cost function (also known as residual). This uses
  // auto-differentiation to obtain the derivative (jacobian).
  CostFunction* cost_function =
      new AutoDiffCostFunction<CostFunctor, 1, 1>(new CostFunctor);
  problem.AddResidualBlock(cost_function, NULL, &x);

  // Run the solver!
  Solver::Options options;
  options.linear_solver_type = ceres::DENSE_QR;
  options.minimizer_progress_to_stdout = true;
  Solver::Summary summary;
  Solve(options, &problem, &summary);

  std::cout << summary.BriefReport() << "\n";
  std::cout << "x : " << initial_x
            << " -> " << x << "\n";
  return 0;
}
g2o

图优化的目标就是把用优化问题变成图优。

优化问题 \(\min\limits_{x} F(x)\) 三个基本因素:

  1. 目标函数
  2. 优化变量
  3. 优化约束

最基本的图优化就是用图模型来表达一个非线性最小二乘的优化问题。

图优化的原理 在图中,以顶点表示优化变量,以边表示观测方程或者边为误差项。 我们目标最短路径 或者全体权值最小。

在图中,我们去掉孤立顶点或化先优化边数较多的顶点。

\[\min\limits_{x} \sum\limits_{k = 1}^n {{e_k}{{\left( {{x_k},{z_k}} \right)}^T}{\Omega _k}{e_k}\left( {{x_k},{z_k}} \right)}\]

与ceres 类似,这个是一个通用优化框架,你需要继承或定义问题本身的基本模型就可以了。 例如g2o就是要定义顶点类与边类的如何更新与计算。 把一堆的顶点与边扔进去。

ch7 VO

feature_exection

特征点的提取与描述子两部分组成。 特征点的提取计算与以及有准备性。

_images/ch7_feature_extraction.png _images/ch7_feature_extraction_ORB.png _images/ch7_feature_extraction_Analysis_summary.png _images/ch7_feature_extraction_timeline.png
header:“method”
comments
   
SIFT  
FAST  
ORB Oriented FAST and Rotateed BRIEF

1000 个字, ORB 15.3ms, SURF,217.3ms, SIFT 5228.7ms.

3D to 3D 的位置估计

也就是从自己观测的3D点,来计算出自身的运动方程

_images/ch7_pose_estimation_3d3d.png

这个基本上都还是单线程。耗时比较除了do_lookup_x之外,那就是cv::FAST函数了。

ch8 V0

LKFlow
_images/ch8_LK_Analysis.png _images/ch8_LK_timeline.png _images/ch8_LK.png
DirectSemiDense and DirectSparse

Buserror wait for debug,这个是由 Trace/breakpoint Trap signal引起的,直接gdb时,gdb也直接发挂那。

Using host libthread_db library "/lib/aarch64-linux-gnu/libthread_db.so.1".
*********** loop 0 ************
[New Thread 0x7fa7430db0 (LWP 9961)]
[New Thread 0x7fa6c30db0 (LWP 9962)]
[New Thread 0x7fa6430db0 (LWP 9963)]
[New Thread 0x7fa5c30db0 (LWP 9964)]
[New Thread 0x7fa5430db0 (LWP 9965)]
[New Thread 0x7fa4c30db0 (LWP 9966)]
[New Thread 0x7f8fffedb0 (LWP 9967)]
[New Thread 0x7f8f7fedb0 (LWP 9968)]
add total 12556 measurements.
*********** loop 1 ************
edges in graph: 12556
iteration= 0  chi2= 72633591.401419   time= 0.735174  cumTime= 0.735174       edges= 12556    schur= 0        lambda= 12900954.939934         levenbergIter= 1
iteration= 1  chi2= 62926900.131419   time= 0.739193  cumTime= 1.47437        edges= 12556    schur= 0        lambda= 4300318.313311  levenbergIter= 1
iteration= 2  chi2= 52845484.671121   time= 0.736138  cumTime= 2.2105         edges= 12556    schur= 0        lambda= 1433439.437770  levenbergIter= 1
iteration= 3  chi2= 45449292.558261   time= 0.733896  cumTime= 2.9444         edges= 12556    schur= 0        lambda= 477813.145923   levenbergIter= 1
iteration= 4  chi2= 38422805.114205   time= 0.744241  cumTime= 3.68864        edges= 12556    schur= 0        lambda= 159271.048641   levenbergIter= 1
iteration= 5  chi2= 31970398.890953   time= 0.74077   cumTime= 4.42941        edges= 12556    schur= 0        lambda= 53090.349547    levenbergIter= 1
iteration= 6  chi2= 24270565.530351   time= 0.732403  cumTime= 5.16182        edges= 12556    schur= 0        lambda= 17696.783182    levenbergIter= 1
iteration= 7  chi2= 12153446.174612   time= 0.73939   cumTime= 5.9012         edges= 12556    schur= 0        lambda= 5898.927727     levenbergIter= 1
iteration= 8  chi2= 5615434.148147    time= 0.74188   cumTime= 6.64308        edges= 12556    schur= 0        lambda= 1966.309242     levenbergIter= 1
iteration= 9  chi2= 4849512.586059    time= 0.733603  cumTime= 7.37669        edges= 12556    schur= 0        lambda= 655.436414      levenbergIter= 1
iteration= 10         chi2= 4785381.917957    time= 0.733941  cumTime= 8.11063        edges= 12556    schur= 0        lambda= 218.478805      levenbergIter= 1
iteration= 11         chi2= 4785319.436062    time= 0.740044  cumTime= 8.85067        edges= 12556    schur= 0        lambda= 145.652536      levenbergIter= 1
iteration= 12         chi2= 4785319.043803    time= 1.63759   cumTime= 10.4883        edges= 12556    schur= 0        lambda= 1708231013067781.250000         levenbergIter= 10

The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /srv/slambook/ch8/directMethod/build/direct_semidense data
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/aarch64-linux-gnu/libthread_db.so.1".
*********** loop 0 ************
[New Thread 0x7fa7430db0 (LWP 5767)]
[New Thread 0x7fa6c30db0 (LWP 5768)]
[New Thread 0x7fa6430db0 (LWP 5769)]
[New Thread 0x7fa5c30db0 (LWP 5770)]
[New Thread 0x7fa5430db0 (LWP 5771)]
[New Thread 0x7fa4c30db0 (LWP 5772)]
[New Thread 0x7f8fffedb0 (LWP 5773)]
[New Thread 0x7f8f7fedb0 (LWP 5774)]
add total 12556 measurements.
*********** loop 1 ************
edges in graph: 12556

Thread 1 "direct_semidens" hit Breakpoint 2, poseEstimationDirect (measurements=std::vector of length 12556, capacity 16384 = {...}, gray=0x7fffffeef8, K=..., Tcw=...)
    at /srv/slambook/ch8/directMethod/direct_semidense.cpp:294
294      optimizer.optimize ( 30 );
(gdb) b g2o::SparseOptimizer::optimize
/build/gdb-qLNsm9/gdb-7.11.1/gdb/aarch64-tdep.c:334: internal-error: aarch64_analyze_prologue: Assertion `inst.operands[0].type == AARCH64_OPND_Rt' failed.
A problem internal to GDB has been detected,
further debugging may prove unreliable.
Quit this debugging session? (y or n)
Please answer y or n.
/build/gdb-qLNsm9/gdb-7.11.1/gdb/

进一步调试发现gdb也有问题,这时候基本挂在 return result上。

operator Isometry3D() const                                                                                                                                                             ¦
   ¦288           {                                                                                                                                                                                       ¦
   ¦289             Isometry3D result = (Isometry3D) rotation();                                                                                                                                          ¦
   ¦290             result.translation() = translation();                                                                                                                                                 ¦
  >¦291             return result;                                           k::now();                                                                                                                    ¦
   ¦292           }
0x537838 <g2o::SE3Quat::operator Eigen::Transform<double, 3, 1, 0>() const+92>  str    x0, [sp]
x0             0x7fffffdf18     549755805464
x29            0x7fffffdf40     549755805504
sp             0x7fffffdef0     0x7fffffdef0

同时进一步发现g2o 的优化时 lamda 异常提前退出

[New Thread 0x7f8fffedb0 (LWP 5718)]
[New Thread 0x7f8f7fedb0 (LWP 5719)]
add total 12556 measurements.
*********** loop 1 ************
edges in graph: 12556

Thread 1 "direct_semidens" hit Breakpoint 2, poseEstimationDirect (measurements=std::vector of length 12556, capacity 16384 = {...}, gray=0x7fffffeef8, K=..., Tcw=...)
    at /srv/slambook/ch8/directMethod/direct_semidense.cpp:294
294      optimizer.optimize ( 30 );
(gdb) s optimizer.optimize(10)
Couldn't find method g2o::SparseOptimizer::optimize

/usr/local/include/g2o/core/base_unary_edge.h, /usr/local/include/g2o/core/base_edge.h, /usr/include/c++/5/bits/stl_set.h, /usr/include/c++/5/bits/ptr_traits.h,
/usr/local/include/g2o/core/sparse_block_matrix_diagonal.h, /usr/local/include/g2o/core/block_solver.h, /usr/include/c++/5/bits/unordered_map.h, /usr/include/c++/5/bits/hashtable.h,
/usr/include/c++/5/utility, /usr/include/c++/5/bits/functional_hash.h, /usr/include/c++/5/bits/hashtable_policy.h, /usr/local/include/g2o/core/sparse_block_matrix_ccs.h,
/usr/include/c++/5/bits/stl_map.h, /usr/include/c++/5/bits/stl_function.h, /usr/include/c++/5/ext/aligned_buffer.h, /usr/local/include/g2o/core/sparse_block_matrix.h,
/usr/local/include/g2o/core/linear_solver.h, /usr/include/c++/5/chrono, /usr/include/c++/5/bits/stl_iterator_base_types.h, /usr/include/c++/5/bits/stl_iterator.h, /usr/include/c++/5/bits/stl_algo.h,
---Type <return> to continue, or q <return> to quit---q
/usrQuit
(gdb) b g2o::SparseOptimizer::optimize
/build/gdb-qLNsm9/gdb-7.11.1/gdb/aarch64-tdep.c:334: internal-error: aarch64_analyze_prologue: Assertion `inst.operands[0].type == AARCH64_OPND_Rt' failed.
A problem internal to GDB has been detected,
further debugging may prove unreliable.
Quit this debugging session? (y or n) y


//gdb source code
/* Analyze a prologue, looking for a recognizable stack frame
   and frame pointer.  Scan until we encounter a store that could
   clobber the stack frame unexpectedly, or an unknown instruction.  */

static CORE_ADDR
aarch64_analyze_prologue (struct gdbarch *gdbarch,
                       CORE_ADDR start, CORE_ADDR limit,
                       struct aarch64_prologue_cache *cache)
{
  enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
  int i;
  pv_t regs[AARCH64_X_REGISTER_COUNT];
  struct pv_area *stack;
  struct cleanup *back_to;

  for (i = 0; i < AARCH64_X_REGISTER_COUNT; i++)
    regs[i] = pv_register (i, 0);
  stack = make_pv_area (AARCH64_SP_REGNUM, gdbarch_addr_bit (gdbarch));
  back_to = make_cleanup_free_pv_area (stack);

  for (; start < limit; start += 4)
    {
      uint32_t insn;
      aarch64_inst inst;

      insn = read_memory_unsigned_integer (start, 4, byte_order_for_code);

      if (aarch64_decode_insn (insn, &inst, 1) != 0)
     break;

      if (inst.opcode->iclass == addsub_imm
       && (inst.opcode->op == OP_ADD
           || strcmp ("sub", inst.opcode->name) == 0))
     {
       unsigned rd = inst.operands[0].reg.regno;
       unsigned rn = inst.operands[1].reg.regno;

       gdb_assert (aarch64_num_of_operands (inst.opcode) == 3);
       gdb_assert (inst.operands[0].type == AARCH64_OPND_Rd_SP);
       gdb_assert (inst.operands[1].type == AARCH64_OPND_Rn_SP);
       gdb_assert (inst.operands[2].type == AARCH64_OPND_AIMM);

       if (inst.opcode->op == OP_ADD)
         {
           regs[rd] = pv_add_constant (regs[rn],
                                       inst.operands[2].imm.value);
         }
       else
         {
           regs[rd] = pv_add_constant (regs[rn],
                                       -inst.operands[2].imm.value);
         }
     }
      else if (inst.opcode->iclass == pcreladdr
            && inst.operands[1].type == AARCH64_OPND_ADDR_ADRP)
     {
       gdb_assert (aarch64_num_of_operands (inst.opcode) == 2);
       gdb_assert (inst.operands[0].type == AARCH64_OPND_Rd);

       regs[inst.operands[0].reg.regno] = pv_unknown ();
     }
      else if (inst.opcode->iclass == branch_imm)
     {
       /* Stop analysis on branch.  */
       break;
     }
      else if (inst.opcode->iclass == condbranch)
     {
       /* Stop analysis on branch.  */
       break;
     }
      else if (inst.opcode->iclass == branch_reg)
     {
       /* Stop analysis on branch.  */
       break;
     }
      else if (inst.opcode->iclass == compbranch)
     {
       /* Stop analysis on branch.  */
       break;
     }
      else if (inst.opcode->op == OP_MOVZ)
     {
       gdb_assert (inst.operands[0].type == AARCH64_OPND_Rd);
       regs[inst.operands[0].reg.regno] = pv_unknown ();
     }
      else if (inst.opcode->iclass == log_shift
            && strcmp (inst.opcode->name, "orr") == 0)
     {
       unsigned rd = inst.operands[0].reg.regno;
       unsigned rn = inst.operands[1].reg.regno;
       unsigned rm = inst.operands[2].reg.regno;

       gdb_assert (inst.operands[0].type == AARCH64_OPND_Rd);
       gdb_assert (inst.operands[1].type == AARCH64_OPND_Rn);
       gdb_assert (inst.operands[2].type == AARCH64_OPND_Rm_SFT);

       if (inst.operands[2].shifter.amount == 0
           && rn == AARCH64_SP_REGNUM)
         regs[rd] = regs[rm];
       else
         {
           if (aarch64_debug)
             {
               debug_printf ("aarch64: prologue analysis gave up "
                             "addr=0x%s opcode=0x%x (orr x register)\n",
                             core_addr_to_string_nz (start), insn);
             }
           break;
         }
     }
      else if (inst.opcode->op == OP_STUR)
     {
       unsigned rt = inst.operands[0].reg.regno;
       unsigned rn = inst.operands[1].addr.base_regno;
       int is64
         = (aarch64_get_qualifier_esize (inst.operands[0].qualifier) == 8);

       gdb_assert (aarch64_num_of_operands (inst.opcode) == 2);
       gdb_assert (inst.operands[0].type == AARCH64_OPND_Rt);
       gdb_assert (inst.operands[1].type == AARCH64_OPND_ADDR_SIMM9);
       gdb_assert (!inst.operands[1].addr.offset.is_reg);

       pv_area_store (stack, pv_add_constant (regs[rn],
                                              inst.operands[1].addr.offset.imm),
                      is64 ? 8 : 4, regs[rt]);
     }
      else if ((inst.opcode->iclass == ldstpair_off
             || inst.opcode->iclass == ldstpair_indexed)
            && inst.operands[2].addr.preind
            && strcmp ("stp", inst.opcode->name) == 0)
     {
       unsigned rt1 = inst.operands[0].reg.regno;
       unsigned rt2 = inst.operands[1].reg.regno;
       unsigned rn = inst.operands[2].addr.base_regno;
       int32_t imm = inst.operands[2].addr.offset.imm;

       gdb_assert (inst.operands[0].type == AARCH64_OPND_Rt);
       gdb_assert (inst.operands[1].type == AARCH64_OPND_Rt2);
       gdb_assert (inst.operands[2].type == AARCH64_OPND_ADDR_SIMM7);
       gdb_assert (!inst.operands[2].addr.offset.is_reg);

       /* If recording this store would invalidate the store area
          (perhaps because rn is not known) then we should abandon
          further prologue analysis.  */
       if (pv_area_store_would_trash (stack,
                                      pv_add_constant (regs[rn], imm)))
         break;

       if (pv_area_store_would_trash (stack,
                                      pv_add_constant (regs[rn], imm + 8)))
         break;

       pv_area_store (stack, pv_add_constant (regs[rn], imm), 8,
                      regs[rt1]);
       pv_area_store (stack, pv_add_constant (regs[rn], imm + 8), 8,
                      regs[rt2]);

       if (inst.operands[2].addr.writeback)
         regs[rn] = pv_add_constant (regs[rn], imm);

     }
      else if (inst.opcode->iclass == testbranch)
     {
       /* Stop analysis on branch.  */
       break;
     }
      else
     {
       if (aarch64_debug)
         {
           debug_printf ("aarch64: prologue analysis gave up addr=0x%s"
                         " opcode=0x%x\n",
                         core_addr_to_string_nz (start), insn);
         }
       break;
     }
    }

  if (cache == NULL)
    {
      do_cleanups (back_to);
      return start;
    }

  if (pv_is_register (regs[AARCH64_FP_REGNUM], AARCH64_SP_REGNUM))
    {
      /* Frame pointer is fp.  Frame size is constant.  */
      cache->framereg = AARCH64_FP_REGNUM;
      cache->framesize = -regs[AARCH64_FP_REGNUM].k;
    }
  else if (pv_is_register (regs[AARCH64_SP_REGNUM], AARCH64_SP_REGNUM))
    {
      /* Try the stack pointer.  */
      cache->framesize = -regs[AARCH64_SP_REGNUM].k;
      cache->framereg = AARCH64_SP_REGNUM;
    }
  else
    {
      /* We're just out of luck.  We don't know where the frame is.  */
      cache->framereg = -1;
      cache->framesize = 0;
    }

  for (i = 0; i < AARCH64_X_REGISTER_COUNT; i++)
    {
      CORE_ADDR offset;

      if (pv_area_find_reg (stack, gdbarch, i, &offset))
     cache->saved_regs[i].addr = offset;
    }

  do_cleanups (back_to);
  return start;
}

实质上采用的是 levenbergIter迭代法,遇到这种问题,就要看返回的对象是不是对,是不是因为未定状态,造成stackoverflow,从而引发的 trap的signal. 例如返回的result的值是否合法。

因为 aarch64 中 x29 就是Framepointer. http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf

0x537838 <g2o::SE3Quat::operator Eigen::Transform<double, 3, 1, 0>() const+92>  str    x0, [sp]
0x537838 <g2o::SE3Quat::operator Eigen::Transform<double, 3, 1, 0>() const+92>  mov    sp, x29
x0             0x7fffffdf18     549755805464
void OptimizationAlgorithmLevenberg::printVerbose(std::ostream& os) const
 {
   os
     << "\t schur= " << _solver->schur()
     << "\t lambda= " << FIXED(_currentLambda)
     << "\t levenbergIter= " << _levenbergIterations;
 }

ch9

工程会报错 opencv/viz.hpp没有
dpkg -L  opencv //可以查看这个库到底安装了哪些文件
apt install -y libvtk5-dev
cmake -DWITH_VTK=On <path to your opence srouce>
make
make install  //update the include path

ch10

g20_bundle

_images/ch10_g2oBundle.png

g2o BuildSystem消耗的时间比较多

cere bundle

_images/ch10_g2oBundle.png

ch11

build error: gtsam module 为什么版本之间ARM不兼容。 https://github.com/introlab/rtabmap/issues/131, 即使都采用了相同的编译器与C++11 还是有这样的问题,很有可能是一些宏导致构成cast 链出现问题。

pose_graph_g2o_lie
_images/ch11_pose_graph_g2o_lie.png

unkown的callstack就需要进一步用的工具分析。或者添加相关的库的dbg文件来进一步解析。

Stage_3/SLAM_book/ch11_pose_graph_g2o_lie_timeline.pngcholmod_factorize是blas库的CHolesky分解也就是LU分解。
_images/ch11_pose_graph_g2o_lie_timeline_topdown.png

ch12

字典的生成,DBoW3。 字典建立的原理,

  1. 从训练图像中离线抽取特征
  2. 将抽取的特征用聚类算法把描述子空间划分成K类。
  3. 将划分的每个子空间,继续利用聚类分析
  4. 直到将描述子形成一个树形结构
struct Node {
   NodeId id;  //节点标号
   WordValue  weight; // 该节点的权重,也出现的字数除以总描述子数目
   TDescriptor descriptor; //描述符
   WordId word_id;  //如果是叶子节点,则有词汇的id
}

DBow#的结构,

Fast Bag of Words 用AVX,SSE,MMX 指令来优化了一下,加载 比DBOW2快了~80倍, 生成字典时快了 ~6.4倍。

feature_training
_images/ch12_feature_training_Analysis_summary.png
_images/ch12_feature_training_timeline.png

虽然有多线程,但是并行度非常低,并且thread context的切换次数比较多。

Loop_check
_images/ch12_loop_check_Analysis_summary.png _images/ch12_loop_check_timeline.png

ch13

octomap 是可以用来展示地图,https://github.com/OctoMap/octomap 几种地图的优缺点 http://www.cnblogs.com/gaoxiang12/p/5041142.html

而简单的pcd 点运地图没有法进行导房,而采用了八叉树的模式来建立地图就像游戏地图的构件过程一样。

dense_monocular

直接计算特征点匹配计算量太大,采用极线搜索与块比匹配。

  1. 各个算法分析与对比

双目相机模型

利用视差来计算深度,并且 视差与距离成反比。虽然视差计算深度的公式很简单,但是视差本身的计算比较困难。 需要左右眼图像点一一匹配关系,如果计算每个像素的的深度时,其计算量与精度将成为问题,而且只有图像毋庸纹理 变化丰富的地方才能计算视差,计算还是GPU或者FPGA来计算。

RGBD-SLAMV2

它可以直接主动测每个像素的深度,现在发为两种

  1. 通过红外结构光
  2. 通过 飞行时间法 来测量。

而采用RGB-D 则生成采集到点云。

采用DVO的方法,直接上PCL来做了。

SLAM++

LSD-SLAM

LSD-SLAM is a novel approach to real-time monocular SLAM. It is fully direct (i.e. does not use keypoints / features) and creates large-scale, semi-dense maps in real-time on a laptop。 是直接法最好的SLAM,就要求传感器有深度信息,如果有每一点深度信息那叫Dense depth maps. Semi-dense Depth maps就是深度图并不并包含每一个象素点,只有移动像素的深度图.如果这些移动的点是刚体运动,这样包含了 rigid body motion+ scale. https://fradelg.gitbooks.io/real-time-3d-reconstruction-from-monocular-video/content/stereo/semi-dense.html 原理: 主要是解决地图的3D重构,直接三维的点来恢复出3D结构,例如深度图,然后用点云匹配生成三维结构.这里dense map,其实相当于图形的Depth buffer,以及Z-buffer的做法.

\[K_{i} = ( I_{i}, D_{i},V_{i})\]

其中每一帧包含三大块,亮度值,深度值,以及深度方差值。

Steps: #. 参考帧上极线的计算。 #. 极线上得到最好的匹配位置 #. 通过匹配位置计算出最佳的深度。

其实上面最终都是误差的计算, #. 几何差异误差,原于相对朝向以及投影矩阵。 #. 图像差异误差, #. 基线量化误差

深度估计直接使用,直接采用卡尔曼滤波来实现。要通过profiling来快速得到其workflow. 然后结合callgraph. 三角化之后,就可以充分利用Opengl来直接化了。

视频:https://www.youtube.com/watch?v=GnuQzP3gty4&feature=youtu.be&t=22s code: github.com/tum-vision/lsd_slam 代码解读:http://blog.csdn.net/lancelot_vim/article/details/51708412 原理: http://vision.in.tum.de/research/vslam/lsdslam 优点: 不需要keypoints,features 提取,精度高,尤其是在室内这种环境. 主要是采用keyframe的方式,进行,有没有办法用skybox再加上LOD来生成一个keyframe. 一个keyframe其实是 生成两个双目frame的视频流。如何反向呢。 性能:

SVO

性能:近年来非常火的SLAM路线,能恢复半稠密三维,但目前的实现不太好,效率也不高 但是完全可以用OPENGL+CUDA 的汇合来实现同时在cuda中再加入DL. LSD-SLAM就是SVO的一种 主要步骤 #. 稀疏直接法, 其实就是金字塔图形里找 #. 重投影,把关键点重新投影 #. 优化 #. 重定位

ORB-SLAM

是目前基于特征的单目SLAM系统中效果最好的。 基于ORB特征来实现环境一致的描述。利用orb贯穿全程,并且dbow2库来进行回环检测。采用nearest neighbor方法训练出来的140多m的树状词典 所有优化问题全是基于g2o算,特征匹配直接用ORB的描述符,回环检测用BOW。并没有太多学术上的创新,论文里几乎没有什么公式。

原理:

  1. MapPoints->KeyFrames->CoovisibilityGraph->SpanningTree 具体的流程可以参考 SLAM、orb-slam7.1 简单重构泡泡.pptx

  2. 并且采用了面向对象思想封装,把获取的图像数据装成帧。后续处理都是针对帧处理。

    Frame {
       Image-> 存储图像数据(输入)
       Camera-> 存储相机的内参信息(输入)
       Feature-> 存储帧对应的特征数据(计算) 一对多的关系
       Mat -> 存储帧对应相机的姿态 (计算)
    }
    Keyframe extends Frame;
    Feature {
    }
    MapPoints {
    }
    是不是可以相当于骨骼动化的关系。
    
  3. Trac根据运动模型,可以opengl中的T&L变换,然后进行对比。

orb-slam得到在世界坐标系中位置 值很小 很多都是0.00几 orb-slam2中 输出的位姿 乘以初始化成功时该帧的变换矩阵 所得变换矩阵是世界坐标系到相机坐标系的转换么 SLAM-职业交流群 无题 李 code: https://github.com/raulmur/ORB_SLAM2 优点: 特征提取的匹配的计算量减少,回环检测做很好,接口丰富,对于地图密度要求不高的定位和追踪问题,ORB-SLAM是个不错的选择 缺点: #. kinect2 qhd分辨率下(960x540),默认参数,thinkpad T450,帧率<=10Hz 计算量具大。 #. 运行前要读取一个几百兆的字典——调试程序的时候比较考验耐心, 巨大的词典。目前orb使用了一个用nearest neighbor方法训练出来的140多m的树状词典。前面已经说过,这个词典在整个orb中至关重要,每次运行前必须载入至内存。在安卓手机上载入的过程长达三分钟,很恼人 #. 比较容易lost,虽然也容易找回来; #. orb 所建图非常稀疏,而稀疏的地图对于机器人下一步的应用会造成很大困难 #. 系统中有很多magic number,比如特征匹配的阈值,回环图像对比的阈值,都是经验设定,在不同场景下对应值也有所不同。这些数其实可以通过机器学习的方法学习得到 #. 采用keyframe这样的空间是不连续的。

性能: #. i7,640*480的图像中提取500orb约用时13ms,匹配精度可以接受,满足实时性要求 #. 追踪部分,平均每帧约30毫秒,基本达到了30fps。特征提取速度是非常快的,平均11毫秒左右,非常适合于实时SLAM。姿态估计稍微耗时一些,平均需要20毫秒,特别是姿态优化需要耗费16毫秒的时间。 #. 地图构建部分,平均每关键帧约385毫秒。其中生成新的点约70毫秒,Local BA约300毫秒,相对还是比较耗时的。不知道这两部分还有没有优化的空间。

  1. 关键点的提取与描述子的计算非常耗时。实践当中,SIFT目前在CPU上是无法实时计算的,而ORB也需要近20毫秒的计算。如果整个SLAM以30毫秒/帧的速度运行,那么一大半时间都花在计算特征点上

DSO

发现DSO方法的特征点提取比ORB更耗时 . SVO,

DTAM Dense Planar SLAM, OpenRatSLAM, G2O, SBA, iSAM, GPU-SLAM

  1. 什么时候开始的
  2. 每种技术优缺点

主要研究内容

#. 传感器的鲁棒性以及传感器融合。 1. 精度上,AR一般更关注于局部精度,要求恢复的相机运动避免出现漂移、抖动,这样叠加的虚拟物体才能看起来与现实场景真实地融合在一起;机器人一般更关注全局精度,需要恢复的整条运动轨迹误差累积不能太大,循环回路要能闭合,而在某个局部的漂移、 抖动等问题往往对机器人应用来说影响不大。

#. 复杂环境的场景理解 前面的都是基础的SLAM,只有”定位”和”建图”两件事。这两件事在今天已经做的比较完善了。近几年的RGB-D SLAM[5], SVO[6], Kinect Fusion[7]等等,都已经做出了十分炫的效果。但是SLAM还未走进人们的实际生活。为什么呢?  因为实际环境往往非常复杂。灯光会变,太阳东升西落,不断的有人从门里面进进出出,并不是一间安安静静的空屋子,让一个机器人以2cm/s的速度慢慢逛。论文中看起来酷炫的算法,在实际环境中往往捉襟见肘,处处碰壁。向实际环境挑战,是SLAM技术的主要发展方向,也就是我们所说的高级话题。主要有:动态场景、语义地图、多机器人协作等等。 http://www.cnblogs.com/gaoxiang12/p/4395446.html 非特点SLAM, 动态环境,多机器人协作,长时间SLAM。 语义信息。 拓扑/网格地图。 #. 复杂背景下慢速的目标的识别,(怎样识别,识别哪些?) #. 动态场景运动分离技术 #. 主动SLAM,SLAM+ Exploration.

#. SLAM 的计算需求巨大的,现有算法达不到实时性的要求。 效率上,AR需要在有限的计算资源下实时求解,人眼的刷新率为24帧,所以AR的计算效率通常需要到达30帧以上,但是要想流畅就得每秒60fps 也就是小于16ms,如果想没有眩晕,要至少达到90fps也就是要11.111111ms.; 机器人本身运动就很慢,可以把帧率降低,所以对算法效率的要求相对较低。 3. 配置上,AR对硬件的体积、功率、成本等问题比机器人更敏感,比如机器人上可以配置鱼眼、双目或深度摄像头、高性能CPU等硬件来降低SLAM的难度,而AR应用更倾向于采用更为高效、鲁邦的算法达到需求。

  1. 研究利用传感器神经网络与回声状态状态网络 解决鲁棒性与精度的问题。
  2. 研究利用3D重构技术来实现地图的重建与深度学习来解决复杂场景理解。
  3. 研究利用LLVM来实现全时优化来解决算法的效率,利用GPU与FGPA来提供的计算能力。

由于SLAM 算法的本身的复杂性,以及硬件平台的多样性,开发难度大,需要直接使用现有大量的成熟的库来加快开发速度。例如对于eigen3矩阵运算库,Ceres 优化库,以及OpenCV,PCL等大量库使用。可以提高开效率。但是对运行效率很难保证,实时性更无从谈起。更加大的算法仿真的效率。需要用大量的分析优化工作来运行效率。 LLVM是基于SSA的能够提供编译时期,链接时期,运行时期,闲置时期全时优化的动态编译技术。利用利用LLVM 的SSA形式的IR来分析数据依赖关系,删除无效代码,实现循环的展开,尽可能并行化,根据硬件资源特性与限制最大化利用硬件资源例如寄存器与高速的cache,ARM的NOEN指令,以及GPU的SIMT(Single instruction Multiplethread)技术。大大提高算法的计算效率。同时利用LLVM本身指令信息的完备性,能够对于进行硬件模型的精确化分析。 #. GPU加速的SLAM

基本的数学模型

相机自身的位置与当前位置下的路标的观测数据。 所以一共两个方程 一个自身的运动方程,另一个对于路标的观测方程。当相机在 \(x_k\) 位置时,看到某个路标点 \(y_j\) ,产生了一个观测数据 \(z_{k,j}\).

\[\begin{split}运动方程 与 观测方程 \begin{array}{lcl} x_k & = & f(x_{k-1},u_k,w_k) \\ z_{k,j} & = & h(y_j,x_k,v_{k,j}) \end{array}\end{split}\]

\(x_i\) 是相机自身的位姿状态 \(t=1,...,K\)\(u_k\) 是传感器参数, \(w_k\) 是噪声, \(v_{k,j}\) 是观测里的噪声。 相机检测的路标用 \(y_1,...,y_n\) 来表示。

基本的工作流

SLAM 的难点,在仅通过计觉里程计算将不避免地出现累计漂移。 当然传感器本身误差不准确,以及和各种噪声的存在。为了解决漂移问题,我们采用两种技术 后端优化与回环检测。 后端优化这就要通过一个nlp优化,而回环检测,就是把机器人回到原始的位置给检测出来。

digraph G {
     graph [layout=dot rankdir=LR];
     node [shape=box];
     sensor->vo->nlp->map[weight=10];
     sensor->loop_check->nlp;
}

传感器

  1. 单目摄像头,无法解决尺度问题
  2. 双目, 通过视差来解决深度
  3. RGB-D 深度摄像头
  4. 激光雷达
  5. IMU 测量自身的位姿
  6. GPS

VO

主要是解决,计算机是如何通过图像确定相机的运动。相机本身的旋转与平移。 主要是通过相邻帧间的图像估计相机运动。并恢复场景的空间。

  1. 特征点,关键点与描述子两部分组成,相似性通过描述子的各种度量距离来计算暴力匹配,可以用汉明距离,当然还快速近假最近邻(FLANN)。 特征是图像信息的另一种数字表达式,我们的目标是要使特征点在相机运动之后保持稳定。

    CPU还无法实时计算SIFT特征。当然通过GPU加速后的SIFT,就可以满足实时计算要求。

特征点个数 ORB(Oriented FAST and Rotated BRIEF) SURF SIFT
1000 15.3.ms 217.3ms 5228.7ms.

不同的方法,角点的描述能力也不一样。

  1. 单目的时候,从2D的像素坐标估计相机问题运动,对采用对极几何,多线共面的约束来求。
  2. 双目 RGB-D, 两组3D点来估计运动,来求平移与旋转,就用ICP来解决。
  3. 如果3D点与它们在相抽的投影位置,用PnP来估计的相机的运动。

而采用传统图像处理的方法,特征点不稳定,量大,并且特征点不突出,并且没有语义。而现在基于 DL的做法,可以直接得到基于语义的特征点。 特征点: 可重复性,可区另性,高效率,本地性。

特征点的优缺点:

  1. 关键点的提取与描述子的计算在耗时。
  2. 使用特点点,忽略了除特征点以外的所有信息,相当于丢弃了大部分可能有用的图像信息。
  3. 相机在没有明显纹理信息的场景下,会遇到特征缺失,没有找到足够的匹配点来计算机相运动。

不同的算法应用不同的场景,并且计算量与精度差别很大,并且适用性不强。

直接法:

  1. 保留特征点,但不计算描述子。直接发使用光流跟踪,并且直接计算特征点在一时刻的位,光流法会根据图像像素灰度信息来计算机运动。对光照要求严格。
_images/SFM_overview.png _images/outline.png _images/overview.png _images/nvx_arch_diagram.png

nlp 非线性优化

因为在自然状态下,看到的结果就是当前环境约束下的最优解。 所谓的优化也就是得到一组平衡点。

  1. 原理根据依赖列出方程组,然后再根据变量的个数建立最小方程组的个数。 最后还能通过量最优解。
  2. 李群李代数利用旋转与平移矩阵组合李群的特征,然后再通过李代数 把转换成代数约束来建立方程。
  3. g2o 图优化,就是利用图论的理论来做优化,利用图论,利用变量当做结点,而约束变成边。

#. 要么是常规的非线性约束,例如非线性的最小二乘,那就利用Ceres库。 https://mp.weixin.qq.com/s?__biz=MzI5OTY0NzA5Ng==&mid=2247484130&idx=1&sn=da1d8c77d7c7c16756c9045bfdd730e3&chksm=ec922bf5dbe5a2e3f4f5db81871e01472565153c2aaed7edd398a45ba4bd25c18a0d798e5f31&mpshare=1&scene=23&srcid=0825m9qjWdGbF8tM27S1BxEE#rd VIO回顾:从滤波和优化的视角

Mapping

建立的地图分为两种,度量地图与拓扑地图。 对于拓图地图可以用游戏里的八叉树之类的 来建立。当然这个是建立图形的识别的基础上的,这个地方可以利用生GAN生成模型。

度量地图,就是那种分网格,tiling地图,记录每一个tile里状态。最好多的状态那就是路标信息。

  1. 稀疏的地图
  2. 半稠密地图
  3. 稠密地图

直接用点阵库这样才建立map会太大,可以词态来建立关键链。 当然词态可以用聚类的这些算法来实现。 最简单的结构那就是点云了,这就是利用上了`PCL <http://pointclouds.org/>`_ 库了。

从3D模型 来生成点云 , 格式也也就差不多了。坐标,格式,以及各种参数。 对于点云的拼接与配准。

传感器的融合

单一的传感器功能往往十分有限,但是多个传感器之间的相互关系复杂,很难直接描述。 现在可以利用神经网络直接连接各个传感器,并且通过神经网络的网络的拓扑结构来表达之间的相互关系。来解决各种滤波的问题。

每一种传感器都有其独特的优势与弱点。可以通过组合来实现取长取补短。同时处理复杂环境数据,会造成车辆驾驶决策的延迟,而最大限度减少这种延迟,是提升车辆安全性的又一关键部分,一个方案那就是数据融合能够减少系统做驾驶决策所需的计算资源。

例如无人驾驶通过传感器融合技术,将各种传感器的不同视角整合,并最终赋能自动驾驶车辆环境感知。 并且通过冗余来提供鲁棒性。 例如毫米波雷达可在低分辨率情况下完成测距,且受天气困素影响很小,而摄像头有更高的分辨率,能够感知颜色,但受强光影响较大,激光雷达则能够提供三维尺度感知信息,对环境的重构能力更强。

同时利用人工智能自动不断迭代来建立一个高精度的地图。

自动驾驶对于计算量的要求。

现在的奥迪A8可以在60KM/h下实现自动驾驶,你就是16.66m/s. 每秒20fps,其核心是每帧可以前进行多少米。也就是16.66/20=0.833 也就是计算每帧0.833秒。 这个同时也与探测的安全的距离相关,也就是给你多少反应时间。同时这么大的计算量的对于资源的消耗又是怎样的。

相机

相机的模型,那就要做相机的标定,包括内参数与外参数。 内参数就是光学参数,焦距以及畸变参数。 而外参数则是指相机的位姿,相比于不变的内参,外参数会随着相机的运动发生改变,同时

ORB

orb 加了码盘信息(帧间约束),走2.4米,跟踪点少,光照100流明以下时,误差30~10cm 特征点跟踪100个以下时,误差太。

普通摄像头噪点多,误匹配多,系统极不稳定。

现在能否直接把识别直接放在传感器,对了用FPGA。把FPGA + DL + 传感器放在传感器上。

SAR

AR利用的是全息成像的原理来提高分辨率. SAR要垂直目标运动, 汽车不合适。

雷达

要穿透粉尘和烟雾,波长更短的波不是表现效果更好么 常规雷达波的波长比红外还长,穿透力还不如可见光 这岂不是从一开头就背道而驰 奇了怪了,可能这方面我的背景知识不够吧 穿透的话常规都是想的伽马射线和X射线短波长连人体都穿透摄像 理论上短波长才是透射强啊 地铁安检用的也是X光机穿透皮包 所以这个。。 唉 光学没专门学过不懂了 怎么就可见光谱不能穿透烟雾呢 不懂不懂,一堆程序猿讨论光学知识 我觉得我们这里缺了光学专业的大佬 电磁波具有绕射能力,波长越短,绕射能力越差 @125.60 你可以关注一下这个公司,看有没有合适的产品 http://www.360doc.com/content/17/0228/18/8507568_632753112.shtml 太赫兹安检主要是中电和中航在做,大学和民企连仪器都买不起,也拿不到国家补贴 /

Indices and tables