Flutter鸿蒙本地数据持久化:Realm与realm_generator实战

在鸿蒙设备上跑 Flutter 工程,最让我头疼的从来不是 UI 适配,而是本地数据这一层。一开始图省事,全用 SharedPreferences 存 JSON,数据量一上来,启动分析和列表加载肉眼可见地卡。后来决定引入 Realm,但手写模型映射和维护字段同步又成了新的体力活。折腾了一轮之后,我用 realm_generator 把整个数据层从“手写维护”变成了“声明模型后自动生成”,并在鸿蒙端完成了插件集成和数据架构治理。这篇文章就把这套实战过程拆开讲,包括选型逻辑、工程配置、生成器原理、鸿蒙集成的坑,以及最后沉淀下来的数据层架构,给同样在 Flutter + 鸿蒙 这条路上做落地的人一个可以直接抄作业的参考。

1. 项目拆解与选型思路

1.1 为什么在鸿蒙场景下选择 Realm

在鸿蒙出现之前,Flutter 的本地数据库基本是老三样:SharedPreferences、Hive、sqflite。SharedPreferences 适合小量键值,Hive 性能不错但类型安全弱一些,sqflite 功能强但要从 SQL 语句写起,映射到 Dart 对象靠自己折腾。Realm 之所以在这个项目里胜出,不是因为它比所有人都快,而是因为它在 Flutter 跨端场景里提供了一条比较顺的“对象映射 + 自动生成”路径。

我在做选型对比时,重点看四个维度:编写效率、查询能力、鸿蒙适配成本、团队上手难度。Realm 的 Flutter 绑定在这几个维度上相对均衡。查询可以直接用类似realm.query<Task>('priority > $0', [8])的方式,生成的 Query 对象是惰性求值,排序和过滤都延迟到真正迭代时执行。这一点对处理大列表很友好,不会在查询语句构造时就把全部数据加载进内存。

更重要的是,Realm 的 Flutter 版本把模型对象和数据库字段直接绑定,配合代码生成器,模型类声明完,增删改查的底层字段名全部自动生成。鸿蒙侧虽然需要额外接入原生适配包,但整体接入路径比我想象中短,后面会详细讲。

1.2 realm_generator 在工程里的真实定位

很多同事第一次听到 realm_generator,以为是另一个数据库框架,其实不是。它只是 Realm Flutter 里的一个编译期代码生成器,底层还是 Realm 存储引擎,只是它帮我们把“模型类声明”到“底层字段操作”之间的样板代码全部自动生成出来。

打个比方,手动写 Realm 模型,就像你买了个智能家居中枢,但每个开关都要自己去接线。而 realm_generator 的作用是,你只需要告诉它房间里有什么灯、什么窗帘、什么插座,它自动把控制面板帮你接好。字段名、类型转换、主键映射、索引标记,这些容易写错又繁琐的工作,全部交给生成器处理。

它在工程里的位置大概是:Dart 模型类源码 -> build_runner 执行生成 -> 生成对应的.realm.dart文件 -> 打开数据库时注册 schema。也就是说,它是链接“业务模型”和“存储引擎”的桥梁,一方面保证强类型,另一方面减少手工映射带来的低级错误。

1.3 手写、半生成、全自动的取舍

我一开始见过不少项目是“半生成”状态:模型类自己写,查询和写入时却还是手写字符串拼条件。比如realm.objects('Task').where('ownerName == $0', name),一旦字段改名,这个字符串条件不会在编译时给你任何提醒,跑起来才报错。这种半生成状态其实最危险,因为你以为自己有类型保护,实际还是裸奔。

用生成器之后,查询条件里的字段、排序字段、索引字段都可以由生成的代码提供强类型引用。改字段名时,编译直接提示错误。这个体验差异在我看来是决定性的。当然,全自动也不是没有代价,生成代码的可读性一般,遇到要 debug 底层字段映射时你得打开生成的.realm.dart看一遍,但总比自己手工维护所有映射成本低得多。

