38、用于立体匹配的脉动阵列技术解析

用于立体匹配的脉动阵列技术解析

脉动阵列的基本控制与操作

在立体匹配的应用场景中,控制器将脉动阵列视为一个黑盒进行驱动。初始化的目标是让阵列达到正向传播前一个时钟周期的状态。具体做法是将数据流移入脉动阵列,直至两条数据流填满除最后一个寄存器之外的所有寄存器。这一条件十分必要,因为在正向传播开始时,两条数据流的首个数据必须在最后一个处理器处相遇。

正向传播的目的是同步地向脉动阵列提供两条数据流。在等待脉动阵列完成操作后,控制器开始从阵列接收视差结果。这个视差值是差分的,即 $\eta \in {1, 0, -1}$,而非绝对值,因此需要进行累加。初始化和正向传播依赖于八种不同类型的电路,不过最终处理和反向传播则是相同的,这是因为所有八种类型的阵列都使用相同的数据结构来存储指针,该结构可以通过数组或队列实现。

脉动阵列的每个元素会与系统时钟同步执行预定操作。其输入包括图像、成本和活动位,输出则是图像数据、成本和活动位。以下是处理元素的算法:
1. 初始化 :传递图像数据。
2. 正向传播 :对于 $t = 0, 1, \cdots, N - 1$,读取 $\varphi(d + 1)$ 和 $\varphi(d - 1)$,确定 $\eta$,并更新 $\varphi(d)$。
3. 最终处理 :设置 $a(0) \leftarrow 1$。
4. 反向传播 :对于 $t = N - 1, N - 2, \cdots, 0$,检索指针。若活动位为真,则根据指针设置两个相邻处理元素之一的活动位,并输出指针;否则,输出高阻抗。

所有元素会完美同步地执行相同操作。每个元素通过节点 ID(即视差编号)和时钟计数(所有元素共用)来知晓自身状态。一旦由控制器启动,元素会自行运行至反向传播结束,然后返回等待状态。在初始化状态下,元素会按预定次数简单传递输入图像数据,最终其寄存器将被图像数据填满。随后进入正向传播阶段,对输入图像执行预定义操作,传递成本,并将指针存储在内部数组中。最后在最终处理阶段,特定元素的活动位会被设置为视差为零的节点。接着进入反向传播阶段,读取指针数组,设置或重置相邻元素的活动位,并输出指针。若该节点不是活动节点,则可通过三态逻辑禁用指针输出。

脉动阵列设计的难点在于初始条件的准备以及数据流的正确同步。一旦实现完美同步,系统将成为一个高速电路,无需额外的控制消息干预。为了用紧凑的代码涵盖八种基本电路,我们需要尽可能创建通用的结构。

电路的通用平台

脉动机器的基本结构与 LVSIM 类似,但存在一些差异。它主要包含三个用于存储中间数据的缓冲区。其中两个图像缓冲区分别存储从两个外部 RAM 读取的左右图像行,并且这些缓冲区扩展了更多通道,以便存储特征向量。第三个视差缓冲区则存储当前周期更新的新视差或从外部 RAM 读取的先前视差结果。

这三个缓冲区是具有列(x)、行(y)和通道(z)三维结构的数组,类似于图像 $I(x, y, z)$,其中 z 表示通道。通常,前三个通道存储 RGB 数据,其余通道存储特征图。通道数量会根据应用的不同而有所变化。除了图像缓冲区的通道扩展外,还添加了脉动阵列这一组件。

在控制单元的监督下,处理器利用这四个主要资源执行以下操作:
1. 处理器将图像和先前的视差结果读取到三个缓冲区中。
2. 处理缓冲区中的图像(即 img1 和 img2 中的 RGB 通道),并将特征存储在两个缓冲区的其余通道中(如 img1 和 img2 中的通道 3、4、5 或 6)。
3. 控制单元从两个图像缓冲区构建两条特征向量流,并将它们作为输入提供给脉动阵列。
4. 当阵列输出一系列视差值时,控制单元将这些值存储在视差缓冲区中。
5. 控制单元回到起点,重复上述操作。通过这种方式,计算会在图像帧中的一对对极线向下进行。

