资讯动态

UDS诊断0x31请求超出范围?可能是ECU状态机变了

发布时间:2026/10/25 1:07:28 来源:尧图企业网站定制
台架上调试一个ECU的诊断功能我遇到过一个特别拧巴的问题诊断仪明明还连着OBD口总线上的电平也正常可刚才还能正常执行的一条写入请求原封不动再发一遍对面就回了一个负响应码 0x31。界面上就一行英文requestOutOfRange翻译过来是“请求超出范围”。线还插着物理连接一点问题没有为什么“刚才能用”的操作现在就不能用了这段时间我在好几个项目里反复碰到类似情况也帮同事排查过几回结论几乎都一样大多数时候并不是真的“范围超了”而是ECU内部的诊断状态机已经悄悄换了一副面孔。UDS调试这事儿最迷惑人的就是这种“看不见的状态”。这篇就把这类“0x31谜案”的常见原因、排查思路和避坑经验整理一下给同样被这个NRC折磨过的人做个参考。1. 现象与本质0x31不是“线断了”而是“状态变了”1.1 先看懂UDS的应答规则UDS全称Unified Diagnostic Services统一诊断服务是汽车诊断ECU之间通信的标准协议几乎所有现代控制器都走这套规则。它的工作方式很直白诊断仪Tester发一条请求ECU回一条响应。请求和响应都通过CAN总线承载收不收得到、回不回得对就是诊断会话的基本盘。在UDS里ECU收到请求后只有两种答复路径要么回正响应要么回负响应。正响应的格式是把请求的服务IDSID加上0x40再带上数据。比如你用0x22服务去读一个DID数据标识符正响应就是0x62 DID 读取到的数据。如果ECU认为请求有问题就回负响应格式是0x7F 请求的SID 负响应码NRC。比如你发0x22它回0x7F 22 31说明这条请求被拒绝了拒绝原因码是0x31。这个“0x31”就是标题里说的“#31”。很多工程师习惯在聊天里把它叫“31线”意思是“报31了”跟“掉线”其实是两码事。线插没插好是物理层问题报什么NRC是协议层和状态机的问题。搞清楚这个区别你排查0x31的速度能快一半。1.2 0x31在报文里长什么样在ISO 14229标准里NRC 0x31的官方定义是requestOutOfRange字面意思是请求超出允许范围。它的大致意思是ECU收到了请求也识别了这个服务但是请求里的参数、数据、或者执行条件不在它允许的范围内于是拒绝执行。问题是“超出范围”这四个字的边界非常模糊。我在实际项目里见过的0x31至少能对应七八种完全不同的情况请求的服务ID本身不被支持服务ID支持但子功能不支持请求的DID根本不存在数据段长度不对数据值不在允许范围内当前诊断会话不允许访问这个服务安全等级不够某个前置条件没满足……这些全都有可能被归到0x31上。为什么这么杂因为很多ECU的诊断协议栈在实现时并没有严格按照标准把不同错误细分成0x11、0x12、0x13、0x22、0x33那一堆NRC而是把“参数校验失败”“访问条件不满足”“执行环境不对”统一扔进了0x31这个大筐。尤其在应用层诊断栈里这种简化的实现相当普遍。结果就是0x31成了诊断调试里最高频、也最让人头疼的一个NRC。提示看到0x31别急着查参数表。先问一句这个ECU的协议栈是不是把各种错误都归拢到0x31了如果是排查重点要放在“状态和条件”而不是单纯“报文内容”。1.3 关键认知物理层正常不等于逻辑层正常诊断线还插着只能说明CAN物理链路是通的0x7DF寻址地址能发出去ECU也能收到。但UDS是一个会话型协议ECU内部维护着一套完整的诊断状态机里面至少包含三组状态当前诊断会话模式默认、扩展、编程、安全访问等级锁定、解锁、以及一堆条件标志比如例程执行状态、前置条件是否满足、写保护是否置位。物理线没断不代表状态机还在你上次离开的位置。只要其中任何一组状态发生了迁移刚才还合法的请求下一秒就可能变成0x31。所以说“线还插着”只是排查工作的最表层真正的诊断对象是ECU内部那些看不见的状态。2. 藏在“刚才”里的三类状态变迁“刚才还能用”这句话是排查0x31的关键线索。既然刚才能用说明报文的格式本身大概率没问题问题出在“从刚才到现在”这段时间里ECU的状态发生了什么变化。我总结了三个最典型的状态变迁方向基本覆盖了90%以上的场景。2.1 会话超时ECU悄悄退回默认会话做诊断开发的人都熟悉S3Server这个概念它就是非默认会话的保持时间。ECU处于扩展会话0x03或编程会话0x02时如果在一段时间内没有收到任何诊断请求就会自动退回默认会话0x01。ISO 14229标准里S3Server的默认值是5000ms但很多厂商会自定义成10秒、30秒甚至更长具体看车型的诊断规范。问题通常出在这里你在扩展会话里读某个DID或者执行某个例程操作间隙超过了会话超时时间ECU已经退回默认会话了但诊断仪端的界面还停留在扩展会话的功能菜单上。下一次请求发出去ECU一看当前是默认会话而这个DID或例程只允许在扩展会话里访问于是直接给你一个0x31。这种场景在手工操作诊断仪的时候特别容易踩。人点鼠标的速度远没有状态机切换得快。你翻一翻菜单、查一下资料再回来点执行三五秒就过去了ECU早就回默认会话了。解决办法很简单先发一条10 03切回扩展会话再重新执行目标请求。或者如果你用的诊断软件有“自动保持会话”功能打开它软件会在空闲间隙自动发TesterPresent3E 00保活避免会话静默超时。这里还要多说一句为什么会话不支持的时候有的ECU回0x31而不是0x7F或0x7E按照ISO 14229服务在当前会话不被允许时标准做法是回0x7FserviceNotSupportedInActiveSession或0x7EsubFunctionNotSupportedInActiveSession。但不少老一代协议栈没有实现这两个NRC或者校验顺序是先查参数、后查会话权限导致参数先被判定“超范围”抛出的就成了0x31。理解这个实现差异能帮你少走很多弯路。2.2 安全访问失效解锁并不永久第二个高频原因是Security Access也就是安全访问服务报文里是0x27。很多受保护的诊断服务比如写入标定数据、执行特殊例程前置条件是先做安全解锁。流程一般是Tester发27 01请求种子ECU回种子Tester用算法算出密钥发27 02ECU验证通过后解锁然后才能执行后续的高权限操作。问题在于解锁状态并不是永久有效的。很多ECU对解锁状态设了时间上限比如5秒、60秒超时后自动重新上锁有些ECU在会话切换、上下电、ECU复位之后也会强制清除解锁状态还有的ECU对解锁尝试次数做了限制超过次数直接锁定。你刚才解锁后成功写入了一条数据过一会儿再写第二条中间隔的时间超过了解锁有效期ECU已经重新上锁新的写入请求自然就被0x31拒之门外。遇到这种情况先别急着怀疑报文格式。发一条27 01看看ECU还能不能正常回种子如果能拿到种子说明ECU的解锁流程本身是活的你重新走一遍27 01/27 02再做目标操作就行如果27 01直接回0x33securityAccessDenied或者0x36exceedNumberOfAttempts说明安全状态已经彻底变了有时候还需要先等一段时间或者给ECU下电重启才能恢复。注意0x31和0x33之间没有绝对的分界线。有的ECU实现里安全未解锁的访问会明确回0x33但有些ECU会把这类“访问条件不满足”的情况也合并成0x31。所以排查时不要看到0x31就把安全访问排除掉先发一条27 01探一下成本极低收益很高。2.3 前置条件、操作顺序与“写一次”约束ECU越复杂诊断服务就越像一套严格的门禁系统。写VIN、写配置信息、执行标定例程ECU在执行前会先校验一堆前置条件当前车速是不是0、档位是不是在P挡、高压电是不是已经下电、生命计数器对不对、有没有先写“解锁标志”、当前是不是在允许的会话模式……任何一个条件不满足都有可能回0x31。这里面最隐蔽的是“只能写一次”的DID。最典型的就是VIN码很多ECU内部有一个“VIN已写入”的标志位第一次用2E服务写入成功后这个标志位就被置起来从此以后哪怕你再发完全相同的请求ECU也坚决不接受直接回0x31。这种机制在量产和售后场景特别常见车已经下线交付诊断仪想重写VINECU死活不让写其实就是因为内部写保护已经生效了。还有一种情况是操作顺序问题。有的项目要求先写A请求再写B请求或者必须先执行例程R1再执行R2如果你跳过中间步骤直接发最终请求ECU的回执同样是0x31。原因是协议栈的校验逻辑不只是“报文参数合不合法”它还查了内部状态寄存器——顺序不对状态就不对请求自然被拒。这类问题没法从单条报文里看出来必须看整个操作序列的时间线。3. 实操排查从一次真实复现走完整个流程3.1 复现与抓包先把两次操作对齐遇到“刚才能用现在不能用”的问题我一般不会凭感觉乱试先把“能用”和“不能用”两个场景完整记录下来。用CAN分析工具抓两段报文一段是刚才成功时从开始到结束的全部请求和响应一段是现在失败时发出的请求和返回的负响应。然后放在同一个时间线上对比。重点看三件事第一成功请求和失败请求之间隔了多久这个时间差是否超过了会话超时或安全解锁的有效期第二两段报文之间有没有夹着会话切换命令比如10 01、10 02、10 03第三失败请求之前总线上有没有其他设备发出的复位命令或诊断请求这一点在台架上尤其容易忽略——车上可能同时挂着好几套工具别人的一条请求就把ECU状态给改了。3.2 逐字节对比请求报文本身的差异如果状态对比没看出问题就把两条请求报文一帧一帧地对比。检查这些点服务ID是否一致子功能字节是否相同DID的字节顺序对不对有的ECU用大端有的用小端抄错字节序就会0x31数据长度是否一致数据区里有没有额外的前缀服务ID是不是从物理寻址变成了功能寻址。我印象很深的一个案例是VIN写入。某ECU的2E F1 90服务要求数据区正好是17个ASCII字符第一次写17字节成功了第二次脚本里一个字段拼接错误数据区前头多了一个字节结果就变成0x31。报文看下来就一个字节的差异但ECU校验长度严格多一个少一个都算“超出范围”。所以逐字节对比这条建议真不是凑字数是实打实的排障刚需。3.3 用只读请求探测当前会话状态很多时候与其对着0x31猜原因不如主动发几条只读请求去“问”ECU现在的状态。先发10 01读一下当前会话模式看答复里的子功能字节是01、02还是03再发27 01请求种子看ECU还认不认这个安全等级有条件的话用22服务去读诊断DID里的状态信息比如会话状态、写保护标志、例程执行状态。把这些探测请求的答复和失败请求放在同一个时间轴上问题范围基本就划出来了会话变了就切会话安全失效了就重新解锁条件不满足就查前置条件。这里有个特别快的验证技巧如果怀疑是会话超时就在失败之后先发一条3E 00 TesterPresent保活然后再发目标请求看0x31是否消失。如果消失基本可以锁定就是S3Server的问题。这个方法比翻诊断规范快得多推荐优先尝试。4. 常见原因速查表下面这个表是我在实际项目里整理出来的“0x31排查速查表”按照出现频率排序遇见问题可以先对照着过一遍。触发场景最常见原因快速判断方法解决方向操作间隙过长后报0x31S3Server超时会话退回默认看两次请求的时间戳间隔先发10 03切扩展会话或开自动保活执行高权限写入时报0x31安全解锁失效发27 01看能否拿到种子重新走27 01/27 02解锁流程同一请求换工具后报0x31功能寻址/物理寻址权限差异查看请求的CAN ID改为物理寻址发送上报文中数据长度与预期不符数据区多字节或少字节对照诊断规范逐字节比对修正脚本或软件字段拼接请求的DID/例程ID不存在字面意义的“超出范围”对照DID/例程调查表改用正确ID或确认软件版本是否支持写VIN等数据第二次失败只写一次写保护查ECU写保护状态DID用厂商工具解除写保护或走刷写流程执行例程时条件不满足车速/档位/高压等前置条件查车载状态量将整车置于目标工况再执行ECU刚复位或刷写完成状态全面清零看复位报文或下电事件重新切会话、重新解锁后再操作这张表看起来都是刚需但有一个共同点它们都印证了“0x31不是单点问题而是状态机问题”。排查的时候要习惯性地把“状态变了”放在候选原因的第一位而不是一上来就怀疑报文格式。5. 几个容易忽略的坑5.1 功能寻址还是物理寻址UDS请求可以通过功能寻址发给总线上所有ECU也可以通过物理寻址发给指定ECU。很多整车厂和零部件厂的项目里都规定安全访问、写入、例程这类服务只允许物理寻址功能寻址请求会被直接拒绝有的ECU在收到功能寻址的非法请求时回的就是0x31。如果你手上有两台工具一个走功能寻址一个走物理寻址就会出现“这台工具能用、那台工具不能用”的诡异现象。排查方法很简单看失败请求的CAN ID如果是功能寻址地址改成物理寻址再试一次。5.2 多个诊断客户端抢占会话台架上经常会同时挂好几套工具抓包软件、诊断仪、自动刷写脚本一起上总线上热闹得很。UDS一般同一时刻只允许一个客户端持有诊断会话如果一个工具发了10 03切到扩展会话另一个工具再发同样的请求ECU的状态可能已经被第一个工具改过了第二个工具就会收到意想不到的NRC。这种场景下“线一直插着”但ECU的会话已经被另一个工具“偷”走了。用CAN工具看总线上的所有报文而不只是自己发出的报文才能看到真相。5.3 ECU复位与刷写后的状态清零ECU一旦发生复位不管是下电、看门狗复位、还是刷写完成后的自动重启诊断会话和安全状态都会被清掉。这是做刷写相关诊断时最容易踩的坑刷写流程结束后ECU自动复位你没有重新进入扩展会话、重新解锁就接着执行写入操作0x31几乎是必然的。所以刷写脚本里刷完以后一定要记得加一个“重建诊断会话”的步骤先10 03、再27 01/27 02然后再做后续操作。5.4 开发端如何减少“0x31谜案”如果你正好在做ECU端的诊断栈开发有一个建议值得听进去失败日志里一定要记录内部失败原因码而不是只输出一个NRC。协议栈对外统一回0x31完全没问题但内部日志必须能区分“DID不支持”“长度不合法”“安全等级不足”“条件未满足”这些具体原因。我自己在调试的时候最绝望的瞬间就是看日志看到一行“Send NRC 0x31”什么上下文都没有等于没记。提示一条高质量诊断日志至少应该包含请求的SID、子功能、接收时间、失败原因码、当前会话ID、当前安全等级。写日志的时候多写两行排查时能少熬两个夜。这个习惯救过我好几次。有一次某个ECU的0x31在台架上复现不出来客户那边却天天报后来是让客户那边的测试工程师用诊断仪重新录了一段带时间戳的日志发过来配合内部原因码才定位到是前置条件里“车速不为0”的问题。从那以后我对“日志必须能回答状态问题”这件事深信不疑。说实话踩过几次这种坑之后我现在遇到0x31的第一反应已经不是去翻参数表了而是先问三件事ECU当前的会话在哪个模式安全状态是解锁还是锁定距离上一次真正成功的请求过去了多久这三件事一确认八成的问题都有了方向。诊断调试讲究的就是“状态先行、报文随后”物理线插没插永远只是最表层的那一问。诊断仪、测试脚本、售后工具每个角色都会遇到自己的0x31但背后的排查逻辑是同一套——学会“猜ECU的状态”再用工具验证你的猜测。希望这篇经验能帮你在下一次遇到“#31”的时候少一点迷惑多一点方向感。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