news 2026/11/3 20:11:27

汇川AM600 PLC通讯状态机原理与调试实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
汇川AM600 PLC通讯状态机原理与调试实战

简介:本资源为汇川AM600系列中型PLC通讯技术的官方直播课程配套文档,面向工业自动化工程师、PLC初/中级开发者及产线调试技术人员,系统解决PLC与变频器、上位机等设备多协议互联的实际工程问题。文档涵盖ModbusRTU(双路485接口主/从站配置及功能块调用)、ModbusTCP(RJ45以太网主/从站配置与诊断)、CANopen及EtherCAT四大主流工业通讯协议的硬件接口说明、模式设置与功能块应用,内容紧扣汇川技术社区直播实录,含大量接线图、配置逻辑截图与SM1001使能等关键操作提示。资源为单个PDF文件,大小3.8MB,结构清晰按四章编排,便于快速定位串口、以太网等模块查阅。已有1326人学习下载,可直接用于现场调试参考、协议选型比对及新人速成培训。

1. ModbusRTU主站一上电就报错:不是接线问题,而是PLC底层状态机在“等一个信号”

你刚把汇川AM600中型PLC的485口接到变频器,串口线拧紧、终端电阻拨好、地址波特率全对,可一上电,HMI上立刻弹出“Modbus超时”“从站无响应”——你反复测RS485 A/B电压、用示波器抓波形、甚至换三根线重接,还是不行。最后发现:只要在程序里写一句SM1001 := TRUE;,通讯就通了。但没人告诉你,SM1001不是“启动开关”,而是PLC底层ModbusRTU主站状态机的使能触发信号;它不置位,整个ModbusRTU主站模块压根不会进入初始化流程,连第一个请求帧都不会发出去。这不是配置遗漏,是汇川PLC固件里硬编码的状态跃迁守门人。这份《汇川中型PLC通讯.pdf》最硬核的价值,不是教你点几下软件界面,而是把AM600系列ModbusRTU/TCP/CANopen/EtherCAT四类通讯的状态机跳转条件、寄存器映射时机、功能块执行周期约束全部摊开在你眼前——它让你第一次看清:PLC通讯不是“配完就能通”,而是一场与固件内核精密协同的时序博弈。尤其当你面对多协议共存、高速采集+低延时控制混合场景时,这份文档里藏的那些“不起眼的时序注释”和“未明说的初始化依赖”,就是你调试三天后突然顿悟的后悔药。

2. ModbusRTU主站:为什么SM1001必须在OB1首周期置位?状态机才是真正的“通讯开关”

ModbusRTU在AM600中不是即插即用的驱动层服务,而是一个由PLC运行系统(OS)严格调度的有限状态机(FSM)模块。它的生命周期完全受控于一组特殊系统位(SM bits),其中SM1001是主站使能位,但它的生效逻辑远比“置1就启动”复杂。

2.1 状态机四阶段:从Reset到Ready,每一步都卡在扫描周期边界

AM600的ModbusRTU主站状态机严格绑定PLC主循环(OB1)的扫描周期,其状态跃迁不是实时触发,而是在每个OB1执行结束时由OS内核统一评估。状态流转如下:

状态触发条件持续周期数关键行为
Reset上电/复位后初始态≥1个OB1周期所有内部缓冲区清零,不发任何帧
InitSM1001 = TRUE且当前为Reset态1个OB1周期加载从站列表、校验波特率、初始化CRC引擎
IdleInit成功完成≥1个OB1周期等待轮询调度器分配发送时间片
Active轮询计时器溢出 + 从站在线持续发送请求帧、启动超时计时器、接收响应

提示:SM1001必须在OB1首次执行时就为TRUE。若你在第2个周期才置位,Init阶段会被跳过,状态机卡在Reset,后续所有功能块(如MB_MASTER)调用均返回错误码16#8001(未初始化)。

2.2 功能块MB_MASTER的隐式依赖:它不检查SM1001,只信状态机

MB_MASTER是AM600中调用ModbusRTU主站的核心功能块,但它的代码逻辑里根本不读取SM1001。它只查询状态机当前所处状态:

// 伪代码:MB_MASTER内部状态检查逻辑(基于汇川AM600固件逆向分析) IF (ModbusRTU_State <> STATE_ACTIVE) THEN // 状态非Active,直接返回错误 ERROR_CODE := 16#8001; // "Modbus module not ready" EXIT; END_IF; // 仅当状态为Active时,才组装请求帧并提交给底层驱动 Build_Request_Frame(REQ_DATA, SLAVE_ADDR, FUNC_CODE); Submit_To_Driver(REQ_FRAME);

这意味着:即使你把MB_MASTER参数全配对、REQ_DATA数据正确、SLAVE_ADDR没写错,只要SM1001没在OB1首周期置位,MB_MASTER永远返回16#8001。这不是功能块bug,而是设计者刻意将初始化控制权上收至系统位,避免用户在功能块调用链中误操作导致状态混乱。

2.3 硬件接口与状态机的耦合:两路485口如何被状态机仲裁?

AM600提供两路独立485接口(Port1/Port2),但ModbusRTU主站状态机默认只绑定Port1。若需使用Port2,必须在硬件组态中显式指定,并触发状态机重初始化:

  • 在汇川HMI编程软件(如AutoShop)中,右键PLC设备 → “属性” → “通讯端口” → 将“ModbusRTU主站端口”设为“Port2”
  • 此操作会生成一条隐藏指令:SET_PORT2_FOR_MODBUSRTU := TRUE;
  • 该指令在下次SM1001上升沿时,强制状态机从Reset重新走Init流程,并加载Port2的物理层参数(如驱动使能引脚、收发切换时序)

注意:Port1和Port2不能同时作为ModbusRTU主站运行。状态机内部只有一个主站实例,端口切换本质是物理层重绑定,而非双实例并发。

3. ModbusTCP主站:为什么IP配置正确却收不到响应?TCP连接池与超时的底层博弈

ModbusTCP看似只是ModbusRTU套上TCP/IP外壳,但在AM600中,它被重构为基于连接池的异步事务模型,与RTU的轮询状态机截然不同。这也是为什么你填对了IP、端口、从站ID,却始终看到“Connection refused”或“Timeout”。

3.1 连接池机制:每个从站独占一个TCP连接,且永不主动断开

AM600的ModbusTCP主站不采用“每次请求建连→发包→收包→断连”的HTTP式模式,而是为每个配置的从站预建立并长期维持一个TCP连接。连接池结构如下:

字段含义典型值说明
CONNECTION_ID连接唯一标识1~16每个从站分配1个ID,最大支持16个从站
TCP_STATETCP连接状态ESTABLISHED/CLOSE_WAIT状态由底层LWIP栈维护,主站只监听
TX_QUEUE_DEPTH待发请求队列深度0~8队列满时新请求被丢弃,返回错误码16#8003
RX_TIMEOUT_MS单次响应等待时长默认2000ms可通过MB_TCP_CONFIG功能块修改

关键点在于:连接建立发生在SM1002 := TRUE(ModbusTCP主站使能位)置位后的首个OB1周期,且一旦建立,除非网络中断或手动调用MB_TCP_CLOSE,否则永不释放。这带来两个硬性约束:

  • 若从站IP变更,必须先MB_TCP_CLOSE再SM1002 := FALSE,否则旧连接残留,新IP无法生效;
  • 若从站重启,TCP连接会进入CLOSE_WAIT,但AM600不会自动重连——必须由用户程序检测TCP_STATE并触发重连。

3.2 MB_TCP_MASTER功能块的“三次握手”式调用逻辑

MB_TCP_MASTER不是简单发包函数,它执行一个三阶段事务:

  1. 阶段1:连接就绪检查
    查询TCP_STATE是否为ESTABLISHED,若否,立即返回16#8002(“Connection not established”);

  2. 阶段2:请求入队
    将REQ_DATA封装成ModbusTCP ADU(含事务ID、协议ID、长度字段),加入对应CONNECTION_ID的发送队列;

  3. 阶段3:响应匹配
    接收网卡中断后,从RX缓冲区读取完整TCP报文,提取事务ID,与队列中待响应的请求ID比对;匹配成功则拷贝RESP_DATA,否则丢弃(防乱序攻击)。

