Flutter for OpenHarmony幸运大转盘数据统计模块实现
幸运大转盘系列写到这里前面九篇已经把页面结构、转盘动画、抽奖算法、奖品库存、历史记录都串起来了。说实话我自己把整个流程跑通的时候第一反应是“终于像个完整 App 了”但紧接着就会发现一个被忽略的问题到底转了多少次哪些奖品被抽走了中奖率真的和配置一致吗如果这几个问题答不上来这个转盘 App 依然只能算一个“能玩的 Demo”而不是一个“能运营的功能模块”。所以这一篇也就是第十篇我用 Flutter for OpenHarmony 给这个幸运大转盘项目补上最后一块关键拼图数据统计。包括统计口径怎么定、记录怎么存、埋点怎么埋、统计看板怎么画以及我在实际调试中踩过的坑。这篇文章不会依赖任何服务端所有统计逻辑都跑在本地但最后我会讲怎么把这套本地统计平滑升级成上报统计。1. 数据统计模块的设计定位与方案选型1.1 先想清楚统计什么做数据统计最容易犯的错不是不采集而是什么都采集。用户点了哪个页面、停留了多久、点击了几次按钮、手机型号是什么全存下来最后真正要回答业务问题时反而从一堆原始数据里翻不出答案。转盘类工具的核心业务问题其实很集中转盘总共被转了多少次这是参与度指标中奖多少次中奖率是多少这是概率验证指标每个奖品分别被抽中多少次这是库存消耗和吸引力指标每天的转盘次数走势如何这是活动热度指标某个时间段内新增了多少用户会话这是粗略的用户量指标。这个项目没有登录体系所以我决定用“会话”代替“用户”。每次冷启动 App 生成一个 sessionId在统计口径上按会话去重够用了。如果后面接上账号系统把 sessionId 映射到用户 ID 就能完成升级不需要推翻数据模型。另一个原则是“存原始事件不存结果快照”。也就是说不要只在某处存一个“今日转了 100 次”这样的计数而是把每一次转动都存成一条明细记录。原因很简单结果快照一旦写错就得重来明细记录随时可以通过聚合重算出所有指标。数据量也不用担心转盘场景一天几百条已经顶天了JSON 文件完全能扛住。1.2 本地存储还是后端上报很多做 App 的同学一上来就会想把统计直接上报到服务端理由是“以后肯定要分析用户行为”。但考虑到当前阶段没有后端资源强行上服务端只会让 App 在没网的时候变成哑巴数据全丢。我的选择是本地存明细异步刷新统计页同时把存储层设计成接口隔离。这样以后要做上报只需要把存储实现换成一个带网络请求的实现类UI 和聚合逻辑不用动。本地存储有三个候选方案SharedPreferences、JSON 文件、SQLite。三者对比方案写入性能查询灵活性实现复杂度适合场景SharedPreferences适合小 KV弱低存 sessionId、版本号JSON 文件适合低频追加中低个人工具类 AppSQLite高强高千级以上高频写入转盘每天产生几百条记录JSON 文件在性能和复杂度之间最平衡而且文件可直接拷贝到电脑上分析方便我 Debug。最终整个统计模块分成三层采集层只负责记录事件存储层只负责读写文件展示层负责聚合和渲染。2. 统计数据模型与存储层搭建2.1 记录模型设计统计记录不能只存一个“中奖了/没中奖”万一后面想分析“某个奖品被抽走时转盘停在哪个位置”没有原始数据就没法回溯。我设计的最小字段集如下id记录唯一标识用时间戳加随机数生成sessionId当前会话标识用于粗粒度用户去重spinTime触发结果的时间完整保存 ISO8601 字符串prizeId命中奖品 ID未中奖时为空字符串prizeName奖品名称冗余保存统计页直接展示用不用多查一次配置isWin布尔值标识本次是否中奖。冗余 prizeName 看起来违反“表设计要规范”的直觉但在这个场景里值得。因为奖品名称可能在活动期间被运营修改如果只存 prizeId 而奖品配置被改掉历史统计就会跟着变。存一个快照名称统计口径才稳定。2.2 存储文件格式与读写封装文件结构采用纯 JSON 数组每行一条记录不现实我直接用整个数组序列化。文件放在应用支持目录下面初始化时通过 path_provider 拿到目录。OpenHarmony 环境下尤其要注意不同版本的适配层对目录处理不完全相同我用的是 getApplicationSupportDirectory而不是硬编码 /data 路径第一次适配的时候因为硬编码路径踩过坑后面细说。核心存储类这样写import dart:async; import dart:convert; import dart:io; import package:path_provider/path_provider.dart; class SpinRecord { final String id; final String sessionId; final DateTime spinTime; final String prizeId; final String prizeName; final bool isWin; const SpinRecord({ required this.id, required this.sessionId, required this.spinTime, required this.prizeId, required this.prizeName, required this.isWin, }); MapString, dynamic toJson() { id: id, sessionId: sessionId, spinTime: spinTime.toIso8601String(), prizeId: prizeId, prizeName: prizeName, isWin: isWin, }; factory SpinRecord.fromJson(MapString, dynamic json) { return SpinRecord( id: json[id] as String? ?? , sessionId: json[sessionId] as String? ?? , spinTime: DateTime.tryParse(json[spinTime] as String? ?? ) ?? DateTime.now(), prizeId: json[prizeId] as String? ?? , prizeName: json[prizeName] as String? ?? , isWin: json[isWin] as bool? ?? false, ); } }fromJson 里我写了大量兜底默认值因为老版本文件可能没有某些字段。这个习惯很重要后面在 5.1 节会专门讲。文件读写我封装成一个 Store启动时一次性 load 到内存业务代码全程只操作内存 List写入时才整体覆盖文件。转盘追加频率极低所以根本不需要增量写文件这种复杂度。class SpinStatsStore { File? _file; ListSpinRecord _records []; bool _loaded false; Futurevoid init() async { if (_loaded) return; final dir await getApplicationSupportDirectory(); _file File(${dir.path}/spin_records.json); if (_file!.existsSync()) { final raw await _file!.readAsString(); if (raw.isNotEmpty) { final list jsonDecode(raw) as Listdynamic; _records list .map((e) SpinRecord.fromJson(e as MapString, dynamic)) .toList(); } } _loaded true; } Futurevoid append(SpinRecord record) async { _records.add(record); await flush(); } Futurevoid flush() async { if (_file null) return; final content jsonEncode(_records.map((e) e.toJson()).toList()); await _file!.writeAsString(content, flush: true); } ListSpinRecord get records List.unmodifiable(_records); }2.3 会话识别与统计口径sessionId 在 App 启动时生成用 SharedPreferences 持久化。注意我保存的是“上一次的 sessionId”启动时如果发现本地没有 sessionId就生成一个新 id如果已经有了说明是同一次会话的后台切回不重新生成。只有 App 进程被杀掉再冷启动sessionId 才会变。统计口径主要卡在“天”的划分上。spinTime 我是用 DateTime.now() 取的但这个时间是用户设备的本地时间。统计“今日次数”时不能直接拿服务器时间对比因为设备时区可能不同当然当前场景都在同一时区问题不大但我会在设计上留好统一用 isoTimestamp 保存展示层再用本地时区转成日期分组。避免以后适配海外用户时踩时区坑。3. 埋点采集与统计核心逻辑实现3.1 在抽奖流程中埋点埋点位置必须放在抽奖结果确定之后而不是动画结束之后。转盘动画可能被用户打断不会但动画结束回调有可能因为页面销毁而不触发。正确做法是抽奖服务返回 outcome 后立刻写统计记录动画只是 UI 表现。我在转盘页面里这样组织流程Futurevoid _handleSpin() async { if (_spinning) return; setState(() _spinning true); final outcome await _lotteryService.draw(); // 结果一出来就记录不依赖动画 final record SpinRecord( id: ${DateTime.now().microsecondsSinceEpoch}${_random.nextInt(10000)}, sessionId: _sessionManager.currentSessionId, spinTime: DateTime.now(), prizeId: outcome.isWin ? outcome.prize?.id ?? : , prizeName: outcome.isWin ? outcome.prize?.name ?? : , isWin: outcome.isWin, ); await _statsStore.append(record); // 再播放转盘动画并更新库存 await _animateToIndex(outcome.index); await _prizeStore.decrementStock(outcome.prizeId); setState(() _spinning false); }先记录、再动画、最后更新库存这个顺序是我刻意安排的。即使动画过程中出现异常用户这次转动已经被统计过了即使库存更新失败统计页也能反映真实参与行为。统计不依赖业务状态的成功这对数据准确性很重要。3.2 防重复采集埋点写完后我最担心的是重复采集。Flutter 页面在热重载或路由重建时事件回调可能被重复绑定或者用户在转盘还没结束时长按按钮掉进并发调用。一开始我只加了_spinning标志但它只拦住了并发请求没有拦住同一个 outcome 被回调两次的问题。后来我换成结果消费令牌每次请求生成一个递增序号回调回来的时候检查序号是否已经处理过。这个在单机转盘里看起来有点大材小用但如果你以后接推送、接 PUSH 到设备事件这个套路能防住绝大部分重复消费。int _requestSeq 0; int? _handledSeq; Futurevoid _handleSpin() async { final seq _requestSeq; final outcome await _lotteryService.draw(); if (_handledSeq seq) return; _handledSeq seq; // 剩余逻辑同上 }当然真正的防重复核心还是业务语义一次转盘请求只允许一次结果。如果你在代码里看到统计数字偏大先怀疑 _handleSpin 被调用了多次不要一头扎进统计模型。3.3 聚合统计计算的实现统计页拿到 records 之后不能直接在 build 里循环聚合那样会在每次 setState 时重复计算。我写了一个独立的聚合方法进入页面时异步执行一次缓存计算结果然后传给 UI。首先定义统计结果几个类class StatsSummary { final int totalSpins; final int winCount; final double winRate; final int sessionCount; final int todaySpins; } class PrizeStat { final String prizeId; final String prizeName; final int winCount; final double ratio; }聚合实现如下StatsSummary computeSummary(ListSpinRecord records, DateTime now) { final today DateTime(now.year, now.month, now.day); int total 0; int wins 0; int todaySpins 0; final sessions String{}; for (final r in records) { total; if (r.isWin) wins; final day DateTime(r.spinTime.year, r.spinTime.month, r.spinTime.day); if (day today) todaySpins; sessions.add(r.sessionId); } return StatsSummary( totalSpins: total, winCount: wins, winRate: total 0 ? 0 : wins * 100.0 / total, sessionCount: sessions.length, todaySpins: todaySpins, ); }按奖品维度分组类似用 Map 先按 prizeId 分组计数最后按 winCount 倒序排列。这里有个细节未中奖的记录不要混进奖品统计里否则“未中奖”也会变成一个特殊的“奖品行”页面展示会很奇怪。因为记录量少这些聚合就算在低端 OpenHarmony 设备上跑耗时也远低于肉眼感知阈值。但我仍然把它放进 async 方法里避免万一未来记录数膨胀时阻塞 UI。4. 统计看板页面的渲染与可视化4.1 页面整体结构与状态加载统计页我做成一个独立的StatsPage路由进入时先显示加载态等聚合完成后显示看板。结构分成三块顶部数值卡片区、中间图表区、底部奖品排行列表区。状态管理沿用项目里最朴素的 setState没有引入额外框架。页面代码骨架如下class StatsPage extends StatefulWidget { override StateStatsPage createState() _StatsPageState(); } class _StatsPageState extends StateStatsPage { late FutureStatsViewData _future; override void initState() { super.initState(); _future _load(); } FutureStatsViewData _load() async { await _statsStore.init(); final records _statsStore.records; final summary computeSummary(records, DateTime.now()); final prizes computePrizeStats(records); final daily computeDailyStats(records, DateTime.now()); return StatsViewData(summary: summary, prizes: prizes, daily: daily); } Futurevoid _refresh() async { setState(() { _future _load(); }); } }StatsViewData 我单独定义成一个不可变数据类统一封装三个视图模型这样 build 方法不需要知道聚合细节。4.2 用 CustomPaint 绘制柱状图图表我吃亏过。刚开始我想直接引入一个第三方图表库但 Flutter for OpenHarmony 的插件兼容层并不保证所有纯 Dart 之外的工具包都能直接跑与其赌兼容性不如自己在 CustomPaint 里画一个柱状图。转盘统计只有几十个数据点根本不需要百兆级的图表引擎。我画一个最近 7 天的转动次数柱状图核心思路先求出最大值用于归一化柱高横轴均分 7 个槽位纵轴高度固定柱体绘制颜色区分今天和历史。关键绘制代码class DailyBarChartPainter extends CustomPainter { DailyBarChartPainter({ required this.data, required this.maxValue, required this.todayIndex, }); final Listint data; final int maxValue; final int todayIndex; override void paint(Canvas canvas, Size size) { final barWidth size.width * 0.08; final gap (size.width - barWidth * data.length) / (data.length 1); final baseY size.height - 24; final paint Paint()..strokeCap StrokeCap.round; for (int i 0; i data.length; i) { final barHeight maxValue 0 ? 0 : (data[i] / maxValue) * (size.height - 40); final left gap i * (barWidth gap); final top baseY - barHeight; paint.color i todayIndex ? const Color(0xFF3A7AFE) : const Color(0xFFB0C7FF); paint.strokeWidth barWidth; canvas.drawLine( Offset(left barWidth / 2, baseY), Offset(left barWidth / 2, top), paint, ); } // 画一条底部基线 final axisPaint Paint() ..color const Color(0xFFDDDDDD) ..strokeWidth 1; canvas.drawLine( Offset(4, baseY), Offset(size.width - 4, baseY), axisPaint, ); } override bool shouldRepaint(covariant DailyBarChartPainter oldDelegate) { return oldDelegate.data ! data || oldDelegate.maxValue ! maxValue; } }这个图没有加刻度数字只用于看大概趋势所以我故意省略了坐标轴文字和网格保持页面简洁。图表上方我用一个“本周趋势”标题配合数值列表完全满足运营看趋势的需要。4.3 展示今日战绩与热门奖品顶部卡片区我会放四个指标总转次数、今日转次数、中奖率、会话数。这里用 GridView 均分两列卡片不做过多的阴影效果OpenHarmony 设备性能有限阴影太多会掉帧我全程用扁平卡面加圆角。热门奖品排行列表直接 ListView.builder每行显示名次、奖品名、中奖次数、占比。行内左侧用渐变色块代表第几名前三名颜色不同后面统一灰色。这个设计在运营页面里非常常见用户一眼能看出哪个奖品被抽得最多。代码没有特别复杂的地方关键是要记住 ListView 的 itemCount 为 0 时显示占位文案比如“暂无抽奖记录”别渲染空列表闪白屏。5. 常见问题与排查经验5.1 典型问题速查我在做统计模块时遇到四类问题整理成表方便你对照排查。问题现象可能原因处理建议统计页数字永远是 0存储文件路径没初始化或者 _loaded 逻辑缺失排查 _statsStore.init() 是否被调用路径目录是否存在转了一次记录出现两条_handleSpin 被并发调用或结果回调重复触发用递增序号令牌消费结果加 !_spinning 拦截今日次数跨天后不准聚合时直接拿 DateTime.now() 与 UTC 时间对比统一用本地日期做分组存储用 ISO8601 字符串打开统计页明显卡顿build 方法里直接做聚合循环聚合移入异步方法缓存结果避免反复计算还有一个文件写入失败的问题在 OpenHarmony 上如果直接写相对路径或者用 getExternalStorageDirectory 拿到的目录没有权限writeAsString 会抛异常。处理办法是统一走 getApplicationSupportDirectory并在 flush 方法里 try-catch写入失败时至少保证内存数据可用避免用户转动一次后 App 闪退。5.2 个人踩坑与优化建议第一不要把 flush 写到每个 append 里。我之前图省事每次转动都全量写盘导致快速测试时 IO 排队动画都卡了。后来改成每次 append 只标记 dirty页面退出或 App 进入后台时统一 flush。但转盘场景数据量小保持同步写也问题不大只是要意识到全量写文件在数据量大时是性能隐患。第二统计页不适合在 initState 里同步加载大文件。有些低配置设备上readAsString 解析几十 KB 的 JSON 都能卡出明显的转圈。我用 FutureBuilder 配合 Future 延迟加载骨架屏加载瞬间就能渲染出来。第三给记录文件加一层备份。我吃过亏手滑把 JSON 文件格式调错所有历史数据全清了。后来加了一个简单机制第一次写入时复制一份 .bak 文件启动加载失败时尝试从 .bak 恢复。这个机制在 Flutter 里实现不过 20 行代码但救了我两次。第四不要在统计 UI 里直接依赖奖品配置。前面说过 prizeName 是冗余字段统计记录必须自包含。如果统计页通过 prizeId 实时查找最新奖品配置一旦后台把奖品名字改了历史统计全都扭曲。6. 这个模块放在真实项目里还能怎么用6.1 从本地统计到上报体系的演进目前这套统计完全是本地的但接口已经做了隔离。假如后面接入服务端需要做三件事定义一个 ReportService 接口方法包括 reportSpin(SpinRecord record) 和 fetchStatsRange(DateTime start, DateTime end)本地存储实现改成批量上报加本地缓存先存本地再同步到服务端同步成功后的记录标记统计页改为服务端聚合结果本地缓存兜底保证弱网体验。接口统一的好处是 UI 层完全无感。你在转盘页里调用 _statsStore.append未来换成 RemoteStatsRepository页面代码一行不用改。6.2 用数据反哺产品与活动设计数据统计的最大价值不是给自己看而是回灌到运营决策。我在这个项目里拿统计结果做了两件很实际的事。第一验证奖品概率配置是否符合预期。比如配置上写了“一等奖 1%”跑了一千次之后看中奖率是否稳定在 1 附近。如果明显偏高说明随机算法分布有偏差需要检查随机种子算法如果偏低就要确认是否有奖品已经被抽空后 fallback 逻辑占了概率。第二观察转盘热度衰减。每天转盘次数连续下降说明用户对当前奖品池失去兴趣运营应该补新奖品进去。热门奖品排行可以指导补货比例排行榜前两名消耗快就多补这些同类奖品补货后重新观察统计趋势。这个循环跑起来之后转盘就不再是死功能而是一个可以持续调优的运营工具。我个人在实际使用中的体会是统计模块是对前面所有功能的一次质量验证。它让我第一次清晰地看到什么奖品是真实被抽走的、什么奖品只是配置上好看但从来不被转中也让我发现了抽奖算法在连续试跑 100 次后会出现的概率偏差。所以别把统计当成“最后加一个页面”那么轻量把它当成整个转盘业务的数据底座来设计你会收获比预期更多的东西。

相关新闻

LabVIEW连接泰克示波器:VISA与SCPI编程实战指南

LabVIEW连接泰克示波器:VISA与SCPI编程实战指南

简介:这份 LabVIEW 连接泰克示波器范例,面向从事电子测试与信号分析、希望用 LabVIEW 远程控制示波器并实现自动化测量的工程师,适用于从基础功能连接到批量自动化测试的进阶场景。压缩包共包含166个文件,以153个 vi 程序文件为核…

2026/10/19 14:43:16 阅读更多 →
GSWOA优化Elman网络:多输入单输出预测的精度与稳定性提升

GSWOA优化Elman网络:多输入单输出预测的精度与稳定性提升

前几天帮一位做设备状态监测的同事看了组数据:一堆传感器读数、环境变量在前面排着,后面只有一列要预测的目标值。他试过线性回归,效果惨不忍睹;又试了BP神经网络,好了一些,但每次换随机种子结果就飘得厉害…

2026/10/19 14:43:16 阅读更多 →
Artcraft:手工艺数字化的三层翻译方法论

Artcraft:手工艺数字化的三层翻译方法论

1. “Artcraft”不是新词,而是被长期误读的设计实践范式“Artcraft”这个词最近突然在设计类社区、创意工作坊和高校课程大纲里高频出现,但翻遍主流设计史文献、权威术语词典甚至近十年的国际设计期刊索引,都找不到它作为独立概念的明确定义。…

2026/10/19 14:43:16 阅读更多 →

最新新闻

JDBC进阶实战:连接池、事务与批处理的核心原理与工程实践

JDBC进阶实战:连接池、事务与批处理的核心原理与工程实践

看到标题里那个"2",你应该知道这是个延续篇。上一篇我们把手搭在JDBC的门把手上,学会了加载驱动、建立连接、执行一条SQL、把ResultSet里的数据一行行捞出来,最后像个好学生一样把资源一个个close掉。那是入门,是"…

2026/10/19 16:09:56 阅读更多 →
AI视觉图像识别全流程:从数据准备到模型部署的实战指南

AI视觉图像识别全流程:从数据准备到模型部署的实战指南

开门见山说一个很多新手容易误解的点:AI视觉图像识别并不是“拿一张图丢进模型,结果就出来了”那么简单。它背后是一条完整的流水线,从数据采集到模型部署,每个环节都会直接影响最终识别效果。我见过太多人花了大把时间调模型结构…

2026/10/19 16:09:56 阅读更多 →
传输层核心机制与计算题突破:TCP/UDP协议期末考点全解析

传输层核心机制与计算题突破:TCP/UDP协议期末考点全解析

1. 传输层到底在整条网络链路里扮演什么角色1.1 从寄快递的类比说起:为什么需要传输层很多同学学到第五章的时候会有一种割裂感——前面几章讲物理层、数据链路层、网络层,好歹还能摸到网线、网卡、IP地址这些看得见摸得着的东西,一到传输层&…

2026/10/19 16:08:53 阅读更多 →
AIGC重复率高怎么办?十大检测改写工具实测与降重全攻略

AIGC重复率高怎么办?十大检测改写工具实测与降重全攻略

你有没有遇到过这种情况:同一段需求,丢给五个AI平台,回来的五段内容居然八九不离十;好不容易熬夜写完一篇稿子,丢进检测工具里,系统却标注你是“疑似AI生成”。这不是错觉,AIGC重复已经成为内容…

2026/10/19 16:08:53 阅读更多 →
垂直领域大模型建设全路径:从数据清洗到微调落地实战

垂直领域大模型建设全路径:从数据清洗到微调落地实战

做垂直领域大模型这件事,我不打算绕弯子。过去一年多,我陆续参与几条行业线的落地项目,覆盖零售、保险、医疗、制造,亲眼看着不少团队把“训练大模型”搞成一场烧钱大赛——模型训完了,demo演示很惊艳,上生…

2026/10/19 16:08:53 阅读更多 →
手游SDK接入最佳实践(进阶篇)最佳实践与踩坑记录

手游SDK接入最佳实践(进阶篇)最佳实践与踩坑记录

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

2026/10/19 16:08:53 阅读更多 →

日新闻

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

简介:本资源为电源技术方向的学术研究文档,聚焦介质阻挡放电型臭氧发生器电源的频率跟踪型PWM控制策略,面向电力电子、高压电源及臭氧应用领域的科研人员与工程技术人员。针对负载呈容性、等效电容随外加电压升高而增大、谐振频率随之漂移的难…

2026/10/19 0:00:47 阅读更多 →
Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

Vibe Coding 遇上 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/19 0:00:47 阅读更多 →
HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

【免费下载链接】HundredCV-Chat 项目地址: https://ai.gitcode.com/hf_mirrors/Jax-dan/HundredCV-Chat 点击查看 免费下载 HundredCV-Chat 是一个由 100 位青年简历数据开发而来的中文多轮对话数据集,共包含 24,750 组日常闲聊对话和 4,871 个话题标签…

2026/10/19 0:00:47 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/19 1:30:52 阅读更多 →

月新闻

我发现了一个新思路:用 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/19 2:54:50 阅读更多 →
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/19 9:10:22 阅读更多 →
黑夜航拍船只数据集训练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/18 12:16:16 阅读更多 →