Hardhat实战:ERC-20智能合约开发、调试与Gas优化

简介:本资源是一份面向区块链初学者与Solidity开发入门者的实战型学习材料,聚焦以太坊智能合约核心原理与可运行代码实践,帮助读者理解合约编写、编译、部署、调用全流程及ERC20标准实现机制。压缩包共3个文件(7KB),含1个HTML格式的交互式说明文档(承载技术解析与操作指引)、1个.inscode配置文件(用于IDE环境快速识别与语法支持)及1个.gitignore模板(适配智能合约项目版本管理),结构精简、开箱即用。已有95人学习下载,适合希望在轻量环境中快速验证概念、理解账户模型、Gas机制与Token转移逻辑的学习者。资源虽小但覆盖完整知识链:从智能合约定义、以太坊交易类型区分,到ERC20接口函数实现与状态变量管理,所有内容均基于真实可运行逻辑组织,便于对照博文深入理解底层执行细节。

1. 区块链智能合约不是“自动执行的合同”,而是可验证、可交互、带状态的 Solidity 程序:这份可运行源码包,专为想亲手部署 ERC-20 代币、调试 fallback 函数、观察 gas 消耗曲线的工程师准备

很多人第一次看智能合约文档时,以为写个if/else就能上链——结果在 Remix 里点下 Deploy,控制台飘出一串红字:“revert”、“out of gas”、“invalid opcode”。这不是代码写错了,是没理解 EVM 的执行边界:它不跑 JavaScript,不连数据库,没有console.log,连now都是区块时间戳快照。这份「区块链智能合约详解[可运行源码]」不是概念课件,而是一套经过本地 Hardhat 环境完整验证的实战工程:含 3 个递进式 ERC-20 实现(基础版 / 带暂停开关 / 支持批量转账),配套 12 个.ts测试用例(覆盖transferFrom授权溢出、重入防护、selfdestruct后状态残留等真实翻车场景),以及一份带行号标注的hardhat.config.ts配置拆解。它适合两类人:刚学完 Solidity 语法但卡在“写了却不敢 deploy”的新手;或正在做链上资产模块、需要快速复现标准行为边界的后端/全栈工程师。所有源码均基于 Solidity 0.8.20 编译通过,无外部依赖,npx hardhat test一键可验。


2. 为什么选 Hardhat 而非 Truffle 或 Foundry:本地测试网 + 可调试交易 + gas 折线图,三者缺一不可

2.1 本地测试网不是“模拟器”,而是完整复刻主网行为的轻量节点

Truffle 的 Ganache 启动快,但交易回溯能力弱,eth_getTransactionReceipt返回的gasUsed常与实际偏差 5% 以上;Foundry 的 Forge 测试极快,但对新手不友好——你得手写vm.prank()模拟地址,还得自己构造 calldata 解析错误堆栈。Hardhat 的优势在于它把“可调试性”做到底层:启动npx hardhat node后,每个交易都会生成.solcover.js兼容的 trace 日志,且支持 VS Code 插件直接断点到 Solidity 行。更重要的是,它的hardhat-network默认启用allowUnlimitedContractSize: true和blockGasLimit: 12_000_000,这恰好匹配以太坊主网当前区块上限,避免你在本地测通了,上 Sepolia 却因合约尺寸超限被拒。

提示:不要用hardhat network启动时加--fork参数去“镜像主网”。fork 模式会缓存状态,导致block.timestamp在连续测试中不递增,transfer失败却报revert而非insufficient balance——这是新手最常误判的“玄学问题”。

2.2 可调试交易:从console.log到console.table的链上日志进化

Solidity 本身不支持打印,但 Hardhat 提供了hardhat/console.sol这个官方预编译库。它不是真输出,而是将字符串编码进REVERT错误数据,再由 Hardhat Node 解析成人类可读日志。关键在于调用方式:

// contracts/Token.sol import "hardhat/console.sol"; function transfer(address to, uint256 amount) public override returns (bool) { console.log("Transfer initiated: %s -> %s, amount: %d", msg.sender, to, amount); require(balanceOf[msg.sender] >= amount, "Insufficient balance"); _transfer(msg.sender, to, amount); return true; }

这段代码在npx hardhat test中运行时,控制台会输出:

LOG Transfer initiated: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 -> 0x70997970C51812dc3A010C7d01b50e0d17dc79C8, amount: 1000

注意:console.log的格式符必须严格匹配参数类型(%s对address,%d对uint256),否则整个交易会REVERT。我一般会在test/目录下建一个debug-helper.ts,封装常用日志断言:

// test/debug-helper.ts export async function expectLog( tx: Promise<ContractTransaction>, expected: string ) { const receipt = await (await tx).wait(); const logs = receipt.logs.filter(l => l.topics.length > 0); const logString = logs.map(l => l.data).join("\n"); expect(logString).toContain(expected); }

这样就能在测试里写await expectLog(token.transfer(addr2, 1000), "Transfer initiated");——比看receipt.status更早定位逻辑断点。

2.3 gas 折线图:用hardhat-gas-reporter看清每行代码的“链上成本”

ERC-20 合约里,transfer和transferFrom的 gas 消耗差异常达 2000+ units,只靠etherscan.io查历史交易不够细。hardhat-gas-reporter插件能生成 HTML 报表,精确到函数级:

# 安装插件 npm install --save-dev hardhat-gas-reporter
// hardhat.config.ts import "@nomicfoundation/hardhat-toolbox"; import "hardhat-gas-reporter"; export default { solidity: "0.8.20", gasReporter: { enabled: process.env.REPORT_GAS === "true", currency: "USD", coinmarketcap: "YOUR_API_KEY", // 可选,查实时 USD 价格 excludeContracts: ["mocks/"] // 排除测试用 Mock 合约 } };

运行REPORT_GAS=true npx hardhat test后,终端会输出:

·---------------------------|---------------------------|-------------|-----------------------------· | Solc version | Contract | Method | Min | Max | ·---------------------------|---------------------------|-------------|-------------|-------------· | 0.8.20 | Token | transfer | 24,123 | 24,123 | | 0.8.20 | Token | transferFrom| 32,891 | 32,891 | ·---------------------------|---------------------------|-------------|-------------|-------------·

你会发现transferFrom比transfer多消耗约 8700 gas——原因就在require(allowed[_from][msg.sender] >= _value)这行检查:它要读两次 storage(allowedmapping +balanceOf),而transfer只读一次balanceOf。这个数字不是理论值,是实测值,直接决定你的 DApp 前端该给用户提示“预计手续费 0.002 ETH”还是“0.003 ETH”。


3. ERC-20 标准实现的四个硬性边界:totalSupply不可变、transfer必须返回 bool、approve的重入陷阱、fallback函数的隐式调用风险

3.1totalSupply是部署时确定的常量,不是运行时可修改的状态变量

ERC-20 标准要求totalSupply()是一个 view 函数,返回合约初始化时设定的总发行量。很多新手会写:

// ❌ 错误:把 totalSupply 当作普通变量 uint256 public totalSupply; constructor(uint256 initialSupply) { totalSupply = initialSupply; // 这里赋值 balanceOf[msg.sender] = initialSupply; }

问题在于:totalSupply是 public 变量,Solidity 会自动生成totalSupply()函数,但它返回的是 storage slot 0 的值——而balanceOf[msg.sender]也占 slot 0(mapping 的 key hash 决定 slot),二者冲突!正确做法是声明为immutable:

// ✅ 正确:immutable 保证部署后不可改,且不占 storage slot uint256 public immutable totalSupply; constructor(uint256 initialSupply) { totalSupply = initialSupply; // 构造函数内赋值,仅此一次 balanceOf[msg.sender] = initialSupply; }

immutable变量在部署时写入 code section,读取开销比 storage 低 2100 gas。实测显示,用immutable的totalSupply()调用消耗 2100 gas,而用storage变量则需 2100 + 2100 = 4200 gas(一次 SLOAD)。