// 实际调试中必须检查的三个关键返回值 MB_TCP_MASTER( REQ := TRUE, CONNECTION_ID := 1, SLAVE_ADDR := 1, FUNC_CODE := 3, START_ADDR := 100, DATA_LENGTH := 10, REQ_DATA := aReqData, RESP_DATA => aRespData, DONE => bDone, ERROR => bError, ERROR_CODE => wErrorCode ); // 重点监控: // wErrorCode = 16#8002 → 检查从站IP是否可达、防火墙是否放行502端口 // wErrorCode = 16#8003 → TX_QUEUE_DEPTH已满,需降低轮询频率或增加队列深度 // wErrorCode = 16#8004 → 事务ID不匹配,大概率是网络丢包或从站响应延迟超阈值

3.3 以太网硬件接口的PHY层陷阱:RJ45指示灯亮≠链路可用

AM600的RJ45接口采用标准IEEE 802.3 PHY芯片,但其链路状态检测存在1.5秒滞后窗口。现象:网线插入后,PLC前面板LINK灯秒亮,但MB_TCP_MASTER仍报16#8002。原因在于:

  • LINK灯由PHY芯片硬件驱动,检测到对端PHY信号即亮;
  • 而AM600 OS的TCP/IP栈需完成ARP请求→收到ARP响应→ICMP Ping通→TCP SYN成功,这一系列软件栈动作耗时约1.2~1.8秒;
  • 在此期间,TCP_STATE仍为INIT,SM1002置位无效。

避坑 / 常见问题 / 排查 / 注意
现象1:PLC与HMI在同一交换机,IP配置无误,但ModbusTCP始终连接失败
原因:交换机启用了端口安全(Port Security),限制MAC地址学习数量,AM600的MAC未被放行。
解决:登录交换机,关闭端口安全,或手动添加AM600 MAC到白名单。

现象2:从站设备(如某品牌IO模块)偶尔出现“Connection reset by peer”错误
原因:该从站TCP栈实现缺陷,收到重复SYN包时直接RST,而AM600在重传超时时会发重试SYN。
解决:在MB_TCP_CONFIG中将RETRY_COUNT设为1,禁用重传;或升级从站固件。

现象3:多从站轮询时,部分从站响应延迟突增200ms以上
原因:AM600的TCP连接池采用FIFO调度,高优先级从站请求被低速从站阻塞。
解决:为关键从站单独配置更高CONNECTION_ID,并在程序中用IF...ELSE优先调用其MB_TCP_MASTER。

现象4:断电重启后,ModbusTCP连接需手动SM1002 := FALSE再TRUE才能恢复
原因:OS未正确清理断电前的TCP TIME_WAIT状态,新连接被旧端口占用。
解决:在OB100(启动组织块)中添加逻辑:SM1002 := FALSE;延迟100ms后SM1002 := TRUE;

现象5:使用Wireshark抓包,看到PLC发出SYN但无SYN-ACK返回
原因:PLC与从站不在同一子网,且未配置网关,ARP请求广播不出去。
解决:确认子网掩码,或在PLC侧静态添加网关ARP条目(需汇川高级配置权限)。

4. CANopen与EtherCAT:为什么AM600的“主站”本质是“协议翻译器”?

CANopen和EtherCAT在AM600中并非原生支持,而是通过专用协处理器(Co-Processor)桥接实现。PLC CPU本身不解析CANopen PDO或EtherCAT FMMU,所有协议细节均由协处理器处理,CPU仅负责配置下发与数据搬运。这种架构决定了其调试逻辑与Modbus截然不同。

4.1 CANopen主站:对象字典同步不是“读写”,而是“镜像刷新”

AM600的CANopen主站将从站的对象字典(Object Dictionary)视为只读镜像。当你在AutoShop中配置从站节点号、PDO映射、SYNC周期时,这些参数被编译为二进制配置帧,经CAN总线发送给从站。但关键点在于:

  • 配置帧不触发从站对象字典更新:从站是否接受配置,取决于其自身固件实现。AM600不校验ACK;
  • 数据交换走PDO(Process Data Object):CPU内存中为每个从站开辟固定大小的PDO缓冲区(如CAN_PDO_IN[1]),协处理器自动将接收到的PDO数据按字节顺序搬入该缓冲区;
  • SDO(Service Data Object)访问受限:仅支持0x2F(Write All)和0x40(Read)两类SDO,且必须在CANOPEN_ENABLE := TRUE后手动调用CAN_SDO_WRITE功能块。

