forked from thedropbears/pysteamworks
/
robot.py
executable file
·336 lines (290 loc) · 12.4 KB
/
robot.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
#!/usr/bin/env python3
import magicbot
import wpilib
from wpilib import XboxController
from ctre import CANTalon
from components.range_finder import RangeFinder
from components.chassis import Chassis
from components.bno055 import BNO055
from components.gearalignmentdevice import GearAlignmentDevice
from components.geardepositiondevice import GearDepositionDevice
from components.vision import Vision
from components.winch import Winch
from automations.manipulategear import ManipulateGear
from automations.profilefollower import ProfileFollower
from automations.winchautomation import WinchAutomation
from automations.vision_filter import VisionFilter
from automations.range_filter import RangeFilter
from utilities.profilegenerator import generate_trapezoidal_trajectory
from networktables import NetworkTable
import logging
import math
import time
class Robot(magicbot.MagicRobot):
chassis = Chassis
gearalignmentdevice = GearAlignmentDevice
geardepositiondevice = GearDepositionDevice
winch_automation = WinchAutomation
manipulategear = ManipulateGear
vision = Vision
winch = Winch
profilefollower = ProfileFollower
range_finder = RangeFinder
vision_filter = VisionFilter
range_filter = RangeFilter
def createObjects(self):
'''Create motors and stuff here'''
# Objects that are created here are shared with all classes
# that declare them. For example, if I had:
# self.elevator_motor = wpilib.TalonSRX(2)
# here, then I could have
# class Elevator:
# elevator_motor = wpilib.TalonSRX
# and that variable would be available to both the MyRobot
# class and the Elevator class. This "variable injection"
# is especially useful if you want to certain objects with
# multiple different classes.
# create the imu object
self.bno055 = BNO055()
# the "logger" - allows you to print to the logging screen
# of the control computer
self.logger = logging.getLogger("robot")
# the SmartDashboard network table allows you to send
# information to a html dashboard. useful for data display
# for drivers, and also for plotting variables over time
# while debugging
self.sd = NetworkTable.getTable('SmartDashboard')
# boilerplate setup for the joystick
self.joystick = wpilib.Joystick(0)
self.gamepad = XboxController(1)
self.pressed_buttons_js = set()
self.pressed_buttons_gp = set()
self.drive_motor_a = CANTalon(2)
self.drive_motor_b = CANTalon(5)
self.drive_motor_c = CANTalon(4)
self.drive_motor_d = CANTalon(3)
self.gear_alignment_motor = CANTalon(14)
self.winch_motor = CANTalon(11)
self.winch_motor.setInverted(True)
self.rope_lock_solenoid = wpilib.DoubleSolenoid(forwardChannel=0,
reverseChannel=1)
# self.rope_lock_solenoid = wpilib.DoubleSolenoid(forwardChannel=3,
# reverseChannel=4)
self.gear_push_solenoid = wpilib.Solenoid(2)
self.gear_drop_solenoid = wpilib.Solenoid(3)
# self.gear_push_solenoid = wpilib.DoubleSolenoid(forwardChannel=1, reverseChannel=2)
# self.gear_drop_solenoid = wpilib.Solenoid(0)
self.test_trajectory = generate_trapezoidal_trajectory(
0, 0, 3, 0, Chassis.max_vel, 1, -1, Chassis.motion_profile_freq)
self.throttle = 1.0
self.direction = 1.0
self.led_dio = wpilib.DigitalOutput(1)
self.compressor = wpilib.Compressor()
def putData(self):
# update the data on the smart dashboard
# put the inputs to the dashboard
self.sd.putNumber("gyro", self.bno055.getHeading())
self.sd.putNumber("vision_x", self.vision.x)
# if self.manipulategear.current_state == "align_peg":
self.sd.putNumber("range", self.range_finder.getDistance())
self.sd.putNumber("climbCurrent", self.winch_motor.getOutputCurrent())
self.sd.putNumber("rail_pos", self.gearalignmentdevice.get_rail_pos())
self.sd.putNumber("error_differential",
self.drive_motor_a.getClosedLoopError()-
self.drive_motor_c.getClosedLoopError())
self.sd.putNumber("velocity", self.chassis.get_velocity())
self.sd.putNumber("left_speed_error", self.drive_motor_a.getClosedLoopError())
self.sd.putNumber("right_speed_error", self.drive_motor_c.getClosedLoopError())
self.sd.putNumber("x_throttle", self.chassis.inputs[0])
self.sd.putNumber("z_throttle", self.chassis.inputs[2])
self.sd.putNumber("filtered_x", self.vision_filter.x)
self.sd.putNumber("filtered_dx", self.vision_filter.dx)
self.sd.putNumber("vision_filter_x_variance", self.vision_filter.filter.P[0][0])
self.sd.putNumber("vision_filter_dx_variance", self.vision_filter.filter.P[1][1])
self.sd.putNumber("vision_filter_covariance", self.vision_filter.filter.P[0][1])
self.sd.putNumber("filtered_range", self.range_filter.filter.x_hat[0][0])
self.sd.putNumber("range_filter_variance", self.range_filter.filter.P[0][0])
self.sd.putNumber("time", time.time())
def teleopInit(self):
'''Called when teleop starts; optional'''
self.sd.putString("state", "stationary")
self.gearalignmentdevice.reset_position()
self.geardepositiondevice.retract_gear()
self.geardepositiondevice.lock_gear()
self.profilefollower.stop()
self.winch.enable_compressor()
self.vision.enabled = False
print("TELEOP INIT RANGE: %s" % (self.range_finder.getDistance()))
print("TELEOP INIT FILTER RANGE: %s" % (self.range_filter.range))
def autonomousPeriodic(self):
self.putData()
def disabledInit(self):
self.sd.putBoolean("log", False)
def disabledPeriodic(self):
self.putData()
self.sd.putString("state", "stationary")
self.vision_filter.execute()
self.range_filter.execute()
def teleopPeriodic(self):
'''Called on each iteration of the control loop'''
self.putData()
try:
if self.debounce(8, gamepad=True) or self.debounce(1):
self.manipulategear.engage(force=True)
except:
self.onException()
try:
if self.debounce(7, gamepad=True) or self.debounce(3):
self.winch_automation.engage(force=True)
except:
self.onException()
try:
if self.debounce(7):
self.sd.putBoolean("log", True)
except:
self.onException()
# try:
# if self.debounce(10):
# # stop the winch
# if self.winch_automation.is_executing:
# self.winch_automation.done()
# self.winch.piston_open()
# self.winch.rotate_winch(0)
# if self.manipulategear.is_executing:
# self.manipulategear.done()
# self.gearalignmentdevice.reset_position()
# self.sd.putString("state", "stationary")
# except:
# self.onException()
try:
if self.debounce(2):
if self.manipulategear.is_executing:
self.manipulategear.done()
self.gearalignmentdevice.reset_position()
self.geardepositiondevice.retract_gear()
self.geardepositiondevice.lock_gear()
except:
self.onException()
try:
if self.debounce(4):
if self.winch_automation.is_executing:
self.winch_automation.done()
self.winch.rotate_winch(0)
except:
self.onException()
try:
if self.debounce(5):
if self.winch_automation.is_executing:
self.winch_automation.done()
self.winch.rotate_winch(1.0)
self.winch.piston_close()
except:
self.onException()
try:
if self.debounce(6):
self.winch.locked = not self.winch.locked
except:
self.onException()
try:
if self.debounce(12):
self.geardepositiondevice.retract_gear()
self.geardepositiondevice.lock_gear()
except:
self.onException()
try:
if self.debounce(10):
# self.geardepositiondevice.push_gear()
# self.geardepositiondevice.drop_gear()
self.manipulategear.engage(initial_state="forward_closed", force=True)
except:
self.onException()
try:
if self.debounce(12):
self.geardepositiondevice.retract_gear()
self.geardepositiondevice.lock_gear()
except:
self.onException()
if (not self.gamepad.getRawButton(5) and
not self.gamepad.getRawButton(6) and
not self.gamepad.getRawAxis(3) > 0.9):
self.throttle = 1
self.direction = 1
self.sd.putString("camera", "front")
elif self.gamepad.getRawButton(5):
# reverse
self.throttle = 1
self.direction = -1
self.sd.putString("camera", "back")
elif self.gamepad.getRawButton(6):
# slow down
self.throttle = 0.3
self.direction = 1
self.sd.putString("camera", "front")
elif self.gamepad.getRawAxis(3) > 0.9:
self.throttle = 0.3
self.direction = -1
self.sd.putString("camera", "back")
if self.joystick.getPOV() == 90:
if not self.manipulategear.is_executing:
self.gearalignmentdevice.move_right()
elif self.joystick.getPOV() == 270:
if not self.manipulategear.is_executing:
self.gearalignmentdevice.move_left()
# elif self.joystick.getPOV() == 0 or self.joystick.getPOV() == 180:
# if not self.manipulategear.is_executing:
# self.gearalignmentdevice.set_position(0)
if 1.5/self.chassis.velocity_to_native_units < abs(self.chassis.get_velocity()) and not self.manipulategear.is_executing:
self.gearalignmentdevice.set_position(0)
self.chassis.inputs = [(
self.direction
* -rescale_js(self.gamepad.getRawAxis(1), deadzone=0.05, exponential=30)),
- rescale_js(self.joystick.getX(), deadzone=0.05, exponential=1.2),
-rescale_js(self.gamepad.getRawAxis(4), deadzone=0.05, exponential=30, rate=0.6 if self.throttle == 1 else 1),
self.throttle
]
# self.chassis.inputs = [(
# self.direction
# * -rescale_js(self.gamepad.getRawAxis(1), rate=0.3, deadzone=0.05, exponential=15)),
# - rescale_js(self.joystick.getX(), deadzone=0.05, exponential=1.2),
# -rescale_js(self.gamepad.getRawAxis(4), deadzone=0.05, exponential=15.0, rate=0.2),
# self.throttle
# ]
self.vision.led_on = self.joystick.getRawButton(11)
# the 'debounce' function keeps tracks of which buttons have been pressed
def debounce(self, button, gamepad=False):
device = None
if gamepad:
pressed_buttons = self.pressed_buttons_gp
device = self.gamepad
else:
pressed_buttons = self.pressed_buttons_js
device = self.joystick
if device.getRawButton(button):
if button in pressed_buttons:
return False
else:
pressed_buttons.add(button)
return True
else:
pressed_buttons.discard(button)
return False
# see comment in teleopPeriodic for information
def rescale_js(value, deadzone=0.0, exponential=0.0, rate=1.0):
value_negative = 1.0
if value < 0:
value_negative = -1.0
value = -value
# Cap to be +/-1
if abs(value) > 1.0:
value /= abs(value)
# Apply deadzone
if abs(value) < deadzone:
return 0.0
elif exponential == 0.0:
value = (value - deadzone) / (1 - deadzone)
else:
a = math.log(exponential + 1) / (1 - deadzone)
value = (math.exp(a * (value - deadzone)) - 1) / exponential
return value * value_negative * rate
if __name__ == '__main__':
wpilib.run(Robot)