从零手写智能合约:一份可运行源码的部署与调用实践
简介面向区块链入门与初阶开发者的智能合约实操源码包围绕以太坊合约开发讲解部署与调用全流程并覆盖用户账户/合约账户区别、交易类型、ERC20 Token标准等核心知识。包内共3个文件含1个HTML页面、1个inscode工程文件及gitignore配置压缩包整体约7KB源码轻量适合快速阅读与本地复现。已有95人学习。该资源将理论说明与可运行示例结合源码中对应的HTML页面可用于直观查看合约交互过程inscode文件则提供直接启动的合约工程环境帮助读者对照理解合约创建交易、消息调用、gas消耗等抽象概念。对于希望快速建立智能合约整体认知并动手体验ERC20 Token转移与管理的学习者是一份便于上手的入门参考。1. 区块链智能合约到底是什么一份能跑起来的源码比一万字文档更有说服力很多人看了几个月智能合约的教程依然说不清合约上链之后到底发生了什么。原因很简单——大部分文章停留在概念层讲共识、讲去中心化、讲信任机器唯独不给你一份能编译、能部署、能调用的源码。区块链智能合约本质上是部署在链上的确定性程序它有自己的存储空间、执行规则和触发条件一旦上链就无法篡改。但这套机制的细节只有当你亲手把一份可运行源码编译部署到本地链上、看着交易回执里出现合约地址的那一刻才能真正建立起来。这篇文章用一个最小但完整的合约项目串起整个链路从执行模型、环境搭建、部署调用到 Gas 优化和常见坑所有步骤都附代码照着敲就能跑。2. 智能合约的执行模型理解链上代码的运行边界2.1 账户、交易与状态三个核心概念决定你写合约的方式在深入源码之前先把智能合约运行时的三个基础概念理清因为后续所有的编码决策都受它们约束。第一个是账户模型。在主流 EVM 兼容链上账户分两类外部账户EOA和合约账户。外部账户由私钥控制能发起交易合约账户由代码控制只能被动响应交易。两者的区别直接影响你的设计——比如合约无法主动调用另一个合约的方法除非有外部交易作为入口。很多新手在设计跨合约交互时忽略这一点导致功能实现不了。第二个是交易。所有状态的改变都必须通过交易完成而交易需要 Gas。Gas 是链上计算的计量单位每笔交易携带 Gas 上限和 Gas 价格矿工或验证节点按实际消耗收费。这意味着你写合约时不仅要考虑功能正确性还要考虑每行代码的“燃料成本”。存储操作最贵计算次之纯读取免费。第三个是状态。合约的持久化数据存储在链上通过 Storage 变量暴露。Storage 是一种 key-value 数据库以 32 字节为基本单位读写。比如一个 uint256 变量恰好占一个槽位而一个数组或映射则可能跨多个槽位。理解状态存储模型直接关系到 Gas 优化和合约升级策略。这三个概念决定了智能合约开发的几个铁律代码一旦部署不可修改执行结果在所有节点上必须一致任何外部依赖都不能影响确定性。这些铁律看似限制其实是安全性的来源——正是因为确定性链上合约才可信。2.2 开发框架选型为什么我用 Hardhat 而不是裸写 Solidity用纯 Solidity 编译器直接编译合约并不是不行但会浪费大量时间在脚本、测试和部署的重复劳动上。我一般会选一套框架来管理合约工程目前生态里最成熟的两个选择是 Hardhat 和 Foundry。Hardhat 基于 Node.js插件生态完善调试体验好支持 console.log 打印链上变量对新手非常友好。它的核心优势是任务驱动定义一次网络配置和部署脚本之后换测试网、换节点只需改参数。Foundry 基于 Rust用 Solidity 写测试和脚本执行速度极快适合追求效率的团队。但它的学习曲线更陡新手调试合约时不如 Hardhat 直观。取舍标准很简单如果你需要快速跑通、大量调试选 Hardhat如果你做长期项目、测试量大可以尝试 Foundry。我用 Hardhat 做演示因为它的调试器和中文资料更丰富遇到坑时更容易找到对照方案。Hardhat 项目结构如下my-contract/ ├── contracts/ # Solidity 源码目录 ├── scripts/ # 部署和交互脚本 ├── test/ # 测试文件 ├── hardhat.config.js # 网络、编译器配置 └── package.json # 依赖管理这是我每个项目的标准骨架。contracts 里只放 Solidity 文件scripts 放部署和调用的 JS 脚本test 放自动化测试。配置文件 hardhat.config.js 是框架的中枢——在这里指定 Solidity 版本、网络节点地址和账户私钥。3. 从零搭建本地开发环境Hardhat 项目初始化与最小合约源码3.1 环境准备与项目初始化一条命令拉起的开发环境开始之前先确保本机已安装 Node.js。版本要求因 Hardhat 版本而异我目前用的环境要求 Node.js 18 或更高。检查版本命令node -v npm -v如果输出版本号低于要求需要先升级 Node.js。常见做法是用 nvm 管理多版本 Node避免系统级安装相互冲突。接下来创建项目目录并初始化 npmmkdir my-contract cd my-contract npm init -y npm install --save-dev hardhatnpm init -y 快速生成 package.json-y 表示跳过交互式问答。安装 hardhat 时我习惯加 --save-dev因为它是开发期工具不需要进入生产依赖。安装完成后在项目根目录执行npx hardhat init此时会出现交互式菜单。选择 Create a JavaScript project框架会自动生成一个包含示例合约的完整工程。如果你看到菜单里有 TypeScript 选项也可以选本质相同只是类型系统帮你减少参数错误。初始化完成后目录里会多出 mapped 的文件结构包括一个 Lock.sol 示例合约。我们不需要它直接删除并创建自己的合约文件rm contracts/Lock.sol touch contracts/Counter.solCounter 合约是我们第一个可运行源码——一个最简单的计数器。它包含三个基本操作读取当前值、自增、设置任意值。麻雀虽小但覆盖了状态变量、函数修改器、事件这几个 Solidity 核心语法点。3.2 编写最小可运行合约Counter 合约逐行拆解打开 contracts/Counter.sol写入以下内容// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; contract Counter { uint256 private count; event CountChanged(uint256 newValue); function get() public view returns (uint256) { return count; } function increment() public { count 1; emit CountChanged(count); } function set(uint256 newValue) public { require(newValue count, new value must be greater); count newValue; emit CountChanged(count); } }第一行 SPDX 许可证标识是 Solidity 编译器的强制要求缺少它编译时会报警告。第二行声明编译器版本——^0.8.20 表示允许使用 0.8.20 及以上但低于 0.9.0 的版本。这让代码在不同补丁版本间有弹性但不会跨大版本。uint256 private count 声明了一个私有状态变量。private 意味着只有合约内部函数能读取它外部调用必须通过 get()。这里有个新手常犯的误区private 并不等于“秘密”链上所有数据都是公开的任何人都能通过节点扫描存储槽位读取到 count 的真实值。事件 CountChanged 是合约与外部世界通信的桥梁。当 count 改变时事件被记录到交易日志中。前端应用可以监听这个事件实时更新界面而不需要轮询节点。事件参数分为 indexed 和普通参数indexed 参数可以被过滤查询但成本更高。这里 newValue 未标记 indexed因为不需要用它做索引筛选。get() 函数标注了 view表示只读取状态不修改链上数据——这类调用不消耗 Gas可以直接用 eth_call 方式执行。increment() 和 set() 都会修改状态必须通过交易提交消耗 Gas。require(newValue count, new value must be greater) 是条件检查。require 失败时交易被回滚状态不变已消耗的 Gas 不退还因为计算已经执行了。这里加了一个简单约束避免 set 把计数器改小——真实场景中这种约束是业务规则的载体比如限购、白名单、资金锁定等。3.3 编译与第一个测试让源码真正运行起来写完合约后第一件事是编译。编译做的事情是把 Solidity 源码转换为字节码和 ABI应用二进制接口。ABI 是 JSON 格式的接口描述后续部署和调用都要靠它。npx hardhat compile编译成功后会生成 artifacts 目录里面包含编译产物。如果编译失败绝大多数原因是指令不匹配的编译器版本或语法错误。Hardhat 默认会自动下载所需版本的 Solidity 编译器所以网络正常的情况下无需手动处理。编译通过后给合约写一个最基本的自动化测试。在 test 目录下创建 counter.test.jsconst { expect } require(chai); describe(Counter, function () { it(should increment count, async function () { const Counter await ethers.getContractFactory(Counter); const counter await Counter.deploy(); await counter.waitForDeployment(); await counter.increment(); const currentValue await counter.get(); expect(currentValue).to.equal(1); }); });这段测试做了三件事通过 getContractFactory 获取合约工厂、部署一个新实例、调用 increment 后验证 get() 返回值。这是最基础的可运行验证——如果测试通过说明你的合约代码在链上确实能执行。跑测试命令npx hardhat test看到绿色的 passing 输出即表示所有测试通过。这是判断“可运行源码”的最低标准。当你后续修改合约时每次编译测试能第一时间发现逻辑错误而不用等到部署之后。4. 部署与调用从本地网络到测试网的完整链路4.1 本地网络部署脚本解析与部署回执理解单元测试跑通后下一步是把合约真正部署到一个链上网络。Hardhat 内置了一个本地网络Hardhat Network它是专门为开发和测试设计的模拟链。启动本地节点npx hardhat node这条命令会启动一个开发链并生成 20 个测试账户及其私钥。每个账户预置 10000 个测试币。这个本地网络的特点是交易即时确认无共识延迟Gas 价格固定。调试效率远超测试网。另开一个终端创建部署脚本 scripts/deploy.jsasync function main() { const Counter await ethers.getContractFactory(Counter); const counter await Counter.deploy(); await counter.waitForDeployment(); const address await counter.getAddress(); console.log(Counter deployed to: ${address}); } main().catch((error) { console.error(error); process.exitCode 1; });执行部署npx hardhat run scripts/deploy.js --network localhost--network localhost 指定连接到刚才启动的节点。如果你不加这个参数Hardhat 会用默认的 hardhat network——它同样能部署合约但每次运行都会重新创建链不会持久化状态。部署成功后会输出合约地址。合约地址由部署者地址和 nonce 共同生成所以不同账户部署同一份字节码得到的地址不同。保存好这个地址后续调用要用。4.2 与合约交互状态读取与交易发送的区别部署之后写一个交互脚本来演示真实调用。创建 scripts/interact.jsasync function main() { const contractAddress 0x5FbDB2315678afecb367f032d93F642f64180aa3; const Counter await ethers.getContractFactory(Counter); const counter Counter.attach(contractAddress); const currentValue await counter.get(); console.log(Current value:, currentValue.toString()); const tx await counter.increment(); const receipt await tx.wait(); console.log(Transaction hash:, receipt.hash); console.log(Gas used:, receipt.gasUsed.toString()); const newValue await counter.get(); console.log(Value after increment:, newValue.toString()); } main().catch((error) { console.error(error); process.exitCode 1; });合约地址需要替换成你实际部署时输出的值。脚本中先调用 get() 读取当前值——这是 view 函数直接通过本地节点响应返回不需要等待区块确认。然后调用 increment()它返回一个交易对象调用 tx.wait() 等待交易被确认最终从回执中读取 Gas 消耗量。打印出的 tx.hash 是这笔交易的标识。你可以通过 Hardhat 节点的终端输出看到同一笔交易的完整处理日志。交易回执的 Gas used 数值非常重要——它是后续调优 Gas 的关键数据。4.3 部署到测试网配置网络与处理真钱交易本地网络跑通之后部署到测试网会带来真实感——因为测试网上的交易流程和主网完全一致只是币没有价值。常见的做法是使用开源的公共测试网申请少量测试币后即可部署。需要修改 hardhat.config.jsmodule.exports { solidity: 0.8.20, networks: { testnet: { url: https://rpc.testnet.example.com, chainId: 11155111, accounts: [process.env.PRIVATE_KEY] } } };url 是测试网的 RPC 节点地址chainId 是网络 IDaccounts 是部署账户的私钥数组。私钥通过环境变量读取绝不能写进代码库。创建 .env 文件并在 package.json 中引入 dotenvPRIVATE_KEY0x你的私钥不要提交到仓库部署命令npx hardhat run scripts/deploy.js --network testnet第一次执行之前确保测试账户有测试币。否则会报错 insufficient funds for gas * price value。没有测试币的解决办法是从测试网水龙头申请——这是每个测试网的标准流程按页面提示操作即可。部署到测试网后覆用 4.2 的交互脚本同样可以调用只是确认时间从秒级变成十几秒因为真实测试网有区块间隔。这一点要提前适应。5. 避坑 / 常见问题智能合约开发者的 5 个高频翻车现场5.1 整数溢出导致无限增发现象某个代币合约的 transfer 函数在特定输入下让账户余额异常变大市场瞬间被砸崩。原因Solidity 0.8.x 之前的版本默认不做溢出检查uint256 加到最大值后会回绕到零。如果合约未使用 SafeMath 库任何 unchecked 的操作都可能被攻击者利用。解决Solidity 0.8.0 及以上版本默认内置了溢出检查溢出时交易回滚。老版本合约需要手动引入 SafeMath 库。另外在处理加减运算时如果确实需要不检查溢出的场景比如某些 Gas 优化显式使用 unchecked 块并确保边界安全。5.2 Gas 估算失败view 函数与交易函数分不清现象调用一个函数时前端总是报 gas required exceeds allowance。原因函数内部存在复杂循环且使用了固定 Gas 上限。前端估算 Gas 时无法精确预测循环次数导致估算值低于实际需求。解决第一避免在合约内做大规模循环——链上计算昂贵且不可预测。如果需要遍历数据考虑链下计算配合索引表。第二前端调用时显式设置更高的 Gas 上限。第三如果函数确实需要大量计算拆分逻辑成多个小交易分步执行。5.3 存储变量与内存变量混用改动本地变量后状态变回去了现象在函数内定义了一个变量修改后再次读取却发现值没变交易也显示成功。原因Solidity 中 storage、memory 和 calldata 是三种不同的数据位置。storage 指持久化存储memory 指临时内存。如果函数内声明变量未标注位置默认对值类型是 memory 拷贝对引用类型如 struct、array则容易误操作到临时副本。解决修改状态时明确标注变量的数据位置。例如合约内全局变量storage、函数参数calldata 用于只读参数memory 用于可修改参数function updateItem(uint256 index, string memory newContent) public { Item storage item items[index]; item.content newContent; }这里的Item storage item表示拿到的是存储位置引用后续修改直接写入链上。如果写成Item memory item则修改只作用于内存副本链上数据纹丝不动。5.4 未受保护的敏感函数谁都能调用管理员操作现象合约里写了一个 withdraw 函数用于提取资金但任何人都能调用它合约余额被洗劫一空。原因函数缺少权限控制。常见做法是用 OpenZeppelin 的 Ownable 模组但有些开发者只复制函数代码忘了继承 Ownable 或忘加 onlyOwner 修饰符。解决推荐用 OpenZeppelin 的标准库而不是自己写权限逻辑import openzeppelin/contracts/access/Ownable.sol; contract MyContract is Ownable { function withdraw() public onlyOwner { // 提取资金逻辑 } }继承 Ownable 后部署者自动成为 owneronlyOwner 修饰符确保只有 owner 能调用该函数。自己写权限逻辑时注意owner 地址不应写死在代码中应该由构造函数参数传入或在部署时赋值。5.5 前端监听不到合约事件数据明明变了界面不刷新现象交易成功链上数据已更新但前端界面没有任何反应。原因事件监听有两种模式——监听已确认区块和监听 pending 交易。如果前端只监听了 pending交易进入内存池时事件已捕获一旦交易被替换或回滚事件就不会持久化。另一种情况是事件没有 indexed 参数而前端试图按错误主题过滤。解决前端监听时使用完整的 topic 结构并配合 getLogs 做双重保障。建议事件参数设计时把需要检索的字段标记为 indexed最多 3 个不必要的字段不标记以减低成本。同时轮询的方式兜底监听逻辑在主代码中执行一次然后每 30 秒调用 getLogs 同步一次确保不丢事件。6. 进阶技巧Gas 优化与可运行源码的验证方法6.1 Gas 优化的两个立竿见影的手段Gas 优化是智能合约开发的硬技能直接决定用户成本。两个最有效的手段第一个是存储优化。链上存储最贵的操作是 SSTORE写一个 32 字节槽位要花费约 22000 Gas。一个实用的技巧是紧凑打包把多个小单位类型拼进同一个槽位。比如一个地址和一个 uint8 合成一个结构体时Solidity 编译器会自动把它们塞进同一个 32 字节槽省一次存储开销。struct User { address addr; // 20 字节 uint8 level; // 1 字节 // 同一槽位存放省一次 SSTORE 成本 }第二个是批量操作。把多次外部调用合并成一次批量交易减少每笔交易的基础开销21000 Gas。例如批量转账function batchTransfer(address[] calldata recipients, uint256 amount) public { for (uint256 i 0; i recipients.length; i) { _transfer(msg.sender, recipients[i], amount); } }但注意这里又回到了 5.2 的问题大循环要控制长度上限防止 Gas 估算偏差或单笔交易耗尽限额。6.2 验证 Vault 合约源码让可运行源码经得起检查最后分享一个验证自己代码是否真的“可运行”的习惯。每次部署到测试网或主网之前我习惯用拿到合约的部署回执做两件事第一件事是检查 Bytecode 一致性——用 solc 对源码重新编译对比部署回执中的 deployedBytecode 是否一致。这一步能发现源头不一致的问题比如编译环境平台差异导致字节码不同。第二件事是跑一遍完整的冒烟测试包括部署、状态变更、异常分支require 失败、事件日志。这个冒烟测试不是熟悉的单元测试而是从用户视角走一遍完整流程——模拟前端依赖的接口是否可用。在两年的智能合约开发里最深的教训是很多看似合理的合约代码真正跑起来才发现存储布局错了、事件没发出、或者 Gas 成本远超预期。所以我现在每写一个合约都坚持先在本机跑通最小可运行源码再谈优化和上线。这个习惯帮我避免了很多次上线前的返工希望也能帮到你。本文还有配套的精品资源点击获取

