YAMS
Yet Another Mechanism System
Loading...
Searching...
No Matches
SwerveInputStream.hpp
Go to the documentation of this file.
1// Copyright (c) 2026 Yet Another Software Suite
2// SPDX-License-Identifier: LGPL-3.0-or-later
3
4#pragma once
5
6#include <cmath>
7#include <functional>
8#include <iostream>
9#include <memory>
10#include <numbers>
11#include <optional>
12#include <stdexcept>
13#include <string>
14#include <utility>
15#include <wpi/driverstation/Alliance.hpp>
16#include <wpi/driverstation/MatchState.hpp>
17#include <wpi/math/controller/SimpleMotorFeedforward.hpp>
18#include <wpi/math/geometry/Pose2d.hpp>
19#include <wpi/math/geometry/Rotation2d.hpp>
20#include <wpi/math/geometry/Translation2d.hpp>
21#include <wpi/math/kinematics/ChassisVelocities.hpp>
22#include <wpi/math/util/MathUtil.hpp>
23#include <wpi/units/angle.hpp>
24#include <wpi/units/angular_velocity.hpp>
25#include <wpi/units/length.hpp>
26#include <wpi/units/math.hpp>
27#include <wpi/units/time.hpp>
28#include <wpi/units/velocity.hpp>
29
30#include "yams/exceptions.hpp"
33
35
36template <size_t NumModules>
38
39template <size_t NumModules = 4>
41 public:
43
44 static SwerveInputStream Of(SwerveDrive<NumModules>& drive, std::function<double()> x,
45 std::function<double()> y) {
46 return SwerveInputStream{drive, std::move(x), std::move(y)};
47 }
48
49 SwerveInputStream(SwerveDrive<NumModules>& drive, std::function<double()> x,
50 std::function<double()> y, std::function<double()> rot)
51 : SwerveInputStream{drive, std::move(x), std::move(y)} {
52 m_controllerOmega = std::move(rot);
53 }
54
55 SwerveInputStream(SwerveDrive<NumModules>& drive, std::function<double()> x,
56 std::function<double()> y, std::function<double()> headingX,
57 std::function<double()> headingY)
58 : SwerveInputStream{drive, std::move(x), std::move(y)} {
59 WithControllerHeadingAxis(std::move(headingX), std::move(headingY));
60 }
61
67 SwerveInputStream clone = *this;
68 clone.m_telemetry.settings.reset();
69 return clone;
70 }
71
85 SwerveInputStream& WithTelemetry(std::string name, TelemetryVerbosity verbosity) {
86 m_telemetry.telemetry.reset();
87 if (verbosity == TelemetryVerbosity::NONE) {
88 m_telemetry.settings.reset();
89 } else {
90 m_telemetry.settings.emplace(std::move(name), verbosity);
91 }
92 return *this;
93 }
94
100 std::optional<std::reference_wrapper<SwerveInputStreamTelemetry<NumModules>>> GetTelemetry() {
101 if (!m_telemetry.settings) {
102 return std::nullopt;
103 }
104 if (!m_telemetry.telemetry) {
105 m_telemetry.telemetry = std::make_unique<SwerveInputStreamTelemetry<NumModules>>(
106 *this, m_telemetry.settings->first, m_telemetry.settings->second);
107 }
108 return std::ref(*m_telemetry.telemetry);
109 }
110
111 std::string GetCurrentModeName() const {
112 switch (m_currentMode) {
113 case SwerveInputMode::TRANSLATION_ONLY:
114 return "TRANSLATION_ONLY";
115 case SwerveInputMode::ANGULAR_VELOCITY:
116 return "ANGULAR_VELOCITY";
117 case SwerveInputMode::HEADING:
118 return "HEADING";
119 case SwerveInputMode::AIM:
120 return "AIM";
121 }
122 return "UNKNOWN";
123 }
124
125 double GetAxisDeadband() const { return m_axisDeadband.value_or(0.0); }
126 void SetAxisDeadband(double deadband) {
127 m_axisDeadband = (deadband == 0.0) ? std::nullopt : std::optional<double>{deadband};
128 }
129
130 double GetTranslationAxisScale() const { return m_translationAxisScale.value_or(1.0); }
131 void SetTranslationAxisScale(double scale) {
132 m_translationAxisScale = (scale == 0.0) ? std::nullopt : std::optional<double>{scale};
133 }
134
135 double GetOmegaAxisScale() const { return m_omegaAxisScale.value_or(1.0); }
136 void SetOmegaAxisScale(double scale) {
137 m_omegaAxisScale = (scale == 0.0) ? std::nullopt : std::optional<double>{scale};
138 }
139
140 wpi::units::meters_per_second_t GetMaximumChassisLinearVelocity() const {
141 return m_maximumChassisLinearVelocity;
142 }
143 void SetMaximumChassisLinearVelocity(wpi::units::meters_per_second_t velocity) {
144 m_maximumChassisLinearVelocity = velocity;
145 }
146
147 wpi::units::radians_per_second_t GetMaximumChassisAngularVelocity() const {
148 return m_maximumChassisAngularVelocity;
149 }
150 void SetMaximumChassisAngularVelocity(wpi::units::radians_per_second_t velocity) {
151 m_maximumChassisAngularVelocity = velocity;
152 }
153
155 return m_translationCube.has_value() && m_translationCube.value()();
156 }
157 void SetTranslationCubeEnabled(bool enabled) {
158 m_translationCube = enabled ? std::optional<std::function<bool()>>{[] { return true; }} : std::nullopt;
159 }
160
161 bool IsOmegaCubeEnabled() const { return m_omegaCube.has_value() && m_omegaCube.value()(); }
162 void SetOmegaCubeEnabled(bool enabled) {
163 m_omegaCube = enabled ? std::optional<std::function<bool()>>{[] { return true; }} : std::nullopt;
164 }
165
167 return m_allianceRelative.has_value() && m_allianceRelative.value()();
168 }
169 void SetAllianceRelativeEnabled(bool enabled) {
170 m_allianceRelative = enabled ? std::optional<std::function<bool()>>{[] { return true; }} : std::nullopt;
171 }
172
173 bool IsRobotRelativeEnabled() const { return m_robotRelative.has_value() && m_robotRelative.value()(); }
174 void SetRobotRelativeEnabled(bool enabled) {
175 m_robotRelative = enabled ? std::optional<std::function<bool()>>{[] { return true; }} : std::nullopt;
176 }
177
178 SwerveInputStream& WithMaximumLinearVelocity(wpi::units::meters_per_second_t velocity) {
179 m_maximumChassisLinearVelocity = velocity;
180 return *this;
181 }
182
183 SwerveInputStream& WithMaximumAngularVelocity(wpi::units::radians_per_second_t velocity) {
184 m_maximumChassisAngularVelocity = velocity;
185 return *this;
186 }
187
188 SwerveInputStream& WithControllerRotationAxis(std::function<double()> rot) {
189 m_controllerOmega = std::move(rot);
190 return *this;
191 }
192
193 SwerveInputStream& WithControllerHeadingAxis(std::function<double()> headingX,
194 std::function<double()> headingY) {
195 WithHeading([headingX, headingY] {
196 return wpi::units::radian_t{std::atan2(headingX(), headingY())};
197 });
198 m_controllerHeadingX = std::move(headingX);
199 m_controllerHeadingY = std::move(headingY);
200 return *this;
201 }
202
203 SwerveInputStream& WithDeadband(double deadband) {
204 m_axisDeadband = (deadband == 0.0) ? std::nullopt : std::optional<double>{deadband};
205 return *this;
206 }
207
209 SwerveInputStream& Deadband(double deadband) { return WithDeadband(deadband); }
210
212 m_translationAxisScale = (scale == 0.0) ? std::nullopt : std::optional<double>{scale};
213 return *this;
214 }
215
217 m_omegaAxisScale = (scale == 0.0) ? std::nullopt : std::optional<double>{scale};
218 return *this;
219 }
220
221 SwerveInputStream& WithHeadingControl(std::function<bool()> trigger) {
222 m_headingEnabled = std::move(trigger);
223 return *this;
224 }
225
226 SwerveInputStream& WithHeading(std::function<wpi::units::radian_t()> heading) {
227 m_headingSupplier = std::move(heading);
228 m_controllerHeadingX.reset();
229 m_controllerHeadingY.reset();
230 return *this;
231 }
232
233 SwerveInputStream& WithAim(std::function<wpi::math::Pose2d()> aimTarget,
234 std::function<bool()> trigger) {
235 m_aimTarget = std::move(aimTarget);
236 m_aimEnabled = std::move(trigger);
237 return *this;
238 }
239
252 SwerveInputStream& WithAimLookahead(wpi::units::second_t lookahead) {
253 m_aimLookahead = lookahead;
254 return *this;
255 }
256
263 wpi::math::SimpleMotorFeedforward<wpi::units::radians> feedforward) {
264 m_aimFeedforward = feedforward;
265 return *this;
266 }
267
272 SwerveInputStream& WithAimHeadingOffset(wpi::math::Rotation2d offset,
273 std::function<bool()> enabled) {
274 m_aimHeadingOffset = offset;
275 m_aimHeadingOffsetEnabled = std::move(enabled);
276 return *this;
277 }
278
279 SwerveInputStream& WithAimHeadingOffset(wpi::math::Rotation2d offset) {
280 return WithAimHeadingOffset(offset, [] { return true; });
281 }
282
287 bool IsAimLocked(wpi::units::radian_t tolerance) {
288 if (!m_aimEnabled.has_value() || !m_aimEnabled.value()() || !m_aimGoalHeading.has_value()) {
289 return false;
290 }
291 auto currentHeading =
292 wpi::math::Rotation2d{wpi::units::radian_t{m_swerveDrive->GetGyroAngle()}};
293 return wpi::units::math::abs((m_aimGoalHeading.value() - currentHeading).Radians()) <=
294 tolerance;
295 }
296
301 wpi::math::Translation2d GetTargetVector(const wpi::math::Pose2d& target) {
302 auto robot = m_swerveDrive->GetPose().Translation();
303 if (m_aimLookahead.has_value()) {
304 auto fieldVelocity = m_swerveDrive->GetFieldRelativeSpeed();
305 robot = robot + wpi::math::Translation2d{fieldVelocity.vx * m_aimLookahead.value(),
306 fieldVelocity.vy * m_aimLookahead.value()};
307 }
308 return target.Translation() - robot;
309 }
310
315 wpi::units::radians_per_second_t CalculateAngularVelocity(wpi::units::radian_t target) {
316 double omega = RequireRotationPID().Calculate(
317 wpi::math::Rotation2d{wpi::units::radian_t{m_swerveDrive->GetGyroAngle()}}.Radians().value(),
318 target.value());
319 if (m_aimFeedforward.has_value()) {
320 omega += m_aimFeedforward
321 ->Calculate(m_swerveDrive->GetRobotRelativeSpeed().omega,
322 wpi::units::radians_per_second_t{omega})
323 .value();
324 }
325 return wpi::units::radians_per_second_t{omega};
326 }
327
328 SwerveInputStream& WithTranslationOnly(std::function<bool()> trigger) {
329 m_translationOnlyEnabled = std::move(trigger);
330 return *this;
331 }
332
333 SwerveInputStream& WithCubeRotationControllerAxis(std::function<bool()> enabled) {
334 m_omegaCube = std::move(enabled);
335 return *this;
336 }
337
341
342 SwerveInputStream& WithCubeTranslationControllerAxis(std::function<bool()> enabled) {
343 m_translationCube = std::move(enabled);
344 return *this;
345 }
346
350
351 SwerveInputStream& WithAllianceRelativeControl(std::function<bool()> enabled) {
352 m_allianceRelative = std::move(enabled);
353 return *this;
354 }
355
359
360 SwerveInputStream& WithRobotRelative(std::function<bool()> enabled) {
361 m_robotRelative = std::move(enabled);
362 return *this;
363 }
364
366 return WithRobotRelative([] { return true; });
367 }
368
370 std::function<bool()> enabled) {
371 m_translationHeadingOffset = angle;
372 m_translationHeadingOffsetEnabled = std::move(enabled);
373 return *this;
374 }
375
376 SwerveInputStream& WithTranslationHeadingOffset(wpi::math::Rotation2d angle) {
377 return WithTranslationHeadingOffset(angle, [] { return true; });
378 }
379
380 wpi::math::ChassisVelocities Get() {
381 // Starts from the drive config's maximums (see the constructor); WithMaximum*Velocity and live
382 // tuning override them.
383 double maxLinear = m_maximumChassisLinearVelocity.value();
384 wpi::units::radians_per_second_t maxAngular = m_maximumChassisAngularVelocity;
385
386 wpi::math::Translation2d scaledTranslation = ApplyTranslationScalar(
387 ApplyDeadband(m_controllerTranslationX()), ApplyDeadband(m_controllerTranslationY()));
388 scaledTranslation = ApplyTranslationCube(scaledTranslation);
389 scaledTranslation = ApplyAllianceAwareTranslation(scaledTranslation);
390
391 double vx = scaledTranslation.X().value() * maxLinear;
392 double vy = scaledTranslation.Y().value() * maxLinear;
393 double omega = 0.0;
394 wpi::math::ChassisVelocities speeds{};
395
396 SwerveInputMode newMode = FindMode();
397 if (m_currentMode != newMode) {
398 TransitionMode(newMode);
399 }
400
401 switch (newMode) {
402 case SwerveInputMode::TRANSLATION_ONLY: {
403 auto& pid = RequireRotationPID();
404 omega = pid.Calculate(wpi::units::radian_t{m_swerveDrive->GetGyroAngle()}.value(),
405 m_lockedHeading.value().Radians().value());
406 break;
407 }
408 case SwerveInputMode::ANGULAR_VELOCITY: {
409 omega = ApplyOmegaCube(ApplyRotationalScalar(ApplyDeadband(m_controllerOmega.value()()))) *
410 maxAngular.value();
411 break;
412 }
413 case SwerveInputMode::HEADING: {
414 auto& pid = RequireRotationPID();
415 omega = pid.Calculate(wpi::units::radian_t{m_swerveDrive->GetGyroAngle()}.value(),
416 m_headingSupplier.value()().value());
417 if (m_controllerHeadingX.has_value() && m_controllerHeadingY.has_value() &&
418 m_axisDeadband.has_value() &&
419 std::abs(m_controllerHeadingX.value()()) + std::abs(m_controllerHeadingY.value()()) <
420 m_axisDeadband.value()) {
421 omega = 0.0;
422 }
423 break;
424 }
425 case SwerveInputMode::AIM: {
426 auto currentHeading =
427 wpi::math::Rotation2d{wpi::units::radian_t{m_swerveDrive->GetGyroAngle()}};
428 auto target = currentHeading;
429 // The vector is field relative. Turn it into a heading in the gyro's frame.
430 if (auto fieldAngle = GetTargetVector(m_aimTarget.value()()).Angle()) {
431 target = fieldAngle.value() - m_swerveDrive->GetPose().Rotation() + currentHeading;
432 }
433 if (m_aimHeadingOffset.has_value() && m_aimHeadingOffsetEnabled.has_value() &&
434 m_aimHeadingOffsetEnabled.value()()) {
435 target = target + m_aimHeadingOffset.value();
436 }
437 m_aimGoalHeading = target;
438 omega = CalculateAngularVelocity(target.Radians()).value();
439 break;
440 }
441 }
442
443 m_currentMode = newMode;
444 speeds = wpi::math::ChassisVelocities{wpi::units::meters_per_second_t{vx},
445 wpi::units::meters_per_second_t{vy},
446 wpi::units::radians_per_second_t{omega}};
447 auto fieldRelativeSpeeds = ApplyTranslationHeadingOffset(ApplyRobotRelativeTranslation(speeds));
448 if (auto telemetry = GetTelemetry()) {
449 telemetry->get().UpdateTelemetry();
450 }
451 return fieldRelativeSpeeds;
452 }
453
454 wpi::math::ChassisVelocities operator()() { return Get(); }
455
456 private:
457 enum class SwerveInputMode {
458 TRANSLATION_ONLY,
459 ANGULAR_VELOCITY,
460 HEADING,
461 AIM,
462 };
463
464 SwerveInputStream(SwerveDrive<NumModules>& drive, std::function<double()> x,
465 std::function<double()> y)
466 : m_swerveDrive{&drive},
467 m_controllerTranslationX{std::move(x)},
468 m_controllerTranslationY{std::move(y)} {
469 auto& cfg = drive.GetConfig();
470 if (auto maxLinear = cfg.GetMaximumChassisLinearVelocity()) {
471 m_maximumChassisLinearVelocity = *maxLinear;
472 }
473 if (auto maxAngular = cfg.GetMaximumChassisAngularVelocity()) {
474 m_maximumChassisAngularVelocity = wpi::units::radians_per_second_t{*maxAngular};
475 }
476 }
477
478 SwerveDrive<NumModules>* m_swerveDrive;
479 std::function<double()> m_controllerTranslationX;
480 std::function<double()> m_controllerTranslationY;
481 std::optional<std::function<double()>> m_controllerOmega;
482 std::optional<std::function<double()>> m_controllerHeadingX;
483 std::optional<std::function<double()>> m_controllerHeadingY;
484 std::optional<std::function<wpi::units::radian_t()>> m_headingSupplier;
485 std::optional<double> m_axisDeadband;
486 std::optional<double> m_translationAxisScale;
487 std::optional<double> m_omegaAxisScale;
488 std::optional<std::function<wpi::math::Pose2d()>> m_aimTarget;
489 std::optional<std::function<bool()>> m_headingEnabled;
490 std::optional<wpi::math::Rotation2d> m_lockedHeading;
491 std::optional<std::function<bool()>> m_aimEnabled;
492 std::optional<std::function<bool()>> m_translationOnlyEnabled;
493 std::optional<std::function<bool()>> m_translationCube;
494 std::optional<std::function<bool()>> m_omegaCube;
495 std::optional<std::function<bool()>> m_robotRelative;
496 std::optional<std::function<bool()>> m_allianceRelative;
497 std::optional<std::function<bool()>> m_translationHeadingOffsetEnabled;
498 std::optional<wpi::math::Rotation2d> m_translationHeadingOffset;
499 std::optional<wpi::units::second_t> m_aimLookahead;
500 std::optional<wpi::math::SimpleMotorFeedforward<wpi::units::radians>> m_aimFeedforward;
501 std::optional<wpi::math::Rotation2d> m_aimHeadingOffset;
502 std::optional<std::function<bool()>> m_aimHeadingOffsetEnabled;
503 // Heading the robot is turning to in AIM, in the gyro's frame.
504 std::optional<wpi::math::Rotation2d> m_aimGoalHeading;
505 SwerveInputMode m_currentMode{SwerveInputMode::ANGULAR_VELOCITY};
506 wpi::units::meters_per_second_t m_maximumChassisLinearVelocity{4.0};
507 wpi::units::radians_per_second_t m_maximumChassisAngularVelocity{2.0 * std::numbers::pi};
508
514 struct TelemetryHolder {
515 std::optional<std::pair<std::string, TelemetryVerbosity>> settings;
516 std::unique_ptr<SwerveInputStreamTelemetry<NumModules>> telemetry;
517
518 TelemetryHolder() = default;
519 TelemetryHolder(const TelemetryHolder& other) : settings{other.settings} {}
520 TelemetryHolder(TelemetryHolder&& other) noexcept : settings{std::move(other.settings)} {
521 other.settings.reset();
522 other.telemetry.reset();
523 }
524 TelemetryHolder& operator=(const TelemetryHolder& other) {
525 if (this != &other) {
526 settings = other.settings;
527 telemetry.reset();
528 }
529 return *this;
530 }
531 TelemetryHolder& operator=(TelemetryHolder&& other) noexcept {
532 if (this != &other) {
533 settings = std::move(other.settings);
534 telemetry.reset();
535 other.settings.reset();
536 other.telemetry.reset();
537 }
538 return *this;
539 }
540 };
541 TelemetryHolder m_telemetry;
542
543 wpi::math::PIDController& RequireRotationPID() {
544 auto pid = m_swerveDrive->GetConfig().GetRotationPID();
545 if (!pid) {
546 throw exceptions::SwerveDriveConfigurationException(
547 "No rotation PID controller configured, heading, aim, and translation only control are "
548 "unavailable. Use SwerveDriveConfig::WithRotationController(PIDController) to fix this "
549 "error.");
550 }
551 return pid->get();
552 }
553
554 SwerveInputMode FindMode() {
555 if (m_translationOnlyEnabled.has_value() && m_translationOnlyEnabled.value()()) {
556 return SwerveInputMode::TRANSLATION_ONLY;
557 }
558 if (m_aimEnabled.has_value() && m_aimEnabled.value()()) {
559 if (m_aimTarget.has_value()) {
560 return SwerveInputMode::AIM;
561 }
562 std::cerr << "[YAMS SwerveInputStream] AIM mode enabled but no target set. Call WithAim() first.\n";
563 }
564 if (m_headingEnabled.has_value() && m_headingEnabled.value()()) {
565 if (m_headingSupplier.has_value()) {
566 return SwerveInputMode::HEADING;
567 }
568 std::cerr << "[YAMS SwerveInputStream] HEADING mode enabled but no heading set. "
569 "Call WithHeading() or WithControllerHeadingAxis() first.\n";
570 }
571 if (!m_controllerOmega.has_value()) {
572 std::cerr << "[YAMS SwerveInputStream] No rotation axis configured. "
573 "Call WithControllerRotationAxis() or WithControllerHeadingAxis(). "
574 "Falling back to TRANSLATION_ONLY.\n";
575 return SwerveInputMode::TRANSLATION_ONLY;
576 }
577 return SwerveInputMode::ANGULAR_VELOCITY;
578 }
579
580 void TransitionMode(SwerveInputMode newMode) {
581 if (m_currentMode == SwerveInputMode::AIM) {
582 m_aimGoalHeading = std::nullopt;
583 }
584 switch (m_currentMode) {
585 case SwerveInputMode::TRANSLATION_ONLY:
586 m_lockedHeading = std::nullopt;
587 break;
588 case SwerveInputMode::HEADING:
589 case SwerveInputMode::AIM:
590 m_swerveDrive->ResetRotationPID();
591 break;
592 default:
593 break;
594 }
595 switch (newMode) {
596 case SwerveInputMode::TRANSLATION_ONLY:
597 m_lockedHeading =
598 wpi::math::Rotation2d{wpi::units::radian_t{m_swerveDrive->GetGyroAngle()}};
599 m_swerveDrive->ResetRotationPID();
600 break;
601 case SwerveInputMode::HEADING:
602 case SwerveInputMode::AIM:
603 m_swerveDrive->ResetRotationPID();
604 break;
605 default:
606 break;
607 }
608 }
609
610 double ApplyDeadband(double axisValue) {
611 return m_axisDeadband.has_value() ? wpi::math::ApplyDeadband(axisValue, m_axisDeadband.value())
612 : axisValue;
613 }
614
615 double ApplyRotationalScalar(double axisValue) {
616 return m_omegaAxisScale.has_value() ? axisValue * m_omegaAxisScale.value() : axisValue;
617 }
618
619 wpi::math::Translation2d ApplyTranslationScalar(double xAxis, double yAxis) {
620 wpi::math::Translation2d t{wpi::units::meter_t{xAxis}, wpi::units::meter_t{yAxis}};
621 return m_translationAxisScale.has_value()
622 ? SwerveDriveConfig::ScaleTranslation(t, m_translationAxisScale.value())
623 : t;
624 }
625
626 wpi::math::Translation2d ApplyTranslationCube(wpi::math::Translation2d translation) {
627 if (m_translationCube.has_value() && m_translationCube.value()()) {
628 return SwerveDriveConfig::CubeTranslation(translation);
629 }
630 return translation;
631 }
632
633 double ApplyOmegaCube(double rotationAxis) {
634 if (m_omegaCube.has_value() && m_omegaCube.value()()) {
635 return rotationAxis * rotationAxis * rotationAxis;
636 }
637 return rotationAxis;
638 }
639
640 wpi::math::ChassisVelocities ApplyRobotRelativeTranslation(wpi::math::ChassisVelocities speeds) {
641 if (m_robotRelative.has_value() && m_robotRelative.value()()) {
642 return speeds.ToFieldRelative(
643 wpi::math::Rotation2d{wpi::units::radian_t{m_swerveDrive->GetGyroAngle()}});
644 }
645 return speeds;
646 }
647
648 wpi::math::Translation2d ApplyAllianceAwareTranslation(wpi::math::Translation2d translation) {
649 if (m_robotRelative.has_value() && m_robotRelative.value()()) {
650 return translation;
651 }
652 if (m_allianceRelative.has_value() && m_allianceRelative.value()()) {
653 auto alliance = wpi::MatchState::GetAlliance();
654 if (alliance.has_value() && alliance.value() == wpi::Alliance::RED) {
655 return translation.RotateBy(wpi::math::Rotation2d{wpi::units::degree_t{180.0}});
656 }
657 }
658 return translation;
659 }
660
661 wpi::math::ChassisVelocities ApplyTranslationHeadingOffset(wpi::math::ChassisVelocities speeds) {
662 if (m_translationHeadingOffsetEnabled.has_value() &&
663 m_translationHeadingOffsetEnabled.value()() && m_translationHeadingOffset.has_value()) {
664 wpi::math::Translation2d vec{wpi::units::meter_t{speeds.vx.value()},
665 wpi::units::meter_t{speeds.vy.value()}};
666 auto rotated = vec.RotateBy(m_translationHeadingOffset.value());
667 return wpi::math::ChassisVelocities{wpi::units::meters_per_second_t{rotated.X().value()},
668 wpi::units::meters_per_second_t{rotated.Y().value()},
669 speeds.omega};
670 }
671 return speeds;
672 }
673};
674
675} // namespace yams::mechanisms::swerve::utility
676
677// The telemetry is a template too, so it only needs to be complete where a stream is used.
static wpi::math::Translation2d CubeTranslation(wpi::math::Translation2d translation)
static wpi::math::Translation2d ScaleTranslation(wpi::math::Translation2d translation, double scalar)
motorcontrollers::SmartMotorControllerConfig::TelemetryVerbosity TelemetryVerbosity
Definition SwerveDriveConfig.hpp:40
Definition SwerveDrive.hpp:136
wpi::math::ChassisVelocities GetFieldRelativeSpeed()
Definition SwerveDrive.hpp:617
wpi::units::degree_t GetGyroAngle()
Definition SwerveDrive.hpp:569
wpi::math::Pose2d GetPose()
Definition SwerveDrive.hpp:458
SwerveDriveConfig & GetConfig()
Definition SwerveDrive.hpp:726
wpi::math::ChassisVelocities GetRobotRelativeSpeed()
Definition SwerveDrive.hpp:612
Definition SwerveInputStreamTelemetry.hpp:47
Definition SwerveInputStream.hpp:40
SwerveInputStream(SwerveDrive< NumModules > &drive, std::function< double()> x, std::function< double()> y, std::function< double()> headingX, std::function< double()> headingY)
Definition SwerveInputStream.hpp:55
bool IsTranslationCubeEnabled() const
Definition SwerveInputStream.hpp:154
bool IsAimLocked(wpi::units::radian_t tolerance)
Definition SwerveInputStream.hpp:287
SwerveInputStream & WithAimHeadingOffset(wpi::math::Rotation2d offset, std::function< bool()> enabled)
Definition SwerveInputStream.hpp:272
wpi::math::Translation2d GetTargetVector(const wpi::math::Pose2d &target)
Definition SwerveInputStream.hpp:301
SwerveInputStream & WithDeadband(double deadband)
Definition SwerveInputStream.hpp:203
void SetTranslationCubeEnabled(bool enabled)
Definition SwerveInputStream.hpp:157
SwerveInputStream & WithControllerRotationAxis(std::function< double()> rot)
Definition SwerveInputStream.hpp:188
SwerveInputStream & WithCubeRotationControllerAxis()
Definition SwerveInputStream.hpp:338
SwerveInputStream & WithAim(std::function< wpi::math::Pose2d()> aimTarget, std::function< bool()> trigger)
Definition SwerveInputStream.hpp:233
SwerveInputStream & WithMaximumAngularVelocity(wpi::units::radians_per_second_t velocity)
Definition SwerveInputStream.hpp:183
void SetAxisDeadband(double deadband)
Definition SwerveInputStream.hpp:126
SwerveInputStream Clone() const
Definition SwerveInputStream.hpp:66
void SetRobotRelativeEnabled(bool enabled)
Definition SwerveInputStream.hpp:174
SwerveInputStream & WithTranslationHeadingOffset(wpi::math::Rotation2d angle, std::function< bool()> enabled)
Definition SwerveInputStream.hpp:369
bool IsRobotRelativeEnabled() const
Definition SwerveInputStream.hpp:173
SwerveInputStream & Deadband(double deadband)
Definition SwerveInputStream.hpp:209
SwerveInputStream & WithAimHeadingOffset(wpi::math::Rotation2d offset)
Definition SwerveInputStream.hpp:279
wpi::units::radians_per_second_t CalculateAngularVelocity(wpi::units::radian_t target)
Definition SwerveInputStream.hpp:315
SwerveInputStream & WithScaleRotation(double scale)
Definition SwerveInputStream.hpp:216
void SetMaximumChassisAngularVelocity(wpi::units::radians_per_second_t velocity)
Definition SwerveInputStream.hpp:150
SwerveInputStream & WithTranslationOnly(std::function< bool()> trigger)
Definition SwerveInputStream.hpp:328
wpi::units::meters_per_second_t GetMaximumChassisLinearVelocity() const
Definition SwerveInputStream.hpp:140
wpi::math::ChassisVelocities Get()
Definition SwerveInputStream.hpp:380
SwerveInputStream & WithControllerHeadingAxis(std::function< double()> headingX, std::function< double()> headingY)
Definition SwerveInputStream.hpp:193
SwerveInputStream & WithRobotRelative()
Definition SwerveInputStream.hpp:365
wpi::math::ChassisVelocities operator()()
Definition SwerveInputStream.hpp:454
double GetOmegaAxisScale() const
Definition SwerveInputStream.hpp:135
SwerveInputStream & WithCubeTranslationControllerAxis()
Definition SwerveInputStream.hpp:347
void SetMaximumChassisLinearVelocity(wpi::units::meters_per_second_t velocity)
Definition SwerveInputStream.hpp:143
SwerveInputStream & WithTelemetry(std::string name, TelemetryVerbosity verbosity)
Definition SwerveInputStream.hpp:85
void SetOmegaCubeEnabled(bool enabled)
Definition SwerveInputStream.hpp:162
std::optional< std::reference_wrapper< SwerveInputStreamTelemetry< NumModules > > > GetTelemetry()
Definition SwerveInputStream.hpp:100
SwerveInputStream & WithRobotRelative(std::function< bool()> enabled)
Definition SwerveInputStream.hpp:360
SwerveInputStream & WithAllianceRelativeControl(std::function< bool()> enabled)
Definition SwerveInputStream.hpp:351
SwerveInputStream & WithScaleTranslation(double scale)
Definition SwerveInputStream.hpp:211
SwerveInputStream & WithCubeTranslationControllerAxis(std::function< bool()> enabled)
Definition SwerveInputStream.hpp:342
SwerveInputStream & WithAimLookahead(wpi::units::second_t lookahead)
Definition SwerveInputStream.hpp:252
SwerveInputStream(SwerveDrive< NumModules > &drive, std::function< double()> x, std::function< double()> y, std::function< double()> rot)
Definition SwerveInputStream.hpp:49
SwerveInputStream & WithAimFeedforward(wpi::math::SimpleMotorFeedforward< wpi::units::radians > feedforward)
Definition SwerveInputStream.hpp:262
SwerveInputStream & WithCubeRotationControllerAxis(std::function< bool()> enabled)
Definition SwerveInputStream.hpp:333
double GetTranslationAxisScale() const
Definition SwerveInputStream.hpp:130
SwerveInputStream & WithMaximumLinearVelocity(wpi::units::meters_per_second_t velocity)
Definition SwerveInputStream.hpp:178
void SetOmegaAxisScale(double scale)
Definition SwerveInputStream.hpp:136
SwerveInputStream & WithHeading(std::function< wpi::units::radian_t()> heading)
Definition SwerveInputStream.hpp:226
double GetAxisDeadband() const
Definition SwerveInputStream.hpp:125
void SetAllianceRelativeEnabled(bool enabled)
Definition SwerveInputStream.hpp:169
void SetTranslationAxisScale(double scale)
Definition SwerveInputStream.hpp:131
SwerveInputStream & WithTranslationHeadingOffset(wpi::math::Rotation2d angle)
Definition SwerveInputStream.hpp:376
static SwerveInputStream Of(SwerveDrive< NumModules > &drive, std::function< double()> x, std::function< double()> y)
Definition SwerveInputStream.hpp:44
SwerveInputStream & WithHeadingControl(std::function< bool()> trigger)
Definition SwerveInputStream.hpp:221
bool IsAllianceRelativeEnabled() const
Definition SwerveInputStream.hpp:166
SwerveInputStream & WithAllianceRelativeControl()
Definition SwerveInputStream.hpp:356
std::string GetCurrentModeName() const
Definition SwerveInputStream.hpp:111
wpi::units::radians_per_second_t GetMaximumChassisAngularVelocity() const
Definition SwerveInputStream.hpp:147
bool IsOmegaCubeEnabled() const
Definition SwerveInputStream.hpp:161
TelemetryVerbosity
Definition SmartMotorControllerConfig.hpp:72
Definition SwerveInputStream.hpp:34