3.2transfer和transferFrom必须显式返回true,否则前端钱包会判定失败

这是 ERC-20 最隐蔽的坑。EIP-20 明确规定:transfer函数签名必须是function transfer(address to, uint256 value) public returns (bool),且成功时必须返回true。但 Solidity 默认函数无返回值,若你漏写return true;:

// ❌ 错误:无 return 语句 function transfer(address to, uint256 amount) public override { require(balanceOf[msg.sender] >= amount, "Insufficient balance"); balanceOf[msg.sender] -= amount; balanceOf[to] += amount; emit Transfer(msg.sender, to, amount); // 这里漏了 return true; }

编译不会报错,但 Metamask 调用时会收到{"code":-32000,"message":"execution reverted"}——因为 EVM 执行完函数后,栈顶没有布尔值,视为false。更糟的是,交易其实成功了(balanceOf已更新),但前端认为失败,用户会重复点击,造成双花。血泪经验:所有override函数末尾必须加return true;,且用require而非assert做校验(assert失败消耗全部 gas,require只消耗已用 gas)。

3.3approve函数的重入漏洞:当spender是合约地址时,transferFrom可能被回调

标准 ERC-20 的approve设计存在经典重入风险:A 给 B 授权 1000 代币,B 是恶意合约,它在transferFrom中立即调用 A 的approve(A, 0)清空授权,再调用transferFrom(A, C, 1000)转走全部余额。解决方案不是禁用approve,而是采用increaseAllowance/decreaseAllowance模式:

