Files
NODEDC_MISSION_CORE/plugins/vesc/runtime/hall_detection.py
T

99 lines
5.0 KiB
Python

"""Native FW 5.02 Hall measurement used by VESC Tool (COMM_DETECT_HALL_FOC).
This is a non-interruptible ~12 s firmware procedure: it locks mc_interface,
overrides phase, sweeps three electrical turns in each direction and restores
its prior RAM configuration. A host stop/current-zero cannot cancel the sweep.
No table is applied here; measurements are archived in the operation receipt.
"""
from .configuration import decode
from .protocol import values, ppm
def parse_result(raw):
if len(raw) != 10 or raw[0] != 28 or raw[9] not in (0, 1):
raise ValueError("Invalid Hall result")
table = list(raw[1:9])
if any(v > 200 and v != 255 for v in table): raise ValueError("Invalid Hall angle")
observed = [i for i, v in enumerate(table) if v != 255]
return {"firmware_success": raw[9] == 0, "hall_table": table,
"observed_states": observed, "valid_six_states": raw[9] == 0 and len(observed) == 6}
def measure(owner, devices, target, original, backups, unchanged):
motor = decode(original, "motor")
if motor["m_sensor_port_mode"] != 0:
from .motor_test import Rejected
raise Rejected("Вход датчиков выбран не в режиме Холла.")
pending = owner.service.root / "hall-pending.json"
owner.atomic(pending, {"device_id": target.id, "identity": target.identity,
"started_at": owner.utc(), "backups": backups})
owner.state("calibrating")
owner.active, owner.mode = True, "hall"
started = owner.monotonic()
samples, issues, result, after = [], [], None, {}
completed, restored, attempted = False, False, False
try:
unchanged()
owner.neutral(devices)
# Do not enter the native procedure if Stop arrived during preflight.
if owner.stop.is_set(): raise ValueError("Measurement cancelled before start")
for device in devices: device.link.test_command("claim")
attempted = True
target.link.detect_hall()
while owner.monotonic() - started < 30:
cycle = owner.monotonic()
try:
unchanged()
for device in devices:
incoming = ppm(device.link.query(31, timeout=0.06))
if abs(incoming["level"]) > 0.02:
owner.state("rc")
if "rc_during_native_cycle" not in issues: issues.append("rc_during_native_cycle")
sample = values(target.link.query(4, timeout=0.06))
samples.append({"at": owner.monotonic() - started, "values": sample})
for device in devices: device.link.test_command("claim")
for device in devices:
if device is not target: device.link.test_command("release")
except (OSError, ValueError, TimeoutError):
if "communication_unconfirmed" not in issues: issues.append("communication_unconfirmed")
if owner.stop.is_set() and "stop_requested_during_native_cycle" not in issues:
issues.append("stop_requested_during_native_cycle")
reply = target.link.hall_result
if reply is not None:
result = parse_result(reply)
completed = True
break
owner.sleep(max(0, 0.1 - (owner.monotonic() - cycle)))
except (OSError, ValueError, TimeoutError):
issues.append("native_completion_unconfirmed")
finally:
# Zero is a release AFTER completion, never a claim that native detect
# is interruptible. Peers also receive release if the target disappears.
for device in devices:
try: device.link.test_command("release")
except (OSError, ValueError, TimeoutError): issues.append("release_unconfirmed")
owner.sleep(0.3)
for device in devices:
try: after[device.id] = values(device.link.query(4, timeout=0.1))
except (OSError, ValueError, TimeoutError): after[device.id] = None
if completed or not attempted:
target.link.hall_pending = False
try: restored = target.link.query(14) == original
except (OSError, ValueError, TimeoutError): pass
released = all(v is not None and abs(v["motor_current_a"]) <= 1 for v in after.values())
if (completed or not attempted) and restored and released:
pending.unlink()
import os
fd = os.open(pending.parent, os.O_RDONLY)
try: os.fsync(fd)
finally: os.close(fd)
if not owner.latched: owner.state("ready")
else:
owner.state("rc")
owner.active, owner.mode = False, None
return {"observed_at": owner.utc(), "device_id": target.id, "procedure": "native_foc_hall",
"current_a": 5, "elapsed_s": owner.monotonic() - started,
"completed": completed, "measurement": result, "issues": issues,
"configuration_restored": restored, "configuration_written": False,
"release_confirmed": released, "after": after, "samples": samples, "backups": backups}