InstallShield中附属MSI的安装卸载实战与调试

前两天帮人看一个安装包的诡异问题:主程序明明是装成功了,但被一起带上的那个MSI组件死活装不上,卸载的时候还赖在系统里不走。这个场景搞Windows安装包的朋友应该不陌生——主程序用InstallShield打成一个安装包,安装时还要顺带把另一个独立的MSI组件一起装上,卸载时再把它卸干净。看起来挺简单一个需求,实际上从时序、权限、静默参数到卸载残留,每一步都有暗坑。

这篇文章按我自己的实际做法来写。主要包括:动手前怎么分析附属MSI和主程序的关系,在InstallShield的Basic MSI项目里怎么把附属MSI挂进安装流程,怎么写卸载逻辑让它能跟着主程序一起退出,以及我在这个过程中踩过的坑和沉淀下来的调试套路。无论你是刚接触InstallShield的新手,还是已经被这种嵌套安装折磨过的老手,应该都能从里面找到能直接抄作业的部分。

1. 动手之前想明白:附属MSI和主程序到底是什么关系

1.1 先分清“附属”的四种常见用法

接到“帮我加个附属MSI安装”的需求,第一反应别急着打开InstallShield,先问清楚这个附属MSI属于哪种类型。我遇到过的场景大致有四种:纯依赖型、业务模块型、可选增强型、部署引导型。纯依赖型指的是主程序依赖某个运行库或驱动包,这个包恰好以MSI形式分发,这种属于典型的前置条件,应该在安装开始之前就把它处理好,装不上就应该终止主程序安装。业务模块型则是附属MSI是业务软件的一个独立模块,比如数据上报服务、帮助文档组件、供其他系统调用的接口服务,它和主程序算平级关系,只是需要用同一个主安装包一次性部署。可选增强型是附属MSI作为用户可选装的增强功能,装不装由用户决定,这种情况一般做成InstallShield的Feature,让用户在向导里勾选。部署引导型则是附属MSI其实是一个完整的第三方软件包,你只是希望主程序安装时把它带上去,并不需要给它单独设计交互。

我这次碰到的需求是业务模块型和部署引导型的混合体:主程序是一个业务系统,附属MSI是第三方提供的数据上报模块,第三方只给了MSI成品,没有源工程,不能改它的打包逻辑,只能原样调用。这个限制直接决定了后面的方案选择。在动手之前先把类型搞清楚能省下大量无用功,尤其是影响卸载逻辑的设计——纯依赖型通常主程序卸载时把它一并处理,业务模块型则需要你明确决定是跟主程序绑定卸载还是独立卸载,这两者在InstallShield里的写法完全不一样。你要是连这个都没想清楚就动手,后面返工是必然的。

1.2 三条技术路线的选型对比

在InstallShield里,实现“主程序之外另装一个MSI”比较常用的有三条路线,我分别试过,直接给出对比:

路线适用项目优点缺点结论
Suite/Advanced UI项目Suite项目多个MSI作为不同组件统一管理,安装卸载顺序自动编排需要把主程序从Basic MSI改造成Suite模式,工程改动量大适合全新项目,不适合老工程
先决条件(Prerequisite)Basic MSI项目安装开始前自动检测并安装,非常干净主要用于系统运行库这类前置依赖,不适合业务附属模块,也难以传业务参数适合VC++ Runtime等系统组件
Custom Action调用msiexecBasic MSI项目灵活控制安装时机、参数、回滚行为,不用改主工程结构安装、卸载逻辑都要自己写,出错定位成本高最终选用的方案

我最后选了Custom Action这条路线,一是主程序是已上线很久的Basic MSI工程,改成Suite模式风险太大,没必要为一个小附属模块动整个项目结构;二是附属MSI必须在主程序文件落盘之后、注册表配置生效之前完成安装,这个时机精度只有Custom Action能给出来。如果你做一个全新项目,且从一开始就知道会有多个MSI一起发布,那真的建议直接用Suite项目,它天生就是干这个的,能省掉后面一大堆手工处理逻辑。至于Prerequisite,它更适合VC++ Runtime这类的系统级前置组件,业务附属模块放进去既不合适,传参也麻烦。

2. 把附属MSI挂进安装流程:一步步实操

2.1 将MSI文件加入工程并放到干净的子目录

