Skip to content

Commit bbd28a0

Browse files
committed
Add quick-vision and ramsete-controller examples
1 parent bdd3520 commit bbd28a0

File tree

6 files changed

+259
-15
lines changed

6 files changed

+259
-15
lines changed

potentiometer-pid/robot.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,8 @@
1111

1212
class MyRobot(wpilib.TimedRobot):
1313
"""
14-
This is a sample program to demonstrate how to use a soft potentiometer and
15-
a PID controller to reach and maintain position setpoints on an elevator
16-
mechanism.
14+
This is a sample program to demonstrate how to use a soft potentiometer and a PID controller to
15+
reach and maintain position setpoints on an elevator mechanism.
1716
"""
1817

1918
kPotChannel = 1

quick-vision/robot.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#!/usr/bin/env python3
2+
#
3+
# Copyright (c) FIRST and other WPILib contributors.
4+
# Open Source Software; you can modify and/or share it under the terms of
5+
# the WPILib BSD license file in the root directory of this project.
6+
#
7+
8+
import wpilib
9+
from wpilib.cameraserver import CameraServer
10+
11+
12+
class MyRobot(wpilib.TimedRobot):
13+
"""
14+
Uses the CameraServer class to automatically capture video from a USB webcam and send it to the
15+
FRC dashboard without doing any vision processing. This is the easiest way to get camera images
16+
to the dashboard. Just add this to the robotInit() method in your program.
17+
"""
18+
19+
def robotInit(self):
20+
CameraServer().launch()
21+
22+
23+
if __name__ == "__main__":
24+
wpilib.run(MyRobot)

ramsete-controller/drivetrain.py

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
#!/usr/bin/env python3
2+
#
3+
# Copyright (c) FIRST and other WPILib contributors.
4+
# Open Source Software; you can modify and/or share it under the terms of
5+
# the WPILib BSD license file in the root directory of this project.
6+
#
7+
8+
import wpilib.drive
9+
import wpimath.controller
10+
import wpimath.geometry
11+
import wpimath.kinematics
12+
import wpimath.units
13+
14+
import math
15+
16+
17+
class Drivetrain:
18+
"""Represents a differential drive style drivetrain."""
19+
20+
kMaxSpeed = 3.0 # meters per second
21+
kMaxAngularSpeed = 2 * math.pi # one rotation per second
22+
23+
kTrackWidth = 0.381 * 2 # meters
24+
kWheelRadius = 0.0508 # meters
25+
kEncoderResolution = 4096 # counts per revolution
26+
27+
def __init__(self):
28+
leftLeader = wpilib.PWMSparkMax(1)
29+
leftFollower = wpilib.PWMSparkMax(2)
30+
rightLeader = wpilib.PWMSparkMax(3)
31+
rightFollower = wpilib.PWMSparkMax(4)
32+
33+
self.leftEncoder = wpilib.Encoder(0, 1)
34+
self.rightEncoder = wpilib.Encoder(2, 3)
35+
36+
self.leftGroup = wpilib.MotorControllerGroup(leftLeader, leftFollower)
37+
self.rightGroup = wpilib.MotorControllerGroup(rightLeader, rightFollower)
38+
39+
self.gyro = wpilib.AnalogGyro(0)
40+
41+
self.leftPIDController = wpimath.controller.PIDController(1.0, 0.0, 0.0)
42+
self.rightPIDController = wpimath.controller.PIDController(1.0, 0.0, 0.0)
43+
44+
self.kinematics = wpimath.kinematics.DifferentialDriveKinematics(
45+
self.kTrackWidth
46+
)
47+
48+
# Gains are for example purposes only - must be determined for your own robot!
49+
self.feedforward = wpimath.controller.SimpleMotorFeedforwardMeters(1, 3)
50+
51+
self.gyro.reset()
52+
53+
# We need to invert one side of the drivetrain so that positive voltages
54+
# result in both sides moving forward. Depending on how your robot's
55+
# gearbox is constructed, you might have to invert the left side instead.
56+
self.rightGroup.setInverted(True)
57+
58+
# Set the distance per pulse for the drive encoders. We can simply use the
59+
# distance traveled for one rotation of the wheel divided by the encoder
60+
# resolution.
61+
self.leftEncoder.setDistancePerPulse(
62+
2 * math.pi * self.kWheelRadius / self.kEncoderResolution
63+
)
64+
self.rightEncoder.setDistancePerPulse(
65+
2 * math.pi * self.kWheelRadius / self.kEncoderResolution
66+
)
67+
68+
self.leftEncoder.reset()
69+
self.rightEncoder.reset()
70+
71+
self.odometry = wpimath.kinematics.DifferentialDriveOdometry(
72+
self.gyro.getRotation2d(),
73+
self.leftEncoder.getDistance(),
74+
self.rightEncoder.getDistance(),
75+
)
76+
77+
def setSpeeds(self, speeds: wpimath.kinematics.DifferentialDriveWheelSpeeds):
78+
"""Sets the desired wheel speeds."""
79+
leftFeedforward = self.feedforward.calculate(speeds.left)
80+
rightFeedforward = self.feedforward.calculate(speeds.right)
81+
82+
leftOutput = self.leftPIDController.calculate(
83+
self.leftEncoder.getRate(), speeds.left
84+
)
85+
rightOutput = self.rightPIDController.calculate(
86+
self.rightEncoder.getRate(), speeds.right
87+
)
88+
89+
self.leftGroup.setVoltage(leftOutput + leftFeedforward)
90+
self.rightGroup.setVoltage(rightOutput + rightFeedforward)
91+
92+
def drive(self, xSpeed, rot):
93+
"""Drives the robot with the given linear velocity and angular velocity."""
94+
wheelSpeeds = self.kinematics.toWheelSpeeds(
95+
wpimath.kinematics.ChassisSpeeds(xSpeed, 0, rot)
96+
)
97+
self.setSpeeds(wheelSpeeds)
98+
99+
def updateOdometry(self):
100+
"""Updates the field-relative position."""
101+
self.odometry.update(
102+
self.gyro.getRotation2d(),
103+
self.leftEncoder.getDistance(),
104+
self.rightEncoder.getDistance(),
105+
)
106+
107+
def resetOdometry(self, pose: wpimath.geometry.Pose2d):
108+
"""Resets the field-relative position to a specific location."""
109+
self.odometry.resetPosition(
110+
self.gyro.getRotation2d(),
111+
self.leftEncoder.getDistance(),
112+
self.rightEncoder.getDistance(),
113+
pose
114+
)
115+
116+
def getPose(self) -> wpimath.geometry.Pose2d:
117+
"""Returns the pose of the robot."""
118+
return self.odometry.getPose()

