十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

GB/T 27930-2015 BMS充电协议测试实战:从报文解析到故障注入

GB/T 27930-2015 BMS充电协议测试实战:从报文解析到故障注入 做BMS测试这些年我最怕听到的一句话不是“桩坏了”而是“协议好像不对”。去年秋天在台架上遇到过一个特别磨人的问题BMS能正常上电充电机模拟器也把CHM发出去了BMS也回了BHM但每次走到CRO准备就绪那一步模拟器就报“接收到BST超时”整个充电流程直接中断。翻了差不多两天代码最后发现问题根本不在协议栈而在一条充电唤醒线上——低压上电后BMS以为自己醒了报文发送使能却没有真正拉起来。那之后我彻底想明白一个道理GB/T 27930-2015这套充电协议表面看只是CAN报文交互规则实际测试时牵涉的却是硬件、底层软件、整车上下电逻辑和外部充电桩实现的全部耦合。这篇文章就把我在BMS测试中对这套协议的底层理解、测试方法、用例设计和踩坑经验完整梳理一遍给正在跟27930较劲的同行一份可以直接参考的实战地图。1. 为什么GBT27930-2015测试是BMS开发绕不过去的坎1.1 充电协议在整车充电链路中到底管哪一段很多人刚接触BMS测试时会混淆几个标准的分工。物理层连接、充电接口的机械结构、锁止逻辑、CC/CP信号属于GB/T 18487.1和GB/T 20234的范畴充电机和BMS之间的CAN总线应用层通信协议才是GB/T 27930-2015的管辖范围。标准全称是《电动汽车传导充电用电池管理系统与充电机之间的通信协议》它定义的不是“怎么把电充进去”而是“充电机和BMS之间怎么通过报文协商充电参数、怎么切换状态、怎么安全中止”。实际测试中这两层经常混在一起。比如充电枪插好后BMS首先要通过低压辅助上电A/A-获得工作电源然后检测CC信号确认充电连接是否可靠再通过CP信号判断充电机是否准备好最后才是CAN报文层面的握手。很多测试问题都不是CAN报文解析错误而是卡在更早的物理接口逻辑上。我之前遇到过一台样车报文层面一切正常但充电就是无法启动排查到最后是充电口电子锁的到位信号没有反馈给BMS导致BMS一直不认为“插枪完成”自然也就不会进入CAN握手流程。GB/T 27930-2015的实际价值在于它把充电过程中的角色、状态和动作全部标准化了。充电机是服务方BMS是请求方两者通过一整套固定ID的CAN报文完成从握手、辨识、参数配置、充电执行到结束统计的完整闭环。对BMS测试工程师来说吃透这套协议不光是能看懂报文更要能判断“在某个状态收不到某条报文时系统应该如何表现”才是关键。1.2 2015版相对2011版动了哪些关键点现在不少存量桩还在用老版本新开发的BMS如果只测2015版到现场照样可能出兼容性问题。我梳理了两个版本差异中直接影响测试的关键点用表格列出来差异维度2011版特点2015版特点对测试的影响配套物理层标准对应GB/T 18487.1-2001对应GB/T 18487.1-2015需要重新核对低压上电、绝缘检测时序报文周期与超时约束部分周期定义比较宽松握手、配置、充电阶段周期和超时阈值更明确测试判据更能量化中止充电原因字段定义较粗故障归因困难细化了多条中止原因编码需要覆盖更多异常编码路径信号字段与单位部分字段单位/分辨率存在歧义统一单位补充必要字段解析脚本的期望值要重新计算状态跳转细节部分转移条件存在二义性明确了重复发送逻辑和条件状态机测试要更严谨如果你手头有一个从2011版迁移到2015版的项目最需要注意的不是报文字段本身而是测试工具链里的DBC文件、上位机解析脚本和充电机模拟器的状态机都要跟着更新。我见过不少团队把新标准报文导入旧版DBC结果所有报文都按错误偏移解析测试结论全部作废。1.3 协议一致性测试和系统级充电功能测试是两码事协议一致性测试关注的是“BMS是否符合标准定义”比如报文ID、周期、数据编码、状态跳转是否合规系统级充电功能测试则关注“BMS放到整车上、接上真实充电桩能不能安全可靠地把电充进去”。这两者的测试思路完全不同。一致性测试适合在实验室里用HIL或总线仿真工具做每个PDU、每个状态转移都按标准逐条验证适合自动化回归。系统级测试则要面对真实充电桩的各种实现差异同一个国标不同桩企在报文周期精度、故障容错策略、绝缘检测时序上都有各自的处理方式。最典型的就是老旧桩的CAN报文周期漂移很严重BMS如果对超时判断过于严格就会出现“别人能充、你充不了”的兼容性问题。所以我的建议是一致性测试一定要做扎实这是底线但系统级兼容性测试也必须留够时间尤其是拿新BMS去匹配市面上不同品牌、不同年份的充电桩。实验室里脚本模拟器全过真桩上照样可能握手失败这类案例在行业里太常见了。2. 协议层骨架报文ID、传输机制与校验规则2.1 从CAN ID看BMS和充电机的“身份”GB/T 27930-2015基于CAN 2.0B扩展帧协议29位ID在整个协议栈里既是报文的“身份证”也包含了发送方的角色信息。一个非常直观的规律是BMS发出的报文ID大多以F456结尾充电机发出的报文ID大多以D11A结尾这和标准里为BMS和充电机分配的逻辑地址有关。实际测试中我建议把常用报文的ID整理成一份速查表贴在工位旁边报文名称发送方CAN ID常见实现典型周期CHM 握手报文充电机0x181156A4250msBHM 握手报文BMS0x180156A4250msBRM 辨识报文BMS0x1801F456多包发送BCP 参数配置报文BMS0x1802F456250msCTS 时间同步报文充电机0x010CD11A50msCML 最大输出能力报文充电机0x010DD11A250msBRO 充电准备就绪报文BMS0x1803F45610msCRO 输出准备就绪报文充电机0x010AD11A10msBCL 充电需求报文BMS0x1807F45650msBCS 充电总状态报文BMS0x1808F456250msBSM 电池状态信息报文BMS0x1809F456250msCCS 充电机充电状态报文充电机0x010BD11A50msBST 中止充电报文BMS0x1804F456事件触发CST 中止充电报文充电机0x010ED11A事件触发需要提醒一句表格里这些ID是测试工具DBC中比较常见的写法但不同项目的DBC可能把PDU特定域写得不完全一样。正式项目里一定要以标准原文和官方DBC为准不要靠记忆里的ID去过滤报文否则漏报文、错报文排查起来非常痛苦。2.2 单包报文与多包报文的传输差异GB/T 27930-2015的报文分两类一类是数据长度不超过8字节的单包报文像CHM、BHM、CTS、CML、BRO、CRO、BCL、BCS、BSM、CCS这些直接用一个CAN帧就发完了另一类是数据长度超过8字节的多包报文典型的就是BRM和BCP需要按传输协议拆成首帧、连续帧来发送。多包传输机制理解起来不复杂但测试时特别容易出问题。以BCP为例数据可能十几个字节发送方先发一个首帧里面携带完整报文的总长度接收方收到首帧后返回流控帧告知发送方“你可以继续发连续帧了”然后发送方按顺序把剩余数据一帧帧发完。这里面有几个参数特别关键STmin是连续帧之间的最小间隔时间BlockSize是收到多少个连续帧之后才需要再次等待流控帧。如果STmin设置过小接收方的接收缓冲区可能溢出如果BlockSize处理错误双方就会在流控逻辑上卡死。我在测试中最常踩的坑是模拟器发多包报文时首帧长度字段写错或者连续帧的帧序号从0开始而不是从1开始导致接收端重组失败后整个充电流程卡住。这类问题用普通CAN工具看数据时很难发现因为每一帧单独看都是合法的必须在传输层做重组解析才能定位。所以测试工具一定要具备多包报文解析能力最好能把重组后的完整数据按字段解码而不是只看原始帧。2.3 计数器与校验和的隐性约束GB/T 27930-2015的报文数据场里通常带有计数器和校验和字段。计数器是一个累加值每发一帧加1用来让接收方判断报文是否连续校验和则是对报文有效数据字节做计算的校验值接收方必须校验通过才认为这帧报文有效。很多BMS项目在早期开发时为了跑通流程会先把校验和检查关掉这样确实能让充电流程尽快跑起来但到了EMC测试和真桩兼容性测试阶段问题就集中爆发了。总线上一旦出现干扰或位错误接收方如果不校验就会把错误数据当成正常数据用比如把充电需求电压从400V错解成410V后果可能是长时间的过压充电。我的建议是协议栈从第一天起就把校验逻辑写上哪怕初期先用最粗暴的求和校验。另外校验算法一定要和DBC、标准附录逐一对齐。有些报文在计算校验和时包含计数器字节有些则排除顺序和缩放因子都可能有差别。测试时可以用报文回放的方式把录制的真实报文重新喂给被测试对象看它能不能正确识别每一帧的校验结果。只有校验逻辑全通了后面做故障注入和EMC测试才有意义。3. 充电全流程状态机拆解从低压上电到统计报文3.1 握手阶段CHM与BHM的时序细节充电流程的起点不是CAN报文而是物理连接。充电枪插入车辆充电口、电子锁锁止、低压辅助上电建立起来之后BMS才具备发送报文的条件。此时充电机会以250ms的周期发送CHM握手报文内容包含充电机通信协议的版本号。BMS收到CHM后同样以250ms周期回复BHM握手报文上报自己的通信协议版本号。握手阶段的核心测试点是时序。低压辅助上电和CHM之间的先后关系、BMS上电后多快能进入CAN通信状态、如果CHM在低压上电后迟迟不来BMS应该怎么表现这些都是要覆盖的用例。实际测试中我习惯用示波器同时抓低压辅助电源的电压波形和CAN总线上的报文时间戳看BMS是否在允许的时间窗口内完成上电初始化并回复BHM。很多BMS在低温环境下启动时间会变长如果启动时间超过了充电机等待超时的阈值握手就永远成功不了——这类问题在冬天实车测试中特别常见。还有一个人容易被忽略的点热插拔场景。充电过程中用户突然拔枪或者插枪后电子锁没有锁止就开始上电这些异常时序会对握手状态机产生什么影响必须在测试用例里明确写出来。标准里定义了正常流程但各种异常组合只能靠测试工程师自己推演。3.2 辨识与参数配置BRM、BCP、CTS、CML的配合握手完成后BMS需要把自己的“身份信息”和“充电边界条件”告诉充电机。BRM是BMS辨识报文包含动力蓄电池类型、电池生产厂商、电池型号等信息由于信息量大通常按多包报文发送。紧接着BMS发送BCP参数配置报文把最高允许充电总电压、最高允许充电电流、动力蓄电池标称容量、标称总能量等关键参数上传给充电机。充电机收到BCP后会开始发送CTS时间同步报文50ms周期和CML最大输出能力报文250ms周期。CTS用于双方时间基准同步CML则告诉BMS这台充电机当前能输出的电压、电流上限。这个阶段实际上是双方在“对齐边界条件”BMS说“我只能接受最大400V、200A的充电参数”充电机说“我最多能输出500V、250A”最终的实际需求必须取两者的交集。测试这个阶段我最关注的是参数换算。BCP里的电压、电流、容量字段都有明确的缩放因子和偏移量DBC文件解析错误时最典型的症状就是BMS上报的最高允许充电电流比实际值整整大了10倍充电机这边看到后可能直接拒绝进入充电状态或者按错误参数输出电流。所以新版BMS在测试前务必用协议一致性测试工具把BCP逐字段解码后的物理值和设计值做比对。3.3 充电阶段BCL、BCS、BSM、CCS的周期交互参数配置对齐后BMS发送BRO充电准备就绪报文充电机确认安全条件满足后回CRO输出准备就绪报文此时高压回路真正闭合充电电流开始建立。进入充电阶段后报文交互进入一个高频、多报文并行的状态BCL充电需求报文以50ms周期发送承载BMS当前请求的充电电压和充电电流目标值这是充电机执行输出的核心依据。BCS充电总状态报文以250ms周期发送包含当前充电电压测量值、充电电流测量值、估算剩余充电时间、最高/最低单体电压及对应编号。BSM电池状态信息报文以250ms周期发送包含最高/最低电池温度及编号、绝缘电阻值和系统状态。充电机则通过CCS充电状态报文以50ms周期反馈自己的输出电压、电流和状态。这里要特别强调BCL报文背后的SOP计算逻辑。很多人以为充电需求电流是随便填的实际上它来自BMS内部的SOP功率估算模块。SOP估算会综合考虑当前SOC、温度、单体电压窗口、电流限制约束以及电池等效电路模型的动态特性最终输出一个当前可用的充放电功率窗口。充电时BMS把功率窗口换算成目标电压下的需求电流填进BCL报文的“充电需求电流”字段。我在测试中遇到过不止一次因为SOP估算模块输出异常导致BCL需求电流大于充电机CML上报的最大能力结果CRO刚建立就被CST中止的案例。BCS报文还有一个让测试工程师头疼的细节最高/最低单体电压值来自AFE采样芯片而AFE在均衡开启时会造成短暂的单体电压跳变如果软件滤波策略不及时BCS里就可能出现电压毛刺。充电机一旦判断电压异常会立刻中止充电。所以做充电阶段测试时要把均衡动作和BCS报文的时序放在一起分析否则很容易把BMS的均衡策略误判成通信问题。3.4 结束阶段BST、CST、BSD、CSD的收尾逻辑充电结束不是简单断开电流就完事而是有一整套报文收尾逻辑。结束触发方式主要有三种正常充满时BMS发送BST中止充电报文原因编码是“充电完成”BMS检测到电池电压或温度异常时也会发送BST但原因编码不同充电机侧出现故障比如绝缘故障、输出过压则会主动发送CST中止充电报文。收到中止报文后对方要停止输出双方最后还要交换BSD/CSD统计数据报文。BSD是BMS统计数据包含充电累计时间、累计充电电量等信息CSD是充电机统计数据由充电机侧发出。这两条报文的意义在于充电结束后可以让运营平台和车主APP展示本次充电的完整统计信息。测试结束阶段时我一直建议用示波器同时抓接触器控制信号和CAN报文观察切断高压和发送中止报文的先后顺序。如果BMS先断开高压接触器再发送BST充电机还带着负载可能产生拉弧损坏继电器触点。正确的动作通常是在发送中止报文的同时或之前由充电机先停止输出BMS再断开接触器。这属于系统级时序问题单看CAN日志很难发现必须结合硬件信号才能验证。4. 实战测试用例设计正常路径、异常注入与边界覆盖4.1 正常充电流程的判定标准一份合格的充电协议测试用例不能只写“能充进去电就算过”。我的做法是把每个阶段拆成可量化的通过判据测试阶段通过判据物理连接检测插枪后电子锁锁止、CC信号有效、低压辅助上电建立握手阶段BMS在收到CHM后按规定周期回复BHM版本号字段正确参数配置BRM/BCP报文ID、周期、数据编码与设计值一致准备就绪BRO在参数配置完成后触发CRO到达后高压接触器闭合时机正确充电阶段BCL需求电压/电流与SOP功率窗口一致CCS反馈值跟随正常结束阶段BST/CST原因编码正确BSD/CSD统计值与实际充电数据吻合每个判据背后都要有具体的报文记录做支撑。比如“BCL需求电流与SOP功率窗口一致”这条需要把BCL报文解析后的电流值和BMS内部记录的目标电流值做比对允许的偏差范围要在测试报告里写清楚。没有量化判据的测试最后出了问题很难追溯是哪一个环节不符合预期。4.2 周期抖动与报文超时的容错测试CAN报文在真实总线上不可能像理想情况那样精确整周期到达充电机的MCU调度延迟、总线繁忙、错误帧重传都会造成周期抖动。BMS对周期性报文通常有超时容忍机制比如连续丢失几帧才判定超时但这个容忍度和充电机的实际行为必须匹配。测试时我会用CANoe或自研脚本把某个报文的发送周期人为拉长从标准的50ms逐步变成60ms、80ms、100ms观察BMS在哪个阈值开始报错或中止充电。还要反向测试把周期缩短看BMS是否会对过快到达的报文产生误判。下面是一个典型的容错测试矩阵注入方式预期表现我的经验周期性丢失BCL报文连续丢3帧应中止充电并发送BST有的BMS丢第4帧才动作要在需求里明确阈值BCL周期抖动±20%BMS不应误判超时抖动超过20%时要跟踪报文时间戳确认CRC错误的BCL帧本帧被丢弃不应影响状态机连续错误帧可能触发CAN控制器bus-off要分开测多包报文传输中断重组超时后应复位状态或重试有的实现会卡在接收状态必须覆盖这类测试的价值在于能把BMS的容错边界测出来。很多现场兼容性问题本质就是BMS的容错区间和充电桩的实际行为区间没有交集。4.3 故障注入让BMS“感到疼”才算有效测试故障注入测试的目的不是验证“协议栈不会崩”而是验证“在真实故障下系统能按设计进入安全状态”。我把它分成三个层面物理层故障注入包括CAN_H和CAN_L短接、CAN线对地短路、终端电阻断开、CAN地偏移等。CAN地偏移是最容易被忽视的一项测试步骤其实不复杂先用万用表测量设备端CAN_GND和系统电源地之间的压差再用示波器观察CAN_H/CAN_L的共模电压正常情况下隐性位电压大约在2.5V左右显性位CAN_H约3.5V、CAN_L约1.5V。如果地偏移导致共模电压偏离这个范围通信误码率就会上升。测量时要注意仪器必须隔离不能把CAN_GND直接和大地短接否则会破坏测试环境。数据链路层故障注入主要是错误帧、填充错误和位错误。可以用CAN工具周期性发送错误帧观察BMS的CAN控制器错误计数是否快速上升以及什么时候进入bus-off状态。这里要注意bus-off恢复后的重新初始化过程很多BMS没有做完善测试时能看到控制器恢复通信但协议状态机早就卡死了。应用层故障注入则是针对报文内容做手脚修改校验和让接收端丢帧修改计数器让帧被判定为乱序或者直接发送一个非法状态跳转报文比如还没完成握手就直接发CRO看BMS能否正确拒绝。这些用例相当于给BMS做“安全体检”只有故障注入下行为仍然可控才算真正测到位。5. 实测中的典型故障与完整排查链路5.1 故障现象握手一直失败但总线上一堆报文有一次在台架上做充电协议测试遇到的问题很典型充电机模拟器正常运行CHM、BHM、BRM全都按预期交互了但模拟器一直反馈“BCP报文校验失败”充电流程卡在参数配置阶段进不了CTS/CML。从总线日志看BCP报文确实在发数据也能解析出电压、电流字段数值都合理但报文末尾的校验和字段怎么看怎么别去。这种问题最折磨人因为从现象上看“报文都在”很容易让人怀疑是模拟器脚本的问题。换了一个模拟器、换了一条CAN线之后故障依旧我才把排查焦点放回BMS侧。5.2 从物理层到应用层的定位顺序排查这类问题我习惯严格按从物理层到应用层的顺序走避免跳步浪费时间。第一步检查物理层。用万用表量CAN_H和CAN_L之间的终端电阻确认在60欧姆左右用示波器看总线空闲时的隐性电平以及报文发送时的显性电平幅度排除电平不达标和地偏移问题。第二步检查数据链路层。看CAN控制器的错误计数记录错误帧的数量和类型。如果错误计数持续增长说明物理层或波特率设置有问题如果错误计数为零说明总线的“搬运能力”正常。第三步检查传输层。BCP是多包报文要确认首帧、流控帧、连续帧的交互过程是否完整。看首帧里声明的报文总长度和实际接收到的数据长度是否一致连续帧序号是否连续。第四步检查应用层。把BCP报文逐字节解码和标准定义逐字段比对重点看校验和的计算范围、计数器字节的位置、缩放因子是否正确。5.3 一个让人印象深刻的根因校验和计算的“半字节”陷阱最后锁定的根因是BMS协议栈在计算校验和时把数据字节的高低半字节做了交换。标准里要求对有效数据字节按顺序求和后取低8位但实现代码里因为对接了另一个项目的底层库把每个字节先做了位反转再参与求和。于是一个正常情况下应该得到0xA5的校验和实际发出来的是0x5A接收端一比对就失败。这个问题单独看任何一帧报文都发现不了因为数据字段的数值都是对的只有校验和不匹配。我的排查手段是手工挑了两帧报文用Excel按标准算法手工算了一遍校验和发现和报文里带的值正好高低半字节互换这才定位到具体代码。修复后我写了一个小的Python脚本循环读取录制的日志文件重新计算每一帧的校验值并和总线上截获的值做比对确认全部通过后才算收工。这个案例给我最大的教训是排查“报文都在但流程走不通”的问题一定要动手算一遍校验和而不是只做字段级解码。工具自动解析会跳过校验环节很多隐藏问题就这么被淹没了。6. 测试环境搭建从CANoe到自研工具链6.1 硬件选型CANoe、PCAN与国产CAN卡的取舍充电协议测试对CAN工具的要求不只是“能发报文”还要能精确控制报文周期、注入故障、解析多包报文、记录带时间戳的总线数据。市面上的主流方案我大概列了一个对比工具方案价格易用性协议支持适用场景CANoe VN系列高高有现成的27930协议栈插件实验室认证测试、一致性测试PCAN-USB中等中等需自己写脚本车载路试、产线快速验证周立功USBCAN低中等需自己写脚本预算有限的项目、教学环境自研CAN盒看实现低完全自己开发大批量产线测试、定制化自动化在实验室做正式协议一致性测试CANoe依然是首选它最大的价值是有现成的协议栈插件和CAPL脚本环境可以快速模拟充电机或BMS也能自动生成一致性测试报告。但CANoe的价格让很多小团队望而却步这时候PCAN由Python-can库驱动也完全能满足日常的报文监控和脚本化测试。我的习惯是台架上用CANoe做深度分析路试和现场排障用PCAN加自研的简易日志工具产线自动化则用Linux工控机加CAN卡跑Python脚本实现一键测试。6.2 用CAPL脚本模拟充电机的关键套路模拟充电机不能只停留在“定时发报文”这个层面核心是要实现一个完整的状态机。下面是一个最简单的CAPL脚本框架用来定时发送CHM并接收BMS的BHM响应variables { msTimer timerCHM; int state 0; int counter 0; } on start { setTimer(timerCHM, 10); } on timer timerCHM { // 进入握手阶段后按250ms周期发送CHM if (state 0) { message CHM msg; msg.id 0x181156A4; msg.dlc 8; msg.byte(0) 0x01; // 协议版本号等字段按实际填写 // 计算校验和并填充到对应字节 output(msg); } setTimer(timerCHM, 250); } on message 0x180156A4 { // 收到BMS的BHM后状态切到辨识/参数配置阶段 if (state 0) { state 1; // 启动下一个阶段的定时器接收BRM/BCP } }写模拟器脚本时有一个经验把状态变量独立出来所有报文回调都只做“更新状态”和“按状态发响应”两件事。比如收到BCP后先校验数据合法性再更新内部参数然后把状态机推进到发送CTS/CML阶段。如果你把状态判断逻辑写在每个定时器回调里脚本很快就会乱成一团。再提一个容易被忽视的点模拟器最好能提供一个“故障模式”比如故意丢帧、错校验、乱序发多包。别小看这个功能很多BMS的容错问题就是靠模拟器的故障模式测出来的。6.3 HIL测试与实车测试怎么搭配HIL测试的价值在于可重复、可自动化。用实时仿真模型替代真实电池和真实充电机可以随时复现同一个故障场景适合做回归测试和故障注入测试。比如我想验证“BMS连续丢失BCL帧9帧后是否中止充电”在HIL环境里用脚本定时屏蔽BCL即可完成实车测试则要等电池工况、充电桩配合很难精准控制。实车测试又不可或缺因为HIL模型再精确也模拟不了真实充电桩的硬件差异和整车电磁环境。我的建议是把两者做分工HIL测试覆盖协议一致性、故障注入、边界条件、自动化回归实车测试覆盖真桩兼容性、充电体验、低温充电、EMC干扰下的通信稳定性。HIL测试跑通后再上实车能省掉大量现场排障时间。最后说点自己的体会。做GB/T 27930-2015测试真正难的不是把报文发出来而是把每一个异常场景下系统的行为预期定清楚。我习惯在项目初期就把充电状态机图打印出来贴在工位旁边每次测试不过就去图上找当前状态、输入事件、输出动作比抱着几十页标准文档翻效率高得多。另外一个小技巧把所有充电相关报文录成长期回放文件一旦现场复现问题先把日志按时间对齐再抓接触器状态、低压上下电信号和CAN报文三条线一起看排查速度能快不少。希望这篇梳理对正在跟27930较劲的你有帮助。
返回列表