高通8.1平台AB升级差分流程问题

Android9 ab系统OTA升级总结 Android9 ab系统OTA升级总结 OTA升级介绍 官方介绍 https://source.android.google.cn/devices/tech/ota/tools#multiple-skus 1.OTA升级包的制作 OTA升级有两种方式,全包升级和差分升级总体升级操作步骤类似 首先需要对代码做一些改动:如下在/build/core/Makefile中添加如下代码: /build/core/Makefile 2850 $(hide) PATH=$(foreach p,$(INTERNAL_ 阅读详情

   最近测试一个项目时高通8.1平台,每个版本做出来差分包有800M,因为版本中有个APK有700M,所以相当于这个APK没有走差分的流程,一开始有两个方面的猜想,第一种是做了限制,操过了一定大小的APK没有执行差分,第二种引入分割理论,将APK分割成小份进行差分

一、大致流程

二、问题来源

三、问题优化

四、问题探究

 

一、大致流程

这里不详细说,我们只看我们的单个问题,所以先不做具体分析

payload_generator/generate_delta_main.cc

GenerateUpdatePayloadFile(payload_config,FLAGS_out_file, FLAGS_private_key,&metadata_size)) {}

 

payload_generator/delta_diff_generator.cc

strategy->GenerateOperations(config,old_part,new_part,&blob_file,&aops)){}

 

payload_generator/ab_generator.cc

GenerateOperations( const PayloadGenerationConfig& config, const PartitionConfig& old_part, const PartitionConfig& new_part,BlobFileWriter* blob_file,vector<AnnotatedOperation>* aops){}

 

payload_generator/delta_diff_utils.cc

DeltaReadPartition(vector<AnnotatedOperation>* aops,const PartitionConfig& old_part,

 const PartitionConfig& new_part,ssize_t hard_chunk_blocks, size_t soft_chunk_blocks, const PayloadVersion& version, BlobFileWriter* blob_file) {}

 

DeltaReadFile(aops, old_part.path,new_part.path,old_unvisited, new_unvisited, "<non-file-data>",  

soft_chunk_blocks, version, blob_file)){}

 

ReadExtentsToDiff(old_part,new_part,old_extents_chunk,new_extents_chunk,version,&data,&operation)){}

 

这就是大致的方法调用顺序,目前大致的方法知道了,我们就先看这个问题如何进行排查

 

二、问题来源

首先呢,我把8.1的中间包放到了9.0的环境里面,发现做出的差分包时400M,而且apk还走差分了,那就不用说啥了,很明显payload_generator9.0处理的有优化的方法

