HIWIN 與 Python TCP 通訊的簡單範例
目標
實做一個可以使用python控制HIWIN手臂的簡單程式,並探討後續的運用
Python 端建立 TCP server,HIWIN 透過 COPEN 主動連線。雙方以 ASCII 文字傳送命令,數值使用大括號包住。
以下使用 Group 1 控制 Cartesian 相對位移,Group 2 移動到已教導點位。
這套流程一次只處理一筆命令。Python 送出命令後等待回覆;HIWIN 收到三個數值,依照 Group 執行對應動作,再用 CWRITE 傳回結果。收到回覆後,Python 才讓使用者輸入下一筆命令,因此執行順序容易觀察,也適合先理解兩端如何交換資料。
通訊方向
Python TCP server(等待連線)
↑
│ TCP/IP
│
HIWIN HRB client(COPEN 主動連線)
兩端必須使用同一個 port。下例使用 5000,HIWIN 程式中的 IP 要改成執行 Python 的電腦或 Raspberry Pi 位址。
Python 是 server,只負責開啟通訊埠並等待連線;HIWIN 是 client,執行 COPEN 時主動連到 Python。若兩邊都設成等待連線,或兩邊都試圖連向對方,連線就不會成立。
一次命令會依序經過下列步驟:
- Python 以
sendall()送出{CMD_GROUP,CMD_AXIS,CMD_AMOUNT}。 - HRB 的
CREAD將三個數值放入對應的REAL變數。 - HRB 根據
CMD_GROUP與CMD_AXIS選擇動作。 - 動作指令執行完畢後,HRB 以
CWRITE傳回ACK。 - Python 的
recv()收到回覆,結束這一輪。
Python TCP server
import socket
HOST = "0.0.0.0"
PORT = 5000
def send_command(client, command):
client.sendall(command.encode("ascii"))
reply = client.recv(1024).decode("ascii")
print("HIWIN reply:", reply)
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server:
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind((HOST, PORT))
server.listen(1)
print(f"Waiting on {HOST}:{PORT}")
client, address = server.accept()
with client:
print("HIWIN connected:", address)
while True:
command = input("Command, or q to quit: ").strip()
if command.lower() == "q":
break
if not (command.startswith("{") and command.endswith("}")):
print("Use format: {group,parameter,value}")
continue
send_command(client, command)
HOST = "0.0.0.0" 表示 Python 接受本機所有網路介面的連線;HIWIN 的 COPEN 則必須填寫 Python 主機在區域網路中的實際位址,例如 192.168.0.10。
bind() 將 socket 綁定到 5000,listen(1) 開始等待連線,accept() 則停在原地,直到 HIWIN 成功連入。連線建立後,client 才是實際收送資料的 socket;server 仍是監聽 socket。
send_command() 使用 sendall(),確保 Python 會持續送到這批位元組全部交給作業系統。接著呼叫 recv() 等待 HIWIN 回覆,所以畫面停在這一行不一定是 Python 當機,也可能是手臂仍在移動、HRB 尚未執行到 CWRITE,或控制器已因警報停止程式。
輸入檢查只確認命令前後有大括號,沒有檢查欄位數量及數值範圍。輸入內容仍應符合下方表格,否則 HRB 可能把缺少的欄位讀成 0,或回覆 ACK = -1。
執行後,等 HIWIN 連上來,再直接輸入命令:
{1,1,10} → X +10 mm
{1,2,-5} → Y -5 mm
{1,3,2} → Z +2 mm
{2,1,0} → 移動到 P1
{2,2,0} → 移動到 P2
第三欄在 Group 2 沒有用途,但仍保留 0,讓封包固定維持三個數值。
正值與負值代表座標增減方向。例如 {1,1,10} 是在目前 X 座標加上 10 mm,不等於畫面上固定向右移動;實際方向仍取決於運動指令使用的 Tool、Base 與手臂姿態。
HIWIN HRB
INT PYTHON
REAL CMD_GROUP, CMD_AXIS, CMD_AMOUNT
REAL ACK
E6POINT TARGET
; Replace this IP with the Python server IP.
PYTHON = COPEN(ETH, 192, 168, 0, 10, 5000)
IF PYTHON > -1 THEN
LOOP
CREAD(PYTHON, CMD_GROUP, CMD_AXIS, CMD_AMOUNT)
ACK = -1
; Group 1: relative Cartesian movement.
IF CMD_GROUP == 1 THEN
TARGET = GETPOINT
IF CMD_AXIS == 1 THEN
TARGET.X = TARGET.X + CMD_AMOUNT
ACK = 0
ENDIF
IF CMD_AXIS == 2 THEN
TARGET.Y = TARGET.Y + CMD_AMOUNT
ACK = 0
ENDIF
IF CMD_AXIS == 3 THEN
TARGET.Z = TARGET.Z + CMD_AMOUNT
ACK = 0
ENDIF
IF ACK == 0 THEN
LIN TARGET FINE=2 Vel=100mm/s Acc=20% TOOL[0] BASE[0]
ENDIF
ENDIF
; Group 2: move to a taught point.
IF CMD_GROUP == 2 THEN
IF CMD_AXIS == 1 THEN
PTP P1 FINE=2 Vel=10% Acc=20% TOOL[0] BASE[0]
ACK = 0
ENDIF
IF CMD_AXIS == 2 THEN
PTP P2 FINE=2 Vel=10% Acc=20% TOOL[0] BASE[0]
ACK = 0
ENDIF
ENDIF
CWRITE(PYTHON, ACK)
ENDLOOP
ENDIF
PYTHON 儲存 COPEN 傳回的通訊 handle。回傳值大於 -1 才代表通道已開啟,因此所有 CREAD、CWRITE 與運動流程都放在 IF PYTHON > -1 裡。若連線失敗,這份程式不會進入主迴圈。
CREAD(PYTHON, CMD_GROUP, CMD_AXIS, CMD_AMOUNT) 會等待 Python 傳入資料。三個接收變數使用 REAL,並依封包中的順序取得數值。這也是封包固定保留三欄的原因:欄位意義不會隨 Group 改變,讀程式時比較不容易把資料放錯位置。
每輪開始先把 ACK 設成 -1。只有命令進入範例允許的分支後,程式才將它改成 0。未知 Group 或未知軸號不會沿用上一輪的成功結果。
ACK = 0 表示命令已進入已知分支,而且 HRB 已執行到動作之後的 CWRITE;ACK = -1 表示 Group 或參數不在範例範圍內。CWRITE 會替數值加上大括號,因此 Python 收到的內容會是 {0...} 或 {-1...},小數位數依控制器通訊格式而定。
ACK 只描述這份 HRB 的程式流程,不能當成控制器狀態碼。它沒有回報目前座標、警報內容、急停狀態,也沒有核對 Python 送出的原始命令。需要可靠的命令同步時,應另外設計命令 ID、逾時及重送規則。
封包欄位
{CMD_GROUP,CMD_AXIS,CMD_AMOUNT}
| Group | 用途 | CMD_AXIS |
CMD_AMOUNT |
|---|---|---|---|
| 1 | Cartesian 相對移動 | 1=X、2=Y、3=Z | 位移量,單位 mm |
| 2 | 移動到教導點 | 1=P1、2=P2 | 固定填 0 |
Group 1 先用 GETPOINT 取得目前位置,只修改 X、Y 或 Z,再用 LIN 前往新位置。Group 2 使用 PTP 前往 P1 或 P2,路徑不保證是直線。
Group 1 為何能形成相對位移
LIN 接收的是絕對目標點,但 TARGET 每次都從當下位置建立。假設目前 X 是 300 mm,收到 {1,1,10} 後,程式會算出 TARGET.X = 300 + 10 = 310 mm,再以 LIN TARGET 移動到 310 mm。目標本身是絕對座標,產生的效果則是從當下位置沿 X 增加 10 mm。
TARGET = GETPOINT 會一併取得 X、Y、Z、A、B、C 與各軸資訊。程式只改其中一個 Cartesian 欄位,其餘姿態資料沿用當下值,可以避免只建立 X、Y、Z 而遺漏末端姿態。這個計算仍以運動時選用的 Tool 與 Base 為前提;現場若不是 TOOL[0] BASE[0],必須同步修改範例設定。
Group 2 與 Group 1 的差別
Group 2 不計算目前位置,而是直接執行 PTP P1 或 PTP P2。P1、P2 是教導器內已儲存的絕對點位。PTP 會讓各關節協調抵達目標,TCP 經過的路徑不保證是直線,因此點位附近即使沒有障礙物,兩點之間的空間仍要確認。
兩種速度單位也不同:範例中的 LIN Vel=100mm/s 是末端直線速度;PTP Vel=10% 是點到點運動的百分比速度。兩者不能直接當成相同數值比較。
建議測試順序
- 先確認 Python 主機與 HIWIN 位於可互通的區域網路,IP 與 port 沒有填反。
- 先啟動 Python,看到
Waiting on 0.0.0.0:5000後,再啟動 HRB。 - Python 顯示
HIWIN connected後,先送{9,0,0}。這筆命令不會進入運動分支,預期收到失敗 ACK,可先確認雙向通訊。 - 在 T1、低速及可立即停止的條件下,用 Group 1 測試很小的位移,先確認正負方向。
- 確認 P1、P2、Tool、Base 與路徑後,才測試 Group 2。
常見狀況
| 現象 | 先檢查的地方 |
|---|---|
Python 一直停在 accept() |
HRB 是否已執行 COPEN、IP 與 port 是否一致、防火牆是否允許連入 |
HRB 停在 CREAD |
Python 是否已連線並送出含大括號的命令;CREAD 沒有資料時會等待 |
Python 停在 recv() |
HRB 是否仍在移動、是否發生警報、程式是否已走到 CWRITE |
| 收到失敗 ACK | CMD_GROUP、CMD_AXIS 是否落在表格定義的範圍 |
| 移動方向與預期不同 | Tool、Base、目前姿態,以及正負方向的判讀基準 |
| 多次收送後回覆黏在一起 | TCP 沒有訊息邊界,正式程式需建立完整的封包緩衝與解析規則 |
指令版本與來源
| 指令 | 用途 | 手冊位置 | 文件列出的適用版本 |
|---|---|---|---|
COPEN |
開啟 Ethernet TCP/IP 通訊 | PDF 186–188/手冊 184–186 | HRSS 2.0.1 以上 |
CREAD |
接收外部設備傳入的數值 | PDF 190–192/手冊 188–190 | HRSS 2.0.1 以上 |
CWRITE |
將數值回傳外部設備 | PDF 192–194/手冊 190–192 | HRSS 2.0.1 以上 |
GETPOINT |
取得目前 Cartesian 與關節資訊 | PDF 162–163/手冊 160–161 | HRSS 3.2.1 以上 |
LIN |
沿直線移動到絕對目標點 | PDF 71–73/手冊 69–71 | HRSS 2.0.1 以上 |
PTP |
以點到點方式移動到目標點 | PDF 67–69/手冊 65–67 | HRSS 2.0.1 以上 |
控制器版本若低於表格要求,應先查該版本手冊,不能只靠範例是否成功匯入來判斷支援程度。
使用限制
- 執行 Group 2 前,P1、P2 必須已完成教導,Tool 與 Base 也要符合點位使用的座標設定。
TOOL[0] BASE[0]應改成設備實際使用的 Tool 與 Base 編號。- 第一次運動測試應使用 T1、低速與小位移,確認方向後再逐步增加距離。
- TCP 是連續位元組串流,單次
recv()不保證剛好取得一筆完整回覆。長時間執行時需要封包緩衝、逾時及重複命令處理。 CREAD沒有資料時會持續等待。需要停止或重新連線功能時,應另行加入資料查詢、離線處理與CCLOSE流程。