先把文件放的位置定下来。在Basic MSI项目里,要让第三方MSI随主安装包一起发布,并在安装时能被命令行找到,需要把它作为文件添加到某个Feature下面。操作路径是:打开InstallShield工程,展开Installation Explorer里的Organize Your Setup -> Feature,选中主功能节点,进入Files选项卡,右键选择Add Files,把MSI文件选进来。我习惯把文件放在INSTALLDIR下的一个单独子目录里,比如[INSTALLDIR]\Redist,而不是直接放在INSTALLDIR根目录。原因有两条:第一,路径越短越干净,命令行引用时不容易踩空格或路径转义的坑;第二,后续如果要在卸载时引用这个MSI文件,它固定在子目录里,代码逻辑会非常清晰。

添加完之后,务必看一眼这个文件所属的组件(Component)。如果组件被设置成了条件安装或延迟安装,文件可能根本不会在目标机器上落盘,后面所有调用都会白费。我一般让这个文件跟随主Feature在安装初期就复制,和主程序的核心文件同一批处理。界面里可以选中该文件后在右侧属性面板确认它归属的Feature和Component,如果发现不对,就手动拖拽到正确的Feature下面。

2.2 创建Custom Action触发静默安装

文件放好了,现在建Custom Action。在InstallShield左侧找到Behavior and Logic -> Custom Actions,在右侧空白区域右键,选择Action Type -> Launch an Application,新建一个Action。名字我习惯起成InstallAuxMsi,一眼就能看出它在干什么。重点配置如下:Source填[SystemFolder]msiexec.exe,这是安装时会被替换成系统msiexec完整路径的属性;Command Line填 /i "[INSTALLDIR]Redist\MyAux.msi" /qn /norestart /l*v "[TempFolder]MyAux_install.log";Working Directory留空;In-Script Execution选择After Install Files,也就是文件复制完成之后。

关于In-Script Execution,不同版本的InstallShield在措辞上会有差别,但本质就是问这个Action在安装流程的哪个阶段执行。我把执行时机定在文件复制完成之后,是因为附属MSI运行时很可能需要主程序的某些文件已经存在;同时它又必须在主程序写注册表之前跑完,避免两边同时写注册表造成冲突。另一个容易被忽略的细节是:某些版本里Custom Action有一个Include in script execution之类的勾选框,必须勾上,否则这个Action只是被定义了,实际根本不会执行。我第一次就是漏了这一步,结果整个Action形同虚设,安装过程中完全不触发,查了很久才反应过来。

2.3 命令行参数的正确写法和执行时机

命令行参数虽然简单,但有几个细节值得展开讲。第一,路径必须用英文双引号包起来。对InstallShield有点概念的朋友都知道,INSTALLDIR在用户选择非默认路径时完全可能带空格,不加双引号msiexec会直接截断路径,日志里就出现“无法访问该文件”之类的报错。第二,/qn是静默模式,整个安装过程不显示任何界面;如果需要用户看到进度条,可以换成/passive,被动模式显示进度但不需要交互。我自己的建议是:调试阶段先用默认模式而不是/qn,肉眼能看见GUI信息定位会快很多,确认无误之后再换回静默。第三,强烈建议加/l*v参数生成详细日志,日志文件路径可以写固定路径也可以写属性,我习惯写成[TempFolder]MyAux_install.log,Windows临时目录对安装操作来说一般没有访问权限问题。第四,/norestart参数很实用,附属MSI有时会要求重启系统,加上这个参数可以避免重启提醒打断主安装流程。

最终我的完整命令行长这样:

[SystemFolder]msiexec.exe /i "[INSTALLDIR]Redist\MyAux.msi" /qn /norestart /l*v "[TempFolder]MyAux_install.log"

2.4 权限上下文:最容易忽略的暗坑

这个坑我印象最深,也是很多人“点击安装没反应”的根因。Custom Action建好后,如果附属MSI是要安装到Program Files或写入系统注册表的系统级组件,而Action此时运行在“当前用户”上下文,Windows Installer会拒绝写入,安装直接失败。常见的表现是:主程序装完了,附属MSI没装上,但InstallShield总体还是提示安装成功——因为Custom Action的失败不一定被算进主安装包的失败结果里。这一点在设计阶段就要有意识地规避,否则客户现场装到一半你才发现附属模块压根没上去,就很被动。

