news 2026/10/13 7:03:03

第53篇:标记标绘-点位标记与清单——不写高度,点就整整齐齐埋进地里

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
第53篇:标记标绘-点位标记与清单——不写高度,点就整整齐齐埋进地里

上周帮一个做应急调度的朋友看代码,他给我发来一张截图:Excel 里 12 行点位,名称那一列,12 个全叫 “北大门”。

我第一反应是他把名称写错了。结果他说,原始点位名明明是 “一号点位” 到 “十二号点位”,是自己在属性里加了一条 “名称 = 北大门”,导出之后就变成这样了。

我说,行,那把代码给我看看。这一看,就是一整天。

我们上次做的事情,是从热力图那口锅里往外舀数据。今天换个方向——先把一个个点标到球上,再让它变成一张能交出去的表。

点位标记这东西,看着是标绘里最不起眼的一块:点一下地图,球上多一个点,旁边列个清单,导出个 CSV 完事。可真正把它接到业务上,问题全在"两份数据"之间——球上那份是给眼睛看的,清单那份是给人交接的,导出那份是要进别人 Excel 的。三份数据只要有一处口径不一致,交付的时候就等着挨问吧。

本猿在项目里被问过最狠的一句是:“这个点和表里第 5 行,是同一个点吗?” 当时我只能盯着屏幕数点。

大家好,我是 Cesium 酱(也可以叫我 “本猿”),一名在 WebGIS 领域摸爬滚打多年的前端开发者。

这几天降温,码字的时候手边一直放着一杯热水。写到第三节的时候水凉了,我也没喝——因为正在跟disableDepthTestDistance较劲。

本文目标:从拾取一个坐标出发,把 “球上点 / 清单 / 导出” 这三条链路的实现拆开,并且盯住它们中间那几处会悄悄丢东西的地方——精度、样式、属性名,一个都不放过。💪

💡 本文涉及的全部代码均已开源,完整的仓库地址见文末,可自行取用、随意魔改。


一、痛点与场景:为什么点位清单这么容易出错?

🎯 背景现状:点位标记是标绘功能的起点,也是所有"图上作业"的落点。它看起来只是"一个点",但它同时承担三个身份:屏幕上的图形、界面里的数据、文件里的记录。字段一多、点位一多,这三个身份就开始各说各话。

典型场景里,它要同时满足这些需求:

  • 🖱️两种录入方式:手动敲经纬度(精确到小数点后 6 位),或者直接在地图上点一下拾取
  • 🏷️每条点位带自定义属性:闸机、摄像头、责任人、备注……字段是用户自己加的,代码事先不知道有哪些
  • 📋清单要能操作:跳转定位、改名、编辑属性、单条删除、一键清空
  • 📤导出要能交接:CSV 给 Excel 打开不能乱码,Excel 要给同事填数字、能参与计算

🤦 传统痛点:

常见做法问题
只画点,不存数据点画完就"死"在球上了,清单是空的,导出无从谈起
清单和球各存一份两套数据两处维护,删点、改名总有一边没跟上
坐标直接toFixed(5)存看着整洁,实际最坏丢掉半米多;再导出一次,误差还会叠加
属性名由用户随便填撞上内置列名(名称 / 经度 / 纬度)就把正经数据顶掉,且不报错

💡 本文方案:

1️⃣一份记录、两个出口:每次添加先建一条PointRecord进清单,再照它建一个Entity上球;球上的点只负责"显示",清单才是唯一事实源。

2️⃣拾取的点地坐标只留经纬度:pickCartographic给出经纬高,写进记录时只记longitude/latitude,高度交给heightReference在渲染期决定——听上去很合理,下一节我们看它会丢什么。

3️⃣导出走两条独立路径:CSV 手写字符串拼接(快、体积小),Excel 交给 SheetJS(能当数字用、列宽友好),两条路各自处理属性列。

✨ 一句话总结:把点位当成一份 “记录 + 一个投影”,用清单守住唯一事实源,再让导出把这份记录翻译成别人看得懂的表格。