ramsete-controller/robot.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
#!/usr/bin/env python3
2+
#
3+
# Copyright (c) FIRST and other WPILib contributors.
4+
# Open Source Software; you can modify and/or share it under the terms of
5+
# the WPILib BSD license file in the root directory of this project.
6+
#
7+
8+
import wpilib
9+
import wpimath.controller
10+
import wpimath.filter
11+
import wpimath.geometry
12+
import wpimath.trajectory
13+
import wpimath.units
14+
15+
from drivetrain import Drivetrain
16+
17+
18+
class MyRobot(wpilib.TimedRobot):
19+
"""
20+
Uses the CameraServer class to automatically capture video from a USB webcam and send it to the
21+
FRC dashboard without doing any vision processing. This is the easiest way to get camera images
22+
to the dashboard. Just add this to the robotInit() method in your program.
23+
"""
24+
25+
def robotInit(self):
26+
self.controller = wpilib.XboxController(0)
27+
self.drive = Drivetrain()
28+
29+
# Slew rate limiters to make joystick inputs more gentle; 1/3 sec from 0 to 1.
30+
self.speedLimiter = wpimath.filter.SlewRateLimiter(3)
31+
self.rotLimiter = wpimath.filter.SlewRateLimiter(3)
32+
33+
# An example trajectory to follow during the autonomous period.
34+
self.trajectory = wpimath.trajectory.TrajectoryGenerator.generateTrajectory(
35+
wpimath.geometry.Pose2d(0, 0, wpimath.geometry.Rotation2d.fromDegrees(0)),
36+
[
37+
wpimath.geometry.Translation2d(1, 1),
38+
wpimath.geometry.Translation2d(2, -1),
39+
],
40+
wpimath.geometry.Pose2d(3, 0, wpimath.geometry.Rotation2d.fromDegrees(0)),
41+
wpimath.trajectory.TrajectoryConfig(
42+
wpimath.units.feetToMeters(3.0), wpimath.units.feetToMeters(3.0)
43+
)
44+
)
45+
46+
# The Ramsete Controller to follow the trajectory.
47+
self.ramseteController = wpimath.controller.RamseteController()
48+
49+
# The timer to use during the autonomous period.
50+
self.timer = wpilib.Timer()
51+
52+
# Create Field2d for robot and trajectory visualizations.
53+
self.field = wpilib.Field2d()
54+
55+
# Create and push Field2d to SmartDashboard.
56+
wpilib.SmartDashboard.putData(self.field)
57+
58+
# Push the trajectory to Field2d.
59+
self.field.getObject("traj").setTrajectory(self.trajectory)
60+
61+
def autonomousInit(self):
62+
# Initialize the timer.
63+
self.timer = wpilib.Timer()
64+
self.timer.start()
65+
66+
# Reset the drivetrain's odometry to the starting pose of the trajectory.
67+
self.drive.resetOdometry(self.trajectory.initialPose())
68+
69+
def autonomousPeriodic(self):
70+
# Update odometry.
71+
self.drive.updateOdometry()
72+
73+
# Update robot position on Field2d.
74+
self.field.setRobotPose(self.drive.getPose())
75+
76+
if self.timer.get() < self.trajectory.totalTime():
77+
# Get the desired pose from the trajectory.
78+
desiredPose = self.trajectory.sample(self.timer.get())
79+
80+
# Get the reference chassis speeds from the Ramsete controller.
81+
refChassisSpeeds = self.ramseteController.calculate(self.drive.getPose(), desiredPose)
82+
83+
# Set the linear and angular speeds.
84+
self.drive.drive(refChassisSpeeds.vx, refChassisSpeeds.omega)
85+
else:
86+
self.drive.drive(0, 0)
87+
88+
def teleopPeriodic(self):
89+
# Get the x speed. We are inverting this because Xbox controllers return
90+
# negative values when we push forward.
91+
xSpeed = -self.speedLimiter.calculate(self.controller.getLeftY()) * Drivetrain.kMaxSpeed
92+
93+
# Get the rate of angular rotation. We are inverting this because we want a
94+
# positive value when we pull to the left (remember, CCW is positive in
95+
# mathematics). Xbox controllers return positive values when you pull to
96+
# the right by default.
97+
rot = -self.rotLimiter.calculate(self.controller.getRightX()) * Drivetrain.kMaxAngularSpeed
98+
99+
self.drive.drive(xSpeed, rot)
100+
101+
102+
if __name__ == "__main__":
103+
wpilib.run(MyRobot)