这意味着:你无法像Modbus那样用MB_MASTER动态读写任意寄存器。CANopen的数据流是单向镜像+事件触发——从站PDO变化时,CAN_PDO_IN缓冲区内容自动更新,CPU需轮询CAN_PDO_IN_UPDATE_FLAG标志位来获知。

4.2 EtherCAT主站:FMMU配置为何必须与从站XML严格一致?

EtherCAT的FMMU(Fieldbus Memory Management Unit)是硬件加速单元,负责将PLC内存地址映射到从站物理寄存器。AM600的EtherCAT主站要求:

  • 从站XML文件必须由官方工具生成:第三方XML若FMMU地址偏移错误,会导致PLC内存越界写入;
  • FMMU配置在下载时固化到协处理器:修改XML后必须重新下载整个EtherCAT配置,不能热更;
  • 同步管理器(Sync Manager)类型决定数据方向:SM0=Output(PLC→从站),SM1=Input(从站→PLC),AM600强制SM0/SM1成对使用。

典型错误:某开发者将从站的SM0配置为0x1000(输出区),但SM1误配为0x1000(同地址),导致PLC读取输入时实际读到自己刚写的输出数据,形成“自反馈幻觉”。

4.3 四协议共存的资源竞争:协处理器带宽如何被悄悄吃掉?

AM600的协处理器是共享资源,其带宽被四类协议按权重分配:

协议默认带宽权重关键影响调整方式
ModbusRTU30%影响485轮询周期稳定性在“系统配置”中调整RTU_BW_RATIO
ModbusTCP40%影响TCP连接数上限与响应延迟修改TCP_MAX_CONNECTIONS参数
CANopen20%影响PDO刷新率与SDO吞吐量调整CANOPEN_BAUDRATE间接影响
EtherCAT10%影响分布式时钟同步精度EtherCAT周期不可调,权重固定

提示:当启用EtherCAT后,ModbusTCP的TX_QUEUE_DEPTH可能从8降至4——因为协处理器将更多周期分配给EtherCAT的实时帧处理。这不是Bug,是设计者为保障EtherCAT微秒级确定性做的主动让渡。

5. 通讯诊断:为什么“ModbusTCP诊断”章节藏着AM600最硬核的调试能力?

《汇川中型PLC通讯.pdf》的CHAPTER02末尾“ModbusTCP诊断”并非泛泛而谈的ping/traceroute,而是暴露了AM600固件中未公开的底层诊断寄存器。这些寄存器是定位通讯顽疾的终极武器,但需用特定指令读取。

5.1 诊断寄存器组:绕过功能块,直击TCP/IP栈内核

AM600在内存地址0x80000000起始处映射了一组诊断寄存器,需用READ_MEMORY系统功能块访问:

地址偏移寄存器名类型含义典型值解读
0x0000TCP_CONN_CNTUINT当前活跃TCP连接数>16表示连接池溢出,需关闭非关键从站
0x0002TCP_RX_ERR_CNTUDINT接收校验错误包计数持续增长说明网线干扰或PHY故障
0x0006TCP_TX_RETRY_CNTUDINT重传次数累计>1000/分钟表明网络拥塞或从站响应慢
0x000ATCP_RTT_AVG_USUDINT平均往返时延(微秒)>50000(50ms)需检查网络拓扑
// 读取TCP平均RTT的实操代码(需在OB1中周期调用) READ_MEMORY( ADDR := 16#8000000A, // TCP_RTT_AVG_US地址 SIZE := 4, // 4字节UDINT DATA => dwRTT_Avg, DONE => bReadDone, ERROR => bReadError ); // 当dwRTT_Avg > 50000时,触发网络告警 IF dwRTT_Avg > 50000 THEN ALARM_NET_LATENCY := TRUE; END_IF;

5.2 ModbusRTU的“静默错误”捕获:CRC校验失败不报错,但留痕

ModbusRTU物理层的CRC校验由485收发器硬件完成,失败帧被直接丢弃,MB_MASTER不报错。但AM600在0x80001000处记录了静默丢弃计数:

地址偏移名称含义
0x0000RTU_CRC_ERR_CNTCRC校验失败帧计数
0x0002RTU_FRAMING_ERR_CNT帧起始位/停止位错误计数

这两个计数器是判断485线路质量的黄金指标。若RTU_CRC_ERR_CNT每分钟增长>5,基本可判定为:

  • 终端电阻未接或阻值错误(应为120Ω);
  • 485线过长未加中继(AM600推荐≤500米@9600bps);
  • 共模电压超标(用万用表测A-GND/B-GND电压差应<7V)。

5.3 诊断数据的“时间戳对齐”玄学:为什么必须用OB35定时中断?

所有诊断寄存器的值都是瞬时快照,若在OB1中读取,因OB1周期波动(如10ms±2ms),两次读数时间差不确定,无法计算速率。正确做法是:

  • 创建OB35(10ms定时中断),在其中调用READ_MEMORY;
  • 所有诊断读取严格对齐10ms边界,RTU_CRC_ERR_CNT的差值除以10ms即得每秒错误率;
  • 避免在OB1中做减法运算,防止因扫描周期抖动导致负值。

避坑 / 常见问题 / 排查 / 注意
现象1:诊断寄存器读数全为0,但通讯明显异常
原因:未启用诊断功能。AM600默认关闭诊断寄存器更新,需在“系统配置”中勾选“Enable Diagnostic Registers”。
解决:勾选后重新下载PLC程序,寄存器开始计数。

现象2:TCP_RX_ERR_CNT持续增长,但Ping测试网络通畅
原因:从站设备TCP栈存在BUG,发送畸形TCP包(如错误校验和),被AM600内核丢弃。
解决:抓包分析从站发出的TCP包,联系其厂商修复固件。

现象3:RTU_CRC_ERR_CNT在电机启停瞬间暴增
原因:电机干扰通过地线耦合到485总线,属EMC设计缺陷。
解决:为485接口加磁环,485线与动力线分开走线槽,PLC与从站单独接地。

现象4:诊断寄存器地址读取返回ERROR_CODE=16#8005
原因:地址超出映射范围或未授权访问。AM600将诊断区分为“基础”(0x80000000)和“高级”(0x80002000),后者需密码解锁。
解决:确认地址正确;如需高级诊断,联系汇川获取调试密钥。

现象5:OB35中读取诊断寄存器,数值跳变剧烈无规律
原因:OB35执行时间超过10ms,导致下一次中断到来时前次读取未完成,寄存器被覆盖。
解决:优化OB35内代码,确保执行时间<5ms;或改用OB1+TON定时器模拟10ms采样。

6. 设计取舍的真相:为什么AM600要把ModbusRTU主站做成“状态机”,而不是“即开即用”?

翻遍《汇川中型PLC通讯.pdf》,你会发现一个贯穿始终的设计哲学:所有通讯协议的初始化,都被强制解耦为“系统位使能”+“功能块调用”两阶段。SM1001/SM1002不是多余开关,而是汇川工程师在资源受限的嵌入式MCU上,为保障确定性而做的残酷取舍。

6.1 确定性优先:为什么不用RTOS的“通讯任务”模型?

AM600主CPU是ARM Cortex-M7,主频280MHz,但运行的是轻量级实时OS(非FreeRTOS或Zephyr)。若为每种通讯协议创建独立任务:

  • 任务切换开销约3μs/次,16个ModbusTCP连接需16个任务,仅切换就吃掉48μs/周期;
  • 内存碎片化:每个TCP连接需独立socket缓冲区(至少4KB),16连接占64KB RAM,挤占用户程序空间;
  • 中断嵌套风险:CANopen的SYNC中断、EtherCAT的DC中断、以太网PHY中断可能嵌套,MCU栈易溢出。

状态机模型将所有协议收敛到一个主循环内,用查表法跳转状态,单次状态跃迁耗时稳定在0.8μs以内,且内存布局连续可控。代价是:用户必须理解状态跃迁时机,不能“想当然”地调用功能块。

6.2 安全边界:为什么功能块不校验参数合法性?

