-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
0e0803d
commit fe260d0
Showing
53 changed files
with
17 additions
and
2,164 deletions.
There are no files selected for viewing
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,108 +1 @@ | ||
# [`NullPointerException`](https://docs.oracle.com/javase/8/docs/api/java/lang/NullPointerException.html) on initialization | ||
|
||
## What does it look like? | ||
The general format of the error is: | ||
``` | ||
java.lang.NullPointerException: Attempt to invoke [...] on a null object reference | ||
``` | ||
|
||
On the Driver Station, you may see a stacktrace similar to this: | ||
|
||
![A stacktrace of the Driver Station with a NPE.](./npe_exception.jpg) | ||
<div style="text-align: center;"><em>A stacktrace of the Driver Station with a NPE.</em></div> | ||
|
||
## Why does this happen? | ||
First, we need to understand more about how Java works a little more in depth. | ||
> This section is a little lengthy, feel free to scroll below for the solution. | ||
When we program in Java, we have expressions, which have a certain **type**. | ||
The type tells us about the properties of said expression. | ||
|
||
This lets us add `int`s, set the power of a `DcMotor`, or check if a `boolean` is true! | ||
|
||
Type systems also give us a degree of validity; we can't add servos to booleans. | ||
|
||
> The following is a very generalized description with oversights, but is sufficient for conceptual understanding. | ||
In Java, there are two categories of types: | ||
- Primitive Types | ||
- Primitives are not objects, and do not have methods, only a value. | ||
- Primitives are passed by value. | ||
- `int`, `double`, and `boolean`, are examples of primitives. | ||
- Reference Types | ||
- All types that extend `Object` are passed by reference, and hence, reference types. | ||
- All objects are passed by reference. | ||
- `class`es, `interface`s, `enum`s, arrays | ||
|
||
> ### **What does it mean to pass/store an object by value or reference?** | ||
> | ||
> **Storing by Value:** | ||
> - You are storing the actual value of the variable in memory. | ||
> - This means that when you assign one variable to another, a copy of the value is made. | ||
> - Changes to one variable do not affect the other. | ||
> - ```java | ||
> int bobMoney = 20; | ||
> int jeffMoney = bobMoney; // "jeffMoney" gets the value of "bobMoney", not a reference to "bobMoney" | ||
> | ||
> jeffMoney = 10; // changing "jeffMoney" does not affect "bobMoney" | ||
> | ||
> System.out.println(bobMoney); // 20 | ||
> ``` | ||
> **Storing by Reference:** | ||
> - You are storing a reference or memory address to the location where the actual data is stored. | ||
> - This means that when you assign one variable to another, they both point to the same memory location. | ||
> - Changes to one variable will affect the other because they both refer to the same data. | ||
> - ```java | ||
> Person bob = new Person("bob", 18); | ||
> Person anon = bob; // "anon" now refers to the same object as "bob" | ||
> | ||
> anon.setAge(21); // changing "anon" also changes "bob" | ||
> | ||
> System.out.println(bob.getAge()); // 21 | ||
> ``` | ||
`null` really refers to a null **reference**. This means any `Object` can have a `null` value. | ||
Any uninitialized `Object` has no reference to point to; a null reference, or `null`. | ||
NPEs occur when you try to use the typed properties of an object while it points to nothing. | ||
This is so no undefined behavior occurs. | ||
Java does not provide any means of "null-safety", and so it is the responsibility of the programmer to check for and handle potential null values. | ||
FTC specific examples include trying to access the `hardwareMap` at instantiation, or just never assigning a value to a `HardwareDevice`. | ||
## How can I fix it? | ||
It is important to note that NPEs are a very common, generic exception. | ||
In FTC, by far the majority of the sources of NPEs is because of when hardware devices are accessible. | ||
- Hardware devices are **NOT** accessible at class instantiation; that is, one **cannot** do the following: | ||
- ```java | ||
@TeleOp | ||
public class Testing extends OpMode { | ||
private DcMotor motor = hardwareMap.get(DcMotor.class, "motor"); | ||
@Override | ||
public void init() { } // it's always a red flag if the init is empty! | ||
@Override | ||
public void loop() { motor.setPower(1.0); } | ||
} | ||
``` | ||
- This will fail by saying that the `hardwareMap` reference itself is null, which will lead to a NPE. | ||
- Hardware devices only start to become accessible: | ||
- For `OpMode`s: during and after `init()`, have all inits there | ||
- ```java | ||
@TeleOp | ||
public class Testing extends OpMode { | ||
private DcMotor motor; | ||
@Override | ||
public void init() { hardwareMap.get(DcMotor.class, "motor"); } | ||
@Override | ||
public void loop() { motor.setPower(1.0); } | ||
} | ||
``` | ||
- For `LinearOpMode`s: in `runOpMode()`, have inits before `waitForStart()` | ||
*Last updated: 2024-03-28* | ||
# NullPointerException on initialization |
Binary file not shown.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,36 +1 @@ | ||
# How to wire odometry pods | ||
|
||
### Ingredients | ||
|
||
|
||
1. An Expansion Hub or Control Hub | ||
2. either 2 or 3 odometry pods/modules | ||
|
||
## The Recipe | ||
|
||
### The problem | ||
The Rev Control Hub and Expansion Hub, as found [here](https://blog.eeshwark.com/robotblog/rev-hub-quadrature) by 7244 alum Eeshwar, only have 2 reliable quadrature encoder ports. | ||
This means high CPR/PPR encoders such as the Rev Through Bore encoder will miss counts on ports 1 and 2 which will lead to drift. | ||
|
||
|
||
### Solutions | ||
### 2 Wheel Odometry | ||
For teams that use a 2 wheel + IMU setup, the solution is simple! | ||
Put the drive motors on the Control Hub. | ||
Then, as you don't need drive encoders, you can simply put the odometry on Control Hub encoder ports 0 and 3 where you would usually put the motor encoders. | ||
|
||
### 3 Wheel Odometry | ||
For teams with 3 wheel odometry, it is a bit more complex. | ||
The most important odometry pods are the parallel ones since encoder drift with them will cause heading drift. | ||
This can rapidly ruin your localization as heading is used as a basis for all other measurements. | ||
Since they're the most important, the parallel pods should go in ports 0 and 3 on the Control Hub. | ||
The perpendicular (strafe) pod is less important to localization, so it is fine to put it in port 1 or 2 on the Control Hub. | ||
|
||
Note that you should always put odometry on the Control Hub (or at least all on the same hub) even if you must place it in ports 1 or 2. | ||
This is because reading from the Expansion Hub requires an additional bulk read. | ||
This can greatly worsen loop times and is not worth the benefits of using the 0 and 3 ports. | ||
|
||
|
||
|
||
|
||
*Last Updated: 2024-05-29* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,32 +1 @@ | ||
# Why to only use USB 3.0 | ||
|
||
|
||
### Ingredients | ||
|
||
|
||
1. A Control Hub | ||
2. At least 1 USB device | ||
3. A USB hub (optional) | ||
|
||
|
||
## The Recipe | ||
|
||
|
||
### Overview | ||
The Control Hub has a lot of nuances that many people do not know of, including the dangers of the onboard USB 2.0 port. | ||
|
||
### The Problem | ||
The Control Hub's USB 2.0 port shares a ground with the Wi-Fi chipset on the Control Hub. | ||
This provides a path for a static shock to the USB device to cause a Wi-Fi disconnect mid-match. | ||
|
||
|
||
### How to Mitigate the Problem | ||
The best way to mitigate the problem is to not use the USB 2.0 port on your Control Hub. | ||
For teams with no or only one USB device, that's not a problem, just use the USB 3.0 port instead of the USB 2.0 port. | ||
If your team needs more than one device, such as an Expansion Hub over USB *and* a USB camera for object detection, it gets more complicated. | ||
To prevent shock, you can get a USB hub and connect all devices through just the USB 3.0 port. | ||
|
||
|
||
|
||
|
||
*Last Updated: 2024-05-29* |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,90 +1 @@ | ||
# Improving Loop Times | ||
|
||
## Recipe | ||
|
||
**This recipe will first cover the various methods to improve loop times. At the end there is a full example of mode that uses all these methods** | ||
|
||
### Why are fast loop times important? | ||
|
||
The more often your teleop is updated, the more responsive it will be to button clicks and joystick changes. | ||
This can make driving easier. | ||
Additionally, if you are using PID(F) controllers, the more frequently they update the more accurate they are and the less they oscillate. | ||
|
||
### What causes slow loop times? | ||
|
||
Surprisingly, the main cause of slow loop times is not processing difficulties or code complexity. | ||
Most of the processing time is spent on communicating with hardware devices, known as hardware "reads" and hardware "writes". | ||
|
||
Hardware reads are when you are receiving data. | ||
For example, getting an encoder position, reading a color sensor, or accessing the IMU are all hardware reads. | ||
On the other hand, hardware writes are when you are sending data. | ||
For example, setting a motor power, setting a servo position, or configuring an LED are all hardware writes. | ||
|
||
### Checking loop times | ||
|
||
Here is some basic code to measure your loop times in milliseconds. | ||
The more milliseconds your loop takes, the slower your loop times are. | ||
|
||
```java | ||
// assume that this is enclosed in an entire op mode | ||
|
||
ElapsedTime elapsedTime = new ElapsedTime(); | ||
|
||
public void loop() { | ||
|
||
// do stuff | ||
|
||
telemetry.addData("Loop Times", elapsedTime.milliseconds()); | ||
elapsedTime.reset(); | ||
} | ||
``` | ||
|
||
### Bulk Reading | ||
|
||
Other than I2C devices, reading can be done all at once in a "bulk read" for a huge loop time improvement. | ||
|
||
By default, every time you do a hardware read, a new command is sent to retrieve it. | ||
Using one command to retrieve ALL the data is bulk reading. | ||
|
||
This recipe will not go into the different bulk reading modes. To learn more look [here](https://gm0.org/en/latest/docs/software/tutorials/bulk-reads.html). | ||
|
||
### Caching Motor Powers | ||
|
||
So now let's try and reduce unnecessary hardware writes. | ||
|
||
If a motor is going at 0.5 power, and we keep setting the power to 0.5, the output of the motor will not change. | ||
However, each one of those `setPower()` commands is a hardware write which will delay your loop. | ||
A simple solution to this is to only send a new motor power when it is different from the previous. | ||
|
||
However, we can go even further then just difference to remove much more unnecessary writes. | ||
If the motor is currently running at 0.5 power, and you tell it to run at 0.51 power instead, it will have very little effect. | ||
However, it will unnecessarily perform a loop-delaying hardware write. | ||
|
||
Instead, you can store the last power sent to a motor and check every new `setPower()` command to only run if the new power is sufficiently different from the previous power. | ||
|
||
<!-- This is the most simplistic implementation of caching motor powers and only supports RUN_WITHOUT_ENCODER mode. | ||
```java | ||
{{#rustdoc_include CachingDcMotorEx.java::}} | ||
``` --> | ||
|
||
|
||
### Photon | ||
|
||
Photon is another way of increasing loop times. | ||
Photon is an experimental library developed by Eeshwar, an alumni originally from team 7244. | ||
It allows for significantly faster loop times by parallelizing much more of the hardware reads and writes. | ||
Installation instructions for Photon are available [here](https://github.com/Eeshwar-Krishnan/PhotonFTC/tree/main). | ||
|
||
Photon has a few known issues at the moment, here's some troubleshooting steps: | ||
|
||
**Some people have installed photon and Android Studio does not recognize the `@Photon` annotation. Instead of `implementation 'com.github.Eeshwar-Krishnan:PhotonFTC:v3.0.1-ALPHA'`, try `implementation 'com.github.Eeshwar-Krishnan:PhotonFTC:main-SNAPSHOT'`.** | ||
|
||
**Also, be warned. Photon somtimes when used just randomly reverses motors and servos (but it's always the same ones reversed the same way).** | ||
|
||
|
||
### Full Example | ||
|
||
```java | ||
{{#rustdoc_include OptimizedOpMode.java::}} | ||
``` | ||
*Last Updated: 2024-05-30* |
Oops, something went wrong.