解决方法是:在Custom Action的属性里找到Run As或Security相关设置,让它以Local System账户运行。InstallShield里通常在In-Script Execution或Security标签下能找到对应选项。设置完再跑一次,问题基本消失。但也要注意,Local System是个超级账户,不映射到任何已登录用户,如果附属MSI的安装逻辑里有“设置当前用户数据”这类操作,用Local System又可能产生副作用。遇到这种情况可以折中:把Action拆成两个,系统级操作走Local System,用户级操作走当前用户上下文,各管各的域。

3. 卸载也要安排明白:让附属MSI跟着主程序退出

3.1 卸载Action在哪里挂才合适

安装逻辑写完,千万别忘了卸载。很多人到了测试才发现,主程序卸载完了,附属MSI还留在控制面板的程序和功能列表里。这不是bug,是压根没写卸载逻辑。在Basic MSI项目里,卸载流程本质上也是同一套脚本在跑,只是走的是Uninstallation分支。我需要在卸载流程里挂一个Custom Action,命名为UninstallAuxMsi,触发msiexec /x。放在哪个阶段?我比较建议放在主程序卸载的中段,在主程序核心文件和注册表项还没有被删光之前。原因很简单:如果附属MSI在卸载时需要读取主程序的某些键值,你太早删主程序它就查不到;反过来,如果附属MSI卸载后会重写某些共用注册表项,你又得确保主程序卸载顺序不会覆盖它的结果,这个顺序需要和附属MSI的卸载逻辑对着看。

实操层面,在Custom Actions区域再新建一个Action,类型同样是Launch an Application,名字UninstallAuxMsi,In-Script Execution放在Uninstallation阶段的合适位置,通常选在主程序卸载脚本的前半段。不同版本菜单词略有差别,但核心思路就一句话:在主程序关键资源还没被删除的窗口期,把这个附属MSI卸掉。

3.2 卸载命令的两种姿势:路径优先还是ProductCode优先

卸载命令我给出两种写法。第一种,用MSI文件路径卸载:

[SystemFolder]msiexec.exe /x "[INSTALLDIR]Redist\MyAux.msi" /qn /norestart /l*v "[TempFolder]MyAux_uninstall.log"

第二种,用ProductCode卸载:

[SystemFolder]msiexec.exe /x {12345678-1234-1234-1234-123456789012} /qn /norestart /l*v "[TempFolder]MyAux_uninstall.log"

两种方法的取舍很清晰。用文件路径的好处是不用关心ProductCode,特别是第三方组件升级时ProductCode经常变,路径法一劳永逸;坏处是如果用户把主程序目录里的MSI文件删了,卸载就会失败。用ProductCode的好处是不依赖文件是否存在,坏处是你要在工程里维护这个GUID并保证它跟随第三方版本同步更新。我这次选的是文件路径,因为附属MSI跟随主程序一起部署,卸载时文件大概率还在,而且msiexec /x时会根据MSI文件里记录的产品信息自动匹配已安装的产品,避免卸错版本。如果要用ProductCode,怎么获取它呢?最简单的方法是用Orca之类的工具直接打开附属MSI,读Summary Information里的ProductCode字段,然后在InstallShield里把它定义成一个属性,命令行里用[属性名]引用,以后升级只改属性值就好。

3.3 卸载顺序与手动回滚

卸载顺序和安装顺序一样:依赖方先卸,被依赖方后卸。我当时的项目里主程序和附属MSI没有强依赖,但为了稳妥,还是把附属MSI的卸载放在了主程序核心数据还存在的时候,给调试留下余地。这里必须强调一个Windows Installer的默认行为:msiexec本身是有事务性的,如果附属MSI卸载失败,它会尝试回滚到卸载前状态。但是这个事务性跟InstallShield主安装包的事务是两条线,二者之间没有自动的原子性。换句话说,主程序卸载失败回滚,并不会自动把附属MSI带回已安装状态;附属MSI卸载失败回滚,主程序也可能已经卸掉了一部分。

如果你需要“主程序没卸干净,附属MSI也不能卸”这种强一致,只能自己在实现层做控制。通用做法是:在主程序卸载脚本的第一步检查附属MSI的卸载结果,失败就中断整个卸载流程并给出提示,确认成功后再继续卸载主程序。这个逻辑在InstallScript项目里容易实现,Basic MSI项目里则要借助自定义条件或序列化Action来模拟,复杂度确实高一些,但真遇到客户现场出现“半卸载”状态时,这点复杂度是值得的。