为了实现可能的邻域操作,我们将缓冲区扩展为一组行,即条带而非单行图像。这些缓冲区实际上是先进先出(FIFO)队列,图像从缓冲区底部进入,从顶部离开。三个缓冲区会同步更新,从而确保它们始终指示图像帧的同一窗口。为了进行额外的预处理,图像缓冲区包含超过三个通道来存储特征图,目的是用相应图像更新缓冲区中心的内容。

从数学角度来看,该机器计算以下方程:
$D(\cdot, y, t) = T(I_l(\cdot, y), I_r(\cdot, y), D(\cdot, y, t - 1))$
其中,$I_l(\cdot, y)$ 和 $I_r(\cdot, y)$ 是包含特征向量的图像行,$D(\cdot, y)$ 是所需的视差,$T(\cdot)$ 表示立体匹配算法的主要操作。除了 $I_l(\cdot, y)$ 和 $I_r(\cdot, y)$ 外,计算结构还允许我们将缓冲区中的其他组件作为邻域使用。此外,视差缓冲区包含当前帧和先前帧中先前计算的视差。这些计算资源使我们能够在主要算法(动态规划,DP)之外进行预处理、邻域和递归操作。

前后左右算法

在八种基本电路中,首先介绍的是 FB 系统,其特点是以右侧为参考,且图像流方向相反。作为对偶电路,FBR 和 FBL 除了两个输入的角色相反外,其余部分相同。因此,我们主要以 FBR 为例进行算法解释。

FBR 系统的架构如下:初始时,左图像向下移动填充除最后一个缓冲区之外的所有缓冲区。随后,右图像也进入阵列,首先在 PE(0) 处与左图像相遇。在正向传播阶段,成本端口 $\varphi$ 处于激活状态;在反向传播阶段,活动端口 $a$ 处于激活状态。结果会驱动总线,使输出能够到达控制器。作为边界条件,末端处理器的输入端口必须设置为合适的值,在这种情况下,终端条件为 $\varphi = \infty$ 和 $a = 0$。处理器使用与其他处理器相同的值,执行相同的操作,避免了复杂的边界条件测试逻辑。

仅靠搜索空间和电路不足以定义详细的算法,我们还需要最详细的描述——时序图。通过分析图 13.7(a) 中的 FBR 搜索空间 $(x, d)$ 和计算顺序,我们可以构建一个新的空间 $(t, d)$,即时序图。该图的横轴表示时间,纵轴表示视差。实际上,$(t, d)$ 图是 $(x, d)$ 的仿射变换,原本倾斜的扫描线被垂直化。此外,这个空间等同于中心参考空间。在正向传播中,计算按指定方向进行,反向传播时方向相反。计算线上的节点会根据成本比较结果尝试匹配图像以确定成本和指针,或者保留先前的成本和指针。最终得到指针表 $(\eta, t)$,可能的解决方案是从 ‘A’ 到 ‘B’ 的线条,这些线条可以在回溯过程中追踪。需要注意的是,起点是固定的,但终点可以是梯形左侧的任意点。

算法必须遵循梯形区域及其边界。该区域是 $(x, d)$ 空间中区域的仿射变换,对于 FBR 和 FBL,梯形区域定义为:
$R = { (t, d) | d \leq t, d \leq N - 1 - \frac{1}{2}t, t \in [0, 2N - 2] }$
$(x, d)$ 和 $(t, d)$ 空间的关系如下:
- FBR:$x_r = \frac{t - d}{2}$,$\forall t + d = even$
- FBL:$x_l = \frac{t + d}{2}$,$\forall t + d = even$
其中,$t$ 等同于中心参考坐标。