MB_MASTER不检查SLAVE_ADDR是否在1~247范围内,MB_TCP_MASTER不验证IP地址格式,是因为:

  • 参数校验需额外CPU周期,破坏扫描周期确定性;
  • 工程师认为:参数错误应在组态阶段由AutoShop拦截,运行时应“相信配置”;
  • 真正的安全防护在物理层:ModbusRTU的CRC、ModbusTCP的校验和、CANopen的EDS文件签名,已在协处理器中硬实现。

这导致一个血泪经验:所有通讯参数必须在AutoShop中完成语法检查并下载,绝不能在PLC运行时用MOVE指令动态修改SLAVE_ADDR——那只会让状态机收到非法指令,进入未知态,最终看门狗复位。

6.3 可维护性妥协:为什么诊断寄存器不集成到HMI变量表?

AM600的HMI变量表仅映射用户DB块,而诊断寄存器位于固件私有地址空间。这样设计的好处:

  • 避免HMI频繁轮询诊断区拖慢画面刷新;
  • 防止用户误删诊断变量导致调试能力丧失;
  • 保持HMI工程与PLC程序解耦,升级固件不影响HMI画面。

坏处是:你必须手写READ_MEMORY代码,无法像普通变量一样拖拽到HMI上。从那以后我每次新建AM600项目,都会在OB100中固化一段诊断读取模板,并导出为.lib库——不是为了省事,而是确保所有项目都用同一套经过验证的时序逻辑读取诊断数据,避免因OB1周期抖动导致的误判。希望帮到你。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/11/3 20:11:25

UE4载具平衡实战:摩托车、四轮车、坦克防翻车逻辑与参数调优

1. 载具平衡这件事&#xff0c;为什么值得单独拎出来聊做载具系统的朋友大概都有过这种体验&#xff1a;物理参数调了一下午&#xff0c;直线上跑得挺稳&#xff0c;结果一拐弯车身就侧翻&#xff0c;或者上个斜坡直接四脚朝天。尤其是摩托车这种两轮载具&#xff0c;稍微给点侧…

作者头像 李华
网站建设 2026/11/3 20:09:03

LeetCode树与链表基础题解析:108与203题精讲

1. 项目概述 今天要分享的是LeetCode上两道经典题目&#xff1a;108题"将有序数组转化为二叉树"和203题"移除链表元素"。这两道题分别代表了树和链表这两个数据结构中的基础操作&#xff0c;也是面试中的高频考点。 我刷了300道LeetCode题目后发现&#x…

作者头像 李华
网站建设 2026/11/3 20:08:10

Archery SQL审计平台在RockyLinux8的容器化部署实践

1. 为什么选择Archery作为SQL审计平台在数据库运维领域&#xff0c;SQL审计一直是保障数据安全的重要环节。Archery作为开源的SQL审核平台&#xff0c;相比商业产品具有几个显著优势&#xff1a;首先它支持多数据库类型&#xff08;MySQL、PostgreSQL、Oracle等&#xff09;&am…

作者头像 李华
网站建设 2026/11/3 20:07:30

Godot协程等待机制:从裸await到可取消超时等待库

上周帮一位朋友看他那个小型横版项目&#xff0c;翻到关卡流程脚本时我愣了一下&#xff1a;一个 400 行的文件里塞了三十七处await get_tree().create_timer(...).timeout&#xff0c;还有十几处裸await各种信号&#xff0c;中间夹着两三个queue_free()。我问他这套跑起来稳不…

作者头像 李华
网站建设 2026/11/3 20:03:57

Hadoop在教育大数据中的实践:自闭症儿童个性化教学

1. 项目背景与核心价值自闭症儿童教育一直面临资源分配不均和个性化教学方案缺失的双重挑战。传统教育系统往往采用"一刀切"的教学模式&#xff0c;难以满足自闭症谱系障碍(ASD)儿童的特殊需求。我们团队开发的这套系统&#xff0c;正是要利用大数据技术解决这个痛点…

作者头像 李华
网站建设 2026/11/3 20:03:54

同义词算法实战:从规则到SBERT的工程落地指南

1. 这不是词典查询&#xff0c;而是让机器真正“理解”词语关系的实战路径“同义词&#xff08;近义词&#xff09;算法总结&#xff08;附代码&#xff09;”——看到这个标题&#xff0c;很多人第一反应是&#xff1a;不就是查个《现代汉语词典》或者调用jieba、HanLP的synon…

作者头像 李华