diff --git a/.github/workflows/pullgompeilib.yml b/.github/workflows/pullgompeilib.yml index a83362f9..e8790377 100644 --- a/.github/workflows/pullgompeilib.yml +++ b/.github/workflows/pullgompeilib.yml @@ -5,7 +5,7 @@ on: jobs: sync: - uses: Team-190/CI-Workflows/.github/workflows/syncgompeilib.yml@main + uses: Team-190/CI-Workflows/.github/workflows/syncgompeilib.yaml@main with: direction: pull secrets: diff --git a/.github/workflows/pushgompeilib.yml b/.github/workflows/pushgompeilib.yml index f3759bb2..84ba499f 100644 --- a/.github/workflows/pushgompeilib.yml +++ b/.github/workflows/pushgompeilib.yml @@ -6,7 +6,7 @@ on: jobs: sync: - uses: Team-190/CI-Workflows/.github/workflows/syncgompeilib.yml@main + uses: Team-190/CI-Workflows/.github/workflows/syncgompeilib.yaml@main with: direction: push secrets: diff --git a/advantagescope_assets/Robot_Delta/config.json b/advantagescope_assets/Robot_Delta/config.json deleted file mode 100644 index 6e4dcff9..00000000 --- a/advantagescope_assets/Robot_Delta/config.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "Delta", - "sourceUrl": "https://frc190.onshape.com/documents/ba8a365713d40fc24cf0b54f/w/90379dd84e9a15f2f3a15787/e/f7562994bcc6d5175e8e4e30", - "disableSimplification": true, - "rotations": [ - { - "axis": "x", - "degrees": 90 - }, - { - "axis": "z", - "degrees": 90 - } - ], - "position": [ - 0, - 0, - 0 - ], - "cameras": [], - "components": [] -} diff --git a/advantagescope_assets/Robot_Delta/model_6.glb b/advantagescope_assets/Robot_Delta/model_6.glb deleted file mode 100644 index 2bf95158..00000000 Binary files a/advantagescope_assets/Robot_Delta/model_6.glb and /dev/null differ diff --git a/advantagescope_assets/Robot_Turnover/config.json b/advantagescope_assets/Robot_Turnover/config.json new file mode 100644 index 00000000..ed744fb3 --- /dev/null +++ b/advantagescope_assets/Robot_Turnover/config.json @@ -0,0 +1,142 @@ +{ + "name": "Turnover", + "sourceUrl": "https://frc190.onshape.com/documents/ba8a365713d40fc24cf0b54f/w/90379dd84e9a15f2f3a15787/e/f7562994bcc6d5175e8e4e30", + "disableSimplification": true, + "rotations": [ + { + "axis": "x", + "degrees": 90 + }, + { + "axis": "z", + "degrees": 90 + } + ], + "position": [ + 0, + 0, + 0 + ], + "cameras": [], + "components": [ + { + "zeroedRotations": [ + { + "axis": "x", + "degrees": 90 + }, + { + "axis": "z", + "degrees": 90 + } + ], + "zeroedPosition": [ + 0.017463, + 0.163513, + -0.371475 + ] + }, + { + "zeroedRotations": [ + { + "axis": "x", + "degrees": 90 + }, + { + "axis": "z", + "degrees": 90 + } + ], + "zeroedPosition": [ + -0.043168, + 0.1635125, + -0.474975 + ] + }, + { + "zeroedRotations": [ + { + "axis": "x", + "degrees": 90 + }, + { + "axis": "z", + "degrees": 90 + } + ], + "zeroedPosition": [ + 0.317482, + -0.090043, + -0.477114 + ] + }, + { + "zeroedRotations": [ + { + "axis": "x", + "degrees": 90 + }, + { + "axis": "z", + "degrees": 90 + } + ], + "zeroedPosition": [ + 0, + 0, + 0 + ] + }, + { + "zeroedRotations": [ + { + "axis": "x", + "degrees": 90 + }, + { + "axis": "z", + "degrees": 90 + } + ], + "zeroedPosition": [ + -0.139700, + 0, + -0.254000 + ] + }, + { + "zeroedRotations": [ + { + "axis": "x", + "degrees": 90 + }, + { + "axis": "z", + "degrees": 90 + } + ], + "zeroedPosition": [ + 0.003638, + 0, + -0.335929 + ] + }, + { + "zeroedRotations": [ + { + "axis": "x", + "degrees": 90 + }, + { + "axis": "z", + "degrees": 90 + } + ], + "zeroedPosition": [ + -0.292100, + 0, + -0.171450 + ] + } + ] +} diff --git a/advantagescope_assets/Robot_Delta/model.glb b/advantagescope_assets/Robot_Turnover/model.glb similarity index 100% rename from advantagescope_assets/Robot_Delta/model.glb rename to advantagescope_assets/Robot_Turnover/model.glb diff --git a/advantagescope_assets/Robot_Delta/model_0.glb b/advantagescope_assets/Robot_Turnover/model_0.glb similarity index 100% rename from advantagescope_assets/Robot_Delta/model_0.glb rename to advantagescope_assets/Robot_Turnover/model_0.glb diff --git a/advantagescope_assets/Robot_Delta/model_1.glb b/advantagescope_assets/Robot_Turnover/model_1.glb similarity index 100% rename from advantagescope_assets/Robot_Delta/model_1.glb rename to advantagescope_assets/Robot_Turnover/model_1.glb diff --git a/advantagescope_assets/Robot_Delta/model_2.glb b/advantagescope_assets/Robot_Turnover/model_2.glb similarity index 100% rename from advantagescope_assets/Robot_Delta/model_2.glb rename to advantagescope_assets/Robot_Turnover/model_2.glb diff --git a/advantagescope_assets/Robot_Delta/model_3.glb b/advantagescope_assets/Robot_Turnover/model_3.glb similarity index 100% rename from advantagescope_assets/Robot_Delta/model_3.glb rename to advantagescope_assets/Robot_Turnover/model_3.glb diff --git a/advantagescope_assets/Robot_Delta/model_4.glb b/advantagescope_assets/Robot_Turnover/model_4.glb similarity index 100% rename from advantagescope_assets/Robot_Delta/model_4.glb rename to advantagescope_assets/Robot_Turnover/model_4.glb diff --git a/advantagescope_assets/Robot_Delta/model_5.glb b/advantagescope_assets/Robot_Turnover/model_5.glb similarity index 100% rename from advantagescope_assets/Robot_Delta/model_5.glb rename to advantagescope_assets/Robot_Turnover/model_5.glb diff --git a/advantagescope_assets/Robot_Turnover/model_6.glb b/advantagescope_assets/Robot_Turnover/model_6.glb new file mode 100644 index 00000000..6b6d685b Binary files /dev/null and b/advantagescope_assets/Robot_Turnover/model_6.glb differ diff --git a/build.gradle b/build.gradle index 175ebe8a..a9285dbe 100755 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,8 @@ plugins { id "com.peterabeles.gversion" version "1.10" id "io.freefair.lombok" version "8.4" id "com.diffplug.spotless" version "6.25.0" - id "io.freefair.aspectj.post-compile-weaving" version "8.4" + id "io.freefair.aspectj.post-compile-weaving" version "8.6" + id "jacoco" } ext { @@ -111,65 +112,11 @@ deploy { } } -def getGompeiVersionFromSource() { - def configFile = file("src/main/java/frc/robot/RobotConfig.java") - if (!configFile.exists()) { - logger.warn("WARNING: No RobotConfig.java class found, falling back to latest GompeiLib package") - return "2026+" - } else { - logger.info("INFO: RobotConfig.java class found") - } - - def content = configFile.text - - def robotMatcher = content =~ /ROBOT\s*=\s*RobotType\.(\w+);/ - if (robotMatcher.find()) { - def activeRobot = robotMatcher.group(1) - - def versionMatcher = content =~ /${activeRobot}\("([^"]+)"\)/ - if (versionMatcher.find()) { - return versionMatcher.group(1) - } - } - return "0.0.0-SNAPSHOT" -} - -def GLibRepoPath = "" - repositories { - - def gprUser = project.findProperty("gpr.user") - def gprKey = project.findProperty("gpr.key") - - if (!useLocalGompeiLib && gprUser != null && gprKey != null) { - // Use GitHub Packages if credentials exist - exclusiveContent { - forRepository { - maven { - name = "GompeiLibGitHubPackages" - url = uri("https://maven.pkg.github.com/Team-190/GompeiLib") - credentials { - username = gprUser - password = gprKey - } - } - } - filter { - includeGroup "edu.wpi.team190" - } - } - def gompeiLibVersion = getGompeiVersionFromSource() - GLibRepoPath = "edu.wpi.team190:gompeilib:${gompeiLibVersion}" - } else { - logger.warn("WARNING: Using local GompeiLib build.") - - mavenLocal() - GLibRepoPath = "edu.wpi.team190:gompeilib:0.0.0-SNAPSHOT" - } + mavenCentral() } // Grab the roboRIO comment via SSH - import com.jcraft.jsch.JSch import edu.wpi.first.gradlerio.GradleRIOPlugin import groovy.json.JsonSlurper @@ -328,12 +275,12 @@ dependencies { implementation 'org.jgrapht:jgrapht-core:1.5.1' implementation 'org.jgrapht:jgrapht-io:1.5.1' - - implementation GLibRepoPath // This is required for the @Tracer annotations to work at runtime implementation 'org.aspectj:aspectjrt:1.9.21' implementation 'org.aspectj:aspectjtools:1.9.21' - aspect GLibRepoPath + + implementation project(':gompeilib') + aspect project(':gompeilib') roborioRelease wpi.java.deps.wpilibJniRelease(wpi.platforms.roborio) roborioRelease wpi.java.vendor.jniRelease(wpi.platforms.roborio) @@ -353,9 +300,42 @@ dependencies { annotationProcessor "org.littletonrobotics.akit:akit-autolog:$akitJson.version" } +jacoco { + toolVersion = "0.8.10" +} + test { useJUnitPlatform() systemProperty 'junit.jupiter.extensions.autodetection.enabled', 'true' + finalizedBy jacocoTestReport +} + +jacocoTestReport { + dependsOn test + reports { + xml.required = true + html.required = true + } + doLast { + def reportFile = file("${buildDir}/reports/jacoco/test/jacocoTestReport.xml") + if (reportFile.exists()) { + def parser = new groovy.xml.XmlParser(false, false) + parser.setFeature("http://apache.org/xml/features/disallow-doctype-decl", false) + parser.setFeature("http://xml.org/sax/features/validation", false) + parser.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false) + def xml = parser.parse(reportFile) + println "\n=== JaCoCo Coverage Summary for root project ===" + xml.counter.each { counter -> + def type = counter.attribute('type') + def missed = counter.attribute('missed').toInteger() + def covered = counter.attribute('covered').toInteger() + def total = missed + covered + def percentage = total > 0 ? (covered * 100.0 / total) : 0.0 + printf("%-12s: %.2f%% (%d/%d)%n", type, percentage, covered, total) + } + println "================================================\n" + } + } } // Simulation configuration (e.g. environment variables). @@ -476,39 +456,3 @@ spotless { endWithNewline() } } - -tasks.register('appendBuildVersion') { - doLast { - def src = useLocalGompeiLib - ? "GompeiLib:local" - : ((project.hasProperty("gpr.user") && project.hasProperty("gpr.key")) - ? "GompeiLib:github-packages" - : "GompeiLib:jitpack") - - def dep = configurations.detachedConfiguration( - dependencies.create(GLibRepoPath) - ).resolvedConfiguration.firstLevelModuleDependencies.find { - it.moduleName == "gompeilib" - } - - def ver = dep?.moduleVersion ?: "unknown" - "${src}:${ver}" - def outFile = file("src/main/java/frc/robot/BuildConstants.java") - if (!outFile.exists()) throw new GradleException("BuildConstants.java not found. Run gversion first.") - - def contents = outFile.text - - // Replace existing VERSION field if present, otherwise append before last brace - if (contents =~ /public static final String VERSION\s*=\s*".*";/) { - contents = contents.replaceFirst(/public static final String VERSION\s*=\s*".*";/, - "public static final String VERSION = \"${src}:${ver}\";") - } else { - contents = contents.replaceFirst(/\}\s*$/, " public static final String VERSION = \"${src}:${ver}\";\n}") - } - - outFile.text = contents - println "VERSION field set to ${src}:${ver}" - } -} - -createVersionFile.finalizedBy appendBuildVersion diff --git a/lib/.github/workflows/approvalautomation.yaml b/lib/.github/workflows/approvalautomation.yaml new file mode 100644 index 00000000..2f1582f3 --- /dev/null +++ b/lib/.github/workflows/approvalautomation.yaml @@ -0,0 +1,92 @@ +name: LCM Review Automation + +on: + pull_request: + types: [opened, ready_for_review, synchronize] + +jobs: + lcm-review-check: + name: LCM Review Check + runs-on: ubuntu-latest + steps: + - name: Determine action for this PR + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.APPROVAL_PAT }} + script: | + const pr = context.payload.pull_request; + const prAuthor = pr.user.login; + const prNumber = pr.number; + const prUpdatedAt = new Date(pr.updated_at); + + // Get all reviews + const { data: reviews } = await github.rest.pulls.listReviews({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: prNumber + }); + + // Separate bot and human reviews + const botReviews = reviews + .filter(r => r.user.login === '190automationbot') + .sort((a, b) => new Date(b.submitted_at) - new Date(a.submitted_at)); + + const humanReviews = reviews + .filter(r => r.user.login !== '190automationbot') + .sort((a, b) => new Date(b.submitted_at) - new Date(a.submitted_at)); + + const latestBotReview = botReviews[0]; + const latestHumanReview = humanReviews[0]; + + if (prAuthor === 'ElliotScher') { + // Auto-approve logic + let needsApproval = false; + + if (!latestBotReview) { + needsApproval = true; + } else { + const lastApprovalTime = new Date(latestBotReview.submitted_at); + if (prUpdatedAt > lastApprovalTime) { + needsApproval = true; + } + } + + // Don't approve if human requested changes + if (latestHumanReview && latestHumanReview.state === 'CHANGES_REQUESTED') { + needsApproval = false; + } + + if (needsApproval && pr.draft === false) { + console.log("Auto-approving PR by ElliotScher"); + await github.rest.pulls.createReview({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: prNumber, + event: 'APPROVE' + }); + } else { + console.log("No approval needed at this time"); + } + + } else { + // Request review from ElliotScher for other authors + if (prAuthor !== 'ElliotScher') { + const alreadyRequested = pr.requested_reviewers + .map(r => r.login) + .includes('ElliotScher'); + + if (!alreadyRequested) { + console.log("Requesting review from ElliotScher"); + await github.rest.pulls.requestReviewers({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: prNumber, + reviewers: ['ElliotScher'] + }); + } else { + console.log("ElliotScher already requested as reviewer"); + } + } else { + console.log("Skipping review request because author is ElliotScher"); + } + } diff --git a/lib/.github/workflows/build.yaml b/lib/.github/workflows/build.yaml new file mode 100644 index 00000000..233cfe66 --- /dev/null +++ b/lib/.github/workflows/build.yaml @@ -0,0 +1,30 @@ +name: Build + +on: + push: + branches: + - main + pull_request: + +jobs: + Build: + runs-on: ubuntu-latest + container: wpilib/roborio-cross-ubuntu:2024-22.04 + + env: + GPR_USER: ${{ github.actor }} + GPR_KEY: ${{ secrets.GPR_KEY }} + + steps: + - uses: actions/checkout@v4 + + - name: Grant execute permission + run: chmod +x gradlew + + - name: Build robot code + run: | + ./gradlew build \ + -Pgpr.user=$GPR_USER \ + -Pgpr.key=$GPR_KEY \ + --refresh-dependencies \ + -x test \ No newline at end of file diff --git a/lib/.github/workflows/lint.yaml b/lib/.github/workflows/lint.yaml new file mode 100644 index 00000000..fd3bf548 --- /dev/null +++ b/lib/.github/workflows/lint.yaml @@ -0,0 +1,28 @@ +name: Lint + +on: + push: + branches: + - main + pull_request: + +jobs: + Lint: + runs-on: ubuntu-latest + container: wpilib/roborio-cross-ubuntu:2024-22.04 + + env: + GPR_USER: ${{ github.actor }} + GPR_KEY: ${{ secrets.GPR_KEY }} + + steps: + - uses: actions/checkout@v4 + + - name: Grant execute permission + run: chmod +x gradlew + + - name: Spotless lint + run: | + ./gradlew spotlessCheck \ + -Pgpr.user=$GPR_USER \ + -Pgpr.key=$GPR_KEY \ No newline at end of file diff --git a/lib/.github/workflows/publish.yaml b/lib/.github/workflows/publish.yaml new file mode 100644 index 00000000..a4de8d12 --- /dev/null +++ b/lib/.github/workflows/publish.yaml @@ -0,0 +1,30 @@ +name: Publish + +on: + release: + types: [created] + +jobs: + publish: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Set up Java + uses: actions/setup-java@v3 + with: + java-version: 17 + distribution: temurin + cache: gradle + + - name: Determine version + run: | + # Use the GitHub tag as version + echo "RELEASE_VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV + + - name: Publish to GitHub Packages + run: ./gradlew publish -PreleaseVersion=$RELEASE_VERSION + env: + GPR_USER: ${{ github.actor }} + GPR_KEY: ${{ secrets.GITHUB_TOKEN }} diff --git a/lib/.github/workflows/test.yaml b/lib/.github/workflows/test.yaml new file mode 100644 index 00000000..09c33b2a --- /dev/null +++ b/lib/.github/workflows/test.yaml @@ -0,0 +1,32 @@ +name: Test + +on: + pull_request: + +jobs: + Test: + runs-on: ubuntu-latest + container: wpilib/roborio-cross-ubuntu:2024-22.04 + steps: + - uses: actions/checkout@v4 + + - name: Grant execute permission + run: chmod +x gradlew + + - name: Run tests with coverage check + run: | + ./gradlew clean test jacocoTestCoverageVerification \ + -Pgpr.user=$GPR_USER \ + -Pgpr.key=$GPR_KEY \ + --refresh-dependencies \ + --no-daemon \ + --stacktrace + + - name: Upload test results + if: always() + uses: actions/upload-artifact@v4 + with: + name: test-results + path: | + build/test-results + build/reports/jacoco \ No newline at end of file diff --git a/lib/.gitignore b/lib/.gitignore new file mode 100644 index 00000000..553ff928 --- /dev/null +++ b/lib/.gitignore @@ -0,0 +1,45 @@ +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build + +*/BuildConstants.java + +### IntelliJ IDEA ### +/.idea +.idea/libraries/ +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store +examples/swerve/src/main/java/frc/robot/BuildConstants.java + +examples/swerve/ctre_sim \ No newline at end of file diff --git a/lib/build.gradle b/lib/build.gradle new file mode 100644 index 00000000..45c95d92 --- /dev/null +++ b/lib/build.gradle @@ -0,0 +1,279 @@ +import org.gradle.internal.os.OperatingSystem + +plugins { + id 'java-library' + id 'maven-publish' + id "com.diffplug.spotless" version "6.25.0" + id "io.freefair.lombok" version "8.4" + id "io.freefair.aspectj.post-compile-weaving" version "8.6" + id "jacoco" +} + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } +} + +group = 'edu.wpi.team190' +version = project.hasProperty("releaseVersion") ? project.property("releaseVersion") : "0.0.0-SNAPSHOT" + +// Detect OS +def osClassifier = { + def osName = System.getProperty("os.name").toLowerCase() + if (osName.contains("win")) return "windowsx86-64" + if (osName.contains("mac")) return "osxuniversal" + return "linuxx86-64" +}() + +repositories { + mavenCentral() + maven { url = uri("https://frcmaven.wpi.edu/artifactory/release") } + maven { url = uri("https://frcmaven.wpi.edu/artifactory/littletonrobotics-mvn-release") } + maven { url = uri("https://maven.ctr-electronics.com/release/") } + maven { url = uri("https://frcmaven.wpi.edu/artifactory/sleipnirgroup-mvn-release/") } + maven { url = uri("https://3015rangerrobotics.github.io/pathplannerlib/repo") } +} + +apply from: "gradle/native-utils.gradle" + +dependencies { + // WPILib bundle + compileOnly libs.bundles.wpilib + + // AspectJ + compileOnly libs.aspectj.rt + runtimeOnly libs.aspectj.tools + + // PathPlanner + compileOnly libs.pathplanner + + // CTRE + compileOnly libs.phoenix6 + + // Choreo + compileOnly libs.choreo + + // AdvantageKit + runtime dependencies + compileOnly(libs.bundles.advantagekit) + annotationProcessor libs.advantagekit.autolog + + // Tests + testImplementation platform('org.junit:junit-bom:5.11.0') + testImplementation "org.mockito:mockito-inline:5.2.0" + testImplementation 'org.junit.jupiter:junit-jupiter' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + + testImplementation libs.bundles.wpilib + testImplementation libs.bundles.aspectj + testImplementation libs.bundles.advantagekit + testImplementation libs.phoenix6 + testImplementation libs.pathplanner + testImplementation libs.choreo + testImplementation "org.ejml:ejml-simple:0.44.0" + + testRuntimeOnly libs.bundles.wpilib + testRuntimeOnly libs.bundles.aspectj + testRuntimeOnly libs.bundles.advantagekit + testRuntimeOnly libs.phoenix6 + testRuntimeOnly libs.pathplanner + testRuntimeOnly libs.choreo +} + +publishing { + publications { + mavenJava(MavenPublication) { + from components.java + groupId = group + artifactId = "gompeilib" + version = version + } + } + + repositories { + // GitHub Packages ONLY in CI + if (System.getenv("CI") == "true") { + maven { + name = "GitHubPackages" + url = uri("https://maven.pkg.github.com/Team-190/GompeiLib") + credentials { + username = System.getenv("GPR_USER") ?: project.findProperty("gpr.user") + password = System.getenv("GPR_KEY") ?: project.findProperty("gpr.key") + } + } + } + } +} + +test { + useJUnitPlatform() + testLogging { + events "passed", "skipped", "failed", "standardOut", "standardError" + showStandardStreams = true + } + jacoco { + enabled = true + } + + // After tests finish, generate coverage report automatically + finalizedBy tasks.jacocoTestReport +} + +jacoco { + toolVersion = "0.8.10" +} + +test { + useJUnitPlatform() + finalizedBy jacocoTestReport +} + +jacocoTestReport { + dependsOn test + reports { + xml.required = true + html.required = true + } + afterEvaluate { + classDirectories.setFrom(files(classDirectories.files.collect { + fileTree(dir: it, exclude: [ + '**/*Aspect.class', + '**/*$AjcClosure*', + '**/ajc$*', + '**/*Tracer.class', + '**/*AutoLogged.class', + '**/*AutoLogged$*.class', + '**/*Tracing*', + '**/LimelightHelpers*.class', + '**/*TalonFX.class', + '**/*Pigeon2.class', + '**/*SysId*.class', + '**/Phoenix*.class', + '**/CameraMovingLimelight*.class', + '**/CameraStaticLimelight*.class', + '**/CameraGompeiVision*.class', + '**/CameraType*.class', + '**/SwerveDrive*.class', + '**/SwerveModule*.class', + '**/GenericFlywheel*.class', + '**/Elevator*.class', + '**/Arm*.class', + '**/Vision*.class', + '**/Gains*.class', + '**/LinearConstraints*.class', + '**/AngularPositionConstraints*.class', + '**/AngularVelocityConstraints*.class', + '**/PoseEstimator*.class', + '**/GeometryUtil*.class' + ]) + })) + } + + doLast { + def reportFile = file("${buildDir}/reports/jacoco/test/jacocoTestReport.xml") + if (reportFile.exists()) { + def parser = new groovy.xml.XmlParser(false, false) + parser.setFeature("http://apache.org/xml/features/disallow-doctype-decl", false) + parser.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false) + parser.setFeature("http://xml.org/sax/features/validation", false) + def xml = parser.parse(reportFile) + println "\n=== JaCoCo Coverage Summary for :gompeilib ===" + xml.counter.each { counter -> + def type = counter.attribute('type') + def missed = counter.attribute('missed').toInteger() + def covered = counter.attribute('covered').toInteger() + def total = missed + covered + def percentage = total > 0 ? (covered * 100.0 / total) : 0.0 + printf("%-12s: %.2f%% (%d/%d)%n", type, percentage, covered, total) + } + println "==============================================\n" + } + } +} + +jacocoTestCoverageVerification { + dependsOn test + violationRules { + rule { + limit { + minimum = 1.00 + } + } + } + afterEvaluate { + classDirectories.setFrom(files(classDirectories.files.collect { + fileTree(dir: it, exclude: [ + '**/*Aspect.class', + '**/*$AjcClosure*', + '**/ajc$*', + '**/*Tracer.class', + '**/*AutoLogged.class', + '**/*AutoLogged$*.class', + '**/*Tracing*', + '**/LimelightHelpers*.class', + '**/*TalonFX.class', + '**/*Pigeon2.class', + '**/*SysId*.class', + '**/Phoenix*.class', + '**/CameraMovingLimelight*.class', + '**/CameraStaticLimelight*.class', + '**/CameraGompeiVision*.class', + '**/CameraType*.class', + '**/SwerveDrive*.class', + '**/SwerveModule*.class', + '**/GenericFlywheel*.class', + '**/Elevator*.class', + '**/Arm*.class', + '**/Vision*.class', + '**/Gains*.class', + '**/LinearConstraints*.class', + '**/AngularPositionConstraints*.class', + '**/AngularVelocityConstraints*.class', + '**/PoseEstimator*.class', + '**/GeometryUtil*.class' + ]) + })) + } +} + +// Spotless formatting +project.compileJava.dependsOn(spotlessApply) +spotless { + java { + target fileTree(".") { + include "**/*.java" + exclude "**/build/**", "**/build-*/**" + } + toggleOffOn() + googleJavaFormat() + removeUnusedImports() + trimTrailingWhitespace() + endWithNewline() + } + groovyGradle { + target fileTree(".") { + include "**/*.gradle" + exclude "**/build/**", "**/build-*/**" + } + greclipse() + indentWithSpaces(4) + trimTrailingWhitespace() + endWithNewline() + } + json { + target fileTree(".") { + include "**/*.json" + exclude "**/build/**", "**/build-*/**" + } + gson().indentWithSpaces(2) + } + format "misc", { + target fileTree(".") { + include "**/*.md", "**/.gitignore" + exclude "**/build/**", "**/build-*/**" + } + trimTrailingWhitespace() + indentWithSpaces(2) + endWithNewline() + } +} diff --git a/lib/examples/swerve/.wpilib/wpilib_preferences.json b/lib/examples/swerve/.wpilib/wpilib_preferences.json new file mode 100644 index 00000000..a1801715 --- /dev/null +++ b/lib/examples/swerve/.wpilib/wpilib_preferences.json @@ -0,0 +1,6 @@ +{ + "enableCppIntellisense": false, + "currentLanguage": "java", + "projectYear": "2026", + "teamNumber": 190 +} diff --git a/lib/examples/swerve/6328-License.md b/lib/examples/swerve/6328-License.md new file mode 100644 index 00000000..3d37d771 --- /dev/null +++ b/lib/examples/swerve/6328-License.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 FRC 6328 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/lib/examples/swerve/AdvantageKit-License.md b/lib/examples/swerve/AdvantageKit-License.md new file mode 100644 index 00000000..f288702d --- /dev/null +++ b/lib/examples/swerve/AdvantageKit-License.md @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/lib/examples/swerve/README.md b/lib/examples/swerve/README.md new file mode 100644 index 00000000..60dad4ae --- /dev/null +++ b/lib/examples/swerve/README.md @@ -0,0 +1 @@ +# 2k26-Robot-Code diff --git a/lib/examples/swerve/WPILib-License.md b/lib/examples/swerve/WPILib-License.md new file mode 100644 index 00000000..051080d8 --- /dev/null +++ b/lib/examples/swerve/WPILib-License.md @@ -0,0 +1,25 @@ +Copyright (c) 2009-2025 FIRST and other WPILib contributors +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +- Neither the name of FIRST, WPILib, nor the names of other WPILib + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY FIRST AND OTHER WPILIB CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY NONINFRINGEMENT AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL FIRST OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/lib/examples/swerve/advantagescope_assets/Robot_Doomspiral/config.json b/lib/examples/swerve/advantagescope_assets/Robot_Doomspiral/config.json new file mode 100644 index 00000000..3cc3b6e3 --- /dev/null +++ b/lib/examples/swerve/advantagescope_assets/Robot_Doomspiral/config.json @@ -0,0 +1,142 @@ +{ + "name": "Doomspiral", + "sourceUrl": "https://frc190.onshape.com/documents/b452dd5a31bf95c6384826fb/w/4d65f099a7d3627693a2bc3a/e/934f62b4a3bd58acfff00615", + "disableSimplification": true, + "rotations": [ + { + "axis": "x", + "degrees": 90 + }, + { + "axis": "z", + "degrees": 90 + } + ], + "position": [ + 0, + 0, + 0 + ], + "cameras": [], + "components": [ + { + "zeroedRotations": [ + { + "axis": "x", + "degrees": 90 + }, + { + "axis": "z", + "degrees": 90 + } + ], + "zeroedPosition": [ + 0.009525, + 0, + -0.067589 + ] + }, + { + "zeroedRotations": [ + { + "axis": "x", + "degrees": 90 + }, + { + "axis": "z", + "degrees": 90 + } + ], + "zeroedPosition": [ + 0.202788, + -0.090048, + -0.477077 + ] + }, + { + "zeroedRotations": [ + { + "axis": "x", + "degrees": 90 + }, + { + "axis": "z", + "degrees": 90 + } + ], + "zeroedPosition": [ + 0, + 0, + 0 + ] + }, + { + "zeroedRotations": [ + { + "axis": "x", + "degrees": 90 + }, + { + "axis": "z", + "degrees": 90 + } + ], + "zeroedPosition": [ + -0.142476, + 0, + -0.278075 + ] + }, + { + "zeroedRotations": [ + { + "axis": "x", + "degrees": 90 + }, + { + "axis": "z", + "degrees": 90 + } + ], + "zeroedPosition": [ + 0.030565, + 0, + -0.312095 + ] + }, + { + "zeroedRotations": [ + { + "axis": "x", + "degrees": 90 + }, + { + "axis": "z", + "degrees": 90 + } + ], + "zeroedPosition": [ + -0.278238, + 0, + -0.196629 + ] + }, + { + "zeroedRotations": [ + { + "axis": "x", + "degrees": 90 + }, + { + "axis": "z", + "degrees": 90 + } + ], + "zeroedPosition": [ + 0.009856, + 0, + -0.304569 + ] + } + ] +} diff --git a/lib/examples/swerve/advantagescope_assets/Robot_Doomspiral/model.glb b/lib/examples/swerve/advantagescope_assets/Robot_Doomspiral/model.glb new file mode 100644 index 00000000..780d3e14 Binary files /dev/null and b/lib/examples/swerve/advantagescope_assets/Robot_Doomspiral/model.glb differ diff --git a/lib/examples/swerve/advantagescope_assets/Robot_Doomspiral/model_0.glb b/lib/examples/swerve/advantagescope_assets/Robot_Doomspiral/model_0.glb new file mode 100644 index 00000000..69d24ee9 Binary files /dev/null and b/lib/examples/swerve/advantagescope_assets/Robot_Doomspiral/model_0.glb differ diff --git a/lib/examples/swerve/advantagescope_assets/Robot_Doomspiral/model_1.glb b/lib/examples/swerve/advantagescope_assets/Robot_Doomspiral/model_1.glb new file mode 100644 index 00000000..f29c3ac2 Binary files /dev/null and b/lib/examples/swerve/advantagescope_assets/Robot_Doomspiral/model_1.glb differ diff --git a/lib/examples/swerve/advantagescope_assets/Robot_Doomspiral/model_2.glb b/lib/examples/swerve/advantagescope_assets/Robot_Doomspiral/model_2.glb new file mode 100644 index 00000000..9da3d878 Binary files /dev/null and b/lib/examples/swerve/advantagescope_assets/Robot_Doomspiral/model_2.glb differ diff --git a/lib/examples/swerve/advantagescope_assets/Robot_Doomspiral/model_3.glb b/lib/examples/swerve/advantagescope_assets/Robot_Doomspiral/model_3.glb new file mode 100644 index 00000000..07c74d72 Binary files /dev/null and b/lib/examples/swerve/advantagescope_assets/Robot_Doomspiral/model_3.glb differ diff --git a/lib/examples/swerve/advantagescope_assets/Robot_Doomspiral/model_4.glb b/lib/examples/swerve/advantagescope_assets/Robot_Doomspiral/model_4.glb new file mode 100644 index 00000000..29fc4cc9 Binary files /dev/null and b/lib/examples/swerve/advantagescope_assets/Robot_Doomspiral/model_4.glb differ diff --git a/lib/examples/swerve/advantagescope_assets/Robot_Doomspiral/model_5.glb b/lib/examples/swerve/advantagescope_assets/Robot_Doomspiral/model_5.glb new file mode 100644 index 00000000..2e97af12 Binary files /dev/null and b/lib/examples/swerve/advantagescope_assets/Robot_Doomspiral/model_5.glb differ diff --git a/lib/examples/swerve/advantagescope_assets/Robot_Doomspiral/model_6.glb b/lib/examples/swerve/advantagescope_assets/Robot_Doomspiral/model_6.glb new file mode 100644 index 00000000..22d3a452 Binary files /dev/null and b/lib/examples/swerve/advantagescope_assets/Robot_Doomspiral/model_6.glb differ diff --git a/lib/examples/swerve/build.gradle b/lib/examples/swerve/build.gradle new file mode 100755 index 00000000..c59ac10a --- /dev/null +++ b/lib/examples/swerve/build.gradle @@ -0,0 +1,456 @@ +plugins { + id "java" + id "edu.wpi.first.GradleRIO" version "2026.2.1" + id "com.peterabeles.gversion" version "1.10" + id "io.freefair.lombok" version "8.4" + id "com.diffplug.spotless" version "6.25.0" + id "io.freefair.aspectj.post-compile-weaving" version "8.6" +} + +repositories { + maven { url = uri("https://3015rangerrobotics.github.io/pathplannerlib/repo") } +} + +ext { + deploySrcDir = "src/main/deploy" + deployRobotDir = "/home/lvuser/deploy" + useLocalGompeiLib = project.hasProperty("useLocalGompeiLib") +} + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} + +def ROBOT_MAIN_CLASS = "frc.robot.Main" + +gradle.projectsEvaluated { + def buildDir = new File("${project.buildDir}/classes/java/main") + if (!buildDir.exists()) { + println "⚠️ Warning: compiled classes not found. Make sure compileJava runs first." + project.ext.deploySrcDir = "src/main/deploy" + return + } + + try { + def loader = new URLClassLoader([buildDir.toURI().toURL()] as URL[], this.class.classLoader) + def constantsClass = loader.loadClass("frc.robot.RobotConfig") + def robotField = constantsClass.getDeclaredField("ROBOT") + robotField.setAccessible(true) + def robotEnumValue = robotField.get(null) // static field + + println "Detected RobotType: ${robotEnumValue}" + + def baseName = robotEnumValue.toString().toLowerCase().replaceAll(/_sim$/, "") + def candidateDir = "src/main/deploy/${baseName}" + + if (file(candidateDir).exists()) { + project.ext.deploySrcDir = candidateDir + project.ext.deployRobotDir = "/home/lvuser/deploy/${baseName}" + } else { + println "Folder '${candidateDir}' does not exist. Using default deploy folder." + } + + println "deploySrcDir set to: ${project.ext.deploySrcDir}" + } catch (Throwable e) { + println "Could not read RobotType: ${e.message}" + project.ext.deploySrcDir = "src/main/deploy" + } +} + +// Define my targets (RoboRIO) and artifacts (deployable files) +// This is added by GradleRIO's backing project DeployUtils. +deploy { + targets { + roborio(getTargetTypeClass('RoboRIO')) { + // Team number is loaded either from the .wpilib/wpilib_preferences.json + // or from command line. If not found an exception will be thrown. + // You can use getTeamOrDefault(team) instead of getTeamNumber if you + // want to store a team number in this file. + team = 190 + debug = project.frc.getDebugOrDefault(false) + + artifacts { + // First part is artifact name, 2nd is artifact type + // getTargetTypeClass is a shortcut to get the class type using a string + + frcJava(getArtifactTypeClass('FRCJavaArtifact')) { + jvmArgs.add("-XX:+UnlockExperimentalVMOptions") + jvmArgs.add("-XX:GCTimeRatio=5") + jvmArgs.add("-XX:+UseSerialGC") + jvmArgs.add("-XX:MaxGCPauseMillis=50") + + // The options below may improve performance, but should only be enabled on the RIO 2 + + final MAX_JAVA_HEAP_SIZE_MB = 100 + jvmArgs.add("-Xmx" + MAX_JAVA_HEAP_SIZE_MB + "M") + jvmArgs.add("-Xms" + MAX_JAVA_HEAP_SIZE_MB + "M") + jvmArgs.add("-XX:+AlwaysPreTouch") + + // Enable VisualVM connection + // jvmArgs.add("-Dcom.sun.management.jmxremote=true") + // jvmArgs.add("-Dcom.sun.management.jmxremote.port=1198") + // jvmArgs.add("-Dcom.sun.management.jmxremote.local.only=false") + // jvmArgs.add("-Dcom.sun.management.jmxremote.ssl=false") + // jvmArgs.add("-Dcom.sun.management.jmxremote.authenticate=false") + // jvmArgs.add("-Djava.rmi.server.hostname=10.1.90.2") // Replace TE.AM with team number + } + + // Static files artifact + frcStaticFileDeploy(getArtifactTypeClass('FileTreeArtifact')) { + files = project.fileTree(project.ext.deploySrcDir) + directory = deployRobotDir + // Change to true to delete files on roboRIO that no + // longer exist in deploy directory on roboRIO + deleteOldFiles = false + } + } + } + } +} + +// Grab the roboRIO comment via SSH + +import com.jcraft.jsch.JSch +import edu.wpi.first.gradlerio.GradleRIOPlugin +import groovy.json.JsonSlurper + +ext { + // RoboRIO SSH details + roboRIOHost = 'roboRIO-190-FRC.local' // Replace with your team number + roboRIOUser = 'admin' // Default user + roboRIOPassword = '' // Default password + machineInfoFile = '/etc/machine-info' // Path to the file to read +} + +task checkRoboRIOtoRobotType { + dependsOn compileJava + doLast { + def ROBORIO_COMMENT = fetchNameUsingJSch( + roboRIOHost, roboRIOUser, roboRIOPassword, machineInfoFile + ) + println "RoboRIO Name: ${ROBORIO_COMMENT}" + + // Ensure the build output directory is in the classpath + def buildOutputDir = file('build/classes/java/main') + URLClassLoader loader = new URLClassLoader([ + buildOutputDir.toURI().toURL() + ] as URL[], this.class.classLoader) + + // Set file path names and constants + def robotTypeClassName = 'frc.robot.RobotConfig$RobotType' + def constantsClassName = 'frc.robot.RobotConfig' + def robotType = null + def constantsClass = null + def robotFieldName = 'ROBOT' + + try { + // Load the class from the extended classloader + robotType = loader.loadClass(robotTypeClassName) + println "Loaded class: ${robotType.name}" + + def constants = robotType.enumConstants + println "Enum constants: ${constants.collect { it.name() }}" + } catch (ClassNotFoundException e) { + println "Error: Class ${robotTypeClassName} not found. ${e.message}" + } + + // Check for valid name of roboRIO comment + boolean foundMatch = false + for (enumConstant in robotType.enumConstants) { + if (enumConstant.toString().equalsIgnoreCase(ROBORIO_COMMENT)) { + foundMatch = true + break // Exit the loop once a match is found + } + } + + if (foundMatch) { + println "Match found for possible RobotType" + } else { + throw new GradleException("Error: '${ROBORIO_COMMENT}' is not a valid RobotType\nTo Fix: Enter valid RoboRIO comment of valid RobotType in file '${machineInfoFile}' or via Webserver!") + } + + try { + // Load the class from the extended classloader + constantsClass = loader.loadClass(constantsClassName) + println "Loaded class: ${constantsClass.name}" + } catch (ClassNotFoundException e) { + println "Error: Class ${constantsClassName} not found. ${e.message}" + } + + // Get the ROBOT field + def robotField = constantsClass.getDeclaredField(robotFieldName) + robotField.setAccessible(true) + + // Get the value of the ROBOT field + def robotEnumValue = robotField.get(null) // For static fields, pass `null` + println "Declared ROBOT in Constants.java: ${robotEnumValue}" + + // Compare roboRIO name with ROBOT value + if (ROBORIO_COMMENT.equalsIgnoreCase(robotEnumValue.name())) { + println "The roboRIO name matches the ROBOT value in Constants.java!" + } else { + throw new GradleException("Mismatch! The RoboRIO comment '${ROBORIO_COMMENT}' does not match the ROBOT value '${robotEnumValue.name()}'\nTo Fix: Match RoboRIO comment to ROBOT in file '${constantsClassName}'") + } + } +} + +// Function to fetch the file content using JSch +def fetchNameUsingJSch(host, user, password, remoteFile) { + def session = null + def channel = null + try { + JSch jsch = new JSch() + session = jsch.getSession(user, host, 22) + session.setPassword(password) + + // Skip strict host key checking for simplicity + session.setConfig("StrictHostKeyChecking", "no") + + // Connect to the session + session.connect() + + // Open an exec channel to run the command + channel = session.openChannel("exec") + def command = "cat ${remoteFile}" + channel.setCommand(command) + + // Capture the output + def inputStream = channel.inputStream + channel.connect() + + def output = inputStream.text.trim() + if (output.isEmpty()) { + throw new GradleException("Failed to fetch data from ${remoteFile}") + } + + // Extract the name within quotes if the output contains PRETTY_HOSTNAME + def matcher = output =~ /PRETTY_HOSTNAME="(.+?)"/ + if (matcher.find()) { + return matcher.group(1) // The name within quotes + } else { + throw new GradleException("RoboRIO Comment is Empty in file '${remoteFile}'\nTo Fix: Add RoboRIO Comment in file '${remoteFile}' or via Webserver!") + } + } catch (Exception e) { + throw new GradleException("Error during SSH connection: ${e.message}", e) + } finally { + if (channel != null) { + channel.disconnect() + } + if (session != null) { + session.disconnect() + } + } +} + +deployroborio.dependsOn(checkRoboRIOtoRobotType) + + +def deployArtifact = deploy.targets.roborio.artifacts.frcJava + +// Set to true to use debug for JNI. +wpi.java.debugJni = false + +// Set this to true to enable desktop support. +def includeDesktopSupport = true + +// Configuration for AdvantageKit +task(replayWatch, type: JavaExec) { + mainClass = "org.littletonrobotics.junction.ReplayWatch" + classpath = sourceSets.main.runtimeClasspath +} + +// Defining my dependencies. In this case, WPILib (+ friends), and vendor libraries. +// Also defines JUnit 4. +dependencies { + annotationProcessor wpi.java.deps.wpilibAnnotations() + implementation wpi.java.deps.wpilib() + implementation wpi.java.vendor.java() + implementation 'org.jgrapht:jgrapht-core:1.5.1' + implementation 'org.jgrapht:jgrapht-io:1.5.1' + + implementation 'com.pathplanner.lib:PathplannerLib-java:2026.1.2' + + + implementation project(":") // <- root project (GompeiLib library) + // This is required for the @Tracer annotations to work at runtime + implementation 'org.aspectj:aspectjrt:1.9.21' + implementation 'org.aspectj:aspectjtools:1.9.21' + + roborioRelease wpi.java.deps.wpilibJniRelease(wpi.platforms.roborio) + roborioRelease wpi.java.vendor.jniRelease(wpi.platforms.roborio) + + nativeDebug wpi.java.deps.wpilibJniDebug(wpi.platforms.desktop) + nativeDebug wpi.java.vendor.jniDebug(wpi.platforms.desktop) + simulationDebug wpi.sim.enableDebug() + + nativeRelease wpi.java.deps.wpilibJniRelease(wpi.platforms.desktop) + nativeRelease wpi.java.vendor.jniRelease(wpi.platforms.desktop) + simulationRelease wpi.sim.enableRelease() + + testImplementation 'org.junit.jupiter:junit-jupiter:5.10.1' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + + def akitJson = new JsonSlurper().parseText(new File(projectDir.getAbsolutePath() + "/vendordeps/AdvantageKit.json").text) + annotationProcessor "org.littletonrobotics.akit:akit-autolog:$akitJson.version" +} + +test { + useJUnitPlatform() + systemProperty 'junit.jupiter.extensions.autodetection.enabled', 'true' +} + +// Simulation configuration (e.g. environment variables). +// +// The sim GUI is *disabled* by default to support running +// AdvantageKit log replay from the command line. Set the +// value to "true" to enable the sim GUI by default (this +// is the standard WPILib behavior). +wpi.sim.addGui().defaultEnabled = false +wpi.sim.addDriverstation() + +// Setting up my Jar File. In this case, adding all libraries into the main jar ('fat jar') +// in order to make them all available at runtime. Also adding the manifest so WPILib +// knows where to look for our Robot Class. +jar { + from { + configurations.runtimeClasspath.collect { + it.isDirectory() ? it : zipTree(it) + } + } + from sourceSets.main.allSource + manifest GradleRIOPlugin.javaManifest(ROBOT_MAIN_CLASS) + duplicatesStrategy = DuplicatesStrategy.INCLUDE +} + +// Configure jar and deploy tasks +deployArtifact.jarTask = jar +wpi.java.configureExecutableTasks(jar) +wpi.java.configureTestTasks(test) + +// Configure string concat to always inline compile +tasks.withType(JavaCompile).configureEach { + options.compilerArgs.add '-XDstringConcat=inline' +} + +// Create version file +project.compileJava.dependsOn(createVersionFile) +gversion { + srcDir = "src/main/java/" + classPackage = "frc.robot" + className = "BuildConstants" + dateFormat = "yyyy-MM-dd HH:mm:ss z" + timeZone = "America/New_York" + indent = " " +} + +// Create commit with working changes on event branches +tasks.register('eventDeploy') { + doLast { + if (project.gradle.startParameter.taskNames.any({ it.toLowerCase().contains("deploy") })) { + def branchPrefix = "event" + def branch = 'git branch --show-current'.execute().text.trim() + def commitMessage = "Update at '${new Date().toString()}'" + + if (branch.startsWith(branchPrefix)) { + exec { + workingDir(projectDir) + executable 'git' + args 'add', '-A' + } + exec { + workingDir(projectDir) + executable 'git' + args 'commit', '-m', commitMessage + ignoreExitValue = true + } + + println "Committed to branch: '$branch'" + println "Commit message: '$commitMessage'" + } else { + println "Not on an event branch, skipping commit" + } + } else { + println "Not running deploy task, skipping commit" + } + } +} +createVersionFile.dependsOn(eventDeploy) + +// Spotless formatting +project.compileJava.dependsOn(spotlessApply) +spotless { + java { + target fileTree(".") { + include "**/*.java" + exclude "**/build/**", "**/build-*/**" + } + toggleOffOn() + googleJavaFormat() + removeUnusedImports() + trimTrailingWhitespace() + endWithNewline() + } + groovyGradle { + target fileTree(".") { + include "**/*.gradle" + exclude "**/build/**", "**/build-*/**" + } + greclipse() + indentWithSpaces(4) + trimTrailingWhitespace() + endWithNewline() + } + json { + target fileTree(".") { + include "**/*.json" + exclude "**/build/**", "**/build-*/**" + } + gson().indentWithSpaces(2) + } + format "misc", { + target fileTree(".") { + include "**/*.md", "**/.gitignore" + exclude "**/build/**", "**/build-*/**" + } + trimTrailingWhitespace() + indentWithSpaces(2) + endWithNewline() + } +} + +//tasks.register('appendBuildVersion') { +// doLast { +// def src = useLocalGompeiLib +// ? "GompeiLib:local" +// : ((project.hasProperty("gpr.user") && project.hasProperty("gpr.key")) +// ? "GompeiLib:github-packages" +// : "GompeiLib:jitpack") +// +// def dep = configurations.detachedConfiguration( +// dependencies.create(GLibRepoPath) +// ).resolvedConfiguration.firstLevelModuleDependencies.find { +// it.moduleName == "gompeilib" +// } +// +// def ver = dep?.moduleVersion ?: "unknown" +// "${src}:${ver}" +// def outFile = file("src/main/java/frc/robot/BuildConstants.java") +// if (!outFile.exists()) throw new GradleException("BuildConstants.java not found. Run gversion first.") +// +// def contents = outFile.text +// +// // Replace existing VERSION field if present, otherwise append before last brace +// if (contents =~ /public static final String VERSION\s*=\s*".*";/) { +// contents = contents.replaceFirst(/public static final String VERSION\s*=\s*".*";/, +// "public static final String VERSION = \"${src}:${ver}\";") +// } else { +// contents = contents.replaceFirst(/\}\s*$/, " public static final String VERSION = \"${src}:${ver}\";\n}") +// } +// +// outFile.text = contents +// println "VERSION field set to ${src}:${ver}" +// } +//} + +//createVersionFile.finalizedBy appendBuildVersion diff --git a/lib/examples/swerve/gradle/wrapper/gradle-wrapper.jar b/lib/examples/swerve/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..a4b76b95 Binary files /dev/null and b/lib/examples/swerve/gradle/wrapper/gradle-wrapper.jar differ diff --git a/lib/examples/swerve/gradle/wrapper/gradle-wrapper.properties b/lib/examples/swerve/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..34bd9ce9 --- /dev/null +++ b/lib/examples/swerve/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=permwrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=permwrapper/dists diff --git a/lib/examples/swerve/gradlew b/lib/examples/swerve/gradlew new file mode 100755 index 00000000..1aa94a42 --- /dev/null +++ b/lib/examples/swerve/gradlew @@ -0,0 +1,249 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/lib/examples/swerve/gradlew.bat b/lib/examples/swerve/gradlew.bat new file mode 100644 index 00000000..93e3f59f --- /dev/null +++ b/lib/examples/swerve/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/lib/examples/swerve/lvrt.sh b/lib/examples/swerve/lvrt.sh new file mode 100644 index 00000000..ac3af941 --- /dev/null +++ b/lib/examples/swerve/lvrt.sh @@ -0,0 +1,17 @@ +#!/bin/sh +exec 2>&1 >/dev/null +while true; do + APP_PATH=`nirtcfg --file /etc/natinst/share/lvrt.conf --get section=LVRT,token=RTTarget.ApplicationPath` + if [ "$APP_PATH" = /home/lvuser/natinst/bin/TBLStartupApp.rtexe ] + then + APP_BOOT=`nirtcfg --file /etc/natinst/share/lvrt.conf --get section=LVRT,token=RTTarget.LaunchAppAtBoot,value=true | tr "[:upper:]" "[:lower:]"` + APP_DISABLED=`nirtcfg --get section=SYSTEMSETTINGS,token=NoApp.enabled,value=false | tr "[:upper:]" "[:lower:]"` + if [ "$APP_BOOT" = true ] && [ "$APP_DISABLED" = false ] + then + /usr/local/frc/bin/frcRunRobot.sh + fi + else + exec -a lvrt ./ni-lvrt + fi + sleep 1 +done \ No newline at end of file diff --git a/lib/examples/swerve/settings.gradle b/lib/examples/swerve/settings.gradle new file mode 100644 index 00000000..8ea4b5d1 --- /dev/null +++ b/lib/examples/swerve/settings.gradle @@ -0,0 +1,30 @@ +import org.gradle.internal.os.OperatingSystem + +pluginManagement { + repositories { + mavenLocal() + gradlePluginPortal() + String frcYear = '2026' + File frcHome + if (OperatingSystem.current().isWindows()) { + String publicFolder = System.getenv('PUBLIC') + if (publicFolder == null) { + publicFolder = "C:\\Users\\Public" + } + def homeRoot = new File(publicFolder, "wpilib") + frcHome = new File(homeRoot, frcYear) + } else { + def userFolder = System.getProperty("user.home") + def homeRoot = new File(userFolder, "wpilib") + frcHome = new File(homeRoot, frcYear) + } + def frcHomeMaven = new File(frcHome, 'maven') + maven { + name 'frcHome' + url frcHomeMaven + } + } +} + +Properties props = System.getProperties() +props.setProperty("org.gradle.internal.native.headers.unresolved.dependencies.ignore", "true") diff --git a/lib/examples/swerve/src/main/deploy/choreo/DEPOT.traj b/lib/examples/swerve/src/main/deploy/choreo/DEPOT.traj new file mode 100644 index 00000000..16e01b16 --- /dev/null +++ b/lib/examples/swerve/src/main/deploy/choreo/DEPOT.traj @@ -0,0 +1,153 @@ +{ + "name":"DEPOT", + "version":3, + "snapshot":{ + "waypoints":[ + {"x":3.7033300399780273, "y":5.980339527130127, "heading":3.141592653589793, "intervals":22, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, + {"x":1.141133427619934, "y":5.967901706695557, "heading":3.127100394245751, "intervals":17, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, + {"x":0.4082718193531037, "y":5.990222454071045, "heading":-3.1415914918446664, "intervals":17, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, + {"x":2.01178240776062, "y":5.955463409423828, "heading":0.0, "intervals":29, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, + {"x":1.9869070053100584, "y":3.828591346740722, "heading":1.5874614566235736, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], + "constraints":[ + {"from":"first", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, + {"from":"last", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, + {"from":"first", "to":"last", "data":{"type":"KeepInRectangle", "props":{"x":0.0, "y":0.0, "w":16.541, "h":8.0692}}, "enabled":true}, + {"from":1, "to":2, "data":{"type":"MaxVelocity", "props":{"max":1.0}}, "enabled":true}, + {"from":1, "to":3, "data":{"type":"MaxAngularVelocity", "props":{"max":0.0}}, "enabled":true}], + "targetDt":0.05 + }, + "params":{ + "waypoints":[ + {"x":{"exp":"3.7033300399780273 m", "val":3.7033300399780273}, "y":{"exp":"5.980339527130127 m", "val":5.980339527130127}, "heading":{"exp":"3.141592653589793 rad", "val":3.141592653589793}, "intervals":22, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, + {"x":{"exp":"1.141133427619934 m", "val":1.141133427619934}, "y":{"exp":"5.967901706695557 m", "val":5.967901706695557}, "heading":{"exp":"3.127100394245751 rad", "val":3.127100394245751}, "intervals":17, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, + {"x":{"exp":"0.40827181935310364 m", "val":0.4082718193531037}, "y":{"exp":"5.990222454071045 m", "val":5.990222454071045}, "heading":{"exp":"-3.1415914918446664 rad", "val":-3.1415914918446664}, "intervals":17, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, + {"x":{"exp":"2.01178240776062 m", "val":2.01178240776062}, "y":{"exp":"5.955463409423828 m", "val":5.955463409423828}, "heading":{"exp":"0 deg", "val":0.0}, "intervals":29, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, + {"x":{"exp":"1.9869070053100586 m", "val":1.9869070053100584}, "y":{"exp":"3.8285913467407227 m", "val":3.828591346740722}, "heading":{"exp":"1.5874614566235736 rad", "val":1.5874614566235736}, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], + "constraints":[ + {"from":"first", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, + {"from":"last", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, + {"from":"first", "to":"last", "data":{"type":"KeepInRectangle", "props":{"x":{"exp":"0 m", "val":0.0}, "y":{"exp":"0 m", "val":0.0}, "w":{"exp":"16.541 m", "val":16.541}, "h":{"exp":"8.0692 m", "val":8.0692}}}, "enabled":true}, + {"from":1, "to":2, "data":{"type":"MaxVelocity", "props":{"max":{"exp":"1 m / s", "val":1.0}}}, "enabled":true}, + {"from":1, "to":3, "data":{"type":"MaxAngularVelocity", "props":{"max":{"exp":"0 rad / s", "val":0.0}}}, "enabled":true}], + "targetDt":{ + "exp":"0.05 s", + "val":0.05 + } + }, + "trajectory":{ + "config":{ + "frontLeft":{ + "x":0.225425, + "y":0.333375 + }, + "backLeft":{ + "x":-0.225425, + "y":0.333375 + }, + "mass":68.0388555, + "inertia":7.5, + "gearing":7.03, + "radius":0.0508, + "vmax":607.3745796940267, + "tmax":1.2, + "cof":1.5, + "bumper":{ + "front":0.34925, + "side":0.4445, + "back":0.34925 + }, + "differentialTrackWidth":0.5588 + }, + "sampleType":"Swerve", + "waypoints":[0.0,0.94415,1.72511,2.4094,3.26434], + "samples":[ + {"t":0.0, "x":3.70333, "y":5.98034, "heading":3.14159, "vx":0.0, "vy":0.0, "omega":0.0, "ax":-9.75303, "ay":-0.03176, "alpha":-0.09915, "fx":[-165.90006,-165.89478,-165.89222,-165.89765], "fy":[0.28066,-1.35304,-1.37302,0.2848]}, + {"t":0.04292, "x":3.69435, "y":5.98031, "heading":3.14159, "vx":-0.41856, "vy":-0.00136, "omega":-0.00426, "ax":-9.75196, "ay":-0.03139, "alpha":-0.09884, "fx":[-165.88194,-165.87675,-165.87392,-165.87928], "fy":[0.28405,-1.34414,-1.36391,0.28823]}, + {"t":0.08583, "x":3.66741, "y":5.98022, "heading":3.14141, "vx":-0.83707, "vy":-0.00271, "omega":-0.0085, "ax":-9.75062, "ay":-0.03102, "alpha":-0.09845, "fx":[-165.8593,-165.85418,-165.85104,-165.85631], "fy":[0.28702,-1.33429,-1.35428,0.29078]}, + {"t":0.12875, "x":3.6225, "y":5.98008, "heading":3.14105, "vx":-1.25553, "vy":-0.00404, "omega":-0.01272, "ax":-9.7489, "ay":-0.03065, "alpha":-0.09795, "fx":[-165.83017,-165.82515,-165.8216,-165.82678], "fy":[0.28926,-1.32319,-1.34379,0.29215]}, + {"t":0.17166, "x":3.55964, "y":5.97988, "heading":3.1405, "vx":-1.67391, "vy":-0.00536, "omega":-0.01693, "ax":-9.7466, "ay":-0.03028, "alpha":-0.09727, "fx":[-165.79133,-165.78642,-165.78232,-165.7874], "fy":[0.29032,-1.31034,-1.33191,0.29189]}, + {"t":0.21458, "x":3.47883, "y":5.97962, "heading":3.13977, "vx":-2.09219, "vy":-0.00666, "omega":-0.0211, "ax":-9.74338, "ay":-0.02989, "alpha":-0.09634, "fx":[-165.73694,-165.73215,-165.7273,-165.73227], "fy":[0.28934,-1.29483,-1.31768,0.28916]}, + {"t":0.25749, "x":3.38007, "y":5.97931, "heading":3.13887, "vx":-2.51034, "vy":-0.00794, "omega":-0.02523, "ax":-9.73856, "ay":-0.0295, "alpha":-0.09493, "fx":[-165.65533,-165.65069,-165.64475,-165.64959], "fy":[0.28469,-1.27489,-1.29922,0.28233]}, + {"t":0.30041, "x":3.26337, "y":5.97894, "heading":3.13778, "vx":-2.92827, "vy":-0.00921, "omega":-0.02931, "ax":-9.73052, "ay":-0.02908, "alpha":-0.09261, "fx":[-165.51927,-165.51484,-165.50718,-165.51182], "fy":[0.27258,-1.24644,-1.2722,0.26765]}, + {"t":0.34333, "x":3.12874, "y":5.97852, "heading":3.13653, "vx":-3.34587, "vy":-0.01045, "omega":-0.03328, "ax":-9.71444, "ay":-0.02859, "alpha":-0.08808, "fx":[-165.24724,-165.24314,-165.23232,-165.23663], "fy":[0.24218,-1.19771,-1.22419,0.2344]}, + {"t":0.38624, "x":2.9762, "y":5.97804, "heading":3.1351, "vx":-3.76277, "vy":-0.01168, "omega":-0.03706, "ax":-9.66645, "ay":-0.02785, "alpha":-0.07537, "fx":[-164.43421,-164.43087,-164.41273,-164.41623], "fy":[0.14487,-1.07543,-1.0991,0.13461]}, + {"t":0.42916, "x":2.80582, "y":5.97751, "heading":3.13351, "vx":-4.17761, "vy":-0.01288, "omega":-0.0403, "ax":-4.82767, "ay":0.00382, "alpha":0.93655, "fx":[-79.12989,-79.09411,-85.10537,-85.13952], "fy":[-3.39746,3.45052,3.3697,-3.16305]}, + {"t":0.47207, "x":2.62209, "y":5.97696, "heading":3.13178, "vx":-4.3848, "vy":-0.01271, "omega":-0.0001, "ax":-0.00128, "ay":0.00422, "alpha":0.00029, "fx":[-0.02063,-0.02064,-0.02284,-0.02283], "fy":[0.07101,0.0725,0.07248,0.07099]}, + {"t":0.51499, "x":2.43391, "y":5.97642, "heading":3.13177, "vx":-4.38485, "vy":-0.01253, "omega":-0.00009, "ax":0.00029, "ay":-0.03094, "alpha":-0.00007, "fx":[0.0047,0.00472,0.00523,0.00521], "fy":[-0.52606,-0.52641,-0.52638,-0.52604]}, + {"t":0.55791, "x":2.24573, "y":5.97586, "heading":3.13177, "vx":-4.38484, "vy":-0.01386, "omega":-0.0001, "ax":1.05143, "ay":-0.24558, "alpha":-0.41961, "fx":[16.29224,16.26004,19.47581,19.50981], "fy":[-3.05602,-5.28375,-5.29398,-3.07554]}, + {"t":0.60082, "x":2.05852, "y":5.97504, "heading":3.13177, "vx":-4.33972, "vy":-0.0244, "omega":-0.0181, "ax":9.65858, "ay":0.01752, "alpha":-0.04815, "fx":[164.28282,164.28417,164.29659,164.2953], "fy":[0.69727,-0.08788,-0.09861,0.6811]}, + {"t":0.64374, "x":1.88117, "y":5.97401, "heading":3.13099, "vx":-3.92521, "vy":-0.02365, "omega":-0.02017, "ax":9.71258, "ay":0.02128, "alpha":0.02972, "fx":[165.21059,165.20957,165.2058,165.20686], "fy":[0.11346,0.60114,0.61144,0.12163]}, + {"t":0.68665, "x":1.72166, "y":5.97301, "heading":3.13012, "vx":-3.50839, "vy":-0.02273, "omega":-0.01889, "ax":9.72974, "ay":0.02228, "alpha":0.05453, "fx":[165.50336,165.5014,165.49678,165.49885], "fy":[-0.07843,0.81805,0.83996,-0.06373]}, + {"t":0.72957, "x":1.58006, "y":5.97205, "heading":3.12931, "vx":-3.09083, "vy":-0.02178, "omega":-0.01655, "ax":9.73815, "ay":0.0226, "alpha":0.06668, "fx":[165.64653,165.6441,165.63982,165.64243], "fy":[-0.17608,0.92118,0.9504,-0.15769]}, + {"t":0.77248, "x":1.45638, "y":5.97114, "heading":3.1286, "vx":-2.67291, "vy":-0.02081, "omega":-0.01369, "ax":9.74315, "ay":0.02265, "alpha":0.07388, "fx":[165.73137,165.72868,165.72484,165.72777], "fy":[-0.23671,0.9797,1.01406,-0.21567]}, + {"t":0.8154, "x":1.35064, "y":5.97027, "heading":3.12801, "vx":-2.25477, "vy":-0.01983, "omega":-0.01052, "ax":9.74645, "ay":0.02257, "alpha":0.07864, "fx":[165.78749,165.78464,165.78119,165.78431], "fy":[-0.27898,1.01625,1.05442,-0.25592]}, + {"t":0.85832, "x":1.26285, "y":5.96944, "heading":3.12756, "vx":-1.8365, "vy":-0.01887, "omega":-0.00715, "ax":9.7488, "ay":0.02241, "alpha":0.08201, "fx":[165.82734,165.8244,165.82127,165.82452], "fy":[-0.31078,1.0404,1.08145,-0.28619]}, + {"t":0.90123, "x":1.19301, "y":5.96865, "heading":3.12726, "vx":-1.41812, "vy":-0.0179, "omega":-0.00363, "ax":9.75056, "ay":0.0222, "alpha":0.08453, "fx":[165.85711,165.85411,165.85125,165.85458], "fy":[-0.33601,1.05694,1.10012,-0.31033]}, + {"t":0.94415, "x":1.14113, "y":5.9679, "heading":3.1271, "vx":-0.99966, "vy":-0.01695, "omega":0.0, "ax":0.04698, "ay":-1.09887, "alpha":0.0, "fx":[0.79911,0.79911,0.79911,0.79911], "fy":[-18.69149,-18.69149,-18.69149,-18.69149]}, + {"t":0.99009, "x":1.09526, "y":5.96596, "heading":3.1271, "vx":-0.99751, "vy":-0.06743, "omega":0.0, "ax":0.00061, "ay":-0.00899, "alpha":0.0, "fx":[0.01037,0.01037,0.01037,0.01037], "fy":[-0.15299,-0.15299,-0.15299,-0.15299]}, + {"t":1.03603, "x":1.04944, "y":5.96286, "heading":3.1271, "vx":-0.99748, "vy":-0.06785, "omega":0.0, "ax":-0.00002, "ay":0.00034, "alpha":0.0, "fx":[-0.0004,-0.0004,-0.0004,-0.0004], "fy":[0.00584,0.00584,0.00584,0.00584]}, + {"t":1.08196, "x":1.00361, "y":5.95974, "heading":3.1271, "vx":-0.99748, "vy":-0.06783, "omega":0.0, "ax":-0.00003, "ay":0.00043, "alpha":0.0, "fx":[-0.0005,-0.0005,-0.0005,-0.0005], "fy":[0.00724,0.00724,0.00724,0.00724]}, + {"t":1.1279, "x":0.95779, "y":5.95662, "heading":3.1271, "vx":-0.99748, "vy":-0.06781, "omega":0.0, "ax":-0.00003, "ay":0.00043, "alpha":0.0, "fx":[-0.0005,-0.0005,-0.0005,-0.0005], "fy":[0.00733,0.00733,0.00733,0.00733]}, + {"t":1.17384, "x":0.91197, "y":5.95351, "heading":3.1271, "vx":-0.99748, "vy":-0.06779, "omega":0.0, "ax":-0.00003, "ay":0.00044, "alpha":0.0, "fx":[-0.00051,-0.00051,-0.00051,-0.00051], "fy":[0.00741,0.00741,0.00741,0.00741]}, + {"t":1.21978, "x":0.86614, "y":5.9504, "heading":3.1271, "vx":-0.99748, "vy":-0.06777, "omega":0.0, "ax":-0.00003, "ay":0.00044, "alpha":0.0, "fx":[-0.00052,-0.00052,-0.00052,-0.00052], "fy":[0.00751,0.00751,0.00751,0.00751]}, + {"t":1.26572, "x":0.82032, "y":5.94728, "heading":3.1271, "vx":-0.99749, "vy":-0.06775, "omega":0.0, "ax":-0.00003, "ay":0.00045, "alpha":0.0, "fx":[-0.00052,-0.00052,-0.00052,-0.00052], "fy":[0.00763,0.00763,0.00763,0.00763]}, + {"t":1.31166, "x":0.7745, "y":5.94417, "heading":3.1271, "vx":-0.99749, "vy":-0.06773, "omega":0.0, "ax":-0.00003, "ay":0.00046, "alpha":0.0, "fx":[-0.00053,-0.00053,-0.00053,-0.00053], "fy":[0.00778,0.00778,0.00778,0.00778]}, + {"t":1.3576, "x":0.72867, "y":5.94106, "heading":3.1271, "vx":-0.99749, "vy":-0.06771, "omega":0.0, "ax":-0.00003, "ay":0.00047, "alpha":0.0, "fx":[-0.00055,-0.00055,-0.00055,-0.00055], "fy":[0.00797,0.00797,0.00797,0.00797]}, + {"t":1.40354, "x":0.68285, "y":5.93795, "heading":3.1271, "vx":-0.99749, "vy":-0.06769, "omega":0.0, "ax":-0.00003, "ay":0.00048, "alpha":0.0, "fx":[-0.00057,-0.00057,-0.00057,-0.00057], "fy":[0.00822,0.00822,0.00822,0.00822]}, + {"t":1.44947, "x":0.63703, "y":5.93484, "heading":3.1271, "vx":-0.99749, "vy":-0.06766, "omega":0.0, "ax":-0.00004, "ay":0.00058, "alpha":0.0, "fx":[-0.00067,-0.00067,-0.00067,-0.00067], "fy":[0.0098,0.0098,0.0098,0.0098]}, + {"t":1.49541, "x":0.5912, "y":5.93173, "heading":3.1271, "vx":-0.99749, "vy":-0.06764, "omega":0.0, "ax":-0.00063, "ay":0.00937, "alpha":0.0, "fx":[-0.01078,-0.01078,-0.01078,-0.01078], "fy":[0.1594,0.1594,0.1594,0.1594]}, + {"t":1.54135, "x":0.54538, "y":5.92864, "heading":3.1271, "vx":-0.99752, "vy":-0.06721, "omega":0.0, "ax":-0.04497, "ay":1.03556, "alpha":0.0, "fx":[-0.765,-0.765,-0.765,-0.765], "fy":[17.61461,17.61461,17.61461,17.61461]}, + {"t":1.58729, "x":0.49951, "y":5.92664, "heading":3.1271, "vx":-0.99959, "vy":-0.01963, "omega":0.0, "ax":1.85009, "ay":9.20613, "alpha":0.0, "fx":[31.46956,31.46956,31.46956,31.46956], "fy":[156.59357,156.59357,156.59357,156.59357]}, + {"t":1.63323, "x":0.45554, "y":5.93545, "heading":3.1271, "vx":-0.9146, "vy":0.40328, "omega":0.0, "ax":8.4588, "ay":4.8153, "alpha":0.0, "fx":[143.88173,143.88173,143.88173,143.88173], "fy":[81.90682,81.90682,81.90682,81.90682]}, + {"t":1.67917, "x":0.42245, "y":5.95906, "heading":3.1271, "vx":-0.52601, "vy":0.62449, "omega":0.0, "ax":9.4653, "ay":2.34333, "alpha":0.0, "fx":[161.00202,161.00202,161.00202,161.00202], "fy":[39.85944,39.85944,39.85944,39.85944]}, + {"t":1.72511, "x":0.40827, "y":5.99022, "heading":3.1271, "vx":-0.09119, "vy":0.73214, "omega":0.0, "ax":9.58027, "ay":1.82225, "alpha":0.0, "fx":[162.9577,162.9577,162.9577,162.9577], "fy":[30.99602,30.99602,30.99602,30.99602]}, + {"t":1.76536, "x":0.41236, "y":6.02117, "heading":3.1271, "vx":0.29445, "vy":0.80549, "omega":0.0, "ax":9.63169, "ay":1.51949, "alpha":0.0, "fx":[163.83224,163.83224,163.83224,163.83224], "fy":[25.84611,25.84611,25.84611,25.84611]}, + {"t":1.80561, "x":0.43202, "y":6.05482, "heading":3.1271, "vx":0.68215, "vy":0.86666, "omega":0.0, "ax":9.68275, "ay":1.13706, "alpha":0.0, "fx":[164.70079,164.70079,164.70079,164.70079], "fy":[19.34105,19.34105,19.34105,19.34105]}, + {"t":1.84587, "x":0.46732, "y":6.09063, "heading":3.1271, "vx":1.07191, "vy":0.91243, "omega":0.0, "ax":9.72619, "ay":0.64204, "alpha":0.0, "fx":[165.43967,165.43967,165.43967,165.43967], "fy":[10.92085,10.92085,10.92085,10.92085]}, + {"t":1.88612, "x":0.51835, "y":6.12788, "heading":3.1271, "vx":1.46341, "vy":0.93827, "omega":0.0, "ax":9.74485, "ay":-0.01709, "alpha":0.0, "fx":[165.75707,165.75707,165.75707,165.75707], "fy":[-0.29068,-0.29068,-0.29068,-0.29068]}, + {"t":1.92637, "x":0.58515, "y":6.16563, "heading":3.1271, "vx":1.85567, "vy":0.93758, "omega":0.0, "ax":9.69785, "ay":-0.92196, "alpha":0.0, "fx":[164.95766,164.95766,164.95766,164.95766], "fy":[-15.68234,-15.68234,-15.68234,-15.68234]}, + {"t":1.96662, "x":0.6677, "y":6.20263, "heading":3.1271, "vx":2.24603, "vy":0.90047, "omega":0.0, "ax":9.48599, "ay":-2.19757, "alpha":0.0, "fx":[161.35399,161.35399,161.35399,161.35399], "fy":[-37.38012,-37.38012,-37.38012,-37.38012]}, + {"t":2.00688, "x":0.7658, "y":6.23709, "heading":3.1271, "vx":2.62787, "vy":0.81201, "omega":0.0, "ax":8.87245, "ay":-3.99806, "alpha":0.0, "fx":[150.91776,150.91776,150.91776,150.91776], "fy":[-68.00594,-68.00594,-68.00594,-68.00594]}, + {"t":2.04713, "x":0.87876, "y":6.26654, "heading":3.1271, "vx":2.98501, "vy":0.65108, "omega":0.0, "ax":7.38032, "ay":-6.33403, "alpha":0.0, "fx":[125.53714,125.53714,125.53714,125.53714], "fy":[-107.74008,-107.74008,-107.74008,-107.74008]}, + {"t":2.08738, "x":1.0049, "y":6.28761, "heading":3.1271, "vx":3.28209, "vy":0.39612, "omega":0.0, "ax":4.56235, "ay":-8.58558, "alpha":0.0, "fx":[77.60432,77.60432,77.60432,77.60432], "fy":[-146.03829,-146.03829,-146.03829,-146.03829]}, + {"t":2.12764, "x":1.14071, "y":6.2966, "heading":3.1271, "vx":3.46574, "vy":0.05052, "omega":0.0, "ax":1.04918, "ay":-9.66822, "alpha":0.0, "fx":[17.84626,17.84626,17.84626,17.84626], "fy":[-164.4537,-164.4537,-164.4537,-164.4537]}, + {"t":2.16789, "x":1.28106, "y":6.29081, "heading":3.1271, "vx":3.50797, "vy":-0.33865, "omega":0.0, "ax":-1.84957, "ay":-9.55339, "alpha":0.0, "fx":[-31.4607,-31.4607,-31.4607,-31.4607], "fy":[-162.50035,-162.50035,-162.50035,-162.50035]}, + {"t":2.20814, "x":1.42077, "y":6.26943, "heading":3.1271, "vx":3.43352, "vy":-0.7232, "omega":0.0, "ax":-3.7718, "ay":-8.97623, "alpha":0.0, "fx":[-64.1572,-64.1572,-64.1572,-64.1572], "fy":[-152.68317,-152.68317,-152.68317,-152.68317]}, + {"t":2.24839, "x":1.55592, "y":6.23305, "heading":3.1271, "vx":3.28169, "vy":-1.08452, "omega":0.0, "ax":-4.98969, "ay":-8.36603, "alpha":0.0, "fx":[-84.87317,-84.87317,-84.87317,-84.87317], "fy":[-142.30373,-142.30373,-142.30373,-142.30373]}, + {"t":2.28865, "x":1.68398, "y":6.18262, "heading":3.1271, "vx":3.08085, "vy":-1.42127, "omega":0.0, "ax":-5.78309, "ay":-7.84282, "alpha":0.0, "fx":[-98.36874,-98.36874,-98.36874,-98.36874], "fy":[-133.40412,-133.40412,-133.40412,-133.40412]}, + {"t":2.3289, "x":1.8033, "y":6.11906, "heading":3.1271, "vx":2.84806, "vy":-1.73697, "omega":0.0, "ax":-6.32408, "ay":-7.4169, "alpha":0.0, "fx":[-107.5708,-107.5708,-107.5708,-107.5708], "fy":[-126.15934,-126.15934,-126.15934,-126.15934]}, + {"t":2.36915, "x":1.91282, "y":6.04313, "heading":3.1271, "vx":2.5935, "vy":-2.03552, "omega":0.0, "ax":-6.70971, "ay":-7.0727, "alpha":0.0, "fx":[-114.1303,-114.1303,-114.1303,-114.1303], "fy":[-120.30455,-120.30455,-120.30455,-120.30455]}, + {"t":2.4094, "x":2.01178, "y":5.95546, "heading":3.1271, "vx":2.32341, "vy":-2.32021, "omega":0.0, "ax":-5.9679, "ay":-6.77454, "alpha":-9.62165, "fx":[-158.2226,-126.2258,-37.23442,-84.36622], "fy":[-49.58274,-107.5984,-161.53004,-142.22086]}, + {"t":2.43889, "x":2.07768, "y":5.88412, "heading":3.1271, "vx":2.14748, "vy":-2.51993, "omega":-0.28365, "ax":-6.22896, "ay":-6.60978, "alpha":-9.13493, "fx":[-158.59772,-127.86037,-43.0544,-94.29898], "fy":[-48.28734,-105.61735,-160.04118,-135.77615]}, + {"t":2.46837, "x":2.13829, "y":5.80696, "heading":3.11874, "vx":1.96384, "vy":-2.71479, "omega":-0.55296, "ax":-6.5172, "ay":-6.39924, "alpha":-8.67285, "fx":[-159.18867,-130.14943,-50.27634,-103.80838], "fy":[-46.1962,-102.74128,-157.87795,-128.58164]}, + {"t":2.49785, "x":2.19335, "y":5.72414, "heading":3.10244, "vx":1.77171, "vy":-2.90345, "omega":-0.80864, "ax":-6.84461, "ay":-6.12749, "alpha":-8.19268, "fx":[-159.9767,-133.13028,-59.25873,-113.33388], "fy":[-43.24795,-98.79352,-154.67647,-120.18921]}, + {"t":2.52733, "x":2.24261, "y":5.63588, "heading":3.0786, "vx":1.56993, "vy":-3.08409, "omega":-1.05017, "ax":-7.22531, "ay":-5.76879, "alpha":-7.64447, "fx":[-160.93522,-136.86214,-70.53855,-123.2658], "fy":[-39.33667,-93.48113,-149.79879,-109.88545]}, + {"t":2.55681, "x":2.28575, "y":5.54246, "heading":3.04764, "vx":1.35692, "vy":-3.25416, "omega":-1.27553, "ax":-7.67403, "ay":-5.28138, "alpha":-6.96355, "fx":[-162.02471,-141.42947,-84.87796,-133.80038], "fy":[-34.28798,-86.30957,-142.07025,-96.67093]}, + {"t":2.58629, "x":2.32242, "y":5.44423, "heading":3.01003, "vx":1.13068, "vy":-3.40986, "omega":-1.48082, "ax":-8.20109, "ay":-4.59726, "alpha":-6.05807, "fx":[-163.1807,-146.9306,-103.19642,-144.68479], "fy":[-27.81337,-76.40591,-129.24053,-79.33242]}, + {"t":2.61577, "x":2.35219, "y":5.3417, "heading":2.96638, "vx":0.88891, "vy":-3.54539, "omega":-1.65942, "ax":-8.79525, "ay":-3.60722, "alpha":-4.79665, "fx":[-164.2832,-153.38937,-125.87916,-154.86677], "fy":[-19.42086,-62.14523,-107.05653,-56.80843]}, + {"t":2.64525, "x":2.37457, "y":5.23561, "heading":2.91746, "vx":0.62962, "vy":-3.65173, "omega":-1.80082, "ax":-9.37004, "ay":-2.16452, "alpha":-3.06979, "fx":[-165.07286,-160.29083,-149.83644,-162.32639], "fy":[-8.24256,-40.53617,-69.3038,-29.18892]}, + {"t":2.67473, "x":2.38906, "y":5.12702, "heading":2.86437, "vx":0.35338, "vy":-3.71554, "omega":-1.89132, "ax":-9.66296, "ay":-0.47806, "alpha":-1.55074, "fx":[-164.93844,-164.6331,-163.02442,-164.8609], "fy":[6.98388,-12.68236,-25.01402,-1.81437]}, + {"t":2.70421, "x":2.39528, "y":5.01727, "heading":2.80861, "vx":0.06851, "vy":-3.72964, "omega":-1.93704, "ax":-9.63608, "ay":0.54738, "alpha":-1.80678, "fx":[-162.80398,-164.91789,-164.3415,-163.56433], "fy":[26.24697,3.04869,-10.40675,18.35398]}, + {"t":2.73369, "x":2.39311, "y":4.90756, "heading":2.75151, "vx":-0.21556, "vy":-3.7135, "omega":-1.99031, "ax":-9.45559, "ay":1.59404, "alpha":-2.69027, "fx":[-157.02956,-163.86996,-164.24786,-158.20011], "fy":[49.73084,17.1615,-2.11677,43.68077]}, + {"t":2.76317, "x":2.38265, "y":4.79878, "heading":2.69283, "vx":-0.49432, "vy":-3.66651, "omega":-2.06962, "ax":-9.00544, "ay":2.84238, "alpha":-4.27347, "fx":[-145.51414,-161.35878,-163.55181,-142.29539], "fy":[76.77915,32.37511,3.98101,80.25676]}, + {"t":2.79265, "x":2.36416, "y":4.69192, "heading":2.63182, "vx":-0.75981, "vy":-3.58271, "omega":-2.1956, "ax":-7.98278, "ay":4.38014, "alpha":-7.00269, "fx":[-126.30498,-156.30916,-161.93137,-98.59381], "fy":[105.18263,50.8421,12.93244,129.06252]}, + {"t":2.82213, "x":2.33829, "y":4.5882, "heading":2.56709, "vx":-0.99515, "vy":-3.45358, "omega":-2.40205, "ax":-6.17213, "ay":5.9355, "alpha":-10.36658, "fx":[-98.87944,-146.45829,-155.69908,-18.90777], "fy":[131.17126,74.15216,37.57254,160.94867]}, + {"t":2.85162, "x":2.30627, "y":4.48897, "heading":2.49627, "vx":-1.17711, "vy":-3.2786, "omega":-2.70766, "ax":-4.01683, "ay":6.97646, "alpha":-12.69006, "fx":[-67.02797,-132.82502,-132.55122,59.10397], "fy":[149.96806,96.21382,77.11218,151.3766]}, + {"t":2.8811, "x":2.26983, "y":4.39534, "heading":2.41645, "vx":-1.29552, "vy":-3.07293, "omega":-3.08177, "ax":-0.34937, "ay":8.55357, "alpha":-11.07355, "fx":[-30.65918,-101.71791,20.39353,88.2126], "fy":[161.42691,128.50169,154.68873,137.35753]}, + {"t":2.91058, "x":2.23148, "y":4.30847, "heading":2.3256, "vx":-1.30582, "vy":-2.82076, "omega":-3.40823, "ax":1.59761, "ay":9.52496, "alpha":0.74197, "fx":[29.17513,33.2747,25.2491,21.00086], "fy":[161.67052,160.93927,162.45445,163.003]}, + {"t":2.94006, "x":2.19368, "y":4.22945, "heading":2.22512, "vx":-1.25873, "vy":-2.53996, "omega":-3.38635, "ax":2.83092, "ay":8.95992, "alpha":6.54608, "fx":[71.4913,95.06673,32.42521,-6.37103], "fy":[148.01543,134.79297,161.96722,164.8473]}, + {"t":2.96954, "x":2.1578, "y":4.15846, "heading":2.12529, "vx":-1.17527, "vy":-2.27581, "omega":-3.19337, "ax":3.51483, "ay":8.41599, "alpha":9.09785, "fx":[94.91377,120.36955,39.98687,-16.12508], "fy":[134.32075,113.21959,160.56392,164.50992]}, + {"t":2.99902, "x":2.12468, "y":4.09503, "heading":2.03115, "vx":-1.07165, "vy":-2.0277, "omega":-2.92516, "ax":3.94388, "ay":8.02729, "alpha":10.23151, "fx":[105.95459,133.88009,47.26326,-18.76076], "fy":[125.8602,97.15522,158.72962,164.42292]}, + {"t":3.0285, "x":2.0948, "y":4.03874, "heading":1.94491, "vx":-0.95538, "vy":-1.79105, "omega":-2.62353, "ax":4.21824, "ay":7.77393, "alpha":10.64812, "fx":[108.90806,142.41963,53.97943,-18.30317], "fy":[123.31836,84.34513,156.66864,164.59706]}, + {"t":3.05798, "x":2.06847, "y":3.98931, "heading":1.86757, "vx":-0.83102, "vy":-1.56187, "omega":-2.30961, "ax":4.37092, "ay":7.63037, "alpha":10.69562, "fx":[105.51601,148.34576,60.02446,-16.49387], "fy":[126.1894,73.58237,154.51852,164.87137]}, + {"t":3.08746, "x":2.04587, "y":3.94659, "heading":1.79948, "vx":-0.70217, "vy":-1.33693, "omega":-1.9943, "ax":4.40758, "ay":7.57339, "alpha":10.60339, "fx":[96.02209,152.66818,65.36339,-14.16728], "fy":[133.48691,64.26718,152.38402,165.14683]}, + {"t":3.11694, "x":2.02709, "y":3.91046, "heading":1.74068, "vx":-0.57223, "vy":-1.11366, "omega":-1.6817, "ax":4.3262, "ay":7.57397, "alpha":10.56761, "fx":[80.21112,155.89122,69.99579,-11.74871], "fy":[143.48066,56.11471,150.34825,165.38036]}, + {"t":3.14642, "x":2.0121, "y":3.88092, "heading":1.69111, "vx":-0.44469, "vy":-0.89037, "omega":-1.37016, "ax":4.13776, "ay":7.59191, "alpha":10.75621, "fx":[58.76247,158.29962,73.93249,-9.46628], "fy":[153.48733,49.01746,148.48003,165.56011]}, + {"t":3.1759, "x":2.00078, "y":3.85797, "heading":1.65071, "vx":-0.3227, "vy":-0.66656, "omega":-1.05306, "ax":3.88424, "ay":7.58699, "alpha":11.23683, "fx":[34.48087,160.07594,77.18057,-7.45845], "fy":[160.71569,42.96547,146.84041,165.68873]}, + {"t":3.20538, "x":1.99296, "y":3.84162, "heading":1.61967, "vx":-0.20819, "vy":-0.44289, "omega":-0.72179, "ax":3.63056, "ay":7.5438, "alpha":11.91068, "fx":[11.76645,161.35282,79.73373,-5.8337], "fy":[164.01727,37.99115,145.4883,165.77451]}, + {"t":3.23486, "x":1.9884, "y":3.83184, "heading":1.59839, "vx":-0.10116, "vy":-0.22049, "omega":-0.37066, "ax":3.43148, "ay":7.4791, "alpha":12.57291, "fx":[-5.63216,162.23384,81.5694,-4.69735], "fy":[164.42428,34.13534,144.48244,165.82715]}, + {"t":3.26434, "x":1.98691, "y":3.82859, "heading":1.58746, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0,0.0,0.0,0.0]}], + "splits":[0] + }, + "events":[] +} diff --git a/lib/examples/swerve/src/main/deploy/choreo/LEFT_TRENCH.traj b/lib/examples/swerve/src/main/deploy/choreo/LEFT_TRENCH.traj new file mode 100644 index 00000000..863d839f --- /dev/null +++ b/lib/examples/swerve/src/main/deploy/choreo/LEFT_TRENCH.traj @@ -0,0 +1,387 @@ +{ + "name":"LEFT_TRENCH", + "version":3, + "snapshot":{ + "waypoints":[ + {"x":4.441253185272217, "y":7.693458557128906, "heading":-1.5707972442597578, "intervals":38, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, + {"x":7.795207023620605, "y":7.227133750915527, "heading":-1.5707963267948966, "intervals":56, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, + {"x":7.795207023620605, "y":4.531103515625, "heading":-1.5707963267948966, "intervals":129, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, + {"x":5.723647117614746, "y":7.42442512512207, "heading":0.0, "intervals":51, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, + {"x":3.2933757305145264, "y":7.325779438018799, "heading":-1.5707963267948966, "intervals":33, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, + {"x":2.809114933013916, "y":5.299058437347412, "heading":0.8379811662545806, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], + "constraints":[ + {"from":"first", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, + {"from":"last", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, + {"from":"first", "to":"last", "data":{"type":"KeepInRectangle", "props":{"x":0.0, "y":0.0, "w":16.541, "h":8.0692}}, "enabled":true}, + {"from":1, "to":2, "data":{"type":"KeepInLane", "props":{"tolerance":0.00001}}, "enabled":true}, + {"from":1, "to":2, "data":{"type":"MaxAngularVelocity", "props":{"max":0.0}}, "enabled":true}, + {"from":"first", "to":"last", "data":{"type":"MaxVelocity", "props":{"max":2.0}}, "enabled":true}, + {"from":3, "to":4, "data":{"type":"MaxAngularVelocity", "props":{"max":0.0}}, "enabled":true}, + {"from":1, "to":2, "data":{"type":"MaxVelocity", "props":{"max":1.0}}, "enabled":true}, + {"from":2, "to":3, "data":{"type":"MaxAngularVelocity", "props":{"max":0.5}}, "enabled":true}, + {"from":3, "to":4, "data":{"type":"MaxVelocity", "props":{"max":1.0}}, "enabled":true}], + "targetDt":0.05 + }, + "params":{ + "waypoints":[ + {"x":{"exp":"4.441253185272217 m", "val":4.441253185272217}, "y":{"exp":"7.693458557128906 m", "val":7.693458557128906}, "heading":{"exp":"-1.5707972442597578 rad", "val":-1.5707972442597578}, "intervals":38, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, + {"x":{"exp":"7.7952070236206055 m", "val":7.795207023620605}, "y":{"exp":"7.227133750915527 m", "val":7.227133750915527}, "heading":{"exp":"-1.5707963267948966 rad", "val":-1.5707963267948966}, "intervals":56, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, + {"x":{"exp":"7.7952070236206055 m", "val":7.795207023620605}, "y":{"exp":"4.531103515625 m", "val":4.531103515625}, "heading":{"exp":"-1.5707963267948966 rad", "val":-1.5707963267948966}, "intervals":129, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, + {"x":{"exp":"5.723647117614746 m", "val":5.723647117614746}, "y":{"exp":"7.42442512512207 m", "val":7.42442512512207}, "heading":{"exp":"0 rad", "val":0.0}, "intervals":51, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, + {"x":{"exp":"3.2933757305145264 m", "val":3.2933757305145264}, "y":{"exp":"7.325779438018799 m", "val":7.325779438018799}, "heading":{"exp":"-90 deg", "val":-1.5707963267948966}, "intervals":33, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, + {"x":{"exp":"2.809114933013916 m", "val":2.809114933013916}, "y":{"exp":"5.299058437347412 m", "val":5.299058437347412}, "heading":{"exp":"0.8379811662545806 rad", "val":0.8379811662545806}, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], + "constraints":[ + {"from":"first", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, + {"from":"last", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, + {"from":"first", "to":"last", "data":{"type":"KeepInRectangle", "props":{"x":{"exp":"0 m", "val":0.0}, "y":{"exp":"0 m", "val":0.0}, "w":{"exp":"16.541 m", "val":16.541}, "h":{"exp":"8.0692 m", "val":8.0692}}}, "enabled":true}, + {"from":1, "to":2, "data":{"type":"KeepInLane", "props":{"tolerance":{"exp":"1e-5 m", "val":0.00001}}}, "enabled":true}, + {"from":1, "to":2, "data":{"type":"MaxAngularVelocity", "props":{"max":{"exp":"0 rad / s", "val":0.0}}}, "enabled":true}, + {"from":"first", "to":"last", "data":{"type":"MaxVelocity", "props":{"max":{"exp":"2 m / s", "val":2.0}}}, "enabled":true}, + {"from":3, "to":4, "data":{"type":"MaxAngularVelocity", "props":{"max":{"exp":"0 rad / s", "val":0.0}}}, "enabled":true}, + {"from":1, "to":2, "data":{"type":"MaxVelocity", "props":{"max":{"exp":"1 m / s", "val":1.0}}}, "enabled":true}, + {"from":2, "to":3, "data":{"type":"MaxAngularVelocity", "props":{"max":{"exp":"0.5 rad / s", "val":0.5}}}, "enabled":true}, + {"from":3, "to":4, "data":{"type":"MaxVelocity", "props":{"max":{"exp":"1 m / s", "val":1.0}}}, "enabled":true}], + "targetDt":{ + "exp":"0.05 s", + "val":0.05 + } + }, + "trajectory":{ + "config":{ + "frontLeft":{ + "x":0.225425, + "y":0.333375 + }, + "backLeft":{ + "x":-0.225425, + "y":0.333375 + }, + "mass":68.0388555, + "inertia":7.5, + "gearing":7.03, + "radius":0.0508, + "vmax":607.3745796940267, + "tmax":1.2, + "cof":1.5, + "bumper":{ + "front":0.34925, + "side":0.4445, + "back":0.34925 + }, + "differentialTrackWidth":0.5588 + }, + "sampleType":"Swerve", + "waypoints":[0.0,1.88626,4.58447,7.75189,10.19451,11.3735], + "samples":[ + {"t":0.0, "x":4.44125, "y":7.69346, "heading":-1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":9.71375, "ay":-0.89248, "alpha":0.0, "fx":[165.22814,165.22814,165.22814,165.22814], "fy":[-15.18078,-15.18078,-15.18079,-15.18079]}, + {"t":0.04964, "x":4.45322, "y":7.69236, "heading":-1.5708, "vx":0.48217, "vy":-0.0443, "omega":0.0, "ax":9.71164, "ay":-0.88564, "alpha":0.0, "fx":[165.1922,165.1922,165.1922,165.1922], "fy":[-15.06444,-15.06444,-15.06445,-15.06445]}, + {"t":0.09928, "x":4.48912, "y":7.68907, "heading":-1.5708, "vx":0.96424, "vy":-0.08826, "omega":0.0, "ax":9.70675, "ay":-0.87909, "alpha":0.0, "fx":[165.10907,165.10907,165.10907,165.10907], "fy":[-14.95309,-14.9531,-14.9531,-14.9531]}, + {"t":0.14891, "x":4.54894, "y":7.6836, "heading":-1.5708, "vx":1.44607, "vy":-0.1319, "omega":0.0, "ax":9.69092, "ay":-0.87202, "alpha":0.0, "fx":[164.83972,164.83972,164.83972,164.83972], "fy":[-14.83278,-14.83278,-14.83279,-14.83279]}, + {"t":0.19855, "x":4.63266, "y":7.67598, "heading":-1.5708, "vx":1.92711, "vy":-0.17518, "omega":0.0, "ax":1.30117, "ay":-0.04707, "alpha":0.00006, "fx":[22.13265,22.13235,22.13235,22.13265], "fy":[-0.80035,-0.80035,-0.80081,-0.80081]}, + {"t":0.24819, "x":4.72992, "y":7.66723, "heading":-1.5708, "vx":1.9917, "vy":-0.17752, "omega":0.0, "ax":0.00291, "ay":0.03269, "alpha":0.00002, "fx":[0.04948,0.0494,0.0494,0.04948], "fy":[0.55606,0.55606,0.55593,0.55593]}, + {"t":0.29783, "x":4.82879, "y":7.65846, "heading":-1.5708, "vx":1.99184, "vy":-0.1759, "omega":0.0, "ax":0.00247, "ay":0.02806, "alpha":-0.00001, "fx":[0.04196,0.04199,0.04199,0.04196], "fy":[0.47736,0.47736,0.4774,0.4774]}, + {"t":0.34747, "x":4.92767, "y":7.64976, "heading":-1.5708, "vx":1.99197, "vy":-0.17451, "omega":0.0, "ax":0.00219, "ay":0.02507, "alpha":-0.00001, "fx":[0.03718,0.03725,0.03725,0.03718], "fy":[0.42642,0.42642,0.42653,0.42653]}, + {"t":0.39711, "x":5.02655, "y":7.64113, "heading":-1.5708, "vx":1.99207, "vy":-0.17326, "omega":0.0, "ax":0.00197, "ay":0.02276, "alpha":-0.00002, "fx":[0.03352,0.03359,0.03359,0.03352], "fy":[0.38711,0.38711,0.38723,0.38723]}, + {"t":0.44674, "x":5.12543, "y":7.63256, "heading":-1.5708, "vx":1.99217, "vy":-0.17213, "omega":0.0, "ax":0.0018, "ay":0.02089, "alpha":-0.00001, "fx":[0.03056,0.03064,0.03064,0.03056], "fy":[0.35526,0.35526,0.35537,0.35537]}, + {"t":0.49638, "x":5.22432, "y":7.62404, "heading":-1.5708, "vx":1.99226, "vy":-0.17109, "omega":0.0, "ax":0.00166, "ay":0.01934, "alpha":-0.00001, "fx":[0.02813,0.02819,0.02819,0.02813], "fy":[0.32886,0.32886,0.32895,0.32895]}, + {"t":0.54602, "x":5.32322, "y":7.61557, "heading":-1.5708, "vx":1.99234, "vy":-0.17013, "omega":0.0, "ax":0.00154, "ay":0.01803, "alpha":-0.00001, "fx":[0.02609,0.02613,0.02613,0.02609], "fy":[0.30663,0.30663,0.30669,0.30669]}, + {"t":0.59566, "x":5.42211, "y":7.60715, "heading":-1.5708, "vx":1.99242, "vy":-0.16924, "omega":0.0, "ax":0.00143, "ay":0.01691, "alpha":-0.00001, "fx":[0.02435,0.02438,0.02438,0.02435], "fy":[0.28761,0.28761,0.28765,0.28765]}, + {"t":0.6453, "x":5.52102, "y":7.59877, "heading":-1.5708, "vx":1.99249, "vy":-0.1684, "omega":0.0, "ax":0.00134, "ay":0.01594, "alpha":0.0, "fx":[0.02285,0.02287,0.02287,0.02285], "fy":[0.27113,0.27113,0.27116,0.27116]}, + {"t":0.69494, "x":5.61992, "y":7.59043, "heading":-1.5708, "vx":1.99256, "vy":-0.16761, "omega":0.0, "ax":0.00127, "ay":0.01509, "alpha":0.0, "fx":[0.02153,0.02155,0.02155,0.02153], "fy":[0.25671,0.25671,0.25673,0.25673]}, + {"t":0.74457, "x":5.71883, "y":7.58213, "heading":-1.5708, "vx":1.99262, "vy":-0.16686, "omega":0.0, "ax":0.0012, "ay":0.01434, "alpha":0.0, "fx":[0.02038,0.02038,0.02038,0.02038], "fy":[0.24396,0.24396,0.24397,0.24397]}, + {"t":0.79421, "x":5.81774, "y":7.57386, "heading":-1.5708, "vx":1.99268, "vy":-0.16615, "omega":0.0, "ax":0.00114, "ay":0.01367, "alpha":0.0, "fx":[0.01935,0.01935,0.01935,0.01935], "fy":[0.2326,0.2326,0.23261,0.23261]}, + {"t":0.84385, "x":5.91666, "y":7.56563, "heading":-1.5708, "vx":1.99274, "vy":-0.16547, "omega":0.0, "ax":0.00108, "ay":0.01308, "alpha":0.0, "fx":[0.01843,0.01843,0.01843,0.01843], "fy":[0.22241,0.22241,0.22241,0.22241]}, + {"t":0.89349, "x":6.01557, "y":7.55743, "heading":-1.5708, "vx":1.99279, "vy":-0.16482, "omega":0.0, "ax":0.00103, "ay":0.01253, "alpha":0.0, "fx":[0.0176,0.01759,0.01759,0.0176], "fy":[0.2132,0.2132,0.2132,0.2132]}, + {"t":0.94313, "x":6.11449, "y":7.54927, "heading":-1.5708, "vx":1.99284, "vy":-0.1642, "omega":0.0, "ax":0.00099, "ay":0.01204, "alpha":0.0, "fx":[0.01684,0.01684,0.01684,0.01684], "fy":[0.20484,0.20484,0.20484,0.20484]}, + {"t":0.99277, "x":6.21342, "y":7.54113, "heading":-1.5708, "vx":1.99289, "vy":-0.1636, "omega":0.0, "ax":0.00095, "ay":0.01159, "alpha":0.0, "fx":[0.01616,0.01615,0.01615,0.01616], "fy":[0.1972,0.1972,0.1972,0.1972]}, + {"t":1.0424, "x":6.31234, "y":7.53303, "heading":-1.5708, "vx":1.99294, "vy":-0.16302, "omega":0.0, "ax":0.00091, "ay":0.01118, "alpha":0.0, "fx":[0.01553,0.01553,0.01553,0.01553], "fy":[0.1902,0.1902,0.1902,0.1902]}, + {"t":1.09204, "x":6.41127, "y":7.52495, "heading":-1.5708, "vx":1.99298, "vy":-0.16247, "omega":0.0, "ax":0.00088, "ay":0.0108, "alpha":0.0, "fx":[0.01495,0.01495,0.01495,0.01495], "fy":[0.18376,0.18376,0.18375,0.18375]}, + {"t":1.14168, "x":6.5102, "y":7.5169, "heading":-1.5708, "vx":1.99303, "vy":-0.16193, "omega":0.0, "ax":0.00085, "ay":0.01045, "alpha":0.0, "fx":[0.01442,0.01442,0.01442,0.01442], "fy":[0.1778,0.1778,0.17779,0.17779]}, + {"t":1.19132, "x":6.60913, "y":7.50887, "heading":-1.5708, "vx":1.99307, "vy":-0.16141, "omega":0.0, "ax":0.00082, "ay":0.01013, "alpha":0.0, "fx":[0.01393,0.01393,0.01393,0.01393], "fy":[0.17227,0.17227,0.17227,0.17227]}, + {"t":1.24096, "x":6.70806, "y":7.50087, "heading":-1.5708, "vx":1.99311, "vy":-0.16091, "omega":0.0, "ax":0.00079, "ay":0.00983, "alpha":0.0, "fx":[0.01347,0.01347,0.01347,0.01347], "fy":[0.16713,0.16713,0.16713,0.16713]}, + {"t":1.2906, "x":6.807, "y":7.49289, "heading":-1.5708, "vx":1.99315, "vy":-0.16042, "omega":0.0, "ax":0.00077, "ay":0.00954, "alpha":0.0, "fx":[0.01304,0.01304,0.01304,0.01304], "fy":[0.16229,0.16229,0.16229,0.16229]}, + {"t":1.34023, "x":6.90594, "y":7.48494, "heading":-1.5708, "vx":1.99319, "vy":-0.15995, "omega":0.0, "ax":0.00074, "ay":0.00927, "alpha":0.0, "fx":[0.01263,0.01263,0.01263,0.01263], "fy":[0.15771,0.15771,0.15771,0.15771]}, + {"t":1.38987, "x":7.00488, "y":7.47702, "heading":-1.5708, "vx":1.99322, "vy":-0.15949, "omega":0.0, "ax":0.00072, "ay":0.00904, "alpha":0.0, "fx":[0.01229,0.01229,0.01229,0.01229], "fy":[0.15382,0.15382,0.15382,0.15382]}, + {"t":1.43951, "x":7.10382, "y":7.46911, "heading":-1.5708, "vx":1.99326, "vy":-0.15904, "omega":0.0, "ax":0.0007, "ay":0.00881, "alpha":0.0, "fx":[0.01194,0.01194,0.01194,0.01194], "fy":[0.14992,0.14992,0.14992,0.14992]}, + {"t":1.48915, "x":7.20276, "y":7.46123, "heading":-1.5708, "vx":1.99329, "vy":-0.1586, "omega":0.0, "ax":0.00059, "ay":0.00742, "alpha":0.0, "fx":[0.01002,0.01002,0.01002,0.01002], "fy":[0.12614,0.12614,0.12614,0.12614]}, + {"t":1.53879, "x":7.3017, "y":7.45336, "heading":-1.5708, "vx":1.99332, "vy":-0.15824, "omega":0.0, "ax":-0.00226, "ay":-0.02832, "alpha":0.0, "fx":[-0.03841,-0.03841,-0.03841,-0.03841], "fy":[-0.48167,-0.48167,-0.48167,-0.48167]}, + {"t":1.58843, "x":7.40065, "y":7.44547, "heading":-1.5708, "vx":1.99321, "vy":-0.15964, "omega":0.0, "ax":-0.13389, "ay":-1.37465, "alpha":0.0, "fx":[-2.27744,-2.27744,-2.27744,-2.27744], "fy":[-23.38237,-23.38237,-23.38237,-23.38237]}, + {"t":1.63806, "x":7.49942, "y":7.43586, "heading":-1.5708, "vx":1.98657, "vy":-0.22788, "omega":0.0, "ax":-1.99103, "ay":-8.68601, "alpha":0.0, "fx":[-33.86677,-33.86677,-33.86677,-33.86677], "fy":[-147.7466,-147.7466,-147.7466,-147.7466]}, + {"t":1.6877, "x":7.59558, "y":7.41384, "heading":-1.5708, "vx":1.88773, "vy":-0.65904, "omega":0.0, "ax":-7.82486, "ay":-5.68329, "alpha":0.0, "fx":[-133.09866,-133.09866,-133.09866,-133.09866], "fy":[-96.67106,-96.67106,-96.67106,-96.67106]}, + {"t":1.73734, "x":7.67964, "y":7.37413, "heading":-1.5708, "vx":1.49932, "vy":-0.94114, "omega":0.0, "ax":-9.68718, "ay":-0.98874, "alpha":0.0, "fx":[-164.77616,-164.77616,-164.77616,-164.77616], "fy":[-16.81816,-16.81816,-16.81816,-16.81816]}, + {"t":1.78698, "x":7.74213, "y":7.32619, "heading":-1.5708, "vx":1.01847, "vy":-0.99022, "omega":0.0, "ax":-9.74584, "ay":-0.22781, "alpha":0.0, "fx":[-165.77401,-165.77401,-165.77401,-165.77401], "fy":[-3.87492,-3.87492,-3.87492,-3.87492]}, + {"t":1.83662, "x":7.78068, "y":7.27676, "heading":-1.5708, "vx":0.5347, "vy":-1.00153, "omega":0.0, "ax":-9.75259, "ay":0.07234, "alpha":0.0, "fx":[-165.88882,-165.88882,-165.88882,-165.88882], "fy":[1.23044,1.23044,1.23044,1.23044]}, + {"t":1.88626, "x":7.79521, "y":7.22713, "heading":-1.5708, "vx":0.0506, "vy":-0.99794, "omega":0.0, "ax":-2.09267, "ay":-0.01224, "alpha":0.0, "fx":[-35.59575,-35.59575,-35.59575,-35.59575], "fy":[-0.20819,-0.20819,-0.20819,-0.20819]}, + {"t":1.93444, "x":7.79522, "y":7.17904, "heading":-1.5708, "vx":-0.05023, "vy":-0.99853, "omega":0.0, "ax":2.07007, "ay":-0.00075, "alpha":0.0, "fx":[35.21127,35.21127,35.21127,35.21127], "fy":[-0.01279,-0.01279,-0.01279,-0.01279]}, + {"t":1.98262, "x":7.7952, "y":7.13092, "heading":-1.5708, "vx":0.04951, "vy":-0.99857, "omega":0.0, "ax":-2.04001, "ay":-0.00074, "alpha":0.0, "fx":[-34.69992,-34.69992,-34.69992,-34.69992], "fy":[-0.01255,-0.01255,-0.01255,-0.01255]}, + {"t":2.0308, "x":7.79522, "y":7.08281, "heading":-1.5708, "vx":-0.04878, "vy":-0.9986, "omega":0.0, "ax":2.01006, "ay":-0.00072, "alpha":0.0, "fx":[34.19054,34.19054,34.19054,34.19054], "fy":[-0.01232,-0.01232,-0.01232,-0.01232]}, + {"t":2.07899, "x":7.7952, "y":7.03469, "heading":-1.5708, "vx":0.04806, "vy":-0.99864, "omega":0.0, "ax":-1.98023, "ay":-0.00071, "alpha":0.0, "fx":[-33.68323,-33.68323,-33.68323,-33.68323], "fy":[-0.01209,-0.01209,-0.01209,-0.01209]}, + {"t":2.12717, "x":7.79522, "y":6.98658, "heading":-1.5708, "vx":-0.04735, "vy":-0.99867, "omega":0.0, "ax":1.95054, "ay":-0.0007, "alpha":0.0, "fx":[33.17806,33.17806,33.17806,33.17806], "fy":[-0.01185,-0.01185,-0.01185,-0.01185]}, + {"t":2.17535, "x":7.7952, "y":6.93846, "heading":-1.5708, "vx":0.04663, "vy":-0.9987, "omega":0.0, "ax":-1.92097, "ay":-0.00068, "alpha":0.0, "fx":[-32.6751,-32.6751,-32.6751,-32.6751], "fy":[-0.01162,-0.01162,-0.01162,-0.01162]}, + {"t":2.22353, "x":7.79522, "y":6.89034, "heading":-1.5708, "vx":-0.04592, "vy":-0.99874, "omega":0.0, "ax":1.89153, "ay":-0.00067, "alpha":0.0, "fx":[32.17446,32.17446,32.17446,32.17446], "fy":[-0.01139,-0.01139,-0.01139,-0.01139]}, + {"t":2.27172, "x":7.7952, "y":6.84221, "heading":-1.5708, "vx":0.04522, "vy":-0.99877, "omega":0.0, "ax":-1.86224, "ay":-0.00066, "alpha":0.0, "fx":[-31.67621,-31.67621,-31.67621,-31.67621], "fy":[-0.01116,-0.01116,-0.01116,-0.01116]}, + {"t":2.3199, "x":7.79522, "y":6.79409, "heading":-1.5708, "vx":-0.04451, "vy":-0.9988, "omega":0.0, "ax":1.8331, "ay":-0.00064, "alpha":0.0, "fx":[31.18047,31.18047,31.18047,31.18047], "fy":[-0.01093,-0.01093,-0.01093,-0.01093]}, + {"t":2.36808, "x":7.7952, "y":6.74596, "heading":-1.5708, "vx":0.04381, "vy":-0.99883, "omega":0.0, "ax":-1.80411, "ay":-0.00063, "alpha":0.0, "fx":[-30.68732,-30.68732,-30.68732,-30.68732], "fy":[-0.0107,-0.0107,-0.0107,-0.0107]}, + {"t":2.41626, "x":7.79522, "y":6.69784, "heading":-1.5708, "vx":-0.04311, "vy":-0.99886, "omega":0.0, "ax":1.77527, "ay":-0.00062, "alpha":0.0, "fx":[30.19689,30.19689,30.19689,30.19689], "fy":[-0.01047,-0.01047,-0.01047,-0.01047]}, + {"t":2.46445, "x":7.7952, "y":6.64971, "heading":-1.5708, "vx":0.04242, "vy":-0.99889, "omega":0.0, "ax":-1.74661, "ay":-0.0006, "alpha":0.0, "fx":[-29.70927,-29.70927,-29.70927,-29.70927], "fy":[-0.01024,-0.01024,-0.01024,-0.01024]}, + {"t":2.51263, "x":7.79522, "y":6.60158, "heading":-1.5708, "vx":-0.04173, "vy":-0.99892, "omega":0.0, "ax":1.71811, "ay":-0.00059, "alpha":0.0, "fx":[29.2246,29.2246,29.2246,29.2246], "fy":[-0.01001,-0.01001,-0.01001,-0.01001]}, + {"t":2.56081, "x":7.7952, "y":6.55345, "heading":-1.5708, "vx":0.04105, "vy":-0.99895, "omega":0.0, "ax":-1.6898, "ay":-0.00057, "alpha":0.0, "fx":[-28.74299,-28.74299,-28.74299,-28.74299], "fy":[-0.00978,-0.00978,-0.00978,-0.00978]}, + {"t":2.60899, "x":7.79522, "y":6.50532, "heading":-1.5708, "vx":-0.04037, "vy":-0.99898, "omega":0.0, "ax":1.66167, "ay":-0.00056, "alpha":0.0, "fx":[28.26459,28.26459,28.26459,28.26459], "fy":[-0.00955,-0.00955,-0.00955,-0.00955]}, + {"t":2.65718, "x":7.7952, "y":6.45718, "heading":-1.5708, "vx":0.03969, "vy":-0.999, "omega":0.0, "ax":-1.63374, "ay":-0.00055, "alpha":0.0, "fx":[-27.78953,-27.78953,-27.78953,-27.78953], "fy":[-0.00932,-0.00932,-0.00932,-0.00932]}, + {"t":2.70536, "x":7.79522, "y":6.40905, "heading":-1.5708, "vx":-0.03902, "vy":-0.99903, "omega":0.0, "ax":1.60602, "ay":-0.00053, "alpha":0.0, "fx":[27.31796,27.31796,27.31796,27.31796], "fy":[-0.00909,-0.00909,-0.00909,-0.00909]}, + {"t":2.75354, "x":7.7952, "y":6.36091, "heading":-1.5708, "vx":0.03836, "vy":-0.99906, "omega":0.0, "ax":-1.57851, "ay":-0.00052, "alpha":0.0, "fx":[-26.85004,-26.85004,-26.85004,-26.85004], "fy":[-0.00887,-0.00887,-0.00887,-0.00887]}, + {"t":2.80172, "x":7.79521, "y":6.31277, "heading":-1.5708, "vx":-0.0377, "vy":-0.99908, "omega":0.0, "ax":1.55123, "ay":-0.00051, "alpha":0.0, "fx":[26.38594,26.38594,26.38594,26.38594], "fy":[-0.00864,-0.00864,-0.00864,-0.00864]}, + {"t":2.84991, "x":7.7952, "y":6.26463, "heading":-1.5708, "vx":0.03704, "vy":-0.99911, "omega":0.0, "ax":-1.52418, "ay":-0.00049, "alpha":0.0, "fx":[-25.92584,-25.92584,-25.92584,-25.92584], "fy":[-0.00841,-0.00841,-0.00841,-0.00841]}, + {"t":2.89809, "x":7.79521, "y":6.21649, "heading":-1.5708, "vx":-0.03639, "vy":-0.99913, "omega":0.0, "ax":1.49737, "ay":-0.00048, "alpha":0.0, "fx":[25.46992,25.46992,25.46992,25.46992], "fy":[-0.00819,-0.00819,-0.00819,-0.00819]}, + {"t":2.94627, "x":7.7952, "y":6.16835, "heading":-1.5708, "vx":0.03575, "vy":-0.99915, "omega":0.0, "ax":-1.47083, "ay":-0.00047, "alpha":0.0, "fx":[-25.01838,-25.01838,-25.01838,-25.01838], "fy":[-0.00796,-0.00796,-0.00796,-0.00796]}, + {"t":2.99445, "x":7.79521, "y":6.12021, "heading":-1.5708, "vx":-0.03512, "vy":-0.99918, "omega":0.0, "ax":1.44455, "ay":-0.00045, "alpha":0.0, "fx":[24.57145,24.57145,24.57145,24.57145], "fy":[-0.00774,-0.00774,-0.00774,-0.00774]}, + {"t":3.04264, "x":7.7952, "y":6.07207, "heading":-1.5708, "vx":0.03449, "vy":-0.9992, "omega":0.0, "ax":-1.41856, "ay":-0.00044, "alpha":0.0, "fx":[-24.12934,-24.12934,-24.12934,-24.12934], "fy":[-0.00752,-0.00752,-0.00752,-0.00752]}, + {"t":3.09082, "x":7.79521, "y":6.02392, "heading":-1.5708, "vx":-0.03386, "vy":-0.99922, "omega":0.0, "ax":1.39287, "ay":-0.00043, "alpha":0.0, "fx":[23.6923,23.6923,23.6923,23.6923], "fy":[-0.00729,-0.00729,-0.00729,-0.00729]}, + {"t":3.139, "x":7.7952, "y":5.97578, "heading":-1.5708, "vx":0.03325, "vy":-0.99924, "omega":0.0, "ax":-1.36749, "ay":-0.00042, "alpha":0.0, "fx":[-23.26058,-23.26058,-23.26058,-23.26058], "fy":[-0.00707,-0.00707,-0.00707,-0.00707]}, + {"t":3.18718, "x":7.79521, "y":5.92763, "heading":-1.5708, "vx":-0.03264, "vy":-0.99926, "omega":0.0, "ax":1.34244, "ay":-0.0004, "alpha":0.0, "fx":[22.83447,22.83447,22.83447,22.83447], "fy":[-0.00685,-0.00685,-0.00685,-0.00685]}, + {"t":3.23537, "x":7.7952, "y":5.87948, "heading":-1.5708, "vx":0.03204, "vy":-0.99928, "omega":0.0, "ax":-1.31773, "ay":-0.00039, "alpha":0.0, "fx":[-22.41425,-22.41425,-22.41425,-22.41425], "fy":[-0.00662,-0.00662,-0.00662,-0.00662]}, + {"t":3.28355, "x":7.79521, "y":5.83134, "heading":-1.5708, "vx":-0.03145, "vy":-0.9993, "omega":0.0, "ax":1.29339, "ay":-0.00038, "alpha":0.0, "fx":[22.00025,22.00025,22.00025,22.00025], "fy":[-0.0064,-0.0064,-0.0064,-0.0064]}, + {"t":3.33173, "x":7.7952, "y":5.78319, "heading":-1.5708, "vx":0.03087, "vy":-0.99932, "omega":0.0, "ax":-1.26944, "ay":-0.00036, "alpha":0.0, "fx":[-21.59279,-21.59279,-21.59279,-21.59279], "fy":[-0.00618,-0.00618,-0.00618,-0.00618]}, + {"t":3.37991, "x":7.79521, "y":5.73504, "heading":-1.5708, "vx":-0.0303, "vy":-0.99933, "omega":0.0, "ax":1.24589, "ay":-0.00035, "alpha":0.0, "fx":[21.19224,21.19224,21.19224,21.19224], "fy":[-0.00596,-0.00596,-0.00596,-0.00596]}, + {"t":3.42809, "x":7.7952, "y":5.68689, "heading":-1.5708, "vx":0.02973, "vy":-0.99935, "omega":0.0, "ax":-1.22277, "ay":-0.00034, "alpha":0.0, "fx":[-20.799,-20.799,-20.799,-20.799], "fy":[-0.00574,-0.00574,-0.00574,-0.00574]}, + {"t":3.47628, "x":7.79521, "y":5.63873, "heading":-1.5708, "vx":-0.02918, "vy":-0.99937, "omega":0.0, "ax":1.20011, "ay":-0.00032, "alpha":0.0, "fx":[20.41346,20.41346,20.41346,20.41346], "fy":[-0.00552,-0.00552,-0.00552,-0.00552]}, + {"t":3.52446, "x":7.7952, "y":5.59058, "heading":-1.5708, "vx":0.02864, "vy":-0.99938, "omega":0.0, "ax":-1.17792, "ay":-0.00031, "alpha":0.0, "fx":[-20.03609,-20.03609,-20.03609,-20.03609], "fy":[-0.0053,-0.0053,-0.0053,-0.0053]}, + {"t":3.57264, "x":7.79521, "y":5.54243, "heading":-1.5708, "vx":-0.02811, "vy":-0.9994, "omega":0.0, "ax":1.15624, "ay":-0.0003, "alpha":0.0, "fx":[19.66737,19.66737,19.66737,19.66737], "fy":[-0.00507,-0.00507,-0.00507,-0.00507]}, + {"t":3.62082, "x":7.7952, "y":5.49427, "heading":-1.5708, "vx":0.0276, "vy":-0.99941, "omega":0.0, "ax":-1.1351, "ay":-0.00029, "alpha":0.0, "fx":[-19.30781,-19.30781,-19.30781,-19.30781], "fy":[-0.00485,-0.00485,-0.00485,-0.00485]}, + {"t":3.66901, "x":7.79521, "y":5.44612, "heading":-1.5708, "vx":-0.02709, "vy":-0.99943, "omega":0.0, "ax":1.11454, "ay":-0.00027, "alpha":0.0, "fx":[18.95797,18.95797,18.95797,18.95797], "fy":[-0.00463,-0.00463,-0.00463,-0.00463]}, + {"t":3.71719, "x":7.7952, "y":5.39796, "heading":-1.5708, "vx":0.02661, "vy":-0.99944, "omega":0.0, "ax":-1.09458, "ay":-0.00026, "alpha":0.0, "fx":[-18.61845,-18.61845,-18.61845,-18.61845], "fy":[-0.00441,-0.00441,-0.00441,-0.00441]}, + {"t":3.76537, "x":7.79521, "y":5.34981, "heading":-1.5708, "vx":-0.02613, "vy":-0.99945, "omega":0.0, "ax":1.07526, "ay":-0.00025, "alpha":0.0, "fx":[18.28991,18.28991,18.28991,18.28991], "fy":[-0.00418,-0.00418,-0.00418,-0.00418]}, + {"t":3.81355, "x":7.7952, "y":5.30165, "heading":-1.5708, "vx":0.02568, "vy":-0.99946, "omega":0.0, "ax":-1.05663, "ay":-0.00023, "alpha":0.0, "fx":[-17.97304,-17.97304,-17.97304,-17.97304], "fy":[-0.00396,-0.00396,-0.00396,-0.00396]}, + {"t":3.86174, "x":7.79521, "y":5.2535, "heading":-1.5708, "vx":-0.02524, "vy":-0.99947, "omega":0.0, "ax":1.03874, "ay":-0.00022, "alpha":0.0, "fx":[17.66859,17.66859,17.66859,17.66859], "fy":[-0.00373,-0.00373,-0.00373,-0.00373]}, + {"t":3.90992, "x":7.7952, "y":5.20534, "heading":-1.5708, "vx":0.02481, "vy":-0.99948, "omega":0.0, "ax":-1.02161, "ay":-0.00021, "alpha":0.0, "fx":[-17.37737,-17.37737,-17.37737,-17.37737], "fy":[-0.0035,-0.0035,-0.0035,-0.0035]}, + {"t":3.9581, "x":7.79521, "y":5.15718, "heading":-1.5708, "vx":-0.02441, "vy":-0.99949, "omega":0.0, "ax":1.00532, "ay":-0.00019, "alpha":0.0, "fx":[17.10025,17.10025,17.10025,17.10025], "fy":[-0.00327,-0.00327,-0.00327,-0.00327]}, + {"t":4.00628, "x":7.7952, "y":5.10902, "heading":-1.5708, "vx":0.02403, "vy":-0.9995, "omega":0.0, "ax":-0.98991, "ay":-0.00018, "alpha":0.0, "fx":[-16.83814,-16.83814,-16.83814,-16.83814], "fy":[-0.00303,-0.00303,-0.00303,-0.00303]}, + {"t":4.05447, "x":7.79521, "y":5.06086, "heading":-1.5708, "vx":-0.02367, "vy":-0.99951, "omega":0.0, "ax":0.97544, "ay":-0.00016, "alpha":0.0, "fx":[16.59203,16.59203,16.59203,16.59203], "fy":[-0.0028,-0.0028,-0.0028,-0.0028]}, + {"t":4.10265, "x":7.7952, "y":5.0127, "heading":-1.5708, "vx":0.02333, "vy":-0.99952, "omega":0.0, "ax":-0.96198, "ay":-0.00015, "alpha":0.0, "fx":[-16.36294,-16.36294,-16.36294,-16.36294], "fy":[-0.00255,-0.00255,-0.00255,-0.00255]}, + {"t":4.15083, "x":7.79521, "y":4.96454, "heading":-1.5708, "vx":-0.02302, "vy":-0.99953, "omega":0.0, "ax":0.94957, "ay":-0.00014, "alpha":0.0, "fx":[16.15197,16.15197,16.15197,16.15197], "fy":[-0.00231,-0.00231,-0.00231,-0.00231]}, + {"t":4.19901, "x":7.7952, "y":4.91639, "heading":-1.5708, "vx":0.02273, "vy":-0.99953, "omega":0.0, "ax":-0.9383, "ay":-0.00012, "alpha":0.0, "fx":[-15.96021,-15.96021,-15.96021,-15.96021], "fy":[-0.00206,-0.00206,-0.00206,-0.00206]}, + {"t":4.2472, "x":7.79521, "y":4.86822, "heading":-1.5708, "vx":-0.02248, "vy":-0.99954, "omega":0.0, "ax":0.92822, "ay":-0.00011, "alpha":0.0, "fx":[15.78881,15.78881,15.78881,15.78881], "fy":[-0.0018,-0.0018,-0.0018,-0.0018]}, + {"t":4.29538, "x":7.7952, "y":4.82006, "heading":-1.5708, "vx":0.02225, "vy":-0.99955, "omega":0.0, "ax":-0.91941, "ay":-0.00009, "alpha":0.0, "fx":[-15.63889,-15.63889,-15.63889,-15.63889], "fy":[-0.00154,-0.00154,-0.00154,-0.00154]}, + {"t":4.34356, "x":7.79521, "y":4.7719, "heading":-1.5708, "vx":-0.02205, "vy":-0.99955, "omega":0.0, "ax":0.91192, "ay":-0.00007, "alpha":0.0, "fx":[15.51153,15.51153,15.51153,15.51153], "fy":[-0.00127,-0.00127,-0.00127,-0.00127]}, + {"t":4.39174, "x":7.79521, "y":4.72374, "heading":-1.5708, "vx":0.02189, "vy":-0.99955, "omega":0.0, "ax":-0.90582, "ay":-0.00006, "alpha":0.0, "fx":[-15.40774,-15.40774,-15.40774,-15.40774], "fy":[-0.001,-0.001,-0.001,-0.001]}, + {"t":4.43993, "x":7.79521, "y":4.67558, "heading":-1.5708, "vx":-0.02176, "vy":-0.99956, "omega":0.0, "ax":0.90116, "ay":-0.00004, "alpha":0.0, "fx":[15.3284,15.3284,15.3284,15.3284], "fy":[-0.00073,-0.00073,-0.00073,-0.00073]}, + {"t":4.48811, "x":7.79521, "y":4.62742, "heading":-1.5708, "vx":0.02166, "vy":-0.99956, "omega":0.0, "ax":-0.89797, "ay":-0.00003, "alpha":0.0, "fx":[-15.27424,-15.27424,-15.27424,-15.27424], "fy":[-0.00045,-0.00045,-0.00045,-0.00045]}, + {"t":4.53629, "x":7.79521, "y":4.57926, "heading":-1.5708, "vx":-0.0216, "vy":-0.99956, "omega":0.0, "ax":0.89633, "ay":0.00439, "alpha":0.0, "fx":[15.2464,15.2464,15.2464,15.2464], "fy":[0.07462,0.07462,0.07462,0.07462]}, + {"t":4.58447, "x":7.79521, "y":4.5311, "heading":-1.5708, "vx":0.02158, "vy":-0.99935, "omega":0.0, "ax":-1.30151, "ay":2.51868, "alpha":20.35533, "fx":[25.14587,-64.03427,-99.35853,49.69392], "fy":[122.27364,111.83408,-26.19061,-36.54879]}, + {"t":4.60903, "x":7.79534, "y":4.50733, "heading":-1.5708, "vx":-0.01037, "vy":-0.93751, "omega":0.4998, "ax":-2.03873, "ay":4.70262, "alpha":0.00002, "fx":[-34.67812,-34.67823,-34.67823,-34.67813], "fy":[79.99028,79.99027,79.99014,79.99015]}, + {"t":4.63358, "x":7.79448, "y":4.48572, "heading":-1.55852, "vx":-0.06043, "vy":-0.82204, "omega":0.4998, "ax":-1.95127, "ay":4.49795, "alpha":0.0, "fx":[-33.19054,-33.19054,-33.19054,-33.19054], "fy":[76.50886,76.50886,76.50886,76.50886]}, + {"t":4.65814, "x":7.7924, "y":4.4669, "heading":-1.54625, "vx":-0.10834, "vy":-0.7116, "omega":0.4998, "ax":-1.86168, "ay":4.2944, "alpha":0.0, "fx":[-31.66671,-31.66671,-31.66671,-31.66671], "fy":[73.0466,73.0466,73.0466,73.0466]}, + {"t":4.68269, "x":7.78918, "y":4.45072, "heading":-1.53398, "vx":-0.15405, "vy":-0.60615, "omega":0.4998, "ax":-1.77052, "ay":4.09198, "alpha":0.0, "fx":[-30.11611,-30.11611,-30.11611,-30.11611], "fy":[69.60336,69.60336,69.60336,69.60336]}, + {"t":4.70724, "x":7.78487, "y":4.43707, "heading":-1.52171, "vx":-0.19753, "vy":-0.50568, "omega":0.4998, "ax":-1.67836, "ay":3.89092, "alpha":0.0, "fx":[-28.54844,-28.54844,-28.54844,-28.54844], "fy":[66.18352,66.18352,66.18352,66.18352]}, + {"t":4.7318, "x":7.77951, "y":4.42582, "heading":-1.50944, "vx":-0.23874, "vy":-0.41015, "omega":0.4998, "ax":-1.58577, "ay":3.69171, "alpha":0.0, "fx":[-26.97346,-26.97346,-26.97346,-26.97346], "fy":[62.79485,62.79485,62.79485,62.79485]}, + {"t":4.75635, "x":7.77317, "y":4.41687, "heading":-1.49717, "vx":-0.27767, "vy":-0.3195, "omega":0.4998, "ax":-1.49333, "ay":3.49491, "alpha":0.0, "fx":[-25.40112,-25.40112,-25.40112,-25.40112], "fy":[59.44742,59.44742,59.44742,59.44742]}, + {"t":4.7809, "x":7.7659, "y":4.41008, "heading":-1.48489, "vx":-0.31434, "vy":-0.23369, "omega":0.4998, "ax":-1.40162, "ay":3.30123, "alpha":0.0, "fx":[-23.84123,-23.84123,-23.84123,-23.84123], "fy":[56.15296,56.15296,56.15296,56.15296]}, + {"t":4.80546, "x":7.75776, "y":4.40533, "heading":-1.47262, "vx":-0.34875, "vy":-0.15263, "omega":0.4998, "ax":-1.31122, "ay":3.1114, "alpha":0.0, "fx":[-22.30342,-22.30342,-22.30342,-22.30342], "fy":[52.92404,52.92404,52.92404,52.92404]}, + {"t":4.83001, "x":7.7488, "y":4.40252, "heading":-1.46035, "vx":-0.38095, "vy":-0.07623, "omega":0.4998, "ax":-1.22264, "ay":2.92618, "alpha":0.0, "fx":[-20.79681,-20.79681,-20.79681,-20.79682], "fy":[49.7735,49.7735,49.7735,49.7735]}, + {"t":4.85456, "x":7.73908, "y":4.40153, "heading":-1.44808, "vx":-0.41097, "vy":-0.00439, "omega":0.4998, "ax":-1.1364, "ay":2.74631, "alpha":0.0, "fx":[-19.32987,-19.32987,-19.32988,-19.32988], "fy":[46.71391,46.71391,46.71391,46.71391]}, + {"t":4.87912, "x":7.72865, "y":4.40225, "heading":-1.43581, "vx":-0.43887, "vy":0.06305, "omega":0.4998, "ax":-1.05294, "ay":2.57247, "alpha":0.0, "fx":[-17.91021,-17.91021,-17.91021,-17.91021], "fy":[43.75701,43.75702,43.75701,43.75701]}, + {"t":4.90367, "x":7.71755, "y":4.40458, "heading":-1.42353, "vx":-0.46473, "vy":0.12621, "omega":0.4998, "ax":-0.97265, "ay":2.40529, "alpha":0.0, "fx":[-16.54443,-16.54443,-16.54443,-16.54443], "fy":[40.91324,40.91324,40.91324,40.91324]}, + {"t":4.92823, "x":7.70585, "y":4.4084, "heading":-1.41126, "vx":-0.48861, "vy":0.18527, "omega":0.4998, "ax":-0.89584, "ay":2.24528, "alpha":0.0, "fx":[-15.23801,-15.23801,-15.23802,-15.23802], "fy":[38.19158,38.19158,38.19158,38.19158]}, + {"t":4.95278, "x":7.69358, "y":4.41363, "heading":-1.39899, "vx":-0.5106, "vy":0.2404, "omega":0.4998, "ax":-0.82279, "ay":2.09288, "alpha":0.0, "fx":[-13.99537,-13.99537,-13.99538,-13.99538], "fy":[35.59924,35.59924,35.59924,35.59924]}, + {"t":4.97733, "x":7.6808, "y":4.42016, "heading":-1.38672, "vx":-0.53081, "vy":0.29179, "omega":0.4998, "ax":-0.75366, "ay":1.9484, "alpha":0.0, "fx":[-12.81955,-12.81955,-12.81955,-12.81955], "fy":[33.14171,33.14171,33.14171,33.14171]}, + {"t":5.00189, "x":7.66754, "y":4.42791, "heading":-1.37445, "vx":-0.54931, "vy":0.33963, "omega":0.4998, "ax":-0.68858, "ay":1.81204, "alpha":0.0, "fx":[-11.71254,-11.71254,-11.71254,-11.71254], "fy":[30.82231,30.82231,30.82231,30.82231]}, + {"t":5.02644, "x":7.65384, "y":4.4368, "heading":-1.36217, "vx":-0.56622, "vy":0.38412, "omega":0.4998, "ax":-0.6276, "ay":1.68391, "alpha":0.0, "fx":[-10.67525,-10.67525,-10.67526,-10.67526], "fy":[28.64287,28.64287,28.64287,28.64287]}, + {"t":5.05099, "x":7.63975, "y":4.44674, "heading":-1.3499, "vx":-0.58163, "vy":0.42546, "omega":0.4998, "ax":-0.57071, "ay":1.56403, "alpha":0.0, "fx":[-9.70761,-9.70761,-9.70761,-9.70761], "fy":[26.60362,26.60362,26.60362,26.60362]}, + {"t":5.07555, "x":7.6253, "y":4.45765, "heading":-1.33763, "vx":-0.59564, "vy":0.46387, "omega":0.4998, "ax":-0.51786, "ay":1.45227, "alpha":0.0, "fx":[-8.80871,-8.80871,-8.80871,-8.80871], "fy":[24.70272,24.70272,24.70272,24.70272]}, + {"t":5.1001, "x":7.61052, "y":4.46948, "heading":-1.32536, "vx":-0.60836, "vy":0.49953, "omega":0.4998, "ax":-0.46896, "ay":1.3485, "alpha":0.0, "fx":[-7.97691,-7.97691,-7.97691,-7.97691], "fy":[22.93759,22.93759,22.93759,22.93759]}, + {"t":5.12465, "x":7.59544, "y":4.48215, "heading":-1.31309, "vx":-0.61987, "vy":0.53264, "omega":0.4998, "ax":-0.42387, "ay":1.25248, "alpha":0.0, "fx":[-7.20997,-7.20997,-7.20997,-7.20997], "fy":[21.30439,21.30439,21.30439,21.30439]}, + {"t":5.14921, "x":7.58009, "y":4.49561, "heading":-1.30082, "vx":-0.63028, "vy":0.56339, "omega":0.4998, "ax":-0.38243, "ay":1.16395, "alpha":0.0, "fx":[-6.50511,-6.50511,-6.50511,-6.50511], "fy":[19.79838,19.79838,19.79838,19.79838]}, + {"t":5.17376, "x":7.5645, "y":4.50979, "heading":-1.28854, "vx":-0.63967, "vy":0.59197, "omega":0.4998, "ax":-0.34447, "ay":1.08257, "alpha":0.0, "fx":[-5.85935,-5.85935,-5.85936,-5.85936], "fy":[18.41422,18.41422,18.41422,18.41422]}, + {"t":5.19831, "x":7.54869, "y":4.52465, "heading":-1.27627, "vx":-0.64813, "vy":0.61855, "omega":0.4998, "ax":-0.30979, "ay":1.00801, "alpha":0.0, "fx":[-5.26943,-5.26943,-5.26944,-5.26944], "fy":[17.14597,17.14597,17.14597,17.14597]}, + {"t":5.22287, "x":7.53268, "y":4.54015, "heading":-1.264, "vx":-0.65573, "vy":0.6433, "omega":0.4998, "ax":-0.27819, "ay":0.93991, "alpha":0.0, "fx":[-4.73185,-4.73185,-4.73185,-4.73185], "fy":[15.98754,15.98754,15.98754,15.98754]}, + {"t":5.24742, "x":7.5165, "y":4.55622, "heading":-1.25173, "vx":-0.66256, "vy":0.66638, "omega":0.4998, "ax":-0.24945, "ay":0.87788, "alpha":0.0, "fx":[-4.24307,-4.24308,-4.24308,-4.24308], "fy":[14.93244,14.93244,14.93244,14.93244]}, + {"t":5.27198, "x":7.50015, "y":4.57285, "heading":-1.23946, "vx":-0.66869, "vy":0.68793, "omega":0.4998, "ax":-0.22338, "ay":0.82154, "alpha":0.0, "fx":[-3.79957,-3.79957,-3.79957,-3.79957], "fy":[13.97419,13.97419,13.97419,13.97419]}, + {"t":5.29653, "x":7.48367, "y":4.58999, "heading":-1.22718, "vx":-0.67417, "vy":0.7081, "omega":0.4998, "ax":-0.19976, "ay":0.77052, "alpha":0.0, "fx":[-3.39782,-3.39782,-3.39782,-3.39782], "fy":[13.10641,13.10641,13.10641,13.10641]}, + {"t":5.32108, "x":7.46705, "y":4.60761, "heading":-1.21491, "vx":-0.67908, "vy":0.72702, "omega":0.4998, "ax":-0.17839, "ay":0.72444, "alpha":0.0, "fx":[-3.03442,-3.03442,-3.03442,-3.03442], "fy":[12.32255,12.32255,12.32255,12.32255]}, + {"t":5.34564, "x":7.45033, "y":4.62568, "heading":-1.20264, "vx":-0.68346, "vy":0.74481, "omega":0.4998, "ax":-0.1591, "ay":0.68295, "alpha":0.0, "fx":[-2.70619,-2.70619,-2.70619,-2.70619], "fy":[11.61671,11.61671,11.61671,11.61671]}, + {"t":5.37019, "x":7.4335, "y":4.64417, "heading":-1.19037, "vx":-0.68736, "vy":0.76158, "omega":0.4998, "ax":-0.14168, "ay":0.64569, "alpha":0.0, "fx":[-2.41,-2.41001,-2.41001,-2.41001], "fy":[10.98302,10.98302,10.98302,10.98302]}, + {"t":5.39474, "x":7.41658, "y":4.66307, "heading":-1.1781, "vx":-0.69084, "vy":0.77743, "omega":0.4998, "ax":-0.12598, "ay":0.61234, "alpha":0.0, "fx":[-2.14293,-2.14293,-2.14293,-2.14293], "fy":[10.41581,10.41581,10.41581,10.4158]}, + {"t":5.4193, "x":7.39958, "y":4.68234, "heading":-1.16583, "vx":-0.69394, "vy":0.79247, "omega":0.4998, "ax":-0.11183, "ay":0.58259, "alpha":0.0, "fx":[-1.90218,-1.90218,-1.90218,-1.90218], "fy":[9.90973,9.90973,9.90973,9.90973]}, + {"t":5.44385, "x":7.3825, "y":4.70197, "heading":-1.15355, "vx":-0.69668, "vy":0.80677, "omega":0.4998, "ax":-0.09907, "ay":0.55614, "alpha":0.0, "fx":[-1.68515,-1.68515,-1.68516,-1.68515], "fy":[9.45975,9.45976,9.45975,9.45975]}, + {"t":5.4684, "x":7.36537, "y":4.72195, "heading":-1.14128, "vx":-0.69912, "vy":0.82043, "omega":0.4998, "ax":-0.08757, "ay":0.53269, "alpha":0.0, "fx":[-1.48954,-1.48954,-1.48954,-1.48954], "fy":[9.06084,9.06084,9.06084,9.06084]}, + {"t":5.49296, "x":7.34818, "y":4.74225, "heading":-1.12901, "vx":-0.70127, "vy":0.83351, "omega":0.4998, "ax":-0.07719, "ay":0.51199, "alpha":0.0, "fx":[-1.31301,-1.31302,-1.31302,-1.31302], "fy":[8.70883,8.70883,8.70883,8.70883]}, + {"t":5.51751, "x":7.33093, "y":4.76287, "heading":-1.11674, "vx":-0.70316, "vy":0.84608, "omega":0.4998, "ax":-0.06783, "ay":0.49381, "alpha":0.0, "fx":[-1.15376,-1.15376,-1.15376,-1.15376], "fy":[8.39956,8.39956,8.39956,8.39956]}, + {"t":5.54207, "x":7.31365, "y":4.7838, "heading":-1.10447, "vx":-0.70483, "vy":0.8582, "omega":0.4998, "ax":-0.05936, "ay":0.47791, "alpha":0.0, "fx":[-1.00969,-1.00969,-1.00969,-1.00969], "fy":[8.12917,8.12918,8.12918,8.12917]}, + {"t":5.56662, "x":7.29632, "y":4.80501, "heading":-1.09219, "vx":-0.70628, "vy":0.86994, "omega":0.4998, "ax":-0.05168, "ay":0.46409, "alpha":0.0, "fx":[-0.87912,-0.87912,-0.87912,-0.87912], "fy":[7.89407,7.89407,7.89407,7.89407]}, + {"t":5.59117, "x":7.27897, "y":4.82651, "heading":-1.07992, "vx":-0.70755, "vy":0.88133, "omega":0.4998, "ax":-0.04471, "ay":0.45213, "alpha":0.0, "fx":[-0.76048,-0.76048,-0.76048,-0.76048], "fy":[7.69054,7.69054,7.69054,7.69054]}, + {"t":5.61573, "x":7.26158, "y":4.84829, "heading":-1.06765, "vx":-0.70865, "vy":0.89244, "omega":0.4998, "ax":-0.03835, "ay":0.44186, "alpha":0.0, "fx":[-0.65238,-0.65238,-0.65238,-0.65238], "fy":[7.51595,7.51595,7.51595,7.51595]}, + {"t":5.64028, "x":7.24417, "y":4.87034, "heading":-1.05538, "vx":-0.70959, "vy":0.90328, "omega":0.4998, "ax":-0.03254, "ay":0.43313, "alpha":0.0, "fx":[-0.55352,-0.55352,-0.55352,-0.55352], "fy":[7.36746,7.36746,7.36746,7.36746]}, + {"t":5.66483, "x":7.22674, "y":4.89265, "heading":-1.04311, "vx":-0.71039, "vy":0.91392, "omega":0.4998, "ax":-0.0272, "ay":0.42579, "alpha":0.0, "fx":[-0.46272,-0.46272,-0.46273,-0.46272], "fy":[7.24249,7.24249,7.24249,7.24249]}, + {"t":5.68939, "x":7.20928, "y":4.91521, "heading":-1.03084, "vx":-0.71106, "vy":0.92437, "omega":0.4998, "ax":-0.02228, "ay":0.41968, "alpha":0.0, "fx":[-0.37893,-0.37893,-0.37894,-0.37894], "fy":[7.13866,7.13866,7.13866,7.13866]}, + {"t":5.71394, "x":7.19182, "y":4.93804, "heading":-1.01856, "vx":-0.71161, "vy":0.93468, "omega":0.4998, "ax":-0.01771, "ay":0.41469, "alpha":0.0, "fx":[-0.3012,-0.3012,-0.3012,-0.3012], "fy":[7.05378,7.05378,7.05378,7.05378]}, + {"t":5.73849, "x":7.17434, "y":4.96111, "heading":-1.00629, "vx":-0.71204, "vy":0.94486, "omega":0.4998, "ax":-0.01344, "ay":0.4107, "alpha":0.0, "fx":[-0.22869,-0.22869,-0.22869,-0.22869], "fy":[6.98584,6.98584,6.98584,6.98584]}, + {"t":5.76305, "x":7.15685, "y":4.98444, "heading":-0.99402, "vx":-0.71237, "vy":0.95495, "omega":0.4998, "ax":-0.00944, "ay":0.40758, "alpha":0.0, "fx":[-0.16062,-0.16062,-0.16062,-0.16062], "fy":[6.9328,6.9328,6.9328,6.9328]}, + {"t":5.7876, "x":7.13936, "y":5.00801, "heading":-0.98175, "vx":-0.7126, "vy":0.96495, "omega":0.4998, "ax":-0.00566, "ay":0.40525, "alpha":0.0, "fx":[-0.09631,-0.09631,-0.09631,-0.09631], "fy":[6.8932,6.8932,6.8932,6.8932]}, + {"t":5.81216, "x":7.12186, "y":5.03182, "heading":-0.96948, "vx":-0.71274, "vy":0.9749, "omega":0.4998, "ax":-0.00207, "ay":0.40362, "alpha":0.0, "fx":[-0.03517,-0.03517,-0.03517,-0.03517], "fy":[6.8655,6.8655,6.8655,6.8655]}, + {"t":5.83671, "x":7.10436, "y":5.05588, "heading":-0.9572, "vx":-0.71279, "vy":0.98481, "omega":0.4998, "ax":0.00137, "ay":0.40261, "alpha":0.0, "fx":[0.02333,0.02333,0.02333,0.02333], "fy":[6.8483,6.8483,6.8483,6.8483]}, + {"t":5.86126, "x":7.08686, "y":5.08018, "heading":-0.94493, "vx":-0.71276, "vy":0.9947, "omega":0.4998, "ax":0.00468, "ay":0.40214, "alpha":0.0, "fx":[0.07968,0.07968,0.07968,0.07968], "fy":[6.84032,6.84032,6.84032,6.84032]}, + {"t":5.88582, "x":7.06936, "y":5.10473, "heading":-0.93266, "vx":-0.71264, "vy":1.00457, "omega":0.4998, "ax":0.0079, "ay":0.40215, "alpha":0.0, "fx":[0.13432,0.13431,0.13431,0.13431], "fy":[6.84038,6.84038,6.84038,6.84038]}, + {"t":5.91037, "x":7.05186, "y":5.12951, "heading":-0.92039, "vx":-0.71245, "vy":1.01445, "omega":0.4998, "ax":0.01101, "ay":0.40257, "alpha":0.0, "fx":[0.18736,0.18736,0.18736,0.18736], "fy":[6.84758,6.84758,6.84758,6.84758]}, + {"t":5.93492, "x":7.03437, "y":5.15454, "heading":-0.90812, "vx":-0.71218, "vy":1.02433, "omega":0.4998, "ax":0.01407, "ay":0.40333, "alpha":0.0, "fx":[0.23936,0.23936,0.23936,0.23936], "fy":[6.86061,6.86061,6.86061,6.86061]}, + {"t":5.95948, "x":7.01689, "y":5.17982, "heading":-0.89584, "vx":-0.71183, "vy":1.03424, "omega":0.4998, "ax":0.01708, "ay":0.4044, "alpha":0.0, "fx":[0.29059,0.29059,0.29059,0.29059], "fy":[6.87873,6.87873,6.87873,6.87873]}, + {"t":5.98403, "x":6.99942, "y":5.20533, "heading":-0.88357, "vx":-0.71141, "vy":1.04416, "omega":0.4998, "ax":0.02006, "ay":0.40573, "alpha":0.0, "fx":[0.34128,0.34128,0.34128,0.34128], "fy":[6.90138,6.90138,6.90138,6.90138]}, + {"t":6.00858, "x":6.98196, "y":5.23109, "heading":-0.8713, "vx":-0.71092, "vy":1.05413, "omega":0.4998, "ax":0.02302, "ay":0.40729, "alpha":0.0, "fx":[0.39164,0.39164,0.39164,0.39164], "fy":[6.92781,6.92781,6.92781,6.92781]}, + {"t":6.03314, "x":6.96451, "y":5.2571, "heading":-0.85903, "vx":-0.71036, "vy":1.06413, "omega":0.4998, "ax":0.02598, "ay":0.409, "alpha":0.0, "fx":[0.44185,0.44185,0.44185,0.44185], "fy":[6.95703,6.95703,6.95703,6.95703]}, + {"t":6.05769, "x":6.94707, "y":5.28335, "heading":-0.84676, "vx":-0.70972, "vy":1.07417, "omega":0.4998, "ax":0.02893, "ay":0.41085, "alpha":0.0, "fx":[0.49205,0.49205,0.49205,0.49205], "fy":[6.98849,6.98849,6.98849,6.98849]}, + {"t":6.08225, "x":6.92966, "y":5.30985, "heading":-0.83449, "vx":-0.70901, "vy":1.08426, "omega":0.4998, "ax":0.03188, "ay":0.41281, "alpha":0.0, "fx":[0.54234,0.54234,0.54234,0.54234], "fy":[7.02183,7.02183,7.02183,7.02183]}, + {"t":6.1068, "x":6.91226, "y":5.3366, "heading":-0.82221, "vx":-0.70823, "vy":1.09439, "omega":0.4998, "ax":0.03485, "ay":0.41485, "alpha":0.0, "fx":[0.59282,0.59282,0.59282,0.59282], "fy":[7.0565,7.0565,7.0565,7.0565]}, + {"t":6.13135, "x":6.89488, "y":5.36359, "heading":-0.80994, "vx":-0.70737, "vy":1.10458, "omega":0.4998, "ax":0.03783, "ay":0.41695, "alpha":0.0, "fx":[0.64355,0.64355,0.64355,0.64355], "fy":[7.09224,7.09224,7.09224,7.09224]}, + {"t":6.15591, "x":6.87752, "y":5.39084, "heading":-0.79767, "vx":-0.70644, "vy":1.11482, "omega":0.4998, "ax":0.04083, "ay":0.41906, "alpha":0.0, "fx":[0.69455,0.69455,0.69455,0.69455], "fy":[7.12814,7.12814,7.12814,7.12814]}, + {"t":6.18046, "x":6.86019, "y":5.41834, "heading":-0.7854, "vx":-0.70544, "vy":1.12511, "omega":0.4998, "ax":0.04385, "ay":0.42119, "alpha":0.0, "fx":[0.74583,0.74583,0.74583,0.74583], "fy":[7.16424,7.16424,7.16424,7.16424]}, + {"t":6.20501, "x":6.84288, "y":5.44609, "heading":-0.77313, "vx":-0.70436, "vy":1.13545, "omega":0.4998, "ax":0.04688, "ay":0.42328, "alpha":0.0, "fx":[0.7974,0.7974,0.7974,0.7974], "fy":[7.19995,7.19995,7.19995,7.19995]}, + {"t":6.22957, "x":6.8256, "y":5.4741, "heading":-0.76085, "vx":-0.70321, "vy":1.14584, "omega":0.4998, "ax":0.04993, "ay":0.42535, "alpha":0.0, "fx":[0.84923,0.84923,0.84923,0.84923], "fy":[7.2351,7.2351,7.2351,7.2351]}, + {"t":6.25412, "x":6.80835, "y":5.50236, "heading":-0.74858, "vx":-0.70199, "vy":1.15629, "omega":0.4998, "ax":0.05297, "ay":0.42735, "alpha":0.0, "fx":[0.90101,0.90101,0.90101,0.90101], "fy":[7.26913,7.26913,7.26913,7.26913]}, + {"t":6.27867, "x":6.79113, "y":5.53088, "heading":-0.73631, "vx":-0.70068, "vy":1.16678, "omega":0.4998, "ax":0.05602, "ay":0.42928, "alpha":0.0, "fx":[0.95289,0.95289,0.95289,0.95289], "fy":[7.30197,7.30197,7.30197,7.30197]}, + {"t":6.30323, "x":6.77394, "y":5.55966, "heading":-0.72404, "vx":-0.69931, "vy":1.17732, "omega":0.4998, "ax":0.05907, "ay":0.43114, "alpha":0.0, "fx":[1.00479,1.00479,1.00479,1.00479], "fy":[7.33355,7.33355,7.33355,7.33355]}, + {"t":6.32778, "x":6.75679, "y":5.5887, "heading":-0.71177, "vx":-0.69786, "vy":1.18791, "omega":0.4998, "ax":0.06211, "ay":0.43288, "alpha":0.0, "fx":[1.05655,1.05655,1.05655,1.05655], "fy":[7.36309,7.36309,7.36309,7.36309]}, + {"t":6.35234, "x":6.73967, "y":5.61799, "heading":-0.6995, "vx":-0.69633, "vy":1.19853, "omega":0.4998, "ax":0.06514, "ay":0.43451, "alpha":0.0, "fx":[1.10803,1.10803,1.10803,1.10803], "fy":[7.39081,7.39081,7.39081,7.39081]}, + {"t":6.37689, "x":6.72259, "y":5.64755, "heading":-0.68722, "vx":-0.69473, "vy":1.2092, "omega":0.4998, "ax":0.06814, "ay":0.43601, "alpha":0.0, "fx":[1.15904,1.15904,1.15904,1.15904], "fy":[7.41643,7.41643,7.41643,7.41643]}, + {"t":6.40144, "x":6.70556, "y":5.67737, "heading":-0.67495, "vx":-0.69306, "vy":1.21991, "omega":0.4998, "ax":0.0711, "ay":0.43737, "alpha":0.0, "fx":[1.20936,1.20936,1.20936,1.20936], "fy":[7.43946,7.43946,7.43946,7.43946]}, + {"t":6.426, "x":6.68856, "y":5.70746, "heading":-0.66268, "vx":-0.69132, "vy":1.23065, "omega":0.4998, "ax":0.074, "ay":0.43858, "alpha":0.0, "fx":[1.25872,1.25872,1.25872,1.25872], "fy":[7.46009,7.46009,7.46009,7.46009]}, + {"t":6.45055, "x":6.67161, "y":5.73781, "heading":-0.65041, "vx":-0.6895, "vy":1.24142, "omega":0.4998, "ax":0.07683, "ay":0.43962, "alpha":0.0, "fx":[1.30689,1.30689,1.30689,1.30689], "fy":[7.47781,7.47781,7.47781,7.47781]}, + {"t":6.4751, "x":6.6547, "y":5.76842, "heading":-0.63814, "vx":-0.68761, "vy":1.25221, "omega":0.4998, "ax":0.07956, "ay":0.44048, "alpha":0.0, "fx":[1.35337,1.35337,1.35337,1.35337], "fy":[7.49246,7.49246,7.49246,7.49246]}, + {"t":6.49966, "x":6.63784, "y":5.7993, "heading":-0.62586, "vx":-0.68566, "vy":1.26303, "omega":0.4998, "ax":0.08218, "ay":0.44117, "alpha":0.0, "fx":[1.39789,1.39789,1.39789,1.39789], "fy":[7.50414,7.50414,7.50414,7.50414]}, + {"t":6.52421, "x":6.62103, "y":5.83045, "heading":-0.61359, "vx":-0.68364, "vy":1.27386, "omega":0.4998, "ax":0.08466, "ay":0.44164, "alpha":0.0, "fx":[1.44005,1.44005,1.44005,1.44005], "fy":[7.51217,7.51217,7.51217,7.51217]}, + {"t":6.54876, "x":6.60427, "y":5.86186, "heading":-0.60132, "vx":-0.68156, "vy":1.2847, "omega":0.4998, "ax":0.08697, "ay":0.44191, "alpha":0.0, "fx":[1.4794,1.4794,1.4794,1.4794], "fy":[7.51668,7.51668,7.51668,7.51668]}, + {"t":6.57332, "x":6.58756, "y":5.89353, "heading":-0.58905, "vx":-0.67943, "vy":1.29555, "omega":0.4998, "ax":0.08909, "ay":0.44194, "alpha":0.0, "fx":[1.51541,1.51541,1.51541,1.51541], "fy":[7.51736,7.51736,7.51736,7.51736]}, + {"t":6.59787, "x":6.57091, "y":5.92548, "heading":-0.57678, "vx":-0.67724, "vy":1.3064, "omega":0.4998, "ax":0.09097, "ay":0.44174, "alpha":0.0, "fx":[1.54744,1.54744,1.54744,1.54744], "fy":[7.51387,7.51387,7.51387,7.51387]}, + {"t":6.62243, "x":6.55431, "y":5.95769, "heading":-0.5645, "vx":-0.675, "vy":1.31725, "omega":0.4998, "ax":0.09258, "ay":0.44127, "alpha":0.0, "fx":[1.57476,1.57476,1.57476,1.57476], "fy":[7.50591,7.50591,7.50591,7.50591]}, + {"t":6.64698, "x":6.53776, "y":5.99016, "heading":-0.55223, "vx":-0.67273, "vy":1.32808, "omega":0.4998, "ax":0.09387, "ay":0.44053, "alpha":0.0, "fx":[1.59675,1.59675,1.59675,1.59675], "fy":[7.49321,7.49321,7.49321,7.49321]}, + {"t":6.67153, "x":6.52127, "y":6.02291, "heading":-0.53996, "vx":-0.67043, "vy":1.3389, "omega":0.4998, "ax":0.09479, "ay":0.43949, "alpha":0.0, "fx":[1.61236,1.61236,1.61236,1.61236], "fy":[7.47561,7.47561,7.47561,7.47561]}, + {"t":6.69609, "x":6.50484, "y":6.05591, "heading":-0.52769, "vx":-0.6681, "vy":1.34969, "omega":0.4998, "ax":0.09528, "ay":0.43812, "alpha":0.0, "fx":[1.62064,1.62064,1.62064,1.62064], "fy":[7.45238,7.45238,7.45238,7.45238]}, + {"t":6.72064, "x":6.48846, "y":6.08919, "heading":-0.51542, "vx":-0.66576, "vy":1.36045, "omega":0.4998, "ax":0.09527, "ay":0.43641, "alpha":0.0, "fx":[1.62047,1.62047,1.62047,1.62047], "fy":[7.42313,7.42313,7.42313,7.42313]}, + {"t":6.74519, "x":6.47214, "y":6.12272, "heading":-0.50315, "vx":-0.66342, "vy":1.37117, "omega":0.4998, "ax":0.09468, "ay":0.43431, "alpha":0.0, "fx":[1.61056,1.61056,1.61056,1.61056], "fy":[7.38748,7.38748,7.38748,7.38748]}, + {"t":6.76975, "x":6.45588, "y":6.15652, "heading":-0.49087, "vx":-0.6611, "vy":1.38183, "omega":0.4998, "ax":0.09345, "ay":0.43179, "alpha":0.0, "fx":[1.58951,1.58951,1.58951,1.58951], "fy":[7.34465,7.34465,7.34465,7.34465]}, + {"t":6.7943, "x":6.43968, "y":6.19058, "heading":-0.4786, "vx":-0.6588, "vy":1.39243, "omega":0.4998, "ax":0.09147, "ay":0.42881, "alpha":0.0, "fx":[1.55581,1.55581,1.55581,1.55581], "fy":[7.29389,7.29389,7.29389,7.29389]}, + {"t":6.81885, "x":6.42353, "y":6.2249, "heading":-0.46633, "vx":-0.65656, "vy":1.40296, "omega":0.4998, "ax":0.08862, "ay":0.4253, "alpha":0.0, "fx":[1.50743,1.50743,1.50743,1.50743], "fy":[7.23424,7.23424,7.23424,7.23424]}, + {"t":6.84341, "x":6.40744, "y":6.25947, "heading":-0.45406, "vx":-0.65438, "vy":1.4134, "omega":0.4998, "ax":0.08479, "ay":0.42121, "alpha":0.0, "fx":[1.44231,1.44231,1.44231,1.44231], "fy":[7.16464,7.16464,7.16464,7.16464]}, + {"t":6.86796, "x":6.39139, "y":6.2943, "heading":-0.44179, "vx":-0.6523, "vy":1.42374, "omega":0.4998, "ax":0.07984, "ay":0.41645, "alpha":0.0, "fx":[1.35808,1.35807,1.35807,1.35808], "fy":[7.08363,7.08364,7.08363,7.08363]}, + {"t":6.89252, "x":6.3754, "y":6.32939, "heading":-0.42951, "vx":-0.65034, "vy":1.43397, "omega":0.4998, "ax":0.07361, "ay":0.41093, "alpha":0.0, "fx":[1.25209,1.25209,1.25209,1.25209], "fy":[6.98979,6.98979,6.98979,6.98979]}, + {"t":6.91707, "x":6.35946, "y":6.36472, "heading":-0.41724, "vx":-0.64853, "vy":1.44406, "omega":0.4998, "ax":0.06594, "ay":0.40453, "alpha":0.0, "fx":[1.1216,1.1216,1.1216,1.1216], "fy":[6.88101,6.88101,6.88101,6.88101]}, + {"t":6.94162, "x":6.34355, "y":6.4003, "heading":-0.40497, "vx":-0.64691, "vy":1.45399, "omega":0.4998, "ax":0.05661, "ay":0.39712, "alpha":0.0, "fx":[0.96287,0.96287,0.96287,0.96287], "fy":[6.75488,6.75488,6.75488,6.75488]}, + {"t":6.96618, "x":6.32769, "y":6.43612, "heading":-0.3927, "vx":-0.64552, "vy":1.46374, "omega":0.4998, "ax":0.04538, "ay":0.3885, "alpha":0.0, "fx":[0.77195,0.77195,0.77195,0.77195], "fy":[6.60835,6.60835,6.60835,6.60835]}, + {"t":6.99073, "x":6.31185, "y":6.47218, "heading":-0.38043, "vx":-0.64441, "vy":1.47328, "omega":0.4998, "ax":0.03203, "ay":0.37846, "alpha":0.0, "fx":[0.54479,0.54479,0.54478,0.54479], "fy":[6.43752,6.43752,6.43752,6.43752]}, + {"t":7.01528, "x":6.29604, "y":6.50847, "heading":-0.36816, "vx":-0.64362, "vy":1.48257, "omega":0.4998, "ax":0.01625, "ay":0.36671, "alpha":0.0, "fx":[0.27639,0.27639,0.27639,0.27639], "fy":[6.23769,6.23769,6.23769,6.23769]}, + {"t":7.03984, "x":6.28024, "y":6.54498, "heading":-0.35588, "vx":-0.64322, "vy":1.49158, "omega":0.4998, "ax":-0.00228, "ay":0.3529, "alpha":0.0, "fx":[-0.03882,-0.03882,-0.03883,-0.03882], "fy":[6.00281,6.00281,6.00281,6.00281]}, + {"t":7.06439, "x":6.26444, "y":6.58171, "heading":-0.34361, "vx":-0.64328, "vy":1.50024, "omega":0.4998, "ax":-0.02392, "ay":0.33659, "alpha":0.0, "fx":[-0.40685,-0.40685,-0.40685,-0.40685], "fy":[5.72524,5.72524,5.72524,5.72524]}, + {"t":7.08894, "x":6.24864, "y":6.61865, "heading":-0.33134, "vx":-0.64386, "vy":1.50851, "omega":0.4998, "ax":-0.04902, "ay":0.3172, "alpha":0.0, "fx":[-0.83388,-0.83388,-0.83388,-0.83388], "fy":[5.39549,5.39549,5.39549,5.39549]}, + {"t":7.1135, "x":6.23282, "y":6.65578, "heading":-0.31907, "vx":-0.64507, "vy":1.5163, "omega":0.4998, "ax":-0.07806, "ay":0.294, "alpha":0.0, "fx":[-1.32778,-1.32778,-1.32778,-1.32778], "fy":[5.00079,5.00079,5.00079,5.00079]}, + {"t":7.13805, "x":6.21696, "y":6.6931, "heading":-0.3068, "vx":-0.64698, "vy":1.52352, "omega":0.4998, "ax":-0.11144, "ay":0.26603, "alpha":0.0, "fx":[-1.89561,-1.89561,-1.89561,-1.89561], "fy":[4.52516,4.52516,4.52516,4.52516]}, + {"t":7.16261, "x":6.20104, "y":6.73059, "heading":-0.29452, "vx":-0.64972, "vy":1.53005, "omega":0.4998, "ax":-0.14969, "ay":0.23208, "alpha":0.0, "fx":[-2.54625,-2.54626,-2.54626,-2.54626], "fy":[3.94769,3.94769,3.94769,3.94769]}, + {"t":7.18716, "x":6.18504, "y":6.76823, "heading":-0.28225, "vx":-0.6534, "vy":1.53575, "omega":0.4998, "ax":-0.19328, "ay":0.19055, "alpha":0.0, "fx":[-3.28759,-3.28759,-3.28759,-3.28759], "fy":[3.24113,3.24113,3.24113,3.24113]}, + {"t":7.21171, "x":6.16894, "y":6.80599, "heading":-0.26998, "vx":-0.65814, "vy":1.54042, "omega":0.4998, "ax":-0.24263, "ay":0.13934, "alpha":0.0, "fx":[-4.12706,-4.12706,-4.12707,-4.12706], "fy":[2.37011,2.37011,2.37011,2.37011]}, + {"t":7.23627, "x":6.1527, "y":6.84386, "heading":-0.25771, "vx":-0.6641, "vy":1.54385, "omega":0.4998, "ax":-0.29818, "ay":0.07571, "alpha":0.0, "fx":[-5.07195,-5.07195,-5.07196,-5.07195], "fy":[1.28778,1.28778,1.28778,1.28778]}, + {"t":7.26082, "x":6.13631, "y":6.88179, "heading":-0.24544, "vx":-0.67142, "vy":1.54571, "omega":0.4998, "ax":-0.36012, "ay":-0.00388, "alpha":0.0, "fx":[-6.12551,-6.12551,-6.12551,-6.12551], "fy":[-0.06598,-0.06598,-0.06598,-0.06598]}, + {"t":7.28537, "x":6.11971, "y":6.91974, "heading":-0.23317, "vx":-0.68026, "vy":1.54561, "omega":0.4998, "ax":-0.42847, "ay":-0.10408, "alpha":0.0, "fx":[-7.28809,-7.2881,-7.2881,-7.28809], "fy":[-1.77031,-1.77031,-1.77031,-1.77032]}, + {"t":7.30993, "x":6.10288, "y":6.95766, "heading":-0.22089, "vx":-0.69078, "vy":1.54305, "omega":0.4998, "ax":-0.50283, "ay":-0.2308, "alpha":0.0, "fx":[-8.55299,-8.553,-8.553,-8.55299], "fy":[-3.92579,-3.92579,-3.92579,-3.92579]}, + {"t":7.33448, "x":6.08577, "y":6.99548, "heading":-0.20862, "vx":-0.70313, "vy":1.53739, "omega":0.4998, "ax":-0.58227, "ay":-0.39155, "alpha":0.0, "fx":[-9.90424,-9.90425,-9.90425,-9.90425], "fy":[-6.66009,-6.66009,-6.6601,-6.6601]}, + {"t":7.35903, "x":6.06833, "y":7.03311, "heading":-0.19635, "vx":-0.71743, "vy":1.52777, "omega":0.4998, "ax":-0.66496, "ay":-0.59536, "alpha":0.0, "fx":[-11.31079,-11.31079,-11.31079,-11.31079], "fy":[-10.12683,-10.12683,-10.12683,-10.12683]}, + {"t":7.38359, "x":6.05051, "y":7.07044, "heading":-0.18408, "vx":-0.73375, "vy":1.51316, "omega":0.4998, "ax":-0.74798, "ay":-0.8526, "alpha":0.0, "fx":[-12.72284,-12.72285,-12.72285,-12.72285], "fy":[-14.50256,-14.50256,-14.50257,-14.50257]}, + {"t":7.40814, "x":6.03227, "y":7.10734, "heading":-0.17181, "vx":-0.75212, "vy":1.49222, "omega":0.4998, "ax":-0.82705, "ay":-1.17401, "alpha":0.0, "fx":[-14.06782,-14.06782,-14.06782,-14.06782], "fy":[-19.96964,-19.96964,-19.96965,-19.96965]}, + {"t":7.4327, "x":6.01355, "y":7.14362, "heading":-0.15953, "vx":-0.77243, "vy":1.46339, "omega":0.4998, "ax":-0.89643, "ay":-1.56863, "alpha":0.0, "fx":[-15.24798,-15.24799,-15.24799,-15.24798], "fy":[-26.682,-26.682,-26.682,-26.68201]}, + {"t":7.45725, "x":5.99432, "y":7.17908, "heading":-0.14726, "vx":-0.79444, "vy":1.42488, "omega":0.4998, "ax":-0.94929, "ay":-2.04036, "alpha":0.0, "fx":[-16.14707,-16.14708,-16.14708,-16.14707], "fy":[-34.70589,-34.70589,-34.70589,-34.70589]}, + {"t":7.4818, "x":5.97453, "y":7.21345, "heading":-0.13499, "vx":-0.81775, "vy":1.37478, "omega":0.4998, "ax":-0.9786, "ay":-2.58411, "alpha":0.0, "fx":[-16.64569,-16.64569,-16.64569,-16.64569], "fy":[-43.9549,-43.9549,-43.9549,-43.9549]}, + {"t":7.50636, "x":5.95415, "y":7.24643, "heading":-0.12272, "vx":-0.84177, "vy":1.31133, "omega":0.4998, "ax":-0.97888, "ay":-3.18268, "alpha":0.0, "fx":[-16.65055,-16.65055,-16.65055,-16.65055], "fy":[-54.13653,-54.13653,-54.13653,-54.13653]}, + {"t":7.53091, "x":5.93319, "y":7.27767, "heading":-0.11045, "vx":-0.86581, "vy":1.23319, "omega":0.4998, "ax":-0.94824, "ay":-3.80816, "alpha":0.0, "fx":[-16.12931,-16.12931,-16.12931,-16.12931], "fy":[-64.77571,-64.77571,-64.77571,-64.77571]}, + {"t":7.55546, "x":5.91164, "y":7.3068, "heading":-0.09817, "vx":-0.88909, "vy":1.13968, "omega":0.4998, "ax":-0.88944, "ay":-4.42806, "alpha":0.0, "fx":[-15.12907,-15.12907,-15.12907,-15.12907], "fy":[-75.32008,-75.32008,-75.32008,-75.32008]}, + {"t":7.58002, "x":5.88955, "y":7.33345, "heading":-0.0859, "vx":-0.91093, "vy":1.03096, "omega":0.4998, "ax":-0.80919, "ay":-5.01404, "alpha":0.0, "fx":[-13.76416,-13.76416,-13.76416,-13.76416], "fy":[-85.28744,-85.28744,-85.28744,-85.28744]}, + {"t":7.60457, "x":5.86693, "y":7.35725, "heading":-0.07363, "vx":-0.9308, "vy":0.90784, "omega":0.4998, "ax":-0.71604, "ay":-5.54782, "alpha":0.0, "fx":[-12.1797,-12.1797,-12.1797,-12.1797], "fy":[-94.36686,-94.36686,-94.36686,-94.36686]}, + {"t":7.62912, "x":5.84386, "y":7.37787, "heading":-0.06136, "vx":-0.94838, "vy":0.77162, "omega":0.4998, "ax":-0.61806, "ay":-6.022, "alpha":0.0, "fx":[-10.51308,-10.51308,-10.51308,-10.51308], "fy":[-102.43252,-102.43252,-102.43252,-102.43252]}, + {"t":7.65368, "x":5.82039, "y":7.395, "heading":-0.04909, "vx":-0.96356, "vy":0.62376, "omega":0.4998, "ax":-0.52157, "ay":-6.43713, "alpha":0.0, "fx":[-8.87169,-8.87169,-8.87169,-8.87169], "fy":[-109.49375,-109.49375,-109.49375,-109.49375]}, + {"t":7.67823, "x":5.79658, "y":7.40837, "heading":-0.03682, "vx":-0.97636, "vy":0.46571, "omega":0.4998, "ax":-0.4308, "ay":-6.79807, "alpha":0.0, "fx":[-7.32773,-7.32773,-7.32773,-7.32773], "fy":[-115.63324,-115.63324,-115.63324,-115.63324]}, + {"t":7.70279, "x":5.77247, "y":7.41776, "heading":-0.02454, "vx":-0.98694, "vy":0.29879, "omega":0.4998, "ax":-0.34828, "ay":-7.11122, "alpha":-0.00003, "fx":[-5.924,-5.92401,-5.92429,-5.92428], "fy":[-120.9599,-120.95984,-120.95983,-120.95989]}, + {"t":7.72734, "x":5.74814, "y":7.42295, "heading":-0.01227, "vx":-0.99549, "vy":0.12418, "omega":0.4998, "ax":-0.14922, "ay":-5.22788, "alpha":-20.35532, "fx":[65.16188,110.36447,-114.32447,-71.35441], "fy":[-126.19003,-55.46564,-50.91362,-123.12938]}, + {"t":7.75189, "x":5.72365, "y":7.42443, "heading":0.0, "vx":-0.99916, "vy":-0.00418, "omega":0.0, "ax":-0.00406, "ay":-0.53282, "alpha":0.0, "fx":[-0.06907,-0.06907,-0.06907,-0.06907], "fy":[-9.06304,-9.06304,-9.06304,-9.06304]}, + {"t":7.79979, "x":5.67579, "y":7.42361, "heading":0.0, "vx":-0.99935, "vy":-0.0297, "omega":0.0, "ax":0.00004, "ay":-0.00138, "alpha":0.0, "fx":[0.0007,0.0007,0.0007,0.0007], "fy":[-0.02352,-0.02352,-0.02352,-0.02352]}, + {"t":7.84768, "x":5.62793, "y":7.42219, "heading":0.0, "vx":-0.99935, "vy":-0.02976, "omega":0.0, "ax":-0.00008, "ay":0.00258, "alpha":0.0, "fx":[-0.00131,-0.00131,-0.00131,-0.00131], "fy":[0.04396,0.04396,0.04396,0.04396]}, + {"t":7.89558, "x":5.58006, "y":7.42077, "heading":0.0, "vx":-0.99935, "vy":-0.02964, "omega":0.0, "ax":-0.00008, "ay":0.0026, "alpha":0.0, "fx":[-0.00131,-0.00131,-0.00131,-0.00131], "fy":[0.04424,0.04424,0.04424,0.04424]}, + {"t":7.94347, "x":5.5322, "y":7.41935, "heading":0.0, "vx":-0.99936, "vy":-0.02952, "omega":0.0, "ax":-0.00008, "ay":0.00259, "alpha":0.0, "fx":[-0.0013,-0.0013,-0.0013,-0.0013], "fy":[0.04398,0.04398,0.04398,0.04398]}, + {"t":7.99136, "x":5.48433, "y":7.41794, "heading":0.0, "vx":-0.99936, "vy":-0.02939, "omega":0.0, "ax":-0.00008, "ay":0.00257, "alpha":0.0, "fx":[-0.00128,-0.00128,-0.00128,-0.00128], "fy":[0.04373,0.04373,0.04373,0.04373]}, + {"t":8.03926, "x":5.43647, "y":7.41654, "heading":0.0, "vx":-0.99936, "vy":-0.02927, "omega":0.0, "ax":-0.00007, "ay":0.00256, "alpha":0.0, "fx":[-0.00127,-0.00127,-0.00127,-0.00127], "fy":[0.04348,0.04348,0.04348,0.04348]}, + {"t":8.08715, "x":5.38861, "y":7.41514, "heading":0.0, "vx":-0.99937, "vy":-0.02915, "omega":0.0, "ax":-0.00007, "ay":0.00254, "alpha":0.0, "fx":[-0.00126,-0.00126,-0.00126,-0.00126], "fy":[0.04323,0.04323,0.04323,0.04323]}, + {"t":8.13505, "x":5.34074, "y":7.41374, "heading":0.0, "vx":-0.99937, "vy":-0.02903, "omega":0.0, "ax":-0.00007, "ay":0.00253, "alpha":0.0, "fx":[-0.00125,-0.00125,-0.00125,-0.00125], "fy":[0.04299,0.04299,0.04299,0.04299]}, + {"t":8.18294, "x":5.29288, "y":7.41236, "heading":0.0, "vx":-0.99937, "vy":-0.0289, "omega":0.0, "ax":-0.00007, "ay":0.00251, "alpha":0.0, "fx":[-0.00123,-0.00123,-0.00123,-0.00123], "fy":[0.04276,0.04276,0.04276,0.04276]}, + {"t":8.23084, "x":5.24501, "y":7.41097, "heading":0.0, "vx":-0.99938, "vy":-0.02878, "omega":0.0, "ax":-0.00007, "ay":0.0025, "alpha":0.0, "fx":[-0.00122,-0.00122,-0.00122,-0.00122], "fy":[0.04252,0.04252,0.04252,0.04252]}, + {"t":8.27873, "x":5.19715, "y":7.4096, "heading":0.0, "vx":-0.99938, "vy":-0.02866, "omega":0.0, "ax":-0.00007, "ay":0.00249, "alpha":0.0, "fx":[-0.00121,-0.00121,-0.00121,-0.00121], "fy":[0.04229,0.04229,0.04229,0.04229]}, + {"t":8.32663, "x":5.14928, "y":7.40823, "heading":0.0, "vx":-0.99938, "vy":-0.02855, "omega":0.0, "ax":-0.00007, "ay":0.00247, "alpha":0.0, "fx":[-0.0012,-0.0012,-0.0012,-0.0012], "fy":[0.04207,0.04207,0.04207,0.04207]}, + {"t":8.37452, "x":5.10142, "y":7.40686, "heading":0.0, "vx":-0.99939, "vy":-0.02843, "omega":0.0, "ax":-0.00007, "ay":0.00246, "alpha":0.0, "fx":[-0.00119,-0.00119,-0.00119,-0.00119], "fy":[0.04185,0.04185,0.04185,0.04185]}, + {"t":8.42241, "x":5.05355, "y":7.40551, "heading":0.0, "vx":-0.99939, "vy":-0.02831, "omega":0.0, "ax":-0.00007, "ay":0.00245, "alpha":0.0, "fx":[-0.00118,-0.00118,-0.00118,-0.00118], "fy":[0.04163,0.04163,0.04163,0.04163]}, + {"t":8.47031, "x":5.00569, "y":7.40415, "heading":0.0, "vx":-0.99939, "vy":-0.02819, "omega":0.0, "ax":-0.00007, "ay":0.00243, "alpha":0.0, "fx":[-0.00117,-0.00117,-0.00117,-0.00117], "fy":[0.04141,0.04141,0.04141,0.04141]}, + {"t":8.5182, "x":4.95782, "y":7.40281, "heading":0.0, "vx":-0.9994, "vy":-0.02808, "omega":0.0, "ax":-0.00007, "ay":0.00242, "alpha":0.0, "fx":[-0.00116,-0.00116,-0.00116,-0.00116], "fy":[0.0412,0.0412,0.0412,0.0412]}, + {"t":8.5661, "x":4.90996, "y":7.40146, "heading":0.0, "vx":-0.9994, "vy":-0.02796, "omega":0.0, "ax":-0.00007, "ay":0.00241, "alpha":0.0, "fx":[-0.00114,-0.00114,-0.00114,-0.00114], "fy":[0.04099,0.04099,0.04099,0.04099]}, + {"t":8.61399, "x":4.86209, "y":7.40013, "heading":0.0, "vx":-0.9994, "vy":-0.02784, "omega":0.0, "ax":-0.00007, "ay":0.0024, "alpha":0.0, "fx":[-0.00113,-0.00113,-0.00113,-0.00113], "fy":[0.04079,0.04079,0.04079,0.04079]}, + {"t":8.66189, "x":4.81423, "y":7.3988, "heading":0.0, "vx":-0.99941, "vy":-0.02773, "omega":0.0, "ax":-0.00007, "ay":0.00239, "alpha":0.0, "fx":[-0.00112,-0.00112,-0.00112,-0.00112], "fy":[0.04059,0.04059,0.04059,0.04059]}, + {"t":8.70978, "x":4.76636, "y":7.39747, "heading":0.0, "vx":-0.99941, "vy":-0.02761, "omega":0.0, "ax":-0.00007, "ay":0.00237, "alpha":0.0, "fx":[-0.00111,-0.00111,-0.00111,-0.00111], "fy":[0.04039,0.04039,0.04039,0.04039]}, + {"t":8.75767, "x":4.71849, "y":7.39615, "heading":0.0, "vx":-0.99941, "vy":-0.0275, "omega":0.0, "ax":-0.00006, "ay":0.00236, "alpha":0.0, "fx":[-0.0011,-0.0011,-0.0011,-0.0011], "fy":[0.04019,0.04019,0.04019,0.04019]}, + {"t":8.80557, "x":4.67063, "y":7.39484, "heading":0.0, "vx":-0.99942, "vy":-0.02739, "omega":0.0, "ax":-0.00006, "ay":0.00235, "alpha":0.0, "fx":[-0.00109,-0.00109,-0.00109,-0.00109], "fy":[0.04,0.04,0.04,0.04]}, + {"t":8.85346, "x":4.62276, "y":7.39353, "heading":0.0, "vx":-0.99942, "vy":-0.02728, "omega":0.0, "ax":-0.00006, "ay":0.00234, "alpha":0.0, "fx":[-0.00108,-0.00108,-0.00108,-0.00108], "fy":[0.03981,0.03981,0.03981,0.03981]}, + {"t":8.90136, "x":4.57489, "y":7.39222, "heading":0.0, "vx":-0.99942, "vy":-0.02716, "omega":0.0, "ax":-0.00006, "ay":0.00233, "alpha":0.0, "fx":[-0.00107,-0.00107,-0.00107,-0.00107], "fy":[0.03962,0.03962,0.03962,0.03962]}, + {"t":8.94925, "x":4.52703, "y":7.39093, "heading":0.0, "vx":-0.99943, "vy":-0.02705, "omega":0.0, "ax":-0.00006, "ay":0.00232, "alpha":0.0, "fx":[-0.00107,-0.00107,-0.00107,-0.00107], "fy":[0.03944,0.03944,0.03944,0.03944]}, + {"t":8.99715, "x":4.47916, "y":7.38963, "heading":0.0, "vx":-0.99943, "vy":-0.02694, "omega":0.0, "ax":-0.00006, "ay":0.00231, "alpha":0.0, "fx":[-0.00106,-0.00106,-0.00106,-0.00106], "fy":[0.03926,0.03926,0.03926,0.03926]}, + {"t":9.04504, "x":4.43129, "y":7.38834, "heading":0.0, "vx":-0.99943, "vy":-0.02683, "omega":0.0, "ax":-0.00006, "ay":0.0023, "alpha":0.0, "fx":[-0.00105,-0.00105,-0.00105,-0.00105], "fy":[0.03908,0.03908,0.03908,0.03908]}, + {"t":9.09294, "x":4.38343, "y":7.38706, "heading":0.0, "vx":-0.99943, "vy":-0.02672, "omega":0.0, "ax":-0.00006, "ay":0.00229, "alpha":0.0, "fx":[-0.00104,-0.00104,-0.00104,-0.00104], "fy":[0.0389,0.0389,0.0389,0.0389]}, + {"t":9.14083, "x":4.33556, "y":7.38579, "heading":0.0, "vx":-0.99944, "vy":-0.02661, "omega":0.0, "ax":-0.00006, "ay":0.00228, "alpha":0.0, "fx":[-0.00103,-0.00103,-0.00103,-0.00103], "fy":[0.03872,0.03872,0.03872,0.03872]}, + {"t":9.18872, "x":4.28769, "y":7.38451, "heading":0.0, "vx":-0.99944, "vy":-0.0265, "omega":0.0, "ax":-0.00006, "ay":0.00227, "alpha":0.0, "fx":[-0.00102,-0.00102,-0.00102,-0.00102], "fy":[0.03855,0.03855,0.03855,0.03855]}, + {"t":9.23662, "x":4.23982, "y":7.38325, "heading":0.0, "vx":-0.99944, "vy":-0.02639, "omega":0.0, "ax":-0.00006, "ay":0.00226, "alpha":0.0, "fx":[-0.00101,-0.00101,-0.00101,-0.00101], "fy":[0.03838,0.03838,0.03838,0.03838]}, + {"t":9.28451, "x":4.19196, "y":7.38199, "heading":0.0, "vx":-0.99945, "vy":-0.02628, "omega":0.0, "ax":-0.00006, "ay":0.00225, "alpha":0.0, "fx":[-0.001,-0.001,-0.001,-0.001], "fy":[0.03822,0.03822,0.03822,0.03822]}, + {"t":9.33241, "x":4.14409, "y":7.38073, "heading":0.0, "vx":-0.99945, "vy":-0.02618, "omega":0.0, "ax":-0.00006, "ay":0.00224, "alpha":0.0, "fx":[-0.00099,-0.00099,-0.00099,-0.00099], "fy":[0.03805,0.03805,0.03805,0.03805]}, + {"t":9.3803, "x":4.09622, "y":7.37948, "heading":0.0, "vx":-0.99945, "vy":-0.02607, "omega":0.0, "ax":-0.00006, "ay":0.00223, "alpha":0.0, "fx":[-0.00099,-0.00099,-0.00099,-0.00099], "fy":[0.03789,0.03789,0.03789,0.03789]}, + {"t":9.4282, "x":4.04835, "y":7.37823, "heading":0.0, "vx":-0.99945, "vy":-0.02596, "omega":0.0, "ax":-0.00006, "ay":0.00222, "alpha":0.0, "fx":[-0.00098,-0.00098,-0.00098,-0.00098], "fy":[0.03773,0.03773,0.03773,0.03773]}, + {"t":9.47609, "x":4.00048, "y":7.37699, "heading":0.0, "vx":-0.99946, "vy":-0.02586, "omega":0.0, "ax":-0.00006, "ay":0.00221, "alpha":0.0, "fx":[-0.00097,-0.00097,-0.00097,-0.00097], "fy":[0.03757,0.03757,0.03757,0.03757]}, + {"t":9.52399, "x":3.95262, "y":7.37576, "heading":0.0, "vx":-0.99946, "vy":-0.02575, "omega":0.0, "ax":-0.00006, "ay":0.0022, "alpha":0.0, "fx":[-0.00096,-0.00096,-0.00096,-0.00096], "fy":[0.03741,0.03741,0.03741,0.03741]}, + {"t":9.57188, "x":3.90475, "y":7.37452, "heading":0.0, "vx":-0.99946, "vy":-0.02565, "omega":0.0, "ax":-0.00006, "ay":0.00219, "alpha":0.0, "fx":[-0.00095,-0.00095,-0.00095,-0.00095], "fy":[0.03726,0.03726,0.03726,0.03726]}, + {"t":9.61977, "x":3.85688, "y":7.3733, "heading":0.0, "vx":-0.99947, "vy":-0.02554, "omega":0.0, "ax":-0.00006, "ay":0.00218, "alpha":0.0, "fx":[-0.00095,-0.00095,-0.00095,-0.00095], "fy":[0.03711,0.03711,0.03711,0.03711]}, + {"t":9.66767, "x":3.80901, "y":7.37208, "heading":0.0, "vx":-0.99947, "vy":-0.02544, "omega":0.0, "ax":-0.00006, "ay":0.00217, "alpha":0.0, "fx":[-0.00094,-0.00094,-0.00094,-0.00094], "fy":[0.03696,0.03696,0.03696,0.03696]}, + {"t":9.71556, "x":3.76114, "y":7.37086, "heading":0.0, "vx":-0.99947, "vy":-0.02533, "omega":0.0, "ax":-0.00005, "ay":0.00216, "alpha":0.0, "fx":[-0.00093,-0.00093,-0.00093,-0.00093], "fy":[0.03681,0.03681,0.03681,0.03681]}, + {"t":9.76346, "x":3.71327, "y":7.36965, "heading":0.0, "vx":-0.99947, "vy":-0.02523, "omega":0.0, "ax":-0.00005, "ay":0.00216, "alpha":0.0, "fx":[-0.00092,-0.00092,-0.00092,-0.00092], "fy":[0.03666,0.03666,0.03666,0.03666]}, + {"t":9.81135, "x":3.6654, "y":7.36845, "heading":0.0, "vx":-0.99948, "vy":-0.02513, "omega":0.0, "ax":-0.00005, "ay":0.00215, "alpha":0.0, "fx":[-0.00092,-0.00092,-0.00092,-0.00092], "fy":[0.03652,0.03652,0.03652,0.03652]}, + {"t":9.85925, "x":3.61753, "y":7.36724, "heading":0.0, "vx":-0.99948, "vy":-0.02502, "omega":0.0, "ax":-0.00005, "ay":0.00214, "alpha":0.0, "fx":[-0.00091,-0.00091,-0.00091,-0.00091], "fy":[0.03638,0.03638,0.03638,0.03638]}, + {"t":9.90714, "x":3.56966, "y":7.36605, "heading":0.0, "vx":-0.99948, "vy":-0.02492, "omega":0.0, "ax":-0.00005, "ay":0.00213, "alpha":0.0, "fx":[-0.0009,-0.0009,-0.0009,-0.0009], "fy":[0.03624,0.03624,0.03624,0.03624]}, + {"t":9.95504, "x":3.52179, "y":7.36486, "heading":0.0, "vx":-0.99948, "vy":-0.02482, "omega":0.0, "ax":-0.00005, "ay":0.00212, "alpha":0.0, "fx":[-0.00089,-0.00089,-0.00089,-0.00089], "fy":[0.036,0.036,0.036,0.036]}, + {"t":10.00293, "x":3.47392, "y":7.36367, "heading":0.0, "vx":-0.99949, "vy":-0.02472, "omega":0.0, "ax":-0.00003, "ay":0.00134, "alpha":0.0, "fx":[-0.00056,-0.00056,-0.00056,-0.00056], "fy":[0.02275,0.02275,0.02275,0.02275]}, + {"t":10.05082, "x":3.42605, "y":7.36249, "heading":0.0, "vx":-0.99949, "vy":-0.02465, "omega":0.0, "ax":0.00275, "ay":-0.10126, "alpha":0.0, "fx":[0.04673,0.04673,0.04673,0.04673], "fy":[-1.72236,-1.72236,-1.72236,-1.72236]}, + {"t":10.09872, "x":3.37819, "y":7.36119, "heading":0.0, "vx":-0.99936, "vy":-0.0295, "omega":0.0, "ax":1.35107, "ay":-6.79397, "alpha":0.0, "fx":[22.98136,22.98136,22.98136,22.98136], "fy":[-115.56353,-115.56353,-115.56353,-115.56353]}, + {"t":10.14661, "x":3.33187, "y":7.35199, "heading":0.0, "vx":-0.93465, "vy":-0.3549, "omega":0.0, "ax":5.4641, "ay":-8.02996, "alpha":0.0, "fx":[92.9427,92.9427,92.9427,92.9427], "fy":[-136.58729,-136.58729,-136.58729,-136.58729]}, + {"t":10.19451, "x":3.29338, "y":7.32578, "heading":0.0, "vx":-0.67295, "vy":-0.73949, "omega":0.0, "ax":-1.68946, "ay":-9.59453, "alpha":0.04225, "fx":[-28.9994,-28.94334,-28.47564,-28.53083], "fy":[-163.1537,-163.16432,-163.24642,-163.23611]}, + {"t":10.23023, "x":3.26826, "y":7.29324, "heading":0.0, "vx":-0.73331, "vy":-1.08227, "omega":0.00151, "ax":-1.55749, "ay":-9.60644, "alpha":0.05731, "fx":[-26.8456,-26.77529,-26.13998,-26.2087], "fy":[-163.34486,-163.35776,-163.4603,-163.44794]}, + {"t":10.26596, "x":3.24106, "y":7.24844, "heading":0.00005, "vx":-0.78895, "vy":-1.42548, "omega":0.00356, "ax":-1.16738, "ay":-9.63105, "alpha":0.10199, "fx":[-20.47267,-20.37749,-19.24363,-19.33366], "fy":[-163.74471,-163.76131,-163.89758,-163.88224]}, + {"t":10.30169, "x":3.21213, "y":7.19136, "heading":0.00018, "vx":-0.83066, "vy":-1.76957, "omega":0.0072, "ax":7.94846, "ay":-4.30137, "alpha":0.66806, "fx":[135.13088,131.94123,135.43156,138.30013], "fy":[-72.28454,-78.56537,-73.953,-67.85718]}, + {"t":10.33742, "x":3.18753, "y":7.1254, "heading":0.00044, "vx":-0.54668, "vy":-1.92324, "omega":0.03107, "ax":3.73105, "ay":-0.92368, "alpha":5.81351, "fx":[44.00803,42.04529,82.79482,85.00801], "fy":[2.55237,-37.21006,-30.4723,2.28413]}, + {"t":10.37314, "x":3.17038, "y":7.0561, "heading":0.00155, "vx":-0.41338, "vy":-1.95624, "omega":0.23877, "ax":0.31077, "ay":-0.06475, "alpha":7.00137, "fx":[-21.72223,-21.51997,32.14892,32.23766], "fy":[17.52294,-20.08319,-19.00365,17.15824]}, + {"t":10.40887, "x":3.15581, "y":6.98616, "heading":0.01008, "vx":-0.40228, "vy":-1.95856, "omega":0.48891, "ax":0.01431, "ay":-0.00294, "alpha":5.99484, "fx":[-23.11175,-22.65922,23.57196,23.17247], "fy":[15.33719,-16.0819,-15.32552,15.87051]}, + {"t":10.4446, "x":3.14144, "y":6.91619, "heading":0.02755, "vx":-0.40177, "vy":-1.95866, "omega":0.70308, "ax":-0.00004, "ay":0.00001, "alpha":5.19946, "fx":[-20.49584,-19.62093,20.49016,19.62418], "fy":[12.92758,-14.21393,-12.91072,14.19765]}, + {"t":10.48032, "x":3.12709, "y":6.84621, "heading":0.05266, "vx":-0.40177, "vy":-1.95866, "omega":0.88884, "ax":-0.00151, "ay":0.00031, "alpha":4.25552, "fx":[-17.06436,-15.78192,17.01286,15.7307], "fy":[10.14516,-12.03056,-10.13392,12.04047]}, + {"t":10.51605, "x":3.11273, "y":6.77624, "heading":0.08442, "vx":-0.40183, "vy":-1.95865, "omega":1.04088, "ax":-0.00209, "ay":0.00043, "alpha":3.54022, "fx":[-14.47259,-12.81627,14.40217,12.74461], "fy":[7.9836,-10.41935,-7.97042,10.4354]}, + {"t":10.55178, "x":3.09838, "y":6.70626, "heading":0.12161, "vx":-0.4019, "vy":-1.95863, "omega":1.16736, "ax":-0.0021, "ay":0.00043, "alpha":2.84998, "fx":[-11.88884,-10.00495,11.81813,9.93309], "fy":[5.99142,-8.76367,-5.97787,8.77945]}, + {"t":10.5875, "x":3.08402, "y":6.63628, "heading":0.16331, "vx":-0.40198, "vy":-1.95862, "omega":1.26918, "ax":-0.00199, "ay":0.00041, "alpha":2.12121, "fx":[-9.03418,-7.17508,8.96688,7.10701], "fy":[4.08907,-6.82511,-4.07577,6.83965]}, + {"t":10.62323, "x":3.06965, "y":6.56631, "heading":0.20866, "vx":-0.40205, "vy":-1.9586, "omega":1.34497, "ax":-0.00193, "ay":0.0004, "alpha":1.65148, "fx":[-7.1758,-5.34792,7.11052,5.2822], "fy":[2.86689,-5.55695,-2.85371,5.57073]}, + {"t":10.65896, "x":3.05529, "y":6.49633, "heading":0.25671, "vx":-0.40212, "vy":-1.95859, "omega":1.40397, "ax":-0.0019, "ay":0.00039, "alpha":0.97022, "fx":[-4.30432,-2.99495,4.23981,2.93024], "fy":[1.48433,-3.40758,-1.47114,3.42097]}, + {"t":10.69469, "x":3.04092, "y":6.42636, "heading":0.30687, "vx":-0.40218, "vy":-1.95858, "omega":1.43863, "ax":-0.00188, "ay":0.00039, "alpha":0.5636, "fx":[-2.55328,-1.65228,2.48923,1.58817], "fy":[0.74054,-2.05987,-0.72738,2.07307]}, + {"t":10.73041, "x":3.02655, "y":6.35639, "heading":0.35827, "vx":-0.40225, "vy":-1.95856, "omega":1.45877, "ax":-0.00187, "ay":0.00039, "alpha":-0.0061, "fx":[-0.00399,-0.01631,-0.05974,-0.04742], "fy":[0.00098,0.03035,0.01213,-0.01723]}, + {"t":10.76614, "x":3.01218, "y":6.28641, "heading":0.41039, "vx":-0.40232, "vy":-1.95855, "omega":1.45855, "ax":-0.00186, "ay":0.00038, "alpha":-0.44486, "fx":[2.00723,1.07353,-2.07056,-1.13692], "fy":[-0.36611,1.75993,0.37913,-1.74688]}, + {"t":10.80187, "x":2.9978, "y":6.21644, "heading":0.4625, "vx":-0.40238, "vy":-1.95853, "omega":1.44266, "ax":-0.00186, "ay":0.00038, "alpha":-0.96356, "fx":[4.42063,2.16209,-4.4836,-2.22534], "fy":[-0.57051,3.9234,0.58344,-3.91035]}, + {"t":10.83759, "x":2.98343, "y":6.14647, "heading":0.51404, "vx":-0.40245, "vy":-1.95852, "omega":1.40823, "ax":-0.00185, "ay":0.00038, "alpha":-1.44886, "fx":[6.69858,2.96356,-6.76113,-3.02674], "fy":[-0.51998,6.05608,0.53281,-6.04304]}, + {"t":10.87332, "x":2.96905, "y":6.0765, "heading":0.56435, "vx":-0.40252, "vy":-1.95851, "omega":1.35647, "ax":-0.00184, "ay":0.00038, "alpha":-2.00384, "fx":[9.30135,3.68913,-9.36344,-3.7524], "fy":[-0.25739,8.56917,0.27015,-8.55614]}, + {"t":10.90905, "x":2.95466, "y":6.00652, "heading":0.61281, "vx":-0.40258, "vy":-1.95849, "omega":1.28488, "ax":-0.00184, "ay":0.00038, "alpha":-2.55618, "fx":[11.87653,4.18633,-11.93802,-4.24976], "fy":[0.24016,11.14409,-0.22744,-11.1311]}, + {"t":10.94477, "x":2.94028, "y":5.93655, "heading":0.65872, "vx":-0.40265, "vy":-1.95848, "omega":1.19355, "ax":-0.00183, "ay":0.00038, "alpha":-3.1995, "fx":[14.84499,4.60983,-14.90573,-4.67356], "fy":[0.97489,14.17221,-0.96216,-14.15933]}, + {"t":10.9805, "x":2.92589, "y":5.86658, "heading":0.70136, "vx":-0.40271, "vy":-1.95847, "omega":1.07925, "ax":-0.00182, "ay":0.00037, "alpha":-3.86742, "fx":[17.88551,4.85224,-17.94532,-4.91637], "fy":[1.93361,17.3505,-1.92079,-17.33781]}, + {"t":11.01623, "x":2.9115, "y":5.79661, "heading":0.73992, "vx":-0.40278, "vy":-1.95845, "omega":0.94107, "ax":-0.00181, "ay":0.00037, "alpha":-4.63604, "fx":[21.34298,5.02604,-21.40165,-5.09072], "fy":[3.13353,21.0054,-3.12052,-20.99303]}, + {"t":11.05196, "x":2.89711, "y":5.72664, "heading":0.77354, "vx":-0.40284, "vy":-1.95844, "omega":0.77544, "ax":-0.00179, "ay":0.00037, "alpha":-5.4723, "fx":[25.06234,5.11103,-25.11936,-5.17612], "fy":[4.53425,24.97781,-4.52101,-24.96592]}, + {"t":11.08768, "x":2.88272, "y":5.65667, "heading":0.80124, "vx":-0.40291, "vy":-1.95843, "omega":0.57993, "ax":-0.00164, "ay":0.00034, "alpha":-6.40481, "fx":[29.1849,5.18601,-29.23545,-5.24701], "fy":[6.10837,29.38667,-6.09617,-29.37591]}, + {"t":11.12341, "x":2.86832, "y":5.58671, "heading":0.82196, "vx":-0.40297, "vy":-1.95841, "omega":0.35111, "ax":0.00017, "ay":0.00032, "alpha":-7.44726, "fx":[33.81244,5.35994,-33.79406,-5.36676], "fy":[7.7885,34.30644,-7.8038,-34.26904]}, + {"t":11.15914, "x":2.85393, "y":5.51674, "heading":0.83451, "vx":-0.40296, "vy":-1.9584, "omega":0.08504, "ax":1.46379, "ay":7.14205, "alpha":-2.23813, "fx":[40.84857,25.24997,8.12129,25.37473], "fy":[120.3549,126.8043,122.53659,116.24117]}, + {"t":11.19486, "x":2.84047, "y":5.45133, "heading":0.83754, "vx":-0.35066, "vy":-1.70324, "omega":0.00508, "ax":1.95623, "ay":9.50251, "alpha":-0.05325, "fx":[33.80583,33.23846,32.74298,33.31262], "fy":[161.52691,161.64682,161.74232,161.62371]}, + {"t":11.23059, "x":2.82919, "y":5.39654, "heading":0.83773, "vx":-0.28077, "vy":-1.36374, "omega":0.00318, "ax":1.96232, "ay":9.53206, "alpha":-0.0305, "fx":[33.68397,33.35701,33.07255,33.40026], "fy":[162.07504,162.14329,162.19993,162.13212]}, + {"t":11.26632, "x":2.82041, "y":5.3539, "heading":0.83784, "vx":-0.21066, "vy":-1.02319, "omega":0.00209, "ax":1.96448, "ay":9.54205, "alpha":-0.02282, "fx":[33.64415,33.39899,33.18598,33.43157], "fy":[162.26049,162.31145,162.35432,162.30361]}, + {"t":11.30204, "x":2.81413, "y":5.32344, "heading":0.83791, "vx":-0.14048, "vy":-0.68228, "omega":0.00127, "ax":1.96564, "ay":9.54705, "alpha":-0.01896, "fx":[33.62533,33.42141,33.24437,33.44858], "fy":[162.35353,162.39583,162.43166,162.38953]}, + {"t":11.33777, "x":2.81037, "y":5.30515, "heading":0.83796, "vx":-0.07025, "vy":-0.34119, "omega":0.00059, "ax":1.9664, "ay":9.55005, "alpha":-0.01664, "fx":[33.61499,33.43594,33.28057,33.45985], "fy":[162.40933,162.44642,162.47797,162.441]}, + {"t":11.3735, "x":2.80911, "y":5.29906, "heading":0.83798, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0,0.0,0.0,0.0]}], + "splits":[0] + }, + "events":[] +} diff --git a/lib/examples/swerve/src/main/deploy/choreo/OUTPOST_1.traj b/lib/examples/swerve/src/main/deploy/choreo/OUTPOST_1.traj new file mode 100644 index 00000000..be0713c4 --- /dev/null +++ b/lib/examples/swerve/src/main/deploy/choreo/OUTPOST_1.traj @@ -0,0 +1,82 @@ +{ + "name":"OUTPOST_1", + "version":3, + "snapshot":{ + "waypoints":[ + {"x":3.690892219543457, "y":0.6445010900497437, "heading":3.1415922179358766, "intervals":24, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, + {"x":0.4072994291782379, "y":0.6569390296936035, "heading":3.141592653589793, "intervals":34, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], + "constraints":[ + {"from":"first", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, + {"from":"last", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, + {"from":"first", "to":"last", "data":{"type":"KeepInRectangle", "props":{"x":0.0, "y":0.0, "w":16.541, "h":8.0692}}, "enabled":true}], + "targetDt":0.05 + }, + "params":{ + "waypoints":[ + {"x":{"exp":"3.690892219543457 m", "val":3.690892219543457}, "y":{"exp":"0.6445010900497437 m", "val":0.6445010900497437}, "heading":{"exp":"3.1415922179358766 rad", "val":3.1415922179358766}, "intervals":24, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, + {"x":{"exp":"0.4072994291782379 m", "val":0.4072994291782379}, "y":{"exp":"0.6569390296936035 m", "val":0.6569390296936035}, "heading":{"exp":"3.141592653589793 rad", "val":3.141592653589793}, "intervals":34, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], + "constraints":[ + {"from":"first", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, + {"from":"last", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, + {"from":"first", "to":"last", "data":{"type":"KeepInRectangle", "props":{"x":{"exp":"0 m", "val":0.0}, "y":{"exp":"0 m", "val":0.0}, "w":{"exp":"16.541 m", "val":16.541}, "h":{"exp":"8.0692 m", "val":8.0692}}}, "enabled":true}], + "targetDt":{ + "exp":"0.05 s", + "val":0.05 + } + }, + "trajectory":{ + "config":{ + "frontLeft":{ + "x":0.225425, + "y":0.333375 + }, + "backLeft":{ + "x":-0.225425, + "y":0.333375 + }, + "mass":68.0388555, + "inertia":7.5, + "gearing":7.03, + "radius":0.0508, + "vmax":607.3745796940267, + "tmax":1.2, + "cof":1.5, + "bumper":{ + "front":0.34925, + "side":0.4445, + "back":0.34925 + }, + "differentialTrackWidth":0.5588 + }, + "sampleType":"Swerve", + "waypoints":[0.0,1.19873], + "samples":[ + {"t":0.0, "x":3.69089, "y":0.6445, "heading":3.14159, "vx":0.0, "vy":0.0, "omega":0.0, "ax":-9.75433, "ay":0.07475, "alpha":0.0, "fx":[-165.91833,-165.91833,-165.91833,-165.91833], "fy":[1.27151,1.27156,1.27156,1.27151]}, + {"t":0.04995, "x":3.67873, "y":0.64459, "heading":3.14159, "vx":-0.4872, "vy":0.00373, "omega":0.0, "ax":-9.75328, "ay":0.07192, "alpha":0.0, "fx":[-165.90055,-165.90055,-165.90055,-165.90055], "fy":[1.22324,1.22329,1.22329,1.22324]}, + {"t":0.09989, "x":3.64223, "y":0.64487, "heading":3.14159, "vx":-0.97435, "vy":0.00733, "omega":0.0, "ax":-9.75192, "ay":0.06906, "alpha":0.0, "fx":[-165.87735,-165.87735,-165.87735,-165.87735], "fy":[1.17462,1.17467,1.17467,1.17462]}, + {"t":0.14984, "x":3.5814, "y":0.64532, "heading":3.14159, "vx":-1.46143, "vy":0.01077, "omega":0.0, "ax":-9.75007, "ay":0.06617, "alpha":0.0, "fx":[-165.84591,-165.84591,-165.84591,-165.84591], "fy":[1.12545,1.12549,1.12549,1.12545]}, + {"t":0.19979, "x":3.49624, "y":0.64594, "heading":3.14159, "vx":-1.94842, "vy":0.01408, "omega":0.0, "ax":-9.74743, "ay":0.06322, "alpha":0.0, "fx":[-165.80096,-165.80096,-165.80096,-165.80096], "fy":[1.07532,1.07535,1.07535,1.07532]}, + {"t":0.24974, "x":3.38676, "y":0.64673, "heading":3.14159, "vx":-2.43527, "vy":0.01724, "omega":0.0, "ax":-9.74335, "ay":0.06017, "alpha":0.0, "fx":[-165.73164,-165.73164,-165.73164,-165.73164], "fy":[1.0234,1.02342,1.02342,1.0234]}, + {"t":0.29968, "x":3.25297, "y":0.64766, "heading":3.14159, "vx":-2.92192, "vy":0.02024, "omega":0.0, "ax":-9.73626, "ay":0.05689, "alpha":0.0, "fx":[-165.61106,-165.61106,-165.61106,-165.61106], "fy":[0.96763,0.96763,0.96763,0.96763]}, + {"t":0.34963, "x":3.09489, "y":0.64874, "heading":3.14159, "vx":-3.40822, "vy":0.02308, "omega":0.0, "ax":-9.72091, "ay":0.05298, "alpha":0.0, "fx":[-165.34993,-165.34993,-165.34993,-165.34993], "fy":[0.90115,0.90112,0.90112,0.90115]}, + {"t":0.39958, "x":2.91253, "y":0.64996, "heading":3.14159, "vx":-3.89375, "vy":0.02573, "omega":0.0, "ax":-9.66322, "ay":0.04586, "alpha":-0.00001, "fx":[-164.36854,-164.36854,-164.36854,-164.36854], "fy":[0.78019,0.78001,0.78002,0.78019]}, + {"t":0.44952, "x":2.706, "y":0.6513, "heading":3.14159, "vx":-4.3764, "vy":0.02802, "omega":0.0, "ax":-0.17953, "ay":-0.12526, "alpha":0.0, "fx":[-3.05381,-3.05381,-3.0538,-3.0538], "fy":[-2.13065,-2.13066,-2.13066,-2.13065]}, + {"t":0.49947, "x":2.48719, "y":0.65255, "heading":3.14159, "vx":-4.38537, "vy":0.02176, "omega":0.0, "ax":-0.00029, "ay":-0.05629, "alpha":0.0, "fx":[-0.00487,-0.00487,-0.00487,-0.00487], "fy":[-0.95753,-0.95753,-0.95753,-0.95753]}, + {"t":0.54942, "x":2.26815, "y":0.65356, "heading":3.14159, "vx":-4.38538, "vy":0.01895, "omega":0.0, "ax":-0.0002, "ay":-0.04883, "alpha":0.0, "fx":[-0.00347,-0.00347,-0.00347,-0.00347], "fy":[-0.83063,-0.83063,-0.83063,-0.83063]}, + {"t":0.59936, "x":2.04911, "y":0.65445, "heading":3.14159, "vx":-4.38539, "vy":0.01651, "omega":0.0, "ax":-0.00018, "ay":-0.04864, "alpha":0.0, "fx":[-0.00301,-0.00301,-0.00301,-0.00301], "fy":[-0.82741,-0.82741,-0.82741,-0.82741]}, + {"t":0.64931, "x":1.83007, "y":0.65521, "heading":3.14159, "vx":-4.3854, "vy":0.01408, "omega":0.0, "ax":-0.00015, "ay":-0.05553, "alpha":0.0, "fx":[-0.00255,-0.00255,-0.00255,-0.00255], "fy":[-0.94462,-0.94462,-0.94462,-0.94462]}, + {"t":0.69926, "x":1.61104, "y":0.65585, "heading":3.14159, "vx":-4.38541, "vy":0.01131, "omega":0.0, "ax":0.17841, "ay":-0.1233, "alpha":0.0, "fx":[3.03464,3.03464,3.03462,3.03462], "fy":[-2.09734,-2.09733,-2.09733,-2.09734]}, + {"t":0.74921, "x":1.39222, "y":0.65626, "heading":3.14159, "vx":-4.3765, "vy":0.00515, "omega":0.0, "ax":9.66334, "ay":-0.02688, "alpha":0.00001, "fx":[164.37059,164.37059,164.37059,164.37059], "fy":[-0.4573,-0.45713,-0.45713,-0.4573]}, + {"t":0.79915, "x":1.18568, "y":0.65648, "heading":3.14159, "vx":-3.89384, "vy":0.00381, "omega":0.0, "ax":9.72106, "ay":-0.02038, "alpha":0.0, "fx":[165.35252,165.35252,165.35252,165.35252], "fy":[-0.34672,-0.34669,-0.34669,-0.34672]}, + {"t":0.8491, "x":1.00332, "y":0.65665, "heading":3.14159, "vx":-3.4083, "vy":0.00279, "omega":0.0, "ax":9.73644, "ay":-0.0167, "alpha":0.0, "fx":[165.61401,165.61401,165.61401,165.61401], "fy":[-0.28404,-0.28405,-0.28405,-0.28404]}, + {"t":0.89905, "x":0.84523, "y":0.65677, "heading":3.14159, "vx":-2.922, "vy":0.00196, "omega":0.0, "ax":9.74355, "ay":-0.01358, "alpha":0.0, "fx":[165.73496,165.73496,165.73496,165.73496], "fy":[-0.2309,-0.23093,-0.23093,-0.2309]}, + {"t":0.94899, "x":0.71144, "y":0.65685, "heading":3.14159, "vx":-2.43534, "vy":0.00128, "omega":0.0, "ax":9.74764, "ay":-0.01066, "alpha":0.0, "fx":[165.80465,165.80465,165.80465,165.80465], "fy":[-0.1813,-0.18133,-0.18133,-0.1813]}, + {"t":0.99894, "x":0.60196, "y":0.6569, "heading":3.14159, "vx":-1.94847, "vy":0.00075, "omega":0.0, "ax":9.75031, "ay":-0.00784, "alpha":0.0, "fx":[165.84996,165.84996,165.84996,165.84996], "fy":[-0.13339,-0.13343,-0.13343,-0.13339]}, + {"t":1.04889, "x":0.5168, "y":0.65693, "heading":3.14159, "vx":-1.46147, "vy":0.00035, "omega":0.0, "ax":9.75218, "ay":-0.00508, "alpha":0.0, "fx":[165.88178,165.88178,165.88178,165.88178], "fy":[-0.08644,-0.08648,-0.08648,-0.08644]}, + {"t":1.09884, "x":0.45597, "y":0.65694, "heading":3.14159, "vx":-0.97438, "vy":0.0001, "omega":0.0, "ax":9.75357, "ay":-0.00236, "alpha":0.0, "fx":[165.90535,165.90535,165.90535,165.90535], "fy":[-0.04009,-0.04013,-0.04013,-0.04009]}, + {"t":1.14878, "x":0.41947, "y":0.65694, "heading":3.14159, "vx":-0.48722, "vy":-0.00002, "omega":0.0, "ax":9.75463, "ay":0.00034, "alpha":0.0, "fx":[165.92351,165.92351,165.92351,165.92351], "fy":[0.00586,0.00581,0.00581,0.00586]}, + {"t":1.19873, "x":0.4073, "y":0.65694, "heading":3.14159, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0,0.0,0.0,0.0]}], + "splits":[0] + }, + "events":[] +} diff --git a/lib/examples/swerve/src/main/deploy/choreo/OUTPOST_2.traj b/lib/examples/swerve/src/main/deploy/choreo/OUTPOST_2.traj new file mode 100644 index 00000000..d82e5f21 --- /dev/null +++ b/lib/examples/swerve/src/main/deploy/choreo/OUTPOST_2.traj @@ -0,0 +1,111 @@ +{ + "name":"OUTPOST_2", + "version":3, + "snapshot":{ + "waypoints":[ + {"x":0.4072994291782379, "y":0.6569390296936035, "heading":3.141592653589793, "intervals":24, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, + {"x":1.6635223627090454, "y":1.6768428087234497, "heading":0.0, "intervals":27, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, + {"x":1.93715500831604, "y":3.816153049468994, "heading":1.5707963267948966, "intervals":25, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], + "constraints":[ + {"from":"first", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, + {"from":"last", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, + {"from":"first", "to":"last", "data":{"type":"KeepInRectangle", "props":{"x":0.0, "y":0.0, "w":16.541, "h":8.0692}}, "enabled":true}], + "targetDt":0.05 + }, + "params":{ + "waypoints":[ + {"x":{"exp":"0.4072994291782379 m", "val":0.4072994291782379}, "y":{"exp":"0.6569390296936035 m", "val":0.6569390296936035}, "heading":{"exp":"3.141592653589793 rad", "val":3.141592653589793}, "intervals":24, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, + {"x":{"exp":"1.6635223627090454 m", "val":1.6635223627090454}, "y":{"exp":"1.6768428087234497 m", "val":1.6768428087234497}, "heading":{"exp":"0 deg", "val":0.0}, "intervals":27, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, + {"x":{"exp":"1.93715500831604 m", "val":1.93715500831604}, "y":{"exp":"3.816153049468994 m", "val":3.816153049468994}, "heading":{"exp":"1.5707963267948966 rad", "val":1.5707963267948966}, "intervals":25, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], + "constraints":[ + {"from":"first", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, + {"from":"last", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, + {"from":"first", "to":"last", "data":{"type":"KeepInRectangle", "props":{"x":{"exp":"0 m", "val":0.0}, "y":{"exp":"0 m", "val":0.0}, "w":{"exp":"16.541 m", "val":16.541}, "h":{"exp":"8.0692 m", "val":8.0692}}}, "enabled":true}], + "targetDt":{ + "exp":"0.05 s", + "val":0.05 + } + }, + "trajectory":{ + "config":{ + "frontLeft":{ + "x":0.225425, + "y":0.333375 + }, + "backLeft":{ + "x":-0.225425, + "y":0.333375 + }, + "mass":68.0388555, + "inertia":7.5, + "gearing":7.03, + "radius":0.0508, + "vmax":607.3745796940267, + "tmax":1.2, + "cof":1.5, + "bumper":{ + "front":0.34925, + "side":0.4445, + "back":0.34925 + }, + "differentialTrackWidth":0.5588 + }, + "sampleType":"Swerve", + "waypoints":[0.0,0.62176,1.36669], + "samples":[ + {"t":0.0, "x":0.4073, "y":0.65694, "heading":3.14159, "vx":0.0, "vy":0.0, "omega":0.0, "ax":7.77717, "ay":4.27371, "alpha":-9.49132, "fx":[60.69069,159.57563,165.55075,143.33265], "fy":[154.2335,42.83598,10.18965,83.51943]}, + {"t":0.02591, "x":0.40991, "y":0.65837, "heading":3.14159, "vx":0.20148, "vy":0.11072, "omega":-0.24589, "ax":7.7829, "ay":4.33302, "alpha":-9.30566, "fx":[62.25925,158.6326,165.45658,143.19121], "fy":[153.5832,46.09709,11.39773,83.73551]}, + {"t":0.05181, "x":0.41774, "y":0.6627, "heading":3.13522, "vx":0.40311, "vy":0.22297, "omega":-0.48697, "ax":7.78577, "ay":4.40975, "alpha":-9.0904, "fx":[64.11681,157.0168,165.36648,143.23464], "fy":[152.79141,51.21555,12.39646,83.6309]}, + {"t":0.07772, "x":0.4308, "y":0.66995, "heading":3.12261, "vx":0.60482, "vy":0.33722, "omega":-0.72247, "ax":7.784, "ay":4.50084, "alpha":-8.86245, "fx":[66.25622,154.63762,165.27768,143.44267], "fy":[151.84787,57.88755,13.25842,83.2379]}, + {"t":0.10363, "x":0.44908, "y":0.6802, "heading":3.10389, "vx":0.80648, "vy":0.45382, "omega":-0.95207, "ax":7.7759, "ay":4.60277, "alpha":-8.64099, "fx":[68.68086,151.41134,165.18306,143.788], "fy":[150.73533,65.75389,14.08072,82.59718]}, + {"t":0.12953, "x":0.47258, "y":0.6935, "heading":3.07922, "vx":1.00793, "vy":0.57306, "omega":-1.17593, "ax":7.76066, "ay":4.71148, "alpha":-8.44347, "fx":[71.40842,147.31314,165.06964,144.23518], "fy":[149.42643,74.37862,14.99682,81.76155]}, + {"t":0.15544, "x":0.5013, "y":0.70993, "heading":3.04876, "vx":1.20898, "vy":0.69512, "omega":-1.39468, "ax":7.73909, "ay":4.82271, "alpha":-8.27952, "fx":[74.47638,142.42952,164.91506,144.73789], "fy":[147.8785,83.25301,16.1974,80.80299]}, + {"t":0.18135, "x":0.53522, "y":0.72955, "heading":3.01263, "vx":1.40948, "vy":0.82006, "omega":-1.60917, "ax":7.71412, "ay":4.93316, "alpha":-8.14257, "fx":[77.95028,136.99801,164.67879,145.2328], "fy":[146.02475,91.82775,17.96812,79.82621]}, + {"t":0.20725, "x":0.57432, "y":0.75246, "heading":2.97094, "vx":1.60932, "vy":0.94787, "omega":-1.82012, "ax":7.69066, "ay":5.04236, "alpha":-7.99924, "fx":[81.93759,131.41981,164.28049,145.6261], "fy":[143.75837,99.56213,20.76018,78.99546]}, + {"t":0.23316, "x":0.61859, "y":0.7787, "heading":2.92379, "vx":1.80857, "vy":1.0785, "omega":-2.02736, "ax":7.67443, "ay":5.15567, "alpha":-7.7745, "fx":[86.6128,126.241,163.5431,145.76245], "fy":[140.90154,105.96525,25.32803,78.59109]}, + {"t":0.25907, "x":0.66802, "y":0.80837, "heading":2.87126, "vx":2.00739, "vy":1.21207, "omega":-2.22877, "ax":7.66855, "ay":5.28941, "alpha":-7.32432, "fx":[92.26812,122.11431,162.03054,145.34656], "fy":[137.13784,110.59971,33.00935,79.13833]}, + {"t":0.28498, "x":0.7226, "y":0.84155, "heading":2.81352, "vx":2.20605, "vy":1.3491, "omega":-2.41852, "ax":7.66388, "ay":5.4807, "alpha":-6.37164, "fx":[99.42833,119.76473,158.53737,143.71106], "fy":[131.84361,113.02239,46.29696,81.73754]}, + {"t":0.31088, "x":0.78232, "y":0.87834, "heading":2.75087, "vx":2.4046, "vy":1.49108, "omega":-2.58359, "ax":7.60468, "ay":5.80652, "alpha":-4.33868, "fx":[109.14167,119.97839,149.35139,138.94221], "fy":[123.58506,112.63713,69.7856,89.06148]}, + {"t":0.33679, "x":0.84717, "y":0.91892, "heading":2.68393, "vx":2.60162, "vy":1.64151, "omega":-2.69599, "ax":7.25268, "ay":6.3889, "alpha":0.08665, "fx":[123.77637,123.60167,122.95623,123.12999], "fy":[108.20511,108.4146,109.14004,108.93398]}, + {"t":0.3627, "x":0.91701, "y":0.96359, "heading":2.61409, "vx":2.78951, "vy":1.80703, "omega":-2.69375, "ax":5.63806, "ay":7.01338, "alpha":10.52124, "fx":[147.9122,131.29068,59.79707,44.60698], "fy":[69.23918,98.58209,152.7597,156.60157]}, + {"t":0.3886, "x":0.99117, "y":1.01276, "heading":2.5443, "vx":2.93557, "vy":1.98873, "omega":-2.42118, "ax":4.11035, "ay":6.95462, "alpha":16.56456, "fx":[152.76319,130.7319,26.94712,-30.77847], "fy":[53.4785,98.64773,161.63738,159.42072]}, + {"t":0.41451, "x":1.0686, "y":1.06661, "heading":2.48158, "vx":3.04206, "vy":2.1689, "omega":-1.99204, "ax":3.0681, "ay":7.29683, "alpha":16.76076, "fx":[141.24708,121.62593,7.83578,-61.95879], "fy":[74.0591,108.78012,163.46856,150.1601]}, + {"t":0.44042, "x":1.14844, "y":1.12525, "heading":2.42997, "vx":3.12155, "vy":2.35794, "omega":-1.55782, "ax":1.23006, "ay":7.72831, "alpha":15.30137, "fx":[99.6387,106.36278,-22.4584,-99.85152], "fy":[112.57909,122.60625,162.0271,128.61297]}, + {"t":0.46632, "x":1.22972, "y":1.18893, "heading":2.38961, "vx":3.15341, "vy":2.55815, "omega":-1.16141, "ax":-2.29365, "ay":8.29783, "alpha":9.738, "fx":[-67.54971,64.59442,-43.54905,-109.5533], "fy":[138.05052,148.04881,157.62962,120.84622]}, + {"t":0.49223, "x":1.31064, "y":1.25799, "heading":2.35952, "vx":3.09399, "vy":2.77312, "omega":-0.90913, "ax":-4.27522, "ay":7.87841, "alpha":7.60081, "fx":[-115.57049,5.34869,-63.69191,-116.96742], "fy":[109.70683,161.37124,150.76111,114.19865]}, + {"t":0.51814, "x":1.38936, "y":1.33248, "heading":2.33597, "vx":2.98323, "vy":2.97723, "omega":-0.71222, "ax":-5.88777, "ay":7.0464, "alpha":6.193, "fx":[-139.59493,-48.75622,-84.18922,-128.05675], "fy":[81.70741,154.53197,140.76161,102.42833]}, + {"t":0.54404, "x":1.46468, "y":1.41197, "heading":2.31752, "vx":2.8307, "vy":3.15978, "omega":-0.55177, "ax":-6.70056, "ay":6.63914, "alpha":4.15368, "fx":[-140.98492,-86.32496,-97.73019,-130.85845], "fy":[82.01704,138.2949,132.05453,99.35312]}, + {"t":0.56995, "x":1.53576, "y":1.49606, "heading":2.30322, "vx":2.65711, "vy":3.33178, "omega":-0.44417, "ax":-7.2751, "ay":6.18766, "alpha":2.99487, "fx":[-142.85827,-108.59245,-109.41701,-134.12203], "fy":[80.2549,122.54395,122.86015,95.34256]}, + {"t":0.59586, "x":1.60216, "y":1.58445, "heading":2.29172, "vx":2.46863, "vy":3.49208, "omega":-0.36658, "ax":-7.71643, "ay":5.72882, "alpha":2.33817, "fx":[-145.44838,-122.57096,-119.18988,-137.80816], "fy":[76.47321,109.28884,113.70177,90.31867]}, + {"t":0.62176, "x":1.66352, "y":1.67684, "heading":2.28222, "vx":2.26872, "vy":3.6405, "omega":-0.306, "ax":-7.99224, "ay":5.34776, "alpha":2.34764, "fx":[-149.33006,-129.18991,-124.00736,-141.25586], "fy":[68.85107,101.52966,108.51767,84.9574]}, + {"t":0.64935, "x":1.72307, "y":1.77932, "heading":2.27378, "vx":2.04822, "vy":3.78804, "omega":-0.24123, "ax":-8.21166, "ay":4.95431, "alpha":2.50031, "fx":[-152.73824,-134.22213,-127.53763,-144.21369], "fy":[59.92593,94.02474,103.86978,79.26533]}, + {"t":0.67694, "x":1.77646, "y":1.88572, "heading":2.26712, "vx":1.82166, "vy":3.92473, "omega":-0.17225, "ax":-8.47224, "ay":4.45653, "alpha":2.34695, "fx":[-154.90895,-140.78016,-133.45917,-147.29332], "fy":[52.59904,82.83765,95.35474,72.4259]}, + {"t":0.70453, "x":1.82349, "y":1.9957, "heading":2.26237, "vx":1.58791, "vy":4.04769, "omega":-0.1075, "ax":-8.77187, "ay":3.79511, "alpha":1.7918, "fx":[-156.01909,-147.94488,-142.10126,-150.76255], "fy":[46.97871,67.67217,80.4535,63.11084]}, + {"t":0.73212, "x":1.86397, "y":2.10882, "heading":2.2594, "vx":1.34589, "vy":4.15239, "omega":-0.05806, "ax":-9.04267, "ay":2.97324, "alpha":0.92574, "fx":[-156.47882,-153.56139,-150.99668,-154.2159], "fy":[41.7564,50.93668,59.09401,50.5089]}, + {"t":0.75971, "x":1.89766, "y":2.22451, "heading":2.2578, "vx":1.09641, "vy":4.23443, "omega":-0.03252, "ax":-9.16617, "ay":2.19665, "alpha":0.31402, "fx":[-156.55778,-155.85699,-155.24971,-155.99093], "fy":[34.47372,37.18522,40.23507,37.56347]}, + {"t":0.7873, "x":1.92442, "y":2.34218, "heading":2.2569, "vx":0.84351, "vy":4.29503, "omega":-0.02385, "ax":-9.12235, "ay":1.55405, "alpha":0.11036, "fx":[-155.31958,-155.13701,-155.01515,-155.20253], "fy":[25.45586,26.30322,27.41073,26.56584]}, + {"t":0.81489, "x":1.94422, "y":2.46127, "heading":2.25625, "vx":0.59183, "vy":4.33791, "omega":-0.02081, "ax":-8.88343, "ay":0.93286, "alpha":-0.05759, "fx":[-151.06209,-151.13811,-151.14645,-151.07153], "fy":[16.35079,15.96315,15.38437,15.77215]}, + {"t":0.84248, "x":1.95717, "y":2.58131, "heading":2.25567, "vx":0.34673, "vy":4.36364, "omega":-0.0224, "ax":-8.05529, "ay":-1.50505, "alpha":-4.86941, "fx":[-141.84265,-145.49045,-131.19864,-129.54129], "fy":[9.37723,-12.62806,-56.89348,-42.25761]}, + {"t":0.87007, "x":1.96367, "y":2.70113, "heading":2.25505, "vx":0.12449, "vy":4.32212, "omega":-0.15675, "ax":-3.09027, "ay":-6.58686, "alpha":-16.57932, "fx":[-124.68132,-128.67674,-17.85233,60.95195], "fy":[-55.92677,-89.42926,-158.28971,-144.51694]}, + {"t":0.89766, "x":1.96593, "y":2.81787, "heading":2.25073, "vx":0.03923, "vy":4.14039, "omega":-0.61417, "ax":-2.24404, "ay":-6.8673, "alpha":-17.05268, "fx":[-112.98053,-126.80477,0.59621,86.50708], "fy":[-69.31018,-98.63212,-162.65069,-136.65058]}, + {"t":0.92525, "x":1.96615, "y":2.92949, "heading":2.23378, "vx":-0.02269, "vy":3.95092, "omega":-1.08465, "ax":-1.94074, "ay":-6.92253, "alpha":-17.07797, "fx":[-101.17933,-128.1769,4.5931,92.7175], "fy":[-73.37593,-99.5066,-163.73689,-134.38149]}, + {"t":0.95284, "x":1.96479, "y":3.03586, "heading":2.20386, "vx":-0.07623, "vy":3.75993, "omega":-1.55583, "ax":-1.4001, "ay":-6.93119, "alpha":-16.93117, "fx":[-62.89761,-130.74453,4.28005,94.10089], "fy":[-75.41779,-97.52727,-164.31766,-134.32749]}, + {"t":0.98043, "x":1.96215, "y":3.13695, "heading":2.16093, "vx":-0.11486, "vy":3.5687, "omega":-2.02296, "ax":-0.03003, "ay":-9.16966, "alpha":-7.12956, "fx":[9.19421,-75.65058,2.74228,61.67093], "fy":[-161.67353,-145.22742,-164.60588,-152.38657]}, + {"t":1.00802, "x":1.95897, "y":3.23193, "heading":2.10512, "vx":-0.11569, "vy":3.3157, "omega":-2.21967, "ax":0.3067, "ay":-9.66915, "alpha":-0.86889, "fx":[6.2934,-3.81708,4.44671,13.94479], "fy":[-164.46639,-164.59411,-164.69429,-164.12331]}, + {"t":1.03561, "x":1.9559, "y":3.31973, "heading":2.04388, "vx":-0.10723, "vy":3.04893, "omega":-2.24364, "ax":0.30834, "ay":-9.6568, "alpha":2.01223, "fx":[2.4602,24.96071,10.15981,-16.60184], "fy":[-165.14749,-163.2164,-164.54891,-164.12495]}, + {"t":1.0632, "x":1.95306, "y":3.40017, "heading":1.98198, "vx":-0.09872, "vy":2.7825, "omega":-2.18812, "ax":0.27341, "ay":-9.57609, "alpha":3.60313, "fx":[-1.66261,37.20008,19.0941,-36.02931], "fy":[-165.40956,-161.12975,-163.85257,-161.15446]}, + {"t":1.09079, "x":1.95044, "y":3.47329, "heading":1.92161, "vx":-0.09118, "vy":2.5183, "omega":-2.08871, "ax":0.26339, "ay":-9.48721, "alpha":4.68297, "fx":[-5.81563,42.68919,30.03949,-48.99242], "fy":[-165.45644,-159.92117,-162.29994,-157.82148]}, + {"t":1.11838, "x":1.94802, "y":3.53916, "heading":1.86398, "vx":-0.08391, "vy":2.25655, "omega":-1.95951, "ax":0.27654, "ay":-9.397, "alpha":5.54878, "fx":[-9.86087,44.97026,41.73205,-58.02604], "fy":[-165.35446,-159.39897,-159.7893,-154.81854]}, + {"t":1.14597, "x":1.94581, "y":3.59784, "heading":1.80992, "vx":-0.07628, "vy":1.99728, "omega":-1.80642, "ax":0.30049, "ay":-9.30672, "alpha":6.30936, "fx":[-13.71151,45.57312,53.0917,-64.50843], "fy":[-165.14197,-159.30078,-156.47611,-152.29961]}, + {"t":1.17356, "x":1.94382, "y":3.64941, "heading":1.76008, "vx":-0.06799, "vy":1.74051, "omega":-1.63234, "ax":0.32482, "ay":-9.21884, "alpha":6.99365, "fx":[-17.30502,45.26708,63.38087,-69.24229], "fy":[-164.84984,-159.44175,-152.68834,-150.25952]}, + {"t":1.20115, "x":1.94207, "y":3.69392, "heading":1.71504, "vx":-0.05903, "vy":1.48616, "omega":-1.43939, "ax":0.3438, "ay":-9.13647, "alpha":7.60217, "fx":[-20.59228,44.48911,72.23601,-72.74082], "fy":[-164.50656,-159.70149,-148.78706,-148.64005]}, + {"t":1.22874, "x":1.94057, "y":3.73145, "heading":1.67533, "vx":-0.04954, "vy":1.23409, "omega":-1.22965, "ax":0.35571, "ay":-9.06194, "alpha":8.13179, "fx":[-23.53336,43.51245,79.59079,-75.36778], "fy":[-164.13938,-160.00239,-145.06118,-147.36107]}, + {"t":1.25633, "x":1.93934, "y":3.76205, "heading":1.6414, "vx":-0.03973, "vy":0.98407, "omega":-1.00529, "ax":0.36124, "ay":-8.99617, "alpha":8.58475, "fx":[-26.09556,42.52071,85.55948,-77.40646], "fy":[-163.77417,-160.29447,-141.68914,-146.33146]}, + {"t":1.28392, "x":1.93838, "y":3.78577, "heading":1.61367, "vx":-0.02976, "vy":0.73586, "omega":-0.76844, "ax":0.36207, "ay":-8.9388, "alpha":8.96948, "fx":[-28.25229,41.64372,90.33551,-79.09244], "fy":[-163.43475,-160.54552,-138.7504,-145.4552]}, + {"t":1.31151, "x":1.9377, "y":3.80267, "heading":1.59247, "vx":-0.01977, "vy":0.48924, "omega":-0.52097, "ax":0.36, "ay":-8.88861, "alpha":9.29824, "fx":[-29.98185,40.97657,94.12526,-80.62628], "fy":[-163.14231,-160.7345,-136.25697,-144.63713]}, + {"t":1.3391, "x":1.93729, "y":3.81279, "heading":1.57809, "vx":-0.00984, "vy":0.24401, "omega":-0.26443, "ax":0.3566, "ay":-8.84401, "alpha":9.58431, "fx":[-31.26606,40.59054,97.11279,-82.17482], "fy":[-162.91488,-160.84708,-134.18526,-143.7889]}, + {"t":1.36669, "x":1.93716, "y":3.81615, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0,0.0,0.0,0.0]}], + "splits":[0] + }, + "events":[] +} diff --git a/lib/examples/swerve/src/main/deploy/choreo/OUTPOST_DEPOT.traj b/lib/examples/swerve/src/main/deploy/choreo/OUTPOST_DEPOT.traj new file mode 100644 index 00000000..7651266b --- /dev/null +++ b/lib/examples/swerve/src/main/deploy/choreo/OUTPOST_DEPOT.traj @@ -0,0 +1,193 @@ +{ + "name":"OUTPOST_DEPOT", + "version":3, + "snapshot":{ + "waypoints":[ + {"x":0.4072994291782379, "y":0.6569390296936035, "heading":3.141592653589793, "intervals":17, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, + {"x":1.6635223627090454, "y":1.6768428087234497, "heading":0.0, "intervals":22, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, + {"x":1.8984053134918213, "y":4.315596580505371, "heading":0.0, "intervals":18, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, + {"x":1.2030097246170044, "y":5.9760308265686035, "heading":3.141592653589793, "intervals":18, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, + {"x":0.42246365547180176, "y":5.961839199066162, "heading":-3.118608391365919, "intervals":16, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, + {"x":1.8416383266448977, "y":5.9760308265686035, "heading":0.0, "intervals":30, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, + {"x":1.9835559129714968, "y":3.8046934604644775, "heading":1.591626654119535, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], + "constraints":[ + {"from":"first", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, + {"from":"last", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, + {"from":"first", "to":"last", "data":{"type":"KeepInRectangle", "props":{"x":0.0, "y":0.0, "w":16.541, "h":8.0692}}, "enabled":true}, + {"from":3, "to":4, "data":{"type":"MaxVelocity", "props":{"max":1.0}}, "enabled":true}, + {"from":3, "to":5, "data":{"type":"MaxAngularVelocity", "props":{"max":0.0}}, "enabled":true}], + "targetDt":0.05 + }, + "params":{ + "waypoints":[ + {"x":{"exp":"0.4072994291782379 m", "val":0.4072994291782379}, "y":{"exp":"0.6569390296936035 m", "val":0.6569390296936035}, "heading":{"exp":"3.141592653589793 rad", "val":3.141592653589793}, "intervals":17, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, + {"x":{"exp":"1.6635223627090454 m", "val":1.6635223627090454}, "y":{"exp":"1.6768428087234497 m", "val":1.6768428087234497}, "heading":{"exp":"0 deg", "val":0.0}, "intervals":22, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, + {"x":{"exp":"1.8984053134918213 m", "val":1.8984053134918213}, "y":{"exp":"4.315596580505371 m", "val":4.315596580505371}, "heading":{"exp":"0 deg", "val":0.0}, "intervals":18, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, + {"x":{"exp":"1.2030097246170044 m", "val":1.2030097246170044}, "y":{"exp":"5.9760308265686035 m", "val":5.9760308265686035}, "heading":{"exp":"3.141592653589793 rad", "val":3.141592653589793}, "intervals":18, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, + {"x":{"exp":"0.42246365547180176 m", "val":0.42246365547180176}, "y":{"exp":"5.961839199066162 m", "val":5.961839199066162}, "heading":{"exp":"-3.118608391365919 rad", "val":-3.118608391365919}, "intervals":16, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, + {"x":{"exp":"1.8416383266448975 m", "val":1.8416383266448977}, "y":{"exp":"5.9760308265686035 m", "val":5.9760308265686035}, "heading":{"exp":"0 deg", "val":0.0}, "intervals":30, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, + {"x":{"exp":"1.9835559129714966 m", "val":1.9835559129714968}, "y":{"exp":"3.8046934604644775 m", "val":3.8046934604644775}, "heading":{"exp":"1.591626654119535 rad", "val":1.591626654119535}, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], + "constraints":[ + {"from":"first", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, + {"from":"last", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, + {"from":"first", "to":"last", "data":{"type":"KeepInRectangle", "props":{"x":{"exp":"0 m", "val":0.0}, "y":{"exp":"0 m", "val":0.0}, "w":{"exp":"16.541 m", "val":16.541}, "h":{"exp":"8.0692 m", "val":8.0692}}}, "enabled":true}, + {"from":3, "to":4, "data":{"type":"MaxVelocity", "props":{"max":{"exp":"1 m / s", "val":1.0}}}, "enabled":true}, + {"from":3, "to":5, "data":{"type":"MaxAngularVelocity", "props":{"max":{"exp":"0 rad / s", "val":0.0}}}, "enabled":true}], + "targetDt":{ + "exp":"0.05 s", + "val":0.05 + } + }, + "trajectory":{ + "config":{ + "frontLeft":{ + "x":0.225425, + "y":0.333375 + }, + "backLeft":{ + "x":-0.225425, + "y":0.333375 + }, + "mass":68.0388555, + "inertia":7.5, + "gearing":7.03, + "radius":0.0508, + "vmax":607.3745796940267, + "tmax":1.2, + "cof":1.5, + "bumper":{ + "front":0.34925, + "side":0.4445, + "back":0.34925 + }, + "differentialTrackWidth":0.5588 + }, + "sampleType":"Swerve", + "waypoints":[0.0,0.59907,1.21595,1.76993,2.60807,3.24518,4.1111], + "samples":[ + {"t":0.0, "x":0.4073, "y":0.65694, "heading":3.14159, "vx":0.0, "vy":0.0, "omega":0.0, "ax":8.45162, "ay":4.86406, "alpha":0.0, "fx":[143.75966,143.75966,143.75966,143.75966], "fy":[82.73623,82.73623,82.73623,82.73623]}, + {"t":0.03524, "x":0.41255, "y":0.65996, "heading":3.14159, "vx":0.29783, "vy":0.17141, "omega":0.0, "ax":8.41694, "ay":4.92189, "alpha":0.0, "fx":[143.16972,143.16972,143.16972,143.16972], "fy":[83.72001,83.72001,83.72001,83.72001]}, + {"t":0.07048, "x":0.42827, "y":0.66906, "heading":3.14159, "vx":0.59444, "vy":0.34485, "omega":0.0, "ax":8.37543, "ay":4.98995, "alpha":0.0, "fx":[142.46362,142.46362,142.46362,142.46362], "fy":[84.87754,84.87754,84.87754,84.87754]}, + {"t":0.10572, "x":0.45442, "y":0.68431, "heading":3.14159, "vx":0.88959, "vy":0.5207, "omega":0.0, "ax":8.32478, "ay":5.07131, "alpha":0.0, "fx":[141.60221,141.60221,141.60221,141.60221], "fy":[86.26152,86.26152,86.26152,86.26152]}, + {"t":0.14096, "x":0.49093, "y":0.7058, "heading":3.14159, "vx":1.18295, "vy":0.69941, "omega":0.0, "ax":8.2616, "ay":5.1704, "alpha":0.0, "fx":[140.52744,140.52744,140.52744,140.52744], "fy":[87.94699,87.94699,87.94699,87.94699]}, + {"t":0.1762, "x":0.53775, "y":0.73366, "heading":3.14159, "vx":1.47408, "vy":0.88161, "omega":0.0, "ax":8.1806, "ay":5.2937, "alpha":0.0, "fx":[139.14967,139.14967,139.14967,139.14967], "fy":[90.04435,90.04435,90.04435,90.04435]}, + {"t":0.21144, "x":0.59478, "y":0.76802, "heading":3.14159, "vx":1.76236, "vy":1.06816, "omega":0.0, "ax":8.07321, "ay":5.45118, "alpha":0.0, "fx":[137.32291,137.32291,137.32291,137.32291], "fy":[92.72304,92.72304,92.72304,92.72304]}, + {"t":0.24668, "x":0.66189, "y":0.80904, "heading":3.14159, "vx":2.04686, "vy":1.26025, "omega":0.0, "ax":7.92448, "ay":5.65892, "alpha":0.0, "fx":[134.79316,134.79316,134.79316,134.79316], "fy":[96.25653,96.25653,96.25653,96.25653]}, + {"t":0.28192, "x":0.73894, "y":0.85697, "heading":3.14159, "vx":2.32611, "vy":1.45967, "omega":0.0, "ax":7.70615, "ay":5.94444, "alpha":0.0, "fx":[131.07942,131.07942,131.07942,131.07942], "fy":[101.11326,101.11326,101.11326,101.11326]}, + {"t":0.31716, "x":0.8257, "y":0.9121, "heading":3.14159, "vx":2.59768, "vy":1.66915, "omega":0.0, "ax":7.35835, "ay":6.3583, "alpha":0.0, "fx":[125.16336,125.16336,125.16336,125.16336], "fy":[108.15285,108.15285,108.15285,108.15285]}, + {"t":0.3524, "x":0.92181, "y":0.97486, "heading":-3.14159, "vx":2.85698, "vy":1.89321, "omega":0.0, "ax":6.73347, "ay":6.9996, "alpha":0.0, "fx":[114.53434,114.53434,114.53434,114.53434], "fy":[119.06121,119.06121,119.06121,119.06121]}, + {"t":0.38763, "x":1.02667, "y":1.04593, "heading":-3.14159, "vx":3.09426, "vy":2.13988, "omega":0.0, "ax":5.38512, "ay":8.05623, "alpha":0.0, "fx":[91.59934,91.59934,91.59934,91.59934], "fy":[137.03415,137.03415,137.03415,137.03415]}, + {"t":0.42287, "x":1.13905, "y":1.12634, "heading":-3.14159, "vx":3.28403, "vy":2.42377, "omega":0.0, "ax":1.72721, "ay":9.49302, "alpha":0.0, "fx":[29.37929,29.37929,29.37929,29.37929], "fy":[161.47351,161.47351,161.47351,161.47351]}, + {"t":0.45811, "x":1.25585, "y":1.21764, "heading":-3.14159, "vx":3.3449, "vy":2.7583, "omega":0.0, "ax":-5.36969, "ay":7.99104, "alpha":0.0, "fx":[-91.33685,-91.33685,-91.33685,-91.33685], "fy":[135.92529,135.92529,135.92529,135.92529]}, + {"t":0.49335, "x":1.37039, "y":1.31981, "heading":-3.14159, "vx":3.15567, "vy":3.0399, "omega":0.0, "ax":-6.95017, "ay":6.70502, "alpha":0.0, "fx":[-118.22046,-118.22046,-118.22046,-118.22046], "fy":[114.05052,114.05052,114.05052,114.05052]}, + {"t":0.52859, "x":1.47728, "y":1.43109, "heading":-3.14159, "vx":2.91075, "vy":3.27619, "omega":0.0, "ax":-7.49246, "ay":6.12989, "alpha":0.0, "fx":[-127.4446,-127.4446,-127.4446,-127.4446], "fy":[104.2676,104.2676,104.2676,104.2676]}, + {"t":0.56383, "x":1.5752, "y":1.55035, "heading":-3.14159, "vx":2.64672, "vy":3.4922, "omega":0.0, "ax":-7.97277, "ay":5.52057, "alpha":0.0, "fx":[-135.6146,-135.6146,-135.6146,-135.6146], "fy":[93.90327,93.90327,93.90327,93.90327]}, + {"t":0.59907, "x":1.66352, "y":1.67684, "heading":-3.14159, "vx":2.36577, "vy":3.68674, "omega":0.0, "ax":-8.31322, "ay":4.96267, "alpha":0.0, "fx":[-141.40541,-141.40541,-141.40541,-141.40541], "fy":[84.41356,84.41356,84.41356,84.41356]}, + {"t":0.62711, "x":1.72659, "y":1.78217, "heading":-3.14159, "vx":2.13267, "vy":3.82589, "omega":0.0, "ax":-8.57173, "ay":4.46051, "alpha":0.0, "fx":[-145.80271,-145.80271,-145.80271,-145.80271], "fy":[75.87199,75.87199,75.87199,75.87199]}, + {"t":0.65515, "x":1.78302, "y":1.8912, "heading":-3.14159, "vx":1.89232, "vy":3.95097, "omega":0.0, "ax":-8.80711, "ay":3.91052, "alpha":0.0, "fx":[-149.80637,-149.80637,-149.80637,-149.80637], "fy":[66.51683,66.51683,66.51683,66.51683]}, + {"t":0.68319, "x":1.83262, "y":2.00352, "heading":-3.14159, "vx":1.64537, "vy":4.06062, "omega":0.0, "ax":-8.99611, "ay":3.34374, "alpha":0.0, "fx":[-153.02129,-153.02129,-153.02129,-153.02129], "fy":[56.87604,56.87604,56.87604,56.87604]}, + {"t":0.71123, "x":1.87522, "y":2.11869, "heading":-3.14159, "vx":1.39312, "vy":4.15437, "omega":0.0, "ax":-9.12793, "ay":2.76628, "alpha":0.0, "fx":[-155.2634,-155.2634,-155.2634,-155.2634], "fy":[47.05356,47.05356,47.05356,47.05356]}, + {"t":0.73927, "x":1.91069, "y":2.23627, "heading":-3.14159, "vx":1.13717, "vy":4.23194, "omega":0.0, "ax":-9.18499, "ay":2.18198, "alpha":0.0, "fx":[-156.23411,-156.23411,-156.23411,-156.23411], "fy":[37.11488,37.11488,37.11488,37.11488]}, + {"t":0.76731, "x":1.93897, "y":2.35579, "heading":-3.14159, "vx":0.87963, "vy":4.29312, "omega":0.0, "ax":-9.12966, "ay":1.59664, "alpha":0.0, "fx":[-155.29292,-155.29292,-155.29292,-155.29292], "fy":[27.15832,27.15832,27.15832,27.15832]}, + {"t":0.79535, "x":1.96004, "y":2.4768, "heading":-3.14159, "vx":0.62363, "vy":4.33789, "omega":0.0, "ax":-8.87067, "ay":1.02203, "alpha":0.0, "fx":[-150.88763,-150.88763,-150.88763,-150.88763], "fy":[17.38443,17.38443,17.38443,17.38443]}, + {"t":0.82339, "x":1.97404, "y":2.59883, "heading":-3.14159, "vx":0.3749, "vy":4.36655, "omega":0.0, "ax":-8.16454, "ay":0.48884, "alpha":0.0, "fx":[-138.87649,-138.87649,-138.87649,-138.87649], "fy":[8.31511,8.31511,8.31511,8.31511]}, + {"t":0.85143, "x":1.98134, "y":2.72146, "heading":-3.14159, "vx":0.14597, "vy":4.38026, "omega":0.0, "ax":-6.42487, "ay":0.08318, "alpha":0.0, "fx":[-109.28525,-109.28525,-109.28525,-109.28525], "fy":[1.41483,1.41483,1.41483,1.41483]}, + {"t":0.87947, "x":1.98291, "y":2.84432, "heading":-3.14159, "vx":-0.03418, "vy":4.38259, "omega":0.0, "ax":-3.47135, "ay":-0.06529, "alpha":0.0, "fx":[-59.04674,-59.04674,-59.04674,-59.04674], "fy":[-1.11055,-1.11055,-1.11055,-1.11055]}, + {"t":0.90751, "x":1.98059, "y":2.96718, "heading":3.14159, "vx":-0.13152, "vy":4.38076, "omega":0.0, "ax":-1.30676, "ay":-0.04465, "alpha":0.0, "fx":[-22.22754,-22.22754,-22.22754,-22.22754], "fy":[-0.75944,-0.75944,-0.75944,-0.75944]}, + {"t":0.93555, "x":1.97639, "y":3.08999, "heading":3.14159, "vx":-0.16816, "vy":4.37951, "omega":0.0, "ax":-0.45001, "ay":-0.01793, "alpha":0.0, "fx":[-7.65448,-7.65448,-7.65448,-7.65448], "fy":[-0.30491,-0.30491,-0.30491,-0.30491]}, + {"t":0.96359, "x":1.97149, "y":3.21279, "heading":3.14159, "vx":-0.18078, "vy":4.379, "omega":0.0, "ax":-0.16431, "ay":-0.00687, "alpha":0.0, "fx":[-2.79485,-2.79485,-2.79485,-2.79485], "fy":[-0.11691,-0.11691,-0.11691,-0.11691]}, + {"t":0.99163, "x":1.96636, "y":3.33557, "heading":3.14159, "vx":-0.18539, "vy":4.37881, "omega":0.0, "ax":-0.08064, "ay":-0.00344, "alpha":0.0, "fx":[-1.37159,-1.37159,-1.37159,-1.37159], "fy":[-0.05848,-0.05848,-0.05848,-0.05848]}, + {"t":1.01967, "x":1.96113, "y":3.45835, "heading":3.14159, "vx":-0.18765, "vy":4.37871, "omega":0.0, "ax":-0.08557, "ay":-0.00369, "alpha":0.0, "fx":[-1.45553,-1.45553,-1.45553,-1.45553], "fy":[-0.06282,-0.06282,-0.06282,-0.06282]}, + {"t":1.04771, "x":1.95584, "y":3.58113, "heading":3.14159, "vx":-0.19005, "vy":4.37861, "omega":0.0, "ax":-0.18578, "ay":-0.00818, "alpha":0.0, "fx":[-3.16008,-3.16008,-3.16008,-3.16008], "fy":[-0.13909,-0.13909,-0.13909,-0.13909]}, + {"t":1.07575, "x":1.95043, "y":3.7039, "heading":3.14159, "vx":-0.19525, "vy":4.37838, "omega":0.0, "ax":-0.51666, "ay":-0.02391, "alpha":0.0, "fx":[-8.78817,-8.78817,-8.78817,-8.78817], "fy":[-0.40666,-0.40666,-0.40666,-0.40666]}, + {"t":1.10379, "x":1.94476, "y":3.82666, "heading":3.14159, "vx":-0.20974, "vy":4.37771, "omega":0.0, "ax":-1.49723, "ay":-0.07901, "alpha":0.0, "fx":[-25.46745,-25.46745,-25.46745,-25.46745], "fy":[-1.34397,-1.34397,-1.34397,-1.34397]}, + {"t":1.13183, "x":1.93829, "y":3.94938, "heading":3.14159, "vx":-0.25172, "vy":4.3755, "omega":0.0, "ax":-3.85159, "ay":-0.26978, "alpha":0.0, "fx":[-65.51451,-65.51451,-65.51451,-65.51451], "fy":[-4.5889,-4.5889,-4.5889,-4.5889]}, + {"t":1.15987, "x":1.92971, "y":4.07196, "heading":3.14159, "vx":-0.35972, "vy":4.36793, "omega":0.0, "ax":-6.7014, "ay":-0.69895, "alpha":0.0, "fx":[-113.98888,-113.98888,-113.98888,-113.98888], "fy":[-11.88889,-11.88889,-11.88889,-11.88889]}, + {"t":1.18791, "x":1.91699, "y":4.19416, "heading":3.14159, "vx":-0.54763, "vy":4.34833, "omega":0.0, "ax":-8.22221, "ay":-1.24883, "alpha":0.0, "fx":[-139.85741,-139.85741,-139.85741,-139.85741], "fy":[-21.24224,-21.24224,-21.24224,-21.24224]}, + {"t":1.21595, "x":1.89841, "y":4.3156, "heading":3.14159, "vx":-0.77818, "vy":4.31332, "omega":0.0, "ax":-8.19102, "ay":-1.71636, "alpha":0.0, "fx":[-139.32693,-139.32693,-139.32693,-139.32693], "fy":[-29.19479,-29.19479,-29.19479,-29.19479]}, + {"t":1.24672, "x":1.87058, "y":4.44753, "heading":3.14159, "vx":-1.03027, "vy":4.26049, "omega":0.0, "ax":-6.29351, "ay":-1.67392, "alpha":0.0, "fx":[-107.05084,-107.05084,-107.05084,-107.05084], "fy":[-28.47291,-28.47291,-28.47291,-28.47291]}, + {"t":1.2775, "x":1.83589, "y":4.57787, "heading":3.14159, "vx":-1.22397, "vy":4.20897, "omega":0.0, "ax":-3.15206, "ay":-0.956, "alpha":0.0, "fx":[-53.61567,-53.61567,-53.61567,-53.61567], "fy":[-16.26121,-16.26121,-16.26121,-16.26121]}, + {"t":1.30828, "x":1.79672, "y":4.70695, "heading":3.14159, "vx":-1.32098, "vy":4.17955, "omega":0.0, "ax":-1.54381, "ay":-0.49759, "alpha":0.0, "fx":[-26.25981,-26.25981,-26.25981,-26.25981], "fy":[-8.46383,-8.46383,-8.46383,-8.46383]}, + {"t":1.33906, "x":1.75534, "y":4.83535, "heading":3.14159, "vx":-1.36849, "vy":4.16424, "omega":0.0, "ax":-2.26552, "ay":-0.7658, "alpha":0.0, "fx":[-38.53589,-38.53589,-38.53589,-38.53589], "fy":[-13.0261,-13.0261,-13.0261,-13.0261]}, + {"t":1.36983, "x":1.71215, "y":4.96315, "heading":3.14159, "vx":-1.43822, "vy":4.14067, "omega":0.0, "ax":-5.04703, "ay":-1.88432, "alpha":0.0, "fx":[-85.84847,-85.84847,-85.84847,-85.84847], "fy":[-32.05173,-32.05173,-32.05173,-32.05173]}, + {"t":1.40061, "x":1.66549, "y":5.0897, "heading":3.14159, "vx":-1.59355, "vy":4.08267, "omega":0.0, "ax":-2.66062, "ay":-8.09233, "alpha":0.0, "fx":[-45.25635,-45.25635,-45.25635,-45.25635], "fy":[-137.64814,-137.64814,-137.64814,-137.64814]}, + {"t":1.43139, "x":1.61519, "y":5.21151, "heading":3.14159, "vx":-1.67543, "vy":3.83362, "omega":0.0, "ax":2.25404, "ay":-9.32669, "alpha":0.0, "fx":[38.34059,38.34059,38.34059,38.34059], "fy":[-158.64434,-158.64434,-158.64434,-158.64434]}, + {"t":1.46216, "x":1.56469, "y":5.32508, "heading":3.14159, "vx":-1.60606, "vy":3.54657, "omega":0.0, "ax":2.61258, "ay":-9.31519, "alpha":0.0, "fx":[44.43926,44.43926,44.43926,44.43926], "fy":[-158.44874,-158.44874,-158.44874,-158.44874]}, + {"t":1.49294, "x":1.5165, "y":5.42982, "heading":3.14159, "vx":-1.52565, "vy":3.25988, "omega":0.0, "ax":2.74232, "ay":-9.3075, "alpha":0.0, "fx":[46.64616,46.64616,46.64616,46.64616], "fy":[-158.31789,-158.31789,-158.31789,-158.31789]}, + {"t":1.52372, "x":1.47084, "y":5.52575, "heading":3.14159, "vx":-1.44125, "vy":2.97342, "omega":0.0, "ax":2.80951, "ay":-9.30269, "alpha":0.0, "fx":[47.789,47.789,47.789,47.789], "fy":[-158.23613,-158.23613,-158.23613,-158.23613]}, + {"t":1.55449, "x":1.42782, "y":5.61285, "heading":3.14159, "vx":-1.35479, "vy":2.68711, "omega":0.0, "ax":2.85075, "ay":-9.29942, "alpha":0.0, "fx":[48.49041,48.49041,48.49041,48.49041], "fy":[-158.18051,-158.18051,-158.18051,-158.18051]}, + {"t":1.58527, "x":1.38747, "y":5.69115, "heading":3.14159, "vx":-1.26705, "vy":2.40091, "omega":0.0, "ax":2.87874, "ay":-9.29704, "alpha":0.0, "fx":[48.96654,48.96654,48.96654,48.96654], "fy":[-158.13996,-158.13996,-158.13996,-158.13996]}, + {"t":1.61605, "x":1.34984, "y":5.76064, "heading":3.14159, "vx":-1.17845, "vy":2.11477, "omega":0.0, "ax":2.89906, "ay":-9.29521, "alpha":0.0, "fx":[49.31222,49.31222,49.31222,49.31222], "fy":[-158.10881,-158.10881,-158.10881,-158.10881]}, + {"t":1.64682, "x":1.31494, "y":5.82132, "heading":3.14159, "vx":-1.08923, "vy":1.82869, "omega":0.0, "ax":2.91455, "ay":-9.29374, "alpha":0.0, "fx":[49.57562,49.57562,49.57562,49.57562], "fy":[-158.08388,-158.08388,-158.08388,-158.08388]}, + {"t":1.6776, "x":1.2828, "y":5.8732, "heading":3.14159, "vx":-0.99952, "vy":1.54266, "omega":0.0, "ax":2.92679, "ay":-9.29253, "alpha":0.0, "fx":[49.78381,49.78381,49.78381,49.78381], "fy":[-158.06327,-158.06327,-158.06327,-158.06327]}, + {"t":1.70838, "x":1.25342, "y":5.91628, "heading":3.14159, "vx":-0.90945, "vy":1.25667, "omega":0.0, "ax":2.93674, "ay":-9.2915, "alpha":0.0, "fx":[49.95317,49.95317,49.95317,49.95317], "fy":[-158.04578,-158.04578,-158.04578,-158.04578]}, + {"t":1.73915, "x":1.22682, "y":5.95056, "heading":3.14159, "vx":-0.81906, "vy":0.9707, "omega":0.0, "ax":2.94503, "ay":-9.29061, "alpha":0.0, "fx":[50.09418,50.09418,50.09418,50.09418], "fy":[-158.03062,-158.03062,-158.03062,-158.03062]}, + {"t":1.76993, "x":1.20301, "y":5.97603, "heading":3.14159, "vx":-0.72842, "vy":0.68477, "omega":0.0, "ax":-4.88244, "ay":-8.40909, "alpha":0.0, "fx":[-83.04891,-83.04891,-83.04891,-83.04891], "fy":[-143.03621,-143.03621,-143.03621,-143.03621]}, + {"t":1.8165, "x":1.1638, "y":5.9988, "heading":3.14159, "vx":-0.95577, "vy":0.29321, "omega":0.0, "ax":-0.77597, "ay":-8.98901, "alpha":0.0, "fx":[-13.19897,-13.19897,-13.19897,-13.19897], "fy":[-152.90048,-152.90048,-152.90048,-152.90048]}, + {"t":1.86306, "x":1.11845, "y":6.00271, "heading":3.14159, "vx":-0.9919, "vy":-0.12534, "omega":0.0, "ax":0.06692, "ay":-0.48521, "alpha":0.0, "fx":[1.13832,1.13832,1.13832,1.13832], "fy":[-8.25327,-8.25327,-8.25327,-8.25327]}, + {"t":1.90962, "x":1.07234, "y":5.99635, "heading":3.14159, "vx":-0.98878, "vy":-0.14794, "omega":0.0, "ax":0.00052, "ay":-0.00344, "alpha":0.0, "fx":[0.00876,0.00876,0.00876,0.00876], "fy":[-0.05856,-0.05856,-0.05856,-0.05856]}, + {"t":1.95618, "x":1.0263, "y":5.98945, "heading":3.14159, "vx":-0.98876, "vy":-0.1481, "omega":0.0, "ax":-0.00007, "ay":0.00047, "alpha":0.0, "fx":[-0.00121,-0.00121,-0.00121,-0.00121], "fy":[0.00808,0.00808,0.00808,0.00808]}, + {"t":2.00275, "x":0.98026, "y":5.98256, "heading":3.14159, "vx":-0.98876, "vy":-0.14808, "omega":0.0, "ax":-0.00008, "ay":0.00051, "alpha":0.0, "fx":[-0.00132,-0.00132,-0.00132,-0.00132], "fy":[0.00876,0.00876,0.00876,0.00876]}, + {"t":2.04931, "x":0.93422, "y":5.97566, "heading":3.14159, "vx":-0.98876, "vy":-0.14805, "omega":0.0, "ax":-0.00008, "ay":0.00052, "alpha":0.0, "fx":[-0.00134,-0.00134,-0.00134,-0.00134], "fy":[0.00893,0.00893,0.00893,0.00893]}, + {"t":2.09587, "x":0.88818, "y":5.96877, "heading":3.14159, "vx":-0.98877, "vy":-0.14803, "omega":0.0, "ax":-0.00008, "ay":0.00054, "alpha":0.0, "fx":[-0.00137,-0.00137,-0.00137,-0.00137], "fy":[0.00913,0.00913,0.00913,0.00913]}, + {"t":2.14244, "x":0.84214, "y":5.96188, "heading":3.14159, "vx":-0.98877, "vy":-0.148, "omega":0.0, "ax":-0.00008, "ay":0.00055, "alpha":0.0, "fx":[-0.00141,-0.00141,-0.00141,-0.00141], "fy":[0.00936,0.00936,0.00936,0.00936]}, + {"t":2.189, "x":0.7961, "y":5.95499, "heading":3.14159, "vx":-0.98878, "vy":-0.14798, "omega":0.0, "ax":-0.00009, "ay":0.00057, "alpha":0.0, "fx":[-0.00145,-0.00145,-0.00145,-0.00145], "fy":[0.00965,0.00965,0.00965,0.00965]}, + {"t":2.23556, "x":0.75006, "y":5.9481, "heading":3.14159, "vx":-0.98878, "vy":-0.14795, "omega":0.0, "ax":-0.00009, "ay":0.00059, "alpha":0.0, "fx":[-0.0015,-0.0015,-0.0015,-0.0015], "fy":[0.01001,0.01001,0.01001,0.01001]}, + {"t":2.28213, "x":0.70402, "y":5.94121, "heading":3.14159, "vx":-0.98878, "vy":-0.14792, "omega":0.0, "ax":-0.00009, "ay":0.00062, "alpha":0.0, "fx":[-0.00157,-0.00157,-0.00157,-0.00157], "fy":[0.01047,0.01047,0.01047,0.01047]}, + {"t":2.32869, "x":0.65798, "y":5.93432, "heading":3.14159, "vx":-0.98879, "vy":-0.14789, "omega":0.0, "ax":-0.0001, "ay":0.00067, "alpha":0.0, "fx":[-0.00172,-0.00172,-0.00172,-0.00172], "fy":[0.01145,0.01145,0.01145,0.01145]}, + {"t":2.37525, "x":0.61194, "y":5.92744, "heading":3.14159, "vx":-0.98879, "vy":-0.14786, "omega":0.0, "ax":-0.00053, "ay":0.00356, "alpha":0.0, "fx":[-0.00906,-0.00906,-0.00906,-0.00906], "fy":[0.06055,0.06055,0.06055,0.06055]}, + {"t":2.42182, "x":0.56589, "y":5.92056, "heading":3.14159, "vx":-0.98882, "vy":-0.1477, "omega":0.0, "ax":-0.04983, "ay":0.35385, "alpha":0.0, "fx":[-0.84761,-0.84761,-0.84761,-0.84761], "fy":[6.01893,6.01893,6.01893,6.01893]}, + {"t":2.46838, "x":0.5198, "y":5.91406, "heading":3.14159, "vx":-0.99114, "vy":-0.13122, "omega":0.0, "ax":0.65084, "ay":8.73589, "alpha":0.0, "fx":[11.07052,11.07052,11.07052,11.07052], "fy":[148.595,148.595,148.595,148.595]}, + {"t":2.51494, "x":0.47435, "y":5.91742, "heading":3.14159, "vx":-0.96083, "vy":0.27555, "omega":0.0, "ax":8.44163, "ay":4.84358, "alpha":0.0, "fx":[143.58977,143.58977,143.58977,143.58977], "fy":[82.38793,82.38793,82.38793,82.38793]}, + {"t":2.56151, "x":0.43877, "y":5.9355, "heading":3.14159, "vx":-0.56776, "vy":0.50108, "omega":0.0, "ax":9.3492, "ay":2.7711, "alpha":0.0, "fx":[159.02729,159.02729,159.02729,159.02729], "fy":[47.13566,47.13566,47.13566,47.13566]}, + {"t":2.60807, "x":0.42246, "y":5.96184, "heading":3.14159, "vx":-0.13244, "vy":0.63011, "omega":0.0, "ax":9.47314, "ay":2.31528, "alpha":0.0, "fx":[161.13544,161.13544,161.13544,161.13544], "fy":[39.38233,39.38233,39.38233,39.38233]}, + {"t":2.64789, "x":0.4247, "y":5.98877, "heading":3.14159, "vx":0.24478, "vy":0.72231, "omega":0.0, "ax":9.5479, "ay":1.97827, "alpha":0.0, "fx":[162.40699,162.40699,162.40699,162.40699], "fy":[33.64986,33.64986,33.64986,33.64986]}, + {"t":2.68771, "x":0.44202, "y":6.0191, "heading":3.14159, "vx":0.62498, "vy":0.80108, "omega":0.0, "ax":9.62544, "ay":1.54776, "alpha":0.0, "fx":[163.72597,163.72597,163.72597,163.72597], "fy":[26.32689,26.32689,26.32689,26.32689]}, + {"t":2.72753, "x":0.47453, "y":6.05222, "heading":3.14159, "vx":1.00826, "vy":0.86271, "omega":0.0, "ax":9.69732, "ay":0.98323, "alpha":0.0, "fx":[164.94872,164.94872,164.94872,164.94872], "fy":[16.72448,16.72448,16.72448,16.72448]}, + {"t":2.76735, "x":0.52237, "y":6.08735, "heading":3.14159, "vx":1.3944, "vy":0.90186, "omega":0.0, "ax":9.74189, "ay":0.22086, "alpha":0.0, "fx":[165.70678,165.70678,165.70678,165.70678], "fy":[3.75675,3.75675,3.75675,3.75675]}, + {"t":2.80717, "x":0.58562, "y":6.12344, "heading":3.14159, "vx":1.78232, "vy":0.91066, "omega":0.0, "ax":9.70463, "ay":-0.83986, "alpha":0.0, "fx":[165.07301,165.07301,165.07301,165.07301], "fy":[-14.28583,-14.28583,-14.28583,-14.28583]}, + {"t":2.84699, "x":0.66428, "y":6.15904, "heading":3.14159, "vx":2.16875, "vy":0.87722, "omega":0.0, "ax":9.4498, "ay":-2.34477, "alpha":0.0, "fx":[160.73839,160.73839,160.73839,160.73839], "fy":[-39.8839,-39.8839,-39.8839,-39.8839]}, + {"t":2.88681, "x":0.75813, "y":6.19211, "heading":3.14159, "vx":2.54504, "vy":0.78385, "omega":0.0, "ax":8.66341, "ay":-4.43124, "alpha":0.0, "fx":[147.36215,147.36215,147.36215,147.36215], "fy":[-75.37411,-75.37411,-75.37411,-75.37411]}, + {"t":2.92663, "x":0.86635, "y":6.21981, "heading":3.14159, "vx":2.89001, "vy":0.6074, "omega":0.0, "ax":6.82056, "ay":-6.93383, "alpha":0.0, "fx":[116.01573,116.01573,116.01573,116.01573], "fy":[-117.94252,-117.94252,-117.94252,-117.94252]}, + {"t":2.96645, "x":0.98683, "y":6.2385, "heading":3.14159, "vx":3.16161, "vy":0.3313, "omega":0.0, "ax":3.77071, "ay":-8.96463, "alpha":0.0, "fx":[64.13872,64.13872,64.13872,64.13872], "fy":[-152.48583,-152.48583,-152.48583,-152.48583]}, + {"t":3.00626, "x":1.11571, "y":6.24458, "heading":3.14159, "vx":3.31175, "vy":-0.02567, "omega":0.0, "ax":0.51801, "ay":-9.7155, "alpha":0.0, "fx":[8.81126,8.81126,8.81126,8.81126], "fy":[-165.25782,-165.25782,-165.25782,-165.25782]}, + {"t":3.04608, "x":1.248, "y":6.23586, "heading":3.14159, "vx":3.33238, "vy":-0.41254, "omega":0.0, "ax":-1.9561, "ay":-9.53625, "alpha":0.0, "fx":[-33.27275,-33.27275,-33.27275,-33.27275], "fy":[-162.20882,-162.20882,-162.20882,-162.20882]}, + {"t":3.0859, "x":1.37914, "y":6.21187, "heading":3.14159, "vx":3.25449, "vy":-0.79227, "omega":0.0, "ax":-3.59234, "ay":-9.05296, "alpha":0.0, "fx":[-61.10467,-61.10467,-61.10467,-61.10467], "fy":[-153.9883,-153.9883,-153.9883,-153.9883]}, + {"t":3.12572, "x":1.50589, "y":6.17314, "heading":3.14159, "vx":3.11144, "vy":-1.15275, "omega":0.0, "ax":-4.66024, "ay":-8.55669, "alpha":0.0, "fx":[-79.26927,-79.26927,-79.26927,-79.26927], "fy":[-145.54679,-145.54679,-145.54679,-145.54679]}, + {"t":3.16554, "x":1.62609, "y":6.12046, "heading":3.14159, "vx":2.92587, "vy":-1.49348, "omega":0.0, "ax":-5.38038, "ay":-8.12663, "alpha":0.0, "fx":[-91.51872,-91.51872,-91.51872,-91.51872], "fy":[-138.23165,-138.23165,-138.23165,-138.23165]}, + {"t":3.20536, "x":1.73833, "y":6.05455, "heading":3.14159, "vx":2.71163, "vy":-1.81708, "omega":0.0, "ax":-5.88681, "ay":-7.77039, "alpha":0.0, "fx":[-100.13296,-100.13296,-100.13296,-100.13296], "fy":[-132.17207,-132.17207,-132.17207,-132.17207]}, + {"t":3.24518, "x":1.84164, "y":5.97603, "heading":3.14159, "vx":2.47722, "vy":-2.12649, "omega":0.0, "ax":-5.23919, "ay":-7.35511, "alpha":-9.96289, "fx":[-152.26373,-117.57601,-27.23444,-59.39415], "fy":[-65.5778,-116.9683,-163.5104,-154.37663]}, + {"t":3.27405, "x":1.91096, "y":5.91159, "heading":3.14159, "vx":2.326, "vy":-2.33879, "omega":-0.28757, "ax":-5.51629, "ay":-7.23196, "alpha":-9.37966, "fx":[-152.59374,-119.25674,-33.36758,-70.1037], "fy":[-64.73809,-115.22055,-162.33379,-149.76191]}, + {"t":3.30291, "x":1.9758, "y":5.84107, "heading":3.13329, "vx":2.16678, "vy":-2.54753, "omega":-0.5583, "ax":-5.83074, "ay":-7.06397, "alpha":-8.80217, "fx":[-153.17534,-121.63811,-41.04599,-80.85716], "fy":[-63.26337,-112.66119,-160.52053,-144.17938]}, + {"t":3.33177, "x":2.03591, "y":5.76459, "heading":3.11718, "vx":1.99848, "vy":-2.75142, "omega":-0.81237, "ax":-6.19521, "ay":-6.8368, "alpha":-8.18471, "fx":[-153.99457,-124.77007,-50.67825,-92.07242], "fy":[-61.13034,-109.12709,-157.68979,-137.22056]}, + {"t":3.36064, "x":2.09101, "y":5.68233, "heading":3.09373, "vx":1.81966, "vy":-2.94876, "omega":-1.04861, "ax":-6.62572, "ay":-6.5258, "alpha":-7.47021, "fx":[-155.0362,-128.7378,-62.8783,-104.15417], "fy":[-58.28907,-104.34177,-153.16296,-128.21394]}, + {"t":3.3895, "x":2.14078, "y":5.5945, "heading":3.06346, "vx":1.62842, "vy":-3.13712, "omega":-1.26423, "ax":-7.14032, "ay":-6.0895, "alpha":-6.58306, "fx":[-156.28504,-133.67842,-78.51857,-117.33709], "fy":[-54.64677,-97.82473,-145.66826,-116.18281]}, + {"t":3.41837, "x":2.1848, "y":5.50141, "heading":3.02697, "vx":1.42232, "vy":-3.31289, "omega":-1.45424, "ax":-7.75384, "ay":-5.45812, "alpha":-5.41824, "fx":[-157.72746,-139.79819,-98.63199,-131.40453], "fy":[-50.0337,-88.69383,-132.73771,-99.89914]}, + {"t":3.44723, "x":2.22263, "y":5.40352, "heading":2.985, "vx":1.19851, "vy":-3.47043, "omega":-1.61063, "ax":-8.45834, "ay":-4.51715, "alpha":-3.83927, "fx":[-159.3526,-147.33975,-123.51732,-145.28577], "fy":[-44.12574,-75.21704,-109.74684,-78.25236]}, + {"t":3.47609, "x":2.2537, "y":5.30146, "heading":2.93851, "vx":0.95437, "vy":-3.60081, "omega":-1.72145, "ax":-9.1601, "ay":-3.12124, "alpha":-1.79436, "fx":[-161.15152,-156.13877,-149.1739,-156.77828], "fy":[-36.23186,-54.0358,-70.69145,-51.40624]}, + {"t":3.50496, "x":2.27743, "y":5.19623, "heading":2.88882, "vx":0.68998, "vy":-3.6909, "omega":-1.77324, "ax":-9.55142, "ay":-1.66222, "alpha":-0.4037, "fx":[-163.17302,-162.38935,-161.69574,-162.60938], "fy":[-24.17844,-29.06049,-32.44891,-27.4074]}, + {"t":3.53382, "x":2.29337, "y":5.08901, "heading":2.83764, "vx":0.41429, "vy":-3.73888, "omega":-1.78489, "ax":-9.64906, "ay":-0.74458, "alpha":-0.67148, "fx":[-164.63187,-164.13768,-163.46711,-164.27466], "fy":[-5.84365,-14.32241,-19.76324,-10.73118]}, + {"t":3.56269, "x":2.3013, "y":4.98078, "heading":2.78612, "vx":0.13578, "vy":-3.76037, "omega":-1.80427, "ax":-9.63684, "ay":0.25439, "alpha":-1.4703, "fx":[-163.43255,-164.54481,-163.85049,-163.8517], "fy":[18.63293,0.08014,-11.5835,10.17881]}, + {"t":3.59155, "x":2.30121, "y":4.87234, "heading":2.73404, "vx":-0.14238, "vy":-3.75303, "omega":-1.84671, "ax":-9.43478, "ay":1.45377, "alpha":-2.87821, "fx":[-156.716,-163.60312,-163.48471,-158.12812], "fy":[49.15977,15.38084,-6.75859,41.13058]}, + {"t":3.62041, "x":2.29317, "y":4.76462, "heading":2.68074, "vx":-0.4147, "vy":-3.71107, "omega":-1.92979, "ax":-8.79621, "ay":2.98612, "alpha":-5.38101, "fx":[-141.439,-160.81009,-162.46953,-133.76559], "fy":[83.10557,32.74388,-4.24365,91.56607]}, + {"t":3.64928, "x":2.27753, "y":4.65875, "heading":2.62504, "vx":-0.6686, "vy":-3.62488, "omega":-2.08511, "ax":-7.22374, "ay":4.87173, "alpha":-9.35282, "fx":[-116.23308,-153.95943,-160.07168,-61.23094], "fy":[115.608,56.18282,10.64212,149.03417]}, + {"t":3.67814, "x":2.25523, "y":4.55615, "heading":2.56485, "vx":-0.8771, "vy":-3.48426, "omega":-2.35506, "ax":-5.05046, "ay":6.30992, "alpha":-12.54923, "fx":[-84.39127,-141.82363,-147.4446,30.03231], "fy":[140.57365,81.81128,48.09584,158.83926]}, + {"t":3.707, "x":2.22781, "y":4.45821, "heading":2.49688, "vx":-1.02288, "vy":-3.30213, "omega":-2.71728, "ax":-1.78963, "ay":7.20981, "alpha":-14.91305, "fx":[-53.31823,-129.05342,-32.19106,92.79818], "fy":[155.19988,100.71114,101.13473,133.50172]}, + {"t":3.73587, "x":2.19754, "y":4.3659, "heading":2.41844, "vx":-1.07453, "vy":-3.09403, "omega":-3.14773, "ax":0.6823, "ay":8.80232, "alpha":-10.18299, "fx":[-14.56522,-76.17763,50.96836,86.19755], "fy":[163.56412,144.87491,151.62703,138.83373]}, + {"t":3.76473, "x":2.16681, "y":4.28026, "heading":2.32759, "vx":-1.05484, "vy":-2.83996, "omega":-3.44165, "ax":1.84452, "ay":9.46254, "alpha":1.9236, "fx":[37.0798,46.65387,26.24109,15.52403], "fy":[160.01463,157.66248,162.44866,163.69479]}, + {"t":3.7936, "x":2.13713, "y":4.20223, "heading":2.22825, "vx":-1.0016, "vy":-2.56683, "omega":-3.38613, "ax":2.67008, "ay":8.96882, "alpha":6.93388, "fx":[69.98179,95.71282,29.04617,-13.0719], "fy":[148.71386,134.37242,162.64731,164.49485]}, + {"t":3.82246, "x":2.10933, "y":4.13188, "heading":2.13051, "vx":-0.92453, "vy":-2.30796, "omega":-3.18599, "ax":3.17126, "ay":8.54228, "alpha":9.04046, "fx":[86.79727,117.57777,34.82344,-23.42957], "fy":[139.65475,116.11937,161.77763,163.65518]}, + {"t":3.85132, "x":2.08397, "y":4.06882, "heading":2.03855, "vx":-0.833, "vy":-2.0614, "omega":-2.92505, "ax":3.48878, "ay":8.25752, "alpha":9.89636, "fx":[92.61038,130.11597,41.12849,-26.48255], "fy":[135.8988,102.12528,160.43485,163.37315]}, + {"t":3.88019, "x":2.06138, "y":4.01276, "heading":1.95412, "vx":-0.7323, "vy":-1.82305, "omega":-2.6394, "ax":3.66426, "ay":8.09388, "alpha":10.14612, "fx":[89.92737,138.44843,47.26372,-26.32746], "fy":[137.66875,90.68774,158.82568,163.51587]}, + {"t":3.90905, "x":2.04176, "y":3.96351, "heading":1.87794, "vx":-0.62653, "vy":-1.58943, "omega":-2.34654, "ax":3.70256, "ay":8.02354, "alpha":10.15008, "fx":[79.2075,144.44861,52.9657,-24.70404], "fy":[144.05885,80.9266,157.07777,163.84911]}, + {"t":3.93792, "x":2.02522, "y":3.92098, "heading":1.81021, "vx":-0.51966, "vy":-1.35784, "omega":-2.05357, "ax":3.59945, "ay":8.00816, "alpha":10.19996, "fx":[60.26358,148.94802,58.12626,-22.43535], "fy":[152.92232,72.42429,155.28695,164.23239]}, + {"t":3.96678, "x":2.01172, "y":3.88512, "heading":1.75094, "vx":-0.41577, "vy":-1.12669, "omega":-1.75916, "ax":3.37279, "ay":7.99385, "alpha":10.54411, "fx":[34.35649,152.37186,62.69827,-19.94617], "fy":[160.75597,65.00856,153.532,164.59569]}, + {"t":3.99564, "x":2.00113, "y":3.85593, "heading":1.70016, "vx":-0.31842, "vy":-0.89596, "omega":-1.45482, "ax":3.08557, "ay":7.93362, "alpha":11.26732, "fx":[5.80367,154.96961,66.65259,-17.4869], "fy":[164.37081,58.63258,151.88379,164.90746]}, + {"t":4.02451, "x":1.99322, "y":3.83337, "heading":1.65817, "vx":-0.22935, "vy":-0.66697, "omega":-1.1296, "ax":2.81984, "ay":7.82331, "alpha":12.21022, "fx":[-19.76488,156.90998,69.95532,-15.24178], "fy":[163.41901,53.30183,150.41263,165.15532]}, + {"t":4.05337, "x":1.98778, "y":3.81738, "heading":1.62556, "vx":-0.14796, "vy":-0.44115, "omega":-0.77717, "ax":2.6239, "ay":7.69625, "alpha":13.11584, "fx":[-38.97793,158.31825,72.56007,-13.37335], "fy":[160.07848,49.03714,149.19113,165.33711]}, + {"t":4.08223, "x":1.9846, "y":3.80785, "heading":1.60313, "vx":-0.07223, "vy":-0.21901, "omega":-0.39859, "ax":2.50235, "ay":7.58773, "alpha":13.80941, "fx":[-51.42039,159.28993,74.41472,-12.02725], "fy":[156.65654,45.85797,148.28945,165.45655]}, + {"t":4.1111, "x":1.98356, "y":3.80469, "heading":1.59163, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0,0.0,0.0,0.0]}], + "splits":[0] + }, + "events":[] +} diff --git a/lib/examples/swerve/src/main/deploy/choreo/RIGHT_TRENCH.traj b/lib/examples/swerve/src/main/deploy/choreo/RIGHT_TRENCH.traj new file mode 100644 index 00000000..bee355fd --- /dev/null +++ b/lib/examples/swerve/src/main/deploy/choreo/RIGHT_TRENCH.traj @@ -0,0 +1,213 @@ +{ + "name":"RIGHT_TRENCH", + "version":3, + "snapshot":{ + "waypoints":[ + {"x":4.441253185272217, "y":0.6037423729896545, "heading":1.5707963267948966, "intervals":30, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, + {"x":7.795207023620605, "y":1.3253390531539917, "heading":1.5707963267948966, "intervals":24, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, + {"x":7.795207023620605, "y":3.931103515625, "heading":-1.5707963267948966, "intervals":33, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, + {"x":5.714679718017578, "y":0.62682590484619, "heading":1.5707963267948966, "intervals":23, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, + {"x":3.2933757305145264, "y":0.62682590484619, "heading":1.5707963267948966, "intervals":31, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, + {"x":1.9037671089172363, "y":4.050841808319092, "heading":1.570796602033749, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], + "constraints":[ + {"from":"first", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, + {"from":"last", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, + {"from":"first", "to":"last", "data":{"type":"KeepInRectangle", "props":{"x":0.0, "y":0.0, "w":16.541, "h":8.0692}}, "enabled":true}, + {"from":1, "to":2, "data":{"type":"KeepInLane", "props":{"tolerance":0.01}}, "enabled":true}, + {"from":"first", "to":"last", "data":{"type":"MaxVelocity", "props":{"max":3.0}}, "enabled":true}, + {"from":1, "to":2, "data":{"type":"MaxAngularVelocity", "props":{"max":0.0}}, "enabled":true}], + "targetDt":0.05 + }, + "params":{ + "waypoints":[ + {"x":{"exp":"4.441253185272217 m", "val":4.441253185272217}, "y":{"exp":"0.6037423729896545 m", "val":0.6037423729896545}, "heading":{"exp":"90 deg", "val":1.5707963267948966}, "intervals":30, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, + {"x":{"exp":"7.7952070236206055 m", "val":7.795207023620605}, "y":{"exp":"1.3253390531539917 m", "val":1.3253390531539917}, "heading":{"exp":"90 deg", "val":1.5707963267948966}, "intervals":24, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, + {"x":{"exp":"7.7952070236206055 m", "val":7.795207023620605}, "y":{"exp":"3.931103515625 m", "val":3.931103515625}, "heading":{"exp":"-1.5707963267948966 rad", "val":-1.5707963267948966}, "intervals":33, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, + {"x":{"exp":"5.714679718017578 m", "val":5.714679718017578}, "y":{"exp":"0.62682590484619 m", "val":0.62682590484619}, "heading":{"exp":"90 deg", "val":1.5707963267948966}, "intervals":23, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, + {"x":{"exp":"3.2933757305145264 m", "val":3.2933757305145264}, "y":{"exp":"0.62682590484619 m", "val":0.62682590484619}, "heading":{"exp":"90 deg", "val":1.5707963267948966}, "intervals":31, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, + {"x":{"exp":"1.9037671089172363 m", "val":1.9037671089172363}, "y":{"exp":"4.050841808319092 m", "val":4.050841808319092}, "heading":{"exp":"1.570796602033749 rad", "val":1.570796602033749}, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], + "constraints":[ + {"from":"first", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, + {"from":"last", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, + {"from":"first", "to":"last", "data":{"type":"KeepInRectangle", "props":{"x":{"exp":"0 m", "val":0.0}, "y":{"exp":"0 m", "val":0.0}, "w":{"exp":"16.541 m", "val":16.541}, "h":{"exp":"8.0692 m", "val":8.0692}}}, "enabled":true}, + {"from":1, "to":2, "data":{"type":"KeepInLane", "props":{"tolerance":{"exp":"0.01 m", "val":0.01}}}, "enabled":true}, + {"from":"first", "to":"last", "data":{"type":"MaxVelocity", "props":{"max":{"exp":"3 m / s", "val":3.0}}}, "enabled":true}, + {"from":1, "to":2, "data":{"type":"MaxAngularVelocity", "props":{"max":{"exp":"0 rad / s", "val":0.0}}}, "enabled":true}], + "targetDt":{ + "exp":"0.05 s", + "val":0.05 + } + }, + "trajectory":{ + "config":{ + "frontLeft":{ + "x":0.225425, + "y":0.333375 + }, + "backLeft":{ + "x":-0.225425, + "y":0.333375 + }, + "mass":68.0388555, + "inertia":7.5, + "gearing":7.03, + "radius":0.0508, + "vmax":607.3745796940267, + "tmax":1.2, + "cof":1.5, + "bumper":{ + "front":0.34925, + "side":0.4445, + "back":0.34925 + }, + "differentialTrackWidth":0.5588 + }, + "sampleType":"Swerve", + "waypoints":[0.0,1.40394,2.41372,3.87973,4.70708,6.10492], + "samples":[ + {"t":0.0, "x":4.44125, "y":0.60374, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":9.75357, "ay":-0.05824, "alpha":0.0, "fx":[165.90536,165.90536,165.90536,165.90536], "fy":[-0.99062,-0.99062,-0.99062,-0.99062]}, + {"t":0.0468, "x":4.45193, "y":0.60368, "heading":1.5708, "vx":0.45645, "vy":-0.00273, "omega":0.0, "ax":9.75174, "ay":-0.05985, "alpha":0.0, "fx":[165.8743,165.8743,165.8743,165.8743], "fy":[-1.01805,-1.01805,-1.01805,-1.01805]}, + {"t":0.0936, "x":4.48397, "y":0.60349, "heading":1.5708, "vx":0.91281, "vy":-0.00553, "omega":0.0, "ax":9.74901, "ay":-0.06147, "alpha":0.0, "fx":[165.82782,165.82782,165.82782,165.82782], "fy":[-1.04558,-1.04558,-1.04558,-1.04558]}, + {"t":0.14039, "x":4.53737, "y":0.60316, "heading":1.5708, "vx":1.36904, "vy":-0.0084, "omega":0.0, "ax":9.74446, "ay":-0.0631, "alpha":0.0, "fx":[165.75056,165.75056,165.75056,165.75056], "fy":[-1.07332,-1.07332,-1.07332,-1.07332]}, + {"t":0.18719, "x":4.6121, "y":0.6027, "heading":1.5708, "vx":1.82506, "vy":-0.01136, "omega":0.0, "ax":9.73541, "ay":-0.06476, "alpha":0.0, "fx":[165.59661,165.59661,165.59661,165.59661], "fy":[-1.10152,-1.10152,-1.10152,-1.10152]}, + {"t":0.23399, "x":4.70817, "y":0.60209, "heading":1.5708, "vx":2.28066, "vy":-0.01439, "omega":0.0, "ax":9.70849, "ay":-0.06652, "alpha":0.0, "fx":[165.13856,165.13856,165.13856,165.13856], "fy":[-1.13142,-1.13142,-1.13142,-1.13142]}, + {"t":0.28079, "x":4.82553, "y":0.60135, "heading":1.5708, "vx":2.73499, "vy":-0.0175, "omega":0.0, "ax":5.64742, "ay":-0.07918, "alpha":0.0, "fx":[96.06098,96.06098,96.06098,96.06098], "fy":[-1.34684,-1.34684,-1.34684,-1.34684]}, + {"t":0.32758, "x":4.95971, "y":0.60044, "heading":1.5708, "vx":2.99928, "vy":-0.0212, "omega":0.0, "ax":-0.00004, "ay":-0.02368, "alpha":0.0, "fx":[-0.00064,-0.00064,-0.00064,-0.00064], "fy":[-0.40272,-0.40272,-0.40272,-0.40272]}, + {"t":0.37438, "x":5.10007, "y":0.59942, "heading":1.5708, "vx":2.99928, "vy":-0.02231, "omega":0.0, "ax":-0.00016, "ay":-0.02147, "alpha":0.0, "fx":[-0.00278,-0.00278,-0.00278,-0.00278], "fy":[-0.36526,-0.36526,-0.36526,-0.36526]}, + {"t":0.42118, "x":5.24043, "y":0.59836, "heading":1.5708, "vx":2.99927, "vy":-0.02332, "omega":0.0, "ax":-0.00017, "ay":-0.02141, "alpha":0.0, "fx":[-0.00289,-0.00289,-0.00289,-0.00289], "fy":[-0.3641,-0.3641,-0.3641,-0.3641]}, + {"t":0.46798, "x":5.38079, "y":0.59724, "heading":1.5708, "vx":2.99926, "vy":-0.02432, "omega":0.0, "ax":-0.00018, "ay":-0.02148, "alpha":0.0, "fx":[-0.00303,-0.00303,-0.00303,-0.00303], "fy":[-0.36533,-0.36533,-0.36533,-0.36533]}, + {"t":0.51478, "x":5.52115, "y":0.59608, "heading":1.5708, "vx":2.99925, "vy":-0.02532, "omega":0.0, "ax":-0.00019, "ay":-0.02156, "alpha":0.0, "fx":[-0.00316,-0.00316,-0.00316,-0.00316], "fy":[-0.36679,-0.36679,-0.36679,-0.36679]}, + {"t":0.56157, "x":5.66151, "y":0.59487, "heading":1.5708, "vx":2.99924, "vy":-0.02633, "omega":0.0, "ax":-0.00019, "ay":-0.02165, "alpha":0.0, "fx":[-0.0033,-0.0033,-0.0033,-0.0033], "fy":[-0.36833,-0.36833,-0.36833,-0.36833]}, + {"t":0.60837, "x":5.80186, "y":0.59362, "heading":1.5708, "vx":2.99924, "vy":-0.02735, "omega":0.0, "ax":-0.0002, "ay":-0.02175, "alpha":0.0, "fx":[-0.00344,-0.00344,-0.00344,-0.00344], "fy":[-0.36994,-0.36994,-0.36994,-0.36994]}, + {"t":0.65517, "x":5.94222, "y":0.59231, "heading":1.5708, "vx":2.99923, "vy":-0.02836, "omega":0.0, "ax":-0.00021, "ay":-0.02185, "alpha":0.0, "fx":[-0.00358,-0.00358,-0.00358,-0.00358], "fy":[-0.37162,-0.37162,-0.37162,-0.37162]}, + {"t":0.70197, "x":6.08258, "y":0.59096, "heading":1.5708, "vx":2.99922, "vy":-0.02939, "omega":0.0, "ax":-0.00022, "ay":-0.02195, "alpha":0.0, "fx":[-0.00372,-0.00372,-0.00372,-0.00372], "fy":[-0.37332,-0.37332,-0.37332,-0.37332]}, + {"t":0.74877, "x":6.22293, "y":0.58956, "heading":1.5708, "vx":2.99921, "vy":-0.03041, "omega":0.0, "ax":-0.00023, "ay":-0.022, "alpha":0.0, "fx":[-0.00386,-0.00386,-0.00386,-0.00386], "fy":[-0.37419,-0.37419,-0.37419,-0.37419]}, + {"t":0.79556, "x":6.36329, "y":0.58811, "heading":1.5708, "vx":2.9992, "vy":-0.03144, "omega":0.0, "ax":-0.00023, "ay":-0.02122, "alpha":0.0, "fx":[-0.00384,-0.00384,-0.00384,-0.00384], "fy":[-0.36088,-0.36088,-0.36088,-0.36088]}, + {"t":0.84236, "x":6.50365, "y":0.58662, "heading":1.5708, "vx":2.99918, "vy":-0.03244, "omega":0.0, "ax":-0.00008, "ay":-0.00741, "alpha":0.0, "fx":[-0.00137,-0.00137,-0.00137,-0.00137], "fy":[-0.12601,-0.12601,-0.12601,-0.12601]}, + {"t":0.88916, "x":6.644, "y":0.58509, "heading":1.5708, "vx":2.99918, "vy":-0.03278, "omega":0.0, "ax":0.00194, "ay":0.20892, "alpha":0.0, "fx":[0.03303,0.03303,0.03303,0.03303], "fy":[3.55373,3.55373,3.55373,3.55373]}, + {"t":0.93596, "x":6.78436, "y":0.58379, "heading":1.5708, "vx":2.99927, "vy":-0.02301, "omega":0.0, "ax":-0.05514, "ay":3.19297, "alpha":0.0, "fx":[-0.93799,-0.93799,-0.93799,-0.93799], "fy":[54.31147,54.31147,54.31147,54.31147]}, + {"t":0.98275, "x":6.92466, "y":0.58621, "heading":1.5708, "vx":2.99669, "vy":0.12642, "omega":0.0, "ax":-0.98673, "ay":8.81506, "alpha":0.0, "fx":[-16.78395,-16.78395,-16.78395,-16.78395], "fy":[149.94157,149.94157,149.94157,149.94157]}, + {"t":1.02955, "x":7.06382, "y":0.60178, "heading":1.5708, "vx":2.95051, "vy":0.53894, "omega":0.0, "ax":-2.41154, "ay":9.23765, "alpha":0.0, "fx":[-41.01952,-41.01952,-41.01952,-41.01952], "fy":[157.12981,157.12981,157.12981,157.12981]}, + {"t":1.07635, "x":7.19925, "y":0.63711, "heading":1.5708, "vx":2.83766, "vy":0.97125, "omega":0.0, "ax":-3.81516, "ay":8.88585, "alpha":0.0, "fx":[-64.89478,-64.89478,-64.89478,-64.89478], "fy":[151.14583,151.14583,151.14583,151.14583]}, + {"t":1.12315, "x":7.32787, "y":0.6923, "heading":1.5708, "vx":2.65912, "vy":1.38708, "omega":0.0, "ax":-5.13673, "ay":8.24187, "alpha":0.0, "fx":[-87.37424,-87.37424,-87.37424,-87.37424], "fy":[140.19186,140.19186,140.19186,140.19186]}, + {"t":1.16995, "x":7.44669, "y":0.76623, "heading":1.5708, "vx":2.41873, "vy":1.77279, "omega":0.0, "ax":-6.51104, "ay":7.23067, "alpha":0.0, "fx":[-110.751,-110.751,-110.751,-110.751], "fy":[122.99159,122.99159,122.99159,122.99159]}, + {"t":1.21674, "x":7.55275, "y":0.85711, "heading":1.5708, "vx":2.11403, "vy":2.11117, "omega":0.0, "ax":-8.27465, "ay":5.13891, "alpha":0.0, "fx":[-140.7494,-140.7494,-140.7494,-140.7494], "fy":[87.41144,87.41144,87.41144,87.41144]}, + {"t":1.26354, "x":7.64262, "y":0.96154, "heading":1.5708, "vx":1.72679, "vy":2.35166, "omega":0.0, "ax":-8.96407, "ay":3.8262, "alpha":0.0, "fx":[-152.47628,-152.47628,-152.47628,-152.47628], "fy":[65.08257,65.08257,65.08257,65.08257]}, + {"t":1.31034, "x":7.71362, "y":1.07578, "heading":1.5708, "vx":1.30729, "vy":2.53071, "omega":0.0, "ax":-9.26781, "ay":3.02852, "alpha":0.0, "fx":[-157.64283,-157.64283,-157.64283,-157.64283], "fy":[51.5142,51.5142,51.5142,51.5142]}, + {"t":1.35714, "x":7.76465, "y":1.19753, "heading":1.5708, "vx":0.87358, "vy":2.67244, "omega":0.0, "ax":-9.42477, "ay":2.5067, "alpha":0.0, "fx":[-160.31267,-160.31267,-160.31267,-160.31267], "fy":[42.63818,42.63818,42.63818,42.63818]}, + {"t":1.40394, "x":7.79521, "y":1.32534, "heading":1.5708, "vx":0.43252, "vy":2.78975, "omega":0.0, "ax":-9.26749, "ay":3.004, "alpha":0.0, "fx":[-157.63742,-157.63742,-157.63742,-157.63742], "fy":[51.09726,51.09726,51.09726,51.09726]}, + {"t":1.44601, "x":7.8052, "y":1.44538, "heading":1.5708, "vx":0.04259, "vy":2.91614, "omega":0.0, "ax":-3.01713, "ay":1.94817, "alpha":0.0, "fx":[-51.32047,-51.32047,-51.32047,-51.32047], "fy":[33.13778,33.13778,33.13778,33.13778]}, + {"t":1.48808, "x":7.80432, "y":1.5698, "heading":1.5708, "vx":-0.08435, "vy":2.99811, "omega":0.0, "ax":0.87649, "ay":0.01931, "alpha":0.0, "fx":[14.90881,14.90881,14.90881,14.90881], "fy":[0.3285,0.3285,0.3285,0.3285]}, + {"t":1.53016, "x":7.80155, "y":1.69596, "heading":1.5708, "vx":-0.04747, "vy":2.99892, "omega":0.0, "ax":0.38228, "ay":0.00502, "alpha":0.0, "fx":[6.5025,6.5025,6.5025,6.5025], "fy":[0.08546,0.08546,0.08546,0.08546]}, + {"t":1.57223, "x":7.79989, "y":1.82214, "heading":1.5708, "vx":-0.03139, "vy":2.99913, "omega":0.0, "ax":0.2206, "ay":0.00197, "alpha":0.0, "fx":[3.75241,3.75241,3.75241,3.75241], "fy":[0.03344,0.03344,0.03344,0.03344]}, + {"t":1.61431, "x":7.79877, "y":1.94833, "heading":1.5708, "vx":-0.02211, "vy":2.99922, "omega":0.0, "ax":0.15127, "ay":0.00095, "alpha":0.0, "fx":[2.57305,2.57305,2.57305,2.57305], "fy":[0.01622,0.01622,0.01622,0.01622]}, + {"t":1.65638, "x":7.79797, "y":2.07452, "heading":1.5708, "vx":-0.01574, "vy":2.99926, "omega":0.0, "ax":0.11267, "ay":0.0005, "alpha":0.0, "fx":[1.91641,1.91641,1.91641,1.91641], "fy":[0.00853,0.00853,0.00853,0.00853]}, + {"t":1.69846, "x":7.79741, "y":2.20071, "heading":1.5708, "vx":-0.011, "vy":2.99928, "omega":0.0, "ax":0.08874, "ay":0.00027, "alpha":0.0, "fx":[1.50943,1.50943,1.50943,1.50943], "fy":[0.00458,0.00458,0.00458,0.00458]}, + {"t":1.74053, "x":7.79702, "y":2.32691, "heading":1.5708, "vx":-0.00727, "vy":2.99929, "omega":0.0, "ax":0.07388, "ay":0.00014, "alpha":0.0, "fx":[1.25663,1.25663,1.25663,1.25663], "fy":[0.00238,0.00238,0.00238,0.00238]}, + {"t":1.78261, "x":7.79678, "y":2.4531, "heading":1.5708, "vx":-0.00416, "vy":2.9993, "omega":0.0, "ax":0.06563, "ay":0.00006, "alpha":0.0, "fx":[1.11643,1.11643,1.11643,1.11643], "fy":[0.00102,0.00102,0.00102,0.00102]}, + {"t":1.82468, "x":7.79667, "y":2.57929, "heading":1.5708, "vx":-0.0014, "vy":2.9993, "omega":0.0, "ax":0.0629, "ay":0.0, "alpha":0.0, "fx":[1.06999,1.06999,1.06999,1.06999], "fy":[0.00002,0.00002,0.00002,0.00002]}, + {"t":1.86676, "x":7.79666, "y":2.70549, "heading":1.5708, "vx":0.00125, "vy":2.9993, "omega":0.0, "ax":0.06535, "ay":-0.00006, "alpha":0.0, "fx":[1.11163,1.11163,1.11163,1.11163], "fy":[-0.00098,-0.00098,-0.00098,-0.00098]}, + {"t":1.90883, "x":7.79677, "y":2.83168, "heading":1.5708, "vx":0.004, "vy":2.9993, "omega":0.0, "ax":0.07328, "ay":-0.00014, "alpha":0.0, "fx":[1.24653,1.24653,1.24653,1.24653], "fy":[-0.00231,-0.00231,-0.00231,-0.00231]}, + {"t":1.9509, "x":7.79701, "y":2.95788, "heading":1.5708, "vx":0.00708, "vy":2.99929, "omega":0.0, "ax":0.08781, "ay":-0.00026, "alpha":0.0, "fx":[1.49358,1.49358,1.49358,1.49358], "fy":[-0.00445,-0.00445,-0.00445,-0.00445]}, + {"t":1.99298, "x":7.79738, "y":3.08407, "heading":1.5708, "vx":0.01078, "vy":2.99928, "omega":0.0, "ax":0.11187, "ay":-0.00049, "alpha":0.0, "fx":[1.90292,1.90292,1.90292,1.90292], "fy":[-0.00833,-0.00833,-0.00833,-0.00833]}, + {"t":2.03505, "x":7.79793, "y":3.21026, "heading":1.5708, "vx":0.01548, "vy":2.99926, "omega":0.0, "ax":0.15781, "ay":-0.00099, "alpha":0.0, "fx":[2.68438,2.68438,2.68438,2.68438], "fy":[-0.01683,-0.01683,-0.01683,-0.01683]}, + {"t":2.07713, "x":7.79873, "y":3.33645, "heading":1.5708, "vx":0.02212, "vy":2.99922, "omega":0.0, "ax":0.32125, "ay":-0.00545, "alpha":0.0, "fx":[5.46437,5.46437,5.46437,5.46437], "fy":[-0.09263,-0.09263,-0.09263,-0.09263]}, + {"t":2.1192, "x":7.79994, "y":3.46264, "heading":1.5708, "vx":0.03564, "vy":2.99899, "omega":0.0, "ax":0.05531, "ay":-9.23843, "alpha":0.0, "fx":[0.94075,0.94075,0.94075,0.94075], "fy":[-157.14308,-157.14308,-157.14308,-157.14308]}, + {"t":2.16128, "x":7.80149, "y":3.58064, "heading":1.5708, "vx":0.03797, "vy":2.61029, "omega":0.0, "ax":-0.12294, "ay":-9.70283, "alpha":0.0, "fx":[-2.09121,-2.09121,-2.09121,-2.09121], "fy":[-165.04235,-165.04235,-165.04235,-165.04235]}, + {"t":2.20335, "x":7.80298, "y":3.68188, "heading":1.5708, "vx":0.03279, "vy":2.20204, "omega":0.0, "ax":-0.17444, "ay":-9.72991, "alpha":0.0, "fx":[-2.96726,-2.96726,-2.96726,-2.96726], "fy":[-165.50292,-165.50292,-165.50292,-165.50292]}, + {"t":2.24543, "x":7.8042, "y":3.76592, "heading":1.5708, "vx":0.02545, "vy":1.79266, "omega":0.0, "ax":-0.25446, "ay":-9.73819, "alpha":0.0, "fx":[-4.3283,-4.3283,-4.3283,-4.3283], "fy":[-165.64377,-165.64377,-165.64377,-165.64377]}, + {"t":2.2875, "x":7.80505, "y":3.83273, "heading":1.5708, "vx":0.01475, "vy":1.38293, "omega":0.0, "ax":-0.5469, "ay":-9.73135, "alpha":0.0, "fx":[-9.30265,-9.30265,-9.30265,-9.30265], "fy":[-165.5274,-165.5274,-165.5274,-165.5274]}, + {"t":2.32958, "x":7.80519, "y":3.8823, "heading":1.5708, "vx":-0.00826, "vy":0.97349, "omega":0.0, "ax":-2.15269, "ay":-9.50954, "alpha":0.0, "fx":[-36.61662,-36.61662,-36.61662,-36.61662], "fy":[-161.75456,-161.75456,-161.75456,-161.75456]}, + {"t":2.37165, "x":7.80293, "y":3.91484, "heading":1.5708, "vx":-0.09884, "vy":0.57338, "omega":0.0, "ax":-4.02989, "ay":-8.88141, "alpha":0.0, "fx":[-68.54735,-68.54735,-68.54735,-68.54735], "fy":[-151.0703,-151.0703,-151.0703,-151.0703]}, + {"t":2.41372, "x":7.79521, "y":3.9311, "heading":1.5708, "vx":-0.26839, "vy":0.1997, "omega":0.0, "ax":-4.64597, "ay":-8.57627, "alpha":0.0, "fx":[-79.02665,-79.02665,-79.02665,-79.02665], "fy":[-145.87993,-145.87993,-145.87993,-145.87993]}, + {"t":2.45815, "x":7.7787, "y":3.93151, "heading":1.5708, "vx":-0.47479, "vy":-0.1813, "omega":0.0, "ax":-4.63899, "ay":-8.5783, "alpha":0.0, "fx":[-78.90788,-78.90788,-78.90788,-78.90788], "fy":[-145.91439,-145.91439,-145.91439,-145.91439]}, + {"t":2.50257, "x":7.75303, "y":3.91499, "heading":1.5708, "vx":-0.68087, "vy":-0.56238, "omega":0.0, "ax":-4.62912, "ay":-8.58115, "alpha":0.0, "fx":[-78.74003,-78.74003,-78.74003,-78.74003], "fy":[-145.96289,-145.96289,-145.96289,-145.96289]}, + {"t":2.547, "x":7.71821, "y":3.88154, "heading":1.5708, "vx":-0.88652, "vy":-0.9436, "omega":0.0, "ax":-4.61412, "ay":-8.58546, "alpha":0.0, "fx":[-78.48489,-78.48489,-78.48489,-78.48489], "fy":[-146.03619,-146.03619,-146.03619,-146.03619]}, + {"t":2.59142, "x":7.67428, "y":3.83115, "heading":1.5708, "vx":-1.0915, "vy":-1.325, "omega":0.0, "ax":-4.58858, "ay":-8.59272, "alpha":0.0, "fx":[-78.05037,-78.05037,-78.05037,-78.05037], "fy":[-146.15978,-146.15978,-146.15978,-146.15978]}, + {"t":2.63585, "x":7.62126, "y":3.76381, "heading":1.5708, "vx":-1.29534, "vy":-1.70673, "omega":0.0, "ax":-4.5353, "ay":-8.6076, "alpha":0.0, "fx":[-77.14417,-77.14417,-77.14417,-77.14417], "fy":[-146.41281,-146.41281,-146.41281,-146.41281]}, + {"t":2.68027, "x":7.55924, "y":3.6795, "heading":1.5708, "vx":-1.49682, "vy":-2.08912, "omega":0.0, "ax":-4.35514, "ay":-8.65532, "alpha":0.0, "fx":[-74.07975,-74.07975,-74.07975,-74.07975], "fy":[-147.22451,-147.22451,-147.22451,-147.22451]}, + {"t":2.7247, "x":7.48845, "y":3.57815, "heading":1.5708, "vx":-1.6903, "vy":-2.47362, "omega":0.0, "ax":4.97585, "ay":-3.17804, "alpha":0.0, "fx":[84.63787,84.63787,84.63787,84.63787], "fy":[-54.05759,-54.05759,-54.05759,-54.05759]}, + {"t":2.76912, "x":7.41827, "y":3.46512, "heading":1.5708, "vx":-1.46925, "vy":-2.61481, "omega":0.0, "ax":0.60481, "ay":-0.33579, "alpha":0.0, "fx":[10.28766,10.28766,10.28766,10.28766], "fy":[-5.71166,-5.71166,-5.71166,-5.71166]}, + {"t":2.81354, "x":7.35359, "y":3.34863, "heading":1.5708, "vx":-1.44238, "vy":-2.62972, "omega":0.0, "ax":0.04559, "ay":-0.02498, "alpha":0.0, "fx":[0.77543,0.77543,0.77543,0.77543], "fy":[-0.42493,-0.42493,-0.42493,-0.42493]}, + {"t":2.85797, "x":7.28956, "y":3.23178, "heading":1.5708, "vx":-1.44035, "vy":-2.63083, "omega":0.0, "ax":0.00363, "ay":-0.00199, "alpha":0.0, "fx":[0.06175,0.06175,0.06175,0.06175], "fy":[-0.03381,-0.03381,-0.03381,-0.03381]}, + {"t":2.90239, "x":7.22558, "y":3.1149, "heading":1.5708, "vx":-1.44019, "vy":-2.63092, "omega":0.0, "ax":0.00056, "ay":-0.00031, "alpha":0.0, "fx":[0.00952,0.00952,0.00952,0.00952], "fy":[-0.00522,-0.00522,-0.00522,-0.00522]}, + {"t":2.94682, "x":7.1616, "y":2.99803, "heading":1.5708, "vx":-1.44017, "vy":-2.63094, "omega":0.0, "ax":0.00039, "ay":-0.00021, "alpha":0.0, "fx":[0.00661,0.00661,0.00661,0.00661], "fy":[-0.00363,-0.00363,-0.00363,-0.00363]}, + {"t":2.99124, "x":7.09762, "y":2.88115, "heading":1.5708, "vx":-1.44015, "vy":-2.63095, "omega":0.0, "ax":0.00044, "ay":-0.00024, "alpha":0.0, "fx":[0.00744,0.00744,0.00744,0.00744], "fy":[-0.00408,-0.00408,-0.00408,-0.00408]}, + {"t":3.03567, "x":7.03364, "y":2.76427, "heading":1.5708, "vx":-1.44013, "vy":-2.63096, "omega":0.0, "ax":0.00051, "ay":-0.00028, "alpha":0.0, "fx":[0.00863,0.00863,0.00863,0.00863], "fy":[-0.00473,-0.00473,-0.00473,-0.00473]}, + {"t":3.08009, "x":6.96967, "y":2.64739, "heading":1.5708, "vx":-1.44011, "vy":-2.63097, "omega":0.0, "ax":0.00058, "ay":-0.00032, "alpha":0.0, "fx":[0.00992,0.00992,0.00992,0.00992], "fy":[-0.00544,-0.00544,-0.00544,-0.00544]}, + {"t":3.12452, "x":6.90569, "y":2.53051, "heading":1.5708, "vx":-1.44008, "vy":-2.63098, "omega":0.0, "ax":0.00067, "ay":-0.00037, "alpha":0.0, "fx":[0.01133,0.01133,0.01133,0.01133], "fy":[-0.00621,-0.00621,-0.00621,-0.00621]}, + {"t":3.16894, "x":6.84172, "y":2.41363, "heading":1.5708, "vx":-1.44005, "vy":-2.631, "omega":0.0, "ax":0.00076, "ay":-0.00042, "alpha":0.0, "fx":[0.01288,0.01288,0.01288,0.01288], "fy":[-0.00706,-0.00706,-0.00706,-0.00706]}, + {"t":3.21337, "x":6.77774, "y":2.29675, "heading":1.5708, "vx":-1.44002, "vy":-2.63102, "omega":0.0, "ax":0.00086, "ay":-0.00047, "alpha":0.0, "fx":[0.01458,0.01458,0.01458,0.01458], "fy":[-0.00799,-0.00799,-0.00799,-0.00799]}, + {"t":3.25779, "x":6.71377, "y":2.17987, "heading":1.5708, "vx":-1.43998, "vy":-2.63104, "omega":0.0, "ax":0.00097, "ay":-0.00053, "alpha":0.0, "fx":[0.01647,0.01647,0.01647,0.01647], "fy":[-0.00903,-0.00903,-0.00903,-0.00903]}, + {"t":3.30221, "x":6.6498, "y":2.06298, "heading":1.5708, "vx":-1.43994, "vy":-2.63106, "omega":0.0, "ax":0.00109, "ay":-0.0006, "alpha":0.0, "fx":[0.0186,0.0186,0.0186,0.0186], "fy":[-0.01019,-0.01019,-0.01019,-0.01019]}, + {"t":3.34664, "x":6.58584, "y":1.9461, "heading":1.5708, "vx":-1.43989, "vy":-2.63109, "omega":0.0, "ax":0.00123, "ay":-0.00068, "alpha":0.0, "fx":[0.021,0.021,0.021,0.021], "fy":[-0.01151,-0.01151,-0.01151,-0.01151]}, + {"t":3.39106, "x":6.52187, "y":1.82922, "heading":1.5708, "vx":-1.43983, "vy":-2.63112, "omega":0.0, "ax":0.0014, "ay":-0.00076, "alpha":0.0, "fx":[0.02374,0.02374,0.02374,0.02374], "fy":[-0.01301,-0.01301,-0.01301,-0.01301]}, + {"t":3.43549, "x":6.45791, "y":1.71233, "heading":1.5708, "vx":-1.43977, "vy":-2.63115, "omega":0.0, "ax":0.00158, "ay":-0.00087, "alpha":0.0, "fx":[0.02689,0.02689,0.02689,0.02689], "fy":[-0.01473,-0.01473,-0.01473,-0.01473]}, + {"t":3.47991, "x":6.39395, "y":1.59544, "heading":1.5708, "vx":-1.4397, "vy":-2.63119, "omega":0.0, "ax":0.00178, "ay":-0.00098, "alpha":0.0, "fx":[0.03029,0.03029,0.03029,0.03029], "fy":[-0.01659,-0.01659,-0.01659,-0.01659]}, + {"t":3.52434, "x":6.32999, "y":1.47855, "heading":1.5708, "vx":-1.43962, "vy":-2.63123, "omega":0.0, "ax":0.00178, "ay":-0.00097, "alpha":0.0, "fx":[0.03026,0.03026,0.03026,0.03026], "fy":[-0.01658,-0.01658,-0.01658,-0.01658]}, + {"t":3.56876, "x":6.26604, "y":1.36166, "heading":1.5708, "vx":-1.43954, "vy":-2.63128, "omega":0.0, "ax":-0.00143, "ay":0.00078, "alpha":0.0, "fx":[-0.02433,-0.02433,-0.02433,-0.02433], "fy":[0.01328,0.01328,0.01328,0.01328]}, + {"t":3.61319, "x":6.20209, "y":1.24476, "heading":1.5708, "vx":-1.43961, "vy":-2.63124, "omega":0.0, "ax":-0.04841, "ay":0.02651, "alpha":0.0, "fx":[-0.82352,-0.82352,-0.82352,-0.82352], "fy":[0.45097,0.45097,0.45097,0.45097]}, + {"t":3.65761, "x":6.13809, "y":1.1279, "heading":1.5708, "vx":-1.44176, "vy":-2.63007, "omega":0.0, "ax":-0.67803, "ay":0.37678, "alpha":0.0, "fx":[-11.53308,-11.53308,-11.53308,-11.53308], "fy":[6.40885,6.40885,6.40885,6.40885]}, + {"t":3.70203, "x":6.07337, "y":1.01143, "heading":1.5708, "vx":-1.47188, "vy":-2.61333, "omega":0.0, "ax":-5.27758, "ay":3.3021, "alpha":0.0, "fx":[-89.77013,-89.77013,-89.77013,-89.77013], "fy":[56.16769,56.16769,56.16769,56.16769]}, + {"t":3.74646, "x":6.00277, "y":0.89859, "heading":1.5708, "vx":-1.70633, "vy":-2.46663, "omega":0.0, "ax":-7.23475, "ay":5.77767, "alpha":0.0, "fx":[-123.06101,-123.06101,-123.06101,-123.06101], "fy":[98.27657,98.27657,98.27657,98.27657]}, + {"t":3.79088, "x":5.91983, "y":0.79472, "heading":1.5708, "vx":-2.02773, "vy":-2.20996, "omega":0.0, "ax":-6.58885, "ay":6.97295, "alpha":0.0, "fx":[-112.07453,-112.07453,-112.07453,-112.07453], "fy":[118.60791,118.60791,118.60791,118.60791]}, + {"t":3.83531, "x":5.82325, "y":0.70342, "heading":1.5708, "vx":-2.32044, "vy":-1.90019, "omega":0.0, "ax":-5.55856, "ay":7.92471, "alpha":0.0, "fx":[-94.5495,-94.5495,-94.5495,-94.5495], "fy":[134.79711,134.79711,134.79711,134.79711]}, + {"t":3.87973, "x":5.71468, "y":0.62683, "heading":1.5708, "vx":-2.56737, "vy":-1.54814, "omega":0.0, "ax":-4.52692, "ay":8.54097, "alpha":0.0, "fx":[-77.00153,-77.00153,-77.00153,-77.00153], "fy":[145.27941,145.27941,145.27941,145.27941]}, + {"t":3.9157, "x":5.6194, "y":0.57666, "heading":1.5708, "vx":-2.73022, "vy":-1.24091, "omega":0.0, "ax":-3.46436, "ay":8.9513, "alpha":0.0, "fx":[-58.92774,-58.92774,-58.92774,-58.92774], "fy":[152.25908,152.25908,152.25908,152.25908]}, + {"t":3.95168, "x":5.51895, "y":0.53782, "heading":1.5708, "vx":-2.85483, "vy":-0.91892, "omega":0.0, "ax":-2.37724, "ay":9.11896, "alpha":0.0, "fx":[-40.43619,-40.43619,-40.43619,-40.43619], "fy":[155.11082,155.11082,155.11082,155.11082]}, + {"t":3.98765, "x":5.41472, "y":0.51066, "heading":1.5708, "vx":-2.94035, "vy":-0.59089, "omega":0.0, "ax":-1.27436, "ay":8.68125, "alpha":0.0, "fx":[-21.67651,-21.67651,-21.67651,-21.67651], "fy":[147.66565,147.66565,147.66565,147.66565]}, + {"t":4.02362, "x":5.30812, "y":0.49502, "heading":1.5708, "vx":-2.98619, "vy":-0.27861, "omega":0.0, "ax":-0.32057, "ay":5.15843, "alpha":0.0, "fx":[-5.45287,-5.45287,-5.45287,-5.45287], "fy":[87.74335,87.74335,87.74335,87.74335]}, + {"t":4.05959, "x":5.2005, "y":0.48834, "heading":1.5708, "vx":-2.99772, "vy":-0.09306, "omega":0.0, "ax":-0.02209, "ay":0.85128, "alpha":0.0, "fx":[-0.3757,-0.3757,-0.3757,-0.3757], "fy":[14.48003,14.48003,14.48003,14.48003]}, + {"t":4.09556, "x":5.09265, "y":0.48554, "heading":1.5708, "vx":-2.99851, "vy":-0.06244, "omega":0.0, "ax":-0.00135, "ay":0.06597, "alpha":0.0, "fx":[-0.02294,-0.02294,-0.02294,-0.02294], "fy":[1.12219,1.12219,1.12219,1.12219]}, + {"t":4.13153, "x":4.98479, "y":0.48334, "heading":1.5708, "vx":-2.99856, "vy":-0.06006, "omega":0.0, "ax":0.00067, "ay":-0.03315, "alpha":0.0, "fx":[0.0114,0.0114,0.0114,0.0114], "fy":[-0.56391,-0.56391,-0.56391,-0.56391]}, + {"t":4.16751, "x":4.87692, "y":0.48116, "heading":1.5708, "vx":-2.99854, "vy":-0.06125, "omega":0.0, "ax":0.00095, "ay":-0.04608, "alpha":0.0, "fx":[0.01622,0.01622,0.01622,0.01622], "fy":[-0.78382,-0.78382,-0.78382,-0.78382]}, + {"t":4.20348, "x":4.76906, "y":0.47892, "heading":1.5708, "vx":-2.9985, "vy":-0.06291, "omega":0.0, "ax":0.00103, "ay":-0.04832, "alpha":0.0, "fx":[0.01747,0.01747,0.01747,0.01747], "fy":[-0.82186,-0.82186,-0.82186,-0.82186]}, + {"t":4.23945, "x":4.6612, "y":0.47663, "heading":1.5708, "vx":-2.99847, "vy":-0.06465, "omega":0.0, "ax":0.00108, "ay":-0.04926, "alpha":0.0, "fx":[0.0183,0.0183,0.0183,0.0183], "fy":[-0.83791,-0.83791,-0.83791,-0.83791]}, + {"t":4.27542, "x":4.55334, "y":0.47427, "heading":1.5708, "vx":-2.99843, "vy":-0.06642, "omega":0.0, "ax":0.00112, "ay":-0.05007, "alpha":0.0, "fx":[0.01911,0.01911,0.01911,0.01911], "fy":[-0.85167,-0.85167,-0.85167,-0.85167]}, + {"t":4.31139, "x":4.44549, "y":0.47185, "heading":1.5708, "vx":-2.99839, "vy":-0.06822, "omega":0.0, "ax":0.00117, "ay":-0.05077, "alpha":0.0, "fx":[0.0199,0.0199,0.0199,0.0199], "fy":[-0.86357,-0.86357,-0.86356,-0.86356]}, + {"t":4.34736, "x":4.33763, "y":0.46936, "heading":1.5708, "vx":-2.99835, "vy":-0.07005, "omega":0.0, "ax":0.00119, "ay":-0.05038, "alpha":0.0, "fx":[0.02027,0.02027,0.02027,0.02027], "fy":[-0.85701,-0.85701,-0.85701,-0.85701]}, + {"t":4.38334, "x":4.22977, "y":0.46681, "heading":1.5708, "vx":-2.9983, "vy":-0.07186, "omega":0.0, "ax":0.00099, "ay":-0.0409, "alpha":0.0, "fx":[0.01683,0.01683,0.01683,0.01683], "fy":[-0.69564,-0.69564,-0.69563,-0.69563]}, + {"t":4.41931, "x":4.12192, "y":0.4642, "heading":1.5708, "vx":-2.99827, "vy":-0.07333, "omega":0.0, "ax":-0.00102, "ay":0.04217, "alpha":0.0, "fx":[-0.01738,-0.01738,-0.01738,-0.01738], "fy":[0.71738,0.71738,0.71738,0.71738]}, + {"t":4.45528, "x":4.01407, "y":0.46159, "heading":1.5708, "vx":-2.9983, "vy":-0.07182, "omega":0.0, "ax":-0.01402, "ay":0.71333, "alpha":0.0, "fx":[-0.23855,-0.23854,-0.23854,-0.23855], "fy":[12.13346,12.13346,12.13346,12.13346]}, + {"t":4.49125, "x":3.9062, "y":0.45947, "heading":1.5708, "vx":-2.99881, "vy":-0.04616, "omega":0.0, "ax":0.06053, "ay":4.70506, "alpha":0.0, "fx":[1.02952,1.02953,1.02953,1.02952], "fy":[80.0317,80.0317,80.0317,80.0317]}, + {"t":4.52722, "x":3.79837, "y":0.46085, "heading":1.5708, "vx":-2.99663, "vy":0.12309, "omega":0.0, "ax":0.80597, "ay":8.62951, "alpha":0.0, "fx":[13.70933,13.70934,13.70934,13.70933], "fy":[146.78542,146.78542,146.78542,146.78542]}, + {"t":4.56319, "x":3.6911, "y":0.47086, "heading":1.5708, "vx":-2.96764, "vy":0.43351, "omega":0.0, "ax":1.88244, "ay":9.21035, "alpha":0.0, "fx":[32.01969,32.01969,32.01969,32.01969], "fy":[156.6655,156.6655,156.6655,156.6655]}, + {"t":4.59917, "x":3.58557, "y":0.49241, "heading":1.5708, "vx":-2.89993, "vy":0.76482, "omega":0.0, "ax":2.97736, "ay":9.1167, "alpha":0.0, "fx":[50.64408,50.64408,50.64408,50.64408], "fy":[155.07246,155.07246,155.07246,155.07246]}, + {"t":4.63514, "x":3.48318, "y":0.52582, "heading":1.5708, "vx":-2.79282, "vy":1.09276, "omega":0.0, "ax":4.0403, "ay":8.77786, "alpha":0.0, "fx":[68.72441,68.72441,68.72441,68.72441], "fy":[149.30887,149.30887,149.30887,149.30887]}, + {"t":4.67111, "x":3.38533, "y":0.57081, "heading":1.5708, "vx":-2.64749, "vy":1.40852, "omega":0.0, "ax":5.07137, "ay":8.26608, "alpha":0.0, "fx":[86.26256,86.26256,86.26256,86.26256], "fy":[140.60364,140.60364,140.60364,140.60364]}, + {"t":4.70708, "x":3.29338, "y":0.62683, "heading":1.5708, "vx":-2.46506, "vy":1.70586, "omega":0.0, "ax":6.06839, "ay":7.57754, "alpha":0.0, "fx":[103.22162,103.22162,103.22162,103.22162], "fy":[128.89179,128.89179,128.89179,128.89179]}, + {"t":4.75217, "x":3.18839, "y":0.71145, "heading":1.5708, "vx":-2.19143, "vy":2.04755, "omega":0.0, "ax":7.0923, "ay":6.56593, "alpha":0.0, "fx":[120.63791,120.63791,120.63791,120.63791], "fy":[111.68467,111.68467,111.68467,111.68467]}, + {"t":4.79726, "x":3.09679, "y":0.81045, "heading":1.5708, "vx":-1.87163, "vy":2.34361, "omega":0.0, "ax":7.86264, "ay":5.40508, "alpha":0.0, "fx":[133.74123,133.74123,133.74123,133.74123], "fy":[91.93886,91.93886,91.93886,91.93886]}, + {"t":4.84235, "x":3.02039, "y":0.92162, "heading":1.5708, "vx":-1.51709, "vy":2.58734, "omega":0.0, "ax":7.97984, "ay":3.98627, "alpha":0.0, "fx":[135.73473,135.73473,135.73473,135.73473], "fy":[67.80529,67.80529,67.80529,67.80529]}, + {"t":4.88745, "x":2.96009, "y":1.04234, "heading":1.5708, "vx":-1.15726, "vy":2.76709, "omega":0.0, "ax":3.45774, "ay":1.33429, "alpha":0.0, "fx":[58.81516,58.81517,58.81517,58.81516], "fy":[22.6959,22.6959,22.6959,22.6959]}, + {"t":4.93254, "x":2.91142, "y":1.16847, "heading":1.5708, "vx":-1.00135, "vy":2.82725, "omega":0.0, "ax":0.28321, "ay":0.09959, "alpha":0.0, "fx":[4.8174,4.81741,4.81741,4.8174], "fy":[1.69401,1.69401,1.69401,1.69401]}, + {"t":4.97763, "x":2.86656, "y":1.29606, "heading":1.5708, "vx":-0.98858, "vy":2.83174, "omega":0.0, "ax":0.01586, "ay":0.00553, "alpha":0.0, "fx":[0.26981,0.26981,0.26981,0.26981], "fy":[0.09413,0.09413,0.09414,0.09414]}, + {"t":5.02272, "x":2.822, "y":1.42375, "heading":1.5708, "vx":-0.98786, "vy":2.83199, "omega":0.0, "ax":-0.00284, "ay":-0.00099, "alpha":0.0, "fx":[-0.04828,-0.04828,-0.04828,-0.04828], "fy":[-0.01686,-0.01686,-0.01686,-0.01686]}, + {"t":5.06781, "x":2.77745, "y":1.55145, "heading":1.5708, "vx":-0.98799, "vy":2.83195, "omega":0.0, "ax":-0.00403, "ay":-0.00141, "alpha":0.0, "fx":[-0.06853,-0.06852,-0.06852,-0.06853], "fy":[-0.02392,-0.02392,-0.02392,-0.02392]}, + {"t":5.1129, "x":2.7329, "y":1.67915, "heading":1.5708, "vx":-0.98817, "vy":2.83188, "omega":0.0, "ax":-0.00401, "ay":-0.0014, "alpha":0.0, "fx":[-0.06829,-0.06829,-0.06829,-0.06829], "fy":[-0.02384,-0.02384,-0.02384,-0.02384]}, + {"t":5.158, "x":2.68833, "y":1.80684, "heading":1.5708, "vx":-0.98835, "vy":2.83182, "omega":0.0, "ax":-0.00394, "ay":-0.00138, "alpha":0.0, "fx":[-0.067,-0.067,-0.067,-0.067], "fy":[-0.0234,-0.0234,-0.0234,-0.0234]}, + {"t":5.20309, "x":2.64376, "y":1.93453, "heading":1.5708, "vx":-0.98853, "vy":2.83176, "omega":0.0, "ax":-0.00388, "ay":-0.00135, "alpha":0.0, "fx":[-0.06594,-0.06594,-0.06594,-0.06594], "fy":[-0.02303,-0.02303,-0.02303,-0.02303]}, + {"t":5.24818, "x":2.59918, "y":2.06222, "heading":1.5708, "vx":-0.98871, "vy":2.8317, "omega":0.0, "ax":-0.00383, "ay":-0.00134, "alpha":0.0, "fx":[-0.06516,-0.06516,-0.06516,-0.06516], "fy":[-0.02276,-0.02276,-0.02276,-0.02276]}, + {"t":5.29327, "x":2.5546, "y":2.1899, "heading":1.5708, "vx":-0.98888, "vy":2.83164, "omega":0.0, "ax":-0.0038, "ay":-0.00133, "alpha":0.0, "fx":[-0.06461,-0.06461,-0.06461,-0.06461], "fy":[-0.02257,-0.02257,-0.02257,-0.02257]}, + {"t":5.33836, "x":2.51, "y":2.31758, "heading":1.5708, "vx":-0.98905, "vy":2.83158, "omega":0.0, "ax":-0.00378, "ay":-0.00132, "alpha":0.0, "fx":[-0.06427,-0.06427,-0.06427,-0.06427], "fy":[-0.02246,-0.02246,-0.02246,-0.02246]}, + {"t":5.38345, "x":2.4654, "y":2.44526, "heading":1.5708, "vx":-0.98922, "vy":2.83152, "omega":0.0, "ax":-0.00377, "ay":-0.00132, "alpha":0.0, "fx":[-0.0641,-0.0641,-0.0641,-0.0641], "fy":[-0.0224,-0.0224,-0.0224,-0.0224]}, + {"t":5.42855, "x":2.42079, "y":2.57294, "heading":1.5708, "vx":-0.98939, "vy":2.83146, "omega":0.0, "ax":-0.00377, "ay":-0.00132, "alpha":0.0, "fx":[-0.06409,-0.06409,-0.06409,-0.06409], "fy":[-0.0224,-0.0224,-0.0224,-0.0224]}, + {"t":5.47364, "x":2.37618, "y":2.70061, "heading":1.5708, "vx":-0.98956, "vy":2.8314, "omega":0.0, "ax":-0.00378, "ay":-0.00132, "alpha":0.0, "fx":[-0.06422,-0.06422,-0.06422,-0.06422], "fy":[-0.02245,-0.02245,-0.02245,-0.02245]}, + {"t":5.51873, "x":2.33155, "y":2.82828, "heading":1.5708, "vx":-0.98973, "vy":2.83134, "omega":0.0, "ax":-0.00379, "ay":-0.00133, "alpha":0.0, "fx":[-0.06448,-0.06449,-0.06449,-0.06448], "fy":[-0.02254,-0.02254,-0.02255,-0.02255]}, + {"t":5.56382, "x":2.28692, "y":2.95595, "heading":1.5708, "vx":-0.9899, "vy":2.83128, "omega":0.0, "ax":-0.00381, "ay":-0.00133, "alpha":0.0, "fx":[-0.06487,-0.06487,-0.06487,-0.06487], "fy":[-0.02268,-0.02268,-0.02268,-0.02268]}, + {"t":5.60891, "x":2.24228, "y":3.08361, "heading":1.5708, "vx":-0.99007, "vy":2.83122, "omega":0.0, "ax":-0.00384, "ay":-0.00134, "alpha":0.0, "fx":[-0.06537,-0.06537,-0.06537,-0.06537], "fy":[-0.02286,-0.02286,-0.02286,-0.02286]}, + {"t":5.654, "x":2.19763, "y":3.21128, "heading":1.5708, "vx":-0.99025, "vy":2.83116, "omega":0.0, "ax":-0.00388, "ay":-0.00136, "alpha":0.0, "fx":[-0.06606,-0.06607,-0.06607,-0.06606], "fy":[-0.0231,-0.0231,-0.02311,-0.02311]}, + {"t":5.6991, "x":2.15298, "y":3.33894, "heading":1.5708, "vx":-0.99042, "vy":2.8311, "omega":0.0, "ax":-0.004, "ay":-0.0014, "alpha":0.0, "fx":[-0.06804,-0.06804,-0.06804,-0.06804], "fy":[-0.0238,-0.0238,-0.02381,-0.02381]}, + {"t":5.74419, "x":2.10831, "y":3.46659, "heading":1.5708, "vx":-0.9906, "vy":2.83103, "omega":0.0, "ax":-0.00494, "ay":-0.00223, "alpha":0.0, "fx":[-0.08409,-0.08409,-0.08409,-0.08409], "fy":[-0.03798,-0.03798,-0.038,-0.038]}, + {"t":5.78928, "x":2.06364, "y":3.59425, "heading":1.5708, "vx":-0.99083, "vy":2.83093, "omega":0.0, "ax":2.66279, "ay":-7.62828, "alpha":0.0, "fx":[45.29323,45.29323,45.29323,45.29323], "fy":[-129.75479,-129.75479,-129.75479,-129.75479]}, + {"t":5.83437, "x":2.02167, "y":3.71414, "heading":1.5708, "vx":-0.87076, "vy":2.48696, "omega":0.0, "ax":3.20661, "ay":-9.16123, "alpha":0.0, "fx":[54.54347,54.54347,54.54347,54.54347], "fy":[-155.82984,-155.82984,-155.82984,-155.82984]}, + {"t":5.87946, "x":1.98566, "y":3.81697, "heading":1.5708, "vx":-0.72617, "vy":2.07387, "omega":0.0, "ax":3.21622, "ay":-9.18737, "alpha":0.0, "fx":[54.70705,54.70705,54.70705,54.70705], "fy":[-156.27453,-156.27453,-156.27453,-156.27453]}, + {"t":5.92455, "x":1.95619, "y":3.90115, "heading":1.5708, "vx":-0.58114, "vy":1.65959, "omega":0.0, "ax":3.2197, "ay":-9.19621, "alpha":0.0, "fx":[54.76619,54.76619,54.76619,54.76619], "fy":[-156.42487,-156.42487,-156.42487,-156.42487]}, + {"t":5.96964, "x":1.93326, "y":3.96663, "heading":1.5708, "vx":-0.43596, "vy":1.24492, "omega":0.0, "ax":3.2216, "ay":-9.20061, "alpha":0.0, "fx":[54.79857,54.79857,54.79857,54.79857], "fy":[-156.49975,-156.49975,-156.49975,-156.49975]}, + {"t":6.01474, "x":1.91688, "y":4.01341, "heading":1.5708, "vx":-0.29069, "vy":0.83005, "omega":0.0, "ax":3.22287, "ay":-9.20322, "alpha":0.0, "fx":[54.82017,54.82017,54.82017,54.82017], "fy":[-156.54415,-156.54415,-156.54415,-156.54415]}, + {"t":6.05983, "x":1.90704, "y":4.04148, "heading":1.5708, "vx":-0.14537, "vy":0.41506, "omega":0.0, "ax":3.22383, "ay":-9.20493, "alpha":0.0, "fx":[54.83635,54.83635,54.83635,54.83635], "fy":[-156.57322,-156.57322,-156.57322,-156.57322]}, + {"t":6.10492, "x":1.90377, "y":4.05084, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0,0.0,0.0,0.0]}], + "splits":[0] + }, + "events":[] +} diff --git a/lib/examples/swerve/src/main/deploy/choreo/V1_DoomSpiralAutos.chor b/lib/examples/swerve/src/main/deploy/choreo/V1_DoomSpiralAutos.chor new file mode 100644 index 00000000..83e3d259 --- /dev/null +++ b/lib/examples/swerve/src/main/deploy/choreo/V1_DoomSpiralAutos.chor @@ -0,0 +1,84 @@ +{ + "name":"V1_DoomSpiralAutos", + "version":2, + "type":"Swerve", + "variables":{ + "expressions":{}, + "poses":{} + }, + "config":{ + "frontLeft":{ + "x":{ + "exp":"8.875 in", + "val":0.225425 + }, + "y":{ + "exp":"13.125 in", + "val":0.333375 + } + }, + "backLeft":{ + "x":{ + "exp":"-8.875 in", + "val":-0.225425 + }, + "y":{ + "exp":"13.125 in", + "val":0.333375 + } + }, + "mass":{ + "exp":"150 lbs", + "val":68.0388555 + }, + "inertia":{ + "exp":"7.5 kg m ^ 2", + "val":7.5 + }, + "gearing":{ + "exp":"7.03", + "val":7.03 + }, + "radius":{ + "exp":"2 in", + "val":0.0508 + }, + "vmax":{ + "exp":"5800 RPM", + "val":607.3745796940267 + }, + "tmax":{ + "exp":"1.2 N * m", + "val":1.2 + }, + "cof":{ + "exp":"1.5", + "val":1.5 + }, + "bumper":{ + "front":{ + "exp":"13.75 in", + "val":0.34925 + }, + "side":{ + "exp":"17.5 in", + "val":0.4445 + }, + "back":{ + "exp":"13.75 in", + "val":0.34925 + } + }, + "differentialTrackWidth":{ + "exp":"22 in", + "val":0.5588 + } + }, + "generationFeatures":[], + "codegen":{ + "root":"../../java/frc/robot", + "genVars":false, + "genTrajData":false, + "useChoreoLib":false + } +} diff --git a/lib/examples/swerve/src/main/java/frc/robot/Constants.java b/lib/examples/swerve/src/main/java/frc/robot/Constants.java new file mode 100644 index 00000000..191dd3d4 --- /dev/null +++ b/lib/examples/swerve/src/main/java/frc/robot/Constants.java @@ -0,0 +1,29 @@ +package frc.robot; + +import edu.wpi.first.wpilibj.RobotBase; +import edu.wpi.team190.gompeilib.core.robot.RobotMode; + +public final class Constants { + public static final boolean TUNING_MODE = true; + public static final double LOOP_PERIOD_SECONDS = 0.02; + + public static RobotMode getMode() { + switch (RobotConfig.ROBOT) { + case V0_FUNKY, V1_DOOMSPIRAL: + return RobotBase.isReal() ? RobotMode.REAL : RobotMode.REPLAY; + + case V0_FUNKY_SIM, V1_DOOMSPIRAL_SIM: + return RobotMode.SIM; + + default: + return RobotMode.REAL; + } + } + + public static void main(String... args) { + if (getMode().equals(RobotMode.SIM)) { + System.err.println("Cannot deploy, invalid mode selected: " + RobotConfig.ROBOT); + System.exit(1); + } + } +} diff --git a/lib/examples/swerve/src/main/java/frc/robot/FieldConstants.java b/lib/examples/swerve/src/main/java/frc/robot/FieldConstants.java new file mode 100644 index 00000000..de1b67a8 --- /dev/null +++ b/lib/examples/swerve/src/main/java/frc/robot/FieldConstants.java @@ -0,0 +1,324 @@ +// Copyright (c) 2025-2026 Littleton Robotics +// http://github.com/Mechanical-Advantage +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file at +// the root directory of this project. + +package frc.robot; + +import edu.wpi.first.apriltag.AprilTagFieldLayout; +import edu.wpi.first.apriltag.AprilTagFields; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.math.geometry.Translation3d; +import edu.wpi.first.math.util.Units; + +/** + * Contains information for location of field element and other useful reference points. + * + *

NOTE: All constants are defined relative to the field coordinate system, and from the + * perspective of the blue alliance station + */ +public class FieldConstants { + + // AprilTag related constants + public static final int aprilTagCount = AprilTagLayoutType.ANDYMARK.getLayout().getTags().size(); + public static final double aprilTagWidth = Units.inchesToMeters(6.5); + + // Field dimensions + public static final double fieldLength = AprilTagLayoutType.ANDYMARK.getLayout().getFieldLength(); + public static final double fieldWidth = AprilTagLayoutType.ANDYMARK.getLayout().getFieldWidth(); + + /** + * Officially defined and relevant vertical lines found on the field (defined by X-axis offset) + */ + public static class LinesVertical { + public static final double center = fieldLength / 2.0; + public static final double starting = + AprilTagLayoutType.ANDYMARK.getLayout().getTagPose(26).get().getX(); + public static final double allianceZone = starting; + public static final double hubCenter = + AprilTagLayoutType.ANDYMARK.getLayout().getTagPose(26).get().getX() + Hub.width / 2.0; + public static final double neutralZoneNear = center - Units.inchesToMeters(120); + public static final double neutralZoneFar = center + Units.inchesToMeters(120); + public static final double oppHubCenter = + AprilTagLayoutType.ANDYMARK.getLayout().getTagPose(4).get().getX() + Hub.width / 2.0; + public static final double oppAllianceZone = + AprilTagLayoutType.ANDYMARK.getLayout().getTagPose(10).get().getX(); + } + + /** + * Officially defined and relevant horizontal lines found on the field (defined by Y-axis offset) + * + *

NOTE: The field element start and end are always left to right from the perspective of the + * alliance station + */ + public static class LinesHorizontal { + + public static final double center = fieldWidth / 2.0; + + // Right of hub + public static final double rightBumpStart = Hub.nearRightCorner.getY(); + public static final double rightBumpEnd = rightBumpStart - RightBump.width; + public static final double rightTrenchOpenStart = rightBumpEnd - Units.inchesToMeters(12.0); + public static final double rightTrenchOpenEnd = 0; + + // Left of hub + public static final double leftBumpEnd = Hub.nearLeftCorner.getY(); + public static final double leftBumpStart = leftBumpEnd + LeftBump.width; + public static final double leftTrenchOpenEnd = leftBumpStart + Units.inchesToMeters(12.0); + public static final double leftTrenchOpenStart = fieldWidth; + } + + /** Hub related constants */ + public static class Hub { + + // Dimensions + public static final double width = Units.inchesToMeters(47.0); + public static final double height = + Units.inchesToMeters(72.0); // includes the catcher at the top + public static final double innerWidth = Units.inchesToMeters(41.7); + public static final double innerHeight = Units.inchesToMeters(56.5); + + // Relevant reference points on alliance side + public static final Translation3d topCenterPoint = + new Translation3d( + AprilTagLayoutType.ANDYMARK.getLayout().getTagPose(26).get().getX() + width / 2.0, + fieldWidth / 2.0, + height); + public static final Translation3d innerCenterPoint = + new Translation3d( + AprilTagLayoutType.ANDYMARK.getLayout().getTagPose(26).get().getX() + width / 2.0, + fieldWidth / 2.0, + innerHeight); + + public static final Translation2d nearLeftCorner = + new Translation2d(topCenterPoint.getX() - width / 2.0, fieldWidth / 2.0 + width / 2.0); + public static final Translation2d nearRightCorner = + new Translation2d(topCenterPoint.getX() - width / 2.0, fieldWidth / 2.0 - width / 2.0); + public static final Translation2d farLeftCorner = + new Translation2d(topCenterPoint.getX() + width / 2.0, fieldWidth / 2.0 + width / 2.0); + public static final Translation2d farRightCorner = + new Translation2d(topCenterPoint.getX() + width / 2.0, fieldWidth / 2.0 - width / 2.0); + + // Relevant reference points on the opposite side + public static final Translation3d oppTopCenterPoint = + new Translation3d( + AprilTagLayoutType.ANDYMARK.getLayout().getTagPose(4).get().getX() + width / 2.0, + fieldWidth / 2.0, + height); + public static final Translation2d oppNearLeftCorner = + new Translation2d(oppTopCenterPoint.getX() - width / 2.0, fieldWidth / 2.0 + width / 2.0); + public static final Translation2d oppNearRightCorner = + new Translation2d(oppTopCenterPoint.getX() - width / 2.0, fieldWidth / 2.0 - width / 2.0); + public static final Translation2d oppFarLeftCorner = + new Translation2d(oppTopCenterPoint.getX() + width / 2.0, fieldWidth / 2.0 + width / 2.0); + public static final Translation2d oppFarRightCorner = + new Translation2d(oppTopCenterPoint.getX() + width / 2.0, fieldWidth / 2.0 - width / 2.0); + + // Hub faces + public static final Pose2d nearFace = + AprilTagLayoutType.ANDYMARK.getLayout().getTagPose(26).get().toPose2d(); + public static final Pose2d farFace = + AprilTagLayoutType.ANDYMARK.getLayout().getTagPose(20).get().toPose2d(); + public static final Pose2d rightFace = + AprilTagLayoutType.ANDYMARK.getLayout().getTagPose(18).get().toPose2d(); + public static final Pose2d leftFace = + AprilTagLayoutType.ANDYMARK.getLayout().getTagPose(21).get().toPose2d(); + } + + /** Left Bump related constants */ + public static class LeftBump { + + // Dimensions + public static final double width = Units.inchesToMeters(73.0); + public static final double height = Units.inchesToMeters(6.513); + public static final double depth = Units.inchesToMeters(44.4); + + // Relevant reference points on alliance side + public static final Translation2d nearLeftCorner = + new Translation2d(LinesVertical.hubCenter - width / 2, Units.inchesToMeters(255)); + public static final Translation2d nearRightCorner = Hub.nearLeftCorner; + public static final Translation2d farLeftCorner = + new Translation2d(LinesVertical.hubCenter + width / 2, Units.inchesToMeters(255)); + public static final Translation2d farRightCorner = Hub.farLeftCorner; + + // Relevant reference points on opposing side + public static final Translation2d oppNearLeftCorner = + new Translation2d(LinesVertical.hubCenter - width / 2, Units.inchesToMeters(255)); + public static final Translation2d oppNearRightCorner = Hub.oppNearLeftCorner; + public static final Translation2d oppFarLeftCorner = + new Translation2d(LinesVertical.hubCenter + width / 2, Units.inchesToMeters(255)); + public static final Translation2d oppFarRightCorner = Hub.oppFarLeftCorner; + } + + /** Right Bump related constants */ + public static class RightBump { + // Dimensions + public static final double width = Units.inchesToMeters(73.0); + public static final double height = Units.inchesToMeters(6.513); + public static final double depth = Units.inchesToMeters(44.4); + + // Relevant reference points on alliance side + public static final Translation2d nearLeftCorner = + new Translation2d(LinesVertical.hubCenter + width / 2, Units.inchesToMeters(255)); + public static final Translation2d nearRightCorner = Hub.nearLeftCorner; + public static final Translation2d farLeftCorner = + new Translation2d(LinesVertical.hubCenter - width / 2, Units.inchesToMeters(255)); + public static final Translation2d farRightCorner = Hub.farLeftCorner; + + // Relevant reference points on opposing side + public static final Translation2d oppNearLeftCorner = + new Translation2d(LinesVertical.hubCenter + width / 2, Units.inchesToMeters(255)); + public static final Translation2d oppNearRightCorner = Hub.oppNearLeftCorner; + public static final Translation2d oppFarLeftCorner = + new Translation2d(LinesVertical.hubCenter - width / 2, Units.inchesToMeters(255)); + public static final Translation2d oppFarRightCorner = Hub.oppFarLeftCorner; + } + + /** Left Trench related constants */ + public static class LeftTrench { + // Dimensions + public static final double width = Units.inchesToMeters(65.65); + public static final double depth = Units.inchesToMeters(47.0); + public static final double height = Units.inchesToMeters(40.25); + public static final double openingWidth = Units.inchesToMeters(50.34); + public static final double openingHeight = Units.inchesToMeters(22.25); + + // Relevant reference points on alliance side + public static final Translation3d openingTopLeft = + new Translation3d(LinesVertical.hubCenter, fieldWidth, openingHeight); + public static final Translation3d openingTopRight = + new Translation3d(LinesVertical.hubCenter, fieldWidth - openingWidth, openingHeight); + + // Relevant reference points on opposing side + public static final Translation3d oppOpeningTopLeft = + new Translation3d(LinesVertical.oppHubCenter, fieldWidth, openingHeight); + public static final Translation3d oppOpeningTopRight = + new Translation3d(LinesVertical.oppHubCenter, fieldWidth - openingWidth, openingHeight); + } + + public static class RightTrench { + + // Dimensions + public static final double width = Units.inchesToMeters(65.65); + public static final double depth = Units.inchesToMeters(47.0); + public static final double height = Units.inchesToMeters(40.25); + public static final double openingWidth = Units.inchesToMeters(50.34); + public static final double openingHeight = Units.inchesToMeters(22.25); + + // Relevant reference points on alliance side + public static final Translation3d openingTopLeft = + new Translation3d(LinesVertical.hubCenter, openingWidth, openingHeight); + public static final Translation3d openingTopRight = + new Translation3d(LinesVertical.hubCenter, 0, openingHeight); + + // Relevant reference points on opposing side + public static final Translation3d oppOpeningTopLeft = + new Translation3d(LinesVertical.oppHubCenter, openingWidth, openingHeight); + public static final Translation3d oppOpeningTopRight = + new Translation3d(LinesVertical.oppHubCenter, 0, openingHeight); + } + + /** Tower related constants */ + public static class Tower { + // Dimensions + public static final double width = Units.inchesToMeters(49.25); + public static final double depth = Units.inchesToMeters(45.0); + public static final double height = Units.inchesToMeters(78.25); + public static final double innerOpeningWidth = Units.inchesToMeters(32.250); + public static final double frontFaceX = Units.inchesToMeters(43.51); + + public static final double uprightHeight = Units.inchesToMeters(72.1); + + // Rung heights from the floor + public static final double lowRungHeight = Units.inchesToMeters(27.0); + public static final double midRungHeight = Units.inchesToMeters(45.0); + public static final double highRungHeight = Units.inchesToMeters(63.0); + + // Relevant reference points on alliance side + public static final Translation2d centerPoint = + new Translation2d( + frontFaceX, AprilTagLayoutType.ANDYMARK.getLayout().getTagPose(31).get().getY()); + public static final Translation2d leftUpright = + new Translation2d( + frontFaceX, + (AprilTagLayoutType.ANDYMARK.getLayout().getTagPose(31).get().getY()) + + innerOpeningWidth / 2 + + Units.inchesToMeters(0.75)); + public static final Translation2d rightUpright = + new Translation2d( + frontFaceX, + (AprilTagLayoutType.ANDYMARK.getLayout().getTagPose(31).get().getY()) + - innerOpeningWidth / 2 + - Units.inchesToMeters(0.75)); + + // Relevant reference points on opposing side + public static final Translation2d oppCenterPoint = + new Translation2d( + fieldLength - frontFaceX, + AprilTagLayoutType.ANDYMARK.getLayout().getTagPose(15).get().getY()); + public static final Translation2d oppLeftUpright = + new Translation2d( + fieldLength - frontFaceX, + (AprilTagLayoutType.ANDYMARK.getLayout().getTagPose(15).get().getY()) + + innerOpeningWidth / 2 + + Units.inchesToMeters(0.75)); + public static final Translation2d oppRightUpright = + new Translation2d( + fieldLength - frontFaceX, + (AprilTagLayoutType.ANDYMARK.getLayout().getTagPose(15).get().getY()) + - innerOpeningWidth / 2 + - Units.inchesToMeters(0.75)); + } + + public static class Depot { + // Dimensions + public static final double width = Units.inchesToMeters(42.0); + public static final double depth = Units.inchesToMeters(27.0); + public static final double height = Units.inchesToMeters(1.125); + public static final double distanceFromCenterY = Units.inchesToMeters(75.93); + + // Relevant reference points on alliance side + public static final Translation3d depotCenter = + new Translation3d(depth, (fieldWidth / 2) + distanceFromCenterY, height); + public static final Translation3d leftCorner = + new Translation3d(depth, (fieldWidth / 2) + distanceFromCenterY + (width / 2), height); + public static final Translation3d rightCorner = + new Translation3d(depth, (fieldWidth / 2) + distanceFromCenterY - (width / 2), height); + } + + public static class Outpost { + // Dimensions + public static final double width = Units.inchesToMeters(31.8); + public static final double openingDistanceFromFloor = Units.inchesToMeters(28.1); + public static final double height = Units.inchesToMeters(7.0); + + // Relevant reference points on alliance side + public static final Translation2d centerPoint = + new Translation2d(0, AprilTagLayoutType.ANDYMARK.getLayout().getTagPose(29).get().getY()); + } + + public enum AprilTagLayoutType { + ANDYMARK(AprilTagFields.k2026RebuiltAndymark), + WELDED(AprilTagFields.k2026RebuiltWelded); + + private final AprilTagFields name; + private volatile AprilTagFieldLayout layout; + + AprilTagLayoutType(AprilTagFields name) { + this.name = name; + } + + public AprilTagFieldLayout getLayout() { + if (layout != null) { + return layout; + } + layout = AprilTagFieldLayout.loadField(name); + return layout; + } + } + + public static final Translation2d FEED_TRANSLATION = new Translation2d(0, 0); +} diff --git a/lib/examples/swerve/src/main/java/frc/robot/Main.java b/lib/examples/swerve/src/main/java/frc/robot/Main.java new file mode 100644 index 00000000..8e389ea6 --- /dev/null +++ b/lib/examples/swerve/src/main/java/frc/robot/Main.java @@ -0,0 +1,11 @@ +package frc.robot; + +import edu.wpi.first.wpilibj.RobotBase; + +public final class Main { + private Main() {} + + public static void main(String... args) { + RobotBase.startRobot(Robot::new); + } +} diff --git a/lib/examples/swerve/src/main/java/frc/robot/Robot.java b/lib/examples/swerve/src/main/java/frc/robot/Robot.java new file mode 100644 index 00000000..1ae16042 --- /dev/null +++ b/lib/examples/swerve/src/main/java/frc/robot/Robot.java @@ -0,0 +1,285 @@ +package frc.robot; + +import com.ctre.phoenix6.SignalLogger; +import edu.wpi.first.math.MathShared; +import edu.wpi.first.math.MathSharedStore; +import edu.wpi.first.math.MathUsageId; +import edu.wpi.first.net.WebServer; +import edu.wpi.first.wpilibj.*; +import edu.wpi.first.wpilibj.livewindow.LiveWindow; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.CommandScheduler; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import edu.wpi.team190.gompeilib.core.robot.RobotContainer; +import edu.wpi.team190.gompeilib.core.utility.VirtualSubsystem; +import edu.wpi.team190.gompeilib.core.utility.phoenix.PhoenixUtil; +import frc.robot.subsystems.v0_Funky.V0_FunkyRobotContainer; +import frc.robot.util.*; +import frc.robot.util.Alert; +import frc.robot.util.Alert.AlertType; +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Map; +import java.util.function.BiConsumer; +import org.littletonrobotics.junction.LogFileUtil; +import org.littletonrobotics.junction.LoggedRobot; +import org.littletonrobotics.junction.Logger; +import org.littletonrobotics.junction.networktables.NT4Publisher; +import org.littletonrobotics.junction.wpilog.WPILOGReader; +import org.littletonrobotics.junction.wpilog.WPILOGWriter; + +/** + * The VM is configured to automatically run this class, and to call the functions corresponding to + * each mode, as described in the TimedRobot documentation. If you change the name of this class or + * the package after creating this project, you must also update the build.gradle file in the + * project. + */ +public class Robot extends LoggedRobot { + private static final double LOW_BATTERY_VOLTAGE = 10.0; + private static final double LOW_BATTERY_DISABLED_TIME = 1.5; + + private static final double LOOP_OVERRUN_WARNING_TIMEOUT = 1; + private static double startupTimestamp = Double.NEGATIVE_INFINITY; + private final Timer disabledTimer = new Timer(); + private final Alert logReceiverQueueAlert = + new Alert("Logging queue exceeded capacity, data will NOT be logged.", AlertType.WARNING); + private final Elastic.Notification lowBatteryAlert = + new Elastic.Notification( + Elastic.NotificationLevel.WARNING, + "Low Battery! Replace battery soon!", + "Battery voltage is very low, consider turning off the robot or replacing the battery."); + private Command autonomousCommand; + private RobotContainer robotContainer; + + public Robot() { + super(Constants.LOOP_PERIOD_SECONDS); + GompeiLib.init(Constants.getMode(), Constants.TUNING_MODE, Constants.LOOP_PERIOD_SECONDS); + } + + public static boolean isJitting() { + return Timer.getFPGATimestamp() - startupTimestamp <= 45.0; + } + + /** + * This function is run when the robot is first started up and should be used for any + * initialization code. + */ + @Override + public void robotInit() { + SignalLogger.enableAutoLogging(false); + LiveWindow.disableAllTelemetry(); + // Record metadata + Logger.recordMetadata("ProjectName", BuildConstants.MAVEN_NAME); + Logger.recordMetadata("BuildDate", BuildConstants.BUILD_DATE); + Logger.recordMetadata("GitSHA", BuildConstants.GIT_SHA); + Logger.recordMetadata("GitDate", BuildConstants.GIT_DATE); + Logger.recordMetadata("GitBranch", BuildConstants.GIT_BRANCH); + Logger.recordMetadata("GompeiLib_Version", BuildConstants.VERSION); + switch (BuildConstants.DIRTY) { + case 0: + Logger.recordMetadata("GitDirty", "All changes committed"); + break; + case 1: + Logger.recordMetadata("GitDirty", "Uncomitted changes"); + break; + default: + Logger.recordMetadata("GitDirty", "Unknown"); + break; + } + + // Set up data receivers & replay source + switch (Constants.getMode()) { + case REAL: + // Running on a real robot, log to a USB stick ("/U/logs") + Logger.addDataReceiver(new WPILOGWriter()); + Logger.addDataReceiver(new NT4Publisher()); + break; + + case SIM: + // Running a physics simulator, log to NT + // setUseTiming(false); + Logger.addDataReceiver(new NT4Publisher()); + // setting up maple sim field + break; + + case REPLAY: + // Replaying a log, set up replay source + setUseTiming(false); // Run as fast as possible + String logPath = LogFileUtil.findReplayLog(); + Logger.setReplaySource(new WPILOGReader(logPath)); + Logger.addDataReceiver(new WPILOGWriter(LogFileUtil.addPathSuffix(logPath, "_sim"))); + break; + } + + // Start AdvantageKit logger + Logger.start(); + + // Start timers + disabledTimer.restart(); + + RobotController.setBrownoutVoltage(6); + // Instantiate our RobotContainer. This will perform all our button bindings, + // and put our autonomous chooser on the dashboard. + robotContainer = + switch (RobotConfig.ROBOT) { + case V0_FUNKY, V0_FUNKY_SIM -> new V0_FunkyRobotContainer(); + default -> new RobotContainer() {}; + }; + + Elastic.selectTab("Autonomous"); + DriverStation.silenceJoystickConnectionWarning(true); + + try { + Field watchdogField = IterativeRobotBase.class.getDeclaredField("m_watchdog"); + watchdogField.setAccessible(true); + Watchdog watchdog = (Watchdog) watchdogField.get(this); + watchdog.setTimeout(LOOP_OVERRUN_WARNING_TIMEOUT); + } catch (Exception e) { + DriverStation.reportWarning("Failed to disable loop overrun warnings.", false); + } + CommandScheduler.getInstance().setPeriod(LOOP_OVERRUN_WARNING_TIMEOUT); + + // Silence Rotation2d warnings + var mathShared = MathSharedStore.getMathShared(); + MathSharedStore.setMathShared( + new MathShared() { + @Override + public void reportError(String error, StackTraceElement[] stackTrace) { + if (error.startsWith("x and y components of Rotation2d are zero")) { + return; + } + mathShared.reportError(error, stackTrace); + } + + @Override + public void reportUsage(MathUsageId id, int count) { + mathShared.reportUsage(id, count); + } + + @Override + public double getTimestamp() { + return mathShared.getTimestamp(); + } + }); + + String DEPLOY_DIR = + Filesystem.getDeployDirectory().getPath() + + RobotConfig.ROBOT.name().toLowerCase().replaceFirst("_sim", ""); + // try { + // var m = Choreo.class.getDeclaredMethod("setChoreoDir", File.class); + // m.setAccessible(true); + // m.invoke(null, new File(DEPLOY_DIR + "/choreo")); + // } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { + // throw new RuntimeException(e); + // } + // Log active commands + Map commandCounts = new HashMap<>(); + BiConsumer logCommandFunction = + (Command command, Boolean active) -> { + String name = command.getName(); + int count = commandCounts.getOrDefault(name, 0) + (active ? 1 : -1); + commandCounts.put(name, count); + Logger.recordOutput( + "CommandsUnique/" + name + "_" + Integer.toHexString(command.hashCode()), active); + Logger.recordOutput("CommandsAll/" + name, count > 0); + }; + CommandScheduler.getInstance() + .onCommandInitialize((Command command) -> logCommandFunction.accept(command, true)); + CommandScheduler.getInstance() + .onCommandFinish((Command command) -> logCommandFunction.accept(command, false)); + CommandScheduler.getInstance() + .onCommandInterrupt((Command command) -> logCommandFunction.accept(command, false)); + + WebServer.start(5800, DEPLOY_DIR); + + startupTimestamp = Timer.getFPGATimestamp(); + } + + /** This function is called periodically during all modes. */ + @Override + public void robotPeriodic() { + // Runs the Scheduler. This is responsible for polling buttons, adding + // newly-scheduled commands, running already-scheduled commands, removing + // finished or interrupted commands, and running subsystem periodic() methods. + // This must be called from the robot's periodic block in order for anything in + // the Command-based framework to work. + + PhoenixUtil.refreshAll(); + VirtualSubsystem.periodicAll(); + robotContainer.robotPeriodic(); + CommandScheduler.getInstance().run(); + + // Check logging fault + logReceiverQueueAlert.set(Logger.getReceiverQueueFault()); + + // Update low battery alert + if (RobotState.isEnabled()) { + disabledTimer.reset(); + } + if (RobotController.getBatteryVoltage() < LOW_BATTERY_VOLTAGE + && disabledTimer.hasElapsed(LOW_BATTERY_DISABLED_TIME)) { + Elastic.sendNotification(lowBatteryAlert); + } + } + + /** This function is called once when the robot is disabled. */ + @Override + public void disabledInit() {} + + /** This function is called periodically when disabled. */ + @Override + public void disabledPeriodic() {} + + /** This autonomous runs the autonomous command selected by your {@link RobotContainer} class. */ + @Override + public void autonomousInit() { + Elastic.selectTab("Autonomous"); + + autonomousCommand = robotContainer.getAutonomousCommand(); + + if (autonomousCommand != null) { + CommandScheduler.getInstance().cancelAll(); + CommandScheduler.getInstance().schedule(autonomousCommand); + } + } + + /** This function is called periodically during autonomous. */ + @Override + public void autonomousPeriodic() {} + + /** This function is called once when teleop is enabled. */ + @Override + public void teleopInit() { + // This makes sure that the autonomous stops running when + // teleop starts running. If you want the autonomous to + // continue until interrupted by another command, remove + // this line or comment it out. + if (autonomousCommand != null) { + CommandScheduler.getInstance().cancel(autonomousCommand); + } + Elastic.selectTab("Teleoperated"); + } + + /** This function is called periodically during operator control. */ + @Override + public void teleopPeriodic() {} + + /** This function is called once when test mode is enabled. */ + @Override + public void testInit() { + // Cancels all running commands at the start of test mode. + CommandScheduler.getInstance().cancelAll(); + } + + /** This function is called periodically during test mode. */ + @Override + public void testPeriodic() {} + + /** This function is called once when the robot is first started up. */ + @Override + public void simulationInit() {} + + /** This function is called periodically whilst in simulation. */ + @Override + public void simulationPeriodic() {} +} diff --git a/lib/examples/swerve/src/main/java/frc/robot/RobotConfig.java b/lib/examples/swerve/src/main/java/frc/robot/RobotConfig.java new file mode 100644 index 00000000..ebea24c8 --- /dev/null +++ b/lib/examples/swerve/src/main/java/frc/robot/RobotConfig.java @@ -0,0 +1,12 @@ +package frc.robot; + +public final class RobotConfig { + public static final RobotType ROBOT = RobotType.V1_DOOMSPIRAL; + + public enum RobotType { + V0_FUNKY, + V0_FUNKY_SIM, + V1_DOOMSPIRAL, + V1_DOOMSPIRAL_SIM, + } +} diff --git a/lib/examples/swerve/src/main/java/frc/robot/commands/shared/AutoAlignCommand.java b/lib/examples/swerve/src/main/java/frc/robot/commands/shared/AutoAlignCommand.java new file mode 100644 index 00000000..6d5c6e92 --- /dev/null +++ b/lib/examples/swerve/src/main/java/frc/robot/commands/shared/AutoAlignCommand.java @@ -0,0 +1,168 @@ +package frc.robot.commands.shared; + +import static edu.wpi.first.units.Units.*; + +import edu.wpi.first.math.controller.ProfiledPIDController; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.kinematics.ChassisSpeeds; +import edu.wpi.first.math.trajectory.TrapezoidProfile; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.team190.gompeilib.subsystems.drivebases.swervedrive.SwerveDrive; +import edu.wpi.team190.gompeilib.subsystems.drivebases.swervedrive.SwerveDriveConstants.AutoAlignConstants; +import java.util.function.BooleanSupplier; +import java.util.function.Supplier; +import org.littletonrobotics.junction.Logger; + +public class AutoAlignCommand extends Command { + private final SwerveDrive drive; + private final Pose2d targetPose; + private final BooleanSupplier valid; + private final Supplier robotPose; + + private ChassisSpeeds speeds; + + private final ProfiledPIDController alignXController; + private final ProfiledPIDController alignYController; + private final ProfiledPIDController alignHeadingController; + + /** + * Creates a new AutoAlignCommand. + * + * @param drive The swerve drive subsystem on which this command will run + * @param targetPose The pose to which the robot will attempt to align + * @param valid A boolean supplier that returns true when auto aligning is possible (e.g. when + * tags are visible) + * @param robotPose A supplier that returns the robot's current pose + * @param constants The swerve drive constants + */ + public AutoAlignCommand( + SwerveDrive drive, + Pose2d targetPose, + BooleanSupplier valid, + Supplier robotPose, + AutoAlignConstants constants, + double maxAccelerationMetersPerSecond) { + this.addRequirements(drive); + + this.drive = drive; + this.targetPose = targetPose; + this.valid = valid; + this.robotPose = robotPose; + + alignXController = + new ProfiledPIDController( + constants.xGains().kP().get(), + 0.0, + constants.xGains().kD().get(), + new TrapezoidProfile.Constraints( + constants.xConstraints().maxVelocity().get().in(MetersPerSecond), + maxAccelerationMetersPerSecond)); + alignYController = + new ProfiledPIDController( + constants.yGains().kP().get(), + 0.0, + constants.yGains().kD().get(), + new TrapezoidProfile.Constraints( + constants.yConstraints().maxVelocity().get().in(MetersPerSecond), + maxAccelerationMetersPerSecond)); + alignHeadingController = + new ProfiledPIDController( + constants.rotationGains().kP().get(), + 0.0, + constants.rotationGains().kD().get(), + new TrapezoidProfile.Constraints( + constants.rotationConstraints().maxVelocity().get().in(RadiansPerSecond), + Double.POSITIVE_INFINITY)); + + alignXController.setTolerance(constants.xConstraints().goalTolerance().get().in(Meters), 0); + alignYController.setTolerance(constants.yConstraints().goalTolerance().get().in(Meters), 0); + + alignHeadingController.enableContinuousInput(-Math.PI, Math.PI); + alignHeadingController.setTolerance( + constants.rotationConstraints().goalTolerance().get().in(Radians), 0); + speeds = new ChassisSpeeds(); + } + + @Override + public void initialize() { + alignHeadingController.reset( + robotPose.get().getRotation().getRadians(), + drive.getMeasuredChassisSpeeds().omegaRadiansPerSecond); + alignXController.reset( + robotPose.get().getX(), drive.getMeasuredChassisSpeeds().vxMetersPerSecond); + alignYController.reset( + robotPose.get().getY(), drive.getMeasuredChassisSpeeds().vyMetersPerSecond); + } + + @Override + public void execute() { + + if (valid.getAsBoolean()) { + ChassisSpeeds measuredSpeeds = drive.getMeasuredChassisSpeeds(); + + double adjustedXSpeed = + calculate( + alignXController, + targetPose.getX(), + robotPose.get().getX(), + measuredSpeeds.vxMetersPerSecond); + double adjustedYSpeed = + calculate( + alignYController, + targetPose.getY(), + robotPose.get().getY(), + measuredSpeeds.vyMetersPerSecond); + double adjustedThetaSpeed = + calculate( + alignHeadingController, + targetPose.getRotation().getRadians(), + robotPose.get().getRotation().getRadians(), + measuredSpeeds.omegaRadiansPerSecond); + speeds = + ChassisSpeeds.fromFieldRelativeSpeeds( + adjustedXSpeed, adjustedYSpeed, adjustedThetaSpeed, robotPose.get().getRotation()); + + } else { + speeds = new ChassisSpeeds(); + } + Logger.recordOutput("Drive/Auto Align/speeds", speeds); + drive.runVelocity(speeds); + } + + @Override + public void end(boolean interrupted) { + drive.runVelocity(new ChassisSpeeds()); + alignHeadingController.reset(robotPose.get().getRotation().getRadians()); + alignXController.reset(robotPose.get().getX()); + alignYController.reset(robotPose.get().getY()); + } + + @Override + public boolean isFinished() { + // Return true when the command should end + return alignXController.atGoal() + && alignYController.atGoal() + && alignHeadingController.atGoal(); + } + + /** + * Calculates the PID output for the given setpoint, measurement, and speed. If the controller is + * not at the setpoint, calculate the PID output. Otherwise, reset the controller with the given + * measurement and speed. + * + * @param controller The profiled PID controller to use. + * @param setpoint The setpoint of the controller. + * @param measurement The current measurement. + * @param speed The current speed. + * @return The calculated PID output. + */ + public static double calculate( + ProfiledPIDController controller, double setpoint, double measurement, double speed) { + double pidOutput = 0.0; + + if (!controller.atSetpoint()) pidOutput = controller.calculate(measurement, setpoint); + else controller.reset(measurement, speed); + + return pidOutput; + } +} diff --git a/lib/examples/swerve/src/main/java/frc/robot/commands/shared/DriveCommands.java b/lib/examples/swerve/src/main/java/frc/robot/commands/shared/DriveCommands.java new file mode 100644 index 00000000..3fcf265c --- /dev/null +++ b/lib/examples/swerve/src/main/java/frc/robot/commands/shared/DriveCommands.java @@ -0,0 +1,399 @@ +package frc.robot.commands.shared; + +import static edu.wpi.first.units.Units.Radians; +import static edu.wpi.first.units.Units.RadiansPerSecond; + +import edu.wpi.first.math.MathUtil; +import edu.wpi.first.math.Pair; +import edu.wpi.first.math.controller.ProfiledPIDController; +import edu.wpi.first.math.filter.SlewRateLimiter; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.kinematics.ChassisSpeeds; +import edu.wpi.first.math.trajectory.TrapezoidProfile; +import edu.wpi.first.math.util.Units; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.Commands; +import edu.wpi.team190.gompeilib.core.logging.Trace; +import edu.wpi.team190.gompeilib.subsystems.drivebases.swervedrive.SwerveDrive; +import edu.wpi.team190.gompeilib.subsystems.drivebases.swervedrive.SwerveDriveConstants; +import edu.wpi.team190.gompeilib.subsystems.drivebases.swervedrive.SwerveDriveConstants.AutoAlignConstants; +import frc.robot.FieldConstants; +import frc.robot.subsystems.v0_Funky.V0_FunkyRobotState; +import frc.robot.util.AllianceFlipUtil; +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.util.List; +import java.util.function.BooleanSupplier; +import java.util.function.DoubleSupplier; +import java.util.function.Supplier; +import org.littletonrobotics.junction.Logger; + +public final class DriveCommands { + + /** + * A command that drives a SwerveDrive using joystick input. + * + * @param drive The SwerveDrive to control. + * @param driveConstants The constants for the SwerveDrive. + * @param xSupplier The supplier of the x-axis joystick input. + * @param ySupplier The supplier of the y-axis joystick input. + * @param omegaSupplier The supplier of the omega joystick input. + * @param rotationSupplier The supplier of the rotation of the robot. + * @param hijackXSuppliers A list of pairs of boolean suppliers and double suppliers for hijacking + * the x velocity. If the boolean supplier is true, the x velocity will be set to the value of + * the double supplier. If multiple boolean suppliers are true, the first one in the list will + * take precedence. + * @param hijackYSuppliers A list of pairs of boolean suppliers and double suppliers for hijacking + * the y velocity. If the boolean supplier is true, the y velocity will be set to the value of + * the double supplier. If multiple boolean suppliers are true, the first one in the list will + * take precedence. + * @param hijackOmegaSuppliers A list of pairs of boolean suppliers and double suppliers for + * hijacking the omega velocity. If the boolean supplier is true, the omega velocity will be + * set to the value of the double supplier. If multiple boolean suppliers are true, the first + * one in the list will take precedence. + * @return A command that drives the SwerveDrive. + */ + @Trace + public static Command joystickDrive( + SwerveDrive drive, + SwerveDriveConstants driveConstants, + DoubleSupplier xSupplier, + DoubleSupplier ySupplier, + DoubleSupplier omegaSupplier, + Supplier rotationSupplier, + List> hijackXSuppliers, + List> hijackYSuppliers, + List> hijackOmegaSuppliers, + BooleanSupplier slowMode) { + return Commands.run( + () -> { + // Apply deadband + double linearMagnitude = + MathUtil.applyDeadband( + Math.hypot(xSupplier.getAsDouble(), ySupplier.getAsDouble()), + driveConstants.driverDeadband); + Rotation2d linearDirection = + new Rotation2d(xSupplier.getAsDouble(), ySupplier.getAsDouble()); + + double omega = + MathUtil.applyDeadband(omegaSupplier.getAsDouble(), driveConstants.driverDeadband); + linearMagnitude *= linearMagnitude; + + // Calculate new linear velocities + + double fieldRelativeXVel = + linearMagnitude * linearDirection.getCos() * drive.getMaxLinearSpeedMetersPerSec(); + double fieldRelativeYVel = + linearMagnitude * linearDirection.getSin() * drive.getMaxLinearSpeedMetersPerSec(); + + double angular = omega * drive.getMaxAngularSpeedRadPerSec(); + + fieldRelativeXVel = + hijackXSuppliers.stream() + .filter(pair -> pair.getFirst().getAsBoolean()) + .map(pair -> pair.getSecond().getAsDouble()) + .findFirst() + .orElse(slowMode.getAsBoolean() ? (fieldRelativeXVel * 0.1) : fieldRelativeXVel); + + fieldRelativeYVel = + hijackYSuppliers.stream() + .filter(pair -> pair.getFirst().getAsBoolean()) + .map(pair -> pair.getSecond().getAsDouble()) + .findFirst() + .orElse(slowMode.getAsBoolean() ? (fieldRelativeYVel * 0.1) : fieldRelativeYVel); + + angular = + hijackOmegaSuppliers.stream() + .filter(pair -> pair.getFirst().getAsBoolean()) + .map(pair -> pair.getSecond().getAsDouble()) + .findFirst() + .orElse(slowMode.getAsBoolean() ? (angular * 0.1) : angular); + + ChassisSpeeds chassisSpeeds = + ChassisSpeeds.fromFieldRelativeSpeeds( + fieldRelativeXVel, + fieldRelativeYVel, + angular, + AllianceFlipUtil.apply(rotationSupplier.get())); + + Logger.recordOutput("Drive/JoystickDrive/chassisSpeeds", chassisSpeeds); + + drive.runVelocity(chassisSpeeds); + }, + drive); + } + + public static Command joystickDrive( + SwerveDrive drive, + SwerveDriveConstants driveConstants, + DoubleSupplier xSupplier, + DoubleSupplier ySupplier, + DoubleSupplier omegaSupplier, + Supplier rotationSupplier) { + return joystickDrive( + drive, + driveConstants, + xSupplier, + ySupplier, + omegaSupplier, + rotationSupplier, + List.of(), + List.of(), + List.of(), + () -> false); + } + + public static Command joystickDriveRotationLock( + SwerveDrive drive, + SwerveDriveConstants driveConstants, + DoubleSupplier xSupplier, + DoubleSupplier ySupplier, + DoubleSupplier omegaSupplier, + Supplier rotationSupplier, + BooleanSupplier pointAtHub, + DoubleSupplier hubSetpoint, + BooleanSupplier cardinalDirectionAlign, + DoubleSupplier cardinalDirectionSetpoint, + BooleanSupplier slowMode) { + ProfiledPIDController omegaController = + new ProfiledPIDController( + driveConstants.autoAlignConstants.rotationGains().kP().get(), + 0.0, + driveConstants.autoAlignConstants.rotationGains().kD().get(), + new TrapezoidProfile.Constraints( + driveConstants + .autoAlignConstants + .rotationConstraints() + .maxVelocity() + .get() + .in(RadiansPerSecond), + Double.POSITIVE_INFINITY)); + omegaController.enableContinuousInput(-Math.PI, Math.PI); + omegaController.setTolerance( + driveConstants.autoAlignConstants.rotationConstraints().goalTolerance().get().in(Radians), + 0); + return joystickDrive( + drive, + driveConstants, + xSupplier, + ySupplier, + omegaSupplier, + rotationSupplier, + List.of(), + List.of(), + List.of( + Pair.of( + pointAtHub, + () -> + AutoAlignCommand.calculate( + omegaController, + hubSetpoint.getAsDouble(), + rotationSupplier.get().getRadians(), + drive.getMeasuredChassisSpeeds().omegaRadiansPerSecond)), + Pair.of( + cardinalDirectionAlign, + () -> + AutoAlignCommand.calculate( + omegaController, + cardinalDirectionSetpoint.getAsDouble(), + rotationSupplier.get().getRadians(), + drive.getMeasuredChassisSpeeds().omegaRadiansPerSecond)), + Pair.of( + slowMode, + () -> + AutoAlignCommand.calculate( + omegaController, + AllianceFlipUtil.apply(Rotation2d.kCCW_90deg).getRadians(), + rotationSupplier.get().getRadians(), + drive.getMeasuredChassisSpeeds().omegaRadiansPerSecond))), + slowMode); + } + + public static Command rotateToAngle( + SwerveDrive drive, + SwerveDriveConstants driveConstants, + Supplier currentRotation, + Rotation2d targetRotation) { + ProfiledPIDController omegaController = + new ProfiledPIDController( + driveConstants.autoAlignConstants.rotationGains().kP().get(), + 0.0, + driveConstants.autoAlignConstants.rotationGains().kD().get(), + new TrapezoidProfile.Constraints( + driveConstants + .autoAlignConstants + .rotationConstraints() + .maxVelocity() + .get() + .in(RadiansPerSecond), + Double.POSITIVE_INFINITY)); + omegaController.enableContinuousInput(-Math.PI, Math.PI); + + omegaController.setTolerance( + driveConstants.autoAlignConstants.rotationConstraints().goalTolerance().get().in(Radians), + 0); + return Commands.run( + () -> + drive.runVelocity( + ChassisSpeeds.fromFieldRelativeSpeeds( + 0.0, + 0.0, + AutoAlignCommand.calculate( + omegaController, + targetRotation.getRadians(), + currentRotation.get().getRadians(), + drive.getMeasuredChassisSpeeds().omegaRadiansPerSecond), + AllianceFlipUtil.apply(currentRotation.get())))); + } + + public static Command inchMovement(SwerveDrive drive, double velocity, double time) { + return Commands.run(() -> drive.runVelocity(new ChassisSpeeds(0.0, velocity, 0.0))) + .withTimeout(time); + } + + public static Command stop(SwerveDrive drive) { + return Commands.run(drive::stopWithX); + } + + public static Command feedforwardCharacterization(SwerveDrive drive) { + return new KSCharacterization( + drive, drive::runCharacterization, drive::getFFCharacterizationVelocity); + } + + public static Command autoAlignPoseCommand( + SwerveDrive drive, + Supplier robotPoseSupplier, + Pose2d targetPose, + AutoAlignConstants constants) { + return new AutoAlignCommand( + drive, targetPose, () -> true, robotPoseSupplier, constants, Double.POSITIVE_INFINITY); + } + + public static Command autoAlignTowerCommand( + SwerveDrive drive, Supplier robotPoseSupplier, AutoAlignConstants constants) { + return autoAlignPoseCommand( + drive, + robotPoseSupplier, + new Pose2d(FieldConstants.Tower.centerPoint, new Rotation2d()), + constants); + } + + public static Command aimAtHub(SwerveDrive drive, SwerveDriveConstants driveConstants) { + ProfiledPIDController omegaController = + new ProfiledPIDController( + driveConstants.autoAlignConstants.rotationGains().kP().get(), + 0.0, + driveConstants.autoAlignConstants.rotationGains().kD().get(), + new TrapezoidProfile.Constraints( + driveConstants + .autoAlignConstants + .rotationConstraints() + .maxVelocity() + .get() + .in(RadiansPerSecond), + Double.POSITIVE_INFINITY)); + omegaController.enableContinuousInput(-Math.PI, Math.PI); + omegaController.setTolerance( + driveConstants.autoAlignConstants.rotationConstraints().goalTolerance().get().in(Radians), + 0); + return Commands.run( + () -> { + drive.runVelocity( + ChassisSpeeds.fromFieldRelativeSpeeds( + 0.0, + 0.0, + AutoAlignCommand.calculate( + omegaController, + (AllianceFlipUtil.shouldFlip() + ? FieldConstants.Hub.oppTopCenterPoint.toTranslation2d() + : FieldConstants.Hub.topCenterPoint.toTranslation2d()) + .minus(V0_FunkyRobotState.getGlobalPose().getTranslation()) + .getAngle() + .minus(Rotation2d.kCW_Pi_2) + .getRadians(), + V0_FunkyRobotState.getHeading().getRadians(), + drive.getMeasuredChassisSpeeds().omegaRadiansPerSecond), + V0_FunkyRobotState.getHeading())); + }); + } + + public static Command wheelRadiusCharacterization( + SwerveDrive drive, SwerveDriveConstants driveConstants) { + double WHEEL_RADIUS_MAX_VELOCITY = 0.25; // Rad/Sec + double WHEEL_RADIUS_RAMP_RATE = 0.05; // Rad/Sec^2 + SlewRateLimiter limiter = new SlewRateLimiter(WHEEL_RADIUS_RAMP_RATE); + WheelRadiusCharacterizationState state = new WheelRadiusCharacterizationState(); + + return Commands.parallel( + // SwerveDrive control sequence + Commands.sequence( + // Reset acceleration limiter + Commands.runOnce( + () -> { + limiter.reset(0.0); + }), + + // Turn in place, accelerating up to full speed + Commands.run( + () -> { + double speed = limiter.calculate(WHEEL_RADIUS_MAX_VELOCITY); + drive.runVelocity(new ChassisSpeeds(0.0, 0.0, speed)); + }, + drive)), + + // Measurement sequence + Commands.sequence( + // Wait for modules to fully orient before starting measurement + Commands.waitSeconds(1.0), + + // Record starting measurement + Commands.runOnce( + () -> { + state.positions = drive.getWheelRadiusCharacterizationPositions(); + state.lastAngle = drive.getRawGyroRotation(); + state.gyroDelta = 0.0; + }), + + // Update gyro delta + Commands.run( + () -> { + var rotation = drive.getRawGyroRotation(); + state.gyroDelta += Math.abs(rotation.minus(state.lastAngle).getRadians()); + state.lastAngle = rotation; + }) + + // When cancelled, calculate and print results + .finallyDo( + () -> { + double[] positions = drive.getWheelRadiusCharacterizationPositions(); + double wheelDelta = 0.0; + for (int i = 0; i < 4; i++) { + wheelDelta += Math.abs(positions[i] - state.positions[i]) / 4.0; + } + double wheelRadius = + (state.gyroDelta * driveConstants.driveConfig.driveBaseRadius()) + / wheelDelta; + + NumberFormat formatter = new DecimalFormat("#0.000"); + System.out.println( + "********** Wheel Radius Characterization Results **********"); + System.out.println( + "\tWheel Delta: " + formatter.format(wheelDelta) + " radians"); + System.out.println( + "\tGyro Delta: " + formatter.format(state.gyroDelta) + " radians"); + System.out.println( + "\tWheel Radius: " + + formatter.format(wheelRadius) + + " meters, " + + formatter.format(Units.metersToInches(wheelRadius)) + + " inches"); + }))); + } + + private static class WheelRadiusCharacterizationState { + double[] positions = new double[4]; + Rotation2d lastAngle = new Rotation2d(); + double gyroDelta = 0.0; + } +} diff --git a/lib/examples/swerve/src/main/java/frc/robot/commands/shared/KSCharacterization.java b/lib/examples/swerve/src/main/java/frc/robot/commands/shared/KSCharacterization.java new file mode 100644 index 00000000..a92b593a --- /dev/null +++ b/lib/examples/swerve/src/main/java/frc/robot/commands/shared/KSCharacterization.java @@ -0,0 +1,55 @@ +package frc.robot.commands.shared; + +import edu.wpi.first.wpilibj.Timer; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.Subsystem; +import edu.wpi.team190.gompeilib.core.utility.tunable.LoggedTunableNumber; +import java.util.function.DoubleConsumer; +import java.util.function.DoubleSupplier; +import org.littletonrobotics.junction.Logger; + +public class KSCharacterization extends Command { + private static final LoggedTunableNumber currentRampFactor = + new LoggedTunableNumber("StaticCharacterization/CurrentRampPerSec", 1.0); + private static final LoggedTunableNumber minVelocity = + new LoggedTunableNumber("StaticCharacterization/MinStaticVelocity", 0.1); + + private final DoubleConsumer inputConsumer; + private final DoubleSupplier velocitySupplier; + private final Timer timer = new Timer(); + private double currentInput = 0.0; + + public KSCharacterization( + Subsystem subsystem, + DoubleConsumer characterizationInputConsumer, + DoubleSupplier velocitySupplier) { + inputConsumer = characterizationInputConsumer; + this.velocitySupplier = velocitySupplier; + addRequirements(subsystem); + } + + @Override + public void initialize() { + timer.restart(); + } + + @Override + public void execute() { + currentInput = timer.get() * currentRampFactor.get(); + inputConsumer.accept(currentInput); + } + + @Override + public boolean isFinished() { + return velocitySupplier.getAsDouble() >= minVelocity.get(); + } + + @Override + public void end(boolean interrupted) { + System.out.println("********** FF Characterization Results **********\n\n\n\n\n\n\n\n\n"); + System.out.println( + "Static Characterization output: " + currentInput + " amps\n\n\n\n\n\n\n\n\n\n"); + Logger.recordOutput("KS output", currentInput); + inputConsumer.accept(0); + } +} diff --git a/lib/examples/swerve/src/main/java/frc/robot/commands/shared/SharedCompositeCommands.java b/lib/examples/swerve/src/main/java/frc/robot/commands/shared/SharedCompositeCommands.java new file mode 100644 index 00000000..d89a8280 --- /dev/null +++ b/lib/examples/swerve/src/main/java/frc/robot/commands/shared/SharedCompositeCommands.java @@ -0,0 +1,35 @@ +package frc.robot.commands.shared; + +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.Commands; +import edu.wpi.team190.gompeilib.subsystems.drivebases.swervedrive.SwerveDrive; +import frc.robot.util.AllianceFlipUtil; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * A class that holds composite commands, which are sequences of commands for complex robot actions. + */ +public class SharedCompositeCommands { + /** + * Creates a command to reset the robot's heading to the alliance-specific zero. + * + * @param drive The drive subsystem. + * @return A command to reset the heading. + */ + public static Command resetHeading( + SwerveDrive drive, + Consumer resetHeadingConsumer, + Supplier currentRobotTranslation) { + return Commands.runOnce( + () -> { + resetHeadingConsumer.accept( + new Pose2d( + currentRobotTranslation.get(), AllianceFlipUtil.apply(new Rotation2d()))); + }) + .ignoringDisable(true); + } +} diff --git a/lib/examples/swerve/src/main/java/frc/robot/commands/v0_Funky/V0_FunkyCompositeCommands.java b/lib/examples/swerve/src/main/java/frc/robot/commands/v0_Funky/V0_FunkyCompositeCommands.java new file mode 100644 index 00000000..2fa8ec61 --- /dev/null +++ b/lib/examples/swerve/src/main/java/frc/robot/commands/v0_Funky/V0_FunkyCompositeCommands.java @@ -0,0 +1,3 @@ +package frc.robot.commands.v0_Funky; + +public class V0_FunkyCompositeCommands {} diff --git a/lib/examples/swerve/src/main/java/frc/robot/commands/v0_Funky/autonomous/V0_FunkyAutonomousTest.java b/lib/examples/swerve/src/main/java/frc/robot/commands/v0_Funky/autonomous/V0_FunkyAutonomousTest.java new file mode 100644 index 00000000..e65de1b5 --- /dev/null +++ b/lib/examples/swerve/src/main/java/frc/robot/commands/v0_Funky/autonomous/V0_FunkyAutonomousTest.java @@ -0,0 +1,3 @@ +package frc.robot.commands.v0_Funky.autonomous; + +public class V0_FunkyAutonomousTest {} diff --git a/lib/examples/swerve/src/main/java/frc/robot/subsystems/v0_Funky/V0_FunkyConstants.java b/lib/examples/swerve/src/main/java/frc/robot/subsystems/v0_Funky/V0_FunkyConstants.java new file mode 100644 index 00000000..69134d4f --- /dev/null +++ b/lib/examples/swerve/src/main/java/frc/robot/subsystems/v0_Funky/V0_FunkyConstants.java @@ -0,0 +1,162 @@ +package frc.robot.subsystems.v0_Funky; + +import static edu.wpi.first.units.Units.*; + +import edu.wpi.first.math.geometry.Rotation3d; +import edu.wpi.first.math.geometry.Transform3d; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.math.util.Units; +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.AngularPositionConstraints; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.LinearConstraints; +import edu.wpi.team190.gompeilib.core.utility.tunable.LoggedTunableMeasure; +import edu.wpi.team190.gompeilib.core.utility.tunable.LoggedTunableNumber; +import edu.wpi.team190.gompeilib.subsystems.drivebases.swervedrive.SwerveDriveConstants; +import edu.wpi.team190.gompeilib.subsystems.vision.VisionConstants.StaticLimelightConfig; +import edu.wpi.team190.gompeilib.subsystems.vision.camera.CameraType; + +public class V0_FunkyConstants { + public static final SwerveDriveConstants.DriveConfig DRIVE_CONFIG = + SwerveDriveConstants.DriveConfig.builder() + .withCanBus(V0_FunkyTunerConstants.kCANBus) + .withPigeon2Id(V0_FunkyTunerConstants.DrivetrainConstants.Pigeon2Id) + .withMaxLinearVelocityMetersPerSecond( + V0_FunkyTunerConstants.kSpeedAt12Volts.in(MetersPerSecond)) + .withWheelRadiusMeters(V0_FunkyTunerConstants.kWheelRadius.in(Meters)) + .withDriveModel(DCMotor.getKrakenX60Foc(1)) + .withTurnModel(DCMotor.getKrakenX44Foc(1)) + .withFrontLeft(V0_FunkyTunerConstants.FrontLeft) + .withFrontRight(V0_FunkyTunerConstants.FrontRight) + .withBackLeft(V0_FunkyTunerConstants.BackLeft) + .withBackRight(V0_FunkyTunerConstants.BackRight) + .withDriveClosedLoopOutputType(V0_FunkyTunerConstants.kDriveClosedLoopOutput) + .withSteerClosedLoopOutputType(V0_FunkyTunerConstants.kSteerClosedLoopOutput) + .withBumperLength(Units.inchesToMeters(34.5)) + .withBumperWidth(Units.inchesToMeters(34.5)) + .build(); + + public static final Gains DRIVE_GAINS = + Gains.builder() + .withKP( + new LoggedTunableNumber( + "Drive/Teleoperated/Drive Kp", V0_FunkyTunerConstants.driveGains.kP)) + .withKD( + new LoggedTunableNumber( + "Drive/Teleoperated/Drive Kd", V0_FunkyTunerConstants.driveGains.kD)) + .withKS( + new LoggedTunableNumber( + "Drive/Teleoperated/Drive Ks", V0_FunkyTunerConstants.driveGains.kS)) + .withKV( + new LoggedTunableNumber( + "Drive/Teleoperated/Drive Kv", V0_FunkyTunerConstants.driveGains.kV)) + .build(); + + public static final Gains TRANSLATION_AUTO_GAINS = + Gains.builder() + .withKP(new LoggedTunableNumber("Drive/Auto/Translation Kp", 0.0)) + .withKD(new LoggedTunableNumber("Drive/Auto/Translation Kd", 0.0)) + .build(); + + public static final Gains ROTATION_AUTO_GAINS = + Gains.builder() + .withKP(new LoggedTunableNumber("Drive/Auto/Rotation Kp", 0.0)) + .withKD(new LoggedTunableNumber("Drive/Auto/Rotation Kd", 0.0)) + .build(); + + public static final Gains AUTO_ALIGN_X_GAINS = + Gains.builder() + .withKP(new LoggedTunableNumber("Drive/Auto Align/X/Kp", 0.0)) + .withKD(new LoggedTunableNumber("Drive/Auto Align/X/Kd", 0.0)) + .build(); + + public static final LinearConstraints AUTO_ALIGN_X_CONSTRAINTS = + LinearConstraints.builder() + .withMaxVelocity( + new LoggedTunableMeasure<>( + "Drive/Auto Align/X/Max Velocity", MetersPerSecond.of(0.0))) + .withMaxAcceleration( + new LoggedTunableMeasure<>( + "Drive/Auto Align/X/Max Acceleration", MetersPerSecondPerSecond.of(0.0))) + .withGoalTolerance( + new LoggedTunableMeasure<>("Drive/Auto Align/X/Max Velocity", Meters.of(0.0))) + .build(); + + public static final Gains AUTO_ALIGN_Y_GAINS = + Gains.builder() + .withKP(new LoggedTunableNumber("Drive/Auto Align/Y/Kp", 0.0)) + .withKD(new LoggedTunableNumber("Drive/Auto Align/Y/Kd", 0.0)) + .build(); + + public static final LinearConstraints AUTO_ALIGN_Y_CONSTRAINTS = + LinearConstraints.builder() + .withMaxVelocity( + new LoggedTunableMeasure<>( + "Drive/Auto Align/Y/Max Velocity", MetersPerSecond.of(0.0))) + .withMaxAcceleration( + new LoggedTunableMeasure<>( + "Drive/Auto Align/Y/Max Acceleration", MetersPerSecondPerSecond.of(0.0))) + .withGoalTolerance( + new LoggedTunableMeasure<>("Drive/Auto Align/Y/Max Velocity", Meters.of(0.0))) + .build(); + + public static final Gains AUTO_ALIGN_THETA_GAINS = + Gains.builder() + .withKP(new LoggedTunableNumber("Drive/Auto Align/Theta/Kp", 0.0)) + .withKD(new LoggedTunableNumber("Drive/Auto Align/Theta/Kd", 0.0)) + .build(); + + public static final AngularPositionConstraints AUTO_ALIGN_THETA_CONSTRAINTS = + AngularPositionConstraints.builder() + .withMaxVelocity( + new LoggedTunableMeasure<>( + "Drive/Auto Align/Theta/Max Velocity", RadiansPerSecond.of(0.0))) + .withMaxAcceleration( + new LoggedTunableMeasure<>( + "Drive/Auto Align/Theta/Max Acceleration", RadiansPerSecondPerSecond.of(0.0))) + .withGoalTolerance( + new LoggedTunableMeasure<>("Drive/Auto Align/Theta/Max Velocity", Radians.of(0.0))) + .build(); + + public static final SwerveDriveConstants.AutoAlignConstants AUTO_ALIGN_NEAR_CONSTANTS = + SwerveDriveConstants.AutoAlignConstants.builder() + .withXGains(AUTO_ALIGN_X_GAINS) + .withXConstraints(AUTO_ALIGN_X_CONSTRAINTS) + .withYGains(AUTO_ALIGN_Y_GAINS) + .withYConstraints(AUTO_ALIGN_Y_CONSTRAINTS) + .withRotationGains(AUTO_ALIGN_THETA_GAINS) + .withRotationConstraints(AUTO_ALIGN_THETA_CONSTRAINTS) + .withLinearThreshold( + new LoggedTunableMeasure<>( + "Drive/Auto Align/Position Threshold Meters", Inches.of(0.25))) + .withAngularThreshold( + new LoggedTunableMeasure<>( + "Drive/Auto Align/Angular Threshold Meters", Radians.of(0.25))) + .build(); + + public static final double ODOMETRY_FREQUENCY = 250.0; + public static final double DRIVER_DEADBAND = 0.1; + public static final double OPERATOR_DEADBAND = 0.1; + + public static final SwerveDriveConstants DRIVE_CONSTANTS = + SwerveDriveConstants.builder() + .withDriveConfig(DRIVE_CONFIG) + .withAutoTranslationGains(TRANSLATION_AUTO_GAINS) + .withAutoRotationGains(ROTATION_AUTO_GAINS) + .withAutoAlignConstants(AUTO_ALIGN_NEAR_CONSTANTS) + .withOdometryFrequency(ODOMETRY_FREQUENCY) + .withDriverDeadband(DRIVER_DEADBAND) + .withOperatorDeadband(OPERATOR_DEADBAND) + .build(); + + public static final StaticLimelightConfig LIMELIGHT_CONFIG = + StaticLimelightConfig.builder() + .key("limelight") + .cameraType(CameraType.LIMELIGHT_4) + .horizontalFOV(82.0) + .verticalFOV(56.2) + .megatagXYStdev(0.1) + .metatagThetaStdev(0.0015) + .megatag2XYStdev(0.001) + .robotToCameraTransform(new Transform3d(0.0, 0.0, 0.0, new Rotation3d(0.0, 0.0, 0.0))) + .build(); +} diff --git a/lib/examples/swerve/src/main/java/frc/robot/subsystems/v0_Funky/V0_FunkyRobotContainer.java b/lib/examples/swerve/src/main/java/frc/robot/subsystems/v0_Funky/V0_FunkyRobotContainer.java new file mode 100644 index 00000000..41b53588 --- /dev/null +++ b/lib/examples/swerve/src/main/java/frc/robot/subsystems/v0_Funky/V0_FunkyRobotContainer.java @@ -0,0 +1,167 @@ +package frc.robot.subsystems.v0_Funky; + +import choreo.auto.AutoChooser; +import edu.wpi.first.apriltag.AprilTagFieldLayout; +import edu.wpi.first.apriltag.AprilTagFields; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.button.CommandXboxController; +import edu.wpi.team190.gompeilib.core.io.components.inertial.GyroIO; +import edu.wpi.team190.gompeilib.core.io.components.inertial.GyroIOPigeon2; +import edu.wpi.team190.gompeilib.core.robot.RobotContainer; +import edu.wpi.team190.gompeilib.core.robot.RobotMode; +import edu.wpi.team190.gompeilib.subsystems.drivebases.swervedrive.SwerveDrive; +import edu.wpi.team190.gompeilib.subsystems.drivebases.swervedrive.SwerveModuleIO; +import edu.wpi.team190.gompeilib.subsystems.drivebases.swervedrive.SwerveModuleIOSim; +import edu.wpi.team190.gompeilib.subsystems.drivebases.swervedrive.SwerveModuleIOTalonFX; +import edu.wpi.team190.gompeilib.subsystems.vision.Vision; +import edu.wpi.team190.gompeilib.subsystems.vision.camera.CameraStaticLimelight; +import edu.wpi.team190.gompeilib.subsystems.vision.io.CameraIOLimelight; +import frc.robot.Constants; +import frc.robot.RobotConfig; +import frc.robot.commands.shared.DriveCommands; +import frc.robot.commands.shared.SharedCompositeCommands; +import frc.robot.util.input.XKeysInput; +import java.util.List; + +public class V0_FunkyRobotContainer implements RobotContainer { + private SwerveDrive drive; + private Vision vision; + + private final CommandXboxController driver = new CommandXboxController(0); + + private final AutoChooser autoChooser = new AutoChooser(); + + private final XKeysInput xkeys = new XKeysInput(1); + + public V0_FunkyRobotContainer() { + if (Constants.getMode() != RobotMode.REPLAY) { + switch (RobotConfig.ROBOT) { + case V0_FUNKY: + drive = + new SwerveDrive( + V0_FunkyConstants.DRIVE_CONSTANTS, + new GyroIOPigeon2( + V0_FunkyConstants.DRIVE_CONSTANTS, V0_FunkyRobotState::setGyroTimestamp), + new SwerveModuleIOTalonFX( + V0_FunkyConstants.DRIVE_CONSTANTS, + V0_FunkyConstants.DRIVE_CONSTANTS.driveConfig.frontLeft()), + new SwerveModuleIOTalonFX( + V0_FunkyConstants.DRIVE_CONSTANTS, + V0_FunkyConstants.DRIVE_CONSTANTS.driveConfig.frontRight()), + new SwerveModuleIOTalonFX( + V0_FunkyConstants.DRIVE_CONSTANTS, + V0_FunkyConstants.DRIVE_CONSTANTS.driveConfig.backLeft()), + new SwerveModuleIOTalonFX( + V0_FunkyConstants.DRIVE_CONSTANTS, + V0_FunkyConstants.DRIVE_CONSTANTS.driveConfig.backRight()), + V0_FunkyRobotState::getGlobalPose, + V0_FunkyRobotState::resetPose); + vision = + new Vision( + () -> AprilTagFieldLayout.loadField(AprilTagFields.k2025ReefscapeAndyMark), + new CameraStaticLimelight( + new CameraIOLimelight(V0_FunkyConstants.LIMELIGHT_CONFIG), + V0_FunkyConstants.LIMELIGHT_CONFIG, + V0_FunkyRobotState::getHeading, + drive::getMeasuredChassisSpeeds, + V0_FunkyRobotState::getGyroTimestamp, + List.of(), + List.of())); + break; + + case V0_FUNKY_SIM: + drive = + new SwerveDrive( + V0_FunkyConstants.DRIVE_CONSTANTS, + new GyroIO() {}, + new SwerveModuleIOSim( + V0_FunkyConstants.DRIVE_CONSTANTS, + V0_FunkyConstants.DRIVE_CONSTANTS.driveConfig.frontLeft()), + new SwerveModuleIOSim( + V0_FunkyConstants.DRIVE_CONSTANTS, + V0_FunkyConstants.DRIVE_CONSTANTS.driveConfig.frontRight()), + new SwerveModuleIOSim( + V0_FunkyConstants.DRIVE_CONSTANTS, + V0_FunkyConstants.DRIVE_CONSTANTS.driveConfig.backLeft()), + new SwerveModuleIOSim( + V0_FunkyConstants.DRIVE_CONSTANTS, + V0_FunkyConstants.DRIVE_CONSTANTS.driveConfig.backRight()), + () -> Pose2d.kZero, + V0_FunkyRobotState::resetPose); + vision = + new Vision( + () -> AprilTagFieldLayout.loadField(AprilTagFields.k2025ReefscapeAndyMark)); + + break; + + default: + break; + } + } + if (drive == null) { + drive = + new SwerveDrive( + V0_FunkyConstants.DRIVE_CONSTANTS, + new GyroIOPigeon2( + V0_FunkyConstants.DRIVE_CONSTANTS, V0_FunkyRobotState::setGyroTimestamp), + new SwerveModuleIO() {}, + new SwerveModuleIO() {}, + new SwerveModuleIO() {}, + new SwerveModuleIO() {}, + V0_FunkyRobotState::getGlobalPose, + V0_FunkyRobotState::resetPose); + } + + if (vision == null) { + new Vision(() -> AprilTagFieldLayout.loadField(AprilTagFields.k2025ReefscapeAndyMark)); + } + + configureButtonBindings(); + configureAutos(); + } + + private void configureButtonBindings() { + drive.setDefaultCommand( + DriveCommands.joystickDrive( + drive, + V0_FunkyConstants.DRIVE_CONSTANTS, + driver::getLeftY, + driver::getLeftX, + driver::getRightX, + V0_FunkyRobotState::getHeading)); + + driver + .povDown() + .onTrue( + SharedCompositeCommands.resetHeading( + drive, + V0_FunkyRobotState::resetPose, + () -> V0_FunkyRobotState.getGlobalPose().getTranslation())); + + xkeys + .a1() + .or(xkeys.a2()) + .or(xkeys.a3()) + .or(xkeys.a4()) + .onTrue( + SharedCompositeCommands.resetHeading( + drive, + V0_FunkyRobotState::resetPose, + () -> V0_FunkyRobotState.getGlobalPose().getTranslation())); + } + + private void configureAutos() { + // Autos here + } + + @Override + public void robotPeriodic() { + V0_FunkyRobotState.periodic(drive.getRawGyroRotation(), drive.getModulePositions()); + } + + @Override + public Command getAutonomousCommand() { + return autoChooser.selectedCommand(); + } +} diff --git a/lib/examples/swerve/src/main/java/frc/robot/subsystems/v0_Funky/V0_FunkyRobotState.java b/lib/examples/swerve/src/main/java/frc/robot/subsystems/v0_Funky/V0_FunkyRobotState.java new file mode 100644 index 00000000..c49be094 --- /dev/null +++ b/lib/examples/swerve/src/main/java/frc/robot/subsystems/v0_Funky/V0_FunkyRobotState.java @@ -0,0 +1,67 @@ +package frc.robot.subsystems.v0_Funky; + +import edu.wpi.first.apriltag.AprilTagFieldLayout; +import edu.wpi.first.apriltag.AprilTagFields; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.kinematics.SwerveModulePosition; +import edu.wpi.first.networktables.NetworkTablesJNI; +import edu.wpi.first.wpilibj.Timer; +import edu.wpi.team190.gompeilib.core.state.localization.FieldZone; +import edu.wpi.team190.gompeilib.core.state.localization.Localization; +import frc.robot.util.NTPrefixes; +import java.util.HashSet; +import java.util.List; +import lombok.Getter; +import lombok.Setter; +import org.littletonrobotics.junction.AutoLogOutput; +import org.littletonrobotics.junction.Logger; + +public class V0_FunkyRobotState { + private static final AprilTagFieldLayout fieldLayout; + private static final Localization localization; + + @AutoLogOutput(key = NTPrefixes.ROBOT_STATE + "Hood/Score Angle") + @Getter + private static final Rotation2d scoreAngle; + + @AutoLogOutput(key = NTPrefixes.ROBOT_STATE + "Hood/Feed Angle") + @Getter + private static final Rotation2d feedAngle; + + private static final FieldZone globalZone; + + @Getter @Setter private static long gyroTimestamp; + + static { + fieldLayout = AprilTagFieldLayout.loadField(AprilTagFields.k2026RebuiltAndymark); + + globalZone = new FieldZone(new HashSet<>(fieldLayout.getTags())); + + localization = + new Localization( + List.of(globalZone), V0_FunkyConstants.DRIVE_CONSTANTS.driveConfig.kinematics(), 2); + scoreAngle = Rotation2d.kZero; + feedAngle = Rotation2d.kZero; + + gyroTimestamp = NetworkTablesJNI.now(); + } + + public static void periodic(Rotation2d heading, SwerveModulePosition[] modulePositions) { + + localization.addOdometryObservation(Timer.getTimestamp(), heading, modulePositions); + Logger.recordOutput(NTPrefixes.ROBOT_STATE + "/Global Pose", getGlobalPose()); + } + + public static void resetPose(Pose2d pose) { + localization.resetPose(pose); + } + + public static Rotation2d getHeading() { + return localization.getHeading(); + } + + public static Pose2d getGlobalPose() { + return localization.getEstimatedPose(globalZone); + } +} diff --git a/lib/examples/swerve/src/main/java/frc/robot/subsystems/v0_Funky/V0_FunkyTunerConstants.java b/lib/examples/swerve/src/main/java/frc/robot/subsystems/v0_Funky/V0_FunkyTunerConstants.java new file mode 100644 index 00000000..cebaaaf4 --- /dev/null +++ b/lib/examples/swerve/src/main/java/frc/robot/subsystems/v0_Funky/V0_FunkyTunerConstants.java @@ -0,0 +1,304 @@ +package frc.robot.subsystems.v0_Funky; + +import static edu.wpi.first.units.Units.*; + +import com.ctre.phoenix6.CANBus; +import com.ctre.phoenix6.configs.*; +import com.ctre.phoenix6.hardware.*; +import com.ctre.phoenix6.signals.*; +import com.ctre.phoenix6.swerve.*; +import com.ctre.phoenix6.swerve.SwerveModuleConstants.*; +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.numbers.N1; +import edu.wpi.first.math.numbers.N3; +import edu.wpi.first.units.measure.*; + +// Generated by the 2026 Tuner X Swerve Project Generator +// https://v6.docs.ctr-electronics.com/en/stable/docs/tuner/tuner-swerve/index.html +public class V0_FunkyTunerConstants { + // Both sets of gains need to be tuned to your individual robot. + + // The steer motor uses any SwerveModule.SteerRequestType control request with the + // output type specified by SwerveModuleConstants.SteerMotorClosedLoopOutput + public static final Slot0Configs steerGains = + new Slot0Configs() + .withKP(100) + .withKI(0) + .withKD(0.5) + .withKS(0.1) + .withKV(2.66) + .withKA(0) + .withStaticFeedforwardSign(StaticFeedforwardSignValue.UseClosedLoopSign); + // When using closed-loop control, the drive motor uses the control + // output type specified by SwerveModuleConstants.DriveMotorClosedLoopOutput + public static final Slot0Configs driveGains = + new Slot0Configs().withKP(60.0).withKI(0).withKD(0.0).withKS(2.361118000000002).withKV(0.0); + + // The closed-loop output type to use for the steer motors; + // This affects the PID/FF gains for the steer motors + public static final ClosedLoopOutputType kSteerClosedLoopOutput = ClosedLoopOutputType.Voltage; + // The closed-loop output type to use for the drive motors; + // This affects the PID/FF gains for the drive motors + public static final ClosedLoopOutputType kDriveClosedLoopOutput = + ClosedLoopOutputType.TorqueCurrentFOC; + + // The type of motor used for the drive motor + private static final DriveMotorArrangement kDriveMotorType = + DriveMotorArrangement.TalonFX_Integrated; + // The type of motor used for the drive motor + private static final SteerMotorArrangement kSteerMotorType = + SteerMotorArrangement.TalonFX_Integrated; + + // The remote sensor feedback type to use for the steer motors; + // When not Pro-licensed, Fused*/Sync* automatically fall back to Remote* + private static final SteerFeedbackType kSteerFeedbackType = SteerFeedbackType.FusedCANcoder; + + // The stator current at which the wheels start to slip; + // This needs to be tuned to your individual robot + private static final Current kSlipCurrent = Amps.of(120); + + // Initial configs for the drive and steer motors and the azimuth encoder; these cannot be null. + // Some configs will be overwritten; check the `with*InitialConfigs()` API documentation. + private static final TalonFXConfiguration driveInitialConfigs = new TalonFXConfiguration(); + private static final TalonFXConfiguration steerInitialConfigs = + new TalonFXConfiguration() + .withCurrentLimits( + new CurrentLimitsConfigs() + // Swerve azimuth does not require much torque output, so we can set a relatively + // low + // stator current limit to help avoid brownouts without impacting performance. + .withStatorCurrentLimit(Amps.of(60)) + .withStatorCurrentLimitEnable(true)); + private static final CANcoderConfiguration encoderInitialConfigs = new CANcoderConfiguration(); + // Configs for the Pigeon 2; leave this null to skip applying Pigeon 2 configs + private static final Pigeon2Configuration pigeonConfigs = null; + + // CAN bus that the devices are located on; + // All swerve devices must share the same CAN bus + public static final CANBus kCANBus = new CANBus("Drive", "./logs/example.hoot"); + + // Theoretical free speed (m/s) at 12 V applied output; + // This needs to be tuned to your individual robot + public static final LinearVelocity kSpeedAt12Volts = MetersPerSecond.of(5.04); + + // Every 1 rotation of the azimuth results in kCoupleRatio drive motor turns; + // This may need to be tuned to your individual robot + private static final double kCoupleRatio = 3.5714285714285716; + + private static final double kDriveGearRatio = 6.122448979591837; + private static final double kSteerGearRatio = 21.428571428571427; + public static final Distance kWheelRadius = Inches.of(2); + + private static final boolean kInvertRightSide = false; + + private static final int kPigeonId = 50; + + // These are only used for simulation + private static final MomentOfInertia kSteerInertia = KilogramSquareMeters.of(0.01); + private static final MomentOfInertia kDriveInertia = KilogramSquareMeters.of(0.01); + // Simulated voltage necessary to overcome friction + private static final Voltage kSteerFrictionVoltage = Volts.of(0.2); + private static final Voltage kDriveFrictionVoltage = Volts.of(0.2); + + public static final SwerveDrivetrainConstants DrivetrainConstants = + new SwerveDrivetrainConstants() + .withCANBusName(kCANBus.getName()) + .withPigeon2Id(kPigeonId) + .withPigeon2Configs(pigeonConfigs); + + private static final SwerveModuleConstantsFactory< + TalonFXConfiguration, TalonFXConfiguration, CANcoderConfiguration> + ConstantCreator = + new SwerveModuleConstantsFactory< + TalonFXConfiguration, TalonFXConfiguration, CANcoderConfiguration>() + .withDriveMotorGearRatio(kDriveGearRatio) + .withSteerMotorGearRatio(kSteerGearRatio) + .withCouplingGearRatio(kCoupleRatio) + .withWheelRadius(kWheelRadius) + .withSteerMotorGains(steerGains) + .withDriveMotorGains(driveGains) + .withSteerMotorClosedLoopOutput(kSteerClosedLoopOutput) + .withDriveMotorClosedLoopOutput(kDriveClosedLoopOutput) + .withSlipCurrent(kSlipCurrent) + .withSpeedAt12Volts(kSpeedAt12Volts) + .withDriveMotorType(kDriveMotorType) + .withSteerMotorType(kSteerMotorType) + .withFeedbackSource(kSteerFeedbackType) + .withDriveMotorInitialConfigs(driveInitialConfigs) + .withSteerMotorInitialConfigs(steerInitialConfigs) + .withEncoderInitialConfigs(encoderInitialConfigs) + .withSteerInertia(kSteerInertia) + .withDriveInertia(kDriveInertia) + .withSteerFrictionVoltage(kSteerFrictionVoltage) + .withDriveFrictionVoltage(kDriveFrictionVoltage); + + // Front Left + private static final int kFrontLeftDriveMotorId = 1; + private static final int kFrontLeftSteerMotorId = 5; + private static final int kFrontLeftEncoderId = 10; + private static final Angle kFrontLeftEncoderOffset = Rotations.of(-0.463134765625 + 0.25); + private static final boolean kFrontLeftSteerMotorInverted = true; + private static final boolean kFrontLeftEncoderInverted = false; + + private static final Distance kFrontLeftXPos = Inches.of(11.5); + private static final Distance kFrontLeftYPos = Inches.of(11.5); + + // Front Right + private static final int kFrontRightDriveMotorId = 2; + private static final int kFrontRightSteerMotorId = 6; + private static final int kFrontRightEncoderId = 11; + private static final Angle kFrontRightEncoderOffset = Rotations.of(0.1376953125 + 0.25); + private static final boolean kFrontRightSteerMotorInverted = true; + private static final boolean kFrontRightEncoderInverted = false; + + private static final Distance kFrontRightXPos = Inches.of(11.5); + private static final Distance kFrontRightYPos = Inches.of(-11.5); + + // Back Left + private static final int kBackLeftDriveMotorId = 3; + private static final int kBackLeftSteerMotorId = 7; + private static final int kBackLeftEncoderId = 12; + private static final Angle kBackLeftEncoderOffset = Rotations.of(-0.018798828125 + 0.25); + private static final boolean kBackLeftSteerMotorInverted = true; + private static final boolean kBackLeftEncoderInverted = false; + + private static final Distance kBackLeftXPos = Inches.of(-11.5); + private static final Distance kBackLeftYPos = Inches.of(11.5); + + // Back Right + private static final int kBackRightDriveMotorId = 4; + private static final int kBackRightSteerMotorId = 8; + private static final int kBackRightEncoderId = 13; + private static final Angle kBackRightEncoderOffset = Rotations.of(0.477294921875 + 0.25); + private static final boolean kBackRightSteerMotorInverted = true; + private static final boolean kBackRightEncoderInverted = false; + + private static final Distance kBackRightXPos = Inches.of(-11.5); + private static final Distance kBackRightYPos = Inches.of(-11.5); + + public static final SwerveModuleConstants< + TalonFXConfiguration, TalonFXConfiguration, CANcoderConfiguration> + FrontLeft = + ConstantCreator.createModuleConstants( + kFrontLeftSteerMotorId, + kFrontLeftDriveMotorId, + kFrontLeftEncoderId, + kFrontLeftEncoderOffset, + kFrontLeftXPos, + kFrontLeftYPos, + false, + kFrontLeftSteerMotorInverted, + kFrontLeftEncoderInverted); + public static final SwerveModuleConstants< + TalonFXConfiguration, TalonFXConfiguration, CANcoderConfiguration> + FrontRight = + ConstantCreator.createModuleConstants( + kFrontRightSteerMotorId, + kFrontRightDriveMotorId, + kFrontRightEncoderId, + kFrontRightEncoderOffset, + kFrontRightXPos, + kFrontRightYPos, + kInvertRightSide, + kFrontRightSteerMotorInverted, + kFrontRightEncoderInverted); + public static final SwerveModuleConstants< + TalonFXConfiguration, TalonFXConfiguration, CANcoderConfiguration> + BackLeft = + ConstantCreator.createModuleConstants( + kBackLeftSteerMotorId, + kBackLeftDriveMotorId, + kBackLeftEncoderId, + kBackLeftEncoderOffset, + kBackLeftXPos, + kBackLeftYPos, + true, + kBackLeftSteerMotorInverted, + kBackLeftEncoderInverted); + public static final SwerveModuleConstants< + TalonFXConfiguration, TalonFXConfiguration, CANcoderConfiguration> + BackRight = + ConstantCreator.createModuleConstants( + kBackRightSteerMotorId, + kBackRightDriveMotorId, + kBackRightEncoderId, + kBackRightEncoderOffset, + kBackRightXPos, + kBackRightYPos, + true, + kBackRightSteerMotorInverted, + kBackRightEncoderInverted); + + /** Swerve Drive class utilizing CTR Electronics' Phoenix 6 API with the selected device types. */ + public static class TunerSwerveDrivetrain extends SwerveDrivetrain { + /** + * Constructs a CTRE SwerveDrivetrain using the specified constants. + * + *

This constructs the underlying hardware devices, so users should not construct the devices + * themselves. If they need the devices, they can access them through getters in the classes. + * + * @param drivetrainConstants Drivetrain-wide constants for the swerve drive + * @param modules Constants for each specific module + */ + public TunerSwerveDrivetrain( + SwerveDrivetrainConstants drivetrainConstants, SwerveModuleConstants... modules) { + super(TalonFX::new, TalonFX::new, CANcoder::new, drivetrainConstants, modules); + } + + /** + * Constructs a CTRE SwerveDrivetrain using the specified constants. + * + *

This constructs the underlying hardware devices, so users should not construct the devices + * themselves. If they need the devices, they can access them through getters in the classes. + * + * @param drivetrainConstants Drivetrain-wide constants for the swerve drive + * @param odometryUpdateFrequency The frequency to run the odometry loop. If unspecified or set + * to 0 Hz, this is 250 Hz on CAN FD, and 100 Hz on CAN 2.0. + * @param modules Constants for each specific module + */ + public TunerSwerveDrivetrain( + SwerveDrivetrainConstants drivetrainConstants, + double odometryUpdateFrequency, + SwerveModuleConstants... modules) { + super( + TalonFX::new, + TalonFX::new, + CANcoder::new, + drivetrainConstants, + odometryUpdateFrequency, + modules); + } + + /** + * Constructs a CTRE SwerveDrivetrain using the specified constants. + * + *

This constructs the underlying hardware devices, so users should not construct the devices + * themselves. If they need the devices, they can access them through getters in the classes. + * + * @param drivetrainConstants Drivetrain-wide constants for the swerve drive + * @param odometryUpdateFrequency The frequency to run the odometry loop. If unspecified or set + * to 0 Hz, this is 250 Hz on CAN FD, and 100 Hz on CAN 2.0. + * @param odometryStandardDeviation The standard deviation for odometry calculation in the form + * [x, y, theta], with units in meters and radians + * @param visionStandardDeviation The standard deviation for vision calculation in the form [x, + * y, theta], with units in meters and radians + * @param modules Constants for each specific module + */ + public TunerSwerveDrivetrain( + SwerveDrivetrainConstants drivetrainConstants, + double odometryUpdateFrequency, + Matrix odometryStandardDeviation, + Matrix visionStandardDeviation, + SwerveModuleConstants... modules) { + super( + TalonFX::new, + TalonFX::new, + CANcoder::new, + drivetrainConstants, + odometryUpdateFrequency, + odometryStandardDeviation, + visionStandardDeviation, + modules); + } + } +} diff --git a/lib/examples/swerve/src/main/java/frc/robot/util/Alert.java b/lib/examples/swerve/src/main/java/frc/robot/util/Alert.java new file mode 100644 index 00000000..e53f04eb --- /dev/null +++ b/lib/examples/swerve/src/main/java/frc/robot/util/Alert.java @@ -0,0 +1,148 @@ +// Copyright (c) 2023 FRC 6328 +// http://github.com/Mechanical-Advantage +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file at +// the root directory of this project. + +package frc.robot.util; + +import edu.wpi.first.util.sendable.Sendable; +import edu.wpi.first.util.sendable.SendableBuilder; +import edu.wpi.first.wpilibj.DriverStation; +import edu.wpi.first.wpilibj.Timer; +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Predicate; +import lombok.Getter; + +/** Class for managing persistent alerts to be sent over NetworkTables. */ +public class Alert { + private static final Map groups = new HashMap<>(); + + private final AlertType type; + @Getter private boolean active = false; + private double activeStartTime = 0.0; + private String text; + + /** + * Creates a new Alert in the default group - "Alerts". If this is the first to be instantiated, + * the appropriate entries will be added to NetworkTables. + * + * @param text Text to be displayed when the alert is active. + * @param type Alert level specifying urgency. + */ + public Alert(String text, AlertType type) { + this("Alerts", text, type); + } + + /** + * Creates a new Alert. If this is the first to be instantiated in its group, the appropriate + * entries will be added to NetworkTables. + * + * @param group Group identifier, also used as NetworkTables title + * @param text Text to be displayed when the alert is active. + * @param type Alert level specifying urgency. + */ + public Alert(String group, String text, AlertType type) { + if (!groups.containsKey(group)) { + groups.put(group, new SendableAlerts()); + SmartDashboard.putData(group, groups.get(group)); + } + + this.text = text; + this.type = type; + groups.get(group).alerts.add(this); + } + + /** + * Sets whether the alert should currently be displayed. When activated, the alert text will also + * be sent to the console. + */ + public void set(boolean active) { + if (active && !this.active) { + activeStartTime = Timer.getTimestamp(); + switch (type) { + case ERROR: + DriverStation.reportError(text, false); + break; + case WARNING: + DriverStation.reportWarning(text, false); + break; + case INFO: + System.out.println(text); + break; + } + } + this.active = active; + } + + /** Updates current alert text. */ + public void setText(String text) { + if (active && !text.equals(this.text)) { + switch (type) { + case ERROR: + DriverStation.reportError(text, false); + break; + case WARNING: + DriverStation.reportWarning(text, false); + break; + case INFO: + System.out.println(text); + break; + } + } + this.text = text; + } + + /** Represents an alert's level of urgency. */ + public enum AlertType { + /** + * High priority alert - displayed first on the dashboard with a red "X" symbol. Use this type + * for problems which will seriously affect the robot's functionality and thus require immediate + * attention. + */ + ERROR, + + /** + * Medium priority alert - displayed second on the dashboard with a yellow "!" symbol. Use this + * type for problems which could affect the robot's functionality but do not necessarily require + * immediate attention. + */ + WARNING, + + /** + * Low priority alert - displayed last on the dashboard with a green "i" symbol. Use this type + * for problems which are unlikely to affect the robot's functionality, or any other alerts + * which do not fall under "ERROR" or "WARNING". + */ + INFO + } + + private static class SendableAlerts implements Sendable { + public final List alerts = new ArrayList<>(); + + public String[] getStrings(AlertType type) { + Predicate activeFilter = (Alert x) -> x.type == type && x.active; + Comparator timeSorter = + (Alert a1, Alert a2) -> (int) (a2.activeStartTime - a1.activeStartTime); + return alerts.stream() + .filter(activeFilter) + .sorted(timeSorter) + .map((Alert a) -> a.text) + .toArray(String[]::new); + } + + @Override + public void initSendable(SendableBuilder builder) { + builder.setSmartDashboardType("Alerts"); + builder.addStringArrayProperty("errors", () -> getStrings(AlertType.ERROR), null); + builder.addStringArrayProperty("warnings", () -> getStrings(AlertType.WARNING), null); + builder.addStringArrayProperty("infos", () -> getStrings(AlertType.INFO), null); + } + } +} diff --git a/lib/examples/swerve/src/main/java/frc/robot/util/AllianceFlipUtil.java b/lib/examples/swerve/src/main/java/frc/robot/util/AllianceFlipUtil.java new file mode 100644 index 00000000..ca3ed684 --- /dev/null +++ b/lib/examples/swerve/src/main/java/frc/robot/util/AllianceFlipUtil.java @@ -0,0 +1,58 @@ +package frc.robot.util; + +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.math.util.Units; +import edu.wpi.first.wpilibj.DriverStation; + +public class AllianceFlipUtil { + public static double fieldWidth = Units.feetToMeters(26.0) + Units.inchesToMeters(5.0); + public static double fieldLength = Units.feetToMeters(57.0) + Units.inchesToMeters(6.875); + + public static double applyX(double x) { + return shouldFlip() ? fieldLength - x : x; + } + + public static double applyY(double y) { + return shouldFlip() ? fieldWidth - y : y; + } + + public static Translation2d apply(Translation2d translation) { + return new Translation2d(applyX(translation.getX()), applyY(translation.getY())); + } + + public static Rotation2d apply(Rotation2d rotation) { + return shouldFlip() ? rotation.rotateBy(Rotation2d.kPi) : rotation; + } + + public static Pose2d apply(Pose2d pose) { + return new Pose2d(apply(pose.getTranslation()), apply(pose.getRotation())); + } + + public static double overrideApplyX(double x) { + return fieldLength - x; + } + + public static double overrideApplyY(double y) { + return fieldWidth - y; + } + + public static Translation2d overrideApply(Translation2d translation) { + return new Translation2d( + overrideApplyX(translation.getX()), overrideApplyY(translation.getY())); + } + + public static Rotation2d overrideApply(Rotation2d rotation) { + return rotation.rotateBy(Rotation2d.kPi); + } + + public static Pose2d overrideApply(Pose2d pose) { + return new Pose2d(overrideApply(pose.getTranslation()), overrideApply(pose.getRotation())); + } + + public static boolean shouldFlip() { + return DriverStation.getAlliance().isPresent() + && DriverStation.getAlliance().get() == DriverStation.Alliance.Red; + } +} diff --git a/lib/examples/swerve/src/main/java/frc/robot/util/Elastic.java b/lib/examples/swerve/src/main/java/frc/robot/util/Elastic.java new file mode 100644 index 00000000..d9a1ed7d --- /dev/null +++ b/lib/examples/swerve/src/main/java/frc/robot/util/Elastic.java @@ -0,0 +1,321 @@ +// Copyright (c) 2023-2025 Gold87 and other Elastic contributors +// This software can be modified and/or shared under the terms +// defined by the Elastic license: +// https://github.com/Gold872/elastic_dashboard/blob/main/LICENSE + +package frc.robot.util; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import edu.wpi.first.networktables.NetworkTableInstance; +import edu.wpi.first.networktables.PubSubOption; +import edu.wpi.first.networktables.StringPublisher; +import edu.wpi.first.networktables.StringTopic; +import lombok.Getter; +import lombok.Setter; + +public final class Elastic { + private static final StringTopic notificationTopic = + NetworkTableInstance.getDefault().getStringTopic("/Elastic/RobotNotifications"); + private static final StringPublisher notificationPublisher = + notificationTopic.publish(PubSubOption.sendAll(true), PubSubOption.keepDuplicates(true)); + private static final StringTopic selectedTabTopic = + NetworkTableInstance.getDefault().getStringTopic("/Elastic/SelectedTab"); + private static final StringPublisher selectedTabPublisher = + selectedTabTopic.publish(PubSubOption.keepDuplicates(true)); + private static final ObjectMapper objectMapper = new ObjectMapper(); + + /** + * Sends an notification to the Elastic dashboard. The notification is serialized as a JSON string + * before being published. + * + * @param notification the {@link Notification} object containing notification details + */ + public static void sendNotification(Notification notification) { + try { + notificationPublisher.set(objectMapper.writeValueAsString(notification)); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + } + + /** + * Selects the tab of the dashboard with the given name. If no tab matches the name, this will + * have no effect on the widgets or tabs in view. + * + *

If the given name is a number, Elastic will select the tab whose index equals the number + * provided. + * + * @param tabName the name of the tab to select + */ + public static void selectTab(String tabName) { + selectedTabPublisher.set(tabName); + } + + /** + * Selects the tab of the dashboard at the given index. If this index is greater than or equal to + * the number of tabs, this will have no effect. + * + * @param tabIndex the index of the tab to select. + */ + public static void selectTab(int tabIndex) { + selectTab(Integer.toString(tabIndex)); + } + + /** + * Represents the possible levels of notifications for the Elastic dashboard. These levels are + * used to indicate the severity or type of notification. + */ + public enum NotificationLevel { + /** Informational Message */ + INFO, + /** Warning message */ + WARNING, + /** Error message */ + ERROR + } + + /** + * Represents an notification object to be sent to the Elastic dashboard. This object holds + * properties such as level, title, description, display time, and dimensions to control how the + * notification is displayed on the dashboard. + */ + @Setter + @Getter + public static class Notification { + /** + * Updates the level of this notification + * + * @return the level of this notification + * @param level the level to set the notification to + */ + private NotificationLevel level; + + /** + * Updates the title of this notification + * + * @return the title of this notification + * @param title the title to set the notification to + */ + private String title; + + /** + * Updates the description of this notification + * + * @param description the description to set the notification to + */ + private String description; + + /** + * Updates the display time of the notification in milliseconds + * + * @return the number of milliseconds the notification is displayed for + * @param displayTimeMillis the number of milliseconds to display the notification for + */ + private int displayTimeMillis; + + /** + * Updates the width of the notification + * + * @return the width of the notification + * @param width the width to set the notification to + */ + private double width; + + /** + * Updates the height of the notification + * + *

If the height is set to -1, the height will be determined automatically by the dashboard + * + * @return the height of the notification + * @param height the height to set the notification to + */ + private double height; + + /** + * Creates a new Notification with all default parameters. This constructor is intended to be + * used with the chainable decorator methods + * + *

Title and description fields are empty. + */ + public Notification() { + this(NotificationLevel.INFO, "", ""); + } + + /** + * Creates a new Notification with all properties specified. + * + * @param level the level of the notification (e.g., INFO, WARNING, ERROR) + * @param title the title text of the notification + * @param description the descriptive text of the notification + * @param displayTimeMillis the time in milliseconds for which the notification is displayed + * @param width the width of the notification display area + * @param height the height of the notification display area, inferred if below zero + */ + public Notification( + NotificationLevel level, + String title, + String description, + int displayTimeMillis, + double width, + double height) { + this.level = level; + this.title = title; + this.displayTimeMillis = displayTimeMillis; + this.description = description; + this.height = height; + this.width = width; + } + + /** + * Creates a new Notification with default display time and dimensions. + * + * @param level the level of the notification + * @param title the title text of the notification + * @param description the descriptive text of the notification + */ + public Notification(NotificationLevel level, String title, String description) { + this(level, title, description, 3000, 350, -1); + } + + /** + * Creates a new Notification with a specified display time and default dimensions. + * + * @param level the level of the notification + * @param title the title text of the notification + * @param description the descriptive text of the notification + * @param displayTimeMillis the display time in milliseconds + */ + public Notification( + NotificationLevel level, String title, String description, int displayTimeMillis) { + this(level, title, description, displayTimeMillis, 350, -1); + } + + /** + * Creates a new Notification with specified dimensions and default display time. If the height + * is below zero, it is automatically inferred based on screen size. + * + * @param level the level of the notification + * @param title the title text of the notification + * @param description the descriptive text of the notification + * @param width the width of the notification display area + * @param height the height of the notification display area, inferred if below zero + */ + public Notification( + NotificationLevel level, String title, String description, double width, double height) { + this(level, title, description, 3000, width, height); + } + + /** + * Updates the display time of the notification + * + * @param seconds the number of seconds to display the notification for + */ + public void setDisplayTimeSeconds(double seconds) { + setDisplayTimeMillis((int) Math.round(seconds * 1000)); + } + + /** + * Modifies the notification's level and returns itself to allow for method chaining + * + * @param level the level to set the notification to + * @return the current notification + */ + public Notification withLevel(NotificationLevel level) { + this.level = level; + return this; + } + + /** + * Modifies the notification's title and returns itself to allow for method chaining + * + * @param title the title to set the notification to + * @return the current notification + */ + public Notification withTitle(String title) { + setTitle(title); + return this; + } + + /** + * Modifies the notification's description and returns itself to allow for method chaining + * + * @param description the description to set the notification to + * @return the current notification + */ + public Notification withDescription(String description) { + setDescription(description); + return this; + } + + /** + * Modifies the notification's display time and returns itself to allow for method chaining + * + * @param seconds the number of seconds to display the notification for + * @return the current notification + */ + public Notification withDisplaySeconds(double seconds) { + return withDisplayMilliseconds((int) Math.round(seconds * 1000)); + } + + /** + * Modifies the notification's display time and returns itself to allow for method chaining + * + * @param displayTimeMillis the number of milliseconds to display the notification for + * @return the current notification + */ + public Notification withDisplayMilliseconds(int displayTimeMillis) { + setDisplayTimeMillis(displayTimeMillis); + return this; + } + + /** + * Modifies the notification's width and returns itself to allow for method chaining + * + * @param width the width to set the notification to + * @return the current notification + */ + public Notification withWidth(double width) { + setWidth(width); + return this; + } + + /** + * Modifies the notification's height and returns itself to allow for method chaining + * + * @param height the height to set the notification to + * @return the current notification + */ + public Notification withHeight(double height) { + setHeight(height); + return this; + } + + /** + * Modifies the notification's height and returns itself to allow for method chaining + * + *

This will set the height to -1 to have it automatically determined by the dashboard + * + * @return the current notification + */ + public Notification withAutomaticHeight() { + setHeight(-1); + return this; + } + + /** + * Modifies the notification to disable the auto dismiss behavior + * + *

This sets the display time to 0 milliseconds + * + *

The auto dismiss behavior can be re-enabled by setting the display time to a number + * greater than 0 + * + * @return the current notification + */ + public Notification withNoAutoDismiss() { + setDisplayTimeMillis(0); + return this; + } + } +} diff --git a/lib/examples/swerve/src/main/java/frc/robot/util/InternalLoggedTracer.java b/lib/examples/swerve/src/main/java/frc/robot/util/InternalLoggedTracer.java new file mode 100644 index 00000000..745e4baa --- /dev/null +++ b/lib/examples/swerve/src/main/java/frc/robot/util/InternalLoggedTracer.java @@ -0,0 +1,31 @@ +// Copyright (c) 2025 FRC 6328 +// http://github.com/Mechanical-Advantage +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file at +// the root directory of this project. + +package frc.robot.util; + +import edu.wpi.first.wpilibj.Timer; +import org.littletonrobotics.junction.Logger; + +/** Utility class for logging code execution times. */ +public class InternalLoggedTracer { + private InternalLoggedTracer() {} + + private static double startTime = -1.0; + + /** Reset the clock. */ + public static void reset() { + startTime = Timer.getFPGATimestamp(); + } + + /** Save the time elapsed since the last reset or record. */ + public static void record(String epochName, String prefix) { + double now = Timer.getFPGATimestamp(); + Logger.recordOutput( + "LoggedTracer/" + prefix + "/" + epochName + "MS", (now - startTime) * 1000.0); + startTime = now; + } +} diff --git a/lib/examples/swerve/src/main/java/frc/robot/util/LTNUpdater.java b/lib/examples/swerve/src/main/java/frc/robot/util/LTNUpdater.java new file mode 100644 index 00000000..5498c297 --- /dev/null +++ b/lib/examples/swerve/src/main/java/frc/robot/util/LTNUpdater.java @@ -0,0 +1 @@ +package frc.robot.util; diff --git a/lib/examples/swerve/src/main/java/frc/robot/util/NTPrefixes.java b/lib/examples/swerve/src/main/java/frc/robot/util/NTPrefixes.java new file mode 100644 index 00000000..2f7e564d --- /dev/null +++ b/lib/examples/swerve/src/main/java/frc/robot/util/NTPrefixes.java @@ -0,0 +1,11 @@ +package frc.robot.util; + +public class NTPrefixes { + public static final String ROBOT_STATE = "RobotState/"; + public static final String OI_DATA = ROBOT_STATE + "Operator Input Data/"; + public static final String POSE_DATA = ROBOT_STATE + "Pose Data/"; + + public static final String CANIVORE_STATUS = ROBOT_STATE + "CANivore Status/"; + + public static final String SUPERSTRUCTURE = "Superstructure/"; +} diff --git a/lib/examples/swerve/src/main/java/frc/robot/util/ShotCalculator.java b/lib/examples/swerve/src/main/java/frc/robot/util/ShotCalculator.java new file mode 100644 index 00000000..419969a4 --- /dev/null +++ b/lib/examples/swerve/src/main/java/frc/robot/util/ShotCalculator.java @@ -0,0 +1,65 @@ +package frc.robot.util; + +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Transform2d; +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.math.geometry.Twist2d; +import edu.wpi.first.math.kinematics.ChassisSpeeds; +import java.util.function.Function; + +public interface ShotCalculator { + static final double phaseDelay = 0.3; // TODO: Change this value based on testing + + /** + * Calculates a corrected pose for a moving target based on the shooter's current velocity. + * + * @param initialPose The current pose of the shooter. + * @param targetPose The pose of the target. + * @param robotVelocityMetersPerSecond The shooter's velocity in meters per second. + * @param distanceToTimeFunction A function that converts distance to time. + * @return The corrected pose to aim at. + */ + public default Translation2d getAdjustedTargetPose( + Pose2d initialPose, + Pose2d targetPose, + ChassisSpeeds robotVelocityMetersPerSecond, + Function distanceToTimeFunction, + Transform2d centerToShooterCenter) { + + // Adds phase delay to the initial pose based on robot velocity to account for latency caused by + // target pose calculation + initialPose = + initialPose.exp( + new Twist2d( + robotVelocityMetersPerSecond.vxMetersPerSecond * phaseDelay, + robotVelocityMetersPerSecond.vyMetersPerSecond * phaseDelay, + robotVelocityMetersPerSecond.omegaRadiansPerSecond * phaseDelay)); + + Pose2d shooterPose = initialPose.plus(centerToShooterCenter); + Transform2d shooterToTarget = new Transform2d(shooterPose, targetPose); + + Translation2d shooterRobotFrameVelocityMetersPerSecond = + new Translation2d( + -centerToShooterCenter.getRotation().getSin(), + centerToShooterCenter.getRotation().getCos()) + .times(robotVelocityMetersPerSecond.omegaRadiansPerSecond) + .times(centerToShooterCenter.getTranslation().getNorm()); + + Translation2d shooterFieldFrameVelocityMetersPerSecond = + shooterRobotFrameVelocityMetersPerSecond + .rotateBy(initialPose.getRotation()) + .plus( + new Translation2d( + robotVelocityMetersPerSecond.vxMetersPerSecond, + robotVelocityMetersPerSecond.vyMetersPerSecond)); + + double deltaT = distanceToTimeFunction.apply(shooterToTarget.getTranslation().getNorm()); + + double correctedX = + targetPose.getX() - shooterFieldFrameVelocityMetersPerSecond.getX() * deltaT; + double correctedY = + targetPose.getY() - shooterFieldFrameVelocityMetersPerSecond.getY() * deltaT; + + return new Translation2d(correctedX, correctedY); + } +} diff --git a/lib/examples/swerve/src/main/java/frc/robot/util/SystemTimeValidReader.java b/lib/examples/swerve/src/main/java/frc/robot/util/SystemTimeValidReader.java new file mode 100644 index 00000000..6a8ffc85 --- /dev/null +++ b/lib/examples/swerve/src/main/java/frc/robot/util/SystemTimeValidReader.java @@ -0,0 +1,40 @@ +// Copyright (c) 2025 FRC 6328 +// http://github.com/Mechanical-Advantage +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file at +// the root directory of this project. + +package frc.robot.util; + +import edu.wpi.first.wpilibj.RobotController; + +public class SystemTimeValidReader { + private static Thread thread = null; + private static boolean ready = false; + + public static void start() { + if (thread != null) return; + thread = + new Thread( + () -> { + while (true) { + boolean readyNew = RobotController.isSystemTimeValid(); + synchronized (SystemTimeValidReader.class) { + ready = readyNew; + } + try { + Thread.sleep(3000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }); + thread.setName("SystemTimeValidReader"); + thread.start(); + } + + public static synchronized boolean isValid() { + return ready; + } +} diff --git a/lib/examples/swerve/src/main/java/frc/robot/util/input/XKeysInput.java b/lib/examples/swerve/src/main/java/frc/robot/util/input/XKeysInput.java new file mode 100644 index 00000000..879ca577 --- /dev/null +++ b/lib/examples/swerve/src/main/java/frc/robot/util/input/XKeysInput.java @@ -0,0 +1,454 @@ +package frc.robot.util.input; + +import edu.wpi.first.wpilibj.event.EventLoop; +import edu.wpi.first.wpilibj2.command.CommandScheduler; +import edu.wpi.first.wpilibj2.command.button.CommandGenericHID; +import edu.wpi.first.wpilibj2.command.button.Trigger; + +@SuppressWarnings("MethodName") +public class XKeysInput extends CommandGenericHID { + /** + * Construct an instance of a controller. + * + * @param port The port index on the Driver Station that the controller is plugged into. + */ + public XKeysInput(int port) { + super(port); + } + + // Rows are lettered a-h from top to bottom + public enum Rows { + ka(11), + kb(12), + kc(13), + kd(14), + ke(15), + kf(16), + kg(17), + kh(18); + public final int value; + + Rows(int value) { + this.value = value; + } + } + + // Columns are numbered 1-10 from left to right + public enum Cols { + k1(1), + k2(2), + k3(3), + k4(4), + k5(5), + k6(6), + k7(7), + k8(8), + k9(9), + k10(10); + public final int value; + + Cols(int value) { + this.value = value; + } + } + + public Trigger a1() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.ka.value, loop).and(button(Cols.k1.value, loop)); + } + + public Trigger a2() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.ka.value, loop).and(button(Cols.k2.value, loop)); + } + + public Trigger a3() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.ka.value, loop).and(button(Cols.k3.value, loop)); + } + + public Trigger a4() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.ka.value, loop).and(button(Cols.k4.value, loop)); + } + + public Trigger a5() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.ka.value, loop).and(button(Cols.k5.value, loop)); + } + + public Trigger a6() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.ka.value, loop).and(button(Cols.k6.value, loop)); + } + + public Trigger a7() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.ka.value, loop).and(button(Cols.k7.value, loop)); + } + + public Trigger a8() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.ka.value, loop).and(button(Cols.k8.value, loop)); + } + + public Trigger a9() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.ka.value, loop).and(button(Cols.k9.value, loop)); + } + + public Trigger a10() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.ka.value, loop).and(button(Cols.k10.value, loop)); + } + + public Trigger b1() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kb.value, loop).and(button(Cols.k1.value, loop)); + } + + public Trigger b2() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kb.value, loop).and(button(Cols.k2.value, loop)); + } + + public Trigger b3() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kb.value, loop).and(button(Cols.k3.value, loop)); + } + + public Trigger b4() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kb.value, loop).and(button(Cols.k4.value, loop)); + } + + public Trigger b5() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kb.value, loop).and(button(Cols.k5.value, loop)); + } + + public Trigger b6() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kb.value, loop).and(button(Cols.k6.value, loop)); + } + + public Trigger b7() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kb.value, loop).and(button(Cols.k7.value, loop)); + } + + public Trigger b8() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kb.value, loop).and(button(Cols.k8.value, loop)); + } + + public Trigger b9() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kb.value, loop).and(button(Cols.k9.value, loop)); + } + + public Trigger b10() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kb.value, loop).and(button(Cols.k10.value, loop)); + } + + public Trigger c1() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kc.value, loop).and(button(Cols.k1.value, loop)); + } + + public Trigger c2() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kc.value, loop).and(button(Cols.k2.value, loop)); + } + + public Trigger c3() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kc.value, loop).and(button(Cols.k3.value, loop)); + } + + public Trigger c4() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kc.value, loop).and(button(Cols.k4.value, loop)); + } + + public Trigger c5() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kc.value, loop).and(button(Cols.k5.value, loop)); + } + + public Trigger c6() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kc.value, loop).and(button(Cols.k6.value, loop)); + } + + public Trigger c7() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kc.value, loop).and(button(Cols.k7.value, loop)); + } + + public Trigger c8() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kc.value, loop).and(button(Cols.k8.value, loop)); + } + + public Trigger c9() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kc.value, loop).and(button(Cols.k9.value, loop)); + } + + public Trigger c10() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kc.value, loop).and(button(Cols.k10.value, loop)); + } + + public Trigger d1() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kd.value, loop).and(button(Cols.k1.value, loop)); + } + + public Trigger d2() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kd.value, loop).and(button(Cols.k2.value, loop)); + } + + public Trigger d3() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kd.value, loop).and(button(Cols.k3.value, loop)); + } + + public Trigger d4() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kd.value, loop).and(button(Cols.k4.value, loop)); + } + + public Trigger d5() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kd.value, loop).and(button(Cols.k5.value, loop)); + } + + public Trigger d6() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kd.value, loop).and(button(Cols.k6.value, loop)); + } + + public Trigger d7() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kd.value, loop).and(button(Cols.k7.value, loop)); + } + + public Trigger d8() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kd.value, loop).and(button(Cols.k8.value, loop)); + } + + public Trigger d9() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kd.value, loop).and(button(Cols.k9.value, loop)); + } + + public Trigger d10() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kd.value, loop).and(button(Cols.k10.value, loop)); + } + + public Trigger e1() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.ke.value, loop).and(button(Cols.k1.value, loop)); + } + + public Trigger e2() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.ke.value, loop).and(button(Cols.k2.value, loop)); + } + + public Trigger e3() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.ke.value, loop).and(button(Cols.k3.value, loop)); + } + + public Trigger e4() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.ke.value, loop).and(button(Cols.k4.value, loop)); + } + + public Trigger e5() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.ke.value, loop).and(button(Cols.k5.value, loop)); + } + + public Trigger e6() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.ke.value, loop).and(button(Cols.k6.value, loop)); + } + + public Trigger e7() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.ke.value, loop).and(button(Cols.k7.value, loop)); + } + + public Trigger e8() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.ke.value, loop).and(button(Cols.k8.value, loop)); + } + + public Trigger e9() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.ke.value, loop).and(button(Cols.k9.value, loop)); + } + + public Trigger e10() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.ke.value, loop).and(button(Cols.k10.value, loop)); + } + + public Trigger f1() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kf.value, loop).and(button(Cols.k1.value, loop)); + } + + public Trigger f2() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kf.value, loop).and(button(Cols.k2.value, loop)); + } + + public Trigger f3() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kf.value, loop).and(button(Cols.k3.value, loop)); + } + + public Trigger f4() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kf.value, loop).and(button(Cols.k4.value, loop)); + } + + public Trigger f5() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kf.value, loop).and(button(Cols.k5.value, loop)); + } + + public Trigger f6() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kf.value, loop).and(button(Cols.k6.value, loop)); + } + + public Trigger f7() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kf.value, loop).and(button(Cols.k7.value, loop)); + } + + public Trigger f8() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kf.value, loop).and(button(Cols.k8.value, loop)); + } + + public Trigger f9() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kf.value, loop).and(button(Cols.k9.value, loop)); + } + + public Trigger f10() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kf.value, loop).and(button(Cols.k10.value, loop)); + } + + public Trigger g1() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kg.value, loop).and(button(Cols.k1.value, loop)); + } + + public Trigger g2() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kg.value, loop).and(button(Cols.k2.value, loop)); + } + + public Trigger g3() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kg.value, loop).and(button(Cols.k3.value, loop)); + } + + public Trigger g4() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kg.value, loop).and(button(Cols.k4.value, loop)); + } + + public Trigger g5() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kg.value, loop).and(button(Cols.k5.value, loop)); + } + + public Trigger g6() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kg.value, loop).and(button(Cols.k6.value, loop)); + } + + public Trigger g7() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kg.value, loop).and(button(Cols.k7.value, loop)); + } + + public Trigger g8() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kg.value, loop).and(button(Cols.k8.value, loop)); + } + + public Trigger g9() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kg.value, loop).and(button(Cols.k9.value, loop)); + } + + public Trigger g10() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kg.value, loop).and(button(Cols.k10.value, loop)); + } + + public Trigger h1() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kh.value, loop).and(button(Cols.k1.value, loop)); + } + + public Trigger h2() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kh.value, loop).and(button(Cols.k2.value, loop)); + } + + public Trigger h3() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kh.value, loop).and(button(Cols.k3.value, loop)); + } + + public Trigger h4() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kh.value, loop).and(button(Cols.k4.value, loop)); + } + + public Trigger h5() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kh.value, loop).and(button(Cols.k5.value, loop)); + } + + public Trigger h6() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kh.value, loop).and(button(Cols.k6.value, loop)); + } + + public Trigger h7() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kh.value, loop).and(button(Cols.k7.value, loop)); + } + + public Trigger h8() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kh.value, loop).and(button(Cols.k8.value, loop)); + } + + public Trigger h9() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kh.value, loop).and(button(Cols.k9.value, loop)); + } + + public Trigger h10() { + EventLoop loop = CommandScheduler.getInstance().getDefaultButtonLoop(); + return button(Rows.kh.value, loop).and(button(Cols.k10.value, loop)); + } +} diff --git a/lib/examples/swerve/src/main/java/frc/robot/util/input/XboxElite2Input.java b/lib/examples/swerve/src/main/java/frc/robot/util/input/XboxElite2Input.java new file mode 100644 index 00000000..c8f91dbd --- /dev/null +++ b/lib/examples/swerve/src/main/java/frc/robot/util/input/XboxElite2Input.java @@ -0,0 +1,102 @@ +package frc.robot.util.input; + +import edu.wpi.first.wpilibj.XboxController; +import edu.wpi.first.wpilibj.event.EventLoop; +import edu.wpi.first.wpilibj2.command.CommandScheduler; +import edu.wpi.first.wpilibj2.command.button.CommandXboxController; +import edu.wpi.first.wpilibj2.command.button.Trigger; + +public class XboxElite2Input extends CommandXboxController { + + public XboxElite2Input(int port) { + super(port); + } + + /** + * Constructs a Trigger instance around the top left paddle's digital signal. + * + * @return a Trigger instance representing the top left paddle's digital signal attached to the + * {@link CommandScheduler#getDefaultButtonLoop() default scheduler button loop}. + * @see #topLeftPaddle(EventLoop) + */ + public Trigger topLeftPaddle() { + return back(CommandScheduler.getInstance().getDefaultButtonLoop()); + } + + /** + * Constructs a Trigger instance around the top left paddle's digital signal. + * + * @param loop the event loop instance to attach the event to. + * @return a Trigger instance representing the top left paddle's digital signal attached to the + * given loop. + */ + public Trigger topLeftPaddle(EventLoop loop) { + return button(XboxController.Button.kBack.value, loop); + } + + /** + * Constructs a Trigger instance around the top right paddle's digital signal. + * + * @return a Trigger instance representing the top right paddle's digital signal attached to the + * {@link CommandScheduler#getDefaultButtonLoop() default scheduler button loop}. + * @see #topRightPaddle(EventLoop) + */ + public Trigger topRightPaddle() { + return start(CommandScheduler.getInstance().getDefaultButtonLoop()); + } + + /** + * Constructs a Trigger instance around the top right paddle's digital signal. + * + * @param loop the event loop instance to attach the event to. + * @return a Trigger instance representing the top right paddle's digital signal attached to the + * given loop. + */ + public Trigger topRightPaddle(EventLoop loop) { + return button(XboxController.Button.kStart.value, loop); + } + + /** + * Constructs a Trigger instance around the bottom left paddle's digital signal. + * + * @return a Trigger instance representing the bottom left paddle's digital signal attached to the + * {@link CommandScheduler#getDefaultButtonLoop() default scheduler button loop}. + * @see #bottomLeftPaddle(EventLoop) + */ + public Trigger bottomLeftPaddle() { + return leftStick(CommandScheduler.getInstance().getDefaultButtonLoop()); + } + + /** + * Constructs a Trigger instance around the bottom left paddle's digital signal. + * + * @param loop the event loop instance to attach the event to. + * @return a Trigger instance representing the bottom left paddle's digital signal attached to the + * given loop. + */ + public Trigger bottomLeftPaddle(EventLoop loop) { + return button(XboxController.Button.kLeftStick.value, loop); + } + + /** + * Constructs a Trigger instance around the bottom right paddle's digital signal. + * + * @return a Trigger instance representing the bottom right paddle's digital signal attached to + * the {@link CommandScheduler#getDefaultButtonLoop() default scheduler button loop}. + * @see #bottomRightPaddle(EventLoop) + */ + public Trigger bottomRightPaddle() { + return rightStick(CommandScheduler.getInstance().getDefaultButtonLoop()); + } + + /** + * Constructs a Trigger instance around the bottom right paddle's digital signal. + * + * @param loop the event loop instance to attach the event to. + * @return a Trigger instance representing the bottom right paddle's digital signal attached to + * the given loop. + */ + public Trigger bottomRightPaddle(EventLoop loop) { + return button(XboxController.Button.kRightStick.value, loop); + } +} diff --git a/lib/examples/swerve/vendordeps/AdvantageKit.json b/lib/examples/swerve/vendordeps/AdvantageKit.json new file mode 100644 index 00000000..2faa4db8 --- /dev/null +++ b/lib/examples/swerve/vendordeps/AdvantageKit.json @@ -0,0 +1,35 @@ +{ + "fileName": "AdvantageKit.json", + "name": "AdvantageKit", + "version": "26.0.0", + "uuid": "d820cc26-74e3-11ec-90d6-0242ac120003", + "frcYear": "2026", + "mavenUrls": [ + "https://frcmaven.wpi.edu/artifactory/littletonrobotics-mvn-release/" + ], + "jsonUrl": "https://github.com/Mechanical-Advantage/AdvantageKit/releases/latest/download/AdvantageKit.json", + "javaDependencies": [ + { + "groupId": "org.littletonrobotics.akit", + "artifactId": "akit-java", + "version": "26.0.0" + } + ], + "jniDependencies": [ + { + "groupId": "org.littletonrobotics.akit", + "artifactId": "akit-wpilibio", + "version": "26.0.0", + "skipInvalidPlatforms": false, + "isJar": false, + "validPlatforms": [ + "linuxathena", + "linuxx86-64", + "linuxarm64", + "osxuniversal", + "windowsx86-64" + ] + } + ], + "cppDependencies": [] +} diff --git a/lib/examples/swerve/vendordeps/ChoreoLib.json b/lib/examples/swerve/vendordeps/ChoreoLib.json new file mode 100644 index 00000000..cf7e7cf3 --- /dev/null +++ b/lib/examples/swerve/vendordeps/ChoreoLib.json @@ -0,0 +1,44 @@ +{ + "fileName": "ChoreoLib2026.json", + "name": "ChoreoLib", + "version": "2026.0.1", + "uuid": "b5e23f0a-dac9-4ad2-8dd6-02767c520aca", + "frcYear": "2026", + "mavenUrls": [ + "https://frcmaven.wpi.edu/artifactory/sleipnirgroup-mvn-release/", + "https://repo1.maven.org/maven2" + ], + "jsonUrl": "https://choreo.autos/lib/ChoreoLib2026.json", + "javaDependencies": [ + { + "groupId": "choreo", + "artifactId": "ChoreoLib-java", + "version": "2026.0.1" + }, + { + "groupId": "com.google.code.gson", + "artifactId": "gson", + "version": "2.11.0" + } + ], + "jniDependencies": [], + "cppDependencies": [ + { + "groupId": "choreo", + "artifactId": "ChoreoLib-cpp", + "version": "2026.0.1", + "libName": "ChoreoLib", + "headerClassifier": "headers", + "sharedLibrary": false, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal", + "linuxathena", + "linuxarm32", + "linuxarm64" + ] + } + ] +} diff --git a/lib/examples/swerve/vendordeps/Phoenix6-26.1.1.json b/lib/examples/swerve/vendordeps/Phoenix6-26.1.1.json new file mode 100644 index 00000000..b136b65b --- /dev/null +++ b/lib/examples/swerve/vendordeps/Phoenix6-26.1.1.json @@ -0,0 +1,449 @@ +{ + "fileName": "Phoenix6-frc2026-latest.json", + "name": "CTRE-Phoenix (v6)", + "version": "26.1.1", + "frcYear": "2026", + "uuid": "e995de00-2c64-4df5-8831-c1441420ff19", + "mavenUrls": [ + "https://maven.ctr-electronics.com/release/" + ], + "jsonUrl": "https://maven.ctr-electronics.com/release/com/ctre/phoenix6/latest/Phoenix6-frc2026-latest.json", + "conflictsWith": [ + { + "uuid": "e7900d8d-826f-4dca-a1ff-182f658e98af", + "errorMessage": "Users can not have both the replay and regular Phoenix 6 vendordeps in their robot program.", + "offlineFileName": "Phoenix6-replay-frc2026-latest.json" + } + ], + "javaDependencies": [ + { + "groupId": "com.ctre.phoenix6", + "artifactId": "wpiapi-java", + "version": "26.1.1" + } + ], + "jniDependencies": [ + { + "groupId": "com.ctre.phoenix6", + "artifactId": "api-cpp", + "version": "26.1.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6", + "artifactId": "tools", + "version": "26.1.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "api-cpp-sim", + "version": "26.1.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "tools-sim", + "version": "26.1.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simTalonSRX", + "version": "26.1.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simVictorSPX", + "version": "26.1.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simPigeonIMU", + "version": "26.1.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFX", + "version": "26.1.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFXS", + "version": "26.1.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANcoder", + "version": "26.1.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProPigeon2", + "version": "26.1.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANrange", + "version": "26.1.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdi", + "version": "26.1.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdle", + "version": "26.1.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + } + ], + "cppDependencies": [ + { + "groupId": "com.ctre.phoenix6", + "artifactId": "wpiapi-cpp", + "version": "26.1.1", + "libName": "CTRE_Phoenix6_WPI", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6", + "artifactId": "tools", + "version": "26.1.1", + "libName": "CTRE_PhoenixTools", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "wpiapi-cpp-sim", + "version": "26.1.1", + "libName": "CTRE_Phoenix6_WPISim", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "tools-sim", + "version": "26.1.1", + "libName": "CTRE_PhoenixTools_Sim", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simTalonSRX", + "version": "26.1.1", + "libName": "CTRE_SimTalonSRX", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simVictorSPX", + "version": "26.1.1", + "libName": "CTRE_SimVictorSPX", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simPigeonIMU", + "version": "26.1.1", + "libName": "CTRE_SimPigeonIMU", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFX", + "version": "26.1.1", + "libName": "CTRE_SimProTalonFX", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFXS", + "version": "26.1.1", + "libName": "CTRE_SimProTalonFXS", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANcoder", + "version": "26.1.1", + "libName": "CTRE_SimProCANcoder", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProPigeon2", + "version": "26.1.1", + "libName": "CTRE_SimProPigeon2", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANrange", + "version": "26.1.1", + "libName": "CTRE_SimProCANrange", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdi", + "version": "26.1.1", + "libName": "CTRE_SimProCANdi", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdle", + "version": "26.1.1", + "libName": "CTRE_SimProCANdle", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + } + ] +} diff --git a/lib/examples/swerve/vendordeps/WPILibNewCommands.json b/lib/examples/swerve/vendordeps/WPILibNewCommands.json new file mode 100644 index 00000000..d90630e9 --- /dev/null +++ b/lib/examples/swerve/vendordeps/WPILibNewCommands.json @@ -0,0 +1,39 @@ +{ + "fileName": "WPILibNewCommands.json", + "name": "WPILib-New-Commands", + "version": "1.0.0", + "uuid": "111e20f7-815e-48f8-9dd6-e675ce75b266", + "frcYear": "2026", + "mavenUrls": [], + "jsonUrl": "", + "javaDependencies": [ + { + "groupId": "edu.wpi.first.wpilibNewCommands", + "artifactId": "wpilibNewCommands-java", + "version": "wpilib" + } + ], + "jniDependencies": [], + "cppDependencies": [ + { + "groupId": "edu.wpi.first.wpilibNewCommands", + "artifactId": "wpilibNewCommands-cpp", + "version": "wpilib", + "libName": "wpilibNewCommands", + "headerClassifier": "headers", + "sourcesClassifier": "sources", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "linuxsystemcore", + "linuxathena", + "linuxarm32", + "linuxarm64", + "windowsx86-64", + "windowsx86", + "linuxx86-64", + "osxuniversal" + ] + } + ] +} diff --git a/lib/gradle/libs.versions.toml b/lib/gradle/libs.versions.toml new file mode 100644 index 00000000..c4681e12 --- /dev/null +++ b/lib/gradle/libs.versions.toml @@ -0,0 +1,88 @@ + +[versions] +wpilib = "2026.2.1" +wpilib-jni = "2025.3.2" +phoenix6 = "26.1.3" +choreo = "2026.0.2" +aspectj = "1.9.21" +advantagekit = "26.0.2" +quickbuf = "1.4" +jackson = "2.17.2" +pathplanner = "2026.1.2" + +[libraries] +# WPILib Core +wpilib-wpilibj = { group = "edu.wpi.first.wpilibj", name = "wpilibj-java", version.ref = "wpilib" } +wpilib-wpimath = { group = "edu.wpi.first.wpimath", name = "wpimath-java", version.ref = "wpilib" } +wpilib-ntcore = { group = "edu.wpi.first.ntcore", name = "ntcore-java", version.ref = "wpilib" } +wpilib-wpinet = { group = "edu.wpi.first.wpinet", name = "wpinet-java", version.ref = "wpilib" } +wpilib-cscore = { group = "edu.wpi.first.cscore", name = "cscore-java", version.ref = "wpilib" } +wpilib-cameraserver = { group = "edu.wpi.first.cameraserver", name = "cameraserver-java", version.ref = "wpilib" } +wpilib-wpiutil = { group = "edu.wpi.first.wpiutil", name = "wpiutil-java", version.ref = "wpilib" } +wpilib-wpiunits = { group = "edu.wpi.first.wpiunits", name = "wpiunits-java", version.ref = "wpilib" } +wpilib-commands = { group = "edu.wpi.first.wpilibNewCommands", name = "wpilibNewCommands-java", version.ref = "wpilib" } +wpilib-hal = { group = "edu.wpi.first.hal", name = "hal-java", version.ref = "wpilib" } +wpilib-apriltag = {group = "edu.wpi.first.apriltag", name="apriltag-java", version.ref = "wpilib"} + +# WPILib JNI +wpilib-ntcore-jni = { group = "edu.wpi.first.ntcore", name = "ntcore-jni", version.ref = "wpilib-jni" } +wpilib-wpiutil-jni = { group = "edu.wpi.first.wpiutil", name = "wpiutil-jni", version.ref = "wpilib-jni" } +wpilib-hal-jni = { group = "edu.wpi.first.hal", name = "hal-jni", version.ref = "wpilib-jni" } + +# CTRE Phoenix 6/Pro +phoenix6 = { group = "com.ctre.phoenix6", name = "wpiapi-java", version.ref = "phoenix6" } + +# Choreo +choreo = { group = "choreo", name = "ChoreoLib-java", version.ref = "choreo" } + +# PathplannerLib +pathplanner = { group = "com.pathplanner.lib", name = "PathplannerLib-java", version.ref = "pathplanner" } + +# AspectJ +aspectj-rt = { group = "org.aspectj", name = "aspectjrt", version.ref = "aspectj" } +aspectj-tools = { group = "org.aspectj", name = "aspectjtools", version.ref = "aspectj" } + +# AdvantageKit +advantagekit-java = { group = "org.littletonrobotics.akit", name = "akit-java", version.ref = "advantagekit" } +advantagekit-autolog = { group = "org.littletonrobotics.akit", name = "akit-autolog", version.ref = "advantagekit" } + +# QuickBuf / Jackson (runtime dependencies needed for generated code) +quickbuf = { group = "us.hebi.quickbuf", name = "quickbuf-runtime", version.ref = "quickbuf" } +jackson-annotations = { group = "com.fasterxml.jackson.core", name = "jackson-annotations", version.ref = "jackson" } +jackson-core = { group = "com.fasterxml.jackson.core", name = "jackson-core", version.ref = "jackson" } +jackson-databind = { group = "com.fasterxml.jackson.core", name = "jackson-databind", version.ref = "jackson" } + +[bundles] +wpilib = [ + "wpilib-wpilibj", + "wpilib-wpimath", + "wpilib-ntcore", + "wpilib-wpinet", + "wpilib-cscore", + "wpilib-cameraserver", + "wpilib-wpiutil", + "wpilib-wpiunits", + "wpilib-commands", + "wpilib-hal", + "wpilib-apriltag" +] + +wpilib-runtime = [ + "wpilib-ntcore-jni", + "wpilib-wpiutil-jni", + "wpilib-hal-jni" +] + +aspectj = [ + "aspectj-rt", + "aspectj-tools" +] + +advantagekit = [ + "advantagekit-java", + "advantagekit-autolog", + "quickbuf", + "jackson-annotations", + "jackson-core", + "jackson-databind" +] diff --git a/lib/gradle/native-utils.gradle b/lib/gradle/native-utils.gradle new file mode 100644 index 00000000..a99c8348 --- /dev/null +++ b/lib/gradle/native-utils.gradle @@ -0,0 +1,47 @@ +// Detect OS for FRC Natives +def osClassifier = { + def osName = System.getProperty("os.name").toLowerCase() + if (osName.contains("win")) return "windowsx86-64" + if (osName.contains("mac")) return "osxuniversal" + return "linuxx86-64" +}() + +configurations { + nativeDeps +} + +dependencies { + // Uses the version provider from your libs.versions.toml [cite: 4] + def wpilibVer = libs.versions.wpilib.jni.get() + nativeDeps "edu.wpi.first.ntcore:ntcore-jni:${wpilibVer}:${osClassifier}" + nativeDeps "edu.wpi.first.wpiutil:wpiutil-jni:${wpilibVer}:${osClassifier}" + nativeDeps "edu.wpi.first.hal:hal-jni:${wpilibVer}:${osClassifier}" + nativeDeps "edu.wpi.first.wpimath:wpimath-jni:${wpilibVer}:${osClassifier}" + + def phoenix6Ver = libs.versions.phoenix6.get() + nativeDeps "com.ctre.phoenix6:tools:${phoenix6Ver}:${osClassifier}@zip" + nativeDeps "com.ctre.phoenix6.sim:tools-sim:${phoenix6Ver}:${osClassifier}@zip" + nativeDeps "com.ctre.phoenix6:api-cpp:${phoenix6Ver}:${osClassifier}@zip" + nativeDeps "com.ctre.phoenix6.sim:api-cpp-sim:${phoenix6Ver}:${osClassifier}@zip" +} + +def jniDestDir = layout.buildDirectory.dir("jni") + +tasks.register('extractNativeLibs', Copy) { + from { + configurations.nativeDeps.collect { zipTree(it) } + } + into jniDestDir + // Supports Windows (.dll), OSX (.dylib), and Linux (.so) + include "**/*.so", "**/*.dll", "**/*.dylib" + eachFile { path = name } + includeEmptyDirs = false +} + +// Ensure all test tasks depend on extraction and see the library path +tasks.withType(Test).configureEach { + dependsOn extractNativeLibs + // .get().asFile converts the DirectoryProperty to a standard File object + systemProperty "java.library.path", jniDestDir.get().asFile.absolutePath + systemProperty "jni.extract.path", jniDestDir.get().asFile.absolutePath +} diff --git a/lib/gradle/wrapper/gradle-wrapper.jar b/lib/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..249e5832 Binary files /dev/null and b/lib/gradle/wrapper/gradle-wrapper.jar differ diff --git a/lib/gradle/wrapper/gradle-wrapper.properties b/lib/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..99e3a00a --- /dev/null +++ b/lib/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Thu Sep 18 10:41:29 EDT 2025 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/lib/gradlew b/lib/gradlew new file mode 100755 index 00000000..1b6c7873 --- /dev/null +++ b/lib/gradlew @@ -0,0 +1,234 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/lib/gradlew.bat b/lib/gradlew.bat new file mode 100644 index 00000000..ac1b06f9 --- /dev/null +++ b/lib/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/lib/lombok.config b/lib/lombok.config new file mode 100644 index 00000000..8f7e8aa1 --- /dev/null +++ b/lib/lombok.config @@ -0,0 +1 @@ +lombok.addLombokGeneratedAnnotation = true \ No newline at end of file diff --git a/lib/settings.gradle b/lib/settings.gradle new file mode 100644 index 00000000..53311584 --- /dev/null +++ b/lib/settings.gradle @@ -0,0 +1,4 @@ +rootProject.name = 'gompeilib' + +include(":swerve") +project(":swerve").projectDir = file("examples/swerve") diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/core/GompeiLib.java b/lib/src/main/java/edu/wpi/team190/gompeilib/core/GompeiLib.java new file mode 100644 index 00000000..fa26bf97 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/core/GompeiLib.java @@ -0,0 +1,69 @@ +package edu.wpi.team190.gompeilib.core; + +import edu.wpi.team190.gompeilib.core.robot.RobotMode; + +/** Main class for the GompeiLib library. Must be initialized by calling init() in robotInit(). */ +public final class GompeiLib { + + private static boolean initialized = false; + private static RobotMode currentMode; + private static boolean tuningMode; + private static double loopPeriod; + + /** + * Initializes the GompeiLib library with global constants from the robot project. This method + * should be called once in robotInit(). + * + * @param mode The current robot mode (REAL, SIM, or REPLAY). + * @param isTuning Whether tuning mode is enabled. + * @param loopPeriodSecs The robot's main loop period in seconds. + */ + public static void init(RobotMode mode, boolean isTuning, double loopPeriodSecs) { + if (initialized) { + System.err.println("GompeiLib has already been initialized!"); + return; + } + + currentMode = mode; + tuningMode = isTuning; + loopPeriod = loopPeriodSecs; + + initialized = true; + } + + public static void deinit() { + if (!initialized) { + System.err.println("GompeiLib has already been deinitialized!"); + return; + } + + currentMode = null; + tuningMode = false; + loopPeriod = 0.0; + + initialized = false; + } + + /** Throws an exception if the library has not been initialized. */ + private static void checkInitialized() { + if (!initialized) { + throw new IllegalStateException("GompeiLib.init() must be called before using GompeiLib."); + } + } + + // Public getters for other classes in your library to use. + public static RobotMode getMode() { + checkInitialized(); + return currentMode; + } + + public static boolean isTuning() { + checkInitialized(); + return tuningMode; + } + + public static double getLoopPeriod() { + checkInitialized(); + return loopPeriod; + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/core/io/components/inertial/GyroIO.java b/lib/src/main/java/edu/wpi/team190/gompeilib/core/io/components/inertial/GyroIO.java new file mode 100644 index 00000000..6dc31cad --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/core/io/components/inertial/GyroIO.java @@ -0,0 +1,44 @@ +package edu.wpi.team190.gompeilib.core.io.components.inertial; + +import static edu.wpi.first.units.Units.RadiansPerSecond; + +import com.ctre.phoenix6.StatusSignal; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.units.measure.Angle; +import edu.wpi.first.units.measure.AngularVelocity; +import java.util.Queue; +import org.littletonrobotics.junction.AutoLog; + +public interface GyroIO { + @AutoLog + public static class GyroIOInputs { + public boolean connected = false; + public Rotation2d yawPosition = new Rotation2d(); + public double[] odometryYawTimestamps = new double[] {}; + public Rotation2d[] odometryYawPositions = new Rotation2d[] {}; + public double yawVelocityRadPerSec = 0.0; + + public Rotation2d pitchPosition = new Rotation2d(); + public AngularVelocity pitchVelocity = RadiansPerSecond.zero(); + + public Rotation2d rollPosition = new Rotation2d(); + public AngularVelocity rollVelocity = RadiansPerSecond.zero(); + } + + public default void updateInputs(GyroIOInputs inputs) {} + + public default void updateInputs( + GyroIOInputs inputs, Queue yawTimestampQueue, Queue yawPositionQueue) {} + + public default StatusSignal getYaw() { + return null; + } + + public default StatusSignal getRoll() { + return null; + } + + public default StatusSignal getPitch() { + return null; + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/core/io/components/inertial/GyroIOPigeon2.java b/lib/src/main/java/edu/wpi/team190/gompeilib/core/io/components/inertial/GyroIOPigeon2.java new file mode 100644 index 00000000..46a935b3 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/core/io/components/inertial/GyroIOPigeon2.java @@ -0,0 +1,89 @@ +package edu.wpi.team190.gompeilib.core.io.components.inertial; + +import com.ctre.phoenix6.BaseStatusSignal; +import com.ctre.phoenix6.StatusSignal; +import com.ctre.phoenix6.configs.Pigeon2Configuration; +import com.ctre.phoenix6.hardware.Pigeon2; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.util.Units; +import edu.wpi.first.networktables.NetworkTablesJNI; +import edu.wpi.first.units.measure.Angle; +import edu.wpi.first.units.measure.AngularVelocity; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import edu.wpi.team190.gompeilib.core.logging.Trace; +import edu.wpi.team190.gompeilib.core.utility.phoenix.PhoenixUtil; +import edu.wpi.team190.gompeilib.subsystems.drivebases.swervedrive.SwerveDriveConstants; +import java.util.Queue; +import java.util.function.Consumer; +import lombok.Getter; + +/** IO implementation for Pigeon 2. */ +public class GyroIOPigeon2 implements GyroIO { + @Getter private final StatusSignal yaw; + private final StatusSignal yawVelocity; + + @Getter private final StatusSignal pitch; + private final StatusSignal pitchVelocity; + + @Getter private final StatusSignal roll; + private final StatusSignal rollVelocity; + + private final Consumer networktablesTimestampConsumer; + + public GyroIOPigeon2( + SwerveDriveConstants driveConstants, Consumer networkTablesTimestampConsumer) { + Pigeon2 pigeon = + new Pigeon2(driveConstants.driveConfig.pigeon2Id(), driveConstants.driveConfig.canBus()); + pigeon.getConfigurator().apply(new Pigeon2Configuration()); + pigeon.getConfigurator().setYaw(0.0); + + yaw = pigeon.getYaw(); + yawVelocity = pigeon.getAngularVelocityZWorld(); + + yaw.setUpdateFrequency(driveConstants.odometryFrequency); + + pitch = pigeon.getPitch(); + pitchVelocity = pigeon.getAngularVelocityXWorld(); + + roll = pigeon.getRoll(); + rollVelocity = pigeon.getAngularVelocityYWorld(); + + this.networktablesTimestampConsumer = networkTablesTimestampConsumer; + + BaseStatusSignal.setUpdateFrequencyForAll( + 2 / GompeiLib.getLoopPeriod(), yawVelocity, pitch, pitchVelocity, roll, rollVelocity); + + pigeon.optimizeBusUtilization(); + + PhoenixUtil.registerSignals( + driveConstants.driveConfig.canBus().isNetworkFD(), + yaw, + yawVelocity, + pitch, + pitchVelocity, + roll, + rollVelocity); + } + + @Trace + @Override + public void updateInputs( + GyroIOInputs inputs, Queue yawTimestampQueue, Queue yawPositionQueue) { + inputs.connected = + BaseStatusSignal.isAllGood(yaw, yawVelocity, pitch, pitchVelocity, roll, rollVelocity); + inputs.yawPosition = Rotation2d.fromDegrees(yaw.getValueAsDouble()); + networktablesTimestampConsumer.accept(NetworkTablesJNI.now()); + inputs.yawVelocityRadPerSec = Units.degreesToRadians(yawVelocity.getValueAsDouble()); + + inputs.pitchPosition = new Rotation2d(pitch.getValue()); + inputs.pitchVelocity = pitchVelocity.getValue(); + + inputs.rollPosition = new Rotation2d(roll.getValue()); + inputs.rollVelocity = rollVelocity.getValue(); + + inputs.odometryYawTimestamps = + yawTimestampQueue.stream().mapToDouble((Double value) -> value).toArray(); + inputs.odometryYawPositions = + yawPositionQueue.stream().map(Rotation2d::fromDegrees).toArray(Rotation2d[]::new); + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/core/logging/Trace.java b/lib/src/main/java/edu/wpi/team190/gompeilib/core/logging/Trace.java new file mode 100644 index 00000000..a545931a --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/core/logging/Trace.java @@ -0,0 +1,14 @@ +package edu.wpi.team190.gompeilib.core.logging; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Marks a method to be profiled for execution time. The time spent inside any method with this + * annotation will be automatically logged to AdvantageKit. + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Trace {} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/core/logging/TracerAspect.java b/lib/src/main/java/edu/wpi/team190/gompeilib/core/logging/TracerAspect.java new file mode 100644 index 00000000..2365c7b8 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/core/logging/TracerAspect.java @@ -0,0 +1,46 @@ +package edu.wpi.team190.gompeilib.core.logging; + +import edu.wpi.first.wpilibj.Timer; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.reflect.MethodSignature; +import org.littletonrobotics.junction.Logger; + +/** + * An AspectJ aspect that intercepts calls to methods annotated with @Tracer and logs their + * execution time to AdvantageKit's logger. This is the core of the automatic profiling system. + */ +@Aspect +public class TracerAspect { + + /** + * This is the "advice" that runs "around" any method annotated with @Tracer. + * + * @param joinPoint The point in the code where the advice is being executed. + * @return The return value of the original method. + * @throws Throwable If the original method throws an exception. + */ + @Around("execution(@Trace * *.*(..))") + public Object profile(ProceedingJoinPoint joinPoint) throws Throwable { + // Use FPGA timestamp for high-resolution, synchronized timing + double startTime = Timer.getFPGATimestamp(); + + // Proceed with the original method call + Object result = joinPoint.proceed(); + + double endTime = Timer.getFPGATimestamp(); + double executionTimeMs = (endTime - startTime) * 1000.0; + + // Get method details for logging + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + String className = signature.getDeclaringType().getSimpleName(); + String methodName = signature.getName(); + + // Log the result to AdvantageKit Logger under the "Tracer/" directory + String logKey = "Tracer/" + className + "/" + methodName + "MS"; + Logger.recordOutput(logKey, executionTimeMs); + + return result; + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/core/robot/RobotContainer.java b/lib/src/main/java/edu/wpi/team190/gompeilib/core/robot/RobotContainer.java new file mode 100644 index 00000000..42ff76b5 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/core/robot/RobotContainer.java @@ -0,0 +1,13 @@ +package edu.wpi.team190.gompeilib.core.robot; + +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.Commands; + +public interface RobotContainer { + + public default void robotPeriodic() {} + + public default Command getAutonomousCommand() { + return Commands.none(); + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/core/robot/RobotMode.java b/lib/src/main/java/edu/wpi/team190/gompeilib/core/robot/RobotMode.java new file mode 100644 index 00000000..f1935e5c --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/core/robot/RobotMode.java @@ -0,0 +1,7 @@ +package edu.wpi.team190.gompeilib.core.robot; + +public enum RobotMode { + REAL, + SIM, + REPLAY +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/core/robot/RobotState.java b/lib/src/main/java/edu/wpi/team190/gompeilib/core/robot/RobotState.java new file mode 100644 index 00000000..c0a4287f --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/core/robot/RobotState.java @@ -0,0 +1,6 @@ +package edu.wpi.team190.gompeilib.core.robot; + +public interface RobotState { + + public default void periodic() {} +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/core/state/localization/EstimationRegion.java b/lib/src/main/java/edu/wpi/team190/gompeilib/core/state/localization/EstimationRegion.java new file mode 100644 index 00000000..58909d5c --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/core/state/localization/EstimationRegion.java @@ -0,0 +1,145 @@ +package edu.wpi.team190.gompeilib.core.state.localization; + +import edu.wpi.first.apriltag.AprilTag; +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.estimator.SwerveDrivePoseEstimator; +import edu.wpi.first.math.geometry.*; +import edu.wpi.first.math.kinematics.SwerveDriveKinematics; +import edu.wpi.first.math.kinematics.SwerveModulePosition; +import edu.wpi.team190.gompeilib.core.utility.GeometryUtil; +import edu.wpi.team190.gompeilib.subsystems.vision.data.VisionMultiTxTyObservation; +import edu.wpi.team190.gompeilib.subsystems.vision.data.VisionPoseObservation; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import lombok.Getter; + +/** + * Represents an independent localization context that estimates the robot's field-relative pose + * using a constrained subset of vision targets. + * + *

An {@code EstimationRegion} owns its own {@link SwerveDrivePoseEstimator} and is configured + * with a fixed set of AprilTags that are considered valid measurement sources for this region. This + * allows multiple estimators to run in parallel, each optimized for different areas of the field, + * tag groupings, or sensing conditions. + * + *

The estimator continuously integrates drivetrain odometry and accepts vision updates in two + * forms: + * + *

    + *
  • Full field-relative pose observations (e.g. solvePNP-based estimates) + *
  • Angular {@code tx/ty}-based observations that are projected into a 2D field pose using + * known AprilTag locations and the camera transform + *
+ * + * Vision measurements are assumed to originate only from the tags assigned to this region; + * observations referencing unknown tags are ignored. + * + *

This abstraction enables higher-level localization logic to dynamically select or weight + * estimators based on robot position, visibility, confidence, or game-specific constraints, without + * entangling tag-selection logic with the underlying state estimation. + */ +public class EstimationRegion { + @Getter private final Map aprilTags; + private final SwerveDrivePoseEstimator poseEstimator; + + public EstimationRegion(Set aprilTags, SwerveDriveKinematics kinematics) { + this.aprilTags = + aprilTags.stream() + .map(aprilTag -> Map.entry(aprilTag.ID, aprilTag.pose)) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + + SwerveModulePosition[] swerveModulePositions = new SwerveModulePosition[4]; + for (int i = 0; i < swerveModulePositions.length; i++) { + swerveModulePositions[i] = new SwerveModulePosition(); + } + + this.poseEstimator = + new SwerveDrivePoseEstimator( + kinematics, Rotation2d.kZero, swerveModulePositions, Pose2d.kZero); + } + + public void addOdometryObservation( + double timestamp, Rotation2d heading, SwerveModulePosition[] modulePositions) { + poseEstimator.updateWithTime(timestamp, heading, modulePositions); + } + + public void addPoseObservation(VisionPoseObservation observation) { + poseEstimator.addVisionMeasurement( + observation.pose(), observation.timestamp(), observation.stddevs()); + } + + public void addTxTyObservation(VisionMultiTxTyObservation observation) { + + // Get odometry-based pose at the timestamp + var sample = poseEstimator.sampleAt(observation.timestamp()); + if (sample.isEmpty()) return; + + // Average tx and ty over four corners + double tx = 0.0; + double ty = 0.0; + for (int j = 0; j < 4; j++) { + tx += observation.tx()[j]; + ty += observation.ty()[j]; + } + tx /= 4.0; + ty /= 4.0; + + Pose3d cameraPose = observation.cameraPose(); + + // Project 3D distance onto horizontal plane + Rotation3d camToTagRotation3d = new Rotation3d(0.0, -cameraPose.getRotation().getY() - ty, 0.0); + + Translation3d camToTag = new Translation3d(observation.distance(), camToTagRotation3d); + + double distance2d = camToTag.toTranslation2d().getNorm(); + + // Compute rotation from camera to tag + // tx and ty are flipped from WPILib convention. + Rotation2d camToTagRotation2d = + sample + .get() + .getRotation() + .plus(cameraPose.toPose2d().getRotation().plus(Rotation2d.fromRadians(-tx))); + int tagId = observation.tagId(); + + Pose3d tagPose3d = aprilTags.get(tagId); + if (tagPose3d == null) return; + Pose2d tagPose2d = tagPose3d.toPose2d(); + + // Compute camera position in field frame + Rotation2d tagToCameraRotation = camToTagRotation2d.plus(Rotation2d.kPi); + + Translation2d fieldToCameraTranslation = + new Pose2d(tagPose2d.getTranslation(), tagToCameraRotation) + .transformBy(GeometryUtil.toTransform2d(distance2d, 0.0)) + .getTranslation(); + + // Compute robot poses + Pose2d robotPose = + new Pose2d( + fieldToCameraTranslation, + sample.get().getRotation().plus(cameraPose.toPose2d().getRotation())) + .transformBy(new Transform2d(cameraPose.toPose2d(), Pose2d.kZero)); + + // Use odometry rotation only + robotPose = new Pose2d(robotPose.getTranslation(), sample.get().getRotation()); + + double xystdev = + LocalizationConstants.XY_STDDEV_COEFFICIENT + * Math.pow(observation.distance(), LocalizationConstants.XY_STDDEV_DISTANCE_EXPONENT); + + poseEstimator.addVisionMeasurement( + robotPose, + observation.timestamp(), + VecBuilder.fill(xystdev, xystdev, Double.POSITIVE_INFINITY)); + } + + public Pose2d getEstimatedPose() { + return poseEstimator.getEstimatedPosition(); + } + + public void resetPose(Pose2d pose) { + poseEstimator.resetPose(pose); + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/core/state/localization/FieldZone.java b/lib/src/main/java/edu/wpi/team190/gompeilib/core/state/localization/FieldZone.java new file mode 100644 index 00000000..4c7fbf67 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/core/state/localization/FieldZone.java @@ -0,0 +1,27 @@ +package edu.wpi.team190.gompeilib.core.state.localization; + +import edu.wpi.first.apriltag.AprilTag; +import java.util.Set; + +/** + * Defines a logical region of the field in terms of the AprilTags that are considered visible, + * reliable, or relevant within that area. + * + *

A {@code FieldZone} contains no estimation or stateful logic on its own. Instead, it serves as + * a configuration object that describes which vision targets should be associated with a given + * localization strategy or {@link EstimationRegion}. + * + *

By separating field structure from estimation behavior, field zones make it possible to: + * + *

    + *
  • Partition the field into overlapping or disjoint regions + *
  • Restrict vision updates to specific tag subsets + *
  • Swap or combine localization strategies based on robot position + *
+ * + *

This abstraction is intentionally lightweight and immutable, making it safe to share across + * subsystems and reuse throughout the localization stack. + * + * @param aprilTags the set of AprilTags associated with this field zone + */ +public record FieldZone(Set aprilTags) {} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/core/state/localization/Localization.java b/lib/src/main/java/edu/wpi/team190/gompeilib/core/state/localization/Localization.java new file mode 100644 index 00000000..74626d8b --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/core/state/localization/Localization.java @@ -0,0 +1,94 @@ +package edu.wpi.team190.gompeilib.core.state.localization; + +import edu.wpi.first.math.estimator.SwerveDrivePoseEstimator; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.kinematics.SwerveDriveKinematics; +import edu.wpi.first.math.kinematics.SwerveModulePosition; +import edu.wpi.team190.gompeilib.core.utility.GeometryUtil; +import edu.wpi.team190.gompeilib.subsystems.vision.data.VisionMultiTxTyObservation; +import edu.wpi.team190.gompeilib.subsystems.vision.data.VisionPoseObservation; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +public class Localization { + private final List estimationRegions; + + private final SwerveDrivePoseEstimator globalPoseEstimator; + + public Localization( + List estimationZones, + SwerveDriveKinematics kinematics, + double bufferLengthSeconds) { + this.globalPoseEstimator = + new SwerveDrivePoseEstimator( + kinematics, + Rotation2d.kZero, + new SwerveModulePosition[] { + new SwerveModulePosition(), + new SwerveModulePosition(), + new SwerveModulePosition(), + new SwerveModulePosition() + }, + Pose2d.kZero); + + this.estimationRegions = + estimationZones.stream() + .map(zone -> new EstimationRegion(zone.aprilTags(), kinematics)) + .toList(); + } + + public void addOdometryObservation( + double timestamp, Rotation2d rawHeading, SwerveModulePosition[] modulePositions) { + globalPoseEstimator.updateWithTime(timestamp, rawHeading, modulePositions); + estimationRegions.forEach( + region -> region.addOdometryObservation(timestamp, rawHeading, modulePositions)); + } + + public void addPoseObservations(List poseObservations) { + poseObservations.stream() + .filter(observation -> !GeometryUtil.isNaN(observation.pose())) + .forEach( + observation -> + globalPoseEstimator.addVisionMeasurement( + observation.pose(), observation.timestamp(), observation.stddevs())); + estimationRegions.forEach( + zone -> + poseObservations.stream() + .filter( + observation -> zone.getAprilTags().keySet().containsAll(observation.tagIds())) + .filter(observation -> !GeometryUtil.isNaN(observation.pose())) + .forEach(zone::addPoseObservation)); + } + + public void addTxTyObservations(List txTyObservations) { + estimationRegions.forEach( + zone -> + txTyObservations.stream() + .filter(observation -> zone.getAprilTags().containsKey(observation.tagId())) + .forEach(zone::addTxTyObservation)); + } + + public Pose2d getEstimatedPose(FieldZone fieldZone) { + Set zoneTagIDs = + fieldZone.aprilTags().stream().map(t -> t.ID).collect(Collectors.toSet()); + + return estimationRegions.stream() + .filter(region -> zoneTagIDs.equals(region.getAprilTags().keySet())) + .findFirst() + .map(EstimationRegion::getEstimatedPose) + .orElseGet(globalPoseEstimator::getEstimatedPosition); + } + + public Rotation2d getHeading() { + return globalPoseEstimator.getEstimatedPosition().getRotation(); + } + + public void resetPose(Pose2d pose) { + for (EstimationRegion region : estimationRegions) { + region.resetPose(pose); + } + globalPoseEstimator.resetPose(pose); + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/core/state/localization/LocalizationConstants.java b/lib/src/main/java/edu/wpi/team190/gompeilib/core/state/localization/LocalizationConstants.java new file mode 100644 index 00000000..76cfa574 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/core/state/localization/LocalizationConstants.java @@ -0,0 +1,6 @@ +package edu.wpi.team190.gompeilib.core.state.localization; + +public class LocalizationConstants { + public static final double XY_STDDEV_COEFFICIENT = 0.1; + public static final double XY_STDDEV_DISTANCE_EXPONENT = 1.2; +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/GeometryUtil.java b/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/GeometryUtil.java new file mode 100644 index 00000000..22b19075 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/GeometryUtil.java @@ -0,0 +1,228 @@ +package edu.wpi.team190.gompeilib.core.utility; + +import edu.wpi.first.math.geometry.*; + +public class GeometryUtil { + /** + * Creates a pure translating transform + * + * @param translation The translation to create the transform with + * @return The resulting transform + */ + public static Transform2d toTransform2d(Translation2d translation) { + return new Transform2d(translation, new Rotation2d()); + } + + /** + * Creates a pure translating transform + * + * @param x The x coordinate of the translation + * @param y The y coordinate of the translation + * @return The resulting transform + */ + public static Transform2d toTransform2d(double x, double y) { + return new Transform2d(x, y, new Rotation2d()); + } + + /** + * Creates a pure rotating transform + * + * @param rotation The rotation to create the transform with + * @return The resulting transform + */ + public static Transform2d toTransform2d(Rotation2d rotation) { + return new Transform2d(new Translation2d(), rotation); + } + + /** + * Converts a Pose2d to a Transform2d to be used in a kinematic chain + * + * @param pose The pose that will represent the transform + * @return The resulting transform + */ + public static Transform2d toTransform2d(Pose2d pose) { + return new Transform2d(pose.getTranslation(), pose.getRotation()); + } + + /** + * Checks if a pose is (0, 0, 0) + * + * @param pose The pose to check + * @return Whether the pose is zero + */ + public static boolean isZero(Pose2d pose) { + return pose.getX() == 0.0 && pose.getY() == 0.0 && pose.getRotation().getDegrees() == 0.0; + } + + /** + * Checks if any poses are (0, 0, 0) + * + * @param poses The poses to check + * @return Whether any poses are zero + */ + public static boolean isZero(Pose2d[] poses) { + for (Pose2d p : poses) { + if (isZero(p)) { + return true; + } + } + return false; + } + + /** + * Checks if a translation is (0, 0) + * + * @param translation The translation to check + * @return Whether the translation is zero + */ + public static boolean isZero(Translation2d translation) { + return translation.getX() == 0.0 && translation.getY() == 0.0; + } + + /** + * Checks if a rotation is zero + * + * @param rotation The rotation to check + * @return Whether the rotation is zero + */ + public static boolean isZero(Rotation2d rotation) { + return rotation.getDegrees() == 0.0; + } + + /** + * Checks if a pose is NaN + * + * @param pose The pose to check + * @return Whether the pose is NaN + */ + public static boolean isNaN(Pose2d pose) { + return Double.isNaN(pose.getX()) + || Double.isNaN(pose.getY()) + || Double.isNaN(pose.getRotation().getDegrees()); + } + + /** + * Checks if any poses are NaN + * + * @param poses The poses to check + * @return Whether any poses are NaN + */ + public static boolean isNaN(Pose2d[] poses) { + for (Pose2d p : poses) { + if (isNaN(p)) { + return true; + } + } + return false; + } + + /** + * @param rectangle2ds Array of rectangles to check if the pose is in + * @param pose The pose to check is contained by the rectangle + * @return Whether the pose is contained by the rectangle + */ + public static boolean contains(Rectangle2d[] rectangle2ds, Pose2d pose) { + for (Rectangle2d rectangle : rectangle2ds) { + if (rectangle.contains(pose.getTranslation())) { + return true; + } + } + return false; + } + + /** + * @param rectangle2ds Array of rectangles to check against + * @param pose Center of the target rectangle + * @param x Full width of the target rectangle + * @param y Full height of the target rectangle + * @return Whether ANY part of the target rectangle is inside any rectangle + */ + public static boolean intersects(Rectangle2d[] rectangle2ds, Pose2d pose, double x, double y) { + Rectangle2d target = new Rectangle2d(pose, x, y); + + double hx = x / 2.0; + double hy = y / 2.0; + + // Target corners (correctly rotated) + Translation2d[] corners = + new Translation2d[] { + new Translation2d(hx, hy), + new Translation2d(-hx, hy), + new Translation2d(-hx, -hy), + new Translation2d(hx, -hy) + }; + + Translation2d center = pose.getTranslation(); + Rotation2d rot = pose.getRotation(); + + for (Rectangle2d rectangle : rectangle2ds) { + + for (Translation2d corner : corners) { + Translation2d worldCorner = center.plus(corner.rotateBy(rot)); + if (rectangle.contains(worldCorner)) { + return true; + } + } + + if (rectangle.contains(center)) { + return true; + } + + if (target.contains(rectangle.getCenter().getTranslation())) { + return true; + } + } + + return false; + } + + /** + * @param rectangle2ds Array of rectangles to check if the translation is in + * @param translation2d The translation to check is contained by the rectangle + * @return Whether the translation is contained by the rectangle + */ + public static boolean contains(Rectangle2d[] rectangle2ds, Translation2d translation2d) { + for (Rectangle2d rectangle : rectangle2ds) { + if (rectangle.contains(translation2d)) { + return true; + } + } + return false; + } + + /** + * Gets the center and corners of a rectangle2d + * + * @param rectangle2d The rectangle2d to get the center and corners of + * @return The poses from the rectangle2d + */ + public static Pose2d[] rectanglePose2ds(Rectangle2d rectangle2d) { + Pose2d[] poses = new Pose2d[5]; + poses[0] = + rectangle2d + .getCenter() + .transformBy( + new Transform2d( + rectangle2d.getXWidth(), rectangle2d.getYWidth(), new Rotation2d())); + poses[1] = + rectangle2d + .getCenter() + .transformBy( + new Transform2d( + -rectangle2d.getXWidth(), rectangle2d.getYWidth(), new Rotation2d())); + poses[2] = + rectangle2d + .getCenter() + .transformBy( + new Transform2d( + -rectangle2d.getXWidth(), -rectangle2d.getYWidth(), new Rotation2d())); + poses[3] = + rectangle2d + .getCenter() + .transformBy( + new Transform2d( + rectangle2d.getXWidth(), -rectangle2d.getYWidth(), new Rotation2d())); + poses[4] = rectangle2d.getCenter(); + return poses; + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/LimelightHelpers.java b/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/LimelightHelpers.java new file mode 100644 index 00000000..652638a7 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/LimelightHelpers.java @@ -0,0 +1,1987 @@ +// LimelightHelpers v1.14 (REQUIRES LLOS 2026.0 OR LATER) + +package edu.wpi.team190.gompeilib.core.utility; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonFormat.Shape; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Pose3d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Rotation3d; +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.math.geometry.Translation3d; +import edu.wpi.first.math.util.Units; +import edu.wpi.first.net.PortForwarder; +import edu.wpi.first.networktables.DoubleArrayEntry; +import edu.wpi.first.networktables.NetworkTable; +import edu.wpi.first.networktables.NetworkTableEntry; +import edu.wpi.first.networktables.NetworkTableInstance; +import edu.wpi.first.networktables.TimestampedDoubleArray; +import java.util.Arrays; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * LimelightHelpers provides static methods and classes for interfacing with Limelight vision + * cameras in FRC. This library supports all Limelight features including AprilTag tracking, Neural + * Networks, and standard color/retroreflective tracking. + */ +public class LimelightHelpers { + + private static final Map doubleArrayEntries = new ConcurrentHashMap<>(); + + /** Represents a Color/Retroreflective Target Result extracted from JSON Output */ + public static class LimelightTarget_Retro { + + @JsonProperty("t6c_ts") + private double[] cameraPose_TargetSpace; + + @JsonProperty("t6r_fs") + private double[] robotPose_FieldSpace; + + @JsonProperty("t6r_ts") + private double[] robotPose_TargetSpace; + + @JsonProperty("t6t_cs") + private double[] targetPose_CameraSpace; + + @JsonProperty("t6t_rs") + private double[] targetPose_RobotSpace; + + public Pose3d getCameraPose_TargetSpace() { + return toPose3D(cameraPose_TargetSpace); + } + + public Pose3d getRobotPose_FieldSpace() { + return toPose3D(robotPose_FieldSpace); + } + + public Pose3d getRobotPose_TargetSpace() { + return toPose3D(robotPose_TargetSpace); + } + + public Pose3d getTargetPose_CameraSpace() { + return toPose3D(targetPose_CameraSpace); + } + + public Pose3d getTargetPose_RobotSpace() { + return toPose3D(targetPose_RobotSpace); + } + + public Pose2d getCameraPose_TargetSpace2D() { + return toPose2D(cameraPose_TargetSpace); + } + + public Pose2d getRobotPose_FieldSpace2D() { + return toPose2D(robotPose_FieldSpace); + } + + public Pose2d getRobotPose_TargetSpace2D() { + return toPose2D(robotPose_TargetSpace); + } + + public Pose2d getTargetPose_CameraSpace2D() { + return toPose2D(targetPose_CameraSpace); + } + + public Pose2d getTargetPose_RobotSpace2D() { + return toPose2D(targetPose_RobotSpace); + } + + @JsonProperty("ta") + public double ta; + + @JsonProperty("tx") + public double tx; + + @JsonProperty("ty") + public double ty; + + @JsonProperty("txp") + public double tx_pixels; + + @JsonProperty("typ") + public double ty_pixels; + + @JsonProperty("tx_nocross") + public double tx_nocrosshair; + + @JsonProperty("ty_nocross") + public double ty_nocrosshair; + + @JsonProperty("ts") + public double ts; + + public LimelightTarget_Retro() { + cameraPose_TargetSpace = new double[6]; + robotPose_FieldSpace = new double[6]; + robotPose_TargetSpace = new double[6]; + targetPose_CameraSpace = new double[6]; + targetPose_RobotSpace = new double[6]; + } + } + + /** Represents an AprilTag/Fiducial Target Result extracted from JSON Output */ + public static class LimelightTarget_Fiducial { + + @JsonProperty("fID") + public double fiducialID; + + @JsonProperty("fam") + public String fiducialFamily; + + @JsonProperty("t6c_ts") + private double[] cameraPose_TargetSpace; + + @JsonProperty("t6r_fs") + private double[] robotPose_FieldSpace; + + @JsonProperty("t6r_ts") + private double[] robotPose_TargetSpace; + + @JsonProperty("t6t_cs") + private double[] targetPose_CameraSpace; + + @JsonProperty("t6t_rs") + private double[] targetPose_RobotSpace; + + public Pose3d getCameraPose_TargetSpace() { + return toPose3D(cameraPose_TargetSpace); + } + + public Pose3d getRobotPose_FieldSpace() { + return toPose3D(robotPose_FieldSpace); + } + + public Pose3d getRobotPose_TargetSpace() { + return toPose3D(robotPose_TargetSpace); + } + + public Pose3d getTargetPose_CameraSpace() { + return toPose3D(targetPose_CameraSpace); + } + + public Pose3d getTargetPose_RobotSpace() { + return toPose3D(targetPose_RobotSpace); + } + + public Pose2d getCameraPose_TargetSpace2D() { + return toPose2D(cameraPose_TargetSpace); + } + + public Pose2d getRobotPose_FieldSpace2D() { + return toPose2D(robotPose_FieldSpace); + } + + public Pose2d getRobotPose_TargetSpace2D() { + return toPose2D(robotPose_TargetSpace); + } + + public Pose2d getTargetPose_CameraSpace2D() { + return toPose2D(targetPose_CameraSpace); + } + + public Pose2d getTargetPose_RobotSpace2D() { + return toPose2D(targetPose_RobotSpace); + } + + @JsonProperty("ta") + public double ta; + + @JsonProperty("tx") + public double tx; + + @JsonProperty("ty") + public double ty; + + @JsonProperty("txp") + public double tx_pixels; + + @JsonProperty("typ") + public double ty_pixels; + + @JsonProperty("tx_nocross") + public double tx_nocrosshair; + + @JsonProperty("ty_nocross") + public double ty_nocrosshair; + + @JsonProperty("ts") + public double ts; + + public LimelightTarget_Fiducial() { + cameraPose_TargetSpace = new double[6]; + robotPose_FieldSpace = new double[6]; + robotPose_TargetSpace = new double[6]; + targetPose_CameraSpace = new double[6]; + targetPose_RobotSpace = new double[6]; + } + } + + /** Represents a Barcode Target Result extracted from JSON Output */ + public static class LimelightTarget_Barcode { + + /** Barcode family type (e.g. "QR", "DataMatrix", etc.) */ + @JsonProperty("fam") + public String family; + + /** Gets the decoded data content of the barcode */ + @JsonProperty("data") + public String data; + + @JsonProperty("txp") + public double tx_pixels; + + @JsonProperty("typ") + public double ty_pixels; + + @JsonProperty("tx") + public double tx; + + @JsonProperty("ty") + public double ty; + + @JsonProperty("tx_nocross") + public double tx_nocrosshair; + + @JsonProperty("ty_nocross") + public double ty_nocrosshair; + + @JsonProperty("ta") + public double ta; + + @JsonProperty("pts") + public double[][] corners; + + public LimelightTarget_Barcode() {} + + public String getFamily() { + return family; + } + } + + /** Represents a Neural Classifier Pipeline Result extracted from JSON Output */ + public static class LimelightTarget_Classifier { + + @JsonProperty("class") + public String className; + + @JsonProperty("classID") + public double classID; + + @JsonProperty("conf") + public double confidence; + + @JsonProperty("zone") + public double zone; + + @JsonProperty("tx") + public double tx; + + @JsonProperty("txp") + public double tx_pixels; + + @JsonProperty("ty") + public double ty; + + @JsonProperty("typ") + public double ty_pixels; + + public LimelightTarget_Classifier() {} + } + + /** Represents a Neural Detector Pipeline Result extracted from JSON Output */ + public static class LimelightTarget_Detector { + + @JsonProperty("class") + public String className; + + @JsonProperty("classID") + public double classID; + + @JsonProperty("conf") + public double confidence; + + @JsonProperty("ta") + public double ta; + + @JsonProperty("tx") + public double tx; + + @JsonProperty("ty") + public double ty; + + @JsonProperty("txp") + public double tx_pixels; + + @JsonProperty("typ") + public double ty_pixels; + + @JsonProperty("tx_nocross") + public double tx_nocrosshair; + + @JsonProperty("ty_nocross") + public double ty_nocrosshair; + + public LimelightTarget_Detector() {} + } + + /** Represents hardware statistics from the Limelight. */ + public static class HardwareReport { + @JsonProperty("cid") + public String cameraId; + + @JsonProperty("cpu") + public double cpuUsage; + + @JsonProperty("dfree") + public double diskFree; + + @JsonProperty("dtot") + public double diskTotal; + + @JsonProperty("ram") + public double ramUsage; + + @JsonProperty("temp") + public double temperature; + + public HardwareReport() {} + } + + /** Represents IMU data from the JSON results. */ + public static class IMUResults { + @JsonProperty("data") + public double[] data; + + @JsonProperty("quat") + public double[] quaternion; + + @JsonProperty("yaw") + public double yaw; + + // Parsed from data array + public double robotYaw; + public double roll; + public double pitch; + public double rawYaw; + public double gyroZ; + public double gyroX; + public double gyroY; + public double accelZ; + public double accelX; + public double accelY; + + public IMUResults() { + data = new double[0]; + quaternion = new double[4]; + } + + public void parseDataArray() { + if (data != null && data.length >= 10) { + robotYaw = data[0]; + roll = data[1]; + pitch = data[2]; + rawYaw = data[3]; + gyroZ = data[4]; + gyroX = data[5]; + gyroY = data[6]; + accelZ = data[7]; + accelX = data[8]; + accelY = data[9]; + } + } + } + + /** Represents capture rewind buffer statistics. */ + public static class RewindStats { + @JsonProperty("bufferUsage") + public double bufferUsage; + + @JsonProperty("enabled") + public int enabled; + + @JsonProperty("flushing") + public int flushing; + + @JsonProperty("frameCount") + public int frameCount; + + @JsonProperty("latpen") + public int latencyPenalty; + + @JsonProperty("storedSeconds") + public double storedSeconds; + + public RewindStats() {} + } + + /** Limelight Results object, parsed from a Limelight's JSON results output. */ + public static class LimelightResults { + + public String error; + + @JsonProperty("pID") + public double pipelineID; + + @JsonProperty("tl") + public double latency_pipeline; + + @JsonProperty("cl") + public double latency_capture; + + public double latency_jsonParse; + + @JsonProperty("ts") + public double timestamp_LIMELIGHT_publish; + + @JsonProperty("ts_rio") + public double timestamp_RIOFPGA_capture; + + @JsonProperty("ts_nt") + public long timestamp_nt; + + @JsonProperty("ts_sys") + public long timestamp_sys; + + @JsonProperty("ts_us") + public long timestamp_us; + + @JsonProperty("v") + @JsonFormat(shape = Shape.NUMBER) + public boolean valid; + + @JsonProperty("pTYPE") + public String pipelineType; + + @JsonProperty("tx") + public double tx; + + @JsonProperty("ty") + public double ty; + + @JsonProperty("txnc") + public double tx_nocrosshair; + + @JsonProperty("tync") + public double ty_nocrosshair; + + @JsonProperty("ta") + public double ta; + + @JsonProperty("botpose") + public double[] botpose; + + @JsonProperty("botpose_wpired") + public double[] botpose_wpired; + + @JsonProperty("botpose_wpiblue") + public double[] botpose_wpiblue; + + @JsonProperty("botpose_tagcount") + public double botpose_tagcount; + + @JsonProperty("botpose_span") + public double botpose_span; + + @JsonProperty("botpose_avgdist") + public double botpose_avgdist; + + @JsonProperty("botpose_avgarea") + public double botpose_avgarea; + + @JsonProperty("botpose_orb") + public double[] botpose_orb; + + @JsonProperty("botpose_orb_wpiblue") + public double[] botpose_orb_wpiblue; + + @JsonProperty("botpose_orb_wpired") + public double[] botpose_orb_wpired; + + @JsonProperty("t6c_rs") + public double[] camerapose_robotspace; + + @JsonProperty("hw") + public HardwareReport hardware; + + @JsonProperty("imu") + public IMUResults imuResults; + + @JsonProperty("rewind") + public RewindStats rewindStats; + + @JsonProperty("PythonOut") + public double[] pythonOutput; + + public Pose3d getBotPose3d() { + return toPose3D(botpose); + } + + public Pose3d getBotPose3d_wpiRed() { + return toPose3D(botpose_wpired); + } + + public Pose3d getBotPose3d_wpiBlue() { + return toPose3D(botpose_wpiblue); + } + + public Pose2d getBotPose2d() { + return toPose2D(botpose); + } + + public Pose2d getBotPose2d_wpiRed() { + return toPose2D(botpose_wpired); + } + + public Pose2d getBotPose2d_wpiBlue() { + return toPose2D(botpose_wpiblue); + } + + @JsonProperty("Retro") + public LimelightTarget_Retro[] targets_Retro; + + @JsonProperty("Fiducial") + public LimelightTarget_Fiducial[] targets_Fiducials; + + @JsonProperty("Classifier") + public LimelightTarget_Classifier[] targets_Classifier; + + @JsonProperty("Detector") + public LimelightTarget_Detector[] targets_Detector; + + @JsonProperty("Barcode") + public LimelightTarget_Barcode[] targets_Barcode; + + public LimelightResults() { + botpose = new double[6]; + botpose_wpired = new double[6]; + botpose_wpiblue = new double[6]; + botpose_orb = new double[6]; + botpose_orb_wpiblue = new double[6]; + botpose_orb_wpired = new double[6]; + camerapose_robotspace = new double[6]; + targets_Retro = new LimelightTarget_Retro[0]; + targets_Fiducials = new LimelightTarget_Fiducial[0]; + targets_Classifier = new LimelightTarget_Classifier[0]; + targets_Detector = new LimelightTarget_Detector[0]; + targets_Barcode = new LimelightTarget_Barcode[0]; + pythonOutput = new double[0]; + pipelineType = ""; + } + } + + /** Represents a Limelight Raw Fiducial result from Limelight's NetworkTables output. */ + public static class RawFiducial { + public int id = 0; + public double txnc = 0; + public double tync = 0; + public double ta = 0; + public double distToCamera = 0; + public double distToRobot = 0; + public double ambiguity = 0; + + public RawFiducial( + int id, + double txnc, + double tync, + double ta, + double distToCamera, + double distToRobot, + double ambiguity) { + this.id = id; + this.txnc = txnc; + this.tync = tync; + this.ta = ta; + this.distToCamera = distToCamera; + this.distToRobot = distToRobot; + this.ambiguity = ambiguity; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + RawFiducial other = (RawFiducial) obj; + return id == other.id + && Double.compare(txnc, other.txnc) == 0 + && Double.compare(tync, other.tync) == 0 + && Double.compare(ta, other.ta) == 0 + && Double.compare(distToCamera, other.distToCamera) == 0 + && Double.compare(distToRobot, other.distToRobot) == 0 + && Double.compare(ambiguity, other.ambiguity) == 0; + } + } + + /** Represents a Limelight Raw Target/Contour result from Limelight's NetworkTables output. */ + public static class RawTarget { + public double txnc = 0; + public double tync = 0; + public double ta = 0; + + public RawTarget(double txnc, double tync, double ta) { + this.txnc = txnc; + this.tync = tync; + this.ta = ta; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + RawTarget other = (RawTarget) obj; + return Double.compare(txnc, other.txnc) == 0 + && Double.compare(tync, other.tync) == 0 + && Double.compare(ta, other.ta) == 0; + } + } + + /** Represents a Limelight Raw Neural Detector result from Limelight's NetworkTables output. */ + public static class RawDetection { + public int classId = 0; + public double txnc = 0; + public double tync = 0; + public double ta = 0; + public double corner0_X = 0; + public double corner0_Y = 0; + public double corner1_X = 0; + public double corner1_Y = 0; + public double corner2_X = 0; + public double corner2_Y = 0; + public double corner3_X = 0; + public double corner3_Y = 0; + + public RawDetection( + int classId, + double txnc, + double tync, + double ta, + double corner0_X, + double corner0_Y, + double corner1_X, + double corner1_Y, + double corner2_X, + double corner2_Y, + double corner3_X, + double corner3_Y) { + this.classId = classId; + this.txnc = txnc; + this.tync = tync; + this.ta = ta; + this.corner0_X = corner0_X; + this.corner0_Y = corner0_Y; + this.corner1_X = corner1_X; + this.corner1_Y = corner1_Y; + this.corner2_X = corner2_X; + this.corner2_Y = corner2_Y; + this.corner3_X = corner3_X; + this.corner3_Y = corner3_Y; + } + } + + /** Represents a 3D Pose Estimate. */ + public static class PoseEstimate { + public Pose2d pose; + public double timestampSeconds; + public double latency; + public int tagCount; + public double tagSpan; + public double avgTagDist; + public double avgTagArea; + + public RawFiducial[] rawFiducials; + public boolean isMegaTag2; + + /** Instantiates a PoseEstimate object with default values */ + public PoseEstimate() { + this.pose = new Pose2d(); + this.timestampSeconds = 0; + this.latency = 0; + this.tagCount = 0; + this.tagSpan = 0; + this.avgTagDist = 0; + this.avgTagArea = 0; + this.rawFiducials = new RawFiducial[] {}; + this.isMegaTag2 = false; + } + + public PoseEstimate( + Pose2d pose, + double timestampSeconds, + double latency, + int tagCount, + double tagSpan, + double avgTagDist, + double avgTagArea, + RawFiducial[] rawFiducials, + boolean isMegaTag2) { + + this.pose = pose; + this.timestampSeconds = timestampSeconds; + this.latency = latency; + this.tagCount = tagCount; + this.tagSpan = tagSpan; + this.avgTagDist = avgTagDist; + this.avgTagArea = avgTagArea; + this.rawFiducials = rawFiducials; + this.isMegaTag2 = isMegaTag2; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + PoseEstimate that = (PoseEstimate) obj; + // We don't compare the timestampSeconds as it isn't relevant for equality and makes + // unit testing harder + return Double.compare(that.latency, latency) == 0 + && tagCount == that.tagCount + && Double.compare(that.tagSpan, tagSpan) == 0 + && Double.compare(that.avgTagDist, avgTagDist) == 0 + && Double.compare(that.avgTagArea, avgTagArea) == 0 + && pose.equals(that.pose) + && Arrays.equals(rawFiducials, that.rawFiducials); + } + } + + /** Encapsulates the state of an internal Limelight IMU. */ + public static class IMUData { + public double robotYaw = 0.0; + public double Roll = 0.0; + public double Pitch = 0.0; + public double Yaw = 0.0; + public double gyroX = 0.0; + public double gyroY = 0.0; + public double gyroZ = 0.0; + public double accelX = 0.0; + public double accelY = 0.0; + public double accelZ = 0.0; + + public IMUData() {} + + public IMUData(double[] imuData) { + if (imuData != null && imuData.length >= 10) { + this.robotYaw = imuData[0]; + this.Roll = imuData[1]; + this.Pitch = imuData[2]; + this.Yaw = imuData[3]; + this.gyroX = imuData[4]; + this.gyroY = imuData[5]; + this.gyroZ = imuData[6]; + this.accelX = imuData[7]; + this.accelY = imuData[8]; + this.accelZ = imuData[9]; + } + } + } + + private static ObjectMapper mapper; + + /** Print JSON Parse time to the console in milliseconds */ + static boolean profileJSON = false; + + static final String sanitizeName(String name) { + if ("".equals(name) || name == null) { + return "limelight"; + } + return name; + } + + /** + * Takes a 6-length array of pose data and converts it to a Pose3d object. Array format: [x, y, z, + * roll, pitch, yaw] where angles are in degrees. + * + * @param inData Array containing pose data [x, y, z, roll, pitch, yaw] + * @return Pose3d object representing the pose, or empty Pose3d if invalid data + */ + public static Pose3d toPose3D(double[] inData) { + if (inData.length < 6) { + // System.err.println("Bad LL 3D Pose Data!"); + return new Pose3d(); + } + return new Pose3d( + new Translation3d(inData[0], inData[1], inData[2]), + new Rotation3d( + Units.degreesToRadians(inData[3]), + Units.degreesToRadians(inData[4]), + Units.degreesToRadians(inData[5]))); + } + + /** + * Takes a 6-length array of pose data and converts it to a Pose2d object. Uses only x, y, and yaw + * components, ignoring z, roll, and pitch. Array format: [x, y, z, roll, pitch, yaw] where angles + * are in degrees. + * + * @param inData Array containing pose data [x, y, z, roll, pitch, yaw] + * @return Pose2d object representing the pose, or empty Pose2d if invalid data + */ + public static Pose2d toPose2D(double[] inData) { + if (inData.length < 6) { + // System.err.println("Bad LL 2D Pose Data!"); + return new Pose2d(); + } + Translation2d tran2d = new Translation2d(inData[0], inData[1]); + Rotation2d r2d = new Rotation2d(Units.degreesToRadians(inData[5])); + return new Pose2d(tran2d, r2d); + } + + /** + * Converts a Pose3d object to an array of doubles in the format [x, y, z, roll, pitch, yaw]. + * Translation components are in meters, rotation components are in degrees. + * + * @param pose The Pose3d object to convert + * @return A 6-element array containing [x, y, z, roll, pitch, yaw] + */ + public static double[] pose3dToArray(Pose3d pose) { + double[] result = new double[6]; + result[0] = pose.getTranslation().getX(); + result[1] = pose.getTranslation().getY(); + result[2] = pose.getTranslation().getZ(); + result[3] = Units.radiansToDegrees(pose.getRotation().getX()); + result[4] = Units.radiansToDegrees(pose.getRotation().getY()); + result[5] = Units.radiansToDegrees(pose.getRotation().getZ()); + return result; + } + + /** + * Converts a Pose2d object to an array of doubles in the format [x, y, z, roll, pitch, yaw]. + * Translation components are in meters, rotation components are in degrees. Note: z, roll, and + * pitch will be 0 since Pose2d only contains x, y, and yaw. + * + * @param pose The Pose2d object to convert + * @return A 6-element array containing [x, y, 0, 0, 0, yaw] + */ + public static double[] pose2dToArray(Pose2d pose) { + double[] result = new double[6]; + result[0] = pose.getTranslation().getX(); + result[1] = pose.getTranslation().getY(); + result[2] = 0; + result[3] = Units.radiansToDegrees(0); + result[4] = Units.radiansToDegrees(0); + result[5] = Units.radiansToDegrees(pose.getRotation().getRadians()); + return result; + } + + private static double extractArrayEntry(double[] inData, int position) { + if (inData.length < position + 1) { + return 0; + } + return inData[position]; + } + + private static PoseEstimate getBotPoseEstimate( + String limelightName, String entryName, boolean isMegaTag2) { + DoubleArrayEntry poseEntry = + LimelightHelpers.getLimelightDoubleArrayEntry(limelightName, entryName); + + TimestampedDoubleArray tsValue = poseEntry.getAtomic(); + double[] poseArray = tsValue.value; + long timestamp = tsValue.timestamp; + + if (poseArray.length == 0) { + // Handle the case where no data is available + return new PoseEstimate(); + } + + var pose = toPose2D(poseArray); + double latency = extractArrayEntry(poseArray, 6); + int tagCount = (int) extractArrayEntry(poseArray, 7); + double tagSpan = extractArrayEntry(poseArray, 8); + double tagDist = extractArrayEntry(poseArray, 9); + double tagArea = extractArrayEntry(poseArray, 10); + + // Convert server timestamp from microseconds to seconds and adjust for latency + double adjustedTimestamp = (timestamp / 1000000.0) - (latency / 1000.0); + + int valsPerFiducial = 7; + int expectedTotalVals = 11 + valsPerFiducial * tagCount; + RawFiducial[] rawFiducials; + + if (poseArray.length != expectedTotalVals) { + // Array size mismatch - return empty array instead of null-filled array + rawFiducials = new RawFiducial[0]; + } else { + rawFiducials = new RawFiducial[tagCount]; + for (int i = 0; i < tagCount; i++) { + int baseIndex = 11 + (i * valsPerFiducial); + int id = (int) poseArray[baseIndex]; + double txnc = poseArray[baseIndex + 1]; + double tync = poseArray[baseIndex + 2]; + double ta = poseArray[baseIndex + 3]; + double distToCamera = poseArray[baseIndex + 4]; + double distToRobot = poseArray[baseIndex + 5]; + double ambiguity = poseArray[baseIndex + 6]; + rawFiducials[i] = new RawFiducial(id, txnc, tync, ta, distToCamera, distToRobot, ambiguity); + } + } + + return new PoseEstimate( + pose, + adjustedTimestamp, + latency, + tagCount, + tagSpan, + tagDist, + tagArea, + rawFiducials, + isMegaTag2); + } + + /** + * Gets the latest raw fiducial/AprilTag detection results from NetworkTables. + * + * @param limelightName Name/identifier of the Limelight + * @return Array of RawFiducial objects containing detection details + */ + public static RawFiducial[] getRawFiducials(String limelightName) { + var entry = LimelightHelpers.getLimelightNTTableEntry(limelightName, "rawfiducials"); + var rawFiducialArray = entry.getDoubleArray(new double[0]); + int valsPerEntry = 7; + if (rawFiducialArray.length % valsPerEntry != 0) { + return new RawFiducial[0]; + } + + int numFiducials = rawFiducialArray.length / valsPerEntry; + RawFiducial[] rawFiducials = new RawFiducial[numFiducials]; + + for (int i = 0; i < numFiducials; i++) { + int baseIndex = i * valsPerEntry; + int id = (int) extractArrayEntry(rawFiducialArray, baseIndex); + double txnc = extractArrayEntry(rawFiducialArray, baseIndex + 1); + double tync = extractArrayEntry(rawFiducialArray, baseIndex + 2); + double ta = extractArrayEntry(rawFiducialArray, baseIndex + 3); + double distToCamera = extractArrayEntry(rawFiducialArray, baseIndex + 4); + double distToRobot = extractArrayEntry(rawFiducialArray, baseIndex + 5); + double ambiguity = extractArrayEntry(rawFiducialArray, baseIndex + 6); + + rawFiducials[i] = new RawFiducial(id, txnc, tync, ta, distToCamera, distToRobot, ambiguity); + } + + return rawFiducials; + } + + /** + * Gets the latest raw neural detector results from NetworkTables + * + * @param limelightName Name/identifier of the Limelight + * @return Array of RawDetection objects containing detection details + */ + public static RawDetection[] getRawDetections(String limelightName) { + var entry = LimelightHelpers.getLimelightNTTableEntry(limelightName, "rawdetections"); + var rawDetectionArray = entry.getDoubleArray(new double[0]); + int valsPerEntry = 12; + if (rawDetectionArray.length % valsPerEntry != 0) { + return new RawDetection[0]; + } + + int numDetections = rawDetectionArray.length / valsPerEntry; + RawDetection[] rawDetections = new RawDetection[numDetections]; + + for (int i = 0; i < numDetections; i++) { + int baseIndex = i * valsPerEntry; // Starting index for this detection's data + int classId = (int) extractArrayEntry(rawDetectionArray, baseIndex); + double txnc = extractArrayEntry(rawDetectionArray, baseIndex + 1); + double tync = extractArrayEntry(rawDetectionArray, baseIndex + 2); + double ta = extractArrayEntry(rawDetectionArray, baseIndex + 3); + double corner0_X = extractArrayEntry(rawDetectionArray, baseIndex + 4); + double corner0_Y = extractArrayEntry(rawDetectionArray, baseIndex + 5); + double corner1_X = extractArrayEntry(rawDetectionArray, baseIndex + 6); + double corner1_Y = extractArrayEntry(rawDetectionArray, baseIndex + 7); + double corner2_X = extractArrayEntry(rawDetectionArray, baseIndex + 8); + double corner2_Y = extractArrayEntry(rawDetectionArray, baseIndex + 9); + double corner3_X = extractArrayEntry(rawDetectionArray, baseIndex + 10); + double corner3_Y = extractArrayEntry(rawDetectionArray, baseIndex + 11); + + rawDetections[i] = + new RawDetection( + classId, txnc, tync, ta, corner0_X, corner0_Y, corner1_X, corner1_Y, corner2_X, + corner2_Y, corner3_X, corner3_Y); + } + + return rawDetections; + } + + /** + * Gets the raw target contours from NetworkTables. Returns ungrouped contours in normalized + * screen space (-1 to 1). + * + * @param limelightName Name/identifier of the Limelight + * @return Array of RawTarget objects containing up to 3 contours + */ + public static RawTarget[] getRawTargets(String limelightName) { + var entry = LimelightHelpers.getLimelightNTTableEntry(limelightName, "rawtargets"); + var rawTargetArray = entry.getDoubleArray(new double[0]); + int valsPerEntry = 3; + if (rawTargetArray.length % valsPerEntry != 0) { + return new RawTarget[0]; + } + + int numTargets = rawTargetArray.length / valsPerEntry; + RawTarget[] rawTargets = new RawTarget[numTargets]; + + for (int i = 0; i < numTargets; i++) { + int baseIndex = i * valsPerEntry; + double txnc = extractArrayEntry(rawTargetArray, baseIndex); + double tync = extractArrayEntry(rawTargetArray, baseIndex + 1); + double ta = extractArrayEntry(rawTargetArray, baseIndex + 2); + + rawTargets[i] = new RawTarget(txnc, tync, ta); + } + + return rawTargets; + } + + /** + * Gets the corner coordinates of detected targets from NetworkTables. Requires "send contours" to + * be enabled in the Limelight Output tab. + * + * @param limelightName Name/identifier of the Limelight + * @return Array of doubles containing corner coordinates [x0, y0, x1, y1, ...] + */ + public static double[] getCornerCoordinates(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "tcornxy"); + } + + /** + * Prints detailed information about a PoseEstimate to standard output. Includes timestamp, + * latency, tag count, tag span, average tag distance, average tag area, and detailed information + * about each detected fiducial. + * + * @param pose The PoseEstimate object to print. If null, prints "No PoseEstimate available." + */ + public static void printPoseEstimate(PoseEstimate pose) { + if (pose == null) { + System.out.println("No PoseEstimate available."); + return; + } + + System.out.printf("Pose Estimate Information:%n"); + System.out.printf("Timestamp (Seconds): %.3f%n", pose.timestampSeconds); + System.out.printf("Latency: %.3f ms%n", pose.latency); + System.out.printf("Tag Count: %d%n", pose.tagCount); + System.out.printf("Tag Span: %.2f meters%n", pose.tagSpan); + System.out.printf("Average Tag Distance: %.2f meters%n", pose.avgTagDist); + System.out.printf("Average Tag Area: %.2f%% of image%n", pose.avgTagArea); + System.out.printf("Is MegaTag2: %b%n", pose.isMegaTag2); + System.out.println(); + + if (pose.rawFiducials == null || pose.rawFiducials.length == 0) { + System.out.println("No RawFiducials data available."); + return; + } + + System.out.println("Raw Fiducials Details:"); + for (int i = 0; i < pose.rawFiducials.length; i++) { + RawFiducial fiducial = pose.rawFiducials[i]; + System.out.printf(" Fiducial #%d:%n", i + 1); + System.out.printf(" ID: %d%n", fiducial.id); + System.out.printf(" TXNC: %.2f%n", fiducial.txnc); + System.out.printf(" TYNC: %.2f%n", fiducial.tync); + System.out.printf(" TA: %.2f%n", fiducial.ta); + System.out.printf(" Distance to Camera: %.2f meters%n", fiducial.distToCamera); + System.out.printf(" Distance to Robot: %.2f meters%n", fiducial.distToRobot); + System.out.printf(" Ambiguity: %.2f%n", fiducial.ambiguity); + System.out.println(); + } + } + + public static Boolean validPoseEstimate(PoseEstimate pose) { + return pose != null && pose.rawFiducials != null && pose.rawFiducials.length != 0; + } + + public static NetworkTable getLimelightNTTable(String tableName) { + return NetworkTableInstance.getDefault().getTable(sanitizeName(tableName)); + } + + public static void Flush() { + NetworkTableInstance.getDefault().flush(); + } + + public static NetworkTableEntry getLimelightNTTableEntry(String tableName, String entryName) { + return getLimelightNTTable(tableName).getEntry(entryName); + } + + public static DoubleArrayEntry getLimelightDoubleArrayEntry(String tableName, String entryName) { + String key = tableName + "/" + entryName; + return doubleArrayEntries.computeIfAbsent( + key, + k -> { + NetworkTable table = getLimelightNTTable(tableName); + return table.getDoubleArrayTopic(entryName).getEntry(new double[0]); + }); + } + + public static double getLimelightNTDouble(String tableName, String entryName) { + return getLimelightNTTableEntry(tableName, entryName).getDouble(0.0); + } + + public static void setLimelightNTDouble(String tableName, String entryName, double val) { + getLimelightNTTableEntry(tableName, entryName).setDouble(val); + } + + public static void setLimelightNTDoubleArray(String tableName, String entryName, double[] val) { + getLimelightNTTableEntry(tableName, entryName).setDoubleArray(val); + } + + public static double[] getLimelightNTDoubleArray(String tableName, String entryName) { + return getLimelightNTTableEntry(tableName, entryName).getDoubleArray(new double[0]); + } + + public static String getLimelightNTString(String tableName, String entryName) { + return getLimelightNTTableEntry(tableName, entryName).getString(""); + } + + public static String[] getLimelightNTStringArray(String tableName, String entryName) { + return getLimelightNTTableEntry(tableName, entryName).getStringArray(new String[0]); + } + + ///// + + /** + * Does the Limelight have a valid target? + * + * @param limelightName Name of the Limelight camera ("" for default) + * @return True if a valid target is present, false otherwise + */ + public static boolean getTV(String limelightName) { + return 1.0 == getLimelightNTDouble(limelightName, "tv"); + } + + /** + * Gets the horizontal offset from the crosshair to the target in degrees. + * + * @param limelightName Name of the Limelight camera ("" for default) + * @return Horizontal offset angle in degrees + */ + public static double getTX(String limelightName) { + return getLimelightNTDouble(limelightName, "tx"); + } + + /** + * Gets the vertical offset from the crosshair to the target in degrees. + * + * @param limelightName Name of the Limelight camera ("" for default) + * @return Vertical offset angle in degrees + */ + public static double getTY(String limelightName) { + return getLimelightNTDouble(limelightName, "ty"); + } + + /** + * Gets the horizontal offset from the principal pixel/point to the target in degrees. This is the + * most accurate 2d metric if you are using a calibrated camera and you don't need adjustable + * crosshair functionality. + * + * @param limelightName Name of the Limelight camera ("" for default) + * @return Horizontal offset angle in degrees + */ + public static double getTXNC(String limelightName) { + return getLimelightNTDouble(limelightName, "txnc"); + } + + /** + * Gets the vertical offset from the principal pixel/point to the target in degrees. This is the + * most accurate 2d metric if you are using a calibrated camera and you don't need adjustable + * crosshair functionality. + * + * @param limelightName Name of the Limelight camera ("" for default) + * @return Vertical offset angle in degrees + */ + public static double getTYNC(String limelightName) { + return getLimelightNTDouble(limelightName, "tync"); + } + + /** + * Gets the target area as a percentage of the image (0-100%). + * + * @param limelightName Name of the Limelight camera ("" for default) + * @return Target area percentage (0-100) + */ + public static double getTA(String limelightName) { + return getLimelightNTDouble(limelightName, "ta"); + } + + /** + * T2D is an array that contains several targeting metrcis + * + * @param limelightName Name of the Limelight camera + * @return Array containing [targetValid, targetCount, targetLatency, captureLatency, tx, ty, + * txnc, tync, ta, tid, targetClassIndexDetector, targetClassIndexClassifier, + * targetLongSidePixels, targetShortSidePixels, targetHorizontalExtentPixels, + * targetVerticalExtentPixels, targetSkewDegrees] + */ + public static double[] getT2DArray(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "t2d"); + } + + /** + * Gets the number of targets currently detected. + * + * @param limelightName Name of the Limelight camera + * @return Number of detected targets + */ + public static int getTargetCount(String limelightName) { + double[] t2d = getT2DArray(limelightName); + if (t2d.length == 17) { + return (int) t2d[1]; + } + return 0; + } + + /** + * Gets the classifier class index from the currently running neural classifier pipeline + * + * @param limelightName Name of the Limelight camera + * @return Class index from classifier pipeline + */ + public static int getClassifierClassIndex(String limelightName) { + double[] t2d = getT2DArray(limelightName); + if (t2d.length == 17) { + return (int) t2d[11]; + } + return 0; + } + + /** + * Gets the detector class index from the primary result of the currently running neural detector + * pipeline. + * + * @param limelightName Name of the Limelight camera + * @return Class index from detector pipeline + */ + public static int getDetectorClassIndex(String limelightName) { + double[] t2d = getT2DArray(limelightName); + if (t2d.length == 17) { + return (int) t2d[10]; + } + return 0; + } + + /** + * Gets the current neural classifier result class name. + * + * @param limelightName Name of the Limelight camera + * @return Class name string from classifier pipeline + */ + public static String getClassifierClass(String limelightName) { + return getLimelightNTString(limelightName, "tcclass"); + } + + /** + * Gets the primary neural detector result class name. + * + * @param limelightName Name of the Limelight camera + * @return Class name string from detector pipeline + */ + public static String getDetectorClass(String limelightName) { + return getLimelightNTString(limelightName, "tdclass"); + } + + /** + * Gets the pipeline's processing latency contribution. + * + * @param limelightName Name of the Limelight camera + * @return Pipeline latency in milliseconds + */ + public static double getLatency_Pipeline(String limelightName) { + return getLimelightNTDouble(limelightName, "tl"); + } + + /** + * Gets the capture latency. + * + * @param limelightName Name of the Limelight camera + * @return Capture latency in milliseconds + */ + public static double getLatency_Capture(String limelightName) { + return getLimelightNTDouble(limelightName, "cl"); + } + + /** + * Gets the active pipeline index. + * + * @param limelightName Name of the Limelight camera + * @return Current pipeline index (0-9) + */ + public static double getCurrentPipelineIndex(String limelightName) { + return getLimelightNTDouble(limelightName, "getpipe"); + } + + /** + * Gets the current pipeline type. + * + * @param limelightName Name of the Limelight camera + * @return Pipeline type string (e.g. "retro", "apriltag", etc) + */ + public static String getCurrentPipelineType(String limelightName) { + return getLimelightNTString(limelightName, "getpipetype"); + } + + /** + * Gets the full JSON results dump. + * + * @param limelightName Name of the Limelight camera + * @return JSON string containing all current results + */ + public static String getJSONDump(String limelightName) { + return getLimelightNTString(limelightName, "json"); + } + + /** + * Switch to getBotPose + * + * @param limelightName + * @return + */ + @Deprecated + public static double[] getBotpose(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "botpose"); + } + + /** + * Switch to getBotPose_wpiRed + * + * @param limelightName + * @return + */ + @Deprecated + public static double[] getBotpose_wpiRed(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "botpose_wpired"); + } + + /** + * Switch to getBotPose_wpiBlue + * + * @param limelightName + * @return + */ + @Deprecated + public static double[] getBotpose_wpiBlue(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "botpose_wpiblue"); + } + + public static double[] getBotPose(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "botpose"); + } + + public static double[] getBotPose_wpiRed(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "botpose_wpired"); + } + + public static double[] getBotPose_wpiBlue(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "botpose_wpiblue"); + } + + public static double[] getBotPose_TargetSpace(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "botpose_targetspace"); + } + + public static double[] getCameraPose_TargetSpace(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "camerapose_targetspace"); + } + + public static double[] getTargetPose_CameraSpace(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "targetpose_cameraspace"); + } + + public static double[] getTargetPose_RobotSpace(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "targetpose_robotspace"); + } + + /** + * Gets the average color under the crosshair region as a 3-element array. + * + * @param limelightName Name of the Limelight camera + * @return Array containing [Blue, Green, Red] color values (BGR order) + */ + public static double[] getTargetColor(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "tc"); + } + + public static double getFiducialID(String limelightName) { + return getLimelightNTDouble(limelightName, "tid"); + } + + /** + * Gets the Limelight heartbeat value. Increments once per frame, allowing you to detect if the + * Limelight is connected and alive. + * + * @param limelightName Name of the Limelight camera + * @return Heartbeat value that increments each frame + */ + public static double getHeartbeat(String limelightName) { + return getLimelightNTDouble(limelightName, "hb"); + } + + public static String getNeuralClassID(String limelightName) { + return getLimelightNTString(limelightName, "tclass"); + } + + public static String[] getRawBarcodeData(String limelightName) { + return getLimelightNTStringArray(limelightName, "rawbarcodes"); + } + + ///// + ///// + + public static Pose3d getBotPose3d(String limelightName) { + double[] poseArray = getLimelightNTDoubleArray(limelightName, "botpose"); + return toPose3D(poseArray); + } + + /** + * (Not Recommended) Gets the robot's 3D pose in the WPILib Red Alliance Coordinate System. + * + * @param limelightName Name/identifier of the Limelight + * @return Pose3d object representing the robot's position and orientation in Red Alliance field + * space + */ + public static Pose3d getBotPose3d_wpiRed(String limelightName) { + double[] poseArray = getLimelightNTDoubleArray(limelightName, "botpose_wpired"); + return toPose3D(poseArray); + } + + /** + * (Recommended) Gets the robot's 3D pose in the WPILib Blue Alliance Coordinate System. + * + * @param limelightName Name/identifier of the Limelight + * @return Pose3d object representing the robot's position and orientation in Blue Alliance field + * space + */ + public static Pose3d getBotPose3d_wpiBlue(String limelightName) { + double[] poseArray = getLimelightNTDoubleArray(limelightName, "botpose_wpiblue"); + return toPose3D(poseArray); + } + + /** + * Gets the robot's 3D pose with respect to the currently tracked target's coordinate system. + * + * @param limelightName Name/identifier of the Limelight + * @return Pose3d object representing the robot's position and orientation relative to the target + */ + public static Pose3d getBotPose3d_TargetSpace(String limelightName) { + double[] poseArray = getLimelightNTDoubleArray(limelightName, "botpose_targetspace"); + return toPose3D(poseArray); + } + + /** + * Gets the camera's 3D pose with respect to the currently tracked target's coordinate system. + * + * @param limelightName Name/identifier of the Limelight + * @return Pose3d object representing the camera's position and orientation relative to the target + */ + public static Pose3d getCameraPose3d_TargetSpace(String limelightName) { + double[] poseArray = getLimelightNTDoubleArray(limelightName, "camerapose_targetspace"); + return toPose3D(poseArray); + } + + /** + * Gets the target's 3D pose with respect to the camera's coordinate system. + * + * @param limelightName Name/identifier of the Limelight + * @return Pose3d object representing the target's position and orientation relative to the camera + */ + public static Pose3d getTargetPose3d_CameraSpace(String limelightName) { + double[] poseArray = getLimelightNTDoubleArray(limelightName, "targetpose_cameraspace"); + return toPose3D(poseArray); + } + + /** + * Gets the target's 3D pose with respect to the robot's coordinate system. + * + * @param limelightName Name/identifier of the Limelight + * @return Pose3d object representing the target's position and orientation relative to the robot + */ + public static Pose3d getTargetPose3d_RobotSpace(String limelightName) { + double[] poseArray = getLimelightNTDoubleArray(limelightName, "targetpose_robotspace"); + return toPose3D(poseArray); + } + + /** + * Gets the camera's 3D pose with respect to the robot's coordinate system. + * + * @param limelightName Name/identifier of the Limelight + * @return Pose3d object representing the camera's position and orientation relative to the robot + */ + public static Pose3d getCameraPose3d_RobotSpace(String limelightName) { + double[] poseArray = getLimelightNTDoubleArray(limelightName, "camerapose_robotspace"); + return toPose3D(poseArray); + } + + /** + * Gets the Pose2d for easy use with Odometry vision pose estimator (addVisionMeasurement) + * + * @param limelightName + * @return + */ + public static Pose2d getBotPose2d_wpiBlue(String limelightName) { + + double[] result = getBotPose_wpiBlue(limelightName); + return toPose2D(result); + } + + /** + * Gets the MegaTag1 Pose2d and timestamp for use with WPILib pose estimator + * (addVisionMeasurement) in the WPILib Blue alliance coordinate system. + * + * @param limelightName + * @return + */ + public static PoseEstimate getBotPoseEstimate_wpiBlue(String limelightName) { + return getBotPoseEstimate(limelightName, "botpose_wpiblue", false); + } + + /** + * Gets the MegaTag2 Pose2d and timestamp for use with WPILib pose estimator + * (addVisionMeasurement) in the WPILib Blue alliance coordinate system. Make sure you are calling + * setRobotOrientation() before calling this method. + * + * @param limelightName + * @return + */ + public static PoseEstimate getBotPoseEstimate_wpiBlue_MegaTag2(String limelightName) { + return getBotPoseEstimate(limelightName, "botpose_orb_wpiblue", true); + } + + /** + * Gets the Pose2d for easy use with Odometry vision pose estimator (addVisionMeasurement) + * + * @param limelightName + * @return + */ + public static Pose2d getBotPose2d_wpiRed(String limelightName) { + + double[] result = getBotPose_wpiRed(limelightName); + return toPose2D(result); + } + + /** + * Gets the Pose2d and timestamp for use with WPILib pose estimator (addVisionMeasurement) when + * you are on the RED alliance + * + * @param limelightName + * @return + */ + public static PoseEstimate getBotPoseEstimate_wpiRed(String limelightName) { + return getBotPoseEstimate(limelightName, "botpose_wpired", false); + } + + /** + * Gets the Pose2d and timestamp for use with WPILib pose estimator (addVisionMeasurement) when + * you are on the RED alliance + * + * @param limelightName + * @return + */ + public static PoseEstimate getBotPoseEstimate_wpiRed_MegaTag2(String limelightName) { + return getBotPoseEstimate(limelightName, "botpose_orb_wpired", true); + } + + /** + * Gets the Pose2d for easy use with Odometry vision pose estimator (addVisionMeasurement) + * + * @param limelightName + * @return + */ + public static Pose2d getBotPose2d(String limelightName) { + + double[] result = getBotPose(limelightName); + return toPose2D(result); + } + + /** + * Gets the current IMU data from NetworkTables. IMU data is formatted as [robotYaw, Roll, Pitch, + * Yaw, gyroX, gyroY, gyroZ, accelX, accelY, accelZ]. Returns all zeros if data is invalid or + * unavailable. + * + * @param limelightName Name/identifier of the Limelight + * @return IMUData object containing all current IMU data + */ + public static IMUData getIMUData(String limelightName) { + double[] imuData = getLimelightNTDoubleArray(limelightName, "imu"); + if (imuData == null || imuData.length < 10) { + return new IMUData(); // Returns object with all zeros + } + return new IMUData(imuData); + } + + ///// + ///// + + public static void setPipelineIndex(String limelightName, int pipelineIndex) { + setLimelightNTDouble(limelightName, "pipeline", pipelineIndex); + } + + public static void setPriorityTagID(String limelightName, int ID) { + setLimelightNTDouble(limelightName, "priorityid", ID); + } + + /** + * Sets LED mode to be controlled by the current pipeline. + * + * @param limelightName Name of the Limelight camera + */ + public static void setLEDMode_PipelineControl(String limelightName) { + setLimelightNTDouble(limelightName, "ledMode", 0); + } + + public static void setLEDMode_ForceOff(String limelightName) { + setLimelightNTDouble(limelightName, "ledMode", 1); + } + + public static void setLEDMode_ForceBlink(String limelightName) { + setLimelightNTDouble(limelightName, "ledMode", 2); + } + + public static void setLEDMode_ForceOn(String limelightName) { + setLimelightNTDouble(limelightName, "ledMode", 3); + } + + /** + * Enables standard side-by-side stream mode. + * + * @param limelightName Name of the Limelight camera + */ + public static void setStreamMode_Standard(String limelightName) { + setLimelightNTDouble(limelightName, "stream", 0); + } + + /** + * Enables Picture-in-Picture mode with secondary stream in the corner. + * + * @param limelightName Name of the Limelight camera + */ + public static void setStreamMode_PiPMain(String limelightName) { + setLimelightNTDouble(limelightName, "stream", 1); + } + + /** + * Enables Picture-in-Picture mode with primary stream in the corner. + * + * @param limelightName Name of the Limelight camera + */ + public static void setStreamMode_PiPSecondary(String limelightName) { + setLimelightNTDouble(limelightName, "stream", 2); + } + + /** + * Sets the crop window for the camera. The crop window in the UI must be completely open. + * + * @param limelightName Name of the Limelight camera + * @param cropXMin Minimum X value (-1 to 1) + * @param cropXMax Maximum X value (-1 to 1) + * @param cropYMin Minimum Y value (-1 to 1) + * @param cropYMax Maximum Y value (-1 to 1) + */ + public static void setCropWindow( + String limelightName, double cropXMin, double cropXMax, double cropYMin, double cropYMax) { + double[] entries = new double[4]; + entries[0] = cropXMin; + entries[1] = cropXMax; + entries[2] = cropYMin; + entries[3] = cropYMax; + setLimelightNTDoubleArray(limelightName, "crop", entries); + } + + /** + * Sets the keystone modification for the crop window. + * + * @param limelightName Name of the Limelight camera + * @param horizontal Horizontal keystone value (-0.95 to 0.95) + * @param vertical Vertical keystone value (-0.95 to 0.95) + */ + public static void setKeystone(String limelightName, double horizontal, double vertical) { + double[] entries = new double[2]; + entries[0] = horizontal; + entries[1] = vertical; + setLimelightNTDoubleArray(limelightName, "keystone_set", entries); + } + + /** Sets 3D offset point for easy 3D targeting. */ + public static void setFiducial3DOffset( + String limelightName, double offsetX, double offsetY, double offsetZ) { + double[] entries = new double[3]; + entries[0] = offsetX; + entries[1] = offsetY; + entries[2] = offsetZ; + setLimelightNTDoubleArray(limelightName, "fiducial_offset_set", entries); + } + + /** + * Sets robot orientation values used by MegaTag2 localization algorithm. + * + * @param limelightName Name/identifier of the Limelight + * @param yaw Robot yaw in degrees. 0 = robot facing red alliance wall in FRC + * @param yawRate (Unnecessary) Angular velocity of robot yaw in degrees per second + * @param pitch (Unnecessary) Robot pitch in degrees + * @param pitchRate (Unnecessary) Angular velocity of robot pitch in degrees per second + * @param roll (Unnecessary) Robot roll in degrees + * @param rollRate (Unnecessary) Angular velocity of robot roll in degrees per second + */ + public static void SetRobotOrientation( + String limelightName, + double yaw, + double yawRate, + double pitch, + double pitchRate, + double roll, + double rollRate) { + SetRobotOrientation_INTERNAL( + limelightName, yaw, yawRate, pitch, pitchRate, roll, rollRate, true); + } + + public static void SetRobotOrientation_NoFlush( + String limelightName, + double yaw, + double yawRate, + double pitch, + double pitchRate, + double roll, + double rollRate) { + SetRobotOrientation_INTERNAL( + limelightName, yaw, yawRate, pitch, pitchRate, roll, rollRate, false); + } + + private static void SetRobotOrientation_INTERNAL( + String limelightName, + double yaw, + double yawRate, + double pitch, + double pitchRate, + double roll, + double rollRate, + boolean flush) { + + double[] entries = new double[6]; + entries[0] = yaw; + entries[1] = yawRate; + entries[2] = pitch; + entries[3] = pitchRate; + entries[4] = roll; + entries[5] = rollRate; + setLimelightNTDoubleArray(limelightName, "robot_orientation_set", entries); + if (flush) { + Flush(); + } + } + + /** + * Configures the IMU mode for MegaTag2 Localization + * + * @param limelightName Name/identifier of the Limelight + * @param mode IMU mode. + */ + public static void SetIMUMode(String limelightName, int mode) { + setLimelightNTDouble(limelightName, "imumode_set", mode); + } + + /** + * Configures the complementary filter alpha value for IMU Assist Modes (Modes 3 and 4) + * + * @param limelightName Name/identifier of the Limelight + * @param alpha Defaults to .001. Higher values will cause the internal IMU to converge onto the + * assist source more rapidly. + */ + public static void SetIMUAssistAlpha(String limelightName, double alpha) { + setLimelightNTDouble(limelightName, "imuassistalpha_set", alpha); + } + + /** + * Configures the throttle value. Set to 100-200 while disabled to reduce thermal + * output/temperature. + * + * @param limelightName Name/identifier of the Limelight + * @param throttle Defaults to 0. Your Limelgiht will process one frame after skipping + * frames. + */ + public static void SetThrottle(String limelightName, int throttle) { + setLimelightNTDouble(limelightName, "throttle_set", throttle); + } + + /** + * Overrides the valid AprilTag IDs that will be used for localization. Tags not in this list will + * be ignored for robot pose estimation. + * + * @param limelightName Name/identifier of the Limelight + * @param validIDs Array of valid AprilTag IDs to track + */ + public static void SetFiducialIDFiltersOverride(String limelightName, int[] validIDs) { + double[] validIDsDouble = new double[validIDs.length]; + for (int i = 0; i < validIDs.length; i++) { + validIDsDouble[i] = validIDs[i]; + } + setLimelightNTDoubleArray(limelightName, "fiducial_id_filters_set", validIDsDouble); + } + + /** + * Sets the downscaling factor for AprilTag detection. Increasing downscale can improve + * performance at the cost of potentially reduced detection range. + * + * @param limelightName Name/identifier of the Limelight + * @param downscale Downscale factor. Valid values: 1.0 (no downscale), 1.5, 2.0, 3.0, 4.0. Set to + * 0 for pipeline control. + */ + public static void SetFiducialDownscalingOverride(String limelightName, float downscale) { + int d = 0; // pipeline + if (downscale == 1.0) { + d = 1; + } + if (downscale == 1.5) { + d = 2; + } + if (downscale == 2) { + d = 3; + } + if (downscale == 3) { + d = 4; + } + if (downscale == 4) { + d = 5; + } + setLimelightNTDouble(limelightName, "fiducial_downscale_set", d); + } + + /** + * Sets the camera pose relative to the robot. + * + * @param limelightName Name of the Limelight camera + * @param forward Forward offset in meters + * @param side Side offset in meters + * @param up Up offset in meters + * @param roll Roll angle in degrees + * @param pitch Pitch angle in degrees + * @param yaw Yaw angle in degrees + */ + public static void setCameraPose_RobotSpace( + String limelightName, + double forward, + double side, + double up, + double roll, + double pitch, + double yaw) { + double[] entries = new double[6]; + entries[0] = forward; + entries[1] = side; + entries[2] = up; + entries[3] = roll; + entries[4] = pitch; + entries[5] = yaw; + setLimelightNTDoubleArray(limelightName, "camerapose_robotspace_set", entries); + } + + ///// + ///// + + public static void setPythonScriptData(String limelightName, double[] outgoingPythonData) { + setLimelightNTDoubleArray(limelightName, "llrobot", outgoingPythonData); + } + + public static double[] getPythonScriptData(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "llpython"); + } + + ///// + ///// + + /** + * Triggers a snapshot capture via NetworkTables by incrementing the snapshot counter. + * Rate-limited to once per 10 frames on the Limelight. + * + * @param limelightName Name of the Limelight camera + */ + public static void triggerSnapshot(String limelightName) { + double current = getLimelightNTDouble(limelightName, "snapshot"); + setLimelightNTDouble(limelightName, "snapshot", current + 1); + } + + /** + * Enables or pauses the rewind buffer recording. + * + * @param limelightName Name of the Limelight camera + * @param enabled True to enable recording, false to pause + */ + public static void setRewindEnabled(String limelightName, boolean enabled) { + setLimelightNTDouble(limelightName, "rewind_enable_set", enabled ? 1 : 0); + } + + /** + * Triggers a rewind capture with the specified duration. Maximum duration is 165 seconds. + * Rate-limited on the Limelight. + * + * @param limelightName Name of the Limelight camera + * @param durationSeconds Duration of rewind capture in seconds (max 165) + */ + public static void triggerRewindCapture(String limelightName, double durationSeconds) { + double[] currentArray = getLimelightNTDoubleArray(limelightName, "capture_rewind"); + double counter = (currentArray.length > 0) ? currentArray[0] : 0; + double[] entries = new double[2]; + entries[0] = counter + 1; + entries[1] = Math.min(durationSeconds, 165); + setLimelightNTDoubleArray(limelightName, "capture_rewind", entries); + } + + /** + * Gets the latest JSON results output and returns a LimelightResults object. + * + * @param limelightName Name of the Limelight camera + * @return LimelightResults object containing all current target data + */ + public static LimelightResults getLatestResults(String limelightName) { + + long start = System.nanoTime(); + LimelightHelpers.LimelightResults results = new LimelightHelpers.LimelightResults(); + if (mapper == null) { + mapper = + new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + } + + try { + String jsonString = getJSONDump(limelightName); + if (jsonString == null || jsonString.isEmpty() || jsonString.isBlank()) { + results.error = "lljson error: empty json"; + } else { + results = mapper.readValue(jsonString, LimelightResults.class); + if (results.imuResults != null) { + results.imuResults.parseDataArray(); + } + } + } catch (JsonProcessingException e) { + results.error = "lljson error: " + e.getMessage(); + } + + long end = System.nanoTime(); + double millis = (end - start) * .000001; + results.latency_jsonParse = millis; + if (profileJSON) { + System.out.printf("lljson: %.2f\r\n", millis); + } + + return results; + } + + /** + * Sets up port forwarding for a Limelight 3A/3G connected via USB. This allows access to the + * Limelight web interface and video stream when connected to the robot over USB. + * + *

For usbIndex 0: ports 5800-5809 forward to 172.29.0.1 For usbIndex 1: ports 5810-5819 + * forward to 172.29.1.1 etc. + * + *

Call this method once during robot initialization. To access the interface of the camera + * with usbIndex0, you would go to roboRIO-(teamnum)-FRC.local:5801. Port 5811 for usb index 1 + * + * @param usbIndex The USB index of the Limelight (0, 1, 2, etc.) + */ + public static void setupPortForwardingUSB(int usbIndex) { + String ip = "172.29." + usbIndex + ".1"; + int basePort = 5800 + (usbIndex * 10); + + for (int i = 0; i < 10; i++) { + PortForwarder.add(basePort + i, ip, 5800 + i); + } + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/Setpoint.java b/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/Setpoint.java new file mode 100644 index 00000000..17070f46 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/Setpoint.java @@ -0,0 +1,91 @@ +package edu.wpi.team190.gompeilib.core.utility; + +import edu.wpi.first.units.Measure; +import edu.wpi.first.units.Unit; +import edu.wpi.team190.gompeilib.core.logging.Trace; +import lombok.Getter; + +public class Setpoint { + @Getter private Measure setpoint; + @Getter private Measure newSetpoint; + @Getter private Measure offset; + private final Measure step; + public Measure min; + public Measure max; + + public Setpoint(Measure setpoint, Measure step, Measure min, Measure max) { + this.setpoint = setpoint; + this.offset = setpoint.times(0); + this.step = step; + this.min = min.minus(setpoint); + this.max = max.minus(setpoint); + this.newSetpoint = setpoint; + } + + public Setpoint(Measure setpoint, Measure step) { + this.setpoint = setpoint; + this.offset = setpoint.times(0); + this.step = step; + this.min = step.times(Double.NEGATIVE_INFINITY); + this.max = step.times(Double.POSITIVE_INFINITY); + this.newSetpoint = setpoint; + } + + public void setSetpoint(Measure setpoint) { + this.min = min.plus(this.setpoint).minus(setpoint); + this.max = max.plus(this.setpoint).minus(setpoint); + this.setpoint = setpoint; + + // offset = clamp(offset); + newSetpoint = calculateSetpoint(); + } + + @Trace + private Measure calculateSetpoint() { + double sign = Math.signum(setpoint.magnitude()); + + // Apply offset in the direction of the setpoint's sign + // positive offset always increases magnitude, negative always decreases + var newMagnitude = setpoint.plus(offset.times(sign)); + + // Clamp to zero if we've crossed zero (don't flip sign) + if (Math.signum(newMagnitude.magnitude()) != sign && sign != 0) { + return setpoint.times(0); // return zero in same unit + } + + return newMagnitude; + } + + public void increment() { + increment(step); + } + + public void decrement() { + decrement(step); + } + + public void increment(Measure step) { + Measure next = offset.plus(step); + if (next.gt(max)) { + offset = max; + } else { + offset = next; + } + newSetpoint = calculateSetpoint(); + } + + public void decrement(Measure step) { + Measure next = offset.minus(step); + if (next.lt(min)) { + offset = min; + } else { + offset = next; + } + newSetpoint = calculateSetpoint(); + } + + public void reset() { + offset = offset.times(0); + newSetpoint = setpoint; + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/VirtualSubsystem.java b/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/VirtualSubsystem.java new file mode 100644 index 00000000..c3444e3e --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/VirtualSubsystem.java @@ -0,0 +1,30 @@ +// Copyright (c) 2023 FRC 6328 +// http://github.com/Mechanical-Advantage +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file at +// the root directory of this project. + +package edu.wpi.team190.gompeilib.core.utility; + +import java.util.ArrayList; +import java.util.List; + +/** Represents a subsystem unit that requires a periodic callback but not a hardware mutex. */ +public abstract class VirtualSubsystem { + private static List subsystems = new ArrayList<>(); + + public VirtualSubsystem() { + subsystems.add(this); + } + + /** Calls {@link #periodic()} on all virtual subsystems. */ + public static void periodicAll() { + for (var subsystem : subsystems) { + subsystem.periodic(); + } + } + + /** This method is called periodically once per loop cycle. */ + public abstract void periodic(); +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/control/CurrentLimits.java b/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/control/CurrentLimits.java new file mode 100644 index 00000000..c707ba18 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/control/CurrentLimits.java @@ -0,0 +1,18 @@ +package edu.wpi.team190.gompeilib.core.utility.control; + +import edu.wpi.first.units.Units; +import edu.wpi.first.units.measure.Current; +import lombok.Builder; + +@Builder(setterPrefix = "with") +public record CurrentLimits(Current supplyCurrentLimit, Current statorCurrentLimit) { + @Builder( + setterPrefix = "with", + builderClassName = "FromDoubles", + builderMethodName = "fromDoubles") + public CurrentLimits(double supplyCurrentLimit, double statorCurrentLimit) { + this( + Current.ofBaseUnits(supplyCurrentLimit, Units.Amps), + Current.ofBaseUnits(statorCurrentLimit, Units.Amps)); + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/control/Gains.java b/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/control/Gains.java new file mode 100644 index 00000000..1cf75598 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/control/Gains.java @@ -0,0 +1,96 @@ +package edu.wpi.team190.gompeilib.core.utility.control; + +import edu.wpi.team190.gompeilib.core.utility.tunable.LoggedTunableNumber; +import java.util.function.Consumer; +import lombok.Builder; +import lombok.NonNull; + +@Builder(setterPrefix = "with") +public record Gains( + LoggedTunableNumber kP, + LoggedTunableNumber kI, + LoggedTunableNumber kD, + LoggedTunableNumber kS, + LoggedTunableNumber kV, + LoggedTunableNumber kA, + LoggedTunableNumber kG) { + + @Builder( + setterPrefix = "with", + builderMethodName = "fromDoubles", + builderClassName = "FromDoubles") + public Gains( + @NonNull String prefix, + double kP, + double kI, + double kD, + double kS, + double kV, + double kA, + double kG) { + this( + new LoggedTunableNumber(prefix + "/Kp", kP), + new LoggedTunableNumber(prefix + "/Ki", kI), + new LoggedTunableNumber(prefix + "/Kd", kD), + new LoggedTunableNumber(prefix + "/Ks", kS), + new LoggedTunableNumber(prefix + "/Kv", kV), + new LoggedTunableNumber(prefix + "/Ka", kA), + new LoggedTunableNumber(prefix + "/Kg", kG)); + } + + public Gains { + if (kP == null) { + kP = new LoggedTunableNumber(""); + } + if (kI == null) { + kI = new LoggedTunableNumber(""); + } + if (kD == null) { + kD = new LoggedTunableNumber(""); + } + if (kS == null) { + kS = new LoggedTunableNumber(""); + } + if (kV == null) { + kV = new LoggedTunableNumber(""); + } + if (kA == null) { + kA = new LoggedTunableNumber(""); + } + if (kG == null) { + kG = new LoggedTunableNumber(""); + } + } + + public double getKP() { + return kP.get(); + } + + public double getKI() { + return kI.get(); + } + + public double getKD() { + return kD.get(); + } + + public double getKS() { + return kS.get(); + } + + public double getKV() { + return kV.get(); + } + + public double getKA() { + return kA.get(); + } + + public double getKG() { + return kG.get(); + } + + public void update(int id, Consumer consumer) { + LoggedTunableNumber.ifChanged(id, g -> consumer.accept(this), kP, kI, kD, kS, kV, kA, kG); + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/control/LinearProfile.java b/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/control/LinearProfile.java new file mode 100644 index 00000000..e2e7c58b --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/control/LinearProfile.java @@ -0,0 +1,102 @@ +// Copyright (c) 2024 FRC 6328 +// http://github.com/Mechanical-Advantage +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file at +// the root directory of this project. + +package edu.wpi.team190.gompeilib.core.utility.control; + +import edu.wpi.first.math.MathUtil; +import edu.wpi.first.math.geometry.Twist2d; +import lombok.Getter; +import lombok.Setter; + +/** Ramps up and down to setpoint for velocity closed loop control */ +public class LinearProfile { + private double dv; + private double maxVelocity; + @Getter private final double period; + @Getter private double currentSetpoint = 0; + @Getter @Setter private double goal = 0; + + /** + * Creates a new LinearProfile + * + * @param maxAcceleration The max ramp rate in velocity in rps/sec + * @param period Period of control loop (0.02) + */ + public LinearProfile(double maxAcceleration, double maxVelocity, double period) { + this.period = period; + this.maxVelocity = maxVelocity; + setMaxAcceleration(maxAcceleration); + } + + /** Set the max acceleration constraint in rpm/sec */ + public void setMaxAcceleration(double maxAcceleration) { + dv = maxAcceleration * period; + } + + public void setMaxVelocity(double maxVelocity) { + this.maxVelocity = maxVelocity; + } + + /** + * Sets the target setpoint, starting from the current speed + * + * @param goal Target setpoint + * @param currentSpeed Current speed, to be used as the starting setpoint + */ + public void setGoal(double goal, double currentSpeed) { + this.goal = goal; + currentSetpoint = currentSpeed; + } + + /** Resets target setpoint and current setpoint */ + public void reset() { + currentSetpoint = 0; + goal = 0; + } + + /** + * Returns the current setpoint to send to motors + * + * @return Setpoint to send to motors + */ + public double calculateSetpoint() { + if (EqualsUtil.epsilonEquals(goal, currentSetpoint)) { + return currentSetpoint; + } + if (goal > currentSetpoint) { + currentSetpoint += dv; + if (currentSetpoint > goal) { + currentSetpoint = goal; + } + } else if (goal < currentSetpoint) { + currentSetpoint -= dv; + if (currentSetpoint < goal) { + currentSetpoint = goal; + } + } + return MathUtil.clamp(currentSetpoint, -maxVelocity, maxVelocity); + } + + public class EqualsUtil { + public static boolean epsilonEquals(double a, double b, double epsilon) { + return (a - epsilon <= b) && (a + epsilon >= b); + } + + public static boolean epsilonEquals(double a, double b) { + return epsilonEquals(a, b, 1e-9); + } + + /** Extension methods for wpi geometry objects */ + public static class GeomExtensions { + public static boolean epsilonEquals(Twist2d twist, Twist2d other) { + return EqualsUtil.epsilonEquals(twist.dx, other.dx) + && EqualsUtil.epsilonEquals(twist.dy, other.dy) + && EqualsUtil.epsilonEquals(twist.dtheta, other.dtheta); + } + } + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/control/constraints/AngularPositionConstraints.java b/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/control/constraints/AngularPositionConstraints.java new file mode 100644 index 00000000..b542df45 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/control/constraints/AngularPositionConstraints.java @@ -0,0 +1,49 @@ +package edu.wpi.team190.gompeilib.core.utility.control.constraints; + +import edu.wpi.first.units.*; +import edu.wpi.team190.gompeilib.core.utility.tunable.LoggedTunableMeasure; +import java.util.function.Consumer; +import lombok.Builder; +import lombok.NonNull; + +/** Specifically for Angular constraints (Degrees, Radians, Rotations). */ +@Builder(setterPrefix = "with") +public record AngularPositionConstraints( + LoggedTunableMeasure goalTolerance, + LoggedTunableMeasure maxVelocity, + LoggedTunableMeasure maxAcceleration) + implements Constraints, + Constraints.PositionConstraints { + + @Builder( + setterPrefix = "with", + builderClassName = "FromMeasures", + builderMethodName = "fromMeasures") + public AngularPositionConstraints( + @NonNull String prefix, + Measure goalTolerance, + Measure maxVelocity, + Measure maxAcceleration) { + this( + new LoggedTunableMeasure<>(String.format("%s/Goal Tolerance", prefix), goalTolerance), + new LoggedTunableMeasure<>(String.format("%s/Max Velocity", prefix), maxVelocity), + new LoggedTunableMeasure<>(String.format("%s/Max Acceleration", prefix), maxAcceleration)); + } + + public double getGoalTolerance(AngleUnit unit) { + return goalTolerance.get(unit); + } + + public double getMaxVelocity(AngularVelocityUnit unit) { + return maxVelocity.get(unit); + } + + public double getMaxAcceleration(AngularAccelerationUnit unit) { + return maxAcceleration.get(unit); + } + + public void update(int id, Consumer consumer) { + LoggedTunableMeasure.ifChanged( + id, () -> consumer.accept(this), goalTolerance, maxVelocity, maxAcceleration); + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/control/constraints/AngularVelocityConstraints.java b/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/control/constraints/AngularVelocityConstraints.java new file mode 100644 index 00000000..d66c91fc --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/control/constraints/AngularVelocityConstraints.java @@ -0,0 +1,38 @@ +package edu.wpi.team190.gompeilib.core.utility.control.constraints; + +import edu.wpi.first.units.AngularAccelerationUnit; +import edu.wpi.first.units.AngularVelocityUnit; +import edu.wpi.first.units.Measure; +import edu.wpi.team190.gompeilib.core.utility.tunable.LoggedTunableMeasure; +import java.util.function.Consumer; +import lombok.Builder; +import lombok.NonNull; + +/** Specifically for Angular constraints (Degrees, Radians, Rotations). */ +@Builder(setterPrefix = "with") +public record AngularVelocityConstraints( + LoggedTunableMeasure goalTolerance, + LoggedTunableMeasure maxVelocity, + LoggedTunableMeasure maxAcceleration) + implements Constraints { + + @Builder( + setterPrefix = "with", + builderClassName = "FromMeasures", + builderMethodName = "fromMeasures") + public AngularVelocityConstraints( + @NonNull String prefix, + Measure goalTolerance, + Measure maxVelocity, + Measure maxAcceleration) { + this( + new LoggedTunableMeasure<>(String.format("%s/Goal Tolerance", prefix), goalTolerance), + new LoggedTunableMeasure<>(String.format("%s/Max Velocity", prefix), maxVelocity), + new LoggedTunableMeasure<>(String.format("%s/Max Acceleration", prefix), maxAcceleration)); + } + + public void update(int id, Consumer consumer) { + LoggedTunableMeasure.ifChanged( + id, () -> consumer.accept(this), goalTolerance, maxVelocity, maxAcceleration); + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/control/constraints/Constraints.java b/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/control/constraints/Constraints.java new file mode 100644 index 00000000..fd09f865 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/control/constraints/Constraints.java @@ -0,0 +1,10 @@ +package edu.wpi.team190.gompeilib.core.utility.control.constraints; + +import java.util.function.Consumer; + +public interface Constraints> { + public void update(int id, Consumer consumer); + + public sealed interface PositionConstraints> + extends Constraints permits AngularPositionConstraints, LinearConstraints {} +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/control/constraints/LinearConstraints.java b/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/control/constraints/LinearConstraints.java new file mode 100644 index 00000000..0de82929 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/control/constraints/LinearConstraints.java @@ -0,0 +1,48 @@ +package edu.wpi.team190.gompeilib.core.utility.control.constraints; + +import edu.wpi.first.units.*; +import edu.wpi.team190.gompeilib.core.utility.tunable.LoggedTunableMeasure; +import java.util.function.Consumer; +import lombok.Builder; +import lombok.NonNull; + +/** Specifically for Angular constraints (Degrees, Radians, Rotations). */ +@Builder(setterPrefix = "with") +public record LinearConstraints( + LoggedTunableMeasure goalTolerance, + LoggedTunableMeasure maxVelocity, + LoggedTunableMeasure maxAcceleration) + implements Constraints, Constraints.PositionConstraints { + + @Builder( + setterPrefix = "with", + builderMethodName = "fromMeasures", + builderClassName = "FromMeasures") + public LinearConstraints( + @NonNull String prefix, + Measure goalTolerance, + Measure maxVelocity, + Measure maxAcceleration) { + this( + new LoggedTunableMeasure<>(String.format("%s/Goal Tolerance", prefix), goalTolerance), + new LoggedTunableMeasure<>(String.format("%s/Max Velocity", prefix), maxVelocity), + new LoggedTunableMeasure<>(String.format("%s/Max Acceleration", prefix), maxAcceleration)); + } + + public double getGoalToleranceMeters(DistanceUnit unit) { + return goalTolerance.get(unit); + } + + public double getMaxVelocityMetersPerSecond(LinearVelocityUnit unit) { + return maxVelocity.get(unit); + } + + public double getMaxAccelerationMetersPerSecondSquared(LinearAccelerationUnit unit) { + return maxAcceleration.get(unit); + } + + public void update(int id, Consumer consumer) { + LoggedTunableMeasure.ifChanged( + id, () -> consumer.accept(this), goalTolerance, maxVelocity, maxAcceleration); + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/phoenix/GainSlot.java b/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/phoenix/GainSlot.java new file mode 100644 index 00000000..328ee878 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/phoenix/GainSlot.java @@ -0,0 +1,17 @@ +package edu.wpi.team190.gompeilib.core.utility.phoenix; + +/** An Enum class that handles all possible gain slots for motor control */ +public enum GainSlot { + ZERO, + ONE, + TWO; + + public static GainSlot integerToGainSlot(Integer integer) { + return switch (integer) { + case 0 -> ZERO; + case 1 -> ONE; + case 2 -> TWO; + default -> null; + }; + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/phoenix/PhoenixOdometryThread.java b/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/phoenix/PhoenixOdometryThread.java new file mode 100644 index 00000000..77cd15be --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/phoenix/PhoenixOdometryThread.java @@ -0,0 +1,167 @@ +// Copyright 2021-2024 FRC 6328 +// http://github.com/Mechanical-Advantage +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 3 as published by the Free Software Foundation or +// available in the root directory of this project. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +package edu.wpi.team190.gompeilib.core.utility.phoenix; + +import com.ctre.phoenix6.BaseStatusSignal; +import com.ctre.phoenix6.StatusSignal; +import edu.wpi.first.units.measure.Angle; +import edu.wpi.first.wpilibj.RobotController; +import edu.wpi.team190.gompeilib.subsystems.drivebases.swervedrive.SwerveDriveConstants; +import java.util.ArrayList; +import java.util.List; +import java.util.Queue; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.function.DoubleSupplier; + +/** + * Provides an interface for asynchronously reading high-frequency measurements to a set of queues. + * + *

This version is intended for Phoenix 6 devices on both the RIO and CANivore buses. When using + * a CANivore, the thread uses the "waitForAll" blocking method to enable more consistent sampling. + * This also allows Phoenix Pro users to benefit from lower latency between devices using CANivore + * time synchronization. + */ +public class PhoenixOdometryThread extends Thread { + private final SwerveDriveConstants driveConstants; + private final Lock signalsLock = + new ReentrantLock(); // Prevents conflicts when registering signals + private BaseStatusSignal[] phoenixSignals = new BaseStatusSignal[0]; + private final List genericSignals = new ArrayList<>(); + private final List> phoenixQueues = new ArrayList<>(); + private final List> genericQueues = new ArrayList<>(); + private final List> timestampQueues = new ArrayList<>(); + + private static boolean isCANFD; + private static PhoenixOdometryThread instance = null; + + public static PhoenixOdometryThread getInstance(SwerveDriveConstants driveConstants) { + if (instance == null) { + instance = new PhoenixOdometryThread(driveConstants); + } + return instance; + } + + private PhoenixOdometryThread(SwerveDriveConstants driveConstants) { + this.driveConstants = driveConstants; + isCANFD = driveConstants.driveConfig.canBus().isNetworkFD(); + setName("PhoenixOdometryThread"); + setDaemon(true); + } + + @Override + public void start() { + if (timestampQueues.size() > 0) { + super.start(); + } + } + + /** Registers a Phoenix signal to be read from the thread. */ + public Queue registerSignal(StatusSignal signal) { + Queue queue = new ArrayBlockingQueue<>(20); + signalsLock.lock(); + driveConstants.reentrantLock.lock(); + try { + BaseStatusSignal[] newSignals = new BaseStatusSignal[phoenixSignals.length + 1]; + System.arraycopy(phoenixSignals, 0, newSignals, 0, phoenixSignals.length); + newSignals[phoenixSignals.length] = signal; + phoenixSignals = newSignals; + phoenixQueues.add(queue); + } finally { + signalsLock.unlock(); + driveConstants.reentrantLock.unlock(); + } + return queue; + } + + /** Registers a generic signal to be read from the thread. */ + public Queue registerSignal(DoubleSupplier signal) { + Queue queue = new ArrayBlockingQueue<>(20); + signalsLock.lock(); + driveConstants.reentrantLock.lock(); + try { + genericSignals.add(signal); + genericQueues.add(queue); + } finally { + signalsLock.unlock(); + driveConstants.reentrantLock.unlock(); + } + return queue; + } + + /** Returns a new queue that returns timestamp values for each sample. */ + public Queue makeTimestampQueue() { + Queue queue = new ArrayBlockingQueue<>(20); + driveConstants.reentrantLock.lock(); + try { + timestampQueues.add(queue); + } finally { + driveConstants.reentrantLock.unlock(); + } + return queue; + } + + @Override + public void run() { + while (true) { + // Wait for updates from all signals + signalsLock.lock(); + try { + if (isCANFD && phoenixSignals.length > 0) { + BaseStatusSignal.waitForAll(2.0 / driveConstants.odometryFrequency, phoenixSignals); + } else { + // "waitForAll" does not support blocking on multiple signals with a bus + // that is not CAN FD, regardless of Pro licensing. No reasoning for this + // behavior is provided by the documentation. + Thread.sleep((long) (1000.0 / driveConstants.odometryFrequency)); + if (phoenixSignals.length > 0) BaseStatusSignal.refreshAll(phoenixSignals); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } finally { + signalsLock.unlock(); + } + + // Save new data to queues + driveConstants.reentrantLock.lock(); + try { + // Sample timestamp is current FPGA time minus average CAN latency + // Default timestamps from Phoenix are NOT compatible with + // FPGA timestamps, this solution is imperfect but close + double timestamp = RobotController.getFPGATime() / 1e6; + double totalLatency = 0.0; + for (BaseStatusSignal signal : phoenixSignals) { + totalLatency += signal.getTimestamp().getLatency(); + } + if (phoenixSignals.length > 0) { + timestamp -= totalLatency / phoenixSignals.length; + } + + // Add new samples to queues + for (int i = 0; i < phoenixSignals.length; i++) { + phoenixQueues.get(i).offer(phoenixSignals[i].getValueAsDouble()); + } + for (int i = 0; i < genericSignals.size(); i++) { + genericQueues.get(i).offer(genericSignals.get(i).getAsDouble()); + } + for (int i = 0; i < timestampQueues.size(); i++) { + timestampQueues.get(i).offer(timestamp); + } + } finally { + driveConstants.reentrantLock.unlock(); + } + } + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/phoenix/PhoenixUtil.java b/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/phoenix/PhoenixUtil.java new file mode 100644 index 00000000..8c6c09fe --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/phoenix/PhoenixUtil.java @@ -0,0 +1,45 @@ +package edu.wpi.team190.gompeilib.core.utility.phoenix; + +import com.ctre.phoenix6.BaseStatusSignal; +import com.ctre.phoenix6.StatusCode; +import java.util.function.Supplier; + +public class PhoenixUtil { + /** Attempts to run the command until no error is produced. */ + public static void tryUntilOk(int maxAttempts, Supplier command) { + for (int i = 0; i < maxAttempts; i++) { + var error = command.get(); + if (error.isOK()) break; + } + } + + /** Signals for synchronized refresh. */ + private static BaseStatusSignal[] canivoreSignals = new BaseStatusSignal[0]; + + private static BaseStatusSignal[] rioSignals = new BaseStatusSignal[0]; + + /** Registers a set of signals for synchronized refresh. */ + public static void registerSignals(boolean canivore, BaseStatusSignal... signals) { + if (canivore) { + BaseStatusSignal[] newSignals = new BaseStatusSignal[canivoreSignals.length + signals.length]; + System.arraycopy(canivoreSignals, 0, newSignals, 0, canivoreSignals.length); + System.arraycopy(signals, 0, newSignals, canivoreSignals.length, signals.length); + canivoreSignals = newSignals; + } else { + BaseStatusSignal[] newSignals = new BaseStatusSignal[rioSignals.length + signals.length]; + System.arraycopy(rioSignals, 0, newSignals, 0, rioSignals.length); + System.arraycopy(signals, 0, newSignals, rioSignals.length, signals.length); + rioSignals = newSignals; + } + } + + /** Refresh all registered signals. */ + public static void refreshAll() { + if (canivoreSignals.length > 0) { + BaseStatusSignal.refreshAll(canivoreSignals); + } + if (rioSignals.length > 0) { + BaseStatusSignal.refreshAll(rioSignals); + } + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/sysid/CustomSysIdRoutine.java b/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/sysid/CustomSysIdRoutine.java new file mode 100644 index 00000000..8f53a9d4 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/sysid/CustomSysIdRoutine.java @@ -0,0 +1,151 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +package edu.wpi.team190.gompeilib.core.utility.sysid; + +import static edu.wpi.first.units.Units.*; + +import edu.wpi.first.units.*; +import edu.wpi.first.units.measure.Per; +import edu.wpi.first.wpilibj.Timer; +import edu.wpi.first.wpilibj.sysid.SysIdRoutineLog; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.Subsystem; +import java.util.function.Consumer; + +/** + * A generic SysId characterization routine. Subclass this for specific units. + * + * @param The unit type for the output (e.g., VoltageUnit, CurrentUnit) + */ +public class CustomSysIdRoutine extends SysIdRoutineLog { + private final Config config; + private final Mechanism mechanism; + private final MutableMeasure outputValue; + private final Consumer recordState; + + /** + * Create a new SysId characterization routine. * @param config Configuration with strongly typed + * measures. + * + * @param mechanism Mechanism interface. + * @param initialMutable The mutable measure instance (created by the subclass). + */ + public CustomSysIdRoutine( + Config config, Mechanism mechanism, MutableMeasure initialMutable) { + super(mechanism.name); + this.config = config; + this.mechanism = mechanism; + outputValue = initialMutable; + recordState = config.recordState != null ? config.recordState : this::recordState; + } + + /** + * @param rampRate We use Measure for ramp rate because it is U/Time. + */ + public record Config( + Per rampRate, + Measure stepOutput, + Measure timeout, + Consumer recordState, + U outputUnit) { + public Config( + Per rampRate, + Measure stepOutput, + Measure timeout, + Consumer recordState, + U outputUnit) { + this.rampRate = rampRate; + this.stepOutput = stepOutput; + this.timeout = timeout != null ? timeout : Seconds.of(10); + this.recordState = recordState; + this.outputUnit = outputUnit; + } + } + + public static class Mechanism { + public final Consumer> drive; + public final Consumer log; + public final Subsystem subsystem; + public final String name; + + public Mechanism( + Consumer> drive, + Consumer log, + Subsystem subsystem, + String name) { + this.drive = drive; + this.log = log != null ? log : l -> {}; + this.subsystem = subsystem; + this.name = name != null ? name : subsystem.getName(); + } + + public Mechanism(Consumer> drive, Subsystem subsystem) { + this(drive, null, subsystem, null); + } + } + + public enum Direction { + kForward, + kReverse + } + + public Command quasistatic(Direction direction) { + State state = + (direction == Direction.kForward) ? State.kQuasistaticForward : State.kQuasistaticReverse; + + double outputSign = (direction == Direction.kForward) ? 1.0 : -1.0; + Timer timer = new Timer(); + + double rampRateUnitsPerSec = config.rampRate.magnitude(); + + return mechanism + .subsystem + .runOnce(timer::restart) + .andThen( + mechanism.subsystem.run( + () -> { + mechanism.drive.accept( + outputValue.mut_replace( + outputSign * timer.get() * rampRateUnitsPerSec, config.outputUnit)); + + mechanism.log.accept(this); + recordState.accept(state); + })) + .finallyDo( + () -> { + mechanism.drive.accept(outputValue.mut_replace(0, config.outputUnit)); + recordState.accept(State.kNone); + timer.stop(); + }) + .withName("sysid-" + state + "-" + mechanism.name) + .withTimeout(config.timeout.in(Seconds)); + } + + public Command dynamic(Direction direction) { + double outputSign = (direction == Direction.kForward) ? 1.0 : -1.0; + State state = (direction == Direction.kForward) ? State.kDynamicForward : State.kDynamicReverse; + + // OPTIMIZED: Pre-calculate step magnitude safely + double stepMagnitude = config.stepOutput.in(config.outputUnit); + + return mechanism + .subsystem + .runOnce(() -> outputValue.mut_replace(stepMagnitude * outputSign, config.outputUnit)) + .andThen( + mechanism.subsystem.run( + () -> { + mechanism.drive.accept(outputValue); + mechanism.log.accept(this); + recordState.accept(state); + })) + .finallyDo( + () -> { + mechanism.drive.accept(outputValue.mut_replace(0, config.outputUnit)); + recordState.accept(State.kNone); + }) + .withName("sysid-" + state.toString() + "-" + mechanism.name) + .withTimeout(config.timeout.in(Seconds)); + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/sysid/CustomUnits.java b/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/sysid/CustomUnits.java new file mode 100644 index 00000000..845eb212 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/sysid/CustomUnits.java @@ -0,0 +1,13 @@ +package edu.wpi.team190.gompeilib.core.utility.sysid; + +import static edu.wpi.first.units.Units.*; + +import edu.wpi.first.units.CurrentUnit; +import edu.wpi.first.units.PerUnit; +import edu.wpi.first.units.TimeUnit; +import edu.wpi.first.units.VoltageUnit; + +public class CustomUnits { + public static final PerUnit ampsPerSecond = Amps.per(Second); + public static final PerUnit voltsPerSecond = Volts.per(Second); +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/tunable/LoggedTunableMeasure.java b/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/tunable/LoggedTunableMeasure.java new file mode 100644 index 00000000..c4179743 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/tunable/LoggedTunableMeasure.java @@ -0,0 +1,100 @@ +package edu.wpi.team190.gompeilib.core.utility.tunable; + +import edu.wpi.first.units.Measure; +import edu.wpi.first.units.Unit; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Supplier; +import org.littletonrobotics.junction.networktables.LoggedNetworkNumber; + +/** + * Class for a tunable measure (unit-safe). Gets value from dashboard in tuning mode, returns + * default if not or value not in dashboard. + */ +public class LoggedTunableMeasure implements Supplier> { + private static final String tableKey = "TunableNumbers"; + + private final String key; + private final U unit; + private boolean hasDefault = false; + private Measure defaultValue; + private LoggedNetworkNumber dashboardNumber; + private final Map> lastHasChangedValues = new HashMap<>(); + + /** + * Create a new LoggedTunableMeasure with the default value + * + * @param dashboardKey Key on dashboard + * @param defaultValue Default measure value + */ + public LoggedTunableMeasure(String dashboardKey, Measure defaultValue) { + this.key = tableKey + "/" + dashboardKey + " (" + defaultValue.unit() + ")"; + this.unit = defaultValue.unit(); + initDefault(defaultValue); + } + + /** Set the default value. The default value can only be set once. */ + private void initDefault(Measure defaultValue) { + hasDefault = true; + this.defaultValue = defaultValue; + if (GompeiLib.isTuning()) { + // We store the raw double value in the base unit + dashboardNumber = new LoggedNetworkNumber(key, defaultValue.in(unit)); + } + } + + /** Get the current measure, from dashboard if available and in tuning mode. */ + @Override + @SuppressWarnings("unchecked") // safe + public Measure get() { + return (Measure) unit.of(getRawValue()); + } + + /** + * Get the current value converted to the specified unit. The unit must be of the same dimension + * (e.g., CurrentUnit for current values). + * + * @param targetUnit The unit to convert the value to (must be compatible with the base unit) + * @return The current value in the specified unit + */ + @SuppressWarnings("unchecked") + public double get(Unit targetUnit) { + return get().in((U) targetUnit); + } + + public double getRawValue() { + return GompeiLib.isTuning() ? dashboardNumber.get() : defaultValue.in(unit); + } + + /** Checks whether the measure has changed since our last check */ + public boolean hasChanged(int id) { + Measure currentValue = get(); + Measure lastValue = lastHasChangedValues.get(id); + if (!currentValue.equals(lastValue)) { + lastHasChangedValues.put(id, currentValue); + return true; + } + return false; + } + + /** Runs action if any of the tunableMeasures have changed */ + @SafeVarargs + public static void ifChanged( + int id, Consumer[]> action, LoggedTunableMeasure... tunableNumbers) { + if (Arrays.stream(tunableNumbers).anyMatch(n -> n.hasChanged(id))) { + @SuppressWarnings("unchecked") + Measure[] values = + Arrays.stream(tunableNumbers).map(LoggedTunableMeasure::get).toArray(Measure[]::new); + action.accept(values); + } + } + + public static void ifChanged(int id, Runnable action, LoggedTunableMeasure... tunableNumbers) { + if (Arrays.stream(tunableNumbers).anyMatch(n -> n.hasChanged(id))) { + action.run(); + } + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/tunable/LoggedTunableNumber.java b/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/tunable/LoggedTunableNumber.java new file mode 100644 index 00000000..07b0db43 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/tunable/LoggedTunableNumber.java @@ -0,0 +1,123 @@ +// Copyright (c) 2024 FRC 6328 +// http://github.com/Mechanical-Advantage +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file at +// the root directory of this project. + +package edu.wpi.team190.gompeilib.core.utility.tunable; + +import edu.wpi.team190.gompeilib.core.GompeiLib; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.DoubleSupplier; +import org.littletonrobotics.junction.networktables.LoggedNetworkNumber; + +/** + * Class for a tunable number. Gets value from dashboard in tuning mode, returns default if not or + * value not in dashboard. + */ +public class LoggedTunableNumber implements DoubleSupplier { + private static final String tableKey = "TunableNumbers"; + + private final String key; + private boolean hasDefault = false; + private double defaultValue; + private LoggedNetworkNumber dashboardNumber; + private final Map lastHasChangedValues = new HashMap<>(); + + /** + * Create a new LoggedTunableNumber + * + * @param dashboardKey Key on dashboard + */ + public LoggedTunableNumber(String dashboardKey) { + this.key = tableKey + "/" + dashboardKey; + } + + /** + * Create a new LoggedTunableNumber with the default value + * + * @param dashboardKey Key on dashboard + * @param defaultValue Default value + */ + public LoggedTunableNumber(String dashboardKey, double defaultValue) { + this(dashboardKey); + initDefault(defaultValue); + } + + /** + * Set the default value of the number. The default value can only be set once. + * + * @param defaultValue The default value + */ + public void initDefault(double defaultValue) { + if (!hasDefault) { + hasDefault = true; + this.defaultValue = defaultValue; + if (GompeiLib.isTuning()) { + dashboardNumber = new LoggedNetworkNumber(key, defaultValue); + } + } + } + + /** + * Get the current value, from dashboard if available and in tuning mode. + * + * @return The current value + */ + public double get() { + if (!hasDefault) { + return 0.0; + } else { + return GompeiLib.isTuning() ? dashboardNumber.get() : defaultValue; + } + } + + /** + * Checks whether the number has changed since our last check + * + * @param id Unique identifier for the caller to avoid conflicts when shared between multiple + * objects. Recommended approach is to pass the result of "hashCode()" + * @return True if the number has changed since the last time this method was called, false + * otherwise. + */ + public boolean hasChanged(int id) { + double currentValue = get(); + Double lastValue = lastHasChangedValues.get(id); + if (lastValue == null || currentValue != lastValue) { + lastHasChangedValues.put(id, currentValue); + return true; + } + + return false; + } + + /** + * Runs action if any of the tunableNumbers have changed + * + * @param id Unique identifier for the caller to avoid conflicts when shared between multiple * + * objects. Recommended approach is to pass the result of "hashCode()" + * @param action Callback to run when any of the tunable numbers have changed. Access tunable + * numbers in order inputted in method + * @param tunableNumbers All tunable numbers to check + */ + public static void ifChanged( + int id, Consumer action, LoggedTunableNumber... tunableNumbers) { + if (Arrays.stream(tunableNumbers).anyMatch(tunableNumber -> tunableNumber.hasChanged(id))) { + action.accept(Arrays.stream(tunableNumbers).mapToDouble(LoggedTunableNumber::get).toArray()); + } + } + + /** Runs action if any of the tunableNumbers have changed */ + public static void ifChanged(int id, Runnable action, LoggedTunableNumber... tunableNumbers) { + ifChanged(id, values -> action.run(), tunableNumbers); + } + + @Override + public double getAsDouble() { + return get(); + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/tunable/TunableUpdaterRegistry.java b/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/tunable/TunableUpdaterRegistry.java new file mode 100644 index 00000000..b08556c6 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/core/utility/tunable/TunableUpdaterRegistry.java @@ -0,0 +1,63 @@ +package edu.wpi.team190.gompeilib.core.utility.tunable; + +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.Constraints; +import java.util.Arrays; +import java.util.HashMap; +import java.util.function.Consumer; + +/** + * Central registry for tunable objects that need to be polled for runtime updates. + * + *

Each registry maps a tunable object or group of tunable objects to the action that should run + * when a value changes. The {@link #periodic()} method should be called regularly to detect changes + * and invoke the corresponding update callbacks. + */ +public class TunableUpdaterRegistry { + + private TunableUpdaterRegistry() {} + + /** Registered gain sets and their update callbacks. */ + private static final HashMap> GAINS_UPDATER = new HashMap<>(); + + /** Registered constraint sets and their update callbacks. */ + private static final HashMap, Consumer> CONSTRAINTS_UPDATER = new HashMap<>(); + + /** Registered tunable number groups and their update callbacks. */ + private static final HashMap> NUMBER_UPDATER = + new HashMap<>(); + + /** Registered tunable measure groups and their update callbacks. */ + private static final HashMap[], Runnable> MEASURE_UPDATER = + new HashMap<>(); + + /** + * Checks all registered tunables for changes and runs any associated update callbacks. + * + *

This method is intended to be called periodically, once per robot loop, so runtime tuning + * changes are detected and applied. + */ + public static void periodic() { + GAINS_UPDATER.forEach((k, v) -> k.update(k.hashCode(), v)); + CONSTRAINTS_UPDATER.forEach((k, v) -> k.update(k.hashCode(), v)); + NUMBER_UPDATER.forEach((k, v) -> LoggedTunableNumber.ifChanged(Arrays.hashCode(k), v, k)); + MEASURE_UPDATER.forEach((k, v) -> LoggedTunableMeasure.ifChanged(Arrays.hashCode(k), v, k)); + } + + public static void registerGains(Gains g, Consumer c) { + GAINS_UPDATER.putIfAbsent(g, c); + } + + public static void registerConstraints( + Constraints constraints, Consumer> consumer) { + CONSTRAINTS_UPDATER.putIfAbsent(constraints, consumer); + } + + public static void registerNumber(LoggedTunableNumber[] numbers, Consumer consumer) { + NUMBER_UPDATER.putIfAbsent(numbers, consumer); + } + + public static void registerMeasure(LoggedTunableMeasure[] measures, Runnable consumer) { + MEASURE_UPDATER.putIfAbsent(measures, consumer); + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/arm/Arm.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/arm/Arm.java new file mode 100644 index 00000000..ff81c393 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/arm/Arm.java @@ -0,0 +1,183 @@ +package edu.wpi.team190.gompeilib.subsystems.arm; + +import static edu.wpi.first.units.Units.*; + +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.units.AngleUnit; +import edu.wpi.first.units.VoltageUnit; +import edu.wpi.first.units.measure.Angle; +import edu.wpi.first.units.measure.Voltage; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.Commands; +import edu.wpi.first.wpilibj2.command.Subsystem; +import edu.wpi.first.wpilibj2.command.sysid.SysIdRoutine; +import edu.wpi.team190.gompeilib.core.logging.Trace; +import edu.wpi.team190.gompeilib.core.utility.Setpoint; +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.AngularPositionConstraints; +import edu.wpi.team190.gompeilib.core.utility.phoenix.GainSlot; +import lombok.Getter; +import org.littletonrobotics.junction.Logger; + +public class Arm { + public ArmIO io; + public ArmIOInputsAutoLogged inputs; + + private final String aKitTopic; + + @Getter private ArmState currentState; + + @Getter private Setpoint voltageGoal; + @Getter private Setpoint positionGoal; + + private final SysIdRoutine characterizationRoutine; + + public Arm( + ArmIO io, + Subsystem subsystem, + int index, + ArmConstants constants, + Setpoint positionGoal, + Setpoint voltageGoal) { + this.io = io; + this.inputs = new ArmIOInputsAutoLogged(); + + aKitTopic = subsystem.getName() + "/Arms" + index; + + currentState = ArmState.IDLE; + + this.positionGoal = positionGoal; + this.voltageGoal = voltageGoal; + + characterizationRoutine = + new SysIdRoutine( + new SysIdRoutine.Config( + Volts.of(1).per(Second), + Volts.of(9), + Seconds.of(12), + (state) -> Logger.recordOutput(aKitTopic + "/SysIdState", state.toString())), + new SysIdRoutine.Mechanism(io::setVoltageGoal, null, subsystem)); + } + + public Arm(ArmIO io, Subsystem subsystem, int index, ArmConstants constants) { + this( + io, + subsystem, + index, + constants, + new Setpoint<>( + Rotation2d.kZero.getMeasure(), + constants.positionOffsetStep.getMeasure(), + constants.armParameters.maxAngle().getMeasure(), + constants.armParameters.minAngle().getMeasure()), + new Setpoint<>(Volts.of(0), constants.voltageOffsetStep, Volts.of(-12), Volts.of(12))); + } + + public Arm( + ArmIO io, + Subsystem subsystem, + int index, + ArmConstants constants, + Setpoint positionGoal) { + this( + io, + subsystem, + index, + constants, + positionGoal, + new Setpoint<>(Volts.of(0), constants.voltageOffsetStep, Volts.of(-12), Volts.of(12))); + } + + @Trace + public void periodic() { + io.updateInputs(inputs); + Logger.processInputs(aKitTopic, inputs); + + Logger.recordOutput(aKitTopic + "/State", currentState.name()); + Logger.recordOutput(aKitTopic + "/Voltage Goal", voltageGoal.getSetpoint()); + Logger.recordOutput( + aKitTopic + "/Position Goal", new Rotation2d((Angle) positionGoal.getSetpoint())); + Logger.recordOutput(aKitTopic + "/Voltage Offset", voltageGoal.getOffset()); + Logger.recordOutput(aKitTopic + "/Position Offset", positionGoal.getOffset()); + Logger.recordOutput(aKitTopic + "/At Voltage Goal", atVoltageGoal()); + Logger.recordOutput(aKitTopic + "/At Position Goal", atPositionGoal()); + + switch (currentState) { + case OPEN_LOOP_VOLTAGE_CONTROL -> io.setVoltageGoal((Voltage) voltageGoal.getNewSetpoint()); + case CLOSED_LOOP_POSITION_CONTROL -> + io.setPositionGoal(new Rotation2d((Angle) positionGoal.getNewSetpoint())); + } + } + + public Rotation2d getArmPosition() { + return inputs.position; + } + + public void setVoltageGoal(Voltage voltageGoal) { + currentState = ArmState.OPEN_LOOP_VOLTAGE_CONTROL; + this.voltageGoal.setSetpoint(voltageGoal); + } + + public void setVoltageGoal(Setpoint voltageGoal) { + currentState = ArmState.OPEN_LOOP_VOLTAGE_CONTROL; + this.voltageGoal = voltageGoal; + } + + public void setPositionGoal(Rotation2d positionGoal) { + currentState = ArmState.CLOSED_LOOP_POSITION_CONTROL; + this.positionGoal.setSetpoint(positionGoal.getMeasure()); + } + + public void setPositionGoal(Setpoint positionGoal) { + currentState = ArmState.CLOSED_LOOP_POSITION_CONTROL; + this.positionGoal = positionGoal; + } + + public boolean atVoltageGoal(Voltage voltageReference) { + return io.atVoltageGoal(voltageReference); + } + + public boolean atPositionGoal(Rotation2d positionReference) { + return io.atPositionGoal(positionReference); + } + + public boolean atVoltageGoal() { + return atVoltageGoal((Voltage) voltageGoal.getNewSetpoint()); + } + + public boolean atPositionGoal() { + return atPositionGoal(new Rotation2d((Angle) positionGoal.getNewSetpoint())); + } + + public void setPosition(Rotation2d position) { + io.setPosition(position); + } + + public void setGainSlot(GainSlot slot) { + io.setGainSlot(slot); + } + + public Command waitUntilAtGoal() { + return Commands.waitUntil(this::atPositionGoal); + } + + public void updateGains(Gains gains, GainSlot slot) { + io.updateGains(gains, slot); + } + + public void updateConstraints(AngularPositionConstraints constraints) { + io.updateConstraints(constraints); + } + + public Command sysIdRoutine() { + return Commands.sequence( + Commands.runOnce(() -> currentState = ArmState.IDLE), + characterizationRoutine.quasistatic(SysIdRoutine.Direction.kForward), + Commands.waitSeconds(1.0), + characterizationRoutine.quasistatic(SysIdRoutine.Direction.kReverse), + Commands.waitSeconds(1.0), + characterizationRoutine.dynamic(SysIdRoutine.Direction.kForward), + Commands.waitSeconds(1.0), + characterizationRoutine.dynamic(SysIdRoutine.Direction.kReverse)); + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/arm/ArmConstants.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/arm/ArmConstants.java new file mode 100644 index 00000000..3099c33a --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/arm/ArmConstants.java @@ -0,0 +1,39 @@ +package edu.wpi.team190.gompeilib.subsystems.arm; + +import com.ctre.phoenix6.CANBus; +import com.ctre.phoenix6.signals.InvertedValue; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.units.measure.Voltage; +import edu.wpi.team190.gompeilib.core.utility.control.CurrentLimits; +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.AngularPositionConstraints; +import lombok.Builder; +import lombok.NonNull; + +@Builder(setterPrefix = "with") +public class ArmConstants { + @NonNull public final Integer armCANID; + @NonNull public final CANBus canBus; + @NonNull public final ArmParameters armParameters; + @NonNull public final Gains slot0Gains; + @Builder.Default public final Gains slot1Gains = Gains.builder().build(); + @Builder.Default public final Gains slot2Gains = Gains.builder().build(); + @NonNull public final AngularPositionConstraints constraints; + @NonNull public final CurrentLimits currentLimits; + @NonNull public final Boolean enableFOC; + @NonNull public final InvertedValue invertedValue; + @NonNull public final Voltage voltageOffsetStep; + @NonNull public final Rotation2d positionOffsetStep; + + @Builder(setterPrefix = "with") + public record ArmParameters( + @NonNull DCMotor motorConfig, + @NonNull Rotation2d minAngle, + @NonNull Rotation2d maxAngle, + @NonNull Boolean continuousOutput, + @NonNull Integer numMotors, + @NonNull Double gearRatio, + @NonNull Double lengthMeters, + @NonNull Double momentOfInertia) {} +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/arm/ArmIO.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/arm/ArmIO.java new file mode 100644 index 00000000..ed9e00c1 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/arm/ArmIO.java @@ -0,0 +1,98 @@ +package edu.wpi.team190.gompeilib.subsystems.arm; + +import static edu.wpi.first.units.Units.RadiansPerSecond; +import static edu.wpi.first.units.Units.RadiansPerSecondPerSecond; + +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.units.measure.*; +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.AngularPositionConstraints; +import edu.wpi.team190.gompeilib.core.utility.phoenix.GainSlot; +import org.littletonrobotics.junction.AutoLog; + +public interface ArmIO { + @AutoLog + public static class ArmIOInputs { + public Rotation2d position = new Rotation2d(); + public AngularVelocity velocity = RadiansPerSecond.of(0.0); + public AngularAcceleration acceleration = RadiansPerSecondPerSecond.of(0.0); + + public double[] appliedVolts = new double[] {}; + public double[] supplyCurrentAmps = new double[] {}; + public double[] torqueCurrentAmps = new double[] {}; + public double[] temperatureCelsius = new double[] {}; + + public Rotation2d positionGoal = new Rotation2d(); + public Rotation2d positionSetpoint = new Rotation2d(); + public Rotation2d positionError = new Rotation2d(); + + public GainSlot gainSlot = GainSlot.ZERO; + } + + /** + * Updates the inputs for the arm. + * + * @param inputs The inputs to update. + */ + default void updateInputs(ArmIOInputs inputs) {} + + /** + * Sets the voltage for the arm. + * + * @param voltageGoal The voltage of the arm in volts + */ + default void setVoltageGoal(Voltage voltageGoal) {} + + /** + * Sets the position goal of the arm + * + * @param positionGoal the position goal of the arm + */ + default void setPositionGoal(Rotation2d positionGoal) {} + + /** + * Checks if the voltage of the arm matches the volts argument + * + * @param voltageReference The voltage to check against + * @return True if the voltage matches, false otherwise + */ + default boolean atVoltageGoal(Voltage voltageReference) { + return false; + } + + /** + * Checks if the position of the arm matches the positionGoal argument + * + * @param positionReference the position to check against + * @return True if the position matches, false otherwise + */ + default boolean atPositionGoal(Rotation2d positionReference) { + return false; + } + + /** + * Sets the position of the arm. + * + * @param position The position to set. + */ + default void setPosition(Rotation2d position) {} + + /** + * @param gainSlot The CTRE gain slot to set the arm to. + */ + default void setGainSlot(GainSlot gainSlot) {} + + /** + * Sets the gains for the arm. + * + * @param gains the gains to update + */ + default void updateGains(Gains gains, GainSlot gainSlot) {} + + /** + * Sets the constraints for the arm. + * + * @param constraints the constraints to update + */ + default void updateConstraints(AngularPositionConstraints constraints) {} +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/arm/ArmIOSim.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/arm/ArmIOSim.java new file mode 100644 index 00000000..9b5db8a9 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/arm/ArmIOSim.java @@ -0,0 +1,164 @@ +package edu.wpi.team190.gompeilib.subsystems.arm; + +import static edu.wpi.first.units.Units.*; + +import edu.wpi.first.math.MathUtil; +import edu.wpi.first.math.controller.ArmFeedforward; +import edu.wpi.first.math.controller.ProfiledPIDController; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.system.plant.LinearSystemId; +import edu.wpi.first.math.trajectory.TrapezoidProfile.Constraints; +import edu.wpi.first.units.measure.*; +import edu.wpi.first.wpilibj.simulation.SingleJointedArmSim; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.AngularPositionConstraints; +import edu.wpi.team190.gompeilib.core.utility.phoenix.GainSlot; +import java.util.Arrays; + +public class ArmIOSim implements ArmIO { + private final SingleJointedArmSim armSim; + + private Voltage appliedVolts; + private boolean isClosedLoop; + private GainSlot gainSlot; + + private final ProfiledPIDController feedback; + private ArmFeedforward feedforward; + + private final ArmConstants constants; + + public ArmIOSim(ArmConstants constants) { + armSim = + new SingleJointedArmSim( + LinearSystemId.createSingleJointedArmSystem( + constants.armParameters.motorConfig(), + constants.armParameters.momentOfInertia(), + constants.armParameters.gearRatio()), + constants.armParameters.motorConfig(), + constants.armParameters.gearRatio(), + constants.armParameters.lengthMeters(), + constants.armParameters.minAngle().getRadians(), + constants.armParameters.maxAngle().getRadians(), + true, + constants.armParameters.minAngle().getRadians()); + + appliedVolts = Volts.of(0.0); + isClosedLoop = true; + gainSlot = GainSlot.ZERO; + + feedback = + new ProfiledPIDController( + constants.slot0Gains.kP().get(), + 0.0, + constants.slot0Gains.kD().get(), + new Constraints( + constants.constraints.maxVelocity().get().in(RadiansPerSecond), + constants.constraints.maxAcceleration().get().in(RadiansPerSecondPerSecond))); + if (constants.armParameters.continuousOutput()) { + feedback.enableContinuousInput( + constants.armParameters.minAngle().getRadians(), + constants.armParameters.maxAngle().getRadians()); + } + feedback.setTolerance(constants.constraints.goalTolerance().get().in(Radians)); + feedforward = + new ArmFeedforward( + constants.slot0Gains.kS().get(), + constants.slot0Gains.kV().get(), + constants.slot0Gains.kA().get(), + constants.slot0Gains.kG().get()); + + this.constants = constants; + } + + @Override + public void updateInputs(ArmIOInputs inputs) { + if (isClosedLoop) + appliedVolts = + Volts.of( + feedback.calculate(armSim.getAngleRads()) + + feedforward.calculate( + feedback.getSetpoint().position, feedback.getSetpoint().velocity)); + + appliedVolts = Volts.of(MathUtil.clamp(appliedVolts.in(Volts), -12.0, 12.0)); + armSim.setInputVoltage(appliedVolts.in(Volts)); + armSim.update(GompeiLib.getLoopPeriod()); + + inputs.position = Rotation2d.fromRadians(armSim.getAngleRads()); + inputs.velocity = RadiansPerSecond.of(armSim.getVelocityRadPerSec()); + + inputs.appliedVolts = new double[constants.armParameters.numMotors()]; + inputs.supplyCurrentAmps = new double[constants.armParameters.numMotors()]; + inputs.torqueCurrentAmps = new double[constants.armParameters.numMotors()]; + inputs.temperatureCelsius = new double[constants.armParameters.numMotors()]; + + Arrays.fill(inputs.appliedVolts, appliedVolts.in(Volts)); + Arrays.fill(inputs.supplyCurrentAmps, armSim.getCurrentDrawAmps()); + Arrays.fill(inputs.torqueCurrentAmps, armSim.getCurrentDrawAmps()); + + inputs.positionGoal = Rotation2d.fromRadians(feedback.getGoal().position); + inputs.positionSetpoint = Rotation2d.fromRadians(feedback.getSetpoint().position); + inputs.positionError = Rotation2d.fromRadians(feedback.getPositionError()); + + inputs.gainSlot = gainSlot; + } + + @Override + public void setVoltageGoal(Voltage voltageGoal) { + isClosedLoop = false; + this.appliedVolts = voltageGoal; + } + + @Override + public void setPositionGoal(Rotation2d rotationGoal) { + isClosedLoop = true; + feedback.setGoal(rotationGoal.getRadians()); + } + + @Override + public boolean atVoltageGoal(Voltage voltageReference) { + return appliedVolts.isNear(voltageReference, Millivolts.of(500)); + } + + @Override + public boolean atPositionGoal(Rotation2d positionReference) { + return Math.abs(positionReference.getRadians() - armSim.getAngleRads()) + < constants.constraints.goalTolerance().get(Radians); + } + + @Override + public void setPosition(Rotation2d position) { + armSim.setState(position.getRadians(), 0); + } + + @Override + public void setGainSlot(GainSlot gainSlot) { + this.gainSlot = gainSlot; + switch (gainSlot) { + case ZERO: + feedback.setPID(constants.slot0Gains.kP().get(), 0.0, constants.slot0Gains.kD().get()); + break; + case ONE: + feedback.setPID(constants.slot1Gains.kP().get(), 0.0, constants.slot1Gains.kD().get()); + break; + case TWO: + feedback.setPID(constants.slot2Gains.kP().get(), 0.0, constants.slot2Gains.kD().get()); + break; + } + } + + @Override + public void updateGains(Gains gains, GainSlot gainSlot) { + feedback.setPID(gains.kP().get(), gains.kI().get(), gains.kD().get()); + feedforward = new ArmFeedforward(gains.kS().get(), gains.kG().get(), gains.kV().get()); + } + + @Override + public void updateConstraints(AngularPositionConstraints constraints) { + feedback.setConstraints( + new Constraints( + constraints.maxVelocity().get().in(RadiansPerSecond), + constraints.maxAcceleration().get().in(RadiansPerSecondPerSecond))); + feedback.setTolerance(constraints.goalTolerance().get().in(Radians)); + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/arm/ArmIOTalonFX.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/arm/ArmIOTalonFX.java new file mode 100644 index 00000000..34c310f2 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/arm/ArmIOTalonFX.java @@ -0,0 +1,268 @@ +package edu.wpi.team190.gompeilib.subsystems.arm; + +import static edu.wpi.first.units.Units.*; + +import com.ctre.phoenix6.BaseStatusSignal; +import com.ctre.phoenix6.StatusSignal; +import com.ctre.phoenix6.configs.MotionMagicConfigs; +import com.ctre.phoenix6.configs.TalonFXConfiguration; +import com.ctre.phoenix6.controls.MotionMagicVoltage; +import com.ctre.phoenix6.controls.VoltageOut; +import com.ctre.phoenix6.hardware.TalonFX; +import com.ctre.phoenix6.signals.GravityTypeValue; +import com.ctre.phoenix6.signals.NeutralModeValue; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.units.measure.Angle; +import edu.wpi.first.units.measure.AngularVelocity; +import edu.wpi.first.units.measure.Current; +import edu.wpi.first.units.measure.Temperature; +import edu.wpi.first.units.measure.Voltage; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.AngularPositionConstraints; +import edu.wpi.team190.gompeilib.core.utility.phoenix.GainSlot; +import edu.wpi.team190.gompeilib.core.utility.phoenix.PhoenixUtil; +import java.util.ArrayList; + +public class ArmIOTalonFX implements ArmIO { + protected final TalonFX talonFX; + private final TalonFX[] followTalonFX; + + private final StatusSignal positionRotations; + private final StatusSignal velocityRotationsPerSecond; + private ArrayList> appliedVolts; + private ArrayList> supplyCurrentAmps; + private ArrayList> torqueCurrentAmps; + private ArrayList> temperatureCelsius; + private final StatusSignal positionSetpointRotations; + private final StatusSignal positionErrorRotations; + + private StatusSignal[] statusSignals; + + private final VoltageOut voltageRequest; + private final MotionMagicVoltage positionVoltageRequest; + + private final TalonFXConfiguration config; + + protected final ArmConstants constants; + + public ArmIOTalonFX(ArmConstants constants) { + talonFX = new TalonFX(constants.armCANID, constants.canBus); + followTalonFX = new TalonFX[constants.armParameters.numMotors() - 1]; + + config = new TalonFXConfiguration(); + + config.MotorOutput.NeutralMode = NeutralModeValue.Brake; + config.CurrentLimits.SupplyCurrentLimit = constants.currentLimits.supplyCurrentLimit().in(Amps); + config.CurrentLimits.SupplyCurrentLimitEnable = true; + config.CurrentLimits.StatorCurrentLimit = constants.currentLimits.statorCurrentLimit().in(Amps); + config.CurrentLimits.StatorCurrentLimitEnable = true; + config.Feedback.SensorToMechanismRatio = constants.armParameters.gearRatio(); + config.MotorOutput.Inverted = constants.invertedValue; + + config.Slot0.withKP(constants.slot0Gains.kP().get()) + .withKD(constants.slot0Gains.kD().get()) + .withKS(constants.slot0Gains.kS().get()) + .withKV(constants.slot0Gains.kV().get()) + .withKA(constants.slot0Gains.kA().get()) + .withKG(constants.slot0Gains.kG().get()) + .withGravityType(GravityTypeValue.Arm_Cosine); + + config.Slot1.withKP(constants.slot1Gains.kP().get()) + .withKD(constants.slot1Gains.kD().get()) + .withKS(constants.slot1Gains.kS().get()) + .withKV(constants.slot1Gains.kV().get()) + .withKA(constants.slot1Gains.kA().get()) + .withKG(constants.slot1Gains.kG().get()) + .withGravityType(GravityTypeValue.Arm_Cosine); + + config.Slot2.withKP(constants.slot2Gains.kP().get()) + .withKD(constants.slot2Gains.kD().get()) + .withKS(constants.slot2Gains.kS().get()) + .withKV(constants.slot2Gains.kV().get()) + .withKA(constants.slot2Gains.kA().get()) + .withKG(constants.slot2Gains.kG().get()) + .withGravityType(GravityTypeValue.Arm_Cosine); + + config.MotorOutput.Inverted = constants.invertedValue; + config.ClosedLoopGeneral.ContinuousWrap = constants.armParameters.continuousOutput(); + config.MotionMagic = + new MotionMagicConfigs() + .withMotionMagicAcceleration( + constants.constraints.maxAcceleration().get().in(RotationsPerSecondPerSecond)) + .withMotionMagicCruiseVelocity( + constants.constraints.maxVelocity().get().in(RotationsPerSecond)); + + PhoenixUtil.tryUntilOk(5, () -> talonFX.getConfigurator().apply(config, 0.25)); + + appliedVolts = new ArrayList<>(); + supplyCurrentAmps = new ArrayList<>(); + torqueCurrentAmps = new ArrayList<>(); + temperatureCelsius = new ArrayList<>(); + + positionRotations = talonFX.getPosition(); + velocityRotationsPerSecond = talonFX.getVelocity(); + appliedVolts.add(talonFX.getMotorVoltage()); + supplyCurrentAmps.add(talonFX.getSupplyCurrent()); + torqueCurrentAmps.add(talonFX.getTorqueCurrent()); + temperatureCelsius.add(talonFX.getDeviceTemp()); + + positionSetpointRotations = talonFX.getClosedLoopReference(); + positionErrorRotations = talonFX.getClosedLoopError(); + + for (int i = 0; i < constants.armParameters.numMotors() - 1; i++) { + appliedVolts.add(followTalonFX[i].getMotorVoltage()); + supplyCurrentAmps.add(followTalonFX[i].getSupplyCurrent()); + torqueCurrentAmps.add(followTalonFX[i].getTorqueCurrent()); + temperatureCelsius.add(followTalonFX[i].getDeviceTemp()); + } + + voltageRequest = new VoltageOut(0).withEnableFOC(constants.enableFOC); + positionVoltageRequest = new MotionMagicVoltage(0).withEnableFOC(constants.enableFOC); + + var signalsList = new ArrayList>(); + + signalsList.add(positionRotations); + signalsList.add(velocityRotationsPerSecond); + signalsList.addAll(appliedVolts); + signalsList.addAll(supplyCurrentAmps); + signalsList.addAll(torqueCurrentAmps); + signalsList.addAll(temperatureCelsius); + signalsList.add(positionSetpointRotations); + signalsList.add(positionErrorRotations); + + statusSignals = new StatusSignal[signalsList.size()]; + + for (int i = 0; i < signalsList.size(); i++) { + statusSignals[i] = signalsList.get(i); + } + + BaseStatusSignal.setUpdateFrequencyForAll(1 / GompeiLib.getLoopPeriod(), statusSignals); + + talonFX.optimizeBusUtilization(); + + PhoenixUtil.registerSignals(constants.canBus.isNetworkFD(), statusSignals); + + talonFX.setPosition(0); + + this.constants = constants; + } + + @Override + public void updateInputs(ArmIOInputs inputs) { + + inputs.position = new Rotation2d(positionRotations.getValue()); + inputs.velocity = velocityRotationsPerSecond.getValue(); + + inputs.appliedVolts = new double[constants.armParameters.numMotors()]; + inputs.supplyCurrentAmps = new double[constants.armParameters.numMotors()]; + inputs.torqueCurrentAmps = new double[constants.armParameters.numMotors()]; + inputs.temperatureCelsius = new double[constants.armParameters.numMotors()]; + + for (int i = 0; i < constants.armParameters.numMotors(); i++) { + inputs.appliedVolts[i] = appliedVolts.get(i).getValueAsDouble(); + inputs.supplyCurrentAmps[i] = supplyCurrentAmps.get(i).getValueAsDouble(); + inputs.torqueCurrentAmps[i] = torqueCurrentAmps.get(i).getValueAsDouble(); + inputs.temperatureCelsius[i] = temperatureCelsius.get(i).getValueAsDouble(); + } + + inputs.positionGoal = new Rotation2d(positionVoltageRequest.getPositionMeasure()); + inputs.positionSetpoint = + Rotation2d.fromRotations(positionSetpointRotations.getValueAsDouble()); + inputs.positionError = Rotation2d.fromRotations(positionErrorRotations.getValueAsDouble()); + + inputs.gainSlot = GainSlot.integerToGainSlot(talonFX.getClosedLoopSlot().getValue()); + } + + @Override + public void setVoltageGoal(Voltage voltageGoal) { + talonFX.setControl(voltageRequest.withOutput(voltageGoal)); + } + + @Override + public void setPositionGoal(Rotation2d positionGoal) { + talonFX.setControl(positionVoltageRequest.withPosition(positionGoal.getRotations())); + } + + @Override + public boolean atVoltageGoal(Voltage voltageReference) { + return appliedVolts.get(0).getValue().isNear(voltageReference, Millivolts.of(500)); + } + + @Override + public boolean atPositionGoal(Rotation2d positionReference) { + return Math.abs(positionRotations.getValueAsDouble() - positionReference.getRotations()) + < constants.constraints.goalTolerance().get().in(Rotations); + } + + @Override + public void setPosition(Rotation2d position) { + talonFX.setPosition(position.getRotations()); + } + + @Override + public void setGainSlot(GainSlot gainSlot) { + switch (gainSlot) { + case ONE: + talonFX.setControl(positionVoltageRequest.withSlot(1)); + break; + case TWO: + talonFX.setControl(positionVoltageRequest.withSlot(2)); + break; + default: + talonFX.setControl(positionVoltageRequest.withSlot(0)); + } + } + + @Override + public void updateGains(Gains gains, GainSlot gainSlot) { + switch (gainSlot) { + case ZERO: + config.Slot0.withKP(gains.kP().get()) + .withKD(gains.kD().get()) + .withKS(gains.kS().get()) + .withKV(gains.kV().get()) + .withKA(gains.kA().get()) + .withKG(gains.kG().get()); + break; + case ONE: + config.Slot1.withKP(gains.kP().get()) + .withKD(gains.kD().get()) + .withKS(gains.kS().get()) + .withKV(gains.kV().get()) + .withKA(gains.kA().get()) + .withKG(gains.kG().get()); + break; + case TWO: + default: + config.Slot2.withKP(gains.kP().get()) + .withKD(gains.kD().get()) + .withKS(gains.kS().get()) + .withKV(gains.kV().get()) + .withKA(gains.kA().get()) + .withKG(gains.kG().get()); + break; + } + PhoenixUtil.tryUntilOk(5, () -> talonFX.getConfigurator().apply(config)); + + for (int i = 0; i < constants.armParameters.numMotors() - 1; i++) { + int finalI = i; + PhoenixUtil.tryUntilOk(5, () -> followTalonFX[finalI].getConfigurator().apply(config)); + } + } + + @Override + public void updateConstraints(AngularPositionConstraints constraints) { + config.MotionMagic = + new MotionMagicConfigs() + .withMotionMagicAcceleration( + constraints.maxAcceleration().get(RotationsPerSecondPerSecond)) + .withMotionMagicCruiseVelocity(constraints.maxVelocity().get(RotationsPerSecond)); + PhoenixUtil.tryUntilOk(5, () -> talonFX.getConfigurator().apply(config, 0.25)); + + for (int i = 0; i < constants.armParameters.numMotors() - 1; i++) { + int finalI = i; + PhoenixUtil.tryUntilOk(5, () -> followTalonFX[finalI].getConfigurator().apply(config, 0.25)); + } + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/arm/ArmIOTalonFXSim.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/arm/ArmIOTalonFXSim.java new file mode 100644 index 00000000..6a2087e5 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/arm/ArmIOTalonFXSim.java @@ -0,0 +1,59 @@ +package edu.wpi.team190.gompeilib.subsystems.arm; + +import static edu.wpi.first.units.Units.Radians; +import static edu.wpi.first.units.Units.RadiansPerSecond; + +import com.ctre.phoenix6.sim.TalonFXSimState; +import edu.wpi.first.math.system.plant.LinearSystemId; +import edu.wpi.first.units.measure.Angle; +import edu.wpi.first.units.measure.AngularVelocity; +import edu.wpi.first.wpilibj.RobotController; +import edu.wpi.first.wpilibj.simulation.SingleJointedArmSim; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import edu.wpi.team190.gompeilib.core.logging.Trace; + +public class ArmIOTalonFXSim extends ArmIOTalonFX { + private final SingleJointedArmSim armSim; + + private final TalonFXSimState armController; + + public ArmIOTalonFXSim(ArmConstants constants) { + super(constants); + armSim = + new SingleJointedArmSim( + LinearSystemId.createSingleJointedArmSystem( + constants.armParameters.motorConfig(), + constants.armParameters.momentOfInertia(), + constants.armParameters.gearRatio()), + constants.armParameters.motorConfig(), + constants.armParameters.gearRatio(), + constants.armParameters.lengthMeters(), + constants.armParameters.minAngle().getRadians(), + constants.armParameters.maxAngle().getRadians(), + true, + constants.armParameters.minAngle().getRadians()); + + armController = super.talonFX.getSimState(); + } + + @Override + @Trace + public void updateInputs(ArmIOInputs inputs) { + armController.setSupplyVoltage(RobotController.getBatteryVoltage()); + double armVoltage = armController.getMotorVoltage(); + + armSim.setInputVoltage(armVoltage); + + armSim.update(GompeiLib.getLoopPeriod()); + + Angle rotorPosition = + Angle.ofBaseUnits(armSim.getAngleRads() * constants.armParameters.gearRatio(), Radians); + AngularVelocity rotorVelocity = + AngularVelocity.ofBaseUnits( + armSim.getVelocityRadPerSec() * constants.armParameters.gearRatio(), RadiansPerSecond); + armController.setRawRotorPosition(rotorPosition); + armController.setRotorVelocity(rotorVelocity); + + super.updateInputs(inputs); + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/arm/ArmState.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/arm/ArmState.java new file mode 100644 index 00000000..46c8de97 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/arm/ArmState.java @@ -0,0 +1,7 @@ +package edu.wpi.team190.gompeilib.subsystems.arm; + +public enum ArmState { + IDLE, + OPEN_LOOP_VOLTAGE_CONTROL, + CLOSED_LOOP_POSITION_CONTROL; +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/drivebases/swervedrive/SwerveDrive.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/drivebases/swervedrive/SwerveDrive.java new file mode 100644 index 00000000..47937c3b --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/drivebases/swervedrive/SwerveDrive.java @@ -0,0 +1,491 @@ +// Copyright 2021-2024 FRC 6328 +package edu.wpi.team190.gompeilib.subsystems.drivebases.swervedrive; + +import choreo.auto.AutoFactory; +import choreo.trajectory.SwerveSample; +import com.pathplanner.lib.auto.AutoBuilder; +import com.pathplanner.lib.config.PIDConstants; +import com.pathplanner.lib.config.RobotConfig; +import com.pathplanner.lib.controllers.PPHolonomicDriveController; +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.Vector; +import edu.wpi.first.math.controller.PIDController; +import edu.wpi.first.math.filter.LinearFilter; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.math.geometry.Twist2d; +import edu.wpi.first.math.kinematics.ChassisSpeeds; +import edu.wpi.first.math.kinematics.SwerveDriveKinematics; +import edu.wpi.first.math.kinematics.SwerveModulePosition; +import edu.wpi.first.math.kinematics.SwerveModuleState; +import edu.wpi.first.math.numbers.N2; +import edu.wpi.first.math.util.Units; +import edu.wpi.first.wpilibj.DriverStation; +import edu.wpi.first.wpilibj2.command.SubsystemBase; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import edu.wpi.team190.gompeilib.core.io.components.inertial.GyroIO; +import edu.wpi.team190.gompeilib.core.io.components.inertial.GyroIOInputsAutoLogged; +import edu.wpi.team190.gompeilib.core.io.components.inertial.GyroIOPigeon2; +import edu.wpi.team190.gompeilib.core.logging.Trace; +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.phoenix.PhoenixOdometryThread; +import java.util.List; +import java.util.Optional; +import java.util.Queue; +import java.util.function.Consumer; +import java.util.function.Supplier; +import java.util.stream.IntStream; +import lombok.Getter; +import org.littletonrobotics.junction.Logger; + +public class SwerveDrive extends SubsystemBase { + private final SwerveDriveConstants driveConstants; + + private final GyroIO gyroIO; + private final GyroIOInputsAutoLogged gyroInputs; + private final SwerveModule[] modules; + + private final LinearFilter xFilter; + private final LinearFilter yFilter; + private double filteredX; + private double filteredY; + + private final SwerveDriveKinematics kinematics; + @Getter private Rotation2d rawGyroRotation; + private final SwerveModulePosition[] lastModulePositions; + @Getter private ChassisSpeeds measuredChassisSpeeds; + + @Getter private final AutoFactory autoFactory; + + private final Supplier robotPoseSupplier; + + private final PIDController autoXController; + private final PIDController autoYController; + private final PIDController autoHeadingController; + + private final Optional> yawTimestampQueue; + private final Optional> yawPositionQueue; + + private RobotConfig config; + + public SwerveDrive( + SwerveDriveConstants driveConstants, + GyroIO gyroIO, + SwerveModuleIO flModuleIO, + SwerveModuleIO frModuleIO, + SwerveModuleIO blModuleIO, + SwerveModuleIO brModuleIO, + Supplier robotPoseSupplier, + Consumer resetPoseConsumer) { + this.driveConstants = driveConstants; + this.gyroIO = gyroIO; + gyroInputs = new GyroIOInputsAutoLogged(); + modules = new SwerveModule[4]; // FL, FR, BL, BR + modules[0] = new SwerveModule(driveConstants, flModuleIO, 0); + modules[1] = new SwerveModule(driveConstants, frModuleIO, 1); + modules[2] = new SwerveModule(driveConstants, blModuleIO, 2); + modules[3] = new SwerveModule(driveConstants, brModuleIO, 3); + + xFilter = LinearFilter.movingAverage(10); + yFilter = LinearFilter.movingAverage(10); + filteredX = 0; + filteredY = 0; + + kinematics = this.driveConstants.driveConfig.kinematics(); + rawGyroRotation = new Rotation2d(); + lastModulePositions = // For delta tracking + new SwerveModulePosition[] { + new SwerveModulePosition(), + new SwerveModulePosition(), + new SwerveModulePosition(), + new SwerveModulePosition() + }; + + autoFactory = + new AutoFactory(robotPoseSupplier, resetPoseConsumer, this::choreoDrive, true, this); + + this.robotPoseSupplier = robotPoseSupplier; + + boolean isGryoHighFrequency = gyroIO instanceof GyroIOPigeon2; + + if (isGryoHighFrequency) { + // Start threads (no-op if no signals have been created) + PhoenixOdometryThread.getInstance(driveConstants).start(); + this.yawTimestampQueue = + Optional.of(PhoenixOdometryThread.getInstance(driveConstants).makeTimestampQueue()); + this.yawPositionQueue = + Optional.of( + PhoenixOdometryThread.getInstance(driveConstants).registerSignal(gyroIO.getYaw())); + } else { + this.yawTimestampQueue = Optional.empty(); + this.yawPositionQueue = Optional.empty(); + } + + autoHeadingController = + new PIDController( + driveConstants.autoRotationGains.kP().get(), + 0.0, + driveConstants.autoRotationGains.kD().get(), + GompeiLib.getLoopPeriod()); + autoXController = + new PIDController( + driveConstants.autoTranslationGains.kP().get(), + 0.0, + driveConstants.autoTranslationGains.kD().get(), + GompeiLib.getLoopPeriod()); + autoYController = + new PIDController( + driveConstants.autoTranslationGains.kP().get(), + 0.0, + driveConstants.autoTranslationGains.kD().get(), + GompeiLib.getLoopPeriod()); + + autoHeadingController.enableContinuousInput(-Math.PI, Math.PI); + autoHeadingController.setTolerance(Units.degreesToRadians(1.0)); + + measuredChassisSpeeds = new ChassisSpeeds(); + + try { + config = RobotConfig.fromGUISettings(); + } catch (Exception e) { + System.err.println("Error occurred while loading robot config: " + e.getMessage()); + } + + try { + AutoBuilder.configure( + this.robotPoseSupplier, + resetPoseConsumer, // resetPose + () -> getChassisSpeeds(), // get robotRelativeSpeeds + (speeds, feedforwards) -> { + List> forces = + IntStream.range(0, 4) + .mapToObj( + i -> + VecBuilder.fill( + feedforwards.robotRelativeForcesXNewtons()[i], + feedforwards.robotRelativeForcesYNewtons()[i])) + .toList(); + + runVelocity(speeds); + }, + new PPHolonomicDriveController( + new PIDConstants( + driveConstants.autoTranslationGains.kP().getAsDouble(), + driveConstants.autoTranslationGains.kI().getAsDouble(), + driveConstants.autoTranslationGains.kD().getAsDouble()), + new PIDConstants( + driveConstants.autoRotationGains.kP().getAsDouble(), + driveConstants.autoRotationGains.kI().getAsDouble(), + driveConstants.autoRotationGains.kD().getAsDouble())), + com.pathplanner.lib.config.RobotConfig.fromGUISettings(), + () -> { + var alliance = DriverStation.getAlliance(); + if (alliance.isPresent()) { + return alliance.get() == DriverStation.Alliance.Red; + } + return false; + }, + this); + } catch (Exception e) { + throw new RuntimeException("Failed to load PathPlanner robot config", e); + } + } + + @Trace + public void periodic() { + driveConstants.reentrantLock.lock(); + + if (yawTimestampQueue.isPresent() && yawPositionQueue.isPresent()) { + gyroIO.updateInputs(gyroInputs, yawTimestampQueue.get(), yawPositionQueue.get()); + yawTimestampQueue.get().clear(); + yawPositionQueue.get().clear(); + } else { + gyroIO.updateInputs(gyroInputs); + } + + for (int i = 0; i < 4; i++) { + modules[i].updateInputs(); + } + + driveConstants.reentrantLock.unlock(); + + Logger.processInputs("Drive/Gyro", gyroInputs); + + for (int i = 0; i < 4; i++) { + modules[i].periodic(); + } + + // Stop moving when disabled + if (DriverStation.isDisabled()) { + for (var module : modules) { + module.stop(); + } + + Logger.recordOutput("SwerveStates/Setpoints", new SwerveModuleState[] {}); + Logger.recordOutput("SwerveStates/SetpointsOptimized", new SwerveModuleState[] {}); + } + + Logger.recordOutput("SwerveStates/Measured", getModuleStates()); + Logger.recordOutput("SwerveChassisSpeeds/Measured", measuredChassisSpeeds); + + // Update odometry + double[] sampleTimestamps = + modules[0].getOdometryTimestamps(); // All signals are sampled together + int sampleCount = sampleTimestamps.length; + for (int i = 0; i < sampleCount; i++) { + // Read wheel positions and deltas from each module + SwerveModulePosition[] modulePositions = new SwerveModulePosition[4]; + SwerveModulePosition[] moduleDeltas = new SwerveModulePosition[4]; + for (int moduleIndex = 0; moduleIndex < 4; moduleIndex++) { + modulePositions[moduleIndex] = modules[moduleIndex].getOdometryPositions()[i]; + moduleDeltas[moduleIndex] = + new SwerveModulePosition( + modulePositions[moduleIndex].distanceMeters + - lastModulePositions[moduleIndex].distanceMeters, + modulePositions[moduleIndex].angle); + lastModulePositions[moduleIndex] = modulePositions[moduleIndex]; + } + + // Update gyro angle + if (gyroInputs.connected) { + // Use the real gyro angle + rawGyroRotation = gyroInputs.odometryYawPositions[i]; + } else { + // Use the angle delta from the kinematics and module deltas + Twist2d twist = kinematics.toTwist2d(moduleDeltas); + rawGyroRotation = rawGyroRotation.plus(new Rotation2d(twist.dtheta)); + } + + ChassisSpeeds chassisSpeeds = kinematics.toChassisSpeeds(getModuleStates()); + measuredChassisSpeeds = chassisSpeeds; + Translation2d rawFieldRelativeVelocity = + new Translation2d(chassisSpeeds.vxMetersPerSecond, chassisSpeeds.vyMetersPerSecond) + .rotateBy(getRawGyroRotation()); + + filteredX = xFilter.calculate(rawFieldRelativeVelocity.getX()); + filteredY = yFilter.calculate(rawFieldRelativeVelocity.getY()); + } + } + + /** + * Runs the drive at the desired velocity. + * + * @param speeds Speeds in meters/sec + */ + @Trace + public void runVelocity(ChassisSpeeds speeds) { + // Calculate module setpoints + ChassisSpeeds optimizedSpeeds = ChassisSpeeds.discretize(speeds, GompeiLib.getLoopPeriod()); + SwerveModuleState[] setpointStates = kinematics.toSwerveModuleStates(optimizedSpeeds); + SwerveDriveKinematics.desaturateWheelSpeeds( + setpointStates, driveConstants.driveConfig.maxLinearVelocityMetersPerSecond()); + + // Log unoptimized setpoints and setpoint speeds + Logger.recordOutput("SwerveStates/Setpoints", setpointStates); + Logger.recordOutput("SwerveChassisSpeeds/Setpoints", speeds); + + // Send setpoints to modules + for (int i = 0; i < 4; i++) { + modules[i].runSetpoint(setpointStates[i], new SwerveModuleState()); + } + + // Log optimized setpoints (runSetpoint mutates each state) + Logger.recordOutput("SwerveStates/SetpointsOptimized", setpointStates); + } + + /** + * Runs the drive at the desired velocity and torque. + * + * @param speeds Speeds in meters/sec + */ + @Trace + public void runVelocityTorque(ChassisSpeeds speeds, List> forces) { + if (forces.size() != 4) { + throw new IllegalArgumentException("Forces array must have 4 elements"); + } + // Calculate module setpoints + ChassisSpeeds optimizedSpeeds = ChassisSpeeds.discretize(speeds, GompeiLib.getLoopPeriod()); + SwerveModuleState[] setpointStates = kinematics.toSwerveModuleStates(optimizedSpeeds); + SwerveModuleState[] setpointTorques = new SwerveModuleState[4]; + SwerveDriveKinematics.desaturateWheelSpeeds( + setpointStates, driveConstants.driveConfig.maxLinearVelocityMetersPerSecond()); + + // Send setpoints to modules + for (int i = 0; i < 4; i++) { + Vector wheelDirection = + VecBuilder.fill(setpointStates[i].angle.getCos(), setpointStates[i].angle.getSin()); + setpointTorques[i] = + new SwerveModuleState( + forces.get(i).dot(wheelDirection) + * driveConstants.driveConfig.frontLeft().DriveMotorGearRatio, + setpointStates[i].angle); + + setpointStates[i].optimize(modules[i].getAngle()); + setpointTorques[i].optimize(modules[i].getAngle()); + + modules[i].runSetpoint(setpointStates[i], setpointTorques[i]); + } + + // Log optimized setpoints (runSetpoint mutates each state) + Logger.recordOutput("SwerveStates/SetpointsOptimized", setpointStates); + Logger.recordOutput("SwerveStates/TorquesOptimized", setpointTorques); + } + + /** Runs the drive in a straight line with the specified drive current. */ + @Trace + public void runCharacterization(double amps) { + for (int i = 0; i < 4; i++) { + modules[i].runCharacterization(amps); + } + } + + /** Stops the drive. */ + @Trace + public void stop() { + runVelocity(new ChassisSpeeds()); + } + + /** + * Stops the drive and turns the modules to an X arrangement to resist movement. The modules will + * return to their normal orientations the next time a nonzero velocity is requested. + */ + @Trace + public void stopWithX() { + Rotation2d[] headings = new Rotation2d[4]; + for (int i = 0; i < 4; i++) { + headings[i] = driveConstants.driveConfig.getModuleTranslations()[i].getAngle(); + } + kinematics.resetHeadings(headings); + stop(); + } + + /** Returns the module states (turn angles and drive velocities) for all of the modules. */ + @Trace + private SwerveModuleState[] getModuleStates() { + SwerveModuleState[] states = new SwerveModuleState[4]; + for (int i = 0; i < 4; i++) { + states[i] = modules[i].getState(); + } + return states; + } + + /** Returns the module positions (turn angles and drive positions) for all of the modules. */ + @Trace + public SwerveModulePosition[] getModulePositions() { + SwerveModulePosition[] states = new SwerveModulePosition[4]; + for (int i = 0; i < 4; i++) { + states[i] = modules[i].getPosition(); + } + return states; + } + + /** Returns the measured chassis speeds of the robot. */ + @Trace + private ChassisSpeeds getChassisSpeeds() { + return kinematics.toChassisSpeeds(getModuleStates()); + } + + /** Returns the position of each module in radians. */ + @Trace + public double[] getWheelRadiusCharacterizationPositions() { + double[] values = new double[4]; + for (int i = 0; i < 4; i++) { + values[i] = modules[i].getWheelRadiusCharacterizationPosition(); + } + return values; + } + + /** Returns the average velocity of the modules in rotations/sec (Phoenix native units). */ + @Trace + public double getFFCharacterizationVelocity() { + double output = 0.0; + for (int i = 0; i < 4; i++) { + output += modules[i].getFFCharacterizationVelocity() / 4.0; + } + return output; + } + + /** Returns the maximum linear speed in meters per sec. */ + @Trace + public double getMaxLinearSpeedMetersPerSec() { + return driveConstants.driveConfig.maxLinearVelocityMetersPerSecond(); + } + + /** Returns the maximum angular speed in radians per sec. */ + @Trace + public double getMaxAngularSpeedRadPerSec() { + return getMaxLinearSpeedMetersPerSec() / driveConstants.driveConfig.driveBaseRadius(); + } + + /** Returns the field relative velocity in X and Y. */ + @Trace + public Translation2d getFieldRelativeVelocity() { + return new Translation2d(filteredX, filteredY); + } + + /** Returns the current yaw velocity */ + @Trace + public double getYawVelocity() { + return gyroInputs.yawVelocityRadPerSec; + } + + /** Sets PID gains for modules */ + @Trace + public void setPIDGains(double drive_Kp, double drive_Kd, double turn_Kp, double turn_Kd) { + for (var module : modules) { + module.setPID(drive_Kp, drive_Kd, turn_Kp, turn_Kd); + } + } + + /** Sets FF gains for modules */ + @Trace + public void setFFGains(double kS, double kV) { + for (var module : modules) { + module.setFF(kS, kV); + } + } + + /** Runs a choreo path from swerve samples */ + @Trace + public void choreoDrive(SwerveSample sample) { + Pose2d pose = robotPoseSupplier.get(); + double xFF = sample.vx; + double yFF = sample.vy; + double rotationFF = sample.omega; + + double xFeedback = autoXController.calculate(pose.getX(), sample.x); + double yFeedback = autoYController.calculate(pose.getY(), sample.y); + double rotationFeedback = + autoHeadingController.calculate(pose.getRotation().getRadians(), sample.heading); + + ChassisSpeeds velocity = + ChassisSpeeds.fromFieldRelativeSpeeds( + xFF + xFeedback, + yFF + yFeedback, + rotationFF + rotationFeedback, + Rotation2d.fromRadians(sample.heading)); + + runVelocity(velocity); + Logger.recordOutput("Auto/Setpoint", sample.getPose()); + } + + public void setAutoControllers(Gains translationGains, Gains rotationGains) { + autoXController.setPID(translationGains.kP().get(), 0.0, translationGains.kD().get()); + autoYController.setPID(translationGains.kP().get(), 0.0, translationGains.kD().get()); + autoHeadingController.setPID(rotationGains.kP().get(), 0.0, rotationGains.kD().get()); + } + + /** + * Updates current limits. + * + * @param driveCurrentLimit The drive current limit. + * @param turnCurrentLimit The turn current limit. + */ + @Trace + public void updateCurrentLimits(double driveCurrentLimit, double turnCurrentLimit) { + for (SwerveModule s : modules) { + s.updateCurrentLimits(driveCurrentLimit, turnCurrentLimit); + } + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/drivebases/swervedrive/SwerveDriveConstants.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/drivebases/swervedrive/SwerveDriveConstants.java new file mode 100644 index 00000000..04fb8eee --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/drivebases/swervedrive/SwerveDriveConstants.java @@ -0,0 +1,101 @@ +package edu.wpi.team190.gompeilib.subsystems.drivebases.swervedrive; + +import com.ctre.phoenix6.CANBus; +import com.ctre.phoenix6.configs.CANcoderConfiguration; +import com.ctre.phoenix6.configs.TalonFXConfiguration; +import com.ctre.phoenix6.swerve.SwerveModuleConstants; +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.math.kinematics.SwerveDriveKinematics; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.units.AngleUnit; +import edu.wpi.first.units.DistanceUnit; +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.AngularPositionConstraints; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.LinearConstraints; +import edu.wpi.team190.gompeilib.core.utility.tunable.LoggedTunableMeasure; +import java.util.concurrent.locks.ReentrantLock; +import lombok.Builder; +import lombok.NonNull; + +@Builder(setterPrefix = "with") +public class SwerveDriveConstants { + @Builder.Default public final ReentrantLock reentrantLock = new ReentrantLock(); + + @NonNull public final DriveConfig driveConfig; + + @NonNull public final Gains driveGains; + @NonNull public final Gains turnGains; + + @NonNull public final Gains autoTranslationGains; + @NonNull public final Gains autoRotationGains; + + @NonNull public final AutoAlignConstants autoAlignConstants; + + @NonNull public final Double odometryFrequency; + @NonNull public final Double driverDeadband; + @NonNull public final Double operatorDeadband; + + @Builder(setterPrefix = "with") + public record DriveConfig( + @NonNull CANBus canBus, + @NonNull Integer pigeon2Id, + @NonNull Double maxLinearVelocityMetersPerSecond, + @NonNull Double wheelRadiusMeters, + @NonNull DCMotor driveModel, + @NonNull DCMotor turnModel, + @NonNull + SwerveModuleConstants + frontLeft, + @NonNull + SwerveModuleConstants + frontRight, + @NonNull + SwerveModuleConstants + backLeft, + @NonNull + SwerveModuleConstants + backRight, + @NonNull SwerveModuleConstants.ClosedLoopOutputType driveClosedLoopOutputType, + @NonNull SwerveModuleConstants.ClosedLoopOutputType steerClosedLoopOutputType, + @NonNull Double bumperWidth, + @NonNull Double bumperLength, + @NonNull Double robotMassKilograms, + @NonNull Double trackWidth, + @NonNull Double robotMOI, + @NonNull Double moduleCurrentLimit, + @NonNull Double wheelCOF) { + public Double driveBaseRadius() { + return Math.hypot( + (Math.abs(frontLeft.LocationX) + Math.abs(frontRight.LocationX)) / 2.0, + (Math.abs(frontLeft.LocationY) + Math.abs(backLeft.LocationY)) / 2.0); + } + + public Double maxAngularVelocity() { + return maxLinearVelocityMetersPerSecond / driveBaseRadius(); + } + + public Translation2d[] getModuleTranslations() { + return new Translation2d[] { + new Translation2d(frontLeft.LocationX, frontLeft.LocationY), + new Translation2d(frontRight.LocationX, frontRight.LocationY), + new Translation2d(backLeft.LocationX, backLeft.LocationY), + new Translation2d(backRight.LocationX, backRight.LocationY) + }; + } + + public SwerveDriveKinematics kinematics() { + return new SwerveDriveKinematics(getModuleTranslations()); + } + } + + @Builder(setterPrefix = "with") + public record AutoAlignConstants( + @NonNull Gains xGains, + @NonNull LinearConstraints xConstraints, + @NonNull Gains yGains, + @NonNull LinearConstraints yConstraints, + @NonNull Gains rotationGains, + @NonNull AngularPositionConstraints rotationConstraints, + @NonNull LoggedTunableMeasure linearThreshold, + @NonNull LoggedTunableMeasure angularThreshold) {} +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/drivebases/swervedrive/SwerveModule.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/drivebases/swervedrive/SwerveModule.java new file mode 100644 index 00000000..955d17e1 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/drivebases/swervedrive/SwerveModule.java @@ -0,0 +1,169 @@ +package edu.wpi.team190.gompeilib.subsystems.drivebases.swervedrive; + +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.kinematics.SwerveModulePosition; +import edu.wpi.first.math.kinematics.SwerveModuleState; +import edu.wpi.first.math.util.Units; +import edu.wpi.first.wpilibj.Alert; +import edu.wpi.first.wpilibj.Alert.AlertType; +import edu.wpi.team190.gompeilib.core.logging.Trace; +import lombok.Getter; +import org.littletonrobotics.junction.Logger; + +public class SwerveModule { + private final SwerveDriveConstants driveConstants; + private final SwerveModuleIO io; + private final ModuleIOInputsAutoLogged inputs = new ModuleIOInputsAutoLogged(); + private final int index; + + private final Alert driveDisconnectedAlert; + private final Alert turnDisconnectedAlert; + private final Alert turnEncoderDisconnectedAlert; + @Getter private SwerveModulePosition[] odometryPositions = new SwerveModulePosition[] {}; + + public SwerveModule(SwerveDriveConstants driveConstants, SwerveModuleIO io, int index) { + this.driveConstants = driveConstants; + this.io = io; + this.index = index; + driveDisconnectedAlert = + new Alert( + "Disconnected drive motor on module " + Integer.toString(index) + ".", + AlertType.kError); + turnDisconnectedAlert = + new Alert( + "Disconnected turn motor on module " + Integer.toString(index) + ".", AlertType.kError); + turnEncoderDisconnectedAlert = + new Alert( + "Disconnected turn encoder on module " + Integer.toString(index) + ".", + AlertType.kError); + } + + @Trace + public void updateInputs() { + io.updateInputs(inputs); + Logger.processInputs("Drive/Module" + Integer.toString(index), inputs); + } + + @Trace + public void periodic() { + // Calculate positions for odometry + int sampleCount = inputs.odometryTimestamps.length; // All signals are sampled together + odometryPositions = new SwerveModulePosition[sampleCount]; + for (int i = 0; i < sampleCount; i++) { + double positionMeters = + inputs.odometryDrivePositionsRadians[i] * driveConstants.driveConfig.wheelRadiusMeters(); + Rotation2d angle = inputs.odometryTurnPositions[i]; + odometryPositions[i] = new SwerveModulePosition(positionMeters, angle); + } + + // Update alerts + driveDisconnectedAlert.set(!inputs.driveConnected); + turnDisconnectedAlert.set(!inputs.turnConnected); + turnEncoderDisconnectedAlert.set(!inputs.turnEncoderConnected); + } + + /** Runs the module with the specified setpoint state. Mutates the state to optimize it. */ + @Trace + public void runSetpoint(SwerveModuleState state, SwerveModuleState torqueFeedforward) { + // Optimize veloci[ty setpoint + state.optimize(getAngle()); + state.cosineScale(inputs.turnPosition); + + double wheelTorqueNewtonMeters = torqueFeedforward.speedMetersPerSecond; + // Apply setpoints + io.setDriveVelocity( + state.speedMetersPerSecond / driveConstants.driveConfig.wheelRadiusMeters(), + driveConstants + .driveConfig + .driveModel() + .getCurrent( + wheelTorqueNewtonMeters + / driveConstants.driveConfig.frontLeft().DriveMotorGearRatio)); + io.setTurnPosition(state.angle); + } + + /** Runs the module with the specified output while controlling to zero degrees. */ + @Trace + public void runCharacterization(double amps) { + io.setDriveAmps(amps); + io.setTurnPosition(new Rotation2d()); + } + + /** Disables all outputs to motors. */ + @Trace + public void stop() { + io.setDriveAmps(0.0); + io.setTurnAmps(0.0); + } + + /** Returns the current turn angle of the module. */ + @Trace + public Rotation2d getAngle() { + return inputs.turnPosition; + } + + /** Returns the current drive position of the module in meters. */ + @Trace + public double getPositionMeters() { + return inputs.drivePositionRadians * driveConstants.driveConfig.wheelRadiusMeters(); + } + + /** Returns the current drive velocity of the module in meters per second. */ + @Trace + public double getVelocityMetersPerSec() { + return inputs.driveVelocityRadiansPerSecond * driveConstants.driveConfig.wheelRadiusMeters(); + } + + /** Returns the module position (turn angle and drive position). */ + @Trace + public SwerveModulePosition getPosition() { + return new SwerveModulePosition(getPositionMeters(), getAngle()); + } + + /** Returns the module state (turn angle and drive velocity). */ + @Trace + public SwerveModuleState getState() { + return new SwerveModuleState(getVelocityMetersPerSec(), getAngle()); + } + + /** Returns the timestamps of the samples received this cycle. */ + @Trace + public double[] getOdometryTimestamps() { + return inputs.odometryTimestamps; + } + + /** Returns the module position in radians. */ + @Trace + public double getWheelRadiusCharacterizationPosition() { + return inputs.drivePositionRadians; + } + + /** Returns the module velocity in rotations/sec (Phoenix native units). */ + @Trace + public double getFFCharacterizationVelocity() { + return Units.radiansToRotations(inputs.driveVelocityRadiansPerSecond); + } + + /** Sets module PID gains */ + @Trace + public void setPID(double drive_Kp, double drive_Kd, double turn_Kp, double turn_Kd) { + io.setPID(drive_Kp, drive_Kd, turn_Kp, turn_Kd); + } + + /** Sets module FF gains */ + @Trace + public void setFF(double kS, double kV) { + io.setFeedforward(kS, kV); + } + + /** + * Updates current limits. + * + * @param driveCurrentLimit The drive current limit. + * @param turnCurrentLimit The turn current limit. + */ + @Trace + public void updateCurrentLimits(double driveCurrentLimit, double turnCurrentLimit) { + io.updateCurrentLimits(driveCurrentLimit, turnCurrentLimit); + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/drivebases/swervedrive/SwerveModuleIO.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/drivebases/swervedrive/SwerveModuleIO.java new file mode 100644 index 00000000..90c99ce4 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/drivebases/swervedrive/SwerveModuleIO.java @@ -0,0 +1,67 @@ +package edu.wpi.team190.gompeilib.subsystems.drivebases.swervedrive; + +import edu.wpi.first.math.geometry.Rotation2d; +import org.littletonrobotics.junction.AutoLog; + +public interface SwerveModuleIO { + @AutoLog + public static class ModuleIOInputs { + public double drivePositionRadians = 0.0; + public double driveVelocityRadiansPerSecond = 0.0; + public double driveAppliedVolts = 0.0; + public double driveSupplyCurrentAmps = 0.0; + public double driveTorqueCurrentAmps = 0.0; + public double driveTemperatureCelcius = 0.0; + public double driveVelocitySetpointRadiansPerSecond = 0.0; + public double driveVelocityErrorRadiansPerSecond = 0.0; + + public Rotation2d turnAbsolutePosition = new Rotation2d(); + public Rotation2d turnPosition = new Rotation2d(); + public double turnVelocityRadiansPerSecond = 0.0; + public double turnAppliedVolts = 0.0; + public double turnSupplyCurrentAmps = 0.0; + public double turnTorqueCurrentAmps = 0.0; + public double turnTemperatureCelcius = 0.0; + public Rotation2d turnPositionGoal = new Rotation2d(); + public Rotation2d turnPositionSetpoint = new Rotation2d(); + public Rotation2d turnPositionError = new Rotation2d(); + + public boolean driveConnected = false; + public boolean turnConnected = false; + public boolean turnEncoderConnected = false; + + public double[] odometryTimestamps = new double[] {}; + public double[] odometryDrivePositionsRadians = new double[] {}; + public Rotation2d[] odometryTurnPositions = new Rotation2d[] {}; + } + + /** Updates the set of loggable inputs. */ + public default void updateInputs(ModuleIOInputs inputs) {} + + /** Run the drive motor at the specified open loop value. */ + public default void setDriveAmps(double currentAmps) {} + + /** Run the turn motor at the specified open loop value. */ + public default void setTurnAmps(double currentAmps) {} + + /** Run the drive motor at the specified velocity. */ + public default void setDriveVelocity( + double velocityRadiansPerSecond, double currentFeedforward) {} + + /** Run the turn motor to the specified rotation. */ + public default void setTurnPosition(Rotation2d position) {} + + /** Sets the module PID gains */ + public default void setPID(double drive_Kp, double drive_Kd, double turn_Kp, double turn_Kd) {} + + /** Sets the module FF gains */ + public default void setFeedforward(double drive_Ks, double drive_Kv) {} + + /** + * Updates current limits. + * + * @param driveCurrentLimit The drive current limit. + * @param turnCurrentLimit The turn current limit. + */ + public default void updateCurrentLimits(double driveCurrentLimit, double turnCurrentLimit) {} +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/drivebases/swervedrive/SwerveModuleIOSim.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/drivebases/swervedrive/SwerveModuleIOSim.java new file mode 100644 index 00000000..19bc8c72 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/drivebases/swervedrive/SwerveModuleIOSim.java @@ -0,0 +1,149 @@ +package edu.wpi.team190.gompeilib.subsystems.drivebases.swervedrive; + +import com.ctre.phoenix6.configs.CANcoderConfiguration; +import com.ctre.phoenix6.configs.TalonFXConfiguration; +import com.ctre.phoenix6.swerve.SwerveModuleConstants; +import edu.wpi.first.math.MathUtil; +import edu.wpi.first.math.controller.PIDController; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.system.plant.LinearSystemId; +import edu.wpi.first.math.util.Units; +import edu.wpi.first.wpilibj.Timer; +import edu.wpi.first.wpilibj.simulation.DCMotorSim; +import edu.wpi.team190.gompeilib.core.GompeiLib; + +/** + * Physics sim implementation of module IO. Simulation is not vendor-specific, but the sim models + * are configured using a set of module constants from Phoenix. + * + *

Simulation is always based on voltage control. + */ +public class SwerveModuleIOSim implements SwerveModuleIO { + private static final double DRIVE_KP = 0.05; + private static final double DRIVE_KD = 0.0; + private static final double DRIVE_KS = 0.0; + private static final double DRIVE_KV_ROT = + 0.91035; // Same units as TunerConstants: (volt * secs) / rotation + private static final double DRIVE_KV = 1.0 / Units.rotationsToRadians(1.0 / DRIVE_KV_ROT); + private static final double TURN_KP = 8.0; + private static final double TURN_KD = 0.0; + + private final DCMotorSim driveSim; + private final DCMotorSim turnSim; + + private boolean driveClosedLoop; + private boolean turnClosedLoop; + + private final PIDController driveController; + private final PIDController turnController; + + private double driveFFVolts; + private double driveAppliedVolts; + private double turnAppliedVolts; + + public SwerveModuleIOSim( + SwerveDriveConstants driveConstants, + SwerveModuleConstants + constants) { + // Create drive and turn sim models + driveSim = + new DCMotorSim( + LinearSystemId.createDCMotorSystem( + driveConstants.driveConfig.driveModel(), + constants.DriveInertia, + constants.DriveMotorGearRatio), + driveConstants.driveConfig.driveModel()); + turnSim = + new DCMotorSim( + LinearSystemId.createDCMotorSystem( + driveConstants.driveConfig.turnModel(), + constants.SteerInertia, + constants.SteerMotorGearRatio), + driveConstants.driveConfig.turnModel()); + + driveClosedLoop = false; + turnClosedLoop = false; + + driveController = new PIDController(DRIVE_KP, 0, DRIVE_KD); + turnController = new PIDController(TURN_KP, 0, TURN_KD); + + driveFFVolts = 0.0; + driveAppliedVolts = 0.0; + turnAppliedVolts = 0.0; + + // Enable wrapping for turn PID + turnController.enableContinuousInput(-Math.PI, Math.PI); + } + + @Override + public void updateInputs(ModuleIOInputs inputs) { + // Run closed-loop control + if (driveClosedLoop) { + driveAppliedVolts = + driveFFVolts + driveController.calculate(driveSim.getAngularVelocityRadPerSec()); + } else { + driveController.reset(); + } + if (turnClosedLoop) { + turnAppliedVolts = turnController.calculate(turnSim.getAngularPositionRad()); + } else { + turnController.reset(); + } + + // Update simulation state + driveSim.setInputVoltage(MathUtil.clamp(driveAppliedVolts, -12.0, 12.0)); + turnSim.setInputVoltage(MathUtil.clamp(turnAppliedVolts, -12.0, 12.0)); + driveSim.update(GompeiLib.getLoopPeriod()); + turnSim.update(GompeiLib.getLoopPeriod()); + + inputs.drivePositionRadians = driveSim.getAngularPositionRad(); + inputs.driveVelocityRadiansPerSecond = driveSim.getAngularVelocityRadPerSec(); + inputs.driveAppliedVolts = driveAppliedVolts; + inputs.driveSupplyCurrentAmps = Math.abs(driveSim.getCurrentDrawAmps()); + inputs.driveVelocitySetpointRadiansPerSecond = driveController.getSetpoint(); + inputs.driveVelocityErrorRadiansPerSecond = driveController.getError(); + + inputs.turnAbsolutePosition = new Rotation2d(turnSim.getAngularPositionRad()); + inputs.turnPosition = new Rotation2d(turnSim.getAngularPositionRad()); + inputs.turnVelocityRadiansPerSecond = turnSim.getAngularVelocityRadPerSec(); + inputs.turnAppliedVolts = turnAppliedVolts; + inputs.turnSupplyCurrentAmps = Math.abs(turnSim.getCurrentDrawAmps()); + inputs.turnPositionGoal = Rotation2d.fromRadians(turnController.getSetpoint()); + inputs.turnPositionSetpoint = Rotation2d.fromRadians(turnController.getSetpoint()); + inputs.turnPositionError = Rotation2d.fromRadians(turnController.getError()); + + inputs.driveConnected = true; + inputs.turnConnected = true; + inputs.turnEncoderConnected = true; + + inputs.odometryTimestamps = new double[] {Timer.getTimestamp()}; + inputs.odometryDrivePositionsRadians = new double[] {inputs.drivePositionRadians}; + inputs.odometryTurnPositions = new Rotation2d[] {inputs.turnPosition}; + } + + @Override + public void setDriveAmps(double currentAmps) { + driveClosedLoop = false; + driveAppliedVolts = currentAmps; + } + + @Override + public void setTurnAmps(double currentAmps) { + turnClosedLoop = false; + turnAppliedVolts = currentAmps; + } + + @Override + public void setDriveVelocity(double velocityRadiansPerSecond, double currentFeedforward) { + driveClosedLoop = true; + driveFFVolts = + DRIVE_KS * Math.signum(velocityRadiansPerSecond) + DRIVE_KV * velocityRadiansPerSecond; + driveController.setSetpoint(velocityRadiansPerSecond); + } + + @Override + public void setTurnPosition(Rotation2d position) { + turnClosedLoop = true; + turnController.setSetpoint(position.getRadians()); + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/drivebases/swervedrive/SwerveModuleIOTalonFX.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/drivebases/swervedrive/SwerveModuleIOTalonFX.java new file mode 100644 index 00000000..55c89eec --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/drivebases/swervedrive/SwerveModuleIOTalonFX.java @@ -0,0 +1,330 @@ +package edu.wpi.team190.gompeilib.subsystems.drivebases.swervedrive; + +import static edu.wpi.team190.gompeilib.core.utility.phoenix.PhoenixUtil.tryUntilOk; + +import com.ctre.phoenix6.BaseStatusSignal; +import com.ctre.phoenix6.StatusSignal; +import com.ctre.phoenix6.configs.CANcoderConfiguration; +import com.ctre.phoenix6.configs.TalonFXConfiguration; +import com.ctre.phoenix6.controls.*; +import com.ctre.phoenix6.hardware.CANcoder; +import com.ctre.phoenix6.hardware.TalonFX; +import com.ctre.phoenix6.signals.FeedbackSensorSourceValue; +import com.ctre.phoenix6.signals.InvertedValue; +import com.ctre.phoenix6.signals.NeutralModeValue; +import com.ctre.phoenix6.swerve.SwerveModuleConstants; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.util.Units; +import edu.wpi.first.units.measure.Angle; +import edu.wpi.first.units.measure.AngularVelocity; +import edu.wpi.first.units.measure.Current; +import edu.wpi.first.units.measure.Temperature; +import edu.wpi.first.units.measure.Voltage; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import edu.wpi.team190.gompeilib.core.logging.Trace; +import edu.wpi.team190.gompeilib.core.utility.phoenix.PhoenixOdometryThread; +import edu.wpi.team190.gompeilib.core.utility.phoenix.PhoenixUtil; +import java.util.Queue; + +/** + * Module IO implementation for Talon FX drive motor controller, Talon FX turn motor controller, and + * CANcoder. Configured using a set of module constants from Phoenix. + * + *

Device configuration and other behaviors not exposed by TunerConstants can be customized here. + */ +public class SwerveModuleIOTalonFX implements SwerveModuleIO { + protected final TalonFX driveTalonFX; + protected final TalonFX turnTalonFX; + protected final CANcoder cancoder; + + private final TalonFXConfiguration driveConfig; + private final TalonFXConfiguration turnConfig; + private final CANcoderConfiguration cancoderConfig; + + private final StatusSignal drivePositionRotations; + private final StatusSignal driveVelocityRotationsPerSecond; + private final StatusSignal driveAppliedVolts; + private final StatusSignal driveSupplyCurrentAmps; + private final StatusSignal driveTorqueCurrentAmps; + private final StatusSignal driveTemperatureCelcius; + private final StatusSignal driveVelocitySetpointRotationsPerSecond; + private final StatusSignal driveVelocityErrorRotationsPerSecond; + + private final StatusSignal turnAbsolutePositionRotations; + private final StatusSignal turnPositionRotations; + private final StatusSignal turnVelocityRotationsPerSecond; + private final StatusSignal turnAppliedVolts; + private final StatusSignal turnSupplyCurrentAmps; + private final StatusSignal turnTorqueCurrentAmps; + private final StatusSignal turnTemperatureCelcius; + private Rotation2d turnPositionGoal; + private final StatusSignal turnPositionSetpointRotations; + private final StatusSignal turnPositionErrorRotations; + + private final Queue timestampQueue; + private final Queue drivePositionQueue; + private final Queue turnPositionQueue; + + private final TorqueCurrentFOC torqueCurrentRequest; + private final VelocityTorqueCurrentFOC velocityTorqueCurrentRequest; + private final VelocityVoltage velocityVoltageRequest; + private final MotionMagicTorqueCurrentFOC positionTorqueCurrentRequest; + private final MotionMagicVoltage positionVoltageRequest; + + private final SwerveModuleConstants.ClosedLoopOutputType driveClosedLoopOutputType; + private final SwerveModuleConstants.ClosedLoopOutputType turnClosedLoopOutputType; + + public SwerveModuleIOTalonFX( + SwerveDriveConstants driveConstants, + SwerveModuleConstants + constants) { + driveTalonFX = new TalonFX(constants.DriveMotorId, driveConstants.driveConfig.canBus()); + turnTalonFX = new TalonFX(constants.SteerMotorId, driveConstants.driveConfig.canBus()); + cancoder = new CANcoder(constants.EncoderId, driveConstants.driveConfig.canBus()); + + driveConfig = new TalonFXConfiguration(); + driveConfig.MotorOutput.NeutralMode = NeutralModeValue.Brake; + driveConfig.Slot0 = constants.DriveMotorGains; + driveConfig.Feedback.SensorToMechanismRatio = constants.DriveMotorGearRatio; + driveConfig.TorqueCurrent.PeakForwardTorqueCurrent = constants.SlipCurrent; + driveConfig.TorqueCurrent.PeakReverseTorqueCurrent = -constants.SlipCurrent; + driveConfig.ClosedLoopRamps.TorqueClosedLoopRampPeriod = GompeiLib.getLoopPeriod(); + driveConfig.CurrentLimits.StatorCurrentLimit = constants.SlipCurrent; + driveConfig.CurrentLimits.StatorCurrentLimitEnable = true; + driveConfig.MotorOutput.Inverted = + constants.DriveMotorInverted + ? InvertedValue.Clockwise_Positive + : InvertedValue.CounterClockwise_Positive; + tryUntilOk(5, () -> driveTalonFX.getConfigurator().apply(driveConfig, 0.25)); + tryUntilOk(5, () -> driveTalonFX.setPosition(0.0, 0.25)); + + turnConfig = new TalonFXConfiguration(); + turnConfig.MotorOutput.NeutralMode = NeutralModeValue.Brake; + turnConfig.Slot0 = constants.SteerMotorGains; + turnConfig.Feedback.FeedbackRemoteSensorID = constants.EncoderId; + turnConfig.Feedback.FeedbackSensorSource = + switch (constants.FeedbackSource) { + case RemoteCANcoder -> FeedbackSensorSourceValue.RemoteCANcoder; + case FusedCANcoder -> FeedbackSensorSourceValue.FusedCANcoder; + case SyncCANcoder -> FeedbackSensorSourceValue.SyncCANcoder; + default -> + throw new IllegalArgumentException("Unexpected value: " + constants.FeedbackSource); + }; + turnConfig.Feedback.RotorToSensorRatio = constants.SteerMotorGearRatio; + turnConfig.TorqueCurrent.PeakForwardTorqueCurrent = 40.0; + turnConfig.TorqueCurrent.PeakReverseTorqueCurrent = -40.0; + turnConfig.ClosedLoopRamps.TorqueClosedLoopRampPeriod = GompeiLib.getLoopPeriod(); + turnConfig.CurrentLimits.StatorCurrentLimit = 40.0; + turnConfig.CurrentLimits.StatorCurrentLimitEnable = true; + turnConfig.MotionMagic.MotionMagicCruiseVelocity = 100.0 / constants.SteerMotorGearRatio; + turnConfig.MotionMagic.MotionMagicAcceleration = + turnConfig.MotionMagic.MotionMagicCruiseVelocity / 0.100; + turnConfig.MotionMagic.MotionMagicExpo_kV = 0.12 * constants.SteerMotorGearRatio; + turnConfig.MotionMagic.MotionMagicExpo_kA = 0.1; + turnConfig.ClosedLoopGeneral.ContinuousWrap = true; + turnConfig.MotorOutput.Inverted = + constants.SteerMotorInverted + ? InvertedValue.Clockwise_Positive + : InvertedValue.CounterClockwise_Positive; + tryUntilOk(5, () -> turnTalonFX.getConfigurator().apply(turnConfig, 0.25)); + + cancoderConfig = constants.EncoderInitialConfigs; + cancoderConfig.MagnetSensor.MagnetOffset = constants.EncoderOffset; + tryUntilOk(5, () -> cancoder.getConfigurator().apply(cancoderConfig, 0.25)); + + drivePositionRotations = driveTalonFX.getPosition(); + driveVelocityRotationsPerSecond = driveTalonFX.getVelocity(); + driveAppliedVolts = driveTalonFX.getMotorVoltage(); + driveSupplyCurrentAmps = driveTalonFX.getSupplyCurrent(); + driveTorqueCurrentAmps = driveTalonFX.getTorqueCurrent(); + driveTemperatureCelcius = driveTalonFX.getDeviceTemp(); + driveVelocitySetpointRotationsPerSecond = driveTalonFX.getClosedLoopReference(); + driveVelocityErrorRotationsPerSecond = driveTalonFX.getClosedLoopError(); + + turnAbsolutePositionRotations = cancoder.getAbsolutePosition(); + turnPositionRotations = turnTalonFX.getPosition(); + turnVelocityRotationsPerSecond = turnTalonFX.getVelocity(); + turnAppliedVolts = turnTalonFX.getMotorVoltage(); + turnSupplyCurrentAmps = turnTalonFX.getSupplyCurrent(); + turnTorqueCurrentAmps = turnTalonFX.getTorqueCurrent(); + turnTemperatureCelcius = turnTalonFX.getDeviceTemp(); + turnPositionGoal = new Rotation2d(); + turnPositionSetpointRotations = turnTalonFX.getClosedLoopReference(); + turnPositionErrorRotations = turnTalonFX.getClosedLoopError(); + + timestampQueue = PhoenixOdometryThread.getInstance(driveConstants).makeTimestampQueue(); + drivePositionQueue = + PhoenixOdometryThread.getInstance(driveConstants) + .registerSignal(driveTalonFX.getPosition()); + turnPositionQueue = + PhoenixOdometryThread.getInstance(driveConstants).registerSignal(turnTalonFX.getPosition()); + + torqueCurrentRequest = new TorqueCurrentFOC(0.0); + velocityTorqueCurrentRequest = new VelocityTorqueCurrentFOC(0.0); + velocityVoltageRequest = new VelocityVoltage(0.0); + positionTorqueCurrentRequest = new MotionMagicTorqueCurrentFOC(0.0); + positionVoltageRequest = new MotionMagicVoltage(0.0); + + driveClosedLoopOutputType = constants.DriveMotorClosedLoopOutput; + turnClosedLoopOutputType = constants.SteerMotorClosedLoopOutput; + + // Configure periodic frames + BaseStatusSignal.setUpdateFrequencyForAll( + driveConstants.odometryFrequency, + drivePositionRotations, + turnPositionRotations, + turnAbsolutePositionRotations); + BaseStatusSignal.setUpdateFrequencyForAll( + 50.0, + driveVelocityRotationsPerSecond, + driveAppliedVolts, + driveSupplyCurrentAmps, + driveTorqueCurrentAmps, + driveTemperatureCelcius, + driveVelocitySetpointRotationsPerSecond, + driveVelocityErrorRotationsPerSecond, + turnVelocityRotationsPerSecond, + turnAppliedVolts, + turnSupplyCurrentAmps, + turnTorqueCurrentAmps, + turnTemperatureCelcius, + turnPositionSetpointRotations, + turnPositionErrorRotations); + + driveTalonFX.optimizeBusUtilization(); + turnTalonFX.optimizeBusUtilization(); + cancoder.optimizeBusUtilization(); + + PhoenixUtil.registerSignals( + true, + drivePositionRotations, + turnPositionRotations, + turnAbsolutePositionRotations, + driveVelocityRotationsPerSecond, + driveAppliedVolts, + driveSupplyCurrentAmps, + driveTorqueCurrentAmps, + driveTemperatureCelcius, + driveVelocitySetpointRotationsPerSecond, + driveVelocityErrorRotationsPerSecond, + turnVelocityRotationsPerSecond, + turnAppliedVolts, + turnSupplyCurrentAmps, + turnTorqueCurrentAmps, + turnTemperatureCelcius, + turnPositionSetpointRotations, + turnPositionErrorRotations); + } + + @Override + @Trace + public void updateInputs(ModuleIOInputs inputs) { + inputs.drivePositionRadians = + Units.rotationsToRadians(drivePositionRotations.getValueAsDouble()); + inputs.driveVelocityRadiansPerSecond = + Units.rotationsToRadians(driveVelocityRotationsPerSecond.getValueAsDouble()); + inputs.driveAppliedVolts = driveAppliedVolts.getValueAsDouble(); + inputs.driveSupplyCurrentAmps = driveSupplyCurrentAmps.getValueAsDouble(); + inputs.driveTorqueCurrentAmps = driveTorqueCurrentAmps.getValueAsDouble(); + inputs.driveTemperatureCelcius = driveTemperatureCelcius.getValueAsDouble(); + inputs.driveVelocitySetpointRadiansPerSecond = + Units.rotationsToRadians(driveVelocitySetpointRotationsPerSecond.getValueAsDouble()); + inputs.driveVelocityErrorRadiansPerSecond = + Units.rotationsToRadians(driveVelocityErrorRotationsPerSecond.getValueAsDouble()); + + inputs.turnAbsolutePosition = + Rotation2d.fromRotations(turnAbsolutePositionRotations.getValueAsDouble()); + inputs.turnPosition = Rotation2d.fromRotations(turnPositionRotations.getValueAsDouble()); + inputs.turnVelocityRadiansPerSecond = + Units.rotationsToRadians(turnVelocityRotationsPerSecond.getValueAsDouble()); + inputs.turnAppliedVolts = turnAppliedVolts.getValueAsDouble(); + inputs.turnSupplyCurrentAmps = turnSupplyCurrentAmps.getValueAsDouble(); + inputs.turnTorqueCurrentAmps = turnTorqueCurrentAmps.getValueAsDouble(); + inputs.turnTemperatureCelcius = turnTemperatureCelcius.getValueAsDouble(); + inputs.turnPositionGoal = turnPositionGoal; + inputs.turnPositionSetpoint = + Rotation2d.fromRotations(turnPositionSetpointRotations.getValueAsDouble()); + inputs.turnPositionError = + Rotation2d.fromRotations(turnPositionErrorRotations.getValueAsDouble()); + + inputs.odometryTimestamps = + timestampQueue.stream().mapToDouble((Double value) -> value).toArray(); + inputs.odometryDrivePositionsRadians = + drivePositionQueue.stream().mapToDouble(Units::rotationsToRadians).toArray(); + inputs.odometryTurnPositions = + turnPositionQueue.stream().map(Rotation2d::fromRotations).toArray(Rotation2d[]::new); + + timestampQueue.clear(); + drivePositionQueue.clear(); + turnPositionQueue.clear(); + } + + @Override + @Trace + public void setDriveAmps(double currentAmps) { + driveTalonFX.setControl(torqueCurrentRequest.withOutput(currentAmps)); + } + + @Override + @Trace + public void setTurnAmps(double currentAmps) { + turnTalonFX.setControl(torqueCurrentRequest.withOutput(currentAmps)); + } + + @Override + @Trace + public void setDriveVelocity(double velocityRadiansPerSecond, double currentFeedforward) { + if (driveClosedLoopOutputType.equals(SwerveModuleConstants.ClosedLoopOutputType.Voltage)) { + driveTalonFX.setControl( + velocityVoltageRequest + .withVelocity(Units.radiansToRotations(velocityRadiansPerSecond)) + .withFeedForward(currentFeedforward)); + } else if (driveClosedLoopOutputType.equals( + SwerveModuleConstants.ClosedLoopOutputType.TorqueCurrentFOC)) { + driveTalonFX.setControl( + velocityTorqueCurrentRequest + .withVelocity(Units.radiansToRotations(velocityRadiansPerSecond)) + .withFeedForward(currentFeedforward)); + } + } + + @Override + @Trace + public void setTurnPosition(Rotation2d rotation) { + turnPositionGoal = rotation; + if (turnClosedLoopOutputType.equals(SwerveModuleConstants.ClosedLoopOutputType.Voltage)) + turnTalonFX.setControl(positionVoltageRequest.withPosition(rotation.getRotations())); + if (turnClosedLoopOutputType.equals( + SwerveModuleConstants.ClosedLoopOutputType.TorqueCurrentFOC)) + turnTalonFX.setControl(positionTorqueCurrentRequest.withPosition(rotation.getRotations())); + } + + @Override + @Trace + public void setPID(double drive_Kp, double drive_Kd, double turn_Kp, double turn_Kd) { + driveConfig.Slot0.kP = drive_Kp; + driveConfig.Slot0.kD = drive_Kd; + turnConfig.Slot0.kP = turn_Kp; + turnConfig.Slot0.kD = turn_Kd; + tryUntilOk(5, () -> driveTalonFX.getConfigurator().apply(driveConfig, 0.25)); + tryUntilOk(5, () -> turnTalonFX.getConfigurator().apply(turnConfig, 0.25)); + } + + @Override + @Trace + public void setFeedforward(double drive_Ks, double drive_Kv) { + driveConfig.Slot0.kS = drive_Ks; + driveConfig.Slot0.kV = drive_Kv; + tryUntilOk(5, () -> driveTalonFX.getConfigurator().apply(driveConfig, 0.25)); + tryUntilOk(5, () -> turnTalonFX.getConfigurator().apply(turnConfig, 0.25)); + } + + @Override + @Trace + public void updateCurrentLimits(double driveCurrentLimit, double turnCurrentLimit) { + driveConfig.CurrentLimits.StatorCurrentLimit = driveCurrentLimit; + turnConfig.CurrentLimits.StatorCurrentLimit = turnCurrentLimit; + tryUntilOk(5, () -> driveTalonFX.getConfigurator().apply(driveConfig, 0.25)); + tryUntilOk(5, () -> turnTalonFX.getConfigurator().apply(turnConfig, 0.25)); + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/drivebases/swervedrive/SwerveModuleIOTalonFXSim.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/drivebases/swervedrive/SwerveModuleIOTalonFXSim.java new file mode 100644 index 00000000..b112b0af --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/drivebases/swervedrive/SwerveModuleIOTalonFXSim.java @@ -0,0 +1,87 @@ +package edu.wpi.team190.gompeilib.subsystems.drivebases.swervedrive; + +import com.ctre.phoenix6.configs.CANcoderConfiguration; +import com.ctre.phoenix6.configs.TalonFXConfiguration; +import com.ctre.phoenix6.sim.CANcoderSimState; +import com.ctre.phoenix6.sim.TalonFXSimState; +import com.ctre.phoenix6.swerve.SwerveModuleConstants; +import edu.wpi.first.math.system.plant.LinearSystemId; +import edu.wpi.first.wpilibj.RobotController; +import edu.wpi.first.wpilibj.simulation.DCMotorSim; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import edu.wpi.team190.gompeilib.core.logging.Trace; + +public class SwerveModuleIOTalonFXSim extends SwerveModuleIOTalonFX { + + private final DCMotorSim steerMotorSim; + private final DCMotorSim driveMotorSim; + + private final TalonFXSimState steerController; + private final TalonFXSimState driveController; + private final CANcoderSimState encoderController; + private final double offset; + + public SwerveModuleIOTalonFXSim( + SwerveDriveConstants driveConstants, + SwerveModuleConstants + constants) { + + super(driveConstants, constants); + driveMotorSim = + new DCMotorSim( + LinearSystemId.createDCMotorSystem( + driveConstants.driveConfig.driveModel(), + constants.DriveInertia, + constants.DriveMotorGearRatio), + driveConstants.driveConfig.driveModel()); + steerMotorSim = + new DCMotorSim( + LinearSystemId.createDCMotorSystem( + driveConstants.driveConfig.turnModel(), + constants.SteerInertia, + constants.SteerMotorGearRatio), + driveConstants.driveConfig.turnModel()); + + steerController = super.turnTalonFX.getSimState(); + driveController = super.driveTalonFX.getSimState(); + encoderController = super.cancoder.getSimState(); + + offset = constants.EncoderOffset; + } + + @Override + @Trace + public void updateInputs(ModuleIOInputs inputs) { + driveController.setSupplyVoltage(RobotController.getBatteryVoltage()); + double motorVoltageDrive = driveController.getMotorVoltage(); + + driveMotorSim.setInputVoltage(motorVoltageDrive); + + driveMotorSim.update(GompeiLib.getLoopPeriod()); + + double rotorPositionRotationsDrive = + driveMotorSim.getAngularPositionRotations() * driveMotorSim.getGearing(); + double rotorVelocityRotationsPerSecondDrive = + driveMotorSim.getAngularVelocityRadPerSec() / (Math.PI * 2) * driveMotorSim.getGearing(); + driveController.setRawRotorPosition(rotorPositionRotationsDrive); + driveController.setRotorVelocity(rotorVelocityRotationsPerSecondDrive); + + steerController.setSupplyVoltage(RobotController.getBatteryVoltage()); + double motorVoltageSteer = steerController.getMotorVoltage(); + + steerMotorSim.setInputVoltage(motorVoltageSteer); + + steerMotorSim.update(GompeiLib.getLoopPeriod()); + + double rotorPositionRotationsSteer = + steerMotorSim.getAngularPositionRotations() * steerMotorSim.getGearing(); + double rotorVelocityRotationsPerSecondSteer = + steerMotorSim.getAngularVelocityRadPerSec() / (Math.PI * 2) * steerMotorSim.getGearing(); + steerController.setRawRotorPosition(rotorPositionRotationsSteer); + steerController.setRotorVelocity(rotorVelocityRotationsPerSecondSteer); + + encoderController.setRawPosition(steerMotorSim.getAngularPositionRotations() + offset); + + super.updateInputs(inputs); + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/elevator/Elevator.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/elevator/Elevator.java new file mode 100644 index 00000000..68db7016 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/elevator/Elevator.java @@ -0,0 +1,186 @@ +package edu.wpi.team190.gompeilib.subsystems.elevator; + +import static edu.wpi.first.units.Units.*; + +import edu.wpi.first.units.DistanceUnit; +import edu.wpi.first.units.VoltageUnit; +import edu.wpi.first.units.measure.Distance; +import edu.wpi.first.units.measure.Voltage; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.Commands; +import edu.wpi.first.wpilibj2.command.Subsystem; +import edu.wpi.first.wpilibj2.command.sysid.SysIdRoutine; +import edu.wpi.team190.gompeilib.core.logging.Trace; +import edu.wpi.team190.gompeilib.core.utility.Setpoint; +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.LinearConstraints; +import edu.wpi.team190.gompeilib.core.utility.phoenix.GainSlot; +import org.littletonrobotics.junction.Logger; + +public class Elevator { + public final ElevatorIO io; + public final ElevatorIOInputsAutoLogged inputs; + + private final String aKitTopic; + + private ElevatorState currentState; + + private Setpoint voltageGoal; + private Setpoint positionGoal; + + private final SysIdRoutine characterizationRoutine; + + public final ElevatorConstants constants; + + public Elevator( + ElevatorConstants constants, + Subsystem subsystem, + int index, + ElevatorIO io, + Setpoint positionGoal, + Setpoint voltageGoal) { + this.io = io; + this.inputs = new ElevatorIOInputsAutoLogged(); + + aKitTopic = subsystem.getName() + "/Elevators" + index; + + currentState = ElevatorState.IDLE; + + this.positionGoal = positionGoal; + this.voltageGoal = voltageGoal; + + characterizationRoutine = + new SysIdRoutine( + new SysIdRoutine.Config( + Volts.of(1).per(Second), + Volts.of(3), + Seconds.of(3), + (state) -> Logger.recordOutput(aKitTopic + "/SysIdState", state.toString())), + new SysIdRoutine.Mechanism(io::setVoltageGoal, null, subsystem)); + + this.constants = constants; + } + + public Elevator(ElevatorConstants constants, Subsystem subsystem, int index, ElevatorIO io) { + this( + constants, + subsystem, + index, + io, + new Setpoint<>( + Meters.of(0), + constants.heightOffsetStep, + constants.elevatorParameters.MIN_HEIGHT(), + constants.elevatorParameters.MAX_HEIGHT()), + new Setpoint<>(Volts.of(0), constants.voltageOffsetStep, Volts.of(-12), Volts.of(12))); + } + + public Elevator( + ElevatorConstants constants, + Subsystem subsystem, + int index, + ElevatorIO io, + Setpoint positionGoal) { + this( + constants, + subsystem, + index, + io, + positionGoal, + new Setpoint<>(Volts.of(0), constants.voltageOffsetStep, Volts.of(-12), Volts.of(12))); + } + + @Trace + public void periodic() { + io.updateInputs(inputs); + Logger.processInputs(aKitTopic, inputs); + + Logger.recordOutput(aKitTopic + "/State", currentState.name()); + Logger.recordOutput(aKitTopic + "/Voltage Goal", voltageGoal.getSetpoint()); + Logger.recordOutput(aKitTopic + "/Position Goal", positionGoal.getSetpoint()); + Logger.recordOutput(aKitTopic + "/Voltage Offset", voltageGoal.getOffset()); + Logger.recordOutput(aKitTopic + "/Position Offset", positionGoal.getOffset()); + Logger.recordOutput(aKitTopic + "/At Voltage Goal", atVoltageGoal()); + Logger.recordOutput(aKitTopic + "/At Position Goal", atPositionGoal()); + + switch (currentState) { + case OPEN_LOOP_VOLTAGE_CONTROL -> io.setVoltageGoal((Voltage) voltageGoal.getNewSetpoint()); + case CLOSED_LOOP_POSITION_CONTROL -> + io.setPositionGoal((Distance) positionGoal.getNewSetpoint()); + } + } + + public Distance getElevatorPosition() { + return inputs.position; + } + + public void setVoltageGoal(Voltage voltageGoal) { + currentState = ElevatorState.OPEN_LOOP_VOLTAGE_CONTROL; + this.voltageGoal.setSetpoint(voltageGoal); + } + + public void setPositionGoal(Distance positionGoal) { + currentState = ElevatorState.CLOSED_LOOP_POSITION_CONTROL; + this.positionGoal.setSetpoint(positionGoal); + } + + public void setVoltageGoal(Setpoint voltageGoal) { + currentState = ElevatorState.OPEN_LOOP_VOLTAGE_CONTROL; + this.voltageGoal = voltageGoal; + } + + public void setPositionGoal(Setpoint positionGoal) { + currentState = ElevatorState.CLOSED_LOOP_POSITION_CONTROL; + this.positionGoal = positionGoal; + } + + public boolean atVoltageGoal(Voltage voltageReference) { + return voltageGoal.getNewSetpoint().isNear(voltageReference, Millivolts.of(500)); + } + + public boolean atPositionGoal(Distance positionReference) { + return positionGoal + .getNewSetpoint() + .isNear(positionReference, constants.constraints.goalTolerance().get()); + } + + public boolean atVoltageGoal() { + return atVoltageGoal((Voltage) voltageGoal.getNewSetpoint()); + } + + public boolean atPositionGoal() { + return atPositionGoal((Distance) positionGoal.getNewSetpoint()); + } + + public void setPosition(Distance position) { + io.setPosition(position); + } + + public void setGainSlot(GainSlot gainSlot) { + io.setGainSlot(gainSlot); + } + + public Command waitUntilAtGoal() { + return Commands.waitUntil(this::atPositionGoal); + } + + public void updateGains(Gains gains, GainSlot slot) { + io.updateGains(gains, slot); + } + + public void updateConstraints(LinearConstraints constraints) { + io.updateConstraints(constraints); + } + + public Command runSysIdRoutine() { + return Commands.sequence( + Commands.runOnce(() -> currentState = ElevatorState.IDLE), + characterizationRoutine.quasistatic(SysIdRoutine.Direction.kForward), + Commands.waitSeconds(1.0), + characterizationRoutine.quasistatic(SysIdRoutine.Direction.kReverse), + Commands.waitSeconds(1.0), + characterizationRoutine.dynamic(SysIdRoutine.Direction.kForward), + Commands.waitSeconds(1.0), + characterizationRoutine.dynamic(SysIdRoutine.Direction.kReverse)); + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/elevator/ElevatorConstants.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/elevator/ElevatorConstants.java new file mode 100644 index 00000000..6b2bd590 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/elevator/ElevatorConstants.java @@ -0,0 +1,48 @@ +package edu.wpi.team190.gompeilib.subsystems.elevator; + +import com.ctre.phoenix6.CANBus; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.units.measure.Distance; +import edu.wpi.first.units.measure.Voltage; +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.LinearConstraints; +import java.util.Set; +import lombok.Builder; +import lombok.NonNull; +import lombok.Singular; + +@Builder(setterPrefix = "with") +public class ElevatorConstants { + @NonNull public final Integer leaderCANID; + @NonNull public final CANBus canBus = new CANBus(); + @NonNull public final Double elevatorGearRatio; + @NonNull public final Double drumRadius; + + @NonNull public final Double elevatorSupplyCurrentLimit; + @NonNull public final Double elevatorStatorCurrentLimit; + + @NonNull public final ElevatorParameters elevatorParameters; + @NonNull public final Gains slot0Gains; + @Builder.Default public final Gains slot1Gains = Gains.builder().build(); + @Builder.Default public final Gains slot2Gains = Gains.builder().build(); + @NonNull public final LinearConstraints constraints; + + @Singular(value = "alignedFollowerCANID") + @NonNull + public final Set alignedFollowerCANIDs; + + @Singular(value = "opposedFollowerCANID") + @NonNull + public final Set opposedFollowerCANIDs; + + @NonNull public final Voltage voltageOffsetStep; + @NonNull public final Distance heightOffsetStep; + + @Builder(setterPrefix = "with") + public record ElevatorParameters( + @NonNull DCMotor ELEVATOR_MOTOR_CONFIG, + @NonNull Double CARRIAGE_MASS_KG, + @NonNull Distance MIN_HEIGHT, + @NonNull Distance MAX_HEIGHT, + @NonNull Integer NUM_MOTORS) {} +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/elevator/ElevatorIO.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/elevator/ElevatorIO.java new file mode 100644 index 00000000..7078080b --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/elevator/ElevatorIO.java @@ -0,0 +1,97 @@ +package edu.wpi.team190.gompeilib.subsystems.elevator; + +import static edu.wpi.first.units.Units.*; + +import edu.wpi.first.units.measure.*; +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.LinearConstraints; +import edu.wpi.team190.gompeilib.core.utility.phoenix.GainSlot; +import org.littletonrobotics.junction.AutoLog; + +public interface ElevatorIO { + + @AutoLog + public static class ElevatorIOInputs { + public Distance position = Meters.of(0.0); + public LinearVelocity velocity = MetersPerSecond.of(0.0); + public LinearAcceleration acceleration = MetersPerSecondPerSecond.of(0.0); + + public double[] appliedVolts = new double[] {}; + public double[] supplyCurrentAmps = new double[] {}; + public double[] torqueCurrentAmps = new double[] {}; + public double[] temperatureCelsius = new double[] {}; + + public Distance positionGoalMeters = Meters.of(0.0); + public Distance positionSetpointMeters = Meters.of(0.0); + public Distance positionErrorMeters = Meters.of(0.0); + + public GainSlot gainSlot; + } + + /** + * Updates the inputs for the elevator. + * + * @param inputs The inputs to update. + */ + default void updateInputs(ElevatorIOInputs inputs) {} + + /** + * Sets the voltage for the elevator. + * + * @param voltageGoal The voltage of the elevator in volts. + */ + default void setVoltageGoal(Voltage voltageGoal) {} + + /** + * Sets the position goal of the elevator. + * + * @param positionGoal The position goal of the elevator in meters. + */ + default void setPositionGoal(Distance positionGoal) {} + + /** + * Checks if the voltage of the elevator matches the volts argument + * + * @param voltageReference The voltage to check against + * @return True if the voltage matches, false otherwise + */ + default boolean atVoltageGoal(Voltage voltageReference) { + return false; + } + + /** + * Checks if the position of the subsystem matches the positionGoal argument + * + * @param positionReference the position to check against + * @return True if the position matches, false otherwise + */ + default boolean atPositionGoal(Distance positionReference) { + return false; + } + + /** + * Sets the position of the elevator. + * + * @param position The position to set. + */ + default void setPosition(Distance position) {} + + /** + * @param gainSlot The CTRE gain slot to set the elevator to. + */ + default void setGainSlot(GainSlot gainSlot) {} + + /** + * Sets the gains for the elevator. + * + * @param gains the gains to update + */ + default void updateGains(Gains gains, GainSlot gainSlot) {} + + /** + * Sets the constraints for the elevator. + * + * @param constraints the constraints to update + */ + default void updateConstraints(LinearConstraints constraints) {} +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/elevator/ElevatorIOSim.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/elevator/ElevatorIOSim.java new file mode 100644 index 00000000..472dc64c --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/elevator/ElevatorIOSim.java @@ -0,0 +1,163 @@ +package edu.wpi.team190.gompeilib.subsystems.elevator; + +import static edu.wpi.first.units.Units.*; + +import edu.wpi.first.math.MathUtil; +import edu.wpi.first.math.controller.ElevatorFeedforward; +import edu.wpi.first.math.controller.ProfiledPIDController; +import edu.wpi.first.math.system.plant.LinearSystemId; +import edu.wpi.first.math.trajectory.TrapezoidProfile; +import edu.wpi.first.units.measure.*; +import edu.wpi.first.wpilibj.simulation.ElevatorSim; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.LinearConstraints; +import edu.wpi.team190.gompeilib.core.utility.phoenix.GainSlot; +import java.util.Arrays; + +public class ElevatorIOSim implements ElevatorIO { + private final ElevatorSim sim; + + private Voltage appliedVolts; + private boolean isClosedLoop; + private GainSlot gainSlot; + + private final ProfiledPIDController feedback; + private ElevatorFeedforward feedforward; + + private final ElevatorConstants constants; + + public ElevatorIOSim(ElevatorConstants constants) { + sim = + new ElevatorSim( + LinearSystemId.createElevatorSystem( + constants.elevatorParameters.ELEVATOR_MOTOR_CONFIG(), + constants.elevatorParameters.CARRIAGE_MASS_KG(), + constants.drumRadius, + constants.elevatorGearRatio), + constants.elevatorParameters.ELEVATOR_MOTOR_CONFIG(), + constants.elevatorParameters.MIN_HEIGHT().in(Meters), + constants.elevatorParameters.MAX_HEIGHT().in(Meters), + true, + constants.elevatorParameters.MIN_HEIGHT().in(Meters)); + + appliedVolts = Volts.of(0.0); + isClosedLoop = true; + gainSlot = GainSlot.ZERO; + + feedback = + new ProfiledPIDController( + constants.slot0Gains.kP().get(), + 0, + constants.slot0Gains.kD().get(), + new TrapezoidProfile.Constraints( + constants.constraints.maxVelocity().get().in(MetersPerSecond), + constants.constraints.maxAcceleration().get().in(MetersPerSecondPerSecond))); + + feedforward = + new ElevatorFeedforward( + constants.slot0Gains.kS().get(), + constants.slot0Gains.kG().get(), + constants.slot0Gains.kV().get(), + constants.slot0Gains.kA().get()); + + this.constants = constants; + } + + @Override + public void updateInputs(ElevatorIOInputs inputs) { + if (isClosedLoop) { + appliedVolts = + Volts.of( + feedback.calculate(sim.getPositionMeters()) + + feedforward.calculate((feedback.getSetpoint().velocity))); + } + + appliedVolts = Volts.of(MathUtil.clamp(appliedVolts.in(Volts), -12, 12)); + + sim.setInputVoltage(appliedVolts.in(Volts)); + sim.update(GompeiLib.getLoopPeriod()); + + inputs.position = Meters.of(sim.getPositionMeters()); + inputs.velocity = MetersPerSecond.of(sim.getVelocityMetersPerSecond()); + inputs.acceleration = + MetersPerSecondPerSecond.of(-1.0); // TODO: Replace with calculation based on velocity + + inputs.appliedVolts = new double[constants.elevatorParameters.NUM_MOTORS()]; + inputs.supplyCurrentAmps = new double[constants.elevatorParameters.NUM_MOTORS()]; + inputs.torqueCurrentAmps = new double[constants.elevatorParameters.NUM_MOTORS()]; + inputs.temperatureCelsius = new double[constants.elevatorParameters.NUM_MOTORS()]; + + Arrays.fill(inputs.appliedVolts, appliedVolts.in(Volts)); + Arrays.fill(inputs.supplyCurrentAmps, sim.getCurrentDrawAmps()); + Arrays.fill(inputs.torqueCurrentAmps, sim.getCurrentDrawAmps()); + Arrays.fill(inputs.temperatureCelsius, 0.0); + + inputs.positionGoalMeters = Meters.of(feedback.getGoal().position); + inputs.positionSetpointMeters = Meters.of(feedback.getSetpoint().position); + inputs.positionErrorMeters = Meters.of(feedback.getPositionError()); + + inputs.gainSlot = gainSlot; + } + + @Override + public void setVoltageGoal(Voltage volts) { + isClosedLoop = false; + appliedVolts = volts; + } + + @Override + public void setPositionGoal(Distance position) { + isClosedLoop = true; + feedback.setGoal(position.in(Meters)); + } + + @Override + public boolean atVoltageGoal(Voltage voltageReference) { + return appliedVolts.isNear(voltageReference, Millivolt.of(500)); + } + + @Override + public boolean atPositionGoal(Distance positionReference) { + return Meters.of(sim.getPositionMeters()) + .isNear(positionReference, constants.constraints.goalTolerance().get()); + } + + @Override + public void setPosition(Distance position) { + sim.setState(position.in(Meters), sim.getVelocityMetersPerSecond()); + } + + @Override + public void setGainSlot(GainSlot gainSlot) { + this.gainSlot = gainSlot; + switch (gainSlot) { + case ZERO: + feedback.setPID(constants.slot0Gains.kP().get(), 0.0, constants.slot0Gains.kD().get()); + break; + case ONE: + feedback.setPID(constants.slot1Gains.kP().get(), 0.0, constants.slot1Gains.kD().get()); + break; + case TWO: + feedback.setPID(constants.slot2Gains.kP().get(), 0.0, constants.slot2Gains.kD().get()); + break; + } + } + + @Override + public void updateGains(Gains gains, GainSlot gainSlot) { + feedback.setPID(gains.kP().get(), gains.kI().get(), gains.kD().get()); + feedforward = + new ElevatorFeedforward( + gains.kS().get(), gains.kG().get(), gains.kV().get(), gains.kA().get()); + } + + @Override + public void updateConstraints(LinearConstraints constraints) { + feedback.setConstraints( + new TrapezoidProfile.Constraints( + constraints.maxVelocity().get().in(MetersPerSecond), + constraints.maxAcceleration().get().in(MetersPerSecondPerSecond))); + feedback.setTolerance(constraints.goalTolerance().get().in(Meters)); + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/elevator/ElevatorIOTalonFX.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/elevator/ElevatorIOTalonFX.java new file mode 100644 index 00000000..47068d82 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/elevator/ElevatorIOTalonFX.java @@ -0,0 +1,299 @@ +package edu.wpi.team190.gompeilib.subsystems.elevator; + +import static edu.wpi.first.units.Units.*; + +import com.ctre.phoenix6.BaseStatusSignal; +import com.ctre.phoenix6.StatusSignal; +import com.ctre.phoenix6.configs.TalonFXConfiguration; +import com.ctre.phoenix6.controls.Follower; +import com.ctre.phoenix6.controls.MotionMagicVoltage; +import com.ctre.phoenix6.controls.VoltageOut; +import com.ctre.phoenix6.hardware.TalonFX; +import com.ctre.phoenix6.signals.GravityTypeValue; +import com.ctre.phoenix6.signals.MotorAlignmentValue; +import edu.wpi.first.units.measure.*; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.LinearConstraints; +import edu.wpi.team190.gompeilib.core.utility.phoenix.GainSlot; +import edu.wpi.team190.gompeilib.core.utility.phoenix.PhoenixUtil; +import java.util.ArrayList; + +public class ElevatorIOTalonFX implements ElevatorIO { + + // Core hardware components + protected final TalonFX talonFX; + public final TalonFX[] followTalonFX; + + // Configuration + public final TalonFXConfiguration config; + protected final ElevatorConstants constants; + + // Sensor inputs + private StatusSignal positionRotations; + private StatusSignal velocityRotationsPerSecond; + private StatusSignal accelerationRotationsPerSecondPerSecond; + private ArrayList> appliedVolts; + private ArrayList> supplyCurrentAmps; + private ArrayList> torqueCurrentAmps; + private ArrayList> temperatureCelsius; + + public Distance positionGoalMeters; + + private StatusSignal positionSetpointRotations; + private StatusSignal positionErrorRotations; + + private StatusSignal[] statusSignals; + + private MotionMagicVoltage positionVoltageRequest; + private VoltageOut voltageRequest; + + public ElevatorIOTalonFX(ElevatorConstants constants) { + + this.constants = constants; + + // Create lead motor + talonFX = new TalonFX(constants.leaderCANID, constants.canBus); + + // Create follower motor array (define length) + followTalonFX = new TalonFX[constants.elevatorParameters.NUM_MOTORS() - 1]; + + config = new TalonFXConfiguration(); + config.Slot0.withKP(constants.slot0Gains.kP().get()) + .withKD(constants.slot0Gains.kD().get()) + .withKS(constants.slot0Gains.kS().get()) + .withKV(constants.slot0Gains.kV().get()) + .withKA(constants.slot0Gains.kA().get()) + .withKG(constants.slot0Gains.kG().get()) + .withGravityType(GravityTypeValue.Elevator_Static); + + config.Slot1.withKP(constants.slot1Gains.kP().get()) + .withKD(constants.slot1Gains.kD().get()) + .withKS(constants.slot1Gains.kS().get()) + .withKV(constants.slot1Gains.kV().get()) + .withKA(constants.slot1Gains.kA().get()) + .withKG(constants.slot1Gains.kG().get()) + .withGravityType(GravityTypeValue.Elevator_Static); + + config.Slot2.withKP(constants.slot2Gains.kP().get()) + .withKD(constants.slot2Gains.kD().get()) + .withKS(constants.slot2Gains.kS().get()) + .withKV(constants.slot2Gains.kV().get()) + .withKA(constants.slot2Gains.kA().get()) + .withKG(constants.slot2Gains.kG().get()) + .withGravityType(GravityTypeValue.Elevator_Static); + + config.CurrentLimits.withSupplyCurrentLimit(constants.elevatorSupplyCurrentLimit) + .withSupplyCurrentLimitEnable(true) + .withStatorCurrentLimit(constants.elevatorStatorCurrentLimit) + .withStatorCurrentLimitEnable(true); + + config.Feedback.SensorToMechanismRatio = + constants.elevatorGearRatio / (2 * Math.PI * constants.drumRadius); + + config.SoftwareLimitSwitch.withForwardSoftLimitThreshold( + constants.elevatorParameters.MAX_HEIGHT().in(Meters)) + .withForwardSoftLimitEnable(true) + .withReverseSoftLimitThreshold(constants.elevatorParameters.MIN_HEIGHT().in(Meters)) + .withReverseSoftLimitEnable(true); + + config.MotionMagic.withMotionMagicAcceleration( + constants.constraints.maxAcceleration().get().in(MetersPerSecondPerSecond)) + .withMotionMagicCruiseVelocity( + constants.constraints.maxVelocity().get().in(MetersPerSecond)); + + PhoenixUtil.tryUntilOk(5, () -> talonFX.getConfigurator().apply(config)); + + final int[] indexHolder = {0}; // mutable index for array insertion + + constants.alignedFollowerCANIDs.forEach( + id -> { + TalonFX follower = new TalonFX(id, talonFX.getNetwork()); + followTalonFX[indexHolder[0]++] = follower; + + PhoenixUtil.tryUntilOk(5, () -> follower.getConfigurator().apply(config, 0.25)); + + follower.setControl(new Follower(talonFX.getDeviceID(), MotorAlignmentValue.Aligned)); + }); + + constants.opposedFollowerCANIDs.forEach( + id -> { + TalonFX follower = new TalonFX(id, talonFX.getNetwork()); + followTalonFX[indexHolder[0]++] = follower; + + PhoenixUtil.tryUntilOk(5, () -> follower.getConfigurator().apply(config, 0.25)); + + follower.setControl(new Follower(talonFX.getDeviceID(), MotorAlignmentValue.Opposed)); + }); + + appliedVolts = new ArrayList<>(); + supplyCurrentAmps = new ArrayList<>(); + torqueCurrentAmps = new ArrayList<>(); + temperatureCelsius = new ArrayList<>(); + + positionRotations = talonFX.getPosition(); + velocityRotationsPerSecond = talonFX.getVelocity(); + accelerationRotationsPerSecondPerSecond = talonFX.getAcceleration(); + appliedVolts.add(talonFX.getMotorVoltage()); + supplyCurrentAmps.add(talonFX.getSupplyCurrent()); + torqueCurrentAmps.add(talonFX.getTorqueCurrent()); + temperatureCelsius.add(talonFX.getDeviceTemp()); + positionGoalMeters = Meters.of(0.0); + positionSetpointRotations = talonFX.getClosedLoopReference(); + positionErrorRotations = talonFX.getClosedLoopError(); + + for (TalonFX follower : followTalonFX) { + appliedVolts.add(follower.getMotorVoltage()); + supplyCurrentAmps.add(follower.getSupplyCurrent()); + torqueCurrentAmps.add(follower.getTorqueCurrent()); + temperatureCelsius.add(follower.getDeviceTemp()); + } + + var signalsList = new ArrayList>(); + + signalsList.add(positionRotations); + signalsList.add(velocityRotationsPerSecond); + signalsList.add(accelerationRotationsPerSecondPerSecond); + signalsList.add(positionSetpointRotations); + signalsList.add(positionErrorRotations); + signalsList.addAll(appliedVolts); + signalsList.addAll(supplyCurrentAmps); + signalsList.addAll(torqueCurrentAmps); + signalsList.addAll(temperatureCelsius); + + statusSignals = new StatusSignal[signalsList.size()]; + + for (int i = 0; i < signalsList.size(); i++) { + statusSignals[i] = signalsList.get(i); + } + + BaseStatusSignal.setUpdateFrequencyForAll(1 / GompeiLib.getLoopPeriod(), statusSignals); + + talonFX.optimizeBusUtilization(); + for (TalonFX follower : followTalonFX) { + follower.optimizeBusUtilization(); + } + + positionVoltageRequest = new MotionMagicVoltage(0.0); + voltageRequest = new VoltageOut(0.0); + + PhoenixUtil.registerSignals(constants.canBus.isNetworkFD(), statusSignals); + } + + @Override + public void updateInputs(ElevatorIOInputs inputs) { + + // CTRE status signals are natively in rotations, but setting sensor to mechanism ratio + // including the circumference of the drum allows us to transform into meters directly from + // status signal object + inputs.position = Meters.of(positionRotations.getValueAsDouble()); + inputs.velocity = MetersPerSecond.of(velocityRotationsPerSecond.getValueAsDouble()); + inputs.acceleration = + MetersPerSecondPerSecond.of(accelerationRotationsPerSecondPerSecond.getValueAsDouble()); + + inputs.appliedVolts = new double[appliedVolts.size()]; + inputs.supplyCurrentAmps = new double[supplyCurrentAmps.size()]; + inputs.torqueCurrentAmps = new double[torqueCurrentAmps.size()]; + inputs.temperatureCelsius = new double[temperatureCelsius.size()]; + + for (int i = 0; i <= followTalonFX.length; i++) { + inputs.appliedVolts[i] = appliedVolts.get(i).getValueAsDouble(); + inputs.supplyCurrentAmps[i] = supplyCurrentAmps.get(i).getValueAsDouble(); + inputs.torqueCurrentAmps[i] = torqueCurrentAmps.get(i).getValueAsDouble(); + inputs.temperatureCelsius[i] = temperatureCelsius.get(i).getValueAsDouble(); + } + inputs.positionGoalMeters = positionGoalMeters; + inputs.positionSetpointMeters = Meters.of(positionSetpointRotations.getValueAsDouble()); + inputs.positionErrorMeters = Meters.of(positionErrorRotations.getValueAsDouble()); + + inputs.gainSlot = GainSlot.integerToGainSlot(talonFX.getClosedLoopSlot().getValue()); + } + + @Override + public void setVoltageGoal(Voltage voltageGoal) { + talonFX.setControl(voltageRequest.withOutput(voltageGoal).withEnableFOC(true)); + } + + @Override + public void setPositionGoal(Distance positionGoal) { + positionGoalMeters = positionGoal; + talonFX.setControl(positionVoltageRequest.withPosition(positionGoal.in(Meters))); + } + + @Override + public boolean atVoltageGoal(Voltage voltageReference) { + return appliedVolts.get(0).getValue().isNear(voltageReference, Millivolts.of(500)); + } + + @Override + public boolean atPositionGoal(Distance positionReference) { + return Math.abs(positionRotations.getValueAsDouble() - positionReference.in(Meters)) + <= constants.constraints.goalTolerance().get(Meters); + } + + @Override + public void setPosition(Distance position) { + talonFX.setPosition(position.in(Meters)); + } + + @Override + public void setGainSlot(GainSlot slot) { + switch (slot) { + case ONE: + talonFX.setControl(positionVoltageRequest.withSlot(1)); + break; + case TWO: + talonFX.setControl(positionVoltageRequest.withSlot(2)); + break; + default: + talonFX.setControl(positionVoltageRequest.withSlot(0)); + break; + } + } + + @Override + public void updateGains(Gains gains, GainSlot gainSlot) { + switch (gainSlot) { + case ZERO: + config.Slot0.withKP(gains.kP().get()) + .withKD(gains.kD().get()) + .withKS(gains.kS().get()) + .withKV(gains.kV().get()) + .withKA(gains.kA().get()) + .withKG(gains.kG().get()); + break; + case ONE: + config.Slot1.withKP(gains.kP().get()) + .withKD(gains.kD().get()) + .withKS(gains.kS().get()) + .withKV(gains.kV().get()) + .withKA(gains.kA().get()) + .withKG(gains.kG().get()); + break; + case TWO: + default: + config.Slot2.withKP(gains.kP().get()) + .withKD(gains.kD().get()) + .withKS(gains.kS().get()) + .withKV(gains.kV().get()) + .withKA(gains.kA().get()) + .withKG(gains.kG().get()); + break; + } + PhoenixUtil.tryUntilOk(5, () -> talonFX.getConfigurator().apply(config, 0.25)); + for (TalonFX follower : followTalonFX) { + PhoenixUtil.tryUntilOk(5, () -> follower.getConfigurator().apply(config, 0.25)); + } + } + + @Override + public void updateConstraints(LinearConstraints constraints) { + config.MotionMagic.withMotionMagicAcceleration( + constraints.maxAcceleration().get().in(MetersPerSecondPerSecond)) + .withMotionMagicCruiseVelocity(constraints.maxVelocity().get().in(MetersPerSecond)); + PhoenixUtil.tryUntilOk(5, () -> talonFX.getConfigurator().apply(config, 0.25)); + for (TalonFX follower : followTalonFX) { + PhoenixUtil.tryUntilOk(5, () -> follower.getConfigurator().apply(config, 0.25)); + } + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/elevator/ElevatorIOTalonFXSim.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/elevator/ElevatorIOTalonFXSim.java new file mode 100644 index 00000000..0b52d670 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/elevator/ElevatorIOTalonFXSim.java @@ -0,0 +1,63 @@ +package edu.wpi.team190.gompeilib.subsystems.elevator; + +import static edu.wpi.first.units.Units.*; +import static edu.wpi.first.units.Units.Meters; + +import com.ctre.phoenix6.sim.TalonFXSimState; +import edu.wpi.first.math.system.plant.LinearSystemId; +import edu.wpi.first.units.measure.Angle; +import edu.wpi.first.units.measure.AngularVelocity; +import edu.wpi.first.wpilibj.RobotController; +import edu.wpi.first.wpilibj.simulation.ElevatorSim; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import edu.wpi.team190.gompeilib.core.logging.Trace; + +public class ElevatorIOTalonFXSim extends ElevatorIOTalonFX { + private final ElevatorSim elevatorSim; + + private final TalonFXSimState elevatorController; + + public ElevatorIOTalonFXSim(ElevatorConstants constants) { + super(constants); + elevatorSim = + new ElevatorSim( + LinearSystemId.createElevatorSystem( + constants.elevatorParameters.ELEVATOR_MOTOR_CONFIG(), + constants.elevatorParameters.CARRIAGE_MASS_KG(), + constants.drumRadius, + constants.elevatorGearRatio), + constants.elevatorParameters.ELEVATOR_MOTOR_CONFIG(), + constants.elevatorParameters.MIN_HEIGHT().in(Meters), + constants.elevatorParameters.MAX_HEIGHT().in(Meters), + true, + constants.elevatorParameters.MIN_HEIGHT().in(Meters)); + + elevatorController = super.talonFX.getSimState(); + } + + @Override + @Trace + public void updateInputs(ElevatorIOInputs inputs) { + elevatorController.setSupplyVoltage(RobotController.getBatteryVoltage()); + double elevatorVoltage = elevatorController.getMotorVoltage(); + + elevatorSim.setInputVoltage(elevatorVoltage); + + elevatorSim.update(GompeiLib.getLoopPeriod()); + + Angle rotorPosition = + Angle.ofBaseUnits( + elevatorSim.getPositionMeters() * constants.elevatorGearRatio * constants.drumRadius, + Radians); + AngularVelocity rotorVelocity = + AngularVelocity.ofBaseUnits( + elevatorSim.getVelocityMetersPerSecond() + * constants.elevatorGearRatio + * constants.drumRadius, + RadiansPerSecond); + elevatorController.setRawRotorPosition(rotorPosition); + elevatorController.setRotorVelocity(rotorVelocity); + + super.updateInputs(inputs); + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/elevator/ElevatorState.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/elevator/ElevatorState.java new file mode 100644 index 00000000..b09c07e2 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/elevator/ElevatorState.java @@ -0,0 +1,7 @@ +package edu.wpi.team190.gompeilib.subsystems.elevator; + +public enum ElevatorState { + IDLE, + OPEN_LOOP_VOLTAGE_CONTROL, + CLOSED_LOOP_POSITION_CONTROL; +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/generic/flywheel/GenericFlywheel.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/generic/flywheel/GenericFlywheel.java new file mode 100644 index 00000000..591dd735 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/generic/flywheel/GenericFlywheel.java @@ -0,0 +1,265 @@ +package edu.wpi.team190.gompeilib.subsystems.generic.flywheel; + +import static edu.wpi.first.units.Units.*; + +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.units.*; +import edu.wpi.first.units.measure.AngularVelocity; +import edu.wpi.first.units.measure.Current; +import edu.wpi.first.units.measure.Voltage; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.Commands; +import edu.wpi.first.wpilibj2.command.Subsystem; +import edu.wpi.team190.gompeilib.core.utility.Setpoint; +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.AngularVelocityConstraints; +import edu.wpi.team190.gompeilib.core.utility.phoenix.GainSlot; +import edu.wpi.team190.gompeilib.core.utility.sysid.CustomSysIdRoutine; +import edu.wpi.team190.gompeilib.core.utility.sysid.CustomUnits; +import java.util.function.Supplier; +import lombok.Getter; +import org.littletonrobotics.junction.Logger; + +public class GenericFlywheel { + private final GenericFlywheelIO io; + private final GenericFlywheelIOInputsAutoLogged inputs; + + private final String aKitTopic; + + @Getter private GenericFlywheelState currentState; + + @Getter private Setpoint velocityGoal; + @Getter private Setpoint voltageGoal; + @Getter private Current currentGoal; + + private final CustomSysIdRoutine voltageCharacterizationRoutine; + private final CustomSysIdRoutine torqueCharacterizationRoutine; + + public GenericFlywheel( + GenericFlywheelIO io, + Subsystem subsystem, + GenericFlywheelConstants constants, + String name, + Setpoint velocityGoal, + Setpoint voltageGoal) { + this.io = io; + inputs = new GenericFlywheelIOInputsAutoLogged(); + + aKitTopic = subsystem.getName() + "/" + "Flywheel" + name; + + currentState = GenericFlywheelState.IDLE; + + this.velocityGoal = velocityGoal; + this.voltageGoal = voltageGoal; + currentGoal = Amps.of(0.0); + + voltageCharacterizationRoutine = + new CustomSysIdRoutine<>( + new CustomSysIdRoutine.Config( + CustomUnits.voltsPerSecond.ofNative(0.5), + Volts.of(8), + Seconds.of(24), + (state) -> + Logger.recordOutput(aKitTopic + "/Voltage SysID State", state.toString()), + Volts), + new CustomSysIdRoutine.Mechanism<>( + (volts) -> io.setVoltageGoal(Volts.of(volts.in(Volts))), subsystem), + Volts.mutable(0)); + + torqueCharacterizationRoutine = + new CustomSysIdRoutine<>( + new CustomSysIdRoutine.Config( + CustomUnits.ampsPerSecond.ofNative(0.5), + Amps.of(3.5), + Seconds.of(10), + (state) -> + Logger.recordOutput( + aKitTopic + "/Torque Current SysID State", state.toString()), + Amp), + new CustomSysIdRoutine.Mechanism<>( + (amps) -> io.setCurrentGoal(Amps.of(amps.in(Amps))), subsystem), + Amp.mutable(0)); + } + + public GenericFlywheel( + GenericFlywheelIO io, Subsystem subsystem, GenericFlywheelConstants constants, String name) { + this( + io, + subsystem, + constants, + name, + new Setpoint<>( + RadiansPerSecond.of(0), + constants.velocityOffsetStep, + RadiansPerSecond.of(-constants.gearRatio * constants.motorConfig.freeSpeedRadPerSec), + RadiansPerSecond.of(constants.gearRatio * constants.motorConfig.freeSpeedRadPerSec)), + new Setpoint<>(Volts.of(0), constants.voltageOffsetStep, Volts.of(-12), Volts.of(12))); + } + + public GenericFlywheel( + GenericFlywheelIO io, + Subsystem subsystem, + GenericFlywheelConstants constants, + String name, + Setpoint velocityGoal) { + this( + io, + subsystem, + constants, + name, + velocityGoal, + new Setpoint<>(Volts.of(0), constants.voltageOffsetStep, Volts.of(-12), Volts.of(12))); + } + + public void periodic() { + io.updateInputs(inputs); + Logger.processInputs(aKitTopic, inputs); + + Logger.recordOutput(aKitTopic + "/State", currentState.name()); + Logger.recordOutput(aKitTopic + "/Velocity Goal", velocityGoal.getSetpoint()); + Logger.recordOutput(aKitTopic + "/Voltage Goal", voltageGoal.getSetpoint()); + Logger.recordOutput(aKitTopic + "/Voltage Offset", voltageGoal.getOffset()); + Logger.recordOutput(aKitTopic + "/Velocity Offset", velocityGoal.getOffset()); + Logger.recordOutput(aKitTopic + "/Current Goal", currentGoal); + Logger.recordOutput(aKitTopic + "/At Velocity Goal", atVelocityGoal()); + Logger.recordOutput(aKitTopic + "/At Voltage Goal", atVoltageGoal()); + Logger.recordOutput(aKitTopic + "/At Current Goal", atCurrentGoal()); + + switch (currentState) { + case VELOCITY_VOLTAGE_CONTROL: + io.setVelocityGoal((AngularVelocity) velocityGoal.getNewSetpoint()); + break; + case VELOCITY_TORQUE_CONTROL: + io.setVelocityGoal((AngularVelocity) velocityGoal.getNewSetpoint(), currentGoal); + break; + case VOLTAGE_CONTROL: + io.setVoltageGoal((Voltage) voltageGoal.getNewSetpoint()); + break; + case STOP: + io.setNeutralControl(); + break; + case IDLE: + break; + } + } + + public AngularVelocity getFlywheelVelocity() { + return inputs.velocity; + } + + public Rotation2d getFlywheelPosition() { + return inputs.position; + } + + public void setVoltageGoal(Voltage voltageGoal) { + currentState = GenericFlywheelState.VOLTAGE_CONTROL; + this.voltageGoal.setSetpoint(voltageGoal); + } + + public void setVoltageGoal(Setpoint voltageGoal) { + currentState = GenericFlywheelState.VOLTAGE_CONTROL; + this.voltageGoal = voltageGoal; + } + + public void setVelocityGoal(AngularVelocity velocityGoal) { + currentState = GenericFlywheelState.VELOCITY_VOLTAGE_CONTROL; + this.velocityGoal.setSetpoint(velocityGoal); + } + + public void setVelocityGoal(Setpoint velocityGoal) { + currentState = GenericFlywheelState.VELOCITY_VOLTAGE_CONTROL; + this.velocityGoal = velocityGoal; + } + + public void setVelocityGoal(Supplier velocityGoal) { + currentState = GenericFlywheelState.VELOCITY_VOLTAGE_CONTROL; + this.velocityGoal.setSetpoint(velocityGoal.get()); + } + + public void setVelocityGoal(AngularVelocity velocityGoal, Current currentGoal) { + currentState = GenericFlywheelState.VELOCITY_TORQUE_CONTROL; + this.velocityGoal.setSetpoint(velocityGoal); + this.currentGoal = currentGoal; + } + + public void setVelocityGoal(Setpoint velocityGoal, Current currentGoal) { + currentState = GenericFlywheelState.VELOCITY_TORQUE_CONTROL; + this.velocityGoal = velocityGoal; + this.currentGoal = currentGoal; + } + + public void setVelocityGoal( + Supplier velocityGoal, Supplier currentGoal) { + currentState = GenericFlywheelState.VELOCITY_TORQUE_CONTROL; + this.velocityGoal.setSetpoint(velocityGoal.get()); + this.currentGoal = currentGoal.get(); + } + + public void stop() { + currentState = GenericFlywheelState.STOP; + } + + public boolean atVoltageGoal(Voltage voltageReference) { + return io.atVoltageGoal(voltageReference); + } + + public boolean atVelocityGoal(AngularVelocity velocityReference) { + return io.atVelocityGoal(velocityReference); + } + + public boolean atCurrentGoal(Current currentReference) { + return io.atCurrentGoal(currentReference); + } + + public boolean atVoltageGoal() { + return atVoltageGoal((Voltage) voltageGoal.getNewSetpoint()); + } + + public boolean atVelocityGoal() { + return atVelocityGoal((AngularVelocity) velocityGoal.getNewSetpoint()); + } + + public boolean atCurrentGoal() { + return atCurrentGoal(currentGoal); + } + + public Command waitUntilAtGoal() { + return Commands.waitUntil(this::atVelocityGoal); + } + + public void updateGains(Gains gains, GainSlot gainSlot) { + io.updateGains(gains, gainSlot); + } + + public void updateConstraints(AngularVelocityConstraints constraints) { + io.updateConstraints(constraints); + } + + public Command sysIdRoutineVoltage() { + return Commands.sequence( + Commands.runOnce(() -> currentState = GenericFlywheelState.IDLE), + voltageCharacterizationRoutine + .dynamic(CustomSysIdRoutine.Direction.kForward) + .withTimeout(10), + Commands.waitSeconds(6.0), + voltageCharacterizationRoutine + .dynamic(CustomSysIdRoutine.Direction.kReverse) + .withTimeout(10), + Commands.waitSeconds(6.0), + voltageCharacterizationRoutine.quasistatic(CustomSysIdRoutine.Direction.kForward), + Commands.waitSeconds(6.0), + voltageCharacterizationRoutine.quasistatic(CustomSysIdRoutine.Direction.kReverse)); + } + + public Command sysIdRoutineTorque() { + return Commands.sequence( + Commands.runOnce(() -> currentState = GenericFlywheelState.IDLE), + torqueCharacterizationRoutine.dynamic(CustomSysIdRoutine.Direction.kForward), + Commands.waitSeconds(5.0), + torqueCharacterizationRoutine.dynamic(CustomSysIdRoutine.Direction.kReverse), + Commands.waitSeconds(5.0), + torqueCharacterizationRoutine.quasistatic(CustomSysIdRoutine.Direction.kForward), + Commands.waitSeconds(5.0), + torqueCharacterizationRoutine.quasistatic(CustomSysIdRoutine.Direction.kReverse)); + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/generic/flywheel/GenericFlywheelConstants.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/generic/flywheel/GenericFlywheelConstants.java new file mode 100644 index 00000000..79623905 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/generic/flywheel/GenericFlywheelConstants.java @@ -0,0 +1,46 @@ +package edu.wpi.team190.gompeilib.subsystems.generic.flywheel; + +import com.ctre.phoenix6.CANBus; +import com.ctre.phoenix6.signals.InvertedValue; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.units.measure.AngularVelocity; +import edu.wpi.first.units.measure.Voltage; +import edu.wpi.team190.gompeilib.core.utility.control.CurrentLimits; +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.AngularVelocityConstraints; +import java.util.Set; +import lombok.Builder; +import lombok.NonNull; +import lombok.Singular; + +@Builder(setterPrefix = "with") +public class GenericFlywheelConstants { + + @NonNull public final Integer leaderCANID; + @NonNull public final InvertedValue leaderInversion; + + @NonNull public final CANBus canBus; + @NonNull public final Boolean enableFOC; + + @NonNull public final CurrentLimits currentLimit; + @NonNull public final Double momentOfInertia; + @NonNull public final Double gearRatio; + + @NonNull public final DCMotor motorConfig; + + @NonNull public final Gains voltageGains; + @NonNull public final Gains torqueGains; + @NonNull public final AngularVelocityConstraints constraints; + + @Singular(value = "alignedFollowerCANID") + @NonNull + public final Set alignedFollowerCANIDs; + + @Singular(value = "opposedFollowerCANID") + @NonNull + public final Set opposedFollowerCANIDs; + + @NonNull public final AngularVelocity velocityOffsetStep; + + @NonNull public final Voltage voltageOffsetStep; +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/generic/flywheel/GenericFlywheelIO.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/generic/flywheel/GenericFlywheelIO.java new file mode 100644 index 00000000..c59924ad --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/generic/flywheel/GenericFlywheelIO.java @@ -0,0 +1,58 @@ +package edu.wpi.team190.gompeilib.subsystems.generic.flywheel; + +import static edu.wpi.first.units.Units.RadiansPerSecond; + +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.units.measure.*; +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.AngularVelocityConstraints; +import edu.wpi.team190.gompeilib.core.utility.phoenix.GainSlot; +import org.littletonrobotics.junction.AutoLog; + +public interface GenericFlywheelIO { + + @AutoLog + public static class GenericFlywheelIOInputs { + public Rotation2d position = new Rotation2d(); + public AngularVelocity velocity = RadiansPerSecond.of(0.0); + + public double[] appliedVolts = new double[] {}; + public double[] supplyCurrentAmps = new double[] {}; + public double[] torqueCurrentAmps = new double[] {}; + public double[] temperatureCelsius = new double[] {}; + + public AngularVelocity velocityGoal = RadiansPerSecond.of(0.0); + public AngularVelocity velocitySetpoint = RadiansPerSecond.of(0.0); + public AngularVelocity velocityError = RadiansPerSecond.of(0.0); + + public GainSlot gainSlot; + } + + default void updateInputs(GenericFlywheelIOInputs inputs) {} + + default void setVoltageGoal(Voltage voltageGoal) {} + + default void setCurrentGoal(Current currentGoal) {} + + default void setVelocityGoal(AngularVelocity velocityGoal) {} + + default void setVelocityGoal(AngularVelocity velocityGoal, Current currentFeedforward) {} + + default void setNeutralControl() {} + + default boolean atVoltageGoal(Voltage voltageReference) { + return false; + } + + default boolean atCurrentGoal(Current currentReference) { + return false; + } + + default boolean atVelocityGoal(AngularVelocity velocityReference) { + return false; + } + + default void updateGains(Gains gains, GainSlot gainSlot) {} + + default void updateConstraints(AngularVelocityConstraints constraints) {} +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/generic/flywheel/GenericFlywheelIOSim.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/generic/flywheel/GenericFlywheelIOSim.java new file mode 100644 index 00000000..63520c9d --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/generic/flywheel/GenericFlywheelIOSim.java @@ -0,0 +1,177 @@ +package edu.wpi.team190.gompeilib.subsystems.generic.flywheel; + +import static edu.wpi.first.units.Units.*; + +import edu.wpi.first.math.MathUtil; +import edu.wpi.first.math.controller.PIDController; +import edu.wpi.first.math.controller.SimpleMotorFeedforward; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.system.plant.LinearSystemId; +import edu.wpi.first.units.measure.*; +import edu.wpi.first.wpilibj.simulation.FlywheelSim; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.control.LinearProfile; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.AngularVelocityConstraints; +import edu.wpi.team190.gompeilib.core.utility.phoenix.GainSlot; +import java.util.Arrays; + +public class GenericFlywheelIOSim implements GenericFlywheelIO { + + private final FlywheelSim motorSim; + + private Voltage appliedVolts; + private boolean isClosedLoop; + private GainSlot gainSlot; + + private final PIDController feedback; + private final SimpleMotorFeedforward feedforward; + private final LinearProfile profile; + + GenericFlywheelConstants constants; + + private Angle accumulatedPosition; + + public GenericFlywheelIOSim(GenericFlywheelConstants constants) { + motorSim = + new FlywheelSim( + LinearSystemId.createFlywheelSystem( + constants.motorConfig, constants.momentOfInertia, constants.gearRatio), + constants.motorConfig); + + appliedVolts = Volts.of(0.0); + isClosedLoop = false; + + feedback = + new PIDController( + constants.voltageGains.kP().get(), 0.0, constants.voltageGains.kD().get()); + feedback.setTolerance(constants.constraints.goalTolerance().get().in(RadiansPerSecond)); + feedforward = + new SimpleMotorFeedforward( + constants.voltageGains.kS().get(), constants.voltageGains.kV().get()); + profile = + new LinearProfile( + constants.constraints.maxAcceleration().get().in(RadiansPerSecondPerSecond), + constants.constraints.maxVelocity().get().in(RadiansPerSecond), + GompeiLib.getLoopPeriod()); + + this.constants = constants; + + accumulatedPosition = Radians.of(0.0); + } + + @Override + public void updateInputs(GenericFlywheelIOInputs inputs) { + if (isClosedLoop) { + double meas = motorSim.getAngularVelocityRadPerSec(); + double nextSetpoint = profile.calculateSetpoint(); + double pidOut = feedback.calculate(meas, nextSetpoint); + double ffOut = feedforward.calculate(nextSetpoint); + System.out.println( + "DEBUG FLYWHEEL SIM: meas=" + + meas + + " setp=" + + nextSetpoint + + " pid=" + + pidOut + + " ff=" + + ffOut); + appliedVolts = Volts.of(pidOut + ffOut); + } + + appliedVolts = Volts.of(MathUtil.clamp(appliedVolts.in(Volts), -12.0, 12.0)); + motorSim.setInputVoltage(appliedVolts.in(Volts)); + motorSim.update(GompeiLib.getLoopPeriod()); + + accumulatedPosition = + accumulatedPosition.plus( + motorSim.getAngularVelocity().times(Seconds.of(GompeiLib.getLoopPeriod()))); + + inputs.position = Rotation2d.fromRadians(accumulatedPosition.in(Radians)); + inputs.velocity = motorSim.getAngularVelocity(); + + int numMotors = + 1 + constants.alignedFollowerCANIDs.size() + constants.opposedFollowerCANIDs.size(); + inputs.appliedVolts = new double[numMotors]; + inputs.supplyCurrentAmps = new double[numMotors]; + inputs.torqueCurrentAmps = new double[numMotors]; + inputs.temperatureCelsius = new double[numMotors]; + + Arrays.fill(inputs.appliedVolts, appliedVolts.in(Volts)); + Arrays.fill(inputs.supplyCurrentAmps, motorSim.getCurrentDrawAmps()); + Arrays.fill(inputs.torqueCurrentAmps, motorSim.getCurrentDrawAmps()); + + inputs.velocityGoal = RadiansPerSecond.of(profile.getGoal()); + inputs.velocitySetpoint = RadiansPerSecond.of(feedback.getSetpoint()); + inputs.velocityError = RadiansPerSecond.of(feedback.getError()); + + inputs.gainSlot = gainSlot; + } + + @Override + public void setVoltageGoal(Voltage voltageGoal) { + isClosedLoop = false; + appliedVolts = voltageGoal; + } + + @Override + public void setVelocityGoal(AngularVelocity velocityGoal) { + isClosedLoop = true; + profile.setGoal(velocityGoal.in(RadiansPerSecond), motorSim.getAngularVelocityRadPerSec()); + double nextSetpoint = profile.calculateSetpoint(); + double pidPart = feedback.calculate(motorSim.getAngularVelocityRadPerSec(), nextSetpoint); + double ffPart = feedforward.calculate(feedback.getSetpoint()); + System.out.println( + "DEBUG FLYWHEEL SET VELOCITY: nextSetpoint=" + + nextSetpoint + + " feedbackSetpoint=" + + feedback.getSetpoint() + + " pidPart=" + + pidPart + + " ffPart=" + + ffPart); + appliedVolts = Volts.of(pidPart + ffPart); + } + + @Override + public void setNeutralControl() { + isClosedLoop = false; + appliedVolts = Volts.of(0.0); + profile.reset(); + } + + @Override + public boolean atVoltageGoal(Voltage voltageReference) { + return appliedVolts.isNear(voltageReference, Millivolts.of(500)); + } + + @Override + public boolean atVelocityGoal(AngularVelocity velocityReference) { + System.out.println( + "DEBUG atVelocityGoal: getAngularVelocity=" + + motorSim.getAngularVelocity() + + " velocityReference=" + + velocityReference + + " tolerance=" + + constants.constraints.goalTolerance().get()); + return motorSim + .getAngularVelocity() + .isNear(velocityReference, constants.constraints.goalTolerance().get()); + } + + @Override + public void updateGains(Gains gains, GainSlot gainSlot) { + this.gainSlot = gainSlot; + feedback.setPID(gains.kP().get(), gains.kI().get(), gains.kD().get()); + feedforward.setKs(gains.kS().get()); + feedforward.setKv(gains.kV().get()); + feedforward.setKa(gains.kA().get()); + } + + @Override + public void updateConstraints(AngularVelocityConstraints constraints) { + profile.setMaxAcceleration(constraints.maxAcceleration().get().in(RadiansPerSecondPerSecond)); + profile.setMaxVelocity(constraints.maxVelocity().get().in(RadiansPerSecond)); + feedback.setTolerance(constraints.goalTolerance().get().in(RadiansPerSecond)); + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/generic/flywheel/GenericFlywheelIOTalonFX.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/generic/flywheel/GenericFlywheelIOTalonFX.java new file mode 100644 index 00000000..18e97f1a --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/generic/flywheel/GenericFlywheelIOTalonFX.java @@ -0,0 +1,290 @@ +package edu.wpi.team190.gompeilib.subsystems.generic.flywheel; + +import static edu.wpi.first.units.Units.*; + +import com.ctre.phoenix6.BaseStatusSignal; +import com.ctre.phoenix6.StatusSignal; +import com.ctre.phoenix6.configs.MotionMagicConfigs; +import com.ctre.phoenix6.configs.TalonFXConfiguration; +import com.ctre.phoenix6.controls.*; +import com.ctre.phoenix6.hardware.TalonFX; +import com.ctre.phoenix6.signals.MotorAlignmentValue; +import com.ctre.phoenix6.signals.NeutralModeValue; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.units.measure.*; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.AngularVelocityConstraints; +import edu.wpi.team190.gompeilib.core.utility.phoenix.GainSlot; +import edu.wpi.team190.gompeilib.core.utility.phoenix.PhoenixUtil; +import java.util.ArrayList; + +public class GenericFlywheelIOTalonFX implements GenericFlywheelIO { + protected final TalonFX talonFX; + private final TalonFX[] followerTalonFX; + + private final StatusSignal positionRotations; + private final StatusSignal velocityRotationsPerSecond; + private final ArrayList> appliedVolts; + private final ArrayList> supplyCurrentAmps; + private final ArrayList> torqueCurrentAmps; + private final ArrayList> temperatureCelsius; + private AngularVelocity velocityGoal; + private final StatusSignal velocitySetpointRotationsPerSecond; + private final StatusSignal velocityErrorRotationsPerSecond; + + private StatusSignal[] statusSignals; + + private final TalonFXConfiguration talonFXConfiguration; + + private final NeutralOut neutralControlRequest; + private final VoltageOut voltageControlRequest; + private final TorqueCurrentFOC torqueCurrentFOCRequest; + private final VelocityVoltage velocityControlRequest; + private final VelocityTorqueCurrentFOC velocityTorqueCurrentRequest; + + protected GenericFlywheelConstants constants; + + public GenericFlywheelIOTalonFX(GenericFlywheelConstants constants) { + talonFX = new TalonFX(constants.leaderCANID, constants.canBus); + followerTalonFX = + new TalonFX + [constants.alignedFollowerCANIDs.size() + constants.opposedFollowerCANIDs.size()]; + + talonFXConfiguration = new TalonFXConfiguration(); + + talonFXConfiguration.MotorOutput.withInverted(constants.leaderInversion); + + talonFXConfiguration + .CurrentLimits + .withSupplyCurrentLimit(constants.currentLimit.supplyCurrentLimit()) + .withSupplyCurrentLimitEnable(true) + .withStatorCurrentLimit(constants.currentLimit.statorCurrentLimit()) + .withStatorCurrentLimitEnable(true); + talonFXConfiguration.MotorOutput.withNeutralMode(NeutralModeValue.Coast); + talonFXConfiguration + .Slot0 + .withKP(constants.voltageGains.kP().getAsDouble()) + .withKD(constants.voltageGains.kD().getAsDouble()) + .withKS(constants.voltageGains.kS().getAsDouble()) + .withKV(constants.voltageGains.kV().getAsDouble()) + .withKA(constants.voltageGains.kA().getAsDouble()); + + talonFXConfiguration + .Slot1 + .withKP(constants.torqueGains.kP().getAsDouble()) + .withKD(constants.torqueGains.kD().getAsDouble()) + .withKS(constants.torqueGains.kS().getAsDouble()) + .withKV(constants.torqueGains.kV().getAsDouble()) + .withKA(constants.torqueGains.kA().getAsDouble()); + + talonFXConfiguration.Feedback.SensorToMechanismRatio = constants.gearRatio; + + talonFXConfiguration.MotionMagic = + new MotionMagicConfigs() + .withMotionMagicAcceleration( + (AngularAcceleration) constants.constraints.maxAcceleration().get()) + .withMotionMagicCruiseVelocity( + (AngularVelocity) constants.constraints.maxVelocity().get()); + + PhoenixUtil.tryUntilOk(5, () -> talonFX.getConfigurator().apply(talonFXConfiguration, 0.25)); + + final int[] indexHolder = {0}; // mutable index for array insertion + + constants.alignedFollowerCANIDs.forEach( + id -> { + TalonFX follower = new TalonFX(id, talonFX.getNetwork()); + followerTalonFX[indexHolder[0]++] = follower; + + PhoenixUtil.tryUntilOk( + 5, () -> follower.getConfigurator().apply(talonFXConfiguration, 0.25)); + + follower.setControl(new Follower(talonFX.getDeviceID(), MotorAlignmentValue.Aligned)); + }); + + constants.opposedFollowerCANIDs.forEach( + id -> { + TalonFX follower = new TalonFX(id, talonFX.getNetwork()); + followerTalonFX[indexHolder[0]++] = follower; + + PhoenixUtil.tryUntilOk( + 5, () -> follower.getConfigurator().apply(talonFXConfiguration, 0.25)); + + follower.setControl(new Follower(talonFX.getDeviceID(), MotorAlignmentValue.Opposed)); + }); + + positionRotations = talonFX.getPosition(); + velocityRotationsPerSecond = talonFX.getVelocity(); + + appliedVolts = new ArrayList<>(); + supplyCurrentAmps = new ArrayList<>(); + torqueCurrentAmps = new ArrayList<>(); + temperatureCelsius = new ArrayList<>(); + + appliedVolts.add(talonFX.getMotorVoltage()); + supplyCurrentAmps.add(talonFX.getSupplyCurrent()); + torqueCurrentAmps.add(talonFX.getTorqueCurrent()); + temperatureCelsius.add(talonFX.getDeviceTemp()); + + for (TalonFX follower : followerTalonFX) { + appliedVolts.add(follower.getMotorVoltage()); + supplyCurrentAmps.add(follower.getSupplyCurrent()); + torqueCurrentAmps.add(follower.getTorqueCurrent()); + temperatureCelsius.add(follower.getDeviceTemp()); + } + + velocityGoal = RotationsPerSecond.of(0.0); + + velocitySetpointRotationsPerSecond = talonFX.getClosedLoopReference(); + velocityErrorRotationsPerSecond = talonFX.getClosedLoopError(); + + var signalsList = new ArrayList>(); + + signalsList.add(positionRotations); + signalsList.add(velocityRotationsPerSecond); + signalsList.addAll(appliedVolts); + signalsList.addAll(supplyCurrentAmps); + signalsList.addAll(torqueCurrentAmps); + signalsList.addAll(temperatureCelsius); + + statusSignals = new StatusSignal[signalsList.size()]; + + for (int i = 0; i < signalsList.size(); i++) { + statusSignals[i] = signalsList.get(i); + } + + BaseStatusSignal.setUpdateFrequencyForAll(1 / GompeiLib.getLoopPeriod(), statusSignals); + + PhoenixUtil.registerSignals(constants.canBus.isNetworkFD(), statusSignals); + + talonFX.optimizeBusUtilization(); + for (TalonFX follower : followerTalonFX) { + follower.optimizeBusUtilization(); + } + + neutralControlRequest = new NeutralOut(); + voltageControlRequest = new VoltageOut(0.0); + torqueCurrentFOCRequest = new TorqueCurrentFOC(0.0); + + velocityControlRequest = new VelocityVoltage(0).withSlot(0); + velocityTorqueCurrentRequest = new VelocityTorqueCurrentFOC(0.0).withSlot(1); + + this.constants = constants; + } + + @Override + public void updateInputs(GenericFlywheelIOInputs inputs) { + inputs.position = Rotation2d.fromRotations(positionRotations.getValueAsDouble()); + inputs.velocity = velocityRotationsPerSecond.getValue(); + + inputs.appliedVolts = new double[appliedVolts.size()]; + inputs.supplyCurrentAmps = new double[supplyCurrentAmps.size()]; + inputs.torqueCurrentAmps = new double[torqueCurrentAmps.size()]; + inputs.temperatureCelsius = new double[temperatureCelsius.size()]; + + for (int i = 0; i <= followerTalonFX.length; i++) { + inputs.appliedVolts[i] = appliedVolts.get(i).getValueAsDouble(); + inputs.supplyCurrentAmps[i] = supplyCurrentAmps.get(i).getValueAsDouble(); + inputs.torqueCurrentAmps[i] = torqueCurrentAmps.get(i).getValueAsDouble(); + inputs.temperatureCelsius[i] = temperatureCelsius.get(i).getValueAsDouble(); + } + + inputs.velocityGoal = velocityGoal; + inputs.velocitySetpoint = + RotationsPerSecond.of(velocitySetpointRotationsPerSecond.getValueAsDouble()); + inputs.velocityError = RotationsPerSecond.of(velocityRotationsPerSecond.getValueAsDouble()); + + inputs.gainSlot = GainSlot.integerToGainSlot(talonFX.getClosedLoopSlot().getValue()); + } + + @Override + public void setVoltageGoal(Voltage voltageGoal) { + talonFX.setControl( + voltageControlRequest.withOutput(voltageGoal).withEnableFOC(constants.enableFOC)); + } + + @Override + public void setCurrentGoal(Current currentGoal) { + talonFX.setControl(torqueCurrentFOCRequest.withOutput(currentGoal)); + } + + @Override + public void setNeutralControl() { + talonFX.setControl(neutralControlRequest); + } + + @Override + public void setVelocityGoal(AngularVelocity velocityGoal) { + this.velocityGoal = velocityGoal; + talonFX.setControl( + velocityControlRequest.withVelocity(this.velocityGoal).withEnableFOC(constants.enableFOC)); + } + + @Override + public void setVelocityGoal(AngularVelocity velocityGoal, Current currentFeedforward) { + this.velocityGoal = velocityGoal; + talonFX.setControl( + velocityTorqueCurrentRequest + .withVelocity(this.velocityGoal) + .withFeedForward(currentFeedforward)); + } + + @Override + public boolean atVoltageGoal(Voltage voltageReference) { + return appliedVolts.get(0).isNear(voltageReference, Millivolts.of(500)); + } + + @Override + public boolean atCurrentGoal(Current currentReference) { + return torqueCurrentAmps.get(0).isNear(currentReference, Milliamps.of(500)); + } + + @Override + public boolean atVelocityGoal(AngularVelocity velocityReference) { + return velocityRotationsPerSecond.isNear( + velocityReference, + RotationsPerSecond.of(constants.constraints.goalTolerance().get(RotationsPerSecond))); + } + + @Override + public void updateGains(Gains gains, GainSlot gainSlot) { + switch (gainSlot) { + case ONE -> + talonFXConfiguration + .Slot1 + .withKP(gains.kP().get()) + .withKI(gains.kI().get()) + .withKD(gains.kD().get()) + .withKS(gains.kS().get()) + .withKV(gains.kV().get()) + .withKA(gains.kA().get()) + .withKG(gains.kG().get()); + default -> + talonFXConfiguration + .Slot0 + .withKP(gains.kP().get()) + .withKI(gains.kI().get()) + .withKD(gains.kD().get()) + .withKS(gains.kS().get()) + .withKV(gains.kV().get()) + .withKA(gains.kA().get()) + .withKG(gains.kG().get()); + } + PhoenixUtil.tryUntilOk(5, () -> talonFX.getConfigurator().apply(talonFXConfiguration)); + for (TalonFX follower : followerTalonFX) { + PhoenixUtil.tryUntilOk(5, () -> follower.getConfigurator().apply(talonFXConfiguration)); + } + } + + @Override + public void updateConstraints(AngularVelocityConstraints constraints) { + talonFXConfiguration + .MotionMagic + .withMotionMagicAcceleration(constraints.maxAcceleration().get(RotationsPerSecondPerSecond)) + .withMotionMagicCruiseVelocity(constraints.maxVelocity().get(RotationsPerSecond)); + PhoenixUtil.tryUntilOk(5, () -> talonFX.getConfigurator().apply(talonFXConfiguration)); + for (TalonFX follower : followerTalonFX) { + PhoenixUtil.tryUntilOk(5, () -> follower.getConfigurator().apply(talonFXConfiguration)); + } + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/generic/flywheel/GenericFlywheelIOTalonFXSim.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/generic/flywheel/GenericFlywheelIOTalonFXSim.java new file mode 100644 index 00000000..05fcdcd0 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/generic/flywheel/GenericFlywheelIOTalonFXSim.java @@ -0,0 +1,53 @@ +package edu.wpi.team190.gompeilib.subsystems.generic.flywheel; + +import static edu.wpi.first.units.Units.RadiansPerSecond; +import static edu.wpi.first.units.Units.RadiansPerSecondPerSecond; + +import com.ctre.phoenix6.sim.TalonFXSimState; +import edu.wpi.first.math.system.plant.LinearSystemId; +import edu.wpi.first.units.measure.AngularAcceleration; +import edu.wpi.first.units.measure.AngularVelocity; +import edu.wpi.first.wpilibj.RobotController; +import edu.wpi.first.wpilibj.simulation.FlywheelSim; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import edu.wpi.team190.gompeilib.core.logging.Trace; + +public class GenericFlywheelIOTalonFXSim extends GenericFlywheelIOTalonFX { + private final FlywheelSim flywheelSim; + + private final TalonFXSimState flywheelController; + + public GenericFlywheelIOTalonFXSim(GenericFlywheelConstants constants) { + super(constants); + flywheelSim = + new FlywheelSim( + LinearSystemId.createFlywheelSystem( + constants.motorConfig, constants.momentOfInertia, constants.gearRatio), + constants.motorConfig); + + flywheelController = super.talonFX.getSimState(); + } + + @Override + @Trace + public void updateInputs(GenericFlywheelIOInputs inputs) { + flywheelController.setSupplyVoltage(RobotController.getBatteryVoltage()); + double flywheelVoltage = flywheelController.getMotorVoltage(); + + flywheelSim.setInputVoltage(flywheelVoltage); + + flywheelSim.update(GompeiLib.getLoopPeriod()); + + AngularVelocity rotorVelocity = + AngularVelocity.ofBaseUnits( + flywheelSim.getAngularVelocityRadPerSec() * constants.gearRatio, RadiansPerSecond); + AngularAcceleration rotorAcceleration = + AngularAcceleration.ofBaseUnits( + flywheelSim.getAngularAccelerationRadPerSecSq() * constants.gearRatio, + RadiansPerSecondPerSecond); + flywheelController.setRotorVelocity(rotorVelocity); + flywheelController.setRotorAcceleration(rotorAcceleration); + + super.updateInputs(inputs); + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/generic/flywheel/GenericFlywheelState.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/generic/flywheel/GenericFlywheelState.java new file mode 100644 index 00000000..05de77f1 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/generic/flywheel/GenericFlywheelState.java @@ -0,0 +1,9 @@ +package edu.wpi.team190.gompeilib.subsystems.generic.flywheel; + +public enum GenericFlywheelState { + VELOCITY_VOLTAGE_CONTROL, + VELOCITY_TORQUE_CONTROL, + VOLTAGE_CONTROL, + IDLE, + STOP +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/generic/roller/GenericRoller.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/generic/roller/GenericRoller.java new file mode 100644 index 00000000..ef1d434c --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/generic/roller/GenericRoller.java @@ -0,0 +1,74 @@ +package edu.wpi.team190.gompeilib.subsystems.generic.roller; + +import static edu.wpi.first.units.Units.Volts; + +import edu.wpi.first.units.VoltageUnit; +import edu.wpi.first.units.measure.Voltage; +import edu.wpi.first.wpilibj2.command.Subsystem; +import edu.wpi.team190.gompeilib.core.logging.Trace; +import edu.wpi.team190.gompeilib.core.utility.Setpoint; +import lombok.Getter; +import org.littletonrobotics.junction.Logger; + +public class GenericRoller { + private final GenericRollerIO io; + private final GenericRollerIOInputsAutoLogged inputs; + + private final String aKitTopic; + + @Getter private Setpoint voltageGoal; + + public GenericRoller( + GenericRollerIO io, + Subsystem subsystem, + GenericRollerConstants constants, + String name, + Setpoint voltageGoal) { + this.io = io; + inputs = new GenericRollerIOInputsAutoLogged(); + aKitTopic = subsystem.getName() + "/Roller" + name; + this.voltageGoal = voltageGoal; + } + + public GenericRoller( + GenericRollerIO io, Subsystem subsystem, GenericRollerConstants constants, String name) { + this( + io, + subsystem, + constants, + name, + new Setpoint<>(Volts.of(0), constants.voltageOffsetStep, Volts.of(-12), Volts.of(12))); + } + + @Trace + public void periodic() { + io.updateInputs(inputs); + Logger.processInputs(aKitTopic, inputs); + + Logger.recordOutput(aKitTopic + "/Voltage Goal", voltageGoal.getSetpoint()); + Logger.recordOutput(aKitTopic + "/Voltage Offset", voltageGoal.getOffset()); + Logger.recordOutput(aKitTopic + "/At Voltage Goal", atVoltageGoal()); + + io.setVoltageGoal((Voltage) voltageGoal.getNewSetpoint()); + } + + public boolean atVoltageGoal(Voltage voltageReference) { + return io.atVoltageGoal(voltageReference); + } + + public boolean atVoltageGoal() { + return atVoltageGoal((Voltage) voltageGoal.getNewSetpoint()); + } + + public void setVoltageGoal(Voltage voltage) { + voltageGoal.setSetpoint(voltage); + } + + public void setVoltageGoal(Setpoint voltage) { + voltageGoal = voltage; + } + + public double[] getTorqueCurrent() { + return inputs.torqueCurrentAmps; + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/generic/roller/GenericRollerConstants.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/generic/roller/GenericRollerConstants.java new file mode 100644 index 00000000..921eb7c3 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/generic/roller/GenericRollerConstants.java @@ -0,0 +1,38 @@ +package edu.wpi.team190.gompeilib.subsystems.generic.roller; + +import com.ctre.phoenix6.CANBus; +import com.ctre.phoenix6.signals.InvertedValue; +import com.ctre.phoenix6.signals.NeutralModeValue; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.units.measure.MomentOfInertia; +import edu.wpi.first.units.measure.Voltage; +import edu.wpi.team190.gompeilib.core.utility.control.CurrentLimits; +import java.util.Set; +import lombok.Builder; +import lombok.NonNull; +import lombok.Singular; + +@Builder(setterPrefix = "with") +public class GenericRollerConstants { + @NonNull public final Integer leaderCANID; + @NonNull public final InvertedValue leaderInvertedValue; + + @Singular(value = "alignedFollowerCANID") + @NonNull + public final Set alignedFollowerCANIDs; + + @Singular(value = "opposedFollowerCANID") + @NonNull + public final Set opposedFollowerCANIDs; + + @NonNull public final CurrentLimits currentLimits; + @NonNull public final DCMotor rollerGearbox; + @NonNull public final Double rollerMotorGearRatio; + @NonNull public final MomentOfInertia momentOfInertia; + @NonNull public final NeutralModeValue neutralMode; + @NonNull public final CANBus canBus; + + @NonNull public final Boolean enableFOC; + + @NonNull public final Voltage voltageOffsetStep; +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/generic/roller/GenericRollerIO.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/generic/roller/GenericRollerIO.java new file mode 100644 index 00000000..a0cb8079 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/generic/roller/GenericRollerIO.java @@ -0,0 +1,29 @@ +package edu.wpi.team190.gompeilib.subsystems.generic.roller; + +import static edu.wpi.first.units.Units.*; + +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.units.measure.AngularVelocity; +import edu.wpi.first.units.measure.Voltage; +import org.littletonrobotics.junction.AutoLog; + +public interface GenericRollerIO { + @AutoLog + public static class GenericRollerIOInputs { + public Rotation2d position = new Rotation2d(); + public AngularVelocity velocity = RadiansPerSecond.of(0.0); + + public double[] appliedVolts = new double[] {}; + public double[] supplyCurrentAmps = new double[] {}; + public double[] torqueCurrentAmps = new double[] {}; + public double[] temperatureCelsius = new double[] {}; + } + + default void updateInputs(GenericRollerIOInputs inputs) {} + + default void setVoltageGoal(Voltage volts) {} + + default boolean atVoltageGoal(Voltage voltageReference) { + return false; + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/generic/roller/GenericRollerIOSim.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/generic/roller/GenericRollerIOSim.java new file mode 100644 index 00000000..12065df8 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/generic/roller/GenericRollerIOSim.java @@ -0,0 +1,72 @@ +package edu.wpi.team190.gompeilib.subsystems.generic.roller; + +import static edu.wpi.first.units.Units.*; + +import edu.wpi.first.math.MathUtil; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.system.plant.LinearSystemId; +import edu.wpi.first.units.measure.Angle; +import edu.wpi.first.units.measure.Voltage; +import edu.wpi.first.wpilibj.simulation.DCMotorSim; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import java.util.Arrays; + +public class GenericRollerIOSim implements GenericRollerIO { + private final DCMotorSim motorSim; + private final GenericRollerConstants constants; + + private Voltage appliedVolts; + + private Angle accumulatedPosition; + + public GenericRollerIOSim(GenericRollerConstants constants) { + this.constants = constants; + motorSim = + new DCMotorSim( + LinearSystemId.createDCMotorSystem( + constants.rollerGearbox, + constants.momentOfInertia.baseUnitMagnitude(), + constants.rollerMotorGearRatio), + constants.rollerGearbox); + + appliedVolts = Volts.of(0.0); + + accumulatedPosition = Radians.of(0.0); + } + + @Override + public void updateInputs(GenericRollerIOInputs inputs) { + appliedVolts = Volts.of(MathUtil.clamp(appliedVolts.in(Volts), -12.0, 12.0)); + motorSim.setInputVoltage(appliedVolts.in(Volts)); + motorSim.update(GompeiLib.getLoopPeriod()); + + accumulatedPosition = + Radians.of( + accumulatedPosition.in(Radians) + + (motorSim.getAngularVelocityRadPerSec() * GompeiLib.getLoopPeriod())); + + inputs.position = Rotation2d.fromRadians(accumulatedPosition.in(Radians)); + inputs.velocity = RadiansPerSecond.of(motorSim.getAngularVelocityRadPerSec()); + + int numMotors = + 1 + constants.alignedFollowerCANIDs.size() + constants.opposedFollowerCANIDs.size(); + inputs.appliedVolts = new double[numMotors]; + inputs.supplyCurrentAmps = new double[numMotors]; + inputs.torqueCurrentAmps = new double[numMotors]; + inputs.temperatureCelsius = new double[numMotors]; + + Arrays.fill(inputs.appliedVolts, appliedVolts.in(Volts)); + Arrays.fill(inputs.supplyCurrentAmps, motorSim.getCurrentDrawAmps()); + Arrays.fill(inputs.torqueCurrentAmps, motorSim.getCurrentDrawAmps()); + } + + @Override + public void setVoltageGoal(Voltage voltageGoal) { + appliedVolts = voltageGoal; + } + + @Override + public boolean atVoltageGoal(Voltage voltageReference) { + return appliedVolts.isNear(voltageReference, Millivolts.of(500)); + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/generic/roller/GenericRollerIOTalonFX.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/generic/roller/GenericRollerIOTalonFX.java new file mode 100644 index 00000000..9378c524 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/generic/roller/GenericRollerIOTalonFX.java @@ -0,0 +1,160 @@ +package edu.wpi.team190.gompeilib.subsystems.generic.roller; + +import static edu.wpi.first.units.Units.Millivolts; + +import com.ctre.phoenix6.BaseStatusSignal; +import com.ctre.phoenix6.StatusSignal; +import com.ctre.phoenix6.configs.TalonFXConfiguration; +import com.ctre.phoenix6.controls.Follower; +import com.ctre.phoenix6.controls.VoltageOut; +import com.ctre.phoenix6.hardware.TalonFX; +import com.ctre.phoenix6.signals.MotorAlignmentValue; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.units.measure.*; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import edu.wpi.team190.gompeilib.core.utility.phoenix.PhoenixUtil; +import java.util.ArrayList; + +public class GenericRollerIOTalonFX implements GenericRollerIO { + protected final TalonFX talonFX; + private final TalonFX[] followerTalonFX; + + private final StatusSignal positionRotations; + private final StatusSignal velocityRotationsPerSecond; + private final ArrayList> appliedVolts; + private final ArrayList> supplyCurrentAmps; + private final ArrayList> torqueCurrentAmps; + private final ArrayList> temperatureCelsius; + + private StatusSignal[] statusSignals; + + private final TalonFXConfiguration talonFXConfiguration; + + private final VoltageOut voltageRequest; + + protected GenericRollerConstants constants; + + public GenericRollerIOTalonFX(GenericRollerConstants constants) { + talonFX = new TalonFX(constants.leaderCANID, constants.canBus); + followerTalonFX = + new TalonFX + [constants.alignedFollowerCANIDs.size() + constants.opposedFollowerCANIDs.size()]; + + talonFXConfiguration = new TalonFXConfiguration(); + + talonFXConfiguration.MotorOutput.withInverted(constants.leaderInvertedValue); + talonFXConfiguration.MotorOutput.NeutralMode = constants.neutralMode; + + talonFXConfiguration + .CurrentLimits + .withSupplyCurrentLimit(constants.currentLimits.supplyCurrentLimit()) + .withSupplyCurrentLimitEnable(true) + .withStatorCurrentLimit(constants.currentLimits.statorCurrentLimit()) + .withStatorCurrentLimitEnable(true); + talonFXConfiguration.OpenLoopRamps.VoltageOpenLoopRampPeriod = 0.25; + + talonFXConfiguration.Feedback.SensorToMechanismRatio = constants.rollerMotorGearRatio; + + PhoenixUtil.tryUntilOk(5, () -> talonFX.getConfigurator().apply(talonFXConfiguration, 0.25)); + + final int[] indexHolder = {0}; // mutable index for array insertion + + constants.alignedFollowerCANIDs.forEach( + id -> { + TalonFX follower = new TalonFX(id, talonFX.getNetwork()); + followerTalonFX[indexHolder[0]++] = follower; + + PhoenixUtil.tryUntilOk( + 5, () -> follower.getConfigurator().apply(talonFXConfiguration, 0.25)); + + follower.setControl(new Follower(talonFX.getDeviceID(), MotorAlignmentValue.Aligned)); + }); + + constants.opposedFollowerCANIDs.forEach( + id -> { + TalonFX follower = new TalonFX(id, talonFX.getNetwork()); + followerTalonFX[indexHolder[0]++] = follower; + + PhoenixUtil.tryUntilOk( + 5, () -> follower.getConfigurator().apply(talonFXConfiguration, 0.25)); + + follower.setControl(new Follower(talonFX.getDeviceID(), MotorAlignmentValue.Opposed)); + }); + + positionRotations = talonFX.getPosition(); + velocityRotationsPerSecond = talonFX.getVelocity(); + + appliedVolts = new ArrayList<>(); + supplyCurrentAmps = new ArrayList<>(); + torqueCurrentAmps = new ArrayList<>(); + temperatureCelsius = new ArrayList<>(); + + appliedVolts.add(talonFX.getMotorVoltage()); + supplyCurrentAmps.add(talonFX.getSupplyCurrent()); + torqueCurrentAmps.add(talonFX.getTorqueCurrent()); + temperatureCelsius.add(talonFX.getDeviceTemp()); + + for (TalonFX follower : followerTalonFX) { + appliedVolts.add(follower.getMotorVoltage()); + supplyCurrentAmps.add(follower.getSupplyCurrent()); + torqueCurrentAmps.add(follower.getTorqueCurrent()); + temperatureCelsius.add(follower.getDeviceTemp()); + } + + var signalsList = new ArrayList>(); + + signalsList.add(positionRotations); + signalsList.add(velocityRotationsPerSecond); + signalsList.addAll(appliedVolts); + signalsList.addAll(supplyCurrentAmps); + signalsList.addAll(torqueCurrentAmps); + signalsList.addAll(temperatureCelsius); + + statusSignals = new StatusSignal[signalsList.size()]; + + for (int i = 0; i < signalsList.size(); i++) { + statusSignals[i] = signalsList.get(i); + } + + BaseStatusSignal.setUpdateFrequencyForAll(1 / GompeiLib.getLoopPeriod(), statusSignals); + + PhoenixUtil.registerSignals(constants.canBus.isNetworkFD(), statusSignals); + + talonFX.optimizeBusUtilization(); + for (TalonFX follower : followerTalonFX) { + follower.optimizeBusUtilization(); + } + + voltageRequest = new VoltageOut(0.0).withEnableFOC(constants.enableFOC); + + this.constants = constants; + } + + @Override + public void updateInputs(GenericRollerIOInputs inputs) { + inputs.position = Rotation2d.fromRotations(positionRotations.getValueAsDouble()); + inputs.velocity = velocityRotationsPerSecond.getValue(); + + inputs.appliedVolts = new double[appliedVolts.size()]; + inputs.supplyCurrentAmps = new double[supplyCurrentAmps.size()]; + inputs.torqueCurrentAmps = new double[torqueCurrentAmps.size()]; + inputs.temperatureCelsius = new double[temperatureCelsius.size()]; + + for (int i = 0; i <= followerTalonFX.length; i++) { + inputs.appliedVolts[i] = appliedVolts.get(i).getValueAsDouble(); + inputs.supplyCurrentAmps[i] = supplyCurrentAmps.get(i).getValueAsDouble(); + inputs.torqueCurrentAmps[i] = torqueCurrentAmps.get(i).getValueAsDouble(); + inputs.temperatureCelsius[i] = temperatureCelsius.get(i).getValueAsDouble(); + } + } + + @Override + public void setVoltageGoal(Voltage voltageGoal) { + talonFX.setControl(voltageRequest.withOutput(voltageGoal)); + } + + @Override + public boolean atVoltageGoal(Voltage referenceVoltage) { + return appliedVolts.get(0).isNear(referenceVoltage, Millivolts.of(500)); + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/generic/roller/GenericRollerIOTalonFXSim.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/generic/roller/GenericRollerIOTalonFXSim.java new file mode 100644 index 00000000..b9d327d6 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/generic/roller/GenericRollerIOTalonFXSim.java @@ -0,0 +1,55 @@ +package edu.wpi.team190.gompeilib.subsystems.generic.roller; + +import static edu.wpi.first.units.Units.Radians; +import static edu.wpi.first.units.Units.RadiansPerSecond; + +import com.ctre.phoenix6.sim.TalonFXSimState; +import edu.wpi.first.math.system.plant.LinearSystemId; +import edu.wpi.first.units.measure.Angle; +import edu.wpi.first.units.measure.AngularVelocity; +import edu.wpi.first.wpilibj.RobotController; +import edu.wpi.first.wpilibj.simulation.DCMotorSim; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import edu.wpi.team190.gompeilib.core.logging.Trace; + +public class GenericRollerIOTalonFXSim extends GenericRollerIOTalonFX { + private final DCMotorSim rollerSim; + + private final TalonFXSimState rollerController; + + public GenericRollerIOTalonFXSim(GenericRollerConstants constants) { + super(constants); + rollerSim = + new DCMotorSim( + LinearSystemId.createDCMotorSystem( + constants.rollerGearbox, + constants.momentOfInertia.baseUnitMagnitude(), + constants.rollerMotorGearRatio), + constants.rollerGearbox); + + rollerController = super.talonFX.getSimState(); + } + + @Override + @Trace + public void updateInputs(GenericRollerIOInputs inputs) { + rollerController.setSupplyVoltage(RobotController.getBatteryVoltage()); + double rollerVoltage = rollerController.getMotorVoltage(); + + rollerSim.setInputVoltage(rollerVoltage); + + rollerSim.update(GompeiLib.getLoopPeriod()); + + Angle rotorPosition = + Angle.ofBaseUnits( + rollerSim.getAngularPositionRad() * constants.rollerMotorGearRatio, Radians); + AngularVelocity rotorVelocity = + AngularVelocity.ofBaseUnits( + rollerSim.getAngularVelocityRadPerSec() * constants.rollerMotorGearRatio, + RadiansPerSecond); + rollerController.setRawRotorPosition(rotorPosition); + rollerController.setRotorVelocity(rotorVelocity); + + super.updateInputs(inputs); + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/vision/Vision.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/vision/Vision.java new file mode 100644 index 00000000..fb491ae1 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/vision/Vision.java @@ -0,0 +1,50 @@ +package edu.wpi.team190.gompeilib.subsystems.vision; + +import edu.wpi.first.apriltag.AprilTag; +import edu.wpi.first.apriltag.AprilTagFieldLayout; +import edu.wpi.first.networktables.NetworkTable; +import edu.wpi.first.networktables.NetworkTableInstance; +import edu.wpi.team190.gompeilib.core.utility.VirtualSubsystem; +import edu.wpi.team190.gompeilib.subsystems.vision.camera.Camera; +import java.util.function.Supplier; +import lombok.Getter; + +/** + * A class to contain all the {@link Camera Camera}s for a robot and methods to interact with them. + * A vision object publishes the data about the field to the robot, and runs the periodic method for + * all of its cameras. + */ +public class Vision extends VirtualSubsystem { + @Getter private final Camera[] cameras; + @Getter private final Supplier fieldLayoutSupplier; + + public Vision(Supplier fieldLayoutSupplier, Camera... cameras) { + this.cameras = cameras; + this.fieldLayoutSupplier = fieldLayoutSupplier; + + NetworkTable fieldTable = NetworkTableInstance.getDefault().getTable("field"); + + for (AprilTag tag : fieldLayoutSupplier.get().getTags()) { + fieldTable + .getDoubleArrayTopic("tag_" + tag.ID) + .publish() + .set( + new double[] { + tag.pose.getX(), + tag.pose.getY(), + tag.pose.getZ(), + tag.pose.getRotation().getQuaternion().getW(), + tag.pose.getRotation().getQuaternion().getX(), + tag.pose.getRotation().getQuaternion().getY(), + tag.pose.getRotation().getQuaternion().getZ() + }); + } + } + + @Override + public void periodic() { + for (Camera camera : cameras) { + camera.periodic(); + } + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/vision/VisionConstants.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/vision/VisionConstants.java new file mode 100644 index 00000000..16deba0c --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/vision/VisionConstants.java @@ -0,0 +1,59 @@ +package edu.wpi.team190.gompeilib.subsystems.vision; + +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.geometry.Pose3d; +import edu.wpi.first.math.geometry.Transform3d; +import edu.wpi.first.math.numbers.N1; +import edu.wpi.first.math.numbers.N3; +import edu.wpi.first.math.numbers.N5; +import edu.wpi.team190.gompeilib.subsystems.vision.camera.CameraType; +import lombok.Builder; + +public class VisionConstants { + public static final double AMBIGUITY_THRESHOLD = 0.4; + public static final double XY_STDEV_DISTANCE_EXPONENT = 1.2; + public static final double XY_STDEV_TAG_COUNT_EXPONENT = 2.0; + + @Builder + public record StaticLimelightConfig( + String key, + CameraType cameraType, + double horizontalFOV, + double verticalFOV, + double megatagXYStdev, + double metatagThetaStdev, + double megatag2XYStdev, + Transform3d robotToCameraTransform, + Boolean enableRewind) {} + + @Builder + public record MovingLimelightConfig( + String key, + CameraType cameraType, + double horizontalFOV, + double verticalFOV, + double megatagXYStdev, + double metatagThetaStdev, + double megatag2XYStdev, + Transform3d robotToRotationAxisTransform, + Transform3d rotationAxisToLensTransform, + Boolean enableRewind) {} + + @Builder + public record GompeiVisionConfig( + String key, + String hardwareID, + CameraType cameraType, + double exposure, + double gain, + int width, + int height, + Matrix cameraMatrix, + Matrix distortionCoefficients, + double horizontalFOV, + double verticalFOV, + double singletagXYStdev, + double thetaStdev, + double multitagXYStdev, + Pose3d robotRelativePose) {} +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/vision/camera/Camera.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/vision/camera/Camera.java new file mode 100644 index 00000000..7745b76b --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/vision/camera/Camera.java @@ -0,0 +1,58 @@ +package edu.wpi.team190.gompeilib.subsystems.vision.camera; + +import edu.wpi.first.math.geometry.Pose3d; +import edu.wpi.team190.gompeilib.core.logging.Trace; +import edu.wpi.team190.gompeilib.subsystems.vision.data.VisionMultiTxTyObservation; +import edu.wpi.team190.gompeilib.subsystems.vision.data.VisionPoseObservation; +import edu.wpi.team190.gompeilib.subsystems.vision.data.VisionSingleTxTyObservation; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; +import lombok.Getter; + +public abstract class Camera { + @Getter private final String name; + List>> poseObservers; + List>> multiTxTyObservers; + List>> singleTxTyObservers; + + @Getter List poseObservationList; + @Getter List multiTxTyObservationList; + @Getter List singleTxTyObservationList; + + @Getter protected Pose3d currentCameraPose; + + public Camera( + String name, + List>> poseObservers, + List>> multiTxTyObservers, + List>> singleTxTyObservers) { + this.name = name; + this.poseObservers = poseObservers; + this.multiTxTyObservers = multiTxTyObservers; + this.singleTxTyObservers = singleTxTyObservers; + + poseObservationList = new ArrayList<>(); + multiTxTyObservationList = new ArrayList<>(); + singleTxTyObservationList = new ArrayList<>(); + } + + @Trace + public void periodic() {} + + public void setCameraPose(Pose3d cameraPose) { + currentCameraPose = cameraPose; + } + + public void sendObservers() { + for (Consumer> observer : poseObservers) { + observer.accept(poseObservationList); + } + for (Consumer> observer : multiTxTyObservers) { + observer.accept(multiTxTyObservationList); + } + for (Consumer> observer : singleTxTyObservers) { + observer.accept(singleTxTyObservationList); + } + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/vision/camera/CameraGompeiVision.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/vision/camera/CameraGompeiVision.java new file mode 100644 index 00000000..69573856 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/vision/camera/CameraGompeiVision.java @@ -0,0 +1,244 @@ +package edu.wpi.team190.gompeilib.subsystems.vision.camera; + +import edu.wpi.first.apriltag.AprilTagFieldLayout; +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.geometry.*; +import edu.wpi.first.wpilibj.DriverStation; +import edu.wpi.team190.gompeilib.core.utility.GeometryUtil; +import edu.wpi.team190.gompeilib.subsystems.vision.VisionConstants; +import edu.wpi.team190.gompeilib.subsystems.vision.VisionConstants.GompeiVisionConfig; +import edu.wpi.team190.gompeilib.subsystems.vision.data.VisionMultiTxTyObservation; +import edu.wpi.team190.gompeilib.subsystems.vision.data.VisionPoseObservation; +import edu.wpi.team190.gompeilib.subsystems.vision.io.CameraIOGompeiVision; +import edu.wpi.team190.gompeilib.subsystems.vision.io.GompeiVisionIOInputsAutoLogged; +import java.util.*; +import java.util.function.Consumer; +import java.util.function.Supplier; +import lombok.Getter; +import lombok.experimental.ExtensionMethod; +import org.littletonrobotics.junction.Logger; + +@ExtensionMethod(GeometryUtil.class) +public class CameraGompeiVision extends Camera { + private final GompeiVisionIOInputsAutoLogged inputs; + private final CameraIOGompeiVision io; + + private final GompeiVisionConfig config; + private final Supplier aprilTagFieldLayoutSupplier; + private final double fieldBorderMarginMeters; + private final Supplier currentRobotPoseSupplier; + + @Getter private final String name; + + @Getter private final List allTagPoses; + @Getter private Pose2d robotPose; + + public CameraGompeiVision( + CameraIOGompeiVision io, + GompeiVisionConfig config, + Supplier aprilTagFieldLayoutSupplier, + double fieldBorderMarginMeters, + Supplier currentRobotPoseSupplier, + List>> poseObservers, + List>> txtyObservers) { + super(config.key(), poseObservers, txtyObservers, new ArrayList<>()); + + inputs = new GompeiVisionIOInputsAutoLogged(); + this.io = io; + + this.config = config; + this.aprilTagFieldLayoutSupplier = aprilTagFieldLayoutSupplier; + this.fieldBorderMarginMeters = fieldBorderMarginMeters; + this.currentRobotPoseSupplier = currentRobotPoseSupplier; + + this.name = this.config.key(); + + allTagPoses = new ArrayList<>(); + robotPose = Pose2d.kZero; + + currentCameraPose = config.robotRelativePose(); + } + + @Override + public void periodic() { + poseObservationList.clear(); + multiTxTyObservationList.clear(); + + io.updateInputs(inputs); + Logger.processInputs("Vision/Cameras/" + this.name, inputs); + + allTagPoses.clear(); + robotPose = Pose2d.kZero; + + for (int i = 0; i < inputs.frames.length; i++) { + double timestamp = inputs.timestamps[i]; + int totalTargets; + double averageDistance = 0.0; + double[] values = inputs.frames[i]; + + if (values.length == 0 || values[0] == 0) { + continue; + } + + Pose3d cameraPose = null; + Pose2d robotPose = null; + + switch ((int) values[0]) { + case 1: + // One pose + cameraPose = + new Pose3d( + values[2], + values[3], + values[4], + new Rotation3d(new Quaternion(values[5], values[6], values[7], values[8]))); + robotPose = + cameraPose + .toPose2d() + .transformBy(currentCameraPose.toPose2d().toTransform2d().inverse()); + break; + + case 2: + // Multiple poses + double error0 = values[1]; + double error1 = values[9]; + Pose3d cameraPose0 = + new Pose3d( + values[2], + values[3], + values[4], + new Rotation3d(new Quaternion(values[5], values[6], values[7], values[8]))); + + Pose3d cameraPose1 = + new Pose3d( + values[10], + values[11], + values[12], + new Rotation3d(new Quaternion(values[13], values[14], values[15], values[16]))); + Transform2d cameraToRobot = currentCameraPose.toPose2d().toTransform2d().inverse(); + Pose2d robotPose0 = cameraPose0.toPose2d().transformBy(cameraToRobot); + Pose2d robotPose1 = cameraPose1.toPose2d().transformBy(cameraToRobot); + + if (error0 < error1 * VisionConstants.AMBIGUITY_THRESHOLD + || error1 < error0 * VisionConstants.AMBIGUITY_THRESHOLD) { + Rotation2d currentRotation = currentRobotPoseSupplier.get().getRotation(); + Rotation2d visionRotation0 = robotPose0.getRotation(); + Rotation2d visionRotation1 = robotPose1.getRotation(); + if (Math.abs(currentRotation.minus(visionRotation0).getRadians()) + < Math.abs(currentRotation.minus(visionRotation1).getRadians())) { + robotPose = robotPose0; + cameraPose = cameraPose0; + } else { + robotPose = robotPose1; + cameraPose = cameraPose1; + } + } + + break; + default: + DriverStation.reportWarning("FAILED TO CAPTURE FRAMES", false); + continue; + } + + if (cameraPose == null || robotPose == null) { + continue; + } + + // Exit if robot pose is off the field + double fieldLength = aprilTagFieldLayoutSupplier.get().getFieldLength(); + double fieldWidth = aprilTagFieldLayoutSupplier.get().getFieldWidth(); + + if (robotPose.getX() < -fieldBorderMarginMeters + || robotPose.getX() > fieldLength + fieldBorderMarginMeters + || robotPose.getY() < -fieldBorderMarginMeters + || robotPose.getY() > fieldWidth + fieldBorderMarginMeters) { + continue; + } + + // Get tag poses and update last detection times + List tagPoses = new ArrayList<>(); + for (int j = (values[0] == 1 ? 9 : 17); j < values.length; j += 10) { + int tagId = (int) values[j]; + Optional tagPose = aprilTagFieldLayoutSupplier.get().getTagPose(tagId); + tagPose.ifPresent(tagPoses::add); + } + + // Prepare arrays + totalTargets = tagPoses.size(); + Set tagIds = new HashSet<>(); + + if (!tagPoses.isEmpty()) { + // Calculate average distance + double totalDistance = 0.0; + for (Pose3d tagPose : tagPoses) { + totalDistance += tagPose.getTranslation().getDistance(currentCameraPose.getTranslation()); + } + averageDistance = totalDistance / tagPoses.size(); + + // --- Parse tag angle + distance data --- + int tagEstimationDataEndIndex = + switch ((int) values[0]) { + case 1 -> 8; + case 2 -> 16; + default -> 0; + }; + + int indexCounter = 0; + + // Step through each 10-value chunk safely + for (int index = tagEstimationDataEndIndex + 1; index + 9 < values.length; index += 10) { + int tagId = (int) values[index]; + double[] tx = new double[4]; + double[] ty = new double[4]; + + // Read 4 corner pairs + for (int j = 0; j < 4; j++) { + tx[i] = values[index + 1 + (2 * i)]; + ty[i] = values[index + 1 + (2 * i) + 1]; + } + + double distance = values[index + 9]; + + // Store data + if (indexCounter < totalTargets) { + indexCounter++; + multiTxTyObservationList.add( + new VisionMultiTxTyObservation(tagId, tx, ty, distance, timestamp, cameraPose)); + tagIds.add(tagId); + } else { + System.out.println("[WARN] More tag data than expected: indexCounter=" + indexCounter); + } + } + + // Optional debug check + if ((values.length - (tagEstimationDataEndIndex + 1)) % 10 != 0) { + System.out.println( + "[WARN] Observation array not multiple of 10! Length=" + + values.length + + " start=" + + (tagEstimationDataEndIndex + 1)); + } + } + double xyStdevCoeff; + double thetaStdev; + + if (totalTargets > 1) { + xyStdevCoeff = config.multitagXYStdev(); + thetaStdev = + config.thetaStdev() * Math.pow(averageDistance, 1.2) / Math.pow(totalTargets, 2.0); + } else { + xyStdevCoeff = config.singletagXYStdev(); + thetaStdev = Double.POSITIVE_INFINITY; + } + + // Add observation to list + double xyStdDev = xyStdevCoeff * Math.pow(averageDistance, 1.2) / Math.pow(totalTargets, 2.0); + + poseObservationList.add( + new VisionPoseObservation( + robotPose, tagIds, timestamp, VecBuilder.fill(xyStdDev, xyStdDev, thetaStdev))); + } + + super.sendObservers(); + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/vision/camera/CameraMovingLimelight.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/vision/camera/CameraMovingLimelight.java new file mode 100644 index 00000000..11ffb7fd --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/vision/camera/CameraMovingLimelight.java @@ -0,0 +1,247 @@ +package edu.wpi.team190.gompeilib.subsystems.vision.camera; + +import static edu.wpi.first.units.Units.Degrees; + +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Pose3d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Rotation3d; +import edu.wpi.first.math.geometry.Transform2d; +import edu.wpi.first.math.kinematics.ChassisSpeeds; +import edu.wpi.first.math.util.Units; +import edu.wpi.first.networktables.DoubleArrayPublisher; +import edu.wpi.first.networktables.NetworkTableInstance; +import edu.wpi.first.wpilibj.DriverStation; +import edu.wpi.first.wpilibj.Timer; +import edu.wpi.team190.gompeilib.core.utility.LimelightHelpers; +import edu.wpi.team190.gompeilib.subsystems.vision.VisionConstants; +import edu.wpi.team190.gompeilib.subsystems.vision.VisionConstants.MovingLimelightConfig; +import edu.wpi.team190.gompeilib.subsystems.vision.data.VisionPoseObservation; +import edu.wpi.team190.gompeilib.subsystems.vision.data.VisionSingleTxTyObservation; +import edu.wpi.team190.gompeilib.subsystems.vision.io.CameraIO; +import edu.wpi.team190.gompeilib.subsystems.vision.io.CameraIOLimelight; +import edu.wpi.team190.gompeilib.subsystems.vision.io.LimelightIOInputsAutoLogged; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.LongSupplier; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import lombok.Getter; +import org.littletonrobotics.junction.Logger; + +public class CameraMovingLimelight extends Camera { + private final LimelightIOInputsAutoLogged inputs; + private final CameraIOLimelight io; + + private final MovingLimelightConfig config; + @Getter private final String name; + + private final Supplier headingSupplier; + private final Supplier rotationAxisSupplier; + private final Supplier chassisSpeedsSupplier; + private final LongSupplier timestampSupplier; + private final DoubleArrayPublisher headingPublisher; + + @Getter private final List allTagPoses; + + private boolean wasEnabled; + private double enabledTimestamp; + + public CameraMovingLimelight( + CameraIOLimelight io, + MovingLimelightConfig config, + Supplier headingSupplier, + Supplier rotationAxisSupplier, + Supplier chassisSpeedsSupplier, + LongSupplier timestampSupplier, + List>> poseObservers, + List>> singleTxTyObservers) { + super(config.key(), poseObservers, new ArrayList<>(), singleTxTyObservers); + + inputs = new LimelightIOInputsAutoLogged(); + this.io = io; + + this.config = config; + this.name = "limelight-" + this.config.key(); + + this.headingSupplier = headingSupplier; + this.rotationAxisSupplier = rotationAxisSupplier; + this.chassisSpeedsSupplier = chassisSpeedsSupplier; + this.timestampSupplier = timestampSupplier; + this.headingPublisher = + NetworkTableInstance.getDefault() + .getTable(this.name) + .getDoubleArrayTopic("robot_orientation_set") + .publish(); + + allTagPoses = new ArrayList<>(); + + currentCameraPose = Pose3d.kZero.transformBy(config.robotToRotationAxisTransform()); + + LimelightHelpers.setCameraPose_RobotSpace( + name, + currentCameraPose.getX(), + -currentCameraPose.getY(), + currentCameraPose.getZ(), + currentCameraPose.getRotation().getMeasureX().in(Degrees), + currentCameraPose.getRotation().getMeasureY().in(Degrees), + currentCameraPose.getRotation().getMeasureZ().in(Degrees)); + + LimelightHelpers.SetIMUAssistAlpha(name, 0.0067); + LimelightHelpers.setRewindEnabled(name, config.enableRewind()); + + LimelightHelpers.SetIMUMode(name, 1); + LimelightHelpers.SetThrottle(name, 190); + + wasEnabled = false; + enabledTimestamp = Timer.getFPGATimestamp(); + } + + @Override + public void periodic() { + poseObservationList.clear(); + multiTxTyObservationList.clear(); + singleTxTyObservationList.clear(); + + currentCameraPose = + Pose3d.kZero + .transformBy(config.robotToRotationAxisTransform()) + .rotateAround( + currentCameraPose.getTranslation(), new Rotation3d(rotationAxisSupplier.get())) + .transformBy(config.rotationAxisToLensTransform()); + + if (DriverStation.isEnabled()) { + if (!wasEnabled) { + enabledTimestamp = Timer.getFPGATimestamp(); + wasEnabled = true; + LimelightHelpers.SetIMUMode(name, 0); + LimelightHelpers.SetThrottle(name, 0); + } + + if (Timer.getFPGATimestamp() - enabledTimestamp >= 165 && config.enableRewind()) { + LimelightHelpers.triggerRewindCapture(name, 165); + enabledTimestamp = Timer.getFPGATimestamp(); + } + } + + if (DriverStation.isDisabled()) { + if (wasEnabled) { + if (config.enableRewind()) { + LimelightHelpers.triggerRewindCapture(name, Timer.getFPGATimestamp() - enabledTimestamp); + } + wasEnabled = false; + LimelightHelpers.SetIMUMode(name, 1); + LimelightHelpers.SetThrottle(name, 190); + } + } + + headingPublisher.set( + new double[] { + -Units.radiansToDegrees(config.robotToRotationAxisTransform().getRotation().getZ()) + + rotationAxisSupplier.get().getDegrees() + + headingSupplier.get().getDegrees(), + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + }, + timestampSupplier.getAsLong()); + + io.updateInputs(inputs); + Logger.processInputs("Vision/Cameras/" + this.name, inputs); + + allTagPoses.clear(); + + double xyStdDev = config.megatagXYStdev(); + double thetaStdev = config.metatagThetaStdev(); + + if (inputs.mt1PoseEstimate.tagCount() != 0) { + xyStdDev = + config.megatagXYStdev() + * Math.pow( + inputs.mt1PoseEstimate.avgTagDist(), VisionConstants.XY_STDEV_DISTANCE_EXPONENT) + / Math.pow( + inputs.mt1PoseEstimate.tagCount(), VisionConstants.XY_STDEV_TAG_COUNT_EXPONENT); + thetaStdev = + inputs.mt1PoseEstimate.tagCount() > 1 + && Math.abs(chassisSpeedsSupplier.get().vxMetersPerSecond) <= 0.15 + && Math.abs(chassisSpeedsSupplier.get().vyMetersPerSecond) <= 0.15 + && Math.abs(chassisSpeedsSupplier.get().omegaRadiansPerSecond) <= 0.05 + && Arrays.stream(inputs.mt1PoseEstimate.rawFiducials()) + .mapToDouble(CameraIO.RawFiducial::ambiguity) + .average() + .orElse(Double.MAX_VALUE) + < VisionConstants.AMBIGUITY_THRESHOLD + ? config.metatagThetaStdev() + * Math.pow( + inputs.mt1PoseEstimate.avgTagDist(), + VisionConstants.XY_STDEV_DISTANCE_EXPONENT) + / Math.pow( + inputs.mt1PoseEstimate.tagCount(), + VisionConstants.XY_STDEV_TAG_COUNT_EXPONENT) + : Double.POSITIVE_INFINITY; + Pose2d tagPose = inputs.mt1PoseEstimate.pose(); + tagPose = + tagPose.rotateAround(tagPose.getTranslation(), rotationAxisSupplier.get().unaryMinus()); + Pose2d correctedRobotPose = + tagPose.transformBy( + new Transform2d( + config.rotationAxisToLensTransform().getTranslation().toTranslation2d(), + new Rotation2d())); + Logger.recordOutput("Vision/Cameras/" + this.name + "/MT1Pose", correctedRobotPose); + poseObservationList.add( + new VisionPoseObservation( + correctedRobotPose, + Arrays.stream(inputs.mt1PoseEstimate.rawFiducials()) + .map(CameraIO.RawFiducial::id) + .collect(Collectors.toSet()), + inputs.mt1PoseEstimate.timestampSeconds(), + VecBuilder.fill(xyStdDev, xyStdDev, thetaStdev))); + } + + if (inputs.mt2PoseEstimate.tagCount() != 0) { + xyStdDev = + config.megatag2XYStdev() + * Math.pow( + inputs.mt2PoseEstimate.avgTagDist(), VisionConstants.XY_STDEV_DISTANCE_EXPONENT) + / Math.pow( + inputs.mt2PoseEstimate.tagCount(), VisionConstants.XY_STDEV_TAG_COUNT_EXPONENT); + thetaStdev = Double.POSITIVE_INFINITY; + Pose2d tagPose = inputs.mt2PoseEstimate.pose(); + Pose2d limelightBelievedCameraPose = + new Pose3d().transformBy(config.robotToRotationAxisTransform()).toPose2d(); + Pose2d trueCameraPose = currentCameraPose.toPose2d(); + Transform2d correction = new Transform2d(limelightBelievedCameraPose, trueCameraPose); + Pose2d correctedRobotPose = tagPose.transformBy(correction.inverse()); + Logger.recordOutput("Vision/" + name + "/MT2Pose", correctedRobotPose); + poseObservationList.add( + new VisionPoseObservation( + correctedRobotPose, + Arrays.stream(inputs.mt2PoseEstimate.rawFiducials()) + .map(CameraIO.RawFiducial::id) + .collect(Collectors.toSet()), + inputs.mt2PoseEstimate.timestampSeconds(), + VecBuilder.fill(xyStdDev, xyStdDev, thetaStdev))); + } + + if (inputs.rawFiducials.length != 0) { + Arrays.stream(inputs.rawFiducials) + .forEach( + fiducial -> + singleTxTyObservationList.add( + new VisionSingleTxTyObservation( + fiducial.id(), + fiducial.txnc(), + fiducial.tync(), + fiducial.distToCamera(), + inputs.mt1PoseEstimate.timestampSeconds(), + currentCameraPose))); + } + + super.sendObservers(); + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/vision/camera/CameraStaticLimelight.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/vision/camera/CameraStaticLimelight.java new file mode 100644 index 00000000..461ac8b8 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/vision/camera/CameraStaticLimelight.java @@ -0,0 +1,220 @@ +package edu.wpi.team190.gompeilib.subsystems.vision.camera; + +import static edu.wpi.first.units.Units.Degrees; + +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.geometry.Pose3d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.kinematics.ChassisSpeeds; +import edu.wpi.first.networktables.DoubleArrayPublisher; +import edu.wpi.first.networktables.NetworkTableInstance; +import edu.wpi.first.wpilibj.DriverStation; +import edu.wpi.first.wpilibj.Timer; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import edu.wpi.team190.gompeilib.core.utility.LimelightHelpers; +import edu.wpi.team190.gompeilib.subsystems.vision.VisionConstants; +import edu.wpi.team190.gompeilib.subsystems.vision.VisionConstants.StaticLimelightConfig; +import edu.wpi.team190.gompeilib.subsystems.vision.data.VisionPoseObservation; +import edu.wpi.team190.gompeilib.subsystems.vision.data.VisionSingleTxTyObservation; +import edu.wpi.team190.gompeilib.subsystems.vision.io.CameraIO; +import edu.wpi.team190.gompeilib.subsystems.vision.io.CameraIOLimelight; +import edu.wpi.team190.gompeilib.subsystems.vision.io.LimelightIOInputsAutoLogged; +import java.util.*; +import java.util.function.Consumer; +import java.util.function.LongSupplier; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import lombok.Getter; +import org.littletonrobotics.junction.Logger; + +public class CameraStaticLimelight extends Camera { + private final LimelightIOInputsAutoLogged inputs; + private final CameraIOLimelight io; + + private final StaticLimelightConfig config; + @Getter private final String name; + + private final Supplier headingSupplier; + private final Supplier chassisSpeedsSupplier; + private final LongSupplier timestampSupplier; + private final DoubleArrayPublisher headingPublisher; + + @Getter private final List allTagPoses; + + private boolean wasEnabled; + private double enabledTimestamp; + + public CameraStaticLimelight( + CameraIOLimelight io, + StaticLimelightConfig config, + Supplier headingSupplier, + Supplier chassisSpeedsSupplier, + LongSupplier timestampSupplier, + List>> poseObservers, + List>> singleTxTyObservers) { + super(config.key(), poseObservers, new ArrayList<>(), singleTxTyObservers); + + inputs = new LimelightIOInputsAutoLogged(); + this.io = io; + + this.config = config; + this.name = "limelight-" + this.config.key(); + + this.headingSupplier = headingSupplier; + this.chassisSpeedsSupplier = chassisSpeedsSupplier; + this.timestampSupplier = timestampSupplier; + this.headingPublisher = + NetworkTableInstance.getDefault() + .getTable(this.name) + .getDoubleArrayTopic("robot_orientation_set") + .publish(); + + allTagPoses = new ArrayList<>(); + + currentCameraPose = + new Pose3d( + config.robotToCameraTransform().getTranslation(), + config.robotToCameraTransform().getRotation()); + + LimelightHelpers.setCameraPose_RobotSpace( + name, + currentCameraPose.getX(), + -currentCameraPose.getY(), + currentCameraPose.getZ(), + currentCameraPose.getRotation().getMeasureX().in(Degrees), + currentCameraPose.getRotation().getMeasureY().in(Degrees), + currentCameraPose.getRotation().getMeasureZ().in(Degrees)); + + LimelightHelpers.SetIMUAssistAlpha(name, 0.0067); + LimelightHelpers.setRewindEnabled(name, config.enableRewind()); + + LimelightHelpers.SetIMUMode(name, 1); + if (GompeiLib.isTuning()) { + LimelightHelpers.SetThrottle(name, 0); + } else { + LimelightHelpers.SetIMUMode(name, 190); + } + + wasEnabled = false; + enabledTimestamp = Timer.getFPGATimestamp(); + } + + @Override + public void periodic() { + poseObservationList.clear(); + multiTxTyObservationList.clear(); + singleTxTyObservationList.clear(); + + if (DriverStation.isEnabled()) { + if (!wasEnabled) { + enabledTimestamp = Timer.getFPGATimestamp(); + wasEnabled = true; + LimelightHelpers.SetIMUMode(name, 0); + LimelightHelpers.SetThrottle(name, 0); + } + + if (Timer.getFPGATimestamp() - enabledTimestamp >= 165 && config.enableRewind()) { + LimelightHelpers.triggerRewindCapture(name, 165); + enabledTimestamp = Timer.getFPGATimestamp(); + } + } + + if (DriverStation.isDisabled()) { + if (wasEnabled) { + if (config.enableRewind()) { + LimelightHelpers.triggerRewindCapture(name, Timer.getFPGATimestamp() - enabledTimestamp); + } + wasEnabled = false; + LimelightHelpers.SetIMUMode(name, 1); + if (GompeiLib.isTuning()) { + LimelightHelpers.SetThrottle(name, 0); + } else { + LimelightHelpers.SetIMUMode(name, 190); + } + } + } + + headingPublisher.set( + new double[] {headingSupplier.get().getDegrees(), 0.0, 0.0, 0.0, 0.0, 0.0}, + timestampSupplier.getAsLong()); + + io.updateInputs(inputs); + Logger.processInputs("Vision/Cameras/" + this.name, inputs); + + allTagPoses.clear(); + + double xyStdDev = config.megatagXYStdev(); + double thetaStdev = config.metatagThetaStdev(); + + if (inputs.mt1PoseEstimate.tagCount() != 0) { + xyStdDev = + config.megatagXYStdev() + * Math.pow( + inputs.mt1PoseEstimate.avgTagDist(), VisionConstants.XY_STDEV_DISTANCE_EXPONENT) + / Math.pow( + inputs.mt1PoseEstimate.tagCount(), VisionConstants.XY_STDEV_TAG_COUNT_EXPONENT); + thetaStdev = + inputs.mt1PoseEstimate.tagCount() > 1 + && Math.abs(chassisSpeedsSupplier.get().vxMetersPerSecond) <= 0.15 + && Math.abs(chassisSpeedsSupplier.get().vyMetersPerSecond) <= 0.15 + && Math.abs(chassisSpeedsSupplier.get().omegaRadiansPerSecond) <= 0.05 + && Arrays.stream(inputs.mt1PoseEstimate.rawFiducials()) + .mapToDouble(CameraIO.RawFiducial::ambiguity) + .average() + .orElse(Double.MAX_VALUE) + < VisionConstants.AMBIGUITY_THRESHOLD + ? config.metatagThetaStdev() + * Math.pow( + inputs.mt1PoseEstimate.avgTagDist(), + VisionConstants.XY_STDEV_DISTANCE_EXPONENT) + / Math.pow( + inputs.mt1PoseEstimate.tagCount(), + VisionConstants.XY_STDEV_TAG_COUNT_EXPONENT) + : Double.POSITIVE_INFINITY; + + poseObservationList.add( + new VisionPoseObservation( + inputs.mt1PoseEstimate.pose(), + Arrays.stream(inputs.mt1PoseEstimate.rawFiducials()) + .map(CameraIO.RawFiducial::id) + .collect(Collectors.toSet()), + inputs.mt1PoseEstimate.timestampSeconds(), + VecBuilder.fill(xyStdDev, xyStdDev, thetaStdev))); + } + + if (inputs.mt2PoseEstimate.tagCount() != 0) { + xyStdDev = + config.megatag2XYStdev() + * Math.pow( + inputs.mt2PoseEstimate.avgTagDist(), VisionConstants.XY_STDEV_DISTANCE_EXPONENT) + / Math.pow( + inputs.mt2PoseEstimate.tagCount(), VisionConstants.XY_STDEV_TAG_COUNT_EXPONENT); + thetaStdev = Double.POSITIVE_INFINITY; + + poseObservationList.add( + new VisionPoseObservation( + inputs.mt2PoseEstimate.pose(), + Arrays.stream(inputs.mt2PoseEstimate.rawFiducials()) + .map(CameraIO.RawFiducial::id) + .collect(Collectors.toSet()), + inputs.mt2PoseEstimate.timestampSeconds(), + VecBuilder.fill(xyStdDev, xyStdDev, thetaStdev))); + } + + if (inputs.rawFiducials.length != 0) { + Arrays.stream(inputs.rawFiducials) + .forEach( + fiducial -> + singleTxTyObservationList.add( + new VisionSingleTxTyObservation( + fiducial.id(), + fiducial.txnc(), + fiducial.tync(), + fiducial.distToCamera(), + inputs.mt1PoseEstimate.timestampSeconds(), + currentCameraPose))); + } + + super.sendObservers(); + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/vision/camera/CameraType.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/vision/camera/CameraType.java new file mode 100644 index 00000000..9970f11d --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/vision/camera/CameraType.java @@ -0,0 +1,120 @@ +package edu.wpi.team190.gompeilib.subsystems.vision.camera; + +import edu.wpi.first.math.util.Units; + +public enum CameraType { + LIMELIGHT_2_PLUS( + Limelight2PlusConstants.HORIZONTAL_FOV, + Limelight2PlusConstants.VERTICAL_FOV, + Limelight2PlusConstants.MEGATAG_XY_STANDARD_DEVIATION_COEFFICIENT, + Limelight2PlusConstants.MEGATAG_2_XY_STANDARD_DEVIATION_COEFFICIENT, + Limelight2PlusConstants.MEGATAG_THETA_STANDARD_DEVIATION_COEFFICIENT), + LIMELIGHT_3( + Limelight3Constants.HORIZONTAL_FOV, + Limelight3Constants.VERTICAL_FOV, + Limelight3Constants.MEGATAG_2_XY_STANDARD_DEVIATION_COEFFICIENT, + Limelight3Constants.MEGATAG_XY_STANDARD_DEVIATION_COEFFICIENT, + Limelight3Constants.MEGATAG_THETA_STANDARD_DEVIATION_COEFFICIENT), + LIMELIGHT_3G( + Limelight3GConstants.HORIZONTAL_FOV, + Limelight3GConstants.VERTICAL_FOV, + Limelight3GConstants.MEGATAG_2_XY_STANDARD_DEVIATION_COEFFICIENT, + Limelight3GConstants.MEGATAG_XY_STANDARD_DEVIATION_COEFFICIENT, + Limelight3GConstants.MEGATAG_THETA_STANDARD_DEVIATION_COEFFICIENT), + LIMELIGHT_4( + Limelight4Constants.HORIZONTAL_FOV, + Limelight4Constants.VERTICAL_FOV, + Limelight4Constants.MEGATAG_2_XY_STANDARD_DEVIATION_COEFFICIENT, + Limelight4Constants.MEGATAG_XY_STANDARD_DEVIATION_COEFFICIENT, + Limelight4Constants.MEGATAG_THETA_STANDARD_DEVIATION_COEFFICIENT), + THRIFTYCAM( + ThriftyCamConstants.HORIZONTAL_FOV, + ThriftyCamConstants.VERTICAL_FOV, + ThriftyCamConstants.SINGLETAG_XY_STANDARD_DEVIATION_COEFFICIENT, + ThriftyCamConstants.MULTITAG_XY_STANDARD_DEVIATION_COEFFICIENT, + ThriftyCamConstants.THETA_STANDARD_DEVIATION_COEFFICIENT), + DEFAULT(); + + public final double horizontalFOV; + public final double verticalFOV; + public final double primaryXYStandardDeviationCoefficient; + public final double secondaryXYStandardDeviationCoefficient; + public final double primaryThetaStandardDeviationCoefficient; + + private CameraType( + double horizontalFOV, + double verticalFOV, + double primaryXYStandardDeviationCoefficient, + double secondaryXYStandardDeviationCoefficient, + double primaryThetaStandardDeviationCoefficient) { + this.horizontalFOV = horizontalFOV; + this.verticalFOV = verticalFOV; + this.primaryXYStandardDeviationCoefficient = primaryXYStandardDeviationCoefficient; + this.secondaryXYStandardDeviationCoefficient = secondaryXYStandardDeviationCoefficient; + this.primaryThetaStandardDeviationCoefficient = primaryThetaStandardDeviationCoefficient; + } + + private CameraType( + double horizontalFOV, + double verticalFOV, + double xyStandardDeviationCoefficient, + double thetaStandardDeviationCoefficient) { + this.horizontalFOV = horizontalFOV; + this.verticalFOV = verticalFOV; + this.primaryXYStandardDeviationCoefficient = xyStandardDeviationCoefficient; + this.secondaryXYStandardDeviationCoefficient = xyStandardDeviationCoefficient; + this.primaryThetaStandardDeviationCoefficient = thetaStandardDeviationCoefficient; + } + + private CameraType() { + this.horizontalFOV = 0.0; + this.verticalFOV = 0.0; + this.primaryXYStandardDeviationCoefficient = 0.0; + this.secondaryXYStandardDeviationCoefficient = 0.0; + this.primaryThetaStandardDeviationCoefficient = 0.0; + } + + public static final double BLINK_TIME = 0.067; + + private static class Limelight2PlusConstants { + private static final double HORIZONTAL_FOV = Units.degreesToRadians(62.5); + private static final double VERTICAL_FOV = Units.degreesToRadians(48.9); + private static final double MEGATAG_XY_STANDARD_DEVIATION_COEFFICIENT = 0.1; + private static final double MEGATAG_THETA_STANDARD_DEVIATION_COEFFICIENT = 0.1; + private static final double MEGATAG_2_XY_STANDARD_DEVIATION_COEFFICIENT = 0.1; + } + + private static class Limelight3Constants { + private static final double HORIZONTAL_FOV = Units.degreesToRadians(62.5); + private static final double VERTICAL_FOV = Units.degreesToRadians(48.9); + private static final double MEGATAG_XY_STANDARD_DEVIATION_COEFFICIENT = 0.1; + private static final double MEGATAG_THETA_STANDARD_DEVIATION_COEFFICIENT = 0.1; + private static final double MEGATAG_2_XY_STANDARD_DEVIATION_COEFFICIENT = 0.1; + } + + private static class Limelight3GConstants { + private static final double HORIZONTAL_FOV = Units.degreesToRadians(82.0); + private static final double VERTICAL_FOV = Units.degreesToRadians(46.2); + private static final double MEGATAG_XY_STANDARD_DEVIATION_COEFFICIENT = 0.05; + private static final double MEGATAG_THETA_STANDARD_DEVIATION_COEFFICIENT = 0.1; + private static final double MEGATAG_2_XY_STANDARD_DEVIATION_COEFFICIENT = 0.00015; + } + + private static class Limelight4Constants { + private static final double HORIZONTAL_FOV = Units.degreesToRadians(82.0); + private static final double VERTICAL_FOV = Units.degreesToRadians(46.2); + private static final double MEGATAG_XY_STANDARD_DEVIATION_COEFFICIENT = 0.05; + private static final double MEGATAG_THETA_STANDARD_DEVIATION_COEFFICIENT = 10; + private static final double MEGATAG_2_XY_STANDARD_DEVIATION_COEFFICIENT = 0.00015; + } + + private static class ThriftyCamConstants { + private static final double HORIZONTAL_FOV = Units.degreesToRadians(82.0); + private static final double VERTICAL_FOV = Units.degreesToRadians(46.2); + private static final double SINGLETAG_XY_STANDARD_DEVIATION_COEFFICIENT = 0.05; + private static final double THETA_STANDARD_DEVIATION_COEFFICIENT = 0.1; + private static final double MULTITAG_XY_STANDARD_DEVIATION_COEFFICIENT = 0.00015; + private static final int WIDTH = 1600; + private static final int HEIGHT = 1304; + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/vision/data/VisionMultiTxTyObservation.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/vision/data/VisionMultiTxTyObservation.java new file mode 100644 index 00000000..dc0db831 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/vision/data/VisionMultiTxTyObservation.java @@ -0,0 +1,6 @@ +package edu.wpi.team190.gompeilib.subsystems.vision.data; + +import edu.wpi.first.math.geometry.Pose3d; + +public record VisionMultiTxTyObservation( + int tagId, double[] tx, double[] ty, double distance, double timestamp, Pose3d cameraPose) {} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/vision/data/VisionPoseObservation.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/vision/data/VisionPoseObservation.java new file mode 100644 index 00000000..5aa31e2d --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/vision/data/VisionPoseObservation.java @@ -0,0 +1,10 @@ +package edu.wpi.team190.gompeilib.subsystems.vision.data; + +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.numbers.N1; +import edu.wpi.first.math.numbers.N3; +import java.util.Set; + +public record VisionPoseObservation( + Pose2d pose, Set tagIds, double timestamp, Matrix stddevs) {} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/vision/data/VisionSingleTxTyObservation.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/vision/data/VisionSingleTxTyObservation.java new file mode 100644 index 00000000..a4dcb206 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/vision/data/VisionSingleTxTyObservation.java @@ -0,0 +1,6 @@ +package edu.wpi.team190.gompeilib.subsystems.vision.data; + +import edu.wpi.first.math.geometry.Pose3d; + +public record VisionSingleTxTyObservation( + int tagId, double tx, double ty, double distance, double timestamp, Pose3d cameraPose) {} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/vision/io/CameraIO.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/vision/io/CameraIO.java new file mode 100644 index 00000000..00253872 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/vision/io/CameraIO.java @@ -0,0 +1,110 @@ +package edu.wpi.team190.gompeilib.subsystems.vision.io; + +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.team190.gompeilib.core.utility.LimelightHelpers; +import edu.wpi.team190.gompeilib.subsystems.vision.camera.Camera; +import java.util.Arrays; +import org.littletonrobotics.junction.AutoLog; + +/** + * An interface for the hardware implementation of a {@link Camera Camera}. Contains the methods + * necessary for providing information to a camera, like its pipeline, and getting information out + * of a camera, like its individual robot pose estimate. + */ +public interface CameraIO { + + @AutoLog + public static class GompeiVisionIOInputs { + public double[] timestamps = new double[] {}; + public double[][] frames = new double[][] {}; + public double captureFPS = 0; + public double processingFPS = 0; + } + + @AutoLog + public static class LimelightIOInputs { + public PoseEstimate mt1PoseEstimate = new PoseEstimate(); + public PoseEstimate mt2PoseEstimate = new PoseEstimate(); + public RawFiducial[] rawFiducials = {}; + } + + public default void updateInputs(GompeiVisionIOInputs inputs) {} + + public default void updateInputs(LimelightIOInputs inputs) {} + + public default String getName() { + return ""; + } + + public record PoseEstimate( + Pose2d pose, + double timestampSeconds, + double latency, + int tagCount, + double tagSpan, + double avgTagDist, + double avgTagArea, + RawFiducial[] rawFiducials, + boolean isMegaTag2) { + public PoseEstimate() { + this(Pose2d.kZero, 0, 0, 0, 0, 0, 0, new RawFiducial[] {}, false); + } + + public PoseEstimate(LimelightHelpers.PoseEstimate poseEstimate) { + this( + poseEstimate.pose, + poseEstimate.timestampSeconds, + poseEstimate.latency, + poseEstimate.tagCount, + poseEstimate.tagSpan, + poseEstimate.avgTagDist, + poseEstimate.avgTagArea, + Arrays.stream(poseEstimate.rawFiducials) + .map(RawFiducial::new) + .toArray(RawFiducial[]::new), + poseEstimate.isMegaTag2); + } + } + + public record RawFiducial( + int id, + double txnc, + double tync, + double ta, + double distToCamera, + double distToRobot, + double ambiguity) { + public RawFiducial() { + this(0, 0, 0, 0, 0, 0, 0); + } + + public RawFiducial(LimelightHelpers.RawFiducial rawFiducial) { + this( + rawFiducial.id, + rawFiducial.txnc, + rawFiducial.tync, + rawFiducial.ta, + rawFiducial.distToCamera, + rawFiducial.distToRobot, + rawFiducial.ambiguity); + } + } + + public record RawDetection( + int classId, + double txnc, + double tync, + double ta, + double corner0_X, + double corner0_Y, + double corner1_X, + double corner1_Y, + double corner2_X, + double corner2_Y, + double corner3_X, + double corner3_Y) { + public RawDetection() { + this(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + } + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/vision/io/CameraIOGompeiVision.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/vision/io/CameraIOGompeiVision.java new file mode 100644 index 00000000..f661f1dc --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/vision/io/CameraIOGompeiVision.java @@ -0,0 +1,73 @@ +package edu.wpi.team190.gompeilib.subsystems.vision.io; + +import edu.wpi.first.math.util.Units; +import edu.wpi.first.networktables.*; +import edu.wpi.team190.gompeilib.subsystems.vision.VisionConstants; +import java.util.*; +import lombok.Getter; + +public class CameraIOGompeiVision implements CameraIO { + @Getter private final String name; + + private final DoubleArraySubscriber observationSubscriber; + private final IntegerSubscriber captureFPSAprilTagSubscriber; + private final IntegerSubscriber processingFPSAprilTagSubscriber; + + public CameraIOGompeiVision(VisionConstants.GompeiVisionConfig config) { + + this.name = config.key(); + + NetworkTable configTable = + NetworkTableInstance.getDefault() + .getTable("cameras") + .getSubTable(this.name) + .getSubTable("config"); + NetworkTable outputTable = + NetworkTableInstance.getDefault() + .getTable("cameras") + .getSubTable(this.name) + .getSubTable("output"); + + String deviceId = config.key(); + + configTable.getStringTopic("role").publish().set(name); + configTable.getStringTopic("hardware_id").publish().set(deviceId); + configTable.getDoubleArrayTopic("camera_matrix").publish().set(config.cameraMatrix().getData()); + configTable + .getDoubleArrayTopic("distortion_coefficients") + .publish() + .set(config.distortionCoefficients().getData()); + configTable.getDoubleTopic("exposure").publish().set(config.exposure()); + configTable.getDoubleTopic("gain").publish().set(config.gain()); + configTable.getIntegerTopic("width").publish().set(config.width()); + configTable.getIntegerTopic("height").publish().set(config.height()); + configTable.getDoubleTopic("fiducial_size_m").publish().set(Units.inchesToMeters(6.50)); + configTable.getBooleanTopic("setup_mode").publish().set(false); + + this.observationSubscriber = + outputTable + .getDoubleArrayTopic("observations") + .subscribe( + new double[] {}, + PubSubOption.keepDuplicates(true), + PubSubOption.sendAll(true), + PubSubOption.pollStorage(5), + PubSubOption.periodic(0.01)); + this.captureFPSAprilTagSubscriber = outputTable.getIntegerTopic("capture_fps").subscribe(0); + this.processingFPSAprilTagSubscriber = + outputTable.getIntegerTopic("processing_fps").subscribe(0); + } + + @Override + public void updateInputs(GompeiVisionIOInputs inputs) { + var aprilTagQueue = observationSubscriber.readQueue(); + inputs.timestamps = new double[aprilTagQueue.length]; + inputs.frames = new double[aprilTagQueue.length][]; + for (int i = 0; i < aprilTagQueue.length; i++) { + inputs.timestamps[i] = aprilTagQueue[i].timestamp / 1000000.0; + inputs.frames[i] = aprilTagQueue[i].value; + } + inputs.captureFPS = captureFPSAprilTagSubscriber.get(); + inputs.processingFPS = processingFPSAprilTagSubscriber.get(); + } +} diff --git a/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/vision/io/CameraIOLimelight.java b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/vision/io/CameraIOLimelight.java new file mode 100644 index 00000000..577166b5 --- /dev/null +++ b/lib/src/main/java/edu/wpi/team190/gompeilib/subsystems/vision/io/CameraIOLimelight.java @@ -0,0 +1,29 @@ +package edu.wpi.team190.gompeilib.subsystems.vision.io; + +import edu.wpi.team190.gompeilib.core.utility.LimelightHelpers; +import edu.wpi.team190.gompeilib.subsystems.vision.VisionConstants; +import java.util.Arrays; +import lombok.Getter; + +public class CameraIOLimelight implements CameraIO { + @Getter private final String name; + + public CameraIOLimelight(VisionConstants.StaticLimelightConfig config) { + this.name = "limelight-" + config.key(); + } + + public CameraIOLimelight(VisionConstants.MovingLimelightConfig config) { + this.name = "limelight-" + config.key(); + } + + @Override + public void updateInputs(LimelightIOInputs inputs) { + inputs.mt1PoseEstimate = new PoseEstimate(LimelightHelpers.getBotPoseEstimate_wpiBlue(name)); + inputs.mt2PoseEstimate = + new PoseEstimate(LimelightHelpers.getBotPoseEstimate_wpiBlue_MegaTag2(name)); + inputs.rawFiducials = + Arrays.stream(LimelightHelpers.getRawFiducials(name)) + .map(RawFiducial::new) + .toArray(RawFiducial[]::new); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/core/GompeiLibTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/core/GompeiLibTest.java new file mode 100644 index 00000000..9b73566d --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/core/GompeiLibTest.java @@ -0,0 +1,140 @@ +package edu.wpi.team190.gompeilib.core; + +import static org.junit.jupiter.api.Assertions.*; + +import edu.wpi.team190.gompeilib.core.robot.RobotMode; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.List; +import lombok.Builder; +import org.junit.jupiter.api.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.FieldSource; + +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class GompeiLibTest { + + private static final List TEST_PARAMETERS = + List.of( + GompeiLibTestParameters.builder() + .withMode(RobotMode.REAL) + .withIsTuning(true) + .withLoopPeriodSecs(0.02) + .build(), + GompeiLibTestParameters.builder() + .withMode(RobotMode.REAL) + .withIsTuning(false) + .withLoopPeriodSecs(0.02) + .build(), + GompeiLibTestParameters.builder() + .withMode(RobotMode.SIM) + .withIsTuning(true) + .withLoopPeriodSecs(0.02) + .build(), + GompeiLibTestParameters.builder() + .withMode(RobotMode.SIM) + .withIsTuning(false) + .withLoopPeriodSecs(0.02) + .build(), + GompeiLibTestParameters.builder() + .withMode(RobotMode.REPLAY) + .withIsTuning(true) + .withLoopPeriodSecs(0.02) + .build(), + GompeiLibTestParameters.builder() + .withMode(RobotMode.REPLAY) + .withIsTuning(false) + .withLoopPeriodSecs(0.02) + .build()); + + @BeforeEach + void resetGompeiLib() { + GompeiLib.deinit(); + } + + @Test + @Order(1) + void testConstruction() { + new GompeiLib(); + } + + @Test + @Order(2) + void testInitializationWhenNotInitialized() throws Exception { + Method checkInitializedMethod = GompeiLib.class.getDeclaredMethod("checkInitialized"); + checkInitializedMethod.setAccessible(true); + InvocationTargetException ex = + assertThrows( + java.lang.reflect.InvocationTargetException.class, + () -> checkInitializedMethod.invoke(null)); + + // Make sure the cause is IllegalStateException + assertInstanceOf(IllegalStateException.class, ex.getCause()); + } + + @Test + @Order(3) + void testInitializationWhenAlreadyInitialized() { + // Initialize once + GompeiLib.init(RobotMode.SIM, true, 0.02); + + // Capture System.err output + ByteArrayOutputStream errContent = new ByteArrayOutputStream(); + PrintStream originalErr = System.err; + System.setErr(new PrintStream(errContent)); + + // Call init again -> should hit the branch + GompeiLib.init(RobotMode.SIM, true, 0.02); + + // Restore System.err + System.setErr(originalErr); + + // Check that the correct message was printed + String output = errContent.toString(); + assertTrue(output.contains("GompeiLib has already been initialized!")); + } + + @Test + @Order(4) + void testDeinitializationFlow() { + GompeiLib.init(RobotMode.SIM, true, 0.02); + GompeiLib.deinit(); + + // The getters should throw IllegalStateException now because we are deinitialized + assertThrows(IllegalStateException.class, GompeiLib::getMode); + } + + @Test + @Order(5) + void testDoubleDeinitWarning() { + GompeiLib.deinit(); // Ensure it is already deinitialized + + ByteArrayOutputStream errContent = new ByteArrayOutputStream(); + PrintStream originalErr = System.err; + System.setErr(new PrintStream(errContent)); + + try { + GompeiLib.deinit(); // Call it while the stream is redirected + } finally { + System.setErr(originalErr); // Always restore in a finally block for safety + } + + assertTrue(errContent.toString().contains("GompeiLib has already been deinitialized!")); + } + + @ParameterizedTest + @FieldSource("TEST_PARAMETERS") + @Order(6) + void testGompeiLib(GompeiLibTestParameters params) { + GompeiLib.init(params.mode, params.isTuning, params.loopPeriodSecs); + + assertEquals(GompeiLib.getMode(), params.mode); + assertEquals(GompeiLib.isTuning(), params.isTuning); + assertEquals(GompeiLib.getLoopPeriod(), params.loopPeriodSecs); + } + + @Builder(setterPrefix = "with") + private record GompeiLibTestParameters(RobotMode mode, boolean isTuning, double loopPeriodSecs) {} +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/core/io/inertial/GyroIOPigeon2Test.java b/lib/src/test/java/edu/wpi/team190/gompeilib/core/io/inertial/GyroIOPigeon2Test.java new file mode 100644 index 00000000..416aa141 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/core/io/inertial/GyroIOPigeon2Test.java @@ -0,0 +1,150 @@ +package edu.wpi.team190.gompeilib.core.io.inertial; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import com.ctre.phoenix6.BaseStatusSignal; +import com.ctre.phoenix6.CANBus; +import com.ctre.phoenix6.StatusSignal; +import com.ctre.phoenix6.configs.Pigeon2Configurator; +import com.ctre.phoenix6.hardware.Pigeon2; +import com.ctre.phoenix6.swerve.SwerveModuleConstants; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.units.measure.Angle; +import edu.wpi.first.units.measure.AngularVelocity; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import edu.wpi.team190.gompeilib.core.io.components.inertial.GyroIO; +import edu.wpi.team190.gompeilib.core.io.components.inertial.GyroIOPigeon2; +import edu.wpi.team190.gompeilib.core.robot.RobotMode; +import edu.wpi.team190.gompeilib.subsystems.drivebases.swervedrive.SwerveDriveConstants; +import java.util.ArrayDeque; +import java.util.Queue; +import java.util.concurrent.atomic.AtomicLong; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockedConstruction; +import org.mockito.MockedStatic; + +public class GyroIOPigeon2Test { + @BeforeEach + public void setUp() { + GompeiLib.deinit(); + GompeiLib.init(RobotMode.SIM, false, 0.02); + } + + @Test + public void testGyroIOPigeon2() { + SwerveDriveConstants driveConstants = mock(SwerveDriveConstants.class); + CANBus canBus = mock(CANBus.class); + when(canBus.isNetworkFD()).thenReturn(false); + + DCMotor motor = mock(DCMotor.class); + SwerveModuleConstants moduleConst = mock(SwerveModuleConstants.class); + SwerveModuleConstants.ClosedLoopOutputType outputType = + SwerveModuleConstants.ClosedLoopOutputType.Voltage; + + SwerveDriveConstants.DriveConfig driveConfig = + new SwerveDriveConstants.DriveConfig( + canBus, + 1, // pigeon2Id + 4.0, // maxLinearVelocityMetersPerSecond + 0.05, // wheelRadiusMeters + motor, + motor, + moduleConst, + moduleConst, + moduleConst, + moduleConst, + outputType, + outputType, + 0.8, // bumperWidth + 0.8, // bumperLength + 40.0, // robotMassKilograms + 0.6, // trackWidth + 2.0, // robotMOI + 40.0, // moduleCurrentLimit + 1.0 // wheelCOF + ); + + try { + java.lang.reflect.Field configField = + SwerveDriveConstants.class.getDeclaredField("driveConfig"); + configField.setAccessible(true); + configField.set(driveConstants, driveConfig); + + java.lang.reflect.Field freqField = + SwerveDriveConstants.class.getDeclaredField("odometryFrequency"); + freqField.setAccessible(true); + freqField.set(driveConstants, 100.0); + } catch (Exception e) { + fail(e); + } + + Pigeon2Configurator configurator = mock(Pigeon2Configurator.class); + StatusSignal yaw = mock(StatusSignal.class); + StatusSignal yawVelocity = mock(StatusSignal.class); + StatusSignal pitch = mock(StatusSignal.class); + StatusSignal pitchVelocity = mock(StatusSignal.class); + StatusSignal roll = mock(StatusSignal.class); + StatusSignal rollVelocity = mock(StatusSignal.class); + + when(yaw.getValueAsDouble()).thenReturn(10.0); + when(yawVelocity.getValueAsDouble()).thenReturn(1.5); + when(pitch.getValue()).thenReturn(edu.wpi.first.units.Units.Degrees.of(5.0)); + when(pitchVelocity.getValue()).thenReturn(edu.wpi.first.units.Units.DegreesPerSecond.of(0.2)); + when(roll.getValue()).thenReturn(edu.wpi.first.units.Units.Degrees.of(-3.0)); + when(rollVelocity.getValue()).thenReturn(edu.wpi.first.units.Units.DegreesPerSecond.of(-0.1)); + + AtomicLong timestampInput = new AtomicLong(0); + + try (MockedConstruction mockPigeon = + mockConstruction( + Pigeon2.class, + (mock, context) -> { + when(mock.getConfigurator()).thenReturn(configurator); + when(mock.getYaw()).thenReturn(yaw); + when(mock.getAngularVelocityZWorld()).thenReturn(yawVelocity); + when(mock.getPitch()).thenReturn(pitch); + when(mock.getAngularVelocityXWorld()).thenReturn(pitchVelocity); + when(mock.getRoll()).thenReturn(roll); + when(mock.getAngularVelocityYWorld()).thenReturn(rollVelocity); + }); + // MockedStatic mockNt = mockStatic(NetworkTablesJNI.class); + MockedStatic mockBss = mockStatic(BaseStatusSignal.class)) { + + // mockNt.when(() -> NetworkTablesJNI.now()).thenReturn(123456789L); + mockBss + .when(() -> BaseStatusSignal.isAllGood(any(BaseStatusSignal[].class))) + .thenReturn(true); + mockBss + .when( + () -> + BaseStatusSignal.setUpdateFrequencyForAll( + anyDouble(), any(BaseStatusSignal[].class))) + .thenReturn(null); + + GyroIOPigeon2 gyroIO = new GyroIOPigeon2(driveConstants, timestampInput::set); + + assertEquals(yaw, gyroIO.getYaw()); + assertEquals(pitch, gyroIO.getPitch()); + assertEquals(roll, gyroIO.getRoll()); + + GyroIO.GyroIOInputs inputs = new GyroIO.GyroIOInputs(); + Queue yawTimestampQueue = new ArrayDeque<>(); + Queue yawPositionQueue = new ArrayDeque<>(); + + yawTimestampQueue.add(1.0); + yawPositionQueue.add(15.0); + + gyroIO.updateInputs(inputs, yawTimestampQueue, yawPositionQueue); + + assertTrue(timestampInput.get() > 0); + assertEquals(Rotation2d.fromDegrees(10.0), inputs.yawPosition); + assertEquals(1, inputs.odometryYawTimestamps.length); + assertEquals(1.0, inputs.odometryYawTimestamps[0]); + assertEquals(1, inputs.odometryYawPositions.length); + assertEquals(Rotation2d.fromDegrees(15.0), inputs.odometryYawPositions[0]); + } + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/core/io/inertial/GyroIOTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/core/io/inertial/GyroIOTest.java new file mode 100644 index 00000000..87a38a44 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/core/io/inertial/GyroIOTest.java @@ -0,0 +1,32 @@ +package edu.wpi.team190.gompeilib.core.io.inertial; + +import static org.junit.jupiter.api.Assertions.*; + +import edu.wpi.team190.gompeilib.core.io.components.inertial.GyroIO; +import org.junit.jupiter.api.Test; + +public class GyroIOTest { + @Test + public void testGyroIODefaults() { + GyroIO gyro = new GyroIO() {}; // Anonymous implementation + GyroIO.GyroIOInputs inputs = new GyroIO.GyroIOInputs(); + + gyro.updateInputs(inputs); + gyro.updateInputs(inputs, null, null); + + assertNull(gyro.getYaw()); + assertNull(gyro.getRoll()); + assertNull(gyro.getPitch()); + + // Inputs defaults + assertFalse(inputs.connected); + assertNotNull(inputs.yawPosition); + assertEquals(0, inputs.odometryYawTimestamps.length); + assertEquals(0, inputs.odometryYawPositions.length); + assertEquals(0.0, inputs.yawVelocityRadPerSec); + assertNotNull(inputs.pitchPosition); + assertNotNull(inputs.pitchVelocity); + assertNotNull(inputs.rollPosition); + assertNotNull(inputs.rollVelocity); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/core/logging/TraceTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/core/logging/TraceTest.java new file mode 100644 index 00000000..422e2fa4 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/core/logging/TraceTest.java @@ -0,0 +1,61 @@ +package edu.wpi.team190.gompeilib.core.logging; + +import static org.junit.jupiter.api.Assertions.*; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Method; +import org.junit.jupiter.api.Test; + +class TraceTest { + + @Trace + void annotatedMethod() {} + + void nonAnnotatedMethod() {} + + @Test + void testRetentionPolicyIsRuntime() { + Retention retention = Trace.class.getAnnotation(Retention.class); + assertNotNull(retention); + assertEquals(RetentionPolicy.RUNTIME, retention.value()); + } + + @Test + void testTargetIsMethod() { + Target target = Trace.class.getAnnotation(Target.class); + assertNotNull(target); + + boolean supportsMethod = false; + for (ElementType type : target.value()) { + if (type == ElementType.METHOD) { + supportsMethod = true; + break; + } + } + + assertTrue(supportsMethod); + } + + @Test + void testAnnotationPresentOnMethod() throws Exception { + Method method = TraceTest.class.getDeclaredMethod("annotatedMethod"); + + assertTrue(method.isAnnotationPresent(Trace.class)); + } + + @Test + void testAnnotationAbsentWhenNotDeclared() throws Exception { + Method method = TraceTest.class.getDeclaredMethod("nonAnnotatedMethod"); + + assertFalse(method.isAnnotationPresent(Trace.class)); + } + + @Test + void testAnnotationMetadataAccessible() { + assertNotNull(Trace.class.getAnnotation(Retention.class)); + assertNotNull(Trace.class.getAnnotation(Target.class)); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/core/robot/RobotContainerTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/core/robot/RobotContainerTest.java new file mode 100644 index 00000000..75cfb2c4 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/core/robot/RobotContainerTest.java @@ -0,0 +1,32 @@ +package edu.wpi.team190.gompeilib.core.robot; + +import static org.junit.jupiter.api.Assertions.*; + +import edu.wpi.first.wpilibj2.command.Command; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +class RobotContainerTest { + + /** Minimal concrete implementation of RobotContainer for testing. */ + static class TestContainer implements RobotContainer {} + + @Test + void testGetAutonomousCommand() { + RobotContainer container = Mockito.mock(TestContainer.class, Mockito.CALLS_REAL_METHODS); + + // Call the default method + Command cmd = container.getAutonomousCommand(); + + // Ensure it returns something (mocked default Commands.none() safe) + assertNotNull(cmd); + } + + @Test + void testRobotPeriodic() { + RobotContainer container = Mockito.mock(TestContainer.class, Mockito.CALLS_REAL_METHODS); + + // Call the default method + container.robotPeriodic(); // just runs, should not throw + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/core/robot/RobotModeTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/core/robot/RobotModeTest.java new file mode 100644 index 00000000..33bd6679 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/core/robot/RobotModeTest.java @@ -0,0 +1,20 @@ +package edu.wpi.team190.gompeilib.core.robot; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +class RobotModeTest { + + @Test + void testEnumValues() { + // Ensure all enum constants exist + RobotMode[] modes = RobotMode.values(); + + assertEquals(3, modes.length, "There should be exactly 3 modes"); + + assertEquals(RobotMode.REAL, RobotMode.valueOf("REAL")); + assertEquals(RobotMode.SIM, RobotMode.valueOf("SIM")); + assertEquals(RobotMode.REPLAY, RobotMode.valueOf("REPLAY")); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/core/robot/RobotStateTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/core/robot/RobotStateTest.java new file mode 100644 index 00000000..f0bef720 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/core/robot/RobotStateTest.java @@ -0,0 +1,19 @@ +package edu.wpi.team190.gompeilib.core.robot; + +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +class RobotStateTest { + + /** Minimal concrete implementation of RobotState for testing. */ + static class TestState implements RobotState {} + + @Test + void testPeriodic() { + // Use Mockito to safely call default method + RobotState state = Mockito.mock(TestState.class, Mockito.CALLS_REAL_METHODS); + + // Call the default method + state.periodic(); // just runs, should not throw + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/core/state/localization/EstimationRegionTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/core/state/localization/EstimationRegionTest.java new file mode 100644 index 00000000..8eaf3db3 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/core/state/localization/EstimationRegionTest.java @@ -0,0 +1,112 @@ +package edu.wpi.team190.gompeilib.core.state.localization; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import edu.wpi.first.apriltag.AprilTag; +import edu.wpi.first.math.geometry.*; +import edu.wpi.first.math.kinematics.SwerveDriveKinematics; +import edu.wpi.first.math.kinematics.SwerveModulePosition; +import edu.wpi.team190.gompeilib.subsystems.vision.data.VisionMultiTxTyObservation; +import edu.wpi.team190.gompeilib.subsystems.vision.data.VisionPoseObservation; +import java.util.Optional; +import java.util.Set; +import org.junit.jupiter.api.Test; + +public class EstimationRegionTest { + @Test + public void testEstimationRegion() { + AprilTag tag1 = new AprilTag(1, new Pose3d(1.0, 2.0, 3.0, new Rotation3d())); + AprilTag tag2 = new AprilTag(2, new Pose3d(4.0, 5.0, 6.0, new Rotation3d())); + + SwerveDriveKinematics kinematics = + new SwerveDriveKinematics( + new Translation2d(0.5, 0.5), + new Translation2d(0.5, -0.5), + new Translation2d(-0.5, 0.5), + new Translation2d(-0.5, -0.5)); + + EstimationRegion region = new EstimationRegion(Set.of(tag1, tag2), kinematics); + + assertNotNull(region.getAprilTags()); + assertEquals(2, region.getAprilTags().size()); + assertEquals(new Pose3d(1.0, 2.0, 3.0, new Rotation3d()), region.getAprilTags().get(1)); + + region.resetPose(new Pose2d(1.0, 2.0, new Rotation2d())); + assertEquals(new Pose2d(1.0, 2.0, new Rotation2d()), region.getEstimatedPose()); + + SwerveModulePosition[] modulePositions = + new SwerveModulePosition[] { + new SwerveModulePosition(0.0, new Rotation2d()), + new SwerveModulePosition(0.0, new Rotation2d()), + new SwerveModulePosition(0.0, new Rotation2d()), + new SwerveModulePosition(0.0, new Rotation2d()) + }; + region.addOdometryObservation(1.0, new Rotation2d(), modulePositions); + + VisionPoseObservation poseObs = + new VisionPoseObservation( + new Pose2d(1.1, 2.1, new Rotation2d()), + Set.of(1), + 1.1, + edu.wpi.first.math.VecBuilder.fill(0.1, 0.1, 0.1)); + region.addPoseObservation(poseObs); + + // Test addTxTyObservation + VisionMultiTxTyObservation txTyObs = + new VisionMultiTxTyObservation( + 1, + new double[] {0.1, 0.1, 0.1, 0.1}, + new double[] {0.2, 0.2, 0.2, 0.2}, + 2.0, + 1.2, + new Pose3d(1.0, 2.0, 0.5, new Rotation3d())); + region.addTxTyObservation(txTyObs); + + // Test addTxTyObservation with empty sample (timestamp far in past) + VisionMultiTxTyObservation txTyObsPast = + new VisionMultiTxTyObservation( + 1, + new double[] {0.1, 0.1, 0.1, 0.1}, + new double[] {0.2, 0.2, 0.2, 0.2}, + 2.0, + -100.0, + new Pose3d(1.0, 2.0, 0.5, new Rotation3d())); + region.addTxTyObservation(txTyObsPast); + + // Test addTxTyObservation with missing tag (tag ID 99 is not in the region) + VisionMultiTxTyObservation txTyObsMissingTag = + new VisionMultiTxTyObservation( + 99, + new double[] {0.1, 0.1, 0.1, 0.1}, + new double[] {0.2, 0.2, 0.2, 0.2}, + 2.0, + 1.2, + new Pose3d(1.0, 2.0, 0.5, new Rotation3d())); + region.addTxTyObservation(txTyObsMissingTag); + + // Test addTxTyObservation with empty sample using Mockito/reflection to force isEmpty() return + // path + try { + edu.wpi.first.math.estimator.SwerveDrivePoseEstimator mockEstimator = + mock(edu.wpi.first.math.estimator.SwerveDrivePoseEstimator.class); + when(mockEstimator.sampleAt(anyDouble())).thenReturn(Optional.empty()); + + java.lang.reflect.Field field = EstimationRegion.class.getDeclaredField("poseEstimator"); + field.setAccessible(true); + field.set(region, mockEstimator); + + VisionMultiTxTyObservation txTyObsMockedEmpty = + new VisionMultiTxTyObservation( + 1, + new double[] {0.1, 0.1, 0.1, 0.1}, + new double[] {0.2, 0.2, 0.2, 0.2}, + 2.0, + 1.2, + new Pose3d(1.0, 2.0, 0.5, new Rotation3d())); + region.addTxTyObservation(txTyObsMockedEmpty); + } catch (Exception e) { + fail(e); + } + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/core/state/localization/FieldZoneTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/core/state/localization/FieldZoneTest.java new file mode 100644 index 00000000..068a31c8 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/core/state/localization/FieldZoneTest.java @@ -0,0 +1,17 @@ +package edu.wpi.team190.gompeilib.core.state.localization; + +import static org.junit.jupiter.api.Assertions.*; + +import edu.wpi.first.apriltag.AprilTag; +import edu.wpi.first.math.geometry.Pose3d; +import java.util.Set; +import org.junit.jupiter.api.Test; + +public class FieldZoneTest { + @Test + public void testFieldZone() { + AprilTag tag = new AprilTag(1, new Pose3d()); + FieldZone zone = new FieldZone(Set.of(tag)); + assertEquals(Set.of(tag), zone.aprilTags()); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/core/state/localization/LocalizationConstantsTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/core/state/localization/LocalizationConstantsTest.java new file mode 100644 index 00000000..9a2466e5 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/core/state/localization/LocalizationConstantsTest.java @@ -0,0 +1,14 @@ +package edu.wpi.team190.gompeilib.core.state.localization; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +public class LocalizationConstantsTest { + @Test + public void testConstants() { + new LocalizationConstants(); // Cover constructor + assertEquals(0.1, LocalizationConstants.XY_STDDEV_COEFFICIENT); + assertEquals(1.2, LocalizationConstants.XY_STDDEV_DISTANCE_EXPONENT); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/core/state/localization/LocalizationTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/core/state/localization/LocalizationTest.java new file mode 100644 index 00000000..0107d9fe --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/core/state/localization/LocalizationTest.java @@ -0,0 +1,74 @@ +package edu.wpi.team190.gompeilib.core.state.localization; + +import static org.junit.jupiter.api.Assertions.*; + +import edu.wpi.first.apriltag.AprilTag; +import edu.wpi.first.math.geometry.*; +import edu.wpi.first.math.kinematics.SwerveDriveKinematics; +import edu.wpi.first.math.kinematics.SwerveModulePosition; +import edu.wpi.team190.gompeilib.subsystems.vision.data.VisionMultiTxTyObservation; +import edu.wpi.team190.gompeilib.subsystems.vision.data.VisionPoseObservation; +import java.util.List; +import java.util.Set; +import org.junit.jupiter.api.Test; + +public class LocalizationTest { + @Test + public void testLocalization() { + AprilTag tag1 = new AprilTag(1, new Pose3d(1.0, 2.0, 3.0, new Rotation3d())); + FieldZone zone = new FieldZone(Set.of(tag1)); + + SwerveDriveKinematics kinematics = + new SwerveDriveKinematics( + new Translation2d(0.5, 0.5), + new Translation2d(0.5, -0.5), + new Translation2d(-0.5, 0.5), + new Translation2d(-0.5, -0.5)); + + Localization loc = new Localization(List.of(zone), kinematics, 1.0); + + loc.resetPose(new Pose2d(1.0, 2.0, new Rotation2d())); + assertEquals(new Pose2d(1.0, 2.0, new Rotation2d()), loc.getEstimatedPose(zone)); + assertEquals(new Rotation2d(), loc.getHeading()); + + SwerveModulePosition[] modulePositions = + new SwerveModulePosition[] { + new SwerveModulePosition(0.0, new Rotation2d()), + new SwerveModulePosition(0.0, new Rotation2d()), + new SwerveModulePosition(0.0, new Rotation2d()), + new SwerveModulePosition(0.0, new Rotation2d()) + }; + loc.addOdometryObservation(1.0, new Rotation2d(), modulePositions); + + VisionPoseObservation poseObs = + new VisionPoseObservation( + new Pose2d(1.1, 2.1, new Rotation2d()), + Set.of(1), + 1.1, + edu.wpi.first.math.VecBuilder.fill(0.1, 0.1, 0.1)); + loc.addPoseObservations(List.of(poseObs)); + + // Test filtering out NaN poses + VisionPoseObservation nanPoseObs = + new VisionPoseObservation( + new Pose2d(Double.NaN, 2.1, new Rotation2d()), + Set.of(1), + 1.1, + edu.wpi.first.math.VecBuilder.fill(0.1, 0.1, 0.1)); + loc.addPoseObservations(List.of(nanPoseObs)); + + VisionMultiTxTyObservation txTyObs = + new VisionMultiTxTyObservation( + 1, + new double[] {0.1, 0.1, 0.1, 0.1}, + new double[] {0.2, 0.2, 0.2, 0.2}, + 2.0, + 1.2, + new Pose3d(1.0, 2.0, 0.5, new Rotation3d())); + loc.addTxTyObservations(List.of(txTyObs)); + + // Also get estimated pose for an unknown/empty zone to hit else branch + FieldZone emptyZone = new FieldZone(Set.of()); + assertNotNull(loc.getEstimatedPose(emptyZone)); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/GeometryUtilTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/GeometryUtilTest.java new file mode 100644 index 00000000..f6dc309b --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/GeometryUtilTest.java @@ -0,0 +1,241 @@ +package edu.wpi.team190.gompeilib.core.utility; + +import static org.junit.jupiter.api.Assertions.*; + +import edu.wpi.first.math.Pair; +import edu.wpi.first.math.geometry.*; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.FieldSource; + +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class GeometryUtilTest { + public static Translation2d[] TRANSLATION2D_CASES; + public static Pair[] XY_PAIR_CASES; + public static Rotation2d[] ROTATION2D_CASES; + public static Pose2d[] POSE2D_CASES; + + static { + TRANSLATION2D_CASES = + new Translation2d[] { + Translation2d.kZero, new Translation2d(190.0, 190.0), new Translation2d(42.0, 42.0) + }; + XY_PAIR_CASES = + new Pair[] {new Pair<>(0.0, 0.0), new Pair<>(190.0, 190.0), new Pair<>(42.0, 42.0)}; + ROTATION2D_CASES = + new Rotation2d[] { + Rotation2d.kZero, Rotation2d.fromDegrees(190.0), Rotation2d.fromDegrees(42.0) + }; + POSE2D_CASES = + new Pose2d[] { + Pose2d.kZero, + new Pose2d(190.0, 190.0, Rotation2d.fromDegrees(190.0)), + new Pose2d(42.0, 42.0, Rotation2d.fromDegrees(42.0)) + }; + } + + @Test + @Order(1) + void testConstruction() { + new GeometryUtil(); + } + + @ParameterizedTest + @FieldSource("TRANSLATION2D_CASES") + @Order(2) + void testToTransform2dGivenTranslation(Translation2d translation) { + assertEquals( + GeometryUtil.toTransform2d(translation), + new Transform2d(translation.getX(), translation.getY(), new Rotation2d())); + } + + @ParameterizedTest + @FieldSource("XY_PAIR_CASES") + @Order(3) + void testToTransform2dGivenDoubles(Pair xyPair) { + double x = xyPair.getFirst(); + double y = xyPair.getSecond(); + + assertEquals(GeometryUtil.toTransform2d(x, y), new Transform2d(x, y, new Rotation2d())); + } + + @ParameterizedTest + @FieldSource("ROTATION2D_CASES") + @Order(4) + void testToTransform2dGivenRotation2d(Rotation2d rotation) { + assertEquals( + GeometryUtil.toTransform2d(rotation), new Transform2d(new Translation2d(), rotation)); + } + + @ParameterizedTest + @FieldSource("POSE2D_CASES") + @Order(5) + void testToTransform2dGivenPose2d(Pose2d pose) { + assertEquals( + GeometryUtil.toTransform2d(pose), + new Transform2d(pose.getX(), pose.getY(), pose.getRotation())); + } + + @Test + @Order(6) + void testSinglePose2dsAreZero() { + Pose2d zeroedPose2d = Pose2d.kZero; + Pose2d nonZeroedPose2d1 = new Pose2d(1.0, 0.0, Rotation2d.fromDegrees(0.0)); + Pose2d nonZeroedPose2d2 = new Pose2d(0.0, 1.0, Rotation2d.fromDegrees(0.0)); + Pose2d nonZeroedPose2d3 = new Pose2d(0.0, 0.0, Rotation2d.fromDegrees(1.0)); + + assertTrue(GeometryUtil.isZero(zeroedPose2d)); + assertFalse(GeometryUtil.isZero(nonZeroedPose2d1)); + assertFalse(GeometryUtil.isZero(nonZeroedPose2d2)); + assertFalse(GeometryUtil.isZero(nonZeroedPose2d3)); + } + + @Test + @Order(7) + void testAnyPose2dsAreZero() { + Pose2d[] cases = new Pose2d[] {POSE2D_CASES[0], POSE2D_CASES[1], POSE2D_CASES[2]}; + assertTrue(GeometryUtil.isZero(cases)); + } + + @Test + @Order(8) + void testAnyPose2dsAreNotZero() { + Pose2d[] cases = new Pose2d[] {POSE2D_CASES[1], POSE2D_CASES[2]}; + assertFalse(GeometryUtil.isZero(cases)); + } + + @Test + @Order(9) + void testSingleTranslation2dsAreZero() { + Translation2d zeroedTranslation2d = Translation2d.kZero; + Translation2d nonZeroedTranslation2d1 = new Translation2d(1.0, 0.0); + Translation2d nonZeroedTranslation2d2 = new Translation2d(0.0, 1.0); + + assertTrue(GeometryUtil.isZero(zeroedTranslation2d)); + assertFalse(GeometryUtil.isZero(nonZeroedTranslation2d1)); + assertFalse(GeometryUtil.isZero(nonZeroedTranslation2d2)); + } + + @Test + @Order(10) + void testSingleRotation2dsAreZero() { + Rotation2d zeroedRotation2d = Rotation2d.kZero; + Rotation2d nonZeroedTranslation2d1 = Rotation2d.fromDegrees(190.0); + Rotation2d nonZeroedTranslation2d2 = Rotation2d.fromDegrees(42.0); + + assertTrue(GeometryUtil.isZero(zeroedRotation2d)); + assertFalse(GeometryUtil.isZero(nonZeroedTranslation2d1)); + assertFalse(GeometryUtil.isZero(nonZeroedTranslation2d2)); + } + + @Test + @Order(11) + void testSinglePose2dsAreNAN() { + Pose2d zeroedPose2d = Pose2d.kZero; + Pose2d nanPose2d1 = new Pose2d(Double.NaN, 0.0, Rotation2d.fromDegrees(0.0)); + Pose2d nanPose2d2 = new Pose2d(0.0, Double.NaN, Rotation2d.fromDegrees(0.0)); + Pose2d nanPose2d3 = new Pose2d(0.0, 0.0, Rotation2d.fromDegrees(Double.NaN)); + + assertFalse(GeometryUtil.isNaN(zeroedPose2d)); + assertTrue(GeometryUtil.isNaN(nanPose2d1)); + assertTrue(GeometryUtil.isNaN(nanPose2d2)); + assertTrue(GeometryUtil.isNaN(nanPose2d3)); + } + + @Test + @Order(12) + void testAnyPose2dsAreNAN() { + Pose2d nanPose = new Pose2d(Double.NaN, 190.0, Rotation2d.fromDegrees(190.0)); + Pose2d[] cases = new Pose2d[] {nanPose, POSE2D_CASES[0], POSE2D_CASES[1], POSE2D_CASES[2]}; + assertTrue(GeometryUtil.isNaN(cases)); + } + + @Test + @Order(13) + void testAnyPose2dsAreNotNAN() { + Pose2d[] cases = new Pose2d[] {POSE2D_CASES[0], POSE2D_CASES[1], POSE2D_CASES[2]}; + assertFalse(GeometryUtil.isNaN(cases)); + } + + @Test + @Order(14) + void testRectangleDoesNotContainsPose() { + Rectangle2d[] rectangles = { + new Rectangle2d(new Pose2d(0, 0, Rotation2d.kZero), 1, 1), + new Rectangle2d(new Pose2d(0, 0, Rotation2d.kZero), 1, 1), + new Rectangle2d(new Pose2d(0, 0, Rotation2d.kZero), 2, 2) + }; + Pose2d pose = new Pose2d(-5, -5, Rotation2d.kZero); + assertFalse(GeometryUtil.contains(rectangles, pose)); + } + + @Test + @Order(15) + void testRectangleContainsPose() { + Rectangle2d[] rectangles = { + new Rectangle2d(new Pose2d(0, 0, Rotation2d.kZero), 1, 1), + new Rectangle2d(new Pose2d(0, 0, Rotation2d.kZero), 2, 2), + new Rectangle2d(new Pose2d(0, 0, Rotation2d.kZero), 3, 3) + }; + Pose2d pose = new Pose2d(1.5, 1.5, Rotation2d.kZero); + assertTrue(GeometryUtil.contains(rectangles, pose)); + } + + @Test + @Order(16) + void testRectangleDoesNotContainsTranslation() { + Rectangle2d[] rectangles = { + new Rectangle2d(new Pose2d(0, 0, Rotation2d.kZero), 1, 1), + new Rectangle2d(new Pose2d(0, 0, Rotation2d.kZero), 1, 1), + new Rectangle2d(new Pose2d(0, 0, Rotation2d.kZero), 2, 2) + }; + Translation2d translation = new Translation2d(-5, -5); + assertFalse(GeometryUtil.contains(rectangles, translation)); + } + + @Test + @Order(17) + void testRectangleContainsTranslation() { + Rectangle2d[] rectangles = { + new Rectangle2d(new Pose2d(0, 0, Rotation2d.kZero), 1, 1), + new Rectangle2d(new Pose2d(0, 0, Rotation2d.kZero), 2, 2), + new Rectangle2d(new Pose2d(0, 0, Rotation2d.kZero), 3, 3) + }; + Translation2d translation = new Translation2d(1.5, 1.5); + assertTrue(GeometryUtil.contains(rectangles, translation)); + } + + @Test + @Order(18) + void testRectanglePose2ds() { + Rectangle2d rectangle = new Rectangle2d(new Pose2d(0, 0, Rotation2d.kZero), 1, 1); + Pose2d[] poses = GeometryUtil.rectanglePose2ds(rectangle); + Pose2d[] correctPoses = { + new Pose2d(1.0, 1.0, Rotation2d.kZero), + new Pose2d(-1.0, 1.0, Rotation2d.kZero), + new Pose2d(-1.0, -1.0, Rotation2d.kZero), + new Pose2d(1.0, -1.0, Rotation2d.kZero), + new Pose2d(0, 0, Rotation2d.kZero), + }; + for (int i = 0; i < 5; i++) { + assertEquals(poses[i], correctPoses[i]); + } + } + + @Test + @Order(19) + void testRectangleIntersects() { + Rectangle2d[] rectangles = {new Rectangle2d(new Pose2d(0, 0, Rotation2d.kZero), 1, 1)}; + + // Intersects center + assertTrue(GeometryUtil.intersects(rectangles, new Pose2d(0, 0, Rotation2d.kZero), 1, 1)); + // Does not intersect + assertFalse(GeometryUtil.intersects(rectangles, new Pose2d(5, 5, Rotation2d.kZero), 1, 1)); + + // Intersects via target containing rectangle center + assertTrue(GeometryUtil.intersects(rectangles, new Pose2d(0.5, 0.5, Rotation2d.kZero), 3, 3)); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/LimelightHelpersTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/LimelightHelpersTest.java new file mode 100644 index 00000000..b07b86bc --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/LimelightHelpersTest.java @@ -0,0 +1,404 @@ +package edu.wpi.team190.gompeilib.core.utility; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Pose3d; +import edu.wpi.first.net.PortForwarder; +import edu.wpi.first.networktables.NetworkTableInstance; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; + +public class LimelightHelpersTest { + + private NetworkTableInstance nt; + private String llName = "limelight-test"; + + @BeforeEach + public void setUp() { + nt = NetworkTableInstance.getDefault(); + } + + @Test + public void testMathConversions() { + // toPose3D + double[] badPose = {1.0, 2.0}; + Pose3d resultBad = LimelightHelpers.toPose3D(badPose); + assertEquals(new Pose3d(), resultBad); + + double[] goodPose = {1.0, 2.0, 3.0, 0.0, 0.0, 90.0}; + Pose3d resultGood = LimelightHelpers.toPose3D(goodPose); + assertEquals(1.0, resultGood.getX()); + assertEquals(2.0, resultGood.getY()); + assertEquals(3.0, resultGood.getZ()); + assertEquals(Math.PI / 2.0, resultGood.getRotation().getZ(), 1e-6); + + // toPose2D + Pose2d result2DBad = LimelightHelpers.toPose2D(badPose); + assertEquals(new Pose2d(), result2DBad); + + Pose2d result2DGood = LimelightHelpers.toPose2D(goodPose); + assertEquals(1.0, result2DGood.getX()); + assertEquals(2.0, result2DGood.getY()); + assertEquals(Math.PI / 2.0, result2DGood.getRotation().getRadians(), 1e-6); + + // pose3dToArray + double[] arr3d = LimelightHelpers.pose3dToArray(resultGood); + assertEquals(6, arr3d.length); + assertEquals(1.0, arr3d[0]); + assertEquals(90.0, arr3d[5], 1e-6); + + // pose2dToArray + double[] arr2d = LimelightHelpers.pose2dToArray(result2DGood); + assertEquals(6, arr2d.length); + assertEquals(1.0, arr2d[0]); + assertEquals(90.0, arr2d[5], 1e-6); + } + + @Test + public void testGettersAndSettersNT() { + // Flush + LimelightHelpers.Flush(); + + // tv, tx, ty, txnc, tync, ta + nt.getTable(llName).getEntry("tv").setDouble(1.0); + nt.getTable(llName).getEntry("tx").setDouble(2.5); + nt.getTable(llName).getEntry("ty").setDouble(-3.5); + nt.getTable(llName).getEntry("txnc").setDouble(1.2); + nt.getTable(llName).getEntry("tync").setDouble(-1.2); + nt.getTable(llName).getEntry("ta").setDouble(15.0); + + assertTrue(LimelightHelpers.getTV(llName)); + assertEquals(2.5, LimelightHelpers.getTX(llName)); + assertEquals(-3.5, LimelightHelpers.getTY(llName)); + assertEquals(1.2, LimelightHelpers.getTXNC(llName)); + assertEquals(-1.2, LimelightHelpers.getTYNC(llName)); + assertEquals(15.0, LimelightHelpers.getTA(llName)); + + // t2d + double[] t2dData = new double[17]; + t2dData[1] = 2.0; // targetCount + t2dData[10] = 3.0; // targetClassIndexDetector + t2dData[11] = 4.0; // targetClassIndexClassifier + nt.getTable(llName).getEntry("t2d").setDoubleArray(t2dData); + + assertArrayEquals(t2dData, LimelightHelpers.getT2DArray(llName)); + assertEquals(2, LimelightHelpers.getTargetCount(llName)); + assertEquals(3, LimelightHelpers.getDetectorClassIndex(llName)); + assertEquals(4, LimelightHelpers.getClassifierClassIndex(llName)); + + // strings tcclass, tdclass, getpipetype, json + nt.getTable(llName).getEntry("tcclass").setString("cls1"); + nt.getTable(llName).getEntry("tdclass").setString("det1"); + nt.getTable(llName).getEntry("getpipetype").setString("tag"); + nt.getTable(llName).getEntry("json").setString("{test}"); + + assertEquals("cls1", LimelightHelpers.getClassifierClass(llName)); + assertEquals("det1", LimelightHelpers.getDetectorClass(llName)); + assertEquals("tag", LimelightHelpers.getCurrentPipelineType(llName)); + assertEquals("{test}", LimelightHelpers.getJSONDump(llName)); + + // latencies and active pipeline + nt.getTable(llName).getEntry("tl").setDouble(12.3); + nt.getTable(llName).getEntry("cl").setDouble(1.1); + nt.getTable(llName).getEntry("getpipe").setDouble(2.0); + + assertEquals(12.3, LimelightHelpers.getLatency_Pipeline(llName)); + assertEquals(1.1, LimelightHelpers.getLatency_Capture(llName)); + assertEquals(2.0, LimelightHelpers.getCurrentPipelineIndex(llName)); + + // botpose methods + double[] botpose = {1.0, 2.0, 3.0, 0.0, 0.0, 0.0}; + nt.getTable(llName).getEntry("botpose").setDoubleArray(botpose); + nt.getTable(llName).getEntry("botpose_wpired").setDoubleArray(botpose); + nt.getTable(llName).getEntry("botpose_wpiblue").setDoubleArray(botpose); + nt.getTable(llName).getEntry("botpose_targetspace").setDoubleArray(botpose); + nt.getTable(llName).getEntry("camerapose_targetspace").setDoubleArray(botpose); + nt.getTable(llName).getEntry("targetpose_cameraspace").setDoubleArray(botpose); + nt.getTable(llName).getEntry("targetpose_robotspace").setDoubleArray(botpose); + nt.getTable(llName).getEntry("camerapose_robotspace").setDoubleArray(botpose); + + assertArrayEquals(botpose, LimelightHelpers.getBotpose(llName)); + assertArrayEquals(botpose, LimelightHelpers.getBotpose_wpiRed(llName)); + assertArrayEquals(botpose, LimelightHelpers.getBotpose_wpiBlue(llName)); + + assertArrayEquals(botpose, LimelightHelpers.getBotPose(llName)); + assertArrayEquals(botpose, LimelightHelpers.getBotPose_wpiRed(llName)); + assertArrayEquals(botpose, LimelightHelpers.getBotPose_wpiBlue(llName)); + assertArrayEquals(botpose, LimelightHelpers.getBotPose_TargetSpace(llName)); + assertArrayEquals(botpose, LimelightHelpers.getCameraPose_TargetSpace(llName)); + assertArrayEquals(botpose, LimelightHelpers.getTargetPose_CameraSpace(llName)); + assertArrayEquals(botpose, LimelightHelpers.getTargetPose_RobotSpace(llName)); + + // pose3d getters + assertNotNull(LimelightHelpers.getBotPose3d(llName)); + assertNotNull(LimelightHelpers.getBotPose3d_wpiRed(llName)); + assertNotNull(LimelightHelpers.getBotPose3d_wpiBlue(llName)); + assertNotNull(LimelightHelpers.getBotPose3d_TargetSpace(llName)); + assertNotNull(LimelightHelpers.getCameraPose3d_TargetSpace(llName)); + assertNotNull(LimelightHelpers.getTargetPose3d_CameraSpace(llName)); + assertNotNull(LimelightHelpers.getTargetPose3d_RobotSpace(llName)); + assertNotNull(LimelightHelpers.getCameraPose3d_RobotSpace(llName)); + + // pose2d getters + assertNotNull(LimelightHelpers.getBotPose2d(llName)); + assertNotNull(LimelightHelpers.getBotPose2d_wpiRed(llName)); + assertNotNull(LimelightHelpers.getBotPose2d_wpiBlue(llName)); + + // set/get python data + double[] pyOut = {9.9, 8.8}; + LimelightHelpers.setPythonScriptData(llName, pyOut); + assertArrayEquals(pyOut, nt.getTable(llName).getEntry("llrobot").getDoubleArray(new double[0])); + + double[] pyIn = {7.7, 6.6}; + nt.getTable(llName).getEntry("llpython").setDoubleArray(pyIn); + assertArrayEquals(pyIn, LimelightHelpers.getPythonScriptData(llName)); + + // other set operations + LimelightHelpers.setPipelineIndex(llName, 4); + assertEquals(4.0, nt.getTable(llName).getEntry("pipeline").getDouble(0.0)); + + LimelightHelpers.setPriorityTagID(llName, 12); + assertEquals(12.0, nt.getTable(llName).getEntry("priorityid").getDouble(0.0)); + + LimelightHelpers.setLEDMode_ForceBlink(llName); + assertEquals(2.0, nt.getTable(llName).getEntry("ledMode").getDouble(0.0)); + + LimelightHelpers.setLEDMode_ForceOff(llName); + assertEquals(1.0, nt.getTable(llName).getEntry("ledMode").getDouble(0.0)); + + LimelightHelpers.setLEDMode_ForceOn(llName); + assertEquals(3.0, nt.getTable(llName).getEntry("ledMode").getDouble(0.0)); + + LimelightHelpers.setLEDMode_PipelineControl(llName); + assertEquals(0.0, nt.getTable(llName).getEntry("ledMode").getDouble(0.0)); + + LimelightHelpers.setStreamMode_PiPMain(llName); + assertEquals(1.0, nt.getTable(llName).getEntry("stream").getDouble(0.0)); + + LimelightHelpers.setStreamMode_PiPSecondary(llName); + assertEquals(2.0, nt.getTable(llName).getEntry("stream").getDouble(0.0)); + + LimelightHelpers.setStreamMode_Standard(llName); + assertEquals(0.0, nt.getTable(llName).getEntry("stream").getDouble(0.0)); + + LimelightHelpers.setCropWindow(llName, -0.5, 0.5, -0.5, 0.5); + assertArrayEquals( + new double[] {-0.5, 0.5, -0.5, 0.5}, + nt.getTable(llName).getEntry("crop").getDoubleArray(new double[0])); + + LimelightHelpers.setKeystone(llName, 0.1, 0.2); + assertArrayEquals( + new double[] {0.1, 0.2}, + nt.getTable(llName).getEntry("keystone_set").getDoubleArray(new double[0])); + + LimelightHelpers.setFiducial3DOffset(llName, 1.0, 2.0, 3.0); + assertArrayEquals( + new double[] {1.0, 2.0, 3.0}, + nt.getTable(llName).getEntry("fiducial_offset_set").getDoubleArray(new double[0])); + + LimelightHelpers.SetRobotOrientation(llName, 10.0, 1.0, 2.0, 3.0, 4.0, 5.0); + assertArrayEquals( + new double[] {10.0, 1.0, 2.0, 3.0, 4.0, 5.0}, + nt.getTable(llName).getEntry("robot_orientation_set").getDoubleArray(new double[0])); + + LimelightHelpers.SetRobotOrientation_NoFlush(llName, 11.0, 1.1, 2.1, 3.1, 4.1, 5.1); + assertArrayEquals( + new double[] {11.0, 1.1, 2.1, 3.1, 4.1, 5.1}, + nt.getTable(llName).getEntry("robot_orientation_set").getDoubleArray(new double[0])); + + LimelightHelpers.SetIMUMode(llName, 2); + assertEquals(2.0, nt.getTable(llName).getEntry("imumode_set").getDouble(0.0)); + + LimelightHelpers.SetIMUAssistAlpha(llName, 0.05); + assertEquals(0.05, nt.getTable(llName).getEntry("imuassistalpha_set").getDouble(0.0)); + + LimelightHelpers.SetThrottle(llName, 5); + assertEquals(5.0, nt.getTable(llName).getEntry("throttle_set").getDouble(0.0)); + + LimelightHelpers.SetFiducialIDFiltersOverride(llName, new int[] {1, 2}); + assertArrayEquals( + new double[] {1.0, 2.0}, + nt.getTable(llName).getEntry("fiducial_id_filters_set").getDoubleArray(new double[0])); + + LimelightHelpers.SetFiducialDownscalingOverride(llName, 1.5f); + assertEquals(2.0, nt.getTable(llName).getEntry("fiducial_downscale_set").getDouble(0.0)); + + // Test other downscale factors + LimelightHelpers.SetFiducialDownscalingOverride(llName, 1.0f); + assertEquals(1.0, nt.getTable(llName).getEntry("fiducial_downscale_set").getDouble(0.0)); + LimelightHelpers.SetFiducialDownscalingOverride(llName, 2.0f); + assertEquals(3.0, nt.getTable(llName).getEntry("fiducial_downscale_set").getDouble(0.0)); + LimelightHelpers.SetFiducialDownscalingOverride(llName, 3.0f); + assertEquals(4.0, nt.getTable(llName).getEntry("fiducial_downscale_set").getDouble(0.0)); + LimelightHelpers.SetFiducialDownscalingOverride(llName, 4.0f); + assertEquals(5.0, nt.getTable(llName).getEntry("fiducial_downscale_set").getDouble(0.0)); + + LimelightHelpers.setCameraPose_RobotSpace(llName, 1.0, 2.0, 3.0, 10.0, 20.0, 30.0); + assertArrayEquals( + new double[] {1.0, 2.0, 3.0, 10.0, 20.0, 30.0}, + nt.getTable(llName).getEntry("camerapose_robotspace_set").getDoubleArray(new double[0])); + + LimelightHelpers.setRewindEnabled(llName, true); + assertEquals(1.0, nt.getTable(llName).getEntry("rewind_enable_set").getDouble(0.0)); + + LimelightHelpers.setRewindEnabled(llName, false); + assertEquals(0.0, nt.getTable(llName).getEntry("rewind_enable_set").getDouble(0.0)); + + nt.getTable(llName).getEntry("capture_rewind").setDoubleArray(new double[] {0.0, 0.0}); + LimelightHelpers.triggerRewindCapture(llName, 5.0); + assertArrayEquals( + new double[] {1.0, 5.0}, + nt.getTable(llName).getEntry("capture_rewind").getDoubleArray(new double[0])); + + nt.getTable(llName).getEntry("snapshot").setDouble(5.0); + LimelightHelpers.triggerSnapshot(llName); + assertEquals(6.0, nt.getTable(llName).getEntry("snapshot").getDouble(0.0)); + } + + @Test + public void testPoseEstimateRetrieval() { + // Empty array + nt.getTable(llName).getEntry("botpose_wpiblue").setDoubleArray(new double[0]); + LimelightHelpers.PoseEstimate emptyEst = LimelightHelpers.getBotPoseEstimate_wpiBlue(llName); + assertEquals(new Pose2d(), emptyEst.pose); + + // Mismatched fiducial values + double[] invalidPoseArr = new double[15]; // expected 11 + 7*1 = 18 for 1 tag + invalidPoseArr[7] = 1.0; // tagCount = 1 + nt.getTable(llName).getEntry("botpose_wpiblue").setDoubleArray(invalidPoseArr); + LimelightHelpers.PoseEstimate invalidEst = LimelightHelpers.getBotPoseEstimate_wpiBlue(llName); + assertEquals(0, invalidEst.rawFiducials.length); + + // Valid pose with fiducials + double[] poseArr = new double[18]; + poseArr[0] = 2.0; // X + poseArr[1] = 3.0; // Y + // index 6 is latency + poseArr[6] = 10.0; + // index 7 is tagCount + poseArr[7] = 1.0; + // fiducials start at index 11 + poseArr[11] = 5.0; // ID + poseArr[12] = 0.1; // txnc + poseArr[13] = 0.2; // tync + poseArr[14] = 0.3; // ta + poseArr[15] = 1.5; // distToCamera + poseArr[16] = 1.6; // distToRobot + poseArr[17] = 0.05; // ambiguity + + nt.getTable(llName).getEntry("botpose_wpiblue").setDoubleArray(poseArr); + LimelightHelpers.PoseEstimate validEst = LimelightHelpers.getBotPoseEstimate_wpiBlue(llName); + assertEquals(2.0, validEst.pose.getX()); + assertEquals(1, validEst.rawFiducials.length); + assertEquals(5, validEst.rawFiducials[0].id); + + // printPoseEstimate + LimelightHelpers.printPoseEstimate(null); + LimelightHelpers.printPoseEstimate(validEst); + + // validPoseEstimate + assertFalse(LimelightHelpers.validPoseEstimate(null)); + assertTrue(LimelightHelpers.validPoseEstimate(validEst)); + + // getBotPoseEstimate wpiRed and MegaTag2 + assertNotNull(LimelightHelpers.getBotPoseEstimate_wpiRed(llName)); + assertNotNull(LimelightHelpers.getBotPoseEstimate_wpiRed_MegaTag2(llName)); + assertNotNull(LimelightHelpers.getBotPoseEstimate_wpiBlue_MegaTag2(llName)); + } + + @Test + public void testRawDetectionsAndTargets() { + // getRawFiducials + double[] rawFids = {5, 0.1, 0.2, 0.3, 1.0, 1.1, 0.05}; + nt.getTable(llName).getEntry("rawfiducials").setDoubleArray(rawFids); + LimelightHelpers.RawFiducial[] fids = LimelightHelpers.getRawFiducials(llName); + assertEquals(1, fids.length); + assertEquals(5, fids[0].id); + assertEquals(0.05, fids[0].ambiguity); + + // Mismatched size + nt.getTable(llName).getEntry("rawfiducials").setDoubleArray(new double[] {1, 2}); + assertEquals(0, LimelightHelpers.getRawFiducials(llName).length); + + // getRawDetections + double[] rawDets = {1, 0.1, 0.2, 0.3, 1, 2, 3, 4, 5, 6, 7, 8}; + nt.getTable(llName).getEntry("rawdetections").setDoubleArray(rawDets); + LimelightHelpers.RawDetection[] dets = LimelightHelpers.getRawDetections(llName); + assertEquals(1, dets.length); + assertEquals(1, dets[0].classId); + + // Mismatched size + nt.getTable(llName).getEntry("rawdetections").setDoubleArray(new double[] {1, 2}); + assertEquals(0, LimelightHelpers.getRawDetections(llName).length); + + // getRawTargets + double[] rawTargets = {0.1, 0.2, 0.3}; + nt.getTable(llName).getEntry("rawtargets").setDoubleArray(rawTargets); + LimelightHelpers.RawTarget[] targets = LimelightHelpers.getRawTargets(llName); + assertEquals(1, targets.length); + assertEquals(0.1, targets[0].txnc); + + // Mismatched size + nt.getTable(llName).getEntry("rawtargets").setDoubleArray(new double[] {1, 2}); + assertEquals(0, LimelightHelpers.getRawTargets(llName).length); + + // getCornerCoordinates + double[] corners = {1.0, 2.0, 3.0, 4.0}; + nt.getTable(llName).getEntry("tcornxy").setDoubleArray(corners); + assertArrayEquals(corners, LimelightHelpers.getCornerCoordinates(llName)); + + // getIMUData + double[] imu = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0}; + nt.getTable(llName).getEntry("imu").setDoubleArray(imu); + LimelightHelpers.IMUData imuData = LimelightHelpers.getIMUData(llName); + assertEquals(1.0, imuData.robotYaw); + assertEquals(10.0, imuData.accelZ); + + // getIMUData empty/bad + nt.getTable(llName).getEntry("imu").setDoubleArray(new double[0]); + assertEquals(0.0, LimelightHelpers.getIMUData(llName).robotYaw); + } + + @Test + public void testLatestResultsAndPortForwarding() { + // Port forwarding (runs safely by mocking PortForwarder) + try (MockedStatic mockPF = mockStatic(PortForwarder.class)) { + LimelightHelpers.setupPortForwardingUSB(0); + mockPF.verify(() -> PortForwarder.add(anyInt(), anyString(), anyInt()), times(10)); + } + + // getLatestResults empty + nt.getTable(llName).getEntry("json").setString(""); + assertNotNull(LimelightHelpers.getLatestResults(llName).error); + + // getLatestResults valid json + String json = + "{\n" + + " \"pID\": 1.0,\n" + + " \"tl\": 10.0,\n" + + " \"cl\": 5.0,\n" + + " \"v\": true,\n" + + " \"botpose\": [1.0, 2.0, 3.0, 0.0, 0.0, 45.0],\n" + + " \"Retro\": [],\n" + + " \"Fiducial\": [],\n" + + " \"Classifier\": [],\n" + + " \"Detector\": [],\n" + + " \"Barcode\": [],\n" + + " \"imu\": {\n" + + " \"data\": [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0],\n" + + " \"quat\": [0.0, 0.0, 0.0, 1.0],\n" + + " \"yaw\": 90.0\n" + + " }\n" + + "}"; + nt.getTable(llName).getEntry("json").setString(json); + LimelightHelpers.LimelightResults res = LimelightHelpers.getLatestResults(llName); + assertNull(res.error); + assertTrue(res.valid); + assertEquals(1.0, res.pipelineID); + assertEquals(90.0, res.imuResults.yaw); + assertEquals(1.0, res.imuResults.robotYaw); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/SetpointTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/SetpointTest.java new file mode 100644 index 00000000..a16ce3ce --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/SetpointTest.java @@ -0,0 +1,207 @@ +package edu.wpi.team190.gompeilib.core.utility; + +import static edu.wpi.first.units.Units.*; +import static org.junit.jupiter.api.Assertions.*; + +import edu.wpi.first.units.DistanceUnit; +import edu.wpi.first.units.VoltageUnit; +import edu.wpi.first.units.measure.Distance; +import edu.wpi.first.units.measure.Voltage; +import org.junit.jupiter.api.*; + +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +class SetpointTest { + + @Test + @Order(1) + public void testApplyZero() { + Distance d = Meters.of(1); + Distance s = Meters.of(0.1); + + Setpoint setpoint = new Setpoint<>(d, s); + assertEquals(d, setpoint.getNewSetpoint()); + } + + @Test + @Order(2) + public void testApplyIncrement() { + Distance d = Meters.of(1); + Distance s = Meters.of(0.1); + + Setpoint setpoint = new Setpoint<>(d, s); + setpoint.increment(); + + assertEquals(d.plus(s), setpoint.getNewSetpoint()); + } + + @Test + @Order(3) + public void testApplyDecrement() { + Distance d = Meters.of(1); + Distance s = Meters.of(0.1); + + Setpoint setpoint = new Setpoint<>(d, s); + setpoint.decrement(); + + assertEquals(d.minus(s), setpoint.getNewSetpoint()); + } + + @Test + @Order(4) + public void testApplyOffsetRepeatedly() { + Distance d = Meters.of(1); + Distance s = Meters.of(0.1); + + Setpoint setpoint = new Setpoint<>(d, s); + + setpoint.decrement(); + setpoint.decrement(); + setpoint.decrement(); + setpoint.decrement(); + setpoint.increment(); + setpoint.decrement(); + setpoint.increment(); + + assertEquals(d.minus(s).minus(s).minus(s), setpoint.getNewSetpoint()); + } + + @Test + @Order(5) + public void testResetOffset() { + Distance d = Meters.of(1); + Distance s = Meters.of(0.1); + + Setpoint setpoint = new Setpoint<>(d, s); + setpoint.decrement(); + setpoint.reset(); + + assertEquals(d, setpoint.getNewSetpoint()); + } + + @Test + @Order(6) + public void testSpecificIncrement() { + Distance d = Meters.of(1); + Distance s = Meters.of(0.1); + Distance a = Inches.of(4); + + Setpoint setpoint = new Setpoint<>(d, s); + setpoint.increment(a); + + assertEquals(d.plus(a), setpoint.getNewSetpoint()); + } + + @Test + @Order(7) + public void testSpecificDecrement() { + Distance d = Meters.of(1); + Distance s = Meters.of(0.1); + Distance a = Inches.of(4); + + Setpoint setpoint = new Setpoint<>(d, s); + setpoint.decrement(a); + + assertEquals(d.minus(a), setpoint.getNewSetpoint()); + } + + @Test + @Order(8) + public void testApplyGhostOffsetMaximum() { + Distance d = Meters.of(1); + Distance s = Meters.of(1); + Distance min = Meters.of(0); + Distance max = Meters.of(2); + + Setpoint setpoint = new Setpoint<>(d, s, min, max); + + setpoint.increment(); + setpoint.increment(); + setpoint.increment(); + + assertEquals(max, setpoint.getNewSetpoint()); + } + + @Test + @Order(9) + public void testApplyGhostOffsetMinimum() { + Distance d = Meters.of(1); + Distance s = Meters.of(1); + Distance min = Meters.of(0); + Distance max = Meters.of(2); + + Setpoint setpoint = new Setpoint<>(d, s, min, max); + + setpoint.decrement(); + setpoint.decrement(); + setpoint.decrement(); + + assertEquals(min, setpoint.getNewSetpoint()); + } + + @Test + @Order(10) + public void testApplyGhostOffsetMaximumOverride() { + Distance d = Meters.of(1); + Distance s = Meters.of(1); + Distance min = Meters.of(0); + Distance max = Meters.of(2); + + Setpoint setpoint = new Setpoint<>(d, s, min, max); + + setpoint.increment(s); + setpoint.increment(s); + setpoint.increment(s); + + assertEquals(max, setpoint.getNewSetpoint()); + } + + @Test + @Order(11) + public void testApplyGhostOffsetMinimumOverride() { + Distance d = Meters.of(1); + Distance s = Meters.of(1); + Distance min = Meters.of(0); + Distance max = Meters.of(2); + + Setpoint setpoint = new Setpoint<>(d, s, min, max); + + setpoint.decrement(s); + setpoint.decrement(s); + setpoint.decrement(s); + + assertEquals(min, setpoint.getNewSetpoint()); + } + + @Test + @Order(12) + public void testOffsetGetter() { + Distance d = Meters.of(1); + Distance s = Meters.of(1); + Distance min = Meters.of(0); + Distance max = Meters.of(2); + + Setpoint setpoint = new Setpoint<>(d, s, min, max); + setpoint.increment(s); + + assertEquals(Meters.of(1.0), setpoint.getOffset()); + } + + @Test + @Order(13) + public void testGhostOffsetMax() { + Voltage v = Volts.of(11); + Voltage s = Volts.of(1); + Voltage min = Volts.of(-12); + Voltage max = Volts.of(12); + + Setpoint setpoint = new Setpoint<>(v, s, min, max); + + setpoint.increment(); + assertEquals(Volts.of(1.0), setpoint.getOffset()); + + setpoint.increment(); + + Voltage a = (Voltage) setpoint.getNewSetpoint(); + assertEquals(Volts.of(12.0), a); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/VirtualSubsystemTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/VirtualSubsystemTest.java new file mode 100644 index 00000000..c206b7e5 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/VirtualSubsystemTest.java @@ -0,0 +1,23 @@ +package edu.wpi.team190.gompeilib.core.utility; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.concurrent.atomic.AtomicInteger; +import org.junit.jupiter.api.Test; + +public class VirtualSubsystemTest { + @Test + public void testVirtualSubsystem() { + AtomicInteger counter = new AtomicInteger(0); + VirtualSubsystem sub = + new VirtualSubsystem() { + @Override + public void periodic() { + counter.incrementAndGet(); + } + }; + + VirtualSubsystem.periodicAll(); + assertEquals(1, counter.get()); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/control/CurrentLimitsTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/control/CurrentLimitsTest.java new file mode 100644 index 00000000..e786ac3c --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/control/CurrentLimitsTest.java @@ -0,0 +1,33 @@ +package edu.wpi.team190.gompeilib.core.utility.control; + +import static org.junit.jupiter.api.Assertions.*; + +import edu.wpi.first.units.Units; +import edu.wpi.first.units.measure.Current; +import org.junit.jupiter.api.Test; + +public class CurrentLimitsTest { + @Test + public void testCurrentLimits() { + Current supply = Units.Amps.of(40); + Current stator = Units.Amps.of(80); + + CurrentLimits limits1 = + CurrentLimits.builder() + .withSupplyCurrentLimit(supply) + .withStatorCurrentLimit(stator) + .build(); + + assertEquals(supply, limits1.supplyCurrentLimit()); + assertEquals(stator, limits1.statorCurrentLimit()); + + CurrentLimits limits2 = + CurrentLimits.fromDoubles() + .withSupplyCurrentLimit(40.0) + .withStatorCurrentLimit(80.0) + .build(); + + assertEquals(40.0, limits2.supplyCurrentLimit().in(Units.Amps)); + assertEquals(80.0, limits2.statorCurrentLimit().in(Units.Amps)); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/control/GainsTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/control/GainsTest.java new file mode 100644 index 00000000..628876b3 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/control/GainsTest.java @@ -0,0 +1,47 @@ +package edu.wpi.team190.gompeilib.core.utility.control; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.concurrent.atomic.AtomicInteger; +import org.junit.jupiter.api.Test; + +public class GainsTest { + @Test + public void testGains() { + Gains gains1 = + Gains.fromDoubles() + .withPrefix("Test") + .withKP(1.0) + .withKI(2.0) + .withKD(3.0) + .withKS(4.0) + .withKV(5.0) + .withKA(6.0) + .withKG(7.0) + .build(); + + assertEquals(1.0, gains1.getKP()); + assertEquals(2.0, gains1.getKI()); + assertEquals(3.0, gains1.getKD()); + assertEquals(4.0, gains1.getKS()); + assertEquals(5.0, gains1.getKV()); + assertEquals(6.0, gains1.getKA()); + assertEquals(7.0, gains1.getKG()); + + // Null checks / defaults + Gains gains2 = Gains.builder().build(); + assertEquals(0.0, gains2.getKP()); + assertEquals(0.0, gains2.getKI()); + assertEquals(0.0, gains2.getKD()); + assertEquals(0.0, gains2.getKS()); + assertEquals(0.0, gains2.getKV()); + assertEquals(0.0, gains2.getKA()); + assertEquals(0.0, gains2.getKG()); + + // Update check + AtomicInteger counter = new AtomicInteger(0); + gains1.update(1, g -> counter.incrementAndGet()); + // Since kP is a LoggedTunableNumber, first check to see if it changed will trigger + assertEquals(1, counter.get()); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/control/LinearProfileTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/control/LinearProfileTest.java new file mode 100644 index 00000000..41d64ea9 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/control/LinearProfileTest.java @@ -0,0 +1,67 @@ +package edu.wpi.team190.gompeilib.core.utility.control; + +import static org.junit.jupiter.api.Assertions.*; + +import edu.wpi.first.math.geometry.Twist2d; +import org.junit.jupiter.api.Test; + +public class LinearProfileTest { + @Test + public void testLinearProfile() { + LinearProfile profile = new LinearProfile(10.0, 100.0, 0.02); + assertEquals(0.02, profile.getPeriod()); + + // Set max velocity and acceleration + profile.setMaxVelocity(50.0); + profile.setMaxAcceleration(20.0); // dv = 20.0 * 0.02 = 0.4 + + profile.setGoal(10.0, 5.0); + assertEquals(10.0, profile.getGoal()); + assertEquals(5.0, profile.getCurrentSetpoint()); + + // calculateSetpoint towards positive goal + double setpoint = profile.calculateSetpoint(); + assertEquals(5.4, setpoint); + + // reach goal + profile.setGoal(5.5, 5.4); + assertEquals(5.5, profile.calculateSetpoint()); + + // epsilon equal setpoint + assertEquals(5.5, profile.calculateSetpoint()); + + // towards negative goal + profile.setGoal(4.0, 5.0); + assertEquals(4.6, profile.calculateSetpoint()); + + // reach negative goal + profile.setGoal(4.5, 4.6); + assertEquals(4.5, profile.calculateSetpoint()); + + // reset + profile.reset(); + assertEquals(0.0, profile.getGoal()); + assertEquals(0.0, profile.getCurrentSetpoint()); + + // test inner EqualsUtil + profile.new EqualsUtil(); // coverage + assertTrue(LinearProfile.EqualsUtil.epsilonEquals(1.0, 1.0000000001)); + assertFalse(LinearProfile.EqualsUtil.epsilonEquals(1.0, 1.1)); + + // test inner GeomExtensions + new LinearProfile.EqualsUtil.GeomExtensions(); // coverage + Twist2d t1 = new Twist2d(1.0, 2.0, 3.0); + Twist2d t2 = new Twist2d(1.0, 2.0, 3.0); + Twist2d t3 = new Twist2d(1.1, 2.0, 3.0); + Twist2d t4 = new Twist2d(1.0, 2.1, 3.0); + Twist2d t5 = new Twist2d(1.0, 2.0, 3.1); + assertTrue(LinearProfile.EqualsUtil.GeomExtensions.epsilonEquals(t1, t2)); + assertFalse(LinearProfile.EqualsUtil.GeomExtensions.epsilonEquals(t1, t3)); // dx diff + assertFalse(LinearProfile.EqualsUtil.GeomExtensions.epsilonEquals(t1, t4)); // dy diff + assertFalse(LinearProfile.EqualsUtil.GeomExtensions.epsilonEquals(t1, t5)); // dtheta diff + + // test calculateSetpoint with NaN goal (covers else branch of goal < currentSetpoint) + profile.setGoal(Double.NaN, 5.0); + assertEquals(5.0, profile.calculateSetpoint()); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/control/constraints/AngularPositionConstraintsTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/control/constraints/AngularPositionConstraintsTest.java new file mode 100644 index 00000000..7f2664c3 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/control/constraints/AngularPositionConstraintsTest.java @@ -0,0 +1,28 @@ +package edu.wpi.team190.gompeilib.core.utility.control.constraints; + +import static org.junit.jupiter.api.Assertions.*; + +import edu.wpi.first.units.*; +import java.util.concurrent.atomic.AtomicInteger; +import org.junit.jupiter.api.Test; + +public class AngularPositionConstraintsTest { + @Test + public void testAngularPositionConstraints() { + AngularPositionConstraints c = + AngularPositionConstraints.fromMeasures() + .withPrefix("Test") + .withGoalTolerance(Units.Degrees.of(1.0)) + .withMaxVelocity(Units.DegreesPerSecond.of(180.0)) + .withMaxAcceleration(Units.DegreesPerSecond.per(Units.Second).of(360.0)) + .build(); + + assertEquals(1.0, c.getGoalTolerance(Units.Degrees)); + assertEquals(180.0, c.getMaxVelocity(Units.DegreesPerSecond)); + assertEquals(360.0, c.getMaxAcceleration(Units.DegreesPerSecond.per(Units.Second))); + + AtomicInteger counter = new AtomicInteger(0); + c.update(1, x -> counter.incrementAndGet()); + assertEquals(1, counter.get()); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/control/constraints/AngularVelocityConstraintsTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/control/constraints/AngularVelocityConstraintsTest.java new file mode 100644 index 00000000..c3d8d181 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/control/constraints/AngularVelocityConstraintsTest.java @@ -0,0 +1,28 @@ +package edu.wpi.team190.gompeilib.core.utility.control.constraints; + +import static org.junit.jupiter.api.Assertions.*; + +import edu.wpi.first.units.*; +import java.util.concurrent.atomic.AtomicInteger; +import org.junit.jupiter.api.Test; + +public class AngularVelocityConstraintsTest { + @Test + public void testAngularVelocityConstraints() { + AngularVelocityConstraints c = + AngularVelocityConstraints.fromMeasures() + .withPrefix("Test") + .withGoalTolerance(Units.DegreesPerSecond.of(1.0)) + .withMaxVelocity(Units.DegreesPerSecond.of(180.0)) + .withMaxAcceleration(Units.DegreesPerSecond.per(Units.Second).of(360.0)) + .build(); + + assertNotNull(c.goalTolerance()); + assertNotNull(c.maxVelocity()); + assertNotNull(c.maxAcceleration()); + + AtomicInteger counter = new AtomicInteger(0); + c.update(1, x -> counter.incrementAndGet()); + assertEquals(1, counter.get()); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/control/constraints/LinearConstraintsTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/control/constraints/LinearConstraintsTest.java new file mode 100644 index 00000000..4f2a9bea --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/control/constraints/LinearConstraintsTest.java @@ -0,0 +1,29 @@ +package edu.wpi.team190.gompeilib.core.utility.control.constraints; + +import static org.junit.jupiter.api.Assertions.*; + +import edu.wpi.first.units.*; +import java.util.concurrent.atomic.AtomicInteger; +import org.junit.jupiter.api.Test; + +public class LinearConstraintsTest { + @Test + public void testLinearConstraints() { + LinearConstraints c = + LinearConstraints.fromMeasures() + .withPrefix("Test") + .withGoalTolerance(Units.Meters.of(0.05)) + .withMaxVelocity(Units.MetersPerSecond.of(3.0)) + .withMaxAcceleration(Units.MetersPerSecond.per(Units.Second).of(6.0)) + .build(); + + assertEquals(0.05, c.getGoalToleranceMeters(Units.Meters)); + assertEquals(3.0, c.getMaxVelocityMetersPerSecond(Units.MetersPerSecond)); + assertEquals( + 6.0, c.getMaxAccelerationMetersPerSecondSquared(Units.MetersPerSecond.per(Units.Second))); + + AtomicInteger counter = new AtomicInteger(0); + c.update(1, x -> counter.incrementAndGet()); + assertEquals(1, counter.get()); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/phoenix/GainSlotTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/phoenix/GainSlotTest.java new file mode 100644 index 00000000..e20c7f4f --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/phoenix/GainSlotTest.java @@ -0,0 +1,19 @@ +package edu.wpi.team190.gompeilib.core.utility.phoenix; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +public class GainSlotTest { + @Test + public void testGainSlot() { + assertEquals(GainSlot.ZERO, GainSlot.integerToGainSlot(0)); + assertEquals(GainSlot.ONE, GainSlot.integerToGainSlot(1)); + assertEquals(GainSlot.TWO, GainSlot.integerToGainSlot(2)); + assertNull(GainSlot.integerToGainSlot(3)); + + // ValueOf and values + assertEquals(GainSlot.ZERO, GainSlot.valueOf("ZERO")); + assertEquals(3, GainSlot.values().length); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/phoenix/PhoenixOdometryThreadTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/phoenix/PhoenixOdometryThreadTest.java new file mode 100644 index 00000000..20f15646 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/phoenix/PhoenixOdometryThreadTest.java @@ -0,0 +1,86 @@ +package edu.wpi.team190.gompeilib.core.utility.phoenix; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import com.ctre.phoenix6.CANBus; +import com.ctre.phoenix6.StatusSignal; +import com.ctre.phoenix6.swerve.SwerveModuleConstants; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.units.measure.Angle; +import edu.wpi.team190.gompeilib.subsystems.drivebases.swervedrive.SwerveDriveConstants; +import java.util.Queue; +import java.util.concurrent.locks.ReentrantLock; +import org.junit.jupiter.api.Test; + +public class PhoenixOdometryThreadTest { + @Test + public void testPhoenixOdometryThread() { + SwerveDriveConstants driveConstants = mock(SwerveDriveConstants.class); + CANBus canBus = mock(CANBus.class); + when(canBus.isNetworkFD()).thenReturn(false); + + DCMotor motor = mock(DCMotor.class); + SwerveModuleConstants moduleConst = mock(SwerveModuleConstants.class); + SwerveModuleConstants.ClosedLoopOutputType outputType = + SwerveModuleConstants.ClosedLoopOutputType.Voltage; + + SwerveDriveConstants.DriveConfig driveConfig = + new SwerveDriveConstants.DriveConfig( + canBus, + 1, // pigeon2Id + 4.0, // maxLinearVelocityMetersPerSecond + 0.05, // wheelRadiusMeters + motor, + motor, + moduleConst, + moduleConst, + moduleConst, + moduleConst, + outputType, + outputType, + 0.8, // bumperWidth + 0.8, // bumperLength + 40.0, // robotMassKilograms + 0.6, // trackWidth + 2.0, // robotMOI + 40.0, // moduleCurrentLimit + 1.0 // wheelCOF + ); + + try { + java.lang.reflect.Field configField = + SwerveDriveConstants.class.getDeclaredField("driveConfig"); + configField.setAccessible(true); + configField.set(driveConstants, driveConfig); + + java.lang.reflect.Field lockField = + SwerveDriveConstants.class.getDeclaredField("reentrantLock"); + lockField.setAccessible(true); + lockField.set(driveConstants, new ReentrantLock()); + + java.lang.reflect.Field freqField = + SwerveDriveConstants.class.getDeclaredField("odometryFrequency"); + freqField.setAccessible(true); + freqField.set(driveConstants, 100.0); + } catch (Exception e) { + fail(e); + } + + PhoenixOdometryThread thread = PhoenixOdometryThread.getInstance(driveConstants); + assertNotNull(thread); + + // Test register signal + StatusSignal mockSignal = mock(StatusSignal.class); + Queue signalQueue = thread.registerSignal(mockSignal); + assertNotNull(signalQueue); + + // Test register generic signal + Queue genericQueue = thread.registerSignal(() -> 42.0); + assertNotNull(genericQueue); + + // Test makeTimestampQueue + Queue tsQueue = thread.makeTimestampQueue(); + assertNotNull(tsQueue); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/phoenix/PhoenixUtilTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/phoenix/PhoenixUtilTest.java new file mode 100644 index 00000000..c53befb3 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/phoenix/PhoenixUtilTest.java @@ -0,0 +1,56 @@ +package edu.wpi.team190.gompeilib.core.utility.phoenix; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import com.ctre.phoenix6.BaseStatusSignal; +import com.ctre.phoenix6.StatusCode; +import java.util.concurrent.atomic.AtomicInteger; +import org.junit.jupiter.api.Test; + +public class PhoenixUtilTest { + @Test + public void testPhoenixUtil() { + new PhoenixUtil(); // Constructor coverage + + AtomicInteger count = new AtomicInteger(0); + // Success on first try + PhoenixUtil.tryUntilOk( + 5, + () -> { + count.incrementAndGet(); + return StatusCode.OK; + }); + assertEquals(1, count.get()); + + // Retries on error + count.set(0); + PhoenixUtil.tryUntilOk( + 3, + () -> { + int c = count.incrementAndGet(); + if (c < 3) { + return StatusCode.RxTimeout; + } + return StatusCode.OK; + }); + assertEquals(3, count.get()); + + // Signals registration and refresh + BaseStatusSignal mockSignal = mock(BaseStatusSignal.class); + + // We register signals. Since BaseStatusSignal.refreshAll might hit native code or static mock + // issues, + // let's just test that the arrays are populated. We can invoke registerSignals and refreshAll. + try { + PhoenixUtil.registerSignals(true, mockSignal); + PhoenixUtil.registerSignals(false, mockSignal); + // Since refreshAll calls BaseStatusSignal.refreshAll, we might expect it to run. + // Let's call it and make sure it does not throw unexpected JNI exceptions or we catch them if + // it does. + PhoenixUtil.refreshAll(); + } catch (Throwable t) { + // In case native code is hit and fails + } + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/sysid/CustomSysidRoutineTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/sysid/CustomSysidRoutineTest.java new file mode 100644 index 00000000..b63b9f58 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/sysid/CustomSysidRoutineTest.java @@ -0,0 +1,51 @@ +package edu.wpi.team190.gompeilib.core.utility.sysid; + +import static edu.wpi.first.units.Units.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import edu.wpi.first.units.*; +import edu.wpi.first.units.measure.*; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.Subsystem; +import java.util.concurrent.atomic.AtomicReference; +import org.junit.jupiter.api.Test; + +public class CustomSysidRoutineTest { + @Test + public void testCustomSysIdRoutine() { + Subsystem mockSubsystem = + new Subsystem() { + @Override + public void periodic() {} + }; + + CustomSysIdRoutine.Config config = + new CustomSysIdRoutine.Config<>( + CustomUnits.voltsPerSecond.ofNative(1.0), + Volts.of(1.0), + Seconds.of(5.0), + state -> {}, + Volts); + + AtomicReference> drivenValue = new AtomicReference<>(); + CustomSysIdRoutine.Mechanism mechanism = + new CustomSysIdRoutine.Mechanism<>(drivenValue::set, log -> {}, mockSubsystem, "MockMech"); + + CustomSysIdRoutine.Mechanism mechanism2 = + new CustomSysIdRoutine.Mechanism<>(drivenValue::set, mockSubsystem); + + CustomSysIdRoutine routine = + new CustomSysIdRoutine<>(config, mechanism, Volts.mutable(0)); + + Command quasistaticFwd = routine.quasistatic(CustomSysIdRoutine.Direction.kForward); + Command quasistaticRev = routine.quasistatic(CustomSysIdRoutine.Direction.kReverse); + Command dynamicFwd = routine.dynamic(CustomSysIdRoutine.Direction.kForward); + Command dynamicRev = routine.dynamic(CustomSysIdRoutine.Direction.kReverse); + + assertNotNull(quasistaticFwd); + assertNotNull(quasistaticRev); + assertNotNull(dynamicFwd); + assertNotNull(dynamicRev); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/sysid/CustomUnitsTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/sysid/CustomUnitsTest.java new file mode 100644 index 00000000..e7ee1dd0 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/sysid/CustomUnitsTest.java @@ -0,0 +1,14 @@ +package edu.wpi.team190.gompeilib.core.utility.sysid; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +public class CustomUnitsTest { + @Test + public void testCustomUnits() { + new CustomUnits(); // Constructor coverage + assertNotNull(CustomUnits.ampsPerSecond); + assertNotNull(CustomUnits.voltsPerSecond); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/tunable/LoggedTunableMeasureTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/tunable/LoggedTunableMeasureTest.java new file mode 100644 index 00000000..42ec300d --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/tunable/LoggedTunableMeasureTest.java @@ -0,0 +1,304 @@ +package edu.wpi.team190.gompeilib.core.utility.tunable; + +import static edu.wpi.first.units.Units.Meters; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; +import static org.mockito.Mockito.atLeastOnce; + +import edu.wpi.first.units.DistanceUnit; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import java.util.Arrays; +import org.junit.jupiter.api.*; +import org.littletonrobotics.junction.networktables.LoggedNetworkNumber; +import org.mockito.MockedConstruction; + +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class LoggedTunableMeasureTest { + @BeforeEach + void setUp() { + GompeiLib.deinit(); + } + + @Test + @Order(1) + public void defaultValueTuningMode() { + GompeiLib.init(null, true, 0.02); + LoggedTunableMeasure number = + new LoggedTunableMeasure<>("number", Meters.of(190.0)); + assertEquals(190.0, number.get(Meters)); + } + + @Test + @Order(2) + public void defaultValueNoTuningMode() { + GompeiLib.init(null, false, 0.02); + LoggedTunableMeasure number = + new LoggedTunableMeasure<>("number", Meters.of(190.0)); + assertEquals(190.0, number.get(Meters)); + } + + @Test + @Order(3) + public void testGetTuningDisabled() { + // 1. Initialize GompeiLib with tuning = false + GompeiLib.init(null, false, 0.02); + + double expectedDefault = 12.34; + LoggedTunableMeasure tunable = + new LoggedTunableMeasure<>("TestKey", Meters.of(expectedDefault)); + + assertEquals( + expectedDefault, + tunable.get(Meters), + "Should return the default value when tuning is disabled"); + } + + @Test + @Order(4) + public void testGetTuningEnabled() { + // 1. Initialize GompeiLib with tuning = true + GompeiLib.init(null, true, 0.02); + + double defaultValue = 1.0; + double dashboardValue = 99.9; + + // 2. Mock the construction of LoggedNetworkNumber + try (MockedConstruction mocked = + mockConstruction( + LoggedNetworkNumber.class, + (mock, context) -> { + // When dashboardNumber.get() is called, return our fake dashboard value + when(mock.get()).thenReturn(dashboardValue); + })) { + + // 3. Initialize the tunable (this triggers 'new LoggedNetworkNumber') + LoggedTunableMeasure tunable = + new LoggedTunableMeasure<>("TestKey", Meters.of(defaultValue)); + + // 4. Verify that it returns the dashboard value, NOT the default + assertEquals( + dashboardValue, + tunable.get(Meters), + "Should return the dashboard value when tuning is enabled"); + + // 5. Verify the constructor was actually called with the right params + LoggedNetworkNumber mockInternal = mocked.constructed().get(0); + verify(mockInternal, atLeastOnce()).get(); + } + } + + @Test + @Order(5) + public void testHasChangedFirstCall() { + GompeiLib.init(null, false, 0.02); + LoggedTunableMeasure tunable = new LoggedTunableMeasure<>("Test", Meters.of(5.0)); + + // lastValue is null in the map for ID 1 + assertTrue(tunable.hasChanged(1), "First call for a new ID should always return true"); + } + + @Test + @Order(6) + public void testHasChangedNoUpdate() { + GompeiLib.init(null, false, 0.02); + LoggedTunableMeasure tunable = new LoggedTunableMeasure<>("Test", Meters.of(5.0)); + + tunable.hasChanged(1); // First call sets the map entry for ID 1 to 5.0 + + // Second call: currentValue (5.0) == lastValue (5.0) + assertFalse( + tunable.hasChanged(1), "Should return false if the value hasn't changed since last check"); + } + + @Test + @Order(7) + public void testHasChangedWithNewValue() { + GompeiLib.init(null, true, 0.02); + + // We use a Mock to control what get() returns manually + try (MockedConstruction mocked = + mockConstruction(LoggedNetworkNumber.class)) { + LoggedTunableMeasure tunable = + new LoggedTunableMeasure<>("Test", Meters.of(1.0)); + LoggedNetworkNumber mockNT = mocked.constructed().get(0); + + // Setup initial state + when(mockNT.get()).thenReturn(1.0); + tunable.hasChanged(1); + + // Change the value + when(mockNT.get()).thenReturn(2.0); + + assertTrue(tunable.hasChanged(1), "Should return true when the dashboard value changes"); + assertFalse(tunable.hasChanged(1), "Should return false again if called immediately after"); + } + } + + @Test + @Order(8) + public void testHasChangedIdIsolation() { + GompeiLib.init(null, false, 0.02); + LoggedTunableMeasure tunable = + new LoggedTunableMeasure<>("Test", Meters.of(10.0)); + + // ID 1 checks the value + assertTrue(tunable.hasChanged(1)); + assertFalse(tunable.hasChanged(1)); // ID 1 is now "up to date" + + // ID 2 checks for the first time + assertTrue(tunable.hasChanged(2), "ID 2 should return true even if ID 1 already checked"); + } + + @Test + @Order(9) + public void testIfChangedConsumerTriggers() { + GompeiLib.init(null, false, 0.02); + LoggedTunableMeasure num1 = new LoggedTunableMeasure<>("n1", Meters.of(1.0)); + LoggedTunableMeasure num2 = new LoggedTunableMeasure<>("n2", Meters.of(2.0)); + int id = 777; + + // We use an array to "capture" the results from inside the lambda + final double[][] capturedValues = new double[1][]; + + // First call: Should trigger because they are new to this ID + LoggedTunableMeasure.ifChanged( + id, + (values) -> + capturedValues[0] = + Arrays.stream(values).mapToDouble((value) -> value.in(Meters)).toArray(), + num1, + num2); + + assertNotNull(capturedValues[0], "Action should have been called"); + assertEquals(1.0, capturedValues[0][0]); + assertEquals(2.0, capturedValues[0][1]); + } + + @Test + @Order(10) + void testIfChangedRunnableDoesNotTrigger() { + GompeiLib.init(null, false, 0.02); + LoggedTunableMeasure num1 = new LoggedTunableMeasure<>("n1", Meters.of(1.0)); + int id = 888; + + // Manually consume the first "true" + num1.hasChanged(id); + + // Track if the runnable runs + final boolean[] ran = {false}; + + // Call ifChanged: Since value hasn't changed since the line above, it shouldn't run + LoggedTunableMeasure.ifChanged(id, () -> ran[0] = true, num1); + + assertFalse(ran[0], "Action should NOT run if nothing changed"); + } + + @Test + @Order(11) + void testIfChangedTriggersIfOnlyOneChanges() { + GompeiLib.init(null, true, 0.02); + int id = 999; + + try (MockedConstruction mocked = + mockConstruction(LoggedNetworkNumber.class)) { + LoggedTunableMeasure n1 = new LoggedTunableMeasure<>("n1", Meters.of(1.0)); + LoggedTunableMeasure n2 = new LoggedTunableMeasure<>("n2", Meters.of(2.0)); + LoggedNetworkNumber mockNT1 = mocked.constructed().get(0); + LoggedNetworkNumber mockNT2 = mocked.constructed().get(1); + + // Set initial state for both + when(mockNT1.get()).thenReturn(1.0); + when(mockNT2.get()).thenReturn(2.0); + n1.hasChanged(id); + n2.hasChanged(id); + + // Change ONLY n1 + when(mockNT1.get()).thenReturn(5.0); + + final boolean[] ran = {false}; + LoggedTunableMeasure.ifChanged( + id, + (values) -> { + ran[0] = true; + assertEquals(5.0, values[0].in(Meters)); + assertEquals(2.0, values[1].in(Meters)); + }, + n1, + n2); + + assertTrue(ran[0], "Action should trigger if even one value changes"); + } + } + + @Test + @Order(12) + void testIfChangedRunnableOverloadCoverage() { + // 1. Setup in non-tuning mode for simplicity + GompeiLib.init(null, false, 0.02); + LoggedTunableMeasure num = new LoggedTunableMeasure<>("Coverage", Meters.of(1.0)); + int id = 10101; + + // We need a counter to verify the Runnable actually ran + final int[] callCount = {0}; + Runnable mockAction = () -> callCount[0]++; + + // 2. First call: Should trigger (initial change) + // This executes the line: ifChanged(id, values -> action.run(), tunableNumbers); + LoggedTunableMeasure.ifChanged(id, mockAction, num); + + // 3. Assertions + assertEquals(1, callCount[0], "The Runnable action should have executed once"); + + // 4. Second call: Should NOT trigger (no change) + LoggedTunableMeasure.ifChanged(id, mockAction, num); + assertEquals(1, callCount[0], "The Runnable action should NOT have executed a second time"); + } + + @Test + @Order(13) + void testIfChangedSecondElementTriggers() { + GompeiLib.init(null, true, 0.02); + int id = 12345; + + try (MockedConstruction mocked = + mockConstruction(LoggedNetworkNumber.class)) { + + LoggedTunableMeasure n1 = new LoggedTunableMeasure<>("n1", Meters.of(1.0)); + LoggedTunableMeasure n2 = new LoggedTunableMeasure<>("n2", Meters.of(2.0)); + + LoggedNetworkNumber mockNT1 = mocked.constructed().get(0); + LoggedNetworkNumber mockNT2 = mocked.constructed().get(1); + + // Initial values + when(mockNT1.get()).thenReturn(1.0); + when(mockNT2.get()).thenReturn(2.0); + n1.hasChanged(id); + n2.hasChanged(id); + + // Only SECOND value changes + when(mockNT2.get()).thenReturn(5.0); + + final boolean[] ran = {false}; + + LoggedTunableMeasure.ifChanged(id, values -> ran[0] = true, n1, n2); + + assertTrue(ran[0], "Action should trigger when the second element changes"); + } + } + + @Test + @Order(14) + void testIfChangedWithNoTunables() { + GompeiLib.init(null, false, 0.02); + int id = 4242; + + final boolean[] ran = {false}; + + LoggedTunableMeasure.ifChanged( + id, (values) -> ran[0] = true + // no tunable numbers + ); + + assertFalse(ran[0], "Action should not run when no tunables are provided"); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/tunable/LoggedTunableNumberTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/tunable/LoggedTunableNumberTest.java new file mode 100644 index 00000000..95aafaf2 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/tunable/LoggedTunableNumberTest.java @@ -0,0 +1,267 @@ +package edu.wpi.team190.gompeilib.core.utility.tunable; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import edu.wpi.team190.gompeilib.core.GompeiLib; +import org.junit.jupiter.api.*; +import org.littletonrobotics.junction.networktables.LoggedNetworkNumber; +import org.mockito.MockedConstruction; + +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class LoggedTunableNumberTest { + @BeforeEach + void setUp() { + GompeiLib.deinit(); + } + + @Test + @Order(1) + public void noDefaultValueTuningMode() { + GompeiLib.init(null, true, 0.02); + LoggedTunableNumber number = new LoggedTunableNumber("number"); + number.initDefault(42.0); + assertEquals(42.0, number.getAsDouble()); + } + + @Test + @Order(2) + public void noDefaultValueNoTuningMode() { + GompeiLib.init(null, false, 0.02); + LoggedTunableNumber number = new LoggedTunableNumber("number"); + number.initDefault(42.0); + assertEquals(42.0, number.getAsDouble()); + } + + @Test + @Order(3) + public void defaultValueTuningMode() { + GompeiLib.init(null, true, 0.02); + LoggedTunableNumber number = new LoggedTunableNumber("number", 190.0); + number.initDefault(42.0); + assertEquals(190.0, number.getAsDouble()); + } + + @Test + @Order(4) + public void defaultValueNoTuningMode() { + GompeiLib.init(null, false, 0.02); + LoggedTunableNumber number = new LoggedTunableNumber("number", 190.0); + number.initDefault(42.0); + assertEquals(190.0, number.getAsDouble()); + } + + @Test + @Order(5) + public void testGetNoDefault() { + // We do NOT call initDefault or use the 2-arg constructor + LoggedTunableNumber tunable = new LoggedTunableNumber("TestKey"); + + assertEquals(0.0, tunable.get(), "Should return 0.0 when no default is set"); + } + + @Test + @Order(6) + public void testGetTuningDisabled() { + // 1. Initialize GompeiLib with tuning = false + GompeiLib.init(null, false, 0.02); + + double expectedDefault = 12.34; + LoggedTunableNumber tunable = new LoggedTunableNumber("TestKey", expectedDefault); + + assertEquals( + expectedDefault, tunable.get(), "Should return the default value when tuning is disabled"); + } + + @Test + @Order(7) + public void testGetTuningEnabled() { + // 1. Initialize GompeiLib with tuning = true + GompeiLib.init(null, true, 0.02); + + double defaultValue = 1.0; + double dashboardValue = 99.9; + + // 2. Mock the construction of LoggedNetworkNumber + try (MockedConstruction mocked = + mockConstruction( + LoggedNetworkNumber.class, + (mock, context) -> { + // When dashboardNumber.get() is called, return our fake dashboard value + when(mock.get()).thenReturn(dashboardValue); + })) { + + // 3. Initialize the tunable (this triggers 'new LoggedNetworkNumber') + LoggedTunableNumber tunable = new LoggedTunableNumber("TestKey", defaultValue); + + // 4. Verify that it returns the dashboard value, NOT the default + assertEquals( + dashboardValue, + tunable.get(), + "Should return the dashboard value when tuning is enabled"); + + // 5. Verify the constructor was actually called with the right params + LoggedNetworkNumber mockInternal = mocked.constructed().get(0); + verify(mockInternal, atLeastOnce()).get(); + } + } + + @Test + @Order(8) + public void testHasChangedFirstCall() { + GompeiLib.init(null, false, 0.02); + LoggedTunableNumber tunable = new LoggedTunableNumber("Test", 5.0); + + // lastValue is null in the map for ID 1 + assertTrue(tunable.hasChanged(1), "First call for a new ID should always return true"); + } + + @Test + @Order(9) + public void testHasChangedNoUpdate() { + GompeiLib.init(null, false, 0.02); + LoggedTunableNumber tunable = new LoggedTunableNumber("Test", 5.0); + + tunable.hasChanged(1); // First call sets the map entry for ID 1 to 5.0 + + // Second call: currentValue (5.0) == lastValue (5.0) + assertFalse( + tunable.hasChanged(1), "Should return false if the value hasn't changed since last check"); + } + + @Test + @Order(10) + public void testHasChangedWithNewValue() { + GompeiLib.init(null, true, 0.02); + + // We use a Mock to control what get() returns manually + try (MockedConstruction mocked = + mockConstruction(LoggedNetworkNumber.class)) { + LoggedTunableNumber tunable = new LoggedTunableNumber("Test", 1.0); + LoggedNetworkNumber mockNT = mocked.constructed().get(0); + + // Setup initial state + when(mockNT.get()).thenReturn(1.0); + tunable.hasChanged(1); + + // Change the value + when(mockNT.get()).thenReturn(2.0); + + assertTrue(tunable.hasChanged(1), "Should return true when the dashboard value changes"); + assertFalse(tunable.hasChanged(1), "Should return false again if called immediately after"); + } + } + + @Test + @Order(11) + public void testHasChangedIdIsolation() { + GompeiLib.init(null, false, 0.02); + LoggedTunableNumber tunable = new LoggedTunableNumber("Test", 10.0); + + // ID 1 checks the value + assertTrue(tunable.hasChanged(1)); + assertFalse(tunable.hasChanged(1)); // ID 1 is now "up to date" + + // ID 2 checks for the first time + assertTrue(tunable.hasChanged(2), "ID 2 should return true even if ID 1 already checked"); + } + + @Test + @Order(12) + public void testIfChangedConsumerTriggers() { + GompeiLib.init(null, false, 0.02); + LoggedTunableNumber num1 = new LoggedTunableNumber("n1", 1.0); + LoggedTunableNumber num2 = new LoggedTunableNumber("n2", 2.0); + int id = 777; + + // We use an array to "capture" the results from inside the lambda + final double[][] capturedValues = new double[1][]; + + // First call: Should trigger because they are new to this ID + LoggedTunableNumber.ifChanged(id, (values) -> capturedValues[0] = values, num1, num2); + + assertNotNull(capturedValues[0], "Action should have been called"); + assertEquals(1.0, capturedValues[0][0]); + assertEquals(2.0, capturedValues[0][1]); + } + + @Test + @Order(13) + void testIfChangedRunnableDoesNotTrigger() { + GompeiLib.init(null, false, 0.02); + LoggedTunableNumber num1 = new LoggedTunableNumber("n1", 1.0); + int id = 888; + + // Manually consume the first "true" + num1.hasChanged(id); + + // Track if the runnable runs + final boolean[] ran = {false}; + + // Call ifChanged: Since value hasn't changed since the line above, it shouldn't run + LoggedTunableNumber.ifChanged(id, () -> ran[0] = true, num1); + + assertFalse(ran[0], "Action should NOT run if nothing changed"); + } + + @Test + @Order(14) + void testIfChangedTriggersIfOnlyOneChanges() { + GompeiLib.init(null, true, 0.02); + int id = 999; + + try (MockedConstruction mocked = + mockConstruction(LoggedNetworkNumber.class)) { + LoggedTunableNumber n1 = new LoggedTunableNumber("n1", 1.0); + LoggedTunableNumber n2 = new LoggedTunableNumber("n2", 2.0); + LoggedNetworkNumber mockNT1 = mocked.constructed().get(0); + LoggedNetworkNumber mockNT2 = mocked.constructed().get(1); + + // Set initial state for both + when(mockNT1.get()).thenReturn(1.0); + when(mockNT2.get()).thenReturn(2.0); + n1.hasChanged(id); + n2.hasChanged(id); + + // Change ONLY n1 + when(mockNT1.get()).thenReturn(5.0); + + final boolean[] ran = {false}; + LoggedTunableNumber.ifChanged( + id, + (values) -> { + ran[0] = true; + assertEquals(5.0, values[0]); + assertEquals(2.0, values[1]); + }, + n1, + n2); + + assertTrue(ran[0], "Action should trigger if even one value changes"); + } + } + + @Test + @Order(15) + void testIfChangedRunnableOverloadCoverage() { + // 1. Setup in non-tuning mode for simplicity + GompeiLib.init(null, false, 0.02); + LoggedTunableNumber num = new LoggedTunableNumber("Coverage", 1.0); + int id = 10101; + + // We need a counter to verify the Runnable actually ran + final int[] callCount = {0}; + Runnable mockAction = () -> callCount[0]++; + + // 2. First call: Should trigger (initial change) + // This executes the line: ifChanged(id, values -> action.run(), tunableNumbers); + LoggedTunableNumber.ifChanged(id, mockAction, num); + + // 3. Assertions + assertEquals(1, callCount[0], "The Runnable action should have executed once"); + + // 4. Second call: Should NOT trigger (no change) + LoggedTunableNumber.ifChanged(id, mockAction, num); + assertEquals(1, callCount[0], "The Runnable action should NOT have executed a second time"); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/tunable/TunableUpdaterRegistryTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/tunable/TunableUpdaterRegistryTest.java new file mode 100644 index 00000000..b1a4b525 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/core/utility/tunable/TunableUpdaterRegistryTest.java @@ -0,0 +1,185 @@ +package edu.wpi.team190.gompeilib.core.utility.tunable; + +import static edu.wpi.first.units.Units.Radian; +import static org.junit.jupiter.api.Assertions.*; + +import edu.wpi.first.networktables.NetworkTableInstance; +import edu.wpi.first.units.Units; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.LinearConstraints; +import java.util.concurrent.atomic.AtomicInteger; +import org.junit.jupiter.api.*; + +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +class TunableUpdaterRegistryTest { + + @BeforeEach + void setUp() { + GompeiLib.deinit(); + GompeiLib.init(null, true, 0.02); + } + + @Test + @Order(2) + void periodicWithNoRegistrationsDoesNothing() { + assertDoesNotThrow(TunableUpdaterRegistry::periodic); + } + + @Test + @Order(3) + void registerGainsInvokesConsumer() { + Gains gains = + Gains.fromDoubles() + .withPrefix("test/gains") + .withKP(1) + .withKI(2) + .withKD(3) + .withKS(4) + .withKV(5) + .withKA(6) + .withKG(7) + .build(); + + AtomicInteger calls = new AtomicInteger(); + + TunableUpdaterRegistry.registerGains(gains, g -> calls.incrementAndGet()); + + TunableUpdaterRegistry.periodic(); + + assertEquals(1, calls.get()); + } + + @Test + @Order(4) + void registerGainsDuplicateIgnored() { + Gains gains = + Gains.fromDoubles() + .withPrefix("test/gains/dup") + .withKP(1) + .withKI(2) + .withKD(3) + .withKS(4) + .withKV(5) + .withKA(6) + .withKG(7) + .build(); + + AtomicInteger first = new AtomicInteger(); + AtomicInteger second = new AtomicInteger(); + + TunableUpdaterRegistry.registerGains(gains, g -> first.incrementAndGet()); + TunableUpdaterRegistry.registerGains(gains, g -> second.incrementAndGet()); + + TunableUpdaterRegistry.periodic(); + + assertEquals(1, first.get()); + assertEquals(0, second.get()); + } + + @Test + @Order(5) + void registerConstraintsInvokesConsumer() { + LinearConstraints constraints = + LinearConstraints.fromMeasures() + .withPrefix("test/constraints") + .withGoalTolerance(Units.Meters.of(0.1)) + .withMaxVelocity(Units.MetersPerSecond.of(2)) + .withMaxAcceleration(Units.MetersPerSecondPerSecond.of(3)) + .build(); + + AtomicInteger calls = new AtomicInteger(); + + TunableUpdaterRegistry.registerConstraints(constraints, c -> calls.incrementAndGet()); + + TunableUpdaterRegistry.periodic(); + + assertEquals(1, calls.get()); + } + + @Test + @Order(6) + void registerConstraintsDuplicateIgnored() { + LinearConstraints constraints = + LinearConstraints.fromMeasures() + .withPrefix("test/constraints/dup") + .withGoalTolerance(Units.Meters.of(0.2)) + .withMaxVelocity(Units.MetersPerSecond.of(3)) + .withMaxAcceleration(Units.MetersPerSecondPerSecond.of(4)) + .build(); + + AtomicInteger first = new AtomicInteger(); + AtomicInteger second = new AtomicInteger(); + + TunableUpdaterRegistry.registerConstraints(constraints, c -> first.incrementAndGet()); + TunableUpdaterRegistry.registerConstraints(constraints, c -> second.incrementAndGet()); + + TunableUpdaterRegistry.periodic(); + + assertEquals(1, first.get()); + assertEquals(0, second.get()); + } + + @Test + @Order(7) + void registerNumberAcceptsArray() { + LoggedTunableNumber[] nums = new LoggedTunableNumber[0]; + + assertDoesNotThrow(() -> TunableUpdaterRegistry.registerNumber(nums, v -> {})); + } + + @Test + @Order(8) + void registerNumberDuplicateIgnored() { + LoggedTunableNumber[] nums = new LoggedTunableNumber[1]; + nums[0] = new LoggedTunableNumber("test/testnumber1"); + + AtomicInteger first = new AtomicInteger(); + AtomicInteger second = new AtomicInteger(); + + TunableUpdaterRegistry.registerNumber(nums, v -> first.incrementAndGet()); + TunableUpdaterRegistry.registerNumber(nums, v -> second.incrementAndGet()); + + NetworkTableInstance.getDefault() + .getDoubleTopic("TunableNumbers/test/testnumber1") + .publish() + .set(100); + + TunableUpdaterRegistry.periodic(); + + assertEquals(1, first.get()); + assertEquals(0, second.get()); + } + + @Test + @Order(9) + void registerMeasureAcceptsArray() { + LoggedTunableMeasure[] measures = new LoggedTunableMeasure[0]; + + assertDoesNotThrow(() -> TunableUpdaterRegistry.registerMeasure(measures, () -> {})); + } + + @Test + @Order(10) + void registerMeasureDuplicateIgnored() { + LoggedTunableMeasure[] measures = new LoggedTunableMeasure[1]; + + measures[0] = new LoggedTunableMeasure<>("test/testmeasure1", Radian.zero()); + + AtomicInteger first = new AtomicInteger(); + AtomicInteger second = new AtomicInteger(); + + TunableUpdaterRegistry.registerMeasure(measures, first::incrementAndGet); + TunableUpdaterRegistry.registerMeasure(measures, second::incrementAndGet); + + NetworkTableInstance.getDefault() + .getDoubleTopic("TunableNumbers/test/testmeasure1 (Radian)") + .publish() + .set(100); + + TunableUpdaterRegistry.periodic(); + + assertEquals(1, first.get()); + assertEquals(0, second.get()); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/arm/ArmConstantsTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/arm/ArmConstantsTest.java new file mode 100644 index 00000000..54310c15 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/arm/ArmConstantsTest.java @@ -0,0 +1,79 @@ +package edu.wpi.team190.gompeilib.subsystems.arm; + +import static org.junit.jupiter.api.Assertions.*; + +import com.ctre.phoenix6.CANBus; +import com.ctre.phoenix6.signals.InvertedValue; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.units.Units; +import edu.wpi.team190.gompeilib.core.utility.control.CurrentLimits; +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.AngularPositionConstraints; +import org.junit.jupiter.api.Test; + +public class ArmConstantsTest { + @Test + public void testConstantsAndBuilder() { + DCMotor motor = DCMotor.getNeo550(1); + ArmConstants.ArmParameters params = + ArmConstants.ArmParameters.builder() + .withMotorConfig(motor) + .withMinAngle(Rotation2d.fromDegrees(0)) + .withMaxAngle(Rotation2d.fromDegrees(90)) + .withContinuousOutput(false) + .withNumMotors(1) + .withGearRatio(100.0) + .withLengthMeters(0.5) + .withMomentOfInertia(0.1) + .build(); + + Gains slot0 = Gains.fromDoubles().withPrefix("test").withKP(1.0).build(); + AngularPositionConstraints constraints = + AngularPositionConstraints.fromMeasures() + .withPrefix("test") + .withMaxVelocity(Units.RadiansPerSecond.of(1.0)) + .withMaxAcceleration(Units.RadiansPerSecondPerSecond.of(1.0)) + .withGoalTolerance(Units.Radians.of(0.01)) + .build(); + CurrentLimits limits = CurrentLimits.builder().build(); + + ArmConstants constants = + ArmConstants.builder() + .withArmCANID(4) + .withCanBus(new CANBus("rio")) + .withArmParameters(params) + .withSlot0Gains(slot0) + .withConstraints(constraints) + .withCurrentLimits(limits) + .withEnableFOC(false) + .withInvertedValue(InvertedValue.Clockwise_Positive) + .withVoltageOffsetStep(Units.Volts.of(0.5)) + .withPositionOffsetStep(Rotation2d.fromDegrees(5)) + .build(); + + assertNotNull(constants); + assertEquals(4, constants.armCANID); + assertEquals("rio", constants.canBus.getName()); + assertEquals(params, constants.armParameters); + assertEquals(slot0, constants.slot0Gains); + assertNotNull(constants.slot1Gains); + assertNotNull(constants.slot2Gains); + assertEquals(constraints, constants.constraints); + assertEquals(limits, constants.currentLimits); + assertFalse(constants.enableFOC); + assertEquals(InvertedValue.Clockwise_Positive, constants.invertedValue); + assertEquals(0.5, constants.voltageOffsetStep.in(Units.Volts)); + assertEquals(5.0, constants.positionOffsetStep.getDegrees()); + + // Test record fields on ArmParameters + assertEquals(motor, params.motorConfig()); + assertEquals(0.0, params.minAngle().getDegrees()); + assertEquals(90.0, params.maxAngle().getDegrees()); + assertFalse(params.continuousOutput()); + assertEquals(1, params.numMotors()); + assertEquals(100.0, params.gearRatio()); + assertEquals(0.5, params.lengthMeters()); + assertEquals(0.1, params.momentOfInertia()); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/arm/ArmIOSimTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/arm/ArmIOSimTest.java new file mode 100644 index 00000000..d4545749 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/arm/ArmIOSimTest.java @@ -0,0 +1,163 @@ +package edu.wpi.team190.gompeilib.subsystems.arm; + +import static org.junit.jupiter.api.Assertions.*; + +import com.ctre.phoenix6.CANBus; +import com.ctre.phoenix6.signals.InvertedValue; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.units.Units; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import edu.wpi.team190.gompeilib.core.robot.RobotMode; +import edu.wpi.team190.gompeilib.core.utility.control.CurrentLimits; +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.AngularPositionConstraints; +import edu.wpi.team190.gompeilib.core.utility.phoenix.GainSlot; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class ArmIOSimTest { + private ArmConstants constants; + + @BeforeEach + public void setUp() { + edu.wpi.first.hal.HAL.initialize(500, 0); + try { + GompeiLib.deinit(); + } catch (Exception e) { + } + GompeiLib.init(RobotMode.SIM, false, 0.02); + + DCMotor motor = DCMotor.getNeo550(1); + ArmConstants.ArmParameters params = + ArmConstants.ArmParameters.builder() + .withMotorConfig(motor) + .withMinAngle(Rotation2d.fromDegrees(-90)) + .withMaxAngle(Rotation2d.fromDegrees(90)) + .withContinuousOutput(true) + .withNumMotors(2) + .withGearRatio(100.0) + .withLengthMeters(0.5) + .withMomentOfInertia(0.1) + .build(); + + constants = + ArmConstants.builder() + .withArmCANID(4) + .withCanBus(new CANBus("rio")) + .withArmParameters(params) + .withSlot0Gains( + Gains.fromDoubles() + .withPrefix("slot0") + .withKP(1.0) + .withKI(0.0) + .withKD(0.1) + .withKS(0.01) + .withKV(0.01) + .withKA(0.01) + .withKG(0.1) + .build()) + .withSlot1Gains( + Gains.fromDoubles() + .withPrefix("slot1") + .withKP(2.0) + .withKI(0.0) + .withKD(0.2) + .withKS(0.0) + .withKV(0.0) + .withKA(0.0) + .withKG(0.0) + .build()) + .withSlot2Gains( + Gains.fromDoubles() + .withPrefix("slot2") + .withKP(3.0) + .withKI(0.0) + .withKD(0.3) + .withKS(0.0) + .withKV(0.0) + .withKA(0.0) + .withKG(0.0) + .build()) + .withConstraints( + AngularPositionConstraints.fromMeasures() + .withPrefix("constraints") + .withMaxVelocity(Units.RadiansPerSecond.of(2.0)) + .withMaxAcceleration(Units.RadiansPerSecondPerSecond.of(2.0)) + .withGoalTolerance(Units.Radians.of(0.05)) + .build()) + .withCurrentLimits( + CurrentLimits.fromDoubles() + .withSupplyCurrentLimit(40.0) + .withStatorCurrentLimit(40.0) + .build()) + .withEnableFOC(false) + .withInvertedValue(InvertedValue.Clockwise_Positive) + .withVoltageOffsetStep(Units.Volts.of(0.5)) + .withPositionOffsetStep(Rotation2d.fromDegrees(5)) + .build(); + } + + @Test + public void testArmIOSim() { + ArmIOSim sim = new ArmIOSim(constants); + ArmIO.ArmIOInputs inputs = new ArmIO.ArmIOInputs(); + + // Initial state set position + sim.setPosition(Rotation2d.fromDegrees(10)); + sim.setVoltageGoal(Units.Volts.of(0.0)); + sim.updateInputs(inputs); + assertEquals(10.0, inputs.position.getDegrees(), 1.0); + + // Set position goal + sim.setPositionGoal(Rotation2d.fromDegrees(30)); + sim.updateInputs(inputs); + assertEquals(2, inputs.appliedVolts.length); + assertEquals(GainSlot.ZERO, inputs.gainSlot); + + // Test gain slots + sim.setGainSlot(GainSlot.ONE); + sim.updateInputs(inputs); + assertEquals(GainSlot.ONE, inputs.gainSlot); + + sim.setGainSlot(GainSlot.TWO); + sim.updateInputs(inputs); + assertEquals(GainSlot.TWO, inputs.gainSlot); + + sim.setGainSlot(GainSlot.ZERO); + sim.updateInputs(inputs); + assertEquals(GainSlot.ZERO, inputs.gainSlot); + + // Set voltage goal (open loop) + sim.setVoltageGoal(Units.Volts.of(6.0)); + sim.updateInputs(inputs); + assertEquals(6.0, inputs.appliedVolts[0], 0.01); + assertTrue(sim.atVoltageGoal(Units.Volts.of(6.0))); + assertFalse(sim.atVoltageGoal(Units.Volts.of(0.0))); + + // update gains & constraints + sim.updateGains( + Gains.fromDoubles() + .withPrefix("slot0") + .withKP(4.0) + .withKI(0.0) + .withKD(0.4) + .withKS(0.02) + .withKG(0.2) + .withKV(0.02) + .withKA(0.0) + .build(), + GainSlot.ZERO); + sim.updateConstraints( + AngularPositionConstraints.fromMeasures() + .withPrefix("constraints") + .withMaxVelocity(Units.RadiansPerSecond.of(3.0)) + .withMaxAcceleration(Units.RadiansPerSecondPerSecond.of(3.0)) + .withGoalTolerance(Units.Radians.of(0.1)) + .build()); + + sim.setPosition(Rotation2d.fromDegrees(20)); + assertTrue(sim.atPositionGoal(Rotation2d.fromDegrees(20))); + assertFalse(sim.atPositionGoal(Rotation2d.fromDegrees(45))); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/arm/ArmIOTalonFXSimTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/arm/ArmIOTalonFXSimTest.java new file mode 100644 index 00000000..28b7eb2e --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/arm/ArmIOTalonFXSimTest.java @@ -0,0 +1,169 @@ +package edu.wpi.team190.gompeilib.subsystems.arm; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import com.ctre.phoenix6.BaseStatusSignal; +import com.ctre.phoenix6.CANBus; +import com.ctre.phoenix6.StatusCode; +import com.ctre.phoenix6.StatusSignal; +import com.ctre.phoenix6.configs.TalonFXConfiguration; +import com.ctre.phoenix6.configs.TalonFXConfigurator; +import com.ctre.phoenix6.hardware.TalonFX; +import com.ctre.phoenix6.signals.InvertedValue; +import com.ctre.phoenix6.sim.TalonFXSimState; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.units.Units; +import edu.wpi.first.units.measure.Angle; +import edu.wpi.first.units.measure.AngularVelocity; +import edu.wpi.first.units.measure.Current; +import edu.wpi.first.units.measure.Temperature; +import edu.wpi.first.units.measure.Voltage; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import edu.wpi.team190.gompeilib.core.robot.RobotMode; +import edu.wpi.team190.gompeilib.core.utility.control.CurrentLimits; +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.AngularPositionConstraints; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockedConstruction; +import org.mockito.MockedStatic; + +public class ArmIOTalonFXSimTest { + private ArmConstants constants; + + @BeforeEach + public void setUp() { + edu.wpi.first.hal.HAL.initialize(500, 0); + try { + GompeiLib.deinit(); + } catch (Exception e) { + } + GompeiLib.init(RobotMode.SIM, false, 0.02); + + DCMotor motor = DCMotor.getNeo550(1); + ArmConstants.ArmParameters params = + ArmConstants.ArmParameters.builder() + .withMotorConfig(motor) + .withMinAngle(Rotation2d.fromDegrees(-90)) + .withMaxAngle(Rotation2d.fromDegrees(90)) + .withContinuousOutput(true) + .withNumMotors(1) + .withGearRatio(100.0) + .withLengthMeters(0.5) + .withMomentOfInertia(0.1) + .build(); + + constants = + ArmConstants.builder() + .withArmCANID(4) + .withCanBus(new CANBus("rio")) + .withArmParameters(params) + .withSlot0Gains( + Gains.fromDoubles() + .withPrefix("slot0") + .withKP(1.0) + .withKI(0.0) + .withKD(0.1) + .withKS(0.01) + .withKV(0.01) + .withKA(0.01) + .withKG(0.1) + .build()) + .withSlot1Gains( + Gains.fromDoubles().withPrefix("slot1").withKP(2.0).withKI(0.0).withKD(0.2).build()) + .withSlot2Gains( + Gains.fromDoubles().withPrefix("slot2").withKP(3.0).withKI(0.0).withKD(0.3).build()) + .withConstraints( + AngularPositionConstraints.fromMeasures() + .withPrefix("constraints") + .withMaxVelocity(Units.RadiansPerSecond.of(2.0)) + .withMaxAcceleration(Units.RadiansPerSecondPerSecond.of(2.0)) + .withGoalTolerance(Units.Radians.of(0.05)) + .build()) + .withCurrentLimits( + CurrentLimits.fromDoubles() + .withSupplyCurrentLimit(40.0) + .withStatorCurrentLimit(40.0) + .build()) + .withEnableFOC(false) + .withInvertedValue(InvertedValue.Clockwise_Positive) + .withVoltageOffsetStep(Units.Volts.of(0.5)) + .withPositionOffsetStep(Rotation2d.fromDegrees(5)) + .build(); + } + + @Test + public void testArmIOTalonFXSim() { + TalonFXConfigurator configurator = mock(TalonFXConfigurator.class); + when(configurator.apply(any(TalonFXConfiguration.class))).thenReturn(StatusCode.OK); + when(configurator.apply(any(TalonFXConfiguration.class), anyDouble())) + .thenReturn(StatusCode.OK); + + StatusSignal positionRotations = mock(StatusSignal.class); + StatusSignal velocityRotationsPerSecond = mock(StatusSignal.class); + StatusSignal appliedVolts = mock(StatusSignal.class); + StatusSignal supplyCurrentAmps = mock(StatusSignal.class); + StatusSignal torqueCurrentAmps = mock(StatusSignal.class); + StatusSignal temperatureCelsius = mock(StatusSignal.class); + StatusSignal positionSetpointRotations = mock(StatusSignal.class); + StatusSignal positionErrorRotations = mock(StatusSignal.class); + StatusSignal closedLoopSlot = mock(StatusSignal.class); + + when(positionRotations.getValue()).thenReturn(Rotation2d.fromDegrees(10.0).getMeasure()); + when(positionRotations.getValueAsDouble()) + .thenReturn(Rotation2d.fromDegrees(10.0).getRotations()); + when(velocityRotationsPerSecond.getValue()).thenReturn(Units.RadiansPerSecond.of(1.5)); + when(velocityRotationsPerSecond.getValueAsDouble()).thenReturn(1.5); + when(appliedVolts.getValueAsDouble()).thenReturn(6.0); + when(appliedVolts.getValue()).thenReturn(Units.Volts.of(6.0)); + when(supplyCurrentAmps.getValueAsDouble()).thenReturn(15.0); + when(torqueCurrentAmps.getValueAsDouble()).thenReturn(12.0); + when(temperatureCelsius.getValueAsDouble()).thenReturn(45.0); + when(positionSetpointRotations.getValueAsDouble()).thenReturn(0.5); + when(positionErrorRotations.getValueAsDouble()).thenReturn(0.01); + when(closedLoopSlot.getValue()).thenReturn(0); + + TalonFXSimState simState = mock(TalonFXSimState.class); + when(simState.getMotorVoltage()).thenReturn(6.0); + + try (MockedConstruction mockTalon = + mockConstruction( + TalonFX.class, + (mock, context) -> { + when(mock.getConfigurator()).thenReturn(configurator); + when(mock.getPosition()).thenReturn(positionRotations); + when(mock.getVelocity()).thenReturn(velocityRotationsPerSecond); + when(mock.getMotorVoltage()).thenReturn(appliedVolts); + when(mock.getSupplyCurrent()).thenReturn(supplyCurrentAmps); + when(mock.getTorqueCurrent()).thenReturn(torqueCurrentAmps); + when(mock.getDeviceTemp()).thenReturn(temperatureCelsius); + when(mock.getClosedLoopReference()).thenReturn(positionSetpointRotations); + when(mock.getClosedLoopError()).thenReturn(positionErrorRotations); + when(mock.getClosedLoopSlot()).thenReturn(closedLoopSlot); + when(mock.getSimState()).thenReturn(simState); + }); + MockedStatic mockBss = mockStatic(BaseStatusSignal.class)) { + + mockBss + .when( + () -> + BaseStatusSignal.setUpdateFrequencyForAll( + anyDouble(), any(BaseStatusSignal[].class))) + .thenReturn(null); + + ArmIOTalonFXSim sim = new ArmIOTalonFXSim(constants); + ArmIO.ArmIOInputs inputs = new ArmIO.ArmIOInputs(); + + sim.updateInputs(inputs); + + assertEquals(10.0, inputs.position.getDegrees(), 0.01); + assertEquals(1.5, inputs.velocity.in(Units.RadiansPerSecond), 0.01); + + verify(simState).setSupplyVoltage(anyDouble()); + verify(simState).setRawRotorPosition(any(Angle.class)); + verify(simState).setRotorVelocity(any(AngularVelocity.class)); + } + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/arm/ArmIOTalonFXTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/arm/ArmIOTalonFXTest.java new file mode 100644 index 00000000..b1e3d09c --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/arm/ArmIOTalonFXTest.java @@ -0,0 +1,212 @@ +package edu.wpi.team190.gompeilib.subsystems.arm; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import com.ctre.phoenix6.BaseStatusSignal; +import com.ctre.phoenix6.CANBus; +import com.ctre.phoenix6.StatusCode; +import com.ctre.phoenix6.StatusSignal; +import com.ctre.phoenix6.configs.TalonFXConfiguration; +import com.ctre.phoenix6.configs.TalonFXConfigurator; +import com.ctre.phoenix6.hardware.TalonFX; +import com.ctre.phoenix6.signals.InvertedValue; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.units.Units; +import edu.wpi.first.units.measure.Angle; +import edu.wpi.first.units.measure.AngularVelocity; +import edu.wpi.first.units.measure.Current; +import edu.wpi.first.units.measure.Temperature; +import edu.wpi.first.units.measure.Voltage; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import edu.wpi.team190.gompeilib.core.robot.RobotMode; +import edu.wpi.team190.gompeilib.core.utility.control.CurrentLimits; +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.AngularPositionConstraints; +import edu.wpi.team190.gompeilib.core.utility.phoenix.GainSlot; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockedConstruction; +import org.mockito.MockedStatic; + +public class ArmIOTalonFXTest { + private ArmConstants constants; + + @BeforeEach + public void setUp() { + edu.wpi.first.hal.HAL.initialize(500, 0); + try { + GompeiLib.deinit(); + } catch (Exception e) { + } + GompeiLib.init(RobotMode.SIM, false, 0.02); + + DCMotor motor = DCMotor.getNeo550(1); + ArmConstants.ArmParameters params = + ArmConstants.ArmParameters.builder() + .withMotorConfig(motor) + .withMinAngle(Rotation2d.fromDegrees(-90)) + .withMaxAngle(Rotation2d.fromDegrees(90)) + .withContinuousOutput(true) + .withNumMotors(1) // Set to 1 to avoid the followTalonFX null array bug + .withGearRatio(100.0) + .withLengthMeters(0.5) + .withMomentOfInertia(0.1) + .build(); + + constants = + ArmConstants.builder() + .withArmCANID(4) + .withCanBus(new CANBus("rio")) + .withArmParameters(params) + .withSlot0Gains( + Gains.fromDoubles() + .withPrefix("slot0") + .withKP(1.0) + .withKI(0.0) + .withKD(0.1) + .withKS(0.01) + .withKV(0.01) + .withKA(0.01) + .withKG(0.1) + .build()) + .withSlot1Gains( + Gains.fromDoubles() + .withPrefix("slot1") + .withKP(2.0) + .withKI(0.0) + .withKD(0.2) + .withKS(0.0) + .withKV(0.0) + .withKA(0.0) + .withKG(0.0) + .build()) + .withSlot2Gains( + Gains.fromDoubles() + .withPrefix("slot2") + .withKP(3.0) + .withKI(0.0) + .withKD(0.3) + .withKS(0.0) + .withKV(0.0) + .withKA(0.0) + .withKG(0.0) + .build()) + .withConstraints( + AngularPositionConstraints.fromMeasures() + .withPrefix("constraints") + .withMaxVelocity(Units.RadiansPerSecond.of(2.0)) + .withMaxAcceleration(Units.RadiansPerSecondPerSecond.of(2.0)) + .withGoalTolerance(Units.Radians.of(0.05)) + .build()) + .withCurrentLimits( + CurrentLimits.fromDoubles() + .withSupplyCurrentLimit(40.0) + .withStatorCurrentLimit(40.0) + .build()) + .withEnableFOC(false) + .withInvertedValue(InvertedValue.Clockwise_Positive) + .withVoltageOffsetStep(Units.Volts.of(0.5)) + .withPositionOffsetStep(Rotation2d.fromDegrees(5)) + .build(); + } + + @Test + public void testArmIOTalonFX() { + TalonFXConfigurator configurator = mock(TalonFXConfigurator.class); + when(configurator.apply(any(TalonFXConfiguration.class))).thenReturn(StatusCode.OK); + when(configurator.apply(any(TalonFXConfiguration.class), anyDouble())) + .thenReturn(StatusCode.OK); + + StatusSignal positionRotations = mock(StatusSignal.class); + StatusSignal velocityRotationsPerSecond = mock(StatusSignal.class); + StatusSignal appliedVolts = mock(StatusSignal.class); + StatusSignal supplyCurrentAmps = mock(StatusSignal.class); + StatusSignal torqueCurrentAmps = mock(StatusSignal.class); + StatusSignal temperatureCelsius = mock(StatusSignal.class); + StatusSignal positionSetpointRotations = mock(StatusSignal.class); + StatusSignal positionErrorRotations = mock(StatusSignal.class); + StatusSignal closedLoopSlot = mock(StatusSignal.class); + + when(positionRotations.getValue()).thenReturn(Rotation2d.fromDegrees(10.0).getMeasure()); + when(positionRotations.getValueAsDouble()) + .thenReturn(Rotation2d.fromDegrees(10.0).getRotations()); + when(velocityRotationsPerSecond.getValue()).thenReturn(Units.RadiansPerSecond.of(1.5)); + when(velocityRotationsPerSecond.getValueAsDouble()).thenReturn(1.5); + when(appliedVolts.getValueAsDouble()).thenReturn(6.0); + when(appliedVolts.getValue()).thenReturn(Units.Volts.of(6.0)); + when(supplyCurrentAmps.getValueAsDouble()).thenReturn(15.0); + when(torqueCurrentAmps.getValueAsDouble()).thenReturn(12.0); + when(temperatureCelsius.getValueAsDouble()).thenReturn(45.0); + when(positionSetpointRotations.getValueAsDouble()).thenReturn(0.5); + when(positionErrorRotations.getValueAsDouble()).thenReturn(0.01); + when(closedLoopSlot.getValue()).thenReturn(0); + + try (MockedConstruction mockTalon = + mockConstruction( + TalonFX.class, + (mock, context) -> { + when(mock.getConfigurator()).thenReturn(configurator); + when(mock.getPosition()).thenReturn(positionRotations); + when(mock.getVelocity()).thenReturn(velocityRotationsPerSecond); + when(mock.getMotorVoltage()).thenReturn(appliedVolts); + when(mock.getSupplyCurrent()).thenReturn(supplyCurrentAmps); + when(mock.getTorqueCurrent()).thenReturn(torqueCurrentAmps); + when(mock.getDeviceTemp()).thenReturn(temperatureCelsius); + when(mock.getClosedLoopReference()).thenReturn(positionSetpointRotations); + when(mock.getClosedLoopError()).thenReturn(positionErrorRotations); + when(mock.getClosedLoopSlot()).thenReturn(closedLoopSlot); + }); + MockedStatic mockBss = mockStatic(BaseStatusSignal.class)) { + + mockBss + .when( + () -> + BaseStatusSignal.setUpdateFrequencyForAll( + anyDouble(), any(BaseStatusSignal[].class))) + .thenReturn(null); + + ArmIOTalonFX io = new ArmIOTalonFX(constants); + ArmIO.ArmIOInputs inputs = new ArmIO.ArmIOInputs(); + + io.updateInputs(inputs); + + assertEquals(10.0, inputs.position.getDegrees(), 0.01); + assertEquals(1.5, inputs.velocity.in(Units.RadiansPerSecond), 0.01); + assertEquals(6.0, inputs.appliedVolts[0], 0.01); + assertEquals(15.0, inputs.supplyCurrentAmps[0], 0.01); + assertEquals(12.0, inputs.torqueCurrentAmps[0], 0.01); + assertEquals(45.0, inputs.temperatureCelsius[0], 0.01); + assertEquals(0.5, inputs.positionSetpoint.getRotations(), 0.01); + assertEquals(0.01, inputs.positionError.getRotations(), 0.01); + assertEquals(GainSlot.ZERO, inputs.gainSlot); + + // Verify command/set methods + io.setVoltageGoal(Units.Volts.of(8.0)); + io.setPositionGoal(Rotation2d.fromDegrees(30.0)); + assertTrue(io.atVoltageGoal(Units.Volts.of(6.0))); + assertFalse(io.atVoltageGoal(Units.Volts.of(0.0))); + assertTrue(io.atPositionGoal(Rotation2d.fromDegrees(10.0))); + assertFalse(io.atPositionGoal(Rotation2d.fromDegrees(40.0))); + + io.setPosition(Rotation2d.fromDegrees(15.0)); + + io.setGainSlot(GainSlot.ZERO); + io.setGainSlot(GainSlot.ONE); + io.setGainSlot(GainSlot.TWO); + + io.updateGains(Gains.fromDoubles().withPrefix("slot0").build(), GainSlot.ZERO); + io.updateGains(Gains.fromDoubles().withPrefix("slot1").build(), GainSlot.ONE); + io.updateGains(Gains.fromDoubles().withPrefix("slot2").build(), GainSlot.TWO); + + io.updateConstraints( + AngularPositionConstraints.fromMeasures() + .withPrefix("constraints") + .withGoalTolerance(Units.Radians.of(0.01)) + .withMaxVelocity(Units.RadiansPerSecond.of(1.0)) + .withMaxAcceleration(Units.RadiansPerSecondPerSecond.of(1.0)) + .build()); + } + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/arm/ArmIOTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/arm/ArmIOTest.java new file mode 100644 index 00000000..d01ad4e2 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/arm/ArmIOTest.java @@ -0,0 +1,42 @@ +package edu.wpi.team190.gompeilib.subsystems.arm; + +import static org.junit.jupiter.api.Assertions.*; + +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.units.Units; +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.AngularPositionConstraints; +import edu.wpi.team190.gompeilib.core.utility.phoenix.GainSlot; +import org.junit.jupiter.api.Test; + +public class ArmIOTest { + @Test + public void testArmIODefaults() { + ArmIO io = new ArmIO() {}; + ArmIO.ArmIOInputs inputs = new ArmIO.ArmIOInputs(); + + // Call defaults - should not throw + io.updateInputs(inputs); + io.setVoltageGoal(Units.Volts.of(1.0)); + io.setPositionGoal(Rotation2d.fromDegrees(10)); + assertFalse(io.atVoltageGoal(Units.Volts.of(1.0))); + assertFalse(io.atPositionGoal(Rotation2d.fromDegrees(10))); + io.setPosition(Rotation2d.fromDegrees(5)); + io.setGainSlot(GainSlot.ZERO); + io.updateGains(Gains.builder().build(), GainSlot.ZERO); + io.updateConstraints(AngularPositionConstraints.builder().build()); + + // Inputs defaults + assertEquals(0.0, inputs.position.getDegrees()); + assertEquals(0.0, inputs.velocity.in(Units.RadiansPerSecond)); + assertEquals(0.0, inputs.acceleration.in(Units.RadiansPerSecondPerSecond)); + assertEquals(0, inputs.appliedVolts.length); + assertEquals(0, inputs.supplyCurrentAmps.length); + assertEquals(0, inputs.torqueCurrentAmps.length); + assertEquals(0, inputs.temperatureCelsius.length); + assertEquals(0.0, inputs.positionGoal.getDegrees()); + assertEquals(0.0, inputs.positionSetpoint.getDegrees()); + assertEquals(0.0, inputs.positionError.getDegrees()); + assertEquals(GainSlot.ZERO, inputs.gainSlot); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/arm/ArmStateTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/arm/ArmStateTest.java new file mode 100644 index 00000000..9026f24b --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/arm/ArmStateTest.java @@ -0,0 +1,16 @@ +package edu.wpi.team190.gompeilib.subsystems.arm; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +public class ArmStateTest { + @Test + public void testEnum() { + assertEquals(3, ArmState.values().length); + assertEquals(ArmState.IDLE, ArmState.valueOf("IDLE")); + assertEquals(ArmState.OPEN_LOOP_VOLTAGE_CONTROL, ArmState.valueOf("OPEN_LOOP_VOLTAGE_CONTROL")); + assertEquals( + ArmState.CLOSED_LOOP_POSITION_CONTROL, ArmState.valueOf("CLOSED_LOOP_POSITION_CONTROL")); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/arm/ArmTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/arm/ArmTest.java new file mode 100644 index 00000000..4bd1333a --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/arm/ArmTest.java @@ -0,0 +1,155 @@ +package edu.wpi.team190.gompeilib.subsystems.arm; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import com.ctre.phoenix6.CANBus; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.units.Units; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.Subsystem; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import edu.wpi.team190.gompeilib.core.robot.RobotMode; +import edu.wpi.team190.gompeilib.core.utility.Setpoint; +import edu.wpi.team190.gompeilib.core.utility.control.CurrentLimits; +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.AngularPositionConstraints; +import edu.wpi.team190.gompeilib.core.utility.phoenix.GainSlot; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.littletonrobotics.junction.Logger; +import org.mockito.MockedStatic; + +public class ArmTest { + private ArmIO io; + private Subsystem subsystem; + private ArmConstants constants; + + @BeforeEach + public void setUp() { + edu.wpi.first.hal.HAL.initialize(500, 0); + try { + GompeiLib.deinit(); + } catch (Exception e) { + } + GompeiLib.init(RobotMode.SIM, false, 0.02); + + io = mock(ArmIO.class); + subsystem = mock(Subsystem.class, CALLS_REAL_METHODS); + when(subsystem.getName()).thenReturn("TestArmSubsystem"); + + constants = + ArmConstants.builder() + .withArmCANID(4) + .withCanBus(new CANBus("rio")) + .withArmParameters( + ArmConstants.ArmParameters.builder() + .withMotorConfig(edu.wpi.first.math.system.plant.DCMotor.getNeo550(1)) + .withMinAngle(Rotation2d.fromDegrees(-90)) + .withMaxAngle(Rotation2d.fromDegrees(90)) + .withContinuousOutput(false) + .withNumMotors(1) + .withGearRatio(100.0) + .withLengthMeters(0.5) + .withMomentOfInertia(0.1) + .build()) + .withSlot0Gains(Gains.fromDoubles().withPrefix("slot0").build()) + .withConstraints( + AngularPositionConstraints.fromMeasures() + .withPrefix("constraints") + .withMaxVelocity(Units.RadiansPerSecond.of(1.0)) + .withMaxAcceleration(Units.RadiansPerSecondPerSecond.of(1.0)) + .withGoalTolerance(Units.Radians.of(0.01)) + .build()) + .withCurrentLimits( + CurrentLimits.fromDoubles() + .withSupplyCurrentLimit(40.0) + .withStatorCurrentLimit(40.0) + .build()) + .withEnableFOC(false) + .withInvertedValue(com.ctre.phoenix6.signals.InvertedValue.Clockwise_Positive) + .withVoltageOffsetStep(Units.Volts.of(0.5)) + .withPositionOffsetStep(Rotation2d.fromDegrees(5)) + .build(); + } + + @Test + public void testArm() { + try (MockedStatic mockLogger = mockStatic(Logger.class)) { + Arm arm = new Arm(io, subsystem, 0, constants); + + assertNotNull(arm); + assertEquals(ArmState.IDLE, arm.getCurrentState()); + + // Test alternative constructors + Arm arm5 = new Arm(io, subsystem, 0, constants, arm.getPositionGoal()); + assertNotNull(arm5); + + Arm arm6 = new Arm(io, subsystem, 0, constants, arm.getPositionGoal(), arm.getVoltageGoal()); + assertNotNull(arm6); + + // Test periodic in IDLE state + arm.periodic(); + verify(io).updateInputs(any()); + + // Test open loop control state + arm.setVoltageGoal(Units.Volts.of(6.0)); + assertEquals(ArmState.OPEN_LOOP_VOLTAGE_CONTROL, arm.getCurrentState()); + arm.periodic(); + verify(io).setVoltageGoal(Units.Volts.of(6.0)); + + // Test closed loop control state + arm.setPositionGoal(Rotation2d.fromDegrees(30)); + assertEquals(ArmState.CLOSED_LOOP_POSITION_CONTROL, arm.getCurrentState()); + arm.periodic(); + verify(io).setPositionGoal(any(Rotation2d.class)); + + // Test setters/getters + arm.setVoltageGoal( + new Setpoint<>( + Units.Volts.of(2.0), Units.Volts.of(0.1), Units.Volts.of(-12), Units.Volts.of(12))); + arm.setPositionGoal( + new Setpoint<>( + Units.Radians.of(0.5), + Units.Radians.of(0.01), + Units.Radians.of(-3.14), + Units.Radians.of(3.14))); + + arm.getArmPosition(); + + // Test delegates + arm.setPosition(Rotation2d.fromDegrees(10)); + verify(io).setPosition(Rotation2d.fromDegrees(10)); + + arm.setGainSlot(GainSlot.ONE); + verify(io).setGainSlot(GainSlot.ONE); + + arm.updateGains(Gains.fromDoubles().withPrefix("slot0").build(), GainSlot.ZERO); + verify(io).updateGains(any(), eq(GainSlot.ZERO)); + + arm.updateConstraints( + AngularPositionConstraints.fromMeasures() + .withPrefix("constraints") + .withGoalTolerance(Units.Radians.of(0.01)) + .withMaxVelocity(Units.RadiansPerSecond.of(1.0)) + .withMaxAcceleration(Units.RadiansPerSecondPerSecond.of(1.0)) + .build()); + verify(io).updateConstraints(any()); + + // test goals + when(io.atVoltageGoal(any())).thenReturn(true); + assertTrue(arm.atVoltageGoal()); + assertTrue(arm.atVoltageGoal(Units.Volts.of(2.0))); + + when(io.atPositionGoal(any())).thenReturn(true); + assertTrue(arm.atPositionGoal()); + assertTrue(arm.atPositionGoal(Rotation2d.fromDegrees(30))); + + Command waitCmd = arm.waitUntilAtGoal(); + assertNotNull(waitCmd); + + Command sysIdCmd = arm.sysIdRoutine(); + assertNotNull(sysIdCmd); + } + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/drivebases/swervedrive/SwerveDriveConstantsTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/drivebases/swervedrive/SwerveDriveConstantsTest.java new file mode 100644 index 00000000..bd251c04 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/drivebases/swervedrive/SwerveDriveConstantsTest.java @@ -0,0 +1,159 @@ +package edu.wpi.team190.gompeilib.subsystems.drivebases.swervedrive; + +import static edu.wpi.first.units.Units.*; +import static org.junit.jupiter.api.Assertions.*; + +import com.ctre.phoenix6.CANBus; +import com.ctre.phoenix6.configs.CANcoderConfiguration; +import com.ctre.phoenix6.configs.Slot0Configs; +import com.ctre.phoenix6.configs.TalonFXConfiguration; +import com.ctre.phoenix6.swerve.SwerveModuleConstants; +import com.ctre.phoenix6.swerve.SwerveModuleConstants.ClosedLoopOutputType; +import com.ctre.phoenix6.swerve.SwerveModuleConstants.SteerFeedbackType; +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.math.kinematics.SwerveDriveKinematics; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.AngularPositionConstraints; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.LinearConstraints; +import edu.wpi.team190.gompeilib.core.utility.tunable.LoggedTunableMeasure; +import edu.wpi.team190.gompeilib.core.utility.tunable.LoggedTunableNumber; +import org.junit.jupiter.api.Test; + +public class SwerveDriveConstantsTest { + + public static SwerveModuleConstants< + TalonFXConfiguration, TalonFXConfiguration, CANcoderConfiguration> + createModuleConstants(int driveId, int steerId, int encoderId, double x, double y) { + SwerveModuleConstants c = + new SwerveModuleConstants<>(); + c.DriveMotorId = driveId; + c.SteerMotorId = steerId; + c.EncoderId = encoderId; + c.LocationX = x; + c.LocationY = y; + c.DriveMotorGearRatio = 6.0; + c.SteerMotorGearRatio = 10.0; + c.SteerInertia = 0.01; + c.DriveInertia = 0.01; + c.EncoderOffset = 0.0; + c.FeedbackSource = SteerFeedbackType.FusedCANcoder; + c.DriveMotorClosedLoopOutput = ClosedLoopOutputType.Voltage; + c.SteerMotorClosedLoopOutput = ClosedLoopOutputType.Voltage; + c.EncoderInitialConfigs = new CANcoderConfiguration(); + c.DriveMotorGains = new Slot0Configs(); + c.SteerMotorGains = new Slot0Configs(); + return c; + } + + @Test + public void testConstantsAndBuilders() { + CANBus canBus = new CANBus("rio"); + DCMotor driveModel = DCMotor.getKrakenX60(1); + DCMotor turnModel = DCMotor.getKrakenX44(1); + + var fl = createModuleConstants(1, 2, 3, 0.5, 0.5); + var fr = createModuleConstants(4, 5, 6, 0.5, -0.5); + var bl = createModuleConstants(7, 8, 9, -0.5, 0.5); + var br = createModuleConstants(10, 11, 12, -0.5, -0.5); + + SwerveDriveConstants.DriveConfig driveConfig = + SwerveDriveConstants.DriveConfig.builder() + .withCanBus(canBus) + .withPigeon2Id(13) + .withMaxLinearVelocityMetersPerSecond(5.0) + .withWheelRadiusMeters(0.05) + .withDriveModel(driveModel) + .withTurnModel(turnModel) + .withFrontLeft(fl) + .withFrontRight(fr) + .withBackLeft(bl) + .withBackRight(br) + .withDriveClosedLoopOutputType(ClosedLoopOutputType.Voltage) + .withSteerClosedLoopOutputType(ClosedLoopOutputType.Voltage) + .withBumperWidth(0.8) + .withBumperLength(0.8) + .withRobotMassKilograms(45.0) + .withTrackWidth(0.6) + .withRobotMOI(2.5) + .withModuleCurrentLimit(40.0) + .withWheelCOF(1.2) + .build(); + + Gains driveGains = + Gains.builder() + .withKP(new LoggedTunableNumber("kp_drive", 1.0)) + .withKD(new LoggedTunableNumber("kd_drive", 0.1)) + .build(); + + Gains turnGains = + Gains.builder() + .withKP(new LoggedTunableNumber("kp_turn", 2.0)) + .withKD(new LoggedTunableNumber("kd_turn", 0.2)) + .build(); + + SwerveDriveConstants.AutoAlignConstants autoAlignConstants = + SwerveDriveConstants.AutoAlignConstants.builder() + .withXGains(driveGains) + .withXConstraints( + LinearConstraints.builder() + .withMaxVelocity(new LoggedTunableMeasure<>("max_v_x", MetersPerSecond.of(1.0))) + .withMaxAcceleration( + new LoggedTunableMeasure<>("max_a_x", MetersPerSecondPerSecond.of(1.0))) + .withGoalTolerance(new LoggedTunableMeasure<>("tol_x", Meters.of(0.05))) + .build()) + .withYGains(driveGains) + .withYConstraints( + LinearConstraints.builder() + .withMaxVelocity(new LoggedTunableMeasure<>("max_v_y", MetersPerSecond.of(1.0))) + .withMaxAcceleration( + new LoggedTunableMeasure<>("max_a_y", MetersPerSecondPerSecond.of(1.0))) + .withGoalTolerance(new LoggedTunableMeasure<>("tol_y", Meters.of(0.05))) + .build()) + .withRotationGains(turnGains) + .withRotationConstraints( + AngularPositionConstraints.builder() + .withMaxVelocity( + new LoggedTunableMeasure<>("max_v_rot", RadiansPerSecond.of(1.0))) + .withMaxAcceleration( + new LoggedTunableMeasure<>("max_a_rot", RadiansPerSecondPerSecond.of(1.0))) + .withGoalTolerance(new LoggedTunableMeasure<>("tol_rot", Radians.of(0.05))) + .build()) + .withLinearThreshold(new LoggedTunableMeasure<>("lin_thresh", Meters.of(0.01))) + .withAngularThreshold(new LoggedTunableMeasure<>("ang_thresh", Radians.of(0.01))) + .build(); + + SwerveDriveConstants constants = + SwerveDriveConstants.builder() + .withDriveConfig(driveConfig) + .withDriveGains(driveGains) + .withTurnGains(turnGains) + .withAutoTranslationGains(driveGains) + .withAutoRotationGains(turnGains) + .withAutoAlignConstants(autoAlignConstants) + .withOdometryFrequency(250.0) + .withDriverDeadband(0.1) + .withOperatorDeadband(0.1) + .build(); + + assertNotNull(constants); + assertEquals(250.0, constants.odometryFrequency); + assertEquals(0.1, constants.driverDeadband); + assertEquals(0.1, constants.operatorDeadband); + + // Verify DriveConfig calculations + double expectedRadius = Math.hypot(0.5, 0.5); + assertEquals(expectedRadius, driveConfig.driveBaseRadius(), 1e-6); + assertEquals(5.0 / expectedRadius, driveConfig.maxAngularVelocity(), 1e-6); + + Translation2d[] translations = driveConfig.getModuleTranslations(); + assertEquals(4, translations.length); + assertEquals(new Translation2d(0.5, 0.5), translations[0]); + assertEquals(new Translation2d(0.5, -0.5), translations[1]); + assertEquals(new Translation2d(-0.5, 0.5), translations[2]); + assertEquals(new Translation2d(-0.5, -0.5), translations[3]); + + SwerveDriveKinematics kinematics = driveConfig.kinematics(); + assertNotNull(kinematics); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/drivebases/swervedrive/SwerveDriveTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/drivebases/swervedrive/SwerveDriveTest.java new file mode 100644 index 00000000..6985cdb0 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/drivebases/swervedrive/SwerveDriveTest.java @@ -0,0 +1,414 @@ +package edu.wpi.team190.gompeilib.subsystems.drivebases.swervedrive; + +import static edu.wpi.first.units.Units.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import choreo.trajectory.SwerveSample; +import com.ctre.phoenix6.CANBus; +import com.ctre.phoenix6.StatusSignal; +import com.ctre.phoenix6.configs.CANcoderConfiguration; +import com.ctre.phoenix6.configs.TalonFXConfiguration; +import com.ctre.phoenix6.swerve.SwerveModuleConstants; +import com.ctre.phoenix6.swerve.SwerveModuleConstants.ClosedLoopOutputType; +import com.pathplanner.lib.auto.AutoBuilder; +import com.pathplanner.lib.config.RobotConfig; +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.Vector; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.math.kinematics.ChassisSpeeds; +import edu.wpi.first.math.kinematics.SwerveModulePosition; +import edu.wpi.first.math.numbers.N2; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.wpilibj.DriverStation; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import edu.wpi.team190.gompeilib.core.io.components.inertial.GyroIO; +import edu.wpi.team190.gompeilib.core.io.components.inertial.GyroIOPigeon2; +import edu.wpi.team190.gompeilib.core.robot.RobotMode; +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.AngularPositionConstraints; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.LinearConstraints; +import edu.wpi.team190.gompeilib.core.utility.phoenix.PhoenixOdometryThread; +import edu.wpi.team190.gompeilib.core.utility.tunable.LoggedTunableMeasure; +import edu.wpi.team190.gompeilib.core.utility.tunable.LoggedTunableNumber; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.Queue; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.function.Consumer; +import java.util.function.Supplier; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.littletonrobotics.junction.Logger; +import org.mockito.MockedStatic; + +public class SwerveDriveTest { + + private SwerveDriveConstants driveConstants; + private SwerveModuleConstants + moduleConstants; + + private SwerveModuleIO flModuleIO; + private SwerveModuleIO frModuleIO; + private SwerveModuleIO blModuleIO; + private SwerveModuleIO brModuleIO; + + private GyroIO lowFreqGyroIO; + private GyroIOPigeon2 highFreqGyroIO; + + private Supplier robotPoseSupplier; + private Consumer resetPoseConsumer; + + @BeforeEach + public void setUp() { + edu.wpi.first.hal.HAL.initialize(500, 0); + try { + GompeiLib.deinit(); + } catch (Exception e) { + } + GompeiLib.init(RobotMode.SIM, false, 0.02); + + try { + java.lang.reflect.Field instanceField = + PhoenixOdometryThread.class.getDeclaredField("instance"); + instanceField.setAccessible(true); + instanceField.set(null, null); + } catch (Exception e) { + } + + flModuleIO = mock(SwerveModuleIO.class); + frModuleIO = mock(SwerveModuleIO.class); + blModuleIO = mock(SwerveModuleIO.class); + brModuleIO = mock(SwerveModuleIO.class); + + setupModuleIO(flModuleIO); + setupModuleIO(frModuleIO); + setupModuleIO(blModuleIO); + setupModuleIO(brModuleIO); + + lowFreqGyroIO = mock(GyroIO.class); + highFreqGyroIO = mock(GyroIOPigeon2.class); + + setupGyroIO(lowFreqGyroIO); + setupGyroIO(highFreqGyroIO); + + robotPoseSupplier = () -> new Pose2d(1.0, 2.0, Rotation2d.fromDegrees(30.0)); + resetPoseConsumer = pose -> {}; + + CANBus canBus = new CANBus("rio"); + DCMotor driveModel = DCMotor.getKrakenX60(1); + DCMotor turnModel = DCMotor.getKrakenX44(1); + + moduleConstants = SwerveDriveConstantsTest.createModuleConstants(1, 2, 3, 0.5, 0.5); + + SwerveDriveConstants.DriveConfig driveConfig = + SwerveDriveConstants.DriveConfig.builder() + .withCanBus(canBus) + .withPigeon2Id(13) + .withMaxLinearVelocityMetersPerSecond(5.0) + .withWheelRadiusMeters(0.05) + .withDriveModel(driveModel) + .withTurnModel(turnModel) + .withFrontLeft(moduleConstants) + .withFrontRight(moduleConstants) + .withBackLeft(moduleConstants) + .withBackRight(moduleConstants) + .withDriveClosedLoopOutputType(ClosedLoopOutputType.Voltage) + .withSteerClosedLoopOutputType(ClosedLoopOutputType.Voltage) + .withBumperWidth(0.8) + .withBumperLength(0.8) + .withRobotMassKilograms(45.0) + .withTrackWidth(0.6) + .withRobotMOI(2.5) + .withModuleCurrentLimit(40.0) + .withWheelCOF(1.2) + .build(); + + Gains driveGains = + Gains.builder() + .withKP(new LoggedTunableNumber("kp_drive", 1.0)) + .withKD(new LoggedTunableNumber("kd_drive", 0.1)) + .build(); + + Gains turnGains = + Gains.builder() + .withKP(new LoggedTunableNumber("kp_turn", 2.0)) + .withKD(new LoggedTunableNumber("kd_turn", 0.2)) + .build(); + + SwerveDriveConstants.AutoAlignConstants autoAlignConstants = + SwerveDriveConstants.AutoAlignConstants.builder() + .withXGains(driveGains) + .withXConstraints( + LinearConstraints.builder() + .withMaxVelocity(new LoggedTunableMeasure<>("max_v_x", MetersPerSecond.of(1.0))) + .withMaxAcceleration( + new LoggedTunableMeasure<>("max_a_x", MetersPerSecondPerSecond.of(1.0))) + .withGoalTolerance(new LoggedTunableMeasure<>("tol_x", Meters.of(0.05))) + .build()) + .withYGains(driveGains) + .withYConstraints( + LinearConstraints.builder() + .withMaxVelocity(new LoggedTunableMeasure<>("max_v_y", MetersPerSecond.of(1.0))) + .withMaxAcceleration( + new LoggedTunableMeasure<>("max_a_y", MetersPerSecondPerSecond.of(1.0))) + .withGoalTolerance(new LoggedTunableMeasure<>("tol_y", Meters.of(0.05))) + .build()) + .withRotationGains(turnGains) + .withRotationConstraints( + AngularPositionConstraints.builder() + .withMaxVelocity( + new LoggedTunableMeasure<>("max_v_rot", RadiansPerSecond.of(1.0))) + .withMaxAcceleration( + new LoggedTunableMeasure<>("max_a_rot", RadiansPerSecondPerSecond.of(1.0))) + .withGoalTolerance(new LoggedTunableMeasure<>("tol_rot", Radians.of(0.05))) + .build()) + .withLinearThreshold(new LoggedTunableMeasure<>("lin_thresh", Meters.of(0.01))) + .withAngularThreshold(new LoggedTunableMeasure<>("ang_thresh", Radians.of(0.01))) + .build(); + + driveConstants = + SwerveDriveConstants.builder() + .withDriveConfig(driveConfig) + .withDriveGains(driveGains) + .withTurnGains(turnGains) + .withAutoTranslationGains(driveGains) + .withAutoRotationGains(turnGains) + .withAutoAlignConstants(autoAlignConstants) + .withOdometryFrequency(250.0) + .withDriverDeadband(0.1) + .withOperatorDeadband(0.1) + .build(); + } + + private void setupModuleIO(SwerveModuleIO io) { + doAnswer( + invocation -> { + SwerveModuleIO.ModuleIOInputs in = invocation.getArgument(0); + in.driveConnected = true; + in.turnConnected = true; + in.turnEncoderConnected = true; + in.odometryTimestamps = new double[] {1.0}; + in.odometryDrivePositionsRadians = new double[] {10.0}; + in.odometryTurnPositions = new Rotation2d[] {Rotation2d.fromDegrees(30.0)}; + in.drivePositionRadians = 10.0; + in.turnPosition = Rotation2d.fromDegrees(30.0); + in.driveVelocityRadiansPerSecond = 5.0; + return null; + }) + .when(io) + .updateInputs(any()); + } + + private void setupGyroIO(GyroIO io) { + doAnswer( + invocation -> { + GyroIO.GyroIOInputs in = invocation.getArgument(0); + in.connected = true; + in.yawPosition = Rotation2d.fromDegrees(15.0); + in.yawVelocityRadPerSec = 0.5; + in.odometryYawTimestamps = new double[] {1.0}; + in.odometryYawPositions = new Rotation2d[] {Rotation2d.fromDegrees(15.0)}; + return null; + }) + .when(io) + .updateInputs(any()); + + doAnswer( + invocation -> { + GyroIO.GyroIOInputs in = invocation.getArgument(0); + in.connected = true; + in.yawPosition = Rotation2d.fromDegrees(15.0); + in.yawVelocityRadPerSec = 0.5; + in.odometryYawTimestamps = new double[] {1.0}; + in.odometryYawPositions = new Rotation2d[] {Rotation2d.fromDegrees(15.0)}; + return null; + }) + .when(io) + .updateInputs(any(), any(), any()); + + when(io.getYaw()).thenReturn(mock(StatusSignal.class)); + } + + @Test + public void testSwerveDriveLowFrequencyGyro() { + try (MockedStatic mockAutoBuilder = mockStatic(AutoBuilder.class); + MockedStatic mockRobotConfig = mockStatic(RobotConfig.class); + MockedStatic mockLogger = mockStatic(Logger.class); + MockedStatic mockDS = mockStatic(DriverStation.class)) { + + mockRobotConfig.when(RobotConfig::fromGUISettings).thenReturn(mock(RobotConfig.class)); + mockDS.when(DriverStation::isDisabled).thenReturn(false); + mockDS.when(DriverStation::getAlliance).thenReturn(Optional.of(DriverStation.Alliance.Red)); + + SwerveDrive drive = + new SwerveDrive( + driveConstants, + lowFreqGyroIO, + flModuleIO, + frModuleIO, + blModuleIO, + brModuleIO, + robotPoseSupplier, + resetPoseConsumer); + + // Verify simple getters + assertEquals(5.0, drive.getMaxLinearSpeedMetersPerSec(), 1e-6); + assertEquals(5.0 / Math.hypot(0.5, 0.5), drive.getMaxAngularSpeedRadPerSec(), 1e-6); + + // Periodic execution + drive.periodic(); + + assertEquals(0.5, drive.getYawVelocity(), 1e-6); + + // Verify modules read + verify(flModuleIO, atLeastOnce()).updateInputs(any()); + + // runVelocity + ChassisSpeeds targetSpeeds = new ChassisSpeeds(1.0, -1.0, 0.5); + drive.runVelocity(targetSpeeds); + verify(flModuleIO).setDriveVelocity(anyDouble(), anyDouble()); + + // stop + drive.stop(); + + // stopWithX + drive.stopWithX(); + + // runVelocityTorque + List> forces = new ArrayList<>(); + for (int i = 0; i < 4; i++) { + forces.add(VecBuilder.fill(10.0, 5.0)); + } + drive.runVelocityTorque(targetSpeeds, forces); + + // runCharacterization + drive.runCharacterization(6.0); + verify(flModuleIO, atLeastOnce()).setDriveAmps(6.0); + + // getModulePositions + SwerveModulePosition[] positions = drive.getModulePositions(); + assertEquals(4, positions.length); + assertEquals(10.0 * 0.05, positions[0].distanceMeters, 1e-6); + + // getWheelRadiusCharacterizationPositions + double[] wheelRadPos = drive.getWheelRadiusCharacterizationPositions(); + assertEquals(4, wheelRadPos.length); + assertEquals(10.0, wheelRadPos[0], 1e-6); + + // getFFCharacterizationVelocity + assertEquals(5.0 / (2 * Math.PI), drive.getFFCharacterizationVelocity(), 1e-6); + + // setPIDGains + drive.setPIDGains(2.0, 0.2, 3.0, 0.3); + verify(flModuleIO).setPID(2.0, 0.2, 3.0, 0.3); + + // setFFGains + drive.setFFGains(0.1, 0.15); + verify(flModuleIO).setFeedforward(0.1, 0.15); + + // updateCurrentLimits + drive.updateCurrentLimits(40.0, 30.0); + verify(flModuleIO).updateCurrentLimits(40.0, 30.0); + + // setAutoControllers + drive.setAutoControllers(driveConstants.driveGains, driveConstants.turnGains); + + // choreoDrive + SwerveSample sample = + new SwerveSample( + 0.0, 1.0, 2.0, 0.5, 0.1, 0.2, 0.3, 0.0, 0.0, 0.0, new double[4], new double[4]); + drive.choreoDrive(sample); + + // getFieldRelativeVelocity + Translation2d fieldRelVel = drive.getFieldRelativeVelocity(); + assertNotNull(fieldRelVel); + } + } + + @Test + public void testSwerveDriveHighFrequencyGyro() { + try (MockedStatic mockAutoBuilder = mockStatic(AutoBuilder.class); + MockedStatic mockRobotConfig = mockStatic(RobotConfig.class); + MockedStatic mockLogger = mockStatic(Logger.class); + MockedStatic mockDS = mockStatic(DriverStation.class); + MockedStatic mockOdomThread = + mockStatic(PhoenixOdometryThread.class)) { + + mockRobotConfig.when(RobotConfig::fromGUISettings).thenReturn(mock(RobotConfig.class)); + mockDS.when(DriverStation::isDisabled).thenReturn(true); // Test isDisabled branch + + PhoenixOdometryThread mockThread = mock(PhoenixOdometryThread.class); + mockOdomThread.when(() -> PhoenixOdometryThread.getInstance(any())).thenReturn(mockThread); + + Queue tsQueue = new ArrayBlockingQueue<>(10); + Queue yawQueue = new ArrayBlockingQueue<>(10); + when(mockThread.makeTimestampQueue()).thenReturn(tsQueue); + when(mockThread.registerSignal(any(StatusSignal.class))).thenReturn(yawQueue); + + SwerveDrive drive = + new SwerveDrive( + driveConstants, + highFreqGyroIO, + flModuleIO, + frModuleIO, + blModuleIO, + brModuleIO, + robotPoseSupplier, + resetPoseConsumer); + + verify(mockThread).start(); + + tsQueue.add(1.1); + yawQueue.add(0.2); + + // Periodic call should consume high frequency inputs + drive.periodic(); + + assertEquals(0, tsQueue.size()); + assertEquals(0, yawQueue.size()); + } + } + + @Test + public void testSwerveDriveDisconnectedGyroFallback() { + try (MockedStatic mockAutoBuilder = mockStatic(AutoBuilder.class); + MockedStatic mockRobotConfig = mockStatic(RobotConfig.class); + MockedStatic mockLogger = mockStatic(Logger.class); + MockedStatic mockDS = mockStatic(DriverStation.class)) { + + mockRobotConfig.when(RobotConfig::fromGUISettings).thenReturn(mock(RobotConfig.class)); + + // Make gyro report disconnected to test kinematics fallback in periodic + doAnswer( + invocation -> { + GyroIO.GyroIOInputs in = invocation.getArgument(0); + in.connected = false; + in.yawPosition = new Rotation2d(); + in.yawVelocityRadPerSec = 0.0; + in.odometryYawTimestamps = new double[] {1.0}; + in.odometryYawPositions = new Rotation2d[] {new Rotation2d()}; + return null; + }) + .when(lowFreqGyroIO) + .updateInputs(any()); + + SwerveDrive drive = + new SwerveDrive( + driveConstants, + lowFreqGyroIO, + flModuleIO, + frModuleIO, + blModuleIO, + brModuleIO, + robotPoseSupplier, + resetPoseConsumer); + + drive.periodic(); + assertNotNull(drive.getRawGyroRotation()); + } + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/drivebases/swervedrive/SwerveModuleIOSimTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/drivebases/swervedrive/SwerveModuleIOSimTest.java new file mode 100644 index 00000000..a702b5c7 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/drivebases/swervedrive/SwerveModuleIOSimTest.java @@ -0,0 +1,158 @@ +package edu.wpi.team190.gompeilib.subsystems.drivebases.swervedrive; + +import static edu.wpi.first.units.Units.*; +import static org.junit.jupiter.api.Assertions.*; + +import com.ctre.phoenix6.CANBus; +import com.ctre.phoenix6.configs.CANcoderConfiguration; +import com.ctre.phoenix6.configs.TalonFXConfiguration; +import com.ctre.phoenix6.swerve.SwerveModuleConstants; +import com.ctre.phoenix6.swerve.SwerveModuleConstants.ClosedLoopOutputType; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import edu.wpi.team190.gompeilib.core.robot.RobotMode; +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.AngularPositionConstraints; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.LinearConstraints; +import edu.wpi.team190.gompeilib.core.utility.tunable.LoggedTunableMeasure; +import edu.wpi.team190.gompeilib.core.utility.tunable.LoggedTunableNumber; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class SwerveModuleIOSimTest { + + private SwerveDriveConstants driveConstants; + private SwerveModuleConstants + moduleConstants; + + @BeforeEach + public void setUp() { + edu.wpi.first.hal.HAL.initialize(500, 0); + try { + GompeiLib.deinit(); + } catch (Exception e) { + } + GompeiLib.init(RobotMode.SIM, false, 0.02); + + CANBus canBus = new CANBus("rio"); + DCMotor driveModel = DCMotor.getKrakenX60(1); + DCMotor turnModel = DCMotor.getKrakenX44(1); + + moduleConstants = SwerveDriveConstantsTest.createModuleConstants(1, 2, 3, 0.5, 0.5); + + SwerveDriveConstants.DriveConfig driveConfig = + SwerveDriveConstants.DriveConfig.builder() + .withCanBus(canBus) + .withPigeon2Id(13) + .withMaxLinearVelocityMetersPerSecond(5.0) + .withWheelRadiusMeters(0.05) + .withDriveModel(driveModel) + .withTurnModel(turnModel) + .withFrontLeft(moduleConstants) + .withFrontRight(moduleConstants) + .withBackLeft(moduleConstants) + .withBackRight(moduleConstants) + .withDriveClosedLoopOutputType(ClosedLoopOutputType.Voltage) + .withSteerClosedLoopOutputType(ClosedLoopOutputType.Voltage) + .withBumperWidth(0.8) + .withBumperLength(0.8) + .withRobotMassKilograms(45.0) + .withTrackWidth(0.6) + .withRobotMOI(2.5) + .withModuleCurrentLimit(40.0) + .withWheelCOF(1.2) + .build(); + + Gains driveGains = + Gains.builder() + .withKP(new LoggedTunableNumber("kp_drive", 1.0)) + .withKD(new LoggedTunableNumber("kd_drive", 0.1)) + .build(); + + Gains turnGains = + Gains.builder() + .withKP(new LoggedTunableNumber("kp_turn", 2.0)) + .withKD(new LoggedTunableNumber("kd_turn", 0.2)) + .build(); + + SwerveDriveConstants.AutoAlignConstants autoAlignConstants = + SwerveDriveConstants.AutoAlignConstants.builder() + .withXGains(driveGains) + .withXConstraints( + LinearConstraints.builder() + .withMaxVelocity(new LoggedTunableMeasure<>("max_v_x", MetersPerSecond.of(1.0))) + .withMaxAcceleration( + new LoggedTunableMeasure<>("max_a_x", MetersPerSecondPerSecond.of(1.0))) + .withGoalTolerance(new LoggedTunableMeasure<>("tol_x", Meters.of(0.05))) + .build()) + .withYGains(driveGains) + .withYConstraints( + LinearConstraints.builder() + .withMaxVelocity(new LoggedTunableMeasure<>("max_v_y", MetersPerSecond.of(1.0))) + .withMaxAcceleration( + new LoggedTunableMeasure<>("max_a_y", MetersPerSecondPerSecond.of(1.0))) + .withGoalTolerance(new LoggedTunableMeasure<>("tol_y", Meters.of(0.05))) + .build()) + .withRotationGains(turnGains) + .withRotationConstraints( + AngularPositionConstraints.builder() + .withMaxVelocity( + new LoggedTunableMeasure<>("max_v_rot", RadiansPerSecond.of(1.0))) + .withMaxAcceleration( + new LoggedTunableMeasure<>("max_a_rot", RadiansPerSecondPerSecond.of(1.0))) + .withGoalTolerance(new LoggedTunableMeasure<>("tol_rot", Radians.of(0.05))) + .build()) + .withLinearThreshold(new LoggedTunableMeasure<>("lin_thresh", Meters.of(0.01))) + .withAngularThreshold(new LoggedTunableMeasure<>("ang_thresh", Radians.of(0.01))) + .build(); + + driveConstants = + SwerveDriveConstants.builder() + .withDriveConfig(driveConfig) + .withDriveGains(driveGains) + .withTurnGains(turnGains) + .withAutoTranslationGains(driveGains) + .withAutoRotationGains(turnGains) + .withAutoAlignConstants(autoAlignConstants) + .withOdometryFrequency(250.0) + .withDriverDeadband(0.1) + .withOperatorDeadband(0.1) + .build(); + } + + @Test + public void testSimIO() { + SwerveModuleIOSim sim = new SwerveModuleIOSim(driveConstants, moduleConstants); + SwerveModuleIO.ModuleIOInputs inputs = new SwerveModuleIO.ModuleIOInputs(); + + // Initial state + sim.updateInputs(inputs); + assertTrue(inputs.driveConnected); + assertTrue(inputs.turnConnected); + assertTrue(inputs.turnEncoderConnected); + assertEquals(0.0, inputs.drivePositionRadians, 1e-6); + assertEquals(0.0, inputs.driveVelocityRadiansPerSecond, 1e-6); + + // Test setDriveAmps + sim.setDriveAmps(6.0); // open loop applied volts + sim.updateInputs(inputs); + assertEquals(6.0, inputs.driveAppliedVolts, 1e-6); + assertTrue(inputs.drivePositionRadians > 0.0 || inputs.driveVelocityRadiansPerSecond > 0.0); + + // Test setTurnAmps + sim.setTurnAmps(-4.0); + sim.updateInputs(inputs); + assertEquals(-4.0, inputs.turnAppliedVolts, 1e-6); + + // Test setDriveVelocity + sim.setDriveVelocity(10.0, 1.0); // closed loop velocity + sim.updateInputs(inputs); + assertEquals(10.0, inputs.driveVelocitySetpointRadiansPerSecond, 1e-6); + + // Test setTurnPosition + sim.setTurnPosition(Rotation2d.fromDegrees(90)); + sim.updateInputs(inputs); + assertEquals(Math.PI / 2.0, inputs.turnPositionGoal.getRadians(), 1e-6); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/drivebases/swervedrive/SwerveModuleIOTalonFXSimTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/drivebases/swervedrive/SwerveModuleIOTalonFXSimTest.java new file mode 100644 index 00000000..eba08d79 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/drivebases/swervedrive/SwerveModuleIOTalonFXSimTest.java @@ -0,0 +1,294 @@ +package edu.wpi.team190.gompeilib.subsystems.drivebases.swervedrive; + +import static edu.wpi.first.units.Units.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import com.ctre.phoenix6.BaseStatusSignal; +import com.ctre.phoenix6.CANBus; +import com.ctre.phoenix6.StatusCode; +import com.ctre.phoenix6.StatusSignal; +import com.ctre.phoenix6.configs.CANcoderConfiguration; +import com.ctre.phoenix6.configs.CANcoderConfigurator; +import com.ctre.phoenix6.configs.TalonFXConfiguration; +import com.ctre.phoenix6.configs.TalonFXConfigurator; +import com.ctre.phoenix6.hardware.CANcoder; +import com.ctre.phoenix6.hardware.TalonFX; +import com.ctre.phoenix6.sim.CANcoderSimState; +import com.ctre.phoenix6.sim.TalonFXSimState; +import com.ctre.phoenix6.swerve.SwerveModuleConstants; +import com.ctre.phoenix6.swerve.SwerveModuleConstants.ClosedLoopOutputType; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.units.measure.*; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import edu.wpi.team190.gompeilib.core.robot.RobotMode; +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.AngularPositionConstraints; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.LinearConstraints; +import edu.wpi.team190.gompeilib.core.utility.phoenix.PhoenixOdometryThread; +import edu.wpi.team190.gompeilib.core.utility.tunable.LoggedTunableMeasure; +import edu.wpi.team190.gompeilib.core.utility.tunable.LoggedTunableNumber; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockedConstruction; +import org.mockito.MockedStatic; + +public class SwerveModuleIOTalonFXSimTest { + + private SwerveDriveConstants driveConstants; + private SwerveModuleConstants + moduleConstants; + + @BeforeEach + public void setUp() { + edu.wpi.first.hal.HAL.initialize(500, 0); + try { + GompeiLib.deinit(); + } catch (Exception e) { + } + GompeiLib.init(RobotMode.SIM, false, 0.02); + + try { + java.lang.reflect.Field instanceField = + PhoenixOdometryThread.class.getDeclaredField("instance"); + instanceField.setAccessible(true); + instanceField.set(null, null); + } catch (Exception e) { + } + + CANBus canBus = new CANBus("rio"); + DCMotor driveModel = DCMotor.getNeo550(1); + DCMotor turnModel = DCMotor.getNeo550(1); + + moduleConstants = SwerveDriveConstantsTest.createModuleConstants(1, 2, 3, 0.5, 0.5); + + SwerveDriveConstants.DriveConfig driveConfig = + SwerveDriveConstants.DriveConfig.builder() + .withCanBus(canBus) + .withPigeon2Id(13) + .withMaxLinearVelocityMetersPerSecond(5.0) + .withWheelRadiusMeters(0.05) + .withDriveModel(driveModel) + .withTurnModel(turnModel) + .withFrontLeft(moduleConstants) + .withFrontRight(moduleConstants) + .withBackLeft(moduleConstants) + .withBackRight(moduleConstants) + .withDriveClosedLoopOutputType(ClosedLoopOutputType.Voltage) + .withSteerClosedLoopOutputType(ClosedLoopOutputType.Voltage) + .withBumperWidth(0.8) + .withBumperLength(0.8) + .withRobotMassKilograms(45.0) + .withTrackWidth(0.6) + .withRobotMOI(2.5) + .withModuleCurrentLimit(40.0) + .withWheelCOF(1.2) + .build(); + + Gains driveGains = + Gains.builder() + .withKP(new LoggedTunableNumber("kp_drive", 1.0)) + .withKD(new LoggedTunableNumber("kd_drive", 0.1)) + .build(); + + Gains turnGains = + Gains.builder() + .withKP(new LoggedTunableNumber("kp_turn", 2.0)) + .withKD(new LoggedTunableNumber("kd_turn", 0.2)) + .build(); + + SwerveDriveConstants.AutoAlignConstants autoAlignConstants = + SwerveDriveConstants.AutoAlignConstants.builder() + .withXGains(driveGains) + .withXConstraints( + LinearConstraints.builder() + .withMaxVelocity(new LoggedTunableMeasure<>("max_v_x", MetersPerSecond.of(1.0))) + .withMaxAcceleration( + new LoggedTunableMeasure<>("max_a_x", MetersPerSecondPerSecond.of(1.0))) + .withGoalTolerance(new LoggedTunableMeasure<>("tol_x", Meters.of(0.05))) + .build()) + .withYGains(driveGains) + .withYConstraints( + LinearConstraints.builder() + .withMaxVelocity(new LoggedTunableMeasure<>("max_v_y", MetersPerSecond.of(1.0))) + .withMaxAcceleration( + new LoggedTunableMeasure<>("max_a_y", MetersPerSecondPerSecond.of(1.0))) + .withGoalTolerance(new LoggedTunableMeasure<>("tol_y", Meters.of(0.05))) + .build()) + .withRotationGains(turnGains) + .withRotationConstraints( + AngularPositionConstraints.builder() + .withMaxVelocity( + new LoggedTunableMeasure<>("max_v_rot", RadiansPerSecond.of(1.0))) + .withMaxAcceleration( + new LoggedTunableMeasure<>("max_a_rot", RadiansPerSecondPerSecond.of(1.0))) + .withGoalTolerance(new LoggedTunableMeasure<>("tol_rot", Radians.of(0.05))) + .build()) + .withLinearThreshold(new LoggedTunableMeasure<>("lin_thresh", Meters.of(0.01))) + .withAngularThreshold(new LoggedTunableMeasure<>("ang_thresh", Radians.of(0.01))) + .build(); + + driveConstants = + SwerveDriveConstants.builder() + .withDriveConfig(driveConfig) + .withDriveGains(driveGains) + .withTurnGains(turnGains) + .withAutoTranslationGains(driveGains) + .withAutoRotationGains(turnGains) + .withAutoAlignConstants(autoAlignConstants) + .withOdometryFrequency(250.0) + .withDriverDeadband(0.1) + .withOperatorDeadband(0.1) + .build(); + } + + @Test + public void testTalonFXSimIO() { + TalonFXConfigurator configurator = mock(TalonFXConfigurator.class); + when(configurator.apply(any(TalonFXConfiguration.class))).thenReturn(StatusCode.OK); + when(configurator.apply(any(TalonFXConfiguration.class), anyDouble())) + .thenReturn(StatusCode.OK); + + CANcoderConfigurator cancoderConfigurator = mock(CANcoderConfigurator.class); + when(cancoderConfigurator.apply(any(CANcoderConfiguration.class), anyDouble())) + .thenReturn(StatusCode.OK); + + StatusSignal drivePositionRotations = mock(StatusSignal.class); + StatusSignal driveVelocityRotationsPerSecond = mock(StatusSignal.class); + StatusSignal driveAppliedVolts = mock(StatusSignal.class); + StatusSignal driveSupplyCurrentAmps = mock(StatusSignal.class); + StatusSignal driveTorqueCurrentAmps = mock(StatusSignal.class); + StatusSignal driveTemperatureCelcius = mock(StatusSignal.class); + StatusSignal driveVelocitySetpointRotationsPerSecond = mock(StatusSignal.class); + StatusSignal driveVelocityErrorRotationsPerSecond = mock(StatusSignal.class); + + StatusSignal turnAbsolutePositionRotations = mock(StatusSignal.class); + StatusSignal turnPositionRotations = mock(StatusSignal.class); + StatusSignal turnVelocityRotationsPerSecond = mock(StatusSignal.class); + StatusSignal turnAppliedVolts = mock(StatusSignal.class); + StatusSignal turnSupplyCurrentAmps = mock(StatusSignal.class); + StatusSignal turnTorqueCurrentAmps = mock(StatusSignal.class); + StatusSignal turnTemperatureCelcius = mock(StatusSignal.class); + StatusSignal turnPositionSetpointRotations = mock(StatusSignal.class); + StatusSignal turnPositionErrorRotations = mock(StatusSignal.class); + + // Mock returns + when(drivePositionRotations.getValueAsDouble()).thenReturn(10.0); + when(driveVelocityRotationsPerSecond.getValueAsDouble()).thenReturn(2.5); + when(driveAppliedVolts.getValueAsDouble()).thenReturn(5.5); + when(driveSupplyCurrentAmps.getValueAsDouble()).thenReturn(12.0); + when(driveTorqueCurrentAmps.getValueAsDouble()).thenReturn(11.5); + when(driveTemperatureCelcius.getValueAsDouble()).thenReturn(35.0); + when(driveVelocitySetpointRotationsPerSecond.getValueAsDouble()).thenReturn(3.0); + when(driveVelocityErrorRotationsPerSecond.getValueAsDouble()).thenReturn(0.5); + + when(turnAbsolutePositionRotations.getValueAsDouble()).thenReturn(0.25); + when(turnPositionRotations.getValueAsDouble()).thenReturn(0.125); + when(turnVelocityRotationsPerSecond.getValueAsDouble()).thenReturn(1.2); + when(turnAppliedVolts.getValueAsDouble()).thenReturn(4.2); + when(turnSupplyCurrentAmps.getValueAsDouble()).thenReturn(8.0); + when(turnTorqueCurrentAmps.getValueAsDouble()).thenReturn(7.8); + when(turnTemperatureCelcius.getValueAsDouble()).thenReturn(40.0); + when(turnPositionSetpointRotations.getValueAsDouble()).thenReturn(0.13); + when(turnPositionErrorRotations.getValueAsDouble()).thenReturn(0.005); + + TalonFXSimState driveSimState = mock(TalonFXSimState.class); + TalonFXSimState turnSimState = mock(TalonFXSimState.class); + CANcoderSimState cancoderSimState = mock(CANcoderSimState.class); + + when(driveSimState.getMotorVoltage()).thenReturn(6.0); + when(turnSimState.getMotorVoltage()).thenReturn(4.0); + + try (MockedConstruction mockTalon = + mockConstruction( + TalonFX.class, + (mock, context) -> { + when(mock.getConfigurator()).thenReturn(configurator); + if (context.getCount() == 1) { + when(mock.getPosition()).thenReturn(drivePositionRotations); + when(mock.getVelocity()).thenReturn(driveVelocityRotationsPerSecond); + when(mock.getMotorVoltage()).thenReturn(driveAppliedVolts); + when(mock.getSupplyCurrent()).thenReturn(driveSupplyCurrentAmps); + when(mock.getTorqueCurrent()).thenReturn(driveTorqueCurrentAmps); + when(mock.getDeviceTemp()).thenReturn(driveTemperatureCelcius); + when(mock.getClosedLoopReference()) + .thenReturn(driveVelocitySetpointRotationsPerSecond); + when(mock.getClosedLoopError()) + .thenReturn(driveVelocityErrorRotationsPerSecond); + when(mock.getSimState()).thenReturn(driveSimState); + } else { + when(mock.getPosition()).thenReturn(turnPositionRotations); + when(mock.getVelocity()).thenReturn(turnVelocityRotationsPerSecond); + when(mock.getMotorVoltage()).thenReturn(turnAppliedVolts); + when(mock.getSupplyCurrent()).thenReturn(turnSupplyCurrentAmps); + when(mock.getTorqueCurrent()).thenReturn(turnTorqueCurrentAmps); + when(mock.getDeviceTemp()).thenReturn(turnTemperatureCelcius); + when(mock.getClosedLoopReference()).thenReturn(turnPositionSetpointRotations); + when(mock.getClosedLoopError()).thenReturn(turnPositionErrorRotations); + when(mock.getSimState()).thenReturn(turnSimState); + } + when(mock.setPosition(anyDouble(), anyDouble())).thenReturn(StatusCode.OK); + when(mock.setControl(any(com.ctre.phoenix6.controls.ControlRequest.class))) + .thenReturn(StatusCode.OK); + }); + MockedConstruction mockCANcoder = + mockConstruction( + CANcoder.class, + (mock, context) -> { + when(mock.getConfigurator()).thenReturn(cancoderConfigurator); + when(mock.getAbsolutePosition()).thenReturn(turnAbsolutePositionRotations); + when(mock.getSimState()).thenReturn(cancoderSimState); + }); + MockedStatic mockBss = mockStatic(BaseStatusSignal.class)) { + + mockBss + .when( + () -> + BaseStatusSignal.setUpdateFrequencyForAll( + anyDouble(), any(BaseStatusSignal[].class))) + .thenReturn(StatusCode.OK); + + // Now construct the sim IO object + SwerveModuleIOTalonFXSim sim = new SwerveModuleIOTalonFXSim(driveConstants, moduleConstants); + + TalonFX driveTalon = mockTalon.constructed().get(0); + TalonFX turnTalon = mockTalon.constructed().get(1); + + when(driveTalon.getSimState()).thenReturn(driveSimState); + when(turnTalon.getSimState()).thenReturn(turnSimState); + + // update stubbing to return the correct signals per motor + when(driveTalon.getPosition()).thenReturn(drivePositionRotations); + when(driveTalon.getVelocity()).thenReturn(driveVelocityRotationsPerSecond); + when(driveTalon.getMotorVoltage()).thenReturn(driveAppliedVolts); + when(driveTalon.getSupplyCurrent()).thenReturn(driveSupplyCurrentAmps); + when(driveTalon.getTorqueCurrent()).thenReturn(driveTorqueCurrentAmps); + when(driveTalon.getDeviceTemp()).thenReturn(driveTemperatureCelcius); + when(driveTalon.getClosedLoopReference()).thenReturn(driveVelocitySetpointRotationsPerSecond); + when(driveTalon.getClosedLoopError()).thenReturn(driveVelocityErrorRotationsPerSecond); + + when(turnTalon.getPosition()).thenReturn(turnPositionRotations); + when(turnTalon.getVelocity()).thenReturn(turnVelocityRotationsPerSecond); + when(turnTalon.getMotorVoltage()).thenReturn(turnAppliedVolts); + when(turnTalon.getSupplyCurrent()).thenReturn(turnSupplyCurrentAmps); + when(turnTalon.getTorqueCurrent()).thenReturn(turnTorqueCurrentAmps); + when(turnTalon.getDeviceTemp()).thenReturn(turnTemperatureCelcius); + when(turnTalon.getClosedLoopReference()).thenReturn(turnPositionSetpointRotations); + when(turnTalon.getClosedLoopError()).thenReturn(turnPositionErrorRotations); + + SwerveModuleIO.ModuleIOInputs inputs = new SwerveModuleIO.ModuleIOInputs(); + sim.updateInputs(inputs); + + // Verify that getMotorVoltage() was called and setters on SimStates were invoked + verify(driveSimState).setSupplyVoltage(anyDouble()); + verify(driveSimState).setRawRotorPosition(anyDouble()); + verify(driveSimState).setRotorVelocity(anyDouble()); + + verify(turnSimState).setSupplyVoltage(anyDouble()); + verify(turnSimState).setRawRotorPosition(anyDouble()); + verify(turnSimState).setRotorVelocity(anyDouble()); + + verify(cancoderSimState).setRawPosition(anyDouble()); + } + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/drivebases/swervedrive/SwerveModuleIOTalonFXTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/drivebases/swervedrive/SwerveModuleIOTalonFXTest.java new file mode 100644 index 00000000..73429e48 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/drivebases/swervedrive/SwerveModuleIOTalonFXTest.java @@ -0,0 +1,387 @@ +package edu.wpi.team190.gompeilib.subsystems.drivebases.swervedrive; + +import static edu.wpi.first.units.Units.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import com.ctre.phoenix6.BaseStatusSignal; +import com.ctre.phoenix6.CANBus; +import com.ctre.phoenix6.StatusCode; +import com.ctre.phoenix6.StatusSignal; +import com.ctre.phoenix6.configs.CANcoderConfiguration; +import com.ctre.phoenix6.configs.CANcoderConfigurator; +import com.ctre.phoenix6.configs.TalonFXConfiguration; +import com.ctre.phoenix6.configs.TalonFXConfigurator; +import com.ctre.phoenix6.controls.*; +import com.ctre.phoenix6.hardware.CANcoder; +import com.ctre.phoenix6.hardware.TalonFX; +import com.ctre.phoenix6.swerve.SwerveModuleConstants; +import com.ctre.phoenix6.swerve.SwerveModuleConstants.ClosedLoopOutputType; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.units.measure.*; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import edu.wpi.team190.gompeilib.core.robot.RobotMode; +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.AngularPositionConstraints; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.LinearConstraints; +import edu.wpi.team190.gompeilib.core.utility.phoenix.PhoenixOdometryThread; +import edu.wpi.team190.gompeilib.core.utility.tunable.LoggedTunableMeasure; +import edu.wpi.team190.gompeilib.core.utility.tunable.LoggedTunableNumber; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockedConstruction; +import org.mockito.MockedStatic; + +public class SwerveModuleIOTalonFXTest { + + private SwerveDriveConstants driveConstants; + private SwerveModuleConstants + moduleConstants; + + @BeforeEach + public void setUp() { + edu.wpi.first.hal.HAL.initialize(500, 0); + try { + GompeiLib.deinit(); + } catch (Exception e) { + } + GompeiLib.init(RobotMode.SIM, false, 0.02); + + // Reset PhoenixOdometryThread instance via reflection to ensure clean state + try { + java.lang.reflect.Field instanceField = + PhoenixOdometryThread.class.getDeclaredField("instance"); + instanceField.setAccessible(true); + instanceField.set(null, null); + } catch (Exception e) { + } + + CANBus canBus = new CANBus("rio"); + DCMotor driveModel = DCMotor.getKrakenX60(1); + DCMotor turnModel = DCMotor.getKrakenX44(1); + + moduleConstants = SwerveDriveConstantsTest.createModuleConstants(1, 2, 3, 0.5, 0.5); + + SwerveDriveConstants.DriveConfig driveConfig = + SwerveDriveConstants.DriveConfig.builder() + .withCanBus(canBus) + .withPigeon2Id(13) + .withMaxLinearVelocityMetersPerSecond(5.0) + .withWheelRadiusMeters(0.05) + .withDriveModel(driveModel) + .withTurnModel(turnModel) + .withFrontLeft(moduleConstants) + .withFrontRight(moduleConstants) + .withBackLeft(moduleConstants) + .withBackRight(moduleConstants) + .withDriveClosedLoopOutputType(ClosedLoopOutputType.Voltage) + .withSteerClosedLoopOutputType(ClosedLoopOutputType.Voltage) + .withBumperWidth(0.8) + .withBumperLength(0.8) + .withRobotMassKilograms(45.0) + .withTrackWidth(0.6) + .withRobotMOI(2.5) + .withModuleCurrentLimit(40.0) + .withWheelCOF(1.2) + .build(); + + Gains driveGains = + Gains.builder() + .withKP(new LoggedTunableNumber("kp_drive", 1.0)) + .withKD(new LoggedTunableNumber("kd_drive", 0.1)) + .build(); + + Gains turnGains = + Gains.builder() + .withKP(new LoggedTunableNumber("kp_turn", 2.0)) + .withKD(new LoggedTunableNumber("kd_turn", 0.2)) + .build(); + + SwerveDriveConstants.AutoAlignConstants autoAlignConstants = + SwerveDriveConstants.AutoAlignConstants.builder() + .withXGains(driveGains) + .withXConstraints( + LinearConstraints.builder() + .withMaxVelocity(new LoggedTunableMeasure<>("max_v_x", MetersPerSecond.of(1.0))) + .withMaxAcceleration( + new LoggedTunableMeasure<>("max_a_x", MetersPerSecondPerSecond.of(1.0))) + .withGoalTolerance(new LoggedTunableMeasure<>("tol_x", Meters.of(0.05))) + .build()) + .withYGains(driveGains) + .withYConstraints( + LinearConstraints.builder() + .withMaxVelocity(new LoggedTunableMeasure<>("max_v_y", MetersPerSecond.of(1.0))) + .withMaxAcceleration( + new LoggedTunableMeasure<>("max_a_y", MetersPerSecondPerSecond.of(1.0))) + .withGoalTolerance(new LoggedTunableMeasure<>("tol_y", Meters.of(0.05))) + .build()) + .withRotationGains(turnGains) + .withRotationConstraints( + AngularPositionConstraints.builder() + .withMaxVelocity( + new LoggedTunableMeasure<>("max_v_rot", RadiansPerSecond.of(1.0))) + .withMaxAcceleration( + new LoggedTunableMeasure<>("max_a_rot", RadiansPerSecondPerSecond.of(1.0))) + .withGoalTolerance(new LoggedTunableMeasure<>("tol_rot", Radians.of(0.05))) + .build()) + .withLinearThreshold(new LoggedTunableMeasure<>("lin_thresh", Meters.of(0.01))) + .withAngularThreshold(new LoggedTunableMeasure<>("ang_thresh", Radians.of(0.01))) + .build(); + + driveConstants = + SwerveDriveConstants.builder() + .withDriveConfig(driveConfig) + .withDriveGains(driveGains) + .withTurnGains(turnGains) + .withAutoTranslationGains(driveGains) + .withAutoRotationGains(turnGains) + .withAutoAlignConstants(autoAlignConstants) + .withOdometryFrequency(250.0) + .withDriverDeadband(0.1) + .withOperatorDeadband(0.1) + .build(); + } + + @Test + public void testTalonFXIO() { + TalonFXConfigurator configurator = mock(TalonFXConfigurator.class); + when(configurator.apply(any(TalonFXConfiguration.class))).thenReturn(StatusCode.OK); + when(configurator.apply(any(TalonFXConfiguration.class), anyDouble())) + .thenReturn(StatusCode.OK); + + CANcoderConfigurator cancoderConfigurator = mock(CANcoderConfigurator.class); + when(cancoderConfigurator.apply(any(CANcoderConfiguration.class), anyDouble())) + .thenReturn(StatusCode.OK); + + StatusSignal drivePositionRotations = mock(StatusSignal.class); + StatusSignal driveVelocityRotationsPerSecond = mock(StatusSignal.class); + StatusSignal driveAppliedVolts = mock(StatusSignal.class); + StatusSignal driveSupplyCurrentAmps = mock(StatusSignal.class); + StatusSignal driveTorqueCurrentAmps = mock(StatusSignal.class); + StatusSignal driveTemperatureCelcius = mock(StatusSignal.class); + StatusSignal driveVelocitySetpointRotationsPerSecond = mock(StatusSignal.class); + StatusSignal driveVelocityErrorRotationsPerSecond = mock(StatusSignal.class); + + StatusSignal turnAbsolutePositionRotations = mock(StatusSignal.class); + StatusSignal turnPositionRotations = mock(StatusSignal.class); + StatusSignal turnVelocityRotationsPerSecond = mock(StatusSignal.class); + StatusSignal turnAppliedVolts = mock(StatusSignal.class); + StatusSignal turnSupplyCurrentAmps = mock(StatusSignal.class); + StatusSignal turnTorqueCurrentAmps = mock(StatusSignal.class); + StatusSignal turnTemperatureCelcius = mock(StatusSignal.class); + StatusSignal turnPositionSetpointRotations = mock(StatusSignal.class); + StatusSignal turnPositionErrorRotations = mock(StatusSignal.class); + + // Mock returns + when(drivePositionRotations.getValueAsDouble()).thenReturn(10.0); + when(driveVelocityRotationsPerSecond.getValueAsDouble()).thenReturn(2.5); + when(driveAppliedVolts.getValueAsDouble()).thenReturn(5.5); + when(driveSupplyCurrentAmps.getValueAsDouble()).thenReturn(12.0); + when(driveTorqueCurrentAmps.getValueAsDouble()).thenReturn(11.5); + when(driveTemperatureCelcius.getValueAsDouble()).thenReturn(35.0); + when(driveVelocitySetpointRotationsPerSecond.getValueAsDouble()).thenReturn(3.0); + when(driveVelocityErrorRotationsPerSecond.getValueAsDouble()).thenReturn(0.5); + + when(turnAbsolutePositionRotations.getValueAsDouble()).thenReturn(0.25); + when(turnPositionRotations.getValueAsDouble()).thenReturn(0.125); + when(turnVelocityRotationsPerSecond.getValueAsDouble()).thenReturn(1.2); + when(turnAppliedVolts.getValueAsDouble()).thenReturn(4.2); + when(turnSupplyCurrentAmps.getValueAsDouble()).thenReturn(8.0); + when(turnTorqueCurrentAmps.getValueAsDouble()).thenReturn(7.8); + when(turnTemperatureCelcius.getValueAsDouble()).thenReturn(40.0); + when(turnPositionSetpointRotations.getValueAsDouble()).thenReturn(0.13); + when(turnPositionErrorRotations.getValueAsDouble()).thenReturn(0.005); + + try (MockedConstruction mockTalon = + mockConstruction( + TalonFX.class, + (mock, context) -> { + when(mock.getConfigurator()).thenReturn(configurator); + if (context.getCount() == 1) { + when(mock.getPosition()).thenReturn(drivePositionRotations); + when(mock.getVelocity()).thenReturn(driveVelocityRotationsPerSecond); + when(mock.getMotorVoltage()).thenReturn(driveAppliedVolts); + when(mock.getSupplyCurrent()).thenReturn(driveSupplyCurrentAmps); + when(mock.getTorqueCurrent()).thenReturn(driveTorqueCurrentAmps); + when(mock.getDeviceTemp()).thenReturn(driveTemperatureCelcius); + when(mock.getClosedLoopReference()) + .thenReturn(driveVelocitySetpointRotationsPerSecond); + when(mock.getClosedLoopError()) + .thenReturn(driveVelocityErrorRotationsPerSecond); + } else { + when(mock.getPosition()).thenReturn(turnPositionRotations); + when(mock.getVelocity()).thenReturn(turnVelocityRotationsPerSecond); + when(mock.getMotorVoltage()).thenReturn(turnAppliedVolts); + when(mock.getSupplyCurrent()).thenReturn(turnSupplyCurrentAmps); + when(mock.getTorqueCurrent()).thenReturn(turnTorqueCurrentAmps); + when(mock.getDeviceTemp()).thenReturn(turnTemperatureCelcius); + when(mock.getClosedLoopReference()).thenReturn(turnPositionSetpointRotations); + when(mock.getClosedLoopError()).thenReturn(turnPositionErrorRotations); + } + when(mock.setPosition(anyDouble(), anyDouble())).thenReturn(StatusCode.OK); + when(mock.setControl(any(com.ctre.phoenix6.controls.ControlRequest.class))) + .thenReturn(StatusCode.OK); + }); + MockedConstruction mockCANcoder = + mockConstruction( + CANcoder.class, + (mock, context) -> { + when(mock.getConfigurator()).thenReturn(cancoderConfigurator); + when(mock.getAbsolutePosition()).thenReturn(turnAbsolutePositionRotations); + }); + MockedStatic mockBss = mockStatic(BaseStatusSignal.class)) { + + mockBss + .when( + () -> + BaseStatusSignal.setUpdateFrequencyForAll( + anyDouble(), any(BaseStatusSignal[].class))) + .thenReturn(StatusCode.OK); + + // We need separate setups for steer talon which returns turnPositionRotations, + // turnVelocityRotationsPerSecond, etc. + // So let's customize based on mock construction indexing if needed, or simply let the default + // mock configuration handle it: + // In constructor: + // driveTalonFX = new TalonFX(constants.DriveMotorId, driveConstants.driveConfig.canBus()); -> + // index 0 + // turnTalonFX = new TalonFX(constants.SteerMotorId, driveConstants.driveConfig.canBus()); -> + // index 1 + // Since they are construction mocked, we can set index-specific stubs. + SwerveModuleIOTalonFX io = new SwerveModuleIOTalonFX(driveConstants, moduleConstants); + + // Now stub the constructed objects specifically: + TalonFX driveTalon = mockTalon.constructed().get(0); + TalonFX turnTalon = mockTalon.constructed().get(1); + + when(driveTalon.getPosition()).thenReturn(drivePositionRotations); + when(driveTalon.getVelocity()).thenReturn(driveVelocityRotationsPerSecond); + when(driveTalon.getMotorVoltage()).thenReturn(driveAppliedVolts); + when(driveTalon.getSupplyCurrent()).thenReturn(driveSupplyCurrentAmps); + when(driveTalon.getTorqueCurrent()).thenReturn(driveTorqueCurrentAmps); + when(driveTalon.getDeviceTemp()).thenReturn(driveTemperatureCelcius); + when(driveTalon.getClosedLoopReference()).thenReturn(driveVelocitySetpointRotationsPerSecond); + when(driveTalon.getClosedLoopError()).thenReturn(driveVelocityErrorRotationsPerSecond); + + when(turnTalon.getPosition()).thenReturn(turnPositionRotations); + when(turnTalon.getVelocity()).thenReturn(turnVelocityRotationsPerSecond); + when(turnTalon.getMotorVoltage()).thenReturn(turnAppliedVolts); + when(turnTalon.getSupplyCurrent()).thenReturn(turnSupplyCurrentAmps); + when(turnTalon.getTorqueCurrent()).thenReturn(turnTorqueCurrentAmps); + when(turnTalon.getDeviceTemp()).thenReturn(turnTemperatureCelcius); + when(turnTalon.getClosedLoopReference()).thenReturn(turnPositionSetpointRotations); + when(turnTalon.getClosedLoopError()).thenReturn(turnPositionErrorRotations); + + SwerveModuleIO.ModuleIOInputs inputs = new SwerveModuleIO.ModuleIOInputs(); + io.updateInputs(inputs); + + // Verify inputs mapped correctly + assertEquals(10.0 * 2 * Math.PI, inputs.drivePositionRadians, 1e-4); + assertEquals(2.5 * 2 * Math.PI, inputs.driveVelocityRadiansPerSecond, 1e-4); + assertEquals(5.5, inputs.driveAppliedVolts, 1e-4); + assertEquals(12.0, inputs.driveSupplyCurrentAmps, 1e-4); + assertEquals(11.5, inputs.driveTorqueCurrentAmps, 1e-4); + assertEquals(35.0, inputs.driveTemperatureCelcius, 1e-4); + assertEquals(3.0 * 2 * Math.PI, inputs.driveVelocitySetpointRadiansPerSecond, 1e-4); + assertEquals(0.5 * 2 * Math.PI, inputs.driveVelocityErrorRadiansPerSecond, 1e-4); + + assertEquals(0.25, inputs.turnAbsolutePosition.getRotations(), 1e-4); + assertEquals(0.125, inputs.turnPosition.getRotations(), 1e-4); + assertEquals(1.2 * 2 * Math.PI, inputs.turnVelocityRadiansPerSecond, 1e-4); + assertEquals(4.2, inputs.turnAppliedVolts, 1e-4); + assertEquals(8.0, inputs.turnSupplyCurrentAmps, 1e-4); + assertEquals(7.8, inputs.turnTorqueCurrentAmps, 1e-4); + assertEquals(40.0, inputs.turnTemperatureCelcius, 1e-4); + assertEquals(0.13, inputs.turnPositionSetpoint.getRotations(), 1e-4); + assertEquals(0.005, inputs.turnPositionError.getRotations(), 1e-4); + + // Verify commands + io.setDriveAmps(10.0); + verify(driveTalon).setControl(any(TorqueCurrentFOC.class)); + + io.setTurnAmps(8.0); + verify(turnTalon).setControl(any(TorqueCurrentFOC.class)); + + // Drive velocity closed loop (Voltage branch) + io.setDriveVelocity(1.5, 0.2); + verify(driveTalon).setControl(any(VelocityVoltage.class)); + + // Turn position closed loop (Voltage branch) + io.setTurnPosition(Rotation2d.fromRotations(0.2)); + verify(turnTalon).setControl(any(MotionMagicVoltage.class)); + + // PID set + io.setPID(1.0, 0.1, 2.0, 0.2); + // Feedforward set + io.setFeedforward(0.05, 0.12); + // Current limit update + io.updateCurrentLimits(30.0, 20.0); + } + } + + @Test + public void testTalonFXIOTorqueCurrentFOCBranches() { + TalonFXConfigurator configurator = mock(TalonFXConfigurator.class); + when(configurator.apply(any(TalonFXConfiguration.class))).thenReturn(StatusCode.OK); + when(configurator.apply(any(TalonFXConfiguration.class), anyDouble())) + .thenReturn(StatusCode.OK); + + CANcoderConfigurator cancoderConfigurator = mock(CANcoderConfigurator.class); + when(cancoderConfigurator.apply(any(CANcoderConfiguration.class), anyDouble())) + .thenReturn(StatusCode.OK); + + // Setup moduleConstants with TorqueCurrentFOC outputs + SwerveModuleConstants + focConstants = SwerveDriveConstantsTest.createModuleConstants(1, 2, 3, 0.5, 0.5); + focConstants.DriveMotorClosedLoopOutput = ClosedLoopOutputType.TorqueCurrentFOC; + focConstants.SteerMotorClosedLoopOutput = ClosedLoopOutputType.TorqueCurrentFOC; + + try (MockedConstruction mockTalon = + mockConstruction( + TalonFX.class, + (mock, context) -> { + when(mock.getConfigurator()).thenReturn(configurator); + when(mock.getPosition()).thenReturn(mock(StatusSignal.class)); + when(mock.getVelocity()).thenReturn(mock(StatusSignal.class)); + when(mock.getMotorVoltage()).thenReturn(mock(StatusSignal.class)); + when(mock.getSupplyCurrent()).thenReturn(mock(StatusSignal.class)); + when(mock.getTorqueCurrent()).thenReturn(mock(StatusSignal.class)); + when(mock.getDeviceTemp()).thenReturn(mock(StatusSignal.class)); + when(mock.getClosedLoopReference()).thenReturn(mock(StatusSignal.class)); + when(mock.getClosedLoopError()).thenReturn(mock(StatusSignal.class)); + when(mock.setPosition(anyDouble(), anyDouble())).thenReturn(StatusCode.OK); + when(mock.setControl(any(com.ctre.phoenix6.controls.ControlRequest.class))) + .thenReturn(StatusCode.OK); + }); + MockedConstruction mockCANcoder = + mockConstruction( + CANcoder.class, + (mock, context) -> { + when(mock.getConfigurator()).thenReturn(cancoderConfigurator); + when(mock.getAbsolutePosition()).thenReturn(mock(StatusSignal.class)); + }); + MockedStatic mockBss = mockStatic(BaseStatusSignal.class)) { + + mockBss + .when( + () -> + BaseStatusSignal.setUpdateFrequencyForAll( + anyDouble(), any(BaseStatusSignal[].class))) + .thenReturn(StatusCode.OK); + + SwerveModuleIOTalonFX io = new SwerveModuleIOTalonFX(driveConstants, focConstants); + + TalonFX driveTalon = mockTalon.constructed().get(0); + TalonFX turnTalon = mockTalon.constructed().get(1); + + // Drive velocity closed loop (TorqueCurrentFOC branch) + io.setDriveVelocity(1.5, 0.2); + verify(driveTalon).setControl(any(VelocityTorqueCurrentFOC.class)); + + // Turn position closed loop (TorqueCurrentFOC branch) + io.setTurnPosition(Rotation2d.fromRotations(0.2)); + verify(turnTalon).setControl(any(MotionMagicTorqueCurrentFOC.class)); + } + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/drivebases/swervedrive/SwerveModuleIOTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/drivebases/swervedrive/SwerveModuleIOTest.java new file mode 100644 index 00000000..97353c21 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/drivebases/swervedrive/SwerveModuleIOTest.java @@ -0,0 +1,55 @@ +package edu.wpi.team190.gompeilib.subsystems.drivebases.swervedrive; + +import static org.junit.jupiter.api.Assertions.*; + +import edu.wpi.first.math.geometry.Rotation2d; +import org.junit.jupiter.api.Test; + +public class SwerveModuleIOTest { + @Test + public void testDefaultInterfaceMethods() { + SwerveModuleIO io = new SwerveModuleIO() {}; + + // Test calling default methods (they are no-ops, should not throw) + assertDoesNotThrow(() -> io.updateInputs(new SwerveModuleIO.ModuleIOInputs())); + assertDoesNotThrow(() -> io.setDriveAmps(1.0)); + assertDoesNotThrow(() -> io.setTurnAmps(2.0)); + assertDoesNotThrow(() -> io.setDriveVelocity(3.0, 0.5)); + assertDoesNotThrow(() -> io.setTurnPosition(new Rotation2d())); + assertDoesNotThrow(() -> io.setPID(1.0, 0.1, 2.0, 0.2)); + assertDoesNotThrow(() -> io.setFeedforward(0.1, 0.2)); + assertDoesNotThrow(() -> io.updateCurrentLimits(30.0, 20.0)); + } + + @Test + public void testModuleIOInputsDefaults() { + SwerveModuleIO.ModuleIOInputs inputs = new SwerveModuleIO.ModuleIOInputs(); + assertEquals(0.0, inputs.drivePositionRadians); + assertEquals(0.0, inputs.driveVelocityRadiansPerSecond); + assertEquals(0.0, inputs.driveAppliedVolts); + assertEquals(0.0, inputs.driveSupplyCurrentAmps); + assertEquals(0.0, inputs.driveTorqueCurrentAmps); + assertEquals(0.0, inputs.driveTemperatureCelcius); + assertEquals(0.0, inputs.driveVelocitySetpointRadiansPerSecond); + assertEquals(0.0, inputs.driveVelocityErrorRadiansPerSecond); + + assertEquals(0.0, inputs.turnAbsolutePosition.getRadians()); + assertEquals(0.0, inputs.turnPosition.getRadians()); + assertEquals(0.0, inputs.turnVelocityRadiansPerSecond); + assertEquals(0.0, inputs.turnAppliedVolts); + assertEquals(0.0, inputs.turnSupplyCurrentAmps); + assertEquals(0.0, inputs.turnTorqueCurrentAmps); + assertEquals(0.0, inputs.turnTemperatureCelcius); + assertEquals(0.0, inputs.turnPositionGoal.getRadians()); + assertEquals(0.0, inputs.turnPositionSetpoint.getRadians()); + assertEquals(0.0, inputs.turnPositionError.getRadians()); + + assertFalse(inputs.driveConnected); + assertFalse(inputs.turnConnected); + assertFalse(inputs.turnEncoderConnected); + + assertEquals(0, inputs.odometryTimestamps.length); + assertEquals(0, inputs.odometryDrivePositionsRadians.length); + assertEquals(0, inputs.odometryTurnPositions.length); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/drivebases/swervedrive/SwerveModuleTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/drivebases/swervedrive/SwerveModuleTest.java new file mode 100644 index 00000000..367b92cb --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/drivebases/swervedrive/SwerveModuleTest.java @@ -0,0 +1,242 @@ +package edu.wpi.team190.gompeilib.subsystems.drivebases.swervedrive; + +import static edu.wpi.first.units.Units.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import com.ctre.phoenix6.CANBus; +import com.ctre.phoenix6.configs.CANcoderConfiguration; +import com.ctre.phoenix6.configs.TalonFXConfiguration; +import com.ctre.phoenix6.swerve.SwerveModuleConstants; +import com.ctre.phoenix6.swerve.SwerveModuleConstants.ClosedLoopOutputType; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.kinematics.SwerveModulePosition; +import edu.wpi.first.math.kinematics.SwerveModuleState; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import edu.wpi.team190.gompeilib.core.robot.RobotMode; +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.AngularPositionConstraints; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.LinearConstraints; +import edu.wpi.team190.gompeilib.core.utility.phoenix.PhoenixOdometryThread; +import edu.wpi.team190.gompeilib.core.utility.tunable.LoggedTunableMeasure; +import edu.wpi.team190.gompeilib.core.utility.tunable.LoggedTunableNumber; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.littletonrobotics.junction.Logger; +import org.mockito.MockedStatic; + +public class SwerveModuleTest { + + private SwerveDriveConstants driveConstants; + private SwerveModuleConstants + moduleConstants; + + @BeforeEach + public void setUp() { + edu.wpi.first.hal.HAL.initialize(500, 0); + try { + GompeiLib.deinit(); + } catch (Exception e) { + } + GompeiLib.init(RobotMode.SIM, false, 0.02); + + try { + java.lang.reflect.Field instanceField = + PhoenixOdometryThread.class.getDeclaredField("instance"); + instanceField.setAccessible(true); + instanceField.set(null, null); + } catch (Exception e) { + } + + CANBus canBus = new CANBus("rio"); + DCMotor driveModel = DCMotor.getKrakenX60(1); + DCMotor turnModel = DCMotor.getKrakenX44(1); + + moduleConstants = SwerveDriveConstantsTest.createModuleConstants(1, 2, 3, 0.5, 0.5); + + SwerveDriveConstants.DriveConfig driveConfig = + SwerveDriveConstants.DriveConfig.builder() + .withCanBus(canBus) + .withPigeon2Id(13) + .withMaxLinearVelocityMetersPerSecond(5.0) + .withWheelRadiusMeters(0.05) + .withDriveModel(driveModel) + .withTurnModel(turnModel) + .withFrontLeft(moduleConstants) + .withFrontRight(moduleConstants) + .withBackLeft(moduleConstants) + .withBackRight(moduleConstants) + .withDriveClosedLoopOutputType(ClosedLoopOutputType.Voltage) + .withSteerClosedLoopOutputType(ClosedLoopOutputType.Voltage) + .withBumperWidth(0.8) + .withBumperLength(0.8) + .withRobotMassKilograms(45.0) + .withTrackWidth(0.6) + .withRobotMOI(2.5) + .withModuleCurrentLimit(40.0) + .withWheelCOF(1.2) + .build(); + + Gains driveGains = + Gains.builder() + .withKP(new LoggedTunableNumber("kp_drive", 1.0)) + .withKD(new LoggedTunableNumber("kd_drive", 0.1)) + .build(); + + Gains turnGains = + Gains.builder() + .withKP(new LoggedTunableNumber("kp_turn", 2.0)) + .withKD(new LoggedTunableNumber("kd_turn", 0.2)) + .build(); + + SwerveDriveConstants.AutoAlignConstants autoAlignConstants = + SwerveDriveConstants.AutoAlignConstants.builder() + .withXGains(driveGains) + .withXConstraints( + LinearConstraints.builder() + .withMaxVelocity(new LoggedTunableMeasure<>("max_v_x", MetersPerSecond.of(1.0))) + .withMaxAcceleration( + new LoggedTunableMeasure<>("max_a_x", MetersPerSecondPerSecond.of(1.0))) + .withGoalTolerance(new LoggedTunableMeasure<>("tol_x", Meters.of(0.05))) + .build()) + .withYGains(driveGains) + .withYConstraints( + LinearConstraints.builder() + .withMaxVelocity(new LoggedTunableMeasure<>("max_v_y", MetersPerSecond.of(1.0))) + .withMaxAcceleration( + new LoggedTunableMeasure<>("max_a_y", MetersPerSecondPerSecond.of(1.0))) + .withGoalTolerance(new LoggedTunableMeasure<>("tol_y", Meters.of(0.05))) + .build()) + .withRotationGains(turnGains) + .withRotationConstraints( + AngularPositionConstraints.builder() + .withMaxVelocity( + new LoggedTunableMeasure<>("max_v_rot", RadiansPerSecond.of(1.0))) + .withMaxAcceleration( + new LoggedTunableMeasure<>("max_a_rot", RadiansPerSecondPerSecond.of(1.0))) + .withGoalTolerance(new LoggedTunableMeasure<>("tol_rot", Radians.of(0.05))) + .build()) + .withLinearThreshold(new LoggedTunableMeasure<>("lin_thresh", Meters.of(0.01))) + .withAngularThreshold(new LoggedTunableMeasure<>("ang_thresh", Radians.of(0.01))) + .build(); + + driveConstants = + SwerveDriveConstants.builder() + .withDriveConfig(driveConfig) + .withDriveGains(driveGains) + .withTurnGains(turnGains) + .withAutoTranslationGains(driveGains) + .withAutoRotationGains(turnGains) + .withAutoAlignConstants(autoAlignConstants) + .withOdometryFrequency(250.0) + .withDriverDeadband(0.1) + .withOperatorDeadband(0.1) + .build(); + } + + @Test + public void testSwerveModule() { + SwerveModuleIO io = mock(SwerveModuleIO.class); + + // Stub updateInputs to populate the ModuleIOInputs fields + doAnswer( + invocation -> { + SwerveModuleIO.ModuleIOInputs in = invocation.getArgument(0); + in.driveConnected = true; + in.turnConnected = true; + in.turnEncoderConnected = true; + in.odometryTimestamps = new double[] {1.5}; + in.odometryDrivePositionsRadians = new double[] {20.0}; + in.odometryTurnPositions = new Rotation2d[] {Rotation2d.fromDegrees(30.0)}; + in.drivePositionRadians = 20.0; + in.driveVelocityRadiansPerSecond = 5.0; + in.turnPosition = Rotation2d.fromDegrees(30.0); + return null; + }) + .when(io) + .updateInputs(any()); + + try (MockedStatic mockLogger = mockStatic(Logger.class)) { + SwerveModule module = new SwerveModule(driveConstants, io, 0); + + module.updateInputs(); + verify(io).updateInputs(any()); + + module.periodic(); + + // Check odometry calculations + SwerveModulePosition[] positions = module.getOdometryPositions(); + assertEquals(1, positions.length); + assertEquals(20.0 * 0.05, positions[0].distanceMeters, 1e-6); + assertEquals(Rotation2d.fromDegrees(30.0), positions[0].angle); + + // Verify getters + assertEquals(Rotation2d.fromDegrees(30.0), module.getAngle()); + assertEquals(20.0 * 0.05, module.getPositionMeters(), 1e-6); + assertEquals(5.0 * 0.05, module.getVelocityMetersPerSec(), 1e-6); + assertEquals(20.0, module.getWheelRadiusCharacterizationPosition(), 1e-6); + assertEquals(5.0 / (2 * Math.PI), module.getFFCharacterizationVelocity(), 1e-6); + assertArrayEquals(new double[] {1.5}, module.getOdometryTimestamps()); + + SwerveModuleState state = module.getState(); + assertEquals(5.0 * 0.05, state.speedMetersPerSecond, 1e-6); + assertEquals(Rotation2d.fromDegrees(30.0), state.angle); + + SwerveModulePosition position = module.getPosition(); + assertEquals(20.0 * 0.05, position.distanceMeters, 1e-6); + assertEquals(Rotation2d.fromDegrees(30.0), position.angle); + + // Verify commands + SwerveModuleState setpoint = new SwerveModuleState(2.0, Rotation2d.fromDegrees(60.0)); + SwerveModuleState feedforward = new SwerveModuleState(0.5, Rotation2d.fromDegrees(60.0)); + module.runSetpoint(setpoint, feedforward); + verify(io).setDriveVelocity(eq(34.64101615137755), anyDouble()); + verify(io).setTurnPosition(any(Rotation2d.class)); + + module.runCharacterization(12.0); + verify(io).setDriveAmps(12.0); + + module.stop(); + verify(io).setDriveAmps(0.0); + verify(io).setTurnAmps(0.0); + + module.setPID(1.0, 0.1, 2.0, 0.2); + verify(io).setPID(1.0, 0.1, 2.0, 0.2); + + module.setFF(0.05, 0.1); + verify(io).setFeedforward(0.05, 0.1); + + module.updateCurrentLimits(30.0, 20.0); + verify(io).updateCurrentLimits(30.0, 20.0); + } + } + + @Test + public void testSwerveModuleDisconnectedAlerts() { + SwerveModuleIO io = mock(SwerveModuleIO.class); + + // Stub to report disconnected + doAnswer( + invocation -> { + SwerveModuleIO.ModuleIOInputs in = invocation.getArgument(0); + in.driveConnected = false; + in.turnConnected = false; + in.turnEncoderConnected = false; + in.odometryTimestamps = new double[0]; + in.odometryDrivePositionsRadians = new double[0]; + in.odometryTurnPositions = new Rotation2d[0]; + return null; + }) + .when(io) + .updateInputs(any()); + + try (MockedStatic mockLogger = mockStatic(Logger.class)) { + SwerveModule module = new SwerveModule(driveConstants, io, 1); + module.updateInputs(); + module.periodic(); + + assertEquals(0, module.getOdometryPositions().length); + } + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/elevator/ElevatorConstantsTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/elevator/ElevatorConstantsTest.java new file mode 100644 index 00000000..71b6d9d6 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/elevator/ElevatorConstantsTest.java @@ -0,0 +1,66 @@ +package edu.wpi.team190.gompeilib.subsystems.elevator; + +import static org.junit.jupiter.api.Assertions.*; + +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.units.Units; +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.LinearConstraints; +import org.junit.jupiter.api.Test; + +public class ElevatorConstantsTest { + @Test + public void testConstantsAndBuilder() { + DCMotor motor = DCMotor.getNeo550(1); + ElevatorConstants.ElevatorParameters params = + ElevatorConstants.ElevatorParameters.builder() + .withELEVATOR_MOTOR_CONFIG(motor) + .withCARRIAGE_MASS_KG(15.0) + .withMIN_HEIGHT(Units.Meters.of(0.0)) + .withMAX_HEIGHT(Units.Meters.of(1.5)) + .withNUM_MOTORS(1) + .build(); + + Gains slot0 = Gains.fromDoubles().withPrefix("test").withKP(1.0).build(); + LinearConstraints constraints = + LinearConstraints.fromMeasures() + .withPrefix("test") + .withMaxVelocity(Units.MetersPerSecond.of(1.0)) + .withMaxAcceleration(Units.MetersPerSecondPerSecond.of(1.0)) + .withGoalTolerance(Units.Meters.of(0.01)) + .build(); + + ElevatorConstants constants = + ElevatorConstants.builder() + .withLeaderCANID(5) + .withElevatorGearRatio(10.0) + .withDrumRadius(0.02) + .withElevatorSupplyCurrentLimit(40.0) + .withElevatorStatorCurrentLimit(40.0) + .withElevatorParameters(params) + .withSlot0Gains(slot0) + .withConstraints(constraints) + .withVoltageOffsetStep(Units.Volts.of(0.5)) + .withHeightOffsetStep(Units.Meters.of(0.05)) + .build(); + + assertNotNull(constants); + assertEquals(5, constants.leaderCANID); + assertEquals(10.0, constants.elevatorGearRatio); + assertEquals(0.02, constants.drumRadius); + assertEquals(40.0, constants.elevatorSupplyCurrentLimit); + assertEquals(40.0, constants.elevatorStatorCurrentLimit); + assertEquals(params, constants.elevatorParameters); + assertEquals(slot0, constants.slot0Gains); + assertEquals(constraints, constants.constraints); + assertEquals(0.5, constants.voltageOffsetStep.in(Units.Volts)); + assertEquals(0.05, constants.heightOffsetStep.in(Units.Meters)); + + // Test record fields + assertEquals(motor, params.ELEVATOR_MOTOR_CONFIG()); + assertEquals(15.0, params.CARRIAGE_MASS_KG()); + assertEquals(0.0, params.MIN_HEIGHT().in(Units.Meters)); + assertEquals(1.5, params.MAX_HEIGHT().in(Units.Meters)); + assertEquals(1, params.NUM_MOTORS()); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/elevator/ElevatorIOSimTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/elevator/ElevatorIOSimTest.java new file mode 100644 index 00000000..7476dc1f --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/elevator/ElevatorIOSimTest.java @@ -0,0 +1,134 @@ +package edu.wpi.team190.gompeilib.subsystems.elevator; + +import static org.junit.jupiter.api.Assertions.*; + +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.units.Units; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import edu.wpi.team190.gompeilib.core.robot.RobotMode; +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.LinearConstraints; +import edu.wpi.team190.gompeilib.core.utility.phoenix.GainSlot; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class ElevatorIOSimTest { + private ElevatorConstants constants; + + @BeforeEach + public void setUp() { + edu.wpi.first.hal.HAL.initialize(500, 0); + try { + GompeiLib.deinit(); + } catch (Exception e) { + } + GompeiLib.init(RobotMode.SIM, false, 0.02); + + DCMotor motor = DCMotor.getNeo550(1); + ElevatorConstants.ElevatorParameters params = + ElevatorConstants.ElevatorParameters.builder() + .withELEVATOR_MOTOR_CONFIG(motor) + .withCARRIAGE_MASS_KG(15.0) + .withMIN_HEIGHT(Units.Meters.of(0.0)) + .withMAX_HEIGHT(Units.Meters.of(1.5)) + .withNUM_MOTORS(2) + .build(); + + constants = + ElevatorConstants.builder() + .withLeaderCANID(5) + .withElevatorGearRatio(10.0) + .withDrumRadius(0.02) + .withElevatorSupplyCurrentLimit(40.0) + .withElevatorStatorCurrentLimit(40.0) + .withElevatorParameters(params) + .withSlot0Gains( + Gains.fromDoubles() + .withPrefix("slot0") + .withKP(1.0) + .withKI(0.0) + .withKD(0.1) + .withKS(0.01) + .withKV(0.01) + .withKA(0.01) + .withKG(0.1) + .build()) + .withSlot1Gains( + Gains.fromDoubles().withPrefix("slot1").withKP(2.0).withKI(0.0).withKD(0.2).build()) + .withSlot2Gains( + Gains.fromDoubles().withPrefix("slot2").withKP(3.0).withKI(0.0).withKD(0.3).build()) + .withConstraints( + LinearConstraints.fromMeasures() + .withPrefix("constraints") + .withMaxVelocity(Units.MetersPerSecond.of(2.0)) + .withMaxAcceleration(Units.MetersPerSecondPerSecond.of(2.0)) + .withGoalTolerance(Units.Meters.of(0.05)) + .build()) + .withVoltageOffsetStep(Units.Volts.of(0.5)) + .withHeightOffsetStep(Units.Meters.of(0.05)) + .build(); + } + + @Test + public void testElevatorIOSim() { + ElevatorIOSim sim = new ElevatorIOSim(constants); + ElevatorIO.ElevatorIOInputs inputs = new ElevatorIO.ElevatorIOInputs(); + + // Initial state set position + sim.setPosition(Units.Meters.of(0.5)); + sim.setVoltageGoal(Units.Volts.of(0.0)); + sim.updateInputs(inputs); + assertEquals(0.5, inputs.position.in(Units.Meters), 0.1); + + // Set position goal + sim.setPositionGoal(Units.Meters.of(1.0)); + sim.updateInputs(inputs); + assertEquals(2, inputs.appliedVolts.length); + assertEquals(GainSlot.ZERO, inputs.gainSlot); + + // Test gain slots + sim.setGainSlot(GainSlot.ONE); + sim.updateInputs(inputs); + assertEquals(GainSlot.ONE, inputs.gainSlot); + + sim.setGainSlot(GainSlot.TWO); + sim.updateInputs(inputs); + assertEquals(GainSlot.TWO, inputs.gainSlot); + + sim.setGainSlot(GainSlot.ZERO); + sim.updateInputs(inputs); + assertEquals(GainSlot.ZERO, inputs.gainSlot); + + // Set voltage goal (open loop) + sim.setVoltageGoal(Units.Volts.of(6.0)); + sim.updateInputs(inputs); + assertEquals(6.0, inputs.appliedVolts[0], 0.01); + assertTrue(sim.atVoltageGoal(Units.Volts.of(6.0))); + assertFalse(sim.atVoltageGoal(Units.Volts.of(0.0))); + + // update gains & constraints + sim.updateGains( + Gains.fromDoubles() + .withPrefix("slot0") + .withKP(4.0) + .withKI(0.0) + .withKD(0.4) + .withKS(0.02) + .withKG(0.2) + .withKV(0.02) + .withKA(0.0) + .build(), + GainSlot.ZERO); + sim.updateConstraints( + LinearConstraints.fromMeasures() + .withPrefix("constraints") + .withMaxVelocity(Units.MetersPerSecond.of(3.0)) + .withMaxAcceleration(Units.MetersPerSecondPerSecond.of(3.0)) + .withGoalTolerance(Units.Meters.of(0.1)) + .build()); + + sim.setPosition(Units.Meters.of(0.8)); + assertTrue(sim.atPositionGoal(Units.Meters.of(0.8))); + assertFalse(sim.atPositionGoal(Units.Meters.of(1.2))); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/elevator/ElevatorIOTalonFXSimTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/elevator/ElevatorIOTalonFXSimTest.java new file mode 100644 index 00000000..64a62919 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/elevator/ElevatorIOTalonFXSimTest.java @@ -0,0 +1,163 @@ +package edu.wpi.team190.gompeilib.subsystems.elevator; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import com.ctre.phoenix6.BaseStatusSignal; +import com.ctre.phoenix6.StatusCode; +import com.ctre.phoenix6.StatusSignal; +import com.ctre.phoenix6.configs.TalonFXConfiguration; +import com.ctre.phoenix6.configs.TalonFXConfigurator; +import com.ctre.phoenix6.hardware.TalonFX; +import com.ctre.phoenix6.sim.TalonFXSimState; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.units.Units; +import edu.wpi.first.units.measure.Angle; +import edu.wpi.first.units.measure.AngularAcceleration; +import edu.wpi.first.units.measure.AngularVelocity; +import edu.wpi.first.units.measure.Current; +import edu.wpi.first.units.measure.Temperature; +import edu.wpi.first.units.measure.Voltage; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import edu.wpi.team190.gompeilib.core.robot.RobotMode; +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.LinearConstraints; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockedConstruction; +import org.mockito.MockedStatic; + +public class ElevatorIOTalonFXSimTest { + private ElevatorConstants constants; + + @BeforeEach + public void setUp() { + edu.wpi.first.hal.HAL.initialize(500, 0); + try { + GompeiLib.deinit(); + } catch (Exception e) { + } + GompeiLib.init(RobotMode.SIM, false, 0.02); + + DCMotor motor = DCMotor.getNeo550(1); + ElevatorConstants.ElevatorParameters params = + ElevatorConstants.ElevatorParameters.builder() + .withELEVATOR_MOTOR_CONFIG(motor) + .withCARRIAGE_MASS_KG(15.0) + .withMIN_HEIGHT(Units.Meters.of(0.0)) + .withMAX_HEIGHT(Units.Meters.of(1.5)) + .withNUM_MOTORS(1) + .build(); + + constants = + ElevatorConstants.builder() + .withLeaderCANID(5) + .withElevatorGearRatio(10.0) + .withDrumRadius(0.02) + .withElevatorSupplyCurrentLimit(40.0) + .withElevatorStatorCurrentLimit(40.0) + .withElevatorParameters(params) + .withSlot0Gains( + Gains.fromDoubles() + .withPrefix("slot0") + .withKP(1.0) + .withKI(0.0) + .withKD(0.1) + .withKS(0.01) + .withKV(0.01) + .withKA(0.01) + .withKG(0.1) + .build()) + .withSlot1Gains( + Gains.fromDoubles().withPrefix("slot1").withKP(2.0).withKI(0.0).withKD(0.2).build()) + .withSlot2Gains( + Gains.fromDoubles().withPrefix("slot2").withKP(3.0).withKI(0.0).withKD(0.3).build()) + .withConstraints( + LinearConstraints.fromMeasures() + .withPrefix("constraints") + .withMaxVelocity(Units.MetersPerSecond.of(2.0)) + .withMaxAcceleration(Units.MetersPerSecondPerSecond.of(2.0)) + .withGoalTolerance(Units.Meters.of(0.05)) + .build()) + .withVoltageOffsetStep(Units.Volts.of(0.5)) + .withHeightOffsetStep(Units.Meters.of(0.05)) + .build(); + } + + @Test + public void testElevatorIOTalonFXSim() { + TalonFXConfigurator configurator = mock(TalonFXConfigurator.class); + when(configurator.apply(any(TalonFXConfiguration.class))).thenReturn(StatusCode.OK); + when(configurator.apply(any(TalonFXConfiguration.class), anyDouble())) + .thenReturn(StatusCode.OK); + + StatusSignal positionRotations = mock(StatusSignal.class); + StatusSignal velocityRotationsPerSecond = mock(StatusSignal.class); + StatusSignal accelerationRotationsPerSecondPerSecond = + mock(StatusSignal.class); + StatusSignal appliedVolts = mock(StatusSignal.class); + StatusSignal supplyCurrentAmps = mock(StatusSignal.class); + StatusSignal torqueCurrentAmps = mock(StatusSignal.class); + StatusSignal temperatureCelsius = mock(StatusSignal.class); + StatusSignal positionSetpointRotations = mock(StatusSignal.class); + StatusSignal positionErrorRotations = mock(StatusSignal.class); + StatusSignal closedLoopSlot = mock(StatusSignal.class); + + when(positionRotations.getValue()).thenReturn(Units.Rotations.of(10.0)); + when(positionRotations.getValueAsDouble()).thenReturn(10.0); + when(velocityRotationsPerSecond.getValue()).thenReturn(Units.RotationsPerSecond.of(1.5)); + when(velocityRotationsPerSecond.getValueAsDouble()).thenReturn(1.5); + when(accelerationRotationsPerSecondPerSecond.getValue()) + .thenReturn(Units.RotationsPerSecondPerSecond.of(0.5)); + when(appliedVolts.getValueAsDouble()).thenReturn(6.0); + when(appliedVolts.getValue()).thenReturn(Units.Volts.of(6.0)); + when(supplyCurrentAmps.getValueAsDouble()).thenReturn(15.0); + when(torqueCurrentAmps.getValueAsDouble()).thenReturn(12.0); + when(temperatureCelsius.getValueAsDouble()).thenReturn(45.0); + when(positionSetpointRotations.getValueAsDouble()).thenReturn(0.5); + when(positionErrorRotations.getValueAsDouble()).thenReturn(0.01); + when(closedLoopSlot.getValue()).thenReturn(0); + + TalonFXSimState simState = mock(TalonFXSimState.class); + when(simState.getMotorVoltage()).thenReturn(6.0); + + try (MockedConstruction mockTalon = + mockConstruction( + TalonFX.class, + (mock, context) -> { + when(mock.getConfigurator()).thenReturn(configurator); + when(mock.getPosition()).thenReturn(positionRotations); + when(mock.getVelocity()).thenReturn(velocityRotationsPerSecond); + when(mock.getAcceleration()).thenReturn(accelerationRotationsPerSecondPerSecond); + when(mock.getMotorVoltage()).thenReturn(appliedVolts); + when(mock.getSupplyCurrent()).thenReturn(supplyCurrentAmps); + when(mock.getTorqueCurrent()).thenReturn(torqueCurrentAmps); + when(mock.getDeviceTemp()).thenReturn(temperatureCelsius); + when(mock.getClosedLoopReference()).thenReturn(positionSetpointRotations); + when(mock.getClosedLoopError()).thenReturn(positionErrorRotations); + when(mock.getClosedLoopSlot()).thenReturn(closedLoopSlot); + when(mock.getSimState()).thenReturn(simState); + }); + MockedStatic mockBss = mockStatic(BaseStatusSignal.class)) { + + mockBss + .when( + () -> + BaseStatusSignal.setUpdateFrequencyForAll( + anyDouble(), any(BaseStatusSignal[].class))) + .thenReturn(null); + + ElevatorIOTalonFXSim sim = new ElevatorIOTalonFXSim(constants); + ElevatorIO.ElevatorIOInputs inputs = new ElevatorIO.ElevatorIOInputs(); + + sim.updateInputs(inputs); + + assertEquals(10.0, inputs.position.in(Units.Meters), 0.01); + assertEquals(1.5, inputs.velocity.in(Units.MetersPerSecond), 0.01); + + verify(simState).setSupplyVoltage(anyDouble()); + verify(simState).setRawRotorPosition(any(Angle.class)); + verify(simState).setRotorVelocity(any(AngularVelocity.class)); + } + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/elevator/ElevatorIOTalonFXTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/elevator/ElevatorIOTalonFXTest.java new file mode 100644 index 00000000..1fd9af81 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/elevator/ElevatorIOTalonFXTest.java @@ -0,0 +1,186 @@ +package edu.wpi.team190.gompeilib.subsystems.elevator; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import com.ctre.phoenix6.BaseStatusSignal; +import com.ctre.phoenix6.StatusCode; +import com.ctre.phoenix6.StatusSignal; +import com.ctre.phoenix6.configs.TalonFXConfiguration; +import com.ctre.phoenix6.configs.TalonFXConfigurator; +import com.ctre.phoenix6.hardware.TalonFX; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.units.Units; +import edu.wpi.first.units.measure.Angle; +import edu.wpi.first.units.measure.AngularAcceleration; +import edu.wpi.first.units.measure.AngularVelocity; +import edu.wpi.first.units.measure.Current; +import edu.wpi.first.units.measure.Temperature; +import edu.wpi.first.units.measure.Voltage; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import edu.wpi.team190.gompeilib.core.robot.RobotMode; +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.LinearConstraints; +import edu.wpi.team190.gompeilib.core.utility.phoenix.GainSlot; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockedConstruction; +import org.mockito.MockedStatic; + +public class ElevatorIOTalonFXTest { + private ElevatorConstants constants; + + @BeforeEach + public void setUp() { + edu.wpi.first.hal.HAL.initialize(500, 0); + try { + GompeiLib.deinit(); + } catch (Exception e) { + } + GompeiLib.init(RobotMode.SIM, false, 0.02); + + DCMotor motor = DCMotor.getNeo550(1); + ElevatorConstants.ElevatorParameters params = + ElevatorConstants.ElevatorParameters.builder() + .withELEVATOR_MOTOR_CONFIG(motor) + .withCARRIAGE_MASS_KG(15.0) + .withMIN_HEIGHT(Units.Meters.of(0.0)) + .withMAX_HEIGHT(Units.Meters.of(1.5)) + .withNUM_MOTORS(1) + .build(); + + constants = + ElevatorConstants.builder() + .withLeaderCANID(5) + .withElevatorGearRatio(10.0) + .withDrumRadius(0.02) + .withElevatorSupplyCurrentLimit(40.0) + .withElevatorStatorCurrentLimit(40.0) + .withElevatorParameters(params) + .withSlot0Gains( + Gains.fromDoubles() + .withPrefix("slot0") + .withKP(1.0) + .withKI(0.0) + .withKD(0.1) + .withKS(0.01) + .withKV(0.01) + .withKA(0.01) + .withKG(0.1) + .build()) + .withSlot1Gains( + Gains.fromDoubles().withPrefix("slot1").withKP(2.0).withKI(0.0).withKD(0.2).build()) + .withSlot2Gains( + Gains.fromDoubles().withPrefix("slot2").withKP(3.0).withKI(0.0).withKD(0.3).build()) + .withConstraints( + LinearConstraints.fromMeasures() + .withPrefix("constraints") + .withMaxVelocity(Units.MetersPerSecond.of(2.0)) + .withMaxAcceleration(Units.MetersPerSecondPerSecond.of(2.0)) + .withGoalTolerance(Units.Meters.of(0.05)) + .build()) + .withVoltageOffsetStep(Units.Volts.of(0.5)) + .withHeightOffsetStep(Units.Meters.of(0.05)) + .build(); + } + + @Test + public void testElevatorIOTalonFX() { + TalonFXConfigurator configurator = mock(TalonFXConfigurator.class); + when(configurator.apply(any(TalonFXConfiguration.class))).thenReturn(StatusCode.OK); + when(configurator.apply(any(TalonFXConfiguration.class), anyDouble())) + .thenReturn(StatusCode.OK); + + StatusSignal positionRotations = mock(StatusSignal.class); + StatusSignal velocityRotationsPerSecond = mock(StatusSignal.class); + StatusSignal accelerationRotationsPerSecondPerSecond = + mock(StatusSignal.class); + StatusSignal appliedVolts = mock(StatusSignal.class); + StatusSignal supplyCurrentAmps = mock(StatusSignal.class); + StatusSignal torqueCurrentAmps = mock(StatusSignal.class); + StatusSignal temperatureCelsius = mock(StatusSignal.class); + StatusSignal positionSetpointRotations = mock(StatusSignal.class); + StatusSignal positionErrorRotations = mock(StatusSignal.class); + StatusSignal closedLoopSlot = mock(StatusSignal.class); + + when(positionRotations.getValue()).thenReturn(Units.Rotations.of(10.0)); + when(positionRotations.getValueAsDouble()).thenReturn(10.0); + when(velocityRotationsPerSecond.getValue()).thenReturn(Units.RotationsPerSecond.of(1.5)); + when(velocityRotationsPerSecond.getValueAsDouble()).thenReturn(1.5); + when(accelerationRotationsPerSecondPerSecond.getValue()) + .thenReturn(Units.RotationsPerSecondPerSecond.of(0.5)); + when(appliedVolts.getValueAsDouble()).thenReturn(6.0); + when(appliedVolts.getValue()).thenReturn(Units.Volts.of(6.0)); + when(supplyCurrentAmps.getValueAsDouble()).thenReturn(15.0); + when(torqueCurrentAmps.getValueAsDouble()).thenReturn(12.0); + when(temperatureCelsius.getValueAsDouble()).thenReturn(45.0); + when(positionSetpointRotations.getValueAsDouble()).thenReturn(0.5); + when(positionErrorRotations.getValueAsDouble()).thenReturn(0.01); + when(closedLoopSlot.getValue()).thenReturn(0); + + try (MockedConstruction mockTalon = + mockConstruction( + TalonFX.class, + (mock, context) -> { + when(mock.getConfigurator()).thenReturn(configurator); + when(mock.getPosition()).thenReturn(positionRotations); + when(mock.getVelocity()).thenReturn(velocityRotationsPerSecond); + when(mock.getAcceleration()).thenReturn(accelerationRotationsPerSecondPerSecond); + when(mock.getMotorVoltage()).thenReturn(appliedVolts); + when(mock.getSupplyCurrent()).thenReturn(supplyCurrentAmps); + when(mock.getTorqueCurrent()).thenReturn(torqueCurrentAmps); + when(mock.getDeviceTemp()).thenReturn(temperatureCelsius); + when(mock.getClosedLoopReference()).thenReturn(positionSetpointRotations); + when(mock.getClosedLoopError()).thenReturn(positionErrorRotations); + when(mock.getClosedLoopSlot()).thenReturn(closedLoopSlot); + }); + MockedStatic mockBss = mockStatic(BaseStatusSignal.class)) { + + mockBss + .when( + () -> + BaseStatusSignal.setUpdateFrequencyForAll( + anyDouble(), any(BaseStatusSignal[].class))) + .thenReturn(null); + + ElevatorIOTalonFX io = new ElevatorIOTalonFX(constants); + ElevatorIO.ElevatorIOInputs inputs = new ElevatorIO.ElevatorIOInputs(); + + io.updateInputs(inputs); + + assertEquals(10.0, inputs.position.in(Units.Meters), 0.01); + assertEquals(1.5, inputs.velocity.in(Units.MetersPerSecond), 0.01); + assertEquals(6.0, inputs.appliedVolts[0], 0.01); + assertEquals(15.0, inputs.supplyCurrentAmps[0], 0.01); + assertEquals(12.0, inputs.torqueCurrentAmps[0], 0.01); + assertEquals(45.0, inputs.temperatureCelsius[0], 0.01); + assertEquals(0.5, inputs.positionSetpointMeters.in(Units.Meters), 0.001); + assertEquals(GainSlot.ZERO, inputs.gainSlot); + + io.setVoltageGoal(Units.Volts.of(8.0)); + io.setPositionGoal(Units.Meters.of(1.0)); + assertTrue(io.atVoltageGoal(Units.Volts.of(6.0))); + assertFalse(io.atVoltageGoal(Units.Volts.of(0.0))); + assertTrue(io.atPositionGoal(Units.Meters.of(10.0))); + assertFalse(io.atPositionGoal(Units.Meters.of(5.0))); + + io.setPosition(Units.Meters.of(0.5)); + + io.setGainSlot(GainSlot.ZERO); + io.setGainSlot(GainSlot.ONE); + io.setGainSlot(GainSlot.TWO); + + io.updateGains(Gains.fromDoubles().withPrefix("slot0").build(), GainSlot.ZERO); + io.updateGains(Gains.fromDoubles().withPrefix("slot1").build(), GainSlot.ONE); + io.updateGains(Gains.fromDoubles().withPrefix("slot2").build(), GainSlot.TWO); + + io.updateConstraints( + LinearConstraints.fromMeasures() + .withPrefix("constraints") + .withGoalTolerance(Units.Meters.of(0.01)) + .withMaxVelocity(Units.MetersPerSecond.of(1.0)) + .withMaxAcceleration(Units.MetersPerSecondPerSecond.of(1.0)) + .build()); + } + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/elevator/ElevatorIOTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/elevator/ElevatorIOTest.java new file mode 100644 index 00000000..b7c54c38 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/elevator/ElevatorIOTest.java @@ -0,0 +1,47 @@ +package edu.wpi.team190.gompeilib.subsystems.elevator; + +import static org.junit.jupiter.api.Assertions.*; + +import edu.wpi.first.units.Units; +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.LinearConstraints; +import edu.wpi.team190.gompeilib.core.utility.phoenix.GainSlot; +import org.junit.jupiter.api.Test; + +public class ElevatorIOTest { + @Test + public void testElevatorIODefaults() { + ElevatorIO io = new ElevatorIO() {}; + ElevatorIO.ElevatorIOInputs inputs = new ElevatorIO.ElevatorIOInputs(); + + // Call defaults - should not throw + io.updateInputs(inputs); + io.setVoltageGoal(Units.Volts.of(1.0)); + io.setPositionGoal(Units.Meters.of(1.0)); + assertFalse(io.atVoltageGoal(Units.Volts.of(1.0))); + assertFalse(io.atPositionGoal(Units.Meters.of(1.0))); + io.setPosition(Units.Meters.of(0.5)); + io.setGainSlot(GainSlot.ZERO); + io.updateGains(Gains.fromDoubles().withPrefix("test").build(), GainSlot.ZERO); + io.updateConstraints( + LinearConstraints.fromMeasures() + .withPrefix("test") + .withGoalTolerance(Units.Meters.of(0.01)) + .withMaxVelocity(Units.MetersPerSecond.of(1.0)) + .withMaxAcceleration(Units.MetersPerSecondPerSecond.of(1.0)) + .build()); + + // Inputs defaults + assertEquals(0.0, inputs.position.in(Units.Meters)); + assertEquals(0.0, inputs.velocity.in(Units.MetersPerSecond)); + assertEquals(0.0, inputs.acceleration.in(Units.MetersPerSecondPerSecond)); + assertEquals(0, inputs.appliedVolts.length); + assertEquals(0, inputs.supplyCurrentAmps.length); + assertEquals(0, inputs.torqueCurrentAmps.length); + assertEquals(0, inputs.temperatureCelsius.length); + assertEquals(0.0, inputs.positionGoalMeters.in(Units.Meters)); + assertEquals(0.0, inputs.positionSetpointMeters.in(Units.Meters)); + assertEquals(0.0, inputs.positionErrorMeters.in(Units.Meters)); + assertNull(inputs.gainSlot); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/elevator/ElevatorStateTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/elevator/ElevatorStateTest.java new file mode 100644 index 00000000..02cbd921 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/elevator/ElevatorStateTest.java @@ -0,0 +1,19 @@ +package edu.wpi.team190.gompeilib.subsystems.elevator; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +public class ElevatorStateTest { + @Test + public void testEnum() { + assertEquals(3, ElevatorState.values().length); + assertEquals(ElevatorState.IDLE, ElevatorState.valueOf("IDLE")); + assertEquals( + ElevatorState.OPEN_LOOP_VOLTAGE_CONTROL, + ElevatorState.valueOf("OPEN_LOOP_VOLTAGE_CONTROL")); + assertEquals( + ElevatorState.CLOSED_LOOP_POSITION_CONTROL, + ElevatorState.valueOf("CLOSED_LOOP_POSITION_CONTROL")); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/elevator/ElevatorTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/elevator/ElevatorTest.java new file mode 100644 index 00000000..510cb99c --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/elevator/ElevatorTest.java @@ -0,0 +1,140 @@ +package edu.wpi.team190.gompeilib.subsystems.elevator; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.units.Units; +import edu.wpi.first.units.measure.Distance; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.Subsystem; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import edu.wpi.team190.gompeilib.core.robot.RobotMode; +import edu.wpi.team190.gompeilib.core.utility.Setpoint; +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.LinearConstraints; +import edu.wpi.team190.gompeilib.core.utility.phoenix.GainSlot; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.littletonrobotics.junction.Logger; +import org.mockito.MockedStatic; + +public class ElevatorTest { + private ElevatorIO io; + private Subsystem subsystem; + private ElevatorConstants constants; + + @BeforeEach + public void setUp() { + edu.wpi.first.hal.HAL.initialize(500, 0); + try { + GompeiLib.deinit(); + } catch (Exception e) { + } + GompeiLib.init(RobotMode.SIM, false, 0.02); + + io = mock(ElevatorIO.class); + subsystem = mock(Subsystem.class, CALLS_REAL_METHODS); + when(subsystem.getName()).thenReturn("TestElevatorSubsystem"); + + DCMotor motor = DCMotor.getNeo550(1); + ElevatorConstants.ElevatorParameters params = + ElevatorConstants.ElevatorParameters.builder() + .withELEVATOR_MOTOR_CONFIG(motor) + .withCARRIAGE_MASS_KG(15.0) + .withMIN_HEIGHT(Units.Meters.of(0.0)) + .withMAX_HEIGHT(Units.Meters.of(1.5)) + .withNUM_MOTORS(1) + .build(); + + constants = + ElevatorConstants.builder() + .withLeaderCANID(5) + .withElevatorGearRatio(10.0) + .withDrumRadius(0.02) + .withElevatorSupplyCurrentLimit(40.0) + .withElevatorStatorCurrentLimit(40.0) + .withElevatorParameters(params) + .withSlot0Gains(Gains.fromDoubles().withPrefix("slot0").build()) + .withConstraints( + LinearConstraints.fromMeasures() + .withPrefix("constraints") + .withMaxVelocity(Units.MetersPerSecond.of(2.0)) + .withMaxAcceleration(Units.MetersPerSecondPerSecond.of(2.0)) + .withGoalTolerance(Units.Meters.of(0.05)) + .build()) + .withVoltageOffsetStep(Units.Volts.of(0.5)) + .withHeightOffsetStep(Units.Meters.of(0.05)) + .build(); + } + + @Test + public void testElevator() { + try (MockedStatic mockLogger = mockStatic(Logger.class)) { + Elevator elevator = new Elevator(constants, subsystem, 0, io); + + assertNotNull(elevator); + + // Test periodic in IDLE state + elevator.periodic(); + verify(io).updateInputs(any()); + + // Test open loop control state + elevator.setVoltageGoal(Units.Volts.of(6.0)); + elevator.periodic(); + verify(io).setVoltageGoal(Units.Volts.of(6.0)); + + // Test closed loop control state + elevator.setPositionGoal(Units.Meters.of(1.0)); + elevator.periodic(); + verify(io).setPositionGoal(any(Distance.class)); + + // Test setters/getters + elevator.setVoltageGoal( + new Setpoint<>( + Units.Volts.of(2.0), Units.Volts.of(0.1), Units.Volts.of(-12), Units.Volts.of(12))); + elevator.setPositionGoal( + new Setpoint<>( + Units.Meters.of(0.5), + Units.Meters.of(0.01), + Units.Meters.of(0.0), + Units.Meters.of(1.5))); + + elevator.getElevatorPosition(); + + // Test delegates + elevator.setPosition(Units.Meters.of(0.4)); + verify(io).setPosition(Units.Meters.of(0.4)); + + elevator.setGainSlot(GainSlot.ONE); + verify(io).setGainSlot(GainSlot.ONE); + + elevator.updateGains(Gains.fromDoubles().withPrefix("slot0").build(), GainSlot.ZERO); + verify(io).updateGains(any(), eq(GainSlot.ZERO)); + + elevator.updateConstraints( + LinearConstraints.fromMeasures() + .withPrefix("constraints") + .withGoalTolerance(Units.Meters.of(0.01)) + .withMaxVelocity(Units.MetersPerSecond.of(1.0)) + .withMaxAcceleration(Units.MetersPerSecondPerSecond.of(1.0)) + .build()); + verify(io).updateConstraints(any()); + + // test goals + when(io.atVoltageGoal(any())).thenReturn(true); + assertTrue(elevator.atVoltageGoal()); + assertTrue(elevator.atVoltageGoal(Units.Volts.of(2.0))); + + when(io.atPositionGoal(any())).thenReturn(true); + assertTrue(elevator.atPositionGoal()); + assertTrue(elevator.atPositionGoal(Units.Meters.of(0.5))); + + Command waitCmd = elevator.waitUntilAtGoal(); + assertNotNull(waitCmd); + + Command sysIdCmd = elevator.runSysIdRoutine(); + assertNotNull(sysIdCmd); + } + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/generic/flywheel/GenericFlywheelConstantsTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/generic/flywheel/GenericFlywheelConstantsTest.java new file mode 100644 index 00000000..695bb100 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/generic/flywheel/GenericFlywheelConstantsTest.java @@ -0,0 +1,62 @@ +package edu.wpi.team190.gompeilib.subsystems.generic.flywheel; + +import static org.junit.jupiter.api.Assertions.*; + +import com.ctre.phoenix6.CANBus; +import com.ctre.phoenix6.signals.InvertedValue; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.units.Units; +import edu.wpi.team190.gompeilib.core.utility.control.CurrentLimits; +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.AngularVelocityConstraints; +import org.junit.jupiter.api.Test; + +public class GenericFlywheelConstantsTest { + @Test + public void testConstantsAndBuilder() { + DCMotor motor = DCMotor.getNeo550(1); + Gains gains = Gains.fromDoubles().withPrefix("test").withKP(1.0).build(); + AngularVelocityConstraints constraints = + AngularVelocityConstraints.fromMeasures() + .withPrefix("test") + .withMaxVelocity(Units.RadiansPerSecond.of(100.0)) + .withMaxAcceleration(Units.RadiansPerSecondPerSecond.of(200.0)) + .withGoalTolerance(Units.RadiansPerSecond.of(1.0)) + .build(); + + GenericFlywheelConstants constants = + GenericFlywheelConstants.builder() + .withLeaderCANID(6) + .withLeaderInversion(InvertedValue.Clockwise_Positive) + .withCanBus(new CANBus("rio")) + .withEnableFOC(false) + .withCurrentLimit( + CurrentLimits.fromDoubles() + .withSupplyCurrentLimit(40.0) + .withStatorCurrentLimit(40.0) + .build()) + .withMomentOfInertia(0.01) + .withGearRatio(1.0) + .withMotorConfig(motor) + .withVoltageGains(gains) + .withTorqueGains(gains) + .withConstraints(constraints) + .withVelocityOffsetStep(Units.RadiansPerSecond.of(5.0)) + .withVoltageOffsetStep(Units.Volts.of(0.5)) + .build(); + + assertNotNull(constants); + assertEquals(6, constants.leaderCANID); + assertEquals(InvertedValue.Clockwise_Positive, constants.leaderInversion); + assertEquals("rio", constants.canBus.getName()); + assertFalse(constants.enableFOC); + assertEquals(0.01, constants.momentOfInertia); + assertEquals(1.0, constants.gearRatio); + assertEquals(motor, constants.motorConfig); + assertEquals(gains, constants.voltageGains); + assertEquals(gains, constants.torqueGains); + assertEquals(constraints, constants.constraints); + assertEquals(5.0, constants.velocityOffsetStep.in(Units.RadiansPerSecond)); + assertEquals(0.5, constants.voltageOffsetStep.in(Units.Volts)); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/generic/flywheel/GenericFlywheelIOSimTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/generic/flywheel/GenericFlywheelIOSimTest.java new file mode 100644 index 00000000..e1a58d51 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/generic/flywheel/GenericFlywheelIOSimTest.java @@ -0,0 +1,143 @@ +package edu.wpi.team190.gompeilib.subsystems.generic.flywheel; + +import static org.junit.jupiter.api.Assertions.*; + +import com.ctre.phoenix6.CANBus; +import com.ctre.phoenix6.signals.InvertedValue; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.units.Units; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import edu.wpi.team190.gompeilib.core.robot.RobotMode; +import edu.wpi.team190.gompeilib.core.utility.control.CurrentLimits; +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.AngularVelocityConstraints; +import edu.wpi.team190.gompeilib.core.utility.phoenix.GainSlot; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class GenericFlywheelIOSimTest { + private GenericFlywheelConstants constants; + + @BeforeEach + public void setUp() { + edu.wpi.first.hal.HAL.initialize(500, 0); + try { + GompeiLib.deinit(); + } catch (Exception e) { + } + GompeiLib.init(RobotMode.SIM, false, 0.02); + + constants = + GenericFlywheelConstants.builder() + .withLeaderCANID(5) + .withLeaderInversion(InvertedValue.CounterClockwise_Positive) + .withCanBus(new CANBus("rio")) + .withEnableFOC(false) + .withCurrentLimit( + CurrentLimits.fromDoubles() + .withSupplyCurrentLimit(40.0) + .withStatorCurrentLimit(40.0) + .build()) + .withMomentOfInertia(0.01) + .withGearRatio(1.0) + .withMotorConfig(DCMotor.getNeo550(1)) + .withVoltageGains( + Gains.fromDoubles() + .withPrefix("voltage") + .withKP(1.0) + .withKI(0.0) + .withKD(0.1) + .withKS(0.01) + .withKV(0.0104) + .withKA(0.0) + .withKG(0.0) + .build()) + .withTorqueGains( + Gains.fromDoubles() + .withPrefix("torque") + .withKP(0.5) + .withKI(0.0) + .withKD(0.05) + .withKS(0.0) + .withKV(0.0) + .withKA(0.0) + .withKG(0.0) + .build()) + .withConstraints( + AngularVelocityConstraints.fromMeasures() + .withPrefix("constraints") + .withMaxVelocity(Units.RadiansPerSecond.of(100.0)) + .withMaxAcceleration(Units.RadiansPerSecondPerSecond.of(50.0)) + .withGoalTolerance(Units.RadiansPerSecond.of(1.0)) + .build()) + .withVelocityOffsetStep(Units.RadiansPerSecond.of(5.0)) + .withVoltageOffsetStep(Units.Volts.of(0.5)) + .withAlignedFollowerCANID(6) + .withOpposedFollowerCANID(7) + .build(); + } + + @Test + public void testGenericFlywheelIOSim() { + GenericFlywheelIOSim sim = new GenericFlywheelIOSim(constants); + GenericFlywheelIO.GenericFlywheelIOInputs inputs = + new GenericFlywheelIO.GenericFlywheelIOInputs(); + + // Initial check + sim.updateInputs(inputs); + assertEquals(0.0, inputs.position.getRadians(), 1e-6); + assertEquals(0.0, inputs.velocity.in(Units.RadiansPerSecond), 1e-6); + assertEquals(3, inputs.appliedVolts.length); + + // Set voltage goal + sim.setVoltageGoal(Units.Volts.of(6.0)); + sim.updateInputs(inputs); + assertEquals(6.0, inputs.appliedVolts[0], 0.01); + assertTrue(sim.atVoltageGoal(Units.Volts.of(6.0))); + assertFalse(sim.atVoltageGoal(Units.Volts.of(0.0))); + + // Set velocity goal + sim.setVelocityGoal(Units.RadiansPerSecond.of(100.0)); + sim.updateInputs(inputs); + assertTrue(inputs.appliedVolts[0] > 0, "Actual applied volts: " + inputs.appliedVolts[0]); + + // atVelocityGoal + assertFalse(sim.atVelocityGoal(Units.RadiansPerSecond.of(100.0))); + // Let simulation run to reach goal + for (int i = 0; i < 150; i++) { + sim.updateInputs(inputs); + } + assertTrue(sim.atVelocityGoal(Units.RadiansPerSecond.of(100.0))); + + // setNeutralControl + sim.setNeutralControl(); + sim.updateInputs(inputs); + assertEquals(0.0, inputs.appliedVolts[0], 0.01); + + // updateGains and updateConstraints + sim.updateGains( + Gains.fromDoubles() + .withPrefix("newGains") + .withKP(2.0) + .withKI(0.0) + .withKD(0.2) + .withKS(0.02) + .withKV(0.2) + .withKA(0.01) + .withKG(0.0) + .build(), + GainSlot.ZERO); + + sim.updateConstraints( + AngularVelocityConstraints.fromMeasures() + .withPrefix("newConstraints") + .withMaxVelocity(Units.RadiansPerSecond.of(120.0)) + .withMaxAcceleration(Units.RadiansPerSecondPerSecond.of(60.0)) + .withGoalTolerance(Units.RadiansPerSecond.of(2.0)) + .build()); + + sim.setVelocityGoal(Units.RadiansPerSecond.of(20.0)); + sim.updateInputs(inputs); + assertEquals(GainSlot.ZERO, inputs.gainSlot); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/generic/flywheel/GenericFlywheelIOTalonFXSimTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/generic/flywheel/GenericFlywheelIOTalonFXSimTest.java new file mode 100644 index 00000000..570cf943 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/generic/flywheel/GenericFlywheelIOTalonFXSimTest.java @@ -0,0 +1,162 @@ +package edu.wpi.team190.gompeilib.subsystems.generic.flywheel; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import com.ctre.phoenix6.BaseStatusSignal; +import com.ctre.phoenix6.CANBus; +import com.ctre.phoenix6.StatusCode; +import com.ctre.phoenix6.StatusSignal; +import com.ctre.phoenix6.configs.TalonFXConfiguration; +import com.ctre.phoenix6.configs.TalonFXConfigurator; +import com.ctre.phoenix6.hardware.TalonFX; +import com.ctre.phoenix6.signals.InvertedValue; +import com.ctre.phoenix6.sim.TalonFXSimState; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.units.Units; +import edu.wpi.first.units.measure.*; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import edu.wpi.team190.gompeilib.core.robot.RobotMode; +import edu.wpi.team190.gompeilib.core.utility.control.CurrentLimits; +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.AngularVelocityConstraints; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockedConstruction; +import org.mockito.MockedStatic; + +public class GenericFlywheelIOTalonFXSimTest { + private GenericFlywheelConstants constants; + + @BeforeEach + public void setUp() { + edu.wpi.first.hal.HAL.initialize(500, 0); + try { + GompeiLib.deinit(); + } catch (Exception e) { + } + GompeiLib.init(RobotMode.SIM, false, 0.02); + + constants = + GenericFlywheelConstants.builder() + .withLeaderCANID(5) + .withLeaderInversion(InvertedValue.CounterClockwise_Positive) + .withCanBus(new CANBus("rio")) + .withEnableFOC(false) + .withCurrentLimit( + CurrentLimits.fromDoubles() + .withSupplyCurrentLimit(40.0) + .withStatorCurrentLimit(40.0) + .build()) + .withMomentOfInertia(0.01) + .withGearRatio(1.0) + .withMotorConfig(DCMotor.getNeo550(1)) + .withVoltageGains( + Gains.fromDoubles() + .withPrefix("voltage") + .withKP(1.0) + .withKI(0.0) + .withKD(0.1) + .withKS(0.01) + .withKV(0.1) + .withKA(0.0) + .withKG(0.0) + .build()) + .withTorqueGains( + Gains.fromDoubles() + .withPrefix("torque") + .withKP(0.5) + .withKI(0.0) + .withKD(0.05) + .withKS(0.0) + .withKV(0.0) + .withKA(0.0) + .withKG(0.0) + .build()) + .withConstraints( + AngularVelocityConstraints.fromMeasures() + .withPrefix("constraints") + .withMaxVelocity(Units.RadiansPerSecond.of(100.0)) + .withMaxAcceleration(Units.RadiansPerSecondPerSecond.of(50.0)) + .withGoalTolerance(Units.RadiansPerSecond.of(1.0)) + .build()) + .withVelocityOffsetStep(Units.RadiansPerSecond.of(5.0)) + .withVoltageOffsetStep(Units.Volts.of(0.5)) + .build(); + } + + @Test + public void testGenericFlywheelIOTalonFXSim() { + TalonFXConfigurator configurator = mock(TalonFXConfigurator.class); + when(configurator.apply(any(TalonFXConfiguration.class))).thenReturn(StatusCode.OK); + when(configurator.apply(any(TalonFXConfiguration.class), anyDouble())) + .thenReturn(StatusCode.OK); + + StatusSignal positionRotations = mock(StatusSignal.class); + StatusSignal velocityRotationsPerSecond = mock(StatusSignal.class); + StatusSignal appliedVolts = mock(StatusSignal.class); + StatusSignal supplyCurrentAmps = mock(StatusSignal.class); + StatusSignal torqueCurrentAmps = mock(StatusSignal.class); + StatusSignal temperatureCelsius = mock(StatusSignal.class); + StatusSignal velocitySetpointRotationsPerSecond = mock(StatusSignal.class); + StatusSignal velocityErrorRotationsPerSecond = mock(StatusSignal.class); + StatusSignal closedLoopSlot = mock(StatusSignal.class); + + when(positionRotations.getValueAsDouble()).thenReturn(10.0); + when(velocityRotationsPerSecond.getValue()).thenReturn(Units.RotationsPerSecond.of(5.0)); + when(velocityRotationsPerSecond.getValueAsDouble()).thenReturn(5.0); + when(appliedVolts.getValueAsDouble()).thenReturn(6.0); + when(appliedVolts.getValue()).thenReturn(Units.Volts.of(6.0)); + when(supplyCurrentAmps.getValueAsDouble()).thenReturn(15.0); + when(torqueCurrentAmps.getValueAsDouble()).thenReturn(12.0); + when(temperatureCelsius.getValueAsDouble()).thenReturn(45.0); + when(velocitySetpointRotationsPerSecond.getValueAsDouble()).thenReturn(5.0); + when(velocityErrorRotationsPerSecond.getValueAsDouble()).thenReturn(0.1); + when(closedLoopSlot.getValue()).thenReturn(0); + + TalonFXSimState simState = mock(TalonFXSimState.class); + when(simState.getMotorVoltage()).thenReturn(6.0); + + try (MockedConstruction mockTalon = + mockConstruction( + TalonFX.class, + (mock, context) -> { + when(mock.getConfigurator()).thenReturn(configurator); + when(mock.getPosition()).thenReturn(positionRotations); + when(mock.getVelocity()).thenReturn(velocityRotationsPerSecond); + when(mock.getMotorVoltage()).thenReturn(appliedVolts); + when(mock.getSupplyCurrent()).thenReturn(supplyCurrentAmps); + when(mock.getTorqueCurrent()).thenReturn(torqueCurrentAmps); + when(mock.getDeviceTemp()).thenReturn(temperatureCelsius); + when(mock.getClosedLoopReference()) + .thenReturn(velocitySetpointRotationsPerSecond); + when(mock.getClosedLoopError()).thenReturn(velocityErrorRotationsPerSecond); + when(mock.getClosedLoopSlot()).thenReturn(closedLoopSlot); + when(mock.getSimState()).thenReturn(simState); + when(mock.getNetwork()).thenReturn(new CANBus("rio")); + when(mock.getDeviceID()).thenReturn(5); + }); + MockedStatic mockBss = mockStatic(BaseStatusSignal.class)) { + + mockBss + .when( + () -> + BaseStatusSignal.setUpdateFrequencyForAll( + anyDouble(), any(BaseStatusSignal[].class))) + .thenReturn(null); + + GenericFlywheelIOTalonFXSim sim = new GenericFlywheelIOTalonFXSim(constants); + GenericFlywheelIO.GenericFlywheelIOInputs inputs = + new GenericFlywheelIO.GenericFlywheelIOInputs(); + + sim.updateInputs(inputs); + + assertEquals(10.0, inputs.position.getRotations(), 0.01); + assertEquals(5.0, inputs.velocity.in(Units.RotationsPerSecond), 0.01); + + verify(simState).setSupplyVoltage(anyDouble()); + verify(simState).setRotorVelocity(any(AngularVelocity.class)); + verify(simState).setRotorAcceleration(any(AngularAcceleration.class)); + } + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/generic/flywheel/GenericFlywheelIOTalonFXTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/generic/flywheel/GenericFlywheelIOTalonFXTest.java new file mode 100644 index 00000000..2f476fbc --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/generic/flywheel/GenericFlywheelIOTalonFXTest.java @@ -0,0 +1,193 @@ +package edu.wpi.team190.gompeilib.subsystems.generic.flywheel; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import com.ctre.phoenix6.BaseStatusSignal; +import com.ctre.phoenix6.CANBus; +import com.ctre.phoenix6.StatusCode; +import com.ctre.phoenix6.StatusSignal; +import com.ctre.phoenix6.configs.TalonFXConfiguration; +import com.ctre.phoenix6.configs.TalonFXConfigurator; +import com.ctre.phoenix6.hardware.TalonFX; +import com.ctre.phoenix6.signals.InvertedValue; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.units.Units; +import edu.wpi.first.units.measure.*; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import edu.wpi.team190.gompeilib.core.robot.RobotMode; +import edu.wpi.team190.gompeilib.core.utility.control.CurrentLimits; +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.AngularVelocityConstraints; +import edu.wpi.team190.gompeilib.core.utility.phoenix.GainSlot; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockedConstruction; +import org.mockito.MockedStatic; + +public class GenericFlywheelIOTalonFXTest { + private GenericFlywheelConstants constants; + + @BeforeEach + public void setUp() { + edu.wpi.first.hal.HAL.initialize(500, 0); + try { + GompeiLib.deinit(); + } catch (Exception e) { + } + GompeiLib.init(RobotMode.SIM, false, 0.02); + + constants = + GenericFlywheelConstants.builder() + .withLeaderCANID(5) + .withLeaderInversion(InvertedValue.CounterClockwise_Positive) + .withCanBus(new CANBus("rio")) + .withEnableFOC(false) + .withCurrentLimit( + CurrentLimits.fromDoubles() + .withSupplyCurrentLimit(40.0) + .withStatorCurrentLimit(40.0) + .build()) + .withMomentOfInertia(0.01) + .withGearRatio(1.0) + .withMotorConfig(DCMotor.getNeo550(1)) + .withVoltageGains( + Gains.fromDoubles() + .withPrefix("voltage") + .withKP(1.0) + .withKI(0.0) + .withKD(0.1) + .withKS(0.01) + .withKV(0.1) + .withKA(0.0) + .withKG(0.0) + .build()) + .withTorqueGains( + Gains.fromDoubles() + .withPrefix("torque") + .withKP(0.5) + .withKI(0.0) + .withKD(0.05) + .withKS(0.0) + .withKV(0.0) + .withKA(0.0) + .withKG(0.0) + .build()) + .withConstraints( + AngularVelocityConstraints.fromMeasures() + .withPrefix("constraints") + .withMaxVelocity(Units.RadiansPerSecond.of(100.0)) + .withMaxAcceleration(Units.RadiansPerSecondPerSecond.of(50.0)) + .withGoalTolerance(Units.RadiansPerSecond.of(1.0)) + .build()) + .withVelocityOffsetStep(Units.RadiansPerSecond.of(5.0)) + .withVoltageOffsetStep(Units.Volts.of(0.5)) + .withAlignedFollowerCANID(6) + .withOpposedFollowerCANID(7) + .build(); + } + + @Test + public void testGenericFlywheelIOTalonFX() { + TalonFXConfigurator configurator = mock(TalonFXConfigurator.class); + when(configurator.apply(any(TalonFXConfiguration.class))).thenReturn(StatusCode.OK); + when(configurator.apply(any(TalonFXConfiguration.class), anyDouble())) + .thenReturn(StatusCode.OK); + + StatusSignal positionRotations = mock(StatusSignal.class); + StatusSignal velocityRotationsPerSecond = mock(StatusSignal.class); + StatusSignal appliedVolts = mock(StatusSignal.class); + StatusSignal supplyCurrentAmps = mock(StatusSignal.class); + StatusSignal torqueCurrentAmps = mock(StatusSignal.class); + StatusSignal temperatureCelsius = mock(StatusSignal.class); + StatusSignal velocitySetpointRotationsPerSecond = mock(StatusSignal.class); + StatusSignal velocityErrorRotationsPerSecond = mock(StatusSignal.class); + StatusSignal closedLoopSlot = mock(StatusSignal.class); + + when(positionRotations.getValueAsDouble()).thenReturn(10.0); + when(velocityRotationsPerSecond.getValue()).thenReturn(Units.RotationsPerSecond.of(5.0)); + when(velocityRotationsPerSecond.getValueAsDouble()).thenReturn(5.0); + when(velocityRotationsPerSecond.isNear(any(), any())).thenReturn(true); + + when(appliedVolts.getValueAsDouble()).thenReturn(6.0); + when(appliedVolts.getValue()).thenReturn(Units.Volts.of(6.0)); + when(appliedVolts.isNear(any(), any())).thenReturn(true); + + when(supplyCurrentAmps.getValueAsDouble()).thenReturn(15.0); + + when(torqueCurrentAmps.getValueAsDouble()).thenReturn(12.0); + when(torqueCurrentAmps.getValue()).thenReturn(Units.Amps.of(12.0)); + when(torqueCurrentAmps.isNear(any(), any())).thenReturn(true); + + when(temperatureCelsius.getValueAsDouble()).thenReturn(45.0); + when(velocitySetpointRotationsPerSecond.getValueAsDouble()).thenReturn(5.0); + when(velocityErrorRotationsPerSecond.getValueAsDouble()).thenReturn(0.1); + when(closedLoopSlot.getValue()).thenReturn(0); + + try (MockedConstruction mockTalon = + mockConstruction( + TalonFX.class, + (mock, context) -> { + when(mock.getConfigurator()).thenReturn(configurator); + when(mock.getPosition()).thenReturn(positionRotations); + when(mock.getVelocity()).thenReturn(velocityRotationsPerSecond); + when(mock.getMotorVoltage()).thenReturn(appliedVolts); + when(mock.getSupplyCurrent()).thenReturn(supplyCurrentAmps); + when(mock.getTorqueCurrent()).thenReturn(torqueCurrentAmps); + when(mock.getDeviceTemp()).thenReturn(temperatureCelsius); + when(mock.getClosedLoopReference()) + .thenReturn(velocitySetpointRotationsPerSecond); + when(mock.getClosedLoopError()).thenReturn(velocityErrorRotationsPerSecond); + when(mock.getClosedLoopSlot()).thenReturn(closedLoopSlot); + when(mock.getNetwork()).thenReturn(new CANBus("rio")); + when(mock.getDeviceID()).thenReturn(5); + }); + MockedStatic mockBss = mockStatic(BaseStatusSignal.class)) { + + mockBss + .when( + () -> + BaseStatusSignal.setUpdateFrequencyForAll( + anyDouble(), any(BaseStatusSignal[].class))) + .thenReturn(null); + + GenericFlywheelIOTalonFX io = new GenericFlywheelIOTalonFX(constants); + GenericFlywheelIO.GenericFlywheelIOInputs inputs = + new GenericFlywheelIO.GenericFlywheelIOInputs(); + + io.updateInputs(inputs); + + // Verify the allocated inputs length matches leader + 2 followers + assertEquals(3, inputs.appliedVolts.length); + assertEquals(10.0, inputs.position.getRotations(), 0.01); + assertEquals(5.0, inputs.velocity.in(Units.RotationsPerSecond), 0.01); + assertEquals(6.0, inputs.appliedVolts[0], 0.01); + assertEquals(15.0, inputs.supplyCurrentAmps[0], 0.01); + assertEquals(12.0, inputs.torqueCurrentAmps[0], 0.01); + assertEquals(45.0, inputs.temperatureCelsius[0], 0.01); + + // Verify command / state check methods + io.setVoltageGoal(Units.Volts.of(8.0)); + io.setCurrentGoal(Units.Amps.of(20.0)); + io.setNeutralControl(); + io.setVelocityGoal(Units.RadiansPerSecond.of(10.0)); + io.setVelocityGoal(Units.RadiansPerSecond.of(10.0), Units.Amps.of(1.0)); + + assertTrue(io.atVoltageGoal(Units.Volts.of(6.0))); + assertTrue(io.atCurrentGoal(Units.Amps.of(12.0))); + assertTrue(io.atVelocityGoal(Units.RadiansPerSecond.of(10.0))); + + // Verify update gains / constraints loops + io.updateGains(Gains.fromDoubles().withPrefix("slot0").build(), GainSlot.ZERO); + io.updateGains(Gains.fromDoubles().withPrefix("slot1").build(), GainSlot.ONE); + + io.updateConstraints( + AngularVelocityConstraints.fromMeasures() + .withPrefix("newConstraints") + .withMaxVelocity(Units.RadiansPerSecond.of(120.0)) + .withMaxAcceleration(Units.RadiansPerSecondPerSecond.of(60.0)) + .withGoalTolerance(Units.RadiansPerSecond.of(2.0)) + .build()); + } + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/generic/flywheel/GenericFlywheelIOTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/generic/flywheel/GenericFlywheelIOTest.java new file mode 100644 index 00000000..498b463e --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/generic/flywheel/GenericFlywheelIOTest.java @@ -0,0 +1,49 @@ +package edu.wpi.team190.gompeilib.subsystems.generic.flywheel; + +import static org.junit.jupiter.api.Assertions.*; + +import edu.wpi.first.units.Units; +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.AngularVelocityConstraints; +import edu.wpi.team190.gompeilib.core.utility.phoenix.GainSlot; +import org.junit.jupiter.api.Test; + +public class GenericFlywheelIOTest { + @Test + public void testGenericFlywheelIODefaults() { + GenericFlywheelIO io = new GenericFlywheelIO() {}; + GenericFlywheelIO.GenericFlywheelIOInputs inputs = + new GenericFlywheelIO.GenericFlywheelIOInputs(); + + // Call defaults - should not throw + io.updateInputs(inputs); + io.setVoltageGoal(Units.Volts.of(1.0)); + io.setCurrentGoal(Units.Amps.of(1.0)); + io.setVelocityGoal(Units.RadiansPerSecond.of(1.0)); + io.setVelocityGoal(Units.RadiansPerSecond.of(1.0), Units.Amps.of(0.5)); + io.setNeutralControl(); + assertFalse(io.atVoltageGoal(Units.Volts.of(1.0))); + assertFalse(io.atCurrentGoal(Units.Amps.of(1.0))); + assertFalse(io.atVelocityGoal(Units.RadiansPerSecond.of(1.0))); + io.updateGains(Gains.fromDoubles().withPrefix("test").build(), GainSlot.ZERO); + io.updateConstraints( + AngularVelocityConstraints.fromMeasures() + .withPrefix("test") + .withGoalTolerance(Units.RadiansPerSecond.of(1.0)) + .withMaxVelocity(Units.RadiansPerSecond.of(100.0)) + .withMaxAcceleration(Units.RadiansPerSecondPerSecond.of(200.0)) + .build()); + + // Inputs defaults + assertEquals(0.0, inputs.position.getDegrees()); + assertEquals(0.0, inputs.velocity.in(Units.RadiansPerSecond)); + assertEquals(0, inputs.appliedVolts.length); + assertEquals(0, inputs.supplyCurrentAmps.length); + assertEquals(0, inputs.torqueCurrentAmps.length); + assertEquals(0, inputs.temperatureCelsius.length); + assertEquals(0.0, inputs.velocityGoal.in(Units.RadiansPerSecond)); + assertEquals(0.0, inputs.velocitySetpoint.in(Units.RadiansPerSecond)); + assertEquals(0.0, inputs.velocityError.in(Units.RadiansPerSecond)); + assertNull(inputs.gainSlot); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/generic/flywheel/GenericFlywheelStateTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/generic/flywheel/GenericFlywheelStateTest.java new file mode 100644 index 00000000..4c97c840 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/generic/flywheel/GenericFlywheelStateTest.java @@ -0,0 +1,22 @@ +package edu.wpi.team190.gompeilib.subsystems.generic.flywheel; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +public class GenericFlywheelStateTest { + @Test + public void testEnum() { + assertEquals(5, GenericFlywheelState.values().length); + assertEquals(GenericFlywheelState.IDLE, GenericFlywheelState.valueOf("IDLE")); + assertEquals(GenericFlywheelState.STOP, GenericFlywheelState.valueOf("STOP")); + assertEquals( + GenericFlywheelState.VELOCITY_VOLTAGE_CONTROL, + GenericFlywheelState.valueOf("VELOCITY_VOLTAGE_CONTROL")); + assertEquals( + GenericFlywheelState.VELOCITY_TORQUE_CONTROL, + GenericFlywheelState.valueOf("VELOCITY_TORQUE_CONTROL")); + assertEquals( + GenericFlywheelState.VOLTAGE_CONTROL, GenericFlywheelState.valueOf("VOLTAGE_CONTROL")); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/generic/flywheel/GenericFlywheelTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/generic/flywheel/GenericFlywheelTest.java new file mode 100644 index 00000000..5064f8f9 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/generic/flywheel/GenericFlywheelTest.java @@ -0,0 +1,201 @@ +package edu.wpi.team190.gompeilib.subsystems.generic.flywheel; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import com.ctre.phoenix6.CANBus; +import com.ctre.phoenix6.signals.InvertedValue; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.units.Units; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.Subsystem; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import edu.wpi.team190.gompeilib.core.robot.RobotMode; +import edu.wpi.team190.gompeilib.core.utility.Setpoint; +import edu.wpi.team190.gompeilib.core.utility.control.CurrentLimits; +import edu.wpi.team190.gompeilib.core.utility.control.Gains; +import edu.wpi.team190.gompeilib.core.utility.control.constraints.AngularVelocityConstraints; +import edu.wpi.team190.gompeilib.core.utility.phoenix.GainSlot; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.littletonrobotics.junction.Logger; +import org.mockito.MockedStatic; + +public class GenericFlywheelTest { + private GenericFlywheelIO io; + private Subsystem subsystem; + private GenericFlywheelConstants constants; + + @BeforeEach + public void setUp() { + edu.wpi.first.hal.HAL.initialize(500, 0); + try { + GompeiLib.deinit(); + } catch (Exception e) { + } + GompeiLib.init(RobotMode.SIM, false, 0.02); + + io = mock(GenericFlywheelIO.class); + subsystem = mock(Subsystem.class, CALLS_REAL_METHODS); + when(subsystem.getName()).thenReturn("TestFlywheelSubsystem"); + + constants = + GenericFlywheelConstants.builder() + .withLeaderCANID(5) + .withLeaderInversion(InvertedValue.CounterClockwise_Positive) + .withCanBus(new CANBus("rio")) + .withEnableFOC(false) + .withCurrentLimit( + CurrentLimits.fromDoubles() + .withSupplyCurrentLimit(40.0) + .withStatorCurrentLimit(40.0) + .build()) + .withMomentOfInertia(0.01) + .withGearRatio(1.0) + .withMotorConfig(DCMotor.getNeo550(1)) + .withVoltageGains(Gains.fromDoubles().withPrefix("voltage").build()) + .withTorqueGains(Gains.fromDoubles().withPrefix("torque").build()) + .withConstraints( + AngularVelocityConstraints.fromMeasures() + .withPrefix("constraints") + .withMaxVelocity(Units.RadiansPerSecond.of(100.0)) + .withMaxAcceleration(Units.RadiansPerSecondPerSecond.of(50.0)) + .withGoalTolerance(Units.RadiansPerSecond.of(1.0)) + .build()) + .withVelocityOffsetStep(Units.RadiansPerSecond.of(5.0)) + .withVoltageOffsetStep(Units.Volts.of(0.5)) + .build(); + } + + @Test + public void testGenericFlywheel() { + try (MockedStatic mockLogger = mockStatic(Logger.class)) { + GenericFlywheel flywheel = new GenericFlywheel(io, subsystem, constants, "Test"); + assertNotNull(flywheel); + assertEquals(GenericFlywheelState.IDLE, flywheel.getCurrentState()); + + // Test alternative constructors + GenericFlywheel flywheel2 = + new GenericFlywheel(io, subsystem, constants, "Test", flywheel.getVelocityGoal()); + assertNotNull(flywheel2); + + GenericFlywheel flywheel3 = + new GenericFlywheel( + io, + subsystem, + constants, + "Test", + flywheel.getVelocityGoal(), + flywheel.getVoltageGoal()); + assertNotNull(flywheel3); + + // periodic in IDLE + flywheel.periodic(); + verify(io).updateInputs(any()); + + // setVoltageGoal (Voltage) + flywheel.setVoltageGoal(Units.Volts.of(6.0)); + assertEquals(GenericFlywheelState.VOLTAGE_CONTROL, flywheel.getCurrentState()); + flywheel.periodic(); + verify(io).setVoltageGoal(Units.Volts.of(6.0)); + + // setVoltageGoal (Setpoint) + Setpoint voltSetpoint = + new Setpoint<>( + Units.Volts.of(4.0), + Units.Volts.of(0.5), + Units.Volts.of(-12.0), + Units.Volts.of(12.0)); + flywheel.setVoltageGoal(voltSetpoint); + flywheel.periodic(); + verify(io).setVoltageGoal(Units.Volts.of(4.0)); + + // setVelocityGoal (AngularVelocity) + flywheel.setVelocityGoal(Units.RadiansPerSecond.of(20.0)); + assertEquals(GenericFlywheelState.VELOCITY_VOLTAGE_CONTROL, flywheel.getCurrentState()); + flywheel.periodic(); + verify(io).setVelocityGoal(Units.RadiansPerSecond.of(20.0)); + + // setVelocityGoal (Setpoint) + Setpoint velocitySetpoint = + new Setpoint<>( + Units.RadiansPerSecond.of(30.0), + Units.RadiansPerSecond.of(5.0), + Units.RadiansPerSecond.of(-100.0), + Units.RadiansPerSecond.of(100.0)); + flywheel.setVelocityGoal(velocitySetpoint); + flywheel.periodic(); + verify(io).setVelocityGoal(Units.RadiansPerSecond.of(30.0)); + + // setVelocityGoal (Supplier) + flywheel.setVelocityGoal(() -> Units.RadiansPerSecond.of(40.0)); + flywheel.periodic(); + verify(io).setVelocityGoal(Units.RadiansPerSecond.of(40.0)); + + // setVelocityGoal (AngularVelocity, Current) + flywheel.setVelocityGoal(Units.RadiansPerSecond.of(50.0), Units.Amps.of(2.0)); + assertEquals(GenericFlywheelState.VELOCITY_TORQUE_CONTROL, flywheel.getCurrentState()); + flywheel.periodic(); + verify(io).setVelocityGoal(Units.RadiansPerSecond.of(50.0), Units.Amps.of(2.0)); + + // setVelocityGoal (Setpoint, Current) + velocitySetpoint = + new Setpoint<>( + Units.RadiansPerSecond.of(30.0), + Units.RadiansPerSecond.of(5.0), + Units.RadiansPerSecond.of(-100.0), + Units.RadiansPerSecond.of(100.0)); + flywheel.setVelocityGoal(velocitySetpoint, Units.Amps.of(3.0)); + flywheel.periodic(); + verify(io).setVelocityGoal(Units.RadiansPerSecond.of(30.0), Units.Amps.of(3.0)); + + // setVelocityGoal (Supplier, Supplier) + flywheel.setVelocityGoal(() -> Units.RadiansPerSecond.of(60.0), () -> Units.Amps.of(4.0)); + flywheel.periodic(); + verify(io).setVelocityGoal(Units.RadiansPerSecond.of(60.0), Units.Amps.of(4.0)); + + // stop + flywheel.stop(); + assertEquals(GenericFlywheelState.STOP, flywheel.getCurrentState()); + flywheel.periodic(); + verify(io).setNeutralControl(); + + // getters + assertNotNull(flywheel.getFlywheelVelocity()); + assertNotNull(flywheel.getFlywheelPosition()); + + // atGoal helpers + when(io.atVoltageGoal(any())).thenReturn(true); + when(io.atVelocityGoal(any())).thenReturn(true); + when(io.atCurrentGoal(any())).thenReturn(true); + + assertTrue(flywheel.atVoltageGoal(Units.Volts.of(1.0))); + assertTrue(flywheel.atVelocityGoal(Units.RadiansPerSecond.of(1.0))); + assertTrue(flywheel.atCurrentGoal(Units.Amps.of(1.0))); + + assertTrue(flywheel.atVoltageGoal()); + assertTrue(flywheel.atVelocityGoal()); + assertTrue(flywheel.atCurrentGoal()); + + Command waitCmd = flywheel.waitUntilAtGoal(); + assertNotNull(waitCmd); + + // updates + flywheel.updateGains(Gains.fromDoubles().withPrefix("slot0").build(), GainSlot.ZERO); + verify(io).updateGains(any(), eq(GainSlot.ZERO)); + + flywheel.updateConstraints( + AngularVelocityConstraints.fromMeasures() + .withPrefix("constraints") + .withMaxVelocity(Units.RadiansPerSecond.of(1.0)) + .withMaxAcceleration(Units.RadiansPerSecondPerSecond.of(1.0)) + .withGoalTolerance(Units.RadiansPerSecond.of(1.0)) + .build()); + verify(io).updateConstraints(any()); + + // sysid routines + assertNotNull(flywheel.sysIdRoutineVoltage()); + assertNotNull(flywheel.sysIdRoutineTorque()); + } + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/generic/roller/GenericRollerConstantsTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/generic/roller/GenericRollerConstantsTest.java new file mode 100644 index 00000000..cfd01024 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/generic/roller/GenericRollerConstantsTest.java @@ -0,0 +1,46 @@ +package edu.wpi.team190.gompeilib.subsystems.generic.roller; + +import static org.junit.jupiter.api.Assertions.*; + +import com.ctre.phoenix6.CANBus; +import com.ctre.phoenix6.signals.InvertedValue; +import com.ctre.phoenix6.signals.NeutralModeValue; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.units.Units; +import edu.wpi.team190.gompeilib.core.utility.control.CurrentLimits; +import java.util.Set; +import org.junit.jupiter.api.Test; + +public class GenericRollerConstantsTest { + @Test + public void testConstantsAndBuilder() { + GenericRollerConstants constants = + GenericRollerConstants.builder() + .withLeaderCANID(1) + .withLeaderInvertedValue(InvertedValue.Clockwise_Positive) + .withAlignedFollowerCANID(2) + .withOpposedFollowerCANID(3) + .withCurrentLimits( + CurrentLimits.fromDoubles() + .withSupplyCurrentLimit(30.0) + .withStatorCurrentLimit(30.0) + .build()) + .withRollerGearbox(DCMotor.getNeo550(1)) + .withRollerMotorGearRatio(5.0) + .withMomentOfInertia(Units.KilogramSquareMeters.of(0.001)) + .withNeutralMode(NeutralModeValue.Brake) + .withCanBus(new CANBus("rio")) + .withEnableFOC(false) + .withVoltageOffsetStep(Units.Volts.of(0.5)) + .build(); + + assertNotNull(constants); + assertEquals(1, constants.leaderCANID); + assertEquals(InvertedValue.Clockwise_Positive, constants.leaderInvertedValue); + assertEquals(Set.of(2), constants.alignedFollowerCANIDs); + assertEquals(Set.of(3), constants.opposedFollowerCANIDs); + assertEquals(5.0, constants.rollerMotorGearRatio); + assertEquals(NeutralModeValue.Brake, constants.neutralMode); + assertFalse(constants.enableFOC); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/generic/roller/GenericRollerIOSimTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/generic/roller/GenericRollerIOSimTest.java new file mode 100644 index 00000000..59079fc2 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/generic/roller/GenericRollerIOSimTest.java @@ -0,0 +1,70 @@ +package edu.wpi.team190.gompeilib.subsystems.generic.roller; + +import static org.junit.jupiter.api.Assertions.*; + +import com.ctre.phoenix6.CANBus; +import com.ctre.phoenix6.signals.InvertedValue; +import com.ctre.phoenix6.signals.NeutralModeValue; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.units.Units; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import edu.wpi.team190.gompeilib.core.robot.RobotMode; +import edu.wpi.team190.gompeilib.core.utility.control.CurrentLimits; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class GenericRollerIOSimTest { + private GenericRollerConstants constants; + + @BeforeEach + public void setUp() { + edu.wpi.first.hal.HAL.initialize(500, 0); + try { + GompeiLib.deinit(); + } catch (Exception e) { + } + GompeiLib.init(RobotMode.SIM, false, 0.02); + + constants = + GenericRollerConstants.builder() + .withLeaderCANID(1) + .withLeaderInvertedValue(InvertedValue.Clockwise_Positive) + .withCurrentLimits( + CurrentLimits.fromDoubles() + .withSupplyCurrentLimit(30.0) + .withStatorCurrentLimit(30.0) + .build()) + .withRollerGearbox(DCMotor.getNeo550(1)) + .withRollerMotorGearRatio(5.0) + .withMomentOfInertia(Units.KilogramSquareMeters.of(0.001)) + .withNeutralMode(NeutralModeValue.Brake) + .withCanBus(new CANBus("rio")) + .withEnableFOC(false) + .withVoltageOffsetStep(Units.Volts.of(0.5)) + .build(); + } + + @Test + public void testRollerIOSim() { + GenericRollerIOSim sim = new GenericRollerIOSim(constants); + GenericRollerIO.GenericRollerIOInputs inputs = new GenericRollerIO.GenericRollerIOInputs(); + + // Initial check + sim.updateInputs(inputs); + assertEquals(0.0, inputs.position.getRadians(), 1e-6); + assertEquals(0.0, inputs.velocity.in(Units.RadiansPerSecond), 1e-6); + assertEquals(1, inputs.appliedVolts.length); + + // Set voltage goal + sim.setVoltageGoal(Units.Volts.of(6.0)); + sim.updateInputs(inputs); + assertEquals(6.0, inputs.appliedVolts[0], 0.01); + assertTrue(sim.atVoltageGoal(Units.Volts.of(6.0))); + assertFalse(sim.atVoltageGoal(Units.Volts.of(0.0))); + + // Let simulation update and check position and velocity change + sim.updateInputs(inputs); + assertTrue(inputs.velocity.in(Units.RadiansPerSecond) > 0.0); + assertTrue(inputs.position.getRadians() > 0.0); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/generic/roller/GenericRollerIOTalonFXSimTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/generic/roller/GenericRollerIOTalonFXSimTest.java new file mode 100644 index 00000000..a3f57fa1 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/generic/roller/GenericRollerIOTalonFXSimTest.java @@ -0,0 +1,121 @@ +package edu.wpi.team190.gompeilib.subsystems.generic.roller; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import com.ctre.phoenix6.BaseStatusSignal; +import com.ctre.phoenix6.CANBus; +import com.ctre.phoenix6.StatusCode; +import com.ctre.phoenix6.StatusSignal; +import com.ctre.phoenix6.configs.TalonFXConfiguration; +import com.ctre.phoenix6.configs.TalonFXConfigurator; +import com.ctre.phoenix6.hardware.TalonFX; +import com.ctre.phoenix6.signals.InvertedValue; +import com.ctre.phoenix6.signals.NeutralModeValue; +import com.ctre.phoenix6.sim.TalonFXSimState; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.units.Units; +import edu.wpi.first.units.measure.*; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import edu.wpi.team190.gompeilib.core.robot.RobotMode; +import edu.wpi.team190.gompeilib.core.utility.control.CurrentLimits; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockedConstruction; +import org.mockito.MockedStatic; + +public class GenericRollerIOTalonFXSimTest { + private GenericRollerConstants constants; + + @BeforeEach + public void setUp() { + edu.wpi.first.hal.HAL.initialize(500, 0); + try { + GompeiLib.deinit(); + } catch (Exception e) { + } + GompeiLib.init(RobotMode.SIM, false, 0.02); + + constants = + GenericRollerConstants.builder() + .withLeaderCANID(1) + .withLeaderInvertedValue(InvertedValue.Clockwise_Positive) + .withCurrentLimits( + CurrentLimits.fromDoubles() + .withSupplyCurrentLimit(30.0) + .withStatorCurrentLimit(30.0) + .build()) + .withRollerGearbox(DCMotor.getNeo550(1)) + .withRollerMotorGearRatio(5.0) + .withMomentOfInertia(Units.KilogramSquareMeters.of(0.001)) + .withNeutralMode(NeutralModeValue.Brake) + .withCanBus(new CANBus("rio")) + .withEnableFOC(false) + .withVoltageOffsetStep(Units.Volts.of(0.5)) + .build(); + } + + @Test + public void testRollerIOTalonFXSim() { + TalonFXConfigurator configurator = mock(TalonFXConfigurator.class); + when(configurator.apply(any(TalonFXConfiguration.class))).thenReturn(StatusCode.OK); + when(configurator.apply(any(TalonFXConfiguration.class), anyDouble())) + .thenReturn(StatusCode.OK); + + StatusSignal positionRotations = mock(StatusSignal.class); + StatusSignal velocityRotationsPerSecond = mock(StatusSignal.class); + StatusSignal appliedVolts = mock(StatusSignal.class); + StatusSignal supplyCurrentAmps = mock(StatusSignal.class); + StatusSignal torqueCurrentAmps = mock(StatusSignal.class); + StatusSignal temperatureCelsius = mock(StatusSignal.class); + + when(positionRotations.getValueAsDouble()).thenReturn(10.0); + when(velocityRotationsPerSecond.getValue()).thenReturn(Units.RotationsPerSecond.of(5.0)); + when(velocityRotationsPerSecond.getValueAsDouble()).thenReturn(5.0); + when(appliedVolts.getValueAsDouble()).thenReturn(6.0); + when(appliedVolts.getValue()).thenReturn(Units.Volts.of(6.0)); + when(supplyCurrentAmps.getValueAsDouble()).thenReturn(15.0); + when(torqueCurrentAmps.getValueAsDouble()).thenReturn(12.0); + when(temperatureCelsius.getValueAsDouble()).thenReturn(45.0); + + TalonFXSimState simState = mock(TalonFXSimState.class); + when(simState.getMotorVoltage()).thenReturn(6.0); + + try (MockedConstruction mockTalon = + mockConstruction( + TalonFX.class, + (mock, context) -> { + when(mock.getConfigurator()).thenReturn(configurator); + when(mock.getPosition()).thenReturn(positionRotations); + when(mock.getVelocity()).thenReturn(velocityRotationsPerSecond); + when(mock.getMotorVoltage()).thenReturn(appliedVolts); + when(mock.getSupplyCurrent()).thenReturn(supplyCurrentAmps); + when(mock.getTorqueCurrent()).thenReturn(torqueCurrentAmps); + when(mock.getDeviceTemp()).thenReturn(temperatureCelsius); + when(mock.getSimState()).thenReturn(simState); + when(mock.getNetwork()).thenReturn(new CANBus("rio")); + when(mock.getDeviceID()).thenReturn(1); + }); + MockedStatic mockBss = mockStatic(BaseStatusSignal.class)) { + + mockBss + .when( + () -> + BaseStatusSignal.setUpdateFrequencyForAll( + anyDouble(), any(BaseStatusSignal[].class))) + .thenReturn(null); + + GenericRollerIOTalonFXSim sim = new GenericRollerIOTalonFXSim(constants); + GenericRollerIO.GenericRollerIOInputs inputs = new GenericRollerIO.GenericRollerIOInputs(); + + sim.updateInputs(inputs); + + assertEquals(10.0, inputs.position.getRotations(), 0.01); + assertEquals(5.0, inputs.velocity.in(Units.RotationsPerSecond), 0.01); + + verify(simState).setSupplyVoltage(anyDouble()); + verify(simState).setRawRotorPosition(any(Angle.class)); + verify(simState).setRotorVelocity(any(AngularVelocity.class)); + } + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/generic/roller/GenericRollerIOTalonFXTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/generic/roller/GenericRollerIOTalonFXTest.java new file mode 100644 index 00000000..cdfc77b5 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/generic/roller/GenericRollerIOTalonFXTest.java @@ -0,0 +1,123 @@ +package edu.wpi.team190.gompeilib.subsystems.generic.roller; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import com.ctre.phoenix6.BaseStatusSignal; +import com.ctre.phoenix6.CANBus; +import com.ctre.phoenix6.StatusCode; +import com.ctre.phoenix6.StatusSignal; +import com.ctre.phoenix6.configs.TalonFXConfiguration; +import com.ctre.phoenix6.configs.TalonFXConfigurator; +import com.ctre.phoenix6.hardware.TalonFX; +import com.ctre.phoenix6.signals.InvertedValue; +import com.ctre.phoenix6.signals.NeutralModeValue; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.units.Units; +import edu.wpi.first.units.measure.*; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import edu.wpi.team190.gompeilib.core.robot.RobotMode; +import edu.wpi.team190.gompeilib.core.utility.control.CurrentLimits; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockedConstruction; +import org.mockito.MockedStatic; + +public class GenericRollerIOTalonFXTest { + private GenericRollerConstants constants; + + @BeforeEach + public void setUp() { + edu.wpi.first.hal.HAL.initialize(500, 0); + try { + GompeiLib.deinit(); + } catch (Exception e) { + } + GompeiLib.init(RobotMode.SIM, false, 0.02); + + constants = + GenericRollerConstants.builder() + .withLeaderCANID(1) + .withLeaderInvertedValue(InvertedValue.Clockwise_Positive) + .withAlignedFollowerCANID(2) + .withOpposedFollowerCANID(3) + .withCurrentLimits( + CurrentLimits.fromDoubles() + .withSupplyCurrentLimit(30.0) + .withStatorCurrentLimit(30.0) + .build()) + .withRollerGearbox(DCMotor.getNeo550(1)) + .withRollerMotorGearRatio(5.0) + .withMomentOfInertia(Units.KilogramSquareMeters.of(0.001)) + .withNeutralMode(NeutralModeValue.Brake) + .withCanBus(new CANBus("rio")) + .withEnableFOC(false) + .withVoltageOffsetStep(Units.Volts.of(0.5)) + .build(); + } + + @Test + public void testRollerIOTalonFX() { + TalonFXConfigurator configurator = mock(TalonFXConfigurator.class); + when(configurator.apply(any(TalonFXConfiguration.class))).thenReturn(StatusCode.OK); + when(configurator.apply(any(TalonFXConfiguration.class), anyDouble())) + .thenReturn(StatusCode.OK); + + StatusSignal positionRotations = mock(StatusSignal.class); + StatusSignal velocityRotationsPerSecond = mock(StatusSignal.class); + StatusSignal appliedVolts = mock(StatusSignal.class); + StatusSignal supplyCurrentAmps = mock(StatusSignal.class); + StatusSignal torqueCurrentAmps = mock(StatusSignal.class); + StatusSignal temperatureCelsius = mock(StatusSignal.class); + + when(positionRotations.getValueAsDouble()).thenReturn(10.0); + when(velocityRotationsPerSecond.getValue()).thenReturn(Units.RotationsPerSecond.of(5.0)); + when(velocityRotationsPerSecond.getValueAsDouble()).thenReturn(5.0); + when(appliedVolts.getValueAsDouble()).thenReturn(6.0); + when(appliedVolts.getValue()).thenReturn(Units.Volts.of(6.0)); + when(appliedVolts.isNear(any(), any())).thenReturn(true); + when(supplyCurrentAmps.getValueAsDouble()).thenReturn(15.0); + when(torqueCurrentAmps.getValueAsDouble()).thenReturn(12.0); + when(temperatureCelsius.getValueAsDouble()).thenReturn(45.0); + + try (MockedConstruction mockTalon = + mockConstruction( + TalonFX.class, + (mock, context) -> { + when(mock.getConfigurator()).thenReturn(configurator); + when(mock.getPosition()).thenReturn(positionRotations); + when(mock.getVelocity()).thenReturn(velocityRotationsPerSecond); + when(mock.getMotorVoltage()).thenReturn(appliedVolts); + when(mock.getSupplyCurrent()).thenReturn(supplyCurrentAmps); + when(mock.getTorqueCurrent()).thenReturn(torqueCurrentAmps); + when(mock.getDeviceTemp()).thenReturn(temperatureCelsius); + when(mock.getNetwork()).thenReturn(new CANBus("rio")); + when(mock.getDeviceID()).thenReturn(1); + }); + MockedStatic mockBss = mockStatic(BaseStatusSignal.class)) { + + mockBss + .when( + () -> + BaseStatusSignal.setUpdateFrequencyForAll( + anyDouble(), any(BaseStatusSignal[].class))) + .thenReturn(null); + + GenericRollerIOTalonFX io = new GenericRollerIOTalonFX(constants); + GenericRollerIO.GenericRollerIOInputs inputs = new GenericRollerIO.GenericRollerIOInputs(); + + io.updateInputs(inputs); + + assertEquals(3, inputs.appliedVolts.length); + assertEquals(10.0, inputs.position.getRotations(), 0.01); + assertEquals(5.0, inputs.velocity.in(Units.RotationsPerSecond), 0.01); + assertEquals(6.0, inputs.appliedVolts[0], 0.01); + assertEquals(15.0, inputs.supplyCurrentAmps[0], 0.01); + assertEquals(12.0, inputs.torqueCurrentAmps[0], 0.01); + assertEquals(45.0, inputs.temperatureCelsius[0], 0.01); + + io.setVoltageGoal(Units.Volts.of(8.0)); + assertTrue(io.atVoltageGoal(Units.Volts.of(6.0))); + } + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/generic/roller/GenericRollerIOTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/generic/roller/GenericRollerIOTest.java new file mode 100644 index 00000000..2bba691c --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/generic/roller/GenericRollerIOTest.java @@ -0,0 +1,22 @@ +package edu.wpi.team190.gompeilib.subsystems.generic.roller; + +import static org.junit.jupiter.api.Assertions.*; + +import edu.wpi.first.units.Units; +import org.junit.jupiter.api.Test; + +public class GenericRollerIOTest { + @Test + public void testDefaultIO() { + GenericRollerIO io = new GenericRollerIO() {}; + GenericRollerIO.GenericRollerIOInputs inputs = new GenericRollerIO.GenericRollerIOInputs(); + + assertNotNull(inputs.position); + assertEquals(0.0, inputs.velocity.in(Units.RadiansPerSecond)); + + // Calling defaults should not throw + io.updateInputs(inputs); + io.setVoltageGoal(Units.Volts.of(0.0)); + assertFalse(io.atVoltageGoal(Units.Volts.of(0.0))); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/generic/roller/GenericRollerTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/generic/roller/GenericRollerTest.java new file mode 100644 index 00000000..68ab1c2a --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/generic/roller/GenericRollerTest.java @@ -0,0 +1,99 @@ +package edu.wpi.team190.gompeilib.subsystems.generic.roller; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import com.ctre.phoenix6.CANBus; +import com.ctre.phoenix6.signals.InvertedValue; +import com.ctre.phoenix6.signals.NeutralModeValue; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.units.Units; +import edu.wpi.first.wpilibj2.command.Subsystem; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import edu.wpi.team190.gompeilib.core.robot.RobotMode; +import edu.wpi.team190.gompeilib.core.utility.Setpoint; +import edu.wpi.team190.gompeilib.core.utility.control.CurrentLimits; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.littletonrobotics.junction.Logger; +import org.mockito.MockedStatic; + +public class GenericRollerTest { + private GenericRollerIO io; + private Subsystem subsystem; + private GenericRollerConstants constants; + + @BeforeEach + public void setUp() { + edu.wpi.first.hal.HAL.initialize(500, 0); + try { + GompeiLib.deinit(); + } catch (Exception e) { + } + GompeiLib.init(RobotMode.SIM, false, 0.02); + + io = mock(GenericRollerIO.class); + subsystem = mock(Subsystem.class, CALLS_REAL_METHODS); + when(subsystem.getName()).thenReturn("TestRollerSubsystem"); + + constants = + GenericRollerConstants.builder() + .withLeaderCANID(1) + .withLeaderInvertedValue(InvertedValue.Clockwise_Positive) + .withCurrentLimits( + CurrentLimits.fromDoubles() + .withSupplyCurrentLimit(30.0) + .withStatorCurrentLimit(30.0) + .build()) + .withRollerGearbox(DCMotor.getNeo550(1)) + .withRollerMotorGearRatio(5.0) + .withMomentOfInertia(Units.KilogramSquareMeters.of(0.001)) + .withNeutralMode(NeutralModeValue.Brake) + .withCanBus(new CANBus("rio")) + .withEnableFOC(false) + .withVoltageOffsetStep(Units.Volts.of(0.5)) + .build(); + } + + @Test + public void testRoller() { + try (MockedStatic mockLogger = mockStatic(Logger.class)) { + GenericRoller roller = new GenericRoller(io, subsystem, constants, "Test"); + assertNotNull(roller); + + // Alternative constructor + GenericRoller roller2 = + new GenericRoller(io, subsystem, constants, "Test", roller.getVoltageGoal()); + assertNotNull(roller2); + + // periodic + roller.periodic(); + verify(io).updateInputs(any()); + verify(io).setVoltageGoal(Units.Volts.of(0.0)); + + // setVoltageGoal (Voltage) + roller.setVoltageGoal(Units.Volts.of(6.0)); + roller.periodic(); + verify(io).setVoltageGoal(Units.Volts.of(6.0)); + + // setVoltageGoal (Setpoint) + Setpoint voltSetpoint = + new Setpoint<>( + Units.Volts.of(4.0), + Units.Volts.of(0.5), + Units.Volts.of(-12.0), + Units.Volts.of(12.0)); + roller.setVoltageGoal(voltSetpoint); + roller.periodic(); + verify(io).setVoltageGoal(Units.Volts.of(4.0)); + + // atVoltageGoal helpers + when(io.atVoltageGoal(any())).thenReturn(true); + assertTrue(roller.atVoltageGoal(Units.Volts.of(1.0))); + assertTrue(roller.atVoltageGoal()); + + // getTorqueCurrent + assertNotNull(roller.getTorqueCurrent()); + } + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/vision/VisionConstantsTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/vision/VisionConstantsTest.java new file mode 100644 index 00000000..a5c284e8 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/vision/VisionConstantsTest.java @@ -0,0 +1,139 @@ +package edu.wpi.team190.gompeilib.subsystems.vision; + +import static org.junit.jupiter.api.Assertions.*; + +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.geometry.Pose3d; +import edu.wpi.first.math.geometry.Rotation3d; +import edu.wpi.first.math.geometry.Transform3d; +import edu.wpi.first.math.geometry.Translation3d; +import edu.wpi.first.math.numbers.N1; +import edu.wpi.first.math.numbers.N3; +import edu.wpi.first.math.numbers.N5; +import edu.wpi.team190.gompeilib.subsystems.vision.camera.CameraType; +import org.ejml.simple.SimpleMatrix; +import org.junit.jupiter.api.Test; + +public class VisionConstantsTest { + + @Test + public void testConstants() { + assertEquals(0.4, VisionConstants.AMBIGUITY_THRESHOLD); + assertEquals(1.2, VisionConstants.XY_STDEV_DISTANCE_EXPONENT); + assertEquals(2.0, VisionConstants.XY_STDEV_TAG_COUNT_EXPONENT); + } + + @Test + public void testStaticLimelightConfig() { + Transform3d transform = new Transform3d(new Translation3d(1, 2, 3), new Rotation3d()); + VisionConstants.StaticLimelightConfig config = + VisionConstants.StaticLimelightConfig.builder() + .key("static-cam") + .cameraType(CameraType.LIMELIGHT_3) + .horizontalFOV(60.0) + .verticalFOV(40.0) + .megatagXYStdev(0.1) + .metatagThetaStdev(0.2) + .megatag2XYStdev(0.05) + .robotToCameraTransform(transform) + .enableRewind(true) + .build(); + + assertEquals("static-cam", config.key()); + assertEquals(CameraType.LIMELIGHT_3, config.cameraType()); + assertEquals(60.0, config.horizontalFOV()); + assertEquals(40.0, config.verticalFOV()); + assertEquals(0.1, config.megatagXYStdev()); + assertEquals(0.2, config.metatagThetaStdev()); + assertEquals(0.05, config.megatag2XYStdev()); + assertEquals(transform, config.robotToCameraTransform()); + assertTrue(config.enableRewind()); + + // For full record coverage, test toString, hashCode, equals + assertNotNull(config.toString()); + assertEquals(config.hashCode(), config.hashCode()); + assertEquals(config, config); + assertNotEquals(config, null); + } + + @Test + public void testMovingLimelightConfig() { + Transform3d t1 = new Transform3d(new Translation3d(1, 2, 3), new Rotation3d()); + Transform3d t2 = new Transform3d(new Translation3d(4, 5, 6), new Rotation3d()); + VisionConstants.MovingLimelightConfig config = + VisionConstants.MovingLimelightConfig.builder() + .key("moving-cam") + .cameraType(CameraType.LIMELIGHT_3G) + .horizontalFOV(80.0) + .verticalFOV(50.0) + .megatagXYStdev(0.15) + .metatagThetaStdev(0.25) + .megatag2XYStdev(0.06) + .robotToRotationAxisTransform(t1) + .rotationAxisToLensTransform(t2) + .enableRewind(false) + .build(); + + assertEquals("moving-cam", config.key()); + assertEquals(CameraType.LIMELIGHT_3G, config.cameraType()); + assertEquals(80.0, config.horizontalFOV()); + assertEquals(50.0, config.verticalFOV()); + assertEquals(0.15, config.megatagXYStdev()); + assertEquals(0.25, config.metatagThetaStdev()); + assertEquals(0.06, config.megatag2XYStdev()); + assertEquals(t1, config.robotToRotationAxisTransform()); + assertEquals(t2, config.rotationAxisToLensTransform()); + assertFalse(config.enableRewind()); + + // Record coverage + assertNotNull(config.toString()); + assertEquals(config.hashCode(), config.hashCode()); + assertEquals(config, config); + } + + @Test + public void testGompeiVisionConfig() { + Matrix cameraMatrix = new Matrix<>(new SimpleMatrix(3, 3)); + Matrix distortion = new Matrix<>(new SimpleMatrix(5, 1)); + Pose3d pose = new Pose3d(); + VisionConstants.GompeiVisionConfig config = + VisionConstants.GompeiVisionConfig.builder() + .key("gompei-cam") + .hardwareID("HW123") + .cameraType(CameraType.THRIFTYCAM) + .exposure(10.0) + .gain(5.0) + .width(640) + .height(480) + .cameraMatrix(cameraMatrix) + .distortionCoefficients(distortion) + .horizontalFOV(70.0) + .verticalFOV(45.0) + .singletagXYStdev(0.3) + .thetaStdev(0.4) + .multitagXYStdev(0.05) + .robotRelativePose(pose) + .build(); + + assertEquals("gompei-cam", config.key()); + assertEquals("HW123", config.hardwareID()); + assertEquals(CameraType.THRIFTYCAM, config.cameraType()); + assertEquals(10.0, config.exposure()); + assertEquals(5.0, config.gain()); + assertEquals(640, config.width()); + assertEquals(480, config.height()); + assertEquals(cameraMatrix, config.cameraMatrix()); + assertEquals(distortion, config.distortionCoefficients()); + assertEquals(70.0, config.horizontalFOV()); + assertEquals(45.0, config.verticalFOV()); + assertEquals(0.3, config.singletagXYStdev()); + assertEquals(0.4, config.thetaStdev()); + assertEquals(0.05, config.multitagXYStdev()); + assertEquals(pose, config.robotRelativePose()); + + // Record coverage + assertNotNull(config.toString()); + assertEquals(config.hashCode(), config.hashCode()); + assertEquals(config, config); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/vision/VisionTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/vision/VisionTest.java new file mode 100644 index 00000000..4ebb1eb5 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/vision/VisionTest.java @@ -0,0 +1,54 @@ +package edu.wpi.team190.gompeilib.subsystems.vision; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import edu.wpi.first.apriltag.AprilTag; +import edu.wpi.first.apriltag.AprilTagFieldLayout; +import edu.wpi.first.math.geometry.Pose3d; +import edu.wpi.first.networktables.NetworkTableInstance; +import edu.wpi.team190.gompeilib.subsystems.vision.camera.Camera; +import java.util.List; +import org.junit.jupiter.api.Test; + +public class VisionTest { + + @Test + public void testVisionInitializationAndPeriodic() { + AprilTag tag1 = + new AprilTag(1, new Pose3d(1.0, 2.0, 3.0, new edu.wpi.first.math.geometry.Rotation3d())); + AprilTagFieldLayout layout = new AprilTagFieldLayout(List.of(tag1), 16.0, 8.0); + + Camera mockCamera1 = mock(Camera.class); + Camera mockCamera2 = mock(Camera.class); + + Vision vision = new Vision(() -> layout, mockCamera1, mockCamera2); + + assertSame(layout, vision.getFieldLayoutSupplier().get()); + assertEquals(2, vision.getCameras().length); + assertSame(mockCamera1, vision.getCameras()[0]); + assertSame(mockCamera2, vision.getCameras()[1]); + + // Check that tags are published to NetworkTables table "field" + double[] tagData = + NetworkTableInstance.getDefault() + .getTable("field") + .getDoubleArrayTopic("tag_1") + .subscribe(new double[0]) + .get(); + + assertEquals(7, tagData.length); + assertEquals(1.0, tagData[0]); + assertEquals(2.0, tagData[1]); + assertEquals(3.0, tagData[2]); + assertEquals(1.0, tagData[3]); // W of Quaternion (no rotation) + assertEquals(0.0, tagData[4]); // X of Quaternion + assertEquals(0.0, tagData[5]); // Y of Quaternion + assertEquals(0.0, tagData[6]); // Z of Quaternion + + // Verify periodic + vision.periodic(); + verify(mockCamera1, times(1)).periodic(); + verify(mockCamera2, times(1)).periodic(); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/vision/camera/CameraGompeiVisionTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/vision/camera/CameraGompeiVisionTest.java new file mode 100644 index 00000000..dd11b4a4 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/vision/camera/CameraGompeiVisionTest.java @@ -0,0 +1,242 @@ +package edu.wpi.team190.gompeilib.subsystems.vision.camera; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import edu.wpi.first.apriltag.AprilTag; +import edu.wpi.first.apriltag.AprilTagFieldLayout; +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Pose3d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.numbers.N1; +import edu.wpi.first.math.numbers.N3; +import edu.wpi.first.math.numbers.N5; +import edu.wpi.first.wpilibj.DriverStation; +import edu.wpi.team190.gompeilib.subsystems.vision.VisionConstants; +import edu.wpi.team190.gompeilib.subsystems.vision.data.VisionMultiTxTyObservation; +import edu.wpi.team190.gompeilib.subsystems.vision.data.VisionPoseObservation; +import edu.wpi.team190.gompeilib.subsystems.vision.io.CameraIO; +import edu.wpi.team190.gompeilib.subsystems.vision.io.CameraIOGompeiVision; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; +import org.ejml.simple.SimpleMatrix; +import org.junit.jupiter.api.Test; +import org.littletonrobotics.junction.Logger; +import org.mockito.MockedStatic; + +public class CameraGompeiVisionTest { + + @Test + public void testGompeiVisionCamera() { + CameraIOGompeiVision io = mock(CameraIOGompeiVision.class); + + Matrix cameraMatrix = new Matrix<>(new SimpleMatrix(3, 3)); + Matrix distortion = new Matrix<>(new SimpleMatrix(5, 1)); + Pose3d cameraPoseRel = new Pose3d(0.5, 0.0, 0.5, new edu.wpi.first.math.geometry.Rotation3d()); + + VisionConstants.GompeiVisionConfig config = + VisionConstants.GompeiVisionConfig.builder() + .key("gompei") + .cameraType(CameraType.THRIFTYCAM) + .cameraMatrix(cameraMatrix) + .distortionCoefficients(distortion) + .singletagXYStdev(0.1) + .thetaStdev(0.2) + .multitagXYStdev(0.01) + .robotRelativePose(cameraPoseRel) + .build(); + + AprilTag tag1 = + new AprilTag(1, new Pose3d(1.0, 2.0, 3.0, new edu.wpi.first.math.geometry.Rotation3d())); + AprilTagFieldLayout layout = new AprilTagFieldLayout(List.of(tag1), 16.0, 8.0); + + List poses = new ArrayList<>(); + List txtys = new ArrayList<>(); + + Consumer> poseObserver = poses::addAll; + Consumer> txtyObserver = txtys::addAll; + + CameraGompeiVision camera = + new CameraGompeiVision( + io, + config, + () -> layout, + 0.5, + () -> new Pose2d(0.0, 0.0, new Rotation2d()), + List.of(poseObserver), + List.of(txtyObserver)); + + assertEquals("gompei", camera.getName()); + assertEquals(cameraPoseRel, camera.getCurrentCameraPose()); + + try (MockedStatic mockLogger = mockStatic(Logger.class); + MockedStatic mockDS = mockStatic(DriverStation.class)) { + + // --- Test empty frames --- + doAnswer( + invocation -> { + var inputs = + (edu.wpi.team190.gompeilib.subsystems.vision.io.GompeiVisionIOInputsAutoLogged) + invocation.getArgument(0); + inputs.timestamps = new double[] {1.0}; + inputs.frames = new double[][] {new double[] {}}; + return null; + }) + .when(io) + .updateInputs(any(CameraIO.GompeiVisionIOInputs.class)); + + camera.periodic(); + assertTrue(poses.isEmpty()); + + // --- Test Invalid Switch Case --- + doAnswer( + invocation -> { + var inputs = + (edu.wpi.team190.gompeilib.subsystems.vision.io.GompeiVisionIOInputsAutoLogged) + invocation.getArgument(0); + inputs.timestamps = new double[] {1.0}; + inputs.frames = new double[][] {new double[] {3.0}}; + return null; + }) + .when(io) + .updateInputs(any(CameraIO.GompeiVisionIOInputs.class)); + + camera.periodic(); + mockDS.verify(() -> DriverStation.reportWarning("FAILED TO CAPTURE FRAMES", false), times(1)); + + // --- Test Case 1: One Pose (Valid, Inside Field) --- + doAnswer( + invocation -> { + var inputs = + (edu.wpi.team190.gompeilib.subsystems.vision.io.GompeiVisionIOInputsAutoLogged) + invocation.getArgument(0); + inputs.timestamps = new double[] {1.0}; + inputs.frames = + new double[][] { + new double[] { + 1.0, + 0.0, // values[0]=1, values[1]=0 + 1.0, + 2.0, + 0.0, // translation (1, 2, 0) + 1.0, + 0.0, + 0.0, + 0.0, // quaternion (w=1, x=0, y=0, z=0) + 1.0, // tagId = 1 + 0.1, + 0.2, + 0.3, + 0.4, + 0.5, + 0.6, + 0.7, + 0.8, // corners + 2.0 // distance = 2.0 + } + }; + return null; + }) + .when(io) + .updateInputs(any(CameraIO.GompeiVisionIOInputs.class)); + + poses.clear(); + txtys.clear(); + camera.periodic(); + + assertFalse(poses.isEmpty()); + assertEquals(1, poses.size()); + assertEquals(1, txtys.size()); + assertEquals(1, poses.get(0).tagIds().size()); + assertTrue(poses.get(0).tagIds().contains(1)); + + // --- Test Case 1: Outside Field boundary --- + doAnswer( + invocation -> { + var inputs = + (edu.wpi.team190.gompeilib.subsystems.vision.io.GompeiVisionIOInputsAutoLogged) + invocation.getArgument(0); + inputs.timestamps = new double[] {1.0}; + inputs.frames = + new double[][] { + new double[] { + 1.0, 0.0, 20.0, 2.0, + 0.0, // X is 20, field is 16. With 0.5 margin, it's outside. + 1.0, 0.0, 0.0, 0.0, 1.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 2.0 + } + }; + return null; + }) + .when(io) + .updateInputs(any(CameraIO.GompeiVisionIOInputs.class)); + + poses.clear(); + camera.periodic(); + assertTrue(poses.isEmpty()); + + // --- Test Case 2: Multiple Poses --- + // We will provide two poses, error0 = 0.05, error1 = 0.5 (ambiguity resolution should favor + // pose 0) + doAnswer( + invocation -> { + var inputs = + (edu.wpi.team190.gompeilib.subsystems.vision.io.GompeiVisionIOInputsAutoLogged) + invocation.getArgument(0); + inputs.timestamps = new double[] {1.0}; + inputs.frames = + new double[][] { + new double[] { + 2.0, 0.05, // values[0]=2, error0=0.05 + 1.0, 2.0, 0.0, // pose0 translation + 1.0, 0.0, 0.0, 0.0, // pose0 quaternion (no rot) + 0.5, // error1=0.5 + 1.0, 5.0, 0.0, // pose1 translation + 1.0, 0.0, 0.0, 0.0, // pose1 quaternion + 1.0, // tagId=1 + 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 2.0 + } + }; + return null; + }) + .when(io) + .updateInputs(any(CameraIO.GompeiVisionIOInputs.class)); + + poses.clear(); + camera.periodic(); + assertFalse(poses.isEmpty()); + // Pose 0 is selected, robot X should be around 1.0 - camera translation + assertTrue(poses.get(0).pose().getX() < 2.0); + + // Test Case 2: Ambiguity favoring pose 1 + doAnswer( + invocation -> { + var inputs = + (edu.wpi.team190.gompeilib.subsystems.vision.io.GompeiVisionIOInputsAutoLogged) + invocation.getArgument(0); + inputs.timestamps = new double[] {1.0}; + inputs.frames = + new double[][] { + new double[] { + 2.0, 0.5, // values[0]=2, error0=0.5 + 1.0, 2.0, 0.0, // pose0 translation + 1.0, 0.0, 0.0, 0.0, // pose0 quaternion + 0.05, // error1=0.05 + 1.5, 2.0, 0.0, // pose1 translation + 1.0, 0.0, 0.0, 0.0, // pose1 quaternion + 1.0, // tagId=1 + 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 2.0 + } + }; + return null; + }) + .when(io) + .updateInputs(any(CameraIO.GompeiVisionIOInputs.class)); + + poses.clear(); + camera.periodic(); + assertFalse(poses.isEmpty()); + } + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/vision/camera/CameraLimelightTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/vision/camera/CameraLimelightTest.java new file mode 100644 index 00000000..4787a1a6 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/vision/camera/CameraLimelightTest.java @@ -0,0 +1,267 @@ +package edu.wpi.team190.gompeilib.subsystems.vision.camera; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Rotation3d; +import edu.wpi.first.math.geometry.Transform3d; +import edu.wpi.first.math.geometry.Translation3d; +import edu.wpi.first.math.kinematics.ChassisSpeeds; +import edu.wpi.first.wpilibj.DriverStation; +import edu.wpi.first.wpilibj.Timer; +import edu.wpi.team190.gompeilib.core.GompeiLib; +import edu.wpi.team190.gompeilib.core.utility.LimelightHelpers; +import edu.wpi.team190.gompeilib.subsystems.vision.VisionConstants; +import edu.wpi.team190.gompeilib.subsystems.vision.data.VisionPoseObservation; +import edu.wpi.team190.gompeilib.subsystems.vision.data.VisionSingleTxTyObservation; +import edu.wpi.team190.gompeilib.subsystems.vision.io.CameraIO; +import edu.wpi.team190.gompeilib.subsystems.vision.io.CameraIOLimelight; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; +import org.junit.jupiter.api.Test; +import org.littletonrobotics.junction.Logger; +import org.mockito.MockedStatic; + +public class CameraLimelightTest { + + @Test + public void testCameraStaticLimelight() { + CameraIOLimelight io = mock(CameraIOLimelight.class); + VisionConstants.StaticLimelightConfig config = + VisionConstants.StaticLimelightConfig.builder() + .key("static") + .cameraType(CameraType.LIMELIGHT_3) + .horizontalFOV(60.0) + .verticalFOV(40.0) + .megatagXYStdev(0.1) + .metatagThetaStdev(0.2) + .megatag2XYStdev(0.05) + .robotToCameraTransform( + new Transform3d(new Translation3d(0.5, 0.0, 0.5), new Rotation3d())) + .enableRewind(true) + .build(); + + List poseObs = new ArrayList<>(); + List singleObs = new ArrayList<>(); + + Consumer> poseObserver = poseObs::addAll; + Consumer> singleObserver = singleObs::addAll; + + try (MockedStatic mockHelpers = mockStatic(LimelightHelpers.class); + MockedStatic mockDS = mockStatic(DriverStation.class); + MockedStatic mockTimer = mockStatic(Timer.class); + MockedStatic mockLib = mockStatic(GompeiLib.class); + MockedStatic mockLogger = mockStatic(Logger.class)) { + + mockDS.when(DriverStation::isEnabled).thenReturn(false); + mockDS.when(DriverStation::isDisabled).thenReturn(true); + mockTimer.when(Timer::getFPGATimestamp).thenReturn(10.0); + mockLib.when(GompeiLib::isTuning).thenReturn(false); + + CameraStaticLimelight camera = + new CameraStaticLimelight( + io, + config, + () -> Rotation2d.fromDegrees(0), + () -> new ChassisSpeeds(), + () -> 1000L, + List.of(poseObserver), + List.of(singleObserver)); + + assertEquals("limelight-static", camera.getName()); + + // --- Setup input data --- + doAnswer( + invocation -> { + var inputs = + (edu.wpi.team190.gompeilib.subsystems.vision.io.LimelightIOInputsAutoLogged) + invocation.getArgument(0); + + // Setup MT1 Pose Estimate + CameraIO.RawFiducial fid = + new CameraIO.RawFiducial(1, 0.1, 0.2, 0.3, 2.0, 2.0, 0.05); + inputs.mt1PoseEstimate = + new CameraIO.PoseEstimate( + new Pose2d(1.0, 2.0, new Rotation2d()), + 10.0, + 5.0, + 1, + 1.0, + 2.0, + 0.5, + new CameraIO.RawFiducial[] {fid}, + false); + + // Setup MT2 Pose Estimate + inputs.mt2PoseEstimate = + new CameraIO.PoseEstimate( + new Pose2d(1.1, 2.1, new Rotation2d()), + 10.0, + 5.0, + 1, + 1.0, + 2.0, + 0.5, + new CameraIO.RawFiducial[] {fid}, + true); + + // Setup raw fiducials + inputs.rawFiducials = new CameraIO.RawFiducial[] {fid}; + + return null; + }) + .when(io) + .updateInputs(any(CameraIO.LimelightIOInputs.class)); + + // Periodic check while disabled + camera.periodic(); + assertFalse(poseObs.isEmpty()); + assertEquals(2, poseObs.size()); // MT1 & MT2 + assertEquals(1, singleObs.size()); + + // Transition to enabled + mockDS.when(DriverStation::isEnabled).thenReturn(true); + mockDS.when(DriverStation::isDisabled).thenReturn(false); + mockTimer.when(Timer::getFPGATimestamp).thenReturn(11.0); + + poseObs.clear(); + singleObs.clear(); + camera.periodic(); + + mockHelpers.verify(() -> LimelightHelpers.SetIMUMode("limelight-static", 0), times(1)); + mockHelpers.verify(() -> LimelightHelpers.SetThrottle("limelight-static", 0), times(1)); + + // Rerun enabled periodic with time advance to trigger rewind + mockTimer.when(Timer::getFPGATimestamp).thenReturn(180.0); // > 165 + camera.periodic(); + mockHelpers.verify( + () -> LimelightHelpers.triggerRewindCapture("limelight-static", 165), times(1)); + + // Transition to disabled while tuning + mockDS.when(DriverStation::isEnabled).thenReturn(false); + mockDS.when(DriverStation::isDisabled).thenReturn(true); + mockLib.when(GompeiLib::isTuning).thenReturn(true); + mockTimer.when(Timer::getFPGATimestamp).thenReturn(185.0); + + camera.periodic(); + mockHelpers.verify( + () -> LimelightHelpers.SetIMUMode("limelight-static", 1), + times(2)); // First in constructor/constructor states + } + } + + @Test + public void testCameraMovingLimelight() { + CameraIOLimelight io = mock(CameraIOLimelight.class); + VisionConstants.MovingLimelightConfig config = + VisionConstants.MovingLimelightConfig.builder() + .key("moving") + .cameraType(CameraType.LIMELIGHT_3G) + .horizontalFOV(80.0) + .verticalFOV(50.0) + .megatagXYStdev(0.1) + .metatagThetaStdev(0.2) + .megatag2XYStdev(0.05) + .robotToRotationAxisTransform( + new Transform3d(new Translation3d(0.5, 0.0, 0.5), new Rotation3d())) + .rotationAxisToLensTransform( + new Transform3d(new Translation3d(0.1, 0.0, 0.0), new Rotation3d())) + .enableRewind(true) + .build(); + + List poseObs = new ArrayList<>(); + List singleObs = new ArrayList<>(); + + Consumer> poseObserver = poseObs::addAll; + Consumer> singleObserver = singleObs::addAll; + + try (MockedStatic mockHelpers = mockStatic(LimelightHelpers.class); + MockedStatic mockDS = mockStatic(DriverStation.class); + MockedStatic mockTimer = mockStatic(Timer.class); + MockedStatic mockLogger = mockStatic(Logger.class)) { + + mockDS.when(DriverStation::isEnabled).thenReturn(false); + mockDS.when(DriverStation::isDisabled).thenReturn(true); + mockTimer.when(Timer::getFPGATimestamp).thenReturn(10.0); + + CameraMovingLimelight camera = + new CameraMovingLimelight( + io, + config, + () -> Rotation2d.fromDegrees(0), + () -> Rotation2d.fromDegrees(45), // rotated 45 degrees + () -> new ChassisSpeeds(), + () -> 1000L, + List.of(poseObserver), + List.of(singleObserver)); + + assertEquals("limelight-moving", camera.getName()); + + // --- Setup input data --- + doAnswer( + invocation -> { + var inputs = + (edu.wpi.team190.gompeilib.subsystems.vision.io.LimelightIOInputsAutoLogged) + invocation.getArgument(0); + CameraIO.RawFiducial fid = + new CameraIO.RawFiducial(1, 0.1, 0.2, 0.3, 2.0, 2.0, 0.05); + inputs.mt1PoseEstimate = + new CameraIO.PoseEstimate( + new Pose2d(1.0, 2.0, new Rotation2d()), + 10.0, + 5.0, + 1, + 1.0, + 2.0, + 0.5, + new CameraIO.RawFiducial[] {fid}, + false); + inputs.mt2PoseEstimate = + new CameraIO.PoseEstimate( + new Pose2d(1.1, 2.1, new Rotation2d()), + 10.0, + 5.0, + 1, + 1.0, + 2.0, + 0.5, + new CameraIO.RawFiducial[] {fid}, + true); + inputs.rawFiducials = new CameraIO.RawFiducial[] {fid}; + return null; + }) + .when(io) + .updateInputs(any(CameraIO.LimelightIOInputs.class)); + + camera.periodic(); + assertFalse(poseObs.isEmpty()); + assertEquals(2, poseObs.size()); + + // Transition to enabled + mockDS.when(DriverStation::isEnabled).thenReturn(true); + mockDS.when(DriverStation::isDisabled).thenReturn(false); + mockTimer.when(Timer::getFPGATimestamp).thenReturn(11.0); + + camera.periodic(); + mockHelpers.verify(() -> LimelightHelpers.SetIMUMode("limelight-moving", 0), times(1)); + mockHelpers.verify(() -> LimelightHelpers.SetThrottle("limelight-moving", 0), times(1)); + + // Advance time to trigger rewind capture + mockTimer.when(Timer::getFPGATimestamp).thenReturn(180.0); + camera.periodic(); + mockHelpers.verify( + () -> LimelightHelpers.triggerRewindCapture("limelight-moving", 165), times(1)); + + // Transition back to disabled + mockDS.when(DriverStation::isEnabled).thenReturn(false); + mockDS.when(DriverStation::isDisabled).thenReturn(true); + mockTimer.when(Timer::getFPGATimestamp).thenReturn(185.0); + + camera.periodic(); + mockHelpers.verify(() -> LimelightHelpers.SetIMUMode("limelight-moving", 1), times(2)); + } + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/vision/camera/CameraTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/vision/camera/CameraTest.java new file mode 100644 index 00000000..4735d5a9 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/vision/camera/CameraTest.java @@ -0,0 +1,61 @@ +package edu.wpi.team190.gompeilib.subsystems.vision.camera; + +import static org.junit.jupiter.api.Assertions.*; + +import edu.wpi.first.math.geometry.Pose3d; +import edu.wpi.team190.gompeilib.subsystems.vision.data.VisionMultiTxTyObservation; +import edu.wpi.team190.gompeilib.subsystems.vision.data.VisionPoseObservation; +import edu.wpi.team190.gompeilib.subsystems.vision.data.VisionSingleTxTyObservation; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; +import org.junit.jupiter.api.Test; + +public class CameraTest { + + private static class TestCamera extends Camera { + public TestCamera( + String name, + List>> poseObservers, + List>> multiTxTyObservers, + List>> singleTxTyObservers) { + super(name, poseObservers, multiTxTyObservers, singleTxTyObservers); + } + } + + @Test + public void testCameraBaseMethods() { + List> receivedPose = new ArrayList<>(); + List> receivedMulti = new ArrayList<>(); + List> receivedSingle = new ArrayList<>(); + + Consumer> poseObserver = receivedPose::add; + Consumer> multiObserver = receivedMulti::add; + Consumer> singleObserver = receivedSingle::add; + + TestCamera camera = + new TestCamera( + "TestCam", List.of(poseObserver), List.of(multiObserver), List.of(singleObserver)); + + assertEquals("TestCam", camera.getName()); + assertNotNull(camera.getPoseObservationList()); + assertNotNull(camera.getMultiTxTyObservationList()); + assertNotNull(camera.getSingleTxTyObservationList()); + + Pose3d pose = new Pose3d(); + camera.setCameraPose(pose); + assertEquals(pose, camera.getCurrentCameraPose()); + + // Call periodic (no-op in base class) + camera.periodic(); + + // Trigger observers when lists are empty + camera.sendObservers(); + assertEquals(1, receivedPose.size()); + assertEquals(1, receivedMulti.size()); + assertEquals(1, receivedSingle.size()); + assertTrue(receivedPose.get(0).isEmpty()); + assertTrue(receivedMulti.get(0).isEmpty()); + assertTrue(receivedSingle.get(0).isEmpty()); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/vision/camera/CameraTypeTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/vision/camera/CameraTypeTest.java new file mode 100644 index 00000000..ffdadcbe --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/vision/camera/CameraTypeTest.java @@ -0,0 +1,49 @@ +package edu.wpi.team190.gompeilib.subsystems.vision.camera; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +public class CameraTypeTest { + + @Test + public void testEnumValues() { + assertEquals(0.067, CameraType.BLINK_TIME); + + for (CameraType type : CameraType.values()) { + assertNotNull(type.name()); + assertNotNull(CameraType.valueOf(type.name())); + + // Basic assertions depending on type + switch (type) { + case LIMELIGHT_2_PLUS: + assertTrue(type.horizontalFOV > 0); + assertTrue(type.verticalFOV > 0); + break; + case LIMELIGHT_3: + assertTrue(type.horizontalFOV > 0); + assertTrue(type.verticalFOV > 0); + break; + case LIMELIGHT_3G: + assertTrue(type.horizontalFOV > 0); + assertTrue(type.verticalFOV > 0); + break; + case LIMELIGHT_4: + assertTrue(type.horizontalFOV > 0); + assertTrue(type.verticalFOV > 0); + break; + case THRIFTYCAM: + assertTrue(type.horizontalFOV > 0); + assertTrue(type.verticalFOV > 0); + break; + case DEFAULT: + assertEquals(0.0, type.horizontalFOV); + assertEquals(0.0, type.verticalFOV); + assertEquals(0.0, type.primaryXYStandardDeviationCoefficient); + assertEquals(0.0, type.secondaryXYStandardDeviationCoefficient); + assertEquals(0.0, type.primaryThetaStandardDeviationCoefficient); + break; + } + } + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/vision/data/VisionMultiTxTyObservationTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/vision/data/VisionMultiTxTyObservationTest.java new file mode 100644 index 00000000..c2a57510 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/vision/data/VisionMultiTxTyObservationTest.java @@ -0,0 +1,29 @@ +package edu.wpi.team190.gompeilib.subsystems.vision.data; + +import static org.junit.jupiter.api.Assertions.*; + +import edu.wpi.first.math.geometry.Pose3d; +import org.junit.jupiter.api.Test; + +public class VisionMultiTxTyObservationTest { + + @Test + public void testRecord() { + Pose3d cameraPose = new Pose3d(); + double[] tx = {1.0, 2.0}; + double[] ty = {3.0, 4.0}; + VisionMultiTxTyObservation obs = + new VisionMultiTxTyObservation(42, tx, ty, 5.5, 10.0, cameraPose); + + assertEquals(42, obs.tagId()); + assertArrayEquals(tx, obs.tx()); + assertArrayEquals(ty, obs.ty()); + assertEquals(5.5, obs.distance()); + assertEquals(10.0, obs.timestamp()); + assertEquals(cameraPose, obs.cameraPose()); + + assertNotNull(obs.toString()); + assertEquals(obs.hashCode(), obs.hashCode()); + assertEquals(obs, obs); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/vision/data/VisionPoseObservationTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/vision/data/VisionPoseObservationTest.java new file mode 100644 index 00000000..2dcb967f --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/vision/data/VisionPoseObservationTest.java @@ -0,0 +1,28 @@ +package edu.wpi.team190.gompeilib.subsystems.vision.data; + +import static org.junit.jupiter.api.Assertions.*; + +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.geometry.Pose2d; +import java.util.Set; +import org.junit.jupiter.api.Test; + +public class VisionPoseObservationTest { + + @Test + public void testRecord() { + Pose2d pose = new Pose2d(); + Set tagIds = Set.of(1, 2, 3); + var stddevs = VecBuilder.fill(0.1, 0.1, 0.2); + VisionPoseObservation obs = new VisionPoseObservation(pose, tagIds, 12.34, stddevs); + + assertEquals(pose, obs.pose()); + assertEquals(tagIds, obs.tagIds()); + assertEquals(12.34, obs.timestamp()); + assertEquals(stddevs, obs.stddevs()); + + assertNotNull(obs.toString()); + assertEquals(obs.hashCode(), obs.hashCode()); + assertEquals(obs, obs); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/vision/data/VisionSingleTxTyObservationTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/vision/data/VisionSingleTxTyObservationTest.java new file mode 100644 index 00000000..02cd8c11 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/vision/data/VisionSingleTxTyObservationTest.java @@ -0,0 +1,27 @@ +package edu.wpi.team190.gompeilib.subsystems.vision.data; + +import static org.junit.jupiter.api.Assertions.*; + +import edu.wpi.first.math.geometry.Pose3d; +import org.junit.jupiter.api.Test; + +public class VisionSingleTxTyObservationTest { + + @Test + public void testRecord() { + Pose3d cameraPose = new Pose3d(); + VisionSingleTxTyObservation obs = + new VisionSingleTxTyObservation(15, 1.1, 2.2, 3.3, 4.4, cameraPose); + + assertEquals(15, obs.tagId()); + assertEquals(1.1, obs.tx()); + assertEquals(2.2, obs.ty()); + assertEquals(3.3, obs.distance()); + assertEquals(4.4, obs.timestamp()); + assertEquals(cameraPose, obs.cameraPose()); + + assertNotNull(obs.toString()); + assertEquals(obs.hashCode(), obs.hashCode()); + assertEquals(obs, obs); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/vision/io/CameraIOGompeiVisionTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/vision/io/CameraIOGompeiVisionTest.java new file mode 100644 index 00000000..5b729166 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/vision/io/CameraIOGompeiVisionTest.java @@ -0,0 +1,96 @@ +package edu.wpi.team190.gompeilib.subsystems.vision.io; + +import static org.junit.jupiter.api.Assertions.*; + +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.geometry.Pose3d; +import edu.wpi.first.math.numbers.N1; +import edu.wpi.first.math.numbers.N3; +import edu.wpi.first.math.numbers.N5; +import edu.wpi.first.networktables.DoubleArrayPublisher; +import edu.wpi.first.networktables.IntegerPublisher; +import edu.wpi.first.networktables.NetworkTable; +import edu.wpi.first.networktables.NetworkTableInstance; +import edu.wpi.team190.gompeilib.subsystems.vision.VisionConstants; +import edu.wpi.team190.gompeilib.subsystems.vision.camera.CameraType; +import org.ejml.simple.SimpleMatrix; +import org.junit.jupiter.api.Test; + +public class CameraIOGompeiVisionTest { + + @Test + public void testGompeiVisionIO() { + // NetworkTable clean up / setup + NetworkTableInstance nt = NetworkTableInstance.getDefault(); + + Matrix cameraMatrix = + new Matrix<>( + new SimpleMatrix( + new double[][] { + {1.0, 0.0, 2.0}, + {0.0, 1.0, 3.0}, + {0.0, 0.0, 1.0} + })); + Matrix distortion = + new Matrix<>(new SimpleMatrix(new double[][] {{0.1}, {0.2}, {0.3}, {0.4}, {0.5}})); + + VisionConstants.GompeiVisionConfig config = + VisionConstants.GompeiVisionConfig.builder() + .key("test_gompei") + .hardwareID("HW001") + .cameraType(CameraType.THRIFTYCAM) + .exposure(100.0) + .gain(1.5) + .width(1280) + .height(720) + .cameraMatrix(cameraMatrix) + .distortionCoefficients(distortion) + .horizontalFOV(82.0) + .verticalFOV(46.0) + .singletagXYStdev(0.1) + .thetaStdev(0.2) + .multitagXYStdev(0.01) + .robotRelativePose(new Pose3d()) + .build(); + + CameraIOGompeiVision io = new CameraIOGompeiVision(config); + assertEquals("test_gompei", io.getName()); + + // Verify NT Config outputs + NetworkTable configTable = + nt.getTable("cameras").getSubTable("test_gompei").getSubTable("config"); + assertEquals("test_gompei", configTable.getStringTopic("role").subscribe("").get()); + assertEquals("test_gompei", configTable.getStringTopic("hardware_id").subscribe("").get()); + assertEquals(100.0, configTable.getDoubleTopic("exposure").subscribe(0.0).get()); + assertEquals(1.5, configTable.getDoubleTopic("gain").subscribe(0.0).get()); + assertEquals(1280, configTable.getIntegerTopic("width").subscribe(0).get()); + assertEquals(720, configTable.getIntegerTopic("height").subscribe(0).get()); + + // Setup input topics to publish observations and fps + NetworkTable outputTable = + nt.getTable("cameras").getSubTable("test_gompei").getSubTable("output"); + DoubleArrayPublisher obsPub = outputTable.getDoubleArrayTopic("observations").publish(); + IntegerPublisher captureFPSPub = outputTable.getIntegerTopic("capture_fps").publish(); + IntegerPublisher processingFPSPub = outputTable.getIntegerTopic("processing_fps").publish(); + + obsPub.set(new double[] {1.0, 2.0, 3.0}); + captureFPSPub.set(60); + processingFPSPub.set(55); + + // Let the subscribers receive it + nt.flush(); + try { + Thread.sleep(50); + } catch (InterruptedException e) { + // ignore + } + + CameraIO.GompeiVisionIOInputs inputs = new CameraIO.GompeiVisionIOInputs(); + io.updateInputs(inputs); + + assertEquals(60.0, inputs.captureFPS); + assertEquals(55.0, inputs.processingFPS); + assertTrue(inputs.frames.length >= 1); + assertArrayEquals(new double[] {1.0, 2.0, 3.0}, inputs.frames[inputs.frames.length - 1]); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/vision/io/CameraIOLimelightTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/vision/io/CameraIOLimelightTest.java new file mode 100644 index 00000000..94197df0 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/vision/io/CameraIOLimelightTest.java @@ -0,0 +1,93 @@ +package edu.wpi.team190.gompeilib.subsystems.vision.io; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Transform3d; +import edu.wpi.team190.gompeilib.core.utility.LimelightHelpers; +import edu.wpi.team190.gompeilib.subsystems.vision.VisionConstants; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; + +public class CameraIOLimelightTest { + + @Test + public void testCameraIOLimelightStaticConfig() { + VisionConstants.StaticLimelightConfig config = + VisionConstants.StaticLimelightConfig.builder() + .key("static_ll") + .robotToCameraTransform(new Transform3d()) + .build(); + + CameraIOLimelight io = new CameraIOLimelight(config); + assertEquals("limelight-static_ll", io.getName()); + + try (MockedStatic mockHelpers = mockStatic(LimelightHelpers.class)) { + LimelightHelpers.PoseEstimate mockEst1 = + new LimelightHelpers.PoseEstimate( + new Pose2d(1.0, 2.0, new edu.wpi.first.math.geometry.Rotation2d()), + 1.0, + 2.0, + 1, + 0.0, + 0.0, + 0.0, + new LimelightHelpers.RawFiducial[] { + new LimelightHelpers.RawFiducial(5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0) + }, + false); + + LimelightHelpers.PoseEstimate mockEst2 = + new LimelightHelpers.PoseEstimate( + new Pose2d(3.0, 4.0, new edu.wpi.first.math.geometry.Rotation2d()), + 3.0, + 4.0, + 2, + 0.0, + 0.0, + 0.0, + new LimelightHelpers.RawFiducial[] { + new LimelightHelpers.RawFiducial(6, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0) + }, + true); + + LimelightHelpers.RawFiducial[] mockFids = + new LimelightHelpers.RawFiducial[] { + new LimelightHelpers.RawFiducial(7, 1.0, 2.0, 3.0, 4.0, 5.0, 0.6) + }; + + mockHelpers + .when(() -> LimelightHelpers.getBotPoseEstimate_wpiBlue("limelight-static_ll")) + .thenReturn(mockEst1); + mockHelpers + .when(() -> LimelightHelpers.getBotPoseEstimate_wpiBlue_MegaTag2("limelight-static_ll")) + .thenReturn(mockEst2); + mockHelpers + .when(() -> LimelightHelpers.getRawFiducials("limelight-static_ll")) + .thenReturn(mockFids); + + CameraIO.LimelightIOInputs inputs = new CameraIO.LimelightIOInputs(); + io.updateInputs(inputs); + + assertEquals(1.0, inputs.mt1PoseEstimate.pose().getX()); + assertEquals(3.0, inputs.mt2PoseEstimate.pose().getX()); + assertEquals(1, inputs.rawFiducials.length); + assertEquals(7, inputs.rawFiducials[0].id()); + assertEquals(0.6, inputs.rawFiducials[0].ambiguity()); + } + } + + @Test + public void testCameraIOLimelightMovingConfig() { + VisionConstants.MovingLimelightConfig config = + VisionConstants.MovingLimelightConfig.builder() + .key("moving_ll") + .robotToRotationAxisTransform(new Transform3d()) + .rotationAxisToLensTransform(new Transform3d()) + .build(); + + CameraIOLimelight io = new CameraIOLimelight(config); + assertEquals("limelight-moving_ll", io.getName()); + } +} diff --git a/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/vision/io/CameraIOTest.java b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/vision/io/CameraIOTest.java new file mode 100644 index 00000000..8f8ad451 --- /dev/null +++ b/lib/src/test/java/edu/wpi/team190/gompeilib/subsystems/vision/io/CameraIOTest.java @@ -0,0 +1,159 @@ +package edu.wpi.team190.gompeilib.subsystems.vision.io; + +import static org.junit.jupiter.api.Assertions.*; + +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.team190.gompeilib.core.utility.LimelightHelpers; +import org.junit.jupiter.api.Test; + +public class CameraIOTest { + + private static class DummyCameraIO implements CameraIO {} + + @Test + public void testDefaultIO() { + DummyCameraIO io = new DummyCameraIO(); + assertEquals("", io.getName()); + + CameraIO.GompeiVisionIOInputs gompeiInputs = new CameraIO.GompeiVisionIOInputs(); + CameraIO.LimelightIOInputs limelightInputs = new CameraIO.LimelightIOInputs(); + + // Default no-ops + io.updateInputs(gompeiInputs); + io.updateInputs(limelightInputs); + + assertNotNull(gompeiInputs); + assertNotNull(limelightInputs); + } + + @Test + public void testPoseEstimateRecord() { + // Default constructor + CameraIO.PoseEstimate defaultEst = new CameraIO.PoseEstimate(); + assertEquals(Pose2d.kZero, defaultEst.pose()); + assertEquals(0.0, defaultEst.timestampSeconds()); + assertEquals(0.0, defaultEst.latency()); + assertEquals(0, defaultEst.tagCount()); + assertEquals(0.0, defaultEst.tagSpan()); + assertEquals(0.0, defaultEst.avgTagDist()); + assertEquals(0.0, defaultEst.avgTagArea()); + assertEquals(0, defaultEst.rawFiducials().length); + assertFalse(defaultEst.isMegaTag2()); + + // Custom constructor + Pose2d pose = new Pose2d(); + CameraIO.RawFiducial[] fids = new CameraIO.RawFiducial[] {new CameraIO.RawFiducial()}; + CameraIO.PoseEstimate customEst = + new CameraIO.PoseEstimate(pose, 1.23, 4.56, 1, 2.0, 3.0, 4.0, fids, true); + + assertEquals(pose, customEst.pose()); + assertEquals(1.23, customEst.timestampSeconds()); + assertEquals(4.56, customEst.latency()); + assertEquals(1, customEst.tagCount()); + assertEquals(2.0, customEst.tagSpan()); + assertEquals(3.0, customEst.avgTagDist()); + assertEquals(4.0, customEst.avgTagArea()); + assertSame(fids, customEst.rawFiducials()); + assertTrue(customEst.isMegaTag2()); + + // LimelightHelpers.PoseEstimate constructor + LimelightHelpers.RawFiducial rawFid = + new LimelightHelpers.RawFiducial(1, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6); + LimelightHelpers.PoseEstimate llEst = + new LimelightHelpers.PoseEstimate( + pose, + 10.0, + 20.0, + 1, + 30.0, + 40.0, + 50.0, + new LimelightHelpers.RawFiducial[] {rawFid}, + true); + CameraIO.PoseEstimate llConverted = new CameraIO.PoseEstimate(llEst); + + assertEquals(pose, llConverted.pose()); + assertEquals(10.0, llConverted.timestampSeconds()); + assertEquals(20.0, llConverted.latency()); + assertEquals(1, llConverted.tagCount()); + assertEquals(30.0, llConverted.tagSpan()); + assertEquals(40.0, llConverted.avgTagDist()); + assertEquals(50.0, llConverted.avgTagArea()); + assertEquals(1, llConverted.rawFiducials().length); + assertEquals(1, llConverted.rawFiducials()[0].id()); + assertTrue(llConverted.isMegaTag2()); + + // Record properties + assertNotNull(customEst.toString()); + assertEquals(customEst.hashCode(), customEst.hashCode()); + assertEquals(customEst, customEst); + assertNotEquals(customEst, defaultEst); + } + + @Test + public void testRawFiducialRecord() { + CameraIO.RawFiducial defaultFid = new CameraIO.RawFiducial(); + assertEquals(0, defaultFid.id()); + assertEquals(0.0, defaultFid.txnc()); + assertEquals(0.0, defaultFid.tync()); + assertEquals(0.0, defaultFid.ta()); + assertEquals(0.0, defaultFid.distToCamera()); + assertEquals(0.0, defaultFid.distToRobot()); + assertEquals(0.0, defaultFid.ambiguity()); + + CameraIO.RawFiducial customFid = new CameraIO.RawFiducial(2, 1.0, 2.0, 3.0, 4.0, 5.0, 0.5); + assertEquals(2, customFid.id()); + assertEquals(1.0, customFid.txnc()); + assertEquals(2.0, customFid.tync()); + assertEquals(3.0, customFid.ta()); + assertEquals(4.0, customFid.distToCamera()); + assertEquals(5.0, customFid.distToRobot()); + assertEquals(0.5, customFid.ambiguity()); + + // LimelightHelpers constructor + LimelightHelpers.RawFiducial llFid = + new LimelightHelpers.RawFiducial(3, 1.1, 2.2, 3.3, 4.4, 5.5, 0.6); + CameraIO.RawFiducial converted = new CameraIO.RawFiducial(llFid); + assertEquals(3, converted.id()); + assertEquals(1.1, converted.txnc()); + assertEquals(2.2, converted.tync()); + assertEquals(3.3, converted.ta()); + assertEquals(4.4, converted.distToCamera()); + assertEquals(5.5, converted.distToRobot()); + assertEquals(0.6, converted.ambiguity()); + + assertNotNull(customFid.toString()); + assertEquals(customFid.hashCode(), customFid.hashCode()); + assertEquals(customFid, customFid); + } + + @Test + public void testRawDetectionRecord() { + CameraIO.RawDetection defaultDet = new CameraIO.RawDetection(); + assertEquals(0, defaultDet.classId()); + assertEquals(0.0, defaultDet.txnc()); + assertEquals(0.0, defaultDet.tync()); + assertEquals(0.0, defaultDet.ta()); + assertEquals(0.0, defaultDet.corner0_X()); + assertEquals(0.0, defaultDet.corner0_Y()); + + CameraIO.RawDetection customDet = + new CameraIO.RawDetection(1, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0); + assertEquals(1, customDet.classId()); + assertEquals(2.0, customDet.txnc()); + assertEquals(3.0, customDet.tync()); + assertEquals(4.0, customDet.ta()); + assertEquals(5.0, customDet.corner0_X()); + assertEquals(6.0, customDet.corner0_Y()); + assertEquals(7.0, customDet.corner1_X()); + assertEquals(8.0, customDet.corner1_Y()); + assertEquals(9.0, customDet.corner2_X()); + assertEquals(10.0, customDet.corner2_Y()); + assertEquals(11.0, customDet.corner3_X()); + assertEquals(12.0, customDet.corner3_Y()); + + assertNotNull(customDet.toString()); + assertEquals(customDet.hashCode(), customDet.hashCode()); + assertEquals(customDet, customDet); + } +} diff --git a/settings.gradle b/settings.gradle index 8ea4b5d1..ab3a5474 100644 --- a/settings.gradle +++ b/settings.gradle @@ -28,3 +28,16 @@ pluginManagement { Properties props = System.getProperties() props.setProperty("org.gradle.internal.native.headers.unresolved.dependencies.ignore", "true") + +rootProject.name = '2k26-Robot-Code' + +include(":gompeilib") +project(":gompeilib").projectDir = file("lib") + +dependencyResolutionManagement { + versionCatalogs { + libs { + from(files("lib/gradle/libs.versions.toml")) + } + } +} diff --git a/src/main/deploy/pathplanner/navgrid.json b/src/main/deploy/pathplanner/navgrid.json index 8d4c8f6d..660ca524 100644 --- a/src/main/deploy/pathplanner/navgrid.json +++ b/src/main/deploy/pathplanner/navgrid.json @@ -72,21 +72,21 @@ true, true, true, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, true, true, true, @@ -133,16 +133,16 @@ false, false, false, - true, - true, - true, - true, - true, - true, - true, - true, - true, - true, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, false, false, false, @@ -191,9 +191,7 @@ false, false, false, - true, - true, - true, + false, true, true, true, @@ -215,16 +213,18 @@ false, false, false, + false, + false, + false, + false, true, true, true, true, true, true, - true, - true, - true, - true, + false, + false, false, false, false, @@ -249,9 +249,7 @@ false, false, false, - true, - true, - true, + false, true, true, true, @@ -273,16 +271,18 @@ false, false, false, + false, + false, + false, + false, true, true, true, true, true, true, - true, - true, - true, - true, + false, + false, false, false, false, @@ -307,9 +307,7 @@ false, false, false, - true, - true, - true, + false, true, true, true, @@ -331,16 +329,18 @@ false, false, false, + false, + false, + false, + false, true, true, true, true, true, true, - true, - true, - true, - true, + false, + false, false, false, false, @@ -365,16 +365,6 @@ false, false, false, - true, - true, - true, - true, - true, - true, - true, - true, - true, - true, false, false, false, @@ -389,16 +379,26 @@ false, false, false, - true, - true, - true, - true, - true, - true, - true, - true, - true, - true, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, false, false, false, @@ -423,16 +423,6 @@ false, false, false, - true, - true, - true, - true, - true, - true, - true, - true, - true, - true, false, false, false, @@ -447,16 +437,26 @@ false, false, false, - true, - true, - true, - true, - true, - true, - true, - true, - true, - true, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, false, false, false, @@ -540,13 +540,13 @@ false, false, false, - false, true, true, true, true, true, - false, + true, + true, false, false, false, @@ -1004,13 +1004,13 @@ false, false, false, - false, true, true, true, true, true, - false, + true, + true, false, false, false, @@ -1119,16 +1119,6 @@ false, false, false, - true, - true, - true, - true, - true, - true, - true, - true, - true, - true, false, false, false, @@ -1143,16 +1133,26 @@ false, false, false, - true, - true, - true, - true, - true, - true, - true, - true, - true, - true, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, false, false, false, @@ -1177,16 +1177,6 @@ false, false, false, - true, - true, - true, - true, - true, - true, - true, - true, - true, - true, false, false, false, @@ -1201,16 +1191,26 @@ false, false, false, - true, - true, - true, - true, - true, - true, - true, - true, - true, - true, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, false, false, false, @@ -1235,9 +1235,7 @@ false, false, false, - true, - true, - true, + false, true, true, true, @@ -1259,16 +1257,18 @@ false, false, false, + false, + false, + false, + false, true, true, true, true, true, true, - true, - true, - true, - true, + false, + false, false, false, false, @@ -1293,9 +1293,7 @@ false, false, false, - true, - true, - true, + false, true, true, true, @@ -1317,10 +1315,10 @@ false, false, false, - true, - true, - true, - true, + false, + false, + false, + false, true, true, true, @@ -1336,6 +1334,8 @@ false, false, false, + false, + false, true, true ], @@ -1351,9 +1351,7 @@ false, false, false, - true, - true, - true, + false, true, true, true, @@ -1375,10 +1373,10 @@ false, false, false, - true, - true, - true, - true, + false, + false, + false, + false, true, true, true, @@ -1394,6 +1392,8 @@ false, false, false, + false, + false, true, true ], @@ -1409,16 +1409,16 @@ false, false, false, - true, - true, - true, - true, - true, - true, - true, - true, - true, - true, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, false, false, false, @@ -1464,21 +1464,21 @@ true, true, true, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, true, true, true, diff --git a/src/main/deploy/pathplanner/paths/FOLLOW_DEPOT_2_BC.path b/src/main/deploy/pathplanner/paths/FOLLOW_DEPOT_2_BC.path new file mode 100644 index 00000000..d7a67b62 --- /dev/null +++ b/src/main/deploy/pathplanner/paths/FOLLOW_DEPOT_2_BC.path @@ -0,0 +1,234 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 5.741, + "y": 4.014 + }, + "prevControl": null, + "nextControl": { + "x": 6.238830473187077, + "y": 4.973976993171868 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 4.634236804564908, + "y": 5.425905848787448 + }, + "prevControl": { + "x": 6.549158345221112, + "y": 5.128316690442225 + }, + "nextControl": { + "x": 2.647230264601464, + "y": 5.734697405673661 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 2.2651763364534876, + "y": 6.095182283773909 + }, + "prevControl": { + "x": 2.4778561442169, + "y": 5.963777187336525 + }, + "nextControl": { + "x": 2.0450051662718063, + "y": 6.2312159493799975 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 0.609597809387995, + "y": 5.940848692267805 + }, + "prevControl": { + "x": 0.44761850931532643, + "y": 6.131276385495362 + }, + "nextControl": { + "x": 1.608940966711986, + "y": 4.765991089434756 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 1.508, + "y": 5.051 + }, + "prevControl": { + "x": 1.2080593177017225, + "y": 5.427218399527798 + }, + "nextControl": { + "x": 1.6638459355059665, + "y": 4.855520987350892 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 3.3144935805991436, + "y": 5.425905848787448 + }, + "prevControl": { + "x": 2.701375663873267, + "y": 5.44970500464505 + }, + "nextControl": { + "x": 4.652828219932671, + "y": 5.373956242027926 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 6.186875891583452, + "y": 5.940848692267805 + }, + "prevControl": { + "x": 6.204462829373812, + "y": 6.190229324272688 + }, + "nextControl": { + "x": 6.057489300998572, + "y": 4.106162624821684 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 8.20530670470756, + "y": 4.014 + }, + "prevControl": { + "x": 5.8014694227199985, + "y": 4.20134293063752 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [ + { + "waypointRelativePos": 0.5, + "rotationDegrees": 0.0 + }, + { + "waypointRelativePos": 1, + "rotationDegrees": 0.0 + }, + { + "waypointRelativePos": 2.0, + "rotationDegrees": 180.0 + }, + { + "waypointRelativePos": 2.925373134328341, + "rotationDegrees": 180.0 + }, + { + "waypointRelativePos": 5, + "rotationDegrees": 0.0 + }, + { + "waypointRelativePos": 6.164179104477555, + "rotationDegrees": -90.0 + } + ], + "constraintZones": [ + { + "name": "SOTM", + "minWaypointRelativePos": 1.5123027408637877, + "maxWaypointRelativePos": 5.0, + "constraints": { + "maxVelocity": 0.75, + "maxAcceleration": 0.75, + "maxAngularVelocity": 45.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + } + } + ], + "pointTowardsZones": [], + "eventMarkers": [ + { + "name": "HOLD", + "waypointRelativePos": 0.0, + "endWaypointRelativePos": 1.2777453271028036, + "command": { + "type": "named", + "data": { + "name": "HOLD" + } + } + }, + { + "name": "STOP_OVERRIDE_ROLLER", + "waypointRelativePos": 0.7540360873694213, + "endWaypointRelativePos": null, + "command": { + "type": "named", + "data": { + "name": "STOP_OVERRIDE_ROLLER" + } + } + }, + { + "name": "SCORE_OR_FEED", + "waypointRelativePos": 1.5, + "endWaypointRelativePos": null, + "command": { + "type": "named", + "data": { + "name": "SCORE_OR_FEED" + } + } + }, + { + "name": "HOLD_WHILE_INTAKE", + "waypointRelativePos": 5.1, + "endWaypointRelativePos": null, + "command": { + "type": "named", + "data": { + "name": "HOLD_WHILE_INTAKE" + } + } + } + ], + "globalConstraints": { + "maxVelocity": 3.0, + "maxAcceleration": 3.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0.0, + "rotation": -90.0 + }, + "reversed": false, + "folder": null, + "idealStartingState": { + "velocity": 0, + "rotation": -92.61887292041673 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/src/main/deploy/pathplanner/paths/FOLLOW_FEED_1.path b/src/main/deploy/pathplanner/paths/FOLLOW_FEED_1.path index 14d5749c..b2e4feee 100644 --- a/src/main/deploy/pathplanner/paths/FOLLOW_FEED_1.path +++ b/src/main/deploy/pathplanner/paths/FOLLOW_FEED_1.path @@ -82,6 +82,17 @@ "name": "DEPLOY" } } + }, + { + "name": "HOLD_WHILE_INTAKE", + "waypointRelativePos": 1.33, + "endWaypointRelativePos": 2.0, + "command": { + "type": "named", + "data": { + "name": "HOLD_WHILE_INTAKE" + } + } } ], "globalConstraints": { diff --git a/src/main/deploy/pathplanner/paths/OP_1.path b/src/main/deploy/pathplanner/paths/OP_1.path index d82a4381..c345374b 100644 --- a/src/main/deploy/pathplanner/paths/OP_1.path +++ b/src/main/deploy/pathplanner/paths/OP_1.path @@ -103,7 +103,11 @@ }, { "waypointRelativePos": 3, - "rotationDegrees": -134.7292666483435 + "rotationDegrees": -146.96683469685627 + }, + { + "waypointRelativePos": 3.6767317939609763, + "rotationDegrees": -160.84732777732415 }, { "waypointRelativePos": 4, @@ -113,12 +117,12 @@ "constraintZones": [ { "name": "Intaking", - "minWaypointRelativePos": 1.7742940199335553, + "minWaypointRelativePos": 1.721034870641137, "maxWaypointRelativePos": 3.4874636627906996, "constraints": { - "maxVelocity": 1.75, + "maxVelocity": 1.25, "maxAcceleration": 3.0, - "maxAngularVelocity": 540.0, + "maxAngularVelocity": 200.0, "maxAngularAcceleration": 720.0, "nominalVoltage": 12.0, "unlimited": false diff --git a/src/main/deploy/pathplanner/paths/OP_2.path b/src/main/deploy/pathplanner/paths/OP_2.path index 3dc28bb5..dd56905c 100644 --- a/src/main/deploy/pathplanner/paths/OP_2.path +++ b/src/main/deploy/pathplanner/paths/OP_2.path @@ -158,7 +158,7 @@ "minWaypointRelativePos": 2.675538169319829, "maxWaypointRelativePos": 3.69644310781476, "constraints": { - "maxVelocity": 2.0, + "maxVelocity": 1.25, "maxAcceleration": 3.0, "maxAngularVelocity": 540.0, "maxAngularAcceleration": 720.0, diff --git a/src/main/deploy/pathplanner/paths/OP_2_BC.path b/src/main/deploy/pathplanner/paths/OP_2_BC.path new file mode 100644 index 00000000..4cee2d69 --- /dev/null +++ b/src/main/deploy/pathplanner/paths/OP_2_BC.path @@ -0,0 +1,255 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 3.307, + "y": 5.662 + }, + "prevControl": null, + "nextControl": { + "x": 3.356595030207022, + "y": 7.726789712266736 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 3.30675055354465, + "y": 7.488156626506025 + }, + "prevControl": { + "x": 3.0371608520750364, + "y": 7.518618417819235 + }, + "nextControl": { + "x": 4.183349467908152, + "y": 7.389106948238781 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 5.975180722891566, + "y": 7.488156626506025 + }, + "prevControl": { + "x": 4.473012739996169, + "y": 7.633652180331236 + }, + "nextControl": { + "x": 7.360682555101732, + "y": 7.353961011435923 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 7.76, + "y": 6.887132530120482 + }, + "prevControl": { + "x": 7.628209658675491, + "y": 7.0995738300919555 + }, + "nextControl": { + "x": 8.121888234831424, + "y": 6.303781684532584 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 8.044056939501779, + "y": 4.772004744958482 + }, + "prevControl": { + "x": 8.49351318324111, + "y": 5.0342658768033735 + }, + "nextControl": { + "x": 7.721660380929114, + "y": 4.583883904427447 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 6.656120996441281, + "y": 5.662 + }, + "prevControl": { + "x": 6.7529537366548045, + "y": 4.621376037959667 + }, + "nextControl": { + "x": 6.417579720004241, + "y": 8.225510726317378 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 8.25937109375, + "y": 7.488156626506025 + }, + "prevControl": { + "x": 8.49345338814226, + "y": 7.400375766108927 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [ + { + "waypointRelativePos": 1, + "rotationDegrees": 0.0 + }, + { + "waypointRelativePos": 1.6633319805194788, + "rotationDegrees": 0.0 + }, + { + "waypointRelativePos": 3.0, + "rotationDegrees": -90.0 + }, + { + "waypointRelativePos": 4.6311300639659, + "rotationDegrees": 158.17376786022444 + }, + { + "waypointRelativePos": 5.091684434968002, + "rotationDegrees": 81.474549544931 + }, + { + "waypointRelativePos": 5.39872068230272, + "rotationDegrees": -83.0655823795409 + } + ], + "constraintZones": [ + { + "name": "SOTM", + "minWaypointRelativePos": 0.0, + "maxWaypointRelativePos": 1.2174974020190024, + "constraints": { + "maxVelocity": 1.25, + "maxAcceleration": 1.5, + "maxAngularVelocity": 36.5, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + } + }, + { + "name": "Intake", + "minWaypointRelativePos": 2.9097222222222214, + "maxWaypointRelativePos": 4.779622395833334, + "constraints": { + "maxVelocity": 1.25, + "maxAcceleration": 1.5, + "maxAngularVelocity": 40.0, + "maxAngularAcceleration": 500.0, + "nominalVoltage": 12.0, + "unlimited": false + } + }, + { + "name": "Acceleration", + "minWaypointRelativePos": 0.7537375415282406, + "maxWaypointRelativePos": 2.483018663194445, + "constraints": { + "maxVelocity": 5.0, + "maxAcceleration": 5.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + } + } + ], + "pointTowardsZones": [], + "eventMarkers": [ + { + "name": "SCORE_AGITATE_OP_1", + "waypointRelativePos": 0, + "endWaypointRelativePos": 0.35, + "command": { + "type": "named", + "data": { + "name": "SCORE_AGITATE_OP_1" + } + } + }, + { + "name": "SCORE_NO_ROLLER", + "waypointRelativePos": 0.36, + "endWaypointRelativePos": 1.0707277823920274, + "command": { + "type": "named", + "data": { + "name": "SCORE_NO_ROLLER" + } + } + }, + { + "name": "DEPLOY", + "waypointRelativePos": 0.5044123754152823, + "endWaypointRelativePos": null, + "command": { + "type": "named", + "data": { + "name": "DEPLOY" + } + } + }, + { + "name": "HOLD", + "waypointRelativePos": 1.0889482973421927, + "endWaypointRelativePos": 5.7811980897009985, + "command": { + "type": "named", + "data": { + "name": "HOLD" + } + } + }, + { + "name": "STOP_ROLLER", + "waypointRelativePos": 5.294292803970212, + "endWaypointRelativePos": null, + "command": { + "type": "named", + "data": { + "name": "STOP_ROLLER" + } + } + } + ], + "globalConstraints": { + "maxVelocity": 3.0, + "maxAcceleration": 3.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0.0, + "rotation": -90.0 + }, + "reversed": false, + "folder": null, + "idealStartingState": { + "velocity": 0, + "rotation": 180.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/src/main/deploy/pathplanner/paths/V1_DOT.path b/src/main/deploy/pathplanner/paths/V1_DOT.path new file mode 100644 index 00000000..9db8a8e0 --- /dev/null +++ b/src/main/deploy/pathplanner/paths/V1_DOT.path @@ -0,0 +1,68 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 3.5325734615325928, + "y": 7.601134300231934 + }, + "prevControl": null, + "nextControl": { + "x": 4.4484308834075925, + "y": 7.596410341898601 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 8.266128978587965, + "y": 7.445107421875 + }, + "prevControl": { + "x": 7.266128978587965, + "y": 7.445107421875 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [ + { + "name": "Constraints Zone", + "minWaypointRelativePos": 0.4, + "maxWaypointRelativePos": 1.0, + "constraints": { + "maxVelocity": 2.0, + "maxAcceleration": 2.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + } + } + ], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 3.0, + "maxAcceleration": 3.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0.0, + "rotation": -90.0 + }, + "reversed": false, + "folder": null, + "idealStartingState": { + "velocity": 0.0, + "rotation": -90.10285170233786 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/src/main/deploy/pathplanner/paths/V1_RETURN.path b/src/main/deploy/pathplanner/paths/V1_RETURN.path new file mode 100644 index 00000000..51986f92 --- /dev/null +++ b/src/main/deploy/pathplanner/paths/V1_RETURN.path @@ -0,0 +1,89 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 3.0215272903442383, + "y": 7.140939712524414 + }, + "prevControl": null, + "nextControl": { + "x": 2.3888659940330506, + "y": 7.569362403173079 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 5.332924393723252, + "y": 7.405520684736091 + }, + "prevControl": { + "x": 3.1721683309557767, + "y": 7.496091298145506 + }, + "nextControl": { + "x": 5.707816314937319, + "y": 7.389806652110352 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 8.153552068473608, + "y": 7.405520684736091 + }, + "prevControl": { + "x": 7.1535520684736085, + "y": 7.405520684736091 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [ + { + "waypointRelativePos": 1, + "rotationDegrees": 0.0 + } + ], + "constraintZones": [ + { + "name": "Constraints Zone", + "minWaypointRelativePos": 0.8480756245779844, + "maxWaypointRelativePos": 2.0, + "constraints": { + "maxVelocity": 2.0, + "maxAcceleration": 2.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + } + } + ], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 3.0, + "maxAcceleration": 3.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0.0, + "rotation": -90.0 + }, + "reversed": false, + "folder": null, + "idealStartingState": { + "velocity": 0.0, + "rotation": 20.638 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/src/main/deploy/pathplanner/paths/V1_SIMPLE.path b/src/main/deploy/pathplanner/paths/V1_SIMPLE.path new file mode 100644 index 00000000..90710707 --- /dev/null +++ b/src/main/deploy/pathplanner/paths/V1_SIMPLE.path @@ -0,0 +1,170 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 4.441253185272217, + "y": 7.693458557128906 + }, + "prevControl": null, + "nextControl": { + "x": 5.441253185272217, + "y": 7.693458557128906 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 7.7952070236206055, + "y": 7.227133750915527 + }, + "prevControl": { + "x": 7.797472945601852, + "y": 7.715225983796296 + }, + "nextControl": { + "x": 7.791758781797629, + "y": 6.484363229768175 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 7.7952070236206055, + "y": 7.8909309895833335 + }, + "prevControl": { + "x": 7.805455009430399, + "y": 8.140720858847166 + }, + "nextControl": { + "x": 7.784959037810812, + "y": 7.641141120319501 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 7.7952070236206055, + "y": 4.726469401041666 + }, + "prevControl": { + "x": 7.773075344679452, + "y": 5.687756827360394 + }, + "nextControl": { + "x": 7.808626736111112, + "y": 4.143585431134259 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 5.503650665283203, + "y": 7.689876556396484 + }, + "prevControl": { + "x": 6.944603464036501, + "y": 7.689876556396484 + }, + "nextControl": { + "x": 4.062697866529906, + "y": 7.689876556396484 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 3.0215272903442383, + "y": 7.140939712524414 + }, + "prevControl": { + "x": 2.202993016234584, + "y": 7.487948617936867 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [ + { + "waypointRelativePos": 1.0, + "rotationDegrees": -90.0 + }, + { + "waypointRelativePos": 2, + "rotationDegrees": -90.0 + }, + { + "waypointRelativePos": 2.3523334703947336, + "rotationDegrees": -107.464 + }, + { + "waypointRelativePos": 3.0, + "rotationDegrees": -107.464 + }, + { + "waypointRelativePos": 4.0, + "rotationDegrees": -90.0 + }, + { + "waypointRelativePos": 4.493503289473685, + "rotationDegrees": -90.0 + } + ], + "constraintZones": [ + { + "name": "Constraints Zone", + "minWaypointRelativePos": 0.6330460963455152, + "maxWaypointRelativePos": 2.3057905938538212, + "constraints": { + "maxVelocity": 1.5, + "maxAcceleration": 2.0, + "maxAngularVelocity": 100.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + } + }, + { + "name": "Constraints Zone", + "minWaypointRelativePos": 2.26140728820598, + "maxWaypointRelativePos": 3.2593438538206003, + "constraints": { + "maxVelocity": 0.55, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + } + } + ], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 3.0, + "maxAcceleration": 3.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0, + "rotation": 20.638 + }, + "reversed": false, + "folder": null, + "idealStartingState": { + "velocity": 0.0, + "rotation": -90.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/src/main/deploy/v1_doomspiral/choreo/CLIMB.traj b/src/main/deploy/v1_doomspiral/choreo/CLIMB.traj index b76bfcc4..b96e5ec5 100644 --- a/src/main/deploy/v1_doomspiral/choreo/CLIMB.traj +++ b/src/main/deploy/v1_doomspiral/choreo/CLIMB.traj @@ -129,12 +129,12 @@ {"t":3.40624, "x":1.88819, "y":3.73294, "heading":1.5708, "vx":-0.4956, "vy":-0.0654, "omega":0.0, "ax":0.0, "ay":0.00001, "alpha":0.0, "fx":[-0.00002,-0.00002,-0.00002,-0.00002], "fy":[0.00011,0.00011,0.00011,0.00011]}, {"t":3.4556, "x":1.86373, "y":3.72971, "heading":1.5708, "vx":-0.4956, "vy":-0.0654, "omega":0.0, "ax":0.0, "ay":0.00001, "alpha":0.0, "fx":[-0.00002,-0.00002,-0.00002,-0.00002], "fy":[0.00012,0.00012,0.00012,0.00012]}, {"t":3.50497, "x":1.83926, "y":3.72648, "heading":1.5708, "vx":-0.4956, "vy":-0.0654, "omega":0.0, "ax":0.0, "ay":0.00001, "alpha":0.0, "fx":[-0.00002,-0.00002,-0.00002,-0.00002], "fy":[0.00012,0.00012,0.00012,0.00012]}, - {"t":3.55433, "x":1.81479, "y":3.72326, "heading":1.5708, "vx":-0.4956, "vy":-0.0654, "omega":0.0, "ax":0.0, "ay":0.00001, "alpha":0.0, "fx":[-0.00002,-0.00002,-0.00002,-0.00002], "fy":[0.00016,0.00016,0.00016,0.00016]}, - {"t":3.6037, "x":1.79033, "y":3.72003, "heading":1.5708, "vx":-0.4956, "vy":-0.0654, "omega":0.0, "ax":-0.00019, "ay":0.00142, "alpha":0.0, "fx":[-0.00318,-0.00318,-0.00318,-0.00318], "fy":[0.0241,0.0241,0.0241,0.0241]}, + {"t":3.55433, "x":1.81479, "y":3.72326, "heading":1.5708, "vx":-0.4956, "vy":-0.0654, "omega":0.0, "ax":0.0, "ay":0.00001, "alpha":0.0, "fx":[-0.00002,-0.00002,-0.00002,-0.00002], "fy":[0.00017,0.00017,0.00017,0.00017]}, + {"t":3.6037, "x":1.79033, "y":3.72003, "heading":1.5708, "vx":-0.4956, "vy":-0.0654, "omega":0.0, "ax":-0.00019, "ay":0.00142, "alpha":0.0, "fx":[-0.00318,-0.00318,-0.00318,-0.00318], "fy":[0.02409,0.02409,0.02409,0.02409]}, {"t":3.65307, "x":1.76586, "y":3.7168, "heading":1.5708, "vx":-0.49561, "vy":-0.06533, "omega":0.0, "ax":-0.0687, "ay":0.75367, "alpha":0.0, "fx":[-1.1686,-1.1686,-1.1686,-1.1686], "fy":[12.8197,12.8197,12.8197,12.8197]}, {"t":3.70243, "x":1.74131, "y":3.71449, "heading":1.5708, "vx":-0.499, "vy":-0.02812, "omega":0.0, "ax":-0.01647, "ay":0.77708, "alpha":0.0, "fx":[-0.28022,-0.28022,-0.28022,-0.28022], "fy":[13.21797,13.21797,13.21797,13.21797]}, - {"t":3.75104, "x":1.71704, "y":3.71405, "heading":1.5708, "vx":-0.4998, "vy":0.00965, "omega":0.0, "ax":0.00003, "ay":0.00151, "alpha":0.0, "fx":[0.0005,0.0005,0.0005,0.0005], "fy":[0.02569,0.02569,0.02569,0.02569]}, - {"t":3.79965, "x":1.69274, "y":3.71452, "heading":1.5708, "vx":-0.4998, "vy":0.00973, "omega":0.0, "ax":0.0, "ay":-0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[-0.00018,-0.00018,-0.00018,-0.00018]}, + {"t":3.75104, "x":1.71704, "y":3.71405, "heading":1.5708, "vx":-0.4998, "vy":0.00965, "omega":0.0, "ax":0.00003, "ay":0.00151, "alpha":0.0, "fx":[0.0005,0.0005,0.0005,0.0005], "fy":[0.02568,0.02568,0.02568,0.02568]}, + {"t":3.79965, "x":1.69274, "y":3.71452, "heading":1.5708, "vx":-0.4998, "vy":0.00973, "omega":0.0, "ax":0.0, "ay":-0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[-0.00017,-0.00017,-0.00017,-0.00017]}, {"t":3.84826, "x":1.66845, "y":3.71499, "heading":1.5708, "vx":-0.4998, "vy":0.00973, "omega":0.0, "ax":0.0, "ay":-0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[-0.00022,-0.00022,-0.00022,-0.00022]}, {"t":3.89686, "x":1.64416, "y":3.71546, "heading":1.5708, "vx":-0.4998, "vy":0.00972, "omega":0.0, "ax":0.0, "ay":-0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[-0.00023,-0.00023,-0.00023,-0.00023]}, {"t":3.94547, "x":1.61986, "y":3.71594, "heading":1.5708, "vx":-0.4998, "vy":0.00972, "omega":0.0, "ax":0.0, "ay":-0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[-0.00023,-0.00023,-0.00023,-0.00023]}, @@ -175,17 +175,17 @@ {"t":5.64674, "x":0.76956, "y":3.73246, "heading":1.5708, "vx":-0.4998, "vy":0.0097, "omega":0.0, "ax":0.0, "ay":-0.00002, "alpha":0.0, "fx":[-0.00001,-0.00001,-0.00001,-0.00001], "fy":[-0.00032,-0.00032,-0.00032,-0.00032]}, {"t":5.69535, "x":0.74527, "y":3.73293, "heading":1.5708, "vx":-0.4998, "vy":0.0097, "omega":0.0, "ax":0.0, "ay":-0.00002, "alpha":0.0, "fx":[-0.00001,-0.00001,-0.00001,-0.00001], "fy":[-0.00033,-0.00033,-0.00033,-0.00033]}, {"t":5.74396, "x":0.72097, "y":3.7334, "heading":1.5708, "vx":-0.4998, "vy":0.0097, "omega":0.0, "ax":0.0, "ay":-0.00002, "alpha":0.0, "fx":[-0.00001,-0.00001,-0.00001,-0.00001], "fy":[-0.00034,-0.00034,-0.00034,-0.00034]}, - {"t":5.79257, "x":0.69668, "y":3.73387, "heading":1.5708, "vx":-0.4998, "vy":0.0097, "omega":0.0, "ax":0.0, "ay":-0.00002, "alpha":0.0, "fx":[-0.00001,-0.00001,-0.00001,-0.00001], "fy":[-0.00035,-0.00035,-0.00035,-0.00035]}, + {"t":5.79257, "x":0.69668, "y":3.73387, "heading":1.5708, "vx":-0.4998, "vy":0.0097, "omega":0.0, "ax":0.0, "ay":-0.00002, "alpha":0.0, "fx":[-0.00001,-0.00001,-0.00001,-0.00001], "fy":[-0.00036,-0.00036,-0.00036,-0.00036]}, {"t":5.84117, "x":0.67238, "y":3.73434, "heading":1.5708, "vx":-0.4998, "vy":0.00969, "omega":0.0, "ax":0.0, "ay":-0.00002, "alpha":0.0, "fx":[-0.00001,-0.00001,-0.00001,-0.00001], "fy":[-0.00031,-0.00031,-0.00031,-0.00031]}, {"t":5.88978, "x":0.64809, "y":3.73481, "heading":1.5708, "vx":-0.4998, "vy":0.00969, "omega":0.0, "ax":0.00004, "ay":0.00205, "alpha":0.0, "fx":[0.00068,0.00068,0.00068,0.00068], "fy":[0.0348,0.0348,0.0348,0.0348]}, - {"t":5.93839, "x":0.62379, "y":3.73529, "heading":1.5708, "vx":-0.4998, "vy":0.00979, "omega":0.0, "ax":0.07681, "ay":1.04907, "alpha":0.0, "fx":[1.30655,1.30655,1.30655,1.30655], "fy":[17.84441,17.84441,17.84441,17.84441]}, - {"t":5.987, "x":0.59959, "y":3.737, "heading":1.5708, "vx":-0.49607, "vy":0.06079, "omega":0.0, "ax":0.2477, "ay":1.36645, "alpha":0.0, "fx":[4.21326,4.21326,4.21326,4.21326], "fy":[23.24298,23.24298,23.24298,23.24298]}, - {"t":6.02954, "x":0.57871, "y":3.74083, "heading":1.5708, "vx":-0.48553, "vy":0.11892, "omega":0.0, "ax":0.00092, "ay":0.00377, "alpha":0.0, "fx":[0.01569,0.01569,0.01569,0.01569], "fy":[0.06405,0.06405,0.06405,0.06405]}, - {"t":6.07208, "x":0.55806, "y":3.74589, "heading":1.5708, "vx":-0.48549, "vy":0.11908, "omega":0.0, "ax":-0.00008, "ay":-0.00032, "alpha":0.0, "fx":[-0.00134,-0.00134,-0.00134,-0.00134], "fy":[-0.00543,-0.00543,-0.00543,-0.00543]}, - {"t":6.11462, "x":0.5374, "y":3.75095, "heading":1.5708, "vx":-0.48549, "vy":0.11906, "omega":0.0, "ax":-0.0001, "ay":-0.0004, "alpha":0.0, "fx":[-0.00167,-0.00167,-0.00167,-0.00167], "fy":[-0.00677,-0.00677,-0.00677,-0.00677]}, + {"t":5.93839, "x":0.62379, "y":3.73529, "heading":1.5708, "vx":-0.4998, "vy":0.00979, "omega":0.0, "ax":0.07681, "ay":1.04907, "alpha":0.0, "fx":[1.30654,1.30654,1.30654,1.30654], "fy":[17.84439,17.84439,17.84439,17.84439]}, + {"t":5.987, "x":0.59959, "y":3.737, "heading":1.5708, "vx":-0.49607, "vy":0.06079, "omega":0.0, "ax":0.2477, "ay":1.36646, "alpha":0.0, "fx":[4.21327,4.21327,4.21327,4.21327], "fy":[23.24304,23.24304,23.24304,23.24304]}, + {"t":6.02954, "x":0.57871, "y":3.74083, "heading":1.5708, "vx":-0.48553, "vy":0.11892, "omega":0.0, "ax":0.00092, "ay":0.00376, "alpha":0.0, "fx":[0.01568,0.01568,0.01568,0.01568], "fy":[0.06398,0.06398,0.06398,0.06398]}, + {"t":6.07208, "x":0.55806, "y":3.74589, "heading":1.5708, "vx":-0.48549, "vy":0.11908, "omega":0.0, "ax":-0.00008, "ay":-0.00032, "alpha":0.0, "fx":[-0.00133,-0.00133,-0.00133,-0.00133], "fy":[-0.00539,-0.00539,-0.00539,-0.00539]}, + {"t":6.11462, "x":0.5374, "y":3.75095, "heading":1.5708, "vx":-0.48549, "vy":0.11906, "omega":0.0, "ax":-0.0001, "ay":-0.0004, "alpha":0.0, "fx":[-0.00167,-0.00167,-0.00167,-0.00167], "fy":[-0.00678,-0.00678,-0.00678,-0.00678]}, {"t":6.15716, "x":0.51675, "y":3.75602, "heading":1.5708, "vx":-0.4855, "vy":0.11905, "omega":0.0, "ax":-0.00013, "ay":-0.00052, "alpha":0.0, "fx":[-0.00217,-0.00217,-0.00217,-0.00217], "fy":[-0.00883,-0.00883,-0.00883,-0.00883]}, - {"t":6.19971, "x":0.4961, "y":3.76108, "heading":1.5708, "vx":-0.4855, "vy":0.11903, "omega":0.0, "ax":-0.00019, "ay":-0.00078, "alpha":0.0, "fx":[-0.00321,-0.00321,-0.00321,-0.00321], "fy":[-0.01318,-0.01318,-0.01318,-0.01318]}, - {"t":6.24225, "x":0.47544, "y":3.76615, "heading":1.5708, "vx":-0.48551, "vy":0.11899, "omega":0.0, "ax":2.25697, "ay":-0.55453, "alpha":0.0, "fx":[38.39047,38.39047,38.39047,38.39047], "fy":[-9.43238,-9.43238,-9.43238,-9.43238]}, + {"t":6.19971, "x":0.4961, "y":3.76108, "heading":1.5708, "vx":-0.4855, "vy":0.11903, "omega":0.0, "ax":-0.00019, "ay":-0.00078, "alpha":0.0, "fx":[-0.00322,-0.00322,-0.00322,-0.00322], "fy":[-0.01319,-0.01319,-0.01319,-0.01319]}, + {"t":6.24225, "x":0.47544, "y":3.76615, "heading":1.5708, "vx":-0.48551, "vy":0.11899, "omega":0.0, "ax":2.25697, "ay":-0.55453, "alpha":0.0, "fx":[38.39047,38.39047,38.39047,38.39047], "fy":[-9.43237,-9.43237,-9.43237,-9.43237]}, {"t":6.28479, "x":0.45683, "y":3.77071, "heading":1.5708, "vx":-0.3895, "vy":0.0954, "omega":0.0, "ax":9.15559, "ay":-2.24254, "alpha":0.0, "fx":[155.73391,155.73391,155.73391,155.73391], "fy":[-38.14488,-38.14488,-38.14488,-38.14488]}, {"t":6.32733, "x":0.44854, "y":3.77273, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0,0.0,0.0,0.0]}], "splits":[0] diff --git a/src/main/deploy/v1_doomspiral/choreo/DEPOT_AND_BACK_HUB_PATH_2.traj b/src/main/deploy/v1_doomspiral/choreo/DEPOT_AND_BACK_HUB_PATH_2.traj index c02f1d23..e7310bd6 100644 --- a/src/main/deploy/v1_doomspiral/choreo/DEPOT_AND_BACK_HUB_PATH_2.traj +++ b/src/main/deploy/v1_doomspiral/choreo/DEPOT_AND_BACK_HUB_PATH_2.traj @@ -63,109 +63,109 @@ "differentialTrackWidth":0.5588 }, "sampleType":"Swerve", - "waypoints":[0.0,1.19022,2.09047,2.72425,3.65124], + "waypoints":[0.0,1.19299,2.09343,2.72722,3.6542], "samples":[ - {"t":0.0, "x":2.43494, "y":5.23076, "heading":0.95518, "vx":0.0, "vy":0.0, "omega":0.0, "ax":-2.4657, "ay":7.18827, "alpha":-17.3117, "fx":[86.01167,-1.70094,-130.4228,-121.65164], "fy":[135.36228,160.39682,93.23291,100.08987]}, - {"t":0.03401, "x":2.43351, "y":5.23491, "heading":0.95518, "vx":-0.08385, "vy":0.24445, "omega":-0.5887, "ax":-2.01609, "ay":7.67416, "alpha":-15.20511, "fx":[84.15224,-1.81605,-127.97092,-91.53783], "fy":[136.49973,160.37737,96.5199,128.74399]}, - {"t":0.06801, "x":2.4295, "y":5.24766, "heading":0.93516, "vx":-0.15241, "vy":0.50541, "omega":-1.10577, "ax":-1.84998, "ay":7.8275, "alpha":-14.66455, "fx":[83.27547,-0.45026,-124.30923,-84.38643], "fy":[137.00027,160.36302,101.13465,134.0763]}, - {"t":0.10202, "x":2.42324, "y":5.26938, "heading":0.89755, "vx":-0.21532, "vy":0.7716, "omega":-1.60446, "ax":-1.75795, "ay":7.9123, "alpha":-14.53057, "fx":[82.86236,2.07991,-119.28605,-85.26526], "fy":[137.19888,160.31691,106.94411,133.88378]}, - {"t":0.13602, "x":2.4149, "y":5.30019, "heading":0.84299, "vx":-0.2751, "vy":1.04066, "omega":-2.09859, "ax":-1.61839, "ay":8.02239, "alpha":-14.35564, "fx":[82.16798,5.50781,-112.38443,-85.40501], "fy":[137.53738,160.1863,114.08521,134.02523]}, - {"t":0.17003, "x":2.40461, "y":5.34022, "heading":0.77163, "vx":-0.33014, "vy":1.31348, "omega":-2.58677, "ax":-1.37634, "ay":8.21929, "alpha":-13.72681, "fx":[79.55357,9.33182,-102.3666,-80.16372], "fy":[138.93619,159.9273,123.02368,137.34381]}, - {"t":0.20404, "x":2.39259, "y":5.38964, "heading":0.68366, "vx":-0.37694, "vy":1.59298, "omega":-3.05356, "ax":-1.00533, "ay":8.57228, "alpha":-11.87801, "fx":[70.26815,12.10331,-85.78797,-64.985], "fy":[143.61418,159.5805,134.8897,145.16361]}, - {"t":0.23804, "x":2.37919, "y":5.44877, "heading":0.57982, "vx":-0.41113, "vy":1.88449, "omega":-3.45749, "ax":-0.56778, "ay":9.14896, "alpha":-6.29877, "fx":[34.86562,7.15325,-48.58893,-32.06081], "fy":[155.51268,159.42576,151.82412,155.72197]}, - {"t":0.27205, "x":2.36488, "y":5.51814, "heading":0.46225, "vx":-0.43044, "vy":2.19561, "omega":-3.67169, "ax":-0.98539, "ay":8.19935, "alpha":14.86706, "fx":[-80.77587,-90.25265,79.27169,24.71183], "fy":[136.3995,127.6457,136.87483,156.95464]}, - {"t":0.30606, "x":2.34967, "y":5.59755, "heading":0.33739, "vx":-0.46395, "vy":2.47444, "omega":-3.16611, "ax":-0.01651, "ay":7.82444, "alpha":17.34003, "fx":[-69.03602,-89.34572,107.63651,49.6217], "fy":[141.86752,125.38991,114.75872,150.34961]}, - {"t":0.34006, "x":2.33389, "y":5.68622, "heading":0.22972, "vx":-0.46451, "vy":2.74052, "omega":-2.57645, "ax":1.53012, "ay":7.13542, "alpha":18.9184, "fx":[-46.8142,-69.37841,137.3488,82.95174], "fy":[148.98562,128.10081,74.60646,133.79259]}, - {"t":0.37407, "x":2.31898, "y":5.78354, "heading":0.1421, "vx":-0.41247, "vy":2.98317, "omega":-1.9331, "ax":4.52644, "ay":5.2126, "alpha":16.78166, "fx":[-12.56658,53.79496,152.2243,114.5214], "fy":[151.72431,69.3748,27.9738,105.58658]}, - {"t":0.40807, "x":2.30757, "y":5.888, "heading":0.07637, "vx":-0.25855, "vy":3.16043, "omega":-1.36242, "ax":3.11612, "ay":5.11668, "alpha":19.13258, "fx":[-15.35611,-21.57139,141.96388,106.98072], "fy":[140.88026,75.15567,29.4223,102.67476]}, - {"t":0.44208, "x":2.30058, "y":5.99843, "heading":0.03003, "vx":-0.15258, "vy":3.33443, "omega":-0.7118, "ax":1.18668, "ay":3.47851, "alpha":14.09033, "fx":[-26.62995,-34.11018,79.13191,62.3485], "fy":[90.01806,33.96582,30.12852,82.56153]}, - {"t":0.47609, "x":2.29607, "y":6.11383, "heading":0.00583, "vx":-0.11222, "vy":3.45272, "omega":-0.23264, "ax":0.11969, "ay":0.46781, "alpha":1.97553, "fx":[-4.60893,-4.5818,8.71734,8.61728], "fy":[12.39529,3.45853,3.52884,12.44631]}, - {"t":0.51009, "x":2.29233, "y":6.23152, "heading":-0.00208, "vx":-0.10815, "vy":3.46863, "omega":-0.16546, "ax":0.08255, "ay":0.03914, "alpha":0.17876, "fx":[0.80572,0.80417,2.00257,2.00404], "fy":[1.0723,0.26183,0.25945,1.06979]}, - {"t":0.5441, "x":2.2887, "y":6.34949, "heading":-0.00771, "vx":-0.10535, "vy":3.46996, "omega":-0.15938, "ax":0.33547, "ay":-0.0625, "alpha":-0.2859, "fx":[6.65814,6.66967,4.75437,4.74312], "fy":[-1.71932,-0.42179,-0.40626,-1.70506]}, - {"t":0.57811, "x":2.28531, "y":6.46746, "heading":-0.01313, "vx":-0.09394, "vy":3.46783, "omega":-0.1691, "ax":1.4022, "ay":-0.65897, "alpha":-2.88972, "fx":[33.16584,33.69298,14.44703,14.09851], "fy":[-17.72876,-4.54823,-4.4501,-18.10815]}, - {"t":0.61211, "x":2.28293, "y":6.585, "heading":-0.01888, "vx":-0.04625, "vy":3.44543, "omega":-0.26737, "ax":3.03888, "ay":-2.86354, "alpha":-11.94405, "fx":[84.78497,97.55156,14.84237,9.58322], "fy":[-67.44661,-17.16076,-24.24139,-85.98307]}, - {"t":0.64612, "x":2.28311, "y":6.70051, "heading":-0.02797, "vx":0.05709, "vy":3.34805, "omega":-0.67354, "ax":2.94219, "ay":-4.6546, "alpha":-17.90327, "fx":[100.85971,129.90659,-19.52415,-11.05858], "fy":[-95.77764,-28.03522,-63.66425,-129.21623]}, - {"t":0.68012, "x":2.28675, "y":6.81168, "heading":-0.05088, "vx":0.15714, "vy":3.18976, "omega":-1.28236, "ax":3.70236, "ay":-5.30271, "alpha":-18.18877, "fx":[114.03077,147.35517,-2.51955,-6.96189], "fy":[-100.05302,-24.85602,-89.49023,-146.39077]}, - {"t":0.71413, "x":2.29424, "y":6.91708, "heading":-0.09448, "vx":0.28304, "vy":3.00944, "omega":-1.90089, "ax":4.8939, "ay":-5.29263, "alpha":-15.6568, "fx":[119.0117,153.33411,63.51154,-2.88195], "fy":[-100.95232,-23.39229,-82.96191,-152.79801]}, - {"t":0.74814, "x":2.30669, "y":7.01636, "heading":-0.15913, "vx":0.44946, "vy":2.82946, "omega":-2.43332, "ax":5.36786, "ay":-5.56086, "alpha":-13.73338, "fx":[115.50752,153.51924,100.21237,-4.01592], "fy":[-107.22919,-33.0035,-82.81761,-155.30445]}, - {"t":0.78214, "x":2.32508, "y":7.10937, "heading":-0.24187, "vx":0.63201, "vy":2.64035, "omega":-2.90034, "ax":5.0897, "ay":-6.16754, "alpha":-12.73277, "fx":[106.7644,150.16943,100.29448,-10.93092], "fy":[-116.70697,-48.18391,-98.752,-155.98945]}, - {"t":0.81615, "x":2.34951, "y":7.19559, "heading":-0.3405, "vx":0.80509, "vy":2.43062, "omega":-3.33334, "ax":5.27281, "ay":-6.47872, "alpha":-10.86916, "fx":[99.84325,144.39157,115.19713,-0.6757], "fy":[-123.06391,-64.46507,-96.45008,-156.82534]}, - {"t":0.85015, "x":2.37994, "y":7.2745, "heading":-0.45386, "vx":0.98439, "vy":2.2103, "omega":-3.70295, "ax":5.89074, "ay":-6.50783, "alpha":-7.94341, "fx":[99.09052,138.1352,127.81642,35.75709], "fy":[-123.91057,-77.652,-88.18476,-153.03776]}, - {"t":0.88416, "x":2.41682, "y":7.3459, "heading":-0.57978, "vx":1.18472, "vy":1.98899, "omega":-3.97308, "ax":6.73677, "ay":-6.26013, "alpha":-3.5752, "fx":[106.92825,131.26674,129.60256,90.56469], "fy":[-117.38118,-89.23637,-89.92095,-129.39346]}, - {"t":0.91817, "x":2.46101, "y":7.40992, "heading":-0.71489, "vx":1.41381, "vy":1.77611, "omega":-4.09466, "ax":7.06773, "ay":-6.10471, "alpha":0.72925, "fx":[123.69066,116.64778,117.06638,123.47529], "fy":[-99.69306,-107.84845,-107.61691,-100.19909]}, - {"t":0.95217, "x":2.51317, "y":7.46679, "heading":-0.85413, "vx":1.65415, "vy":1.56851, "omega":-4.06986, "ax":6.98986, "ay":-5.94321, "alpha":4.70425, "fx":[144.94348,97.73427,101.07492,131.82963], "fy":[-65.50801,-125.30858,-123.52534,-90.02738]}, - {"t":0.98618, "x":2.57346, "y":7.51669, "heading":-0.99253, "vx":1.89185, "vy":1.36641, "omega":-3.90988, "ax":6.76266, "ay":-5.69248, "alpha":8.28342, "fx":[156.97187,84.82544,86.61444,131.71173], "fy":[-27.78885,-134.36558,-134.40912,-90.74627]}, - {"t":1.02019, "x":2.64171, "y":7.55986, "heading":-1.12549, "vx":2.12183, "vy":1.17283, "omega":-3.6282, "ax":6.43629, "ay":-5.60468, "alpha":10.63421, "fx":[159.69101,78.77857,71.73776,127.71062], "fy":[-3.79545,-137.86648,-143.09191,-96.5819]}, - {"t":1.05419, "x":2.71759, "y":7.59651, "heading":-1.24888, "vx":2.3407, "vy":0.98223, "omega":-3.26657, "ax":6.00539, "ay":-5.74568, "alpha":12.03595, "fx":[159.8559,73.24345,54.85731,120.64329], "fy":[5.59956,-140.60119,-150.48985,-105.43781]}, - {"t":1.0882, "x":2.80066, "y":7.62659, "heading":-1.35996, "vx":2.54492, "vy":0.78685, "omega":-2.85727, "ax":3.4546, "ay":-5.95258, "alpha":19.36895, "fx":[159.23495,-59.4264,26.71252,108.5257], "fy":[16.51274,-145.51386,-158.0256,-117.98011]}, - {"t":1.1222, "x":2.8892, "y":7.6499, "heading":-1.45712, "vx":2.6624, "vy":0.58442, "omega":-2.19861, "ax":1.45117, "ay":-3.82414, "alpha":31.00989, "fx":[158.62346,-158.43315,2.24997,96.2954], "fy":[22.30926,6.03263,-160.31145,-128.22077]}, - {"t":1.15621, "x":2.98057, "y":7.66757, "heading":-1.53189, "vx":2.71175, "vy":0.45438, "omega":-1.14408, "ax":1.23123, "ay":-3.06146, "alpha":33.6433, "fx":[158.53845,-146.39907,-14.84024,86.47224], "fy":[23.36308,63.09504,-159.6774,-135.07926]}, - {"t":1.19022, "x":3.0735, "y":7.68125, "heading":-1.5708, "vx":2.75361, "vy":0.35027, "omega":0.0, "ax":4.57664, "ay":-8.22923, "alpha":0.0, "fx":[77.84736,77.84736,77.84736,77.84736], "fy":[-139.97677,-139.97677,-139.97677,-139.97677]}, - {"t":1.22623, "x":3.17563, "y":7.68852, "heading":-1.5708, "vx":2.91842, "vy":0.05393, "omega":0.0, "ax":6.77954, "ay":-6.49335, "alpha":0.0, "fx":[115.31805,115.31805,115.31805,115.31805], "fy":[-110.45005,-110.45005,-110.45005,-110.45005]}, - {"t":1.26224, "x":3.28511, "y":7.68626, "heading":-1.5708, "vx":3.16255, "vy":-0.17989, "omega":0.0, "ax":9.25858, "ay":-0.26251, "alpha":0.0, "fx":[157.48581,157.48581,157.48581,157.48581], "fy":[-4.46527,-4.46527,-4.46527,-4.46527]}, - {"t":1.29825, "x":3.405, "y":7.67961, "heading":-1.5708, "vx":3.49595, "vy":-0.18935, "omega":0.0, "ax":0.24374, "ay":1.54344, "alpha":0.0, "fx":[4.14593,4.14593,4.14593,4.14593], "fy":[26.2534,26.2534,26.2534,26.2534]}, - {"t":1.33426, "x":3.53105, "y":7.67379, "heading":-1.5708, "vx":3.50473, "vy":-0.13377, "omega":0.0, "ax":0.01908, "ay":0.53785, "alpha":0.0, "fx":[0.32453,0.32453,0.32453,0.32453], "fy":[9.14872,9.14872,9.14872,9.14872]}, - {"t":1.37027, "x":3.65727, "y":7.66932, "heading":-1.5708, "vx":3.50542, "vy":-0.1144, "omega":0.0, "ax":0.00868, "ay":0.27862, "alpha":0.0, "fx":[0.14772,0.14772,0.14772,0.14772], "fy":[4.73921,4.73921,4.73921,4.73921]}, - {"t":1.40628, "x":3.7835, "y":7.66538, "heading":-1.5708, "vx":3.50573, "vy":-0.10437, "omega":0.0, "ax":0.00526, "ay":0.18272, "alpha":0.0, "fx":[0.08947,0.08947,0.08947,0.08947], "fy":[3.10807,3.10807,3.10807,3.10807]}, - {"t":1.44229, "x":3.90975, "y":7.66174, "heading":-1.5708, "vx":3.50592, "vy":-0.09779, "omega":0.0, "ax":0.00354, "ay":0.1302, "alpha":0.0, "fx":[0.06017,0.06017,0.06017,0.06017], "fy":[2.21468,2.21468,2.21468,2.21468]}, - {"t":1.4783, "x":4.036, "y":7.65831, "heading":-1.5708, "vx":3.50605, "vy":-0.0931, "omega":0.0, "ax":0.00248, "ay":0.09532, "alpha":0.0, "fx":[0.04214,0.04214,0.04214,0.04214], "fy":[1.62134,1.62134,1.62134,1.62134]}, - {"t":1.51431, "x":4.16225, "y":7.65502, "heading":-1.5708, "vx":3.50614, "vy":-0.08967, "omega":0.0, "ax":0.00178, "ay":0.07086, "alpha":0.0, "fx":[0.03028,0.03028,0.03028,0.03028], "fy":[1.20525,1.20525,1.20525,1.20525]}, - {"t":1.55032, "x":4.28851, "y":7.65183, "heading":-1.5708, "vx":3.5062, "vy":-0.08711, "omega":0.0, "ax":0.00131, "ay":0.05376, "alpha":0.0, "fx":[0.02237,0.02237,0.02237,0.02237], "fy":[0.91442,0.91442,0.91442,0.91442]}, - {"t":1.58633, "x":4.41477, "y":7.64873, "heading":-1.5708, "vx":3.50625, "vy":-0.08518, "omega":0.0, "ax":0.00101, "ay":0.04226, "alpha":0.0, "fx":[0.01721,0.01721,0.01721,0.01721], "fy":[0.71883,0.71883,0.71883,0.71883]}, - {"t":1.62234, "x":4.54103, "y":7.64569, "heading":-1.5708, "vx":3.50629, "vy":-0.08366, "omega":0.0, "ax":0.00083, "ay":0.03522, "alpha":0.0, "fx":[0.0141,0.0141,0.0141,0.0141], "fy":[0.59915,0.59915,0.59915,0.59915]}, - {"t":1.65835, "x":4.6673, "y":7.6427, "heading":-1.5708, "vx":3.50632, "vy":-0.08239, "omega":0.0, "ax":0.00074, "ay":0.03198, "alpha":0.0, "fx":[0.0126,0.0126,0.0126,0.0126], "fy":[0.54393,0.54393,0.54393,0.54393]}, - {"t":1.69436, "x":4.79356, "y":7.63976, "heading":-1.5708, "vx":3.50634, "vy":-0.08124, "omega":0.0, "ax":0.00074, "ay":0.0324, "alpha":0.0, "fx":[0.01259,0.01259,0.01259,0.01259], "fy":[0.55117,0.55117,0.55117,0.55117]}, - {"t":1.73037, "x":4.91982, "y":7.63685, "heading":-1.5708, "vx":3.50637, "vy":-0.08007, "omega":0.0, "ax":0.00085, "ay":0.03791, "alpha":0.0, "fx":[0.01452,0.01452,0.01452,0.01452], "fy":[0.64481,0.64481,0.64481,0.64481]}, - {"t":1.76638, "x":5.04609, "y":7.63399, "heading":-1.5708, "vx":3.5064, "vy":-0.0787, "omega":0.0, "ax":0.00126, "ay":0.05729, "alpha":0.0, "fx":[0.02151,0.02151,0.02151,0.02151], "fy":[0.97451,0.97451,0.97451,0.97451]}, - {"t":1.80239, "x":5.17235, "y":7.6312, "heading":-1.5708, "vx":3.50644, "vy":-0.07664, "omega":0.0, "ax":0.00297, "ay":0.14105, "alpha":0.0, "fx":[0.0506,0.0506,0.0506,0.0506], "fy":[2.39928,2.39928,2.39928,2.39928]}, - {"t":1.8384, "x":5.29862, "y":7.62853, "heading":-1.5708, "vx":3.50655, "vy":-0.07156, "omega":0.0, "ax":0.00473, "ay":0.58001, "alpha":0.0, "fx":[0.08054,0.08054,0.08054,0.08054], "fy":[9.86574,9.86574,9.86574,9.86574]}, - {"t":1.87441, "x":5.4249, "y":7.62633, "heading":-1.5708, "vx":3.50672, "vy":-0.05068, "omega":0.0, "ax":-7.31945, "ay":1.27661, "alpha":0.0, "fx":[-124.50168,-124.50168,-124.50168,-124.50168], "fy":[21.71484,21.71484,21.71484,21.71484]}, - {"t":1.91042, "x":5.54643, "y":7.62533, "heading":-1.5708, "vx":3.24315, "vy":-0.0047, "omega":0.0, "ax":-9.3296, "ay":0.24236, "alpha":0.0, "fx":[-158.69386,-158.69386,-158.69386,-158.69386], "fy":[4.12246,4.12246,4.12246,4.12246]}, - {"t":1.94643, "x":5.65717, "y":7.62532, "heading":-1.5708, "vx":2.90719, "vy":0.00402, "omega":0.0, "ax":-9.38196, "ay":0.19205, "alpha":0.0, "fx":[-159.5844,-159.5844,-159.5844,-159.5844], "fy":[3.26678,3.26678,3.26678,3.26678]}, - {"t":1.98244, "x":5.75577, "y":7.62559, "heading":-1.5708, "vx":2.56934, "vy":0.01094, "omega":0.0, "ax":-9.40038, "ay":0.14629, "alpha":0.0, "fx":[-159.89771,-159.89771,-159.89771,-159.89771], "fy":[2.48838,2.48838,2.48838,2.48838]}, - {"t":2.01845, "x":5.8422, "y":7.62608, "heading":-1.5708, "vx":2.23083, "vy":0.01621, "omega":0.0, "ax":-8.05837, "ay":-4.86804, "alpha":0.0, "fx":[-137.07051,-137.07051,-137.07051,-137.07051], "fy":[-82.80405,-82.80405,-82.80405,-82.80405]}, - {"t":2.05446, "x":5.91731, "y":7.6235, "heading":-1.5708, "vx":1.94065, "vy":-0.15909, "omega":0.0, "ax":-5.36411, "ay":-7.75026, "alpha":0.0, "fx":[-91.24198,-91.24198,-91.24198,-91.24198], "fy":[-131.82967,-131.82967,-131.82967,-131.82967]}, - {"t":2.09047, "x":5.98371, "y":7.61275, "heading":-1.5708, "vx":1.74749, "vy":-0.43818, "omega":0.0, "ax":-4.70768, "ay":-8.16842, "alpha":0.0, "fx":[-80.07637,-80.07637,-80.07637,-80.07637], "fy":[-138.94253,-138.94253,-138.94253,-138.94253]}, - {"t":2.12775, "x":6.04559, "y":7.59074, "heading":-1.5708, "vx":1.57198, "vy":-0.74271, "omega":0.0, "ax":-4.75137, "ay":-8.14194, "alpha":0.0, "fx":[-80.81941,-80.81941,-80.81941,-80.81941], "fy":[-138.4921,-138.4921,-138.4921,-138.4921]}, - {"t":2.16503, "x":6.10089, "y":7.55739, "heading":-1.5708, "vx":1.39484, "vy":-1.04625, "omega":0.0, "ax":-4.80474, "ay":-8.10914, "alpha":0.0, "fx":[-81.72719,-81.72719,-81.72719,-81.72719], "fy":[-137.93417,-137.93417,-137.93417,-137.93417]}, - {"t":2.20231, "x":6.14956, "y":7.51275, "heading":-1.5708, "vx":1.21572, "vy":-1.34857, "omega":0.0, "ax":-4.87154, "ay":-8.06739, "alpha":0.0, "fx":[-82.86343,-82.86343,-82.86343,-82.86343], "fy":[-137.22391,-137.22391,-137.22391,-137.22391]}, - {"t":2.23959, "x":6.19149, "y":7.45686, "heading":-1.5708, "vx":1.0341, "vy":-1.64933, "omega":0.0, "ax":-4.95769, "ay":-8.01237, "alpha":0.0, "fx":[-84.32892,-84.32892,-84.32892,-84.32892], "fy":[-136.28805,-136.28805,-136.28805,-136.28805]}, - {"t":2.27688, "x":6.2266, "y":7.38981, "heading":-1.5708, "vx":0.84927, "vy":-1.94805, "omega":0.0, "ax":-5.07315, "ay":-7.93654, "alpha":0.0, "fx":[-86.2928,-86.2928,-86.2928,-86.2928], "fy":[-134.99823,-134.99823,-134.99823,-134.99823]}, - {"t":2.31416, "x":6.25474, "y":7.31167, "heading":-1.5708, "vx":0.66014, "vy":-2.24393, "omega":0.0, "ax":-5.23595, "ay":-7.82541, "alpha":0.0, "fx":[-89.06207,-89.06207,-89.06207,-89.06207], "fy":[-133.10796,-133.10796,-133.10796,-133.10796]}, - {"t":2.35144, "x":6.27571, "y":7.22257, "heading":-1.5708, "vx":0.46493, "vy":-2.53567, "omega":0.0, "ax":-5.48246, "ay":-7.64735, "alpha":0.0, "fx":[-93.25503,-93.25503,-93.25503,-93.25503], "fy":[-130.07919,-130.07919,-130.07919,-130.07919]}, - {"t":2.38872, "x":6.28923, "y":7.12272, "heading":-1.5708, "vx":0.26054, "vy":-2.82077, "omega":0.0, "ax":-5.89736, "ay":-7.31877, "alpha":0.0, "fx":[-100.31236,-100.31236,-100.31236,-100.31236], "fy":[-124.49025,-124.49025,-124.49025,-124.49025]}, - {"t":2.426, "x":6.29485, "y":7.01248, "heading":-1.5708, "vx":0.04068, "vy":-3.09363, "omega":0.0, "ax":-6.72261, "ay":-6.5372, "alpha":0.0, "fx":[-114.34964,-114.34964,-114.34964,-114.34964], "fy":[-111.19593,-111.19593,-111.19593,-111.19593]}, - {"t":2.46328, "x":6.29169, "y":6.8926, "heading":-1.5708, "vx":-0.20995, "vy":-3.33734, "omega":0.0, "ax":-8.67537, "ay":-3.38172, "alpha":0.0, "fx":[-147.56555,-147.56555,-147.56555,-147.56555], "fy":[-57.52202,-57.52202,-57.52202,-57.52202]}, - {"t":2.50056, "x":6.27784, "y":6.76583, "heading":-1.5708, "vx":-0.53338, "vy":-3.46342, "omega":0.0, "ax":-9.001, "ay":1.75759, "alpha":0.0, "fx":[-153.1045,-153.1045,-153.1045,-153.1045], "fy":[29.89604,29.89604,29.89604,29.89604]}, - {"t":2.53784, "x":6.2517, "y":6.63793, "heading":-1.5708, "vx":-0.86895, "vy":-3.39789, "omega":0.0, "ax":-8.41627, "ay":2.57315, "alpha":0.0, "fx":[-143.15838,-143.15838,-143.15838,-143.15838], "fy":[43.7686,43.7686,43.7686,43.7686]}, - {"t":2.57513, "x":6.21345, "y":6.51304, "heading":-1.5708, "vx":-1.18272, "vy":-3.30196, "omega":0.0, "ax":-5.8241, "ay":2.30632, "alpha":0.0, "fx":[-99.06634,-99.06634,-99.06634,-99.06634], "fy":[39.22986,39.22986,39.22986,39.22986]}, - {"t":2.61241, "x":6.16531, "y":6.39154, "heading":-1.5708, "vx":-1.39985, "vy":-3.21598, "omega":0.0, "ax":6.53805, "ay":-2.55852, "alpha":0.0, "fx":[111.21042,111.21042,111.21042,111.21042], "fy":[-43.51976,-43.51976,-43.51976,-43.51976]}, - {"t":2.64969, "x":6.11767, "y":6.26987, "heading":-1.5708, "vx":-1.1561, "vy":-3.31136, "omega":0.0, "ax":8.5008, "ay":-2.52128, "alpha":0.0, "fx":[144.59619,144.59619,144.59619,144.59619], "fy":[-42.88626,-42.88626,-42.88626,-42.88626]}, - {"t":2.68697, "x":6.08047, "y":6.14466, "heading":-1.5708, "vx":-0.83918, "vy":-3.40536, "omega":0.0, "ax":9.01854, "ay":-1.75987, "alpha":0.0, "fx":[153.40274,153.40274,153.40274,153.40274], "fy":[-29.93493,-29.93493,-29.93493,-29.93493]}, - {"t":2.72425, "x":6.05546, "y":6.01648, "heading":-1.5708, "vx":-0.50296, "vy":-3.47097, "omega":0.0, "ax":9.07061, "ay":-0.84253, "alpha":0.0, "fx":[154.28856,154.28856,154.28856,154.28856], "fy":[-14.33122,-14.33122,-14.33122,-14.33122]}, - {"t":2.76455, "x":6.04255, "y":5.8759, "heading":-1.5708, "vx":-0.13738, "vy":-3.50493, "omega":0.0, "ax":3.97476, "ay":-0.06661, "alpha":0.0, "fx":[67.60945,67.60945,67.60945,67.60945], "fy":[-1.13295,-1.13295,-1.13295,-1.13295]}, - {"t":2.80486, "x":6.04024, "y":5.73459, "heading":-1.5708, "vx":0.02282, "vy":-3.50761, "omega":0.0, "ax":-1.02432, "ay":-0.0006, "alpha":0.0, "fx":[-17.42345,-17.42345,-17.42345,-17.42345], "fy":[-0.0102,-0.0102,-0.0102,-0.0102]}, - {"t":2.84516, "x":6.04033, "y":5.59322, "heading":-1.5708, "vx":-0.01846, "vy":-3.50764, "omega":0.0, "ax":-0.50729, "ay":0.00416, "alpha":0.0, "fx":[-8.62882,-8.62882,-8.62882,-8.62882], "fy":[0.0707,0.0707,0.0707,0.0707]}, - {"t":2.88547, "x":6.03918, "y":5.45185, "heading":-1.5708, "vx":-0.03891, "vy":-3.50747, "omega":0.0, "ax":-0.28156, "ay":0.00358, "alpha":0.0, "fx":[-4.78919,-4.78919,-4.78919,-4.78919], "fy":[0.06096,0.06096,0.06096,0.06096]}, - {"t":2.92577, "x":6.03738, "y":5.31049, "heading":-1.5708, "vx":-0.05026, "vy":-3.50733, "omega":0.0, "ax":-0.17942, "ay":0.00276, "alpha":0.0, "fx":[-3.05181,-3.05181,-3.05181,-3.05181], "fy":[0.04693,0.04693,0.04693,0.04693]}, - {"t":2.96607, "x":6.03521, "y":5.16913, "heading":-1.5708, "vx":-0.05749, "vy":-3.50721, "omega":0.0, "ax":-0.12258, "ay":0.0021, "alpha":0.0, "fx":[-2.08505,-2.08505,-2.08505,-2.08505], "fy":[0.03569,0.03569,0.03569,0.03569]}, - {"t":3.00638, "x":6.03279, "y":5.02778, "heading":-1.5708, "vx":-0.06243, "vy":-3.50713, "omega":0.0, "ax":-0.08635, "ay":0.00158, "alpha":0.0, "fx":[-1.4688,-1.4688,-1.4688,-1.4688], "fy":[0.02691,0.02691,0.02691,0.02691]}, - {"t":3.04668, "x":6.0302, "y":4.88643, "heading":-1.5708, "vx":-0.06591, "vy":-3.50707, "omega":0.0, "ax":-0.06164, "ay":0.00118, "alpha":0.0, "fx":[-1.04848,-1.04848,-1.04848,-1.04848], "fy":[0.0201,0.0201,0.0201,0.0201]}, - {"t":3.08698, "x":6.0275, "y":4.74508, "heading":-1.5708, "vx":-0.06839, "vy":-3.50702, "omega":0.0, "ax":-0.04423, "ay":0.00088, "alpha":0.0, "fx":[-0.75235,-0.75235,-0.75235,-0.75235], "fy":[0.01489,0.01489,0.01489,0.01489]}, - {"t":3.12729, "x":6.02471, "y":4.60374, "heading":-1.5708, "vx":-0.07017, "vy":-3.50698, "omega":0.0, "ax":-0.03177, "ay":0.00064, "alpha":0.0, "fx":[-0.54039,-0.54039,-0.54039,-0.54039], "fy":[0.01093,0.01093,0.01093,0.01093]}, - {"t":3.16759, "x":6.02185, "y":4.4624, "heading":-1.5708, "vx":-0.07145, "vy":-3.50696, "omega":0.0, "ax":-0.02289, "ay":0.00047, "alpha":0.0, "fx":[-0.38936,-0.38936,-0.38936,-0.38936], "fy":[0.008,0.008,0.008,0.008]}, - {"t":3.2079, "x":6.01895, "y":4.32105, "heading":-1.5708, "vx":-0.07238, "vy":-3.50694, "omega":0.0, "ax":-0.01755, "ay":0.00079, "alpha":0.0, "fx":[-0.29855,-0.29855,-0.29855,-0.29855], "fy":[0.01347,0.01347,0.01347,0.01347]}, - {"t":3.2482, "x":6.01602, "y":4.17971, "heading":-1.5708, "vx":-0.07308, "vy":-3.50691, "omega":0.0, "ax":0.0278, "ay":2.34463, "alpha":0.0, "fx":[0.47295,0.47295,0.47295,0.47295], "fy":[39.88143,39.88143,39.88143,39.88143]}, - {"t":3.2885, "x":6.0131, "y":4.04027, "heading":-1.5708, "vx":-0.07196, "vy":-3.41241, "omega":0.0, "ax":0.19543, "ay":9.34592, "alpha":0.0, "fx":[3.32425,3.32425,3.32425,3.32425], "fy":[158.97137,158.97137,158.97137,158.97137]}, - {"t":3.32881, "x":6.01036, "y":3.91033, "heading":-1.5708, "vx":-0.06409, "vy":-3.03573, "omega":0.0, "ax":0.19708, "ay":9.39104, "alpha":0.0, "fx":[3.35235,3.35235,3.35235,3.35235], "fy":[159.73898,159.73898,159.73898,159.73898]}, - {"t":3.36911, "x":6.00793, "y":3.79561, "heading":-1.5708, "vx":-0.05614, "vy":-2.65724, "omega":0.0, "ax":0.19787, "ay":9.40592, "alpha":0.0, "fx":[3.36579,3.36579,3.36579,3.36579], "fy":[159.99206,159.99206,159.99206,159.99206]}, - {"t":3.40941, "x":6.00583, "y":3.69615, "heading":-1.5708, "vx":-0.04817, "vy":-2.27815, "omega":0.0, "ax":0.19841, "ay":9.41332, "alpha":0.0, "fx":[3.37486,3.37486,3.37486,3.37486], "fy":[160.11785,160.11785,160.11785,160.11785]}, - {"t":3.44972, "x":6.00405, "y":3.61198, "heading":-1.5708, "vx":-0.04017, "vy":-1.89875, "omega":0.0, "ax":0.19881, "ay":9.41774, "alpha":0.0, "fx":[3.38172,3.38172,3.38172,3.38172], "fy":[160.19304,160.19304,160.19304,160.19304]}, - {"t":3.49002, "x":6.00259, "y":3.5431, "heading":-1.5708, "vx":-0.03216, "vy":-1.51918, "omega":0.0, "ax":0.19913, "ay":9.42068, "alpha":0.0, "fx":[3.38717,3.38717,3.38717,3.38717], "fy":[160.24303,160.24303,160.24303,160.24303]}, - {"t":3.53033, "x":6.00146, "y":3.48952, "heading":-1.5708, "vx":-0.02413, "vy":-1.13949, "omega":0.0, "ax":0.19939, "ay":9.42277, "alpha":0.0, "fx":[3.39163,3.39163,3.39163,3.39163], "fy":[160.27867,160.27867,160.27867,160.27867]}, - {"t":3.57063, "x":6.00065, "y":3.45125, "heading":-1.5708, "vx":-0.0161, "vy":-0.75972, "omega":0.0, "ax":0.19961, "ay":9.42434, "alpha":0.0, "fx":[3.39535,3.39535,3.39535,3.39535], "fy":[160.30535,160.30535,160.30535,160.30535]}, - {"t":3.61093, "x":6.00016, "y":3.42828, "heading":-1.5708, "vx":-0.00805, "vy":-0.37989, "omega":0.0, "ax":0.1998, "ay":9.42556, "alpha":0.0, "fx":[3.3985,3.3985,3.3985,3.3985], "fy":[160.32608,160.32608,160.32608,160.32608]}, - {"t":3.65124, "x":6.0, "y":3.42063, "heading":-1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0,0.0,0.0,0.0]}], + {"t":0.0, "x":2.43494, "y":5.23076, "heading":0.95518, "vx":0.0, "vy":0.0, "omega":0.0, "ax":-2.24411, "ay":7.47038, "alpha":-16.0962, "fx":[84.83647,-1.9575,-129.22831,-106.33706], "fy":[136.10184,160.39394,94.88259,116.89791]}, + {"t":0.03409, "x":2.43363, "y":5.2351, "heading":0.94583, "vx":-0.07649, "vy":0.25463, "omega":-0.54864, "ax":-1.94478, "ay":7.75552, "alpha":-14.91704, "fx":[83.48966,-1.3441,-126.22531,-88.24057], "fy":[136.90524,160.38201,98.79527,131.59398]}, + {"t":0.06817, "x":2.4299, "y":5.24828, "heading":0.91846, "vx":-0.14278, "vy":0.51898, "omega":-1.0571, "ax":-1.82926, "ay":7.86075, "alpha":-14.63374, "fx":[82.97304,0.56085,-122.13848,-85.85605], "fy":[137.18219,160.36264,103.75166,133.53981]}, + {"t":0.10226, "x":2.42397, "y":5.27054, "heading":0.87393, "vx":-0.20513, "vy":0.78692, "omega":-1.55589, "ax":-1.73622, "ay":7.93246, "alpha":-14.60675, "fx":[82.86564,3.56339,-116.7575,-87.80225], "fy":[137.1946,160.2908,109.70756,132.52276]}, + {"t":0.13634, "x":2.41597, "y":5.30197, "heading":0.81241, "vx":-0.26431, "vy":1.0573, "omega":-2.05377, "ax":-1.58408, "ay":8.03582, "alpha":-14.50544, "fx":[82.46067,7.42701,-109.58819,-88.07818], "fy":[137.35879,160.10901,116.78552,132.49435]}, + {"t":0.17043, "x":2.40604, "y":5.34268, "heading":0.73398, "vx":-0.3183, "vy":1.3312, "omega":-2.54819, "ax":-1.33264, "ay":8.22336, "alpha":-13.9486, "fx":[80.18024,11.65523,-99.50397,-83.00283], "fy":[138.57137,159.77585,125.36674,135.79428]}, + {"t":0.20451, "x":2.39441, "y":5.39283, "heading":0.63902, "vx":-0.36373, "vy":1.6115, "omega":-3.02364, "ax":-0.95906, "ay":8.56122, "alpha":-12.189, "fx":[71.54544,14.81903,-83.43977,-68.17815], "fy":[142.98001,159.35597,136.37962,143.77967]}, + {"t":0.2386, "x":2.38146, "y":5.45273, "heading":0.52888, "vx":-0.39642, "vy":1.90331, "omega":-3.4391, "ax":-0.5042, "ay":9.12187, "alpha":-6.86694, "fx":[39.02697,10.63274,-49.08903,-34.87604], "fy":[154.53708,159.26065,151.70722,155.13665]}, + {"t":0.27268, "x":2.36765, "y":5.5229, "heading":0.40767, "vx":-0.4136, "vy":2.21423, "omega":-3.67316, "ax":-0.73511, "ay":8.3138, "alpha":14.31529, "fx":[-74.00258,-83.69306,79.11763,28.56225], "fy":[140.19288,132.30414,136.89763,156.26681]}, + {"t":0.30677, "x":2.35313, "y":5.6032, "heading":0.29078, "vx":-0.43866, "vy":2.49761, "omega":-3.18522, "ax":0.40721, "ay":7.73037, "alpha":17.78223, "fx":[-62.42591,-84.97538,116.21555,58.89211], "fy":[144.87469,128.00954,106.10432,146.97666]}, + {"t":0.34085, "x":2.33841, "y":5.69283, "heading":0.19254, "vx":-0.42478, "vy":2.7611, "omega":-2.57911, "ax":2.27029, "ay":6.80128, "alpha":18.92372, "fx":[-35.81645,-52.42218,146.23002,96.47621], "fy":[151.92701,129.92807,56.29155,124.60497]}, + {"t":0.37494, "x":2.32525, "y":5.79089, "heading":0.11562, "vx":-0.3474, "vy":2.99293, "omega":-1.93409, "ax":3.43925, "ay":5.57838, "alpha":19.23686, "fx":[-17.85943,-12.76134,151.64494,112.97857], "fy":[151.18841,92.97319,28.52315,106.8619]}, + {"t":0.40902, "x":2.31541, "y":5.89615, "heading":0.06088, "vx":-0.23017, "vy":3.18307, "omega":-1.27839, "ax":2.30902, "ay":5.30925, "alpha":20.08508, "fx":[-25.48673,-51.07635,135.46485,98.20136], "fy":[137.32743,78.98799,37.44667,107.47346]}, + {"t":0.44311, "x":2.30891, "y":6.00773, "heading":0.02897, "vx":-0.15146, "vy":3.36404, "omega":-0.59379, "ax":0.67986, "ay":2.72812, "alpha":11.15401, "fx":[-25.80576,-29.36154,55.31423,46.1097], "fy":[70.11982,23.59544,23.83157,68.07133]}, + {"t":0.47719, "x":2.30414, "y":6.12398, "heading":0.01521, "vx":-0.12829, "vy":3.45702, "omega":-0.2136, "ax":0.06158, "ay":0.29136, "alpha":1.2496, "fx":[-3.1828,-3.10339,5.28671,5.18918], "fy":[7.72354,2.06441,2.19047,7.84522]}, + {"t":0.51128, "x":2.2998, "y":6.24198, "heading":0.00865, "vx":-0.12619, "vy":3.46696, "omega":-0.171, "ax":0.09279, "ay":0.02977, "alpha":0.13774, "fx":[1.11402,1.11945,2.04275,2.03729], "fy":[0.8147,0.19024,0.19821,0.8226]}, + {"t":0.54537, "x":2.29556, "y":6.36017, "heading":0.00291, "vx":-0.12303, "vy":3.46797, "omega":-0.16631, "ax":0.35799, "ay":-0.05848, "alpha":-0.27978, "fx":[7.02782,7.02483,5.15073,5.15391], "fy":[-1.62671,-0.35654,-0.36232,-1.63362]}, + {"t":0.57945, "x":2.29157, "y":6.47834, "heading":-0.00293, "vx":-0.11083, "vy":3.46598, "omega":-0.17585, "ax":1.5032, "ay":-0.62957, "alpha":-2.81398, "fx":[34.68126,35.06883,16.37363,16.15201], "fy":[-16.98067,-4.09273,-4.19438,-17.56722]}, + {"t":0.61354, "x":2.28867, "y":6.59612, "heading":-0.01055, "vx":-0.05959, "vy":3.44452, "omega":-0.27176, "ax":3.42439, "ay":-2.77675, "alpha":-11.60507, "fx":[89.69541,101.75898,24.41104,17.1259], "fy":[-65.19172,-14.87102,-22.65171,-86.21271]}, + {"t":0.64762, "x":2.28862, "y":6.71191, "heading":-0.02656, "vx":0.05713, "vy":3.34987, "omega":-0.66732, "ax":3.6725, "ay":-4.48028, "alpha":-17.06869, "fx":[108.60391,135.73343,6.14673,-0.61143], "fy":[-91.5506,-21.89678,-59.33258,-132.0535]}, + {"t":0.68171, "x":2.2927, "y":6.82349, "heading":-0.05922, "vx":0.18231, "vy":3.19716, "omega":-1.24912, "ax":4.42002, "ay":-5.00866, "alpha":-16.82549, "fx":[117.77878,149.24857,34.02696,-0.32149], "fy":[-96.88035,-20.84058,-75.89902,-147.16372]}, + {"t":0.71579, "x":2.30149, "y":6.92956, "heading":-0.11157, "vx":0.33297, "vy":3.02644, "omega":-1.82262, "ax":5.04819, "ay":-5.28068, "alpha":-15.10463, "fx":[117.97864,152.95112,74.96668,-2.42326], "fy":[-102.1214,-25.75981,-78.70938,-152.70088]}, + {"t":0.74988, "x":2.31577, "y":7.02965, "heading":-0.18247, "vx":0.50504, "vy":2.84644, "omega":-2.33747, "ax":4.36541, "ay":-6.14469, "alpha":-14.91257, "fx":[109.25779,151.40384,52.82951,-16.47397], "fy":[-113.14319,-39.86062,-110.94448,-154.12957]}, + {"t":0.78396, "x":2.33552, "y":7.1231, "heading":-0.27081, "vx":0.65383, "vy":2.637, "omega":-2.84577, "ax":4.15842, "ay":-6.65267, "alpha":-13.66524, "fx":[99.62595,147.38983,60.79872,-24.88023], "fy":[-122.52519,-55.07587,-120.96349,-154.0757]}, + {"t":0.81805, "x":2.36022, "y":7.20912, "heading":-0.37574, "vx":0.79557, "vy":2.41024, "omega":-3.31155, "ax":4.76635, "ay":-6.7469, "alpha":-11.34558, "fx":[93.32091,141.66628,103.72601,-14.4162], "fy":[-127.87671,-69.76212,-105.51113,-155.90129]}, + {"t":0.85213, "x":2.39011, "y":7.28735, "heading":-0.49521, "vx":0.95804, "vy":2.18027, "omega":-3.69827, "ax":5.57342, "ay":-6.69069, "alpha":-8.41906, "fx":[93.48351,135.611,125.48884,24.62557], "fy":[-128.07058,-81.7608,-90.42415,-154.9714]}, + {"t":0.88622, "x":2.426, "y":7.35778, "heading":-0.62616, "vx":1.14801, "vy":1.95222, "omega":-3.98524, "ax":6.57513, "ay":-6.37303, "alpha":-4.07149, "fx":[102.73404,130.24491,130.32168,84.06395], "fy":[-121.01115,-90.65085,-88.48338,-133.46841]}, + {"t":0.9203, "x":2.46895, "y":7.42062, "heading":-0.76436, "vx":1.37212, "vy":1.73499, "omega":-4.12401, "ax":7.02393, "ay":-6.15727, "alpha":0.30813, "fx":[121.00215,118.08663,118.00224,120.80944], "fy":[-102.93943,-106.26818,-106.45985,-103.26616]}, + {"t":0.95439, "x":2.5198, "y":7.47618, "heading":-0.90475, "vx":1.61154, "vy":1.52512, "omega":-4.11351, "ax":6.99589, "ay":-5.98834, "alpha":4.26731, "fx":[143.31613,101.97949,100.86938,129.82745], "fy":[-69.05964,-121.89083,-123.64601,-92.84362]}, + {"t":0.98848, "x":2.57879, "y":7.52469, "heading":-1.04248, "vx":1.85, "vy":1.321, "omega":-3.96806, "ax":6.79045, "ay":-5.75472, "alpha":7.80726, "fx":[156.1461,91.23612,85.11367,129.51879], "fy":[-32.25278,-130.11161,-135.34524,-93.8347]}, + {"t":1.02256, "x":2.64579, "y":7.56637, "heading":-1.1732, "vx":2.08145, "vy":1.12485, "omega":-3.70194, "ax":6.47428, "ay":-5.65724, "alpha":10.24916, "fx":[159.57115,86.7507,68.93755,125.24314], "fy":[-7.71678,-132.98264,-144.4544,-99.7586]}, + {"t":1.05665, "x":2.7205, "y":7.60142, "heading":-1.29343, "vx":2.30213, "vy":0.93202, "omega":-3.3526, "ax":6.09456, "ay":-5.69842, "alpha":11.88212, "fx":[159.88182,85.46095,50.94196,118.38193], "fy":[5.41295,-133.29485,-151.8578,-107.97403]}, + {"t":1.09073, "x":2.80251, "y":7.62988, "heading":-1.4008, "vx":2.50986, "vy":0.73779, "omega":-2.94759, "ax":3.69775, "ay":-6.02481, "alpha":18.22361, "fx":[159.18991,-39.1459,24.26673,107.28021], "fy":[17.14245,-149.53311,-158.41562,-119.11504]}, + {"t":1.12482, "x":2.89021, "y":7.65153, "heading":-1.49068, "vx":2.6359, "vy":0.53243, "omega":-2.32643, "ax":1.58989, "ay":-2.9272, "alpha":33.42368, "fx":[158.24873,-144.46528,-0.94059,95.33133], "fy":[24.94439,65.15618,-160.32273,-128.94096]}, + {"t":1.1589, "x":2.98098, "y":7.66798, "heading":-1.55056, "vx":2.6901, "vy":0.43266, "omega":-1.18718, "ax":1.429, "ay":-2.54483, "alpha":34.82949, "fx":[157.74159,-129.59161,-17.41088,86.4884], "fy":[28.32003,93.02077,-159.41733,-135.07059]}, + {"t":1.19299, "x":3.0735, "y":7.68125, "heading":-1.5708, "vx":2.7388, "vy":0.34591, "omega":0.0, "ax":4.75918, "ay":-8.12456, "alpha":0.0, "fx":[80.95233,80.95233,80.95233,80.95233], "fy":[-138.19652,-138.19652,-138.19652,-138.19652]}, + {"t":1.22901, "x":3.17523, "y":7.68844, "heading":-1.5708, "vx":2.91022, "vy":0.05329, "omega":0.0, "ax":6.96031, "ay":-6.29954, "alpha":0.0, "fx":[118.39283,118.39283,118.39283,118.39283], "fy":[-107.15333,-107.15333,-107.15333,-107.15333]}, + {"t":1.26502, "x":3.28457, "y":7.68627, "heading":-1.5708, "vx":3.16091, "vy":-0.17361, "omega":0.0, "ax":9.26972, "ay":-0.30786, "alpha":0.0, "fx":[157.67531,157.67531,157.67531,157.67531], "fy":[-5.23656,-5.23656,-5.23656,-5.23656]}, + {"t":1.30104, "x":3.40443, "y":7.67982, "heading":-1.5708, "vx":3.49479, "vy":-0.1847, "omega":0.0, "ax":0.27487, "ay":1.38149, "alpha":0.0, "fx":[4.67541,4.67541,4.67541,4.67541], "fy":[23.49878,23.49878,23.49878,23.49878]}, + {"t":1.33706, "x":3.53048, "y":7.67406, "heading":-1.5708, "vx":3.50469, "vy":-0.13494, "omega":0.0, "ax":0.01887, "ay":0.52579, "alpha":0.0, "fx":[0.32097,0.32097,0.32097,0.32097], "fy":[8.94359,8.94359,8.94359,8.94359]}, + {"t":1.37308, "x":3.65673, "y":7.66954, "heading":-1.5708, "vx":3.50537, "vy":-0.116, "omega":0.0, "ax":0.00908, "ay":0.28738, "alpha":0.0, "fx":[0.15438,0.15438,0.15438,0.15438], "fy":[4.88821,4.88821,4.88821,4.88821]}, + {"t":1.40909, "x":3.78299, "y":7.66555, "heading":-1.5708, "vx":3.50569, "vy":-0.10565, "omega":0.0, "ax":0.00558, "ay":0.19173, "alpha":0.0, "fx":[0.09493,0.09493,0.09493,0.09493], "fy":[3.2612,3.2612,3.2612,3.2612]}, + {"t":1.44511, "x":3.90926, "y":7.66187, "heading":-1.5708, "vx":3.50589, "vy":-0.09875, "omega":0.0, "ax":0.00376, "ay":0.13716, "alpha":0.0, "fx":[0.06394,0.06394,0.06394,0.06394], "fy":[2.33297,2.33297,2.33297,2.33297]}, + {"t":1.48113, "x":4.03553, "y":7.6584, "heading":-1.5708, "vx":3.50603, "vy":-0.09381, "omega":0.0, "ax":0.00263, "ay":0.10047, "alpha":0.0, "fx":[0.04473,0.04473,0.04473,0.04473], "fy":[1.70903,1.70903,1.70903,1.70903]}, + {"t":1.51715, "x":4.16182, "y":7.65509, "heading":-1.5708, "vx":3.50612, "vy":-0.09019, "omega":0.0, "ax":0.00189, "ay":0.07465, "alpha":0.0, "fx":[0.03207,0.03207,0.03207,0.03207], "fy":[1.26977,1.26977,1.26977,1.26977]}, + {"t":1.55316, "x":4.2881, "y":7.65189, "heading":-1.5708, "vx":3.50619, "vy":-0.0875, "omega":0.0, "ax":0.00139, "ay":0.05654, "alpha":0.0, "fx":[0.02362,0.02362,0.02362,0.02362], "fy":[0.96167,0.96167,0.96167,0.96167]}, + {"t":1.58918, "x":4.41439, "y":7.64877, "heading":-1.5708, "vx":3.50624, "vy":-0.08546, "omega":0.0, "ax":0.00106, "ay":0.04428, "alpha":0.0, "fx":[0.01809,0.01809,0.01809,0.01809], "fy":[0.75316,0.75316,0.75316,0.75316]}, + {"t":1.6252, "x":4.54067, "y":7.64572, "heading":-1.5708, "vx":3.50628, "vy":-0.08387, "omega":0.0, "ax":0.00086, "ay":0.03667, "alpha":0.0, "fx":[0.01471,0.01471,0.01471,0.01471], "fy":[0.62377,0.62377,0.62377,0.62377]}, + {"t":1.66122, "x":4.66696, "y":7.64273, "heading":-1.5708, "vx":3.50631, "vy":-0.08255, "omega":0.0, "ax":0.00077, "ay":0.03299, "alpha":0.0, "fx":[0.01303,0.01303,0.01303,0.01303], "fy":[0.56119,0.56119,0.56119,0.56119]}, + {"t":1.69724, "x":4.79325, "y":7.63977, "heading":-1.5708, "vx":3.50634, "vy":-0.08136, "omega":0.0, "ax":0.00076, "ay":0.03308, "alpha":0.0, "fx":[0.01288,0.01288,0.01288,0.01288], "fy":[0.56273,0.56273,0.56273,0.56273]}, + {"t":1.73325, "x":4.91954, "y":7.63687, "heading":-1.5708, "vx":3.50637, "vy":-0.08017, "omega":0.0, "ax":0.00086, "ay":0.03831, "alpha":0.0, "fx":[0.01469,0.01469,0.01469,0.01469], "fy":[0.65173,0.65173,0.65173,0.65173]}, + {"t":1.76927, "x":5.04584, "y":7.634, "heading":-1.5708, "vx":3.5064, "vy":-0.07879, "omega":0.0, "ax":0.00127, "ay":0.05744, "alpha":0.0, "fx":[0.02159,0.02159,0.02159,0.02159], "fy":[0.97698,0.97698,0.97698,0.97698]}, + {"t":1.80529, "x":5.17213, "y":7.6312, "heading":-1.5708, "vx":3.50644, "vy":-0.07672, "omega":0.0, "ax":0.00297, "ay":0.1408, "alpha":0.0, "fx":[0.05056,0.05056,0.05056,0.05056], "fy":[2.39491,2.39491,2.39491,2.39491]}, + {"t":1.84131, "x":5.29842, "y":7.62853, "heading":-1.5708, "vx":3.50655, "vy":-0.07165, "omega":0.0, "ax":0.00477, "ay":0.57857, "alpha":0.0, "fx":[0.08119,0.08119,0.08119,0.08119], "fy":[9.84126,9.84126,9.84126,9.84126]}, + {"t":1.87732, "x":5.42473, "y":7.62633, "heading":-1.5708, "vx":3.50672, "vy":-0.05081, "omega":0.0, "ax":-7.30588, "ay":1.28074, "alpha":0.0, "fx":[-124.27097,-124.27097,-124.27097,-124.27097], "fy":[21.78509,21.78509,21.78509,21.78509]}, + {"t":1.91334, "x":5.54629, "y":7.62533, "heading":-1.5708, "vx":3.24358, "vy":-0.00468, "omega":0.0, "ax":-9.3296, "ay":0.2425, "alpha":0.0, "fx":[-158.69384,-158.69384,-158.69384,-158.69384], "fy":[4.1249,4.1249,4.1249,4.1249]}, + {"t":1.94936, "x":5.65707, "y":7.62531, "heading":-1.5708, "vx":2.90755, "vy":0.00406, "omega":0.0, "ax":-9.38196, "ay":0.19228, "alpha":0.0, "fx":[-159.58451,-159.58451,-159.58451,-159.58451], "fy":[3.27056,3.27056,3.27056,3.27056]}, + {"t":1.98538, "x":5.7557, "y":7.62559, "heading":-1.5708, "vx":2.56963, "vy":0.01098, "omega":0.0, "ax":-9.40038, "ay":0.14657, "alpha":0.0, "fx":[-159.89779,-159.89779,-159.89779,-159.89779], "fy":[2.49316,2.49316,2.49316,2.49316]}, + {"t":2.0214, "x":5.84216, "y":7.62608, "heading":-1.5708, "vx":2.23105, "vy":0.01626, "omega":0.0, "ax":-8.05865, "ay":-4.86759, "alpha":0.0, "fx":[-137.07528,-137.07528,-137.07528,-137.07528], "fy":[-82.79636,-82.79636,-82.79636,-82.79636]}, + {"t":2.05741, "x":5.91729, "y":7.6235, "heading":-1.5708, "vx":1.9408, "vy":-0.15906, "omega":0.0, "ax":-5.3645, "ay":-7.74999, "alpha":0.0, "fx":[-91.24859,-91.24859,-91.24859,-91.24859], "fy":[-131.82515,-131.82515,-131.82515,-131.82515]}, + {"t":2.09343, "x":5.98371, "y":7.61275, "heading":-1.5708, "vx":1.74758, "vy":-0.4382, "omega":0.0, "ax":-4.70806, "ay":-8.16821, "alpha":0.0, "fx":[-80.08272,-80.08272,-80.08272,-80.08272], "fy":[-138.93888,-138.93888,-138.93888,-138.93888]}, + {"t":2.13071, "x":6.04559, "y":7.59074, "heading":-1.5708, "vx":1.57206, "vy":-0.74272, "omega":0.0, "ax":-4.75174, "ay":-8.14172, "alpha":0.0, "fx":[-80.82574,-80.82574,-80.82574,-80.82574], "fy":[-138.48841,-138.48841,-138.48841,-138.48841]}, + {"t":2.16799, "x":6.1009, "y":7.55739, "heading":-1.5708, "vx":1.39491, "vy":-1.04626, "omega":0.0, "ax":-4.80511, "ay":-8.10892, "alpha":0.0, "fx":[-81.73349,-81.73349,-81.73349,-81.73349], "fy":[-137.93044,-137.93044,-137.93044,-137.93044]}, + {"t":2.20528, "x":6.14957, "y":7.51275, "heading":-1.5708, "vx":1.21576, "vy":-1.34857, "omega":0.0, "ax":-4.8719, "ay":-8.06716, "alpha":0.0, "fx":[-82.86969,-82.86969,-82.86969,-82.86969], "fy":[-137.22014,-137.22014,-137.22014,-137.22014]}, + {"t":2.24256, "x":6.1915, "y":7.45686, "heading":-1.5708, "vx":1.03413, "vy":-1.64932, "omega":0.0, "ax":-4.95806, "ay":-8.01214, "alpha":0.0, "fx":[-84.3351,-84.3351,-84.3351,-84.3351], "fy":[-136.28424,-136.28424,-136.28424,-136.28424]}, + {"t":2.27984, "x":6.22661, "y":7.38981, "heading":-1.5708, "vx":0.84929, "vy":-1.94803, "omega":0.0, "ax":-5.07351, "ay":-7.93631, "alpha":0.0, "fx":[-86.29887,-86.29887,-86.29887,-86.29887], "fy":[-134.99437,-134.99437,-134.99437,-134.99437]}, + {"t":2.31712, "x":6.25475, "y":7.31166, "heading":-1.5708, "vx":0.66014, "vy":-2.24391, "omega":0.0, "ax":-5.2363, "ay":-7.82518, "alpha":0.0, "fx":[-89.06793,-89.06793,-89.06793,-89.06793], "fy":[-133.10407,-133.10407,-133.10407,-133.10407]}, + {"t":2.3544, "x":6.27572, "y":7.22257, "heading":-1.5708, "vx":0.46492, "vy":-2.53564, "omega":0.0, "ax":-5.48278, "ay":-7.64712, "alpha":0.0, "fx":[-93.26048,-93.26048,-93.26048,-93.26048], "fy":[-130.07533,-130.07533,-130.07533,-130.07533]}, + {"t":2.39168, "x":6.28924, "y":7.12272, "heading":-1.5708, "vx":0.26052, "vy":-2.82074, "omega":0.0, "ax":-5.89762, "ay":-7.31857, "alpha":0.0, "fx":[-100.31687,-100.31687,-100.31687,-100.31687], "fy":[-124.4867,-124.4867,-124.4867,-124.4867]}, + {"t":2.42896, "x":6.29486, "y":7.01248, "heading":-1.5708, "vx":0.04064, "vy":-3.09359, "omega":0.0, "ax":-6.72271, "ay":-6.53711, "alpha":0.0, "fx":[-114.35142,-114.35142,-114.35142,-114.35142], "fy":[-111.19431,-111.19431,-111.19431,-111.19431]}, + {"t":2.46625, "x":6.2917, "y":6.8926, "heading":-1.5708, "vx":-0.20999, "vy":-3.3373, "omega":0.0, "ax":-8.67501, "ay":-3.38271, "alpha":0.0, "fx":[-147.55949,-147.55949,-147.55949,-147.55949], "fy":[-57.53896,-57.53896,-57.53896,-57.53896]}, + {"t":2.50353, "x":6.27784, "y":6.76583, "heading":-1.5708, "vx":-0.53341, "vy":-3.46341, "omega":0.0, "ax":-9.00105, "ay":1.75762, "alpha":0.0, "fx":[-153.10533,-153.10533,-153.10533,-153.10533], "fy":[29.89659,29.89659,29.89659,29.89659]}, + {"t":2.54081, "x":6.2517, "y":6.63793, "heading":-1.5708, "vx":-0.86898, "vy":-3.39788, "omega":0.0, "ax":-8.4164, "ay":2.57329, "alpha":0.0, "fx":[-143.16052,-143.16052,-143.16052,-143.16052], "fy":[43.77092,43.77092,43.77092,43.77092]}, + {"t":2.57809, "x":6.21346, "y":6.51304, "heading":-1.5708, "vx":-1.18276, "vy":-3.30195, "omega":0.0, "ax":-5.82458, "ay":2.30661, "alpha":0.0, "fx":[-99.0744,-99.0744,-99.0744,-99.0744], "fy":[39.23476,39.23476,39.23476,39.23476]}, + {"t":2.61537, "x":6.16531, "y":6.39154, "heading":-1.5708, "vx":-1.3999, "vy":-3.21595, "omega":0.0, "ax":6.53917, "ay":-2.55904, "alpha":0.0, "fx":[111.22934,111.22934,111.22934,111.22934], "fy":[-43.52861,-43.52861,-43.52861,-43.52861]}, + {"t":2.65265, "x":6.11767, "y":6.26987, "heading":-1.5708, "vx":-1.15611, "vy":-3.31136, "omega":0.0, "ax":8.50099, "ay":-2.52137, "alpha":0.0, "fx":[144.59938,144.59938,144.59938,144.59938], "fy":[-42.88771,-42.88771,-42.88771,-42.88771]}, + {"t":2.68994, "x":6.08047, "y":6.14466, "heading":-1.5708, "vx":-0.83918, "vy":-3.40536, "omega":0.0, "ax":9.0186, "ay":-1.7599, "alpha":0.0, "fx":[153.40381,153.40381,153.40381,153.40381], "fy":[-29.93532,-29.93532,-29.93532,-29.93532]}, + {"t":2.72722, "x":6.05546, "y":6.01648, "heading":-1.5708, "vx":-0.50296, "vy":-3.47097, "omega":0.0, "ax":9.07069, "ay":-0.84254, "alpha":0.0, "fx":[154.28979,154.28979,154.28979,154.28979], "fy":[-14.3314,-14.3314,-14.3314,-14.3314]}, + {"t":2.76752, "x":6.04255, "y":5.8759, "heading":-1.5708, "vx":-0.13738, "vy":-3.50493, "omega":0.0, "ax":3.97476, "ay":-0.06661, "alpha":0.0, "fx":[67.60947,67.60947,67.60947,67.60947], "fy":[-1.13294,-1.13294,-1.13294,-1.13294]}, + {"t":2.80782, "x":6.04024, "y":5.73459, "heading":-1.5708, "vx":0.02282, "vy":-3.50761, "omega":0.0, "ax":-1.02434, "ay":-0.0006, "alpha":0.0, "fx":[-17.42371,-17.42371,-17.42371,-17.42371], "fy":[-0.01021,-0.01021,-0.01021,-0.01021]}, + {"t":2.84813, "x":6.04033, "y":5.59322, "heading":-1.5708, "vx":-0.01846, "vy":-3.50764, "omega":0.0, "ax":-0.50729, "ay":0.00416, "alpha":0.0, "fx":[-8.62884,-8.62884,-8.62884,-8.62884], "fy":[0.0707,0.0707,0.0707,0.0707]}, + {"t":2.88843, "x":6.03918, "y":5.45185, "heading":-1.5708, "vx":-0.03891, "vy":-3.50747, "omega":0.0, "ax":-0.28156, "ay":0.00358, "alpha":0.0, "fx":[-4.78919,-4.78919,-4.78919,-4.78919], "fy":[0.06096,0.06096,0.06096,0.06096]}, + {"t":2.92874, "x":6.03738, "y":5.31049, "heading":-1.5708, "vx":-0.05026, "vy":-3.50733, "omega":0.0, "ax":-0.17942, "ay":0.00276, "alpha":0.0, "fx":[-3.05181,-3.05181,-3.05181,-3.05181], "fy":[0.04693,0.04693,0.04693,0.04693]}, + {"t":2.96904, "x":6.03521, "y":5.16913, "heading":-1.5708, "vx":-0.05749, "vy":-3.50721, "omega":0.0, "ax":-0.12258, "ay":0.0021, "alpha":0.0, "fx":[-2.08505,-2.08505,-2.08505,-2.08505], "fy":[0.03569,0.03569,0.03569,0.03569]}, + {"t":3.00934, "x":6.03279, "y":5.02778, "heading":-1.5708, "vx":-0.06243, "vy":-3.50713, "omega":0.0, "ax":-0.08635, "ay":0.00158, "alpha":0.0, "fx":[-1.46879,-1.46879,-1.46879,-1.46879], "fy":[0.02691,0.02691,0.02691,0.02691]}, + {"t":3.04965, "x":6.0302, "y":4.88643, "heading":-1.5708, "vx":-0.06591, "vy":-3.50707, "omega":0.0, "ax":-0.06164, "ay":0.00118, "alpha":0.0, "fx":[-1.04848,-1.04848,-1.04848,-1.04848], "fy":[0.0201,0.0201,0.0201,0.0201]}, + {"t":3.08995, "x":6.0275, "y":4.74508, "heading":-1.5708, "vx":-0.06839, "vy":-3.50702, "omega":0.0, "ax":-0.04423, "ay":0.00088, "alpha":0.0, "fx":[-0.75235,-0.75235,-0.75235,-0.75235], "fy":[0.01489,0.01489,0.01489,0.01489]}, + {"t":3.13025, "x":6.02471, "y":4.60374, "heading":-1.5708, "vx":-0.07017, "vy":-3.50698, "omega":0.0, "ax":-0.03177, "ay":0.00064, "alpha":0.0, "fx":[-0.54039,-0.54039,-0.54039,-0.54039], "fy":[0.01093,0.01093,0.01093,0.01093]}, + {"t":3.17056, "x":6.02185, "y":4.4624, "heading":-1.5708, "vx":-0.07145, "vy":-3.50696, "omega":0.0, "ax":-0.02289, "ay":0.00047, "alpha":0.0, "fx":[-0.38936,-0.38936,-0.38936,-0.38936], "fy":[0.008,0.008,0.008,0.008]}, + {"t":3.21086, "x":6.01895, "y":4.32105, "heading":-1.5708, "vx":-0.07238, "vy":-3.50694, "omega":0.0, "ax":-0.01755, "ay":0.00079, "alpha":0.0, "fx":[-0.29855,-0.29855,-0.29855,-0.29855], "fy":[0.01347,0.01347,0.01347,0.01347]}, + {"t":3.25117, "x":6.01602, "y":4.17971, "heading":-1.5708, "vx":-0.07308, "vy":-3.50691, "omega":0.0, "ax":0.0278, "ay":2.34463, "alpha":0.0, "fx":[0.47295,0.47295,0.47295,0.47295], "fy":[39.88143,39.88143,39.88143,39.88143]}, + {"t":3.29147, "x":6.0131, "y":4.04027, "heading":-1.5708, "vx":-0.07196, "vy":-3.41241, "omega":0.0, "ax":0.19543, "ay":9.34592, "alpha":0.0, "fx":[3.32425,3.32425,3.32425,3.32425], "fy":[158.97137,158.97137,158.97137,158.97137]}, + {"t":3.33177, "x":6.01036, "y":3.91033, "heading":-1.5708, "vx":-0.06409, "vy":-3.03573, "omega":0.0, "ax":0.19708, "ay":9.39104, "alpha":0.0, "fx":[3.35235,3.35235,3.35235,3.35235], "fy":[159.73898,159.73898,159.73898,159.73898]}, + {"t":3.37208, "x":6.00793, "y":3.79561, "heading":-1.5708, "vx":-0.05614, "vy":-2.65724, "omega":0.0, "ax":0.19787, "ay":9.40592, "alpha":0.0, "fx":[3.36579,3.36579,3.36579,3.36579], "fy":[159.99206,159.99206,159.99206,159.99206]}, + {"t":3.41238, "x":6.00583, "y":3.69615, "heading":-1.5708, "vx":-0.04817, "vy":-2.27815, "omega":0.0, "ax":0.19841, "ay":9.41332, "alpha":0.0, "fx":[3.37486,3.37486,3.37486,3.37486], "fy":[160.11785,160.11785,160.11785,160.11785]}, + {"t":3.45268, "x":6.00405, "y":3.61198, "heading":-1.5708, "vx":-0.04017, "vy":-1.89875, "omega":0.0, "ax":0.19881, "ay":9.41774, "alpha":0.0, "fx":[3.38172,3.38172,3.38172,3.38172], "fy":[160.19304,160.19304,160.19304,160.19304]}, + {"t":3.49299, "x":6.00259, "y":3.5431, "heading":-1.5708, "vx":-0.03216, "vy":-1.51918, "omega":0.0, "ax":0.19913, "ay":9.42068, "alpha":0.0, "fx":[3.38717,3.38717,3.38717,3.38717], "fy":[160.24303,160.24303,160.24303,160.24303]}, + {"t":3.53329, "x":6.00146, "y":3.48952, "heading":-1.5708, "vx":-0.02413, "vy":-1.13949, "omega":0.0, "ax":0.19939, "ay":9.42277, "alpha":0.0, "fx":[3.39163,3.39163,3.39163,3.39163], "fy":[160.27867,160.27867,160.27867,160.27867]}, + {"t":3.5736, "x":6.00065, "y":3.45125, "heading":-1.5708, "vx":-0.0161, "vy":-0.75972, "omega":0.0, "ax":0.19961, "ay":9.42434, "alpha":0.0, "fx":[3.39535,3.39535,3.39535,3.39535], "fy":[160.30535,160.30535,160.30535,160.30535]}, + {"t":3.6139, "x":6.00016, "y":3.42828, "heading":-1.5708, "vx":-0.00805, "vy":-0.37989, "omega":0.0, "ax":0.1998, "ay":9.42556, "alpha":0.0, "fx":[3.3985,3.3985,3.3985,3.3985], "fy":[160.32608,160.32608,160.32608,160.32608]}, + {"t":3.6542, "x":6.0, "y":3.42063, "heading":-1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0,0.0,0.0,0.0]}], "splits":[0] }, "events":[] diff --git a/src/main/deploy/v1_doomspiral/choreo/DEPOT_AND_CLIMB_PATH_2.traj b/src/main/deploy/v1_doomspiral/choreo/DEPOT_AND_CLIMB_PATH_2.traj index 752a5b31..ab261028 100644 --- a/src/main/deploy/v1_doomspiral/choreo/DEPOT_AND_CLIMB_PATH_2.traj +++ b/src/main/deploy/v1_doomspiral/choreo/DEPOT_AND_CLIMB_PATH_2.traj @@ -47,31 +47,31 @@ "differentialTrackWidth":0.5588 }, "sampleType":"Swerve", - "waypoints":[0.0,0.58896], + "waypoints":[0.0,0.58885], "samples":[ - {"t":0.0, "x":3.04, "y":4.86702, "heading":0.95518, "vx":-3.32898, "vy":-0.85624, "omega":0.17759, "ax":-2.36319, "ay":-0.70065, "alpha":-6.11648, "fx":[-17.68654,-41.448,-61.15081,-40.50369], "fy":[-3.50507,14.30735,-20.37166,-38.10211]}, - {"t":0.02677, "x":2.95003, "y":4.84384, "heading":0.95993, "vx":-3.39225, "vy":-0.875, "omega":0.01384, "ax":-0.00151, "ay":-0.02866, "alpha":-0.02206, "fx":[0.05767,-0.02424,-0.10906,-0.02715], "fy":[-0.45553,-0.39818,-0.51931,-0.57666]}, - {"t":0.05354, "x":2.85922, "y":4.82041, "heading":0.9603, "vx":-3.39229, "vy":-0.87576, "omega":0.01325, "ax":-0.00621, "ay":0.02397, "alpha":0.0, "fx":[-0.10561,-0.10563,-0.10565,-0.10563], "fy":[0.4078,0.40782,0.40779,0.40778]}, - {"t":0.08031, "x":2.7684, "y":4.79697, "heading":0.96066, "vx":-3.39245, "vy":-0.87512, "omega":0.01325, "ax":-0.02988, "ay":0.11619, "alpha":0.00023, "fx":[-0.50907,-0.50822,-0.50734,-0.50819], "fy":[1.97594,1.97535,1.97661,1.9772]}, - {"t":0.10708, "x":2.67757, "y":4.77359, "heading":0.96101, "vx":-3.39325, "vy":-0.87201, "omega":0.01326, "ax":-0.10443, "ay":0.41006, "alpha":0.00102, "fx":[-1.78013,-1.77632,-1.77238,-1.77619], "fy":[6.97356,6.97092,6.97653,6.97918]}, - {"t":0.13385, "x":2.58669, "y":4.75039, "heading":0.96137, "vx":-3.39605, "vy":-0.86103, "omega":0.01329, "ax":-0.31675, "ay":1.39583, "alpha":0.07438, "fx":[-5.67551,-5.39541,-5.10027,-5.38032], "fy":[23.63432,23.44807,23.85113,24.03709]}, - {"t":0.16063, "x":2.49566, "y":4.72784, "heading":0.96172, "vx":-3.40453, "vy":-0.82367, "omega":0.01528, "ax":3.53748, "ay":3.61113, "alpha":10.73502, "fx":[11.87613,80.71222,94.96478,53.1328], "fy":[64.32992,8.51641,69.62516,103.22576]}, - {"t":0.1874, "x":2.40579, "y":4.70708, "heading":0.96213, "vx":-3.30983, "vy":-0.72699, "omega":0.30266, "ax":6.50125, "ay":2.00518, "alpha":17.0698, "fx":[75.14922,130.17451,145.23982,91.77419], "fy":[34.15335,-78.76263,56.33932,124.69999]}, - {"t":0.21417, "x":2.31951, "y":4.68834, "heading":0.97023, "vx":-3.13578, "vy":-0.67331, "omega":0.75964, "ax":6.92115, "ay":1.10781, "alpha":17.68827, "fx":[98.44586,130.76912,148.05392,93.63822], "fy":[-21.12475,-85.59785,55.40071,126.696]}, - {"t":0.24094, "x":2.23804, "y":4.67071, "heading":0.99057, "vx":-2.9505, "vy":-0.64365, "omega":1.23317, "ax":7.18858, "ay":0.46277, "alpha":18.10059, "fx":[113.05776,132.35403,148.8782,94.81286], "fy":[-65.15745,-85.8557,55.49187,127.0073]}, - {"t":0.26771, "x":2.16163, "y":4.65364, "heading":1.02358, "vx":-2.75805, "vy":-0.63127, "omega":1.71774, "ax":7.37551, "ay":0.27574, "alpha":18.11839, "fx":[121.60122,134.95147,149.08136,96.18728], "fy":[-80.73782,-83.17497,56.10935,126.56462]}, - {"t":0.29448, "x":2.09044, "y":4.63684, "heading":1.06957, "vx":-2.5606, "vy":-0.62388, "omega":2.20279, "ax":8.76283, "ay":2.25317, "alpha":7.08143, "fx":[156.09167,158.34882,150.80918,130.9632], "fy":[25.88709,-14.86053,51.74075,90.53574]}, - {"t":0.32125, "x":2.02503, "y":4.62095, "heading":1.12854, "vx":-2.32601, "vy":-0.56356, "omega":2.39237, "ax":9.03618, "ay":2.49687, "alpha":0.60972, "fx":[154.03078,154.89831,153.43874,152.44368], "fy":[41.29019,38.01787,43.65036,46.92551]}, - {"t":0.34802, "x":1.966, "y":4.60676, "heading":1.19259, "vx":-2.0841, "vy":-0.49672, "omega":2.40869, "ax":9.01421, "ay":2.4133, "alpha":-3.30689, "fx":[152.26455,146.16896,156.17376,158.70896], "fy":[48.62539,64.47072,32.94516,18.15668]}, - {"t":0.37479, "x":1.91343, "y":4.59432, "heading":1.25707, "vx":-1.84278, "vy":-0.43211, "omega":2.32016, "ax":8.92734, "ay":2.28575, "alpha":-5.94478, "fx":[150.55123,138.56608,158.37738,159.91162], "fy":[54.22027,79.8154,20.4694,1.01481]}, - {"t":0.40156, "x":1.8673, "y":4.58357, "heading":1.31918, "vx":-1.60379, "vy":-0.37092, "omega":2.16101, "ax":8.82717, "ay":2.16658, "alpha":-7.93755, "fx":[148.84787,132.36428,159.60035,159.77803], "fy":[59.01555,89.90245,7.53492,-9.04108]}, - {"t":0.42834, "x":1.82753, "y":4.57442, "heading":1.37703, "vx":-1.36748, "vy":-0.31292, "omega":1.94852, "ax":8.72551, "ay":2.06884, "alpha":-9.54941, "fx":[147.18102,127.27983,159.79126,159.42156], "fy":[63.22863,97.07999,-4.62814,-14.91909]}, - {"t":0.45511, "x":1.79405, "y":4.56679, "heading":1.4292, "vx":-1.13389, "vy":-0.25754, "omega":1.69287, "ax":8.62758, "ay":1.99591, "alpha":-10.87869, "fx":[145.59672,123.07934,159.20582,159.12884], "fy":[66.9098,102.43164,-15.22141,-18.32045]}, - {"t":0.48188, "x":1.76678, "y":4.56061, "heading":1.47452, "vx":-0.90292, "vy":-0.2041, "omega":1.40164, "ax":8.53757, "ay":1.94574, "alpha":-11.96538, "fx":[144.14549,119.60553,158.19557,158.93999], "fy":[70.06228,106.52553,-23.93846,-20.26347]}, - {"t":0.50865, "x":1.74567, "y":4.55584, "heading":1.51204, "vx":-0.67436, "vy":-0.15201, "omega":1.08131, "ax":8.45854, "ay":1.91298, "alpha":-12.8364, "fx":[142.87587,116.75002,157.05476,158.82903], "fy":[72.67673,109.69328,-30.82767,-21.38552]}, - {"t":0.53542, "x":1.73065, "y":4.55246, "heading":1.54099, "vx":-0.44791, "vy":-0.1008, "omega":0.73767, "ax":8.3922, "ay":1.89116, "alpha":-13.51821, "fx":[141.83105,114.43375,155.97143,158.75928], "fy":[74.74233,112.14502,-36.11322,-22.10189]}, - {"t":0.56219, "x":1.72166, "y":4.55043, "heading":1.56074, "vx":-0.22325, "vy":-0.05017, "omega":0.37577, "ax":8.33915, "ay":1.87419, "alpha":-14.03656, "fx":[141.047,112.59761,155.04306,158.69884], "fy":[76.24965,114.02055,-40.06187,-22.69051]}, - {"t":0.58896, "x":1.71867, "y":4.54976, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0,0.0,0.0,0.0]}], + {"t":0.0, "x":3.04, "y":4.86702, "heading":0.95518, "vx":-3.38491, "vy":-0.87412, "omega":0.02612, "ax":-0.27131, "ay":-0.07651, "alpha":-0.48056, "fx":[-2.7963,-4.57549,-6.43207,-4.65549], "fy":[-0.61556,0.64421,-1.98723,-3.24717]}, + {"t":0.02677, "x":2.9493, "y":4.84359, "heading":0.9557, "vx":-3.39217, "vy":-0.87617, "omega":0.01325, "ax":-0.00273, "ay":0.00749, "alpha":-0.00189, "fx":[-0.03928,-0.04628,-0.05359,-0.04659], "fy":[0.13006,0.13501,0.12466,0.11971]}, + {"t":0.05353, "x":2.85851, "y":4.82015, "heading":0.95606, "vx":-3.39225, "vy":-0.87597, "omega":0.0132, "ax":-0.009, "ay":0.03488, "alpha":0.00007, "fx":[-0.15331,-0.15304,-0.15276,-0.15303], "fy":[0.59318,0.59299,0.59339,0.59358]}, + {"t":0.0803, "x":2.76771, "y":4.79671, "heading":0.95641, "vx":-3.39249, "vy":-0.87503, "omega":0.0132, "ax":-0.03156, "ay":0.12274, "alpha":0.00024, "fx":[-0.53766,-0.53678,-0.53587,-0.53675], "fy":[2.08742,2.0868,2.08809,2.08871]}, + {"t":0.10706, "x":2.67689, "y":4.77333, "heading":0.95676, "vx":-3.39333, "vy":-0.87175, "omega":0.01321, "ax":-0.10779, "ay":0.42351, "alpha":0.00105, "fx":[-1.83749,-1.8336,-1.82955,-1.83343], "fy":[7.20234,7.19962,7.20534,7.20807]}, + {"t":0.13383, "x":2.58603, "y":4.75015, "heading":0.95712, "vx":-3.39622, "vy":-0.86041, "omega":0.01324, "ax":-0.32498, "ay":1.43687, "alpha":0.07797, "fx":[-5.83013,-5.5372,-5.22539,-5.51827], "fy":[24.32833,24.1323,24.55319,24.74889]}, + {"t":0.1606, "x":2.49501, "y":4.72764, "heading":0.9575, "vx":-3.40492, "vy":-0.82195, "omega":0.01533, "ax":3.6159, "ay":3.63429, "alpha":10.93745, "fx":[12.09823,82.87914,96.72224,54.32179], "fy":[65.72804,7.34892,69.83818,104.35763]}, + {"t":0.18736, "x":2.40517, "y":4.70694, "heading":0.96183, "vx":-3.30813, "vy":-0.72468, "omega":0.30808, "ax":6.48752, "ay":2.03784, "alpha":17.08236, "fx":[74.27298,130.23804,145.21946,91.67283], "fy":[36.09256,-78.71724,56.4605,124.81626]}, + {"t":0.21413, "x":2.31894, "y":4.68827, "heading":0.97619, "vx":-3.13449, "vy":-0.67013, "omega":0.7653, "ax":6.93517, "ay":1.05091, "alpha":17.76009, "fx":[99.6254,131.13851,147.85917,93.23766], "fy":[-26.34674,-85.06375,55.92447,126.98844]}, + {"t":0.24089, "x":2.23753, "y":4.67071, "heading":1.00304, "vx":-2.94886, "vy":-0.642, "omega":1.24067, "ax":7.19931, "ay":0.4278, "alpha":18.22755, "fx":[113.82234,133.15593,148.54574,94.30909], "fy":[-69.9823,-84.63898,56.36396,127.36446]}, + {"t":0.26766, "x":2.16118, "y":4.65368, "heading":1.04278, "vx":-2.75617, "vy":-0.63055, "omega":1.72855, "ax":7.37079, "ay":0.27477, "alpha":18.32557, "fx":[121.25362,136.01582,148.62857,95.60185], "fy":[-84.10963,-81.45705,57.27913,126.98233]}, + {"t":0.29443, "x":2.09005, "y":4.6369, "heading":1.09561, "vx":-2.55888, "vy":-0.6232, "omega":2.21905, "ax":8.75997, "ay":2.23114, "alpha":7.24865, "fx":[156.51656,158.34975,150.5031,130.64922], "fy":[23.18596,-14.95529,52.61117,90.96251]}, + {"t":0.32119, "x":2.0247, "y":4.62102, "heading":1.1576, "vx":-2.32441, "vy":-0.56348, "omega":2.41307, "ax":9.03902, "ay":2.48537, "alpha":0.63957, "fx":[154.12533,154.9771,153.44583,152.45659], "fy":[40.92327,37.68808,43.61815,46.87208]}, + {"t":0.34796, "x":1.96572, "y":4.60683, "heading":1.22242, "vx":-2.08247, "vy":-0.49696, "omega":2.43018, "ax":9.01576, "ay":2.40545, "alpha":-3.352, "fx":[152.13191,146.23457,156.35417,158.70118], "fy":[49.02978,64.30893,32.08356,18.24174]}, + {"t":0.37472, "x":1.91321, "y":4.59439, "heading":1.28626, "vx":-1.84116, "vy":-0.43257, "omega":2.34046, "ax":8.92602, "ay":2.28127, "alpha":-6.03688, "fx":[150.28017,138.56712,158.55653,159.91207], "fy":[54.96038,79.80192,19.06629,1.38645]}, + {"t":0.40149, "x":1.86713, "y":4.58363, "heading":1.34674, "vx":-1.60224, "vy":-0.37151, "omega":2.17888, "ax":8.82275, "ay":2.16562, "alpha":-8.05758, "fx":[148.49572,132.30654,159.67415,159.81334], "fy":[59.89098,89.97745,5.93013,-8.45229]}, + {"t":0.42826, "x":1.8274, "y":4.57446, "heading":1.40218, "vx":-1.36609, "vy":-0.31355, "omega":1.96321, "ax":8.71866, "ay":2.07146, "alpha":-9.67849, "fx":[146.7912,127.18225,159.74628,159.48785], "fy":[64.12445,97.19948,-6.16293,-14.22145]}, + {"t":0.45502, "x":1.79396, "y":4.56681, "heading":1.45126, "vx":-1.13273, "vy":-0.2581, "omega":1.70416, "ax":8.61945, "ay":2.0014, "alpha":-11.00121, "fx":[145.20563,122.95968,159.08135,159.2107], "fy":[67.75132,102.56849,-16.53421,-17.6125]}, + {"t":0.48179, "x":1.76673, "y":4.56062, "heading":1.49293, "vx":-0.90202, "vy":-0.20453, "omega":1.4097, "ax":8.52928, "ay":1.9526, "alpha":-12.07195, "fx":[143.78474,119.47689,158.03938,159.02143], "fy":[70.79741,106.66449,-24.98336,-19.62558]}, + {"t":0.50855, "x":1.74564, "y":4.55584, "heading":1.52634, "vx":-0.67373, "vy":-0.15227, "omega":1.08658, "ax":8.45093, "ay":1.91935, "alpha":-12.92351, "fx":[142.57285,116.62115,156.90061,158.89713], "fy":[73.26781,109.82635,-31.62299,-20.88082]}, + {"t":0.53532, "x":1.73063, "y":4.55246, "heading":1.55079, "vx":-0.44753, "vy":-0.1009, "omega":0.74067, "ax":8.38581, "ay":1.89521, "alpha":-13.58596, "fx":[141.60891,114.3109,155.83634,158.8045], "fy":[75.16133,112.26762,-36.70255,-21.77879]}, + {"t":0.56209, "x":1.72166, "y":4.55043, "heading":1.56575, "vx":-0.22308, "vy":-0.05017, "omega":0.37703, "ax":8.33434, "ay":1.87437, "alpha":-14.08624, "fx":[140.92463,112.48699,154.93323,158.71412], "fy":[76.47506,114.12828,-40.48851,-22.58514]}, + {"t":0.58885, "x":1.71867, "y":4.54976, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0,0.0,0.0,0.0]}], "splits":[0] }, "events":[] diff --git a/src/main/deploy/v1_doomspiral/choreo/DEPOT_AND_SIDE_WALL_PATH_2.traj b/src/main/deploy/v1_doomspiral/choreo/DEPOT_AND_SIDE_WALL_PATH_2.traj index a2f292d4..63a19bd0 100644 --- a/src/main/deploy/v1_doomspiral/choreo/DEPOT_AND_SIDE_WALL_PATH_2.traj +++ b/src/main/deploy/v1_doomspiral/choreo/DEPOT_AND_SIDE_WALL_PATH_2.traj @@ -55,79 +55,79 @@ "differentialTrackWidth":0.5588 }, "sampleType":"Swerve", - "waypoints":[0.0,1.1564,2.94796], + "waypoints":[0.0,1.15622,2.94777], "samples":[ - {"t":0.0, "x":3.04, "y":4.86702, "heading":0.95518, "vx":0.0, "vy":0.0, "omega":0.0, "ax":-3.18958, "ay":8.24172, "alpha":-10.41375, "fx":[20.97045,-32.83575,-113.04526,-92.10448], "fy":[158.97974,156.9992,113.72411,131.05418]}, - {"t":0.0373, "x":3.03778, "y":4.87275, "heading":0.95518, "vx":-0.11898, "vy":0.30744, "omega":-0.38847, "ax":-3.17071, "ay":8.29398, "alpha":-10.01961, "fx":[18.49499,-33.36437,-111.24211,-89.61972], "fy":[159.26327,156.86869,115.45542,132.72564]}, - {"t":0.07461, "x":3.03113, "y":4.88999, "heading":0.94069, "vx":-0.23726, "vy":0.61683, "omega":-0.76223, "ax":-3.14866, "ay":8.34548, "alpha":-9.67239, "fx":[15.92584,-33.31683,-108.78979,-88.05031], "fy":[159.50897,156.85356,117.72859,133.7255]}, - {"t":0.11191, "x":3.02009, "y":4.91881, "heading":0.91225, "vx":-0.35471, "vy":0.92815, "omega":-1.12304, "ax":-3.12218, "ay":8.40394, "alpha":-9.28884, "fx":[12.74904,-32.90066,-105.47362,-86.80453], "fy":[159.75002,156.90639,120.65672,134.48111]}, - {"t":0.14921, "x":3.00469, "y":4.95928, "heading":0.87036, "vx":-0.47118, "vy":1.24164, "omega":-1.46954, "ax":-3.09086, "ay":8.47978, "alpha":-8.72921, "fx":[8.04692,-32.47657,-100.85701,-85.01178], "fy":[159.99114,156.94299,124.47078,135.54957]}, - {"t":0.18652, "x":2.98496, "y":5.01149, "heading":0.81554, "vx":-0.58648, "vy":1.55796, "omega":-1.79517, "ax":-3.05875, "ay":8.58643, "alpha":-7.72253, "fx":[0.00546,-32.81112,-93.9936,-81.31445], "fy":[160.0927,156.7893,129.62646,137.70224]}, - {"t":0.22382, "x":2.96096, "y":5.07559, "heading":0.74857, "vx":-0.70058, "vy":1.87827, "omega":-2.08325, "ax":-3.04267, "ay":8.73382, "alpha":-5.65778, "fx":[-15.40751,-35.95418,-82.40654,-73.25167], "fy":[159.17811,155.93866,137.10667,142.01533]}, - {"t":0.26112, "x":2.93271, "y":5.15173, "heading":0.67086, "vx":-0.81408, "vy":2.20406, "omega":-2.2943, "ax":-3.08788, "ay":8.85802, "alpha":-0.77999, "fx":[-48.01871,-49.83854,-56.91385,-55.32455], "fy":[152.21137,151.65547,149.12899,149.69371]}, - {"t":0.29843, "x":2.90019, "y":5.24011, "heading":0.58528, "vx":-0.92927, "vy":2.5345, "omega":-2.3234, "ax":-3.19431, "ay":7.55497, "alpha":16.03898, "fx":[-112.3094,-130.24934,38.91931,-13.69748], "fy":[112.6654,89.04789,153.67676,158.64156]}, - {"t":0.33573, "x":2.8633, "y":5.33991, "heading":0.49861, "vx":-1.04843, "vy":2.81632, "omega":-1.72509, "ax":-2.49831, "ay":6.68377, "alpha":21.98185, "fx":[-114.23236,-145.58439,80.85195,8.98269], "fy":[109.10993,53.30369,134.35495,157.98727]}, - {"t":0.37303, "x":2.82245, "y":5.44962, "heading":0.43426, "vx":-1.14162, "vy":3.06565, "omega":-0.9051, "ax":-1.1806, "ay":6.12082, "alpha":23.34232, "fx":[-97.34874,-129.18462,108.8666,37.34009], "fy":[116.39797,48.16683,103.08826,148.80057]}, - {"t":0.41034, "x":2.77905, "y":5.56824, "heading":0.40049, "vx":-1.18566, "vy":3.29397, "omega":-0.03435, "ax":2.41383, "ay":0.97021, "alpha":0.56887, "fx":[38.83385,39.94796,43.27567,42.17685], "fy":[17.11603,14.46599,15.90761,18.52234]}, - {"t":0.44764, "x":2.7365, "y":5.69179, "heading":0.39921, "vx":-1.09562, "vy":3.33017, "omega":-0.01313, "ax":0.41132, "ay":0.1347, "alpha":0.00241, "fx":[6.98694,6.9912,7.00609,7.00183], "fy":[2.29313,2.28302,2.28931,2.29942]}, - {"t":0.48494, "x":2.69591, "y":5.81611, "heading":0.39872, "vx":-1.08028, "vy":3.33519, "omega":-0.01304, "ax":0.08759, "ay":0.02837, "alpha":0.00026, "fx":[1.48892,1.48938,1.491,1.49054], "fy":[0.4827,0.48161,0.48229,0.48338]}, - {"t":0.52224, "x":2.65568, "y":5.94054, "heading":0.39824, "vx":-1.07701, "vy":3.33625, "omega":-0.01303, "ax":0.17446, "ay":0.05621, "alpha":0.0006, "fx":[2.9651,2.96617,2.9699,2.96884], "fy":[0.95668,0.95415,0.95572,0.95825]}, - {"t":0.55955, "x":2.61562, "y":6.06503, "heading":0.39775, "vx":-1.0705, "vy":3.33835, "omega":-0.01301, "ax":1.06907, "ay":0.33625, "alpha":0.00398, "fx":[18.16878,18.17587,18.20023,18.19315], "fy":[5.72288,5.7059,5.71621,5.73319]}, - {"t":0.59685, "x":2.57643, "y":6.1898, "heading":0.39726, "vx":-1.03062, "vy":3.35089, "omega":-0.01286, "ax":4.9729, "ay":1.38272, "alpha":0.01821, "fx":[84.5249,84.56471,84.65036,84.61058], "fy":[23.5525,23.43976,23.48683,23.59946]}, - {"t":0.63415, "x":2.54145, "y":6.31576, "heading":0.39678, "vx":-0.84512, "vy":3.40247, "omega":-0.01218, "ax":8.17045, "ay":1.64639, "alpha":0.015, "fx":[138.94921,138.98519,139.00474,138.96877], "fy":[28.05721,27.89891,27.95204,28.1101]}, - {"t":0.67146, "x":2.51561, "y":6.44383, "heading":0.39633, "vx":-0.54033, "vy":3.46388, "omega":-0.01162, "ax":8.99166, "ay":0.84063, "alpha":-0.37551, "fx":[153.23197,152.75359,152.67634,153.12041], "fy":[13.1967,17.41763,15.48073,11.10066]}, - {"t":0.70876, "x":2.50171, "y":6.57363, "heading":0.3959, "vx":-0.20491, "vy":3.49524, "omega":-0.02563, "ax":8.70429, "ay":-2.12477, "alpha":-6.41137, "fx":[154.56926,157.19398,151.21337,129.25354], "fy":[-33.34109,13.16085,-35.26088,-89.1256]}, - {"t":0.74606, "x":2.50012, "y":6.70253, "heading":0.39494, "vx":0.11978, "vy":3.41598, "omega":-0.2648, "ax":7.36736, "ay":-4.60155, "alpha":-10.75723, "fx":[147.02819,158.91268,109.44194,85.88427], "fy":[-61.42819,-5.23905,-112.73659,-133.68024]}, - {"t":0.78337, "x":2.50971, "y":6.82676, "heading":0.38506, "vx":0.39461, "vy":3.24433, "omega":-0.66608, "ax":6.77232, "ay":-5.53839, "alpha":-11.03995, "fx":[140.36067,157.03245,88.10896,75.27855], "fy":[-76.29537,-27.94742,-131.92436,-140.65842]}, - {"t":0.82067, "x":2.52914, "y":6.94393, "heading":0.36022, "vx":0.64724, "vy":3.03773, "omega":-1.0779, "ax":6.49724, "ay":-6.2666, "alpha":-8.63652, "fx":[130.9156,147.91667,86.86839,76.36445], "fy":[-91.88624,-60.38666,-133.69507,-140.40433]}, - {"t":0.85797, "x":2.55781, "y":7.05288, "heading":0.32001, "vx":0.88961, "vy":2.80397, "omega":-1.40007, "ax":5.85971, "ay":-7.31645, "alpha":-2.58649, "fx":[106.84751,112.23152,91.9009,87.70809], "fy":[-119.121,-113.98753,-130.8862,-133.80844]}, - {"t":0.89528, "x":2.59507, "y":7.15239, "heading":0.26778, "vx":1.10819, "vy":2.53104, "omega":-1.49656, "ax":5.23416, "ay":-7.81444, "alpha":1.24316, "fx":[85.01964,82.9348,92.8714,95.30073], "fy":[-135.64632,-136.95724,-130.43597,-128.64578]}, - {"t":0.93258, "x":2.64005, "y":7.24137, "heading":0.21195, "vx":1.30344, "vy":2.23954, "omega":-1.45018, "ax":4.76368, "ay":-8.05187, "alpha":3.65935, "fx":[68.16589,63.17011,92.29959,100.47981], "fy":[-144.91276,-147.22117,-130.96493,-124.74083]}, - {"t":0.96988, "x":2.69199, "y":7.31931, "heading":0.15786, "vx":1.48114, "vy":1.93917, "omega":-1.31368, "ax":4.41115, "ay":-8.17769, "alpha":5.27252, "fx":[55.65657,49.29195,91.07149,104.10935], "fy":[-150.20907,-152.49182,-131.89724,-121.8023]}, - {"t":1.00719, "x":2.75031, "y":7.38596, "heading":0.10885, "vx":1.64569, "vy":1.63412, "omega":-1.11699, "ax":4.13901, "ay":-8.25128, "alpha":6.40185, "fx":[46.32565,38.97125,89.61122,106.70561], "fy":[-153.38006,-155.49209,-132.94398,-119.59168]}, - {"t":1.04449, "x":2.81458, "y":7.44117, "heading":0.06718, "vx":1.80009, "vy":1.32632, "omega":-0.87818, "ax":3.9227, "ay":-8.29747, "alpha":7.23192, "fx":[39.15206,30.95465,88.16909,108.62042], "fy":[-155.39473,-157.31482,-133.94053,-117.90009]}, - {"t":1.08179, "x":2.88446, "y":7.48488, "heading":0.03443, "vx":1.94642, "vy":1.0168, "omega":-0.60841, "ax":3.74653, "ay":-8.32708, "alpha":7.88073, "fx":[33.34052,24.54837,86.91161,110.10919], "fy":[-156.76784,-158.46417,-134.78649,-116.54643]}, - {"t":1.1191, "x":2.95967, "y":7.51701, "heading":0.01173, "vx":2.08618, "vy":0.70617, "omega":-0.31443, "ax":3.60016, "ay":-8.34488, "alpha":8.42915, "fx":[28.28141,19.34965,85.95818,111.36124], "fy":[-157.77877,-159.20114,-135.41703,-115.37909]}, - {"t":1.1564, "x":3.04, "y":7.53755, "heading":0.0, "vx":2.22048, "vy":0.39488, "omega":0.0, "ax":5.40611, "ay":-7.71988, "alpha":0.0, "fx":[91.95637,91.95637,91.95637,91.95637], "fy":[-131.31289,-131.31289,-131.31289,-131.31289]}, - {"t":1.20234, "x":3.14771, "y":7.54754, "heading":0.0, "vx":2.46882, "vy":0.04025, "omega":0.0, "ax":9.2312, "ay":-1.79683, "alpha":0.0, "fx":[157.02006,157.02006,157.02006,157.02006], "fy":[-30.56358,-30.56358,-30.56358,-30.56358]}, - {"t":1.24827, "x":3.27086, "y":7.5475, "heading":0.0, "vx":2.89288, "vy":-0.04229, "omega":0.0, "ax":9.36794, "ay":-0.20853, "alpha":0.0, "fx":[159.34591,159.34591,159.34591,159.34591], "fy":[-3.54704,-3.54704,-3.54704,-3.54704]}, - {"t":1.29421, "x":3.41363, "y":7.54533, "heading":0.0, "vx":3.32322, "vy":-0.05187, "omega":0.0, "ax":4.01853, "ay":0.1592, "alpha":0.0, "fx":[68.35408,68.35408,68.35408,68.35408], "fy":[2.70803,2.70803,2.70803,2.70803]}, - {"t":1.34015, "x":3.57053, "y":7.54312, "heading":0.0, "vx":3.50782, "vy":-0.04456, "omega":0.0, "ax":0.00388, "ay":0.31923, "alpha":0.0, "fx":[0.06599,0.06599,0.06599,0.06599], "fy":[5.42996,5.42996,5.42996,5.42996]}, - {"t":1.38609, "x":3.73168, "y":7.54141, "heading":0.0, "vx":3.508, "vy":-0.02989, "omega":0.0, "ax":0.00147, "ay":0.20559, "alpha":0.0, "fx":[0.02507,0.02507,0.02507,0.02507], "fy":[3.49697,3.49697,3.49697,3.49697]}, - {"t":1.43202, "x":3.89283, "y":7.54025, "heading":0.0, "vx":3.50807, "vy":-0.02045, "omega":0.0, "ax":0.00067, "ay":0.13497, "alpha":0.0, "fx":[0.01134,0.01134,0.01134,0.01134], "fy":[2.29575,2.29575,2.29575,2.29575]}, - {"t":1.47796, "x":4.05398, "y":7.53946, "heading":0.0, "vx":3.5081, "vy":-0.01425, "omega":0.0, "ax":0.00032, "ay":0.09198, "alpha":0.0, "fx":[0.00541,0.00541,0.00541,0.00541], "fy":[1.56459,1.56459,1.56459,1.56459]}, - {"t":1.5239, "x":4.21514, "y":7.5389, "heading":0.0, "vx":3.50811, "vy":-0.01002, "omega":0.0, "ax":0.00016, "ay":0.064, "alpha":0.0, "fx":[0.00265,0.00265,0.00265,0.00265], "fy":[1.08863,1.08863,1.08863,1.08863]}, - {"t":1.56984, "x":4.37629, "y":7.5385, "heading":0.0, "vx":3.50812, "vy":-0.00708, "omega":0.0, "ax":0.00008, "ay":0.04499, "alpha":0.0, "fx":[0.00132,0.00132,0.00132,0.00132], "fy":[0.7652,0.7652,0.7652,0.7652]}, - {"t":1.61577, "x":4.53744, "y":7.53823, "heading":0.0, "vx":3.50812, "vy":-0.00502, "omega":0.0, "ax":0.00004, "ay":0.03178, "alpha":0.0, "fx":[0.00066,0.00066,0.00066,0.00066], "fy":[0.54052,0.54052,0.54052,0.54052]}, - {"t":1.66171, "x":4.6986, "y":7.53803, "heading":0.0, "vx":3.50812, "vy":-0.00356, "omega":0.0, "ax":0.00002, "ay":0.0225, "alpha":0.0, "fx":[0.00033,0.00033,0.00033,0.00033], "fy":[0.38275,0.38275,0.38275,0.38275]}, - {"t":1.70765, "x":4.85975, "y":7.53789, "heading":0.0, "vx":3.50813, "vy":-0.00252, "omega":0.0, "ax":0.00001, "ay":0.01595, "alpha":0.0, "fx":[0.00017,0.00017,0.00017,0.00017], "fy":[0.27135,0.27135,0.27135,0.27135]}, - {"t":1.75359, "x":5.02091, "y":7.53779, "heading":0.0, "vx":3.50813, "vy":-0.00179, "omega":0.0, "ax":0.0, "ay":0.01132, "alpha":0.0, "fx":[0.00008,0.00008,0.00008,0.00008], "fy":[0.1925,0.1925,0.1925,0.1925]}, - {"t":1.79952, "x":5.18206, "y":7.53772, "heading":0.0, "vx":3.50813, "vy":-0.00127, "omega":0.0, "ax":0.0, "ay":0.00803, "alpha":0.0, "fx":[0.00004,0.00004,0.00004,0.00004], "fy":[0.1366,0.1366,0.1366,0.1366]}, - {"t":1.84546, "x":5.34322, "y":7.53767, "heading":0.0, "vx":3.50813, "vy":-0.0009, "omega":0.0, "ax":0.0, "ay":0.0057, "alpha":0.0, "fx":[0.00002,0.00002,0.00002,0.00002], "fy":[0.09695,0.09695,0.09695,0.09695]}, - {"t":1.8914, "x":5.50437, "y":7.53764, "heading":0.0, "vx":3.50813, "vy":-0.00064, "omega":0.0, "ax":0.0, "ay":0.00405, "alpha":0.0, "fx":[0.00001,0.00001,0.00001,0.00001], "fy":[0.06881,0.06881,0.06881,0.06881]}, - {"t":1.93734, "x":5.66553, "y":7.53761, "heading":0.0, "vx":3.50813, "vy":-0.00045, "omega":0.0, "ax":0.0, "ay":0.00287, "alpha":0.0, "fx":[0.00001,0.00001,0.00001,0.00001], "fy":[0.04884,0.04884,0.04884,0.04884]}, - {"t":1.98328, "x":5.82668, "y":7.53759, "heading":0.0, "vx":3.50813, "vy":-0.00032, "omega":0.0, "ax":0.0, "ay":0.00204, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.03467,0.03467,0.03467,0.03467]}, - {"t":2.02921, "x":5.98784, "y":7.53758, "heading":0.0, "vx":3.50813, "vy":-0.00023, "omega":0.0, "ax":0.0, "ay":0.00145, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.02461,0.02461,0.02461,0.02461]}, - {"t":2.07515, "x":6.14899, "y":7.53757, "heading":0.0, "vx":3.50813, "vy":-0.00016, "omega":0.0, "ax":0.0, "ay":0.00103, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.01747,0.01747,0.01747,0.01747]}, - {"t":2.12109, "x":6.31015, "y":7.53756, "heading":0.0, "vx":3.50813, "vy":-0.00012, "omega":0.0, "ax":0.0, "ay":0.00073, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0124,0.0124,0.0124,0.0124]}, - {"t":2.16703, "x":6.4713, "y":7.53756, "heading":0.0, "vx":3.50813, "vy":-0.00008, "omega":0.0, "ax":0.0, "ay":0.00052, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0088,0.0088,0.0088,0.0088]}, - {"t":2.21296, "x":6.63246, "y":7.53756, "heading":0.0, "vx":3.50813, "vy":-0.00006, "omega":0.0, "ax":0.0, "ay":0.00037, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.00624,0.00624,0.00624,0.00624]}, - {"t":2.2589, "x":6.79361, "y":7.53755, "heading":0.0, "vx":3.50813, "vy":-0.00004, "omega":0.0, "ax":0.0, "ay":0.00026, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.00443,0.00443,0.00443,0.00443]}, - {"t":2.30484, "x":6.95477, "y":7.53755, "heading":0.0, "vx":3.50813, "vy":-0.00003, "omega":0.0, "ax":0.0, "ay":0.00018, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.00314,0.00314,0.00314,0.00314]}, - {"t":2.35078, "x":7.11592, "y":7.53755, "heading":0.0, "vx":3.50813, "vy":-0.00002, "omega":0.0, "ax":0.0, "ay":0.00013, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.00222,0.00222,0.00222,0.00222]}, - {"t":2.39671, "x":7.27708, "y":7.53755, "heading":0.0, "vx":3.50813, "vy":-0.00001, "omega":0.0, "ax":0.0, "ay":0.00009, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.00157,0.00157,0.00157,0.00157]}, - {"t":2.44265, "x":7.43823, "y":7.53755, "heading":0.0, "vx":3.50813, "vy":-0.00001, "omega":0.0, "ax":0.0, "ay":0.00006, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0011,0.0011,0.0011,0.0011]}, - {"t":2.48859, "x":7.59938, "y":7.53755, "heading":0.0, "vx":3.50813, "vy":-0.00001, "omega":0.0, "ax":-0.00011, "ay":0.00005, "alpha":0.0, "fx":[-0.00182,-0.00182,-0.00182,-0.00182], "fy":[0.0008,0.0008,0.0008,0.0008]}, - {"t":2.53453, "x":7.76054, "y":7.53755, "heading":0.0, "vx":3.50812, "vy":-0.00001, "omega":0.0, "ax":-1.05564, "ay":0.00006, "alpha":0.0, "fx":[-17.9562,-17.9562,-17.9562,-17.9562], "fy":[0.00094,0.00094,0.00094,0.00094]}, - {"t":2.58046, "x":7.92058, "y":7.53755, "heading":0.0, "vx":3.45963, "vy":0.0, "omega":0.0, "ax":-9.36677, "ay":0.00001, "alpha":0.0, "fx":[-159.326,-159.326,-159.326,-159.326], "fy":[0.00021,0.00021,0.00021,0.00021]}, - {"t":2.6264, "x":8.06962, "y":7.53755, "heading":0.0, "vx":3.02934, "vy":0.0, "omega":0.0, "ax":-9.40296, "ay":0.00001, "alpha":0.0, "fx":[-159.94165,-159.94165,-159.94165,-159.94165], "fy":[0.00017,0.00017,0.00017,0.00017]}, - {"t":2.67234, "x":8.19886, "y":7.53755, "heading":0.0, "vx":2.59739, "vy":0.0, "omega":0.0, "ax":-9.41465, "ay":0.00001, "alpha":0.0, "fx":[-160.14055,-160.14055,-160.14055,-160.14055], "fy":[0.00015,0.00015,0.00015,0.00015]}, - {"t":2.71828, "x":8.30825, "y":7.53755, "heading":0.0, "vx":2.16491, "vy":0.0, "omega":0.0, "ax":-9.42042, "ay":0.00001, "alpha":0.0, "fx":[-160.23872,-160.23872,-160.23872,-160.23872], "fy":[0.00014,0.00014,0.00014,0.00014]}, - {"t":2.76421, "x":8.39776, "y":7.53755, "heading":0.0, "vx":1.73216, "vy":0.0, "omega":0.0, "ax":-9.42386, "ay":0.00001, "alpha":0.0, "fx":[-160.29719,-160.29719,-160.29719,-160.29719], "fy":[0.00012,0.00012,0.00012,0.00012]}, - {"t":2.81015, "x":8.46739, "y":7.53755, "heading":0.0, "vx":1.29925, "vy":0.0, "omega":0.0, "ax":-9.42614, "ay":0.00001, "alpha":0.0, "fx":[-160.33598,-160.33598,-160.33598,-160.33598], "fy":[0.00011,0.00011,0.00011,0.00011]}, - {"t":2.85609, "x":8.51712, "y":7.53755, "heading":0.0, "vx":0.86623, "vy":0.0, "omega":0.0, "ax":-9.42777, "ay":0.00001, "alpha":0.0, "fx":[-160.3636,-160.3636,-160.3636,-160.3636], "fy":[0.0001,0.0001,0.0001,0.0001]}, - {"t":2.90203, "x":8.54697, "y":7.53755, "heading":0.0, "vx":0.43314, "vy":0.0, "omega":0.0, "ax":-9.42898, "ay":0.00001, "alpha":0.0, "fx":[-160.38426,-160.38426,-160.38426,-160.38426], "fy":[0.0001,0.0001,0.0001,0.0001]}, - {"t":2.94796, "x":8.55692, "y":7.53755, "heading":0.0, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0,0.0,0.0,0.0]}], + {"t":0.0, "x":3.04, "y":4.86702, "heading":0.95518, "vx":0.0, "vy":0.0, "omega":0.0, "ax":-3.19671, "ay":8.24167, "alpha":-10.3951, "fx":[20.66382,-32.98439,-113.01813,-92.16159], "fy":[159.01978,156.96796,113.75114,131.01523]}, + {"t":0.0373, "x":3.03778, "y":4.87275, "heading":0.94795, "vx":-0.11923, "vy":0.30739, "omega":-0.38771, "ax":-3.17699, "ay":8.29104, "alpha":-10.05407, "fx":[18.34504,-33.17552,-111.04287,-90.2856], "fy":[159.27995,156.90853,115.64795,132.27672]}, + {"t":0.07459, "x":3.03112, "y":4.88998, "heading":0.92649, "vx":-0.23772, "vy":0.61663, "omega":-0.7627, "ax":-3.15418, "ay":8.34023, "alpha":-9.75275, "fx":[15.89672,-32.83077,-108.45399,-89.21869], "fy":[159.51056,156.95568,118.03983,132.95383]}, + {"t":0.11189, "x":3.02006, "y":4.91878, "heading":0.89126, "vx":-0.35536, "vy":0.92769, "omega":-1.12645, "ax":-3.12699, "ay":8.39694, "alpha":-9.40768, "fx":[12.79492,-32.16294,-105.04297,-88.34558], "fy":[159.74422,157.05879,121.03471,133.48056]}, + {"t":0.14919, "x":3.00463, "y":4.95922, "heading":0.84271, "vx":-0.47199, "vy":1.24088, "omega":-1.47733, "ax":-3.09573, "ay":8.47141, "alpha":-8.87688, "fx":[8.11192,-31.55066,-100.39903,-86.79233], "fy":[159.98471,157.13112,124.84464,134.42469]}, + {"t":0.18649, "x":2.98487, "y":5.0114, "heading":0.78143, "vx":-0.58746, "vy":1.55684, "omega":-1.80842, "ax":-3.0662, "ay":8.5766, "alpha":-7.88679, "fx":[0.03765,-31.80282,-93.64675,-83.20913], "fy":[160.08846,156.99629,129.88299,136.57451]}, + {"t":0.22378, "x":2.96083, "y":5.07543, "heading":0.7085, "vx":-0.70182, "vy":1.87672, "omega":-2.10257, "ax":-3.05799, "ay":8.72169, "alpha":-5.83115, "fx":[-15.3674,-35.06449,-82.50941,-75.12097], "fy":[159.17751,156.14137,137.05245,141.04245]}, + {"t":0.26108, "x":2.93253, "y":5.15149, "heading":0.62602, "vx":-0.81587, "vy":2.20202, "omega":-2.32006, "ax":-3.11816, "ay":8.84576, "alpha":-1.00418, "fx":[-47.3928,-49.44338,-58.49158,-56.82861], "fy":[152.40797,151.79372,148.52532,149.12807]}, + {"t":0.29838, "x":2.89993, "y":5.23977, "heading":0.53879, "vx":-0.93217, "vy":2.53194, "omega":-2.35751, "ax":-3.18506, "ay":7.63942, "alpha":15.58674, "fx":[-109.79742,-128.77865,34.99403,-13.12589], "fy":[115.13515,91.41859,154.55614,158.66748]}, + {"t":0.33568, "x":2.86294, "y":5.33952, "heading":0.4617, "vx":-1.05097, "vy":2.81687, "omega":-1.77617, "ax":-2.42155, "ay":6.61753, "alpha":22.53944, "fx":[-113.63383,-147.01368,84.10555,11.78225], "fy":[109.85836,50.26603,132.29234,157.83213]}, + {"t":0.37297, "x":2.82206, "y":5.44919, "heading":0.41113, "vx":-1.14128, "vy":3.06369, "omega":-0.93551, "ax":-1.29021, "ay":6.09606, "alpha":23.98817, "fx":[-99.51366,-134.79785,109.62374,36.90315], "fy":[116.21027,45.08652,103.70726,149.76467]}, + {"t":0.41027, "x":2.7786, "y":5.56769, "heading":0.39293, "vx":-1.1894, "vy":3.29106, "omega":-0.04081, "ax":2.46116, "ay":1.03285, "alpha":0.75016, "fx":[38.92783,40.39276,44.78627,43.34769], "fy":[18.42129,14.89317,16.74766,20.21185]}, + {"t":0.44757, "x":2.73595, "y":5.69116, "heading":0.39193, "vx":-1.09761, "vy":3.32958, "omega":-0.01283, "ax":0.42371, "ay":0.13902, "alpha":0.00249, "fx":[7.19741,7.20172,7.2171,7.21278], "fy":[2.3667,2.35626,2.36262,2.37306]}, + {"t":0.48487, "x":2.6953, "y":5.81544, "heading":0.39145, "vx":-1.08181, "vy":3.33476, "omega":-0.01274, "ax":0.09002, "ay":0.02919, "alpha":0.00024, "fx":[1.53018,1.5306,1.53208,1.53167], "fy":[0.49667,0.49567,0.49628,0.49729]}, + {"t":0.52216, "x":2.65502, "y":5.93984, "heading":0.39097, "vx":-1.07845, "vy":3.33585, "omega":-0.01273, "ax":0.17782, "ay":0.05737, "alpha":0.00054, "fx":[3.02256,3.0235,3.02686,3.02592], "fy":[0.97623,0.97396,0.97534,0.97761]}, + {"t":0.55946, "x":2.61492, "y":6.0643, "heading":0.3905, "vx":-1.07182, "vy":3.33799, "omega":-0.01271, "ax":1.08775, "ay":0.34239, "alpha":0.00355, "fx":[18.48833,18.49453,18.51629,18.51008], "fy":[5.82706,5.81189,5.82091,5.83608]}, + {"t":0.59676, "x":2.5757, "y":6.18903, "heading":0.39003, "vx":-1.03125, "vy":3.35076, "omega":-0.01258, "ax":5.01833, "ay":1.39473, "alpha":0.01655, "fx":[85.30347,85.33945,85.41724,85.38129], "fy":[23.75477,23.65133,23.69304,23.7964]}, + {"t":0.63405, "x":2.54073, "y":6.31498, "heading":0.38957, "vx":-0.84408, "vy":3.40278, "omega":-0.01196, "ax":8.18244, "ay":1.64554, "alpha":0.01367, "fx":[139.15554,139.18829,139.20625,139.1735], "fy":[28.03922,27.89412,27.9412,28.08611]}, + {"t":0.67135, "x":2.51494, "y":6.44304, "heading":0.38913, "vx":-0.53889, "vy":3.46416, "omega":-0.01145, "ax":8.99443, "ay":0.8412, "alpha":-0.36583, "fx":[153.27346,152.80694,152.72762,153.16237], "fy":[13.20213,17.34294,15.49182,11.19726]}, + {"t":0.70865, "x":2.50109, "y":6.57283, "heading":0.38845, "vx":-0.20342, "vy":3.49553, "omega":-0.0251, "ax":8.72074, "ay":-2.08244, "alpha":-6.32342, "fx":[154.60948,157.20423,151.64762,129.88755], "fy":[-33.17014,13.19891,-33.52532,-88.19022]}, + {"t":0.74595, "x":2.49957, "y":6.70175, "heading":0.38312, "vx":0.12184, "vy":3.41786, "omega":-0.26094, "ax":7.38844, "ay":-4.55581, "alpha":-10.7301, "fx":[147.14243,158.96696,111.10973,85.48183], "fy":[-61.17191,-3.89753,-110.97699,-133.92599]}, + {"t":0.78324, "x":2.50926, "y":6.82606, "heading":0.36592, "vx":0.3974, "vy":3.24794, "omega":-0.66115, "ax":6.78863, "ay":-5.5002, "alpha":-11.04548, "fx":[140.44399,157.35219,89.64328,74.45113], "fy":[-76.15378,-26.1674,-130.81666,-141.08914]}, + {"t":0.82054, "x":2.5288, "y":6.94337, "heading":0.33358, "vx":0.6506, "vy":3.0428, "omega":-1.07311, "ax":6.4984, "ay":-6.30642, "alpha":-8.242, "fx":[129.7119,146.87109,88.87865,76.68214], "fy":[-93.57535,-62.90332,-132.37785,-140.22514]}, + {"t":0.85784, "x":2.55758, "y":7.05248, "heading":0.28782, "vx":0.89298, "vy":2.80759, "omega":-1.38052, "ax":5.83581, "ay":-7.34174, "alpha":-2.36915, "fx":[105.69704,110.97594,92.37503,88.01359], "fy":[-120.14243,-115.21609,-130.558,-133.60696]}, + {"t":0.89514, "x":2.59495, "y":7.15208, "heading":0.23468, "vx":1.11064, "vy":2.53376, "omega":-1.46888, "ax":5.2131, "ay":-7.82757, "alpha":1.30773, "fx":[84.56824,82.12257,92.60047,95.40222], "fy":[-135.92794,-137.447,-130.63156,-128.57258]}, + {"t":0.93243, "x":2.64, "y":7.24114, "heading":0.18081, "vx":1.30507, "vy":2.24181, "omega":-1.42011, "ax":4.74754, "ay":-8.06198, "alpha":3.62605, "fx":[68.37019,62.63641,91.66177,100.34854], "fy":[-144.81607,-147.4486,-131.41385,-124.84944]}, + {"t":0.96973, "x":2.69198, "y":7.31915, "heading":0.13036, "vx":1.48214, "vy":1.94112, "omega":-1.28487, "ax":4.39964, "ay":-8.18789, "alpha":5.17355, "fx":[56.3374,48.97343,90.27453,103.76128], "fy":[-149.95467,-152.5935,-132.44487,-122.10185]}, + {"t":1.00703, "x":2.75032, "y":7.38585, "heading":0.08604, "vx":1.64624, "vy":1.63573, "omega":-1.09191, "ax":4.13199, "ay":-8.26229, "alpha":6.26023, "fx":[47.30835,38.86292,88.78411,106.18066], "fy":[-153.07969,-155.51823,-133.49814,-120.06038]}, + {"t":1.04433, "x":2.81459, "y":7.44111, "heading":0.04967, "vx":1.80035, "vy":1.32757, "omega":-0.85842, "ax":3.9201, "ay":-8.30915, "alpha":7.06525, "fx":[40.29252,31.06673,87.39624,107.96352], "fy":[-155.10319,-157.29187,-134.44608,-118.50363]}, + {"t":1.08162, "x":2.88447, "y":7.48485, "heading":0.02257, "vx":1.94656, "vy":1.01766, "omega":-0.5949, "ax":3.74827, "ay":-8.33911, "alpha":7.70222, "fx":[34.52669,24.89264,86.2491,109.35973], "fy":[-156.51159,-158.40978,-135.21113,-117.25101]}, + {"t":1.11892, "x":2.95967, "y":7.51701, "heading":0.00574, "vx":2.08636, "vy":0.70664, "omega":-0.30763, "ax":3.60619, "ay":-8.35696, "alpha":8.24801, "fx":[29.43243,19.93717,85.44284,110.5485], "fy":[-157.56932,-159.12815,-135.74233,-116.15846]}, + {"t":1.15622, "x":3.04, "y":7.53755, "heading":0.0, "vx":2.22086, "vy":0.39494, "omega":0.0, "ax":5.40229, "ay":-7.72256, "alpha":0.0, "fx":[91.89144,91.89144,91.89144,91.89144], "fy":[-131.35852,-131.35852,-131.35852,-131.35852]}, + {"t":1.20215, "x":3.14772, "y":7.54754, "heading":0.0, "vx":2.46903, "vy":0.04019, "omega":0.0, "ax":9.23167, "ay":-1.79438, "alpha":0.0, "fx":[157.02814,157.02814,157.02814,157.02814], "fy":[-30.52183,-30.52183,-30.52183,-30.52183]}, + {"t":1.24809, "x":3.27088, "y":7.5475, "heading":0.0, "vx":2.8931, "vy":-0.04224, "omega":0.0, "ax":9.36793, "ay":-0.20874, "alpha":0.0, "fx":[159.34578,159.34578,159.34578,159.34578], "fy":[-3.55066,-3.55066,-3.55066,-3.55066]}, + {"t":1.29403, "x":3.41367, "y":7.54534, "heading":0.0, "vx":3.32344, "vy":-0.05183, "omega":0.0, "ax":4.01374, "ay":0.15781, "alpha":0.0, "fx":[68.27257,68.27257,68.27257,68.27257], "fy":[2.6843,2.6843,2.6843,2.6843]}, + {"t":1.33997, "x":3.57057, "y":7.54312, "heading":0.0, "vx":3.50782, "vy":-0.04458, "omega":0.0, "ax":0.00388, "ay":0.3193, "alpha":0.0, "fx":[0.06602,0.06602,0.06602,0.06602], "fy":[5.43119,5.43119,5.43119,5.43119]}, + {"t":1.3859, "x":3.73171, "y":7.54141, "heading":0.0, "vx":3.508, "vy":-0.02991, "omega":0.0, "ax":0.00148, "ay":0.20571, "alpha":0.0, "fx":[0.0251,0.0251,0.0251,0.0251], "fy":[3.49902,3.49902,3.49902,3.49902]}, + {"t":1.43184, "x":3.89286, "y":7.54025, "heading":0.0, "vx":3.50807, "vy":-0.02046, "omega":0.0, "ax":0.00067, "ay":0.13504, "alpha":0.0, "fx":[0.01136,0.01136,0.01136,0.01136], "fy":[2.29706,2.29706,2.29706,2.29706]}, + {"t":1.47778, "x":4.05401, "y":7.53946, "heading":0.0, "vx":3.5081, "vy":-0.01426, "omega":0.0, "ax":0.00032, "ay":0.09203, "alpha":0.0, "fx":[0.00541,0.00541,0.00541,0.00541], "fy":[1.56543,1.56543,1.56543,1.56543]}, + {"t":1.52372, "x":4.21517, "y":7.5389, "heading":0.0, "vx":3.50811, "vy":-0.01003, "omega":0.0, "ax":0.00016, "ay":0.06403, "alpha":0.0, "fx":[0.00265,0.00265,0.00265,0.00265], "fy":[1.08919,1.08919,1.08919,1.08919]}, + {"t":1.56965, "x":4.37632, "y":7.53851, "heading":0.0, "vx":3.50812, "vy":-0.00709, "omega":0.0, "ax":0.00008, "ay":0.04501, "alpha":0.0, "fx":[0.00132,0.00132,0.00132,0.00132], "fy":[0.76559,0.76559,0.76559,0.76559]}, + {"t":1.61559, "x":4.53747, "y":7.53823, "heading":0.0, "vx":3.50812, "vy":-0.00502, "omega":0.0, "ax":0.00004, "ay":0.03179, "alpha":0.0, "fx":[0.00066,0.00066,0.00066,0.00066], "fy":[0.5408,0.5408,0.5408,0.5408]}, + {"t":1.66153, "x":4.69863, "y":7.53803, "heading":0.0, "vx":3.50812, "vy":-0.00356, "omega":0.0, "ax":0.00002, "ay":0.02251, "alpha":0.0, "fx":[0.00033,0.00033,0.00033,0.00033], "fy":[0.38294,0.38294,0.38294,0.38294]}, + {"t":1.70746, "x":4.85978, "y":7.53789, "heading":0.0, "vx":3.50813, "vy":-0.00253, "omega":0.0, "ax":0.00001, "ay":0.01596, "alpha":0.0, "fx":[0.00017,0.00017,0.00017,0.00017], "fy":[0.27149,0.27149,0.27149,0.27149]}, + {"t":1.7534, "x":5.02094, "y":7.53779, "heading":0.0, "vx":3.50813, "vy":-0.00179, "omega":0.0, "ax":0.0, "ay":0.01132, "alpha":0.0, "fx":[0.00008,0.00008,0.00008,0.00008], "fy":[0.19259,0.19259,0.19259,0.19259]}, + {"t":1.79934, "x":5.18209, "y":7.53772, "heading":0.0, "vx":3.50813, "vy":-0.00127, "omega":0.0, "ax":0.0, "ay":0.00803, "alpha":0.0, "fx":[0.00004,0.00004,0.00004,0.00004], "fy":[0.13667,0.13667,0.13667,0.13667]}, + {"t":1.84528, "x":5.34324, "y":7.53767, "heading":0.0, "vx":3.50813, "vy":-0.0009, "omega":0.0, "ax":0.0, "ay":0.0057, "alpha":0.0, "fx":[0.00002,0.00002,0.00002,0.00002], "fy":[0.097,0.097,0.097,0.097]}, + {"t":1.89121, "x":5.5044, "y":7.53764, "heading":0.0, "vx":3.50813, "vy":-0.00064, "omega":0.0, "ax":0.0, "ay":0.00405, "alpha":0.0, "fx":[0.00001,0.00001,0.00001,0.00001], "fy":[0.06884,0.06884,0.06884,0.06884]}, + {"t":1.93715, "x":5.66555, "y":7.53761, "heading":0.0, "vx":3.50813, "vy":-0.00045, "omega":0.0, "ax":0.0, "ay":0.00287, "alpha":0.0, "fx":[0.00001,0.00001,0.00001,0.00001], "fy":[0.04887,0.04887,0.04887,0.04887]}, + {"t":1.98309, "x":5.8267, "y":7.53759, "heading":0.0, "vx":3.50813, "vy":-0.00032, "omega":0.0, "ax":0.0, "ay":0.00204, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.03469,0.03469,0.03469,0.03469]}, + {"t":2.02903, "x":5.98786, "y":7.53758, "heading":0.0, "vx":3.50813, "vy":-0.00023, "omega":0.0, "ax":0.0, "ay":0.00145, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.02462,0.02462,0.02462,0.02462]}, + {"t":2.07496, "x":6.14901, "y":7.53757, "heading":0.0, "vx":3.50813, "vy":-0.00016, "omega":0.0, "ax":0.0, "ay":0.00103, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.01747,0.01747,0.01747,0.01747]}, + {"t":2.1209, "x":6.31017, "y":7.53756, "heading":0.0, "vx":3.50813, "vy":-0.00012, "omega":0.0, "ax":0.0, "ay":0.00073, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0124,0.0124,0.0124,0.0124]}, + {"t":2.16684, "x":6.47132, "y":7.53756, "heading":0.0, "vx":3.50813, "vy":-0.00008, "omega":0.0, "ax":0.0, "ay":0.00052, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0088,0.0088,0.0088,0.0088]}, + {"t":2.21277, "x":6.63247, "y":7.53756, "heading":0.0, "vx":3.50813, "vy":-0.00006, "omega":0.0, "ax":0.0, "ay":0.00037, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.00625,0.00625,0.00625,0.00625]}, + {"t":2.25871, "x":6.79363, "y":7.53755, "heading":0.0, "vx":3.50813, "vy":-0.00004, "omega":0.0, "ax":0.0, "ay":0.00026, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.00443,0.00443,0.00443,0.00443]}, + {"t":2.30465, "x":6.95478, "y":7.53755, "heading":0.0, "vx":3.50813, "vy":-0.00003, "omega":0.0, "ax":0.0, "ay":0.00018, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.00314,0.00314,0.00314,0.00314]}, + {"t":2.35059, "x":7.11593, "y":7.53755, "heading":0.0, "vx":3.50813, "vy":-0.00002, "omega":0.0, "ax":0.0, "ay":0.00013, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.00222,0.00222,0.00222,0.00222]}, + {"t":2.39652, "x":7.27709, "y":7.53755, "heading":0.0, "vx":3.50813, "vy":-0.00001, "omega":0.0, "ax":0.0, "ay":0.00009, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.00157,0.00157,0.00157,0.00157]}, + {"t":2.44246, "x":7.43824, "y":7.53755, "heading":0.0, "vx":3.50813, "vy":-0.00001, "omega":0.0, "ax":0.0, "ay":0.00006, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0011,0.0011,0.0011,0.0011]}, + {"t":2.4884, "x":7.59939, "y":7.53755, "heading":0.0, "vx":3.50813, "vy":-0.00001, "omega":0.0, "ax":-0.00011, "ay":0.00005, "alpha":0.0, "fx":[-0.00183,-0.00183,-0.00183,-0.00183], "fy":[0.0008,0.0008,0.0008,0.0008]}, + {"t":2.53434, "x":7.76055, "y":7.53755, "heading":0.0, "vx":3.50812, "vy":-0.00001, "omega":0.0, "ax":-1.05612, "ay":0.00006, "alpha":0.0, "fx":[-17.96423,-17.96423,-17.96423,-17.96423], "fy":[0.00094,0.00094,0.00094,0.00094]}, + {"t":2.58027, "x":7.92059, "y":7.53755, "heading":0.0, "vx":3.45961, "vy":0.0, "omega":0.0, "ax":-9.36677, "ay":0.00001, "alpha":0.0, "fx":[-159.32602,-159.32602,-159.32602,-159.32602], "fy":[0.00021,0.00021,0.00021,0.00021]}, + {"t":2.62621, "x":8.06963, "y":7.53755, "heading":0.0, "vx":3.02932, "vy":0.0, "omega":0.0, "ax":-9.40296, "ay":0.00001, "alpha":0.0, "fx":[-159.94165,-159.94165,-159.94165,-159.94165], "fy":[0.00017,0.00017,0.00017,0.00017]}, + {"t":2.67215, "x":8.19887, "y":7.53755, "heading":0.0, "vx":2.59738, "vy":0.0, "omega":0.0, "ax":-9.41465, "ay":0.00001, "alpha":0.0, "fx":[-160.14055,-160.14055,-160.14055,-160.14055], "fy":[0.00015,0.00015,0.00015,0.00015]}, + {"t":2.71808, "x":8.30825, "y":7.53755, "heading":0.0, "vx":2.16489, "vy":0.0, "omega":0.0, "ax":-9.42042, "ay":0.00001, "alpha":0.0, "fx":[-160.23872,-160.23872,-160.23872,-160.23872], "fy":[0.00014,0.00014,0.00014,0.00014]}, + {"t":2.76402, "x":8.39776, "y":7.53755, "heading":0.0, "vx":1.73214, "vy":0.0, "omega":0.0, "ax":-9.42386, "ay":0.00001, "alpha":0.0, "fx":[-160.29719,-160.29719,-160.29719,-160.29719], "fy":[0.00012,0.00012,0.00012,0.00012]}, + {"t":2.80996, "x":8.46739, "y":7.53755, "heading":0.0, "vx":1.29924, "vy":0.0, "omega":0.0, "ax":-9.42614, "ay":0.00001, "alpha":0.0, "fx":[-160.33598,-160.33598,-160.33598,-160.33598], "fy":[0.00011,0.00011,0.00011,0.00011]}, + {"t":2.8559, "x":8.51712, "y":7.53755, "heading":0.0, "vx":0.86623, "vy":0.0, "omega":0.0, "ax":-9.42777, "ay":0.00001, "alpha":0.0, "fx":[-160.3636,-160.3636,-160.3636,-160.3636], "fy":[0.0001,0.0001,0.0001,0.0001]}, + {"t":2.90183, "x":8.54697, "y":7.53755, "heading":0.0, "vx":0.43314, "vy":0.0, "omega":0.0, "ax":-9.42898, "ay":0.00001, "alpha":0.0, "fx":[-160.38426,-160.38426,-160.38426,-160.38426], "fy":[0.0001,0.0001,0.0001,0.0001]}, + {"t":2.94777, "x":8.55692, "y":7.53755, "heading":0.0, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0,0.0,0.0,0.0]}], "splits":[0] }, "events":[] diff --git a/src/main/deploy/v1_doomspiral/choreo/DEPOT_AND_XXX_PATH_1.traj b/src/main/deploy/v1_doomspiral/choreo/DEPOT_AND_XXX_PATH_1.traj index 805e4ff0..8661153b 100644 --- a/src/main/deploy/v1_doomspiral/choreo/DEPOT_AND_XXX_PATH_1.traj +++ b/src/main/deploy/v1_doomspiral/choreo/DEPOT_AND_XXX_PATH_1.traj @@ -149,7 +149,7 @@ {"t":3.70699, "x":1.44872, "y":5.94651, "heading":3.14159, "vx":0.90135, "vy":-0.43279, "omega":0.0, "ax":7.50141, "ay":-5.693, "alpha":0.0, "fx":[127.5969,127.5969,127.5969,127.5969], "fy":[-96.83634,-96.83634,-96.83634,-96.83634]}, {"t":3.7508, "x":1.49541, "y":5.92209, "heading":3.14159, "vx":1.22999, "vy":-0.6822, "omega":0.0, "ax":7.46676, "ay":-5.70521, "alpha":0.0, "fx":[127.00749,127.00749,127.00749,127.00749], "fy":[-97.04391,-97.04391,-97.04391,-97.04391]}, {"t":3.79461, "x":1.55646, "y":5.88673, "heading":3.14159, "vx":1.55711, "vy":-0.93214, "omega":0.0, "ax":1.07865, "ay":-5.9636, "alpha":0.0, "fx":[18.3476,18.3476,18.3476,18.3476], "fy":[-101.43908,-101.43908,-101.43908,-101.43908]}, - {"t":3.83842, "x":1.62571, "y":5.84017, "heading":3.14159, "vx":1.60436, "vy":-1.19341, "omega":0.0, "ax":-0.16292, "ay":-0.21779, "alpha":0.0, "fx":[-2.7713,-2.7713,-2.7713,-2.7713], "fy":[-3.70452,-3.70452,-3.70452,-3.70452]}, + {"t":3.83842, "x":1.62571, "y":5.84017, "heading":3.14159, "vx":1.60436, "vy":-1.19341, "omega":0.0, "ax":-0.16292, "ay":-0.21779, "alpha":0.0, "fx":[-2.7713,-2.7713,-2.7713,-2.7713], "fy":[-3.70453,-3.70453,-3.70453,-3.70453]}, {"t":3.88223, "x":1.69584, "y":5.78767, "heading":3.14159, "vx":1.59722, "vy":-1.20295, "omega":0.0, "ax":-0.00632, "ay":-0.00839, "alpha":0.0, "fx":[-0.10753,-0.10753,-0.10753,-0.10753], "fy":[-0.14274,-0.14274,-0.14274,-0.14274]}, {"t":3.92604, "x":1.76581, "y":5.73496, "heading":3.14159, "vx":1.59695, "vy":-1.20332, "omega":0.0, "ax":-0.00024, "ay":-0.00032, "alpha":0.0, "fx":[-0.00415,-0.00415,-0.00415,-0.00415], "fy":[-0.00551,-0.00551,-0.00551,-0.00551]}, {"t":3.96985, "x":1.83577, "y":5.68225, "heading":3.14159, "vx":1.59694, "vy":-1.20333, "omega":0.0, "ax":-0.00001, "ay":-0.00001, "alpha":0.0, "fx":[-0.00016,-0.00016,-0.00016,-0.00016], "fy":[-0.00021,-0.00021,-0.00021,-0.00021]}, diff --git a/src/main/deploy/v1_doomspiral/choreo/FAIL_PATH.traj b/src/main/deploy/v1_doomspiral/choreo/FAIL_PATH.traj deleted file mode 100644 index 074f0a05..00000000 --- a/src/main/deploy/v1_doomspiral/choreo/FAIL_PATH.traj +++ /dev/null @@ -1,147 +0,0 @@ -{ - "name":"FAIL_PATH", - "version":3, - "snapshot":{ - "waypoints":[ - {"x":3.928042411804199, "y":0.3954239785671234, "heading":1.5707963267948966, "intervals":30, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, - {"x":7.795207023620605, "y":0.9033589959144592, "heading":1.5707963267948966, "intervals":32, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, - {"x":7.795207023620605, "y":3.0137579441070557, "heading":-1.5707963267948966, "intervals":15, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":7.570225238800049, "y":1.831631541252136, "heading":1.5707963267948966, "intervals":26, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], - "constraints":[ - {"from":"first", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, - {"from":"last", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, - {"from":"first", "to":"last", "data":{"type":"KeepInRectangle", "props":{"x":0.0, "y":0.0, "w":16.541, "h":8.0692}}, "enabled":true}, - {"from":1, "to":2, "data":{"type":"KeepInLane", "props":{"tolerance":0.00001}}, "enabled":true}, - {"from":1, "to":2, "data":{"type":"MaxAngularVelocity", "props":{"max":0.0}}, "enabled":true}, - {"from":3, "to":3, "data":{"type":"MaxAngularVelocity", "props":{"max":0.0}}, "enabled":true}, - {"from":1, "to":2, "data":{"type":"MaxVelocity", "props":{"max":1.5}}, "enabled":true}], - "targetDt":0.05 - }, - "params":{ - "waypoints":[ - {"x":{"exp":"3.928042411804199 m", "val":3.928042411804199}, "y":{"exp":"0.3954239785671234 m", "val":0.3954239785671234}, "heading":{"exp":"90 deg", "val":1.5707963267948966}, "intervals":30, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, - {"x":{"exp":"7.7952070236206055 m", "val":7.795207023620605}, "y":{"exp":"0.9033589959144592 m", "val":0.9033589959144592}, "heading":{"exp":"1.5707963267948966 rad", "val":1.5707963267948966}, "intervals":32, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, - {"x":{"exp":"7.7952070236206055 m", "val":7.795207023620605}, "y":{"exp":"3.0137579441070557 m", "val":3.0137579441070557}, "heading":{"exp":"-1.5707963267948966 rad", "val":-1.5707963267948966}, "intervals":15, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":{"exp":"7.570225238800049 m", "val":7.570225238800049}, "y":{"exp":"1.8316315412521362 m", "val":1.831631541252136}, "heading":{"exp":"90 deg", "val":1.5707963267948966}, "intervals":26, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], - "constraints":[ - {"from":"first", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, - {"from":"last", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, - {"from":"first", "to":"last", "data":{"type":"KeepInRectangle", "props":{"x":{"exp":"0 m", "val":0.0}, "y":{"exp":"0 m", "val":0.0}, "w":{"exp":"16.541 m", "val":16.541}, "h":{"exp":"8.0692 m", "val":8.0692}}}, "enabled":true}, - {"from":1, "to":2, "data":{"type":"KeepInLane", "props":{"tolerance":{"exp":"1e-5 m", "val":0.00001}}}, "enabled":true}, - {"from":1, "to":2, "data":{"type":"MaxAngularVelocity", "props":{"max":{"exp":"0 rad / s", "val":0.0}}}, "enabled":true}, - {"from":3, "to":3, "data":{"type":"MaxAngularVelocity", "props":{"max":{"exp":"0 rad / s", "val":0.0}}}, "enabled":true}, - {"from":1, "to":2, "data":{"type":"MaxVelocity", "props":{"max":{"exp":"1.5 m / s", "val":1.5}}}, "enabled":true}], - "targetDt":{ - "exp":"0.05 s", - "val":0.05 - } - }, - "trajectory":{ - "config":{ - "frontLeft":{ - "x":0.225425, - "y":0.333375 - }, - "backLeft":{ - "x":-0.225425, - "y":0.333375 - }, - "mass":68.0388555, - "inertia":6.5136193464, - "gearing":7.03, - "radius":0.0508, - "vmax":485.8996637552213, - "tmax":1.16, - "cof":2.255, - "bumper":{ - "front":0.34925, - "side":0.4445, - "back":0.34925 - }, - "differentialTrackWidth":0.5588 - }, - "sampleType":"Swerve", - "waypoints":[0.0,1.47237,2.94855,3.67918], - "samples":[ - {"t":0.0, "x":3.92804, "y":0.39542, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":9.42894, "ay":0.05469, "alpha":0.0, "fx":[160.38362,160.38362,160.38362,160.38362], "fy":[0.93021,0.93021,0.93021,0.93021]}, - {"t":0.04908, "x":3.9394, "y":0.39549, "heading":1.5708, "vx":0.46276, "vy":0.00268, "omega":0.0, "ax":9.4276, "ay":0.04677, "alpha":0.0, "fx":[160.36079,160.36079,160.36079,160.36079], "fy":[0.79554,0.79554,0.79554,0.79554]}, - {"t":0.09816, "x":3.97346, "y":0.39568, "heading":1.5708, "vx":0.92546, "vy":0.00498, "omega":0.0, "ax":9.4257, "ay":0.03884, "alpha":0.0, "fx":[160.32843,160.32843,160.32843,160.32843], "fy":[0.66061,0.66061,0.66061,0.66061]}, - {"t":0.14724, "x":4.03024, "y":0.39597, "heading":1.5708, "vx":1.38807, "vy":0.00689, "omega":0.0, "ax":9.42282, "ay":0.03086, "alpha":0.0, "fx":[160.2795,160.2795,160.2795,160.2795], "fy":[0.52495,0.52495,0.52495,0.52495]}, - {"t":0.19632, "x":4.10971, "y":0.39634, "heading":1.5708, "vx":1.85053, "vy":0.0084, "omega":0.0, "ax":9.418, "ay":0.02277, "alpha":0.0, "fx":[160.19756,160.19756,160.19756,160.19756], "fy":[0.38729,0.38729,0.38729,0.38729]}, - {"t":0.2454, "x":4.21188, "y":0.39678, "heading":1.5708, "vx":2.31276, "vy":0.00952, "omega":0.0, "ax":9.40836, "ay":0.01433, "alpha":0.0, "fx":[160.03346,160.03346,160.03346,160.03346], "fy":[0.24373,0.24373,0.24373,0.24373]}, - {"t":0.29447, "x":4.33672, "y":0.39727, "heading":1.5708, "vx":2.77451, "vy":0.01022, "omega":0.0, "ax":9.37955, "ay":0.00441, "alpha":0.0, "fx":[159.54353,159.54353,159.54353,159.54353], "fy":[0.07502,0.07502,0.07502,0.07502]}, - {"t":0.34355, "x":4.48418, "y":0.39778, "heading":1.5708, "vx":3.23485, "vy":0.01044, "omega":0.0, "ax":5.5718, "ay":-0.236, "alpha":0.0, "fx":[94.77478,94.77478,94.77478,94.77478], "fy":[-4.01438,-4.01438,-4.01438,-4.01438]}, - {"t":0.39263, "x":4.64966, "y":0.398, "heading":1.5708, "vx":3.50831, "vy":-0.00115, "omega":0.0, "ax":0.00049, "ay":-0.13368, "alpha":0.0, "fx":[0.00827,0.00827,0.00827,0.00827], "fy":[-2.27389,-2.27389,-2.27389,-2.27389]}, - {"t":0.44171, "x":4.82184, "y":0.39779, "heading":1.5708, "vx":3.50834, "vy":-0.00771, "omega":0.0, "ax":-0.00035, "ay":-0.11658, "alpha":0.0, "fx":[-0.00599,-0.00599,-0.00599,-0.00599], "fy":[-1.98308,-1.98308,-1.98308,-1.98308]}, - {"t":0.49079, "x":4.99403, "y":0.39727, "heading":1.5708, "vx":3.50832, "vy":-0.01343, "omega":0.0, "ax":-0.00054, "ay":-0.11663, "alpha":0.0, "fx":[-0.00922,-0.00922,-0.00922,-0.00922], "fy":[-1.98383,-1.98383,-1.98383,-1.98383]}, - {"t":0.53987, "x":5.16621, "y":0.39647, "heading":1.5708, "vx":3.50829, "vy":-0.01915, "omega":0.0, "ax":-0.00075, "ay":-0.11868, "alpha":0.0, "fx":[-0.0127,-0.0127,-0.0127,-0.0127], "fy":[-2.01864,-2.01864,-2.01864,-2.01864]}, - {"t":0.58895, "x":5.3384, "y":0.39538, "heading":1.5708, "vx":3.50826, "vy":-0.02498, "omega":0.0, "ax":-0.00097, "ay":-0.12162, "alpha":0.0, "fx":[-0.01649,-0.01649,-0.01649,-0.01649], "fy":[-2.06865,-2.06865,-2.06865,-2.06865]}, - {"t":0.63803, "x":5.51058, "y":0.39401, "heading":1.5708, "vx":3.50821, "vy":-0.03095, "omega":0.0, "ax":-0.00122, "ay":-0.1255, "alpha":0.0, "fx":[-0.0207,-0.0207,-0.0207,-0.0207], "fy":[-2.13469,-2.13469,-2.13469,-2.13469]}, - {"t":0.68711, "x":5.68276, "y":0.39234, "heading":1.5708, "vx":3.50815, "vy":-0.03711, "omega":0.0, "ax":-0.0015, "ay":-0.13053, "alpha":0.0, "fx":[-0.0255,-0.0255,-0.0255,-0.0255], "fy":[-2.22021,-2.22021,-2.22021,-2.22021]}, - {"t":0.73619, "x":5.85493, "y":0.39036, "heading":1.5708, "vx":3.50807, "vy":-0.04351, "omega":0.0, "ax":-0.00183, "ay":-0.13699, "alpha":0.0, "fx":[-0.03112,-0.03112,-0.03112,-0.03112], "fy":[-2.33021,-2.33021,-2.33021,-2.33021]}, - {"t":0.78527, "x":6.0271, "y":0.38806, "heading":1.5708, "vx":3.50799, "vy":-0.05023, "omega":0.0, "ax":-0.00222, "ay":-0.14512, "alpha":0.0, "fx":[-0.03784,-0.03784,-0.03784,-0.03784], "fy":[-2.46843,-2.46843,-2.46843,-2.46843]}, - {"t":0.83435, "x":6.19927, "y":0.38542, "heading":1.5708, "vx":3.50788, "vy":-0.05736, "omega":0.0, "ax":-0.00265, "ay":-0.15237, "alpha":0.0, "fx":[-0.04511,-0.04511,-0.04511,-0.04511], "fy":[-2.59181,-2.59181,-2.59181,-2.59181]}, - {"t":0.88342, "x":6.37143, "y":0.38243, "heading":1.5708, "vx":3.50775, "vy":-0.06484, "omega":0.0, "ax":-0.00229, "ay":-0.11849, "alpha":0.0, "fx":[-0.03889,-0.03889,-0.03889,-0.03889], "fy":[-2.0155,-2.0155,-2.0155,-2.0155]}, - {"t":0.9325, "x":6.54358, "y":0.3791, "heading":1.5708, "vx":3.50763, "vy":-0.07065, "omega":0.0, "ax":0.00798, "ay":0.47635, "alpha":0.0, "fx":[0.13579,0.13579,0.13579,0.13579], "fy":[8.10249,8.10249,8.10249,8.10249]}, - {"t":0.98158, "x":6.71574, "y":0.37621, "heading":1.5708, "vx":3.50803, "vy":-0.04727, "omega":0.0, "ax":-0.13561, "ay":5.45877, "alpha":0.0, "fx":[-2.30668,-2.30668,-2.30668,-2.30668], "fy":[92.85208,92.85208,92.85208,92.85208]}, - {"t":1.03066, "x":6.88775, "y":0.38046, "heading":1.5708, "vx":3.50137, "vy":0.22064, "omega":0.0, "ax":-1.11307, "ay":8.80341, "alpha":0.0, "fx":[-18.93302,-18.93302,-18.93302,-18.93302], "fy":[149.74343,149.74343,149.74343,149.74343]}, - {"t":1.07974, "x":7.05826, "y":0.40189, "heading":1.5708, "vx":3.44674, "vy":0.6527, "omega":0.0, "ax":-3.91234, "ay":8.39224, "alpha":0.0, "fx":[-66.54782,-66.54782,-66.54782,-66.54782], "fy":[142.7496,142.7496,142.7496,142.7496]}, - {"t":1.12882, "x":7.22271, "y":0.44403, "heading":1.5708, "vx":3.25473, "vy":1.06459, "omega":0.0, "ax":-8.98977, "ay":2.70254, "alpha":0.0, "fx":[-152.91343,-152.91343,-152.91343,-152.91343], "fy":[45.96935,45.96935,45.96935,45.96935]}, - {"t":1.1779, "x":7.37162, "y":0.49954, "heading":1.5708, "vx":2.81352, "vy":1.19722, "omega":0.0, "ax":-9.27144, "ay":1.60997, "alpha":0.0, "fx":[-157.7045,-157.7045,-157.7045,-157.7045], "fy":[27.38511,27.38511,27.38511,27.38511]}, - {"t":1.22698, "x":7.49854, "y":0.56024, "heading":1.5708, "vx":2.35848, "vy":1.27624, "omega":0.0, "ax":-9.34253, "ay":1.19635, "alpha":0.0, "fx":[-158.91372,-158.91372,-158.91372,-158.91372], "fy":[20.34956,20.34956,20.34956,20.34956]}, - {"t":1.27606, "x":7.60304, "y":0.62431, "heading":1.5708, "vx":1.89996, "vy":1.33496, "omega":0.0, "ax":-9.37211, "ay":0.98091, "alpha":0.0, "fx":[-159.41695,-159.41695,-159.41695,-159.41695], "fy":[16.68499,16.68499,16.68499,16.68499]}, - {"t":1.32514, "x":7.685, "y":0.69101, "heading":1.5708, "vx":1.43998, "vy":1.3831, "omega":0.0, "ax":-9.38772, "ay":0.84925, "alpha":0.0, "fx":[-159.68238,-159.68238,-159.68238,-159.68238], "fy":[14.44551,14.44551,14.44551,14.44551]}, - {"t":1.37422, "x":7.74436, "y":0.75992, "heading":1.5708, "vx":0.97924, "vy":1.42478, "omega":0.0, "ax":-9.39717, "ay":0.76069, "alpha":0.0, "fx":[-159.84313,-159.84313,-159.84313,-159.84313], "fy":[12.93906,12.93906,12.93906,12.93906]}, - {"t":1.42329, "x":7.78111, "y":0.83076, "heading":1.5708, "vx":0.51804, "vy":1.46211, "omega":0.0, "ax":-9.40343, "ay":0.69717, "alpha":0.0, "fx":[-159.94965,-159.94965,-159.94965,-159.94965], "fy":[11.85874,11.85874,11.85874,11.85874]}, - {"t":1.47237, "x":7.79521, "y":0.90336, "heading":1.5708, "vx":0.05653, "vy":1.49633, "omega":0.0, "ax":-2.4423, "ay":0.04969, "alpha":0.0, "fx":[-41.54274,-41.54274,-41.54274,-41.54274], "fy":[0.84523,0.84523,0.84523,0.84523]}, - {"t":1.5185, "x":7.79522, "y":0.97244, "heading":1.5708, "vx":-0.05614, "vy":1.49862, "omega":0.0, "ax":2.41702, "ay":0.00063, "alpha":0.0, "fx":[41.11289,41.11289,41.11289,41.11289], "fy":[0.01065,0.01065,0.01065,0.01065]}, - {"t":1.56463, "x":7.7952, "y":1.04157, "heading":1.5708, "vx":0.05536, "vy":1.49865, "omega":0.0, "ax":-2.38336, "ay":0.00062, "alpha":0.0, "fx":[-40.54025,-40.54025,-40.54025,-40.54025], "fy":[0.01049,0.01049,0.01049,0.01049]}, - {"t":1.61077, "x":7.79522, "y":1.11071, "heading":1.5708, "vx":-0.05458, "vy":1.49868, "omega":0.0, "ax":2.34974, "ay":0.00061, "alpha":0.0, "fx":[39.96835,39.96835,39.96835,39.96835], "fy":[0.01032,0.01032,0.01032,0.01032]}, - {"t":1.6569, "x":7.7952, "y":1.17984, "heading":1.5708, "vx":0.05381, "vy":1.49871, "omega":0.0, "ax":-2.31616, "ay":0.0006, "alpha":0.0, "fx":[-39.3972,-39.3972,-39.3972,-39.3972], "fy":[0.01016,0.01016,0.01016,0.01016]}, - {"t":1.70303, "x":7.79522, "y":1.24898, "heading":1.5708, "vx":-0.05304, "vy":1.49873, "omega":0.0, "ax":2.28263, "ay":0.00059, "alpha":0.0, "fx":[38.82681,38.82681,38.82681,38.82681], "fy":[0.01,0.01,0.01,0.01]}, - {"t":1.74916, "x":7.7952, "y":1.31812, "heading":1.5708, "vx":0.05226, "vy":1.49876, "omega":0.0, "ax":-2.24914, "ay":0.00058, "alpha":0.0, "fx":[-38.25717,-38.25717,-38.25717,-38.25717], "fy":[0.00984,0.00984,0.00984,0.00984]}, - {"t":1.79529, "x":7.79522, "y":1.38726, "heading":1.5708, "vx":-0.05149, "vy":1.49879, "omega":0.0, "ax":2.21569, "ay":0.00057, "alpha":0.0, "fx":[37.68831,37.68831,37.68831,37.68831], "fy":[0.00968,0.00968,0.00968,0.00968]}, - {"t":1.84142, "x":7.7952, "y":1.4564, "heading":1.5708, "vx":0.05072, "vy":1.49881, "omega":0.0, "ax":-2.18229, "ay":0.00056, "alpha":0.0, "fx":[-37.12021,-37.12021,-37.12021,-37.12021], "fy":[0.00953,0.00953,0.00953,0.00953]}, - {"t":1.88755, "x":7.79522, "y":1.52554, "heading":1.5708, "vx":-0.04995, "vy":1.49884, "omega":0.0, "ax":2.14894, "ay":0.00055, "alpha":0.0, "fx":[36.55289,36.55289,36.55289,36.55289], "fy":[0.00937,0.00937,0.00937,0.00937]}, - {"t":1.93368, "x":7.7952, "y":1.59468, "heading":1.5708, "vx":0.04918, "vy":1.49887, "omega":0.0, "ax":-2.11564, "ay":0.00054, "alpha":0.0, "fx":[-35.98636,-35.98636,-35.98636,-35.98636], "fy":[0.00921,0.00921,0.00921,0.00921]}, - {"t":1.97981, "x":7.79522, "y":1.66383, "heading":1.5708, "vx":-0.04841, "vy":1.49889, "omega":0.0, "ax":2.08237, "ay":0.00053, "alpha":0.0, "fx":[35.4206,35.4206,35.4206,35.4206], "fy":[0.00905,0.00905,0.00905,0.00905]}, - {"t":2.02594, "x":7.7952, "y":1.73297, "heading":1.5708, "vx":0.04765, "vy":1.49892, "omega":0.0, "ax":-2.04916, "ay":0.00052, "alpha":0.0, "fx":[-34.85564,-34.85564,-34.85564,-34.85564], "fy":[0.0089,0.0089,0.0089,0.0089]}, - {"t":2.07207, "x":7.79522, "y":1.80212, "heading":1.5708, "vx":-0.04688, "vy":1.49894, "omega":0.0, "ax":2.01599, "ay":0.00051, "alpha":0.0, "fx":[34.29147,34.29147,34.29147,34.29147], "fy":[0.00874,0.00874,0.00874,0.00874]}, - {"t":2.1182, "x":7.7952, "y":1.87126, "heading":1.5708, "vx":0.04612, "vy":1.49896, "omega":0.0, "ax":-1.98287, "ay":0.0005, "alpha":0.0, "fx":[-33.72809,-33.72809,-33.72809,-33.72809], "fy":[0.00858,0.00858,0.00858,0.00858]}, - {"t":2.16433, "x":7.79522, "y":1.94041, "heading":1.5708, "vx":-0.04535, "vy":1.49899, "omega":0.0, "ax":1.9498, "ay":0.0005, "alpha":0.0, "fx":[33.16552,33.16552,33.16552,33.16552], "fy":[0.00843,0.00843,0.00843,0.00843]}, - {"t":2.21046, "x":7.7952, "y":2.00956, "heading":1.5708, "vx":0.04459, "vy":1.49901, "omega":0.0, "ax":-1.91677, "ay":0.00049, "alpha":0.0, "fx":[-32.60374,-32.60374,-32.60374,-32.60374], "fy":[0.00827,0.00827,0.00827,0.00827]}, - {"t":2.25659, "x":7.79522, "y":2.07871, "heading":1.5708, "vx":-0.04383, "vy":1.49903, "omega":0.0, "ax":1.88379, "ay":0.00048, "alpha":0.0, "fx":[32.04278,32.04278,32.04278,32.04278], "fy":[0.00812,0.00812,0.00812,0.00812]}, - {"t":2.30272, "x":7.7952, "y":2.14787, "heading":1.5708, "vx":0.04307, "vy":1.49905, "omega":0.0, "ax":-1.85086, "ay":0.00047, "alpha":0.0, "fx":[-31.48262,-31.48262,-31.48262,-31.48262], "fy":[0.00797,0.00797,0.00797,0.00797]}, - {"t":2.34886, "x":7.79522, "y":2.21702, "heading":1.5708, "vx":-0.04231, "vy":1.49908, "omega":0.0, "ax":1.81798, "ay":0.00046, "alpha":0.0, "fx":[30.92327,30.92327,30.92327,30.92327], "fy":[0.00781,0.00781,0.00781,0.00781]}, - {"t":2.39499, "x":7.7952, "y":2.28617, "heading":1.5708, "vx":0.04155, "vy":1.4991, "omega":0.0, "ax":-1.78514, "ay":0.00045, "alpha":0.0, "fx":[-30.36473,-30.36473,-30.36473,-30.36473], "fy":[0.00766,0.00766,0.00766,0.00766]}, - {"t":2.44112, "x":7.79522, "y":2.35533, "heading":1.5708, "vx":-0.0408, "vy":1.49912, "omega":0.0, "ax":1.75235, "ay":0.00044, "alpha":0.0, "fx":[29.80701,29.80701,29.80701,29.80701], "fy":[0.00751,0.00751,0.00751,0.00751]}, - {"t":2.48725, "x":7.7952, "y":2.42448, "heading":1.5708, "vx":0.04004, "vy":1.49914, "omega":0.0, "ax":-1.71961, "ay":0.00043, "alpha":0.0, "fx":[-29.2501,-29.2501,-29.2501,-29.2501], "fy":[0.00736,0.00736,0.00736,0.00736]}, - {"t":2.53338, "x":7.79522, "y":2.49364, "heading":1.5708, "vx":-0.03929, "vy":1.49916, "omega":0.0, "ax":1.68692, "ay":0.00042, "alpha":0.0, "fx":[28.694,28.694,28.694,28.694], "fy":[0.00721,0.00721,0.00721,0.00721]}, - {"t":2.57951, "x":7.7952, "y":2.5628, "heading":1.5708, "vx":0.03853, "vy":1.49918, "omega":0.0, "ax":-1.65427, "ay":0.00041, "alpha":0.0, "fx":[-28.13872,-28.13872,-28.13872,-28.13872], "fy":[0.00706,0.00706,0.00706,0.00706]}, - {"t":2.62564, "x":7.79522, "y":2.63196, "heading":1.5708, "vx":-0.03778, "vy":1.4992, "omega":0.0, "ax":1.62168, "ay":0.00041, "alpha":0.0, "fx":[27.58425,27.58425,27.58425,27.58425], "fy":[0.00691,0.00691,0.00691,0.00691]}, - {"t":2.67177, "x":7.7952, "y":2.70111, "heading":1.5708, "vx":0.03703, "vy":1.49922, "omega":0.0, "ax":-1.58913, "ay":0.0004, "alpha":0.0, "fx":[-27.0306,-27.0306,-27.0306,-27.0306], "fy":[0.00676,0.00676,0.00676,0.00676]}, - {"t":2.7179, "x":7.79522, "y":2.77027, "heading":1.5708, "vx":-0.03628, "vy":1.49923, "omega":0.0, "ax":1.55663, "ay":0.00038, "alpha":0.0, "fx":[26.47776,26.47776,26.47776,26.47776], "fy":[0.00647,0.00647,0.00647,0.00647]}, - {"t":2.76403, "x":7.7952, "y":2.83944, "heading":1.5708, "vx":0.03553, "vy":1.49925, "omega":0.0, "ax":-1.5242, "ay":-1.78134, "alpha":0.0, "fx":[-25.92629,-25.92629,-25.92629,-25.92629], "fy":[-30.30015,-30.30015,-30.30015,-30.30015]}, - {"t":2.81016, "x":7.79522, "y":2.9067, "heading":1.5708, "vx":-0.03478, "vy":1.41708, "omega":0.0, "ax":1.49206, "ay":-9.29027, "alpha":0.0, "fx":[25.37957,25.37957,25.37957,25.37957], "fy":[-158.02485,-158.02485,-158.02485,-158.02485]}, - {"t":2.85629, "x":7.7952, "y":2.96219, "heading":1.5708, "vx":0.03405, "vy":0.98851, "omega":0.0, "ax":-1.46086, "ay":-9.30944, "alpha":0.0, "fx":[-24.84879,-24.84879,-24.84879,-24.84879], "fy":[-158.35088,-158.35088,-158.35088,-158.35088]}, - {"t":2.90242, "x":7.79521, "y":2.99788, "heading":1.5708, "vx":-0.03334, "vy":0.55906, "omega":0.0, "ax":1.43826, "ay":-9.31769, "alpha":0.0, "fx":[24.46439,24.46439,24.46439,24.46439], "fy":[-158.49127,-158.49127,-158.49127,-158.49127]}, - {"t":2.94855, "x":7.79521, "y":3.01376, "heading":1.5708, "vx":0.033, "vy":0.12923, "omega":0.0, "ax":-1.7857, "ay":-9.25868, "alpha":0.0, "fx":[-30.37426,-30.37426,-30.37426,-30.37426], "fy":[-157.48757,-157.48757,-157.48757,-157.48757]}, - {"t":2.99726, "x":7.7947, "y":3.00907, "heading":1.5708, "vx":-0.05397, "vy":-0.32175, "omega":0.0, "ax":-1.78548, "ay":-9.25735, "alpha":0.0, "fx":[-30.37058,-30.37058,-30.37058,-30.37058], "fy":[-157.46491,-157.46491,-157.46491,-157.46491]}, - {"t":3.04597, "x":7.78995, "y":2.98242, "heading":1.5708, "vx":-0.14094, "vy":-0.77266, "omega":0.0, "ax":-1.78518, "ay":-9.25549, "alpha":0.0, "fx":[-30.36547,-30.36547,-30.36547,-30.36547], "fy":[-157.43317,-157.43317,-157.43317,-157.43317]}, - {"t":3.09468, "x":7.78097, "y":2.9338, "heading":1.5708, "vx":-0.2279, "vy":-1.22348, "omega":0.0, "ax":-1.78474, "ay":-9.25269, "alpha":0.0, "fx":[-30.3579,-30.3579,-30.3579,-30.3579], "fy":[-157.38555,-157.38555,-157.38555,-157.38555]}, - {"t":3.14339, "x":7.76775, "y":2.86323, "heading":1.5708, "vx":-0.31483, "vy":-1.67416, "omega":0.0, "ax":-1.784, "ay":-9.24802, "alpha":0.0, "fx":[-30.34538,-30.34538,-30.34538,-30.34538], "fy":[-157.30618,-157.30618,-157.30618,-157.30618]}, - {"t":3.1921, "x":7.7503, "y":2.77071, "heading":1.5708, "vx":-0.40172, "vy":-2.12462, "omega":0.0, "ax":-1.78254, "ay":-9.23869, "alpha":0.0, "fx":[-30.3205,-30.3205,-30.3205,-30.3205], "fy":[-157.14753,-157.14753,-157.14753,-157.14753]}, - {"t":3.2408, "x":7.72862, "y":2.65627, "heading":1.5708, "vx":-0.48855, "vy":-2.57463, "omega":0.0, "ax":-1.77817, "ay":-9.2108, "alpha":0.0, "fx":[-30.24623,-30.24623,-30.24623,-30.24623], "fy":[-156.67302,-156.67302,-156.67302,-156.67302]}, - {"t":3.28951, "x":7.70271, "y":2.51994, "heading":1.5708, "vx":-0.57516, "vy":-3.02327, "omega":0.0, "ax":-0.6655, "ay":-2.65454, "alpha":0.0, "fx":[-11.31999,-11.31999,-11.31999,-11.31999], "fy":[-45.15298,-45.15298,-45.15298,-45.15298]}, - {"t":3.33822, "x":7.6739, "y":2.36953, "heading":1.5708, "vx":-0.60758, "vy":-3.15257, "omega":0.0, "ax":1.774, "ay":9.211, "alpha":0.0, "fx":[30.17531,30.17531,30.17531,30.17531], "fy":[156.67644,156.67644,156.67644,156.67644]}, - {"t":3.38693, "x":7.64642, "y":2.2269, "heading":1.5708, "vx":-0.52117, "vy":-2.70391, "omega":0.0, "ax":1.78038, "ay":9.23895, "alpha":0.0, "fx":[30.28374,30.28374,30.28374,30.28374], "fy":[157.15195,157.15195,157.15195,157.15195]}, - {"t":3.43564, "x":7.62314, "y":2.10615, "heading":1.5708, "vx":-0.43445, "vy":-2.2539, "omega":0.0, "ax":1.78248, "ay":9.24824, "alpha":0.0, "fx":[30.31943,30.31943,30.31943,30.31943], "fy":[157.30999,157.30999,157.30999,157.30999]}, - {"t":3.48435, "x":7.60409, "y":2.00734, "heading":1.5708, "vx":-0.34763, "vy":-1.80343, "omega":0.0, "ax":1.78351, "ay":9.25289, "alpha":0.0, "fx":[30.33695,30.33695,30.33695,30.33695], "fy":[157.38893,157.38893,157.38893,157.38893]}, - {"t":3.53306, "x":7.58928, "y":1.93047, "heading":1.5708, "vx":-0.26075, "vy":-1.35274, "omega":0.0, "ax":1.78411, "ay":9.25567, "alpha":0.0, "fx":[30.3472,30.3472,30.3472,30.3472], "fy":[157.43628,157.43628,157.43628,157.43628]}, - {"t":3.58176, "x":7.57869, "y":1.87556, "heading":1.5708, "vx":-0.17385, "vy":-0.90191, "omega":0.0, "ax":1.7845, "ay":9.25753, "alpha":0.0, "fx":[30.3538,30.3538,30.3538,30.3538], "fy":[157.46787,157.46787,157.46787,157.46787]}, - {"t":3.63047, "x":7.57234, "y":1.84261, "heading":1.5708, "vx":-0.08693, "vy":-0.45099, "omega":0.0, "ax":1.78476, "ay":9.25885, "alpha":0.0, "fx":[30.35831,30.35831,30.35831,30.35831], "fy":[157.49046,157.49046,157.49046,157.49046]}, - {"t":3.67918, "x":7.57023, "y":1.83163, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0,0.0,0.0,0.0]}], - "splits":[0] - }, - "events":[] -} diff --git a/src/main/deploy/v1_doomspiral/choreo/FAIL_PATH_LEFT.traj b/src/main/deploy/v1_doomspiral/choreo/FAIL_PATH_LEFT.traj deleted file mode 100644 index 05c20f1e..00000000 --- a/src/main/deploy/v1_doomspiral/choreo/FAIL_PATH_LEFT.traj +++ /dev/null @@ -1,158 +0,0 @@ -{ - "name":"FAIL_PATH_LEFT", - "version":3, - "snapshot":{ - "waypoints":[ - {"x":4.441253185272217, "y":7.693458557128906, "heading":-1.5707972442597578, "intervals":27, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, - {"x":7.795207023620605, "y":7.227133750915527, "heading":-1.5707963267948966, "intervals":33, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, - {"x":7.795207023620605, "y":5.0552420559, "heading":-1.5707963267948966, "intervals":26, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":6.758491516113281, "y":5.603033542633057, "heading":0.0, "intervals":17, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}], - "constraints":[ - {"from":"first", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, - {"from":"last", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, - {"from":"first", "to":"last", "data":{"type":"KeepInRectangle", "props":{"x":0.0, "y":0.0, "w":16.541, "h":8.0692}}, "enabled":true}, - {"from":1, "to":2, "data":{"type":"KeepInLane", "props":{"tolerance":0.00001}}, "enabled":true}, - {"from":1, "to":2, "data":{"type":"MaxAngularVelocity", "props":{"max":0.0}}, "enabled":true}, - {"from":1, "to":2, "data":{"type":"MaxVelocity", "props":{"max":1.5}}, "enabled":true}, - {"from":3, "to":3, "data":{"type":"MaxVelocity", "props":{"max":2.0}}, "enabled":true}, - {"from":2, "to":3, "data":{"type":"MaxVelocity", "props":{"max":2.5}}, "enabled":true}], - "targetDt":0.05 - }, - "params":{ - "waypoints":[ - {"x":{"exp":"4.441253185272217 m", "val":4.441253185272217}, "y":{"exp":"7.693458557128906 m", "val":7.693458557128906}, "heading":{"exp":"-1.5707972442597578 rad", "val":-1.5707972442597578}, "intervals":27, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, - {"x":{"exp":"7.7952070236206055 m", "val":7.795207023620605}, "y":{"exp":"7.227133750915527 m", "val":7.227133750915527}, "heading":{"exp":"-1.5707963267948966 rad", "val":-1.5707963267948966}, "intervals":33, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, - {"x":{"exp":"7.7952070236206055 m", "val":7.795207023620605}, "y":{"exp":"5.0552420559 m", "val":5.0552420559}, "heading":{"exp":"-1.5707963267948966 rad", "val":-1.5707963267948966}, "intervals":26, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":{"exp":"6.758491516113281 m", "val":6.758491516113281}, "y":{"exp":"5.603033542633057 m", "val":5.603033542633057}, "heading":{"exp":"0 deg", "val":0.0}, "intervals":17, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}], - "constraints":[ - {"from":"first", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, - {"from":"last", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, - {"from":"first", "to":"last", "data":{"type":"KeepInRectangle", "props":{"x":{"exp":"0 m", "val":0.0}, "y":{"exp":"0 m", "val":0.0}, "w":{"exp":"16.541 m", "val":16.541}, "h":{"exp":"8.0692 m", "val":8.0692}}}, "enabled":true}, - {"from":1, "to":2, "data":{"type":"KeepInLane", "props":{"tolerance":{"exp":"1e-5 m", "val":0.00001}}}, "enabled":true}, - {"from":1, "to":2, "data":{"type":"MaxAngularVelocity", "props":{"max":{"exp":"0 rad / s", "val":0.0}}}, "enabled":true}, - {"from":1, "to":2, "data":{"type":"MaxVelocity", "props":{"max":{"exp":"1.5 m / s", "val":1.5}}}, "enabled":true}, - {"from":3, "to":3, "data":{"type":"MaxVelocity", "props":{"max":{"exp":"2 m / s", "val":2.0}}}, "enabled":true}, - {"from":2, "to":3, "data":{"type":"MaxVelocity", "props":{"max":{"exp":"2.5 m / s", "val":2.5}}}, "enabled":true}], - "targetDt":{ - "exp":"0.05 s", - "val":0.05 - } - }, - "trajectory":{ - "config":{ - "frontLeft":{ - "x":0.225425, - "y":0.333375 - }, - "backLeft":{ - "x":-0.225425, - "y":0.333375 - }, - "mass":68.0388555, - "inertia":6.5136193464, - "gearing":7.03, - "radius":0.0508, - "vmax":485.8996637552213, - "tmax":1.16, - "cof":2.255, - "bumper":{ - "front":0.34925, - "side":0.4445, - "back":0.34925 - }, - "differentialTrackWidth":0.5588 - }, - "sampleType":"Swerve", - "waypoints":[0.0,1.31812,2.79936,3.5657], - "samples":[ - {"t":0.0, "x":4.44125, "y":7.69346, "heading":-1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":9.42872, "ay":-0.07634, "alpha":0.00001, "fx":[160.37975,160.37975,160.37975,160.37975], "fy":[-1.29842,-1.29842,-1.29856,-1.29856]}, - {"t":0.04882, "x":4.45249, "y":7.69337, "heading":-1.5708, "vx":0.4603, "vy":-0.00373, "omega":0.0, "ax":9.42742, "ay":-0.06331, "alpha":0.00001, "fx":[160.35763,160.35763,160.35763,160.35763], "fy":[-1.07689,-1.0769,-1.07703,-1.07703]}, - {"t":0.09764, "x":4.48619, "y":7.69311, "heading":-1.5708, "vx":0.92054, "vy":-0.00682, "omega":0.0, "ax":9.42554, "ay":-0.05034, "alpha":0.00001, "fx":[160.32571,160.32571,160.32571,160.32571], "fy":[-0.85613,-0.85613,-0.85625,-0.85625]}, - {"t":0.14646, "x":4.54237, "y":7.69272, "heading":-1.5708, "vx":1.38069, "vy":-0.00928, "omega":0.0, "ax":9.42267, "ay":-0.03736, "alpha":0.00001, "fx":[160.27689,160.27689,160.27689,160.27689], "fy":[-0.63547,-0.63547,-0.63559,-0.63559]}, - {"t":0.19528, "x":4.621, "y":7.69222, "heading":-1.5708, "vx":1.8407, "vy":-0.0111, "omega":0.0, "ax":9.41783, "ay":-0.02428, "alpha":0.00001, "fx":[160.19461,160.19461,160.19461,160.19461], "fy":[-0.41287,-0.41287,-0.41298,-0.41298]}, - {"t":0.2441, "x":4.72208, "y":7.69165, "heading":-1.5708, "vx":2.30047, "vy":-0.01228, "omega":0.0, "ax":9.40812, "ay":-0.01069, "alpha":0.00001, "fx":[160.02943,160.02943,160.02943,160.02943], "fy":[-0.18172,-0.18173,-0.18183,-0.18183]}, - {"t":0.29292, "x":4.8456, "y":7.69104, "heading":-1.5708, "vx":2.75977, "vy":-0.01281, "omega":0.0, "ax":9.37914, "ay":0.00532, "alpha":0.00001, "fx":[159.53652,159.53652,159.53652,159.53652], "fy":[0.09056,0.09056,0.09047,0.09047]}, - {"t":0.34173, "x":4.99151, "y":7.69042, "heading":-1.5708, "vx":3.21765, "vy":-0.01255, "omega":0.0, "ax":5.9534, "ay":0.37943, "alpha":-0.00008, "fx":[101.26557,101.26577,101.26574,101.26554], "fy":[6.45358,6.45356,6.45424,6.45426]}, - {"t":0.39055, "x":5.15569, "y":7.69026, "heading":-1.5708, "vx":3.50829, "vy":0.00598, "omega":0.0, "ax":0.00007, "ay":0.21772, "alpha":0.0, "fx":[0.00126,0.00126,0.00126,0.00126], "fy":[3.70339,3.70339,3.70339,3.70339]}, - {"t":0.43937, "x":5.32696, "y":7.69081, "heading":-1.5708, "vx":3.50829, "vy":0.01661, "omega":0.0, "ax":-0.00116, "ay":0.19061, "alpha":0.0, "fx":[-0.01965,-0.01965,-0.01965,-0.01965], "fy":[3.24227,3.24227,3.24227,3.24227]}, - {"t":0.48819, "x":5.49823, "y":7.69185, "heading":-1.5708, "vx":3.50824, "vy":0.02591, "omega":0.0, "ax":-0.00172, "ay":0.1964, "alpha":0.0, "fx":[-0.02923,-0.02923,-0.02923,-0.02923], "fy":[3.34064,3.34064,3.34064,3.34064]}, - {"t":0.53701, "x":5.6695, "y":7.69335, "heading":-1.5708, "vx":3.50815, "vy":0.0355, "omega":0.0, "ax":-0.00242, "ay":0.20907, "alpha":0.0, "fx":[-0.04114,-0.04114,-0.04114,-0.04114], "fy":[3.55624,3.55624,3.55624,3.55624]}, - {"t":0.58583, "x":5.84076, "y":7.69533, "heading":-1.5708, "vx":3.50803, "vy":0.04571, "omega":0.0, "ax":-0.00334, "ay":0.22867, "alpha":0.0, "fx":[-0.05684,-0.05684,-0.05684,-0.05684], "fy":[3.88961,3.88961,3.88961,3.88961]}, - {"t":0.63465, "x":6.01202, "y":7.69783, "heading":-1.5708, "vx":3.50787, "vy":0.05687, "omega":0.0, "ax":-0.00467, "ay":0.25953, "alpha":0.0, "fx":[-0.07949,-0.07949,-0.07949,-0.07949], "fy":[4.41455,4.41455,4.41455,4.41455]}, - {"t":0.68347, "x":6.18326, "y":7.70092, "heading":-1.5708, "vx":3.50764, "vy":0.06954, "omega":0.0, "ax":-0.00683, "ay":0.31077, "alpha":0.0, "fx":[-0.11615,-0.11615,-0.11615,-0.11615], "fy":[5.28608,5.28608,5.28608,5.28608]}, - {"t":0.73229, "x":6.35449, "y":7.70468, "heading":-1.5708, "vx":3.50731, "vy":0.08471, "omega":0.0, "ax":-0.01033, "ay":0.38514, "alpha":0.0, "fx":[-0.17565,-0.17565,-0.17565,-0.17565], "fy":[6.55104,6.55104,6.55104,6.55104]}, - {"t":0.78111, "x":6.52571, "y":7.70928, "heading":-1.5708, "vx":3.50681, "vy":0.10351, "omega":0.0, "ax":-0.00548, "ay":0.17817, "alpha":0.0, "fx":[-0.09314,-0.09314,-0.09314,-0.09314], "fy":[3.03054,3.03054,3.03054,3.03054]}, - {"t":0.82993, "x":6.6969, "y":7.71454, "heading":-1.5708, "vx":3.50654, "vy":0.11221, "omega":0.0, "ax":0.00483, "ay":-4.42413, "alpha":0.0, "fx":[0.08216,0.08216,0.08216,0.08216], "fy":[-75.2531,-75.2531,-75.2531,-75.2531]}, - {"t":0.87875, "x":6.86809, "y":7.71475, "heading":-1.5708, "vx":3.50677, "vy":-0.10377, "omega":0.0, "ax":-0.80557, "ay":-8.77287, "alpha":0.0, "fx":[-13.70254,-13.70254,-13.70254,-13.70254], "fy":[-149.22408,-149.22408,-149.22408,-149.22408]}, - {"t":0.92757, "x":7.03833, "y":7.69923, "heading":-1.5708, "vx":3.46745, "vy":-0.53206, "omega":0.0, "ax":-3.40369, "ay":-8.60138, "alpha":0.0, "fx":[-57.89583,-57.89587,-57.89585,-57.8958], "fy":[-146.30698,-146.30697,-146.30698,-146.30699]}, - {"t":0.97639, "x":7.20355, "y":7.663, "heading":-1.5708, "vx":3.30128, "vy":-0.95197, "omega":0.0, "ax":-8.89473, "ay":-2.99673, "alpha":0.0, "fx":[-151.29677,-151.29677,-151.29676,-151.29676], "fy":[-50.97349,-50.97349,-50.9735,-50.9735]}, - {"t":1.0252, "x":7.35412, "y":7.61296, "heading":-1.5708, "vx":2.86705, "vy":-1.09827, "omega":0.0, "ax":-9.21297, "ay":-1.91421, "alpha":0.0, "fx":[-156.70996,-156.70996,-156.70996,-156.70996], "fy":[-32.56019,-32.56019,-32.56018,-32.56018]}, - {"t":1.07402, "x":7.48311, "y":7.55706, "heading":-1.5708, "vx":2.41728, "vy":-1.19172, "omega":0.0, "ax":-9.29709, "ay":-1.50774, "alpha":0.0, "fx":[-158.1409,-158.1409,-158.1409,-158.1409], "fy":[-25.64625,-25.64625,-25.64625,-25.64625]}, - {"t":1.12284, "x":7.59004, "y":7.49709, "heading":-1.5708, "vx":1.9634, "vy":-1.26532, "omega":0.0, "ax":-9.33346, "ay":-1.29686, "alpha":0.0, "fx":[-158.75955,-158.75955,-158.75955,-158.75955], "fy":[-22.05927,-22.05927,-22.05926,-22.05926]}, - {"t":1.17166, "x":7.67477, "y":7.43377, "heading":-1.5708, "vx":1.50775, "vy":-1.32864, "omega":0.0, "ax":-9.35324, "ay":-1.16831, "alpha":0.0, "fx":[-159.0959,-159.0959,-159.0959,-159.0959], "fy":[-19.87269,-19.87269,-19.87268,-19.87268]}, - {"t":1.22048, "x":7.73723, "y":7.36751, "heading":-1.5708, "vx":1.05113, "vy":-1.38567, "omega":0.0, "ax":-9.36551, "ay":-1.082, "alpha":0.0, "fx":[-159.30461,-159.30461,-159.30461,-159.30461], "fy":[-18.40452,-18.40452,-18.40451,-18.40451]}, - {"t":1.2693, "x":7.77738, "y":7.29858, "heading":-1.5708, "vx":0.59391, "vy":-1.43849, "omega":0.0, "ax":-9.3738, "ay":-1.02019, "alpha":0.0, "fx":[-159.44568,-159.44568,-159.44568,-159.44568], "fy":[-17.35319,-17.35319,-17.35318,-17.35317]}, - {"t":1.31812, "x":7.79521, "y":7.22713, "heading":-1.5708, "vx":0.13629, "vy":-1.4883, "omega":0.0, "ax":-6.06382, "ay":-0.11574, "alpha":0.0, "fx":[-103.14378,-103.14378,-103.14378,-103.14378], "fy":[-1.96871,-1.96871,-1.96871,-1.96871]}, - {"t":1.36301, "x":7.79522, "y":7.16021, "heading":-1.5708, "vx":-0.13589, "vy":-1.4935, "omega":0.0, "ax":6.03712, "ay":-0.00161, "alpha":0.0, "fx":[102.68965,102.68965,102.68965,102.68965], "fy":[-0.02745,-0.02745,-0.02745,-0.02745]}, - {"t":1.40789, "x":7.7952, "y":7.09317, "heading":-1.5708, "vx":0.13509, "vy":-1.49357, "omega":0.0, "ax":-6.0016, "ay":-0.0016, "alpha":0.0, "fx":[-102.08552,-102.08552,-102.08552,-102.08552], "fy":[-0.02718,-0.02718,-0.02718,-0.02718]}, - {"t":1.45278, "x":7.79522, "y":7.02613, "heading":-1.5708, "vx":-0.1343, "vy":-1.49364, "omega":0.0, "ax":5.96621, "ay":-0.00158, "alpha":0.0, "fx":[101.4835,101.4835,101.4835,101.4835], "fy":[-0.02692,-0.02692,-0.02692,-0.02692]}, - {"t":1.49766, "x":7.7952, "y":6.95909, "heading":-1.5708, "vx":0.1335, "vy":-1.49371, "omega":0.0, "ax":-5.93095, "ay":-0.00157, "alpha":0.0, "fx":[-100.88372,-100.88372,-100.88372,-100.88372], "fy":[-0.02666,-0.02666,-0.02666,-0.02666]}, - {"t":1.54255, "x":7.79522, "y":6.89204, "heading":-1.5708, "vx":-0.13271, "vy":-1.49378, "omega":0.0, "ax":5.89582, "ay":-0.00155, "alpha":0.0, "fx":[100.28629,100.28629,100.28629,100.28629], "fy":[-0.02639,-0.02639,-0.02639,-0.02639]}, - {"t":1.58744, "x":7.7952, "y":6.82499, "heading":-1.5708, "vx":0.13193, "vy":-1.49385, "omega":0.0, "ax":-5.86085, "ay":-0.00154, "alpha":0.0, "fx":[-99.69135,-99.69135,-99.69135,-99.69135], "fy":[-0.02612,-0.02612,-0.02612,-0.02612]}, - {"t":1.63232, "x":7.79522, "y":6.75793, "heading":-1.5708, "vx":-0.13114, "vy":-1.49392, "omega":0.0, "ax":5.82603, "ay":-0.00152, "alpha":0.0, "fx":[99.09905,99.09905,99.09905,99.09905], "fy":[-0.02585,-0.02585,-0.02585,-0.02585]}, - {"t":1.67721, "x":7.7952, "y":6.69088, "heading":-1.5708, "vx":0.13036, "vy":-1.49399, "omega":0.0, "ax":-5.79137, "ay":-0.0015, "alpha":0.0, "fx":[-98.50953,-98.50953,-98.50953,-98.50953], "fy":[-0.02557,-0.02557,-0.02557,-0.02557]}, - {"t":1.72209, "x":7.79522, "y":6.62381, "heading":-1.5708, "vx":-0.12959, "vy":-1.49405, "omega":0.0, "ax":5.75689, "ay":-0.00149, "alpha":0.0, "fx":[97.92298,97.92298,97.92298,97.92298], "fy":[-0.02528,-0.02528,-0.02528,-0.02528]}, - {"t":1.76698, "x":7.7952, "y":6.55675, "heading":-1.5708, "vx":0.12882, "vy":-1.49412, "omega":0.0, "ax":-5.72259, "ay":-0.00147, "alpha":0.0, "fx":[-97.33958,-97.33958,-97.33958,-97.33958], "fy":[-0.02499,-0.02499,-0.02499,-0.02499]}, - {"t":1.81187, "x":7.79522, "y":6.48968, "heading":-1.5708, "vx":-0.12805, "vy":-1.49419, "omega":0.0, "ax":5.68849, "ay":-0.00145, "alpha":0.0, "fx":[96.75953,96.75953,96.75953,96.75953], "fy":[-0.0247,-0.0247,-0.0247,-0.0247]}, - {"t":1.85675, "x":7.7952, "y":6.42261, "heading":-1.5708, "vx":0.12729, "vy":-1.49425, "omega":0.0, "ax":-5.6546, "ay":-0.00143, "alpha":0.0, "fx":[-96.18307,-96.18307,-96.18307,-96.18307], "fy":[-0.02439,-0.02439,-0.02439,-0.02439]}, - {"t":1.90164, "x":7.79522, "y":6.35554, "heading":-1.5708, "vx":-0.12653, "vy":-1.49432, "omega":0.0, "ax":5.62093, "ay":-0.00142, "alpha":0.0, "fx":[95.61043,95.61043,95.61043,95.61043], "fy":[-0.02408,-0.02408,-0.02408,-0.02408]}, - {"t":1.94652, "x":7.7952, "y":6.28847, "heading":-1.5708, "vx":0.12577, "vy":-1.49438, "omega":0.0, "ax":-5.58751, "ay":-0.0014, "alpha":0.0, "fx":[-95.04191,-95.04191,-95.04191,-95.04191], "fy":[-0.02375,-0.02375,-0.02375,-0.02375]}, - {"t":1.99141, "x":7.79522, "y":6.22139, "heading":-1.5708, "vx":-0.12503, "vy":-1.49444, "omega":0.0, "ax":5.55434, "ay":-0.00138, "alpha":0.0, "fx":[94.47781,94.47781,94.47781,94.47781], "fy":[-0.02342,-0.02342,-0.02342,-0.02342]}, - {"t":2.0363, "x":7.7952, "y":6.15431, "heading":-1.5708, "vx":0.12429, "vy":-1.49451, "omega":0.0, "ax":-5.52146, "ay":-0.00136, "alpha":0.0, "fx":[-93.91846,-93.91846,-93.91846,-93.91846], "fy":[-0.02308,-0.02308,-0.02308,-0.02308]}, - {"t":2.08118, "x":7.79522, "y":6.08722, "heading":-1.5708, "vx":-0.12355, "vy":-1.49457, "omega":0.0, "ax":5.48888, "ay":-0.00134, "alpha":0.0, "fx":[93.36427,93.36427,93.36427,93.36427], "fy":[-0.02272,-0.02272,-0.02272,-0.02272]}, - {"t":2.12607, "x":7.7952, "y":6.02014, "heading":-1.5708, "vx":0.12282, "vy":-1.49463, "omega":0.0, "ax":-5.45663, "ay":-0.00131, "alpha":0.0, "fx":[-92.81565,-92.81565,-92.81565,-92.81565], "fy":[-0.02235,-0.02235,-0.02235,-0.02235]}, - {"t":2.17095, "x":7.79522, "y":5.95305, "heading":-1.5708, "vx":-0.1221, "vy":-1.49468, "omega":0.0, "ax":5.42473, "ay":-0.00129, "alpha":0.0, "fx":[92.27311,92.27311,92.27311,92.27311], "fy":[-0.02196,-0.02196,-0.02196,-0.02196]}, - {"t":2.21584, "x":7.7952, "y":5.88596, "heading":-1.5708, "vx":0.12139, "vy":-1.49474, "omega":0.0, "ax":-5.39322, "ay":-0.00127, "alpha":0.0, "fx":[-91.73719,-91.73719,-91.73719,-91.73719], "fy":[-0.02155,-0.02155,-0.02155,-0.02155]}, - {"t":2.26073, "x":7.79521, "y":5.81886, "heading":-1.5708, "vx":-0.12069, "vy":-1.4948, "omega":0.0, "ax":5.36214, "ay":-0.00124, "alpha":0.0, "fx":[91.20853,91.20853,91.20853,91.20853], "fy":[-0.02112,-0.02112,-0.02112,-0.02112]}, - {"t":2.30561, "x":7.7952, "y":5.75176, "heading":-1.5708, "vx":0.12, "vy":-1.49486, "omega":0.0, "ax":-5.33153, "ay":-0.00121, "alpha":0.0, "fx":[-90.68785,-90.68785,-90.68785,-90.68785], "fy":[-0.02066,-0.02066,-0.02066,-0.02066]}, - {"t":2.3505, "x":7.79521, "y":5.68467, "heading":-1.5708, "vx":-0.11932, "vy":-1.49491, "omega":0.0, "ax":5.30144, "ay":-0.00119, "alpha":0.0, "fx":[90.17601,90.17601,90.17601,90.17601], "fy":[-0.02018,-0.02018,-0.02018,-0.02018]}, - {"t":2.39539, "x":7.7952, "y":5.61756, "heading":-1.5708, "vx":0.11865, "vy":-1.49496, "omega":0.0, "ax":-5.27193, "ay":-0.00116, "alpha":0.0, "fx":[-89.67398,-89.67398,-89.67398,-89.67398], "fy":[-0.01965,-0.01965,-0.01965,-0.01965]}, - {"t":2.44027, "x":7.79521, "y":5.55046, "heading":-1.5708, "vx":-0.11799, "vy":-1.49502, "omega":0.0, "ax":5.24306, "ay":-0.00112, "alpha":0.0, "fx":[89.1829,89.1829,89.1829,89.1829], "fy":[-0.01909,-0.01909,-0.01909,-0.01909]}, - {"t":2.48516, "x":7.7952, "y":5.48335, "heading":-1.5708, "vx":0.11735, "vy":-1.49507, "omega":0.0, "ax":-5.21491, "ay":-0.00109, "alpha":0.0, "fx":[-88.70414,-88.70414,-88.70414,-88.70414], "fy":[-0.01848,-0.01848,-0.01848,-0.01848]}, - {"t":2.53004, "x":7.79521, "y":5.41624, "heading":-1.5708, "vx":-0.11673, "vy":-1.49511, "omega":0.0, "ax":5.18758, "ay":-0.00105, "alpha":0.0, "fx":[88.23928,88.23928,88.23928,88.23928], "fy":[-0.01781,-0.01781,-0.01781,-0.01781]}, - {"t":2.57493, "x":7.7952, "y":5.34913, "heading":-1.5708, "vx":0.11612, "vy":-1.49516, "omega":0.0, "ax":-5.16118, "ay":-0.001, "alpha":0.0, "fx":[-87.79027,-87.79027,-87.79027,-87.79027], "fy":[-0.01706,-0.01706,-0.01706,-0.01706]}, - {"t":2.61982, "x":7.79521, "y":5.28202, "heading":-1.5708, "vx":-0.11554, "vy":-1.49521, "omega":0.0, "ax":5.13586, "ay":-0.00094, "alpha":0.0, "fx":[87.35943,87.35943,87.35943,87.35943], "fy":[-0.01597,-0.01597,-0.01597,-0.01597]}, - {"t":2.6647, "x":7.7952, "y":5.21491, "heading":-1.5708, "vx":0.11499, "vy":-1.49525, "omega":0.0, "ax":-5.11346, "ay":1.94861, "alpha":0.0, "fx":[-86.97853,-86.97853,-86.97853,-86.97853], "fy":[33.14529,33.14529,33.14529,33.14529]}, - {"t":2.70959, "x":7.79521, "y":5.14975, "heading":-1.5708, "vx":-0.11454, "vy":-1.40778, "omega":0.0, "ax":5.10615, "ay":7.9067, "alpha":0.0, "fx":[86.85416,86.85416,86.85416,86.85416], "fy":[134.4907,134.4907,134.4907,134.4907]}, - {"t":2.75447, "x":7.79521, "y":5.09453, "heading":-1.5708, "vx":0.11466, "vy":-1.05288, "omega":0.0, "ax":-5.11554, "ay":7.91567, "alpha":0.0, "fx":[-87.01381,-87.01381,-87.01381,-87.01381], "fy":[134.64325,134.64325,134.64325,134.64325]}, - {"t":2.79936, "x":7.79521, "y":5.05524, "heading":-1.5708, "vx":-0.11496, "vy":-0.69758, "omega":0.0, "ax":-7.41808, "ay":5.8149, "alpha":0.0, "fx":[-126.17945,-126.17945,-126.17945,-126.17945], "fy":[98.90984,98.90984,98.90984,98.90984]}, - {"t":2.82883, "x":7.7886, "y":5.03721, "heading":-1.5708, "vx":-0.3336, "vy":-0.52619, "omega":0.0, "ax":-7.4074, "ay":5.82637, "alpha":0.0, "fx":[-125.99772,-125.99772,-125.99772,-125.99772], "fy":[99.1048,99.1048,99.1048,99.1048]}, - {"t":2.85831, "x":7.77555, "y":5.02423, "heading":-1.5708, "vx":-0.55193, "vy":-0.35446, "omega":0.0, "ax":-7.39401, "ay":5.84067, "alpha":0.0, "fx":[-125.77002,-125.77002,-125.77002,-125.77002], "fy":[99.34815,99.34815,99.34815,99.34815]}, - {"t":2.88778, "x":7.75607, "y":5.01632, "heading":-1.5708, "vx":-0.76987, "vy":-0.18231, "omega":0.0, "ax":-7.37675, "ay":5.85903, "alpha":0.0, "fx":[-125.47646,-125.47646,-125.47646,-125.47646], "fy":[99.66034,99.66034,99.66034,99.66034]}, - {"t":2.91726, "x":7.73017, "y":5.01349, "heading":-1.5708, "vx":-0.98729, "vy":-0.00962, "omega":0.0, "ax":-7.35366, "ay":5.88343, "alpha":0.0, "fx":[-125.08367,-125.08367,-125.08367,-125.08367], "fy":[100.07538,100.07538,100.07538,100.07538]}, - {"t":2.94673, "x":7.69788, "y":5.01576, "heading":-1.5708, "vx":-1.20404, "vy":0.1638, "omega":0.0, "ax":-7.32117, "ay":5.91745, "alpha":0.0, "fx":[-124.53106,-124.53106,-124.53106,-124.53106], "fy":[100.65416,100.65416,100.65416,100.65416]}, - {"t":2.97621, "x":7.65921, "y":5.02316, "heading":-1.5708, "vx":-1.41983, "vy":0.33821, "omega":0.0, "ax":-7.27208, "ay":5.96821, "alpha":0.0, "fx":[-123.69605,-123.69605,-123.69605,-123.69605], "fy":[101.5176,101.5176,101.5176,101.5176]}, - {"t":3.00568, "x":7.6142, "y":5.03572, "heading":-1.5708, "vx":-1.63417, "vy":0.51412, "omega":0.0, "ax":-7.18928, "ay":6.05211, "alpha":0.0, "fx":[-122.28766,-122.28766,-122.28766,-122.28766], "fy":[102.94461,102.94461,102.94461,102.94461]}, - {"t":3.03516, "x":7.56291, "y":5.0535, "heading":-1.5708, "vx":-1.84607, "vy":0.6925, "omega":0.0, "ax":-7.01998, "ay":6.21726, "alpha":0.0, "fx":[-119.4078,-119.4078,-119.4078,-119.4078], "fy":[105.75382,105.75382,105.75382,105.75382]}, - {"t":3.06463, "x":7.50545, "y":5.07662, "heading":-1.5708, "vx":-2.05298, "vy":0.87575, "omega":0.0, "ax":-6.48447, "ay":6.68917, "alpha":0.0, "fx":[-110.29895,-110.29895,-110.29895,-110.29895], "fy":[113.7809,113.7809,113.7809,113.7809]}, - {"t":3.0941, "x":7.44212, "y":5.10533, "heading":-1.5708, "vx":-2.24411, "vy":1.07291, "omega":0.0, "ax":3.74469, "ay":7.74023, "alpha":0.0, "fx":[63.69603,63.69603,63.69603,63.69603], "fy":[131.65905,131.65905,131.65905,131.65905]}, - {"t":3.12358, "x":7.37761, "y":5.14032, "heading":-1.5708, "vx":-2.13373, "vy":1.30105, "omega":0.0, "ax":3.54338, "ay":5.34622, "alpha":0.0, "fx":[60.27185,60.27185,60.27185,60.27185], "fy":[90.93762,90.93762,90.93762,90.93762]}, - {"t":3.15305, "x":7.31625, "y":5.18099, "heading":-1.5708, "vx":-2.02929, "vy":1.45863, "omega":0.0, "ax":1.10864, "ay":1.50709, "alpha":0.0, "fx":[18.85765,18.85765,18.85765,18.85765], "fy":[25.63514,25.63514,25.63514,25.63514]}, - {"t":3.18253, "x":7.25692, "y":5.22464, "heading":-1.5708, "vx":-1.99662, "vy":1.50305, "omega":0.0, "ax":0.18708, "ay":0.24758, "alpha":0.0, "fx":[3.18225,3.18225,3.18225,3.18225], "fy":[4.21123,4.21123,4.21123,4.21123]}, - {"t":3.212, "x":7.19815, "y":5.26905, "heading":-1.5708, "vx":-1.9911, "vy":1.51035, "omega":0.0, "ax":0.03053, "ay":0.04023, "alpha":0.0, "fx":[0.51938,0.51938,0.51938,0.51938], "fy":[0.6843,0.6843,0.6843,0.6843]}, - {"t":3.24148, "x":7.13948, "y":5.31358, "heading":-1.5708, "vx":-1.9902, "vy":1.51153, "omega":0.0, "ax":0.00578, "ay":0.00759, "alpha":0.0, "fx":[0.09837,0.09837,0.09837,0.09837], "fy":[0.12906,0.12906,0.12906,0.12906]}, - {"t":3.27095, "x":7.08082, "y":5.35814, "heading":-1.5708, "vx":-1.99003, "vy":1.51176, "omega":0.0, "ax":0.17078, "ay":-0.1234, "alpha":0.0, "fx":[2.90496,2.90496,2.90496,2.90496], "fy":[-2.09898,-2.09898,-2.09898,-2.09898]}, - {"t":3.30043, "x":7.02224, "y":5.40264, "heading":-1.5708, "vx":-1.985, "vy":1.50812, "omega":0.0, "ax":7.40835, "ay":-5.62797, "alpha":0.0, "fx":[126.01391,126.01391,126.01391,126.01391], "fy":[-95.73008,-95.73008,-95.73008,-95.73008]}, - {"t":3.3299, "x":6.96695, "y":5.44465, "heading":-1.5708, "vx":-1.76664, "vy":1.34224, "omega":0.0, "ax":7.46472, "ay":-5.67102, "alpha":0.0, "fx":[126.97275,126.97275,126.97275,126.97275], "fy":[-96.46242,-96.46242,-96.46242,-96.46242]}, - {"t":3.35938, "x":6.91812, "y":5.48174, "heading":-1.5708, "vx":-1.54662, "vy":1.17509, "omega":0.0, "ax":7.48201, "ay":-5.6843, "alpha":0.0, "fx":[127.26689,127.26689,127.26689,127.26689], "fy":[-96.68834,-96.68834,-96.68834,-96.68834]}, - {"t":3.38885, "x":6.87579, "y":5.51391, "heading":-1.5708, "vx":-1.32609, "vy":1.00755, "omega":0.0, "ax":7.49037, "ay":-5.69078, "alpha":0.0, "fx":[127.40912,127.40912,127.40912,127.40912], "fy":[-96.79855,-96.79855,-96.79855,-96.79855]}, - {"t":3.41832, "x":6.83996, "y":5.54114, "heading":-1.5708, "vx":-1.10532, "vy":0.83981, "omega":0.0, "ax":7.49529, "ay":-5.69463, "alpha":0.0, "fx":[127.49271,127.49271,127.49271,127.49271], "fy":[-96.86411,-96.86411,-96.86411,-96.86411]}, - {"t":3.4478, "x":6.81063, "y":5.56342, "heading":-1.5708, "vx":-0.8844, "vy":0.67197, "omega":0.0, "ax":7.49851, "ay":-5.6972, "alpha":0.0, "fx":[127.54756,127.54756,127.54756,127.54756], "fy":[-96.90779,-96.90779,-96.90779,-96.90779]}, - {"t":3.47727, "x":6.78782, "y":5.58075, "heading":-1.5708, "vx":-0.66338, "vy":0.50404, "omega":0.0, "ax":7.50078, "ay":-5.69905, "alpha":0.0, "fx":[127.58618,127.58618,127.58618,127.58618], "fy":[-96.93914,-96.93914,-96.93914,-96.93914]}, - {"t":3.50675, "x":6.77153, "y":5.59313, "heading":-1.5708, "vx":-0.4423, "vy":0.33607, "omega":0.0, "ax":7.50246, "ay":-5.70044, "alpha":0.0, "fx":[127.61476,127.61476,127.61476,127.61476], "fy":[-96.96286,-96.96286,-96.96286,-96.96286]}, - {"t":3.53622, "x":6.76175, "y":5.60056, "heading":-1.5708, "vx":-0.22117, "vy":0.16805, "omega":0.0, "ax":7.50375, "ay":-5.70154, "alpha":0.0, "fx":[127.63668,127.63668,127.63668,127.63668], "fy":[-96.98152,-96.98152,-96.98152,-96.98152]}, - {"t":3.5657, "x":6.75849, "y":5.60303, "heading":-1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0,0.0,0.0,0.0]}], - "splits":[0] - }, - "events":[] -} diff --git a/src/main/deploy/v1_doomspiral/choreo/LEFT_DOT_1.traj b/src/main/deploy/v1_doomspiral/choreo/LEFT_DOT_1.traj new file mode 100644 index 00000000..4b5f0f0e --- /dev/null +++ b/src/main/deploy/v1_doomspiral/choreo/LEFT_DOT_1.traj @@ -0,0 +1,28 @@ +{ + "name":"LEFT_DOT_1", + "version":3, + "snapshot":{ + "waypoints":[], + "constraints":[], + "targetDt":0.05 + }, + "params":{ + "waypoints":[], + "constraints":[ + {"from":"first", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, + {"from":"last", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, + {"from":"first", "to":"last", "data":{"type":"KeepInRectangle", "props":{"x":{"exp":"0 m", "val":0.0}, "y":{"exp":"0 m", "val":0.0}, "w":{"exp":"16.541 m", "val":16.541}, "h":{"exp":"8.0692 m", "val":8.0692}}}, "enabled":false}], + "targetDt":{ + "exp":"0.05 s", + "val":0.05 + } + }, + "trajectory":{ + "config":null, + "sampleType":null, + "waypoints":[], + "samples":[], + "splits":[] + }, + "events":[] +} diff --git a/src/main/deploy/v1_doomspiral/choreo/LEFT_RETURN_TO_MID.traj b/src/main/deploy/v1_doomspiral/choreo/LEFT_RETURN_TO_MID.traj index b5898bf9..32c2c8c3 100644 --- a/src/main/deploy/v1_doomspiral/choreo/LEFT_RETURN_TO_MID.traj +++ b/src/main/deploy/v1_doomspiral/choreo/LEFT_RETURN_TO_MID.traj @@ -5,8 +5,8 @@ "waypoints":[ {"x":2.818082809448242, "y":6.5, "heading":0.65, "intervals":22, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, {"x":3.217587471008301, "y":7.499445915222168, "heading":0.0, "intervals":23, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":5.039078712463379, "y":7.561816692352295, "heading":0.0, "intervals":34, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, - {"x":7.840170383453369, "y":7.278725624084473, "heading":-1.5707963267948966, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], + {"x":5.039078712463379, "y":7.586472, "heading":0.0, "intervals":37, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, + {"x":8.256524, "y":7.365762, "heading":-1.5707963267948966, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], "constraints":[ {"from":"first", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, {"from":"last", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, @@ -20,8 +20,8 @@ "waypoints":[ {"x":{"exp":"2.818082809448242 m", "val":2.818082809448242}, "y":{"exp":"6.5 m", "val":6.5}, "heading":{"exp":"0.65 rad", "val":0.65}, "intervals":22, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, {"x":{"exp":"3.217587471008301 m", "val":3.217587471008301}, "y":{"exp":"7.499445915222168 m", "val":7.499445915222168}, "heading":{"exp":"0 deg", "val":0.0}, "intervals":23, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":{"exp":"5.039078712463379 m", "val":5.039078712463379}, "y":{"exp":"7.561816692352295 m", "val":7.561816692352295}, "heading":{"exp":"0 deg", "val":0.0}, "intervals":34, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, - {"x":{"exp":"7.840170383453369 m", "val":7.840170383453369}, "y":{"exp":"7.278725624084473 m", "val":7.278725624084473}, "heading":{"exp":"-90 deg", "val":-1.5707963267948966}, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], + {"x":{"exp":"5.039078712463379 m", "val":5.039078712463379}, "y":{"exp":"7.586472 m", "val":7.586472}, "heading":{"exp":"0 deg", "val":0.0}, "intervals":37, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, + {"x":{"exp":"8.256524 m", "val":8.256524}, "y":{"exp":"(8 - 0.634238) m", "val":7.365762}, "heading":{"exp":"-90 deg", "val":-1.5707963267948966}, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], "constraints":[ {"from":"first", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, {"from":"last", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, @@ -59,88 +59,91 @@ "differentialTrackWidth":0.5588 }, "sampleType":"Swerve", - "waypoints":[0.0,0.65277,1.5728,2.67788], + "waypoints":[0.0,0.65252,1.57274,2.79473], "samples":[ - {"t":0.0, "x":2.81808, "y":6.5, "heading":0.65, "vx":0.0, "vy":0.0, "omega":0.0, "ax":2.74987, "ay":9.0137, "alpha":-0.22952, "fx":[48.65619,46.84141,44.88146,46.71872], "fy":[152.74119,153.30943,153.89054,153.3407]}, - {"t":0.02967, "x":2.81929, "y":6.50397, "heading":0.65, "vx":0.08159, "vy":0.26745, "omega":-0.00681, "ax":2.74923, "ay":9.01155, "alpha":-0.24249, "fx":[48.75087,46.83486,44.76391,46.70499], "fy":[152.67189,153.27293,153.88549,153.30496]}, - {"t":0.05934, "x":2.82292, "y":6.51587, "heading":0.6498, "vx":0.16316, "vy":0.53483, "omega":-0.01401, "ax":2.74842, "ay":9.0085, "alpha":-0.26071, "fx":[48.88485,46.82748,44.60033,46.68663], "fy":[152.57431,153.22138,153.87796,153.25457]}, - {"t":0.08901, "x":2.82898, "y":6.5357, "heading":0.64938, "vx":0.24471, "vy":0.80212, "omega":-0.02174, "ax":2.74729, "ay":9.0039, "alpha":-0.2881, "fx":[49.08738,46.81849,44.35623,46.66031], "fy":[152.42722,153.14363,153.86595,153.17839]}, - {"t":0.11868, "x":2.83745, "y":6.56347, "heading":0.64874, "vx":0.32623, "vy":1.06928, "omega":-0.03029, "ax":2.74551, "ay":8.99618, "alpha":-0.33383, "fx":[49.42638,46.80607,43.95097,46.61826], "fy":[152.18101,153.01383,153.84455,153.05031]}, - {"t":0.14836, "x":2.84833, "y":6.59915, "heading":0.64784, "vx":0.40769, "vy":1.33621, "omega":-0.04019, "ax":2.74208, "ay":8.98062, "alpha":-0.42539, "fx":[50.10385,46.78481,43.14235,46.53697], "fy":[151.68659,152.75537,153.7979,152.79127]}, - {"t":0.17803, "x":2.86164, "y":6.64275, "heading":0.64665, "vx":0.48905, "vy":1.60267, "omega":-0.05282, "ax":2.7315, "ay":8.93348, "alpha":-0.70026, "fx":[52.10695,46.72811,40.71679,46.29654], "fy":[150.19714,151.99612,153.63248,151.9978]}, - {"t":0.2077, "x":2.87735, "y":6.69424, "heading":0.64508, "vx":0.5701, "vy":1.86774, "omega":-0.07359, "ax":0.36563, "ay":1.52671, "alpha":-27.89076, "fx":[117.22679,43.75036,-107.68153,-28.4189], "fy":[40.78615,120.2163,37.5023,-94.62929]}, - {"t":0.23737, "x":2.89443, "y":6.75033, "heading":0.6429, "vx":0.58095, "vy":1.91304, "omega":-0.90115, "ax":0.04333, "ay":-0.01289, "alpha":-26.2583, "fx":[106.5574,35.80749,-105.69589,-33.721], "fy":[4.96272,100.13407,-5.30358,-100.67047]}, - {"t":0.26704, "x":2.91168, "y":6.80709, "heading":0.61616, "vx":0.58223, "vy":1.91266, "omega":-1.68026, "ax":0.00544, "ay":-0.00166, "alpha":-22.06297, "fx":[89.31937,31.5768,-89.18861,-31.3373], "fy":[1.90134,83.51643,-1.94937,-83.5816]}, - {"t":0.29671, "x":2.92896, "y":6.86384, "heading":0.5663, "vx":0.5824, "vy":1.91261, "omega":-2.3349, "ax":-0.00525, "ay":0.00159, "alpha":-15.93228, "fx":[64.36628,25.59276,-64.51761,-25.79855], "fy":[-1.8005,59.15535,1.84881,-59.09574]}, - {"t":0.32638, "x":2.94624, "y":6.92059, "heading":0.49702, "vx":0.58224, "vy":1.91266, "omega":-2.80763, "ax":-0.00724, "ay":0.0022, "alpha":-8.02992, "fx":[32.21879,14.85484,-32.4559,-15.11066], "fy":[-3.13015,28.87015,3.20198,-28.79257]}, - {"t":0.35605, "x":2.96351, "y":6.97734, "heading":0.41372, "vx":0.58202, "vy":1.91272, "omega":-3.04588, "ax":-0.00192, "ay":0.00059, "alpha":0.48086, "fx":[-1.94668,-1.07036,1.88135,1.00501], "fy":[0.36002,-1.63589,-0.33992,1.656]}, - {"t":0.38573, "x":2.98078, "y":7.03409, "heading":0.32334, "vx":0.58197, "vy":1.91274, "omega":-3.03162, "ax":0.01425, "ay":-0.00432, "alpha":9.36976, "fx":[-36.29568,-22.77824,36.75953,23.28408], "fy":[10.09119,-30.19794,-10.22228,30.03501]}, - {"t":0.4154, "x":2.99805, "y":7.09084, "heading":0.23339, "vx":0.58239, "vy":1.91261, "omega":-2.7536, "ax":0.08158, "ay":-0.02488, "alpha":17.38442, "fx":[-64.58938,-46.01312,67.01665,49.1365], "fy":[24.63926,-52.44744,-25.07484,51.19015]}, - {"t":0.44507, "x":3.01537, "y":7.14758, "heading":0.15169, "vx":0.58481, "vy":1.91187, "omega":-2.23779, "ax":0.36421, "ay":-0.11251, "alpha":23.07194, "fx":[-79.18762,-61.11041,89.38696,75.69126], "fy":[40.1389,-67.43046,-40.05992,59.69618]}, - {"t":0.47474, "x":3.03288, "y":7.20426, "heading":0.08529, "vx":0.59562, "vy":1.90853, "omega":-1.55322, "ax":1.34611, "ay":-0.43572, "alpha":25.90723, "fx":[-71.55137,-51.92413,109.13294,105.93006], "fy":[56.13155,-86.70324,-50.4829,51.40874]}, - {"t":0.50441, "x":3.05115, "y":7.2607, "heading":0.0392, "vx":0.63556, "vy":1.8956, "omega":-0.78452, "ax":5.03982, "ay":-1.91764, "alpha":16.99426, "fx":[50.15142,30.72743,125.77925,136.24522], "fy":[31.39776,-116.12399,-60.33508,14.58713]}, - {"t":0.53408, "x":3.07222, "y":7.3161, "heading":0.01593, "vx":0.7851, "vy":1.83871, "omega":-0.28028, "ax":7.73395, "ay":-3.9095, "alpha":4.56853, "fx":[134.06678,110.33081,134.78171,147.02998], "fy":[-58.41491,-100.08277,-70.94863,-36.5515]}, - {"t":0.56375, "x":3.09892, "y":7.36893, "heading":0.00761, "vx":1.01457, "vy":1.72271, "omega":-0.14472, "ax":7.55974, "ay":-5.17809, "alpha":2.25017, "fx":[128.69225,117.07141,129.82063,138.7717], "fy":[-87.91861,-103.54308,-88.14818,-72.70125]}, - {"t":0.59342, "x":3.13236, "y":7.41777, "heading":0.00332, "vx":1.23888, "vy":1.56907, "omega":-0.07796, "ax":6.88143, "ay":-6.257, "alpha":1.48812, "fx":[115.90687,108.80571,118.56053,124.93178], "fy":[-107.76478,-115.12366,-105.32239,-97.50855]}, - {"t":0.6231, "x":3.17214, "y":7.46157, "heading":0.001, "vx":1.44306, "vy":1.38341, "omega":-0.0338, "ax":5.96736, "ay":-7.20569, "alpha":1.13925, "fx":[99.63563,94.90766,103.43958,108.02964], "fy":[-124.16068,-127.89028,-121.18119,-117.03474]}, - {"t":0.65277, "x":3.21759, "y":7.49945, "heading":0.0, "vx":1.62012, "vy":1.16961, "omega":0.0, "ax":4.77606, "ay":-8.06936, "alpha":0.0, "fx":[81.23938,81.23938,81.23938,81.23938], "fy":[-137.25745,-137.25745,-137.25745,-137.25745]}, - {"t":0.69277, "x":3.28622, "y":7.53978, "heading":0.0, "vx":1.81117, "vy":0.84682, "omega":0.0, "ax":3.14158, "ay":-8.7583, "alpha":0.0, "fx":[53.43743,53.43743,53.43743,53.43743], "fy":[-148.97614,-148.97614,-148.97614,-148.97614]}, - {"t":0.73277, "x":3.36118, "y":7.56664, "heading":0.0, "vx":1.93684, "vy":0.49648, "omega":0.0, "ax":1.43852, "ay":-8.83866, "alpha":0.0, "fx":[24.46882,24.46882,24.46882,24.46882], "fy":[-150.34306,-150.34306,-150.34306,-150.34306]}, - {"t":0.77277, "x":3.43981, "y":7.57943, "heading":0.0, "vx":1.99438, "vy":0.14292, "omega":0.0, "ax":0.12214, "ay":-4.3396, "alpha":0.0, "fx":[2.0775,2.0775,2.0775,2.0775], "fy":[-73.81542,-73.81542,-73.81542,-73.81542]}, - {"t":0.81277, "x":3.51968, "y":7.58168, "heading":0.0, "vx":1.99926, "vy":-0.03067, "omega":0.0, "ax":-0.00358, "ay":-0.20572, "alpha":0.0, "fx":[-0.06085,-0.06085,-0.06085,-0.06085], "fy":[-3.49922,-3.49922,-3.49922,-3.49922]}, - {"t":0.85277, "x":3.59965, "y":7.58029, "heading":0.0, "vx":1.99912, "vy":-0.0389, "omega":0.0, "ax":0.00107, "ay":0.05659, "alpha":0.0, "fx":[0.01817,0.01817,0.01817,0.01817], "fy":[0.96256,0.96256,0.96256,0.96256]}, - {"t":0.89278, "x":3.67962, "y":7.57877, "heading":0.0, "vx":1.99916, "vy":-0.03664, "omega":0.0, "ax":0.00116, "ay":0.0654, "alpha":0.0, "fx":[0.01965,0.01965,0.01965,0.01965], "fy":[1.11247,1.11247,1.11247,1.11247]}, - {"t":0.93278, "x":3.75959, "y":7.57736, "heading":0.0, "vx":1.99921, "vy":-0.03402, "omega":0.0, "ax":0.00102, "ay":0.06219, "alpha":0.0, "fx":[0.01733,0.01733,0.01733,0.01733], "fy":[1.0579,1.0579,1.0579,1.0579]}, - {"t":0.97278, "x":3.83957, "y":7.57605, "heading":0.0, "vx":1.99925, "vy":-0.03154, "omega":0.0, "ax":0.00089, "ay":0.05897, "alpha":0.0, "fx":[0.01522,0.01522,0.01522,0.01522], "fy":[1.00306,1.00306,1.00306,1.00306]}, - {"t":1.01278, "x":3.91954, "y":7.57484, "heading":0.0, "vx":1.99929, "vy":-0.02918, "omega":0.0, "ax":0.00079, "ay":0.05619, "alpha":0.0, "fx":[0.0134,0.0134,0.0134,0.0134], "fy":[0.95586,0.95586,0.95586,0.95586]}, - {"t":1.05278, "x":3.99952, "y":7.57371, "heading":0.0, "vx":1.99932, "vy":-0.02693, "omega":0.0, "ax":0.0007, "ay":0.05379, "alpha":0.0, "fx":[0.01182,0.01182,0.01182,0.01182], "fy":[0.91503,0.91503,0.91503,0.91503]}, - {"t":1.09278, "x":4.07949, "y":7.57268, "heading":0.0, "vx":1.99935, "vy":-0.02478, "omega":0.0, "ax":0.00061, "ay":0.05169, "alpha":0.0, "fx":[0.01043,0.01043,0.01043,0.01043], "fy":[0.87932,0.87932,0.87932,0.87932]}, - {"t":1.13279, "x":4.15947, "y":7.57173, "heading":0.0, "vx":1.99937, "vy":-0.02271, "omega":0.0, "ax":0.00054, "ay":0.04984, "alpha":0.0, "fx":[0.0092,0.0092,0.0092,0.0092], "fy":[0.84777,0.84777,0.84777,0.84777]}, - {"t":1.17279, "x":4.23945, "y":7.57086, "heading":0.0, "vx":1.99939, "vy":-0.02072, "omega":0.0, "ax":0.00048, "ay":0.04819, "alpha":0.0, "fx":[0.00809,0.00809,0.00809,0.00809], "fy":[0.81968,0.81968,0.81968,0.81968]}, - {"t":1.21279, "x":4.31943, "y":7.57007, "heading":0.0, "vx":1.99941, "vy":-0.01879, "omega":0.0, "ax":0.00042, "ay":0.04671, "alpha":0.0, "fx":[0.00709,0.00709,0.00709,0.00709], "fy":[0.79449,0.79449,0.79449,0.79449]}, - {"t":1.25279, "x":4.39941, "y":7.56936, "heading":0.0, "vx":1.99943, "vy":-0.01692, "omega":0.0, "ax":0.00036, "ay":0.04537, "alpha":0.0, "fx":[0.00618,0.00618,0.00618,0.00618], "fy":[0.77178,0.77178,0.77178,0.77178]}, - {"t":1.29279, "x":4.47939, "y":7.56872, "heading":0.0, "vx":1.99944, "vy":-0.0151, "omega":0.0, "ax":0.00031, "ay":0.04416, "alpha":0.0, "fx":[0.00534,0.00534,0.00534,0.00534], "fy":[0.75119,0.75119,0.75119,0.75119]}, - {"t":1.33279, "x":4.55937, "y":7.56815, "heading":0.0, "vx":1.99946, "vy":-0.01334, "omega":0.0, "ax":0.00027, "ay":0.04306, "alpha":0.0, "fx":[0.00457,0.00457,0.00457,0.00457], "fy":[0.73242,0.73242,0.73242,0.73242]}, - {"t":1.3728, "x":4.63935, "y":7.56765, "heading":0.0, "vx":1.99947, "vy":-0.01162, "omega":0.0, "ax":0.00023, "ay":0.04202, "alpha":0.0, "fx":[0.00385,0.00385,0.00385,0.00385], "fy":[0.7147,0.7147,0.7147,0.7147]}, - {"t":1.4128, "x":4.71933, "y":7.56722, "heading":0.0, "vx":1.99947, "vy":-0.00993, "omega":0.0, "ax":0.00018, "ay":0.04039, "alpha":0.0, "fx":[0.00313,0.00313,0.00313,0.00313], "fy":[0.68706,0.68706,0.68706,0.68706]}, - {"t":1.4528, "x":4.79932, "y":7.56685, "heading":0.0, "vx":1.99948, "vy":-0.00832, "omega":0.0, "ax":0.0001, "ay":0.02564, "alpha":0.0, "fx":[0.0017,0.0017,0.0017,0.0017], "fy":[0.43617,0.43617,0.43617,0.43617]}, - {"t":1.4928, "x":4.8793, "y":7.56654, "heading":0.0, "vx":1.99949, "vy":-0.00729, "omega":0.0, "ax":-0.00154, "ay":-0.24973, "alpha":0.0, "fx":[-0.02617,-0.02617,-0.02617,-0.02617], "fy":[-4.24786,-4.24786,-4.24786,-4.24786]}, - {"t":1.5328, "x":4.95928, "y":7.56605, "heading":0.0, "vx":1.99942, "vy":-0.01728, "omega":0.0, "ax":-0.22756, "ay":-4.42469, "alpha":0.0, "fx":[-3.87074,-3.87074,-3.87074,-3.87074], "fy":[-75.26274,-75.26274,-75.26274,-75.26274]}, - {"t":1.5728, "x":5.03908, "y":7.56182, "heading":0.0, "vx":1.99032, "vy":-0.19428, "omega":0.0, "ax":5.8475, "ay":-0.38744, "alpha":-24.69788, "fx":[149.76682,155.86086,55.86284,36.36685], "fy":[-56.82103,36.90031,148.94571,-155.38576]}, - {"t":1.60531, "x":5.10686, "y":7.5553, "heading":0.0, "vx":2.18038, "vy":-0.20687, "omega":-0.80274, "ax":6.51253, "ay":-0.55698, "alpha":-21.62528, "fx":[149.92546,156.46597,83.46232,53.25123], "fy":[-55.92821,33.41971,134.59561,-149.98325]}, - {"t":1.63781, "x":5.18116, "y":7.54828, "heading":-0.02609, "vx":2.39205, "vy":-0.22497, "omega":-1.5056, "ax":7.86245, "ay":-1.18854, "alpha":-14.03703, "fx":[149.73853,158.0251,134.77137,92.4174], "fy":[-55.06472,21.64969,80.81497,-128.26659]}, - {"t":1.67031, "x":5.26306, "y":7.54034, "heading":-0.07503, "vx":2.6476, "vy":-0.2636, "omega":-1.96184, "ax":8.0221, "ay":-2.63245, "alpha":9.18779, "fx":[149.69624,97.57143,142.03611,156.51041], "fy":[9.18681,-117.93651,-66.44544,-3.91341]}, - {"t":1.70281, "x":5.35335, "y":7.53038, "heading":-0.13879, "vx":2.90833, "vy":-0.34916, "omega":-1.66322, "ax":6.41864, "ay":-1.27285, "alpha":19.13775, "fx":[95.98602,47.31861,139.28353,154.12856], "fy":[103.68492,-137.09703,-67.90644,14.71511]}, - {"t":1.73532, "x":5.45127, "y":7.51836, "heading":-0.19285, "vx":3.11695, "vy":-0.39053, "omega":-1.0412, "ax":6.28111, "ay":-0.48943, "alpha":17.91609, "fx":[79.99699,60.49011,138.45281,148.41973], "fy":[108.07054,-107.14117,-57.04194,22.81246]}, - {"t":1.76782, "x":5.5559, "y":7.50541, "heading":-0.22669, "vx":3.3211, "vy":-0.40644, "omega":-0.45888, "ax":4.51751, "ay":0.94314, "alpha":12.28906, "fx":[47.68084,38.27308,108.74363,112.66877], "fy":[72.7469,-11.8564,-26.73889,30.01889]}, - {"t":1.80032, "x":5.66623, "y":7.4927, "heading":-0.2416, "vx":3.46793, "vy":-0.37579, "omega":-0.05946, "ax":0.21857, "ay":0.55992, "alpha":0.44893, "fx":[2.49453,2.01196,4.94364,5.42118], "fy":[10.86831,8.90184,8.17979,10.14645]}, - {"t":1.83282, "x":5.77906, "y":7.48078, "heading":-0.24354, "vx":3.47504, "vy":-0.35759, "omega":-0.04487, "ax":0.02174, "ay":0.1755, "alpha":0.01224, "fx":[0.336,0.3235,0.40347,0.41594], "fy":[3.02139,2.96734,2.9489,3.00292]}, - {"t":1.86532, "x":5.89201, "y":7.46925, "heading":-0.245, "vx":3.47574, "vy":-0.35188, "omega":-0.04447, "ax":0.00979, "ay":0.08811, "alpha":0.00366, "fx":[0.1577,0.15207,0.17531,0.18092], "fy":[1.51077,1.49508,1.48679,1.50249]}, - {"t":1.89783, "x":6.00499, "y":7.45786, "heading":-0.24644, "vx":3.47606, "vy":-0.34902, "omega":-0.04435, "ax":0.00736, "ay":0.06613, "alpha":0.00308, "fx":[0.11704,0.1133,0.13325,0.13699], "fy":[1.13435,1.1209,1.11541,1.12888]}, - {"t":1.93033, "x":6.11797, "y":7.44655, "heading":-0.24788, "vx":3.4763, "vy":-0.34687, "omega":-0.04425, "ax":0.00652, "ay":0.05826, "alpha":0.00299, "fx":[0.10542,0.09827,0.1163,0.12346], "fy":[1.00229,0.99015,0.97959,0.99178]}, - {"t":1.96283, "x":6.23096, "y":7.43531, "heading":-0.24932, "vx":3.47651, "vy":-0.34498, "omega":-0.04416, "ax":0.00604, "ay":0.0537, "alpha":0.00297, "fx":[0.09431,0.09156,0.11108,0.11386], "fy":[0.92199,0.90883,0.90479,0.91799]}, - {"t":1.99533, "x":6.34396, "y":7.42412, "heading":-0.25076, "vx":3.47671, "vy":-0.34323, "omega":-0.04406, "ax":0.00562, "ay":0.04971, "alpha":0.00294, "fx":[0.08929,0.0836,0.10179,0.10753], "fy":[0.85595,0.84368,0.83528,0.84761]}, - {"t":2.02784, "x":6.45696, "y":7.41299, "heading":-0.25219, "vx":3.47689, "vy":-0.34162, "omega":-0.04396, "ax":0.00497, "ay":0.04363, "alpha":0.00283, "fx":[0.07469,0.07477,0.09437,0.09435], "fy":[0.74864,0.73542,0.73555,0.74882]}, - {"t":2.06034, "x":6.56997, "y":7.40191, "heading":-0.25362, "vx":3.47705, "vy":-0.3402, "omega":-0.04387, "ax":0.00297, "ay":0.02494, "alpha":0.00229, "fx":[0.0429,0.04244,0.05814,0.05865], "fy":[0.42979,0.4192,0.41854,0.42918]}, - {"t":2.09284, "x":6.68299, "y":7.39087, "heading":-0.25504, "vx":3.47715, "vy":-0.33939, "omega":-0.0438, "ax":-0.00644, "ay":-0.05708, "alpha":-0.00194, "fx":[-0.11038,-0.09957,-0.10889,-0.11947], "fy":[-0.98216,-0.97565,-0.95975,-0.96608]}, - {"t":2.12534, "x":6.796, "y":7.37981, "heading":-0.25647, "vx":3.47694, "vy":-0.34124, "omega":-0.04386, "ax":-0.09649, "ay":-0.4447, "alpha":-0.14499, "fx":[-1.26011,-1.0844,-2.02271,-2.19796], "fy":[-8.00994,-7.37864,-7.11848,-7.74987]}, - {"t":2.15784, "x":6.90896, "y":7.36848, "heading":-0.25789, "vx":3.4738, "vy":-0.3557, "omega":-0.04857, "ax":-2.76926, "ay":-1.67598, "alpha":-7.10493, "fx":[-28.19952,-21.26632,-67.99334,-70.95835], "fy":[-53.69384,-20.71135,-4.40356,-35.22279]}, - {"t":2.19035, "x":7.0204, "y":7.35603, "heading":-0.25947, "vx":3.3838, "vy":-0.41017, "omega":-0.2795, "ax":-6.23758, "ay":-0.55063, "alpha":-16.75003, "fx":[-69.14759,-71.02852,-139.07347,-145.14844], "fy":[-117.08031,62.85142,47.78782,-31.0229]}, - {"t":2.22285, "x":7.12709, "y":7.34241, "heading":-0.26856, "vx":3.18106, "vy":-0.42807, "omega":-0.82391, "ax":-6.70425, "ay":0.28695, "alpha":-17.66956, "fx":[-83.90697,-77.40855,-142.21245,-152.6218], "fy":[-120.28023,104.29221,58.70459,-23.193]}, - {"t":2.25535, "x":7.22694, "y":7.32865, "heading":-0.29533, "vx":2.96316, "vy":-0.41874, "omega":-1.39821, "ax":-6.92236, "ay":0.90061, "alpha":-17.53049, "fx":[-98.59495,-75.83775,-140.70283,-155.85409], "fy":[-113.92481,120.87874,67.76588,-13.44309]}, - {"t":2.28785, "x":7.31959, "y":7.31552, "heading":-0.34078, "vx":2.73817, "vy":-0.38947, "omega":-1.96799, "ax":-7.03808, "ay":1.45166, "alpha":-17.09281, "fx":[-111.63501,-72.41126,-137.35926,-157.45746], "fy":[-104.53567,129.426,76.70145,-2.82261]}, - {"t":2.32035, "x":7.40487, "y":7.30362, "heading":-0.40474, "vx":2.50941, "vy":-0.34229, "omega":-2.52354, "ax":-7.06339, "ay":1.93546, "alpha":-16.71843, "fx":[-121.16453,-68.55495,-132.92678,-157.939], "fy":[-95.92608,134.52302,85.40979,7.67955]}, - {"t":2.35286, "x":7.4827, "y":7.29352, "heading":-0.48676, "vx":2.27984, "vy":-0.27938, "omega":-3.06693, "ax":-6.99439, "ay":2.31406, "alpha":-16.66761, "fx":[-126.18995,-64.36213,-127.72,-157.61834], "fy":[-91.35427,137.77521,93.7532,17.27161]}, - {"t":2.38536, "x":7.5531, "y":7.28566, "heading":-0.58645, "vx":2.0525, "vy":-0.20417, "omega":-3.60866, "ax":-6.82104, "ay":2.55994, "alpha":-17.10734, "fx":[-126.44745,-59.01924,-121.79688,-156.83211], "fy":[-92.72525,139.70465,101.78387,25.41218]}, - {"t":2.41786, "x":7.61621, "y":7.28038, "heading":-0.70374, "vx":1.8308, "vy":-0.12096, "omega":-4.16469, "ax":-6.58597, "ay":2.63963, "alpha":-17.80837, "fx":[-122.77747,-54.21537,-115.21563,-155.89338], "fy":[-98.90471,137.18537,109.45764,31.85903]}, - {"t":2.45036, "x":7.67224, "y":7.27784, "heading":-0.8391, "vx":1.61675, "vy":-0.03517, "omega":-4.7435, "ax":-9.11122, "ay":1.61332, "alpha":3.983, "fx":[-147.92189,-156.40636,-158.98388,-156.60451], "fy":[59.11596,30.90079,-6.56095,26.31263]}, - {"t":2.48287, "x":7.71997, "y":7.27755, "heading":-0.99327, "vx":1.32061, "vy":0.01727, "omega":-4.61404, "ax":-8.42506, "ay":0.38284, "alpha":12.90417, "fx":[-123.21486,-155.09757,-142.35609,-152.56293], "fy":[101.8515,39.36068,-72.54482,-42.61969]}, - {"t":2.51537, "x":7.75845, "y":7.27832, "heading":-1.14324, "vx":1.04678, "vy":0.02971, "omega":-4.19463, "ax":-7.29153, "ay":-0.49981, "alpha":20.92708, "fx":[-105.24908,-151.82706,-135.84578,-103.18573], "fy":[120.57746,51.07454,-84.65864,-120.99957]}, - {"t":2.54787, "x":7.78862, "y":7.27902, "heading":-1.27957, "vx":0.80979, "vy":0.01346, "omega":-3.51445, "ax":-6.59116, "ay":-0.45373, "alpha":25.08633, "fx":[-90.17926,-147.70819,-136.38234,-74.18542], "fy":[132.33696,62.19907,-84.05633,-141.35105]}, - {"t":2.58037, "x":7.81146, "y":7.27921, "heading":-1.3938, "vx":0.59556, "vy":-0.00128, "omega":-2.69909, "ax":-6.26937, "ay":-0.20037, "alpha":26.8876, "fx":[-77.36302,-143.46707,-138.76554,-66.96513], "fy":[140.2763,71.5475,-80.21483,-145.24183]}, - {"t":2.61287, "x":7.8275, "y":7.27907, "heading":-1.48153, "vx":0.39179, "vy":-0.0078, "omega":-1.82519, "ax":-6.08806, "ay":0.03626, "alpha":27.80711, "fx":[-66.95375,-139.70797,-141.07911,-66.48391], "fy":[145.57318,78.70051,-76.17233,-145.63405]}, - {"t":2.64538, "x":7.83702, "y":7.27883, "heading":-1.54085, "vx":0.19392, "vy":-0.00662, "omega":-0.9214, "ax":-5.96628, "ay":0.20358, "alpha":28.34874, "fx":[-59.23812,-136.87976,-142.68127,-67.13948], "fy":[148.91196,83.5672,-73.19651,-145.43116]}, - {"t":2.67788, "x":7.84017, "y":7.27873, "heading":-1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0,0.0,0.0,0.0]}], + {"t":0.0, "x":2.81808, "y":6.5, "heading":0.65, "vx":0.0, "vy":0.0, "omega":0.0, "ax":2.7666, "ay":9.00859, "alpha":-0.22418, "fx":[48.8975,47.12085,45.20984,47.00819], "fy":[152.66396,153.22358,153.79428,153.25215]}, + {"t":0.02966, "x":2.8193, "y":6.50396, "heading":0.6499, "vx":0.08206, "vy":0.26719, "omega":-0.00665, "ax":2.76596, "ay":9.00644, "alpha":-0.23693, "fx":[48.99023,47.11411,45.09407,46.9944], "fy":[152.59508,153.18709,153.78894,153.21643]}, + {"t":0.05932, "x":2.82295, "y":6.51585, "heading":0.6496, "vx":0.1641, "vy":0.53432, "omega":-0.01368, "ax":2.76514, "ay":9.0034, "alpha":-0.25483, "fx":[49.12147,47.10641,44.93303,46.97601], "fy":[152.49809,153.13558,153.78099,153.16604]}, + {"t":0.08898, "x":2.82903, "y":6.53566, "heading":0.64908, "vx":0.24611, "vy":0.80136, "omega":-0.02123, "ax":2.764, "ay":8.9988, "alpha":-0.28174, "fx":[49.31986,47.09688,44.69276,46.94972], "fy":[152.3519,153.05789,153.76835,153.08985]}, + {"t":0.11864, "x":2.83755, "y":6.56338, "heading":0.64833, "vx":0.32809, "vy":1.06827, "omega":-0.02959, "ax":2.7622, "ay":8.99108, "alpha":-0.32665, "fx":[49.65194,47.0835,44.29397,46.90782], "fy":[152.10722,152.92822,153.74593,152.96174]}, + {"t":0.1483, "x":2.84849, "y":6.59902, "heading":0.64731, "vx":0.41002, "vy":1.33494, "omega":-0.03928, "ax":2.75874, "ay":8.97555, "alpha":-0.41657, "fx":[50.31563,47.06024,43.49841,46.82693], "fy":[151.61597,152.66999,153.69729,152.70267]}, + {"t":0.17796, "x":2.86187, "y":6.64257, "heading":0.64596, "vx":0.49184, "vy":1.60116, "omega":-0.05163, "ax":2.74808, "ay":8.92849, "alpha":-0.68646, "fx":[52.2779,46.9976,41.11301,46.58792], "fy":[150.13673,151.91129,153.52663,151.90944]}, + {"t":0.20762, "x":2.87767, "y":6.69398, "heading":0.64413, "vx":0.57335, "vy":1.86598, "omega":-0.072, "ax":0.37665, "ay":1.55007, "alpha":-27.69724, "fx":[116.67881,43.75127,-106.73902,-28.06408], "fy":[40.97705,119.80877,38.11558,-93.43609]}, + {"t":0.23728, "x":2.89484, "y":6.75001, "heading":0.62981, "vx":0.58452, "vy":1.91195, "omega":-0.89349, "ax":0.04287, "ay":-0.01284, "alpha":-26.10547, "fx":[105.99641,36.89312,-105.13689,-34.83572], "fy":[3.55924,99.08409,-3.88642,-99.63065]}, + {"t":0.26694, "x":2.91219, "y":6.80671, "heading":0.59182, "vx":0.58579, "vy":1.91157, "omega":-1.66778, "ax":0.00522, "ay":-0.00161, "alpha":-21.95407, "fx":[88.89677,33.42796,-88.77078,-33.19905], "fy":[-0.26572,82.31856,0.22167,-82.38373]}, + {"t":0.2966, "x":2.92957, "y":6.86341, "heading":0.5327, "vx":0.58595, "vy":1.91152, "omega":-2.31894, "ax":-0.00535, "ay":0.00163, "alpha":-15.94484, "fx":[64.31793,27.58212,-64.47235,-27.79149], "fy":[-3.96509,58.30747,4.01286,-58.24449]}, + {"t":0.32626, "x":2.94695, "y":6.92011, "heading":0.45691, "vx":0.58579, "vy":1.91157, "omega":-2.79186, "ax":-0.00721, "ay":0.0022, "alpha":-8.20242, "fx":[32.75799,16.34442,-32.99395,-16.59935], "fy":[-4.51889,28.85343,4.59007,-28.77475]}, + {"t":0.35592, "x":2.96432, "y":6.9768, "heading":0.37049, "vx":0.58557, "vy":1.91164, "omega":-3.03514, "ax":-0.00193, "ay":0.0006, "alpha":0.25362, "fx":[-1.03344,-0.61718,0.9677,0.55142], "fy":[0.2382,-0.83347,-0.21786,0.85382]}, + {"t":0.38558, "x":2.98169, "y":7.0335, "heading":0.28058, "vx":0.58552, "vy":1.91165, "omega":-3.02762, "ax":0.01285, "ay":-0.00392, "alpha":9.08723, "fx":[-34.76224,-23.33839,35.18274,23.79231], "fy":[11.29727,-28.30057,-11.41571,28.15214]}, + {"t":0.41524, "x":2.99906, "y":7.0902, "heading":0.19478, "vx":0.5859, "vy":1.91154, "omega":-2.75809, "ax":0.07378, "ay":-0.02265, "alpha":17.03097, "fx":[-62.36042,-47.14028,64.59333,49.92751], "fy":[26.62758,-49.50063,-27.01962,48.35188]}, + {"t":0.4449, "x":3.01647, "y":7.14689, "heading":0.12047, "vx":0.58809, "vy":1.91087, "omega":-2.25296, "ax":0.33489, "ay":-0.104, "alpha":22.73125, "fx":[-76.96166,-62.75368,86.58586,75.91509], "fy":[42.10254,-64.02971,-42.04604,56.89702]}, + {"t":0.47456, "x":3.03406, "y":7.20352, "heading":0.06364, "vx":0.59802, "vy":1.90778, "omega":-1.57875, "ax":1.25752, "ay":-0.40783, "alpha":25.72733, "fx":[-70.68441,-55.32547,106.60005,104.97028], "fy":[57.30659,-83.02578,-52.01896,49.99004]}, + {"t":0.50422, "x":3.05235, "y":7.25992, "heading":0.02813, "vx":0.63532, "vy":1.89569, "omega":-0.81568, "ax":4.76379, "ay":-1.7997, "alpha":17.89537, "fx":[40.01275,24.77699,124.3565,134.9769], "fy":[37.70911,-115.00587,-60.70525,15.55275]}, + {"t":0.53388, "x":3.07329, "y":7.31536, "heading":0.01181, "vx":0.77661, "vy":1.84231, "omega":-0.2849, "ax":7.70701, "ay":-3.84736, "alpha":4.72327, "fx":[133.71209,109.23768,134.53731,146.88896], "fy":[-56.41515,-100.06466,-70.38368,-34.90659]}, + {"t":0.56354, "x":3.09971, "y":7.36831, "heading":0.00544, "vx":1.0052, "vy":1.72819, "omega":-0.14481, "ax":7.57883, "ay":-5.12958, "alpha":2.26939, "fx":[129.1026,117.34043,130.10589,139.10609], "fy":[-86.92426,-102.94138,-87.44793,-71.69703]}, + {"t":0.5932, "x":3.13286, "y":7.41731, "heading":0.00214, "vx":1.22999, "vy":1.57605, "omega":-0.0775, "ax":6.91418, "ay":-6.21481, "alpha":1.48386, "fx":[116.52606,109.40484,119.06531,125.4366], "fy":[-106.98489,-114.45695,-104.65608,-96.7508]}, + {"t":0.62286, "x":3.17238, "y":7.46132, "heading":0.0005, "vx":1.43506, "vy":1.39172, "omega":-0.03349, "ax":6.00832, "ay":-7.16925, "alpha":1.1291, "fx":[100.39145,95.66868,104.0818,108.65699], "fy":[-123.5071,-127.28123,-120.58956,-116.40951]}, + {"t":0.65252, "x":3.21759, "y":7.49945, "heading":0.0, "vx":1.61327, "vy":1.17908, "omega":0.0, "ax":4.82195, "ay":-8.04015, "alpha":0.0, "fx":[82.01992,82.01992,82.01992,82.01992], "fy":[-136.76071,-136.76071,-136.76071,-136.76071]}, + {"t":0.69253, "x":3.28599, "y":7.54019, "heading":0.0, "vx":1.80619, "vy":0.8574, "omega":0.0, "ax":3.19113, "ay":-8.73469, "alpha":0.0, "fx":[54.28014,54.28014,54.28014,54.28014], "fy":[-148.57458,-148.57458,-148.57458,-148.57458]}, + {"t":0.73254, "x":3.36081, "y":7.5675, "heading":0.0, "vx":1.93387, "vy":0.50793, "omega":0.0, "ax":1.4877, "ay":-8.793, "alpha":0.0, "fx":[25.30529,25.30529,25.30529,25.30529], "fy":[-149.56644,-149.56644,-149.56644,-149.56644]}, + {"t":0.77255, "x":3.43938, "y":7.58078, "heading":0.0, "vx":1.99339, "vy":0.15612, "omega":0.0, "ax":0.15256, "ay":-4.03077, "alpha":0.0, "fx":[2.59507,2.59507,2.59507,2.59507], "fy":[-68.56229,-68.56229,-68.56229,-68.56229]}, + {"t":0.81256, "x":3.51925, "y":7.5838, "heading":0.0, "vx":1.99949, "vy":-0.00515, "omega":0.0, "ax":-0.00076, "ay":-0.17596, "alpha":0.0, "fx":[-0.01293,-0.01293,-0.01293,-0.01293], "fy":[-2.99303,-2.99303,-2.99303,-2.99303]}, + {"t":0.85257, "x":3.59925, "y":7.58346, "heading":0.0, "vx":1.99946, "vy":-0.01219, "omega":0.0, "ax":0.00033, "ay":0.06034, "alpha":0.0, "fx":[0.00563,0.00563,0.00563,0.00563], "fy":[1.02636,1.02636,1.02636,1.02636]}, + {"t":0.89258, "x":3.67925, "y":7.58302, "heading":0.0, "vx":1.99948, "vy":-0.00977, "omega":0.0, "ax":0.00029, "ay":0.06903, "alpha":0.0, "fx":[0.00492,0.00492,0.00492,0.00492], "fy":[1.17415,1.17415,1.17415,1.17415]}, + {"t":0.93259, "x":3.75925, "y":7.58268, "heading":0.0, "vx":1.99949, "vy":-0.00701, "omega":0.0, "ax":0.00019, "ay":0.06688, "alpha":0.0, "fx":[0.00322,0.00322,0.00322,0.00322], "fy":[1.13769,1.13769,1.13769,1.13769]}, + {"t":0.97259, "x":3.83925, "y":7.58245, "heading":0.0, "vx":1.9995, "vy":-0.00433, "omega":0.0, "ax":0.0001, "ay":0.06471, "alpha":0.0, "fx":[0.00167,0.00167,0.00167,0.00167], "fy":[1.10066,1.10066,1.10066,1.10066]}, + {"t":1.0126, "x":3.91924, "y":7.58233, "heading":0.0, "vx":1.9995, "vy":-0.00174, "omega":0.0, "ax":0.00001, "ay":0.06293, "alpha":0.0, "fx":[0.00025,0.00025,0.00025,0.00025], "fy":[1.07048,1.07048,1.07048,1.07048]}, + {"t":1.05261, "x":3.99924, "y":7.58231, "heading":0.0, "vx":1.9995, "vy":0.00077, "omega":0.0, "ax":-0.00006, "ay":0.06151, "alpha":0.0, "fx":[-0.00105,-0.00105,-0.00105,-0.00105], "fy":[1.04626,1.04626,1.04626,1.04626]}, + {"t":1.09262, "x":4.07924, "y":7.58239, "heading":0.0, "vx":1.9995, "vy":0.00323, "omega":0.0, "ax":-0.00013, "ay":0.06038, "alpha":0.0, "fx":[-0.00229,-0.00229,-0.00229,-0.00229], "fy":[1.02707,1.02707,1.02707,1.02707]}, + {"t":1.13263, "x":4.15924, "y":7.58257, "heading":0.0, "vx":1.99949, "vy":0.00565, "omega":0.0, "ax":-0.0002, "ay":0.05951, "alpha":0.0, "fx":[-0.00347,-0.00347,-0.00347,-0.00347], "fy":[1.01225,1.01225,1.01225,1.01225]}, + {"t":1.17264, "x":4.23924, "y":7.58284, "heading":0.0, "vx":1.99948, "vy":0.00803, "omega":0.0, "ax":-0.00027, "ay":0.05887, "alpha":0.0, "fx":[-0.00462,-0.00462,-0.00462,-0.00462], "fy":[1.00134,1.00134,1.00134,1.00134]}, + {"t":1.21265, "x":4.31924, "y":7.58321, "heading":0.0, "vx":1.99947, "vy":0.01039, "omega":0.0, "ax":-0.00034, "ay":0.05844, "alpha":0.0, "fx":[-0.00575,-0.00575,-0.00575,-0.00575], "fy":[0.99404,0.99404,0.99404,0.99404]}, + {"t":1.25266, "x":4.39924, "y":7.58368, "heading":0.0, "vx":1.99946, "vy":0.01272, "omega":0.0, "ax":-0.0004, "ay":0.05821, "alpha":0.0, "fx":[-0.00688,-0.00688,-0.00688,-0.00688], "fy":[0.99017,0.99017,0.99017,0.99017]}, + {"t":1.29267, "x":4.47923, "y":7.58423, "heading":0.0, "vx":1.99944, "vy":0.01505, "omega":0.0, "ax":-0.00047, "ay":0.05818, "alpha":0.0, "fx":[-0.00803,-0.00803,-0.00803,-0.00803], "fy":[0.98965,0.98965,0.98965,0.98965]}, + {"t":1.33268, "x":4.55923, "y":7.58488, "heading":0.0, "vx":1.99942, "vy":0.01738, "omega":0.0, "ax":-0.00054, "ay":0.05835, "alpha":0.0, "fx":[-0.00921,-0.00921,-0.00921,-0.00921], "fy":[0.99247,0.99247,0.99247,0.99247]}, + {"t":1.37269, "x":4.63922, "y":7.58562, "heading":0.0, "vx":1.9994, "vy":0.01972, "omega":0.0, "ax":-0.00061, "ay":0.05868, "alpha":0.0, "fx":[-0.01043,-0.01043,-0.01043,-0.01043], "fy":[0.99818,0.99818,0.99818,0.99818]}, + {"t":1.4127, "x":4.71922, "y":7.58646, "heading":0.0, "vx":1.99938, "vy":0.02206, "omega":0.0, "ax":-0.00068, "ay":0.05858, "alpha":0.0, "fx":[-0.01158,-0.01158,-0.01158,-0.01158], "fy":[0.99642,0.99642,0.99642,0.99642]}, + {"t":1.45271, "x":4.79921, "y":7.58739, "heading":0.0, "vx":1.99935, "vy":0.02441, "omega":0.0, "ax":-0.00058, "ay":0.04576, "alpha":0.0, "fx":[-0.00986,-0.00986,-0.00986,-0.00986], "fy":[0.77831,0.77831,0.77831,0.77831]}, + {"t":1.49272, "x":4.87921, "y":7.5884, "heading":0.0, "vx":1.99933, "vy":0.02624, "omega":0.0, "ax":0.00242, "ay":-0.22276, "alpha":0.0, "fx":[0.04122,0.04122,0.04122,0.04122], "fy":[-3.78908,-3.78908,-3.78908,-3.78908]}, + {"t":1.53273, "x":4.9592, "y":7.58927, "heading":0.0, "vx":1.99942, "vy":0.01733, "omega":0.0, "ax":-0.14587, "ay":-4.36427, "alpha":0.0, "fx":[-2.48124,-2.48124,-2.48124,-2.48124], "fy":[-74.235,-74.235,-74.235,-74.235]}, + {"t":1.57274, "x":5.03908, "y":7.58647, "heading":0.0, "vx":1.99359, "vy":-0.15729, "omega":0.0, "ax":5.67554, "ay":-0.3071, "alpha":-25.45322, "fx":[150.17373,155.34362,47.54288,33.09713], "fy":[-55.77145,39.08066,151.92279,-156.12674]}, + {"t":1.60576, "x":5.10802, "y":7.58111, "heading":-0.01388, "vx":2.18103, "vy":-0.16743, "omega":-0.84064, "ax":6.35311, "ay":-0.4789, "alpha":-22.41898, "fx":[150.02706,156.21117,75.62831,50.39177], "fy":[-55.69776,34.68778,139.37321,-150.94677]}, + {"t":1.63879, "x":5.18351, "y":7.57532, "heading":-0.05387, "vx":2.39086, "vy":-0.18325, "omega":-1.58107, "ax":7.80617, "ay":-1.03109, "alpha":-14.53604, "fx":[149.76614,157.95888,129.43352,93.96426], "fy":[-55.03279,22.3455,89.5493,-127.01648]}, + {"t":1.67182, "x":5.26673, "y":7.5687, "heading":-0.11402, "vx":2.64867, "vy":-0.2173, "omega":-2.06115, "ax":7.6691, "ay":-2.21364, "alpha":12.33474, "fx":[141.9758,82.02382,141.04352,156.75338], "fy":[44.54995,-128.36642,-68.85079,2.05345]}, + {"t":1.70485, "x":5.35839, "y":7.56032, "heading":-0.17536, "vx":2.90196, "vy":-0.29041, "omega":-1.65377, "ax":6.55414, "ay":-1.01084, "alpha":18.75411, "fx":[93.90885,57.33752,140.70583,153.98389], "fy":[109.5015,-130.84903,-64.80729,17.37863]}, + {"t":1.73787, "x":5.45781, "y":7.55018, "heading":-0.21975, "vx":3.11842, "vy":-0.32379, "omega":-1.03438, "ax":6.33855, "ay":-0.11807, "alpha":17.52528, "fx":[76.36472,67.52563,139.56328,147.8142], "fy":[113.4121,-94.45891,-53.39637,26.40954]}, + {"t":1.7709, "x":5.56426, "y":7.53942, "heading":-0.24436, "vx":3.32776, "vy":-0.32769, "omega":-0.45558, "ax":4.4591, "ay":1.36284, "alpha":11.87361, "fx":[46.65702,38.28051,107.91226,110.5425], "fy":[76.80327,2.01977,-20.38279,34.28556]}, + {"t":1.80393, "x":5.6766, "y":7.52934, "heading":-0.25293, "vx":3.47503, "vy":-0.28268, "omega":-0.06343, "ax":0.21946, "ay":0.7043, "alpha":0.45555, "fx":[2.50619,1.99191,4.96256,5.47082], "fy":[13.35559,11.37317,10.60363,12.5873]}, + {"t":1.83695, "x":5.79148, "y":7.52039, "heading":-0.25478, "vx":3.48228, "vy":-0.25942, "omega":-0.04838, "ax":0.02206, "ay":0.24872, "alpha":0.01257, "fx":[0.34153,0.32717,0.40879,0.42315], "fy":[4.26878,4.21367,4.19244,4.24755]}, + {"t":1.86998, "x":5.90651, "y":7.51196, "heading":-0.25637, "vx":3.48301, "vy":-0.25121, "omega":-0.04797, "ax":0.01061, "ay":0.13941, "alpha":0.0033, "fx":[0.1716,0.1678,0.18922,0.19302], "fy":[2.38129,2.36681,2.3612,2.37567]}, + {"t":1.90301, "x":6.02154, "y":7.50374, "heading":-0.25795, "vx":3.48336, "vy":-0.2466, "omega":-0.04786, "ax":0.00807, "ay":0.1069, "alpha":0.00288, "fx":[0.12955,0.12622,0.14486,0.14818], "fy":[1.82714,1.81454,1.80962,1.82222]}, + {"t":1.93603, "x":6.13659, "y":7.49565, "heading":-0.25953, "vx":3.48362, "vy":-0.24307, "omega":-0.04776, "ax":0.00702, "ay":0.0925, "alpha":0.00294, "fx":[0.11161,0.10818,0.12726,0.13069], "fy":[1.58241,1.56951,1.56444,1.57734]}, + {"t":1.96906, "x":6.25165, "y":7.48767, "heading":-0.2611, "vx":3.48386, "vy":-0.24002, "omega":-0.04767, "ax":0.00638, "ay":0.0835, "alpha":0.00305, "fx":[0.10052,0.09696,0.11669,0.12025], "fy":[1.42959,1.41625,1.41097,1.42431]}, + {"t":2.00209, "x":6.36671, "y":7.47979, "heading":-0.26268, "vx":3.48407, "vy":-0.23726, "omega":-0.04756, "ax":0.00591, "ay":0.07679, "alpha":0.00312, "fx":[0.09228,0.0886,0.10882,0.1125], "fy":[1.31581,1.30213,1.2967,1.31037]}, + {"t":2.03511, "x":6.48178, "y":7.472, "heading":-0.26425, "vx":3.48426, "vy":-0.23472, "omega":-0.04746, "ax":0.00553, "ay":0.07142, "alpha":0.00317, "fx":[0.08565,0.08189,0.10242,0.10618], "fy":[1.2245,1.21061,1.20506,1.21894]}, + {"t":2.06814, "x":6.59686, "y":7.46428, "heading":-0.26581, "vx":3.48444, "vy":-0.23237, "omega":-0.04736, "ax":0.0052, "ay":0.06691, "alpha":0.0032, "fx":[0.08008,0.07627,0.09697,0.10078], "fy":[1.14793,1.13394,1.1283,1.1423]}, + {"t":2.10117, "x":6.71194, "y":7.45664, "heading":-0.26737, "vx":3.48462, "vy":-0.23016, "omega":-0.04725, "ax":0.00491, "ay":0.06286, "alpha":0.0032, "fx":[0.07506,0.07122,0.09194,0.09578], "fy":[1.079,1.065,1.05932,1.07333]}, + {"t":2.13419, "x":6.82703, "y":7.44908, "heading":-0.26893, "vx":3.48478, "vy":-0.22808, "omega":-0.04715, "ax":0.00456, "ay":0.05819, "alpha":0.00316, "fx":[0.06933,0.06552,0.08594,0.08974], "fy":[0.99944,0.98564,0.98001,0.99382]}, + {"t":2.16722, "x":6.94213, "y":7.44158, "heading":-0.27049, "vx":3.48493, "vy":-0.22616, "omega":-0.04704, "ax":0.00382, "ay":0.04817, "alpha":0.00291, "fx":[0.05727,0.05375,0.07253,0.07606], "fy":[0.82827,0.81556,0.81035,0.82306]}, + {"t":2.20025, "x":7.05722, "y":7.43413, "heading":-0.27204, "vx":3.48505, "vy":-0.22457, "omega":-0.04695, "ax":0.00073, "ay":0.00934, "alpha":0.00117, "fx":[0.00936,0.00794,0.01547,0.01689], "fy":[0.16239,0.1573,0.1552,0.16029]}, + {"t":2.23327, "x":7.17233, "y":7.42672, "heading":-0.27359, "vx":3.48508, "vy":-0.22426, "omega":-0.04691, "ax":-0.02698, "ay":-0.17563, "alpha":-0.04199, "fx":[-0.34904,-0.29761,-0.56872,-0.62014], "fy":[-3.11712,-2.93394,-2.85788,-3.04107]}, + {"t":2.2663, "x":7.28741, "y":7.41922, "heading":-0.27516, "vx":3.48419, "vy":-0.23006, "omega":-0.04829, "ax":-0.99419, "ay":-1.00279, "alpha":-2.4863, "fx":[-10.36095,-7.38627,-23.62438,-26.27218], "fy":[-24.86887,-14.1094,-9.27712,-19.97349]}, + {"t":2.29933, "x":7.40194, "y":7.41107, "heading":-0.27811, "vx":3.45135, "vy":-0.26318, "omega":-0.13041, "ax":-5.97145, "ay":-0.80684, "alpha":-15.71987, "fx":[-66.94485,-64.7453,-134.26929,-140.33094], "fy":[-108.96637,39.79145,44.47639,-30.19816]}, + {"t":2.33235, "x":7.51267, "y":7.40194, "heading":-0.29099, "vx":3.25413, "vy":-0.28983, "omega":-0.64959, "ax":-6.66886, "ay":0.15597, "alpha":-17.26843, "fx":[-82.59817,-78.36161,-141.0907,-151.69103], "fy":[-119.23377,94.21857,58.39489,-22.7679]}, + {"t":2.36538, "x":7.61651, "y":7.39246, "heading":-0.32186, "vx":3.03388, "vy":-0.28468, "omega":-1.21991, "ax":-6.92385, "ay":0.77447, "alpha":-17.32587, "fx":[-96.15788,-79.05927,-140.35855,-155.51527], "fy":[-115.65879,114.51761,67.54215,-13.70689]}, + {"t":2.39841, "x":7.71293, "y":7.38348, "heading":-0.3716, "vx":2.80521, "vy":-0.2591, "omega":-1.79213, "ax":-7.04396, "ay":1.27473, "alpha":-17.06169, "fx":[-107.36087,-77.08621,-137.48788,-157.32772], "fy":[-109.22542,123.90403,76.10708,-4.05433]}, + {"t":2.43144, "x":7.80174, "y":7.37561, "heading":-0.4401, "vx":2.57257, "vy":-0.217, "omega":-2.35562, "ax":-7.06308, "ay":1.68455, "alpha":-16.88447, "fx":[-114.78926,-74.31152,-133.44821,-158.01487], "fy":[-104.0466,128.9226,84.42449,5.31461]}, + {"t":2.46446, "x":7.88285, "y":7.36937, "heading":-0.5271, "vx":2.3393, "vy":-0.16136, "omega":-2.91326, "ax":-6.97611, "ay":1.98546, "alpha":-17.04598, "fx":[-117.60481,-70.58056,-128.48682,-157.97428], "fy":[-102.81743,131.55076,92.59906,13.75629]}, + {"t":2.49749, "x":7.9563, "y":7.36512, "heading":-0.63262, "vx":2.1089, "vy":-0.09579, "omega":-3.47623, "ax":-6.76326, "ay":2.14603, "alpha":-17.69016, "fx":[-115.64051,-64.47977,-122.52207,-157.52198], "fy":[-106.50798,130.83104,100.82516,20.86503]}, + {"t":2.53052, "x":8.02227, "y":7.36313, "heading":-0.75707, "vx":1.88553, "vy":-0.02491, "omega":-4.06048, "ax":-6.61708, "ay":0.94684, "alpha":-18.76733, "fx":[-110.79676,-66.74478,-115.75135,-156.92534], "fy":[-112.69195,41.88751,108.79433,26.43211]}, + {"t":2.56354, "x":8.08093, "y":7.36282, "heading":-0.90141, "vx":1.66699, "vy":0.00636, "omega":-4.68031, "ax":-9.30234, "ay":0.89793, "alpha":0.83857, "fx":[-157.55049,-158.21972,-158.78485,-158.36542], "fy":[21.77396,16.66079,8.77119,13.88826]}, + {"t":2.59657, "x":8.13091, "y":7.36352, "heading":-1.05553, "vx":1.35976, "vy":0.03602, "omega":-4.65261, "ax":-8.52629, "ay":0.02222, "alpha":12.82211, "fx":[-131.19104,-156.09694,-144.10769,-148.72337], "fy":[91.17086,34.92258,-68.96265,-55.61918]}, + {"t":2.6296, "x":8.17117, "y":7.36472, "heading":-1.2022, "vx":1.07817, "vy":0.03675, "omega":-4.22914, "ax":-7.40539, "ay":-0.55878, "alpha":20.76835, "fx":[-110.12436,-152.44503,-136.98183,-104.30332], "fy":[116.06391,49.12466,-82.81151,-120.3961]}, + {"t":2.66262, "x":8.20274, "y":7.36563, "heading":-1.33055, "vx":0.83359, "vy":0.01829, "omega":-3.54323, "ax":-6.71705, "ay":-0.46014, "alpha":24.78049, "fx":[-93.46826,-148.21411,-137.03084,-78.30729], "fy":[129.98792,60.95246,-83.00333,-139.24476]}, + {"t":2.69565, "x":8.22661, "y":7.36598, "heading":-1.43406, "vx":0.61175, "vy":0.0031, "omega":-2.72481, "ax":-6.36429, "ay":-0.21628, "alpha":26.63386, "fx":[-79.91406,-144.08575,-138.90106,-70.11824], "fy":[138.80701,70.27766,-79.98841,-143.81183]}, + {"t":2.72868, "x":8.24334, "y":7.36597, "heading":-1.50952, "vx":0.40156, "vy":-0.00405, "omega":-1.84517, "ax":-6.15213, "ay":-0.00519, "alpha":27.62795, "fx":[-69.29213,-140.61587,-140.69693,-67.97901], "fy":[144.45266,77.05845,-76.88378,-144.98037]}, + {"t":2.7617, "x":8.25325, "y":7.36583, "heading":-1.55539, "vx":0.19837, "vy":-0.00422, "omega":-0.93271, "ax":-6.00635, "ay":0.12769, "alpha":28.24102, "fx":[-61.76943,-138.21386,-141.76907,-66.9127], "fy":[147.86535,81.33784,-74.95505,-145.56023]}, + {"t":2.79473, "x":8.25652, "y":7.36576, "heading":-1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0,0.0,0.0,0.0]}], "splits":[0] }, "events":[] diff --git a/src/main/deploy/v1_doomspiral/choreo/LEFT_TRENCH_ANTI_BUCKS.traj b/src/main/deploy/v1_doomspiral/choreo/LEFT_TRENCH_ANTI_BUCKS.traj index 3f5e3aab..8428e5a2 100644 --- a/src/main/deploy/v1_doomspiral/choreo/LEFT_TRENCH_ANTI_BUCKS.traj +++ b/src/main/deploy/v1_doomspiral/choreo/LEFT_TRENCH_ANTI_BUCKS.traj @@ -4,14 +4,14 @@ "snapshot":{ "waypoints":[ {"x":4.441253185272217, "y":7.693458557128906, "heading":-1.5707972442597578, "intervals":27, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, - {"x":7.694813251495361, "y":7.532973289489746, "heading":0.0, "intervals":19, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":8.526999855041504, "y":6.732084846496582, "heading":-1.5707963267948966, "intervals":26, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, + {"x":7.694813251495361, "y":7.532973289489746, "heading":0.0, "intervals":29, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, + {"x":8.526999855041504, "y":6.732084846496582, "heading":-1.5707963267948966, "intervals":42, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, {"x":8.526999855041504, "y":5.0552420559, "heading":-1.5707963267948966, "intervals":17, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, {"x":7.337273120880127, "y":6.027435779571533, "heading":0.0, "intervals":26, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, {"x":5.503650665283203, "y":7.689876556396484, "heading":-1.5707963267948966, "intervals":20, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, {"x":3.785822629928589, "y":7.73520040512085, "heading":0.0, "intervals":13, "split":false, "fixTranslation":false, "fixHeading":false, "overrideIntervals":false}, - {"x":2.9093637466430664, "y":7.689876556396484, "heading":-1.5707963267948966, "intervals":27, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":2.818082809448242, "y":6.5, "heading":0.65, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], + {"x":2.9093637466430664, "y":7.689876556396484, "heading":-1.5707963267948966, "intervals":22, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, + {"x":3.0215272903442383, "y":7.140939712524414, "heading":0.3602010510265898, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], "constraints":[ {"from":"first", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, {"from":"last", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, @@ -20,21 +20,21 @@ {"from":5, "to":7, "data":{"type":"KeepInLane", "props":{"tolerance":0.03550910949707031}}, "enabled":true}, {"from":5, "to":7, "data":{"type":"MaxAngularVelocity", "props":{"max":0.0}}, "enabled":true}, {"from":2, "to":3, "data":{"type":"MaxAngularVelocity", "props":{"max":0.0}}, "enabled":true}, - {"from":1, "to":3, "data":{"type":"MaxVelocity", "props":{"max":1.5}}, "enabled":true}, + {"from":1, "to":3, "data":{"type":"MaxVelocity", "props":{"max":0.85}}, "enabled":true}, {"from":4, "to":6, "data":{"type":"MaxVelocity", "props":{"max":2.5}}, "enabled":true}], "targetDt":0.05 }, "params":{ "waypoints":[ {"x":{"exp":"4.441253185272217 m", "val":4.441253185272217}, "y":{"exp":"7.693458557128906 m", "val":7.693458557128906}, "heading":{"exp":"-1.5707972442597578 rad", "val":-1.5707972442597578}, "intervals":27, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, - {"x":{"exp":"7.694813251495361 m", "val":7.694813251495361}, "y":{"exp":"7.532973289489746 m", "val":7.532973289489746}, "heading":{"exp":"0 deg", "val":0.0}, "intervals":19, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":{"exp":"8.526999855041504 m", "val":8.526999855041504}, "y":{"exp":"6.732084846496582 m", "val":6.732084846496582}, "heading":{"exp":"-1.5707963267948966 rad", "val":-1.5707963267948966}, "intervals":26, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, + {"x":{"exp":"7.694813251495361 m", "val":7.694813251495361}, "y":{"exp":"7.532973289489746 m", "val":7.532973289489746}, "heading":{"exp":"0 deg", "val":0.0}, "intervals":29, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, + {"x":{"exp":"8.526999855041504 m", "val":8.526999855041504}, "y":{"exp":"6.732084846496582 m", "val":6.732084846496582}, "heading":{"exp":"-1.5707963267948966 rad", "val":-1.5707963267948966}, "intervals":42, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, {"x":{"exp":"8.526999855041504 m", "val":8.526999855041504}, "y":{"exp":"5.0552420559 m", "val":5.0552420559}, "heading":{"exp":"-1.5707963267948966 rad", "val":-1.5707963267948966}, "intervals":17, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, {"x":{"exp":"7.337273120880127 m", "val":7.337273120880127}, "y":{"exp":"6.027435779571533 m", "val":6.027435779571533}, "heading":{"exp":"0 deg", "val":0.0}, "intervals":26, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, {"x":{"exp":"5.503650665283203 m", "val":5.503650665283203}, "y":{"exp":"7.689876556396484 m", "val":7.689876556396484}, "heading":{"exp":"-90 deg", "val":-1.5707963267948966}, "intervals":20, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, {"x":{"exp":"3.785822629928589 m", "val":3.785822629928589}, "y":{"exp":"7.73520040512085 m", "val":7.73520040512085}, "heading":{"exp":"0 deg", "val":0.0}, "intervals":13, "split":false, "fixTranslation":false, "fixHeading":false, "overrideIntervals":false}, - {"x":{"exp":"2.9093637466430664 m", "val":2.9093637466430664}, "y":{"exp":"7.689876556396484 m", "val":7.689876556396484}, "heading":{"exp":"-90 deg", "val":-1.5707963267948966}, "intervals":27, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":{"exp":"2.818082809448242 m", "val":2.818082809448242}, "y":{"exp":"6.5 m", "val":6.5}, "heading":{"exp":"0.65 rad", "val":0.65}, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], + {"x":{"exp":"2.9093637466430664 m", "val":2.9093637466430664}, "y":{"exp":"7.689876556396484 m", "val":7.689876556396484}, "heading":{"exp":"-90 deg", "val":-1.5707963267948966}, "intervals":22, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, + {"x":{"exp":"3.0215272903442383 m", "val":3.0215272903442383}, "y":{"exp":"7.140939712524414 m", "val":7.140939712524414}, "heading":{"exp":"20.638 deg", "val":0.3602010510265898}, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], "constraints":[ {"from":"first", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, {"from":"last", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, @@ -43,7 +43,7 @@ {"from":5, "to":7, "data":{"type":"KeepInLane", "props":{"tolerance":{"exp":"35.50910949707031 mm", "val":0.03550910949707031}}}, "enabled":true}, {"from":5, "to":7, "data":{"type":"MaxAngularVelocity", "props":{"max":{"exp":"0 rad / s", "val":0.0}}}, "enabled":true}, {"from":2, "to":3, "data":{"type":"MaxAngularVelocity", "props":{"max":{"exp":"0 rad / s", "val":0.0}}}, "enabled":true}, - {"from":1, "to":3, "data":{"type":"MaxVelocity", "props":{"max":{"exp":"1.5 m / s", "val":1.5}}}, "enabled":true}, + {"from":1, "to":3, "data":{"type":"MaxVelocity", "props":{"max":{"exp":"INTAKE_VELOCITY", "val":0.55}}}, "enabled":true}, {"from":4, "to":6, "data":{"type":"MaxVelocity", "props":{"max":{"exp":"2.5 m / s", "val":2.5}}}, "enabled":true}], "targetDt":{ "exp":"0.05 s", @@ -75,184 +75,205 @@ "differentialTrackWidth":0.5588 }, "sampleType":"Swerve", - "waypoints":[0.0,1.17838,1.96838,3.13995,3.79778,4.79657,4.79718,5.59631,6.40921], + "waypoints":[0.0,1.22378,2.59549,4.5816,5.2492,6.24753,6.24816,7.09532,7.70869], "samples":[ - {"t":0.0, "x":4.44125, "y":7.69346, "heading":-1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":9.41986, "ay":-0.39777, "alpha":0.00001, "fx":[160.22916,160.22916,160.22916,160.22916], "fy":[-6.7659,-6.7659,-6.76595,-6.76595]}, - {"t":0.04364, "x":4.45022, "y":7.69308, "heading":-1.5708, "vx":0.41112, "vy":-0.01736, "omega":0.0, "ax":9.41903, "ay":-0.38638, "alpha":0.0, "fx":[160.21494,160.21494,160.21494,160.21494], "fy":[-6.57216,-6.57216,-6.5722,-6.5722]}, - {"t":0.08729, "x":4.47714, "y":7.69195, "heading":-1.5708, "vx":0.8222, "vy":-0.03422, "omega":0.0, "ax":9.41772, "ay":-0.37544, "alpha":0.0, "fx":[160.19276,160.19276,160.19276,160.19276], "fy":[-6.38604,-6.38604,-6.38607,-6.38607]}, - {"t":0.13093, "x":4.52199, "y":7.6901, "heading":-1.5708, "vx":1.23322, "vy":-0.05061, "omega":0.0, "ax":9.41569, "ay":-0.36497, "alpha":0.0, "fx":[160.15817,160.15817,160.15816,160.15816], "fy":[-6.208,-6.208,-6.20803,-6.20803]}, - {"t":0.17457, "x":4.58478, "y":7.68755, "heading":-1.5708, "vx":1.64416, "vy":-0.06654, "omega":0.0, "ax":9.4124, "ay":-0.35494, "alpha":0.0, "fx":[160.10231,160.10231,160.10231,160.10231], "fy":[-6.03735,-6.03735,-6.03737,-6.03737]}, - {"t":0.21822, "x":4.6655, "y":7.6843, "heading":-1.5708, "vx":2.05495, "vy":-0.08203, "omega":0.0, "ax":9.40666, "ay":-0.34518, "alpha":0.0, "fx":[160.00457,160.00457,160.00457,160.00457], "fy":[-5.87136,-5.87136,-5.87139,-5.87139]}, - {"t":0.26186, "x":4.76415, "y":7.6804, "heading":-1.5708, "vx":2.46549, "vy":-0.09709, "omega":0.0, "ax":9.39482, "ay":-0.3352, "alpha":0.0, "fx":[159.80315,159.80315,159.80315,159.80315], "fy":[-5.70161,-5.70161,-5.70163,-5.70163]}, - {"t":0.30551, "x":4.8807, "y":7.67584, "heading":-1.5708, "vx":2.87551, "vy":-0.11172, "omega":0.0, "ax":9.35886, "ay":-0.32263, "alpha":0.0, "fx":[159.1915,159.1915,159.1915,159.1915], "fy":[-5.48787,-5.48787,-5.48788,-5.48788]}, - {"t":0.34915, "x":5.01511, "y":7.67066, "heading":-1.5708, "vx":3.28397, "vy":-0.1258, "omega":0.0, "ax":5.08546, "ay":0.13376, "alpha":-0.00003, "fx":[86.50218,86.50226,86.50225,86.50218], "fy":[2.27519,2.27519,2.27539,2.27539]}, - {"t":0.39279, "x":5.16327, "y":7.66529, "heading":-1.5708, "vx":3.50592, "vy":-0.11996, "omega":0.0, "ax":0.00565, "ay":0.1436, "alpha":0.0, "fx":[0.0961,0.0961,0.0961,0.0961], "fy":[2.4426,2.4426,2.4426,2.4426]}, - {"t":0.43644, "x":5.31629, "y":7.66019, "heading":-1.5708, "vx":3.50616, "vy":-0.1137, "omega":0.0, "ax":0.00336, "ay":0.10604, "alpha":0.0, "fx":[0.05723,0.05723,0.05723,0.05723], "fy":[1.80373,1.80373,1.80373,1.80373]}, - {"t":0.48008, "x":5.46932, "y":7.65533, "heading":-1.5708, "vx":3.50631, "vy":-0.10907, "omega":0.0, "ax":0.00293, "ay":0.09633, "alpha":0.0, "fx":[0.04992,0.04992,0.04992,0.04992], "fy":[1.63848,1.63848,1.63848,1.63848]}, - {"t":0.52372, "x":5.62235, "y":7.65066, "heading":-1.5708, "vx":3.50644, "vy":-0.10487, "omega":0.0, "ax":0.00265, "ay":0.09049, "alpha":0.0, "fx":[0.0451,0.0451,0.0451,0.0451], "fy":[1.53915,1.53915,1.53915,1.53915]}, - {"t":0.56737, "x":5.77538, "y":7.64617, "heading":-1.5708, "vx":3.50655, "vy":-0.10092, "omega":0.0, "ax":0.00242, "ay":0.08577, "alpha":0.0, "fx":[0.04115,0.04115,0.04115,0.04115], "fy":[1.45899,1.45899,1.45899,1.45899]}, - {"t":0.61101, "x":5.92842, "y":7.64185, "heading":-1.5708, "vx":3.50666, "vy":-0.09717, "omega":0.0, "ax":0.00222, "ay":0.08175, "alpha":0.0, "fx":[0.03777,0.03777,0.03777,0.03777], "fy":[1.39049,1.39049,1.39049,1.39049]}, - {"t":0.65465, "x":6.08147, "y":7.63769, "heading":-1.5708, "vx":3.50675, "vy":-0.0936, "omega":0.0, "ax":0.00205, "ay":0.07824, "alpha":0.0, "fx":[0.03483,0.03483,0.03483,0.03483], "fy":[1.3309,1.3309,1.3309,1.3309]}, - {"t":0.6983, "x":6.23452, "y":7.63368, "heading":-1.5708, "vx":3.50684, "vy":-0.09019, "omega":0.0, "ax":0.00189, "ay":0.07512, "alpha":0.0, "fx":[0.03222,0.03222,0.03222,0.03222], "fy":[1.27774,1.27774,1.27774,1.27774]}, - {"t":0.74194, "x":6.38757, "y":7.62981, "heading":-1.5708, "vx":3.50693, "vy":-0.08691, "omega":0.0, "ax":0.00175, "ay":0.07186, "alpha":0.0, "fx":[0.02971,0.02971,0.02971,0.02971], "fy":[1.22229,1.22229,1.22229,1.22229]}, - {"t":0.78559, "x":6.54063, "y":7.62609, "heading":-1.5708, "vx":3.507, "vy":-0.08378, "omega":0.0, "ax":0.00151, "ay":0.06426, "alpha":0.0, "fx":[0.02564,0.02564,0.02564,0.02564], "fy":[1.093,1.093,1.093,1.093]}, - {"t":0.82923, "x":6.69369, "y":7.62249, "heading":-1.5708, "vx":3.50707, "vy":-0.08097, "omega":0.0, "ax":0.0003, "ay":0.01301, "alpha":0.0, "fx":[0.00506,0.00506,0.00506,0.00506], "fy":[0.22135,0.22135,0.22135,0.22135]}, - {"t":0.87287, "x":6.84675, "y":7.61897, "heading":-1.5708, "vx":3.50708, "vy":-0.0804, "omega":0.0, "ax":-0.01167, "ay":-0.45235, "alpha":0.0, "fx":[-0.19845,-0.19845,-0.19845,-0.19845], "fy":[-7.69443,-7.69443,-7.69443,-7.69443]}, - {"t":0.91652, "x":6.9998, "y":7.61503, "heading":-1.5708, "vx":3.50657, "vy":-0.10015, "omega":0.0, "ax":-0.23873, "ay":-3.97608, "alpha":0.0, "fx":[-4.06074,-4.06074,-4.06074,-4.06074], "fy":[-67.63193,-67.63193,-67.63193,-67.63193]}, - {"t":0.96016, "x":7.15261, "y":7.60687, "heading":-1.5708, "vx":3.49615, "vy":-0.27368, "omega":0.0, "ax":-9.04743, "ay":-1.50755, "alpha":0.0, "fx":[-153.89417,-153.89417,-153.89416,-153.89416], "fy":[-25.6429,-25.64289,-25.64293,-25.64293]}, - {"t":1.0038, "x":7.29658, "y":7.59349, "heading":-1.5708, "vx":3.10129, "vy":-0.33947, "omega":0.0, "ax":-9.37759, "ay":-0.19117, "alpha":0.0, "fx":[-159.51019,-159.51019,-159.51019,-159.51018], "fy":[-3.25175,-3.25175,-3.25175,-3.25175]}, - {"t":1.04745, "x":7.423, "y":7.5785, "heading":-1.5708, "vx":2.69202, "vy":-0.34781, "omega":0.0, "ax":-9.40499, "ay":-0.02901, "alpha":0.0, "fx":[-159.97624,-159.97624,-159.97624,-159.97624], "fy":[-0.49339,-0.49339,-0.49339,-0.49339]}, - {"t":1.09109, "x":7.53153, "y":7.56329, "heading":-1.5708, "vx":2.28155, "vy":-0.34908, "omega":0.0, "ax":-9.41489, "ay":0.03271, "alpha":0.0, "fx":[-160.14461,-160.14461,-160.14461,-160.14461], "fy":[0.55633,0.55633,0.55633,0.55633]}, - {"t":1.13473, "x":7.62214, "y":7.54808, "heading":-1.5708, "vx":1.87065, "vy":-0.34765, "omega":0.0, "ax":-9.42, "ay":0.06442, "alpha":0.0, "fx":[-160.23143,-160.23143,-160.23143,-160.23143], "fy":[1.09579,1.09579,1.09579,1.09579]}, - {"t":1.17838, "x":7.69481, "y":7.53297, "heading":-1.5708, "vx":1.45953, "vy":-0.34484, "omega":0.0, "ax":-3.28804, "ay":-8.69594, "alpha":0.0, "fx":[-55.92854,-55.92854,-55.92854,-55.92853], "fy":[-147.91553,-147.91553,-147.91553,-147.91553]}, - {"t":1.21996, "x":7.75266, "y":7.51112, "heading":-1.5708, "vx":1.32281, "vy":-0.70641, "omega":0.0, "ax":-4.26731, "ay":-6.29128, "alpha":0.0, "fx":[-72.58573,-72.58575,-72.58574,-72.58572], "fy":[-107.01294,-107.01293,-107.01294,-107.01295]}, - {"t":1.26154, "x":7.80397, "y":7.47631, "heading":-1.5708, "vx":1.14538, "vy":-0.96799, "omega":0.0, "ax":-0.36992, "ay":-0.43081, "alpha":0.00001, "fx":[-6.29219,-6.29223,-6.29223,-6.29219], "fy":[-7.32788,-7.32788,-7.32795,-7.32795]}, - {"t":1.30312, "x":7.85127, "y":7.43569, "heading":-1.5708, "vx":1.13, "vy":-0.98591, "omega":0.0, "ax":-0.00752, "ay":-0.00862, "alpha":0.00001, "fx":[-0.12793,-0.12796,-0.12796,-0.12793], "fy":[-0.14656,-0.14656,-0.1466,-0.1466]}, - {"t":1.34469, "x":7.89825, "y":7.39469, "heading":-1.5708, "vx":1.12969, "vy":-0.98627, "omega":0.0, "ax":0.00199, "ay":0.00228, "alpha":0.0, "fx":[0.03387,0.03385,0.03385,0.03387], "fy":[0.03882,0.03882,0.03879,0.03879]}, - {"t":1.38627, "x":7.94522, "y":7.35368, "heading":-1.5708, "vx":1.12977, "vy":-0.98617, "omega":0.0, "ax":0.00204, "ay":0.00234, "alpha":0.0, "fx":[0.03478,0.03477,0.03477,0.03478], "fy":[0.03987,0.03987,0.03985,0.03985]}, - {"t":1.42785, "x":7.9922, "y":7.31268, "heading":-1.5708, "vx":1.12986, "vy":-0.98607, "omega":0.0, "ax":0.00188, "ay":0.00216, "alpha":0.0, "fx":[0.032,0.03199,0.03199,0.032], "fy":[0.03668,0.03668,0.03667,0.03667]}, - {"t":1.46943, "x":8.03918, "y":7.27168, "heading":-1.5708, "vx":1.12994, "vy":-0.98598, "omega":0.0, "ax":0.00174, "ay":0.00199, "alpha":0.0, "fx":[0.02956,0.02955,0.02955,0.02956], "fy":[0.03389,0.03389,0.03389,0.03389]}, - {"t":1.51101, "x":8.08616, "y":7.23069, "heading":-1.5708, "vx":1.13001, "vy":-0.9859, "omega":0.0, "ax":0.00161, "ay":0.00185, "alpha":0.0, "fx":[0.02746,0.02747,0.02747,0.02746], "fy":[0.03149,0.03149,0.0315,0.0315]}, - {"t":1.55259, "x":8.13315, "y":7.1897, "heading":-1.5708, "vx":1.13008, "vy":-0.98582, "omega":0.0, "ax":0.00151, "ay":0.00173, "alpha":0.0, "fx":[0.02565,0.02565,0.02565,0.02565], "fy":[0.02941,0.02941,0.02942,0.02942]}, - {"t":1.59417, "x":8.18014, "y":7.14871, "heading":-1.5708, "vx":1.13014, "vy":-0.98575, "omega":0.0, "ax":0.00141, "ay":0.00162, "alpha":0.0, "fx":[0.02405,0.02406,0.02406,0.02405], "fy":[0.02759,0.02759,0.0276,0.0276]}, - {"t":1.63575, "x":8.22713, "y":7.10772, "heading":-1.5708, "vx":1.1302, "vy":-0.98568, "omega":0.0, "ax":0.00133, "ay":0.00153, "alpha":0.0, "fx":[0.02262,0.02264,0.02264,0.02262], "fy":[0.02595,0.02595,0.02597,0.02597]}, - {"t":1.67733, "x":8.27412, "y":7.06674, "heading":-1.5708, "vx":1.13025, "vy":-0.98562, "omega":0.0, "ax":0.00121, "ay":0.00139, "alpha":0.0, "fx":[0.02062,0.02064,0.02064,0.02062], "fy":[0.02365,0.02365,0.02368,0.02368]}, - {"t":1.7189, "x":8.32112, "y":7.02576, "heading":-1.5708, "vx":1.1303, "vy":-0.98556, "omega":0.0, "ax":-0.00049, "ay":-0.00056, "alpha":-0.00001, "fx":[-0.00839,-0.00836,-0.00836,-0.00839], "fy":[-0.00962,-0.00962,-0.00958,-0.00958]}, - {"t":1.76048, "x":8.36812, "y":6.98478, "heading":-1.5708, "vx":1.13028, "vy":-0.98559, "omega":0.0, "ax":-0.06226, "ay":-0.07121, "alpha":-0.00001, "fx":[-1.05899,-1.05896,-1.05896,-1.05899], "fy":[-1.21123,-1.21123,-1.21118,-1.21118]}, - {"t":1.80206, "x":8.41506, "y":6.94374, "heading":-1.5708, "vx":1.12769, "vy":-0.98855, "omega":0.0, "ax":-2.21793, "ay":-2.31386, "alpha":-0.00001, "fx":[-37.72635,-37.72631,-37.72632,-37.72636], "fy":[-39.3581,-39.3581,-39.35805,-39.35805]}, - {"t":1.84364, "x":8.46003, "y":6.90064, "heading":-1.5708, "vx":1.03548, "vy":-1.08475, "omega":0.0, "ax":-7.30571, "ay":-5.39213, "alpha":0.0, "fx":[-124.26809,-124.26809,-124.26809,-124.26809], "fy":[-91.71864,-91.71864,-91.71864,-91.71864]}, - {"t":1.88522, "x":8.49677, "y":6.85087, "heading":-1.5708, "vx":0.73171, "vy":-1.30895, "omega":0.0, "ax":-8.69328, "ay":-3.46438, "alpha":0.0, "fx":[-147.87017,-147.87017,-147.87017,-147.87017], "fy":[-58.92818,-58.92818,-58.92818,-58.92818]}, - {"t":1.9268, "x":8.51968, "y":6.79345, "heading":-1.5708, "vx":0.37025, "vy":-1.453, "omega":0.0, "ax":-9.33797, "ay":-1.10493, "alpha":0.0, "fx":[-158.83616,-158.83616,-158.83616,-158.83616], "fy":[-18.79457,-18.79457,-18.79457,-18.79457]}, - {"t":1.96838, "x":8.527, "y":6.73208, "heading":-1.5708, "vx":-0.01801, "vy":-1.49894, "omega":0.0, "ax":0.80672, "ay":-0.01371, "alpha":0.0, "fx":[13.72208,13.72208,13.72208,13.72208], "fy":[-0.23328,-0.23328,-0.23328,-0.23328]}, - {"t":2.01344, "x":8.52701, "y":6.66453, "heading":-1.5708, "vx":0.01834, "vy":-1.49956, "omega":0.0, "ax":-0.82892, "ay":0.00018, "alpha":0.0, "fx":[-14.09975,-14.09975,-14.09975,-14.09975], "fy":[0.00314,0.00314,0.00314,0.00314]}, - {"t":2.0585, "x":8.52699, "y":6.59696, "heading":-1.5708, "vx":-0.01901, "vy":-1.49955, "omega":0.0, "ax":0.85859, "ay":0.00019, "alpha":0.0, "fx":[14.60431,14.60431,14.60431,14.60431], "fy":[0.00326,0.00326,0.00326,0.00326]}, - {"t":2.10356, "x":8.52701, "y":6.52939, "heading":-1.5708, "vx":0.01968, "vy":-1.49954, "omega":0.0, "ax":-0.88834, "ay":0.0002, "alpha":0.0, "fx":[-15.11038,-15.11038,-15.11038,-15.11038], "fy":[0.00339,0.00339,0.00339,0.00339]}, - {"t":2.14862, "x":8.52699, "y":6.46182, "heading":-1.5708, "vx":-0.02035, "vy":-1.49953, "omega":0.0, "ax":0.91818, "ay":0.00021, "alpha":0.0, "fx":[15.61798,15.61798,15.61798,15.61798], "fy":[0.00351,0.00351,0.00351,0.00351]}, - {"t":2.19368, "x":8.52701, "y":6.39425, "heading":-1.5708, "vx":0.02102, "vy":-1.49952, "omega":0.0, "ax":-0.94811, "ay":0.00021, "alpha":0.0, "fx":[-16.12713,-16.12713,-16.12713,-16.12713], "fy":[0.00364,0.00364,0.00364,0.00364]}, - {"t":2.23874, "x":8.52699, "y":6.32668, "heading":-1.5708, "vx":-0.0217, "vy":-1.49951, "omega":0.0, "ax":0.97814, "ay":0.00022, "alpha":0.0, "fx":[16.63786,16.63786,16.63786,16.63786], "fy":[0.00376,0.00376,0.00376,0.00376]}, - {"t":2.2838, "x":8.52701, "y":6.25911, "heading":-1.5708, "vx":0.02238, "vy":-1.4995, "omega":0.0, "ax":-1.00826, "ay":0.00023, "alpha":0.0, "fx":[-17.15018,-17.15018,-17.15018,-17.15018], "fy":[0.00389,0.00389,0.00389,0.00389]}, - {"t":2.32886, "x":8.52699, "y":6.19154, "heading":-1.5708, "vx":-0.02306, "vy":-1.49949, "omega":0.0, "ax":1.03847, "ay":0.00024, "alpha":0.0, "fx":[17.66411,17.66411,17.66411,17.66411], "fy":[0.00402,0.00402,0.00402,0.00402]}, - {"t":2.37392, "x":8.52701, "y":6.12398, "heading":-1.5708, "vx":0.02374, "vy":-1.49948, "omega":0.0, "ax":-1.06878, "ay":0.00024, "alpha":0.0, "fx":[-18.17966,-18.17966,-18.17966,-18.17966], "fy":[0.00415,0.00415,0.00415,0.00415]}, - {"t":2.41898, "x":8.52699, "y":6.05641, "heading":-1.5708, "vx":-0.02442, "vy":-1.49947, "omega":0.0, "ax":1.09919, "ay":0.00025, "alpha":0.0, "fx":[18.69686,18.69686,18.69686,18.69686], "fy":[0.00428,0.00428,0.00428,0.00428]}, - {"t":2.46404, "x":8.52701, "y":5.98884, "heading":-1.5708, "vx":0.02511, "vy":-1.49946, "omega":0.0, "ax":-1.12969, "ay":0.00026, "alpha":0.0, "fx":[-19.2157,-19.2157,-19.2157,-19.2157], "fy":[0.00441,0.00441,0.00441,0.00441]}, - {"t":2.5091, "x":8.52699, "y":5.92128, "heading":-1.5708, "vx":-0.0258, "vy":-1.49945, "omega":0.0, "ax":1.16029, "ay":0.00027, "alpha":0.0, "fx":[19.7362,19.7362,19.7362,19.7362], "fy":[0.00455,0.00455,0.00455,0.00455]}, - {"t":2.55416, "x":8.52701, "y":5.85371, "heading":-1.5708, "vx":0.02649, "vy":-1.49944, "omega":0.0, "ax":-1.19099, "ay":0.00028, "alpha":0.0, "fx":[-20.25837,-20.25837,-20.25837,-20.25837], "fy":[0.00468,0.00468,0.00468,0.00468]}, - {"t":2.59922, "x":8.52699, "y":5.78615, "heading":-1.5708, "vx":-0.02718, "vy":-1.49942, "omega":0.0, "ax":1.22179, "ay":0.00028, "alpha":0.0, "fx":[20.78222,20.78222,20.78222,20.78222], "fy":[0.00482,0.00482,0.00482,0.00482]}, - {"t":2.64428, "x":8.52701, "y":5.71858, "heading":-1.5708, "vx":0.02787, "vy":-1.49941, "omega":0.0, "ax":-1.25268, "ay":0.00029, "alpha":0.0, "fx":[-21.30774,-21.30774,-21.30774,-21.30774], "fy":[0.00496,0.00496,0.00496,0.00496]}, - {"t":2.68934, "x":8.52699, "y":5.65102, "heading":-1.5708, "vx":-0.02857, "vy":-1.4994, "omega":0.0, "ax":1.28367, "ay":0.0003, "alpha":0.0, "fx":[21.83494,21.83494,21.83494,21.83494], "fy":[0.00509,0.00509,0.00509,0.00509]}, - {"t":2.7344, "x":8.52701, "y":5.58345, "heading":-1.5708, "vx":0.02927, "vy":-1.49939, "omega":0.0, "ax":-1.31477, "ay":0.00031, "alpha":0.0, "fx":[-22.36384,-22.36384,-22.36384,-22.36384], "fy":[0.00523,0.00523,0.00523,0.00523]}, - {"t":2.77946, "x":8.52699, "y":5.51589, "heading":-1.5708, "vx":-0.02997, "vy":-1.49937, "omega":0.0, "ax":1.34596, "ay":0.00032, "alpha":0.0, "fx":[22.89441,22.89441,22.89441,22.89441], "fy":[0.00537,0.00537,0.00537,0.00537]}, - {"t":2.82453, "x":8.52701, "y":5.44833, "heading":-1.5708, "vx":0.03068, "vy":-1.49936, "omega":0.0, "ax":-1.37725, "ay":0.00032, "alpha":0.0, "fx":[-23.42668,-23.42668,-23.42668,-23.42668], "fy":[0.00552,0.00552,0.00552,0.00552]}, - {"t":2.86959, "x":8.52699, "y":5.38077, "heading":-1.5708, "vx":-0.03138, "vy":-1.49934, "omega":0.0, "ax":1.40864, "ay":0.00033, "alpha":0.0, "fx":[23.96062,23.96062,23.96062,23.96062], "fy":[0.00566,0.00566,0.00566,0.00566]}, - {"t":2.91465, "x":8.52701, "y":5.31321, "heading":-1.5708, "vx":0.03209, "vy":-1.49933, "omega":0.0, "ax":-1.44013, "ay":0.00034, "alpha":0.0, "fx":[-24.49625,-24.49625,-24.49625,-24.49625], "fy":[0.00581,0.00581,0.00581,0.00581]}, - {"t":2.95971, "x":8.52699, "y":5.24565, "heading":-1.5708, "vx":-0.0328, "vy":-1.49931, "omega":0.0, "ax":1.47172, "ay":0.00055, "alpha":0.0, "fx":[25.03358,25.03358,25.03358,25.03358], "fy":[0.0094,0.0094,0.0094,0.0094]}, - {"t":3.00477, "x":8.52701, "y":5.17809, "heading":-1.5708, "vx":0.03351, "vy":-1.49929, "omega":0.0, "ax":-1.50349, "ay":8.29775, "alpha":0.0, "fx":[-25.57387,-25.57387,-25.57387,-25.57387], "fy":[141.14234,141.14234,141.14234,141.14234]}, - {"t":3.04983, "x":8.52699, "y":5.11895, "heading":-1.5708, "vx":-0.03423, "vy":-1.12539, "omega":0.0, "ax":1.53574, "ay":9.2832, "alpha":0.0, "fx":[26.12242,26.12242,26.12242,26.12242], "fy":[157.90461,157.90461,157.90461,157.90461]}, - {"t":3.09489, "x":8.52701, "y":5.07767, "heading":-1.5708, "vx":0.03497, "vy":-0.70708, "omega":0.0, "ax":-1.56033, "ay":9.29312, "alpha":0.0, "fx":[-26.54073,-26.54073,-26.54073,-26.54073], "fy":[158.07335,158.07335,158.07335,158.07335]}, - {"t":3.13995, "x":8.527, "y":5.05524, "heading":-1.5708, "vx":-0.03534, "vy":-0.28833, "omega":0.0, "ax":-6.91651, "ay":6.40666, "alpha":0.0, "fx":[-117.64781,-117.64781,-117.64781,-117.64781], "fy":[108.97552,108.97552,108.97552,108.97552]}, - {"t":3.17864, "x":8.52045, "y":5.04888, "heading":-1.5708, "vx":-0.30298, "vy":-0.04042, "omega":0.0, "ax":-6.91418, "ay":6.40759, "alpha":0.0, "fx":[-117.60821,-117.60821,-117.60821,-117.60821], "fy":[108.9912,108.9912,108.9912,108.9912]}, - {"t":3.21734, "x":8.50355, "y":5.05211, "heading":-1.5708, "vx":-0.57053, "vy":0.20753, "omega":0.0, "ax":-6.91128, "ay":6.40872, "alpha":0.0, "fx":[-117.55885,-117.55885,-117.55885,-117.55885], "fy":[109.01053,109.01053,109.01053,109.01053]}, - {"t":3.25604, "x":8.4763, "y":5.06494, "heading":-1.5708, "vx":-0.83797, "vy":0.45552, "omega":0.0, "ax":-6.90756, "ay":6.41016, "alpha":0.0, "fx":[-117.49556,-117.49556,-117.49556,-117.49556], "fy":[109.03501,109.03501,109.03501,109.03501]}, - {"t":3.29473, "x":8.4387, "y":5.08737, "heading":-1.5708, "vx":-1.10527, "vy":0.70357, "omega":0.0, "ax":-6.9026, "ay":6.41205, "alpha":0.0, "fx":[-117.41132,-117.41132,-117.41132,-117.41132], "fy":[109.06718,109.06718,109.06718,109.06718]}, - {"t":3.33343, "x":8.39077, "y":5.1194, "heading":-1.5708, "vx":-1.37237, "vy":0.95169, "omega":0.0, "ax":-6.89567, "ay":6.41466, "alpha":0.0, "fx":[-117.29343,-117.29343,-117.29343,-117.29343], "fy":[109.11154,109.11154,109.11154,109.11154]}, - {"t":3.37212, "x":8.3325, "y":5.16102, "heading":-1.5708, "vx":-1.63921, "vy":1.19991, "omega":0.0, "ax":-6.88526, "ay":6.41851, "alpha":0.0, "fx":[-117.11633,-117.11633,-117.11633,-117.11633], "fy":[109.17709,109.17709,109.17709,109.17709]}, - {"t":3.41082, "x":8.26391, "y":5.21226, "heading":-1.5708, "vx":-1.90564, "vy":1.44828, "omega":0.0, "ax":-6.86781, "ay":6.42484, "alpha":0.0, "fx":[-116.81949,-116.81949,-116.81949,-116.81949], "fy":[109.28464,109.28464,109.28464,109.28464]}, - {"t":3.44952, "x":8.18503, "y":5.27311, "heading":-1.5708, "vx":-2.1714, "vy":1.6969, "omega":0.0, "ax":-6.83239, "ay":6.43728, "alpha":0.0, "fx":[-116.21704,-116.21704,-116.21704,-116.21704], "fy":[109.49631,109.49631,109.49631,109.49631]}, - {"t":3.48821, "x":8.09589, "y":5.3436, "heading":-1.5708, "vx":-2.43578, "vy":1.94599, "omega":0.0, "ax":-6.72116, "ay":6.47394, "alpha":0.0, "fx":[-114.32501,-114.32501,-114.32501,-114.32501], "fy":[110.11994,110.11994,110.11994,110.11994]}, - {"t":3.52691, "x":7.9966, "y":5.42375, "heading":-1.5708, "vx":-2.69586, "vy":2.19651, "omega":0.0, "ax":2.37744, "ay":3.96683, "alpha":0.0, "fx":[40.43964,40.43964,40.43964,40.43964], "fy":[67.47472,67.47472,67.47472,67.47472]}, - {"t":3.5656, "x":7.89406, "y":5.51171, "heading":-1.5708, "vx":-2.60387, "vy":2.35001, "omega":0.0, "ax":0.57361, "ay":0.62996, "alpha":0.0, "fx":[9.75687,9.75687,9.75687,9.75687], "fy":[10.71543,10.71543,10.71543,10.71543]}, - {"t":3.6043, "x":7.79373, "y":5.60312, "heading":-1.5708, "vx":-2.58167, "vy":2.37439, "omega":0.0, "ax":0.09817, "ay":0.10657, "alpha":0.0, "fx":[1.66984,1.66984,1.66984,1.66984], "fy":[1.81279,1.81279,1.81279,1.81279]}, - {"t":3.643, "x":7.69391, "y":5.69508, "heading":-1.5708, "vx":-2.57787, "vy":2.37851, "omega":0.0, "ax":0.11633, "ay":0.12016, "alpha":0.0, "fx":[1.97877,1.97877,1.97877,1.97877], "fy":[2.04391,2.04391,2.04391,2.04391]}, - {"t":3.68169, "x":7.59424, "y":5.78721, "heading":-1.5708, "vx":-2.57337, "vy":2.38316, "omega":0.0, "ax":5.64703, "ay":-4.66592, "alpha":0.0, "fx":[96.05441,96.05441,96.05441,96.05441], "fy":[-79.36595,-79.36595,-79.36595,-79.36595]}, - {"t":3.72039, "x":7.49889, "y":5.87593, "heading":-1.5708, "vx":-2.35485, "vy":2.20261, "omega":0.0, "ax":6.88212, "ay":-6.32254, "alpha":0.0, "fx":[117.06282,117.06282,117.06282,117.06282], "fy":[-107.54467,-107.54467,-107.54467,-107.54467]}, - {"t":3.75908, "x":7.41292, "y":5.95643, "heading":-1.5708, "vx":-2.08854, "vy":1.95795, "omega":0.0, "ax":6.90857, "ay":-6.36044, "alpha":0.0, "fx":[117.51274,117.51274,117.51274,117.51274], "fy":[-108.18922,-108.18922,-108.18922,-108.18922]}, - {"t":3.79778, "x":7.33727, "y":6.02744, "heading":-1.5708, "vx":-1.82121, "vy":1.71183, "omega":0.0, "ax":1.26065, "ay":1.3012, "alpha":0.0, "fx":[21.44325,21.44325,21.44325,21.44325], "fy":[22.13312,22.13312,22.13312,22.13312]}, - {"t":3.8362, "x":7.26824, "y":6.09416, "heading":-1.5708, "vx":-1.77278, "vy":1.76181, "omega":0.0, "ax":0.11179, "ay":0.11221, "alpha":0.0, "fx":[1.90143,1.90143,1.90143,1.90143], "fy":[1.90865,1.90865,1.90865,1.90865]}, - {"t":3.87461, "x":7.20022, "y":6.16192, "heading":-1.5708, "vx":-1.76849, "vy":1.76613, "omega":0.0, "ax":0.01065, "ay":0.01066, "alpha":0.0, "fx":[0.18119,0.18119,0.18119,0.18119], "fy":[0.18141,0.18141,0.18141,0.18141]}, - {"t":3.91303, "x":7.13229, "y":6.22977, "heading":-1.5708, "vx":-1.76808, "vy":1.76653, "omega":0.0, "ax":0.0023, "ay":0.0023, "alpha":0.0, "fx":[0.03908,0.03908,0.03908,0.03908], "fy":[0.03913,0.03913,0.03913,0.03913]}, - {"t":3.95144, "x":7.06437, "y":6.29764, "heading":-1.5708, "vx":-1.76799, "vy":1.76662, "omega":0.0, "ax":0.00169, "ay":0.00169, "alpha":0.0, "fx":[0.02874,0.02874,0.02874,0.02874], "fy":[0.02878,0.02878,0.02878,0.02878]}, - {"t":3.98986, "x":6.99646, "y":6.3655, "heading":-1.5708, "vx":-1.76792, "vy":1.76669, "omega":0.0, "ax":0.00173, "ay":0.00174, "alpha":0.0, "fx":[0.02951,0.02951,0.02951,0.02951], "fy":[0.02955,0.02955,0.02955,0.02955]}, - {"t":4.02827, "x":6.92854, "y":6.43337, "heading":-1.5708, "vx":-1.76786, "vy":1.76675, "omega":0.0, "ax":0.00184, "ay":0.00185, "alpha":0.0, "fx":[0.03136,0.03136,0.03136,0.03136], "fy":[0.0314,0.0314,0.0314,0.0314]}, - {"t":4.06669, "x":6.86063, "y":6.50124, "heading":-1.5708, "vx":-1.76779, "vy":1.76683, "omega":0.0, "ax":0.00197, "ay":0.00197, "alpha":0.0, "fx":[0.03348,0.03348,0.03348,0.03348], "fy":[0.03352,0.03352,0.03352,0.03352]}, - {"t":4.1051, "x":6.79273, "y":6.56912, "heading":-1.5708, "vx":-1.76771, "vy":1.7669, "omega":0.0, "ax":0.00211, "ay":0.00211, "alpha":0.0, "fx":[0.03583,0.03583,0.03583,0.03583], "fy":[0.03586,0.03586,0.03586,0.03586]}, - {"t":4.14352, "x":6.72482, "y":6.63699, "heading":-1.5708, "vx":-1.76763, "vy":1.76698, "omega":0.0, "ax":0.00226, "ay":0.00226, "alpha":0.0, "fx":[0.03844,0.03844,0.03844,0.03844], "fy":[0.03848,0.03848,0.03848,0.03848]}, - {"t":4.18193, "x":6.65692, "y":6.70487, "heading":-1.5708, "vx":-1.76754, "vy":1.76707, "omega":0.0, "ax":0.00243, "ay":0.00243, "alpha":0.0, "fx":[0.04137,0.04137,0.04137,0.04137], "fy":[0.04141,0.04141,0.04141,0.04141]}, - {"t":4.22035, "x":6.58902, "y":6.77276, "heading":-1.5708, "vx":-1.76745, "vy":1.76716, "omega":0.0, "ax":0.00263, "ay":0.00263, "alpha":0.0, "fx":[0.04468,0.04468,0.04468,0.04468], "fy":[0.04472,0.04472,0.04472,0.04472]}, - {"t":4.25876, "x":6.52113, "y":6.84065, "heading":-1.5708, "vx":-1.76735, "vy":1.76726, "omega":0.0, "ax":0.00285, "ay":0.00285, "alpha":0.0, "fx":[0.04846,0.04846,0.04846,0.04846], "fy":[0.04849,0.04849,0.04849,0.04849]}, - {"t":4.29718, "x":6.45323, "y":6.90854, "heading":-1.5708, "vx":-1.76724, "vy":1.76737, "omega":0.0, "ax":0.0031, "ay":0.00311, "alpha":0.0, "fx":[0.05281,0.05281,0.05281,0.05281], "fy":[0.05283,0.05283,0.05283,0.05283]}, - {"t":4.33559, "x":6.38535, "y":6.97643, "heading":-1.5708, "vx":-1.76712, "vy":1.76749, "omega":0.0, "ax":0.0034, "ay":0.0034, "alpha":0.0, "fx":[0.05788,0.05788,0.05788,0.05788], "fy":[0.05789,0.05789,0.05789,0.05789]}, - {"t":4.37401, "x":6.31747, "y":7.04434, "heading":-1.5708, "vx":-1.76699, "vy":1.76762, "omega":0.0, "ax":0.00376, "ay":0.00376, "alpha":0.0, "fx":[0.06387,0.06387,0.06387,0.06387], "fy":[0.06388,0.06388,0.06388,0.06388]}, - {"t":4.41242, "x":6.24959, "y":7.11224, "heading":-1.5708, "vx":-1.76684, "vy":1.76777, "omega":0.0, "ax":0.00417, "ay":0.00417, "alpha":0.0, "fx":[0.071,0.071,0.071,0.071], "fy":[0.07099,0.07099,0.07099,0.07099]}, - {"t":4.45084, "x":6.18172, "y":7.18015, "heading":-1.5708, "vx":-1.76668, "vy":1.76793, "omega":0.0, "ax":0.00461, "ay":0.00461, "alpha":0.0, "fx":[0.07842,0.07842,0.07842,0.07842], "fy":[0.0784,0.0784,0.0784,0.0784]}, - {"t":4.48925, "x":6.11386, "y":7.24807, "heading":-1.5708, "vx":-1.76651, "vy":1.76811, "omega":0.0, "ax":0.00419, "ay":0.00419, "alpha":0.0, "fx":[0.07132,0.07132,0.07132,0.07132], "fy":[0.0713,0.0713,0.0713,0.0713]}, - {"t":4.52767, "x":6.046, "y":7.316, "heading":-1.5708, "vx":-1.76635, "vy":1.76827, "omega":0.0, "ax":-0.008, "ay":-0.00799, "alpha":0.0, "fx":[-0.13614,-0.13614,-0.13614,-0.13614], "fy":[-0.13595,-0.13595,-0.13595,-0.13595]}, - {"t":4.56608, "x":5.97814, "y":7.38392, "heading":-1.5708, "vx":-1.76665, "vy":1.76796, "omega":0.0, "ax":-0.16372, "ay":-0.16418, "alpha":0.0, "fx":[-2.78482,-2.78482,-2.78482,-2.78482], "fy":[-2.79267,-2.79267,-2.79267,-2.79267]}, - {"t":4.6045, "x":5.91015, "y":7.45171, "heading":-1.5708, "vx":-1.77294, "vy":1.76165, "omega":0.0, "ax":-1.8434, "ay":-1.93311, "alpha":0.0, "fx":[-31.35574,-31.35574,-31.35574,-31.35574], "fy":[-32.88157,-32.88157,-32.88157,-32.88157]}, - {"t":4.64291, "x":5.84068, "y":7.51796, "heading":-1.5708, "vx":-1.84376, "vy":1.68739, "omega":0.0, "ax":-5.12952, "ay":-6.3665, "alpha":0.0, "fx":[-87.25174,-87.25174,-87.25174,-87.25174], "fy":[-108.29235,-108.29235,-108.29235,-108.29235]}, - {"t":4.68133, "x":5.76607, "y":7.57809, "heading":-1.5708, "vx":-2.04081, "vy":1.44282, "omega":0.0, "ax":-4.73605, "ay":-7.81234, "alpha":0.0, "fx":[-80.55882,-80.55882,-80.55882,-80.55882], "fy":[-132.88562,-132.88562,-132.88562,-132.88562]}, - {"t":4.71974, "x":5.68418, "y":7.62775, "heading":-1.5708, "vx":-2.22274, "vy":1.14271, "omega":0.0, "ax":-3.65071, "ay":-8.56471, "alpha":0.0, "fx":[-62.09746,-62.09746,-62.09746,-62.09746], "fy":[-145.6832,-145.6832,-145.6832,-145.6832]}, - {"t":4.75816, "x":5.5961, "y":7.66533, "heading":-1.5708, "vx":-2.36299, "vy":0.8137, "omega":0.0, "ax":-2.26642, "ay":-9.08975, "alpha":0.0, "fx":[-38.55117,-38.55117,-38.55117,-38.55117], "fy":[-154.614,-154.614,-154.614,-154.614]}, - {"t":4.79657, "x":5.50365, "y":7.68988, "heading":-1.5708, "vx":-2.45005, "vy":0.46451, "omega":0.0, "ax":-0.04488, "ay":-0.65533, "alpha":0.0, "fx":[-0.76343,-0.76343,-0.76343,-0.76343], "fy":[-11.14702,-11.14702,-11.14702,-11.14702]}, - {"t":4.7966, "x":5.50358, "y":7.68989, "heading":-1.5708, "vx":-2.45005, "vy":0.46449, "omega":0.0, "ax":-0.09831, "ay":-0.64507, "alpha":0.0, "fx":[-1.67228,-1.67228,-1.67228,-1.67228], "fy":[-10.97237,-10.97237,-10.97237,-10.97237]}, - {"t":4.79663, "x":5.5035, "y":7.6899, "heading":-1.5708, "vx":-2.45005, "vy":0.46447, "omega":0.0, "ax":-0.15173, "ay":-0.63476, "alpha":0.0, "fx":[-2.58097,-2.58097,-2.58097,-2.58097], "fy":[-10.79703,-10.79703,-10.79703,-10.79703]}, - {"t":4.79666, "x":5.50343, "y":7.68992, "heading":-1.5708, "vx":-2.45006, "vy":0.46445, "omega":0.0, "ax":-0.20515, "ay":-0.62441, "alpha":0.0, "fx":[-3.48956,-3.48956,-3.48956,-3.48956], "fy":[-10.62098,-10.62098,-10.62098,-10.62098]}, - {"t":4.79669, "x":5.50335, "y":7.68993, "heading":-1.5708, "vx":-2.45007, "vy":0.46443, "omega":0.0, "ax":-0.25856, "ay":-0.61401, "alpha":0.0, "fx":[-4.39811,-4.39811,-4.39811,-4.39811], "fy":[-10.44421,-10.44421,-10.44421,-10.44421]}, - {"t":4.79673, "x":5.50328, "y":7.68995, "heading":-1.5708, "vx":-2.45007, "vy":0.46442, "omega":0.0, "ax":-0.31198, "ay":-0.60358, "alpha":0.0, "fx":[-5.30667,-5.30667,-5.30667,-5.30667], "fy":[-10.26674,-10.26674,-10.26674,-10.26674]}, - {"t":4.79676, "x":5.5032, "y":7.68996, "heading":-1.5708, "vx":-2.45008, "vy":0.4644, "omega":0.0, "ax":-0.3654, "ay":-0.5931, "alpha":0.0, "fx":[-6.2153,-6.2153,-6.2153,-6.2153], "fy":[-10.08853,-10.08853,-10.08853,-10.08853]}, - {"t":4.79679, "x":5.50313, "y":7.68998, "heading":-1.5708, "vx":-2.45009, "vy":0.46438, "omega":0.0, "ax":-0.41882, "ay":-0.58258, "alpha":0.0, "fx":[-7.12405,-7.12405,-7.12405,-7.12405], "fy":[-9.90957,-9.90957,-9.90957,-9.90957]}, - {"t":4.79682, "x":5.50305, "y":7.68999, "heading":-1.5708, "vx":-2.45011, "vy":0.46436, "omega":0.0, "ax":-0.47226, "ay":-0.57202, "alpha":0.0, "fx":[-8.03299,-8.03299,-8.03299,-8.03299], "fy":[-9.72984,-9.72984,-9.72984,-9.72984]}, - {"t":4.79685, "x":5.50298, "y":7.69, "heading":-1.5708, "vx":-2.45012, "vy":0.46434, "omega":0.0, "ax":-0.52571, "ay":-0.56141, "alpha":0.0, "fx":[-8.94217,-8.94217,-8.94217,-8.94217], "fy":[-9.54934,-9.54934,-9.54934,-9.54934]}, - {"t":4.79688, "x":5.5029, "y":7.69002, "heading":-1.5708, "vx":-2.45014, "vy":0.46433, "omega":0.0, "ax":-0.57918, "ay":-0.55075, "alpha":0.0, "fx":[-9.85165,-9.85165,-9.85165,-9.85165], "fy":[-9.36805,-9.36805,-9.36805,-9.36805]}, - {"t":4.79691, "x":5.50283, "y":7.69003, "heading":-1.5708, "vx":-2.45016, "vy":0.46431, "omega":0.0, "ax":-0.63267, "ay":-0.54004, "alpha":0.0, "fx":[-10.76149,-10.76149,-10.76149,-10.76149], "fy":[-9.18597,-9.18597,-9.18597,-9.18597]}, - {"t":4.79694, "x":5.50275, "y":7.69005, "heading":-1.5708, "vx":-2.45017, "vy":0.46429, "omega":0.0, "ax":-0.68618, "ay":-0.52929, "alpha":0.0, "fx":[-11.67176,-11.67176,-11.67176,-11.67176], "fy":[-9.00307,-9.00307,-9.00307,-9.00307]}, - {"t":4.79697, "x":5.50268, "y":7.69006, "heading":-1.5708, "vx":-2.4502, "vy":0.46428, "omega":0.0, "ax":-0.73973, "ay":-0.51849, "alpha":0.0, "fx":[-12.58252,-12.58252,-12.58252,-12.58252], "fy":[-8.81935,-8.81935,-8.81935,-8.81935]}, - {"t":4.797, "x":5.5026, "y":7.69008, "heading":-1.5708, "vx":-2.45022, "vy":0.46426, "omega":0.0, "ax":-0.7933, "ay":-0.50764, "alpha":0.0, "fx":[-13.49382,-13.49382,-13.49382,-13.49382], "fy":[-8.63479,-8.63479,-8.63479,-8.63479]}, - {"t":4.79703, "x":5.50253, "y":7.69009, "heading":-1.5708, "vx":-2.45024, "vy":0.46425, "omega":0.0, "ax":-0.84691, "ay":-0.49674, "alpha":0.0, "fx":[-14.40575,-14.40575,-14.40575,-14.40575], "fy":[-8.44939,-8.44939,-8.44939,-8.44939]}, - {"t":4.79706, "x":5.50245, "y":7.6901, "heading":-1.5708, "vx":-2.45027, "vy":0.46423, "omega":0.0, "ax":-0.90057, "ay":-0.48579, "alpha":0.0, "fx":[-15.31836,-15.31836,-15.31836,-15.31836], "fy":[-8.26311,-8.26311,-8.26311,-8.26311]}, - {"t":4.79709, "x":5.50238, "y":7.69012, "heading":-1.5708, "vx":-2.4503, "vy":0.46422, "omega":0.0, "ax":-0.95426, "ay":-0.47478, "alpha":0.0, "fx":[-16.23174,-16.23174,-16.23174,-16.23174], "fy":[-8.07595,-8.07595,-8.07595,-8.07595]}, - {"t":4.79712, "x":5.5023, "y":7.69013, "heading":-1.5708, "vx":-2.45032, "vy":0.4642, "omega":0.0, "ax":-1.00801, "ay":-0.46373, "alpha":0.0, "fx":[-17.14595,-17.14595,-17.14595,-17.14595], "fy":[-7.88789,-7.88789,-7.88789,-7.88789]}, - {"t":4.79715, "x":5.50223, "y":7.69015, "heading":-1.5708, "vx":-2.45036, "vy":0.46419, "omega":0.0, "ax":-1.06181, "ay":-0.45262, "alpha":0.0, "fx":[-18.06107,-18.06107,-18.06107,-18.06107], "fy":[-7.69892,-7.69892,-7.69892,-7.69892]}, - {"t":4.79718, "x":5.50215, "y":7.69016, "heading":-1.5708, "vx":-2.45039, "vy":0.46417, "omega":0.0, "ax":-8.73137, "ay":-3.51689, "alpha":0.0, "fx":[-148.51809,-148.51809,-148.51809,-148.51809], "fy":[-59.82128,-59.82128,-59.82128,-59.82128]}, - {"t":4.85865, "x":5.33503, "y":7.71205, "heading":-1.5708, "vx":-2.98712, "vy":0.24799, "omega":0.0, "ax":-8.45857, "ay":-4.00753, "alpha":0.0, "fx":[-143.8779,-143.8779,-143.8779,-143.8779], "fy":[-68.16685,-68.16685,-68.16685,-68.16685]}, - {"t":4.92013, "x":5.13543, "y":7.71972, "heading":-1.5708, "vx":-3.50707, "vy":0.00164, "omega":0.0, "ax":0.00241, "ay":-1.9313, "alpha":0.0, "fx":[0.04101,0.04101,0.04101,0.04101], "fy":[-32.85084,-32.85084,-32.85084,-32.85084]}, - {"t":4.9816, "x":4.91985, "y":7.71617, "heading":-1.5708, "vx":-3.50693, "vy":-0.11708, "omega":0.0, "ax":-0.02022, "ay":0.75625, "alpha":0.0, "fx":[-0.34391,-0.34391,-0.34391,-0.34391], "fy":[12.86368,12.86368,12.86368,12.86368]}, - {"t":5.04307, "x":4.70423, "y":7.7104, "heading":-1.5708, "vx":-3.50817, "vy":-0.07059, "omega":0.0, "ax":-0.00756, "ay":0.47392, "alpha":0.0, "fx":[-0.12867,-0.12867,-0.12867,-0.12867], "fy":[8.06126,8.06126,8.06126,8.06126]}, - {"t":5.10454, "x":4.48857, "y":7.70696, "heading":-1.5708, "vx":-3.50863, "vy":-0.04146, "omega":0.0, "ax":-0.00319, "ay":0.37286, "alpha":0.0, "fx":[-0.05421,-0.05421,-0.05421,-0.05421], "fy":[6.34231,6.34231,6.34231,6.34231]}, - {"t":5.16601, "x":4.27288, "y":7.70512, "heading":-1.5708, "vx":-3.50883, "vy":-0.01854, "omega":0.0, "ax":-0.00078, "ay":0.34033, "alpha":0.0, "fx":[-0.01334,-0.01334,-0.01334,-0.01334], "fy":[5.78888,5.78888,5.78888,5.78888]}, - {"t":5.22748, "x":4.05719, "y":7.70462, "heading":-1.5708, "vx":-3.50888, "vy":0.00238, "omega":0.0, "ax":0.00128, "ay":0.34546, "alpha":0.0, "fx":[0.02173,0.02173,0.02173,0.02173], "fy":[5.87611,5.87611,5.87611,5.87611]}, - {"t":5.28895, "x":3.8415, "y":7.70542, "heading":-1.5708, "vx":-3.5088, "vy":0.02362, "omega":0.0, "ax":0.00434, "ay":0.41744, "alpha":0.0, "fx":[0.07377,0.07377,0.07377,0.07377], "fy":[7.1005,7.1005,7.1005,7.1005]}, - {"t":5.35042, "x":3.62582, "y":7.70766, "heading":-1.5708, "vx":-3.50853, "vy":0.04928, "omega":0.0, "ax":0.15156, "ay":1.15232, "alpha":0.0, "fx":[2.57791,2.57791,2.57791,2.57791], "fy":[19.60067,19.60067,19.60067,19.60067]}, - {"t":5.4119, "x":3.41043, "y":7.71287, "heading":-1.5708, "vx":-3.49922, "vy":0.12011, "omega":0.0, "ax":9.38675, "ay":-0.17503, "alpha":0.0, "fx":[159.66599,159.66599,159.66599,159.66599], "fy":[-2.97729,-2.97729,-2.97729,-2.97729]}, - {"t":5.47337, "x":3.21306, "y":7.71992, "heading":-1.5708, "vx":-2.9222, "vy":0.10935, "omega":0.0, "ax":7.84575, "ay":-5.21281, "alpha":0.0, "fx":[133.45396,133.45396,133.45396,133.45396], "fy":[-88.66835,-88.66835,-88.66835,-88.66835]}, - {"t":5.53484, "x":3.04826, "y":7.71679, "heading":-1.5708, "vx":-2.43991, "vy":-0.21109, "omega":0.0, "ax":5.87131, "ay":-7.37781, "alpha":0.0, "fx":[99.86933,99.86933,99.86933,99.86933], "fy":[-125.49436,-125.49436,-125.49436,-125.49436]}, - {"t":5.59631, "x":2.90936, "y":7.68988, "heading":-1.5708, "vx":-2.079, "vy":-0.66461, "omega":0.0, "ax":4.73574, "ay":-2.24113, "alpha":28.37402, "fx":[160.27472,69.09988,1.29476,91.54525], "fy":[3.66289,135.94772,-160.37563,-131.71901]}, - {"t":5.62642, "x":2.84892, "y":7.66885, "heading":-1.5708, "vx":-1.93642, "vy":-0.73208, "omega":0.85427, "ax":5.74248, "ay":-3.13612, "alpha":22.87706, "fx":[160.2701,134.4333,3.48747,92.52104], "fy":[2.76173,75.203,-160.32461,-131.01786]}, - {"t":5.65652, "x":2.79322, "y":7.64539, "heading":-1.54508, "vx":-1.76352, "vy":-0.8265, "omega":1.54304, "ax":6.11453, "ay":-4.63139, "alpha":16.52944, "fx":[160.2375,151.61954,8.76948,95.39893], "fy":[2.685,-28.78564,-160.10004,-128.91407]}, - {"t":5.68663, "x":2.74289, "y":7.61841, "heading":-1.49862, "vx":-1.57943, "vy":-0.96594, "omega":2.0407, "ax":5.70015, "ay":-5.76109, "alpha":13.43746, "fx":[160.18666,111.06655,16.77621,99.80245], "fy":[2.81798,-109.86102,-159.43098,-125.50384]}, - {"t":5.71674, "x":2.69793, "y":7.58671, "heading":-1.43718, "vx":-1.40781, "vy":-1.1394, "omega":2.44527, "ax":5.56357, "ay":-6.0141, "alpha":12.7705, "fx":[160.13086,85.71017,27.37447,105.32378], "fy":[1.93419,-132.34862,-157.91665,-120.8612]}, - {"t":5.74685, "x":2.65806, "y":7.54968, "heading":-1.36356, "vx":-1.24031, "vy":-1.32047, "omega":2.82976, "ax":5.84622, "ay":-5.95647, "alpha":11.91357, "fx":[160.03367,84.79832,41.13956,111.79854], "fy":[-1.76256,-133.84143,-154.8416,-114.82583]}, - {"t":5.77695, "x":2.62337, "y":7.50723, "heading":-1.27836, "vx":-1.06429, "vy":-1.4998, "omega":3.18845, "ax":6.41611, "ay":-5.75764, "alpha":10.24429, "fx":[159.50444,97.97877,59.76814,119.2933], "fy":[-11.22961,-125.07614,-148.54584,-106.89188]}, - {"t":5.80706, "x":2.59423, "y":7.45946, "heading":-1.18236, "vx":-0.87112, "vy":-1.67315, "omega":3.49688, "ax":7.21177, "ay":-5.39521, "alpha":7.24132, "fx":[156.66303,118.4595,87.39004,128.16774], "fy":[-30.99674,-106.32556,-133.95758,-95.80392]}, - {"t":5.83717, "x":2.57127, "y":7.40664, "heading":-1.07708, "vx":-0.65399, "vy":-1.83558, "omega":3.7149, "ax":8.20538, "ay":-4.50099, "alpha":1.65216, "fx":[146.28967,139.46486,132.57252,139.95728], "fy":[-63.51624,-77.20752,-88.79145,-76.72678]}, - {"t":5.86728, "x":2.5553, "y":7.34934, "heading":-0.96523, "vx":-0.40695, "vy":-1.9711, "omega":3.76464, "ax":8.64773, "ay":-3.11098, "alpha":-4.87401, "fx":[131.85994,149.14157,158.63562,148.74453], "fy":[-89.35573,-56.34366,-11.05768,-54.91068]}, - {"t":5.89738, "x":2.54697, "y":7.28858, "heading":-0.85189, "vx":-0.14659, "vy":-2.06476, "omega":3.6179, "ax":8.43218, "ay":-2.93477, "alpha":-7.2544, "fx":[123.70834,150.77632,157.266,141.96528], "fy":[-99.86342,-51.01484,18.16301,-66.96303]}, - {"t":5.92749, "x":2.54638, "y":7.22509, "heading":-0.74296, "vx":0.10729, "vy":-2.15312, "omega":3.39948, "ax":7.91676, "ay":-3.15459, "alpha":-9.84013, "fx":[117.83961,151.69097,152.197,116.9198], "fy":[-106.04266,-46.71452,38.65275,-100.5301]}, - {"t":5.9576, "x":2.5532, "y":7.15883, "heading":-0.64061, "vx":0.34564, "vy":-2.2481, "omega":3.10322, "ax":7.26814, "ay":-3.56513, "alpha":-12.12399, "fx":[114.78515,151.21562,149.20891,79.30628], "fy":[-108.0899,-45.18453,39.7556,-129.04875]}, - {"t":5.98771, "x":2.5669, "y":7.08953, "heading":-0.54718, "vx":0.56447, "vy":-2.35544, "omega":2.7382, "ax":6.84783, "ay":-3.61669, "alpha":-13.13368, "fx":[114.31053,150.01862,143.89953,57.69004], "fy":[-105.52216,-41.53302,35.51449,-134.5347]}, - {"t":6.01781, "x":2.587, "y":7.01698, "heading":-0.46474, "vx":0.77064, "vy":-2.46432, "omega":2.34278, "ax":7.10944, "ay":-1.62064, "alpha":-9.77178, "fx":[124.15161,143.55214,120.98326,95.03131], "fy":[-71.51673,-14.75559,44.87106,-68.86552]}, - {"t":6.04792, "x":2.61342, "y":6.94205, "heading":-0.39421, "vx":0.98469, "vy":-2.51312, "omega":2.04857, "ax":-1.28681, "ay":6.58582, "alpha":17.21934, "fx":[-65.95488,-122.32449,43.81675,56.90979], "fy":[135.24556,82.44758,96.39833,134.0002]}, - {"t":6.07803, "x":2.64248, "y":6.86937, "heading":-0.33253, "vx":0.94594, "vy":-2.31484, "omega":2.567, "ax":-1.80581, "ay":6.71336, "alpha":18.61551, "fx":[-83.63261,-140.00722,48.78676,51.98787], "fy":[132.27638,67.47992,111.92133,145.09163]}, - {"t":6.10814, "x":2.67015, "y":6.80272, "heading":-0.25525, "vx":0.89158, "vy":-2.11271, "omega":3.12747, "ax":-1.54962, "ay":6.97508, "alpha":19.62196, "fx":[-86.14793,-141.57018,73.44541,48.83818], "fy":[132.47589,68.36304,124.7053,149.03229]}, - {"t":6.13824, "x":2.69629, "y":6.74227, "heading":-0.16108, "vx":0.84492, "vy":-1.90271, "omega":3.71824, "ax":-1.37327, "ay":7.15869, "alpha":20.32393, "fx":[-85.28337,-139.4197,84.86644,46.40121], "fy":[133.86131,74.26051,127.77566,151.17181]}, - {"t":6.16835, "x":2.7211, "y":6.68823, "heading":-0.04914, "vx":0.80357, "vy":-1.68718, "omega":4.33014, "ax":-3.57787, "ay":8.62717, "alpha":-0.72783, "fx":[-58.58666,-56.56772,-63.07349,-65.20655], "fy":[147.66905,148.50782,145.89913,144.90675]}, - {"t":6.19846, "x":2.74367, "y":6.64134, "heading":0.08123, "vx":0.69585, "vy":-1.42744, "omega":4.30823, "ax":-3.40138, "ay":8.07663, "alpha":-11.57178, "fx":[-6.04113,-8.97404,-92.98799,-123.42311], "fy":[158.99378,159.45296,129.98543,101.09277]}, - {"t":6.22857, "x":2.76308, "y":6.60203, "heading":0.21094, "vx":0.59345, "vy":-1.18427, "omega":3.95983, "ax":-3.12634, "ay":7.47355, "alpha":-17.24091, "fx":[35.29092,0.45629,-106.54164,-141.91784], "fy":[155.55007,160.00929,119.45862,73.47352]}, - {"t":6.25867, "x":2.77953, "y":6.56976, "heading":0.33016, "vx":0.49932, "vy":-0.95926, "omega":3.44075, "ax":-3.10013, "ay":6.97896, "alpha":-20.40568, "fx":[55.61033,-0.16102,-115.95547,-150.42297], "fy":[149.81703,160.15819,110.50957,54.35596]}, - {"t":6.28878, "x":2.79316, "y":6.54404, "heading":0.43376, "vx":0.40598, "vy":-0.74914, "omega":2.82639, "ax":-3.20054, "ay":6.60224, "alpha":-22.17409, "fx":[64.01482,-3.72044,-123.04192,-155.01348], "fy":[146.63056,160.20011,102.66392,39.71419]}, - {"t":6.31889, "x":2.80393, "y":6.52448, "heading":0.51885, "vx":0.30962, "vy":-0.55036, "omega":2.15878, "ax":-3.32974, "ay":6.30993, "alpha":-23.23608, "fx":[67.21738,-7.75348,-128.40156,-157.61435], "fy":[145.32169,160.10981,95.94887,27.94008]}, - {"t":6.349, "x":2.81175, "y":6.51077, "heading":0.58385, "vx":0.20937, "vy":-0.36039, "omega":1.4592, "ax":-3.4411, "ay":6.07747, "alpha":-23.95324, "fx":[68.48941,-11.19751,-132.35558,-159.06497], "fy":[144.81243,159.94298,90.47224,18.27663]}, - {"t":6.3791, "x":2.81649, "y":6.50267, "heading":0.62778, "vx":0.10577, "vy":-0.17741, "omega":0.73803, "ax":-3.51303, "ay":5.8925, "alpha":-24.51299, "fx":[69.43028,-13.52833,-135.11278,-159.81147], "fy":[144.42198,159.7895,86.34571,10.3615]}, - {"t":6.40921, "x":2.81808, "y":6.5, "heading":0.65, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0,0.0,0.0,0.0]}], + {"t":0.0, "x":4.44125, "y":7.69346, "heading":-1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":9.41816, "ay":-0.44598, "alpha":0.0, "fx":[160.20024,160.20024,160.20024,160.20024], "fy":[-7.58597,-7.58597,-7.58602,-7.58602]}, + {"t":0.04533, "x":4.45093, "y":7.693, "heading":-1.5708, "vx":0.42688, "vy":-0.02021, "omega":0.0, "ax":9.41744, "ay":-0.43468, "alpha":0.0, "fx":[160.18797,160.18797,160.18797,160.18797], "fy":[-7.39371,-7.39371,-7.39375,-7.39375]}, + {"t":0.09065, "x":4.47995, "y":7.69164, "heading":-1.5708, "vx":0.85373, "vy":-0.03992, "omega":0.0, "ax":9.41626, "ay":-0.42391, "alpha":0.0, "fx":[160.16794,160.16794,160.16794,160.16794], "fy":[-7.21059,-7.2106,-7.21063,-7.21063]}, + {"t":0.13598, "x":4.52832, "y":7.68939, "heading":-1.5708, "vx":1.28052, "vy":-0.05913, "omega":0.0, "ax":9.41438, "ay":-0.41371, "alpha":0.0, "fx":[160.13587,160.13587,160.13587,160.13587], "fy":[-7.03715,-7.03715,-7.03718,-7.03718]}, + {"t":0.1813, "x":4.59603, "y":7.68629, "heading":-1.5708, "vx":1.70723, "vy":-0.07788, "omega":0.0, "ax":9.41128, "ay":-0.40404, "alpha":0.0, "fx":[160.08321,160.08321,160.08321,160.08321], "fy":[-6.87267,-6.87267,-6.87269,-6.87269]}, + {"t":0.22663, "x":4.68307, "y":7.68234, "heading":-1.5708, "vx":2.1338, "vy":-0.09619, "omega":0.0, "ax":9.4058, "ay":-0.39474, "alpha":0.0, "fx":[159.98994,159.98994,159.98994,159.98994], "fy":[-6.71444,-6.71444,-6.71446,-6.71446]}, + {"t":0.27195, "x":4.78945, "y":7.67758, "heading":-1.5708, "vx":2.56011, "vy":-0.11409, "omega":0.0, "ax":9.39437, "ay":-0.38534, "alpha":0.0, "fx":[159.79552,159.79552,159.79552,159.79552], "fy":[-6.5545,-6.5545,-6.55451,-6.55451]}, + {"t":0.31728, "x":4.91514, "y":7.67201, "heading":-1.5708, "vx":2.98592, "vy":-0.13155, "omega":0.0, "ax":9.35899, "ay":-0.37364, "alpha":0.0, "fx":[159.19372,159.19372,159.19372,159.19372], "fy":[-6.35546,-6.35546,-6.35546,-6.35546]}, + {"t":0.3626, "x":5.06009, "y":7.66566, "heading":-1.5708, "vx":3.41011, "vy":-0.14849, "omega":0.0, "ax":2.10054, "ay":0.22429, "alpha":-0.00002, "fx":[35.72948,35.72956,35.72956,35.72948], "fy":[3.81506,3.81506,3.8152,3.8152]}, + {"t":0.40793, "x":5.21681, "y":7.65916, "heading":-1.5708, "vx":3.50532, "vy":-0.13832, "omega":0.0, "ax":0.00486, "ay":0.11952, "alpha":0.0, "fx":[0.08261,0.08261,0.08261,0.08261], "fy":[2.03294,2.03294,2.03294,2.03294]}, + {"t":0.45325, "x":5.37569, "y":7.65302, "heading":-1.5708, "vx":3.50554, "vy":-0.1329, "omega":0.0, "ax":0.00346, "ay":0.09287, "alpha":0.0, "fx":[0.05887,0.05887,0.05887,0.05887], "fy":[1.57975,1.57975,1.57975,1.57975]}, + {"t":0.49858, "x":5.53459, "y":7.64709, "heading":-1.5708, "vx":3.5057, "vy":-0.12869, "omega":0.0, "ax":0.00306, "ay":0.08474, "alpha":0.0, "fx":[0.05206,0.05206,0.05206,0.05206], "fy":[1.44146,1.44146,1.44146,1.44146]}, + {"t":0.5439, "x":5.69349, "y":7.64134, "heading":-1.5708, "vx":3.50584, "vy":-0.12485, "omega":0.0, "ax":0.00278, "ay":0.07933, "alpha":0.0, "fx":[0.04731,0.04731,0.04731,0.04731], "fy":[1.34945,1.34945,1.34945,1.34945]}, + {"t":0.58923, "x":5.85239, "y":7.63577, "heading":-1.5708, "vx":3.50596, "vy":-0.12126, "omega":0.0, "ax":0.00255, "ay":0.07488, "alpha":0.0, "fx":[0.04338,0.04338,0.04338,0.04338], "fy":[1.27377,1.27377,1.27377,1.27377]}, + {"t":0.63455, "x":6.0113, "y":7.63035, "heading":-1.5708, "vx":3.50608, "vy":-0.11786, "omega":0.0, "ax":0.00235, "ay":0.07107, "alpha":0.0, "fx":[0.04003,0.04003,0.04003,0.04003], "fy":[1.20891,1.20891,1.20891,1.20891]}, + {"t":0.67988, "x":6.17022, "y":7.62508, "heading":-1.5708, "vx":3.50618, "vy":-0.11464, "omega":0.0, "ax":0.00218, "ay":0.06773, "alpha":0.0, "fx":[0.03712,0.03712,0.03712,0.03712], "fy":[1.15211,1.15211,1.15211,1.15211]}, + {"t":0.7252, "x":6.32914, "y":7.61995, "heading":-1.5708, "vx":3.50628, "vy":-0.11157, "omega":0.0, "ax":0.00202, "ay":0.06453, "alpha":0.0, "fx":[0.03443,0.03443,0.03443,0.03443], "fy":[1.0976,1.0976,1.0976,1.0976]}, + {"t":0.77053, "x":6.48806, "y":7.61496, "heading":-1.5708, "vx":3.50637, "vy":-0.10865, "omega":0.0, "ax":0.0018, "ay":0.05891, "alpha":0.0, "fx":[0.03063,0.03063,0.03063,0.03063], "fy":[1.00212,1.00212,1.00212,1.00212]}, + {"t":0.81585, "x":6.64699, "y":7.6101, "heading":-1.5708, "vx":3.50646, "vy":-0.10598, "omega":0.0, "ax":0.00077, "ay":0.02559, "alpha":0.0, "fx":[0.01304,0.01304,0.01304,0.01304], "fy":[0.43528,0.43528,0.43528,0.43528]}, + {"t":0.86118, "x":6.80592, "y":7.60532, "heading":-1.5708, "vx":3.50649, "vy":-0.10482, "omega":0.0, "ax":-0.00964, "ay":-0.29287, "alpha":0.0, "fx":[-0.16398,-0.16398,-0.16398,-0.16398], "fy":[-4.98166,-4.98166,-4.98166,-4.98166]}, + {"t":0.9065, "x":6.96485, "y":7.60027, "heading":-1.5708, "vx":3.50605, "vy":-0.11809, "omega":0.0, "ax":-2.6186, "ay":-2.84765, "alpha":0.00001, "fx":[-44.54169,-44.54175,-44.54173,-44.54167], "fy":[-48.4377,-48.43769,-48.43777,-48.43778]}, + {"t":0.95183, "x":7.12107, "y":7.59199, "heading":-1.5708, "vx":3.38737, "vy":-0.24716, "omega":0.0, "ax":-9.36476, "ay":0.19773, "alpha":0.0, "fx":[-159.29182,-159.29182,-159.29182,-159.29182], "fy":[3.3634,3.3634,3.36339,3.36339]}, + {"t":0.99715, "x":7.26498, "y":7.58099, "heading":-1.5708, "vx":2.96291, "vy":-0.2382, "omega":0.0, "ax":-9.39963, "ay":0.22627, "alpha":0.0, "fx":[-159.88508,-159.88508,-159.88508,-159.88508], "fy":[3.84878,3.84878,3.84877,3.84877]}, + {"t":1.04248, "x":7.38962, "y":7.57043, "heading":-1.5708, "vx":2.53687, "vy":-0.22794, "omega":0.0, "ax":-9.4112, "ay":0.23411, "alpha":0.0, "fx":[-160.08182,-160.08182,-160.08182,-160.08182], "fy":[3.98213,3.98213,3.98213,3.98213]}, + {"t":1.0878, "x":7.49494, "y":7.56034, "heading":-1.5708, "vx":2.1103, "vy":-0.21733, "omega":0.0, "ax":-9.41699, "ay":0.23672, "alpha":0.0, "fx":[-160.18032,-160.18032,-160.18032,-160.18032], "fy":[4.0265,4.0265,4.0265,4.0265]}, + {"t":1.13313, "x":7.58091, "y":7.55073, "heading":-1.5708, "vx":1.68348, "vy":-0.2066, "omega":0.0, "ax":-9.42048, "ay":0.23726, "alpha":0.0, "fx":[-160.23972,-160.23972,-160.23972,-160.23972], "fy":[4.03569,4.03569,4.03569,4.03569]}, + {"t":1.17845, "x":7.64754, "y":7.54161, "heading":-1.5708, "vx":1.25649, "vy":-0.19585, "omega":0.0, "ax":-9.42283, "ay":0.23679, "alpha":0.0, "fx":[-160.2796,-160.2796,-160.2796,-160.2796], "fy":[4.02777,4.02777,4.02777,4.02777]}, + {"t":1.22378, "x":7.69481, "y":7.53297, "heading":-1.5708, "vx":0.8294, "vy":-0.18512, "omega":0.0, "ax":-4.07456, "ay":-7.9866, "alpha":0.0, "fx":[-69.30705,-69.30706,-69.30706,-69.30705], "fy":[-135.84984,-135.84983,-135.84984,-135.84984]}, + {"t":1.27108, "x":7.72949, "y":7.51528, "heading":-1.5708, "vx":0.63667, "vy":-0.56289, "omega":0.0, "ax":-0.36121, "ay":-0.39652, "alpha":0.0, "fx":[-6.14409,-6.14411,-6.14411,-6.14409], "fy":[-6.7447,-6.7447,-6.74472,-6.74472]}, + {"t":1.31838, "x":7.7592, "y":7.48821, "heading":-1.5708, "vx":0.61959, "vy":-0.58164, "omega":0.0, "ax":-0.00138, "ay":-0.00146, "alpha":0.0, "fx":[-0.02339,-0.02339,-0.02339,-0.02339], "fy":[-0.0249,-0.0249,-0.02491,-0.02491]}, + {"t":1.36568, "x":7.7885, "y":7.4607, "heading":-1.5708, "vx":0.61952, "vy":-0.58171, "omega":0.0, "ax":0.00077, "ay":0.00082, "alpha":0.0, "fx":[0.01304,0.01304,0.01304,0.01304], "fy":[0.01389,0.01389,0.01389,0.01389]}, + {"t":1.41298, "x":7.81781, "y":7.43319, "heading":-1.5708, "vx":0.61956, "vy":-0.58167, "omega":0.0, "ax":0.00072, "ay":0.00077, "alpha":0.0, "fx":[0.01223,0.01224,0.01224,0.01223], "fy":[0.01303,0.01303,0.01304,0.01304]}, + {"t":1.46028, "x":7.84711, "y":7.40567, "heading":-1.5708, "vx":0.61959, "vy":-0.58164, "omega":0.0, "ax":0.00067, "ay":0.00071, "alpha":0.0, "fx":[0.01136,0.01137,0.01137,0.01136], "fy":[0.0121,0.0121,0.01211,0.01211]}, + {"t":1.50758, "x":7.87642, "y":7.37816, "heading":-1.5708, "vx":0.61963, "vy":-0.5816, "omega":0.0, "ax":0.00062, "ay":0.00066, "alpha":0.0, "fx":[0.01061,0.01062,0.01062,0.01061], "fy":[0.01131,0.01131,0.01132,0.01132]}, + {"t":1.55488, "x":7.90573, "y":7.35066, "heading":-1.5708, "vx":0.61965, "vy":-0.58157, "omega":0.0, "ax":0.00059, "ay":0.00062, "alpha":0.0, "fx":[0.00996,0.00996,0.00996,0.00996], "fy":[0.01061,0.01061,0.01062,0.01062]}, + {"t":1.60218, "x":7.93504, "y":7.32315, "heading":-1.5708, "vx":0.61968, "vy":-0.58154, "omega":0.0, "ax":0.00055, "ay":0.00059, "alpha":0.0, "fx":[0.00938,0.00939,0.00939,0.00938], "fy":[0.01,0.01,0.01001,0.01001]}, + {"t":1.64948, "x":7.96435, "y":7.29564, "heading":-1.5708, "vx":0.61971, "vy":-0.58151, "omega":0.0, "ax":0.00052, "ay":0.00056, "alpha":0.0, "fx":[0.00887,0.00888,0.00888,0.00887], "fy":[0.00946,0.00946,0.00947,0.00947]}, + {"t":1.69678, "x":7.99366, "y":7.26814, "heading":-1.5708, "vx":0.61973, "vy":-0.58149, "omega":0.0, "ax":0.00049, "ay":0.00053, "alpha":0.0, "fx":[0.00842,0.00842,0.00842,0.00842], "fy":[0.00897,0.00897,0.00898,0.00898]}, + {"t":1.74408, "x":8.02298, "y":7.24063, "heading":-1.5708, "vx":0.61976, "vy":-0.58146, "omega":0.0, "ax":0.00047, "ay":0.0005, "alpha":0.0, "fx":[0.00801,0.00801,0.00801,0.00801], "fy":[0.00853,0.00853,0.00854,0.00854]}, + {"t":1.79138, "x":8.05229, "y":7.21313, "heading":-1.5708, "vx":0.61978, "vy":-0.58144, "omega":0.0, "ax":0.00045, "ay":0.00048, "alpha":0.0, "fx":[0.00763,0.00764,0.00764,0.00763], "fy":[0.00814,0.00814,0.00815,0.00815]}, + {"t":1.83868, "x":8.08161, "y":7.18563, "heading":-1.5708, "vx":0.6198, "vy":-0.58142, "omega":0.0, "ax":0.00043, "ay":0.00046, "alpha":0.0, "fx":[0.00729,0.0073,0.0073,0.00729], "fy":[0.00778,0.00778,0.00778,0.00778]}, + {"t":1.88598, "x":8.11093, "y":7.15813, "heading":-1.5708, "vx":0.61982, "vy":-0.58139, "omega":0.0, "ax":0.00041, "ay":0.00044, "alpha":0.0, "fx":[0.00698,0.00699,0.00699,0.00698], "fy":[0.00745,0.00745,0.00745,0.00745]}, + {"t":1.93328, "x":8.14024, "y":7.13063, "heading":-1.5708, "vx":0.61984, "vy":-0.58137, "omega":0.0, "ax":0.00039, "ay":0.00042, "alpha":0.0, "fx":[0.0067,0.0067,0.0067,0.0067], "fy":[0.00714,0.00714,0.00715,0.00715]}, + {"t":1.98058, "x":8.16956, "y":7.10313, "heading":-1.5708, "vx":0.61986, "vy":-0.58135, "omega":0.0, "ax":0.00038, "ay":0.0004, "alpha":0.0, "fx":[0.00643,0.00643,0.00643,0.00643], "fy":[0.00686,0.00686,0.00686,0.00686]}, + {"t":2.02788, "x":8.19888, "y":7.07563, "heading":-1.5708, "vx":0.61988, "vy":-0.58133, "omega":0.0, "ax":0.00036, "ay":0.00039, "alpha":0.0, "fx":[0.00619,0.00619,0.00619,0.00619], "fy":[0.0066,0.0066,0.0066,0.0066]}, + {"t":2.07518, "x":8.2282, "y":7.04813, "heading":-1.5708, "vx":0.61989, "vy":-0.58132, "omega":0.0, "ax":0.00035, "ay":0.00037, "alpha":0.0, "fx":[0.00596,0.00596,0.00596,0.00596], "fy":[0.00636,0.00636,0.00636,0.00636]}, + {"t":2.12248, "x":8.25753, "y":7.02064, "heading":-1.5708, "vx":0.61991, "vy":-0.5813, "omega":0.0, "ax":0.00034, "ay":0.00036, "alpha":0.0, "fx":[0.00575,0.00575,0.00575,0.00575], "fy":[0.00613,0.00613,0.00613,0.00613]}, + {"t":2.16978, "x":8.28685, "y":6.99314, "heading":-1.5708, "vx":0.61993, "vy":-0.58128, "omega":0.0, "ax":0.00033, "ay":0.00035, "alpha":0.0, "fx":[0.00555,0.00555,0.00555,0.00555], "fy":[0.00592,0.00592,0.00592,0.00592]}, + {"t":2.21708, "x":8.31617, "y":6.96565, "heading":-1.5708, "vx":0.61994, "vy":-0.58126, "omega":0.0, "ax":0.00032, "ay":0.00034, "alpha":0.0, "fx":[0.00536,0.00536,0.00536,0.00536], "fy":[0.00572,0.00572,0.00572,0.00572]}, + {"t":2.26438, "x":8.34549, "y":6.93816, "heading":-1.5708, "vx":0.61996, "vy":-0.58125, "omega":0.0, "ax":0.00031, "ay":0.00033, "alpha":0.0, "fx":[0.00519,0.00519,0.00519,0.00519], "fy":[0.00553,0.00553,0.00554,0.00554]}, + {"t":2.31168, "x":8.37482, "y":6.91066, "heading":-1.5708, "vx":0.61997, "vy":-0.58123, "omega":0.0, "ax":0.0003, "ay":0.00032, "alpha":0.0, "fx":[0.00502,0.00502,0.00502,0.00502], "fy":[0.00536,0.00536,0.00536,0.00536]}, + {"t":2.35898, "x":8.40414, "y":6.88317, "heading":-1.5708, "vx":0.61998, "vy":-0.58122, "omega":0.0, "ax":0.00029, "ay":0.0003, "alpha":0.0, "fx":[0.00485,0.00485,0.00485,0.00485], "fy":[0.00517,0.00517,0.00518,0.00518]}, + {"t":2.40628, "x":8.43347, "y":6.85568, "heading":-1.5708, "vx":0.62, "vy":-0.5812, "omega":0.0, "ax":0.0001, "ay":0.0001, "alpha":0.0, "fx":[0.00166,0.00166,0.00166,0.00166], "fy":[0.00178,0.00178,0.00178,0.00178]}, + {"t":2.45358, "x":8.4628, "y":6.82819, "heading":-1.5708, "vx":0.62, "vy":-0.5812, "omega":0.0, "ax":-0.02953, "ay":-0.03143, "alpha":0.0, "fx":[-0.50237,-0.50237,-0.50237,-0.50237], "fy":[-0.53459,-0.53459,-0.53459,-0.53459]}, + {"t":2.50088, "x":8.49209, "y":6.80066, "heading":-1.5708, "vx":0.61861, "vy":-0.58269, "omega":0.0, "ax":-4.01466, "ay":-3.19333, "alpha":0.0, "fx":[-68.28818,-68.28818,-68.28818,-68.28818], "fy":[-54.31758,-54.31758,-54.31758,-54.31758]}, + {"t":2.54818, "x":8.51686, "y":6.76953, "heading":-1.5708, "vx":0.42871, "vy":-0.73373, "omega":0.0, "ax":-9.06179, "ay":-2.44816, "alpha":0.0, "fx":[-154.13841,-154.13841,-154.13841,-154.13841], "fy":[-41.64245,-41.64245,-41.64245,-41.64245]}, + {"t":2.59549, "x":8.527, "y":6.73208, "heading":-1.5708, "vx":0.00009, "vy":-0.84953, "omega":0.0, "ax":0.00282, "ay":-0.00614, "alpha":0.0, "fx":[0.04795,0.04795,0.04795,0.04795], "fy":[-0.10452,-0.10452,-0.10452,-0.10452]}, + {"t":2.64277, "x":8.52701, "y":6.69191, "heading":-1.5708, "vx":0.00022, "vy":-0.84982, "omega":0.0, "ax":-0.02232, "ay":0.00001, "alpha":0.0, "fx":[-0.37965,-0.37965,-0.37965,-0.37965], "fy":[0.00014,0.00014,0.00014,0.00014]}, + {"t":2.69006, "x":8.52699, "y":6.65172, "heading":-1.5708, "vx":-0.00084, "vy":-0.84982, "omega":0.0, "ax":0.04833, "ay":0.00002, "alpha":0.0, "fx":[0.82203,0.82203,0.82203,0.82203], "fy":[0.0003,0.0003,0.0003,0.0003]}, + {"t":2.73735, "x":8.52701, "y":6.61153, "heading":-1.5708, "vx":0.00145, "vy":-0.84982, "omega":0.0, "ax":-0.07435, "ay":0.00003, "alpha":0.0, "fx":[-1.2647,-1.2647,-1.2647,-1.2647], "fy":[0.00046,0.00046,0.00046,0.00046]}, + {"t":2.78464, "x":8.52699, "y":6.57135, "heading":-1.5708, "vx":-0.00207, "vy":-0.84982, "omega":0.0, "ax":0.1004, "ay":0.00004, "alpha":0.0, "fx":[1.70781,1.70781,1.70781,1.70781], "fy":[0.00062,0.00062,0.00062,0.00062]}, + {"t":2.83193, "x":8.52701, "y":6.53116, "heading":-1.5708, "vx":0.00268, "vy":-0.84982, "omega":0.0, "ax":-0.12649, "ay":0.00005, "alpha":0.0, "fx":[-2.1515,-2.1515,-2.1515,-2.1515], "fy":[0.00078,0.00078,0.00078,0.00078]}, + {"t":2.87922, "x":8.52699, "y":6.49097, "heading":-1.5708, "vx":-0.0033, "vy":-0.84981, "omega":0.0, "ax":0.15261, "ay":0.00006, "alpha":0.0, "fx":[2.59593,2.59593,2.59593,2.59593], "fy":[0.00095,0.00095,0.00095,0.00095]}, + {"t":2.9265, "x":8.52701, "y":6.45079, "heading":-1.5708, "vx":0.00392, "vy":-0.84981, "omega":0.0, "ax":-0.17879, "ay":0.00007, "alpha":0.0, "fx":[-3.04123,-3.04123,-3.04123,-3.04123], "fy":[0.00111,0.00111,0.00111,0.00111]}, + {"t":2.97379, "x":8.52699, "y":6.4106, "heading":-1.5708, "vx":-0.00454, "vy":-0.84981, "omega":0.0, "ax":0.20503, "ay":0.00007, "alpha":0.0, "fx":[3.48754,3.48754,3.48754,3.48754], "fy":[0.00128,0.00128,0.00128,0.00128]}, + {"t":3.02108, "x":8.52701, "y":6.37041, "heading":-1.5708, "vx":0.00516, "vy":-0.8498, "omega":0.0, "ax":-0.23134, "ay":0.00008, "alpha":0.0, "fx":[-3.93501,-3.93501,-3.93501,-3.93501], "fy":[0.00144,0.00144,0.00144,0.00144]}, + {"t":3.06837, "x":8.52699, "y":6.33023, "heading":-1.5708, "vx":-0.00578, "vy":-0.8498, "omega":0.0, "ax":0.25772, "ay":0.00009, "alpha":0.0, "fx":[4.38374,4.38374,4.38374,4.38374], "fy":[0.00161,0.00161,0.00161,0.00161]}, + {"t":3.11566, "x":8.52701, "y":6.29004, "heading":-1.5708, "vx":0.00641, "vy":-0.8498, "omega":0.0, "ax":-0.28418, "ay":0.0001, "alpha":0.0, "fx":[-4.83388,-4.83388,-4.83388,-4.83388], "fy":[0.00178,0.00178,0.00178,0.00178]}, + {"t":3.16295, "x":8.52699, "y":6.24986, "heading":-1.5708, "vx":-0.00703, "vy":-0.84979, "omega":0.0, "ax":0.31074, "ay":0.00012, "alpha":0.0, "fx":[5.28553,5.28553,5.28553,5.28553], "fy":[0.00196,0.00196,0.00196,0.00196]}, + {"t":3.21023, "x":8.52701, "y":6.20967, "heading":-1.5708, "vx":0.00766, "vy":-0.84978, "omega":0.0, "ax":-0.33738, "ay":0.00013, "alpha":0.0, "fx":[-5.73881,-5.73881,-5.73881,-5.73881], "fy":[0.00213,0.00213,0.00213,0.00213]}, + {"t":3.25752, "x":8.52699, "y":6.16949, "heading":-1.5708, "vx":-0.00829, "vy":-0.84978, "omega":0.0, "ax":0.36413, "ay":0.00014, "alpha":0.0, "fx":[6.19381,6.19381,6.19381,6.19381], "fy":[0.00231,0.00231,0.00231,0.00231]}, + {"t":3.30481, "x":8.52701, "y":6.1293, "heading":-1.5708, "vx":0.00893, "vy":-0.84977, "omega":0.0, "ax":-0.39099, "ay":0.00015, "alpha":0.0, "fx":[-6.65064,-6.65064,-6.65064,-6.65064], "fy":[0.00249,0.00249,0.00249,0.00249]}, + {"t":3.3521, "x":8.52699, "y":6.08912, "heading":-1.5708, "vx":-0.00956, "vy":-0.84977, "omega":0.0, "ax":0.41796, "ay":0.00016, "alpha":0.0, "fx":[7.10937,7.10937,7.10937,7.10937], "fy":[0.00267,0.00267,0.00267,0.00267]}, + {"t":3.39939, "x":8.52701, "y":6.04894, "heading":-1.5708, "vx":0.0102, "vy":-0.84976, "omega":0.0, "ax":-0.44505, "ay":0.00017, "alpha":0.0, "fx":[-7.5701,-7.5701,-7.5701,-7.5701], "fy":[0.00286,0.00286,0.00286,0.00286]}, + {"t":3.44668, "x":8.52699, "y":6.00875, "heading":-1.5708, "vx":-0.01084, "vy":-0.84975, "omega":0.0, "ax":0.47225, "ay":0.00018, "alpha":0.0, "fx":[8.03289,8.03289,8.03289,8.03289], "fy":[0.00305,0.00305,0.00305,0.00305]}, + {"t":3.49396, "x":8.52701, "y":5.96857, "heading":-1.5708, "vx":0.01149, "vy":-0.84974, "omega":0.0, "ax":-0.49959, "ay":0.00019, "alpha":0.0, "fx":[-8.4978,-8.4978,-8.4978,-8.4978], "fy":[0.00324,0.00324,0.00324,0.00324]}, + {"t":3.54125, "x":8.52699, "y":5.92839, "heading":-1.5708, "vx":-0.01214, "vy":-0.84973, "omega":0.0, "ax":0.52705, "ay":0.0002, "alpha":0.0, "fx":[8.96491,8.96491,8.96491,8.96491], "fy":[0.00343,0.00343,0.00343,0.00343]}, + {"t":3.58854, "x":8.52701, "y":5.8882, "heading":-1.5708, "vx":0.01279, "vy":-0.84972, "omega":0.0, "ax":-0.55464, "ay":0.00021, "alpha":0.0, "fx":[-9.43425,-9.43425,-9.43425,-9.43425], "fy":[0.00363,0.00363,0.00363,0.00363]}, + {"t":3.63583, "x":8.52699, "y":5.84802, "heading":-1.5708, "vx":-0.01344, "vy":-0.84971, "omega":0.0, "ax":0.58237, "ay":0.00023, "alpha":0.0, "fx":[9.90587,9.90587,9.90587,9.90587], "fy":[0.00383,0.00383,0.00383,0.00383]}, + {"t":3.68312, "x":8.52701, "y":5.80784, "heading":-1.5708, "vx":0.0141, "vy":-0.8497, "omega":0.0, "ax":-0.61023, "ay":0.00024, "alpha":0.0, "fx":[-10.37981,-10.37981,-10.37981,-10.37981], "fy":[0.00403,0.00403,0.00403,0.00403]}, + {"t":3.73041, "x":8.52699, "y":5.76766, "heading":-1.5708, "vx":-0.01476, "vy":-0.84969, "omega":0.0, "ax":0.63823, "ay":0.00025, "alpha":0.0, "fx":[10.85609,10.85609,10.85609,10.85609], "fy":[0.00424,0.00424,0.00424,0.00424]}, + {"t":3.7777, "x":8.52701, "y":5.72748, "heading":-1.5708, "vx":0.01542, "vy":-0.84968, "omega":0.0, "ax":-0.66637, "ay":0.00026, "alpha":0.0, "fx":[-11.33475,-11.33475,-11.33475,-11.33475], "fy":[0.00445,0.00445,0.00445,0.00445]}, + {"t":3.82498, "x":8.52699, "y":5.6873, "heading":-1.5708, "vx":-0.01609, "vy":-0.84967, "omega":0.0, "ax":0.69465, "ay":0.00027, "alpha":0.0, "fx":[11.81579,11.81579,11.81579,11.81579], "fy":[0.00466,0.00466,0.00466,0.00466]}, + {"t":3.87227, "x":8.52701, "y":5.64712, "heading":-1.5708, "vx":0.01676, "vy":-0.84965, "omega":0.0, "ax":-0.72307, "ay":0.00029, "alpha":0.0, "fx":[-12.29923,-12.29923,-12.29923,-12.29923], "fy":[0.00488,0.00488,0.00488,0.00488]}, + {"t":3.91956, "x":8.52699, "y":5.60694, "heading":-1.5708, "vx":-0.01743, "vy":-0.84964, "omega":0.0, "ax":0.75163, "ay":0.0003, "alpha":0.0, "fx":[12.78507,12.78507,12.78507,12.78507], "fy":[0.00509,0.00509,0.00509,0.00509]}, + {"t":3.96685, "x":8.52701, "y":5.56676, "heading":-1.5708, "vx":0.01811, "vy":-0.84963, "omega":0.0, "ax":-0.78034, "ay":0.00031, "alpha":0.0, "fx":[-13.27331,-13.27331,-13.27331,-13.27331], "fy":[0.00531,0.00531,0.00531,0.00531]}, + {"t":4.01414, "x":8.52699, "y":5.52659, "heading":-1.5708, "vx":-0.01879, "vy":-0.84961, "omega":0.0, "ax":0.80918, "ay":0.00033, "alpha":0.0, "fx":[13.76396,13.76396,13.76396,13.76396], "fy":[0.00554,0.00554,0.00554,0.00554]}, + {"t":4.06143, "x":8.52701, "y":5.48641, "heading":-1.5708, "vx":0.01947, "vy":-0.8496, "omega":0.0, "ax":-0.83817, "ay":0.00034, "alpha":0.0, "fx":[-14.25699,-14.25699,-14.25699,-14.25699], "fy":[0.00576,0.00576,0.00576,0.00576]}, + {"t":4.10871, "x":8.52699, "y":5.44624, "heading":-1.5708, "vx":-0.02016, "vy":-0.84958, "omega":0.0, "ax":0.86729, "ay":0.00035, "alpha":0.0, "fx":[14.7524,14.7524,14.7524,14.7524], "fy":[0.00599,0.00599,0.00599,0.00599]}, + {"t":4.156, "x":8.52701, "y":5.40606, "heading":-1.5708, "vx":0.02085, "vy":-0.84956, "omega":0.0, "ax":-0.89656, "ay":0.00037, "alpha":0.0, "fx":[-15.25017,-15.25017,-15.25017,-15.25017], "fy":[0.00622,0.00622,0.00622,0.00622]}, + {"t":4.20329, "x":8.52699, "y":5.36589, "heading":-1.5708, "vx":-0.02155, "vy":-0.84955, "omega":0.0, "ax":0.92596, "ay":0.00038, "alpha":0.0, "fx":[15.75028,15.75028,15.75028,15.75028], "fy":[0.00646,0.00646,0.00646,0.00646]}, + {"t":4.25058, "x":8.52701, "y":5.32571, "heading":-1.5708, "vx":0.02224, "vy":-0.84953, "omega":0.0, "ax":-0.9555, "ay":0.00039, "alpha":0.0, "fx":[-16.2527,-16.2527,-16.2527,-16.2527], "fy":[0.0067,0.0067,0.0067,0.0067]}, + {"t":4.29787, "x":8.52699, "y":5.28554, "heading":-1.5708, "vx":-0.02294, "vy":-0.84951, "omega":0.0, "ax":0.98517, "ay":0.00041, "alpha":0.0, "fx":[16.75741,16.75741,16.75741,16.75741], "fy":[0.00693,0.00693,0.00693,0.00693]}, + {"t":4.34516, "x":8.52701, "y":5.24537, "heading":-1.5708, "vx":0.02365, "vy":-0.84949, "omega":0.0, "ax":-1.01497, "ay":0.00042, "alpha":0.0, "fx":[-17.26439,-17.26439,-17.26439,-17.26439], "fy":[0.00718,0.00718,0.00718,0.00718]}, + {"t":4.39244, "x":8.52699, "y":5.2052, "heading":-1.5708, "vx":-0.02435, "vy":-0.84947, "omega":0.0, "ax":1.04491, "ay":0.00044, "alpha":0.0, "fx":[17.77359,17.77359,17.77359,17.77359], "fy":[0.00742,0.00742,0.00742,0.00742]}, + {"t":4.43973, "x":8.52701, "y":5.16503, "heading":-1.5708, "vx":0.02506, "vy":-0.84945, "omega":0.0, "ax":-1.07497, "ay":0.00045, "alpha":0.0, "fx":[-18.28498,-18.28498,-18.28498,-18.28498], "fy":[0.00767,0.00767,0.00767,0.00767]}, + {"t":4.48702, "x":8.52699, "y":5.12486, "heading":-1.5708, "vx":-0.02577, "vy":-0.84943, "omega":0.0, "ax":1.1052, "ay":0.07739, "alpha":0.0, "fx":[18.79922,18.79922,18.79922,18.79922], "fy":[1.31632,1.31632,1.31632,1.31632]}, + {"t":4.53431, "x":8.52701, "y":5.08478, "heading":-1.5708, "vx":0.02649, "vy":-0.84577, "omega":0.0, "ax":-1.12799, "ay":9.35373, "alpha":0.0, "fx":[-19.18681,-19.18681,-19.18681,-19.18681], "fy":[159.10421,159.10421,159.10421,159.10421]}, + {"t":4.5816, "x":8.527, "y":5.05524, "heading":-1.5708, "vx":-0.02685, "vy":-0.40345, "omega":0.0, "ax":-6.77243, "ay":6.55929, "alpha":0.0, "fx":[-115.19706,-115.19706,-115.19706,-115.19706], "fy":[111.57165,111.57165,111.57165,111.57165]}, + {"t":4.62087, "x":8.52072, "y":5.04446, "heading":-1.5708, "vx":-0.29281, "vy":-0.14586, "omega":0.0, "ax":-6.76978, "ay":6.56053, "alpha":0.0, "fx":[-115.15207,-115.15207,-115.15207,-115.15207], "fy":[111.5928,111.5928,111.5928,111.5928]}, + {"t":4.66014, "x":8.504, "y":5.04379, "heading":-1.5708, "vx":-0.55866, "vy":0.11178, "omega":0.0, "ax":-6.76649, "ay":6.56207, "alpha":0.0, "fx":[-115.09603,-115.09603,-115.09603,-115.09603], "fy":[111.61894,111.61894,111.61894,111.61894]}, + {"t":4.69941, "x":8.47685, "y":5.05324, "heading":-1.5708, "vx":-0.82439, "vy":0.36948, "omega":0.0, "ax":-6.76227, "ay":6.56402, "alpha":0.0, "fx":[-115.02427,-115.02427,-115.02427,-115.02427], "fy":[111.65216,111.65216,111.65216,111.65216]}, + {"t":4.73868, "x":8.43926, "y":5.07281, "heading":-1.5708, "vx":-1.08995, "vy":0.62725, "omega":0.0, "ax":-6.75666, "ay":6.56659, "alpha":0.0, "fx":[-114.92894,-114.92894,-114.92894,-114.92894], "fy":[111.69589,111.69589,111.69589,111.69589]}, + {"t":4.77795, "x":8.39125, "y":5.1025, "heading":-1.5708, "vx":-1.35529, "vy":0.88512, "omega":0.0, "ax":-6.74885, "ay":6.57014, "alpha":0.0, "fx":[-114.79595,-114.79595,-114.79595,-114.79595], "fy":[111.75627,111.75627,111.75627,111.75627]}, + {"t":4.81722, "x":8.33282, "y":5.14233, "heading":-1.5708, "vx":-1.62032, "vy":1.14314, "omega":0.0, "ax":-6.73716, "ay":6.57539, "alpha":0.0, "fx":[-114.59711,-114.59711,-114.59711,-114.59711], "fy":[111.84545,111.84545,111.84545,111.84545]}, + {"t":4.85649, "x":8.26399, "y":5.19229, "heading":-1.5708, "vx":-1.88489, "vy":1.40136, "omega":0.0, "ax":-6.71772, "ay":6.58396, "alpha":0.0, "fx":[-114.26646,-114.26646,-114.26646,-114.26646], "fy":[111.99136,111.99136,111.99136,111.99136]}, + {"t":4.89576, "x":8.18479, "y":5.2524, "heading":-1.5708, "vx":-2.1487, "vy":1.65992, "omega":0.0, "ax":-6.67886, "ay":6.6007, "alpha":0.0, "fx":[-113.60546,-113.60546,-113.60546,-113.60546], "fy":[112.27609,112.27609,112.27609,112.27609]}, + {"t":4.93503, "x":8.09526, "y":5.32268, "heading":-1.5708, "vx":-2.41099, "vy":1.91913, "omega":0.0, "ax":-6.56163, "ay":6.64871, "alpha":0.0, "fx":[-111.61139,-111.61139,-111.61139,-111.61139], "fy":[113.0927,113.0927,113.0927,113.0927]}, + {"t":4.97431, "x":7.99552, "y":5.40317, "heading":-1.5708, "vx":-2.66866, "vy":2.18023, "omega":0.0, "ax":2.60262, "ay":5.36399, "alpha":0.0, "fx":[44.26978,44.26978,44.26978,44.26978], "fy":[91.23987,91.23987,91.23987,91.23987]}, + {"t":5.01358, "x":7.89273, "y":5.49292, "heading":-1.5708, "vx":-2.56646, "vy":2.39088, "omega":0.0, "ax":0.89143, "ay":0.94389, "alpha":0.0, "fx":[15.16304,15.16304,15.16304,15.16304], "fy":[16.05536,16.05536,16.05536,16.05536]}, + {"t":5.05285, "x":7.79263, "y":5.58754, "heading":-1.5708, "vx":-2.53145, "vy":2.42795, "omega":0.0, "ax":0.1321, "ay":0.13744, "alpha":0.0, "fx":[2.24699,2.24699,2.24699,2.24699], "fy":[2.33789,2.33789,2.33789,2.33789]}, + {"t":5.09212, "x":7.69332, "y":5.683, "heading":-1.5708, "vx":-2.52626, "vy":2.43334, "omega":0.0, "ax":0.05633, "ay":0.0542, "alpha":0.0, "fx":[0.95819,0.95819,0.95819,0.95819], "fy":[0.92187,0.92187,0.92187,0.92187]}, + {"t":5.13139, "x":7.59415, "y":5.7786, "heading":-1.5708, "vx":-2.52405, "vy":2.43547, "omega":0.0, "ax":5.10643, "ay":-4.68776, "alpha":0.0, "fx":[86.85894,86.85894,86.85894,86.85894], "fy":[-79.73749,-79.73749,-79.73749,-79.73749]}, + {"t":5.17066, "x":7.49897, "y":5.87063, "heading":-1.5708, "vx":-2.32352, "vy":2.25138, "omega":0.0, "ax":6.73549, "ay":-6.48156, "alpha":0.0, "fx":[114.56872,114.56872,114.56872,114.56872], "fy":[-110.24944,-110.24944,-110.24944,-110.24944]}, + {"t":5.20993, "x":7.41292, "y":5.95404, "heading":-1.5708, "vx":-2.05901, "vy":1.99684, "omega":0.0, "ax":6.76484, "ay":-6.51491, "alpha":0.0, "fx":[115.06793,115.06793,115.06793,115.06793], "fy":[-110.81681,-110.81681,-110.81681,-110.81681]}, + {"t":5.2492, "x":7.33727, "y":6.02744, "heading":-1.5708, "vx":-1.79335, "vy":1.741, "omega":0.0, "ax":0.50481, "ay":0.51095, "alpha":0.0, "fx":[8.58667,8.58667,8.58667,8.58667], "fy":[8.69119,8.69119,8.69119,8.69119]}, + {"t":5.2876, "x":7.26879, "y":6.09466, "heading":-1.5708, "vx":-1.77397, "vy":1.76062, "omega":0.0, "ax":0.0437, "ay":0.04399, "alpha":0.0, "fx":[0.74334,0.74334,0.74334,0.74334], "fy":[0.74828,0.74828,0.74828,0.74828]}, + {"t":5.326, "x":7.2007, "y":6.1623, "heading":-1.5708, "vx":-1.77229, "vy":1.76231, "omega":0.0, "ax":0.00498, "ay":0.005, "alpha":0.0, "fx":[0.08464,0.08464,0.08464,0.08464], "fy":[0.08512,0.08512,0.08512,0.08512]}, + {"t":5.36439, "x":7.13265, "y":6.22997, "heading":-1.5708, "vx":-1.7721, "vy":1.7625, "omega":0.0, "ax":0.00183, "ay":0.00184, "alpha":0.0, "fx":[0.03107,0.03107,0.03107,0.03107], "fy":[0.03125,0.03125,0.03125,0.03125]}, + {"t":5.40279, "x":7.06461, "y":6.29765, "heading":-1.5708, "vx":-1.77203, "vy":1.76257, "omega":0.0, "ax":0.00165, "ay":0.00166, "alpha":0.0, "fx":[0.02809,0.02809,0.02809,0.02809], "fy":[0.02826,0.02826,0.02826,0.02826]}, + {"t":5.44119, "x":6.99657, "y":6.36533, "heading":-1.5708, "vx":-1.77196, "vy":1.76263, "omega":0.0, "ax":0.00173, "ay":0.00174, "alpha":0.0, "fx":[0.02947,0.02947,0.02947,0.02947], "fy":[0.02965,0.02965,0.02965,0.02965]}, + {"t":5.47958, "x":6.92853, "y":6.43301, "heading":-1.5708, "vx":-1.7719, "vy":1.7627, "omega":0.0, "ax":0.00184, "ay":0.00185, "alpha":0.0, "fx":[0.03137,0.03137,0.03137,0.03137], "fy":[0.03155,0.03155,0.03155,0.03155]}, + {"t":5.51798, "x":6.8605, "y":6.50069, "heading":-1.5708, "vx":-1.77183, "vy":1.76277, "omega":0.0, "ax":0.00197, "ay":0.00198, "alpha":0.0, "fx":[0.03348,0.03348,0.03348,0.03348], "fy":[0.03367,0.03367,0.03367,0.03367]}, + {"t":5.55638, "x":6.79247, "y":6.56838, "heading":-1.5708, "vx":-1.77175, "vy":1.76285, "omega":0.0, "ax":0.00211, "ay":0.00212, "alpha":0.0, "fx":[0.03582,0.03582,0.03582,0.03582], "fy":[0.03603,0.03603,0.03603,0.03603]}, + {"t":5.59478, "x":6.72444, "y":6.63607, "heading":-1.5708, "vx":-1.77167, "vy":1.76293, "omega":0.0, "ax":0.00226, "ay":0.00227, "alpha":0.0, "fx":[0.03843,0.03843,0.03843,0.03843], "fy":[0.03864,0.03864,0.03864,0.03864]}, + {"t":5.63317, "x":6.65641, "y":6.70376, "heading":-1.5708, "vx":-1.77158, "vy":1.76302, "omega":0.0, "ax":0.00243, "ay":0.00244, "alpha":0.0, "fx":[0.04135,0.04135,0.04135,0.04135], "fy":[0.04157,0.04157,0.04157,0.04157]}, + {"t":5.67157, "x":6.58839, "y":6.77146, "heading":-1.5708, "vx":-1.77149, "vy":1.76311, "omega":0.0, "ax":0.00262, "ay":0.00264, "alpha":0.0, "fx":[0.04464,0.04464,0.04464,0.04464], "fy":[0.04488,0.04488,0.04488,0.04488]}, + {"t":5.70997, "x":6.52037, "y":6.83916, "heading":-1.5708, "vx":-1.77139, "vy":1.76321, "omega":0.0, "ax":0.00285, "ay":0.00286, "alpha":0.0, "fx":[0.0484,0.0484,0.0484,0.0484], "fy":[0.04865,0.04865,0.04865,0.04865]}, + {"t":5.74837, "x":6.45236, "y":6.90687, "heading":-1.5708, "vx":-1.77128, "vy":1.76332, "omega":0.0, "ax":0.0031, "ay":0.00311, "alpha":0.0, "fx":[0.05272,0.05272,0.05272,0.05272], "fy":[0.05298,0.05298,0.05298,0.05298]}, + {"t":5.78676, "x":6.38434, "y":6.97458, "heading":-1.5708, "vx":-1.77116, "vy":1.76344, "omega":0.0, "ax":0.0034, "ay":0.00341, "alpha":0.0, "fx":[0.05775,0.05775,0.05775,0.05775], "fy":[0.05804,0.05804,0.05804,0.05804]}, + {"t":5.82516, "x":6.31634, "y":7.04229, "heading":-1.5708, "vx":-1.77103, "vy":1.76357, "omega":0.0, "ax":0.00375, "ay":0.00376, "alpha":0.0, "fx":[0.06371,0.06371,0.06371,0.06371], "fy":[0.06401,0.06401,0.06401,0.06401]}, + {"t":5.86356, "x":6.24834, "y":7.11001, "heading":-1.5708, "vx":-1.77089, "vy":1.76372, "omega":0.0, "ax":0.00416, "ay":0.00418, "alpha":0.0, "fx":[0.07079,0.07079,0.07079,0.07079], "fy":[0.07111,0.07111,0.07111,0.07111]}, + {"t":5.90196, "x":6.18034, "y":7.17774, "heading":-1.5708, "vx":-1.77073, "vy":1.76388, "omega":0.0, "ax":0.00461, "ay":0.00463, "alpha":0.0, "fx":[0.07841,0.07841,0.07841,0.07841], "fy":[0.07875,0.07875,0.07875,0.07875]}, + {"t":5.94035, "x":6.11236, "y":7.24547, "heading":-1.5708, "vx":-1.77055, "vy":1.76406, "omega":0.0, "ax":0.00439, "ay":0.00441, "alpha":0.0, "fx":[0.07468,0.07468,0.07468,0.07468], "fy":[0.075,0.075,0.075,0.075]}, + {"t":5.97875, "x":6.04438, "y":7.31321, "heading":-1.5708, "vx":-1.77038, "vy":1.76422, "omega":0.0, "ax":-0.00533, "ay":-0.00534, "alpha":0.0, "fx":[-0.09065,-0.09065,-0.09065,-0.09065], "fy":[-0.09091,-0.09091,-0.09091,-0.09091]}, + {"t":6.01715, "x":5.97639, "y":7.38094, "heading":-1.5708, "vx":-1.77059, "vy":1.76402, "omega":0.0, "ax":-0.13114, "ay":-0.132, "alpha":0.0, "fx":[-2.23057,-2.23057,-2.23057,-2.23057], "fy":[-2.24524,-2.24524,-2.24524,-2.24524]}, + {"t":6.05555, "x":5.90831, "y":7.44858, "heading":-1.5708, "vx":-1.77562, "vy":1.75895, "omega":0.0, "ax":-1.53473, "ay":-1.60311, "alpha":0.0, "fx":[-26.10537,-26.10537,-26.10537,-26.10537], "fy":[-27.26846,-27.26846,-27.26846,-27.26846]}, + {"t":6.09394, "x":5.839, "y":7.51494, "heading":-1.5708, "vx":-1.83455, "vy":1.6974, "omega":0.0, "ax":-5.02414, "ay":-6.14313, "alpha":0.0, "fx":[-85.45921,-85.45921,-85.45921,-85.45921], "fy":[-104.4928,-104.4928,-104.4928,-104.4928]}, + {"t":6.13234, "x":5.76485, "y":7.57558, "heading":-1.5708, "vx":-2.02747, "vy":1.46152, "omega":0.0, "ax":-4.79409, "ay":-7.74193, "alpha":0.0, "fx":[-81.54604,-81.54604,-81.54604,-81.54604], "fy":[-131.688,-131.688,-131.688,-131.688]}, + {"t":6.17074, "x":5.68347, "y":7.626, "heading":-1.5708, "vx":-2.21155, "vy":1.16424, "omega":0.0, "ax":-3.7314, "ay":-8.52102, "alpha":0.0, "fx":[-63.47008,-63.47008,-63.47008,-63.47008], "fy":[-144.94009,-144.94009,-144.94009,-144.94009]}, + {"t":6.20914, "x":5.5958, "y":7.66442, "heading":-1.5708, "vx":-2.35482, "vy":0.83706, "omega":0.0, "ax":-2.34937, "ay":-9.06533, "alpha":0.0, "fx":[-39.96204,-39.96204,-39.96204,-39.96204], "fy":[-154.19871,-154.19871,-154.19871,-154.19871]}, + {"t":6.24753, "x":5.50365, "y":7.68988, "heading":-1.5708, "vx":-2.44503, "vy":0.48897, "omega":0.0, "ax":-0.0402, "ay":-0.64702, "alpha":0.0, "fx":[-0.68377,-0.68377,-0.68377,-0.68377], "fy":[-11.00555,-11.00555,-11.00555,-11.00555]}, + {"t":6.24757, "x":5.50357, "y":7.68989, "heading":-1.5708, "vx":-2.44503, "vy":0.48895, "omega":0.0, "ax":-0.09275, "ay":-0.63637, "alpha":0.0, "fx":[-1.57767,-1.57767,-1.57767,-1.57767], "fy":[-10.8245,-10.8245,-10.8245,-10.8245]}, + {"t":6.2476, "x":5.5035, "y":7.68991, "heading":-1.5708, "vx":-2.44504, "vy":0.48893, "omega":0.0, "ax":-0.14529, "ay":-0.62569, "alpha":0.0, "fx":[-2.4714,-2.4714,-2.4714,-2.4714], "fy":[-10.64278,-10.64278,-10.64278,-10.64278]}, + {"t":6.24763, "x":5.50342, "y":7.68992, "heading":-1.5708, "vx":-2.44504, "vy":0.48891, "omega":0.0, "ax":-0.19783, "ay":-0.61497, "alpha":0.0, "fx":[-3.36502,-3.36502,-3.36502,-3.36502], "fy":[-10.46039,-10.46039,-10.46039,-10.46039]}, + {"t":6.24766, "x":5.50334, "y":7.68994, "heading":-1.5708, "vx":-2.44505, "vy":0.4889, "omega":0.0, "ax":-0.25036, "ay":-0.6042, "alpha":0.0, "fx":[-4.25858,-4.25858,-4.25858,-4.25858], "fy":[-10.27732,-10.27732,-10.27732,-10.27732]}, + {"t":6.24769, "x":5.50327, "y":7.68995, "heading":-1.5708, "vx":-2.44505, "vy":0.48888, "omega":0.0, "ax":-0.30289, "ay":-0.5934, "alpha":0.0, "fx":[-5.15213,-5.15213,-5.15213,-5.15213], "fy":[-10.09355,-10.09355,-10.09355,-10.09355]}, + {"t":6.24772, "x":5.50319, "y":7.68997, "heading":-1.5708, "vx":-2.44506, "vy":0.48886, "omega":0.0, "ax":-0.35543, "ay":-0.58255, "alpha":0.0, "fx":[-6.04573,-6.04573,-6.04573,-6.04573], "fy":[-9.90908,-9.90908,-9.90908,-9.90908]}, + {"t":6.24775, "x":5.50312, "y":7.68998, "heading":-1.5708, "vx":-2.44508, "vy":0.48884, "omega":0.0, "ax":-0.40797, "ay":-0.57167, "alpha":0.0, "fx":[-6.93944,-6.93944,-6.93944,-6.93944], "fy":[-9.7239,-9.7239,-9.7239,-9.7239]}, + {"t":6.24778, "x":5.50304, "y":7.69, "heading":-1.5708, "vx":-2.44509, "vy":0.48882, "omega":0.0, "ax":-0.46052, "ay":-0.56074, "alpha":0.0, "fx":[-7.8333,-7.8333,-7.8333,-7.8333], "fy":[-9.53799,-9.53799,-9.53799,-9.53799]}, + {"t":6.24782, "x":5.50296, "y":7.69001, "heading":-1.5708, "vx":-2.4451, "vy":0.4888, "omega":0.0, "ax":-0.51308, "ay":-0.54976, "alpha":0.0, "fx":[-8.72737,-8.72737,-8.72737,-8.72737], "fy":[-9.35134,-9.35134,-9.35134,-9.35134]}, + {"t":6.24785, "x":5.50289, "y":7.69003, "heading":-1.5708, "vx":-2.44512, "vy":0.48879, "omega":0.0, "ax":-0.56566, "ay":-0.53875, "alpha":0.0, "fx":[-9.62171,-9.62171,-9.62171,-9.62171], "fy":[-9.16395,-9.16395,-9.16395,-9.16395]}, + {"t":6.24788, "x":5.50281, "y":7.69004, "heading":-1.5708, "vx":-2.44514, "vy":0.48877, "omega":0.0, "ax":-0.61826, "ay":-0.52769, "alpha":0.0, "fx":[-10.51637,-10.51637,-10.51637,-10.51637], "fy":[-8.97579,-8.97579,-8.97579,-8.97579]}, + {"t":6.24791, "x":5.50273, "y":7.69006, "heading":-1.5708, "vx":-2.44516, "vy":0.48875, "omega":0.0, "ax":-0.67088, "ay":-0.51658, "alpha":0.0, "fx":[-11.41142,-11.41142,-11.41142,-11.41142], "fy":[-8.78686,-8.78686,-8.78686,-8.78686]}, + {"t":6.24794, "x":5.50266, "y":7.69008, "heading":-1.5708, "vx":-2.44518, "vy":0.48874, "omega":0.0, "ax":-0.72352, "ay":-0.50543, "alpha":0.0, "fx":[-12.30691,-12.30691,-12.30691,-12.30691], "fy":[-8.59714,-8.59714,-8.59714,-8.59714]}, + {"t":6.24797, "x":5.50258, "y":7.69009, "heading":-1.5708, "vx":-2.4452, "vy":0.48872, "omega":0.0, "ax":-0.7762, "ay":-0.49422, "alpha":0.0, "fx":[-13.20291,-13.20291,-13.20291,-13.20291], "fy":[-8.40662,-8.40662,-8.40662,-8.40662]}, + {"t":6.248, "x":5.5025, "y":7.69011, "heading":-1.5708, "vx":-2.44522, "vy":0.48871, "omega":0.0, "ax":-0.82891, "ay":-0.48298, "alpha":0.0, "fx":[-14.09947,-14.09947,-14.09947,-14.09947], "fy":[-8.21528,-8.21528,-8.21528,-8.21528]}, + {"t":6.24803, "x":5.50243, "y":7.69012, "heading":-1.5708, "vx":-2.44525, "vy":0.48869, "omega":0.0, "ax":-0.88165, "ay":-0.47168, "alpha":0.0, "fx":[-14.99667,-14.99667,-14.99667,-14.99667], "fy":[-8.02311,-8.02311,-8.02311,-8.02311]}, + {"t":6.24807, "x":5.50235, "y":7.69014, "heading":-1.5708, "vx":-2.44528, "vy":0.48868, "omega":0.0, "ax":-0.93444, "ay":-0.46033, "alpha":0.0, "fx":[-15.89457,-15.89457,-15.89457,-15.89457], "fy":[-7.8301,-7.8301,-7.8301,-7.8301]}, + {"t":6.2481, "x":5.50227, "y":7.69015, "heading":-1.5708, "vx":-2.44531, "vy":0.48866, "omega":0.0, "ax":-0.98727, "ay":-0.44893, "alpha":0.0, "fx":[-16.79324,-16.79324,-16.79324,-16.79324], "fy":[-7.63623,-7.63623,-7.63623,-7.63623]}, + {"t":6.24813, "x":5.5022, "y":7.69017, "heading":-1.5708, "vx":-2.44534, "vy":0.48865, "omega":0.0, "ax":-1.04016, "ay":-0.43748, "alpha":0.0, "fx":[-17.69275,-17.69275,-17.69275,-17.69275], "fy":[-7.44147,-7.44147,-7.44147,-7.44147]}, + {"t":6.24816, "x":5.50212, "y":7.69018, "heading":-1.5708, "vx":-2.44537, "vy":0.48863, "omega":0.0, "ax":-8.71509, "ay":-3.55511, "alpha":0.0, "fx":[-148.24124,-148.24124,-148.24124,-148.24124], "fy":[-60.47147,-60.47147,-60.47147,-60.47147]}, + {"t":6.31333, "x":5.32426, "y":7.71448, "heading":-1.5708, "vx":-3.0133, "vy":0.25696, "omega":0.0, "ax":-7.57766, "ay":-5.42153, "alpha":0.0, "fx":[-128.89383,-128.89383,-128.89383,-128.89383], "fy":[-92.21859,-92.21859,-92.21859,-92.21859]}, + {"t":6.37849, "x":5.11181, "y":7.71971, "heading":-1.5708, "vx":-3.5071, "vy":-0.09634, "omega":0.0, "ax":-0.01349, "ay":0.15497, "alpha":0.0, "fx":[-0.2295,-0.2295,-0.2295,-0.2295], "fy":[2.63595,2.63595,2.63595,2.63595]}, + {"t":6.44366, "x":4.88324, "y":7.71376, "heading":-1.5708, "vx":-3.50798, "vy":-0.08624, "omega":0.0, "ax":-0.01108, "ay":0.57674, "alpha":0.0, "fx":[-0.18849,-0.18849,-0.18849,-0.18849], "fy":[9.81011,9.81011,9.81011,9.81011]}, + {"t":6.50882, "x":4.65461, "y":7.70937, "heading":-1.5708, "vx":-3.5087, "vy":-0.04866, "omega":0.0, "ax":-0.00415, "ay":0.41461, "alpha":0.0, "fx":[-0.07061,-0.07061,-0.07061,-0.07061], "fy":[7.05233,7.05233,7.05233,7.05233]}, + {"t":6.57399, "x":4.42595, "y":7.70708, "heading":-1.5708, "vx":-3.50897, "vy":-0.02164, "omega":0.0, "ax":-0.00101, "ay":0.36355, "alpha":0.0, "fx":[-0.01726,-0.01726,-0.01726,-0.01726], "fy":[6.18388,6.18388,6.18388,6.18388]}, + {"t":6.63915, "x":4.19729, "y":7.70644, "heading":-1.5708, "vx":-3.50904, "vy":0.00205, "omega":0.0, "ax":0.00147, "ay":0.36726, "alpha":0.0, "fx":[0.02493,0.02493,0.02493,0.02493], "fy":[6.24696,6.24696,6.24696,6.24696]}, + {"t":6.70432, "x":3.96862, "y":7.70735, "heading":-1.5708, "vx":-3.50895, "vy":0.02599, "omega":0.0, "ax":0.0047, "ay":0.41671, "alpha":0.0, "fx":[0.07987,0.07987,0.07987,0.07987], "fy":[7.08806,7.08806,7.08806,7.08806]}, + {"t":6.76949, "x":3.73997, "y":7.70993, "heading":-1.5708, "vx":-3.50864, "vy":0.05314, "omega":0.0, "ax":0.05279, "ay":0.22137, "alpha":0.0, "fx":[0.89788,0.89788,0.89788,0.89788], "fy":[3.76538,3.76538,3.76538,3.76538]}, + {"t":6.83465, "x":3.51143, "y":7.71386, "heading":-1.5708, "vx":-3.5052, "vy":0.06757, "omega":0.0, "ax":9.37759, "ay":-0.29595, "alpha":0.0, "fx":[159.51011,159.51011,159.51011,159.51011], "fy":[-5.034,-5.034,-5.034,-5.034]}, + {"t":6.89982, "x":3.30292, "y":7.71764, "heading":-1.5708, "vx":-2.8941, "vy":0.04828, "omega":0.0, "ax":9.40767, "ay":-0.41251, "alpha":0.0, "fx":[160.02184,160.02184,160.02184,160.02184], "fy":[-7.01664,-7.01664,-7.01664,-7.01664]}, + {"t":6.96498, "x":3.1343, "y":7.71991, "heading":-1.5708, "vx":-2.28104, "vy":0.0214, "omega":0.0, "ax":8.83902, "ay":-3.27276, "alpha":0.0, "fx":[150.34919,150.34919,150.34919,150.34919], "fy":[-55.66878,-55.66878,-55.66878,-55.66878]}, + {"t":7.03015, "x":3.00443, "y":7.71435, "heading":-1.5708, "vx":-1.70504, "vy":-0.19187, "omega":0.0, "ax":7.55853, "ay":-5.63862, "alpha":0.0, "fx":[128.56849,128.56849,128.56849,128.56849], "fy":[-95.91139,-95.91139,-95.91139,-95.91139]}, + {"t":7.09532, "x":2.90936, "y":7.68988, "heading":-1.5708, "vx":-1.21248, "vy":-0.55932, "omega":0.0, "ax":6.33632, "ay":-2.70113, "alpha":22.55991, "fx":[159.53555,139.18206,25.31764,107.08079], "fy":[15.94816,78.02832,-158.34109,-119.41694]}, + {"t":7.1232, "x":2.87802, "y":7.67323, "heading":-1.56203, "vx":-1.03582, "vy":-0.63463, "omega":0.62899, "ax":6.29609, "ay":-2.57132, "alpha":22.98075, "fx":[159.2915,135.53878,25.70205,107.84662], "fy":[18.04486,83.97468,-158.26138,-118.7077]}, + {"t":7.15108, "x":2.85159, "y":7.65454, "heading":-1.53556, "vx":-0.86028, "vy":-0.70632, "omega":1.26971, "ax":6.31894, "ay":-2.4508, "alpha":22.98532, "fx":[158.88818,133.37904,27.92371,109.74261], "fy":[21.09318,86.95496,-157.86346,-116.93405]}, + {"t":7.17896, "x":2.83006, "y":7.63389, "heading":-1.49123, "vx":-0.6841, "vy":-0.77465, "omega":1.91055, "ax":6.42421, "ay":-2.36218, "alpha":22.43105, "fx":[158.28084,133.93881,32.11395,112.76235], "fy":[24.99812,85.31432,-157.03852,-113.99408]}, + {"t":7.20684, "x":2.81348, "y":7.61138, "heading":-1.42924, "vx":-0.50499, "vy":-0.84051, "omega":2.53595, "ax":6.66041, "ay":-2.40511, "alpha":20.85291, "fx":[157.44995,140.49139,38.38828,116.83678], "fy":[29.47937,72.24003,-155.58935,-109.77099]}, + {"t":7.23472, "x":2.80199, "y":7.58701, "heading":-1.35043, "vx":-0.31929, "vy":-0.90756, "omega":3.11734, "ax":7.04897, "ay":-2.92416, "alpha":17.22008, "fx":[156.45343,154.48634,46.84069,121.8231], "fy":[33.9794,24.41651,-153.20816,-104.14456]}, + {"t":7.2626, "x":2.79583, "y":7.56057, "heading":-1.25682, "vx":-0.12276, "vy":-0.98909, "omega":3.59745, "ax":7.04152, "ay":-4.10661, "alpha":13.24503, "fx":[155.49958,138.55247,57.54502,127.4997], "fy":[37.54173,-70.50343,-149.44154,-97.00571]}, + {"t":7.29048, "x":2.79515, "y":7.5314, "heading":-1.15138, "vx":0.07356, "vy":-1.10359, "omega":3.96673, "ax":6.86332, "ay":-4.51307, "alpha":12.50683, "fx":[155.08887,107.26131,70.96266,133.65936], "fy":[38.27877,-113.79217,-143.42306,-88.12797]}, + {"t":7.31836, "x":2.79986, "y":7.49887, "heading":-1.03592, "vx":0.26492, "vy":-1.22942, "omega":4.31543, "ax":7.34727, "ay":-4.19828, "alpha":10.99441, "fx":[156.19822,113.94448,89.40602,140.35087], "fy":[31.43243,-108.09533,-132.41598,-76.56709]}, + {"t":7.34624, "x":2.81011, "y":7.46296, "heading":-0.91133, "vx":0.46976, "vy":-1.34647, "omega":4.62197, "ax":8.41978, "ay":-3.25876, "alpha":6.40462, "fx":[158.47063,142.69376,123.19727,148.51067], "fy":[2.96315,-66.70867,-100.576,-57.40077]}, + {"t":7.37412, "x":2.82648, "y":7.42416, "heading":-0.77998, "vx":0.70451, "vy":-1.43732, "omega":4.80053, "ax":8.69976, "ay":0.54793, "alpha":-8.97035, "fx":[145.65194,157.60736,138.34674,150.3159], "fy":[-58.89047,-7.16998,73.40744,29.93322]}, + {"t":7.40201, "x":2.8495, "y":7.3843, "heading":-0.64962, "vx":0.94707, "vy":-1.42205, "omega":4.55043, "ax":8.10809, "ay":1.01758, "alpha":-10.29462, "fx":[141.7565,154.09465,120.63547,135.17886], "fy":[-56.72761,3.68272,90.4653,31.8147]}, + {"t":7.42989, "x":2.87906, "y":7.34504, "heading":-0.52675, "vx":1.17313, "vy":-1.39368, "omega":4.26341, "ax":-3.51245, "ay":4.76168, "alpha":12.89508, "fx":[-72.85332,-115.19064,-64.68461,13.74558], "fy":[113.68748,64.86427,32.35217,113.07552]}, + {"t":7.45777, "x":2.9104, "y":7.30804, "heading":-0.40287, "vx":1.0752, "vy":-1.26092, "omega":4.62293, "ax":-7.17873, "ay":2.48602, "alpha":13.83101, "fx":[-128.07914,-154.33838,-133.65581,-72.35942], "fy":[87.75207,17.88143,-62.09529,125.60808]}, + {"t":7.48565, "x":2.93759, "y":7.27385, "heading":-0.26861, "vx":0.87505, "vy":-1.1916, "omega":5.00855, "ax":-6.60415, "ay":4.95464, "alpha":-11.83532, "fx":[-150.83224,-26.63979,-118.00473,-153.86197], "fy":[33.93611,155.7217,106.94589,40.50417]}, + {"t":7.51353, "x":2.95942, "y":7.24255, "heading":-0.13356, "vx":0.69092, "vy":-1.05347, "omega":4.67857, "ax":-6.00503, "ay":5.13641, "alpha":-13.88307, "fx":[-135.99441,6.40125,-121.03925,-157.94312], "fy":[61.53736,159.26554,104.49306,24.17947]}, + {"t":7.54141, "x":2.97634, "y":7.21518, "heading":-0.00852, "vx":0.5235, "vy":-0.91026, "omega":4.2915, "ax":-3.06107, "ay":5.97353, "alpha":-22.30988, "fx":[69.6028,8.14536,-126.44553,-159.57452], "fy":[136.81687,159.63727,98.2222,11.75547]}, + {"t":7.56929, "x":2.98975, "y":7.19212, "heading":0.10246, "vx":0.43815, "vy":-0.74371, "omega":3.66949, "ax":-2.81385, "ay":5.55555, "alpha":-25.48411, "fx":[96.66893,3.43853,-131.44912,-160.10939], "fy":[124.83627,160.01458,91.59562,1.54702]}, + {"t":7.59717, "x":3.00087, "y":7.17354, "heading":0.19486, "vx":0.3597, "vy":-0.58882, "omega":2.95897, "ax":-2.97768, "ay":5.38834, "alpha":-26.30234, "fx":[95.29875,-2.39208,-135.4548,-160.05007], "fy":[127.31299,160.1476,85.67279,-6.51694]}, + {"t":7.62505, "x":3.00974, "y":7.15922, "heading":0.26714, "vx":0.27668, "vy":-0.43859, "omega":2.22564, "ax":-3.16477, "ay":5.28863, "alpha":-26.59571, "fx":[90.51882,-7.70007,-138.40327,-159.74313], "fy":[131.32968,160.05055,80.90149,-12.44949]}, + {"t":7.65293, "x":3.01623, "y":7.14905, "heading":0.31886, "vx":0.18844, "vy":-0.29114, "omega":1.48414, "ax":-3.3219, "ay":5.23045, "alpha":-26.6584, "fx":[85.66378,-11.87973,-140.37181,-159.43055], "fy":[134.834,159.84135,77.49407,-16.29542]}, + {"t":7.68081, "x":3.02019, "y":7.14297, "heading":0.34987, "vx":0.09583, "vy":-0.14531, "omega":0.74088, "ax":-3.43707, "ay":5.21179, "alpha":-26.57319, "fx":[81.54987,-14.69272,-141.44926,-159.26189], "fy":[137.52104,159.63985,75.55484,-18.11163]}, + {"t":7.70869, "x":3.02153, "y":7.14094, "heading":0.3602, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0,0.0,0.0,0.0]}], "splits":[0] }, "events":[] diff --git a/src/main/deploy/v1_doomspiral/choreo/LEFT_TRENCH_ANTI_BUCKS_CROSSES.traj b/src/main/deploy/v1_doomspiral/choreo/LEFT_TRENCH_ANTI_BUCKS_CROSSES.traj index 6dc867bf..363af9b4 100644 --- a/src/main/deploy/v1_doomspiral/choreo/LEFT_TRENCH_ANTI_BUCKS_CROSSES.traj +++ b/src/main/deploy/v1_doomspiral/choreo/LEFT_TRENCH_ANTI_BUCKS_CROSSES.traj @@ -5,13 +5,13 @@ "waypoints":[ {"x":4.441253185272217, "y":7.693458557128906, "heading":-1.5707972442597578, "intervals":28, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, {"x":7.900742053985596, "y":7.263101100921631, "heading":0.0, "intervals":12, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":8.526999855041504, "y":6.732084846496582, "heading":-1.5707963267948966, "intervals":52, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, + {"x":8.526999855041504, "y":6.732084846496582, "heading":-1.5707963267948966, "intervals":74, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, {"x":8.526999855041504, "y":3.693901538848877, "heading":-1.5707963267948966, "intervals":26, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, {"x":6.7872819900512695, "y":6.392120838165283, "heading":0.0, "intervals":23, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, {"x":5.503650665283203, "y":7.689876556396484, "heading":-1.5707963267948966, "intervals":20, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, {"x":3.9331870079040527, "y":7.706951141357422, "heading":0.0, "intervals":14, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":2.9093637466430664, "y":7.689876556396484, "heading":-1.5707963267948966, "intervals":27, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":2.818082809448242, "y":6.5, "heading":0.65, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], + {"x":2.9093637466430664, "y":7.689876556396484, "heading":-1.5707963267948966, "intervals":22, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, + {"x":3.0215272903442383, "y":7.140939712524414, "heading":0.3602010510265898, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], "constraints":[ {"from":"first", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, {"from":"last", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, @@ -20,7 +20,7 @@ {"from":5, "to":7, "data":{"type":"KeepInLane", "props":{"tolerance":0.03550910949707031}}, "enabled":true}, {"from":5, "to":7, "data":{"type":"MaxAngularVelocity", "props":{"max":0.0}}, "enabled":true}, {"from":2, "to":3, "data":{"type":"MaxAngularVelocity", "props":{"max":0.0}}, "enabled":true}, - {"from":2, "to":3, "data":{"type":"MaxVelocity", "props":{"max":1.25}}, "enabled":true}, + {"from":2, "to":3, "data":{"type":"MaxVelocity", "props":{"max":0.85}}, "enabled":true}, {"from":4, "to":6, "data":{"type":"MaxVelocity", "props":{"max":2.0}}, "enabled":true}], "targetDt":0.05 }, @@ -28,13 +28,13 @@ "waypoints":[ {"x":{"exp":"4.441253185272217 m", "val":4.441253185272217}, "y":{"exp":"7.693458557128906 m", "val":7.693458557128906}, "heading":{"exp":"-1.5707972442597578 rad", "val":-1.5707972442597578}, "intervals":28, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, {"x":{"exp":"7.900742053985596 m", "val":7.900742053985596}, "y":{"exp":"7.263101100921631 m", "val":7.263101100921631}, "heading":{"exp":"0 deg", "val":0.0}, "intervals":12, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":{"exp":"8.526999855041504 m", "val":8.526999855041504}, "y":{"exp":"6.732084846496582 m", "val":6.732084846496582}, "heading":{"exp":"-1.5707963267948966 rad", "val":-1.5707963267948966}, "intervals":52, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, + {"x":{"exp":"8.526999855041504 m", "val":8.526999855041504}, "y":{"exp":"6.732084846496582 m", "val":6.732084846496582}, "heading":{"exp":"-1.5707963267948966 rad", "val":-1.5707963267948966}, "intervals":74, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, {"x":{"exp":"8.526999855041504 m", "val":8.526999855041504}, "y":{"exp":"3.693901538848877 m", "val":3.693901538848877}, "heading":{"exp":"-1.5707963267948966 rad", "val":-1.5707963267948966}, "intervals":26, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, {"x":{"exp":"6.7872819900512695 m", "val":6.7872819900512695}, "y":{"exp":"6.392120838165283 m", "val":6.392120838165283}, "heading":{"exp":"0 deg", "val":0.0}, "intervals":23, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, {"x":{"exp":"5.503650665283203 m", "val":5.503650665283203}, "y":{"exp":"7.689876556396484 m", "val":7.689876556396484}, "heading":{"exp":"-90 deg", "val":-1.5707963267948966}, "intervals":20, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, {"x":{"exp":"3.9331870079040527 m", "val":3.9331870079040527}, "y":{"exp":"7.706951141357422 m", "val":7.706951141357422}, "heading":{"exp":"0 deg", "val":0.0}, "intervals":14, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":{"exp":"2.9093637466430664 m", "val":2.9093637466430664}, "y":{"exp":"7.689876556396484 m", "val":7.689876556396484}, "heading":{"exp":"-90 deg", "val":-1.5707963267948966}, "intervals":27, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":{"exp":"2.818082809448242 m", "val":2.818082809448242}, "y":{"exp":"6.5 m", "val":6.5}, "heading":{"exp":"0.65 rad", "val":0.65}, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], + {"x":{"exp":"2.9093637466430664 m", "val":2.9093637466430664}, "y":{"exp":"7.689876556396484 m", "val":7.689876556396484}, "heading":{"exp":"-90 deg", "val":-1.5707963267948966}, "intervals":22, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, + {"x":{"exp":"3.0215272903442383 m", "val":3.0215272903442383}, "y":{"exp":"7.140939712524414 m", "val":7.140939712524414}, "heading":{"exp":"20.638 deg", "val":0.3602010510265898}, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], "constraints":[ {"from":"first", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, {"from":"last", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, @@ -43,7 +43,7 @@ {"from":5, "to":7, "data":{"type":"KeepInLane", "props":{"tolerance":{"exp":"35.50910949707031 mm", "val":0.03550910949707031}}}, "enabled":true}, {"from":5, "to":7, "data":{"type":"MaxAngularVelocity", "props":{"max":{"exp":"0 rad / s", "val":0.0}}}, "enabled":true}, {"from":2, "to":3, "data":{"type":"MaxAngularVelocity", "props":{"max":{"exp":"0 rad / s", "val":0.0}}}, "enabled":true}, - {"from":2, "to":3, "data":{"type":"MaxVelocity", "props":{"max":{"exp":"1.25 m / s", "val":1.25}}}, "enabled":true}, + {"from":2, "to":3, "data":{"type":"MaxVelocity", "props":{"max":{"exp":"INTAKE_VELOCITY", "val":0.55}}}, "enabled":true}, {"from":4, "to":6, "data":{"type":"MaxVelocity", "props":{"max":{"exp":"2 m / s", "val":2.0}}}, "enabled":true}], "targetDt":{ "exp":"0.05 s", @@ -75,211 +75,228 @@ "differentialTrackWidth":0.5588 }, "sampleType":"Swerve", - "waypoints":[0.0,1.18146,1.54489,4.01737,5.18039,6.09682,6.88434,7.25521,8.06557], + "waypoints":[0.0,1.1822,1.5566,5.18346,6.31525,7.23162,8.01914,8.43773,9.04831], "samples":[ - {"t":0.0, "x":4.44125, "y":7.69346, "heading":-1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":9.36217, "ay":-1.10967, "alpha":0.00001, "fx":[159.24789,159.24789,159.24788,159.24788], "fy":[-18.87508,-18.87509,-18.87519,-18.87518]}, - {"t":0.04219, "x":4.44959, "y":7.69247, "heading":-1.5708, "vx":0.39504, "vy":-0.04682, "omega":0.0, "ax":9.36191, "ay":-1.10013, "alpha":0.00001, "fx":[159.24338,159.24338,159.24337,159.24337], "fy":[-18.71289,-18.7129,-18.713,-18.71299]}, - {"t":0.08439, "x":4.47459, "y":7.68952, "heading":-1.5708, "vx":0.79006, "vy":-0.09324, "omega":0.0, "ax":9.36108, "ay":-1.09139, "alpha":0.00001, "fx":[159.22932,159.22931,159.2293,159.2293], "fy":[-18.56413,-18.56414,-18.56423,-18.56423]}, - {"t":0.12658, "x":4.51626, "y":7.68461, "heading":-1.5708, "vx":1.18505, "vy":-0.13929, "omega":0.0, "ax":9.35943, "ay":-1.08338, "alpha":0.00001, "fx":[159.20116,159.20116,159.20115,159.20115], "fy":[-18.42798,-18.42799,-18.42808,-18.42807]}, - {"t":0.16878, "x":4.5746, "y":7.67777, "heading":-1.5708, "vx":1.57997, "vy":-0.18501, "omega":0.0, "ax":9.35642, "ay":-1.07594, "alpha":0.00001, "fx":[159.15004,159.15004,159.15003,159.15003], "fy":[-18.30135,-18.30136,-18.30145,-18.30144]}, - {"t":0.21097, "x":4.64959, "y":7.669, "heading":-1.5708, "vx":1.97477, "vy":-0.23041, "omega":0.0, "ax":9.35085, "ay":-1.06868, "alpha":0.00001, "fx":[159.05523,159.05523,159.05522,159.05522], "fy":[-18.17791,-18.17792,-18.178,-18.17799]}, - {"t":0.25317, "x":4.74124, "y":7.65833, "heading":-1.5708, "vx":2.36933, "vy":-0.2755, "omega":0.0, "ax":9.33908, "ay":-1.0607, "alpha":0.00001, "fx":[158.85507,158.85507,158.85506,158.85506], "fy":[-18.04211,-18.04212,-18.04219,-18.04219]}, - {"t":0.29536, "x":4.84953, "y":7.64576, "heading":-1.5708, "vx":2.76339, "vy":-0.32026, "omega":0.0, "ax":9.30358, "ay":-1.048, "alpha":0.00001, "fx":[158.25125,158.25125,158.25124,158.25124], "fy":[-17.82619,-17.82619,-17.82625,-17.82624]}, - {"t":0.33756, "x":4.97441, "y":7.63132, "heading":-1.5708, "vx":3.15595, "vy":-0.36448, "omega":0.0, "ax":7.80837, "ay":-0.72701, "alpha":-0.00007, "fx":[132.81816,132.81824,132.81829,132.81821], "fy":[-12.36662,-12.36659,-12.36594,-12.36598]}, - {"t":0.37975, "x":5.11453, "y":7.61529, "heading":-1.5708, "vx":3.48543, "vy":-0.39515, "omega":0.0, "ax":0.01616, "ay":0.11124, "alpha":0.0, "fx":[0.27489,0.27489,0.27489,0.27489], "fy":[1.89216,1.89216,1.89216,1.89216]}, - {"t":0.42195, "x":5.26161, "y":7.59871, "heading":-1.5708, "vx":3.48611, "vy":-0.39046, "omega":0.0, "ax":0.00679, "ay":0.06087, "alpha":0.0, "fx":[0.11546,0.11546,0.11546,0.11546], "fy":[1.03532,1.03532,1.03532,1.03532]}, - {"t":0.46414, "x":5.40871, "y":7.58229, "heading":-1.5708, "vx":3.4864, "vy":-0.38789, "omega":0.0, "ax":0.00559, "ay":0.05048, "alpha":0.0, "fx":[0.09516,0.09516,0.09516,0.09516], "fy":[0.85867,0.85867,0.85867,0.85867]}, - {"t":0.50634, "x":5.55583, "y":7.56597, "heading":-1.5708, "vx":3.48663, "vy":-0.38576, "omega":0.0, "ax":0.00503, "ay":0.04561, "alpha":0.0, "fx":[0.08552,0.08552,0.08552,0.08552], "fy":[0.77582,0.77582,0.77582,0.77582]}, - {"t":0.54853, "x":5.70295, "y":7.54973, "heading":-1.5708, "vx":3.48684, "vy":-0.38384, "omega":0.0, "ax":0.00461, "ay":0.04202, "alpha":0.0, "fx":[0.0784,0.0784,0.0784,0.0784], "fy":[0.71473,0.71473,0.71473,0.71473]}, - {"t":0.59073, "x":5.85008, "y":7.53358, "heading":-1.5708, "vx":3.48704, "vy":-0.38206, "omega":0.0, "ax":0.00427, "ay":0.03906, "alpha":0.0, "fx":[0.07255,0.07255,0.07255,0.07255], "fy":[0.66438,0.66438,0.66438,0.66438]}, - {"t":0.63292, "x":5.99722, "y":7.51749, "heading":-1.5708, "vx":3.48722, "vy":-0.38041, "omega":0.0, "ax":0.00397, "ay":0.03654, "alpha":0.0, "fx":[0.06759,0.06759,0.06759,0.06759], "fy":[0.6216,0.6216,0.6216,0.6216]}, - {"t":0.67512, "x":6.14437, "y":7.50147, "heading":-1.5708, "vx":3.48739, "vy":-0.37887, "omega":0.0, "ax":0.00372, "ay":0.03437, "alpha":0.0, "fx":[0.06332,0.06332,0.06332,0.06332], "fy":[0.58467,0.58467,0.58467,0.58467]}, - {"t":0.71731, "x":6.29152, "y":7.48552, "heading":-1.5708, "vx":3.48754, "vy":-0.37742, "omega":0.0, "ax":0.0035, "ay":0.03248, "alpha":0.0, "fx":[0.0596,0.0596,0.0596,0.0596], "fy":[0.5524,0.5524,0.5524,0.5524]}, - {"t":0.75951, "x":6.43868, "y":7.46962, "heading":-1.5708, "vx":3.48769, "vy":-0.37605, "omega":0.0, "ax":0.00331, "ay":0.0308, "alpha":0.0, "fx":[0.05632,0.05632,0.05632,0.05632], "fy":[0.52389,0.52389,0.52389,0.52389]}, - {"t":0.8017, "x":6.58585, "y":7.45378, "heading":-1.5708, "vx":3.48783, "vy":-0.37475, "omega":0.0, "ax":0.00314, "ay":0.0293, "alpha":0.0, "fx":[0.0534,0.0534,0.0534,0.0534], "fy":[0.49837,0.49837,0.49837,0.49837]}, - {"t":0.8439, "x":6.73302, "y":7.43799, "heading":-1.5708, "vx":3.48796, "vy":-0.37352, "omega":0.0, "ax":0.00298, "ay":0.02788, "alpha":0.0, "fx":[0.05065,0.05065,0.05065,0.05065], "fy":[0.47425,0.47425,0.47425,0.47425]}, - {"t":0.88609, "x":6.8802, "y":7.42226, "heading":-1.5708, "vx":3.48809, "vy":-0.37234, "omega":0.0, "ax":0.00277, "ay":0.02598, "alpha":0.0, "fx":[0.04704,0.04704,0.04704,0.04704], "fy":[0.44183,0.44183,0.44183,0.44183]}, - {"t":0.92829, "x":7.02738, "y":7.40657, "heading":-1.5708, "vx":3.48821, "vy":-0.37124, "omega":0.0, "ax":0.00199, "ay":0.01877, "alpha":0.0, "fx":[0.03389,0.03389,0.03389,0.03389], "fy":[0.31928,0.31928,0.31928,0.31928]}, - {"t":0.97048, "x":7.17456, "y":7.39092, "heading":-1.5708, "vx":3.48829, "vy":-0.37045, "omega":0.0, "ax":-0.0038, "ay":-0.03567, "alpha":0.0, "fx":[-0.06463,-0.06463,-0.06463,-0.06463], "fy":[-0.6068,-0.6068,-0.6068,-0.6068]}, - {"t":1.01268, "x":7.32175, "y":7.37526, "heading":-1.5708, "vx":3.48813, "vy":-0.37196, "omega":0.0, "ax":-0.05518, "ay":-0.50276, "alpha":0.0, "fx":[-0.93856,-0.93856,-0.93856,-0.93856], "fy":[-8.55174,-8.55174,-8.55174,-8.55174]}, - {"t":1.05487, "x":7.46888, "y":7.35912, "heading":-1.5708, "vx":3.4858, "vy":-0.39317, "omega":0.0, "ax":-0.52016, "ay":-3.81359, "alpha":0.0, "fx":[-8.84777,-8.84777,-8.84777,-8.84777], "fy":[-64.86801,-64.86801,-64.86801,-64.86801]}, - {"t":1.09707, "x":7.6155, "y":7.33913, "heading":-1.5708, "vx":3.46385, "vy":-0.55408, "omega":0.0, "ax":-1.71034, "ay":-8.08216, "alpha":0.0, "fx":[-29.09238,-29.09238,-29.09238,-29.09238], "fy":[-137.47525,-137.47525,-137.47525,-137.47525]}, - {"t":1.13926, "x":7.76014, "y":7.30856, "heading":-1.5708, "vx":3.39169, "vy":-0.89511, "omega":0.0, "ax":-2.81603, "ay":-8.63397, "alpha":0.0, "fx":[-47.89987,-47.89988,-47.89987,-47.89987], "fy":[-146.86144,-146.86144,-146.86144,-146.86144]}, - {"t":1.18146, "x":7.90074, "y":7.2631, "heading":-1.5708, "vx":3.27286, "vy":-1.25942, "omega":0.0, "ax":-4.59619, "ay":-7.93149, "alpha":0.00002, "fx":[-78.17986,-78.18001,-78.17989,-78.17974], "fy":[-134.91231,-134.91222,-134.9123,-134.91239]}, - {"t":1.21175, "x":7.99776, "y":7.22132, "heading":-1.5708, "vx":3.13366, "vy":-1.49963, "omega":0.0, "ax":-8.96133, "ay":-2.50953, "alpha":0.00002, "fx":[-152.4297,-152.42971,-152.42966,-152.42965], "fy":[-42.68627,-42.68624,-42.6864,-42.68643]}, - {"t":1.24203, "x":8.08855, "y":7.17475, "heading":-1.5708, "vx":2.86226, "vy":-1.57564, "omega":0.0, "ax":-9.3524, "ay":-0.44419, "alpha":0.00001, "fx":[-159.08168,-159.08168,-159.08168,-159.08168], "fy":[-7.55545,-7.55545,-7.5555,-7.55551]}, - {"t":1.27232, "x":8.17095, "y":7.12683, "heading":-1.5708, "vx":2.57902, "vy":-1.58909, "omega":0.0, "ax":-9.37849, "ay":0.3828, "alpha":0.0, "fx":[-159.5254,-159.5254,-159.5254,-159.5254], "fy":[6.51129,6.51129,6.51129,6.51129]}, - {"t":1.3026, "x":8.24476, "y":7.07888, "heading":-1.5708, "vx":2.29498, "vy":-1.5775, "omega":0.0, "ax":-9.36319, "ay":0.81569, "alpha":0.0, "fx":[-159.26511,-159.26511,-159.26511,-159.26511], "fy":[13.87458,13.87458,13.8746,13.8746]}, - {"t":1.33289, "x":8.30997, "y":7.03148, "heading":-1.5708, "vx":2.01141, "vy":-1.55279, "omega":0.0, "ax":-9.34403, "ay":1.08, "alpha":0.0, "fx":[-158.93923,-158.93923,-158.93923,-158.93923], "fy":[18.37043,18.37043,18.37047,18.37046]}, - {"t":1.36318, "x":8.3666, "y":6.98494, "heading":-1.5708, "vx":1.72842, "vy":-1.52008, "omega":0.0, "ax":-9.32695, "ay":1.25761, "alpha":0.0, "fx":[-158.64872,-158.64872,-158.64872,-158.64872], "fy":[21.39155,21.39155,21.3916,21.39159]}, - {"t":1.39346, "x":8.41467, "y":6.93948, "heading":-1.5708, "vx":1.44594, "vy":-1.482, "omega":0.0, "ax":-9.31261, "ay":1.38495, "alpha":-0.00001, "fx":[-158.40488,-158.40488,-158.40487,-158.40487], "fy":[23.55764,23.55764,23.55769,23.55769]}, - {"t":1.42375, "x":8.45419, "y":6.89523, "heading":-1.5708, "vx":1.1639, "vy":-1.44005, "omega":0.0, "ax":-9.30069, "ay":1.48061, "alpha":-0.00001, "fx":[-158.20211,-158.20211,-158.2021,-158.2021], "fy":[25.18479,25.1848,25.18486,25.18485]}, - {"t":1.45403, "x":8.48517, "y":6.8523, "heading":-1.5708, "vx":0.88222, "vy":-1.39521, "omega":0.0, "ax":-9.29073, "ay":1.55504, "alpha":-0.00001, "fx":[-158.03269,-158.03269,-158.03268,-158.03268], "fy":[26.45073,26.45073,26.4508,26.45079]}, - {"t":1.48432, "x":8.50763, "y":6.81076, "heading":-1.5708, "vx":0.60084, "vy":-1.34811, "omega":0.0, "ax":-9.28234, "ay":1.61454, "alpha":-0.00001, "fx":[-157.88989,-157.88989,-157.88988,-157.88988], "fy":[27.46289,27.4629,27.46297,27.46296]}, - {"t":1.5146, "x":8.52157, "y":6.77067, "heading":-1.5708, "vx":0.31972, "vy":-1.29922, "omega":0.0, "ax":-9.27519, "ay":1.66317, "alpha":-0.00001, "fx":[-157.76836,-157.76835,-157.76834,-157.76834], "fy":[28.29,28.29001,28.29008,28.29007]}, - {"t":1.54489, "x":8.527, "y":6.73208, "heading":-1.5708, "vx":0.03881, "vy":-1.24885, "omega":0.0, "ax":-1.62471, "ay":-0.0063, "alpha":0.0, "fx":[-27.63592,-27.63592,-27.63592,-27.63592], "fy":[-0.10721,-0.10721,-0.10721,-0.10721]}, - {"t":1.59244, "x":8.52701, "y":6.6727, "heading":-1.5708, "vx":-0.03844, "vy":-1.24915, "omega":0.0, "ax":1.60135, "ay":-0.00047, "alpha":0.0, "fx":[27.23859,27.23859,27.23859,27.23859], "fy":[-0.00807,-0.00807,-0.00807,-0.00807]}, - {"t":1.63999, "x":8.52699, "y":6.6133, "heading":-1.5708, "vx":0.0377, "vy":-1.24917, "omega":0.0, "ax":-1.57024, "ay":-0.00046, "alpha":0.0, "fx":[-26.7094,-26.7094,-26.7094,-26.7094], "fy":[-0.0079,-0.0079,-0.0079,-0.0079]}, - {"t":1.68753, "x":8.52701, "y":6.55391, "heading":-1.5708, "vx":-0.03696, "vy":-1.24919, "omega":0.0, "ax":1.53919, "ay":-0.00045, "alpha":0.0, "fx":[26.18111,26.18111,26.18111,26.18111], "fy":[-0.00773,-0.00773,-0.00773,-0.00773]}, - {"t":1.73508, "x":8.52699, "y":6.49451, "heading":-1.5708, "vx":0.03622, "vy":-1.24921, "omega":0.0, "ax":-1.50818, "ay":-0.00044, "alpha":0.0, "fx":[-25.6537,-25.6537,-25.6537,-25.6537], "fy":[-0.00756,-0.00756,-0.00756,-0.00756]}, - {"t":1.78263, "x":8.52701, "y":6.43511, "heading":-1.5708, "vx":-0.03549, "vy":-1.24923, "omega":0.0, "ax":1.47723, "ay":-0.00043, "alpha":0.0, "fx":[25.1272,25.1272,25.1272,25.1272], "fy":[-0.00739,-0.00739,-0.00739,-0.00739]}, - {"t":1.83018, "x":8.52699, "y":6.37571, "heading":-1.5708, "vx":0.03475, "vy":-1.24925, "omega":0.0, "ax":-1.44633, "ay":-0.00042, "alpha":0.0, "fx":[-24.60159,-24.60159,-24.60159,-24.60159], "fy":[-0.00722,-0.00722,-0.00722,-0.00722]}, - {"t":1.87772, "x":8.52701, "y":6.31631, "heading":-1.5708, "vx":-0.03402, "vy":-1.24927, "omega":0.0, "ax":1.41548, "ay":-0.00041, "alpha":0.0, "fx":[24.07688,24.07688,24.07688,24.07688], "fy":[-0.00706,-0.00706,-0.00706,-0.00706]}, - {"t":1.92527, "x":8.52699, "y":6.25691, "heading":-1.5708, "vx":0.03329, "vy":-1.24929, "omega":0.0, "ax":-1.38468, "ay":-0.00041, "alpha":0.0, "fx":[-23.55308,-23.55308,-23.55308,-23.55308], "fy":[-0.00689,-0.00689,-0.00689,-0.00689]}, - {"t":1.97282, "x":8.52701, "y":6.19751, "heading":-1.5708, "vx":-0.03255, "vy":-1.24931, "omega":0.0, "ax":1.35394, "ay":-0.0004, "alpha":0.0, "fx":[23.03018,23.03018,23.03018,23.03018], "fy":[-0.00673,-0.00673,-0.00673,-0.00673]}, - {"t":2.02037, "x":8.52699, "y":6.13811, "heading":-1.5708, "vx":0.03182, "vy":-1.24933, "omega":0.0, "ax":-1.32326, "ay":-0.00039, "alpha":0.0, "fx":[-22.50819,-22.50819,-22.50819,-22.50819], "fy":[-0.00656,-0.00656,-0.00656,-0.00656]}, - {"t":2.06792, "x":8.52701, "y":6.07871, "heading":-1.5708, "vx":-0.03109, "vy":-1.24935, "omega":0.0, "ax":1.29262, "ay":-0.00038, "alpha":0.0, "fx":[21.9871,21.9871,21.9871,21.9871], "fy":[-0.0064,-0.0064,-0.0064,-0.0064]}, - {"t":2.11546, "x":8.52699, "y":6.0193, "heading":-1.5708, "vx":0.03037, "vy":-1.24937, "omega":0.0, "ax":-1.26204, "ay":-0.00037, "alpha":0.0, "fx":[-21.46691,-21.46691,-21.46691,-21.46691], "fy":[-0.00624,-0.00624,-0.00624,-0.00624]}, - {"t":2.16301, "x":8.52701, "y":5.9599, "heading":-1.5708, "vx":-0.02964, "vy":-1.24938, "omega":0.0, "ax":1.23151, "ay":-0.00036, "alpha":0.0, "fx":[20.94762,20.94762,20.94762,20.94762], "fy":[-0.00608,-0.00608,-0.00608,-0.00608]}, - {"t":2.21056, "x":8.52699, "y":5.90049, "heading":-1.5708, "vx":0.02892, "vy":-1.2494, "omega":0.0, "ax":-1.20103, "ay":-0.00035, "alpha":0.0, "fx":[-20.42923,-20.42923,-20.42923,-20.42923], "fy":[-0.00592,-0.00592,-0.00592,-0.00592]}, - {"t":2.25811, "x":8.52701, "y":5.84109, "heading":-1.5708, "vx":-0.02819, "vy":-1.24942, "omega":0.0, "ax":1.17061, "ay":-0.00034, "alpha":0.0, "fx":[19.91173,19.91173,19.91173,19.91173], "fy":[-0.00576,-0.00576,-0.00576,-0.00576]}, - {"t":2.30565, "x":8.52699, "y":5.78168, "heading":-1.5708, "vx":0.02747, "vy":-1.24943, "omega":0.0, "ax":-1.14024, "ay":-0.00033, "alpha":0.0, "fx":[-19.39513,-19.39513,-19.39513,-19.39513], "fy":[-0.0056,-0.0056,-0.0056,-0.0056]}, - {"t":2.3532, "x":8.52701, "y":5.72227, "heading":-1.5708, "vx":-0.02675, "vy":-1.24945, "omega":0.0, "ax":1.10992, "ay":-0.00032, "alpha":0.0, "fx":[18.87941,18.87941,18.87941,18.87941], "fy":[-0.00544,-0.00544,-0.00544,-0.00544]}, - {"t":2.40075, "x":8.52699, "y":5.66286, "heading":-1.5708, "vx":0.02603, "vy":-1.24947, "omega":0.0, "ax":-1.07965, "ay":-0.00031, "alpha":0.0, "fx":[-18.36456,-18.36456,-18.36456,-18.36456], "fy":[-0.00528,-0.00528,-0.00528,-0.00528]}, - {"t":2.4483, "x":8.52701, "y":5.60345, "heading":-1.5708, "vx":-0.02531, "vy":-1.24948, "omega":0.0, "ax":1.04944, "ay":-0.0003, "alpha":0.0, "fx":[17.85059,17.85059,17.85059,17.85059], "fy":[-0.00512,-0.00512,-0.00512,-0.00512]}, - {"t":2.49585, "x":8.52699, "y":5.54404, "heading":-1.5708, "vx":0.02459, "vy":-1.24949, "omega":0.0, "ax":-1.01927, "ay":-0.00029, "alpha":0.0, "fx":[-17.33749,-17.33749,-17.33749,-17.33749], "fy":[-0.00497,-0.00497,-0.00497,-0.00497]}, - {"t":2.54339, "x":8.52701, "y":5.48463, "heading":-1.5708, "vx":-0.02387, "vy":-1.24951, "omega":0.0, "ax":0.98915, "ay":-0.00028, "alpha":0.0, "fx":[16.82524,16.82524,16.82524,16.82524], "fy":[-0.00481,-0.00481,-0.00481,-0.00481]}, - {"t":2.59094, "x":8.52699, "y":5.42522, "heading":-1.5708, "vx":0.02316, "vy":-1.24952, "omega":0.0, "ax":-0.95909, "ay":-0.00027, "alpha":0.0, "fx":[-16.31384,-16.31384,-16.31384,-16.31384], "fy":[-0.00466,-0.00466,-0.00466,-0.00466]}, - {"t":2.63849, "x":8.52701, "y":5.36581, "heading":-1.5708, "vx":-0.02244, "vy":-1.24953, "omega":0.0, "ax":0.92907, "ay":-0.00026, "alpha":0.0, "fx":[15.80327,15.80327,15.80327,15.80327], "fy":[-0.00451,-0.00451,-0.00451,-0.00451]}, - {"t":2.68604, "x":8.52699, "y":5.30639, "heading":-1.5708, "vx":0.02173, "vy":-1.24955, "omega":0.0, "ax":-0.89911, "ay":-0.00026, "alpha":0.0, "fx":[-15.29353,-15.29353,-15.29353,-15.29353], "fy":[-0.00435,-0.00435,-0.00435,-0.00435]}, - {"t":2.73358, "x":8.52701, "y":5.24698, "heading":-1.5708, "vx":-0.02102, "vy":-1.24956, "omega":0.0, "ax":0.86919, "ay":-0.00025, "alpha":0.0, "fx":[14.7846,14.7846,14.7846,14.7846], "fy":[-0.0042,-0.0042,-0.0042,-0.0042]}, - {"t":2.78113, "x":8.52699, "y":5.18757, "heading":-1.5708, "vx":0.02031, "vy":-1.24957, "omega":0.0, "ax":-0.83931, "ay":-0.00024, "alpha":0.0, "fx":[-14.27647,-14.27647,-14.27647,-14.27647], "fy":[-0.00405,-0.00405,-0.00405,-0.00405]}, - {"t":2.82868, "x":8.52701, "y":5.12815, "heading":-1.5708, "vx":-0.0196, "vy":-1.24958, "omega":0.0, "ax":0.80949, "ay":-0.00023, "alpha":0.0, "fx":[13.76913,13.76913,13.76913,13.76913], "fy":[-0.0039,-0.0039,-0.0039,-0.0039]}, - {"t":2.87623, "x":8.52699, "y":5.06874, "heading":-1.5708, "vx":0.01889, "vy":-1.24959, "omega":0.0, "ax":-0.7797, "ay":-0.00022, "alpha":0.0, "fx":[-13.26255,-13.26255,-13.26255,-13.26255], "fy":[-0.00375,-0.00375,-0.00375,-0.00375]}, - {"t":2.92378, "x":8.52701, "y":5.00932, "heading":-1.5708, "vx":-0.01818, "vy":-1.2496, "omega":0.0, "ax":0.74997, "ay":-0.00021, "alpha":0.0, "fx":[12.75672,12.75672,12.75672,12.75672], "fy":[-0.0036,-0.0036,-0.0036,-0.0036]}, - {"t":2.97132, "x":8.52699, "y":4.9499, "heading":-1.5708, "vx":0.01748, "vy":-1.24961, "omega":0.0, "ax":-0.72027, "ay":-0.0002, "alpha":0.0, "fx":[-12.25164,-12.25164,-12.25164,-12.25164], "fy":[-0.00346,-0.00346,-0.00346,-0.00346]}, - {"t":3.01887, "x":8.52701, "y":4.89049, "heading":-1.5708, "vx":-0.01677, "vy":-1.24962, "omega":0.0, "ax":0.69062, "ay":-0.00019, "alpha":0.0, "fx":[11.74726,11.74726,11.74726,11.74726], "fy":[-0.00331,-0.00331,-0.00331,-0.00331]}, - {"t":3.06642, "x":8.52699, "y":4.83107, "heading":-1.5708, "vx":0.01607, "vy":-1.24963, "omega":0.0, "ax":-0.66101, "ay":-0.00019, "alpha":0.0, "fx":[-11.24358,-11.24358,-11.24358,-11.24358], "fy":[-0.00316,-0.00316,-0.00316,-0.00316]}, - {"t":3.11397, "x":8.52701, "y":4.77165, "heading":-1.5708, "vx":-0.01536, "vy":-1.24964, "omega":0.0, "ax":0.63144, "ay":-0.00018, "alpha":0.0, "fx":[10.74058,10.74058,10.74058,10.74058], "fy":[-0.00302,-0.00302,-0.00302,-0.00302]}, - {"t":3.16151, "x":8.52699, "y":4.71224, "heading":-1.5708, "vx":0.01466, "vy":-1.24965, "omega":0.0, "ax":-0.60191, "ay":-0.00017, "alpha":0.0, "fx":[-10.23824,-10.23824,-10.23824,-10.23824], "fy":[-0.00287,-0.00287,-0.00287,-0.00287]}, - {"t":3.20906, "x":8.52701, "y":4.65282, "heading":-1.5708, "vx":-0.01396, "vy":-1.24966, "omega":0.0, "ax":0.57241, "ay":-0.00016, "alpha":0.0, "fx":[9.73652,9.73652,9.73652,9.73652], "fy":[-0.00273,-0.00273,-0.00273,-0.00273]}, - {"t":3.25661, "x":8.52699, "y":4.5934, "heading":-1.5708, "vx":0.01326, "vy":-1.24967, "omega":0.0, "ax":-0.54295, "ay":-0.00015, "alpha":0.0, "fx":[-9.23542,-9.23542,-9.23542,-9.23542], "fy":[-0.00259,-0.00259,-0.00259,-0.00259]}, - {"t":3.30416, "x":8.52701, "y":4.53398, "heading":-1.5708, "vx":-0.01256, "vy":-1.24967, "omega":0.0, "ax":0.51352, "ay":-0.00014, "alpha":0.0, "fx":[8.7349,8.7349,8.7349,8.7349], "fy":[-0.00244,-0.00244,-0.00244,-0.00244]}, - {"t":3.35171, "x":8.52699, "y":4.47456, "heading":-1.5708, "vx":0.01186, "vy":-1.24968, "omega":0.0, "ax":-0.48413, "ay":-0.00014, "alpha":0.0, "fx":[-8.23493,-8.23493,-8.23493,-8.23493], "fy":[-0.0023,-0.0023,-0.0023,-0.0023]}, - {"t":3.39925, "x":8.52701, "y":4.41514, "heading":-1.5708, "vx":-0.01116, "vy":-1.24969, "omega":0.0, "ax":0.45477, "ay":-0.00013, "alpha":0.0, "fx":[7.7355,7.7355,7.7355,7.7355], "fy":[-0.00216,-0.00216,-0.00216,-0.00216]}, - {"t":3.4468, "x":8.52699, "y":4.35572, "heading":-1.5708, "vx":0.01046, "vy":-1.24969, "omega":0.0, "ax":-0.42544, "ay":-0.00012, "alpha":0.0, "fx":[-7.23657,-7.23657,-7.23657,-7.23657], "fy":[-0.00202,-0.00202,-0.00202,-0.00202]}, - {"t":3.49435, "x":8.52701, "y":4.2963, "heading":-1.5708, "vx":-0.00977, "vy":-1.2497, "omega":0.0, "ax":0.39613, "ay":-0.00011, "alpha":0.0, "fx":[6.73812,6.73812,6.73812,6.73812], "fy":[-0.00188,-0.00188,-0.00188,-0.00188]}, - {"t":3.5419, "x":8.52699, "y":4.23688, "heading":-1.5708, "vx":0.00907, "vy":-1.2497, "omega":0.0, "ax":-0.36686, "ay":-0.0001, "alpha":0.0, "fx":[-6.24012,-6.24012,-6.24012,-6.24012], "fy":[-0.00174,-0.00174,-0.00174,-0.00174]}, - {"t":3.58944, "x":8.52701, "y":4.17746, "heading":-1.5708, "vx":-0.00837, "vy":-1.24971, "omega":0.0, "ax":0.3376, "ay":-0.00009, "alpha":0.0, "fx":[5.74254,5.74254,5.74254,5.74254], "fy":[-0.0016,-0.0016,-0.0016,-0.0016]}, - {"t":3.63699, "x":8.52699, "y":4.11804, "heading":-1.5708, "vx":0.00768, "vy":-1.24971, "omega":0.0, "ax":-0.30837, "ay":-0.00009, "alpha":0.0, "fx":[-5.24534,-5.24534,-5.24534,-5.24534], "fy":[-0.00146,-0.00146,-0.00146,-0.00146]}, - {"t":3.68454, "x":8.52701, "y":4.05862, "heading":-1.5708, "vx":-0.00698, "vy":-1.24972, "omega":0.0, "ax":0.27916, "ay":-0.00008, "alpha":0.0, "fx":[4.7485,4.7485,4.7485,4.7485], "fy":[-0.00132,-0.00132,-0.00132,-0.00132]}, - {"t":3.73209, "x":8.52699, "y":3.9992, "heading":-1.5708, "vx":0.00629, "vy":-1.24972, "omega":0.0, "ax":-0.24997, "ay":-0.00007, "alpha":0.0, "fx":[-4.25199,-4.25199,-4.25199,-4.25199], "fy":[-0.00118,-0.00118,-0.00118,-0.00118]}, - {"t":3.77964, "x":8.52701, "y":3.93977, "heading":-1.5708, "vx":-0.0056, "vy":-1.24972, "omega":0.0, "ax":0.2208, "ay":-0.00006, "alpha":0.0, "fx":[3.75576,3.75576,3.75576,3.75576], "fy":[-0.00104,-0.00104,-0.00104,-0.00104]}, - {"t":3.82718, "x":8.52699, "y":3.88035, "heading":-1.5708, "vx":0.0049, "vy":-1.24973, "omega":0.0, "ax":-0.19164, "ay":-0.00005, "alpha":0.0, "fx":[-3.2598,-3.2598,-3.2598,-3.2598], "fy":[-0.00083,-0.00083,-0.00083,-0.00083]}, - {"t":3.87473, "x":8.52701, "y":3.82093, "heading":-1.5708, "vx":-0.00421, "vy":-1.24973, "omega":0.0, "ax":0.1625, "ay":1.53152, "alpha":0.0, "fx":[2.76415,2.76415,2.76415,2.76415], "fy":[26.05077,26.05077,26.05077,26.05077]}, - {"t":3.92228, "x":8.52699, "y":3.76324, "heading":-1.5708, "vx":0.00352, "vy":-1.17691, "omega":0.0, "ax":-0.13341, "ay":9.41424, "alpha":0.0, "fx":[-2.26921,-2.26921,-2.26921,-2.26921], "fy":[160.13353,160.13353,160.13353,160.13353]}, - {"t":3.96983, "x":8.52701, "y":3.71792, "heading":-1.5708, "vx":-0.00283, "vy":-0.72928, "omega":0.0, "ax":0.11162, "ay":9.42564, "alpha":0.0, "fx":[1.8986,1.8986,1.8986,1.8986], "fy":[160.32746,160.32746,160.32746,160.32746]}, - {"t":4.01737, "x":8.527, "y":3.6939, "heading":-1.5708, "vx":0.00248, "vy":-0.28111, "omega":0.0, "ax":-4.92033, "ay":8.04324, "alpha":0.0, "fx":[-83.69335,-83.69335,-83.69335,-83.69335], "fy":[136.81315,136.81315,136.81315,136.81315]}, - {"t":4.06211, "x":8.52219, "y":3.68937, "heading":-1.5708, "vx":-0.21761, "vy":0.07867, "omega":0.0, "ax":-4.91743, "ay":8.04359, "alpha":0.0, "fx":[-83.64406,-83.64406,-83.64406,-83.64406], "fy":[136.81917,136.81917,136.81917,136.81917]}, - {"t":4.10684, "x":8.50753, "y":3.70094, "heading":-1.5708, "vx":-0.43758, "vy":0.43847, "omega":0.0, "ax":-4.91358, "ay":8.04406, "alpha":0.0, "fx":[-83.57852,-83.57852,-83.57852,-83.57852], "fy":[136.82716,136.82716,136.82716,136.82716]}, - {"t":4.15157, "x":8.48305, "y":3.7286, "heading":-1.5708, "vx":-0.65737, "vy":0.7983, "omega":0.0, "ax":-4.90821, "ay":8.04471, "alpha":0.0, "fx":[-83.48717,-83.48717,-83.48717,-83.48717], "fy":[136.83825,136.83825,136.83825,136.83825]}, - {"t":4.1963, "x":8.44873, "y":3.77236, "heading":-1.5708, "vx":-0.87692, "vy":1.15815, "omega":0.0, "ax":-4.90021, "ay":8.04567, "alpha":0.0, "fx":[-83.35117,-83.35117,-83.35117,-83.35117], "fy":[136.85459,136.85459,136.85459,136.85459]}, - {"t":4.24103, "x":8.4046, "y":3.83221, "heading":-1.5708, "vx":-1.09611, "vy":1.51804, "omega":0.0, "ax":-4.88704, "ay":8.04723, "alpha":0.0, "fx":[-83.12724,-83.12724,-83.12724,-83.12724], "fy":[136.88104,136.88104,136.88104,136.88104]}, - {"t":4.28576, "x":8.35068, "y":3.90817, "heading":-1.5708, "vx":-1.31472, "vy":1.87801, "omega":0.0, "ax":-4.8613, "ay":8.05018, "alpha":0.0, "fx":[-82.68924,-82.68924,-82.68924,-82.68924], "fy":[136.93125,136.93125,136.93125,136.93125]}, - {"t":4.33049, "x":8.28701, "y":4.00023, "heading":-1.5708, "vx":-1.53217, "vy":2.23811, "omega":0.0, "ax":-4.78826, "ay":8.05803, "alpha":0.0, "fx":[-81.44686,-81.44686,-81.44686,-81.44686], "fy":[137.06481,137.06481,137.06481,137.06481]}, - {"t":4.37523, "x":8.21368, "y":4.10841, "heading":-1.5708, "vx":-1.74636, "vy":2.59855, "omega":0.0, "ax":-3.03344, "ay":8.08499, "alpha":0.0, "fx":[-51.59788,-51.59788,-51.59788,-51.59788], "fy":[137.52344,137.52344,137.52344,137.52344]}, - {"t":4.41996, "x":8.13253, "y":4.23273, "heading":-1.5708, "vx":-1.88205, "vy":2.96021, "omega":0.0, "ax":0.90862, "ay":0.5761, "alpha":0.0, "fx":[15.45538,15.45538,15.45538,15.45538], "fy":[9.79924,9.79924,9.79924,9.79924]}, - {"t":4.46469, "x":8.04925, "y":4.36572, "heading":-1.5708, "vx":-1.8414, "vy":2.98598, "omega":0.0, "ax":0.09183, "ay":0.05654, "alpha":0.0, "fx":[1.56194,1.56194,1.56194,1.56194], "fy":[0.96178,0.96178,0.96178,0.96178]}, - {"t":4.50942, "x":7.96697, "y":4.49935, "heading":-1.5708, "vx":-1.8373, "vy":2.98851, "omega":0.0, "ax":0.00936, "ay":0.00575, "alpha":0.0, "fx":[0.15915,0.15915,0.15915,0.15915], "fy":[0.09784,0.09784,0.09784,0.09784]}, - {"t":4.55415, "x":7.8848, "y":4.63303, "heading":-1.5708, "vx":-1.83688, "vy":2.98876, "omega":0.0, "ax":0.00129, "ay":0.00079, "alpha":0.0, "fx":[0.02189,0.02189,0.02189,0.02189], "fy":[0.01347,0.01347,0.01347,0.01347]}, - {"t":4.59888, "x":7.80263, "y":4.76673, "heading":-1.5708, "vx":-1.83682, "vy":2.9888, "omega":0.0, "ax":0.00057, "ay":0.00035, "alpha":0.0, "fx":[0.0097,0.0097,0.0097,0.0097], "fy":[0.00598,0.00598,0.00598,0.00598]}, - {"t":4.64362, "x":7.72047, "y":4.90042, "heading":-1.5708, "vx":-1.8368, "vy":2.98882, "omega":0.0, "ax":0.00058, "ay":0.00036, "alpha":0.0, "fx":[0.00993,0.00993,0.00993,0.00993], "fy":[0.00613,0.00613,0.00613,0.00613]}, - {"t":4.68835, "x":7.63831, "y":5.03411, "heading":-1.5708, "vx":-1.83677, "vy":2.98883, "omega":0.0, "ax":0.00067, "ay":0.00042, "alpha":0.0, "fx":[0.01147,0.01147,0.01147,0.01147], "fy":[0.00708,0.00708,0.00708,0.00708]}, - {"t":4.73308, "x":7.55615, "y":5.16781, "heading":-1.5708, "vx":-1.83674, "vy":2.98885, "omega":0.0, "ax":0.00078, "ay":0.00048, "alpha":0.0, "fx":[0.01322,0.01322,0.01322,0.01322], "fy":[0.00816,0.00816,0.00816,0.00816]}, - {"t":4.77781, "x":7.47399, "y":5.30151, "heading":-1.5708, "vx":-1.8367, "vy":2.98887, "omega":0.0, "ax":0.00087, "ay":0.00054, "alpha":0.0, "fx":[0.01485,0.01485,0.01485,0.01485], "fy":[0.00917,0.00917,0.00917,0.00917]}, - {"t":4.82254, "x":7.39183, "y":5.4352, "heading":-1.5708, "vx":-1.83667, "vy":2.9889, "omega":0.0, "ax":0.00081, "ay":0.0005, "alpha":0.0, "fx":[0.01373,0.01373,0.01373,0.01373], "fy":[0.00849,0.00849,0.00849,0.00849]}, - {"t":4.86727, "x":7.30967, "y":5.5689, "heading":-1.5708, "vx":-1.83663, "vy":2.98892, "omega":0.0, "ax":-0.00098, "ay":-0.0006, "alpha":0.0, "fx":[-0.01674,-0.01674,-0.01674,-0.01674], "fy":[-0.01024,-0.01024,-0.01024,-0.01024]}, - {"t":4.912, "x":7.22752, "y":5.7026, "heading":-1.5708, "vx":-1.83667, "vy":2.98889, "omega":0.0, "ax":-0.02037, "ay":-0.01252, "alpha":0.0, "fx":[-0.34655,-0.34655,-0.34655,-0.34655], "fy":[-0.21298,-0.21298,-0.21298,-0.21298]}, - {"t":4.95674, "x":7.14534, "y":5.83628, "heading":-1.5708, "vx":-1.83758, "vy":2.98833, "omega":0.0, "ax":-0.21732, "ay":-0.13526, "alpha":0.0, "fx":[-3.69657,-3.69657,-3.69657,-3.69657], "fy":[-2.30075,-2.30075,-2.30075,-2.30075]}, - {"t":5.00147, "x":7.06293, "y":5.96982, "heading":-1.5708, "vx":-1.84731, "vy":2.98228, "omega":0.0, "ax":1.55496, "ay":-5.59189, "alpha":0.0, "fx":[26.44946,26.44946,26.44946,26.44946], "fy":[-95.1164,-95.1164,-95.1164,-95.1164]}, - {"t":5.0462, "x":6.98185, "y":6.09763, "heading":-1.5708, "vx":-1.77775, "vy":2.73215, "omega":0.0, "ax":4.86996, "ay":-8.00365, "alpha":0.0, "fx":[82.8366,82.8366,82.8366,82.8366], "fy":[-136.13976,-136.13976,-136.13976,-136.13976]}, - {"t":5.09093, "x":6.9072, "y":6.21184, "heading":-1.5708, "vx":-1.55991, "vy":2.37413, "omega":0.0, "ax":4.90424, "ay":-8.02271, "alpha":0.0, "fx":[83.41975,83.41975,83.41975,83.41975], "fy":[-136.46393,-136.46393,-136.46393,-136.46393]}, - {"t":5.13566, "x":6.84233, "y":6.31001, "heading":-1.5708, "vx":-1.34053, "vy":2.01526, "omega":0.0, "ax":4.9158, "ay":-8.02907, "alpha":0.0, "fx":[83.61631,83.61631,83.61631,83.61631], "fy":[-136.57226,-136.57226,-136.57226,-136.57226]}, - {"t":5.18039, "x":6.78728, "y":6.39212, "heading":-1.5708, "vx":-1.12064, "vy":1.65611, "omega":0.0, "ax":-5.74144, "ay":-4.53305, "alpha":0.0, "fx":[-97.66024,-97.66024,-97.66024,-97.66024], "fy":[-77.10588,-77.10588,-77.10588,-77.10588]}, - {"t":5.22024, "x":6.73807, "y":6.45451, "heading":-1.5708, "vx":-1.34941, "vy":1.47549, "omega":0.0, "ax":-0.68219, "ay":-0.6356, "alpha":0.0, "fx":[-11.60384,-11.60384,-11.60384,-11.60384], "fy":[-10.81143,-10.81143,-10.81143,-10.81143]}, - {"t":5.26008, "x":6.68376, "y":6.5128, "heading":-1.5708, "vx":-1.37659, "vy":1.45017, "omega":0.0, "ax":-0.03376, "ay":-0.03208, "alpha":0.0, "fx":[-0.57432,-0.57432,-0.57432,-0.57432], "fy":[-0.54567,-0.54567,-0.54567,-0.54567]}, - {"t":5.29993, "x":6.62889, "y":6.57055, "heading":-1.5708, "vx":-1.37794, "vy":1.44889, "omega":0.0, "ax":-0.00052, "ay":-0.00049, "alpha":0.0, "fx":[-0.00876,-0.00876,-0.00876,-0.00876], "fy":[-0.00832,-0.00832,-0.00832,-0.00832]}, - {"t":5.33977, "x":6.57398, "y":6.62828, "heading":-1.5708, "vx":-1.37796, "vy":1.44887, "omega":0.0, "ax":0.00126, "ay":0.0012, "alpha":0.0, "fx":[0.02143,0.02143,0.02143,0.02143], "fy":[0.02039,0.02039,0.02039,0.02039]}, - {"t":5.37962, "x":6.51908, "y":6.68601, "heading":-1.5708, "vx":-1.37791, "vy":1.44892, "omega":0.0, "ax":0.00143, "ay":0.00136, "alpha":0.0, "fx":[0.02439,0.02439,0.02439,0.02439], "fy":[0.02321,0.02321,0.02321,0.02321]}, - {"t":5.41946, "x":6.46418, "y":6.74375, "heading":-1.5708, "vx":-1.37785, "vy":1.44897, "omega":0.0, "ax":0.00153, "ay":0.00146, "alpha":0.0, "fx":[0.0261,0.0261,0.0261,0.0261], "fy":[0.02483,0.02483,0.02483,0.02483]}, - {"t":5.45931, "x":6.40928, "y":6.80148, "heading":-1.5708, "vx":-1.37779, "vy":1.44903, "omega":0.0, "ax":0.00164, "ay":0.00156, "alpha":0.0, "fx":[0.02793,0.02793,0.02793,0.02793], "fy":[0.02657,0.02657,0.02657,0.02657]}, - {"t":5.49915, "x":6.35438, "y":6.85922, "heading":-1.5708, "vx":-1.37772, "vy":1.44909, "omega":0.0, "ax":0.00176, "ay":0.00168, "alpha":0.0, "fx":[0.02997,0.02997,0.02997,0.02997], "fy":[0.02851,0.02851,0.02851,0.02851]}, - {"t":5.539, "x":6.29949, "y":6.91696, "heading":-1.5708, "vx":-1.37765, "vy":1.44916, "omega":0.0, "ax":0.0019, "ay":0.00181, "alpha":0.0, "fx":[0.03228,0.03228,0.03228,0.03228], "fy":[0.03071,0.03071,0.03071,0.03071]}, - {"t":5.57884, "x":6.2446, "y":6.9747, "heading":-1.5708, "vx":-1.37758, "vy":1.44923, "omega":0.0, "ax":0.00205, "ay":0.00195, "alpha":0.0, "fx":[0.03492,0.03492,0.03492,0.03492], "fy":[0.03321,0.03321,0.03321,0.03321]}, - {"t":5.61868, "x":6.18971, "y":7.03245, "heading":-1.5708, "vx":-1.3775, "vy":1.44931, "omega":0.0, "ax":0.00223, "ay":0.00212, "alpha":0.0, "fx":[0.03795,0.03795,0.03795,0.03795], "fy":[0.03609,0.03609,0.03609,0.03609]}, - {"t":5.65853, "x":6.13483, "y":7.09019, "heading":-1.5708, "vx":-1.37741, "vy":1.44939, "omega":0.0, "ax":0.00244, "ay":0.00232, "alpha":0.0, "fx":[0.04149,0.04149,0.04149,0.04149], "fy":[0.03945,0.03945,0.03945,0.03945]}, - {"t":5.69837, "x":6.07995, "y":7.14795, "heading":-1.5708, "vx":-1.37731, "vy":1.44949, "omega":0.0, "ax":0.00269, "ay":0.00255, "alpha":0.0, "fx":[0.04569,0.04569,0.04569,0.04569], "fy":[0.04344,0.04344,0.04344,0.04344]}, - {"t":5.73822, "x":6.02507, "y":7.2057, "heading":-1.5708, "vx":-1.3772, "vy":1.44959, "omega":0.0, "ax":0.00298, "ay":0.00284, "alpha":0.0, "fx":[0.05076,0.05076,0.05076,0.05076], "fy":[0.04825,0.04825,0.04825,0.04825]}, - {"t":5.77806, "x":5.9702, "y":7.26346, "heading":-1.5708, "vx":-1.37708, "vy":1.4497, "omega":0.0, "ax":0.00335, "ay":0.00319, "alpha":0.0, "fx":[0.05702,0.05702,0.05702,0.05702], "fy":[0.05419,0.05419,0.05419,0.05419]}, - {"t":5.81791, "x":5.91533, "y":7.32123, "heading":-1.5708, "vx":-1.37695, "vy":1.44983, "omega":0.0, "ax":0.0038, "ay":0.00361, "alpha":0.0, "fx":[0.06466,0.06466,0.06466,0.06466], "fy":[0.06144,0.06144,0.06144,0.06144]}, - {"t":5.85775, "x":5.86047, "y":7.379, "heading":-1.5708, "vx":-1.3768, "vy":1.44997, "omega":0.0, "ax":0.00399, "ay":0.00379, "alpha":0.0, "fx":[0.06792,0.06792,0.06792,0.06792], "fy":[0.06452,0.06452,0.06452,0.06452]}, - {"t":5.8976, "x":5.80562, "y":7.43678, "heading":-1.5708, "vx":-1.37664, "vy":1.45012, "omega":0.0, "ax":-0.0037, "ay":-0.00351, "alpha":0.0, "fx":[-0.06289,-0.06289,-0.06289,-0.06289], "fy":[-0.05966,-0.05966,-0.05966,-0.05966]}, - {"t":5.93744, "x":5.75076, "y":7.49455, "heading":-1.5708, "vx":-1.37679, "vy":1.44998, "omega":0.0, "ax":-0.16989, "ay":-0.16207, "alpha":0.0, "fx":[-2.88971,-2.88971,-2.88971,-2.88971], "fy":[-2.75673,-2.75673,-2.75673,-2.75673]}, - {"t":5.97729, "x":5.69577, "y":7.5522, "heading":-1.5708, "vx":-1.38356, "vy":1.44353, "omega":0.0, "ax":-2.73498, "ay":-2.83577, "alpha":0.0, "fx":[-46.5212,-46.5212,-46.5212,-46.5212], "fy":[-48.23563,-48.23563,-48.23563,-48.23563]}, - {"t":6.01713, "x":5.63847, "y":7.60747, "heading":-1.5708, "vx":-1.49253, "vy":1.33054, "omega":0.0, "ax":-5.30516, "ay":-7.13632, "alpha":0.0, "fx":[-90.23922,-90.23922,-90.23922,-90.23922], "fy":[-121.38682,-121.38682,-121.38682,-121.38682]}, - {"t":6.05698, "x":5.57479, "y":7.65482, "heading":-1.5708, "vx":-1.70391, "vy":1.04619, "omega":0.0, "ax":-4.09385, "ay":-8.34445, "alpha":0.0, "fx":[-69.63522,-69.63522,-69.63522,-69.63522], "fy":[-141.93663,-141.93663,-141.93663,-141.93663]}, - {"t":6.09682, "x":5.50365, "y":7.68988, "heading":-1.5708, "vx":-1.86703, "vy":0.71371, "omega":0.0, "ax":-2.52571, "ay":-8.94223, "alpha":0.0, "fx":[-42.96163,-42.96163,-42.96163,-42.96163], "fy":[-152.10469,-152.10469,-152.10469,-152.10469]}, - {"t":6.1362, "x":5.42818, "y":7.71105, "heading":-1.5708, "vx":-1.96648, "vy":0.3616, "omega":0.0, "ax":-0.83674, "ay":-8.79425, "alpha":0.0, "fx":[-14.23264,-14.23264,-14.23264,-14.23264], "fy":[-149.58773,-149.58773,-149.58773,-149.58773]}, - {"t":6.17557, "x":5.3501, "y":7.71847, "heading":-1.5708, "vx":-1.99943, "vy":0.01532, "omega":0.0, "ax":0.04932, "ay":-2.66279, "alpha":0.0, "fx":[0.8389,0.8389,0.8389,0.8389], "fy":[-45.29329,-45.29329,-45.29329,-45.29329]}, - {"t":6.21495, "x":5.27141, "y":7.71701, "heading":-1.5708, "vx":-1.99749, "vy":-0.08953, "omega":0.0, "ax":-0.0165, "ay":0.40432, "alpha":0.0, "fx":[-0.2807,-0.2807,-0.2807,-0.2807], "fy":[6.8773,6.8773,6.8773,6.8773]}, - {"t":6.25432, "x":5.19274, "y":7.7138, "heading":-1.5708, "vx":-1.99814, "vy":-0.07361, "omega":0.0, "ax":-0.01156, "ay":0.34581, "alpha":0.0, "fx":[-0.19657,-0.19657,-0.19657,-0.19657], "fy":[5.88213,5.88213,5.88213,5.88213]}, - {"t":6.2937, "x":5.11405, "y":7.71117, "heading":-1.5708, "vx":-1.99859, "vy":-0.05999, "omega":0.0, "ax":-0.00723, "ay":0.26385, "alpha":0.0, "fx":[-0.12301,-0.12301,-0.12301,-0.12301], "fy":[4.48794,4.48794,4.48794,4.48794]}, - {"t":6.33308, "x":5.03535, "y":7.70901, "heading":-1.5708, "vx":-1.99888, "vy":-0.0496, "omega":0.0, "ax":-0.00495, "ay":0.2186, "alpha":0.0, "fx":[-0.08424,-0.08424,-0.08424,-0.08424], "fy":[3.71837,3.71837,3.71837,3.71837]}, - {"t":6.37245, "x":4.95664, "y":7.70722, "heading":-1.5708, "vx":-1.99907, "vy":-0.04099, "omega":0.0, "ax":-0.00356, "ay":0.19118, "alpha":0.0, "fx":[-0.06055,-0.06055,-0.06055,-0.06055], "fy":[3.25188,3.25188,3.25188,3.25188]}, - {"t":6.41183, "x":4.87792, "y":7.70576, "heading":-1.5708, "vx":-1.99921, "vy":-0.03347, "omega":0.0, "ax":-0.0026, "ay":0.17316, "alpha":0.0, "fx":[-0.04427,-0.04427,-0.04427,-0.04427], "fy":[2.94545,2.94545,2.94545,2.94545]}, - {"t":6.4512, "x":4.7992, "y":7.70457, "heading":-1.5708, "vx":-1.99931, "vy":-0.02665, "omega":0.0, "ax":-0.00189, "ay":0.16088, "alpha":0.0, "fx":[-0.03213,-0.03213,-0.03213,-0.03213], "fy":[2.73647,2.73647,2.73647,2.73647]}, - {"t":6.49058, "x":4.72047, "y":7.70365, "heading":-1.5708, "vx":-1.99939, "vy":-0.02031, "omega":0.0, "ax":-0.00132, "ay":0.15249, "alpha":0.0, "fx":[-0.02245,-0.02245,-0.02245,-0.02245], "fy":[2.59384,2.59384,2.59384,2.59384]}, - {"t":6.52995, "x":4.64175, "y":7.70297, "heading":-1.5708, "vx":-1.99944, "vy":-0.01431, "omega":0.0, "ax":-0.00084, "ay":0.14702, "alpha":0.0, "fx":[-0.01427,-0.01427,-0.01427,-0.01427], "fy":[2.50075,2.50075,2.50075,2.50075]}, - {"t":6.56933, "x":4.56302, "y":7.70252, "heading":-1.5708, "vx":-1.99947, "vy":-0.00852, "omega":0.0, "ax":-0.00041, "ay":0.14391, "alpha":0.0, "fx":[-0.00696,-0.00696,-0.00696,-0.00696], "fy":[2.44791,2.44791,2.44791,2.44791]}, - {"t":6.60871, "x":4.48429, "y":7.7023, "heading":-1.5708, "vx":-1.99949, "vy":-0.00285, "omega":0.0, "ax":0.0, "ay":0.14289, "alpha":0.0, "fx":[-0.00005,-0.00005,-0.00005,-0.00005], "fy":[2.43058,2.43058,2.43058,2.43058]}, - {"t":6.64808, "x":4.40555, "y":7.70229, "heading":-1.5708, "vx":-1.99949, "vy":0.00277, "omega":0.0, "ax":0.0004, "ay":0.14388, "alpha":0.0, "fx":[0.00686,0.00686,0.00686,0.00686], "fy":[2.44729,2.44729,2.44729,2.44729]}, - {"t":6.68746, "x":4.32682, "y":7.70251, "heading":-1.5708, "vx":-1.99947, "vy":0.00844, "omega":0.0, "ax":0.00083, "ay":0.14694, "alpha":0.0, "fx":[0.01416,0.01416,0.01416,0.01416], "fy":[2.49944,2.49944,2.49944,2.49944]}, - {"t":6.72683, "x":4.24809, "y":7.70296, "heading":-1.5708, "vx":-1.99944, "vy":0.01423, "omega":0.0, "ax":0.00131, "ay":0.15233, "alpha":0.0, "fx":[0.02231,0.02231,0.02231,0.02231], "fy":[2.5911,2.5911,2.5911,2.5911]}, - {"t":6.76621, "x":4.16936, "y":7.70364, "heading":-1.5708, "vx":-1.99939, "vy":0.02022, "omega":0.0, "ax":0.00187, "ay":0.15993, "alpha":0.0, "fx":[0.03179,0.03179,0.03179,0.03179], "fy":[2.72031,2.72031,2.72031,2.72031]}, - {"t":6.80558, "x":4.09064, "y":7.70456, "heading":-1.5708, "vx":-1.99932, "vy":0.02652, "omega":0.0, "ax":0.00235, "ay":0.15833, "alpha":0.0, "fx":[0.03991,0.03991,0.03991,0.03991], "fy":[2.69315,2.69315,2.69315,2.69315]}, - {"t":6.84496, "x":4.01192, "y":7.70573, "heading":-1.5708, "vx":-1.99922, "vy":0.03276, "omega":0.0, "ax":-0.00971, "ay":-0.08368, "alpha":0.0, "fx":[-0.16514,-0.16514,-0.16514,-0.16514], "fy":[-1.42342,-1.42342,-1.42342,-1.42342]}, - {"t":6.88434, "x":3.93319, "y":7.70695, "heading":-1.5708, "vx":-1.99961, "vy":0.02946, "omega":0.0, "ax":-9.40104, "ay":0.15053, "alpha":0.0, "fx":[-159.90904,-159.90904,-159.90904,-159.90904], "fy":[2.5605,2.5605,2.5605,2.5605]}, - {"t":6.91083, "x":3.87692, "y":7.70778, "heading":-1.5708, "vx":-2.24865, "vy":0.03345, "omega":0.0, "ax":-9.39288, "ay":0.19502, "alpha":0.0, "fx":[-159.77019,-159.77019,-159.77019,-159.77019], "fy":[3.31732,3.31732,3.31732,3.31732]}, - {"t":6.93732, "x":3.81405, "y":7.70874, "heading":-1.5708, "vx":-2.49748, "vy":0.03861, "omega":0.0, "ax":-9.38053, "ay":0.24375, "alpha":0.0, "fx":[-159.56014,-159.56014,-159.56014,-159.56014], "fy":[4.14611,4.14611,4.14611,4.14611]}, - {"t":6.96381, "x":3.7446, "y":7.70985, "heading":-1.5708, "vx":-2.74598, "vy":0.04507, "omega":0.0, "ax":-9.35974, "ay":0.29865, "alpha":0.0, "fx":[-159.20654,-159.20654,-159.20654,-159.20654], "fy":[5.07988,5.07988,5.07988,5.07988]}, - {"t":6.9903, "x":3.66857, "y":7.71115, "heading":-1.5708, "vx":-2.99393, "vy":0.05298, "omega":0.0, "ax":-9.31707, "ay":0.36408, "alpha":0.0, "fx":[-158.48061,-158.48061,-158.48061,-158.48061], "fy":[6.19294,6.19294,6.19294,6.19294]}, - {"t":7.01679, "x":3.58599, "y":7.71268, "heading":-1.5708, "vx":-3.24075, "vy":0.06263, "omega":0.0, "ax":-9.1755, "ay":0.45685, "alpha":0.0, "fx":[-156.07257,-156.07257,-156.07257,-156.07257], "fy":[7.77081,7.77081,7.77081,7.77081]}, - {"t":7.04328, "x":3.49692, "y":7.7145, "heading":-1.5708, "vx":-3.48382, "vy":0.07473, "omega":0.0, "ax":0.34819, "ay":-0.03752, "alpha":0.0, "fx":[5.92257,5.92257,5.92257,5.92257], "fy":[-0.63829,-0.63829,-0.63829,-0.63829]}, - {"t":7.06977, "x":3.40475, "y":7.71646, "heading":-1.5708, "vx":-3.47459, "vy":0.07374, "omega":0.0, "ax":9.17965, "ay":-0.65823, "alpha":0.0, "fx":[156.1433,156.1433,156.1433,156.1433], "fy":[-11.19628,-11.19628,-11.19628,-11.19628]}, - {"t":7.09626, "x":3.31593, "y":7.71819, "heading":-1.5708, "vx":-3.23141, "vy":0.0563, "omega":0.0, "ax":9.29293, "ay":-0.80745, "alpha":0.0, "fx":[158.07015,158.07015,158.07015,158.07015], "fy":[-13.73442,-13.73442,-13.73442,-13.73442]}, - {"t":7.12276, "x":3.23358, "y":7.71939, "heading":-1.5708, "vx":-2.98523, "vy":0.03491, "omega":0.0, "ax":9.29658, "ay":-1.14193, "alpha":0.0, "fx":[158.13221,158.13221,158.13221,158.13221], "fy":[-19.42382,-19.42382,-19.42382,-19.42382]}, - {"t":7.14925, "x":3.15776, "y":7.71992, "heading":-1.5708, "vx":-2.73896, "vy":0.00466, "omega":0.0, "ax":8.56477, "ay":-3.8471, "alpha":0.0, "fx":[145.68423,145.68423,145.68423,145.68423], "fy":[-65.43809,-65.43809,-65.43809,-65.43809]}, - {"t":7.17574, "x":3.08821, "y":7.71869, "heading":-1.5708, "vx":-2.51207, "vy":-0.09726, "omega":0.0, "ax":7.06709, "ay":-6.20691, "alpha":0.0, "fx":[120.20922,120.20922,120.20922,120.20922], "fy":[-105.57779,-105.57779,-105.57779,-105.57779]}, - {"t":7.20223, "x":3.02414, "y":7.71394, "heading":-1.5708, "vx":-2.32485, "vy":-0.26168, "omega":0.0, "ax":6.12687, "ay":-7.14833, "alpha":0.0, "fx":[104.21631,104.21631,104.21631,104.21631], "fy":[-121.59108,-121.59108,-121.59108,-121.59108]}, - {"t":7.22872, "x":2.96471, "y":7.7045, "heading":-1.5708, "vx":-2.16254, "vy":-0.45105, "omega":0.0, "ax":5.54812, "ay":-7.61266, "alpha":0.0, "fx":[94.37195,94.37195,94.37195,94.37195], "fy":[-129.48918,-129.48918,-129.48918,-129.48918]}, - {"t":7.25521, "x":2.90936, "y":7.68988, "heading":-1.5708, "vx":-2.01557, "vy":-0.65272, "omega":0.0, "ax":3.79004, "ay":-2.04012, "alpha":31.30207, "fx":[160.27537,6.69625,0.10329,90.79474], "fy":[3.37722,150.42859,-160.37873,-132.2348]}, - {"t":7.28522, "x":2.85058, "y":7.66937, "heading":-1.5708, "vx":-1.90182, "vy":-0.71395, "omega":0.93948, "ax":5.65894, "ay":-3.12823, "alpha":23.10463, "fx":[160.27327,130.47773,2.48665,91.79046], "fy":[2.09987,76.92653,-160.34038,-131.52723]}, - {"t":7.31524, "x":2.79605, "y":7.64653, "heading":-1.5426, "vx":-1.73197, "vy":-0.80784, "omega":1.63293, "ax":5.90891, "ay":-5.17566, "alpha":15.1535, "fx":[160.23954,138.80684,8.12923,94.85974], "fy":[1.90526,-64.61473,-160.1301,-129.3067]}, - {"t":7.34525, "x":2.74672, "y":7.61995, "heading":-1.49359, "vx":-1.55463, "vy":-0.96318, "omega":2.08773, "ax":5.37075, "ay":-6.04827, "alpha":13.32984, "fx":[160.18761,89.29176,16.48262,99.45738], "fy":[1.91764,-128.20741,-159.45664,-125.7711]}, - {"t":7.37526, "x":2.70248, "y":7.58832, "heading":-1.43093, "vx":-1.39343, "vy":-1.14471, "omega":2.48781, "ax":5.38076, "ay":-6.14158, "alpha":12.86282, "fx":[160.12436,73.51103,27.3725,105.09281], "fy":[0.67459,-139.57785,-157.9099,-121.05287]}, - {"t":7.40528, "x":2.66309, "y":7.5512, "heading":-1.35626, "vx":-1.23194, "vy":-1.32904, "omega":2.87386, "ax":5.76547, "ay":-6.03627, "alpha":11.88089, "fx":[159.97722,79.05874,41.55674,111.68325], "fy":[-3.70252,-137.35595,-154.71908,-114.92344]}, - {"t":7.43529, "x":2.62871, "y":7.50859, "heading":-1.27001, "vx":-1.0589, "vy":-1.5102, "omega":3.23045, "ax":6.40343, "ay":-5.81676, "alpha":10.01725, "fx":[159.21608,96.19129,60.94979,119.32516], "fy":[-14.40873,-126.48142,-148.04511,-106.83016]}, - {"t":7.4653, "x":2.59981, "y":7.46064, "heading":-1.17305, "vx":-0.86671, "vy":-1.68478, "omega":3.5311, "ax":7.24902, "ay":-5.42635, "alpha":6.73949, "fx":[155.52906,118.9945,90.24664,128.44481], "fy":[-36.07853,-105.74634,-132.00525,-95.37248]}, - {"t":7.49532, "x":2.57706, "y":7.40763, "heading":-1.06707, "vx":-0.64914, "vy":-1.84765, "omega":3.73337, "ax":8.27857, "ay":-4.39332, "alpha":0.61743, "fx":[143.29718,140.75978,138.2936,140.91375], "fy":[-69.92466,-74.84682,-79.42275,-74.72241]}, - {"t":7.52533, "x":2.56131, "y":7.3502, "heading":-0.95502, "vx":-0.40067, "vy":-1.97951, "omega":3.7519, "ax":8.59002, "ay":-3.18308, "alpha":-5.18996, "fx":[129.58792,148.74849,158.67535,147.44315], "fy":[-92.54252,-57.26775,-8.92562,-57.8372]}, - {"t":7.55534, "x":2.55315, "y":7.28936, "heading":-0.84241, "vx":-0.14286, "vy":-2.07504, "omega":3.59614, "ax":8.33812, "ay":-3.05666, "alpha":-7.57145, "fx":[121.55136,150.38223,156.92833,138.45402], "fy":[-102.38474,-51.98525,19.61873,-73.22009]}, - {"t":7.58536, "x":2.55262, "y":7.2257, "heading":-0.73448, "vx":0.1074, "vy":-2.16678, "omega":3.36889, "ax":7.76726, "ay":-3.34235, "alpha":-10.21602, "fx":[116.00688,151.10476,152.25263,109.11152], "fy":[-107.90123,-48.23361,37.1574,-108.43249]}, - {"t":7.61537, "x":2.55934, "y":7.15916, "heading":-0.63337, "vx":0.34052, "vy":-2.2671, "omega":3.06227, "ax":7.12713, "ay":-3.72503, "alpha":-12.36029, "fx":[113.10865,150.44875,149.47471,71.88946], "fy":[-109.56792,-46.97953,36.02198,-132.92136]}, - {"t":7.64538, "x":2.57277, "y":7.08944, "heading":-0.54146, "vx":0.55443, "vy":-2.3789, "omega":2.6913, "ax":6.72322, "ay":-3.71697, "alpha":-13.19547, "fx":[112.77454,149.03296,142.96599,52.66643], "fy":[-106.35157,-42.85911,31.63823,-135.3262]}, - {"t":7.6754, "x":2.59244, "y":7.01637, "heading":-0.46069, "vx":0.75621, "vy":-2.49045, "omega":2.29526, "ax":7.03995, "ay":-0.78916, "alpha":-7.68289, "fx":[124.33264,137.0683,114.71959,102.86955], "fy":[-53.23634,-6.00092,40.79993,-35.2564]}, - {"t":7.70541, "x":2.61831, "y":6.94127, "heading":-0.3918, "vx":0.9675, "vy":-2.51414, "omega":2.06467, "ax":-1.56318, "ay":6.53638, "alpha":17.49121, "fx":[-71.12568,-127.93023,37.04557,55.65314], "fy":[134.33193,77.68395,96.24815,136.46357]}, - {"t":7.73542, "x":2.64664, "y":6.86875, "heading":-0.32983, "vx":0.92059, "vy":-2.31796, "omega":2.58964, "ax":-1.72441, "ay":6.76212, "alpha":18.76132, "fx":[-83.38041,-140.04925,53.57889,52.52412], "fy":[132.74518,68.11179,113.8479,145.3823]}, - {"t":7.76544, "x":2.6735, "y":6.80223, "heading":-0.25211, "vx":0.86883, "vy":-2.11501, "omega":3.15273, "ax":-1.45879, "ay":7.01196, "alpha":19.74685, "fx":[-84.96842,-140.79887,76.73276,49.78045], "fy":[133.35262,70.17201,124.63573,148.92507]}, - {"t":7.79545, "x":2.69891, "y":6.74191, "heading":-0.15748, "vx":0.82505, "vy":-1.90456, "omega":3.7454, "ax":-1.28801, "ay":7.19781, "alpha":20.34955, "fx":[-83.72082,-138.15084,86.72262,47.51421], "fy":[134.90261,76.68803,127.20909,150.93094]}, - {"t":7.82546, "x":2.7231, "y":6.68799, "heading":-0.04507, "vx":0.78639, "vy":-1.68853, "omega":4.35616, "ax":-3.49264, "ay":8.65295, "alpha":-1.80274, "fx":[-53.54168,-48.95928,-64.90813,-70.2259], "fy":[149.59422,151.28547,145.23517,142.62176]}, - {"t":7.85548, "x":2.74513, "y":6.64121, "heading":0.08567, "vx":0.68157, "vy":-1.42882, "omega":4.30205, "ax":-3.28932, "ay":8.08512, "alpha":-11.95505, "fx":[-1.20949,-6.54085,-92.54412,-123.50721], "fy":[159.14455,159.60215,130.32977,101.02561]}, - {"t":7.88549, "x":2.7641, "y":6.60197, "heading":0.21479, "vx":0.58284, "vy":-1.18616, "omega":3.94324, "ax":-3.04996, "ay":7.48826, "alpha":-17.38495, "fx":[38.03141,1.72493,-105.87428,-141.3977], "fy":[154.93607,160.0131,120.06048,74.48297]}, - {"t":7.9155, "x":2.78022, "y":6.56974, "heading":0.33314, "vx":0.4913, "vy":-0.96141, "omega":3.42146, "ax":-3.0457, "ay":7.00399, "alpha":-20.4189, "fx":[57.0884,0.80411,-115.2395,-149.87861], "fy":[149.28064,160.16229,111.26022,55.84043]}, - {"t":7.94552, "x":2.79359, "y":6.54404, "heading":0.43583, "vx":0.39989, "vy":-0.7512, "omega":2.80862, "ax":-3.15569, "ay":6.63469, "alpha":-22.12541, "fx":[64.98548,-2.82185,-122.31812,-154.55536], "fy":[146.2153,160.22173,103.52694,41.45243]}, - {"t":7.97553, "x":2.80418, "y":6.52448, "heading":0.52013, "vx":0.30518, "vy":-0.55207, "omega":2.14456, "ax":-3.28918, "ay":6.34671, "alpha":-23.15972, "fx":[68.01031,-6.84718,-127.68833,-157.26698], "fy":[144.96002,160.15303,96.89639,29.81362]}, - {"t":8.00554, "x":2.81185, "y":6.51077, "heading":0.58449, "vx":0.20646, "vy":-0.36158, "omega":1.44946, "ax":-3.40278, "ay":6.11623, "alpha":-23.86683, "fx":[69.23471,-10.26404,-131.66374,-158.82826], "fy":[144.46274,160.00676,91.4758,20.19566]}, - {"t":8.03556, "x":2.81652, "y":6.50267, "heading":0.628, "vx":0.10433, "vy":-0.17802, "omega":0.73314, "ax":-3.47618, "ay":5.93123, "alpha":-24.42717, "fx":[70.17588,-12.56926,-134.44845,-159.67338], "fy":[144.06485,159.86855,87.37575,12.24528]}, - {"t":8.06557, "x":2.81808, "y":6.5, "heading":0.65, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0,0.0,0.0,0.0]}], + {"t":0.0, "x":4.44125, "y":7.69346, "heading":-1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":9.36814, "ay":-1.05823, "alpha":0.00001, "fx":[159.34931,159.34931,159.3493,159.3493], "fy":[-18.00011,-18.00012,-18.00023,-18.00022]}, + {"t":0.04222, "x":4.4496, "y":7.69252, "heading":-1.5708, "vx":0.39554, "vy":-0.04468, "omega":0.0, "ax":9.36783, "ay":-1.04859, "alpha":0.00001, "fx":[159.34414,159.34414,159.34412,159.34413], "fy":[-17.83609,-17.8361,-17.8362,-17.8362]}, + {"t":0.08444, "x":4.47465, "y":7.68969, "heading":-1.5708, "vx":0.79106, "vy":-0.08895, "omega":0.0, "ax":9.36697, "ay":-1.03972, "alpha":0.00001, "fx":[159.32951,159.32951,159.3295,159.3295], "fy":[-17.68523,-17.68523,-17.68533,-17.68533]}, + {"t":0.12666, "x":4.5164, "y":7.68501, "heading":-1.5708, "vx":1.18655, "vy":-0.13285, "omega":0.0, "ax":9.36529, "ay":-1.03158, "alpha":0.00001, "fx":[159.30089,159.30089,159.30088,159.30088], "fy":[-17.54682,-17.54683,-17.54692,-17.54691]}, + {"t":0.16889, "x":4.57485, "y":7.67848, "heading":-1.5708, "vx":1.58196, "vy":-0.17641, "omega":0.0, "ax":9.36226, "ay":-1.024, "alpha":0.00001, "fx":[159.24937,159.24937,159.24936,159.24936], "fy":[-17.41794,-17.41795,-17.41804,-17.41803]}, + {"t":0.21111, "x":4.64998, "y":7.67012, "heading":-1.5708, "vx":1.97725, "vy":-0.21964, "omega":0.0, "ax":9.35666, "ay":-1.01662, "alpha":0.00001, "fx":[159.15418,159.15418,159.15417,159.15417], "fy":[-17.29245,-17.29246,-17.29254,-17.29254]}, + {"t":0.25333, "x":4.74181, "y":7.65994, "heading":-1.5708, "vx":2.3723, "vy":-0.26256, "omega":0.0, "ax":9.34487, "ay":-1.00855, "alpha":0.00001, "fx":[158.95349,158.95349,158.95348,158.95348], "fy":[-17.15516,-17.15517,-17.15524,-17.15524]}, + {"t":0.29555, "x":4.8503, "y":7.64796, "heading":-1.5708, "vx":2.76685, "vy":-0.30515, "omega":0.0, "ax":9.30927, "ay":-0.99588, "alpha":0.00001, "fx":[158.34799,158.34799,158.34799,158.34799], "fy":[-16.93961,-16.93961,-16.93967,-16.93966]}, + {"t":0.33777, "x":4.97542, "y":7.63419, "heading":-1.5708, "vx":3.15991, "vy":-0.34719, "omega":0.0, "ax":7.76134, "ay":-0.67196, "alpha":-0.00007, "fx":[132.01818,132.01827,132.01832,132.01823], "fy":[-11.43011,-11.43008,-11.42942,-11.42946]}, + {"t":0.37999, "x":5.11575, "y":7.61893, "heading":-1.5708, "vx":3.4876, "vy":-0.37556, "omega":0.0, "ax":0.01561, "ay":0.11339, "alpha":0.0, "fx":[0.26558,0.26558,0.26558,0.26558], "fy":[1.92868,1.92868,1.92868,1.92868]}, + {"t":0.42221, "x":5.26301, "y":7.60317, "heading":-1.5708, "vx":3.48826, "vy":-0.37078, "omega":0.0, "ax":0.00664, "ay":0.06275, "alpha":0.0, "fx":[0.11293,0.11293,0.11293,0.11293], "fy":[1.0673,1.0673,1.0673,1.0673]}, + {"t":0.46444, "x":5.4103, "y":7.58757, "heading":-1.5708, "vx":3.48854, "vy":-0.36813, "omega":0.0, "ax":0.00549, "ay":0.05223, "alpha":0.0, "fx":[0.09336,0.09336,0.09336,0.09336], "fy":[0.88845,0.88845,0.88845,0.88845]}, + {"t":0.50666, "x":5.5576, "y":7.57208, "heading":-1.5708, "vx":3.48877, "vy":-0.36592, "omega":0.0, "ax":0.00494, "ay":0.04725, "alpha":0.0, "fx":[0.08397,0.08397,0.08397,0.08397], "fy":[0.80376,0.80376,0.80376,0.80376]}, + {"t":0.54888, "x":5.7049, "y":7.55667, "heading":-1.5708, "vx":3.48898, "vy":-0.36393, "omega":0.0, "ax":0.00453, "ay":0.04357, "alpha":0.0, "fx":[0.07701,0.07701,0.07701,0.07701], "fy":[0.74108,0.74108,0.74108,0.74108]}, + {"t":0.5911, "x":5.85222, "y":7.54134, "heading":-1.5708, "vx":3.48917, "vy":-0.36209, "omega":0.0, "ax":0.00419, "ay":0.04053, "alpha":0.0, "fx":[0.07128,0.07128,0.07128,0.07128], "fy":[0.68935,0.68935,0.68935,0.68935]}, + {"t":0.63332, "x":5.99954, "y":7.52609, "heading":-1.5708, "vx":3.48935, "vy":-0.36038, "omega":0.0, "ax":0.00391, "ay":0.03794, "alpha":0.0, "fx":[0.06642,0.06642,0.06642,0.06642], "fy":[0.64537,0.64537,0.64537,0.64537]}, + {"t":0.67554, "x":6.14687, "y":7.51091, "heading":-1.5708, "vx":3.48951, "vy":-0.35878, "omega":0.0, "ax":0.00366, "ay":0.03571, "alpha":0.0, "fx":[0.06224,0.06224,0.06224,0.06224], "fy":[0.60737,0.60737,0.60737,0.60737]}, + {"t":0.71776, "x":6.2942, "y":7.49579, "heading":-1.5708, "vx":3.48967, "vy":-0.35727, "omega":0.0, "ax":0.00344, "ay":0.03375, "alpha":0.0, "fx":[0.05859,0.05859,0.05859,0.05859], "fy":[0.57414,0.57414,0.57414,0.57414]}, + {"t":0.75999, "x":6.44154, "y":7.48074, "heading":-1.5708, "vx":3.48981, "vy":-0.35584, "omega":0.0, "ax":0.00326, "ay":0.03203, "alpha":0.0, "fx":[0.05538,0.05538,0.05538,0.05538], "fy":[0.54476,0.54476,0.54476,0.54476]}, + {"t":0.80221, "x":6.58889, "y":7.46574, "heading":-1.5708, "vx":3.48995, "vy":-0.35449, "omega":0.0, "ax":0.00309, "ay":0.03047, "alpha":0.0, "fx":[0.05248,0.05248,0.05248,0.05248], "fy":[0.51821,0.51821,0.51821,0.51821]}, + {"t":0.84443, "x":6.73624, "y":7.4508, "heading":-1.5708, "vx":3.49008, "vy":-0.3532, "omega":0.0, "ax":0.00291, "ay":0.02887, "alpha":0.0, "fx":[0.04955,0.04955,0.04955,0.04955], "fy":[0.49104,0.49104,0.49104,0.49104]}, + {"t":0.88665, "x":6.8836, "y":7.43592, "heading":-1.5708, "vx":3.4902, "vy":-0.35199, "omega":0.0, "ax":0.00259, "ay":0.02571, "alpha":0.0, "fx":[0.04397,0.04397,0.04397,0.04397], "fy":[0.43724,0.43724,0.43724,0.43724]}, + {"t":0.92887, "x":7.03097, "y":7.42108, "heading":-1.5708, "vx":3.49031, "vy":-0.3509, "omega":0.0, "ax":0.00078, "ay":0.00776, "alpha":0.0, "fx":[0.01322,0.01322,0.01322,0.01322], "fy":[0.13206,0.13206,0.13206,0.13206]}, + {"t":0.97109, "x":7.17833, "y":7.40627, "heading":-1.5708, "vx":3.49035, "vy":-0.35057, "omega":0.0, "ax":-0.01424, "ay":-0.14057, "alpha":0.0, "fx":[-0.24229,-0.24229,-0.24229,-0.24229], "fy":[-2.39104,-2.39104,-2.39104,-2.39104]}, + {"t":1.01331, "x":7.32569, "y":7.39134, "heading":-1.5708, "vx":3.48974, "vy":-0.35651, "omega":0.0, "ax":-0.15505, "ay":-1.39946, "alpha":0.0, "fx":[-2.63729,-2.63729,-2.63729,-2.63729], "fy":[-23.80448,-23.80448,-23.80448,-23.80448]}, + {"t":1.05554, "x":7.47289, "y":7.37504, "heading":-1.5708, "vx":3.4832, "vy":-0.41559, "omega":0.0, "ax":-1.03148, "ay":-6.46229, "alpha":0.0, "fx":[-17.54513,-17.54513,-17.54513,-17.54513], "fy":[-109.92178,-109.92178,-109.92178,-109.92178]}, + {"t":1.09776, "x":7.61904, "y":7.35174, "heading":-1.5708, "vx":3.43965, "vy":-0.68844, "omega":0.0, "ax":-2.1884, "ay":-8.53761, "alpha":0.0, "fx":[-37.22404,-37.22404,-37.22404,-37.22404], "fy":[-145.22231,-145.22231,-145.22231,-145.22231]}, + {"t":1.13998, "x":7.76231, "y":7.31506, "heading":-1.5708, "vx":3.34725, "vy":-1.04891, "omega":0.0, "ax":-3.25084, "ay":-8.60693, "alpha":0.0, "fx":[-55.29592,-55.29593,-55.29593,-55.29592], "fy":[-146.40138,-146.40138,-146.40138,-146.40138]}, + {"t":1.1822, "x":7.90074, "y":7.2631, "heading":-1.5708, "vx":3.21, "vy":-1.41231, "omega":0.0, "ax":-4.29248, "ay":-8.15046, "alpha":0.0, "fx":[-73.01388,-73.01392,-73.01389,-73.01385], "fy":[-138.63705,-138.63703,-138.63704,-138.63706]}, + {"t":1.2134, "x":7.99881, "y":7.21507, "heading":-1.5708, "vx":3.07607, "vy":-1.66661, "omega":0.0, "ax":-8.74171, "ay":-3.10215, "alpha":0.00003, "fx":[-148.69404,-148.69406,-148.69398,-148.69396], "fy":[-52.76658,-52.76653,-52.76676,-52.76681]}, + {"t":1.2446, "x":8.09053, "y":7.16156, "heading":-1.5708, "vx":2.80332, "vy":-1.76339, "omega":0.0, "ax":-9.32582, "ay":0.69294, "alpha":0.00001, "fx":[-158.6295,-158.6295,-158.62951,-158.62951], "fy":[11.78672,11.78672,11.78663,11.78664]}, + {"t":1.2758, "x":8.17345, "y":7.10688, "heading":-1.5708, "vx":2.51235, "vy":-1.74177, "omega":0.0, "ax":-9.16435, "ay":2.00668, "alpha":0.0, "fx":[-155.8829,-155.8829,-155.88291,-155.88291], "fy":[34.13308,34.13308,34.13306,34.13306]}, + {"t":1.307, "x":8.24738, "y":7.05351, "heading":-1.5708, "vx":2.22642, "vy":-1.67917, "omega":0.0, "ax":-9.02182, "ay":2.62615, "alpha":0.0, "fx":[-153.4585,-153.4585,-153.45849,-153.45849], "fy":[44.67005,44.67005,44.67007,44.67007]}, + {"t":1.3382, "x":8.31245, "y":7.0024, "heading":-1.5708, "vx":1.94494, "vy":-1.59723, "omega":0.0, "ax":-8.91996, "ay":2.98118, "alpha":0.0, "fx":[-151.72596,-151.72596,-151.72595,-151.72595], "fy":[50.70901,50.70901,50.70904,50.70904]}, + {"t":1.3694, "x":8.36879, "y":6.95402, "heading":-1.5708, "vx":1.66663, "vy":-1.50421, "omega":0.0, "ax":-8.84621, "ay":3.21008, "alpha":0.0, "fx":[-150.47154,-150.47154,-150.47152,-150.47153], "fy":[54.60247,54.60248,54.60252,54.60251]}, + {"t":1.4006, "x":8.41649, "y":6.90865, "heading":-1.5708, "vx":1.39063, "vy":-1.40406, "omega":0.0, "ax":-8.79101, "ay":3.36951, "alpha":-0.00001, "fx":[-149.5325,-149.53249,-149.53247,-149.53248], "fy":[57.31438,57.31439,57.31444,57.31443]}, + {"t":1.4318, "x":8.4556, "y":6.86648, "heading":-1.5708, "vx":1.11635, "vy":-1.29893, "omega":0.0, "ax":-8.74836, "ay":3.48676, "alpha":-0.00001, "fx":[-148.80705,-148.80705,-148.80703,-148.80703], "fy":[59.30874,59.30875,59.3088,59.30879]}, + {"t":1.463, "x":8.48617, "y":6.82765, "heading":-1.5708, "vx":0.84339, "vy":-1.19014, "omega":0.0, "ax":-8.71452, "ay":3.57652, "alpha":-0.00001, "fx":[-148.23146,-148.23145,-148.23143,-148.23143], "fy":[60.83547,60.83548,60.83554,60.83552]}, + {"t":1.4942, "x":8.50824, "y":6.79226, "heading":-1.5708, "vx":0.5715, "vy":-1.07855, "omega":0.0, "ax":-8.68706, "ay":3.64738, "alpha":-0.00001, "fx":[-147.76449,-147.76449,-147.76446,-147.76447], "fy":[62.0408,62.04082,62.04088,62.04086]}, + {"t":1.5254, "x":8.52184, "y":6.76038, "heading":-1.5708, "vx":0.30046, "vy":-0.96475, "omega":0.0, "ax":-8.66438, "ay":3.7047, "alpha":-0.00001, "fx":[-147.37857,-147.37856,-147.37854,-147.37855], "fy":[63.01589,63.01591,63.01597,63.01595]}, + {"t":1.5566, "x":8.527, "y":6.73208, "heading":-1.5708, "vx":0.03013, "vy":-0.84916, "omega":0.0, "ax":-1.22219, "ay":-0.00289, "alpha":0.0, "fx":[-20.78903,-20.78903,-20.78903,-20.78903], "fy":[-0.04917,-0.04917,-0.04917,-0.04917]}, + {"t":1.60562, "x":8.52701, "y":6.69046, "heading":-1.5708, "vx":-0.02977, "vy":-0.84931, "omega":0.0, "ax":1.20065, "ay":-0.0005, "alpha":0.0, "fx":[20.42264,20.42264,20.42264,20.42264], "fy":[-0.00845,-0.00845,-0.00845,-0.00845]}, + {"t":1.65463, "x":8.52699, "y":6.64884, "heading":-1.5708, "vx":0.02907, "vy":-0.84933, "omega":0.0, "ax":-1.172, "ay":-0.00048, "alpha":0.0, "fx":[-19.93533,-19.93533,-19.93533,-19.93533], "fy":[-0.00822,-0.00822,-0.00822,-0.00822]}, + {"t":1.70364, "x":8.52701, "y":6.60721, "heading":-1.5708, "vx":-0.02837, "vy":-0.84935, "omega":0.0, "ax":1.14346, "ay":-0.00047, "alpha":0.0, "fx":[19.44987,19.44987,19.44987,19.44987], "fy":[-0.00799,-0.00799,-0.00799,-0.00799]}, + {"t":1.75265, "x":8.52699, "y":6.56558, "heading":-1.5708, "vx":0.02767, "vy":-0.84938, "omega":0.0, "ax":-1.11503, "ay":-0.00046, "alpha":0.0, "fx":[-18.96629,-18.96629,-18.96629,-18.96629], "fy":[-0.00776,-0.00776,-0.00776,-0.00776]}, + {"t":1.80166, "x":8.52701, "y":6.52395, "heading":-1.5708, "vx":-0.02698, "vy":-0.8494, "omega":0.0, "ax":1.08671, "ay":-0.00044, "alpha":0.0, "fx":[18.48463,18.48463,18.48463,18.48463], "fy":[-0.00753,-0.00753,-0.00753,-0.00753]}, + {"t":1.85067, "x":8.52699, "y":6.48232, "heading":-1.5708, "vx":0.02628, "vy":-0.84942, "omega":0.0, "ax":-1.05851, "ay":-0.00043, "alpha":0.0, "fx":[-18.00494,-18.00494,-18.00494,-18.00494], "fy":[-0.00731,-0.00731,-0.00731,-0.00731]}, + {"t":1.89969, "x":8.52701, "y":6.44069, "heading":-1.5708, "vx":-0.02559, "vy":-0.84944, "omega":0.0, "ax":1.03043, "ay":-0.00042, "alpha":0.0, "fx":[17.52724,17.52724,17.52724,17.52724], "fy":[-0.00708,-0.00708,-0.00708,-0.00708]}, + {"t":1.9487, "x":8.52699, "y":6.39905, "heading":-1.5708, "vx":0.02491, "vy":-0.84946, "omega":0.0, "ax":-1.00246, "ay":-0.0004, "alpha":0.0, "fx":[-17.05158,-17.05158,-17.05158,-17.05158], "fy":[-0.00686,-0.00686,-0.00686,-0.00686]}, + {"t":1.99771, "x":8.52701, "y":6.35742, "heading":-1.5708, "vx":-0.02422, "vy":-0.84948, "omega":0.0, "ax":0.97462, "ay":-0.00039, "alpha":0.0, "fx":[16.57799,16.57799,16.57799,16.57799], "fy":[-0.00664,-0.00664,-0.00664,-0.00664]}, + {"t":2.04672, "x":8.52699, "y":6.31578, "heading":-1.5708, "vx":0.02354, "vy":-0.8495, "omega":0.0, "ax":-0.9469, "ay":-0.00038, "alpha":0.0, "fx":[-16.1065,-16.1065,-16.1065,-16.1065], "fy":[-0.00642,-0.00642,-0.00642,-0.00642]}, + {"t":2.09573, "x":8.52701, "y":6.27415, "heading":-1.5708, "vx":-0.02287, "vy":-0.84952, "omega":0.0, "ax":0.91931, "ay":-0.00036, "alpha":0.0, "fx":[15.63715,15.63715,15.63715,15.63715], "fy":[-0.00621,-0.00621,-0.00621,-0.00621]}, + {"t":2.14474, "x":8.52699, "y":6.23251, "heading":-1.5708, "vx":0.02219, "vy":-0.84954, "omega":0.0, "ax":-0.89184, "ay":-0.00035, "alpha":0.0, "fx":[-15.16997,-15.16997,-15.16997,-15.16997], "fy":[-0.00599,-0.00599,-0.00599,-0.00599]}, + {"t":2.19376, "x":8.52701, "y":6.19087, "heading":-1.5708, "vx":-0.02152, "vy":-0.84956, "omega":0.0, "ax":0.8645, "ay":-0.00034, "alpha":0.0, "fx":[14.70498,14.70498,14.70498,14.70498], "fy":[-0.00578,-0.00578,-0.00578,-0.00578]}, + {"t":2.24277, "x":8.52699, "y":6.14924, "heading":-1.5708, "vx":0.02085, "vy":-0.84957, "omega":0.0, "ax":-0.8373, "ay":-0.00033, "alpha":0.0, "fx":[-14.24222,-14.24222,-14.24222,-14.24222], "fy":[-0.00557,-0.00557,-0.00557,-0.00557]}, + {"t":2.29178, "x":8.52701, "y":6.1076, "heading":-1.5708, "vx":-0.02019, "vy":-0.84959, "omega":0.0, "ax":0.81023, "ay":-0.00032, "alpha":0.0, "fx":[13.7817,13.7817,13.7817,13.7817], "fy":[-0.00537,-0.00537,-0.00537,-0.00537]}, + {"t":2.34079, "x":8.52699, "y":6.06596, "heading":-1.5708, "vx":0.01952, "vy":-0.8496, "omega":0.0, "ax":-0.78328, "ay":-0.0003, "alpha":0.0, "fx":[-13.32345,-13.32345,-13.32345,-13.32345], "fy":[-0.00516,-0.00516,-0.00516,-0.00516]}, + {"t":2.3898, "x":8.52701, "y":6.02432, "heading":-1.5708, "vx":-0.01887, "vy":-0.84962, "omega":0.0, "ax":0.75648, "ay":-0.00029, "alpha":0.0, "fx":[12.86748,12.86748,12.86748,12.86748], "fy":[-0.00496,-0.00496,-0.00496,-0.00496]}, + {"t":2.43881, "x":8.52699, "y":5.98267, "heading":-1.5708, "vx":0.01821, "vy":-0.84963, "omega":0.0, "ax":-0.72981, "ay":-0.00028, "alpha":0.0, "fx":[-12.41381,-12.41381,-12.41381,-12.41381], "fy":[-0.00476,-0.00476,-0.00476,-0.00476]}, + {"t":2.48782, "x":8.52701, "y":5.94103, "heading":-1.5708, "vx":-0.01756, "vy":-0.84965, "omega":0.0, "ax":0.70327, "ay":-0.00027, "alpha":0.0, "fx":[11.96245,11.96245,11.96245,11.96245], "fy":[-0.00456,-0.00456,-0.00456,-0.00456]}, + {"t":2.53684, "x":8.52699, "y":5.89939, "heading":-1.5708, "vx":0.01691, "vy":-0.84966, "omega":0.0, "ax":-0.67687, "ay":-0.00026, "alpha":0.0, "fx":[-11.5134,-11.5134,-11.5134,-11.5134], "fy":[-0.00437,-0.00437,-0.00437,-0.00437]}, + {"t":2.58585, "x":8.52701, "y":5.85775, "heading":-1.5708, "vx":-0.01626, "vy":-0.84967, "omega":0.0, "ax":0.65061, "ay":-0.00025, "alpha":0.0, "fx":[11.06666,11.06666,11.06666,11.06666], "fy":[-0.00418,-0.00418,-0.00418,-0.00418]}, + {"t":2.63486, "x":8.52699, "y":5.8161, "heading":-1.5708, "vx":0.01562, "vy":-0.84968, "omega":0.0, "ax":-0.62448, "ay":-0.00023, "alpha":0.0, "fx":[-10.62224,-10.62224,-10.62224,-10.62224], "fy":[-0.00399,-0.00399,-0.00399,-0.00399]}, + {"t":2.68387, "x":8.52701, "y":5.77446, "heading":-1.5708, "vx":-0.01498, "vy":-0.8497, "omega":0.0, "ax":0.59849, "ay":-0.00022, "alpha":0.0, "fx":[10.18012,10.18012,10.18012,10.18012], "fy":[-0.0038,-0.0038,-0.0038,-0.0038]}, + {"t":2.73288, "x":8.52699, "y":5.73281, "heading":-1.5708, "vx":0.01435, "vy":-0.84971, "omega":0.0, "ax":-0.57263, "ay":-0.00021, "alpha":0.0, "fx":[-9.74029,-9.74029,-9.74029,-9.74029], "fy":[-0.00362,-0.00362,-0.00362,-0.00362]}, + {"t":2.78189, "x":8.52701, "y":5.69117, "heading":-1.5708, "vx":-0.01372, "vy":-0.84972, "omega":0.0, "ax":0.54691, "ay":-0.0002, "alpha":0.0, "fx":[9.30273,9.30273,9.30273,9.30273], "fy":[-0.00344,-0.00344,-0.00344,-0.00344]}, + {"t":2.83091, "x":8.52699, "y":5.64952, "heading":-1.5708, "vx":0.01309, "vy":-0.84973, "omega":0.0, "ax":-0.52131, "ay":-0.00019, "alpha":0.0, "fx":[-8.8674,-8.8674,-8.8674,-8.8674], "fy":[-0.00326,-0.00326,-0.00326,-0.00326]}, + {"t":2.87992, "x":8.52701, "y":5.60787, "heading":-1.5708, "vx":-0.01246, "vy":-0.84974, "omega":0.0, "ax":0.49585, "ay":-0.00018, "alpha":0.0, "fx":[8.43429,8.43429,8.43429,8.43429], "fy":[-0.00309,-0.00309,-0.00309,-0.00309]}, + {"t":2.92893, "x":8.52699, "y":5.56623, "heading":-1.5708, "vx":0.01184, "vy":-0.84975, "omega":0.0, "ax":-0.47052, "ay":-0.00017, "alpha":0.0, "fx":[-8.00334,-8.00334,-8.00334,-8.00334], "fy":[-0.00292,-0.00292,-0.00292,-0.00292]}, + {"t":2.97794, "x":8.52701, "y":5.52458, "heading":-1.5708, "vx":-0.01122, "vy":-0.84975, "omega":0.0, "ax":0.44531, "ay":-0.00016, "alpha":0.0, "fx":[7.57452,7.57452,7.57452,7.57452], "fy":[-0.00275,-0.00275,-0.00275,-0.00275]}, + {"t":3.02695, "x":8.52699, "y":5.48293, "heading":-1.5708, "vx":0.0106, "vy":-0.84976, "omega":0.0, "ax":-0.42022, "ay":-0.00015, "alpha":0.0, "fx":[-7.14777,-7.14777,-7.14777,-7.14777], "fy":[-0.00258,-0.00258,-0.00258,-0.00258]}, + {"t":3.07596, "x":8.52701, "y":5.44128, "heading":-1.5708, "vx":-0.00999, "vy":-0.84977, "omega":0.0, "ax":0.39525, "ay":-0.00014, "alpha":0.0, "fx":[6.72303,6.72303,6.72303,6.72303], "fy":[-0.00241,-0.00241,-0.00241,-0.00241]}, + {"t":3.12498, "x":8.52699, "y":5.39963, "heading":-1.5708, "vx":0.00938, "vy":-0.84978, "omega":0.0, "ax":-0.37039, "ay":-0.00013, "alpha":0.0, "fx":[-6.30024,-6.30024,-6.30024,-6.30024], "fy":[-0.00225,-0.00225,-0.00225,-0.00225]}, + {"t":3.17399, "x":8.52701, "y":5.35798, "heading":-1.5708, "vx":-0.00877, "vy":-0.84978, "omega":0.0, "ax":0.34564, "ay":-0.00012, "alpha":0.0, "fx":[5.87931,5.87931,5.87931,5.87931], "fy":[-0.00209,-0.00209,-0.00209,-0.00209]}, + {"t":3.223, "x":8.52699, "y":5.31633, "heading":-1.5708, "vx":0.00817, "vy":-0.84979, "omega":0.0, "ax":-0.321, "ay":-0.00011, "alpha":0.0, "fx":[-5.46017,-5.46017,-5.46017,-5.46017], "fy":[-0.00194,-0.00194,-0.00194,-0.00194]}, + {"t":3.27201, "x":8.52701, "y":5.27469, "heading":-1.5708, "vx":-0.00757, "vy":-0.84979, "omega":0.0, "ax":0.29646, "ay":-0.0001, "alpha":0.0, "fx":[5.04273,5.04273,5.04273,5.04273], "fy":[-0.00178,-0.00178,-0.00178,-0.00178]}, + {"t":3.32102, "x":8.52699, "y":5.23304, "heading":-1.5708, "vx":0.00696, "vy":-0.8498, "omega":0.0, "ax":-0.27201, "ay":-0.0001, "alpha":0.0, "fx":[-4.62689,-4.62689,-4.62689,-4.62689], "fy":[-0.00163,-0.00163,-0.00163,-0.00163]}, + {"t":3.37003, "x":8.52701, "y":5.19139, "heading":-1.5708, "vx":-0.00637, "vy":-0.8498, "omega":0.0, "ax":0.24766, "ay":-0.00009, "alpha":0.0, "fx":[4.21255,4.21255,4.21255,4.21255], "fy":[-0.00148,-0.00148,-0.00148,-0.00148]}, + {"t":3.41905, "x":8.52699, "y":5.14973, "heading":-1.5708, "vx":0.00577, "vy":-0.84981, "omega":0.0, "ax":-0.22338, "ay":-0.00008, "alpha":0.0, "fx":[-3.7996,-3.7996,-3.7996,-3.7996], "fy":[-0.00133,-0.00133,-0.00133,-0.00133]}, + {"t":3.46806, "x":8.52701, "y":5.10808, "heading":-1.5708, "vx":-0.00518, "vy":-0.84981, "omega":0.0, "ax":0.19918, "ay":-0.00007, "alpha":0.0, "fx":[3.38793,3.38793,3.38793,3.38793], "fy":[-0.00118,-0.00118,-0.00118,-0.00118]}, + {"t":3.51707, "x":8.52699, "y":5.06643, "heading":-1.5708, "vx":0.00458, "vy":-0.84982, "omega":0.0, "ax":-0.17504, "ay":-0.00006, "alpha":0.0, "fx":[-2.97741,-2.97741,-2.97741,-2.97741], "fy":[-0.00103,-0.00103,-0.00103,-0.00103]}, + {"t":3.56608, "x":8.52701, "y":5.02478, "heading":-1.5708, "vx":-0.00399, "vy":-0.84982, "omega":0.0, "ax":0.15097, "ay":-0.00005, "alpha":0.0, "fx":[2.56792,2.56792,2.56792,2.56792], "fy":[-0.00089,-0.00089,-0.00089,-0.00089]}, + {"t":3.61509, "x":8.52699, "y":4.98313, "heading":-1.5708, "vx":0.0034, "vy":-0.84982, "omega":0.0, "ax":-0.12695, "ay":-0.00004, "alpha":0.0, "fx":[-2.15933,-2.15933,-2.15933,-2.15933], "fy":[-0.00075,-0.00075,-0.00075,-0.00075]}, + {"t":3.6641, "x":8.52701, "y":4.94148, "heading":-1.5708, "vx":-0.00282, "vy":-0.84982, "omega":0.0, "ax":0.10297, "ay":-0.00004, "alpha":0.0, "fx":[1.75151,1.75151,1.75151,1.75151], "fy":[-0.0006,-0.0006,-0.0006,-0.0006]}, + {"t":3.71311, "x":8.52699, "y":4.89983, "heading":-1.5708, "vx":0.00223, "vy":-0.84982, "omega":0.0, "ax":-0.07903, "ay":-0.00003, "alpha":0.0, "fx":[-1.34431,-1.34431,-1.34431,-1.34431], "fy":[-0.00046,-0.00046,-0.00046,-0.00046]}, + {"t":3.76213, "x":8.52701, "y":4.85818, "heading":-1.5708, "vx":-0.00164, "vy":-0.84983, "omega":0.0, "ax":0.05512, "ay":-0.00002, "alpha":0.0, "fx":[0.93759,0.93759,0.93759,0.93759], "fy":[-0.00032,-0.00032,-0.00032,-0.00032]}, + {"t":3.81114, "x":8.52699, "y":4.81653, "heading":-1.5708, "vx":0.00106, "vy":-0.84983, "omega":0.0, "ax":-0.03123, "ay":-0.00001, "alpha":0.0, "fx":[-0.53121,-0.53121,-0.53121,-0.53121], "fy":[-0.00018,-0.00018,-0.00018,-0.00018]}, + {"t":3.86015, "x":8.52701, "y":4.77488, "heading":-1.5708, "vx":-0.00047, "vy":-0.84983, "omega":0.0, "ax":0.00735, "ay":0.0, "alpha":0.0, "fx":[0.12502,0.12502,0.12502,0.12502], "fy":[-0.00004,-0.00004,-0.00004,-0.00004]}, + {"t":3.90916, "x":8.52699, "y":4.73322, "heading":-1.5708, "vx":-0.00011, "vy":-0.84983, "omega":0.0, "ax":0.01653, "ay":0.00001, "alpha":0.0, "fx":[0.28112,0.28112,0.28112,0.28112], "fy":[0.0001,0.0001,0.0001,0.0001]}, + {"t":3.95817, "x":8.52701, "y":4.69157, "heading":-1.5708, "vx":0.0007, "vy":-0.84983, "omega":0.0, "ax":-0.04041, "ay":0.00001, "alpha":0.0, "fx":[-0.68737,-0.68737,-0.68737,-0.68737], "fy":[0.00024,0.00024,0.00024,0.00024]}, + {"t":4.00718, "x":8.52699, "y":4.64992, "heading":-1.5708, "vx":-0.00128, "vy":-0.84983, "omega":0.0, "ax":0.06431, "ay":0.00002, "alpha":0.0, "fx":[1.09386,1.09386,1.09386,1.09386], "fy":[0.00038,0.00038,0.00038,0.00038]}, + {"t":4.0562, "x":8.52701, "y":4.60827, "heading":-1.5708, "vx":0.00187, "vy":-0.84983, "omega":0.0, "ax":-0.08823, "ay":0.00003, "alpha":0.0, "fx":[-1.50075,-1.50075,-1.50075,-1.50075], "fy":[0.00052,0.00052,0.00052,0.00052]}, + {"t":4.10521, "x":8.52699, "y":4.56662, "heading":-1.5708, "vx":-0.00246, "vy":-0.84982, "omega":0.0, "ax":0.11218, "ay":0.00004, "alpha":0.0, "fx":[1.90817,1.90817,1.90817,1.90817], "fy":[0.00066,0.00066,0.00066,0.00066]}, + {"t":4.15422, "x":8.52701, "y":4.52497, "heading":-1.5708, "vx":0.00304, "vy":-0.84982, "omega":0.0, "ax":-0.13617, "ay":0.00005, "alpha":0.0, "fx":[-2.31628,-2.31628,-2.31628,-2.31628], "fy":[0.0008,0.0008,0.0008,0.0008]}, + {"t":4.20323, "x":8.52699, "y":4.48332, "heading":-1.5708, "vx":-0.00363, "vy":-0.84982, "omega":0.0, "ax":0.16021, "ay":0.00006, "alpha":0.0, "fx":[2.72519,2.72519,2.72519,2.72519], "fy":[0.00095,0.00095,0.00095,0.00095]}, + {"t":4.25224, "x":8.52701, "y":4.44167, "heading":-1.5708, "vx":0.00422, "vy":-0.84982, "omega":0.0, "ax":-0.18431, "ay":0.00006, "alpha":0.0, "fx":[-3.13506,-3.13506,-3.13506,-3.13506], "fy":[0.00109,0.00109,0.00109,0.00109]}, + {"t":4.30125, "x":8.52699, "y":4.40001, "heading":-1.5708, "vx":-0.00481, "vy":-0.84981, "omega":0.0, "ax":0.20847, "ay":0.00007, "alpha":0.0, "fx":[3.54601,3.54601,3.54601,3.54601], "fy":[0.00124,0.00124,0.00124,0.00124]}, + {"t":4.35027, "x":8.52701, "y":4.35836, "heading":-1.5708, "vx":0.00541, "vy":-0.84981, "omega":0.0, "ax":-0.2327, "ay":0.00008, "alpha":0.0, "fx":[-3.95816,-3.95816,-3.95816,-3.95816], "fy":[0.00138,0.00138,0.00138,0.00138]}, + {"t":4.39928, "x":8.52699, "y":4.31671, "heading":-1.5708, "vx":-0.006, "vy":-0.84981, "omega":0.0, "ax":0.25701, "ay":0.00009, "alpha":0.0, "fx":[4.37163,4.37163,4.37163,4.37163], "fy":[0.00153,0.00153,0.00153,0.00153]}, + {"t":4.44829, "x":8.52701, "y":4.27506, "heading":-1.5708, "vx":0.0066, "vy":-0.8498, "omega":0.0, "ax":-0.2814, "ay":0.0001, "alpha":0.0, "fx":[-4.78653,-4.78653,-4.78653,-4.78653], "fy":[0.00169,0.00169,0.00169,0.00169]}, + {"t":4.4973, "x":8.52699, "y":4.23341, "heading":-1.5708, "vx":-0.0072, "vy":-0.8498, "omega":0.0, "ax":0.30588, "ay":0.00011, "alpha":0.0, "fx":[5.20297,5.20297,5.20297,5.20297], "fy":[0.00184,0.00184,0.00184,0.00184]}, + {"t":4.54631, "x":8.52701, "y":4.19176, "heading":-1.5708, "vx":0.0078, "vy":-0.84979, "omega":0.0, "ax":-0.33046, "ay":0.00012, "alpha":0.0, "fx":[-5.62106,-5.62106,-5.62106,-5.62106], "fy":[0.002,0.002,0.002,0.002]}, + {"t":4.59532, "x":8.52699, "y":4.15011, "heading":-1.5708, "vx":-0.0084, "vy":-0.84979, "omega":0.0, "ax":0.35514, "ay":0.00013, "alpha":0.0, "fx":[6.04087,6.04087,6.04087,6.04087], "fy":[0.00215,0.00215,0.00215,0.00215]}, + {"t":4.64434, "x":8.52701, "y":4.10846, "heading":-1.5708, "vx":0.00901, "vy":-0.84978, "omega":0.0, "ax":-0.37993, "ay":0.00014, "alpha":0.0, "fx":[-6.46251,-6.46251,-6.46251,-6.46251], "fy":[0.00231,0.00231,0.00231,0.00231]}, + {"t":4.69335, "x":8.52699, "y":4.06682, "heading":-1.5708, "vx":-0.00961, "vy":-0.84977, "omega":0.0, "ax":0.40483, "ay":0.00015, "alpha":0.0, "fx":[6.88604,6.88604,6.88604,6.88604], "fy":[0.00248,0.00248,0.00248,0.00248]}, + {"t":4.74236, "x":8.52701, "y":4.02517, "heading":-1.5708, "vx":0.01023, "vy":-0.84977, "omega":0.0, "ax":-0.42985, "ay":0.00016, "alpha":0.0, "fx":[-7.31155,-7.31155,-7.31155,-7.31155], "fy":[0.00264,0.00264,0.00264,0.00264]}, + {"t":4.79137, "x":8.52699, "y":3.98352, "heading":-1.5708, "vx":-0.01084, "vy":-0.84976, "omega":0.0, "ax":0.45498, "ay":0.00017, "alpha":0.0, "fx":[7.73908,7.73908,7.73908,7.73908], "fy":[0.00281,0.00281,0.00281,0.00281]}, + {"t":4.84038, "x":8.52701, "y":3.94187, "heading":-1.5708, "vx":0.01146, "vy":-0.84975, "omega":0.0, "ax":-0.48024, "ay":0.00018, "alpha":0.0, "fx":[-8.16871,-8.16871,-8.16871,-8.16871], "fy":[0.00298,0.00298,0.00298,0.00298]}, + {"t":4.88939, "x":8.52699, "y":3.90022, "heading":-1.5708, "vx":-0.01208, "vy":-0.84974, "omega":0.0, "ax":0.50562, "ay":0.00019, "alpha":0.0, "fx":[8.60048,8.60048,8.60048,8.60048], "fy":[0.00316,0.00316,0.00316,0.00316]}, + {"t":4.9384, "x":8.52701, "y":3.85858, "heading":-1.5708, "vx":0.0127, "vy":-0.84973, "omega":0.0, "ax":-0.53113, "ay":0.0002, "alpha":0.0, "fx":[-9.03444,-9.03444,-9.03444,-9.03444], "fy":[0.00333,0.00333,0.00333,0.00333]}, + {"t":4.98742, "x":8.52699, "y":3.81693, "heading":-1.5708, "vx":-0.01333, "vy":-0.84972, "omega":0.0, "ax":0.55678, "ay":0.00021, "alpha":0.0, "fx":[9.47062,9.47062,9.47062,9.47062], "fy":[0.00351,0.00351,0.00351,0.00351]}, + {"t":5.03643, "x":8.52701, "y":3.77528, "heading":-1.5708, "vx":0.01396, "vy":-0.84971, "omega":0.0, "ax":-0.58255, "ay":0.00032, "alpha":0.0, "fx":[-9.90907,-9.90907,-9.90907,-9.90907], "fy":[0.00551,0.00551,0.00551,0.00551]}, + {"t":5.08544, "x":8.52699, "y":3.73364, "heading":-1.5708, "vx":-0.01459, "vy":-0.8497, "omega":0.0, "ax":0.60851, "ay":8.9531, "alpha":0.0, "fx":[10.35066,10.35066,10.35066,10.35066], "fy":[152.2896,152.2896,152.2896,152.2896]}, + {"t":5.13445, "x":8.52701, "y":3.70275, "heading":-1.5708, "vx":0.01523, "vy":-0.41089, "omega":0.0, "ax":-0.62811, "ay":9.40283, "alpha":0.0, "fx":[-10.68404,-10.68404,-10.68404,-10.68404], "fy":[159.93949,159.93949,159.93949,159.93949]}, + {"t":5.18346, "x":8.527, "y":3.6939, "heading":-1.5708, "vx":-0.01555, "vy":0.04996, "omega":0.0, "ax":-5.09184, "ay":7.93494, "alpha":0.0, "fx":[-86.61082,-86.61082,-86.61082,-86.61082], "fy":[134.97099,134.97099,134.97099,134.97099]}, + {"t":5.22699, "x":8.5215, "y":3.70359, "heading":-1.5708, "vx":-0.2372, "vy":0.39536, "omega":0.0, "ax":-5.09123, "ay":7.93376, "alpha":0.0, "fx":[-86.6003,-86.6003,-86.6003,-86.6003], "fy":[134.95097,134.95097,134.95097,134.95097]}, + {"t":5.27052, "x":8.50635, "y":3.72832, "heading":-1.5708, "vx":-0.45882, "vy":0.74072, "omega":0.0, "ax":-5.0904, "ay":7.93219, "alpha":0.0, "fx":[-86.5862,-86.5862,-86.5862,-86.5862], "fy":[134.92431,134.92431,134.92431,134.92431]}, + {"t":5.31405, "x":8.48155, "y":3.76808, "heading":-1.5708, "vx":-0.68041, "vy":1.08601, "omega":0.0, "ax":-5.08923, "ay":7.93, "alpha":0.0, "fx":[-86.56638,-86.56638,-86.56638,-86.56638], "fy":[134.88704,134.88704,134.88704,134.88704]}, + {"t":5.35758, "x":8.44711, "y":3.82287, "heading":-1.5708, "vx":-0.90194, "vy":1.4312, "omega":0.0, "ax":-5.08748, "ay":7.92672, "alpha":0.0, "fx":[-86.53651,-86.53651,-86.53651,-86.53651], "fy":[134.83116,134.83116,134.83116,134.83116]}, + {"t":5.40111, "x":8.40303, "y":3.89268, "heading":-1.5708, "vx":-1.1234, "vy":1.77626, "omega":0.0, "ax":-5.08454, "ay":7.92124, "alpha":0.0, "fx":[-86.48657,-86.48657,-86.48657,-86.48657], "fy":[134.73808,134.73808,134.73808,134.73808]}, + {"t":5.44464, "x":8.34931, "y":3.9775, "heading":-1.5708, "vx":-1.34473, "vy":2.12107, "omega":0.0, "ax":-5.07865, "ay":7.91031, "alpha":0.0, "fx":[-86.38636,-86.38636,-86.38636,-86.38636], "fy":[134.55204,134.55204,134.55204,134.55204]}, + {"t":5.48817, "x":8.28597, "y":4.07733, "heading":-1.5708, "vx":-1.56581, "vy":2.4654, "omega":0.0, "ax":-5.06093, "ay":7.8776, "alpha":0.0, "fx":[-86.08502,-86.08502,-86.08502,-86.08502], "fy":[133.99564,133.99564,133.99564,133.99564]}, + {"t":5.5317, "x":8.21301, "y":4.19211, "heading":-1.5708, "vx":-1.78611, "vy":2.80832, "omega":0.0, "ax":-2.45189, "ay":3.33422, "alpha":0.0, "fx":[-41.70589,-41.70589,-41.70589,-41.70589], "fy":[56.71417,56.71417,56.71417,56.71417]}, + {"t":5.57523, "x":8.13294, "y":4.31752, "heading":-1.5708, "vx":-1.89284, "vy":2.95346, "omega":0.0, "ax":-0.02961, "ay":-0.01824, "alpha":0.0, "fx":[-0.5037,-0.5037,-0.5037,-0.5037], "fy":[-0.31021,-0.31021,-0.31021,-0.31021]}, + {"t":5.61876, "x":8.05051, "y":4.44606, "heading":-1.5708, "vx":-1.89413, "vy":2.95266, "omega":0.0, "ax":-0.00284, "ay":-0.00182, "alpha":0.0, "fx":[-0.04824,-0.04824,-0.04824,-0.04824], "fy":[-0.03093,-0.03093,-0.03093,-0.03093]}, + {"t":5.66229, "x":7.96806, "y":4.57459, "heading":-1.5708, "vx":-1.89425, "vy":2.95258, "omega":0.0, "ax":0.00006, "ay":0.00004, "alpha":0.0, "fx":[0.00101,0.00101,0.00101,0.00101], "fy":[0.00067,0.00067,0.00067,0.00067]}, + {"t":5.70582, "x":7.8856, "y":4.70312, "heading":-1.5708, "vx":-1.89425, "vy":2.95258, "omega":0.0, "ax":0.00045, "ay":0.00029, "alpha":0.0, "fx":[0.00761,0.00761,0.00761,0.00761], "fy":[0.0049,0.0049,0.0049,0.0049]}, + {"t":5.74935, "x":7.80315, "y":4.83164, "heading":-1.5708, "vx":-1.89423, "vy":2.9526, "omega":0.0, "ax":0.00057, "ay":0.00037, "alpha":0.0, "fx":[0.00972,0.00972,0.00972,0.00972], "fy":[0.00626,0.00626,0.00626,0.00626]}, + {"t":5.79288, "x":7.72069, "y":4.96017, "heading":-1.5708, "vx":-1.89421, "vy":2.95261, "omega":0.0, "ax":0.00067, "ay":0.00043, "alpha":0.0, "fx":[0.01143,0.01143,0.01143,0.01143], "fy":[0.00737,0.00737,0.00737,0.00737]}, + {"t":5.83641, "x":7.63824, "y":5.0887, "heading":-1.5708, "vx":-1.89418, "vy":2.95263, "omega":0.0, "ax":0.00078, "ay":0.0005, "alpha":0.0, "fx":[0.01319,0.01319,0.01319,0.01319], "fy":[0.0085,0.0085,0.0085,0.0085]}, + {"t":5.87994, "x":7.55578, "y":5.21723, "heading":-1.5708, "vx":-1.89414, "vy":2.95265, "omega":0.0, "ax":0.00088, "ay":0.00057, "alpha":0.0, "fx":[0.01503,0.01503,0.01503,0.01503], "fy":[0.00968,0.00968,0.00968,0.00968]}, + {"t":5.92347, "x":7.47333, "y":5.34576, "heading":-1.5708, "vx":-1.8941, "vy":2.95268, "omega":0.0, "ax":0.00097, "ay":0.00063, "alpha":0.0, "fx":[0.01657,0.01657,0.01657,0.01657], "fy":[0.01068,0.01068,0.01068,0.01068]}, + {"t":5.967, "x":7.39088, "y":5.47429, "heading":-1.5708, "vx":-1.89406, "vy":2.95271, "omega":0.0, "ax":0.00083, "ay":0.00053, "alpha":0.0, "fx":[0.01409,0.01409,0.01409,0.01409], "fy":[0.00909,0.00909,0.00909,0.00909]}, + {"t":6.01053, "x":7.30844, "y":5.60282, "heading":-1.5708, "vx":-1.89403, "vy":2.95273, "omega":0.0, "ax":-0.00162, "ay":-0.00104, "alpha":0.0, "fx":[-0.02759,-0.02759,-0.02759,-0.02759], "fy":[-0.01764,-0.01764,-0.01764,-0.01764]}, + {"t":6.05406, "x":7.22599, "y":5.73135, "heading":-1.5708, "vx":-1.8941, "vy":2.95268, "omega":0.0, "ax":-0.02579, "ay":-0.01655, "alpha":0.0, "fx":[-0.43863,-0.43863,-0.43863,-0.43863], "fy":[-0.28143,-0.28143,-0.28143,-0.28143]}, + {"t":6.09759, "x":7.14351, "y":5.85987, "heading":-1.5708, "vx":-1.89522, "vy":2.95196, "omega":0.0, "ax":-0.25225, "ay":-0.16454, "alpha":0.0, "fx":[-4.29078,-4.29078,-4.29078,-4.29078], "fy":[-2.79875,-2.79875,-2.79875,-2.79875]}, + {"t":6.14112, "x":7.06077, "y":5.98821, "heading":-1.5708, "vx":-1.9062, "vy":2.9448, "omega":0.0, "ax":2.33393, "ay":-6.2332, "alpha":0.0, "fx":[39.69941,39.69941,39.69941,39.69941], "fy":[-106.02501,-106.02501,-106.02501,-106.02501]}, + {"t":6.18465, "x":6.98001, "y":6.11049, "heading":-1.5708, "vx":-1.8046, "vy":2.67347, "omega":0.0, "ax":5.01734, "ay":-7.90714, "alpha":0.0, "fx":[85.34346,85.34346,85.34346,85.34346], "fy":[-134.49821,-134.49821,-134.49821,-134.49821]}, + {"t":6.22818, "x":6.90621, "y":6.21938, "heading":-1.5708, "vx":-1.5862, "vy":2.32927, "omega":0.0, "ax":5.05506, "ay":-7.92586, "alpha":0.0, "fx":[85.98505,85.98505,85.98505,85.98505], "fy":[-134.81666,-134.81666,-134.81666,-134.81666]}, + {"t":6.27172, "x":6.84195, "y":6.31326, "heading":-1.5708, "vx":-1.36615, "vy":1.98426, "omega":0.0, "ax":5.06782, "ay":-7.93216, "alpha":0.0, "fx":[86.20218,86.20218,86.20218,86.20218], "fy":[-134.92382,-134.92382,-134.92382,-134.92382]}, + {"t":6.31525, "x":6.78728, "y":6.39212, "heading":-1.5708, "vx":-1.14555, "vy":1.63897, "omega":0.0, "ax":-5.26862, "ay":-4.24258, "alpha":0.0, "fx":[-89.61764,-89.61764,-89.61764,-89.61764], "fy":[-72.16506,-72.16506,-72.16506,-72.16506]}, + {"t":6.35509, "x":6.73746, "y":6.45405, "heading":-1.5708, "vx":-1.35546, "vy":1.46993, "omega":0.0, "ax":-0.51719, "ay":-0.48369, "alpha":0.0, "fx":[-8.79726,-8.79726,-8.79726,-8.79726], "fy":[-8.22741,-8.22741,-8.22741,-8.22741]}, + {"t":6.39493, "x":6.68304, "y":6.51224, "heading":-1.5708, "vx":-1.37607, "vy":1.45066, "omega":0.0, "ax":-0.02528, "ay":-0.02399, "alpha":0.0, "fx":[-0.42993,-0.42993,-0.42993,-0.42993], "fy":[-0.40809,-0.40809,-0.40809,-0.40809]}, + {"t":6.43477, "x":6.6282, "y":6.57001, "heading":-1.5708, "vx":-1.37708, "vy":1.44971, "omega":0.0, "ax":-0.00008, "ay":-0.00008, "alpha":0.0, "fx":[-0.0014,-0.0014,-0.0014,-0.0014], "fy":[-0.00132,-0.00132,-0.00132,-0.00132]}, + {"t":6.47461, "x":6.57333, "y":6.62777, "heading":-1.5708, "vx":-1.37708, "vy":1.4497, "omega":0.0, "ax":0.00128, "ay":0.00122, "alpha":0.0, "fx":[0.02179,0.02179,0.02179,0.02179], "fy":[0.02071,0.02071,0.02071,0.02071]}, + {"t":6.51446, "x":6.51847, "y":6.68553, "heading":-1.5708, "vx":-1.37703, "vy":1.44975, "omega":0.0, "ax":0.00143, "ay":0.00136, "alpha":0.0, "fx":[0.02439,0.02439,0.02439,0.02439], "fy":[0.02318,0.02318,0.02318,0.02318]}, + {"t":6.5543, "x":6.4636, "y":6.7433, "heading":-1.5708, "vx":-1.37697, "vy":1.44981, "omega":0.0, "ax":0.00153, "ay":0.00146, "alpha":0.0, "fx":[0.02609,0.02609,0.02609,0.02609], "fy":[0.02479,0.02479,0.02479,0.02479]}, + {"t":6.59414, "x":6.40874, "y":6.80106, "heading":-1.5708, "vx":-1.37691, "vy":1.44987, "omega":0.0, "ax":0.00164, "ay":0.00156, "alpha":0.0, "fx":[0.02791,0.02791,0.02791,0.02791], "fy":[0.02652,0.02652,0.02652,0.02652]}, + {"t":6.63398, "x":6.35389, "y":6.85883, "heading":-1.5708, "vx":-1.37684, "vy":1.44993, "omega":0.0, "ax":0.00176, "ay":0.00167, "alpha":0.0, "fx":[0.02996,0.02996,0.02996,0.02996], "fy":[0.02846,0.02846,0.02846,0.02846]}, + {"t":6.67383, "x":6.29903, "y":6.9166, "heading":-1.5708, "vx":-1.37677, "vy":1.44999, "omega":0.0, "ax":0.0019, "ay":0.0018, "alpha":0.0, "fx":[0.03226,0.03226,0.03226,0.03226], "fy":[0.03065,0.03065,0.03065,0.03065]}, + {"t":6.71367, "x":6.24418, "y":6.97437, "heading":-1.5708, "vx":-1.3767, "vy":1.45007, "omega":0.0, "ax":0.00205, "ay":0.00195, "alpha":0.0, "fx":[0.0349,0.0349,0.0349,0.0349], "fy":[0.03315,0.03315,0.03315,0.03315]}, + {"t":6.75351, "x":6.18933, "y":7.03215, "heading":-1.5708, "vx":-1.37662, "vy":1.45014, "omega":0.0, "ax":0.00223, "ay":0.00212, "alpha":0.0, "fx":[0.03793,0.03793,0.03793,0.03793], "fy":[0.03603,0.03603,0.03603,0.03603]}, + {"t":6.79335, "x":6.13448, "y":7.08993, "heading":-1.5708, "vx":-1.37653, "vy":1.45023, "omega":0.0, "ax":0.00244, "ay":0.00232, "alpha":0.0, "fx":[0.04147,0.04147,0.04147,0.04147], "fy":[0.03938,0.03938,0.03938,0.03938]}, + {"t":6.8332, "x":6.07964, "y":7.14771, "heading":-1.5708, "vx":-1.37643, "vy":1.45032, "omega":0.0, "ax":0.00268, "ay":0.00255, "alpha":0.0, "fx":[0.04567,0.04567,0.04567,0.04567], "fy":[0.04336,0.04336,0.04336,0.04336]}, + {"t":6.87304, "x":6.0248, "y":7.2055, "heading":-1.5708, "vx":-1.37632, "vy":1.45042, "omega":0.0, "ax":0.00298, "ay":0.00283, "alpha":0.0, "fx":[0.05074,0.05074,0.05074,0.05074], "fy":[0.04817,0.04817,0.04817,0.04817]}, + {"t":6.91288, "x":5.96997, "y":7.26329, "heading":-1.5708, "vx":-1.37621, "vy":1.45053, "omega":0.0, "ax":0.00335, "ay":0.00318, "alpha":0.0, "fx":[0.057,0.057,0.057,0.057], "fy":[0.0541,0.0541,0.0541,0.0541]}, + {"t":6.95272, "x":5.91514, "y":7.32108, "heading":-1.5708, "vx":-1.37607, "vy":1.45066, "omega":0.0, "ax":0.0038, "ay":0.00361, "alpha":0.0, "fx":[0.06464,0.06464,0.06464,0.06464], "fy":[0.06134,0.06134,0.06134,0.06134]}, + {"t":6.99257, "x":5.86032, "y":7.37888, "heading":-1.5708, "vx":-1.37592, "vy":1.45081, "omega":0.0, "ax":0.00399, "ay":0.00378, "alpha":0.0, "fx":[0.06783,0.06783,0.06783,0.06783], "fy":[0.06436,0.06436,0.06436,0.06436]}, + {"t":7.03241, "x":5.8055, "y":7.43669, "heading":-1.5708, "vx":-1.37576, "vy":1.45096, "omega":0.0, "ax":-0.00377, "ay":-0.00358, "alpha":0.0, "fx":[-0.06418,-0.06418,-0.06418,-0.06418], "fy":[-0.06081,-0.06081,-0.06081,-0.06081]}, + {"t":7.07225, "x":5.75068, "y":7.49449, "heading":-1.5708, "vx":-1.37591, "vy":1.45081, "omega":0.0, "ax":-0.17131, "ay":-0.16323, "alpha":0.0, "fx":[-2.9139,-2.9139,-2.9139,-2.9139], "fy":[-2.77654,-2.77654,-2.77654,-2.77654]}, + {"t":7.11209, "x":5.69573, "y":7.55217, "heading":-1.5708, "vx":-1.38274, "vy":1.44431, "omega":0.0, "ax":-2.75009, "ay":-2.84935, "alpha":0.0, "fx":[-46.77824,-46.77824,-46.77824,-46.77824], "fy":[-48.46655,-48.46655,-48.46655,-48.46655]}, + {"t":7.15193, "x":5.63845, "y":7.60745, "heading":-1.5708, "vx":-1.49231, "vy":1.33079, "omega":0.0, "ax":-5.30782, "ay":-7.13775, "alpha":0.0, "fx":[-90.28457,-90.28457,-90.28457,-90.28457], "fy":[-121.41113,-121.41113,-121.41113,-121.41113]}, + {"t":7.19178, "x":5.57478, "y":7.65481, "heading":-1.5708, "vx":-1.70378, "vy":1.0464, "omega":0.0, "ax":-4.09504, "ay":-8.34427, "alpha":0.0, "fx":[-69.65546,-69.65546,-69.65546,-69.65546], "fy":[-141.93359,-141.93359,-141.93359,-141.93359]}, + {"t":7.23162, "x":5.50365, "y":7.68988, "heading":-1.5708, "vx":-1.86694, "vy":0.71395, "omega":0.0, "ax":-2.52691, "ay":-8.94226, "alpha":0.0, "fx":[-42.98209,-42.98209,-42.98209,-42.98209], "fy":[-152.10526,-152.10526,-152.10526,-152.10526]}, + {"t":7.271, "x":5.42818, "y":7.71106, "heading":-1.5708, "vx":-1.96644, "vy":0.36184, "omega":0.0, "ax":-0.8378, "ay":-8.79658, "alpha":0.0, "fx":[-14.25074,-14.25074,-14.25074,-14.25074], "fy":[-149.62733,-149.62733,-149.62733,-149.62733]}, + {"t":7.31037, "x":5.3501, "y":7.71848, "heading":-1.5708, "vx":-1.99943, "vy":0.01546, "omega":0.0, "ax":0.04941, "ay":-2.66899, "alpha":0.0, "fx":[0.84041,0.84041,0.84041,0.84041], "fy":[-45.39869,-45.39869,-45.39869,-45.39869]}, + {"t":7.34975, "x":5.27141, "y":7.71702, "heading":-1.5708, "vx":-1.99748, "vy":-0.08963, "omega":0.0, "ax":-0.0166, "ay":0.40629, "alpha":0.0, "fx":[-0.28229,-0.28229,-0.28229,-0.28229], "fy":[6.91091,6.91091,6.91091,6.91091]}, + {"t":7.38912, "x":5.19274, "y":7.71381, "heading":-1.5708, "vx":-1.99814, "vy":-0.07363, "omega":0.0, "ax":-0.01158, "ay":0.34655, "alpha":0.0, "fx":[-0.19702,-0.19702,-0.19702,-0.19702], "fy":[5.89469,5.89469,5.89469,5.89469]}, + {"t":7.4285, "x":5.11406, "y":7.71118, "heading":-1.5708, "vx":-1.99859, "vy":-0.05999, "omega":0.0, "ax":-0.00724, "ay":0.26424, "alpha":0.0, "fx":[-0.12317,-0.12317,-0.12317,-0.12317], "fy":[4.49473,4.49473,4.49473,4.49473]}, + {"t":7.46787, "x":5.03535, "y":7.70902, "heading":-1.5708, "vx":-1.99888, "vy":-0.04958, "omega":0.0, "ax":-0.00496, "ay":0.21889, "alpha":0.0, "fx":[-0.0843,-0.0843,-0.0843,-0.0843], "fy":[3.72327,3.72327,3.72327,3.72327]}, + {"t":7.50725, "x":4.95664, "y":7.70724, "heading":-1.5708, "vx":-1.99907, "vy":-0.04096, "omega":0.0, "ax":-0.00356, "ay":0.19142, "alpha":0.0, "fx":[-0.06056,-0.06056,-0.06056,-0.06056], "fy":[3.25599,3.25599,3.25599,3.25599]}, + {"t":7.54663, "x":4.87793, "y":7.70578, "heading":-1.5708, "vx":-1.99921, "vy":-0.03342, "omega":0.0, "ax":-0.0026, "ay":0.17339, "alpha":0.0, "fx":[-0.04426,-0.04426,-0.04426,-0.04426], "fy":[2.94923,2.94923,2.94923,2.94923]}, + {"t":7.586, "x":4.7992, "y":7.70459, "heading":-1.5708, "vx":-1.99931, "vy":-0.0266, "omega":0.0, "ax":-0.00189, "ay":0.16109, "alpha":0.0, "fx":[-0.0321,-0.0321,-0.0321,-0.0321], "fy":[2.74016,2.74016,2.74016,2.74016]}, + {"t":7.62538, "x":4.72048, "y":7.70367, "heading":-1.5708, "vx":-1.99939, "vy":-0.02025, "omega":0.0, "ax":-0.00132, "ay":0.15271, "alpha":0.0, "fx":[-0.02241,-0.02241,-0.02241,-0.02241], "fy":[2.5976,2.5976,2.5976,2.5976]}, + {"t":7.66475, "x":4.64175, "y":7.70299, "heading":-1.5708, "vx":-1.99944, "vy":-0.01424, "omega":0.0, "ax":-0.00084, "ay":0.14725, "alpha":0.0, "fx":[-0.01421,-0.01421,-0.01421,-0.01421], "fy":[2.50471,2.50471,2.50471,2.50471]}, + {"t":7.70413, "x":4.56302, "y":7.70255, "heading":-1.5708, "vx":-1.99947, "vy":-0.00844, "omega":0.0, "ax":-0.0004, "ay":0.14416, "alpha":0.0, "fx":[-0.00688,-0.00688,-0.00688,-0.00688], "fy":[2.45219,2.45219,2.45219,2.45219]}, + {"t":7.7435, "x":4.48429, "y":7.70232, "heading":-1.5708, "vx":-1.99949, "vy":-0.00277, "omega":0.0, "ax":0.0, "ay":0.14317, "alpha":0.0, "fx":[0.00006,0.00006,0.00006,0.00006], "fy":[2.43533,2.43533,2.43533,2.43533]}, + {"t":7.78288, "x":4.40556, "y":7.70233, "heading":-1.5708, "vx":-1.99949, "vy":0.00287, "omega":0.0, "ax":0.00041, "ay":0.14419, "alpha":0.0, "fx":[0.007,0.007,0.007,0.007], "fy":[2.45268,2.45268,2.45268,2.45268]}, + {"t":7.82226, "x":4.32682, "y":7.70255, "heading":-1.5708, "vx":-1.99947, "vy":0.00855, "omega":0.0, "ax":0.00084, "ay":0.14731, "alpha":0.0, "fx":[0.01434,0.01434,0.01434,0.01434], "fy":[2.5057,2.5057,2.5057,2.5057]}, + {"t":7.86163, "x":4.24809, "y":7.703, "heading":-1.5708, "vx":-1.99944, "vy":0.01435, "omega":0.0, "ax":0.00133, "ay":0.15276, "alpha":0.0, "fx":[0.02255,0.02255,0.02255,0.02255], "fy":[2.59841,2.59841,2.59841,2.59841]}, + {"t":7.90101, "x":4.16937, "y":7.70369, "heading":-1.5708, "vx":-1.99939, "vy":0.02036, "omega":0.0, "ax":0.00188, "ay":0.16026, "alpha":0.0, "fx":[0.03206,0.03206,0.03206,0.03206], "fy":[2.72597,2.72597,2.72597,2.72597]}, + {"t":7.94038, "x":4.09064, "y":7.70461, "heading":-1.5708, "vx":-1.99931, "vy":0.02667, "omega":0.0, "ax":0.0023, "ay":0.15506, "alpha":0.0, "fx":[0.0392,0.0392,0.0392,0.0392], "fy":[2.63746,2.63746,2.63746,2.63746]}, + {"t":7.97976, "x":4.01192, "y":7.70578, "heading":-1.5708, "vx":-1.99922, "vy":0.03278, "omega":0.0, "ax":-0.01078, "ay":-0.15756, "alpha":0.0, "fx":[-0.18337,-0.18337,-0.18337,-0.18337], "fy":[-2.67998,-2.67998,-2.67998,-2.67998]}, + {"t":8.01914, "x":3.93319, "y":7.70695, "heading":-1.5708, "vx":-1.99965, "vy":0.02658, "omega":0.0, "ax":-9.40519, "ay":0.1321, "alpha":0.0, "fx":[-159.97962,-159.97962,-159.97962,-159.97962], "fy":[2.24693,2.24693,2.24693,2.24693]}, + {"t":8.04904, "x":3.86919, "y":7.7078, "heading":-1.5708, "vx":-2.28086, "vy":0.03053, "omega":0.0, "ax":-9.39658, "ay":0.1735, "alpha":0.0, "fx":[-159.83305,-159.83305,-159.83305,-159.83305], "fy":[2.9512,2.9512,2.9512,2.9512]}, + {"t":8.07894, "x":3.7968, "y":7.7088, "heading":-1.5708, "vx":-2.56182, "vy":0.03571, "omega":0.0, "ax":-9.38229, "ay":0.21899, "alpha":0.0, "fx":[-159.59011,-159.59011,-159.59011,-159.59011], "fy":[3.72491,3.72491,3.72491,3.72491]}, + {"t":8.10884, "x":3.716, "y":7.70996, "heading":-1.5708, "vx":-2.84235, "vy":0.04226, "omega":0.0, "ax":-9.35395, "ay":0.27102, "alpha":0.0, "fx":[-159.10809,-159.10809,-159.10809,-159.10809], "fy":[4.60992,4.60992,4.60992,4.60992]}, + {"t":8.13874, "x":3.62684, "y":7.71135, "heading":-1.5708, "vx":-3.12203, "vy":0.05036, "omega":0.0, "ax":-9.26873, "ay":0.33819, "alpha":0.0, "fx":[-157.65837,-157.65837,-157.65837,-157.65837], "fy":[5.75246,5.75246,5.75246,5.75246]}, + {"t":8.16864, "x":3.52934, "y":7.713, "heading":-1.5708, "vx":-3.39916, "vy":0.06048, "omega":0.0, "ax":4.54375, "ay":0.45536, "alpha":0.0, "fx":[77.28794,77.28794,77.28794,77.28794], "fy":[7.74561,7.74561,7.74561,7.74561]}, + {"t":8.19854, "x":3.42974, "y":7.71501, "heading":-1.5708, "vx":-3.26331, "vy":0.07409, "omega":0.0, "ax":9.27584, "ay":-0.41662, "alpha":0.0, "fx":[157.77941,157.77941,157.77941,157.77941], "fy":[-7.08666,-7.08666,-7.08666,-7.08666]}, + {"t":8.22843, "x":3.33632, "y":7.71704, "heading":-1.5708, "vx":-2.98596, "vy":0.06163, "omega":0.0, "ax":9.34524, "ay":-0.53654, "alpha":0.0, "fx":[158.9599,158.9599,158.9599,158.9599], "fy":[-9.12633,-9.12633,-9.12633,-9.12633]}, + {"t":8.25833, "x":3.25121, "y":7.71865, "heading":-1.5708, "vx":-2.70654, "vy":0.04559, "omega":0.0, "ax":9.35934, "ay":-0.70932, "alpha":0.0, "fx":[159.19966,159.19966,159.19966,159.19966], "fy":[-12.06528,-12.06528,-12.06528,-12.06528]}, + {"t":8.28823, "x":3.17447, "y":7.71969, "heading":-1.5708, "vx":-2.42669, "vy":0.02438, "omega":0.0, "ax":9.32489, "ay":-1.17921, "alpha":0.0, "fx":[158.61369,158.61369,158.61369,158.61369], "fy":[-20.05803,-20.05803,-20.05803,-20.05803]}, + {"t":8.31813, "x":3.10608, "y":7.71989, "heading":-1.5708, "vx":-2.14788, "vy":-0.01087, "omega":0.0, "ax":8.98687, "ay":-2.78305, "alpha":0.0, "fx":[152.86408,152.86408,152.86408,152.86408], "fy":[-47.33891,-47.33891,-47.33891,-47.33891]}, + {"t":8.34803, "x":3.04588, "y":7.71833, "heading":-1.5708, "vx":-1.87918, "vy":-0.09409, "omega":0.0, "ax":8.28353, "ay":-4.4745, "alpha":0.0, "fx":[140.90052,140.90052,140.90052,140.90052], "fy":[-76.11004,-76.11004,-76.11004,-76.11004]}, + {"t":8.37793, "x":2.99339, "y":7.71351, "heading":-1.5708, "vx":-1.6315, "vy":-0.22787, "omega":0.0, "ax":7.68403, "ay":-5.44805, "alpha":0.0, "fx":[130.7032,130.7032,130.7032,130.7032], "fy":[-92.6697,-92.6697,-92.6697,-92.6697]}, + {"t":8.40783, "x":2.94805, "y":7.70426, "heading":-1.5708, "vx":-1.40175, "vy":-0.39077, "omega":0.0, "ax":7.22654, "ay":-6.04671, "alpha":0.0, "fx":[122.92143,122.92143,122.92143,122.92143], "fy":[-102.85288,-102.85288,-102.85288,-102.85288]}, + {"t":8.43773, "x":2.90936, "y":7.68988, "heading":-1.5708, "vx":-1.18567, "vy":-0.57157, "omega":0.0, "ax":6.26201, "ay":-2.66341, "alpha":22.91554, "fx":[159.53495,136.94951,23.39818,106.17769], "fy":[15.9009,81.73456,-158.63373,-120.21742]}, + {"t":8.46549, "x":2.87887, "y":7.67299, "heading":-1.56197, "vx":-1.01188, "vy":-0.64549, "omega":0.63599, "ax":6.21878, "ay":-2.53153, "alpha":23.34824, "fx":[159.2897,132.99892,23.85212,106.97813], "fy":[18.00597,87.78626,-158.54781,-119.48709]}, + {"t":8.49324, "x":2.85318, "y":7.6541, "heading":-1.53533, "vx":-0.83929, "vy":-0.71575, "omega":1.28398, "ax":6.24023, "ay":-2.40835, "alpha":23.36155, "fx":[158.88286,130.58394,26.17946,108.93209], "fy":[21.07655,90.90474,-158.15835,-117.68461]}, + {"t":8.521, "x":2.83229, "y":7.63331, "heading":-1.4907, "vx":-0.6661, "vy":-0.78259, "omega":1.93235, "ax":6.34771, "ay":-2.3176, "alpha":22.8034, "fx":[158.2692,131.07093,30.51683,112.03382], "fy":[25.01114,89.35758,-157.35178,-114.70378]}, + {"t":8.54875, "x":2.81625, "y":7.61069, "heading":-1.42828, "vx":-0.48993, "vy":-0.84691, "omega":2.56523, "ax":6.59956, "ay":-2.3708, "alpha":21.14186, "fx":[157.43086,138.39041,36.99045,116.21463], "fy":[29.51254,75.5222,-155.92085,-110.4206]}, + {"t":8.5765, "x":2.80519, "y":7.58628, "heading":-1.34895, "vx":-0.30677, "vy":-0.91271, "omega":3.15199, "ax":7.02228, "ay":-2.98628, "alpha":17.11869, "fx":[156.43266,154.31359,45.71072,121.33109], "fy":[33.99017,21.0702,-153.53922,-104.70415]}, + {"t":8.60426, "x":2.79938, "y":7.5598, "heading":-1.25488, "vx":-0.11187, "vy":-0.99559, "omega":3.62709, "ax":6.93066, "ay":-4.27351, "alpha":13.17329, "fx":[155.49924,132.11736,56.7781,127.15916], "fy":[37.42561,-81.04149,-149.71883,-97.4299]}, + {"t":8.63201, "x":2.79895, "y":7.53052, "heading":-1.14914, "vx":0.08048, "vy":-1.11419, "omega":3.9927, "ax":6.80577, "ay":-4.57063, "alpha":12.56742, "fx":[155.17118,103.58518,70.78991,133.51059], "fy":[37.74906,-116.9388,-143.4792,-88.31131]}, + {"t":8.65976, "x":2.8038, "y":7.49784, "heading":-1.03349, "vx":0.26936, "vy":-1.24104, "omega":4.34149, "ax":7.40343, "ay":-4.17032, "alpha":10.75957, "fx":[156.4741,116.09882,90.62795,140.52016], "fy":[29.56381,-105.64891,-131.51063,-76.14838]}, + {"t":8.68752, "x":2.81413, "y":7.46179, "heading":-0.90885, "vx":0.47483, "vy":-1.35678, "omega":4.6401, "ax":8.57839, "ay":-3.04997, "alpha":5.45578, "fx":[158.29109,146.34385,129.48163,149.54755], "fy":[-3.24572,-58.27822,-91.95514,-54.03768]}, + {"t":8.71527, "x":2.83061, "y":7.42296, "heading":-0.77797, "vx":0.71291, "vy":-1.44143, "omega":4.79152, "ax":8.65221, "ay":0.60415, "alpha":-9.15717, "fx":[145.21814,157.36163,137.04731,149.05967], "fy":[-59.05017,-6.65418,75.00209,31.80777]}, + {"t":8.74302, "x":2.85373, "y":7.38318, "heading":-0.64851, "vx":0.95304, "vy":-1.42466, "omega":4.53738, "ax":8.04338, "ay":1.14743, "alpha":-9.96903, "fx":[142.115,152.74413,120.09561,132.30758], "fy":[-51.35867,6.10161,88.38186,34.94471]}, + {"t":8.77078, "x":2.88328, "y":7.34409, "heading":-0.52643, "vx":1.17628, "vy":-1.39282, "omega":4.2607, "ax":-4.62493, "ay":4.38065, "alpha":13.74952, "fx":[-87.17807,-129.02849,-98.50371,0.03515], "fy":[112.26507,56.63406,7.57158,121.58394]}, + {"t":8.79853, "x":2.91414, "y":7.30712, "heading":-0.40288, "vx":1.04792, "vy":-1.27124, "omega":4.6423, "ax":-7.15405, "ay":2.67994, "alpha":13.73239, "fx":[-126.96751,-154.24578,-136.30525,-69.23512], "fy":[89.84287,20.54843,-56.8676,128.81607]}, + {"t":8.82628, "x":2.94047, "y":7.27287, "heading":-0.26875, "vx":0.84937, "vy":-1.19686, "omega":5.02342, "ax":-6.53389, "ay":5.09443, "alpha":-11.66519, "fx":[-149.23961,-24.83877,-117.02995,-153.45012], "fy":[40.39907,156.08815,108.03944,42.09238]}, + {"t":8.85404, "x":2.96153, "y":7.24161, "heading":-0.13383, "vx":0.66803, "vy":-1.05547, "omega":4.69967, "ax":-5.8168, "ay":5.43375, "alpha":-13.65145, "fx":[-125.7373,7.93725,-120.21447,-157.75368], "fy":[79.66589,159.21298,105.44471,25.38232]}, + {"t":8.88179, "x":2.97783, "y":7.21441, "heading":-0.00865, "vx":0.50659, "vy":-0.90467, "omega":4.3208, "ax":-2.91303, "ay":5.95148, "alpha":-22.85276, "fx":[77.30022,9.71237,-125.71658,-159.49551], "fy":[133.47231,159.55635,99.15353,12.74944]}, + {"t":8.90954, "x":2.99077, "y":7.1916, "heading":0.10247, "vx":0.42575, "vy":-0.73949, "omega":3.68655, "ax":-2.73618, "ay":5.54838, "alpha":-25.74535, "fx":[99.68855,5.04316,-130.80185,-160.09611], "fy":[122.6366,159.97553,92.51648,2.37669]}, + {"t":8.9373, "x":3.00153, "y":7.17321, "heading":0.19486, "vx":0.34981, "vy":-0.58551, "omega":2.97203, "ax":-2.90583, "ay":5.38327, "alpha":-26.53413, "fx":[97.98018,-0.74567,-134.87012,-160.0737], "fy":[125.33916,160.16577,86.58874,-5.82182]}, + {"t":8.96505, "x":3.01012, "y":7.15903, "heading":0.26713, "vx":0.26916, "vy":-0.4361, "omega":2.23561, "ax":-3.09247, "ay":5.28371, "alpha":-26.82611, "fx":[93.25735,-6.01371,-137.86807,-159.78373], "fy":[129.44097,160.12413,81.80847,-11.87571]}, + {"t":8.99281, "x":3.0164, "y":7.14897, "heading":0.31884, "vx":0.18333, "vy":-0.28946, "omega":1.49109, "ax":-3.2472, "ay":5.22486, "alpha":-26.89744, "fx":[88.56232,-10.15048,-139.87336,-159.47436], "fy":[132.97497,159.96144,78.38841,-15.83129]}, + {"t":9.02056, "x":3.02023, "y":7.14294, "heading":0.34987, "vx":0.09321, "vy":-0.14445, "omega":0.74459, "ax":-3.35855, "ay":5.20475, "alpha":-26.82863, "fx":[84.67544,-12.91186,-140.97463,-159.30058], "fy":[135.6389,159.79452,76.43509,-17.74351]}, + {"t":9.04831, "x":3.02153, "y":7.14094, "heading":0.3602, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0,0.0,0.0,0.0]}], "splits":[0] }, "events":[] diff --git a/src/main/deploy/v1_doomspiral/choreo/LEFT_TRENCH_SIMPLE.traj b/src/main/deploy/v1_doomspiral/choreo/LEFT_TRENCH_SIMPLE.traj index 5d2bf83d..6444cc97 100644 --- a/src/main/deploy/v1_doomspiral/choreo/LEFT_TRENCH_SIMPLE.traj +++ b/src/main/deploy/v1_doomspiral/choreo/LEFT_TRENCH_SIMPLE.traj @@ -3,46 +3,21 @@ "version":3, "snapshot":{ "waypoints":[ - {"x":4.441253185272217, "y":7.693458557128906, "heading":-1.5707972442597578, "intervals":27, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, - {"x":7.795207023620605, "y":7.227133750915527, "heading":-1.5707963267948966, "intervals":33, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, - {"x":7.795207023620605, "y":5.0552420559, "heading":-1.5707963267948966, "intervals":21, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":6.451829433441162, "y":6.918713092803955, "heading":0.0, "intervals":17, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":5.503650665283203, "y":7.689876556396484, "heading":-1.5707963267948966, "intervals":22, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, - {"x":3.820880889892578, "y":7.718027114868164, "heading":0.0, "intervals":13, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":2.9093637466430664, "y":7.689876556396484, "heading":-1.5707963267948966, "intervals":27, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":2.818082809448242, "y":6.5, "heading":0.65, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], + {"x":4.441253185272217, "y":7.693458557128906, "heading":-1.5707972442597578, "intervals":26, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, + {"x":5.788935661315918, "y":7.198985576629639, "heading":0.0, "intervals":40, "split":false, "fixTranslation":false, "fixHeading":false, "overrideIntervals":false}], "constraints":[ {"from":"first", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, {"from":"last", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, - {"from":"first", "to":"last", "data":{"type":"KeepInRectangle", "props":{"x":0.0, "y":0.0, "w":16.541, "h":8.0692}}, "enabled":true}, - {"from":1, "to":2, "data":{"type":"KeepInLane", "props":{"tolerance":0.00001}}, "enabled":true}, - {"from":4, "to":6, "data":{"type":"KeepInLane", "props":{"tolerance":0.03550910949707031}}, "enabled":true}, - {"from":4, "to":6, "data":{"type":"MaxAngularVelocity", "props":{"max":0.0}}, "enabled":true}, - {"from":1, "to":2, "data":{"type":"MaxAngularVelocity", "props":{"max":0.0}}, "enabled":true}, - {"from":1, "to":2, "data":{"type":"MaxVelocity", "props":{"max":1.5}}, "enabled":true}, - {"from":3, "to":5, "data":{"type":"MaxVelocity", "props":{"max":2.0}}, "enabled":true}], + {"from":"first", "to":"last", "data":{"type":"KeepInRectangle", "props":{"x":0.0, "y":0.0, "w":16.541, "h":8.0692}}, "enabled":true}], "targetDt":0.05 }, "params":{ "waypoints":[ - {"x":{"exp":"4.441253185272217 m", "val":4.441253185272217}, "y":{"exp":"7.693458557128906 m", "val":7.693458557128906}, "heading":{"exp":"-1.5707972442597578 rad", "val":-1.5707972442597578}, "intervals":27, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, - {"x":{"exp":"7.7952070236206055 m", "val":7.795207023620605}, "y":{"exp":"7.227133750915527 m", "val":7.227133750915527}, "heading":{"exp":"-1.5707963267948966 rad", "val":-1.5707963267948966}, "intervals":33, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, - {"x":{"exp":"7.7952070236206055 m", "val":7.795207023620605}, "y":{"exp":"5.0552420559 m", "val":5.0552420559}, "heading":{"exp":"-1.5707963267948966 rad", "val":-1.5707963267948966}, "intervals":21, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":{"exp":"6.451829433441162 m", "val":6.451829433441162}, "y":{"exp":"6.918713092803955 m", "val":6.918713092803955}, "heading":{"exp":"0 deg", "val":0.0}, "intervals":17, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":{"exp":"5.503650665283203 m", "val":5.503650665283203}, "y":{"exp":"7.689876556396484 m", "val":7.689876556396484}, "heading":{"exp":"-90 deg", "val":-1.5707963267948966}, "intervals":22, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, - {"x":{"exp":"3.820880889892578 m", "val":3.820880889892578}, "y":{"exp":"7.718027114868164 m", "val":7.718027114868164}, "heading":{"exp":"0 deg", "val":0.0}, "intervals":13, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":{"exp":"2.9093637466430664 m", "val":2.9093637466430664}, "y":{"exp":"7.689876556396484 m", "val":7.689876556396484}, "heading":{"exp":"-90 deg", "val":-1.5707963267948966}, "intervals":27, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":{"exp":"2.818082809448242 m", "val":2.818082809448242}, "y":{"exp":"6.5 m", "val":6.5}, "heading":{"exp":"0.65 rad", "val":0.65}, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], + {"x":{"exp":"4.441253185272217 m", "val":4.441253185272217}, "y":{"exp":"7.693458557128906 m", "val":7.693458557128906}, "heading":{"exp":"-1.5707972442597578 rad", "val":-1.5707972442597578}, "intervals":26, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], "constraints":[ {"from":"first", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, {"from":"last", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, - {"from":"first", "to":"last", "data":{"type":"KeepInRectangle", "props":{"x":{"exp":"0 m", "val":0.0}, "y":{"exp":"0 m", "val":0.0}, "w":{"exp":"16.541 m", "val":16.541}, "h":{"exp":"8.0692 m", "val":8.0692}}}, "enabled":true}, - {"from":1, "to":2, "data":{"type":"KeepInLane", "props":{"tolerance":{"exp":"1e-5 m", "val":0.00001}}}, "enabled":true}, - {"from":4, "to":6, "data":{"type":"KeepInLane", "props":{"tolerance":{"exp":"35.50910949707031 mm", "val":0.03550910949707031}}}, "enabled":true}, - {"from":4, "to":6, "data":{"type":"MaxAngularVelocity", "props":{"max":{"exp":"0 rad / s", "val":0.0}}}, "enabled":true}, - {"from":1, "to":2, "data":{"type":"MaxAngularVelocity", "props":{"max":{"exp":"0 rad / s", "val":0.0}}}, "enabled":true}, - {"from":1, "to":2, "data":{"type":"MaxVelocity", "props":{"max":{"exp":"1.5 m / s", "val":1.5}}}, "enabled":true}, - {"from":3, "to":5, "data":{"type":"MaxVelocity", "props":{"max":{"exp":"2 m / s", "val":2.0}}}, "enabled":true}], + {"from":"first", "to":"last", "data":{"type":"KeepInRectangle", "props":{"x":{"exp":"0 m", "val":0.0}, "y":{"exp":"0 m", "val":0.0}, "w":{"exp":"16.541 m", "val":16.541}, "h":{"exp":"8.0692 m", "val":8.0692}}}, "enabled":true}], "targetDt":{ "exp":"0.05 s", "val":0.05 @@ -73,169 +48,35 @@ "differentialTrackWidth":0.5588 }, "sampleType":"Swerve", - "waypoints":[0.0,1.3223,2.82829,3.72608,4.33981,5.18304,5.52212,6.33206], + "waypoints":[0.0,0.00026], "samples":[ - {"t":0.0, "x":4.44125, "y":7.69346, "heading":-1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":9.42878, "ay":-0.07522, "alpha":0.00001, "fx":[160.38084,160.38084,160.38084,160.38084], "fy":[-1.27939,-1.27939,-1.27953,-1.27953]}, - {"t":0.04897, "x":4.45256, "y":7.69337, "heading":-1.5708, "vx":0.46177, "vy":-0.00368, "omega":0.0, "ax":9.42749, "ay":-0.0622, "alpha":0.00001, "fx":[160.35884,160.35884,160.35884,160.35884], "fy":[-1.05802,-1.05802,-1.05815,-1.05815]}, - {"t":0.09795, "x":4.48648, "y":7.69311, "heading":-1.5708, "vx":0.92347, "vy":-0.00673, "omega":0.0, "ax":9.42562, "ay":-0.04924, "alpha":0.00001, "fx":[160.3271,160.3271,160.3271,160.3271], "fy":[-0.83744,-0.83744,-0.83757,-0.83757]}, - {"t":0.14692, "x":4.54301, "y":7.69272, "heading":-1.5708, "vx":1.38508, "vy":-0.00914, "omega":0.0, "ax":9.42277, "ay":-0.03628, "alpha":0.00001, "fx":[160.27855,160.27855,160.27855,160.27855], "fy":[-0.61703,-0.61704,-0.61715,-0.61715]}, - {"t":0.1959, "x":4.62214, "y":7.69223, "heading":-1.5708, "vx":1.84655, "vy":-0.01092, "omega":0.0, "ax":9.41796, "ay":-0.02322, "alpha":0.00001, "fx":[160.19674,160.19674,160.19674,160.19674], "fy":[-0.39483,-0.39483,-0.39494,-0.39494]}, - {"t":0.24487, "x":4.72387, "y":7.69167, "heading":-1.5708, "vx":2.30779, "vy":-0.01206, "omega":0.0, "ax":9.4083, "ay":-0.00967, "alpha":0.00001, "fx":[160.03244,160.03244,160.03244,160.03244], "fy":[-0.1645,-0.1645,-0.1646,-0.1646]}, - {"t":0.29384, "x":4.84818, "y":7.69107, "heading":-1.5708, "vx":2.76855, "vy":-0.01253, "omega":0.0, "ax":9.37945, "ay":0.00618, "alpha":0.00001, "fx":[159.54175,159.54175,159.54175,159.54175], "fy":[0.10524,0.10524,0.10515,0.10515]}, - {"t":0.34282, "x":4.99501, "y":7.69046, "heading":-1.5708, "vx":3.2279, "vy":-0.01223, "omega":0.0, "ax":5.72555, "ay":0.38334, "alpha":-0.00008, "fx":[97.38993,97.39014,97.39012,97.3899], "fy":[6.52021,6.52019,6.52087,6.52088]}, - {"t":0.39179, "x":5.15996, "y":7.69032, "heading":-1.5708, "vx":3.5083, "vy":0.00655, "omega":0.0, "ax":-0.00003, "ay":0.21537, "alpha":0.0, "fx":[-0.00044,-0.00044,-0.00044,-0.00044], "fy":[3.66343,3.66343,3.66343,3.66343]}, - {"t":0.44077, "x":5.33178, "y":7.6909, "heading":-1.5708, "vx":3.5083, "vy":0.0171, "omega":0.0, "ax":-0.00118, "ay":0.19041, "alpha":0.0, "fx":[-0.02009,-0.02009,-0.02009,-0.02009], "fy":[3.23875,3.23875,3.23875,3.23875]}, - {"t":0.48974, "x":5.50359, "y":7.69197, "heading":-1.5708, "vx":3.50824, "vy":0.02642, "omega":0.0, "ax":-0.00175, "ay":0.19661, "alpha":0.0, "fx":[-0.02977,-0.02977,-0.02977,-0.02977], "fy":[3.34421,3.34421,3.34421,3.34421]}, - {"t":0.53871, "x":5.6754, "y":7.6935, "heading":-1.5708, "vx":3.50816, "vy":0.03605, "omega":0.0, "ax":-0.00246, "ay":0.20965, "alpha":0.0, "fx":[-0.04184,-0.04184,-0.04184,-0.04184], "fy":[3.5661,3.5661,3.5661,3.5661]}, - {"t":0.58769, "x":5.84721, "y":7.69551, "heading":-1.5708, "vx":3.50804, "vy":0.04632, "omega":0.0, "ax":-0.0034, "ay":0.22984, "alpha":0.0, "fx":[-0.05786,-0.05786,-0.05786,-0.05786], "fy":[3.90949,3.90949,3.90949,3.90949]}, - {"t":0.63666, "x":6.01901, "y":7.69806, "heading":-1.5708, "vx":3.50787, "vy":0.05757, "omega":0.0, "ax":-0.00477, "ay":0.26183, "alpha":0.0, "fx":[-0.08119,-0.08119,-0.08119,-0.08119], "fy":[4.45369,4.45369,4.45369,4.45369]}, - {"t":0.68564, "x":6.19079, "y":7.70119, "heading":-1.5708, "vx":3.50764, "vy":0.0704, "omega":0.0, "ax":-0.00703, "ay":0.31582, "alpha":0.0, "fx":[-0.11958,-0.11958,-0.11958,-0.11958], "fy":[5.37194,5.37194,5.37194,5.37194]}, - {"t":0.73461, "x":6.36257, "y":7.70502, "heading":-1.5708, "vx":3.50729, "vy":0.08586, "omega":0.0, "ax":-0.01087, "ay":0.39901, "alpha":0.0, "fx":[-0.18493,-0.18493,-0.18493,-0.18493], "fy":[6.78706,6.78706,6.78706,6.78706]}, - {"t":0.78359, "x":6.53432, "y":7.7097, "heading":-1.5708, "vx":3.50676, "vy":0.1054, "omega":0.0, "ax":-0.00692, "ay":0.21917, "alpha":0.0, "fx":[-0.11766,-0.11766,-0.11766,-0.11766], "fy":[3.72799,3.72799,3.72799,3.72799]}, - {"t":0.83256, "x":6.70606, "y":7.71513, "heading":-1.5708, "vx":3.50642, "vy":0.11614, "omega":0.0, "ax":0.00609, "ay":-4.53404, "alpha":0.0, "fx":[0.10358,0.10358,0.10358,0.10358], "fy":[-77.12266,-77.12266,-77.12266,-77.12266]}, - {"t":0.88153, "x":6.87779, "y":7.71538, "heading":-1.5708, "vx":3.50672, "vy":-0.10591, "omega":0.0, "ax":-0.81893, "ay":-8.8013, "alpha":0.0, "fx":[-13.92983,-13.92983,-13.92983,-13.92983], "fy":[-149.70756,-149.70756,-149.70756,-149.70756]}, - {"t":0.93051, "x":7.04854, "y":7.69964, "heading":-1.5708, "vx":3.46661, "vy":-0.53695, "omega":0.0, "ax":-3.97575, "ay":-8.36327, "alpha":0.0, "fx":[-67.6263,-67.62633,-67.62631,-67.62627], "fy":[-142.25692,-142.2569,-142.25691,-142.25693]}, - {"t":0.97948, "x":7.21355, "y":7.66331, "heading":-1.5708, "vx":3.2719, "vy":-0.94653, "omega":0.0, "ax":-8.90996, "ay":-2.95722, "alpha":0.0, "fx":[-151.5559,-151.55591,-151.5559,-151.5559], "fy":[-50.30153,-50.30153,-50.30154,-50.30154]}, - {"t":1.02846, "x":7.3631, "y":7.61341, "heading":-1.5708, "vx":2.83555, "vy":-1.09136, "omega":0.0, "ax":-9.21107, "ay":-1.92662, "alpha":0.0, "fx":[-156.67759,-156.67759,-156.67759,-156.67759], "fy":[-32.7712,-32.7712,-32.7712,-32.7712]}, - {"t":1.07743, "x":7.49092, "y":7.55765, "heading":-1.5708, "vx":2.38444, "vy":-1.18571, "omega":0.0, "ax":-9.29333, "ay":-1.53294, "alpha":0.0, "fx":[-158.07693,-158.07693,-158.07694,-158.07694], "fy":[-26.07486,-26.07486,-26.07486,-26.07486]}, - {"t":1.1264, "x":7.59656, "y":7.49774, "heading":-1.5708, "vx":1.92931, "vy":-1.26079, "omega":0.0, "ax":-9.32944, "ay":-1.3271, "alpha":0.0, "fx":[-158.69118,-158.69118,-158.69118,-158.69118], "fy":[-22.57361,-22.57361,-22.5736,-22.5736]}, - {"t":1.17538, "x":7.67985, "y":7.4344, "heading":-1.5708, "vx":1.47241, "vy":-1.32578, "omega":0.0, "ax":-9.34925, "ay":-1.20107, "alpha":0.0, "fx":[-159.02813,-159.02813,-159.02813,-159.02813], "fy":[-20.4299,-20.42991,-20.4299,-20.4299]}, - {"t":1.22435, "x":7.74075, "y":7.36803, "heading":-1.5708, "vx":1.01454, "vy":-1.3846, "omega":0.0, "ax":-9.36162, "ay":-1.11621, "alpha":0.0, "fx":[-159.23846,-159.23846,-159.23846,-159.23846], "fy":[-18.98649,-18.98649,-18.98648,-18.98648]}, - {"t":1.27333, "x":7.77921, "y":7.29889, "heading":-1.5708, "vx":0.55606, "vy":-1.43927, "omega":0.0, "ax":-9.37001, "ay":-1.05533, "alpha":0.0, "fx":[-159.38124,-159.38124,-159.38124,-159.38124], "fy":[-17.95088,-17.95088,-17.95087,-17.95087]}, - {"t":1.3223, "x":7.79521, "y":7.22713, "heading":-1.5708, "vx":0.09718, "vy":-1.49095, "omega":0.0, "ax":-4.25008, "ay":-0.12251, "alpha":0.0, "fx":[-72.29257,-72.29257,-72.29257,-72.29257], "fy":[-2.08384,-2.08384,-2.08384,-2.08384]}, - {"t":1.36794, "x":7.79522, "y":7.15896, "heading":-1.5708, "vx":-0.09678, "vy":-1.49654, "omega":0.0, "ax":4.22424, "ay":-0.00111, "alpha":0.0, "fx":[71.85315,71.85315,71.85315,71.85315], "fy":[-0.01886,-0.01886,-0.01886,-0.01886]}, - {"t":1.41357, "x":7.7952, "y":7.09067, "heading":-1.5708, "vx":0.096, "vy":-1.49659, "omega":0.0, "ax":-4.18985, "ay":-0.0011, "alpha":0.0, "fx":[-71.26815,-71.26815,-71.26815,-71.26815], "fy":[-0.01865,-0.01865,-0.01865,-0.01865]}, - {"t":1.45921, "x":7.79522, "y":7.02237, "heading":-1.5708, "vx":-0.09521, "vy":-1.49664, "omega":0.0, "ax":4.15554, "ay":-0.00108, "alpha":0.0, "fx":[70.68452,70.68452,70.68452,70.68452], "fy":[-0.01845,-0.01845,-0.01845,-0.01845]}, - {"t":1.50484, "x":7.7952, "y":6.95407, "heading":-1.5708, "vx":0.09443, "vy":-1.49669, "omega":0.0, "ax":-4.12131, "ay":-0.00107, "alpha":0.0, "fx":[-70.10229,-70.10229,-70.10229,-70.10229], "fy":[-0.01826,-0.01826,-0.01826,-0.01826]}, - {"t":1.55048, "x":7.79522, "y":6.88576, "heading":-1.5708, "vx":-0.09365, "vy":-1.49674, "omega":0.0, "ax":4.08716, "ay":-0.00106, "alpha":0.0, "fx":[69.52151,69.52151,69.52151,69.52151], "fy":[-0.01806,-0.01806,-0.01806,-0.01806]}, - {"t":1.59612, "x":7.7952, "y":6.81745, "heading":-1.5708, "vx":0.09287, "vy":-1.49679, "omega":0.0, "ax":-4.05311, "ay":-0.00105, "alpha":0.0, "fx":[-68.94222,-68.94222,-68.94222,-68.94222], "fy":[-0.01786,-0.01786,-0.01786,-0.01786]}, - {"t":1.64175, "x":7.79522, "y":6.74915, "heading":-1.5708, "vx":-0.0921, "vy":-1.49684, "omega":0.0, "ax":4.01914, "ay":-0.00104, "alpha":0.0, "fx":[68.36448,68.36448,68.36448,68.36448], "fy":[-0.01766,-0.01766,-0.01766,-0.01766]}, - {"t":1.68739, "x":7.7952, "y":6.68083, "heading":-1.5708, "vx":0.09132, "vy":-1.49689, "omega":0.0, "ax":-3.98527, "ay":-0.00103, "alpha":0.0, "fx":[-67.78834,-67.78834,-67.78834,-67.78834], "fy":[-0.01747,-0.01747,-0.01747,-0.01747]}, - {"t":1.73303, "x":7.79522, "y":6.61252, "heading":-1.5708, "vx":-0.09055, "vy":-1.49693, "omega":0.0, "ax":3.9515, "ay":-0.00102, "alpha":0.0, "fx":[67.21384,67.21384,67.21384,67.21384], "fy":[-0.01727,-0.01727,-0.01727,-0.01727]}, - {"t":1.77866, "x":7.7952, "y":6.54421, "heading":-1.5708, "vx":0.08978, "vy":-1.49698, "omega":0.0, "ax":-3.91782, "ay":-0.001, "alpha":0.0, "fx":[-66.64105,-66.64105,-66.64105,-66.64105], "fy":[-0.01707,-0.01707,-0.01707,-0.01707]}, - {"t":1.8243, "x":7.79522, "y":6.47589, "heading":-1.5708, "vx":-0.08901, "vy":-1.49703, "omega":0.0, "ax":3.88425, "ay":-0.00099, "alpha":0.0, "fx":[66.07003,66.07003,66.07003,66.07003], "fy":[-0.01687,-0.01687,-0.01687,-0.01687]}, - {"t":1.86993, "x":7.7952, "y":6.40757, "heading":-1.5708, "vx":0.08825, "vy":-1.49707, "omega":0.0, "ax":-3.85079, "ay":-0.00098, "alpha":0.0, "fx":[-65.50083,-65.50083,-65.50083,-65.50083], "fy":[-0.01667,-0.01667,-0.01667,-0.01667]}, - {"t":1.91557, "x":7.79522, "y":6.33925, "heading":-1.5708, "vx":-0.08749, "vy":-1.49712, "omega":0.0, "ax":3.81744, "ay":-0.00097, "alpha":0.0, "fx":[64.93352,64.93352,64.93352,64.93352], "fy":[-0.01647,-0.01647,-0.01647,-0.01647]}, - {"t":1.96121, "x":7.7952, "y":6.27092, "heading":-1.5708, "vx":0.08673, "vy":-1.49716, "omega":0.0, "ax":-3.7842, "ay":-0.00096, "alpha":0.0, "fx":[-64.36817,-64.36817,-64.36817,-64.36817], "fy":[-0.01627,-0.01627,-0.01627,-0.01627]}, - {"t":2.00684, "x":7.79522, "y":6.2026, "heading":-1.5708, "vx":-0.08597, "vy":-1.4972, "omega":0.0, "ax":3.75108, "ay":-0.00094, "alpha":0.0, "fx":[63.80485,63.80485,63.80485,63.80485], "fy":[-0.01607,-0.01607,-0.01607,-0.01607]}, - {"t":2.05248, "x":7.7952, "y":6.13427, "heading":-1.5708, "vx":0.08522, "vy":-1.49725, "omega":0.0, "ax":-3.71809, "ay":-0.00093, "alpha":0.0, "fx":[-63.24363,-63.24363,-63.24363,-63.24363], "fy":[-0.01586,-0.01586,-0.01586,-0.01586]}, - {"t":2.09811, "x":7.79522, "y":6.06594, "heading":-1.5708, "vx":-0.08446, "vy":-1.49729, "omega":0.0, "ax":3.68522, "ay":-0.00092, "alpha":0.0, "fx":[62.6846,62.6846,62.6846,62.6846], "fy":[-0.01566,-0.01566,-0.01566,-0.01566]}, - {"t":2.14375, "x":7.7952, "y":5.99761, "heading":-1.5708, "vx":0.08372, "vy":-1.49733, "omega":0.0, "ax":-3.65249, "ay":-0.00091, "alpha":0.0, "fx":[-62.12784,-62.12784,-62.12784,-62.12784], "fy":[-0.01546,-0.01546,-0.01546,-0.01546]}, - {"t":2.18939, "x":7.79522, "y":5.92928, "heading":-1.5708, "vx":-0.08297, "vy":-1.49737, "omega":0.0, "ax":3.6199, "ay":-0.0009, "alpha":0.0, "fx":[61.57344,61.57344,61.57344,61.57344], "fy":[-0.01525,-0.01525,-0.01525,-0.01525]}, - {"t":2.23502, "x":7.7952, "y":5.86094, "heading":-1.5708, "vx":0.08223, "vy":-1.49741, "omega":0.0, "ax":-3.58745, "ay":-0.00088, "alpha":0.0, "fx":[-61.02149,-61.02149,-61.02149,-61.02149], "fy":[-0.01505,-0.01505,-0.01505,-0.01505]}, - {"t":2.28066, "x":7.79522, "y":5.7926, "heading":-1.5708, "vx":-0.08149, "vy":-1.49745, "omega":0.0, "ax":3.55515, "ay":-0.00087, "alpha":0.0, "fx":[60.47209,60.47209,60.47209,60.47209], "fy":[-0.01484,-0.01484,-0.01484,-0.01484]}, - {"t":2.3263, "x":7.7952, "y":5.72426, "heading":-1.5708, "vx":0.08075, "vy":-1.49749, "omega":0.0, "ax":-3.52301, "ay":-0.00086, "alpha":0.0, "fx":[-59.92534,-59.92534,-59.92534,-59.92534], "fy":[-0.01463,-0.01463,-0.01463,-0.01463]}, - {"t":2.37193, "x":7.79522, "y":5.65592, "heading":-1.5708, "vx":-0.08002, "vy":-1.49753, "omega":0.0, "ax":3.49103, "ay":-0.00085, "alpha":0.0, "fx":[59.38136,59.38136,59.38136,59.38136], "fy":[-0.01442,-0.01442,-0.01442,-0.01442]}, - {"t":2.41757, "x":7.7952, "y":5.58758, "heading":-1.5708, "vx":0.07929, "vy":-1.49757, "omega":0.0, "ax":-3.45922, "ay":-0.00084, "alpha":0.0, "fx":[-58.84026,-58.84026,-58.84026,-58.84026], "fy":[-0.01422,-0.01422,-0.01422,-0.01422]}, - {"t":2.4632, "x":7.79522, "y":5.51924, "heading":-1.5708, "vx":-0.07857, "vy":-1.49761, "omega":0.0, "ax":3.42758, "ay":-0.00082, "alpha":0.0, "fx":[58.30218,58.30218,58.30218,58.30218], "fy":[-0.01401,-0.01401,-0.01401,-0.01401]}, - {"t":2.50884, "x":7.7952, "y":5.45089, "heading":-1.5708, "vx":0.07785, "vy":-1.49765, "omega":0.0, "ax":-3.39613, "ay":-0.00081, "alpha":0.0, "fx":[-57.76724,-57.76724,-57.76724,-57.76724], "fy":[-0.01379,-0.01379,-0.01379,-0.01379]}, - {"t":2.55448, "x":7.79522, "y":5.38254, "heading":-1.5708, "vx":-0.07714, "vy":-1.49768, "omega":0.0, "ax":3.36488, "ay":-0.0008, "alpha":0.0, "fx":[57.23558,57.23558,57.23558,57.23558], "fy":[-0.01358,-0.01358,-0.01358,-0.01358]}, - {"t":2.60011, "x":7.7952, "y":5.31419, "heading":-1.5708, "vx":0.07642, "vy":-1.49772, "omega":0.0, "ax":-3.33382, "ay":-0.00079, "alpha":0.0, "fx":[-56.70736,-56.70736,-56.70736,-56.70736], "fy":[-0.01336,-0.01336,-0.01336,-0.01336]}, - {"t":2.64575, "x":7.79522, "y":5.24584, "heading":-1.5708, "vx":-0.07572, "vy":-1.49776, "omega":0.0, "ax":3.3031, "ay":0.01502, "alpha":0.0, "fx":[56.18476,56.18476,56.18476,56.18476], "fy":[0.25545,0.25545,0.25545,0.25545]}, - {"t":2.69138, "x":7.7952, "y":5.17751, "heading":-1.5708, "vx":0.07502, "vy":-1.49707, "omega":0.0, "ax":-3.27375, "ay":8.80909, "alpha":0.0, "fx":[-55.68548,-55.68548,-55.68548,-55.68548], "fy":[149.84012,149.84012,149.84012,149.84012]}, - {"t":2.73702, "x":7.79521, "y":5.11836, "heading":-1.5708, "vx":-0.07438, "vy":-1.09506, "omega":0.0, "ax":3.25615, "ay":8.84065, "alpha":0.0, "fx":[55.38622,55.38622,55.38622,55.38622], "fy":[150.377,150.377,150.377,150.377]}, - {"t":2.78266, "x":7.79521, "y":5.07759, "heading":-1.5708, "vx":0.07422, "vy":-0.6916, "omega":0.0, "ax":-3.25574, "ay":8.84722, "alpha":0.0, "fx":[-55.37923,-55.37923,-55.37923,-55.37923], "fy":[150.48867,150.48867,150.48867,150.48867]}, - {"t":2.82829, "x":7.79521, "y":5.05524, "heading":-1.5708, "vx":-0.07436, "vy":-0.28785, "omega":0.0, "ax":-5.12448, "ay":7.91464, "alpha":0.0, "fx":[-87.16598,-87.16598,-87.16598,-87.16598], "fy":[134.6258,134.6258,134.6258,134.6258]}, - {"t":2.87104, "x":7.78734, "y":5.05017, "heading":-1.5708, "vx":-0.29344, "vy":0.05051, "omega":0.0, "ax":-5.12249, "ay":7.91464, "alpha":0.0, "fx":[-87.13215,-87.13215,-87.13215,-87.13215], "fy":[134.62573,134.62573,134.62573,134.62573]}, - {"t":2.9138, "x":7.77012, "y":5.05956, "heading":-1.5708, "vx":-0.51244, "vy":0.38888, "omega":0.0, "ax":-5.11995, "ay":7.91462, "alpha":0.0, "fx":[-87.08887,-87.08887,-87.08887,-87.08887], "fy":[134.62546,134.62546,134.62546,134.62546]}, - {"t":2.95655, "x":7.74353, "y":5.08342, "heading":-1.5708, "vx":-0.73132, "vy":0.72724, "omega":0.0, "ax":-5.11658, "ay":7.91459, "alpha":0.0, "fx":[-87.03149,-87.03149,-87.03149,-87.03149], "fy":[134.62483,134.62483,134.62483,134.62483]}, - {"t":2.9993, "x":7.70759, "y":5.12174, "heading":-1.5708, "vx":-0.95006, "vy":1.0656, "omega":0.0, "ax":-5.11188, "ay":7.91451, "alpha":0.0, "fx":[-86.95163,-86.95163,-86.95163,-86.95163], "fy":[134.62351,134.62351,134.62351,134.62351]}, - {"t":3.04205, "x":7.6623, "y":5.17453, "heading":-1.5708, "vx":-1.16861, "vy":1.40396, "omega":0.0, "ax":-5.10488, "ay":7.91435, "alpha":0.0, "fx":[-86.83258,-86.83258,-86.83258,-86.83258], "fy":[134.62077,134.62077,134.62077,134.62077]}, - {"t":3.0848, "x":7.60768, "y":5.24179, "heading":-1.5708, "vx":-1.38685, "vy":1.74231, "omega":0.0, "ax":-5.0933, "ay":7.91399, "alpha":0.0, "fx":[-86.63549,-86.63549,-86.63549,-86.63549], "fy":[134.61467,134.61467,134.61467,134.61467]}, - {"t":3.12755, "x":7.54373, "y":5.3235, "heading":-1.5708, "vx":-1.60459, "vy":2.08065, "omega":0.0, "ax":-5.07029, "ay":7.91302, "alpha":0.0, "fx":[-86.24424,-86.24424,-86.24424,-86.24424], "fy":[134.59823,134.59823,134.59823,134.59823]}, - {"t":3.17031, "x":7.4705, "y":5.41969, "heading":-1.5708, "vx":-1.82136, "vy":2.41894, "omega":0.0, "ax":-5.00194, "ay":7.90879, "alpha":0.0, "fx":[-85.08151,-85.08151,-85.08151,-85.08151], "fy":[134.52621,134.52621,134.52621,134.52621]}, - {"t":3.21306, "x":7.38806, "y":5.53033, "heading":-1.5708, "vx":-2.0352, "vy":2.75706, "omega":0.0, "ax":2.34201, "ay":3.94862, "alpha":0.0, "fx":[39.83686,39.83686,39.83686,39.83686], "fy":[67.16496,67.16496,67.16496,67.16496]}, - {"t":3.25581, "x":7.3032, "y":5.65181, "heading":-1.5708, "vx":-1.93508, "vy":2.92587, "omega":0.0, "ax":0.50826, "ay":0.33387, "alpha":0.0, "fx":[8.64533,8.64533,8.64533,8.64533], "fy":[5.67909,5.67909,5.67909,5.67909]}, - {"t":3.29856, "x":7.22093, "y":5.7772, "heading":-1.5708, "vx":-1.91335, "vy":2.94014, "omega":0.0, "ax":0.05882, "ay":0.03825, "alpha":0.0, "fx":[1.00048,1.00048,1.00048,1.00048], "fy":[0.65055,0.65055,0.65055,0.65055]}, - {"t":3.34131, "x":7.13919, "y":5.90293, "heading":-1.5708, "vx":-1.91083, "vy":2.94178, "omega":0.0, "ax":0.00807, "ay":0.00525, "alpha":0.0, "fx":[0.13728,0.13728,0.13728,0.13728], "fy":[0.08924,0.08924,0.08924,0.08924]}, - {"t":3.38407, "x":7.0575, "y":6.0287, "heading":-1.5708, "vx":-1.91049, "vy":2.942, "omega":0.0, "ax":0.00175, "ay":0.00114, "alpha":0.0, "fx":[0.02975,0.02975,0.02975,0.02975], "fy":[0.01941,0.01941,0.01941,0.01941]}, - {"t":3.42682, "x":6.97583, "y":6.15448, "heading":-1.5708, "vx":-1.91041, "vy":2.94205, "omega":0.0, "ax":-0.00604, "ay":-0.00392, "alpha":0.0, "fx":[-0.10277,-0.10277,-0.10277,-0.10277], "fy":[-0.06664,-0.06664,-0.06664,-0.06664]}, - {"t":3.46957, "x":6.89415, "y":6.28025, "heading":-1.5708, "vx":-1.91067, "vy":2.94188, "omega":0.0, "ax":-0.07173, "ay":-0.04664, "alpha":0.0, "fx":[-1.22013,-1.22013,-1.22013,-1.22013], "fy":[-0.79326,-0.79326,-0.79326,-0.79326]}, - {"t":3.51232, "x":6.8124, "y":6.40598, "heading":-1.5708, "vx":-1.91374, "vy":2.93989, "omega":0.0, "ax":-0.65077, "ay":-0.43145, "alpha":0.0, "fx":[-11.06936,-11.06936,-11.06936,-11.06936], "fy":[-7.33892,-7.33892,-7.33892,-7.33892]}, - {"t":3.55507, "x":6.72999, "y":6.53127, "heading":-1.5708, "vx":-1.94156, "vy":2.92144, "omega":0.0, "ax":1.94572, "ay":-7.35499, "alpha":0.0, "fx":[33.09612,33.09612,33.09612,33.09612], "fy":[-125.10621,-125.10621,-125.10621,-125.10621]}, - {"t":3.59782, "x":6.64876, "y":6.64944, "heading":-1.5708, "vx":-1.85838, "vy":2.60701, "omega":0.0, "ax":5.00378, "ay":-7.91312, "alpha":0.0, "fx":[85.11295,85.11295,85.11295,85.11295], "fy":[-134.59988,-134.59988,-134.59988,-134.59988]}, - {"t":3.64058, "x":6.57389, "y":6.75367, "heading":-1.5708, "vx":-1.64446, "vy":2.26871, "omega":0.0, "ax":5.06887, "ay":-7.91539, "alpha":0.0, "fx":[86.22003,86.22003,86.22003,86.22003], "fy":[-134.63844,-134.63844,-134.63844,-134.63844]}, - {"t":3.68333, "x":6.50822, "y":6.84342, "heading":-1.5708, "vx":-1.42775, "vy":1.93031, "omega":0.0, "ax":5.0911, "ay":-7.91606, "alpha":0.0, "fx":[86.59815,86.59815,86.59815,86.59815], "fy":[-134.64995,-134.64995,-134.64995,-134.64995]}, - {"t":3.72608, "x":6.45183, "y":6.91871, "heading":-1.5708, "vx":-1.2101, "vy":1.59188, "omega":0.0, "ax":-6.44353, "ay":-5.7497, "alpha":0.0, "fx":[-109.60266,-109.60266,-109.60266,-109.60266], "fy":[-97.80083,-97.80083,-97.80083,-97.80083]}, - {"t":3.76218, "x":6.40394, "y":6.97244, "heading":-1.5708, "vx":-1.44272, "vy":1.38431, "omega":0.0, "ax":-2.31161, "ay":-2.5644, "alpha":0.0, "fx":[-39.31981,-39.31981,-39.31981,-39.31981], "fy":[-43.6198,-43.6198,-43.6198,-43.6198]}, - {"t":3.79828, "x":6.35035, "y":7.02074, "heading":-1.5708, "vx":-1.52618, "vy":1.29173, "omega":0.0, "ax":-0.16848, "ay":-0.20001, "alpha":0.0, "fx":[-2.86584,-2.86584,-2.86584,-2.86584], "fy":[-3.40215,-3.40215,-3.40215,-3.40215]}, - {"t":3.83438, "x":6.29514, "y":7.06725, "heading":-1.5708, "vx":-1.53226, "vy":1.28451, "omega":0.0, "ax":-0.00876, "ay":-0.01045, "alpha":0.0, "fx":[-0.14898,-0.14898,-0.14898,-0.14898], "fy":[-0.17773,-0.17773,-0.17773,-0.17773]}, - {"t":3.87049, "x":6.23982, "y":7.11361, "heading":-1.5708, "vx":-1.53258, "vy":1.28413, "omega":0.0, "ax":0.00203, "ay":0.00242, "alpha":0.0, "fx":[0.03446,0.03446,0.03446,0.03446], "fy":[0.04115,0.04115,0.04115,0.04115]}, - {"t":3.90659, "x":6.18449, "y":7.15997, "heading":-1.5708, "vx":-1.5325, "vy":1.28422, "omega":0.0, "ax":0.00295, "ay":0.00353, "alpha":0.0, "fx":[0.05024,0.05024,0.05024,0.05024], "fy":[0.05998,0.05998,0.05998,0.05998]}, - {"t":3.94269, "x":6.12917, "y":7.20634, "heading":-1.5708, "vx":-1.5324, "vy":1.28435, "omega":0.0, "ax":0.00326, "ay":0.00389, "alpha":0.0, "fx":[0.05546,0.05546,0.05546,0.05546], "fy":[0.0662,0.0662,0.0662,0.0662]}, - {"t":3.97879, "x":6.07385, "y":7.25271, "heading":-1.5708, "vx":-1.53228, "vy":1.28449, "omega":0.0, "ax":0.00357, "ay":0.00426, "alpha":0.0, "fx":[0.06075,0.06075,0.06075,0.06075], "fy":[0.07251,0.07251,0.07251,0.07251]}, - {"t":4.01489, "x":6.01853, "y":7.29908, "heading":-1.5708, "vx":-1.53215, "vy":1.28464, "omega":0.0, "ax":0.00394, "ay":0.0047, "alpha":0.0, "fx":[0.06707,0.06707,0.06707,0.06707], "fy":[0.08003,0.08003,0.08003,0.08003]}, - {"t":4.051, "x":5.96322, "y":7.34546, "heading":-1.5708, "vx":-1.53201, "vy":1.28481, "omega":0.0, "ax":0.0044, "ay":0.00525, "alpha":0.0, "fx":[0.07484,0.07484,0.07484,0.07484], "fy":[0.08927,0.08927,0.08927,0.08927]}, - {"t":4.0871, "x":5.90791, "y":7.39185, "heading":-1.5708, "vx":-1.53185, "vy":1.285, "omega":0.0, "ax":0.00495, "ay":0.0059, "alpha":0.0, "fx":[0.08421,0.08421,0.08421,0.08421], "fy":[0.10042,0.10042,0.10042,0.10042]}, - {"t":4.1232, "x":5.85261, "y":7.43825, "heading":-1.5708, "vx":-1.53167, "vy":1.28521, "omega":0.0, "ax":0.00521, "ay":0.00622, "alpha":0.0, "fx":[0.08869,0.08869,0.08869,0.08869], "fy":[0.10574,0.10574,0.10574,0.10574]}, - {"t":4.1593, "x":5.79732, "y":7.48465, "heading":-1.5708, "vx":-1.53148, "vy":1.28544, "omega":0.0, "ax":-0.00143, "ay":-0.0017, "alpha":0.0, "fx":[-0.02438,-0.02438,-0.02438,-0.02438], "fy":[-0.02898,-0.02898,-0.02898,-0.02898]}, - {"t":4.1954, "x":5.74203, "y":7.53106, "heading":-1.5708, "vx":-1.53153, "vy":1.28538, "omega":0.0, "ax":-0.11602, "ay":-0.13869, "alpha":0.0, "fx":[-1.97345,-1.97345,-1.97345,-1.97345], "fy":[-2.35914,-2.35914,-2.35914,-2.35914]}, - {"t":4.23151, "x":5.68666, "y":7.57737, "heading":-1.5708, "vx":-1.53572, "vy":1.28037, "omega":0.0, "ax":-1.66085, "ay":-2.09286, "alpha":0.0, "fx":[-28.25062,-28.25062,-28.25062,-28.25062], "fy":[-35.59894,-35.59894,-35.59894,-35.59894]}, - {"t":4.26761, "x":5.63014, "y":7.62223, "heading":-1.5708, "vx":-1.59568, "vy":1.20481, "omega":0.0, "ax":-4.54468, "ay":-7.08083, "alpha":0.0, "fx":[-77.30365,-77.30365,-77.30365,-77.30365], "fy":[-120.4429,-120.4429,-120.4429,-120.4429]}, - {"t":4.30371, "x":5.56957, "y":7.66111, "heading":-1.5708, "vx":-1.75975, "vy":0.94918, "omega":0.0, "ax":-3.66559, "ay":-8.44393, "alpha":0.0, "fx":[-62.35071,-62.35071,-62.35071,-62.35071], "fy":[-143.62885,-143.62885,-143.62885,-143.62885]}, - {"t":4.33981, "x":5.50365, "y":7.68988, "heading":-1.5708, "vx":-1.89209, "vy":0.64434, "omega":0.0, "ax":-2.20393, "ay":-8.93964, "alpha":0.0, "fx":[-37.48815,-37.48815,-37.48815,-37.48815], "fy":[-152.06079,-152.06079,-152.06079,-152.06079]}, - {"t":4.37814, "x":5.42951, "y":7.70801, "heading":-1.5708, "vx":-1.97656, "vy":0.30169, "omega":0.0, "ax":-0.59746, "ay":-8.06756, "alpha":0.0, "fx":[-10.16255,-10.16255,-10.16255,-10.16255], "fy":[-137.22681,-137.22681,-137.22681,-137.22681]}, - {"t":4.41647, "x":5.35331, "y":7.71364, "heading":-1.5708, "vx":-1.99946, "vy":-0.00752, "omega":0.0, "ax":0.02318, "ay":-1.37264, "alpha":0.0, "fx":[0.39435,0.39435,0.39435,0.39435], "fy":[-23.34828,-23.34828,-23.34828,-23.34828]}, - {"t":4.4548, "x":5.27669, "y":7.71235, "heading":-1.5708, "vx":-1.99857, "vy":-0.06013, "omega":0.0, "ax":-0.00576, "ay":0.20502, "alpha":0.0, "fx":[-0.09803,-0.09803,-0.09803,-0.09803], "fy":[3.48728,3.48728,3.48728,3.48728]}, - {"t":4.49313, "x":5.20009, "y":7.71019, "heading":-1.5708, "vx":-1.99879, "vy":-0.05228, "omega":0.0, "ax":-0.0058, "ay":0.24366, "alpha":0.0, "fx":[-0.09869,-0.09869,-0.09869,-0.09869], "fy":[4.14465,4.14465,4.14465,4.14465]}, - {"t":4.53146, "x":5.12347, "y":7.70837, "heading":-1.5708, "vx":-1.99902, "vy":-0.04294, "omega":0.0, "ax":-0.00417, "ay":0.21468, "alpha":0.0, "fx":[-0.0709,-0.0709,-0.0709,-0.0709], "fy":[3.65169,3.65169,3.65169,3.65169]}, - {"t":4.56978, "x":5.04685, "y":7.70688, "heading":-1.5708, "vx":-1.99918, "vy":-0.03471, "omega":0.0, "ax":-0.00299, "ay":0.19311, "alpha":0.0, "fx":[-0.05093,-0.05093,-0.05093,-0.05093], "fy":[3.28476,3.28476,3.28476,3.28476]}, - {"t":4.60811, "x":4.97022, "y":7.70569, "heading":-1.5708, "vx":-1.99929, "vy":-0.02731, "omega":0.0, "ax":-0.00213, "ay":0.17862, "alpha":0.0, "fx":[-0.03629,-0.03629,-0.03629,-0.03629], "fy":[3.03829,3.03829,3.03829,3.03829]}, - {"t":4.64644, "x":4.89359, "y":7.70478, "heading":-1.5708, "vx":-1.99937, "vy":-0.02046, "omega":0.0, "ax":-0.00146, "ay":0.16898, "alpha":0.0, "fx":[-0.02475,-0.02475,-0.02475,-0.02475], "fy":[2.87428,2.87428,2.87428,2.87428]}, - {"t":4.68477, "x":4.81696, "y":7.70412, "heading":-1.5708, "vx":-1.99943, "vy":-0.01398, "omega":0.0, "ax":-0.00088, "ay":0.1629, "alpha":0.0, "fx":[-0.01505,-0.01505,-0.01505,-0.01505], "fy":[2.77087,2.77087,2.77087,2.77087]}, - {"t":4.7231, "x":4.74032, "y":7.7037, "heading":-1.5708, "vx":-1.99946, "vy":-0.00774, "omega":0.0, "ax":-0.00037, "ay":0.15969, "alpha":0.0, "fx":[-0.00636,-0.00636,-0.00636,-0.00636], "fy":[2.71632,2.71632,2.71632,2.71632]}, - {"t":4.76143, "x":4.66369, "y":7.70352, "heading":-1.5708, "vx":-1.99948, "vy":-0.00162, "omega":0.0, "ax":0.00011, "ay":0.15903, "alpha":0.0, "fx":[0.00193,0.00193,0.00193,0.00193], "fy":[2.70508,2.70508,2.70508,2.70508]}, - {"t":4.79975, "x":4.58705, "y":7.70358, "heading":-1.5708, "vx":-1.99947, "vy":0.00448, "omega":0.0, "ax":0.00061, "ay":0.16085, "alpha":0.0, "fx":[0.01034,0.01034,0.01034,0.01034], "fy":[2.73607,2.73607,2.73607,2.73607]}, - {"t":4.83808, "x":4.51041, "y":7.70387, "heading":-1.5708, "vx":-1.99945, "vy":0.01064, "omega":0.0, "ax":0.00114, "ay":0.16534, "alpha":0.0, "fx":[0.01942,0.01942,0.01942,0.01942], "fy":[2.81234,2.81234,2.81234,2.81234]}, - {"t":4.87641, "x":4.43378, "y":7.70439, "heading":-1.5708, "vx":-1.99941, "vy":0.01698, "omega":0.0, "ax":0.00175, "ay":0.17296, "alpha":0.0, "fx":[0.02985,0.02985,0.02985,0.02985], "fy":[2.94201,2.94201,2.94201,2.94201]}, - {"t":4.91474, "x":4.35715, "y":7.70517, "heading":-1.5708, "vx":-1.99934, "vy":0.02361, "omega":0.0, "ax":0.00251, "ay":0.18465, "alpha":0.0, "fx":[0.04263,0.04263,0.04263,0.04263], "fy":[3.1409,3.1409,3.1409,3.1409]}, - {"t":4.95307, "x":4.28052, "y":7.70621, "heading":-1.5708, "vx":-1.99924, "vy":0.03069, "omega":0.0, "ax":0.00349, "ay":0.20218, "alpha":0.0, "fx":[0.05943,0.05943,0.05943,0.05943], "fy":[3.43895,3.43895,3.43895,3.43895]}, - {"t":4.9914, "x":4.20389, "y":7.70754, "heading":-1.5708, "vx":-1.99911, "vy":0.03843, "omega":0.0, "ax":0.00491, "ay":0.22905, "alpha":0.0, "fx":[0.08344,0.08344,0.08344,0.08344], "fy":[3.89616,3.89616,3.89616,3.89616]}, - {"t":5.02972, "x":4.12727, "y":7.70918, "heading":-1.5708, "vx":-1.99892, "vy":0.04721, "omega":0.0, "ax":0.00716, "ay":0.27308, "alpha":0.0, "fx":[0.12185,0.12185,0.12185,0.12185], "fy":[4.64507,4.64507,4.64507,4.64507]}, - {"t":5.06805, "x":4.05066, "y":7.71119, "heading":-1.5708, "vx":-1.99865, "vy":0.05768, "omega":0.0, "ax":0.01124, "ay":0.34918, "alpha":0.0, "fx":[0.19127,0.19127,0.19127,0.19127], "fy":[5.93953,5.93953,5.93953,5.93953]}, - {"t":5.10638, "x":3.97407, "y":7.71366, "heading":-1.5708, "vx":-1.99821, "vy":0.07106, "omega":0.0, "ax":0.01449, "ay":0.37054, "alpha":0.0, "fx":[0.24649,0.24649,0.24649,0.24649], "fy":[6.30273,6.30273,6.30273,6.30273]}, - {"t":5.14471, "x":3.89749, "y":7.71665, "heading":-1.5708, "vx":-1.99766, "vy":0.08527, "omega":0.0, "ax":-0.05492, "ay":-2.57783, "alpha":0.0, "fx":[-0.93411,-0.93411,-0.93411,-0.93411], "fy":[-43.84819,-43.84819,-43.84819,-43.84819]}, - {"t":5.18304, "x":3.82088, "y":7.71803, "heading":-1.5708, "vx":-1.99976, "vy":-0.01354, "omega":0.0, "ax":-9.39749, "ay":-0.13667, "alpha":0.0, "fx":[-159.84864,-159.84864,-159.84864,-159.84864], "fy":[-2.32471,-2.32471,-2.32471,-2.32471]}, - {"t":5.20912, "x":3.76552, "y":7.71763, "heading":-1.5708, "vx":-2.24488, "vy":-0.0171, "omega":0.0, "ax":-9.38925, "ay":0.00854, "alpha":0.0, "fx":[-159.70842,-159.70842,-159.70842,-159.70842], "fy":[0.14528,0.14528,0.14528,0.14528]}, - {"t":5.2352, "x":3.70378, "y":7.71718, "heading":-1.5708, "vx":-2.48978, "vy":-0.01688, "omega":0.0, "ax":-9.37308, "ay":0.1521, "alpha":0.0, "fx":[-159.43339,-159.43339,-159.43339,-159.43339], "fy":[2.58714,2.58714,2.58714,2.58714]}, - {"t":5.26129, "x":3.63565, "y":7.7168, "heading":-1.5708, "vx":-2.73426, "vy":-0.01291, "omega":0.0, "ax":-9.34091, "ay":0.30892, "alpha":0.0, "fx":[-158.88624,-158.88624,-158.88624,-158.88624], "fy":[5.25463,5.25463,5.25463,5.25463]}, - {"t":5.28737, "x":3.56115, "y":7.71656, "heading":-1.5708, "vx":-2.97791, "vy":-0.00485, "omega":0.0, "ax":-9.25706, "ay":0.52845, "alpha":0.0, "fx":[-157.46,-157.46,-157.46,-157.46], "fy":[8.98884,8.98884,8.98884,8.98884]}, - {"t":5.31345, "x":3.48033, "y":7.71662, "heading":-1.5708, "vx":-3.21936, "vy":0.00893, "omega":0.0, "ax":-8.47006, "ay":1.53578, "alpha":0.0, "fx":[-144.07323,-144.07323,-144.07323,-144.07323], "fy":[26.12321,26.12321,26.12321,26.12321]}, - {"t":5.33954, "x":3.39347, "y":7.71737, "heading":-1.5708, "vx":-3.44029, "vy":0.04899, "omega":0.0, "ax":9.15002, "ay":-0.16419, "alpha":0.0, "fx":[155.63923,155.63923,155.63923,155.63923], "fy":[-2.79279,-2.79279,-2.79279,-2.79279]}, - {"t":5.36562, "x":3.30685, "y":7.71859, "heading":-1.5708, "vx":-3.20162, "vy":0.0447, "omega":0.0, "ax":9.29795, "ay":-0.61101, "alpha":0.0, "fx":[158.15552,158.15552,158.15552,158.15552], "fy":[-10.39303,-10.39303,-10.39303,-10.39303]}, - {"t":5.3917, "x":3.22651, "y":7.71955, "heading":-1.5708, "vx":-2.9591, "vy":0.02877, "omega":0.0, "ax":9.29374, "ay":-1.13272, "alpha":0.0, "fx":[158.08392,158.08392,158.08392,158.08392], "fy":[-19.26732,-19.26732,-19.26732,-19.26732]}, - {"t":5.41779, "x":3.15249, "y":7.71992, "heading":-1.5708, "vx":-2.71669, "vy":-0.00078, "omega":0.0, "ax":8.51897, "ay":-3.94351, "alpha":0.0, "fx":[144.90519,144.90519,144.90519,144.90519], "fy":[-67.07804,-67.07804,-67.07804,-67.07804]}, - {"t":5.44387, "x":3.08452, "y":7.71856, "heading":-1.5708, "vx":-2.49449, "vy":-0.10364, "omega":0.0, "ax":7.01557, "ay":-6.26397, "alpha":0.0, "fx":[119.33278,119.33278,119.33278,119.33278], "fy":[-106.54837,-106.54837,-106.54837,-106.54837]}, - {"t":5.46995, "x":3.02185, "y":7.71372, "heading":-1.5708, "vx":-2.3115, "vy":-0.26702, "omega":0.0, "ax":6.09459, "ay":-7.17528, "alpha":0.0, "fx":[103.66727,103.66727,103.66727,103.66727], "fy":[-122.04943,-122.04943,-122.04943,-122.04943]}, - {"t":5.49604, "x":2.96363, "y":7.70432, "heading":-1.5708, "vx":-2.15253, "vy":-0.45418, "omega":0.0, "ax":5.5326, "ay":-7.62355, "alpha":0.0, "fx":[94.10789,94.10789,94.10789,94.10789], "fy":[-129.67443,-129.67443,-129.67443,-129.67443]}, - {"t":5.52212, "x":2.90936, "y":7.68988, "heading":-1.5708, "vx":-2.00823, "vy":-0.65302, "omega":0.0, "ax":3.67293, "ay":-2.04008, "alpha":31.57794, "fx":[160.2749,-1.07906,-0.01655,90.72293], "fy":[3.36408,150.49355,-160.37843,-132.28368]}, - {"t":5.55212, "x":2.85077, "y":7.66937, "heading":-1.5708, "vx":-1.89805, "vy":-0.71422, "omega":0.94727, "ax":5.6451, "ay":-3.12056, "alpha":23.16411, "fx":[160.27303,129.70182,2.38897,91.72222], "fy":[2.04808,77.54801,-160.34142,-131.57433]}, - {"t":5.58212, "x":2.79638, "y":7.64654, "heading":-1.54238, "vx":-1.72871, "vy":-0.80783, "omega":1.64214, "ax":5.88091, "ay":-5.22794, "alpha":15.04208, "fx":[160.23912,137.01131,8.06899,94.81106], "fy":[1.84076,-68.06933,-160.13257,-129.34173]}, - {"t":5.61211, "x":2.74717, "y":7.61995, "heading":-1.49312, "vx":-1.55229, "vy":-0.96466, "omega":2.09337, "ax":5.34129, "ay":-6.06952, "alpha":13.33543, "fx":[160.18697,87.33349,16.46391,99.43098], "fy":[1.83906,-129.55329,-159.45782,-125.79108]}, - {"t":5.64211, "x":2.703, "y":7.58829, "heading":-1.43032, "vx":-1.39206, "vy":-1.14673, "omega":2.4934, "ax":5.36732, "ay":-6.15058, "alpha":12.87051, "fx":[160.12269,72.58434,27.39631,105.08288], "fy":[0.55467,-140.06826,-157.90471,-121.0602]}, - {"t":5.67211, "x":2.66366, "y":7.55112, "heading":-1.35553, "vx":-1.23106, "vy":-1.33123, "omega":2.87949, "ax":5.76214, "ay":-6.04142, "alpha":11.8712, "fx":[159.96948,78.74249,41.64533,111.69213], "fy":[-3.90382,-137.54078,-154.69362,-114.91278]}, - {"t":5.70211, "x":2.62932, "y":7.50847, "heading":-1.26915, "vx":-1.05821, "vy":-1.51246, "omega":3.2356, "ax":6.40715, "ay":-5.81953, "alpha":9.98446, "fx":[159.17955,96.2407,61.15609,119.35889], "fy":[-14.76207,-126.4459,-147.95711,-106.78879]}, - {"t":5.7321, "x":2.60046, "y":7.46048, "heading":-1.17209, "vx":-0.866, "vy":-1.68704, "omega":3.53511, "ax":7.25825, "ay":-5.42473, "alpha":6.67229, "fx":[155.38677,119.22875,90.70634,128.52125], "fy":[-36.66386,-105.48444,-131.68326,-95.26096]}, - {"t":5.7621, "x":2.57775, "y":7.40743, "heading":-1.06604, "vx":-0.64827, "vy":-1.84977, "omega":3.73527, "ax":8.29151, "ay":-4.37024, "alpha":0.47407, "fx":[142.93353,140.99353,139.11423,141.10374], "fy":[-70.65494,-74.40914,-77.95316,-74.32859]}, - {"t":5.7921, "x":2.56203, "y":7.34997, "heading":-0.95399, "vx":-0.39955, "vy":-1.98086, "omega":3.74949, "ax":8.58435, "ay":-3.1888, "alpha":-5.22355, "fx":[129.36459,148.71577,158.67766,147.3112], "fy":[-92.84364,-57.33773,-8.66766,-58.11314]}, - {"t":5.8221, "x":2.55391, "y":7.28912, "heading":-0.84152, "vx":-0.14203, "vy":-2.07652, "omega":3.59279, "ax":8.32828, "ay":-3.06815, "alpha":-7.60524, "fx":[121.33937,150.34345,156.88739,138.07675], "fy":[-102.62258,-52.07231,19.7814,-73.83996]}, - {"t":5.8521, "x":2.5534, "y":7.22545, "heading":-0.73374, "vx":0.1078, "vy":-2.16856, "omega":3.36465, "ax":7.75185, "ay":-3.36032, "alpha":-10.25419, "fx":[115.82025,151.0405,152.2517,108.31458], "fy":[-108.0813,-48.38789,36.9977,-109.16099]}, - {"t":5.88209, "x":2.56012, "y":7.15888, "heading":-0.63281, "vx":0.34033, "vy":-2.26936, "omega":3.05705, "ax":7.11222, "ay":-3.7404, "alpha":-12.38398, "fx":[112.92512,150.36094,149.47757,71.14338], "fy":[-109.71892,-47.16606,35.64891,-133.25652]}, - {"t":5.91209, "x":2.57353, "y":7.08912, "heading":-0.5411, "vx":0.55368, "vy":-2.38156, "omega":2.68556, "ax":6.70858, "ay":-3.72672, "alpha":-13.20064, "fx":[112.58755,148.90599,142.81289,52.13778], "fy":[-106.43635,-43.00731,31.22624,-135.34458]}, - {"t":5.94209, "x":2.59316, "y":7.01601, "heading":-0.46054, "vx":0.75493, "vy":-2.49336, "omega":2.28957, "ax":7.01509, "ay":-0.66147, "alpha":-7.35273, "fx":[124.13684,135.86347,113.84455,103.45353], "fy":[-49.99981,-4.58946,40.06542,-30.48181]}, - {"t":5.97209, "x":2.61896, "y":6.94091, "heading":-0.39186, "vx":0.96536, "vy":-2.5132, "omega":2.069, "ax":-1.59439, "ay":6.5277, "alpha":17.51368, "fx":[-71.63306,-128.46399,36.082,55.53436], "fy":[134.21934,77.17976,96.05344,136.68481]}, - {"t":6.00208, "x":2.6472, "y":6.86846, "heading":-0.32979, "vx":0.91754, "vy":-2.31738, "omega":2.59437, "ax":-1.71786, "ay":6.76532, "alpha":18.77525, "fx":[-83.35926,-140.06048,53.94996,52.58859], "fy":[132.78692,68.1564,113.95976,145.40142]}, - {"t":6.03208, "x":2.67395, "y":6.80199, "heading":-0.25197, "vx":0.866, "vy":-2.11444, "omega":3.15759, "ax":-1.44943, "ay":7.01504, "alpha":19.75953, "fx":[-84.84215,-140.71883,77.05127,49.89238], "fy":[133.44288,70.35193,124.59527,148.90494]}, - {"t":6.06208, "x":2.69928, "y":6.74171, "heading":-0.15725, "vx":0.82252, "vy":-1.904, "omega":3.75033, "ax":-1.27893, "ay":7.20215, "alpha":20.34882, "fx":[-83.54348,-138.00441,86.89362,47.63765], "fy":[135.01682,76.95677,127.15182,150.90061]}, - {"t":6.09208, "x":2.72337, "y":6.68784, "heading":-0.04475, "vx":0.78416, "vy":-1.68796, "omega":4.36075, "ax":-3.48287, "ay":8.65502, "alpha":-1.92282, "fx":[-52.95593,-48.11943,-65.10907,-70.7862], "fy":[149.80324,151.56394,145.15865,142.35155]}, - {"t":6.12208, "x":2.74533, "y":6.6411, "heading":0.08607, "vx":0.67968, "vy":-1.42832, "omega":4.30307, "ax":-3.2762, "ay":8.08488, "alpha":-12.00811, "fx":[-0.60351,-6.23159,-92.51241,-123.56136], "fy":[159.15102,159.61744,130.35497,100.9624]}, - {"t":6.15207, "x":2.76425, "y":6.60189, "heading":0.21515, "vx":0.5814, "vy":-1.1858, "omega":3.94285, "ax":-3.04034, "ay":7.48821, "alpha":-17.41356, "fx":[38.42925,1.90308,-105.81716,-141.37639], "fy":[154.84069,160.01225,120.11175,74.52428]}, - {"t":6.18207, "x":2.78032, "y":6.56969, "heading":0.33343, "vx":0.4902, "vy":-0.96117, "omega":3.42049, "ax":-3.03854, "ay":7.00481, "alpha":-20.43295, "fx":[57.34203,0.94232,-115.17683,-149.84647], "fy":[149.18522,160.16209,111.32543,55.92618]}, - {"t":6.21207, "x":2.79366, "y":6.54401, "heading":0.43603, "vx":0.39905, "vy":-0.75104, "omega":2.80754, "ax":-3.14975, "ay":6.63619, "alpha":-22.132, "fx":[65.17208,-2.69648,-122.25451,-154.52621], "fy":[146.13333,160.22417,103.60214,41.55935]}, - {"t":6.24207, "x":2.80421, "y":6.52446, "heading":0.52025, "vx":0.30456, "vy":-0.55197, "omega":2.14363, "ax":-3.28382, "ay":6.34863, "alpha":-23.16274, "fx":[68.16777,-6.72514,-127.62569,-157.24444], "fy":[144.88673,160.15836,96.97885,29.92936]}, - {"t":6.27207, "x":2.81187, "y":6.51076, "heading":0.58456, "vx":0.20606, "vy":-0.36152, "omega":1.4488, "ax":-3.39774, "ay":6.11831, "alpha":-23.86839, "fx":[69.37984,-10.14199,-131.60309,-158.81287], "fy":[144.39356,160.01463,91.56292,20.3115]}, - {"t":6.30206, "x":2.81652, "y":6.50267, "heading":0.62802, "vx":0.10413, "vy":-0.17799, "omega":0.7328, "ax":-3.47132, "ay":5.93329, "alpha":-24.4285, "fx":[70.31609,-12.44638,-134.39034,-159.66435], "fy":[143.99679,159.87821,87.46494,12.35421]}, - {"t":6.33206, "x":2.81808, "y":6.5, "heading":0.65, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0,0.0,0.0,0.0]}], + {"t":0.0, "x":4.44125, "y":7.69346, "heading":-1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":-0.00002, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[-0.00028,-0.00028,-0.00028,-0.00028]}, + {"t":0.00001, "x":4.44125, "y":7.69346, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":-0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[-0.00025,-0.00025,-0.00025,-0.00025]}, + {"t":0.00002, "x":4.44125, "y":7.69346, "heading":-1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":-0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[-0.00022,-0.00022,-0.00022,-0.00022]}, + {"t":0.00003, "x":4.44125, "y":7.69346, "heading":-1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":-0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[-0.00019,-0.00019,-0.00019,-0.00019]}, + {"t":0.00004, "x":4.44125, "y":7.69346, "heading":-1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":-0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[-0.00016,-0.00016,-0.00016,-0.00016]}, + {"t":0.00005, "x":4.44125, "y":7.69346, "heading":-1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":-0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[-0.00013,-0.00013,-0.00013,-0.00013]}, + {"t":0.00006, "x":4.44125, "y":7.69346, "heading":-1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":-0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[-0.0001,-0.0001,-0.0001,-0.0001]}, + {"t":0.00007, "x":4.44125, "y":7.69346, "heading":-1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[-0.00008,-0.00008,-0.00008,-0.00008]}, + {"t":0.00008, "x":4.44125, "y":7.69346, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[-0.00006,-0.00006,-0.00006,-0.00006]}, + {"t":0.00009, "x":4.44125, "y":7.69346, "heading":-1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[-0.00003,-0.00003,-0.00003,-0.00003]}, + {"t":0.0001, "x":4.44125, "y":7.69346, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[-0.00001,-0.00001,-0.00001,-0.00001]}, + {"t":0.00011, "x":4.44125, "y":7.69346, "heading":-1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.00001,0.00001,0.00001,0.00001]}, + {"t":0.00012, "x":4.44125, "y":7.69346, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.00003,0.00003,0.00003,0.00003]}, + {"t":0.00013, "x":4.44125, "y":7.69346, "heading":-1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.00005,0.00005,0.00005,0.00005]}, + {"t":0.00014, "x":4.44125, "y":7.69346, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.00006,0.00006,0.00006,0.00006]}, + {"t":0.00015, "x":4.44125, "y":7.69346, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.00008,0.00008,0.00008,0.00008]}, + {"t":0.00016, "x":4.44125, "y":7.69346, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.00009,0.00009,0.00009,0.00009]}, + {"t":0.00017, "x":4.44125, "y":7.69346, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0001,0.0001,0.0001,0.0001]}, + {"t":0.00018, "x":4.44125, "y":7.69346, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.00011,0.00011,0.00011,0.00011]}, + {"t":0.00019, "x":4.44125, "y":7.69346, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.00012,0.00012,0.00012,0.00012]}, + {"t":0.0002, "x":4.44125, "y":7.69346, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.00013,0.00013,0.00013,0.00013]}, + {"t":0.00021, "x":4.44125, "y":7.69346, "heading":-1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.00014,0.00014,0.00014,0.00014]}, + {"t":0.00022, "x":4.44125, "y":7.69346, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.00014,0.00014,0.00014,0.00014]}, + {"t":0.00023, "x":4.44125, "y":7.69346, "heading":-1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.00015,0.00015,0.00015,0.00015]}, + {"t":0.00024, "x":4.44125, "y":7.69346, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.00015,0.00015,0.00015,0.00015]}, + {"t":0.00025, "x":4.44125, "y":7.69346, "heading":-1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.00015,0.00015,0.00015,0.00015]}, + {"t":0.00026, "x":4.44125, "y":7.69346, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0,0.0,0.0,0.0]}], "splits":[0] }, "events":[] diff --git a/src/main/deploy/v1_doomspiral/choreo/LEFT_TRENCH_SIMPLE_CROSSES.traj b/src/main/deploy/v1_doomspiral/choreo/LEFT_TRENCH_SIMPLE_CROSSES.traj index c81fdf0e..7e046e92 100644 --- a/src/main/deploy/v1_doomspiral/choreo/LEFT_TRENCH_SIMPLE_CROSSES.traj +++ b/src/main/deploy/v1_doomspiral/choreo/LEFT_TRENCH_SIMPLE_CROSSES.traj @@ -5,13 +5,13 @@ "waypoints":[ {"x":4.441253185272217, "y":7.693458557128906, "heading":-1.5707972442597578, "intervals":19, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, {"x":6.432096481323242, "y":7.694302558898926, "heading":0.0, "intervals":16, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":7.795207023620605, "y":7.227133750915527, "heading":-1.5707963267948966, "intervals":60, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, + {"x":7.795207023620605, "y":7.227133750915527, "heading":-1.5707963267948966, "intervals":86, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, {"x":7.795207023620605, "y":3.657550096511841, "heading":-1.5707963267948966, "intervals":27, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":6.542033672332764, "y":6.716022491455078, "heading":0.0, "intervals":19, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, + {"x":6.630087852478027, "y":6.730510234832764, "heading":0.0, "intervals":20, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, {"x":5.503650665283203, "y":7.689876556396484, "heading":-1.5707963267948966, "intervals":21, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, {"x":3.892171859741211, "y":7.6997504234313965, "heading":0.0, "intervals":13, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":2.9093637466430664, "y":7.689876556396484, "heading":-1.5707963267948966, "intervals":27, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":2.818082809448242, "y":6.5, "heading":0.65, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], + {"x":2.9093637466430664, "y":7.689876556396484, "heading":-1.5707963267948966, "intervals":22, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, + {"x":3.0215272903442383, "y":7.140939712524414, "heading":0.3602010510265898, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], "constraints":[ {"from":"first", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, {"from":"last", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, @@ -20,7 +20,7 @@ {"from":5, "to":7, "data":{"type":"KeepInLane", "props":{"tolerance":0.03550910949707031}}, "enabled":true}, {"from":5, "to":7, "data":{"type":"MaxAngularVelocity", "props":{"max":0.0}}, "enabled":true}, {"from":2, "to":3, "data":{"type":"MaxAngularVelocity", "props":{"max":0.0}}, "enabled":true}, - {"from":2, "to":3, "data":{"type":"MaxVelocity", "props":{"max":1.25}}, "enabled":true}, + {"from":2, "to":3, "data":{"type":"MaxVelocity", "props":{"max":0.85}}, "enabled":true}, {"from":4, "to":6, "data":{"type":"MaxVelocity", "props":{"max":2.0}}, "enabled":true}], "targetDt":0.05 }, @@ -28,13 +28,13 @@ "waypoints":[ {"x":{"exp":"4.441253185272217 m", "val":4.441253185272217}, "y":{"exp":"7.693458557128906 m", "val":7.693458557128906}, "heading":{"exp":"-1.5707972442597578 rad", "val":-1.5707972442597578}, "intervals":19, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, {"x":{"exp":"6.432096481323242 m", "val":6.432096481323242}, "y":{"exp":"7.694302558898926 m", "val":7.694302558898926}, "heading":{"exp":"0 deg", "val":0.0}, "intervals":16, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":{"exp":"7.7952070236206055 m", "val":7.795207023620605}, "y":{"exp":"7.227133750915527 m", "val":7.227133750915527}, "heading":{"exp":"-1.5707963267948966 rad", "val":-1.5707963267948966}, "intervals":60, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, + {"x":{"exp":"7.7952070236206055 m", "val":7.795207023620605}, "y":{"exp":"7.227133750915527 m", "val":7.227133750915527}, "heading":{"exp":"-1.5707963267948966 rad", "val":-1.5707963267948966}, "intervals":86, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, {"x":{"exp":"7.7952070236206055 m", "val":7.795207023620605}, "y":{"exp":"3.657550096511841 m", "val":3.657550096511841}, "heading":{"exp":"-1.5707963267948966 rad", "val":-1.5707963267948966}, "intervals":27, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":{"exp":"6.542033672332764 m", "val":6.542033672332764}, "y":{"exp":"6.716022491455078 m", "val":6.716022491455078}, "heading":{"exp":"0 deg", "val":0.0}, "intervals":19, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, + {"x":{"exp":"6.630087852478027 m", "val":6.630087852478027}, "y":{"exp":"6.730510234832764 m", "val":6.730510234832764}, "heading":{"exp":"0 deg", "val":0.0}, "intervals":20, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, {"x":{"exp":"5.503650665283203 m", "val":5.503650665283203}, "y":{"exp":"7.689876556396484 m", "val":7.689876556396484}, "heading":{"exp":"-90 deg", "val":-1.5707963267948966}, "intervals":21, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, {"x":{"exp":"3.892171859741211 m", "val":3.892171859741211}, "y":{"exp":"7.6997504234313965 m", "val":7.6997504234313965}, "heading":{"exp":"0 deg", "val":0.0}, "intervals":13, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":{"exp":"2.9093637466430664 m", "val":2.9093637466430664}, "y":{"exp":"7.689876556396484 m", "val":7.689876556396484}, "heading":{"exp":"-90 deg", "val":-1.5707963267948966}, "intervals":27, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":{"exp":"2.818082809448242 m", "val":2.818082809448242}, "y":{"exp":"6.5 m", "val":6.5}, "heading":{"exp":"0.65 rad", "val":0.65}, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], + {"x":{"exp":"2.9093637466430664 m", "val":2.9093637466430664}, "y":{"exp":"7.689876556396484 m", "val":7.689876556396484}, "heading":{"exp":"-90 deg", "val":-1.5707963267948966}, "intervals":22, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, + {"x":{"exp":"3.0215272903442383 m", "val":3.0215272903442383}, "y":{"exp":"7.140939712524414 m", "val":7.140939712524414}, "heading":{"exp":"20.638 deg", "val":0.3602010510265898}, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], "constraints":[ {"from":"first", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, {"from":"last", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, @@ -43,7 +43,7 @@ {"from":5, "to":7, "data":{"type":"KeepInLane", "props":{"tolerance":{"exp":"35.50910949707031 mm", "val":0.03550910949707031}}}, "enabled":true}, {"from":5, "to":7, "data":{"type":"MaxAngularVelocity", "props":{"max":{"exp":"0 rad / s", "val":0.0}}}, "enabled":true}, {"from":2, "to":3, "data":{"type":"MaxAngularVelocity", "props":{"max":{"exp":"0 rad / s", "val":0.0}}}, "enabled":true}, - {"from":2, "to":3, "data":{"type":"MaxVelocity", "props":{"max":{"exp":"1.25 m / s", "val":1.25}}}, "enabled":true}, + {"from":2, "to":3, "data":{"type":"MaxVelocity", "props":{"max":{"exp":"INTAKE_VELOCITY", "val":0.55}}}, "enabled":true}, {"from":4, "to":6, "data":{"type":"MaxVelocity", "props":{"max":{"exp":"2 m / s", "val":2.0}}}, "enabled":true}], "targetDt":{ "exp":"0.05 s", @@ -75,211 +75,233 @@ "differentialTrackWidth":0.5588 }, "sampleType":"Swerve", - "waypoints":[0.0,0.75429,1.32559,4.25013,5.41585,6.13177,6.93961,7.30073,8.1093], + "waypoints":[0.0,0.75432,1.32865,5.58393,6.73539,7.47936,8.28691,8.69127,9.3041], "samples":[ - {"t":0.0, "x":4.44125, "y":7.69346, "heading":-1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":9.42645, "ay":-0.13683, "alpha":0.00001, "fx":[160.3413,160.3413,160.34129,160.34129], "fy":[-2.32736,-2.32736,-2.32749,-2.32748]}, - {"t":0.0397, "x":4.44868, "y":7.69335, "heading":-1.5708, "vx":0.37423, "vy":-0.00543, "omega":0.0, "ax":9.42538, "ay":-0.12405, "alpha":0.00001, "fx":[160.32306,160.32306,160.32306,160.32306], "fy":[-2.10994,-2.10994,-2.11006,-2.11006]}, - {"t":0.0794, "x":4.47097, "y":7.69304, "heading":-1.5708, "vx":0.74841, "vy":-0.01036, "omega":0.0, "ax":9.42393, "ay":-0.11139, "alpha":0.00001, "fx":[160.29843,160.29843,160.29843,160.29843], "fy":[-1.8946,-1.8946,-1.89471,-1.89471]}, - {"t":0.1191, "x":4.5081, "y":7.69254, "heading":-1.5708, "vx":1.12253, "vy":-0.01478, "omega":0.0, "ax":9.42193, "ay":-0.09885, "alpha":0.00001, "fx":[160.26436,160.26436,160.26435,160.26435], "fy":[-1.68135,-1.68135,-1.68145,-1.68145]}, - {"t":0.1588, "x":4.56009, "y":7.69187, "heading":-1.5708, "vx":1.49658, "vy":-0.0187, "omega":0.0, "ax":9.41905, "ay":-0.08641, "alpha":0.00001, "fx":[160.21537,160.21537,160.21537,160.21537], "fy":[-1.46973,-1.46973,-1.46983,-1.46983]}, - {"t":0.1985, "x":4.62693, "y":7.69106, "heading":-1.5708, "vx":1.87051, "vy":-0.02213, "omega":0.0, "ax":9.41465, "ay":-0.07398, "alpha":0.00001, "fx":[160.14049,160.14049,160.14049,160.14049], "fy":[-1.25839,-1.25839,-1.25848,-1.25848]}, - {"t":0.2382, "x":4.7086, "y":7.69013, "heading":-1.5708, "vx":2.24427, "vy":-0.02507, "omega":0.0, "ax":9.40722, "ay":-0.06137, "alpha":0.00001, "fx":[160.0141,160.0141,160.0141,160.0141], "fy":[-1.04378,-1.04378,-1.04386,-1.04386]}, - {"t":0.2779, "x":4.80511, "y":7.68908, "heading":-1.5708, "vx":2.61773, "vy":-0.02751, "omega":0.0, "ax":9.39223, "ay":-0.04792, "alpha":0.00001, "fx":[159.75919,159.75919,159.75919,159.75919], "fy":[-0.81501,-0.81501,-0.81508,-0.81508]}, - {"t":0.3176, "x":4.91644, "y":7.68795, "heading":-1.5708, "vx":2.99059, "vy":-0.02941, "omega":0.0, "ax":9.3471, "ay":-0.0305, "alpha":0.0, "fx":[158.99143,158.99143,158.99143,158.99143], "fy":[-0.51882,-0.51882,-0.51886,-0.51886]}, - {"t":0.3573, "x":5.04253, "y":7.68676, "heading":-1.5708, "vx":3.36167, "vy":-0.03062, "omega":0.0, "ax":3.67592, "ay":0.50055, "alpha":-0.00008, "fx":[62.52614,62.52645,62.52643,62.52612], "fy":[8.51387,8.51385,8.51447,8.51449]}, - {"t":0.39699, "x":5.17888, "y":7.68594, "heading":-1.5708, "vx":3.5076, "vy":-0.01075, "omega":0.0, "ax":0.0012, "ay":0.25133, "alpha":0.0, "fx":[0.02035,0.02035,0.02035,0.02035], "fy":[4.27513,4.27513,4.27513,4.27513]}, - {"t":0.43669, "x":5.31813, "y":7.68571, "heading":-1.5708, "vx":3.50765, "vy":-0.00077, "omega":0.0, "ax":-0.00019, "ay":0.20423, "alpha":0.0, "fx":[-0.00327,-0.00327,-0.00327,-0.00327], "fy":[3.47394,3.47394,3.47394,3.47394]}, - {"t":0.47639, "x":5.45738, "y":7.68584, "heading":-1.5708, "vx":3.50764, "vy":0.00734, "omega":0.0, "ax":-0.00064, "ay":0.19956, "alpha":0.0, "fx":[-0.01095,-0.01095,-0.01095,-0.01095], "fy":[3.39438,3.39438,3.39438,3.39438]}, - {"t":0.51609, "x":5.59663, "y":7.68629, "heading":-1.5708, "vx":3.50762, "vy":0.01526, "omega":0.0, "ax":-0.00111, "ay":0.20209, "alpha":0.0, "fx":[-0.01889,-0.01889,-0.01889,-0.01889], "fy":[3.43753,3.43753,3.43753,3.43753]}, - {"t":0.55579, "x":5.73588, "y":7.68706, "heading":-1.5708, "vx":3.50757, "vy":0.02328, "omega":0.0, "ax":-0.00162, "ay":0.20707, "alpha":0.0, "fx":[-0.0275,-0.0275,-0.0275,-0.0275], "fy":[3.52214,3.52214,3.52214,3.52214]}, - {"t":0.59549, "x":5.87513, "y":7.68814, "heading":-1.5708, "vx":3.50751, "vy":0.0315, "omega":0.0, "ax":-0.00216, "ay":0.21202, "alpha":0.0, "fx":[-0.0367,-0.0367,-0.0367,-0.0367], "fy":[3.60634,3.60634,3.60634,3.60634]}, - {"t":0.63519, "x":6.01438, "y":7.68956, "heading":-1.5708, "vx":3.50742, "vy":0.03992, "omega":0.0, "ax":-0.00251, "ay":0.20073, "alpha":0.0, "fx":[-0.04271,-0.04271,-0.04271,-0.04271], "fy":[3.41433,3.41433,3.41433,3.41433]}, - {"t":0.67489, "x":6.15362, "y":7.6913, "heading":-1.5708, "vx":3.50732, "vy":0.04789, "omega":0.0, "ax":-0.00068, "ay":0.04852, "alpha":0.0, "fx":[-0.01149,-0.01149,-0.01149,-0.01149], "fy":[0.82527,0.82527,0.82527,0.82527]}, - {"t":0.71459, "x":6.29286, "y":7.69324, "heading":-1.5708, "vx":3.50729, "vy":0.04981, "omega":0.0, "ax":0.00421, "ay":-1.16511, "alpha":0.0, "fx":[0.07164,0.07164,0.07164,0.07164], "fy":[-19.81826,-19.81826,-19.81826,-19.81826]}, - {"t":0.75429, "x":6.4321, "y":7.6943, "heading":-1.5708, "vx":3.50746, "vy":0.00356, "omega":0.0, "ax":-0.01323, "ay":-1.31928, "alpha":0.0, "fx":[-0.22502,-0.22502,-0.22502,-0.22502], "fy":[-22.44054,-22.44054,-22.44054,-22.44054]}, - {"t":0.79, "x":6.55733, "y":7.69359, "heading":-1.5708, "vx":3.50699, "vy":-0.04355, "omega":0.0, "ax":-0.00357, "ay":-0.25908, "alpha":0.0, "fx":[-0.06065,-0.06065,-0.06065,-0.06065], "fy":[-4.4069,-4.4069,-4.4069,-4.4069]}, - {"t":0.8257, "x":6.68255, "y":7.69187, "heading":-1.5708, "vx":3.50686, "vy":-0.0528, "omega":0.0, "ax":-0.02892, "ay":-1.32278, "alpha":0.0, "fx":[-0.49193,-0.49193,-0.49193,-0.49193], "fy":[-22.50004,-22.50004,-22.50004,-22.50004]}, - {"t":0.86141, "x":6.80774, "y":7.68914, "heading":-1.5708, "vx":3.50583, "vy":-0.10003, "omega":0.0, "ax":-0.32715, "ay":-5.67347, "alpha":0.0, "fx":[-5.56467,-5.56467,-5.56467,-5.56467], "fy":[-96.50402,-96.50402,-96.50402,-96.50402]}, - {"t":0.89712, "x":6.93272, "y":7.68195, "heading":-1.5708, "vx":3.49415, "vy":-0.30261, "omega":0.0, "ax":-1.08691, "ay":-8.33701, "alpha":0.0, "fx":[-18.48799,-18.488,-18.488,-18.48799], "fy":[-141.81014,-141.81014,-141.81014,-141.81014]}, - {"t":0.93282, "x":7.05679, "y":7.66583, "heading":-1.5708, "vx":3.45534, "vy":-0.60029, "omega":0.0, "ax":-2.27599, "ay":-8.74219, "alpha":0.0, "fx":[-38.71393,-38.71398,-38.71396,-38.71391], "fy":[-148.70222,-148.7022,-148.70221,-148.70222]}, - {"t":0.96853, "x":7.17871, "y":7.63883, "heading":-1.5708, "vx":3.37407, "vy":-0.91244, "omega":0.0, "ax":-8.69518, "ay":-3.33845, "alpha":0.00001, "fx":[-147.90262,-147.90263,-147.90259,-147.90258], "fy":[-56.78604,-56.78601,-56.78611,-56.78614]}, - {"t":1.00423, "x":7.29365, "y":7.60412, "heading":-1.5708, "vx":3.0636, "vy":-1.03165, "omega":0.0, "ax":-9.23645, "ay":-1.622, "alpha":0.0, "fx":[-157.10941,-157.10941,-157.10941,-157.1094], "fy":[-27.58972,-27.58972,-27.58975,-27.58975]}, - {"t":1.03994, "x":7.39715, "y":7.56625, "heading":-1.5708, "vx":2.7338, "vy":-1.08956, "omega":0.0, "ax":-9.33881, "ay":-1.05752, "alpha":0.0, "fx":[-158.85043,-158.85043,-158.85043,-158.85043], "fy":[-17.98811,-17.98811,-17.98811,-17.98811]}, - {"t":1.07565, "x":7.48881, "y":7.52667, "heading":-1.5708, "vx":2.40035, "vy":-1.12732, "omega":0.0, "ax":-9.37604, "ay":-0.78178, "alpha":0.0, "fx":[-159.48381,-159.48381,-159.48381,-159.48381], "fy":[-13.29778,-13.29778,-13.29777,-13.29777]}, - {"t":1.11135, "x":7.56854, "y":7.48592, "heading":-1.5708, "vx":2.06556, "vy":-1.15524, "omega":0.0, "ax":-9.39414, "ay":-0.61926, "alpha":0.0, "fx":[-159.79161,-159.79161,-159.79161,-159.79161], "fy":[-10.53337,-10.53337,-10.53335,-10.53335]}, - {"t":1.14706, "x":7.6363, "y":7.44427, "heading":-1.5708, "vx":1.73013, "vy":-1.17735, "omega":0.0, "ax":-9.40449, "ay":-0.51246, "alpha":0.0, "fx":[-159.96776,-159.96776,-159.96776,-159.96776], "fy":[-8.71678,-8.71678,-8.71675,-8.71675]}, - {"t":1.18277, "x":7.69209, "y":7.40191, "heading":-1.5708, "vx":1.39433, "vy":-1.19564, "omega":0.0, "ax":-9.41108, "ay":-0.43712, "alpha":0.0, "fx":[-160.0797,-160.0797,-160.0797,-160.0797], "fy":[-7.43523,-7.43523,-7.4352,-7.4352]}, - {"t":1.21847, "x":7.73587, "y":7.35894, "heading":-1.5708, "vx":1.0583, "vy":-1.21125, "omega":0.0, "ax":-9.41557, "ay":-0.38124, "alpha":0.0, "fx":[-160.15621,-160.15621,-160.15621,-160.15621], "fy":[-6.48485,-6.48485,-6.48482,-6.48482]}, - {"t":1.25418, "x":7.76766, "y":7.31545, "heading":-1.5708, "vx":0.7221, "vy":-1.22487, "omega":0.0, "ax":-9.41882, "ay":-0.33825, "alpha":0.0, "fx":[-160.21138,-160.21138,-160.21138,-160.21139], "fy":[-5.75351,-5.75351,-5.75348,-5.75348]}, - {"t":1.28988, "x":7.78744, "y":7.27149, "heading":-1.5708, "vx":0.38579, "vy":-1.23694, "omega":0.0, "ax":-9.42125, "ay":-0.30421, "alpha":0.0, "fx":[-160.25282,-160.25282,-160.25282,-160.25282], "fy":[-5.17449,-5.17449,-5.17446,-5.17445]}, - {"t":1.32559, "x":7.79521, "y":7.22713, "heading":-1.5708, "vx":0.04939, "vy":-1.24781, "omega":0.0, "ax":-2.01918, "ay":-0.02007, "alpha":0.0, "fx":[-34.34569,-34.34569,-34.34569,-34.34569], "fy":[-0.34133,-0.34133,-0.34133,-0.34133]}, - {"t":1.37433, "x":7.79522, "y":7.16629, "heading":-1.5708, "vx":-0.04903, "vy":-1.24878, "omega":0.0, "ax":1.99675, "ay":-0.00058, "alpha":0.0, "fx":[33.96412,33.96412,33.96412,33.96412], "fy":[-0.0099,-0.0099,-0.0099,-0.0099]}, - {"t":1.42308, "x":7.7952, "y":7.10542, "heading":-1.5708, "vx":0.0483, "vy":-1.24881, "omega":0.0, "ax":-1.96688, "ay":-0.00057, "alpha":0.0, "fx":[-33.456,-33.456,-33.456,-33.456], "fy":[-0.00974,-0.00974,-0.00974,-0.00974]}, - {"t":1.47182, "x":7.79522, "y":7.04455, "heading":-1.5708, "vx":-0.04757, "vy":-1.24884, "omega":0.0, "ax":1.93706, "ay":-0.00056, "alpha":0.0, "fx":[32.94884,32.94884,32.94884,32.94884], "fy":[-0.00957,-0.00957,-0.00957,-0.00957]}, - {"t":1.52056, "x":7.7952, "y":6.98368, "heading":-1.5708, "vx":0.04685, "vy":-1.24887, "omega":0.0, "ax":-1.9073, "ay":-0.00055, "alpha":0.0, "fx":[-32.44266,-32.44266,-32.44266,-32.44266], "fy":[-0.0094,-0.0094,-0.0094,-0.0094]}, - {"t":1.5693, "x":7.79522, "y":6.9228, "heading":-1.5708, "vx":-0.04612, "vy":-1.24889, "omega":0.0, "ax":1.8776, "ay":-0.00054, "alpha":0.0, "fx":[31.93745,31.93745,31.93745,31.93745], "fy":[-0.00924,-0.00924,-0.00924,-0.00924]}, - {"t":1.61804, "x":7.7952, "y":6.86193, "heading":-1.5708, "vx":0.0454, "vy":-1.24892, "omega":0.0, "ax":-1.84796, "ay":-0.00053, "alpha":0.0, "fx":[-31.43325,-31.43325,-31.43325,-31.43325], "fy":[-0.00908,-0.00908,-0.00908,-0.00908]}, - {"t":1.66679, "x":7.79522, "y":6.80105, "heading":-1.5708, "vx":-0.04468, "vy":-1.24895, "omega":0.0, "ax":1.81838, "ay":-0.00052, "alpha":0.0, "fx":[30.93005,30.93005,30.93005,30.93005], "fy":[-0.00891,-0.00891,-0.00891,-0.00891]}, - {"t":1.71553, "x":7.7952, "y":6.74018, "heading":-1.5708, "vx":0.04396, "vy":-1.24897, "omega":0.0, "ax":-1.78885, "ay":-0.00051, "alpha":0.0, "fx":[-30.42788,-30.42788,-30.42788,-30.42788], "fy":[-0.00875,-0.00875,-0.00875,-0.00875]}, - {"t":1.76427, "x":7.79522, "y":6.6793, "heading":-1.5708, "vx":-0.04324, "vy":-1.249, "omega":0.0, "ax":1.75939, "ay":-0.0005, "alpha":0.0, "fx":[29.92675,29.92675,29.92675,29.92675], "fy":[-0.00859,-0.00859,-0.00859,-0.00859]}, - {"t":1.81301, "x":7.7952, "y":6.61842, "heading":-1.5708, "vx":0.04252, "vy":-1.24902, "omega":0.0, "ax":-1.72999, "ay":-0.0005, "alpha":0.0, "fx":[-29.42666,-29.42666,-29.42666,-29.42666], "fy":[-0.00843,-0.00843,-0.00843,-0.00843]}, - {"t":1.86176, "x":7.79522, "y":6.55754, "heading":-1.5708, "vx":-0.0418, "vy":-1.24905, "omega":0.0, "ax":1.70065, "ay":-0.00049, "alpha":0.0, "fx":[28.92764,28.92764,28.92764,28.92764], "fy":[-0.00827,-0.00827,-0.00827,-0.00827]}, - {"t":1.9105, "x":7.7952, "y":6.49665, "heading":-1.5708, "vx":0.04109, "vy":-1.24907, "omega":0.0, "ax":-1.67138, "ay":-0.00048, "alpha":0.0, "fx":[-28.42969,-28.42969,-28.42969,-28.42969], "fy":[-0.00811,-0.00811,-0.00811,-0.00811]}, - {"t":1.95924, "x":7.79522, "y":6.43577, "heading":-1.5708, "vx":-0.04038, "vy":-1.24909, "omega":0.0, "ax":1.64217, "ay":-0.00047, "alpha":0.0, "fx":[27.93283,27.93283,27.93283,27.93283], "fy":[-0.00795,-0.00795,-0.00795,-0.00795]}, - {"t":2.00798, "x":7.7952, "y":6.37489, "heading":-1.5708, "vx":0.03967, "vy":-1.24912, "omega":0.0, "ax":-1.61302, "ay":-0.00046, "alpha":0.0, "fx":[-27.43707,-27.43707,-27.43707,-27.43707], "fy":[-0.00779,-0.00779,-0.00779,-0.00779]}, - {"t":2.05673, "x":7.79522, "y":6.314, "heading":-1.5708, "vx":-0.03896, "vy":-1.24914, "omega":0.0, "ax":1.58394, "ay":-0.00045, "alpha":0.0, "fx":[26.94242,26.94242,26.94242,26.94242], "fy":[-0.00763,-0.00763,-0.00763,-0.00763]}, - {"t":2.10547, "x":7.7952, "y":6.25312, "heading":-1.5708, "vx":0.03825, "vy":-1.24916, "omega":0.0, "ax":-1.55493, "ay":-0.00044, "alpha":0.0, "fx":[-26.44889,-26.44889,-26.44889,-26.44889], "fy":[-0.00747,-0.00747,-0.00747,-0.00747]}, - {"t":2.15421, "x":7.79522, "y":6.19223, "heading":-1.5708, "vx":-0.03754, "vy":-1.24918, "omega":0.0, "ax":1.52598, "ay":-0.00043, "alpha":0.0, "fx":[25.9565,25.9565,25.9565,25.9565], "fy":[-0.00732,-0.00732,-0.00732,-0.00732]}, - {"t":2.20295, "x":7.7952, "y":6.13134, "heading":-1.5708, "vx":0.03684, "vy":-1.2492, "omega":0.0, "ax":-1.4971, "ay":-0.00042, "alpha":0.0, "fx":[-25.46525,-25.46525,-25.46525,-25.46525], "fy":[-0.00716,-0.00716,-0.00716,-0.00716]}, - {"t":2.2517, "x":7.79522, "y":6.07045, "heading":-1.5708, "vx":-0.03613, "vy":-1.24922, "omega":0.0, "ax":1.46829, "ay":-0.00041, "alpha":0.0, "fx":[24.97515,24.97515,24.97515,24.97515], "fy":[-0.00701,-0.00701,-0.00701,-0.00701]}, - {"t":2.30044, "x":7.7952, "y":6.00956, "heading":-1.5708, "vx":0.03543, "vy":-1.24924, "omega":0.0, "ax":-1.43954, "ay":-0.0004, "alpha":0.0, "fx":[-24.48621,-24.48621,-24.48621,-24.48621], "fy":[-0.00685,-0.00685,-0.00685,-0.00685]}, - {"t":2.34918, "x":7.79522, "y":5.94867, "heading":-1.5708, "vx":-0.03473, "vy":-1.24926, "omega":0.0, "ax":1.41087, "ay":-0.00039, "alpha":0.0, "fx":[23.99845,23.99845,23.99845,23.99845], "fy":[-0.0067,-0.0067,-0.0067,-0.0067]}, - {"t":2.39792, "x":7.7952, "y":5.88778, "heading":-1.5708, "vx":0.03404, "vy":-1.24928, "omega":0.0, "ax":-1.38226, "ay":-0.00039, "alpha":0.0, "fx":[-23.51187,-23.51187,-23.51187,-23.51187], "fy":[-0.00655,-0.00655,-0.00655,-0.00655]}, - {"t":2.44666, "x":7.79522, "y":5.82688, "heading":-1.5708, "vx":-0.03334, "vy":-1.2493, "omega":0.0, "ax":1.35372, "ay":-0.00038, "alpha":0.0, "fx":[23.02647,23.02647,23.02647,23.02647], "fy":[-0.0064,-0.0064,-0.0064,-0.0064]}, - {"t":2.49541, "x":7.7952, "y":5.76599, "heading":-1.5708, "vx":0.03264, "vy":-1.24932, "omega":0.0, "ax":-1.32526, "ay":-0.00037, "alpha":0.0, "fx":[-22.54228,-22.54228,-22.54228,-22.54228], "fy":[-0.00625,-0.00625,-0.00625,-0.00625]}, - {"t":2.54415, "x":7.79522, "y":5.70509, "heading":-1.5708, "vx":-0.03195, "vy":-1.24934, "omega":0.0, "ax":1.29686, "ay":-0.00036, "alpha":0.0, "fx":[22.05928,22.05928,22.05928,22.05928], "fy":[-0.0061,-0.0061,-0.0061,-0.0061]}, - {"t":2.59289, "x":7.7952, "y":5.6442, "heading":-1.5708, "vx":0.03126, "vy":-1.24935, "omega":0.0, "ax":-1.26854, "ay":-0.00035, "alpha":0.0, "fx":[-21.5775,-21.5775,-21.5775,-21.5775], "fy":[-0.00595,-0.00595,-0.00595,-0.00595]}, - {"t":2.64163, "x":7.79522, "y":5.5833, "heading":-1.5708, "vx":-0.03057, "vy":-1.24937, "omega":0.0, "ax":1.24029, "ay":-0.00034, "alpha":0.0, "fx":[21.09693,21.09693,21.09693,21.09693], "fy":[-0.0058,-0.0058,-0.0058,-0.0058]}, - {"t":2.69038, "x":7.7952, "y":5.5224, "heading":-1.5708, "vx":0.02988, "vy":-1.24939, "omega":0.0, "ax":-1.21211, "ay":-0.00033, "alpha":0.0, "fx":[-20.61758,-20.61758,-20.61758,-20.61758], "fy":[-0.00566,-0.00566,-0.00566,-0.00566]}, - {"t":2.73912, "x":7.79522, "y":5.4615, "heading":-1.5708, "vx":-0.0292, "vy":-1.2494, "omega":0.0, "ax":1.184, "ay":-0.00032, "alpha":0.0, "fx":[20.13944,20.13944,20.13944,20.13944], "fy":[-0.00551,-0.00551,-0.00551,-0.00551]}, - {"t":2.78786, "x":7.7952, "y":5.4006, "heading":-1.5708, "vx":0.02851, "vy":-1.24942, "omega":0.0, "ax":-1.15596, "ay":-0.00032, "alpha":0.0, "fx":[-19.66254,-19.66254,-19.66254,-19.66254], "fy":[-0.00537,-0.00537,-0.00537,-0.00537]}, - {"t":2.8366, "x":7.79522, "y":5.3397, "heading":-1.5708, "vx":-0.02783, "vy":-1.24944, "omega":0.0, "ax":1.12799, "ay":-0.00031, "alpha":0.0, "fx":[19.18686,19.18686,19.18686,19.18686], "fy":[-0.00522,-0.00522,-0.00522,-0.00522]}, - {"t":2.88535, "x":7.7952, "y":5.2788, "heading":-1.5708, "vx":0.02715, "vy":-1.24945, "omega":0.0, "ax":-1.1001, "ay":-0.0003, "alpha":0.0, "fx":[-18.7124,-18.7124,-18.7124,-18.7124], "fy":[-0.00508,-0.00508,-0.00508,-0.00508]}, - {"t":2.93409, "x":7.79522, "y":5.2179, "heading":-1.5708, "vx":-0.02647, "vy":-1.24946, "omega":0.0, "ax":1.07228, "ay":-0.00029, "alpha":0.0, "fx":[18.23918,18.23918,18.23918,18.23918], "fy":[-0.00494,-0.00494,-0.00494,-0.00494]}, - {"t":2.98283, "x":7.7952, "y":5.157, "heading":-1.5708, "vx":0.02579, "vy":-1.24948, "omega":0.0, "ax":-1.04453, "ay":-0.00028, "alpha":0.0, "fx":[-17.76717,-17.76717,-17.76717,-17.76717], "fy":[-0.0048,-0.0048,-0.0048,-0.0048]}, - {"t":3.03157, "x":7.79522, "y":5.0961, "heading":-1.5708, "vx":-0.02512, "vy":-1.24949, "omega":0.0, "ax":1.01685, "ay":-0.00027, "alpha":0.0, "fx":[17.29639,17.29639,17.29639,17.29639], "fy":[-0.00466,-0.00466,-0.00466,-0.00466]}, - {"t":3.08031, "x":7.7952, "y":5.03519, "heading":-1.5708, "vx":0.02445, "vy":-1.24951, "omega":0.0, "ax":-0.98925, "ay":-0.00027, "alpha":0.0, "fx":[-16.82683,-16.82683,-16.82683,-16.82683], "fy":[-0.00452,-0.00452,-0.00452,-0.00452]}, - {"t":3.12906, "x":7.79522, "y":4.97429, "heading":-1.5708, "vx":-0.02377, "vy":-1.24952, "omega":0.0, "ax":0.96171, "ay":-0.00026, "alpha":0.0, "fx":[16.35848,16.35848,16.35848,16.35848], "fy":[-0.00439,-0.00439,-0.00439,-0.00439]}, - {"t":3.1778, "x":7.7952, "y":4.91339, "heading":-1.5708, "vx":0.0231, "vy":-1.24953, "omega":0.0, "ax":-0.93425, "ay":-0.00025, "alpha":0.0, "fx":[-15.89134,-15.89134,-15.89134,-15.89134], "fy":[-0.00425,-0.00425,-0.00425,-0.00425]}, - {"t":3.22654, "x":7.79522, "y":4.85248, "heading":-1.5708, "vx":-0.02243, "vy":-1.24954, "omega":0.0, "ax":0.90686, "ay":-0.00024, "alpha":0.0, "fx":[15.42539,15.42539,15.42539,15.42539], "fy":[-0.00411,-0.00411,-0.00411,-0.00411]}, - {"t":3.27528, "x":7.7952, "y":4.79157, "heading":-1.5708, "vx":0.02177, "vy":-1.24956, "omega":0.0, "ax":-0.87953, "ay":-0.00023, "alpha":0.0, "fx":[-14.96063,-14.96063,-14.96063,-14.96063], "fy":[-0.00398,-0.00398,-0.00398,-0.00398]}, - {"t":3.32403, "x":7.79522, "y":4.73067, "heading":-1.5708, "vx":-0.0211, "vy":-1.24957, "omega":0.0, "ax":0.85228, "ay":-0.00023, "alpha":0.0, "fx":[14.49705,14.49705,14.49705,14.49705], "fy":[-0.00385,-0.00385,-0.00385,-0.00385]}, - {"t":3.37277, "x":7.7952, "y":4.66976, "heading":-1.5708, "vx":0.02044, "vy":-1.24958, "omega":0.0, "ax":-0.8251, "ay":-0.00022, "alpha":0.0, "fx":[-14.03464,-14.03464,-14.03464,-14.03464], "fy":[-0.00371,-0.00371,-0.00371,-0.00371]}, - {"t":3.42151, "x":7.79522, "y":4.60885, "heading":-1.5708, "vx":-0.01978, "vy":-1.24959, "omega":0.0, "ax":0.79798, "ay":-0.00021, "alpha":0.0, "fx":[13.57338,13.57338,13.57338,13.57338], "fy":[-0.00358,-0.00358,-0.00358,-0.00358]}, - {"t":3.47025, "x":7.7952, "y":4.54794, "heading":-1.5708, "vx":0.01912, "vy":-1.2496, "omega":0.0, "ax":-0.77093, "ay":-0.0002, "alpha":0.0, "fx":[-13.11326,-13.11326,-13.11326,-13.11326], "fy":[-0.00345,-0.00345,-0.00345,-0.00345]}, - {"t":3.519, "x":7.79522, "y":4.48704, "heading":-1.5708, "vx":-0.01846, "vy":-1.24961, "omega":0.0, "ax":0.74394, "ay":-0.0002, "alpha":0.0, "fx":[12.65426,12.65426,12.65426,12.65426], "fy":[-0.00332,-0.00332,-0.00332,-0.00332]}, - {"t":3.56774, "x":7.7952, "y":4.42613, "heading":-1.5708, "vx":0.0178, "vy":-1.24962, "omega":0.0, "ax":-0.71702, "ay":-0.00019, "alpha":0.0, "fx":[-12.19637,-12.19637,-12.19637,-12.19637], "fy":[-0.0032,-0.0032,-0.0032,-0.0032]}, - {"t":3.61648, "x":7.79521, "y":4.36522, "heading":-1.5708, "vx":-0.01715, "vy":-1.24963, "omega":0.0, "ax":0.69017, "ay":-0.00018, "alpha":0.0, "fx":[11.73955,11.73955,11.73955,11.73955], "fy":[-0.00307,-0.00307,-0.00307,-0.00307]}, - {"t":3.66522, "x":7.7952, "y":4.30431, "heading":-1.5708, "vx":0.01649, "vy":-1.24964, "omega":0.0, "ax":-0.66337, "ay":-0.00017, "alpha":0.0, "fx":[-11.2838,-11.2838,-11.2838,-11.2838], "fy":[-0.00294,-0.00294,-0.00294,-0.00294]}, - {"t":3.71397, "x":7.79521, "y":4.2434, "heading":-1.5708, "vx":-0.01584, "vy":-1.24964, "omega":0.0, "ax":0.63664, "ay":-0.00017, "alpha":0.0, "fx":[10.82909,10.82909,10.82909,10.82909], "fy":[-0.00282,-0.00282,-0.00282,-0.00282]}, - {"t":3.76271, "x":7.7952, "y":4.18249, "heading":-1.5708, "vx":0.01519, "vy":-1.24965, "omega":0.0, "ax":-0.60997, "ay":-0.00016, "alpha":0.0, "fx":[-10.37539,-10.37539,-10.37539,-10.37539], "fy":[-0.00269,-0.00269,-0.00269,-0.00269]}, - {"t":3.81145, "x":7.79521, "y":4.12157, "heading":-1.5708, "vx":-0.01454, "vy":-1.24966, "omega":0.0, "ax":0.58335, "ay":-0.00015, "alpha":0.0, "fx":[9.92268,9.92268,9.92268,9.92268], "fy":[-0.00257,-0.00257,-0.00257,-0.00257]}, - {"t":3.86019, "x":7.7952, "y":4.06066, "heading":-1.5708, "vx":0.01389, "vy":-1.24967, "omega":0.0, "ax":-0.5568, "ay":-0.00014, "alpha":0.0, "fx":[-9.47093,-9.47093,-9.47093,-9.47093], "fy":[-0.00245,-0.00245,-0.00245,-0.00245]}, - {"t":3.90893, "x":7.79521, "y":3.99975, "heading":-1.5708, "vx":-0.01325, "vy":-1.24967, "omega":0.0, "ax":0.53029, "ay":-0.00014, "alpha":0.0, "fx":[9.02011,9.02011,9.02011,9.02011], "fy":[-0.00233,-0.00233,-0.00233,-0.00233]}, - {"t":3.95768, "x":7.7952, "y":3.93884, "heading":-1.5708, "vx":0.0126, "vy":-1.24968, "omega":0.0, "ax":-0.50384, "ay":-0.00013, "alpha":0.0, "fx":[-8.57018,-8.57018,-8.57018,-8.57018], "fy":[-0.00221,-0.00221,-0.00221,-0.00221]}, - {"t":4.00642, "x":7.79521, "y":3.87793, "heading":-1.5708, "vx":-0.01196, "vy":-1.24969, "omega":0.0, "ax":0.47744, "ay":-0.00012, "alpha":0.0, "fx":[8.12113,8.12113,8.12113,8.12113], "fy":[-0.00209,-0.00209,-0.00209,-0.00209]}, - {"t":4.05516, "x":7.7952, "y":3.81701, "heading":-1.5708, "vx":0.01131, "vy":-1.24969, "omega":0.0, "ax":-0.45109, "ay":-0.00008, "alpha":0.0, "fx":[-7.67291,-7.67291,-7.67291,-7.67291], "fy":[-0.00141,-0.00141,-0.00141,-0.00141]}, - {"t":4.1039, "x":7.79521, "y":3.7561, "heading":-1.5708, "vx":-0.01067, "vy":-1.2497, "omega":0.0, "ax":0.42481, "ay":6.64556, "alpha":0.0, "fx":[7.22587,7.22587,7.22587,7.22587], "fy":[113.03899,113.03899,113.03899,113.03899]}, - {"t":4.15265, "x":7.7952, "y":3.70308, "heading":-1.5708, "vx":0.01003, "vy":-0.92578, "omega":0.0, "ax":-0.39872, "ay":9.40818, "alpha":0.0, "fx":[-6.78218,-6.78218,-6.78218,-6.78218], "fy":[160.03053,160.03053,160.03053,160.03053]}, - {"t":4.20139, "x":7.79521, "y":3.66913, "heading":-1.5708, "vx":-0.0094, "vy":-0.4672, "omega":0.0, "ax":0.37933, "ay":9.41935, "alpha":0.0, "fx":[6.45233,6.45233,6.45233,6.45233], "fy":[160.22039,160.22039,160.22039,160.22039]}, - {"t":4.25013, "x":7.79521, "y":3.65755, "heading":-1.5708, "vx":0.00909, "vy":-0.00808, "omega":0.0, "ax":-3.47664, "ay":8.76367, "alpha":0.0, "fx":[-59.13673,-59.13673,-59.13673,-59.13673], "fy":[149.06752,149.06752,149.06752,149.06752]}, - {"t":4.29331, "x":7.79236, "y":3.66537, "heading":-1.5708, "vx":-0.14102, "vy":0.37029, "omega":0.0, "ax":-3.47622, "ay":8.76241, "alpha":0.0, "fx":[-59.12956,-59.12956,-59.12956,-59.12956], "fy":[149.04603,149.04603,149.04603,149.04603]}, - {"t":4.33648, "x":7.78303, "y":3.68952, "heading":-1.5708, "vx":-0.2911, "vy":0.7486, "omega":0.0, "ax":-3.47565, "ay":8.76073, "alpha":0.0, "fx":[-59.11987,-59.11987,-59.11987,-59.11987], "fy":[149.01744,149.01744,149.01744,149.01744]}, - {"t":4.37966, "x":7.76722, "y":3.73001, "heading":-1.5708, "vx":-0.44116, "vy":1.12685, "omega":0.0, "ax":-3.47485, "ay":8.75838, "alpha":0.0, "fx":[-59.10617,-59.10617,-59.10617,-59.10617], "fy":[148.97749,148.97749,148.97749,148.97749]}, - {"t":4.42283, "x":7.74494, "y":3.78682, "heading":-1.5708, "vx":-0.59119, "vy":1.50499, "omega":0.0, "ax":-3.47363, "ay":8.75486, "alpha":0.0, "fx":[-59.08543,-59.08543,-59.08543,-59.08543], "fy":[148.9177,148.9177,148.9177,148.9177]}, - {"t":4.466, "x":7.71618, "y":3.85996, "heading":-1.5708, "vx":-0.74116, "vy":1.88297, "omega":0.0, "ax":-3.47159, "ay":8.74902, "alpha":0.0, "fx":[-59.05069,-59.05069,-59.05069,-59.05069], "fy":[148.81831,148.81831,148.81831,148.81831]}, - {"t":4.50918, "x":7.68094, "y":3.94941, "heading":-1.5708, "vx":-0.89104, "vy":2.26071, "omega":0.0, "ax":-3.46749, "ay":8.73738, "alpha":0.0, "fx":[-58.98107,-58.98107,-58.98107,-58.98107], "fy":[148.62036,148.62036,148.62036,148.62036]}, - {"t":4.55235, "x":7.63924, "y":4.05516, "heading":-1.5708, "vx":-1.04075, "vy":2.63795, "omega":0.0, "ax":-3.4553, "ay":8.70288, "alpha":0.0, "fx":[-58.77365,-58.77365,-58.77365,-58.77365], "fy":[148.03351,148.03351,148.03351,148.03351]}, - {"t":4.59553, "x":7.59108, "y":4.17716, "heading":-1.5708, "vx":-1.18993, "vy":3.01369, "omega":0.0, "ax":-2.3783, "ay":5.73001, "alpha":0.0, "fx":[-40.45426,-40.45426,-40.45426,-40.45426], "fy":[97.46582,97.46582,97.46582,97.46582]}, - {"t":4.6387, "x":7.53749, "y":4.31262, "heading":-1.5708, "vx":-1.29262, "vy":3.26108, "omega":0.0, "ax":-0.01817, "ay":-0.00572, "alpha":0.0, "fx":[-0.30912,-0.30912,-0.30912,-0.30912], "fy":[-0.09722,-0.09722,-0.09722,-0.09722]}, - {"t":4.68188, "x":7.48167, "y":4.45341, "heading":-1.5708, "vx":-1.2934, "vy":3.26083, "omega":0.0, "ax":-0.0019, "ay":-0.00075, "alpha":0.0, "fx":[-0.03228,-0.03228,-0.03228,-0.03228], "fy":[-0.01279,-0.01279,-0.01279,-0.01279]}, - {"t":4.72505, "x":7.42582, "y":4.5942, "heading":-1.5708, "vx":-1.29348, "vy":3.2608, "omega":0.0, "ax":-0.00013, "ay":-0.00005, "alpha":0.0, "fx":[-0.00223,-0.00223,-0.00223,-0.00223], "fy":[-0.00086,-0.00086,-0.00086,-0.00086]}, - {"t":4.76823, "x":7.36998, "y":4.73498, "heading":-1.5708, "vx":-1.29349, "vy":3.2608, "omega":0.0, "ax":0.00013, "ay":0.00005, "alpha":0.0, "fx":[0.00213,0.00213,0.00213,0.00213], "fy":[0.00087,0.00087,0.00087,0.00087]}, - {"t":4.8114, "x":7.31413, "y":4.87577, "heading":-1.5708, "vx":-1.29348, "vy":3.2608, "omega":0.0, "ax":0.00022, "ay":0.00009, "alpha":0.0, "fx":[0.00378,0.00378,0.00378,0.00378], "fy":[0.00153,0.00153,0.00153,0.00153]}, - {"t":4.85458, "x":7.25829, "y":5.01655, "heading":-1.5708, "vx":-1.29347, "vy":3.26081, "omega":0.0, "ax":0.00031, "ay":0.00012, "alpha":0.0, "fx":[0.00521,0.00521,0.00521,0.00521], "fy":[0.0021,0.0021,0.0021,0.0021]}, - {"t":4.89775, "x":7.20244, "y":5.15733, "heading":-1.5708, "vx":-1.29346, "vy":3.26081, "omega":0.0, "ax":0.00039, "ay":0.00016, "alpha":0.0, "fx":[0.00671,0.00671,0.00671,0.00671], "fy":[0.0027,0.0027,0.0027,0.0027]}, - {"t":4.94093, "x":7.1466, "y":5.29812, "heading":-1.5708, "vx":-1.29344, "vy":3.26082, "omega":0.0, "ax":0.00049, "ay":0.0002, "alpha":0.0, "fx":[0.00831,0.00831,0.00831,0.00831], "fy":[0.00334,0.00334,0.00334,0.00334]}, - {"t":4.9841, "x":7.09075, "y":5.4389, "heading":-1.5708, "vx":-1.29342, "vy":3.26083, "omega":0.0, "ax":0.00058, "ay":0.00023, "alpha":0.0, "fx":[0.00979,0.00979,0.00979,0.00979], "fy":[0.00393,0.00393,0.00393,0.00393]}, - {"t":5.02728, "x":7.03491, "y":5.57969, "heading":-1.5708, "vx":-1.2934, "vy":3.26084, "omega":0.0, "ax":0.00052, "ay":0.00021, "alpha":0.0, "fx":[0.00892,0.00892,0.00892,0.00892], "fy":[0.0036,0.0036,0.0036,0.0036]}, - {"t":5.07045, "x":6.97907, "y":5.72048, "heading":-1.5708, "vx":-1.29337, "vy":3.26085, "omega":0.0, "ax":-0.00089, "ay":-0.00035, "alpha":0.0, "fx":[-0.01517,-0.01517,-0.01517,-0.01517], "fy":[-0.00595,-0.00595,-0.00595,-0.00595]}, - {"t":5.11363, "x":6.92323, "y":5.86126, "heading":-1.5708, "vx":-1.29341, "vy":3.26083, "omega":0.0, "ax":-0.01505, "ay":-0.00597, "alpha":0.0, "fx":[-0.25598,-0.25598,-0.25598,-0.25598], "fy":[-0.10149,-0.10149,-0.10149,-0.10149]}, - {"t":5.1568, "x":6.86737, "y":6.00204, "heading":-1.5708, "vx":-1.29406, "vy":3.26057, "omega":0.0, "ax":-0.14714, "ay":-0.05856, "alpha":0.0, "fx":[-2.50281,-2.50281,-2.50281,-2.50281], "fy":[-0.9961,-0.9961,-0.9961,-0.9961]}, - {"t":5.19997, "x":6.81136, "y":6.14276, "heading":-1.5708, "vx":-1.30042, "vy":3.25805, "omega":0.0, "ax":-1.33254, "ay":-0.54909, "alpha":0.0, "fx":[-22.66615,-22.66615,-22.66615,-22.66615], "fy":[-9.33988,-9.33988,-9.33988,-9.33988]}, - {"t":5.24315, "x":6.75397, "y":6.28291, "heading":-1.5708, "vx":-1.35795, "vy":3.23434, "omega":0.0, "ax":-0.79994, "ay":-7.94399, "alpha":0.0, "fx":[-13.60668,-13.60668,-13.60668,-13.60668], "fy":[-135.12506,-135.12506,-135.12506,-135.12506]}, - {"t":5.28632, "x":6.6946, "y":6.41515, "heading":-1.5708, "vx":-1.39248, "vy":2.89136, "omega":0.0, "ax":3.2632, "ay":-8.77865, "alpha":0.0, "fx":[55.50603,55.50603,55.50603,55.50603], "fy":[-149.32233,-149.32233,-149.32233,-149.32233]}, - {"t":5.3295, "x":6.63752, "y":6.5318, "heading":-1.5708, "vx":-1.2516, "vy":2.51234, "omega":0.0, "ax":3.36733, "ay":-8.77698, "alpha":0.0, "fx":[57.27724,57.27724,57.27724,57.27724], "fy":[-149.294,-149.294,-149.294,-149.294]}, - {"t":5.37267, "x":6.58662, "y":6.63209, "heading":-1.5708, "vx":-1.10621, "vy":2.1334, "omega":0.0, "ax":3.40321, "ay":-8.77608, "alpha":0.0, "fx":[57.8877,57.8877,57.8877,57.8877], "fy":[-149.27856,-149.27856,-149.27856,-149.27856]}, - {"t":5.41585, "x":6.54203, "y":6.71602, "heading":-1.5708, "vx":-0.95928, "vy":1.75449, "omega":0.0, "ax":-7.55472, "ay":-5.0188, "alpha":0.0, "fx":[-128.5036,-128.5036,-128.5036,-128.5036], "fy":[-85.36833,-85.36833,-85.36833,-85.36833]}, - {"t":5.45353, "x":6.50052, "y":6.77857, "heading":-1.5708, "vx":-1.24394, "vy":1.56539, "omega":0.0, "ax":-4.77022, "ay":-4.28514, "alpha":0.0, "fx":[-81.14007,-81.14007,-81.14007,-81.14007], "fy":[-72.88904,-72.88904,-72.88904,-72.88904]}, - {"t":5.49121, "x":6.45027, "y":6.83451, "heading":-1.5708, "vx":-1.42368, "vy":1.40392, "omega":0.0, "ax":-0.49061, "ay":-0.50414, "alpha":0.0, "fx":[-8.34522,-8.34522,-8.34522,-8.34522], "fy":[-8.57529,-8.57529,-8.57529,-8.57529]}, - {"t":5.52889, "x":6.39627, "y":6.88705, "heading":-1.5708, "vx":-1.44217, "vy":1.38493, "omega":0.0, "ax":-0.02726, "ay":-0.02841, "alpha":0.0, "fx":[-0.46368,-0.46368,-0.46368,-0.46368], "fy":[-0.48319,-0.48319,-0.48319,-0.48319]}, - {"t":5.56657, "x":6.34191, "y":6.93922, "heading":-1.5708, "vx":-1.4432, "vy":1.38386, "omega":0.0, "ax":0.00035, "ay":0.00036, "alpha":0.0, "fx":[0.0059,0.0059,0.0059,0.0059], "fy":[0.00618,0.00618,0.00618,0.00618]}, - {"t":5.60425, "x":6.28753, "y":6.99136, "heading":-1.5708, "vx":-1.44318, "vy":1.38387, "omega":0.0, "ax":0.00213, "ay":0.00222, "alpha":0.0, "fx":[0.03627,0.03627,0.03627,0.03627], "fy":[0.03784,0.03784,0.03784,0.03784]}, - {"t":5.64193, "x":6.23316, "y":7.04351, "heading":-1.5708, "vx":-1.4431, "vy":1.38395, "omega":0.0, "ax":0.0024, "ay":0.00251, "alpha":0.0, "fx":[0.04089,0.04089,0.04089,0.04089], "fy":[0.04266,0.04266,0.04266,0.04266]}, - {"t":5.67961, "x":6.17878, "y":7.09566, "heading":-1.5708, "vx":-1.44301, "vy":1.38405, "omega":0.0, "ax":0.00261, "ay":0.00272, "alpha":0.0, "fx":[0.04442,0.04442,0.04442,0.04442], "fy":[0.04634,0.04634,0.04634,0.04634]}, - {"t":5.71729, "x":6.12441, "y":7.14781, "heading":-1.5708, "vx":-1.44292, "vy":1.38415, "omega":0.0, "ax":0.00285, "ay":0.00297, "alpha":0.0, "fx":[0.04843,0.04843,0.04843,0.04843], "fy":[0.05052,0.05052,0.05052,0.05052]}, - {"t":5.75497, "x":6.07004, "y":7.19996, "heading":-1.5708, "vx":-1.44281, "vy":1.38426, "omega":0.0, "ax":0.00313, "ay":0.00326, "alpha":0.0, "fx":[0.05318,0.05318,0.05318,0.05318], "fy":[0.05545,0.05545,0.05545,0.05545]}, - {"t":5.79265, "x":6.01568, "y":7.25213, "heading":-1.5708, "vx":-1.44269, "vy":1.38438, "omega":0.0, "ax":0.00346, "ay":0.00361, "alpha":0.0, "fx":[0.0589,0.0589,0.0589,0.0589], "fy":[0.06141,0.06141,0.06141,0.06141]}, - {"t":5.83033, "x":5.96132, "y":7.30429, "heading":-1.5708, "vx":-1.44256, "vy":1.38452, "omega":0.0, "ax":0.00388, "ay":0.00404, "alpha":0.0, "fx":[0.06595,0.06595,0.06595,0.06595], "fy":[0.06875,0.06875,0.06875,0.06875]}, - {"t":5.86801, "x":5.90697, "y":7.35646, "heading":-1.5708, "vx":-1.44241, "vy":1.38467, "omega":0.0, "ax":0.00437, "ay":0.00456, "alpha":0.0, "fx":[0.07441,0.07441,0.07441,0.07441], "fy":[0.07755,0.07755,0.07755,0.07755]}, - {"t":5.90569, "x":5.85262, "y":7.40864, "heading":-1.5708, "vx":-1.44225, "vy":1.38484, "omega":0.0, "ax":0.0045, "ay":0.00468, "alpha":0.0, "fx":[0.07647,0.07647,0.07647,0.07647], "fy":[0.07968,0.07968,0.07968,0.07968]}, - {"t":5.94337, "x":5.79828, "y":7.46083, "heading":-1.5708, "vx":-1.44208, "vy":1.38502, "omega":0.0, "ax":-0.00441, "ay":-0.00459, "alpha":0.0, "fx":[-0.07508,-0.07508,-0.07508,-0.07508], "fy":[-0.07813,-0.07813,-0.07813,-0.07813]}, - {"t":5.98105, "x":5.74394, "y":7.51301, "heading":-1.5708, "vx":-1.44225, "vy":1.38485, "omega":0.0, "ax":-0.1694, "ay":-0.17724, "alpha":0.0, "fx":[-2.88146,-2.88146,-2.88146,-2.88146], "fy":[-3.0148,-3.0148,-3.0148,-3.0148]}, - {"t":6.01873, "x":5.68948, "y":7.56507, "heading":-1.5708, "vx":-1.44863, "vy":1.37817, "omega":0.0, "ax":-2.42931, "ay":-2.73676, "alpha":0.0, "fx":[-41.3219,-41.3219,-41.3219,-41.3219], "fy":[-46.55157,-46.55157,-46.55157,-46.55157]}, - {"t":6.05641, "x":5.63317, "y":7.61505, "heading":-1.5708, "vx":-1.54017, "vy":1.27505, "omega":0.0, "ax":-5.00978, "ay":-7.18673, "alpha":0.0, "fx":[-85.2149,-85.2149,-85.2149,-85.2149], "fy":[-122.24427,-122.24427,-122.24427,-122.24427]}, - {"t":6.09409, "x":5.57158, "y":7.65799, "heading":-1.5708, "vx":-1.72893, "vy":1.00425, "omega":0.0, "ax":-3.91975, "ay":-8.39238, "alpha":0.0, "fx":[-66.67384,-66.67384,-66.67384,-66.67384], "fy":[-142.75199,-142.75199,-142.75199,-142.75199]}, - {"t":6.13177, "x":5.50365, "y":7.68988, "heading":-1.5708, "vx":-1.87663, "vy":0.68803, "omega":0.0, "ax":-2.41733, "ay":-8.94148, "alpha":0.0, "fx":[-41.11805,-41.11805,-41.11805,-41.11805], "fy":[-152.09197,-152.09197,-152.09197,-152.09197]}, - {"t":6.17024, "x":5.42967, "y":7.70973, "heading":-1.5708, "vx":-1.96962, "vy":0.34406, "omega":0.0, "ax":-0.77515, "ay":-8.63102, "alpha":0.0, "fx":[-13.18506,-13.18506,-13.18506,-13.18506], "fy":[-146.81123,-146.81123,-146.81123,-146.81123]}, - {"t":6.20871, "x":5.35333, "y":7.71658, "heading":-1.5708, "vx":-1.99944, "vy":0.01204, "omega":0.0, "ax":0.04471, "ay":-2.49323, "alpha":0.0, "fx":[0.76055,0.76055,0.76055,0.76055], "fy":[-42.40915,-42.40915,-42.40915,-42.40915]}, - {"t":6.24717, "x":5.27645, "y":7.7152, "heading":-1.5708, "vx":-1.99772, "vy":-0.08387, "omega":0.0, "ax":-0.00978, "ay":0.24691, "alpha":0.0, "fx":[-0.16628,-0.16628,-0.16628,-0.16628], "fy":[4.19991,4.19991,4.19991,4.19991]}, - {"t":6.28564, "x":5.19959, "y":7.71215, "heading":-1.5708, "vx":-1.9981, "vy":-0.07438, "omega":0.0, "ax":-0.00983, "ay":0.28513, "alpha":0.0, "fx":[-0.16716,-0.16716,-0.16716,-0.16716], "fy":[4.84992,4.84992,4.84992,4.84992]}, - {"t":6.32411, "x":5.12272, "y":7.7095, "heading":-1.5708, "vx":-1.99847, "vy":-0.06341, "omega":0.0, "ax":-0.00677, "ay":0.22941, "alpha":0.0, "fx":[-0.11516,-0.11516,-0.11516,-0.11516], "fy":[3.90214,3.90214,3.90214,3.90214]}, - {"t":6.36258, "x":5.04583, "y":7.70723, "heading":-1.5708, "vx":-1.99873, "vy":-0.05458, "omega":0.0, "ax":-0.00491, "ay":0.19276, "alpha":0.0, "fx":[-0.08343,-0.08343,-0.08343,-0.08343], "fy":[3.27879,3.27879,3.27879,3.27879]}, - {"t":6.40105, "x":4.96894, "y":7.70528, "heading":-1.5708, "vx":-1.99892, "vy":-0.04717, "omega":0.0, "ax":-0.00371, "ay":0.16872, "alpha":0.0, "fx":[-0.06304,-0.06304,-0.06304,-0.06304], "fy":[2.86994,2.86994,2.86994,2.86994]}, - {"t":6.43952, "x":4.89204, "y":7.70359, "heading":-1.5708, "vx":-1.99907, "vy":-0.04068, "omega":0.0, "ax":-0.00287, "ay":0.15191, "alpha":0.0, "fx":[-0.04879,-0.04879,-0.04879,-0.04879], "fy":[2.58402,2.58402,2.58402,2.58402]}, - {"t":6.47799, "x":4.81514, "y":7.70213, "heading":-1.5708, "vx":-1.99918, "vy":-0.03483, "omega":0.0, "ax":-0.00224, "ay":0.13964, "alpha":0.0, "fx":[-0.03819,-0.03819,-0.03819,-0.03819], "fy":[2.37519,2.37519,2.37519,2.37519]}, - {"t":6.51645, "x":4.73823, "y":7.7009, "heading":-1.5708, "vx":-1.99926, "vy":-0.02946, "omega":0.0, "ax":-0.00176, "ay":0.13045, "alpha":0.0, "fx":[-0.02991,-0.02991,-0.02991,-0.02991], "fy":[2.2189,2.2189,2.2189,2.2189]}, - {"t":6.55492, "x":4.66132, "y":7.69986, "heading":-1.5708, "vx":-1.99933, "vy":-0.02444, "omega":0.0, "ax":-0.00136, "ay":0.12351, "alpha":0.0, "fx":[-0.02318,-0.02318,-0.02318,-0.02318], "fy":[2.10091,2.10091,2.10091,2.10091]}, - {"t":6.59339, "x":4.58441, "y":7.69901, "heading":-1.5708, "vx":-1.99938, "vy":-0.01969, "omega":0.0, "ax":-0.00103, "ay":0.11831, "alpha":0.0, "fx":[-0.01753,-0.01753,-0.01753,-0.01753], "fy":[2.01249,2.01249,2.01249,2.01249]}, - {"t":6.63186, "x":4.5075, "y":7.69834, "heading":-1.5708, "vx":-1.99942, "vy":-0.01514, "omega":0.0, "ax":-0.00074, "ay":0.11453, "alpha":0.0, "fx":[-0.0126,-0.0126,-0.0126,-0.0126], "fy":[1.94807,1.94807,1.94807,1.94807]}, - {"t":6.67033, "x":4.43058, "y":7.69784, "heading":-1.5708, "vx":-1.99945, "vy":-0.01073, "omega":0.0, "ax":-0.00048, "ay":0.11194, "alpha":0.0, "fx":[-0.00817,-0.00817,-0.00817,-0.00817], "fy":[1.90411,1.90411,1.90411,1.90411]}, - {"t":6.7088, "x":4.35366, "y":7.69751, "heading":-1.5708, "vx":-1.99947, "vy":-0.00643, "omega":0.0, "ax":-0.00024, "ay":0.11043, "alpha":0.0, "fx":[-0.00405,-0.00405,-0.00405,-0.00405], "fy":[1.87842,1.87842,1.87842,1.87842]}, - {"t":6.74727, "x":4.27675, "y":7.69735, "heading":-1.5708, "vx":-1.99948, "vy":-0.00218, "omega":0.0, "ax":0.0, "ay":0.10993, "alpha":0.0, "fx":[-0.00006,-0.00006,-0.00006,-0.00006], "fy":[1.86991,1.86991,1.86991,1.86991]}, - {"t":6.78574, "x":4.19983, "y":7.69735, "heading":-1.5708, "vx":-1.99948, "vy":0.00205, "omega":0.0, "ax":0.00023, "ay":0.11055, "alpha":0.0, "fx":[0.00392,0.00392,0.00392,0.00392], "fy":[1.88045,1.88045,1.88045,1.88045]}, - {"t":6.8242, "x":4.12291, "y":7.69751, "heading":-1.5708, "vx":-1.99947, "vy":0.0063, "omega":0.0, "ax":0.00049, "ay":0.11466, "alpha":0.0, "fx":[0.00829,0.00829,0.00829,0.00829], "fy":[1.95028,1.95028,1.95028,1.95028]}, - {"t":6.86267, "x":4.046, "y":7.69783, "heading":-1.5708, "vx":-1.99945, "vy":0.01071, "omega":0.0, "ax":0.00114, "ay":0.16391, "alpha":0.0, "fx":[0.01933,0.01933,0.01933,0.01933], "fy":[2.788,2.788,2.788,2.788]}, - {"t":6.90114, "x":3.96908, "y":7.69837, "heading":-1.5708, "vx":-1.99941, "vy":0.01702, "omega":0.0, "ax":0.00903, "ay":0.98582, "alpha":0.0, "fx":[0.15363,0.15363,0.15363,0.15363], "fy":[16.76845,16.76845,16.76845,16.76845]}, - {"t":6.93961, "x":3.89217, "y":7.69975, "heading":-1.5708, "vx":-1.99906, "vy":0.05494, "omega":0.0, "ax":-9.39656, "ay":0.31365, "alpha":0.0, "fx":[-159.83272,-159.83272,-159.83272,-159.83272], "fy":[5.33502,5.33502,5.33502,5.33502]}, - {"t":6.96739, "x":3.83302, "y":7.7014, "heading":-1.5708, "vx":-2.26008, "vy":0.06365, "omega":0.0, "ax":-9.38683, "ay":0.34082, "alpha":0.0, "fx":[-159.66721,-159.66721,-159.66721,-159.66721], "fy":[5.79723,5.79723,5.79723,5.79723]}, - {"t":6.99517, "x":3.76661, "y":7.7033, "heading":-1.5708, "vx":-2.52083, "vy":0.07312, "omega":0.0, "ax":-9.37118, "ay":0.37058, "alpha":0.0, "fx":[-159.40114,-159.40114,-159.40114,-159.40114], "fy":[6.30348,6.30348,6.30348,6.30348]}, - {"t":7.02295, "x":3.69297, "y":7.70547, "heading":-1.5708, "vx":-2.78115, "vy":0.08341, "omega":0.0, "ax":-9.34129, "ay":0.4034, "alpha":0.0, "fx":[-158.89274,-158.89274,-158.89274,-158.89274], "fy":[6.86177,6.86177,6.86177,6.86177]}, - {"t":7.05072, "x":3.61211, "y":7.70794, "heading":-1.5708, "vx":-3.04063, "vy":0.09462, "omega":0.0, "ax":-9.25821, "ay":0.43823, "alpha":0.0, "fx":[-157.47956,-157.47956,-157.47956,-157.47956], "fy":[7.4541,7.4541,7.4541,7.4541]}, - {"t":7.0785, "x":3.52408, "y":7.71074, "heading":-1.5708, "vx":-3.29781, "vy":0.10679, "omega":0.0, "ax":-7.3514, "ay":0.34143, "alpha":0.0, "fx":[-125.04519,-125.04519,-125.04519,-125.04519], "fy":[5.8076,5.8076,5.8076,5.8076]}, - {"t":7.10628, "x":3.42963, "y":7.71384, "heading":-1.5708, "vx":-3.50202, "vy":0.11628, "omega":0.0, "ax":8.63713, "ay":-1.18009, "alpha":0.0, "fx":[146.91517,146.91517,146.91517,146.91517], "fy":[-20.07303,-20.07303,-20.07303,-20.07303]}, - {"t":7.13406, "x":3.33569, "y":7.71661, "heading":-1.5708, "vx":-3.26209, "vy":0.0835, "omega":0.0, "ax":9.25419, "ay":-0.8343, "alpha":0.0, "fx":[157.41108,157.41108,157.41108,157.41108], "fy":[-14.19114,-14.19114,-14.19114,-14.19114]}, - {"t":7.16184, "x":3.24864, "y":7.71861, "heading":-1.5708, "vx":-3.00503, "vy":0.06032, "omega":0.0, "ax":9.30747, "ay":-0.96041, "alpha":0.0, "fx":[158.31748,158.31748,158.31748,158.31748], "fy":[-16.33635,-16.33635,-16.33635,-16.33635]}, - {"t":7.18961, "x":3.16876, "y":7.71992, "heading":-1.5708, "vx":-2.74648, "vy":0.03364, "omega":0.0, "ax":8.56467, "ay":-3.8407, "alpha":0.0, "fx":[145.68253,145.68253,145.68253,145.68253], "fy":[-65.32915,-65.32915,-65.32915,-65.32915]}, - {"t":7.21739, "x":3.09577, "y":7.71937, "heading":-1.5708, "vx":-2.50857, "vy":-0.07304, "omega":0.0, "ax":6.9992, "ay":-6.28309, "alpha":0.0, "fx":[119.05446,119.05446,119.05446,119.05446], "fy":[-106.87355,-106.87355,-106.87355,-106.87355]}, - {"t":7.24517, "x":3.02879, "y":7.71492, "heading":-1.5708, "vx":-2.31414, "vy":-0.24758, "omega":0.0, "ax":6.0627, "ay":-7.20342, "alpha":0.0, "fx":[103.12482,103.12482,103.12482,103.12482], "fy":[-122.52812,-122.52812,-122.52812,-122.52812]}, - {"t":7.27295, "x":2.96684, "y":7.70526, "heading":-1.5708, "vx":-2.14573, "vy":-0.44768, "omega":0.0, "ax":5.50974, "ay":-7.6412, "alpha":0.0, "fx":[93.71913,93.71913,93.71913,93.71913], "fy":[-129.97459,-129.97459,-129.97459,-129.97459]}, - {"t":7.30073, "x":2.90936, "y":7.68988, "heading":-1.5708, "vx":-1.99268, "vy":-0.65994, "omega":0.0, "ax":3.42809, "ay":-2.0548, "alpha":32.10289, "fx":[160.27219,-17.34541,-0.26956,90.58618], "fy":[3.39411,149.55314,-160.37711,-132.37617]}, - {"t":7.33067, "x":2.85123, "y":7.66919, "heading":-1.5708, "vx":-1.89002, "vy":-0.72147, "omega":0.96138, "ax":5.59696, "ay":-3.0731, "alpha":23.44287, "fx":[160.27108,126.75452,2.18722,91.59791], "fy":[2.00577,80.90624,-160.3429,-131.65939]}, - {"t":7.36062, "x":2.79714, "y":7.64621, "heading":-1.54201, "vx":-1.72241, "vy":-0.8135, "omega":1.66343, "ax":5.83161, "ay":-5.30956, "alpha":14.88156, "fx":[160.23677,133.85862,7.95153,94.72935], "fy":[1.7691,-73.48936,-160.13667,-129.39964]}, - {"t":7.39057, "x":2.74817, "y":7.61947, "heading":-1.49219, "vx":-1.54777, "vy":-0.97251, "omega":2.10908, "ax":5.29382, "ay":-6.10183, "alpha":13.35047, "fx":[160.18391,84.13626,16.45795,99.40728], "fy":[1.73728,-131.63567,-159.45611,-125.80719]}, - {"t":7.42052, "x":2.70419, "y":7.58761, "heading":-1.42903, "vx":-1.38923, "vy":-1.15524, "omega":2.50889, "ax":5.3517, "ay":-6.16185, "alpha":12.87523, "fx":[160.11756,71.37314,27.52151,105.1111], "fy":[0.35804,-140.69138,-157.87968,-121.03194]}, - {"t":7.45046, "x":2.66499, "y":7.55025, "heading":-1.3539, "vx":-1.22897, "vy":-1.33977, "omega":2.89447, "ax":5.7681, "ay":-6.04448, "alpha":11.83347, "fx":[159.95133,78.7496,41.97249,111.78137], "fy":[-4.3011,-137.53838,-154.6002,-114.82007]}, - {"t":7.48041, "x":2.63077, "y":7.50741, "heading":-1.26722, "vx":-1.05623, "vy":-1.52078, "omega":3.24884, "ax":6.42923, "ay":-5.8156, "alpha":9.89032, "fx":[159.09362,96.96486,61.84823,119.53065], "fy":[-15.54704,-125.89372,-147.66037,-106.58581]}, - {"t":7.51036, "x":2.60202, "y":7.45926, "heading":-1.16992, "vx":-0.86369, "vy":-1.69494, "omega":3.54503, "ax":7.29394, "ay":-5.40653, "alpha":6.48695, "fx":[155.03879,120.22269,92.17246,128.83765], "fy":[-38.04958,-104.35542,-130.64167,-94.80777]}, - {"t":7.5403, "x":2.57943, "y":7.40608, "heading":-1.06376, "vx":-0.64526, "vy":-1.85685, "omega":3.73929, "ax":8.33553, "ay":-4.28624, "alpha":0.06389, "fx":[142.03639,141.77995,141.533,141.79038], "fy":[-72.41429,-72.90947,-73.4,-72.90708]}, - {"t":7.57025, "x":2.56384, "y":7.34855, "heading":-0.95178, "vx":-0.39564, "vy":-1.98521, "omega":3.7412, "ax":8.57403, "ay":-3.19459, "alpha":-5.29804, "fx":[128.93744,148.67365,158.68096,147.07482], "fy":[-93.40366,-57.40204,-7.99387,-58.55689]}, - {"t":7.6002, "x":2.55584, "y":7.28767, "heading":-0.83974, "vx":-0.13887, "vy":-2.08088, "omega":3.58254, "ax":8.30869, "ay":-3.08744, "alpha":-7.67705, "fx":[120.92907,150.27222,156.78049,137.33187], "fy":[-103.06532,-52.20374,20.19288,-74.98968]}, - {"t":7.63014, "x":2.55541, "y":7.22397, "heading":-0.73246, "vx":0.10995, "vy":-2.17334, "omega":3.35264, "ax":7.7203, "ay":-3.39358, "alpha":-10.33319, "fx":[115.43927,150.90325,152.21155,106.72645], "fy":[-108.42745,-48.68087,36.72891,-110.51569]}, - {"t":7.66009, "x":2.56216, "y":7.15736, "heading":-0.63205, "vx":0.34115, "vy":-2.27496, "omega":3.04319, "ax":7.08002, "ay":-3.76972, "alpha":-12.43302, "fx":[112.51994,150.15553,149.4188,69.62252], "fy":[-110.02174,-47.54989,34.92041,-133.83602]}, - {"t":7.69004, "x":2.57555, "y":7.08754, "heading":-0.54092, "vx":0.55317, "vy":-2.38786, "omega":2.67086, "ax":6.67391, "ay":-3.74405, "alpha":-13.20561, "fx":[112.13436,148.57086,142.33588,51.04418], "fy":[-106.58326,-43.32519,30.34983,-135.18191]}, - {"t":7.71999, "x":2.59511, "y":7.01436, "heading":-0.46094, "vx":0.75303, "vy":-2.49998, "omega":2.27539, "ax":6.91649, "ay":-0.2751, "alpha":-6.34221, "fx":[123.06852,131.86309,111.27807,104.3803], "fy":[-39.4835,-0.12379,37.91841,-17.02886]}, - {"t":7.74993, "x":2.62076, "y":6.93936, "heading":-0.39279, "vx":0.96016, "vy":-2.50822, "omega":2.08546, "ax":-1.68391, "ay":6.49757, "alpha":17.55891, "fx":[-72.91987,-129.79765,32.92439,55.22209], "fy":[133.89359,75.84201,95.1574,137.19448]}, - {"t":7.77988, "x":2.64876, "y":6.86716, "heading":-0.33034, "vx":0.90973, "vy":-2.31364, "omega":2.6113, "ax":-1.70885, "ay":6.76791, "alpha":18.80373, "fx":[-83.35253,-140.12859,54.46458,52.74851], "fy":[132.85327,68.17055,114.02784,145.42909]}, - {"t":7.80983, "x":2.67524, "y":6.80091, "heading":-0.25214, "vx":0.85856, "vy":-2.11096, "omega":3.17441, "ax":-1.42911, "ay":7.02028, "alpha":19.78731, "fx":[-84.56615,-140.55452,77.7195,50.16606], "fy":[133.63646,70.71668,124.45392,148.84482]}, - {"t":7.83977, "x":2.70031, "y":6.74084, "heading":-0.15708, "vx":0.81576, "vy":-1.90072, "omega":3.76698, "ax":-1.25813, "ay":7.21203, "alpha":20.3427, "fx":[-83.12367,-137.66006,87.24955,47.9326], "fy":[135.28163,77.57508,127.02013,150.82157]}, - {"t":7.86972, "x":2.72418, "y":6.68716, "heading":-0.04427, "vx":0.77809, "vy":-1.68474, "omega":4.37619, "ax":-3.46016, "ay":8.6586, "alpha":-2.22105, "fx":[-51.51596,-46.07028,-65.63038,-72.20894], "fy":[150.30181,152.21825,144.95278,141.64855]}, - {"t":7.89967, "x":2.74593, "y":6.64059, "heading":0.08679, "vx":0.67446, "vy":-1.42544, "omega":4.30967, "ax":-3.24333, "ay":8.08145, "alpha":-12.16145, "fx":[1.01555,-5.39597,-92.48482,-123.80737], "fy":[159.15268,159.65388,130.3802,100.66595]}, - {"t":7.92961, "x":2.76467, "y":6.60152, "heading":0.21585, "vx":0.57734, "vy":-1.18343, "omega":3.94547, "ax":-3.01482, "ay":7.48384, "alpha":-17.51333, "fx":[39.60714,2.41433,-105.72698,-141.41949], "fy":[154.54837,160.00769,120.1929,74.44309]}, - {"t":7.95956, "x":2.78061, "y":6.56944, "heading":0.334, "vx":0.48705, "vy":-0.95931, "omega":3.421, "ax":-3.01905, "ay":7.00176, "alpha":-20.49785, "fx":[58.16223,1.34293,-115.07335,-149.84485], "fy":[148.87068,160.16028,111.43291,55.9281]}, - {"t":7.98951, "x":2.79384, "y":6.54385, "heading":0.43645, "vx":0.39664, "vy":-0.74963, "omega":2.80715, "ax":-3.13347, "ay":6.63443, "alpha":-22.17793, "fx":[65.80654,-2.33916,-122.14881,-154.51615], "fy":[145.85076,160.23029,103.7267,41.59148]}, - {"t":8.01946, "x":2.80431, "y":6.52438, "heading":0.52052, "vx":0.3028, "vy":-0.55095, "omega":2.14299, "ax":-3.2692, "ay":6.3476, "alpha":-23.19919, "fx":[68.71089,-6.38532,-127.52187,-157.2361], "fy":[144.63121,160.1725,97.11501,29.96446]}, - {"t":8.0494, "x":2.81191, "y":6.51072, "heading":0.58469, "vx":0.2049, "vy":-0.36086, "omega":1.44825, "ax":-3.384, "ay":6.1175, "alpha":-23.90068, "fx":[69.87743,-9.80908,-131.50313,-158.80844], "fy":[144.15422,160.03548,91.70597,20.33235]}, - {"t":8.07935, "x":2.81653, "y":6.50266, "heading":0.62806, "vx":0.10356, "vy":-0.17766, "omega":0.73249, "ax":-3.45808, "ay":5.93232, "alpha":-24.4597, "fx":[70.79069,-12.11611,-134.29514,-159.66294], "fy":[143.76462,159.90359,87.6105,12.34961]}, - {"t":8.1093, "x":2.81808, "y":6.5, "heading":0.65, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0,0.0,0.0,0.0]}], + {"t":0.0, "x":4.44125, "y":7.69346, "heading":-1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":9.42675, "ay":-0.11496, "alpha":0.00001, "fx":[160.34627,160.34627,160.34627,160.34627], "fy":[-1.95544,-1.95544,-1.95556,-1.95556]}, + {"t":0.0397, "x":4.44868, "y":7.69337, "heading":-1.5708, "vx":0.37425, "vy":-0.00456, "omega":0.0, "ax":9.42565, "ay":-0.10205, "alpha":0.00001, "fx":[160.32756,160.32756,160.32756,160.32756], "fy":[-1.73579,-1.73579,-1.7359,-1.7359]}, + {"t":0.0794, "x":4.47097, "y":7.69311, "heading":-1.5708, "vx":0.74846, "vy":-0.00862, "omega":0.0, "ax":9.42417, "ay":-0.08923, "alpha":0.00001, "fx":[160.30245,160.30245,160.30245,160.30245], "fy":[-1.51768,-1.51768,-1.51778,-1.51778]}, + {"t":0.1191, "x":4.50811, "y":7.69269, "heading":-1.5708, "vx":1.12261, "vy":-0.01216, "omega":0.0, "ax":9.42214, "ay":-0.07649, "alpha":0.00001, "fx":[160.26791,160.26791,160.26791,160.26791], "fy":[-1.30103,-1.30103,-1.30113,-1.30113]}, + {"t":0.1588, "x":4.5601, "y":7.69215, "heading":-1.5708, "vx":1.49668, "vy":-0.01519, "omega":0.0, "ax":9.41923, "ay":-0.06381, "alpha":0.00001, "fx":[160.21845,160.21845,160.21845,160.21845], "fy":[-1.08531,-1.08531,-1.0854,-1.0854]}, + {"t":0.19851, "x":4.62695, "y":7.6915, "heading":-1.5708, "vx":1.87063, "vy":-0.01773, "omega":0.0, "ax":9.4148, "ay":-0.0511, "alpha":0.00001, "fx":[160.1431,160.1431,160.1431,160.1431], "fy":[-0.86908,-0.86908,-0.86916,-0.86916]}, + {"t":0.23821, "x":4.70863, "y":7.69075, "heading":-1.5708, "vx":2.24441, "vy":-0.01976, "omega":0.0, "ax":9.40734, "ay":-0.03814, "alpha":0.00001, "fx":[160.01622,160.01622,160.01622,160.01622], "fy":[-0.64868,-0.64868,-0.64876,-0.64876]}, + {"t":0.27791, "x":4.80515, "y":7.68994, "heading":-1.5708, "vx":2.61789, "vy":-0.02127, "omega":0.0, "ax":9.39233, "ay":-0.02429, "alpha":0.00001, "fx":[159.76078,159.76078,159.76078,159.76078], "fy":[-0.41305,-0.41305,-0.41312,-0.41312]}, + {"t":0.31761, "x":4.91649, "y":7.68908, "heading":-1.5708, "vx":2.99078, "vy":-0.02223, "omega":0.0, "ax":9.34715, "ay":-0.00636, "alpha":0.0, "fx":[158.99231,158.99231,158.99231,158.99231], "fy":[-0.10812,-0.10812,-0.10816,-0.10816]}, + {"t":0.35731, "x":5.04259, "y":7.68819, "heading":-1.5708, "vx":3.36187, "vy":-0.02249, "omega":0.0, "ax":3.67112, "ay":0.52776, "alpha":-0.00008, "fx":[62.44453,62.44483,62.44481,62.4445], "fy":[8.97672,8.97671,8.97732,8.97733]}, + {"t":0.39701, "x":5.17895, "y":7.68771, "heading":-1.5708, "vx":3.50762, "vy":-0.00153, "omega":0.0, "ax":0.0005, "ay":0.26408, "alpha":0.0, "fx":[0.00857,0.00857,0.00857,0.00857], "fy":[4.49197,4.49197,4.49197,4.49197]}, + {"t":0.43671, "x":5.31821, "y":7.68786, "heading":-1.5708, "vx":3.50764, "vy":0.00895, "omega":0.0, "ax":-0.00083, "ay":0.21802, "alpha":0.0, "fx":[-0.01405,-0.01405,-0.01405,-0.01405], "fy":[3.70852,3.70852,3.70852,3.70852]}, + {"t":0.47641, "x":5.45747, "y":7.68839, "heading":-1.5708, "vx":3.5076, "vy":0.01761, "omega":0.0, "ax":-0.00135, "ay":0.21603, "alpha":0.0, "fx":[-0.02294,-0.02294,-0.02294,-0.02294], "fy":[3.67465,3.67465,3.67465,3.67465]}, + {"t":0.51611, "x":5.59672, "y":7.68925, "heading":-1.5708, "vx":3.50755, "vy":0.02618, "omega":0.0, "ax":-0.00193, "ay":0.22195, "alpha":0.0, "fx":[-0.03291,-0.03291,-0.03291,-0.03291], "fy":[3.77537,3.77537,3.77537,3.77537]}, + {"t":0.55582, "x":5.73597, "y":7.69047, "heading":-1.5708, "vx":3.50747, "vy":0.03499, "omega":0.0, "ax":-0.00259, "ay":0.22974, "alpha":0.0, "fx":[-0.04405,-0.04405,-0.04405,-0.04405], "fy":[3.9078,3.9078,3.9078,3.9078]}, + {"t":0.59552, "x":5.87522, "y":7.69204, "heading":-1.5708, "vx":3.50737, "vy":0.04411, "omega":0.0, "ax":-0.00313, "ay":0.22609, "alpha":0.0, "fx":[-0.05325,-0.05325,-0.05325,-0.05325], "fy":[3.84575,3.84575,3.84575,3.84575]}, + {"t":0.63522, "x":6.01447, "y":7.69397, "heading":-1.5708, "vx":3.50725, "vy":0.05309, "omega":0.0, "ax":-0.00173, "ay":0.11013, "alpha":0.0, "fx":[-0.02948,-0.02948,-0.02948,-0.02948], "fy":[1.87325,1.87325,1.87325,1.87325]}, + {"t":0.67492, "x":6.15371, "y":7.69616, "heading":-1.5708, "vx":3.50718, "vy":0.05746, "omega":0.0, "ax":0.00994, "ay":-0.87414, "alpha":0.0, "fx":[0.16913,0.16913,0.16913,0.16913], "fy":[-14.86879,-14.86879,-14.86879,-14.86879]}, + {"t":0.71462, "x":6.29295, "y":7.69776, "heading":-1.5708, "vx":3.50757, "vy":0.02276, "omega":0.0, "ax":-0.14142, "ay":-5.528, "alpha":0.0, "fx":[-2.40552,-2.40552,-2.40552,-2.40552], "fy":[-94.02967,-94.02967,-94.02967,-94.02967]}, + {"t":0.75432, "x":6.4321, "y":7.6943, "heading":-1.5708, "vx":3.50196, "vy":-0.19671, "omega":0.0, "ax":-0.49772, "ay":-5.74705, "alpha":0.0, "fx":[-8.46613,-8.46613,-8.46613,-8.46613], "fy":[-97.75571,-97.75571,-97.75571,-97.75571]}, + {"t":0.79022, "x":6.55748, "y":7.68354, "heading":-1.5708, "vx":3.48409, "vy":-0.403, "omega":0.0, "ax":-0.17097, "ay":-1.39115, "alpha":0.0, "fx":[-2.90817,-2.90817,-2.90817,-2.90817], "fy":[-23.66312,-23.66312,-23.66312,-23.66312]}, + {"t":0.82611, "x":6.68243, "y":7.66818, "heading":-1.5708, "vx":3.47796, "vy":-0.45294, "omega":0.0, "ax":-0.0409, "ay":-0.31002, "alpha":0.0, "fx":[-0.69565,-0.69565,-0.69565,-0.69565], "fy":[-5.27332,-5.27332,-5.27332,-5.27332]}, + {"t":0.86201, "x":6.80725, "y":7.65172, "heading":-1.5708, "vx":3.47649, "vy":-0.46406, "omega":0.0, "ax":-0.14407, "ay":-1.03645, "alpha":0.0, "fx":[-2.45067,-2.45067,-2.45067,-2.45067], "fy":[-17.62968,-17.62968,-17.62968,-17.62968]}, + {"t":0.8979, "x":6.93194, "y":7.63439, "heading":-1.5708, "vx":3.47132, "vy":-0.50127, "omega":0.0, "ax":-0.81853, "ay":-4.81113, "alpha":0.0, "fx":[-13.92301,-13.92301,-13.92301,-13.92301], "fy":[-81.83599,-81.83599,-81.83599,-81.83599]}, + {"t":0.9338, "x":7.05602, "y":7.6133, "heading":-1.5708, "vx":3.44193, "vy":-0.67397, "omega":0.0, "ax":-1.95701, "ay":-7.95533, "alpha":0.0, "fx":[-33.28823,-33.28824,-33.28824,-33.28823], "fy":[-135.31785,-135.31785,-135.31786,-135.31786]}, + {"t":0.96969, "x":7.17831, "y":7.58398, "heading":-1.5708, "vx":3.37169, "vy":-0.95952, "omega":0.0, "ax":-8.51362, "ay":-3.36643, "alpha":0.00002, "fx":[-144.81429,-144.81431,-144.81425,-144.81423], "fy":[-57.26195,-57.26191,-57.26208,-57.26212]}, + {"t":1.00559, "x":7.29385, "y":7.54737, "heading":-1.5708, "vx":3.06609, "vy":-1.08036, "omega":0.0, "ax":-9.35583, "ay":-0.11175, "alpha":0.0, "fx":[-159.14002,-159.14002,-159.14002,-159.14002], "fy":[-1.9008,-1.9008,-1.90083,-1.90083]}, + {"t":1.04148, "x":7.39788, "y":7.50852, "heading":-1.5708, "vx":2.73026, "vy":-1.08437, "omega":0.0, "ax":-9.38059, "ay":0.44057, "alpha":0.0, "fx":[-159.56116,-159.56116,-159.56116,-159.56116], "fy":[7.49402,7.49402,7.49403,7.49403]}, + {"t":1.07738, "x":7.48984, "y":7.46988, "heading":-1.5708, "vx":2.39354, "vy":-1.06856, "omega":0.0, "ax":-9.38142, "ay":0.66355, "alpha":0.0, "fx":[-159.57531,-159.57531,-159.57531,-159.57531], "fy":[11.28678,11.28678,11.2868,11.2868]}, + {"t":1.11327, "x":7.56972, "y":7.43195, "heading":-1.5708, "vx":2.05679, "vy":-1.04474, "omega":0.0, "ax":-9.37972, "ay":0.78344, "alpha":0.0, "fx":[-159.54628,-159.54628,-159.54627,-159.54627], "fy":[13.32603,13.32603,13.32605,13.32605]}, + {"t":1.14917, "x":7.6375, "y":7.39496, "heading":-1.5708, "vx":1.7201, "vy":-1.01662, "omega":0.0, "ax":-9.37791, "ay":0.858, "alpha":0.0, "fx":[-159.51555,-159.51555,-159.51555,-159.51555], "fy":[14.59432,14.59432,14.59435,14.59435]}, + {"t":1.18506, "x":7.6932, "y":7.35902, "heading":-1.5708, "vx":1.38348, "vy":-0.98582, "omega":0.0, "ax":-9.37637, "ay":0.90867, "alpha":0.0, "fx":[-159.4893,-159.4893,-159.4893,-159.4893], "fy":[15.45619,15.45619,15.45622,15.45622]}, + {"t":1.22096, "x":7.73682, "y":7.32422, "heading":-1.5708, "vx":1.04691, "vy":-0.9532, "omega":0.0, "ax":-9.3751, "ay":0.94522, "alpha":0.0, "fx":[-159.46781,-159.46781,-159.46781,-159.46781], "fy":[16.07785,16.07785,16.07788,16.07788]}, + {"t":1.25685, "x":7.76836, "y":7.29061, "heading":-1.5708, "vx":0.71039, "vy":-0.91928, "omega":0.0, "ax":-9.37407, "ay":0.97273, "alpha":0.0, "fx":[-159.45031,-159.45031,-159.45031,-159.45031], "fy":[16.54585,16.54585,16.54588,16.54588]}, + {"t":1.29275, "x":7.78782, "y":7.25824, "heading":-1.5708, "vx":0.37391, "vy":-0.88436, "omega":0.0, "ax":-9.37323, "ay":0.99412, "alpha":0.0, "fx":[-159.43601,-159.43601,-159.43601,-159.43601], "fy":[16.90967,16.90967,16.90971,16.90971]}, + {"t":1.32865, "x":7.79521, "y":7.22713, "heading":-1.5708, "vx":0.03745, "vy":-0.84868, "omega":0.0, "ax":-1.50658, "ay":-0.00696, "alpha":0.0, "fx":[-25.6265,-25.6265,-25.6265,-25.6265], "fy":[-0.11835,-0.11835,-0.11835,-0.11835]}, + {"t":1.37813, "x":7.79522, "y":7.18513, "heading":-1.5708, "vx":-0.03709, "vy":-0.84902, "omega":0.0, "ax":1.48501, "ay":-0.00062, "alpha":0.0, "fx":[25.25962,25.25962,25.25962,25.25962], "fy":[-0.01057,-0.01057,-0.01057,-0.01057]}, + {"t":1.42761, "x":7.7952, "y":7.14312, "heading":-1.5708, "vx":0.03638, "vy":-0.84905, "omega":0.0, "ax":-1.45632, "ay":-0.00061, "alpha":0.0, "fx":[-24.77158,-24.77158,-24.77158,-24.77158], "fy":[-0.01033,-0.01033,-0.01033,-0.01033]}, + {"t":1.47709, "x":7.79522, "y":7.10111, "heading":-1.5708, "vx":-0.03568, "vy":-0.84908, "omega":0.0, "ax":1.42773, "ay":-0.00059, "alpha":0.0, "fx":[24.28527,24.28527,24.28527,24.28527], "fy":[-0.01009,-0.01009,-0.01009,-0.01009]}, + {"t":1.52657, "x":7.7952, "y":7.0591, "heading":-1.5708, "vx":0.03497, "vy":-0.84911, "omega":0.0, "ax":-1.39924, "ay":-0.00058, "alpha":0.0, "fx":[-23.80074,-23.80074,-23.80074,-23.80074], "fy":[-0.00985,-0.00985,-0.00985,-0.00985]}, + {"t":1.57605, "x":7.79522, "y":7.01708, "heading":-1.5708, "vx":-0.03427, "vy":-0.84914, "omega":0.0, "ax":1.37087, "ay":-0.00057, "alpha":0.0, "fx":[23.31806,23.31806,23.31806,23.31806], "fy":[-0.00962,-0.00962,-0.00962,-0.00962]}, + {"t":1.62553, "x":7.7952, "y":6.97507, "heading":-1.5708, "vx":0.03356, "vy":-0.84917, "omega":0.0, "ax":-1.3426, "ay":-0.00055, "alpha":0.0, "fx":[-22.83726,-22.83726,-22.83726,-22.83726], "fy":[-0.00938,-0.00938,-0.00938,-0.00938]}, + {"t":1.67501, "x":7.79522, "y":6.93305, "heading":-1.5708, "vx":-0.03287, "vy":-0.84919, "omega":0.0, "ax":1.31445, "ay":-0.00054, "alpha":0.0, "fx":[22.35843,22.35843,22.35843,22.35843], "fy":[-0.00915,-0.00915,-0.00915,-0.00915]}, + {"t":1.72449, "x":7.7952, "y":6.89103, "heading":-1.5708, "vx":0.03217, "vy":-0.84922, "omega":0.0, "ax":-1.28642, "ay":-0.00052, "alpha":0.0, "fx":[-21.8816,-21.8816,-21.8816,-21.8816], "fy":[-0.00891,-0.00891,-0.00891,-0.00891]}, + {"t":1.77397, "x":7.79522, "y":6.84901, "heading":-1.5708, "vx":-0.03148, "vy":-0.84925, "omega":0.0, "ax":1.25851, "ay":-0.00051, "alpha":0.0, "fx":[21.40685,21.40685,21.40685,21.40685], "fy":[-0.00868,-0.00868,-0.00868,-0.00868]}, + {"t":1.82345, "x":7.7952, "y":6.80699, "heading":-1.5708, "vx":0.03079, "vy":-0.84927, "omega":0.0, "ax":-1.23072, "ay":-0.0005, "alpha":0.0, "fx":[-20.93424,-20.93424,-20.93424,-20.93424], "fy":[-0.00845,-0.00845,-0.00845,-0.00845]}, + {"t":1.87293, "x":7.79522, "y":6.76497, "heading":-1.5708, "vx":-0.03011, "vy":-0.8493, "omega":0.0, "ax":1.20307, "ay":-0.00048, "alpha":0.0, "fx":[20.46382,20.46382,20.46382,20.46382], "fy":[-0.00822,-0.00822,-0.00822,-0.00822]}, + {"t":1.92241, "x":7.7952, "y":6.72294, "heading":-1.5708, "vx":0.02942, "vy":-0.84932, "omega":0.0, "ax":-1.17554, "ay":-0.00047, "alpha":0.0, "fx":[-19.99567,-19.99567,-19.99567,-19.99567], "fy":[-0.00799,-0.00799,-0.00799,-0.00799]}, + {"t":1.97189, "x":7.79522, "y":6.68092, "heading":-1.5708, "vx":-0.02874, "vy":-0.84934, "omega":0.0, "ax":1.14816, "ay":-0.00046, "alpha":0.0, "fx":[19.52985,19.52985,19.52985,19.52985], "fy":[-0.00777,-0.00777,-0.00777,-0.00777]}, + {"t":2.02137, "x":7.7952, "y":6.63889, "heading":-1.5708, "vx":0.02807, "vy":-0.84937, "omega":0.0, "ax":-1.12091, "ay":-0.00044, "alpha":0.0, "fx":[-19.06643,-19.06643,-19.06643,-19.06643], "fy":[-0.00754,-0.00754,-0.00754,-0.00754]}, + {"t":2.07085, "x":7.79522, "y":6.59686, "heading":-1.5708, "vx":-0.0274, "vy":-0.84939, "omega":0.0, "ax":1.09381, "ay":-0.00043, "alpha":0.0, "fx":[18.60547,18.60547,18.60547,18.60547], "fy":[-0.00732,-0.00732,-0.00732,-0.00732]}, + {"t":2.12033, "x":7.7952, "y":6.55484, "heading":-1.5708, "vx":0.02673, "vy":-0.84941, "omega":0.0, "ax":-1.06686, "ay":-0.00042, "alpha":0.0, "fx":[-18.14705,-18.14705,-18.14705,-18.14705], "fy":[-0.0071,-0.0071,-0.0071,-0.0071]}, + {"t":2.16981, "x":7.79522, "y":6.51281, "heading":-1.5708, "vx":-0.02606, "vy":-0.84943, "omega":0.0, "ax":1.04007, "ay":-0.0004, "alpha":0.0, "fx":[17.69123,17.69123,17.69123,17.69123], "fy":[-0.00688,-0.00688,-0.00688,-0.00688]}, + {"t":2.21929, "x":7.7952, "y":6.47078, "heading":-1.5708, "vx":0.0254, "vy":-0.84945, "omega":0.0, "ax":-1.01343, "ay":-0.00039, "alpha":0.0, "fx":[-17.23809,-17.23809,-17.23809,-17.23809], "fy":[-0.00666,-0.00666,-0.00666,-0.00666]}, + {"t":2.26877, "x":7.79522, "y":6.42875, "heading":-1.5708, "vx":-0.02474, "vy":-0.84947, "omega":0.0, "ax":0.98695, "ay":-0.00038, "alpha":0.0, "fx":[16.7877,16.7877,16.7877,16.7877], "fy":[-0.00645,-0.00645,-0.00645,-0.00645]}, + {"t":2.31825, "x":7.7952, "y":6.38671, "heading":-1.5708, "vx":0.02409, "vy":-0.84949, "omega":0.0, "ax":-0.96063, "ay":-0.00037, "alpha":0.0, "fx":[-16.34013,-16.34013,-16.34013,-16.34013], "fy":[-0.00624,-0.00624,-0.00624,-0.00624]}, + {"t":2.36773, "x":7.79522, "y":6.34468, "heading":-1.5708, "vx":-0.02344, "vy":-0.84951, "omega":0.0, "ax":0.93449, "ay":-0.00035, "alpha":0.0, "fx":[15.89544,15.89544,15.89544,15.89544], "fy":[-0.00603,-0.00603,-0.00603,-0.00603]}, + {"t":2.41721, "x":7.7952, "y":6.30265, "heading":-1.5708, "vx":0.0228, "vy":-0.84952, "omega":0.0, "ax":-0.90852, "ay":-0.00034, "alpha":0.0, "fx":[-15.45371,-15.45371,-15.45371,-15.45371], "fy":[-0.00582,-0.00582,-0.00582,-0.00582]}, + {"t":2.46669, "x":7.79521, "y":6.26061, "heading":-1.5708, "vx":-0.02216, "vy":-0.84954, "omega":0.0, "ax":0.88273, "ay":-0.00033, "alpha":0.0, "fx":[15.01502,15.01502,15.01502,15.01502], "fy":[-0.00562,-0.00562,-0.00562,-0.00562]}, + {"t":2.51617, "x":7.7952, "y":6.21858, "heading":-1.5708, "vx":0.02152, "vy":-0.84956, "omega":0.0, "ax":-0.85712, "ay":-0.00032, "alpha":0.0, "fx":[-14.57943,-14.57943,-14.57943,-14.57943], "fy":[-0.00541,-0.00541,-0.00541,-0.00541]}, + {"t":2.56565, "x":7.79521, "y":6.17654, "heading":-1.5708, "vx":-0.02089, "vy":-0.84957, "omega":0.0, "ax":0.8317, "ay":-0.00031, "alpha":0.0, "fx":[14.147,14.147,14.147,14.147], "fy":[-0.00522,-0.00522,-0.00522,-0.00522]}, + {"t":2.61513, "x":7.7952, "y":6.1345, "heading":-1.5708, "vx":0.02026, "vy":-0.84959, "omega":0.0, "ax":-0.80647, "ay":-0.0003, "alpha":0.0, "fx":[-13.71781,-13.71781,-13.71781,-13.71781], "fy":[-0.00502,-0.00502,-0.00502,-0.00502]}, + {"t":2.66461, "x":7.79521, "y":6.09246, "heading":-1.5708, "vx":-0.01964, "vy":-0.8496, "omega":0.0, "ax":0.78143, "ay":-0.00028, "alpha":0.0, "fx":[13.29192,13.29192,13.29192,13.29192], "fy":[-0.00482,-0.00482,-0.00482,-0.00482]}, + {"t":2.71409, "x":7.7952, "y":6.05042, "heading":-1.5708, "vx":0.01902, "vy":-0.84962, "omega":0.0, "ax":-0.75659, "ay":-0.00027, "alpha":0.0, "fx":[-12.86939,-12.86939,-12.86939,-12.86939], "fy":[-0.00463,-0.00463,-0.00463,-0.00463]}, + {"t":2.76357, "x":7.79521, "y":6.00839, "heading":-1.5708, "vx":-0.01841, "vy":-0.84963, "omega":0.0, "ax":0.73195, "ay":-0.00026, "alpha":0.0, "fx":[12.45029,12.45029,12.45029,12.45029], "fy":[-0.00445,-0.00445,-0.00445,-0.00445]}, + {"t":2.81305, "x":7.7952, "y":5.96635, "heading":-1.5708, "vx":0.0178, "vy":-0.84964, "omega":0.0, "ax":-0.70752, "ay":-0.00025, "alpha":0.0, "fx":[-12.03466,-12.03466,-12.03466,-12.03466], "fy":[-0.00426,-0.00426,-0.00426,-0.00426]}, + {"t":2.86253, "x":7.79521, "y":5.9243, "heading":-1.5708, "vx":-0.0172, "vy":-0.84965, "omega":0.0, "ax":0.68329, "ay":-0.00024, "alpha":0.0, "fx":[11.62256,11.62256,11.62256,11.62256], "fy":[-0.00408,-0.00408,-0.00408,-0.00408]}, + {"t":2.91201, "x":7.7952, "y":5.88226, "heading":-1.5708, "vx":0.01661, "vy":-0.84967, "omega":0.0, "ax":-0.65927, "ay":-0.00023, "alpha":0.0, "fx":[-11.21404,-11.21404,-11.21404,-11.21404], "fy":[-0.0039,-0.0039,-0.0039,-0.0039]}, + {"t":2.96149, "x":7.79521, "y":5.84022, "heading":-1.5708, "vx":-0.01601, "vy":-0.84968, "omega":0.0, "ax":0.63547, "ay":-0.00022, "alpha":0.0, "fx":[10.80913,10.80913,10.80913,10.80913], "fy":[-0.00373,-0.00373,-0.00373,-0.00373]}, + {"t":3.01097, "x":7.7952, "y":5.79818, "heading":-1.5708, "vx":0.01543, "vy":-0.84969, "omega":0.0, "ax":-0.61188, "ay":-0.00021, "alpha":0.0, "fx":[-10.40788,-10.40788,-10.40788,-10.40788], "fy":[-0.00356,-0.00356,-0.00356,-0.00356]}, + {"t":3.06045, "x":7.79521, "y":5.75614, "heading":-1.5708, "vx":-0.01485, "vy":-0.8497, "omega":0.0, "ax":0.58851, "ay":-0.0002, "alpha":0.0, "fx":[10.01032,10.01032,10.01032,10.01032], "fy":[-0.00339,-0.00339,-0.00339,-0.00339]}, + {"t":3.10993, "x":7.7952, "y":5.71409, "heading":-1.5708, "vx":0.01427, "vy":-0.84971, "omega":0.0, "ax":-0.56535, "ay":-0.00019, "alpha":0.0, "fx":[-9.61647,-9.61647,-9.61647,-9.61647], "fy":[-0.00322,-0.00322,-0.00322,-0.00322]}, + {"t":3.15941, "x":7.79521, "y":5.67205, "heading":-1.5708, "vx":-0.0137, "vy":-0.84972, "omega":0.0, "ax":0.54242, "ay":-0.00018, "alpha":0.0, "fx":[9.22634,9.22634,9.22634,9.22634], "fy":[-0.00306,-0.00306,-0.00306,-0.00306]}, + {"t":3.20889, "x":7.7952, "y":5.63, "heading":-1.5708, "vx":0.01314, "vy":-0.84973, "omega":0.0, "ax":-0.5197, "ay":-0.00017, "alpha":0.0, "fx":[-8.83994,-8.83994,-8.83994,-8.83994], "fy":[-0.00291,-0.00291,-0.00291,-0.00291]}, + {"t":3.25837, "x":7.79521, "y":5.58796, "heading":-1.5708, "vx":-0.01258, "vy":-0.84974, "omega":0.0, "ax":0.4972, "ay":-0.00016, "alpha":0.0, "fx":[8.45728,8.45728,8.45728,8.45728], "fy":[-0.00276,-0.00276,-0.00276,-0.00276]}, + {"t":3.30785, "x":7.7952, "y":5.54592, "heading":-1.5708, "vx":0.01202, "vy":-0.84974, "omega":0.0, "ax":-0.47492, "ay":-0.00015, "alpha":0.0, "fx":[-8.07833,-8.07833,-8.07833,-8.07833], "fy":[-0.00261,-0.00261,-0.00261,-0.00261]}, + {"t":3.35733, "x":7.79521, "y":5.50387, "heading":-1.5708, "vx":-0.01148, "vy":-0.84975, "omega":0.0, "ax":0.45286, "ay":-0.00014, "alpha":0.0, "fx":[7.70308,7.70308,7.70308,7.70308], "fy":[-0.00246,-0.00246,-0.00246,-0.00246]}, + {"t":3.40681, "x":7.7952, "y":5.46182, "heading":-1.5708, "vx":0.01093, "vy":-0.84976, "omega":0.0, "ax":-0.43102, "ay":-0.00014, "alpha":0.0, "fx":[-7.3315,-7.3315,-7.3315,-7.3315], "fy":[-0.00232,-0.00232,-0.00232,-0.00232]}, + {"t":3.45629, "x":7.79521, "y":5.41978, "heading":-1.5708, "vx":-0.01039, "vy":-0.84977, "omega":0.0, "ax":0.40939, "ay":-0.00013, "alpha":0.0, "fx":[6.96355,6.96355,6.96355,6.96355], "fy":[-0.00218,-0.00218,-0.00218,-0.00218]}, + {"t":3.50577, "x":7.7952, "y":5.37773, "heading":-1.5708, "vx":0.00986, "vy":-0.84977, "omega":0.0, "ax":-0.38796, "ay":-0.00012, "alpha":0.0, "fx":[-6.59916,-6.59916,-6.59916,-6.59916], "fy":[-0.00205,-0.00205,-0.00205,-0.00205]}, + {"t":3.55525, "x":7.79521, "y":5.33568, "heading":-1.5708, "vx":-0.00933, "vy":-0.84978, "omega":0.0, "ax":0.36675, "ay":-0.00011, "alpha":0.0, "fx":[6.23828,6.23828,6.23828,6.23828], "fy":[-0.00192,-0.00192,-0.00192,-0.00192]}, + {"t":3.60473, "x":7.7952, "y":5.29364, "heading":-1.5708, "vx":0.00881, "vy":-0.84978, "omega":0.0, "ax":-0.34573, "ay":-0.00011, "alpha":0.0, "fx":[-5.88082,-5.88082,-5.88082,-5.88082], "fy":[-0.00179,-0.00179,-0.00179,-0.00179]}, + {"t":3.65421, "x":7.79521, "y":5.25159, "heading":-1.5708, "vx":-0.00829, "vy":-0.84979, "omega":0.0, "ax":0.32491, "ay":-0.0001, "alpha":0.0, "fx":[5.5267,5.5267,5.5267,5.5267], "fy":[-0.00167,-0.00167,-0.00167,-0.00167]}, + {"t":3.70369, "x":7.7952, "y":5.20954, "heading":-1.5708, "vx":0.00778, "vy":-0.84979, "omega":0.0, "ax":-0.30429, "ay":-0.00009, "alpha":0.0, "fx":[-5.17581,-5.17581,-5.17581,-5.17581], "fy":[-0.00155,-0.00155,-0.00155,-0.00155]}, + {"t":3.75317, "x":7.79521, "y":5.16749, "heading":-1.5708, "vx":-0.00727, "vy":-0.8498, "omega":0.0, "ax":0.28384, "ay":-0.00008, "alpha":0.0, "fx":[4.82804,4.82804,4.82804,4.82804], "fy":[-0.00143,-0.00143,-0.00143,-0.00143]}, + {"t":3.80265, "x":7.7952, "y":5.12545, "heading":-1.5708, "vx":0.00677, "vy":-0.8498, "omega":0.0, "ax":-0.26357, "ay":-0.00008, "alpha":0.0, "fx":[-4.48326,-4.48326,-4.48326,-4.48326], "fy":[-0.00132,-0.00132,-0.00132,-0.00132]}, + {"t":3.85213, "x":7.79521, "y":5.0834, "heading":-1.5708, "vx":-0.00627, "vy":-0.84981, "omega":0.0, "ax":0.24347, "ay":-0.00007, "alpha":0.0, "fx":[4.14132,4.14132,4.14132,4.14132], "fy":[-0.00121,-0.00121,-0.00121,-0.00121]}, + {"t":3.90161, "x":7.7952, "y":5.04135, "heading":-1.5708, "vx":0.00578, "vy":-0.84981, "omega":0.0, "ax":-0.22352, "ay":-0.00006, "alpha":0.0, "fx":[-3.80208,-3.80208,-3.80208,-3.80208], "fy":[-0.0011,-0.0011,-0.0011,-0.0011]}, + {"t":3.95109, "x":7.79521, "y":4.9993, "heading":-1.5708, "vx":-0.00528, "vy":-0.84981, "omega":0.0, "ax":0.20373, "ay":-0.00006, "alpha":0.0, "fx":[3.46537,3.46537,3.46537,3.46537], "fy":[-0.00099,-0.00099,-0.00099,-0.00099]}, + {"t":4.00057, "x":7.7952, "y":4.95725, "heading":-1.5708, "vx":0.0048, "vy":-0.84982, "omega":0.0, "ax":-0.18407, "ay":-0.00005, "alpha":0.0, "fx":[-3.13102,-3.13102,-3.13102,-3.13102], "fy":[-0.00089,-0.00089,-0.00089,-0.00089]}, + {"t":4.05005, "x":7.79521, "y":4.9152, "heading":-1.5708, "vx":-0.00431, "vy":-0.84982, "omega":0.0, "ax":0.16454, "ay":-0.00005, "alpha":0.0, "fx":[2.79883,2.79883,2.79883,2.79883], "fy":[-0.00079,-0.00079,-0.00079,-0.00079]}, + {"t":4.09953, "x":7.7952, "y":4.87315, "heading":-1.5708, "vx":0.00383, "vy":-0.84982, "omega":0.0, "ax":-0.14513, "ay":-0.00004, "alpha":0.0, "fx":[-2.46861,-2.46861,-2.46861,-2.46861], "fy":[-0.0007,-0.0007,-0.0007,-0.0007]}, + {"t":4.14901, "x":7.79521, "y":4.8311, "heading":-1.5708, "vx":-0.00335, "vy":-0.84982, "omega":0.0, "ax":0.12582, "ay":-0.00004, "alpha":0.0, "fx":[2.14016,2.14016,2.14016,2.14016], "fy":[-0.0006,-0.0006,-0.0006,-0.0006]}, + {"t":4.19849, "x":7.7952, "y":4.78905, "heading":-1.5708, "vx":0.00287, "vy":-0.84982, "omega":0.0, "ax":-0.1066, "ay":-0.00003, "alpha":0.0, "fx":[-1.81326,-1.81326,-1.81326,-1.81326], "fy":[-0.00051,-0.00051,-0.00051,-0.00051]}, + {"t":4.24797, "x":7.79521, "y":4.74701, "heading":-1.5708, "vx":-0.0024, "vy":-0.84983, "omega":0.0, "ax":0.08746, "ay":-0.00002, "alpha":0.0, "fx":[1.48769,1.48769,1.48769,1.48769], "fy":[-0.00041,-0.00041,-0.00041,-0.00041]}, + {"t":4.29745, "x":7.7952, "y":4.70496, "heading":-1.5708, "vx":0.00193, "vy":-0.84983, "omega":0.0, "ax":-0.06839, "ay":-0.00002, "alpha":0.0, "fx":[-1.16322,-1.16322,-1.16322,-1.16322], "fy":[-0.00032,-0.00032,-0.00032,-0.00032]}, + {"t":4.34693, "x":7.79521, "y":4.66291, "heading":-1.5708, "vx":-0.00146, "vy":-0.84983, "omega":0.0, "ax":0.04936, "ay":-0.00001, "alpha":0.0, "fx":[0.83963,0.83963,0.83963,0.83963], "fy":[-0.00023,-0.00023,-0.00023,-0.00023]}, + {"t":4.39641, "x":7.7952, "y":4.62086, "heading":-1.5708, "vx":0.00099, "vy":-0.84983, "omega":0.0, "ax":-0.03037, "ay":-0.00001, "alpha":0.0, "fx":[-0.51666,-0.51666,-0.51666,-0.51666], "fy":[-0.00014,-0.00014,-0.00014,-0.00014]}, + {"t":4.44589, "x":7.79521, "y":4.57881, "heading":-1.5708, "vx":-0.00052, "vy":-0.84983, "omega":0.0, "ax":0.01141, "ay":0.0, "alpha":0.0, "fx":[0.19408,0.19408,0.19408,0.19408], "fy":[-0.00005,-0.00005,-0.00005,-0.00005]}, + {"t":4.49537, "x":7.7952, "y":4.53676, "heading":-1.5708, "vx":0.00005, "vy":-0.84983, "omega":0.0, "ax":0.00755, "ay":0.0, "alpha":0.0, "fx":[0.12836,0.12836,0.12836,0.12836], "fy":[0.00004,0.00004,0.00004,0.00004]}, + {"t":4.54485, "x":7.79521, "y":4.49471, "heading":-1.5708, "vx":0.00042, "vy":-0.84983, "omega":0.0, "ax":-0.02651, "ay":0.00001, "alpha":0.0, "fx":[-0.45089,-0.45089,-0.45089,-0.45089], "fy":[0.00012,0.00012,0.00012,0.00012]}, + {"t":4.59433, "x":7.7952, "y":4.45266, "heading":-1.5708, "vx":-0.00089, "vy":-0.84983, "omega":0.0, "ax":0.04549, "ay":0.00001, "alpha":0.0, "fx":[0.77376,0.77376,0.77376,0.77376], "fy":[0.00021,0.00021,0.00021,0.00021]}, + {"t":4.64381, "x":7.79521, "y":4.41061, "heading":-1.5708, "vx":0.00136, "vy":-0.84983, "omega":0.0, "ax":-0.0645, "ay":0.00002, "alpha":0.0, "fx":[-1.09721,-1.09721,-1.09721,-1.09721], "fy":[0.0003,0.0003,0.0003,0.0003]}, + {"t":4.69329, "x":7.7952, "y":4.36856, "heading":-1.5708, "vx":-0.00183, "vy":-0.84983, "omega":0.0, "ax":0.08357, "ay":0.00002, "alpha":0.0, "fx":[1.42148,1.42148,1.42148,1.42148], "fy":[0.0004,0.0004,0.0004,0.0004]}, + {"t":4.74277, "x":7.79521, "y":4.32651, "heading":-1.5708, "vx":0.0023, "vy":-0.84983, "omega":0.0, "ax":-0.10269, "ay":0.00003, "alpha":0.0, "fx":[-1.74681,-1.74681,-1.74681,-1.74681], "fy":[0.00049,0.00049,0.00049,0.00049]}, + {"t":4.79225, "x":7.7952, "y":4.28446, "heading":-1.5708, "vx":-0.00278, "vy":-0.84982, "omega":0.0, "ax":0.1219, "ay":0.00003, "alpha":0.0, "fx":[2.07342,2.07342,2.07342,2.07342], "fy":[0.00058,0.00058,0.00058,0.00058]}, + {"t":4.84173, "x":7.79521, "y":4.24241, "heading":-1.5708, "vx":0.00325, "vy":-0.84982, "omega":0.0, "ax":-0.14119, "ay":0.00004, "alpha":0.0, "fx":[-2.40154,-2.40154,-2.40154,-2.40154], "fy":[0.00068,0.00068,0.00068,0.00068]}, + {"t":4.89121, "x":7.7952, "y":4.20036, "heading":-1.5708, "vx":-0.00373, "vy":-0.84982, "omega":0.0, "ax":0.16058, "ay":0.00005, "alpha":0.0, "fx":[2.73137,2.73137,2.73137,2.73137], "fy":[0.00077,0.00077,0.00077,0.00077]}, + {"t":4.94069, "x":7.79521, "y":4.15831, "heading":-1.5708, "vx":0.00421, "vy":-0.84982, "omega":0.0, "ax":-0.18008, "ay":0.00005, "alpha":0.0, "fx":[-3.06314,-3.06314,-3.06314,-3.06314], "fy":[0.00087,0.00087,0.00087,0.00087]}, + {"t":4.99017, "x":7.7952, "y":4.11626, "heading":-1.5708, "vx":-0.0047, "vy":-0.84982, "omega":0.0, "ax":0.19971, "ay":0.00006, "alpha":0.0, "fx":[3.39704,3.39704,3.39704,3.39704], "fy":[0.00097,0.00097,0.00097,0.00097]}, + {"t":5.03965, "x":7.79521, "y":4.07422, "heading":-1.5708, "vx":0.00518, "vy":-0.84981, "omega":0.0, "ax":-0.21948, "ay":0.00006, "alpha":0.0, "fx":[-3.73326,-3.73326,-3.73326,-3.73326], "fy":[0.00108,0.00108,0.00108,0.00108]}, + {"t":5.08913, "x":7.7952, "y":4.03217, "heading":-1.5708, "vx":-0.00568, "vy":-0.84981, "omega":0.0, "ax":0.23939, "ay":0.00007, "alpha":0.0, "fx":[4.07197,4.07197,4.07197,4.07197], "fy":[0.00118,0.00118,0.00118,0.00118]}, + {"t":5.13861, "x":7.79521, "y":3.99012, "heading":-1.5708, "vx":0.00617, "vy":-0.84981, "omega":0.0, "ax":-0.25946, "ay":0.00008, "alpha":0.0, "fx":[-4.41334,-4.41334,-4.41334,-4.41334], "fy":[0.00129,0.00129,0.00129,0.00129]}, + {"t":5.18809, "x":7.7952, "y":3.94807, "heading":-1.5708, "vx":-0.00667, "vy":-0.8498, "omega":0.0, "ax":0.27969, "ay":0.00008, "alpha":0.0, "fx":[4.75753,4.75753,4.75753,4.75753], "fy":[0.00141,0.00141,0.00141,0.00141]}, + {"t":5.23757, "x":7.79521, "y":3.90602, "heading":-1.5708, "vx":0.00717, "vy":-0.8498, "omega":0.0, "ax":-0.3001, "ay":0.00009, "alpha":0.0, "fx":[-5.10468,-5.10468,-5.10468,-5.10468], "fy":[0.00152,0.00152,0.00152,0.00152]}, + {"t":5.28705, "x":7.7952, "y":3.86397, "heading":-1.5708, "vx":-0.00768, "vy":-0.84979, "omega":0.0, "ax":0.32069, "ay":0.0001, "alpha":0.0, "fx":[5.45492,5.45492,5.45492,5.45492], "fy":[0.00164,0.00164,0.00164,0.00164]}, + {"t":5.33653, "x":7.79521, "y":3.82193, "heading":-1.5708, "vx":0.00819, "vy":-0.84979, "omega":0.0, "ax":-0.34147, "ay":0.0001, "alpha":0.0, "fx":[-5.80837,-5.80837,-5.80837,-5.80837], "fy":[0.00176,0.00176,0.00176,0.00176]}, + {"t":5.38601, "x":7.7952, "y":3.77988, "heading":-1.5708, "vx":-0.00871, "vy":-0.84978, "omega":0.0, "ax":0.36245, "ay":0.00011, "alpha":0.0, "fx":[6.16514,6.16514,6.16514,6.16514], "fy":[0.00189,0.00189,0.00189,0.00189]}, + {"t":5.43549, "x":7.79521, "y":3.73783, "heading":-1.5708, "vx":0.00923, "vy":-0.84978, "omega":0.0, "ax":-0.38363, "ay":0.00083, "alpha":0.0, "fx":[-6.52551,-6.52551,-6.52551,-6.52551], "fy":[0.01407,0.01407,0.01407,0.01407]}, + {"t":5.48497, "x":7.7952, "y":3.69578, "heading":-1.5708, "vx":-0.00975, "vy":-0.84974, "omega":0.0, "ax":0.40516, "ay":9.34776, "alpha":0.0, "fx":[6.89159,6.89159,6.89159,6.89159], "fy":[159.00271,159.00271,159.00271,159.00271]}, + {"t":5.53445, "x":7.79521, "y":3.66518, "heading":-1.5708, "vx":0.01029, "vy":-0.38721, "omega":0.0, "ax":-0.42154, "ay":9.41618, "alpha":0.0, "fx":[-7.17034,-7.17034,-7.17034,-7.17034], "fy":[160.16646,160.16646,160.16646,160.16646]}, + {"t":5.58393, "x":7.79521, "y":3.65755, "heading":-1.5708, "vx":-0.01057, "vy":0.0787, "omega":0.0, "ax":-3.222, "ay":8.86016, "alpha":0.0, "fx":[-54.80525,-54.80525,-54.80525,-54.80525], "fy":[150.70884,150.70884,150.70884,150.70884]}, + {"t":5.62658, "x":7.79183, "y":3.66896, "heading":-1.5708, "vx":-0.14797, "vy":0.45656, "omega":0.0, "ax":-3.22169, "ay":8.85882, "alpha":0.0, "fx":[-54.79996,-54.79996,-54.79996,-54.79996], "fy":[150.68595,150.68595,150.68595,150.68595]}, + {"t":5.66922, "x":7.78259, "y":3.69649, "heading":-1.5708, "vx":-0.28537, "vy":0.83436, "omega":0.0, "ax":-3.22126, "ay":8.85703, "alpha":0.0, "fx":[-54.79278,-54.79278,-54.79278,-54.79278], "fy":[150.65547,150.65547,150.65547,150.65547]}, + {"t":5.71187, "x":7.76749, "y":3.74013, "heading":-1.5708, "vx":-0.42274, "vy":1.21208, "omega":0.0, "ax":-3.22066, "ay":8.85452, "alpha":0.0, "fx":[-54.78256,-54.78256,-54.78256,-54.78256], "fy":[150.61287,150.61287,150.61287,150.61287]}, + {"t":5.75452, "x":7.74653, "y":3.79987, "heading":-1.5708, "vx":-0.56009, "vy":1.5897, "omega":0.0, "ax":-3.21975, "ay":8.85077, "alpha":0.0, "fx":[-54.76702,-54.76702,-54.76702,-54.76702], "fy":[150.54905,150.54905,150.54905,150.54905]}, + {"t":5.79716, "x":7.71972, "y":3.87571, "heading":-1.5708, "vx":-0.69741, "vy":1.96715, "omega":0.0, "ax":-3.21821, "ay":8.84453, "alpha":0.0, "fx":[-54.74081,-54.74081,-54.74081,-54.74081], "fy":[150.44285,150.44285,150.44285,150.44285]}, + {"t":5.83981, "x":7.68705, "y":3.96765, "heading":-1.5708, "vx":-0.83465, "vy":2.34434, "omega":0.0, "ax":-3.2151, "ay":8.83207, "alpha":0.0, "fx":[-54.68787,-54.68787,-54.68787,-54.68787], "fy":[150.23099,150.23099,150.23099,150.23099]}, + {"t":5.88246, "x":7.64853, "y":4.07566, "heading":-1.5708, "vx":-0.97176, "vy":2.721, "omega":0.0, "ax":-3.2057, "ay":8.79498, "alpha":0.0, "fx":[-54.52801,-54.52801,-54.52801,-54.52801], "fy":[149.60005,149.60005,149.60005,149.60005]}, + {"t":5.9251, "x":7.60417, "y":4.1997, "heading":-1.5708, "vx":-1.10848, "vy":3.09608, "omega":0.0, "ax":-2.05652, "ay":4.72633, "alpha":0.0, "fx":[-34.98078,-34.98078,-34.98078,-34.98078], "fy":[80.39344,80.39344,80.39344,80.39344]}, + {"t":5.96775, "x":7.55503, "y":4.33604, "heading":-1.5708, "vx":-1.19618, "vy":3.29764, "omega":0.0, "ax":-0.04848, "ay":-0.01656, "alpha":0.0, "fx":[-0.82469,-0.82469,-0.82469,-0.82469], "fy":[-0.28174,-0.28174,-0.28174,-0.28174]}, + {"t":6.0104, "x":7.50397, "y":4.47665, "heading":-1.5708, "vx":-1.19825, "vy":3.29693, "omega":0.0, "ax":-0.00537, "ay":-0.00195, "alpha":0.0, "fx":[-0.09129,-0.09129,-0.09129,-0.09129], "fy":[-0.03316,-0.03316,-0.03316,-0.03316]}, + {"t":6.05304, "x":7.45286, "y":4.61726, "heading":-1.5708, "vx":-1.19848, "vy":3.29685, "omega":0.0, "ax":-0.00053, "ay":-0.00019, "alpha":0.0, "fx":[-0.00902,-0.00902,-0.00902,-0.00902], "fy":[-0.00326,-0.00326,-0.00326,-0.00326]}, + {"t":6.09569, "x":7.40175, "y":4.75785, "heading":-1.5708, "vx":-1.1985, "vy":3.29684, "omega":0.0, "ax":0.00007, "ay":0.00003, "alpha":0.0, "fx":[0.00123,0.00123,0.00123,0.00123], "fy":[0.00047,0.00047,0.00047,0.00047]}, + {"t":6.13834, "x":7.35064, "y":4.89845, "heading":-1.5708, "vx":-1.1985, "vy":3.29684, "omega":0.0, "ax":0.00021, "ay":0.00008, "alpha":0.0, "fx":[0.0035,0.0035,0.0035,0.0035], "fy":[0.0013,0.0013,0.0013,0.0013]}, + {"t":6.18098, "x":7.29953, "y":5.03905, "heading":-1.5708, "vx":-1.19849, "vy":3.29685, "omega":0.0, "ax":0.00029, "ay":0.00011, "alpha":0.0, "fx":[0.00495,0.00495,0.00495,0.00495], "fy":[0.00184,0.00184,0.00184,0.00184]}, + {"t":6.22363, "x":7.24842, "y":5.17965, "heading":-1.5708, "vx":-1.19848, "vy":3.29685, "omega":0.0, "ax":0.00038, "ay":0.00014, "alpha":0.0, "fx":[0.0064,0.0064,0.0064,0.0064], "fy":[0.00237,0.00237,0.00237,0.00237]}, + {"t":6.26628, "x":7.19731, "y":5.32025, "heading":-1.5708, "vx":-1.19846, "vy":3.29686, "omega":0.0, "ax":0.00046, "ay":0.00017, "alpha":0.0, "fx":[0.0079,0.0079,0.0079,0.0079], "fy":[0.00292,0.00292,0.00292,0.00292]}, + {"t":6.30892, "x":7.1462, "y":5.46085, "heading":-1.5708, "vx":-1.19844, "vy":3.29686, "omega":0.0, "ax":0.00052, "ay":0.00019, "alpha":0.0, "fx":[0.00892,0.00892,0.00892,0.00892], "fy":[0.0033,0.0033,0.0033,0.0033]}, + {"t":6.35157, "x":7.09509, "y":5.60145, "heading":-1.5708, "vx":-1.19842, "vy":3.29687, "omega":0.0, "ax":0.00027, "ay":0.0001, "alpha":0.0, "fx":[0.00456,0.00456,0.00456,0.00456], "fy":[0.00172,0.00172,0.00172,0.00172]}, + {"t":6.39422, "x":7.04398, "y":5.74205, "heading":-1.5708, "vx":-1.19841, "vy":3.29688, "omega":0.0, "ax":-0.00291, "ay":-0.00105, "alpha":0.0, "fx":[-0.04954,-0.04954,-0.04954,-0.04954], "fy":[-0.01794,-0.01794,-0.01794,-0.01794]}, + {"t":6.43686, "x":6.99287, "y":5.88265, "heading":-1.5708, "vx":-1.19853, "vy":3.29683, "omega":0.0, "ax":-0.03238, "ay":-0.01178, "alpha":0.0, "fx":[-0.55084,-0.55084,-0.55084,-0.55084], "fy":[-0.20031,-0.20031,-0.20031,-0.20031]}, + {"t":6.47951, "x":6.94173, "y":6.02324, "heading":-1.5708, "vx":-1.19991, "vy":3.29633, "omega":0.0, "ax":-0.29695, "ay":-0.10875, "alpha":0.0, "fx":[-5.0511,-5.0511,-5.0511,-5.0511], "fy":[-1.84973,-1.84973,-1.84973,-1.84973]}, + {"t":6.52216, "x":6.89028, "y":6.16372, "heading":-1.5708, "vx":-1.21258, "vy":3.29169, "omega":0.0, "ax":-2.44486, "ay":-0.95207, "alpha":0.0, "fx":[-41.58644,-41.58644,-41.58644,-41.58644], "fy":[-16.19439,-16.19439,-16.19439,-16.19439]}, + {"t":6.5648, "x":6.83635, "y":6.30324, "heading":-1.5708, "vx":-1.31684, "vy":3.25109, "omega":0.0, "ax":-0.91211, "ay":-8.55193, "alpha":0.0, "fx":[-15.51471,-15.51471,-15.51471,-15.51471], "fy":[-145.4658,-145.4658,-145.4658,-145.4658]}, + {"t":6.60745, "x":6.77936, "y":6.43411, "heading":-1.5708, "vx":-1.35574, "vy":2.88638, "omega":0.0, "ax":2.87427, "ay":-8.91416, "alpha":0.0, "fx":[48.89052,48.89052,48.89052,48.89052], "fy":[-151.62723,-151.62723,-151.62723,-151.62723]}, + {"t":6.6501, "x":6.72416, "y":6.5491, "heading":-1.5708, "vx":-1.23316, "vy":2.50622, "omega":0.0, "ax":3.04051, "ay":-8.89511, "alpha":0.0, "fx":[51.7182,51.7182,51.7182,51.7182], "fy":[-151.30322,-151.30322,-151.30322,-151.30322]}, + {"t":6.69274, "x":6.67433, "y":6.64789, "heading":-1.5708, "vx":-1.10349, "vy":2.12687, "omega":0.0, "ax":3.09902, "ay":-8.88764, "alpha":0.0, "fx":[52.71343,52.71343,52.71343,52.71343], "fy":[-151.1762,-151.1762,-151.1762,-151.1762]}, + {"t":6.73539, "x":6.63009, "y":6.73051, "heading":-1.5708, "vx":-0.97133, "vy":1.74784, "omega":0.0, "ax":-7.61302, "ay":-5.13318, "alpha":0.0, "fx":[-129.49527,-129.49527,-129.49527,-129.49527], "fy":[-87.31384,-87.31384,-87.31384,-87.31384]}, + {"t":6.77259, "x":6.58869, "y":6.79198, "heading":-1.5708, "vx":-1.25452, "vy":1.5569, "omega":0.0, "ax":-5.86773, "ay":-5.50015, "alpha":0.0, "fx":[-99.80847,-99.80847,-99.80847,-99.80847], "fy":[-93.55593,-93.55593,-93.55593,-93.55593]}, + {"t":6.80979, "x":6.53796, "y":6.84608, "heading":-1.5708, "vx":-1.47279, "vy":1.3523, "omega":0.0, "ax":-1.18899, "ay":-1.33896, "alpha":0.0, "fx":[-20.22436,-20.22436,-20.22436,-20.22436], "fy":[-22.77525,-22.77525,-22.77525,-22.77525]}, + {"t":6.84698, "x":6.48235, "y":6.89546, "heading":-1.5708, "vx":-1.51702, "vy":1.30249, "omega":0.0, "ax":-0.07241, "ay":-0.08452, "alpha":0.0, "fx":[-1.23174,-1.23174,-1.23174,-1.23174], "fy":[-1.43758,-1.43758,-1.43758,-1.43758]}, + {"t":6.88418, "x":6.42587, "y":6.94385, "heading":-1.5708, "vx":-1.51972, "vy":1.29935, "omega":0.0, "ax":-0.00245, "ay":-0.00287, "alpha":0.0, "fx":[-0.04173,-0.04173,-0.04173,-0.04173], "fy":[-0.04879,-0.04879,-0.04879,-0.04879]}, + {"t":6.92138, "x":6.36934, "y":6.99219, "heading":-1.5708, "vx":-1.51981, "vy":1.29924, "omega":0.0, "ax":0.00198, "ay":0.00231, "alpha":0.0, "fx":[0.03364,0.03364,0.03364,0.03364], "fy":[0.03937,0.03937,0.03937,0.03937]}, + {"t":6.95858, "x":6.31281, "y":7.04052, "heading":-1.5708, "vx":-1.51973, "vy":1.29933, "omega":0.0, "ax":0.0024, "ay":0.00281, "alpha":0.0, "fx":[0.04086,0.04086,0.04086,0.04086], "fy":[0.04782,0.04782,0.04782,0.04782]}, + {"t":6.99578, "x":6.25628, "y":7.08885, "heading":-1.5708, "vx":-1.51964, "vy":1.29943, "omega":0.0, "ax":0.0026, "ay":0.00304, "alpha":0.0, "fx":[0.04426,0.04426,0.04426,0.04426], "fy":[0.05179,0.05179,0.05179,0.05179]}, + {"t":7.03298, "x":6.19975, "y":7.13719, "heading":-1.5708, "vx":-1.51955, "vy":1.29955, "omega":0.0, "ax":0.00281, "ay":0.00329, "alpha":0.0, "fx":[0.04788,0.04788,0.04788,0.04788], "fy":[0.056,0.056,0.056,0.056]}, + {"t":7.07018, "x":6.14323, "y":7.18553, "heading":-1.5708, "vx":-1.51944, "vy":1.29967, "omega":0.0, "ax":0.00306, "ay":0.00358, "alpha":0.0, "fx":[0.05207,0.05207,0.05207,0.05207], "fy":[0.0609,0.0609,0.0609,0.0609]}, + {"t":7.10737, "x":6.08671, "y":7.23388, "heading":-1.5708, "vx":-1.51933, "vy":1.2998, "omega":0.0, "ax":0.00335, "ay":0.00392, "alpha":0.0, "fx":[0.05703,0.05703,0.05703,0.05703], "fy":[0.06668,0.06668,0.06668,0.06668]}, + {"t":7.14457, "x":6.0302, "y":7.28224, "heading":-1.5708, "vx":-1.5192, "vy":1.29995, "omega":0.0, "ax":0.0037, "ay":0.00433, "alpha":0.0, "fx":[0.063,0.063,0.063,0.063], "fy":[0.07366,0.07366,0.07366,0.07366]}, + {"t":7.18177, "x":5.97369, "y":7.3306, "heading":-1.5708, "vx":-1.51907, "vy":1.30011, "omega":0.0, "ax":0.00414, "ay":0.00484, "alpha":0.0, "fx":[0.07037,0.07037,0.07037,0.07037], "fy":[0.08225,0.08225,0.08225,0.08225]}, + {"t":7.21897, "x":5.91718, "y":7.37896, "heading":-1.5708, "vx":-1.51891, "vy":1.30029, "omega":0.0, "ax":0.00467, "ay":0.00545, "alpha":0.0, "fx":[0.07939,0.07939,0.07939,0.07939], "fy":[0.09277,0.09277,0.09277,0.09277]}, + {"t":7.25617, "x":5.86068, "y":7.42733, "heading":-1.5708, "vx":-1.51874, "vy":1.30049, "omega":0.0, "ax":0.00502, "ay":0.00587, "alpha":0.0, "fx":[0.0854,0.0854,0.0854,0.0854], "fy":[0.09977,0.09977,0.09977,0.09977]}, + {"t":7.29337, "x":5.80419, "y":7.47571, "heading":-1.5708, "vx":-1.51855, "vy":1.30071, "omega":0.0, "ax":-0.00018, "ay":-0.00021, "alpha":0.0, "fx":[-0.00305,-0.00305,-0.00305,-0.00305], "fy":[-0.00349,-0.00349,-0.00349,-0.00349]}, + {"t":7.33056, "x":5.7477, "y":7.5241, "heading":-1.5708, "vx":-1.51856, "vy":1.3007, "omega":0.0, "ax":-0.10001, "ay":-0.11709, "alpha":0.0, "fx":[-1.70111,-1.70111,-1.70111,-1.70111], "fy":[-1.99175,-1.99175,-1.99175,-1.99175]}, + {"t":7.36776, "x":5.69115, "y":7.5724, "heading":-1.5708, "vx":-1.52228, "vy":1.29635, "omega":0.0, "ax":-1.57901, "ay":-1.94433, "alpha":0.0, "fx":[-26.85846,-26.85846,-26.85846,-26.85846], "fy":[-33.07247,-33.07247,-33.07247,-33.07247]}, + {"t":7.40496, "x":5.63343, "y":7.61928, "heading":-1.5708, "vx":-1.58102, "vy":1.22402, "omega":0.0, "ax":-4.61954, "ay":-7.04672, "alpha":0.0, "fx":[-78.57705,-78.57705,-78.57705,-78.57705], "fy":[-119.86276,-119.86276,-119.86276,-119.86276]}, + {"t":7.44216, "x":5.57142, "y":7.65993, "heading":-1.5708, "vx":-1.75286, "vy":0.96189, "omega":0.0, "ax":-3.71004, "ay":-8.43921, "alpha":0.0, "fx":[-63.10668,-63.10668,-63.10668,-63.10668], "fy":[-143.54849,-143.54849,-143.54849,-143.54849]}, + {"t":7.47936, "x":5.50365, "y":7.68988, "heading":-1.5708, "vx":-1.89086, "vy":0.64797, "omega":0.0, "ax":-2.2199, "ay":-8.94794, "alpha":0.0, "fx":[-37.75979,-37.75979,-37.75979,-37.75979], "fy":[-152.20197,-152.20197,-152.20197,-152.20197]}, + {"t":7.51781, "x":5.4293, "y":7.70818, "heading":-1.5708, "vx":-1.97623, "vy":0.30388, "omega":0.0, "ax":-0.60374, "ay":-8.20077, "alpha":0.0, "fx":[-10.26949,-10.26949,-10.26949,-10.26949], "fy":[-139.49274,-139.49274,-139.49274,-139.49274]}, + {"t":7.55627, "x":5.35286, "y":7.7138, "heading":-1.5708, "vx":-1.99944, "vy":-0.01148, "omega":0.0, "ax":0.0323, "ay":-1.55963, "alpha":0.0, "fx":[0.54948,0.54948,0.54948,0.54948], "fy":[-26.52891,-26.52891,-26.52891,-26.52891]}, + {"t":7.59472, "x":5.27599, "y":7.71221, "heading":-1.5708, "vx":-1.9982, "vy":-0.07145, "omega":0.0, "ax":-0.00623, "ay":0.18322, "alpha":0.0, "fx":[-0.10591,-0.10591,-0.10591,-0.10591], "fy":[3.11655,3.11655,3.11655,3.11655]}, + {"t":7.63318, "x":5.19915, "y":7.70959, "heading":-1.5708, "vx":-1.99844, "vy":-0.06441, "omega":0.0, "ax":-0.00676, "ay":0.22507, "alpha":0.0, "fx":[-0.11506,-0.11506,-0.11506,-0.11506], "fy":[3.82839,3.82839,3.82839,3.82839]}, + {"t":7.67163, "x":5.12229, "y":7.70728, "heading":-1.5708, "vx":-1.9987, "vy":-0.05575, "omega":0.0, "ax":-0.00502, "ay":0.19295, "alpha":0.0, "fx":[-0.08544,-0.08544,-0.08544,-0.08544], "fy":[3.28208,3.28208,3.28208,3.28208]}, + {"t":7.71009, "x":5.04543, "y":7.70528, "heading":-1.5708, "vx":-1.9989, "vy":-0.04833, "omega":0.0, "ax":-0.0038, "ay":0.1686, "alpha":0.0, "fx":[-0.06468,-0.06468,-0.06468,-0.06468], "fy":[2.86785,2.86785,2.86785,2.86785]}, + {"t":7.74854, "x":4.96856, "y":7.70355, "heading":-1.5708, "vx":-1.99904, "vy":-0.04185, "omega":0.0, "ax":-0.00295, "ay":0.15139, "alpha":0.0, "fx":[-0.05015,-0.05015,-0.05015,-0.05015], "fy":[2.57509,2.57509,2.57509,2.57509]}, + {"t":7.787, "x":4.89168, "y":7.70205, "heading":-1.5708, "vx":-1.99915, "vy":-0.03603, "omega":0.0, "ax":-0.00232, "ay":0.13879, "alpha":0.0, "fx":[-0.03939,-0.03939,-0.03939,-0.03939], "fy":[2.36084,2.36084,2.36084,2.36084]}, + {"t":7.82545, "x":4.81481, "y":7.70077, "heading":-1.5708, "vx":-1.99924, "vy":-0.03069, "omega":0.0, "ax":-0.00182, "ay":0.12933, "alpha":0.0, "fx":[-0.03103,-0.03103,-0.03103,-0.03103], "fy":[2.19993,2.19993,2.19993,2.19993]}, + {"t":7.8639, "x":4.73792, "y":7.69968, "heading":-1.5708, "vx":-1.99931, "vy":-0.02572, "omega":0.0, "ax":-0.00143, "ay":0.12215, "alpha":0.0, "fx":[-0.02428,-0.02428,-0.02428,-0.02428], "fy":[2.07765,2.07765,2.07765,2.07765]}, + {"t":7.90236, "x":4.66104, "y":7.69878, "heading":-1.5708, "vx":-1.99937, "vy":-0.02102, "omega":0.0, "ax":-0.0011, "ay":0.1167, "alpha":0.0, "fx":[-0.01864,-0.01864,-0.01864,-0.01864], "fy":[1.98498,1.98498,1.98498,1.98498]}, + {"t":7.94081, "x":4.58415, "y":7.69806, "heading":-1.5708, "vx":-1.99941, "vy":-0.01653, "omega":0.0, "ax":-0.00081, "ay":0.11265, "alpha":0.0, "fx":[-0.01377,-0.01377,-0.01377,-0.01377], "fy":[1.91614,1.91614,1.91614,1.91614]}, + {"t":7.97927, "x":4.50727, "y":7.69751, "heading":-1.5708, "vx":-1.99944, "vy":-0.0122, "omega":0.0, "ax":-0.00055, "ay":0.10978, "alpha":0.0, "fx":[-0.00942,-0.00942,-0.00942,-0.00942], "fy":[1.86741,1.86741,1.86741,1.86741]}, + {"t":8.01772, "x":4.43038, "y":7.69712, "heading":-1.5708, "vx":-1.99946, "vy":-0.00798, "omega":0.0, "ax":-0.00032, "ay":0.10796, "alpha":0.0, "fx":[-0.00542,-0.00542,-0.00542,-0.00542], "fy":[1.83642,1.83642,1.83642,1.83642]}, + {"t":8.05618, "x":4.35349, "y":7.69689, "heading":-1.5708, "vx":-1.99948, "vy":-0.00383, "omega":0.0, "ax":-0.00009, "ay":0.1071, "alpha":0.0, "fx":[-0.00161,-0.00161,-0.00161,-0.00161], "fy":[1.82177,1.82177,1.82177,1.82177]}, + {"t":8.09463, "x":4.2766, "y":7.69683, "heading":-1.5708, "vx":-1.99948, "vy":0.00029, "omega":0.0, "ax":0.00013, "ay":0.10717, "alpha":0.0, "fx":[0.00214,0.00214,0.00214,0.00214], "fy":[1.82298,1.82298,1.82298,1.82298]}, + {"t":8.13309, "x":4.19971, "y":7.69692, "heading":-1.5708, "vx":-1.99947, "vy":0.00441, "omega":0.0, "ax":0.00035, "ay":0.10833, "alpha":0.0, "fx":[0.00598,0.00598,0.00598,0.00598], "fy":[1.8426,1.8426,1.8426,1.8426]}, + {"t":8.17154, "x":4.12282, "y":7.69717, "heading":-1.5708, "vx":-1.99946, "vy":0.00858, "omega":0.0, "ax":0.00061, "ay":0.11323, "alpha":0.0, "fx":[0.01035,0.01035,0.01035,0.01035], "fy":[1.92601,1.92601,1.92601,1.92601]}, + {"t":8.21, "x":4.04593, "y":7.69758, "heading":-1.5708, "vx":-1.99944, "vy":0.01293, "omega":0.0, "ax":0.00136, "ay":0.16847, "alpha":0.0, "fx":[0.02317,0.02317,0.02317,0.02317], "fy":[2.86563,2.86563,2.86563,2.86563]}, + {"t":8.24845, "x":3.96905, "y":7.6982, "heading":-1.5708, "vx":-1.99939, "vy":0.01941, "omega":0.0, "ax":0.01344, "ay":1.08438, "alpha":0.0, "fx":[0.22866,0.22866,0.22866,0.22866], "fy":[18.44501,18.44501,18.44501,18.44501]}, + {"t":8.28691, "x":3.89217, "y":7.69975, "heading":-1.5708, "vx":-1.99887, "vy":0.06111, "omega":0.0, "ax":-9.39571, "ay":0.34953, "alpha":0.0, "fx":[-159.81839,-159.81839,-159.81839,-159.81839], "fy":[5.9454,5.9454,5.9454,5.9454]}, + {"t":8.31801, "x":3.82545, "y":7.70182, "heading":-1.5708, "vx":-2.29112, "vy":0.07198, "omega":0.0, "ax":-9.38327, "ay":0.37259, "alpha":0.0, "fx":[-159.60668,-159.60668,-159.60668,-159.60668], "fy":[6.3377,6.3377,6.3377,6.3377]}, + {"t":8.34912, "x":3.74965, "y":7.70424, "heading":-1.5708, "vx":-2.58299, "vy":0.08357, "omega":0.0, "ax":-9.35954, "ay":0.39469, "alpha":0.0, "fx":[-159.20307,-159.20307,-159.20307,-159.20307], "fy":[6.71357,6.71357,6.71357,6.71357]}, + {"t":8.38022, "x":3.66478, "y":7.70703, "heading":-1.5708, "vx":-2.87411, "vy":0.09585, "omega":0.0, "ax":-9.29309, "ay":0.40395, "alpha":0.0, "fx":[-158.07272,-158.07272,-158.07272,-158.07272], "fy":[6.87114,6.87114,6.87114,6.87114]}, + {"t":8.41133, "x":3.57088, "y":7.71021, "heading":-1.5708, "vx":-3.16317, "vy":0.10841, "omega":0.0, "ax":-7.06804, "ay":-0.40502, "alpha":0.0, "fx":[-120.22533,-120.22533,-120.22533,-120.22533], "fy":[-6.88933,-6.88933,-6.88933,-6.88933]}, + {"t":8.44243, "x":3.46907, "y":7.71338, "heading":-1.5708, "vx":-3.38303, "vy":0.09582, "omega":0.0, "ax":9.26277, "ay":-0.6224, "alpha":0.0, "fx":[157.55704,157.55704,157.55704,157.55704], "fy":[-10.58683,-10.58683,-10.58683,-10.58683]}, + {"t":8.47354, "x":3.36832, "y":7.71606, "heading":-1.5708, "vx":-3.09491, "vy":0.07646, "omega":0.0, "ax":9.33896, "ay":-0.67469, "alpha":0.0, "fx":[158.85303,158.85303,158.85303,158.85303], "fy":[-11.4763,-11.4763,-11.4763,-11.4763]}, + {"t":8.50464, "x":3.27658, "y":7.71811, "heading":-1.5708, "vx":-2.80442, "vy":0.05547, "omega":0.0, "ax":9.35528, "ay":-0.79145, "alpha":0.0, "fx":[159.13072,159.13072,159.13072,159.13072], "fy":[-13.46241,-13.46241,-13.46241,-13.46241]}, + {"t":8.53575, "x":3.19387, "y":7.71946, "heading":-1.5708, "vx":-2.51343, "vy":0.03085, "omega":0.0, "ax":9.34016, "ay":-1.06993, "alpha":0.0, "fx":[158.8735,158.8735,158.8735,158.8735], "fy":[-18.19917,-18.19917,-18.19917,-18.19917]}, + {"t":8.56685, "x":3.12021, "y":7.7199, "heading":-1.5708, "vx":-2.2229, "vy":-0.00243, "omega":0.0, "ax":9.0512, "ay":-2.57194, "alpha":0.0, "fx":[153.95828,153.95828,153.95828,153.95828], "fy":[-43.74798,-43.74798,-43.74798,-43.74798]}, + {"t":8.59795, "x":3.05544, "y":7.71858, "heading":-1.5708, "vx":-1.94136, "vy":-0.08243, "omega":0.0, "ax":8.3718, "ay":-4.30973, "alpha":0.0, "fx":[142.40198,142.40198,142.40198,142.40198], "fy":[-73.30729,-73.30729,-73.30729,-73.30729]}, + {"t":8.62906, "x":2.99911, "y":7.71393, "heading":-1.5708, "vx":-1.68096, "vy":-0.21648, "omega":0.0, "ax":7.7771, "ay":-5.31608, "alpha":0.0, "fx":[132.2862,132.2862,132.2862,132.2862], "fy":[-90.42496,-90.42496,-90.42496,-90.42496]}, + {"t":8.66016, "x":2.95058, "y":7.70462, "heading":-1.5708, "vx":-1.43905, "vy":-0.38184, "omega":0.0, "ax":7.3197, "ay":-5.9349, "alpha":0.0, "fx":[124.50597,124.50597,124.50597,124.50597], "fy":[-100.95094,-100.95094,-100.95094,-100.95094]}, + {"t":8.69127, "x":2.90936, "y":7.68988, "heading":-1.5708, "vx":-1.21138, "vy":-0.56644, "omega":0.0, "ax":6.33509, "ay":-2.69015, "alpha":22.59588, "fx":[159.51822,138.91701,25.42223,107.17466], "fy":[16.11691,78.50435,-158.32372,-119.3322]}, + {"t":8.71913, "x":2.87808, "y":7.67305, "heading":-1.56203, "vx":-1.03491, "vy":-0.64138, "omega":0.62943, "ax":6.29506, "ay":-2.56109, "alpha":23.01333, "fx":[159.27216,135.27984,25.81391,107.94305], "fy":[18.21107,84.39715,-158.24247,-118.61943]}, + {"t":8.74698, "x":2.85169, "y":7.65419, "heading":-1.53557, "vx":-0.85955, "vy":-0.71272, "omega":1.27048, "ax":6.3178, "ay":-2.44038, "alpha":23.01847, "fx":[158.86538,133.10615,28.04289,109.84147], "fy":[21.26044,87.38064,-157.84152,-116.84049]}, + {"t":8.77484, "x":2.8302, "y":7.63339, "heading":-1.49125, "vx":-0.68357, "vy":-0.7807, "omega":1.91168, "ax":6.42237, "ay":-2.35006, "alpha":22.47125, "fx":[158.25286,133.61355,32.24087,112.86364], "fy":[25.17084,85.83834,-157.01155,-113.89295]}, + {"t":8.80269, "x":2.81365, "y":7.61074, "heading":-1.42928, "vx":-0.50467, "vy":-0.84616, "omega":2.53763, "ax":6.65692, "ay":-2.38711, "alpha":20.91538, "fx":[157.41454,140.04902,38.52482,116.94117], "fy":[29.66389,73.13283,-155.55443,-109.65868]}, + {"t":8.83055, "x":2.80217, "y":7.58624, "heading":-1.35048, "vx":-0.31923, "vy":-0.91266, "omega":3.12024, "ax":7.0475, "ay":-2.88731, "alpha":17.32869, "fx":[156.40793,154.16974,46.99341,121.93291], "fy":[34.1837,26.54115,-153.1599,-104.01451]}, + {"t":8.8584, "x":2.79602, "y":7.5597, "heading":-1.25684, "vx":-0.12292, "vy":-0.99308, "omega":3.60295, "ax":7.06552, "ay":-4.05854, "alpha":13.28912, "fx":[155.44237,139.93677,57.73095,127.62004], "fy":[37.77211,-67.69788,-149.36774,-96.84513]}, + {"t":8.88626, "x":2.79533, "y":7.53046, "heading":-1.15132, "vx":0.0739, "vy":-1.10614, "omega":3.97313, "ax":6.89296, "ay":-4.47992, "alpha":12.47676, "fx":[155.02295,108.95483,71.21369,133.7976], "fy":[38.53572,-112.13511,-143.29526,-87.9141]}, + {"t":8.91412, "x":2.80007, "y":7.49791, "heading":-1.0358, "vx":0.26591, "vy":-1.23093, "omega":4.32068, "ax":7.37487, "ay":-4.16366, "alpha":10.95608, "fx":[156.1392,115.32139,89.7964,140.52045], "fy":[31.70344,-106.60284,-132.14459,-76.24651]}, + {"t":8.94197, "x":2.81033, "y":7.462, "heading":-0.9112, "vx":0.47134, "vy":-1.34691, "omega":4.62587, "ax":8.44634, "ay":-3.20609, "alpha":6.34574, "fx":[158.45738,143.46497,123.99759,148.75928], "fy":[3.1656,-65.0326,-99.55916,-56.7124]}, + {"t":8.96983, "x":2.82674, "y":7.42324, "heading":-0.77988, "vx":0.70662, "vy":-1.43622, "omega":4.80263, "ax":8.6999, "ay":0.58784, "alpha":-8.95116, "fx":[145.98052,157.61262,138.2192,150.11866], "fy":[-57.99567,-6.58153,73.61532,30.95809]}, + {"t":8.99768, "x":2.8498, "y":7.38346, "heading":-0.64957, "vx":0.94896, "vy":-1.41985, "omega":4.55329, "ax":8.11138, "ay":1.07643, "alpha":-10.20794, "fx":[142.24177,153.97745,120.67587,134.99421], "fy":[-55.15156,4.54401,90.28999,33.55694]}, + {"t":9.02554, "x":2.87938, "y":7.34433, "heading":-0.52669, "vx":1.17491, "vy":-1.38986, "omega":4.26894, "ax":-3.61886, "ay":4.75424, "alpha":13.01568, "fx":[-74.24279,-116.83756,-68.25045,13.10801], "fy":[113.99551,64.38576,30.69713,114.39437]}, + {"t":9.05339, "x":2.9107, "y":7.30746, "heading":-0.40273, "vx":1.0741, "vy":-1.25743, "omega":4.6315, "ax":-7.19871, "ay":2.49183, "alpha":13.7252, "fx":[-128.17463,-154.35332,-134.08483,-73.17929], "fy":[87.66093,17.98065,-61.39636,125.29592]}, + {"t":9.08125, "x":2.93783, "y":7.2734, "heading":-0.26839, "vx":0.87358, "vy":-1.18802, "omega":5.01383, "ax":-6.6037, "ay":4.94055, "alpha":-11.89146, "fx":[-151.00208,-26.33847,-118.0457,-153.92215], "fy":[33.15391,155.7824,106.90999,40.303]}, + {"t":9.10911, "x":2.9596, "y":7.24222, "heading":-0.13334, "vx":0.68963, "vy":-1.05039, "omega":4.68258, "ax":-6.01465, "ay":5.10974, "alpha":-13.92836, "fx":[-136.60244,6.44896,-121.10592,-157.97049], "fy":[59.96826,159.26494,104.41754,24.01033]}, + {"t":9.13696, "x":2.97648, "y":7.21494, "heading":-0.00831, "vx":0.52208, "vy":-0.90806, "omega":4.2946, "ax":-3.04766, "ay":5.96141, "alpha":-22.38944, "fx":[70.58429,8.15638,-126.51224,-159.58758], "fy":[136.24887,159.63688,98.13683,11.58515]}, + {"t":9.16482, "x":2.98984, "y":7.19196, "heading":0.10264, "vx":0.43719, "vy":-0.742, "omega":3.67092, "ax":-2.80735, "ay":5.5457, "alpha":-25.53301, "fx":[97.17655,3.43803,-131.5124,-160.11127], "fy":[124.4305,160.0145,91.50495,1.37306]}, + {"t":9.19267, "x":3.00093, "y":7.17345, "heading":0.19499, "vx":0.35899, "vy":-0.58752, "omega":2.95968, "ax":-2.97329, "ay":5.38037, "alpha":-26.33844, "fx":[95.65382,-2.39653,-135.51385,-160.04294], "fy":[127.04155,160.14735,85.57941,-6.69382]}, + {"t":9.22053, "x":3.00978, "y":7.15917, "heading":0.26721, "vx":0.27616, "vy":-0.43764, "omega":2.226, "ax":-3.16122, "ay":5.2816, "alpha":-26.62616, "fx":[90.80826,-7.70592,-138.45871,-159.72914], "fy":[131.12667,160.05007,80.80654,-12.62907]}, + {"t":9.24838, "x":3.01624, "y":7.14903, "heading":0.31889, "vx":0.18811, "vy":-0.29052, "omega":1.48431, "ax":-3.31874, "ay":5.22393, "alpha":-26.68607, "fx":[85.91966,-11.88609,-140.42471,-159.41188], "fy":[134.66884,159.84067,77.39808,-16.47765]}, + {"t":9.27624, "x":3.02019, "y":7.14296, "heading":0.34988, "vx":0.09566, "vy":-0.145, "omega":0.74095, "ax":-3.43411, "ay":5.20553, "alpha":-26.59954, "fx":[81.78841,-14.69937,-141.50083,-159.24081], "fy":[137.37744,159.63904,75.45811,-18.2964]}, + {"t":9.3041, "x":3.02153, "y":7.14094, "heading":0.3602, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0,0.0,0.0,0.0]}], "splits":[0] }, "events":[] diff --git a/src/main/deploy/v1_doomspiral/choreo/RIGHT_DOT_1.traj b/src/main/deploy/v1_doomspiral/choreo/RIGHT_DOT_1.traj new file mode 100644 index 00000000..399a7348 --- /dev/null +++ b/src/main/deploy/v1_doomspiral/choreo/RIGHT_DOT_1.traj @@ -0,0 +1,29 @@ +{ + "name":"RIGHT_DOT_1", + "version":3, + "snapshot":{ + "waypoints":[], + "constraints":[], + "targetDt":0.05 + }, + "params":{ + "waypoints":[ + {"x":{"exp":"3.545992612838745 m", "val":3.545992612838745}, "y":{"exp":"0.43048295378685 m", "val":0.43048295378685}, "heading":{"exp":"90 deg", "val":1.5707963267948966}, "intervals":17, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], + "constraints":[ + {"from":"first", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, + {"from":"last", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, + {"from":"first", "to":"last", "data":{"type":"KeepInRectangle", "props":{"x":{"exp":"0 m", "val":0.0}, "y":{"exp":"0 m", "val":0.0}, "w":{"exp":"16.541 m", "val":16.541}, "h":{"exp":"8.0692 m", "val":8.0692}}}, "enabled":false}], + "targetDt":{ + "exp":"0.05 s", + "val":0.05 + } + }, + "trajectory":{ + "config":null, + "sampleType":null, + "waypoints":[], + "samples":[], + "splits":[] + }, + "events":[] +} diff --git a/src/main/deploy/v1_doomspiral/choreo/RIGHT_RETURN_TO_MID.traj b/src/main/deploy/v1_doomspiral/choreo/RIGHT_RETURN_TO_MID.traj index c1ddec03..d6ced8c0 100644 --- a/src/main/deploy/v1_doomspiral/choreo/RIGHT_RETURN_TO_MID.traj +++ b/src/main/deploy/v1_doomspiral/choreo/RIGHT_RETURN_TO_MID.traj @@ -5,8 +5,8 @@ "waypoints":[ {"x":2.818082809448242, "y":1.564, "heading":2.491592653589793, "intervals":20, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, {"x":3.611614465713501, "y":0.5622705116, "heading":0.0, "intervals":21, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":5.039078712463379, "y":0.5021833076, "heading":3.141592653589793, "intervals":34, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, - {"x":7.840170383453369, "y":0.7852743759, "heading":1.5707963267948966, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], + {"x":5.039078712463379, "y":0.5021833076, "heading":3.141592653589793, "intervals":37, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, + {"x":8.256524, "y":0.634238, "heading":1.5707963267948966, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], "constraints":[ {"from":"first", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, {"from":"last", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, @@ -17,8 +17,8 @@ "waypoints":[ {"x":{"exp":"2.818082809448242 m", "val":2.818082809448242}, "y":{"exp":"1.564 m", "val":1.564}, "heading":{"exp":"(pi - 0.65) rad", "val":2.491592653589793}, "intervals":20, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, {"x":{"exp":"3.611614465713501 m", "val":3.611614465713501}, "y":{"exp":"0.5622705116 m", "val":0.5622705116}, "heading":{"exp":"0 deg", "val":0.0}, "intervals":21, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":{"exp":"5.039078712463379 m", "val":5.039078712463379}, "y":{"exp":"0.5021833076 m", "val":0.5021833076}, "heading":{"exp":"180 deg", "val":3.141592653589793}, "intervals":34, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, - {"x":{"exp":"7.840170383453369 m", "val":7.840170383453369}, "y":{"exp":"0.7852743759 m", "val":0.7852743759}, "heading":{"exp":"90 deg", "val":1.5707963267948966}, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], + {"x":{"exp":"5.039078712463379 m", "val":5.039078712463379}, "y":{"exp":"0.5021833076 m", "val":0.5021833076}, "heading":{"exp":"180 deg", "val":3.141592653589793}, "intervals":37, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, + {"x":{"exp":"8.256524 m", "val":8.256524}, "y":{"exp":"0.634238 m", "val":0.634238}, "heading":{"exp":"90 deg", "val":1.5707963267948966}, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], "constraints":[ {"from":"first", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, {"from":"last", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, @@ -53,84 +53,87 @@ "differentialTrackWidth":0.5588 }, "sampleType":"Swerve", - "waypoints":[0.0,0.58552,1.00098,2.08094], + "waypoints":[0.0,0.58527,1.00053,2.19591], "samples":[ - {"t":0.0, "x":2.81808, "y":1.564, "heading":2.49159, "vx":0.0, "vy":0.0, "omega":0.0, "ax":3.2941, "ay":-7.80471, "alpha":11.23706, "fx":[59.45493,122.28381,96.23794,-53.84971], "fy":[-148.95302,-103.73419,-127.39912,-150.93709]}, - {"t":0.02928, "x":2.81949, "y":1.56066, "heading":2.49159, "vx":0.09644, "vy":-0.22849, "omega":0.32897, "ax":3.34746, "ay":-7.84257, "alpha":10.89442, "fx":[60.06564,121.60838,95.4449,-49.36171], "fy":[-148.68999,-104.49651,-127.98002,-152.43266]}, - {"t":0.05855, "x":2.82375, "y":1.55061, "heading":2.50122, "vx":0.19444, "vy":-0.45809, "omega":0.64792, "ax":3.40028, "ay":-7.89863, "alpha":10.44624, "fx":[61.39657,121.16584,92.39596,-43.60739], "fy":[-148.12314,-104.97305,-130.17568,-154.14202]}, - {"t":0.08783, "x":2.8309, "y":1.53381, "heading":2.52019, "vx":0.29398, "vy":-0.68933, "omega":0.95374, "ax":3.46731, "ay":-7.96146, "alpha":9.91086, "fx":[63.43175,120.84769,87.96105,-36.32871], "fy":[-147.23445,-105.29225,-133.18526,-155.97636]}, - {"t":0.1171, "x":2.84099, "y":1.51022, "heading":2.54811, "vx":0.39549, "vy":-0.9224, "omega":1.24389, "ax":3.5667, "ay":-8.02006, "alpha":9.28771, "fx":[66.15718,120.47736,83.11313,-27.07325], "fy":[-145.99185,-105.6534,-136.23467,-157.79554]}, - {"t":0.14638, "x":2.8541, "y":1.47978, "heading":2.58453, "vx":0.49991, "vy":-1.1572, "omega":1.51579, "ax":3.7229, "ay":-8.06426, "alpha":8.52986, "fx":[69.5728,119.75302,79.01327,-15.03698], "fy":[-144.34151,-106.38785,-138.62496,-159.32852]}, - {"t":0.17565, "x":2.87033, "y":1.44244, "heading":2.62891, "vx":0.6089, "vy":-1.39328, "omega":1.76551, "ax":3.96895, "ay":-8.08305, "alpha":7.50342, "fx":[73.71769,118.1059,76.9772,1.24199], "fy":[-142.18802,-108.09022,-139.73942,-159.94356]}, - {"t":0.20493, "x":2.88986, "y":1.39819, "heading":2.68059, "vx":0.72509, "vy":-1.62992, "omega":1.98518, "ax":4.34844, "ay":-8.05627, "alpha":5.91166, "fx":[78.74024,114.29905,78.35268,24.47067], "fy":[-139.33311,-111.92151,-138.94739,-157.93766]}, - {"t":0.23421, "x":2.91295, "y":1.34702, "heading":2.73871, "vx":0.8524, "vy":-1.86578, "omega":2.15825, "ax":4.90692, "ay":-7.92428, "alpha":3.12237, "fx":[85.16776,105.05466,84.3218,59.31679], "fy":[-135.22694,-120.33973,-135.38338,-148.20857]}, - {"t":0.26348, "x":2.94001, "y":1.289, "heading":2.80189, "vx":0.99605, "vy":-2.09777, "omega":2.24966, "ax":5.57765, "ay":-7.4856, "alpha":-2.16373, "fx":[95.57361,79.1413,95.54,109.24209], "fy":[-127.28632,-138.24422,-127.69247,-116.08834]}, - {"t":0.29276, "x":2.97156, "y":1.22438, "heading":2.86775, "vx":1.15934, "vy":-2.31691, "omega":2.18631, "ax":5.98786, "ay":-5.27051, "alpha":-13.41567, "fx":[146.35055,-3.62562,111.37697,153.30495], "fy":[-42.71677,-158.44198,-114.12927,-43.31126]}, - {"t":0.32203, "x":3.00806, "y":1.15429, "heading":2.93176, "vx":1.33464, "vy":-2.47121, "omega":1.79356, "ax":6.70911, "ay":-3.44271, "alpha":-15.93055, "fx":[148.82981,22.93395,126.77709,157.93922], "fy":[37.90079,-156.11565,-96.42007,-19.60319]}, - {"t":0.35131, "x":3.05001, "y":1.08047, "heading":2.98427, "vx":1.53106, "vy":-2.572, "omega":1.32718, "ax":7.63944, "ay":-1.78076, "alpha":-14.42201, "fx":[136.30245,81.44505,143.15532,158.87575], "fy":[75.49168,-133.80543,-69.28905,6.44198]}, - {"t":0.38059, "x":3.09811, "y":1.00441, "heading":3.02312, "vx":1.75471, "vy":-2.62413, "omega":0.90496, "ax":8.38058, "ay":1.07406, "alpha":-10.48741, "fx":[112.61467,147.69524,156.20598,153.68941], "fy":[109.73226,-48.40526,-28.85021,40.601]}, - {"t":0.40986, "x":3.15307, "y":0.92805, "heading":3.04962, "vx":2.00006, "vy":-2.59269, "omega":0.59794, "ax":8.17674, "ay":3.88998, "alpha":-5.90881, "fx":[110.18975,145.82834,156.95917,143.35857], "fy":[113.46197,58.66813,23.85158,68.68798]}, - {"t":0.43914, "x":3.21513, "y":0.85381, "heading":3.06712, "vx":2.23944, "vy":-2.47881, "omega":0.42495, "ax":7.40988, "ay":5.5768, "alpha":-3.13118, "fx":[109.70342,124.77886,140.49578,129.18143], "fy":[114.75081,97.67932,74.22276,92.78613]}, - {"t":0.46841, "x":3.28386, "y":0.78363, "heading":3.07956, "vx":2.45637, "vy":-2.31554, "omega":0.33328, "ax":6.50852, "ay":6.7079, "alpha":-1.33529, "fx":[103.40454,108.78384,117.87621,112.76766], "fy":[120.97488,116.04874,106.95296,112.42115]}, - {"t":0.49769, "x":3.35857, "y":0.71872, "heading":3.08932, "vx":2.64691, "vy":-2.11916, "omega":0.29419, "ax":5.6966, "ay":7.44117, "alpha":-0.44288, "fx":[94.40203,95.97033,99.39356,97.82451], "fy":[128.46626,127.27309,124.64484,125.90475]}, - {"t":0.52696, "x":3.4385, "y":0.65986, "heading":3.09793, "vx":2.81368, "vy":-1.90132, "omega":0.28123, "ax":5.02492, "ay":7.92527, "alpha":-0.22567, "fx":[84.18391,84.89859,86.76367,86.04366], "fy":[135.62093,135.16465,133.98369,134.45707]}, - {"t":0.55624, "x":3.52302, "y":0.6076, "heading":3.10617, "vx":2.96079, "vy":-1.6693, "omega":0.27462, "ax":4.46064, "ay":8.26505, "alpha":-0.51477, "fx":[72.93303,74.38268,78.83851,77.34282], "fy":[142.16152,141.38996,138.96851,139.82428]}, - {"t":0.58552, "x":3.61161, "y":0.56227, "heading":3.1142, "vx":3.09138, "vy":-1.42733, "omega":0.25955, "ax":3.97999, "ay":8.48331, "alpha":-0.6779, "fx":[63.84741,65.54871,71.60048,69.7976], "fy":[146.09063,145.30055,142.43896,143.36433]}, - {"t":0.6053, "x":3.67355, "y":0.53569, "heading":3.11934, "vx":3.17012, "vy":-1.2595, "omega":0.24614, "ax":3.70561, "ay":8.58912, "alpha":-1.17349, "fx":[56.42944,59.10615,69.8014,66.78827], "fy":[148.90631,147.79342,143.09832,144.5956]}, - {"t":0.62508, "x":3.737, "y":0.51246, "heading":3.12421, "vx":3.24343, "vy":-1.08957, "omega":0.22292, "ax":3.38726, "ay":8.69597, "alpha":-1.62221, "fx":[48.61128,51.87278,66.97504,63.00588], "fy":[151.36242,150.15819,144.128,146.01537]}, - {"t":0.64487, "x":3.80183, "y":0.4926, "heading":3.12862, "vx":3.31044, "vy":-0.91753, "omega":0.19083, "ax":3.0164, "ay":8.80103, "alpha":-1.96297, "fx":[40.59421,43.9392,62.58923,58.10992], "fy":[153.31569,152.21491,145.63778,147.64353]}, - {"t":0.66465, "x":3.86791, "y":0.47617, "heading":3.1324, "vx":3.37012, "vy":-0.74341, "omega":0.15199, "ax":2.57187, "ay":8.90051, "alpha":-2.13814, "fx":[32.31654,35.22846,55.90946,51.53235], "fy":[154.67473,153.79183,147.65138,149.46256]}, - {"t":0.68444, "x":3.93509, "y":0.46321, "heading":3.1354, "vx":3.421, "vy":-0.56732, "omega":0.10969, "ax":2.02444, "ay":8.97962, "alpha":-2.06318, "fx":[23.68142,25.71904,45.92535,42.41444], "fy":[155.21231,154.56323,149.92546,151.26223]}, - {"t":0.70422, "x":4.00317, "y":0.45374, "heading":3.13757, "vx":3.46106, "vy":-0.38966, "omega":0.06887, "ax":1.35898, "ay":8.98536, "alpha":-1.64145, "fx":[14.82509,15.81044,31.9006,29.92718], "fy":[154.22431,153.7281,151.30621,152.09516]}, - {"t":0.724, "x":4.07191, "y":0.44779, "heading":3.13893, "vx":3.48794, "vy":-0.2119, "omega":0.0364, "ax":0.72817, "ay":8.69833, "alpha":-1.35127, "fx":[5.8834,6.1849,19.22366,18.25212], "fy":[148.78904,148.10731,147.07182,147.85603]}, - {"t":0.74379, "x":4.14106, "y":0.4453, "heading":3.13965, "vx":3.50235, "vy":-0.03981, "omega":0.00966, "ax":-0.03652, "ay":7.46599, "alpha":-0.10894, "fx":[-1.09977,-1.10538,-0.14057,-0.13876], "fy":[127.06775,126.91789,126.9211,127.07093]}, - {"t":0.76357, "x":4.21034, "y":0.44597, "heading":3.13985, "vx":3.50163, "vy":0.1079, "omega":0.00751, "ax":-0.22478, "ay":5.04539, "alpha":-0.00679, "fx":[-3.84945,-3.85023,-3.79734,-3.79658], "fy":[85.83003,85.81014,85.81114,85.83103]}, - {"t":0.78336, "x":4.27957, "y":0.44909, "heading":3.13999, "vx":3.49718, "vy":0.20772, "omega":0.00737, "ax":-0.14202, "ay":2.17042, "alpha":-0.00071, "fx":[-2.41822,-2.41824,-2.41332,-2.41329], "fy":[36.91974,36.91675,36.91677,36.91976]}, - {"t":0.80314, "x":4.34873, "y":0.45363, "heading":3.14014, "vx":3.49437, "vy":0.25066, "omega":0.00736, "ax":-0.03645, "ay":0.49657, "alpha":0.00012, "fx":[-0.61969,-0.61968,-0.62048,-0.62048], "fy":[8.44618,8.44672,8.44672,8.44618]}, - {"t":0.82293, "x":4.41786, "y":0.45868, "heading":3.14029, "vx":3.49365, "vy":0.26048, "omega":0.00736, "ax":0.01085, "ay":-0.14832, "alpha":0.00016, "fx":[0.18507,0.18507,0.18398,0.18398], "fy":[-2.52326,-2.52252,-2.52252,-2.52326]}, - {"t":0.84271, "x":4.48698, "y":0.46381, "heading":3.14043, "vx":3.49386, "vy":0.25755, "omega":0.00737, "ax":0.02547, "ay":-0.35193, "alpha":0.00013, "fx":[0.43369,0.43369,0.43285,0.43285], "fy":[-5.98644,-5.98587,-5.98587,-5.98644]}, - {"t":0.86249, "x":4.55611, "y":0.46884, "heading":3.14058, "vx":3.49437, "vy":0.25059, "omega":0.00737, "ax":0.02703, "ay":-0.38414, "alpha":0.0001, "fx":[0.46016,0.46016,0.45948,0.45948], "fy":[-6.53438,-6.53392,-6.53392,-6.53438]}, - {"t":0.88228, "x":4.62525, "y":0.47372, "heading":3.14072, "vx":3.4949, "vy":0.24299, "omega":0.00737, "ax":0.02348, "ay":-0.34372, "alpha":0.00009, "fx":[0.39972,0.39972,0.39912,0.39912], "fy":[-5.84685,-5.84645,-5.84645,-5.84685]}, - {"t":0.90206, "x":4.69439, "y":0.47846, "heading":3.14087, "vx":3.49537, "vy":0.23619, "omega":0.00737, "ax":0.01682, "ay":-0.2526, "alpha":0.00009, "fx":[0.28634,0.28634,0.28575,0.28575], "fy":[-4.29687,-4.29647,-4.29647,-4.29687]}, - {"t":0.92185, "x":4.76355, "y":0.48308, "heading":3.14101, "vx":3.4957, "vy":0.23119, "omega":0.00737, "ax":0.00539, "ay":-0.08278, "alpha":0.0001, "fx":[0.09199,0.09199,0.09131,0.09131], "fy":[-1.40837,-1.4079,-1.4079,-1.40837]}, - {"t":0.94163, "x":4.83271, "y":0.48764, "heading":3.14116, "vx":3.4958, "vy":0.22955, "omega":0.00738, "ax":-0.01732, "ay":0.25728, "alpha":0.00041, "fx":[-0.29331,-0.29331,-0.29606,-0.29606], "fy":[4.37527,4.37712,4.37713,4.37527]}, - {"t":0.96141, "x":4.90187, "y":0.49223, "heading":3.14131, "vx":3.49546, "vy":0.23464, "omega":0.00738, "ax":-0.11115, "ay":0.97088, "alpha":-0.0154, "fx":[-1.94251,-1.9427,-1.83878,-1.83859], "fy":[16.54864,16.47983,16.48005,16.54887]}, - {"t":0.9812, "x":4.971, "y":0.49706, "heading":3.14145, "vx":3.49326, "vy":0.25385, "omega":0.00708, "ax":-5.28022, "ay":0.50332, "alpha":-16.27856, "fx":[-128.38506,-131.54278,-53.42353,-45.90904], "fy":[43.23112,-29.43313,-69.87738,90.32461]}, - {"t":1.00098, "x":5.03908, "y":0.50218, "heading":3.14159, "vx":3.3888, "vy":0.26381, "omega":-0.31498, "ax":2.06067, "ay":3.1197, "alpha":6.69763, "fx":[61.33801,55.34874,10.92343,12.59524], "fy":[36.34627,63.55908,70.44309,41.91262]}, - {"t":1.03275, "x":5.14776, "y":0.51214, "heading":3.13159, "vx":3.45425, "vy":0.3629, "omega":-0.10224, "ax":0.11252, "ay":0.74033, "alpha":0.55068, "fx":[3.75876,3.77465,0.07371,0.04877], "fy":[11.33354,13.80854,13.85262,11.37649]}, - {"t":1.06451, "x":5.25753, "y":0.52404, "heading":3.12834, "vx":3.45783, "vy":0.38641, "omega":-0.08475, "ax":0.00402, "ay":0.08025, "alpha":0.04025, "fx":[0.20214,0.20456,-0.06528,-0.0677], "fy":[1.27194,1.45438,1.45795,1.27552]}, - {"t":1.09627, "x":5.36737, "y":0.53635, "heading":3.12565, "vx":3.45795, "vy":0.38896, "omega":-0.08347, "ax":0.00649, "ay":-0.05216, "alpha":0.00431, "fx":[0.12473,0.12505,0.09618,0.09586], "fy":[-0.89722,-0.8777,-0.87723,-0.89676]}, - {"t":1.12804, "x":5.47721, "y":0.54868, "heading":3.123, "vx":3.45816, "vy":0.38731, "omega":-0.08333, "ax":0.00771, "ay":-0.07358, "alpha":0.00088, "fx":[0.13403,0.13411,0.12821,0.12814], "fy":[-1.25368,-1.24969,-1.24958,-1.25357]}, - {"t":1.1598, "x":5.58705, "y":0.56094, "heading":3.12035, "vx":3.45841, "vy":0.38497, "omega":-0.0833, "ax":0.00763, "ay":-0.07265, "alpha":0.0011, "fx":[0.13347,0.13358,0.1262,0.12609], "fy":[-1.23828,-1.23329,-1.23313,-1.23812]}, - {"t":1.19156, "x":5.69691, "y":0.57314, "heading":3.1177, "vx":3.45865, "vy":0.38266, "omega":-0.08327, "ax":0.0073, "ay":-0.0679, "alpha":0.00181, "fx":[0.13017,0.13037,0.11825,0.11805], "fy":[-1.15928,-1.15109,-1.1508,-1.15899]}, - {"t":1.22333, "x":5.80677, "y":0.58526, "heading":3.11506, "vx":3.45888, "vy":0.3805, "omega":-0.08321, "ax":0.00696, "ay":-0.06308, "alpha":0.00249, "fx":[0.12666,0.12696,0.11026,0.10996], "fy":[-1.07887,-1.06758,-1.06713,-1.07843]}, - {"t":1.25509, "x":5.91664, "y":0.59731, "heading":3.11242, "vx":3.4591, "vy":0.3785, "omega":-0.08313, "ax":0.00666, "ay":-0.05881, "alpha":0.00308, "fx":[0.12343,0.12384,0.10321,0.1028], "fy":[-1.00757,-0.99362,-0.99302,-1.00697]}, - {"t":1.28685, "x":6.02652, "y":0.6093, "heading":3.10978, "vx":3.45931, "vy":0.37663, "omega":-0.08303, "ax":0.0064, "ay":-0.0551, "alpha":0.00357, "fx":[0.1205,0.12102,0.09711,0.0966], "fy":[-0.94574,-0.92958,-0.92882,-0.94499]}, - {"t":1.31862, "x":6.1364, "y":0.62124, "heading":3.10714, "vx":3.45952, "vy":0.37488, "omega":-0.08292, "ax":0.00616, "ay":-0.05192, "alpha":0.00397, "fx":[0.11786,0.11847,0.09187,0.09125], "fy":[-0.89255,-0.87456,-0.87365,-0.89164]}, - {"t":1.35038, "x":6.24629, "y":0.63312, "heading":3.1045, "vx":3.45971, "vy":0.37323, "omega":-0.08279, "ax":0.00597, "ay":-0.04933, "alpha":0.00428, "fx":[0.11556,0.11628,0.08759,0.08688], "fy":[-0.84933,-0.82993,-0.82887,-0.84827]}, - {"t":1.38214, "x":6.35618, "y":0.64495, "heading":3.10187, "vx":3.4599, "vy":0.37167, "omega":-0.08266, "ax":0.00586, "ay":-0.04804, "alpha":0.00442, "fx":[0.11411,0.1149,0.08531,0.08452], "fy":[-0.82766,-0.80766,-0.80649,-0.8265]}, - {"t":1.41391, "x":6.46609, "y":0.65673, "heading":3.09925, "vx":3.46009, "vy":0.37014, "omega":-0.08252, "ax":0.00604, "ay":-0.05153, "alpha":0.00389, "fx":[0.11542,0.11616,0.09011,0.08937], "fy":[-0.88591,-0.86829,-0.8672,-0.88482]}, - {"t":1.44567, "x":6.57599, "y":0.66846, "heading":3.09663, "vx":3.46028, "vy":0.3685, "omega":-0.08239, "ax":0.00702, "ay":-0.07637, "alpha":-0.00085, "fx":[0.11664,0.11645,0.12216,0.12235], "fy":[-1.29702,-1.30087,-1.30116,-1.29731]}, - {"t":1.47743, "x":6.68591, "y":0.68013, "heading":3.09401, "vx":3.4605, "vy":0.36608, "omega":-0.08242, "ax":-0.00002, "ay":-0.20037, "alpha":-0.05682, "fx":[-0.18458,-0.19687,0.18373,0.19604], "fy":[-3.27049,-3.52763,-3.54581,-3.28868]}, - {"t":1.5092, "x":6.79582, "y":0.69166, "heading":3.09139, "vx":3.4605, "vy":0.35971, "omega":-0.08423, "ax":-0.31416, "ay":-0.79033, "alpha":-1.08475, "fx":[-8.88826,-9.0895,-1.81837,-1.57867], "fy":[-10.8102,-15.6741,-16.08168,-11.20745]}, - {"t":1.54096, "x":6.90558, "y":0.70268, "heading":3.08872, "vx":3.45052, "vy":0.33461, "omega":-0.11868, "ax":-3.63721, "ay":-2.36543, "alpha":-10.58081, "fx":[-98.93396,-91.52001,-26.74799,-30.26963], "fy":[-8.12586,-55.50732,-78.34653,-18.96174]}, - {"t":1.57272, "x":7.01335, "y":0.71212, "heading":3.08495, "vx":3.33499, "vy":0.25948, "omega":-0.45476, "ax":-5.55506, "ay":-2.19691, "alpha":-15.55921, "fx":[-139.73725,-128.90132,-40.07331,-69.24801], "fy":[12.41571,-60.95261,-120.06875,19.13048]}, - {"t":1.60449, "x":7.11648, "y":0.71925, "heading":3.0705, "vx":3.15854, "vy":0.18969, "omega":-0.94898, "ax":-6.27255, "ay":-1.47943, "alpha":-16.74483, "fx":[-147.14813,-139.48123,-51.55077,-88.5968], "fy":[23.72868,-55.48265,-128.70061,59.79597]}, - {"t":1.63625, "x":7.21364, "y":0.72453, "heading":3.04036, "vx":2.95931, "vy":0.1427, "omega":-1.48085, "ax":-6.7212, "ay":-0.73737, "alpha":-16.91412, "fx":[-148.55736,-146.35379,-68.32213,-94.06927], "fy":[35.18561,-45.34213,-126.55255,86.53951]}, - {"t":1.66801, "x":7.30425, "y":0.72869, "heading":2.99332, "vx":2.74582, "vy":0.11928, "omega":-2.0181, "ax":-7.01356, "ay":-0.0138, "alpha":-16.64847, "fx":[-147.21699,-151.41599,-86.36011,-92.20184], "fy":[47.32766,-32.8562,-118.84655,103.43583]}, - {"t":1.69978, "x":7.38792, "y":0.73247, "heading":2.92922, "vx":2.52304, "vy":0.11884, "omega":-2.54691, "ax":-7.16811, "ay":0.64934, "alpha":-16.32367, "fx":[-144.17087,-154.76982,-101.40056,-87.36844], "fy":[59.23013,-20.01905,-109.56583,114.53486]}, - {"t":1.73154, "x":7.46445, "y":0.73658, "heading":2.84832, "vx":2.29536, "vy":0.13947, "omega":-3.06541, "ax":-7.19009, "ay":1.1714, "alpha":-16.26115, "fx":[-140.15993,-156.61841,-110.54264,-81.88444], "fy":[70.01629,-8.53042,-103.39352,121.60867]}, - {"t":1.7633, "x":7.53373, "y":0.7416, "heading":2.75096, "vx":2.06698, "vy":0.17668, "omega":-3.58192, "ax":-7.0861, "ay":1.47534, "alpha":-16.7046, "fx":[-135.81174,-157.43604,-112.30226,-76.58008], "fy":[79.20425,0.22272,-104.24846,125.20202]}, - {"t":1.79507, "x":7.59581, "y":0.74795, "heading":2.63718, "vx":1.8419, "vy":0.22354, "omega":-4.11251, "ax":-6.84759, "ay":1.49829, "alpha":-17.7371, "fx":[-131.56217,-157.80046,-105.91881,-70.62057], "fy":[86.72073,5.1522,-113.08732,123.15625]}, - {"t":1.82683, "x":7.65086, "y":0.75581, "heading":2.50655, "vx":1.6244, "vy":0.27113, "omega":-4.6759, "ax":-6.54081, "ay":-0.78004, "alpha":-19.19175, "fx":[-128.43866,-158.14935,-94.60234,-63.83901], "fy":[91.61053,5.4343,-124.68007,-25.43795]}, - {"t":1.85859, "x":7.69916, "y":0.76403, "heading":2.35803, "vx":1.41664, "vy":0.24635, "omega":-5.2855, "ax":-9.22377, "ay":-1.08433, "alpha":1.22242, "fx":[-155.73531,-156.54936,-157.84526,-157.44514], "fy":[-27.28948,-21.19432,-9.4666,-15.82614]}, - {"t":1.89036, "x":7.7395, "y":0.77131, "heading":2.19015, "vx":1.12366, "vy":0.21191, "omega":-5.24667, "ax":-7.37972, "ay":-1.96787, "alpha":19.07854, "fx":[-118.27364,-91.23024,-133.46086,-159.14293], "fy":[-107.18826,-128.96639,87.41411,14.84887]}, - {"t":1.92212, "x":7.77147, "y":0.77704, "heading":2.02349, "vx":0.88926, "vy":0.1494, "omega":-4.64067, "ax":-6.08655, "ay":-1.74954, "alpha":26.4233, "fx":[-114.87262,-31.81541,-110.79207,-156.64157], "fy":[-111.41129,-156.04368,115.27056,33.14784]}, - {"t":1.95388, "x":7.79665, "y":0.78091, "heading":1.87609, "vx":0.69593, "vy":0.09383, "omega":-3.80138, "ax":-5.69879, "ay":-1.26218, "alpha":28.73715, "fx":[-119.29788,-23.68995,-91.90401,-152.84701], "fy":[-106.90617,-158.02808,130.99954,48.0574]}, - {"t":1.98565, "x":7.81588, "y":0.78325, "heading":1.75535, "vx":0.51491, "vy":0.05374, "omega":-2.88859, "ax":-5.52431, "ay":-0.84689, "alpha":29.7654, "fx":[-124.43153,-27.27444,-75.49152,-148.66995], "fy":[-101.02065,-157.69228,141.18404,59.90734]}, - {"t":2.01741, "x":7.82944, "y":0.78453, "heading":1.66359, "vx":0.33944, "vy":0.02684, "omega":-1.94314, "ax":-5.39831, "ay":-0.52957, "alpha":30.37302, "fx":[-128.57124,-32.25019,-61.67444,-144.79904], "fy":[-95.78258,-156.87926,147.7997,68.8308]}, - {"t":2.04917, "x":7.8375, "y":0.78512, "heading":1.60187, "vx":0.16797, "vy":0.01002, "omega":-0.97839, "ax":-5.28827, "ay":-0.31546, "alpha":30.80231, "fx":[-131.29709,-35.74169,-51.00752,-141.76182], "fy":[-92.06942,-156.19705,151.8553,74.94794]}, - {"t":2.08094, "x":7.84017, "y":0.78527, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0,0.0,0.0,0.0]}], + {"t":0.0, "x":2.81808, "y":1.564, "heading":2.49159, "vx":0.0, "vy":0.0, "omega":0.0, "ax":3.31927, "ay":-7.79875, "alpha":11.20893, "fx":[59.68891,122.32717,96.93608,-53.11311], "fy":[-148.85902,-103.68264,-126.8797,-151.19684]}, + {"t":0.02926, "x":2.8195, "y":1.56066, "heading":2.49639, "vx":0.09713, "vy":-0.22822, "omega":0.32801, "ax":3.36245, "ay":-7.84742, "alpha":10.83541, "fx":[60.59253,121.82688,94.78114,-48.42317], "fy":[-148.47561,-104.24077,-128.47975,-152.73341]}, + {"t":0.05853, "x":2.82379, "y":1.55062, "heading":2.51063, "vx":0.19553, "vy":-0.45786, "omega":0.64509, "ax":3.40835, "ay":-7.90973, "alpha":10.37428, "fx":[62.18644,121.53549,90.68086,-42.50279], "fy":[-147.79274,-104.54334,-131.3816,-154.4514]}, + {"t":0.08779, "x":2.83097, "y":1.53384, "heading":2.53395, "vx":0.29527, "vy":-0.68932, "omega":0.94868, "ax":3.47128, "ay":-7.97433, "alpha":9.84613, "fx":[64.44482,121.33587,85.49969,-35.09849], "fy":[-146.79322,-104.72705,-134.78368,-156.26014]}, + {"t":0.11705, "x":2.84109, "y":1.51025, "heading":2.56593, "vx":0.39685, "vy":-0.92268, "omega":1.23681, "ax":3.56966, "ay":-8.03085, "alpha":9.24671, "fx":[67.34307,121.04314,80.25678,-25.76764], "fy":[-145.44782,-105.00152,-137.94265,-158.01757]}, + {"t":0.14632, "x":2.85424, "y":1.47981, "heading":2.60608, "vx":0.50131, "vy":-1.15769, "omega":1.5074, "ax":3.72794, "ay":-8.07032, "alpha":8.52041, "fx":[70.86684,120.34538,76.15768,-13.72538], "fy":[-143.70954,-105.71291,-140.22121,-159.45165]}, + {"t":0.17558, "x":2.8705, "y":1.44248, "heading":2.65384, "vx":0.6104, "vy":-1.39385, "omega":1.75673, "ax":3.97815, "ay":-8.08337, "alpha":7.52187, "fx":[75.02999,118.65716,74.52435,2.4576], "fy":[-141.49848,-107.47894,-141.07055,-159.93541]}, + {"t":0.20484, "x":2.89007, "y":1.39823, "heading":2.70847, "vx":0.72682, "vy":-1.6304, "omega":1.97685, "ax":4.36148, "ay":-8.05173, "alpha":5.94148, "fx":[79.92676,114.7083,76.66622,25.4486], "fy":[-138.6538,-111.4949,-139.89206,-157.78962]}, + {"t":0.23411, "x":2.9132, "y":1.34707, "heading":2.76886, "vx":0.85445, "vy":-1.86602, "omega":2.15071, "ax":4.91966, "ay":-7.91785, "alpha":3.12733, "fx":[85.94159,105.15499,83.68713,59.94435], "fy":[-134.73337,-120.2455,-135.7816,-147.96113]}, + {"t":0.26337, "x":2.94031, "y":1.28907, "heading":2.83314, "vx":0.99841, "vy":-2.09772, "omega":2.24223, "ax":5.58083, "ay":-7.4806, "alpha":-2.25808, "fx":[95.09311,78.76079,96.08437,109.77532], "fy":[-127.6401,-138.46536,-127.28561,-115.58015]}, + {"t":0.29263, "x":2.97192, "y":1.22448, "heading":2.89778, "vx":1.16173, "vy":-2.31663, "omega":2.17615, "ax":5.9606, "ay":-5.39642, "alpha":-13.05158, "fx":[141.61922,-2.82384,112.86808,153.88877], "fy":[-54.86067,-158.50481,-112.65568,-41.1451]}, + {"t":0.3219, "x":3.00847, "y":1.15438, "heading":2.95588, "vx":1.33615, "vy":-2.47455, "omega":1.79422, "ax":6.71026, "ay":-3.39906, "alpha":-15.97933, "fx":[148.40546,22.1716,127.78778,158.19334], "fy":[37.47789,-156.28294,-95.0829,-17.38042]}, + {"t":0.35116, "x":3.05044, "y":1.08051, "heading":3.00154, "vx":1.53252, "vy":-2.57401, "omega":1.32661, "ax":7.62814, "ay":-1.7688, "alpha":-14.49288, "fx":[136.35825,80.27458,143.55691,158.82026], "fy":[75.1109,-134.59071,-68.46167,7.59454]}, + {"t":0.38042, "x":3.09856, "y":1.00443, "heading":3.03416, "vx":1.75574, "vy":-2.62578, "omega":0.9025, "ax":8.3706, "ay":1.05054, "alpha":-10.57395, "fx":[112.72218,146.97657,156.26869,153.55861], "fy":[109.55338,-50.63947,-28.50407,41.06749]}, + {"t":0.40969, "x":3.15352, "y":0.92804, "heading":3.05604, "vx":2.00069, "vy":-2.59503, "omega":0.59307, "ax":8.17285, "ay":3.89515, "alpha":-5.90347, "fx":[110.02221,145.95043,156.91219,143.18666], "fy":[113.59545,58.30039,24.10181,69.02357]}, + {"t":0.43895, "x":3.21556, "y":0.85377, "heading":3.07087, "vx":2.23986, "vy":-2.48105, "omega":0.42032, "ax":7.39743, "ay":5.59586, "alpha":-3.0725, "fx":[109.7545,124.58812,140.08473,128.88535], "fy":[114.68481,97.9108,74.96764,93.17251]}, + {"t":0.46821, "x":3.28428, "y":0.78356, "heading":3.08185, "vx":2.45633, "vy":-2.3173, "omega":0.33041, "ax":6.48121, "ay":6.73563, "alpha":-1.23172, "fx":[103.49299,108.44619,116.88168,112.15342], "fy":[120.88739,116.36091,108.02312,113.01285]}, + {"t":0.49748, "x":3.35893, "y":0.71863, "heading":3.09099, "vx":2.64599, "vy":-2.12019, "omega":0.29436, "ax":5.65629, "ay":7.47202, "alpha":-0.3085, "fx":[94.47034,95.55881,97.95393,96.86457], "fy":[128.4073,127.58143,125.77017,126.62846]}, + {"t":0.52674, "x":3.43879, "y":0.65979, "heading":3.09947, "vx":2.81152, "vy":-1.90153, "omega":0.28533, "ax":4.97578, "ay":7.95613, "alpha":-0.07088, "fx":[84.23112,84.45467,85.04229,84.81818], "fy":[135.58495,135.44267,135.07714,135.2211]}, + {"t":0.556, "x":3.52319, "y":0.60755, "heading":3.10779, "vx":2.95712, "vy":-1.66871, "omega":0.28326, "ax":4.41065, "ay":8.29246, "alpha":-0.3228, "fx":[73.17586,74.08315,76.88139,75.95548], "fy":[142.03129,141.54864,140.05698,140.57254]}, + {"t":0.58527, "x":3.61161, "y":0.56227, "heading":3.11594, "vx":3.08619, "vy":-1.42604, "omega":0.27381, "ax":4.02557, "ay":8.46003, "alpha":-0.84954, "fx":[63.64662,65.80042,73.37872,71.06962], "fy":[146.17395,145.17312,141.52406,142.73941]}, + {"t":0.60504, "x":3.67343, "y":0.53573, "heading":3.12119, "vx":3.1658, "vy":-1.25875, "omega":0.25701, "ax":3.75619, "ay":8.56496, "alpha":-1.30865, "fx":[56.52319,59.55178,71.46452,68.02768], "fy":[148.86564,147.60026,142.2674,144.01664]}, + {"t":0.62482, "x":3.73677, "y":0.51251, "heading":3.12602, "vx":3.24007, "vy":-1.08939, "omega":0.23114, "ax":3.43583, "ay":8.67427, "alpha":-1.74718, "fx":[48.73434,52.30202,68.55338,64.18051], "fy":[151.31701,149.993,143.37519,145.50244]}, + {"t":0.64459, "x":3.80151, "y":0.49266, "heading":3.13025, "vx":3.30802, "vy":-0.91786, "omega":0.19659, "ax":3.06084, "ay":8.78287, "alpha":-2.07553, "fx":[40.72489,44.31793,64.02827,59.1849], "fy":[153.27443,152.08669,145.00056,147.21486]}, + {"t":0.66436, "x":3.86752, "y":0.47623, "heading":3.13373, "vx":3.36854, "vy":-0.74418, "omega":0.15554, "ax":2.6091, "ay":8.88712, "alpha":-2.23054, "fx":[32.44579,35.53059,57.10439,52.43955], "fy":[154.64044,153.70262,147.1815,149.14474]}, + {"t":0.68414, "x":3.93464, "y":0.46325, "heading":3.13637, "vx":3.42014, "vy":-0.56844, "omega":0.11144, "ax":2.04904, "ay":8.97237, "alpha":-2.12074, "fx":[23.79186,25.91592,46.69018,43.01641], "fy":[155.18892,154.51337,149.67775,151.08959]}, + {"t":0.70391, "x":4.00267, "y":0.45377, "heading":3.13816, "vx":3.46066, "vy":-0.39102, "omega":0.0695, "ax":1.36126, "ay":8.98537, "alpha":-1.63337, "fx":[14.89891,15.89095,31.89921,29.92917], "fy":[154.22009,153.72491,151.31167,152.09775]}, + {"t":0.72369, "x":4.07137, "y":0.44779, "heading":3.13921, "vx":3.48757, "vy":-0.21334, "omega":0.0372, "ax":0.67634, "ay":8.71293, "alpha":-1.10569, "fx":[6.1712,6.43155,17.06272,16.3519], "fy":[148.85455,148.3026,147.52252,148.1384]}, + {"t":0.74346, "x":4.14047, "y":0.44527, "heading":3.13973, "vx":3.50095, "vy":-0.04104, "omega":0.01534, "ax":0.02733, "ay":7.53398, "alpha":-0.36477, "fx":[-1.14337,-1.16276,2.09445,2.07084], "fy":[128.39834,127.91367,127.90311,128.38797]}, + {"t":0.76324, "x":4.2097, "y":0.44594, "heading":3.13997, "vx":3.50149, "vy":0.10794, "omega":0.00812, "ax":-0.23063, "ay":5.21803, "alpha":-0.02672, "fx":[-4.02629,-4.02954,-3.81952,-3.81643], "fy":[88.7929,88.71731,88.72148,88.79706]}, + {"t":0.78301, "x":4.2789, "y":0.44909, "heading":3.14012, "vx":3.49693, "vy":0.21112, "omega":0.0076, "ax":-0.15729, "ay":2.3539, "alpha":-0.00186, "fx":[-2.68197,-2.68205,-2.6691,-2.66902], "fy":[40.04302,40.03528,40.03537,40.0431]}, + {"t":0.80279, "x":4.34802, "y":0.45373, "heading":3.14027, "vx":3.49382, "vy":0.25767, "omega":0.00756, "ax":-0.04537, "ay":0.60002, "alpha":-0.00003, "fx":[-0.77185,-0.77185,-0.77162,-0.77162], "fy":[10.20625,10.2061,10.2061,10.20625]}, + {"t":0.82256, "x":4.4171, "y":0.45894, "heading":3.14042, "vx":3.49292, "vy":0.26953, "omega":0.00756, "ax":0.00735, "ay":-0.09745, "alpha":0.00014, "fx":[0.12547,0.12547,0.12454,0.12453], "fy":[-1.65791,-1.65727,-1.65727,-1.65791]}, + {"t":0.84233, "x":4.48617, "y":0.46425, "heading":3.14057, "vx":3.49307, "vy":0.2676, "omega":0.00756, "ax":0.02486, "ay":-0.33015, "alpha":0.00013, "fx":[0.42334,0.42334,0.4225,0.42249], "fy":[-5.61604,-5.61547,-5.61547,-5.61604]}, + {"t":0.86211, "x":4.55525, "y":0.46948, "heading":3.14072, "vx":3.49356, "vy":0.26108, "omega":0.00756, "ax":0.02832, "ay":-0.38591, "alpha":0.0001, "fx":[0.48203,0.48203,0.48133,0.48133], "fy":[-6.56443,-6.56396,-6.56396,-6.56443]}, + {"t":0.88188, "x":4.62434, "y":0.47456, "heading":3.14087, "vx":3.49412, "vy":0.25344, "omega":0.00756, "ax":0.02701, "ay":-0.37908, "alpha":0.00009, "fx":[0.45966,0.45966,0.45904,0.45904], "fy":[-6.44825,-6.44784,-6.44784,-6.44825]}, + {"t":0.90166, "x":4.69344, "y":0.4795, "heading":3.14102, "vx":3.49465, "vy":0.24595, "omega":0.00757, "ax":0.02451, "ay":-0.35434, "alpha":0.00008, "fx":[0.41717,0.41717,0.41661,0.41661], "fy":[-6.02739,-6.02701,-6.02701,-6.02739]}, + {"t":0.92143, "x":4.76255, "y":0.4843, "heading":3.14117, "vx":3.49514, "vy":0.23894, "omega":0.00757, "ax":0.02216, "ay":-0.32953, "alpha":0.00008, "fx":[0.37711,0.37711,0.37659,0.37659], "fy":[-5.60541,-5.60506,-5.60506,-5.60541]}, + {"t":0.94121, "x":4.83166, "y":0.48896, "heading":3.14132, "vx":3.49557, "vy":0.23243, "omega":0.00757, "ax":0.02052, "ay":-0.31364, "alpha":0.00006, "fx":[0.34924,0.34924,0.34881,0.34881], "fy":[-5.33503,-5.33474,-5.33474,-5.33503]}, + {"t":0.96098, "x":4.90079, "y":0.49349, "heading":3.14147, "vx":3.49598, "vy":0.22622, "omega":0.00757, "ax":0.02063, "ay":-0.31598, "alpha":-0.00202, "fx":[0.3442,0.34419,0.35774,0.35774], "fy":[-5.37009,-5.37922,-5.37922,-5.37009]}, + {"t":0.98076, "x":4.96993, "y":0.4979, "heading":-3.14157, "vx":3.49639, "vy":0.21998, "omega":0.00753, "ax":0.06871, "ay":-0.35137, "alpha":-0.8832, "fx":[-1.79584,-1.79241,4.12793,4.1355], "fy":[-3.97972,-7.97711,-7.9727,-3.97744]}, + {"t":1.00053, "x":5.03908, "y":0.50218, "heading":3.14159, "vx":3.49775, "vy":0.21303, "omega":-0.00993, "ax":-0.80954, "ay":-0.47018, "alpha":-2.58833, "fx":[-22.49859,-22.33972,-5.1017,-5.14005], "fy":[-2.07157,-13.77604,-14.03196,-2.1109]}, + {"t":1.03284, "x":5.15166, "y":0.50882, "heading":3.13992, "vx":3.47159, "vy":0.19784, "omega":-0.09356, "ax":-0.03901, "ay":-0.20294, "alpha":-0.14452, "fx":[-1.14769,-1.14858,-0.17953,-0.17847], "fy":[-3.12368,-3.7784,-3.78021,-3.12546]}, + {"t":1.06515, "x":5.2638, "y":0.51511, "heading":3.13682, "vx":3.47033, "vy":0.19128, "omega":-0.09823, "ax":0.00348, "ay":-0.13494, "alpha":-0.00866, "fx":[0.0302,0.03001,0.08804,0.08823], "fy":[-2.27548,-2.3147,-2.31498,-2.27575]}, + {"t":1.09745, "x":5.37592, "y":0.52122, "heading":3.13364, "vx":3.47044, "vy":0.18692, "omega":-0.09851, "ax":0.0057, "ay":-0.11451, "alpha":0.0017, "fx":[0.10265,0.10271,0.09134,0.09128], "fy":[-1.9517,-1.94402,-1.94393,-1.95161]}, + {"t":1.12976, "x":5.48804, "y":0.52719, "heading":3.13046, "vx":3.47063, "vy":0.18322, "omega":-0.09845, "ax":0.00569, "ay":-0.10503, "alpha":0.00356, "fx":[0.10855,0.10873,0.08487,0.08469], "fy":[-1.79475,-1.77862,-1.77835,-1.79448]}, + {"t":1.16207, "x":5.60018, "y":0.53306, "heading":3.12728, "vx":3.47081, "vy":0.17983, "omega":-0.09834, "ax":0.00555, "ay":-0.09853, "alpha":0.00453, "fx":[0.10949,0.10979,0.07945,0.07915], "fy":[-1.68636,-1.66584,-1.66541,-1.68592]}, + {"t":1.19438, "x":5.71231, "y":0.53882, "heading":3.12411, "vx":3.47099, "vy":0.17664, "omega":-0.09819, "ax":0.00543, "ay":-0.0932, "alpha":0.00527, "fx":[0.10978,0.1102,0.07489,0.07447], "fy":[-1.59752,-1.57365,-1.57303,-1.5969]}, + {"t":1.22668, "x":5.82445, "y":0.54448, "heading":3.12094, "vx":3.47117, "vy":0.17363, "omega":-0.09802, "ax":0.00531, "ay":-0.08858, "alpha":0.00589, "fx":[0.10983,0.11038,0.07093,0.07038], "fy":[-1.52039,-1.49371,-1.4929,-1.51957]}, + {"t":1.25899, "x":5.9366, "y":0.55004, "heading":3.11778, "vx":3.47134, "vy":0.17077, "omega":-0.09783, "ax":0.00521, "ay":-0.08448, "alpha":0.00641, "fx":[0.10969,0.11038,0.06743,0.06674], "fy":[-1.452,-1.42296,-1.42194,-1.45097]}, + {"t":1.2913, "x":6.04876, "y":0.55551, "heading":3.11462, "vx":3.47151, "vy":0.16804, "omega":-0.09762, "ax":0.00511, "ay":-0.08081, "alpha":0.00685, "fx":[0.10938,0.11022,0.0643,0.06347], "fy":[-1.39072,-1.35968,-1.35844,-1.38948]}, + {"t":1.32361, "x":6.16092, "y":0.5609, "heading":3.11147, "vx":3.47167, "vy":0.16543, "omega":-0.0974, "ax":0.00501, "ay":-0.0775, "alpha":0.00723, "fx":[0.10892,0.1099,0.06148,0.0605], "fy":[-1.33542,-1.30268,-1.30123,-1.33396]}, + {"t":1.35591, "x":6.27308, "y":0.5662, "heading":3.10833, "vx":3.47183, "vy":0.16293, "omega":-0.09717, "ax":0.00492, "ay":-0.0745, "alpha":0.00754, "fx":[0.1083,0.10944,0.05892,0.05778], "fy":[-1.28522,-1.25107,-1.24939,-1.28354]}, + {"t":1.38822, "x":6.38525, "y":0.57143, "heading":3.10519, "vx":3.47199, "vy":0.16052, "omega":-0.09692, "ax":0.00482, "ay":-0.07178, "alpha":0.0078, "fx":[0.10756,0.10885,0.05658,0.05529], "fy":[-1.2395,-1.20416,-1.20226,-1.23759]}, + {"t":1.42053, "x":6.49742, "y":0.57658, "heading":3.10206, "vx":3.47215, "vy":0.1582, "omega":-0.09667, "ax":0.00474, "ay":-0.0693, "alpha":0.00801, "fx":[0.10668,0.10811,0.05444,0.053], "fy":[-1.19798,-1.16169,-1.15957,-1.19586]}, + {"t":1.45284, "x":6.6096, "y":0.58165, "heading":3.09894, "vx":3.4723, "vy":0.15596, "omega":-0.09641, "ax":0.00465, "ay":-0.06714, "alpha":0.00816, "fx":[0.10558,0.10716,0.05249,0.05092], "fy":[-1.16174,-1.12478,-1.12244,-1.15941]}, + {"t":1.48515, "x":6.72179, "y":0.58666, "heading":3.09583, "vx":3.47245, "vy":0.15379, "omega":-0.09615, "ax":0.00455, "ay":-0.06573, "alpha":0.00817, "fx":[0.10387,0.10556,0.05085,0.04916], "fy":[-1.13775,-1.10076,-1.09825,-1.13524]}, + {"t":1.51745, "x":6.83398, "y":0.59159, "heading":3.09273, "vx":3.4726, "vy":0.15167, "omega":-0.09589, "ax":0.00438, "ay":-0.06718, "alpha":0.00759, "fx":[0.09908,0.10076,0.04996,0.04828], "fy":[-1.16105,-1.1267,-1.12421,-1.15856]}, + {"t":1.54976, "x":6.94617, "y":0.59646, "heading":3.08964, "vx":3.47274, "vy":0.1495, "omega":-0.09564, "ax":0.00357, "ay":-0.08177, "alpha":0.00344, "fx":[0.07187,0.07268,0.04962,0.04881], "fy":[-1.39936,-1.38377,-1.38257,-1.39816]}, + {"t":1.58207, "x":7.05837, "y":0.60124, "heading":3.08655, "vx":3.47285, "vy":0.14686, "omega":-0.09553, "ax":-0.00639, "ay":-0.15877, "alpha":-0.03454, "fx":[-0.22001,-0.22862,0.00265,0.01127], "fy":[-2.61619,-2.77249,-2.78523,-2.62893]}, + {"t":1.61438, "x":7.17057, "y":0.6059, "heading":3.08345, "vx":3.47265, "vy":0.14173, "omega":-0.09664, "ax":-0.17613, "ay":-0.52746, "alpha":-0.55804, "fx":[-4.79727,-4.93587,-1.19789,-1.0525], "fy":[-7.60248,-10.11706,-10.34209,-7.82614]}, + {"t":1.64668, "x":7.28267, "y":0.61021, "heading":3.08003, "vx":3.46696, "vy":0.12469, "omega":-0.11467, "ax":-2.33829, "ay":-1.83832, "alpha":-6.76929, "fx":[-63.17925,-61.01924,-17.7248,-17.17115], "fy":[-12.8127,-42.63151,-51.26073,-18.37216]}, + {"t":1.67899, "x":7.39346, "y":0.61328, "heading":3.07279, "vx":3.39141, "vy":0.0653, "omega":-0.33337, "ax":-5.19123, "ay":-2.15724, "alpha":-14.58304, "fx":[-132.25949,-123.08403,-39.87773,-57.98377], "fy":[10.84315,-57.55366,-108.79478,8.72908]}, + {"t":1.7113, "x":7.50032, "y":0.61426, "heading":3.05441, "vx":3.2237, "vy":-0.0044, "omega":-0.80452, "ax":-6.13804, "ay":-1.37791, "alpha":-16.49109, "fx":[-144.38629,-138.41702,-52.97603,-81.84604], "fy":[25.69926,-52.15163,-123.34595,56.04694]}, + {"t":1.74361, "x":7.60126, "y":0.6134, "heading":3.01981, "vx":3.02539, "vy":-0.04892, "omega":-1.33731, "ax":-6.67075, "ay":-0.50052, "alpha":-16.88545, "fx":[-146.58424,-146.83657,-71.90061,-88.5488], "fy":[39.38595,-40.61101,-122.00702,89.17739]}, + {"t":1.77591, "x":7.69552, "y":0.61156, "heading":2.9678, "vx":2.80987, "vy":-0.06509, "omega":-1.88284, "ax":-6.9848, "ay":0.31621, "alpha":-16.64077, "fx":[-144.97192,-152.30782,-91.82947,-86.12846], "fy":[52.98732,-26.6576,-113.25976,108.44476]}, + {"t":1.80822, "x":7.78266, "y":0.60962, "heading":2.89828, "vx":2.58421, "vy":-0.05487, "omega":-2.42046, "ax":-7.13315, "ay":1.03412, "alpha":-16.27181, "fx":[-141.32036,-155.47632,-107.82691,-80.70753], "fy":[65.67862,-12.73657,-102.60714,120.02517]}, + {"t":1.84053, "x":7.86243, "y":0.60839, "heading":2.81159, "vx":2.35376, "vy":-0.02146, "omega":-2.94617, "ax":-7.13977, "ay":1.58703, "alpha":-16.16542, "fx":[-136.60962,-156.90117,-117.36686,-74.90393], "fy":[76.87987,-0.43973,-95.45927,126.99885]}, + {"t":1.87284, "x":7.93474, "y":0.60852, "heading":2.70797, "vx":2.12309, "vy":0.02981, "omega":-3.46843, "ax":-7.02008, "ay":1.91406, "alpha":-16.5942, "fx":[-131.48028,-157.26491,-119.60894,-69.28411], "fy":[86.42199,9.09732,-95.90387,130.61491]}, + {"t":1.90515, "x":7.99967, "y":0.61048, "heading":2.58725, "vx":1.89628, "vy":0.09165, "omega":-4.00455, "ax":-6.76815, "ay":1.96756, "alpha":-17.66418, "fx":[-126.32035,-157.27025,-114.07833,-62.82846], "fy":[94.39944,14.93732,-105.00024,129.53429]}, + {"t":1.93745, "x":8.05741, "y":0.61447, "heading":2.44866, "vx":1.67762, "vy":0.15522, "omega":-4.57524, "ax":-6.4968, "ay":0.36586, "alpha":-19.22717, "fx":[-121.90794,-157.48252,-102.81655,-59.82775], "fy":[100.30394,16.21653,-118.08849,26.46043]}, + {"t":1.96976, "x":8.10821, "y":0.61968, "heading":2.29081, "vx":1.46772, "vy":0.16704, "omega":-5.19643, "ax":-9.2445, "ay":-0.52634, "alpha":-2.18848, "fx":[-157.81707,-158.12126,-156.00148,-157.04539], "fy":[7.19502,-4.20375,-24.70076,-14.10212]}, + {"t":2.00207, "x":8.15081, "y":0.6248, "heading":2.12178, "vx":1.16906, "vy":0.15003, "omega":-5.26713, "ax":-7.70392, "ay":-1.38077, "alpha":17.93536, "fx":[-127.36573,-108.84561,-130.01343,-157.94088], "fy":[-96.15776,-114.54579,92.40296,24.35466]}, + {"t":2.03438, "x":8.18456, "y":0.62893, "heading":1.96097, "vx":0.92016, "vy":0.10542, "omega":-4.68768, "ax":-6.28564, "ay":-1.34986, "alpha":25.99551, "fx":[-122.52115,-46.16181,-104.65685,-154.32826], "fy":[-102.93396,-152.40141,120.86369,42.62905]}, + {"t":2.06668, "x":8.21101, "y":0.63163, "heading":1.82309, "vx":0.71709, "vy":0.06181, "omega":-3.84783, "ax":-5.81184, "ay":-0.92669, "alpha":28.53817, "fx":[-125.74469,-34.66128,-85.16072,-149.86455], "fy":[-99.24321,-155.97985,135.48829,56.68374]}, + {"t":2.09899, "x":8.23114, "y":0.63314, "heading":1.71367, "vx":0.52932, "vy":0.03187, "omega":-2.92583, "ax":-5.59452, "ay":-0.56099, "alpha":29.63479, "fx":[-129.79133,-36.16193,-69.2592,-145.43261], "fy":[-94.03655,-155.88646,144.3577,67.39637]}, + {"t":2.1313, "x":8.24532, "y":0.63388, "heading":1.63461, "vx":0.34857, "vy":0.01375, "omega":-1.96839, "ax":-5.45046, "ay":-0.2938, "alpha":30.25553, "fx":[-132.95547,-39.44398,-56.76628,-141.67754], "fy":[-89.59901,-155.21873,149.77167,75.05628]}, + {"t":2.16361, "x":8.25374, "y":0.63417, "heading":1.5868, "vx":0.17248, "vy":0.00426, "omega":-0.99091, "ax":-5.33867, "ay":-0.13178, "alpha":30.67097, "fx":[-134.80531,-41.3326,-48.03078,-139.06852], "fy":[-86.85281,-154.80267,152.84428,79.8449]}, + {"t":2.19591, "x":8.25652, "y":0.63424, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0,0.0,0.0,0.0]}], "splits":[0] }, "events":[] diff --git a/src/main/deploy/v1_doomspiral/choreo/RIGHT_TRENCH_ANTI_BUCKS.traj b/src/main/deploy/v1_doomspiral/choreo/RIGHT_TRENCH_ANTI_BUCKS.traj index 2416cddd..23cd0463 100644 --- a/src/main/deploy/v1_doomspiral/choreo/RIGHT_TRENCH_ANTI_BUCKS.traj +++ b/src/main/deploy/v1_doomspiral/choreo/RIGHT_TRENCH_ANTI_BUCKS.traj @@ -4,14 +4,14 @@ "snapshot":{ "waypoints":[ {"x":4.428042411804199, "y":0.3954239785671234, "heading":1.5707963267948966, "intervals":27, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, - {"x":7.831703662872315, "y":0.4928959503, "heading":0.0, "intervals":14, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":8.526999855041504, "y":0.9033589959144592, "heading":1.5707963267948966, "intervals":32, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, + {"x":7.831703662872315, "y":0.4928959503, "heading":0.0, "intervals":21, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, + {"x":8.526999855041504, "y":0.9033589959144592, "heading":1.5707963267948966, "intervals":52, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, {"x":8.526999855041504, "y":3.0137579441070557, "heading":-1.5707963267948966, "intervals":17, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, {"x":7.337273120880127, "y":2.0415642204, "heading":0.0, "intervals":29, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, {"x":5.503650665283203, "y":0.3939896523952484, "heading":1.5707963267948966, "intervals":19, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, {"x":4.069419860839844, "y":0.38825881481170654, "heading":0.0, "intervals":15, "split":false, "fixTranslation":false, "fixHeading":false, "overrideIntervals":false}, - {"x":2.9093637466430664, "y":0.3876216113567352, "heading":-1.5707963267948966, "intervals":23, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":2.8122479915618896, "y":1.560916781425476, "heading":2.49, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], + {"x":2.9093637466430664, "y":0.3876216113567352, "heading":-1.5707963267948966, "intervals":20, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, + {"x":3.0215272903442383, "y":0.9590602874755856, "heading":2.7813916025632035, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], "constraints":[ {"from":"first", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, {"from":"last", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, @@ -19,7 +19,7 @@ {"from":2, "to":3, "data":{"type":"KeepInLane", "props":{"tolerance":0.00001}}, "enabled":true}, {"from":2, "to":3, "data":{"type":"MaxAngularVelocity", "props":{"max":0.0}}, "enabled":true}, {"from":5, "to":7, "data":{"type":"MaxAngularVelocity", "props":{"max":0.0}}, "enabled":true}, - {"from":1, "to":3, "data":{"type":"MaxVelocity", "props":{"max":1.5}}, "enabled":true}, + {"from":1, "to":3, "data":{"type":"MaxVelocity", "props":{"max":0.85}}, "enabled":true}, {"from":5, "to":7, "data":{"type":"KeepInLane", "props":{"tolerance":0.01}}, "enabled":true}, {"from":4, "to":6, "data":{"type":"MaxVelocity", "props":{"max":2.0}}, "enabled":true}], "targetDt":0.05 @@ -27,14 +27,14 @@ "params":{ "waypoints":[ {"x":{"exp":"4.428042411804199 m", "val":4.428042411804199}, "y":{"exp":"0.3954239785671234 m", "val":0.3954239785671234}, "heading":{"exp":"90 deg", "val":1.5707963267948966}, "intervals":27, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, - {"x":{"exp":"7.8317036628723145 m", "val":7.831703662872315}, "y":{"exp":"0.4928959503 m", "val":0.4928959503}, "heading":{"exp":"0 deg", "val":0.0}, "intervals":14, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":{"exp":"8.526999855041504 m", "val":8.526999855041504}, "y":{"exp":"0.9033589959144592 m", "val":0.9033589959144592}, "heading":{"exp":"1.5707963267948966 rad", "val":1.5707963267948966}, "intervals":32, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, + {"x":{"exp":"7.8317036628723145 m", "val":7.831703662872315}, "y":{"exp":"0.4928959503 m", "val":0.4928959503}, "heading":{"exp":"0 deg", "val":0.0}, "intervals":21, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, + {"x":{"exp":"8.526999855041504 m", "val":8.526999855041504}, "y":{"exp":"0.9033589959144592 m", "val":0.9033589959144592}, "heading":{"exp":"1.5707963267948966 rad", "val":1.5707963267948966}, "intervals":52, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, {"x":{"exp":"8.526999855041504 m", "val":8.526999855041504}, "y":{"exp":"3.0137579441070557 m", "val":3.0137579441070557}, "heading":{"exp":"-1.5707963267948966 rad", "val":-1.5707963267948966}, "intervals":17, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, {"x":{"exp":"7.337273120880127 m", "val":7.337273120880127}, "y":{"exp":"2.0415642204 m", "val":2.0415642204}, "heading":{"exp":"0 deg", "val":0.0}, "intervals":29, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, {"x":{"exp":"5.503650665283203 m", "val":5.503650665283203}, "y":{"exp":"0.3939896523952484 m", "val":0.3939896523952484}, "heading":{"exp":"90 deg", "val":1.5707963267948966}, "intervals":19, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, {"x":{"exp":"4.069419860839844 m", "val":4.069419860839844}, "y":{"exp":"0.38825881481170654 m", "val":0.38825881481170654}, "heading":{"exp":"0 deg", "val":0.0}, "intervals":15, "split":false, "fixTranslation":false, "fixHeading":false, "overrideIntervals":false}, - {"x":{"exp":"2.9093637466430664 m", "val":2.9093637466430664}, "y":{"exp":"0.38762161135673523 m", "val":0.3876216113567352}, "heading":{"exp":"-90 deg", "val":-1.5707963267948966}, "intervals":23, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":{"exp":"2.8122479915618896 m", "val":2.8122479915618896}, "y":{"exp":"1.560916781425476 m", "val":1.560916781425476}, "heading":{"exp":"2.49 rad", "val":2.49}, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], + {"x":{"exp":"2.9093637466430664 m", "val":2.9093637466430664}, "y":{"exp":"0.38762161135673523 m", "val":0.3876216113567352}, "heading":{"exp":"-90 deg", "val":-1.5707963267948966}, "intervals":20, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, + {"x":{"exp":"3.0215272903442383 m", "val":3.0215272903442383}, "y":{"exp":"(8.1 - 7.140939712524414) m", "val":0.9590602874755856}, "heading":{"exp":"(180 - 20.638) deg", "val":2.7813916025632035}, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], "constraints":[ {"from":"first", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, {"from":"last", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, @@ -42,7 +42,7 @@ {"from":2, "to":3, "data":{"type":"KeepInLane", "props":{"tolerance":{"exp":"1e-5 m", "val":0.00001}}}, "enabled":true}, {"from":2, "to":3, "data":{"type":"MaxAngularVelocity", "props":{"max":{"exp":"0 rad / s", "val":0.0}}}, "enabled":true}, {"from":5, "to":7, "data":{"type":"MaxAngularVelocity", "props":{"max":{"exp":"0 rad / s", "val":0.0}}}, "enabled":true}, - {"from":1, "to":3, "data":{"type":"MaxVelocity", "props":{"max":{"exp":"1.5 m / s", "val":1.5}}}, "enabled":true}, + {"from":1, "to":3, "data":{"type":"MaxVelocity", "props":{"max":{"exp":"INTAKE_VELOCITY", "val":0.55}}}, "enabled":true}, {"from":5, "to":7, "data":{"type":"KeepInLane", "props":{"tolerance":{"exp":"0.01 m", "val":0.01}}}, "enabled":true}, {"from":4, "to":6, "data":{"type":"MaxVelocity", "props":{"max":{"exp":"2 m / s", "val":2.0}}}, "enabled":true}], "targetDt":{ @@ -75,185 +75,209 @@ "differentialTrackWidth":0.5588 }, "sampleType":"Swerve", - "waypoints":[0.0,1.21913,1.79417,3.24986,3.93285,5.17473,5.17519,6.00873,6.73364], + "waypoints":[0.0,1.26435,2.23388,4.7303,5.41641,6.65801,6.65846,7.52898,8.09436], "samples":[ - {"t":0.0, "x":4.42804, "y":0.39542, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":9.42361, "ay":0.31123, "alpha":0.0, "fx":[160.29299,160.29299,160.29299,160.29299], "fy":[5.29386,5.29386,5.29386,5.29386]}, - {"t":0.04515, "x":4.43765, "y":0.39574, "heading":1.5708, "vx":0.4255, "vy":0.01405, "omega":0.0, "ax":9.42261, "ay":0.30381, "alpha":0.0, "fx":[160.27597,160.27597,160.27597,160.27597], "fy":[5.16769,5.16769,5.16769,5.16769]}, - {"t":0.09031, "x":4.46647, "y":0.39669, "heading":1.5708, "vx":0.85096, "vy":0.02777, "omega":0.0, "ax":9.42119, "ay":0.29652, "alpha":0.0, "fx":[160.25172,160.25172,160.25172,160.25172], "fy":[5.04372,5.04372,5.04372,5.04372]}, - {"t":0.13546, "x":4.51449, "y":0.39824, "heading":1.5708, "vx":1.27636, "vy":0.04116, "omega":0.0, "ax":9.41909, "ay":0.28937, "alpha":0.0, "fx":[160.21596,160.21596,160.21596,160.21596], "fy":[4.92206,4.92206,4.92206,4.92206]}, - {"t":0.18061, "x":4.58173, "y":0.4004, "heading":1.5708, "vx":1.70166, "vy":0.05423, "omega":0.0, "ax":9.41581, "ay":0.28233, "alpha":0.0, "fx":[160.16018,160.16018,160.16018,160.16018], "fy":[4.80227,4.80227,4.80227,4.80227]}, - {"t":0.22576, "x":4.66816, "y":0.40313, "heading":1.5708, "vx":2.12681, "vy":0.06697, "omega":0.0, "ax":9.41018, "ay":0.2753, "alpha":0.0, "fx":[160.06443,160.06443,160.06443,160.06443], "fy":[4.68277,4.68277,4.68277,4.68277]}, - {"t":0.27092, "x":4.77379, "y":0.40644, "heading":1.5708, "vx":2.55171, "vy":0.0794, "omega":0.0, "ax":9.39867, "ay":0.26798, "alpha":0.0, "fx":[159.86861,159.86861,159.86861,159.86861], "fy":[4.55821,4.55821,4.55821,4.55821]}, - {"t":0.31607, "x":4.89858, "y":0.41029, "heading":1.5708, "vx":2.97608, "vy":0.0915, "omega":0.0, "ax":9.36346, "ay":0.25877, "alpha":0.0, "fx":[159.26986,159.26986,159.26986,159.26986], "fy":[4.4016,4.4016,4.4016,4.4016]}, - {"t":0.36122, "x":5.04251, "y":0.41469, "heading":1.5708, "vx":3.39887, "vy":0.10319, "omega":0.0, "ax":2.38977, "ay":-0.1761, "alpha":0.0, "fx":[40.6493,40.6493,40.6493,40.6493], "fy":[-2.99547,-2.99547,-2.99547,-2.99547]}, - {"t":0.40638, "x":5.19841, "y":0.41917, "heading":1.5708, "vx":3.50678, "vy":0.09524, "omega":0.0, "ax":0.00302, "ay":-0.1038, "alpha":0.0, "fx":[0.05136,0.05136,0.05136,0.05136], "fy":[-1.76565,-1.76565,-1.76565,-1.76565]}, - {"t":0.45153, "x":5.35676, "y":0.42336, "heading":1.5708, "vx":3.50691, "vy":0.09055, "omega":0.0, "ax":0.00215, "ay":-0.08505, "alpha":0.0, "fx":[0.03651,0.03651,0.03651,0.03651], "fy":[-1.44675,-1.44675,-1.44675,-1.44675]}, - {"t":0.49668, "x":5.51511, "y":0.42737, "heading":1.5708, "vx":3.50701, "vy":0.08671, "omega":0.0, "ax":0.00193, "ay":-0.08001, "alpha":0.0, "fx":[0.0329,0.0329,0.0329,0.0329], "fy":[-1.36095,-1.36095,-1.36095,-1.36095]}, - {"t":0.54184, "x":5.67346, "y":0.4312, "heading":1.5708, "vx":3.5071, "vy":0.0831, "omega":0.0, "ax":0.00178, "ay":-0.07672, "alpha":0.0, "fx":[0.03023,0.03023,0.03023,0.03023], "fy":[-1.30493,-1.30493,-1.30493,-1.30493]}, - {"t":0.58699, "x":5.83182, "y":0.43487, "heading":1.5708, "vx":3.50718, "vy":0.07963, "omega":0.0, "ax":0.00164, "ay":-0.07393, "alpha":0.0, "fx":[0.02791,0.02791,0.02791,0.02791], "fy":[-1.25756,-1.25756,-1.25756,-1.25756]}, - {"t":0.63214, "x":5.99018, "y":0.43839, "heading":1.5708, "vx":3.50725, "vy":0.07629, "omega":0.0, "ax":0.00152, "ay":-0.07148, "alpha":0.0, "fx":[0.02585,0.02585,0.02585,0.02585], "fy":[-1.2158,-1.2158,-1.2158,-1.2158]}, - {"t":0.67729, "x":6.14854, "y":0.44177, "heading":1.5708, "vx":3.50732, "vy":0.07307, "omega":0.0, "ax":0.00141, "ay":-0.06929, "alpha":0.0, "fx":[0.02399,0.02399,0.02399,0.02399], "fy":[-1.17863,-1.17863,-1.17863,-1.17863]}, - {"t":0.72245, "x":6.30691, "y":0.44499, "heading":1.5708, "vx":3.50738, "vy":0.06994, "omega":0.0, "ax":0.00131, "ay":-0.06733, "alpha":0.0, "fx":[0.02231,0.02231,0.02231,0.02231], "fy":[-1.14531,-1.14531,-1.14531,-1.14531]}, - {"t":0.7676, "x":6.46528, "y":0.44808, "heading":1.5708, "vx":3.50744, "vy":0.0669, "omega":0.0, "ax":0.00122, "ay":-0.06554, "alpha":0.0, "fx":[0.02076,0.02076,0.02076,0.02076], "fy":[-1.1148,-1.1148,-1.1148,-1.1148]}, - {"t":0.81275, "x":6.62365, "y":0.45104, "heading":1.5708, "vx":3.5075, "vy":0.06394, "omega":0.0, "ax":0.00113, "ay":-0.06359, "alpha":0.0, "fx":[0.01925,0.01925,0.01925,0.01925], "fy":[-1.08158,-1.08158,-1.08158,-1.08158]}, - {"t":0.85791, "x":6.78203, "y":0.45386, "heading":1.5708, "vx":3.50755, "vy":0.06107, "omega":0.0, "ax":0.00099, "ay":-0.05841, "alpha":0.0, "fx":[0.0169,0.0169,0.0169,0.0169], "fy":[-0.9936,-0.9936,-0.9936,-0.9936]}, - {"t":0.90306, "x":6.94041, "y":0.45656, "heading":1.5708, "vx":3.5076, "vy":0.05843, "omega":0.0, "ax":0.00031, "ay":-0.01897, "alpha":0.0, "fx":[0.00531,0.00531,0.00531,0.00531], "fy":[-0.32273,-0.32273,-0.32273,-0.32273]}, - {"t":0.94821, "x":7.09879, "y":0.45918, "heading":1.5708, "vx":3.50761, "vy":0.05757, "omega":0.0, "ax":-0.00879, "ay":0.37089, "alpha":0.0, "fx":[-0.14945,-0.14945,-0.14945,-0.14945], "fy":[6.30869,6.30869,6.30869,6.30869]}, - {"t":0.99337, "x":7.25716, "y":0.46215, "heading":1.5708, "vx":3.50721, "vy":0.07432, "omega":0.0, "ax":-6.98474, "ay":1.67874, "alpha":0.0, "fx":[-118.80843,-118.80843,-118.80843,-118.80843], "fy":[28.55491,28.55491,28.55491,28.55491]}, - {"t":1.03852, "x":7.4084, "y":0.46722, "heading":1.5708, "vx":3.19183, "vy":0.15012, "omega":0.0, "ax":-9.3695, "ay":-0.07065, "alpha":0.0, "fx":[-159.37252,-159.37252,-159.37252,-159.37252], "fy":[-1.20171,-1.20171,-1.20171,-1.20171]}, - {"t":1.08367, "x":7.54297, "y":0.47393, "heading":1.5708, "vx":2.76877, "vy":0.14693, "omega":0.0, "ax":-9.40267, "ay":-0.09828, "alpha":0.0, "fx":[-159.93681,-159.93681,-159.93681,-159.93681], "fy":[-1.67179,-1.67179,-1.67179,-1.67179]}, - {"t":1.12882, "x":7.6584, "y":0.48046, "heading":1.5708, "vx":2.34421, "vy":0.14249, "omega":0.0, "ax":-9.41392, "ay":-0.10567, "alpha":0.0, "fx":[-160.12814,-160.12814,-160.12814,-160.12814], "fy":[-1.79746,-1.79746,-1.79746,-1.79746]}, - {"t":1.17398, "x":7.75465, "y":0.48679, "heading":1.5708, "vx":1.91914, "vy":0.13772, "omega":0.0, "ax":-9.41959, "ay":-0.1079, "alpha":0.0, "fx":[-160.22461,-160.22461,-160.22461,-160.22461], "fy":[-1.83528,-1.83528,-1.83528,-1.83528]}, - {"t":1.21913, "x":7.8317, "y":0.4929, "heading":1.5708, "vx":1.49382, "vy":0.13285, "omega":0.0, "ax":-1.71012, "ay":8.24509, "alpha":0.0, "fx":[-29.08863,-29.08863,-29.08863,-29.08863], "fy":[140.2466,140.2466,140.2466,140.2466]}, - {"t":1.2602, "x":7.89162, "y":0.50531, "heading":1.5708, "vx":1.42358, "vy":0.47151, "omega":0.0, "ax":-0.36851, "ay":1.05804, "alpha":0.0, "fx":[-6.26821,-6.26821,-6.26821,-6.26821], "fy":[17.99703,17.99703,17.99703,17.99703]}, - {"t":1.30128, "x":7.94978, "y":0.52557, "heading":1.5708, "vx":1.40844, "vy":0.51497, "omega":0.0, "ax":-0.00832, "ay":0.02273, "alpha":0.0, "fx":[-0.14154,-0.14154,-0.14154,-0.14154], "fy":[0.38669,0.38669,0.38669,0.38669]}, - {"t":1.34235, "x":8.00762, "y":0.54674, "heading":1.5708, "vx":1.4081, "vy":0.5159, "omega":0.0, "ax":0.00199, "ay":-0.00543, "alpha":0.0, "fx":[0.03385,0.03385,0.03385,0.03385], "fy":[-0.09244,-0.09244,-0.09244,-0.09244]}, - {"t":1.38343, "x":8.06546, "y":0.56792, "heading":1.5708, "vx":1.40818, "vy":0.51568, "omega":0.0, "ax":0.0021, "ay":-0.00574, "alpha":0.0, "fx":[0.03573,0.03573,0.03573,0.03573], "fy":[-0.09763,-0.09763,-0.09763,-0.09763]}, - {"t":1.4245, "x":8.1233, "y":0.5891, "heading":1.5708, "vx":1.40827, "vy":0.51544, "omega":0.0, "ax":0.00196, "ay":-0.00535, "alpha":0.0, "fx":[0.03327,0.03327,0.03327,0.03327], "fy":[-0.09093,-0.09093,-0.09093,-0.09093]}, - {"t":1.46558, "x":8.18115, "y":0.61027, "heading":1.5708, "vx":1.40835, "vy":0.51522, "omega":0.0, "ax":0.00174, "ay":-0.00477, "alpha":0.0, "fx":[0.02968,0.02968,0.02968,0.02968], "fy":[-0.08117,-0.08117,-0.08117,-0.08117]}, - {"t":1.50665, "x":8.239, "y":0.63143, "heading":1.5708, "vx":1.40842, "vy":0.51503, "omega":0.0, "ax":-0.00135, "ay":0.00368, "alpha":0.0, "fx":[-0.0229,-0.0229,-0.0229,-0.0229], "fy":[0.0626,0.0626,0.0626,0.0626]}, - {"t":1.54772, "x":8.29685, "y":0.65258, "heading":1.5708, "vx":1.40837, "vy":0.51518, "omega":0.0, "ax":-0.11092, "ay":0.29913, "alpha":0.0, "fx":[-1.88664,-1.88664,-1.88664,-1.88664], "fy":[5.08817,5.08817,5.08817,5.08817]}, - {"t":1.5888, "x":8.3546, "y":0.674, "heading":1.5708, "vx":1.40381, "vy":0.52746, "omega":0.0, "ax":-2.76298, "ay":5.76232, "alpha":0.0, "fx":[-46.99743,-46.99743,-46.99743,-46.99743], "fy":[98.01541,98.01541,98.01541,98.01541]}, - {"t":1.62987, "x":8.40993, "y":0.70052, "heading":1.5708, "vx":1.29032, "vy":0.76415, "omega":0.0, "ax":-5.68981, "ay":7.30132, "alpha":0.0, "fx":[-96.78204,-96.78204,-96.78204,-96.78204], "fy":[124.19341,124.19341,124.19341,124.19341]}, - {"t":1.67095, "x":8.45813, "y":0.73807, "heading":1.5708, "vx":1.05662, "vy":1.06404, "omega":0.0, "ax":-7.45397, "ay":5.69505, "alpha":0.0, "fx":[-126.78984,-126.78984,-126.78984,-126.78984], "fy":[96.87118,96.87118,96.87118,96.87118]}, - {"t":1.71202, "x":8.49524, "y":0.78658, "heading":1.5708, "vx":0.75045, "vy":1.29796, "omega":0.0, "ax":-8.70096, "ay":3.58219, "alpha":0.0, "fx":[-148.00081,-148.00081,-148.00081,-148.00081], "fy":[60.93206,60.93206,60.93206,60.93206]}, - {"t":1.75309, "x":8.51873, "y":0.84291, "heading":1.5708, "vx":0.39307, "vy":1.4451, "omega":0.0, "ax":-9.33095, "ay":1.29558, "alpha":0.0, "fx":[-158.71681,-158.71681,-158.71681,-158.71681], "fy":[22.03747,22.03747,22.03747,22.03747]}, - {"t":1.79417, "x":8.527, "y":0.90336, "heading":1.5708, "vx":0.00981, "vy":1.49831, "omega":0.0, "ax":-0.42304, "ay":0.02925, "alpha":0.0, "fx":[-7.19587,-7.19587,-7.19587,-7.19587], "fy":[0.49757,0.49757,0.49757,0.49757]}, - {"t":1.83966, "x":8.52701, "y":0.97155, "heading":1.5708, "vx":-0.00944, "vy":1.49964, "omega":0.0, "ax":0.39841, "ay":0.0001, "alpha":0.0, "fx":[6.77681,6.77681,6.77681,6.77681], "fy":[0.00168,0.00168,0.00168,0.00168]}, - {"t":1.88515, "x":8.52699, "y":1.03977, "heading":1.5708, "vx":0.00869, "vy":1.49965, "omega":0.0, "ax":-0.36557, "ay":0.00009, "alpha":0.0, "fx":[-6.21824,-6.21824,-6.21824,-6.21824], "fy":[0.00154,0.00154,0.00154,0.00154]}, - {"t":1.93064, "x":8.52701, "y":1.10799, "heading":1.5708, "vx":-0.00794, "vy":1.49965, "omega":0.0, "ax":0.33275, "ay":0.00008, "alpha":0.0, "fx":[5.6599,5.6599,5.6599,5.6599], "fy":[0.0014,0.0014,0.0014,0.0014]}, - {"t":1.97613, "x":8.52699, "y":1.17621, "heading":1.5708, "vx":0.0072, "vy":1.49966, "omega":0.0, "ax":-0.29993, "ay":0.00007, "alpha":0.0, "fx":[-5.10179,-5.10179,-5.10179,-5.10179], "fy":[0.00126,0.00126,0.00126,0.00126]}, - {"t":2.02162, "x":8.52701, "y":1.24443, "heading":1.5708, "vx":-0.00645, "vy":1.49966, "omega":0.0, "ax":0.26713, "ay":0.00007, "alpha":0.0, "fx":[4.54387,4.54387,4.54387,4.54387], "fy":[0.00112,0.00112,0.00112,0.00112]}, - {"t":2.06711, "x":8.52699, "y":1.31265, "heading":1.5708, "vx":0.0057, "vy":1.49966, "omega":0.0, "ax":-0.23434, "ay":0.00006, "alpha":0.0, "fx":[-3.98613,-3.98613,-3.98613,-3.98613], "fy":[0.00099,0.00099,0.00099,0.00099]}, - {"t":2.1126, "x":8.52701, "y":1.38087, "heading":1.5708, "vx":-0.00496, "vy":1.49966, "omega":0.0, "ax":0.20156, "ay":0.00005, "alpha":0.0, "fx":[3.42855,3.42855,3.42855,3.42855], "fy":[0.00085,0.00085,0.00085,0.00085]}, - {"t":2.15809, "x":8.52699, "y":1.44909, "heading":1.5708, "vx":0.00421, "vy":1.49967, "omega":0.0, "ax":-0.16879, "ay":0.00004, "alpha":0.0, "fx":[-2.8711,-2.8711,-2.8711,-2.8711], "fy":[0.00071,0.00071,0.00071,0.00071]}, - {"t":2.20358, "x":8.52701, "y":1.51731, "heading":1.5708, "vx":-0.00347, "vy":1.49967, "omega":0.0, "ax":0.13603, "ay":0.00003, "alpha":0.0, "fx":[2.31377,2.31377,2.31377,2.31377], "fy":[0.00057,0.00057,0.00057,0.00057]}, - {"t":2.24907, "x":8.52699, "y":1.58553, "heading":1.5708, "vx":0.00272, "vy":1.49967, "omega":0.0, "ax":-0.10327, "ay":0.00003, "alpha":0.0, "fx":[-1.75652,-1.75652,-1.75652,-1.75652], "fy":[0.00043,0.00043,0.00043,0.00043]}, - {"t":2.29456, "x":8.52701, "y":1.65375, "heading":1.5708, "vx":-0.00198, "vy":1.49967, "omega":0.0, "ax":0.07051, "ay":0.00002, "alpha":0.0, "fx":[1.19935,1.19935,1.19935,1.19935], "fy":[0.00029,0.00029,0.00029,0.00029]}, - {"t":2.34005, "x":8.52699, "y":1.72197, "heading":1.5708, "vx":0.00123, "vy":1.49967, "omega":0.0, "ax":-0.03776, "ay":0.00001, "alpha":0.0, "fx":[-0.64222,-0.64222,-0.64222,-0.64222], "fy":[0.00016,0.00016,0.00016,0.00016]}, - {"t":2.38554, "x":8.52701, "y":1.79019, "heading":1.5708, "vx":-0.00049, "vy":1.49967, "omega":0.0, "ax":0.005, "ay":0.0, "alpha":0.0, "fx":[0.08512,0.08512,0.08512,0.08512], "fy":[0.00002,0.00002,0.00002,0.00002]}, - {"t":2.43103, "x":8.52699, "y":1.85841, "heading":1.5708, "vx":-0.00026, "vy":1.49967, "omega":0.0, "ax":0.02775, "ay":-0.00001, "alpha":0.0, "fx":[0.47198,0.47198,0.47198,0.47198], "fy":[-0.00012,-0.00012,-0.00012,-0.00012]}, - {"t":2.47652, "x":8.52701, "y":1.92663, "heading":1.5708, "vx":0.001, "vy":1.49967, "omega":0.0, "ax":-0.0605, "ay":-0.00002, "alpha":0.0, "fx":[-1.02909,-1.02909,-1.02909,-1.02909], "fy":[-0.00026,-0.00026,-0.00026,-0.00026]}, - {"t":2.52201, "x":8.52699, "y":1.99485, "heading":1.5708, "vx":-0.00175, "vy":1.49967, "omega":0.0, "ax":0.09326, "ay":-0.00002, "alpha":0.0, "fx":[1.58625,1.58625,1.58625,1.58625], "fy":[-0.0004,-0.0004,-0.0004,-0.0004]}, - {"t":2.56751, "x":8.52701, "y":2.06307, "heading":1.5708, "vx":0.00249, "vy":1.49967, "omega":0.0, "ax":-0.12601, "ay":-0.00003, "alpha":0.0, "fx":[-2.14347,-2.14347,-2.14347,-2.14347], "fy":[-0.00054,-0.00054,-0.00054,-0.00054]}, - {"t":2.613, "x":8.52699, "y":2.13129, "heading":1.5708, "vx":-0.00324, "vy":1.49967, "omega":0.0, "ax":0.15878, "ay":-0.00004, "alpha":0.0, "fx":[2.70077,2.70077,2.70077,2.70077], "fy":[-0.00067,-0.00067,-0.00067,-0.00067]}, - {"t":2.65849, "x":8.52701, "y":2.19951, "heading":1.5708, "vx":0.00398, "vy":1.49967, "omega":0.0, "ax":-0.19155, "ay":-0.00005, "alpha":0.0, "fx":[-3.25819,-3.25819,-3.25819,-3.25819], "fy":[-0.00081,-0.00081,-0.00081,-0.00081]}, - {"t":2.70398, "x":8.52699, "y":2.26773, "heading":1.5708, "vx":-0.00473, "vy":1.49967, "omega":0.0, "ax":0.22433, "ay":-0.00006, "alpha":0.0, "fx":[3.81572,3.81572,3.81572,3.81572], "fy":[-0.00095,-0.00095,-0.00095,-0.00095]}, - {"t":2.74947, "x":8.52701, "y":2.33595, "heading":1.5708, "vx":0.00548, "vy":1.49966, "omega":0.0, "ax":-0.25711, "ay":-0.00006, "alpha":0.0, "fx":[-4.37341,-4.37341,-4.37341,-4.37341], "fy":[-0.00109,-0.00109,-0.00109,-0.00109]}, - {"t":2.79496, "x":8.52699, "y":2.40418, "heading":1.5708, "vx":-0.00622, "vy":1.49966, "omega":0.0, "ax":0.28991, "ay":-0.00007, "alpha":0.0, "fx":[4.93127,4.93127,4.93127,4.93127], "fy":[-0.00123,-0.00123,-0.00123,-0.00123]}, - {"t":2.84045, "x":8.52701, "y":2.4724, "heading":1.5708, "vx":0.00697, "vy":1.49966, "omega":0.0, "ax":-0.32272, "ay":-0.00008, "alpha":0.0, "fx":[-5.48933,-5.48933,-5.48933,-5.48933], "fy":[-0.00137,-0.00137,-0.00137,-0.00137]}, - {"t":2.88594, "x":8.52699, "y":2.54061, "heading":1.5708, "vx":-0.00771, "vy":1.49965, "omega":0.0, "ax":0.35554, "ay":-0.00009, "alpha":0.0, "fx":[6.04759,6.04759,6.04759,6.04759], "fy":[-0.00151,-0.00151,-0.00151,-0.00151]}, - {"t":2.93143, "x":8.52701, "y":2.60883, "heading":1.5708, "vx":0.00846, "vy":1.49965, "omega":0.0, "ax":-0.38837, "ay":-0.0001, "alpha":0.0, "fx":[-6.60609,-6.60609,-6.60609,-6.60609], "fy":[-0.00165,-0.00165,-0.00165,-0.00165]}, - {"t":2.97692, "x":8.52699, "y":2.67705, "heading":1.5708, "vx":-0.00921, "vy":1.49964, "omega":0.0, "ax":0.42122, "ay":-0.00011, "alpha":0.0, "fx":[7.16484,7.16484,7.16484,7.16484], "fy":[-0.00179,-0.00179,-0.00179,-0.00179]}, - {"t":3.02241, "x":8.52701, "y":2.74527, "heading":1.5708, "vx":0.00995, "vy":1.49964, "omega":0.0, "ax":-0.45409, "ay":-0.00011, "alpha":0.0, "fx":[-7.72387,-7.72387,-7.72387,-7.72387], "fy":[-0.00193,-0.00193,-0.00193,-0.00193]}, - {"t":3.0679, "x":8.52699, "y":2.81349, "heading":1.5708, "vx":-0.0107, "vy":1.49963, "omega":0.0, "ax":0.48697, "ay":-0.00018, "alpha":0.0, "fx":[8.28319,8.28319,8.28319,8.28319], "fy":[-0.00307,-0.00307,-0.00307,-0.00307]}, - {"t":3.11339, "x":8.52701, "y":2.88171, "heading":1.5708, "vx":0.01145, "vy":1.49963, "omega":0.0, "ax":-0.51988, "ay":-6.51831, "alpha":0.0, "fx":[-8.84307,-8.84307,-8.84307,-8.84307], "fy":[-110.87458,-110.87458,-110.87458,-110.87458]}, - {"t":3.15888, "x":8.52699, "y":2.94319, "heading":1.5708, "vx":-0.0122, "vy":1.20311, "omega":0.0, "ax":0.55291, "ay":-9.39264, "alpha":0.0, "fx":[9.40479,9.40479,9.40479,9.40479], "fy":[-159.76618,-159.76618,-159.76618,-159.76618]}, - {"t":3.20437, "x":8.52701, "y":2.9882, "heading":1.5708, "vx":0.01295, "vy":0.77583, "omega":0.0, "ax":-0.57777, "ay":-9.40536, "alpha":0.0, "fx":[-9.82769,-9.82769,-9.82769,-9.82769], "fy":[-159.98255,-159.98255,-159.98255,-159.98255]}, - {"t":3.24986, "x":8.527, "y":3.01376, "heading":1.5708, "vx":-0.01333, "vy":0.34798, "omega":0.0, "ax":-6.87125, "ay":-6.45549, "alpha":0.0, "fx":[-116.87806,-116.87806,-116.87806,-116.87806], "fy":[-109.80603,-109.80603,-109.80603,-109.80603]}, - {"t":3.29004, "x":8.52092, "y":3.02253, "heading":1.5708, "vx":-0.28939, "vy":0.08862, "omega":0.0, "ax":-6.86721, "ay":-6.45809, "alpha":0.0, "fx":[-116.80931,-116.80931,-116.80931,-116.80931], "fy":[-109.85034,-109.85034,-109.85034,-109.85034]}, - {"t":3.33021, "x":8.50375, "y":3.02088, "heading":1.5708, "vx":-0.56529, "vy":-0.17084, "omega":0.0, "ax":-6.86205, "ay":-6.46141, "alpha":0.0, "fx":[-116.72145,-116.72145,-116.72145,-116.72145], "fy":[-109.90675,-109.90675,-109.90675,-109.90675]}, - {"t":3.37039, "x":8.4755, "y":3.0088, "heading":1.5708, "vx":-0.84098, "vy":-0.43043, "omega":0.0, "ax":-6.85521, "ay":-6.46578, "alpha":0.0, "fx":[-116.60513,-116.60513,-116.60513,-116.60513], "fy":[-109.98114,-109.98114,-109.98114,-109.98114]}, - {"t":3.41056, "x":8.43618, "y":2.98629, "heading":1.5708, "vx":-1.11639, "vy":-0.6902, "omega":0.0, "ax":-6.84572, "ay":-6.47183, "alpha":0.0, "fx":[-116.44369,-116.44369,-116.44369,-116.44369], "fy":[-110.0839,-110.0839,-110.0839,-110.0839]}, - {"t":3.45074, "x":8.3858, "y":2.95333, "heading":1.5708, "vx":-1.39143, "vy":-0.95021, "omega":0.0, "ax":-6.83164, "ay":-6.48074, "alpha":0.0, "fx":[-116.2042,-116.2042,-116.2042,-116.2042], "fy":[-110.23546,-110.23546,-110.23546,-110.23546]}, - {"t":3.49092, "x":8.32439, "y":2.90993, "heading":1.5708, "vx":-1.66589, "vy":-1.21058, "omega":0.0, "ax":-6.80854, "ay":-6.49524, "alpha":0.0, "fx":[-115.81128,-115.81128,-115.81128,-115.81128], "fy":[-110.48213,-110.48213,-110.48213,-110.48213]}, - {"t":3.53109, "x":8.25197, "y":2.85605, "heading":1.5708, "vx":-1.93943, "vy":-1.47154, "omega":0.0, "ax":-6.76355, "ay":-6.52313, "alpha":0.0, "fx":[-115.04611,-115.04611,-115.04611,-115.04611], "fy":[-110.95653,-110.95653,-110.95653,-110.95653]}, - {"t":3.57127, "x":8.16859, "y":2.79166, "heading":1.5708, "vx":-2.21117, "vy":-1.73361, "omega":0.0, "ax":-6.63705, "ay":-6.59945, "alpha":0.0, "fx":[-112.89432,-112.89432,-112.89432,-112.89432], "fy":[-112.25473,-112.25473,-112.25473,-112.25473]}, - {"t":3.61144, "x":8.0744, "y":2.71669, "heading":1.5708, "vx":-2.47782, "vy":-1.99875, "omega":0.0, "ax":-4.10825, "ay":-7.64412, "alpha":0.0, "fx":[-69.88021,-69.88021,-69.88021,-69.88021], "fy":[-130.02422,-130.02422,-130.02422,-130.02422]}, - {"t":3.65162, "x":7.97153, "y":2.63022, "heading":1.5708, "vx":-2.64287, "vy":-2.30586, "omega":0.0, "ax":1.65157, "ay":-1.85385, "alpha":0.0, "fx":[28.09276,28.09276,28.09276,28.09276], "fy":[-31.53353,-31.53353,-31.53353,-31.53353]}, - {"t":3.6918, "x":7.86668, "y":2.53608, "heading":1.5708, "vx":-2.57652, "vy":-2.38034, "omega":0.0, "ax":0.24249, "ay":-0.2614, "alpha":0.0, "fx":[4.12475,4.12475,4.12475,4.12475], "fy":[-4.44639,-4.44639,-4.44639,-4.44639]}, - {"t":3.73197, "x":7.76337, "y":2.44024, "heading":1.5708, "vx":-2.56677, "vy":-2.39084, "omega":0.0, "ax":0.17091, "ay":-0.06627, "alpha":0.0, "fx":[2.9071,2.9071,2.9071,2.9071], "fy":[-1.12725,-1.12725,-1.12725,-1.12725]}, - {"t":3.77215, "x":7.66038, "y":2.34413, "heading":1.5708, "vx":-2.55991, "vy":-2.3935, "omega":0.0, "ax":6.78958, "ay":6.30078, "alpha":0.0, "fx":[115.48889,115.48889,115.48889,115.48889], "fy":[107.17443,107.17443,107.17443,107.17443]}, - {"t":3.81232, "x":7.56301, "y":2.25305, "heading":1.5708, "vx":-2.28713, "vy":-2.14036, "omega":0.0, "ax":6.86565, "ay":6.39084, "alpha":0.0, "fx":[116.78279,116.78279,116.78279,116.78279], "fy":[108.70638,108.70638,108.70638,108.70638]}, - {"t":3.8525, "x":7.47667, "y":2.17222, "heading":1.5708, "vx":-2.01129, "vy":-1.88361, "omega":0.0, "ax":6.8806, "ay":6.40881, "alpha":0.0, "fx":[117.03707,117.03707,117.03707,117.03707], "fy":[109.0121,109.0121,109.0121,109.0121]}, - {"t":3.89268, "x":7.40141, "y":2.10172, "heading":1.5708, "vx":-1.73486, "vy":-1.62613, "omega":0.0, "ax":6.88691, "ay":6.41656, "alpha":0.0, "fx":[117.14433,117.14433,117.14433,117.14433], "fy":[109.14387,109.14387,109.14387,109.14387]}, - {"t":3.93285, "x":7.33727, "y":2.04156, "heading":1.5708, "vx":-1.45817, "vy":-1.36833, "omega":0.0, "ax":0.43396, "ay":-0.45231, "alpha":0.0, "fx":[7.38147,7.38147,7.38147,7.38147], "fy":[-7.6937,-7.6937,-7.6937,-7.6937]}, - {"t":3.97568, "x":7.27523, "y":1.98255, "heading":1.5708, "vx":-1.43959, "vy":-1.3877, "omega":0.0, "ax":0.0191, "ay":-0.0198, "alpha":0.0, "fx":[0.32488,0.32488,0.32488,0.32488], "fy":[-0.33683,-0.33683,-0.33683,-0.33683]}, - {"t":4.0185, "x":7.2136, "y":1.92311, "heading":1.5708, "vx":-1.43877, "vy":-1.38855, "omega":0.0, "ax":0.00159, "ay":-0.00164, "alpha":0.0, "fx":[0.02696,0.02696,0.02696,0.02696], "fy":[-0.02794,-0.02794,-0.02794,-0.02794]}, - {"t":4.06132, "x":7.15199, "y":1.86364, "heading":1.5708, "vx":-1.4387, "vy":-1.38862, "omega":0.0, "ax":0.00089, "ay":-0.00092, "alpha":0.0, "fx":[0.01515,0.01515,0.01515,0.01515], "fy":[-0.0157,-0.0157,-0.0157,-0.0157]}, - {"t":4.10415, "x":7.09038, "y":1.80418, "heading":1.5708, "vx":-1.43866, "vy":-1.38866, "omega":0.0, "ax":0.00091, "ay":-0.00094, "alpha":0.0, "fx":[0.0154,0.0154,0.0154,0.0154], "fy":[-0.01597,-0.01597,-0.01597,-0.01597]}, - {"t":4.14697, "x":7.02877, "y":1.74471, "heading":1.5708, "vx":-1.43862, "vy":-1.3887, "omega":0.0, "ax":0.00095, "ay":-0.00099, "alpha":0.0, "fx":[0.01622,0.01622,0.01622,0.01622], "fy":[-0.01681,-0.01681,-0.01681,-0.01681]}, - {"t":4.18979, "x":6.96716, "y":1.68524, "heading":1.5708, "vx":-1.43858, "vy":-1.38874, "omega":0.0, "ax":0.00101, "ay":-0.00104, "alpha":0.0, "fx":[0.01713,0.01713,0.01713,0.01713], "fy":[-0.01775,-0.01775,-0.01775,-0.01775]}, - {"t":4.23262, "x":6.90556, "y":1.62577, "heading":1.5708, "vx":-1.43854, "vy":-1.38879, "omega":0.0, "ax":0.00106, "ay":-0.0011, "alpha":0.0, "fx":[0.01811,0.01811,0.01811,0.01811], "fy":[-0.01877,-0.01877,-0.01877,-0.01877]}, - {"t":4.27544, "x":6.84396, "y":1.5663, "heading":1.5708, "vx":-1.43849, "vy":-1.38884, "omega":0.0, "ax":0.00113, "ay":-0.00117, "alpha":0.0, "fx":[0.01918,0.01918,0.01918,0.01918], "fy":[-0.01988,-0.01988,-0.01988,-0.01988]}, - {"t":4.31826, "x":6.78236, "y":1.50682, "heading":1.5708, "vx":-1.43845, "vy":-1.38889, "omega":0.0, "ax":0.0012, "ay":-0.00124, "alpha":0.0, "fx":[0.02035,0.02035,0.02035,0.02035], "fy":[-0.02109,-0.02109,-0.02109,-0.02109]}, - {"t":4.36109, "x":6.72076, "y":1.44734, "heading":1.5708, "vx":-1.43839, "vy":-1.38894, "omega":0.0, "ax":0.00127, "ay":-0.00132, "alpha":0.0, "fx":[0.02163,0.02163,0.02163,0.02163], "fy":[-0.02241,-0.02241,-0.02241,-0.02241]}, - {"t":4.40391, "x":6.65916, "y":1.38786, "heading":1.5708, "vx":-1.43834, "vy":-1.389, "omega":0.0, "ax":0.00135, "ay":-0.0014, "alpha":0.0, "fx":[0.02304,0.02304,0.02304,0.02304], "fy":[-0.02387,-0.02387,-0.02387,-0.02387]}, - {"t":4.44673, "x":6.59757, "y":1.32838, "heading":1.5708, "vx":-1.43828, "vy":-1.38906, "omega":0.0, "ax":0.00145, "ay":-0.0015, "alpha":0.0, "fx":[0.02461,0.02461,0.02461,0.02461], "fy":[-0.02549,-0.02549,-0.02549,-0.02549]}, - {"t":4.48956, "x":6.53598, "y":1.26889, "heading":1.5708, "vx":-1.43822, "vy":-1.38912, "omega":0.0, "ax":0.00155, "ay":-0.0016, "alpha":0.0, "fx":[0.02635,0.02635,0.02635,0.02635], "fy":[-0.0273,-0.0273,-0.0273,-0.0273]}, - {"t":4.53238, "x":6.47439, "y":1.20941, "heading":1.5708, "vx":-1.43815, "vy":-1.38919, "omega":0.0, "ax":0.00166, "ay":-0.00172, "alpha":0.0, "fx":[0.02832,0.02832,0.02832,0.02832], "fy":[-0.02933,-0.02933,-0.02933,-0.02933]}, - {"t":4.5752, "x":6.41281, "y":1.14991, "heading":1.5708, "vx":-1.43808, "vy":-1.38926, "omega":0.0, "ax":0.0018, "ay":-0.00186, "alpha":0.0, "fx":[0.03055,0.03055,0.03055,0.03055], "fy":[-0.03164,-0.03164,-0.03164,-0.03164]}, - {"t":4.61803, "x":6.35123, "y":1.09042, "heading":1.5708, "vx":-1.43801, "vy":-1.38934, "omega":0.0, "ax":0.00195, "ay":-0.00202, "alpha":0.0, "fx":[0.0331,0.0331,0.0331,0.0331], "fy":[-0.03428,-0.03428,-0.03428,-0.03428]}, - {"t":4.66085, "x":6.28965, "y":1.03092, "heading":1.5708, "vx":-1.43792, "vy":-1.38943, "omega":0.0, "ax":0.00212, "ay":-0.00219, "alpha":0.0, "fx":[0.03606,0.03606,0.03606,0.03606], "fy":[-0.03733,-0.03733,-0.03733,-0.03733]}, - {"t":4.70367, "x":6.22807, "y":0.97142, "heading":1.5708, "vx":-1.43783, "vy":-1.38952, "omega":0.0, "ax":0.00232, "ay":-0.00241, "alpha":0.0, "fx":[0.03953,0.03953,0.03953,0.03953], "fy":[-0.04092,-0.04092,-0.04092,-0.04092]}, - {"t":4.7465, "x":6.1665, "y":0.91191, "heading":1.5708, "vx":-1.43773, "vy":-1.38963, "omega":0.0, "ax":0.00257, "ay":-0.00266, "alpha":0.0, "fx":[0.04368,0.04368,0.04368,0.04368], "fy":[-0.04521,-0.04521,-0.04521,-0.04521]}, - {"t":4.78932, "x":6.10494, "y":0.8524, "heading":1.5708, "vx":-1.43762, "vy":-1.38974, "omega":0.0, "ax":0.00287, "ay":-0.00297, "alpha":0.0, "fx":[0.04874,0.04874,0.04874,0.04874], "fy":[-0.05044,-0.05044,-0.05044,-0.05044]}, - {"t":4.83214, "x":6.04337, "y":0.79289, "heading":1.5708, "vx":-1.4375, "vy":-1.38987, "omega":0.0, "ax":0.00323, "ay":-0.00334, "alpha":0.0, "fx":[0.05499,0.05499,0.05499,0.05499], "fy":[-0.05689,-0.05689,-0.05689,-0.05689]}, - {"t":4.87497, "x":5.98182, "y":0.73337, "heading":1.5708, "vx":-1.43736, "vy":-1.39001, "omega":0.0, "ax":0.00358, "ay":-0.0037, "alpha":0.0, "fx":[0.06089,0.06089,0.06089,0.06089], "fy":[-0.06299,-0.06299,-0.06299,-0.06299]}, - {"t":4.91779, "x":5.92027, "y":0.67384, "heading":1.5708, "vx":-1.43721, "vy":-1.39017, "omega":0.0, "ax":0.00096, "ay":-0.00099, "alpha":0.0, "fx":[0.01631,0.01631,0.01631,0.01631], "fy":[-0.0169,-0.0169,-0.0169,-0.0169]}, - {"t":4.96061, "x":5.85872, "y":0.61431, "heading":1.5708, "vx":-1.43717, "vy":-1.39021, "omega":0.0, "ax":-0.07625, "ay":0.07901, "alpha":0.0, "fx":[-1.29696,-1.29696,-1.29696,-1.29696], "fy":[1.3439,1.3439,1.3439,1.3439]}, - {"t":5.00344, "x":5.79711, "y":0.55484, "heading":1.5708, "vx":-1.44043, "vy":-1.38683, "omega":0.0, "ax":-1.7399, "ay":1.91035, "alpha":0.0, "fx":[-29.59516,-29.59516,-29.59516,-29.59516], "fy":[32.49446,32.49446,32.49446,32.49446]}, - {"t":5.04626, "x":5.73383, "y":0.49721, "heading":1.5708, "vx":-1.51494, "vy":-1.30502, "omega":0.0, "ax":-5.06488, "ay":7.13736, "alpha":0.0, "fx":[-86.15221,-86.15221,-86.15221,-86.15221], "fy":[121.40453,121.40453,121.40453,121.40453]}, - {"t":5.08908, "x":5.66431, "y":0.44787, "heading":1.5708, "vx":-1.73184, "vy":-0.99937, "omega":0.0, "ax":-3.8198, "ay":8.47517, "alpha":0.0, "fx":[-64.97377,-64.97377,-64.97377,-64.97377], "fy":[144.16016,144.16016,144.16016,144.16016]}, - {"t":5.13191, "x":5.58665, "y":0.41284, "heading":1.5708, "vx":-1.89541, "vy":-0.63644, "omega":0.0, "ax":-1.99385, "ay":9.1644, "alpha":0.0, "fx":[-33.91479,-33.91479,-33.91479,-33.91479], "fy":[155.88378,155.88378,155.88378,155.88378]}, - {"t":5.17473, "x":5.50365, "y":0.39399, "heading":1.5708, "vx":-1.98079, "vy":-0.24399, "omega":0.0, "ax":-0.01004, "ay":0.60349, "alpha":0.0, "fx":[-0.17072,-0.17072,-0.17072,-0.17072], "fy":[10.26511,10.26511,10.26511,10.26511]}, - {"t":5.17475, "x":5.5036, "y":0.39398, "heading":1.5708, "vx":-1.9808, "vy":-0.24397, "omega":0.0, "ax":-0.07338, "ay":0.59558, "alpha":0.0, "fx":[-1.24813,-1.24813,-1.24813,-1.24813], "fy":[10.13071,10.13071,10.13071,10.13071]}, - {"t":5.17478, "x":5.50355, "y":0.39398, "heading":1.5708, "vx":-1.9808, "vy":-0.24396, "omega":0.0, "ax":-0.13671, "ay":0.58763, "alpha":0.0, "fx":[-2.32544,-2.32544,-2.32544,-2.32544], "fy":[9.99539,9.99539,9.99539,9.99539]}, - {"t":5.1748, "x":5.50351, "y":0.39397, "heading":1.5708, "vx":-1.9808, "vy":-0.24395, "omega":0.0, "ax":-0.20005, "ay":0.57962, "alpha":0.0, "fx":[-3.40274,-3.40274,-3.40274,-3.40274], "fy":[9.85916,9.85916,9.85916,9.85916]}, - {"t":5.17483, "x":5.50346, "y":0.39397, "heading":1.5708, "vx":-1.98081, "vy":-0.24393, "omega":0.0, "ax":-0.26339, "ay":0.57155, "alpha":0.0, "fx":[-4.48012,-4.48012,-4.48012,-4.48012], "fy":[9.72199,9.72199,9.72199,9.72199]}, - {"t":5.17485, "x":5.50341, "y":0.39396, "heading":1.5708, "vx":-1.98081, "vy":-0.24392, "omega":0.0, "ax":-0.32674, "ay":0.56344, "alpha":0.0, "fx":[-5.55769,-5.55769,-5.55769,-5.55769], "fy":[9.58387,9.58387,9.58387,9.58387]}, - {"t":5.17487, "x":5.50336, "y":0.39395, "heading":1.5708, "vx":-1.98082, "vy":-0.2439, "omega":0.0, "ax":-0.3901, "ay":0.55526, "alpha":0.0, "fx":[-6.63555,-6.63555,-6.63555,-6.63555], "fy":[9.44479,9.44479,9.44479,9.44479]}, - {"t":5.1749, "x":5.50331, "y":0.39395, "heading":1.5708, "vx":-1.98083, "vy":-0.24389, "omega":0.0, "ax":-0.45349, "ay":0.54703, "alpha":0.0, "fx":[-7.7138,-7.7138,-7.7138,-7.7138], "fy":[9.30474,9.30474,9.30474,9.30474]}, - {"t":5.17492, "x":5.50327, "y":0.39394, "heading":1.5708, "vx":-1.98084, "vy":-0.24388, "omega":0.0, "ax":-0.51691, "ay":0.53873, "alpha":0.0, "fx":[-8.79253,-8.79253,-8.79253,-8.79253], "fy":[9.16371,9.16371,9.16371,9.16371]}, - {"t":5.17495, "x":5.50322, "y":0.39394, "heading":1.5708, "vx":-1.98085, "vy":-0.24386, "omega":0.0, "ax":-0.58037, "ay":0.53038, "alpha":0.0, "fx":[-9.87187,-9.87187,-9.87187,-9.87187], "fy":[9.02167,9.02167,9.02167,9.02167]}, - {"t":5.17497, "x":5.50317, "y":0.39393, "heading":1.5708, "vx":-1.98087, "vy":-0.24385, "omega":0.0, "ax":-0.64386, "ay":0.52197, "alpha":0.0, "fx":[-10.95191,-10.95191,-10.95191,-10.95191], "fy":[8.87861,8.87861,8.87861,8.87861]}, - {"t":5.175, "x":5.50312, "y":0.39392, "heading":1.5708, "vx":-1.98088, "vy":-0.24384, "omega":0.0, "ax":-0.70741, "ay":0.5135, "alpha":0.0, "fx":[-12.03277,-12.03277,-12.03277,-12.03277], "fy":[8.73452,8.73452,8.73452,8.73452]}, - {"t":5.17502, "x":5.50307, "y":0.39392, "heading":1.5708, "vx":-1.9809, "vy":-0.24383, "omega":0.0, "ax":-0.771, "ay":0.50497, "alpha":0.0, "fx":[-13.11456,-13.11456,-13.11456,-13.11456], "fy":[8.58938,8.58938,8.58938,8.58938]}, - {"t":5.17504, "x":5.50303, "y":0.39391, "heading":1.5708, "vx":-1.98092, "vy":-0.24381, "omega":0.0, "ax":-0.83466, "ay":0.49637, "alpha":0.0, "fx":[-14.19739,-14.19739,-14.19739,-14.19739], "fy":[8.44317,8.44317,8.44317,8.44317]}, - {"t":5.17507, "x":5.50298, "y":0.39391, "heading":1.5708, "vx":-1.98094, "vy":-0.2438, "omega":0.0, "ax":-0.89839, "ay":0.48771, "alpha":0.0, "fx":[-15.2814,-15.2814,-15.2814,-15.2814], "fy":[8.29588,8.29588,8.29588,8.29588]}, - {"t":5.17509, "x":5.50293, "y":0.3939, "heading":1.5708, "vx":-1.98096, "vy":-0.24379, "omega":0.0, "ax":-0.9622, "ay":0.47899, "alpha":0.0, "fx":[-16.36671,-16.36671,-16.36671,-16.36671], "fy":[8.14748,8.14748,8.14748,8.14748]}, - {"t":5.17512, "x":5.50288, "y":0.39389, "heading":1.5708, "vx":-1.98098, "vy":-0.24378, "omega":0.0, "ax":-1.02609, "ay":0.4702, "alpha":0.0, "fx":[-17.45345,-17.45345,-17.45345,-17.45345], "fy":[7.99794,7.99794,7.99794,7.99794]}, - {"t":5.17514, "x":5.50283, "y":0.39389, "heading":1.5708, "vx":-1.98101, "vy":-0.24377, "omega":0.0, "ax":-1.09007, "ay":0.46134, "alpha":0.0, "fx":[-18.54177,-18.54177,-18.54177,-18.54177], "fy":[7.84726,7.84726,7.84726,7.84726]}, - {"t":5.17517, "x":5.50278, "y":0.39388, "heading":1.5708, "vx":-1.98103, "vy":-0.24376, "omega":0.0, "ax":-1.15415, "ay":0.45241, "alpha":0.0, "fx":[-19.63179,-19.63179,-19.63179,-19.63179], "fy":[7.6954,7.6954,7.6954,7.6954]}, - {"t":5.17519, "x":5.50274, "y":0.39388, "heading":1.5708, "vx":-1.98106, "vy":-0.24375, "omega":0.0, "ax":-8.94173, "ay":2.96138, "alpha":0.0, "fx":[-152.09635,-152.09635,-152.09635,-152.09635], "fy":[50.37229,50.37229,50.37229,50.37229]}, - {"t":5.23076, "x":5.37884, "y":0.3849, "heading":1.5708, "vx":-2.47795, "vy":-0.07918, "omega":0.0, "ax":-9.21804, "ay":1.86556, "alpha":0.0, "fx":[-156.79624,-156.79624,-156.79624,-156.79624], "fy":[31.73268,31.73268,31.73268,31.73268]}, - {"t":5.28633, "x":5.22691, "y":0.38338, "heading":1.5708, "vx":-2.99019, "vy":0.02449, "omega":0.0, "ax":-9.30299, "ay":0.34213, "alpha":0.0, "fx":[-158.24127,-158.24127,-158.24127,-158.24127], "fy":[5.81945,5.81945,5.81945,5.81945]}, - {"t":5.3419, "x":5.04639, "y":0.38527, "heading":1.5708, "vx":-3.50715, "vy":0.0435, "omega":0.0, "ax":-0.02487, "ay":-0.12844, "alpha":0.0, "fx":[-0.42303,-0.42303,-0.42303,-0.42303], "fy":[-2.18475,-2.18475,-2.18475,-2.18475]}, - {"t":5.39747, "x":4.85146, "y":0.38749, "heading":1.5708, "vx":-3.50853, "vy":0.03636, "omega":0.0, "ax":-0.00246, "ay":-0.31219, "alpha":0.0, "fx":[-0.0419,-0.0419,-0.0419,-0.0419], "fy":[-5.31018,-5.31018,-5.31018,-5.31018]}, - {"t":5.45304, "x":4.65649, "y":0.38903, "heading":1.5708, "vx":-3.50867, "vy":0.01901, "omega":0.0, "ax":-0.00082, "ay":-0.22673, "alpha":0.0, "fx":[-0.01398,-0.01398,-0.01398,-0.01398], "fy":[-3.85661,-3.85661,-3.85661,-3.85661]}, - {"t":5.50861, "x":4.46151, "y":0.38974, "heading":1.5708, "vx":-3.50872, "vy":0.00641, "omega":0.0, "ax":-0.00007, "ay":-0.18205, "alpha":0.0, "fx":[-0.00121,-0.00121,-0.00121,-0.00121], "fy":[-3.09654,-3.09654,-3.09654,-3.09654]}, - {"t":5.56418, "x":4.26653, "y":0.38981, "heading":1.5708, "vx":-3.50872, "vy":-0.0037, "omega":0.0, "ax":0.0004, "ay":-0.16923, "alpha":0.0, "fx":[0.00687,0.00687,0.00687,0.00687], "fy":[-2.87849,-2.87849,-2.87849,-2.87849]}, - {"t":5.61975, "x":4.07156, "y":0.38935, "heading":1.5708, "vx":-3.5087, "vy":-0.01311, "omega":0.0, "ax":0.00095, "ay":-0.18355, "alpha":0.0, "fx":[0.01617,0.01617,0.01617,0.01617], "fy":[-3.12215,-3.12215,-3.12215,-3.12215]}, - {"t":5.67531, "x":3.87658, "y":0.38833, "heading":1.5708, "vx":-3.50865, "vy":-0.02331, "omega":0.0, "ax":0.00194, "ay":-0.22945, "alpha":0.0, "fx":[0.03298,0.03298,0.03298,0.03298], "fy":[-3.90296,-3.90296,-3.90296,-3.90296]}, - {"t":5.73088, "x":3.68161, "y":0.38668, "heading":1.5708, "vx":-3.50854, "vy":-0.03606, "omega":0.0, "ax":0.00463, "ay":-0.30741, "alpha":0.0, "fx":[0.07878,0.07878,0.07878,0.07878], "fy":[-5.22898,-5.22898,-5.22898,-5.22898]}, - {"t":5.78645, "x":3.48665, "y":0.38421, "heading":1.5708, "vx":-3.50828, "vy":-0.05314, "omega":0.0, "ax":7.06956, "ay":0.12027, "alpha":0.0, "fx":[120.25118,120.25118,120.25118,120.25118], "fy":[2.04579,2.04579,2.04579,2.04579]}, - {"t":5.84202, "x":3.30261, "y":0.38144, "heading":1.5708, "vx":-3.11543, "vy":-0.04646, "omega":0.0, "ax":9.39057, "ay":0.23305, "alpha":0.0, "fx":[159.73092,159.73092,159.73092,159.73092], "fy":[3.96419,3.96419,3.96419,3.96419]}, - {"t":5.89759, "x":3.14399, "y":0.37922, "heading":1.5708, "vx":-2.5936, "vy":-0.03351, "omega":0.0, "ax":9.40902, "ay":0.34336, "alpha":0.0, "fx":[160.04467,160.04467,160.04467,160.04467], "fy":[5.84047,5.84047,5.84047,5.84047]}, - {"t":5.95316, "x":3.01439, "y":0.37789, "heading":1.5708, "vx":-2.07075, "vy":-0.01443, "omega":0.0, "ax":6.5028, "ay":6.82534, "alpha":0.0, "fx":[110.61074,110.61074,110.61074,110.61074], "fy":[116.09702,116.09702,116.09702,116.09702]}, - {"t":6.00873, "x":2.90936, "y":0.38762, "heading":1.5708, "vx":-1.70939, "vy":0.36485, "omega":0.0, "ax":4.66178, "ay":7.77157, "alpha":7.0174, "fx":[70.56889,132.50879,82.55967,31.54516], "fy":[143.79573,90.2236,137.51089,157.23858]}, - {"t":6.04025, "x":2.8578, "y":0.40298, "heading":1.5708, "vx":-1.56246, "vy":0.6098, "omega":0.22117, "ax":4.72829, "ay":7.74781, "alpha":6.8859, "fx":[72.87144,132.20944,83.25822,33.36857], "fy":[142.61081,90.62486,137.07203,156.84459]}, - {"t":6.07177, "x":2.81091, "y":0.42605, "heading":1.57777, "vx":-1.41344, "vy":0.85399, "omega":0.4382, "ax":4.81017, "ay":7.71474, "alpha":6.75996, "fx":[76.22513,131.92601,83.83739,35.28969], "fy":[140.80814,90.99414,136.69754,156.402]}, - {"t":6.10328, "x":2.76875, "y":0.4568, "heading":1.59158, "vx":-1.26183, "vy":1.09714, "omega":0.65126, "ax":4.9108, "ay":7.66972, "alpha":6.63814, "fx":[80.5911,131.65282,84.38737,37.4939], "fy":[138.30815,91.33777,136.33213,155.8606]}, - {"t":6.1348, "x":2.73142, "y":0.49519, "heading":1.6121, "vx":-1.10705, "vy":1.33888, "omega":0.86048, "ax":5.03558, "ay":7.60875, "alpha":6.51542, "fx":[85.92284,131.37662,85.04482,40.27077], "fy":[135.00127,91.67136,135.88825,155.12967]}, - {"t":6.16632, "x":2.69902, "y":0.54116, "heading":1.63923, "vx":-0.94834, "vy":1.57869, "omega":1.06583, "ax":5.19367, "ay":7.52538, "alpha":6.37584, "fx":[92.15619,131.07646,86.03566,44.10276], "fy":[130.75005,92.01869,135.21571,154.03355]}, - {"t":6.19784, "x":2.67171, "y":0.59466, "heading":1.67282, "vx":-0.78465, "vy":1.81587, "omega":1.26679, "ax":5.40207, "ay":7.40787, "alpha":6.17506, "fx":[99.20096,130.72251,87.77064,49.85667], "fy":[125.39009,92.41064,134.02735,152.19504]}, - {"t":6.22936, "x":2.64967, "y":0.65557, "heading":1.71274, "vx":-0.61439, "vy":2.04935, "omega":1.46141, "ax":5.69577, "ay":7.23078, "alpha":5.79419, "fx":[106.94077,130.27334,91.08759,59.2322], "fy":[118.72237,92.88298,131.68693,148.68157]}, - {"t":6.26087, "x":2.63313, "y":0.72375, "heading":1.75881, "vx":-0.43487, "vy":2.27725, "omega":1.64403, "ax":6.15484, "ay":6.92403, "alpha":4.89846, "fx":[115.25685,129.67071,97.97178,75.8687], "fy":[110.46979,93.46643,126.45036,140.71639]}, - {"t":6.29239, "x":2.62248, "y":0.79897, "heading":1.81062, "vx":-0.24088, "vy":2.49548, "omega":1.79842, "ax":6.9729, "ay":6.22958, "alpha":2.42801, "fx":[124.11812,128.83999,114.11971,107.35008], "fy":[100.0943,94.12578,111.61271,118.02069]}, - {"t":6.32391, "x":2.61835, "y":0.88071, "heading":1.8673, "vx":-0.02111, "vy":2.69182, "omega":1.87495, "ax":8.33282, "ay":3.95271, "alpha":-5.23001, "fx":[133.90767,127.97331,152.42783,152.64646], "fy":[85.95501,94.04025,44.20629,44.73626]}, - {"t":6.35543, "x":2.62183, "y":0.96752, "heading":1.9264, "vx":0.24152, "vy":2.81641, "omega":1.71011, "ax":9.16651, "ay":0.51485, "alpha":-4.78831, "fx":[154.32069,154.80463,156.4516,158.10166], "fy":[34.43034,29.25842,-20.99668,-7.66236]}, - {"t":6.38695, "x":2.63399, "y":1.05654, "heading":1.9803, "vx":0.53043, "vy":2.83263, "omega":1.55919, "ax":7.63126, "ay":-4.5425, "alpha":7.05574, "fx":[99.36317,133.56128,155.40112,130.89633], "fy":[-122.34372,-84.44937,-20.27794,-81.99538]}, - {"t":6.41846, "x":2.6545, "y":1.14356, "heading":2.02944, "vx":0.77095, "vy":2.68946, "omega":1.78157, "ax":2.02675, "ay":-8.22778, "alpha":10.25486, "fx":[16.22553,81.23063,98.95992,-58.51833], "fy":[-158.01822,-136.38484,-119.75779,-145.64766]}, - {"t":6.44998, "x":2.67981, "y":1.22424, "heading":2.08559, "vx":0.83483, "vy":2.43014, "omega":2.10478, "ax":-1.22507, "ay":-9.28244, "alpha":-0.05893, "fx":[-20.86417,-21.33155,-20.81161,-20.34491], "fy":[-157.88432,-157.82471,-157.89966,-157.95765]}, - {"t":6.4815, "x":2.70551, "y":1.29622, "heading":2.15193, "vx":0.79622, "vy":2.13758, "omega":2.10293, "ax":-2.30864, "ay":-9.01316, "alpha":-3.82834, "fx":[-46.71174,-67.25199,-32.92806,-10.18538], "fy":[-152.54407,-144.85857,-156.41532,-159.42739]}, - {"t":6.51302, "x":2.72946, "y":1.35912, "heading":2.21821, "vx":0.72346, "vy":1.8535, "omega":1.98227, "ax":-2.81725, "ay":-8.76704, "alpha":-5.99625, "fx":[-65.63228,-86.02546,-35.21656,-4.80785], "fy":[-145.62781,-134.84808,-156.13547,-159.88832]}, - {"t":6.54453, "x":2.75086, "y":1.41318, "heading":2.28069, "vx":0.63466, "vy":1.57718, "omega":1.79328, "ax":-3.09657, "ay":-8.58335, "alpha":-7.48365, "fx":[-79.55893,-94.88054,-34.95905,-1.2888], "fy":[-138.67937,-128.94518,-156.30678,-160.07018]}, - {"t":6.57605, "x":2.76933, "y":1.45863, "heading":2.33721, "vx":0.53707, "vy":1.30665, "omega":1.55741, "ax":-3.26192, "ay":-8.44726, "alpha":-8.58957, "fx":[-89.74679,-99.48879,-33.86961,1.16814], "fy":[-132.4459,-125.53675,-156.61553,-160.14403]}, - {"t":6.60757, "x":2.78463, "y":1.49562, "heading":2.38629, "vx":0.43426, "vy":1.04042, "omega":1.28668, "ax":-3.36387, "ay":-8.34647, "alpha":-9.42641, "fx":[-97.14634,-102.03398,-32.58303,2.88976], "fy":[-127.22175,-123.55467,-156.93471,-160.17305]}, - {"t":6.63909, "x":2.79665, "y":1.52426, "heading":2.42685, "vx":0.32824, "vy":0.77735, "omega":0.98958, "ax":-3.42948, "ay":-8.27191, "alpha":-10.05314, "fx":[-102.48506,-103.51581,-31.39688,4.05955], "fy":[-123.04393,-122.37184,-157.20979,-160.18559]}, - {"t":6.67061, "x":2.80529, "y":1.54465, "heading":2.45804, "vx":0.22015, "vy":0.51664, "omega":0.67273, "ax":-3.47485, "ay":-8.21663, "alpha":-10.51124, "fx":[-106.30272,-104.45636,-30.47068,4.80524], "fy":[-119.82639,-121.61178,-157.4173,-160.1948]}, - {"t":6.70212, "x":2.8105, "y":1.55686, "heading":2.47924, "vx":0.11063, "vy":0.25767, "omega":0.34144, "ax":-3.50994, "ay":-8.17531, "alpha":-10.83313, "fx":[-108.98493,-105.15408,-29.89695,5.22357], "fy":[-117.44413,-121.0411,-157.54727,-160.20621]}, - {"t":6.73364, "x":2.81225, "y":1.56092, "heading":2.49, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0,0.0,0.0,0.0]}], + {"t":0.0, "x":4.42804, "y":0.39542, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":9.42388, "ay":0.29392, "alpha":0.0, "fx":[160.29751,160.29751,160.29751,160.29751], "fy":[4.99949,4.99949,4.99949,4.99949]}, + {"t":0.04683, "x":4.43837, "y":0.39575, "heading":1.5708, "vx":0.4413, "vy":0.01376, "omega":0.0, "ax":9.42266, "ay":0.28622, "alpha":0.0, "fx":[160.27672,160.27672,160.27672,160.27672], "fy":[4.86855,4.86855,4.86855,4.86855]}, + {"t":0.09366, "x":4.46937, "y":0.3967, "heading":1.5708, "vx":0.88254, "vy":0.02717, "omega":0.0, "ax":9.42086, "ay":0.27855, "alpha":0.0, "fx":[160.24615,160.24615,160.24615,160.24615], "fy":[4.73807,4.73807,4.73807,4.73807]}, + {"t":0.14048, "x":4.52103, "y":0.39828, "heading":1.5708, "vx":1.3237, "vy":0.04021, "omega":0.0, "ax":9.41808, "ay":0.27084, "alpha":0.0, "fx":[160.19884,160.19884,160.19884,160.19884], "fy":[4.60687,4.60687,4.60687,4.60687]}, + {"t":0.18731, "x":4.59334, "y":0.40046, "heading":1.5708, "vx":1.76472, "vy":0.05289, "omega":0.0, "ax":9.41338, "ay":0.26288, "alpha":0.0, "fx":[160.11898,160.11898,160.11898,160.11898], "fy":[4.47152,4.47152,4.47152,4.47152]}, + {"t":0.23414, "x":4.6863, "y":0.40323, "heading":1.5708, "vx":2.20553, "vy":0.0652, "omega":0.0, "ax":9.40411, "ay":0.25405, "alpha":0.0, "fx":[159.96125,159.96125,159.96125,159.96125], "fy":[4.32134,4.32134,4.32134,4.32134]}, + {"t":0.28097, "x":4.79989, "y":0.40656, "heading":1.5708, "vx":2.6459, "vy":0.0771, "omega":0.0, "ax":9.37829, "ay":0.24148, "alpha":0.0, "fx":[159.52205,159.52205,159.52205,159.52205], "fy":[4.10757,4.10757,4.10757,4.10757]}, + {"t":0.32779, "x":4.93407, "y":0.41043, "heading":1.5708, "vx":3.08506, "vy":0.08841, "omega":0.0, "ax":8.99804, "ay":0.14601, "alpha":0.0, "fx":[153.05415,153.05415,153.05415,153.05415], "fy":[2.48366,2.48366,2.48366,2.48366]}, + {"t":0.37462, "x":5.0884, "y":0.41473, "heading":1.5708, "vx":3.50642, "vy":0.09525, "omega":0.0, "ax":0.01515, "ay":-0.17682, "alpha":0.0, "fx":[0.25768,0.25768,0.25768,0.25768], "fy":[-3.00758,-3.00758,-3.00758,-3.00758]}, + {"t":0.42145, "x":5.25262, "y":0.419, "heading":1.5708, "vx":3.50713, "vy":0.08697, "omega":0.0, "ax":0.00223, "ay":-0.09226, "alpha":0.0, "fx":[0.03792,0.03792,0.03792,0.03792], "fy":[-1.56932,-1.56932,-1.56932,-1.56932]}, + {"t":0.46828, "x":5.41685, "y":0.42297, "heading":1.5708, "vx":3.50724, "vy":0.08265, "omega":0.0, "ax":0.00187, "ay":-0.08125, "alpha":0.0, "fx":[0.03177,0.03177,0.03177,0.03177], "fy":[-1.3821,-1.3821,-1.3821,-1.3821]}, + {"t":0.5151, "x":5.58109, "y":0.42675, "heading":1.5708, "vx":3.50732, "vy":0.07884, "omega":0.0, "ax":0.0017, "ay":-0.07729, "alpha":0.0, "fx":[0.02883,0.02883,0.02883,0.02883], "fy":[-1.31474,-1.31474,-1.31474,-1.31474]}, + {"t":0.56193, "x":5.74533, "y":0.43036, "heading":1.5708, "vx":3.5074, "vy":0.07522, "omega":0.0, "ax":0.00156, "ay":-0.07434, "alpha":0.0, "fx":[0.02645,0.02645,0.02645,0.02645], "fy":[-1.2645,-1.2645,-1.2645,-1.2645]}, + {"t":0.60876, "x":5.90958, "y":0.4338, "heading":1.5708, "vx":3.50748, "vy":0.07174, "omega":0.0, "ax":0.00143, "ay":-0.07179, "alpha":0.0, "fx":[0.02436,0.02436,0.02436,0.02436], "fy":[-1.22111,-1.22111,-1.22111,-1.22111]}, + {"t":0.65559, "x":6.07382, "y":0.43708, "heading":1.5708, "vx":3.50754, "vy":0.06838, "omega":0.0, "ax":0.00132, "ay":-0.06954, "alpha":0.0, "fx":[0.02248,0.02248,0.02248,0.02248], "fy":[-1.18279,-1.18279,-1.18279,-1.18279]}, + {"t":0.70241, "x":6.23808, "y":0.44021, "heading":1.5708, "vx":3.5076, "vy":0.06512, "omega":0.0, "ax":0.00122, "ay":-0.06752, "alpha":0.0, "fx":[0.02078,0.02078,0.02078,0.02078], "fy":[-1.14857,-1.14857,-1.14857,-1.14857]}, + {"t":0.74924, "x":6.40233, "y":0.44318, "heading":1.5708, "vx":3.50766, "vy":0.06196, "omega":0.0, "ax":0.00113, "ay":-0.06563, "alpha":0.0, "fx":[0.0192,0.0192,0.0192,0.0192], "fy":[-1.11642,-1.11642,-1.11642,-1.11642]}, + {"t":0.79607, "x":6.56659, "y":0.44601, "heading":1.5708, "vx":3.50771, "vy":0.05889, "omega":0.0, "ax":0.00103, "ay":-0.06289, "alpha":0.0, "fx":[0.01749,0.01749,0.01749,0.01749], "fy":[-1.0698,-1.0698,-1.0698,-1.0698]}, + {"t":0.8429, "x":6.73085, "y":0.4487, "heading":1.5708, "vx":3.50776, "vy":0.05594, "omega":0.0, "ax":0.00076, "ay":-0.04869, "alpha":0.0, "fx":[0.01292,0.01292,0.01292,0.01292], "fy":[-0.82825,-0.82825,-0.82825,-0.82825]}, + {"t":0.88973, "x":6.89511, "y":0.45127, "heading":1.5708, "vx":3.5078, "vy":0.05366, "omega":0.0, "ax":-0.00157, "ay":0.09577, "alpha":0.0, "fx":[-0.02668,-0.02668,-0.02668,-0.02668], "fy":[1.62896,1.62896,1.62896,1.62896]}, + {"t":0.93655, "x":7.05937, "y":0.45388, "heading":1.5708, "vx":3.50772, "vy":0.05815, "omega":0.0, "ax":-0.45777, "ay":1.64085, "alpha":0.0, "fx":[-7.78647,-7.78647,-7.78647,-7.78647], "fy":[27.91031,27.91031,27.91031,27.91031]}, + {"t":0.98338, "x":7.22312, "y":0.45841, "heading":1.5708, "vx":3.48629, "vy":0.13498, "omega":0.0, "ax":-9.36388, "ay":-0.06345, "alpha":0.0, "fx":[-159.27695,-159.27695,-159.27695,-159.27695], "fy":[-1.07929,-1.07929,-1.07929,-1.07929]}, + {"t":1.03021, "x":7.37611, "y":0.46466, "heading":1.5708, "vx":3.0478, "vy":0.13201, "omega":0.0, "ax":-9.40272, "ay":-0.0926, "alpha":0.0, "fx":[-159.93761,-159.93761,-159.93761,-159.93761], "fy":[-1.57509,-1.57509,-1.57509,-1.57509]}, + {"t":1.07704, "x":7.50852, "y":0.47074, "heading":1.5708, "vx":2.60749, "vy":0.12768, "omega":0.0, "ax":-9.41458, "ay":-0.09939, "alpha":0.0, "fx":[-160.13931,-160.13931,-160.13931,-160.13931], "fy":[-1.69061,-1.69061,-1.69061,-1.69061]}, + {"t":1.12386, "x":7.6203, "y":0.47661, "heading":1.5708, "vx":2.16663, "vy":0.12302, "omega":0.0, "ax":-9.42033, "ay":-0.10104, "alpha":0.0, "fx":[-160.23712,-160.23712,-160.23712,-160.23712], "fy":[-1.71869,-1.71869,-1.71869,-1.71869]}, + {"t":1.17069, "x":7.71143, "y":0.48226, "heading":1.5708, "vx":1.7255, "vy":0.11829, "omega":0.0, "ax":-9.42373, "ay":-0.10074, "alpha":0.0, "fx":[-160.29499,-160.29499,-160.29499,-160.29499], "fy":[-1.71353,-1.71353,-1.71353,-1.71353]}, + {"t":1.21752, "x":7.7819, "y":0.48769, "heading":1.5708, "vx":1.28421, "vy":0.11357, "omega":0.0, "ax":-9.42599, "ay":-0.09951, "alpha":0.0, "fx":[-160.33333,-160.33333,-160.33333,-160.33333], "fy":[-1.69259,-1.69259,-1.69259,-1.69259]}, + {"t":1.26435, "x":7.8317, "y":0.4929, "heading":1.5708, "vx":0.84281, "vy":0.10891, "omega":0.0, "ax":-2.01077, "ay":6.29746, "alpha":0.0, "fx":[-34.20271,-34.20271,-34.20271,-34.20271], "fy":[107.11806,107.11806,107.11806,107.11806]}, + {"t":1.31051, "x":7.86847, "y":0.50464, "heading":1.5708, "vx":0.74998, "vy":0.39966, "omega":0.0, "ax":-0.038, "ay":0.07094, "alpha":0.0, "fx":[-0.64637,-0.64637,-0.64637,-0.64637], "fy":[1.20669,1.20669,1.20669,1.20669]}, + {"t":1.35668, "x":7.90306, "y":0.52316, "heading":1.5708, "vx":0.74822, "vy":0.40293, "omega":0.0, "ax":0.00068, "ay":-0.00127, "alpha":0.0, "fx":[0.01159,0.01159,0.01159,0.01159], "fy":[-0.02154,-0.02154,-0.02154,-0.02154]}, + {"t":1.40285, "x":7.9376, "y":0.54176, "heading":1.5708, "vx":0.74825, "vy":0.40287, "omega":0.0, "ax":0.00086, "ay":-0.0016, "alpha":0.0, "fx":[0.01464,0.01464,0.01464,0.01464], "fy":[-0.02719,-0.02719,-0.02719,-0.02719]}, + {"t":1.44902, "x":7.97215, "y":0.56036, "heading":1.5708, "vx":0.74829, "vy":0.4028, "omega":0.0, "ax":0.0008, "ay":-0.00149, "alpha":0.0, "fx":[0.01362,0.01362,0.01362,0.01362], "fy":[-0.02531,-0.02531,-0.02531,-0.02531]}, + {"t":1.49519, "x":8.0067, "y":0.57896, "heading":1.5708, "vx":0.74833, "vy":0.40273, "omega":0.0, "ax":0.00075, "ay":-0.00139, "alpha":0.0, "fx":[0.01273,0.01273,0.01273,0.01273], "fy":[-0.02366,-0.02366,-0.02366,-0.02366]}, + {"t":1.54136, "x":8.04125, "y":0.59755, "heading":1.5708, "vx":0.74836, "vy":0.40267, "omega":0.0, "ax":0.0007, "ay":-0.00131, "alpha":0.0, "fx":[0.01195,0.01195,0.01195,0.01195], "fy":[-0.02223,-0.02223,-0.02223,-0.02223]}, + {"t":1.58752, "x":8.0758, "y":0.61614, "heading":1.5708, "vx":0.7484, "vy":0.40261, "omega":0.0, "ax":0.00066, "ay":-0.00123, "alpha":0.0, "fx":[0.01128,0.01128,0.01128,0.01128], "fy":[-0.02097,-0.02097,-0.02097,-0.02097]}, + {"t":1.63369, "x":8.11035, "y":0.63472, "heading":1.5708, "vx":0.74843, "vy":0.40255, "omega":0.0, "ax":0.00063, "ay":-0.00117, "alpha":0.0, "fx":[0.01068,0.01068,0.01068,0.01068], "fy":[-0.01986,-0.01986,-0.01986,-0.01986]}, + {"t":1.67986, "x":8.1449, "y":0.65331, "heading":1.5708, "vx":0.74846, "vy":0.40249, "omega":0.0, "ax":0.0006, "ay":-0.00111, "alpha":0.0, "fx":[0.01015,0.01015,0.01015,0.01015], "fy":[-0.01887,-0.01887,-0.01887,-0.01887]}, + {"t":1.72603, "x":8.17946, "y":0.67189, "heading":1.5708, "vx":0.74848, "vy":0.40244, "omega":0.0, "ax":0.00057, "ay":-0.00106, "alpha":0.0, "fx":[0.00967,0.00967,0.00967,0.00967], "fy":[-0.01798,-0.01798,-0.01798,-0.01798]}, + {"t":1.7722, "x":8.21402, "y":0.69047, "heading":1.5708, "vx":0.74851, "vy":0.40239, "omega":0.0, "ax":0.00054, "ay":-0.00101, "alpha":0.0, "fx":[0.00923,0.00923,0.00923,0.00923], "fy":[-0.01718,-0.01718,-0.01718,-0.01718]}, + {"t":1.81837, "x":8.24858, "y":0.70905, "heading":1.5708, "vx":0.74854, "vy":0.40235, "omega":0.0, "ax":0.00052, "ay":-0.00097, "alpha":0.0, "fx":[0.00884,0.00884,0.00884,0.00884], "fy":[-0.01645,-0.01645,-0.01645,-0.01645]}, + {"t":1.86453, "x":8.28313, "y":0.72762, "heading":1.5708, "vx":0.74856, "vy":0.4023, "omega":0.0, "ax":0.0005, "ay":-0.00093, "alpha":0.0, "fx":[0.00848,0.00848,0.00848,0.00848], "fy":[-0.01578,-0.01578,-0.01578,-0.01578]}, + {"t":1.9107, "x":8.31769, "y":0.74619, "heading":1.5708, "vx":0.74858, "vy":0.40226, "omega":0.0, "ax":0.00048, "ay":-0.00089, "alpha":0.0, "fx":[0.00815,0.00815,0.00815,0.00815], "fy":[-0.01516,-0.01516,-0.01516,-0.01516]}, + {"t":1.95687, "x":8.35226, "y":0.76476, "heading":1.5708, "vx":0.7486, "vy":0.40222, "omega":0.0, "ax":0.00046, "ay":-0.00086, "alpha":0.0, "fx":[0.00784,0.00784,0.00784,0.00784], "fy":[-0.01459,-0.01459,-0.01459,-0.01459]}, + {"t":2.00304, "x":8.38682, "y":0.78333, "heading":1.5708, "vx":0.74863, "vy":0.40218, "omega":0.0, "ax":0.00043, "ay":-0.0008, "alpha":0.0, "fx":[0.00727,0.00727,0.00727,0.00727], "fy":[-0.01353,-0.01353,-0.01353,-0.01353]}, + {"t":2.04921, "x":8.42138, "y":0.8019, "heading":1.5708, "vx":0.74865, "vy":0.40214, "omega":0.0, "ax":-0.00222, "ay":0.00412, "alpha":0.0, "fx":[-0.03768,-0.03768,-0.03768,-0.03768], "fy":[0.07012,0.07012,0.07012,0.07012]}, + {"t":2.09538, "x":8.45594, "y":0.82047, "heading":1.5708, "vx":0.74854, "vy":0.40233, "omega":0.0, "ax":-0.42225, "ay":0.74354, "alpha":0.0, "fx":[-7.18236,-7.18236,-7.18236,-7.18236], "fy":[12.64736,12.64736,12.64736,12.64736]}, + {"t":2.14154, "x":8.49005, "y":0.83984, "heading":1.5708, "vx":0.72905, "vy":0.43666, "omega":0.0, "ax":-6.47656, "ay":6.41614, "alpha":0.0, "fx":[-110.16437,-110.16437,-110.16437,-110.16437], "fy":[109.13668,109.13668,109.13668,109.13668]}, + {"t":2.18771, "x":8.51681, "y":0.86684, "heading":1.5708, "vx":0.43004, "vy":0.73288, "omega":0.0, "ax":-9.06625, "ay":2.52173, "alpha":0.0, "fx":[-154.21424,-154.21424,-154.21424,-154.21424], "fy":[42.89384,42.89384,42.89384,42.89384]}, + {"t":2.23388, "x":8.527, "y":0.90336, "heading":1.5708, "vx":0.01146, "vy":0.84931, "omega":0.0, "ax":-0.4706, "ay":0.00922, "alpha":0.0, "fx":[-8.00477,-8.00477,-8.00477,-8.00477], "fy":[0.15675,0.15675,0.15675,0.15675]}, + {"t":2.28189, "x":8.52701, "y":0.94414, "heading":1.5708, "vx":-0.01113, "vy":0.84975, "omega":0.0, "ax":0.44973, "ay":0.00018, "alpha":0.0, "fx":[7.64977,7.64977,7.64977,7.64977], "fy":[0.003,0.003,0.003,0.003]}, + {"t":2.3299, "x":8.52699, "y":0.98494, "heading":1.5708, "vx":0.01046, "vy":0.84976, "omega":0.0, "ax":-0.42196, "ay":0.00017, "alpha":0.0, "fx":[-7.17736,-7.17736,-7.17736,-7.17736], "fy":[0.00281,0.00281,0.00281,0.00281]}, + {"t":2.37791, "x":8.52701, "y":1.02573, "heading":1.5708, "vx":-0.0098, "vy":0.84977, "omega":0.0, "ax":0.39426, "ay":0.00015, "alpha":0.0, "fx":[6.7063,6.7063,6.7063,6.7063], "fy":[0.00262,0.00262,0.00262,0.00262]}, + {"t":2.42591, "x":8.52699, "y":1.06653, "heading":1.5708, "vx":0.00913, "vy":0.84977, "omega":0.0, "ax":-0.36665, "ay":0.00014, "alpha":0.0, "fx":[-6.23653,-6.23653,-6.23653,-6.23653], "fy":[0.00243,0.00243,0.00243,0.00243]}, + {"t":2.47392, "x":8.52701, "y":1.10733, "heading":1.5708, "vx":-0.00847, "vy":0.84978, "omega":0.0, "ax":0.3391, "ay":0.00013, "alpha":0.0, "fx":[5.76798,5.76798,5.76798,5.76798], "fy":[0.00224,0.00224,0.00224,0.00224]}, + {"t":2.52193, "x":8.52699, "y":1.14812, "heading":1.5708, "vx":0.00781, "vy":0.84979, "omega":0.0, "ax":-0.31162, "ay":0.00012, "alpha":0.0, "fx":[-5.30059,-5.30059,-5.30059,-5.30059], "fy":[0.00205,0.00205,0.00205,0.00205]}, + {"t":2.56994, "x":8.52701, "y":1.18892, "heading":1.5708, "vx":-0.00715, "vy":0.84979, "omega":0.0, "ax":0.28421, "ay":0.00011, "alpha":0.0, "fx":[4.83427,4.83427,4.83427,4.83427], "fy":[0.00187,0.00187,0.00187,0.00187]}, + {"t":2.61795, "x":8.52699, "y":1.22972, "heading":1.5708, "vx":0.00649, "vy":0.8498, "omega":0.0, "ax":-0.25685, "ay":0.0001, "alpha":0.0, "fx":[-4.36895,-4.36895,-4.36895,-4.36895], "fy":[0.00168,0.00168,0.00168,0.00168]}, + {"t":2.66595, "x":8.52701, "y":1.27051, "heading":1.5708, "vx":-0.00584, "vy":0.8498, "omega":0.0, "ax":0.22955, "ay":0.00009, "alpha":0.0, "fx":[3.90455,3.90455,3.90455,3.90455], "fy":[0.0015,0.0015,0.0015,0.0015]}, + {"t":2.71396, "x":8.52699, "y":1.31131, "heading":1.5708, "vx":0.00518, "vy":0.84981, "omega":0.0, "ax":-0.20229, "ay":0.00008, "alpha":0.0, "fx":[-3.44097,-3.44097,-3.44097,-3.44097], "fy":[0.00132,0.00132,0.00132,0.00132]}, + {"t":2.76197, "x":8.52701, "y":1.35211, "heading":1.5708, "vx":-0.00453, "vy":0.84981, "omega":0.0, "ax":0.17508, "ay":0.00007, "alpha":0.0, "fx":[2.97814,2.97814,2.97814,2.97814], "fy":[0.00114,0.00114,0.00114,0.00114]}, + {"t":2.80998, "x":8.52699, "y":1.39291, "heading":1.5708, "vx":0.00388, "vy":0.84981, "omega":0.0, "ax":-0.14791, "ay":0.00006, "alpha":0.0, "fx":[-2.51596,-2.51596,-2.51596,-2.51596], "fy":[0.00096,0.00096,0.00096,0.00096]}, + {"t":2.85799, "x":8.52701, "y":1.4337, "heading":1.5708, "vx":-0.00322, "vy":0.84982, "omega":0.0, "ax":0.12077, "ay":0.00005, "alpha":0.0, "fx":[2.05433,2.05433,2.05433,2.05433], "fy":[0.00079,0.00079,0.00079,0.00079]}, + {"t":2.90599, "x":8.52699, "y":1.4745, "heading":1.5708, "vx":0.00257, "vy":0.84982, "omega":0.0, "ax":-0.09366, "ay":0.00004, "alpha":0.0, "fx":[-1.59315,-1.59315,-1.59315,-1.59315], "fy":[0.00061,0.00061,0.00061,0.00061]}, + {"t":2.954, "x":8.52701, "y":1.5153, "heading":1.5708, "vx":-0.00192, "vy":0.84982, "omega":0.0, "ax":0.06657, "ay":0.00003, "alpha":0.0, "fx":[1.13233,1.13233,1.13233,1.13233], "fy":[0.00043,0.00043,0.00043,0.00043]}, + {"t":3.00201, "x":8.52699, "y":1.5561, "heading":1.5708, "vx":0.00127, "vy":0.84982, "omega":0.0, "ax":-0.03949, "ay":0.00002, "alpha":0.0, "fx":[-0.67176,-0.67176,-0.67176,-0.67176], "fy":[0.00026,0.00026,0.00026,0.00026]}, + {"t":3.05002, "x":8.52701, "y":1.5969, "heading":1.5708, "vx":-0.00062, "vy":0.84982, "omega":0.0, "ax":0.01242, "ay":0.0, "alpha":0.0, "fx":[0.21134,0.21134,0.21134,0.21134], "fy":[0.00008,0.00008,0.00008,0.00008]}, + {"t":3.09803, "x":8.52699, "y":1.63769, "heading":1.5708, "vx":-0.00003, "vy":0.84982, "omega":0.0, "ax":0.01464, "ay":-0.00001, "alpha":0.0, "fx":[0.24903,0.24903,0.24903,0.24903], "fy":[-0.0001,-0.0001,-0.0001,-0.0001]}, + {"t":3.14603, "x":8.52701, "y":1.67849, "heading":1.5708, "vx":0.00068, "vy":0.84982, "omega":0.0, "ax":-0.04171, "ay":-0.00002, "alpha":0.0, "fx":[-0.70946,-0.70946,-0.70946,-0.70946], "fy":[-0.00027,-0.00027,-0.00027,-0.00027]}, + {"t":3.19404, "x":8.52699, "y":1.71929, "heading":1.5708, "vx":-0.00133, "vy":0.84982, "omega":0.0, "ax":0.06879, "ay":-0.00003, "alpha":0.0, "fx":[1.17004,1.17004,1.17004,1.17004], "fy":[-0.00045,-0.00045,-0.00045,-0.00045]}, + {"t":3.24205, "x":8.52701, "y":1.76009, "heading":1.5708, "vx":0.00198, "vy":0.84982, "omega":0.0, "ax":-0.09588, "ay":-0.00004, "alpha":0.0, "fx":[-1.63089,-1.63089,-1.63089,-1.63089], "fy":[-0.00063,-0.00063,-0.00063,-0.00063]}, + {"t":3.29006, "x":8.52699, "y":1.80089, "heading":1.5708, "vx":-0.00263, "vy":0.84982, "omega":0.0, "ax":0.12299, "ay":-0.00005, "alpha":0.0, "fx":[2.0921,2.0921,2.0921,2.0921], "fy":[-0.0008,-0.0008,-0.0008,-0.0008]}, + {"t":3.33807, "x":8.52701, "y":1.84168, "heading":1.5708, "vx":0.00328, "vy":0.84982, "omega":0.0, "ax":-0.15014, "ay":-0.00006, "alpha":0.0, "fx":[-2.55377,-2.55377,-2.55377,-2.55377], "fy":[-0.00098,-0.00098,-0.00098,-0.00098]}, + {"t":3.38607, "x":8.52699, "y":1.88248, "heading":1.5708, "vx":-0.00393, "vy":0.84981, "omega":0.0, "ax":0.17731, "ay":-0.00007, "alpha":0.0, "fx":[3.016,3.016,3.016,3.016], "fy":[-0.00116,-0.00116,-0.00116,-0.00116]}, + {"t":3.43408, "x":8.52701, "y":1.92328, "heading":1.5708, "vx":0.00458, "vy":0.84981, "omega":0.0, "ax":-0.20452, "ay":-0.00008, "alpha":0.0, "fx":[-3.47889,-3.47889,-3.47889,-3.47889], "fy":[-0.00134,-0.00134,-0.00134,-0.00134]}, + {"t":3.48209, "x":8.52699, "y":1.96408, "heading":1.5708, "vx":-0.00524, "vy":0.84981, "omega":0.0, "ax":0.23178, "ay":-0.00009, "alpha":0.0, "fx":[3.94253,3.94253,3.94253,3.94253], "fy":[-0.00152,-0.00152,-0.00152,-0.00152]}, + {"t":3.5301, "x":8.52701, "y":2.00488, "heading":1.5708, "vx":0.00589, "vy":0.8498, "omega":0.0, "ax":-0.25909, "ay":-0.0001, "alpha":0.0, "fx":[-4.407,-4.407,-4.407,-4.407], "fy":[-0.0017,-0.0017,-0.0017,-0.0017]}, + {"t":3.57811, "x":8.52699, "y":2.04567, "heading":1.5708, "vx":-0.00655, "vy":0.8498, "omega":0.0, "ax":0.28645, "ay":-0.00011, "alpha":0.0, "fx":[4.8724,4.8724,4.8724,4.8724], "fy":[-0.00189,-0.00189,-0.00189,-0.00189]}, + {"t":3.62611, "x":8.52701, "y":2.08647, "heading":1.5708, "vx":0.0072, "vy":0.84979, "omega":0.0, "ax":-0.31387, "ay":-0.00012, "alpha":0.0, "fx":[-5.3388,-5.3388,-5.3388,-5.3388], "fy":[-0.00207,-0.00207,-0.00207,-0.00207]}, + {"t":3.67412, "x":8.52699, "y":2.12727, "heading":1.5708, "vx":-0.00786, "vy":0.84979, "omega":0.0, "ax":0.34135, "ay":-0.00013, "alpha":0.0, "fx":[5.80629,5.80629,5.80629,5.80629], "fy":[-0.00226,-0.00226,-0.00226,-0.00226]}, + {"t":3.72213, "x":8.52701, "y":2.16806, "heading":1.5708, "vx":0.00852, "vy":0.84978, "omega":0.0, "ax":-0.3689, "ay":-0.00014, "alpha":0.0, "fx":[-6.27493,-6.27493,-6.27493,-6.27493], "fy":[-0.00245,-0.00245,-0.00245,-0.00245]}, + {"t":3.77014, "x":8.52699, "y":2.20886, "heading":1.5708, "vx":-0.00919, "vy":0.84977, "omega":0.0, "ax":0.39653, "ay":-0.00015, "alpha":0.0, "fx":[6.7448,6.7448,6.7448,6.7448], "fy":[-0.00264,-0.00264,-0.00264,-0.00264]}, + {"t":3.81815, "x":8.52701, "y":2.24965, "heading":1.5708, "vx":0.00985, "vy":0.84977, "omega":0.0, "ax":-0.42423, "ay":-0.00017, "alpha":0.0, "fx":[-7.21597,-7.21597,-7.21597,-7.21597], "fy":[-0.00283,-0.00283,-0.00283,-0.00283]}, + {"t":3.86615, "x":8.52699, "y":2.29045, "heading":1.5708, "vx":-0.01052, "vy":0.84976, "omega":0.0, "ax":0.45201, "ay":-0.00018, "alpha":0.0, "fx":[7.68848,7.68848,7.68848,7.68848], "fy":[-0.00302,-0.00302,-0.00302,-0.00302]}, + {"t":3.91416, "x":8.52701, "y":2.33124, "heading":1.5708, "vx":0.01118, "vy":0.84975, "omega":0.0, "ax":-0.47987, "ay":-0.00019, "alpha":0.0, "fx":[-8.1624,-8.1624,-8.1624,-8.1624], "fy":[-0.00322,-0.00322,-0.00322,-0.00322]}, + {"t":3.96217, "x":8.52699, "y":2.37204, "heading":1.5708, "vx":-0.01185, "vy":0.84974, "omega":0.0, "ax":0.50781, "ay":-0.0002, "alpha":0.0, "fx":[8.63779,8.63779,8.63779,8.63779], "fy":[-0.00341,-0.00341,-0.00341,-0.00341]}, + {"t":4.01018, "x":8.52701, "y":2.41283, "heading":1.5708, "vx":0.01253, "vy":0.84973, "omega":0.0, "ax":-0.53585, "ay":-0.00021, "alpha":0.0, "fx":[-9.11467,-9.11467,-9.11467,-9.11467], "fy":[-0.00362,-0.00362,-0.00362,-0.00362]}, + {"t":4.05819, "x":8.52699, "y":2.45363, "heading":1.5708, "vx":-0.0132, "vy":0.84972, "omega":0.0, "ax":0.56398, "ay":-0.00022, "alpha":0.0, "fx":[9.59311,9.59311,9.59311,9.59311], "fy":[-0.00382,-0.00382,-0.00382,-0.00382]}, + {"t":4.10619, "x":8.52701, "y":2.49442, "heading":1.5708, "vx":0.01388, "vy":0.84971, "omega":0.0, "ax":-0.5922, "ay":-0.00024, "alpha":0.0, "fx":[-10.07312,-10.07312,-10.07312,-10.07312], "fy":[-0.00402,-0.00402,-0.00402,-0.00402]}, + {"t":4.1542, "x":8.52699, "y":2.53521, "heading":1.5708, "vx":-0.01455, "vy":0.8497, "omega":0.0, "ax":0.62051, "ay":-0.00025, "alpha":0.0, "fx":[10.55476,10.55476,10.55476,10.55476], "fy":[-0.00423,-0.00423,-0.00423,-0.00423]}, + {"t":4.20221, "x":8.52701, "y":2.576, "heading":1.5708, "vx":0.01524, "vy":0.84969, "omega":0.0, "ax":-0.64893, "ay":-0.00026, "alpha":0.0, "fx":[-11.03803,-11.03803,-11.03803,-11.03803], "fy":[-0.00444,-0.00444,-0.00444,-0.00444]}, + {"t":4.25022, "x":8.52699, "y":2.6168, "heading":1.5708, "vx":-0.01592, "vy":0.84967, "omega":0.0, "ax":0.67743, "ay":-0.00027, "alpha":0.0, "fx":[11.52297,11.52297,11.52297,11.52297], "fy":[-0.00465,-0.00465,-0.00465,-0.00465]}, + {"t":4.29823, "x":8.52701, "y":2.65759, "heading":1.5708, "vx":0.0166, "vy":0.84966, "omega":0.0, "ax":-0.70604, "ay":-0.00029, "alpha":0.0, "fx":[-12.0096,-12.0096,-12.0096,-12.0096], "fy":[-0.00486,-0.00486,-0.00486,-0.00486]}, + {"t":4.34623, "x":8.52699, "y":2.69838, "heading":1.5708, "vx":-0.01729, "vy":0.84965, "omega":0.0, "ax":0.73475, "ay":-0.0003, "alpha":0.0, "fx":[12.49794,12.49794,12.49794,12.49794], "fy":[-0.00508,-0.00508,-0.00508,-0.00508]}, + {"t":4.39424, "x":8.52701, "y":2.73917, "heading":1.5708, "vx":0.01798, "vy":0.84963, "omega":0.0, "ax":-0.76356, "ay":-0.00031, "alpha":0.0, "fx":[-12.98798,-12.98798,-12.98798,-12.98798], "fy":[-0.00529,-0.00529,-0.00529,-0.00529]}, + {"t":4.44225, "x":8.52699, "y":2.77996, "heading":1.5708, "vx":-0.01867, "vy":0.84962, "omega":0.0, "ax":0.79247, "ay":-0.00032, "alpha":0.0, "fx":[13.47975,13.47975,13.47975,13.47975], "fy":[-0.00551,-0.00551,-0.00551,-0.00551]}, + {"t":4.49026, "x":8.52701, "y":2.82074, "heading":1.5708, "vx":0.01937, "vy":0.8496, "omega":0.0, "ax":-0.82149, "ay":-0.00034, "alpha":0.0, "fx":[-13.97324,-13.97324,-13.97324,-13.97324], "fy":[-0.00574,-0.00574,-0.00574,-0.00574]}, + {"t":4.53827, "x":8.52699, "y":2.86153, "heading":1.5708, "vx":-0.02007, "vy":0.84959, "omega":0.0, "ax":0.8506, "ay":-0.00035, "alpha":0.0, "fx":[14.46846,14.46846,14.46846,14.46846], "fy":[-0.00596,-0.00596,-0.00596,-0.00596]}, + {"t":4.58627, "x":8.52701, "y":2.90232, "heading":1.5708, "vx":0.02077, "vy":0.84957, "omega":0.0, "ax":-0.87982, "ay":-0.00036, "alpha":0.0, "fx":[-14.96541,-14.96541,-14.96541,-14.96541], "fy":[-0.00619,-0.00619,-0.00619,-0.00619]}, + {"t":4.63428, "x":8.52699, "y":2.9431, "heading":1.5708, "vx":-0.02147, "vy":0.84955, "omega":0.0, "ax":0.90916, "ay":-0.03236, "alpha":0.0, "fx":[15.46458,15.46458,15.46458,15.46458], "fy":[-0.55051,-0.55051,-0.55051,-0.55051]}, + {"t":4.68229, "x":8.52701, "y":2.98385, "heading":1.5708, "vx":0.02218, "vy":0.848, "omega":0.0, "ax":-0.93126, "ay":-9.37484, "alpha":0.0, "fx":[-15.84046,-15.84046,-15.84046,-15.84046], "fy":[-159.46339,-159.46339,-159.46339,-159.46339]}, + {"t":4.7303, "x":8.527, "y":3.01376, "heading":1.5708, "vx":-0.02253, "vy":0.39793, "omega":0.0, "ax":-6.79187, "ay":-6.5392, "alpha":0.0, "fx":[-115.52782,-115.52782,-115.52782,-115.52782], "fy":[-111.22988,-111.22988,-111.22988,-111.22988]}, + {"t":4.77066, "x":8.52056, "y":3.02449, "heading":1.5708, "vx":-0.29665, "vy":0.13401, "omega":0.0, "ax":-6.78699, "ay":-6.54264, "alpha":0.0, "fx":[-115.4447,-115.4447,-115.4447,-115.4447], "fy":[-111.28835,-111.28835,-111.28835,-111.28835]}, + {"t":4.81102, "x":8.50306, "y":3.02457, "heading":1.5708, "vx":-0.57057, "vy":-0.13005, "omega":0.0, "ax":-6.78075, "ay":-6.54701, "alpha":0.0, "fx":[-115.33854,-115.33854,-115.33854,-115.33854], "fy":[-111.36281,-111.36281,-111.36281,-111.36281]}, + {"t":4.85138, "x":8.47451, "y":3.01399, "heading":1.5708, "vx":-0.84424, "vy":-0.39428, "omega":0.0, "ax":-6.77249, "ay":-6.55278, "alpha":0.0, "fx":[-115.19814,-115.19814,-115.19814,-115.19814], "fy":[-111.46096,-111.46096,-111.46096,-111.46096]}, + {"t":4.89174, "x":8.43492, "y":2.99274, "heading":1.5708, "vx":-1.11757, "vy":-0.65875, "omega":0.0, "ax":-6.76105, "ay":-6.56074, "alpha":0.0, "fx":[-115.00359,-115.00359,-115.00359,-115.00359], "fy":[-111.59638,-111.59638,-111.59638,-111.59638]}, + {"t":4.9321, "x":8.38431, "y":2.96081, "heading":1.5708, "vx":-1.39045, "vy":-0.92354, "omega":0.0, "ax":-6.74413, "ay":-6.57246, "alpha":0.0, "fx":[-114.71572,-114.71572,-114.71572,-114.71572], "fy":[-111.79565,-111.79565,-111.79565,-111.79565]}, + {"t":4.97246, "x":8.3227, "y":2.91818, "heading":1.5708, "vx":-1.66264, "vy":-1.1888, "omega":0.0, "ax":-6.71649, "ay":-6.59144, "alpha":0.0, "fx":[-114.24556,-114.24556,-114.24556,-114.24556], "fy":[-112.11855,-112.11855,-112.11855,-112.11855]}, + {"t":5.01281, "x":8.25012, "y":2.86484, "heading":1.5708, "vx":-1.93371, "vy":-1.45483, "omega":0.0, "ax":-6.66313, "ay":-6.62761, "alpha":0.0, "fx":[-113.33789,-113.33789,-113.33789,-113.33789], "fy":[-112.73383,-112.73383,-112.73383,-112.73383]}, + {"t":5.05317, "x":8.16665, "y":2.80072, "heading":1.5708, "vx":-2.20264, "vy":-1.72232, "omega":0.0, "ax":-6.51641, "ay":-6.72415, "alpha":0.0, "fx":[-110.84234,-110.84234,-110.84234,-110.84234], "fy":[-114.37593,-114.37593,-114.37593,-114.37593]}, + {"t":5.09353, "x":8.07245, "y":2.72573, "heading":1.5708, "vx":-2.46564, "vy":-1.99371, "omega":0.0, "ax":-4.36966, "ay":-7.75321, "alpha":0.0, "fx":[-74.32662,-74.32662,-74.32662,-74.32662], "fy":[-131.87994,-131.87994,-131.87994,-131.87994]}, + {"t":5.13389, "x":7.96938, "y":2.63895, "heading":1.5708, "vx":-2.64199, "vy":-2.30662, "omega":0.0, "ax":2.18285, "ay":-2.4285, "alpha":0.0, "fx":[37.12969,37.12969,37.12969,37.12969], "fy":[-41.30804,-41.30804,-41.30804,-41.30804]}, + {"t":5.17425, "x":7.86452, "y":2.54388, "heading":1.5708, "vx":-2.55389, "vy":-2.40464, "omega":0.0, "ax":0.32442, "ay":-0.34271, "alpha":0.0, "fx":[5.51829,5.51829,5.51829,5.51829], "fy":[-5.82933,-5.82933,-5.82933,-5.82933]}, + {"t":5.21461, "x":7.76171, "y":2.44655, "heading":1.5708, "vx":-2.5408, "vy":-2.41847, "omega":0.0, "ax":0.09096, "ay":-0.04612, "alpha":0.0, "fx":[1.54725,1.54725,1.54725,1.54725], "fy":[-0.78448,-0.78448,-0.78448,-0.78448]}, + {"t":5.25497, "x":7.65924, "y":2.3489, "heading":1.5708, "vx":-2.53713, "vy":-2.42033, "omega":0.0, "ax":6.63257, "ay":6.30324, "alpha":0.0, "fx":[112.81806,112.81806,112.81806,112.81806], "fy":[107.21626,107.21626,107.21626,107.21626]}, + {"t":5.29533, "x":7.56225, "y":2.25635, "heading":1.5708, "vx":-2.26944, "vy":-2.16593, "omega":0.0, "ax":6.78562, "ay":6.4643, "alpha":0.0, "fx":[115.42149,115.42149,115.42149,115.42149], "fy":[109.95587,109.95587,109.95587,109.95587]}, + {"t":5.33569, "x":7.47618, "y":2.1742, "heading":1.5708, "vx":-1.99558, "vy":-1.90504, "omega":0.0, "ax":6.80525, "ay":6.4852, "alpha":0.0, "fx":[115.75543,115.75543,115.75543,115.75543], "fy":[110.31135,110.31135,110.31135,110.31135]}, + {"t":5.37605, "x":7.40118, "y":2.1026, "heading":1.5708, "vx":-1.72092, "vy":-1.64329, "omega":0.0, "ax":6.81289, "ay":6.49347, "alpha":0.0, "fx":[115.88523,115.88523,115.88523,115.88523], "fy":[110.45214,110.45214,110.45214,110.45214]}, + {"t":5.41641, "x":7.33727, "y":2.04156, "heading":1.5708, "vx":-1.44595, "vy":-1.38122, "omega":0.0, "ax":0.12998, "ay":-0.13199, "alpha":0.0, "fx":[2.21086,2.21086,2.21086,2.21086], "fy":[-2.24514,-2.24514,-2.24514,-2.24514]}, + {"t":5.45923, "x":7.27549, "y":1.98231, "heading":1.5708, "vx":-1.44039, "vy":-1.38687, "omega":0.0, "ax":0.00615, "ay":-0.00638, "alpha":0.0, "fx":[0.10454,0.10454,0.10454,0.10454], "fy":[-0.10856,-0.10856,-0.10856,-0.10856]}, + {"t":5.50204, "x":7.21382, "y":1.92293, "heading":1.5708, "vx":-1.44012, "vy":-1.38714, "omega":0.0, "ax":0.00104, "ay":-0.00108, "alpha":0.0, "fx":[0.01771,0.01771,0.01771,0.01771], "fy":[-0.0184,-0.0184,-0.0184,-0.0184]}, + {"t":5.54485, "x":7.15217, "y":1.86354, "heading":1.5708, "vx":-1.44008, "vy":-1.38719, "omega":0.0, "ax":0.00087, "ay":-0.0009, "alpha":0.0, "fx":[0.01477,0.01477,0.01477,0.01477], "fy":[-0.01534,-0.01534,-0.01534,-0.01534]}, + {"t":5.58767, "x":7.09051, "y":1.80414, "heading":1.5708, "vx":-1.44004, "vy":-1.38723, "omega":0.0, "ax":0.0009, "ay":-0.00094, "alpha":0.0, "fx":[0.01539,0.01539,0.01539,0.01539], "fy":[-0.01599,-0.01599,-0.01599,-0.01599]}, + {"t":5.63048, "x":7.02886, "y":1.74475, "heading":1.5708, "vx":-1.44, "vy":-1.38727, "omega":0.0, "ax":0.00095, "ay":-0.00099, "alpha":0.0, "fx":[0.01623,0.01623,0.01623,0.01623], "fy":[-0.01685,-0.01685,-0.01685,-0.01685]}, + {"t":5.67329, "x":6.96721, "y":1.68536, "heading":1.5708, "vx":-1.43996, "vy":-1.38731, "omega":0.0, "ax":0.00101, "ay":-0.00105, "alpha":0.0, "fx":[0.01713,0.01713,0.01713,0.01713], "fy":[-0.01779,-0.01779,-0.01779,-0.01779]}, + {"t":5.71611, "x":6.90556, "y":1.62596, "heading":1.5708, "vx":-1.43992, "vy":-1.38736, "omega":0.0, "ax":0.00106, "ay":-0.00111, "alpha":0.0, "fx":[0.01811,0.01811,0.01811,0.01811], "fy":[-0.01881,-0.01881,-0.01881,-0.01881]}, + {"t":5.75892, "x":6.84391, "y":1.56656, "heading":1.5708, "vx":-1.43987, "vy":-1.3874, "omega":0.0, "ax":0.00113, "ay":-0.00117, "alpha":0.0, "fx":[0.01918,0.01918,0.01918,0.01918], "fy":[-0.01992,-0.01992,-0.01992,-0.01992]}, + {"t":5.80173, "x":6.78227, "y":1.50716, "heading":1.5708, "vx":-1.43983, "vy":-1.38745, "omega":0.0, "ax":0.0012, "ay":-0.00124, "alpha":0.0, "fx":[0.02035,0.02035,0.02035,0.02035], "fy":[-0.02112,-0.02112,-0.02112,-0.02112]}, + {"t":5.84455, "x":6.72062, "y":1.44776, "heading":1.5708, "vx":-1.43978, "vy":-1.38751, "omega":0.0, "ax":0.00127, "ay":-0.00132, "alpha":0.0, "fx":[0.02162,0.02162,0.02162,0.02162], "fy":[-0.02245,-0.02245,-0.02245,-0.02245]}, + {"t":5.88736, "x":6.65898, "y":1.38835, "heading":1.5708, "vx":-1.43972, "vy":-1.38756, "omega":0.0, "ax":0.00135, "ay":-0.00141, "alpha":0.0, "fx":[0.02303,0.02303,0.02303,0.02303], "fy":[-0.02391,-0.02391,-0.02391,-0.02391]}, + {"t":5.93018, "x":6.59735, "y":1.32894, "heading":1.5708, "vx":-1.43966, "vy":-1.38762, "omega":0.0, "ax":0.00145, "ay":-0.0015, "alpha":0.0, "fx":[0.0246,0.0246,0.0246,0.0246], "fy":[-0.02553,-0.02553,-0.02553,-0.02553]}, + {"t":5.97299, "x":6.53571, "y":1.26953, "heading":1.5708, "vx":-1.4396, "vy":-1.38769, "omega":0.0, "ax":0.00155, "ay":-0.00161, "alpha":0.0, "fx":[0.02634,0.02634,0.02634,0.02634], "fy":[-0.02734,-0.02734,-0.02734,-0.02734]}, + {"t":6.0158, "x":6.47408, "y":1.21012, "heading":1.5708, "vx":-1.43953, "vy":-1.38776, "omega":0.0, "ax":0.00166, "ay":-0.00173, "alpha":0.0, "fx":[0.0283,0.0283,0.0283,0.0283], "fy":[-0.02937,-0.02937,-0.02937,-0.02937]}, + {"t":6.05862, "x":6.41245, "y":1.1507, "heading":1.5708, "vx":-1.43946, "vy":-1.38783, "omega":0.0, "ax":0.00179, "ay":-0.00186, "alpha":0.0, "fx":[0.03052,0.03052,0.03052,0.03052], "fy":[-0.03167,-0.03167,-0.03167,-0.03167]}, + {"t":6.10143, "x":6.35082, "y":1.09128, "heading":1.5708, "vx":-1.43939, "vy":-1.38791, "omega":0.0, "ax":0.00194, "ay":-0.00202, "alpha":0.0, "fx":[0.03307,0.03307,0.03307,0.03307], "fy":[-0.03431,-0.03431,-0.03431,-0.03431]}, + {"t":6.14424, "x":6.2892, "y":1.03186, "heading":1.5708, "vx":-1.4393, "vy":-1.388, "omega":0.0, "ax":0.00212, "ay":-0.0022, "alpha":0.0, "fx":[0.03601,0.03601,0.03601,0.03601], "fy":[-0.03736,-0.03736,-0.03736,-0.03736]}, + {"t":6.18706, "x":6.22758, "y":0.97243, "heading":1.5708, "vx":-1.43921, "vy":-1.38809, "omega":0.0, "ax":0.00232, "ay":-0.00241, "alpha":0.0, "fx":[0.03947,0.03947,0.03947,0.03947], "fy":[-0.04094,-0.04094,-0.04094,-0.04094]}, + {"t":6.22987, "x":6.16596, "y":0.913, "heading":1.5708, "vx":-1.43911, "vy":-1.38819, "omega":0.0, "ax":0.00256, "ay":-0.00266, "alpha":0.0, "fx":[0.0436,0.0436,0.0436,0.0436], "fy":[-0.04522,-0.04522,-0.04522,-0.04522]}, + {"t":6.27268, "x":6.10435, "y":0.85356, "heading":1.5708, "vx":-1.439, "vy":-1.38831, "omega":0.0, "ax":0.00286, "ay":-0.00296, "alpha":0.0, "fx":[0.04863,0.04863,0.04863,0.04863], "fy":[-0.05043,-0.05043,-0.05043,-0.05043]}, + {"t":6.3155, "x":6.04274, "y":0.79412, "heading":1.5708, "vx":-1.43888, "vy":-1.38844, "omega":0.0, "ax":0.00322, "ay":-0.00334, "alpha":0.0, "fx":[0.05485,0.05485,0.05485,0.05485], "fy":[-0.05687,-0.05687,-0.05687,-0.05687]}, + {"t":6.35831, "x":5.98114, "y":0.73468, "heading":1.5708, "vx":-1.43874, "vy":-1.38858, "omega":0.0, "ax":0.00358, "ay":-0.00371, "alpha":0.0, "fx":[0.06096,0.06096,0.06096,0.06096], "fy":[-0.06318,-0.06318,-0.06318,-0.06318]}, + {"t":6.40113, "x":5.91955, "y":0.67522, "heading":1.5708, "vx":-1.43859, "vy":-1.38874, "omega":0.0, "ax":0.00132, "ay":-0.00137, "alpha":0.0, "fx":[0.02247,0.02247,0.02247,0.02247], "fy":[-0.02332,-0.02332,-0.02332,-0.02332]}, + {"t":6.44394, "x":5.85796, "y":0.61576, "heading":1.5708, "vx":-1.43853, "vy":-1.3888, "omega":0.0, "ax":-0.06719, "ay":0.06974, "alpha":0.0, "fx":[-1.14291,-1.14291,-1.14291,-1.14291], "fy":[1.18627,1.18627,1.18627,1.18627]}, + {"t":6.48675, "x":5.79631, "y":0.55637, "heading":1.5708, "vx":-1.44141, "vy":-1.38581, "omega":0.0, "ax":-1.57267, "ay":1.71976, "alpha":0.0, "fx":[-26.75071,-26.75071,-26.75071,-26.75071], "fy":[29.25262,29.25262,29.25262,29.25262]}, + {"t":6.52957, "x":5.73315, "y":0.49861, "heading":1.5708, "vx":-1.50874, "vy":-1.31218, "omega":0.0, "ax":-5.06209, "ay":7.05033, "alpha":0.0, "fx":[-86.10469,-86.10469,-86.10469,-86.10469], "fy":[119.92409,119.92409,119.92409,119.92409]}, + {"t":6.57238, "x":5.66392, "y":0.4489, "heading":1.5708, "vx":-1.72547, "vy":-1.01033, "omega":0.0, "ax":-3.87161, "ay":8.4446, "alpha":0.0, "fx":[-65.85492,-65.85492,-65.85492,-65.85492], "fy":[143.64025,143.64025,143.64025,143.64025]}, + {"t":6.61519, "x":5.5865, "y":0.41338, "heading":1.5708, "vx":-1.89123, "vy":-0.64879, "omega":0.0, "ax":-2.04724, "ay":9.15093, "alpha":0.0, "fx":[-34.82293,-34.82293,-34.82293,-34.82293], "fy":[155.65466,155.65466,155.65466,155.65466]}, + {"t":6.65801, "x":5.50365, "y":0.39399, "heading":1.5708, "vx":-1.97888, "vy":-0.257, "omega":0.0, "ax":-0.00207, "ay":0.58519, "alpha":0.0, "fx":[-0.03526,-0.03526,-0.03526,-0.03526], "fy":[9.95385,9.95385,9.95385,9.95385]}, + {"t":6.65803, "x":5.5036, "y":0.39398, "heading":1.5708, "vx":-1.97888, "vy":-0.25699, "omega":0.0, "ax":-0.06094, "ay":0.57745, "alpha":0.0, "fx":[-1.03653,-1.03653,-1.03653,-1.03653], "fy":[9.82227,9.82227,9.82227,9.82227]}, + {"t":6.65805, "x":5.50356, "y":0.39398, "heading":1.5708, "vx":-1.97888, "vy":-0.25697, "omega":0.0, "ax":-0.11979, "ay":0.56967, "alpha":0.0, "fx":[-2.03768,-2.03768,-2.03768,-2.03768], "fy":[9.68994,9.68994,9.68994,9.68994]}, + {"t":6.65808, "x":5.50351, "y":0.39397, "heading":1.5708, "vx":-1.97888, "vy":-0.25696, "omega":0.0, "ax":-0.17865, "ay":0.56185, "alpha":0.0, "fx":[-3.03879,-3.03879,-3.03879,-3.03879], "fy":[9.55684,9.55684,9.55684,9.55684]}, + {"t":6.6581, "x":5.50346, "y":0.39396, "heading":1.5708, "vx":-1.97888, "vy":-0.25695, "omega":0.0, "ax":-0.23751, "ay":0.55398, "alpha":0.0, "fx":[-4.03995,-4.03995,-4.03995,-4.03995], "fy":[9.42296,9.42296,9.42296,9.42296]}, + {"t":6.65813, "x":5.50341, "y":0.39396, "heading":1.5708, "vx":-1.97889, "vy":-0.25693, "omega":0.0, "ax":-0.29637, "ay":0.54606, "alpha":0.0, "fx":[-5.04123,-5.04123,-5.04123,-5.04123], "fy":[9.28829,9.28829,9.28829,9.28829]}, + {"t":6.65815, "x":5.50337, "y":0.39395, "heading":1.5708, "vx":-1.9789, "vy":-0.25692, "omega":0.0, "ax":-0.35525, "ay":0.53809, "alpha":0.0, "fx":[-6.04271,-6.04271,-6.04271,-6.04271], "fy":[9.15283,9.15283,9.15283,9.15283]}, + {"t":6.65817, "x":5.50332, "y":0.39395, "heading":1.5708, "vx":-1.97891, "vy":-0.25691, "omega":0.0, "ax":-0.41414, "ay":0.53008, "alpha":0.0, "fx":[-7.04447,-7.04447,-7.04447,-7.04447], "fy":[9.01655,9.01655,9.01655,9.01655]}, + {"t":6.6582, "x":5.50327, "y":0.39394, "heading":1.5708, "vx":-1.97892, "vy":-0.2569, "omega":0.0, "ax":-0.47306, "ay":0.52202, "alpha":0.0, "fx":[-8.0466,-8.0466,-8.0466,-8.0466], "fy":[8.87946,8.87946,8.87946,8.87946]}, + {"t":6.65822, "x":5.50322, "y":0.39393, "heading":1.5708, "vx":-1.97893, "vy":-0.25688, "omega":0.0, "ax":-0.532, "ay":0.51391, "alpha":0.0, "fx":[-9.04917,-9.04917,-9.04917,-9.04917], "fy":[8.74153,8.74153,8.74153,8.74153]}, + {"t":6.65825, "x":5.50318, "y":0.39393, "heading":1.5708, "vx":-1.97894, "vy":-0.25687, "omega":0.0, "ax":-0.59097, "ay":0.50576, "alpha":0.0, "fx":[-10.05228,-10.05228,-10.05228,-10.05228], "fy":[8.60276,8.60276,8.60276,8.60276]}, + {"t":6.65827, "x":5.50313, "y":0.39392, "heading":1.5708, "vx":-1.97895, "vy":-0.25686, "omega":0.0, "ax":-0.64998, "ay":0.49755, "alpha":0.0, "fx":[-11.05601,-11.05601,-11.05601,-11.05601], "fy":[8.46312,8.46312,8.46312,8.46312]}, + {"t":6.6583, "x":5.50308, "y":0.39392, "heading":1.5708, "vx":-1.97897, "vy":-0.25685, "omega":0.0, "ax":-0.70903, "ay":0.48929, "alpha":0.0, "fx":[-12.06044,-12.06044,-12.06044,-12.06044], "fy":[8.32262,8.32262,8.32262,8.32262]}, + {"t":6.65832, "x":5.50303, "y":0.39391, "heading":1.5708, "vx":-1.97899, "vy":-0.25683, "omega":0.0, "ax":-0.76813, "ay":0.48097, "alpha":0.0, "fx":[-13.06568,-13.06568,-13.06568,-13.06568], "fy":[8.18123,8.18123,8.18123,8.18123]}, + {"t":6.65834, "x":5.50299, "y":0.3939, "heading":1.5708, "vx":-1.979, "vy":-0.25682, "omega":0.0, "ax":-0.82728, "ay":0.47261, "alpha":0.0, "fx":[-14.0718,-14.0718,-14.0718,-14.0718], "fy":[8.03894,8.03894,8.03894,8.03894]}, + {"t":6.65837, "x":5.50294, "y":0.3939, "heading":1.5708, "vx":-1.97902, "vy":-0.25681, "omega":0.0, "ax":-0.88649, "ay":0.46419, "alpha":0.0, "fx":[-15.07893,-15.07893,-15.07893,-15.07893], "fy":[7.89573,7.89573,7.89573,7.89573]}, + {"t":6.65839, "x":5.50289, "y":0.39389, "heading":1.5708, "vx":-1.97905, "vy":-0.2568, "omega":0.0, "ax":-0.94576, "ay":0.45572, "alpha":0.0, "fx":[-16.08714,-16.08714,-16.08714,-16.08714], "fy":[7.75159,7.75159,7.75159,7.75159]}, + {"t":6.65842, "x":5.50284, "y":0.39388, "heading":1.5708, "vx":-1.97907, "vy":-0.25679, "omega":0.0, "ax":-1.00511, "ay":0.44719, "alpha":0.0, "fx":[-17.09656,-17.09656,-17.09656,-17.09656], "fy":[7.6065,7.6065,7.6065,7.6065]}, + {"t":6.65844, "x":5.5028, "y":0.39388, "heading":1.5708, "vx":-1.97909, "vy":-0.25678, "omega":0.0, "ax":-1.06453, "ay":0.4386, "alpha":0.0, "fx":[-18.10729,-18.10729,-18.10729,-18.10729], "fy":[7.46044,7.46044,7.46044,7.46044]}, + {"t":6.65846, "x":5.50275, "y":0.39387, "heading":1.5708, "vx":-1.97912, "vy":-0.25677, "omega":0.0, "ax":-8.88586, "ay":3.12229, "alpha":0.0, "fx":[-151.14595,-151.14595,-151.14595,-151.14595], "fy":[53.10927,53.10927,53.10927,53.10927]}, + {"t":6.7165, "x":5.37293, "y":0.38423, "heading":1.5708, "vx":-2.4948, "vy":-0.07557, "omega":0.0, "ax":-9.16447, "ay":2.08585, "alpha":0.0, "fx":[-155.88506,-155.88506,-155.88506,-155.88506], "fy":[35.47966,35.47966,35.47966,35.47966]}, + {"t":6.77453, "x":5.21271, "y":0.38336, "heading":1.5708, "vx":-3.02666, "vy":0.04548, "omega":0.0, "ax":-8.30035, "ay":0.11391, "alpha":0.0, "fx":[-141.18663,-141.18663,-141.18663,-141.18663], "fy":[1.93755,1.93755,1.93755,1.93755]}, + {"t":6.83257, "x":5.02308, "y":0.38619, "heading":1.5708, "vx":-3.50836, "vy":0.05209, "omega":0.0, "ax":-0.00624, "ay":-0.42589, "alpha":0.0, "fx":[-0.1061,-0.1061,-0.1061,-0.1061], "fy":[-7.24419,-7.24419,-7.24419,-7.24419]}, + {"t":6.8906, "x":4.81947, "y":0.38849, "heading":1.5708, "vx":-3.50872, "vy":0.02738, "omega":0.0, "ax":-0.00147, "ay":-0.25924, "alpha":0.0, "fx":[-0.02495,-0.02495,-0.02495,-0.02495], "fy":[-4.4096,-4.4096,-4.4096,-4.4096]}, + {"t":6.94863, "x":4.61584, "y":0.38965, "heading":1.5708, "vx":-3.50881, "vy":0.01233, "omega":0.0, "ax":-0.00037, "ay":-0.1882, "alpha":0.0, "fx":[-0.00628,-0.00628,-0.00628,-0.00628], "fy":[-3.20115,-3.20115,-3.20115,-3.20115]}, + {"t":7.00667, "x":4.41221, "y":0.39004, "heading":1.5708, "vx":-3.50883, "vy":0.00141, "omega":0.0, "ax":0.00015, "ay":-0.15989, "alpha":0.0, "fx":[0.00248,0.00248,0.00248,0.00248], "fy":[-2.71973,-2.71973,-2.71973,-2.71973]}, + {"t":7.0647, "x":4.20858, "y":0.38986, "heading":1.5708, "vx":-3.50882, "vy":-0.00787, "omega":0.0, "ax":0.00056, "ay":-0.15912, "alpha":0.0, "fx":[0.00961,0.00961,0.00961,0.00961], "fy":[-2.70656,-2.70656,-2.70656,-2.70656]}, + {"t":7.12274, "x":4.00495, "y":0.38913, "heading":1.5708, "vx":-3.50879, "vy":-0.0171, "omega":0.0, "ax":0.00119, "ay":-0.18527, "alpha":0.0, "fx":[0.02016,0.02016,0.02016,0.02016], "fy":[-3.15133,-3.15133,-3.15133,-3.15133]}, + {"t":7.18077, "x":3.80132, "y":0.38783, "heading":1.5708, "vx":-3.50872, "vy":-0.02786, "omega":0.0, "ax":0.00276, "ay":-0.24854, "alpha":0.0, "fx":[0.04701,0.04701,0.04701,0.04701], "fy":[-4.22762,-4.22762,-4.22762,-4.22762]}, + {"t":7.23881, "x":3.5977, "y":0.38579, "heading":1.5708, "vx":-3.50856, "vy":-0.04228, "omega":0.0, "ax":5.21912, "ay":-0.1788, "alpha":0.0, "fx":[88.77569,88.77569,88.77569,88.77569], "fy":[-3.04136,-3.04136,-3.04136,-3.04136]}, + {"t":7.29684, "x":3.40287, "y":0.38304, "heading":1.5708, "vx":-3.20567, "vy":-0.05266, "omega":0.0, "ax":9.39515, "ay":0.17009, "alpha":0.0, "fx":[159.80879,159.80879,159.80879,159.80879], "fy":[2.89312,2.89312,2.89312,2.89312]}, + {"t":7.35487, "x":3.23265, "y":0.38027, "heading":1.5708, "vx":-2.66043, "vy":-0.04279, "omega":0.0, "ax":9.41405, "ay":0.23621, "alpha":0.0, "fx":[160.13022,160.13022,160.13022,160.13022], "fy":[4.01794,4.01794,4.01794,4.01794]}, + {"t":7.41291, "x":3.09411, "y":0.37818, "heading":1.5708, "vx":-2.11409, "vy":-0.02908, "omega":0.0, "ax":9.39092, "ay":0.78707, "alpha":0.0, "fx":[159.73685,159.73685,159.73685,159.73685], "fy":[13.38786,13.38786,13.38786,13.38786]}, + {"t":7.47094, "x":2.98723, "y":0.37782, "heading":1.5708, "vx":-1.5691, "vy":0.0166, "omega":0.0, "ax":7.83361, "ay":5.24776, "alpha":0.0, "fx":[133.24742,133.24742,133.24742,133.24742], "fy":[89.26292,89.26292,89.26292,89.26292]}, + {"t":7.52898, "x":2.90936, "y":0.38762, "heading":1.5708, "vx":-1.11448, "vy":0.32115, "omega":0.0, "ax":6.37654, "ay":5.5291, "alpha":12.56307, "fx":[141.86604,158.96125,100.88525,32.13981], "fy":[73.54947,20.83829,124.69667,157.1089]}, + {"t":7.55725, "x":2.88041, "y":0.39891, "heading":1.57582, "vx":-0.93422, "vy":0.47745, "omega":0.35515, "ax":6.40035, "ay":5.52441, "alpha":12.50884, "fx":[142.36254,158.78388,101.04714,33.27888], "fy":[72.48571,21.99077,124.54615,156.85172]}, + {"t":7.58552, "x":2.85655, "y":0.41461, "heading":1.59085, "vx":-0.75329, "vy":0.63363, "omega":0.70877, "ax":6.43162, "ay":5.48546, "alpha":12.62732, "fx":[144.30694,158.55834,100.78213,33.95236], "fy":[68.41739,23.38873,124.7367,156.68183]}, + {"t":7.61378, "x":2.83783, "y":0.43472, "heading":1.61594, "vx":-0.57147, "vy":0.7887, "omega":1.06573, "ax":6.46505, "ay":5.41783, "alpha":12.91902, "fx":[147.18947,158.28284,100.1375,34.26466], "fy":[61.82742,24.99095,125.2243,156.58027]}, + {"t":7.64205, "x":2.82426, "y":0.45918, "heading":1.65123, "vx":-0.38871, "vy":0.94186, "omega":1.43095, "ax":6.49532, "ay":5.32736, "alpha":13.38352, "fx":[150.43609,157.95438,99.17581,34.36809], "fy":[53.24673,26.76152,125.9472,156.51187]}, + {"t":7.67032, "x":2.81586, "y":0.48793, "heading":1.69703, "vx":-0.20509, "vy":1.09246, "omega":1.80929, "ax":6.51994, "ay":5.22022, "alpha":14.0029, "fx":[153.50927,157.57049,98.00383,34.52564], "fy":[43.30423,28.65565,126.80605,156.41182]}, + {"t":7.69859, "x":2.81267, "y":0.5209, "heading":1.75377, "vx":-0.02077, "vy":1.24003, "omega":2.20514, "ax":6.54392, "ay":5.10073, "alpha":14.72286, "fx":[156.00199,157.13527,96.8421,35.26171], "fy":[32.70448,30.58195,127.61417,156.1475]}, + {"t":7.72686, "x":2.8147, "y":0.558, "heading":1.82199, "vx":0.16422, "vy":1.38422, "omega":2.62135, "ax":6.59015, "ay":4.96372, "alpha":15.42083, "fx":[157.69505,156.67893,96.22123,37.79118], "fy":[22.09645,32.29359,127.95066,155.38509]}, + {"t":7.75513, "x":2.82197, "y":0.59911, "heading":1.90226, "vx":0.35052, "vy":1.52454, "omega":3.05729, "ax":6.73317, "ay":4.76631, "alpha":15.79411, "fx":[158.53136,156.32469,97.67631,45.58453], "fy":[11.76172,33.00699,126.58754,152.93773]}, + {"t":7.7834, "x":2.83457, "y":0.64411, "heading":1.99499, "vx":0.54086, "vy":1.65929, "omega":3.50378, "ax":7.27534, "ay":4.24583, "alpha":14.62705, "fx":[158.39754,156.55102,107.36012,72.69729], "fy":[0.62144,29.4223,117.76807,141.06936]}, + {"t":7.81167, "x":2.85277, "y":0.69272, "heading":2.09989, "vx":0.74653, "vy":1.77931, "omega":3.91727, "ax":9.15684, "ay":-0.16083, "alpha":2.91768, "fx":[154.83838,156.58085,155.7079,155.8938], "fy":[-22.95924,-10.56084,16.46206,6.11561]}, + {"t":7.83994, "x":2.87753, "y":0.74295, "heading":2.21179, "vx":1.00539, "vy":1.77477, "omega":3.99976, "ax":0.42575, "ay":-9.0826, "alpha":-2.49309, "fx":[7.87047,-14.12733,7.76797,27.45613], "fy":[-154.34454,-154.51749,-155.84765,-153.26011]}, + {"t":7.86821, "x":2.90613, "y":0.78949, "heading":2.32387, "vx":1.01743, "vy":1.51801, "omega":3.92928, "ax":-4.20484, "ay":-7.30111, "alpha":-14.14804, "fx":[-132.31147,-125.48654,-39.99577,11.7012], "fy":[-86.16998,-97.90801,-154.2685,-158.41259]}, + {"t":7.89648, "x":2.93321, "y":0.82949, "heading":2.42929, "vx":0.89856, "vy":1.31161, "omega":3.52932, "ax":-4.58751, "ay":-6.74324, "alpha":-16.90331, "fx":[-149.16268,-132.28192,-41.297,10.61236], "fy":[-55.61918,-89.66313,-154.42858,-159.09143]}, + {"t":7.92475, "x":2.95678, "y":0.86387, "heading":2.52231, "vx":0.76887, "vy":1.12098, "omega":3.05148, "ax":-4.61793, "ay":-6.60331, "alpha":-17.79053, "fx":[-153.11082,-132.79436,-38.82942,10.53596], "fy":[-45.33728,-89.33005,-155.26544,-159.34871]}, + {"t":7.95302, "x":2.97667, "y":0.89292, "heading":2.60146, "vx":0.63832, "vy":0.93431, "omega":2.54855, "ax":-4.58568, "ay":-6.58093, "alpha":-18.06276, "fx":[-154.47992,-131.95172,-35.8075,10.23484], "fy":[-41.36922,-90.78596,-156.09596,-159.5077]}, + {"t":7.98129, "x":2.99288, "y":0.91671, "heading":2.66629, "vx":0.50869, "vy":0.74827, "omega":2.03792, "ax":-4.54468, "ay":-6.59508, "alpha":-18.08609, "fx":[-155.042,-130.82486,-32.99349,9.64581], "fy":[-39.7751,-92.5301,-156.78131,-159.63487]}, + {"t":8.00956, "x":3.00544, "y":0.93523, "heading":2.71668, "vx":0.38022, "vy":0.56183, "omega":1.52664, "ax":-4.50873, "ay":-6.6157, "alpha":-18.02624, "fx":[-155.31889,-129.80752,-30.63808,8.99534], "fy":[-39.0458,-94.03551,-157.30472,-159.73887]}, + {"t":8.03783, "x":3.01439, "y":0.94846, "heading":2.75263, "vx":0.25276, "vy":0.37481, "omega":1.01705, "ax":-4.48071, "ay":-6.62935, "alpha":-17.97786, "fx":[-155.52207,-129.08828,-28.8365,8.58466], "fy":[-38.48072,-95.07913,-157.6794,-159.81438]}, + {"t":8.06609, "x":3.01975, "y":0.95641, "heading":2.7742, "vx":0.12609, "vy":0.1874, "omega":0.50883, "ax":-4.46028, "ay":-6.62917, "alpha":-17.99927, "fx":[-155.75413,-128.77113,-27.63384,8.68695], "fy":[-37.7132,-95.55169,-157.9218,-159.8545]}, + {"t":8.09436, "x":3.02153, "y":0.95906, "heading":2.78139, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0,0.0,0.0,0.0]}], "splits":[0] }, "events":[] diff --git a/src/main/deploy/v1_doomspiral/choreo/RIGHT_TRENCH_ANTI_BUCKS_CROSSES.traj b/src/main/deploy/v1_doomspiral/choreo/RIGHT_TRENCH_ANTI_BUCKS_CROSSES.traj index c75dad6a..7e1a64c6 100644 --- a/src/main/deploy/v1_doomspiral/choreo/RIGHT_TRENCH_ANTI_BUCKS_CROSSES.traj +++ b/src/main/deploy/v1_doomspiral/choreo/RIGHT_TRENCH_ANTI_BUCKS_CROSSES.traj @@ -4,46 +4,44 @@ "snapshot":{ "waypoints":[ {"x":4.428042411804199, "y":0.3954239785671234, "heading":1.5707963267948966, "intervals":26, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, - {"x":7.649963855743408, "y":0.3576396703720093, "heading":0.0, "intervals":20, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":8.526999855041504, "y":0.9033589959144592, "heading":1.5707963267948966, "intervals":58, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, + {"x":7.649963855743408, "y":0.3576396703720093, "heading":0.0, "intervals":27, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, + {"x":8.526999855041504, "y":0.9033589959144592, "heading":1.5707963267948966, "intervals":83, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, {"x":8.526999855041504, "y":4.354134035110474, "heading":-1.5707963267948966, "intervals":24, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, {"x":7.274408340454102, "y":1.8902108669281008, "heading":0.0, "intervals":28, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, {"x":5.503650665283203, "y":0.3939896523952484, "heading":1.5707963267948966, "intervals":23, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, - {"x":3.7084639072418217, "y":0.3992261290550232, "heading":0.0, "intervals":12, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":2.9093637466430664, "y":0.3876216113567352, "heading":-1.5707963267948966, "intervals":23, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":2.822967529296875, "y":1.564, "heading":2.49, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], + {"x":3.7084639072418217, "y":0.3992261290550232, "heading":0.0, "intervals":22, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, + {"x":3.0215272903442383, "y":0.9590602874755856, "heading":2.7813916025632035, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], "constraints":[ {"from":"first", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, {"from":"last", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, {"from":"first", "to":"last", "data":{"type":"KeepInRectangle", "props":{"x":0.0, "y":0.0, "w":16.541, "h":8.0692}}, "enabled":true}, {"from":2, "to":3, "data":{"type":"KeepInLane", "props":{"tolerance":0.00001}}, "enabled":true}, {"from":2, "to":3, "data":{"type":"MaxAngularVelocity", "props":{"max":0.0}}, "enabled":true}, - {"from":5, "to":7, "data":{"type":"MaxAngularVelocity", "props":{"max":0.0}}, "enabled":true}, - {"from":1, "to":3, "data":{"type":"MaxVelocity", "props":{"max":1.25}}, "enabled":true}, - {"from":5, "to":7, "data":{"type":"KeepInLane", "props":{"tolerance":0.01}}, "enabled":true}, + {"from":5, "to":6, "data":{"type":"MaxAngularVelocity", "props":{"max":0.0}}, "enabled":true}, + {"from":1, "to":3, "data":{"type":"MaxVelocity", "props":{"max":0.85}}, "enabled":true}, + {"from":5, "to":6, "data":{"type":"KeepInLane", "props":{"tolerance":0.01}}, "enabled":true}, {"from":4, "to":6, "data":{"type":"MaxVelocity", "props":{"max":2.0}}, "enabled":true}], "targetDt":0.05 }, "params":{ "waypoints":[ {"x":{"exp":"4.428042411804199 m", "val":4.428042411804199}, "y":{"exp":"0.3954239785671234 m", "val":0.3954239785671234}, "heading":{"exp":"90 deg", "val":1.5707963267948966}, "intervals":26, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, - {"x":{"exp":"7.649963855743408 m", "val":7.649963855743408}, "y":{"exp":"0.3576396703720093 m", "val":0.3576396703720093}, "heading":{"exp":"0 deg", "val":0.0}, "intervals":20, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":{"exp":"8.526999855041504 m", "val":8.526999855041504}, "y":{"exp":"0.9033589959144592 m", "val":0.9033589959144592}, "heading":{"exp":"1.5707963267948966 rad", "val":1.5707963267948966}, "intervals":58, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, + {"x":{"exp":"7.649963855743408 m", "val":7.649963855743408}, "y":{"exp":"0.3576396703720093 m", "val":0.3576396703720093}, "heading":{"exp":"0 deg", "val":0.0}, "intervals":27, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, + {"x":{"exp":"8.526999855041504 m", "val":8.526999855041504}, "y":{"exp":"0.9033589959144592 m", "val":0.9033589959144592}, "heading":{"exp":"1.5707963267948966 rad", "val":1.5707963267948966}, "intervals":83, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, {"x":{"exp":"8.526999855041504 m", "val":8.526999855041504}, "y":{"exp":"4.354134035110474 m", "val":4.354134035110474}, "heading":{"exp":"-1.5707963267948966 rad", "val":-1.5707963267948966}, "intervals":24, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, {"x":{"exp":"7.274408340454102 m", "val":7.274408340454102}, "y":{"exp":"1.8902108669281006 m", "val":1.8902108669281008}, "heading":{"exp":"0 deg", "val":0.0}, "intervals":28, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, {"x":{"exp":"5.503650665283203 m", "val":5.503650665283203}, "y":{"exp":"0.3939896523952484 m", "val":0.3939896523952484}, "heading":{"exp":"90 deg", "val":1.5707963267948966}, "intervals":23, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, - {"x":{"exp":"3.7084639072418213 m", "val":3.7084639072418217}, "y":{"exp":"0.3992261290550232 m", "val":0.3992261290550232}, "heading":{"exp":"0 deg", "val":0.0}, "intervals":12, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":{"exp":"2.9093637466430664 m", "val":2.9093637466430664}, "y":{"exp":"0.38762161135673523 m", "val":0.3876216113567352}, "heading":{"exp":"-90 deg", "val":-1.5707963267948966}, "intervals":23, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":{"exp":"2.822967529296875 m", "val":2.822967529296875}, "y":{"exp":"(8.064 - 6.5) m", "val":1.564}, "heading":{"exp":"2.49 rad", "val":2.49}, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], + {"x":{"exp":"3.7084639072418213 m", "val":3.7084639072418217}, "y":{"exp":"0.3992261290550232 m", "val":0.3992261290550232}, "heading":{"exp":"0 deg", "val":0.0}, "intervals":22, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, + {"x":{"exp":"3.0215272903442383 m", "val":3.0215272903442383}, "y":{"exp":"(8.1 - 7.140939712524414) m", "val":0.9590602874755856}, "heading":{"exp":"(180 - 20.638) deg", "val":2.7813916025632035}, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], "constraints":[ {"from":"first", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, {"from":"last", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, {"from":"first", "to":"last", "data":{"type":"KeepInRectangle", "props":{"x":{"exp":"0 m", "val":0.0}, "y":{"exp":"0 m", "val":0.0}, "w":{"exp":"16.541 m", "val":16.541}, "h":{"exp":"8.0692 m", "val":8.0692}}}, "enabled":true}, {"from":2, "to":3, "data":{"type":"KeepInLane", "props":{"tolerance":{"exp":"1e-5 m", "val":0.00001}}}, "enabled":true}, {"from":2, "to":3, "data":{"type":"MaxAngularVelocity", "props":{"max":{"exp":"0 rad / s", "val":0.0}}}, "enabled":true}, - {"from":5, "to":7, "data":{"type":"MaxAngularVelocity", "props":{"max":{"exp":"0 rad / s", "val":0.0}}}, "enabled":true}, - {"from":1, "to":3, "data":{"type":"MaxVelocity", "props":{"max":{"exp":"1.25 m / s", "val":1.25}}}, "enabled":true}, - {"from":5, "to":7, "data":{"type":"KeepInLane", "props":{"tolerance":{"exp":"0.01 m", "val":0.01}}}, "enabled":true}, + {"from":5, "to":6, "data":{"type":"MaxAngularVelocity", "props":{"max":{"exp":"0 rad / s", "val":0.0}}}, "enabled":true}, + {"from":1, "to":3, "data":{"type":"MaxVelocity", "props":{"max":{"exp":"INTAKE_VELOCITY", "val":0.55}}}, "enabled":true}, + {"from":5, "to":6, "data":{"type":"KeepInLane", "props":{"tolerance":{"exp":"0.01 m", "val":0.01}}}, "enabled":true}, {"from":4, "to":6, "data":{"type":"MaxVelocity", "props":{"max":{"exp":"2 m / s", "val":2.0}}}, "enabled":true}], "targetDt":{ "exp":"0.05 s", @@ -75,223 +73,242 @@ "differentialTrackWidth":0.5588 }, "sampleType":"Swerve", - "waypoints":[0.0,1.18332,2.03596,4.85617,5.87446,7.03944,7.93788,8.25796,8.98218], + "waypoints":[0.0,1.21224,2.44588,6.56064,7.56273,8.7278,9.62676,10.17316], "samples":[ - {"t":0.0, "x":4.42804, "y":0.39542, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":9.42877, "ay":0.00302, "alpha":0.0, "fx":[160.3807,160.3807,160.3807,160.3807], "fy":[0.05144,0.05144,0.05144,0.05144]}, - {"t":0.04551, "x":4.43781, "y":0.39543, "heading":1.5708, "vx":0.42912, "vy":0.00014, "omega":0.0, "ax":9.42753, "ay":-0.00514, "alpha":0.0, "fx":[160.35958,160.35958,160.35958,160.35958], "fy":[-0.08751,-0.08751,-0.08751,-0.08751]}, - {"t":0.09102, "x":4.4671, "y":0.39543, "heading":1.5708, "vx":0.85819, "vy":-0.0001, "omega":0.0, "ax":9.42586, "ay":-0.01336, "alpha":0.0, "fx":[160.33126,160.33126,160.33126,160.33126], "fy":[-0.22719,-0.22719,-0.22719,-0.22719]}, - {"t":0.13654, "x":4.51592, "y":0.39541, "heading":1.5708, "vx":1.28718, "vy":-0.0007, "omega":0.0, "ax":9.42353, "ay":-0.02164, "alpha":0.0, "fx":[160.29146,160.29146,160.29146,160.29146], "fy":[-0.36805,-0.36805,-0.36805,-0.36805]}, - {"t":0.18205, "x":4.58426, "y":0.39536, "heading":1.5708, "vx":1.71607, "vy":-0.00169, "omega":0.0, "ax":9.42001, "ay":-0.03003, "alpha":0.0, "fx":[160.23162,160.23162,160.23162,160.23162], "fy":[-0.51086,-0.51086,-0.51086,-0.51086]}, - {"t":0.22756, "x":4.67212, "y":0.39525, "heading":1.5708, "vx":2.14479, "vy":-0.00306, "omega":0.0, "ax":9.41413, "ay":-0.03865, "alpha":0.0, "fx":[160.13158,160.13158,160.13158,160.13158], "fy":[-0.65741,-0.65741,-0.65741,-0.65741]}, - {"t":0.27307, "x":4.77949, "y":0.39507, "heading":1.5708, "vx":2.57325, "vy":-0.00482, "omega":0.0, "ax":9.4023, "ay":-0.04778, "alpha":0.0, "fx":[159.93049,159.93049,159.93049,159.93049], "fy":[-0.81274,-0.81274,-0.81274,-0.81274]}, - {"t":0.31859, "x":4.90634, "y":0.3948, "heading":1.5708, "vx":3.00117, "vy":-0.00699, "omega":0.0, "ax":9.36637, "ay":-0.05886, "alpha":0.0, "fx":[159.31922,159.31922,159.31922,159.31922], "fy":[-1.00117,-1.00117,-1.00117,-1.00117]}, - {"t":0.3641, "x":5.05263, "y":0.39442, "heading":1.5708, "vx":3.42745, "vy":-0.00967, "omega":0.0, "ax":1.76923, "ay":-0.33589, "alpha":0.0, "fx":[30.09404,30.09404,30.09404,30.09404], "fy":[-5.71348,-5.71348,-5.71348,-5.71348]}, - {"t":0.40961, "x":5.21045, "y":0.39363, "heading":1.5708, "vx":3.50797, "vy":-0.02496, "omega":0.0, "ax":-0.00109, "ay":-0.1576, "alpha":0.0, "fx":[-0.01849,-0.01849,-0.01849,-0.01849], "fy":[-2.68068,-2.68068,-2.68068,-2.68068]}, - {"t":0.45512, "x":5.37011, "y":0.39233, "heading":1.5708, "vx":3.50792, "vy":-0.03213, "omega":0.0, "ax":-0.00146, "ay":-0.14443, "alpha":0.0, "fx":[-0.0248,-0.0248,-0.0248,-0.0248], "fy":[-2.45678,-2.45678,-2.45678,-2.45678]}, - {"t":0.50063, "x":5.52976, "y":0.39072, "heading":1.5708, "vx":3.50786, "vy":-0.0387, "omega":0.0, "ax":-0.00178, "ay":-0.14849, "alpha":0.0, "fx":[-0.03029,-0.03029,-0.03029,-0.03029], "fy":[-2.52576,-2.52576,-2.52576,-2.52576]}, - {"t":0.54615, "x":5.68941, "y":0.38881, "heading":1.5708, "vx":3.50778, "vy":-0.04546, "omega":0.0, "ax":-0.00218, "ay":-0.15578, "alpha":0.0, "fx":[-0.037,-0.037,-0.037,-0.037], "fy":[-2.64983,-2.64983,-2.64983,-2.64983]}, - {"t":0.59166, "x":5.84905, "y":0.38658, "heading":1.5708, "vx":3.50768, "vy":-0.05255, "omega":0.0, "ax":-0.00266, "ay":-0.16549, "alpha":0.0, "fx":[-0.04517,-0.04517,-0.04517,-0.04517], "fy":[-2.81492,-2.81492,-2.81492,-2.81492]}, - {"t":0.63717, "x":6.00869, "y":0.38401, "heading":1.5708, "vx":3.50756, "vy":-0.06008, "omega":0.0, "ax":-0.00326, "ay":-0.17838, "alpha":0.0, "fx":[-0.05545,-0.05545,-0.05545,-0.05545], "fy":[-3.03415,-3.03415,-3.03415,-3.03415]}, - {"t":0.68268, "x":6.16832, "y":0.38109, "heading":1.5708, "vx":3.50741, "vy":-0.0682, "omega":0.0, "ax":-0.00406, "ay":-0.19598, "alpha":0.0, "fx":[-0.06901,-0.06901,-0.06901,-0.06901], "fy":[-3.33352,-3.33352,-3.33352,-3.33352]}, - {"t":0.72819, "x":6.32795, "y":0.37779, "heading":1.5708, "vx":3.50722, "vy":-0.07712, "omega":0.0, "ax":-0.00517, "ay":-0.22105, "alpha":0.0, "fx":[-0.08801,-0.08801,-0.08801,-0.08801], "fy":[-3.76007,-3.76007,-3.76007,-3.76007]}, - {"t":0.77371, "x":6.48756, "y":0.37405, "heading":1.5708, "vx":3.50699, "vy":-0.08718, "omega":0.0, "ax":-0.00685, "ay":-0.25825, "alpha":0.0, "fx":[-0.11647,-0.11647,-0.11647,-0.11647], "fy":[-4.3928,-4.3928,-4.3928,-4.3928]}, - {"t":0.81922, "x":6.64717, "y":0.36981, "heading":1.5708, "vx":3.50668, "vy":-0.09893, "omega":0.0, "ax":-0.00926, "ay":-0.30668, "alpha":0.0, "fx":[-0.15743,-0.15743,-0.15743,-0.15743], "fy":[-5.21654,-5.21654,-5.21654,-5.21654]}, - {"t":0.86473, "x":6.80675, "y":0.36499, "heading":1.5708, "vx":3.50625, "vy":-0.11289, "omega":0.0, "ax":-0.00839, "ay":-0.23915, "alpha":0.0, "fx":[-0.14268,-0.14268,-0.14268,-0.14268], "fy":[-4.06791,-4.06791,-4.06791,-4.06791]}, - {"t":0.91024, "x":6.96632, "y":0.35961, "heading":1.5708, "vx":3.50587, "vy":-0.12378, "omega":0.0, "ax":-2.71079, "ay":1.53789, "alpha":0.0, "fx":[-46.10975,-46.10975,-46.10975,-46.10975], "fy":[26.15902,26.15902,26.15902,26.15902]}, - {"t":0.95576, "x":7.12307, "y":0.35557, "heading":1.5708, "vx":3.3825, "vy":-0.05378, "omega":0.0, "ax":-9.35569, "ay":0.47411, "alpha":0.0, "fx":[-159.13762,-159.13762,-159.13762,-159.13762], "fy":[8.06439,8.06439,8.06439,8.06439]}, - {"t":1.00127, "x":7.26733, "y":0.35361, "heading":1.5708, "vx":2.9567, "vy":-0.03221, "omega":0.0, "ax":-9.38793, "ay":0.52763, "alpha":0.0, "fx":[-159.68597,-159.68597,-159.68597,-159.68597], "fy":[8.97486,8.97486,8.97486,8.97486]}, - {"t":1.04678, "x":7.39217, "y":0.35269, "heading":1.5708, "vx":2.52944, "vy":-0.00819, "omega":0.0, "ax":-9.39498, "ay":0.60391, "alpha":0.0, "fx":[-159.80591,-159.80591,-159.80591,-159.80591], "fy":[10.27226,10.27226,10.27226,10.27226]}, - {"t":1.09229, "x":7.49756, "y":0.35294, "heading":1.5708, "vx":2.10185, "vy":0.01929, "omega":0.0, "ax":-9.39492, "ay":0.68923, "alpha":0.0, "fx":[-159.80485,-159.80485,-159.80485,-159.80485], "fy":[11.72367,11.72367,11.72367,11.72367]}, - {"t":1.1378, "x":7.58349, "y":0.35454, "heading":1.5708, "vx":1.67427, "vy":0.05066, "omega":0.0, "ax":-9.39202, "ay":0.77127, "alpha":0.0, "fx":[-159.7556,-159.7556,-159.7556,-159.7556], "fy":[13.11909,13.11909,13.11909,13.11909]}, - {"t":1.18332, "x":7.64996, "y":0.35764, "heading":1.5708, "vx":1.24682, "vy":0.08576, "omega":0.0, "ax":-2.06307, "ay":8.95996, "alpha":0.0, "fx":[-35.09227,-35.09227,-35.09227,-35.09227], "fy":[152.40637,152.40637,152.40637,152.40637]}, - {"t":1.22595, "x":7.70124, "y":0.36944, "heading":1.5708, "vx":1.15886, "vy":0.46775, "omega":0.0, "ax":-1.3014, "ay":2.79222, "alpha":0.0, "fx":[-22.13646,-22.13646,-22.13646,-22.13646], "fy":[47.49485,47.49485,47.49485,47.49485]}, - {"t":1.26858, "x":7.74947, "y":0.39192, "heading":1.5708, "vx":1.10338, "vy":0.58679, "omega":0.0, "ax":-0.02191, "ay":0.04112, "alpha":0.0, "fx":[-0.37271,-0.37271,-0.37271,-0.37271], "fy":[0.69946,0.69946,0.69946,0.69946]}, - {"t":1.31121, "x":7.79649, "y":0.41697, "heading":1.5708, "vx":1.10245, "vy":0.58854, "omega":0.0, "ax":0.00404, "ay":-0.00757, "alpha":0.0, "fx":[0.06867,0.06867,0.06867,0.06867], "fy":[-0.12873,-0.12873,-0.12873,-0.12873]}, - {"t":1.35384, "x":7.84349, "y":0.44205, "heading":1.5708, "vx":1.10262, "vy":0.58822, "omega":0.0, "ax":0.00353, "ay":-0.00662, "alpha":0.0, "fx":[0.06001,0.06001,0.06001,0.06001], "fy":[-0.11256,-0.11256,-0.11256,-0.11256]}, - {"t":1.39648, "x":7.8905, "y":0.46713, "heading":1.5708, "vx":1.10277, "vy":0.58793, "omega":0.0, "ax":0.00293, "ay":-0.0055, "alpha":0.0, "fx":[0.04984,0.04984,0.04984,0.04984], "fy":[-0.09354,-0.09354,-0.09354,-0.09354]}, - {"t":1.43911, "x":7.93752, "y":0.49219, "heading":1.5708, "vx":1.10289, "vy":0.5877, "omega":0.0, "ax":0.00252, "ay":-0.00473, "alpha":0.0, "fx":[0.04284,0.04284,0.04284,0.04284], "fy":[-0.08043,-0.08043,-0.08043,-0.08043]}, - {"t":1.48174, "x":7.98454, "y":0.51724, "heading":1.5708, "vx":1.103, "vy":0.5875, "omega":0.0, "ax":0.00222, "ay":-0.00417, "alpha":0.0, "fx":[0.03772,0.03772,0.03772,0.03772], "fy":[-0.07086,-0.07086,-0.07086,-0.07086]}, - {"t":1.52437, "x":8.03156, "y":0.54228, "heading":1.5708, "vx":1.1031, "vy":0.58732, "omega":0.0, "ax":0.00199, "ay":-0.00373, "alpha":0.0, "fx":[0.03381,0.03381,0.03381,0.03381], "fy":[-0.06352,-0.06352,-0.06352,-0.06352]}, - {"t":1.56701, "x":8.07859, "y":0.56731, "heading":1.5708, "vx":1.10318, "vy":0.58716, "omega":0.0, "ax":0.0018, "ay":-0.00339, "alpha":0.0, "fx":[0.0307,0.0307,0.0307,0.0307], "fy":[-0.0577,-0.0577,-0.0577,-0.0577]}, - {"t":1.60964, "x":8.12562, "y":0.59234, "heading":1.5708, "vx":1.10326, "vy":0.58702, "omega":0.0, "ax":0.00166, "ay":-0.00311, "alpha":0.0, "fx":[0.02816,0.02816,0.02816,0.02816], "fy":[-0.05294,-0.05294,-0.05294,-0.05294]}, - {"t":1.65227, "x":8.17266, "y":0.61737, "heading":1.5708, "vx":1.10333, "vy":0.58688, "omega":0.0, "ax":0.00153, "ay":-0.00288, "alpha":0.0, "fx":[0.02604,0.02604,0.02604,0.02604], "fy":[-0.04897,-0.04897,-0.04897,-0.04897]}, - {"t":1.6949, "x":8.2197, "y":0.64238, "heading":1.5708, "vx":1.10339, "vy":0.58676, "omega":0.0, "ax":0.00142, "ay":-0.00268, "alpha":0.0, "fx":[0.02423,0.02423,0.02423,0.02423], "fy":[-0.04559,-0.04559,-0.04559,-0.04559]}, - {"t":1.73754, "x":8.26674, "y":0.6674, "heading":1.5708, "vx":1.10346, "vy":0.58665, "omega":0.0, "ax":0.00133, "ay":-0.00249, "alpha":0.0, "fx":[0.02255,0.02255,0.02255,0.02255], "fy":[-0.04242,-0.04242,-0.04242,-0.04242]}, - {"t":1.78017, "x":8.31379, "y":0.6924, "heading":1.5708, "vx":1.10351, "vy":0.58654, "omega":0.0, "ax":0.00079, "ay":-0.00148, "alpha":0.0, "fx":[0.01339,0.01339,0.01339,0.01339], "fy":[-0.02521,-0.02521,-0.02521,-0.02521]}, - {"t":1.8228, "x":8.36083, "y":0.71741, "heading":1.5708, "vx":1.10355, "vy":0.58648, "omega":0.0, "ax":-0.0256, "ay":0.04806, "alpha":0.0, "fx":[-0.43544,-0.43544,-0.43544,-0.43544], "fy":[0.81748,0.81748,0.81748,0.81748]}, - {"t":1.86543, "x":8.40785, "y":0.74245, "heading":1.5708, "vx":1.10245, "vy":0.58853, "omega":0.0, "ax":-1.51702, "ay":2.52681, "alpha":0.0, "fx":[-25.80399,-25.80399,-25.80399,-25.80399], "fy":[42.98025,42.98025,42.98025,42.98025]}, - {"t":1.90806, "x":8.45348, "y":0.76984, "heading":1.5708, "vx":1.03778, "vy":0.69625, "omega":0.0, "ax":-6.24715, "ay":6.72824, "alpha":0.0, "fx":[-106.26223,-106.26223,-106.26223,-106.26223], "fy":[114.44542,114.44542,114.44542,114.44542]}, - {"t":1.9507, "x":8.49204, "y":0.80564, "heading":1.5708, "vx":0.77145, "vy":0.98309, "omega":0.0, "ax":-8.21797, "ay":4.53114, "alpha":0.0, "fx":[-139.78528,-139.78528,-139.78528,-139.78528], "fy":[77.07338,77.07338,77.07338,77.07338]}, - {"t":1.99333, "x":8.51746, "y":0.85167, "heading":1.5708, "vx":0.4211, "vy":1.17626, "omega":0.0, "ax":-9.2598, "ay":1.69978, "alpha":0.0, "fx":[-157.5066,-157.5066,-157.5066,-157.5066], "fy":[28.91278,28.91278,28.91278,28.91278]}, - {"t":2.03596, "x":8.527, "y":0.90336, "heading":1.5708, "vx":0.02633, "vy":1.24873, "omega":0.0, "ax":-1.07631, "ay":0.01531, "alpha":0.0, "fx":[-18.3078,-18.3078,-18.3078,-18.3078], "fy":[0.26041,0.26041,0.26041,0.26041]}, - {"t":2.08459, "x":8.52701, "y":0.9641, "heading":1.5708, "vx":-0.026, "vy":1.24947, "omega":0.0, "ax":1.056, "ay":0.00028, "alpha":0.0, "fx":[17.96223,17.96223,17.96223,17.96223], "fy":[0.00473,0.00473,0.00473,0.00473]}, - {"t":2.13321, "x":8.52699, "y":1.02485, "heading":1.5708, "vx":0.02534, "vy":1.24949, "omega":0.0, "ax":-1.02897, "ay":0.00027, "alpha":0.0, "fx":[-17.5025,-17.5025,-17.5025,-17.5025], "fy":[0.00459,0.00459,0.00459,0.00459]}, - {"t":2.18183, "x":8.52701, "y":1.08561, "heading":1.5708, "vx":-0.02469, "vy":1.2495, "omega":0.0, "ax":1.00204, "ay":0.00026, "alpha":0.0, "fx":[17.04435,17.04435,17.04435,17.04435], "fy":[0.00445,0.00445,0.00445,0.00445]}, - {"t":2.23046, "x":8.52699, "y":1.14636, "heading":1.5708, "vx":0.02403, "vy":1.24951, "omega":0.0, "ax":-0.97519, "ay":0.00025, "alpha":0.0, "fx":[-16.58777,-16.58777,-16.58777,-16.58777], "fy":[0.00432,0.00432,0.00432,0.00432]}, - {"t":2.27908, "x":8.52701, "y":1.20712, "heading":1.5708, "vx":-0.02338, "vy":1.24952, "omega":0.0, "ax":0.94844, "ay":0.00025, "alpha":0.0, "fx":[16.13277,16.13277,16.13277,16.13277], "fy":[0.00419,0.00419,0.00419,0.00419]}, - {"t":2.32771, "x":8.52699, "y":1.26788, "heading":1.5708, "vx":0.02273, "vy":1.24954, "omega":0.0, "ax":-0.92179, "ay":0.00024, "alpha":0.0, "fx":[-15.67933,-15.67933,-15.67933,-15.67933], "fy":[0.00405,0.00405,0.00405,0.00405]}, - {"t":2.37633, "x":8.52701, "y":1.32864, "heading":1.5708, "vx":-0.02209, "vy":1.24955, "omega":0.0, "ax":0.89522, "ay":0.00023, "alpha":0.0, "fx":[15.22745,15.22745,15.22745,15.22745], "fy":[0.00392,0.00392,0.00392,0.00392]}, - {"t":2.42496, "x":8.52699, "y":1.38939, "heading":1.5708, "vx":0.02144, "vy":1.24956, "omega":0.0, "ax":-0.86875, "ay":0.00022, "alpha":0.0, "fx":[-14.77712,-14.77712,-14.77712,-14.77712], "fy":[0.0038,0.0038,0.0038,0.0038]}, - {"t":2.47358, "x":8.52701, "y":1.45015, "heading":1.5708, "vx":-0.0208, "vy":1.24957, "omega":0.0, "ax":0.84236, "ay":0.00022, "alpha":0.0, "fx":[14.32834,14.32834,14.32834,14.32834], "fy":[0.00367,0.00367,0.00367,0.00367]}, - {"t":2.5222, "x":8.52699, "y":1.51091, "heading":1.5708, "vx":0.02016, "vy":1.24958, "omega":0.0, "ax":-0.81607, "ay":0.00021, "alpha":0.0, "fx":[-13.8811,-13.8811,-13.8811,-13.8811], "fy":[0.00354,0.00354,0.00354,0.00354]}, - {"t":2.57083, "x":8.52701, "y":1.57167, "heading":1.5708, "vx":-0.01952, "vy":1.24959, "omega":0.0, "ax":0.78986, "ay":0.0002, "alpha":0.0, "fx":[13.43538,13.43538,13.43538,13.43538], "fy":[0.00342,0.00342,0.00342,0.00342]}, - {"t":2.61945, "x":8.52699, "y":1.63243, "heading":1.5708, "vx":0.01889, "vy":1.2496, "omega":0.0, "ax":-0.76375, "ay":0.00019, "alpha":0.0, "fx":[-12.99116,-12.99116,-12.99116,-12.99116], "fy":[0.00329,0.00329,0.00329,0.00329]}, - {"t":2.66808, "x":8.52701, "y":1.69319, "heading":1.5708, "vx":-0.01825, "vy":1.24961, "omega":0.0, "ax":0.73772, "ay":0.00019, "alpha":0.0, "fx":[12.54844,12.54844,12.54844,12.54844], "fy":[0.00317,0.00317,0.00317,0.00317]}, - {"t":2.7167, "x":8.52699, "y":1.75396, "heading":1.5708, "vx":0.01762, "vy":1.24962, "omega":0.0, "ax":-0.71178, "ay":0.00018, "alpha":0.0, "fx":[-12.10718,-12.10718,-12.10718,-12.10718], "fy":[0.00305,0.00305,0.00305,0.00305]}, - {"t":2.76533, "x":8.52701, "y":1.81472, "heading":1.5708, "vx":-0.01699, "vy":1.24963, "omega":0.0, "ax":0.68592, "ay":0.00017, "alpha":0.0, "fx":[11.66738,11.66738,11.66738,11.66738], "fy":[0.00293,0.00293,0.00293,0.00293]}, - {"t":2.81395, "x":8.52699, "y":1.87548, "heading":1.5708, "vx":0.01636, "vy":1.24964, "omega":0.0, "ax":-0.66015, "ay":0.00017, "alpha":0.0, "fx":[-11.229,-11.229,-11.229,-11.229], "fy":[0.00281,0.00281,0.00281,0.00281]}, - {"t":2.86257, "x":8.52701, "y":1.93624, "heading":1.5708, "vx":-0.01574, "vy":1.24964, "omega":0.0, "ax":0.63446, "ay":0.00016, "alpha":0.0, "fx":[10.79203,10.79203,10.79203,10.79203], "fy":[0.00269,0.00269,0.00269,0.00269]}, - {"t":2.9112, "x":8.52699, "y":1.99701, "heading":1.5708, "vx":0.01511, "vy":1.24965, "omega":0.0, "ax":-0.60885, "ay":0.00015, "alpha":0.0, "fx":[-10.35642,-10.35642,-10.35642,-10.35642], "fy":[0.00257,0.00257,0.00257,0.00257]}, - {"t":2.95982, "x":8.52701, "y":2.05777, "heading":1.5708, "vx":-0.01449, "vy":1.24966, "omega":0.0, "ax":0.58332, "ay":0.00014, "alpha":0.0, "fx":[9.92216,9.92216,9.92216,9.92216], "fy":[0.00246,0.00246,0.00246,0.00246]}, - {"t":3.00845, "x":8.52699, "y":2.11853, "heading":1.5708, "vx":0.01387, "vy":1.24967, "omega":0.0, "ax":-0.55787, "ay":0.00014, "alpha":0.0, "fx":[-9.48921,-9.48921,-9.48921,-9.48921], "fy":[0.00234,0.00234,0.00234,0.00234]}, - {"t":3.05707, "x":8.52701, "y":2.1793, "heading":1.5708, "vx":-0.01325, "vy":1.24967, "omega":0.0, "ax":0.53249, "ay":0.00013, "alpha":0.0, "fx":[9.05754,9.05754,9.05754,9.05754], "fy":[0.00223,0.00223,0.00223,0.00223]}, - {"t":3.1057, "x":8.52699, "y":2.24006, "heading":1.5708, "vx":0.01264, "vy":1.24968, "omega":0.0, "ax":-0.50719, "ay":0.00012, "alpha":0.0, "fx":[-8.6271,-8.6271,-8.6271,-8.6271], "fy":[0.00212,0.00212,0.00212,0.00212]}, - {"t":3.15432, "x":8.52701, "y":2.30083, "heading":1.5708, "vx":-0.01202, "vy":1.24969, "omega":0.0, "ax":0.48195, "ay":0.00012, "alpha":0.0, "fx":[8.19787,8.19787,8.19787,8.19787], "fy":[0.00201,0.00201,0.00201,0.00201]}, - {"t":3.20294, "x":8.52699, "y":2.36159, "heading":1.5708, "vx":0.01141, "vy":1.24969, "omega":0.0, "ax":-0.45679, "ay":0.00011, "alpha":0.0, "fx":[-7.76979,-7.76979,-7.76979,-7.76979], "fy":[0.0019,0.0019,0.0019,0.0019]}, - {"t":3.25157, "x":8.52701, "y":2.42236, "heading":1.5708, "vx":-0.0108, "vy":1.2497, "omega":0.0, "ax":0.43168, "ay":0.00011, "alpha":0.0, "fx":[7.34282,7.34282,7.34282,7.34282], "fy":[0.00179,0.00179,0.00179,0.00179]}, - {"t":3.30019, "x":8.52699, "y":2.48312, "heading":1.5708, "vx":0.01019, "vy":1.2497, "omega":0.0, "ax":-0.40665, "ay":0.0001, "alpha":0.0, "fx":[-6.91692,-6.91692,-6.91692,-6.91692], "fy":[0.00168,0.00168,0.00168,0.00168]}, - {"t":3.34882, "x":8.52701, "y":2.54389, "heading":1.5708, "vx":-0.00958, "vy":1.24971, "omega":0.0, "ax":0.38167, "ay":0.00009, "alpha":0.0, "fx":[6.49205,6.49205,6.49205,6.49205], "fy":[0.00157,0.00157,0.00157,0.00157]}, - {"t":3.39744, "x":8.52699, "y":2.60466, "heading":1.5708, "vx":0.00898, "vy":1.24971, "omega":0.0, "ax":-0.35675, "ay":0.00009, "alpha":0.0, "fx":[-6.06814,-6.06814,-6.06814,-6.06814], "fy":[0.00147,0.00147,0.00147,0.00147]}, - {"t":3.44607, "x":8.52701, "y":2.66542, "heading":1.5708, "vx":-0.00837, "vy":1.24972, "omega":0.0, "ax":0.33188, "ay":0.00008, "alpha":0.0, "fx":[5.64514,5.64514,5.64514,5.64514], "fy":[0.00136,0.00136,0.00136,0.00136]}, - {"t":3.49469, "x":8.52699, "y":2.72619, "heading":1.5708, "vx":0.00777, "vy":1.24972, "omega":0.0, "ax":-0.30706, "ay":0.00007, "alpha":0.0, "fx":[-5.22301,-5.22301,-5.22301,-5.22301], "fy":[0.00126,0.00126,0.00126,0.00126]}, - {"t":3.54331, "x":8.52701, "y":2.78696, "heading":1.5708, "vx":-0.00716, "vy":1.24972, "omega":0.0, "ax":0.28229, "ay":0.00007, "alpha":0.0, "fx":[4.80169,4.80169,4.80169,4.80169], "fy":[0.00115,0.00115,0.00115,0.00115]}, - {"t":3.59194, "x":8.52699, "y":2.84772, "heading":1.5708, "vx":0.00656, "vy":1.24973, "omega":0.0, "ax":-0.25757, "ay":0.00006, "alpha":0.0, "fx":[-4.38111,-4.38111,-4.38111,-4.38111], "fy":[0.00105,0.00105,0.00105,0.00105]}, - {"t":3.64056, "x":8.52701, "y":2.90849, "heading":1.5708, "vx":-0.00596, "vy":1.24973, "omega":0.0, "ax":0.23288, "ay":0.00006, "alpha":0.0, "fx":[3.96123,3.96123,3.96123,3.96123], "fy":[0.00095,0.00095,0.00095,0.00095]}, - {"t":3.68919, "x":8.52699, "y":2.96926, "heading":1.5708, "vx":0.00536, "vy":1.24973, "omega":0.0, "ax":-0.20823, "ay":0.00005, "alpha":0.0, "fx":[-3.54197,-3.54197,-3.54197,-3.54197], "fy":[0.00085,0.00085,0.00085,0.00085]}, - {"t":3.73781, "x":8.52701, "y":3.03003, "heading":1.5708, "vx":-0.00476, "vy":1.24973, "omega":0.0, "ax":0.18362, "ay":0.00004, "alpha":0.0, "fx":[3.12328,3.12328,3.12328,3.12328], "fy":[0.00075,0.00075,0.00075,0.00075]}, - {"t":3.78644, "x":8.52699, "y":3.09079, "heading":1.5708, "vx":0.00417, "vy":1.24974, "omega":0.0, "ax":-0.15903, "ay":0.00004, "alpha":0.0, "fx":[-2.70509,-2.70509,-2.70509,-2.70509], "fy":[0.00065,0.00065,0.00065,0.00065]}, - {"t":3.83506, "x":8.52701, "y":3.15156, "heading":1.5708, "vx":-0.00357, "vy":1.24974, "omega":0.0, "ax":0.13447, "ay":0.00003, "alpha":0.0, "fx":[2.28734,2.28734,2.28734,2.28734], "fy":[0.00055,0.00055,0.00055,0.00055]}, - {"t":3.88368, "x":8.52699, "y":3.21233, "heading":1.5708, "vx":0.00297, "vy":1.24974, "omega":0.0, "ax":-0.10993, "ay":0.00003, "alpha":0.0, "fx":[-1.86996,-1.86996,-1.86996,-1.86996], "fy":[0.00045,0.00045,0.00045,0.00045]}, - {"t":3.93231, "x":8.52701, "y":3.2731, "heading":1.5708, "vx":-0.00237, "vy":1.24974, "omega":0.0, "ax":0.08542, "ay":0.00002, "alpha":0.0, "fx":[1.45288,1.45288,1.45288,1.45288], "fy":[0.00035,0.00035,0.00035,0.00035]}, - {"t":3.98093, "x":8.52699, "y":3.33386, "heading":1.5708, "vx":0.00178, "vy":1.24974, "omega":0.0, "ax":-0.06091, "ay":0.00001, "alpha":0.0, "fx":[-1.03605,-1.03605,-1.03605,-1.03605], "fy":[0.00025,0.00025,0.00025,0.00025]}, - {"t":4.02956, "x":8.52701, "y":3.39463, "heading":1.5708, "vx":-0.00118, "vy":1.24974, "omega":0.0, "ax":0.03641, "ay":0.00001, "alpha":0.0, "fx":[0.61938,0.61938,0.61938,0.61938], "fy":[0.00015,0.00015,0.00015,0.00015]}, - {"t":4.07818, "x":8.52699, "y":3.4554, "heading":1.5708, "vx":0.00059, "vy":1.24974, "omega":0.0, "ax":-0.01192, "ay":0.0, "alpha":0.0, "fx":[-0.20282,-0.20282,-0.20282,-0.20282], "fy":[0.00005,0.00005,0.00005,0.00005]}, - {"t":4.12681, "x":8.52701, "y":3.51617, "heading":1.5708, "vx":0.00001, "vy":1.24974, "omega":0.0, "ax":-0.01256, "ay":0.0, "alpha":0.0, "fx":[-0.21371,-0.21371,-0.21371,-0.21371], "fy":[-0.00005,-0.00005,-0.00005,-0.00005]}, - {"t":4.17543, "x":8.52699, "y":3.57693, "heading":1.5708, "vx":-0.0006, "vy":1.24974, "omega":0.0, "ax":0.03705, "ay":-0.00001, "alpha":0.0, "fx":[0.63028,0.63028,0.63028,0.63028], "fy":[-0.00015,-0.00015,-0.00015,-0.00015]}, - {"t":4.22405, "x":8.52701, "y":3.6377, "heading":1.5708, "vx":0.0012, "vy":1.24974, "omega":0.0, "ax":-0.06155, "ay":-0.00001, "alpha":0.0, "fx":[-1.04694,-1.04694,-1.04694,-1.04694], "fy":[-0.00025,-0.00025,-0.00025,-0.00025]}, - {"t":4.27268, "x":8.52699, "y":3.69847, "heading":1.5708, "vx":-0.00179, "vy":1.24974, "omega":0.0, "ax":0.08606, "ay":-0.00002, "alpha":0.0, "fx":[1.46379,1.46379,1.46379,1.46379], "fy":[-0.00035,-0.00035,-0.00035,-0.00035]}, - {"t":4.3213, "x":8.52701, "y":3.75924, "heading":1.5708, "vx":0.00239, "vy":1.24974, "omega":0.0, "ax":-0.11058, "ay":-0.00003, "alpha":0.0, "fx":[-1.88087,-1.88087,-1.88087,-1.88087], "fy":[-0.00045,-0.00045,-0.00045,-0.00045]}, - {"t":4.36993, "x":8.52699, "y":3.82001, "heading":1.5708, "vx":-0.00299, "vy":1.24974, "omega":0.0, "ax":0.13511, "ay":-0.00003, "alpha":0.0, "fx":[2.29825,2.29825,2.29825,2.29825], "fy":[-0.00055,-0.00055,-0.00055,-0.00055]}, - {"t":4.41855, "x":8.52701, "y":3.88077, "heading":1.5708, "vx":0.00358, "vy":1.24974, "omega":0.0, "ax":-0.15967, "ay":-0.00004, "alpha":0.0, "fx":[-2.71602,-2.71602,-2.71602,-2.71602], "fy":[-0.00065,-0.00065,-0.00065,-0.00065]}, - {"t":4.46717, "x":8.52699, "y":3.94154, "heading":1.5708, "vx":-0.00418, "vy":1.24974, "omega":0.0, "ax":0.18426, "ay":-0.00004, "alpha":0.0, "fx":[3.13422,3.13422,3.13422,3.13422], "fy":[-0.00075,-0.00075,-0.00075,-0.00075]}, - {"t":4.5158, "x":8.52701, "y":4.00231, "heading":1.5708, "vx":0.00478, "vy":1.24973, "omega":0.0, "ax":-0.20888, "ay":-0.00005, "alpha":0.0, "fx":[-3.55292,-3.55292,-3.55292,-3.55292], "fy":[-0.00085,-0.00085,-0.00085,-0.00085]}, - {"t":4.56442, "x":8.52699, "y":4.06308, "heading":1.5708, "vx":-0.00538, "vy":1.24973, "omega":0.0, "ax":0.23353, "ay":-0.00006, "alpha":0.0, "fx":[3.9722,3.9722,3.9722,3.9722], "fy":[-0.00095,-0.00095,-0.00095,-0.00095]}, - {"t":4.61305, "x":8.52701, "y":4.12384, "heading":1.5708, "vx":0.00598, "vy":1.24973, "omega":0.0, "ax":-0.25821, "ay":-0.00006, "alpha":0.0, "fx":[-4.3921,-4.3921,-4.3921,-4.3921], "fy":[-0.00106,-0.00106,-0.00106,-0.00106]}, - {"t":4.66167, "x":8.52699, "y":4.18461, "heading":1.5708, "vx":-0.00658, "vy":1.24973, "omega":0.0, "ax":0.28294, "ay":-0.00009, "alpha":0.0, "fx":[4.81269,4.81269,4.81269,4.81269], "fy":[-0.00145,-0.00145,-0.00145,-0.00145]}, - {"t":4.7103, "x":8.52701, "y":4.24538, "heading":1.5708, "vx":0.00718, "vy":1.24972, "omega":0.0, "ax":-0.30774, "ay":-4.91191, "alpha":0.0, "fx":[-5.2345,-5.2345,-5.2345,-5.2345], "fy":[-83.55021,-83.55021,-83.55021,-83.55021]}, - {"t":4.75892, "x":8.52699, "y":4.30034, "heading":1.5708, "vx":-0.00778, "vy":1.01088, "omega":0.0, "ax":0.33275, "ay":-9.4105, "alpha":0.0, "fx":[5.65998,5.65998,5.65998,5.65998], "fy":[-160.06995,-160.06995,-160.06995,-160.06995]}, - {"t":4.80754, "x":8.52701, "y":4.33837, "heading":1.5708, "vx":0.0084, "vy":0.55331, "omega":0.0, "ax":-0.3517, "ay":-9.42031, "alpha":0.0, "fx":[-5.98227,-5.98227,-5.98227,-5.98227], "fy":[-160.23684,-160.23684,-160.23684,-160.23684]}, - {"t":4.85617, "x":8.527, "y":4.35413, "heading":1.5708, "vx":-0.00871, "vy":0.09525, "omega":0.0, "ax":-4.09726, "ay":-8.49136, "alpha":0.0, "fx":[-69.69316,-69.69316,-69.69316,-69.69316], "fy":[-144.43554,-144.43554,-144.43554,-144.43554]}, - {"t":4.8986, "x":8.52294, "y":4.35053, "heading":1.5708, "vx":-0.18255, "vy":-0.26503, "omega":0.0, "ax":-4.09568, "ay":-8.49073, "alpha":0.0, "fx":[-69.66628,-69.66628,-69.66628,-69.66628], "fy":[-144.42487,-144.42487,-144.42487,-144.42487]}, - {"t":4.94103, "x":8.51151, "y":4.33164, "heading":1.5708, "vx":-0.35632, "vy":-0.62528, "omega":0.0, "ax":-4.0936, "ay":-8.48991, "alpha":0.0, "fx":[-69.63094,-69.63094,-69.63094,-69.63094], "fy":[-144.41086,-144.41086,-144.41086,-144.41086]}, - {"t":4.98346, "x":8.49271, "y":4.29747, "heading":1.5708, "vx":-0.53001, "vy":-0.9855, "omega":0.0, "ax":-4.09075, "ay":-8.48878, "alpha":0.0, "fx":[-69.58245,-69.58245,-69.58245,-69.58245], "fy":[-144.39164,-144.39164,-144.39164,-144.39164]}, - {"t":5.02588, "x":8.46654, "y":4.24802, "heading":1.5708, "vx":-0.70357, "vy":-1.34566, "omega":0.0, "ax":-4.0866, "ay":-8.48713, "alpha":0.0, "fx":[-69.51186,-69.51186,-69.51186,-69.51186], "fy":[-144.36359,-144.36359,-144.36359,-144.36359]}, - {"t":5.06831, "x":8.43301, "y":4.18329, "heading":1.5708, "vx":-0.87696, "vy":-1.70576, "omega":0.0, "ax":-4.08, "ay":-8.48449, "alpha":0.0, "fx":[-69.39965,-69.39965,-69.39965,-69.39965], "fy":[-144.31881,-144.31881,-144.31881,-144.31881]}, - {"t":5.11074, "x":8.39213, "y":4.10327, "heading":1.5708, "vx":-1.05007, "vy":-2.06575, "omega":0.0, "ax":-4.06789, "ay":-8.47963, "alpha":0.0, "fx":[-69.19369,-69.19369,-69.19369,-69.19369], "fy":[-144.23606,-144.23606,-144.23606,-144.23606]}, - {"t":5.15317, "x":8.34391, "y":4.008, "heading":1.5708, "vx":-1.22267, "vy":-2.42553, "omega":0.0, "ax":-4.0384, "ay":-8.46764, "alpha":0.0, "fx":[-68.69199,-68.69199,-68.69199,-68.69199], "fy":[-144.03205,-144.03205,-144.03205,-144.03205]}, - {"t":5.1956, "x":8.2884, "y":3.89746, "heading":1.5708, "vx":-1.39401, "vy":-2.7848, "omega":0.0, "ax":-3.85858, "ay":-8.39188, "alpha":0.0, "fx":[-65.63328,-65.63328,-65.63328,-65.63328], "fy":[-142.74349,-142.74349,-142.74349,-142.74349]}, - {"t":5.23803, "x":8.22578, "y":3.77175, "heading":1.5708, "vx":-1.55773, "vy":-3.14086, "omega":0.0, "ax":0.63166, "ay":-0.36235, "alpha":0.0, "fx":[10.74438,10.74438,10.74438,10.74438], "fy":[-6.16339,-6.16339,-6.16339,-6.16339]}, - {"t":5.28046, "x":8.16026, "y":3.63816, "heading":1.5708, "vx":-1.53093, "vy":-3.15623, "omega":0.0, "ax":0.07442, "ay":-0.03606, "alpha":0.0, "fx":[1.26585,1.26585,1.26585,1.26585], "fy":[-0.61336,-0.61336,-0.61336,-0.61336]}, - {"t":5.32289, "x":8.09537, "y":3.50422, "heading":1.5708, "vx":-1.52777, "vy":-3.15776, "omega":0.0, "ax":0.00872, "ay":-0.00422, "alpha":0.0, "fx":[0.14837,0.14837,0.14837,0.14837], "fy":[-0.07179,-0.07179,-0.07179,-0.07179]}, - {"t":5.36531, "x":8.03056, "y":3.37023, "heading":1.5708, "vx":-1.5274, "vy":-3.15794, "omega":0.0, "ax":0.00138, "ay":-0.00067, "alpha":0.0, "fx":[0.02352,0.02352,0.02352,0.02352], "fy":[-0.0114,-0.0114,-0.0114,-0.0114]}, - {"t":5.40774, "x":7.96575, "y":3.23624, "heading":1.5708, "vx":-1.52734, "vy":-3.15797, "omega":0.0, "ax":0.00063, "ay":-0.00031, "alpha":0.0, "fx":[0.01068,0.01068,0.01068,0.01068], "fy":[-0.00519,-0.00519,-0.00519,-0.00519]}, - {"t":5.45017, "x":7.90095, "y":3.10226, "heading":1.5708, "vx":-1.52731, "vy":-3.15798, "omega":0.0, "ax":0.0006, "ay":-0.00029, "alpha":0.0, "fx":[0.01019,0.01019,0.01019,0.01019], "fy":[-0.00496,-0.00496,-0.00496,-0.00496]}, - {"t":5.4926, "x":7.83615, "y":2.96827, "heading":1.5708, "vx":-1.52729, "vy":-3.15799, "omega":0.0, "ax":0.00047, "ay":-0.00023, "alpha":0.0, "fx":[0.00799,0.00799,0.00799,0.00799], "fy":[-0.0039,-0.0039,-0.0039,-0.0039]}, - {"t":5.53503, "x":7.77135, "y":2.83428, "heading":1.5708, "vx":-1.52727, "vy":-3.158, "omega":0.0, "ax":-0.00133, "ay":0.00064, "alpha":0.0, "fx":[-0.02266,-0.02266,-0.02266,-0.02266], "fy":[0.01092,0.01092,0.01092,0.01092]}, - {"t":5.57746, "x":7.70655, "y":2.70029, "heading":1.5708, "vx":-1.52732, "vy":-3.15798, "omega":0.0, "ax":-0.0181, "ay":0.00875, "alpha":0.0, "fx":[-0.30791,-0.30791,-0.30791,-0.30791], "fy":[0.14891,0.14891,0.14891,0.14891]}, - {"t":5.61989, "x":7.64173, "y":2.56631, "heading":1.5708, "vx":-1.52809, "vy":-3.1576, "omega":0.0, "ax":-0.16754, "ay":0.08131, "alpha":0.0, "fx":[-2.84983,-2.84983,-2.84983,-2.84983], "fy":[1.38311,1.38311,1.38311,1.38311]}, - {"t":5.66232, "x":7.57674, "y":2.43241, "heading":1.5708, "vx":-1.5352, "vy":-3.15415, "omega":0.0, "ax":-1.44092, "ay":0.72403, "alpha":0.0, "fx":[-24.50964,-24.50964,-24.50964,-24.50964], "fy":[12.31555,12.31555,12.31555,12.31555]}, - {"t":5.70474, "x":7.51031, "y":2.29923, "heading":1.5708, "vx":-1.59634, "vy":-3.12343, "omega":0.0, "ax":0.50215, "ay":8.26554, "alpha":0.0, "fx":[8.54139,8.54139,8.54139,8.54139], "fy":[140.59445,140.59445,140.59445,140.59445]}, - {"t":5.74717, "x":7.44303, "y":2.17415, "heading":1.5708, "vx":-1.57503, "vy":-2.77274, "omega":0.0, "ax":3.8796, "ay":8.52264, "alpha":0.0, "fx":[65.99084,65.99084,65.99084,65.99084], "fy":[144.9676,144.9676,144.9676,144.9676]}, - {"t":5.7896, "x":7.3797, "y":2.06417, "heading":1.5708, "vx":-1.41042, "vy":-2.41113, "omega":0.0, "ax":3.98941, "ay":8.51116, "alpha":0.0, "fx":[67.85881,67.85881,67.85881,67.85881], "fy":[144.77238,144.77238,144.77238,144.77238]}, - {"t":5.83203, "x":7.32344, "y":1.96953, "heading":1.5708, "vx":-1.24116, "vy":-2.05002, "omega":0.0, "ax":4.0276, "ay":8.50681, "alpha":0.0, "fx":[68.50829,68.50829,68.50829,68.50829], "fy":[144.69833,144.69833,144.69833,144.69833]}, - {"t":5.87446, "x":7.27441, "y":1.89021, "heading":1.5708, "vx":-1.07027, "vy":-1.68908, "omega":0.0, "ax":-7.13392, "ay":5.52643, "alpha":0.0, "fx":[-121.34595,-121.34595,-121.34595,-121.34595], "fy":[94.00296,94.00296,94.00296,94.00296]}, - {"t":5.91607, "x":7.2237, "y":1.82472, "heading":1.5708, "vx":-1.36709, "vy":-1.45915, "omega":0.0, "ax":-3.22967, "ay":3.33273, "alpha":0.0, "fx":[-54.93583,-54.93583,-54.93583,-54.93583], "fy":[56.68875,56.68875,56.68875,56.68875]}, - {"t":5.95767, "x":7.16403, "y":1.76689, "heading":1.5708, "vx":-1.50146, "vy":-1.32049, "omega":0.0, "ax":-0.18199, "ay":0.20813, "alpha":0.0, "fx":[-3.09555,-3.09555,-3.09555,-3.09555], "fy":[3.54018,3.54018,3.54018,3.54018]}, - {"t":5.99928, "x":7.1014, "y":1.71213, "heading":1.5708, "vx":-1.50904, "vy":-1.31183, "omega":0.0, "ax":-0.00728, "ay":0.00837, "alpha":0.0, "fx":[-0.12381,-0.12381,-0.12381,-0.12381], "fy":[0.14245,0.14245,0.14245,0.14245]}, - {"t":6.04088, "x":7.03861, "y":1.65756, "heading":1.5708, "vx":-1.50934, "vy":-1.31148, "omega":0.0, "ax":0.00069, "ay":-0.00079, "alpha":0.0, "fx":[0.01174,0.01174,0.01174,0.01174], "fy":[-0.01352,-0.01352,-0.01352,-0.01352]}, - {"t":6.08249, "x":6.97581, "y":1.60299, "heading":1.5708, "vx":-1.50931, "vy":-1.31151, "omega":0.0, "ax":0.00111, "ay":-0.00127, "alpha":0.0, "fx":[0.01881,0.01881,0.01881,0.01881], "fy":[-0.02166,-0.02166,-0.02166,-0.02166]}, - {"t":6.1241, "x":6.91302, "y":1.54842, "heading":1.5708, "vx":-1.50926, "vy":-1.31156, "omega":0.0, "ax":0.00118, "ay":-0.00136, "alpha":0.0, "fx":[0.02011,0.02011,0.02011,0.02011], "fy":[-0.02316,-0.02316,-0.02316,-0.02316]}, - {"t":6.1657, "x":6.85022, "y":1.49385, "heading":1.5708, "vx":-1.50921, "vy":-1.31162, "omega":0.0, "ax":0.00125, "ay":-0.00144, "alpha":0.0, "fx":[0.02124,0.02124,0.02124,0.02124], "fy":[-0.02445,-0.02445,-0.02445,-0.02445]}, - {"t":6.20731, "x":6.78743, "y":1.43928, "heading":1.5708, "vx":-1.50916, "vy":-1.31168, "omega":0.0, "ax":0.00132, "ay":-0.00152, "alpha":0.0, "fx":[0.02246,0.02246,0.02246,0.02246], "fy":[-0.02585,-0.02585,-0.02585,-0.02585]}, - {"t":6.24892, "x":6.72464, "y":1.38471, "heading":1.5708, "vx":-1.50911, "vy":-1.31174, "omega":0.0, "ax":0.0014, "ay":-0.00161, "alpha":0.0, "fx":[0.02378,0.02378,0.02378,0.02378], "fy":[-0.02738,-0.02738,-0.02738,-0.02738]}, - {"t":6.29052, "x":6.66185, "y":1.33013, "heading":1.5708, "vx":-1.50905, "vy":-1.31181, "omega":0.0, "ax":0.00148, "ay":-0.00171, "alpha":0.0, "fx":[0.02524,0.02524,0.02524,0.02524], "fy":[-0.02905,-0.02905,-0.02905,-0.02905]}, - {"t":6.33213, "x":6.59907, "y":1.27555, "heading":1.5708, "vx":-1.50899, "vy":-1.31188, "omega":0.0, "ax":0.00158, "ay":-0.00182, "alpha":0.0, "fx":[0.02686,0.02686,0.02686,0.02686], "fy":[-0.03091,-0.03091,-0.03091,-0.03091]}, - {"t":6.37374, "x":6.53629, "y":1.22096, "heading":1.5708, "vx":-1.50892, "vy":-1.31196, "omega":0.0, "ax":0.00168, "ay":-0.00194, "alpha":0.0, "fx":[0.02866,0.02866,0.02866,0.02866], "fy":[-0.03297,-0.03297,-0.03297,-0.03297]}, - {"t":6.41534, "x":6.47351, "y":1.16637, "heading":1.5708, "vx":-1.50885, "vy":-1.31204, "omega":0.0, "ax":0.0018, "ay":-0.00207, "alpha":0.0, "fx":[0.03067,0.03067,0.03067,0.03067], "fy":[-0.03528,-0.03528,-0.03528,-0.03528]}, - {"t":6.45695, "x":6.41073, "y":1.11178, "heading":1.5708, "vx":-1.50878, "vy":-1.31212, "omega":0.0, "ax":0.00194, "ay":-0.00223, "alpha":0.0, "fx":[0.03294,0.03294,0.03294,0.03294], "fy":[-0.03789,-0.03789,-0.03789,-0.03789]}, - {"t":6.49855, "x":6.34796, "y":1.05719, "heading":1.5708, "vx":-1.5087, "vy":-1.31222, "omega":0.0, "ax":0.00209, "ay":-0.0024, "alpha":0.0, "fx":[0.03553,0.03553,0.03553,0.03553], "fy":[-0.04086,-0.04086,-0.04086,-0.04086]}, - {"t":6.54016, "x":6.28519, "y":1.00259, "heading":1.5708, "vx":-1.50861, "vy":-1.31232, "omega":0.0, "ax":0.00226, "ay":-0.0026, "alpha":0.0, "fx":[0.03851,0.03851,0.03851,0.03851], "fy":[-0.04428,-0.04428,-0.04428,-0.04428]}, - {"t":6.58177, "x":6.22242, "y":0.94799, "heading":1.5708, "vx":-1.50852, "vy":-1.31242, "omega":0.0, "ax":0.00247, "ay":-0.00284, "alpha":0.0, "fx":[0.04198,0.04198,0.04198,0.04198], "fy":[-0.04827,-0.04827,-0.04827,-0.04827]}, - {"t":6.62337, "x":6.15966, "y":0.89338, "heading":1.5708, "vx":-1.50841, "vy":-1.31254, "omega":0.0, "ax":0.00271, "ay":-0.00312, "alpha":0.0, "fx":[0.04609,0.04609,0.04609,0.04609], "fy":[-0.05299,-0.05299,-0.05299,-0.05299]}, - {"t":6.66498, "x":6.0969, "y":0.83877, "heading":1.5708, "vx":-1.5083, "vy":-1.31267, "omega":0.0, "ax":0.003, "ay":-0.00345, "alpha":0.0, "fx":[0.05106,0.05106,0.05106,0.05106], "fy":[-0.05869,-0.05869,-0.05869,-0.05869]}, - {"t":6.70659, "x":6.03415, "y":0.78415, "heading":1.5708, "vx":-1.50818, "vy":-1.31282, "omega":0.0, "ax":0.00336, "ay":-0.00386, "alpha":0.0, "fx":[0.05719,0.05719,0.05719,0.05719], "fy":[-0.06572,-0.06572,-0.06572,-0.06572]}, - {"t":6.74819, "x":5.9714, "y":0.72952, "heading":1.5708, "vx":-1.50804, "vy":-1.31298, "omega":0.0, "ax":0.0038, "ay":-0.00437, "alpha":0.0, "fx":[0.0647,0.0647,0.0647,0.0647], "fy":[-0.07434,-0.07434,-0.07434,-0.07434]}, - {"t":6.7898, "x":5.90866, "y":0.67489, "heading":1.5708, "vx":-1.50788, "vy":-1.31316, "omega":0.0, "ax":0.004, "ay":-0.0046, "alpha":0.0, "fx":[0.06807,0.06807,0.06807,0.06807], "fy":[-0.07819,-0.07819,-0.07819,-0.07819]}, - {"t":6.83141, "x":5.84593, "y":0.62025, "heading":1.5708, "vx":-1.50771, "vy":-1.31335, "omega":0.0, "ax":-0.00428, "ay":0.00491, "alpha":0.0, "fx":[-0.07276,-0.07276,-0.07276,-0.07276], "fy":[0.0835,0.0835,0.0835,0.0835]}, - {"t":6.87301, "x":5.7832, "y":0.56561, "heading":1.5708, "vx":-1.50789, "vy":-1.31315, "omega":0.0, "ax":-0.20345, "ay":0.23516, "alpha":0.0, "fx":[-3.46065,-3.46065,-3.46065,-3.46065], "fy":[4.0,4.0,4.0,4.0]}, - {"t":6.91462, "x":5.72028, "y":0.51118, "heading":1.5708, "vx":-1.51635, "vy":-1.30336, "omega":0.0, "ax":-3.08479, "ay":3.99596, "alpha":0.0, "fx":[-52.47139,-52.47139,-52.47139,-52.47139], "fy":[67.97014,67.97014,67.97014,67.97014]}, - {"t":6.95622, "x":5.65452, "y":0.46041, "heading":1.5708, "vx":-1.6447, "vy":-1.1371, "omega":0.0, "ax":-4.42631, "ay":7.90595, "alpha":0.0, "fx":[-75.29027,-75.29027,-75.29027,-75.29027], "fy":[134.478,134.478,134.478,134.478]}, - {"t":6.99783, "x":5.58226, "y":0.41994, "heading":1.5708, "vx":-1.82886, "vy":-0.80817, "omega":0.0, "ax":-2.90825, "ay":8.86288, "alpha":0.0, "fx":[-49.46857,-49.46857,-49.46857,-49.46857], "fy":[150.75513,150.75513,150.75513,150.75513]}, - {"t":7.03944, "x":5.50365, "y":0.39399, "heading":1.5708, "vx":-1.94986, "vy":-0.43941, "omega":0.0, "ax":-1.22886, "ay":9.20338, "alpha":0.0, "fx":[-20.90249,-20.90249,-20.90249,-20.90249], "fy":[156.54689,156.54689,156.54689,156.54689]}, - {"t":7.0785, "x":5.42655, "y":0.38385, "heading":1.5708, "vx":-1.99787, "vy":-0.0799, "omega":0.0, "ax":0.00717, "ay":4.27724, "alpha":0.0, "fx":[0.12193,0.12193,0.12193,0.12193], "fy":[72.75465,72.75465,72.75465,72.75465]}, - {"t":7.11756, "x":5.34851, "y":0.38399, "heading":1.5708, "vx":-1.99759, "vy":0.08718, "omega":0.0, "ax":-0.03558, "ay":-1.07465, "alpha":0.0, "fx":[-0.60526,-0.60526,-0.60526,-0.60526], "fy":[-18.27949,-18.27949,-18.27949,-18.27949]}, - {"t":7.15663, "x":5.27045, "y":0.38657, "heading":1.5708, "vx":-1.99898, "vy":0.0452, "omega":0.0, "ax":-0.00592, "ay":-0.30088, "alpha":0.0, "fx":[-0.10065,-0.10065,-0.10065,-0.10065], "fy":[-5.11789,-5.11789,-5.11789,-5.11789]}, - {"t":7.19569, "x":5.19236, "y":0.38811, "heading":1.5708, "vx":-1.99921, "vy":0.03344, "omega":0.0, "ax":-0.00265, "ay":-0.17681, "alpha":0.0, "fx":[-0.0451,-0.0451,-0.0451,-0.0451], "fy":[-3.00741,-3.00741,-3.00741,-3.00741]}, - {"t":7.23475, "x":5.11427, "y":0.38928, "heading":1.5708, "vx":-1.99931, "vy":0.02654, "omega":0.0, "ax":-0.00156, "ay":-0.12984, "alpha":0.0, "fx":[-0.02651,-0.02651,-0.02651,-0.02651], "fy":[-2.20852,-2.20852,-2.20852,-2.20852]}, - {"t":7.27381, "x":5.03617, "y":0.39022, "heading":1.5708, "vx":-1.99937, "vy":0.02147, "omega":0.0, "ax":-0.00099, "ay":-0.10106, "alpha":0.0, "fx":[-0.01676,-0.01676,-0.01676,-0.01676], "fy":[-1.71907,-1.71907,-1.71907,-1.71907]}, - {"t":7.31288, "x":4.95806, "y":0.39098, "heading":1.5708, "vx":-1.99941, "vy":0.01752, "omega":0.0, "ax":-0.00064, "ay":-0.08003, "alpha":0.0, "fx":[-0.01086,-0.01086,-0.01086,-0.01086], "fy":[-1.36123,-1.36123,-1.36123,-1.36123]}, - {"t":7.35194, "x":4.87996, "y":0.3916, "heading":1.5708, "vx":-1.99944, "vy":0.01439, "omega":0.0, "ax":-0.00042, "ay":-0.06323, "alpha":0.0, "fx":[-0.00708,-0.00708,-0.00708,-0.00708], "fy":[-1.07545,-1.07545,-1.07545,-1.07545]}, - {"t":7.391, "x":4.80186, "y":0.39212, "heading":1.5708, "vx":-1.99945, "vy":0.01192, "omega":0.0, "ax":-0.00027, "ay":-0.049, "alpha":0.0, "fx":[-0.00457,-0.00457,-0.00457,-0.00457], "fy":[-0.83347,-0.83347,-0.83347,-0.83347]}, - {"t":7.43006, "x":4.72375, "y":0.39255, "heading":1.5708, "vx":-1.99946, "vy":0.01001, "omega":0.0, "ax":-0.00017, "ay":-0.03639, "alpha":0.0, "fx":[-0.00288,-0.00288,-0.00288,-0.00288], "fy":[-0.619,-0.619,-0.619,-0.619]}, - {"t":7.46913, "x":4.64565, "y":0.39291, "heading":1.5708, "vx":-1.99947, "vy":0.00859, "omega":0.0, "ax":-0.0001, "ay":-0.02475, "alpha":0.0, "fx":[-0.00171,-0.00171,-0.00171,-0.00171], "fy":[-0.42093,-0.42093,-0.42093,-0.42093]}, - {"t":7.50819, "x":4.56754, "y":0.39323, "heading":1.5708, "vx":-1.99947, "vy":0.00762, "omega":0.0, "ax":-0.00005, "ay":-0.01353, "alpha":0.0, "fx":[-0.00085,-0.00085,-0.00085,-0.00085], "fy":[-0.23021,-0.23021,-0.23021,-0.23021]}, - {"t":7.54725, "x":4.48944, "y":0.39351, "heading":1.5708, "vx":-1.99948, "vy":0.00709, "omega":0.0, "ax":-0.00001, "ay":-0.00224, "alpha":0.0, "fx":[-0.00014,-0.00014,-0.00014,-0.00014], "fy":[-0.03809,-0.03809,-0.03809,-0.03809]}, - {"t":7.58631, "x":4.41133, "y":0.39379, "heading":1.5708, "vx":-1.99948, "vy":0.007, "omega":0.0, "ax":0.00003, "ay":0.00972, "alpha":0.0, "fx":[0.00059,0.00059,0.00059,0.00059], "fy":[0.16541,0.16541,0.16541,0.16541]}, - {"t":7.62538, "x":4.33323, "y":0.39407, "heading":1.5708, "vx":-1.99947, "vy":0.00738, "omega":0.0, "ax":0.00009, "ay":0.02313, "alpha":0.0, "fx":[0.00154,0.00154,0.00154,0.00154], "fy":[0.39344,0.39344,0.39344,0.39344]}, - {"t":7.66444, "x":4.25513, "y":0.39438, "heading":1.5708, "vx":-1.99947, "vy":0.00829, "omega":0.0, "ax":0.00018, "ay":0.03913, "alpha":0.0, "fx":[0.00301,0.00301,0.00301,0.00301], "fy":[0.66563,0.66563,0.66563,0.66563]}, - {"t":7.7035, "x":4.17702, "y":0.39473, "heading":1.5708, "vx":-1.99946, "vy":0.00982, "omega":0.0, "ax":0.00033, "ay":0.05971, "alpha":0.0, "fx":[0.00557,0.00557,0.00557,0.00557], "fy":[1.01561,1.01561,1.01561,1.01561]}, - {"t":7.74257, "x":4.09892, "y":0.39516, "heading":1.5708, "vx":-1.99945, "vy":0.01215, "omega":0.0, "ax":0.00062, "ay":0.08889, "alpha":0.0, "fx":[0.01049,0.01049,0.01049,0.01049], "fy":[1.51202,1.51202,1.51202,1.51202]}, - {"t":7.78163, "x":4.02081, "y":0.3957, "heading":1.5708, "vx":-1.99943, "vy":0.01562, "omega":0.0, "ax":0.00126, "ay":0.13783, "alpha":0.0, "fx":[0.02146,0.02146,0.02146,0.02146], "fy":[2.34444,2.34444,2.34444,2.34444]}, - {"t":7.82069, "x":3.94271, "y":0.39642, "heading":1.5708, "vx":-1.99938, "vy":0.021, "omega":0.0, "ax":0.00338, "ay":0.259, "alpha":0.0, "fx":[0.05741,0.05741,0.05741,0.05741], "fy":[4.40557,4.40557,4.40557,4.40557]}, - {"t":7.85975, "x":3.86461, "y":0.39743, "heading":1.5708, "vx":-1.99925, "vy":0.03112, "omega":0.0, "ax":0.02036, "ay":0.85217, "alpha":0.0, "fx":[0.34626,0.34626,0.34626,0.34626], "fy":[14.49513,14.49513,14.49513,14.49513]}, - {"t":7.89882, "x":3.78653, "y":0.3993, "heading":1.5708, "vx":-1.99845, "vy":0.06441, "omega":0.0, "ax":-0.00506, "ay":-3.39357, "alpha":0.0, "fx":[-0.08607,-0.08607,-0.08607,-0.08607], "fy":[-57.72367,-57.72367,-57.72367,-57.72367]}, - {"t":7.93788, "x":3.70846, "y":0.39923, "heading":1.5708, "vx":-1.99865, "vy":-0.06815, "omega":0.0, "ax":-9.38431, "ay":-0.52138, "alpha":0.0, "fx":[-159.62451,-159.62451,-159.62451,-159.62451], "fy":[-8.86846,-8.86846,-8.86846,-8.86846]}, - {"t":7.96455, "x":3.65181, "y":0.39722, "heading":1.5708, "vx":-2.24896, "vy":-0.08206, "omega":0.0, "ax":-9.37545, "ay":-0.49211, "alpha":0.0, "fx":[-159.47367,-159.47367,-159.47367,-159.47367], "fy":[-8.37064,-8.37064,-8.37064,-8.37064]}, - {"t":7.99123, "x":3.58849, "y":0.39486, "heading":1.5708, "vx":-2.49904, "vy":-0.09519, "omega":0.0, "ax":-9.35858, "ay":-0.46707, "alpha":0.0, "fx":[-159.18676,-159.18676,-159.18676,-159.18676], "fy":[-7.94465,-7.94465,-7.94465,-7.94465]}, - {"t":8.0179, "x":3.5185, "y":0.39215, "heading":1.5708, "vx":-2.74866, "vy":-0.10764, "omega":0.0, "ax":-9.32131, "ay":-0.42423, "alpha":0.0, "fx":[-158.55288,-158.55288,-158.55288,-158.55288], "fy":[-7.21601,-7.21601,-7.21601,-7.21601]}, - {"t":8.04457, "x":3.44187, "y":0.38913, "heading":1.5708, "vx":-2.9973, "vy":-0.11896, "omega":0.0, "ax":-9.17884, "ay":-0.25557, "alpha":0.0, "fx":[-156.12947,-156.12947,-156.12947,-156.12947], "fy":[-4.3471,-4.3471,-4.3471,-4.3471]}, - {"t":8.07125, "x":3.35866, "y":0.38587, "heading":1.5708, "vx":-3.24213, "vy":-0.12578, "omega":0.0, "ax":8.73128, "ay":1.25448, "alpha":0.0, "fx":[148.5165,148.5165,148.5165,148.5165], "fy":[21.33836,21.33836,21.33836,21.33836]}, - {"t":8.09792, "x":3.27529, "y":0.38296, "heading":1.5708, "vx":-3.00923, "vy":-0.09232, "omega":0.0, "ax":9.26413, "ay":0.77441, "alpha":0.0, "fx":[157.5802,157.5802,157.5802,157.5802], "fy":[13.17243,13.17243,13.17243,13.17243]}, - {"t":8.12459, "x":3.19831, "y":0.38077, "heading":1.5708, "vx":-2.76213, "vy":-0.07166, "omega":0.0, "ax":9.3277, "ay":0.75242, "alpha":0.0, "fx":[158.66142,158.66142,158.66142,158.66142], "fy":[12.79846,12.79846,12.79846,12.79846]}, - {"t":8.15127, "x":3.12796, "y":0.37913, "heading":1.5708, "vx":-2.51332, "vy":-0.05159, "omega":0.0, "ax":9.34707, "ay":0.81108, "alpha":0.0, "fx":[158.99092,158.99092,158.99092,158.99092], "fy":[13.79616,13.79616,13.79616,13.79616]}, - {"t":8.17794, "x":3.06424, "y":0.37804, "heading":1.5708, "vx":-2.26401, "vy":-0.02996, "omega":0.0, "ax":9.22884, "ay":1.76267, "alpha":0.0, "fx":[156.98,156.98,156.98,156.98], "fy":[29.98252,29.98252,29.98252,29.98252]}, - {"t":8.20461, "x":3.00714, "y":0.37787, "heading":1.5708, "vx":-2.01784, "vy":0.01706, "omega":0.0, "ax":7.50691, "ay":5.67381, "alpha":0.0, "fx":[127.69044,127.69044,127.69044,127.69044], "fy":[96.50988,96.50988,96.50988,96.50988]}, - {"t":8.23129, "x":2.95598, "y":0.38034, "heading":1.5708, "vx":-1.8176, "vy":0.1684, "omega":0.0, "ax":5.23112, "ay":7.83553, "alpha":0.0, "fx":[88.97993,88.97993,88.97993,88.97993], "fy":[133.28019,133.28019,133.28019,133.28019]}, - {"t":8.25796, "x":2.90936, "y":0.38762, "heading":1.5708, "vx":-1.67807, "vy":0.3774, "omega":0.0, "ax":4.63087, "ay":7.78222, "alpha":7.07289, "fx":[69.49084,132.60169,82.23775,30.74915], "fy":[144.31323,90.08244,137.70254,157.39513]}, - {"t":8.28945, "x":2.85882, "y":0.40336, "heading":1.5708, "vx":-1.53225, "vy":0.62245, "omega":0.22271, "ax":4.69733, "ay":7.75929, "alpha":6.93826, "fx":[71.82499,132.27993,82.92631,32.5695], "fy":[143.13343,90.51671,137.27172,157.01117]}, - {"t":8.32094, "x":2.8129, "y":0.42681, "heading":1.57781, "vx":-1.38435, "vy":0.86677, "omega":0.44118, "ax":4.7793, "ay":7.72715, "alpha":6.80811, "fx":[75.23281,131.96878,83.49074,34.48595], "fy":[141.33262,90.92608,136.90785,156.57957]}, - {"t":8.35242, "x":2.77168, "y":0.45793, "heading":1.5917, "vx":-1.23386, "vy":1.11008, "omega":0.65556, "ax":4.8802, "ay":7.68318, "alpha":6.68108, "fx":[79.67288,131.66108,84.02199,36.68698], "fy":[138.82972,91.31882,136.55547,156.05043]}, - {"t":8.38391, "x":2.73525, "y":0.4967, "heading":1.61234, "vx":-1.08019, "vy":1.35201, "omega":0.86593, "ax":5.00542, "ay":7.62339, "alpha":6.55202, "fx":[85.09498,131.34112,84.65844,39.46824], "fy":[135.51359,91.7138,136.12648,155.33299]}, - {"t":8.4154, "x":2.70372, "y":0.54305, "heading":1.63961, "vx":-0.92258, "vy":1.59206, "omega":1.07224, "ax":5.16422, "ay":7.54136, "alpha":6.40441, "fx":[91.4293,130.9837,85.6299,43.32488], "fy":[131.24619,92.14026,135.46911,154.2502]}, - {"t":8.44689, "x":2.67723, "y":0.59692, "heading":1.67337, "vx":-0.75997, "vy":1.82952, "omega":1.2739, "ax":5.37396, "ay":7.42536, "alpha":6.19191, "fx":[98.57715,130.55139,87.35716,49.15256], "fy":[125.8649,92.6387,134.29139,152.41794]}, - {"t":8.47838, "x":2.65596, "y":0.65821, "heading":1.71348, "vx":-0.59075, "vy":2.06333, "omega":1.46887, "ax":5.67071, "ay":7.24959, "alpha":5.78921, "fx":[106.41041,129.9877,90.70691,58.72384], "fy":[119.17735,93.2634,131.93975,148.87319]}, - {"t":8.50986, "x":2.64017, "y":0.72677, "heading":1.75974, "vx":-0.41219, "vy":2.2916, "omega":1.65116, "ax":6.13825, "ay":6.94148, "alpha":4.83949, "fx":[114.7962,129.20041,97.76413,75.87863], "fy":[110.92071,94.08577,126.59175,140.69202]}, - {"t":8.54135, "x":2.63024, "y":0.80237, "heading":1.81173, "vx":-0.21891, "vy":2.51018, "omega":1.80355, "ax":6.9815, "ay":6.22514, "alpha":2.1942, "fx":[123.69224,128.02135,114.65059,108.64903], "fy":[100.5793,95.1789,111.01264,116.78046]}, - {"t":8.57284, "x":2.6268, "y":0.88449, "heading":1.86852, "vx":0.00092, "vy":2.70619, "omega":1.87264, "ax":8.35267, "ay":3.79753, "alpha":-6.10258, "fx":[133.51999,126.37845,154.26527,154.14264], "fy":[86.48181,96.00104,36.79239,39.10445]}, - {"t":8.60433, "x":2.63097, "y":0.97159, "heading":1.92748, "vx":0.26393, "vy":2.82577, "omega":1.68048, "ax":9.17767, "ay":0.35548, "alpha":-4.5179, "fx":[154.96101,155.42185,156.16246,157.89281], "fy":[30.74684,24.97829,-22.23404,-9.30451]}, - {"t":8.63582, "x":2.64383, "y":1.06074, "heading":1.9804, "vx":0.55291, "vy":2.83696, "omega":1.53822, "ax":7.38358, "ay":-4.78328, "alpha":7.67626, "fx":[91.93476,131.19382,155.46186,123.77998], "fy":[-128.02949,-88.07932,-18.21805,-91.12201]}, - {"t":8.6673, "x":2.6649, "y":1.1477, "heading":2.02883, "vx":0.78541, "vy":2.68635, "omega":1.77993, "ax":1.75431, "ay":-8.27464, "alpha":10.36655, "fx":[12.74821,78.7472,93.28464,-65.41898], "fy":[-158.35962,-137.8425,-124.00125,-142.79352]}, - {"t":8.69879, "x":2.6905, "y":1.22819, "heading":2.08488, "vx":0.84065, "vy":2.4258, "omega":2.10635, "ax":-1.33252, "ay":-9.2681, "alpha":0.0157, "fx":[-22.65729,-22.53514,-22.67411,-22.7963], "fy":[-157.65015,-157.6669,-157.64552,-157.62866]}, - {"t":8.73028, "x":2.71631, "y":1.29998, "heading":2.1512, "vx":0.79869, "vy":2.13396, "omega":2.10685, "ax":-2.35927, "ay":-9.00195, "alpha":-3.79755, "fx":[-47.63681,-67.7484,-33.75958,-11.37699], "fy":[-152.26429,-144.63091,-156.23893,-159.34822]}, - {"t":8.76177, "x":2.74029, "y":1.36271, "heading":2.21754, "vx":0.7244, "vy":1.85051, "omega":1.98727, "ax":-2.8449, "ay":-8.75903, "alpha":-5.98869, "fx":[-66.14864,-86.33005,-35.66603,-5.41892], "fy":[-145.3967,-134.65475,-156.03383,-159.86922]}, - {"t":8.79326, "x":2.76169, "y":1.41663, "heading":2.28012, "vx":0.63482, "vy":1.5747, "omega":1.7987, "ax":-3.1125, "ay":-8.57711, "alpha":-7.49267, "fx":[-79.89815,-95.11702,-35.19989,-1.55572], "fy":[-138.4848,-128.77136,-156.25291,-160.0679]}, - {"t":8.82474, "x":2.78014, "y":1.46197, "heading":2.33676, "vx":0.53682, "vy":1.30463, "omega":1.56277, "ax":-3.27085, "ay":-8.44188, "alpha":-8.61241, "fx":[-90.01659,-99.69118,-33.97258,1.13518], "fy":[-132.26215,-125.37621,-156.59332,-160.14418]}, - {"t":8.85623, "x":2.79542, "y":1.49886, "heading":2.38597, "vx":0.43382, "vy":1.03881, "omega":1.29158, "ax":-3.36811, "ay":-8.34145, "alpha":-9.46128, "fx":[-97.39277,-102.21389,-32.58499,3.02898], "fy":[-127.03211,-123.40583,-156.93434,-160.17029]}, - {"t":8.88772, "x":2.80741, "y":1.52744, "heading":2.42663, "vx":0.32777, "vy":0.77616, "omega":0.99367, "ax":-3.4303, "ay":-8.26702, "alpha":-10.09833, "fx":[-102.72579,-103.67837,-31.32078,4.33133], "fy":[-122.84167,-122.23401,-157.22497,-160.17823]}, - {"t":8.91921, "x":2.81603, "y":1.54778, "heading":2.45792, "vx":0.21976, "vy":0.51585, "omega":0.67569, "ax":-3.47301, "ay":-8.2118, "alpha":-10.56498, "fx":[-106.54326,-104.60481,-30.33296,5.18146], "fy":[-119.61112,-121.48395,-157.44388,-160.18278]}, - {"t":8.9507, "x":2.82123, "y":1.55995, "heading":2.4792, "vx":0.1104, "vy":0.25727, "omega":0.34302, "ax":-3.50602, "ay":-8.17052, "alpha":-10.89371, "fx":[-109.22589,-105.29124,-29.71075,5.68263], "fy":[-117.21858,-120.92162,-157.58246,-160.19028]}, - {"t":8.98218, "x":2.82297, "y":1.564, "heading":2.49, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0,0.0,0.0,0.0]}], + {"t":0.0, "x":4.42804, "y":0.39542, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":9.42861, "ay":0.00518, "alpha":0.0, "fx":[160.37803,160.37803,160.37803,160.37803], "fy":[0.08809,0.08809,0.08809,0.08809]}, + {"t":0.04662, "x":4.43829, "y":0.39543, "heading":1.5708, "vx":0.4396, "vy":0.00024, "omega":0.0, "ax":9.42724, "ay":-0.00322, "alpha":0.0, "fx":[160.35465,160.35465,160.35465,160.35465], "fy":[-0.0547,-0.0547,-0.0547,-0.0547]}, + {"t":0.09325, "x":4.46903, "y":0.39544, "heading":1.5708, "vx":0.87915, "vy":0.00009, "omega":0.0, "ax":9.42535, "ay":-0.01177, "alpha":0.0, "fx":[160.32248,160.32248,160.32248,160.32248], "fy":[-0.20013,-0.20013,-0.20013,-0.20013]}, + {"t":0.13987, "x":4.52027, "y":0.39543, "heading":1.5708, "vx":1.3186, "vy":-0.00046, "omega":0.0, "ax":9.42259, "ay":-0.02057, "alpha":0.0, "fx":[160.27558,160.27558,160.27558,160.27558], "fy":[-0.34997,-0.34997,-0.34997,-0.34997]}, + {"t":0.1865, "x":4.59199, "y":0.39539, "heading":1.5708, "vx":1.75792, "vy":-0.00142, "omega":0.0, "ax":9.41821, "ay":-0.02987, "alpha":0.0, "fx":[160.20109,160.20109,160.20109,160.20109], "fy":[-0.50804,-0.50804,-0.50804,-0.50804]}, + {"t":0.23312, "x":4.68419, "y":0.39529, "heading":1.5708, "vx":2.19704, "vy":-0.00281, "omega":0.0, "ax":9.41019, "ay":-0.04023, "alpha":0.0, "fx":[160.06467,160.06467,160.06467,160.06467], "fy":[-0.68437,-0.68437,-0.68437,-0.68437]}, + {"t":0.27975, "x":4.79685, "y":0.39511, "heading":1.5708, "vx":2.63579, "vy":-0.00468, "omega":0.0, "ax":9.39077, "ay":-0.05391, "alpha":0.0, "fx":[159.73432,159.73432,159.73432,159.73432], "fy":[-0.91705,-0.91705,-0.91705,-0.91705]}, + {"t":0.32637, "x":4.92995, "y":0.39483, "heading":1.5708, "vx":3.07363, "vy":-0.0072, "omega":0.0, "ax":9.27617, "ay":-0.09467, "alpha":0.0, "fx":[157.78502,157.78502,157.78502,157.78502], "fy":[-1.61026,-1.61026,-1.61026,-1.61026]}, + {"t":0.373, "x":5.08334, "y":0.3944, "heading":1.5708, "vx":3.50613, "vy":-0.01161, "omega":0.0, "ax":0.04183, "ay":-0.29128, "alpha":0.0, "fx":[0.7115,0.7115,0.7115,0.7115], "fy":[-4.95466,-4.95466,-4.95466,-4.95466]}, + {"t":0.41962, "x":5.24686, "y":0.39354, "heading":1.5708, "vx":3.50808, "vy":-0.02519, "omega":0.0, "ax":-0.00121, "ay":-0.14869, "alpha":0.0, "fx":[-0.02059,-0.02059,-0.02059,-0.02059], "fy":[-2.52915,-2.52915,-2.52915,-2.52915]}, + {"t":0.46625, "x":5.41042, "y":0.3922, "heading":1.5708, "vx":3.50802, "vy":-0.03213, "omega":0.0, "ax":-0.00141, "ay":-0.13986, "alpha":0.0, "fx":[-0.02399,-0.02399,-0.02399,-0.02399], "fy":[-2.37895,-2.37895,-2.37895,-2.37895]}, + {"t":0.51287, "x":5.57398, "y":0.39055, "heading":1.5708, "vx":3.50795, "vy":-0.03865, "omega":0.0, "ax":-0.00173, "ay":-0.14441, "alpha":0.0, "fx":[-0.02941,-0.02941,-0.02941,-0.02941], "fy":[-2.45642,-2.45642,-2.45642,-2.45642]}, + {"t":0.55949, "x":5.73753, "y":0.38859, "heading":1.5708, "vx":3.50787, "vy":-0.04538, "omega":0.0, "ax":-0.00212, "ay":-0.15177, "alpha":0.0, "fx":[-0.03599,-0.03599,-0.03599,-0.03599], "fy":[-2.58164,-2.58164,-2.58164,-2.58164]}, + {"t":0.60612, "x":5.90108, "y":0.38631, "heading":1.5708, "vx":3.50778, "vy":-0.05246, "omega":0.0, "ax":-0.00259, "ay":-0.16153, "alpha":0.0, "fx":[-0.04402,-0.04402,-0.04402,-0.04402], "fy":[-2.74762,-2.74762,-2.74762,-2.74762]}, + {"t":0.65274, "x":6.06463, "y":0.38369, "heading":1.5708, "vx":3.50765, "vy":-0.05999, "omega":0.0, "ax":-0.00319, "ay":-0.17454, "alpha":0.0, "fx":[-0.05419,-0.05419,-0.05419,-0.05419], "fy":[-2.96891,-2.96891,-2.96891,-2.96891]}, + {"t":0.69937, "x":6.22817, "y":0.3807, "heading":1.5708, "vx":3.50751, "vy":-0.06813, "omega":0.0, "ax":-0.00398, "ay":-0.19238, "alpha":0.0, "fx":[-0.0677,-0.0677,-0.0677,-0.0677], "fy":[-3.27226,-3.27226,-3.27226,-3.27226]}, + {"t":0.74599, "x":6.3917, "y":0.37732, "heading":1.5708, "vx":3.50732, "vy":-0.0771, "omega":0.0, "ax":-0.00509, "ay":-0.21744, "alpha":0.0, "fx":[-0.08659,-0.08659,-0.08659,-0.08659], "fy":[-3.69863,-3.69863,-3.69863,-3.69863]}, + {"t":0.79262, "x":6.55522, "y":0.37349, "heading":1.5708, "vx":3.50708, "vy":-0.08723, "omega":0.0, "ax":-0.00659, "ay":-0.24853, "alpha":0.0, "fx":[-0.11205,-0.11205,-0.11205,-0.11205], "fy":[-4.22748,-4.22748,-4.22748,-4.22748]}, + {"t":0.83924, "x":6.71873, "y":0.36915, "heading":1.5708, "vx":3.50678, "vy":-0.09882, "omega":0.0, "ax":-0.00642, "ay":-0.21481, "alpha":0.0, "fx":[-0.10917,-0.10917,-0.10917,-0.10917], "fy":[-3.65392,-3.65392,-3.65392,-3.65392]}, + {"t":0.88587, "x":6.88223, "y":0.36431, "heading":1.5708, "vx":3.50648, "vy":-0.10884, "omega":0.0, "ax":-0.659, "ay":0.81103, "alpha":0.0, "fx":[-11.20936,-11.20936,-11.20936,-11.20936], "fy":[13.79533,13.79533,13.79533,13.79533]}, + {"t":0.93249, "x":7.045, "y":0.36012, "heading":1.5708, "vx":3.47575, "vy":-0.07102, "omega":0.0, "ax":-9.3587, "ay":0.38608, "alpha":0.0, "fx":[-159.18881,-159.18881,-159.18881,-159.18881], "fy":[6.56705,6.56705,6.56705,6.56705]}, + {"t":0.97912, "x":7.19688, "y":0.35723, "heading":1.5708, "vx":3.03941, "vy":-0.05302, "omega":0.0, "ax":-9.39441, "ay":0.41231, "alpha":0.0, "fx":[-159.79617,-159.79617,-159.79617,-159.79617], "fy":[7.01325,7.01325,7.01325,7.01325]}, + {"t":1.02574, "x":7.32838, "y":0.3552, "heading":1.5708, "vx":2.6014, "vy":-0.0338, "omega":0.0, "ax":-9.40412, "ay":0.45441, "alpha":0.0, "fx":[-159.96143,-159.96143,-159.96143,-159.96143], "fy":[7.72943,7.72943,7.72943,7.72943]}, + {"t":1.07236, "x":7.43945, "y":0.35412, "heading":1.5708, "vx":2.16293, "vy":-0.01261, "omega":0.0, "ax":-9.40714, "ay":0.50707, "alpha":0.0, "fx":[-160.01282,-160.01282,-160.01282,-160.01282], "fy":[8.62515,8.62515,8.62515,8.62515]}, + {"t":1.11899, "x":7.53007, "y":0.35408, "heading":1.5708, "vx":1.72433, "vy":0.01103, "omega":0.0, "ax":-9.40715, "ay":0.56644, "alpha":0.0, "fx":[-160.01288,-160.01288,-160.01288,-160.01288], "fy":[9.63497,9.63497,9.63497,9.63497]}, + {"t":1.16561, "x":7.60024, "y":0.35521, "heading":1.5708, "vx":1.28573, "vy":0.03744, "omega":0.0, "ax":-9.40555, "ay":0.62695, "alpha":0.0, "fx":[-159.98579,-159.98579,-159.98579,-159.98579], "fy":[10.66431,10.66431,10.66431,10.66431]}, + {"t":1.21224, "x":7.64996, "y":0.35764, "heading":1.5708, "vx":0.8472, "vy":0.06667, "omega":0.0, "ax":-2.41208, "ay":7.80152, "alpha":0.0, "fx":[-41.02878,-41.02878,-41.02878,-41.02878], "fy":[132.70166,132.70166,132.70166,132.70166]}, + {"t":1.25793, "x":7.68615, "y":0.36883, "heading":1.5708, "vx":0.73699, "vy":0.42313, "omega":0.0, "ax":-0.1008, "ay":0.17342, "alpha":0.0, "fx":[-1.71457,-1.71457,-1.71457,-1.71457], "fy":[2.94975,2.94975,2.94975,2.94975]}, + {"t":1.30362, "x":7.71972, "y":0.38834, "heading":1.5708, "vx":0.73238, "vy":0.43105, "omega":0.0, "ax":0.00253, "ay":-0.0043, "alpha":0.0, "fx":[0.043,0.043,0.043,0.043], "fy":[-0.0731,-0.0731,-0.0731,-0.0731]}, + {"t":1.34931, "x":7.75319, "y":0.40803, "heading":1.5708, "vx":0.7325, "vy":0.43085, "omega":0.0, "ax":0.00234, "ay":-0.00398, "alpha":0.0, "fx":[0.03976,0.03976,0.03976,0.03976], "fy":[-0.06764,-0.06764,-0.06764,-0.06764]}, + {"t":1.395, "x":7.78666, "y":0.42771, "heading":1.5708, "vx":0.7326, "vy":0.43067, "omega":0.0, "ax":0.00186, "ay":-0.00317, "alpha":0.0, "fx":[0.03164,0.03164,0.03164,0.03164], "fy":[-0.05385,-0.05385,-0.05385,-0.05385]}, + {"t":1.44069, "x":7.82013, "y":0.44739, "heading":1.5708, "vx":0.73269, "vy":0.43053, "omega":0.0, "ax":0.00156, "ay":-0.00265, "alpha":0.0, "fx":[0.02647,0.02647,0.02647,0.02647], "fy":[-0.04507,-0.04507,-0.04507,-0.04507]}, + {"t":1.48638, "x":7.85361, "y":0.46706, "heading":1.5708, "vx":0.73276, "vy":0.43041, "omega":0.0, "ax":0.00135, "ay":-0.00229, "alpha":0.0, "fx":[0.02289,0.02289,0.02289,0.02289], "fy":[-0.03898,-0.03898,-0.03898,-0.03898]}, + {"t":1.53207, "x":7.88709, "y":0.48672, "heading":1.5708, "vx":0.73282, "vy":0.4303, "omega":0.0, "ax":0.00119, "ay":-0.00203, "alpha":0.0, "fx":[0.02024,0.02024,0.02024,0.02024], "fy":[-0.03448,-0.03448,-0.03448,-0.03448]}, + {"t":1.57776, "x":7.92058, "y":0.50638, "heading":1.5708, "vx":0.73288, "vy":0.43021, "omega":0.0, "ax":0.00107, "ay":-0.00182, "alpha":0.0, "fx":[0.0182,0.0182,0.0182,0.0182], "fy":[-0.03101,-0.03101,-0.03101,-0.03101]}, + {"t":1.62345, "x":7.95406, "y":0.52603, "heading":1.5708, "vx":0.73292, "vy":0.43012, "omega":0.0, "ax":0.00097, "ay":-0.00166, "alpha":0.0, "fx":[0.01657,0.01657,0.01657,0.01657], "fy":[-0.02824,-0.02824,-0.02824,-0.02824]}, + {"t":1.66914, "x":7.98755, "y":0.54568, "heading":1.5708, "vx":0.73297, "vy":0.43005, "omega":0.0, "ax":0.0009, "ay":-0.00153, "alpha":0.0, "fx":[0.01523,0.01523,0.01523,0.01523], "fy":[-0.02597,-0.02597,-0.02597,-0.02597]}, + {"t":1.71483, "x":8.02104, "y":0.56533, "heading":1.5708, "vx":0.73301, "vy":0.42998, "omega":0.0, "ax":0.00083, "ay":-0.00141, "alpha":0.0, "fx":[0.01411,0.01411,0.01411,0.01411], "fy":[-0.02407,-0.02407,-0.02407,-0.02407]}, + {"t":1.76052, "x":8.05454, "y":0.58498, "heading":1.5708, "vx":0.73305, "vy":0.42991, "omega":0.0, "ax":0.00077, "ay":-0.00132, "alpha":0.0, "fx":[0.01316,0.01316,0.01316,0.01316], "fy":[-0.02245,-0.02245,-0.02245,-0.02245]}, + {"t":1.80621, "x":8.08803, "y":0.60462, "heading":1.5708, "vx":0.73308, "vy":0.42985, "omega":0.0, "ax":0.00073, "ay":-0.00124, "alpha":0.0, "fx":[0.01234,0.01234,0.01234,0.01234], "fy":[-0.02106,-0.02106,-0.02106,-0.02106]}, + {"t":1.8519, "x":8.12152, "y":0.62426, "heading":1.5708, "vx":0.73312, "vy":0.4298, "omega":0.0, "ax":0.00068, "ay":-0.00117, "alpha":0.0, "fx":[0.01163,0.01163,0.01163,0.01163], "fy":[-0.01984,-0.01984,-0.01984,-0.01984]}, + {"t":1.89759, "x":8.15502, "y":0.64389, "heading":1.5708, "vx":0.73315, "vy":0.42974, "omega":0.0, "ax":0.00065, "ay":-0.0011, "alpha":0.0, "fx":[0.01099,0.01099,0.01099,0.01099], "fy":[-0.01876,-0.01876,-0.01876,-0.01876]}, + {"t":1.94328, "x":8.18852, "y":0.66353, "heading":1.5708, "vx":0.73318, "vy":0.42969, "omega":0.0, "ax":0.00061, "ay":-0.00105, "alpha":0.0, "fx":[0.01043,0.01043,0.01043,0.01043], "fy":[-0.01781,-0.01781,-0.01781,-0.01781]}, + {"t":1.98897, "x":8.22202, "y":0.68316, "heading":1.5708, "vx":0.7332, "vy":0.42965, "omega":0.0, "ax":0.00058, "ay":-0.001, "alpha":0.0, "fx":[0.00993,0.00993,0.00993,0.00993], "fy":[-0.01695,-0.01695,-0.01695,-0.01695]}, + {"t":2.03466, "x":8.25552, "y":0.70279, "heading":1.5708, "vx":0.73323, "vy":0.4296, "omega":0.0, "ax":0.00056, "ay":-0.00095, "alpha":0.0, "fx":[0.00947,0.00947,0.00947,0.00947], "fy":[-0.01618,-0.01618,-0.01618,-0.01618]}, + {"t":2.08035, "x":8.28902, "y":0.72242, "heading":1.5708, "vx":0.73326, "vy":0.42956, "omega":0.0, "ax":0.00053, "ay":-0.00091, "alpha":0.0, "fx":[0.00906,0.00906,0.00906,0.00906], "fy":[-0.01547,-0.01547,-0.01547,-0.01547]}, + {"t":2.12605, "x":8.32253, "y":0.74204, "heading":1.5708, "vx":0.73328, "vy":0.42952, "omega":0.0, "ax":0.00051, "ay":-0.00087, "alpha":0.0, "fx":[0.00868,0.00868,0.00868,0.00868], "fy":[-0.01483,-0.01483,-0.01483,-0.01483]}, + {"t":2.17174, "x":8.35603, "y":0.76167, "heading":1.5708, "vx":0.7333, "vy":0.42948, "omega":0.0, "ax":0.00049, "ay":-0.00084, "alpha":0.0, "fx":[0.00834,0.00834,0.00834,0.00834], "fy":[-0.01424,-0.01424,-0.01424,-0.01424]}, + {"t":2.21743, "x":8.38954, "y":0.78129, "heading":1.5708, "vx":0.73333, "vy":0.42944, "omega":0.0, "ax":0.00046, "ay":-0.00079, "alpha":0.0, "fx":[0.00786,0.00786,0.00786,0.00786], "fy":[-0.01343,-0.01343,-0.01343,-0.01343]}, + {"t":2.26312, "x":8.42304, "y":0.80091, "heading":1.5708, "vx":0.73335, "vy":0.4294, "omega":0.0, "ax":-0.00096, "ay":0.00164, "alpha":0.0, "fx":[-0.01636,-0.01636,-0.01636,-0.01636], "fy":[0.02794,0.02794,0.02794,0.02794]}, + {"t":2.30881, "x":8.45655, "y":0.82053, "heading":1.5708, "vx":0.7333, "vy":0.42948, "omega":0.0, "ax":-0.21514, "ay":0.35801, "alpha":0.0, "fx":[-3.65942,-3.65942,-3.65942,-3.65942], "fy":[6.08958,6.08958,6.08958,6.08958]}, + {"t":2.3545, "x":8.48983, "y":0.84053, "heading":1.5708, "vx":0.72347, "vy":0.44583, "omega":0.0, "ax":-6.23394, "ay":6.17085, "alpha":0.0, "fx":[-106.0376,-106.0376,-106.0376,-106.0376], "fy":[104.96446,104.96446,104.96446,104.96446]}, + {"t":2.40019, "x":8.51638, "y":0.86734, "heading":1.5708, "vx":0.43864, "vy":0.72778, "omega":0.0, "ax":-9.02411, "ay":2.65352, "alpha":0.0, "fx":[-153.49757,-153.49757,-153.49757,-153.49757], "fy":[45.13561,45.13561,45.13561,45.13561]}, + {"t":2.44588, "x":8.527, "y":0.90336, "heading":1.5708, "vx":0.02633, "vy":0.84902, "omega":0.0, "ax":-1.05587, "ay":0.00825, "alpha":0.0, "fx":[-17.96013,-17.96013,-17.96013,-17.96013], "fy":[0.14035,0.14035,0.14035,0.14035]}, + {"t":2.49545, "x":8.52701, "y":0.94546, "heading":1.5708, "vx":-0.02602, "vy":0.84943, "omega":0.0, "ax":1.03702, "ay":0.00038, "alpha":0.0, "fx":[17.63935,17.63935,17.63935,17.63935], "fy":[0.00646,0.00646,0.00646,0.00646]}, + {"t":2.54503, "x":8.52699, "y":0.98757, "heading":1.5708, "vx":0.02539, "vy":0.84945, "omega":0.0, "ax":-1.01204, "ay":0.00037, "alpha":0.0, "fx":[-17.21447,-17.21447,-17.21447,-17.21447], "fy":[0.00624,0.00624,0.00624,0.00624]}, + {"t":2.5946, "x":8.52701, "y":1.02968, "heading":1.5708, "vx":-0.02478, "vy":0.84947, "omega":0.0, "ax":0.98731, "ay":0.00035, "alpha":0.0, "fx":[16.79393,16.79393,16.79393,16.79393], "fy":[0.00602,0.00602,0.00602,0.00602]}, + {"t":2.64418, "x":8.52699, "y":1.0718, "heading":1.5708, "vx":0.02417, "vy":0.84949, "omega":0.0, "ax":-0.96286, "ay":0.00034, "alpha":0.0, "fx":[-16.37792,-16.37792,-16.37792,-16.37792], "fy":[0.00581,0.00581,0.00581,0.00581]}, + {"t":2.69375, "x":8.52701, "y":1.11391, "heading":1.5708, "vx":-0.02357, "vy":0.8495, "omega":0.0, "ax":0.93868, "ay":0.00033, "alpha":0.0, "fx":[15.96662,15.96662,15.96662,15.96662], "fy":[0.0056,0.0056,0.0056,0.0056]}, + {"t":2.74333, "x":8.52699, "y":1.15603, "heading":1.5708, "vx":0.02297, "vy":0.84952, "omega":0.0, "ax":-0.91478, "ay":0.00032, "alpha":0.0, "fx":[-15.56022,-15.56022,-15.56022,-15.56022], "fy":[0.00539,0.00539,0.00539,0.00539]}, + {"t":2.79291, "x":8.52701, "y":1.19814, "heading":1.5708, "vx":-0.02238, "vy":0.84953, "omega":0.0, "ax":0.89119, "ay":0.0003, "alpha":0.0, "fx":[15.15893,15.15893,15.15893,15.15893], "fy":[0.00518,0.00518,0.00518,0.00518]}, + {"t":2.84248, "x":8.52699, "y":1.24026, "heading":1.5708, "vx":0.0218, "vy":0.84955, "omega":0.0, "ax":-0.86791, "ay":0.00029, "alpha":0.0, "fx":[-14.76296,-14.76296,-14.76296,-14.76296], "fy":[0.00498,0.00498,0.00498,0.00498]}, + {"t":2.89206, "x":8.52701, "y":1.28238, "heading":1.5708, "vx":-0.02123, "vy":0.84956, "omega":0.0, "ax":0.84496, "ay":0.00028, "alpha":0.0, "fx":[14.37252,14.37252,14.37252,14.37252], "fy":[0.00478,0.00478,0.00478,0.00478]}, + {"t":2.94163, "x":8.52699, "y":1.32449, "heading":1.5708, "vx":0.02066, "vy":0.84958, "omega":0.0, "ax":-0.82234, "ay":0.00027, "alpha":0.0, "fx":[-13.98784,-13.98784,-13.98784,-13.98784], "fy":[0.00458,0.00458,0.00458,0.00458]}, + {"t":2.99121, "x":8.52701, "y":1.36661, "heading":1.5708, "vx":-0.02011, "vy":0.84959, "omega":0.0, "ax":0.80008, "ay":0.00026, "alpha":0.0, "fx":[13.60915,13.60915,13.60915,13.60915], "fy":[0.00438,0.00438,0.00438,0.00438]}, + {"t":3.04078, "x":8.52699, "y":1.40873, "heading":1.5708, "vx":0.01956, "vy":0.8496, "omega":0.0, "ax":-0.77818, "ay":0.00025, "alpha":0.0, "fx":[-13.23669,-13.23669,-13.23669,-13.23669], "fy":[0.00419,0.00419,0.00419,0.00419]}, + {"t":3.09036, "x":8.52701, "y":1.45085, "heading":1.5708, "vx":-0.01902, "vy":0.84962, "omega":0.0, "ax":0.75667, "ay":0.00024, "alpha":0.0, "fx":[12.87072,12.87072,12.87072,12.87072], "fy":[0.004,0.004,0.004,0.004]}, + {"t":3.13993, "x":8.52699, "y":1.49297, "heading":1.5708, "vx":0.01849, "vy":0.84963, "omega":0.0, "ax":-0.73555, "ay":0.00022, "alpha":0.0, "fx":[-12.51149,-12.51149,-12.51149,-12.51149], "fy":[0.00382,0.00382,0.00382,0.00382]}, + {"t":3.18951, "x":8.52701, "y":1.53509, "heading":1.5708, "vx":-0.01797, "vy":0.84964, "omega":0.0, "ax":0.71484, "ay":0.00021, "alpha":0.0, "fx":[12.15926,12.15926,12.15926,12.15926], "fy":[0.00363,0.00363,0.00363,0.00363]}, + {"t":3.23908, "x":8.52699, "y":1.57721, "heading":1.5708, "vx":0.01747, "vy":0.84965, "omega":0.0, "ax":-0.69456, "ay":0.0002, "alpha":0.0, "fx":[-11.81431,-11.81431,-11.81431,-11.81431], "fy":[0.00346,0.00346,0.00346,0.00346]}, + {"t":3.28866, "x":8.52701, "y":1.61934, "heading":1.5708, "vx":-0.01697, "vy":0.84966, "omega":0.0, "ax":0.67473, "ay":0.00019, "alpha":0.0, "fx":[11.47693,11.47693,11.47693,11.47693], "fy":[0.00328,0.00328,0.00328,0.00328]}, + {"t":3.33824, "x":8.52699, "y":1.66146, "heading":1.5708, "vx":0.01648, "vy":0.84967, "omega":0.0, "ax":-0.65535, "ay":0.00018, "alpha":0.0, "fx":[-11.14738,-11.14738,-11.14738,-11.14738], "fy":[0.00311,0.00311,0.00311,0.00311]}, + {"t":3.38781, "x":8.52701, "y":1.70358, "heading":1.5708, "vx":-0.01601, "vy":0.84968, "omega":0.0, "ax":0.63646, "ay":0.00017, "alpha":0.0, "fx":[10.82598,10.82598,10.82598,10.82598], "fy":[0.00294,0.00294,0.00294,0.00294]}, + {"t":3.43739, "x":8.52699, "y":1.7457, "heading":1.5708, "vx":0.01555, "vy":0.84969, "omega":0.0, "ax":-0.61806, "ay":0.00016, "alpha":0.0, "fx":[-10.51301,-10.51301,-10.51301,-10.51301], "fy":[0.00278,0.00278,0.00278,0.00278]}, + {"t":3.48696, "x":8.52701, "y":1.78783, "heading":1.5708, "vx":-0.0151, "vy":0.8497, "omega":0.0, "ax":0.60017, "ay":0.00015, "alpha":0.0, "fx":[10.20879,10.20879,10.20879,10.20879], "fy":[0.00262,0.00262,0.00262,0.00262]}, + {"t":3.53654, "x":8.52699, "y":1.82995, "heading":1.5708, "vx":0.01466, "vy":0.8497, "omega":0.0, "ax":-0.58282, "ay":0.00015, "alpha":0.0, "fx":[-9.91362,-9.91362,-9.91362,-9.91362], "fy":[0.00247,0.00247,0.00247,0.00247]}, + {"t":3.58611, "x":8.52701, "y":1.87208, "heading":1.5708, "vx":-0.01424, "vy":0.84971, "omega":0.0, "ax":0.56602, "ay":0.00014, "alpha":0.0, "fx":[9.62782,9.62782,9.62782,9.62782], "fy":[0.00232,0.00232,0.00232,0.00232]}, + {"t":3.63569, "x":8.52699, "y":1.9142, "heading":1.5708, "vx":0.01383, "vy":0.84972, "omega":0.0, "ax":-0.54979, "ay":0.00013, "alpha":0.0, "fx":[-9.35169,-9.35169,-9.35169,-9.35169], "fy":[0.00217,0.00217,0.00217,0.00217]}, + {"t":3.68526, "x":8.527, "y":1.95633, "heading":1.5708, "vx":-0.01343, "vy":0.84972, "omega":0.0, "ax":0.53414, "ay":0.00012, "alpha":0.0, "fx":[9.08556,9.08556,9.08556,9.08556], "fy":[0.00203,0.00203,0.00203,0.00203]}, + {"t":3.73484, "x":8.527, "y":1.99845, "heading":1.5708, "vx":0.01305, "vy":0.84973, "omega":0.0, "ax":-0.5191, "ay":0.00011, "alpha":0.0, "fx":[-8.82975,-8.82975,-8.82975,-8.82975], "fy":[0.0019,0.0019,0.0019,0.0019]}, + {"t":3.78441, "x":8.527, "y":2.04058, "heading":1.5708, "vx":-0.01268, "vy":0.84973, "omega":0.0, "ax":0.50469, "ay":0.0001, "alpha":0.0, "fx":[8.58457,8.58457,8.58457,8.58457], "fy":[0.00176,0.00176,0.00176,0.00176]}, + {"t":3.83399, "x":8.527, "y":2.08271, "heading":1.5708, "vx":0.01234, "vy":0.84974, "omega":0.0, "ax":-0.49092, "ay":0.0001, "alpha":0.0, "fx":[-8.35033,-8.35033,-8.35033,-8.35033], "fy":[0.00164,0.00164,0.00164,0.00164]}, + {"t":3.88357, "x":8.527, "y":2.12483, "heading":1.5708, "vx":-0.012, "vy":0.84974, "omega":0.0, "ax":0.47781, "ay":0.00009, "alpha":0.0, "fx":[8.12735,8.12735,8.12735,8.12735], "fy":[0.00151,0.00151,0.00151,0.00151]}, + {"t":3.93314, "x":8.527, "y":2.16696, "heading":1.5708, "vx":0.01169, "vy":0.84975, "omega":0.0, "ax":-0.46538, "ay":0.00008, "alpha":0.0, "fx":[-7.91594,-7.91594,-7.91594,-7.91594], "fy":[0.00139,0.00139,0.00139,0.00139]}, + {"t":3.98272, "x":8.527, "y":2.20908, "heading":1.5708, "vx":-0.01139, "vy":0.84975, "omega":0.0, "ax":0.45365, "ay":0.00008, "alpha":0.0, "fx":[7.71638,7.71638,7.71638,7.71638], "fy":[0.00128,0.00128,0.00128,0.00128]}, + {"t":4.03229, "x":8.527, "y":2.25121, "heading":1.5708, "vx":0.0111, "vy":0.84976, "omega":0.0, "ax":-0.44263, "ay":0.00007, "alpha":0.0, "fx":[-7.52898,-7.52898,-7.52898,-7.52898], "fy":[0.00117,0.00117,0.00117,0.00117]}, + {"t":4.08187, "x":8.527, "y":2.29334, "heading":1.5708, "vx":-0.01084, "vy":0.84976, "omega":0.0, "ax":0.43234, "ay":0.00006, "alpha":0.0, "fx":[7.35401,7.35401,7.35401,7.35401], "fy":[0.00106,0.00106,0.00106,0.00106]}, + {"t":4.13144, "x":8.527, "y":2.33547, "heading":1.5708, "vx":0.01059, "vy":0.84976, "omega":0.0, "ax":-0.4228, "ay":0.00006, "alpha":0.0, "fx":[-7.19175,-7.19175,-7.19175,-7.19175], "fy":[0.00096,0.00096,0.00096,0.00096]}, + {"t":4.18102, "x":8.527, "y":2.37759, "heading":1.5708, "vx":-0.01037, "vy":0.84977, "omega":0.0, "ax":0.41402, "ay":0.00005, "alpha":0.0, "fx":[7.04244,7.04244,7.04244,7.04244], "fy":[0.00086,0.00086,0.00086,0.00086]}, + {"t":4.23059, "x":8.527, "y":2.41972, "heading":1.5708, "vx":0.01016, "vy":0.84977, "omega":0.0, "ax":-0.40602, "ay":0.00005, "alpha":0.0, "fx":[-6.90633,-6.90633,-6.90633,-6.90633], "fy":[0.00077,0.00077,0.00077,0.00077]}, + {"t":4.28017, "x":8.527, "y":2.46185, "heading":1.5708, "vx":-0.00997, "vy":0.84977, "omega":0.0, "ax":0.39881, "ay":0.00004, "alpha":0.0, "fx":[6.78363,6.78363,6.78363,6.78363], "fy":[0.00067,0.00067,0.00067,0.00067]}, + {"t":4.32974, "x":8.527, "y":2.50398, "heading":1.5708, "vx":0.0098, "vy":0.84977, "omega":0.0, "ax":-0.3924, "ay":0.00003, "alpha":0.0, "fx":[-6.67457,-6.67457,-6.67457,-6.67457], "fy":[0.00058,0.00058,0.00058,0.00058]}, + {"t":4.37932, "x":8.527, "y":2.5461, "heading":1.5708, "vx":-0.00965, "vy":0.84977, "omega":0.0, "ax":0.3868, "ay":0.00003, "alpha":0.0, "fx":[6.57931,6.57931,6.57931,6.57931], "fy":[0.0005,0.0005,0.0005,0.0005]}, + {"t":4.4289, "x":8.527, "y":2.58823, "heading":1.5708, "vx":0.00952, "vy":0.84978, "omega":0.0, "ax":-0.38202, "ay":0.00002, "alpha":0.0, "fx":[-6.49803,-6.49803,-6.49803,-6.49803], "fy":[0.00041,0.00041,0.00041,0.00041]}, + {"t":4.47847, "x":8.527, "y":2.63036, "heading":1.5708, "vx":-0.00942, "vy":0.84978, "omega":0.0, "ax":0.37807, "ay":0.00002, "alpha":0.0, "fx":[6.43086,6.43086,6.43086,6.43086], "fy":[0.00033,0.00033,0.00033,0.00033]}, + {"t":4.52805, "x":8.527, "y":2.67249, "heading":1.5708, "vx":0.00933, "vy":0.84978, "omega":0.0, "ax":-0.37496, "ay":0.00001, "alpha":0.0, "fx":[-6.37792,-6.37792,-6.37792,-6.37792], "fy":[0.00025,0.00025,0.00025,0.00025]}, + {"t":4.57762, "x":8.527, "y":2.71462, "heading":1.5708, "vx":-0.00926, "vy":0.84978, "omega":0.0, "ax":0.37269, "ay":0.00001, "alpha":0.0, "fx":[6.33932,6.33932,6.33932,6.33932], "fy":[0.00017,0.00017,0.00017,0.00017]}, + {"t":4.6272, "x":8.527, "y":2.75675, "heading":1.5708, "vx":0.00922, "vy":0.84978, "omega":0.0, "ax":-0.37126, "ay":0.00001, "alpha":0.0, "fx":[-6.31511,-6.31511,-6.31511,-6.31511], "fy":[0.00009,0.00009,0.00009,0.00009]}, + {"t":4.67677, "x":8.527, "y":2.79887, "heading":1.5708, "vx":-0.00919, "vy":0.84978, "omega":0.0, "ax":0.37069, "ay":0.0, "alpha":0.0, "fx":[6.30533,6.30533,6.30533,6.30533], "fy":[0.00001,0.00001,0.00001,0.00001]}, + {"t":4.72635, "x":8.527, "y":2.841, "heading":1.5708, "vx":0.00919, "vy":0.84978, "omega":0.0, "ax":-0.37097, "ay":0.0, "alpha":0.0, "fx":[-6.31002,-6.31002,-6.31002,-6.31002], "fy":[-0.00006,-0.00006,-0.00006,-0.00006]}, + {"t":4.77592, "x":8.527, "y":2.88313, "heading":1.5708, "vx":-0.0092, "vy":0.84978, "omega":0.0, "ax":0.37209, "ay":-0.00001, "alpha":0.0, "fx":[6.32915,6.32915,6.32915,6.32915], "fy":[-0.00014,-0.00014,-0.00014,-0.00014]}, + {"t":4.8255, "x":8.527, "y":2.92526, "heading":1.5708, "vx":0.00924, "vy":0.84978, "omega":0.0, "ax":-0.37406, "ay":-0.00001, "alpha":0.0, "fx":[-6.3627,-6.3627,-6.3627,-6.3627], "fy":[-0.00022,-0.00022,-0.00022,-0.00022]}, + {"t":4.87507, "x":8.527, "y":2.96739, "heading":1.5708, "vx":-0.0093, "vy":0.84978, "omega":0.0, "ax":0.37688, "ay":-0.00002, "alpha":0.0, "fx":[6.4106,6.4106,6.4106,6.4106], "fy":[-0.0003,-0.0003,-0.0003,-0.0003]}, + {"t":4.92465, "x":8.527, "y":3.00951, "heading":1.5708, "vx":0.00938, "vy":0.84978, "omega":0.0, "ax":-0.38053, "ay":-0.00002, "alpha":0.0, "fx":[-6.47277,-6.47277,-6.47277,-6.47277], "fy":[-0.00038,-0.00038,-0.00038,-0.00038]}, + {"t":4.97423, "x":8.527, "y":3.05164, "heading":1.5708, "vx":-0.00948, "vy":0.84978, "omega":0.0, "ax":0.38502, "ay":-0.00003, "alpha":0.0, "fx":[6.5491,6.5491,6.5491,6.5491], "fy":[-0.00047,-0.00047,-0.00047,-0.00047]}, + {"t":5.0238, "x":8.527, "y":3.09377, "heading":1.5708, "vx":0.0096, "vy":0.84978, "omega":0.0, "ax":-0.39033, "ay":-0.00003, "alpha":0.0, "fx":[-6.63945,-6.63945,-6.63945,-6.63945], "fy":[-0.00055,-0.00055,-0.00055,-0.00055]}, + {"t":5.07338, "x":8.527, "y":3.1359, "heading":1.5708, "vx":-0.00975, "vy":0.84977, "omega":0.0, "ax":0.39646, "ay":-0.00004, "alpha":0.0, "fx":[6.74367,6.74367,6.74367,6.74367], "fy":[-0.00064,-0.00064,-0.00064,-0.00064]}, + {"t":5.12295, "x":8.527, "y":3.17803, "heading":1.5708, "vx":0.00991, "vy":0.84977, "omega":0.0, "ax":-0.40339, "ay":-0.00004, "alpha":0.0, "fx":[-6.86158,-6.86158,-6.86158,-6.86158], "fy":[-0.00073,-0.00073,-0.00073,-0.00073]}, + {"t":5.17253, "x":8.527, "y":3.22015, "heading":1.5708, "vx":-0.01009, "vy":0.84977, "omega":0.0, "ax":0.41112, "ay":-0.00005, "alpha":0.0, "fx":[6.99299,6.99299,6.99299,6.99299], "fy":[-0.00083,-0.00083,-0.00083,-0.00083]}, + {"t":5.2221, "x":8.527, "y":3.26228, "heading":1.5708, "vx":0.01029, "vy":0.84977, "omega":0.0, "ax":-0.41962, "ay":-0.00005, "alpha":0.0, "fx":[-7.13767,-7.13767,-7.13767,-7.13767], "fy":[-0.00093,-0.00093,-0.00093,-0.00093]}, + {"t":5.27168, "x":8.527, "y":3.30441, "heading":1.5708, "vx":-0.01051, "vy":0.84976, "omega":0.0, "ax":0.4289, "ay":-0.00006, "alpha":0.0, "fx":[7.29539,7.29539,7.29539,7.29539], "fy":[-0.00103,-0.00103,-0.00103,-0.00103]}, + {"t":5.32125, "x":8.527, "y":3.34654, "heading":1.5708, "vx":0.01075, "vy":0.84976, "omega":0.0, "ax":-0.43892, "ay":-0.00007, "alpha":0.0, "fx":[-7.46591,-7.46591,-7.46591,-7.46591], "fy":[-0.00113,-0.00113,-0.00113,-0.00113]}, + {"t":5.37083, "x":8.527, "y":3.38866, "heading":1.5708, "vx":-0.01101, "vy":0.84976, "omega":0.0, "ax":0.44968, "ay":-0.00007, "alpha":0.0, "fx":[7.64895,7.64895,7.64895,7.64895], "fy":[-0.00124,-0.00124,-0.00124,-0.00124]}, + {"t":5.4204, "x":8.527, "y":3.43079, "heading":1.5708, "vx":0.01128, "vy":0.84975, "omega":0.0, "ax":-0.46116, "ay":-0.00008, "alpha":0.0, "fx":[-7.84425,-7.84425,-7.84425,-7.84425], "fy":[-0.00135,-0.00135,-0.00135,-0.00135]}, + {"t":5.46998, "x":8.527, "y":3.47292, "heading":1.5708, "vx":-0.01158, "vy":0.84975, "omega":0.0, "ax":0.47335, "ay":-0.00009, "alpha":0.0, "fx":[8.0515,8.0515,8.0515,8.0515], "fy":[-0.00147,-0.00147,-0.00147,-0.00147]}, + {"t":5.51956, "x":8.527, "y":3.51504, "heading":1.5708, "vx":0.01189, "vy":0.84975, "omega":0.0, "ax":-0.48622, "ay":-0.00009, "alpha":0.0, "fx":[-8.27043,-8.27043,-8.27043,-8.27043], "fy":[-0.00159,-0.00159,-0.00159,-0.00159]}, + {"t":5.56913, "x":8.527, "y":3.55717, "heading":1.5708, "vx":-0.01222, "vy":0.84974, "omega":0.0, "ax":0.49976, "ay":-0.0001, "alpha":0.0, "fx":[8.50071,8.50071,8.50071,8.50071], "fy":[-0.00172,-0.00172,-0.00172,-0.00172]}, + {"t":5.61871, "x":8.527, "y":3.5993, "heading":1.5708, "vx":0.01256, "vy":0.84974, "omega":0.0, "ax":-0.51394, "ay":-0.00011, "alpha":0.0, "fx":[-8.74206,-8.74206,-8.74206,-8.74206], "fy":[-0.00185,-0.00185,-0.00185,-0.00185]}, + {"t":5.66828, "x":8.527, "y":3.64142, "heading":1.5708, "vx":-0.01292, "vy":0.84973, "omega":0.0, "ax":0.52876, "ay":-0.00012, "alpha":0.0, "fx":[8.99414,8.99414,8.99414,8.99414], "fy":[-0.00198,-0.00198,-0.00198,-0.00198]}, + {"t":5.71786, "x":8.527, "y":3.68355, "heading":1.5708, "vx":0.01329, "vy":0.84973, "omega":0.0, "ax":-0.5442, "ay":-0.00012, "alpha":0.0, "fx":[-9.25665,-9.25665,-9.25665,-9.25665], "fy":[-0.00212,-0.00212,-0.00212,-0.00212]}, + {"t":5.76743, "x":8.52699, "y":3.72567, "heading":1.5708, "vx":-0.01368, "vy":0.84972, "omega":0.0, "ax":0.56022, "ay":-0.00013, "alpha":0.0, "fx":[9.52926,9.52926,9.52926,9.52926], "fy":[-0.00227,-0.00227,-0.00227,-0.00227]}, + {"t":5.81701, "x":8.527, "y":3.7678, "heading":1.5708, "vx":0.01409, "vy":0.84971, "omega":0.0, "ax":-0.57683, "ay":-0.00014, "alpha":0.0, "fx":[-9.81167,-9.81167,-9.81167,-9.81167], "fy":[-0.00242,-0.00242,-0.00242,-0.00242]}, + {"t":5.86658, "x":8.52699, "y":3.80992, "heading":1.5708, "vx":-0.01451, "vy":0.84971, "omega":0.0, "ax":0.59399, "ay":-0.00015, "alpha":0.0, "fx":[10.10354,10.10354,10.10354,10.10354], "fy":[-0.00257,-0.00257,-0.00257,-0.00257]}, + {"t":5.91616, "x":8.52701, "y":3.85205, "heading":1.5708, "vx":0.01494, "vy":0.8497, "omega":0.0, "ax":-0.61168, "ay":-0.00016, "alpha":0.0, "fx":[-10.40458,-10.40458,-10.40458,-10.40458], "fy":[-0.00273,-0.00273,-0.00273,-0.00273]}, + {"t":5.96573, "x":8.52699, "y":3.89417, "heading":1.5708, "vx":-0.01538, "vy":0.84969, "omega":0.0, "ax":0.6299, "ay":-0.00017, "alpha":0.0, "fx":[10.71447,10.71447,10.71447,10.71447], "fy":[-0.00289,-0.00289,-0.00289,-0.00289]}, + {"t":6.01531, "x":8.52701, "y":3.9363, "heading":1.5708, "vx":0.01584, "vy":0.84968, "omega":0.0, "ax":-0.64862, "ay":-0.00018, "alpha":0.0, "fx":[-11.0329,-11.0329,-11.0329,-11.0329], "fy":[-0.00305,-0.00305,-0.00305,-0.00305]}, + {"t":6.06489, "x":8.52699, "y":3.97842, "heading":1.5708, "vx":-0.01631, "vy":0.84967, "omega":0.0, "ax":0.66783, "ay":-0.00019, "alpha":0.0, "fx":[11.35958,11.35958,11.35958,11.35958], "fy":[-0.00322,-0.00322,-0.00322,-0.00322]}, + {"t":6.11446, "x":8.52701, "y":4.02054, "heading":1.5708, "vx":0.01679, "vy":0.84966, "omega":0.0, "ax":-0.6875, "ay":-0.0002, "alpha":0.0, "fx":[-11.6942,-11.6942,-11.6942,-11.6942], "fy":[-0.00339,-0.00339,-0.00339,-0.00339]}, + {"t":6.16404, "x":8.52699, "y":4.06266, "heading":1.5708, "vx":-0.01729, "vy":0.84965, "omega":0.0, "ax":0.70762, "ay":-0.00021, "alpha":0.0, "fx":[12.03648,12.03648,12.03648,12.03648], "fy":[-0.00357,-0.00357,-0.00357,-0.00357]}, + {"t":6.21361, "x":8.52701, "y":4.10479, "heading":1.5708, "vx":0.01779, "vy":0.84964, "omega":0.0, "ax":-0.72818, "ay":-0.00022, "alpha":0.0, "fx":[-12.38613,-12.38613,-12.38613,-12.38613], "fy":[-0.00375,-0.00375,-0.00375,-0.00375]}, + {"t":6.26319, "x":8.52699, "y":4.14691, "heading":1.5708, "vx":-0.01831, "vy":0.84963, "omega":0.0, "ax":0.74915, "ay":-0.00023, "alpha":0.0, "fx":[12.74289,12.74289,12.74289,12.74289], "fy":[-0.00394,-0.00394,-0.00394,-0.00394]}, + {"t":6.31276, "x":8.52701, "y":4.18903, "heading":1.5708, "vx":0.01883, "vy":0.84962, "omega":0.0, "ax":-0.77053, "ay":-0.00024, "alpha":0.0, "fx":[-13.10647,-13.10647,-13.10647,-13.10647], "fy":[-0.00412,-0.00412,-0.00412,-0.00412]}, + {"t":6.36234, "x":8.52699, "y":4.23115, "heading":1.5708, "vx":-0.01937, "vy":0.84961, "omega":0.0, "ax":0.79229, "ay":-0.00025, "alpha":0.0, "fx":[13.47663,13.47663,13.47663,13.47663], "fy":[-0.00431,-0.00431,-0.00431,-0.00431]}, + {"t":6.41191, "x":8.52701, "y":4.27327, "heading":1.5708, "vx":0.01991, "vy":0.8496, "omega":0.0, "ax":-0.81443, "ay":-0.00053, "alpha":0.0, "fx":[-13.85325,-13.85325,-13.85325,-13.85325], "fy":[-0.00902,-0.00902,-0.00902,-0.00902]}, + {"t":6.46149, "x":8.52699, "y":4.31539, "heading":1.5708, "vx":-0.02046, "vy":0.84957, "omega":0.0, "ax":0.83708, "ay":-9.21, "alpha":0.0, "fx":[14.23842,14.23842,14.23842,14.23842], "fy":[-156.65948,-156.65948,-156.65948,-156.65948]}, + {"t":6.51106, "x":8.52701, "y":4.34619, "heading":1.5708, "vx":0.02103, "vy":0.39298, "omega":0.0, "ax":-0.8544, "ay":-9.38582, "alpha":0.0, "fx":[-14.53313,-14.53313,-14.53313,-14.53313], "fy":[-159.65005,-159.65005,-159.65005,-159.65005]}, + {"t":6.56064, "x":8.527, "y":4.35413, "heading":1.5708, "vx":-0.02132, "vy":-0.07233, "omega":0.0, "ax":-4.18073, "ay":-8.44982, "alpha":0.0, "fx":[-71.11296,-71.11296,-71.11296,-71.11296], "fy":[-143.72905,-143.72905,-143.72905,-143.72905]}, + {"t":6.60239, "x":8.52247, "y":4.34375, "heading":1.5708, "vx":-0.19588, "vy":-0.42514, "omega":0.0, "ax":-4.18024, "ay":-8.44849, "alpha":0.0, "fx":[-71.10473,-71.10473,-71.10473,-71.10473], "fy":[-143.70634,-143.70634,-143.70634,-143.70634]}, + {"t":6.64415, "x":8.51064, "y":4.31863, "heading":1.5708, "vx":-0.37042, "vy":-0.77789, "omega":0.0, "ax":-4.1796, "ay":-8.44671, "alpha":0.0, "fx":[-71.09372,-71.09372,-71.09372,-71.09372], "fy":[-143.67611,-143.67611,-143.67611,-143.67611]}, + {"t":6.6859, "x":8.49153, "y":4.27879, "heading":1.5708, "vx":-0.54494, "vy":-1.13057, "omega":0.0, "ax":-4.17869, "ay":-8.44423, "alpha":0.0, "fx":[-71.07826,-71.07826,-71.07826,-71.07826], "fy":[-143.63385,-143.63385,-143.63385,-143.63385]}, + {"t":6.72765, "x":8.46514, "y":4.22422, "heading":1.5708, "vx":-0.71941, "vy":-1.48315, "omega":0.0, "ax":-4.17732, "ay":-8.44051, "alpha":0.0, "fx":[-71.05504,-71.05504,-71.05504,-71.05504], "fy":[-143.57061,-143.57061,-143.57061,-143.57061]}, + {"t":6.76941, "x":8.43146, "y":4.15494, "heading":1.5708, "vx":-0.89383, "vy":-1.83557, "omega":0.0, "ax":-4.17505, "ay":-8.43433, "alpha":0.0, "fx":[-71.01634,-71.01634,-71.01634,-71.01634], "fy":[-143.46558,-143.46558,-143.46558,-143.46558]}, + {"t":6.81116, "x":8.3905, "y":4.07095, "heading":1.5708, "vx":-1.06815, "vy":-2.18773, "omega":0.0, "ax":-4.17051, "ay":-8.42206, "alpha":0.0, "fx":[-70.93918,-70.93918,-70.93918,-70.93918], "fy":[-143.25685,-143.25685,-143.25685,-143.25685]}, + {"t":6.85292, "x":8.34226, "y":3.97226, "heading":1.5708, "vx":-1.24229, "vy":-2.53939, "omega":0.0, "ax":-4.15708, "ay":-8.38591, "alpha":0.0, "fx":[-70.71078,-70.71078,-70.71078,-70.71078], "fy":[-142.64197,-142.64197,-142.64197,-142.64197]}, + {"t":6.89467, "x":8.28677, "y":3.85892, "heading":1.5708, "vx":-1.41586, "vy":-2.88953, "omega":0.0, "ax":-3.27803, "ay":-6.12863, "alpha":0.0, "fx":[-55.75828,-55.75828,-55.75828,-55.75828], "fy":[-104.24632,-104.24632,-104.24632,-104.24632]}, + {"t":6.93642, "x":8.22479, "y":3.73293, "heading":1.5708, "vx":-1.55273, "vy":-3.14542, "omega":0.0, "ax":-0.05252, "ay":0.0235, "alpha":0.0, "fx":[-0.89342,-0.89342,-0.89342,-0.89342], "fy":[0.39972,0.39972,0.39972,0.39972]}, + {"t":6.97818, "x":8.15992, "y":3.60162, "heading":1.5708, "vx":-1.55492, "vy":-3.14444, "omega":0.0, "ax":-0.00572, "ay":0.00283, "alpha":0.0, "fx":[-0.09737,-0.09737,-0.09737,-0.09737], "fy":[0.04813,0.04813,0.04813,0.04813]}, + {"t":7.01993, "x":8.09499, "y":3.47033, "heading":1.5708, "vx":-1.55516, "vy":-3.14432, "omega":0.0, "ax":-0.00031, "ay":0.00015, "alpha":0.0, "fx":[-0.00527,-0.00527,-0.00527,-0.00527], "fy":[0.00258,0.00258,0.00258,0.00258]}, + {"t":7.06168, "x":8.03005, "y":3.33904, "heading":1.5708, "vx":-1.55518, "vy":-3.14431, "omega":0.0, "ax":0.00039, "ay":-0.0002, "alpha":0.0, "fx":[0.00671,0.00671,0.00671,0.00671], "fy":[-0.00334,-0.00334,-0.00334,-0.00334]}, + {"t":7.10344, "x":7.96512, "y":3.20776, "heading":1.5708, "vx":-1.55516, "vy":-3.14432, "omega":0.0, "ax":0.00055, "ay":-0.00027, "alpha":0.0, "fx":[0.00933,0.00933,0.00933,0.00933], "fy":[-0.00465,-0.00465,-0.00465,-0.00465]}, + {"t":7.14519, "x":7.90019, "y":3.07647, "heading":1.5708, "vx":-1.55514, "vy":-3.14433, "omega":0.0, "ax":0.00061, "ay":-0.00031, "alpha":0.0, "fx":[0.01044,0.01044,0.01044,0.01044], "fy":[-0.0052,-0.0052,-0.0052,-0.0052]}, + {"t":7.18694, "x":7.83526, "y":2.94518, "heading":1.5708, "vx":-1.55511, "vy":-3.14435, "omega":0.0, "ax":0.00042, "ay":-0.00021, "alpha":0.0, "fx":[0.00708,0.00708,0.00708,0.00708], "fy":[-0.00354,-0.00354,-0.00354,-0.00354]}, + {"t":7.2287, "x":7.77032, "y":2.81389, "heading":1.5708, "vx":-1.55509, "vy":-3.14435, "omega":0.0, "ax":-0.00201, "ay":0.00099, "alpha":0.0, "fx":[-0.03422,-0.03422,-0.03422,-0.03422], "fy":[0.01688,0.01688,0.01688,0.01688]}, + {"t":7.27045, "x":7.70539, "y":2.68261, "heading":1.5708, "vx":-1.55518, "vy":-3.14431, "omega":0.0, "ax":-0.02361, "ay":0.01168, "alpha":0.0, "fx":[-0.40164,-0.40164,-0.40164,-0.40164], "fy":[0.19868,0.19868,0.19868,0.19868]}, + {"t":7.31221, "x":7.64044, "y":2.55133, "heading":1.5708, "vx":-1.55616, "vy":-3.14383, "omega":0.0, "ax":-0.20937, "ay":0.104, "alpha":0.0, "fx":[-3.56125,-3.56125,-3.56125,-3.56125], "fy":[1.76899,1.76899,1.76899,1.76899]}, + {"t":7.35396, "x":7.57528, "y":2.42016, "heading":1.5708, "vx":-1.5649, "vy":-3.13948, "omega":0.0, "ax":-1.7136, "ay":0.88727, "alpha":0.0, "fx":[-29.14782,-29.14782,-29.14782,-29.14782], "fy":[15.09226,15.09226,15.09226,15.09226]}, + {"t":7.39571, "x":7.50845, "y":2.28984, "heading":1.5708, "vx":-1.63645, "vy":-3.10244, "omega":0.0, "ax":1.30819, "ay":8.50696, "alpha":0.0, "fx":[22.25191,22.25191,22.25191,22.25191], "fy":[144.70096,144.70096,144.70096,144.70096]}, + {"t":7.43747, "x":7.44126, "y":2.16772, "heading":1.5708, "vx":-1.58183, "vy":-2.74724, "omega":0.0, "ax":3.93077, "ay":8.49925, "alpha":0.0, "fx":[66.86127,66.86127,66.86127,66.86127], "fy":[144.56981,144.56981,144.56981,144.56981]}, + {"t":7.47922, "x":7.37864, "y":2.06042, "heading":1.5708, "vx":-1.41771, "vy":-2.39237, "omega":0.0, "ax":4.0505, "ay":8.48174, "alpha":0.0, "fx":[68.89778,68.89778,68.89778,68.89778], "fy":[144.27203,144.27203,144.27203,144.27203]}, + {"t":7.52097, "x":7.32297, "y":1.96793, "heading":1.5708, "vx":-1.24859, "vy":-2.03822, "omega":0.0, "ax":4.09282, "ay":8.47514, "alpha":0.0, "fx":[69.61768,69.61768,69.61768,69.61768], "fy":[144.15965,144.15965,144.15965,144.15965]}, + {"t":7.56273, "x":7.27441, "y":1.89021, "heading":1.5708, "vx":-1.0777, "vy":-1.68436, "omega":0.0, "ax":-7.09468, "ay":5.54381, "alpha":0.0, "fx":[-120.67849,-120.67849,-120.67849,-120.67849], "fy":[94.29856,94.29856,94.29856,94.29856]}, + {"t":7.60434, "x":7.22342, "y":1.82492, "heading":1.5708, "vx":-1.3729, "vy":-1.45368, "omega":0.0, "ax":-3.07772, "ay":3.18744, "alpha":0.0, "fx":[-52.35106,-52.35106,-52.35106,-52.35106], "fy":[54.21744,54.21744,54.21744,54.21744]}, + {"t":7.64595, "x":7.16363, "y":1.7672, "heading":1.5708, "vx":-1.50097, "vy":-1.32105, "omega":0.0, "ax":-0.16901, "ay":0.19306, "alpha":0.0, "fx":[-2.87475,-2.87475,-2.87475,-2.87475], "fy":[3.28382,3.28382,3.28382,3.28382]}, + {"t":7.68756, "x":7.10103, "y":1.71239, "heading":1.5708, "vx":-1.508, "vy":-1.31302, "omega":0.0, "ax":-0.00669, "ay":0.00768, "alpha":0.0, "fx":[-0.11379,-0.11379,-0.11379,-0.11379], "fy":[0.13071,0.13071,0.13071,0.13071]}, + {"t":7.72917, "x":7.03828, "y":1.65777, "heading":1.5708, "vx":-1.50828, "vy":-1.3127, "omega":0.0, "ax":0.00072, "ay":-0.00082, "alpha":0.0, "fx":[0.01219,0.01219,0.01219,0.01219], "fy":[-0.01402,-0.01402,-0.01402,-0.01402]}, + {"t":7.77078, "x":6.97552, "y":1.60314, "heading":1.5708, "vx":-1.50825, "vy":-1.31273, "omega":0.0, "ax":0.00111, "ay":-0.00127, "alpha":0.0, "fx":[0.01883,0.01883,0.01883,0.01883], "fy":[-0.02164,-0.02164,-0.02164,-0.02164]}, + {"t":7.81239, "x":6.91276, "y":1.54852, "heading":1.5708, "vx":-1.5082, "vy":-1.31278, "omega":0.0, "ax":0.00118, "ay":-0.00136, "alpha":0.0, "fx":[0.02011,0.02011,0.02011,0.02011], "fy":[-0.02312,-0.02312,-0.02312,-0.02312]}, + {"t":7.854, "x":6.85001, "y":1.4939, "heading":1.5708, "vx":-1.50815, "vy":-1.31284, "omega":0.0, "ax":0.00125, "ay":-0.00144, "alpha":0.0, "fx":[0.02124,0.02124,0.02124,0.02124], "fy":[-0.02441,-0.02441,-0.02441,-0.02441]}, + {"t":7.89561, "x":6.78725, "y":1.43927, "heading":1.5708, "vx":-1.5081, "vy":-1.3129, "omega":0.0, "ax":0.00132, "ay":-0.00152, "alpha":0.0, "fx":[0.02246,0.02246,0.02246,0.02246], "fy":[-0.02581,-0.02581,-0.02581,-0.02581]}, + {"t":7.93722, "x":6.7245, "y":1.38464, "heading":1.5708, "vx":-1.50805, "vy":-1.31296, "omega":0.0, "ax":0.0014, "ay":-0.00161, "alpha":0.0, "fx":[0.02379,0.02379,0.02379,0.02379], "fy":[-0.02733,-0.02733,-0.02733,-0.02733]}, + {"t":7.97883, "x":6.66175, "y":1.33, "heading":1.5708, "vx":-1.50799, "vy":-1.31303, "omega":0.0, "ax":0.00148, "ay":-0.00171, "alpha":0.0, "fx":[0.02525,0.02525,0.02525,0.02525], "fy":[-0.02901,-0.02901,-0.02901,-0.02901]}, + {"t":8.02044, "x":6.59901, "y":1.27537, "heading":1.5708, "vx":-1.50793, "vy":-1.3131, "omega":0.0, "ax":0.00158, "ay":-0.00181, "alpha":0.0, "fx":[0.02687,0.02687,0.02687,0.02687], "fy":[-0.03087,-0.03087,-0.03087,-0.03087]}, + {"t":8.06205, "x":6.53627, "y":1.22073, "heading":1.5708, "vx":-1.50786, "vy":-1.31318, "omega":0.0, "ax":0.00169, "ay":-0.00194, "alpha":0.0, "fx":[0.02867,0.02867,0.02867,0.02867], "fy":[-0.03293,-0.03293,-0.03293,-0.03293]}, + {"t":8.10366, "x":6.47352, "y":1.16608, "heading":1.5708, "vx":-1.50779, "vy":-1.31326, "omega":0.0, "ax":0.0018, "ay":-0.00207, "alpha":0.0, "fx":[0.03068,0.03068,0.03068,0.03068], "fy":[-0.03524,-0.03524,-0.03524,-0.03524]}, + {"t":8.14527, "x":6.41079, "y":1.11144, "heading":1.5708, "vx":-1.50772, "vy":-1.31334, "omega":0.0, "ax":0.00194, "ay":-0.00223, "alpha":0.0, "fx":[0.03296,0.03296,0.03296,0.03296], "fy":[-0.03785,-0.03785,-0.03785,-0.03785]}, + {"t":8.18688, "x":6.34805, "y":1.05679, "heading":1.5708, "vx":-1.50763, "vy":-1.31344, "omega":0.0, "ax":0.00209, "ay":-0.0024, "alpha":0.0, "fx":[0.03555,0.03555,0.03555,0.03555], "fy":[-0.04082,-0.04082,-0.04082,-0.04082]}, + {"t":8.22849, "x":6.28532, "y":1.00213, "heading":1.5708, "vx":-1.50755, "vy":-1.31354, "omega":0.0, "ax":0.00227, "ay":-0.0026, "alpha":0.0, "fx":[0.03854,0.03854,0.03854,0.03854], "fy":[-0.04425,-0.04425,-0.04425,-0.04425]}, + {"t":8.2701, "x":6.2226, "y":0.94747, "heading":1.5708, "vx":-1.50745, "vy":-1.31364, "omega":0.0, "ax":0.00247, "ay":-0.00284, "alpha":0.0, "fx":[0.04202,0.04202,0.04202,0.04202], "fy":[-0.04824,-0.04824,-0.04824,-0.04824]}, + {"t":8.3117, "x":6.15987, "y":0.89281, "heading":1.5708, "vx":-1.50735, "vy":-1.31376, "omega":0.0, "ax":0.00271, "ay":-0.00311, "alpha":0.0, "fx":[0.04615,0.04615,0.04615,0.04615], "fy":[-0.05297,-0.05297,-0.05297,-0.05297]}, + {"t":8.35331, "x":6.09715, "y":0.83814, "heading":1.5708, "vx":-1.50724, "vy":-1.31389, "omega":0.0, "ax":0.00301, "ay":-0.00345, "alpha":0.0, "fx":[0.05113,0.05113,0.05113,0.05113], "fy":[-0.05868,-0.05868,-0.05868,-0.05868]}, + {"t":8.39492, "x":6.03444, "y":0.78347, "heading":1.5708, "vx":-1.50711, "vy":-1.31404, "omega":0.0, "ax":0.00337, "ay":-0.00386, "alpha":0.0, "fx":[0.05728,0.05728,0.05728,0.05728], "fy":[-0.06572,-0.06572,-0.06572,-0.06572]}, + {"t":8.43653, "x":5.97173, "y":0.72879, "heading":1.5708, "vx":-1.50697, "vy":-1.3142, "omega":0.0, "ax":0.00381, "ay":-0.00437, "alpha":0.0, "fx":[0.0648,0.0648,0.0648,0.0648], "fy":[-0.07433,-0.07433,-0.07433,-0.07433]}, + {"t":8.47814, "x":5.90903, "y":0.6741, "heading":1.5708, "vx":-1.50681, "vy":-1.31438, "omega":0.0, "ax":0.00397, "ay":-0.00456, "alpha":0.0, "fx":[0.06759,0.06759,0.06759,0.06759], "fy":[-0.07752,-0.07752,-0.07752,-0.07752]}, + {"t":8.51975, "x":5.84634, "y":0.61941, "heading":1.5708, "vx":-1.50665, "vy":-1.31457, "omega":0.0, "ax":-0.00513, "ay":0.00588, "alpha":0.0, "fx":[-0.08728,-0.08728,-0.08728,-0.08728], "fy":[0.1,0.1,0.1,0.1]}, + {"t":8.56136, "x":5.78364, "y":0.56471, "heading":1.5708, "vx":-1.50686, "vy":-1.31432, "omega":0.0, "ax":-0.22244, "ay":0.25686, "alpha":0.0, "fx":[-3.78362,-3.78362,-3.78362,-3.78362], "fy":[4.36907,4.36907,4.36907,4.36907]}, + {"t":8.60297, "x":5.72075, "y":0.51025, "heading":1.5708, "vx":-1.51612, "vy":-1.30363, "omega":0.0, "ax":-3.22231, "ay":4.19434, "alpha":0.0, "fx":[-54.81064,-54.81064,-54.81064,-54.81064], "fy":[71.34444,71.34444,71.34444,71.34444]}, + {"t":8.64458, "x":5.65487, "y":0.45963, "heading":1.5708, "vx":-1.6502, "vy":-1.12911, "omega":0.0, "ax":-4.39524, "ay":7.94446, "alpha":0.0, "fx":[-74.76169,-74.76169,-74.76169,-74.76169], "fy":[135.13293,135.13293,135.13293,135.13293]}, + {"t":8.68619, "x":5.5824, "y":0.41953, "heading":1.5708, "vx":-1.83308, "vy":-0.79854, "omega":0.0, "ax":-2.86214, "ay":8.88113, "alpha":0.0, "fx":[-48.68424,-48.68424,-48.68424,-48.68424], "fy":[151.06545,151.06545,151.06545,151.06545]}, + {"t":8.7278, "x":5.50365, "y":0.39399, "heading":1.5708, "vx":-1.95218, "vy":-0.429, "omega":0.0, "ax":-1.17951, "ay":9.21344, "alpha":0.0, "fx":[-20.06319,-20.06319,-20.06319,-20.06319], "fy":[156.71793,156.71793,156.71793,156.71793]}, + {"t":8.76689, "x":5.42645, "y":0.38426, "heading":1.5708, "vx":-1.99828, "vy":-0.06889, "omega":0.0, "ax":0.02876, "ay":4.23455, "alpha":0.0, "fx":[0.4892,0.4892,0.4892,0.4892], "fy":[72.02851,72.02851,72.02851,72.02851]}, + {"t":8.80597, "x":5.34837, "y":0.3848, "heading":1.5708, "vx":-1.99715, "vy":0.09662, "omega":0.0, "ax":-0.04271, "ay":-1.15171, "alpha":0.0, "fx":[-0.72645,-0.72645,-0.72645,-0.72645], "fy":[-19.59021,-19.59021,-19.59021,-19.59021]}, + {"t":8.84506, "x":5.27028, "y":0.3877, "heading":1.5708, "vx":-1.99882, "vy":0.0516, "omega":0.0, "ax":-0.0072, "ay":-0.31683, "alpha":0.0, "fx":[-0.12241,-0.12241,-0.12241,-0.12241], "fy":[-5.38914,-5.38914,-5.38914,-5.38914]}, + {"t":8.88414, "x":5.19215, "y":0.38947, "heading":1.5708, "vx":-1.9991, "vy":0.03922, "omega":0.0, "ax":-0.00339, "ay":-0.19101, "alpha":0.0, "fx":[-0.05766,-0.05766,-0.05766,-0.05766], "fy":[-3.24906,-3.24906,-3.24906,-3.24906]}, + {"t":8.92323, "x":5.11401, "y":0.39086, "heading":1.5708, "vx":-1.99924, "vy":0.03175, "omega":0.0, "ax":-0.00211, "ay":-0.14611, "alpha":0.0, "fx":[-0.03592,-0.03592,-0.03592,-0.03592], "fy":[-2.48521,-2.48521,-2.48521,-2.48521]}, + {"t":8.96231, "x":5.03587, "y":0.39199, "heading":1.5708, "vx":-1.99932, "vy":0.02604, "omega":0.0, "ax":-0.00143, "ay":-0.12041, "alpha":0.0, "fx":[-0.02427,-0.02427,-0.02427,-0.02427], "fy":[-2.04816,-2.04816,-2.04816,-2.04816]}, + {"t":9.0014, "x":4.95772, "y":0.39292, "heading":1.5708, "vx":-1.99938, "vy":0.02134, "omega":0.0, "ax":-0.001, "ay":-0.10348, "alpha":0.0, "fx":[-0.017,-0.017,-0.017,-0.017], "fy":[-1.76009,-1.76009,-1.76009,-1.76009]}, + {"t":9.04048, "x":4.87958, "y":0.39367, "heading":1.5708, "vx":-1.99941, "vy":0.01729, "omega":0.0, "ax":-0.00071, "ay":-0.09194, "alpha":0.0, "fx":[-0.01212,-0.01212,-0.01212,-0.01212], "fy":[-1.56393,-1.56393,-1.56393,-1.56393]}, + {"t":9.07957, "x":4.80143, "y":0.39428, "heading":1.5708, "vx":-1.99944, "vy":0.0137, "omega":0.0, "ax":-0.00051, "ay":-0.08434, "alpha":0.0, "fx":[-0.00865,-0.00865,-0.00865,-0.00865], "fy":[-1.43459,-1.43459,-1.43459,-1.43459]}, + {"t":9.11865, "x":4.72328, "y":0.39475, "heading":1.5708, "vx":-1.99946, "vy":0.0104, "omega":0.0, "ax":-0.00035, "ay":-0.07994, "alpha":0.0, "fx":[-0.00601,-0.00601,-0.00601,-0.00601], "fy":[-1.35982,-1.35982,-1.35982,-1.35982]}, + {"t":9.15774, "x":4.64513, "y":0.39509, "heading":1.5708, "vx":-1.99948, "vy":0.00728, "omega":0.0, "ax":-0.00023, "ay":-0.07841, "alpha":0.0, "fx":[-0.00383,-0.00383,-0.00383,-0.00383], "fy":[-1.33381,-1.33381,-1.33381,-1.33381]}, + {"t":9.19682, "x":4.56698, "y":0.39532, "heading":1.5708, "vx":-1.99949, "vy":0.00421, "omega":0.0, "ax":-0.00011, "ay":-0.07965, "alpha":0.0, "fx":[-0.0018,-0.0018,-0.0018,-0.0018], "fy":[-1.35476,-1.35476,-1.35476,-1.35476]}, + {"t":9.23591, "x":4.48883, "y":0.39542, "heading":1.5708, "vx":-1.99949, "vy":0.0011, "omega":0.0, "ax":0.00002, "ay":-0.08373, "alpha":0.0, "fx":[0.00038,0.00038,0.00038,0.00038], "fy":[-1.42426,-1.42426,-1.42426,-1.42426]}, + {"t":9.27499, "x":4.41068, "y":0.3954, "heading":1.5708, "vx":-1.99949, "vy":-0.00217, "omega":0.0, "ax":0.00018, "ay":-0.091, "alpha":0.0, "fx":[0.00305,0.00305,0.00305,0.00305], "fy":[-1.5479,-1.5479,-1.5479,-1.5479]}, + {"t":9.31408, "x":4.33253, "y":0.39525, "heading":1.5708, "vx":-1.99948, "vy":-0.00573, "omega":0.0, "ax":0.00039, "ay":-0.10216, "alpha":0.0, "fx":[0.00671,0.00671,0.00671,0.00671], "fy":[-1.73772,-1.73772,-1.73772,-1.73772]}, + {"t":9.35316, "x":4.25438, "y":0.39494, "heading":1.5708, "vx":-1.99947, "vy":-0.00972, "omega":0.0, "ax":0.00071, "ay":-0.11867, "alpha":0.0, "fx":[0.01215,0.01215,0.01215,0.01215], "fy":[-2.01856,-2.01856,-2.01856,-2.01856]}, + {"t":9.39225, "x":4.17623, "y":0.39447, "heading":1.5708, "vx":-1.99944, "vy":-0.01436, "omega":0.0, "ax":0.00124, "ay":-0.14387, "alpha":0.0, "fx":[0.02101,0.02101,0.02101,0.02101], "fy":[-2.44717,-2.44717,-2.44717,-2.44717]}, + {"t":9.43133, "x":4.09809, "y":0.3938, "heading":1.5708, "vx":-1.99939, "vy":-0.01998, "omega":0.0, "ax":0.00223, "ay":-0.18821, "alpha":0.0, "fx":[0.03787,0.03787,0.03787,0.03787], "fy":[-3.20142,-3.20142,-3.20142,-3.20142]}, + {"t":9.47042, "x":4.01994, "y":0.39288, "heading":1.5708, "vx":-1.9993, "vy":-0.02734, "omega":0.0, "ax":0.00526, "ay":-0.31427, "alpha":0.0, "fx":[0.0895,0.0895,0.0895,0.0895], "fy":[-5.34558,-5.34558,-5.34558,-5.34558]}, + {"t":9.5095, "x":3.9418, "y":0.39157, "heading":1.5708, "vx":-1.9991, "vy":-0.03962, "omega":0.0, "ax":0.03653, "ay":-1.16887, "alpha":0.0, "fx":[0.62128,0.62128,0.62128,0.62128], "fy":[-19.88223,-19.88223,-19.88223,-19.88223]}, + {"t":9.54859, "x":3.8637, "y":0.38913, "heading":1.5708, "vx":-1.99767, "vy":-0.08531, "omega":0.0, "ax":-0.00435, "ay":4.21743, "alpha":0.0, "fx":[-0.07402,-0.07402,-0.07402,-0.07402], "fy":[71.73736,71.73736,71.73736,71.73736]}, + {"t":9.58767, "x":3.78561, "y":0.38901, "heading":1.5708, "vx":-1.99784, "vy":0.07953, "omega":0.0, "ax":1.2237, "ay":9.3008, "alpha":0.0, "fx":[20.81487,20.81487,20.81487,20.81487], "fy":[158.20392,158.20392,158.20392,158.20392]}, + {"t":9.62676, "x":3.70846, "y":0.39923, "heading":1.5708, "vx":-1.95001, "vy":0.44305, "omega":0.0, "ax":-1.21219, "ay":5.30655, "alpha":25.824, "fx":[-159.71886,145.96482,11.91908,-80.64092], "fy":[7.73214,55.15783,159.72163,138.43976]}, + {"t":9.6516, "x":3.65966, "y":0.41187, "heading":1.57876, "vx":-1.98012, "vy":0.57485, "omega":0.64138, "ax":-1.41872, "ay":5.91138, "alpha":23.1841, "fx":[-159.02944,129.00095,12.17681,-78.67653], "fy":[14.98133,88.11814,159.62298,139.4811]}, + {"t":9.67643, "x":3.61004, "y":0.42797, "heading":1.60184, "vx":-2.01535, "vy":0.72167, "omega":1.21719, "ax":-1.63289, "ay":6.44021, "alpha":20.74368, "fx":[-157.19497,110.27763,11.97094,-76.15336], "fy":[26.57695,111.34903,159.51751,140.74069]}, + {"t":9.70127, "x":3.55948, "y":0.44788, "heading":1.63847, "vx":-2.05591, "vy":0.88162, "omega":1.73239, "ax":-1.70526, "ay":7.07831, "alpha":17.76479, "fx":[-150.04548,92.40476,12.34602,-70.72928], "fy":[51.95061,127.07481,159.27556,143.2993]}, + {"t":9.7261, "x":3.5079, "y":0.47196, "heading":1.68698, "vx":-2.09826, "vy":1.05742, "omega":2.17361, "ax":-0.9889, "ay":8.32242, "alpha":11.20653, "fx":[-103.73189,72.88014,16.70996,-53.14191], "fy":[118.29401,139.42112,158.39023,150.14283]}, + {"t":9.75094, "x":3.45548, "y":0.50079, "heading":1.74442, "vx":-2.12282, "vy":1.26412, "omega":2.45194, "ax":2.92085, "ay":8.77098, "alpha":-0.26028, "fx":[49.93408,47.53906,49.41667,51.8417], "fy":[149.17171,149.92367,149.23307,148.43895]}, + {"t":9.77578, "x":3.40366, "y":0.53489, "heading":1.80523, "vx":-2.05028, "vy":1.48196, "omega":2.44547, "ax":4.99653, "ay":7.63006, "alpha":-2.45372, "fx":[82.14765,69.13927,88.46055,100.2108], "fy":[132.90553,139.69034,127.40671,119.13802]}, + {"t":9.80061, "x":3.35427, "y":0.57405, "heading":1.86521, "vx":-1.92618, "vy":1.67146, "omega":2.38453, "ax":6.83509, "ay":5.79534, "alpha":3.54968, "fx":[124.34641,129.59653,110.14538,100.96311], "fy":[88.31631,82.62718,107.95323,115.4118]}, + {"t":9.82545, "x":3.30854, "y":0.61735, "heading":1.92553, "vx":-1.75642, "vy":1.8154, "omega":2.47269, "ax":7.9851, "ay":1.13523, "alpha":13.84751, "fx":[137.15252,152.76288,134.20997,119.17142], "fy":[-63.60922,-21.24847,74.46223,87.63488]}, + {"t":9.85029, "x":3.26738, "y":0.66279, "heading":1.99122, "vx":-1.5581, "vy":1.84359, "omega":2.81662, "ax":6.55928, "ay":-1.97793, "alpha":17.94942, "fx":[72.4749,135.69895,141.16653,96.94569], "fy":[-137.27828,-77.42622,59.84743,20.28117]}, + {"t":9.87512, "x":3.23071, "y":0.70796, "heading":2.06671, "vx":-1.39519, "vy":1.79447, "omega":3.26242, "ax":3.86761, "ay":-5.29121, "alpha":20.12219, "fx":[49.24841,121.08243,150.77969,-57.96282], "fy":[-149.27719,-100.53682,24.03266,-134.22672]}, + {"t":9.89996, "x":3.19725, "y":0.7509, "heading":2.15394, "vx":-1.29913, "vy":1.66305, "omega":3.76218, "ax":4.85289, "ay":-7.81545, "alpha":2.424, "fx":[71.60959,95.33547,97.65099,65.58904], "fy":[-140.65819,-125.77874,-122.77915,-142.53815]}, + {"t":9.9248, "x":3.16648, "y":0.7898, "heading":2.24813, "vx":-1.1786, "vy":1.46895, "omega":3.82238, "ax":4.95354, "ay":-7.52576, "alpha":-6.20803, "fx":[122.04015,35.01044,68.03223,111.95008], "fy":[-100.39783,-154.3121,-144.04281,-113.2911]}, + {"t":9.94963, "x":3.13874, "y":0.82396, "heading":2.34115, "vx":-1.05558, "vy":1.28203, "omega":3.6682, "ax":4.74308, "ay":-6.9189, "alpha":-11.44278, "fx":[143.53427,-12.07224,67.28263,123.96934], "fy":[-66.58609,-158.43213,-144.95131,-100.78449]}, + {"t":9.97447, "x":3.11398, "y":0.85366, "heading":2.42872, "vx":-0.93777, "vy":1.11019, "omega":3.384, "ax":4.67106, "ay":-6.38604, "alpha":-14.41998, "fx":[152.05859,-36.02996,70.72055,131.06433], "fy":[-43.97187,-155.24893,-143.54338,-91.73454]}, + {"t":9.99931, "x":3.09213, "y":0.87927, "heading":2.50832, "vx":-0.82176, "vy":0.95158, "omega":3.02586, "ax":4.6972, "ay":-5.99742, "alpha":-15.97295, "fx":[155.36953,-46.99016,75.03413,136.17856], "fy":[-29.8055,-152.60763,-141.46883,-84.17574]}, + {"t":10.02414, "x":3.07317, "y":0.90105, "heading":2.57855, "vx":-0.7051, "vy":0.80263, "omega":2.62915, "ax":4.76105, "ay":-5.73044, "alpha":-16.72694, "fx":[156.43415,-51.86806,79.27149,140.09914], "fy":[-21.81879,-151.22325,-139.22341,-77.6271]}, + {"t":10.04898, "x":3.05713, "y":0.91922, "heading":2.63869, "vx":-0.58685, "vy":0.6603, "omega":2.21371, "ax":4.82963, "ay":-5.56103, "alpha":-17.0148, "fx":[156.33445,-53.96713,83.07487,143.16004], "fy":[-18.75613,-150.6231,-137.04699,-71.94009]}, + {"t":10.07382, "x":3.04404, "y":0.9339, "heading":2.68842, "vx":-0.4669, "vy":0.52219, "omega":1.79112, "ax":4.88288, "ay":-5.47456, "alpha":-17.01951, "fx":[155.26001,-54.88087,86.2973,145.54926], "fy":[-19.9446,-150.38743,-135.08475,-67.06574]}, + {"t":10.09865, "x":3.03395, "y":0.94518, "heading":2.72766, "vx":-0.34563, "vy":0.38622, "omega":1.36841, "ax":4.90153, "ay":-5.45826, "alpha":-16.87904, "fx":[152.65039,-55.42654,88.87996,147.39062], "fy":[-24.69293,-150.2557,-133.43336,-62.99182]}, + {"t":10.12349, "x":3.02688, "y":0.95309, "heading":2.75644, "vx":-0.22389, "vy":0.25065, "omega":0.9492, "ax":4.84788, "ay":-5.46564, "alpha":-16.87239, "fx":[146.31556,-56.04508,90.80251,148.77097], "fy":[-29.91549,-150.07705,-132.15939,-59.72377]}, + {"t":10.14833, "x":3.02281, "y":0.95763, "heading":2.77481, "vx":-0.10349, "vy":0.11491, "omega":0.53015, "ax":4.16667, "ay":-4.62656, "alpha":-21.34546, "fx":[98.68164,-56.99219,92.0556,149.75066], "fy":[23.56414,-149.75873,-131.31149,-57.27979]}, + {"t":10.17316, "x":3.02153, "y":0.95906, "heading":2.78139, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0,0.0,0.0,0.0]}], "splits":[0] }, "events":[] diff --git a/src/main/deploy/v1_doomspiral/choreo/RIGHT_TRENCH_SIMPLE.traj b/src/main/deploy/v1_doomspiral/choreo/RIGHT_TRENCH_SIMPLE.traj index a3d011ba..a934f83c 100644 --- a/src/main/deploy/v1_doomspiral/choreo/RIGHT_TRENCH_SIMPLE.traj +++ b/src/main/deploy/v1_doomspiral/choreo/RIGHT_TRENCH_SIMPLE.traj @@ -3,46 +3,21 @@ "version":3, "snapshot":{ "waypoints":[ - {"x":4.428042411804199, "y":0.3954239785671234, "heading":1.5707963267948966, "intervals":27, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, - {"x":7.795207023620605, "y":0.9033589959144592, "heading":1.5707963267948966, "intervals":32, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, - {"x":7.795207023620605, "y":3.0137579441070557, "heading":-1.5707963267948966, "intervals":20, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":7.098419189453125, "y":1.044877052307129, "heading":0.0, "intervals":17, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":6.116927146911621, "y":0.3939896523952484, "heading":1.5707963267948966, "intervals":27, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, - {"x":3.8894782066345215, "y":0.388911485671997, "heading":0.0, "intervals":13, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":2.9093637466430664, "y":0.3876216113567352, "heading":-1.5707963267948966, "intervals":23, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":2.822967529296875, "y":1.564, "heading":2.49, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], + {"x":4.441253185272217, "y":0.4065414428710934, "heading":1.5707963267948966, "intervals":57, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, + {"x":10.862669944763184, "y":2.7872402667999268, "heading":0.0, "intervals":40, "split":false, "fixTranslation":false, "fixHeading":false, "overrideIntervals":false}], "constraints":[ {"from":"first", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, {"from":"last", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, - {"from":"first", "to":"last", "data":{"type":"KeepInRectangle", "props":{"x":0.0, "y":0.0, "w":16.541, "h":8.0692}}, "enabled":true}, - {"from":1, "to":2, "data":{"type":"KeepInLane", "props":{"tolerance":0.00001}}, "enabled":true}, - {"from":1, "to":2, "data":{"type":"MaxAngularVelocity", "props":{"max":0.0}}, "enabled":true}, - {"from":4, "to":6, "data":{"type":"MaxAngularVelocity", "props":{"max":0.0}}, "enabled":true}, - {"from":1, "to":2, "data":{"type":"MaxVelocity", "props":{"max":1.5}}, "enabled":true}, - {"from":4, "to":6, "data":{"type":"KeepInLane", "props":{"tolerance":0.01}}, "enabled":true}, - {"from":3, "to":5, "data":{"type":"MaxVelocity", "props":{"max":2.0}}, "enabled":true}], + {"from":"first", "to":"last", "data":{"type":"KeepInRectangle", "props":{"x":0.0, "y":0.0, "w":16.541, "h":8.0692}}, "enabled":true}], "targetDt":0.05 }, "params":{ "waypoints":[ - {"x":{"exp":"4.428042411804199 m", "val":4.428042411804199}, "y":{"exp":"0.3954239785671234 m", "val":0.3954239785671234}, "heading":{"exp":"90 deg", "val":1.5707963267948966}, "intervals":27, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, - {"x":{"exp":"7.7952070236206055 m", "val":7.795207023620605}, "y":{"exp":"0.9033589959144592 m", "val":0.9033589959144592}, "heading":{"exp":"1.5707963267948966 rad", "val":1.5707963267948966}, "intervals":32, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, - {"x":{"exp":"7.7952070236206055 m", "val":7.795207023620605}, "y":{"exp":"3.0137579441070557 m", "val":3.0137579441070557}, "heading":{"exp":"-1.5707963267948966 rad", "val":-1.5707963267948966}, "intervals":20, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":{"exp":"7.098419189453125 m", "val":7.098419189453125}, "y":{"exp":"1.044877052307129 m", "val":1.044877052307129}, "heading":{"exp":"0 deg", "val":0.0}, "intervals":17, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":{"exp":"6.116927146911621 m", "val":6.116927146911621}, "y":{"exp":"0.3939896523952484 m", "val":0.3939896523952484}, "heading":{"exp":"90 deg", "val":1.5707963267948966}, "intervals":27, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, - {"x":{"exp":"3.8894782066345215 m", "val":3.8894782066345215}, "y":{"exp":"0.38891148567199707 m", "val":0.388911485671997}, "heading":{"exp":"0 deg", "val":0.0}, "intervals":13, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":{"exp":"2.9093637466430664 m", "val":2.9093637466430664}, "y":{"exp":"0.38762161135673523 m", "val":0.3876216113567352}, "heading":{"exp":"-90 deg", "val":-1.5707963267948966}, "intervals":23, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":{"exp":"2.822967529296875 m", "val":2.822967529296875}, "y":{"exp":"(8.064 - 6.5) m", "val":1.564}, "heading":{"exp":"2.49 rad", "val":2.49}, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], + {"x":{"exp":"4.441253185272217 m", "val":4.441253185272217}, "y":{"exp":"(8.1 - 7.693458557128906) m", "val":0.4065414428710934}, "heading":{"exp":"90 deg", "val":1.5707963267948966}, "intervals":57, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], "constraints":[ {"from":"first", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, {"from":"last", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, - {"from":"first", "to":"last", "data":{"type":"KeepInRectangle", "props":{"x":{"exp":"0 m", "val":0.0}, "y":{"exp":"0 m", "val":0.0}, "w":{"exp":"16.541 m", "val":16.541}, "h":{"exp":"8.0692 m", "val":8.0692}}}, "enabled":true}, - {"from":1, "to":2, "data":{"type":"KeepInLane", "props":{"tolerance":{"exp":"1e-5 m", "val":0.00001}}}, "enabled":true}, - {"from":1, "to":2, "data":{"type":"MaxAngularVelocity", "props":{"max":{"exp":"0 rad / s", "val":0.0}}}, "enabled":true}, - {"from":4, "to":6, "data":{"type":"MaxAngularVelocity", "props":{"max":{"exp":"0 rad / s", "val":0.0}}}, "enabled":true}, - {"from":1, "to":2, "data":{"type":"MaxVelocity", "props":{"max":{"exp":"1.5 m / s", "val":1.5}}}, "enabled":true}, - {"from":4, "to":6, "data":{"type":"KeepInLane", "props":{"tolerance":{"exp":"0.01 m", "val":0.01}}}, "enabled":true}, - {"from":3, "to":5, "data":{"type":"MaxVelocity", "props":{"max":{"exp":"2 m / s", "val":2.0}}}, "enabled":true}], + {"from":"first", "to":"last", "data":{"type":"KeepInRectangle", "props":{"x":{"exp":"0 m", "val":0.0}, "y":{"exp":"0 m", "val":0.0}, "w":{"exp":"16.541 m", "val":16.541}, "h":{"exp":"8.0692 m", "val":8.0692}}}, "enabled":true}], "targetDt":{ "exp":"0.05 s", "val":0.05 @@ -73,168 +48,66 @@ "differentialTrackWidth":0.5588 }, "sampleType":"Swerve", - "waypoints":[0.0,1.33038,2.8063,3.63935,4.23379,5.34834,5.7176,6.44625], + "waypoints":[0.0,0.00057], "samples":[ - {"t":0.0, "x":4.42804, "y":0.39542, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":9.42914, "ay":0.01609, "alpha":0.0, "fx":[160.387,160.387,160.387,160.387], "fy":[0.27367,0.27367,0.27367,0.27367]}, - {"t":0.04927, "x":4.43949, "y":0.39544, "heading":1.5708, "vx":0.46461, "vy":0.00079, "omega":0.0, "ax":9.42778, "ay":0.00808, "alpha":0.0, "fx":[160.36377,160.36377,160.36377,160.36377], "fy":[0.13744,0.13744,0.13744,0.13744]}, - {"t":0.09855, "x":4.47383, "y":0.39549, "heading":1.5708, "vx":0.92915, "vy":0.00119, "omega":0.0, "ax":9.42585, "ay":0.00003, "alpha":0.0, "fx":[160.33107,160.33107,160.33107,160.33107], "fy":[0.00051,0.00051,0.00051,0.00051]}, - {"t":0.14782, "x":4.53105, "y":0.39555, "heading":1.5708, "vx":1.39359, "vy":0.00119, "omega":0.0, "ax":9.42296, "ay":-0.00809, "alpha":0.0, "fx":[160.28188,160.28188,160.28188,160.28188], "fy":[-0.13767,-0.13767,-0.13767,-0.13767]}, - {"t":0.19709, "x":4.61116, "y":0.3956, "heading":1.5708, "vx":1.85789, "vy":0.00079, "omega":0.0, "ax":9.41814, "ay":-0.01637, "alpha":0.0, "fx":[160.19984,160.19984,160.19984,160.19984], "fy":[-0.27841,-0.27841,-0.27841,-0.27841]}, - {"t":0.24637, "x":4.71413, "y":0.39562, "heading":1.5708, "vx":2.32195, "vy":-0.00001, "omega":0.0, "ax":9.40851, "ay":-0.02502, "alpha":0.0, "fx":[160.03602,160.03602,160.03602,160.03602], "fy":[-0.4255,-0.4255,-0.4255,-0.4255]}, - {"t":0.29564, "x":4.83997, "y":0.39559, "heading":1.5708, "vx":2.78554, "vy":-0.00125, "omega":0.0, "ax":9.37979, "ay":-0.03511, "alpha":0.0, "fx":[159.54751,159.54751,159.54751,159.54751], "fy":[-0.5972,-0.5972,-0.5972,-0.5972]}, - {"t":0.34491, "x":4.98861, "y":0.39548, "heading":1.5708, "vx":3.24772, "vy":-0.00298, "omega":0.0, "ax":5.2882, "ay":-0.27006, "alpha":0.0, "fx":[89.95079,89.95079,89.95079,89.95079], "fy":[-4.59362,-4.59362,-4.59362,-4.59362]}, - {"t":0.39419, "x":5.15505, "y":0.39501, "heading":1.5708, "vx":3.50829, "vy":-0.01628, "omega":0.0, "ax":-0.00021, "ay":-0.14037, "alpha":0.0, "fx":[-0.00353,-0.00353,-0.00353,-0.00353], "fy":[-2.3877,-2.3877,-2.3877,-2.3877]}, - {"t":0.44346, "x":5.32792, "y":0.39404, "heading":1.5708, "vx":3.50828, "vy":-0.0232, "omega":0.0, "ax":-0.00095, "ay":-0.12602, "alpha":0.0, "fx":[-0.01607,-0.01607,-0.01607,-0.01607], "fy":[-2.14358,-2.14358,-2.14358,-2.14358]}, - {"t":0.49273, "x":5.50078, "y":0.39274, "heading":1.5708, "vx":3.50823, "vy":-0.02941, "omega":0.0, "ax":-0.00119, "ay":-0.12858, "alpha":0.0, "fx":[-0.02031,-0.02031,-0.02031,-0.02031], "fy":[-2.18709,-2.18709,-2.18709,-2.18709]}, - {"t":0.54201, "x":5.67364, "y":0.39114, "heading":1.5708, "vx":3.50817, "vy":-0.03574, "omega":0.0, "ax":-0.00149, "ay":-0.13361, "alpha":0.0, "fx":[-0.02528,-0.02528,-0.02528,-0.02528], "fy":[-2.2726,-2.2726,-2.2726,-2.2726]}, - {"t":0.59128, "x":5.8465, "y":0.38921, "heading":1.5708, "vx":3.5081, "vy":-0.04233, "omega":0.0, "ax":-0.00183, "ay":-0.14027, "alpha":0.0, "fx":[-0.03113,-0.03113,-0.03113,-0.03113], "fy":[-2.38594,-2.38594,-2.38594,-2.38594]}, - {"t":0.64055, "x":6.01935, "y":0.38696, "heading":1.5708, "vx":3.50801, "vy":-0.04924, "omega":0.0, "ax":-0.00224, "ay":-0.14876, "alpha":0.0, "fx":[-0.03814,-0.03814,-0.03814,-0.03814], "fy":[-2.53038,-2.53038,-2.53038,-2.53038]}, - {"t":0.68983, "x":6.1922, "y":0.38435, "heading":1.5708, "vx":3.5079, "vy":-0.05657, "omega":0.0, "ax":-0.0027, "ay":-0.15685, "alpha":0.0, "fx":[-0.04594,-0.04594,-0.04594,-0.04594], "fy":[-2.66803,-2.66803,-2.66803,-2.66803]}, - {"t":0.7391, "x":6.36504, "y":0.38137, "heading":1.5708, "vx":3.50776, "vy":-0.0643, "omega":0.0, "ax":-0.00244, "ay":-0.12704, "alpha":0.0, "fx":[-0.0415,-0.0415,-0.0415,-0.0415], "fy":[-2.16091,-2.16091,-2.16091,-2.16091]}, - {"t":0.78837, "x":6.53788, "y":0.37805, "heading":1.5708, "vx":3.50764, "vy":-0.07056, "omega":0.0, "ax":0.00736, "ay":0.43208, "alpha":0.0, "fx":[0.12516,0.12516,0.12516,0.12516], "fy":[7.34951,7.34951,7.34951,7.34951]}, - {"t":0.83765, "x":6.71072, "y":0.3751, "heading":1.5708, "vx":3.50801, "vy":-0.04927, "omega":0.0, "ax":-0.12459, "ay":5.31765, "alpha":0.0, "fx":[-2.11922,-2.11922,-2.11922,-2.11922], "fy":[90.45167,90.45167,90.45167,90.45167]}, - {"t":0.88692, "x":6.88342, "y":0.37913, "heading":1.5708, "vx":3.50187, "vy":0.21275, "omega":0.0, "ax":-1.0928, "ay":8.7944, "alpha":0.0, "fx":[-18.5882,-18.5882,-18.5882,-18.5882], "fy":[149.59031,149.59031,149.59031,149.59031]}, - {"t":0.9362, "x":7.05465, "y":0.40028, "heading":1.5708, "vx":3.44802, "vy":0.64608, "omega":0.0, "ax":-3.76941, "ay":8.45528, "alpha":0.0, "fx":[-64.11651,-64.11651,-64.11651,-64.11651], "fy":[143.82197,143.82197,143.82197,143.82197]}, - {"t":0.98547, "x":7.21997, "y":0.44238, "heading":1.5708, "vx":3.26229, "vy":1.0627, "omega":0.0, "ax":-8.98745, "ay":2.70995, "alpha":0.0, "fx":[-152.87389,-152.87389,-152.87389,-152.87389], "fy":[46.09554,46.09554,46.09554,46.09554]}, - {"t":1.03474, "x":7.3698, "y":0.49804, "heading":1.5708, "vx":2.81945, "vy":1.19623, "omega":0.0, "ax":-9.27148, "ay":1.61005, "alpha":0.0, "fx":[-157.70528,-157.70528,-157.70528,-157.70528], "fy":[27.38656,27.38656,27.38656,27.38656]}, - {"t":1.08402, "x":7.49747, "y":0.55893, "heading":1.5708, "vx":2.36261, "vy":1.27556, "omega":0.0, "ax":-9.34266, "ay":1.19582, "alpha":0.0, "fx":[-158.91606,-158.91606,-158.91606,-158.91606], "fy":[20.34053,20.34053,20.34053,20.34053]}, - {"t":1.13329, "x":7.60254, "y":0.62324, "heading":1.5708, "vx":1.90226, "vy":1.33449, "omega":0.0, "ax":-9.37221, "ay":0.98055, "alpha":0.0, "fx":[-159.41868,-159.41868,-159.41868,-159.41868], "fy":[16.67882,16.67882,16.67882,16.67882]}, - {"t":1.18256, "x":7.6849, "y":0.69018, "heading":1.5708, "vx":1.44046, "vy":1.3828, "omega":0.0, "ax":-9.38778, "ay":0.84916, "alpha":0.0, "fx":[-159.68349,-159.68349,-159.68349,-159.68349], "fy":[14.44393,14.44393,14.44393,14.44393]}, - {"t":1.23184, "x":7.74448, "y":0.75935, "heading":1.5708, "vx":0.9779, "vy":1.42464, "omega":0.0, "ax":-9.3972, "ay":0.76085, "alpha":0.0, "fx":[-159.84376,-159.84376,-159.84376,-159.84376], "fy":[12.94181,12.94181,12.94181,12.94181]}, - {"t":1.28111, "x":7.78125, "y":0.83047, "heading":1.5708, "vx":0.51486, "vy":1.46213, "omega":0.0, "ax":-9.40345, "ay":0.69756, "alpha":0.0, "fx":[-159.94994,-159.94994,-159.94994,-159.94994], "fy":[11.86523,11.86523,11.86523,11.86523]}, - {"t":1.33038, "x":7.79521, "y":0.90336, "heading":1.5708, "vx":0.05152, "vy":1.4965, "omega":0.0, "ax":-2.22574, "ay":0.04981, "alpha":0.0, "fx":[-37.85912,-37.85912,-37.85912,-37.85912], "fy":[0.84733,0.84733,0.84733,0.84733]}, - {"t":1.37651, "x":7.79522, "y":0.97243, "heading":1.5708, "vx":-0.05113, "vy":1.4988, "omega":0.0, "ax":2.20045, "ay":0.00057, "alpha":0.0, "fx":[37.42898,37.42898,37.42898,37.42898], "fy":[0.0097,0.0097,0.0097,0.0097]}, - {"t":1.42263, "x":7.7952, "y":1.04156, "heading":1.5708, "vx":0.05036, "vy":1.49883, "omega":0.0, "ax":-2.16676, "ay":0.00056, "alpha":0.0, "fx":[-36.85589,-36.85589,-36.85589,-36.85589], "fy":[0.00954,0.00954,0.00954,0.00954]}, - {"t":1.46875, "x":7.79522, "y":1.11069, "heading":1.5708, "vx":-0.04958, "vy":1.49885, "omega":0.0, "ax":2.1331, "ay":0.00055, "alpha":0.0, "fx":[36.28347,36.28347,36.28347,36.28347], "fy":[0.00938,0.00938,0.00938,0.00938]}, - {"t":1.51487, "x":7.7952, "y":1.17982, "heading":1.5708, "vx":0.0488, "vy":1.49888, "omega":0.0, "ax":-2.09949, "ay":0.00054, "alpha":0.0, "fx":[-35.71173,-35.71173,-35.71173,-35.71173], "fy":[0.00922,0.00922,0.00922,0.00922]}, - {"t":1.56099, "x":7.79522, "y":1.24896, "heading":1.5708, "vx":-0.04803, "vy":1.4989, "omega":0.0, "ax":2.06592, "ay":0.00053, "alpha":0.0, "fx":[35.14066,35.14066,35.14066,35.14066], "fy":[0.00906,0.00906,0.00906,0.00906]}, - {"t":1.60712, "x":7.7952, "y":1.31809, "heading":1.5708, "vx":0.04726, "vy":1.49893, "omega":0.0, "ax":-2.03238, "ay":0.00052, "alpha":0.0, "fx":[-34.57028,-34.57028,-34.57028,-34.57028], "fy":[0.0089,0.0089,0.0089,0.0089]}, - {"t":1.65324, "x":7.79522, "y":1.38722, "heading":1.5708, "vx":-0.04648, "vy":1.49895, "omega":0.0, "ax":1.99889, "ay":0.00051, "alpha":0.0, "fx":[34.00057,34.00057,34.00057,34.00057], "fy":[0.00875,0.00875,0.00875,0.00875]}, - {"t":1.69936, "x":7.7952, "y":1.45636, "heading":1.5708, "vx":0.04571, "vy":1.49898, "omega":0.0, "ax":-1.96544, "ay":0.00051, "alpha":0.0, "fx":[-33.43154,-33.43154,-33.43154,-33.43154], "fy":[0.00859,0.00859,0.00859,0.00859]}, - {"t":1.74548, "x":7.79522, "y":1.5255, "heading":1.5708, "vx":-0.04494, "vy":1.499, "omega":0.0, "ax":1.93203, "ay":0.0005, "alpha":0.0, "fx":[32.8632,32.8632,32.8632,32.8632], "fy":[0.00843,0.00843,0.00843,0.00843]}, - {"t":1.79161, "x":7.7952, "y":1.59464, "heading":1.5708, "vx":0.04417, "vy":1.49902, "omega":0.0, "ax":-1.89865, "ay":0.00049, "alpha":0.0, "fx":[-32.29554,-32.29554,-32.29554,-32.29554], "fy":[0.00828,0.00828,0.00828,0.00828]}, - {"t":1.83773, "x":7.79522, "y":1.66377, "heading":1.5708, "vx":-0.0434, "vy":1.49904, "omega":0.0, "ax":1.86532, "ay":0.00048, "alpha":0.0, "fx":[31.72857,31.72857,31.72857,31.72857], "fy":[0.00812,0.00812,0.00812,0.00812]}, - {"t":1.88385, "x":7.7952, "y":1.73291, "heading":1.5708, "vx":0.04263, "vy":1.49907, "omega":0.0, "ax":-1.83203, "ay":0.00047, "alpha":0.0, "fx":[-31.16228,-31.16228,-31.16228,-31.16228], "fy":[0.00797,0.00797,0.00797,0.00797]}, - {"t":1.92997, "x":7.79522, "y":1.80206, "heading":1.5708, "vx":-0.04187, "vy":1.49909, "omega":0.0, "ax":1.79878, "ay":0.00046, "alpha":0.0, "fx":[30.59668,30.59668,30.59668,30.59668], "fy":[0.00782,0.00782,0.00782,0.00782]}, - {"t":1.9761, "x":7.7952, "y":1.8712, "heading":1.5708, "vx":0.0411, "vy":1.49911, "omega":0.0, "ax":-1.76557, "ay":0.00045, "alpha":0.0, "fx":[-30.03177,-30.03177,-30.03177,-30.03177], "fy":[0.00766,0.00766,0.00766,0.00766]}, - {"t":2.02222, "x":7.79522, "y":1.94034, "heading":1.5708, "vx":-0.04033, "vy":1.49913, "omega":0.0, "ax":1.73239, "ay":0.00044, "alpha":0.0, "fx":[29.46754,29.46754,29.46754,29.46754], "fy":[0.00751,0.00751,0.00751,0.00751]}, - {"t":2.06834, "x":7.7952, "y":2.00948, "heading":1.5708, "vx":0.03957, "vy":1.49915, "omega":0.0, "ax":-1.69926, "ay":0.00043, "alpha":0.0, "fx":[-28.904,-28.904,-28.904,-28.904], "fy":[0.00736,0.00736,0.00736,0.00736]}, - {"t":2.11446, "x":7.79522, "y":2.07863, "heading":1.5708, "vx":-0.03881, "vy":1.49917, "omega":0.0, "ax":1.66617, "ay":0.00042, "alpha":0.0, "fx":[28.34114,28.34114,28.34114,28.34114], "fy":[0.0072,0.0072,0.0072,0.0072]}, - {"t":2.16058, "x":7.7952, "y":2.14777, "heading":1.5708, "vx":0.03804, "vy":1.49919, "omega":0.0, "ax":-1.63312, "ay":0.00041, "alpha":0.0, "fx":[-27.77897,-27.77897,-27.77897,-27.77897], "fy":[0.00705,0.00705,0.00705,0.00705]}, - {"t":2.20671, "x":7.79522, "y":2.21692, "heading":1.5708, "vx":-0.03728, "vy":1.49921, "omega":0.0, "ax":1.60011, "ay":0.00041, "alpha":0.0, "fx":[27.21748,27.21748,27.21748,27.21748], "fy":[0.0069,0.0069,0.0069,0.0069]}, - {"t":2.25283, "x":7.7952, "y":2.28607, "heading":1.5708, "vx":0.03652, "vy":1.49923, "omega":0.0, "ax":-1.56714, "ay":0.0004, "alpha":0.0, "fx":[-26.65666,-26.65666,-26.65666,-26.65666], "fy":[0.00675,0.00675,0.00675,0.00675]}, - {"t":2.29895, "x":7.79522, "y":2.35522, "heading":1.5708, "vx":-0.03576, "vy":1.49925, "omega":0.0, "ax":1.53421, "ay":0.00039, "alpha":0.0, "fx":[26.09653,26.09653,26.09653,26.09653], "fy":[0.0066,0.0066,0.0066,0.0066]}, - {"t":2.34507, "x":7.7952, "y":2.42437, "heading":1.5708, "vx":0.035, "vy":1.49926, "omega":0.0, "ax":-1.50132, "ay":0.00038, "alpha":0.0, "fx":[-25.53706,-25.53706,-25.53706,-25.53706], "fy":[0.00645,0.00645,0.00645,0.00645]}, - {"t":2.3912, "x":7.79522, "y":2.49352, "heading":1.5708, "vx":-0.03424, "vy":1.49928, "omega":0.0, "ax":1.46847, "ay":0.00037, "alpha":0.0, "fx":[24.97827,24.97827,24.97827,24.97827], "fy":[0.0063,0.0063,0.0063,0.0063]}, - {"t":2.43732, "x":7.7952, "y":2.56267, "heading":1.5708, "vx":0.03349, "vy":1.4993, "omega":0.0, "ax":-1.43566, "ay":0.00036, "alpha":0.0, "fx":[-24.42014,-24.42014,-24.42014,-24.42014], "fy":[0.00616,0.00616,0.00616,0.00616]}, - {"t":2.48344, "x":7.79522, "y":2.63182, "heading":1.5708, "vx":-0.03273, "vy":1.49932, "omega":0.0, "ax":1.40289, "ay":0.00035, "alpha":0.0, "fx":[23.86268,23.86268,23.86268,23.86268], "fy":[0.00601,0.00601,0.00601,0.00601]}, - {"t":2.52956, "x":7.7952, "y":2.70097, "heading":1.5708, "vx":0.03197, "vy":1.49933, "omega":0.0, "ax":-1.37015, "ay":0.00034, "alpha":0.0, "fx":[-23.30587,-23.30587,-23.30587,-23.30587], "fy":[0.00586,0.00586,0.00586,0.00586]}, - {"t":2.57569, "x":7.79522, "y":2.77012, "heading":1.5708, "vx":-0.03122, "vy":1.49935, "omega":0.0, "ax":1.33745, "ay":0.00033, "alpha":0.0, "fx":[22.74971,22.74971,22.74971,22.74971], "fy":[0.00557,0.00557,0.00557,0.00557]}, - {"t":2.62181, "x":7.7952, "y":2.83928, "heading":1.5708, "vx":0.03047, "vy":1.49936, "omega":0.0, "ax":-1.30482, "ay":-1.71976, "alpha":0.0, "fx":[-22.19464,-22.19464,-22.19464,-22.19464], "fy":[-29.25266,-29.25266,-29.25266,-29.25266]}, - {"t":2.66793, "x":7.79522, "y":2.9066, "heading":1.5708, "vx":-0.02971, "vy":1.42004, "omega":0.0, "ax":1.2724, "ay":-9.32278, "alpha":0.0, "fx":[21.64321,21.64321,21.64321,21.64321], "fy":[-158.57786,-158.57786,-158.57786,-158.57786]}, - {"t":2.71405, "x":7.7952, "y":2.96218, "heading":1.5708, "vx":0.02897, "vy":0.99005, "omega":0.0, "ax":-1.24066, "ay":-9.34128, "alpha":0.0, "fx":[-21.10324,-21.10324,-21.10324,-21.10324], "fy":[-158.8925,-158.8925,-158.8925,-158.8925]}, - {"t":2.76018, "x":7.79522, "y":2.99791, "heading":1.5708, "vx":-0.02825, "vy":0.55921, "omega":0.0, "ax":1.21741, "ay":-9.34908, "alpha":0.0, "fx":[20.70782,20.70782,20.70782,20.70782], "fy":[-159.02511,-159.02511,-159.02511,-159.02511]}, - {"t":2.8063, "x":7.79521, "y":3.01376, "heading":1.5708, "vx":0.0279, "vy":0.12801, "omega":0.0, "ax":-2.76759, "ay":-9.01298, "alpha":0.0, "fx":[-47.07584,-47.07584,-47.07584,-47.07584], "fy":[-153.30824,-153.30824,-153.30824,-153.30824]}, - {"t":2.84795, "x":7.79397, "y":3.01127, "heading":1.5708, "vx":-0.08738, "vy":-0.2474, "omega":0.0, "ax":-2.76716, "ay":-9.01192, "alpha":0.0, "fx":[-47.06855,-47.06855,-47.06855,-47.06855], "fy":[-153.29021,-153.29021,-153.29021,-153.29021]}, - {"t":2.8896, "x":7.78793, "y":2.99315, "heading":1.5708, "vx":-0.20264, "vy":-0.62277, "omega":0.0, "ax":-2.76661, "ay":-9.01056, "alpha":0.0, "fx":[-47.05928,-47.05928,-47.05928,-47.05928], "fy":[-153.26697,-153.26697,-153.26697,-153.26697]}, - {"t":2.93126, "x":7.77709, "y":2.95939, "heading":1.5708, "vx":-0.31787, "vy":-0.99809, "omega":0.0, "ax":-2.76589, "ay":-9.00873, "alpha":0.0, "fx":[-47.04704,-47.04704,-47.04704,-47.04704], "fy":[-153.23591,-153.23591,-153.23591,-153.23591]}, - {"t":2.97291, "x":7.76145, "y":2.91, "heading":1.5708, "vx":-0.43308, "vy":-1.37332, "omega":0.0, "ax":-2.7649, "ay":-9.00617, "alpha":0.0, "fx":[-47.03008,-47.03008,-47.03008,-47.03008], "fy":[-153.19232,-153.19232,-153.19232,-153.19232]}, - {"t":3.01456, "x":7.74101, "y":2.84499, "heading":1.5708, "vx":-0.54825, "vy":-1.74845, "omega":0.0, "ax":-2.76341, "ay":-9.00231, "alpha":0.0, "fx":[-47.00483,-47.00483,-47.00483,-47.00483], "fy":[-153.12673,-153.12673,-153.12673,-153.12673]}, - {"t":3.05621, "x":7.71578, "y":2.76435, "heading":1.5708, "vx":-0.66335, "vy":-2.12342, "omega":0.0, "ax":-2.76096, "ay":-8.99586, "alpha":0.0, "fx":[-46.96306,-46.96306,-46.96306,-46.96306], "fy":[-153.01701,-153.01701,-153.01701,-153.01701]}, - {"t":3.09787, "x":7.68575, "y":2.6681, "heading":1.5708, "vx":-0.77835, "vy":-2.49812, "omega":0.0, "ax":-2.75607, "ay":-8.98289, "alpha":0.0, "fx":[-46.87998,-46.87998,-46.87998,-46.87998], "fy":[-152.79644,-152.79644,-152.79644,-152.79644]}, - {"t":3.13952, "x":7.65094, "y":2.55626, "heading":1.5708, "vx":-0.89315, "vy":-2.87228, "omega":0.0, "ax":-2.74147, "ay":-8.94366, "alpha":0.0, "fx":[-46.63159,-46.63159,-46.63159,-46.63159], "fy":[-152.12916,-152.12916,-152.12916,-152.12916]}, - {"t":3.18117, "x":7.61136, "y":2.42886, "heading":1.5708, "vx":-1.00734, "vy":-3.24481, "omega":0.0, "ax":-0.56928, "ay":-2.59355, "alpha":0.0, "fx":[-9.68327,-9.68327,-9.68327,-9.68327], "fy":[-44.11556,-44.11556,-44.11556,-44.11556]}, - {"t":3.22282, "x":7.56891, "y":2.29146, "heading":1.5708, "vx":-1.03105, "vy":-3.35284, "omega":0.0, "ax":0.02743, "ay":-0.00888, "alpha":0.0, "fx":[0.46654,0.46654,0.46654,0.46654], "fy":[-0.15109,-0.15109,-0.15109,-0.15109]}, - {"t":3.26448, "x":7.52599, "y":2.15179, "heading":1.5708, "vx":-1.02991, "vy":-3.35321, "omega":0.0, "ax":0.00178, "ay":-0.00055, "alpha":0.0, "fx":[0.03024,0.03024,0.03024,0.03024], "fy":[-0.00937,-0.00937,-0.00937,-0.00937]}, - {"t":3.30613, "x":7.48309, "y":2.01212, "heading":1.5708, "vx":-1.02983, "vy":-3.35323, "omega":0.0, "ax":-0.01922, "ay":0.0059, "alpha":0.0, "fx":[-0.32697,-0.32697,-0.32697,-0.32697], "fy":[0.10037,0.10037,0.10037,0.10037]}, - {"t":3.34778, "x":7.44018, "y":1.87246, "heading":1.5708, "vx":-1.03063, "vy":-3.35299, "omega":0.0, "ax":-0.17282, "ay":0.05332, "alpha":0.0, "fx":[-2.9397,-2.9397,-2.9397,-2.9397], "fy":[0.90699,0.90699,0.90699,0.90699]}, - {"t":3.38943, "x":7.3971, "y":1.73284, "heading":1.5708, "vx":-1.03783, "vy":-3.35077, "omega":0.0, "ax":-1.41828, "ay":0.45316, "alpha":0.0, "fx":[-24.1245,-24.1245,-24.1245,-24.1245], "fy":[7.70814,7.70814,7.70814,7.70814]}, - {"t":3.43109, "x":7.35264, "y":1.59367, "heading":1.5708, "vx":-1.09691, "vy":-3.33189, "omega":0.0, "ax":-6.11048, "ay":2.3277, "alpha":0.0, "fx":[-103.93747,-103.93747,-103.93747,-103.93747], "fy":[39.59356,39.59356,39.59356,39.59356]}, - {"t":3.47274, "x":7.30165, "y":1.45691, "heading":1.5708, "vx":-1.35142, "vy":-3.23494, "omega":0.0, "ax":0.6541, "ay":9.20157, "alpha":0.0, "fx":[11.12608,11.12608,11.12608,11.12608], "fy":[156.5161,156.5161,156.5161,156.5161]}, - {"t":3.51439, "x":7.24593, "y":1.33014, "heading":1.5708, "vx":-1.32418, "vy":-2.85167, "omega":0.0, "ax":2.17898, "ay":9.12125, "alpha":0.0, "fx":[37.06383,37.06383,37.06383,37.06383], "fy":[155.14988,155.14988,155.14988,155.14988]}, - {"t":3.55604, "x":7.19266, "y":1.21928, "heading":1.5708, "vx":-1.23342, "vy":-2.47174, "omega":0.0, "ax":2.42598, "ay":9.08463, "alpha":0.0, "fx":[41.26526,41.26526,41.26526,41.26526], "fy":[154.52699,154.52699,154.52699,154.52699]}, - {"t":3.5977, "x":7.14339, "y":1.1242, "heading":1.5708, "vx":-1.13237, "vy":-2.09334, "omega":0.0, "ax":2.52674, "ay":9.06774, "alpha":0.0, "fx":[42.9792,42.9792,42.9792,42.9792], "fy":[154.2396,154.2396,154.2396,154.2396]}, - {"t":3.63935, "x":7.09842, "y":1.04488, "heading":1.5708, "vx":-1.02712, "vy":-1.71565, "omega":0.0, "ax":-7.55729, "ay":5.41304, "alpha":0.0, "fx":[-128.54727,-128.54727,-128.54727,-128.54727], "fy":[92.07424,92.07424,92.07424,92.07424]}, - {"t":3.67432, "x":7.05788, "y":0.98819, "heading":1.5708, "vx":-1.29138, "vy":-1.52637, "omega":0.0, "ax":-6.42191, "ay":6.36849, "alpha":0.0, "fx":[-109.23482,-109.23482,-109.23482,-109.23482], "fy":[108.32623,108.32623,108.32623,108.32623]}, - {"t":3.70928, "x":7.0088, "y":0.93872, "heading":1.5708, "vx":-1.51594, "vy":-1.30368, "omega":0.0, "ax":-4.1773, "ay":5.49594, "alpha":0.0, "fx":[-71.05465,-71.05465,-71.05465,-71.05465], "fy":[93.48442,93.48442,93.48442,93.48442]}, - {"t":3.74425, "x":6.95324, "y":0.89649, "heading":1.5708, "vx":-1.66201, "vy":-1.1115, "omega":0.0, "ax":-0.58362, "ay":0.89045, "alpha":0.0, "fx":[-9.92713,-9.92713,-9.92713,-9.92713], "fy":[15.14634,15.14634,15.14634,15.14634]}, - {"t":3.77922, "x":6.89477, "y":0.85817, "heading":1.5708, "vx":-1.68242, "vy":-1.08037, "omega":0.0, "ax":-0.03911, "ay":0.06099, "alpha":0.0, "fx":[-0.66532,-0.66532,-0.66532,-0.66532], "fy":[1.03748,1.03748,1.03748,1.03748]}, - {"t":3.81419, "x":6.83591, "y":0.82043, "heading":1.5708, "vx":-1.68378, "vy":-1.07823, "omega":0.0, "ax":0.00037, "ay":-0.00057, "alpha":0.0, "fx":[0.00622,0.00622,0.00622,0.00622], "fy":[-0.00975,-0.00975,-0.00975,-0.00975]}, - {"t":3.84915, "x":6.77704, "y":0.78272, "heading":1.5708, "vx":-1.68377, "vy":-1.07825, "omega":0.0, "ax":0.00347, "ay":-0.00542, "alpha":0.0, "fx":[0.05907,0.05907,0.05907,0.05907], "fy":[-0.09227,-0.09227,-0.09227,-0.09227]}, - {"t":3.88412, "x":6.71816, "y":0.74502, "heading":1.5708, "vx":-1.68365, "vy":-1.07844, "omega":0.0, "ax":0.004, "ay":-0.00624, "alpha":0.0, "fx":[0.068,0.068,0.068,0.068], "fy":[-0.10619,-0.10619,-0.10619,-0.10619]}, - {"t":3.91909, "x":6.65929, "y":0.7073, "heading":1.5708, "vx":-1.68351, "vy":-1.07866, "omega":0.0, "ax":0.0044, "ay":-0.00686, "alpha":0.0, "fx":[0.07478,0.07478,0.07478,0.07478], "fy":[-0.11674,-0.11674,-0.11674,-0.11674]}, - {"t":3.95406, "x":6.60043, "y":0.66958, "heading":1.5708, "vx":-1.68336, "vy":-1.0789, "omega":0.0, "ax":0.00487, "ay":-0.00759, "alpha":0.0, "fx":[0.08275,0.08275,0.08275,0.08275], "fy":[-0.12915,-0.12915,-0.12915,-0.12915]}, - {"t":3.98902, "x":6.54157, "y":0.63185, "heading":1.5708, "vx":-1.68319, "vy":-1.07917, "omega":0.0, "ax":0.00542, "ay":-0.00846, "alpha":0.0, "fx":[0.0922,0.0922,0.0922,0.0922], "fy":[-0.14384,-0.14384,-0.14384,-0.14384]}, - {"t":4.02399, "x":6.48271, "y":0.59411, "heading":1.5708, "vx":-1.683, "vy":-1.07946, "omega":0.0, "ax":0.00567, "ay":-0.00885, "alpha":0.0, "fx":[0.09648,0.09648,0.09648,0.09648], "fy":[-0.15047,-0.15047,-0.15047,-0.15047]}, - {"t":4.05896, "x":6.42387, "y":0.55636, "heading":1.5708, "vx":-1.6828, "vy":-1.07977, "omega":0.0, "ax":-0.00053, "ay":0.00083, "alpha":0.0, "fx":[-0.00909,-0.00909,-0.00909,-0.00909], "fy":[0.01409,0.01409,0.01409,0.01409]}, - {"t":4.09392, "x":6.36502, "y":0.5186, "heading":1.5708, "vx":-1.68282, "vy":-1.07974, "omega":0.0, "ax":-0.09942, "ay":0.15549, "alpha":0.0, "fx":[-1.69106,-1.69106,-1.69106,-1.69106], "fy":[2.64491,2.64491,2.64491,2.64491]}, - {"t":4.12889, "x":6.30612, "y":0.48094, "heading":1.5708, "vx":-1.68629, "vy":-1.0743, "omega":0.0, "ax":-1.32663, "ay":2.18913, "alpha":0.0, "fx":[-22.56553,-22.56553,-22.56553,-22.56553], "fy":[37.23649,37.23649,37.23649,37.23649]}, - {"t":4.16386, "x":6.24634, "y":0.44471, "heading":1.5708, "vx":-1.73268, "vy":-0.99776, "omega":0.0, "ax":-3.6034, "ay":7.46214, "alpha":0.0, "fx":[-61.29282,-61.29282,-61.29282,-61.29282], "fy":[126.92882,126.92882,126.92882,126.92882]}, - {"t":4.19883, "x":6.18355, "y":0.41439, "heading":1.5708, "vx":-1.85868, "vy":-0.73683, "omega":0.0, "ax":-2.67087, "ay":8.77939, "alpha":0.0, "fx":[-45.43067,-45.43067,-45.43067,-45.43067], "fy":[149.33499,149.33499,149.33499,149.33499]}, - {"t":4.23379, "x":6.11693, "y":0.39399, "heading":1.5708, "vx":-1.95207, "vy":-0.42983, "omega":0.0, "ax":-1.13043, "ay":9.07094, "alpha":0.0, "fx":[-19.22824,-19.22824,-19.22824,-19.22824], "fy":[154.29407,154.29407,154.29407,154.29407]}, - {"t":4.27507, "x":6.03538, "y":0.38397, "heading":1.5708, "vx":-1.99874, "vy":-0.05539, "omega":0.0, "ax":0.0117, "ay":3.06112, "alpha":0.0, "fx":[0.19904,0.19904,0.19904,0.19904], "fy":[52.06873,52.06873,52.06873,52.06873]}, - {"t":4.31635, "x":5.95289, "y":0.3843, "heading":1.5708, "vx":-1.99826, "vy":0.07097, "omega":0.0, "ax":-0.01961, "ay":-0.69159, "alpha":0.0, "fx":[-0.33362,-0.33362,-0.33362,-0.33362], "fy":[-11.7637,-11.7637,-11.7637,-11.7637]}, - {"t":4.35763, "x":5.87038, "y":0.38664, "heading":1.5708, "vx":-1.99907, "vy":0.04242, "omega":0.0, "ax":-0.00492, "ay":-0.26625, "alpha":0.0, "fx":[-0.08364,-0.08364,-0.08364,-0.08364], "fy":[-4.52885,-4.52885,-4.52885,-4.52885]}, - {"t":4.39891, "x":5.78786, "y":0.38816, "heading":1.5708, "vx":-1.99927, "vy":0.03143, "omega":0.0, "ax":-0.00236, "ay":-0.1687, "alpha":0.0, "fx":[-0.04011,-0.04011,-0.04011,-0.04011], "fy":[-2.8696,-2.8696,-2.8696,-2.8696]}, - {"t":4.44019, "x":5.70533, "y":0.38932, "heading":1.5708, "vx":-1.99937, "vy":0.02447, "omega":0.0, "ax":-0.00138, "ay":-0.12634, "alpha":0.0, "fx":[-0.02349,-0.02349,-0.02349,-0.02349], "fy":[-2.14895,-2.14895,-2.14895,-2.14895]}, - {"t":4.48147, "x":5.62279, "y":0.39022, "heading":1.5708, "vx":-1.99942, "vy":0.01925, "omega":0.0, "ax":-0.00086, "ay":-0.10035, "alpha":0.0, "fx":[-0.01467,-0.01467,-0.01467,-0.01467], "fy":[-1.70685,-1.70685,-1.70685,-1.70685]}, - {"t":4.52275, "x":5.54026, "y":0.39093, "heading":1.5708, "vx":-1.99946, "vy":0.01511, "omega":0.0, "ax":-0.00055, "ay":-0.08195, "alpha":0.0, "fx":[-0.00936,-0.00936,-0.00936,-0.00936], "fy":[-1.39391,-1.39391,-1.39391,-1.39391]}, - {"t":4.56403, "x":5.45772, "y":0.39148, "heading":1.5708, "vx":-1.99948, "vy":0.01173, "omega":0.0, "ax":-0.00035, "ay":-0.06798, "alpha":0.0, "fx":[-0.00597,-0.00597,-0.00597,-0.00597], "fy":[-1.15629,-1.15629,-1.15629,-1.15629]}, - {"t":4.60531, "x":5.37518, "y":0.39191, "heading":1.5708, "vx":-1.9995, "vy":0.00892, "omega":0.0, "ax":-0.00022, "ay":-0.05697, "alpha":0.0, "fx":[-0.00376,-0.00376,-0.00376,-0.00376], "fy":[-0.96897,-0.96897,-0.96897,-0.96897]}, - {"t":4.64659, "x":5.29264, "y":0.39223, "heading":1.5708, "vx":-1.9995, "vy":0.00657, "omega":0.0, "ax":-0.00013, "ay":-0.04812, "alpha":0.0, "fx":[-0.00229,-0.00229,-0.00229,-0.00229], "fy":[-0.81856,-0.81856,-0.81856,-0.81856]}, - {"t":4.68787, "x":5.2101, "y":0.39246, "heading":1.5708, "vx":-1.99951, "vy":0.00458, "omega":0.0, "ax":-0.00008, "ay":-0.04099, "alpha":0.0, "fx":[-0.00131,-0.00131,-0.00131,-0.00131], "fy":[-0.69716,-0.69716,-0.69716,-0.69716]}, - {"t":4.72915, "x":5.12756, "y":0.39261, "heading":1.5708, "vx":-1.99951, "vy":0.00289, "omega":0.0, "ax":-0.00004, "ay":-0.03526, "alpha":0.0, "fx":[-0.00065,-0.00065,-0.00065,-0.00065], "fy":[-0.59971,-0.59971,-0.59971,-0.59971]}, - {"t":4.77043, "x":5.04502, "y":0.3927, "heading":1.5708, "vx":-1.99951, "vy":0.00144, "omega":0.0, "ax":-0.00001, "ay":-0.03073, "alpha":0.0, "fx":[-0.00021,-0.00021,-0.00021,-0.00021], "fy":[-0.52279,-0.52279,-0.52279,-0.52279]}, - {"t":4.81171, "x":4.96248, "y":0.39273, "heading":1.5708, "vx":-1.99952, "vy":0.00017, "omega":0.0, "ax":0.00001, "ay":-0.02728, "alpha":0.0, "fx":[0.00009,0.00009,0.00009,0.00009], "fy":[-0.46397,-0.46397,-0.46397,-0.46397]}, - {"t":4.85299, "x":4.87995, "y":0.39272, "heading":1.5708, "vx":-1.99951, "vy":-0.00096, "omega":0.0, "ax":0.00002, "ay":-0.02478, "alpha":0.0, "fx":[0.00031,0.00031,0.00031,0.00031], "fy":[-0.42153,-0.42153,-0.42153,-0.42153]}, - {"t":4.89427, "x":4.79741, "y":0.39266, "heading":1.5708, "vx":-1.99951, "vy":-0.00198, "omega":0.0, "ax":0.00003, "ay":-0.02318, "alpha":0.0, "fx":[0.00048,0.00048,0.00048,0.00048], "fy":[-0.39427,-0.39427,-0.39427,-0.39427]}, - {"t":4.93555, "x":4.71487, "y":0.39256, "heading":1.5708, "vx":-1.99951, "vy":-0.00294, "omega":0.0, "ax":0.00004, "ay":-0.02243, "alpha":0.0, "fx":[0.00065,0.00065,0.00065,0.00065], "fy":[-0.38146,-0.38146,-0.38146,-0.38146]}, - {"t":4.97683, "x":4.63233, "y":0.39242, "heading":1.5708, "vx":-1.99951, "vy":-0.00386, "omega":0.0, "ax":0.00005, "ay":-0.0225, "alpha":0.0, "fx":[0.00083,0.00083,0.00083,0.00083], "fy":[-0.38277,-0.38277,-0.38277,-0.38277]}, - {"t":5.01811, "x":4.54979, "y":0.39224, "heading":1.5708, "vx":-1.99951, "vy":-0.00479, "omega":0.0, "ax":0.00006, "ay":-0.02342, "alpha":0.0, "fx":[0.00105,0.00105,0.00105,0.00105], "fy":[-0.39829,-0.39829,-0.39829,-0.39829]}, - {"t":5.05939, "x":4.46725, "y":0.39202, "heading":1.5708, "vx":-1.99951, "vy":-0.00576, "omega":0.0, "ax":0.00008, "ay":-0.02519, "alpha":0.0, "fx":[0.00134,0.00134,0.00134,0.00134], "fy":[-0.42848,-0.42848,-0.42848,-0.42848]}, - {"t":5.10067, "x":4.38471, "y":0.39176, "heading":1.5708, "vx":-1.9995, "vy":-0.0068, "omega":0.0, "ax":0.0001, "ay":-0.02788, "alpha":0.0, "fx":[0.00175,0.00175,0.00175,0.00175], "fy":[-0.47426,-0.47426,-0.47426,-0.47426]}, - {"t":5.14195, "x":4.30217, "y":0.39146, "heading":1.5708, "vx":-1.9995, "vy":-0.00795, "omega":0.0, "ax":0.00014, "ay":-0.03157, "alpha":0.0, "fx":[0.00231,0.00231,0.00231,0.00231], "fy":[-0.53697,-0.53697,-0.53697,-0.53697]}, - {"t":5.18323, "x":4.21963, "y":0.3911, "heading":1.5708, "vx":-1.99949, "vy":-0.00925, "omega":0.0, "ax":0.00018, "ay":-0.03637, "alpha":0.0, "fx":[0.00309,0.00309,0.00309,0.00309], "fy":[-0.61866,-0.61866,-0.61866,-0.61866]}, - {"t":5.2245, "x":4.1371, "y":0.39069, "heading":1.5708, "vx":-1.99949, "vy":-0.01075, "omega":0.0, "ax":0.00025, "ay":-0.04261, "alpha":0.0, "fx":[0.00421,0.00421,0.00421,0.00421], "fy":[-0.72474,-0.72474,-0.72474,-0.72474]}, - {"t":5.26578, "x":4.05456, "y":0.39021, "heading":1.5708, "vx":-1.99948, "vy":-0.01251, "omega":0.0, "ax":0.00037, "ay":-0.05402, "alpha":0.0, "fx":[0.00626,0.00626,0.00626,0.00626], "fy":[-0.91894,-0.91894,-0.91894,-0.91894]}, - {"t":5.30706, "x":3.97202, "y":0.38964, "heading":1.5708, "vx":-1.99946, "vy":-0.01474, "omega":0.0, "ax":-0.00603, "ay":-0.14631, "alpha":0.0, "fx":[-0.10253,-0.10253,-0.10253,-0.10253], "fy":[-2.48877,-2.48877,-2.48877,-2.48877]}, - {"t":5.34834, "x":3.88948, "y":0.38891, "heading":1.5708, "vx":-1.99971, "vy":-0.02078, "omega":0.0, "ax":-9.40202, "ay":-0.1119, "alpha":0.0, "fx":[-159.92575,-159.92575,-159.92575,-159.92575], "fy":[-1.90337,-1.90337,-1.90337,-1.90337]}, - {"t":5.37675, "x":3.82888, "y":0.38828, "heading":1.5708, "vx":-2.26677, "vy":-0.02396, "omega":0.0, "ax":-9.3931, "ay":-0.13035, "alpha":0.0, "fx":[-159.77396,-159.77396,-159.77396,-159.77396], "fy":[-2.21723,-2.21723,-2.21723,-2.21723]}, - {"t":5.40515, "x":3.76071, "y":0.38754, "heading":1.5708, "vx":-2.53357, "vy":-0.02766, "omega":0.0, "ax":-9.37832, "ay":-0.15156, "alpha":0.0, "fx":[-159.52254,-159.52254,-159.52254,-159.52254], "fy":[-2.57807,-2.57807,-2.57807,-2.57807]}, - {"t":5.43356, "x":3.68496, "y":0.3867, "heading":1.5708, "vx":-2.79996, "vy":-0.03197, "omega":0.0, "ax":-9.34904, "ay":-0.17706, "alpha":0.0, "fx":[-159.02443,-159.02443,-159.02443,-159.02443], "fy":[-3.01178,-3.01178,-3.01178,-3.01178]}, - {"t":5.46196, "x":3.60166, "y":0.38572, "heading":1.5708, "vx":-3.06551, "vy":-0.037, "omega":0.0, "ax":-9.26287, "ay":-0.21269, "alpha":0.0, "fx":[-157.55873,-157.55873,-157.55873,-157.55873], "fy":[-3.61788,-3.61788,-3.61788,-3.61788]}, - {"t":5.49037, "x":3.51085, "y":0.38458, "heading":1.5708, "vx":-3.32862, "vy":-0.04304, "omega":0.0, "ax":-4.32761, "ay":-0.63461, "alpha":0.0, "fx":[-73.61148,-73.61148,-73.61148,-73.61148], "fy":[-10.7945,-10.7945,-10.7945,-10.7945]}, - {"t":5.51877, "x":3.41456, "y":0.3831, "heading":1.5708, "vx":-3.45154, "vy":-0.06106, "omega":0.0, "ax":9.24368, "ay":0.22906, "alpha":0.0, "fx":[157.23241,157.23241,157.23241,157.23241], "fy":[3.89631,3.89631,3.89631,3.89631]}, - {"t":5.54717, "x":3.32025, "y":0.38146, "heading":1.5708, "vx":-3.18898, "vy":-0.05456, "omega":0.0, "ax":9.34149, "ay":0.29069, "alpha":0.0, "fx":[158.89612,158.89612,158.89612,158.89612], "fy":[4.9446,4.9446,4.9446,4.9446]}, - {"t":5.57558, "x":3.23343, "y":0.38003, "heading":1.5708, "vx":-2.92364, "vy":-0.0463, "omega":0.0, "ax":9.37049, "ay":0.3622, "alpha":0.0, "fx":[159.38933,159.38933,159.38933,159.38933], "fy":[6.16095,6.16095,6.16095,6.16095]}, - {"t":5.60398, "x":3.15417, "y":0.37886, "heading":1.5708, "vx":-2.65748, "vy":-0.03601, "omega":0.0, "ax":9.38068, "ay":0.47869, "alpha":0.0, "fx":[159.56264,159.56264,159.56264,159.56264], "fy":[8.14243,8.14243,8.14243,8.14243]}, - {"t":5.63239, "x":3.08247, "y":0.37803, "heading":1.5708, "vx":-2.39103, "vy":-0.02242, "omega":0.0, "ax":9.3347, "ay":1.12455, "alpha":0.0, "fx":[158.78049,158.78049,158.78049,158.78049], "fy":[19.12826,19.12826,19.12826,19.12826]}, - {"t":5.66079, "x":3.01832, "y":0.37784, "heading":1.5708, "vx":-2.12588, "vy":0.00953, "omega":0.0, "ax":7.91947, "ay":5.08715, "alpha":0.0, "fx":[134.70784,134.70784,134.70784,134.70784], "fy":[86.53094,86.53094,86.53094,86.53094]}, - {"t":5.6892, "x":2.96113, "y":0.38017, "heading":1.5708, "vx":-1.90093, "vy":0.15402, "omega":0.0, "ax":5.52512, "ay":7.63272, "alpha":0.0, "fx":[93.98078,93.98078,93.98078,93.98078], "fy":[129.83041,129.83041,129.83041,129.83041]}, - {"t":5.7176, "x":2.90936, "y":0.38762, "heading":1.5708, "vx":-1.74399, "vy":0.37083, "omega":0.0, "ax":4.86149, "ay":7.66782, "alpha":6.91137, "fx":[76.95469,133.56002,84.81748,35.43785], "fy":[140.49673,88.67253,136.13195,156.40857]}, - {"t":5.74928, "x":2.85655, "y":0.40322, "heading":1.5708, "vx":-1.58998, "vy":0.61375, "omega":0.21896, "ax":4.92598, "ay":7.64232, "alpha":6.78522, "fx":[79.05834,133.28282,85.53032,37.28636], "fy":[139.29275,89.05261,135.66837,155.96129]}, - {"t":5.78096, "x":2.80865, "y":0.4265, "heading":1.57773, "vx":-1.43392, "vy":0.85586, "omega":0.43392, "ax":5.005, "ay":7.60721, "alpha":6.66797, "fx":[82.13698,133.02324,86.13326,39.24106], "fy":[137.46226,89.39799,135.26563,155.45976]}, - {"t":5.81264, "x":2.76574, "y":0.45743, "heading":1.59148, "vx":-1.27536, "vy":1.09686, "omega":0.64516, "ax":5.10186, "ay":7.55992, "alpha":6.55716, "fx":[86.14792,132.77703,86.71534,41.48412], "fy":[134.93871,89.7132,134.86703,154.84907]}, - {"t":5.84432, "x":2.72789, "y":0.49597, "heading":1.61192, "vx":-1.11373, "vy":1.33636, "omega":0.8529, "ax":5.22177, "ay":7.49653, "alpha":6.4464, "fx":[91.04113,132.53346,87.41072,44.29803], "fy":[131.62945,90.01087,134.38327,154.03176]}, - {"t":5.876, "x":2.69523, "y":0.54207, "heading":1.63894, "vx":-0.9483, "vy":1.57386, "omega":1.05712, "ax":5.37363, "ay":7.41065, "alpha":6.31816, "fx":[96.75139,132.27568,88.43966,48.14894], "fy":[127.41791,90.30997,133.66198,152.82245]}, - {"t":5.90768, "x":2.66788, "y":0.59565, "heading":1.67243, "vx":-0.77806, "vy":1.80863, "omega":1.25729, "ax":5.57381, "ay":7.29059, "alpha":6.127, "fx":[103.1922,131.98099,90.19898,53.86339], "fy":[122.16408,90.63304,132.4143,150.83181]}, - {"t":5.93936, "x":2.64603, "y":0.65661, "heading":1.71226, "vx":-0.60148, "vy":2.0396, "omega":1.45139, "ax":5.85544, "ay":7.11108, "alpha":5.75653, "fx":[110.25774,131.62142,93.48658,63.03152], "fy":[115.69571,90.99995,130.00917,147.12514]}, - {"t":5.97105, "x":2.62991, "y":0.72479, "heading":1.75824, "vx":-0.41598, "vy":2.26489, "omega":1.63376, "ax":6.29242, "ay":6.80436, "alpha":4.89179, "fx":[117.84668,131.16771,100.15061,78.96405], "fy":[107.76413,91.40714,124.75903,139.03066]}, - {"t":6.00273, "x":2.61989, "y":0.79996, "heading":1.81, "vx":-0.21663, "vy":2.48045, "omega":1.78874, "ax":7.05746, "ay":6.13284, "alpha":2.57846, "fx":[125.94571,130.6187,115.2955,108.32178], "fy":[97.86904,91.73334,110.47074,117.19797]}, - {"t":6.03441, "x":2.61657, "y":0.88162, "heading":1.86667, "vx":0.00696, "vy":2.67474, "omega":1.87042, "ax":8.33403, "ay":4.05288, "alpha":-4.32186, "fx":[134.91412,130.34151,150.64576,151.13624], "fy":[84.49738,90.96816,50.4437,49.84382]}, - {"t":6.06609, "x":2.62098, "y":0.96839, "heading":1.92592, "vx":0.27098, "vy":2.80314, "omega":1.73351, "ax":9.1437, "ay":0.6647, "alpha":-5.30149, "fx":[153.4015,153.87163,156.54709,158.30693], "fy":[39.05507,34.56278,-21.48586,-6.90652]}, - {"t":6.09777, "x":2.63415, "y":1.05753, "heading":1.98084, "vx":0.56066, "vy":2.8242, "omega":1.56555, "ax":7.8826, "ay":-4.30255, "alpha":6.16183, "fx":[108.50768,136.16215,154.97915,136.67409], "fy":[-114.2937,-80.1532,-24.59664,-73.69679]}, - {"t":6.12945, "x":2.65587, "y":1.14484, "heading":2.03044, "vx":0.81039, "vy":2.68789, "omega":1.76076, "ax":2.23248, "ay":-8.14325, "alpha":10.44957, "fx":[18.67659,83.65984,104.7924,-55.23331], "fy":[-157.71788,-134.88619,-114.67446,-146.77914]}, - {"t":6.16113, "x":2.68266, "y":1.22591, "heading":2.08622, "vx":0.88111, "vy":2.42991, "omega":2.09181, "ax":-1.29288, "ay":-9.27247, "alpha":-0.02242, "fx":[-22.00295,-22.17835,-21.98,-21.80469], "fy":[-157.71884,-157.69537,-157.72525,-157.74849]}, - {"t":6.19281, "x":2.70993, "y":1.29824, "heading":2.15249, "vx":0.84015, "vy":2.13615, "omega":2.0911, "ax":-2.44109, "ay":-8.98031, "alpha":-3.8, "fx":[-49.30852,-68.86953,-34.97477,-12.93638], "fy":[-151.72634,-144.09592,-155.96557,-159.22216]}, - {"t":6.22449, "x":2.73532, "y":1.3614, "heading":2.21874, "vx":0.76282, "vy":1.85165, "omega":1.97071, "ax":-2.96891, "ay":-8.72175, "alpha":-5.96011, "fx":[-68.60527,-87.60258,-37.5818,-8.21155], "fy":[-144.25945,-133.8315,-155.58078,-159.74643]}, - {"t":6.25617, "x":2.75799, "y":1.41569, "heading":2.28117, "vx":0.66876, "vy":1.57534, "omega":1.78189, "ax":-3.25476, "ay":-8.53247, "alpha":-7.43047, "fx":[-82.44965,-96.36121,-37.51752,-5.122], "fy":[-136.99175,-127.84569,-155.71111,-159.99118]}, - {"t":6.28785, "x":2.77755, "y":1.46132, "heading":2.33762, "vx":0.56565, "vy":1.30503, "omega":1.54649, "ax":-3.42257, "ay":-8.39439, "alpha":-8.51325, "fx":[-92.40789,-100.90431,-36.5713,-2.98425], "fy":[-130.6153,-124.40497,-156.00585,-160.11885]}, - {"t":6.31953, "x":2.79375, "y":1.49845, "heading":2.38662, "vx":0.45722, "vy":1.03909, "omega":1.27679, "ax":-3.52582, "ay":-8.29329, "alpha":-9.32521, "fx":[-99.56749,-103.41496,-35.39861,-1.51153], "fy":[-125.3479,-122.40399,-156.32301,-160.19072]}, - {"t":6.35121, "x":2.80647, "y":1.5272, "heading":2.42707, "vx":0.34552, "vy":0.77635, "omega":0.98136, "ax":-3.5925, "ay":-8.21899, "alpha":-9.92913, "fx":[-104.70683,-104.88321,-34.3035,-0.53618], "fy":[-121.17021,-121.20449,-156.60074,-160.23514]}, - {"t":6.38289, "x":2.81561, "y":1.54767, "heading":2.45816, "vx":0.23171, "vy":0.51597, "omega":0.6668, "ax":-3.63889, "ay":-8.16397, "alpha":-10.36897, "fx":[-108.37867,-105.82293,-33.44738,0.0629], "fy":[-117.96265,-120.42687,-156.81141,-160.26605]}, - {"t":6.41457, "x":2.82112, "y":1.55992, "heading":2.47928, "vx":0.11642, "vy":0.25733, "omega":0.3383, "ax":-3.67493, "ay":-8.1226, "alpha":-10.67856, "fx":[-110.96751,-106.52664,-32.92312,0.3795], "fy":[-115.58251,-119.83708,-156.94249,-160.29025]}, - {"t":6.44625, "x":2.82297, "y":1.564, "heading":2.49, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0,0.0,0.0,0.0]}], + {"t":0.0, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.00003, "alpha":0.0, "fx":[0.00001,0.00001,0.00001,0.00001], "fy":[0.00057,0.00057,0.00057,0.00057]}, + {"t":0.00001, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.00003, "alpha":0.0, "fx":[0.00001,0.00001,0.00001,0.00001], "fy":[0.00054,0.00054,0.00054,0.00054]}, + {"t":0.00002, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.00003, "alpha":0.0, "fx":[0.00001,0.00001,0.00001,0.00001], "fy":[0.00051,0.00051,0.00051,0.00051]}, + {"t":0.00003, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.00003, "alpha":0.0, "fx":[0.00001,0.00001,0.00001,0.00001], "fy":[0.00048,0.00048,0.00048,0.00048]}, + {"t":0.00004, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.00003, "alpha":0.0, "fx":[0.00001,0.00001,0.00001,0.00001], "fy":[0.00045,0.00045,0.00045,0.00045]}, + {"t":0.00005, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.00002, "alpha":0.0, "fx":[0.00001,0.00001,0.00001,0.00001], "fy":[0.00042,0.00042,0.00042,0.00042]}, + {"t":0.00006, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.00002, "alpha":0.0, "fx":[0.00001,0.00001,0.00001,0.00001], "fy":[0.00039,0.00039,0.00039,0.00039]}, + {"t":0.00007, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.00002, "alpha":0.0, "fx":[0.00001,0.00001,0.00001,0.00001], "fy":[0.00037,0.00037,0.00037,0.00037]}, + {"t":0.00008, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.00002, "alpha":0.0, "fx":[0.00001,0.00001,0.00001,0.00001], "fy":[0.00034,0.00034,0.00034,0.00034]}, + {"t":0.00009, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.00002, "alpha":0.0, "fx":[0.00001,0.00001,0.00001,0.00001], "fy":[0.00032,0.00032,0.00032,0.00032]}, + {"t":0.0001, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.00002, "alpha":0.0, "fx":[0.00001,0.00001,0.00001,0.00001], "fy":[0.00029,0.00029,0.00029,0.00029]}, + {"t":0.00011, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.00002, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.00027,0.00027,0.00027,0.00027]}, + {"t":0.00012, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.00024,0.00024,0.00024,0.00024]}, + {"t":0.00013, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.00022,0.00022,0.00022,0.00022]}, + {"t":0.00014, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0002,0.0002,0.0002,0.0002]}, + {"t":0.00015, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.00017,0.00017,0.00017,0.00017]}, + {"t":0.00016, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.00015,0.00015,0.00015,0.00015]}, + {"t":0.00017, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.00013,0.00013,0.00013,0.00013]}, + {"t":0.00018, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.00011,0.00011,0.00011,0.00011]}, + {"t":0.00019, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.00009,0.00009,0.00009,0.00009]}, + {"t":0.0002, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.00007,0.00007,0.00007,0.00007]}, + {"t":0.00021, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.00005,0.00005,0.00005,0.00005]}, + {"t":0.00022, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.00003,0.00003,0.00003,0.00003]}, + {"t":0.00023, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.00001,0.00001,0.00001,0.00001]}, + {"t":0.00024, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[-0.00001,-0.00001,-0.00001,-0.00001]}, + {"t":0.00025, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[-0.00002,-0.00002,-0.00002,-0.00002]}, + {"t":0.00026, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[-0.00004,-0.00004,-0.00004,-0.00004]}, + {"t":0.00027, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[-0.00006,-0.00006,-0.00006,-0.00006]}, + {"t":0.00028, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[-0.00007,-0.00007,-0.00007,-0.00007]}, + {"t":0.00029, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":-0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[-0.00009,-0.00009,-0.00009,-0.00009]}, + {"t":0.0003, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":-0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[-0.0001,-0.0001,-0.0001,-0.0001]}, + {"t":0.00031, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":-0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[-0.00012,-0.00012,-0.00012,-0.00012]}, + {"t":0.00032, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":-0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[-0.00013,-0.00013,-0.00013,-0.00013]}, + {"t":0.00033, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":-0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[-0.00014,-0.00014,-0.00014,-0.00014]}, + {"t":0.00034, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":-0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[-0.00015,-0.00015,-0.00015,-0.00015]}, + {"t":0.00035, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":-0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[-0.00017,-0.00017,-0.00017,-0.00017]}, + {"t":0.00036, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":-0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[-0.00018,-0.00018,-0.00018,-0.00018]}, + {"t":0.00037, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":-0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[-0.00019,-0.00019,-0.00019,-0.00019]}, + {"t":0.00038, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":-0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[-0.0002,-0.0002,-0.0002,-0.0002]}, + {"t":0.00039, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":-0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[-0.00021,-0.00021,-0.00021,-0.00021]}, + {"t":0.0004, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":-0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[-0.00022,-0.00022,-0.00022,-0.00022]}, + {"t":0.00041, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":-0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[-0.00023,-0.00023,-0.00023,-0.00023]}, + {"t":0.00042, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":-0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[-0.00023,-0.00023,-0.00023,-0.00023]}, + {"t":0.00043, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":-0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[-0.00024,-0.00024,-0.00024,-0.00024]}, + {"t":0.00044, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":-0.00001, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[-0.00025,-0.00025,-0.00025,-0.00025]}, + {"t":0.00045, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":-0.00002, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[-0.00026,-0.00026,-0.00026,-0.00026]}, + {"t":0.00046, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":-0.00002, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[-0.00026,-0.00026,-0.00026,-0.00026]}, + {"t":0.00047, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":-0.00002, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[-0.00027,-0.00027,-0.00027,-0.00027]}, + {"t":0.00048, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":-0.00002, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[-0.00027,-0.00027,-0.00027,-0.00027]}, + {"t":0.00049, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":-0.00002, "alpha":0.0, "fx":[-0.00001,-0.00001,-0.00001,-0.00001], "fy":[-0.00028,-0.00028,-0.00028,-0.00028]}, + {"t":0.0005, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":-0.00002, "alpha":0.0, "fx":[-0.00001,-0.00001,-0.00001,-0.00001], "fy":[-0.00028,-0.00028,-0.00028,-0.00028]}, + {"t":0.00051, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":-0.00002, "alpha":0.0, "fx":[-0.00001,-0.00001,-0.00001,-0.00001], "fy":[-0.00028,-0.00028,-0.00028,-0.00028]}, + {"t":0.00052, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":-0.00002, "alpha":0.0, "fx":[-0.00001,-0.00001,-0.00001,-0.00001], "fy":[-0.00029,-0.00029,-0.00029,-0.00029]}, + {"t":0.00053, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":-0.00002, "alpha":0.0, "fx":[-0.00001,-0.00001,-0.00001,-0.00001], "fy":[-0.00029,-0.00029,-0.00029,-0.00029]}, + {"t":0.00054, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":-0.00002, "alpha":0.0, "fx":[-0.00001,-0.00001,-0.00001,-0.00001], "fy":[-0.00029,-0.00029,-0.00029,-0.00029]}, + {"t":0.00055, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":-0.00002, "alpha":0.0, "fx":[-0.00001,-0.00001,-0.00001,-0.00001], "fy":[-0.00029,-0.00029,-0.00029,-0.00029]}, + {"t":0.00056, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":-0.00002, "alpha":0.0, "fx":[-0.00001,-0.00001,-0.00001,-0.00001], "fy":[-0.00029,-0.00029,-0.00029,-0.00029]}, + {"t":0.00057, "x":4.44125, "y":0.40654, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0,0.0,0.0,0.0]}], "splits":[0] }, "events":[] diff --git a/src/main/deploy/v1_doomspiral/choreo/RIGHT_TRENCH_SIMPLE_CROSSES.traj b/src/main/deploy/v1_doomspiral/choreo/RIGHT_TRENCH_SIMPLE_CROSSES.traj index ff7b5636..8a56a574 100644 --- a/src/main/deploy/v1_doomspiral/choreo/RIGHT_TRENCH_SIMPLE_CROSSES.traj +++ b/src/main/deploy/v1_doomspiral/choreo/RIGHT_TRENCH_SIMPLE_CROSSES.traj @@ -4,44 +4,42 @@ "snapshot":{ "waypoints":[ {"x":4.428042411804199, "y":0.3954239785671234, "heading":1.5707963267948966, "intervals":27, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, - {"x":7.795207023620605, "y":0.9033589959144592, "heading":1.5707963267948966, "intervals":50, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, + {"x":7.795207023620605, "y":0.9033589959144592, "heading":1.5707963267948966, "intervals":83, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, {"x":7.795207023620605, "y":4.34244990349, "heading":-1.5707963267948966, "intervals":26, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, {"x":7.183448791503906, "y":1.288017511367798, "heading":0.0, "intervals":19, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, {"x":6.116927146911621, "y":0.3939896523952484, "heading":1.5707963267948966, "intervals":29, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, - {"x":3.660466432571411, "y":0.39405015110969543, "heading":0.0, "intervals":12, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":2.9093637466430664, "y":0.3876216113567352, "heading":-1.5707963267948966, "intervals":22, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":2.8122479915618896, "y":1.5284013748168943, "heading":2.49, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], + {"x":3.660466432571411, "y":0.39405015110969543, "heading":0.0, "intervals":22, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, + {"x":3.0215272903442383, "y":0.9590602874755856, "heading":2.7813916025632035, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], "constraints":[ {"from":"first", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, {"from":"last", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, {"from":"first", "to":"last", "data":{"type":"KeepInRectangle", "props":{"x":0.0, "y":0.0, "w":16.541, "h":8.0692}}, "enabled":true}, {"from":1, "to":2, "data":{"type":"KeepInLane", "props":{"tolerance":0.00001}}, "enabled":true}, {"from":1, "to":2, "data":{"type":"MaxAngularVelocity", "props":{"max":0.0}}, "enabled":true}, - {"from":4, "to":6, "data":{"type":"MaxAngularVelocity", "props":{"max":0.0}}, "enabled":true}, - {"from":1, "to":2, "data":{"type":"MaxVelocity", "props":{"max":1.5}}, "enabled":true}, - {"from":4, "to":6, "data":{"type":"KeepInLane", "props":{"tolerance":0.01}}, "enabled":true}, + {"from":4, "to":5, "data":{"type":"MaxAngularVelocity", "props":{"max":0.0}}, "enabled":true}, + {"from":1, "to":2, "data":{"type":"MaxVelocity", "props":{"max":0.85}}, "enabled":true}, + {"from":4, "to":5, "data":{"type":"KeepInLane", "props":{"tolerance":0.01}}, "enabled":true}, {"from":3, "to":5, "data":{"type":"MaxVelocity", "props":{"max":2.0}}, "enabled":true}], "targetDt":0.05 }, "params":{ "waypoints":[ {"x":{"exp":"4.428042411804199 m", "val":4.428042411804199}, "y":{"exp":"0.3954239785671234 m", "val":0.3954239785671234}, "heading":{"exp":"90 deg", "val":1.5707963267948966}, "intervals":27, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, - {"x":{"exp":"7.7952070236206055 m", "val":7.795207023620605}, "y":{"exp":"0.9033589959144592 m", "val":0.9033589959144592}, "heading":{"exp":"1.5707963267948966 rad", "val":1.5707963267948966}, "intervals":50, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, + {"x":{"exp":"7.7952070236206055 m", "val":7.795207023620605}, "y":{"exp":"0.9033589959144592 m", "val":0.9033589959144592}, "heading":{"exp":"1.5707963267948966 rad", "val":1.5707963267948966}, "intervals":83, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, {"x":{"exp":"7.7952070236206055 m", "val":7.795207023620605}, "y":{"exp":"4.34244990349 m", "val":4.34244990349}, "heading":{"exp":"-1.5707963267948966 rad", "val":-1.5707963267948966}, "intervals":26, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, {"x":{"exp":"7.183448791503906 m", "val":7.183448791503906}, "y":{"exp":"1.2880175113677979 m", "val":1.288017511367798}, "heading":{"exp":"0 deg", "val":0.0}, "intervals":19, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, {"x":{"exp":"6.116927146911621 m", "val":6.116927146911621}, "y":{"exp":"0.3939896523952484 m", "val":0.3939896523952484}, "heading":{"exp":"90 deg", "val":1.5707963267948966}, "intervals":29, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}, - {"x":{"exp":"3.660466432571411 m", "val":3.660466432571411}, "y":{"exp":"0.39405015110969543 m", "val":0.39405015110969543}, "heading":{"exp":"0 deg", "val":0.0}, "intervals":12, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":{"exp":"2.9093637466430664 m", "val":2.9093637466430664}, "y":{"exp":"0.38762161135673523 m", "val":0.3876216113567352}, "heading":{"exp":"-90 deg", "val":-1.5707963267948966}, "intervals":22, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, - {"x":{"exp":"2.8122479915618896 m", "val":2.8122479915618896}, "y":{"exp":"1.5284013748168945 m", "val":1.5284013748168943}, "heading":{"exp":"2.49 rad", "val":2.49}, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], + {"x":{"exp":"3.660466432571411 m", "val":3.660466432571411}, "y":{"exp":"0.39405015110969543 m", "val":0.39405015110969543}, "heading":{"exp":"0 deg", "val":0.0}, "intervals":22, "split":false, "fixTranslation":true, "fixHeading":false, "overrideIntervals":false}, + {"x":{"exp":"3.0215272903442383 m", "val":3.0215272903442383}, "y":{"exp":"(8.1 - 7.140939712524414) m", "val":0.9590602874755856}, "heading":{"exp":"(180 - 20.638) deg", "val":2.7813916025632035}, "intervals":40, "split":false, "fixTranslation":true, "fixHeading":true, "overrideIntervals":false}], "constraints":[ {"from":"first", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, {"from":"last", "to":null, "data":{"type":"StopPoint", "props":{}}, "enabled":true}, {"from":"first", "to":"last", "data":{"type":"KeepInRectangle", "props":{"x":{"exp":"0 m", "val":0.0}, "y":{"exp":"0 m", "val":0.0}, "w":{"exp":"16.541 m", "val":16.541}, "h":{"exp":"8.0692 m", "val":8.0692}}}, "enabled":true}, {"from":1, "to":2, "data":{"type":"KeepInLane", "props":{"tolerance":{"exp":"1e-5 m", "val":0.00001}}}, "enabled":true}, {"from":1, "to":2, "data":{"type":"MaxAngularVelocity", "props":{"max":{"exp":"0 rad / s", "val":0.0}}}, "enabled":true}, - {"from":4, "to":6, "data":{"type":"MaxAngularVelocity", "props":{"max":{"exp":"0 rad / s", "val":0.0}}}, "enabled":true}, - {"from":1, "to":2, "data":{"type":"MaxVelocity", "props":{"max":{"exp":"1.5 m / s", "val":1.5}}}, "enabled":true}, - {"from":4, "to":6, "data":{"type":"KeepInLane", "props":{"tolerance":{"exp":"0.01 m", "val":0.01}}}, "enabled":true}, + {"from":4, "to":5, "data":{"type":"MaxAngularVelocity", "props":{"max":{"exp":"0 rad / s", "val":0.0}}}, "enabled":true}, + {"from":1, "to":2, "data":{"type":"MaxVelocity", "props":{"max":{"exp":"INTAKE_VELOCITY", "val":0.55}}}, "enabled":true}, + {"from":4, "to":5, "data":{"type":"KeepInLane", "props":{"tolerance":{"exp":"0.01 m", "val":0.01}}}, "enabled":true}, {"from":3, "to":5, "data":{"type":"MaxVelocity", "props":{"max":{"exp":"2 m / s", "val":2.0}}}, "enabled":true}], "targetDt":{ "exp":"0.05 s", @@ -73,194 +71,215 @@ "differentialTrackWidth":0.5588 }, "sampleType":"Swerve", - "waypoints":[0.0,1.32943,3.68743,4.81697,5.52088,6.74998,7.05478,7.76748], + "waypoints":[0.0,1.33117,5.43334,6.53701,7.24099,8.47064,9.01034], "samples":[ - {"t":0.0, "x":4.42804, "y":0.39542, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":9.42913, "ay":0.01768, "alpha":0.0, "fx":[160.38675,160.38675,160.38675,160.38675], "fy":[0.30071,0.30071,0.30071,0.30071]}, - {"t":0.04924, "x":4.43947, "y":0.39545, "heading":1.5708, "vx":0.46427, "vy":0.00087, "omega":0.0, "ax":9.42776, "ay":0.00967, "alpha":0.0, "fx":[160.36351,160.36351,160.36351,160.36351], "fy":[0.16452,0.16452,0.16452,0.16452]}, - {"t":0.09848, "x":4.47376, "y":0.3955, "heading":1.5708, "vx":0.92848, "vy":0.00135, "omega":0.0, "ax":9.42584, "ay":0.00163, "alpha":0.0, "fx":[160.33079,160.33079,160.33079,160.33079], "fy":[0.02765,0.02765,0.02765,0.02765]}, - {"t":0.14771, "x":4.5309, "y":0.39557, "heading":1.5708, "vx":1.39259, "vy":0.00143, "omega":0.0, "ax":9.42294, "ay":-0.00649, "alpha":0.0, "fx":[160.28155,160.28155,160.28155,160.28155], "fy":[-0.11047,-0.11047,-0.11047,-0.11047]}, - {"t":0.19695, "x":4.6109, "y":0.39563, "heading":1.5708, "vx":1.85656, "vy":0.00111, "omega":0.0, "ax":9.41811, "ay":-0.01476, "alpha":0.0, "fx":[160.19943,160.19943,160.19943,160.19943], "fy":[-0.25113,-0.25113,-0.25113,-0.25113]}, - {"t":0.24619, "x":4.71373, "y":0.39567, "heading":1.5708, "vx":2.3203, "vy":0.00038, "omega":0.0, "ax":9.40847, "ay":-0.02341, "alpha":0.0, "fx":[160.03543,160.03543,160.03543,160.03543], "fy":[-0.39817,-0.39817,-0.39817,-0.39817]}, - {"t":0.29543, "x":4.83938, "y":0.39566, "heading":1.5708, "vx":2.78355, "vy":-0.00077, "omega":0.0, "ax":9.37972, "ay":-0.03351, "alpha":0.0, "fx":[159.54644,159.54644,159.54644,159.54644], "fy":[-0.56998,-0.56998,-0.56998,-0.56998]}, - {"t":0.34467, "x":4.98781, "y":0.39558, "heading":1.5708, "vx":3.2454, "vy":-0.00242, "omega":0.0, "ax":5.33915, "ay":-0.26861, "alpha":0.0, "fx":[90.81745,90.81745,90.81745,90.81745], "fy":[-4.56894,-4.56894,-4.56894,-4.56894]}, - {"t":0.39391, "x":5.15408, "y":0.39513, "heading":1.5708, "vx":3.50829, "vy":-0.01565, "omega":0.0, "ax":-0.00017, "ay":-0.14034, "alpha":0.0, "fx":[-0.00288,-0.00288,-0.00288,-0.00288], "fy":[-2.3871,-2.3871,-2.3871,-2.3871]}, - {"t":0.44314, "x":5.32682, "y":0.39419, "heading":1.5708, "vx":3.50828, "vy":-0.02256, "omega":0.0, "ax":-0.00092, "ay":-0.12569, "alpha":0.0, "fx":[-0.01564,-0.01564,-0.01564,-0.01564], "fy":[-2.13794,-2.13794,-2.13794,-2.13794]}, - {"t":0.49238, "x":5.49956, "y":0.39293, "heading":1.5708, "vx":3.50823, "vy":-0.02875, "omega":0.0, "ax":-0.00116, "ay":-0.12811, "alpha":0.0, "fx":[-0.01981,-0.01981,-0.01981,-0.01981], "fy":[-2.17909,-2.17909,-2.17909,-2.17909]}, - {"t":0.54162, "x":5.6723, "y":0.39136, "heading":1.5708, "vx":3.50818, "vy":-0.03505, "omega":0.0, "ax":-0.00145, "ay":-0.13298, "alpha":0.0, "fx":[-0.02471,-0.02471,-0.02471,-0.02471], "fy":[-2.26196,-2.26196,-2.26196,-2.26196]}, - {"t":0.59086, "x":5.84503, "y":0.38947, "heading":1.5708, "vx":3.5081, "vy":-0.0416, "omega":0.0, "ax":-0.00179, "ay":-0.13944, "alpha":0.0, "fx":[-0.03044,-0.03044,-0.03044,-0.03044], "fy":[-2.37189,-2.37189,-2.37189,-2.37189]}, - {"t":0.6401, "x":6.01776, "y":0.38725, "heading":1.5708, "vx":3.50802, "vy":-0.04847, "omega":0.0, "ax":-0.00219, "ay":-0.14766, "alpha":0.0, "fx":[-0.03729,-0.03729,-0.03729,-0.03729], "fy":[-2.51168,-2.51168,-2.51168,-2.51168]}, - {"t":0.68934, "x":6.19049, "y":0.38469, "heading":1.5708, "vx":3.50791, "vy":-0.05574, "omega":0.0, "ax":-0.00264, "ay":-0.15539, "alpha":0.0, "fx":[-0.04486,-0.04486,-0.04486,-0.04486], "fy":[-2.6432,-2.6432,-2.6432,-2.6432]}, - {"t":0.73857, "x":6.36321, "y":0.38176, "heading":1.5708, "vx":3.50778, "vy":-0.06339, "omega":0.0, "ax":-0.00238, "ay":-0.12555, "alpha":0.0, "fx":[-0.04044,-0.04044,-0.04044,-0.04044], "fy":[-2.1355,-2.1355,-2.1355,-2.1355]}, - {"t":0.78781, "x":6.53592, "y":0.37848, "heading":1.5708, "vx":3.50766, "vy":-0.06957, "omega":0.0, "ax":0.00716, "ay":0.42621, "alpha":0.0, "fx":[0.12174,0.12174,0.12174,0.12174], "fy":[7.24969,7.24969,7.24969,7.24969]}, - {"t":0.83705, "x":6.70864, "y":0.37557, "heading":1.5708, "vx":3.50801, "vy":-0.04859, "omega":0.0, "ax":-0.12281, "ay":5.27371, "alpha":0.0, "fx":[-2.08897,-2.08897,-2.08897,-2.08897], "fy":[89.70425,89.70425,89.70425,89.70425]}, - {"t":0.88629, "x":6.88122, "y":0.37957, "heading":1.5708, "vx":3.50197, "vy":0.21108, "omega":0.0, "ax":-1.08666, "ay":8.78851, "alpha":0.0, "fx":[-18.48384,-18.48384,-18.48384,-18.48384], "fy":[149.49009,149.49009,149.49009,149.49009]}, - {"t":0.93553, "x":7.05234, "y":0.40062, "heading":1.5708, "vx":3.44846, "vy":0.64381, "omega":0.0, "ax":-3.65133, "ay":8.50477, "alpha":0.0, "fx":[-62.10802,-62.10802,-62.10802,-62.10802], "fy":[144.66376,144.66376,144.66376,144.66376]}, - {"t":0.98477, "x":7.21771, "y":0.44263, "heading":1.5708, "vx":3.26868, "vy":1.06257, "omega":0.0, "ax":-8.98279, "ay":2.72388, "alpha":0.0, "fx":[-152.79477,-152.79477,-152.79477,-152.79477], "fy":[46.33248,46.33248,46.33248,46.33248]}, - {"t":1.034, "x":7.36776, "y":0.49825, "heading":1.5708, "vx":2.82638, "vy":1.19669, "omega":0.0, "ax":-9.27098, "ay":1.61211, "alpha":0.0, "fx":[-157.6967,-157.6967,-157.6967,-157.6967], "fy":[27.42155,27.42155,27.42155,27.42155]}, - {"t":1.08324, "x":7.49569, "y":0.55913, "heading":1.5708, "vx":2.36989, "vy":1.27607, "omega":0.0, "ax":-9.34269, "ay":1.19499, "alpha":0.0, "fx":[-158.91655,-158.91655,-158.91655,-158.91655], "fy":[20.32636,20.32636,20.32636,20.32636]}, - {"t":1.13248, "x":7.60105, "y":0.62341, "heading":1.5708, "vx":1.90987, "vy":1.33491, "omega":0.0, "ax":-9.37237, "ay":0.97858, "alpha":0.0, "fx":[-159.42132,-159.42132,-159.42132,-159.42132], "fy":[16.64542,16.64542,16.64542,16.64542]}, - {"t":1.18172, "x":7.68373, "y":0.69033, "heading":1.5708, "vx":1.44839, "vy":1.38309, "omega":0.0, "ax":-9.38797, "ay":0.84663, "alpha":0.0, "fx":[-159.68677,-159.68677,-159.68677,-159.68677], "fy":[14.40095,14.40095,14.40095,14.40095]}, - {"t":1.23096, "x":7.74367, "y":0.75945, "heading":1.5708, "vx":0.98614, "vy":1.42478, "omega":0.0, "ax":-9.39741, "ay":0.758, "alpha":0.0, "fx":[-159.84722,-159.84722,-159.84722,-159.84722], "fy":[12.89332,12.89332,12.89332,12.89332]}, - {"t":1.2802, "x":7.78083, "y":0.83053, "heading":1.5708, "vx":0.52343, "vy":1.4621, "omega":0.0, "ax":-9.40365, "ay":0.6945, "alpha":0.0, "fx":[-159.95342,-159.95342,-159.95342,-159.95342], "fy":[11.81324,11.81324,11.81324,11.81324]}, - {"t":1.32943, "x":7.79521, "y":0.90336, "heading":1.5708, "vx":0.06041, "vy":1.4963, "omega":0.0, "ax":-2.55393, "ay":0.04619, "alpha":0.0, "fx":[-43.44167,-43.44167,-43.44167,-43.44167], "fy":[0.78563,0.78563,0.78563,0.78563]}, - {"t":1.37659, "x":7.79522, "y":0.97398, "heading":1.5708, "vx":-0.06003, "vy":1.49848, "omega":0.0, "ax":2.52982, "ay":0.00064, "alpha":0.0, "fx":[43.03144,43.03144,43.03144,43.03144], "fy":[0.01088,0.01088,0.01088,0.01088]}, - {"t":1.42375, "x":7.7952, "y":1.04464, "heading":1.5708, "vx":0.05927, "vy":1.49851, "omega":0.0, "ax":-2.49769, "ay":0.00063, "alpha":0.0, "fx":[-42.48499,-42.48499,-42.48499,-42.48499], "fy":[0.01072,0.01072,0.01072,0.01072]}, - {"t":1.47091, "x":7.79522, "y":1.11531, "heading":1.5708, "vx":-0.05852, "vy":1.49854, "omega":0.0, "ax":2.46561, "ay":0.00062, "alpha":0.0, "fx":[41.93934,41.93934,41.93934,41.93934], "fy":[0.01057,0.01057,0.01057,0.01057]}, - {"t":1.51807, "x":7.7952, "y":1.18599, "heading":1.5708, "vx":0.05776, "vy":1.49857, "omega":0.0, "ax":-2.43358, "ay":0.00061, "alpha":0.0, "fx":[-41.3945,-41.3945,-41.3945,-41.3945], "fy":[0.01042,0.01042,0.01042,0.01042]}, - {"t":1.56523, "x":7.79522, "y":1.25666, "heading":1.5708, "vx":-0.05701, "vy":1.4986, "omega":0.0, "ax":2.4016, "ay":0.0006, "alpha":0.0, "fx":[40.85047,40.85047,40.85047,40.85047], "fy":[0.01026,0.01026,0.01026,0.01026]}, - {"t":1.61239, "x":7.7952, "y":1.32733, "heading":1.5708, "vx":0.05625, "vy":1.49862, "omega":0.0, "ax":-2.36966, "ay":0.00059, "alpha":0.0, "fx":[-40.30726,-40.30726,-40.30726,-40.30726], "fy":[0.01011,0.01011,0.01011,0.01011]}, - {"t":1.65955, "x":7.79522, "y":1.39801, "heading":1.5708, "vx":-0.0555, "vy":1.49865, "omega":0.0, "ax":2.33777, "ay":0.00059, "alpha":0.0, "fx":[39.76488,39.76488,39.76488,39.76488], "fy":[0.00996,0.00996,0.00996,0.00996]}, - {"t":1.70671, "x":7.7952, "y":1.46869, "heading":1.5708, "vx":0.05475, "vy":1.49868, "omega":0.0, "ax":-2.30594, "ay":0.00058, "alpha":0.0, "fx":[-39.22334,-39.22334,-39.22334,-39.22334], "fy":[0.00981,0.00981,0.00981,0.00981]}, - {"t":1.75387, "x":7.79522, "y":1.53936, "heading":1.5708, "vx":-0.054, "vy":1.49871, "omega":0.0, "ax":2.27415, "ay":0.00057, "alpha":0.0, "fx":[38.68265,38.68265,38.68265,38.68265], "fy":[0.00966,0.00966,0.00966,0.00966]}, - {"t":1.80103, "x":7.7952, "y":1.61004, "heading":1.5708, "vx":0.05325, "vy":1.49873, "omega":0.0, "ax":-2.24241, "ay":0.00056, "alpha":0.0, "fx":[-38.14281,-38.14281,-38.14281,-38.14281], "fy":[0.00951,0.00951,0.00951,0.00951]}, - {"t":1.84819, "x":7.79522, "y":1.68072, "heading":1.5708, "vx":-0.0525, "vy":1.49876, "omega":0.0, "ax":2.21073, "ay":0.00055, "alpha":0.0, "fx":[37.60384,37.60384,37.60384,37.60384], "fy":[0.00936,0.00936,0.00936,0.00936]}, - {"t":1.89535, "x":7.7952, "y":1.75141, "heading":1.5708, "vx":0.05176, "vy":1.49879, "omega":0.0, "ax":-2.17909, "ay":0.00054, "alpha":0.0, "fx":[-37.06574,-37.06574,-37.06574,-37.06574], "fy":[0.00921,0.00921,0.00921,0.00921]}, - {"t":1.94251, "x":7.79522, "y":1.82209, "heading":1.5708, "vx":-0.05101, "vy":1.49881, "omega":0.0, "ax":2.14751, "ay":0.00053, "alpha":0.0, "fx":[36.52852,36.52852,36.52852,36.52852], "fy":[0.00906,0.00906,0.00906,0.00906]}, - {"t":1.98967, "x":7.7952, "y":1.89277, "heading":1.5708, "vx":0.05027, "vy":1.49884, "omega":0.0, "ax":-2.11598, "ay":0.00052, "alpha":0.0, "fx":[-35.99218,-35.99218,-35.99218,-35.99218], "fy":[0.00891,0.00891,0.00891,0.00891]}, - {"t":2.03683, "x":7.79522, "y":1.96346, "heading":1.5708, "vx":-0.04952, "vy":1.49886, "omega":0.0, "ax":2.0845, "ay":0.00052, "alpha":0.0, "fx":[35.45675,35.45675,35.45675,35.45675], "fy":[0.00877,0.00877,0.00877,0.00877]}, - {"t":2.08399, "x":7.7952, "y":2.03415, "heading":1.5708, "vx":0.04878, "vy":1.49889, "omega":0.0, "ax":-2.05307, "ay":0.00051, "alpha":0.0, "fx":[-34.92221,-34.92221,-34.92221,-34.92221], "fy":[0.00862,0.00862,0.00862,0.00862]}, - {"t":2.13115, "x":7.79522, "y":2.10483, "heading":1.5708, "vx":-0.04804, "vy":1.49891, "omega":0.0, "ax":2.0217, "ay":0.0005, "alpha":0.0, "fx":[34.38859,34.38859,34.38859,34.38859], "fy":[0.00847,0.00847,0.00847,0.00847]}, - {"t":2.17831, "x":7.7952, "y":2.17552, "heading":1.5708, "vx":0.0473, "vy":1.49893, "omega":0.0, "ax":-1.99039, "ay":0.00049, "alpha":0.0, "fx":[-33.85588,-33.85588,-33.85588,-33.85588], "fy":[0.00833,0.00833,0.00833,0.00833]}, - {"t":2.22547, "x":7.79522, "y":2.24621, "heading":1.5708, "vx":-0.04656, "vy":1.49896, "omega":0.0, "ax":1.95912, "ay":0.00048, "alpha":0.0, "fx":[33.3241,33.3241,33.3241,33.3241], "fy":[0.00818,0.00818,0.00818,0.00818]}, - {"t":2.27263, "x":7.7952, "y":2.3169, "heading":1.5708, "vx":0.04583, "vy":1.49898, "omega":0.0, "ax":-1.92791, "ay":0.00047, "alpha":0.0, "fx":[-32.79325,-32.79325,-32.79325,-32.79325], "fy":[0.00804,0.00804,0.00804,0.00804]}, - {"t":2.31979, "x":7.79522, "y":2.3876, "heading":1.5708, "vx":-0.04509, "vy":1.499, "omega":0.0, "ax":1.89676, "ay":0.00046, "alpha":0.0, "fx":[32.26333,32.26333,32.26333,32.26333], "fy":[0.00789,0.00789,0.00789,0.00789]}, - {"t":2.36695, "x":7.7952, "y":2.45829, "heading":1.5708, "vx":0.04436, "vy":1.49902, "omega":0.0, "ax":-1.86566, "ay":0.00046, "alpha":0.0, "fx":[-31.73436,-31.73436,-31.73436,-31.73436], "fy":[0.00775,0.00775,0.00775,0.00775]}, - {"t":2.41411, "x":7.79522, "y":2.52898, "heading":1.5708, "vx":-0.04363, "vy":1.49905, "omega":0.0, "ax":1.83462, "ay":0.00045, "alpha":0.0, "fx":[31.20634,31.20634,31.20634,31.20634], "fy":[0.00761,0.00761,0.00761,0.00761]}, - {"t":2.46127, "x":7.7952, "y":2.59968, "heading":1.5708, "vx":0.04289, "vy":1.49907, "omega":0.0, "ax":-1.80363, "ay":0.00044, "alpha":0.0, "fx":[-30.67927,-30.67927,-30.67927,-30.67927], "fy":[0.00747,0.00747,0.00747,0.00747]}, - {"t":2.50843, "x":7.79522, "y":2.67038, "heading":1.5708, "vx":-0.04216, "vy":1.49909, "omega":0.0, "ax":1.7727, "ay":0.00043, "alpha":0.0, "fx":[30.15316,30.15316,30.15316,30.15316], "fy":[0.00733,0.00733,0.00733,0.00733]}, - {"t":2.55559, "x":7.7952, "y":2.74107, "heading":1.5708, "vx":0.04144, "vy":1.49911, "omega":0.0, "ax":-1.74183, "ay":0.00042, "alpha":0.0, "fx":[-29.62801,-29.62801,-29.62801,-29.62801], "fy":[0.00718,0.00718,0.00718,0.00718]}, - {"t":2.60275, "x":7.79522, "y":2.81177, "heading":1.5708, "vx":-0.04071, "vy":1.49913, "omega":0.0, "ax":1.71101, "ay":0.00041, "alpha":0.0, "fx":[29.10383,29.10383,29.10383,29.10383], "fy":[0.00704,0.00704,0.00704,0.00704]}, - {"t":2.64991, "x":7.7952, "y":2.88247, "heading":1.5708, "vx":0.03998, "vy":1.49915, "omega":0.0, "ax":-1.68025, "ay":0.00041, "alpha":0.0, "fx":[-28.58062,-28.58062,-28.58062,-28.58062], "fy":[0.0069,0.0069,0.0069,0.0069]}, - {"t":2.69707, "x":7.79522, "y":2.95317, "heading":1.5708, "vx":-0.03926, "vy":1.49917, "omega":0.0, "ax":1.64955, "ay":0.0004, "alpha":0.0, "fx":[28.05839,28.05839,28.05839,28.05839], "fy":[0.00677,0.00677,0.00677,0.00677]}, - {"t":2.74423, "x":7.7952, "y":3.02387, "heading":1.5708, "vx":0.03853, "vy":1.49918, "omega":0.0, "ax":-1.61891, "ay":0.00039, "alpha":0.0, "fx":[-27.53714,-27.53714,-27.53714,-27.53714], "fy":[0.00663,0.00663,0.00663,0.00663]}, - {"t":2.79139, "x":7.79522, "y":3.09457, "heading":1.5708, "vx":-0.03781, "vy":1.4992, "omega":0.0, "ax":1.58832, "ay":0.00038, "alpha":0.0, "fx":[27.01686,27.01686,27.01686,27.01686], "fy":[0.00649,0.00649,0.00649,0.00649]}, - {"t":2.83855, "x":7.7952, "y":3.16527, "heading":1.5708, "vx":0.03709, "vy":1.49922, "omega":0.0, "ax":-1.55779, "ay":0.00037, "alpha":0.0, "fx":[-26.49757,-26.49757,-26.49757,-26.49757], "fy":[0.00635,0.00635,0.00635,0.00635]}, - {"t":2.88571, "x":7.79522, "y":3.23598, "heading":1.5708, "vx":-0.03637, "vy":1.49924, "omega":0.0, "ax":1.52732, "ay":0.00037, "alpha":0.0, "fx":[25.97927,25.97927,25.97927,25.97927], "fy":[0.00622,0.00622,0.00622,0.00622]}, - {"t":2.93287, "x":7.7952, "y":3.30668, "heading":1.5708, "vx":0.03566, "vy":1.49926, "omega":0.0, "ax":-1.49691, "ay":0.00036, "alpha":0.0, "fx":[-25.46195,-25.46195,-25.46195,-25.46195], "fy":[0.00608,0.00608,0.00608,0.00608]}, - {"t":2.98003, "x":7.79522, "y":3.37739, "heading":1.5708, "vx":-0.03494, "vy":1.49927, "omega":0.0, "ax":1.46655, "ay":0.00035, "alpha":0.0, "fx":[24.94562,24.94562,24.94562,24.94562], "fy":[0.00595,0.00595,0.00595,0.00595]}, - {"t":3.02719, "x":7.7952, "y":3.44809, "heading":1.5708, "vx":0.03422, "vy":1.49929, "omega":0.0, "ax":-1.43625, "ay":0.00034, "alpha":0.0, "fx":[-24.43027,-24.43027,-24.43027,-24.43027], "fy":[0.00581,0.00581,0.00581,0.00581]}, - {"t":3.07435, "x":7.79522, "y":3.5188, "heading":1.5708, "vx":-0.03351, "vy":1.49931, "omega":0.0, "ax":1.40601, "ay":0.00033, "alpha":0.0, "fx":[23.91591,23.91591,23.91591,23.91591], "fy":[0.00568,0.00568,0.00568,0.00568]}, - {"t":3.12151, "x":7.7952, "y":3.58951, "heading":1.5708, "vx":0.0328, "vy":1.49932, "omega":0.0, "ax":-1.37583, "ay":0.00033, "alpha":0.0, "fx":[-23.40253,-23.40253,-23.40253,-23.40253], "fy":[0.00555,0.00555,0.00555,0.00555]}, - {"t":3.16867, "x":7.79522, "y":3.66022, "heading":1.5708, "vx":-0.03209, "vy":1.49934, "omega":0.0, "ax":1.34571, "ay":0.00032, "alpha":0.0, "fx":[22.89013,22.89013,22.89013,22.89013], "fy":[0.00542,0.00542,0.00542,0.00542]}, - {"t":3.21583, "x":7.7952, "y":3.73092, "heading":1.5708, "vx":0.03138, "vy":1.49935, "omega":0.0, "ax":-1.31564, "ay":0.00031, "alpha":0.0, "fx":[-22.37872,-22.37872,-22.37872,-22.37872], "fy":[0.00528,0.00528,0.00528,0.00528]}, - {"t":3.26299, "x":7.79522, "y":3.80163, "heading":1.5708, "vx":-0.03067, "vy":1.49937, "omega":0.0, "ax":1.28563, "ay":0.0003, "alpha":0.0, "fx":[21.86828,21.86828,21.86828,21.86828], "fy":[0.00515,0.00515,0.00515,0.00515]}, - {"t":3.31015, "x":7.7952, "y":3.87234, "heading":1.5708, "vx":0.02996, "vy":1.49938, "omega":0.0, "ax":-1.25568, "ay":0.0003, "alpha":0.0, "fx":[-21.35881,-21.35881,-21.35881,-21.35881], "fy":[0.00502,0.00502,0.00502,0.00502]}, - {"t":3.35731, "x":7.79522, "y":3.94306, "heading":1.5708, "vx":-0.02926, "vy":1.49939, "omega":0.0, "ax":1.22579, "ay":0.00029, "alpha":0.0, "fx":[20.85031,20.85031,20.85031,20.85031], "fy":[0.0049,0.0049,0.0049,0.0049]}, - {"t":3.40447, "x":7.7952, "y":4.01377, "heading":1.5708, "vx":0.02855, "vy":1.49941, "omega":0.0, "ax":-1.19595, "ay":0.00028, "alpha":0.0, "fx":[-20.34277,-20.34277,-20.34277,-20.34277], "fy":[0.00477,0.00477,0.00477,0.00477]}, - {"t":3.45163, "x":7.79522, "y":4.08448, "heading":1.5708, "vx":-0.02785, "vy":1.49942, "omega":0.0, "ax":1.16617, "ay":0.00027, "alpha":0.0, "fx":[19.83619,19.83619,19.83619,19.83619], "fy":[0.00462,0.00462,0.00462,0.00462]}, - {"t":3.49879, "x":7.7952, "y":4.15519, "heading":1.5708, "vx":0.02715, "vy":1.49943, "omega":0.0, "ax":-1.13648, "ay":-0.25544, "alpha":0.0, "fx":[-19.33117,-19.33117,-19.33117,-19.33117], "fy":[-4.34496,-4.34496,-4.34496,-4.34496]}, - {"t":3.54595, "x":7.79522, "y":4.22562, "heading":1.5708, "vx":-0.02645, "vy":1.48739, "omega":0.0, "ax":1.1071, "ay":-9.3444, "alpha":0.0, "fx":[18.83139,18.83139,18.83139,18.83139], "fy":[-158.94549,-158.94549,-158.94549,-158.94549]}, - {"t":3.59311, "x":7.7952, "y":4.28537, "heading":1.5708, "vx":0.02576, "vy":1.04671, "omega":0.0, "ax":-1.07871, "ay":-9.3618, "alpha":0.0, "fx":[-18.34862,-18.34862,-18.34862,-18.34862], "fy":[-159.24147,-159.24147,-159.24147,-159.24147]}, - {"t":3.64027, "x":7.79521, "y":4.32433, "heading":1.5708, "vx":-0.02511, "vy":0.60521, "omega":0.0, "ax":1.05827, "ay":-9.36875, "alpha":0.0, "fx":[18.00094,18.00094,18.00094,18.00094], "fy":[-159.35973,-159.35973,-159.35973,-159.35973]}, - {"t":3.68743, "x":7.79521, "y":4.34245, "heading":1.5708, "vx":0.0248, "vy":0.16338, "omega":0.0, "ax":-1.6061, "ay":-9.29073, "alpha":0.0, "fx":[-27.31931,-27.31931,-27.31931,-27.31931], "fy":[-158.03273,-158.03273,-158.03273,-158.03273]}, - {"t":3.73087, "x":7.79477, "y":4.34078, "heading":1.5708, "vx":-0.04498, "vy":-0.24025, "omega":0.0, "ax":-1.60583, "ay":-9.28955, "alpha":0.0, "fx":[-27.31467,-27.31467,-27.31467,-27.31467], "fy":[-158.01252,-158.01252,-158.01252,-158.01252]}, - {"t":3.77432, "x":7.7913, "y":4.32158, "heading":1.5708, "vx":-0.11474, "vy":-0.64382, "omega":0.0, "ax":-1.60546, "ay":-9.28798, "alpha":0.0, "fx":[-27.30847,-27.30847,-27.30847,-27.30847], "fy":[-157.98591,-157.98591,-157.98591,-157.98591]}, - {"t":3.81776, "x":7.7848, "y":4.28484, "heading":1.5708, "vx":-0.18449, "vy":-1.04733, "omega":0.0, "ax":-1.60495, "ay":-9.28583, "alpha":0.0, "fx":[-27.29982,-27.29982,-27.29982,-27.29982], "fy":[-157.94925,-157.94925,-157.94925,-157.94925]}, - {"t":3.8612, "x":7.77527, "y":4.23058, "heading":1.5708, "vx":-0.25421, "vy":-1.45074, "omega":0.0, "ax":-1.6042, "ay":-9.28267, "alpha":0.0, "fx":[-27.28703,-27.28703,-27.28703,-27.28703], "fy":[-157.89555,-157.89555,-157.89555,-157.89555]}, - {"t":3.90465, "x":7.76271, "y":4.15879, "heading":1.5708, "vx":-0.32391, "vy":-1.85401, "omega":0.0, "ax":-1.60299, "ay":-9.2776, "alpha":0.0, "fx":[-27.26636,-27.26636,-27.26636,-27.26636], "fy":[-157.80928,-157.80928,-157.80928,-157.80928]}, - {"t":3.94809, "x":7.74713, "y":4.06949, "heading":1.5708, "vx":-0.39355, "vy":-2.25707, "omega":0.0, "ax":-1.6007, "ay":-9.26811, "alpha":0.0, "fx":[-27.22752,-27.22752,-27.22752,-27.22752], "fy":[-157.64795,-157.64795,-157.64795,-157.64795]}, - {"t":3.99153, "x":7.72852, "y":3.96269, "heading":1.5708, "vx":-0.46309, "vy":-2.65971, "omega":0.0, "ax":-1.5949, "ay":-9.24404, "alpha":0.0, "fx":[-27.12882,-27.12882,-27.12882,-27.12882], "fy":[-157.23852,-157.23852,-157.23852,-157.23852]}, - {"t":4.03498, "x":7.7069, "y":3.83842, "heading":1.5708, "vx":-0.53238, "vy":-3.0613, "omega":0.0, "ax":-1.55114, "ay":-9.06182, "alpha":0.0, "fx":[-26.38443,-26.38443,-26.38443,-26.38443], "fy":[-154.13895,-154.13895,-154.13895,-154.13895]}, - {"t":4.07842, "x":7.6823, "y":3.69687, "heading":1.5708, "vx":-0.59976, "vy":-3.45498, "omega":0.0, "ax":0.03901, "ay":-0.03829, "alpha":0.0, "fx":[0.66348,0.66348,0.66348,0.66348], "fy":[-0.65122,-0.65122,-0.65122,-0.65122]}, - {"t":4.12187, "x":7.65629, "y":3.54674, "heading":1.5708, "vx":-0.59807, "vy":-3.45665, "omega":0.0, "ax":0.00464, "ay":-0.00081, "alpha":0.0, "fx":[0.07896,0.07896,0.07896,0.07896], "fy":[-0.01374,-0.01374,-0.01374,-0.01374]}, - {"t":4.16531, "x":7.63031, "y":3.39657, "heading":1.5708, "vx":-0.59787, "vy":-3.45668, "omega":0.0, "ax":0.00046, "ay":-0.00008, "alpha":0.0, "fx":[0.00777,0.00777,0.00777,0.00777], "fy":[-0.00136,-0.00136,-0.00136,-0.00136]}, - {"t":4.20875, "x":7.60433, "y":3.2464, "heading":1.5708, "vx":-0.59785, "vy":-3.45669, "omega":0.0, "ax":0.00005, "ay":-0.00001, "alpha":0.0, "fx":[0.0008,0.0008,0.0008,0.0008], "fy":[-0.00016,-0.00016,-0.00016,-0.00016]}, - {"t":4.2522, "x":7.57836, "y":3.09623, "heading":1.5708, "vx":-0.59784, "vy":-3.45669, "omega":0.0, "ax":0.00004, "ay":-0.00001, "alpha":0.0, "fx":[0.0007,0.0007,0.0007,0.0007], "fy":[-0.00015,-0.00015,-0.00015,-0.00015]}, - {"t":4.29564, "x":7.55239, "y":2.94605, "heading":1.5708, "vx":-0.59784, "vy":-3.45669, "omega":0.0, "ax":0.00008, "ay":-0.00002, "alpha":0.0, "fx":[0.00136,0.00136,0.00136,0.00136], "fy":[-0.00027,-0.00027,-0.00027,-0.00027]}, - {"t":4.33909, "x":7.52642, "y":2.79588, "heading":1.5708, "vx":-0.59784, "vy":-3.45669, "omega":0.0, "ax":0.00012, "ay":-0.00002, "alpha":0.0, "fx":[0.00206,0.00206,0.00206,0.00206], "fy":[-0.0004,-0.0004,-0.0004,-0.0004]}, - {"t":4.38253, "x":7.50044, "y":2.64571, "heading":1.5708, "vx":-0.59783, "vy":-3.45669, "omega":0.0, "ax":0.0001, "ay":-0.00002, "alpha":0.0, "fx":[0.00172,0.00172,0.00172,0.00172], "fy":[-0.00034,-0.00034,-0.00034,-0.00034]}, - {"t":4.42597, "x":7.47447, "y":2.49554, "heading":1.5708, "vx":-0.59783, "vy":-3.45669, "omega":0.0, "ax":-0.00054, "ay":0.00009, "alpha":0.0, "fx":[-0.00915,-0.00915,-0.00915,-0.00915], "fy":[0.00153,0.00153,0.00153,0.00153]}, - {"t":4.46942, "x":7.4485, "y":2.34537, "heading":1.5708, "vx":-0.59785, "vy":-3.45669, "omega":0.0, "ax":-0.00705, "ay":0.00122, "alpha":0.0, "fx":[-0.11986,-0.11986,-0.11986,-0.11986], "fy":[0.02068,0.02068,0.02068,0.02068]}, - {"t":4.51286, "x":7.42252, "y":2.1952, "heading":1.5708, "vx":-0.59816, "vy":-3.45663, "omega":0.0, "ax":-0.06879, "ay":0.01193, "alpha":0.0, "fx":[-1.17015,-1.17015,-1.17015,-1.17015], "fy":[0.20295,0.20295,0.20295,0.20295]}, - {"t":4.5563, "x":7.39647, "y":2.04504, "heading":1.5708, "vx":-0.60115, "vy":-3.45611, "omega":0.0, "ax":-0.64634, "ay":0.11515, "alpha":0.0, "fx":[-10.99409,-10.99409,-10.99409,-10.99409], "fy":[1.95874,1.95874,1.95874,1.95874]}, - {"t":4.59975, "x":7.36974, "y":1.895, "heading":1.5708, "vx":-0.62923, "vy":-3.45111, "omega":0.0, "ax":-4.56679, "ay":0.97977, "alpha":0.0, "fx":[-77.67983,-77.67983,-77.67983,-77.67983], "fy":[16.66562,16.66562,16.66562,16.66562]}, - {"t":4.64319, "x":7.3381, "y":1.746, "heading":1.5708, "vx":-0.82763, "vy":-3.40855, "omega":0.0, "ax":-3.10895, "ay":8.3599, "alpha":0.0, "fx":[-52.88235,-52.88235,-52.88235,-52.88235], "fy":[142.19949,142.19949,142.19949,142.19949]}, - {"t":4.68664, "x":7.29921, "y":1.60581, "heading":1.5708, "vx":-0.96269, "vy":-3.04536, "omega":0.0, "ax":1.00721, "ay":9.3173, "alpha":0.0, "fx":[17.13238,17.13238,17.13238,17.13238], "fy":[158.48464,158.48464,158.48464,158.48464]}, - {"t":4.73008, "x":7.25834, "y":1.4823, "heading":1.5708, "vx":-0.91893, "vy":-2.64058, "omega":0.0, "ax":1.28784, "ay":9.314, "alpha":0.0, "fx":[21.90579,21.90579,21.90579,21.90579], "fy":[158.42851,158.42851,158.42851,158.42851]}, - {"t":4.77352, "x":7.21963, "y":1.37637, "heading":1.5708, "vx":-0.86298, "vy":-2.23595, "omega":0.0, "ax":1.38922, "ay":9.31071, "alpha":0.0, "fx":[23.63027,23.63027,23.63027,23.63027], "fy":[158.37251,158.37251,158.37251,158.37251]}, - {"t":4.81697, "x":7.18345, "y":1.28802, "heading":1.5708, "vx":-0.80263, "vy":-1.83145, "omega":0.0, "ax":-8.15953, "ay":4.45607, "alpha":0.0, "fx":[-138.79122,-138.79122,-138.79122,-138.79122], "fy":[75.7965,75.7965,75.7965,75.7965]}, - {"t":4.85401, "x":7.14811, "y":1.22322, "heading":1.5708, "vx":-1.10493, "vy":-1.66637, "omega":0.0, "ax":-7.06049, "ay":5.58068, "alpha":0.0, "fx":[-120.09689,-120.09689,-120.09689,-120.09689], "fy":[94.92574,94.92574,94.92574,94.92574]}, - {"t":4.89106, "x":7.10233, "y":1.16532, "heading":1.5708, "vx":-1.3665, "vy":-1.45961, "omega":0.0, "ax":-3.98282, "ay":4.14808, "alpha":0.0, "fx":[-67.74663,-67.74663,-67.74663,-67.74663], "fy":[70.55763,70.55763,70.55763,70.55763]}, - {"t":4.92811, "x":7.04897, "y":1.11409, "heading":1.5708, "vx":-1.51406, "vy":-1.30593, "omega":0.0, "ax":-0.36621, "ay":0.42906, "alpha":0.0, "fx":[-6.22904,-6.22904,-6.22904,-6.22904], "fy":[7.29824,7.29824,7.29824,7.29824]}, - {"t":4.96516, "x":6.99263, "y":1.066, "heading":1.5708, "vx":-1.52763, "vy":-1.29004, "omega":0.0, "ax":-0.02048, "ay":0.02426, "alpha":0.0, "fx":[-0.3483,-0.3483,-0.3483,-0.3483], "fy":[0.41266,0.41266,0.41266,0.41266]}, - {"t":5.00221, "x":6.93602, "y":1.01822, "heading":1.5708, "vx":-1.52838, "vy":-1.28914, "omega":0.0, "ax":0.00109, "ay":-0.0013, "alpha":0.0, "fx":[0.01859,0.01859,0.01859,0.01859], "fy":[-0.02206,-0.02206,-0.02206,-0.02206]}, - {"t":5.03925, "x":6.8794, "y":0.97046, "heading":1.5708, "vx":-1.52834, "vy":-1.28919, "omega":0.0, "ax":0.00261, "ay":-0.0031, "alpha":0.0, "fx":[0.04439,0.04439,0.04439,0.04439], "fy":[-0.05265,-0.05265,-0.05265,-0.05265]}, - {"t":5.0763, "x":6.82278, "y":0.9227, "heading":1.5708, "vx":-1.52825, "vy":-1.2893, "omega":0.0, "ax":0.00291, "ay":-0.00345, "alpha":0.0, "fx":[0.04946,0.04946,0.04946,0.04946], "fy":[-0.05865,-0.05865,-0.05865,-0.05865]}, - {"t":5.11335, "x":6.76616, "y":0.87493, "heading":1.5708, "vx":-1.52814, "vy":-1.28943, "omega":0.0, "ax":0.00316, "ay":-0.00375, "alpha":0.0, "fx":[0.05383,0.05383,0.05383,0.05383], "fy":[-0.06382,-0.06382,-0.06382,-0.06382]}, - {"t":5.1504, "x":6.70955, "y":0.82716, "heading":1.5708, "vx":-1.52802, "vy":-1.28957, "omega":0.0, "ax":0.00346, "ay":-0.0041, "alpha":0.0, "fx":[0.0589,0.0589,0.0589,0.0589], "fy":[-0.06982,-0.06982,-0.06982,-0.06982]}, - {"t":5.18745, "x":6.65294, "y":0.77938, "heading":1.5708, "vx":-1.52789, "vy":-1.28972, "omega":0.0, "ax":0.00382, "ay":-0.00453, "alpha":0.0, "fx":[0.06501,0.06501,0.06501,0.06501], "fy":[-0.07704,-0.07704,-0.07704,-0.07704]}, - {"t":5.2245, "x":6.59634, "y":0.73159, "heading":1.5708, "vx":-1.52775, "vy":-1.28989, "omega":0.0, "ax":0.00426, "ay":-0.00505, "alpha":0.0, "fx":[0.07244,0.07244,0.07244,0.07244], "fy":[-0.08584,-0.08584,-0.08584,-0.08584]}, - {"t":5.26154, "x":6.53974, "y":0.6838, "heading":1.5708, "vx":-1.52759, "vy":-1.29008, "omega":0.0, "ax":0.0047, "ay":-0.00557, "alpha":0.0, "fx":[0.07999,0.07999,0.07999,0.07999], "fy":[-0.09475,-0.09475,-0.09475,-0.09475]}, - {"t":5.29859, "x":6.48315, "y":0.636, "heading":1.5708, "vx":-1.52742, "vy":-1.29028, "omega":0.0, "ax":0.00345, "ay":-0.00409, "alpha":0.0, "fx":[0.05873,0.05873,0.05873,0.05873], "fy":[-0.06958,-0.06958,-0.06958,-0.06958]}, - {"t":5.33564, "x":6.42656, "y":0.5882, "heading":1.5708, "vx":-1.52729, "vy":-1.29043, "omega":0.0, "ax":-0.02779, "ay":0.03292, "alpha":0.0, "fx":[-0.47275,-0.47275,-0.47275,-0.47275], "fy":[0.55991,0.55991,0.55991,0.55991]}, - {"t":5.37269, "x":6.36996, "y":0.54041, "heading":1.5708, "vx":-1.52832, "vy":-1.28921, "omega":0.0, "ax":-0.53716, "ay":0.64697, "alpha":0.0, "fx":[-9.13699,-9.13699,-9.13699,-9.13699], "fy":[11.00485,11.00485,11.00485,11.00485]}, - {"t":5.40974, "x":6.31297, "y":0.4931, "heading":1.5708, "vx":-1.54822, "vy":-1.26524, "omega":0.0, "ax":-3.93249, "ay":5.47837, "alpha":0.0, "fx":[-66.89052,-66.89052,-66.89052,-66.89052], "fy":[93.18558,93.18558,93.18558,93.18558]}, - {"t":5.44678, "x":6.25291, "y":0.44998, "heading":1.5708, "vx":-1.69391, "vy":-1.06228, "omega":0.0, "ax":-4.15923, "ay":8.07285, "alpha":0.0, "fx":[-70.74733,-70.74733,-70.74733,-70.74733], "fy":[137.3169,137.3169,137.3169,137.3169]}, - {"t":5.48383, "x":6.1873, "y":0.41616, "heading":1.5708, "vx":-1.84801, "vy":-0.7632, "omega":0.0, "ax":-2.78273, "ay":8.8884, "alpha":0.0, "fx":[-47.33336,-47.33336,-47.33336,-47.33336], "fy":[151.18915,151.18915,151.18915,151.18915]}, - {"t":5.52088, "x":6.11693, "y":0.39399, "heading":1.5708, "vx":-1.9511, "vy":-0.4339, "omega":0.0, "ax":-1.13107, "ay":9.21096, "alpha":0.0, "fx":[-19.23921,-19.23921,-19.23921,-19.23921], "fy":[156.67576,156.67576,156.67576,156.67576]}, - {"t":5.56326, "x":6.03322, "y":0.38387, "heading":1.5708, "vx":-1.99904, "vy":-0.04352, "omega":0.0, "ax":0.01547, "ay":2.62383, "alpha":0.0, "fx":[0.26319,0.26319,0.26319,0.26319], "fy":[44.6306,44.6306,44.6306,44.6306]}, - {"t":5.60564, "x":5.94851, "y":0.38438, "heading":1.5708, "vx":-1.99838, "vy":0.06769, "omega":0.0, "ax":-0.01683, "ay":-0.61567, "alpha":0.0, "fx":[-0.28622,-0.28622,-0.28622,-0.28622], "fy":[-10.47231,-10.47231,-10.47231,-10.47231]}, - {"t":5.64803, "x":5.8638, "y":0.3867, "heading":1.5708, "vx":-1.9991, "vy":0.0416, "omega":0.0, "ax":-0.00454, "ay":-0.25002, "alpha":0.0, "fx":[-0.0772,-0.0772,-0.0772,-0.0772], "fy":[-4.25276,-4.25276,-4.25276,-4.25276]}, - {"t":5.69041, "x":5.77906, "y":0.38824, "heading":1.5708, "vx":-1.99929, "vy":0.031, "omega":0.0, "ax":-0.00221, "ay":-0.15998, "alpha":0.0, "fx":[-0.03757,-0.03757,-0.03757,-0.03757], "fy":[-2.72115,-2.72115,-2.72115,-2.72115]}, - {"t":5.73279, "x":5.69433, "y":0.38941, "heading":1.5708, "vx":-1.99938, "vy":0.02422, "omega":0.0, "ax":-0.0013, "ay":-0.11949, "alpha":0.0, "fx":[-0.02204,-0.02204,-0.02204,-0.02204], "fy":[-2.03242,-2.03242,-2.03242,-2.03242]}, - {"t":5.77518, "x":5.60959, "y":0.39033, "heading":1.5708, "vx":-1.99944, "vy":0.01915, "omega":0.0, "ax":-0.00081, "ay":-0.09426, "alpha":0.0, "fx":[-0.01376,-0.01376,-0.01376,-0.01376], "fy":[-1.60332,-1.60332,-1.60332,-1.60332]}, - {"t":5.81756, "x":5.52485, "y":0.39106, "heading":1.5708, "vx":-1.99947, "vy":0.01516, "omega":0.0, "ax":-0.00052, "ay":-0.07616, "alpha":0.0, "fx":[-0.00878,-0.00878,-0.00878,-0.00878], "fy":[-1.29552,-1.29552,-1.29552,-1.29552]}, - {"t":5.85994, "x":5.4401, "y":0.39163, "heading":1.5708, "vx":-1.99949, "vy":0.01193, "omega":0.0, "ax":-0.00033, "ay":-0.0622, "alpha":0.0, "fx":[-0.00562,-0.00562,-0.00562,-0.00562], "fy":[-1.05796,-1.05796,-1.05796,-1.05796]}, - {"t":5.90232, "x":5.35536, "y":0.39208, "heading":1.5708, "vx":-1.99951, "vy":0.0093, "omega":0.0, "ax":-0.00021, "ay":-0.05094, "alpha":0.0, "fx":[-0.00356,-0.00356,-0.00356,-0.00356], "fy":[-0.86653,-0.86653,-0.86653,-0.86653]}, - {"t":5.94471, "x":5.27061, "y":0.39243, "heading":1.5708, "vx":-1.99952, "vy":0.00714, "omega":0.0, "ax":-0.00013, "ay":-0.04163, "alpha":0.0, "fx":[-0.00222,-0.00222,-0.00222,-0.00222], "fy":[-0.70804,-0.70804,-0.70804,-0.70804]}, - {"t":5.98709, "x":5.18587, "y":0.39269, "heading":1.5708, "vx":-1.99952, "vy":0.00537, "omega":0.0, "ax":-0.00008, "ay":-0.03377, "alpha":0.0, "fx":[-0.00134,-0.00134,-0.00134,-0.00134], "fy":[-0.57435,-0.57435,-0.57435,-0.57435]}, - {"t":6.02947, "x":5.10112, "y":0.39289, "heading":1.5708, "vx":-1.99952, "vy":0.00394, "omega":0.0, "ax":-0.00005, "ay":-0.02704, "alpha":0.0, "fx":[-0.00078,-0.00078,-0.00078,-0.00078], "fy":[-0.45994,-0.45994,-0.45994,-0.45994]}, - {"t":6.07185, "x":5.01638, "y":0.39303, "heading":1.5708, "vx":-1.99953, "vy":0.0028, "omega":0.0, "ax":-0.00003, "ay":-0.0212, "alpha":0.0, "fx":[-0.00043,-0.00043,-0.00043,-0.00043], "fy":[-0.36063,-0.36063,-0.36063,-0.36063]}, - {"t":6.11424, "x":4.93163, "y":0.39313, "heading":1.5708, "vx":-1.99953, "vy":0.0019, "omega":0.0, "ax":-0.00001, "ay":-0.01605, "alpha":0.0, "fx":[-0.00022,-0.00022,-0.00022,-0.00022], "fy":[-0.27301,-0.27301,-0.27301,-0.27301]}, - {"t":6.15662, "x":4.84689, "y":0.3932, "heading":1.5708, "vx":-1.99953, "vy":0.00122, "omega":0.0, "ax":-0.00001, "ay":-0.01141, "alpha":0.0, "fx":[-0.0001,-0.0001,-0.0001,-0.0001], "fy":[-0.1941,-0.1941,-0.1941,-0.1941]}, - {"t":6.199, "x":4.76214, "y":0.39324, "heading":1.5708, "vx":-1.99953, "vy":0.00073, "omega":0.0, "ax":0.0, "ay":-0.00712, "alpha":0.0, "fx":[-0.00004,-0.00004,-0.00004,-0.00004], "fy":[-0.1211,-0.1211,-0.1211,-0.1211]}, - {"t":6.24138, "x":4.6774, "y":0.39326, "heading":1.5708, "vx":-1.99953, "vy":0.00043, "omega":0.0, "ax":0.0, "ay":-0.00302, "alpha":0.0, "fx":[-0.00001,-0.00001,-0.00001,-0.00001], "fy":[-0.05128,-0.05128,-0.05128,-0.05128]}, - {"t":6.28377, "x":4.59265, "y":0.39328, "heading":1.5708, "vx":-1.99953, "vy":0.0003, "omega":0.0, "ax":0.0, "ay":0.00107, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.01819,0.01819,0.01819,0.01819]}, - {"t":6.32615, "x":4.50791, "y":0.39329, "heading":1.5708, "vx":-1.99953, "vy":0.00035, "omega":0.0, "ax":0.0, "ay":0.00532, "alpha":0.0, "fx":[0.00002,0.00002,0.00002,0.00002], "fy":[0.09042,0.09042,0.09042,0.09042]}, - {"t":6.36853, "x":4.42316, "y":0.39331, "heading":1.5708, "vx":-1.99953, "vy":0.00057, "omega":0.0, "ax":0.0, "ay":0.00993, "alpha":0.0, "fx":[0.00006,0.00006,0.00006,0.00006], "fy":[0.16898,0.16898,0.16898,0.16898]}, - {"t":6.41092, "x":4.33842, "y":0.39335, "heading":1.5708, "vx":-1.99953, "vy":0.001, "omega":0.0, "ax":0.00001, "ay":0.01518, "alpha":0.0, "fx":[0.00017,0.00017,0.00017,0.00017], "fy":[0.25827,0.25827,0.25827,0.25827]}, - {"t":6.4533, "x":4.25367, "y":0.3934, "heading":1.5708, "vx":-1.99953, "vy":0.00164, "omega":0.0, "ax":0.00002, "ay":0.0214, "alpha":0.0, "fx":[0.00038,0.00038,0.00038,0.00038], "fy":[0.36403,0.36403,0.36403,0.36403]}, - {"t":6.49568, "x":4.16893, "y":0.39349, "heading":1.5708, "vx":-1.99953, "vy":0.00255, "omega":0.0, "ax":0.00005, "ay":0.02906, "alpha":0.0, "fx":[0.00078,0.00078,0.00078,0.00078], "fy":[0.49432,0.49432,0.49432,0.49432]}, - {"t":6.53806, "x":4.08418, "y":0.39363, "heading":1.5708, "vx":-1.99952, "vy":0.00378, "omega":0.0, "ax":0.00009, "ay":0.03887, "alpha":0.0, "fx":[0.00152,0.00152,0.00152,0.00152], "fy":[0.66122,0.66122,0.66122,0.66122]}, - {"t":6.58045, "x":3.99943, "y":0.39382, "heading":1.5708, "vx":-1.99952, "vy":0.00542, "omega":0.0, "ax":0.00017, "ay":0.05189, "alpha":0.0, "fx":[0.00288,0.00288,0.00288,0.00288], "fy":[0.88261,0.88261,0.88261,0.88261]}, - {"t":6.62283, "x":3.91469, "y":0.3941, "heading":1.5708, "vx":-1.99951, "vy":0.00762, "omega":0.0, "ax":0.0003, "ay":0.06734, "alpha":0.0, "fx":[0.00518,0.00518,0.00518,0.00518], "fy":[1.14542,1.14542,1.14542,1.14542]}, - {"t":6.66521, "x":3.82995, "y":0.39448, "heading":1.5708, "vx":-1.9995, "vy":0.01048, "omega":0.0, "ax":0.00013, "ay":0.02294, "alpha":0.0, "fx":[0.00214,0.00214,0.00214,0.00214], "fy":[0.39022,0.39022,0.39022,0.39022]}, - {"t":6.70759, "x":3.7452, "y":0.39494, "heading":1.5708, "vx":-1.9995, "vy":0.01145, "omega":0.0, "ax":0.00987, "ay":-1.53647, "alpha":0.0, "fx":[0.16787,0.16787,0.16787,0.16787], "fy":[-26.13486,-26.13486,-26.13486,-26.13486]}, - {"t":6.74998, "x":3.66047, "y":0.39405, "heading":1.5708, "vx":-1.99908, "vy":-0.05367, "omega":0.0, "ax":-9.38568, "ay":-0.34967, "alpha":0.0, "fx":[-159.64771,-159.64771,-159.64771,-159.64771], "fy":[-5.94775,-5.94775,-5.94775,-5.94775]}, - {"t":6.77538, "x":3.60666, "y":0.39257, "heading":1.5708, "vx":-2.23748, "vy":-0.06255, "omega":0.0, "ax":-9.37236, "ay":-0.34673, "alpha":0.0, "fx":[-159.42117,-159.42117,-159.42117,-159.42117], "fy":[-5.89779,-5.89779,-5.89779,-5.89779]}, - {"t":6.80078, "x":3.5468, "y":0.39087, "heading":1.5708, "vx":-2.47554, "vy":-0.07136, "omega":0.0, "ax":-9.34779, "ay":-0.34256, "alpha":0.0, "fx":[-159.00319,-159.00319,-159.00319,-159.00319], "fy":[-5.82679,-5.82679,-5.82679,-5.82679]}, - {"t":6.82618, "x":3.48091, "y":0.38895, "heading":1.5708, "vx":-2.71298, "vy":-0.08006, "omega":0.0, "ax":-9.2878, "ay":-0.32553, "alpha":0.0, "fx":[-157.98281,-157.98281,-157.98281,-157.98281], "fy":[-5.53718,-5.53718,-5.53718,-5.53718]}, - {"t":6.85158, "x":3.409, "y":0.38681, "heading":1.5708, "vx":-2.94889, "vy":-0.08833, "omega":0.0, "ax":-8.91339, "ay":-0.16424, "alpha":0.0, "fx":[-151.61423,-151.61423,-151.61423,-151.61423], "fy":[-2.79362,-2.79362,-2.79362,-2.79362]}, - {"t":6.87698, "x":3.33122, "y":0.38452, "heading":1.5708, "vx":-3.1753, "vy":-0.0925, "omega":0.0, "ax":9.10811, "ay":0.55738, "alpha":0.0, "fx":[154.92641,154.92641,154.92641,154.92641], "fy":[9.48093,9.48093,9.48093,9.48093]}, - {"t":6.90238, "x":3.25351, "y":0.38235, "heading":1.5708, "vx":-2.94395, "vy":-0.07834, "omega":0.0, "ax":9.30183, "ay":0.51488, "alpha":0.0, "fx":[158.22152,158.22152,158.22152,158.22152], "fy":[8.75799,8.75799,8.75799,8.75799]}, - {"t":6.92778, "x":3.18173, "y":0.38052, "heading":1.5708, "vx":-2.70768, "vy":-0.06526, "omega":0.0, "ax":9.34643, "ay":0.54323, "alpha":0.0, "fx":[158.98015,158.98015,158.98015,158.98015], "fy":[9.24026,9.24026,9.24026,9.24026]}, - {"t":6.95318, "x":3.11597, "y":0.37904, "heading":1.5708, "vx":-2.47027, "vy":-0.05147, "omega":0.0, "ax":9.36276, "ay":0.61549, "alpha":0.0, "fx":[159.25782,159.25782,159.25782,159.25782], "fy":[10.46936,10.46936,10.46936,10.46936]}, - {"t":6.97858, "x":3.05624, "y":0.37793, "heading":1.5708, "vx":-2.23245, "vy":-0.03583, "omega":0.0, "ax":9.0664, "ay":2.46732, "alpha":0.0, "fx":[154.21684,154.21684,154.21684,154.21684], "fy":[41.96846,41.96846,41.96846,41.96846]}, - {"t":7.00398, "x":3.00246, "y":0.37782, "heading":1.5708, "vx":-2.00216, "vy":0.02684, "omega":0.0, "ax":7.15862, "ay":6.10851, "alpha":0.0, "fx":[121.76605,121.76605,121.76605,121.76605], "fy":[103.90395,103.90395,103.90395,103.90395]}, - {"t":7.02938, "x":2.95392, "y":0.38047, "heading":1.5708, "vx":-1.82033, "vy":0.182, "omega":0.0, "ax":5.21933, "ay":7.84285, "alpha":0.0, "fx":[88.77937,88.77937,88.77937,88.77937], "fy":[133.40468,133.40468,133.40468,133.40468]}, - {"t":7.05478, "x":2.90936, "y":0.38762, "heading":1.5708, "vx":-1.68776, "vy":0.38121, "omega":0.0, "ax":4.6899, "ay":7.75489, "alpha":7.0249, "fx":[71.44214,132.81187,82.88537,31.95573], "fy":[143.37172,89.78544,137.31807,157.15876]}, - {"t":7.08718, "x":2.85715, "y":0.40404, "heading":1.5708, "vx":-1.53583, "vy":0.63243, "omega":0.22757, "ax":4.75967, "ay":7.72763, "alpha":6.90425, "fx":[73.81753,132.61228,83.6304,33.78258], "fy":[142.12988,90.04213,136.84825,156.75872]}, - {"t":7.11957, "x":2.80989, "y":0.42858, "heading":1.57817, "vx":-1.38163, "vy":0.88277, "omega":0.45124, "ax":4.84639, "ay":7.68965, "alpha":6.7929, "fx":[77.32631,132.45203,84.25773,35.70649], "fy":[140.21082,90.23324,136.44133,156.30957]}, - {"t":7.15197, "x":2.76768, "y":0.46122, "heading":1.59279, "vx":-1.22463, "vy":1.13188, "omega":0.6713, "ax":4.95414, "ay":7.63766, "alpha":6.6906, "fx":[81.94617,132.33244,84.86791,37.92737], "fy":[137.51078,90.35474,136.03508,155.75681]}, - {"t":7.18436, "x":2.7306, "y":0.50189, "heading":1.61453, "vx":-1.06414, "vy":1.37931, "omega":0.88804, "ax":5.08951, "ay":7.56649, "alpha":6.59387, "fx":[87.65225,132.25102,85.61678,40.76459], "fy":[133.88095,90.40635,135.52853,154.99979]}, - {"t":7.21676, "x":2.6988, "y":0.55054, "heading":1.6433, "vx":-0.89927, "vy":1.62443, "omega":1.10165, "ax":5.26388, "ay":7.46759, "alpha":6.48803, "fx":[94.4078,132.20516,86.76934,44.76625], "fy":[129.12154,90.38468,134.74286,153.83746]}, - {"t":7.24915, "x":2.67243, "y":0.60709, "heading":1.67899, "vx":-0.72874, "vy":1.86634, "omega":1.31184, "ax":5.49889, "ay":7.32488, "alpha":6.32643, "fx":[102.15724,132.19939,88.82538,50.9565], "fy":[122.96758,90.26952,133.32109,151.81821]}, - {"t":7.28155, "x":2.65171, "y":0.67139, "heading":1.72149, "vx":-0.5506, "vy":2.10363, "omega":1.51678, "ax":5.84084, "ay":7.10192, "alpha":5.97046, "fx":[110.83121,132.26392,92.85607,61.45255], "fy":[115.04844,89.98807,130.42167,147.74849]}, - {"t":7.31395, "x":2.63694, "y":0.74327, "heading":1.77062, "vx":-0.36139, "vy":2.3337, "omega":1.7102, "ax":6.40193, "ay":6.69103, "alpha":4.98448, "fx":[120.38753,132.51682,101.58225,81.09343], "fy":[104.75602,89.29514,123.50222,137.6969]}, - {"t":7.34634, "x":2.62859, "y":0.82238, "heading":1.82603, "vx":-0.15399, "vy":2.55046, "omega":1.87167, "ax":7.46149, "ay":5.64406, "alpha":1.80272, "fx":[130.95524,133.46072,123.41683,119.83811], "fy":[90.75536,87.19291,100.98165,105.08533]}, - {"t":7.37874, "x":2.62752, "y":0.90796, "heading":1.88666, "vx":0.08772, "vy":2.7333, "omega":1.93007, "ax":8.84234, "ay":2.15035, "alpha":-7.168, "fx":[144.02183,140.68018,158.34434,158.5764], "fy":[66.9963,72.53181,-3.10262,9.88205]}, - {"t":7.41113, "x":2.635, "y":0.99764, "heading":1.94919, "vx":0.37418, "vy":2.80296, "omega":1.69786, "ax":8.95447, "ay":-2.32321, "alpha":1.56062, "fx":[149.31933,151.74273,154.93088,153.25919], "fy":[-50.39656,-43.16801,-28.76604,-35.73801]}, - {"t":7.44353, "x":2.65182, "y":1.08722, "heading":2.00419, "vx":0.66426, "vy":2.7277, "omega":1.74842, "ax":4.27598, "ay":-6.83179, "alpha":12.86763, "fx":[43.59747,108.30769,150.12125,-11.09394], "fy":[-152.45677,-115.91867,-42.39125,-154.06021]}, - {"t":7.47592, "x":2.67558, "y":1.172, "heading":2.06083, "vx":0.80278, "vy":2.50639, "omega":2.16527, "ax":-0.37982, "ay":-9.34478, "alpha":0.26743, "fx":[-6.61913,-4.11644,-6.28369,-8.82341], "fy":[-158.97753,-159.04916,-158.94429,-158.83733]}, - {"t":7.50832, "x":2.70139, "y":1.24829, "heading":2.13097, "vx":0.79048, "vy":2.20366, "omega":2.17394, "ax":-1.96031, "ay":-9.08912, "alpha":-3.80902, "fx":[-38.96262,-62.72247,-28.16483,-3.52736], "fy":[-154.62592,-146.80874,-157.30433,-159.67454]}, - {"t":7.54071, "x":2.72597, "y":1.31491, "heading":2.2014, "vx":0.72697, "vy":1.90921, "omega":2.05054, "ax":-2.63416, "ay":-8.81337, "alpha":-6.05295, "fx":[-61.41007,-84.75359,-32.75914,-0.30224], "fy":[-147.41518,-135.62526,-156.66028,-159.95096]}, - {"t":7.57311, "x":2.74814, "y":1.37214, "heading":2.26783, "vx":0.64164, "vy":1.6237, "omega":1.85445, "ax":-2.98609, "ay":-8.61081, "alpha":-7.56064, "fx":[-77.26038,-94.51096,-33.40055,2.00186], "fy":[-139.95179,-129.20823,-156.64677,-160.06307]}, - {"t":7.6055, "x":2.76735, "y":1.42022, "heading":2.3279, "vx":0.5449, "vy":1.34475, "omega":1.60952, "ax":-3.18865, "ay":-8.464, "alpha":-8.67061, "fx":[-88.48138,-99.37267,-32.75498,3.65724], "fy":[-133.2867,-125.62855,-156.85525,-160.11049]}, - {"t":7.6379, "x":2.78333, "y":1.45934, "heading":2.38005, "vx":0.4416, "vy":1.07055, "omega":1.32864, "ax":-3.3116, "ay":-8.3576, "alpha":-9.50104, "fx":[-96.41711,-101.96075,-31.72719,4.78738], "fy":[-127.77554,-123.6187,-157.11407,-160.13313]}, - {"t":7.67029, "x":2.7959, "y":1.48964, "heading":2.42309, "vx":0.33432, "vy":0.79981, "omega":1.02085, "ax":-3.39014, "ay":-8.28057, "alpha":-10.11307, "fx":[-102.01574,-103.41339,-30.71729,5.48512], "fy":[-123.43851,-122.46375,-157.34858,-160.14936]}, - {"t":7.70269, "x":2.80495, "y":1.5112, "heading":2.45616, "vx":0.2245, "vy":0.53155, "omega":0.69323, "ax":-3.44421, "ay":-8.22465, "alpha":-10.55058, "fx":[-105.94029,-104.30435,-29.93056,5.83513], "fy":[-120.15505,-121.74831,-157.52561,-160.1671]}, - {"t":7.73509, "x":2.81042, "y":1.52411, "heading":2.47862, "vx":0.11292, "vy":0.26511, "omega":0.35144, "ax":-3.48575, "ay":-8.1837, "alpha":-10.84842, "fx":[-108.64443,-104.95347,-29.48175,5.91308], "fy":[-117.76897,-121.2215,-157.63019,-160.18881]}, - {"t":7.76748, "x":2.81225, "y":1.5284, "heading":2.49, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0,0.0,0.0,0.0]}], + {"t":0.0, "x":4.42804, "y":0.39542, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":9.40885, "ay":0.61838, "alpha":0.0, "fx":[160.0418,160.0418,160.0418,160.0418], "fy":[10.51851,10.51851,10.51851,10.51851]}, + {"t":0.0493, "x":4.43948, "y":0.39618, "heading":1.5708, "vx":0.46388, "vy":0.03049, "omega":0.0, "ax":9.40792, "ay":0.61142, "alpha":0.0, "fx":[160.02606,160.02606,160.02606,160.02606], "fy":[10.40007,10.40007,10.40007,10.40007]}, + {"t":0.0986, "x":4.47378, "y":0.39842, "heading":1.5708, "vx":0.92771, "vy":0.06063, "omega":0.0, "ax":9.40642, "ay":0.60472, "alpha":0.0, "fx":[160.00055,160.00055,160.00055,160.00055], "fy":[10.28605,10.28605,10.28605,10.28605]}, + {"t":0.14791, "x":4.53095, "y":0.40215, "heading":1.5708, "vx":1.39147, "vy":0.09045, "omega":0.0, "ax":9.40394, "ay":0.59827, "alpha":0.0, "fx":[159.95826,159.95826,159.95826,159.95826], "fy":[10.17635,10.17635,10.17635,10.17635]}, + {"t":0.19721, "x":4.61099, "y":0.40733, "heading":1.5708, "vx":1.85511, "vy":0.11994, "omega":0.0, "ax":9.3995, "ay":0.59198, "alpha":0.0, "fx":[159.88289,159.88289,159.88289,159.88289], "fy":[10.06945,10.06945,10.06945,10.06945]}, + {"t":0.24651, "x":4.71387, "y":0.41397, "heading":1.5708, "vx":2.31853, "vy":0.14913, "omega":0.0, "ax":9.39026, "ay":0.58554, "alpha":0.0, "fx":[159.7257,159.7257,159.7257,159.7257], "fy":[9.95981,9.95981,9.95981,9.95981]}, + {"t":0.29581, "x":4.83959, "y":0.42203, "heading":1.5708, "vx":2.78149, "vy":0.178, "omega":0.0, "ax":9.36198, "ay":0.57732, "alpha":0.0, "fx":[159.24467,159.24467,159.24467,159.24467], "fy":[9.81998,9.81998,9.81998,9.81998]}, + {"t":0.34512, "x":4.98811, "y":0.43151, "heading":1.5708, "vx":3.24306, "vy":0.20646, "omega":0.0, "ax":5.24711, "ay":0.16208, "alpha":0.0, "fx":[89.25187,89.25187,89.25187,89.25187], "fy":[2.75691,2.75691,2.75691,2.75691]}, + {"t":0.39442, "x":5.15437, "y":0.44188, "heading":1.5708, "vx":3.50176, "vy":0.21445, "omega":0.0, "ax":0.00506, "ay":-0.07392, "alpha":0.0, "fx":[0.08607,0.08607,0.08607,0.08607], "fy":[-1.25739,-1.25739,-1.25739,-1.25739]}, + {"t":0.44372, "x":5.32703, "y":0.45237, "heading":1.5708, "vx":3.50201, "vy":0.21081, "omega":0.0, "ax":0.00341, "ay":-0.05709, "alpha":0.0, "fx":[0.058,0.058,0.058,0.058], "fy":[-0.97108,-0.97108,-0.97108,-0.97108]}, + {"t":0.49302, "x":5.49969, "y":0.46269, "heading":1.5708, "vx":3.50218, "vy":0.20799, "omega":0.0, "ax":0.00306, "ay":-0.05196, "alpha":0.0, "fx":[0.0521,0.0521,0.0521,0.0521], "fy":[-0.88376,-0.88376,-0.88376,-0.88376]}, + {"t":0.54233, "x":5.67236, "y":0.47288, "heading":1.5708, "vx":3.50233, "vy":0.20543, "omega":0.0, "ax":0.00282, "ay":-0.04836, "alpha":0.0, "fx":[0.04792,0.04792,0.04792,0.04792], "fy":[-0.82267,-0.82267,-0.82267,-0.82267]}, + {"t":0.59163, "x":5.84503, "y":0.48295, "heading":1.5708, "vx":3.50247, "vy":0.20305, "omega":0.0, "ax":0.00261, "ay":-0.04537, "alpha":0.0, "fx":[0.04444,0.04444,0.04444,0.04444], "fy":[-0.7717,-0.7717,-0.7717,-0.7717]}, + {"t":0.64093, "x":6.01772, "y":0.49291, "heading":1.5708, "vx":3.5026, "vy":0.20081, "omega":0.0, "ax":0.00244, "ay":-0.04277, "alpha":0.0, "fx":[0.04144,0.04144,0.04144,0.04144], "fy":[-0.7275,-0.7275,-0.7275,-0.7275]}, + {"t":0.69023, "x":6.19041, "y":0.50275, "heading":1.5708, "vx":3.50272, "vy":0.1987, "omega":0.0, "ax":0.00227, "ay":-0.04023, "alpha":0.0, "fx":[0.03858,0.03858,0.03858,0.03858], "fy":[-0.68427,-0.68427,-0.68427,-0.68427]}, + {"t":0.73954, "x":6.3631, "y":0.5125, "heading":1.5708, "vx":3.50283, "vy":0.19672, "omega":0.0, "ax":0.00192, "ay":-0.03447, "alpha":0.0, "fx":[0.03274,0.03274,0.03274,0.03274], "fy":[-0.58629,-0.58629,-0.58629,-0.58629]}, + {"t":0.78884, "x":6.5358, "y":0.52216, "heading":1.5708, "vx":3.50292, "vy":0.19502, "omega":0.0, "ax":-0.00087, "ay":0.01552, "alpha":0.0, "fx":[-0.01477,-0.01477,-0.01477,-0.01477], "fy":[0.26397,0.26397,0.26397,0.26397]}, + {"t":0.83814, "x":6.7085, "y":0.53179, "heading":1.5708, "vx":3.50288, "vy":0.19578, "omega":0.0, "ax":-0.03797, "ay":0.62882, "alpha":0.0, "fx":[-0.64584,-0.64584,-0.64584,-0.64584], "fy":[10.69604,10.69604,10.69604,10.69604]}, + {"t":0.88744, "x":6.88116, "y":0.54221, "heading":1.5708, "vx":3.50101, "vy":0.22679, "omega":0.0, "ax":-0.57519, "ay":5.51129, "alpha":0.0, "fx":[-9.7839,-9.7839,-9.7839,-9.7839], "fy":[93.74541,93.74541,93.74541,93.74541]}, + {"t":0.93675, "x":7.05307, "y":0.56009, "heading":1.5708, "vx":3.47265, "vy":0.49851, "omega":0.0, "ax":-3.70351, "ay":8.08822, "alpha":0.0, "fx":[-62.99569,-62.99569,-62.99569,-62.99569], "fy":[137.57823,137.57823,137.57823,137.57823]}, + {"t":0.98605, "x":7.21978, "y":0.5945, "heading":1.5708, "vx":3.29006, "vy":0.89727, "omega":0.0, "ax":-9.37024, "ay":0.44214, "alpha":0.0, "fx":[-159.38517,-159.38517,-159.38517,-159.38517], "fy":[7.52069,7.52069,7.52069,7.52069]}, + {"t":1.03535, "x":7.3706, "y":0.63927, "heading":1.5708, "vx":2.82808, "vy":0.91907, "omega":0.0, "ax":-9.40865, "ay":-0.0181, "alpha":0.0, "fx":[-160.03844,-160.03844,-160.03844,-160.03844], "fy":[-0.30781,-0.30781,-0.30781,-0.30781]}, + {"t":1.08465, "x":7.49859, "y":0.68456, "heading":1.5708, "vx":2.36421, "vy":0.91818, "omega":0.0, "ax":-9.41656, "ay":-0.17518, "alpha":0.0, "fx":[-160.17297,-160.17297,-160.17297,-160.17297], "fy":[-2.97977,-2.97977,-2.97977,-2.97977]}, + {"t":1.13396, "x":7.60371, "y":0.72962, "heading":1.5708, "vx":1.89995, "vy":0.90954, "omega":0.0, "ax":-9.41955, "ay":-0.25392, "alpha":0.0, "fx":[-160.22391,-160.22391,-160.22391,-160.22391], "fy":[-4.31908,-4.31908,-4.31908,-4.31908]}, + {"t":1.18326, "x":7.68593, "y":0.77415, "heading":1.5708, "vx":1.43554, "vy":0.89703, "omega":0.0, "ax":-9.42105, "ay":-0.30096, "alpha":0.0, "fx":[-160.24934,-160.24934,-160.24934,-160.24934], "fy":[-5.1192,-5.1192,-5.1192,-5.1192]}, + {"t":1.23256, "x":7.74526, "y":0.81801, "heading":1.5708, "vx":0.97106, "vy":0.88219, "omega":0.0, "ax":-9.42193, "ay":-0.33206, "alpha":0.0, "fx":[-160.26428,-160.26428,-160.26428,-160.26428], "fy":[-5.64825,-5.64825,-5.64825,-5.64825]}, + {"t":1.28186, "x":7.78169, "y":0.8611, "heading":1.5708, "vx":0.50654, "vy":0.86582, "omega":0.0, "ax":-9.4225, "ay":-0.35403, "alpha":0.0, "fx":[-160.27406,-160.27406,-160.27406,-160.27406], "fy":[-6.02199,-6.02199,-6.02199,-6.02199]}, + {"t":1.33117, "x":7.79521, "y":0.90336, "heading":1.5708, "vx":0.04198, "vy":0.84836, "omega":0.0, "ax":-1.69168, "ay":0.00906, "alpha":0.0, "fx":[-28.77494,-28.77494,-28.77494,-28.77494], "fy":[0.15406,0.15406,0.15406,0.15406]}, + {"t":1.38059, "x":7.79522, "y":0.9453, "heading":1.5708, "vx":-0.04163, "vy":0.84881, "omega":0.0, "ax":1.66993, "ay":0.0007, "alpha":0.0, "fx":[28.40511,28.40511,28.40511,28.40511], "fy":[0.01197,0.01197,0.01197,0.01197]}, + {"t":1.43002, "x":7.7952, "y":0.98725, "heading":1.5708, "vx":0.04091, "vy":0.84884, "omega":0.0, "ax":-1.64101, "ay":0.00069, "alpha":0.0, "fx":[-27.91317,-27.91317,-27.91317,-27.91317], "fy":[0.01172,0.01172,0.01172,0.01172]}, + {"t":1.47944, "x":7.79522, "y":1.02921, "heading":1.5708, "vx":-0.0402, "vy":0.84888, "omega":0.0, "ax":1.6122, "ay":0.00067, "alpha":0.0, "fx":[27.42303,27.42303,27.42303,27.42303], "fy":[0.01148,0.01148,0.01148,0.01148]}, + {"t":1.52886, "x":7.7952, "y":1.07116, "heading":1.5708, "vx":0.03949, "vy":0.84891, "omega":0.0, "ax":-1.58349, "ay":0.00066, "alpha":0.0, "fx":[-26.93475,-26.93475,-26.93475,-26.93475], "fy":[0.01123,0.01123,0.01123,0.01123]}, + {"t":1.57829, "x":7.79522, "y":1.11312, "heading":1.5708, "vx":-0.03878, "vy":0.84894, "omega":0.0, "ax":1.5549, "ay":0.00065, "alpha":0.0, "fx":[26.44842,26.44842,26.44842,26.44842], "fy":[0.01098,0.01098,0.01098,0.01098]}, + {"t":1.62771, "x":7.7952, "y":1.15508, "heading":1.5708, "vx":0.03807, "vy":0.84898, "omega":0.0, "ax":-1.52643, "ay":0.00063, "alpha":0.0, "fx":[-25.96409,-25.96409,-25.96409,-25.96409], "fy":[0.01073,0.01073,0.01073,0.01073]}, + {"t":1.67713, "x":7.79522, "y":1.19704, "heading":1.5708, "vx":-0.03737, "vy":0.84901, "omega":0.0, "ax":1.49808, "ay":0.00062, "alpha":0.0, "fx":[25.48185,25.48185,25.48185,25.48185], "fy":[0.01049,0.01049,0.01049,0.01049]}, + {"t":1.72656, "x":7.7952, "y":1.239, "heading":1.5708, "vx":0.03667, "vy":0.84904, "omega":0.0, "ax":-1.46985, "ay":0.0006, "alpha":0.0, "fx":[-25.00178,-25.00178,-25.00178,-25.00178], "fy":[0.01024,0.01024,0.01024,0.01024]}, + {"t":1.77598, "x":7.79522, "y":1.28096, "heading":1.5708, "vx":-0.03597, "vy":0.84907, "omega":0.0, "ax":1.44176, "ay":0.00059, "alpha":0.0, "fx":[24.52396,24.52396,24.52396,24.52396], "fy":[0.01,0.01,0.01,0.01]}, + {"t":1.82541, "x":7.7952, "y":1.32293, "heading":1.5708, "vx":0.03528, "vy":0.8491, "omega":0.0, "ax":-1.41381, "ay":0.00057, "alpha":0.0, "fx":[-24.04847,-24.04847,-24.04847,-24.04847], "fy":[0.00975,0.00975,0.00975,0.00975]}, + {"t":1.87483, "x":7.79522, "y":1.36489, "heading":1.5708, "vx":-0.03459, "vy":0.84912, "omega":0.0, "ax":1.386, "ay":0.00056, "alpha":0.0, "fx":[23.57542,23.57542,23.57542,23.57542], "fy":[0.00951,0.00951,0.00951,0.00951]}, + {"t":1.92425, "x":7.7952, "y":1.40686, "heading":1.5708, "vx":0.03391, "vy":0.84915, "omega":0.0, "ax":-1.35834, "ay":0.00055, "alpha":0.0, "fx":[-23.10489,-23.10489,-23.10489,-23.10489], "fy":[0.00927,0.00927,0.00927,0.00927]}, + {"t":1.97368, "x":7.79522, "y":1.44883, "heading":1.5708, "vx":-0.03323, "vy":0.84918, "omega":0.0, "ax":1.33083, "ay":0.00053, "alpha":0.0, "fx":[22.63699,22.63699,22.63699,22.63699], "fy":[0.00903,0.00903,0.00903,0.00903]}, + {"t":2.0231, "x":7.7952, "y":1.4908, "heading":1.5708, "vx":0.03255, "vy":0.84921, "omega":0.0, "ax":-1.30348, "ay":0.00052, "alpha":0.0, "fx":[-22.17182,-22.17182,-22.17182,-22.17182], "fy":[0.00879,0.00879,0.00879,0.00879]}, + {"t":2.07252, "x":7.79522, "y":1.53277, "heading":1.5708, "vx":-0.03187, "vy":0.84923, "omega":0.0, "ax":1.2763, "ay":0.0005, "alpha":0.0, "fx":[21.70948,21.70948,21.70948,21.70948], "fy":[0.00856,0.00856,0.00856,0.00856]}, + {"t":2.12195, "x":7.7952, "y":1.57475, "heading":1.5708, "vx":0.0312, "vy":0.84926, "omega":0.0, "ax":-1.24929, "ay":0.00049, "alpha":0.0, "fx":[-21.2501,-21.2501,-21.2501,-21.2501], "fy":[0.00832,0.00832,0.00832,0.00832]}, + {"t":2.17137, "x":7.79522, "y":1.61672, "heading":1.5708, "vx":-0.03054, "vy":0.84928, "omega":0.0, "ax":1.22247, "ay":0.00048, "alpha":0.0, "fx":[20.79379,20.79379,20.79379,20.79379], "fy":[0.00808,0.00808,0.00808,0.00808]}, + {"t":2.2208, "x":7.7952, "y":1.65869, "heading":1.5708, "vx":0.02988, "vy":0.8493, "omega":0.0, "ax":-1.19583, "ay":0.00046, "alpha":0.0, "fx":[-20.34068,-20.34068,-20.34068,-20.34068], "fy":[0.00785,0.00785,0.00785,0.00785]}, + {"t":2.27022, "x":7.79522, "y":1.70067, "heading":1.5708, "vx":-0.02922, "vy":0.84933, "omega":0.0, "ax":1.16938, "ay":0.00045, "alpha":0.0, "fx":[19.8909,19.8909,19.8909,19.8909], "fy":[0.00762,0.00762,0.00762,0.00762]}, + {"t":2.31964, "x":7.7952, "y":1.74265, "heading":1.5708, "vx":0.02857, "vy":0.84935, "omega":0.0, "ax":-1.14315, "ay":0.00043, "alpha":0.0, "fx":[-19.44457,-19.44457,-19.44457,-19.44457], "fy":[0.00739,0.00739,0.00739,0.00739]}, + {"t":2.36907, "x":7.79521, "y":1.78463, "heading":1.5708, "vx":-0.02793, "vy":0.84937, "omega":0.0, "ax":1.11712, "ay":0.00042, "alpha":0.0, "fx":[19.00185,19.00185,19.00185,19.00185], "fy":[0.00716,0.00716,0.00716,0.00716]}, + {"t":2.41849, "x":7.7952, "y":1.82661, "heading":1.5708, "vx":0.02729, "vy":0.84939, "omega":0.0, "ax":-1.09131, "ay":0.00041, "alpha":0.0, "fx":[-18.56289,-18.56289,-18.56289,-18.56289], "fy":[0.00694,0.00694,0.00694,0.00694]}, + {"t":2.46791, "x":7.79521, "y":1.86859, "heading":1.5708, "vx":-0.02665, "vy":0.84941, "omega":0.0, "ax":1.06573, "ay":0.00039, "alpha":0.0, "fx":[18.12783,18.12783,18.12783,18.12783], "fy":[0.00671,0.00671,0.00671,0.00671]}, + {"t":2.51734, "x":7.7952, "y":1.91057, "heading":1.5708, "vx":0.02602, "vy":0.84943, "omega":0.0, "ax":-1.0404, "ay":0.00038, "alpha":0.0, "fx":[-17.69684,-17.69684,-17.69684,-17.69684], "fy":[0.00649,0.00649,0.00649,0.00649]}, + {"t":2.56676, "x":7.79521, "y":1.95255, "heading":1.5708, "vx":-0.0254, "vy":0.84945, "omega":0.0, "ax":1.01531, "ay":0.00037, "alpha":0.0, "fx":[17.27009,17.27009,17.27009,17.27009], "fy":[0.00627,0.00627,0.00627,0.00627]}, + {"t":2.61619, "x":7.7952, "y":1.99453, "heading":1.5708, "vx":0.02478, "vy":0.84947, "omega":0.0, "ax":-0.99048, "ay":0.00036, "alpha":0.0, "fx":[-16.84775,-16.84775,-16.84775,-16.84775], "fy":[0.00605,0.00605,0.00605,0.00605]}, + {"t":2.66561, "x":7.79521, "y":2.03652, "heading":1.5708, "vx":-0.02417, "vy":0.84948, "omega":0.0, "ax":0.96592, "ay":0.00034, "alpha":0.0, "fx":[16.43001,16.43001,16.43001,16.43001], "fy":[0.00583,0.00583,0.00583,0.00583]}, + {"t":2.71503, "x":7.7952, "y":2.0785, "heading":1.5708, "vx":0.02357, "vy":0.8495, "omega":0.0, "ax":-0.94164, "ay":0.00033, "alpha":0.0, "fx":[-16.01705,-16.01705,-16.01705,-16.01705], "fy":[0.00562,0.00562,0.00562,0.00562]}, + {"t":2.76446, "x":7.79521, "y":2.12049, "heading":1.5708, "vx":-0.02297, "vy":0.84952, "omega":0.0, "ax":0.91766, "ay":0.00032, "alpha":0.0, "fx":[15.60908,15.60908,15.60908,15.60908], "fy":[0.00541,0.00541,0.00541,0.00541]}, + {"t":2.81388, "x":7.7952, "y":2.16248, "heading":1.5708, "vx":0.02238, "vy":0.84953, "omega":0.0, "ax":-0.89398, "ay":0.00031, "alpha":0.0, "fx":[-15.2063,-15.2063,-15.2063,-15.2063], "fy":[0.0052,0.0052,0.0052,0.0052]}, + {"t":2.8633, "x":7.79521, "y":2.20446, "heading":1.5708, "vx":-0.0218, "vy":0.84955, "omega":0.0, "ax":0.87062, "ay":0.00029, "alpha":0.0, "fx":[14.80893,14.80893,14.80893,14.80893], "fy":[0.005,0.005,0.005,0.005]}, + {"t":2.91273, "x":7.7952, "y":2.24645, "heading":1.5708, "vx":0.02123, "vy":0.84956, "omega":0.0, "ax":-0.84759, "ay":0.00028, "alpha":0.0, "fx":[-14.41718,-14.41718,-14.41718,-14.41718], "fy":[0.00479,0.00479,0.00479,0.00479]}, + {"t":2.96215, "x":7.79521, "y":2.28844, "heading":1.5708, "vx":-0.02066, "vy":0.84958, "omega":0.0, "ax":0.8249, "ay":0.00027, "alpha":0.0, "fx":[14.03129,14.03129,14.03129,14.03129], "fy":[0.00459,0.00459,0.00459,0.00459]}, + {"t":3.01158, "x":7.7952, "y":2.33043, "heading":1.5708, "vx":0.02011, "vy":0.84959, "omega":0.0, "ax":-0.80257, "ay":0.00026, "alpha":0.0, "fx":[-13.6515,-13.6515,-13.6515,-13.6515], "fy":[0.0044,0.0044,0.0044,0.0044]}, + {"t":3.061, "x":7.79521, "y":2.37242, "heading":1.5708, "vx":-0.01956, "vy":0.8496, "omega":0.0, "ax":0.78062, "ay":0.00025, "alpha":0.0, "fx":[13.27805,13.27805,13.27805,13.27805], "fy":[0.0042,0.0042,0.0042,0.0042]}, + {"t":3.11042, "x":7.7952, "y":2.41441, "heading":1.5708, "vx":0.01902, "vy":0.84962, "omega":0.0, "ax":-0.75905, "ay":0.00024, "alpha":0.0, "fx":[-12.9112,-12.9112,-12.9112,-12.9112], "fy":[0.00401,0.00401,0.00401,0.00401]}, + {"t":3.15985, "x":7.79521, "y":2.4564, "heading":1.5708, "vx":-0.01849, "vy":0.84963, "omega":0.0, "ax":0.73789, "ay":0.00022, "alpha":0.0, "fx":[12.55122,12.55122,12.55122,12.55122], "fy":[0.00382,0.00382,0.00382,0.00382]}, + {"t":3.20927, "x":7.7952, "y":2.4984, "heading":1.5708, "vx":0.01798, "vy":0.84964, "omega":0.0, "ax":-0.71714, "ay":0.00021, "alpha":0.0, "fx":[-12.19838,-12.19838,-12.19838,-12.19838], "fy":[0.00364,0.00364,0.00364,0.00364]}, + {"t":3.2587, "x":7.79521, "y":2.54039, "heading":1.5708, "vx":-0.01747, "vy":0.84965, "omega":0.0, "ax":0.69683, "ay":0.0002, "alpha":0.0, "fx":[11.85295,11.85295,11.85295,11.85295], "fy":[0.00346,0.00346,0.00346,0.00346]}, + {"t":3.30812, "x":7.7952, "y":2.58238, "heading":1.5708, "vx":0.01697, "vy":0.84966, "omega":0.0, "ax":-0.67698, "ay":0.00019, "alpha":0.0, "fx":[-11.51523,-11.51523,-11.51523,-11.51523], "fy":[0.00328,0.00328,0.00328,0.00328]}, + {"t":3.35754, "x":7.79521, "y":2.62438, "heading":1.5708, "vx":-0.01649, "vy":0.84967, "omega":0.0, "ax":0.6576, "ay":0.00018, "alpha":0.0, "fx":[11.18551,11.18551,11.18551,11.18551], "fy":[0.00311,0.00311,0.00311,0.00311]}, + {"t":3.40697, "x":7.7952, "y":2.66637, "heading":1.5708, "vx":0.01601, "vy":0.84968, "omega":0.0, "ax":-0.6387, "ay":0.00017, "alpha":0.0, "fx":[-10.86409,-10.86409,-10.86409,-10.86409], "fy":[0.00294,0.00294,0.00294,0.00294]}, + {"t":3.45639, "x":7.79521, "y":2.70836, "heading":1.5708, "vx":-0.01555, "vy":0.84969, "omega":0.0, "ax":0.62031, "ay":0.00016, "alpha":0.0, "fx":[10.55128,10.55128,10.55128,10.55128], "fy":[0.00278,0.00278,0.00278,0.00278]}, + {"t":3.50581, "x":7.7952, "y":2.75036, "heading":1.5708, "vx":0.0151, "vy":0.84969, "omega":0.0, "ax":-0.60244, "ay":0.00015, "alpha":0.0, "fx":[-10.2474,-10.2474,-10.2474,-10.2474], "fy":[0.00262,0.00262,0.00262,0.00262]}, + {"t":3.55524, "x":7.79521, "y":2.79235, "heading":1.5708, "vx":-0.01467, "vy":0.8497, "omega":0.0, "ax":0.58512, "ay":0.00014, "alpha":0.0, "fx":[9.95275,9.95275,9.95275,9.95275], "fy":[0.00247,0.00247,0.00247,0.00247]}, + {"t":3.60466, "x":7.7952, "y":2.83435, "heading":1.5708, "vx":0.01425, "vy":0.84971, "omega":0.0, "ax":-0.56836, "ay":0.00014, "alpha":0.0, "fx":[-9.66768,-9.66768,-9.66768,-9.66768], "fy":[0.00232,0.00232,0.00232,0.00232]}, + {"t":3.65409, "x":7.79521, "y":2.87635, "heading":1.5708, "vx":-0.01384, "vy":0.84972, "omega":0.0, "ax":0.55218, "ay":0.00013, "alpha":0.0, "fx":[9.39249,9.39249,9.39249,9.39249], "fy":[0.00217,0.00217,0.00217,0.00217]}, + {"t":3.70351, "x":7.7952, "y":2.91834, "heading":1.5708, "vx":0.01345, "vy":0.84972, "omega":0.0, "ax":-0.53661, "ay":0.00012, "alpha":0.0, "fx":[-9.12753,-9.12753,-9.12753,-9.12753], "fy":[0.00203,0.00203,0.00203,0.00203]}, + {"t":3.75293, "x":7.79521, "y":2.96034, "heading":1.5708, "vx":-0.01307, "vy":0.84973, "omega":0.0, "ax":0.52165, "ay":0.00011, "alpha":0.0, "fx":[8.87311,8.87311,8.87311,8.87311], "fy":[0.00189,0.00189,0.00189,0.00189]}, + {"t":3.80236, "x":7.7952, "y":3.00234, "heading":1.5708, "vx":0.01271, "vy":0.84973, "omega":0.0, "ax":-0.50733, "ay":0.0001, "alpha":0.0, "fx":[-8.62957,-8.62957,-8.62957,-8.62957], "fy":[0.00176,0.00176,0.00176,0.00176]}, + {"t":3.85178, "x":7.79521, "y":3.04433, "heading":1.5708, "vx":-0.01236, "vy":0.84974, "omega":0.0, "ax":0.49367, "ay":0.0001, "alpha":0.0, "fx":[8.39723,8.39723,8.39723,8.39723], "fy":[0.00163,0.00163,0.00163,0.00163]}, + {"t":3.9012, "x":7.7952, "y":3.08633, "heading":1.5708, "vx":0.01203, "vy":0.84974, "omega":0.0, "ax":-0.48069, "ay":0.00009, "alpha":0.0, "fx":[-8.17641,-8.17641,-8.17641,-8.17641], "fy":[0.0015,0.0015,0.0015,0.0015]}, + {"t":3.95063, "x":7.79521, "y":3.12833, "heading":1.5708, "vx":-0.01172, "vy":0.84975, "omega":0.0, "ax":0.4684, "ay":0.00008, "alpha":0.0, "fx":[7.96743,7.96743,7.96743,7.96743], "fy":[0.00138,0.00138,0.00138,0.00138]}, + {"t":4.00005, "x":7.7952, "y":3.17033, "heading":1.5708, "vx":0.01143, "vy":0.84975, "omega":0.0, "ax":-0.45683, "ay":0.00007, "alpha":0.0, "fx":[-7.77061,-7.77061,-7.77061,-7.77061], "fy":[0.00127,0.00127,0.00127,0.00127]}, + {"t":4.04948, "x":7.79521, "y":3.21232, "heading":1.5708, "vx":-0.01115, "vy":0.84976, "omega":0.0, "ax":0.44599, "ay":0.00007, "alpha":0.0, "fx":[7.58623,7.58623,7.58623,7.58623], "fy":[0.00115,0.00115,0.00115,0.00115]}, + {"t":4.0989, "x":7.7952, "y":3.25432, "heading":1.5708, "vx":0.01089, "vy":0.84976, "omega":0.0, "ax":-0.4359, "ay":0.00006, "alpha":0.0, "fx":[-7.4146,-7.4146,-7.4146,-7.4146], "fy":[0.00105,0.00105,0.00105,0.00105]}, + {"t":4.14832, "x":7.79521, "y":3.29632, "heading":1.5708, "vx":-0.01065, "vy":0.84976, "omega":0.0, "ax":0.42658, "ay":0.00006, "alpha":0.0, "fx":[7.25599,7.25599,7.25599,7.25599], "fy":[0.00094,0.00094,0.00094,0.00094]}, + {"t":4.19775, "x":7.7952, "y":3.33832, "heading":1.5708, "vx":0.01043, "vy":0.84976, "omega":0.0, "ax":-0.41804, "ay":0.00005, "alpha":0.0, "fx":[-7.11067,-7.11067,-7.11067,-7.11067], "fy":[0.00084,0.00084,0.00084,0.00084]}, + {"t":4.24717, "x":7.79521, "y":3.38032, "heading":1.5708, "vx":-0.01023, "vy":0.84977, "omega":0.0, "ax":0.41029, "ay":0.00004, "alpha":0.0, "fx":[6.97887,6.97887,6.97887,6.97887], "fy":[0.00075,0.00075,0.00075,0.00075]}, + {"t":4.29659, "x":7.7952, "y":3.42232, "heading":1.5708, "vx":0.01005, "vy":0.84977, "omega":0.0, "ax":-0.40335, "ay":0.00004, "alpha":0.0, "fx":[-6.86084,-6.86084,-6.86084,-6.86084], "fy":[0.00065,0.00065,0.00065,0.00065]}, + {"t":4.34602, "x":7.79521, "y":3.46432, "heading":1.5708, "vx":-0.00989, "vy":0.84977, "omega":0.0, "ax":0.39723, "ay":0.00003, "alpha":0.0, "fx":[6.75678,6.75678,6.75678,6.75678], "fy":[0.00056,0.00056,0.00056,0.00056]}, + {"t":4.39544, "x":7.79521, "y":3.50631, "heading":1.5708, "vx":0.00975, "vy":0.84977, "omega":0.0, "ax":-0.39195, "ay":0.00003, "alpha":0.0, "fx":[-6.66687,-6.66687,-6.66687,-6.66687], "fy":[0.00047,0.00047,0.00047,0.00047]}, + {"t":4.44487, "x":7.79521, "y":3.54831, "heading":1.5708, "vx":-0.00963, "vy":0.84977, "omega":0.0, "ax":0.3875, "ay":0.00002, "alpha":0.0, "fx":[6.5913,6.5913,6.5913,6.5913], "fy":[0.00039,0.00039,0.00039,0.00039]}, + {"t":4.49429, "x":7.79521, "y":3.59031, "heading":1.5708, "vx":0.00953, "vy":0.84978, "omega":0.0, "ax":-0.38391, "ay":0.00002, "alpha":0.0, "fx":[-6.53018,-6.53018,-6.53018,-6.53018], "fy":[0.0003,0.0003,0.0003,0.0003]}, + {"t":4.54371, "x":7.79521, "y":3.63231, "heading":1.5708, "vx":-0.00945, "vy":0.84978, "omega":0.0, "ax":0.38117, "ay":0.00001, "alpha":0.0, "fx":[6.48365,6.48365,6.48365,6.48365], "fy":[0.00022,0.00022,0.00022,0.00022]}, + {"t":4.59314, "x":7.79521, "y":3.67431, "heading":1.5708, "vx":0.00939, "vy":0.84978, "omega":0.0, "ax":-0.3793, "ay":0.00001, "alpha":0.0, "fx":[-6.45178,-6.45178,-6.45178,-6.45178], "fy":[0.00014,0.00014,0.00014,0.00014]}, + {"t":4.64256, "x":7.79521, "y":3.71631, "heading":1.5708, "vx":-0.00936, "vy":0.84978, "omega":0.0, "ax":0.37829, "ay":0.0, "alpha":0.0, "fx":[6.43465,6.43465,6.43465,6.43465], "fy":[0.00005,0.00005,0.00005,0.00005]}, + {"t":4.69198, "x":7.79521, "y":3.75831, "heading":1.5708, "vx":0.00934, "vy":0.84978, "omega":0.0, "ax":-0.37815, "ay":0.0, "alpha":0.0, "fx":[-6.43227,-6.43227,-6.43227,-6.43227], "fy":[-0.00003,-0.00003,-0.00003,-0.00003]}, + {"t":4.74141, "x":7.79521, "y":3.80031, "heading":1.5708, "vx":-0.00935, "vy":0.84978, "omega":0.0, "ax":0.37888, "ay":-0.00001, "alpha":0.0, "fx":[6.44466,6.44466,6.44466,6.44466], "fy":[-0.00011,-0.00011,-0.00011,-0.00011]}, + {"t":4.79083, "x":7.79521, "y":3.84231, "heading":1.5708, "vx":0.00938, "vy":0.84978, "omega":0.0, "ax":-0.38048, "ay":-0.00001, "alpha":0.0, "fx":[-6.47179,-6.47179,-6.47179,-6.47179], "fy":[-0.00019,-0.00019,-0.00019,-0.00019]}, + {"t":4.84026, "x":7.79521, "y":3.88431, "heading":1.5708, "vx":-0.00943, "vy":0.84978, "omega":0.0, "ax":0.38293, "ay":-0.00002, "alpha":0.0, "fx":[6.51361,6.51361,6.51361,6.51361], "fy":[-0.00027,-0.00027,-0.00027,-0.00027]}, + {"t":4.88968, "x":7.79521, "y":3.92631, "heading":1.5708, "vx":0.0095, "vy":0.84978, "omega":0.0, "ax":-0.38625, "ay":-0.00002, "alpha":0.0, "fx":[-6.57005,-6.57005,-6.57005,-6.57005], "fy":[-0.00036,-0.00036,-0.00036,-0.00036]}, + {"t":4.9391, "x":7.79521, "y":3.96831, "heading":1.5708, "vx":-0.00959, "vy":0.84977, "omega":0.0, "ax":0.39042, "ay":-0.00003, "alpha":0.0, "fx":[6.64098,6.64098,6.64098,6.64098], "fy":[-0.00044,-0.00044,-0.00044,-0.00044]}, + {"t":4.98853, "x":7.79521, "y":4.0103, "heading":1.5708, "vx":0.0097, "vy":0.84977, "omega":0.0, "ax":-0.39544, "ay":-0.00003, "alpha":0.0, "fx":[-6.72629,-6.72629,-6.72629,-6.72629], "fy":[-0.00053,-0.00053,-0.00053,-0.00053]}, + {"t":5.03795, "x":7.79521, "y":4.0523, "heading":1.5708, "vx":-0.00984, "vy":0.84977, "omega":0.0, "ax":0.40129, "ay":-0.00004, "alpha":0.0, "fx":[6.82582,6.82582,6.82582,6.82582], "fy":[-0.00062,-0.00062,-0.00062,-0.00062]}, + {"t":5.08738, "x":7.79521, "y":4.0943, "heading":1.5708, "vx":0.00999, "vy":0.84977, "omega":0.0, "ax":-0.40797, "ay":-0.00004, "alpha":0.0, "fx":[-6.93937,-6.93937,-6.93937,-6.93937], "fy":[-0.00071,-0.00071,-0.00071,-0.00071]}, + {"t":5.1368, "x":7.7952, "y":4.1363, "heading":1.5708, "vx":-0.01017, "vy":0.84977, "omega":0.0, "ax":0.41545, "ay":-0.00005, "alpha":0.0, "fx":[7.06676,7.06676,7.06676,7.06676], "fy":[-0.00081,-0.00081,-0.00081,-0.00081]}, + {"t":5.18622, "x":7.79521, "y":4.1783, "heading":1.5708, "vx":0.01036, "vy":0.84977, "omega":0.0, "ax":-0.42374, "ay":-0.00005, "alpha":0.0, "fx":[-7.20776,-7.20776,-7.20776,-7.20776], "fy":[-0.00091,-0.00091,-0.00091,-0.00091]}, + {"t":5.23565, "x":7.7952, "y":4.2203, "heading":1.5708, "vx":-0.01058, "vy":0.84976, "omega":0.0, "ax":0.43282, "ay":-0.00006, "alpha":0.0, "fx":[7.36212,7.36212,7.36212,7.36212], "fy":[-0.00101,-0.00101,-0.00101,-0.00101]}, + {"t":5.28507, "x":7.79521, "y":4.2623, "heading":1.5708, "vx":0.01081, "vy":0.84976, "omega":0.0, "ax":-0.44272, "ay":-0.00121, "alpha":0.0, "fx":[-7.53061,-7.53061,-7.53061,-7.53061], "fy":[-0.0206,-0.0206,-0.0206,-0.0206]}, + {"t":5.33449, "x":7.7952, "y":4.30429, "heading":1.5708, "vx":-0.01107, "vy":0.8497, "omega":0.0, "ax":0.45393, "ay":-9.37079, "alpha":0.0, "fx":[7.72115,7.72115,7.72115,7.72115], "fy":[-159.39442,-159.39442,-159.39442,-159.39442]}, + {"t":5.38392, "x":7.79521, "y":4.33484, "heading":1.5708, "vx":0.01137, "vy":0.38656, "omega":0.0, "ax":-0.46327, "ay":-9.41492, "alpha":0.0, "fx":[-7.88009,-7.88009,-7.88009,-7.88009], "fy":[-160.14514,-160.14514,-160.14514,-160.14514]}, + {"t":5.43334, "x":7.79521, "y":4.34245, "heading":1.5708, "vx":-0.01153, "vy":-0.07876, "omega":0.0, "ax":-1.61179, "ay":-9.28894, "alpha":0.0, "fx":[-27.41609,-27.41609,-27.41609,-27.41609], "fy":[-158.00222,-158.00222,-158.00222,-158.00222]}, + {"t":5.47579, "x":7.79327, "y":4.33074, "heading":1.5708, "vx":-0.07995, "vy":-0.47307, "omega":0.0, "ax":-1.61159, "ay":-9.28757, "alpha":0.0, "fx":[-27.41265,-27.41265,-27.41265,-27.41265], "fy":[-157.97897,-157.97897,-157.97897,-157.97897]}, + {"t":5.51824, "x":7.78842, "y":4.30229, "heading":1.5708, "vx":-0.14836, "vy":-0.86731, "omega":0.0, "ax":-1.61131, "ay":-9.28575, "alpha":0.0, "fx":[-27.40796,-27.40796,-27.40796,-27.40796], "fy":[-157.94798,-157.94798,-157.94798,-157.94798]}, + {"t":5.56069, "x":7.78067, "y":4.25711, "heading":1.5708, "vx":-0.21676, "vy":-1.26148, "omega":0.0, "ax":-1.61092, "ay":-9.2832, "alpha":0.0, "fx":[-27.40129,-27.40129,-27.40129,-27.40129], "fy":[-157.90462,-157.90462,-157.90462,-157.90462]}, + {"t":5.60314, "x":7.77002, "y":4.19519, "heading":1.5708, "vx":-0.28514, "vy":-1.65555, "omega":0.0, "ax":-1.61032, "ay":-9.27938, "alpha":0.0, "fx":[-27.39116,-27.39116,-27.39116,-27.39116], "fy":[-157.83964,-157.83964,-157.83964,-157.83964]}, + {"t":5.64559, "x":7.75646, "y":4.11656, "heading":1.5708, "vx":-0.3535, "vy":-2.04945, "omega":0.0, "ax":-1.60932, "ay":-9.27302, "alpha":0.0, "fx":[-27.37411,-27.37411,-27.37411,-27.37411], "fy":[-157.73144,-157.73144,-157.73144,-157.73144]}, + {"t":5.68804, "x":7.74001, "y":4.02121, "heading":1.5708, "vx":-0.42181, "vy":-2.44307, "omega":0.0, "ax":-1.60731, "ay":-9.26033, "alpha":0.0, "fx":[-27.33982,-27.33982,-27.33982,-27.33982], "fy":[-157.51556,-157.51556,-157.51556,-157.51556]}, + {"t":5.73048, "x":7.72065, "y":3.90916, "heading":1.5708, "vx":-0.49004, "vy":-2.83617, "omega":0.0, "ax":-1.60127, "ay":-9.22256, "alpha":0.0, "fx":[-27.23716,-27.23716,-27.23716,-27.23716], "fy":[-156.87303,-156.87303,-156.87303,-156.87303]}, + {"t":5.77293, "x":7.69841, "y":3.78046, "heading":1.5708, "vx":-0.55801, "vy":-3.22765, "omega":0.0, "ax":-0.97457, "ay":-5.38601, "alpha":0.0, "fx":[-16.57722,-16.57722,-16.57722,-16.57722], "fy":[-91.61455,-91.61455,-91.61455,-91.61455]}, + {"t":5.81538, "x":7.67384, "y":3.63859, "heading":1.5708, "vx":-0.59938, "vy":-3.45628, "omega":0.0, "ax":-0.00697, "ay":0.00007, "alpha":0.0, "fx":[-0.11864,-0.11864,-0.11864,-0.11864], "fy":[0.00123,0.00123,0.00123,0.00123]}, + {"t":5.85783, "x":7.6484, "y":3.49188, "heading":1.5708, "vx":-0.59968, "vy":-3.45628, "omega":0.0, "ax":-0.00082, "ay":0.00014, "alpha":0.0, "fx":[-0.01402,-0.01402,-0.01402,-0.01402], "fy":[0.00241,0.00241,0.00241,0.00241]}, + {"t":5.90028, "x":7.62294, "y":3.34516, "heading":1.5708, "vx":-0.59971, "vy":-3.45627, "omega":0.0, "ax":-0.00012, "ay":0.00002, "alpha":0.0, "fx":[-0.002,-0.002,-0.002,-0.002], "fy":[0.00032,0.00032,0.00032,0.00032]}, + {"t":5.94273, "x":7.59748, "y":3.19845, "heading":1.5708, "vx":-0.59972, "vy":-3.45627, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[-0.00003,-0.00003,-0.00003,-0.00003], "fy":[-0.00002,-0.00002,-0.00002,-0.00002]}, + {"t":5.98518, "x":7.57202, "y":3.05173, "heading":1.5708, "vx":-0.59972, "vy":-3.45627, "omega":0.0, "ax":0.00005, "ay":-0.00001, "alpha":0.0, "fx":[0.00084,0.00084,0.00084,0.00084], "fy":[-0.00018,-0.00018,-0.00018,-0.00018]}, + {"t":6.02763, "x":7.54657, "y":2.90502, "heading":1.5708, "vx":-0.59971, "vy":-3.45627, "omega":0.0, "ax":0.0001, "ay":-0.00002, "alpha":0.0, "fx":[0.00162,0.00162,0.00162,0.00162], "fy":[-0.00032,-0.00032,-0.00032,-0.00032]}, + {"t":6.07008, "x":7.52111, "y":2.7583, "heading":1.5708, "vx":-0.59971, "vy":-3.45627, "omega":0.0, "ax":0.00013, "ay":-0.00003, "alpha":0.0, "fx":[0.00222,0.00222,0.00222,0.00222], "fy":[-0.00043,-0.00043,-0.00043,-0.00043]}, + {"t":6.11252, "x":7.49565, "y":2.61159, "heading":1.5708, "vx":-0.5997, "vy":-3.45628, "omega":0.0, "ax":0.00005, "ay":-0.00001, "alpha":0.0, "fx":[0.00083,0.00083,0.00083,0.00083], "fy":[-0.00019,-0.00019,-0.00019,-0.00019]}, + {"t":6.15497, "x":7.4702, "y":2.46487, "heading":1.5708, "vx":-0.5997, "vy":-3.45628, "omega":0.0, "ax":-0.00111, "ay":0.00019, "alpha":0.0, "fx":[-0.01891,-0.01891,-0.01891,-0.01891], "fy":[0.00322,0.00322,0.00322,0.00322]}, + {"t":6.19742, "x":7.44474, "y":2.31816, "heading":1.5708, "vx":-0.59975, "vy":-3.45627, "omega":0.0, "ax":-0.01188, "ay":0.00206, "alpha":0.0, "fx":[-0.20211,-0.20211,-0.20211,-0.20211], "fy":[0.03502,0.03502,0.03502,0.03502]}, + {"t":6.23987, "x":7.41927, "y":2.17144, "heading":1.5708, "vx":-0.60025, "vy":-3.45618, "omega":0.0, "ax":-0.10787, "ay":0.01881, "alpha":0.0, "fx":[-1.83491,-1.83491,-1.83491,-1.83491], "fy":[0.31987,0.31987,0.31987,0.31987]}, + {"t":6.28232, "x":7.39369, "y":2.02475, "heading":1.5708, "vx":-0.60483, "vy":-3.45538, "omega":0.0, "ax":-0.94843, "ay":0.17177, "alpha":0.0, "fx":[-16.1326,-16.1326,-16.1326,-16.1326], "fy":[2.9217,2.9217,2.9217,2.9217]}, + {"t":6.32477, "x":7.36716, "y":1.87823, "heading":1.5708, "vx":-0.64509, "vy":-3.44809, "omega":0.0, "ax":-5.44134, "ay":1.22878, "alpha":0.0, "fx":[-92.55559,-92.55559,-92.55559,-92.55559], "fy":[20.90111,20.90111,20.90111,20.90111]}, + {"t":6.36722, "x":7.33488, "y":1.73297, "heading":1.5708, "vx":-0.87607, "vy":-3.39593, "omega":0.0, "ax":-1.85626, "ay":8.89479, "alpha":0.0, "fx":[-31.57444,-31.57444,-31.57444,-31.57444], "fy":[151.2979,151.2979,151.2979,151.2979]}, + {"t":6.40967, "x":7.29602, "y":1.59683, "heading":1.5708, "vx":-0.95487, "vy":-3.01836, "omega":0.0, "ax":0.97397, "ay":9.32224, "alpha":0.0, "fx":[16.56699,16.56699,16.56699,16.56699], "fy":[158.56869,158.56869,158.56869,158.56869]}, + {"t":6.45212, "x":7.25636, "y":1.4771, "heading":1.5708, "vx":-0.91352, "vy":-2.62264, "omega":0.0, "ax":1.26145, "ay":9.3172, "alpha":0.0, "fx":[21.45699,21.45699,21.45699,21.45699], "fy":[158.48289,158.48289,158.48289,158.48289]}, + {"t":6.49456, "x":7.21872, "y":1.37417, "heading":1.5708, "vx":-0.85998, "vy":-2.22713, "omega":0.0, "ax":1.37004, "ay":9.31297, "alpha":0.0, "fx":[23.30396,23.30396,23.30396,23.30396], "fy":[158.41102,158.41102,158.41102,158.41102]}, + {"t":6.53701, "x":7.18345, "y":1.28802, "heading":1.5708, "vx":-0.80182, "vy":-1.83181, "omega":0.0, "ax":-8.16157, "ay":4.45254, "alpha":0.0, "fx":[-138.82606,-138.82606,-138.82606,-138.82606], "fy":[75.73643,75.73643,75.73643,75.73643]}, + {"t":6.57406, "x":7.14814, "y":1.2232, "heading":1.5708, "vx":-1.10422, "vy":-1.66684, "omega":0.0, "ax":-7.06317, "ay":5.57804, "alpha":0.0, "fx":[-120.14244,-120.14244,-120.14244,-120.14244], "fy":[94.88083,94.88083,94.88083,94.88083]}, + {"t":6.61112, "x":7.10238, "y":1.16527, "heading":1.5708, "vx":-1.36591, "vy":-1.46016, "omega":0.0, "ax":-3.9872, "ay":4.14965, "alpha":0.0, "fx":[-67.82118,-67.82118,-67.82118,-67.82118], "fy":[70.58441,70.58441,70.58441,70.58441]}, + {"t":6.64817, "x":7.04903, "y":1.11402, "heading":1.5708, "vx":-1.51365, "vy":-1.30641, "omega":0.0, "ax":-0.36684, "ay":0.42953, "alpha":0.0, "fx":[-6.23978,-6.23978,-6.23978,-6.23978], "fy":[7.30625,7.30625,7.30625,7.30625]}, + {"t":6.68522, "x":6.9927, "y":1.06591, "heading":1.5708, "vx":-1.52724, "vy":-1.2905, "omega":0.0, "ax":-0.02051, "ay":0.02429, "alpha":0.0, "fx":[-0.34887,-0.34887,-0.34887,-0.34887], "fy":[0.41309,0.41309,0.41309,0.41309]}, + {"t":6.72227, "x":6.9361, "y":1.01811, "heading":1.5708, "vx":-1.528, "vy":-1.2896, "omega":0.0, "ax":0.00109, "ay":-0.00129, "alpha":0.0, "fx":[0.01857,0.01857,0.01857,0.01857], "fy":[-0.02202,-0.02202,-0.02202,-0.02202]}, + {"t":6.75932, "x":6.87948, "y":0.97033, "heading":1.5708, "vx":-1.52796, "vy":-1.28965, "omega":0.0, "ax":0.00261, "ay":-0.00309, "alpha":0.0, "fx":[0.0444,0.0444,0.0444,0.0444], "fy":[-0.05263,-0.05263,-0.05263,-0.05263]}, + {"t":6.79637, "x":6.82287, "y":0.92255, "heading":1.5708, "vx":-1.52786, "vy":-1.28976, "omega":0.0, "ax":0.00291, "ay":-0.00345, "alpha":0.0, "fx":[0.04948,0.04948,0.04948,0.04948], "fy":[-0.05863,-0.05863,-0.05863,-0.05863]}, + {"t":6.83342, "x":6.76627, "y":0.87476, "heading":1.5708, "vx":-1.52775, "vy":-1.28989, "omega":0.0, "ax":0.00317, "ay":-0.00375, "alpha":0.0, "fx":[0.05385,0.05385,0.05385,0.05385], "fy":[-0.0638,-0.0638,-0.0638,-0.0638]}, + {"t":6.87047, "x":6.70966, "y":0.82696, "heading":1.5708, "vx":-1.52763, "vy":-1.29003, "omega":0.0, "ax":0.00346, "ay":-0.0041, "alpha":0.0, "fx":[0.05892,0.05892,0.05892,0.05892], "fy":[-0.0698,-0.0698,-0.0698,-0.0698]}, + {"t":6.90753, "x":6.65307, "y":0.77916, "heading":1.5708, "vx":-1.52751, "vy":-1.29018, "omega":0.0, "ax":0.00382, "ay":-0.00453, "alpha":0.0, "fx":[0.06504,0.06504,0.06504,0.06504], "fy":[-0.07703,-0.07703,-0.07703,-0.07703]}, + {"t":6.94458, "x":6.59647, "y":0.73136, "heading":1.5708, "vx":-1.52736, "vy":-1.29035, "omega":0.0, "ax":0.00426, "ay":-0.00505, "alpha":0.0, "fx":[0.07248,0.07248,0.07248,0.07248], "fy":[-0.08583,-0.08583,-0.08583,-0.08583]}, + {"t":6.98163, "x":6.53988, "y":0.68354, "heading":1.5708, "vx":-1.52721, "vy":-1.29053, "omega":0.0, "ax":0.0047, "ay":-0.00557, "alpha":0.0, "fx":[0.07997,0.07997,0.07997,0.07997], "fy":[-0.09467,-0.09467,-0.09467,-0.09467]}, + {"t":7.01868, "x":6.4833, "y":0.63573, "heading":1.5708, "vx":-1.52703, "vy":-1.29074, "omega":0.0, "ax":0.00338, "ay":-0.004, "alpha":0.0, "fx":[0.05745,0.05745,0.05745,0.05745], "fy":[-0.06801,-0.06801,-0.06801,-0.06801]}, + {"t":7.05573, "x":6.42673, "y":0.5879, "heading":1.5708, "vx":-1.52691, "vy":-1.29089, "omega":0.0, "ax":-0.0291, "ay":0.03444, "alpha":0.0, "fx":[-0.49494,-0.49494,-0.49494,-0.49494], "fy":[0.58586,0.58586,0.58586,0.58586]}, + {"t":7.09278, "x":6.37013, "y":0.54009, "heading":1.5708, "vx":-1.52799, "vy":-1.28961, "omega":0.0, "ax":-0.5575, "ay":0.67152, "alpha":0.0, "fx":[-9.48299,-9.48299,-9.48299,-9.48299], "fy":[11.4224,11.4224,11.4224,11.4224]}, + {"t":7.12983, "x":6.31314, "y":0.49277, "heading":1.5708, "vx":-1.54864, "vy":-1.26473, "omega":0.0, "ax":-3.97322, "ay":5.54777, "alpha":0.0, "fx":[-67.58338,-67.58338,-67.58338,-67.58338], "fy":[94.3659,94.3659,94.3659,94.3659]}, + {"t":7.16688, "x":6.25303, "y":0.44972, "heading":1.5708, "vx":-1.69585, "vy":-1.05918, "omega":0.0, "ax":-4.14669, "ay":8.0861, "alpha":0.0, "fx":[-70.53401,-70.53401,-70.53401,-70.53401], "fy":[137.54217,137.54217,137.54217,137.54217]}, + {"t":7.20393, "x":6.18735, "y":0.41603, "heading":1.5708, "vx":-1.84949, "vy":-0.75958, "omega":0.0, "ax":-2.76554, "ay":8.89513, "alpha":0.0, "fx":[-47.04101,-47.04101,-47.04101,-47.04101], "fy":[151.30367,151.30367,151.30367,151.30367]}, + {"t":7.24099, "x":6.11693, "y":0.39399, "heading":1.5708, "vx":-1.95196, "vy":-0.43001, "omega":0.0, "ax":-1.11235, "ay":9.2147, "alpha":0.0, "fx":[-18.92076,-18.92076,-18.92076,-18.92076], "fy":[156.73944,156.73944,156.73944,156.73944]}, + {"t":7.28339, "x":6.03316, "y":0.38404, "heading":1.5708, "vx":-1.99913, "vy":-0.03929, "omega":0.0, "ax":0.02029, "ay":2.60234, "alpha":0.0, "fx":[0.34506,0.34506,0.34506,0.34506], "fy":[44.26506,44.26506,44.26506,44.26506]}, + {"t":7.32579, "x":5.94841, "y":0.38471, "heading":1.5708, "vx":-1.99827, "vy":0.07106, "omega":0.0, "ax":-0.01808, "ay":-0.62557, "alpha":0.0, "fx":[-0.30761,-0.30761,-0.30761,-0.30761], "fy":[-10.64075,-10.64075,-10.64075,-10.64075]}, + {"t":7.36819, "x":5.86367, "y":0.38716, "heading":1.5708, "vx":-1.99903, "vy":0.04453, "omega":0.0, "ax":-0.00496, "ay":-0.25346, "alpha":0.0, "fx":[-0.08443,-0.08443,-0.08443,-0.08443], "fy":[-4.31124,-4.31124,-4.31124,-4.31124]}, + {"t":7.41059, "x":5.7789, "y":0.38882, "heading":1.5708, "vx":-1.99924, "vy":0.03378, "omega":0.0, "ax":-0.00248, "ay":-0.16322, "alpha":0.0, "fx":[-0.0421,-0.0421,-0.0421,-0.0421], "fy":[-2.77636,-2.77636,-2.77636,-2.77636]}, + {"t":7.45299, "x":5.69413, "y":0.39011, "heading":1.5708, "vx":-1.99935, "vy":0.02686, "omega":0.0, "ax":-0.00149, "ay":-0.12313, "alpha":0.0, "fx":[-0.0254,-0.0254,-0.0254,-0.0254], "fy":[-2.09448,-2.09448,-2.09448,-2.09448]}, + {"t":7.4954, "x":5.60935, "y":0.39114, "heading":1.5708, "vx":-1.99941, "vy":0.02164, "omega":0.0, "ax":-0.00096, "ay":-0.09859, "alpha":0.0, "fx":[-0.0164,-0.0164,-0.0164,-0.0164], "fy":[-1.67707,-1.67707,-1.67707,-1.67707]}, + {"t":7.5378, "x":5.52457, "y":0.39197, "heading":1.5708, "vx":-1.99945, "vy":0.01746, "omega":0.0, "ax":-0.00064, "ay":-0.08146, "alpha":0.0, "fx":[-0.0109,-0.0109,-0.0109,-0.0109], "fy":[-1.38565,-1.38565,-1.38565,-1.38565]}, + {"t":7.5802, "x":5.43979, "y":0.39264, "heading":1.5708, "vx":-1.99948, "vy":0.01401, "omega":0.0, "ax":-0.00043, "ay":-0.06876, "alpha":0.0, "fx":[-0.00734,-0.00734,-0.00734,-0.00734], "fy":[-1.16962,-1.16962,-1.16962,-1.16962]}, + {"t":7.6226, "x":5.35501, "y":0.39317, "heading":1.5708, "vx":-1.9995, "vy":0.01109, "omega":0.0, "ax":-0.00029, "ay":-0.05912, "alpha":0.0, "fx":[-0.00495,-0.00495,-0.00495,-0.00495], "fy":[-1.00557,-1.00557,-1.00557,-1.00557]}, + {"t":7.665, "x":5.27022, "y":0.39358, "heading":1.5708, "vx":-1.99951, "vy":0.00859, "omega":0.0, "ax":-0.00019, "ay":-0.05181, "alpha":0.0, "fx":[-0.0033,-0.0033,-0.0033,-0.0033], "fy":[-0.8812,-0.8812,-0.8812,-0.8812]}, + {"t":7.7074, "x":5.18544, "y":0.3939, "heading":1.5708, "vx":-1.99952, "vy":0.00639, "omega":0.0, "ax":-0.00013, "ay":-0.04642, "alpha":0.0, "fx":[-0.00214,-0.00214,-0.00214,-0.00214], "fy":[-0.78952,-0.78952,-0.78952,-0.78952]}, + {"t":7.74981, "x":5.10066, "y":0.39413, "heading":1.5708, "vx":-1.99952, "vy":0.00442, "omega":0.0, "ax":-0.00008, "ay":-0.0427, "alpha":0.0, "fx":[-0.00128,-0.00128,-0.00128,-0.00128], "fy":[-0.7264,-0.7264,-0.7264,-0.7264]}, + {"t":7.79221, "x":5.01587, "y":0.39428, "heading":1.5708, "vx":-1.99953, "vy":0.00261, "omega":0.0, "ax":-0.00004, "ay":-0.04053, "alpha":0.0, "fx":[-0.00061,-0.00061,-0.00061,-0.00061], "fy":[-0.68938,-0.68938,-0.68938,-0.68938]}, + {"t":7.83461, "x":4.93109, "y":0.39435, "heading":1.5708, "vx":-1.99953, "vy":0.00089, "omega":0.0, "ax":0.0, "ay":-0.03981, "alpha":0.0, "fx":[-0.00002,-0.00002,-0.00002,-0.00002], "fy":[-0.67716,-0.67716,-0.67716,-0.67716]}, + {"t":7.87701, "x":4.84631, "y":0.39436, "heading":1.5708, "vx":-1.99953, "vy":-0.0008, "omega":0.0, "ax":0.00003, "ay":-0.04053, "alpha":0.0, "fx":[0.00057,0.00057,0.00057,0.00057], "fy":[-0.68933,-0.68933,-0.68933,-0.68933]}, + {"t":7.91941, "x":4.76152, "y":0.39429, "heading":1.5708, "vx":-1.99953, "vy":-0.00252, "omega":0.0, "ax":0.00007, "ay":-0.0427, "alpha":0.0, "fx":[0.00124,0.00124,0.00124,0.00124], "fy":[-0.7263,-0.7263,-0.7263,-0.7263]}, + {"t":7.96182, "x":4.67674, "y":0.39414, "heading":1.5708, "vx":-1.99952, "vy":-0.00433, "omega":0.0, "ax":0.00012, "ay":-0.04641, "alpha":0.0, "fx":[0.00209,0.00209,0.00209,0.00209], "fy":[-0.78937,-0.78937,-0.78937,-0.78937]}, + {"t":8.00422, "x":4.59196, "y":0.39392, "heading":1.5708, "vx":-1.99952, "vy":-0.00629, "omega":0.0, "ax":0.00019, "ay":-0.05179, "alpha":0.0, "fx":[0.00325,0.00325,0.00325,0.00325], "fy":[-0.88097,-0.88097,-0.88097,-0.88097]}, + {"t":8.04662, "x":4.50717, "y":0.3936, "heading":1.5708, "vx":-1.99951, "vy":-0.00849, "omega":0.0, "ax":0.00029, "ay":-0.0591, "alpha":0.0, "fx":[0.00489,0.00489,0.00489,0.00489], "fy":[-1.00525,-1.00525,-1.00525,-1.00525]}, + {"t":8.08902, "x":4.42239, "y":0.39319, "heading":1.5708, "vx":-1.9995, "vy":-0.011, "omega":0.0, "ax":0.00043, "ay":-0.06874, "alpha":0.0, "fx":[0.00728,0.00728,0.00728,0.00728], "fy":[-1.16918,-1.16918,-1.16918,-1.16918]}, + {"t":8.13142, "x":4.33761, "y":0.39266, "heading":1.5708, "vx":-1.99948, "vy":-0.01391, "omega":0.0, "ax":0.00064, "ay":-0.08143, "alpha":0.0, "fx":[0.01083,0.01083,0.01083,0.01083], "fy":[-1.38505,-1.38505,-1.38505,-1.38505]}, + {"t":8.17382, "x":4.25283, "y":0.392, "heading":1.5708, "vx":-1.99945, "vy":-0.01736, "omega":0.0, "ax":0.00096, "ay":-0.09855, "alpha":0.0, "fx":[0.0163,0.0163,0.0163,0.0163], "fy":[-1.67625,-1.67625,-1.67625,-1.67625]}, + {"t":8.21623, "x":4.16805, "y":0.39117, "heading":1.5708, "vx":-1.99941, "vy":-0.02154, "omega":0.0, "ax":0.00149, "ay":-0.12307, "alpha":0.0, "fx":[0.02528,0.02528,0.02528,0.02528], "fy":[-2.09337,-2.09337,-2.09337,-2.09337]}, + {"t":8.25863, "x":4.08327, "y":0.39015, "heading":1.5708, "vx":-1.99935, "vy":-0.02676, "omega":0.0, "ax":0.00246, "ay":-0.16314, "alpha":0.0, "fx":[0.04193,0.04193,0.04193,0.04193], "fy":[-2.77488,-2.77488,-2.77488,-2.77488]}, + {"t":8.30103, "x":3.9985, "y":0.38887, "heading":1.5708, "vx":-1.99925, "vy":-0.03368, "omega":0.0, "ax":0.00495, "ay":-0.25348, "alpha":0.0, "fx":[0.0842,0.0842,0.0842,0.0842], "fy":[-4.31154,-4.31154,-4.31154,-4.31154]}, + {"t":8.34343, "x":3.91373, "y":0.38721, "heading":1.5708, "vx":-1.99904, "vy":-0.04442, "omega":0.0, "ax":0.01818, "ay":-0.62926, "alpha":0.0, "fx":[0.30927,0.30927,0.30927,0.30927], "fy":[-10.70349,-10.70349,-10.70349,-10.70349]}, + {"t":8.38583, "x":3.82899, "y":0.38476, "heading":1.5708, "vx":-1.99826, "vy":-0.07111, "omega":0.0, "ax":-0.02001, "ay":2.57201, "alpha":0.0, "fx":[-0.34033,-0.34033,-0.34033,-0.34033], "fy":[43.74914,43.74914,43.74914,43.74914]}, + {"t":8.42823, "x":3.74424, "y":0.38406, "heading":1.5708, "vx":-1.99911, "vy":0.03795, "omega":0.0, "ax":1.10651, "ay":9.32269, "alpha":0.0, "fx":[18.82147,18.82147,18.82147,18.82147], "fy":[158.57621,158.57621,158.57621,158.57621]}, + {"t":8.47064, "x":3.66047, "y":0.39405, "heading":1.5708, "vx":-1.95219, "vy":0.43325, "omega":0.0, "ax":-1.07083, "ay":5.50404, "alpha":25.33818, "fx":[-159.31633,145.28624,17.11319,-75.94131], "fy":[13.31729,60.83901,159.25829,141.07405]}, + {"t":8.49517, "x":3.61225, "y":0.40633, "heading":1.57842, "vx":-1.97846, "vy":0.56827, "omega":0.62159, "ax":-1.15417, "ay":5.9178, "alpha":23.60363, "fx":[-158.33244,135.37063,18.00044,-73.56731], "fy":[20.80642,80.49819,159.08767,142.24832]}, + {"t":8.5197, "x":3.56337, "y":0.42206, "heading":1.60077, "vx":-2.00678, "vy":0.71345, "omega":1.20063, "ax":-1.23377, "ay":6.36011, "alpha":21.69958, "fx":[-155.9887,123.8751,18.6783,-70.50902], "fy":[32.69192,97.47355,158.90115,143.66791]}, + {"t":8.54423, "x":3.51377, "y":0.44147, "heading":1.63676, "vx":-2.03704, "vy":0.86947, "omega":1.73296, "ax":-1.18228, "ay":6.97046, "alpha":18.9695, "fx":[-147.77066,111.55268,20.29613,-64.51938], "fy":[57.95454,111.54256,158.5201,146.24468]}, + {"t":8.56876, "x":3.46344, "y":0.4649, "heading":1.68498, "vx":-2.06605, "vy":1.04047, "omega":2.19832, "ax":-0.36667, "ay":8.14588, "alpha":12.7775, "fx":[-100.90952,97.32029,26.3069,-47.66539], "fy":[120.67217,124.18603,157.25456,152.1233]}, + {"t":8.5933, "x":3.41265, "y":0.49288, "heading":1.74275, "vx":-2.07504, "vy":1.24031, "omega":2.51178, "ax":3.07985, "ay":8.67619, "alpha":3.10784, "fx":[48.66672,78.62492,54.21599,28.0415], "fy":[149.46434,136.60908,148.68165,155.56322]}, + {"t":8.61783, "x":3.36267, "y":0.52591, "heading":1.8053, "vx":-1.99949, "vy":1.45315, "omega":2.58802, "ax":5.61945, "ay":7.2984, "alpha":-0.734, "fx":[94.44929,91.2909,96.79703,99.8039], "fy":[125.22443,127.41398,123.10374,120.83293]}, + {"t":8.64236, "x":3.31531, "y":0.56376, "heading":1.86857, "vx":-1.86163, "vy":1.63219, "omega":2.57001, "ax":7.06247, "ay":5.64398, "alpha":4.33503, "fx":[130.51705,135.34503,112.65762,102.003], "fy":[82.59794,76.63754,107.84265,116.93172]}, + {"t":8.66689, "x":3.27176, "y":0.6055, "heading":1.93292, "vx":-1.68838, "vy":1.77065, "omega":2.67636, "ax":8.08903, "ay":2.05507, "alpha":12.71308, "fx":[148.24599,155.48896,130.94401,115.68921], "fy":[-37.94828,-4.12233,83.42218,98.47301]}, + {"t":8.69142, "x":3.23278, "y":0.64955, "heading":2.0024, "vx":-1.48994, "vy":1.82106, "omega":2.98823, "ax":7.28833, "ay":-0.75362, "alpha":17.59924, "fx":[92.92158,143.90271,139.27511,119.78993], "fy":[-124.60751,-61.88925,67.4872,67.73437]}, + {"t":8.71595, "x":3.19842, "y":0.694, "heading":2.08101, "vx":-1.31114, "vy":1.80258, "omega":3.41997, "ax":4.32428, "ay":-4.79353, "alpha":20.34781, "fx":[60.25991,129.17819,147.6114,-42.83013], "fy":[-145.22579,-90.17345,43.61237,-134.3595]}, + {"t":8.74049, "x":3.16756, "y":0.73678, "heading":2.17103, "vx":-1.20506, "vy":1.68498, "omega":3.91914, "ax":5.73614, "ay":-7.26979, "alpha":-1.69393, "fx":[108.26497,88.21478,88.69037,105.11011], "fy":[-114.65744,-130.67078,-131.02061,-118.2794]}, + {"t":8.76502, "x":3.13972, "y":0.77593, "heading":2.26666, "vx":-1.06434, "vy":1.50664, "omega":3.87759, "ax":5.1511, "ay":-7.05345, "alpha":-8.76152, "fx":[139.20363,21.4043,69.66501,120.20233], "fy":[-74.83111,-156.80316,-143.47295,-104.80117]}, + {"t":8.78955, "x":3.11516, "y":0.81076, "heading":2.35915, "vx":-0.93798, "vy":1.33361, "omega":3.66265, "ax":4.77963, "ay":-6.55312, "alpha":-13.24678, "fx":[150.78109,-22.06684,68.82785,127.65854], "fy":[-47.95666,-157.43161,-144.29624,-96.18248]}, + {"t":8.81408, "x":3.09359, "y":0.84151, "heading":2.44502, "vx":-0.82072, "vy":1.17285, "omega":3.33768, "ax":4.66869, "ay":-6.12761, "alpha":-15.58654, "fx":[155.12594,-42.22479,71.71325,133.038], "fy":[-31.18621,-153.74342,-143.08339,-88.90277]}, + {"t":8.83861, "x":3.07486, "y":0.86844, "heading":2.52221, "vx":-0.70619, "vy":1.02252, "omega":2.95532, "ax":4.67706, "ay":-5.82713, "alpha":-16.71276, "fx":[156.60025,-51.12717,75.49454,137.25437], "fy":[-21.47568,-151.31485,-141.24139,-82.4392]}, + {"t":8.86315, "x":3.05894, "y":0.89177, "heading":2.58968, "vx":-0.59145, "vy":0.87957, "omega":2.54532, "ax":4.72686, "ay":-5.6353, "alpha":-17.16161, "fx":[156.73337,-55.01087,79.26318,140.62471], "fy":[-17.35696,-150.13696,-139.23818,-76.68692]}, + {"t":8.88768, "x":3.04586, "y":0.91165, "heading":2.64695, "vx":-0.4755, "vy":0.74133, "omega":2.12432, "ax":4.77934, "ay":-5.54376, "alpha":-17.17935, "fx":[155.86584,-56.66373,82.6599,143.3187], "fy":[-18.60708,-149.64855,-137.30349,-71.6317]}, + {"t":8.91221, "x":3.03563, "y":0.92817, "heading":2.6939, "vx":-0.35825, "vy":0.60533, "omega":1.70288, "ax":4.80336, "ay":-5.56866, "alpha":-16.84735, "fx":[153.23569,-57.40247,85.5321,145.45011], "fy":[-26.57152,-149.4561,-135.57382,-67.28375]}, + {"t":8.93674, "x":3.02829, "y":0.94134, "heading":2.7306, "vx":-0.24041, "vy":0.46872, "omega":1.28958, "ax":4.72288, "ay":-5.80088, "alpha":-16.06648, "fx":[144.29135,-57.87046,87.8187,147.10003], "fy":[-47.54199,-149.34043,-134.13583,-63.66725]}, + {"t":8.96127, "x":3.02381, "y":0.95109, "heading":2.7574, "vx":-0.12455, "vy":0.32642, "omega":0.89544, "ax":3.8229, "ay":-6.72282, "alpha":-14.89235, "fx":[80.53374,-58.23861,89.50634,148.30397], "fy":[-114.25566,-149.24554,-133.04099,-60.87094]}, + {"t":8.98581, "x":3.0219, "y":0.95708, "heading":2.77489, "vx":-0.03077, "vy":0.16149, "omega":0.5301, "ax":1.25431, "ay":-6.58298, "alpha":-21.60872, "fx":[-95.99299,-58.32475,90.60748,149.05232], "fy":[-107.2704,-149.2481,-132.31376,-59.06614]}, + {"t":9.01034, "x":3.02153, "y":0.95906, "heading":2.78139, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0,0.0,0.0,0.0]}], "splits":[0] }, "events":[] diff --git a/src/main/deploy/v1_doomspiral/choreo/TO_TEST_EFFECTIVENESS.traj b/src/main/deploy/v1_doomspiral/choreo/TO_TEST_EFFECTIVENESS.traj index aa886062..eaeb9bc1 100644 --- a/src/main/deploy/v1_doomspiral/choreo/TO_TEST_EFFECTIVENESS.traj +++ b/src/main/deploy/v1_doomspiral/choreo/TO_TEST_EFFECTIVENESS.traj @@ -67,165 +67,165 @@ "differentialTrackWidth":0.5588 }, "sampleType":"Swerve", - "waypoints":[0.0,1.3028,2.61727,3.4231,4.3147,4.71882,5.22996], + "waypoints":[0.0,1.3028,2.61728,3.42307,4.31465,4.71881,5.22994], "samples":[ {"t":0.0, "x":4.46868, "y":0.36495, "heading":1.5708, "vx":0.0, "vy":0.0, "omega":0.0, "ax":9.42785, "ay":0.13954, "alpha":0.0, "fx":[160.36512,160.36512,160.36512,160.36512], "fy":[2.37346,2.37346,2.37346,2.37346]}, {"t":0.04653, "x":4.47888, "y":0.3651, "heading":1.5708, "vx":0.43866, "vy":0.00649, "omega":0.0, "ax":9.42686, "ay":0.11896, "alpha":0.0, "fx":[160.34811,160.34811,160.34811,160.34811], "fy":[2.02342,2.02342,2.02342,2.02342]}, - {"t":0.09306, "x":4.5095, "y":0.36553, "heading":1.5708, "vx":0.87728, "vy":0.01203, "omega":0.0, "ax":9.42535, "ay":0.09871, "alpha":0.0, "fx":[160.32258,160.32258,160.32258,160.32258], "fy":[1.67906,1.67906,1.67906,1.67906]}, + {"t":0.09306, "x":4.5095, "y":0.36553, "heading":1.5708, "vx":0.87728, "vy":0.01203, "omega":0.0, "ax":9.42535, "ay":0.09871, "alpha":0.0, "fx":[160.32258,160.32258,160.32258,160.32258], "fy":[1.67907,1.67907,1.67907,1.67907]}, {"t":0.13959, "x":4.56052, "y":0.3662, "heading":1.5708, "vx":1.31583, "vy":0.01662, "omega":0.0, "ax":9.42306, "ay":0.07873, "alpha":0.0, "fx":[160.2836,160.2836,160.2836,160.2836], "fy":[1.33914,1.33914,1.33914,1.33914]}, {"t":0.18611, "x":4.63194, "y":0.36705, "heading":1.5708, "vx":1.75427, "vy":0.02028, "omega":0.0, "ax":9.41937, "ay":0.05875, "alpha":0.0, "fx":[160.22083,160.22083,160.22083,160.22083], "fy":[0.99924,0.99924,0.99924,0.99924]}, {"t":0.23264, "x":4.72376, "y":0.36806, "heading":1.5708, "vx":2.19253, "vy":0.02302, "omega":0.0, "ax":9.41276, "ay":0.03804, "alpha":0.0, "fx":[160.10832,160.10832,160.10832,160.10832], "fy":[0.6471,0.6471,0.6471,0.6471]}, - {"t":0.27917, "x":4.83596, "y":0.36917, "heading":1.5708, "vx":2.6305, "vy":0.02479, "omega":0.0, "ax":9.39801, "ay":0.01416, "alpha":0.0, "fx":[159.85753,159.85753,159.85753,159.85753], "fy":[0.2409,0.2409,0.2409,0.2409]}, - {"t":0.3257, "x":4.96853, "y":0.37034, "heading":1.5708, "vx":3.06777, "vy":0.02545, "omega":0.0, "ax":9.33875, "ay":-0.02969, "alpha":0.0, "fx":[158.84944,158.84944,158.84944,158.84944], "fy":[-0.5051,-0.5051,-0.5051,-0.5051]}, - {"t":0.37223, "x":5.12138, "y":0.37149, "heading":1.5708, "vx":3.50229, "vy":0.02406, "omega":0.0, "ax":0.12623, "ay":-0.63707, "alpha":0.0, "fx":[2.14709,2.14709,2.14709,2.14709], "fy":[-10.8363,-10.8363,-10.8363,-10.8363]}, + {"t":0.27917, "x":4.83596, "y":0.36917, "heading":1.5708, "vx":2.6305, "vy":0.02479, "omega":0.0, "ax":9.39801, "ay":0.01416, "alpha":0.0, "fx":[159.85754,159.85754,159.85754,159.85754], "fy":[0.2409,0.2409,0.2409,0.2409]}, + {"t":0.3257, "x":4.96853, "y":0.37034, "heading":1.5708, "vx":3.06777, "vy":0.02545, "omega":0.0, "ax":9.33875, "ay":-0.02969, "alpha":0.0, "fx":[158.84945,158.84945,158.84945,158.84945], "fy":[-0.50509,-0.50509,-0.50509,-0.50509]}, + {"t":0.37223, "x":5.12138, "y":0.37149, "heading":1.5708, "vx":3.50229, "vy":0.02406, "omega":0.0, "ax":0.12623, "ay":-0.63707, "alpha":0.0, "fx":[2.14713,2.14713,2.14713,2.14713], "fy":[-10.83631,-10.83631,-10.83631,-10.83631]}, {"t":0.41876, "x":5.28447, "y":0.37192, "heading":1.5708, "vx":3.50816, "vy":-0.00558, "omega":0.0, "ax":-0.00099, "ay":-0.28675, "alpha":0.0, "fx":[-0.01683,-0.01683,-0.01683,-0.01683], "fy":[-4.87755,-4.87755,-4.87755,-4.87755]}, - {"t":0.46528, "x":5.4477, "y":0.37135, "heading":1.5708, "vx":3.50811, "vy":-0.01892, "omega":0.0, "ax":-0.00189, "ay":-0.2643, "alpha":0.0, "fx":[-0.03212,-0.03212,-0.03212,-0.03212], "fy":[-4.49563,-4.49563,-4.49563,-4.49563]}, + {"t":0.46528, "x":5.4477, "y":0.37135, "heading":1.5708, "vx":3.50811, "vy":-0.01892, "omega":0.0, "ax":-0.00189, "ay":-0.2643, "alpha":0.0, "fx":[-0.03212,-0.03212,-0.03212,-0.03212], "fy":[-4.49562,-4.49562,-4.49562,-4.49562]}, {"t":0.51181, "x":5.61092, "y":0.37019, "heading":1.5708, "vx":3.50803, "vy":-0.03122, "omega":0.0, "ax":-0.00301, "ay":-0.28023, "alpha":0.0, "fx":[-0.05125,-0.05125,-0.05125,-0.05125], "fy":[-4.76665,-4.76665,-4.76665,-4.76665]}, - {"t":0.55834, "x":5.77414, "y":0.36843, "heading":1.5708, "vx":3.50789, "vy":-0.04426, "omega":0.0, "ax":-0.00459, "ay":-0.31273, "alpha":0.0, "fx":[-0.0781,-0.0781,-0.0781,-0.0781], "fy":[-5.31949,-5.31949,-5.31949,-5.31949]}, + {"t":0.55834, "x":5.77414, "y":0.36843, "heading":1.5708, "vx":3.50789, "vy":-0.04426, "omega":0.0, "ax":-0.00459, "ay":-0.31273, "alpha":0.0, "fx":[-0.0781,-0.0781,-0.0781,-0.0781], "fy":[-5.31948,-5.31948,-5.31948,-5.31948]}, {"t":0.60487, "x":5.93735, "y":0.36603, "heading":1.5708, "vx":3.50767, "vy":-0.05881, "omega":0.0, "ax":-0.00714, "ay":-0.37135, "alpha":0.0, "fx":[-0.12138,-0.12138,-0.12138,-0.12138], "fy":[-6.31663,-6.31663,-6.31663,-6.31663]}, - {"t":0.6514, "x":6.10055, "y":0.3629, "heading":1.5708, "vx":3.50734, "vy":-0.07609, "omega":0.0, "ax":-0.01187, "ay":-0.47766, "alpha":0.0, "fx":[-0.20185,-0.20185,-0.20185,-0.20185], "fy":[-8.12489,-8.12489,-8.12489,-8.12489]}, - {"t":0.69793, "x":6.26373, "y":0.35884, "heading":1.5708, "vx":3.50679, "vy":-0.09831, "omega":0.0, "ax":-0.01634, "ay":-0.51976, "alpha":0.0, "fx":[-0.27802,-0.27802,-0.27802,-0.27802], "fy":[-8.84099,-8.84099,-8.84099,-8.84099]}, - {"t":0.74445, "x":6.42688, "y":0.3537, "heading":1.5708, "vx":3.50603, "vy":-0.12249, "omega":0.0, "ax":0.04562, "ay":2.55864, "alpha":0.0, "fx":[0.7759,0.7759,0.7759,0.7759], "fy":[43.52177,43.52177,43.52177,43.52177]}, - {"t":0.79098, "x":6.59006, "y":0.35077, "heading":1.5708, "vx":3.50815, "vy":-0.00344, "omega":0.0, "ax":-0.48439, "ay":8.5862, "alpha":0.0, "fx":[-8.23939,-8.23939,-8.23939,-8.23939], "fy":[146.04877,146.04877,146.04877,146.04877]}, - {"t":0.83751, "x":6.75276, "y":0.35991, "heading":1.5708, "vx":3.48561, "vy":0.39606, "omega":0.0, "ax":-1.60219, "ay":9.07077, "alpha":0.0, "fx":[-27.25276,-27.25276,-27.25276,-27.25276], "fy":[154.29121,154.29121,154.29121,154.29121]}, - {"t":0.88404, "x":6.91321, "y":0.38815, "heading":1.5708, "vx":3.41107, "vy":0.81811, "omega":0.0, "ax":-2.74327, "ay":8.92021, "alpha":0.0, "fx":[-46.66227,-46.66227,-46.66227,-46.66227], "fy":[151.73023,151.73023,151.73023,151.73023]}, - {"t":0.93057, "x":7.06895, "y":0.43587, "heading":1.5708, "vx":3.28343, "vy":1.23315, "omega":0.0, "ax":-3.89095, "ay":8.53115, "alpha":0.0, "fx":[-66.184,-66.184,-66.184,-66.184], "fy":[145.1124,145.1124,145.1124,145.1124]}, - {"t":0.9771, "x":7.21751, "y":0.50249, "heading":1.5708, "vx":3.10239, "vy":1.63009, "omega":0.0, "ax":-5.89876, "ay":7.31636, "alpha":0.0, "fx":[-100.33616,-100.33616,-100.33616,-100.33616], "fy":[124.44917,124.44917,124.44917,124.44917]}, - {"t":1.02362, "x":7.35547, "y":0.58625, "heading":1.5708, "vx":2.82793, "vy":1.97051, "omega":0.0, "ax":-7.67474, "ay":5.44637, "alpha":0.0, "fx":[-130.54512,-130.54512,-130.54512,-130.54512], "fy":[92.64127,92.64127,92.64127,92.64127]}, - {"t":1.07015, "x":7.47874, "y":0.68383, "heading":1.5708, "vx":2.47083, "vy":2.22392, "omega":0.0, "ax":-8.37738, "ay":4.30334, "alpha":0.0, "fx":[-142.49691,-142.49691,-142.49691,-142.49691], "fy":[73.19863,73.19863,73.19863,73.19863]}, - {"t":1.11668, "x":7.58464, "y":0.79196, "heading":1.5708, "vx":2.08105, "vy":2.42415, "omega":0.0, "ax":-8.70672, "ay":3.60177, "alpha":0.0, "fx":[-148.09885,-148.09885,-148.09885,-148.09885], "fy":[61.265,61.265,61.265,61.265]}, - {"t":1.16321, "x":7.67204, "y":0.90865, "heading":1.5708, "vx":1.67594, "vy":2.59173, "omega":0.0, "ax":-8.88694, "ay":3.13916, "alpha":0.0, "fx":[-151.16429,-151.16429,-151.16429,-151.16429], "fy":[53.39623,53.39623,53.39623,53.39623]}, - {"t":1.20974, "x":7.7404, "y":1.03264, "heading":1.5708, "vx":1.26244, "vy":2.73779, "omega":0.0, "ax":-8.99704, "ay":2.81455, "alpha":0.0, "fx":[-153.0371,-153.0371,-153.0371,-153.0371], "fy":[47.8747,47.8747,47.8747,47.8747]}, - {"t":1.25627, "x":7.7894, "y":1.16307, "heading":1.5708, "vx":0.84382, "vy":2.86875, "omega":0.0, "ax":-9.06981, "ay":2.5756, "alpha":0.0, "fx":[-154.27485,-154.27485,-154.27485,-154.27485], "fy":[43.81029,43.81029,43.81029,43.81029]}, - {"t":1.3028, "x":7.81885, "y":1.29934, "heading":1.5708, "vx":0.42182, "vy":2.98859, "omega":0.0, "ax":-8.78238, "ay":3.40674, "alpha":0.0, "fx":[-149.38586,-149.38586,-149.38586,-149.38586], "fy":[57.94763,57.94763,57.94763,57.94763]}, - {"t":1.34146, "x":7.82859, "y":1.41743, "heading":1.5708, "vx":0.08228, "vy":3.1203, "omega":0.0, "ax":-4.65345, "ay":8.11321, "alpha":0.0, "fx":[-79.15392,-79.15392,-79.15392,-79.15392], "fy":[138.00335,138.00335,138.00335,138.00335]}, - {"t":1.38012, "x":7.8283, "y":1.54412, "heading":1.5708, "vx":-0.09762, "vy":3.43396, "omega":0.0, "ax":0.63287, "ay":1.88428, "alpha":0.0, "fx":[10.76488,10.76488,10.76488,10.76488], "fy":[32.05114,32.05114,32.05114,32.05114]}, - {"t":1.41878, "x":7.82499, "y":1.67829, "heading":1.5708, "vx":-0.07316, "vy":3.50681, "omega":0.0, "ax":0.59826, "ay":0.01087, "alpha":0.0, "fx":[10.17619,10.17619,10.17619,10.17619], "fy":[0.18497,0.18497,0.18497,0.18497]}, - {"t":1.45744, "x":7.82261, "y":1.81388, "heading":1.5708, "vx":-0.05003, "vy":3.50723, "omega":0.0, "ax":0.34919, "ay":0.0043, "alpha":0.0, "fx":[5.93968,5.93968,5.93968,5.93968], "fy":[0.07313,0.07313,0.07313,0.07313]}, - {"t":1.4961, "x":7.82094, "y":1.94947, "heading":1.5708, "vx":-0.03653, "vy":3.50739, "omega":0.0, "ax":0.22062, "ay":0.00202, "alpha":0.0, "fx":[3.75277,3.75277,3.75277,3.75277], "fy":[0.0344,0.0344,0.0344,0.0344]}, - {"t":1.53476, "x":7.81969, "y":2.08507, "heading":1.5708, "vx":-0.028, "vy":3.50747, "omega":0.0, "ax":0.14965, "ay":0.00107, "alpha":0.0, "fx":[2.54555,2.54555,2.54555,2.54555], "fy":[0.01812,0.01812,0.01812,0.01812]}, - {"t":1.57342, "x":7.81872, "y":2.22068, "heading":1.5708, "vx":-0.02221, "vy":3.50751, "omega":0.0, "ax":0.10549, "ay":0.0006, "alpha":0.0, "fx":[1.7944,1.7944,1.7944,1.7944], "fy":[0.01024,0.01024,0.01024,0.01024]}, - {"t":1.61208, "x":7.81794, "y":2.35628, "heading":1.5708, "vx":-0.01813, "vy":3.50754, "omega":0.0, "ax":0.07583, "ay":0.00036, "alpha":0.0, "fx":[1.28986,1.28986,1.28986,1.28986], "fy":[0.00606,0.00606,0.00606,0.00606]}, - {"t":1.65074, "x":7.8173, "y":2.49189, "heading":1.5708, "vx":-0.0152, "vy":3.50755, "omega":0.0, "ax":0.05511, "ay":0.00022, "alpha":0.0, "fx":[0.93735,0.93735,0.93735,0.93735], "fy":[0.00372,0.00372,0.00372,0.00372]}, - {"t":1.6894, "x":7.81675, "y":2.62749, "heading":1.5708, "vx":-0.01307, "vy":3.50756, "omega":0.0, "ax":0.04036, "ay":0.00014, "alpha":0.0, "fx":[0.68654,0.68654,0.68654,0.68654], "fy":[0.00235,0.00235,0.00235,0.00235]}, - {"t":1.72807, "x":7.81628, "y":2.7631, "heading":1.5708, "vx":-0.01151, "vy":3.50756, "omega":0.0, "ax":0.02981, "ay":0.00009, "alpha":0.0, "fx":[0.50704,0.50704,0.50704,0.50704], "fy":[0.00153,0.00153,0.00153,0.00153]}, - {"t":1.76673, "x":7.81585, "y":2.8987, "heading":1.5708, "vx":-0.01036, "vy":3.50757, "omega":0.0, "ax":0.02228, "ay":0.00006, "alpha":0.0, "fx":[0.37898,0.37898,0.37898,0.37898], "fy":[0.00103,0.00103,0.00103,0.00103]}, - {"t":1.80539, "x":7.81547, "y":3.03431, "heading":1.5708, "vx":-0.0095, "vy":3.50757, "omega":0.0, "ax":0.01699, "ay":0.00004, "alpha":0.0, "fx":[0.28892,0.28892,0.28892,0.28892], "fy":[0.00072,0.00072,0.00072,0.00072]}, - {"t":1.84405, "x":7.81511, "y":3.16991, "heading":1.5708, "vx":-0.00884, "vy":3.50757, "omega":0.0, "ax":0.01338, "ay":0.00003, "alpha":0.0, "fx":[0.22764,0.22764,0.22764,0.22764], "fy":[0.00053,0.00053,0.00053,0.00053]}, - {"t":1.88271, "x":7.81478, "y":3.30552, "heading":1.5708, "vx":-0.00832, "vy":3.50757, "omega":0.0, "ax":0.01111, "ay":0.00002, "alpha":0.0, "fx":[0.18896,0.18896,0.18896,0.18896], "fy":[0.00041,0.00041,0.00041,0.00041]}, - {"t":1.92137, "x":7.81447, "y":3.44113, "heading":1.5708, "vx":-0.00789, "vy":3.50757, "omega":0.0, "ax":0.00994, "ay":0.00002, "alpha":0.0, "fx":[0.169,0.169,0.169,0.169], "fy":[0.00035,0.00035,0.00035,0.00035]}, - {"t":1.96003, "x":7.81417, "y":3.57673, "heading":1.5708, "vx":-0.00751, "vy":3.50758, "omega":0.0, "ax":0.00975, "ay":0.00002, "alpha":0.0, "fx":[0.16579,0.16579,0.16579,0.16579], "fy":[0.00033,0.00033,0.00033,0.00033]}, - {"t":1.99869, "x":7.81389, "y":3.71234, "heading":1.5708, "vx":-0.00713, "vy":3.50758, "omega":0.0, "ax":0.01052, "ay":0.00002, "alpha":0.0, "fx":[0.17899,0.17899,0.17899,0.17899], "fy":[0.00034,0.00034,0.00034,0.00034]}, - {"t":2.03735, "x":7.81362, "y":3.84795, "heading":1.5708, "vx":-0.00673, "vy":3.50758, "omega":0.0, "ax":0.01234, "ay":0.00002, "alpha":0.0, "fx":[0.20992,0.20992,0.20992,0.20992], "fy":[0.00038,0.00038,0.00038,0.00038]}, - {"t":2.07601, "x":7.81337, "y":3.98355, "heading":1.5708, "vx":-0.00625, "vy":3.50758, "omega":0.0, "ax":0.01538, "ay":0.00003, "alpha":0.0, "fx":[0.26168,0.26168,0.26168,0.26168], "fy":[0.00044,0.00044,0.00044,0.00044]}, - {"t":2.11468, "x":7.81314, "y":4.11916, "heading":1.5708, "vx":-0.00565, "vy":3.50758, "omega":0.0, "ax":0.01996, "ay":0.00003, "alpha":0.0, "fx":[0.33947,0.33947,0.33947,0.33947], "fy":[0.00052,0.00052,0.00052,0.00052]}, - {"t":2.15334, "x":7.81294, "y":4.25476, "heading":1.5708, "vx":-0.00488, "vy":3.50758, "omega":0.0, "ax":0.02653, "ay":0.00003, "alpha":0.0, "fx":[0.4512,0.4512,0.4512,0.4512], "fy":[0.00057,0.00057,0.00057,0.00057]}, - {"t":2.192, "x":7.81277, "y":4.39037, "heading":1.5708, "vx":-0.00386, "vy":3.50758, "omega":0.0, "ax":0.03577, "ay":0.00003, "alpha":0.0, "fx":[0.60848,0.60848,0.60848,0.60848], "fy":[0.00056,0.00056,0.00056,0.00056]}, - {"t":2.23066, "x":7.81265, "y":4.52598, "heading":1.5708, "vx":-0.00247, "vy":3.50758, "omega":0.0, "ax":0.0487, "ay":0.00002, "alpha":0.0, "fx":[0.8283,0.8283,0.8283,0.8283], "fy":[0.00038,0.00038,0.00038,0.00038]}, - {"t":2.26932, "x":7.81259, "y":4.66158, "heading":1.5708, "vx":-0.00059, "vy":3.50758, "omega":0.0, "ax":0.0668, "ay":-0.00001, "alpha":0.0, "fx":[1.13627,1.13627,1.13627,1.13627], "fy":[-0.0002,-0.0002,-0.0002,-0.0002]}, - {"t":2.30798, "x":7.81261, "y":4.79719, "heading":1.5708, "vx":0.00199, "vy":3.50758, "omega":0.0, "ax":0.09252, "ay":-0.0001, "alpha":0.0, "fx":[1.57367,1.57367,1.57367,1.57367], "fy":[-0.00166,-0.00166,-0.00166,-0.00166]}, - {"t":2.34664, "x":7.81276, "y":4.9328, "heading":1.5708, "vx":0.00557, "vy":3.50758, "omega":0.0, "ax":0.1303, "ay":-0.0003, "alpha":0.0, "fx":[2.21636,2.21636,2.21636,2.21636], "fy":[-0.00507,-0.00507,-0.00507,-0.00507]}, - {"t":2.3853, "x":7.81307, "y":5.0684, "heading":1.5708, "vx":0.01061, "vy":3.50757, "omega":0.0, "ax":0.19035, "ay":-0.00077, "alpha":0.0, "fx":[3.23772,3.23772,3.23772,3.23772], "fy":[-0.01313,-0.01313,-0.01313,-0.01313]}, - {"t":2.42396, "x":7.81362, "y":5.20401, "heading":1.5708, "vx":0.01797, "vy":3.50754, "omega":0.0, "ax":0.30323, "ay":-0.00205, "alpha":0.0, "fx":[5.15778,5.15778,5.15778,5.15778], "fy":[-0.03495,-0.03495,-0.03495,-0.03495]}, - {"t":2.46262, "x":7.81455, "y":5.33961, "heading":1.5708, "vx":0.02969, "vy":3.50746, "omega":0.0, "ax":0.58113, "ay":-0.00677, "alpha":0.0, "fx":[9.88485,9.88485,9.88485,9.88485], "fy":[-0.11521,-0.11521,-0.11521,-0.11521]}, - {"t":2.50128, "x":7.81613, "y":5.47521, "heading":1.5708, "vx":0.05216, "vy":3.5072, "omega":0.0, "ax":1.38453, "ay":-0.03112, "alpha":0.0, "fx":[23.5504,23.5504,23.5504,23.5504], "fy":[-0.5293,-0.5293,-0.5293,-0.5293]}, - {"t":2.53995, "x":7.81918, "y":5.61078, "heading":1.5708, "vx":0.10568, "vy":3.50599, "omega":0.0, "ax":-5.16785, "ay":-0.04035, "alpha":0.0, "fx":[-87.90357,-87.90357,-87.90357,-87.90357], "fy":[-0.6863,-0.6863,-0.6863,-0.6863]}, - {"t":2.57861, "x":7.8194, "y":5.74629, "heading":1.5708, "vx":-0.09411, "vy":3.50443, "omega":0.0, "ax":-8.86483, "ay":-2.97826, "alpha":0.0, "fx":[-150.78814,-150.78814,-150.78814,-150.78814], "fy":[-50.65931,-50.65931,-50.65931,-50.65931]}, - {"t":2.61727, "x":7.80914, "y":5.87955, "heading":1.5708, "vx":-0.43683, "vy":3.38929, "omega":0.0, "ax":-7.44539, "ay":-3.47673, "alpha":15.24536, "fx":[-121.18324,-67.80367,-158.02209,-159.56681], "fy":[-104.20879,-144.43321,8.58812,3.50083]}, - {"t":2.64413, "x":7.79472, "y":5.96934, "heading":1.5708, "vx":-0.63683, "vy":3.2959, "omega":0.40951, "ax":-7.16458, "ay":-5.27444, "alpha":9.08803, "fx":[-114.97395,-75.68641,-142.38724,-154.422], "fy":[-110.6641,-140.20022,-69.07606,-38.92621]}, - {"t":2.67099, "x":7.77503, "y":6.05597, "heading":1.5818, "vx":-0.82928, "vy":3.15422, "omega":0.65362, "ax":-6.91775, "ay":-5.64406, "alpha":8.43734, "fx":[-111.18294,-72.39566,-135.65095,-151.44655], "fy":[-114.08204,-141.55612,-80.39629,-47.98127]}, - {"t":2.69785, "x":7.75026, "y":6.13866, "heading":1.59935, "vx":-1.01509, "vy":3.00262, "omega":0.88026, "ax":-6.60477, "ay":-6.00938, "alpha":7.97623, "fx":[-106.31429,-67.24678,-128.0401,-147.78006], "fy":[-118.08121,-143.53719,-90.53001,-56.72325]}, - {"t":2.72471, "x":7.72061, "y":6.21714, "heading":1.623, "vx":-1.19251, "vy":2.8412, "omega":1.09451, "ax":-6.19524, "ay":-6.383, "alpha":7.68416, "fx":[-99.94909,-59.62419,-118.73222,-143.2115], "fy":[-122.7018,-146.06049,-100.24458,-65.28492]}, - {"t":2.75157, "x":7.68634, "y":6.29116, "heading":1.6524, "vx":-1.35892, "vy":2.66974, "omega":1.30092, "ax":-5.63725, "ay":-6.75866, "alpha":7.63037, "fx":[-91.41398,-48.19975,-106.34399,-137.59446], "fy":[-127.92389,-148.9435,-109.77386,-73.2103]}, - {"t":2.77843, "x":7.6478, "y":6.36043, "heading":1.68734, "vx":-1.51034, "vy":2.4882, "omega":1.50588, "ax":-4.82213, "ay":-7.10747, "alpha":7.96846, "fx":[-79.50763,-30.67955,-87.27238,-130.63246], "fy":[-133.57229,-151.38581,-118.9904,-79.63529]}, - {"t":2.8053, "x":7.6055, "y":6.4247, "heading":1.72779, "vx":-1.63987, "vy":2.29728, "omega":1.71992, "ax":-3.47994, "ay":-7.32147, "alpha":9.05196, "fx":[-62.01444,-4.02097,-49.88593,-120.84993], "fy":[-138.90902,-150.66267,-125.08089,-83.49164]}, - {"t":2.83216, "x":7.56019, "y":6.48377, "heading":1.77399, "vx":-1.73335, "vy":2.10062, "omega":1.96307, "ax":-1.0348, "ay":-6.99836, "alpha":11.44658, "fx":[-33.92201,31.59728,29.03201,-97.11374], "fy":[-141.3891,-140.40091,-105.66094,-88.70977]}, - {"t":2.85902, "x":7.51326, "y":6.53767, "heading":1.82672, "vx":-1.76114, "vy":1.91263, "omega":2.27054, "ax":1.82074, "ay":-5.40366, "alpha":13.20498, "fx":[6.27726,68.4621,87.75303,-38.61158], "fy":[-131.71623,-113.29792,-41.9112,-80.73352]}, - {"t":2.88588, "x":7.46661, "y":6.5871, "heading":1.88771, "vx":-1.71223, "vy":1.76749, "omega":2.62524, "ax":3.68416, "ay":-2.64013, "alpha":12.1738, "fx":[43.50513,90.47864,90.38681,26.29555], "fy":[-98.08924,-70.13844,9.00848,-20.4121]}, - {"t":2.91274, "x":7.42194, "y":6.63362, "heading":1.95823, "vx":-1.61327, "vy":1.69657, "omega":2.95224, "ax":3.62796, "ay":0.13254, "alpha":8.26363, "fx":[57.31849,83.83734,68.05569,37.63085], "fy":[-35.32387,-16.82623,35.98969,25.17798]}, - {"t":2.9396, "x":7.37992, "y":6.67924, "heading":2.03753, "vx":-1.51582, "vy":1.70013, "omega":3.17421, "ax":1.6837, "ay":2.20876, "alpha":2.13352, "fx":[28.25742,36.51896,29.06296,20.71782], "fy":[29.34605,33.03053,45.58069,42.32409]}, - {"t":2.96646, "x":7.33981, "y":6.7257, "heading":2.12279, "vx":-1.4706, "vy":1.75946, "omega":3.23152, "ax":-1.3658, "ay":2.27132, "alpha":-3.14767, "fx":[-21.98834,-35.00471,-24.98489,-10.94926], "fy":[51.13945,43.15594,25.81246,34.42998]}, - {"t":2.99332, "x":7.29982, "y":6.77378, "heading":2.20959, "vx":-1.50728, "vy":1.82047, "omega":3.14697, "ax":-4.4446, "ay":-0.62136, "alpha":-4.49946, "fx":[-77.59891,-88.51408,-74.33155,-61.96112], "fy":[11.08433,-2.96705,-31.21577,-19.17822]}, - {"t":3.02019, "x":7.25772, "y":6.82246, "heading":2.29413, "vx":-1.62667, "vy":1.80378, "omega":3.02611, "ax":-5.15237, "ay":-4.13016, "alpha":-1.29119, "fx":[-90.31381,-91.92231,-85.11149,-83.21344], "fy":[-65.03708,-67.07535,-75.28437,-73.61434]}, - {"t":3.04705, "x":7.21217, "y":6.86942, "heading":2.37541, "vx":-1.76507, "vy":1.69284, "omega":2.99143, "ax":-4.01284, "ay":-5.80002, "alpha":2.2537, "fx":[-62.77534,-58.28423,-74.37071,-77.59899], "fy":[-105.28335,-103.79911,-91.64588,-93.8987]}, - {"t":3.07391, "x":7.16331, "y":6.9128, "heading":2.45576, "vx":-1.87286, "vy":1.53704, "omega":3.05197, "ax":-2.47763, "ay":-6.39246, "alpha":4.67782, "fx":[-31.25752,-17.76259,-55.68045,-63.87467], "fy":[-119.64579,-115.63434,-97.09353,-102.56204]}, - {"t":3.10077, "x":7.11211, "y":6.95178, "heading":2.53774, "vx":-1.93941, "vy":1.36533, "omega":3.17762, "ax":-0.2088, "ay":-4.72035, "alpha":4.61789, "fx":[4.57405,19.28603,-12.66291,-25.40369], "fy":[-92.72986,-81.08587,-67.20581,-80.14554]}, - {"t":3.12763, "x":7.05994, "y":6.98675, "heading":2.6231, "vx":-1.94502, "vy":1.23854, "omega":3.30166, "ax":-0.11692, "ay":1.7197, "alpha":-6.78554, "fx":[-14.03234,-30.4289,10.7558,25.7502], "fy":[52.32068,27.7436,4.90994,32.03251]}, - {"t":3.15449, "x":7.00765, "y":7.02064, "heading":2.71179, "vx":-1.94816, "vy":1.28473, "omega":3.11939, "ax":-1.97724, "ay":4.06815, "alpha":-13.1689, "fx":[-58.10893,-93.88248,-9.44989,26.91202], "fy":[101.53781,56.0998,27.88071,91.27409]}, - {"t":3.18135, "x":6.95461, "y":7.05662, "heading":2.79558, "vx":-2.00127, "vy":1.39401, "omega":2.76566, "ax":-4.21539, "ay":3.29224, "alpha":-15.07788, "fx":[-94.92236,-127.2565,-63.97999,-0.65162], "fy":[94.17065,32.60842,-10.54909,107.77016]}, - {"t":3.20821, "x":6.89933, "y":7.09525, "heading":2.86987, "vx":-2.1145, "vy":1.48244, "omega":2.36065, "ax":-6.13567, "ay":1.17467, "alpha":-14.99735, "fx":[-126.58462,-142.97253,-91.43851,-56.46813], "fy":[65.71491,-4.80858,-74.41796,93.4349]}, - {"t":3.23508, "x":6.84032, "y":7.1355, "heading":2.93327, "vx":-2.27931, "vy":1.51399, "omega":1.9578, "ax":-7.03463, "ay":-1.65103, "alpha":-12.82632, "fx":[-145.95267,-144.06329,-77.10149,-111.5107], "fy":[29.60024,-42.7739,-118.20979,19.04938]}, - {"t":3.26194, "x":6.77656, "y":7.17557, "heading":2.98586, "vx":-2.46827, "vy":1.46965, "omega":1.61327, "ax":-7.02775, "ay":-4.03528, "alpha":-10.40001, "fx":[-152.98595,-138.32374,-68.52861,-118.32162], "fy":[-4.37778,-68.34352,-133.85774,-67.97648]}, - {"t":3.2888, "x":6.70772, "y":7.21359, "heading":3.0292, "vx":-2.65705, "vy":1.36125, "omega":1.33392, "ax":-6.69491, "ay":-5.26667, "alpha":-9.39717, "fx":[-152.58501,-131.60302,-64.14301,-107.18296], "fy":[-30.4564,-84.69825,-140.57132,-102.61255]}, - {"t":3.31566, "x":6.63393, "y":7.24825, "heading":3.06503, "vx":-2.83688, "vy":1.21978, "omega":1.0815, "ax":-6.29115, "ay":-6.0171, "alpha":-9.07165, "fx":[-149.63404,-125.22157,-57.58613,-95.60081], "fy":[-47.99185,-96.02172,-145.66716,-119.71583]}, - {"t":3.34252, "x":6.55546, "y":7.27885, "heading":3.09408, "vx":-3.00587, "vy":1.05816, "omega":0.83782, "ax":-5.88092, "ay":-6.52953, "alpha":-9.12834, "fx":[-146.44365,-119.60382,-49.77303,-84.31087], "fy":[-59.58109,-104.14672,-149.78763,-130.74599]}, - {"t":3.36938, "x":6.4726, "y":7.30492, "heading":3.11658, "vx":-3.16384, "vy":0.88277, "omega":0.59262, "ax":-5.47684, "ay":-6.90024, "alpha":-9.46417, "fx":[-143.78908,-114.80598,-41.28335,-72.75974], "fy":[-67.20228,-110.15086,-153.11299,-139.01804]}, - {"t":3.39624, "x":6.38564, "y":7.32614, "heading":3.1325, "vx":-3.31095, "vy":0.69742, "omega":0.3384, "ax":-4.37377, "ay":-7.29488, "alpha":-12.59823, "fx":[-144.41818,-108.84569,-14.22307,-30.09958], "fy":[-66.83953,-116.56849,-158.48291,-154.44405]}, - {"t":3.4231, "x":6.29513, "y":7.34224, "heading":3.14159, "vx":-3.42843, "vy":0.50147, "omega":0.0, "ax":-2.0602, "ay":-9.07529, "alpha":0.0, "fx":[-35.04333,-35.04333,-35.04333,-35.04333], "fy":[-154.36805,-154.36805,-154.36805,-154.36805]}, - {"t":3.45877, "x":6.17155, "y":7.35435, "heading":3.14159, "vx":-3.50191, "vy":0.17781, "omega":0.0, "ax":-0.12929, "ay":-6.97948, "alpha":0.0, "fx":[-2.19922,-2.19922,-2.19922,-2.19922], "fy":[-118.71898,-118.71898,-118.71898,-118.71898]}, - {"t":3.49443, "x":6.04657, "y":7.35626, "heading":3.14159, "vx":-3.50652, "vy":-0.0711, "omega":0.0, "ax":-0.01989, "ay":1.77384, "alpha":0.0, "fx":[-0.33834,-0.33834,-0.33834,-0.33834], "fy":[30.17259,30.17259,30.17259,30.17259]}, - {"t":3.5301, "x":5.9215, "y":7.35485, "heading":3.14159, "vx":-3.50723, "vy":-0.00784, "omega":0.0, "ax":0.00109, "ay":0.7317, "alpha":0.0, "fx":[0.0185,0.0185,0.0185,0.0185], "fy":[12.44598,12.44598,12.44598,12.44598]}, - {"t":3.56576, "x":5.79642, "y":7.35503, "heading":3.14159, "vx":-3.50719, "vy":0.01825, "omega":0.0, "ax":0.00264, "ay":0.37226, "alpha":0.0, "fx":[0.04495,0.04495,0.04495,0.04495], "fy":[6.33207,6.33207,6.33207,6.33207]}, - {"t":3.60142, "x":5.67135, "y":7.35592, "heading":3.14159, "vx":-3.5071, "vy":0.03153, "omega":0.0, "ax":0.00236, "ay":0.23202, "alpha":0.0, "fx":[0.04012,0.04012,0.04012,0.04012], "fy":[3.94667,3.94667,3.94667,3.94667]}, - {"t":3.63709, "x":5.54627, "y":7.35719, "heading":3.14159, "vx":-3.50701, "vy":0.0398, "omega":0.0, "ax":0.00195, "ay":0.16042, "alpha":0.0, "fx":[0.03317,0.03317,0.03317,0.03317], "fy":[2.72871,2.72871,2.72871,2.72871]}, - {"t":3.67275, "x":5.4212, "y":7.35871, "heading":3.14159, "vx":-3.50694, "vy":0.04552, "omega":0.0, "ax":0.00157, "ay":0.11618, "alpha":0.0, "fx":[0.02678,0.02678,0.02678,0.02678], "fy":[1.97611,1.97611,1.97611,1.97611]}, - {"t":3.70841, "x":5.29613, "y":7.36041, "heading":3.14159, "vx":-3.50689, "vy":0.04967, "omega":0.0, "ax":0.00126, "ay":0.08624, "alpha":0.0, "fx":[0.02138,0.02138,0.02138,0.02138], "fy":[1.46691,1.46691,1.46691,1.46691]}, - {"t":3.74408, "x":5.17106, "y":7.36224, "heading":3.14159, "vx":-3.50684, "vy":0.05274, "omega":0.0, "ax":0.001, "ay":0.06535, "alpha":0.0, "fx":[0.01704,0.01704,0.01704,0.01704], "fy":[1.11156,1.11156,1.11156,1.11156]}, - {"t":3.77974, "x":5.046, "y":7.36416, "heading":3.14159, "vx":-3.50681, "vy":0.05507, "omega":0.0, "ax":0.00081, "ay":0.05083, "alpha":0.0, "fx":[0.01375,0.01375,0.01375,0.01375], "fy":[0.86454,0.86454,0.86454,0.86454]}, - {"t":3.8154, "x":4.92093, "y":7.36616, "heading":3.14159, "vx":-3.50678, "vy":0.05689, "omega":0.0, "ax":0.00067, "ay":0.0411, "alpha":0.0, "fx":[0.01144,0.01144,0.01144,0.01144], "fy":[0.69914,0.69914,0.69914,0.69914]}, - {"t":3.85107, "x":4.79587, "y":7.36821, "heading":3.14159, "vx":-3.50675, "vy":0.05835, "omega":0.0, "ax":0.00059, "ay":0.03521, "alpha":0.0, "fx":[0.01002,0.01002,0.01002,0.01002], "fy":[0.59899,0.59899,0.59899,0.59899]}, - {"t":3.88673, "x":4.6708, "y":7.37032, "heading":3.14159, "vx":-3.50673, "vy":0.05961, "omega":0.0, "ax":0.00056, "ay":0.03262, "alpha":0.0, "fx":[0.00947,0.00947,0.00947,0.00947], "fy":[0.55494,0.55494,0.55494,0.55494]}, - {"t":3.9224, "x":4.54574, "y":7.37246, "heading":3.14159, "vx":-3.50671, "vy":0.06077, "omega":0.0, "ax":0.00058, "ay":0.03322, "alpha":0.0, "fx":[0.00983,0.00983,0.00983,0.00983], "fy":[0.56514,0.56514,0.56514,0.56514]}, - {"t":3.95806, "x":4.42068, "y":7.37465, "heading":3.14159, "vx":-3.50669, "vy":0.06196, "omega":0.0, "ax":0.00067, "ay":0.03784, "alpha":0.0, "fx":[0.01144,0.01144,0.01144,0.01144], "fy":[0.6437,0.6437,0.6437,0.6437]}, - {"t":3.99372, "x":4.29562, "y":7.37688, "heading":3.14159, "vx":-3.50667, "vy":0.06331, "omega":0.0, "ax":0.00094, "ay":0.05134, "alpha":0.0, "fx":[0.01593,0.01593,0.01593,0.01593], "fy":[0.87322,0.87322,0.87322,0.87322]}, - {"t":4.02939, "x":4.17056, "y":7.37918, "heading":3.14159, "vx":-3.50663, "vy":0.06514, "omega":0.0, "ax":0.00191, "ay":0.10028, "alpha":0.0, "fx":[0.03249,0.03249,0.03249,0.03249], "fy":[1.70572,1.70572,1.70572,1.70572]}, - {"t":4.06505, "x":4.0455, "y":7.38156, "heading":3.14159, "vx":-3.50657, "vy":0.06871, "omega":0.0, "ax":0.00789, "ay":0.33374, "alpha":0.0, "fx":[0.13414,0.13414,0.13414,0.13414], "fy":[5.67686,5.67686,5.67686,5.67686]}, - {"t":4.10071, "x":3.92045, "y":7.38422, "heading":3.14159, "vx":-3.50629, "vy":0.08062, "omega":0.0, "ax":2.57757, "ay":1.38895, "alpha":0.0, "fx":[43.84367,43.84367,43.84367,43.84367], "fy":[23.62568,23.62568,23.62568,23.62568]}, - {"t":4.13638, "x":3.79704, "y":7.38798, "heading":3.14159, "vx":-3.41436, "vy":0.13015, "omega":0.0, "ax":9.3219, "ay":-0.15494, "alpha":0.0, "fx":[158.56288,158.56288,158.56288,158.56288], "fy":[-2.63556,-2.63556,-2.63556,-2.63556]}, - {"t":4.17204, "x":3.6812, "y":7.39253, "heading":3.14159, "vx":-3.08191, "vy":0.12463, "omega":0.0, "ax":9.37883, "ay":-0.19581, "alpha":0.0, "fx":[159.53119,159.53119,159.53119,159.53119], "fy":[-3.33059,-3.33059,-3.33059,-3.33059]}, - {"t":4.20771, "x":3.57725, "y":7.39685, "heading":3.14159, "vx":-2.74742, "vy":0.11764, "omega":0.0, "ax":9.39688, "ay":-0.23795, "alpha":0.0, "fx":[159.83831,159.83831,159.83831,159.83831], "fy":[-4.04753,-4.04753,-4.04753,-4.04753]}, - {"t":4.24337, "x":3.48524, "y":7.40089, "heading":3.14159, "vx":-2.41229, "vy":0.10916, "omega":0.0, "ax":7.80953, "ay":-5.25662, "alpha":0.0, "fx":[132.8378,132.8378,132.8378,132.8378], "fy":[-89.41358,-89.41358,-89.41358,-89.41358]}, - {"t":4.27903, "x":3.40418, "y":7.40144, "heading":3.14159, "vx":-2.13378, "vy":-0.07831, "omega":0.0, "ax":4.99004, "ay":-7.99588, "alpha":0.0, "fx":[84.87915,84.87915,84.87915,84.87915], "fy":[-136.00764,-136.00764,-136.00764,-136.00764]}, - {"t":4.3147, "x":3.33125, "y":7.39356, "heading":3.14159, "vx":-1.95581, "vy":-0.36348, "omega":0.0, "ax":3.79705, "ay":-8.05209, "alpha":10.2808, "fx":[93.66492,125.25002,24.9577,14.47429], "fy":[-130.11857,-99.97212,-158.14052,-159.62357]}, - {"t":4.33597, "x":3.29051, "y":7.38401, "heading":3.14159, "vx":-1.87505, "vy":-0.53474, "omega":0.21867, "ax":3.84926, "ay":-8.05974, "alpha":9.96388, "fx":[93.50709,124.33487,27.70337,16.35389], "fy":[-130.21345,-101.07862,-157.65939,-159.42387]}, - {"t":4.35724, "x":3.2515, "y":7.37081, "heading":-3.13694, "vx":-1.79318, "vy":-0.70617, "omega":0.43059, "ax":3.90677, "ay":-8.06471, "alpha":9.64683, "fx":[93.50731,123.37286,30.35212,18.57965], "fy":[-130.1917,-102.21654,-157.14582,-159.15944]}, - {"t":4.37851, "x":3.21425, "y":7.35397, "heading":-3.12778, "vx":-1.71009, "vy":-0.8777, "omega":0.63578, "ax":3.97062, "ay":-8.06741, "alpha":9.31585, "fx":[93.64079,122.31985,33.00464,21.19086], "fy":[-130.07034,-103.43474,-156.58272,-158.80957]}, - {"t":4.39977, "x":3.17877, "y":7.33348, "heading":-3.11426, "vx":-1.62563, "vy":-1.04929, "omega":0.83392, "ax":4.04217, "ay":-8.06818, "alpha":8.95357, "fx":[93.87643,121.11685,35.78574,24.24536], "fy":[-129.87011,-104.79453,-155.94013,-158.34495]}, - {"t":4.42104, "x":3.14511, "y":7.30933, "heading":-3.09652, "vx":-1.53966, "vy":-1.2209, "omega":1.02436, "ax":4.12312, "ay":-8.06724, "alpha":8.53762, "fx":[94.17369,119.68427,38.84802,27.82639], "fy":[-129.61809,-106.37325,-155.17123,-157.723]}, - {"t":4.44231, "x":3.11329, "y":7.28154, "heading":-3.07474, "vx":-1.45196, "vy":-1.39248, "omega":1.20595, "ax":4.21556, "ay":-8.06459, "alpha":8.0386, "fx":[94.47734,117.91292,42.3779,32.05404], "fy":[-129.35173,-108.26896,-154.20522,-156.87967]}, - {"t":4.46358, "x":3.08336, "y":7.2501, "heading":-3.04909, "vx":-1.3623, "vy":-1.56401, "omega":1.37693, "ax":4.32206, "ay":-8.05982, "alpha":7.41679, "fx":[94.70854,115.6497,46.60502,37.10459], "fy":[-129.12554,-110.6063,-152.93483,-155.71433]}, - {"t":4.48485, "x":3.05537, "y":7.21501, "heading":-3.0198, "vx":-1.27037, "vy":-1.73544, "omega":1.53468, "ax":4.44574, "ay":-8.05158, "alpha":6.61685, "fx":[94.7481,112.67464,51.81544,43.24478], "fy":[-129.02261,-113.54314,-151.19436,-154.06038]}, - {"t":4.50612, "x":3.02935, "y":7.17628, "heading":-2.98716, "vx":-1.17581, "vy":-1.90669, "omega":1.67542, "ax":4.59045, "ay":-8.03654, "alpha":5.55855, "fx":[94.40304,108.66292,58.36867,50.89436], "fy":[-129.17617,-117.27646,-148.72104,-151.62317]}, - {"t":4.52739, "x":3.00538, "y":7.13391, "heading":-2.95152, "vx":-1.07818, "vy":-2.07763, "omega":1.79364, "ax":4.76067, "ay":-8.0069, "alpha":4.11939, "fx":[93.33209,103.12106,66.71396,60.74335], "fy":[-129.81309,-122.04192,-145.08685,-147.83813]}, - {"t":4.54866, "x":2.98353, "y":7.0879, "heading":-2.91337, "vx":-0.97692, "vy":-2.24793, "omega":1.88126, "ax":4.96028, "ay":-7.94452, "alpha":2.09839, "fx":[90.85693,95.28033,77.3865,73.96835], "fy":[-131.35063,-128.09064,-139.58166,-141.51313]}, - {"t":4.56993, "x":2.96387, "y":7.03829, "heading":-2.87336, "vx":-0.87142, "vy":-2.41691, "omega":1.92589, "ax":5.18499, "ay":-7.80597, "alpha":-0.86786, "fx":[85.39179,83.93119,90.91352,92.54429], "fy":[-134.63302,-135.59457,-131.03701,-129.84466]}, - {"t":4.5912, "x":2.94651, "y":6.98512, "heading":-2.8324, "vx":-0.76113, "vy":-2.58294, "omega":1.90743, "ax":5.37537, "ay":-7.4866, "alpha":-5.50839, "fx":[72.27095,67.27349,107.38014,118.8095], "fy":[-141.49685,-144.33698,-117.7391,-105.80705]}, - {"t":4.61247, "x":2.93154, "y":6.92849, "heading":-2.79183, "vx":-0.6468, "vy":-2.74217, "omega":1.79027, "ax":5.15577, "ay":-6.86697, "alpha":-12.72711, "fx":[36.14089,44.58021,123.62044,146.45148], "fy":[-153.50416,-152.5777,-100.27506,-60.86387]}, - {"t":4.63374, "x":2.91894, "y":6.86861, "heading":-2.75375, "vx":-0.53714, "vy":-2.88823, "omega":1.51957, "ax":4.34005, "ay":-6.9268, "alpha":-15.9571, "fx":[-1.95702,28.32392,121.78555,147.13932], "fy":[-156.75533,-155.97114,-101.77754,-56.78781]}, - {"t":4.65501, "x":2.9085, "y":6.80562, "heading":-2.72143, "vx":-0.44483, "vy":-3.03556, "omega":1.18017, "ax":3.02273, "ay":-7.22791, "alpha":-17.98337, "fx":[-46.54711,4.24029,110.25924,137.71081], "fy":[-148.73082,-157.74234,-112.82813,-72.47725]}, - {"t":4.67628, "x":2.89972, "y":6.73942, "heading":-2.69633, "vx":-0.38054, "vy":-3.18929, "omega":0.79768, "ax":0.41097, "ay":-7.2323, "alpha":-19.21302, "fx":[-111.34425,-45.5486,81.85979,102.99517], "fy":[-107.98902,-149.88438,-132.01217,-102.19181]}, - {"t":4.69755, "x":2.89172, "y":6.66994, "heading":-2.67936, "vx":-0.3718, "vy":-3.34312, "omega":0.38903, "ax":-7.22036, "ay":-3.42165, "alpha":-11.72617, "fx":[-151.9614,-121.4724,-71.95385,-145.87765], "fy":[-34.4087,-97.53053,-129.57461,28.70874]}, - {"t":4.71882, "x":2.88218, "y":6.59806, "heading":-2.67108, "vx":-0.52537, "vy":-3.4159, "omega":0.13962, "ax":-8.95259, "ay":0.03448, "alpha":-4.29475, "fx":[-155.86206,-152.0135,-152.38416,-148.86397], "fy":[3.43187,-32.54766,-8.12556,39.58715]}, - {"t":4.74437, "x":2.86583, "y":6.51078, "heading":-2.66752, "vx":-0.75417, "vy":-3.41502, "omega":0.02985, "ax":-8.64353, "ay":1.97867, "alpha":-0.60162, "fx":[-147.46063,-148.1164,-146.64969,-145.8694], "fy":[33.22453,29.12458,34.07892,38.19838]}, - {"t":4.76993, "x":2.84373, "y":6.42414, "heading":-2.66675, "vx":-0.97508, "vy":-3.36445, "omega":0.01448, "ax":-8.00833, "ay":2.57139, "alpha":-0.05456, "fx":[-136.28505,-136.34639,-136.15419,-136.0922], "fy":[43.68068,43.37086,43.79658,44.10619]}, - {"t":4.79549, "x":2.8162, "y":6.339, "heading":-2.66638, "vx":-1.17974, "vy":-3.29873, "omega":0.01308, "ax":-6.69503, "ay":2.98176, "alpha":0.90851, "fx":[-112.00008,-111.48845,-115.78027,-116.25306], "fy":[51.72051,55.76452,49.7679,45.62273]}, - {"t":4.82105, "x":2.78386, "y":6.25567, "heading":-2.66605, "vx":-1.35085, "vy":-3.22252, "omega":0.0363, "ax":0.68189, "ay":6.96018, "alpha":16.64783, "fx":[113.95557,53.34758,-67.63631,-53.27199], "fy":[96.33976,142.17425,131.40563,103.64337]}, - {"t":4.8466, "x":2.74956, "y":6.17558, "heading":-2.66512, "vx":-1.33342, "vy":-3.04464, "omega":0.46177, "ax":1.44967, "ay":7.0003, "alpha":17.35779, "fx":[132.77926,68.75846,-68.40074,-34.50294], "fy":[82.76662,141.42373,139.19819,112.90377]}, - {"t":4.87216, "x":2.71595, "y":6.10006, "heading":-2.65332, "vx":-1.29637, "vy":-2.86574, "omega":0.90539, "ax":1.63357, "ay":6.99327, "alpha":17.43316, "fx":[136.11874,70.8478,-71.2049,-24.61573], "fy":[80.3726,141.81397,140.03203,113.5955]}, - {"t":4.89772, "x":2.68336, "y":6.0291, "heading":-2.63018, "vx":-1.25462, "vy":-2.68701, "omega":1.35093, "ax":2.50595, "ay":7.24591, "alpha":14.8674, "fx":[135.46923,70.11509,-69.82543,34.74308], "fy":[82.80861,142.79556,141.72105,125.67832]}, - {"t":4.92327, "x":2.65211, "y":5.96279, "heading":-2.59565, "vx":-1.19058, "vy":-2.50182, "omega":1.73089, "ax":3.94436, "ay":8.34703, "alpha":3.96632, "fx":[95.91484,68.75907,33.8484,69.84763], "fy":[126.92642,143.62209,155.1653,142.20822]}, - {"t":4.94883, "x":2.62297, "y":5.90158, "heading":-2.55142, "vx":-1.08977, "vy":-2.2885, "omega":1.83226, "ax":4.07863, "ay":8.43177, "alpha":-0.85539, "fx":[62.43481,69.96898,76.06335,69.03846], "fy":[146.64963,143.16685,140.12368,143.74771]}, - {"t":4.97439, "x":2.59645, "y":5.84585, "heading":-2.50459, "vx":-0.98554, "vy":-2.07301, "omega":1.8104, "ax":4.00699, "ay":8.39873, "alpha":-3.27789, "fx":[39.55825,73.7378,92.41512,66.91985], "fy":[154.61655,141.37426,130.31613,145.13281]}, - {"t":4.99994, "x":2.57257, "y":5.79561, "heading":-2.45832, "vx":-0.88313, "vy":-1.85836, "omega":1.72663, "ax":3.93217, "ay":8.34388, "alpha":-4.70786, "fx":[23.85381,79.36975,99.8796,64.43733], "fy":[157.94996,138.39151,124.9223,146.44461]}, - {"t":5.0255, "x":2.55129, "y":5.75084, "heading":-2.4142, "vx":-0.78263, "vy":-1.64512, "omega":1.60631, "ax":3.88237, "ay":8.28095, "alpha":-5.69259, "fx":[12.59607,86.07151,103.60778,61.87675], "fy":[159.34619,134.42012,122.00055,147.65961]}, - {"t":5.05106, "x":2.53255, "y":5.7115, "heading":-2.37314, "vx":-0.68341, "vy":-1.43348, "omega":1.46082, "ax":3.85355, "ay":8.21306, "alpha":-6.46614, "fx":[4.17641,93.1287,105.51254,59.37376], "fy":[159.86416,129.72089,120.46257,148.75974]}, - {"t":5.07662, "x":2.51635, "y":5.67755, "heading":-2.33581, "vx":-0.58493, "vy":-1.22358, "omega":1.29557, "ax":3.83743, "ay":8.14255, "alpha":-7.13106, "fx":[-2.34592,99.98144,106.44925,57.00968], "fy":[159.9594,124.60264,119.71229,149.73561]}, - {"t":5.10217, "x":2.50265, "y":5.64894, "heading":-2.3027, "vx":-0.48685, "vy":-1.01548, "omega":1.11332, "ax":3.8273, "ay":8.07209, "alpha":-7.72884, "fx":[-7.5482,106.25204,106.85938,54.84162], "fy":[159.84457,119.38364,119.40412,150.58343]}, - {"t":5.12773, "x":2.49146, "y":5.62562, "heading":-2.27424, "vx":-0.38904, "vy":-0.80918, "omega":0.91579, "ax":3.81898, "ay":8.00425, "alpha":-8.27283, "fx":[-11.80728,111.73359,106.99825,52.91433], "fy":[159.62459,114.34809,119.32456,151.30262]}, - {"t":5.15329, "x":2.48276, "y":5.60755, "heading":-2.25084, "vx":-0.29144, "vy":-0.60461, "omega":0.70436, "ax":3.81048, "ay":7.94102, "alpha":-8.7651, "fx":[-15.38432,116.352,107.0276,51.26512], "fy":[159.35213,109.71794,119.33391,151.89424]}, - {"t":5.17884, "x":2.47656, "y":5.59469, "heading":-2.23284, "vx":-0.19405, "vy":-0.40167, "omega":0.48035, "ax":3.80126, "ay":7.88367, "alpha":-9.20465, "fx":[-18.46859,120.11871,107.05742,49.92612], "fy":[159.0528,105.64669,119.3361,152.35997]}, - {"t":5.2044, "x":2.47284, "y":5.587, "heading":-2.22056, "vx":-0.0969, "vy":-0.20018, "omega":0.24511, "ax":3.79165, "ay":7.83276, "alpha":-9.59063, "fx":[-21.19963,123.08775,107.16634,48.92526], "fy":[158.73779,102.23053,119.26217,152.70133]}, - {"t":5.22996, "x":2.4716, "y":5.58445, "heading":-2.2143, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0,0.0,0.0,0.0]}], + {"t":0.6514, "x":6.10055, "y":0.3629, "heading":1.5708, "vx":3.50734, "vy":-0.07609, "omega":0.0, "ax":-0.01187, "ay":-0.47766, "alpha":0.0, "fx":[-0.20185,-0.20185,-0.20185,-0.20185], "fy":[-8.12484,-8.12484,-8.12484,-8.12484]}, + {"t":0.69793, "x":6.26373, "y":0.35884, "heading":1.5708, "vx":3.50679, "vy":-0.09831, "omega":0.0, "ax":-0.01634, "ay":-0.51975, "alpha":0.0, "fx":[-0.27802,-0.27802,-0.27802,-0.27802], "fy":[-8.84084,-8.84084,-8.84084,-8.84084]}, + {"t":0.74445, "x":6.42688, "y":0.3537, "heading":1.5708, "vx":3.50603, "vy":-0.12249, "omega":0.0, "ax":0.04561, "ay":2.55866, "alpha":0.0, "fx":[0.7759,0.7759,0.7759,0.7759], "fy":[43.52206,43.52206,43.52206,43.52206]}, + {"t":0.79098, "x":6.59006, "y":0.35077, "heading":1.5708, "vx":3.50815, "vy":-0.00344, "omega":0.0, "ax":-0.48439, "ay":8.58618, "alpha":0.0, "fx":[-8.23942,-8.23942,-8.23942,-8.23942], "fy":[146.0485,146.0485,146.0485,146.0485]}, + {"t":0.83751, "x":6.75276, "y":0.35991, "heading":1.5708, "vx":3.48561, "vy":0.39606, "omega":0.0, "ax":-1.60219, "ay":9.07077, "alpha":0.0, "fx":[-27.25277,-27.25277,-27.25277,-27.25277], "fy":[154.29114,154.29114,154.29114,154.29114]}, + {"t":0.88404, "x":6.91321, "y":0.38815, "heading":1.5708, "vx":3.41107, "vy":0.81811, "omega":0.0, "ax":-2.74327, "ay":8.92021, "alpha":0.0, "fx":[-46.66228,-46.66228,-46.66228,-46.66228], "fy":[151.7302,151.7302,151.7302,151.7302]}, + {"t":0.93057, "x":7.06895, "y":0.43587, "heading":1.5708, "vx":3.28342, "vy":1.23315, "omega":0.0, "ax":-3.89095, "ay":8.53115, "alpha":0.0, "fx":[-66.18396,-66.18396,-66.18396,-66.18396], "fy":[145.1124,145.1124,145.1124,145.1124]}, + {"t":0.9771, "x":7.21751, "y":0.50249, "heading":1.5708, "vx":3.10239, "vy":1.63009, "omega":0.0, "ax":-5.89873, "ay":7.31638, "alpha":0.0, "fx":[-100.33568,-100.33568,-100.33568,-100.33568], "fy":[124.44954,124.44954,124.44954,124.44954]}, + {"t":1.02362, "x":7.35547, "y":0.58625, "heading":1.5708, "vx":2.82793, "vy":1.97051, "omega":0.0, "ax":-7.67474, "ay":5.44637, "alpha":0.0, "fx":[-130.54513,-130.54513,-130.54513,-130.54513], "fy":[92.64127,92.64127,92.64127,92.64127]}, + {"t":1.07015, "x":7.47874, "y":0.68383, "heading":1.5708, "vx":2.47083, "vy":2.22392, "omega":0.0, "ax":-8.37739, "ay":4.30333, "alpha":0.0, "fx":[-142.49704,-142.49704,-142.49704,-142.49704], "fy":[73.19837,73.19837,73.19837,73.19837]}, + {"t":1.11668, "x":7.58464, "y":0.79196, "heading":1.5708, "vx":2.08105, "vy":2.42415, "omega":0.0, "ax":-8.70673, "ay":3.60174, "alpha":0.0, "fx":[-148.09901,-148.09901,-148.09901,-148.09901], "fy":[61.26462,61.26462,61.26462,61.26462]}, + {"t":1.16321, "x":7.67204, "y":0.90865, "heading":1.5708, "vx":1.67594, "vy":2.59173, "omega":0.0, "ax":-8.88695, "ay":3.13913, "alpha":0.0, "fx":[-151.16445,-151.16445,-151.16445,-151.16445], "fy":[53.39578,53.39578,53.39578,53.39578]}, + {"t":1.20974, "x":7.7404, "y":1.03264, "heading":1.5708, "vx":1.26244, "vy":2.73779, "omega":0.0, "ax":-8.99705, "ay":2.81452, "alpha":0.0, "fx":[-153.03725,-153.03725,-153.03725,-153.03725], "fy":[47.87421,47.87421,47.87421,47.87421]}, + {"t":1.25627, "x":7.7894, "y":1.16307, "heading":1.5708, "vx":0.84382, "vy":2.86875, "omega":0.0, "ax":-9.06982, "ay":2.57557, "alpha":0.0, "fx":[-154.27499,-154.27499,-154.27499,-154.27499], "fy":[43.80978,43.80978,43.80978,43.80978]}, + {"t":1.3028, "x":7.81885, "y":1.29934, "heading":1.5708, "vx":0.42182, "vy":2.98858, "omega":0.0, "ax":-8.78239, "ay":3.40672, "alpha":0.0, "fx":[-149.38595,-149.38595,-149.38595,-149.38595], "fy":[57.94739,57.94739,57.94739,57.94739]}, + {"t":1.34146, "x":7.82859, "y":1.41743, "heading":1.5708, "vx":0.08228, "vy":3.12029, "omega":0.0, "ax":-4.65323, "ay":8.11334, "alpha":0.0, "fx":[-79.15005,-79.15005,-79.15005,-79.15005], "fy":[138.00553,138.00553,138.00553,138.00553]}, + {"t":1.38012, "x":7.8283, "y":1.54413, "heading":1.5708, "vx":-0.09762, "vy":3.43396, "omega":0.0, "ax":0.63276, "ay":1.88417, "alpha":0.0, "fx":[10.76311,10.76311,10.76311,10.76311], "fy":[32.04921,32.04921,32.04921,32.04921]}, + {"t":1.41878, "x":7.82499, "y":1.6783, "heading":1.5708, "vx":-0.07316, "vy":3.50681, "omega":0.0, "ax":0.59825, "ay":0.01087, "alpha":0.0, "fx":[10.17605,10.17605,10.17605,10.17605], "fy":[0.18497,0.18497,0.18497,0.18497]}, + {"t":1.45744, "x":7.82261, "y":1.81388, "heading":1.5708, "vx":-0.05003, "vy":3.50723, "omega":0.0, "ax":0.3492, "ay":0.0043, "alpha":0.0, "fx":[5.93976,5.93976,5.93976,5.93976], "fy":[0.07314,0.07314,0.07314,0.07314]}, + {"t":1.4961, "x":7.82094, "y":1.94948, "heading":1.5708, "vx":-0.03653, "vy":3.50739, "omega":0.0, "ax":0.22063, "ay":0.00202, "alpha":0.0, "fx":[3.75278,3.75278,3.75278,3.75278], "fy":[0.0344,0.0344,0.0344,0.0344]}, + {"t":1.53476, "x":7.81969, "y":2.08508, "heading":1.5708, "vx":-0.028, "vy":3.50747, "omega":0.0, "ax":0.14965, "ay":0.00107, "alpha":0.0, "fx":[2.54555,2.54555,2.54555,2.54555], "fy":[0.01812,0.01812,0.01812,0.01812]}, + {"t":1.57342, "x":7.81872, "y":2.22068, "heading":1.5708, "vx":-0.02221, "vy":3.50751, "omega":0.0, "ax":0.10549, "ay":0.0006, "alpha":0.0, "fx":[1.79439,1.79439,1.79439,1.79439], "fy":[0.01024,0.01024,0.01024,0.01024]}, + {"t":1.61209, "x":7.81794, "y":2.35629, "heading":1.5708, "vx":-0.01813, "vy":3.50754, "omega":0.0, "ax":0.07583, "ay":0.00036, "alpha":0.0, "fx":[1.28985,1.28985,1.28985,1.28985], "fy":[0.00606,0.00606,0.00606,0.00606]}, + {"t":1.65075, "x":7.8173, "y":2.4919, "heading":1.5708, "vx":-0.0152, "vy":3.50755, "omega":0.0, "ax":0.05511, "ay":0.00022, "alpha":0.0, "fx":[0.93734,0.93734,0.93734,0.93734], "fy":[0.00372,0.00372,0.00372,0.00372]}, + {"t":1.68941, "x":7.81675, "y":2.6275, "heading":1.5708, "vx":-0.01307, "vy":3.50756, "omega":0.0, "ax":0.04036, "ay":0.00014, "alpha":0.0, "fx":[0.68653,0.68653,0.68653,0.68653], "fy":[0.00235,0.00235,0.00235,0.00235]}, + {"t":1.72807, "x":7.81628, "y":2.76311, "heading":1.5708, "vx":-0.01151, "vy":3.50756, "omega":0.0, "ax":0.02981, "ay":0.00009, "alpha":0.0, "fx":[0.50702,0.50702,0.50702,0.50702], "fy":[0.00153,0.00153,0.00153,0.00153]}, + {"t":1.76673, "x":7.81585, "y":2.89872, "heading":1.5708, "vx":-0.01036, "vy":3.50757, "omega":0.0, "ax":0.02228, "ay":0.00006, "alpha":0.0, "fx":[0.37896,0.37896,0.37896,0.37896], "fy":[0.00103,0.00103,0.00103,0.00103]}, + {"t":1.80539, "x":7.81547, "y":3.03432, "heading":1.5708, "vx":-0.0095, "vy":3.50757, "omega":0.0, "ax":0.01698, "ay":0.00004, "alpha":0.0, "fx":[0.2889,0.2889,0.2889,0.2889], "fy":[0.00072,0.00072,0.00072,0.00072]}, + {"t":1.84405, "x":7.81511, "y":3.16993, "heading":1.5708, "vx":-0.00884, "vy":3.50757, "omega":0.0, "ax":0.01338, "ay":0.00003, "alpha":0.0, "fx":[0.22762,0.22762,0.22762,0.22762], "fy":[0.00053,0.00053,0.00053,0.00053]}, + {"t":1.88271, "x":7.81478, "y":3.30554, "heading":1.5708, "vx":-0.00832, "vy":3.50757, "omega":0.0, "ax":0.01111, "ay":0.00002, "alpha":0.0, "fx":[0.18893,0.18893,0.18893,0.18893], "fy":[0.00041,0.00041,0.00041,0.00041]}, + {"t":1.92138, "x":7.81447, "y":3.44114, "heading":1.5708, "vx":-0.00789, "vy":3.50757, "omega":0.0, "ax":0.00993, "ay":0.00002, "alpha":0.0, "fx":[0.16897,0.16897,0.16897,0.16897], "fy":[0.00035,0.00035,0.00035,0.00035]}, + {"t":1.96004, "x":7.81417, "y":3.57675, "heading":1.5708, "vx":-0.00751, "vy":3.50758, "omega":0.0, "ax":0.00974, "ay":0.00002, "alpha":0.0, "fx":[0.16574,0.16574,0.16574,0.16574], "fy":[0.00033,0.00033,0.00033,0.00033]}, + {"t":1.9987, "x":7.81389, "y":3.71236, "heading":1.5708, "vx":-0.00713, "vy":3.50758, "omega":0.0, "ax":0.01052, "ay":0.00002, "alpha":0.0, "fx":[0.17893,0.17893,0.17893,0.17893], "fy":[0.00034,0.00034,0.00034,0.00034]}, + {"t":2.03736, "x":7.81362, "y":3.84797, "heading":1.5708, "vx":-0.00673, "vy":3.50758, "omega":0.0, "ax":0.01234, "ay":0.00002, "alpha":0.0, "fx":[0.20985,0.20985,0.20985,0.20985], "fy":[0.00038,0.00038,0.00038,0.00038]}, + {"t":2.07602, "x":7.81337, "y":3.98357, "heading":1.5708, "vx":-0.00625, "vy":3.50758, "omega":0.0, "ax":0.01538, "ay":0.00003, "alpha":0.0, "fx":[0.26158,0.26158,0.26158,0.26158], "fy":[0.00044,0.00044,0.00044,0.00044]}, + {"t":2.11468, "x":7.81314, "y":4.11918, "heading":1.5708, "vx":-0.00565, "vy":3.50758, "omega":0.0, "ax":0.01995, "ay":0.00003, "alpha":0.0, "fx":[0.33934,0.33934,0.33934,0.33934], "fy":[0.00052,0.00052,0.00052,0.00052]}, + {"t":2.15334, "x":7.81294, "y":4.25479, "heading":1.5708, "vx":-0.00488, "vy":3.50758, "omega":0.0, "ax":0.02652, "ay":0.00003, "alpha":0.0, "fx":[0.45103,0.45103,0.45103,0.45103], "fy":[0.00057,0.00057,0.00057,0.00057]}, + {"t":2.192, "x":7.81277, "y":4.3904, "heading":1.5708, "vx":-0.00386, "vy":3.50758, "omega":0.0, "ax":0.03576, "ay":0.00003, "alpha":0.0, "fx":[0.60824,0.60824,0.60824,0.60824], "fy":[0.00056,0.00056,0.00056,0.00056]}, + {"t":2.23067, "x":7.81264, "y":4.526, "heading":1.5708, "vx":-0.00248, "vy":3.50758, "omega":0.0, "ax":0.04868, "ay":0.00002, "alpha":0.0, "fx":[0.82797,0.82797,0.82797,0.82797], "fy":[0.00038,0.00038,0.00038,0.00038]}, + {"t":2.26933, "x":7.81259, "y":4.66161, "heading":1.5708, "vx":-0.00059, "vy":3.50758, "omega":0.0, "ax":0.06678, "ay":-0.00001, "alpha":0.0, "fx":[1.13583,1.13583,1.13583,1.13583], "fy":[-0.0002,-0.0002,-0.0002,-0.0002]}, + {"t":2.30799, "x":7.81261, "y":4.79722, "heading":1.5708, "vx":0.00199, "vy":3.50758, "omega":0.0, "ax":0.09248, "ay":-0.0001, "alpha":0.0, "fx":[1.57305,1.57305,1.57305,1.57305], "fy":[-0.00166,-0.00166,-0.00166,-0.00166]}, + {"t":2.34665, "x":7.81276, "y":4.93283, "heading":1.5708, "vx":0.00556, "vy":3.50758, "omega":0.0, "ax":0.13025, "ay":-0.0003, "alpha":0.0, "fx":[2.21548,2.21548,2.21548,2.21548], "fy":[-0.00506,-0.00506,-0.00506,-0.00506]}, + {"t":2.38531, "x":7.81307, "y":5.06843, "heading":1.5708, "vx":0.0106, "vy":3.50757, "omega":0.0, "ax":0.19028, "ay":-0.00077, "alpha":0.0, "fx":[3.23655,3.23655,3.23655,3.23655], "fy":[-0.01312,-0.01312,-0.01312,-0.01312]}, + {"t":2.42397, "x":7.81362, "y":5.20404, "heading":1.5708, "vx":0.01796, "vy":3.50754, "omega":0.0, "ax":0.3032, "ay":-0.00205, "alpha":0.0, "fx":[5.15727,5.15727,5.15727,5.15727], "fy":[-0.03494,-0.03494,-0.03494,-0.03494]}, + {"t":2.46263, "x":7.81454, "y":5.33965, "heading":1.5708, "vx":0.02968, "vy":3.50746, "omega":0.0, "ax":0.58165, "ay":-0.00678, "alpha":0.0, "fx":[9.89364,9.89364,9.89364,9.89364], "fy":[-0.11531,-0.11531,-0.11531,-0.11531]}, + {"t":2.50129, "x":7.81613, "y":5.47524, "heading":1.5708, "vx":0.05216, "vy":3.5072, "omega":0.0, "ax":1.38937, "ay":-0.03127, "alpha":0.0, "fx":[23.63274,23.63274,23.63274,23.63274], "fy":[-0.53184,-0.53184,-0.53184,-0.53184]}, + {"t":2.53996, "x":7.81918, "y":5.61081, "heading":1.5708, "vx":0.10588, "vy":3.50599, "omega":0.0, "ax":-5.1819, "ay":-0.04034, "alpha":0.0, "fx":[-88.14269,-88.14269,-88.14269,-88.14269], "fy":[-0.68618,-0.68618,-0.68618,-0.68618]}, + {"t":2.57862, "x":7.8194, "y":5.74633, "heading":1.5708, "vx":-0.09446, "vy":3.50443, "omega":0.0, "ax":-8.84592, "ay":-3.02905, "alpha":0.0, "fx":[-150.4665,-150.4665,-150.4665,-150.4665], "fy":[-51.52329,-51.52329,-51.52329,-51.52329]}, + {"t":2.61728, "x":7.80914, "y":5.87955, "heading":1.5708, "vx":-0.43645, "vy":3.38732, "omega":0.0, "ax":-7.38583, "ay":-3.48425, "alpha":15.53781, "fx":[-120.19807,-64.95595,-157.82741,-159.54233], "fy":[-105.33653,-145.73097,10.03887,3.96394]}, + {"t":2.64414, "x":7.79475, "y":5.96928, "heading":1.5764, "vx":-0.63483, "vy":3.29374, "omega":0.41734, "ax":-7.0873, "ay":-5.32988, "alpha":9.32822, "fx":[-113.50802,-72.33816,-141.91699,-154.44828], "fy":[-112.15635,-141.94347,-69.80694,-38.73236]}, + {"t":2.671, "x":7.77514, "y":6.05582, "heading":1.59098, "vx":-0.8252, "vy":3.15058, "omega":0.66789, "ax":-6.82651, "ay":-5.70443, "alpha":8.7187, "fx":[-109.40654,-68.55179,-135.03217,-151.47769], "fy":[-115.76829,-143.43339,-81.15075,-47.77024]}, + {"t":2.69786, "x":7.75052, "y":6.13839, "heading":1.61206, "vx":-1.00855, "vy":2.99736, "omega":0.90207, "ax":-6.49647, "ay":-6.07386, "alpha":8.29655, "fx":[-104.18259,-62.83829,-127.18475,-147.80665], "fy":[-119.93351,-145.47938,-91.35054,-56.49536]}, + {"t":2.72472, "x":7.72108, "y":6.2167, "heading":1.63928, "vx":-1.18305, "vy":2.83422, "omega":1.12491, "ax":-6.06375, "ay":-6.4497, "alpha":8.04851, "fx":[-97.37627,-54.49614,-117.47206,-143.22584], "fy":[-124.69346,-147.97523,-101.15519,-65.00654]}, + {"t":2.75158, "x":7.68712, "y":6.2905, "heading":1.6724, "vx":-1.34592, "vy":2.66098, "omega":1.34109, "ax":-5.46939, "ay":-6.82353, "alpha":8.05476, "fx":[-88.23254,-42.04448,-104.27039,-137.58389], "fy":[-130.02439,-150.65823,-110.78227,-72.80015]}, + {"t":2.77844, "x":7.649, "y":6.35951, "heading":1.71133, "vx":-1.49282, "vy":2.4777, "omega":1.55744, "ax":-4.58463, "ay":-7.15922, "alpha":8.4963, "fx":[-75.37704,-22.93522,-83.07315,-130.54777], "fy":[-135.72016,-152.48976,-119.97834,-78.91716]}, + {"t":2.8053, "x":7.60725, "y":6.42348, "heading":1.75622, "vx":-1.61596, "vy":2.28541, "omega":1.78565, "ax":-3.06231, "ay":-7.31898, "alpha":9.82657, "fx":[-56.1515,5.95758,-38.05665,-120.10564], "fy":[-140.91715,-150.1136,-124.39242,-82.55177]}, + {"t":2.83216, "x":7.56274, "y":6.48223, "heading":1.80773, "vx":-1.69821, "vy":2.08883, "omega":2.04958, "ax":-0.30962, "ay":-6.78928, "alpha":12.52764, "fx":[-24.31341,43.23877,50.78338,-90.7746], "fy":[-142.36912,-136.44403,-93.02722,-90.09425]}, + {"t":2.85901, "x":7.51701, "y":6.53588, "heading":1.8673, "vx":-1.70653, "vy":1.90647, "omega":2.38607, "ax":2.49314, "ay":-4.87719, "alpha":13.91747, "fx":[18.10715,79.81035,97.06411,-25.35088], "fy":[-129.20684,-104.91004,-23.65675,-74.06512]}, + {"t":2.88587, "x":7.47208, "y":6.58533, "heading":1.93641, "vx":-1.63957, "vy":1.77547, "omega":2.75989, "ax":4.04766, "ay":-2.03041, "alpha":12.50158, "fx":[53.01424,97.61169,91.71291,33.05912], "fy":[-91.93948,-60.22748,21.37466,-7.35431]}, + {"t":2.91273, "x":7.4295, "y":6.63229, "heading":2.01505, "vx":-1.53085, "vy":1.72093, "omega":3.09568, "ax":3.39076, "ay":0.52221, "alpha":8.08535, "fx":[55.7522,81.08537,61.64682,32.21909], "fy":[-27.47557,-8.5511,41.68217,29.87504]}, + {"t":2.93959, "x":7.3896, "y":6.6787, "heading":2.10111, "vx":-1.43977, "vy":1.73496, "omega":3.31284, "ax":-0.25261, "ay":2.03902, "alpha":-0.50232, "fx":[-4.13822,-6.22476,-4.46211,-2.362], "fy":[36.63982,35.51202,32.71891,33.86185]}, + {"t":2.96645, "x":7.35084, "y":6.72603, "heading":2.18991, "vx":-1.44656, "vy":1.78973, "omega":3.29935, "ax":-4.53444, "ay":0.11255, "alpha":-5.6855, "fx":[-77.75014,-92.82925,-77.58666,-60.35239], "fy":[29.87132,10.97405,-25.05739,-8.13035]}, + {"t":2.99331, "x":7.31035, "y":6.77415, "heading":2.27648, "vx":-1.56835, "vy":1.79275, "omega":3.14664, "ax":-5.88881, "ay":-3.15248, "alpha":-4.11555, "fx":[-107.59178,-111.22143,-93.70054,-88.15418], "fy":[-33.91366,-44.00594,-71.30582,-65.26595]}, + {"t":3.02017, "x":7.2661, "y":6.82116, "heading":2.35951, "vx":-1.72652, "vy":1.70808, "omega":3.0361, "ax":-5.04837, "ay":-4.96167, "alpha":-0.97202, "fx":[-88.33658,-89.32631,-83.5059,-82.31639], "fy":[-80.77044,-82.03931,-87.92033,-86.8565]}, + {"t":3.04703, "x":7.21791, "y":6.86525, "heading":2.44071, "vx":-1.86212, "vy":1.57481, "omega":3.00999, "ax":-3.38115, "ay":-5.92729, "alpha":2.36948, "fx":[-51.5483,-46.57065,-64.17346,-67.75698], "fy":[-107.37514,-105.24095,-93.91711,-96.75295]}, + {"t":3.07389, "x":7.16667, "y":6.90541, "heading":2.52241, "vx":-1.95294, "vy":1.4156, "omega":3.07364, "ax":-0.64317, "ay":-4.89574, "alpha":4.3182, "fx":[-3.01568,10.46556,-19.97357,-31.23669], "fy":[-94.84241,-84.97393,-71.14201,-82.14201]}, + {"t":3.10075, "x":7.11398, "y":6.94167, "heading":2.60653, "vx":-1.97021, "vy":1.2841, "omega":3.18962, "ax":1.26371, "ay":-0.32201, "alpha":-0.46346, "fx":[20.72084,19.64056,22.27377,23.34602], "fy":[-3.74813,-5.61064,-7.20256,-5.34806]}, + {"t":3.12761, "x":7.06152, "y":6.97604, "heading":2.69203, "vx":-1.93627, "vy":1.27546, "omega":3.17717, "ax":-0.12735, "ay":3.47091, "alpha":-8.80367, "fx":[-20.32052,-42.33567,19.26659,34.72517], "fy":[83.73244,55.56214,31.72771,65.13416]}, + {"t":3.15447, "x":7.00947, "y":7.01155, "heading":2.77419, "vx":-1.93969, "vy":1.36868, "omega":2.94071, "ax":-2.23223, "ay":4.08563, "alpha":-12.69697, "fx":[-63.485,-95.97831,-12.08931,19.67439], "fy":[98.54407,52.6403,32.62224,94.17483]}, + {"t":3.18133, "x":6.95656, "y":7.04979, "heading":2.8486, "vx":-1.99965, "vy":1.47842, "omega":2.59967, "ax":-4.56106, "ay":2.85016, "alpha":-14.34976, "fx":[-101.14603,-127.91188,-67.31418,-13.9574], "fy":[84.83599,23.0508,-16.39678,102.43142]}, + {"t":3.20819, "x":6.90121, "y":7.09052, "heading":2.91325, "vx":-2.12215, "vy":1.55497, "omega":2.21425, "ax":-6.36885, "ay":0.47403, "alpha":-13.94679, "fx":[-131.27508,-141.61625,-88.20994,-72.22776], "fy":[53.36143,-15.92055,-81.28143,76.09272]}, + {"t":3.23505, "x":6.84191, "y":7.13246, "heading":2.96769, "vx":-2.29322, "vy":1.56771, "omega":1.83964, "ax":-7.06567, "ay":-2.42795, "alpha":-11.51968, "fx":[-147.68984,-140.98494,-75.16485,-116.90023], "fy":[16.6462,-51.6686,-120.36513,-9.80722]}, + {"t":3.26191, "x":6.77777, "y":7.17369, "heading":3.01295, "vx":-2.483, "vy":1.50249, "omega":1.53023, "ax":-6.95173, "ay":-4.452, "alpha":-9.54249, "fx":[-152.11657,-135.07797,-69.90941,-115.88351], "fy":[-16.29921,-74.47252,-133.48186,-78.6557]}, + {"t":3.28877, "x":6.70857, "y":7.21244, "heading":3.05061, "vx":-2.66972, "vy":1.38291, "omega":1.27392, "ax":-6.59682, "ay":-5.53151, "alpha":-8.76255, "fx":[-150.37084,-128.50097,-65.02713,-104.9413], "fy":[-40.00326,-89.32562,-140.32629,-106.70272]}, + {"t":3.31563, "x":6.63448, "y":7.24759, "heading":3.08167, "vx":-2.84691, "vy":1.23434, "omega":1.03856, "ax":-6.1933, "ay":-6.20887, "alpha":-8.56512, "fx":[-147.02874,-122.41564,-58.07881,-93.86188], "fy":[-55.50368,-99.57746,-145.55502,-121.80815]}, + {"t":3.34249, "x":6.55578, "y":7.27851, "heading":3.10647, "vx":-3.01326, "vy":1.06757, "omega":0.80851, "ax":-5.79087, "ay":-6.67556, "alpha":-8.71778, "fx":[-143.8808,-117.14262,-50.04784,-82.93281], "fy":[-65.5557,-106.91183,-149.74307,-131.98655]}, + {"t":3.36935, "x":6.47276, "y":7.30477, "heading":3.12504, "vx":-3.1688, "vy":0.88827, "omega":0.57435, "ax":-5.39696, "ay":-7.01392, "alpha":-9.1349, "fx":[-141.4602,-112.7034,-41.4232,-71.61619], "fy":[-71.99198,-112.30517,-153.10413,-139.81775]}, + {"t":3.39621, "x":6.3857, "y":7.3261, "heading":3.13717, "vx":-3.31376, "vy":0.69988, "omega":0.32899, "ax":-4.33726, "ay":-7.37631, "alpha":-12.24858, "fx":[-142.51756,-107.14631,-14.64403,-30.79452], "fy":[-70.81032,-118.13573,-158.46124,-154.46841]}, + {"t":3.42307, "x":6.29513, "y":7.34224, "heading":3.14159, "vx":-3.43025, "vy":0.50175, "omega":0.0, "ax":-2.00973, "ay":-9.08916, "alpha":0.0, "fx":[-34.185,-34.185,-34.185,-34.185], "fy":[-154.60403,-154.60403,-154.60403,-154.60403]}, + {"t":3.45873, "x":6.17151, "y":7.35435, "heading":3.14159, "vx":-3.50193, "vy":0.1776, "omega":0.0, "ax":-0.12884, "ay":-6.97082, "alpha":0.0, "fx":[-2.19159,-2.19159,-2.19159,-2.19159], "fy":[-118.57173,-118.57173,-118.57173,-118.57173]}, + {"t":3.49439, "x":6.04654, "y":7.35626, "heading":3.14159, "vx":-3.50652, "vy":-0.071, "omega":0.0, "ax":-0.01983, "ay":1.77112, "alpha":0.0, "fx":[-0.33732,-0.33732,-0.33732,-0.33732], "fy":[30.12624,30.12624,30.12624,30.12624]}, + {"t":3.53006, "x":5.92147, "y":7.35485, "heading":3.14159, "vx":-3.50723, "vy":-0.00783, "omega":0.0, "ax":0.00109, "ay":0.73131, "alpha":0.0, "fx":[0.01849,0.01849,0.01849,0.01849], "fy":[12.43933,12.43933,12.43933,12.43933]}, + {"t":3.56572, "x":5.79639, "y":7.35504, "heading":3.14159, "vx":-3.50719, "vy":0.01825, "omega":0.0, "ax":0.00264, "ay":0.37228, "alpha":0.0, "fx":[0.04494,0.04494,0.04494,0.04494], "fy":[6.33237,6.33237,6.33237,6.33237]}, + {"t":3.60138, "x":5.67132, "y":7.35592, "heading":3.14159, "vx":-3.5071, "vy":0.03152, "omega":0.0, "ax":0.00236, "ay":0.23208, "alpha":0.0, "fx":[0.04013,0.04013,0.04013,0.04013], "fy":[3.94757,3.94757,3.94757,3.94757]}, + {"t":3.63705, "x":5.54624, "y":7.35719, "heading":3.14159, "vx":-3.50701, "vy":0.0398, "omega":0.0, "ax":0.00195, "ay":0.16047, "alpha":0.0, "fx":[0.03318,0.03318,0.03318,0.03318], "fy":[2.72954,2.72954,2.72954,2.72954]}, + {"t":3.67271, "x":5.42117, "y":7.35872, "heading":3.14159, "vx":-3.50694, "vy":0.04552, "omega":0.0, "ax":0.00158, "ay":0.11622, "alpha":0.0, "fx":[0.02679,0.02679,0.02679,0.02679], "fy":[1.97687,1.97687,1.97687,1.97687]}, + {"t":3.70837, "x":5.29611, "y":7.36041, "heading":3.14159, "vx":-3.50689, "vy":0.04967, "omega":0.0, "ax":0.00126, "ay":0.08628, "alpha":0.0, "fx":[0.02139,0.02139,0.02139,0.02139], "fy":[1.46766,1.46766,1.46766,1.46766]}, + {"t":3.74404, "x":5.17104, "y":7.36224, "heading":3.14159, "vx":-3.50684, "vy":0.05274, "omega":0.0, "ax":0.001, "ay":0.0654, "alpha":0.0, "fx":[0.01705,0.01705,0.01705,0.01705], "fy":[1.11236,1.11236,1.11236,1.11236]}, + {"t":3.7797, "x":5.04597, "y":7.36416, "heading":3.14159, "vx":-3.50681, "vy":0.05508, "omega":0.0, "ax":0.00081, "ay":0.05088, "alpha":0.0, "fx":[0.01377,0.01377,0.01377,0.01377], "fy":[0.86547,0.86547,0.86547,0.86547]}, + {"t":3.81536, "x":4.92091, "y":7.36616, "heading":3.14159, "vx":-3.50678, "vy":0.05689, "omega":0.0, "ax":0.00067, "ay":0.04117, "alpha":0.0, "fx":[0.01146,0.01146,0.01146,0.01146], "fy":[0.70028,0.70028,0.70028,0.70028]}, + {"t":3.85103, "x":4.79585, "y":7.36821, "heading":3.14159, "vx":-3.50675, "vy":0.05836, "omega":0.0, "ax":0.00059, "ay":0.0353, "alpha":0.0, "fx":[0.01005,0.01005,0.01005,0.01005], "fy":[0.60044,0.60044,0.60044,0.60044]}, + {"t":3.88669, "x":4.67078, "y":7.37032, "heading":3.14159, "vx":-3.50673, "vy":0.05962, "omega":0.0, "ax":0.00056, "ay":0.03274, "alpha":0.0, "fx":[0.0095,0.0095,0.0095,0.0095], "fy":[0.55683,0.55683,0.55683,0.55683]}, + {"t":3.92235, "x":4.54572, "y":7.37246, "heading":3.14159, "vx":-3.50671, "vy":0.06079, "omega":0.0, "ax":0.00058, "ay":0.03337, "alpha":0.0, "fx":[0.00988,0.00988,0.00988,0.00988], "fy":[0.56764,0.56764,0.56764,0.56764]}, + {"t":3.95802, "x":4.42066, "y":7.37465, "heading":3.14159, "vx":-3.50669, "vy":0.06198, "omega":0.0, "ax":0.00068, "ay":0.03804, "alpha":0.0, "fx":[0.0115,0.0115,0.0115,0.0115], "fy":[0.647,0.647,0.647,0.647]}, + {"t":3.99368, "x":4.2956, "y":7.37689, "heading":3.14159, "vx":-3.50667, "vy":0.06333, "omega":0.0, "ax":0.00094, "ay":0.05158, "alpha":0.0, "fx":[0.01601,0.01601,0.01601,0.01601], "fy":[0.87741,0.87741,0.87741,0.87741]}, + {"t":4.02934, "x":4.17054, "y":7.37918, "heading":3.14159, "vx":-3.50663, "vy":0.06517, "omega":0.0, "ax":0.00192, "ay":0.10054, "alpha":0.0, "fx":[0.0326,0.0326,0.0326,0.0326], "fy":[1.71011,1.71011,1.71011,1.71011]}, + {"t":4.06501, "x":4.04549, "y":7.38157, "heading":3.14159, "vx":-3.50657, "vy":0.06876, "omega":0.0, "ax":0.00789, "ay":0.33362, "alpha":0.0, "fx":[0.13416,0.13416,0.13416,0.13416], "fy":[5.67473,5.67473,5.67473,5.67473]}, + {"t":4.10067, "x":3.92043, "y":7.38423, "heading":3.14159, "vx":-3.50628, "vy":0.08066, "omega":0.0, "ax":2.57801, "ay":1.38649, "alpha":0.0, "fx":[43.85123,43.85123,43.85123,43.85123], "fy":[23.58377,23.58377,23.58377,23.58377]}, + {"t":4.13633, "x":3.79703, "y":7.38799, "heading":3.14159, "vx":-3.41434, "vy":0.1301, "omega":0.0, "ax":9.3219, "ay":-0.15525, "alpha":0.0, "fx":[158.56287,158.56287,158.56287,158.56287], "fy":[-2.64073,-2.64073,-2.64073,-2.64073]}, + {"t":4.172, "x":3.68119, "y":7.39253, "heading":3.14159, "vx":-3.08189, "vy":0.12457, "omega":0.0, "ax":9.37882, "ay":-0.1961, "alpha":0.0, "fx":[159.53112,159.53112,159.53112,159.53112], "fy":[-3.33564,-3.33564,-3.33564,-3.33564]}, + {"t":4.20766, "x":3.57724, "y":7.39685, "heading":3.14159, "vx":-2.74741, "vy":0.11757, "omega":0.0, "ax":9.39688, "ay":-0.23828, "alpha":0.0, "fx":[159.83819,159.83819,159.83819,159.83819], "fy":[-4.05316,-4.05316,-4.05316,-4.05316]}, + {"t":4.24332, "x":3.48524, "y":7.40089, "heading":3.14159, "vx":-2.41229, "vy":0.10907, "omega":0.0, "ax":7.81111, "ay":-5.25425, "alpha":0.0, "fx":[132.86478,132.86478,132.86478,132.86478], "fy":[-89.37337,-89.37337,-89.37337,-89.37337]}, + {"t":4.27899, "x":3.40417, "y":7.40144, "heading":3.14159, "vx":-2.13372, "vy":-0.07831, "omega":0.0, "ax":4.99261, "ay":-7.99427, "alpha":0.0, "fx":[84.92285,84.92285,84.92285,84.92285], "fy":[-135.98024,-135.98024,-135.98024,-135.98024]}, + {"t":4.31465, "x":3.33125, "y":7.39356, "heading":3.14159, "vx":-1.95566, "vy":-0.36341, "omega":0.0, "ax":3.79446, "ay":-8.04836, "alpha":10.32475, "fx":[93.7527,125.44856,24.69055,14.27923], "fy":[-130.05556,-99.72298,-158.18136,-159.64121]}, + {"t":4.33592, "x":3.29051, "y":7.38401, "heading":-3.13926, "vx":-1.87495, "vy":-0.53461, "omega":0.21962, "ax":3.8479, "ay":-8.05554, "alpha":10.01455, "fx":[93.66932,124.5299,27.34281,16.26443], "fy":[-130.09692,-100.83789,-157.72184,-159.43327]}, + {"t":4.35719, "x":3.2515, "y":7.37082, "heading":-3.13232, "vx":-1.7931, "vy":-0.70597, "omega":0.43265, "ax":3.90655, "ay":-8.06019, "alpha":9.70251, "fx":[93.73736,123.5586,29.91082,18.59053], "fy":[-130.02615,-101.99111,-157.23054,-159.1586]}, + {"t":4.37847, "x":3.21424, "y":7.35398, "heading":-3.12092, "vx":-1.71, "vy":-0.87742, "omega":0.63904, "ax":3.97151, "ay":-8.06274, "alpha":9.37431, "fx":[93.93048,122.48826,32.50079,21.29715], "fy":[-129.86113,-103.23389,-156.68882,-158.79597]}, + {"t":4.39974, "x":3.17876, "y":7.33349, "heading":-3.10521, "vx":-1.62552, "vy":-1.04893, "omega":0.83844, "ax":4.04412, "ay":-8.06355, "alpha":9.01195, "fx":[94.21548,121.25732,35.24249,24.44212], "fy":[-129.62402,-104.63004,-156.06518,-158.31552]}, + {"t":4.42101, "x":3.1451, "y":7.30935, "heading":-3.08533, "vx":-1.53949, "vy":-1.22045, "omega":1.03014, "ax":4.12607, "ay":-8.06287, "alpha":8.59237, "fx":[94.5489,119.78308,38.29305,28.10827], "fy":[-129.34408,-106.25948,-155.31113,-157.67404]}, + {"t":4.44228, "x":3.11329, "y":7.28157, "heading":-3.06148, "vx":-1.45173, "vy":-1.39196, "omega":1.21292, "ax":4.21938, "ay":-8.06077, "alpha":8.08531, "fx":[94.87145,117.95263,41.84326,32.41478], "fy":[-129.0621,-108.22264,-154.35379,-156.80676]}, + {"t":4.46355, "x":3.08336, "y":7.25013, "heading":-3.03385, "vx":-1.36197, "vy":-1.56343, "omega":1.3849, "ax":4.32651, "ay":-8.05684, "alpha":7.45011, "fx":[95.09829,115.60855,46.12716,37.53659], "fy":[-128.83753,-110.64573,-153.08287,-155.61216]}, + {"t":4.48482, "x":3.05537, "y":7.21505, "heading":-3.0027, "vx":-1.26994, "vy":-1.73481, "omega":1.54338, "ax":4.45043, "ay":-8.04977, "alpha":6.63031, "fx":[95.10117,112.5263,51.43561,43.73888], "fy":[-128.76086,-113.68624,-151.32779,-153.92233]}, + {"t":4.50609, "x":3.02936, "y":7.17633, "heading":-2.96837, "vx":-1.17527, "vy":-1.90604, "omega":1.68442, "ax":4.59478, "ay":-8.03616, "alpha":5.54446, "fx":[94.67242,108.37729,58.1334,51.44022], "fy":[-128.97649,-117.53659,-148.81742,-151.44031]}, + {"t":4.52737, "x":3.0054, "y":7.13397, "heading":-2.93129, "vx":-1.07754, "vy":-2.07699, "omega":1.80236, "ax":4.76375, "ay":-8.00798, "alpha":4.06859, "fx":[93.44498,102.66833,66.67516,61.3318], "fy":[-129.7286,-122.41993,-145.10934,-147.5961]}, + {"t":4.54864, "x":2.98356, "y":7.08797, "heading":-2.89203, "vx":-0.9762, "vy":-2.24733, "omega":1.8889, "ax":4.96084, "ay":-7.94655, "alpha":1.99949, "fx":[90.68997,94.64323,77.59807,74.59836], "fy":[-131.46159,-128.5611,-139.46913,-141.18205]}, + {"t":4.56991, "x":2.96392, "y":7.03837, "heading":-2.85139, "vx":-0.87068, "vy":-2.41636, "omega":1.93143, "ax":5.18081, "ay":-7.80706, "alpha":-1.03218, "fx":[84.70714,83.13666,91.41511,93.23759], "fy":[-135.05934,-136.08654,-130.693,-129.34468]}, + {"t":4.59118, "x":2.94657, "y":6.98521, "heading":-2.81054, "vx":-0.76047, "vy":-2.58243, "omega":1.90948, "ax":5.36052, "ay":-7.48188, "alpha":-5.77368, "fx":[70.53692,66.46755,108.13485,119.58429], "fy":[-142.36555,-144.72104,-117.05292,-104.91886]}, + {"t":4.61245, "x":2.9316, "y":6.92858, "heading":-2.77123, "vx":-0.64645, "vy":-2.74159, "omega":1.78666, "ax":5.11869, "ay":-6.87371, "alpha":-12.9419, "fx":[33.49714,44.4691,123.92082,146.38308], "fy":[-154.1577,-152.6313,-99.90605,-60.9846]}, + {"t":4.63372, "x":2.91901, "y":6.86871, "heading":-2.73615, "vx":-0.53756, "vy":-2.8878, "omega":1.51137, "ax":4.31871, "ay":-6.95756, "alpha":-15.90435, "fx":[-2.80419,28.65098,121.58071,146.41248], "fy":[-156.85772,-155.93823,-102.02158,-58.56715]}, + {"t":4.655, "x":2.90855, "y":6.80571, "heading":-2.7076, "vx":-0.4457, "vy":-3.0358, "omega":1.17306, "ax":3.00779, "ay":-7.24898, "alpha":-17.90037, "fx":[-46.94431,4.43354,110.17164,136.98548], "fy":[-148.73165,-157.77619,-112.92883,-73.77558]}, + {"t":4.67627, "x":2.89975, "y":6.73949, "heading":-2.6867, "vx":-0.38172, "vy":-3.19, "omega":0.79229, "ax":0.41895, "ay":-7.24258, "alpha":-19.12895, "fx":[-111.21477,-45.09314,82.34685,102.46571], "fy":[-108.29307,-150.09384,-131.77798,-102.61184]}, + {"t":4.69754, "x":2.89173, "y":6.66999, "heading":-2.67418, "vx":-0.37281, "vy":-3.34406, "omega":0.38538, "ax":-7.14317, "ay":-3.52129, "alpha":-11.93513, "fx":[-151.66957,-120.22075,-68.0351,-146.08774], "fy":[-35.96944,-99.19882,-131.91762,27.50108]}, + {"t":4.71881, "x":2.88218, "y":6.59806, "heading":-2.66868, "vx":-0.52475, "vy":-3.41896, "omega":0.1315, "ax":-8.96928, "ay":0.13301, "alpha":-4.02863, "fx":[-155.76041,-152.6237,-152.6927,-149.1827], "fy":[4.59971,-29.17856,-4.93705,38.56554]}, + {"t":4.74437, "x":2.86584, "y":6.51073, "heading":-2.66663, "vx":-0.75398, "vy":-3.41556, "omega":0.02855, "ax":-8.64159, "ay":1.99734, "alpha":-0.54987, "fx":[-147.38956,-147.99857,-146.64427,-145.93174], "fy":[33.56851,29.83719,34.37264,38.11864]}, + {"t":4.76992, "x":2.84375, "y":6.42409, "heading":-2.66608, "vx":-0.97483, "vy":-3.36452, "omega":0.01449, "ax":-8.01657, "ay":2.5746, "alpha":-0.05217, "fx":[-136.42206,-136.48098,-136.29745,-136.23793], "fy":[43.73745,43.44136,43.84908,44.14498]}, + {"t":4.79548, "x":2.81622, "y":6.33895, "heading":-2.66573, "vx":-1.1797, "vy":-3.29872, "omega":0.01316, "ax":-6.73412, "ay":3.01066, "alpha":0.93729, "fx":[-112.62517,-112.06591,-116.48741,-117.00321], "fy":[52.27082,56.43185,50.20564,45.93358]}, + {"t":4.82104, "x":2.78387, "y":6.25563, "heading":-2.66509, "vx":-1.3518, "vy":-3.22178, "omega":0.03711, "ax":0.60987, "ay":6.99409, "alpha":16.64042, "fx":[113.01682,52.31114,-68.31546,-55.51759], "fy":[97.55833,142.65656,131.30077,104.35413]}, + {"t":4.84659, "x":2.74952, "y":6.17557, "heading":-2.6587, "vx":-1.33622, "vy":-3.04303, "omega":0.46239, "ax":1.40044, "ay":7.0193, "alpha":17.33289, "fx":[132.18411,67.78327,-69.28026,-35.40282], "fy":[83.74038,141.90752,138.78952,113.14777]}, + {"t":4.87215, "x":2.71583, "y":6.1001, "heading":-2.64123, "vx":-1.30043, "vy":-2.86364, "omega":0.90536, "ax":1.65425, "ay":6.96839, "alpha":17.40153, "fx":[135.53691,69.63254,-72.71671,-19.89974], "fy":[81.37611,142.42181,139.24653,111.0767]}, + {"t":4.89771, "x":2.68314, "y":6.02919, "heading":-2.61241, "vx":-1.25815, "vy":-2.68555, "omega":1.35008, "ax":2.72324, "ay":7.15117, "alpha":14.76246, "fx":[134.67414,68.63734,-71.35073,53.3253], "fy":[84.11934,143.51564,140.93649,117.98593]}, + {"t":4.92326, "x":2.65187, "y":5.96289, "heading":-2.57308, "vx":-1.18855, "vy":-2.50279, "omega":1.72736, "ax":3.92792, "ay":8.35606, "alpha":3.91786, "fx":[95.35708,67.87878,33.58352,70.43172], "fy":[127.35523,144.04259,155.21638,141.92271]}, + {"t":4.94882, "x":2.62278, "y":5.90165, "heading":-2.52766, "vx":-1.08817, "vy":-2.28924, "omega":1.82749, "ax":4.06432, "ay":8.43892, "alpha":-0.83577, "fx":[62.29546,69.89273,75.70713,68.63631], "fy":[146.70899,143.20603,140.31877,143.94094]}, + {"t":4.97438, "x":2.5963, "y":5.84591, "heading":-2.48123, "vx":-0.9843, "vy":-2.07357, "omega":1.80613, "ax":3.99727, "ay":8.40479, "alpha":-3.22709, "fx":[39.60949,74.32655,91.86846,66.16499], "fy":[154.59975,141.06813,130.7057,145.47865]}, + {"t":4.99993, "x":2.57245, "y":5.79566, "heading":-2.43612, "vx":-0.88214, "vy":-1.85877, "omega":1.72365, "ax":3.92648, "ay":8.34826, "alpha":-4.65713, "fx":[23.98904,80.39994,99.22537,63.53859], "fy":[157.92448,137.79934,125.4458,146.83666]}, + {"t":5.02549, "x":2.55118, "y":5.75088, "heading":-2.39359, "vx":-0.7818, "vy":-1.64542, "omega":1.60463, "ax":3.87924, "ay":8.28336, "alpha":-5.65599, "fx":[12.77864,87.3168,102.90866,60.935], "fy":[159.32641,133.61985,122.5934,148.05048]}, + {"t":5.05105, "x":2.53247, "y":5.71153, "heading":-2.35443, "vx":-0.68266, "vy":-1.43373, "omega":1.46008, "ax":3.85143, "ay":8.21386, "alpha":-6.44662, "fx":[4.39034,94.39698,104.81126,58.44819], "fy":[159.85343,128.80748,121.07517,149.12554]}, + {"t":5.0766, "x":2.51628, "y":5.67757, "heading":-2.31922, "vx":-0.58423, "vy":-1.22381, "omega":1.29533, "ax":3.8351, "ay":8.14251, "alpha":-7.12545, "fx":[-2.11404,101.13288,105.77716,56.14011], "fy":[159.95814,123.67726,120.30799,150.06351]}, + {"t":5.10216, "x":2.5026, "y":5.64896, "heading":-2.28844, "vx":-0.48621, "vy":-1.01571, "omega":1.11323, "ax":3.82409, "ay":8.07204, "alpha":-7.73142, "fx":[-7.31425,107.20157,106.24143,54.05778], "fy":[159.85161,118.53921,119.95532,150.86633]}, + {"t":5.12772, "x":2.49143, "y":5.62563, "heading":-2.26252, "vx":-0.38848, "vy":-0.80942, "omega":0.91564, "ax":3.81469, "ay":8.00488, "alpha":-8.27782, "fx":[-11.58982,112.44175,106.45529,52.24001], "fy":[159.63745,113.65874,119.80993,151.53653]}, + {"t":5.15327, "x":2.48274, "y":5.60756, "heading":-2.24182, "vx":-0.29099, "vy":-0.60484, "omega":0.70409, "ax":3.80525, "ay":7.94276, "alpha":-8.76784, "fx":[-15.20289,116.81021,106.5774,50.72011], "fy":[159.3673,109.2358,119.73661,152.07692]}, + {"t":5.17883, "x":2.47655, "y":5.5947, "heading":-2.22669, "vx":-0.19374, "vy":-0.40185, "omega":0.48001, "ax":3.79541, "ay":7.88673, "alpha":-9.20193, "fx":[-18.3412,120.33444,106.71505,49.52713], "fy":[159.06617,105.40517,119.64261,152.49002]}, + {"t":5.20439, "x":2.47284, "y":5.587, "heading":-2.21743, "vx":-0.09675, "vy":-0.20029, "omega":0.24484, "ax":3.78553, "ay":7.83722, "alpha":-9.58033, "fx":[-21.13997,123.07259,106.94426,48.68638], "fy":[158.74527,102.25116,119.46144,152.7776]}, + {"t":5.22994, "x":2.4716, "y":5.58445, "heading":-2.2143, "vx":0.0, "vy":0.0, "omega":0.0, "ax":0.0, "ay":0.0, "alpha":0.0, "fx":[0.0,0.0,0.0,0.0], "fy":[0.0,0.0,0.0,0.0]}], "splits":[0] }, "events":[] diff --git a/src/main/deploy/v1_doomspiral/choreo/V1_DoomSpiralAutos.chor b/src/main/deploy/v1_doomspiral/choreo/V1_DoomSpiralAutos.chor index 10595bd4..24d6e774 100644 --- a/src/main/deploy/v1_doomspiral/choreo/V1_DoomSpiralAutos.chor +++ b/src/main/deploy/v1_doomspiral/choreo/V1_DoomSpiralAutos.chor @@ -3,7 +3,15 @@ "version":2, "type":"Swerve", "variables":{ - "expressions":{}, + "expressions":{ + "INTAKE_VELOCITY":{ + "dimension":"LinVel", + "var":{ + "exp":"0.55 m / s", + "val":0.55 + } + } + }, "poses":{} }, "config":{ diff --git a/src/main/java/frc/robot/FieldConstants.java b/src/main/java/frc/robot/FieldConstants.java index a04184b5..e15464f2 100644 --- a/src/main/java/frc/robot/FieldConstants.java +++ b/src/main/java/frc/robot/FieldConstants.java @@ -27,7 +27,7 @@ */ public class FieldConstants { - public static AprilTagLayoutType tagLayoutType = AprilTagLayoutType.WELDED; + public static AprilTagLayoutType tagLayoutType = AprilTagLayoutType.ANDYMARK; // AprilTag related constants public static final int aprilTagCount = tagLayoutType.getLayout().getTags().size(); diff --git a/src/main/java/frc/robot/commands/shared/DriveCommands.java b/src/main/java/frc/robot/commands/shared/DriveCommands.java index ce801656..f5d9b98d 100644 --- a/src/main/java/frc/robot/commands/shared/DriveCommands.java +++ b/src/main/java/frc/robot/commands/shared/DriveCommands.java @@ -429,15 +429,10 @@ public static Command aimAtHub(SwerveDrive drive, SwerveDriveConstants driveCons 0.0, 0.0, AutoAlignCommand.calculate( - omegaController, - V1_DoomSpiralRobotState.getShootingParameters() - .chassisAngle() - .getRadians(), - V1_DoomSpiralRobotState.getHeading().getRadians(), - drive.getMeasuredChassisSpeeds().omegaRadiansPerSecond) - + V1_DoomSpiralRobotState.getShootingParameters() - .chassisVelocity() - .in(RadiansPerSecond), + omegaController, + V1_DoomSpiralRobotState.getRobotToHubAngle().getRadians(), + V1_DoomSpiralRobotState.getHeading().getRadians(), + drive.getMeasuredChassisSpeeds().omegaRadiansPerSecond), V1_DoomSpiralRobotState.getHeading())); }); } diff --git a/src/main/java/frc/robot/commands/v1_DoomSpiral/V1_DoomSpiralCompositeCommands.java b/src/main/java/frc/robot/commands/v1_DoomSpiral/V1_DoomSpiralCompositeCommands.java index db90c06d..d7ba0e64 100644 --- a/src/main/java/frc/robot/commands/v1_DoomSpiral/V1_DoomSpiralCompositeCommands.java +++ b/src/main/java/frc/robot/commands/v1_DoomSpiral/V1_DoomSpiralCompositeCommands.java @@ -1,7 +1,5 @@ package frc.robot.commands.v1_DoomSpiral; -import static edu.wpi.first.units.Units.RadiansPerSecond; - import edu.wpi.first.wpilibj2.command.Command; import edu.wpi.first.wpilibj2.command.Commands; import edu.wpi.team190.gompeilib.core.utility.phoenix.GainSlot; @@ -34,12 +32,7 @@ public static Command scoreCommand( V1_DoomSpiralShooter shooter, Intake intake, V1_DoomSpiralSpindexer spindexer) { return Commands.parallel( intake.stopRollerOverride(), - shooter.setGoal( - HoodGoal.SCORE, - () -> - V1_DoomSpiralRobotState.getShootingParameters() - .flywheelSpeed() - .in(RadiansPerSecond)), + shooter.setGoal(HoodGoal.SCORE, () -> V1_DoomSpiralRobotState.getScoreVelocity()), new ContinuousConditionalCommand( spindexer.setVoltage(V1_DoomSpiralSpindexerConstants.SPINDEXER_VOLTAGE), spindexer.agitateSpindexer(), @@ -47,7 +40,7 @@ public static Command scoreCommand( shooter.atGoal() && DriveCommands.atAngle( V1_DoomSpiralRobotState.getHeading(), - V1_DoomSpiralRobotState.getShootingParameters().chassisAngle()))); + V1_DoomSpiralRobotState.getRobotToHubAngle()))); } public static Command stopShooterCommand( diff --git a/src/main/java/frc/robot/commands/v1_DoomSpiral/autonomous/V1_DoomSpiralAutoClimb.java b/src/main/java/frc/robot/commands/v1_DoomSpiral/autonomous/V1_DoomSpiralAutoClimb.java index 712493b9..4f012c42 100644 --- a/src/main/java/frc/robot/commands/v1_DoomSpiral/autonomous/V1_DoomSpiralAutoClimb.java +++ b/src/main/java/frc/robot/commands/v1_DoomSpiral/autonomous/V1_DoomSpiralAutoClimb.java @@ -1,7 +1,5 @@ package frc.robot.commands.v1_DoomSpiral.autonomous; -import static edu.wpi.first.units.Units.RadiansPerSecond; - import choreo.auto.AutoRoutine; import choreo.auto.AutoTrajectory; import edu.wpi.first.math.geometry.Pose2d; @@ -50,10 +48,7 @@ public static final BetterAutoChooser.AutoRoutineConfiguration getAutoRoutine( intake.stopRollerOverride(), shooter.setGoal( V1_DoomSpiralShooterConstants.HoodGoal.SCORE, - () -> - V1_DoomSpiralRobotState.getShootingParameters() - .flywheelSpeed() - .in(RadiansPerSecond)), + () -> V1_DoomSpiralRobotState.getScoreVelocity()), Commands.sequence( spindexer.agitateSpindexer().until(shooter::atGoal), spindexer.setVoltage( diff --git a/src/main/java/frc/robot/commands/v1_DoomSpiral/autonomous/V1_DoomSpiralAutoLeftTrenchAntiBucks.java b/src/main/java/frc/robot/commands/v1_DoomSpiral/autonomous/V1_DoomSpiralAutoLeftTrenchAntiBucks.java index aca295bf..fd3a5a57 100644 --- a/src/main/java/frc/robot/commands/v1_DoomSpiral/autonomous/V1_DoomSpiralAutoLeftTrenchAntiBucks.java +++ b/src/main/java/frc/robot/commands/v1_DoomSpiral/autonomous/V1_DoomSpiralAutoLeftTrenchAntiBucks.java @@ -2,6 +2,8 @@ import choreo.auto.AutoRoutine; import choreo.auto.AutoTrajectory; +import com.pathplanner.lib.auto.AutoBuilder; +import com.pathplanner.lib.path.PathPlannerPath; import edu.wpi.first.math.geometry.Pose2d; import edu.wpi.first.wpilibj2.command.Commands; import edu.wpi.team190.gompeilib.subsystems.drivebases.swervedrive.SwerveDrive; @@ -16,6 +18,7 @@ import frc.robot.subsystems.v1_DoomSpiral.shooter.V1_DoomSpiralShooter; import frc.robot.subsystems.v1_DoomSpiral.spindexer.V1_DoomSpiralSpindexer; import frc.robot.util.BetterAutoChooser; +import frc.robot.util.Elastic; import java.util.function.Supplier; import org.littletonrobotics.junction.networktables.LoggedNetworkBoolean; @@ -36,8 +39,18 @@ public static final BetterAutoChooser.AutoRoutineConfiguration getAutoRoutine( AutoTrajectory LEFT_TRENCH_ANTI_BUCKS = routine.trajectory(V1_DoomSpiralAutoTrajectoryCache.LEFT_TRENCH_ANTI_BUCKS); - AutoTrajectory LEFT_RETURN = - routine.trajectory(V1_DoomSpiralAutoTrajectoryCache.LEFT_RETURN_TO_MID); + PathPlannerPath LEFT_RETURN_PP; + try { + LEFT_RETURN_PP = PathPlannerPath.fromPathFile("V1_RETURN"); + } catch (Exception e) { + e.printStackTrace(); + Elastic.sendNotification( + new Elastic.Notification( + Elastic.NotificationLevel.ERROR, "Failed to load V1_RETURN path", e.getMessage())); + LEFT_RETURN_PP = null; + } + + final PathPlannerPath LEFT_RETURN_PATH = LEFT_RETURN_PP; V1_DoomSpiralAutoTrajectoryCache.GO_BACK_TRIGGER.onTrue( Commands.runOnce(() -> RETURN_TO_MID = returnToMid.get())); @@ -51,6 +64,10 @@ public static final BetterAutoChooser.AutoRoutineConfiguration getAutoRoutine( .onTrue( Commands.sequence( + // Reset the RETURN_TO_MID flag + + Commands.runOnce(() -> RETURN_TO_MID = false), + // Set the inital pose LEFT_TRENCH_ANTI_BUCKS.resetOdometry(), @@ -87,11 +104,13 @@ public static final BetterAutoChooser.AutoRoutineConfiguration getAutoRoutine( DriveCommands.aimAtHub(drive, V1_DoomSpiralConstants.DRIVE_CONSTANTS), Commands.sequence(Commands.waitSeconds(3.0), intake.agitate())) .until(() -> RETURN_TO_MID), - LEFT_RETURN - .cmd() - .alongWith( - V1_DoomSpiralCompositeCommands.stopShooterCommand(shooter, spindexer), - intake.collect()))); + LEFT_RETURN_PATH != null + ? AutoBuilder.followPath(LEFT_RETURN_PATH) + .alongWith( + V1_DoomSpiralCompositeCommands.stopShooterCommand(shooter, spindexer), + intake.collect()) + : Commands.print("V1_RETURN path unavailable, skipping"), + DriveCommands.stop(drive))); return new BetterAutoChooser.AutoRoutineConfiguration( () -> routine, @@ -102,7 +121,7 @@ public static final BetterAutoChooser.AutoRoutineConfiguration getAutoRoutine( drive.setAutoControllers( V1_DoomSpiralConstants.TRANSLATION_AUTO_GAINS, V1_DoomSpiralConstants.ROTATION_AUTO_GAINS); - V1_DoomSpiralRobotState.setAutoTrajectory(LEFT_TRENCH_ANTI_BUCKS, LEFT_RETURN); + V1_DoomSpiralRobotState.setAutoTrajectory(LEFT_TRENCH_ANTI_BUCKS); })); } } diff --git a/src/main/java/frc/robot/commands/v1_DoomSpiral/autonomous/V1_DoomSpiralAutoLeftTrenchAntiBucksCrosses.java b/src/main/java/frc/robot/commands/v1_DoomSpiral/autonomous/V1_DoomSpiralAutoLeftTrenchAntiBucksCrosses.java index 7947fb68..1909fe7a 100644 --- a/src/main/java/frc/robot/commands/v1_DoomSpiral/autonomous/V1_DoomSpiralAutoLeftTrenchAntiBucksCrosses.java +++ b/src/main/java/frc/robot/commands/v1_DoomSpiral/autonomous/V1_DoomSpiralAutoLeftTrenchAntiBucksCrosses.java @@ -2,6 +2,8 @@ import choreo.auto.AutoRoutine; import choreo.auto.AutoTrajectory; +import com.pathplanner.lib.auto.AutoBuilder; +import com.pathplanner.lib.path.PathPlannerPath; import edu.wpi.first.math.geometry.Pose2d; import edu.wpi.first.wpilibj2.command.Commands; import edu.wpi.team190.gompeilib.subsystems.drivebases.swervedrive.SwerveDrive; @@ -16,6 +18,7 @@ import frc.robot.subsystems.v1_DoomSpiral.shooter.V1_DoomSpiralShooter; import frc.robot.subsystems.v1_DoomSpiral.spindexer.V1_DoomSpiralSpindexer; import frc.robot.util.BetterAutoChooser; +import frc.robot.util.Elastic; import java.util.function.Supplier; import org.littletonrobotics.junction.networktables.LoggedNetworkBoolean; @@ -36,8 +39,18 @@ public static final BetterAutoChooser.AutoRoutineConfiguration getAutoRoutine( AutoTrajectory LEFT_TRENCH_ANTI_BUCKS_CROSSES = routine.trajectory(V1_DoomSpiralAutoTrajectoryCache.LEFT_TRENCH_ANTI_BUCKS_CROSSES); - AutoTrajectory LEFT_RETURN = - routine.trajectory(V1_DoomSpiralAutoTrajectoryCache.LEFT_RETURN_TO_MID); + PathPlannerPath LEFT_RETURN_PP; + try { + LEFT_RETURN_PP = PathPlannerPath.fromPathFile("V1_RETURN"); + } catch (Exception e) { + e.printStackTrace(); + Elastic.sendNotification( + new Elastic.Notification( + Elastic.NotificationLevel.ERROR, "Failed to load V1_RETURN path", e.getMessage())); + LEFT_RETURN_PP = null; + } + + final PathPlannerPath LEFT_RETURN_PATH = LEFT_RETURN_PP; V1_DoomSpiralAutoTrajectoryCache.GO_BACK_TRIGGER.onTrue( Commands.runOnce(() -> RETURN_TO_MID = returnToMid.get())); @@ -53,6 +66,10 @@ public static final BetterAutoChooser.AutoRoutineConfiguration getAutoRoutine( .onTrue( Commands.sequence( + // Reset the RETURN_TO_MID flag + + Commands.runOnce(() -> RETURN_TO_MID = false), + // Set the inital pose LEFT_TRENCH_ANTI_BUCKS_CROSSES.resetOdometry(), @@ -89,11 +106,13 @@ public static final BetterAutoChooser.AutoRoutineConfiguration getAutoRoutine( DriveCommands.aimAtHub(drive, V1_DoomSpiralConstants.DRIVE_CONSTANTS), Commands.sequence(Commands.waitSeconds(3.0), intake.agitate())) .until(() -> RETURN_TO_MID), - LEFT_RETURN - .cmd() - .alongWith( - V1_DoomSpiralCompositeCommands.stopShooterCommand(shooter, spindexer), - intake.collect()))); + LEFT_RETURN_PATH != null + ? AutoBuilder.followPath(LEFT_RETURN_PATH) + .alongWith( + V1_DoomSpiralCompositeCommands.stopShooterCommand(shooter, spindexer), + intake.collect()) + : Commands.print("V1_RETURN path unavailable, skipping"), + DriveCommands.stop(drive))); return new BetterAutoChooser.AutoRoutineConfiguration( () -> routine, @@ -104,8 +123,7 @@ public static final BetterAutoChooser.AutoRoutineConfiguration getAutoRoutine( drive.setAutoControllers( V1_DoomSpiralConstants.TRANSLATION_AUTO_GAINS, V1_DoomSpiralConstants.ROTATION_AUTO_GAINS); - V1_DoomSpiralRobotState.setAutoTrajectory( - LEFT_TRENCH_ANTI_BUCKS_CROSSES, LEFT_RETURN); + V1_DoomSpiralRobotState.setAutoTrajectory(LEFT_TRENCH_ANTI_BUCKS_CROSSES); })); } } diff --git a/src/main/java/frc/robot/commands/v1_DoomSpiral/autonomous/V1_DoomSpiralAutoLeftTrenchSimple.java b/src/main/java/frc/robot/commands/v1_DoomSpiral/autonomous/V1_DoomSpiralAutoLeftTrenchSimple.java index 27242875..c7a1e2c1 100644 --- a/src/main/java/frc/robot/commands/v1_DoomSpiral/autonomous/V1_DoomSpiralAutoLeftTrenchSimple.java +++ b/src/main/java/frc/robot/commands/v1_DoomSpiral/autonomous/V1_DoomSpiralAutoLeftTrenchSimple.java @@ -2,6 +2,8 @@ import choreo.auto.AutoRoutine; import choreo.auto.AutoTrajectory; +import com.pathplanner.lib.auto.AutoBuilder; +import com.pathplanner.lib.path.PathPlannerPath; import edu.wpi.first.math.geometry.Pose2d; import edu.wpi.first.wpilibj2.command.Commands; import edu.wpi.team190.gompeilib.subsystems.drivebases.swervedrive.SwerveDrive; @@ -16,6 +18,7 @@ import frc.robot.subsystems.v1_DoomSpiral.shooter.V1_DoomSpiralShooter; import frc.robot.subsystems.v1_DoomSpiral.spindexer.V1_DoomSpiralSpindexer; import frc.robot.util.BetterAutoChooser; +import frc.robot.util.Elastic; import java.util.function.Supplier; import org.littletonrobotics.junction.networktables.LoggedNetworkBoolean; @@ -36,8 +39,32 @@ public static final BetterAutoChooser.AutoRoutineConfiguration getAutoRoutine( AutoTrajectory LEFT_TRENCH_SIMPLE = routine.trajectory(V1_DoomSpiralAutoTrajectoryCache.LEFT_TRENCH_SIMPLE); - AutoTrajectory LEFT_RETURN = - routine.trajectory(V1_DoomSpiralAutoTrajectoryCache.LEFT_RETURN_TO_MID); + + PathPlannerPath V1_SIMPLE; + try { + V1_SIMPLE = PathPlannerPath.fromPathFile("V1_SIMPLE"); + } catch (Exception e) { + e.printStackTrace(); + Elastic.sendNotification( + new Elastic.Notification( + Elastic.NotificationLevel.ERROR, "Failed to load V1_SIMPLE path", e.getMessage())); + V1_SIMPLE = null; + } + + final PathPlannerPath V1_SIMPLE_PATH = V1_SIMPLE; + + PathPlannerPath LEFT_RETURN_PP; + try { + LEFT_RETURN_PP = PathPlannerPath.fromPathFile("V1_RETURN"); + } catch (Exception e) { + e.printStackTrace(); + Elastic.sendNotification( + new Elastic.Notification( + Elastic.NotificationLevel.ERROR, "Failed to load V1_RETURN path", e.getMessage())); + LEFT_RETURN_PP = null; + } + + final PathPlannerPath LEFT_RETURN_PATH = LEFT_RETURN_PP; V1_DoomSpiralAutoTrajectoryCache.GO_BACK_TRIGGER.onTrue( Commands.runOnce(() -> RETURN_TO_MID = returnToMid.get())); @@ -51,6 +78,10 @@ public static final BetterAutoChooser.AutoRoutineConfiguration getAutoRoutine( .onTrue( Commands.sequence( + // Reset the RETURN_TO_MID flag + + Commands.runOnce(() -> RETURN_TO_MID = false), + // Set the inital pose LEFT_TRENCH_SIMPLE.resetOdometry(), @@ -75,6 +106,12 @@ public static final BetterAutoChooser.AutoRoutineConfiguration getAutoRoutine( < V1_DoomSpiralConstants.AUTO_CORRECTION_THRESHOLD_METERS; return !isFinished; }), + V1_SIMPLE_PATH != null + ? AutoBuilder.followPath(V1_SIMPLE_PATH) + .alongWith( + V1_DoomSpiralCompositeCommands.stopShooterCommand(shooter, spindexer), + intake.collect()) + : Commands.print("V1_DOT path unavailable, skipping"), // Stop drive @@ -87,11 +124,13 @@ public static final BetterAutoChooser.AutoRoutineConfiguration getAutoRoutine( DriveCommands.aimAtHub(drive, V1_DoomSpiralConstants.DRIVE_CONSTANTS), Commands.sequence(Commands.waitSeconds(3.0), intake.agitate())) .until(() -> RETURN_TO_MID), - LEFT_RETURN - .cmd() - .alongWith( - V1_DoomSpiralCompositeCommands.stopShooterCommand(shooter, spindexer), - intake.collect()))); + LEFT_RETURN_PATH != null + ? AutoBuilder.followPath(LEFT_RETURN_PATH) + .alongWith( + V1_DoomSpiralCompositeCommands.stopShooterCommand(shooter, spindexer), + intake.collect()) + : Commands.print("V1_RETURN path unavailable, skipping"), + DriveCommands.stop(drive))); return new BetterAutoChooser.AutoRoutineConfiguration( () -> routine, @@ -102,7 +141,7 @@ public static final BetterAutoChooser.AutoRoutineConfiguration getAutoRoutine( drive.setAutoControllers( V1_DoomSpiralConstants.TRANSLATION_AUTO_GAINS, V1_DoomSpiralConstants.ROTATION_AUTO_GAINS); - V1_DoomSpiralRobotState.setAutoTrajectory(LEFT_TRENCH_SIMPLE, LEFT_RETURN); + V1_DoomSpiralRobotState.setAutoTrajectory(LEFT_TRENCH_SIMPLE); })); } } diff --git a/src/main/java/frc/robot/commands/v1_DoomSpiral/autonomous/V1_DoomSpiralAutoLeftTrenchSimpleCrosses.java b/src/main/java/frc/robot/commands/v1_DoomSpiral/autonomous/V1_DoomSpiralAutoLeftTrenchSimpleCrosses.java index 6152f751..1bebcab4 100644 --- a/src/main/java/frc/robot/commands/v1_DoomSpiral/autonomous/V1_DoomSpiralAutoLeftTrenchSimpleCrosses.java +++ b/src/main/java/frc/robot/commands/v1_DoomSpiral/autonomous/V1_DoomSpiralAutoLeftTrenchSimpleCrosses.java @@ -2,6 +2,8 @@ import choreo.auto.AutoRoutine; import choreo.auto.AutoTrajectory; +import com.pathplanner.lib.auto.AutoBuilder; +import com.pathplanner.lib.path.PathPlannerPath; import edu.wpi.first.math.geometry.Pose2d; import edu.wpi.first.wpilibj2.command.Commands; import edu.wpi.team190.gompeilib.subsystems.drivebases.swervedrive.SwerveDrive; @@ -16,6 +18,7 @@ import frc.robot.subsystems.v1_DoomSpiral.shooter.V1_DoomSpiralShooter; import frc.robot.subsystems.v1_DoomSpiral.spindexer.V1_DoomSpiralSpindexer; import frc.robot.util.BetterAutoChooser; +import frc.robot.util.Elastic; import java.util.function.Supplier; import org.littletonrobotics.junction.networktables.LoggedNetworkBoolean; @@ -36,8 +39,18 @@ public static final BetterAutoChooser.AutoRoutineConfiguration getAutoRoutine( AutoTrajectory LEFT_TRENCH_SIMPLE_CROSSES = routine.trajectory(V1_DoomSpiralAutoTrajectoryCache.LEFT_TRENCH_SIMPLE_CROSSES); - AutoTrajectory LEFT_RETURN = - routine.trajectory(V1_DoomSpiralAutoTrajectoryCache.LEFT_RETURN_TO_MID); + PathPlannerPath LEFT_RETURN_PP; + try { + LEFT_RETURN_PP = PathPlannerPath.fromPathFile("V1_RETURN"); + } catch (Exception e) { + e.printStackTrace(); + Elastic.sendNotification( + new Elastic.Notification( + Elastic.NotificationLevel.ERROR, "Failed to load V1_RETURN path", e.getMessage())); + LEFT_RETURN_PP = null; + } + + final PathPlannerPath LEFT_RETURN_PATH = LEFT_RETURN_PP; V1_DoomSpiralAutoTrajectoryCache.GO_BACK_TRIGGER.onTrue( Commands.runOnce(() -> RETURN_TO_MID = returnToMid.get())); @@ -51,6 +64,10 @@ public static final BetterAutoChooser.AutoRoutineConfiguration getAutoRoutine( .onTrue( Commands.sequence( + // Reset the RETURN_TO_MID flag + + Commands.runOnce(() -> RETURN_TO_MID = false), + // Set the inital pose LEFT_TRENCH_SIMPLE_CROSSES.resetOdometry(), @@ -87,11 +104,13 @@ public static final BetterAutoChooser.AutoRoutineConfiguration getAutoRoutine( DriveCommands.aimAtHub(drive, V1_DoomSpiralConstants.DRIVE_CONSTANTS), Commands.sequence(Commands.waitSeconds(3.0), intake.agitate())) .until(() -> RETURN_TO_MID), - LEFT_RETURN - .cmd() - .alongWith( - V1_DoomSpiralCompositeCommands.stopShooterCommand(shooter, spindexer), - intake.collect()))); + LEFT_RETURN_PATH != null + ? AutoBuilder.followPath(LEFT_RETURN_PATH) + .alongWith( + V1_DoomSpiralCompositeCommands.stopShooterCommand(shooter, spindexer), + intake.collect()) + : Commands.print("V1_RETURN path unavailable, skipping"), + DriveCommands.stop(drive))); return new BetterAutoChooser.AutoRoutineConfiguration( () -> routine, @@ -102,8 +121,7 @@ public static final BetterAutoChooser.AutoRoutineConfiguration getAutoRoutine( drive.setAutoControllers( V1_DoomSpiralConstants.TRANSLATION_AUTO_GAINS, V1_DoomSpiralConstants.ROTATION_AUTO_GAINS); - V1_DoomSpiralRobotState.setAutoTrajectory( - LEFT_TRENCH_SIMPLE_CROSSES, LEFT_RETURN); + V1_DoomSpiralRobotState.setAutoTrajectory(LEFT_TRENCH_SIMPLE_CROSSES); })); } } diff --git a/src/main/java/frc/robot/commands/v1_DoomSpiral/autonomous/V1_DoomSpiralAutoRightTrenchAntiBucks.java b/src/main/java/frc/robot/commands/v1_DoomSpiral/autonomous/V1_DoomSpiralAutoRightTrenchAntiBucks.java index 6752cfbc..d47e8d75 100644 --- a/src/main/java/frc/robot/commands/v1_DoomSpiral/autonomous/V1_DoomSpiralAutoRightTrenchAntiBucks.java +++ b/src/main/java/frc/robot/commands/v1_DoomSpiral/autonomous/V1_DoomSpiralAutoRightTrenchAntiBucks.java @@ -2,6 +2,8 @@ import choreo.auto.AutoRoutine; import choreo.auto.AutoTrajectory; +import com.pathplanner.lib.auto.AutoBuilder; +import com.pathplanner.lib.path.PathPlannerPath; import edu.wpi.first.math.geometry.Pose2d; import edu.wpi.first.wpilibj2.command.Commands; import edu.wpi.team190.gompeilib.subsystems.drivebases.swervedrive.SwerveDrive; @@ -16,6 +18,7 @@ import frc.robot.subsystems.v1_DoomSpiral.shooter.V1_DoomSpiralShooter; import frc.robot.subsystems.v1_DoomSpiral.spindexer.V1_DoomSpiralSpindexer; import frc.robot.util.BetterAutoChooser; +import frc.robot.util.Elastic; import java.util.function.Supplier; import org.littletonrobotics.junction.networktables.LoggedNetworkBoolean; @@ -36,8 +39,18 @@ public static final BetterAutoChooser.AutoRoutineConfiguration getAutoRoutine( AutoTrajectory RIGHT_TRENCH_ANTI_BUCKS = routine.trajectory(V1_DoomSpiralAutoTrajectoryCache.RIGHT_TRENCH_ANTI_BUCKS); - AutoTrajectory RIGHT_RETURN = - routine.trajectory(V1_DoomSpiralAutoTrajectoryCache.RIGHT_RETURN_TO_MID); + PathPlannerPath RIGHT_RETURN_PP; + try { + RIGHT_RETURN_PP = PathPlannerPath.fromPathFile("V1_RETURN").mirrorPath(); + } catch (Exception e) { + e.printStackTrace(); + Elastic.sendNotification( + new Elastic.Notification( + Elastic.NotificationLevel.ERROR, "Failed to load V1_RETURN path", e.getMessage())); + RIGHT_RETURN_PP = null; + } + + final PathPlannerPath RIGHT_RETURN_PATH = RIGHT_RETURN_PP; V1_DoomSpiralAutoTrajectoryCache.GO_BACK_TRIGGER.onTrue( Commands.runOnce(() -> RETURN_TO_MID = returnToMid.get())); @@ -51,6 +64,10 @@ public static final BetterAutoChooser.AutoRoutineConfiguration getAutoRoutine( .onTrue( Commands.sequence( + // Reset the RETURN_TO_MID flag + + Commands.runOnce(() -> RETURN_TO_MID = false), + // Set the inital pose RIGHT_TRENCH_ANTI_BUCKS.resetOdometry(), @@ -87,11 +104,13 @@ public static final BetterAutoChooser.AutoRoutineConfiguration getAutoRoutine( DriveCommands.aimAtHub(drive, V1_DoomSpiralConstants.DRIVE_CONSTANTS), Commands.sequence(Commands.waitSeconds(3.0), intake.agitate())) .until(() -> RETURN_TO_MID), - RIGHT_RETURN - .cmd() - .alongWith( - V1_DoomSpiralCompositeCommands.stopShooterCommand(shooter, spindexer), - intake.collect()))); + RIGHT_RETURN_PATH != null + ? AutoBuilder.followPath(RIGHT_RETURN_PATH) + .alongWith( + V1_DoomSpiralCompositeCommands.stopShooterCommand(shooter, spindexer), + intake.collect()) + : Commands.print("V1_RETURN path unavailable, skipping"), + DriveCommands.stop(drive))); return new BetterAutoChooser.AutoRoutineConfiguration( () -> routine, @@ -102,7 +121,7 @@ public static final BetterAutoChooser.AutoRoutineConfiguration getAutoRoutine( drive.setAutoControllers( V1_DoomSpiralConstants.TRANSLATION_AUTO_GAINS, V1_DoomSpiralConstants.ROTATION_AUTO_GAINS); - V1_DoomSpiralRobotState.setAutoTrajectory(RIGHT_TRENCH_ANTI_BUCKS, RIGHT_RETURN); + V1_DoomSpiralRobotState.setAutoTrajectory(RIGHT_TRENCH_ANTI_BUCKS); })); } } diff --git a/src/main/java/frc/robot/commands/v1_DoomSpiral/autonomous/V1_DoomSpiralAutoRightTrenchAntiBucksCrosses.java b/src/main/java/frc/robot/commands/v1_DoomSpiral/autonomous/V1_DoomSpiralAutoRightTrenchAntiBucksCrosses.java index c9c20e1c..d9041d31 100644 --- a/src/main/java/frc/robot/commands/v1_DoomSpiral/autonomous/V1_DoomSpiralAutoRightTrenchAntiBucksCrosses.java +++ b/src/main/java/frc/robot/commands/v1_DoomSpiral/autonomous/V1_DoomSpiralAutoRightTrenchAntiBucksCrosses.java @@ -2,6 +2,8 @@ import choreo.auto.AutoRoutine; import choreo.auto.AutoTrajectory; +import com.pathplanner.lib.auto.AutoBuilder; +import com.pathplanner.lib.path.PathPlannerPath; import edu.wpi.first.math.geometry.Pose2d; import edu.wpi.first.wpilibj2.command.Commands; import edu.wpi.team190.gompeilib.subsystems.drivebases.swervedrive.SwerveDrive; @@ -16,6 +18,7 @@ import frc.robot.subsystems.v1_DoomSpiral.shooter.V1_DoomSpiralShooter; import frc.robot.subsystems.v1_DoomSpiral.spindexer.V1_DoomSpiralSpindexer; import frc.robot.util.BetterAutoChooser; +import frc.robot.util.Elastic; import java.util.function.Supplier; import org.littletonrobotics.junction.networktables.LoggedNetworkBoolean; @@ -36,8 +39,19 @@ public static final BetterAutoChooser.AutoRoutineConfiguration getAutoRoutine( AutoTrajectory RIGHT_TRENCH_ANTI_BUCKS_CROSSES = routine.trajectory(V1_DoomSpiralAutoTrajectoryCache.RIGHT_TRENCH_ANTI_BUCKS_CROSSES); - AutoTrajectory RIGHT_RETURN = - routine.trajectory(V1_DoomSpiralAutoTrajectoryCache.RIGHT_RETURN_TO_MID); + PathPlannerPath RIGHT_RETURN_PP; + try { + RIGHT_RETURN_PP = PathPlannerPath.fromPathFile("V1_RETURN").mirrorPath(); + } catch (Exception e) { + e.printStackTrace(); + Elastic.sendNotification( + new Elastic.Notification( + Elastic.NotificationLevel.ERROR, "Failed to load V1_RETURN path", e.getMessage())); + RIGHT_RETURN_PP = null; + } + + final PathPlannerPath RIGHT_RETURN_PATH = RIGHT_RETURN_PP; + V1_DoomSpiralAutoTrajectoryCache.GO_BACK_TRIGGER.onTrue( Commands.runOnce(() -> RETURN_TO_MID = returnToMid.get())); @@ -52,6 +66,10 @@ public static final BetterAutoChooser.AutoRoutineConfiguration getAutoRoutine( .onTrue( Commands.sequence( + // Reset the RETURN_TO_MID flag + + Commands.runOnce(() -> RETURN_TO_MID = false), + // Set the inital pose RIGHT_TRENCH_ANTI_BUCKS_CROSSES.resetOdometry(), @@ -88,11 +106,13 @@ public static final BetterAutoChooser.AutoRoutineConfiguration getAutoRoutine( DriveCommands.aimAtHub(drive, V1_DoomSpiralConstants.DRIVE_CONSTANTS), Commands.sequence(Commands.waitSeconds(3.0), intake.agitate())) .until(() -> RETURN_TO_MID), - RIGHT_RETURN - .cmd() - .alongWith( - V1_DoomSpiralCompositeCommands.stopShooterCommand(shooter, spindexer), - intake.collect()))); + RIGHT_RETURN_PATH != null + ? AutoBuilder.followPath(RIGHT_RETURN_PATH) + .alongWith( + V1_DoomSpiralCompositeCommands.stopShooterCommand(shooter, spindexer), + intake.collect()) + : Commands.print("V1_RETURN path unavailable, skipping"), + DriveCommands.stop(drive))); return new BetterAutoChooser.AutoRoutineConfiguration( () -> routine, () -> RIGHT_TRENCH_ANTI_BUCKS_CROSSES.getInitialPose().orElse(new Pose2d()), @@ -102,8 +122,7 @@ public static final BetterAutoChooser.AutoRoutineConfiguration getAutoRoutine( drive.setAutoControllers( V1_DoomSpiralConstants.TRANSLATION_AUTO_GAINS, V1_DoomSpiralConstants.ROTATION_AUTO_GAINS); - V1_DoomSpiralRobotState.setAutoTrajectory( - RIGHT_TRENCH_ANTI_BUCKS_CROSSES, RIGHT_RETURN); + V1_DoomSpiralRobotState.setAutoTrajectory(RIGHT_TRENCH_ANTI_BUCKS_CROSSES); })); } } diff --git a/src/main/java/frc/robot/commands/v1_DoomSpiral/autonomous/V1_DoomSpiralAutoRightTrenchSimple.java b/src/main/java/frc/robot/commands/v1_DoomSpiral/autonomous/V1_DoomSpiralAutoRightTrenchSimple.java index fcd0d18b..0d1ac9eb 100644 --- a/src/main/java/frc/robot/commands/v1_DoomSpiral/autonomous/V1_DoomSpiralAutoRightTrenchSimple.java +++ b/src/main/java/frc/robot/commands/v1_DoomSpiral/autonomous/V1_DoomSpiralAutoRightTrenchSimple.java @@ -2,6 +2,8 @@ import choreo.auto.AutoRoutine; import choreo.auto.AutoTrajectory; +import com.pathplanner.lib.auto.AutoBuilder; +import com.pathplanner.lib.path.PathPlannerPath; import edu.wpi.first.math.geometry.Pose2d; import edu.wpi.first.wpilibj2.command.Commands; import edu.wpi.team190.gompeilib.subsystems.drivebases.swervedrive.SwerveDrive; @@ -16,6 +18,7 @@ import frc.robot.subsystems.v1_DoomSpiral.shooter.V1_DoomSpiralShooter; import frc.robot.subsystems.v1_DoomSpiral.spindexer.V1_DoomSpiralSpindexer; import frc.robot.util.BetterAutoChooser; +import frc.robot.util.Elastic; import java.util.function.Supplier; import org.littletonrobotics.junction.networktables.LoggedNetworkBoolean; @@ -35,23 +38,50 @@ public static final BetterAutoChooser.AutoRoutineConfiguration getAutoRoutine( AutoRoutine routine = drive.getAutoFactory().newRoutine("RIGHT_TRENCH_SIMPLE"); AutoTrajectory RIGHT_TRENCH_SIMPLE = - routine.trajectory(V1_DoomSpiralAutoTrajectoryCache.FAIL_PATH); - AutoTrajectory RIGHT_TRENCH_SIMPLE_REAL = routine.trajectory(V1_DoomSpiralAutoTrajectoryCache.RIGHT_TRENCH_SIMPLE); - AutoTrajectory RIGHT_RETURN = - routine.trajectory(V1_DoomSpiralAutoTrajectoryCache.RIGHT_RETURN_TO_MID); + + PathPlannerPath V1_SIMPLE; + try { + V1_SIMPLE = PathPlannerPath.fromPathFile("V1_SIMPLE").mirrorPath(); + } catch (Exception e) { + e.printStackTrace(); + Elastic.sendNotification( + new Elastic.Notification( + Elastic.NotificationLevel.ERROR, "Failed to load V1_SIMPLE path", e.getMessage())); + V1_SIMPLE = null; + } + + final PathPlannerPath V1_SIMPLE_PATH = V1_SIMPLE; + + PathPlannerPath RIGHT_RETURN_PP; + try { + RIGHT_RETURN_PP = PathPlannerPath.fromPathFile("V1_RETURN").mirrorPath(); + } catch (Exception e) { + e.printStackTrace(); + Elastic.sendNotification( + new Elastic.Notification( + Elastic.NotificationLevel.ERROR, "Failed to load V1_RETURN path", e.getMessage())); + RIGHT_RETURN_PP = null; + } + + final PathPlannerPath LEFT_RETURN_PATH = RIGHT_RETURN_PP; + V1_DoomSpiralAutoTrajectoryCache.GO_BACK_TRIGGER.onTrue( Commands.runOnce(() -> RETURN_TO_MID = returnToMid.get())); AdjustPathCommand followCommand = new AdjustPathCommand( - () -> RIGHT_TRENCH_SIMPLE_REAL.getFinalPose().get(), 0, pathAdjustmentModeSupplier); + () -> RIGHT_TRENCH_SIMPLE.getFinalPose().get(), 0, pathAdjustmentModeSupplier); routine .active() .onTrue( Commands.sequence( + // Reset the RETURN_TO_MID flag + + Commands.runOnce(() -> RETURN_TO_MID = false), + // Set the inital pose RIGHT_TRENCH_SIMPLE.resetOdometry(), @@ -68,7 +98,7 @@ public static final BetterAutoChooser.AutoRoutineConfiguration getAutoRoutine( followCommand.onlyWhile( () -> { Pose2d currentPose = V1_DoomSpiralRobotState.getGlobalPose(); - Pose2d targetPose = RIGHT_TRENCH_SIMPLE_REAL.getFinalPose().get(); + Pose2d targetPose = RIGHT_TRENCH_SIMPLE.getFinalPose().get(); double distanceToTarget = currentPose.getTranslation().getDistance(targetPose.getTranslation()); boolean isFinished = @@ -76,6 +106,13 @@ public static final BetterAutoChooser.AutoRoutineConfiguration getAutoRoutine( < V1_DoomSpiralConstants.AUTO_CORRECTION_THRESHOLD_METERS; return !isFinished; }), + V1_SIMPLE_PATH != null + ? AutoBuilder.followPath(V1_SIMPLE_PATH) + .alongWith( + V1_DoomSpiralCompositeCommands.stopShooterCommand(shooter, spindexer), + intake.collect()) + : Commands.print("V1_DOT path unavailable, skipping"), + // Stop drive Commands.runOnce(() -> drive.stop()), @@ -87,11 +124,13 @@ public static final BetterAutoChooser.AutoRoutineConfiguration getAutoRoutine( DriveCommands.aimAtHub(drive, V1_DoomSpiralConstants.DRIVE_CONSTANTS), Commands.sequence(Commands.waitSeconds(3.0), intake.agitate())) .until(() -> RETURN_TO_MID), - RIGHT_RETURN - .cmd() - .alongWith( - V1_DoomSpiralCompositeCommands.stopShooterCommand(shooter, spindexer), - intake.collect()))); + LEFT_RETURN_PATH != null + ? AutoBuilder.followPath(LEFT_RETURN_PATH) + .alongWith( + V1_DoomSpiralCompositeCommands.stopShooterCommand(shooter, spindexer), + intake.collect()) + : Commands.print("V1_RETURN path unavailable, skipping"), + DriveCommands.stop(drive))); return new BetterAutoChooser.AutoRoutineConfiguration( () -> routine, @@ -102,7 +141,7 @@ public static final BetterAutoChooser.AutoRoutineConfiguration getAutoRoutine( drive.setAutoControllers( V1_DoomSpiralConstants.TRANSLATION_AUTO_GAINS, V1_DoomSpiralConstants.ROTATION_AUTO_GAINS); - V1_DoomSpiralRobotState.setAutoTrajectory(RIGHT_TRENCH_SIMPLE, RIGHT_RETURN); + V1_DoomSpiralRobotState.setAutoTrajectory(RIGHT_TRENCH_SIMPLE); })); } } diff --git a/src/main/java/frc/robot/commands/v1_DoomSpiral/autonomous/V1_DoomSpiralAutoRightTrenchSimpleCrosses.java b/src/main/java/frc/robot/commands/v1_DoomSpiral/autonomous/V1_DoomSpiralAutoRightTrenchSimpleCrosses.java index 06e3c67f..c7238627 100644 --- a/src/main/java/frc/robot/commands/v1_DoomSpiral/autonomous/V1_DoomSpiralAutoRightTrenchSimpleCrosses.java +++ b/src/main/java/frc/robot/commands/v1_DoomSpiral/autonomous/V1_DoomSpiralAutoRightTrenchSimpleCrosses.java @@ -2,6 +2,8 @@ import choreo.auto.AutoRoutine; import choreo.auto.AutoTrajectory; +import com.pathplanner.lib.auto.AutoBuilder; +import com.pathplanner.lib.path.PathPlannerPath; import edu.wpi.first.math.geometry.Pose2d; import edu.wpi.first.wpilibj2.command.Commands; import edu.wpi.team190.gompeilib.subsystems.drivebases.swervedrive.SwerveDrive; @@ -16,6 +18,7 @@ import frc.robot.subsystems.v1_DoomSpiral.shooter.V1_DoomSpiralShooter; import frc.robot.subsystems.v1_DoomSpiral.spindexer.V1_DoomSpiralSpindexer; import frc.robot.util.BetterAutoChooser; +import frc.robot.util.Elastic; import java.util.function.Supplier; import org.littletonrobotics.junction.networktables.LoggedNetworkBoolean; @@ -36,8 +39,19 @@ public static final BetterAutoChooser.AutoRoutineConfiguration getAutoRoutine( AutoTrajectory RIGHT_TRENCH_SIMPLE_CROSSES = routine.trajectory(V1_DoomSpiralAutoTrajectoryCache.RIGHT_TRENCH_SIMPLE_CROSSES); - AutoTrajectory RIGHT_RETURN = - routine.trajectory(V1_DoomSpiralAutoTrajectoryCache.RIGHT_RETURN_TO_MID); + PathPlannerPath RIGHT_RETURN_PP; + try { + RIGHT_RETURN_PP = PathPlannerPath.fromPathFile("V1_RETURN").mirrorPath(); + } catch (Exception e) { + e.printStackTrace(); + Elastic.sendNotification( + new Elastic.Notification( + Elastic.NotificationLevel.ERROR, "Failed to load V1_RETURN path", e.getMessage())); + RIGHT_RETURN_PP = null; + } + + final PathPlannerPath RIGHT_RETURN_PATH = RIGHT_RETURN_PP; + V1_DoomSpiralAutoTrajectoryCache.GO_BACK_TRIGGER.onTrue( Commands.runOnce(() -> RETURN_TO_MID = returnToMid.get())); @@ -50,6 +64,10 @@ public static final BetterAutoChooser.AutoRoutineConfiguration getAutoRoutine( .onTrue( Commands.sequence( + // Reset the RETURN_TO_MID flag + + Commands.runOnce(() -> RETURN_TO_MID = false), + // Set the inital pose RIGHT_TRENCH_SIMPLE_CROSSES.resetOdometry(), @@ -86,11 +104,13 @@ public static final BetterAutoChooser.AutoRoutineConfiguration getAutoRoutine( DriveCommands.aimAtHub(drive, V1_DoomSpiralConstants.DRIVE_CONSTANTS), Commands.sequence(Commands.waitSeconds(3.0), intake.agitate())) .until(() -> RETURN_TO_MID), - RIGHT_RETURN - .cmd() - .alongWith( - V1_DoomSpiralCompositeCommands.stopShooterCommand(shooter, spindexer), - intake.collect()))); + RIGHT_RETURN_PATH != null + ? AutoBuilder.followPath(RIGHT_RETURN_PATH) + .alongWith( + V1_DoomSpiralCompositeCommands.stopShooterCommand(shooter, spindexer), + intake.collect()) + : Commands.print("V1_RETURN path unavailable, skipping"), + DriveCommands.stop(drive))); return new BetterAutoChooser.AutoRoutineConfiguration( () -> routine, @@ -101,8 +121,7 @@ public static final BetterAutoChooser.AutoRoutineConfiguration getAutoRoutine( drive.setAutoControllers( V1_DoomSpiralConstants.TRANSLATION_AUTO_GAINS, V1_DoomSpiralConstants.ROTATION_AUTO_GAINS); - V1_DoomSpiralRobotState.setAutoTrajectory( - RIGHT_TRENCH_SIMPLE_CROSSES, RIGHT_RETURN); + V1_DoomSpiralRobotState.setAutoTrajectory(RIGHT_TRENCH_SIMPLE_CROSSES); })); } } diff --git a/src/main/java/frc/robot/commands/v1_DoomSpiral/autonomous/V1_DoomSpiralAutoTrajectoryCache.java b/src/main/java/frc/robot/commands/v1_DoomSpiral/autonomous/V1_DoomSpiralAutoTrajectoryCache.java index 26205713..52912e27 100644 --- a/src/main/java/frc/robot/commands/v1_DoomSpiral/autonomous/V1_DoomSpiralAutoTrajectoryCache.java +++ b/src/main/java/frc/robot/commands/v1_DoomSpiral/autonomous/V1_DoomSpiralAutoTrajectoryCache.java @@ -38,8 +38,7 @@ public class V1_DoomSpiralAutoTrajectoryCache { Choreo.loadTrajectory("LEFT_RETURN_TO_MID").get(); public static final Trajectory RIGHT_RETURN_TO_MID = Choreo.loadTrajectory("RIGHT_RETURN_TO_MID").get(); - public static final Trajectory FAIL_PATH = Choreo.loadTrajectory("FAIL_PATH").get(); public static final Trigger GO_BACK_TRIGGER = - RobotModeTriggers.autonomous().debounce(17.5, Debouncer.DebounceType.kRising); + RobotModeTriggers.autonomous().debounce(16, Debouncer.DebounceType.kRising); } diff --git a/src/main/java/frc/robot/commands/v2_Turnover/V2_TurnoverCompositeCommands.java b/src/main/java/frc/robot/commands/v2_Turnover/V2_TurnoverCompositeCommands.java index 262c40ca..7ed9982f 100644 --- a/src/main/java/frc/robot/commands/v2_Turnover/V2_TurnoverCompositeCommands.java +++ b/src/main/java/frc/robot/commands/v2_Turnover/V2_TurnoverCompositeCommands.java @@ -14,6 +14,7 @@ import frc.robot.subsystems.v2_Turnover.shooter.V2_TurnoverShooterConstants.ShooterGoal; import frc.robot.subsystems.v2_Turnover.shooter.V2_TurnoverShotCalculator; import frc.robot.util.command.ContinuousConditionalCommand; +import java.util.function.BooleanSupplier; import org.littletonrobotics.junction.Logger; public class V2_TurnoverCompositeCommands { @@ -41,9 +42,12 @@ public static Command holdAndIntake( private static boolean toggleShouldHold = false; - public static Command toggleHold(V2_TurnoverClopper clopper, V2_TurnoverShooter shooter) { + public static Command toggleHold( + V2_TurnoverClopper clopper, V2_TurnoverShooter shooter, BooleanSupplier invert) { return Commands.either( - hold(clopper, shooter), scoreOrFeedCommand(shooter, clopper), () -> toggleShouldHold) + hold(clopper, shooter), + scoreOrFeedCommand(shooter, clopper, invert), + () -> toggleShouldHold) .beforeStarting( () -> { toggleShouldHold = !toggleShouldHold; @@ -51,10 +55,18 @@ public static Command toggleHold(V2_TurnoverClopper clopper, V2_TurnoverShooter }); } - public static Command scoreOrFeedCommand(V2_TurnoverShooter shooter, V2_TurnoverClopper clopper) { + public static Command scoreOrFeedCommand( + V2_TurnoverShooter shooter, V2_TurnoverClopper clopper, BooleanSupplier invert) { return Commands.parallel( shooter.setGoal( - () -> V2_TurnoverRobotState.isInAllianceZone() ? ShooterGoal.SCORE : ShooterGoal.FEED), + () -> + invert.getAsBoolean() + ? (V2_TurnoverRobotState.isInAllianceZone() + ? ShooterGoal.FEED + : ShooterGoal.SCORE) + : (V2_TurnoverRobotState.isInAllianceZone() + ? ShooterGoal.SCORE + : ShooterGoal.FEED)), runHopperWhenReady(shooter, clopper)); } diff --git a/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoDepot.java b/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoDepot.java index f90f50e8..0ce143b7 100644 --- a/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoDepot.java +++ b/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoDepot.java @@ -15,6 +15,7 @@ import frc.robot.subsystems.v2_Turnover.shooter.V2_TurnoverShooterConstants.ShooterGoal; import frc.robot.util.AllianceFlipUtil; import frc.robot.util.Elastic; +import java.util.function.BooleanSupplier; public class V2_TurnoverAutoDepot { public static Command getAutoRoutine( @@ -27,6 +28,7 @@ public static Command getAutoRoutine( .negate() .onTrue(intake.stopRollerOverride().alongWith(intake.deploy()).ignoringDisable(true)); + BooleanSupplier invertScoreLocation = () -> false; return Commands.sequence( Commands.runOnce( () -> @@ -38,7 +40,8 @@ public static Command getAutoRoutine( AutoBuilder.followPath(DEPOT), V2_TurnoverCompositeCommands.hold(clopper, shooter)), drive.runOnce(drive::stop), Commands.parallel( - V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper), + V2_TurnoverCompositeCommands.scoreOrFeedCommand( + shooter, clopper, invertScoreLocation), Commands.sequence( Commands.waitSeconds(4.0), intake.stopRollerOverride(), diff --git a/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoFollowDepot.java b/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoFollowDepot.java index a17c857d..61e94de3 100644 --- a/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoFollowDepot.java +++ b/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoFollowDepot.java @@ -15,6 +15,7 @@ import frc.robot.util.AllianceFlipUtil; import frc.robot.util.Elastic; import frc.robot.util.command.AutoCommandBuilder; +import java.util.function.BooleanSupplier; import java.util.function.Supplier; public class V2_TurnoverAutoFollowDepot { @@ -42,6 +43,7 @@ public static Command getAutoRoutine( RobotModeTriggers.autonomous() .negate() .onTrue(intake.stopRollerOverride().alongWith(intake.deploy()).ignoringDisable(true)); + BooleanSupplier invertScoreLocation = () -> false; return Commands.sequence( Commands.runOnce( () -> @@ -50,7 +52,7 @@ public static Command getAutoRoutine( intake.deploy(), AutoCommandBuilder.sequence( AutoBuilder.followPath(FOLLOW_DEPOT_1), AutoBuilder.followPath(FOLLOW_DEPOT_2)), - V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper)); + V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper, invertScoreLocation)); } catch (Exception e) { e.printStackTrace(); diff --git a/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoFollowDepotBC.java b/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoFollowDepotBC.java new file mode 100644 index 00000000..d614617d --- /dev/null +++ b/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoFollowDepotBC.java @@ -0,0 +1,68 @@ +package frc.robot.commands.v2_Turnover.autonomous; + +import com.pathplanner.lib.auto.AutoBuilder; +import com.pathplanner.lib.path.PathPlannerPath; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.Commands; +import edu.wpi.first.wpilibj2.command.button.RobotModeTriggers; +import edu.wpi.team190.gompeilib.subsystems.drivebases.swervedrive.SwerveDrive; +import frc.robot.commands.shared.AdjustPathCommand; +import frc.robot.commands.shared.DriveCommands; +import frc.robot.commands.v2_Turnover.V2_TurnoverCompositeCommands; +import frc.robot.subsystems.shared.intake.Intake; +import frc.robot.subsystems.v2_Turnover.V2_TurnoverRobotState; +import frc.robot.subsystems.v2_Turnover.clopper.V2_TurnoverClopper; +import frc.robot.subsystems.v2_Turnover.shooter.V2_TurnoverShooter; +import frc.robot.util.AllianceFlipUtil; +import frc.robot.util.Elastic; +import frc.robot.util.command.AutoCommandBuilder; +import java.util.function.Supplier; + +public class V2_TurnoverAutoFollowDepotBC { + public static Command getAutoRoutine( + SwerveDrive drive, + Intake intake, + V2_TurnoverClopper clopper, + V2_TurnoverShooter shooter, + Supplier pathAdjustmentModeSupplier) { + + try { + PathPlannerPath FOLLOW_DEPOT_1 = PathPlannerPath.fromPathFile("FOLLOW_FEED_1"); + PathPlannerPath FOLLOW_DEPOT_2 = PathPlannerPath.fromPathFile("FOLLOW_DEPOT_2_BC"); + AdjustPathCommand adjustPathCommand1 = + new AdjustPathCommand( + () -> FOLLOW_DEPOT_1.getPathPoses().get(FOLLOW_DEPOT_1.getPathPoses().size() - 1), + 0, + pathAdjustmentModeSupplier); + AdjustPathCommand adjustPathCommand2 = + new AdjustPathCommand( + () -> FOLLOW_DEPOT_2.getPathPoses().get(FOLLOW_DEPOT_2.getPathPoses().size() - 1), + 0, + pathAdjustmentModeSupplier); + + RobotModeTriggers.autonomous() + .negate() + .onTrue(intake.stopRollerOverride().alongWith(intake.deploy()).ignoringDisable(true)); + return Commands.sequence( + Commands.runOnce( + () -> + V2_TurnoverRobotState.resetPose( + AllianceFlipUtil.apply(FOLLOW_DEPOT_1.getStartingHolonomicPose().get()))), + intake.deploy(), + AutoCommandBuilder.sequence( + AutoBuilder.followPath(FOLLOW_DEPOT_1), AutoBuilder.followPath(FOLLOW_DEPOT_2)), + DriveCommands.stop(drive).alongWith(V2_TurnoverCompositeCommands.hold(clopper, shooter))); + + } catch (Exception e) { + e.printStackTrace(); + return Commands.runOnce( + () -> + Elastic.sendNotification( + new Elastic.Notification( + Elastic.NotificationLevel.ERROR, + "Failed to load auto path", + e.getMessage()))) + .ignoringDisable(true); + } + } +} diff --git a/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoFollowFeedFullLeft.java b/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoFollowFeedFullLeft.java index def8ba10..5e6bd17e 100644 --- a/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoFollowFeedFullLeft.java +++ b/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoFollowFeedFullLeft.java @@ -15,6 +15,7 @@ import frc.robot.util.AllianceFlipUtil; import frc.robot.util.Elastic; import frc.robot.util.command.AutoCommandBuilder; +import java.util.function.BooleanSupplier; import java.util.function.Supplier; public class V2_TurnoverAutoFollowFeedFullLeft { @@ -42,6 +43,7 @@ public static Command getAutoRoutine( RobotModeTriggers.autonomous() .negate() .onTrue(intake.stopRollerOverride().alongWith(intake.deploy()).ignoringDisable(true)); + BooleanSupplier invertScoreLocation = () -> false; return Commands.sequence( Commands.runOnce( () -> @@ -50,7 +52,7 @@ public static Command getAutoRoutine( intake.deploy(), AutoCommandBuilder.sequence( AutoBuilder.followPath(FOLLOW_1), AutoBuilder.followPath(FOLLOW_2)), - V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper)); + V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper, invertScoreLocation)); } catch (Exception e) { e.printStackTrace(); diff --git a/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoFollowFeedFullRight.java b/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoFollowFeedFullRight.java index 62affb1b..a10407e4 100644 --- a/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoFollowFeedFullRight.java +++ b/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoFollowFeedFullRight.java @@ -15,6 +15,7 @@ import frc.robot.util.AllianceFlipUtil; import frc.robot.util.Elastic; import frc.robot.util.command.AutoCommandBuilder; +import java.util.function.BooleanSupplier; import java.util.function.Supplier; public class V2_TurnoverAutoFollowFeedFullRight { @@ -42,6 +43,7 @@ public static Command getAutoRoutine( RobotModeTriggers.autonomous() .negate() .onTrue(intake.stopRollerOverride().alongWith(intake.deploy()).ignoringDisable(true)); + BooleanSupplier invertScoreLocation = () -> false; return Commands.sequence( Commands.runOnce( () -> @@ -50,7 +52,7 @@ public static Command getAutoRoutine( intake.deploy(), AutoCommandBuilder.sequence( AutoBuilder.followPath(FOLLOW_1), AutoBuilder.followPath(FOLLOW_2)), - V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper)); + V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper, invertScoreLocation)); } catch (Exception e) { e.printStackTrace(); diff --git a/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoFollowFeedMiddleLeft.java b/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoFollowFeedMiddleLeft.java index e819c022..6487987d 100644 --- a/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoFollowFeedMiddleLeft.java +++ b/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoFollowFeedMiddleLeft.java @@ -15,6 +15,7 @@ import frc.robot.util.AllianceFlipUtil; import frc.robot.util.Elastic; import frc.robot.util.command.AutoCommandBuilder; +import java.util.function.BooleanSupplier; import java.util.function.Supplier; public class V2_TurnoverAutoFollowFeedMiddleLeft { @@ -42,6 +43,7 @@ public static Command getAutoRoutine( RobotModeTriggers.autonomous() .negate() .onTrue(intake.stopRollerOverride().alongWith(intake.deploy()).ignoringDisable(true)); + BooleanSupplier invertScoreLocation = () -> false; return Commands.sequence( Commands.runOnce( () -> @@ -50,7 +52,7 @@ public static Command getAutoRoutine( intake.deploy(), AutoCommandBuilder.sequence( AutoBuilder.followPath(FOLLOW_1), AutoBuilder.followPath(FOLLOW_2)), - V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper)); + V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper, invertScoreLocation)); } catch (Exception e) { e.printStackTrace(); diff --git a/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoFollowFeedMiddleRight.java b/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoFollowFeedMiddleRight.java index a35a810b..c5af2d2e 100644 --- a/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoFollowFeedMiddleRight.java +++ b/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoFollowFeedMiddleRight.java @@ -15,6 +15,7 @@ import frc.robot.util.AllianceFlipUtil; import frc.robot.util.Elastic; import frc.robot.util.command.AutoCommandBuilder; +import java.util.function.BooleanSupplier; import java.util.function.Supplier; public class V2_TurnoverAutoFollowFeedMiddleRight { @@ -42,6 +43,7 @@ public static Command getAutoRoutine( RobotModeTriggers.autonomous() .negate() .onTrue(intake.stopRollerOverride().alongWith(intake.deploy()).ignoringDisable(true)); + BooleanSupplier invertScoreLocation = () -> false; return Commands.sequence( Commands.runOnce( () -> @@ -50,7 +52,7 @@ public static Command getAutoRoutine( intake.deploy(), AutoCommandBuilder.sequence( AutoBuilder.followPath(FOLLOW_1), AutoBuilder.followPath(FOLLOW_2)), - V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper)); + V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper, invertScoreLocation)); } catch (Exception e) { e.printStackTrace(); diff --git a/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoLeftHalf.java b/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoLeftHalf.java index 72d0d7d3..97674539 100644 --- a/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoLeftHalf.java +++ b/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoLeftHalf.java @@ -14,6 +14,7 @@ import frc.robot.subsystems.v2_Turnover.shooter.V2_TurnoverShooter; import frc.robot.util.AllianceFlipUtil; import frc.robot.util.Elastic; +import java.util.function.BooleanSupplier; import java.util.function.Supplier; public class V2_TurnoverAutoLeftHalf { @@ -35,6 +36,7 @@ public static Command getAutoRoutine( RobotModeTriggers.autonomous() .negate() .onTrue(intake.stopRollerOverride().alongWith(intake.deploy()).ignoringDisable(true)); + BooleanSupplier invertScoreLocation = () -> false; return Commands.sequence( Commands.runOnce( () -> @@ -42,7 +44,7 @@ public static Command getAutoRoutine( AllianceFlipUtil.apply(HALF.getStartingHolonomicPose().get()))), intake.deploy().alongWith(intake.setOverrideRollerVoltage(11)), AutoBuilder.followPath(HALF), - V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper)); + V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper, invertScoreLocation)); } catch (Exception e) { e.printStackTrace(); diff --git a/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoLeftOP.java b/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoLeftOP.java index a40e8de6..189e0b87 100644 --- a/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoLeftOP.java +++ b/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoLeftOP.java @@ -15,6 +15,7 @@ import frc.robot.subsystems.v2_Turnover.shooter.V2_TurnoverShooterConstants; import frc.robot.util.AllianceFlipUtil; import frc.robot.util.Elastic; +import java.util.function.BooleanSupplier; import java.util.function.Supplier; public class V2_TurnoverAutoLeftOP { @@ -46,6 +47,7 @@ public static Command getAutoRoutine( .negate() .onTrue(intake.stopRollerOverride().alongWith(intake.deploy()).ignoringDisable(true)); + BooleanSupplier invertScoreLocation = () -> false; return Commands.sequence( Commands.runOnce( () -> @@ -56,11 +58,11 @@ public static Command getAutoRoutine( AutoBuilder.followPath(OP_2) .alongWith( Commands.sequence( - Commands.waitSeconds(6.72), + Commands.waitSeconds(7.8), shooter.setNonRequiringGoal(V2_TurnoverShooterConstants.ShooterGoal.STOW), clopper.stopBallTunnel(), clopper.stopRollerFloor())), - V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper)); + V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper, invertScoreLocation)); } catch (Exception e) { e.printStackTrace(); return Commands.runOnce( diff --git a/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoLeftOPBC.java b/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoLeftOPBC.java new file mode 100644 index 00000000..8545cd2f --- /dev/null +++ b/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoLeftOPBC.java @@ -0,0 +1,77 @@ +package frc.robot.commands.v2_Turnover.autonomous; + +import com.pathplanner.lib.auto.AutoBuilder; +import com.pathplanner.lib.path.PathPlannerPath; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.Commands; +import edu.wpi.first.wpilibj2.command.button.RobotModeTriggers; +import edu.wpi.team190.gompeilib.subsystems.drivebases.swervedrive.SwerveDrive; +import frc.robot.commands.shared.AdjustPathCommand; +import frc.robot.commands.shared.DriveCommands; +import frc.robot.commands.v2_Turnover.V2_TurnoverCompositeCommands; +import frc.robot.subsystems.shared.intake.Intake; +import frc.robot.subsystems.v2_Turnover.V2_TurnoverRobotState; +import frc.robot.subsystems.v2_Turnover.clopper.V2_TurnoverClopper; +import frc.robot.subsystems.v2_Turnover.shooter.V2_TurnoverShooter; +import frc.robot.subsystems.v2_Turnover.shooter.V2_TurnoverShooterConstants; +import frc.robot.util.AllianceFlipUtil; +import frc.robot.util.Elastic; +import java.util.function.Supplier; + +public class V2_TurnoverAutoLeftOPBC { + public static Command getAutoRoutine( + SwerveDrive drive, + Intake intake, + V2_TurnoverClopper clopper, + V2_TurnoverShooter shooter, + Supplier pathAdjustmentModeSupplier) { + + try { + + PathPlannerPath OP_1 = PathPlannerPath.fromPathFile("OP_1"); + PathPlannerPath OP_2 = PathPlannerPath.fromPathFile("OP_2_BC"); + + AdjustPathCommand followCommandOP_1 = + new AdjustPathCommand( + () -> OP_1.getPathPoses().get(OP_1.getPathPoses().size() - 1), + 0, + pathAdjustmentModeSupplier); + + AdjustPathCommand followCommandOP_2 = + new AdjustPathCommand( + () -> OP_2.getPathPoses().get(OP_2.getPathPoses().size() - 1), + 0, + pathAdjustmentModeSupplier); + + RobotModeTriggers.autonomous() + .negate() + .onTrue(intake.stopRollerOverride().alongWith(intake.deploy()).ignoringDisable(true)); + + return Commands.sequence( + Commands.runOnce( + () -> + V2_TurnoverRobotState.resetPose( + AllianceFlipUtil.apply(OP_1.getStartingHolonomicPose().get()))), + intake.deploy().alongWith(intake.setOverrideRollerVoltage(11)), + AutoBuilder.followPath(OP_1), + AutoBuilder.followPath(OP_2) + .alongWith( + Commands.sequence( + Commands.waitSeconds(6.72), + shooter.setNonRequiringGoal(V2_TurnoverShooterConstants.ShooterGoal.STOW), + clopper.stopBallTunnel(), + clopper.stopRollerFloor())), + V2_TurnoverCompositeCommands.hold(clopper, shooter).alongWith(DriveCommands.stop(drive))); + } catch (Exception e) { + e.printStackTrace(); + return Commands.runOnce( + () -> + Elastic.sendNotification( + new Elastic.Notification( + Elastic.NotificationLevel.ERROR, + "Failed to load auto path", + e.getMessage()))) + .ignoringDisable(true); + } + } +} diff --git a/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoLeftOPBucks.java b/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoLeftOPBucks.java index 3f05a73f..83970d6b 100644 --- a/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoLeftOPBucks.java +++ b/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoLeftOPBucks.java @@ -15,6 +15,7 @@ import frc.robot.subsystems.v2_Turnover.shooter.V2_TurnoverShooterConstants; import frc.robot.util.AllianceFlipUtil; import frc.robot.util.Elastic; +import java.util.function.BooleanSupplier; import java.util.function.Supplier; public class V2_TurnoverAutoLeftOPBucks { @@ -45,6 +46,7 @@ public static Command getAutoRoutine( .negate() .onTrue(intake.stopRollerOverride().alongWith(intake.deploy()).ignoringDisable(true)); + BooleanSupplier invertScoreLocation = () -> false; return Commands.sequence( Commands.runOnce( () -> @@ -59,7 +61,7 @@ public static Command getAutoRoutine( shooter.setNonRequiringGoal(V2_TurnoverShooterConstants.ShooterGoal.STOW), clopper.stopBallTunnel(), clopper.stopRollerFloor())), - V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper)); + V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper, invertScoreLocation)); } catch (Exception e) { e.printStackTrace(); return Commands.runOnce( diff --git a/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoRightHalf.java b/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoRightHalf.java index ee4635cd..279fabd1 100644 --- a/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoRightHalf.java +++ b/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoRightHalf.java @@ -15,6 +15,7 @@ import frc.robot.subsystems.v2_Turnover.clopper.V2_TurnoverClopper; import frc.robot.subsystems.v2_Turnover.shooter.V2_TurnoverShooter; import frc.robot.util.AllianceFlipUtil; +import java.util.function.BooleanSupplier; import java.util.function.Supplier; public class V2_TurnoverAutoRightHalf { @@ -41,6 +42,7 @@ public static Command getAutoRoutine( () -> RIGHT_HALF_2.getPathPoses().get(RIGHT_HALF_2.getPathPoses().size() - 1), 0, pathAdjustmentModeSupplier); + BooleanSupplier invertScoreLocation = () -> false; return Commands.parallel( Commands.sequence( Commands.runOnce( @@ -78,11 +80,13 @@ public static Command getAutoRoutine( Commands.sequence( V2_TurnoverCompositeCommands.hold(clopper, shooter).withTimeout(3), intake.stopRollerOverride(), - V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper).withTimeout(5), + V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper, invertScoreLocation) + .withTimeout(5), intake.setOverrideRollerVoltage(IntakeConstants.INTAKE_VOLTAGE), V2_TurnoverCompositeCommands.hold(clopper, shooter).withTimeout(3), intake.stopRollerOverride(), - V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper))); + V2_TurnoverCompositeCommands.scoreOrFeedCommand( + shooter, clopper, invertScoreLocation))); } catch (Exception e) { e.printStackTrace(); return Commands.none(); diff --git a/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoRightOP.java b/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoRightOP.java index 58884b77..9485cf85 100644 --- a/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoRightOP.java +++ b/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoRightOP.java @@ -15,6 +15,7 @@ import frc.robot.subsystems.v2_Turnover.shooter.V2_TurnoverShooterConstants; import frc.robot.util.AllianceFlipUtil; import frc.robot.util.Elastic; +import java.util.function.BooleanSupplier; import java.util.function.Supplier; public class V2_TurnoverAutoRightOP { @@ -45,6 +46,7 @@ public static Command getAutoRoutine( .negate() .onTrue(intake.stopRollerOverride().alongWith(intake.deploy()).ignoringDisable(true)); + BooleanSupplier invertScoreLocation = () -> false; return Commands.sequence( Commands.runOnce( () -> @@ -55,11 +57,11 @@ public static Command getAutoRoutine( AutoBuilder.followPath(OP_2) .alongWith( Commands.sequence( - Commands.waitSeconds(6.72), + Commands.waitSeconds(7.8), shooter.setNonRequiringGoal(V2_TurnoverShooterConstants.ShooterGoal.STOW), clopper.stopBallTunnel(), clopper.stopRollerFloor())), - V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper)); + V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper, invertScoreLocation)); } catch (Exception e) { e.printStackTrace(); return Commands.runOnce( diff --git a/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoRightOPBC.java b/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoRightOPBC.java new file mode 100644 index 00000000..c2c39cd0 --- /dev/null +++ b/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoRightOPBC.java @@ -0,0 +1,77 @@ +package frc.robot.commands.v2_Turnover.autonomous; + +import com.pathplanner.lib.auto.AutoBuilder; +import com.pathplanner.lib.path.PathPlannerPath; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.Commands; +import edu.wpi.first.wpilibj2.command.button.RobotModeTriggers; +import edu.wpi.team190.gompeilib.subsystems.drivebases.swervedrive.SwerveDrive; +import frc.robot.commands.shared.AdjustPathCommand; +import frc.robot.commands.shared.DriveCommands; +import frc.robot.commands.v2_Turnover.V2_TurnoverCompositeCommands; +import frc.robot.subsystems.shared.intake.Intake; +import frc.robot.subsystems.v2_Turnover.V2_TurnoverRobotState; +import frc.robot.subsystems.v2_Turnover.clopper.V2_TurnoverClopper; +import frc.robot.subsystems.v2_Turnover.shooter.V2_TurnoverShooter; +import frc.robot.subsystems.v2_Turnover.shooter.V2_TurnoverShooterConstants; +import frc.robot.util.AllianceFlipUtil; +import frc.robot.util.Elastic; +import java.util.function.Supplier; + +public class V2_TurnoverAutoRightOPBC { + public static Command getAutoRoutine( + SwerveDrive drive, + Intake intake, + V2_TurnoverClopper clopper, + V2_TurnoverShooter shooter, + Supplier pathAdjustmentModeSupplier) { + + try { + + PathPlannerPath OP_1 = PathPlannerPath.fromPathFile("OP_1").mirrorPath(); + PathPlannerPath OP_2 = PathPlannerPath.fromPathFile("OP_2_BC").mirrorPath(); + + AdjustPathCommand followCommandOP_1 = + new AdjustPathCommand( + () -> OP_1.getPathPoses().get(OP_1.getPathPoses().size() - 1), + 0, + pathAdjustmentModeSupplier); + + AdjustPathCommand followCommandOP_2 = + new AdjustPathCommand( + () -> OP_2.getPathPoses().get(OP_2.getPathPoses().size() - 1), + 0, + pathAdjustmentModeSupplier); + + RobotModeTriggers.autonomous() + .negate() + .onTrue(intake.stopRollerOverride().alongWith(intake.deploy()).ignoringDisable(true)); + + return Commands.sequence( + Commands.runOnce( + () -> + V2_TurnoverRobotState.resetPose( + AllianceFlipUtil.apply(OP_1.getStartingHolonomicPose().get()))), + intake.deploy().alongWith(intake.setOverrideRollerVoltage(11)), + AutoBuilder.followPath(OP_1), + AutoBuilder.followPath(OP_2) + .alongWith( + Commands.sequence( + Commands.waitSeconds(6.72), + shooter.setNonRequiringGoal(V2_TurnoverShooterConstants.ShooterGoal.STOW), + clopper.stopBallTunnel(), + clopper.stopRollerFloor())), + V2_TurnoverCompositeCommands.hold(clopper, shooter).alongWith(DriveCommands.stop(drive))); + } catch (Exception e) { + e.printStackTrace(); + return Commands.runOnce( + () -> + Elastic.sendNotification( + new Elastic.Notification( + Elastic.NotificationLevel.ERROR, + "Failed to load auto path", + e.getMessage()))) + .ignoringDisable(true); + } + } +} diff --git a/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoRightOPBucks.java b/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoRightOPBucks.java index 58f92923..a21124de 100644 --- a/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoRightOPBucks.java +++ b/src/main/java/frc/robot/commands/v2_Turnover/autonomous/V2_TurnoverAutoRightOPBucks.java @@ -15,6 +15,7 @@ import frc.robot.subsystems.v2_Turnover.shooter.V2_TurnoverShooterConstants; import frc.robot.util.AllianceFlipUtil; import frc.robot.util.Elastic; +import java.util.function.BooleanSupplier; import java.util.function.Supplier; public class V2_TurnoverAutoRightOPBucks { @@ -44,6 +45,7 @@ public static Command getAutoRoutine( .negate() .onTrue(intake.stopRollerOverride().alongWith(intake.deploy()).ignoringDisable(true)); + BooleanSupplier invertScoreLocation = () -> false; return Commands.sequence( Commands.runOnce( () -> @@ -58,7 +60,7 @@ public static Command getAutoRoutine( shooter.setNonRequiringGoal(V2_TurnoverShooterConstants.ShooterGoal.STOW), clopper.stopBallTunnel(), clopper.stopRollerFloor())), - V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper)); + V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper, invertScoreLocation)); } catch (Exception e) { e.printStackTrace(); return Commands.runOnce( diff --git a/src/main/java/frc/robot/subsystems/shared/climber/ClimberConstants.java b/src/main/java/frc/robot/subsystems/shared/climber/ClimberConstants.java index 53360ad4..ada91278 100644 --- a/src/main/java/frc/robot/subsystems/shared/climber/ClimberConstants.java +++ b/src/main/java/frc/robot/subsystems/shared/climber/ClimberConstants.java @@ -109,7 +109,7 @@ public class ClimberConstants { .withConstraints(CONSTRAINTS) .withCurrentLimits(CURRENT_LIMITS) .withEnableFOC(FOC_ENABLE) - .withInvertedValue(InvertedValue.Clockwise_Positive) + .withInvertedValue(InvertedValue.CounterClockwise_Positive) .withCanBus(CANBus.roboRIO()) .withVoltageOffsetStep(Volts.of(5.0)) .withPositionOffsetStep(new Rotation2d(5 * Math.PI / 180)) diff --git a/src/main/java/frc/robot/subsystems/shared/intake/Intake.java b/src/main/java/frc/robot/subsystems/shared/intake/Intake.java index d97ce440..71c97bbc 100644 --- a/src/main/java/frc/robot/subsystems/shared/intake/Intake.java +++ b/src/main/java/frc/robot/subsystems/shared/intake/Intake.java @@ -1,17 +1,12 @@ package frc.robot.subsystems.shared.intake; import static edu.wpi.first.units.Units.*; -import static edu.wpi.first.units.Units.Amps; -import static edu.wpi.first.units.Units.Degrees; -import static edu.wpi.first.units.Units.Milliamps; -import static edu.wpi.first.units.Units.Volts; import edu.wpi.first.math.geometry.Pose3d; import edu.wpi.first.math.geometry.Rotation2d; import edu.wpi.first.math.geometry.Rotation3d; import edu.wpi.first.math.geometry.Transform3d; import edu.wpi.first.units.VoltageUnit; -import edu.wpi.first.units.measure.Angle; import edu.wpi.first.util.function.BooleanConsumer; import edu.wpi.first.wpilibj2.command.Command; import edu.wpi.first.wpilibj2.command.Commands; @@ -227,7 +222,8 @@ public Command agitate() { Commands.runOnce( () -> { intakeState = IntakeState.AGITATE; - linkage.setPositionGoal(IntakeConstants.INTAKE_STATES.get(IntakeState.STOW)); + linkage.setPositionGoal(IntakeConstants.INTAKE_STATES.get(IntakeState.AGITATE)); + linkage.setPositionGoal(Rotation2d.fromDegrees(168.134766 + 18.0)); }), linkage .waitUntilLinkageAtGoal() @@ -238,11 +234,7 @@ public Command agitate() { linkage .getPosition() .minus( - new Rotation2d( - (Angle) - IntakeConstants.INTAKE_STATES - .get(IntakeState.AGITATE) - .getNewSetpoint()) + Rotation2d.fromDegrees(168.134766 + 18.0) .minus(Rotation2d.fromDegrees(90 + 5.0)))); }), linkage diff --git a/src/main/java/frc/robot/subsystems/shared/intake/IntakeConstants.java b/src/main/java/frc/robot/subsystems/shared/intake/IntakeConstants.java index 076b61f3..fda0bbf4 100644 --- a/src/main/java/frc/robot/subsystems/shared/intake/IntakeConstants.java +++ b/src/main/java/frc/robot/subsystems/shared/intake/IntakeConstants.java @@ -123,10 +123,10 @@ public class IntakeConstants { CONSTRAINTS = AngularPositionConstraints.builder() .withMaxVelocity( - new LoggedTunableMeasure<>("Linkage/Max Velocity", RadiansPerSecond.of(10.0))) + new LoggedTunableMeasure<>("Linkage/Max Velocity", RadiansPerSecond.of(50.0))) .withMaxAcceleration( new LoggedTunableMeasure<>( - "Linkage/Max Acceleration", RadiansPerSecondPerSecond.of(50.0))) + "Linkage/Max Acceleration", RadiansPerSecondPerSecond.of(15.0))) .withGoalTolerance( new LoggedTunableMeasure<>("Linkage/Goal Tolerance", Degrees.of(1.0))) .build(); @@ -191,13 +191,13 @@ public class IntakeConstants { MAX_ANGLE.getMeasure()), IntakeState.INTAKE, new Setpoint<>( - Rotation2d.fromDegrees(168.134766 + 8.0).getMeasure(), + Rotation2d.fromDegrees(168.134766 + 18.0).getMeasure(), LINKAGE_ANGLE_INCREMENT.getMeasure(), MIN_ANGLE.getMeasure(), MAX_ANGLE.getMeasure()), IntakeState.AGITATE, new Setpoint<>( - Rotation2d.fromDegrees(168.134766 + 8.0).getMeasure(), + Rotation2d.fromDegrees(168.134766 + 18.0).getMeasure(), LINKAGE_ANGLE_INCREMENT.getMeasure(), MIN_ANGLE.getMeasure(), MAX_ANGLE.getMeasure())); @@ -376,11 +376,11 @@ public class IntakeConstants { MOMENT_OF_INERTIA = 0.004; MOTOR_CONFIG = DCMotor.getKrakenX60Foc(1); - INTAKE_ANGLE_OFFSET = Rotation2d.fromDegrees(-30.9603232217); + INTAKE_ANGLE_OFFSET = Rotation2d.fromDegrees(-30.838927); ZERO_OFFSET = Rotation2d.kPi; MIN_ANGLE = Rotation2d.fromDegrees(30.838927); - MAX_ANGLE = Rotation2d.fromDegrees(236.74); + MAX_ANGLE = Rotation2d.fromDegrees(168.912511); // points A and D on the intake. PIN_LENGTH = Units.Inches.of(6.125).in(Units.Meters); @@ -510,7 +510,7 @@ public class IntakeConstants { MOMENT_OF_INERTIA = 0.004; MOTOR_CONFIG = DCMotor.getKrakenX60Foc(1); - INTAKE_ANGLE_OFFSET = Rotation2d.fromDegrees(-30.9603232217); + INTAKE_ANGLE_OFFSET = Rotation2d.fromDegrees(-30.838927); ZERO_OFFSET = Rotation2d.kPi; MIN_ANGLE = Rotation2d.fromDegrees(30.838927); @@ -590,19 +590,19 @@ public class IntakeConstants { Map.of( IntakeState.STOW, new Setpoint<>( - Rotation2d.fromDegrees(9.0).getMeasure(), + MIN_ANGLE.getMeasure(), LINKAGE_ANGLE_INCREMENT.getMeasure(), MIN_ANGLE.getMeasure(), MAX_ANGLE.getMeasure()), IntakeState.INTAKE, new Setpoint<>( - Rotation2d.fromDegrees(168.134766 + 8.0).getMeasure(), + MAX_ANGLE.getMeasure(), LINKAGE_ANGLE_INCREMENT.getMeasure(), MIN_ANGLE.getMeasure(), MAX_ANGLE.getMeasure()), IntakeState.AGITATE, new Setpoint<>( - Rotation2d.fromDegrees(168.134766 + 8.0).getMeasure(), + MAX_ANGLE.getMeasure(), LINKAGE_ANGLE_INCREMENT.getMeasure(), MIN_ANGLE.getMeasure(), MAX_ANGLE.getMeasure())); diff --git a/src/main/java/frc/robot/subsystems/v1_DoomSpiral/V1_DoomSpiralRobotContainer.java b/src/main/java/frc/robot/subsystems/v1_DoomSpiral/V1_DoomSpiralRobotContainer.java index 67ebcac5..b0140655 100644 --- a/src/main/java/frc/robot/subsystems/v1_DoomSpiral/V1_DoomSpiralRobotContainer.java +++ b/src/main/java/frc/robot/subsystems/v1_DoomSpiral/V1_DoomSpiralRobotContainer.java @@ -1,7 +1,6 @@ package frc.robot.subsystems.v1_DoomSpiral; import static edu.wpi.first.units.Units.Radians; -import static edu.wpi.first.units.Units.RadiansPerSecond; import edu.wpi.first.apriltag.AprilTagFieldLayout; import edu.wpi.first.apriltag.AprilTagFields; @@ -11,10 +10,10 @@ import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import edu.wpi.first.wpilibj2.command.Command; import edu.wpi.first.wpilibj2.command.Commands; +import edu.wpi.first.wpilibj2.command.button.CommandXboxController; import edu.wpi.first.wpilibj2.command.button.RobotModeTriggers; import edu.wpi.team190.gompeilib.core.io.components.inertial.GyroIO; import edu.wpi.team190.gompeilib.core.io.components.inertial.GyroIOPigeon2; -import edu.wpi.team190.gompeilib.core.logging.Trace; import edu.wpi.team190.gompeilib.core.robot.RobotContainer; import edu.wpi.team190.gompeilib.core.robot.RobotMode; import edu.wpi.team190.gompeilib.subsystems.arm.ArmIO; @@ -82,6 +81,7 @@ public class V1_DoomSpiralRobotContainer implements RobotContainer { private final XboxElite2Input driver = new XboxElite2Input(0); private final XKeysInput xkeys = new XKeysInput(1); + private final CommandXboxController operator = new CommandXboxController(2); private final BetterAutoChooser autoChooser; private final LoggedNetworkBoolean returnToMidChooser; @@ -314,7 +314,6 @@ public Supplier getAdjustmentModeSupplier() { }; } - @Trace private void configureButtonBindings() { drive.setDefaultCommand( @@ -326,11 +325,8 @@ private void configureButtonBindings() { () -> -driver.getRightX(), V1_DoomSpiralRobotState::getHeading, driver.rightTrigger(), - () -> V1_DoomSpiralRobotState.getShootingParameters().chassisAngle().getRadians(), - () -> - V1_DoomSpiralRobotState.getShootingParameters() - .chassisVelocity() - .in(RadiansPerSecond), + () -> V1_DoomSpiralRobotState.getRobotToHubAngle().getRadians(), + () -> 0.0, driver.leftTrigger().or(driver.x()), driver.x()) .withName("joystickDriveRotationLock")); @@ -357,6 +353,8 @@ private void configureButtonBindings() { () -> V1_DoomSpiralRobotState.getGlobalPose().getTranslation()) .withName("driver-povDown-true")); + driver.povUp().onTrue(drive.runOnce(() -> drive.stopWithX()).withName("driver-povUp")); + driver.leftBumper().onTrue(intake.collect().withName("driver-leftBumper-true")); driver.a().onTrue(intake.stopCollect().withName("driver-a-true")); @@ -448,10 +446,10 @@ private void configureButtonBindings() { V1_DoomSpiralCompositeCommands.stopShooterCommand(shooter, spindexer) .withName("driver-bottomRightPaddle-false")); - xkeys.f5().onTrue(shooter.incrementFlywheelVelocity().withName("xkeys-f5-true")); - xkeys.f6().onTrue(shooter.decrementFlywheelVelocity().withName("xkeys-f6-true")); - xkeys.g5().onTrue(shooter.incrementHoodAngle().withName("xkeys-g5-true")); - xkeys.g6().onTrue(shooter.decrementHoodAngle().withName("xkeys-g6-true")); + xkeys.f4().onTrue(shooter.incrementFlywheelVelocity().withName("xkeys-f4-true")); + xkeys.f5().onTrue(shooter.decrementFlywheelVelocity().withName("xkeys-f5-true")); + xkeys.f6().onTrue(shooter.incrementHoodAngle().withName("xkeys-f6-true")); + xkeys.f7().onTrue(shooter.decrementHoodAngle().withName("xkeys-f7-true")); xkeys.d8().onTrue(climber.setPositionDefault().withName("xkeys-d8-true")); xkeys.d9().onTrue(climber.setPositionL1().withName("xkeys-d9-true")); @@ -474,41 +472,31 @@ private void configureButtonBindings() { .withName("xkeys-e10-true")); xkeys - .b5() + .b4() .whileTrue( spindexer .setVoltage(V1_DoomSpiralSpindexerConstants.SPINDEXER_SLOW_VOLTAGE) - .withName("xkeys-b5-while")) - .onFalse(spindexer.setVoltage(0).withName("xkeys-b5-false")); + .withName("xkeys-b4-while")) + .onFalse(spindexer.setVoltage(0).withName("xkeys-b4-false")); xkeys - .b6() + .b5() .whileTrue( spindexer .setVoltage(-V1_DoomSpiralSpindexerConstants.SPINDEXER_SLOW_VOLTAGE) - .withName("xkeys-b6-while")) - .onFalse(spindexer.setVoltage(0).withName("xkeys-b6-false")); + .withName("xkeys-b5-while")) + .onFalse(spindexer.setVoltage(0).withName("xkeys-b5-false")); - xkeys.c5().onTrue(spindexer.increaseSpindexerVoltage().withName("xkeys-c5-true")); - xkeys.c6().onTrue(spindexer.decreaseSpindexerVoltage().withName("xkeys-c6-true")); - xkeys.d5().onTrue(spindexer.increaseFeederVoltage().withName("xkeys-d5-true")); - xkeys.d5().onTrue(spindexer.decreaseFeederVoltage().withName("xkeys-d5b-true")); + xkeys.b6().onTrue(spindexer.increaseSpindexerVoltage().withName("xkeys-b6-true")); + xkeys.b7().onTrue(spindexer.decreaseSpindexerVoltage().withName("xkeys-b7-true")); + xkeys.c6().onTrue(spindexer.increaseFeederVoltage().withName("xkeys-c6-true")); + xkeys.c7().onTrue(spindexer.decreaseFeederVoltage().withName("xkeys-c7-true")); xkeys.h8().onTrue(climber.resetClimberZero().withName("xkeys-h8-true")); xkeys.h9().onTrue(intake.resetIntakeZero().withName("xkeys-h9-true")); xkeys.h10().whileTrue(shooter.zeroHood().withName("xkeys-h10-while")); - xkeys - .b8() - .whileTrue( - swank - .setVoltage(-V1_DoomSpiralSwankConstants.SWANK_VOLTAGE) - .withName("xkeys-b8-while")); - - xkeys - .b9() - .whileTrue( - swank.setVoltage(V1_DoomSpiralSwankConstants.SWANK_VOLTAGE).withName("xkeys-b9-while")); + xkeys.g8().whileTrue(drive.run(() -> drive.stopWithX()).withName("xkeys-g8")); xkeys .b10() @@ -526,50 +514,107 @@ private void configureButtonBindings() { xkeys.b2().onTrue(intake.incrementStowOffset().withName("xkeys-b2-true")); xkeys - .d1() - .onTrue(intake.deploy().alongWith(intake.stopRollerOverride()).withName("xkeys-d1-true")); - xkeys.d3().onTrue(intake.decrementCollectOffset().withName("xkeys-d3-true")); - xkeys.d2().onTrue(intake.incrementCollectOffset().withName("xkeys-d2-true")); + .c1() + .onTrue(intake.deploy().alongWith(intake.stopRollerOverride()).withName("xkeys-c1-true")); + xkeys.c3().onTrue(intake.decrementCollectOffset().withName("xkeys-c3-true")); + xkeys.c2().onTrue(intake.incrementCollectOffset().withName("xkeys-c2-true")); xkeys - .e1() + .d1() .whileTrue( intake .setOverrideRollerVoltage(IntakeConstants.INTAKE_VOLTAGE) - .withName("xkeys-e1-while")) - .onFalse(intake.stopRollerOverride().withName("xkeys-e1-false")); + .withName("xkeys-d1-while")) + .onFalse(intake.stopRollerOverride().withName("xkeys-d1-false")); xkeys - .e2() + .d2() .whileTrue( intake .setOverrideRollerVoltage(IntakeConstants.EXTAKE_VOLTAGE) - .withName("xkeys-e2-while")) - .onFalse(intake.stopRollerOverride().withName("xkeys-e2-false")); + .withName("xkeys-d2-while")) + .onFalse(intake.stopRollerOverride().withName("xkeys-d2-false")); - xkeys.f1().onTrue(intake.increaseSpeedOffset().withName("xkeys-f1-true")); - xkeys.f2().onTrue(intake.decreaseSpeedOffset().withName("xkeys-f2-true")); + xkeys.e1().onTrue(intake.increaseSpeedOffset().withName("xkeys-e1-true")); + xkeys.e2().onTrue(intake.decreaseSpeedOffset().withName("xkeys-e2-true")); xkeys - .g1() + .f1() .whileTrue( intake .setLinkageVoltage(-IntakeConstants.LINKAGE_SLOW_VOLTAGE) - .withName("xkeys-g1-while")) - .onFalse(intake.setLinkageVoltage(0).withName("xkeys-g1-false")); + .withName("xkeys-f1-while")) + .onFalse(intake.setLinkageVoltage(0).withName("xkeys-f1-false")); xkeys - .g2() + .f2() .whileTrue( intake .setLinkageVoltage(IntakeConstants.LINKAGE_SLOW_VOLTAGE) - .withName("xkeys-g2-while")) - .onFalse(intake.setLinkageVoltage(0).withName("xkeys-g2-false")); + .withName("xkeys-f2-while")) + .onFalse(intake.setLinkageVoltage(0).withName("xkeys-f2-false")); xkeys - .h1() - .or(xkeys.h2().or(xkeys.h3())) - .whileTrue(intake.agitate().withName("xkeys-h1-h2-h3-while")); + .g1() + .or(xkeys.g2().or(xkeys.g3())) + .whileTrue(intake.agitate().withName("xkeys-g1-g2-g3-while")); + + operator + .leftTrigger() + .onTrue(intake.stopRollerOverride().alongWith(intake.stow()).withName("op-lt")); + operator + .rightTrigger() + .onTrue(intake.deploy().alongWith(intake.stopRollerOverride()).withName("op-rt")); + operator + .leftBumper() + .whileTrue( + intake.setOverrideRollerVoltage(IntakeConstants.INTAKE_VOLTAGE).withName("op-lb-while")) + .onFalse(intake.stopRollerOverride().withName("op-lb-false")); + operator + .rightBumper() + .whileTrue( + intake.setOverrideRollerVoltage(IntakeConstants.EXTAKE_VOLTAGE).withName("op-rb-while")) + .onFalse(intake.stopRollerOverride().withName("op-rb-false")); + + operator.povUp().onTrue(climber.setPositionDefault().withName("op-povUp")); + operator.povDown().onTrue(climber.resetClimberZero().withName("op-povDown")); + + operator + .povRight() + .whileTrue(climber.clockwiseSlow().withName("op-povRight-while")) + .onFalse(climber.setVoltage(0).withName("op-povRight-false")); + + operator + .povLeft() + .whileTrue(climber.counterClockwiseSlow().withName("op-povLeft-while")) + .onFalse(climber.setVoltage(0).withName("op-povLeft-false")); + + operator.y().onTrue(shooter.incrementFlywheelVelocity().withName("op-y-true")); + operator.x().onTrue(shooter.decrementFlywheelVelocity().withName("op-x-true")); + operator.b().onTrue(shooter.incrementHoodAngle().withName("op-b-true")); + operator.a().onTrue(shooter.decrementHoodAngle().withName("op-a-true")); + + operator + .leftStick() + .whileTrue( + intake.setLinkageVoltage(-IntakeConstants.LINKAGE_SLOW_VOLTAGE).withName("op-ls-while")) + .onFalse(intake.setLinkageVoltage(0).withName("op-ls-false")); + + operator + .rightStick() + .whileTrue( + intake.setLinkageVoltage(IntakeConstants.LINKAGE_SLOW_VOLTAGE).withName("op-rs-while")) + .onFalse(intake.setLinkageVoltage(0).withName("op-rs-false")); + + operator.start().onTrue(intake.resetIntakeZero().withName("op-start")); + + operator + .back() + .whileTrue( + spindexer + .setVoltage(-V1_DoomSpiralSpindexerConstants.SPINDEXER_SLOW_VOLTAGE) + .withName("op-back-while")) + .onFalse(spindexer.setVoltage(0).withName("op-back-false")); } private void configureAutos() { @@ -612,6 +657,7 @@ private void configureAutos() { "Right Trench Anti Bucks Crosses", V1_DoomSpiralAutoRightTrenchAntiBucksCrosses.getAutoRoutine( drive, intake, shooter, spindexer, returnToMidChooser, getAdjustmentModeSupplier())); + SmartDashboard.putData("Autonomous Modes", autoChooser); RobotModeTriggers.autonomous() diff --git a/src/main/java/frc/robot/subsystems/v1_DoomSpiral/V1_DoomSpiralRobotState.java b/src/main/java/frc/robot/subsystems/v1_DoomSpiral/V1_DoomSpiralRobotState.java index 3ee6a3c2..9104107d 100644 --- a/src/main/java/frc/robot/subsystems/v1_DoomSpiral/V1_DoomSpiralRobotState.java +++ b/src/main/java/frc/robot/subsystems/v1_DoomSpiral/V1_DoomSpiralRobotState.java @@ -13,7 +13,6 @@ import edu.wpi.first.networktables.NetworkTablesJNI; import edu.wpi.first.units.measure.AngularVelocity; import edu.wpi.first.units.measure.Distance; -import edu.wpi.first.units.measure.Time; import edu.wpi.first.wpilibj.DriverStation; import edu.wpi.first.wpilibj.DriverStation.Alliance; import edu.wpi.first.wpilibj.Timer; @@ -25,7 +24,7 @@ import edu.wpi.team190.gompeilib.subsystems.drivebases.swervedrive.SwerveDrive; import edu.wpi.team190.gompeilib.subsystems.vision.data.VisionPoseObservation; import frc.robot.FieldConstants; -import frc.robot.subsystems.v1_DoomSpiral.shooter.ShotCalculator; +import frc.robot.commands.v1_DoomSpiral.autonomous.V1_DoomSpiralAutoTrajectoryCache; import frc.robot.subsystems.v1_DoomSpiral.shooter.V1_DoomSpiralShooterConstants; import frc.robot.util.AllianceFlipUtil; import frc.robot.util.HubActivePeriod; @@ -53,15 +52,16 @@ public class V1_DoomSpiralRobotState { private static final Localization localization; @Getter private static Distance distanceToHub; + @Getter private static Rotation2d robotToHubAngle; @Getter private static Distance distanceToFeedTranslation; private static final InterpolatingTreeMap shootAngleTree; private static final InterpolatingTreeMap shootSpeedTree; - private static final InterpolatingTreeMap timeOfFlightMap; private static final InterpolatingTreeMap feedAngleTree; private static final InterpolatingTreeMap feedSpeedTree; - @Getter private static final ShotCalculator.ShotParameters shootingParameters; + @Getter private static Rotation2d scoreAngle; + @Getter private static double scoreVelocity; @Getter private static Rotation2d feedAngle; @Getter private static double feedVelocity; @@ -102,6 +102,8 @@ public class V1_DoomSpiralRobotState { .getNorm(), Meters); + robotToHubAngle = Rotation2d.fromDegrees(0); + ledStates = new LEDStates(false, false, false, false, false, false); shootAngleTree = @@ -121,16 +123,6 @@ public class V1_DoomSpiralRobotState { .interpolate(start.in(RadiansPerSecond), end.in(RadiansPerSecond), t), RadiansPerSecond)); - timeOfFlightMap = - new InterpolatingTreeMap<>( - (start, end, q) -> - InverseInterpolator.forDouble() - .inverseInterpolate(start.in(Meters), end.in(Meters), q.in(Meters)), - (start, end, t) -> - Time.ofBaseUnits( - Interpolator.forDouble().interpolate(start.in(Seconds), end.in(Seconds), t), - Seconds)); - feedAngleTree = new InterpolatingTreeMap<>( (start, end, q) -> @@ -181,54 +173,6 @@ public class V1_DoomSpiralRobotState { shootAngleTree.put(Meters.of(4.120246303911951), Rotation2d.fromDegrees(20.0)); shootSpeedTree.put(Meters.of(4.120246303911951), RadiansPerSecond.of(457.0)); - timeOfFlightMap.put(Meters.of(1.465609), Seconds.of(1.14)); - timeOfFlightMap.put(Meters.of(1.465609), Seconds.of(1.19)); - timeOfFlightMap.put(Meters.of(1.465609), Seconds.of(1.12)); - - timeOfFlightMap.put(Meters.of(1.723154), Seconds.of(1.11)); - timeOfFlightMap.put(Meters.of(1.723154), Seconds.of(1.12)); - timeOfFlightMap.put(Meters.of(1.723154), Seconds.of(1.03)); - - timeOfFlightMap.put(Meters.of(1.997918), Seconds.of(1.04)); - timeOfFlightMap.put(Meters.of(2.003457), Seconds.of(1.04)); - timeOfFlightMap.put(Meters.of(1.993788), Seconds.of(1.04)); - - timeOfFlightMap.put(Meters.of(2.247938), Seconds.of(1.02)); - timeOfFlightMap.put(Meters.of(2.25919), Seconds.of(1.08)); - timeOfFlightMap.put(Meters.of(2.246664), Seconds.of(1.10)); - - timeOfFlightMap.put(Meters.of(2.485868), Seconds.of(1.09)); - timeOfFlightMap.put(Meters.of(2.485575), Seconds.of(1.10)); - timeOfFlightMap.put(Meters.of(2.486227), Seconds.of(1.13)); - - timeOfFlightMap.put(Meters.of(2.751185), Seconds.of(1.14)); - timeOfFlightMap.put(Meters.of(2.738152), Seconds.of(1.14)); - timeOfFlightMap.put(Meters.of(2.735363), Seconds.of(1.09)); - - timeOfFlightMap.put(Meters.of(2.965804), Seconds.of(1.23)); - timeOfFlightMap.put(Meters.of(2.997758), Seconds.of(1.16)); - timeOfFlightMap.put(Meters.of(3.003162), Seconds.of(1.20)); - - timeOfFlightMap.put(Meters.of(3.234271), Seconds.of(1.19)); - timeOfFlightMap.put(Meters.of(3.223711), Seconds.of(1.23)); - timeOfFlightMap.put(Meters.of(3.2486), Seconds.of(1.21)); - - timeOfFlightMap.put(Meters.of(3.514255), Seconds.of(1.28)); - timeOfFlightMap.put(Meters.of(3.493659), Seconds.of(1.25)); - timeOfFlightMap.put(Meters.of(3.481445), Seconds.of(1.24)); - - timeOfFlightMap.put(Meters.of(3.841055), Seconds.of(1.20)); - timeOfFlightMap.put(Meters.of(3.819408), Seconds.of(1.26)); - timeOfFlightMap.put(Meters.of(3.81803), Seconds.of(1.26)); - - timeOfFlightMap.put(Meters.of(3.95738), Seconds.of(1.35)); - timeOfFlightMap.put(Meters.of(3.988535), Seconds.of(1.34)); - timeOfFlightMap.put(Meters.of(4.013547), Seconds.of(1.30)); - - timeOfFlightMap.put(Meters.of(4.333429), Seconds.of(1.35)); - timeOfFlightMap.put(Meters.of(4.361226), Seconds.of(1.34)); - timeOfFlightMap.put(Meters.of(4.382547), Seconds.of(1.28)); - feedAngleTree.put( Meters.of(0.0), Rotation2d.fromDegrees(V1_DoomSpiralShooterConstants.HOOD_CONSTANTS.maxAngle.getDegrees())); @@ -254,15 +198,8 @@ public class V1_DoomSpiralRobotState { // feedFlywheelSpeedTree.put(Meters.of(5.57), RadiansPerSecond.of(275.0)); // feedFlywheelSpeedTree.put(Meters.of(5.60), RadiansPerSecond.of(290.0)); - shootingParameters = - new ShotCalculator.ShotParameters( - false, - new Pose2d(), - new Rotation2d(), - new Rotation2d(), - RadiansPerSecond.zero(), - RadiansPerSecond.zero(), - RadiansPerSecond.zero()); + scoreAngle = new Rotation2d(); + scoreVelocity = 0; feedAngle = new Rotation2d(); feedVelocity = 0; @@ -290,20 +227,15 @@ public static void periodic( Translation2d hubTranslation = AllianceFlipUtil.apply(FieldConstants.Hub.topCenterPoint.toTranslation2d()); - ShotCalculator.ShotParameters shotParameters = - ShotCalculator.getShotParameters( - hubPose, - hubTranslation, - V1_DoomSpiralShooterConstants.SHOOTER_POSE, - drive.getMeasuredChassisSpeeds(), - Seconds.of(0.04), - timeOfFlightMap::get, - shootAngleTree::get, - shootSpeedTree::get); + Logger.recordOutput( + "GO_BACK_TRIGGER", V1_DoomSpiralAutoTrajectoryCache.GO_BACK_TRIGGER.getAsBoolean()); distanceToHub = Distance.ofBaseUnits( - hubTranslation.getDistance(shotParameters.adjustedRobotPose().getTranslation()), + getHubZonePose() + .getTranslation() + .minus(AllianceFlipUtil.apply(FieldConstants.Hub.topCenterPoint.toTranslation2d())) + .getNorm(), Meters); distanceToFeedTranslation = @@ -313,17 +245,24 @@ public static void periodic( .getDistance(AllianceFlipUtil.apply(FieldConstants.Outpost.BLUE_FEED_TRANSLATION)), Meters); + Pose2d shooterPosition = hubPose.transformBy(V1_DoomSpiralShooterConstants.SHOOTER_POSE); + robotToHubAngle = + hubTranslation + .minus(shooterPosition.getTranslation()) + .getAngle() + .minus(V1_DoomSpiralShooterConstants.SHOOTER_POSE.getRotation()); + + scoreAngle = shootAngleTree.get(distanceToHub); + scoreVelocity = shootSpeedTree.get(distanceToHub).in(RadiansPerSecond); + feedAngle = feedAngleTree.get(distanceToFeedTranslation); feedVelocity = feedSpeedTree.get(distanceToFeedTranslation).in(RadiansPerSecond); field.setRobotPose(getGlobalPose()); Logger.recordOutput(NTPrefixes.POSE_DATA + "Distance To Hub", distanceToHub); - Logger.recordOutput(NTPrefixes.ROBOT_STATE + "Shot Parameters", shotParameters); Logger.recordOutput( NTPrefixes.POSE_DATA + "Hub Translation", new Pose2d(hubTranslation, new Rotation2d())); - Logger.recordOutput( - NTPrefixes.POSE_DATA + "Hub Pose Adjusted", shotParameters.adjustedRobotPose()); Logger.recordOutput(NTPrefixes.ROBOT_STATE + "Hood/Feed Angle", feedAngle); Logger.recordOutput(NTPrefixes.ROBOT_STATE + "Shooter/Feed Velocity", feedVelocity); diff --git a/src/main/java/frc/robot/subsystems/v1_DoomSpiral/shooter/V1_DoomSpiralShooter.java b/src/main/java/frc/robot/subsystems/v1_DoomSpiral/shooter/V1_DoomSpiralShooter.java index 3b4e51e9..30515288 100644 --- a/src/main/java/frc/robot/subsystems/v1_DoomSpiral/shooter/V1_DoomSpiralShooter.java +++ b/src/main/java/frc/robot/subsystems/v1_DoomSpiral/shooter/V1_DoomSpiralShooter.java @@ -78,7 +78,7 @@ public Command setHoodGoal(HoodGoal goal) { private Rotation2d getHoodGoal(HoodGoal goal) { Rotation2d rotation = switch (goal) { - case SCORE -> V1_DoomSpiralRobotState.getShootingParameters().hoodAngle(); + case SCORE -> V1_DoomSpiralRobotState.getScoreAngle(); case FEED -> V1_DoomSpiralRobotState.getFeedAngle(); default -> Rotation2d.kZero; }; diff --git a/src/main/java/frc/robot/subsystems/v1_DoomSpiral/shooter/V1_DoomSpiralShooterConstants.java b/src/main/java/frc/robot/subsystems/v1_DoomSpiral/shooter/V1_DoomSpiralShooterConstants.java index c3fbe502..545fb457 100644 --- a/src/main/java/frc/robot/subsystems/v1_DoomSpiral/shooter/V1_DoomSpiralShooterConstants.java +++ b/src/main/java/frc/robot/subsystems/v1_DoomSpiral/shooter/V1_DoomSpiralShooterConstants.java @@ -49,10 +49,10 @@ public class V1_DoomSpiralShooterConstants { .withMotorConfig(DCMotor.getKrakenX60Foc(2)) .withVoltageGains( Gains.builder() - .withKP(new LoggedTunableNumber("Shooter/Flywheel/VoltageKp", 1.5)) + .withKP(new LoggedTunableNumber("Shooter/Flywheel/VoltageKp", 0.5)) .withKD(new LoggedTunableNumber("Shooter/Flywheel/VoltageKd", 0.0)) .withKS(new LoggedTunableNumber("Shooter/Flywheel/VoltageKs", 0.21467)) - .withKV(new LoggedTunableNumber("Shooter/Flywheel/VoltageKv", 0.3915)) + .withKV(new LoggedTunableNumber("Shooter/Flywheel/VoltageKv", 0.14015)) .withKA(new LoggedTunableNumber("Shooter/Flywheel/VoltageKa", 0.0045447)) .build()) .withTorqueGains( @@ -91,7 +91,7 @@ public class V1_DoomSpiralShooterConstants { .withMotorConfig(DCMotor.getKrakenX44Foc(1)) .withCanBus(CANBus.roboRIO()) .withLengthMeters(0.101596) - .withMinAngle(Rotation2d.fromDegrees(0.1)) + .withMinAngle(Rotation2d.fromDegrees(2)) .withMaxAngle(Rotation2d.fromDegrees(20)) .withZeroVoltage(Volts.of(1.0)) .withZeroCurrentThreshold(Amps.of(40.0)) diff --git a/src/main/java/frc/robot/subsystems/v2_Turnover/V2_TurnoverMechanism3d.java b/src/main/java/frc/robot/subsystems/v2_Turnover/V2_TurnoverMechanism3d.java index 6c10917d..2f21993a 100644 --- a/src/main/java/frc/robot/subsystems/v2_Turnover/V2_TurnoverMechanism3d.java +++ b/src/main/java/frc/robot/subsystems/v2_Turnover/V2_TurnoverMechanism3d.java @@ -3,35 +3,55 @@ import edu.wpi.first.math.geometry.Pose3d; import edu.wpi.first.math.geometry.Rotation2d; import edu.wpi.first.math.geometry.Rotation3d; +import edu.wpi.first.math.geometry.Transform3d; import edu.wpi.first.math.geometry.Translation3d; import frc.robot.subsystems.shared.fourbarlinkage.FourBarLinkageConstants.LinkageState; import frc.robot.subsystems.shared.intake.Intake; +import frc.robot.subsystems.v2_Turnover.shooter.V2_TurnoverShooter; import java.util.List; public class V2_TurnoverMechanism3d { - private static final Translation3d spindexerTranslation = - new Translation3d(-0.009525, 0, 0.067589); + // Shooter + private static final Translation3d baseTurretTranslation = // robot centric + new Translation3d(-0.017463, -0.163513, 0.371475); + private static final Translation3d baseHoodTranslation = + new Translation3d(0.043168, -0.1635125, 0.474975); // robot centric + private static final Transform3d turretToHoodTransform = + new Transform3d(baseHoodTranslation.minus(baseTurretTranslation), Rotation3d.kZero); + + // Climber private static final Translation3d climberTranslation = - new Translation3d(-0.202788, 0.090048, 0.477077); - private static final Translation3d staticIntakeTranslation = new Translation3d(0.0, 0.0, 0.0); + new Translation3d(-0.317482, 0.090043, 0.477114); + + // Intake private static final Translation3d intakeCrankTranslation = - new Translation3d(0.142476, 0, 0.278075); + new Translation3d(0.139700, 0, 0.254000); private static final Translation3d intakeFollowerTranslation = - new Translation3d(0.278238, 0, 0.196629); + new Translation3d(0.292100, 0, 0.171450); - private static final Rotation2d crankOffset = Rotation2d.fromDegrees(-171); - private static final Rotation2d couplerOffset = Rotation2d.fromDegrees(-18.88); - private static final Rotation2d followerOffset = Rotation2d.fromDegrees(-60.909742); + private static final Rotation2d crankOffset = Rotation2d.fromDegrees(-180 + 30.838927); + private static final Rotation2d couplerOffset = Rotation2d.fromDegrees(0); + private static final Rotation2d followerOffset = Rotation2d.fromDegrees(-70.753060); public static Pose3d[] getPoses( - Rotation2d spindexerPosition, Rotation2d climberPosition, Intake intake) { - - List linkageStates = intake.getLinkage().getLinkagePoses(); + Rotation2d climberPosition, Intake intake, V2_TurnoverShooter shooter) { + // Shooter + Pose3d turretPose = + new Pose3d( + baseTurretTranslation, + new Rotation3d(Rotation2d.k180deg.plus(shooter.getTurretRotation()))); + Pose3d hoodPose = + turretPose.transformBy( + new Transform3d( + turretToHoodTransform.getTranslation(), + new Rotation3d(0.0, shooter.getHoodAngle().getRadians(), 0.0))); - Pose3d spindexerPose = new Pose3d(spindexerTranslation, new Rotation3d(spindexerPosition)); + // Climber Pose3d climberPose = new Pose3d(climberTranslation, new Rotation3d(-climberPosition.getRadians(), 0.0, 0.0)); - Pose3d staticIntakePose = new Pose3d(staticIntakeTranslation, new Rotation3d()); + + // Intake + List linkageStates = intake.getLinkage().getLinkagePoses(); Pose3d crankPose = new Pose3d( intakeCrankTranslation, @@ -55,7 +75,7 @@ public static Pose3d[] getPoses( 0.0)); return new Pose3d[] { - spindexerPose, climberPose, staticIntakePose, crankPose, couplerPose, followerPose, + turretPose, hoodPose, climberPose, Pose3d.kZero, crankPose, couplerPose, followerPose }; } } diff --git a/src/main/java/frc/robot/subsystems/v2_Turnover/V2_TurnoverRobotContainer.java b/src/main/java/frc/robot/subsystems/v2_Turnover/V2_TurnoverRobotContainer.java index 36b951c6..24331f23 100644 --- a/src/main/java/frc/robot/subsystems/v2_Turnover/V2_TurnoverRobotContainer.java +++ b/src/main/java/frc/robot/subsystems/v2_Turnover/V2_TurnoverRobotContainer.java @@ -5,6 +5,7 @@ import com.pathplanner.lib.auto.NamedCommands; import com.pathplanner.lib.commands.FollowPathCommand; import com.pathplanner.lib.commands.PathfindingCommand; +import com.pathplanner.lib.util.PathPlannerLogging; import edu.wpi.first.math.geometry.Pose2d; import edu.wpi.first.math.geometry.Rotation2d; import edu.wpi.first.math.geometry.Transform2d; @@ -15,9 +16,9 @@ import edu.wpi.first.wpilibj2.command.CommandScheduler; import edu.wpi.first.wpilibj2.command.Commands; import edu.wpi.first.wpilibj2.command.button.RobotModeTriggers; +import edu.wpi.first.wpilibj2.command.button.Trigger; import edu.wpi.team190.gompeilib.core.io.components.inertial.GyroIO; import edu.wpi.team190.gompeilib.core.io.components.inertial.GyroIOPigeon2; -import edu.wpi.team190.gompeilib.core.logging.Trace; import edu.wpi.team190.gompeilib.core.robot.RobotContainer; import edu.wpi.team190.gompeilib.core.robot.RobotMode; import edu.wpi.team190.gompeilib.subsystems.drivebases.swervedrive.SwerveDrive; @@ -43,15 +44,18 @@ import frc.robot.commands.v2_Turnover.V2_TurnoverCompositeCommands; import frc.robot.commands.v2_Turnover.autonomous.V2_TurnoverAutoDepot; import frc.robot.commands.v2_Turnover.autonomous.V2_TurnoverAutoFollowDepot; +import frc.robot.commands.v2_Turnover.autonomous.V2_TurnoverAutoFollowDepotBC; import frc.robot.commands.v2_Turnover.autonomous.V2_TurnoverAutoFollowFeedFullLeft; import frc.robot.commands.v2_Turnover.autonomous.V2_TurnoverAutoFollowFeedFullRight; import frc.robot.commands.v2_Turnover.autonomous.V2_TurnoverAutoFollowFeedMiddleLeft; import frc.robot.commands.v2_Turnover.autonomous.V2_TurnoverAutoFollowFeedMiddleRight; import frc.robot.commands.v2_Turnover.autonomous.V2_TurnoverAutoLeftHalf; import frc.robot.commands.v2_Turnover.autonomous.V2_TurnoverAutoLeftOP; +import frc.robot.commands.v2_Turnover.autonomous.V2_TurnoverAutoLeftOPBC; import frc.robot.commands.v2_Turnover.autonomous.V2_TurnoverAutoLeftOPBucks; import frc.robot.commands.v2_Turnover.autonomous.V2_TurnoverAutoRightHalf; import frc.robot.commands.v2_Turnover.autonomous.V2_TurnoverAutoRightOP; +import frc.robot.commands.v2_Turnover.autonomous.V2_TurnoverAutoRightOPBC; import frc.robot.commands.v2_Turnover.autonomous.V2_TurnoverAutoRightOPBucks; import frc.robot.subsystems.shared.fourbarlinkage.FourBarLinkageIO; import frc.robot.subsystems.shared.fourbarlinkage.FourBarLinkageIOSim; @@ -83,6 +87,7 @@ import java.util.Map; import java.util.function.Supplier; import java.util.stream.Collectors; +import org.littletonrobotics.junction.Logger; import org.littletonrobotics.junction.networktables.LoggedDashboardChooser; import org.littletonrobotics.junction.networktables.LoggedNetworkBoolean; @@ -145,7 +150,9 @@ public V2_TurnoverRobotContainer() { clopper = new V2_TurnoverClopper( new GenericRollerIOTalonFX(V2_TurnoverClopperConstants.ROLLER_FLOOR_CONSTANTS), - new GenericRollerIOTalonFX(V2_TurnoverClopperConstants.BALL_TUNNEL_CONSTANTS), + new GenericRollerIOTalonFX(V2_TurnoverClopperConstants.BALL_TUNNEL_TOP_CONSTANTS), + new GenericRollerIOTalonFX( + V2_TurnoverClopperConstants.BALL_TUNNEL_BOTTOM_CONSTANTS), new GenericRollerIOTalonFX( V2_TurnoverClopperConstants.BALLS_TO_THE_WALL_CONSTANTS)); shooter = @@ -216,7 +223,10 @@ public V2_TurnoverRobotContainer() { clopper = new V2_TurnoverClopper( new GenericRollerIOTalonFXSim(V2_TurnoverClopperConstants.ROLLER_FLOOR_CONSTANTS), - new GenericRollerIOTalonFXSim(V2_TurnoverClopperConstants.BALL_TUNNEL_CONSTANTS), + new GenericRollerIOTalonFXSim( + V2_TurnoverClopperConstants.BALL_TUNNEL_TOP_CONSTANTS), + new GenericRollerIOTalonFX( + V2_TurnoverClopperConstants.BALL_TUNNEL_BOTTOM_CONSTANTS), new GenericRollerIOTalonFXSim( V2_TurnoverClopperConstants.BALLS_TO_THE_WALL_CONSTANTS)); @@ -258,7 +268,10 @@ public V2_TurnoverRobotContainer() { if (clopper == null) { clopper = new V2_TurnoverClopper( - new GenericRollerIO() {}, new GenericRollerIO() {}, new GenericRollerIO() {}); + new GenericRollerIO() {}, + new GenericRollerIO() {}, + new GenericRollerIO() {}, + new GenericRollerIO() {}); } if (vision == null) { vision = new Vision(() -> FieldConstants.tagLayoutType.getLayout()); @@ -376,11 +389,12 @@ public Supplier getAdjustmentModeSupplier() { private void configureAutos() { // Named commands that are used during the paths NamedCommands.registerCommand( - "SCORE_OR_FEED", V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper)); + "SCORE_OR_FEED", + V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper, () -> false)); NamedCommands.registerCommand( "SCORE_NO_ROLLER", - V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper) + V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper, () -> false) .alongWith(intake.setOverrideRollerVoltage(0))); NamedCommands.registerCommand("STOP_OVERRIDE_ROLLER", intake.stopRollerOverride()); @@ -401,7 +415,7 @@ private void configureAutos() { NamedCommands.registerCommand( "SCORE_AGITATE_OP_2", - V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper) + V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper, () -> false) .alongWith( Commands.sequence( intake.stopRollerOverride(), @@ -417,7 +431,8 @@ private void configureAutos() { NamedCommands.registerCommand( "HOLD_WHILE_INTAKE", V2_TurnoverCompositeCommands.hold(clopper, shooter) - .alongWith(intake.setOverrideRollerVoltage(IntakeConstants.INTAKE_VOLTAGE))); + .alongWith(intake.setOverrideRollerVoltage(IntakeConstants.INTAKE_VOLTAGE)) + .withName("HOLD_WHILE_INTAKE")); NamedCommands.registerCommand( "DEPLOY", @@ -529,10 +544,27 @@ private void configureAutos() { "Follow Depot", V2_TurnoverAutoFollowDepot.getAutoRoutine( drive, intake, clopper, shooter, getAdjustmentModeSupplier())); + autoChooser.addOption( + "Follow Depot BC", + V2_TurnoverAutoFollowDepotBC.getAutoRoutine( + drive, intake, clopper, shooter, getAdjustmentModeSupplier())); + autoChooser.addOption( + "Right OP BC", + V2_TurnoverAutoRightOPBC.getAutoRoutine( + drive, intake, clopper, shooter, getAdjustmentModeSupplier())); + autoChooser.addOption( + "Left OP BC", + V2_TurnoverAutoLeftOPBC.getAutoRoutine( + drive, intake, clopper, shooter, getAdjustmentModeSupplier())); + + PathPlannerLogging.setLogActivePathCallback( + l -> Logger.recordOutput("Auto/PathPlanner/Path", l.toArray(Pose2d[]::new))); + PathPlannerLogging.setLogTargetPoseCallback( + p -> Logger.recordOutput("Auto/PathPlanner/Target", p)); } - @Trace private void configureButtonBindings() { + Trigger invertScoreLocation = driver.povRight(); driver .povDown() .onTrue( @@ -541,6 +573,10 @@ private void configureButtonBindings() { V2_TurnoverRobotState::resetPose, () -> V2_TurnoverRobotState.getGlobalPose().getTranslation())); + driver.povUp().onTrue(drive.runOnce(() -> drive.stopWithX()).withName("driver-povUp")); + + xkeys.g8().whileTrue(drive.run(() -> drive.stopWithX()).withName("xkeys-g8")); + drive.setDefaultCommand( new ContinuousConditionalCommand( DriveCommands.joystickDriveRotationLock( @@ -605,8 +641,8 @@ private void configureButtonBindings() { / (Math.PI / 2.0)) * (Math.PI / 2.0))) .withName("cardinal-direction-set")); - xkeys.b9().onTrue(DriveCommands.incrementSlowFactor().withName("xkeys-b9-true")); - xkeys.b10().onTrue(DriveCommands.decrementSlowFactor().withName("xkeys-b10-true")); + + xkeys.b9().onTrue(DriveCommands.decrementSlowFactor().withName("xkeys-b10-true")); driver .povDown() @@ -625,7 +661,7 @@ private void configureButtonBindings() { V2_TurnoverCompositeCommands.hold(clopper, shooter) .withName("driver-rightBumper-while")) .onFalse( - V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper) + V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper, invertScoreLocation) .withName("driver-rightBumper-false")); xkeys .b8() @@ -633,7 +669,7 @@ private void configureButtonBindings() { V2_TurnoverCompositeCommands.hold(clopper, shooter) .withName("driver-rightBumper-while")) .onFalse( - V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper) + V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper, invertScoreLocation) .withName("driver-rightBumper-false")); xkeys @@ -649,7 +685,7 @@ private void configureButtonBindings() { shooter, clopper, V2_TurnoverRobotState.FixedShots.LEFT_TRENCH) .withName("driver-topLeftPaddle-while")) .onFalse( - V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper) + V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper, invertScoreLocation) .withName("driver-topLeftPaddle-false")); driver @@ -659,7 +695,7 @@ private void configureButtonBindings() { shooter, clopper, V2_TurnoverRobotState.FixedShots.RIGHT_TRENCH) .withName("driver-topRightPaddle-while")) .onFalse( - V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper) + V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper, invertScoreLocation) .withName("driver-topRightPaddle-false")); driver @@ -669,7 +705,7 @@ private void configureButtonBindings() { shooter, clopper, V2_TurnoverRobotState.FixedShots.LEFT_CORNER) .withName("driver-topLeftPaddle-while")) .onFalse( - V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper) + V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper, invertScoreLocation) .withName("driver-topLeftPaddle-false")); driver @@ -679,18 +715,16 @@ private void configureButtonBindings() { shooter, clopper, V2_TurnoverRobotState.FixedShots.RIGHT_CORNER) .withName("driver-topRightPaddle-while")) .onFalse( - V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper) + V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper, invertScoreLocation) .withName("driver-topRightPaddle-false")); driver .a() .whileTrue( - V2_TurnoverCompositeCommands.fixedShotCommand( - shooter, clopper, V2_TurnoverRobotState.FixedShots.HUB) - .withName("driver-bottomLeftPaddle-while")) - .onFalse( - V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper) - .withName("driver-bottomLeftPaddle-false")); + intake + .setOverrideRollerVoltage(-IntakeConstants.INTAKE_VOLTAGE) + .withName("driver-a-while")) + .onFalse(intake.stopRollerOverride().withName("driver-a-false")); driver .b() @@ -699,7 +733,7 @@ private void configureButtonBindings() { shooter, clopper, V2_TurnoverRobotState.FixedShots.TOWER) .withName("driver-bottomRightPaddle-while")) .onFalse( - V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper) + V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper, invertScoreLocation) .withName("driver-bottomRightPaddle-false")); driver @@ -746,6 +780,8 @@ private void configureButtonBindings() { V2_TurnoverClopperConstants.BALL_TUNNEL_FEED_VOLTAGE.unaryMinus()) .withName("xkeys-c5-true")); + xkeys.d3().whileTrue(clopper.marcusCommand()).onFalse(clopper.stopBallTunnel()); + xkeys .d4() .whileTrue( @@ -879,7 +915,7 @@ private void configureButtonBindings() { .alongWith(clopper.intake()) .withName("xkeys-h1-h2-h3-while")) .onFalse( - V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper) + V2_TurnoverCompositeCommands.scoreOrFeedCommand(shooter, clopper, invertScoreLocation) .withName("xkeys-h1-h2-h3-false")); // xkeys.h4().onTrue(); SLOW WRAP MODE @@ -917,6 +953,8 @@ public void robotPeriodic() { intake.isIntakeAtStow(), driver.rightBumper().getAsBoolean()); fuelSimulator.updateSim(); + Logger.recordOutput( + "Mechanism 3d", V2_TurnoverMechanism3d.getPoses(Rotation2d.kZero, intake, shooter)); } @Override diff --git a/src/main/java/frc/robot/subsystems/v2_Turnover/V2_TurnoverRobotState.java b/src/main/java/frc/robot/subsystems/v2_Turnover/V2_TurnoverRobotState.java index 302b2199..c4563e5f 100644 --- a/src/main/java/frc/robot/subsystems/v2_Turnover/V2_TurnoverRobotState.java +++ b/src/main/java/frc/robot/subsystems/v2_Turnover/V2_TurnoverRobotState.java @@ -290,8 +290,9 @@ public static void periodic( new Rectangle2d( AllianceFlipUtil.apply( new Translation2d( - FieldConstants.LinesVertical.neutralZoneNear, FieldConstants.fieldWidth)), - AllianceFlipUtil.apply(new Translation2d(0, 0))); + FieldConstants.LinesVertical.neutralZoneNear, + FieldConstants.fieldWidth + Units.inchesToMeters(15))), + AllianceFlipUtil.apply(new Translation2d(0, -Units.inchesToMeters(-15)))); inAllianceZone = allianceZone.contains(getGlobalPose().getTranslation()); diff --git a/src/main/java/frc/robot/subsystems/v2_Turnover/clopper/V2_TurnoverClopper.java b/src/main/java/frc/robot/subsystems/v2_Turnover/clopper/V2_TurnoverClopper.java index cc05aaaa..e3c0dda0 100644 --- a/src/main/java/frc/robot/subsystems/v2_Turnover/clopper/V2_TurnoverClopper.java +++ b/src/main/java/frc/robot/subsystems/v2_Turnover/clopper/V2_TurnoverClopper.java @@ -16,14 +16,19 @@ public class V2_TurnoverClopper extends SubsystemBase { private final GenericRoller rollerFloor; - private final GenericRoller ballTunnel; + private final GenericRoller ballTunnelTop; + private final GenericRoller ballTunnelBottom; private final GenericRoller ballsToWall; - private boolean overrideBallTunnel; + private boolean overrideTopBallTunnel; + private boolean overrideBottomBallTunnel; private boolean overrideRollerFloor; private boolean overrideBallsToWall; - private final Setpoint ballTunnelSetpoint, ballTunnelOverrideSetpoint; + private final Setpoint ballTunnelTopSetpoint, + ballTunnelBottomSetpoint, + ballTunnelTopOverrideSetpoint, + ballTunnelBottomOverrideSetpoint; private final Setpoint rollerFloorSetpoint, rollerFloorOverrideSetpoint; private final Setpoint ballsToWallSetpoint, ballsToWallsverrideSetpoint; @@ -32,7 +37,10 @@ public class V2_TurnoverClopper extends SubsystemBase { private boolean shouldReverse; public V2_TurnoverClopper( - GenericRollerIO rollerFloorIO, GenericRollerIO ballTunnelIO, GenericRollerIO ballsToWallIO) { + GenericRollerIO rollerFloorIO, + GenericRollerIO ballTunnelTopIO, + GenericRollerIO ballTunnelBottomIO, + GenericRollerIO ballsToWallIO) { setName("Hopper"); rollerFloorSetpoint = @@ -48,16 +56,31 @@ public V2_TurnoverClopper( Volts.of(-12), Volts.of(12)); - ballTunnelSetpoint = + ballTunnelTopSetpoint = new Setpoint<>( Volts.of(0), - V2_TurnoverClopperConstants.BALL_TUNNEL_CONSTANTS.voltageOffsetStep, + V2_TurnoverClopperConstants.BALL_TUNNEL_TOP_CONSTANTS.voltageOffsetStep, Volts.of(-12), Volts.of(12)); - ballTunnelOverrideSetpoint = + + ballTunnelBottomSetpoint = + new Setpoint<>( + Volts.of(0), + V2_TurnoverClopperConstants.BALL_TUNNEL_BOTTOM_CONSTANTS.voltageOffsetStep, + Volts.of(-12), + Volts.of(12)); + + ballTunnelTopOverrideSetpoint = new Setpoint<>( Volts.of(0), - V2_TurnoverClopperConstants.BALL_TUNNEL_CONSTANTS.voltageOffsetStep, + V2_TurnoverClopperConstants.BALL_TUNNEL_TOP_CONSTANTS.voltageOffsetStep, + Volts.of(-12), + Volts.of(12)); + + ballTunnelBottomOverrideSetpoint = + new Setpoint<>( + Volts.of(0), + V2_TurnoverClopperConstants.BALL_TUNNEL_BOTTOM_CONSTANTS.voltageOffsetStep, Volts.of(-12), Volts.of(12)); @@ -81,13 +104,20 @@ public V2_TurnoverClopper( V2_TurnoverClopperConstants.ROLLER_FLOOR_CONSTANTS, " Floor", rollerFloorSetpoint); - ballTunnel = + ballTunnelTop = new GenericRoller( - ballTunnelIO, + ballTunnelTopIO, this, - V2_TurnoverClopperConstants.BALL_TUNNEL_CONSTANTS, + V2_TurnoverClopperConstants.BALL_TUNNEL_TOP_CONSTANTS, " Tunnel", - ballTunnelSetpoint); + ballTunnelTopSetpoint); + ballTunnelBottom = + new GenericRoller( + ballTunnelBottomIO, + this, + V2_TurnoverClopperConstants.BALL_TUNNEL_BOTTOM_CONSTANTS, + " Tunnel", + ballTunnelBottomSetpoint); ballsToWall = new GenericRoller( ballsToWallIO, @@ -96,7 +126,8 @@ public V2_TurnoverClopper( " Side", ballsToWallSetpoint); - overrideBallTunnel = false; + overrideTopBallTunnel = false; + overrideBottomBallTunnel = false; overrideRollerFloor = false; overrideBallsToWall = false; @@ -107,7 +138,8 @@ public V2_TurnoverClopper( ballsToWall.getTorqueCurrent().length > 0 && ballsToWall.getTorqueCurrent()[0] >= V2_TurnoverClopperConstants.BALLS_TO_WALL_CURRENT_THRESHOLD - && Math.abs(ballTunnel.getVoltageGoal().getSetpoint().in(Volts)) > 0) + && Math.abs(ballTunnelTop.getVoltageGoal().getSetpoint().in(Volts)) > 0 + && Math.abs(ballTunnelBottom.getVoltageGoal().getSetpoint().in(Volts)) > 0) .debounce(0.1) .debounce(3, Debouncer.DebounceType.kFalling); ballToWallCurrentTrigger.onTrue( @@ -125,8 +157,11 @@ public V2_TurnoverClopper( @Override public void periodic() { - if (!overrideBallTunnel) { - ballTunnel.setVoltageGoal(ballTunnelSetpoint); + if (!overrideTopBallTunnel) { + ballTunnelTop.setVoltageGoal(ballTunnelTopSetpoint); + } + if (!overrideBottomBallTunnel) { + ballTunnelBottom.setVoltageGoal(ballTunnelBottomSetpoint); } if (!overrideRollerFloor) { rollerFloor.setVoltageGoal(rollerFloorSetpoint); @@ -135,12 +170,14 @@ public void periodic() { ballsToWall.setVoltageGoal(ballsToWallSetpoint); if (shouldReverse) { ballsToWall.setVoltageGoal(V2_TurnoverClopperConstants.BALLS_TO_THE_WALL_REVERSE_VOLTAGE); - } else if (Math.abs(ballTunnel.getVoltageGoal().getSetpoint().in(Volts)) > 0) { + } else if (Math.abs(ballTunnelTop.getVoltageGoal().getSetpoint().in(Volts)) > 0 + && Math.abs(ballTunnelBottom.getVoltageGoal().getSetpoint().in(Volts)) > 0) { ballsToWall.setVoltageGoal(V2_TurnoverClopperConstants.BALLS_TO_THE_WALL_FORWARD_VOLTAGE); } else ballsToWall.setVoltageGoal(Volts.of(0)); } rollerFloor.periodic(); - ballTunnel.periodic(); + ballTunnelTop.periodic(); + ballTunnelBottom.periodic(); ballsToWall.periodic(); Logger.recordOutput( @@ -151,11 +188,18 @@ public void periodic() { String.format("%.1f", rollerFloor.getVoltageGoal().getOffset().in(Volts))); Logger.recordOutput( - "Elastic/Hopper/BallTunnel/Voltage Magnitude", - String.format("%.1f", Math.abs(ballTunnel.getVoltageGoal().getSetpoint().in(Volts)))); + "Elastic/Hopper/BallTunnelTop/Voltage Magnitude", + String.format("%.1f", Math.abs(ballTunnelTop.getVoltageGoal().getSetpoint().in(Volts)))); Logger.recordOutput( - "Elastic/Hopper/BallTunnel/Voltage Offset", - String.format("%.1f", ballTunnel.getVoltageGoal().getOffset().in(Volts))); + "Elastic/Hopper/BallTunnelTop/Voltage Offset", + String.format("%.1f", ballTunnelTop.getVoltageGoal().getOffset().in(Volts))); + + Logger.recordOutput( + "Elastic/Hopper/BallTunnelBottom/Voltage Magnitude", + String.format("%.1f", Math.abs(ballTunnelBottom.getVoltageGoal().getSetpoint().in(Volts)))); + Logger.recordOutput( + "Elastic/Hopper/BallTunnelBottom/Voltage Offset", + String.format("%.1f", ballTunnelBottom.getVoltageGoal().getOffset().in(Volts))); } public Command setRollerFloorVoltage(Voltage voltage) { @@ -192,18 +236,34 @@ public Command stopRollerFloor() { } public Command setBallTunnelVoltage(Voltage voltage) { - return Commands.runOnce(() -> ballTunnelSetpoint.setSetpoint(voltage)); + return Commands.runOnce( + () -> { + ballTunnelTopSetpoint.setSetpoint(voltage); + ballTunnelBottomSetpoint.setSetpoint(voltage); + }); + } + + public Command setBallTunnelVoltage(Voltage voltage, Voltage voltage2) { + return Commands.runOnce( + () -> { + ballTunnelTopSetpoint.setSetpoint(voltage); + ballTunnelBottomSetpoint.setSetpoint(voltage2); + }); } public Command setOverrideBallTunnelVoltage(Voltage voltage) { return Commands.startEnd( () -> { - overrideBallTunnel = true; - ballTunnel.setVoltageGoal(ballTunnelOverrideSetpoint); - ballTunnel.setVoltageGoal(voltage); + overrideTopBallTunnel = true; + overrideBottomBallTunnel = true; + ballTunnelTop.setVoltageGoal(ballTunnelTopOverrideSetpoint); + ballTunnelTop.setVoltageGoal(voltage); + ballTunnelBottom.setVoltageGoal(ballTunnelBottomOverrideSetpoint); + ballTunnelBottom.setVoltageGoal(voltage); }, () -> { - overrideBallTunnel = false; + overrideTopBallTunnel = false; + overrideBottomBallTunnel = false; }, this); } @@ -229,11 +289,19 @@ public Command decrementBallsToWallVelocity() { } public Command incrementBallTunnelVelocity() { - return Commands.runOnce(ballTunnelSetpoint::increment); + return Commands.runOnce( + () -> { + ballTunnelTopSetpoint.increment(); + ballTunnelBottomSetpoint.increment(); + }); } public Command decrementBallTunnelVelocity() { - return Commands.runOnce(ballTunnelSetpoint::decrement); + return Commands.runOnce( + () -> { + ballTunnelTopSetpoint.decrement(); + ballTunnelBottomSetpoint.decrement(); + }); } public Command feedShooterBallTunnel() { @@ -244,6 +312,12 @@ public Command stopBallTunnel() { return setBallTunnelVoltage(Volts.of(0.0)); } + public Command marcusCommand() { + return setBallTunnelVoltage( + V2_TurnoverClopperConstants.BALL_TUNNEL_FEED_VOLTAGE.unaryMinus(), + V2_TurnoverClopperConstants.ROLLER_FLOOR_FEED_VOLTAGE); + } + public Command intake() { return Commands.parallel(feedShooterRollerFloor(), feedShooterBallTunnel()); } diff --git a/src/main/java/frc/robot/subsystems/v2_Turnover/clopper/V2_TurnoverClopperConstants.java b/src/main/java/frc/robot/subsystems/v2_Turnover/clopper/V2_TurnoverClopperConstants.java index 7185b846..765098ed 100644 --- a/src/main/java/frc/robot/subsystems/v2_Turnover/clopper/V2_TurnoverClopperConstants.java +++ b/src/main/java/frc/robot/subsystems/v2_Turnover/clopper/V2_TurnoverClopperConstants.java @@ -23,7 +23,8 @@ public class V2_TurnoverClopperConstants { public static final double BALLS_TO_WALL_CURRENT_THRESHOLD; public static final GenericRollerConstants ROLLER_FLOOR_CONSTANTS; - public static final GenericRollerConstants BALL_TUNNEL_CONSTANTS; + public static final GenericRollerConstants BALL_TUNNEL_TOP_CONSTANTS; + public static final GenericRollerConstants BALL_TUNNEL_BOTTOM_CONSTANTS; public static final GenericRollerConstants BALLS_TO_THE_WALL_CONSTANTS; static { @@ -54,7 +55,7 @@ public class V2_TurnoverClopperConstants { .withCanBus(CANBus.roboRIO()) .withEnableFOC(false) .build(); - BALL_TUNNEL_CONSTANTS = + BALL_TUNNEL_BOTTOM_CONSTANTS = GenericRollerConstants.builder() .withLeaderCANID(31) // Bottom Ball Tunnel .withCurrentLimits( @@ -66,7 +67,23 @@ public class V2_TurnoverClopperConstants { .withRollerGearbox(DCMotor.getKrakenX60Foc(1)) .withRollerMotorGearRatio(42.0) .withLeaderInvertedValue(InvertedValue.CounterClockwise_Positive) - .withAlignedFollowerCANID(30) // Top Ball Tunnel + .withMomentOfInertia(Units.KilogramSquareMeters.of(0.0001)) + .withVoltageOffsetStep(Volts.of(1)) + .withCanBus(CANBus.roboRIO()) + .withEnableFOC(false) + .build(); + BALL_TUNNEL_TOP_CONSTANTS = + GenericRollerConstants.builder() + .withLeaderCANID(30) // Tiop Ball Tunnel + .withCurrentLimits( + CurrentLimits.builder() + .withSupplyCurrentLimit(Amps.of(40.0)) + .withStatorCurrentLimit(Amps.of(80.0)) + .build()) + .withNeutralMode(NeutralModeValue.Coast) + .withRollerGearbox(DCMotor.getKrakenX60Foc(1)) + .withRollerMotorGearRatio(42.0) + .withLeaderInvertedValue(InvertedValue.CounterClockwise_Positive) .withMomentOfInertia(Units.KilogramSquareMeters.of(0.0001)) .withVoltageOffsetStep(Volts.of(1)) .withCanBus(CANBus.roboRIO())