CODESYS與Python“牽手”

2025/10/14 10:06:57

CODESYS PLC與Python的Socket“牽手”:開啟工業(yè)控制無(wú)限可能

 

關(guān)鍵詞:CODESYS、Python、Socket 通信、Raspberry Pi、工業(yè)物聯(lián)網(wǎng)、實(shí)時(shí)數(shù)據(jù)采集、跨語(yǔ)言集成

 

微信公眾號(hào)原文連接:

https://mp.weixin.qq.com/s/0jwwYbOWUb5lJ0G26GVjsA

CSDN:

https://blog.csdn.net/qq_36063437/article/details/153248117?fromshare=blogdetail&sharetype=blogdetail&sharerId=153248117&sharerefer=PC&sharesource=qq_36063437&sharefrom=from_link

一、引言:讓 PLC 擁抱開放的編程世界

傳統(tǒng)工業(yè)里,PLC 穩(wěn)守固定邏輯,精準(zhǔn)執(zhí)行控制任務(wù)。在工業(yè)物聯(lián)網(wǎng)與智能制造浪潮下,工程師有了新期待:讓 PLC 與 Python 等現(xiàn)代高級(jí)編程語(yǔ)言聯(lián)手,將 PLC 的穩(wěn)定可靠與 Python 的開放多元完美融合,從而釋放出無(wú)限潛能。

本文將以一個(gè)精彩案例,展示 PLC 程序通過(guò) Socket 請(qǐng)求,讓樹莓派上 Python 服務(wù)器抓取實(shí)時(shí)天氣數(shù)據(jù)并回傳解析存儲(chǔ)的奇妙過(guò)程,一起探索!

 

二、系統(tǒng)總體架構(gòu)與數(shù)據(jù)流

系統(tǒng)由兩個(gè)主要部分構(gòu)成:

模塊

平臺(tái)

功能描述

CODESYS PLC 程序

樹莓派上的 CODESYS   Runtime

客戶端。檢測(cè)觸發(fā)信號(hào)、建立TCP連接、發(fā)送命令、接收天氣 JSON 數(shù)據(jù)、解析并輸出到變量。

Python 服務(wù)端程序

樹莓派 / 其他主機(jī)

服務(wù)器。監(jiān)聽TCP端口,接收PLC命令,通過(guò) HTTP 從   weather.com.cn 獲取實(shí)時(shí)天氣數(shù)據(jù),解析后返回 JSON 格式響應(yīng)。

 

三、CODESYS 端:實(shí)現(xiàn) PLC 調(diào)用外部服務(wù)的關(guān)鍵邏輯

Codesys中新建名為Socket_FB的功能塊(Function Block),在PLC主循環(huán)中調(diào)用。

3.1 功能塊的引腳設(shè)計(jì)

Socket_FB功能塊引腳示意圖

 

3.2 上升沿觸發(fā)與一次通信周期

代碼示例:

bRisingEdge := bSendTrigger AND NOT bTrigOld;

bTrigOld := bSendTrigger;

PLC 程序通過(guò)檢測(cè)輸入 bSendTrigger 的上升沿,觸發(fā)一次完整的通信任務(wù)。這樣可確保每次請(qǐng)求都是用戶或外部事件驅(qū)動(dòng),不會(huì)連續(xù)觸發(fā)導(dǎo)致網(wǎng)絡(luò)阻塞。一旦觸發(fā)在后續(xù)的程序中會(huì)依次執(zhí)行以下命令:

  • 周期初始化,清空所有狀態(tài)變量;

  • 創(chuàng)建 TCP socket;

  • 連接到服務(wù)器;

  • 發(fā)送命令;

  • 等待并接收應(yīng)答;

  • 解析結(jié)果;

  • 關(guān)閉連接。

這是一個(gè)典型的“事務(wù)式通信模式”,類似工業(yè)現(xiàn)場(chǎng)中“一次握手、一問(wèn)一答”的數(shù)據(jù)采集流程。

 

3.3 周期初始化

代碼示例:

IF bRisingEdge THEN

    bConnectOK := FALSE;

    bSendOK := FALSE;

    bRecvOK := FALSE;

    bDone := FALSE;

    sRecvBuffer := '';

    iErrorCode := Errors.ERR_OK;