2. 工程准备与鸿蒙侧集成

2.1 前置条件与工程结构确认

开始之前,先把基础环境确认好。Flutter 版本建议用稳定渠道的较新版本,鸿蒙工程目录已经存在,并且 FlutterEngine 能在真机上跑起来,这样我们才把重点放到数据库层。如果工程连 Flutter 页面都还跑不通,先解决工具链的问题,否则后面排查数据库层问题时会混入环境噪声。

鸿蒙工程的本地数据库通常落在应用沙箱的 data 目录下,最好在开发阶段把 Realm 文件的 path 固定到一个可预期的位置,比如{getApplicationContext files 下的子目录}/db/xxx.realm,方便后续用工具看数据。我在调试时吃过“找不到数据库文件”的亏,后来发现是路径用了临时目录,系统清理后文件就没了。

2.2 pubspec 依赖与 build.yaml 配置

在 pubspec.yaml 里,realm 作为主依赖,realm_generator 放在 dev_dependencies,build_runner 也放在 dev_dependencies。注意 generator 千万不要放 dependencies,否则业务包体积会变大。示例配置如下:

dependencies: realm: ^3.0.0 dev_dependencies: realm_generator: ^3.0.0 build_runner: ^2.4.0

如果你的工程只有一个默认入口,不写 build.yaml 也能直接跑。但我建议还是显式加一个 build.yaml,把生成范围收敛到lib/models目录,避免以后模块多了 generator 全工程扫描,拉长构建时间,也避免误生成一些不该生成的内部类。

targets: $default: builders: realm_generator|realm: generate_for: - lib/models/

这里有个小坑:不同版本的 generator 对 generate_for 的写法有细微差异,有的版本用目录结尾的/会识别异常。遇到这种情况,可以去掉末尾斜杠或者指定具体的.dart文件。

2.3 鸿蒙插件的落地方式

鸿蒙侧最麻烦的一步是原生适配包的集成。因为 Flutter 的 realm 包在 Android 上默认依赖 Android 原生模块,鸿蒙并不直接兼容,所以需要找到对应的鸿蒙适配包,把它以源码或 .har 的方式集成进entry工程。

我采用的方式是先在entry/oh-package.json5里声明本地依赖,指向放在entry/libs下的 .har 文件。大致结构如下:

{ "dependencies": { "harmony_realm": "file:./libs/harmony_realm.har" } }

这里有个需要特别注意的地方:不要把它加进 Android 的依赖体系里,也不要天真的以为 Flutter 插件会自动传递鸿蒙实现。鸿蒙的 Flutter 插件机制需要在原生工程侧显式声明,且必须使用鸿蒙的包管理方式。集成完成后,先跑一个最简单的Realm(config)打开动作,确认没有MissingPluginException,再继续往下做模型生成。

注意:如果你发现MissingPluginException,优先检查 .har 是否正确打进包体,以及oh-package.json5的依赖声明是否生效,而不是去改模型代码。

2.4 hvigor 构建与缓存问题

鸿蒙工程构建用的是 hvigor,它有自己的构建缓存。每次改了原生依赖或者新增 .har,最好先清理一次缓存再构建。我遇到过几次奇怪的问题:代码看起来没问题,但运行起来还是旧版行为,清理缓存后立即恢复正常。

具体操作就是在工程根目录执行 hvigor 的清理任务:

hvigor --clear-cache hvigor assembleHap

如果你的 IDE 是 DevEco Studio,直接在构建菜单里选择 Clean 即可。这个动作虽然简单,但能省下大量定位假问题的的时间。

3. 模型定义与代码生成实操

3.1 模型类的正确写法

Realm 的模型类必须继承RealmObject,字段类型也有限制,不能直接把自定义普通类塞进去,必须是 Realm 支持的类型:bool、int、double、String、DateTime、ObjectId、Uint8List 以及 RealmList、RealmSet、RealmDictionary 等集合类型。

