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

Add XrpReference example #118

Open
wants to merge 1 commit into
base: main
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
35 changes: 35 additions & 0 deletions XrpReference/commands/arcadedrive.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#
# Copyright (c) FIRST and other WPILib contributors.
# Open Source Software; you can modify and/or share it under the terms of
# the WPILib BSD license file in the root directory of this project.
#

import typing
import commands2
from subsystems.drivetrain import Drivetrain


class ArcadeDrive(commands2.Command):
def __init__(
self,
drive: Drivetrain,
xaxisSpeedSupplier: typing.Callable[[], float],
zaxisRotateSupplier: typing.Callable[[], float],
) -> None:
"""Creates a new ArcadeDrive. This command will drive your robot according to the speed supplier
lambdas. This command does not terminate.

:param drivetrain: The drivetrain subsystem on which this command will run
:param xaxisSpeedSupplier: Callable supplier of forward/backward speed
:param zaxisRotateSupplier: Callable supplier of rotational speed
"""
super().__init__()

self.drive = drive
self.xaxisSpeedSupplier = xaxisSpeedSupplier
self.zaxisRotateSupplier = zaxisRotateSupplier

self.addRequirements(self.drive)

def execute(self) -> None:
self.drive.arcadeDrive(self.xaxisSpeedSupplier(), self.zaxisRotateSupplier())
28 changes: 28 additions & 0 deletions XrpReference/commands/autonomous_distance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#
# Copyright (c) FIRST and other WPILib contributors.
# Open Source Software; you can modify and/or share it under the terms of
# the WPILib BSD license file in the root directory of this project.
#

import commands2

from commands.drivedistance import DriveDistance
from commands.turndegrees import TurnDegrees
from subsystems.drivetrain import Drivetrain


class AutonomousDistance(commands2.SequentialCommandGroup):
def __init__(self, drive: Drivetrain) -> None:
"""Creates a new Autonomous Drive based on distance. This will drive out for a specified distance,
turn around and drive back.

:param drivetrain: The drivetrain subsystem on which this command will run
"""
super().__init__()

self.addCommands(
DriveDistance(-0.5, 10, drive),
TurnDegrees(-0.5, 180, drive),
DriveDistance(-0.5, 10, drive),
TurnDegrees(0.5, 180, drive),
)
30 changes: 30 additions & 0 deletions XrpReference/commands/autonomous_time.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#
# Copyright (c) FIRST and other WPILib contributors.
# Open Source Software; you can modify and/or share it under the terms of
# the WPILib BSD license file in the root directory of this project.
#

import commands2

from commands.drivetime import DriveTime
from commands.turntime import TurnTime
from subsystems.drivetrain import Drivetrain


class AutonomousTime(commands2.SequentialCommandGroup):
def __init__(self, drive: Drivetrain) -> None:
"""Creates a new Autonomous Drive based on time. This will drive out for a period of time, turn
around for time (equivalent to time to turn around) and drive forward again. This should mimic
driving out, turning around and driving back.

:param drivetrain: The drive subsystem on which this command will run

"""
super().__init__()

self.addCommands(
DriveTime(-0.6, 2.0, drive),
TurnTime(-0.5, 1.3, drive),
DriveTime(-0.6, 2.0, drive),
TurnTime(0.5, 1.3, drive),
)
44 changes: 44 additions & 0 deletions XrpReference/commands/drivedistance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#
# Copyright (c) FIRST and other WPILib contributors.
# Open Source Software; you can modify and/or share it under the terms of
# the WPILib BSD license file in the root directory of this project.
#

import commands2

from subsystems.drivetrain import Drivetrain


class DriveDistance(commands2.Command):
def __init__(self, speed: float, inches: float, drive: Drivetrain) -> None:
"""Creates a new DriveDistance. This command will drive your your robot for a desired distance at
a desired speed.

:param speed: The speed at which the robot will drive
:param inches: The number of inches the robot will drive
:param drive: The drivetrain subsystem on which this command will run
"""
super().__init__()

self.distance = inches
self.speed = speed
self.drive = drive
self.addRequirements(drive)

def initialize(self) -> None:
"""Called when the command is initially scheduled."""
self.drive.arcadeDrive(0, 0)
self.drive.resetEncoders()

