Skip to content

Commit fd0a48c

Browse files
committed
copy of code from 2017
1 parent 4d36e8a commit fd0a48c

File tree

8 files changed

+362
-2
lines changed

8 files changed

+362
-2
lines changed

README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,6 @@
1-
# 2018
2-
3299's code for the 2018 season
1+
# Steamworks
2+
3299's code for the 2017 season. Programmed in Python with [RobotPy](https://robotpy.github.io).
3+
4+
## Notable features
5+
- A Raspberry Pi on the robot does vision tracking with OpenCV and [GRIP](https://github.com/WPIRoboticsProjects/GRIP). It then publishes the results on Network Tables for the robot to use.
6+
- Organized in a convoluted system that separates logic, actuators, and other components. It uses neither MagicBot or the Command framework. Don't ask.

__init__.py

Whitespace-only changes.

autonomous.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
"""
2+
Runs the auto routine. Called once.
3+
"""
4+
from networktables import NetworkTable
5+
import time
6+
import wpilib
7+
8+
class Autonomous(object):
9+
def __init__(self, drive, randy, frontGear, backGear, vision):
10+
self.drive = drive
11+
self.randy = randy
12+
self.frontGear = frontGear
13+
self.backGear = backGear
14+
self.vision = vision
15+
self.sd = NetworkTable.getTable('SmartDashboard')
16+
17+
def run(self):
18+
self.randy.run(True) # deploy Randy
19+
20+
if (self.sd.getBoolean('autoAngle') == True):
21+
# Drive forward
22+
startTime = time.clock()
23+
while (time.clock() - startTime < 2.3):
24+
self.drive.driveToAngle(-0.65, 0, True)
25+
26+
# Stop
27+
self.drive.cartesian(0, 0, 0)
28+
29+
# Turn 60 or -60 degrees
30+
if (self.sd.getBoolean('isLeft') == True):
31+
self.drive.driveToAngle(0, -60, False)
32+
else:
33+
self.drive.driveToAngle(0, 60, False)
34+
35+
# Do vision
36+
if (self.vision.alignToPeg(direction=1) == False): # if returns an error, stop auto
37+
return False
38+
39+
# Stop
40+
self.drive.cartesian(0, 0, 0)
41+
42+
'''
43+
Center peg
44+
'''
45+
startTime = time.clock()
46+
while (time.clock() - startTime < 5.2):
47+
self.drive.cartesian(0, -0.3, 0.025)
48+
self.drive.cartesian(0, 0, 0)
49+
50+
# Activate front gear
51+
self.frontGear.run(True)
52+
53+
# Stop Randy
54+
self.randy.run(False)

cameras.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
'''
2+
Launches the camera streamers in a seperate
3+
thread to prevent the main robot thread
4+
from being overloaded.
5+
'''
6+
7+
from cscore import CameraServer
8+
9+
def main():
10+
cs = CameraServer.getInstance()
11+
cs.enableLogging()
12+
13+
usb1 = cs.startAutomaticCapture(dev=0)
14+
15+
usb1.setResolution(320, 240)
16+
17+
cs.waitForever()

helpers.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
"""
2+
Provides various helper functions.
3+
"""
4+
5+
import math
6+
7+
def remap( x, oMin, oMax, nMin, nMax ): # thanks stackoverflow.com/a/15537393
8+
#range check
9+
if oMin == oMax:
10+
print("Warning: Zero input range")
11+
return None
12+
13+
if nMin == nMax:
14+
print("Warning: Zero output range")
15+
return None
16+
17+
#check reversed input range
18+
reverseInput = False
19+
oldMin = min( oMin, oMax )
20+
oldMax = max( oMin, oMax )
21+
if not oldMin == oMin:
22+
reverseInput = True
23+
24+
#check reversed output range
25+
reverseOutput = False
26+
newMin = min( nMin, nMax )
27+
newMax = max( nMin, nMax )
28+
if not newMin == nMin :
29+
reverseOutput = True
30+
31+
portion = (x-oldMin)*(newMax-newMin)/(oldMax-oldMin)
32+
if reverseInput:
33+
portion = (oldMax-x)*(newMax-newMin)/(oldMax-oldMin)
34+
35+
result = portion + newMin
36+
if reverseOutput:
37+
result = newMax - portion
38+
39+
return result
40+
41+
'''
42+
Given a value and power, this raises the value
43+
to the power, then negates it if the value was
44+
originally negative.
45+
'''
46+
47+
def raiseKeepSign(value, power):
48+
newValue = value ** power
49+
50+
if (value < 0 and newValue > 0):
51+
return newValue * -1
52+
else:
53+
return value ** power
54+
55+
'''
56+
Given a Cartesian x,y position, this 'snaps'
57+
it to the nearest angle, in degrees (the
58+
number of snappable angles is determined by
59+
`divisions`). Intended to be used with joystick
60+
values.
61+
'''
62+
def snap(divisions, x, y):
63+
if (x == 0):
64+
return 0
65+
66+
result = round(math.atan2(y, x) / (2 * math.pi / divisions) + divisions, 0) % divisions
67+
68+
return result * (360 / divisions)
69+
70+
'''
71+
Maps a value onto a sin curve. Made for
72+
the driving fuctions.
73+
'''
74+
def curve(value):
75+
return math.sin(value ** 2) * math.pi/2.6
76+
77+
'''
78+
Rotates a vector in Caresian space.
79+
'''
80+
def rotateVector(x, y, angle):
81+
angle = math.radians(angle)
82+
cosA = math.cos(angle)
83+
sinA = math.sin(angle)
84+
return (x * cosA - y * sinA), (x * sinA + y * cosA)
85+
86+
def normalizeAngle(angle):
87+
'''Normalize angle to [-180,180]'''
88+
return ((angle + 180) % 360) - 180.0

inits.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
"""
2+
Inits wpilib objects
3+
"""
4+
5+
import wpilib
6+
from map import Map
7+
8+
class Component(object):
9+
def __init__(self):
10+
# Mapping object stores port numbers for all connected motors, sensors, and joysticks. See map.py.
11+
Mapping = Map()
12+
13+
# Init drivetrain
14+
self.driveTrain = {'frontLeft': wpilib.Spark(Mapping.frontLeftM), 'backLeft': wpilib.Spark(Mapping.backLeftM), 'frontRight': wpilib.Spark(Mapping.frontRightM), 'backRight': wpilib.Spark(Mapping.backRightM)}
15+
self.driveTrain['frontLeft'].setInverted(True)
16+
self.driveTrain['backLeft'].setInverted(True)
17+
18+
# Init other motors
19+
self.shooterM = wpilib.Spark(Mapping.shooterM)
20+
self.hopperM = wpilib.Spark(Mapping.hopperM)
21+
self.climbM = wpilib.Spark(Mapping.climbM)
22+
self.groundGearM = wpilib.Talon(Mapping.groundGearM)
23+
24+
# Init soleniods
25+
self.gearSol = wpilib.DoubleSolenoid(Mapping.gearSol['out'], Mapping.gearSol['in'])
26+
self.groundSol = wpilib.DoubleSolenoid(Mapping.groundGearSol['out'], Mapping.groundGearSol['in'])
27+
28+
# Init sensors
29+
self.gyroS = wpilib.ADXRS450_Gyro(Mapping.gyroS)
30+
self.allienceS = wpilib.DigitalInput(Mapping.allienceS)
31+
self.shooterS = wpilib.DigitalInput(Mapping.shooterS)
32+
self.hopperS = wpilib.DigitalInput(Mapping.hopperS)
33+
34+
# Init relays
35+
self.bumpPopR = wpilib.Relay(Mapping.bumpPopR)
36+
self.greenLEDR = wpilib.Relay(Mapping.greenLEDR)

map.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
"""
2+
Defines port numbers for motors and sensors.
3+
"""
4+
5+
class Map(object):
6+
def __init__(self):
7+
# Motors have suffix 'M'. All motors use PWM.
8+
self.frontLeftM = 3
9+
self.frontRightM = 1
10+
self.backLeftM = 7
11+
self.backRightM = 0
12+
13+
self.climbM = 2
14+
self.collectorM = 5
15+
self.shooterM = 9
16+
self.hopperM = 8
17+
self.groundGearM = 4
18+
19+
# Soleniods
20+
self.gearSol = {'in': 3, 'out': 4}
21+
self.groundGearSol = {'in': 2, 'out': 0}
22+
23+
# Sensors have suffix 'S'. Gyro uses SPI, everything else uses the DIO.
24+
self.gyroS = 0
25+
self.allienceS = 0
26+
self.shooterS = 1
27+
self.hopperS = 2
28+
29+
# Relays
30+
self.bumpPopR = 0
31+
self.greenLEDR = 1

robot.py

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
"""
2+
Main logic code
3+
"""
4+
import wpilib
5+
import time
6+
from networktables import NetworkTable
7+
8+
from inits import Component
9+
import helpers
10+
11+
from components.chassis import Chassis
12+
from components.gear import GearSol
13+
from components.climber import Climber
14+
from components.bumpPop import BumpPop
15+
from components.shooter import Shooter
16+
from components.groundGear import GroundGear
17+
18+
from components.vision import Vision
19+
from autonomous import Autonomous
20+
21+
class Randy(wpilib.SampleRobot):
22+
def robotInit(self):
23+
# init cameras
24+
wpilib.CameraServer.launch('cameras.py:main')
25+
26+
self.C = Component() # Components inits all connected motors, sensors, and joysticks. See inits.py.
27+
28+
# Setup subsystems
29+
self.drive = Chassis(self.C.driveTrain, self.C.gyroS)
30+
self.climb = Climber(self.C.climbM)
31+
self.bumpPop = BumpPop(self.C.bumpPopR)
32+
self.groundGear = GroundGear(self.C.groundSol, self.C.groundGearM)
33+
self.gearSol = GearSol(self.C.gearSol)
34+
self.shooter = Shooter(self.C.shooterM, self.C.hopperM, self.C.shooterS, self.C.hopperS)
35+
self.vision = Vision(self.drive, self.C.greenLEDR)
36+
self.autonomousRoutine = Autonomous(self.drive, self.bumpPop, self.gearSol, self.groundGear, self.vision)
37+
38+
# Smart Dashboard
39+
self.sd = NetworkTable.getTable('SmartDashboard')
40+
self.sd.putBoolean('autoAngle', False)
41+
self.sd.putBoolean('isLeft', False)
42+
43+
# Joysticks or xBox controller?
44+
self.controller = 'joysticks' # || joysticks
45+
46+
if (self.controller == 'joysticks'):
47+
self.C.leftJ = wpilib.Joystick(0)
48+
self.C.middleJ = wpilib.Joystick(1)
49+
self.C.rightJ = wpilib.Joystick(2)
50+
elif (self.controller == 'xbox'):
51+
self.C.joystick = wpilib.XboxController(0)
52+
53+
def operatorControl(self):
54+
# runs when robot is enabled
55+
while self.isOperatorControl() and self.isEnabled():
56+
'''
57+
Components
58+
'''
59+
# Drive
60+
if (self.controller == 'joysticks'):
61+
self.drive.run(self.C.leftJ.getX(),
62+
self.C.leftJ.getY(),
63+
self.C.middleJ.getX(),
64+
self.C.leftJ.getRawButton(4),
65+
self.C.leftJ.getRawButton(3),
66+
self.C.leftJ.getRawButton(5),
67+
self.C.leftJ.getRawButton(2))
68+
69+
elif (self.controller == 'xbox'):
70+
self.drive.arcade(self.C.joystick.getRawAxis(0), self.C.joystick.getRawAxis(1), self.C.joystick.getRawAxis(4))
71+
72+
# Back gear
73+
if (self.controller == 'joysticks'):
74+
if (self.C.middleJ.getRawButton(4)):
75+
self.groundGear.run(True, 'out')
76+
elif (self.C.middleJ.getRawButton(5)):
77+
self.groundGear.run(True, 'in')
78+
else:
79+
self.groundGear.run(False, False)
80+
81+
elif (self.controller == 'xbox'):
82+
if (self.C.joystick.getBumper(wpilib.GenericHID.Hand.kLeft)):
83+
self.groundGear.run(True, 'out')
84+
elif (self.C.joystick.getTriggerAxis(wpilib.GenericHID.Hand.kLeft) > 0.5):
85+
self.groundGear.run(True, 'in')
86+
else:
87+
self.groundGear.run(False, False)
88+
89+
# Front gear
90+
if (self.controller == 'joysticks'):
91+
if (self.C.middleJ.getRawButton(1) == True and self.C.middleJ.getRawButton(2) == True):
92+
self.gearSol.run(True)
93+
else:
94+
self.gearSol.run(False)
95+
96+
elif (self.controller == 'xbox'):
97+
if (self.C.joystick.getTriggerAxis(wpilib.GenericHID.Hand.kRight) > 0.5):
98+
self.gearSol.run(True)
99+
else:
100+
self.gearSol.run(False)
101+
102+
# LEDs
103+
if (self.controller == 'xbox'):
104+
if (self.C.joystick.getStickButton(wpilib.GenericHID.Hand.kRight)):
105+
self.C.greenLEDR.set(wpilib.Relay.Value.kForward)
106+
else:
107+
self.C.greenLEDR.set(wpilib.Relay.Value.kOff)
108+
elif (self.controller == 'joysticks'):
109+
if (self.C.middleJ.getRawButton(3)):
110+
self.C.greenLEDR.set(wpilib.Relay.Value.kForward)
111+
else:
112+
self.C.greenLEDR.set(wpilib.Relay.Value.kOff)
113+
114+
# Climb
115+
if (self.controller == 'joysticks'):
116+
self.climb.run(self.C.rightJ.getRawButton(1))
117+
elif (self.controller == 'xbox'):
118+
self.climb.run(self.C.joystick.getBumper(wpilib.GenericHID.Hand.kRight))
119+
120+
wpilib.Timer.delay(0.002) # wait for a motor update time
121+
122+
def test(self):
123+
"""This function is called periodically during test mode."""
124+
125+
def autonomous(self):
126+
"""Runs once during autonomous."""
127+
self.autonomousRoutine.run() # see autonomous.py
128+
129+
if __name__ == "__main__":
130+
wpilib.run(Randy)

0 commit comments

Comments
 (0)