对比了9.0 跟 8.0的做包log发现

 

  1payload_generator/delta_diff_utils.cc
  2
  3bool DeltaReadPartition(vector<AnnotatedOperation>* aops,
  4                        const PartitionConfig& old_part,
  5                        const PartitionConfig& new_part,
  6                        ssize_t hard_chunk_blocks,
  7                        size_t soft_chunk_blocks,
  8                        const PayloadVersion& version,
  9                        BlobFileWriter* blob_file) {
 10  ExtentRanges old_visited_blocks;
 11  ExtentRanges new_visited_blocks;
 12  //处理move 和zero的情况
 13  TEST_AND_RETURN_FALSE(DeltaMovedAndZeroBlocks(
 14      aops,
 15      old_part.path,
 16      new_part.path,
 17      old_part.size / kBlockSize,
 18      new_part.size / kBlockSize,
 19      soft_chunk_blocks,
 20      version,
 21      blob_file,
 22      &old_visited_blocks,
 23      &new_visited_blocks));
 24
 25  map<string, vector<Extent>> old_files_map;
 26  if (old_part.fs_interface) {
 27    vector<FilesystemInterface::File> old_files;
 28    old_part.fs_interface->GetFiles(&old_files);
 29    for (const FilesystemInterface::File& file : old_files)
 30      old_files_map[file.name] = file.extents;
 31  }
 32
 33  TEST_AND_RETURN_FALSE(new_part.fs_interface);
 34  vector<FilesystemInterface::File> new_files;
 35  new_part.fs_interface->GetFiles(&new_files);
 36
 37  vector<FileDeltaProcessor> file_delta_processors;
 38
 39  // The processing is very straightforward here, we generate operations for
 40  // every file (and pseudo-file such as the metadata) in the new filesystem
 41  // based on the file with the same name in the old filesystem, if any.
 42  // Files with overlapping data blocks (like hardlinks or filesystems with tail
 43  // packing or compression where the blocks store more than one file) are only
 44  // generated once in the new image, but are also used only once from the old
 45  // image due to some simplifications (see below).
 46  for (const FilesystemInterface::File& new_file : new_files) {
 47    // Ignore the files in the new filesystem without blocks. Symlinks with
 48    // data blocks (for example, symlinks bigger than 60 bytes in ext2) are
 49    // handled as normal files. We also ignore blocks that were already
 50    // processed by a previous file.
 51    vector<Extent> new_file_extents = FilterExtentRanges(
 52        new_file.extents, new_visited_blocks);
 53    new_visited_blocks.AddExtents(new_file_extents);
 54
 55    if (new_file_extents.empty())
 56      continue;
 57
 58    LOG(INFO) << "Encoding file " << new_file.name << " ("
 59              << BlocksInExtents(new_file_extents) << " blocks)";
 60
 61    // We can't visit each dst image inode more than once, as that would
 62    // duplicate work. Here, we avoid visiting each source image inode
 63    // more than once. Technically, we could have multiple operations
 64    // that read the same blocks from the source image for diffing, but
 65    // we choose not to avoid complexity. Eventually we will move away
 66    // from using a graph/cycle detection/etc to generate diffs, and at that
 67    // time, it will be easy (non-complex) to have many operations read
 68    // from the same source blocks. At that time, this code can die. -adlr
 69    vector<Extent> old_file_extents = FilterExtentRanges(
 70        old_files_map[new_file.name], old_visited_blocks);
 71    old_visited_blocks.AddExtents(old_file_extents);
 72    //处理差分的流程
 73    TEST_AND_RETURN_FALSE(DeltaReadFile(aops,
 74                                        old_part.path,
 75                                        new_part.path,
 76                                        old_file_extents,
 77                                        new_file_extents,
 78                                        new_file.name,  // operation name
 79                                        hard_chunk_blocks,
 80                                        version,
 81                                        blob_file));
 82  }
 83  // Process all the blocks not included in any file. We provided all the unused
 84  // blocks in the old partition as available data.
 85  vector<Extent> new_unvisited = {
 86      ExtentForRange(0, new_part.size / kBlockSize)};
 87  new_unvisited = FilterExtentRanges(new_unvisited, new_visited_blocks);
 88  if (new_unvisited.empty())
 89    return true;
 90
 91  vector<Extent> old_unvisited;
 92  if (old_part.fs_interface) {
 93    old_unvisited.push_back(ExtentForRange(0, old_part.size / kBlockSize));
 94    old_unvisited = FilterExtentRanges(old_unvisited, old_visited_blocks);
 95  }
 96
 97  LOG(INFO) << "Scanning " << BlocksInExtents(new_unvisited)
 98            << " unwritten blocks using chunk size of "
 99            << soft_chunk_blocks << " blocks.";
100  // We use the soft_chunk_blocks limit for the <non-file-data> as we don't
101  // really know the structure of this data and we should not expect it to have
102  // redundancy between partitions.
103  // 处理没有文件的数据的部分
104  TEST_AND_RETURN_FALSE(DeltaReadFile(aops,
105                                      old_part.path,
106                                      new_part.path,
107                                      old_unvisited,
108                                      new_unvisited,
109                                      "<non-file-data>",  // operation name
110                                      soft_chunk_blocks,
111                                      version,
112                                      blob_file));
113
114  return true;
115}

 

这里面一共调用了三次DeltaReadFile处理不同的情况,很明显中间的差分部分必须会出现打印

    LOG(INFO) << "Encoding file " << new_file.name << " ("

             << BlocksInExtents(new_file_extents) << " blocks)";

实际发现8.1的环境并没有这个打印,也就等于没进入for循环,很明显 new_part.fs_interface这个参数起到了至关重要的部分,我们只需要确认哪里赋值,为什么不对就可以了(实际做了很多无用功,最初一直以为是什么参数不对)

 