4. 复盘五个真实踩过的坑

4.1 静默参数被拆分导致弹窗

有一次我在命令行里把参数顺序写成了 /qn 放在路径后面、附加了/l*v,结果安装时还是弹出了MSI的界面。从语法上看好像没问题,反复看了几遍也没看出名堂,后来才知道,msiexec对参数顺序非常敏感,像/qn这类模式参数应该紧跟/i或/x主命令之后,中间不要插入其他开关。调换顺序之后,静默就恢复正常了。这个坑很隐蔽,因为界面弹出来的时候,第一反应往往是怀疑/qn是不是拼错了,很少有人第一时间去检查参数顺序。

所以我在项目里定了一个规矩:命令行保持固定格式,主命令在前,模式参数紧跟其后,再到安装包路径或产品代码,最后才放其他开关。比如/i /qn /x这种,永远让/qn紧跟在操作命令后面。养成这个习惯之后,这类参数解析的问题基本就绝迹了。

4.2 权限不足被1603劝退

有一次部署到一台普通机器上,主程序安装正常,附属MSI却始终装不上,查看msiexec日志,最终错误写着“安装过程中发生致命错误代码1603”。1603这个代码本身非常笼统,它只是“致命错误”的汇总,真正的原因要到日志中间段去找,而不是只看最后几行。我往下翻,看到CreateProcess失败以及权限相关的字样,确认就是权限上下文的问题。

那次处理起来很简单,把Custom Action的Run As改成Local System再重新打包,问题就解决了。但这件事给我的教训是通用的:遇到1603别慌,先去看日志中段,绝大多数具体的失败操作都会在那里留下记录。Windows Installer的详细日志是全文详尽的,里面记录了每一个文件操作、注册表操作和系统调用的结果,只要按时间戳从中间部分往后读,基本都能定位到是哪一步、哪个文件名、哪个返回值出了问题。很多人只翻日志最后几行,看到1603就以为事件结束了,其实真正的线索全在中间。

4.3 附属MSI先装但主程序依赖文件没落盘

另一个坑和时序有关。最初我把附属MSI的Custom Action放在“安装文件之前”执行,想着附属MSI先装好,主程序再装就不会冲突。结果跑完一看,附属MSI确实安装成功了,但它的功能模块运行时报“找不到某个文件”,原因是要用到的辅助库是主程序在文件复制阶段才释放的,优先级从一开始就算反了。

后来我把执行时机调整到After Install Files,确保主程序所有文件落盘后再触发附属MSI,问题就消失了。这个case的核心教训是:附属MSI的执行时机不要想当然地放在最前面,而是要根据“它依赖什么、什么时候需要这个依赖”来定。我当时要是先仔细看一下附属模块的安装说明,里面其实写了一句“需要主程序目录下的某个接口库存在”,就不会犯这个错。所以现在拿到附属MSI之后,我一定会先问提供方:它在运行时需要哪些外部文件?它是否需要在主程序注册表配置完成之后才能启动?这两个问题的答案基本就是执行时机的答案。

4.4 卸载残留导致二次安装失败

还有一次更隐蔽:主程序卸载干净了,但附属MSI没有跟着卸,系统里残留着注册表项和服务。这不仅只是“没卸干净”这么简单——下次用户重新安装主程序时,InstallShield安装附属MSI时会检测到目标机器上已经存在同名产品,然后跳过安装,结果业务模块功能就是坏的,而且如果不深入日志,你根本不知道为什么“装不上”了。

这个问题要从两方面治:一是卸载逻辑必须保证附属MSI被卸掉;二是安装逻辑最好加一个已安装检测,如果检测到同名产品已存在,要么提示用户先卸载旧版本,要么直接调用msiexec /x把旧版本卸掉再装。我后来在项目里加了检测逻辑之后,这个怪问题才算彻底止住。具体检测方法放到第五部分,这里先记住一个结论:附属MSI的安装,绝不能假设目标机器上一定是“干净”的。

4.5 定位问题的通用日志战术