二、核心技术原理:一份记录、两个出口

2.1 数据结构:清单里到底存了什么

整个模块的数据结构只有两个类型,很克制:

// 源码级剧透:src/cases/point-markers/PointMarkersDemo.vuetypePointAttribute={key:string// 属性名,用户自己填,代码不校验value:string// 属性值,一律按字符串存}typePointRecord={id:string// point-{时间戳}-{自增序号}name:string// "点位1"、"点位2"……longitude:number// 完整 double,不做任何截断latitude:number// 完整 double,不做任何截断attributes:PointAttribute[]// 自定义属性,顺序即录入顺序}

几个刻意的选择:

  • 📝属性值一律是字符串:用户填的是"闸机"不是 1,是"A-12"不是 12,导出时 CSV 侧原样输出、Excel 侧也原样输出,避免中途被当数字处理掉前导零
  • 🔢经纬度存完整 double:界面显示几位、导出几位,那是"格式化"的事,不在存储层做截断
  • 🎯id用时间戳 + 自增序号:point-1735234567890-3,同一毫秒内连加两个点也不会撞 id —— Cesium 的entities.add()对重复 id 会直接抛错(实测DeveloperError: An entity with id point-1 already exists in this collection.)

2.2 核心流程:一次添加要写两份数据

从"用户点了一下地图"到"球上出现一个点、清单里多一行",中间的完整链路是这样的:

核心亮点:第 ④ 步那一个分叉是整篇的关键 🔪 —— 同一个addPoint里,既要往points[]里塞一条记录,又要往viewer.entities里加一个实体。这两份数据从此各自演化:清单里改名不会动球上的标签,球上的样式改面板也不会回头改已存在的点。这不是 bug,但它是后来所有"对不上"的源头。

2.3 球上那一份,为什么用 Entity 的 point + label

"在地球上标一个点"至少有四条路,我们选了最省事也最容易被误解的那条:

方案原理成本适用
Entity+point+label数据源层,两个图形挂在同一个实体上,共享一个position🐢 每个点一个实体,千级尚可点位需要对号入座、要能编辑(本文)
PointPrimitiveCollection渲染层集合,一次 Draw 画一堆点⚡ 万级轻松纯展示、无交互、无清单
BillboardCollection用图片当点,能做各种图标⚡ 万级轻松需要自定义图标、要缩放质感
EntityCluster点上再叠一层聚合🐌 聚合本身有开销点位过密、需要"合并成数字"

选Entity的理由很直白:我们的点要能被"认领"。清单里点"跳转",得知道跳到哪个实体;点"删除",得把对应的实体从场景里摘掉。Entity自带 id 和统一的位置,配一个Map<id, Entity>就能把两份数据对上号。

简单说:Entity是"带户口本的点",PointPrimitive是"打工的点"——便宜、快,但叫不出名字。


三、代码实战:三个关键实现

⚠️温馨提示:本文的实现范围是"点位标记 + 清单管理 + 导出"这条最小闭环,不包含拖动改位置、撤销栈、CZML 持久化(这些留给后面的篇目)。另外,示例底图走 Bing 影像,坐标拾取复用measure-lib里的三级降级方案。

3.1 addPoint:一次点击,写下两份数据

