Skip to content

Commit ccdb378

Browse files
committed
Renamed motor pins to standand. Expanded brake/coast and exposed it to DifferentialDrive
1 parent 3495695 commit ccdb378

File tree

11 files changed

+74
-33
lines changed

11 files changed

+74
-33
lines changed

XRPLib/board.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ def get_default_board(cls):
1313
Get the default board instance. This is a singleton, so only one instance of the board will ever exist.
1414
"""
1515
if cls._DEFAULT_BOARD_INSTANCE is None:
16-
cls._DEFAULT_BOARD_INSTANCE = cls("VIN_MEAS","USER_BUTTON")
16+
cls._DEFAULT_BOARD_INSTANCE = cls()
1717
return cls._DEFAULT_BOARD_INSTANCE
1818

19-
def __init__(self, vin_pin:int, button_pin:int):
19+
def __init__(self, vin_pin="BOARD_VIN_MEASURE", button_pin="BOARD_USER_BUTTON",
20+
rgb_led_pin = "BOARD_NEOPIXEL", led_pin = "BOARD_LED"):
2021
"""
2122
Implements for extra features on the XRP v2 board. Handles the on/off switch, button, and LED.
2223
@@ -30,10 +31,10 @@ def __init__(self, vin_pin:int, button_pin:int):
3031

3132
self.button = Pin(button_pin, Pin.IN, Pin.PULL_UP)
3233

33-
self.led = Pin("LED", Pin.OUT)
34+
self.led = Pin(led_pin, Pin.OUT)
3435

3536
if "RP2350" in sys.implementation._machine:
36-
self.rgb_led = NeoPixel(Pin("RGB_LED", Pin.OUT), 1)
37+
self.rgb_led = NeoPixel(Pin(rgb_led_pin, Pin.OUT), 1)
3738
# A timer ID of -1 is a virtual timer.
3839
# Leaves the hardware timers for more important uses
3940
self._virt_timer = Timer(-1)