每次通訊開始前重置所有狀態(tài)標(biāo)志,清空接收緩沖區(qū)。

 

3.4 Socket 通信核心流程

CODESYS 的 SysSocket 庫(kù)提供了底層網(wǎng)絡(luò)訪問(wèn)能力:

函數(shù)

作用

SysSockCreate()

創(chuàng)建 socket,返回句柄

SysSockConnect()

與服務(wù)器建立 TCP 連接

SysSockSend()

發(fā)送數(shù)據(jù)

SysSockRecv()

接收數(shù)據(jù)

SysSockClose()

關(guān)閉連接

 

(1)    創(chuàng)建socket:

代碼示例:

hSocket := SysSockCreate(SOCKET_AF_INET, SOCKET_STREAM, SOCKET_IPPROTO_TCP, ADR(iResult));

Codesys庫(kù)SysSockCreate文檔

創(chuàng)建一個(gè)新的 socket,并返回該 socket 的句柄(handle)。這個(gè)句柄以后會(huì)作為參數(shù)傳給其他套接字相關(guān)函數(shù),例如 SysSockBind、SysSockConnect、SysSockListen、SysSockAccept、SysSockSend、SysSockRecv、SysSockClose 等。

 

參數(shù):SOCKET_AF_INET, SOCKET_STREAM, SOCKET_IPPROTO_TCP是 CODESYS 系統(tǒng)庫(kù)中定義的常量,初始值如下表所示。

Name

Type

Initial

Comment

SOCKET_AF_INET

INT

2

AddressFamily: DINTernetwork: UDP, TCP,   etc.

SOCKET_STREAM

DINT

1

Socket types: stream socket

SOCKET_IPPROTO_TCP

DINT

6

Protocols: tcp

 

(2)設(shè)置socket服務(wù)器地址

代碼示例:

SockInetAddr_Result := SysSockInetAddr('127.0.0.1', ADR(ipAddr));

IF SockInetAddr_Result = Errors.ERR_OK THEN

addrServer.sin_family := SOCKET_AF_INET;

    addrServer.sin_port := SysSockHtons(5678);

    addrServer.sin_addr.ulAddr := ipAddr;

這段代碼的作用是:將字符串形式的 IP 地址 "127.0.0.1" 轉(zhuǎn)換為可用于網(wǎng)絡(luò)通信的數(shù)值格式,并在轉(zhuǎn)換成功后,設(shè)置服務(wù)器地址結(jié)構(gòu) addrServer 的基本參數(shù):指定使用 IPv4 協(xié)議、端口號(hào)為 5678,并將目標(biāo) IP 地址設(shè)為 127.0.0.1,為后續(xù)建立 socket 連接做準(zhǔn)備。

 

Codesys庫(kù)SysSockInetAddr文檔

在使用SysSockConnect 前,需要把目標(biāo) IP(字符串)轉(zhuǎn)換為可寫入地址結(jié)構(gòu)的二進(jìn)制值。所以,SysSockInetAddr 通常是網(wǎng)絡(luò)通信初始化步驟中的一環(huán)。SysSockInetAddr 的作用就是:把 "點(diǎn)分十進(jìn)制" 的 IP 地址(例如 '127.0.0.1')轉(zhuǎn)成一個(gè) 32 位無(wú)符號(hào)整數(shù)(UDINT)形式。

在實(shí)際測(cè)試中使用的‘127.0.0.1’通過(guò)SysSockInetAddr轉(zhuǎn)換結(jié)果是16777343。一個(gè) IPv4 地址本質(zhì)上是 4 個(gè)字節(jié)(共 32 位),把它轉(zhuǎn)換為16進(jìn)制按字節(jié)拼起來(lái)是0x7F000001。網(wǎng)絡(luò)中數(shù)據(jù)是 Big Endian(高位在前),但大多數(shù) PLC/CPU(x86、ARM)是 Little Endian(低位在前)。也就是說(shuō)在內(nèi)存中這 4 個(gè)字節(jié)的排列是反的:

網(wǎng)絡(luò)字節(jié)序(標(biāo)準(zhǔn)): 7F 00 00 01

PLC內(nèi)存(小端表示): 01 00 00 7F

0x0100007F = (1 × 256^3) + (0 × 256^2) + (0 × 256) + 127= 16777343

 

Codesys庫(kù)SOCKADDRESS文檔

