OAuth签名版本混淆:HMAC-SHA1与PLAINTEXT算法详解及Python实现
很多同学一看到OAuth 签名这几个字下意识就默认这是 OAuth 2.0 的机制实际上这里藏着一个非常经典的版本混淆。HMAC-SHA1 和 PLAINTEXT 这两种签名算法出自 OAuth 1.0a 规范RFC 5849OAuth 2.0 里压根没有签名方法这个概念。标题里会出现OAuth2 HMAC-SHA1这种混搭多半是因为不少老系统的对接文档写着 OAuth 2.0实际跑的却是 1.0a 的签名流程。这篇文章就专门把这件事彻底理清楚两种签名算法各自的构造原理、Python 手写实现、什么时候该用哪个以及我实际调接口时踩过的一堆坑。这篇文章适合三类人接老平台 OpenAPI 时被签名验证折磨的对接工程师想手写 OAuth 1.0a 客户端而不是直接套库的底层开发者以及面试前想搞明白签名基字符串到底怎么回事的同学。不需要你有密码学基础只要写过 Python、见过 HTTP 请求就能跟着走完。1. 先理清版本HMAC-SHA1 和 PLAINTEXT 到底属于哪个 OAuth1.1 OAuth 1.0a 和 OAuth 2.0 不是升级关系很多人以为 OAuth 2.0 是 OAuth 1.0 的升级版就像 Python 2 升 Python 3 那样实际上这俩是两套完全不同的协议设计。OAuth 2.0 的主打思路是授权码换令牌令牌直接带走令牌本身以 Bearer Token 的形式放到请求头里服务端看到令牌就认账。换句话说OAuth 2.0 的安全重心不在怎么构造请求签名而在于令牌的颁发、过期、刷新和传输层保护。而 OAuth 1.0a 的设计思路完全不同。它的核心是每一个请求都要被签名即使你持有访问令牌发出的请求也要带上 oauth_consumer_key、oauth_token、oauth_timestamp、oauth_nonce 等一系列协议参数并且通过签名算法把这些参数和请求内容绑定在一起。服务端验签通过才认为这个请求确实是某个客户端的合法请求。所以 HMAC-SHA1 和 PLAINTEXT 这两个签名方法从诞生那天起就属于 OAuth 1.0a。你接的如果是老平台的 OpenAPI文档里出现 signature_method 这种字段不用怀疑它走的就是 1.0a 的流程。标题里的 OAuth2 只是沿用了大家的习惯性叫法或者说文档写错了版本号这种情况在真实接口对接里太常见了。1.2 为什么老机制依然值得研究你可能想问都 2025 年了为什么还要翻这种老古董原因很现实。大量企业级开放平台、支付网关、数据服务商的存量接口至今还在用 OAuth 1.0a 的 HMAC-SHA1 签名。这些接口要么是历史包袱太重没法升级要么是平台方基于安全考虑刻意保留这套无状态验签机制——因为 HMAC-SHA1 验签不需要在服务端存储会话状态天然适合分布式网关做水平扩展。另外搞懂 OAuth 1.0a 的签名基字符串Signature Base String构造过程对你理解任何带签名的 API 协议都有帮助。现在的开放平台签名方案层出不穷什么 MD5 签名、HMAC-SHA256 签名、国密 SM3 签名本质上都是同一套思路把所有请求要素归一化成一个字符串再用约定的密钥和哈希算法算指纹。你只要把 OAuth 1.0a 这套吃透了其他签名方案基本都是换汤不换药。2. 签名机制的骨架签名基字符串与编码规则2.1 签名基字符串的三段式结构HMAC-SHA1 和 PLAINTEXT 虽然一个要算哈希、一个纯拼接但它们都绕不开一个共同的前置步骤构造签名基字符串。RFC 5849 里把它的结构定义得非常死板死板其实是好事——只有两边都严格按同一套规则来才能算出一样的签名。签名基字符串由三段组成中间用连接HTTP请求方法(大写) 经过编码的基础URI 经过编码的参数字符串第一段是请求方法的大写形式比如 GET、POST、PUT、DELETE。第二段是基础 URI注意不是完整的请求 URL而是协议 主机 端口非默认端口才保留 路径查询参数和 Fragment 统统不能带进去。第三段是最容易出错的参数字符串它要把协议参数除了 oauth_signature 本身和业务请求参数全部收进来每个参数名和参数值各自做百分号编码然后按参数名排序同名参数按值排序最后用拼接。有个细节很多新手会忽略拼好的参数字符串在塞进签名基字符串之前还要整体再做一次百分号编码。也就是说会变成%26会变成%3D。这个二次编码是签名对不上的头号原因后面我会专门展开讲。2.2 RFC 3986 百分号编码才是真正的门槛两个客户端算出来的签名对不上十次有八次是编码规则不一致。OAuth 1.0a 要求的是 RFC 3986 百分号编码网上很多资料把它和 URL 编码application/x-www-form-urlencoded混为一谈这是个大坑。RFC 3986 的规则是字母、数字以及-、.、_、~这四个字符保留原样其他所有字符都要转成%XX的形式。特别注意两点空格必须编码成%20不是。用 Python 的urllib.parse.quote_plus会把空格变成用在 OAuth 签名里必死。没有安全路径字符这种说法连/都要编码成%2F。但如果用 Pythonquote函数不指定 safe 参数默认 safe/也会出错。正确写法是from urllib.parse import quote def rfc3986_encode(value): RFC 3986 百分号编码保留 -._~ 四个字符 return quote(str(value), safe-._~)我见过不少项目里手撸签名结果在编码这步就翻车。比如一个参数值是hello world正确编码是hello%20world如果用 quote_plus 就成了helloworld服务端按 RFC 3986 解析后验签直接就 401。这种事没有任何提示只能靠日志里比对签名一步一步排查。3. HMAC-SHA1 签名方法全解析3.1 从原理到公式双密钥与 HMAC 结构HMAC-SHA1 的全称是 Hash-based Message Authentication Code using SHA-1它是一种带密钥的哈希。普通哈希像 MD5、SHA-1任何人拿到原文都能验证摘要而 HMAC 需要一把密钥只有持有密钥的人才算得出正确的摘要所以它能同时保证数据完整性和一定的身份真实性。OAuth 1.0a 里 HMAC-SHA1 的输入有三样东西签名基字符串上一节说的三段式结构作为消息输入。签名密钥由客户端密钥consumer_secret和令牌密钥token_secret拼接而成中间用分隔两部分都要先做 RFC 3986 编码。哈希算法SHA-1输出 160 位20 字节的摘要最后做 Base64 编码得到最终的签名值。用公式表达就是签名密钥 rfc3986_encode(client_secret) rfc3986_encode(token_secret) 签名值 Base64( HMAC-SHA1(签名密钥, 签名基字符串) )这里有个容易想岔的点很多人以为 PLAINTEXT 才是把密钥拼起来HMAC-SHA1 的密钥怎么也是拼起来的是的OAuth 1.0a 的双密钥结构就是 consumer_secret 和 token_secret 拼在一起。即使 token_secret 为空比如还没拿到访问令牌的 request token 阶段那个也不能省密钥就是consumer_secret。3.2 Python 手写实现一步步拆解我用纯标准库实现一份不依赖任何第三方组件这样你能看到每一个环节在做什么。完整代码如下import base64 import hashlib import hmac import time import random import string from urllib.parse import quote def rfc3986_encode(value): RFC 3986 百分号编码 return quote(str(value), safe-._~) def build_signature_base_string(method, url, query_params, oauth_params): 构造签名基字符串 method: HTTP 方法大写 url: 不含查询参数的基础 URL query_params: 业务请求参数来自 query string 或表单体 oauth_params: OAuth 协议参数注意不要包含 oauth_signature params {} params.update(oauth_params) params.update(query_params) # 保险起见排除签名本身 params.pop(oauth_signature, None) # 每个键值对先各自编码再按编码后的名字排序名字相同按值排序 encoded_pairs sorted( (rfc3986_encode(k), rfc3986_encode(v)) for k, v in params.items() ) param_string .join(f{k}{v} for k, v in encoded_pairs) # 三段式拼接注意 URL 和整个参数字符串都要编码 base_string .join([ method.upper(), rfc3986_encode(url), rfc3986_encode(param_string) ]) return base_string def hmac_sha1_sign(method, url, query_params, oauth_params, client_secret, token_secret): HMAC-SHA1 签名 base_string build_signature_base_string( method, url, query_params, oauth_params ) key rfc3986_encode(client_secret) rfc3986_encode(token_secret) digest hmac.new( key.encode(utf-8), base_string.encode(utf-8), hashlib.sha1 ).digest() return base64.b64encode(digest).decode(utf-8)用的时候这样组织参数# 生成时间戳和 nonce timestamp str(int(time.time())) nonce .join(random.choices(string.ascii_letters string.digits, k16)) oauth_params { oauth_consumer_key: 你的_consumer_key, oauth_token: 你的_access_token, oauth_signature_method: HMAC-SHA1, oauth_timestamp: timestamp, oauth_nonce: nonce, oauth_version: 1.0, } # 业务参数GET 请求就来自 query string query_params { page: 1, status: paid, } signature hmac_sha1_sign( GET, https://api.example.com/v1/orders, query_params, oauth_params, client_secret你的_consumer_secret, token_secret你的_token_secret, ) # 签名值本身要做百分号编码后放进 Authorization 头 oauth_params[oauth_signature] rfc3986_encode(signature) auth_header OAuth , .join( f{k}{v} for k, v in oauth_params.items() )注意最后一个细节计算签名时 oauth_signature 必须排除在外算完之后再把签名值加回去而且签名值本身也要做一次百分号编码。因为 Base64 里可能包含、/、放进 HTTP 头之前必须转成安全字符RFC 5849 的示例里那个签名结尾的就被编码成了%3D。3.3 requests 生态的开箱即用方案手写实现能帮你理解原理但生产环境里我建议优先用 requests 的 OAuth1 认证模块它把上面这套繁琐的流程全部封装好了from requests_oauthlib import OAuth1 import requests auth OAuth1( client_key你的_consumer_key, client_secret你的_consumer_secret, resource_owner_key你的_access_token, resource_owner_secret你的_token_secret, signature_methodHMAC-SHA1, signature_typeAUTH_HEADER, rsa_keyNone, ) resp requests.get( https://api.example.com/v1/orders, params{page: 1, status: paid}, authauth, ) print(resp.status_code) print(resp.json())这个模块默认就会帮你生成时间戳和 nonce自动收集 params 里的业务参数参与签名发请求前把 Authorization 头拼好。但要注意如果业务参数是通过 data 以表单格式提交的Content-Type 为 application/x-www-form-urlencoded它也支持同时收集表单体和 query string 里的参数参与签名。如果参数是以 JSON 体提交的那 JSON 体不参与签名只有 query string 和 Authorization 头里的 OAuth 参数参与这个区别特别容易踩。4. PLAINTEXT 签名方法全解析4.1 签名的字面意义拼接即签名PLAINTEXT 这个名字很直白它根本不是传统意义上的签名没有哈希、没有摘要、没有 Base64。它的规则就是把客户端密钥和令牌密钥拼起来签名值 rfc3986_encode(client_secret) rfc3986_encode(token_secret)你没看错签名值等于签名密钥本身。这就意味着 PLAINTEXT 签名没有任何防篡改能力——任何拿到 consumer_secret 和 token_secret 的人都能伪造出一个合法的签名因为签名就是密钥本身。那它存在的意义是什么RFC 5849 自己都写了PLAINTEXT 方法不应在非安全通道上使用只有在传输层已经加密比如 HTTPS/TLS的环境下才考虑。换句话说它的安全不来自签名本身而来自底层的 TLS。在 TLS 的保护下客户端密钥和令牌密钥不会被窃听拼接密钥这个动作就变成了一种身份声明服务端比对密钥一致即通过。4.2 Python 实现与安全边界PLAINTEXT 的实现简单到让人怀疑是不是漏了步骤def plaintext_sign(client_secret, token_secret): PLAINTEXT 签名本质就是拼接密钥 return rfc3986_encode(client_secret) rfc3986_encode(token_secret) signature plaintext_sign(你的_consumer_secret, 你的_token_secret)如果你用的是 requests 的 OAuth1 模块把签名方法换成字符串即可auth OAuth1( client_key..., client_secret..., resource_owner_key..., resource_owner_secret..., signature_methodPLAINTEXT, )关于 PLAINTEXT 的安全边界我说几点实际判断依据。第一如果某个老接口要求 PLAINTEXT它八成要求所有请求必须走 HTTPS否则密钥在网络里裸奔。第二如果服务端给你发的令牌密钥里含有特殊字符记得按 RFC 3986 编码后再拼接不然密钥里带个会让服务端解析错位你会看到一堆莫名其妙的验签失败。第三很多平台虽然文档里写着支持 PLAINTEXT但实际上只是支持而已默认还是推荐 HMAC-SHA1因为 PLAINTEXT 过于简单出问题说不清楚。我自己的经验是能用 HMAC-SHA1 就绝对不用 PLAINTEXT只有在接口明确要求、或者客户端和服务端都在可信内网环境时才考虑后者。5. HMAC-SHA1 与 PLAINTEXT 的全面对比5.1 安全性从哈希对抗到明文暴露两种方法的安全模型天差地别。HMAC-SHA1 里即使攻击者截获了完整的签名基字符串和最终签名值他也无法反推出签名密钥——因为 HMAC 的单向性和密钥混淆机制保证了这一点。攻击者最多能做的是猜测密钥或者做离线暴力破解只要密钥强度够这条路基本走不通。PLAINTEXT 就完全不一样了签名值直接等于密钥拼接串截获到签名等于同时拿到了两个密钥。如果传输通道被中间人劫持整个授权体系直接沦陷。因此 PLAINTEXT 的安全性完全依附于 TLS一旦 TLS 被绕过或者配置错误它就没有任何兜底能力。还有一个经常被忽略的差异HMAC-SHA1 签名与请求内容强绑定请求参数被改一个字符签名就失效天然防篡改。PLAINTEXT 的签名值只跟密钥有关跟请求内容毫无关系攻击者如果拿到密钥可以随意篡改请求体再重新拼接签名服务端照样验签通过。5.2 场景匹配遗留接口该怎么选实际对接时选择哪种方法通常不是你能决定的而是服务端接口文档写死的。但理解它们的适用范围能帮你预判问题和做技术方案评估。对比维度HMAC-SHA1PLAINTEXT签名本质带密钥的哈希摘要密钥明文拼接防篡改能力强请求内容绑定无仅身份声明依赖传输层安全不强依赖但推荐 HTTPS强依赖必须 HTTPS签名长度固定 28 字符左右的 Base64随密钥长度变化抗重放能力配合 timestamp/honce 可防重放同样靠时间戳和 nonce计算开销需计算 SHA-1 哈希几乎为零适用场合一切需要真实验签的开放平台内网服务、传输层已强化的旧系统如果你的对接方是金融、支付、政务类的老网关大概率是 HMAC-SHA1。如果某个内部系统走的是局域网 HTTPS且要求实现成本最低PLAINTEXT 可能还在用。前几年我遇到一个很有意思的场景某个老平台支持两种签名但生产环境切到 PLAINTEXT 后经常出现超时重试导致订单重复下单的问题后来我分析了一下这不是签名算法本身的锅而是重试机制没有处理幂等时间戳和 nonce 重用导致服务端拒单。这个案例给我的教训是签名算法选型只是第一步配套的重试和幂等策略才是真正容易出事的环节。5.3 性能与实现成本性能上HMAC-SHA1 的 SHA-1 计算在现代 CPU 上大概是微秒到几十微秒级别对单个 HTTP 请求来说完全可以忽略。PLAINTEXT 省掉了哈希计算但在服务端压力测试中两者的吞吐量差异通常连 1% 都不到。真正影响性能的是验签时涉及的参数归一化、排序和字符串拼接这些环节两种算法一模一样。实现成本方面PLAINTEXT 确实低到离谱三五行业务代码就能搞定。但如果你用的是封装好的客户端模块切换到哪个算法都只是改一行配置的事。所以我的结论很明确除非接口文档明确要求 PLAINTEXT否则一律优先 HMAC-SHA1。安全余量更大出问题好排查也不会给人留下这个团队连签名都不懂的印象。6. 实战调试与高频踩坑记录6.1 签名不匹配的六大典型原因签名 401 是 OAuth 1.0a 对接里最常见的报错而且报错信息通常只有一句signature mismatch没有更多提示。我把这几年踩过的坑归纳成六大原因按出现频率排序序号原因具体表现1参数编码不一致空格被编成而不是%202参数字符串没有二次编码和在基字符串里没有变成%26和%3D3请求参数收集不全漏掉了 query string 里的业务参数或漏了表单体参数4密钥拼接错误token_secret 为空时漏掉了那个5URL 基础地址不一致带了默认端口、大小写不一致、多了尾部斜杠6参数排序错误没有按编码后的名字排序或名字相同时没按值排序其中第 2 条最隐蔽。很多人写完代码后自己先打印签名基字符串检查发现里面对比文档少了一层%26却不知道错在哪。这里提醒一句你在日志里看到的签名基字符串应该是 URL 和参数字符串都被编码后的形态参数字符串内部原本的分隔符在基字符串里就是%26。6.2 timestamp 与 nonce 的隐藏约束OAuth 1.0a 要求请求里必须带 oauth_timestamp 和 oauth_nonce这俩字段不是摆设。时间戳是 Unix 秒级时间服务端通常会校验它的新鲜度。我遇到过有的服务端把窗口设成 5 分钟有的设成 15 分钟还有极端的只认当前时间前后 30 秒。如果你的客户端机器时间和服务端差太多比如没做 NTP 同步的服务器签名会因为时间戳超窗而失败。排查这类问题时先看服务端日志里有没有 timestamp 相关的拒绝原因再用date %s对比一下本地和服务端的时间误差。nonce 的作用是防重放。同一个时间戳下nonce 应该是唯一的随机字符串。有的服务端会把用过的时间戳nonce 组合存起来遇到重复直接拒绝。这里有个实际坑如果你的 HTTP 客户端启用了自动重试而重试时复用了同一个请求对象时间戳和 nonce 也跟着复用了服务端就会把它判定为重放攻击。解决方法是每次重试都重新生成时间戳和 nonce或者在请求层做好幂等控制。6.3 一套可复用的排查流程签名对不上时别盲目改代码按下面这套流程走基本能在十分钟内定位问题。第一步抓出你自己生成的签名基字符串打印出来。第二步和服务端提供的验签日志如果对方愿意给的话逐字符比对。很多平台虽然不告诉你签名的具体参数但会在文档里提供一个签名生成示例你拿自己的请求参数跑一遍示例看结果是否一致。第三步如果中间对不上用分片法排查先只看 URL 部分编码对不对再看参数字符串排序对不对最后看是不是二次编码的问题。第四步如果签名基字符串完全一致但签名值还不对问题就出在密钥上——检查 consumer_secret 和 token_secret 是否填反了、是否多了空格、token_secret 为空时有没有保留。调试时我习惯写一个只打印不发送的函数把签名基字符串、签名密钥、最终签名值全部输出def debug_signature(method, url, query_params, oauth_params, client_secret, token_secret): base_string build_signature_base_string( method, url, query_params, oauth_params ) key rfc3986_encode(client_secret) rfc3986_encode(token_secret) signature base64.b64encode( hmac.new(key.encode(), base_string.encode(), hashlib.sha1).digest() ).decode() print( Signature Debug ) print(Base String:, base_string) print(Signing Key:, key) print(Signature:, signature) return signature这个函数被我写进过好几个项目的基础工具类里排查效率提升非常明显。还有一个经验网上有些教程会用 Python 的 dict 去保存参数但 Python 3.7 之后 dict 保持插入顺序这会让排序后拼接显得好像没问题一旦你改成手动拼接字符串就忘了排序。排序一定要显式做别依赖任何隐式顺序。踩过几次坑之后我个人的体会是OAuth 1.0a 签名最难的不是算法本身而是和对方保持完全一致。客户端和服务端是两套独立实现任何一处在编码、排序、收集参数上有细微差异签名就废了。所以对接前一定要先把对方的签名示例吃透把自己的实现打印出来逐字节比对不要想当然。这篇文章里的代码我都是按 RFC 5849 的规则写的你直接抄去用基本不会有问题但生产环境务必先用沙箱用例验证一遍确认无误再上线。