def execute(self) -> None:
"""Called every time the scheduler runs while the command is scheduled."""
self.drive.arcadeDrive(self.speed, 0)

def end(self, interrupted: bool) -> None:
"""Called once the command ends or is interrupted."""
self.drive.arcadeDrive(0, 0)

def isFinished(self) -> bool:
"""Returns true when the command should end."""
# Compare distance travelled from start to desired distance
return abs(self.drive.getAverageDistanceInch()) >= self.distance
47 changes: 47 additions & 0 deletions XrpReference/commands/drivetime.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#
# Copyright (c) FIRST and other WPILib contributors.
# Open Source Software; you can modify and/or share it under the terms of
# the WPILib BSD license file in the root directory of this project.
#

import commands2
import wpilib

from subsystems.drivetrain import Drivetrain


class DriveTime(commands2.Command):
"""Creates a new DriveTime. This command will drive your robot for a desired speed and time."""

def __init__(self, speed: float, time: float, drive: Drivetrain) -> None:
"""Creates a new DriveTime. This command will drive your robot for a desired speed and time.

:param speed: The speed which the robot will drive. Negative is in reverse.
:param time: How much time to drive in seconds
:param drive: The drivetrain subsystem on which this command will run
"""
super().__init__()

self.speed = speed
self.duration = time
self.drive = drive
self.addRequirements(drive)

self.startTime = 0.0

def initialize(self) -> None:
"""Called when the command is initially scheduled."""
self.startTime = wpilib.Timer.getFPGATimestamp()
self.drive.arcadeDrive(0, 0)

def execute(self) -> None:
"""Called every time the scheduler runs while the command is scheduled."""
self.drive.arcadeDrive(self.speed, 0)

def end(self, interrupted: bool) -> None:
"""Called once the command ends or is interrupted."""
self.drive.arcadeDrive(0, 0)

def isFinished(self) -> bool:
"""Returns true when the command should end"""
return wpilib.Timer.getFPGATimestamp() - self.startTime >= self.duration
58 changes: 58 additions & 0 deletions XrpReference/commands/turndegrees.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#
# Copyright (c) FIRST and other WPILib contributors.
# Open Source Software; you can modify and/or share it under the terms of
# the WPILib BSD license file in the root directory of this project.
#

import math
import commands2

from subsystems.drivetrain import Drivetrain


class TurnDegrees(commands2.Command):
def __init__(self, speed: float, degrees: float, drive: Drivetrain) -> None:
"""Creates a new TurnDegrees. This command will turn your robot for a desired rotation (in
degrees) and rotational speed.

:param speed: The speed which the robot will drive. Negative is in reverse.
:param degrees: Degrees to turn. Leverages encoders to compare distance.
:param drive: The drive subsystem on which this command will run
"""
super().__init__()

self.degrees = degrees
self.speed = speed
self.drive = drive
self.addRequirements(drive)

def initialize(self) -> None:
"""Called when the command is initially scheduled."""
# Set motors to stop, read encoder values for starting point
self.drive.arcadeDrive(0, 0)
self.drive.resetEncoders()

def execute(self) -> None:
"""Called every time the scheduler runs while the command is scheduled."""
self.drive.arcadeDrive(0, self.speed)

def end(self, interrupted: bool) -> None:
"""Called once the command ends or is interrupted."""
self.drive.arcadeDrive(0, 0)

def isFinished(self) -> bool:
"""Returns true when the command should end."""

# Need to convert distance travelled to degrees. The Standard
# XRP Chassis found here, https://www.sparkfun.com/products/22230,
# has a wheel placement diameter (163 mm) - width of the wheel (8 mm) = 155 mm
# or 6.102 inches. We then take into consideration the width of the tires.
inchPerDegree = math.pi * 6.102 / 360

# Compare distance travelled from start to distance based on degree turn
return self._getAverageTurningDistance() >= inchPerDegree * self.degrees

def _getAverageTurningDistance(self) -> float:
leftDistance = abs(self.drive.getLeftDistanceInch())
rightDistance = abs(self.drive.getRightDistanceInch())
return (leftDistance + rightDistance) / 2.0
49 changes: 49 additions & 0 deletions XrpReference/commands/turntime.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#
# Copyright (c) FIRST and other WPILib contributors.
# Open Source Software; you can modify and/or share it under the terms of
# the WPILib BSD license file in the root directory of this project.
#