三、问题优化

 确认fs_interface赋值

 1payload_generator/payload_generation_config.cc
 2
 3bool PartitionConfig::OpenFilesystem() {
 4  if (path.empty())
 5    return true;
 6  fs_interface.reset();
 7  //lbb test问题的关键就出在这个判断,fs_interface这里时给到正确的值,但是没return,也就意味着
 8  //走到方法最后去了
 9  if (utils::IsExtFilesystem(path)) {
10    fs_interface = Ext2Filesystem::CreateFromFile(path);
11    if (fs_interface) {
12      //加入return 并给定blocksize为4096
13      TEST_AND_RETURN_FALSE(fs_interface->GetBlockSize() == kBlockSize);
14      return true;
15     }
16  }
17
18  if (!mapfile_path.empty()) {
19    fs_interface = MapfileFilesystem::CreateFromFile(path, mapfile_path);
20    if (fs_interface) {
21      TEST_AND_RETURN_FALSE(fs_interface->GetBlockSize() == kBlockSize);
22      return true;
23    }
24  }
25
26  // Fall back to a RAW filesystem.
27  TEST_AND_RETURN_FALSE(size % kBlockSize == 0);
28  fs_interface = RawFilesystem::Create(
29      "<" + name + "-partition>", kBlockSize, size / kBlockSize);
30  return true;
31}

 

加入该代码后重新编译delta_generator问题解决,这样8.1也可以走差分

 

四、问题探究

 

在发现这个问题之前其实做了很多无用功,这里面有几个重要的参数

 

1、payload_generator/generate_delta_main.cc


  DEFINE_int32(chunk_size,200 * 1024 * 1024,

              "Payload chunk size (-1 for whole files)");

 

2、payload_generator/delta_diff_utils.cc

 

// The maximum destination size allowed for bsdiff. In general, bsdiff should

// work for arbitrary big files, but the payload generation and payload

// application requires a significant amount of RAM. We put a hard-limit of

// 200 MiB that should not affect any released board, but will limit the

// Chrome binary in ASan builders.

const uint64_t kMaxBsdiffDestinationSize = 100 * 1024 * 1024;  // bytes

 

// The maximum destination size allowed for imgdiff. In general, imgdiff should

// work for arbitrary big files, but the payload application is quite memory

// intensive, so we limit these operations to 50 MiB.

// lbb test

const uint64_t kMaxImgdiffDestinationSize = 100 * 1024 * 1024;  // bytes

 

