exoskeleton/code/test/multi_turn_encode.py

117 lines
3.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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()