functionaddPoint(longitude:number,latitude:number,name?:string):void{if(!viewer||viewer.isDestroyed())return// 🔑 校验只有两步:是不是数字、在不在合法区间if(!isFiniteNumber(longitude)||!isFiniteNumber(latitude)){flashStatus('经纬度数值无效,请检查输入');return}if(longitude<-180||longitude>180||latitude<-90||latitude>90){flashStatus('经纬度超出有效范围(经度 ±180,纬度 ±90)');return}constrecord:PointRecord={id:`point-${Date.now()}-${nextIndex}`,name:name??`点位${nextIndex}`,longitude,latitude,attributes:[]}nextIndex+=1points.value.push(record)// 🌰 出口一:清单// 🌰 出口二:球上的实体constcolor=Color.fromCssColorString(MARKER_COLORS[(points.value.length-1)%MARKER_COLORS.length])constposition=Cartesian3.fromDegrees(longitude,latitude)// 🔑 高度缺省 = 0constentity=viewer.entities.add({id:record.id,position,point:{// 🔑 贴不贴地,建这一刻就定死了pixelSize:pointSize.value,color,outlineColor:Color.WHITE,outlineWidth:1.5,heightReference:clampToGround.value?HeightReference.CLAMP_TO_GROUND:HeightReference.NONE,disableDepthTestDistance:Number.POSITIVE_INFINITY},label:{// 🔑 标签写的是坐标,不是名称text:`${latitude.toFixed(6)},${longitude.toFixed(6)}`,font:'12px sans-serif',fillColor:Color.WHITE,outlineColor:Color.BLACK,outlineWidth:3,showBackground:true,backgroundColor:Color.fromCssColorString('#0a2030').withAlpha(0.75),verticalOrigin:VerticalOrigin.BOTTOM,horizontalOrigin:HorizontalOrigin.CENTER,pixelOffset:newCartesian2(0,-20),disableDepthTestDistance:Number.POSITIVE_INFINITY}})entityMap.set(record.id,entity)selectedId.value=record.idclearStatus()}

🔑 关键点说明:

  • Cartesian3.fromDegrees(lon, lat)的缺省高度是 0,也就是椭球面。点地拾取得到的Cartographic里是带height的,但写进record时只留了经纬度 —— 于是这个高度在数据层就没了。实测(探针跑 Cesium 内核):地形高 50 m 处,不贴地的点和真位置相差整整50.000 m;1000 m 处差1000.000 m,一点折扣都不打。
  • 颜色不是跟点走的,是跟"添加时的下标"走的:MARKER_COLORS[(points.length - 1) % 8]。8 色循环,只要中间删过点,下标就错位。实测:加 3 个点 → 删掉第 2 个 → 再加 1 个,新点拿到的下标是 2,#4a9eff直接和现存的"点位3"撞色。
  • outlineWidth: 1.5在贴地路径上会被Math.round:PointVisualizer处理heightReference非 NONE 的点时,会走 Billboard 分支,outlineWidth和pixelSize都会先Math.round()(这个坑在第 33 篇拆heightReference时已经领教过一次)。所以 1.5 实际是 2。
  • disableDepthTestDistance给的是Infinity:注意它是平方之后再进 shader 的。BillboardCollection里一行disableDepthTestDistance *= disableDepthTestDistance,Infinity 平方还是 Infinity,紧接着被换成-1.0这个哨兵值送进 shader;顶点着色器里disableDepthTestDistanceSq < 0.0成立 →enableDepthCheck = 0,并把gl_Position.z直接压到近平面。结果就是:这个点永远画在最前面,山挡不住它,地球背面也挡不住它。想用有限值的人要小心——传 1000,实际生效的分界是 √1000 ≈ 31.6 m。

nextIndex只增不减,所以清空之后再添加,第一个点叫"点位 5"(实测),而不是从"点位 1"重来。

3.2 样式开关为什么"只管新点"

界面上有两个样式开关:点大小滑块(4 ~ 24)和贴地开关。它们看起来应该"实时生效",但实际只影响之后新建的点:

pointSize.value=24// 面板里的 ref 变了clampToGround.value=false// 开关也拨了// 但是——已存在的实体身上是这样的:// entity.point.pixelSize → ConstantProperty,getValue() 恒为构造时的 10// entity.point.heightReference → ConstantProperty,恒为构造时的 CLAMP_TO_GROUND

🔑 关键点说明:

  • Entity在构造时会把传进去的普通值包成ConstantProperty。实测确认:entity.point.pixelSize.constructor.name === 'ConstantProperty'、isConstant === true,getValue()永远是10;同一个位置上新建一个点,getValue()才是24。
  • PointVisualizer每帧都会读一次Property.getValueOrDefault(pointGraphics._pixelSize, time, ...)—— 也就是说渲染侧是"每帧问一次",只要 Property 变了就会跟着变。问题不在 Cesium,在我们没去写回这个 Property。
  • 想让滑块真正实时,要么遍历entityMap一个个entity.point.pixelSize = new ConstantProperty(v),要么一开始就用CallbackProperty(() => pointSize.value, false)把它挂成"每帧求值"。

这一条不写进正文容易挨骂,因为用户的第一反应一定是"我拖了滑块,为什么没反应"。

3.3 属性去重:同名的 key,后者胜

属性编辑里允许用户加任意多条"属性名 = 属性值",保存时会做一次去重:

functionsaveEdit():void{constrecord=points.value.find((p)=>p.id===editingId.value)if(!record){closeEdit();return}constname=editName.value.trim()if(!name){flashStatus('名称不能为空');return}// 🔑 名称是必填constcleaned:PointAttribute[]=[]constseen=newSet<string>()editAttrs.value.forEach((attribute)=>{constkey=attribute.key.trim()if(!key)return// 🔑 空属性名直接丢if(seen.has(key)){constexisting=cleaned.find((item)=>item.key===key)if(existing)existing.value=attribute.value// 🔑 重名:后来的值覆盖先前的return}seen.add(key)cleaned.push({key,value:attribute.value})})record.name=name record.attributes=cleanedcloseEdit()flashStatus('点位信息已更新')}

🔑 关键点说明:

  • 去重是"后者胜":同名的两条属性,最后一条的值留下来。这个策略本身无所谓对错,但要在 UI 上说清楚,否则用户以为"第一条优先"。
  • 属性名只trim,不做任何合法性校验:它可以是空的(丢)、可以是空格(trim 后空 → 丢)、也可以叫 “名称”——而 “名称” 恰好是导出时的内置列名。这就是开头那张截图的成因,下一节单独说。
  • 改名字不会同步到球上的标签:label.text写的是坐标字符串,record.name只活在清单和导出里。所以"我在清单里把点位 3 改成 3 号闸机",球上那个标签纹丝不动——它在设计上就不是名称标签。

3.4 导出:两条路,各自的脾气

functioncsvCell(value:string):string{// 🔑 只转义引号、逗号、换行return/[",\n]/.test(value)?`"${value.replace(/"/g,'""')}"`:value}functionexportCsv():void{constkeys=attributeKeys()// 全部点位属性名的并集,按首次出现顺序constheader=['序号','名称','经度','纬度',...keys].join(',')constrows=points.value.map((p,i)=>{constattrs=attributeMap(p)return[i+1,p.name,p.longitude.toFixed(6),p.latitude.toFixed(6),...keys.map((key)=>attrs[key]??'')].map((cell)=>csvCell(String(cell))).join(',')})constblob=newBlob([`\ufeff${header}\n${rows.join('\n')}`],{type:'text/csv;charset=utf-8'})// 🔑 BOM 必须有,否则 Excel 打开中文乱码downloadBlob(blob,'点位清单.csv')}functionexportExcel():void{if(points.value.length===0){flashStatus('点位清单为空,无可导出的数据');return}constkeys=attributeKeys()constdata=points.value.map((p,i)=>{constattrs=attributeMap(p)constrow:Record<string,number|string>={序号:i+1,名称:p.name,// 🔑 内置列先写经度:Number(p.longitude.toFixed(6)),纬度:Number(p.latitude.toFixed(6))}keys.forEach((key)=>{row[key]=attrs[key]??''})// 🔑 属性列后写 → 能覆盖returnrow})constsheet=XLSX.utils.json_to_sheet(data)constworkbook=XLSX.utils.book_new()XLSX.utils.book_append_sheet(workbook,sheet,'点位清单')XLSX.writeFile(workbook,'点位清单.xlsx')}

🔑 关键点说明:

  • CSV 用toFixed(6),Excel 用Number(toFixed(6)):前者写进文件是字符串,后者在 xlsx 里是真的数字单元格(实测t: 'n'数字单元格,v = 116.000123)。要参与计算就用 Excel 那条,要绝对原样就留着 CSV 那条。
  • CSV 表头的属性列是"拼上去"的:['序号','名称','经度','纬度', ...keys]—— 用户属性名如果也叫 “名称”,表头就是序号,名称,经度,纬度,名称,类型,两列同名,谁也不覆盖谁,只是多一列。
  • Excel 反过来,是"覆盖"的:row['名称'] = p.name先写,属性列row[key] = attrs[key]后写 —— 同名时后写覆盖先写。实测:一号点位在导出的表里变成了北大门,而且不报错、不提示,肉眼还得靠对比才能发现。
  • csvCell的转义清单里没有\r:值里带一个回车符(比如从别处粘贴进来的多行备注),CSV 就在那一格断行了。=、@、+、-开头的值也不会被处理 —— 在 Excel 里这些是公式前缀,属于经典的 CSV 注入面,导出给外部之前最好加一层前缀过滤。
  • __proto__这个属性名会出事:row['__proto__'] = 'x'不是赋值而是改原型(字符串会被静默忽略),实测该属性在Object.keys()里根本不出现;而 CSV 那条路读attrs['__proto__']拿到的是Object.prototype,String()之后写出[object Object]。同一个数据,两条导出路径给出两种结果。

四、功能亮点与性能优势

先把真实跑起来的画面放上来:

4.1 清单与球上的双向操作

  • 🎯跳转:camera.flyTo到该点上方8000 m,飞行时长 1 s —— 高度是写死的,点位在城市里刚好,跑到高原地带就会觉得"怎么贴这么近"
  • ✏️编辑:打开对话框改名称与任意条属性,经纬度显示为只读(toFixed(6))——设计上不允许拖动改位置
  • 🗑️删除:从entityMap里拿实体entities.remove,同步splice清单,两边一起走

4.2 一个坐标,四种精度

这是本篇最容易挨问的一块:同一个坐标,在四个地方露出的精度不一样。实测数据(北京 39.9975°N,取 12 位小数的拾取坐标):

出口写法最坏偏差平均偏差
球上Entity.position完整 double0.0000 m0.0000 m
标签text/ 导出toFixed(6)0.0584 m0.0256 m
清单显示toFixed(5)0.5977 m0.3936 m
导出后再导入toFixed(6)往返0.0663 m0.0432 m

顺带把"一位小数到底值多少米"记下来:在北京,1e-5°是纬度 0.5552 m、经度 0.4270 m;1e-6°是纬度 0.0555 m、经度 0.0427 m。1°纬度实测 111042.8 m,1°经度实测 85395.9 m;后者比 111320 × cos39.9975°(85279.2 m)多出 116.7 m,因为 WGS84 的卯酉圈曲率半径(6386975.2 m)比赤道半径大 0.139%。

结论很朴素:清单里少给的那一位,最坏就是半米多。给同事核对点位、给施工队找井盖,半米可能刚好压在马路中线两侧。

4.3 导出性能:CSV 比 Excel 快一个量级

测试条件是本猿自己的机器、纯 CPU 侧生成(不含浏览器下载动作),属性固定两列:

点位数CSV 生成CSV 体积Excel 生成Excel 体积
1000.163 ms ⚡4.6 KB7.316 ms36.6 KB
10001.220 ms ⚡48.6 KB14.351 ms234.4 KB
50003.689 ms ⚡255.6 KB61.932 ms1152.3 KB
100008.290 ms ⚡514.4 KB103.522 ms2299.8 KB

10000 个点:CSV 8.29 ms / 514 KB,Excel 103.5 ms / 2.3 MB ——慢 12.5 倍、大 4.5 倍。所以界面上的策略是"两个都给",要快给 CSV,要格式给 Excel。

顺带一组实体侧的成本(new Entity+EntityCollection.add,不含渲染):

点位数总耗时单点耗时
1003.739 ms0.03739 ms
100022.771 ms0.02277 ms
5000122.145 ms0.02443 ms

每个点还要跑一次Color.fromCssColorString(实测单次 0.000361 ms)和一次withAlpha(0.000344 ms)——所以颜色解析和背景色如果是固定值,提到循环外面会更舒服。

核心优势:

  • 🚀纯前端运行:拾取、清单、导出全在浏览器里,零服务器成本,点位数据不出本机
  • 🧩属性结构自由:属性列取并集后统一对齐,缺属性的行留空,不要求所有点字段一致
  • 📦两条导出路径:CSV 兼容一切文本工具,Excel 直接给业务同事填数
  • 🎯清单是唯一事实源:球上负责"看",清单负责"改"和"交",职责划得清

五、小贴士:避坑指南

🎯 关于坐标精度

点位数据一旦落进业务,精度就不只是"显示几位"的问题。⚠️ 重要提示:存储层永远存完整 double,只在"给人看"和"写文件"这两处格式化,且两处保持一致。

  1. 清单列表里至少显示到 6 位小数,或者干脆显示"经纬度 + 到最近参考点的距离"
  2. 导出前问一句"这份表拿去干什么":核对位置 → CSV 留 6 位;填数据 → Excel 用数字单元格
  3. 导入回项目时,6 位小数的往返偏差最坏 0.0663 m,可以接受;但如果对方把坐标粘进 Excel 又被自动保留成 2 位小数(“116.36”),那就是几公里的事了

📦 关于导出与属性命名

同名的坑有两种表现,值得单独贴一张图(上面这张就是那两张表的实录)。

⚠️ 重要提示:给用户的属性名加一层保留字校验,比出了事再解释便宜得多。

  1. 保留字至少包含:序号、名称、经度、纬度,一律拒绝或自动改名(如"名称(自定义)")
  2. 属性名不放空、不含首尾空格、长度给个上限(20 字够用)
  3. 拒掉__proto__、constructor这类原型链上的名字——它们在 JS 里不是普通字符串键
  4. 属性值导出前过滤=、+、-、@开头,或者统一加一个前置单引号
  5. CSV 的单元格转义要补上\r

⚡ 关于性能

⚠️ 重要提示:Entity的每一个Property(pixelSize、color、heightReference……)在赋值时都会被包装成ConstantProperty。想改已有点的样式,必须显式写回 Property,别指望改一个外部的ref就能生效。

  1. 1000 点以内用 Entity 直管没问题;上万点应该换PointPrimitiveCollection+ 外置数据数组
  2. 颜色、背景色这些固定值提到循环外
  3. 批量改样式时把entityMap遍历一次写回,别在watch里逐点触发
  4. disableDepthTestDistance: Infinity很方便,但每个点都关掉深度测试意味着它们之间的遮挡关系也没了,点位密集时会互相压字

🗺️ 关于高度与贴地

⚠️ 重要提示:Cartesian3.fromDegrees(lon, lat)的高度缺省是 0(椭球面),而地图上拾取到的点是带地形高度的。这两个值不是一回事。

  1. 需要"浮在地形上方"的点,用HeightReference.RELATIVE_TO_GROUND(它是叠加,不是替换)
  2. 需要"站在地形上"的点,用CLAMP_TO_GROUND(替换高度,但依赖深度纹理)
  3. 如果你的场景没加载地形,CLAMP_TO_GROUND贴的就是椭球面——和NONE在视觉上没区别,容易误判成"开关坏了"

坦诚说,这个工具目前还不完美:拖动改位置、批量导入、撤销栈、CZML 持久化都还没有;属性编辑只支持字符串值,没有类型与校验。但"球上一个点"和"表里一行数据"之间的那条线,这篇算是把它理直了。


六、写在最后

一个点位,在屏幕上它是一颗圆点,/ 在清单里它是一行字,在文件里它是一格数据。/ 我们总以为标个点是"画一下",/ 直到有人问:球上那个点,是表里第几行?

从 #33 拆完heightReference的两条渲染路径,到今天把清单与导出接上,标绘线终于把"点"这件事讲完整了。

现在,Cesium 上的一个点,既能被眼睛看见,也能被表格接住了。🎉

如果你有更好的想法,或者在实际使用中遇到问题,欢迎来 GitHub 提 Issue!


开源地址:https://github.com/jianlei-wang/CESIUM-TREASURE-BOX

🌟 项目已开源,欢迎 Star!(你的 Star ⭐ 就是对我最大的支持!)

仓库公开,直接到 GitHub 取即可。本篇涉及的目录:

  • src/cases/point-markers/—— 演示面板与清单 / 导出逻辑(PointMarkersDemo.vue,478 行)+index.ts(id: point-markers/title: 标记标绘-点位标记与清单/category: draw/tag: 点位管理/updatedAt: 2026-08-28)
  • src/cases/measure-lib/pick.ts——pickPosition三级降级拾取 +pickCartographic(共 28 行,与量测线的几篇共用同一份)

Cesium酱の百宝箱 · 第 53 篇
点落球成影,行归表有踪
2026 年深秋,与你共探标绘之尺

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/13 7:03:02

元宝 LeetCode 329. Longest Increasing Path in a Matrix Java Implement

Core idea: DFS memoization. The grid is a DAG — every edge goes from a smaller value to a larger one, so no cycles and no need for a “visited” set. “dfs(r,c)” longest increasing path starting at “(r,c)”, computed once per cell and cached. class So…

作者头像 李华
网站建设 2026/10/13 7:02:56

AI 数学基础知识

范围&#xff1a;AI 学习阶段 A 的数学三条主线——“矩阵线性变换、梯度方向导数、交叉熵信息论”——的知识点展开。面向 20 年工程经验读者&#xff1a;不啃证明&#xff0c;每个知识点只回答两个问题——它在深度学习里对应什么 / 后面哪个实验会用到。结论先行 AI 所需的数…

作者头像 李华
网站建设 2026/10/13 7:02:55

Redis 从入门到实战(12):性能调优与生产避坑(完结)

性能调优与生产避坑 本篇命令全部实测(Redis 7.4.11)。收官篇:把前面 11 篇的知识变成线上战斗力。 一、性能排查的三板斧 遇到 Redis 慢,按这个顺序排查: #mermaid-svg-c3qvFd5v0vxNscNd{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px…

作者头像 李华
网站建设 2026/10/13 7:02:06

幻 觉

很多新概念都是换汤不换药&#xff0c;工作的本质上并没有什么改变。AI的差距在于卡和数据&#xff0c;人的差距大概是方法论。 下面这一套东西api&#xff0c;打通了完全可以agent化。 用一个前辈中肯的话说&#xff0c;现在AI方向的一些工作&#xff0c;大部分人跟时间是在处…

作者头像 李华
网站建设 2026/10/13 7:01:49

项目管理到底是在管人还是管事?具体项目进程中引发的思考

1. 引言周五下午&#xff0c;项目例会开到一半&#xff0c;会议室里的气氛却越来越僵。开发组长老张拍着桌子说&#xff1a;「我早就说过&#xff0c;这个需求根本排不进这轮迭代&#xff0c;你们只盯着进度表&#xff0c;谁管过我们组已经连续加班三周了&#xff1f;」坐在对面…

作者头像 李华
网站建设 2026/10/13 7:01:48

Allegro 动态铜皮不更新与焊盘不连接问题排查记录

Allegro 动态铜皮不更新与焊盘不连接问题排查记录Allegro 动态铜皮不更新与焊盘不连接问题排查记录一、问题起因1. 动态铜皮大面积过期2. GND 焊盘无法连接铜皮二、排查过程问题 1&#xff1a;铜皮无法更新&#xff08;Out of date&#xff09;第一步&#xff1a;排除数据库损坏…

作者头像 李华