relay/robot.py

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,30 +10,28 @@
1010

1111
class MyRobot(wpilib.TimedRobot):
1212
"""
13-
This is a sample program which uses joystick buttons to control a relay.
14-
A Relay (generally a spike) has two outputs, each of which can be at either
15-
0V or 12V and so can be used for actions such as turning a motor off, full
16-
forwards, or full reverse, and is generally used on the compressor. This
17-
program uses two buttons on a joystick and each button corresponds to one
13+
This is a sample program which uses joystick buttons to control a relay. A Relay (generally a
14+
spike) has two outputs, each of which can be at either 0V or 12V and so can be used for actions
15+
such as turning a motor off, full forwards, or full reverse, and is generally used on the
16+
compressor. This program uses two buttons on a joystick and each button corresponds to one
1817
output; pressing the button sets the output to 12V and releasing sets it to 0V.
1918
"""
2019

20+
kRelayForwardButton = 1
21+
kRelayReverseButton = 2
22+
2123
def robotInit(self):
2224
"""Robot initialization function"""
2325

26+
self.stick = wpilib.Joystick(0)
2427
self.relay = wpilib.Relay(0)
2528

26-
self.joystick = wpilib.Joystick(0)
27-
28-
self.relayForwardButton = 1
29-
self.relayReverseButton = 2
30-
3129
def teleopPeriodic(self):
3230
# Retrieve the button values. GetRawButton will
3331
# return true if the button is pressed and false if not.
3432

35-
forward = self.joystick.getRawButton(self.relayForwardButton)
36-
reverse = self.joystick.getRawButton(self.relayReverseButton)
33+
forward = self.stick.getRawButton(self.kRelayForwardButton)
34+
reverse = self.stick.getRawButton(self.kRelayReverseButton)
3735

3836
##
3937
# Depending on the button values, we want to use one of

run_tests.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ BASE_TESTS="
3838
physics-mecanum/src
3939
physics-spi/src
4040
potentiometer-pid
41+
quick-vision
42+
ramsete-controller
4143
relay
4244
shuffleboard
4345
solenoid

0 commit comments

Comments
 (0)