exoskeleton/code/test/multi_turn_encode.py

117 lines
3.7 KiB
Python
Raw Permalink Normal View History

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import argparse
import math
import struct
import can
CMD_READ_MULTITURN_ANGLE = 0x92
def arb_id_single(motor_id: int) -> int:
# 单电机命令/回包 arbitration id
return 0x140 + int(motor_id)
def build_read_multiturn_angle(motor_id: int) -> can.Message:
"""
读取多圈角度命令
data[0]=0x92其余填0
"""
data = [CMD_READ_MULTITURN_ANGLE, 0, 0, 0, 0, 0, 0, 0]
return can.Message(arbitration_id=arb_id_single(motor_id), is_extended_id=False, data=data)
def sign_extend(value: int, bits: int) -> int:
"""
value 做符号扩展使其成为 Python int有符号
"""
sign_bit = 1 << (bits - 1)
mask = (1 << bits) - 1
value &= mask
return (value ^ sign_bit) - sign_bit
def parse_multiturn_angle_reply_int56(msg: can.Message) -> int:
"""
兼容你贴的文档DATA[1..7] 是角度的低->高字节共7字节
=> 解析为有符号 int56
单位0.01 deg / LSB
"""
d = bytes(msg.data)
if len(d) != 8 or d[0] != CMD_READ_MULTITURN_ANGLE:
raise ValueError("not 0x92 reply")
raw_u56 = int.from_bytes(d[1:8], byteorder="little", signed=False) # 7 bytes
raw_s56 = sign_extend(raw_u56, 56)
return raw_s56
def parse_multiturn_angle_reply_int64(msg: can.Message) -> int:
"""
如果你确认实际回的是 8 字节 motorAngleint64
常见做法是 DATA[0..7] 都是 motorAngle 8 字节但会与命令字节=0x92冲突
或者命令字节在别处
这个函数留作你确认后切换用
"""
d = bytes(msg.data)
# 这里假设 d[0] 仍是 0x92则 motorAngle 应该在 d[1:9] 不存在
# 所以只有当你的实际报文不是“d[0]=0x92”时才用它
raise NotImplementedError("Need exact 8-byte layout confirmation.")
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--interface", default="canalystii", help="python-can interface: canalystii / socketcan / ...")
ap.add_argument("--channel", default="0", help="channel: canalystii 0/1, socketcan can0/can1 ...")
ap.add_argument("--bitrate", type=int, default=1000000)
ap.add_argument("--motor-id", type=int, default=2)
ap.add_argument("--timeout", type=float, default=0.2, help="seconds")
ap.add_argument("--repeat", type=int, default=3, help="query times")
args = ap.parse_args()
motor_id = args.motor_id
rx_arb = arb_id_single(motor_id)
bus = can.Bus(interface=args.interface, channel=args.channel, bitrate=args.bitrate)
# 只收这个电机的回包(降低噪声)
try:
bus.set_filters([{"can_id": rx_arb, "can_mask": 0x7FF, "extended": False}])
except Exception:
pass
try:
for i in range(args.repeat):
# 发送 0x92
bus.send(build_read_multiturn_angle(motor_id))
# 等回包
msg = bus.recv(timeout=args.timeout)
if msg is None:
print(f"[M{motor_id}] timeout: no reply")
continue
if msg.arbitration_id != rx_arb or len(msg.data) != 8:
print(f"[M{motor_id}] unexpected frame: arb=0x{msg.arbitration_id:X} data={list(msg.data)}")
continue
# 解析按你贴的文档int56 in DATA[1..7]
motor_angle_raw = parse_multiturn_angle_reply_int56(msg)
# 单位换算0.01°/LSB
angle_deg = motor_angle_raw * 0.01
angle_rad = angle_deg * math.pi / 180.0
print(
f"[M{motor_id}] motorAngle_raw={motor_angle_raw} "
f"angle_deg={angle_deg:+.4f}° angle_rad={angle_rad:+.6f} rad"
)
finally:
bus.shutdown()
if __name__ == "__main__":
main()