最后分享一个我自己一直在用的排查固定套路,我觉得它比任何功能开关都值钱。遇到附属MSI安装或卸载失败的场景,我一般是按顺序查三层日志:第一层是InstallShield主安装包的日志,Basic MSI项目可以通过/log参数开启,InstallShield自身也有安装日志,这一层的目的是确认Custom Action是否真的被触发、在哪一步失败。第二层是msiexec的日志,也就是/l*v参数生成的那个文件,里面记录了MSI赋值的所有属性、每步操作的返回码,大部分问题都藏在这一层。第三层是Windows事件查看器里的应用程序日志,MsiInstaller事件会给出事件ID,可以用来快速判断最终结果是成功还是失败。

三层日志配合起来看,基本能回答“是没触发、触发没成功、还是成功了但业务有问题”这三个层次的问题。遇到异常先别急着改代码,把日志全部拉出来,对着时间戳一行行过,原因通常就自己浮出来了。我接触过一些同行,他们一看到1603之类错误就直接重做安装包,完全不看日志,这样不仅效率低,问题还可能反复出现。

5. 三个可以直接抄的小技巧

5.1 利用[属性]向附属MSI传递生产环境参数

有些附属MSI是支持外部传参的,比如安装目录、服务端口、数据库连接串。你可以在InstallShield里定义自定义属性,然后在命令行参数里引用。举个例子,在项目中新建属性MyPort,命令行写成:

[SystemFolder]msiexec.exe /i "[INSTALLDIR]Redist\MyAux.msi" APP_PORT=[MyPort] /qn /norestart /l*v "[TempFolder]MyAux_install.log"

运行时InstallShield会把它替换成实际值。好处是敏感参数不硬编码在安装包里,换成环境或升级版本时只改属性表。需要注意两点:传参的格式是“属性名=属性值”,值如果包含空格或特殊字符,要用英文双引号包住;而且这个传参要确认附属MSI本身支持这条属性路径,否则msiexec只是忽略它而不会报错,反而容易让人误以为传成功了,最后功能起不来又要排查半天。

5.2 用注册表判断附属MSI是否已安装

检测一个MSI是否已安装,最直接的办法是看注册表卸载项。Windows Installer在安装成功后,会在HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall下写入一个以ProductCode命名的键,64位系统上还会在HKLM\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall出现对应项。你可以通过自定义条件检查这个键是否存在,从而决定是否跳过安装或卸载。实际使用有几处要注意:这个检测最好在Local System上下文下做,因为HKLM对普通用户未必可读;还要先确认附属MSI是Per-Machine安装还是Per-User安装,如果是Per-User,卸载项会写到HKCU下,检测位置完全不同。另外,光看注册表卸载项也不完全可靠,某些专业的MSI会采用自定义Action清理注册表,卸载后这些键可能提前被删,所以检测逻辑要结合现场情况灵活调整。最稳妥的方式是搭配一个安装状态文件,两边都确认过了才做判定。

5.3 先测MSI再测InstallShield:分步验证法

最后这个方法我一直在用,分享给你。把附属MSI塞进InstallShield之前,先在命令行把它当作普通独立MSI完整测一遍,跑通之后再接入工程。具体步骤是:用管理员权限打开命令行,手动执行msiexec /i "附属MSI路径" /l*v "日志路径",确认MSI本身能装成功、能卸干净;然后确认它支持的静默参数、是否弹窗、是否强制重启;再确认它的ProductCode在注册表里的实际位置和你预期的检测方式匹配;全部通过后,再把调用逻辑写进InstallShield。这样做最大的价值是:最后如果安装包出问题,你可以非常自信地排除“MSI本身有问题”,把注意力集中到“调用方式是否写对”上。我在无数个排查加班夜里,就是靠这个分步验证法快速定位问题的。

反正现在每次接到这种“主程序附带安装一个MSI”的需求,我都会先问自己三个问题:这个MSI是必选还是可选、它和主程序的依赖方向是什么、出问题时靠哪层日志去查。把这三件事想清楚了,剩下的实际操作也就是加两个Custom Action、调好顺序和权限,基本不会出什么大问题。希望这篇能帮你少踩几个我踩过的坑。

相关新闻

HUSB351快充芯片:小体积PD协议芯片的工程落地逻辑

HUSB351快充芯片:小体积PD协议芯片的工程落地逻辑

