cmvr-head-client/test.py

119 lines
3.5 KiB
Python
Raw Normal View History

2026-08-05 16:42:59 +08:00
import argparse
import statistics
import time
import erpc
from servo_service import client
DEFAULT_PORT = "COM8"
DEFAULT_BAUD = 115200
DEFAULT_ITERATIONS = 100
DEFAULT_JOINT_COUNT = 26
DEFAULT_WARMUP = 5
def positive_int(value):
parsed = int(value)
if parsed <= 0:
raise argparse.ArgumentTypeError("value must be > 0")
return parsed
def non_negative_float(value):
parsed = float(value)
if parsed < 0:
raise argparse.ArgumentTypeError("value must be >= 0")
return parsed
def percentile(values, ratio):
if not values:
raise ValueError("values must not be empty")
ordered = sorted(values)
index = int(ratio * (len(ordered) - 1))
return ordered[index]
def measure_movej_latency(api, angles, iterations, warmup):
for index in range(warmup):
result = api.moveJ(angles)
# if result != 0:
# raise RuntimeError(f"Warmup moveJ failed at iteration {index}: {result}")
costs_ms = []
for index in range(iterations):
t0 = time.perf_counter()
result = api.moveJ(angles)
elapsed_ms = (time.perf_counter() - t0) * 1000.0
# if result != 0:
# raise RuntimeError(f"moveJ failed at iteration {index}: {result}")
costs_ms.append(elapsed_ms)
return costs_ms
def print_summary(costs_ms):
print(f"count = {len(costs_ms)}")
print(f"avg = {statistics.mean(costs_ms):.3f} ms")
print(f"min = {min(costs_ms):.3f} ms")
print(f"max = {max(costs_ms):.3f} ms")
print(f"p50 = {statistics.median(costs_ms):.3f} ms")
print(f"p95 = {percentile(costs_ms, 0.95):.3f} ms")
def build_parser():
parser = argparse.ArgumentParser(description="Measure moveJ RPC latency.")
parser.add_argument("--port", default=DEFAULT_PORT, help=f"Serial port, default: {DEFAULT_PORT}")
parser.add_argument("--baud", type=positive_int, default=DEFAULT_BAUD, help=f"Serial baud, default: {DEFAULT_BAUD}")
parser.add_argument(
"--iterations",
type=positive_int,
default=DEFAULT_ITERATIONS,
help=f"Measured moveJ calls, default: {DEFAULT_ITERATIONS}",
)
parser.add_argument(
"--warmup",
type=positive_int,
default=DEFAULT_WARMUP,
help=f"Warmup moveJ calls before measuring, default: {DEFAULT_WARMUP}",
)
parser.add_argument(
"--joint-count",
type=positive_int,
default=DEFAULT_JOINT_COUNT,
help=f"Length of angles_rad list, default: {DEFAULT_JOINT_COUNT}",
)
parser.add_argument(
"--angle-rad",
type=non_negative_float,
default=0.0,
help="Angle value written into every joint slot, default: 0.0",
)
return parser
def main():
args = build_parser().parse_args()
angles = [args.angle_rad] * args.joint_count
transport = erpc.transport.SerialTransport(args.port, args.baud)
try:
client_mgr = erpc.client.ClientManager(transport, erpc.basic_codec.BasicCodec)
api = client.servo_serviceClient(client_mgr)
print(f"port = {args.port}")
print(f"baud = {args.baud}")
print(f"iterations = {args.iterations}")
print(f"warmup = {args.warmup}")
print(f"joint_count = {args.joint_count}")
print(f"angle_rad = {args.angle_rad}")
costs_ms = measure_movej_latency(api, angles, args.iterations, args.warmup)
print_summary(costs_ms)
finally:
if hasattr(transport, "close"):
transport.close()
if __name__ == "__main__":
main()