Skip to content
Merged
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
61 changes: 60 additions & 1 deletion docs/code/swerve.rst
Original file line number Diff line number Diff line change
@@ -1,2 +1,61 @@
Swerve Drive:
-------------
=============

Swerve drive is a type of drivetrain that allows a robot to move in any direction without turning. Each wheel operates independently, with the ability to rotate to a specific angle and drive at a specific speed, which allows the robot to move in any direction. This type of drivetrain is widely used in various robotics applications, including FIRST Robotics Competition (FRC) robots, due to its flexibility and enhanced maneuverability.

One of the main advantages is that it allows the robot to move in any direction without turning, which can be very useful in FRC games where the robot needs to cycle quickly across the field. Another advantage is that the robot can strafe or move sideways, which helps avoid defense bots or obstacles. Robots can also turn in place, which is useful for lining up shots or navigating tight spaces. It increases maneuverability and allows for more precise control of the robot. Swerve is also very effective if your robot needs to aim a shooter or other mechanism while moving. You can rotate in the robot while also moving toward the target.

.. figure:: https://i.ibb.co/2qLNCnP/9hhpas.gif
:alt: PID Control
:align: center

Swerve drives are made up of some number of, usually four, modules. These modules are made up of a wheel and a motor
that can rotate the wheel. The wheel is mounted on a turntable that can rotate the wheel to any angle. One motor is
used to drive the wheel at the desired speed. The angle of the wheel is controlled by another motor.

If all the wheels are at the same angle and speed then the robot will move in a straight line. If the wheels are at
at an angle perpendicular to the line that connects the wheel to the center of the robot and all the all the wheels are
powered equally then the robot will rotate in place. Thinking of these settings as vectors, you can use vector addition
to calculate the direction and speed necessary to make the robot do both of these things at the same time.

The main disadvantage of swerve drive is that it is more complex than other drive trains. This means that it can be more
difficult to design, build, and program. Swerve drive also requires more motors than other drive trains, which can be a
limiting factor in some games.

Programming a Swerve Drive:
---------------------------

Programming a Swerve is generally going to take several different abstractions. The first is the **SwerveModule**.
This will be a class that represents a single swerve module. It will have a method to set the speed and angle of the
wheel. It will also have a method to get the current speed and angle of the wheel.

The next abstraction is the **SwerveDrive**. This will be a class that represents the entire swerve drive. It will have
a method to set the speed and angle of the entire robot. It will also have a method to get the current speed and angle
of the entire robot. It will have a method to get the current speed and angle of each wheel. It will also have a method
to set the speed and angle of each wheel.

Another abstraction is the **SwerveDriveController**. This will be a class that represents the controller for the swerve
drive. It will have a method to get the desired speed and angle of the robot. It will also have a method to get the desired
speed and angle of each wheel. It will have a method to set the desired speed and angle of the robot. It will also have a
method to set the desired speed and angle of each wheel.

Another abstraction that is generally used is the **SwerveDriveKinematics**. This will be a class that represents the
kinematics of the swerve drive. It will have a method to calculate the speed and angle of each wheel given the speed and
angle of the robot. It will also have a method to calculate the speed and angle of the robot given the speed and angle of
each wheel.

Each swerve drive is generally made up of a series of **SwerveNodes**. These are the individual wheels that make up the
swerve drive. Generally what the swerve code needs to do is determine the speed and angle for each of the swerve nodes to
get the robot to move in the desired direction. This is generally done by calculating with the speed and angle of the robot

Finally there is the **ChassisSpeeds** class. This will be a class that represents the speed of the robot. It will have
a method to get the speed of the robot in the x direction. It will also have a method to get the speed of the robot in the
y direction. It will have a method to get the speed of the robot rotating.

Swerve Drive on 7407
--------------------

On 7407, the swerve drive code is often used based on the code that we used in previous years. This code has been written
with an eye to allow us to use encoder information, camera information, and gyro to get accurate odometry to allow use to
write autonomous code that can be used to navigate the field. Check out code in the ``toolkit`` folder of the template repo.

199 changes: 198 additions & 1 deletion docs/code/testing_code.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,202 @@
============
Testing Code
============

Unit testing
------------

Testing code is an important part of the development process. As you make code, you revise and revise it.
As you make changes in the code, you can introduce revisions that have negative effects on the code in unexpected ways.
Writing and running tests are ways to ensure that as you update and improve your code, you maintain functionality
that you understand and that works as intended.

Writing Tests for FRC Robots
----------------------------

Check out:

`RobotPy Testing Guide <https://robotpy.readthedocs.io/en/stable/guide/testing.html>`_

To start writing tests for the code, use the repository linked below,
and make sure that you have set up the proper libraries.

Starter Code
^^^^^^^^^^^^

You can find starter code for this project here: `DriveStraight2024 <https://github.com/mbardoeChoate/DriveStraight2024>`_