SOCKADDRESS 結(jié)構(gòu)用于在 CODESYS 中描述一個(gè)完整的網(wǎng)絡(luò)通信地址,它包含了建立或識(shí)別網(wǎng)絡(luò)連接所需的全部信息——包括地址族(如 IPv4)、端口號(hào)以及目標(biāo)或本地的 IP 地址。該結(jié)構(gòu)在調(diào)用SysSockConnect函數(shù)時(shí)作為參數(shù)使用,用來(lái)告訴系統(tǒng)“我要與哪個(gè) IP、哪個(gè)端口進(jìn)行通信”。其中端口號(hào)需要通過(guò) SysSockHtons() 轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)序,IP 地址通常由 SysSockInetAddr() 生成。簡(jiǎn)單來(lái)說(shuō),SOCKADDRESS 就是 CODESYS 中 socket 通信的“地址卡片”。

 

(3)建立socket連接

代碼示例:

SockConnect_Result := SysSockConnect(hSocket, ADR(addrServer), SIZEOF(addrServer));

這段代碼的作用是:通過(guò)已創(chuàng)建的 socket (hSocket),調(diào)用 SysSockConnect() 函數(shù),將其連接到由 addrServer 定義的遠(yuǎn)程服務(wù)器地址,并返回連接結(jié)果。

Codesys庫(kù)SysSockConnect文檔

SysSockConnect是一個(gè)用于實(shí)現(xiàn)客戶端連接socket服務(wù)器功能的功能塊。使用時(shí),需將傳入socket句柄和包含服務(wù)器IP地址和端口號(hào)等信息的SOCKADDRESS結(jié)構(gòu)等。函數(shù)執(zhí)行后會(huì)返回一個(gè)RTS_IEC_RESULT類型的值,用于指示連接操作是否成功,若返回0表示連接成功,可進(jìn)行后續(xù)數(shù)據(jù)傳輸?shù)炔僮?,否則需根據(jù)返回值進(jìn)行相應(yīng)的錯(cuò)誤處理。

 

(4) 發(fā)送命令

代碼示例:

sSendBuffer := 'fun1';

IF SysSockSend(hSocket, ADR(sSendBuffer), LEN(sSendBuffer), 0, ADR(SockSend_Result)) > 0 AND SockSend_Result = Errors.ERR_OK THEN

bSendOK := TRUE;

這段代碼的作用是:PLC通過(guò)已連接的socket發(fā)送字符串 “fun1”,并在確認(rèn)發(fā)送成功后設(shè)置發(fā)送成功標(biāo)志。

 

Codesys庫(kù)SysSockSend文檔

SysSockSend 函數(shù)用于向已建立的 socket發(fā)送數(shù)據(jù)。hSocket 是先前創(chuàng)建并連接成功的 socket 句柄;ADR(sSendBuffer) 提供發(fā)送緩沖區(qū)的內(nèi)存地址;LEN(sSendBuffer) 指定要發(fā)送的數(shù)據(jù)長(zhǎng)度;0 表示不使用額外的發(fā)送標(biāo)志;ADR(SockSend_Result) 用于接收運(yùn)行時(shí)系統(tǒng)返回的錯(cuò)誤碼。函數(shù)返回成功發(fā)送的字節(jié)數(shù)。如果發(fā)送的字節(jié)數(shù)大于 0 且系統(tǒng)返回碼 SockSend_Result 等于 Errors.ERR_OK,則說(shuō)明數(shù)據(jù)成功發(fā)出,于是程序?qū)?bSendOK 置為 TRUE,表示發(fā)送完成。

 

(5) 接收數(shù)據(jù)

代碼示例:

diRecvBytes := SysSockRecv(hSocket, ADR(byRecvBuffer), SIZEOF(byRecvBuffer), 0, ADR(SockRecv_Result));

IF diRecvBytes > 0 AND SockRecv_Result = Errors.ERR_OK THEN

IF diRecvBytes > SIZEOF(sRecvBuffer) - 1 THEN

    diRecvBytes := SIZEOF(sRecvBuffer) - 1;

    END_IF

    SysMemCpy(ADR(sRecvBuffer), ADR(byRecvBuffer), diRecvBytes);

    sRecvBuffer[diRecvBytes] := BYTE#0;

    bRecvOK := TRUE;

