diff --git a/docs/code/swerve.rst b/docs/code/swerve.rst index 5484221..2309c35 100644 --- a/docs/code/swerve.rst +++ b/docs/code/swerve.rst @@ -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. + diff --git a/docs/code/testing_code.rst b/docs/code/testing_code.rst index 0fbc7d0..b1f8361 100644 --- a/docs/code/testing_code.rst +++ b/docs/code/testing_code.rst @@ -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 `_ + +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 `_ + +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 `_. + +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. \ No newline at end of file diff --git a/docs/git/github_actions.rst b/docs/git/github_actions.rst new file mode 100644 index 0000000..6af01dd --- /dev/null +++ b/docs/git/github_actions.rst @@ -0,0 +1,179 @@ +.. _github-actions-setup: + +===================================== +Setting Up GitHub Actions for Testing +===================================== + +This guide will walk you through setting up GitHub Actions for your FRC robotics programming team to automate code testing. + +Overview +======== +GitHub Actions allows you to define workflows that run automated processes, such as testing your code, whenever certain events occur in your repository. By setting up a workflow file, you can ensure your team’s code is tested automatically with every push or pull request. + +Example code from 2025 +====================== + +.. code-block:: yaml + + name: Tests + +on: + push: + pull_request: + branches: [master] + +jobs: + test: + timeout-minutes: 5 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + with: + fetch-depth: 1 + + - name: Set up Python 3.12 + uses: actions/setup-python@v1 + with: + python-version: 3.12 + + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python3 -m pip install robotpy==2025.2.1.0 + python3 -m robotpy sync + + + - name: Run robotpy Tests + run: | + python3 -m robotpy test +Explanation of Workflow +======================== +Below is an explanation of each line in your current test YAML file: + +.. code-block:: yaml + + name: Tests + +**Explanation:** Sets the name of the workflow as "Tests." This will appear in the Actions tab on GitHub. + + on: + push: + pull_request: + branches: [master] + +**Explanation:** Specifies the events that trigger this workflow. It runs on every push and pull request to the `master` branch. + + jobs: + test: + timeout-minutes: 5 + runs-on: ubuntu-latest + +**Explanation:** +- `jobs:` Defines a job named `test`. +- `timeout-minutes: 5` Limits the job's execution time to 5 minutes to prevent long-running tests. +- `runs-on: ubuntu-latest` Specifies that the job will run on the latest Ubuntu runner provided by GitHub. + + steps: + - uses: actions/checkout@v1 + with: + fetch-depth: 1 + +**Explanation:** +- `uses: actions/checkout@v1`: Utilizes the `checkout` action to clone the repository into the runner. +- `with:` Provides configuration options for the `checkout` action. +- `fetch-depth: 1`: Specifies to fetch only the latest commit, reducing the amount of data transferred. + + - name: Set up Python 3.12 + uses: actions/setup-python@v1 + with: + python-version: 3.12 + +**Explanation:** +- `name: Set up Python 3.12`: Names the step for clarity. +- `uses: actions/setup-python@v1`: Utilizes the `setup-python` action to install and configure Python. +- `with:` Provides options for this action. +- `python-version: 3.12`: Specifies Python version 3.12 to be set up in the environment. + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python3 -m pip install robotpy==2025.2.1.0 + python3 -m robotpy sync + +**Explanation:** +- `name: Install dependencies`: Names the step for clarity. +- `run:` Executes the following commands in the runner's shell: + - `python -m pip install --upgrade pip`: Updates pip to the latest version. + - `python3 -m pip install robotpy==2025.2.1.0`: Installs a specific version of the `robotpy` library. + - `python3 -m robotpy sync`: Synchronizes dependencies for the `robotpy` library. + + - name:Run robotpy Tests + run: | + python3 -m robotpy test + +**Explanation:** +- `name:`: The name is `Run robotpy Tests` which indicates this is where the robotpy code is tested. +- `run: python3 -m robotpy test`: Runs the test suite for `robotpy` using Python 3. + +Steps to Set Up GitHub Actions +============================== + +1. **Create a Workflow Directory** + + In your repository, create a directory named ``.github/workflows`` if it doesn’t already exist. + + .. code-block:: bash + + mkdir -p .github/workflows + +2. **Create a Workflow File** + + Inside the ``.github/workflows`` directory, create a YAML file. For example, ``test.yml``. + + .. code-block:: bash + + touch .github/workflows/test.yml + +3. **Define the Workflow** + + Open the ``test.yml`` file in your editor and define the workflow based on the explained YAML file above. + +4. **Commit the Workflow File** + + Add the workflow file to your repository and commit the changes: + + .. code-block:: bash + + git add .github/workflows/test.yml + git commit -m "Add GitHub Actions workflow for testing" + git push origin main + +5. **Verify the Workflow** + + Push a change to your repository or open a pull request. Navigate to the **Actions** tab in your GitHub repository to see the workflow running. Ensure all tests pass successfully. + +Troubleshooting +=============== +- **Dependency Issues**: Check your ``pyproject.toml`` file to ensure all necessary dependencies are listed. +- **Failed Tests**: Review the output in the Actions tab to identify which tests failed and why. +- **Workflow Errors**: Validate your YAML file using an online tool like `YAML Lint `_. + +Advanced Topics +=============== +- **Matrix Testing**: Run tests across multiple Python versions or environments by using a matrix strategy. +- **Cache Dependencies**: Use caching to speed up subsequent runs. + + Example: + + .. code-block:: yaml + + - name: Cache pip + uses: actions/cache@v3 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip + +By following these steps, your FRC robotics programming team can ensure a smooth and automated process for testing code changes. This will help maintain code quality and streamline development workflows. diff --git a/docs/index.rst b/docs/index.rst index 5ea3ff3..0d14a61 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -46,6 +46,8 @@ These are the Standard Operating Procedures for the Choate Robotics Programming Deploying Code + Github Actions + Documentation