这两个参数好像毫不相干,却是他们控制着文件是否会走差分

 1payload_generator/delta_diff_utils.cc
 2
 3bool DeltaReadFile(vector<AnnotatedOperation>* aops,
 4                   const string& old_part,
 5                   const string& new_part,
 6                   const vector<Extent>& old_extents,
 7                   const vector<Extent>& new_extents,
 8                   const string& name,
 9                   ssize_t chunk_blocks,
10                   const PayloadVersion& version,
11                   BlobFileWriter* blob_file) {
12  brillo::Blob data;
13  InstallOperation operation;
14
15  uint64_t total_blocks = BlocksInExtents(new_extents);
16  if (chunk_blocks == -1)
17    chunk_blocks = total_blocks;
18
19  for (uint64_t block_offset = 0; block_offset < total_blocks;
20      block_offset += chunk_blocks) {
21    // Split the old/new file in the same chunks. Note that this could drop
22    // some information from the old file used for the new chunk. If the old
23    // file is smaller (or even empty when there's no old file) the chunk will
24    // also be empty.
25    // 将文件按照chunk_size对文件进行分割,非常重要的流程
26    vector<Extent> old_extents_chunk = ExtentsSublist(
27        old_extents, block_offset, chunk_blocks);
28    vector<Extent> new_extents_chunk = ExtentsSublist(
29        new_extents, block_offset, chunk_blocks);
30    NormalizeExtents(&old_extents_chunk);
31    NormalizeExtents(&new_extents_chunk);
32    
33    //lbb test
34    LOG(INFO) << "file name"<< name.c_str() << ".";
35    LOG(INFO) << "old_extents_DeltaReadFile"<< utils::BlocksInExtents(old_extents_chunk) * kBlockSize << " bytes";
36
37    TEST_AND_RETURN_FALSE(ReadExtentsToDiff(old_part,
38                                            new_part,
39                                            old_extents_chunk,
40                                            new_extents_chunk,
41                                            version,
42                                            &data,
43                                            &operation));
44
45  ......
46  ......
47  return true;
48}

 

 1payload_generator/delta_diff_utils.cc
 2
 3bool ReadExtentsToDiff(const string& old_part,
 4                       const string& new_part,
 5                       const vector<Extent>& old_extents,
 6                       const vector<Extent>& new_extents,
 7                       const PayloadVersion& version,
 8                       brillo::Blob* out_data,
 9                       InstallOperation* out_op) {
10  InstallOperation operation;
11
12  // We read blocks from old_extents and write blocks to new_extents.
13  uint64_t blocks_to_read = BlocksInExtents(old_extents);
14  uint64_t blocks_to_write = BlocksInExtents(new_extents);
15
16  // Disable bsdiff and imgdiff when the data is too big.
17  //文件操过了kMaxBsdiffDestinationSize 200M,跳过执行bsdiff差分,采用整包
18  bool bsdiff_allowed =
19      version.OperationAllowed(InstallOperation::SOURCE_BSDIFF) ||
20      version.OperationAllowed(InstallOperation::BSDIFF);
21  if (bsdiff_allowed &&
22      blocks_to_read * kBlockSize > kMaxBsdiffDestinationSize) {
23    LOG(INFO) << "bsdiff blacklisted, data too big: "
24              << blocks_to_read * kBlockSize << " bytes";
25    bsdiff_allowed = false;
26  }
27  //文件操过kMaxImgdiffDestinationSize 50M,跳过执行imagediff差分,采用整包
28  bool imgdiff_allowed = version.OperationAllowed(InstallOperation::IMGDIFF);
29  if (imgdiff_allowed &&
30      blocks_to_read * kBlockSize > kMaxImgdiffDestinationSize) {
31    LOG(INFO) << "imgdiff blacklisted, data too big: "
32              << blocks_to_read * kBlockSize << " bytes";
33    imgdiff_allowed = false;
34  }
35  ......
36  ......
37}
38    

这两个值的逻辑是这样的

1、首先进来的文件都会走一个分割,而分割的大小按chunk_size的大小200M

2、根据chunk_size 判断是不是操过了kMaxBsdiffDestinationSize ,如果超过了,那就不走差分了,考虑到机器内存和性能

3、目前来说,chunk_size 与 kMaxBsdiffDestinationSize相同,所以你多大的APK都会差分,只不过分割的份数不一样而已,看起来kMaxBsdiffDestinationSize判断有点儿多余

 

如果我们想更改流程,超过chunk_size大小的文件,就不走差分,那好办,更改kMaxBsdiffDestinationSize小鱼chunk_size即可

 

这次的分析,暂时不做流程上详细的讲解,我们只看这个问题的解决方法,后续流程讲解delta_generator的时候再详细说明

Android OTA 脚本流程 OTA的平台是高通 为了实现8937、8940共image的需求,因此详细看了这部分 这里主要跟踪升级高通mbn文件的流程 阅读详情

相关推荐

汽车OTA升级技术:从FOTA到SOTA的全面解析

本文全面解析汽车OTA升级技术,从FOTA(固件远程升级)到SOTA(软件远程升级)的核心区别与应用场景。FOTA涉及车辆核心性能优化,如动力系统和电池管理;SOTA则专注于信息娱乐系统升级。文章还探讨了整车OTA的架构设计、安全防护体系及开发者实战指南,为智能汽车技术发展提供深入见解。

weixin_29281915的博客 493

安卓12 -高通-OTA-AB升级相关信息.zip

安卓12 --高通-OTA-AB升级相关信息.zip

TPA3255EVM评估板:D类功放设计、四种输出模式配置与实战调试指南

D类音频放大器是一种高效率的开关模式功率放大器,其核心原理是通过脉宽调制(PWM)将模拟音频信号转换为高频开关信号,再经低通滤波器还原,从而在极低的发热下实现高功率输出。这项技术解决了传统模拟放大器效率低、发热大的痛点,其技术价值在于为高保真音频系统的小型化、大功率化提供了可能,广泛应用于家庭影院、专业音响、汽车音频和便携设备等领域。本文聚焦于德州仪器(TI)采用PurePath™ Ultra-HD技术的TPA3255芯片及其评估板,深入剖析其**闭环反馈架构**与**高速栅极驱动器误差校正**原理,这些技

489

ota升级分区即差分升级又整分区升级(android11)

fota差分增量升级

qq_36975610的博客 1860

AC695x蓝牙音频SoC开发实战:从核心特性到音频调试全解析

蓝牙音频SoC作为现代无线音频设备的核心,集成了微控制器、蓝牙射频、音频编解码和电源管理等功能,通过高度集成的单芯片方案简化了产品设计。其工作原理基于经典的蓝牙协议栈(如A2DP、HFP)和实时操作系统调度,实现稳定的音频流传输与低功耗运行。这类芯片的技术价值在于为TWS耳机、蓝牙音箱等消费电子产品提供了高性价比、低开发门槛的解决方案,广泛应用于对成本、功耗和音质有综合要求的场景。本文以恒玄AC695x系列为例,深入剖析其ARM Cortex-M4F内核、蓝牙5.0双模支持及音频子系统的设计要点,并分享开发

weixin_34297704的博客 390

数据工程师的降维实战:解决维度灾难与特征冗余

维度约简是应对高维数据挑战的基础技术,源于‘维度灾难’导致的距离失效与模型不稳定,其核心原理在于通过线性或非线性变换实现特征压缩,在保留关键信息的前提下降低计算复杂度。该技术具备显著工程价值:提升模型鲁棒性、加速推理响应、缓解内存压力,并支撑实时推荐、用户分群、端侧部署等典型场景。本文聚焦数据工程一线实践,结合电商用户行为数据(218维→45维),详解PCA、UMAP、t-SNE与自编码器的选型逻辑、避坑要点及线上落地方法,特别强调维度灾难与特征冗余两大高频痛点的诊断与治理。

weixin_30568591的博客 309

Android实战进阶篇--QCM6490 OTA升级全流程解析

本文深入解析QCM6490平台的OTA升级全流程,涵盖整包升级与差分升级的实战技巧。针对Android物联网设备,详细讲解镜像生成、升级包制作、执行策略及安全加固方案,特别强调update_engine_client工具的使用和升级失败排查方法,助力开发者高效完成工业级OTA部署。

weixin_42527160的博客 428

140. 安卓底层安全绕过|FRP账户锁、分区校验、AVB验证合法绕过实操

摘要 本文针对Linux技术用户和手机维修工程师,详细解析主流品牌手机的刷机与维修技术。内容涵盖高通、联发科和苹果芯片的底层通信协议差异,重点介绍Fastboot、EDL和DFU三种核心模式的触发机制与操作方法。文章提供完整的Python自动化刷机脚本,实现分区备份、固件写入和校验回滚全流程,所有方案均基于实测数据,无需依赖商业工具。 核心内容 刷机协议架构: 物理层:USB差分信号传输 传输层:Sahara/MTK Preloader/iBSS协议 应用层:Fastboot/EDL Firehose/iT

2601_95651806的博客 191

嵌入式语音交互硬件平台设计与实现

语音交互系统是物联网人机交互的核心技术之一,其本质是将声学信号转化为结构化指令并完成闭环反馈。原理上涉及音频采集、前端处理、通信传输、云端语义理解与TTS合成等多层协同。在资源受限的MCU平台上,采用云边协同架构可有效平衡实时性、功耗与功能复杂度。该方案广泛应用于智能硬件教学、低功耗语音终端及IoT边缘设备开发场景。本文以SF32LB52为主控的插件式小智AI平台为例,详解基于VAD语音活动检测与BLE透传的轻量级语音交互实现路径,覆盖从MEMS麦克风选型、PWM音频播放到物理焊接调试的完整工程链路。

weixin_36184718的博客 60

【信息科学与工程学】【物理/化学科学和工程技术】知识体系081 磁学——系列二 电磁学理论模型 02

微波与射频工程是一个建立在坚实的电磁学物理模型网络与系统抽象、以及计算与实现方法之上的复杂学科。其模型库可被组织成一个从物理基础到系统功能计算电磁学的前沿,正经历从“计算”到“认知”目标演变:从“仿真一个已知设计”​ 到“探索未知设计空间”​ 和“预测系统在不确定环境下的行为”。方法论融合“第一性原理数值计算”“物理启发模型”​ 与“数据驱动AI”​ 三者的界限日益模糊,形成“物理信息机器学习”​ 等新范式。系统复杂度:从“单一物理场、单一尺度、确定性”​ 的问题,转向应对。

weixin_49199313的博客 1167

【信息科学与工程学】计算机科学与自动化——第十八篇 存储系统设计 10 存储器/存储软件/存储芯片/存储盘/存储系统/存储网络01

③ MTJ电阻 → 界面:TMR=(R_AP-R_P)/R_P = 2P₁P₂/(1-P₁P₂) (Julliere模型);③ 访问延迟模型 → 统计:平均访问延迟 = p_hot * t_dram + (1-p_hot) * t_nvm + t_migration_overhead;:① 储能电容计算 → 数学物理:所需能量 E = P * t_backup,电容 C = 2E / (V_start² - V_end²);:① B-tree平衡 → 代数:树高 h = log_m(N),m为扇出;

weixin_49199313的博客 263

独家首发:Gemini Nano v1.5移动端SDK隐藏API解析(含动态量化开关、上下文流式卸载等5个灰度功能)

深度解析Gemini Nano v1.5移动端SDK隐藏API,助力开发者优化Gemini Nano移动端应用性能。涵盖动态量化开关、上下文流式卸载等5个灰度功能,适用于端侧低内存设备与实时推理场景,显著提升响应速度与资源利用率。值得收藏

CompiWander的博客 360

【信息科学与工程学】【安全领域】第三十五篇 网络安全算法表02

​分级处理​:实时增量 + 离线全量 + 自动校验三线并行​最终一致优先​:强同步仅用于支付等核心场景​双向容错​:Redis侧:读CK兜底(CK侧:自动屏蔽校验失败分区​业务适配​:graph LR高实时场景 --> Redis写+双重读分析场景 --> 放宽CK同步延迟报表场景 --> T+1校准保障该方案已在京东、美团等千亿级数据平台验证,支撑双11级流量下Redis与ClickHouse的数据协同。

weixin_49199313的博客 1342

2026–2028大模型技术拐点:8个产线验证的工程突破

大模型已进入后Scaling Law时代,单纯扩大参数与数据不再带来线性收益,算力瓶颈、功耗墙与推理成本成为刚性约束。在此背景下,动态稀疏激活、混合精度训练编译器等关键技术转向‘约束驱动创新’,强调在有限硬件资源下提升单位token效率与场景落地可靠性。这些拐点并非理论构想,而是经金融风控、工业质检、医疗影像等高价值产线反复验证的工程方案,兼顾模型性能、经济可行性与部署兼容性。尤其在边缘部署、低延迟响应与小样本适配等现实需求推动下,KV Cache分层压缩与小样本提示工程工业化正成为企业AI落地的关键支点。

weixin_33726318的博客 632

【信息科学与工程学】信息工程领域——第三十六篇 电路电子06 电路设计01

问题的数学分析及组合数学分析(涵盖材料科学、材料力学、数学物理、电路/电气/电机/电子/控制科学/控制论、微电子/电路系统架构、半导体器件物理、几何学/拓扑学/代数学/数论/离散数学/泛函分析/复变函数和时变函数/数理逻辑/运筹学、数学物理、数学华为、计算机系统架构设计、通信科学、信息科学、光科学、电科学、热科学、力学科学、网络科学 、复杂系统、其他学科知识)③LED驱动、PD接收、跨阻放大、带通滤波、ADC、数字处理;③三电极体系(工作、对、参比)、恒电位电路、电流-电压转换、低通滤波;

weixin_49199313的博客 331

【信息科学与工程学】信息工程领域——第三十六篇 电路电子05 高频电路/低频电路01

编号类型领域子领域问题问题的数学分析及组合数学分析参数列表及常量/参数的数值设计及数值范围关联知识1半导体物理微电子/电子工程半导体基础本征半导体载流子浓度如何计算?​数学分析:利用状态密度函数 N(E) 和费米-狄拉克分布函数 F(E) 在导带和价带积分。 公式:n=∫EC​∞​N(E)F(E)dE,p=∫−∞EV​​N(E)[1−F(E)]dE。 简化:对于非简并半导体,服从玻尔兹曼统计,n=NC​exp(−kTEC​−EF​​),p=NV​exp(−kTEF​−EV​​)。 质量作用

weixin_49199313的博客 385

小模型落地实战:如何让AI在十年前手机上稳定运行

小模型并非参数缩减的权宜之计,而是面向边缘与端侧场景的系统性工程重构。其核心原理在于协同优化模型结构、超低比特量化、知识蒸馏与硬件原生推理引擎,突破内存墙、功耗墙与系统沙盒限制。技术价值体现在离线可用、低延迟响应、高资源兼容性与可审计干预能力,显著降低AI部署门槛与运维成本。典型应用场景覆盖乡村医疗、工业质检、养老陪护、产线看板及农牧服务等网络受限、设备老旧、环境严苛的真实毛细血管场景。本文聚焦阿里Qwen轻量化实践,深入解析混合注意力架构、4-bit+1-bit量化、规则注入式蒸馏与ARM NEON直编译

weixin_30463341的博客 377

光流法实战:如何用OpenCV的Farneback算法实现图像对齐(附调参技巧)

本文详细介绍了如何使用OpenCV的Farneback稠密光流算法实现图像对齐,包括核心原理、参数调优和工程实现。通过实战代码和参数优化策略,帮助开发者解决动态场景中的图像对齐问题,提升多帧融合和视频处理的效果。

weixin_29266007的博客 291
上一篇: 六、A/B 升级update_engine分析-Action流程
下一篇: partition has unexpected contents概率性问题处理
李标标
博客等级 码龄11年 124粉丝 43原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值