相关新闻

Modelship:基于 Ray 的模型集群,统一管理多模型与动态扩缩容

Modelship:基于 Ray 的模型集群,统一管理多模型与动态扩缩容

最近在技术社区刷到一个挺有意思的开源项目,名字叫 Modelship,定位是“基于 Ray 的模型集群”。第一眼看到标题的时候,我还以为又是个玩具级 demo,翻了一下发布信息和代码结构,发现它的切入点很准:很多团队…

2026/10/20 1:10:24 阅读更多 →
基于Hadoop与Python的金融投诉迁移分析与趋势预测平台

基于Hadoop与Python的金融投诉迁移分析与趋势预测平台

1. 从投诉数据到决策依据:这个平台到底在解决什么问题金融投诉数据这几年积累得越来越厚,但真正能把它们用起来的人并不多。大多数团队的做法是:投诉来了,登记进系统,处理完归档,然后就没有然后了。数据躺在…

2026/10/20 0:12:43 阅读更多 →
Python+Hadoop跨平台电商评论情感分析系统

Python+Hadoop跨平台电商评论情感分析系统

1. 这不是“做个毕设”,而是一次真实的数据工程实战演练你搜“计算机毕设怎么做”,页面上堆满“一键生成”“模板下载”“包过代做”的广告,但真正能让你在答辩现场被导师追问细节、在实习面试时被技术主管当场考算法、在入职后能直接接手数据…