1. 从“机乐堂”供应链变动看快充芯片选型的底层逻辑最近在整理一批快充配件的BOM清单时,注意到一个细节:某款标称30W PD快充的迷你插线板,其协议识别模块的丝印从常见的“FP6606”悄然换成了“HUSB351”。进一步交叉查证发现,该产…

2026/10/20 17:04:26 阅读更多 →
MVMD联合小波阈值去噪:多通道非平稳信号MATLAB实战

MVMD联合小波阈值去噪:多通道非平稳信号MATLAB实战

简介:这份资源面向信号处理、故障诊断与振动监测方向的学习者和研究人员,提供一套基于MATLAB的完整联合去噪方案,用于从含噪多通道信号中恢复原始信号。核心流程涵盖MVMD多元变分模态分解、基于相关系数的有效模态筛选、小波阈值去噪、模态重…

2026/10/20 17:03:25 阅读更多 →
Jenkinsfile编写五大致命难点与生产级避坑指南

Jenkinsfile编写五大致命难点与生产级避坑指南

1. 项目概述:Jenkinsfile不是“写脚本”,而是定义持续交付流水线的契约你有没有在某个周五下午三点,刚把 Jenkinsfile 提交到 Git 仓库,CI 流水线就突然卡在“Checkout”阶段,日志里只有一行红色报错:hudso…

2026/10/20 17:03:25 阅读更多 →

最新新闻

JMeter接口测试进阶:从参数化、断言到结果分析的核心能力

JMeter接口测试进阶:从参数化、断言到结果分析的核心能力

做接口测试这行,接触JMeter是早晚的事。早年我还在手工点接口文档、拿浏览器调试工具一个个试请求的时候,第一次用JMeter跑通一个几十线程的并发场景,那感觉确实挺新鲜。但说句实在话,从“会用JMeter”到“能把接口测试做出价值”…

2026/10/20 17:52:45 阅读更多 →
Java学籍管理系统毕业设计全攻略:技术栈选型、数据库设计与避坑指南

Java学籍管理系统毕业设计全攻略:技术栈选型、数据库设计与避坑指南

简介:基于Java与MySQL的大学生学籍管理系统毕业设计资源包,面向计算机相关专业毕业设计或课程设计的学生。功能覆盖学生信息、班级、院系、专业、课程、成绩、奖惩等管理模块,并设计视图、存储过程与触发器,用于查询学生成绩单、自…

2026/10/20 17:52:45 阅读更多 →
能力矩阵实战:从skills拆解到团队协作的完整指南

能力矩阵实战:从skills拆解到团队协作的完整指南

1. 从“skills”这个词说起:它到底指什么“skills”这个词看起来简单,但放在不同语境里,指向的东西差别很大。有人看到它想到的是简历上那一栏“专业技能”,有人想到的是游戏里的技能树,还有人想到的是团队协作中每个人…

2026/10/20 17:52:45 阅读更多 →
GPT-5.5切换后API报401?不是Key失效而是Organization头强制校验

GPT-5.5切换后API报401?不是Key失效而是Organization头强制校验

前两天我在配合一个长期维护的某跨平台系统做模型升级时,撞上一个非常典型的鉴权坑:同一个 API Key,model 参数填 GPT-5.4 一切正常,一换成 GPT-5.5,接口立刻返回 401 Unauthorized。看到报错的第一眼,几乎…

2026/10/20 17:52:45 阅读更多 →
永磁同步电机模型预测转矩控制仿真实现与调试实战

永磁同步电机模型预测转矩控制仿真实现与调试实战

刚接手这个"永磁同步电机传统模型预测转矩控制研究(Simulink仿真实现)"课题时,我第一反应是:这不就是把直接转矩控制(DTC)的滞环换成滚动优化吗?真做起来才发现,坑全藏在细…

2026/10/20 17:52:45 阅读更多 →
基于CAN总线的DSP28335 IAP在线升级方案与Bootloader设计详解

基于CAN总线的DSP28335 IAP在线升级方案与Bootloader设计详解

做DSP28335应用开发的人,基本都体会过一种尴尬:整机已经装配完毕,外壳固定好了,或者设备已经在现场跑了大半年,这时候突然要改一个控制参数、修一个不起眼的逻辑bug,拿JTAG下载器过去才发现,拆机…

2026/10/20 17:51:45 阅读更多 →

日新闻

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实现详解

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

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

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

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

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

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

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在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 阅读更多 →