173 lines
6.1 KiB
Python
173 lines
6.1 KiB
Python
|
|
import time
|
|||
|
|
|
|||
|
|
import can
|
|||
|
|
import threading
|
|||
|
|
from typing import Dict, List, Tuple, Optional
|
|||
|
|
from canbus import RxCache4Ch, BroadcastSenderThread, SingleSenderThread
|
|||
|
|
from usb_can import resolve_canalyst_device_index
|
|||
|
|
from can_msg_builder import *
|
|||
|
|
|
|||
|
|
|
|||
|
|
class Exoskeleton:
|
|||
|
|
def __init__(self):
|
|||
|
|
self.bitrate = 1000000
|
|||
|
|
self.motor_map = {
|
|||
|
|
("dev0", 0): [1, 2, 3, 4],
|
|||
|
|
("dev0", 1): [1, 2, 3, 4],
|
|||
|
|
("dev1", 0): [1, 2, 3, 4],
|
|||
|
|
("dev1", 1): [1, 2, 3, 4],
|
|||
|
|
}
|
|||
|
|
self.joint_map = {
|
|||
|
|
"sender-dev0-ch0": ["LJ1", "LJ2", "RJ1", "RJ2"],
|
|||
|
|
"sender-dev0-ch1": ["LJ5", "LJ3", "RJ5", "RJ3"],
|
|||
|
|
"sender-dev1-ch0": ["LJ7", "LJ6", "LJ4"],
|
|||
|
|
"sender-dev1-ch1": ["RJ7", "RJ6", "RJ4"],
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
idx0 = resolve_canalyst_device_index("/dev/canalystii_1")
|
|||
|
|
idx1 = resolve_canalyst_device_index("/dev/canalystii_0")
|
|||
|
|
self.bus0 = can.Bus(interface="canalystii", channel=[0, 1], bitrate=self.bitrate, device=idx0)
|
|||
|
|
self.bus1 = can.Bus(interface="canalystii", channel=[0, 1], bitrate=self.bitrate, device=idx1)
|
|||
|
|
|
|||
|
|
# ========= 接收缓存:每个设备一个 Notifier,一个 cache(按 channel 分流) =========
|
|||
|
|
self.cache0 = RxCache4Ch({0: ["LJ1", "LJ2", "RJ1", "RJ2"], 1: ["LJ5", "LJ3", "RJ5", "RJ3"]})
|
|||
|
|
self.cache1 = RxCache4Ch({0: ["LJ7", "LJ6", "LJ4"], 1: ["RJ7", "RJ6", "RJ4"]})
|
|||
|
|
self.notifier0 = can.Notifier(self.bus0, [self.cache0], timeout=0.01)
|
|||
|
|
self.notifier1 = can.Notifier(self.bus1, [self.cache1], timeout=0.01)
|
|||
|
|
|
|||
|
|
# ========= 发送线程:4个逻辑channel =========
|
|||
|
|
self.stop_event = threading.Event()
|
|||
|
|
self.cmd_event = threading.Event()
|
|||
|
|
self.barrier = threading.Barrier(4 + 1) # 4个sender + 主线程
|
|||
|
|
|
|||
|
|
self.bus0_lock = threading.Lock()
|
|||
|
|
self.bus1_lock = threading.Lock()
|
|||
|
|
|
|||
|
|
sender_dev0_ch0 = BroadcastSenderThread(
|
|||
|
|
"sender-dev0-ch0", self.bus0, self.bus0_lock, 0, self.cmd_event, self.barrier, self.stop_event)
|
|||
|
|
sender_dev0_ch1 = SingleSenderThread(
|
|||
|
|
"sender-dev0-ch1", self.bus0, self.bus0_lock, 1, self.cmd_event, self.barrier, self.stop_event, [1, 3])
|
|||
|
|
sender_dev1_ch0 = BroadcastSenderThread(
|
|||
|
|
"sender-dev1-ch0", self.bus1, self.bus1_lock, 0, self.cmd_event, self.barrier, self.stop_event)
|
|||
|
|
sender_dev1_ch1 = BroadcastSenderThread(
|
|||
|
|
"sender-dev1-ch1", self.bus1, self.bus1_lock, 1, self.cmd_event, self.barrier, self.stop_event)
|
|||
|
|
self.senders = [sender_dev0_ch0, sender_dev0_ch1, sender_dev1_ch0, sender_dev1_ch1]
|
|||
|
|
for sender in self.senders:
|
|||
|
|
sender.start()
|
|||
|
|
|
|||
|
|
def turn_on(self):
|
|||
|
|
for sender in self.senders:
|
|||
|
|
sender.turn_on()
|
|||
|
|
self.cmd_event.set()
|
|||
|
|
try:
|
|||
|
|
self.barrier.wait(timeout=0.05)
|
|||
|
|
except threading.BrokenBarrierError:
|
|||
|
|
pass
|
|||
|
|
self.cmd_event.clear()
|
|||
|
|
|
|||
|
|
def turn_off(self):
|
|||
|
|
for sender in self.senders:
|
|||
|
|
sender.turn_off()
|
|||
|
|
self.cmd_event.set()
|
|||
|
|
try:
|
|||
|
|
self.barrier.wait(timeout=0.05)
|
|||
|
|
except threading.BrokenBarrierError:
|
|||
|
|
pass
|
|||
|
|
self.cmd_event.clear()
|
|||
|
|
|
|||
|
|
def clear_error(self):
|
|||
|
|
for sender in self.senders:
|
|||
|
|
sender.clear_error()
|
|||
|
|
self.cmd_event.set()
|
|||
|
|
try:
|
|||
|
|
self.barrier.wait(timeout=0.05)
|
|||
|
|
except threading.BrokenBarrierError:
|
|||
|
|
pass
|
|||
|
|
self.cmd_event.clear()
|
|||
|
|
|
|||
|
|
def stop_all(self):
|
|||
|
|
for sender in self.senders:
|
|||
|
|
sender.stop()
|
|||
|
|
self.cmd_event.set()
|
|||
|
|
try:
|
|||
|
|
self.barrier.wait(timeout=0.05)
|
|||
|
|
except threading.BrokenBarrierError:
|
|||
|
|
pass
|
|||
|
|
self.cmd_event.clear()
|
|||
|
|
|
|||
|
|
def set_zero(self):
|
|||
|
|
for sender in self.senders:
|
|||
|
|
sender.set_zero()
|
|||
|
|
self.cmd_event.set()
|
|||
|
|
try:
|
|||
|
|
self.barrier.wait(timeout=0.05)
|
|||
|
|
except threading.BrokenBarrierError:
|
|||
|
|
pass
|
|||
|
|
self.cmd_event.clear()
|
|||
|
|
|
|||
|
|
def set_torq(self, torq: Dict[str, float]):
|
|||
|
|
torq_cmd = {}
|
|||
|
|
for sender in self.senders:
|
|||
|
|
torq_cmd[sender.name] = [torq[x] for x in self.joint_map[sender.name]]
|
|||
|
|
for sender in self.senders:
|
|||
|
|
sender.set_torq(torq_cmd[sender.name])
|
|||
|
|
self.cmd_event.set()
|
|||
|
|
try:
|
|||
|
|
self.barrier.wait(timeout=0.05)
|
|||
|
|
except threading.BrokenBarrierError:
|
|||
|
|
pass
|
|||
|
|
self.cmd_event.clear()
|
|||
|
|
|
|||
|
|
def set_pos(self, pos: Dict[str, float]):
|
|||
|
|
pos_cmd = {}
|
|||
|
|
for sender in self.senders:
|
|||
|
|
pos_cmd[sender.name] = [pos[x] for x in self.joint_map[sender.name]]
|
|||
|
|
for sender in self.senders:
|
|||
|
|
sender.set_pos(pos_cmd[sender.name])
|
|||
|
|
self.cmd_event.set()
|
|||
|
|
try:
|
|||
|
|
self.barrier.wait(timeout=0.05)
|
|||
|
|
except threading.BrokenBarrierError:
|
|||
|
|
pass
|
|||
|
|
self.cmd_event.clear()
|
|||
|
|
|
|||
|
|
def get_states(self):
|
|||
|
|
c0 = self.cache0.snapshot()
|
|||
|
|
c1 = self.cache1.snapshot()
|
|||
|
|
print({"c0": c0, "c1": c1})
|
|||
|
|
|
|||
|
|
@staticmethod
|
|||
|
|
def _arb_id_single(motor_id: int) -> int:
|
|||
|
|
return 0x140 + int(motor_id)
|
|||
|
|
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
exoskeleton = Exoskeleton()
|
|||
|
|
exoskeleton.turn_on()
|
|||
|
|
count = 0
|
|||
|
|
try:
|
|||
|
|
while True:
|
|||
|
|
time.sleep(0.02)
|
|||
|
|
exoskeleton.set_torq({
|
|||
|
|
"LJ1": 30, "LJ2": 30, "LJ3": 30, "LJ4": 30, "LJ5": 100, "LJ6": 100, "LJ7": 100,
|
|||
|
|
"RJ1": 30, "RJ2": 30, "RJ3": 30, "RJ4": 30, "RJ5": 100, "RJ6": 100, "RJ7": 100,
|
|||
|
|
})
|
|||
|
|
count += 1
|
|||
|
|
if count % 10 == 0:
|
|||
|
|
exoskeleton.get_states()
|
|||
|
|
except KeyboardInterrupt:
|
|||
|
|
exoskeleton.set_torq({
|
|||
|
|
"LJ1": 0, "LJ2": 0, "LJ3": 0, "LJ4": 0, "LJ5": 0, "LJ6": 0, "LJ7": 0,
|
|||
|
|
"RJ1": 0, "RJ2": 0, "RJ3": 0, "RJ4": 0, "RJ5": 0, "RJ6": 0, "RJ7": 0,
|
|||
|
|
})
|
|||
|
|
exoskeleton.stop_all()
|
|||
|
|
exoskeleton.turn_off()
|
|||
|
|
exoskeleton.stop_event.set()
|
|||
|
|
exoskeleton.cmd_event.set()
|
|||
|
|
for th in exoskeleton.senders:
|
|||
|
|
th.join(timeout=0.5)
|
|||
|
|
exoskeleton.notifier0.stop()
|
|||
|
|
exoskeleton.notifier1.stop()
|
|||
|
|
exoskeleton.bus0.shutdown()
|
|||
|
|
exoskeleton.bus1.shutdown()
|