ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于SAD模板匹配的FPGA实时目标跟踪系统设计与实现

基于SAD模板匹配的FPGA实时目标跟踪系统设计与实现 这篇文章聊聊我最近完成的一个FPGA目标跟踪项目基于SAD模板匹配算法实现目标坐标的实时输出。SAD全称Sum of Absolute Differences中文叫绝对差值和是模板匹配里最基础、也最适合硬件实现的一类相似度度量。项目里我把一路640×48060fps的视频流送进Xilinx Artix-7系列FPGA模板匹配引擎全程不经过CPU、也不依赖外部DDR图像像素从传感器一出来就直接进入匹配流水线最终在输出端拿到目标中心坐标。整个系统综合后稳定跑在100MHz以上单帧跟踪延迟做到微秒级CPU全程没有参与任何图像计算。这个项目适合两类人参考一是刚接触FPGA图像处理想知道“模板匹配这种算法到底怎么落到硬件上”的开发者二是已经在用CPU或HALCON这类工具做视觉检测但对实时性不满、想评估FPGA方案的工程师。下面我会把从算法复杂度估算、硬件架构设计、流水线切分到跟踪策略和实际踩坑的完整链路都展开讲尽量把每个“为什么这么做”都说清楚。1. 从“摄像头进、坐标出”说起为什么目标跟踪会选择FPGA1.1 实时视频处理的时间预算先算一笔最常见的账。假设视频是VGA分辨率640×48060fps一帧的可用处理时间只有16.7ms。如果目标模板是8×8像素、搜索区域是40×40像素那么候选位置有(40-81)²1089个每个候选位置要算64次减法和63次加法单目标的总计算量在二十万次运算级别。听起来不多对吧但真用CPU做起来问题往往不是算力不够而是数据搬移太慢。摄像头数据要经过MIPI或DVP接口进到主控主控再通过DMA搬到内存CPU再从内存取回来做匹配。这一圈下来纯运算可能只要几百微秒但帧延迟轻松到几十毫秒。更麻烦的是一旦目标数量增加到四五个、搜索范围再扩大到半搜索半径32像素CPU端的帧率立刻断崖式下跌。FPGA解决的是另一件事像素从传感器出来之后根本不落地直接流进匹配引擎。数据在哪一拍的寄存器里、参与哪一拍的运算全部在硬件里固定好。这种“边进边算”的流式处理理论上能做到每个时钟周期处理一个像素而且延迟是确定的、可预测的。1.2 CPU和FPGA处理模板匹配的本质差异CPU是“取指-译码-执行”的冯诺依曼结构一条指令处理一个数据虽然流水线也能叠加但本质上还是串行思维。GPU虽然并行能力强但延迟高、功耗大还需要主机配合。FPGA呢本质上是把算法直接“画”成了硬件电路64个绝对差运算可以同时做加法树可以逐级展开比较器可以每个周期刷新最优值。平台并行方式典型延迟开发难度功耗CPU多核指令流水几十毫秒级低中等DSP定点指令并行毫秒级中低GPU大规模SIMT十几毫秒级中高高FPGA空间并行流水线微秒级高低在工业视觉、无人机视觉里几十毫秒的延迟往往直接决定系统能不能用。举个例子目标在画面里以每帧20像素的速度运动30fps下帧间目标移动了约20像素如果你的处理延迟是50ms那目标已经移动了一个身位控制回路根本没法定点。FPGA的微秒级延迟让“当前帧目标位置”真正是当前帧的而不是三帧之前的。1.3 系统整体框架我把整个系统分成三条数据通路视频采集通路、模板匹配通路、结果输出通路。视频采集通路用OV5640摄像头模组通过DVP接口把YUV或RGB数据送进FPGA。模板匹配通路就是SAD引擎的核心包含行缓冲Line Buffer、滑动窗口寄存器阵列、绝对差计算单元、加法树、最小值比较器。结果输出通路通过一个简单的并行总线比如STM32主控的FMC接口把目标坐标寄存器暴露给外部MCU读取。这里要特别强调一个设计选择我没有用帧缓存方案。全图匹配才需要把整帧存在DDR里局部搜索只需要行缓冲就能实现。省掉DDR意味着系统架构简单一个数量级不用处理DDR控制器的突发、刷新、时序收敛问题这在很多小型化项目里是决定性优势。2. SAD的硬件翻译绝对差、加法树和位宽计算2.1 SAD公式里的三个动作SAD的定义很简单SAD(x, y) Σ|I(xi, yj) - T(i, j)|就是把模板T逐像素挪到图像I的某个位置每个对应像素做差、取绝对值、再求和。这个公式里只有三个动作减法、取绝对值、累加。很多人觉得这太简单了不就是三个操作循环吗但放到硬件里每个动作都要重新想一遍。减法器在FPGA里是直接可用的数字逻辑但是两个8bit数相减结果可能是负数而硬件里没有“负数”的概念只有补码。取绝对值也不是白送的它本质上是根据符号位做一个“取反加一”操作。最后累加更不能串行做否则64个周期才能算完一个位置整个流水线全废了。2.2 绝对差单元的Verilog实现绝对差单元是整个引擎里最基础的细胞。最直观的写法是wire [7:0] pixel_img, pixel_tpl; wire [7:0] abs_diff (pixel_img pixel_tpl) ? (pixel_img - pixel_tpl) : (pixel_tpl - pixel_img);这段代码综合出来实际上是一个8bit比较器加上两个8bit减法器再加一个二选一多路器。如果你嫌组合逻辑多也可以用补码求绝对值的方式wire [8:0] diff_signed {1b0, pixel_img} - {1b0, pixel_tpl}; wire [7:0] abs_diff diff_signed[8] ? (~diff_signed[7:0] 1b1) : diff_signed[7:0];两种写法综合结果差不多第一种更直观第二种更接近底层原理。实际工程里我建议用第一种可读性好综合工具自己会优化。如果模板是8×8就需要例化64个这样的绝对差单元。这些单元在同一时刻工作输入是滑动窗口里的64个像素和模板寄存器里的64个像素。这里就体现了FPGA的“空间并行”优势64个减法器同时做不需要排队。2.3 用加法树替代串行累加拿到64个绝对差之后如果按CPU的习惯写一个for循环串行累加在硬件里综合出来的就是一条63级的加法链每个周期只能算完一个累加步骤时钟频率会低得可怜。正确做法是用树形结构第1级32个加法器第2级16个第3级8个第4级4个第5级2个第6级1个总共63个加法器但逻辑深度只有6级。// 示意64路绝对差 - 加法树 - 14bit SAD结果 // 中间各级务必插入流水寄存器这里省略寄存器写法 reg [8:0] sum_l1 [0:31]; // 第1级输出 reg [9:0] sum_l2 [0:15]; reg [10:0] sum_l3 [0:7]; reg [11:0] sum_l4 [0:3]; reg [12:0] sum_l5 [0:1]; reg [13:0] sad_out;每一级对应一组加法器组合逻辑深度只有2~3个加法器加上流水寄存器后关键路径非常短时序很容易收敛。这也是为什么一个8×8模板的SAD引擎在100MHz甚至200MHz下都能稳定跑的原因。2.4 位宽怎么算才不翻车位宽计算是新手最容易忽略的地方。8bit像素值范围0~255差的绝对值最大是255需要8bit。64个绝对差相加最大和是64×255163202^1416384所以至少需要14bit保险起见建议用15bit。如果你把模板放大到16×16256×255652802^16刚好够但再加几个模板或做多帧累加16bit就会溢出。我的习惯是每级加法树都多留1bit余量让综合工具自己去优化省得后期为了一个溢出问题返工。还要提醒一个反直觉的坑比较SAD大小时千万别把结果声明成有符号数。如果最小SAD比较器的初值是0而有符号数最小值是个负数那你会发现目标位置永远不更新——我后面讲到实测踩坑时会详细展开。3. 数据流架构行缓冲、滑动窗口、搜索控制与流水线3.1 行缓冲让N-1行数据陪跑SAD引擎面临的核心问题不是“怎么算”而是“数据怎么到对的地方去”。如果图像数据是一行行流进来的要构建一个8×8的滑动窗口就必须同时拿到当前行的数据以及前7行的数据。把整帧存进DDR再去读当然可以但对于局部搜索来说完全没有必要——只需要行缓冲就够了。行缓冲的本质上是一组同步FIFO每个FIFO缓存一行图像数据。对于一个8×8窗口需要7行历史数据N-1当前行直接来自摄像头接口。实现方式有两种一种是用BRAM写一个自定义的行缓存模块另一种是用Xilinx的Shift Register IP。VGA宽度6408bit像素单行数据量只有640×8bit用BRAM非常宽裕。我这边例化了7个BRAM每个深度640宽度8bit。module line_buffer #( parameter DATA_WIDTH 8, parameter LINE_WIDTH 640 )( input wire clk, input wire rst_n, input wire wr_en, input wire [DATA_WIDTH-1:0] din, output wire [DATA_WIDTH-1:0] line_out ); reg [DATA_WIDTH-1:0] mem [0:LINE_WIDTH-1]; reg [$clog2(LINE_WIDTH)-1:0] wr_ptr; // 写入侧每个有效像素使能写入 always (posedge clk or negedge rst_n) begin if (!rst_n) begin wr_ptr 0; end else if (wr_en) begin mem[wr_ptr] din; wr_ptr (wr_ptr LINE_WIDTH-1) ? 0 : wr_ptr 1; end end assign line_out mem[wr_ptr]; endmodule这里有个细节值得注意首次写入整行之前行缓冲里是无有效数据的需要给一个“数据有效”信号贯穿整个流水线否则前几帧的SAD结果是垃圾数据会干扰模板匹配。3.2 滑动窗口与坐标对齐行缓冲输出的7行历史数据加上当前行的新像素共同组成一个8×8的窗口寄存器阵列。每来一个新像素窗口整体左移一列最右边一列填入新数据。这相当于一个二维的移位寄存器。// 滑动窗口寄存器img_window[row][col] always (posedge clk) begin for (int r 0; r 8; r) begin for (int c 0; c 7; c) begin img_window[r][c] img_window[r][c1]; end end // 最右侧一列由行缓冲和当前像素填充 img_window[0][7] pixel_in; // 当前行 for (int r 1; r 8; r) begin img_window[r][7] line_buf_out[r-1]; // 历史行 end end这里最容易出错的是窗口数据和当前候选位置坐标没对齐。你想图像数据从输入到窗口需要经过7级行缓冲延迟而x/y坐标计数器如果直接从像素有效信号开始计数那么计数到某个位置时窗口里装的其实是延迟了7行之后的数据坐标自然就偏移了。解决方案很简单坐标计数器和窗口数据走同一条延迟链保证“哪个周期的窗口数据就对应该周期的坐标”。3.3 局部搜索的控制时序全图搜索在这个项目里没必要。目标跟踪的语义就是“上一帧在哪里这一帧大概率还在附近”所以在上一帧目标中心周围划一个搜索区域即可。我的配置是模板8×8、搜索半半径16搜索区域就是40×40。控制逻辑的关键状态有几个左上角起始坐标、搜索范围边界、行列计数器。当像素坐标落在搜索区域左上角时启动窗口数据有效标志允许SAD引擎开始计算。每次窗口滑动一个像素SAD引擎算出一个候选位置的SAD值送入最小比较器。当坐标到达搜索区域右下角时本轮搜索结束把当前最优坐标锁存到输出寄存器并复位最小SAD值。这个逻辑看似简单但要特别小心“搜索区域边界”的处理。如果搜索区域左边界是30右边界是70模板宽度是8那么当窗口中心位于67时窗口的最右列已经需要图像x74处的像素。此时如果摄像头数据还没喂到74SAD结果就是无效的。我的做法是把搜索区域的SAD计算范围限制为“窗口完全落在图像内”的区域也就是左右各留出模板宽度的一半作为安全区。3.4 流水线怎么切流水线切分是FPGA算法设计里最核心的步骤。SAD计算链路包括绝对差、加法树6级、比较器如果全部不切流水组合逻辑延迟可能达到十几ns甚至几十ns100MHz时钟直接爆掉。我的切法是这样的第0级流水寄存器输入口窗口寄存器输出、模板寄存器输出。第1级流水64个绝对差单元的输出打入寄存器。第2、3级流水加法树前两级的输出分别打拍。第4、5级流水加法树后两级的输出打拍。第6级流水最终SAD值打入寄存器。第7级流水与历史最小值比较更新最优坐标。这样任何一级寄存器到下一级寄存器之间组合逻辑最多不超过两三个加法器或一个比较器。时序收敛非常轻松。代价是SAD结果比像素输入晚了7个周期但这7个周期在搜索范围内完全可接受只要在比较器里对齐好节拍就行。// 流水级示意 always (posedge clk) begin abs_diff_reg abs_diff_comb; // 第1级 tree_l1_reg abs_diff_reg; // 第2级加法树第1层 tree_l2_reg tree_l1_reg; tree_l3_reg tree_l2_reg; sad_result tree_l3_reg; // 第6级 end3.5 最优位置追踪模块SAD引擎计算出每个候选位置的SAD值之后还需要一个“幽灵比较器”来记住我们看到过的最小值和它的坐标。这个模块非常小但往往是整个工程里埋坑最多的地方。reg [13:0] min_sad; reg [9:0] best_x, best_y; // 每轮搜索开始时复位 if (search_start) begin min_sad {14{1b1}}; // 全1表示无穷大 best_x 0; best_y 0; end else if (sad_valid (sad_result min_sad)) begin min_sad sad_result; best_x cand_x; best_y cand_y; endmini_sad的初值设成全1而不是0很多人第一次写都会犯这个错。为什么因为如果初值是0每一个新算出来的SAD都是正数永远不可能小于0那么best_x和best_y就永远不被更新目标坐标永远锁在初始位置。这个坑我后面细说。4. 从匹配到跟踪模板更新、卡尔曼滤波和目标丢失恢复4.1 模板不更新会怎样SAD匹配本身只是一帧的“穷举”要变成“跟踪”得把时间维的连续性用起来。最简单也最容易犯的错就是模板在第一帧提取后永远不变。目标在画面里一旦发生旋转、尺度变化、光照变化SAD值很快会突破阈值匹配位置开始随机跳最后直接丢目标。反过来如果每帧都更新模板又会出现另一个问题——误差累积。某帧匹配位置偏了一两个像素模板里就混入了一部分背景下一帧模板更像背景了偏得更远几个循环之后模板彻底漂移到背景上再也回不来。所以模板更新不能走极端。我的方案是“固定模板动态模板”混合策略。4.2 动态模板与混合模板策略固定模板负责“拉住”目标的原始特征动态模板负责“跟上”目标的外观变化。每一帧同时用两个模板做SAD得到两个最小值然后用一个加权公式融合得分score α × SAD_fixed (1-α) × SAD_dynamicα取0.4到0.6之间比较稳我最终用了0.5。融合得分的最优位置才作为当前帧的目标坐标输出。动态模板的更新规则是只有当本帧融合得分小于上一帧融合得分的1.6倍时才把当前帧的最优匹配区域更新为新的动态模板超过这个比例就说明当前匹配置信度低大概率发生了遮挡或剧烈变化本帧冻结动态模板。这样遮挡时不会把遮挡物学进模板目标重新出现后还能找回来。4.3 一个工程化的卡尔曼滤波实现SAD匹配输出的坐标其实是有噪声的尤其是目标边缘纹理复杂时匹配位置会在几个像素之间抖。要获得平滑稳定的轨迹需要给跟踪结果加一个滤波器。完整的多维卡尔曼滤波在FPGA里实现起来开销不小矩阵求逆更是麻烦但好在目标跟踪这个场景里我们完全可以退一步用常增益卡尔曼滤波器也就是α-β滤波器它本质上就是稳态卡尔曼。系统模型很简单状态向量是位置和速度[x, vx, y, vy]观测是SAD给出的[x_obs, y_obs]。预测方程x_pred x_est vx_est × dt vx_pred vx_est更新方程x_est x_pred α × (x_obs - x_pred) vx_est vx_pred (β / dt) × (x_obs - x_pred)y方向同理。α和β是一组常系数α取0.6β取0.15在多数场景下效果都够用。FPGA里用16bit定点数实现不会占用太多资源。我单独建了一个小模块输入是SAD匹配的坐标输出是平滑后的坐标和预测的下一帧中心位置预测结果正好可以作为下一帧搜索区域的开窗中心形成闭环。工程上有两点经验一是预测值要限制在图像边界内否则搜索区域跑到画面外去SAD引擎会白算好几行二是当目标丢失判定触发时卡尔曼滤波器要暂停预测或者把过程噪声调大否则它会按惯性一直往外飞。4.4 丢失判定与重捕获状态机目标有没有丢最直接的信号就是最小SAD的大小。目标稳定跟踪时我的系统里最小SAD通常在2000~3000之间一旦目标被完全遮挡或者跑出搜索区域最小SAD会瞬间跳到10000以上。我把丢失阈值设在6000但这个值不是拍脑袋定的而是先跑了一段正常跟踪把正常情况下的最小SAD记录下来取了2到3倍作为阈值。丢目标之后的处理我用了一个简单的状态机TRACKING正常输出跟踪坐标。PREDICT按照卡尔曼预测位置继续开窗搜索搜索半径暂时扩大1.2倍。REACQUIRE如果PREDICT状态里连续3帧最小SAD都超阈值就切换到全图扫描模式每隔几帧对全图做一次粗匹配试图找回目标。找回目标后重新初始化模板和滤波器回到TRACKING。这个状态机在FPGA里不过几十行代码但让整个跟踪器从“一次性的匹配器”变成了“有记忆的跟踪系统”。5. 实测数据与四个让我熬夜的坑5.1 平台与参数项目配置FPGAXilinx Artix-7 XC7A35T视频输入OV5640VGA 640×480 60fps模板尺寸8×8搜索半半径16像素搜索区域40×40工作时钟100MHz输出接口并行总线FMC类协议给外部MCU实测单帧从像素输入到坐标输出的延迟约18μs处理能力在60fps的输入下还剩十倍以上余量。系统连续跑了30分钟室内场景目标用50像素/秒的速度移动全程稳定跟踪遮挡1.5秒后能自动重捕获。这些指标在CPU方案里很难做到。5.2 资源占用与关键路径资源用量占比LUT412620%Flip-Flop32898%BRAM9块18%DSP48E112个13%综合后最高时钟127MHz100MHz设计稳定收敛。加法树前两级用了少量DSP48来做预加器主要是为了把关键路径压缩得更短实际上纯LUT也能实现只是时序会紧张一点点。5.3 避坑一min_sad初值导致目标“冻结”这个问题困扰了我整整一个下午。现象极其诡异第一帧模板匹配完全正常能从全图里找到目标但到了第二帧目标坐标就再也不动了像被冻住一样。排查状态寄存器发现min_sad里存的值始终是第一帧算出的最小SAD。为什么因为我在搜索周期开始时没有把min_sad复位成全1而是保留了上一次的最小值。第二帧目标虽然没有大幅移动但因为光照或角度的微小变化所有候选位置的SAD都略大于第一帧的最小值导致“sad_result min_sad”永远不成立最优坐标自然也就永远不更新。修复就是三行代码的事每个搜索周期开始时把min_sad置为全1把best_x、best_y清零。但这件事给我留下的教训很深凡遇到“只有第一帧正常”的FPGA问题第一反应就去找所有寄存器的初值和复位逻辑不要急着翻算法代码。5.4 避坑二行缓冲节拍错位导致固定偏移另一个经典问题是跟踪位置总是固定在目标左上角偏移几个像素的位置。我当时在仿真里对比了窗口数据和坐标计数器发现窗口里第0列是当前像素窗口的“视觉中心”其实在第4列而坐标计数器给的是窗口左上角的位置差了半个模板的偏移。更隐蔽的是行缓冲带来的延迟。一个8×8窗口需要7行历史数据历史行数据输出和当前行数据在同一个时钟周期对齐看起来没问题但一旦行缓冲的读使能比写使能晚一拍整个窗口内容就会错位一行。这个问题在仿真里很难发现因为仿真激励数据太规则了我用ILA抓真实摄像头数据才看到“模板区域和预期位置存在恒定偏移”。最终的解决方案就是前面说的让数据有效信号贯穿所有流水级坐标计数器也跟随同样的延迟链打拍保证SAD结果对应的窗口坐标是真实的窗口中心坐标。写testbench的时候我专门加了一个打印任务逐周期比对“窗口中心坐标”和“候选位置坐标”确保整条链路对齐。5.5 避坑三动态模板漂移到背景动态模板方案跑了一段时间后出现过一个恶心的问题目标框慢慢滑出了目标钉在了旁边一块浅色背景上。原因是目标在某一帧被手挡了一下匹配位置偏了2个像素更新后的模板里混进了背景边缘下一帧基于这个“脏模板”去匹配最佳位置更偏了模板更脏如此恶性循环最后彻底漂移。修复方式就是前面提到的不要无条件更新模板当本帧SAD明显高于历史正常水平时冻结模板更新。我加的那个1.6倍系数就是反复调试出来的经验值太小了会在目标正常变化时冻结模板太大了又起不到抗漂移作用。如果你做类似项目可以从1.5到2.0之间试起。5.6 避坑四加法树不切流水时序直接崩第一次综合时我把64路绝对差和一个超级长的串行累加写在了同一个always块里结果综合报告里关键路径延迟有几十ns100MHz时钟根本满足不了。后来把加法树改成六级、每一级之间加流水寄存器之后关键路径立刻缩短到约7.8ns最高时钟跑到127MHz。这里有个判断技巧当关键路径超过时钟周期的60%时不要先急着改布局布线约束先回头看看是不是组合逻辑本身太深。SAD引擎里最深的组合逻辑路径就是“绝对差-加法树-比较器”这段路径必须用流水级打断。记住一句话宁可多打几拍不可堆逻辑。5.7 调试方法心得FPGA图像处理项目最忌讳直接拿摄像头实拍来调试因为输入数据不可控出了问题你根本分不清是摄像头时序的问题还是引擎逻辑的问题。我的做法是分三步走第一步用testbench生成固定的小尺寸图像比如16×16把SAD结果和MATLAB里用同样数据算出的SAD结果逐点比对。第二步把模板数据固定为寄存器里的常量不从摄像头提取先用任意图像跑通整个搜索流程确认坐标输出规律正确。第三步接真实摄像头用ILA抓窗口数据、SAD值和最优坐标重点观察边缘区域的坐标是否越界。这套流程下来绝大部分bug都能在仿真阶段解决实拍调试的时间能省一半以上。6. 还能往上叠什么多分辨率、亚像素、多目标与检测级联6.1 多分辨率金字塔搜索单尺度局部搜索的软肋是目标运动速度超过搜索半径时会丢。更好的做法是把图像降采样成1/4分辨率先在低分辨率上做大范围粗搜锁定大致位置后再回到原分辨率做小范围精搜。FPGA上实现一个2×2均值降采样非常便宜只需加一行累加器却能把等效搜索范围扩大好几倍同时计算量增加不到20%。这个方案我强烈推荐给要处理高速目标的场景。6.2 亚像素插值如果你需要目标坐标平滑到亚像素精度可以对最小SAD点以及它左右/上下的相邻SAD值做抛物线拟合。x方向的细分公式是subpixel_offset_x (SAD(x-1) - SAD(x1)) / (2 × (SAD(x-1) - 2×SAD(x) SAD(x1)))在FPGA里这个公式只需要减法、一次除法和几次移位定点数运算量不大。y方向同理。插值后跟踪位置不再是一个个整数像素在跳而是连续变化的对下游控制回路特别友好。注意除法器在FPGA里很贵最好用Newton-Raphson近似或者直接用查找表能省不少DSP。6.3 多目标并行引擎FPGA的一大优势是复用。一个SAD引擎大概占不到3000个LUT在XC7A035T这颗中端芯片上放4~6个引擎绰绰有余。每个引擎共享同一套行缓冲和窗口数据只需要各自的模板寄存器、SAD比较器和坐标追踪逻辑。这样就能同时跟踪四五个目标而CPU方案在同等资源下早就掉帧了。多目标场景里的模板分配也简单每个目标一个模板存储区状态机里加一个目标ID字段就行。6.4 与目标检测算法级联实际部署时模板匹配很少单独用它需要知道“第一帧的目标模板从哪来”。常见方案是CPU/GPU上跑检测网络YOLO、SSD之类检测到目标框之后把框的坐标和图像区域通过总线下发到FPGAFPGA截取对应区域作为初始模板之后所有跟踪运算全部在FPGA上完成。这种“检测在帧级、跟踪在像素级”的架构既利用了深度学习网络的高准确率又保住了FPGA的低延迟跟踪优势是我认为最有工程前景的落地形态。最后说一点个人体会FPGA上做算法最难的不是背公式而是把每一拍数据的来龙去脉想清楚。SAD模板匹配看起来是教科书里最简单的那一类算法但真正把它跑成一条微秒级延迟的实时数据流之后你对并行架构的理解会上一个台阶。做这个项目时我反复提醒自己的就是一句话硬件里没有“循环”只有“时刻”没有“变量”只有“寄存器”。想通了这一点路就好走了。
返回列表