Matrix

CarlyleLiu‘s Blog

投影

这里以小孔成像模型为例,全相模型是相同的流程只是投影模型不一样而已。在相机校准模型一文中已经介绍了将像素坐标系变换到相机坐标系中 相机校准模型 如下:

[uv1]=[1dx0uo01dyv0001][xy1]=1zc[fdx0uo0fdyv0001][xcyczc]

这里我们取 zc=1 再做一个变换得到:

[xcyczc]=[fdx0uo0fdyv0001]1[uv1]

我们记 :

K1=[fdx0uo0fdyv0001]1

则将像素坐标系与该矩阵相乘就可以将像素坐标系转换到相机坐标系上,待后面使用。

IMU 数据处理

具体可以参考 imu 姿态解算 一文,我们需要对 IMU 数据做三件事情:

  • IMU 坐标系对齐相机坐标系,这一步目的为了将 IMU 的正交三轴数据与 Camera 坐标系对齐。

  • 卡尔曼滤波:为了消除 IMU 的系统误差以及如果要做重力矫正需要做姿态解算(重力对齐)。这里常用的就是卡尔曼滤波和 mahony 滤波,这一层滤波的目的就是为了得到更为准确的相机姿态和相机抖动状态,如下图所示:

  • 平滑滤波:在我们得到相机的抖动状态就可以对相机的姿态做一个平滑滤波(可以采用四元数求平均,或者高斯滤波等算法),这个平滑滤波的目的是为了得到相机防抖后想要得到的状态。

我们将从 Large inner region 曲线或者 Small inner region 曲线到 Input 曲线的变换矩阵记为 M(α,β,γ) 为了简化模型这里面的数据是已经将 IMU 坐标系变化到相机坐标系下的变换矩阵。

到这里我们就得到了两条曲线和一个变换矩阵。

求透视变换矩阵

这里我们记旋转矩阵为:

M(α,β,γ)=[cosγsinγ0sinγcosγ0001][cosβ0sinβ010sinβ0cosβ][1000cosαsinα0sinαcosα]=[cosγcosβsinγcosγsinβsinγcosβcosγsinγsinβsinβ0cosβ][1000cosαsinα0sinαcosα]=[cosγcosβsinγcosα+cosγsinβsinαsinγsinα+cosγsinβcosαsinγcosβcosγcosα+sinγsinβsinαcosγsinα+sinγsinβcosαsinβcosβsinαcosβcosα]

综合前面两节的内容我们可以通过如下公式得到投影变换矩阵 R:

R=K1M(α,β,γ)K

改写成完整的公式如下:

R=[fdx0uo0fdyv0001]1[cosγcosβsinγcosα+cosγsinβsinαsinγsinα+cosγsinβcosαsinγcosβcosγcosα+sinγsinβsinαcosγsinα+sinγsinβcosαsinβcosβsinαcosβcosα][fdx0uo0fdyv0001]

该公式是通过相机的内参矩阵将像素坐标系变换到相机坐标系,然后通过 IMU 得到相机的姿态,将相机坐标系变换到世界坐标系下对相机的姿态进行稳定,得到稳定后的相机姿态,再将上述矩阵的逆乘起来变换回像素坐标系。这样就得到了透视变换的矩阵,将像素的坐标与该 R 矩阵相乘就得到了该像素防抖后的坐标值(就是 opencv 里的 remap)。

效果

特别注意

  • 在做 IMU 姿态解算的时候要给陀螺仪足够的置信度,不然可能很难得到较好的防抖效果。
  • 要将视频帧的时间戳与 IMU 的时间戳对齐,不然一定不能得到较好的效果,笔者没有研究好这块算法手动试出来的,精度要在 1ms 内才行,另外有香港科技大学的 VIO 项目里面有相关论文和实现可以参考。
  • rollingshutter 需要矫正,其实算法上需要做的就是找到帧曝光开始和结束的时刻,然后将旋转矩阵按行进行插值得到每一行的矩阵然后分别对每一行进行应用 remap 就可以了。
  • 这里没有考虑破图问题,实际工程中要考虑破图问题。
  • 这里存在一个问题就是将原图乘透视变换矩阵得到的坐标值大概率是浮点值,但是像素坐标系下只有蒸熟点那么就会造成画质损失,如果想改善这个,可以通过将目标图像乘矩阵 R 的逆得到原图的坐标点,当然这里也会得到浮点坐标坐标值,但是对于原图我们可以通过双线性插值得到变换后的图像值(注意这里是操作图像值而不是坐标值了)。
  • 实际使用中一般不会直接使用旋转矩阵,会改用四元数来处理,便于插值。

概述

V4L2 是专门为 linux 设备设计的一套视频框架,其主体框架在 linux 内核,可以理解为是整个 linux 系统上面的视频源捕获驱动框架。其广泛应用在嵌入式设备以及移动端、个人电脑设备上面,市面上的编码产品类如:SDV、手机、IPC、行车记录仪都会用到这个框架来进行视频采集。

框架

该图描述了 v4l2 驱动框架的整体结构,v4l2 本质上也是一个字符设备驱动程序,图中芯片模块对应 Soc 的各个子模块,video_device 结构体主要用来控制 Soc 的 video 模块,v4l2_device 会包含多个 v4l2_subdev ,每个 v4l2_subdev 用来控制各自的子模块,某些驱动不需要 v4l2_subdev ,依靠 video 模块就能实现功能。

  • video_device:一个字符设备,为用户空间提供设备节点(/dev/videoX),提供系统调用的相关操作,是采集设备的抽象接口。
  • vb2_queue:与 vb2_v4l2_buffer 一起用于数据流的实际逻辑和 DMA 操作。
  • v4l2_device:表示一个 v4l2 设备的实例。
  • v4l2_subdev:属于 v4l2 设备的子设备,一个 v4l2 设备可以拥有多个子设备。
  • videobuf:v4l2 驱动的缓存管理。
