Skip to content

Conversation

@AnthonyHoneine
Copy link

@AnthonyHoneine AnthonyHoneine commented Jan 14, 2026

Overview

Added a steering offset to the SC UKF. Raw offset is passed into a low pass filter. Filtered offset is added to the command steering angle and passed to Firmware. Simulation allows testing the use, values and variance of the offset.

Linked Issues

#70

Tasks

  • Test during mock rolls
  • Initialization Convergence Check
  • Test during rolls

Testing

We will test the steering offset accuracy during mock rolls in FoxGlove, and then check the offset variance and UKF covariance to try to find a threshold to determine convergence.

rk012 and others added 30 commits September 30, 2025 16:54
…lowup, added auton check to steering offset ukf, started publishing both corrected and raw steering_cmd in controller, corrected wheelbase constants
@rk012 rk012 linked an issue Jan 14, 2026 that may be closed by this pull request
@mehulgoel873 mehulgoel873 marked this pull request as ready for review January 20, 2026 22:57

# accuracy is the circular error radius (meters)
# k is the factor that maps between this radius and the Gaussian σ
k = 0.848867684498
Copy link

@viv511 viv511 Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are the rest of the decimal points of this number fully accurate? does it matter much?

I could be wrong, but I looked up some stuff (and assuming this is like a CEP50 based conversation)

It might be worth writing the computation in a comment rather than just using a magic number?

is it the same as k = 1 / sqrt(2 * ln(2))?

https://www.wolframalpha.com/input?i=1+%2F+sqrt%282+*+ln%282%29%29

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

throw it in constants.py

@@ -1,4 +1,4 @@
output_bags:
- uri: nand-raceline-mcap
- uri: rosbag2_2025_11_15-12_47_46_0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this?

Copy link
Contributor

@mehulgoel873 mehulgoel873 Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will revert, but basically a script to convert bags to .mcap (needs bags uri to work) @hpuranik

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove dead code

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

offset_variance = self.Sigma[-1, -1]

# Checks the offset variance is reasonable, corresponds to 6 deg std deviation.
if offset_variance < 0.1:
Copy link
Member

@saransh323 saransh323 Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make sure we remove magic numbers from our code? It helps with readability and finding bugs.

During our review, we found that this number is actually wrong.

6 degrees standard deviation corresponds to 0.10472 radians, but because this is standard deviation—we need to square the resulting value to find the variance.

The correct constant should be closer to 0.010966

In general:

  • Try not to use magic numbers
  • If we HAVE to use magic numbers, we should set a standard that we have a comment explaining the formula and the numbers we used to avoid confusion

*similar logic to the k = 0.8 yada yada constant

Also consider moving constants like these into the constants python file.
-vivek

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also, what are observed values currently?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do! Current values are rougly 0.05

self.Q = np.diag([1e-4, 1e-4, 1e-4, 2.4e-1, 1e-6]) # init process covariance values (2.4e-1 for velocity based on 3 x std dev of 0.16)

# Offset variance extremely high out of caution and proof of convergence
self.Sigma: np.ndarray = np.diag([1e-4, 1e-4, 1e-2, 1e-2, 5e-2]) # state covariance
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does offset covariance usually settle to / hover around? (I believe we only have sim data right now, but let's mock roll and observe and then finalize this init value)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In simulation it hovers around 0.01-0.05, i agree with waiting until mock roll.

Copy link

@the-beast-coder the-beast-coder left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just some questions about the changes

self.Q = np.diag([1e-4, 1e-4, 1e-4, 2.4e-1, 1e-6]) # init process covariance values (2.4e-1 for velocity based on 3 x std dev of 0.16)

# Offset variance extremely high out of caution and proof of convergence
self.Sigma: np.ndarray = np.diag([1e-4, 1e-4, 1e-2, 1e-2, 5e-2]) # state covariance

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where do these values come from?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These values are based on the following calculation:

(Rough estimate abt std dev) -> 3x confidence interval -> Variance.

self.auton_enabled_prev = auton

@classmethod
def wrap_angle(cls, angle, limit=np.pi):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does this function do?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given an angle, it turns it into [-pi-pi range] (in radians). This ensure's that it's always an accurate angle [-pi, pi]

offset_variance = self.Sigma[-1, -1]

# Checks the offset variance is reasonable, corresponds to 6 deg std deviation.
if offset_variance < 0.1:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if offset variance is more than 0.1? Doesnt that represent some sort of error happening

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the offset variance is greater than 0.1, this means we are very unsure of the offset. If this is the case, we don't want to publish any values because the data is too noisy.

# measurement vector
y = [msg.pose.pose.position.x, msg.pose.pose.position.y]
# extract 2x2 position covariance from the 6x6 pose covariance
self.R = np.reshape(np.stack((msg.pose.covariance[:2], msg.pose.covariance[6:8]), axis=0), (2, 2))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What changed that made us take this out of the if condition?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We noticed a bug in the original implementation, so that the self.R was only set once. Instead, since it's related to the measurement, it can update each time.

viv511

This comment was marked as resolved.


# accuracy is the circular error radius (meters)
# k is the factor that maps between this radius and the Gaussian σ
k = 0.848867684498
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

throw it in constants.py

self.auton_enabled_prev = auton

@classmethod
def wrap_angle(cls, angle, limit=np.pi):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given an angle, it turns it into [-pi-pi range] (in radians). This ensure's that it's always an accurate angle [-pi, pi]

# measurement vector
y = [msg.pose.pose.position.x, msg.pose.pose.position.y]
# extract 2x2 position covariance from the 6x6 pose covariance
self.R = np.reshape(np.stack((msg.pose.covariance[:2], msg.pose.covariance[6:8]), axis=0), (2, 2))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We noticed a bug in the original implementation, so that the self.R was only set once. Instead, since it's related to the measurement, it can update each time.

offset_variance = self.Sigma[-1, -1]

# Checks the offset variance is reasonable, corresponds to 6 deg std deviation.
if offset_variance < 0.1:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do! Current values are rougly 0.05

self.Q = np.diag([1e-4, 1e-4, 1e-4, 2.4e-1, 1e-6]) # init process covariance values (2.4e-1 for velocity based on 3 x std dev of 0.16)

# Offset variance extremely high out of caution and proof of convergence
self.Sigma: np.ndarray = np.diag([1e-4, 1e-4, 1e-2, 1e-2, 5e-2]) # state covariance
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These values are based on the following calculation:

(Rough estimate abt std dev) -> 3x confidence interval -> Variance.

self.Sigma: np.ndarray = np.diag([1e-4, 1e-4, 1e-2, 1e-2, 1.2e-3]) # state covariance
self.Q = np.diag([1e-4, 1e-4, 1e-4, 2.4e-1, 1e-6]) # init process covariance values (2.4e-1 for velocity based on 3 x std dev of 0.16)

# Offset variance extremely high out of caution and proof of convergence
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove comment

@@ -1,4 +1,4 @@
output_bags:
- uri: nand-raceline-mcap
- uri: rosbag2_2025_11_15-12_47_46_0
Copy link
Contributor

@mehulgoel873 mehulgoel873 Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will revert, but basically a script to convert bags to .mcap (needs bags uri to work) @hpuranik

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Short Circuit UKF for steering offset

8 participants