這段代碼的主要作用是:從一個(gè)已建立的 TCP Socket (hSocket) 中接收數(shù)據(jù)并保存到接收緩沖區(qū)中 (sRecvBuffer),并在成功接收后標(biāo)記 bRecvOK := TRUE。

 

Codesys庫(kù)SysSockRecv文檔

SysSockRecv用于從 socket中接收數(shù)據(jù)。它通過(guò)指定的socket句柄 hSocket 從端口讀取數(shù)據(jù),并將接收到的字節(jié)寫入由 pbyBuffer 指向的接收緩沖區(qū)中,最大接收長(zhǎng)度由 diBufferSize 限制。

 

Codesys庫(kù)SysMemCpy文檔

SysMemCpy用于內(nèi)存數(shù)據(jù)復(fù)制,其作用是將指定源地址 pSrc 中的內(nèi)容復(fù)制到目標(biāo)地址 pDest,復(fù)制的字節(jié)數(shù)由參數(shù) udiCount 決定。

 

實(shí)際運(yùn)行狀態(tài)監(jiān)控

網(wǎng)絡(luò)傳輸?shù)讓硬徽J(rèn)識(shí)“字符串”,所有內(nèi)容(包括文字、數(shù)字、圖片)都要被轉(zhuǎn)為字節(jié)流(byte stream)。byRecvBuffer 收到的就是這些 ASCII/UTF-8 字節(jié),diRecvBytes是接收到的字節(jié)數(shù)量。

Codesys監(jiān)控byRecvBuffer

byRecvBuffer接收到的字節(jié)數(shù)據(jù)前9個(gè)依次為:123,34,110,97,109,101,101,110,34。根據(jù)字符與字節(jié)(ASCII / UTF-8 編碼)之間的關(guān)系,以上字節(jié)可轉(zhuǎn)譯為:{、"、n、a、m、e、e、n、"。

字符

十進(jìn)制字節(jié)值

十六進(jìn)制

含義

{

123

0x7B

左花括號(hào)

}

125

0x7D

右花括號(hào)

"

34

0x22

雙引號(hào)

:

58

0x3A

冒號(hào)

,

44

0x2C

逗號(hào)

空格

32

0x20

空格

0–9

48–57

0x30–0x39

數(shù)字字符

a–z

97–122

0x61–0x7A

小寫字母

標(biāo)準(zhǔn) ASCII 或 UTF-8 編碼

文本的案例中接收到的完整字符串為:{"nameen": "baoshan", "temp": "23.9", "wde": "NW", "wse": "11km/h", "SD": "84%", "qy": "1015", "njd": "4km", "updatetime": "20:40", "rain": "0", "rain24h": "0", "aqi": "88", "aqi_pm25": "88", "weathere": "haze"},共211字節(jié),與監(jiān)控的diRecvBytes值一致。

 

(6) JSON 解析

代碼示例:

s_Nameen := GetFieldValue(sRecvBuffer, 'nameen');

FUNCTION GetFieldValue : STRING

...

sPattern := CONCAT(sKey, '": "');

iStart := FIND(sSrc, sPattern);

...

GetFieldValue := LEFT(sTemp, iEnd - 1);

這段代碼的主要作用是:從 sSrc 字符串中查找以 sKey 為字段名的鍵值對(duì),并提取該鍵對(duì)應(yīng)的字符串值。類似從 ... "name": "Alice", ... 中提取 Alice 的功能。

雖然 PLC 沒(méi)有內(nèi)置完整 JSON 解析器,但通過(guò)字符串查找函數(shù)即可實(shí)現(xiàn)簡(jiǎn)化版字段提取。這說(shuō)明即便在嵌入式 PLC 環(huán)境中,也可以通過(guò)基礎(chǔ)字符串操作解析網(wǎng)絡(luò)數(shù)據(jù)。解析完成后,PLC 將天氣各項(xiàng)指標(biāo)寫入輸出變量,如:

s_Temp := GetFieldValue(sRecvBuffer, 'temp');

s_WindSpeed := GetFieldValue(sRecvBuffer, 'wse');

s_Humidity := GetFieldValue(sRecvBuffer, 'SD');

s_Weather := GetFieldValue(sRecvBuffer, 'weathere');

這些變量可用于顯示在 HMI、記錄數(shù)據(jù)庫(kù)、或驅(qū)動(dòng)后續(xù)控制邏輯。

 

(7) 關(guān)閉socket

SysSockClose(hSocket);

關(guān)閉已創(chuàng)建的套接字 hSocket,釋放與該套接字相關(guān)的系統(tǒng)資源,結(jié)束該網(wǎng)絡(luò)連接。

 

四、Python 端:CODESYS 的“外部智能助手”

4.1 設(shè)計(jì)思路

Python 在此系統(tǒng)中扮演“中間件服務(wù)層”角色。PLC 不直接訪問(wèn)互聯(lián)網(wǎng),而是請(qǐng)求 Python 服務(wù)端,由 Python 完成網(wǎng)絡(luò)請(qǐng)求與數(shù)據(jù)解析任務(wù),再將結(jié)果以簡(jiǎn)潔 JSON 返回。

這既保證了:

  • PLC 穩(wěn)定、安全(不直接暴露外網(wǎng)請(qǐng)求);

  • Python 靈活、強(qiáng)大(可訪問(wèn)任意 API 或算法)。

這種設(shè)計(jì)模式是“PLC + 外部語(yǔ)言”協(xié)同的典型結(jié)構(gòu)。

本文案例以python監(jiān)聽socket端口,接收來(lái)自PLC的命令來(lái)執(zhí)行獲取當(dāng)前天氣數(shù)據(jù)的功能,并且將天氣數(shù)據(jù)返還給PLC進(jìn)行解析。

 

4.2 主要功能模塊

(1) 天氣數(shù)據(jù)抓取

代碼示例:

def get_weather_data():

    url = f"https://d1.weather.com.cn/sk_2d/101020300.html?_{int(time.time() * 1000)}"

    headers = {

        'Referer': 'https://e.weather.com.cn/',

        'User-Agent': 'Mozilla/5.0'

    }

    response = requests.get(url, headers=headers, timeout=10)

return parse_weather_data(response.text)

這段代碼的作用是:Python 使用 requests 庫(kù)訪問(wèn)氣象網(wǎng)站,提取返回?cái)?shù)據(jù)包中的 JSON 數(shù)據(jù)段。