The starter code contains three files: ``robot.py``, ``subsystems/drivetrain.py``, and ``pyproject.toml``.
You may need to set up the project to be a RobotPy project. Use the following commands:

.. code-block:: shell

python3 -m pip install robotpy
python3 -m robotpy init
python3 -m robotpy sync

To run the tests, type the following into the terminal:

.. code-block:: python

python3 -m robotpy test

This command creates a ``tests`` folder and adds some basic tests to ensure that
the code runs in autonomous and teleoperated modes without errors. This allows you
to test your code without running the robot.

How to Write Tests
------------------

When writing a test, there are a few things to keep in mind:

- Organize your tests in the same way you organize your robot's code.
- Place tests for one file primarily in a single test file.

ArcadeDrive Test
^^^^^^^^^^^^^^^^

At the top of the file, include necessary imports. For example, we will use the ``pytest`` library and sometimes the ``unittest.mock`` library.

Create a Python file called ``test_drivetrain.py`` in a ``tests`` directory:

.. code-block:: python

import pytest
from drivetrain import Drivetrain

When writing tests:

- The filename must include the word ``test`` so the testing library recognizes it as a test file.
- Method names should start with ``test`` to indicate they are test methods.

Example test for ``arcadeDrive``:

.. code-block:: python

def test_arcadeDrive():
# Setup the test
drivetrain = Drivetrain()
drivetrain.drive = MagicMock()

# Action
drivetrain.arcadeDrive(0.2, 0.3)

# Assert
drivetrain.drive.arcadeDrive.assert_called_once_with(0.3, 0.2)

Each test includes three parts:

1. **Setup**: Create objects and prepare for the test.
2. **Action**: Call the method being tested.
3. **Assert**: Verify the results.

Testing Resetting Encoders
^^^^^^^^^^^^^^^^^^^^^^^^^^

Mocking
-------

The ``MagicMock`` object is a tool for creating fake objects used in tests. It tracks method calls and arguments passed.

Example of mocking dependencies in the ``Drivetrain`` class:

.. code-block:: python

@pytest.fixture
def drivetrain() -> Drivetrain:
# Create a drivetrain with mocked dependencies
drive = Drivetrain()
drive.left_motor = MagicMock()
drive.right_motor = MagicMock()
drive.leftEncoder = MagicMock()
drive.rightEncoder = MagicMock()
drive.drive = MagicMock()
drive.gyro = MagicMock()
return drive

Example test using the fixture:

.. code-block:: python

def test_reset_encoders(drivetrain: Drivetrain):
'''A test to ensure that resetting the encoders resets all encoders.'''
# Setup
left_reset = drivetrain.leftEncoder.reset
right_reset = drivetrain.rightEncoder.reset

# Action
drivetrain.resetEncoders()

# Assert
left_reset.assert_called_once()
right_reset.assert_called_once()

For more details on assert methods, visit the `unittest.mock documentation <https://docs.python.org/3/library/unittest.mock.html#unittest.mock.Mock.assert_called>`_.

Monkeypatching
--------------

Monkeypatching allows you to override functions during tests. Example:

.. code-block:: python

def test_averageDistanceMeter(drivetrain: Drivetrain, monkeypatch) -> None:
# Setup
def mock_getRightDistanceInch(self):
return 3.0

def mock_getLeftDistanceInch(self):
return 2.0

monkeypatch.setattr(Drivetrain, "getLeftDistanceInch", mock_getLeftDistanceInch)
monkeypatch.setattr(Drivetrain, "getRightDistanceInch", mock_getRightDistanceInch)

# Action
dist = drivetrain.getAverageDistanceInch()

# Assert
assert dist == 2.5

Parameterizing
--------------

Use parameterization to test with multiple input values. Add a decorator:

.. code-block:: python

@pytest.mark.parametrize(('left_Distance', 'right_Distance', 'output'), [
(2, 3, 2.5),
(10, 20, 15),
(-3, 3, 0),
])
def test_averageDistanceMeter(drivetrain: Drivetrain, monkeypatch, left_Distance,
right_Distance, output) -> None:
# Setup
def mock_getRightDistanceInch(self):
return right_Distance

def mock_getLeftDistanceInch(self):
return left_Distance

monkeypatch.setattr(Drivetrain, "getLeftDistanceInch", mock_getLeftDistanceInch)
monkeypatch.setattr(Drivetrain, "getRightDistanceInch", mock_getRightDistanceInch)

# Action
dist = drivetrain.getAverageDistanceInch()

# Assert
assert dist == output


Running Tests
-------------

To run the tests, type the following into the terminal:

.. code-block:: python

python3 -m robotpy test

This command runs all the tests in the ``tests`` directory.
If a test fails, the command will output the error message, and you can debug the issue. It will give you information
about the test that failed and the line number where the failure occurred, as well as what the inputs were, and generally
what the expected output was as well as the actual output generated.
Loading
Loading