import commands2
import wpilib

from subsystems.drivetrain import Drivetrain


class TurnTime(commands2.Command):
"""Creates a new TurnTime command. This command will turn your robot for a
desired rotational speed and time.
"""

def __init__(self, speed: float, time: float, drive: Drivetrain) -> None:
"""Creates a new TurnTime.

:param speed: The speed which the robot will turn. Negative is in reverse.
:param time: How much time to turn in seconds
:param drive: The drive subsystem on which this command will run
"""
super().__init__()

self.rotationalSpeed = speed
self.duration = time
self.drive = drive
self.addRequirements(drive)

self.startTime = 0.0

def initialize(self) -> None:
"""Called when the command is initially scheduled."""
self.startTime = wpilib.Timer.getFPGATimestamp()
self.drive.arcadeDrive(0, 0)

def execute(self) -> None:
"""Called every time the scheduler runs while the command is scheduled."""
self.drive.arcadeDrive(0, self.rotationalSpeed)

def end(self, interrupted: bool) -> None:
"""Called once the command ends or is interrupted."""
self.drive.arcadeDrive(0, 0)

def isFinished(self) -> bool:
"""Returns true when the command should end"""
return wpilib.Timer.getFPGATimestamp() - self.startTime >= self.duration
97 changes: 97 additions & 0 deletions XrpReference/robot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#!/usr/bin/env python3
#
# Copyright (c) FIRST and other WPILib contributors.
# Open Source Software; you can modify and/or share it under the terms of
# the WPILib BSD license file in the root directory of this project.
#

#
# Example that shows how to connect to a XRP from RobotPy
#
# Requirements
# ------------
#
# You must have the robotpy-xrp package installed. This is best done via:
#
# # Windows
# py -3 -m pip install robotpy[commands2,sim,xrp]
#
# # Linux/macOS
# pip3 install robotpy[commands2,sim,xrp]
#
# Run the program
# ---------------
#
# To run the program you will need to explicitly use the XRP option:
#
# # Windows
# py -3 robotpy sim --xrp
#
# # Linux/macOS
# python robotpy sim --xrp
#
# By default the WPILib simulation GUI will be displayed. To disable the display
# you can add the --nogui option
#

import os
import typing

import wpilib
import commands2

from robotcontainer import RobotContainer

# If your ROMI isn't at the default address, set that here
os.environ["HALSIMXRP_HOST"] = "192.168.42.1"
os.environ["HALSIMXRP_PORT"] = "3540"


class MyRobot(commands2.TimedCommandRobot):
"""
Command v2 robots are encouraged to inherit from TimedCommandRobot, which
has an implementation of robotPeriodic which runs the scheduler for you
"""

autonomousCommand: typing.Optional[commands2.Command] = None

def robotInit(self) -> None:
"""
This function is run when the robot is first started up and should be used for any
initialization code.
"""

# Instantiate our RobotContainer. This will perform all our button bindings, and put our
# autonomous chooser on the dashboard.
self.container = RobotContainer()

def disabledInit(self) -> None:
"""This function is called once each time the robot enters Disabled mode."""

def disabledPeriodic(self) -> None:
"""This function is called periodically when disabled"""

def autonomousInit(self) -> None:
"""This autonomous runs the autonomous command selected by your RobotContainer class."""
self.autonomousCommand = self.container.getAutonomousCommand()

if self.autonomousCommand:
self.autonomousCommand.schedule()

def autonomousPeriodic(self) -> None:
"""This function is called periodically during autonomous"""

def teleopInit(self) -> None:
# This makes sure that the autonomous stops running when
# teleop starts running. If you want the autonomous to
# continue until interrupted by another command, remove
# this line or comment it out.
if self.autonomousCommand:
self.autonomousCommand.cancel()

def teleopPeriodic(self) -> None:
"""This function is called periodically during operator control"""

def testInit(self) -> None:
# Cancels all running commands at the start of test mode
commands2.CommandScheduler.getInstance().cancelAll()
Loading
Loading