解析后得到標(biāo)準(zhǔn)字典對(duì)象,例如:

{

  "nameen": "Pudong",

  "temp": "26",

  "wde": "East",

  "wse": "3.4",

  "SD": "65%",

  "qy": "1012"

}

(2) 端口監(jiān)聽

def handle_client(conn, addr):

    data = conn.recv(2048).decode('utf-8').strip()

    if data == "fun1":

        weather_data = format_weather_data(get_weather_data())

        reply = json.dumps(weather_data, ensure_ascii=False)

        conn.sendall(reply.encode('utf-8'))

Python 服務(wù)監(jiān)聽端口 5678,一旦接收到 "fun1",便執(zhí)行天氣抓取并回傳 JSON。采用多線程模式,保證可以同時(shí)服務(wù)多個(gè) PLC 連接。

 

4.3 CODESYS 與 Python 的契約:數(shù)據(jù)格式 + 通信協(xié)議

項(xiàng)目

內(nèi)容

連接方式

TCP

端口號(hào)

5678

請(qǐng)求命令

fun1

返回格式

UTF-8 編碼的 JSON 文本

通信周期

按 PLC 觸發(fā)(例如每 30 秒或人工觸發(fā))

通過(guò)這種契約,PLC不需要理解Python,只需發(fā)命令并解析字符串即可。這正是 “CODESYS 調(diào)用 Python” 的精髓:PLC 不直接運(yùn)行 Python 代碼,但通過(guò) Socket 請(qǐng)求 → Python 執(zhí)行 → 結(jié)果返回,實(shí)現(xiàn)了間接調(diào)用。

 

五、實(shí)驗(yàn)結(jié)果與運(yùn)行驗(yàn)證

實(shí)驗(yàn)環(huán)境:

  • 硬件:Raspberry Pi 4B + 2GB RAM

  • 操作系統(tǒng):Raspberry Pi OS (64-bit)

  • CODESYS 版本:3.5 SP21

  • Python 版本:3.11

  • 網(wǎng)絡(luò):同機(jī)運(yùn)行(127.0.0.1)

 

運(yùn)行效果:

1.       啟動(dòng) Python 服務(wù)器:

[服務(wù)器啟動(dòng)] 正在監(jiān)聽 127.0.0.1:5678

樹莓派Python

2.       在 CODESYS 中觸發(fā) bSendTrigger 上升沿:

bConnectOK = TRUE 

bSendOK = TRUE 

bRecvOK = TRUE 

s_Temp = "26" 

s_WindSpeed = "3.4" 

s_Humidity = "65%" 

bDone = TRUE

Codesys在線監(jiān)控

3.       Codesys可視化界面顯示:

Codesys可視化界面

驗(yàn)證:通信成功,數(shù)據(jù)解析正確。

 

六、CODESYS 與 Python 協(xié)同的技術(shù)意義

6.1 打破 PLC 封閉邊界

傳統(tǒng) PLC 依賴專有協(xié)議和有限的函數(shù)庫(kù),難以直接與云端 API 或第三方系統(tǒng)交互。通過(guò) SysSocket,CODESYS 實(shí)現(xiàn)了跨語(yǔ)言通信的“開放接口”,使 PLC 能夠訪問(wèn):

  • Web 服務(wù)(RESTful API)

  • 本地算法服務(wù)(Python、C/C++)

  • 數(shù)據(jù)庫(kù)代理(通過(guò) Python、Node.js 等)

6.2 各取所長(zhǎng)的架構(gòu)優(yōu)勢(shì)

模塊

優(yōu)勢(shì)

角色定位

CODESYS PLC

實(shí)時(shí)性強(qiáng)、控制邏輯穩(wěn)定

數(shù)據(jù)消費(fèi)者、執(zhí)行層

Python 程序

網(wǎng)絡(luò)與算法能力強(qiáng)

數(shù)據(jù)提供者、智能層

這是一種工業(yè)界常見(jiàn)的分層架構(gòu):PLC 負(fù)責(zé)“控制”,Python 負(fù)責(zé)“智能”。

 

七、擴(kuò)展應(yīng)用方向

(1)工業(yè) IoT 數(shù)據(jù)融合

可將 Python 改為接入 MQTT、Modbus、OPC UA 等接口,PLC 作為訂閱者。

(2)AI 輔助控制

Python 端可運(yùn)行機(jī)器學(xué)習(xí)模型,根據(jù)實(shí)時(shí)天氣預(yù)測(cè)能耗或生產(chǎn)計(jì)劃,再通過(guò) Socket 返回控制參數(shù)。

(3)邊緣計(jì)算節(jié)點(diǎn)

樹莓派同時(shí)運(yùn)行 PLC 與 Python,形成“混合智能邊緣設(shè)備”,既具實(shí)時(shí)性又具云連接能力。

(4)云服務(wù)接口

可替換天氣 API 為任意 Web 服務(wù)(設(shè)備管理、能源監(jiān)控、物流狀態(tài)等),實(shí)現(xiàn) CODESYS 與云端系統(tǒng)的數(shù)據(jù)橋接。

 

八、結(jié)語(yǔ):CODESYS 與 Python 的融合之路

本文以“CODESYS 獲取實(shí)時(shí)天氣數(shù)據(jù)”為案例,完整展示了:

l   如何在樹莓派上運(yùn)行 CODESYS Runtime;

l   如何用 IEC 61131-3 語(yǔ)言建立Socket通信;

l   如何通過(guò) Python 服務(wù)器實(shí)現(xiàn)外部數(shù)據(jù)訪問(wèn);

l   以及兩者協(xié)作實(shí)現(xiàn)“PLC 調(diào)用 Python”的機(jī)制。

這不是簡(jiǎn)單的網(wǎng)絡(luò)實(shí)驗(yàn),而是一個(gè)工業(yè)控制新時(shí)代的縮影。PLC 不再局限于封閉的邏輯循環(huán),而是可以與 AI、云端、Web 世界自由交互。Python 也不只是桌面腳本,而能成為工業(yè)現(xiàn)場(chǎng)的“智慧補(bǔ)腦”。

這種模式預(yù)示著未來(lái)工業(yè)控制系統(tǒng)的方向:控制邏輯與數(shù)據(jù)智能的融合,實(shí)時(shí)性與開放性的統(tǒng)一。

審核編輯(
王靜
)

提交

查看更多評(píng)論