相关新闻

前端面试题200道:从题眼清单到自测复盘的高效刷题法

前端面试题200道:从题眼清单到自测复盘的高效刷题法

简介:覆盖前端90%以上技术模块的面试题集锦,面向准备跳槽或冲刺大厂的前端开发者,内容涵盖HTML、CSS、JavaScript、TypeScript、Node.js、前端工程化、MVVM、计算机网络、前端安全、跨端开发、数据结构与算法、操作系统及手写题等模块&#x…

2026/10/19 1:49:22 阅读更多 →
Hive SQL近12个月统计:日期函数选型、分区裁剪与性能优化实战

Hive SQL近12个月统计:日期函数选型、分区裁剪与性能优化实战

1. 为什么“近12个月”在Hive里是个高频刚需 做数据这行的,尤其是搞离线数仓的,几乎绕不开一个场景:业务方要看最近一年的趋势。可能是运营想看过去12个月的订单走势,可能是风控要拉近一年的逾期率变化,也可能是财务要…

2026/10/18 17:49:25 阅读更多 →
SVN版本控制实战指南:从检出提交到分支合并与冲突处理

SVN版本控制实战指南:从检出提交到分支合并与冲突处理

1. 为什么版本控制这件事,绕不开 SVN 很多人第一次接触版本控制,都是从 Git 开始的。命令行敲得飞起,分支合并玩得溜,觉得 SVN 这种"老古董"早该进博物馆了。但现实情况是,不少公司的项目管理系统、硬件研发…