每个处理器必须知道自己在空间中的位置,特别是在梯形区域内的位置。在正向传播中,处理器处于以下四种状态之一:
1. 右侧外部 :该区域是禁止区域,所有节点的指针设置为三态,成本设置为一个非常大的数。三态逻辑允许我们将 D 个处理器的输出捆绑到一条线上,高成本简化了父节点决策时的比较过程。即使是边界处理器 PE(D - 1) 和 PE(0) 也会从伪邻居接收非常高的成本。
2. 左侧 :这些节点是右图像上的相同点,必须特殊处理,因为它们是最短路径的终点。在回溯过程中会访问这些节点之外的节点,但不应改变已经在左侧确定的视差值,这一策略需要在编码时通过某种技术实现。
3. 左侧外部 :在回溯过程中会访问这些节点,但不改变视差值。
4. 梯形内部
- 匹配节点 :是确定指针和成本的主要位置,根据邻居成本和输入图像进行比较,由于非法节点在之前的阶段已经被赋予了非常大的数,因此可以在不考虑边界条件的情况下进行比较。
- 遮挡节点 :不进行匹配,而是保留成本并指向先前的匹配节点。随着时间线的移动,节点的角色在匹配和遮挡之间交替。

在回溯过程中,节点 $(2N - 2, 0)$ 是唯一的起点,有 D 个可能的终点,都位于左侧。指针必须紧凑,用三个可能的值 $(-1, 0, 1)$ 表示相对于父节点的相对位置。真实视差是指针的累加值。在恢复最短路径后,需要根据上述方程将位置映射到右图像上。

以下是 FBR 的控制算法:
1. 初始化 :对于 $t = 0, 1, \cdots, D - 2$,$I_{l_i}(D - 1) \leftarrow I_l(t)$。
2. 正向传播 :对于 $t = 0, 1, \cdots, 2N - 2$,$I_{r_i} \leftarrow I_r(t)$ 且 $I_{l_i} \leftarrow I_l(t + D - 2)$。
3. 最终处理 :$d(2N - 2) \leftarrow 0$。
4. 反向传播 :对于 $t = 2N - 2, 2N - 3, \cdots, 0$,
- $d(t) \leftarrow d((t + 1)) + \eta(t)$
- 若 $(t + d)$ 为偶数,则 $disparity(\frac{t - d}{2}) \leftarrow d(t)$

该系统是交错的,因此时钟周期需要两倍的图像宽度。指针数组的长度为 $2N - 1$。在初始化状态下,左图像流入阵列,填充 $D - 1$ 个寄存器。在正向传播中,左右图像流之间存在 $D - 2$ 的偏移。左图像流结束后,继续流入 $D - 2$ 个任意值。在反向传播中,指针被累加,但仅在偶数周期采样。

以下是 FBR 处理元素的算法:
1. 初始化 :对于 $t = 0, 1, \cdots, D - 2$,$I_{r_o} \leftarrow I_{r_i}$ 且 $I_{l_o} \leftarrow I_{l_i}$。
2. 正向传播 :对于 $t = 0, 1, \cdots, 2N - 2$,
- $I_{r_o} \leftarrow I_{r_i}$,$I_{l_o} \leftarrow I_{l_i}$,$\varphi_{u_o} \leftarrow \varphi$,$\varphi_{d_o} \leftarrow \varphi$。
- 若 $d > t$ 或 $d > 2N - 2 - t$,则 $\eta \leftarrow 0$ 且 $\varphi \leftarrow \infty$;若 $d = t$ 且 $\eta(0) \leftarrow 0$,则 $\varphi \leftarrow |I_{r_i} - I_{l_i}|$;若 $t + d$ 为偶数,
- $\eta(t) \leftarrow \arg \min { \varphi_{u_i}, \varphi, \varphi_{d_i} }$
- $\varphi \leftarrow \min { \varphi_{u_i} + \alpha, \varphi_{d_i} + \alpha, \varphi } + ||I_{r_i} - I_{l_i}||$
- 否则,$\eta(t) \leftarrow 0$ 且 $\varphi \leftarrow \varphi$。
3. 最终处理 :$a \leftarrow I(d = 0)$。
4. 反向传播 :对于 $t = 2N - 2, 2N - 4, \cdots, 0$,
- $a_{u_o} \leftarrow a \cdot I(\eta(t) = 1)$
- $a_{d_o} \leftarrow a \cdot I(\eta(t) = -1)$
- $a \leftarrow a \cdot I(\eta(t) = 0) + a_{u_i} + a_{d_i}$