2026/10/20 0:11:57 阅读更多 →

最新新闻

微服务商城对接支付宝支付:独立支付服务与异步回调实战

微服务商城对接支付宝支付:独立支付服务与异步回调实战

1. 支付服务在微服务架构里的定位做电商项目,前面把商品、库存、订单、用户这些服务都搭起来之后,早晚要面对一个绕不开的环节——支付。我这次在基于Vue和SpringCloudAlibaba的微服务商城项目里,把支付单独拆成了一个平台服务,专…

2026/10/20 7:12:54 阅读更多 →
前缀表达式求值:从右往左扫描的栈式算法详解

前缀表达式求值:从右往左扫描的栈式算法详解

这是一道很经典的数据结构题目,很多朋友在学栈和递归的时候都会遇到。我当年第一次做这题也卡了一阵子,倒不是代码写不出来,而是方向搞反了,一直在想怎么从左往右处理。后来想通前缀表达式的本质之后,代码其实就几行。…

2026/10/20 7:12:54 阅读更多 →
QoS原理与实战:从拥塞控制到带宽分配,一套完整的网络服务质量配置指南

QoS原理与实战:从拥塞控制到带宽分配,一套完整的网络服务质量配置指南

网络时不时卡顿、视频会议断断续续、后台同步任务把带宽占满,这种场景在现网里太常见了。QoS(服务质量,Quality of Service)就是专门用来处理这类问题的机制集合:在有限的带宽条件下,按业务的重要程度重新分…