写模型类时要注意几个点:

  • 字段不要用final,生成器需要对字段做映射。
  • 主键字段用@PrimaryKey()标记,如果主键是自增的 ObjectId,在构造函数里赋值最方便。
  • 高频查询字段用@Indexed(),但不要给所有字段都加索引,因为写事务会有额外开销。
  • 不需要持久化的字段用@Ignore()标记。

一个比较标准的模型类大概是这样的:

import 'package:realm/realm.dart'; part 'task.realm.dart'; class Task extends RealmObject { Task({ ObjectId? id, String title = '', int priority = 0, DateTime? dueAt, }) : _id = id ?? ObjectId(), _title = title, _priority = priority, _dueAt = dueAt; @PrimaryKey() late ObjectId _id; late String _title; @Indexed() late int _priority; late DateTime? _dueAt; @Ignore() bool isExpired = false; }

注意头部必须写part 'task.realm.dart';,这是生成器的目标文件。如果忘记这一行,生成器不会报错,但也不会生成对应的映射代码,后续打开数据库时 schema 会缺失。

3.2 运行生成命令与产物解读

模型定义好之后,在工程根目录跑:

dart run build_runner build --delete-conflicting-outputs

第一次跑可以把整个生成过程看一遍,尤其注意有没有绿色输出显示生成了哪个文件。如果没生成.realm.dart,先检查 part 指令和文件目录是否匹配。

生成的.realm.dart文件内容一般很长,核心做的事情可以归纳为:为每个字段生成底层 setter/getter,注册字段元信息,生成 schema 描述。这些逻辑普通开发者不需要逐行改,但建议至少扫一眼,知道字段名是怎么映射的,这样以后出现“字段读出来是 null”这类问题时,你能快速定位是生成文件旧了,还是打开数据库时 schema 配置漏了。

打开数据库时,要把模型类的 schema 传进去:

final config = Configuration.local([Task.schema], path: 'db/xxx.realm'); final realm = Realm(config);

如果没有传 schema,Realm 会尝试读取现有数据库文件,如果文件不存在通常能自动创建,但如果文件已经存在且 schema 不一致,就会报 misconfigured schema 的错误。

3.3 生成前后的功能对照

生成器带来的收益,列个表更直观:

功能点手写代码生成器代码
字段映射每次新增字段要手写映射模型声明后自动完成
主键冲突检测自己处理异常生成器统一管理元信息
查询字段名字符串拼接,容易拼错强类型引用,编译期检查
schema 版本管理手动迁移容易漏生成代码携带 schema 信息,减少遗漏
索引维护需要手动关联注解驱动,自动注册

这并不意味着生成器能解决所有问题。它只解决“模型定义和底层存储之间的映射”,不解决业务逻辑,也不替代迁移策略。真正要保证数据层稳定,还是得靠架构。

4. 强类型模型与持久化架构治理

4.1 强类型模型解决了什么

我用一段实际经历说明。之前手写 Map 做缓存时,有一次把字段priority误写成了priorityLevel,JSON 序列化不报错,前端取出来的数据全是默认值。这种问题在编译期完全暴露不出来,只能靠运行时打印日志,配合多个端联调才能发现。

用强类型模型之后,这种低级错误基本绝迹。字段定义在模型类里,生成器自动生成对应的读写方法,任何引用都走编译期校验。哪怕只是迁移数据库时少了新字段,代码都编译不过。

这里要提醒一点:不要在一个模型类里同时混用“业务显示字段”和“存储字段”而不做任何隔离。比如 UI 上需要一个“是否过期”的临时判断,我在模型里用@Ignore()忽略掉,这个字段不会持久化,但可以在模型类里保留计算逻辑。这种做法能让项目快速迭代,但注意不要往模型里塞太多 UI 状态,否则数据层会越来越像一堆散乱的静态变量。

4.2 查询与写入性能调优

Realm 推荐一次写事务处理多个对象,而不是循环单个 add。实际测试时,循环写入的表现特别差,每次事务提交都要等待磁盘同步,几十条数据可能就慢到明显卡顿。正确的做法是:

realm.write(() { for (final item in items) { realm.add(RealmModel.fromMap(item)); } });

读操作方面,查询本身是惰性的,使用.sort()、.filter()不会立即执行。但如果你在.watch()流里频繁重建查询,或者把每个列表项都做一次新的realm.find,同样会引发性能问题。更好的方式是数据进入内存或 ViewModel 后,尽量复用同一个查询结果。

索引配置也要克制。索引能加速读,但写入时要多维护一层索引结构。低频筛选字段不要加索引,真正的高频条件字段才值得加。

4.3 事务、监听与 UI 响应式架构

Realm 提供.watch(),可以对单个对象或集合变化做订阅。这个能力很适合 Flutter 的响应式 UI,但使用不当也会成为性能杀手。

我的建议是:UI 层尽量不要直接持有 RealmObject。正确的做法是,数据层把 RealmObject 转化为轻量 DTO 或领域对象,提供给 ViewModel,再由页面渲染。这样做有几个好处:RealmObject 在事务提交后自动刷新,如果 UI 层持有的是旧引用,可能读到的数据与预期不符;另外,领域对象更容易做单元测试和 mock。

监听订阅记得在页面销毁时取消,否则StreamSubscription泄漏会导致页面回收后仍在更新数据。真机上比较常见的现象就是退出列表页再进来,内存不降反升,多半是订阅没清理。

5. 鸿蒙适配中踩过的坑与排查实录

5.1 编译期的坑

我在鸿蒙工程里遇到的最大编译坑,是 Flutter 侧添加 realm 依赖后,原生插件没有被正确打包进 HAP。报错信息是MissingPluginException,但问题实际发生在构建链路上。原因是我把 .har 文件放错了目录,导致 hvigor 没有把它当作运行时依赖打包。

排查顺序很重要:

  1. 先确认模型文件有没有生成成功,排除 Dart 侧问题。
  2. 再看鸿蒙原生侧依赖是否加载,最简单的验证是把数据库路径改成明显错误的位置,看能否正常抛出 Realm 相关的路径异常。
  3. 如果异常是MissingPluginException,多半是插件没走到鸿蒙的实现,回头检查 oh-package.json5。

还有一次,构建成功但打开数据库时报“unknown table”,一看生成的.realm.dart文件日期还是几天以前的,说明 build_runner 没有真正重新执行,清理构建缓存后重新生成就恢复了。

5.2 运行期的坑

列表页因为查询构造不当变卡的案例也很典型。起初是在每个 item 的 build 方法里写realm.all<Task>().where(...),导致每次滚动都在创建新查询,内存和 CPU 双双报警。改成在 ViewModel 里先获取RealmResults,再对结果做分页切片,性能立刻好了很多。

另一个常见问题是数据库本地文件路径下写到错误的位置,尤其是在鸿蒙文件系统沙箱限制下。如果路径指向了缓存目录,系统可能随时清理掉文件,表现为应用重启后数据丢失。日志里可能什么错误都没有,数据就这么消失了。我把 path 改成应用数据目录后,这个问题再没出现过。

5.3 快速定位问题的方法论

复盘下来,数据库类问题最怕黑盒排查。我建立一个固定流程,先确认生成文件版本,再确认数据库 schema 版本,最后才是看业务代码逻辑。日志要分两层:Flutter 侧打 dart 日志,鸿蒙侧用 DevEco 过滤原生插件日志,两边的错误码对上之后,才定位到具体是查询错误还是 SQLite 层的问题。

现象常见原因处理办法
打开数据库报 schema 错误生成文件过期或 schema 列表漏传重新 build_runner,确认传入全部模型
数据写入后列表不刷新watch 订阅未正确注册检查订阅流,页面销毁时取消
列表卡顿每行重建查询查询结果缓存或提升到 ViewModel
缺少插件异常.har 未打包或路径不对检查 oh-package.json5 与入口目录
应用重启数据消失数据库 path 指向缓存目录改为应用数据目录,保证沙箱持久性