在初始化阶段,$d \neq 0$ 的 PE(d) 也被分配 $\eta = 0$,这简化了反向传播操作的逻辑。在反向传播期间,所有 PE 都会向总线输出 $\eta$,但只有一个 PE 输出所需的指针,其他 PE 的输出为零(线或逻辑)或三态(总线)。由于简单性的原因,我们更倾向于使用前者。在正向传播中,$\eta \in {1, 0, -1}$ 表示三个父节点 PE(d + 1)、PE(d) 和 PE(d - 1) 之一。搜索区域在上述方程定义的梯形 $R$ 内,梯形内未匹配的节点简单地保留其先前的成本和零指针。

对于 FBL,$I_r$ 和 $I_l$ 的角色在控制器中互换,但脉动阵列完全相同。该算法描述了整体方案,但在实际编码中还需要一些细节。接下来将解释 Verilog HDL 代码的整体框架。

FBR 和 FBL 的整体方案

电路的规格可以通过头文件 head.v 定义:

//image property
`define WIDTH 225 //image width (225)
`define HEIGHT 188 //image height (188)
//memory property
`define DATA_BITS 8 //word size
`define ADDR_BITS 20 //max image size (17)
`define LINES 3 //strip size -(L-1)/2, 0, (L-1)/2
`define CHANNELS 6 //buffer channel, CHANNEL =1,2,3,...
`define DMAX 32 //max disparity
//mode
//`define LEFT //right or left reference mode

通过头文件中的 LEFT 关键字,我们可以指定 FBR 和 FBL 两种模式之一。所有图像和视差规格由一个 $M \times N$ 图像、具有 L 行的三个缓冲区和视差级别 D 定义。可以有一个或多个通道,一个通道可能表示单色,而额外的通道可能表示 RGB 颜色或特征图。最大视差级别为 $D < N$。

主系统由两个子系统组成,一个用于控制(对应算法 13.4),另一个用于脉动元素(对应算法 13.4)。控制部分除了脉动控制外,还包括缓冲区管理、读写等额外任务。系统的状态和连接由状态图(图 13.14)所示。控制从缓冲区移位开始,阵列从等待状态开始。三个缓冲区(左图像、右图像和视差缓冲区)向上移位,在每个缓冲区的底部提供一个空行。在下一个状态中,空行被来自外部 RAM 的数据填充。尽管缓冲区底部的数据是新的,但要处理的数据位于缓冲区的中心。这种安排的目的是便于进行邻域处理,在这种方案中,缓冲区中心的像素可以与其邻域分组。

两个系统仅在 DP 计算(初始化、正向传播、最终处理和反向传播)中完全同步,在其他时期则解耦。这种同步由一个信号量激活,该信号量调用空闲的处理器。在初始化状态下,计算起始节点的成本。接下来是正向传播,递归地计算成本和指针,并将指针写入指针矩阵。当正向传播在最终像素位置结束时,开始最终处理过程。在 BF 电路中,这个阶段最简单,因为具有最小成本的节点已经已知。从那里开始,反向传播开始,从指针矩阵中读取指针。当计算到达终点时,重新开始并读取下一行图像。对于实时处理,循环中的计算必须在光栅扫描间隔内完成。

以下是用于实现控制单元的 processor.v 代码框架:

`include 'head.v'
module processor(
    //DP stereo processor
    input clock, reset,
    output reg [`ADDR_BITS - 1:0] i_raddr, r_raddr, r_waddr, //address bus
    input [`DATA_BITS - 1:0] i_rdata1, i_rdata2, r_rdata, //data bus
    output reg [`DATA_BITS - 1:0] r_wdata, //data bus
    output reg r_wen //write enable
);
    //working array: window of images
    reg [`DATA_BITS - 1:0] img1 [-((`LINES-1)>>1):(`LINES-1)>>1] [0: `CHANNELS* `WIDTH - 1]; //1st image
    reg [`DATA_BITS - 1:0] img2 [-((`LINES-1)>>1):(`LINES-1)>>1] [0: `CHANNELS* `WIDTH - 1]; //2nd image
    reg [`DATA_BITS - 1:0] res [-((`LINES-1)>>1):(`LINES-1)>>1] [0: 3* `WIDTH - 1]; //disparity map
    //variables
    reg [`ADDR_BITS - 1:0] k, K, idx, idx1,idx2; //variables
    reg [9:0] row; //pointer
    reg signed [9:0] J; //row in an image
    reg [2:0] state; //state variables
    reg [15:0] count; //count variable
    reg run; //activate array
    reg signed [7:0] disparity; //inputs to the systolic array
    reg [0:`CHANNELS*`DATA_BITS - 1] left_image, right_image;
    //main part for the systolic system
    always @ (posedge clock) begin: PROCESSING
        if (reset) begin
            //initialize
            state <= 3'b000; //global state
            row <= 0;
            k <= 0; //pixel address
            count <= 10'h0;
            run <= 1'b0;
        end
        else begin: MAIN
            case (state)
                //state machine
                3'b000: begin: BUFFER //buffer management
                end
                3'b001: begin: READING //RAM reading
                end
                3'b010: begin: PREPROCESSING
                end
                3'b011: begin: INITIALIZATION //fill the array
                end
                3'b100: begin: FORWARD //forward processing
                end
                3'b101: begin: FINALIZATION //finalization
                end
                3'b110: begin: BACKWARD //backward processing
                end
                3'b111: begin: WRITE //RAM writing
                end
                default: state <= 3'b000;
            endcase
        end //MAIN
    end //PROCESSING
    //connection nets for the array
    wire [`CHANNELS*`DATA_BITS-1:0]up[0:`DMAX],down[0:`DMAX];//image net
    wire [7:0] upcost[0:`DMAX],downcost[0:`DMAX]; //cost net
    wire upactive[0:`DMAX], downactive[0:`DMAX]; //active flag net
    wire signed [1:0] disp[0:`DMAX - 1]; //pointer net
    wor signed [1:0] dispbus; //pointer bus
    //feed the systolic array with the two image streams
    `ifdef LEFT //left reference
        assign up[0] = left_image; //upward image
        assign down[`DMAX] = right_image; //downward image
    `else //right reference
        assign up[0] = right_image; //upward image
        assign down[`DMAX] = left_image; //downward image
    `endif
    //boundary conditions for the top and bottom PEs
    assign upcost[0] = 8'hFF; //top boundary cost
    assign downcost[`DMAX] = 8'hFF; //bottom boundary cost
    assign upactive[0] = 0; //bottom boundary active bit
    assign downactive[`DMAX] = 0; //top boundary active bit
    //build a linear network of PE arrays by instantiation
    genvar varx; //systolic array
    generate
        for (varx = 0; varx < `DMAX; varx = varx + 1) begin: SYSTOLIC_ARRAY
            systolic #(varx) SYSTOLIC (
                //array ID
                .clock(clock),
                .reset(reset),
                .upin(up[varx]), //image upstream link
                .upout(up[varx + 1]),
                .downin(down[varx+1]), //image downstream link
                .downout(down[varx]),
                .upcostin(upcost[varx]), //cost upstream link
                .upcostout(upcost[varx+1]),
                .downcostin(downcost[varx+1]), //cost downstream link
                .downcostout(downcost[varx]),
                .upactivein(upactive[varx]), //active bit upstream link
                .upactiveout(upactive[varx+1]),
                .downactivein(downactive[varx+1]), //active bit downstream link
                .downactiveout(downactive[varx]),
                .disp(disp[varx]), //pointer output (tri-state)
                .run(run) //control signal
            );
        end
    endgenerate
    //combine the outputs of the systolic array to obtain a pointer
    genvar p; //00, 01, 10, or ZZ
    for (p =0; p < `DMAX; p = p + 1) begin: BUS
        assign dispbus = disp[p]; //wired-OR D pointers
    end
    //0 or 1 bit.
endmodule

从概念上讲,这段代码实现了算法 13.4,由程序块、实例化、输入和输出四部分组成。代码实例化了脉动阵列 SYSTOLIC,将各个处理元素连接到网络数组。通过这些连接,图像($I_l$ 和 $I_r$)、成本($\varphi$)和激活位($a$)的数据流向上和向下流动,使元素能够从两个邻居接收必要的数据,并将处理后的数据发送给两个邻居。需要注意的是,阵列顶部和底部的处理器缺少一个邻居,因此需要为缺失邻居的数据流提供合适的值:$\varphi = \infty$ 和 $a = 0$,这样终端处理器就可以执行与其他处理器相同的邻域操作。控制部分还充当驱动器,向阵列提供两个方向的图像流。在 FBR 中,上游是右图像,下游是左图像;在 FBL 中,两个图像的角色相反。最后,输出是阵列的指针,在 D 个元素中,只有一个元素输出有效的指针(一个两位二进制数),其他元素输出三态或零。为了检测有效指针,我们可以通过线或操作组合输出线。

处理元素的代码 systolic.v 实现了算法 13.5,该代码由控制部分实例化形成一个由 D 个元素组成的阵列。每个元素仅根据时间和处理器 ID 执行相同的操作,ID 由实例化分配,时间由同步时钟确定,无需额外的握手操作,避免了不必要的通信开销。系统就像一个引擎,所有操作都由时钟和处理器标识预先确定。

`include head.v
module systolic #(parameter ID = 0) (
    //processing element ID
    input clock, reset,
    input [`CHANNELS*`DATA_BITS - 1:0] upin, //image upstream input
    output reg [`CHANNELS*`DATA_BITS - 1:0] upout,//image upstream output
    input [`CHANNELS*`DATA_BITS - 1:0] downin, //image downstream input
    output reg [`CHANNELS*`DATA_BITS - 1:0] downout, //image down output
    input [7:0] upcostin, //cost upstream input
    output [7:0] upcostout, //cost upstream output
    output [7:0] downcostout, //cost downstream output
    input [7:0] downcostin, //cost downstream input
    input upactivein, //active upstream output
    output upactiveout, //active upstream output
    input downactivein, //active down input
    output downactiveout, //active down output
    output signed [1:0] disp, //pointer output
    input run //control input
);
    reg [2:0] state; //state variable
    reg [9:0] count; //count variable
    reg signed [1:0] queue [0:2*(`WIDTH - 1)]; //pointer array
    reg [7:0] cost, costp; //child parent costs
    wire [7:0] ldistance; //local distance
    reg active; //activity bit
    //state machine
    always @(posedge clock) begin
        if (reset) begin
            state <= 3'b000;
            count <= 10'h0;
        end
        else case (state)
            3'b000: begin: IDLE //wait for activation
                if (run) begin state <= 3'b001; //start processing
                else state <= 3'b000; //idling
                end
            3'b001: begin: INITIALIZATION //pass the image streams
            end
            3'b010: begin: FORWARD_PASS //forward processing
            end
            3'b011: begin: FINALIZATION //define the start point
            end
            3'b100: begin: BACKWARD_PASS //backward processing
            end
            default: state <= 3'b000;
        endcase
    end

综上所述,通过对脉动阵列在立体匹配中的应用进行深入分析,我们详细了解了其控制、操作、电路平台以及相关算法和代码实现。这些技术为立体匹配的高效计算提供了有力的支持。在实际应用中,我们可以根据具体需求选择合适的模式(FBR 或 FBL),并通过相应的代码实现来完成立体匹配任务。同时,我们也可以进一步优化代码和算法,以提高系统的性能和效率。例如,可以考虑对缓冲区管理进行优化,减少数据读写的延迟;或者对计算过程进行并行化处理,加快计算速度。此外,还可以探索更多的应用场景,将脉动阵列技术应用到其他领域,如机器人视觉、自动驾驶等。

用于立体匹配的脉动阵列技术解析

代码详解与优化思路

在上述代码中, processor.v systolic.v 分别实现了控制单元和处理元素的功能。下面我们对这两个代码文件进行更详细的分析,并探讨一些可能的优化思路。

processor.v 代码分析

processor.v 主要负责系统的整体控制,包括缓冲区管理、数据读写、脉动阵列的驱动等。代码中的状态机根据不同的状态执行相应的操作,具体状态如下:
| 状态编码 | 状态名称 | 操作描述 |
| ---- | ---- | ---- |
| 3’b000 | BUFFER | 缓冲区管理,负责缓冲区的移位操作 |
| 3’b001 | READING | 从外部 RAM 读取数据 |
| 3’b010 | PREPROCESSING | 图像预处理 |
| 3’b011 | INITIALIZATION | 初始化脉动阵列 |
| 3’b100 | FORWARD | 正向传播处理 |
| 3’b101 | FINALIZATION | 最终处理 |
| 3’b110 | BACKWARD | 反向传播处理 |
| 3’b111 | WRITE | 将结果写入外部 RAM |

在代码中,通过 generate 语句实例化了多个脉动阵列元素,并将它们连接成一个线性网络。同时,使用 assign 语句为阵列的输入输出端口赋值,确保数据的正确流动。为了处理边界条件,对顶部和底部的处理器设置了特殊的输入值($\varphi = \infty$ 和 $a = 0$)。

优化思路:
- 缓冲区管理优化 :可以采用双缓冲技术,减少数据读写的等待时间。在一个缓冲区进行数据处理的同时,另一个缓冲区可以进行数据的读取或写入操作,提高系统的并行性。
- 并行计算 :对于一些独立的计算任务,可以考虑使用并行处理技术,例如在正向传播和反向传播过程中,多个处理元素可以同时进行计算,加快计算速度。

systolic.v 代码分析

systolic.v 实现了单个处理元素的功能,通过状态机控制元素的不同操作阶段。状态机的状态如下:
| 状态编码 | 状态名称 | 操作描述 |
| ---- | ---- | ---- |
| 3’b000 | IDLE | 等待激活信号 |
| 3’b001 | INITIALIZATION | 传递图像数据流 |
| 3’b010 | FORWARD_PASS | 正向传播处理 |
| 3’b011 | FINALIZATION | 定义起点 |
| 3’b100 | BACKWARD_PASS | 反向传播处理 |

在代码中,使用 queue 数组存储指针,根据不同的状态更新成本、指针和活动位。处理元素根据时钟信号和自身的 ID 执行相应的操作,无需额外的握手信号。

优化思路:
- 指针存储优化 :可以考虑使用更高效的数据结构来存储指针,例如链表或哈希表,减少指针访问的时间复杂度。
- 状态机优化 :对状态机的逻辑进行优化,减少不必要的状态跳转,提高状态机的执行效率。

脉动阵列技术的应用拓展

脉动阵列技术在立体匹配领域具有显著的优势,其高效的并行计算能力和低通信开销使得它在实时处理方面表现出色。除了立体匹配,脉动阵列技术还可以应用于其他领域,以下是一些可能的应用拓展:

机器人视觉

在机器人视觉中,需要实时处理大量的图像数据,以实现目标检测、定位和导航等功能。脉动阵列技术可以用于加速图像特征提取、匹配和三维重建等任务,提高机器人的视觉处理能力和反应速度。

操作步骤:
1. 数据采集 :使用摄像头等设备采集机器人周围的图像数据。
2. 特征提取 :利用脉动阵列对图像进行特征提取,例如 SIFT、SURF 等特征点的提取。
3. 特征匹配 :在不同图像之间进行特征匹配,确定目标的位置和姿态。
4. 三维重建 :根据匹配结果进行三维重建,生成机器人周围环境的三维模型。

自动驾驶

自动驾驶系统需要实时处理车辆周围的图像和传感器数据,以做出安全可靠的决策。脉动阵列技术可以用于加速目标检测、车道线识别和障碍物避障等任务,提高自动驾驶系统的性能和安全性。

操作步骤:
1. 数据采集 :使用摄像头、雷达等传感器采集车辆周围的图像和距离数据。
2. 目标检测 :利用脉动阵列对图像进行目标检测,识别出车辆、行人、障碍物等目标。
3. 车道线识别 :通过图像处理和分析,识别出车道线的位置和方向。
4. 决策规划 :根据目标检测和车道线识别的结果,做出决策规划,例如加速、减速、转弯等。

总结与展望

脉动阵列技术在立体匹配以及其他相关领域具有巨大的应用潜力。通过对脉动阵列的控制、操作、电路平台以及相关算法和代码的深入研究,我们可以实现高效的立体匹配系统。在实际应用中,我们可以根据具体需求选择合适的模式(FBR 或 FBL),并通过优化代码和算法来提高系统的性能和效率。

未来,随着硬件技术的不断发展和算法的不断创新,脉动阵列技术有望在更多领域得到应用。例如,在人工智能、深度学习等领域,脉动阵列可以用于加速神经网络的计算,提高模型的训练和推理速度。同时,我们也需要进一步研究如何更好地利用脉动阵列的并行计算能力,开发出更加高效、灵活的算法和系统。

此外,为了推动脉动阵列技术的广泛应用,还需要解决一些技术挑战。例如,如何降低脉动阵列的功耗、如何提高系统的可扩展性等。通过不断地研究和实践,我们相信脉动阵列技术将为计算机视觉和其他相关领域带来更多的创新和突破。

以下是脉动阵列系统的整体工作流程 mermaid 流程图:

graph TD;
    A[开始] --> B[缓冲区移位];
    B --> C[读取数据];
    C --> D[预处理];
    D --> E[初始化脉动阵列];
    E --> F[正向传播];
    F --> G[最终处理];
    G --> H[反向传播];
    H --> I[写入结果];
    I --> J{是否完成所有行};
    J -- 否 --> B;
    J -- 是 --> K[结束];

通过这个流程图,我们可以清晰地看到脉动阵列系统的整体工作流程,从缓冲区管理到数据处理,再到结果输出,各个环节紧密相连,协同工作。希望本文对读者理解和应用脉动阵列技术有所帮助,激发更多的研究和创新。

先展示下效果 https://pan.quark.cn/s/a4b39357ea24 遗传算法 - 简书 遗传算法的理论是根据达尔文进化论而设计出来的算法: 人类是朝着好的方向(最优解)进化,进化过程中,会自动选择优良基因,淘汰劣等基因。 遗传算法(英语:genetic algorithm (GA) )是计算数学中用于解决最佳化的搜索算法,是进化算法的一种。 进化算法最初是借鉴了进化生物学中的一些现象而发展起来的,这些现象包括遗传、突变、自然选择、杂交等。 搜索算法的共同特征为: 首先组成一组候选解 依据某些适应性条件测算这些候选解的适应度 根据适应度保留某些候选解,放弃其他候选解 对保留的候选解进行某些操作,生成新的候选解 遗传算法流程 遗传算法的一般步骤 my_fitness函数 评估每条染色体所对应个体的适应度 升序排列适应度评估值,选出 前 parent_number 个 个体作为 待选 parent 种群(适应度函数的值越小越好) 从 待选 parent 种群 中随机选择 2 个个体作为父方和母方。 抽取父母双方的染色体,进行交叉,产生 2 个子代。 (交叉概率) 对子代(parent + 生成的 child)的染色体进行变异。 (变异概率) 重复3,4,5步骤,直到新种群(parentnumber + childnumber)的产生。 循环以上步骤直至找到满意的解。 名词解释 交叉概率:两个个体进行交配的概率。 例如,交配概率为0.8,则80%的“夫妻”会生育后代。 变异概率:所有的基因中发生变异的占总体的比例。 GA函数 适应度函数 适应度函数由解决的问题决定。 举一个平方和的例子。 简单的平方和问题 求函数的最小值,其中每个变量的取值区间都是 [-1, ...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值