2026/10/20 7:12:54 阅读更多 →
AI芯片软硬件协同设计:算子映射、指令集与存储调度

AI芯片软硬件协同设计:算子映射、指令集与存储调度

每个做AI加速器的团队,几乎都遇到过这种场景:硬件团队说MAC阵列利用率跑到了98%,软件团队盯着执行时间说这绝对没到预期,两边各拿出一套数据,结果谁都没错。问题就出在软硬件协同设计上,而且多半不是单点失…

2026/10/20 7:12:54 阅读更多 →
Delphi 11数据库界面增强:EhLib 10.0.031实战指南

Delphi 11数据库界面增强:EhLib 10.0.031实战指南

简介:EhLib 10.0.031 是一款专为 Delphi 11(特别是 11.1.5 环境)深度适配的高性能第三方组件库,面向中高级 Delphi 桌面与数据库应用开发者,显著提升图形处理、多源数据库交互、专业报表生成及数据可视化开发效率。资源…

2026/10/20 7:12:54 阅读更多 →
Repast多智能体建模实战:从SIR模型到Headless批量仿真

Repast多智能体建模实战:从SIR模型到Headless批量仿真

简介:一份针对 Repast 建模与仿真平台的英文原版教程与配套实例,面向社会科学、经济学及复杂系统领域的研究者,也适合希望快速上手多智能体仿真的学生和工程师。整个资源包内共包含72个文件,主体为66个HTML/HTM格式的教程页面&…

2026/10/20 7:11:54 阅读更多 →

日新闻

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/19 10:02:04 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出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/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 阅读更多 →