阅读全文 »

概述

该框架旨在提供标准内核接口来控制电压和电流 Regulator。其目的是允许系统动态控制 Regulator 功率输出,以节省功率并延长电池寿命。 这适用于电压 Regulator(电压输出可控)和电流 Regulator(电流限制可控)。

阅读全文 »

概述

RTC 和系统时钟有不同的用途。前者是硬件时钟,以非易失方式维护绝对时间和日期,而后者是内核维护的软件时钟,用于实现 gettimeofday(2)和 time(2)系统调用,以及在文件上设置时间戳等。系统时钟报告从起点开始的秒和微秒,起点定义为 POSIX 纪元:1970-01-0100:00:00 +0000(UTC)。

阅读全文 »

概述

CCF 主要用于系统 clock 的管理等操作。clk 的种类说明:

如上图所示,时钟源大概可分为如下几种:

  • 提供基础时钟源的晶振(可分为有源晶振、无源晶振两种)
  • 用于倍频的锁相环
  • 用于分频的 divider
  • 用于多路时钟源选择的 mux
  • 用于时钟使能的与门电路等

而在 CCF 子系统的抽象中,这五种均抽象为 clk,但是针对这 5 种类型的时钟也提供了单独的时钟注册函数(也就是对 clk_register 函数的封装,并针对不同的时钟类型定义了不同的结构体)。

在 CCF 子系统中,针对硬件时钟的操作接口,也抽象了对应的结构体 struct clk_ops,包含时钟的使能接口、时钟频率的修改接口等等。而针对上述所说的不同种类的时钟源,其并不需要实现所有 struct clk_ops 中定义的接口。针对“时钟使能的与门电路”而言,仅需要实现 enabel、disable、is_enable 接口即可;针对多路时钟源选择的 mux 而言,则需要实现父时钟源的设置及获取的接口 set_parent、get_parent 等;对于倍频、分频而言,则需要实现时钟频率相关的接口 set_rate、recalc_rate 等。

阅读全文 »

概述

按键输入、键盘、鼠标、触摸屏等等这些都属于输入设备,按键和键盘就是代表按键信息,鼠标和触摸屏代表坐标信息,因此在应用层的处理就不同。为此 input 子系统分为 input 驱动层、 input 核心层、 input 事件处理层,最终给用户空间提供可访问的设备节点,input 子系统框架如图 所示:

  • 驱动层:输入设备的具体驱动程序,比如按键驱动程序,向内核层报告输入内容。
  • 核心层:承上启下,为驱动层提供输入设备注册和操作接口。通知事件层对输入事件进行处理。
  • 事件层:主要和用户空间进行交互。
阅读全文 »

概述

内核版本 3.1 中引入了 Regmap API,用于统一内核开发人员访问 SPI/IIC 设备的方式,无论是 SPI 设备还是 IIC 设备,只需要初始化,配置 Regmap 就可以通过 Regmap 读写设备。Regmap 子系统主要提供如下两种功能:

  • 第一是为 IIC/SPI/MMIO 等提供统一的访问接口,linux 中大量的 iic 和 spi 设备就可以通过统一的接口进行访问尤其对于那些同时支持 iic 和 spi 接口的设备。
  • 第二是提供缓存访问机制用于加速设备访问设备,对于支持缓存的设备这将大大加快设备的当问速度。

下面看一下 Regmapz 子系统在驱动中的位置,如下:原图

阅读全文 »

Pinctrl 概述

关于 pinctrl 主要可以归结为两类设置,其中一类是功能选择,即一组 gpio 是用于 iic 还是 uart 还是就作为普通 gpio 来用,另一类是 gpio 的特性配置,即上拉、下拉、驱动能力和速率的配置。而 pinctrl 主要负责这两类配置的管理工作。总结起来 pinctrl 主要完成以下三种功能:

  • 引脚枚举与命名 (Enumerating and naming)
  • 引脚复用 (Multiplexing):比如用作 GPIO、I2C 或其他功能
  • 引脚配置 (Configuration):比如上拉、下来、open drain、驱动强度等
阅读全文 »

GPIO 概述

以前学习 stm32 的时候看到手册里有关于 gpio 的电路图,如下:

  • 保护二极体:
    IO 引脚上下两边两个二极体用于防止引脚外部过高、过低的电压输入。当引脚电压高于 VDD 时,上方的二极体导通;当引脚电压低于 VSS 时,下方的二极体导通,防止不正常电压引入晶片导致晶片烧毁

  • P-MOS 管和 N-MOS 管:
    由 P-MOS 管和 N-MOS 管组成的单元电路使得 GPIO 具有“推挽输出”和“开漏输出”的模式

  • TTL 肖特基触发器:
    信号经过触发器后,模拟信号转化为 0 和 1 的数字信号。但是,当 GPIO 引脚作为 ADC 采集电压的输入通道时,用其“模拟输入”功能,此时信号不再经过触发器进行 TTL 电平转换。ADC 外设要采集到的原始的模拟信号

阅读全文 »
0%