6. 工程实践建议与后续扩展

6.1 多模块项目的模型目录规划

项目变大后,不要把所有模型塞在一个文件里。我现在的做法是,lib/models下按业务域划分子目录,比如task/、user/、setting/。每个域里的模型文件独立生成自己的.realm.dart,打开数据库时把所有 schema 汇总。

这样拆分的好处是,团队并行开发时不太会发生文件冲突。每个域模型的字段变化都集中在自己的目录里,review 起来也直观。如果项目更大,可以进一步把models、repositories、providers分别放进各自模块,用 publish 的方式在模块间共享。

6.2 数据迁移策略

Realm 的数据迁移是在Configuration.local()里通过schemaVersion和migrationCallback显式控制的。最开始项目里没有迁移概念,一旦模型增加必填字段,老用户数据库打不开,只能清空重建。后来我补上了迁移策略:

final config = Configuration.local( [Task.schema], schemaVersion: 2, migrationCallback: (migration, oldSchemaVersion) { if (oldSchemaVersion < 2) { // 给已有对象补充默认值,或者重命名旧字段 } }, );

迁移回调里最怕直接访问新版本才添加的对象,应该在 migration 提供的入口上操作历史 schema。这种问题跑了之后才明白,迁移不是“改完模型自动就行的”,必须提前设计好版本号规则。

6.3 自动化流水线建议

我最后把生成和迁移检查放进了 CI。每次代码合并前,CI 会执行一次dart run build_runner build并检查 git diff 中是否包含预期的.realm.dart文件变化。这样即使有同事忘了重新生成,也会在流程上被卡住。

另外,release 构建时开的混淆需要小心。RealmObject 子类的字段名要保持安全,否则混淆后的随机字段名会让生成的映射关系错乱。鸿蒙 release 模式尤其要检查 keep 规则。

6.4 后续扩展方向

这套架构稳定之后,后续可以继续扩展的方向包括:把数据库操作抽象成统一的 Repository 接口,方便以后替换或测试;把 Realm 的 schema 版本号纳入应用版本发布检查;把本地数据上传逻辑改造成通过事务队列发布,避免网络异常时数据堆积。每一步都不难,但前提是先把模型生成和鸿蒙集成的基础打好。

我个人的体会是,realm_generator 最核心的价值不是省那些代码量,而是让数据层的变更在编译期就暴露问题。只要模型声明写对,生成代码就是可信的。最后再分享一个小技巧:每次改完模型,先把旧的生成文件删掉再执行 build_runner,能避免很多奇怪的缓存冲突,尤其在鸿蒙这种构建链路比较复杂的工程里,这个习惯很管用。

相关新闻

YOLOv3三尺度特征金字塔:原理、锚框分配与实战调优

YOLOv3三尺度特征金字塔:原理、锚框分配与实战调优

1. 三尺度特征金字塔到底解决了什么问题目标检测这行做久了&#xff0c;你会发现一个特别朴素的规律&#xff1a;小目标漏检&#xff0c;十有八九不是模型容量不够&#xff0c;而是特征图分辨率不够。一张416416的输入图&#xff0c;经过骨干网络五次下采样之后&#xff0c;最小…

2026/10/20 15:12:56 阅读更多 →
银行数据库国产化替换实战:从Oracle迁移到GBase的完整链路

银行数据库国产化替换实战:从Oracle迁移到GBase的完整链路

把数据架构整体搬一遍家&#xff0c;无论放在哪个行业都不是小事&#xff0c;放在银行这种对一致性、可用性要求极高的场景里&#xff0c;更是牵一发动全身。最近我刚好深度参与了某农商银行&#xff08;以下按惯例以“某农商银行”代称&#xff09;的全栈数据库国产化替换项目…

2026/10/20 15:12:56 阅读更多 →
海量小文件删除慢?揭秘文件系统元数据瓶颈与优化方案

海量小文件删除慢?揭秘文件系统元数据瓶颈与优化方案

1. 项目概述&#xff1a;当“删文件”变成一场系统级压力测试你有没有试过在终端里敲下rm -rf&#xff0c;然后盯着光标一动不动地等&#xff1f;不是卡死&#xff0c;是真等——等它把几万个零散小文件一个个从磁盘上抹掉。这次不是误操作后的抢救现场&#xff0c;而是一次刻意…

2026/10/20 15:12:56 阅读更多 →

最新新闻

大模型部署从单机到集群:推理性能调优与架构演进实践

大模型部署从单机到集群:推理性能调优与架构演进实践

1. 为什么大模型部署要聊"从单机到集群"这个演进1.1 单机部署不是起点&#xff0c;而是最该被认真对待的阶段很多刚接触大模型的人&#xff0c;看到动辄几十GB甚至上百GB的模型权重文件&#xff0c;下意识觉得"这玩意儿肯定得上集群"。但实际落地的时候&am…

2026/10/20 20:31:43 阅读更多 →
MVMD结合小波阈值去噪:多通道信号降噪的MATLAB实现与调参指南

MVMD结合小波阈值去噪:多通道信号降噪的MATLAB实现与调参指南

简介&#xff1a;这份资源面向信号处理、故障诊断与振动监测方向的学习者和研究人员&#xff0c;提供一套基于MATLAB的完整联合去噪方案&#xff0c;用于从多通道含噪信号中恢复原始信号。其核心思路是先通过多变量变分模态分解&#xff08;MVMD&#xff09;将信号拆解为多个模…

2026/10/20 20:31:43 阅读更多 →
AI神经网络如何捕捉黄金牛市信号?——从特征工程到实战

AI神经网络如何捕捉黄金牛市信号?——从特征工程到实战

黄金价格突破5000美元这件事&#xff0c;放在几个月前可能没人敢想&#xff0c;但现在市场上讨论得越来越像那么回事儿。我做量化交易也有几年了&#xff0c;手里积累了一套基于AI神经网络模型的信号识别框架。这个项目的核心目标&#xff0c;不是去预测“明天涨不涨”&#xf…

2026/10/20 20:31:43 阅读更多 →
一人公司AI实战:从内容获客到交付的自动化运营系统

一人公司AI实战:从内容获客到交付的自动化运营系统

把"一人公司"这个标签贴在自己身上&#xff0c;我跑了快三年。身边经常有人问我&#xff1a;你一个人怎么接得下这么多项目&#xff0c;还能保持稳定更新&#xff1f;我的回答基本是同一句——我不是一个人在工作&#xff0c;我是带着一支虚拟团队在跑&#xff0c;团…

2026/10/20 20:31:43 阅读更多 →
精益为骨、数字为经、AI为智:破解企业AI投资困局的落地路线图

精益为骨、数字为经、AI为智:破解企业AI投资困局的落地路线图

过去两年&#xff0c;我身边已经有不下十位企业一把手跟我聊过同一个烦恼&#xff1a;AI的预算批了&#xff0c;专家也请了&#xff0c;模型也跑通了&#xff0c;可到了年底一算账&#xff0c;钱花了&#xff0c;利润没涨&#xff0c;流程也没变。审批流还是那个审批流&#xf…

2026/10/20 20:31:43 阅读更多 →
Agent框架插件化深度拆解:从注册表到模型网关的可扩展架构

Agent框架插件化深度拆解:从注册表到模型网关的可扩展架构

最近拆完一套开源的 Agent 框架&#xff0c;我最大的感受是&#xff1a;插件化这件事&#xff0c;很多项目都做了&#xff0c;但大多数停留在“能用”的阶段&#xff0c;而这套框架把插件化推进到了“可观测、可干预、可替换”的完整生命周。以前我做业务系统时也写过插件机制&…

2026/10/20 20:30:37 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:45 阅读更多 →