Add swerve rotation module debug plot
Change-Id: Id915b5abff7ccea1a3119c4a6889ebf9cdf1b5a9
Signed-off-by: James Kuszmaul <jabukuszmaul+collab@gmail.com>
diff --git a/y2024_swerve/control_loops/BUILD b/y2024_swerve/control_loops/BUILD
new file mode 100644
index 0000000..67fa349
--- /dev/null
+++ b/y2024_swerve/control_loops/BUILD
@@ -0,0 +1,13 @@
+load("//tools/build_rules:js.bzl", "ts_project")
+
+ts_project(
+ name = "swerve_plotter",
+ srcs = ["swerve_plotter.ts"],
+ target_compatible_with = ["@platforms//os:linux"],
+ visibility = ["//visibility:public"],
+ deps = [
+ "//aos/network/www:aos_plotter",
+ "//aos/network/www:colors",
+ "//aos/network/www:proxy",
+ ],
+)
diff --git a/y2024_swerve/control_loops/swerve_plotter.ts b/y2024_swerve/control_loops/swerve_plotter.ts
new file mode 100644
index 0000000..0f103f3
--- /dev/null
+++ b/y2024_swerve/control_loops/swerve_plotter.ts
@@ -0,0 +1,130 @@
+// Provides a plot for debugging robot state-related issues.
+import {AosPlotter, MessageHandler} from '../../aos/network/www/aos_plotter';
+import {BLUE, BROWN, CYAN, GREEN, PINK, RED, WHITE} from '../../aos/network/www/colors';
+import * as proxy from '../../aos/network/www/proxy';
+
+import Connection = proxy.Connection;
+
+const TIME = AosPlotter.TIME;
+const DEFAULT_WIDTH = AosPlotter.DEFAULT_WIDTH * 2;
+const DEFAULT_HEIGHT = AosPlotter.DEFAULT_HEIGHT * 1;
+
+function plotModule(
+ name: string, plotter: AosPlotter, element: Element, position: MessageHandler, positionName: string[],
+ status: MessageHandler, statusName: string[], output: MessageHandler, outputName: string[]): void {
+ {
+ const positionPlot =
+ plotter.addPlot(element, [DEFAULT_WIDTH, DEFAULT_HEIGHT]);
+ positionPlot.plot.getAxisLabels().setTitle(name + ' Position');
+ positionPlot.plot.getAxisLabels().setXLabel(TIME);
+ positionPlot.plot.getAxisLabels().setYLabel('Position [rad,m]');
+ positionPlot.addMessageLine(position, positionName .concat( ['encoder']))
+ .setColor(RED);
+ positionPlot.addMessageLine(position, positionName .concat( ['absolute_encoder']))
+ .setColor(GREEN);
+ positionPlot
+ .addMessageLine(status, statusName .concat( [ 'estimator_state', 'position']))
+ .setColor(BROWN);
+ positionPlot.addMessageLine(status, statusName .concat( [ 'position']))
+ .setColor(WHITE);
+ }
+ {
+ const statesPlot =
+ plotter.addPlot(element, [DEFAULT_WIDTH, DEFAULT_HEIGHT / 2]);
+ statesPlot.plot.getAxisLabels().setTitle(name + ' State');
+ statesPlot.plot.getAxisLabels().setXLabel(TIME);
+ statesPlot.plot.getAxisLabels().setYLabel('[bool,ZeroingError]');
+ statesPlot.addMessageLine(status, statusName .concat( [ 'estopped'])).setColor(RED);
+ statesPlot.addMessageLine(
+ status, statusName.concat(['zeroed'])).setColor(GREEN);
+ statesPlot
+ .addMessageLine(status, statusName .concat( [ 'estimator_state', 'errors[]']))
+ .setColor(BLUE)
+ .setDrawLine(false);
+ }
+ {
+ const positionConvergencePlot =
+ plotter.addPlot(element, [DEFAULT_WIDTH, DEFAULT_HEIGHT]);
+ positionConvergencePlot.plot.getAxisLabels().setTitle(name + ' Position Goals');
+ positionConvergencePlot.plot.getAxisLabels().setXLabel(TIME);
+ positionConvergencePlot.plot.getAxisLabels().setYLabel('[rad,m]');
+ positionConvergencePlot.addMessageLine(status, statusName .concat( [ 'position']))
+ .setColor(RED);
+ positionConvergencePlot.addMessageLine(status, statusName .concat( [ 'goal_position']))
+ .setColor(GREEN);
+ positionConvergencePlot
+ .addMessageLine(status, statusName .concat( [ 'unprofiled_goal_position']))
+ .setColor(BROWN);
+ }
+ {
+ const velocityConvergencePlot =
+ plotter.addPlot(element, [DEFAULT_WIDTH, DEFAULT_HEIGHT]);
+ velocityConvergencePlot.plot.getAxisLabels().setTitle(name + ' Velocity Goals');
+ velocityConvergencePlot.plot.getAxisLabels().setXLabel(TIME);
+ velocityConvergencePlot.plot.getAxisLabels().setYLabel('[rad,m]');
+ velocityConvergencePlot.addMessageLine(status, statusName .concat( [ 'velocity']))
+ .setColor(RED);
+ velocityConvergencePlot.addMessageLine(status, statusName .concat( [ 'calculated_velocity']))
+ .setColor(RED).setDrawLine(false);
+ velocityConvergencePlot.addMessageLine(status, statusName .concat( [ 'goal_velocity']))
+ .setColor(GREEN);
+ velocityConvergencePlot
+ .addMessageLine(status, statusName .concat( [ 'unprofiled_goal_velocity']))
+ .setColor(BROWN);
+ }
+ {
+ const outputPlot =
+ plotter.addPlot(element, [DEFAULT_WIDTH, DEFAULT_HEIGHT]);
+ outputPlot.plot.getAxisLabels().setTitle(name + ' Outputs');
+ outputPlot.plot.getAxisLabels().setXLabel(TIME);
+ outputPlot.plot.getAxisLabels().setYLabel('[volts]');
+ outputPlot.addMessageLine(output, outputName)
+ .setColor(RED);
+ outputPlot.addMessageLine(status, statusName .concat( [ 'voltage_error']))
+ .setColor(GREEN);
+ outputPlot.addMessageLine(status, statusName .concat( [ 'position_power']))
+ .setColor(BLUE);
+ outputPlot.addMessageLine(status, statusName .concat( [ 'velocity_power']))
+ .setColor(BROWN);
+ outputPlot.addMessageLine(status, statusName .concat( [ 'feedforwards_power']))
+ .setColor(WHITE);
+ }
+}
+
+export function plotSwerve(conn: Connection, element: Element): void {
+ const aosPlotter = new AosPlotter(conn);
+ const robotState = aosPlotter.addMessageSource('/aos', 'aos.RobotState');
+
+ {
+ const robotStatePlot =
+ aosPlotter.addPlot(element, [DEFAULT_WIDTH, DEFAULT_HEIGHT]);
+ robotStatePlot.plot.getAxisLabels().setTitle('Robot State Plot');
+ robotStatePlot.plot.getAxisLabels().setXLabel(TIME);
+ robotStatePlot.plot.getAxisLabels().setYLabel('[bool]');
+ robotStatePlot.addMessageLine(robotState, ['outputs_enabled'])
+ .setColor(RED);
+ }
+
+ const goal = aosPlotter.addMessageSource(
+ '/drivetrain', 'frc971.control_loops.swerve.Goal');
+ const output = aosPlotter.addMessageSource(
+ '/drivetrain', 'frc971.control_loops.swerve.Output');
+ const status = aosPlotter.addMessageSource(
+ '/drivetrain', 'frc971.control_loops.swerve.Status');
+ const position = aosPlotter.addMessageSource(
+ '/drivetrain', 'frc971.control_loops.swerve.Position');
+
+ plotModule(
+ 'Front Left', aosPlotter, element, position, ['front_left', 'rotation_position'], status, ['front_left_status', 'rotation'],
+ output, ['front_left_output', 'rotation_current']);
+ plotModule(
+ 'Front Right', aosPlotter, element, position, ['front_right', 'rotation_position'], status, ['front_right_status', 'rotation'],
+ output, ['front_right_output', 'rotation_current']);
+ plotModule(
+ 'Back Left', aosPlotter, element, position, ['back_left', 'rotation_position'], status, ['back_left_status', 'rotation'],
+ output, ['back_left_output', 'rotation_current']);
+ // TODO(james): Chrome runs out of WebGL canvasses when attempting to plot the final module....
+// plotModule(
+// 'Back Right', aosPlotter, element, position, ['back_right', 'rotation_position'], status, ['back_right_status', 'rotation'],
+// output, ['back_right_output', 'rotation_current']);
+}