XRPLib/differential_drive.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ def __init__(self, left_motor: EncodedMotor, right_motor: EncodedMotor, imu: IMU
4646
self.right_motor = right_motor
4747
self.imu = imu
4848

49+
self.brake_at_zero_power = False
4950
self.wheel_diam = wheel_diam
5051
self.track_width = wheel_track
5152

@@ -76,9 +77,19 @@ def set_speed(self, left_speed: float, right_speed: float) -> None:
7677
self.left_motor.set_speed(left_speed*cmpsToRPM)
7778
self.right_motor.set_speed(right_speed*cmpsToRPM)
7879

80+
def set_zero_effort_behavior(self, brake_at_zero_effort):
81+
82+
"""
83+
Sets the behavior of both motor at 0 effort to either brake (hold position) or coast (free spin)
84+
:param brake_at_zero_effort: Whether or not to brake at 0 effort. Can use EncodedMotor.ZERO_EFFORT_BREAK or EncodedMotor.ZERO_EFFORT_COAST for clarity.
85+
:type brake_at_zero_effort: bool
86+
"""
87+
self.left_motor.set_zero_effort_behavior(brake_at_zero_effort)
88+
self.right_motor.set_zero_effort_behavior(brake_at_zero_effort)
89+
7990
def stop(self) -> None:
8091
"""
81-
Stops both drivetrain motors
92+
Stops both drivetrain motors by setting power to zero.
8293
"""
8394
self.left_motor.set_speed()
8495
self.right_motor.set_speed()

XRPLib/encoded_motor.py

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77

88
class EncodedMotor:
99

10+
ZERO_EFFORT_BREAK = True
11+
ZERO_EFFORT_COAST = False
12+
1013
_DEFAULT_LEFT_MOTOR_INSTANCE = None
1114
_DEFAULT_RIGHT_MOTOR_INSTANCE = None
1215
_DEFAULT_MOTOR_THREE_INSTANCE = None
@@ -30,29 +33,29 @@ def get_default_encoded_motor(cls, index:int = 1):
3033
if index == 1:
3134
if cls._DEFAULT_LEFT_MOTOR_INSTANCE is None:
3235
cls._DEFAULT_LEFT_MOTOR_INSTANCE = cls(
33-
MotorImplementation("MOTOR_L_IN1", "MOTOR_L_IN2", flip_dir=True),
34-
Encoder(0, "ENCODER_LA", "ENCODER_LB")
36+
MotorImplementation("MOTOR_L_IN_1", "MOTOR_L_IN_2", flip_dir=True),
37+
Encoder(0, "MOTOR_L_ENCODER_A", "MOTOR_L_ENCODER_B")
3538
)
3639
motor = cls._DEFAULT_LEFT_MOTOR_INSTANCE
3740
elif index == 2:
3841
if cls._DEFAULT_RIGHT_MOTOR_INSTANCE is None:
3942
cls._DEFAULT_RIGHT_MOTOR_INSTANCE = cls(
40-
MotorImplementation("MOTOR_R_IN1", "MOTOR_R_IN2"),
41-
Encoder(1, "ENCODER_RA", "ENCODER_RB")
43+
MotorImplementation("MOTOR_R_IN_1", "MOTOR_R_IN_2"),
44+
Encoder(1, "MOTOR_R_ENCODER_A", "MOTOR_R_ENCODER_A")
4245
)
4346
motor = cls._DEFAULT_RIGHT_MOTOR_INSTANCE
4447
elif index == 3:
4548
if cls._DEFAULT_MOTOR_THREE_INSTANCE is None:
4649
cls._DEFAULT_MOTOR_THREE_INSTANCE = cls(
47-
MotorImplementation("MOTOR_3_IN1", "MOTOR_3_IN2", flip_dir=True),
48-
Encoder(2, "ENCODER_3A", "ENCODER_3B")
50+
MotorImplementation("MOTOR_3_IN_1", "MOTOR_3_IN_2", flip_dir=True),
51+
Encoder(2, "MOTOR_3_ENCODER_A", "MOTOR_3_ENCODER_A")
4952
)
5053
motor = cls._DEFAULT_MOTOR_THREE_INSTANCE
5154
elif index == 4:
5255
if cls._DEFAULT_MOTOR_FOUR_INSTANCE is None:
5356
cls._DEFAULT_MOTOR_FOUR_INSTANCE = cls(
54-
MotorImplementation("MOTOR_4_IN1", "MOTOR_4_IN2"),
55-
Encoder(3, "ENCODER_4A", "ENCODER_4B")
57+
MotorImplementation("MOTOR_4_IN_1", "MOTOR_4_IN_2"),
58+
Encoder(3, "MOTOR_4_ENCODER_A", "MOTOR_4_ENCODER_A")
5659
)
5760
motor = cls._DEFAULT_MOTOR_FOUR_INSTANCE
5861
else:
@@ -64,6 +67,8 @@ def __init__(self, motor: Motor, encoder: Encoder):
6467
self._motor = motor
6568
self._encoder = encoder
6669

70+
self.brake_at_zero = False
71+
6772
self.target_speed = None
6873
self.DEFAULT_SPEED_CONTROLLER = PID(
6974
kp=0.035,
@@ -78,12 +83,38 @@ def __init__(self, motor: Motor, encoder: Encoder):
7883
# If the update timer is not running, start it at 50 Hz (20ms updates)
7984
self.updateTimer.init(period=20, callback=lambda t:self._update())
8085

86+
8187
def set_effort(self, effort: float):
8288
"""
8389
:param effort: The effort to set this motor to, from -1 to 1
8490
:type effort: float
8591
"""
86-
self._motor.set_effort(effort)
92+
if self.brake_at_zero and effort == 0:
93+
self.brake()
94+
else:
95+
self._motor.set_effort(effort)
96+
97+
# EncodedMotor.set_zero_effort_behavior(EncodedMotor.ZERO_POWER_BRAKE)
98+
def set_zero_effort_behavior(self, brake_at_zero_effort):
99+
"""
100+
Sets the behavior of the motor at 0 effort to either brake (hold position) or coast (free spin)
101+
:param brake_at_zero_effort: Whether or not to brake at 0 effort. Can use EncodedMotor.ZERO_EFFORT_BREAK or EncodedMotor.ZERO_EFFORT_COAST for clarity.
102+
:type brake_at_zero_effort: bool
103+
"""
104+
self.brake_at_zero = brake_at_zero_effort
105+
106+
def brake(self):
107+
"""
108+
Causes the motor to resist rotation.
109+
"""
110+
# Exact impl of brake depends on which board is being used.
111+
self._motor.brake()
112+
113+
def coast(self):
114+
"""
115+
Allows the motor to spin freely.
116+
"""
117+
self._motor.coast()
87118

88119
def get_position(self) -> float:
89120
"""

XRPLib/encoder.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ class Encoder:
99
_counts_per_motor_shaft_revolution = 12
1010
resolution = _counts_per_motor_shaft_revolution * _gear_ratio # 585
1111

12-
def __init__(self, index, encAPin, encBPin):
12+
def __init__(self, index, encAPin: int|str, encBPin: int|str):
1313
"""
1414
Uses the on board PIO State Machine to keep track of encoder positions.
1515
Only 4 encoders can be instantiated this way.

XRPLib/imu.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,11 @@ def get_default_imu(cls):
2323
"""
2424

2525
if cls._DEFAULT_IMU_INSTANCE is None:
26-
cls._DEFAULT_IMU_INSTANCE = cls(
27-
scl_pin="SCL1",
28-
sda_pin="SDA1",
29-
addr=LSM_ADDR_PRIMARY
30-
)
26+
cls._DEFAULT_IMU_INSTANCE = cls()
3127
cls._DEFAULT_IMU_INSTANCE.calibrate()
3228
return cls._DEFAULT_IMU_INSTANCE
3329

34-
def __init__(self, scl_pin: int, sda_pin: int, addr):
30+
def __init__(self, scl_pin: int|str = "I2C_SCL_1", sda_pin: int|str = "I2C_SDA_1", addr=LSM_ADDR_PRIMARY):
3531
# I2C values
3632
self.i2c = I2C(id=1, scl=Pin(scl_pin), sda=Pin(sda_pin), freq=400000)
3733
self.addr = addr

XRPLib/motor.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ class SinglePWMMotor:
88
This version is used for the beta version of the XRP, which uses the rp2040 processor
99
"""
1010

11-
def __init__(self, in1_direction_pin: int, in2_speed_pin: int, flip_dir:bool=False):
11+
def __init__(self, in1_direction_pin: int|str, in2_speed_pin: int|str, flip_dir:bool=False):
1212
self.flip_dir = flip_dir
1313
self._MAX_PWM = 65534 # Motor holds when actually at full power
1414

@@ -45,7 +45,7 @@ def brake(self):
4545
self._in2SpeedPin.duty_u16(self._MAX_PWM+1)
4646

4747
def coast(self):
48-
raise NotImplementedError("Motor.coast is not implemented for not implemented for the XRP RP2040")
48+
self.set_effort(0)
4949

5050
class DualPWMMotor:
5151
"""
@@ -54,7 +54,7 @@ class DualPWMMotor:
5454
This version of the Motor class is used
5555
"""
5656

57-
def __init__(self, in1_pwm_forward: int, in2_pwm_backward: int, flip_dir:bool=False):
57+
def __init__(self, in1_pwm_forward: int|str, in2_pwm_backward: int|str, flip_dir:bool=False):
5858
self.flip_dir = flip_dir
5959
self._MAX_PWM = 65535 # Motor holds when actually at full power
6060

XRPLib/rangefinder.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ def get_default_rangefinder(cls):
1111
Get the default XRP v2 rangefinder instance. This is a singleton, so only one instance of the rangefinder will ever exist.
1212
"""
1313
if cls._DEFAULT_RANGEFINDER_INSTANCE is None:
14-
cls._DEFAULT_RANGEFINDER_INSTANCE = cls("DIST_TRIG", "DIST_ECHO")
14+
cls._DEFAULT_RANGEFINDER_INSTANCE = cls()
1515
return cls._DEFAULT_RANGEFINDER_INSTANCE
1616

17-
def __init__(self, trigger_pin:int, echo_pin:int, timeout_us:int=500*2*30):
17+
def __init__(self, trigger_pin: int|str = "RANGE_TRIGGER", echo_pin: int|str = "RANGE_ECHO", timeout_us:int=500*2*30):
1818
"""
1919
A basic class for using the HC-SR04 Ultrasonic Rangefinder.
2020
The sensor range is between 2cm and 4m.

XRPLib/reflectance.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ def get_default_reflectance(cls):
1212
Get the default XRP v2 reflectance sensor instance. This is a singleton, so only one instance of the reflectance sensor will ever exist.
1313
"""
1414
if cls._DEFAULT_REFLECTANCE_INSTANCE is None:
15-
cls._DEFAULT_REFLECTANCE_INSTANCE = cls("LINE_L", "LINE_R")
15+
cls._DEFAULT_REFLECTANCE_INSTANCE = cls()
1616
return cls._DEFAULT_REFLECTANCE_INSTANCE
1717

18-
def __init__(self, leftPin:int, rightPin:int):
18+
def __init__(self, leftPin: int|str = "LINE_L", rightPin: int|str = "LINE_R"):
1919
"""
2020
Implements for a reflectance sensor using the built in 12-bit ADC.
2121
Reads from analog in and converts to a float from 0 (white) to 1 (black)

XRPLib/resetbot.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ def reset_led():
1616
from XRPLib.board import Board
1717
# Turn off the on-board LED
1818
Board.get_default_board().led_off()
19+
try:
20+
# Turn off the RGB LED for boards that have it
21+
Board.get_default_board().set_rgb_led(0, 0, 0)
22+
except:
23+
pass
1924

2025
def reset_servos():
2126
from XRPLib.servo import Servo

XRPLib/servo.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@ def get_default_servo(cls, index:int):
3434
return Exception("Invalid servo index")
3535
return servo
3636

37-
def __init__(self, signal_pin:int):
37+
def __init__(self, signal_pin: int|str):
3838
"""
3939
A simple class for interacting with a servo through PWM
4040
4141
:param signal_pin: The pin the servo is connected to
42-
:type signal_pin: int
42+
:type signal_pin: int | str
4343
"""
4444

4545
self._servo = PWM(Pin(signal_pin, Pin.OUT))

0 commit comments

Comments
 (0)