Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bdt] Switch to dcargs #69

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 12 additions & 59 deletions bd_tools/src/bd_tools/__main__.py
Original file line number Diff line number Diff line change
@@ -1,70 +1,23 @@
"""The betz drive tools package."""
import argparse
import sys
from pathlib import Path

import bd_tools

BIN_PATH = Path(__file__).parent / "bin"
import dataclasses
from typing import Union

import dcargs

def get_tools():
"""Returns all scripts in the bin directory"""
return [tool.stem for tool in BIN_PATH.glob("[!__]*.py")]


def parser_args(tools):
parser = argparse.ArgumentParser(description="BetzDrive Tools Package")
parser.add_argument(
"tool",
type=str,
choices=tools,
help="Tool from the following: %(choices)s",
metavar="tool",
)
return parser
import bd_tools


def action(args):
file_name = BIN_PATH / (args.tool + ".py")
# NOTE(greg): Shifts argv down one (and deletes the 0th arg) so the
# sub-tool does not see its own name as the 1st arg.
sys.argv = sys.argv[1:]
# Correctly make arg0 the path to the file we're executing.
sys.argv[0] = str(file_name)
tool = getattr(bd_tools.bin, args.tool)
tool.action(tool.parser_args())
def cli_main(
cmd: Union[
bd_tools.bin.calibrate_encoder.Calibrate,
bd_tools.bin.control_motor.Control,
]
):
cmd.main()


def main():
# NOTE(greg): We have to hack the help to make sure it only operates on
# this argparser if its the first arg.
tool_help = False
if "-h" in sys.argv or "--help" in sys.argv:
if not (sys.argv[1] == "-h" or sys.argv[1] == "--help"):
tool_help = True
if "-h" in sys.argv:
sys.argv.remove("-h")
if "--help" in sys.argv:
sys.argv.remove("--help")

# Need to cache args to sub-command so the parser doesn't see them as
# "unrecognized arguments"
cache_args = []
if len(sys.argv) > 2:
cache_args = sys.argv[2:]
sys.argv = sys.argv[:2]

args = parser_args(get_tools()).parse_args()

sys.argv.extend(cache_args)

# If we requested a tool help, inject it back into the sys args for the
# sub-tool to process.
if tool_help:
sys.argv.append("--help")

action(args)
dcargs.cli(cli_main)


if __name__ == "__main__":
Expand Down
21 changes: 20 additions & 1 deletion bd_tools/src/bd_tools/bin/calibrate_encoder.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
#!/usr/bin/env python3

import argparse
import dataclasses
import json
import struct
import time

import dcargs
import matplotlib.pyplot as plt
import numpy as np
import serial
Expand All @@ -23,6 +24,24 @@
]


@dataclasses.dataclass
class Calibrate:
"""Calibrate the encoder on a motor controller board.

Args:
duty_cycle: fixed duty cycle through the motor for feed forward control
during calibration.
max_steps: maximum number of steps before giving up (should be greater
than or equal to erevs/mrev * 6).
delay: time between stepping phases.
"""

serial: boards.Serial
duty_cycle: dcargs.conf.Positional[float]
max_steps: int = 126
delay: float = 0.05


def parser_args():
parser = argparse.ArgumentParser(
description="Calibrate the encoder on a motor controller board."
Expand Down
21 changes: 20 additions & 1 deletion bd_tools/src/bd_tools/bin/control_motor.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,29 @@

import argparse
import ast
import dataclasses

import serial

from bd_tools import boards, comms, utils


@dataclasses.dataclass
class Control:
"""Drive motor module(s) with a given control mode.

Args:
num_iters: Number of iterations to loop (default to infinity).
"""

serial: boards.Serial
motor: boards.Motor
num_iters: int = 0

def main(self):
action(self)


def parser_args():
parser = argparse.ArgumentParser(
description="Drive motor module(s) with a given control mode."
Expand All @@ -35,7 +52,9 @@ def make_list(x):
def make_type(x, to_type):
return [to_type(y) for y in x]

board_ids = make_type(make_list(ast.literal_eval(args.board_ids)), int)
board_ids = make_type(
make_list(ast.literal_eval(args.motor.board_ids)), int
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this still needed? since board_ids is annotated as List[int] now

)
actuations = make_list(ast.literal_eval(args.actuations))

mode = args.mode
Expand Down
45 changes: 44 additions & 1 deletion bd_tools/src/bd_tools/boards.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
from __future__ import print_function

import dataclasses
import struct
import time
from typing import List, Literal, Tuple, Union

import dcargs

from bd_tools import comms

Expand All @@ -18,6 +22,45 @@
COMM_ROR_ROTOR_POS_RAW = 0x3010


@dataclasses.dataclass
class Serial:
"""Serial args.

Args:
serial: Serial port that the motor controller is connected to.
board_ids: List of motor controllers to talk with.
baud_rate: communication frequency over serial connection.
"""

serial: dcargs.conf.Positional[str]
board_ids: dcargs.conf.Positional[List[int]]
baud_rate: int = comms.COMM_DEFAULT_BAUD_RATE


@dataclasses.dataclass
class Motor:
"""Motor args.

Args:
actuation: Methods to actuate the motor.
current (Id[A], Iq[A])
phase (dc,dc,dc)
torque (N*m)
velocity (rad/s)
position (rad)
pos_vel (rad,rad/s)
pos_ff (rad,ff[A])
pwm (dc)
values: list of values for per-motor actuation. Should match the number
of args listed under the method of actuation.
"""

actuation: dcargs.conf.Positional[Literal["current", "phase",
"torque", "velocity", "position",
"pos_vel", "pos_ff", "pwm"]]
values: dcargs.conf.Positional[List[Tuple[float, float, float]]]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in general, variable-length positional arguments are scary because it means you can't have add more positional args afterward



def addBoardArgs(parser):
parser.add_argument("serial", type=str, help="Serial port")
parser.add_argument("--baud_rate", type=int, help="Serial baud rate")
Expand Down Expand Up @@ -96,7 +139,7 @@ def loadCalibrationFromJSON(client, board_id, calibration_obj):
eac_table_len = len(calibration_obj["eac_table"])
slice_len = 64
for i in range(0, eac_table_len, slice_len):
table_slice = calibration_obj["eac_table"][i : i + slice_len]
table_slice = calibration_obj["eac_table"][i: i + slice_len]
client.writeRegisters(
[board_id],
[0x1200 + i],
Expand Down