新版布局
This commit is contained in:
+59
-17
@@ -2,12 +2,12 @@
|
||||
"""车端 CAN 报文模拟器 / 上行抓包器(联调用,非工程代码)。
|
||||
|
||||
依据《洗消机器人遥控器通信协议20260810.xlsx》sheet RC(分类一/二/三,全部扩展帧、Intel 小端位序)。
|
||||
线格式:32 位帧头 0x5AA5FF00 + 32 位 canId + DLC(8) + 8 字节数据 + 校验和,共 18 字节,
|
||||
多字节字段按小端排列,与 comm/udpcommunication.cpp 的 UdpCanFrame 一致。
|
||||
线格式:帧头 0xAA + 32 位 canId + DLC(8) + 8 字节数据 + 校验和 + 帧尾 0xBB,共 16 字节,
|
||||
多字节字段按小端排列,与 comm/car.cpp 的 CanFrame 一致。
|
||||
|
||||
用法示例:
|
||||
# 向 UI 的 9001 端口周期发 VCU 反馈,看底盘页电量/电压/车速刷新
|
||||
python can_sender.py --soc 85 --volt 360 --speed 4.5
|
||||
python can_sender.py --soc 85 --volt 360 --speed 2.0
|
||||
|
||||
# 制造故障:MCU 码 61(电机缺相故障)+ 整系统通信异常(底盘BMS)
|
||||
python can_sender.py --mcu-fault 61 --comm-fault 2
|
||||
@@ -24,7 +24,8 @@ import socket
|
||||
import struct
|
||||
import time
|
||||
|
||||
HDR = 0x5AA5FF00
|
||||
HDR = 0xAA # 帧头
|
||||
TAIL = 0xBB # 帧尾
|
||||
|
||||
# 分类一 遥控器控制指令(上行,UI → 车端)
|
||||
ID_CHASSIS_CMD = 0x18208081
|
||||
@@ -91,11 +92,14 @@ def get_signed(d, start, ln):
|
||||
|
||||
|
||||
def checksum(can_id, dlc, d):
|
||||
return (sum((can_id >> (8 * i)) & 0xFF for i in range(4)) + dlc + sum(d)) & 0xFF
|
||||
# 帧头 + canId 四字节 + DLC + 数据八字节 + 帧尾 求和取低 8 位
|
||||
return (HDR + sum((can_id >> (8 * i)) & 0xFF for i in range(4))
|
||||
+ dlc + sum(d) + TAIL) & 0xFF
|
||||
|
||||
|
||||
def pack(can_id, d):
|
||||
return struct.pack("<IIB8BB", HDR, can_id, 8, *d, checksum(can_id, 8, d))
|
||||
return struct.pack("<BI B 8B B B", HDR, can_id, 8, *d,
|
||||
checksum(can_id, 8, d), TAIL)
|
||||
|
||||
|
||||
def clamp(v, lo, hi):
|
||||
@@ -117,9 +121,31 @@ def fb_vehicle1(a):
|
||||
def fb_vehicle2(a):
|
||||
d = bytearray(8)
|
||||
set_bits(d, 22, 2, 1 if a.handbrake else 0) # Handbrake
|
||||
# Vehi_spd 16 位 0~500 × 0.01;其后字段整体后移 8 位
|
||||
set_bits(d, 24, 16, clamp(round(a.speed * 100), 0, 500)) # Vehi_spd
|
||||
set_bits(d, 48, 8, a.comm_fault) # All_Comm_FaultCode
|
||||
# Vehi_spd 协议表为 8 位 × 0.01(上限 2.55 km/h),与 comm/car.cpp 的读法一致;
|
||||
# 早先按 16 位写、并把后续字段整体后移 8 位,会让 rdyStatus/通信故障码错位。
|
||||
set_bits(d, 24, 8, clamp(round(a.speed * 100), 0, 255)) # Vehi_spd
|
||||
set_bits(d, 32, 2, a.rdy_status) # Vehi_RdyStatus
|
||||
set_bits(d, 40, 8, a.comm_fault) # All_Comm_FaultCode
|
||||
return d
|
||||
|
||||
|
||||
def fb_vehicle3(a):
|
||||
# 底盘状态3:46 个 EPS/制动故障位 + EPS_FaultCode,全 0 即无故障
|
||||
d = bytearray(8)
|
||||
set_bits(d, 48, 8, a.eps_fault) # EPS_FaultCode
|
||||
return d
|
||||
|
||||
|
||||
def fb_vehicle4(a):
|
||||
d = bytearray(8)
|
||||
set_bits(d, 0, 2, a.gear) # 0=N挡 1=D挡 2=R挡
|
||||
set_bits(d, 2, 2, a.parking) # 0未驻车 1左电机 2右电机 3完全驻车
|
||||
set_bits(d, 4, 2, a.awd) # 0二驱 1四驱 2四驱锁定
|
||||
set_bits(d, 8, 16, a.steer) # Steering_Angle
|
||||
# ALIGN 是状态字节:0x11=对中失败、0xEE/0x55=成功。给 0 会被判成“方向盘未对中”
|
||||
set_bits(d, 24, 8, a.align) # Angle_alignment_status
|
||||
set_bits(d, 32, 8, a.brake_r) # Brake_Right_Mot_fault
|
||||
set_bits(d, 40, 8, a.brake_l) # Brake_Left_Mot_fault
|
||||
return d
|
||||
|
||||
|
||||
@@ -161,6 +187,8 @@ def fb_decontam1(a):
|
||||
set_bits(d, 3, 1, 1 if a.plunger else 0)
|
||||
set_bits(d, 4, 1, 1 if a.air else 0)
|
||||
set_bits(d, 5, 1, 1 if a.leaf else 0)
|
||||
set_bits(d, 6, 1, 1 if a.level_a_low else 0) # A_LiquidLevel_Low
|
||||
set_bits(d, 7, 1, 1 if a.level_b_low else 0) # B_LiquidLevel_Low
|
||||
set_bits(d, 8, 1, 1 if a.d1open else 0) # MBV_D1_Sta
|
||||
set_bits(d, 9, 1, 1 if a.d2open else 0)
|
||||
set_bits(d, 10, 1, 1 if a.sys_fault else 0)
|
||||
@@ -199,6 +227,7 @@ def fb_detector2(a):
|
||||
|
||||
FEEDBACK = [
|
||||
(ID_VEHICLE_FB1, fb_vehicle1), (ID_VEHICLE_FB2, fb_vehicle2),
|
||||
(ID_VEHICLE_FB3, fb_vehicle3), (ID_VEHICLE_FB4, fb_vehicle4),
|
||||
(ID_ARM_FB1, fb_arm1), (ID_ARM_FB2, fb_arm2),
|
||||
(ID_ARM_FB3, fb_arm3), (ID_ARM_FB4, fb_arm4),
|
||||
(ID_DECONTAM_FB1, fb_decontam1), (ID_DECONTAM_FB2, fb_decontam2),
|
||||
@@ -250,11 +279,11 @@ def listen(host, port, once):
|
||||
while True:
|
||||
data, addr = sock.recvfrom(4096)
|
||||
off = 0
|
||||
while off + 18 <= len(data):
|
||||
hdr, cid, dlc = struct.unpack_from("<IIB", data, off)
|
||||
if hdr == HDR and dlc == 8:
|
||||
body = data[off + 9:off + 17]
|
||||
cks = data[off + 17]
|
||||
while off + 16 <= len(data):
|
||||
hdr, cid, dlc = struct.unpack_from("<BIB", data, off)
|
||||
if hdr == HDR and dlc == 8 and data[off + 15] == TAIL:
|
||||
body = data[off + 6:off + 14]
|
||||
cks = data[off + 14]
|
||||
if cks == checksum(cid, dlc, body):
|
||||
txt = describe(cid, body)
|
||||
if txt:
|
||||
@@ -262,7 +291,7 @@ def listen(host, port, once):
|
||||
if once is None or last.get(cid) != body:
|
||||
last[cid] = bytes(body)
|
||||
print("%-16s %s" % (NAMES.get(cid, hex(cid)), txt))
|
||||
off += 18
|
||||
off += 16
|
||||
continue
|
||||
off += 1
|
||||
except KeyboardInterrupt:
|
||||
@@ -272,7 +301,8 @@ def listen(host, port, once):
|
||||
def main():
|
||||
p = argparse.ArgumentParser()
|
||||
p.add_argument("--host", default="127.0.0.1")
|
||||
p.add_argument("--port", type=int, default=9001, help="发送目标(UI 的 net/recv_port)")
|
||||
p.add_argument("--port", type=int, default=9000,
|
||||
help="发送目标端口 = UI 的 kCanRxPort(默认 9000);--listen 抓上行时改 3001")
|
||||
p.add_argument("--listen", action="store_true", help="改为绑端口抓 UI 上行控制帧")
|
||||
p.add_argument("--hz", type=float, default=10.0, help="反馈帧发送频率,默认 10Hz")
|
||||
p.add_argument("--count", type=int, default=0, help="0=一直发")
|
||||
@@ -280,13 +310,23 @@ def main():
|
||||
# 电池 / 底盘
|
||||
p.add_argument("--soc", type=float, default=85.0, help="电量 %")
|
||||
p.add_argument("--volt", type=float, default=360.0, help="总电压 V(精度 0.1)")
|
||||
p.add_argument("--speed", type=float, default=3.45, help="km/h,精度 0.01")
|
||||
p.add_argument("--speed", type=float, default=2.0, help="km/h,精度 0.01,协议只给 8 位(上限 2.55)")
|
||||
p.add_argument("--no-handbrake", action="store_true")
|
||||
p.add_argument("--rdy-status", type=int, default=2, help="底盘 ready:0初始/1上电完成/2就绪")
|
||||
p.add_argument("--mcu-fault", type=int, default=0)
|
||||
p.add_argument("--mcu-level", type=int, default=0)
|
||||
p.add_argument("--celllow-level", type=int, default=0)
|
||||
p.add_argument("--cellhigh-level", type=int, default=0)
|
||||
p.add_argument("--comm-fault", type=int, default=0, help="整系统通信故障码 0~8")
|
||||
# 底盘状态3/4
|
||||
p.add_argument("--eps-fault", type=int, default=0, help="EPS 故障码,0=无")
|
||||
p.add_argument("--gear", type=int, default=0, help="0=N挡 1=D挡 2=R挡")
|
||||
p.add_argument("--parking", type=int, default=0, help="0未驻车 1左电机 2右电机 3完全驻车")
|
||||
p.add_argument("--awd", type=int, default=0, help="0二驱 1四驱 2四驱锁定")
|
||||
p.add_argument("--steer", type=int, default=0, help="方向盘角度")
|
||||
p.add_argument("--align", type=int, default=0x55, help="0x11对中失败 / 0xEE、0x55成功;别给 0")
|
||||
p.add_argument("--brake-r", type=int, default=0, help="右制动电机故障码")
|
||||
p.add_argument("--brake-l", type=int, default=0, help="左制动电机故障码")
|
||||
# 机械臂
|
||||
p.add_argument("--posx", type=int, default=1200)
|
||||
p.add_argument("--posy", type=int, default=300)
|
||||
@@ -306,6 +346,8 @@ def main():
|
||||
p.add_argument("--d1open", action="store_true")
|
||||
p.add_argument("--d2open", action="store_true")
|
||||
p.add_argument("--sys-fault", action="store_true")
|
||||
p.add_argument("--level-a-low", action="store_true", help="A组分/清水液位低 → 首页液位行红闪")
|
||||
p.add_argument("--level-b-low", action="store_true", help="B组分/泡沫液位低 → 首页液位行红闪")
|
||||
p.add_argument("--level-a", type=float, default=100)
|
||||
p.add_argument("--level-foam", type=float, default=30)
|
||||
p.add_argument("--flow-a", type=float, default=20)
|
||||
|
||||
Reference in New Issue
Block a user