2026/10/17 15:51:14 阅读更多 →

最新新闻

EMC设计整改与仿真应用:从PCB正向设计到实验室超标排查实战

EMC设计整改与仿真应用:从PCB正向设计到实验室超标排查实战

/* 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:02:29 阅读更多 →
deepagents中间件:30行代码搞定AI Agent文件系统、记忆与上下文压缩基建

deepagents中间件:30行代码搞定AI Agent文件系统、记忆与上下文压缩基建

1. 从"手搓轮子"到"搭积木":Agent 基建的痛点到底在哪如果你最近半年在折腾 AI Agent,大概率经历过这样一个阶段:一开始兴致勃勃地写了个 ReAct 循环,觉得"不过如此";等到真正要把它做成…

2026/10/19 2:02:29 阅读更多 →
Spring AI Alibaba Graph实战:ReAct Agent与Workflow混合编排的企业级AI应用

Spring AI Alibaba Graph实战:ReAct Agent与Workflow混合编排的企业级AI应用

1. 从“能聊天”到“能干活”:企业级AI编排的真实分水岭很多团队做AI应用,第一步都是接个大模型,写个对话接口,跑通“你问我答”就以为大功告成。但真正落到企业业务里,你会发现单轮问答根本撑不起一个完整流程。比如一…

2026/10/19 2:02:29 阅读更多 →
移动端AIcoding落地成本与端云协同选型实战

移动端AIcoding落地成本与端云协同选型实战

1. 移动端 AIcoding 落地,为什么大家都在算这笔账移动端 AIcoding 这个词,最近半年在圈子里被反复提起。我最早接触这个概念是在一个跨平台工具链项目里,当时团队想把代码补全和自然语言生成代码的能力塞进手机和平板端,让开发者在…

2026/10/19 2:02:29 阅读更多 →
移动端AIcoding真实成本账:端侧适配与上下文管理占大头

移动端AIcoding真实成本账:端侧适配与上下文管理占大头

1. 这笔账为什么值得算移动端接入 AIcoding,前两年大家聊的都是“能不能做”,现在聊的是“划不划算”。我所在的团队从去年下半年开始,把一个内部代码辅助工具从桌面端往移动端搬,前后折腾了差不多两个季度,踩过的坑、…

2026/10/19 2:02:29 阅读更多 →
1~30秒参考音频克隆任意声音:Seed-VC 零样本语音转换完整零训练指南

1~30秒参考音频克隆任意声音:Seed-VC 零样本语音转换完整零训练指南

1~30秒参考音频克隆任意声音:Seed-VC 零样本语音转换完整零训练指南 【免费下载链接】seed-vc zero-shot voice conversion & singing voice conversion, with real-time support 项目地址: https://gitcode.com/GitHub_Trending/se/seed-vc 有一场会要开…

2026/10/19 2:01:28 阅读更多 →

日新闻

频率跟踪型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/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出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/18 0:55:02 阅读更多 →
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/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练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 阅读更多 →