You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
124 lines
3.9 KiB
Python
124 lines
3.9 KiB
Python
2 years ago
|
#!/usr/bin/python3 -B
|
||
|
|
||
|
# Copyright 2021 Josh Pieper, jjp@pobox.com.
|
||
|
#
|
||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
# you may not use this file except in compliance with the License.
|
||
|
# You may obtain a copy of the License at
|
||
|
#
|
||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||
|
#
|
||
|
# Unless required by applicable law or agreed to in writing, software
|
||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
# See the License for the specific language governing permissions and
|
||
|
# limitations under the License.
|
||
|
|
||
|
|
||
|
"""
|
||
|
This example commands a single servo at ID #1 using the default
|
||
|
transport to hold the current position indefinitely, and prints the
|
||
|
state of the servo to the console.
|
||
|
"""
|
||
|
|
||
|
import asyncio
|
||
|
import math
|
||
|
import signal
|
||
|
import sys
|
||
|
from typing import Callable
|
||
|
|
||
|
import moteus
|
||
|
|
||
|
import logging
|
||
|
|
||
|
|
||
|
|
||
|
class BLDCMotor:
|
||
|
"""A BLDC Motor"""
|
||
|
|
||
|
def __init__(self) -> None:
|
||
|
self.controller = moteus.Controller()
|
||
|
|
||
|
async def set_stop(self, *args, **kwargs):
|
||
|
"""Stops the BLDC Motor"""
|
||
|
await self.controller.set_stop(*args, **kwargs)
|
||
|
|
||
|
async def set_position(self, *args, **kwargs):
|
||
|
state = await self.controller.set_position(*args, **kwargs)
|
||
|
return state
|
||
|
|
||
|
|
||
|
def create_async_signal_handler(coro_func: Callable[[], None]) -> Callable[[signal.Signals], None]:
|
||
|
async def _async_signal_handler(signal_type: signal.Signals) -> None:
|
||
|
logging.info(f"Received exit signal {signal.name}...")
|
||
|
|
||
|
await coro_func()
|
||
|
|
||
|
tasks = [t for t in asyncio.all_tasks() if t is not
|
||
|
asyncio.current_task()]
|
||
|
|
||
|
[task.cancel() for task in tasks]
|
||
|
|
||
|
logging.info(f"Cancelling {len(tasks)} outstanding tasks")
|
||
|
await asyncio.gather(*tasks)
|
||
|
|
||
|
loop = asyncio.get_event_loop()
|
||
|
loop.stop()
|
||
|
loop.close()
|
||
|
|
||
|
|
||
|
def _signal_handler(signal_type: signal.Signals, loop: asyncio.AbstractEventLoop) -> None:
|
||
|
print(f"Signal {signal_type.name} received.")
|
||
|
asyncio.create_task(_async_signal_handler(signal_type))
|
||
|
|
||
|
return _signal_handler
|
||
|
|
||
|
|
||
|
async def main(motor: BLDCMotor):
|
||
|
# In case the controller had faulted previously, at the start of
|
||
|
# this script we send the stop command in order to clear it.
|
||
|
await motor.set_stop()
|
||
|
|
||
|
while True:
|
||
|
# `set_position` accepts an optional keyword argument for each
|
||
|
# possible position mode register as described in the moteus
|
||
|
# reference manual. If a given register is omitted, then that
|
||
|
# register is omitted from the command itself, with semantics
|
||
|
# as described in the reference manual.
|
||
|
#
|
||
|
# The return type of 'set_position' is a moteus.Result type.
|
||
|
# It has a __repr__ method, and has a 'values' field which can
|
||
|
# be used to examine individual result registers.
|
||
|
state = await motor.set_position(position=math.nan, query=True)
|
||
|
|
||
|
# Print out everything.
|
||
|
print(state)
|
||
|
|
||
|
# Print out just the position register.
|
||
|
print("Position:", state.values[moteus.Register.POSITION])
|
||
|
|
||
|
# And a blank line so we can separate one iteration from the
|
||
|
# next.
|
||
|
print()
|
||
|
|
||
|
# Wait 20ms between iterations. By default, when commanded
|
||
|
# over CAN, there is a watchdog which requires commands to be
|
||
|
# sent at least every 100ms or the controller will enter a
|
||
|
# latched fault state.
|
||
|
await asyncio.sleep(0.02)
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
motor = BLDCMotor()
|
||
|
|
||
|
loop = asyncio.new_event_loop()
|
||
|
signal_handler = create_async_signal_handler(motor.set_stop)
|
||
|
|
||
|
# Register signal handlers for SIGNINT(Keyboard interrupt) and SIGTERM (Termination)
|
||
|
# for sig in (signal.SIGINT, signal.SIGTERM):
|
||
|
for sig in (signal.SIGINT):
|
||
|
loop.add_signal_handler(sig, signal_handler, sig, loop)
|
||
|
|
||
|
try:
|
||
|
loop.run_until_complete(main(motor))
|
||
|
finally:
|
||
|
loop.close()
|