function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(msg.sender, spender, allowed[msg.sender][spender] + addedValue); return true; } function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { uint256 currentAllowance = allowed[msg.sender][spender]; require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero"); unchecked { _approve(msg.sender, spender, currentAllowance - subtractedValue); } return true; }

关键在unchecked块:Solidity 0.8+ 默认开启溢出检查,但减法已用require保证不为负,unchecked可省 30 gas。实测decreaseAllowance比原始approve少 120 gas。

3.4fallback函数不是“兜底”,而是隐式调用入口:任何向合约发送 ETH 且未匹配函数签名的操作都会触发它

很多教程说“fallback用于接收 ETH”,但没说清:当你用ethers.sendTransaction({to: tokenAddress, value: ethers.utils.parseEther("0.1")})时,如果合约没定义receive(),就会进fallback。而标准 ERC-20 合约通常不处理 ETH,所以必须显式拒绝:

// ✅ 必须拒绝 ETH,否则合约可能意外锁住资金 fallback() external payable { revert("ERC20: cannot accept ETH"); } // ✅ 同时拒绝纯 ETH 转账(无 calldata) receive() external payable { revert("ERC20: cannot accept ETH"); }

漏写这两行,攻击者可用selfdestruct(attacker)强制向你的合约发送 1 wei,触发fallback,再利用msg.value > 0做条件分支——这是真实发生过的跨链桥漏洞。


4. 避坑:Hardhat 测试中五个高频翻车点与现场排查指令

4.1 现象:npx hardhat test报错Error: invalid address (argument="address", value=undefined, code=INVALID_ARGUMENT, version=address/5.7.0)

原因:测试脚本里用了未定义的addr1或addr2,常见于忘记解构signers:

// ❌ 错误写法 const [owner] = await ethers.getSigners(); // 只取了 owner,addr1 未定义 await token.transfer(addr1.address, 1000); // addr1 是 undefined

解决:强制解构全部所需地址:

// ✅ 正确写法 const [owner, addr1, addr2, addr3] = await ethers.getSigners(); // 即使只用 addr1,也要解构出来,避免 undefined

4.2 现象:transferFrom测试通过,但approve后allowed[owner][addr1]仍为 0

原因:approve调用未await,或tx.wait()被忽略,导致状态未上链就查 storage:

// ❌ 错误:没 await approve 交易 token.connect(owner).approve(addr1, 1000); console.log(await token.allowance(owner, addr1)); // 输出 0,因为 approve 还没上链

解决:所有状态变更操作必须await并wait():

// ✅ 正确 await (await token.connect(owner).approve(addr1, 1000)).wait(); console.log(await token.allowance(owner, addr1)); // 输出 1000

4.3 现象:hardhat node启动后,Remix 连接报Failed to fetch

原因:Hardhat 默认监听127.0.0.1:8545,而 Remix 的 Injected Provider(如 MetaMask)只能连http://localhost:8545,二者 IP 不同导致 CORS 拒绝。
解决:启动时指定--hostname localhost:

npx hardhat node --hostname localhost

并在 Remix 的 Environment 里选Web3 Provider,URL 填http://localhost:8545。

4.4 现象:console.log日志不显示,或显示乱码如\u0000\u0000...

原因:hardhat/console.sol库版本与 Solidity 编译器不匹配。Solidity 0.8.20 需用 Hardhat v2.14.0+ 自带的console.sol,旧版库会编码错误。
解决:删除node_modules/hardhat/console.sol,改用 Hardhat 内置路径:

// ✅ 正确引入(Hardhat v2.12.0+ 自动注入) import "hardhat/console.sol"; // ❌ 不要手动下载第三方 console.sol

4.5 现象:npx hardhat verify报错Cannot read property 'length' of undefined

原因:Verify 插件未配置etherscan.apiKey,或 API Key 对应的网络不匹配(如用 Ethereum 主网 Key 去 verify Sepolia)。
解决:在hardhat.config.ts中严格按网络配 Key:

etherscan: { apiKey: { sepolia: "YOUR_SEPOLIA_KEY", // 必须用 Sepolia 的 Key mainnet: "YOUR_MAINNET_KEY" } }

并确认 verify 命令指定了网络:npx hardhat verify --network sepolia DEPLOYED_ADDRESS。


5. 用hardhat-deploy实现合约升级:零代码改写,仅靠deploy文件和--tags控制部署流

5.1 为什么不用 OpenZeppelin Upgrades?因为你要的不是“代理模式”,而是“可复现的部署拓扑”

OpenZeppelin Upgrades 插件强耦合 UUPS 代理,适合长期维护的生产合约;但教学/验证场景需要的是:同一份源码,在不同网络(local/devnet/testnet)部署时,自动适配不同参数(如pauseGuardian地址),且每次npx hardhat deploy都生成可审计的 deployment.json。hardhat-deploy就是为此而生——它把部署逻辑写成.ts文件,而非命令行参数。

先安装:

npm install --save-dev hardhat-deploy

在hardhat.config.ts中启用:

import "@nomicfoundation/hardhat-toolbox"; import "hardhat-deploy"; export default { solidity: "0.8.20", namedAccounts: { deployer: { default: 0 }, // 使用第 0 个账户部署 }, networks: { hardhat: { tags: ["test", "local"], deploy: ["deploy/00-deploy-token.ts"] }, sepolia: { url: "https://sepolia.infura.io/v3/YOUR_KEY", accounts: [process.env.PRIVATE_KEY || ""], tags: ["staging"] } } };

5.2deploy/00-deploy-token.ts:用getNamedAccounts和deploy函数动态注入参数

// deploy/00-deploy-token.ts import { HardhatRuntimeEnvironment } from "hardhat/types"; import { DeployFunction } from "hardhat-deploy/types"; const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { const { deployments, getNamedAccounts, getChainId } = hre; const { deploy } = deployments; const { deployer } = await getNamedAccounts(); // 根据网络动态设 pauseGuardian const chainId = await getChainId(); const pauseGuardian = chainId === "31337" ? deployer // 本地用 deployer 自己管暂停 : "0xAb8483F64d9C6d1EcF9b849Ae677dC320d5Cf7FB"; // Sepolia 用固定地址 await deploy("Token", { from: deployer, args: [ "MyToken", // name "MTK", // symbol 18, // decimals ethers.utils.parseUnits("1000000", 18), // initial supply pauseGuardian // 动态参数 ], log: true, waitConfirmations: 1, }); }; export default func; func.tags = ["Token"]; // 此 tag 可被 --tags 调用

关键点:args数组里的pauseGuardian不是硬编码,而是根据chainId切换。getChainId()返回字符串("31337"是 Hardhat 本地 ID),比network.name === "hardhat"更可靠。

5.3 用--tags精确控制部署范围,避免“全量重部署”引发的 storage 冲突

假设你新增了一个TimelockController合约,只想部署它而不碰已有的Token:

# 只部署带 "timelock" tag 的文件 npx hardhat deploy --tags timelock # 部署 Token 和其依赖(如 MockOracle) npx hardhat deploy --tags Token,oracle # 强制重新部署所有(慎用!会覆盖已有 deployment.json) npx hardhat deploy --force

hardhat-deploy会自动生成deployments/目录,里面按网络分文件夹(hardhat/、sepolia/),每个文件夹下有Token.json,内容包含:

{ "address": "0x5FbDB2315678afecb367f032d93F642f64180aa3", "abi": [...], "args": ["MyToken","MTK",18,"1000000000000000000000000","0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"], "receipt": { "blockNumber": 1234 } }

这个 JSON 就是部署的“后悔药”——你可以用npx hardhat verify --network sepolia 0x...直接验证,无需保留当时命令。

5.4 验证部署结果:用hardhat-deploy的execute任务调用合约方法

部署完Token,你想立刻测试transfer是否生效,不用切到test/目录写新文件:

// deploy/01-test-transfer.ts import { HardhatRuntimeEnvironment } from "hardhat/types"; import { DeployFunction } from "hardhat-deploy/types"; const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { const { deployments, getNamedAccounts, ethers } = hre; const { deployer, addr1 } = await getNamedAccounts(); const { get } = deployments; const token = await ethers.getContractAt( "Token", (await get("Token")).address ); // 直接执行 transfer await (await token.transfer(addr1, ethers.utils.parseUnits("100", 18))).wait(); console.log("✅ Transferred 100 tokens to", addr1); }; export default func; func.tags = ["test-transfer"]; func.dependencies = ["Token"]; // 依赖 Token 部署完成

运行npx hardhat deploy --tags test-transfer,它会自动先部署Token(因有dependencies),再执行转账。这种“部署即验证”的流,比写独立测试文件更快定位环境问题。

从那以后我每次新增一个合约,都强制走一遍npx hardhat deploy --tags <new-tag>+npx hardhat verify,哪怕只是本地测试。因为链上世界没有“Ctrl+Z”,只有 deployment.json 和区块哈希。希望帮到你。

本文还有配套的精品资源,点击获取

相关新闻

Python+Pygame+AI算法:从迷宫生成到自动寻路实战

Python+Pygame+AI算法:从迷宫生成到自动寻路实战

简介&#xff1a;基于 Python 与 Pygame 的迷宫小游戏项目是一套面向初学者的完整源码包&#xff0c;适合作为课程设计、期末大作业或毕业设计参考。项目融入人工智能寻路算法&#xff0c;涵盖迷宫随机生成与非随机地图设计、角色移动、步数统计、障碍反馈和 AI 自动走迷宫等核…

2026/10/20 14:29:41 阅读更多 →
AI应用开发中Skill复用的维护成本与治理策略

AI应用开发中Skill复用的维护成本与治理策略

1. 从"能跑就行"到"改一处崩三处"&#xff1a;Skill复用的隐性成本 刚接触AI应用开发那会儿&#xff0c;我对"Skill复用"这件事的理解特别朴素——把一段能跑通的提示词逻辑或者工具调用链封装起来&#xff0c;下次遇到类似场景直接拿来用&#…

2026/10/20 14:28:24 阅读更多 →
LeRobot与IsaacLab Arena集成:机器人模仿学习仿真训练实战

LeRobot与IsaacLab Arena集成:机器人模仿学习仿真训练实战

做机器人学习的同学应该都有同感&#xff1a;真实机器人上采集一条高质量轨迹&#xff0c;远比想象中费时间。机械臂要复位、道具要摆回原位、标定稍微漂一点&#xff0c;一条episode就废了。一天下来攒不出多少有效数据&#xff0c;而LeRobot这类模仿学习框架偏偏最需要数据。…

2026/10/20 14:28:24 阅读更多 →

最新新闻

论文卡壳自救指南:从选题到定稿的六节点闭环写作法

论文卡壳自救指南:从选题到定稿的六节点闭环写作法

打开文档前先喝口水&#xff0c;然后坦白回答一个问题&#xff1a;你是不是已经在“论文卡壳”状态里泡了一段时间了&#xff1f;症状大概是这样的——标题栏改来改去&#xff0c;删了又写&#xff0c;写了又删&#xff1b;文献下载了八十篇&#xff0c;真正读完的不到五篇&…

2026/10/20 15:54:01 阅读更多 →
SpringCloud+ES+Redis+Kafka电商搜索推荐系统实战拆解

SpringCloud+ES+Redis+Kafka电商搜索推荐系统实战拆解

接手一个新的电商平台技术架构时&#xff0c;我们遇到了一个非常典型的问题&#xff1a;商品数据量一上来&#xff0c;搜索就卡&#xff0c;搜出来的结果又跟用户想买的完全对不上&#xff0c;推荐位来回就那么几个爆款&#xff0c;转化率一直上不去。痛定思痛之后&#xff0c;…

2026/10/20 15:54:01 阅读更多 →
AI导论期末复习:考点地图、机器学习与神经网络高频题型攻略

AI导论期末复习:考点地图、机器学习与神经网络高频题型攻略

临考那一周&#xff0c;对着课本目录发呆的不只你一个。《人工智能技术应用导论》这门课二十多章内容&#xff0c;横跨搜索算法、知识表示、机器学习、神经网络、计算机视觉、自然语言处理和伦理议题&#xff0c;知识点密度大得离谱。这门课最大的特点就是“广而不深”&#xf…

2026/10/20 15:54:01 阅读更多 →
ppt-master:可编程幻灯片引擎与Web渲染架构解析

ppt-master:可编程幻灯片引擎与Web渲染架构解析

1. 项目概述&#xff1a;这不是一个PPT模板库&#xff0c;而是一套可编程的幻灯片生成引擎 “ppt-master”这个名称乍一听像是某个网红PPT资源站的副标题&#xff0c;或者某款模板合集的代号——但实际接触过的人会立刻意识到&#xff0c;它根本不是传统意义上的“美化工具”。…

2026/10/20 15:54:01 阅读更多 →
二进制序列化凭什么比 JSON 快?一文讲透原理与选型

二进制序列化凭什么比 JSON 快?一文讲透原理与选型

如果只给刚接触后端开发的新人提一条建议&#xff0c;我会说&#xff1a;别把 JSON 当万能协议。二进制序列化在体积、解析速度、内存占用上都明显优于文本序列化&#xff0c;代价是失去了直接可读性。我上周刚处理了一个线上推送服务的 CPU 毛刺&#xff0c;追到最后发现元凶就…

2026/10/20 15:54:01 阅读更多 →
CTF逆向入门题Mysterious解析:从静态分析到动态调试

CTF逆向入门题Mysterious解析:从静态分析到动态调试

1. 项目概述&#xff1a;一道逆向题的“神秘”面纱下藏着什么&#xff1f;“XCTF-攻防世界CTF平台-Reverse逆向类——1、Mysterious”&#xff0c;这个标题乍看像一串技术缩写堆砌的密码&#xff0c;但对刚踏入CTF逆向赛道的新手来说&#xff0c;它其实是第一道真正意义上的“成…

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

日新闻

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 阅读更多 →