From 1989b2cd47eb4322783c5667f24eafb50f44e27b Mon Sep 17 00:00:00 2001 From: Aaron Larisch Date: Fri, 13 Oct 2023 11:19:26 +0200 Subject: [PATCH] Code Release 2023 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Leon B. Co-authored-by: Dominik Brämer Co-authored-by: Lisa Dasmann Co-authored-by: Alicia Gayda Co-authored-by: Mrunal Hatwar Co-authored-by: Diana Kleingarn Co-authored-by: Thomas Klute Co-authored-by: Aaron Larisch Co-authored-by: Mahdokht Mohammadi Co-authored-by: Arne Moos Co-authored-by: Steven Pieper Co-authored-by: Ingmar Schwarz Co-authored-by: Alexander Voß --- CITATION.cff | 36 + Config/KeyFrameEngine/cheering1.kfm | 98 +- Config/KeyFrameEngine/cheering2.kfm | 64 +- Config/KeyFrameEngine/cheering3.kfm | 296 +- Config/KeyFrameEngine/cheering4.kfm | 187 +- Config/KeyFrameEngine/cheering5.kfm | 23 +- .../KeyFrameEngine/goalkeeperDefendLeft.kfm | 169 +- Config/KeyFrameEngine/kickLeftFast.kfm | 91 +- Config/KeyFrameEngine/kickLeftSlow.kfm | 100 +- Config/KeyFrameEngine/lying.kfm | 29 + .../KeyFrameEngine/penaltyGoalieDiveLeft.kfm | 155 +- .../penaltyGoaliePrepareDive.kfm | 30 +- Config/KeyFrameEngine/playDead.kfm | 13 +- Config/KeyFrameEngine/saveFall.kfm | 47 + Config/KeyFrameEngine/saveFallBack.kfm | 47 + Config/KeyFrameEngine/saveFallFront.kfm | 65 + Config/KeyFrameEngine/sitDown.kfm | 23 +- Config/KeyFrameEngine/stand.kfm | 38 +- Config/KeyFrameEngine/standHigh.kfm | 27 +- Config/KeyFrameEngine/standUpBackNao.kfm | 224 - Config/KeyFrameEngine/standUpBackNaoFast.kfm | 271 +- .../KeyFrameEngine/standUpBackNaoFastOld.kfm | 257 + Config/KeyFrameEngine/standUpBackNaoMed.kfm | 223 - Config/KeyFrameEngine/standUpBackNaoSlow.kfm | 224 - Config/KeyFrameEngine/standUpFrontNaoFast.kfm | 232 +- ...rontNao.kfm => standUpFrontNaoFastOld.kfm} | 146 +- Config/KeyFrameEngine/standUpFrontNaoMed.kfm | 209 - ...NaoSlow.kfm => standUpFrontNaoSlowOld.kfm} | 144 +- Config/KeyFrameEngine/standUpSideNao.kfm | 98 +- .../KeyFrameEngine/standUpSideNaoGoalie.kfm | 76 + Config/KeyFrameEngine/test.kfm | 29 + Config/KeyFrameEngine/testUnstiff.kfm | 29 + Config/KeyFrameEngine/wave_left.kfm | 93 +- Config/KeyFrameEngine/wave_right.kfm | 191 - .../KeyFrameEngine/wideStanceWithStandUp.kfm | 93 +- Config/KickEngine/kickInner.kmc | 560 -- Config/KickEngine/kickInnerFast.kmc | 664 -- Config/KickEngine/kickOuter.kmc | 560 -- Config/KickEngine/kickOuterFast.kmc | 664 -- Config/KickEngine/mostPowerfulKick.kmc | 560 -- Config/KickEngine/penaltyKickInner_left.kmc | 664 -- Config/KickEngine/penaltyKickInner_right.kmc | 664 -- Config/KickEngine/penaltyKickMiddle_left.kmc | 664 -- Config/KickEngine/penaltyKickMiddle_right.kmc | 664 -- Config/KickEngine/penaltyKickOuter_left.kmc | 664 -- Config/KickEngine/penaltyKickOuter_right.kmc | 664 -- Config/{ => Kicks}/KickEngine/kickMiddle.kmc | 27 +- .../{ => Kicks}/KickEngine/kickMiddleFast.kmc | 18 + .../WalkingEngine/keeperKick45.cfg} | 18 +- Config/Kicks/WalkingEngine/kickHack.cfg | 52 + Config/Kicks/WalkingEngine/kickHackLong.cfg | 54 + Config/Kicks/WalkingEngine/kickHackShort.cfg | 53 + .../WalkingEngine/rotateKick45.cfg} | 18 +- .../WalkingEngine/sideKickOuter45.cfg} | 21 +- .../WalkingEngine/sideKickOuterFoot.cfg} | 18 +- Config/Logs/.gitignore | 1 - Config/Robots/Nao/Body/modules.cfg | 1 + Config/Robots/Nao/Body/motionSettings.cfg | 12 +- .../Nao/Body/odometryCorrectionTables.cfg | 48 +- Config/Robots/Nao/Body/robotDimensions.cfg | 2 +- Config/Robots/Nao/Body/usConfiguration.cfg | 22 - Config/Robots/V6/csConverter2019.cfg | 49 +- Config/Robots/V6/flipmObserverGains.cfg | 6 +- Config/Robots/V6/flipmParamsProvider.cfg | 21 +- Config/Robots/V6/flipmStateProvider.cfg | 90 + Config/Robots/V6/walkCalibration.cfg | 7 +- Config/Robots/V6/walkingParamsFLIPM.cfg | 69 +- Config/Scenes/Game.ros2 | 10 +- Config/Scenes/Game7vs7.con | 7 + Config/Scenes/Game7vs7.ros2 | 102 + Config/Scenes/Game7vs7DummyOpponents.con | 7 + Config/Scenes/Game7vs7DummyOpponents.ros2 | 97 + Config/Scenes/GameDummyOpponentsFast.con | 1 + Config/Scenes/GameFast.con | 1 + Config/Scenes/GameFast.ros2 | 10 +- Config/Scenes/GameFast7vs7.con | 7 + Config/Scenes/GameFast7vs7.ros2 | 98 + Config/Scenes/GameFast7vs7DummyOpponents.con | 7 + Config/Scenes/GameFast7vs7DummyOpponents.ros2 | 94 + Config/Scenes/GameFast7vs7PerceptOracle.con | 7 + Config/Scenes/GameFast7vs7PerceptOracle.ros2 | 98 + Config/Scenes/Includes/AutoCalibrator.con | 26 + Config/Scenes/Includes/Ballchaser.con | 30 +- Config/Scenes/Includes/Behavior3D.con | 1 + Config/Scenes/Includes/CNN-Log.con | 8 - Config/Scenes/Includes/CNN-PNGs.con | 7 - Config/Scenes/Includes/CNN-Remote.con | 6 - Config/Scenes/Includes/CSVLogger.con | 9 - Config/Scenes/Includes/CustomSteps.con | 25 - .../Scenes/Includes/CustomStepsSimulator.con | 27 - Config/Scenes/Includes/DebugResponses.con | 3 +- Config/Scenes/Includes/FallDownReduction.con | 3 - Config/Scenes/Includes/Fast.con | 60 +- Config/Scenes/Includes/FieldCoverageView.con | 3 - Config/Scenes/Includes/ImageWriterPNG.con | 5 - Config/Scenes/Includes/InertialSensorData.con | 13 - .../Scenes/Includes/KalmanMultiRobotMap.con | 35 + Config/Scenes/Includes/MotionMindfulness.con | 14 - Config/Scenes/Includes/MultipleBallModel.con | 14 - .../Scenes/Includes/MultipleBallPercept.con | 9 - Config/Scenes/Includes/NaoV6H25.rsi2 | 56 +- Config/Scenes/Includes/Normal.con | 2 +- Config/Scenes/Includes/PerceptOracle.con | 48 +- Config/Scenes/Includes/Perception.con | 2 +- Config/Scenes/Includes/PreviewTest.con | 19 - Config/Scenes/Includes/Sonar.con | 20 + Config/Scenes/Includes/WhistleDbg.con | 22 +- Config/Scenes/ReplayRobot.con | 7 + Config/Scenes/SingleRobotDummyOpponents.ros2 | 2 +- .../Scenes/SingleRobotDummyOpponentsFast.ros2 | 8 +- Config/Scenes/TestScene.con | 2 +- Config/Scenes/TestScene.ros2 | 6 +- Config/Scenes/TwoPlayers.ros2 | 14 +- Config/Sounds/Whistle/.gitignore | 3 + Config/Sounds/alarm.wav | Bin 0 -> 708148 bytes Config/Voices/.gitignore | 1 - ...0.9900_recall_0.6050_threshold_0.85.tflite | Bin 899376 -> 0 bytes Config/WhistleNetMk16.tflite | Bin 0 -> 193960 bytes Config/armContactProvider.cfg | 4 +- Config/autoCalibrator.cfg | 10 +- Config/ballChaserDecisionProvider.cfg | 10 +- Config/ballModelProvider.cfg | 2 +- Config/ball_position_v10_bs4096_19933.tflite | Bin 15332 -> 0 bytes Config/ballchaserProvider.cfg | 27 +- Config/behavior.cfg | 1 + Config/brokenJointDetector.cfg | 10 + ...terProvider2022.cfg => centerProvider.cfg} | 0 ...lassificator_rgb_mini_weighted_rc22.tflite | Bin 33448 -> 0 bytes Config/clipBallPerceptor.cfg | 26 +- Config/clipLineFinder.cfg | 10 +- Config/eventManager.cfg | 14 +- Config/fallDownStateDetector.cfg | 6 - Config/fieldColorProvider.cfg | 5 +- Config/fieldCoverageProvider.cfg | 12 - Config/gyroFallDownStateDetector.cfg | 9 - Config/heatMapProvider.cfg | 2 +- Config/imageWriterPNG.cfg | 20 +- Config/jerseyColorDetector.cfg | 201 + Config/jointErrorCalc.cfg | 9 + Config/kalmanMultiRobotMapProvider.cfg | 95 +- Config/keeperKickFront | 113 - Config/keyFrameEngine.cfg | 49 +- Config/kickHack | 77 - Config/kickHackLong | 77 - Config/kickHackVeryLong | 39 - Config/kickWheelProvider.cfg | 1 + Config/kicksProvider.cfg | 1 + Config/logger.cfg | 4 +- Config/modules.cfg | 50 +- Config/motionCombinator.cfg | 33 +- Config/motionMindfulness.cfg | 21 +- Config/motionSettings.cfg | 12 +- Config/odometryCorrectionTables.cfg | 1 + Config/pathToSpeedStable.cfg | 9 +- Config/patternGenerator2017.cfg | 28 +- Config/penaltyCrossClassifier.cfg | 6 + ...cross_classification_32x32_v1_e1629.tflite | Bin 0 -> 23564 bytes Config/rawGameInfoProvider.cfg | 5 +- Config/robotClassifier.cfg | 14 + Config/robotDimensions.cfg | 9 + Config/roleDynamicProvider.cfg | 2 + Config/roleSelectionProvider.cfg | 181 +- Config/rotateKick45Long | 79 - Config/selfLocator2017.cfg | 9 +- Config/settings.cfg | 4 +- Config/sideKickInner45 | 109 - Config/sideKickInnerFoot | 76 - Config/sideKickOuterFront | 52 - Config/simpleFallDownStateDetector.cfg | 24 + Config/sonarConfiguration.cfg | 2 + Config/sonarDetector.cfg | 22 + Config/specialActions.cfg | 44 +- Config/standEngine.cfg | 4 +- Config/tacticProvider.cfg | 1 + .../ball/ball_position_v23_bs256-4984.tflite | Bin 0 -> 33912 bytes ...early_exit_v2_bs256-5138_early_exit.tflite | Bin 0 -> 5124 bytes ..._early_exit_v2_bs256-5138_remaining.tflite | Bin 0 -> 32492 bytes .../bbox_correctifier_y_rel_0.057.tflite | Bin 0 -> 25416 bytes ...obot_classifier_rec_0.824_thr_0.828.tflite | Bin 0 -> 23712 bytes Config/tfliteInterpreterProvider.cfg | 7 +- Config/timeProvider.cfg | 2 +- Config/usConfiguration.cfg | 22 - Config/usControl.cfg | 6 - Config/walkParamsProvider.cfg | 24 +- Config/whistleDetector.cfg | 24 +- Config/yoloRobotDetector.cfg | 38 +- Install/addRobot.sh | 1 + License.txt | 704 +- Make/CMake/conan.cmake | 7 +- Make/CMake/warnings.cmake | 4 +- Make/Common/copyfiles | 2 +- README.md | 81 +- Src/CMakeLists.txt | 4 +- Src/Controller/AudioPlayer.cpp | 7 +- Src/Controller/ConsoleRoboCupCtrl.cpp | 17 +- Src/Controller/GameController.cpp | 41 +- Src/Controller/GameController.h | 11 +- Src/Controller/LocalRobot.cpp | 8 +- Src/Controller/LocalRobot.h | 4 +- Src/Controller/LogPlayer.cpp | 440 +- Src/Controller/LogPlayer.h | 28 +- Src/Controller/Platform/Windows/Joystick.cpp | 21 +- Src/Controller/Representations/TimeInfo.h | 2 +- Src/Controller/RoboCupCtrl.cpp | 180 +- Src/Controller/RoboCupCtrl.h | 5 + Src/Controller/RobotConsole.cpp | 185 +- Src/Controller/RobotConsole.h | 16 +- Src/Controller/SimulatedRobot.cpp | 116 +- Src/Controller/SimulatedRobot.h | 28 +- Src/Controller/Views/DataView/DataWidget.cpp | 3 +- .../Views/DataView/PropertyManager.h | 2 +- Src/Controller/Views/ImageView.cpp | 11 +- Src/Controller/Views/JointView.cpp | 28 +- Src/Controller/Views/KickView/KickView.cpp | 31 +- Src/Controller/Views/KickView/KickView.h | 2 - .../Views/KickView/KickViewGLWidget.cpp | 10 +- .../Views/KickView/KickViewWidget.cpp | 247 +- .../Views/KickView/KickViewWidget.h | 7 +- Src/Controller/Views/SensorView.cpp | 2 +- .../BehaviorControl/CABSL/BehaviorControl.cpp | 61 +- .../BehaviorControl/CABSL/BehaviorControl.h | 52 +- .../CABSL/BehaviorParameters.h | 1 + .../BehaviorControl/CABSL/Libraries.cpp | 36 - Src/Modules/BehaviorControl/CABSL/Libraries.h | 41 - .../CABSL/Libraries/HelperFunctions.h | 8 +- .../CABSL/Libraries/LibTactic.cpp | 45 - .../CABSL/Libraries/LibTactic.h | 22 - .../BehaviorControl/CABSL/LibraryBase.cpp | 15 - .../BehaviorControl/CABSL/LibraryBase.h | 39 - Src/Modules/BehaviorControl/CABSL/Options.h | 8 +- .../CABSL/Options/BodyControl.h | 9 +- .../Options/Controls/RobotSafetyControl.h | 44 + .../BehaviorControl/CABSL/Options/Playing.h | 18 +- .../CABSL/Options/Roles/Ballchaser.h | 23 +- .../CABSL/Options/Roles/BallchaserKeeper.h | 2 +- .../CABSL/Options/Roles/Keeper.h | 10 +- .../CABSL/Options/Roles/ReplacementKeeper.h | 2 +- .../Skills/BallSearch/KeeperBallSearch.h | 35 +- .../CABSL/Options/Skills/Cheering.h | 12 +- .../CABSL/Options/Skills/DecideKick.h | 40 +- .../CABSL/Options/Skills/Dive.h | 68 +- .../CABSL/Options/Skills/Dribble.h | 1 - .../CABSL/Options/Skills/GetInPosition.h | 32 - .../Options/Skills/GoToFieldCoordinates.h | 21 +- .../Options/Skills/RobotSafetyShutdown.h | 31 + .../CABSL/Options/Skills/StandUp.h | 87 +- .../CABSL/Options/Skills/WalkKick.h | 4 +- .../BehaviorControl/CABSL/Options/Start.h | 81 +- .../CABSL/Options/Testing/Testing.h | 39 + .../CABSL/Options/Testing/TestingUnstiff.h | 39 + .../HeadControl/HeadControl.cpp | 63 +- .../BehaviorControl/HeadControl/HeadControl.h | 5 +- .../JoystickControl/JoystickControl.cpp | 60 +- .../JoystickControl/JoystickControl.h | 15 +- .../BehaviorControl/LEDHandler/LEDHandler.cpp | 526 +- .../BehaviorControl/LEDHandler/LEDHandler.h | 22 + .../BallChaserDecisionProvider.cpp | 212 +- .../BallChaserDecisionProvider.h | 24 +- .../TacticControl/BallSearchProvider.cpp | 56 +- .../TacticControl/BallSearchProvider.h | 6 + .../TacticControl/BallSymbolsProvider.cpp | 98 +- .../TacticControl/BallSymbolsProvider.h | 7 +- .../TacticControl/GameSymbolsProvider.cpp | 37 +- .../TacticControl/GameSymbolsProvider.h | 2 +- .../HeatMapProvider/HeatMapProvider.cpp | 81 + .../HeatMapProvider.h | 13 +- .../HeatMapProvider/HeatMapUtils.h | 158 + .../KickWheelProvider/KickWheelProvider.cpp | 154 + .../KickWheelProvider/KickWheelProvider.h | 30 + .../Enums/KickWithLeftCondition.h | 16 + .../TacticControl/KicksProvider/Enums/Side.h | 8 + .../TacticControl/KicksProvider/Kick.cpp | 59 + .../Kick/Kicks => KicksProvider}/Kick.h | 62 +- .../TacticControl/KicksProvider/KickUtils.h | 83 + .../KicksProvider/KickWithLeftUtils.h | 156 + .../KicksProvider/KicksProvider.cpp | 184 + .../KicksProvider/KicksProvider.h | 47 + .../KicksProvider/Types/Dribble.h | 24 + .../KicksProvider/Types/KickEngineKick.h | 54 + .../KicksProvider/Types/WalkingEngineKick.h | 54 + .../PositioningSymbolsProvider.cpp | 102 +- .../PositioningSymbolsProvider.h | 44 +- .../TacticControl/RoleDynamicProvider.cpp | 346 +- .../TacticControl/RoleDynamicProvider.h | 43 +- .../RoleProvider/BackWingProvider.cpp | 111 + .../RoleProvider/BackWingProvider.h | 76 + .../Ballchaser/BallchaserProvider.cpp | 467 ++ .../{ => Ballchaser}/BallchaserProvider.h | 106 +- .../{Utils => Ballchaser}/BallchaserUtils.h | 4 +- .../Ballchaser/Objectives/GoalObjective.cpp | 295 + .../Objectives}/GoalObjective.h | 18 +- .../Ballchaser/Objectives/MoveObjective.cpp | 180 + .../Objectives}/MoveObjective.h | 29 +- .../Objectives/Test/KickBallTestObjective.cpp | 33 + .../Objectives/Test/KickBallTestObjective.h} | 15 +- .../Test/KickInPositionTestObjective.cpp | 38 + .../Test/KickInPositionTestObjective.h | 23 + .../Test/KickToCenterTestObjective.cpp | 56 + .../Test/KickToCenterTestObjective.h | 23 + .../Objectives/TrickKickObjective.cpp | 182 + .../Objectives/TrickKickObjective.h | 27 + .../RoleProvider/BallchaserProvider.cpp | 393 -- .../RoleProvider/CenterProvider.cpp | 118 + .../RoleProvider/CenterProvider.h | 76 + .../RoleProvider/DefenderLeftProvider.cpp | 26 +- .../RoleProvider/DefenderRightProvider.cpp | 26 +- .../RoleProvider/DefenderSingleProvider.cpp | 2 +- .../RoleProvider/DefenderSingleProvider.h | 1 - .../RoleProvider/FrontWingProvider.cpp | 102 + .../RoleProvider/FrontWingProvider.h | 76 + .../HeatMapProvider/HeatMapProvider.cpp | 81 + .../RoleProvider/KeeperProvider.cpp | 33 +- .../RoleProvider/KeeperProvider.h | 6 +- .../Kick/ExecutableKicks/ExecutableKick.cpp | 7 - .../Kick/ExecutableKicks/ExecutableKick.h | 49 - .../Kick/ExecutableKicks/ExecutableKicks.cpp | 402 -- .../Kick/ExecutableKicks/ExecutableKicks.h | 70 - .../RoleProvider/Kick/HeatMapProvider.cpp | 64 - .../RoleProvider/Kick/KickManager.cpp | 268 - .../RoleProvider/Kick/KickManager.h | 114 - .../Kick/KickManager_ExecutableKick.cpp | 94 - .../RoleProvider/Kick/Kicks/FastLongKick.h | 26 - .../RoleProvider/Kick/Kicks/KickHack.h | 26 - .../RoleProvider/Kick/Kicks/KickHackLong.h | 25 - .../Kick/Kicks/KickHackVeryLong.h | 25 - .../RoleProvider/Kick/Kicks/Rotate45Kick.h | 33 - .../Kick/Kicks/RotateKick45Long.h | 33 - .../RoleProvider/Kick/Kicks/SideKickOuter45.h | 28 - .../Kick/Kicks/SideKickOuterFoot.h | 29 - .../RoleProvider/Kick/Kicks/SlowLongKick.h | 26 - .../RoleProvider/Kick/Models/KickRange.h | 52 - .../RoleProvider/Kick/Models/Optimize.h | 8 - .../RoleProvider/KickManager/Constants.h | 2 + .../Models => KickManager/Enums}/Danger.h | 2 +- .../Enums}/DistanceRequirement.h | 10 +- .../KickManager/Enums/SelectedFoot.h | 8 + .../KickManager/Functions/DrawFunctions.h | 109 + .../KickManager/Functions/ScoreFunctions.cpp | 251 + .../KickManager/Functions/ScoreFunctions.h | 56 + .../KickManager/Functions/SelectFunctions.h | 56 + .../Functions/SelectFunctions_.cpp | 192 + .../Functions/SelectFunctions_Direction.cpp | 55 + .../Functions/SelectFunctions_Pose.cpp | 48 + .../Functions/SelectFunctions_Target.cpp | 82 + .../KickManager/Functions/TargetFunctions.h | 68 + .../KickManager/Models/CurrentKick.h | 30 + .../RoleProvider/KickManager/Models/Factors.h | 56 + .../KickManager/Models/Filterer/Filterer.cpp | 81 + .../KickManager/Models/Filterer/Filterer.h | 75 + .../Models/Filterer/Filterer_Direction.cpp | 56 + .../Models/Filterer/Filterer_Kick.cpp | 0 .../Models/Filterer/Filterer_Pose.cpp | 58 + .../Models/Filterer/Filterer_Target.cpp | 180 + .../KickManager/Models/KickManager.cpp | 71 + .../KickManager/Models/KickManager.h | 37 + .../KickManager/Models/Ranges/Cone.h | 37 + .../KickManager/Models/Ranges/KickCone.h | 31 + .../KickManager/Models/Ranges/KickLine.h | 69 + .../KickManager/Models/Ranges/KickRange.h | 38 + .../KickManager/Models/Selectables/KickPlan.h | 18 + .../Models/Selectables/SelectableDirection.h | 33 + .../Models/Selectables/SelectableKick.h | 17 + .../Models/Selectables/SelectablePose.h | 23 + .../Models/Selectables/SelectableTarget.h | 23 + .../RoleProvider/LeftWingProvider.cpp | 105 + .../RoleProvider/LeftWingProvider.h | 76 + .../RoleProvider/Logs/KickDrawings.h | 6 +- .../Objectives/Ballchaser/GoalObjective.cpp | 232 - .../Objectives/Ballchaser/MoveObjective.cpp | 71 - .../Ballchaser/NoDeadlockObjective.cpp | 13 - .../Ballchaser/NoDeadlockObjective.h | 15 - .../Ballchaser/OneVsOneObjective.cpp | 202 - .../Objectives/Ballchaser/OneVsOneObjective.h | 40 - .../Objectives/Ballchaser/TestObjective.cpp | 69 - .../Objectives/ObjectivesManager.h | 116 - .../Objective.h | 12 +- .../ObjectivesManager/ObjectiveBase.h | 22 + .../ObjectivesManager/ObjectivesList.h | 145 + .../ObjectivesManager/ObjectivesManager.h | 26 + .../ObjectivesManager/ObjectivesSwitch.h | 51 + .../BackupBallchaserProvider.cpp | 13 +- .../{ => OldRoles}/BackupBallchaserProvider.h | 2 + .../BallchaserKeeperProvider.cpp | 4 +- .../{ => OldRoles}/BallchaserKeeperProvider.h | 0 .../CenterProviderOld.cpp} | 75 +- .../CenterProviderOld.h} | 25 +- .../{ => OldRoles}/ReceiverProvider.cpp | 18 +- .../{ => OldRoles}/ReceiverProvider.h | 7 +- .../ReplacementKeeperProvider.cpp | 9 +- .../ReplacementKeeperProvider.h | 6 + .../RoleProvider/RightWingProvider.cpp | 105 + .../RoleProvider/RightWingProvider.h | 76 + .../TacticControl/RoleProvider/RoleProvider.h | 35 +- .../RoleProvider/Utils/AvoidUtils.h | 2 +- .../RoleProvider/Utils/BallUtils.h | 35 +- .../RoleProvider/Utils/BlockUtils.cpp | 191 - .../RoleProvider/Utils/BlockUtils.h | 18 - .../RoleProvider/Utils/DangerUtils.h | 86 - .../RoleProvider/Utils/FieldUtils.h | 17 + .../RoleProvider/Utils/FrontUtils.h | 64 - .../RoleProvider/Utils/HeatUtils.h | 164 - .../RoleProvider/Utils/HysterUtils.h | 37 - .../RoleProvider/Utils/HysteresisUtils.h | 37 + .../RoleProvider/Utils/KickUtils.cpp | 164 +- .../RoleProvider/Utils/KickUtils.h | 36 +- .../RoleProvider/Utils/MathUtils.h | 117 +- .../RoleProvider/Utils/PassUtils.h | 115 - .../RoleProvider/Utils/PathUtils.h | 77 + .../RoleProvider/Utils/PositionUtils.cpp | 81 - .../RoleProvider/Utils/PositionUtils.h | 98 +- .../RoleProvider/Utils/TacticUtils.h | 46 + .../RoleProvider/Utils/TeamUtils.h | 51 +- .../RoleProvider/Utils/ThresholdUtils.h | 39 +- .../TacticControl/RoleSelectionProvider.cpp | 106 +- .../TacticControl/RoleSelectionProvider.h | 19 +- .../TacticControl/TacticProvider.cpp | 135 +- .../TacticControl/TacticProvider.h | 28 +- .../TestProvider/PathToSpeedPressToStop.h | 12 +- .../TestProvider/RoleTestProvider.cpp | 4 +- Src/Modules/CMakeLists.txt | 182 +- .../CognitionConfigurationDataProvider.cpp | 2 +- .../MotionConfigurationDataProvider.cpp | 20 +- .../MotionConfigurationDataProvider.h | 10 +- .../Infrastructure/CameraProviderV6.cpp | 9 +- .../Infrastructure/CognitionLogDataProvider.h | 24 +- .../Infrastructure/HealthScoreProvider.cpp | 75 + .../Infrastructure/HealthScoreProvider.h | 47 + .../Infrastructure/JointRequestProvider.cpp | 19 + .../Infrastructure/JointRequestProvider.h | 26 + .../LowFrameRateImageProvider.cpp | 2 +- .../Infrastructure/MotionLogDataProvider.h | 6 +- Src/Modules/Infrastructure/NaoProviderV6.cpp | 48 +- Src/Modules/Infrastructure/NaoProviderV6.h | 11 + .../Infrastructure/Network/EventManager.cpp | 127 +- .../Infrastructure/Network/EventManager.h | 15 +- .../Network/TeamCommDataPacker.cpp | 90 +- .../Network/TeamCommDataPacker.h | 60 +- .../Network/TeamCommLocalSocketProvider.cpp | 77 + .../Network/TeamCommLocalSocketProvider.h | 44 + .../Infrastructure/Network/TeamCommSender.cpp | 13 +- .../Infrastructure/Network/TeamCommSender.h | 1 - .../Network/TeamCommUDPSocketProvider.cpp | 94 +- .../Network/TeamCommUDPSocketProvider.h | 9 +- .../Network/TeammateDataProvider.cpp | 197 +- .../Network/TeammateDataProvider.h | 45 +- .../Infrastructure/Network/TimeProvider.cpp | 58 +- .../Infrastructure/Network/TimeProvider.h | 2 + .../Infrastructure/PortAudioRecorder.cpp | 5 + .../Infrastructure/RawGameInfoProvider.cpp | 52 +- .../Infrastructure/RawGameInfoProvider.h | 14 +- .../Infrastructure/SequenceImageProvider.cpp | 2 +- .../Infrastructure/ThumbnailProvider.cpp | 20 +- Src/Modules/Infrastructure/USBMounter.cpp | 94 +- .../Infrastructure/WhistleHandlerDortmund.cpp | 2 +- .../DangerMapProvider/DangerMapProvider.cpp | 4 +- .../IMUModelProvider/IMUModelProvider.cpp | 43 +- .../IMUModelProvider/IMUModelProvider.h | 15 +- .../IMUModelProvider/IMURotationState.h | 8 +- .../AngleModels/MultiKalmanModelAngle.h | 5 +- .../AngleModels/MultiKalmanModelAngle_impl.h | 65 +- .../BallModelProvider/BallModelProvider.cpp | 57 +- .../BallModelProvider/BallModelProvider.h | 18 +- .../Models/MultiKalmanModel.h | 4 +- .../Models/MultiKalmanModel_impl.h | 37 +- .../KalmanMultiRobotMapProvider.cpp | 851 ++- .../KalmanMultiRobotMapProvider.h | 182 +- .../KalmanMultiRobotMapProviderParameters.h | 35 +- .../KalmanRobotMapProvider.cpp | 102 +- .../RobotMapProvider/KalmanRobotMapProvider.h | 27 +- .../PathProvider/PathToSpeedStable.cpp | 46 +- .../Modeling/PathProvider/PathToSpeedStable.h | 23 +- .../PathProvider/SimplePathProvider.cpp | 46 +- .../PathProvider/SimplePathProvider.h | 16 +- Src/Modules/Modeling/Predictor.cpp | 31 +- Src/Modules/Modeling/Predictor.h | 19 +- .../SimpleRobotMapProvider.cpp | 165 +- .../SimpleRobotMapProvider.h | 9 +- .../TemplateGenerator/LineMatcher.cpp | 8 +- .../Modeling/TemplateGenerator/LineMatcher.h | 2 - .../WhistleDetector/WhistleDetector.cpp | 359 +- .../WhistleDetector/WhistleDetector.h | 47 +- .../WorldModelGenerator/SelfLocator2017.cpp | 95 +- .../WorldModelGenerator/SelfLocator2017.h | 12 +- .../SelfLocator2017Parameters.h | 4 +- .../models/PoseHypothesis2017.cpp | 25 +- .../models/PoseHypothesis2017.h | 2 +- .../DortmundWalkingEngine/AutoCalibrator.cpp | 230 +- .../DortmundWalkingEngine/AutoCalibrator.h | 35 +- .../DortmundWalkingEngine/CSConverter2019.cpp | 118 +- .../DortmundWalkingEngine/CSConverter2019.h | 5 + .../DortmundWalkingEngine/CoMProviderModule.h | 2 +- .../DortmundWalkingEngine/FLIPMController.cpp | 177 +- .../DortmundWalkingEngine/FLIPMController.h | 3 +- .../FLIPMObservedStateProvider.cpp | 30 +- .../DortmundWalkingEngine/FLIPMObserver.cpp | 52 +- .../DortmundWalkingEngine/FLIPMObserver.h | 2 + .../FLIPMParamsProvider.cpp | 736 +- .../FLIPMParamsProvider.h | 114 +- .../FLIPMStateProvider.cpp | 536 ++ .../FLIPMStateProvider.h | 113 + .../DortmundWalkingEngine/LimbCombinator.cpp | 52 +- .../DortmundWalkingEngine/LimbCombinator.h | 9 +- .../PatternGenerator2017.cpp | 477 +- .../PatternGenerator2017.h | 18 +- .../DortmundWalkingEngine/StepData.cpp | 20 - .../DortmundWalkingEngine/StepData.h | 63 +- .../WalkParamsProvider.cpp | 24 +- .../WalkParamsProvider.h | 3 +- Src/Modules/MotionControl/JointErrorCalc.cpp | 146 + Src/Modules/MotionControl/JointErrorCalc.h | 101 + .../KeyFrameEngine/KeyFrameEngine.cpp | 410 +- .../KeyFrameEngine/KeyFrameEngine.h | 61 +- .../MotionControl/KickEngine/DynPoint.h | 38 + .../MotionControl/KickEngine/KickEngine.cpp | 45 +- .../MotionControl/KickEngine/KickEngine.h | 4 +- .../KickEngine/KickEngineData.cpp | 101 +- .../KickEngine/KickEngineParameters.cpp | 27 +- .../KickEngine/KickEngineParameters.h | 64 +- .../MotionControl/MotionCombinator.cpp | 414 +- Src/Modules/MotionControl/MotionCombinator.h | 43 +- .../MotionControl/MotionMindfulness.cpp | 385 +- Src/Modules/MotionControl/MotionMindfulness.h | 64 +- Src/Modules/MotionControl/MotionSelector.cpp | 7 +- Src/Modules/MotionControl/MotionSelector.h | 2 + Src/Modules/MotionControl/StandEngine.cpp | 4 +- Src/Modules/MotionControl/StandEngine.h | 2 + .../Perception/BodyContourProvider.cpp | 4 - .../Perception/CLIP/BallPerceptTools.cpp | 261 +- .../Perception/CLIP/BallPerceptTools.h | 19 +- .../Perception/CLIP/CLIPBallPerceptor.cpp | 1892 +++--- .../Perception/CLIP/CLIPBallPerceptor.h | 211 +- .../Perception/CLIP/CLIPLineFinder.cpp | 288 +- Src/Modules/Perception/CLIP/CLIPLineFinder.h | 27 +- .../Perception/CLIP/CLIPPreprocessor.cpp | 94 +- .../Perception/CLIP/CLIPPreprocessor.h | 13 +- .../Perception/CLIP/FieldColorProvider.cpp | 79 +- .../Perception/CLIP/FieldColorProvider.h | 8 +- .../CLIP/TfliteInterpreterProvider.cpp | 206 +- .../CLIP/TfliteInterpreterProvider.h | 30 +- .../Perception/CMCalibration/CMCorrector.cpp | 12 +- .../Perception/CMCalibration/CMCorrector.h | 4 +- .../Perception/CNNs/CLIPBallPerceptorCNNs.cpp | 11 + .../Perception/CNNs/YoloRobotDetectorCNNs.cpp | 10 + Src/Modules/Perception/CNNs/cnn_qball.c | 26 +- .../Perception/CognitionMindfulness.cpp | 18 + Src/Modules/Perception/CognitionMindfulness.h | 9 + Src/Modules/Perception/ImageWriterPNG.cpp | 460 +- Src/Modules/Perception/ImageWriterPNG.h | 57 +- .../Perception/JerseyColorDetector.cpp | 492 ++ Src/Modules/Perception/JerseyColorDetector.h | 142 + .../Perception/OracledPerceptsProvider.cpp | 34 +- .../Perception/OracledPerceptsProvider.h | 8 +- .../Perception/PenaltyCrossClassifier.cpp | 187 + .../Perception/PenaltyCrossClassifier.h | 77 + Src/Modules/Perception/RobotClassifier.cpp | 455 ++ Src/Modules/Perception/RobotClassifier.h | 94 + .../Perception/RobotOrientationDetector.cpp | 15 + .../Perception/RobotOrientationDetector.h | 24 + .../Perception/RobotsPerceptProvider.cpp | 20 + .../Perception/RobotsPerceptProvider.h | 20 + Src/Modules/Perception/SonarDetector.cpp | 256 + Src/Modules/Perception/SonarDetector.h | 173 + Src/Modules/Perception/TFlite.h | 2 +- Src/Modules/Perception/YoloRobotDetector.cpp | 457 +- Src/Modules/Perception/YoloRobotDetector.h | 98 +- Src/Modules/Sensing/ArmContactProvider.cpp | 10 +- Src/Modules/Sensing/ArmContactProvider.h | 6 +- Src/Modules/Sensing/BrokenJointDetector.cpp | 128 + Src/Modules/Sensing/BrokenJointDetector.h | 82 + Src/Modules/Sensing/CoPProvider.cpp | 98 +- Src/Modules/Sensing/CoPProvider.h | 12 +- Src/Modules/Sensing/GroundContactDetector.cpp | 3 +- .../Sensing/GyroFallDownStateDetector.cpp | 126 - .../Sensing/GyroFallDownStateDetector.h | 67 - Src/Modules/Sensing/RobotModelProvider.cpp | 18 +- .../Sensing/SimpleFallDownStateDetector.cpp | 349 + .../Sensing/SimpleFallDownStateDetector.h | 118 + Src/Platform/Windows/Semaphore.cpp | 3 +- Src/Processes/Cognition.cpp | 6 +- Src/Processes/Motion.cpp | 37 +- .../BehaviorControl/BallChaserDecision.cpp | 14 + .../BehaviorControl/BallChaserDecision.h | 10 +- .../BehaviorControl/BallSearch.h | 2 +- .../BehaviorControl/BehaviorData.h | 54 +- .../BehaviorControl/BehaviorLEDRequest.h | 6 + .../BehaviorControl/GameSymbols.h | 2 +- .../BehaviorControl/RoleSelection.h | 4 +- .../BehaviorControl/RoleSymbols.cpp | 54 +- .../BehaviorControl/RoleSymbols.h | 3 +- .../BehaviorControl/RoleSymbols/BackWing.h | 14 + .../BehaviorControl/RoleSymbols/Ballchaser.h | 40 +- .../BehaviorControl/RoleSymbols/Center.h | 6 +- .../RoleSymbols/DefenderLeft.h | 24 +- .../RoleSymbols/DefenderRight.h | 26 +- .../RoleSymbols/DefenderSingle.h | 6 +- .../BehaviorControl/RoleSymbols/FrontWing.h | 42 + .../BehaviorControl/RoleSymbols/Keeper.h | 11 +- .../BehaviorControl/RoleSymbols/LeftWing.h | 45 + .../RoleSymbols/PositioningAndKickSymbols.h | 43 + .../{ => RoleSymbols}/PositioningSymbols.cpp | 0 .../{ => RoleSymbols}/PositioningSymbols.h | 0 .../BehaviorControl/RoleSymbols/Receiver.h | 4 +- .../RoleSymbols/ReplacementKeeper.h | 11 +- .../BehaviorControl/RoleSymbols/RightWing.h | 44 + .../BehaviorControl/TacticSymbols.h | 13 +- Src/Representations/CMakeLists.txt | 26 +- .../Configuration/FieldDimensions.cpp | 8 +- .../Configuration/FieldDimensions.h | 2 +- .../Configuration/MotionSettings.h | 12 +- .../Configuration/OdometryCorrectionTable.h | 19 +- .../Configuration/RobotDimensions.h | 13 +- .../Configuration/SonarConfiguration.h | 10 + .../Configuration/UsConfiguration.h | 22 - .../Infrastructure/CameraInfo.h | 5 +- .../Infrastructure/GameInfo.cpp | 8 +- Src/Representations/Infrastructure/GameInfo.h | 9 +- .../Infrastructure/HealthScore.h | 10 + Src/Representations/Infrastructure/Image.cpp | 364 +- Src/Representations/Infrastructure/Image.h | 143 +- .../Infrastructure/JPEGImage.cpp | 176 +- .../Infrastructure/JPEGImage.h | 37 +- .../Infrastructure/JointRequest.h | 4 +- .../Infrastructure/LEDRequest.h | 4 +- .../Infrastructure/RoboCupGameControlData.h | 18 +- .../Infrastructure/SensorData/FsrSensorData.h | 5 +- .../SensorData/SonarSensorData.h | 10 + .../SensorData/SystemSensorData.h | 1 + .../Infrastructure/SensorData/UsSensorData.h | 31 - .../Infrastructure/TeamCommData.cpp | 49 - .../Infrastructure/TeamCommData.h | 57 +- .../Infrastructure/TeamCommEvents.h | 19 +- .../Infrastructure/TeamCommSocket.h | 2 +- .../Infrastructure/TeamInfo.cpp | 17 +- .../Infrastructure/TeammateData.cpp | 159 +- .../Infrastructure/TeammateData.h | 119 +- Src/Representations/Infrastructure/Time.h | 6 +- .../Infrastructure/USRequest.h | 20 - .../Infrastructure/YoloInput.cpp | 16 +- Src/Representations/Modeling/BallModel.cpp | 17 +- Src/Representations/Modeling/BallModel.h | 17 +- Src/Representations/Modeling/HeatMap.h | 211 +- .../Modeling/HeatMapCollection.h | 12 +- Src/Representations/Modeling/KickWheel.cpp | 77 + Src/Representations/Modeling/KickWheel.h | 30 + Src/Representations/Modeling/Kicks.h | 13 + Src/Representations/Modeling/RobotMap.h | 85 +- Src/Representations/Modeling/RobotPose.cpp | 20 +- Src/Representations/Modeling/RobotPose.h | 19 +- .../Modeling/RobotPoseHypotheses.h | 43 +- .../Modeling/WhistleDortmund.h | 26 +- .../MotionControl/FLIPMObservedState.h | 4 +- .../MotionControl/FLIPMObserverGains.h | 14 +- .../MotionControl/FLIPMParams.h | 20 +- .../MotionControl/JointError.cpp | 105 + .../MotionControl/JointError.h | 21 + .../MotionControl/KickRequest.h | 9 +- .../MotionControl/MotionRequest.cpp | 12 +- .../MotionControl/MotionRequest.h | 5 +- .../MotionControl/MotionState.h | 22 +- .../MotionControl/ObservedFLIPMError.h | 6 +- Src/Representations/MotionControl/RefZMP.h | 9 +- .../MotionControl/SpecialActionRequest.h | 30 +- .../MotionControl/SpecialActionsOutput.h | 2 + Src/Representations/MotionControl/SpeedInfo.h | 27 +- .../MotionControl/WalkCalibration.h | 2 +- .../MotionControl/WalkRequest.h | 10 +- .../MotionControl/WalkingEngineParams.h | 11 +- .../MotionControl/WalkingInfo.h | 7 +- .../Perception/BallPercept.cpp | 145 +- Src/Representations/Perception/BallPercept.h | 41 +- Src/Representations/Perception/BallSpot.h | 47 +- Src/Representations/Perception/BallSpots.h | 46 +- .../Perception/BodyContour.cpp | 42 +- Src/Representations/Perception/BodyContour.h | 5 +- .../Perception/CognitionState.h | 5 + Src/Representations/Perception/ImagePatch.cpp | 50 + Src/Representations/Perception/ImagePatch.h | 24 + .../Perception/PenaltyCrossHypotheses.cpp | 52 + .../Perception/PenaltyCrossHypotheses.h | 89 +- .../Perception/PenaltyCrossPercept.h | 8 +- .../Perception/RobotsPercept.cpp | 133 +- .../Perception/RobotsPercept.h | 112 +- .../Perception/SonarPercept.cpp | 62 + Src/Representations/Perception/SonarPercept.h | 27 + .../Perception/TfliteInterpreter.cpp | 43 + .../Perception/TfliteInterpreter.h | 41 +- .../Sensing/BrokenJointState.h | 32 + Src/Representations/Sensing/FallDownState.cpp | 2 - Src/Representations/Sensing/FallDownState.h | 17 +- Src/Representations/Sensing/ZMPModel.h | 5 +- Src/Tools/CMakeLists.txt | 2 + Src/Tools/Cabsl.h | 15 +- Src/Tools/ColorModelConversions.h | 21 +- Src/Tools/Debugging/CSVLogger.cpp | 7 +- Src/Tools/Debugging/DebugDataStreamer.cpp | 2 +- Src/Tools/Debugging/DebugDrawings.h | 24 +- Src/Tools/Debugging/DebugDrawings3D.h | 12 +- Src/Tools/Debugging/DebugImages.h | 1 + Src/Tools/Debugging/Stopwatch.h | 24 + Src/Tools/Debugging/TimingManager.cpp | 4 +- Src/Tools/ImageCoordinateTransformations.h | 9 + Src/Tools/ImageProcessing/stb_image.h | 10 +- Src/Tools/Joints.h | 7 +- Src/Tools/Math/Angle.h | 3 + Src/Tools/Math/Eigen.h | 3 +- Src/Tools/Math/EigenMatrixBaseExtensions.h | 11 +- Src/Tools/Math/Geometry.cpp | 18 +- Src/Tools/Math/Geometry.h | 5 +- Src/Tools/Math/Pose2f.h | 12 +- Src/Tools/Math/Pose3f.h | 8 +- Src/Tools/MessageQueue/MessageIDs.h | 27 +- Src/Tools/MessageQueue/MessageQueue.cpp | 2 +- Src/Tools/MessageQueue/MessageQueueBase.cpp | 17 + Src/Tools/MessageQueue/MessageQueueBase.h | 8 + Src/Tools/Modeling/PoseGenerator.h | 6 +- Src/Tools/Module/Module.h | 4 +- Src/Tools/Module/ModuleManager.cpp | 13 +- Src/Tools/Motion/InverseKinematic.cpp | 8 +- Src/Tools/Motion/ZmpPreviewController3.cpp | 22 +- Src/Tools/Network/TcpComm.cpp | 29 + Src/Tools/Network/TcpComm.h | 7 + Src/Tools/Network/UdpComm.cpp | 13 +- Src/Tools/Network/UdpComm.h | 2 +- Src/Tools/ProcessFramework/ExecutorObserver.h | 4 + Src/Tools/ProcessFramework/Receiver.cpp | 8 +- Src/Tools/ProcessFramework/Sender.cpp | 8 +- Src/Tools/ProcessFramework/SubThread.cpp | 106 +- Src/Tools/ProcessFramework/SubThread.h | 5 +- Src/Tools/RingBufferWithSum.h | 1 + Src/Tools/SSE.h | 25 +- Src/Tools/Streams/AutoStreamable.h | 1 + Src/Tools/Streams/Compressed.cpp | 95 + Src/Tools/Streams/Compressed.h | 221 + Src/Tools/Streams/Eigen.h | 4 +- Src/Tools/Streams/InOut.h | 14 +- Src/Tools/Streams/InStreams.h | 13 + Src/Tools/Streams/OutStreams.cpp | 143 + Src/Tools/Streams/OutStreams.h | 134 +- Src/Tools/Streams/Streamable.h | 6 +- Src/Utils/dorsh/agents/DataAgent.cpp | 4 +- Src/Utils/dorsh/cmdlib/Commands.cpp | 3 +- Src/Utils/dorsh/cmds/DeployCmd.cpp | 25 +- Src/Utils/dorsh/cmds/DeployCmd.h | 7 +- Src/Utils/dorsh/cmds/PushConfigCmd.cpp | 54 +- Src/Utils/dorsh/cmds/PushConfigCmd.h | 6 +- Src/Utils/dorsh/models/Team.cpp | 2 +- Src/Utils/dorsh/ui/MainWindow.cpp | 5 +- Src/naodevilsbase/naodevilsbase.cpp | 45 +- Src/naodevilsbase/naodevilsbase.h | 8 +- .../include/RoboCupGameControlData.h | 29 +- Util/GameController/include/SPLCoachMessage.h | 35 - .../include/SPLStandardMessage.h | 84 - Util/SimRobot/Src/SimRobot/MainWindow.cpp | 8 +- .../Src/SimRobot/SceneGraphDockWidget.cpp | 4 +- .../SimRobot/Src/SimRobotCore2/CoreModule.cpp | 3 +- .../Src/SimRobotCore2/Parser/Parser.cpp | 43 +- .../Src/SimRobotCore2/Parser/Reader.cpp | 8 +- .../Src/SimRobotCore2/SimRobotCore2.h | 5 + .../Appearances/ComplexAppearance.cpp | 34 +- .../Src/SimRobotCore2/Simulation/Body.cpp | 11 +- .../Src/SimRobotCore2/Simulation/Body.h | 2 + .../Src/SimRobotCore2/Simulation/Compound.cpp | 6 +- .../Sensors/ApproxDistanceSensor.cpp | 3 +- .../Simulation/Sensors/DepthImageSensor.cpp | 6 +- Util/qtpropertybrowser/qtpropertymanager.cpp | 6017 +++++++++-------- Util/qtpropertybrowser/qtvariantproperty.cpp | 2790 ++++---- 766 files changed, 31700 insertions(+), 28188 deletions(-) create mode 100644 CITATION.cff create mode 100644 Config/KeyFrameEngine/lying.kfm create mode 100644 Config/KeyFrameEngine/saveFall.kfm create mode 100644 Config/KeyFrameEngine/saveFallBack.kfm create mode 100644 Config/KeyFrameEngine/saveFallFront.kfm delete mode 100644 Config/KeyFrameEngine/standUpBackNao.kfm create mode 100644 Config/KeyFrameEngine/standUpBackNaoFastOld.kfm delete mode 100644 Config/KeyFrameEngine/standUpBackNaoMed.kfm delete mode 100644 Config/KeyFrameEngine/standUpBackNaoSlow.kfm rename Config/KeyFrameEngine/{standUpFrontNao.kfm => standUpFrontNaoFastOld.kfm} (63%) delete mode 100644 Config/KeyFrameEngine/standUpFrontNaoMed.kfm rename Config/KeyFrameEngine/{standUpFrontNaoSlow.kfm => standUpFrontNaoSlowOld.kfm} (71%) create mode 100644 Config/KeyFrameEngine/standUpSideNaoGoalie.kfm create mode 100644 Config/KeyFrameEngine/test.kfm create mode 100644 Config/KeyFrameEngine/testUnstiff.kfm delete mode 100644 Config/KeyFrameEngine/wave_right.kfm delete mode 100644 Config/KickEngine/kickInner.kmc delete mode 100644 Config/KickEngine/kickInnerFast.kmc delete mode 100644 Config/KickEngine/kickOuter.kmc delete mode 100644 Config/KickEngine/kickOuterFast.kmc delete mode 100644 Config/KickEngine/mostPowerfulKick.kmc delete mode 100644 Config/KickEngine/penaltyKickInner_left.kmc delete mode 100644 Config/KickEngine/penaltyKickInner_right.kmc delete mode 100644 Config/KickEngine/penaltyKickMiddle_left.kmc delete mode 100644 Config/KickEngine/penaltyKickMiddle_right.kmc delete mode 100644 Config/KickEngine/penaltyKickOuter_left.kmc delete mode 100644 Config/KickEngine/penaltyKickOuter_right.kmc rename Config/{ => Kicks}/KickEngine/kickMiddle.kmc (95%) rename Config/{ => Kicks}/KickEngine/kickMiddleFast.kmc (96%) rename Config/{keeperKick45 => Kicks/WalkingEngine/keeperKick45.cfg} (78%) create mode 100644 Config/Kicks/WalkingEngine/kickHack.cfg create mode 100644 Config/Kicks/WalkingEngine/kickHackLong.cfg create mode 100644 Config/Kicks/WalkingEngine/kickHackShort.cfg rename Config/{rotateKick45 => Kicks/WalkingEngine/rotateKick45.cfg} (79%) rename Config/{sideKickOuter45 => Kicks/WalkingEngine/sideKickOuter45.cfg} (76%) rename Config/{sideKickOuterFoot => Kicks/WalkingEngine/sideKickOuterFoot.cfg} (75%) delete mode 100644 Config/Logs/.gitignore delete mode 100644 Config/Robots/Nao/Body/usConfiguration.cfg create mode 100644 Config/Robots/V6/flipmStateProvider.cfg create mode 100644 Config/Scenes/Game7vs7.con create mode 100644 Config/Scenes/Game7vs7.ros2 create mode 100644 Config/Scenes/Game7vs7DummyOpponents.con create mode 100644 Config/Scenes/Game7vs7DummyOpponents.ros2 create mode 100644 Config/Scenes/GameFast7vs7.con create mode 100644 Config/Scenes/GameFast7vs7.ros2 create mode 100644 Config/Scenes/GameFast7vs7DummyOpponents.con create mode 100644 Config/Scenes/GameFast7vs7DummyOpponents.ros2 create mode 100644 Config/Scenes/GameFast7vs7PerceptOracle.con create mode 100644 Config/Scenes/GameFast7vs7PerceptOracle.ros2 create mode 100644 Config/Scenes/Includes/AutoCalibrator.con delete mode 100644 Config/Scenes/Includes/CNN-Log.con delete mode 100644 Config/Scenes/Includes/CNN-PNGs.con delete mode 100644 Config/Scenes/Includes/CNN-Remote.con delete mode 100644 Config/Scenes/Includes/CSVLogger.con delete mode 100644 Config/Scenes/Includes/CustomSteps.con delete mode 100644 Config/Scenes/Includes/CustomStepsSimulator.con delete mode 100644 Config/Scenes/Includes/FallDownReduction.con delete mode 100644 Config/Scenes/Includes/FieldCoverageView.con delete mode 100644 Config/Scenes/Includes/ImageWriterPNG.con delete mode 100644 Config/Scenes/Includes/InertialSensorData.con create mode 100644 Config/Scenes/Includes/KalmanMultiRobotMap.con delete mode 100644 Config/Scenes/Includes/MotionMindfulness.con delete mode 100644 Config/Scenes/Includes/MultipleBallModel.con delete mode 100644 Config/Scenes/Includes/MultipleBallPercept.con delete mode 100644 Config/Scenes/Includes/PreviewTest.con create mode 100644 Config/Scenes/Includes/Sonar.con create mode 100644 Config/Sounds/Whistle/.gitignore create mode 100644 Config/Sounds/alarm.wav delete mode 100644 Config/Voices/.gitignore delete mode 100644 Config/WhistleDet_precision_0.9900_recall_0.6050_threshold_0.85.tflite create mode 100644 Config/WhistleNetMk16.tflite delete mode 100644 Config/ball_position_v10_bs4096_19933.tflite create mode 100644 Config/brokenJointDetector.cfg rename Config/{centerProvider2022.cfg => centerProvider.cfg} (100%) delete mode 100644 Config/classificator_rgb_mini_weighted_rc22.tflite delete mode 100644 Config/fallDownStateDetector.cfg delete mode 100644 Config/fieldCoverageProvider.cfg delete mode 100644 Config/gyroFallDownStateDetector.cfg create mode 100644 Config/jerseyColorDetector.cfg create mode 100644 Config/jointErrorCalc.cfg delete mode 100644 Config/keeperKickFront delete mode 100644 Config/kickHack delete mode 100644 Config/kickHackLong delete mode 100644 Config/kickHackVeryLong create mode 100644 Config/kickWheelProvider.cfg create mode 100644 Config/kicksProvider.cfg create mode 100644 Config/penaltyCrossClassifier.cfg create mode 100644 Config/penalty_cross_classification_32x32_v1_e1629.tflite create mode 100644 Config/robotClassifier.cfg create mode 100644 Config/roleDynamicProvider.cfg delete mode 100644 Config/rotateKick45Long delete mode 100644 Config/sideKickInner45 delete mode 100644 Config/sideKickInnerFoot delete mode 100644 Config/sideKickOuterFront create mode 100644 Config/simpleFallDownStateDetector.cfg create mode 100644 Config/sonarConfiguration.cfg create mode 100644 Config/sonarDetector.cfg create mode 100644 Config/tacticProvider.cfg create mode 100644 Config/tflite/ball/ball_position_v23_bs256-4984.tflite create mode 100644 Config/tflite/ball/ball_position_v23_early_exit_v2_bs256-5138_early_exit.tflite create mode 100644 Config/tflite/ball/ball_position_v23_early_exit_v2_bs256-5138_remaining.tflite create mode 100644 Config/tflite/robot/bbox_correctifier_y_rel_0.057.tflite create mode 100644 Config/tflite/robot/robot_classifier_rec_0.824_thr_0.828.tflite delete mode 100644 Config/usConfiguration.cfg delete mode 100644 Config/usControl.cfg mode change 100644 => 100755 Install/addRobot.sh delete mode 100644 Src/Modules/BehaviorControl/CABSL/Libraries.cpp delete mode 100644 Src/Modules/BehaviorControl/CABSL/Libraries.h delete mode 100644 Src/Modules/BehaviorControl/CABSL/Libraries/LibTactic.cpp delete mode 100644 Src/Modules/BehaviorControl/CABSL/Libraries/LibTactic.h delete mode 100644 Src/Modules/BehaviorControl/CABSL/LibraryBase.cpp delete mode 100644 Src/Modules/BehaviorControl/CABSL/LibraryBase.h create mode 100644 Src/Modules/BehaviorControl/CABSL/Options/Controls/RobotSafetyControl.h delete mode 100644 Src/Modules/BehaviorControl/CABSL/Options/Skills/GetInPosition.h create mode 100644 Src/Modules/BehaviorControl/CABSL/Options/Skills/RobotSafetyShutdown.h create mode 100644 Src/Modules/BehaviorControl/CABSL/Options/Testing/Testing.h create mode 100644 Src/Modules/BehaviorControl/CABSL/Options/Testing/TestingUnstiff.h create mode 100644 Src/Modules/BehaviorControl/TacticControl/HeatMapProvider/HeatMapProvider.cpp rename Src/Modules/BehaviorControl/TacticControl/{RoleProvider/Kick => HeatMapProvider}/HeatMapProvider.h (85%) create mode 100644 Src/Modules/BehaviorControl/TacticControl/HeatMapProvider/HeatMapUtils.h create mode 100644 Src/Modules/BehaviorControl/TacticControl/KickWheelProvider/KickWheelProvider.cpp create mode 100644 Src/Modules/BehaviorControl/TacticControl/KickWheelProvider/KickWheelProvider.h create mode 100644 Src/Modules/BehaviorControl/TacticControl/KicksProvider/Enums/KickWithLeftCondition.h create mode 100644 Src/Modules/BehaviorControl/TacticControl/KicksProvider/Enums/Side.h create mode 100644 Src/Modules/BehaviorControl/TacticControl/KicksProvider/Kick.cpp rename Src/Modules/BehaviorControl/TacticControl/{RoleProvider/Kick/Kicks => KicksProvider}/Kick.h (53%) create mode 100644 Src/Modules/BehaviorControl/TacticControl/KicksProvider/KickUtils.h create mode 100644 Src/Modules/BehaviorControl/TacticControl/KicksProvider/KickWithLeftUtils.h create mode 100644 Src/Modules/BehaviorControl/TacticControl/KicksProvider/KicksProvider.cpp create mode 100644 Src/Modules/BehaviorControl/TacticControl/KicksProvider/KicksProvider.h create mode 100644 Src/Modules/BehaviorControl/TacticControl/KicksProvider/Types/Dribble.h create mode 100644 Src/Modules/BehaviorControl/TacticControl/KicksProvider/Types/KickEngineKick.h create mode 100644 Src/Modules/BehaviorControl/TacticControl/KicksProvider/Types/WalkingEngineKick.h create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/BackWingProvider.cpp create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/BackWingProvider.h create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/BallchaserProvider.cpp rename Src/Modules/BehaviorControl/TacticControl/RoleProvider/{ => Ballchaser}/BallchaserProvider.h (64%) rename Src/Modules/BehaviorControl/TacticControl/RoleProvider/{Utils => Ballchaser}/BallchaserUtils.h (85%) create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/GoalObjective.cpp rename Src/Modules/BehaviorControl/TacticControl/RoleProvider/{Objectives/Ballchaser => Ballchaser/Objectives}/GoalObjective.h (66%) create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/MoveObjective.cpp rename Src/Modules/BehaviorControl/TacticControl/RoleProvider/{Objectives/Ballchaser => Ballchaser/Objectives}/MoveObjective.h (57%) create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/Test/KickBallTestObjective.cpp rename Src/Modules/BehaviorControl/TacticControl/RoleProvider/{Objectives/Ballchaser/TestObjective.h => Ballchaser/Objectives/Test/KickBallTestObjective.h} (58%) create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/Test/KickInPositionTestObjective.cpp create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/Test/KickInPositionTestObjective.h create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/Test/KickToCenterTestObjective.cpp create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/Test/KickToCenterTestObjective.h create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/TrickKickObjective.cpp create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/TrickKickObjective.h delete mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/BallchaserProvider.cpp create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/CenterProvider.cpp create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/CenterProvider.h create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/FrontWingProvider.cpp create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/FrontWingProvider.h create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/HeatMapProvider/HeatMapProvider.cpp delete mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/ExecutableKicks/ExecutableKick.cpp delete mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/ExecutableKicks/ExecutableKick.h delete mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/ExecutableKicks/ExecutableKicks.cpp delete mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/ExecutableKicks/ExecutableKicks.h delete mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/HeatMapProvider.cpp delete mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/KickManager.cpp delete mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/KickManager.h delete mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/KickManager_ExecutableKick.cpp delete mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/FastLongKick.h delete mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/KickHack.h delete mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/KickHackLong.h delete mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/KickHackVeryLong.h delete mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/Rotate45Kick.h delete mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/RotateKick45Long.h delete mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/SideKickOuter45.h delete mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/SideKickOuterFoot.h delete mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/SlowLongKick.h delete mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Models/KickRange.h delete mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Models/Optimize.h create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Constants.h rename Src/Modules/BehaviorControl/TacticControl/RoleProvider/{Kick/Models => KickManager/Enums}/Danger.h (78%) rename Src/Modules/BehaviorControl/TacticControl/RoleProvider/{Kick/Models => KickManager/Enums}/DistanceRequirement.h (93%) create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Enums/SelectedFoot.h create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/DrawFunctions.h create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/ScoreFunctions.cpp create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/ScoreFunctions.h create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/SelectFunctions.h create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/SelectFunctions_.cpp create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/SelectFunctions_Direction.cpp create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/SelectFunctions_Pose.cpp create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/SelectFunctions_Target.cpp create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/TargetFunctions.h create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/CurrentKick.h create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Factors.h create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Filterer/Filterer.cpp create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Filterer/Filterer.h create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Filterer/Filterer_Direction.cpp create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Filterer/Filterer_Kick.cpp create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Filterer/Filterer_Pose.cpp create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Filterer/Filterer_Target.cpp create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/KickManager.cpp create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/KickManager.h create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Ranges/Cone.h create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Ranges/KickCone.h create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Ranges/KickLine.h create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Ranges/KickRange.h create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Selectables/KickPlan.h create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Selectables/SelectableDirection.h create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Selectables/SelectableKick.h create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Selectables/SelectablePose.h create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Selectables/SelectableTarget.h create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/LeftWingProvider.cpp create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/LeftWingProvider.h delete mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/GoalObjective.cpp delete mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/MoveObjective.cpp delete mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/NoDeadlockObjective.cpp delete mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/NoDeadlockObjective.h delete mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/OneVsOneObjective.cpp delete mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/OneVsOneObjective.h delete mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/TestObjective.cpp delete mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/ObjectivesManager.h rename Src/Modules/BehaviorControl/TacticControl/RoleProvider/{Objectives => ObjectivesManager}/Objective.h (54%) create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/ObjectivesManager/ObjectiveBase.h create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/ObjectivesManager/ObjectivesList.h create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/ObjectivesManager/ObjectivesManager.h create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/ObjectivesManager/ObjectivesSwitch.h rename Src/Modules/BehaviorControl/TacticControl/RoleProvider/{ => OldRoles}/BackupBallchaserProvider.cpp (94%) rename Src/Modules/BehaviorControl/TacticControl/RoleProvider/{ => OldRoles}/BackupBallchaserProvider.h (95%) rename Src/Modules/BehaviorControl/TacticControl/RoleProvider/{ => OldRoles}/BallchaserKeeperProvider.cpp (93%) rename Src/Modules/BehaviorControl/TacticControl/RoleProvider/{ => OldRoles}/BallchaserKeeperProvider.h (100%) rename Src/Modules/BehaviorControl/TacticControl/RoleProvider/{CenterProvider2022.cpp => OldRoles/CenterProviderOld.cpp} (73%) rename Src/Modules/BehaviorControl/TacticControl/RoleProvider/{CenterProvider2022.h => OldRoles/CenterProviderOld.h} (81%) rename Src/Modules/BehaviorControl/TacticControl/RoleProvider/{ => OldRoles}/ReceiverProvider.cpp (95%) rename Src/Modules/BehaviorControl/TacticControl/RoleProvider/{ => OldRoles}/ReceiverProvider.h (94%) rename Src/Modules/BehaviorControl/TacticControl/RoleProvider/{ => OldRoles}/ReplacementKeeperProvider.cpp (94%) rename Src/Modules/BehaviorControl/TacticControl/RoleProvider/{ => OldRoles}/ReplacementKeeperProvider.h (83%) create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/RightWingProvider.cpp create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/RightWingProvider.h delete mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/BlockUtils.cpp delete mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/BlockUtils.h delete mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/DangerUtils.h delete mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/FrontUtils.h delete mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/HeatUtils.h delete mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/HysterUtils.h create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/HysteresisUtils.h delete mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/PassUtils.h create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/PathUtils.h delete mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/PositionUtils.cpp create mode 100644 Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/TacticUtils.h create mode 100644 Src/Modules/Infrastructure/HealthScoreProvider.cpp create mode 100644 Src/Modules/Infrastructure/HealthScoreProvider.h create mode 100644 Src/Modules/Infrastructure/JointRequestProvider.cpp create mode 100644 Src/Modules/Infrastructure/JointRequestProvider.h create mode 100644 Src/Modules/Infrastructure/Network/TeamCommLocalSocketProvider.cpp create mode 100644 Src/Modules/Infrastructure/Network/TeamCommLocalSocketProvider.h create mode 100644 Src/Modules/MotionControl/DortmundWalkingEngine/FLIPMStateProvider.cpp create mode 100644 Src/Modules/MotionControl/DortmundWalkingEngine/FLIPMStateProvider.h create mode 100644 Src/Modules/MotionControl/JointErrorCalc.cpp create mode 100644 Src/Modules/MotionControl/JointErrorCalc.h create mode 100644 Src/Modules/MotionControl/KickEngine/DynPoint.h create mode 100644 Src/Modules/Perception/JerseyColorDetector.cpp create mode 100644 Src/Modules/Perception/JerseyColorDetector.h create mode 100644 Src/Modules/Perception/PenaltyCrossClassifier.cpp create mode 100644 Src/Modules/Perception/PenaltyCrossClassifier.h create mode 100644 Src/Modules/Perception/RobotClassifier.cpp create mode 100644 Src/Modules/Perception/RobotClassifier.h create mode 100644 Src/Modules/Perception/RobotOrientationDetector.cpp create mode 100644 Src/Modules/Perception/RobotOrientationDetector.h create mode 100644 Src/Modules/Perception/RobotsPerceptProvider.cpp create mode 100644 Src/Modules/Perception/RobotsPerceptProvider.h create mode 100644 Src/Modules/Perception/SonarDetector.cpp create mode 100644 Src/Modules/Perception/SonarDetector.h create mode 100644 Src/Modules/Sensing/BrokenJointDetector.cpp create mode 100644 Src/Modules/Sensing/BrokenJointDetector.h delete mode 100644 Src/Modules/Sensing/GyroFallDownStateDetector.cpp delete mode 100644 Src/Modules/Sensing/GyroFallDownStateDetector.h create mode 100644 Src/Modules/Sensing/SimpleFallDownStateDetector.cpp create mode 100644 Src/Modules/Sensing/SimpleFallDownStateDetector.h create mode 100644 Src/Representations/BehaviorControl/BallChaserDecision.cpp create mode 100644 Src/Representations/BehaviorControl/RoleSymbols/BackWing.h create mode 100644 Src/Representations/BehaviorControl/RoleSymbols/FrontWing.h create mode 100644 Src/Representations/BehaviorControl/RoleSymbols/LeftWing.h create mode 100644 Src/Representations/BehaviorControl/RoleSymbols/PositioningAndKickSymbols.h rename Src/Representations/BehaviorControl/{ => RoleSymbols}/PositioningSymbols.cpp (100%) rename Src/Representations/BehaviorControl/{ => RoleSymbols}/PositioningSymbols.h (100%) create mode 100644 Src/Representations/BehaviorControl/RoleSymbols/RightWing.h create mode 100644 Src/Representations/Configuration/SonarConfiguration.h delete mode 100644 Src/Representations/Configuration/UsConfiguration.h create mode 100644 Src/Representations/Infrastructure/HealthScore.h create mode 100644 Src/Representations/Infrastructure/SensorData/SonarSensorData.h delete mode 100644 Src/Representations/Infrastructure/SensorData/UsSensorData.h delete mode 100644 Src/Representations/Infrastructure/TeamCommData.cpp delete mode 100644 Src/Representations/Infrastructure/USRequest.h create mode 100644 Src/Representations/Modeling/KickWheel.cpp create mode 100644 Src/Representations/Modeling/KickWheel.h create mode 100644 Src/Representations/Modeling/Kicks.h create mode 100644 Src/Representations/MotionControl/JointError.cpp create mode 100644 Src/Representations/MotionControl/JointError.h create mode 100644 Src/Representations/Perception/ImagePatch.cpp create mode 100644 Src/Representations/Perception/ImagePatch.h create mode 100644 Src/Representations/Perception/PenaltyCrossHypotheses.cpp create mode 100644 Src/Representations/Perception/SonarPercept.cpp create mode 100644 Src/Representations/Perception/SonarPercept.h create mode 100644 Src/Representations/Perception/TfliteInterpreter.cpp create mode 100644 Src/Representations/Sensing/BrokenJointState.h create mode 100644 Src/Tools/ImageCoordinateTransformations.h create mode 100644 Src/Tools/Streams/Compressed.cpp create mode 100644 Src/Tools/Streams/Compressed.h delete mode 100644 Util/GameController/include/SPLCoachMessage.h delete mode 100644 Util/GameController/include/SPLStandardMessage.h diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 00000000..7a2265f9 --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,36 @@ +cff-version: 1.2.0 +title: Nao Devils Code Release +message: If you use this software, please cite it using these metadata. +type: software +authors: + - family-names: B + given-names: Leon + - family-names: Brämer + given-names: Dominik + - family-names: Dasmann + given-names: Lisa + - family-names: Gayda + given-names: Alicia + - family-names: Hatwar + given-names: Mrunal + - family-names: Kleingarn + given-names: Diana + - family-names: Klute + given-names: Thomas + - family-names: Larisch + given-names: Aaron + - family-names: Mohammadi + given-names: Mahdokht + - family-names: Moos + given-names: Arne + - family-names: Pieper + given-names: Steven + - family-names: Schwarz + given-names: Ingmar + - family-names: Voß + given-names: Alexander +repository-code: "https://github.com/NaoDevils/CodeRelease/tree/CodeRelease2023" +license-url: "https://github.com/NaoDevils/CodeRelease/blob/CodeRelease2023/License.txt" +url: "https://naodevils.de" +version: "2023" +date-released: "2023-10-15" diff --git a/Config/KeyFrameEngine/cheering1.kfm b/Config/KeyFrameEngine/cheering1.kfm index 974bfb95..7d703d50 100644 --- a/Config/KeyFrameEngine/cheering1.kfm +++ b/Config/KeyFrameEngine/cheering1.kfm @@ -1,9 +1,11 @@ //Cheering for a scored goal // old -> needs adjustments keyFrameID = cheering1; -YstabilizationP = 0; +initialKeyFrame = true; +nextKeyFrameID = none; +YstabilizationP = -0.05; YstabilizationI = 0; -YstabilizationD = 0; +YstabilizationD = -0.6; XstabilizationP = 0; XstabilizationI = 0; XstabilizationD = 0; @@ -23,12 +25,15 @@ keyFrames = [ duration = 200; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront = 30deg; + angleAtKeyFrameErrorBack = 30deg; useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; + activateFallDownProtection = false; + stabilize = true; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { //frame 2 : Hands up // HeadYaw, HeadPitch @@ -69,10 +74,13 @@ keyFrames = [ // advanced parameters angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = true; + angleAtKeyFrameErrorFront = 30deg; + angleAtKeyFrameErrorBack = 30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; waitForStable = true; - hipYawErrorCompensation = 0; - waitForTorsoTime = 0; + fallDownProtection = none; + holdFallDownProtection = 12; }, { //frame 3 : hands up straight // HeadYaw, HeadPitch @@ -86,8 +94,8 @@ keyFrames = [ // HipYawPitch, HipRoll, HipPitch, KneePitch, AnklePitch, AnkleRoll legsAngles = [ - 0deg, 0deg, -21deg, 49deg, -27.5deg, 0deg, // left leg - 0deg, 0deg, -21deg, 49deg, -27.5deg, 0deg, // right leg + 0deg, 0deg, -31deg, 55deg, -24deg, 0deg, // left leg + 0deg, 0deg, -31deg, 55deg, -24deg, 0deg, // right leg ]; // Amount of force / torque (%) for each joint @@ -100,7 +108,7 @@ keyFrames = [ ]; // key frame duration in milliseconds - duration = 200; + duration = 400; // interpolation type between key frames: sine or linear intType = linear; @@ -113,10 +121,13 @@ keyFrames = [ // advanced parameters angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = true; + angleAtKeyFrameErrorFront = 30deg; + angleAtKeyFrameErrorBack = 30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; waitForStable = true; - hipYawErrorCompensation = 0; - waitForTorsoTime = 0; + fallDownProtection = none; + holdFallDownProtection = 12; }, { //frame 4 : hands down same as frame 2 // HeadYaw, HeadPitch @@ -144,7 +155,7 @@ keyFrames = [ ]; // key frame duration in milliseconds - duration = 200; + duration = 400; // interpolation type between key frames: sine or linear intType = linear ; @@ -157,10 +168,13 @@ keyFrames = [ // advanced parameters angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront = 30deg; + angleAtKeyFrameErrorBack = 30deg; useAngleAtKeyFrameTarget = false; - waitForStable = true; - hipYawErrorCompensation = 0; - waitForTorsoTime = 0; + activateFallDownProtection = false; + waitForStable = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { //frame 5 : hands up straight same as frame3 // HeadYaw, HeadPitch @@ -174,8 +188,8 @@ keyFrames = [ // HipYawPitch, HipRoll, HipPitch, KneePitch, AnklePitch, AnkleRoll legsAngles = [ - 0deg, 0deg, -21deg, 49deg, -27.5deg, 0deg, // left leg - 0deg, 0deg, -21deg, 49deg, -27.5deg, 0deg, // right leg + 0deg, 0deg, -31deg, 55deg, -24deg, 0deg, // left leg + 0deg, 0deg, -31deg, 55deg, -24deg, 0deg, // right leg ]; // Amount of force / torque (%) for each joint @@ -188,7 +202,7 @@ keyFrames = [ ]; // key frame duration in milliseconds - duration = 200; + duration = 400; // interpolation type between key frames: sine or linear intType = linear; @@ -201,10 +215,13 @@ keyFrames = [ // advanced parameters angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = true; + angleAtKeyFrameErrorFront = 30deg; + angleAtKeyFrameErrorBack = 30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; waitForStable = false; - hipYawErrorCompensation = 0; - waitForTorsoTime = 0; + fallDownProtection = none; + holdFallDownProtection = 12; }, { //frame 6 : hands down same as frame 2 // HeadYaw, HeadPitch @@ -233,7 +250,7 @@ keyFrames = [ ]; // key frame duration in milliseconds - duration = 200; + duration = 400; // interpolation type between key frames: sine or linear intType = linear ; @@ -246,10 +263,13 @@ keyFrames = [ // advanced parameters angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = true; + angleAtKeyFrameErrorFront = 30deg; + angleAtKeyFrameErrorBack = 30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; waitForStable = false; - hipYawErrorCompensation = 0; - waitForTorsoTime = 0; + fallDownProtection = none; + holdFallDownProtection = 12; }, { //frame 7 : hands down same as frame 2 // HeadYaw, HeadPitch @@ -291,10 +311,13 @@ keyFrames = [ // advanced parameters angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = true; + angleAtKeyFrameErrorFront = 30deg; + angleAtKeyFrameErrorBack = 30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; waitForStable = false; - hipYawErrorCompensation = 0; - waitForTorsoTime = 0; + fallDownProtection = none; + holdFallDownProtection = 12; }, {//end headAngles = [0deg,0deg]; @@ -304,11 +327,14 @@ keyFrames = [ duration = 200; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront = 30deg; + angleAtKeyFrameErrorBack = 30deg; useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = true; + activateFallDownProtection = false; + stabilize = true; + waitForStable = true; + leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; } ]; diff --git a/Config/KeyFrameEngine/cheering2.kfm b/Config/KeyFrameEngine/cheering2.kfm index e3c63607..74e85f31 100644 --- a/Config/KeyFrameEngine/cheering2.kfm +++ b/Config/KeyFrameEngine/cheering2.kfm @@ -1,5 +1,7 @@ //copy from stand keyFrameID = cheering2; +initialKeyFrame = true; +nextKeyFrameID = none; YstabilizationP = 0.1; YstabilizationI = 0; YstabilizationD = -0.02; @@ -12,17 +14,20 @@ keyFrames = [ //shoulderPitch,shoulderRoll,ElbowYaw, ElbowRoll, WristYaw, Hand armsAngles = [90deg,11deg,0,90deg,-90deg,0, 90deg,-11deg,0,-90deg,90deg,0]; //hipYawPitch, hipRoll, hipPitch, kneePitch, anklePitch, ankleRoll - legsAngles = [-10deg,0, 0, 0, 0,0, -10deg,0, 0, 0,0,0]; - stiffnesses = [40,40, 40,40,40,40,40,40, 40,40,40,40,40,40, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 600; + legsAngles = [0,0,-21deg,49deg,-27.5deg,0, 0,0,-21deg,49deg,-27.5deg,0]; + stiffnesses = [40,40, 60,60,60,60,60,60, 60,60,60,60,60,60, 100,100,100,100,100,100, 100,100,100,100,100,100]; + duration = 1000; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront = 30deg; + angleAtKeyFrameErrorBack = 30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = true; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { //frame2: hands to the right - right leg straight //HeandYaw, HeadPitch @@ -30,17 +35,21 @@ keyFrames = [ //shoulderPitch,shoulderRoll,ElbowYaw, ElbowRoll, WristYaw, Hand armsAngles = [18.8deg,-18.5deg,-13.6deg,-75.4deg,-90deg,0deg, -67.9deg,-62.7deg,-39.3deg,1.1deg,90deg,0deg]; //hipYawPitch, hipRoll, hipPitch, kneePitch, anklePitch, ankleRoll - legsAngles = [-25.4deg,16.2deg,-14.4deg,51.9deg,-28.0deg,-4.9deg, -25.4deg,5.4deg,23.9deg,-6.9deg,0.1deg,5.3deg]; - stiffnesses = [40,40, 40,40,40,40,40,40, 40,40,40,40,40,40, 100,100,100,100,100,100, 100,100,100,100,100,100]; + //legsAngles = [-25.4deg,16.2deg,-14.4deg,51.9deg,-28.0deg,-4.9deg, -25.4deg,5.4deg,23.9deg,-6.9deg,0.1deg,5.3deg]; + legsAngles = [0,0,-21deg,49deg,-27.5deg,0, 0,0,-21deg,49deg,-27.5deg,0]; + stiffnesses = [40,40, 60,60,60,60,60,60, 60,60,60,60,60,60, 80,80,80,80,80,80, 80,80,80,80,80,80]; duration = 800; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront = 30deg; + angleAtKeyFrameErrorBack = 30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = true; - hipYawErrorCompensation = 0; waitForStable = true; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { //frame3: wait in the position //HeandYaw, HeadPitch @@ -48,49 +57,60 @@ keyFrames = [ //shoulderPitch,shoulderRoll,ElbowYaw, ElbowRoll, WristYaw, Hand armsAngles = [18.8deg,-18.5deg,-13.6deg,-75.4deg,-90deg,0deg, -67.9deg,-62.7deg,-39.3deg,1.1deg,90deg,0deg]; //hipYawPitch, hipRoll, hipPitch, kneePitch, anklePitch, ankleRoll - legsAngles = [-25.4deg,16.2deg,-14.4deg,51.9deg,-28.0deg,-4.9deg, -25.4deg,5.4deg,23.9deg,-6.9deg,0.1deg,5.3deg]; - stiffnesses = [40,40, 40,40,40,40,40,40, 40,40,40,40,40,40, 100,100,100,100,100,100, 100,100,100,100,100,100]; + //legsAngles = [-25.4deg,16.2deg,-14.4deg,51.9deg,-28.0deg,-4.9deg, -25.4deg,5.4deg,23.9deg,-6.9deg,0.1deg,5.3deg]; + legsAngles = [0,0,-21deg,49deg,-27.5deg,0, 0,0,-21deg,49deg,-27.5deg,0]; + stiffnesses = [40,40, 60,60,60,60,60,60, 60,60,60,60,60,60, 80,80,80,80,80,80, 80,80,80,80,80,80]; duration = 1500; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront = 30deg; + angleAtKeyFrameErrorBack = 30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = true; - hipYawErrorCompensation = 0; waitForStable = true; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, {//frame4 headAngles = [20000,20000]; //armsAngles = [90deg,11deg,0,90deg,-90deg,0, 90deg,-11deg,0,-90deg,90deg,0]; armsAngles = [18.8deg,-18.5deg,-13.6deg,-75.4deg,-90deg,0deg, -67.9deg,-62.7deg,-39.3deg,1.1deg,90deg,0deg]; //legsAngles = [0,0,-21deg,49deg,-27.5deg,0, 0,0,-21deg,49deg,-27.5deg,0]; - legsAngles = [0.0deg,0.1deg,-21.1deg,49.0deg,-27.7deg,-0.1deg, 0.0deg,-0.3deg,-21.1deg,49.0deg,-27.6deg,0.0deg]; - stiffnesses = [40,40, 40,40,40,40,40,40, 40,40,40,40,40,40, 60,60,60,60,60,60, 60,60,60,60,60,60]; + //legsAngles = [0.0deg,0.1deg,-21.1deg,49.0deg,-27.7deg,-0.1deg, 0.0deg,-0.3deg,-21.1deg,49.0deg,-27.6deg,0.0deg]; + legsAngles = [0,0,-21deg,49deg,-27.5deg,0, 0,0,-21deg,49deg,-27.5deg,0]; + stiffnesses = [40,40, 60,60,60,60,60,60, 60,60,60,60,60,60, 80,80,80,80,80,80, 80,80,80,80,80,80]; duration = 500; - intType = sine; + intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront = 30deg; + angleAtKeyFrameErrorBack = 30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = true; - waitForTorsoTime = 0; leavingPossible = true; + fallDownProtection = none; + holdFallDownProtection = 12; }, {//end headAngles = [20000,20000]; armsAngles = [90deg,11deg,0,90deg,-90deg,0, 90deg,-11deg,0,-90deg,90deg,0]; //legsAngles = [0,0,-21deg,49deg,-27.5deg,0, 0,0,-21deg,49deg,-27.5deg,0]; legsAngles = [0.0deg,0.1deg,-21.1deg,49.0deg,-27.7deg,-0.1deg, 0.0deg,-0.3deg,-21.1deg,49.0deg,-27.6deg,0.0deg]; - stiffnesses = [40,40, 40,40,40,40,40,40, 40,40,40,40,40,40, 60,60,60,60,60,60, 60,60,60,60,60,60]; + stiffnesses = [40,40, 60,60,60,60,60,60, 60,60,60,60,60,60, 60,60,60,60,60,60, 60,60,60,60,60,60]; duration = 500; - intType = sine; + intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront = 30deg; + angleAtKeyFrameErrorBack = 30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = true; - waitForTorsoTime = 0; leavingPossible = true; + fallDownProtection = none; + holdFallDownProtection = 12; } ]; diff --git a/Config/KeyFrameEngine/cheering3.kfm b/Config/KeyFrameEngine/cheering3.kfm index 51313d87..e1668c54 100644 --- a/Config/KeyFrameEngine/cheering3.kfm +++ b/Config/KeyFrameEngine/cheering3.kfm @@ -1,5 +1,8 @@ //copy from stand -keyFrameID = cheering3; YstabilizationP = 0; //0.1 +keyFrameID = cheering3; +initialKeyFrame = true; +nextKeyFrameID = none; +YstabilizationP = 0; //0.1 YstabilizationI = 0.0; //0 YstabilizationD = 0; //-0.02 XstabilizationP = 0; @@ -11,15 +14,18 @@ keyFrames = [ armsAngles = [20000,20000,20000,20000,20000,20000, 20000,20000,20000,20000,20000,20000]; legsAngles = [20000,20000,20000,20000,20000,20000, 20000,20000,20000,20000,20000,20000]; stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 300; + duration = 1000; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront = 30deg; + angleAtKeyFrameErrorBack = 30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { //frame 0 : initializing the hands // HeadYaw, HeadPitch @@ -32,11 +38,11 @@ keyFrames = [ ]; // HipYawPitch, HipRoll, HipPitch, KneePitch, AnklePitch, AnkleRoll - legsAngles = [ + /*legsAngles = [ 0deg, 0deg, -21deg, 49deg, -27.5deg, 0deg, // left leg 0deg, 0deg, -21deg, 49deg, -27.5deg, 0deg, // right leg - ]; - + ];*/ + legsAngles = [0.1deg,0.1deg,-20.9deg,49.0deg,-27.2deg,0.0deg, 0.1deg,-0.1deg,-21.1deg,49.0deg,-27.5deg,0.0deg]; // Amount of force / torque (%) for each joint stiffnesses = [ 40,40, // head @@ -53,17 +59,20 @@ keyFrames = [ intType = linear; // enable PID sensor control during this frame (see stablization parameters above) - stabilize = true; + stabilize = false; // if the key frame motion can be interrupted at this point (set this to true for the last frame) leavingPossible = false; // advanced parameters angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = true; + angleAtKeyFrameErrorFront = 30deg; + angleAtKeyFrameErrorBack = 30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; waitForStable = false; - hipYawErrorCompensation = 0; - waitForTorsoTime = 0; + fallDownProtection = none; + holdFallDownProtection = 12; }, { //frame 1 : reaching the left initial pose (left-down) // HeadYaw, HeadPitch @@ -76,11 +85,11 @@ keyFrames = [ ]; // HipYawPitch, HipRoll, HipPitch, KneePitch, AnklePitch, AnkleRoll - legsAngles = [ + /*legsAngles = [ 0deg, 0deg, -21deg, 49deg, -27.5deg, 0deg, // left leg 0deg, 0deg, -21deg, 49deg, -27.5deg, 0deg, // right leg - ]; - + ];*/ + legsAngles = [0.1deg,0.1deg,-20.9deg,49.0deg,-27.2deg,0.0deg, 0.1deg,-0.1deg,-21.1deg,49.0deg,-27.5deg,0.0deg]; // Amount of force / torque (%) for each joint stiffnesses = [ 40,40, // head @@ -97,17 +106,20 @@ keyFrames = [ intType = linear; // enable PID sensor control during this frame (see stablization parameters above) - stabilize = true; + stabilize = false; // if the key frame motion can be interrupted at this point (set this to true for the last frame) leavingPossible = false; // advanced parameters angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = true; + angleAtKeyFrameErrorFront = 30deg; + angleAtKeyFrameErrorBack = 30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; waitForStable = false; - hipYawErrorCompensation = 0; - waitForTorsoTime = 0; + fallDownProtection = none; + holdFallDownProtection = 12; }, { //frame 2 : shaking the hands in the current position // HeadYaw, HeadPitch @@ -120,11 +132,11 @@ keyFrames = [ ]; // HipYawPitch, HipRoll, HipPitch, KneePitch, AnklePitch, AnkleRoll - legsAngles = [ + /*legsAngles = [ 0deg, 0deg, -30.5deg, 61deg, -30.5deg, 0deg, // left leg 0deg, 0deg, -30.5deg, 61deg, -30.5deg, 0deg, // right leg - ]; - + ];*/ + legsAngles = [0.1deg,0.1deg,-20.9deg,49.0deg,-27.2deg,0.0deg, 0.1deg,-0.1deg,-21.1deg,49.0deg,-27.5deg,0.0deg]; // Amount of force / torque (%) for each joint stiffnesses = [ 40,40, // head @@ -141,17 +153,20 @@ keyFrames = [ intType = linear; // enable PID sensor control during this frame (see stablization parameters above) - stabilize = true; + stabilize = false; // if the key frame motion can be interrupted at this point (set this to true for the last frame) leavingPossible = false; // advanced parameters angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = true; + angleAtKeyFrameErrorFront = 30deg; + angleAtKeyFrameErrorBack = 30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; waitForStable = false; - hipYawErrorCompensation = 0; - waitForTorsoTime = 0; + fallDownProtection = none; + holdFallDownProtection = 12; }, { //frame 3 : shaking the hands in the current position // HeadYaw, HeadPitch @@ -164,11 +179,11 @@ keyFrames = [ ]; // HipYawPitch, HipRoll, HipPitch, KneePitch, AnklePitch, AnkleRoll - legsAngles = [ + /*legsAngles = [ 0deg, 0deg, -21deg, 49deg, -27.5deg, 0deg, // left leg 0deg, 0deg, -21deg, 49deg, -27.5deg, 0deg, // right leg - ]; - + ];*/ + legsAngles = [0.1deg,0.1deg,-20.9deg,49.0deg,-27.2deg,0.0deg, 0.1deg,-0.1deg,-21.1deg,49.0deg,-27.5deg,0.0deg]; // Amount of force / torque (%) for each joint stiffnesses = [ 40,40, // head @@ -185,17 +200,20 @@ keyFrames = [ intType = linear; // enable PID sensor control during this frame (see stablization parameters above) - stabilize = true; + stabilize = false; // if the key frame motion can be interrupted at this point (set this to true for the last frame) leavingPossible = false; // advanced parameters angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = true; + angleAtKeyFrameErrorFront = 30deg; + angleAtKeyFrameErrorBack = 30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; waitForStable = false; - hipYawErrorCompensation = 0; - waitForTorsoTime = 0; + fallDownProtection = none; + holdFallDownProtection = 12; }, { //frame 4 : shaking the hands in the current position //same as frame 2 // HeadYaw, HeadPitch @@ -208,11 +226,11 @@ keyFrames = [ ]; // HipYawPitch, HipRoll, HipPitch, KneePitch, AnklePitch, AnkleRoll - legsAngles = [ + /*legsAngles = [ 0deg, 0deg, -30.5deg, 61deg, -30.5deg, 0deg, // left leg 0deg, 0deg, -30.5deg, 61deg, -30.5deg, 0deg, // right leg - ]; - + ];*/ + legsAngles = [0.1deg,0.1deg,-20.9deg,49.0deg,-27.2deg,0.0deg, 0.1deg,-0.1deg,-21.1deg,49.0deg,-27.5deg,0.0deg]; // Amount of force / torque (%) for each joint stiffnesses = [ 40,40, // head @@ -229,18 +247,22 @@ keyFrames = [ intType = linear; // enable PID sensor control during this frame (see stablization parameters above) - stabilize = true; + stabilize = false; // if the key frame motion can be interrupted at this point (set this to true for the last frame) leavingPossible = false; // advanced parameters angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = true; + angleAtKeyFrameErrorFront = 30deg; + angleAtKeyFrameErrorBack = 30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; waitForStable = false; - hipYawErrorCompensation = 0; - waitForTorsoTime = 0; - },{ //frame 5 : shaking the hands in the current position + fallDownProtection = none; + holdFallDownProtection = 12; + }, + { //frame 5 : shaking the hands in the current position // HeadYaw, HeadPitch headAngles = [0deg,0deg]; // 20000 = unchanged, we do not change the head angles @@ -251,11 +273,11 @@ keyFrames = [ ]; // HipYawPitch, HipRoll, HipPitch, KneePitch, AnklePitch, AnkleRoll - legsAngles = [ + /*legsAngles = [ 0deg, 0deg, -21deg, 49deg, -27.5deg, 0deg, // left leg 0deg, 0deg, -21deg, 49deg, -27.5deg, 0deg, // right leg - ]; - + ];*/ + legsAngles = [0.1deg,0.1deg,-20.9deg,49.0deg,-27.2deg,0.0deg, 0.1deg,-0.1deg,-21.1deg,49.0deg,-27.5deg,0.0deg]; // Amount of force / torque (%) for each joint stiffnesses = [ 40,40, // head @@ -272,17 +294,20 @@ keyFrames = [ intType = linear; // enable PID sensor control during this frame (see stablization parameters above) - stabilize = true; + stabilize = false; // if the key frame motion can be interrupted at this point (set this to true for the last frame) leavingPossible = false; // advanced parameters angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = true; + angleAtKeyFrameErrorFront = 30deg; + angleAtKeyFrameErrorBack = 30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; waitForStable = false; - hipYawErrorCompensation = 0; - waitForTorsoTime = 0; + fallDownProtection = none; + holdFallDownProtection = 12; }, @@ -297,11 +322,11 @@ keyFrames = [ ]; // HipYawPitch, HipRoll, HipPitch, KneePitch, AnklePitch, AnkleRoll - legsAngles = [ + /*legsAngles = [ 0deg, 0deg, -21deg, 49deg, -27.5deg, 0deg, // left leg 0deg, 0deg, -21deg, 49deg, -27.5deg, 0deg, // right leg - ]; - + ];*/ + legsAngles = [0.1deg,0.1deg,-20.9deg,49.0deg,-27.2deg,0.0deg, 0.1deg,-0.1deg,-21.1deg,49.0deg,-27.5deg,0.0deg]; // Amount of force / torque (%) for each joint stiffnesses = [ 40,40, // head @@ -318,17 +343,20 @@ keyFrames = [ intType = linear; // enable PID sensor control during this frame (see stablization parameters above) - stabilize = true; + stabilize = false; // if the key frame motion can be interrupted at this point (set this to true for the last frame) leavingPossible = false; // advanced parameters angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = true; + angleAtKeyFrameErrorFront = 30deg; + angleAtKeyFrameErrorBack = 30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; waitForStable = false; - hipYawErrorCompensation = 0; - waitForTorsoTime = 0; + fallDownProtection = none; + holdFallDownProtection = 12; }, { //frame 2 : shaking the hands in the current position // HeadYaw, HeadPitch @@ -341,11 +369,11 @@ keyFrames = [ ]; // HipYawPitch, HipRoll, HipPitch, KneePitch, AnklePitch, AnkleRoll - legsAngles = [ + /*legsAngles = [ 0deg, 0deg, -30.5deg, 61deg, -30.5deg, 0deg, // left leg 0deg, 0deg, -30.5deg, 61deg, -30.5deg, 0deg, // right leg - ]; - + ];*/ + legsAngles = [0.1deg,0.1deg,-20.9deg,49.0deg,-27.2deg,0.0deg, 0.1deg,-0.1deg,-21.1deg,49.0deg,-27.5deg,0.0deg]; // Amount of force / torque (%) for each joint stiffnesses = [ 40,40, // head @@ -362,17 +390,20 @@ keyFrames = [ intType = linear; // enable PID sensor control during this frame (see stablization parameters above) - stabilize = true; + stabilize = false; // if the key frame motion can be interrupted at this point (set this to true for the last frame) leavingPossible = false; // advanced parameters angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = true; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; waitForStable = false; - hipYawErrorCompensation = 0; - waitForTorsoTime = 0; + fallDownProtection = none; + holdFallDownProtection = 12; }, { //frame 3 : shaking the hands in the current position // HeadYaw, HeadPitch @@ -385,11 +416,11 @@ keyFrames = [ ]; // HipYawPitch, HipRoll, HipPitch, KneePitch, AnklePitch, AnkleRoll - legsAngles = [ + /*legsAngles = [ 0deg, 0deg, -21deg, 49deg, -27.5deg, 0deg, // left leg 0deg, 0deg, -21deg, 49deg, -27.5deg, 0deg, // right leg - ]; - + ];*/ + legsAngles = [0.1deg,0.1deg,-20.9deg,49.0deg,-27.2deg,0.0deg, 0.1deg,-0.1deg,-21.1deg,49.0deg,-27.5deg,0.0deg]; // Amount of force / torque (%) for each joint stiffnesses = [ 40,40, // head @@ -406,17 +437,20 @@ keyFrames = [ intType = linear; // enable PID sensor control during this frame (see stablization parameters above) - stabilize = true; + stabilize = false; // if the key frame motion can be interrupted at this point (set this to true for the last frame) leavingPossible = false; // advanced parameters angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = true; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; waitForStable = false; - hipYawErrorCompensation = 0; - waitForTorsoTime = 0; + fallDownProtection = none; + holdFallDownProtection = 12; }, { //frame 4 : shaking the hands in the current position //same as frame 2 // HeadYaw, HeadPitch @@ -430,11 +464,11 @@ keyFrames = [ ]; // HipYawPitch, HipRoll, HipPitch, KneePitch, AnklePitch, AnkleRoll - legsAngles = [ + /*legsAngles = [ 0deg, 0deg, -30.5deg, 61deg, -30.5deg, 0deg, // left leg 0deg, 0deg, -30.5deg, 61deg, -30.5deg, 0deg, // right leg - ]; - + ];*/ + legsAngles = [0.1deg,0.1deg,-20.9deg,49.0deg,-27.2deg,0.0deg, 0.1deg,-0.1deg,-21.1deg,49.0deg,-27.5deg,0.0deg]; // Amount of force / torque (%) for each joint stiffnesses = [ 40,40, // head @@ -451,17 +485,20 @@ keyFrames = [ intType = linear; // enable PID sensor control during this frame (see stablization parameters above) - stabilize = true; + stabilize = false; // if the key frame motion can be interrupted at this point (set this to true for the last frame) leavingPossible = false; // advanced parameters angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = true; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; waitForStable = false; - hipYawErrorCompensation = 0; - waitForTorsoTime = 0; + fallDownProtection = none; + holdFallDownProtection = 12; }, { //frame 5 : shaking the hands in the current position // HeadYaw, HeadPitch @@ -474,11 +511,11 @@ keyFrames = [ ]; // HipYawPitch, HipRoll, HipPitch, KneePitch, AnklePitch, AnkleRoll - legsAngles = [ + /*legsAngles = [ 0deg, 0deg, -21deg, 49deg, -27.5deg, 0deg, // left leg 0deg, 0deg, -21deg, 49deg, -27.5deg, 0deg, // right leg - ]; - + ];*/ + legsAngles = [0.1deg,0.1deg,-20.9deg,49.0deg,-27.2deg,0.0deg, 0.1deg,-0.1deg,-21.1deg,49.0deg,-27.5deg,0.0deg]; // Amount of force / torque (%) for each joint stiffnesses = [ 40,40, // head @@ -495,17 +532,20 @@ keyFrames = [ intType = linear; // enable PID sensor control during this frame (see stablization parameters above) - stabilize = true; + stabilize = false; // if the key frame motion can be interrupted at this point (set this to true for the last frame) leavingPossible = false; // advanced parameters angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = true; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; waitForStable = false; - hipYawErrorCompensation = 0; - waitForTorsoTime = 0; + fallDownProtection = none; + holdFallDownProtection = 12; }, //round 2 @@ -520,11 +560,11 @@ keyFrames = [ ]; // HipYawPitch, HipRoll, HipPitch, KneePitch, AnklePitch, AnkleRoll - legsAngles = [ + /*legsAngles = [ 0deg, 0deg, -21deg, 49deg, -27.5deg, 0deg, // left leg 0deg, 0deg, -21deg, 49deg, -27.5deg, 0deg, // right leg - ]; - + ];*/ + legsAngles = [0.1deg,0.1deg,-20.9deg,49.0deg,-27.2deg,0.0deg, 0.1deg,-0.1deg,-21.1deg,49.0deg,-27.5deg,0.0deg]; // Amount of force / torque (%) for each joint stiffnesses = [ 40,40, // head @@ -541,17 +581,20 @@ keyFrames = [ intType = linear; // enable PID sensor control during this frame (see stablization parameters above) - stabilize = true; + stabilize = false; // if the key frame motion can be interrupted at this point (set this to true for the last frame) leavingPossible = false; // advanced parameters angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = true; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; waitForStable = false; - hipYawErrorCompensation = 0; - waitForTorsoTime = 0; + fallDownProtection = none; + holdFallDownProtection = 12; }, { //frame 2 : shaking the hands in the current position // HeadYaw, HeadPitch @@ -564,11 +607,11 @@ keyFrames = [ ]; // HipYawPitch, HipRoll, HipPitch, KneePitch, AnklePitch, AnkleRoll - legsAngles = [ + /*legsAngles = [ 0deg, 0deg, -30.5deg, 61deg, -30.5deg, 0deg, // left leg 0deg, 0deg, -30.5deg, 61deg, -30.5deg, 0deg, // right leg - ]; - + ];*/ + legsAngles = [0.1deg,0.1deg,-20.9deg,49.0deg,-27.2deg,0.0deg, 0.1deg,-0.1deg,-21.1deg,49.0deg,-27.5deg,0.0deg]; // Amount of force / torque (%) for each joint stiffnesses = [ 40,40, // head @@ -585,17 +628,20 @@ keyFrames = [ intType = linear; // enable PID sensor control during this frame (see stablization parameters above) - stabilize = true; + stabilize = false; // if the key frame motion can be interrupted at this point (set this to true for the last frame) leavingPossible = false; // advanced parameters angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = true; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; waitForStable = false; - hipYawErrorCompensation = 0; - waitForTorsoTime = 0; + fallDownProtection = none; + holdFallDownProtection = 12; }, { //frame 3 : shaking the hands in the current position // HeadYaw, HeadPitch @@ -608,11 +654,11 @@ keyFrames = [ ]; // HipYawPitch, HipRoll, HipPitch, KneePitch, AnklePitch, AnkleRoll - legsAngles = [ + /*legsAngles = [ 0deg, 0deg, -21deg, 49deg, -27.5deg, 0deg, // left leg 0deg, 0deg, -21deg, 49deg, -27.5deg, 0deg, // right leg - ]; - + ];*/ + legsAngles = [0.1deg,0.1deg,-20.9deg,49.0deg,-27.2deg,0.0deg, 0.1deg,-0.1deg,-21.1deg,49.0deg,-27.5deg,0.0deg]; // Amount of force / torque (%) for each joint stiffnesses = [ 40,40, // head @@ -629,17 +675,20 @@ keyFrames = [ intType = linear; // enable PID sensor control during this frame (see stablization parameters above) - stabilize = true; + stabilize = false; // if the key frame motion can be interrupted at this point (set this to true for the last frame) leavingPossible = false; // advanced parameters angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = true; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; waitForStable = false; - hipYawErrorCompensation = 0; - waitForTorsoTime = 0; + fallDownProtection = none; + holdFallDownProtection = 12; }, { //frame 4 : shaking the hands in the current position //same as frame 2 // HeadYaw, HeadPitch @@ -652,11 +701,11 @@ keyFrames = [ ]; // HipYawPitch, HipRoll, HipPitch, KneePitch, AnklePitch, AnkleRoll - legsAngles = [ + /*legsAngles = [ 0deg, 0deg, -30.5deg, 61deg, -30.5deg, 0deg, // left leg 0deg, 0deg, -30.5deg, 61deg, -30.5deg, 0deg, // right leg - ]; - + ];*/ + legsAngles = [0.1deg,0.1deg,-20.9deg,49.0deg,-27.2deg,0.0deg, 0.1deg,-0.1deg,-21.1deg,49.0deg,-27.5deg,0.0deg]; // Amount of force / torque (%) for each joint stiffnesses = [ 40,40, // head @@ -673,17 +722,20 @@ keyFrames = [ intType = linear; // enable PID sensor control during this frame (see stablization parameters above) - stabilize = true; + stabilize = false; // if the key frame motion can be interrupted at this point (set this to true for the last frame) leavingPossible = false; // advanced parameters angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = true; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; waitForStable = false; - hipYawErrorCompensation = 0; - waitForTorsoTime = 0; + fallDownProtection = none; + holdFallDownProtection = 12; }, { //frame 5 : shaking the hands in the current position // HeadYaw, HeadPitch @@ -696,11 +748,11 @@ keyFrames = [ ]; // HipYawPitch, HipRoll, HipPitch, KneePitch, AnklePitch, AnkleRoll - legsAngles = [ + /*legsAngles = [ 0deg, 0deg, -21deg, 49deg, -27.5deg, 0deg, // left leg 0deg, 0deg, -21deg, 49deg, -27.5deg, 0deg, // right leg - ]; - + ];*/ + legsAngles = [0.1deg,0.1deg,-20.9deg,49.0deg,-27.2deg,0.0deg, 0.1deg,-0.1deg,-21.1deg,49.0deg,-27.5deg,0.0deg]; // Amount of force / torque (%) for each joint stiffnesses = [ 40,40, // head @@ -720,14 +772,17 @@ keyFrames = [ stabilize = false; // if the key frame motion can be interrupted at this point (set this to true for the last frame) - leavingPossible = true; + leavingPossible = false; // advanced parameters angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = true; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; waitForStable = false; - hipYawErrorCompensation = 0; - waitForTorsoTime = 0; + fallDownProtection = none; + holdFallDownProtection = 12; }, // the last key frame starts standing { @@ -769,9 +824,12 @@ keyFrames = [ // advanced parameters angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; waitForStable = false; - hipYawErrorCompensation = 0; - waitForTorsoTime = 0; + fallDownProtection = none; + holdFallDownProtection = 12; } ]; diff --git a/Config/KeyFrameEngine/cheering4.kfm b/Config/KeyFrameEngine/cheering4.kfm index a939faf4..35c9c57e 100644 --- a/Config/KeyFrameEngine/cheering4.kfm +++ b/Config/KeyFrameEngine/cheering4.kfm @@ -1,55 +1,192 @@ -//copy from stand +//Cheering for a scored goal keyFrameID = cheering4; -YstabilizationP = 0.1; +initialKeyFrame = true; +nextKeyFrameID = none; +YstabilizationP = 0; YstabilizationI = 0; -YstabilizationD = -0.02; +YstabilizationD = 0; XstabilizationP = 0; XstabilizationI = 0; XstabilizationD = 0; keyFrames = [ - { - headAngles = [20000,20000]; - armsAngles = [20000,20000,20000,20000,20000,20000, 20000,20000,20000,20000,20000,20000]; - legsAngles = [20000,20000,20000,20000,20000,20000, 20000,20000,20000,20000,20000,20000]; - stiffnesses = [40,40, 40,40,40,40,40,40, 40,40,40,40,40,40, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 400; + {//line 1 + headAngles = [0,0]; + armsAngles = [90deg,10deg,0,0,-90deg,0, 90deg,-30deg,0,0,90deg,0]; + legsAngles = [0,0,-13deg,35deg,-22deg,0, 0,0,-13deg,35deg,-22deg,0]; + stiffnesses = [30,30, 40,40,40,40,40,40, 40,40,40,40,40,40, 60,60,60,60,60,60, 60,60,60,60,60,60]; + duration = 1000; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, - { - headAngles = [20000,20000]; - armsAngles = [90deg,11deg,0,90deg,-90deg,0, 90deg,-11deg,0,-90deg,90deg,0]; - legsAngles = [0,0,-21deg,49deg,-27.5deg,0, 0,0,-21deg,49deg,-27.5deg,0]; - stiffnesses = [40,40, 40,40,40,40,40,40, 40,40,40,40,40,40, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 1000; + {//in pose + headAngles = [0,0]; + armsAngles = [90deg,10deg,0,0,-90deg,0, 81deg,-10deg,95deg,80deg,90deg,0]; + legsAngles = [0deg,0deg,-30deg,70.0deg,-40deg,0deg, 0deg,0deg,-30deg,70deg,-40deg,0deg]; + stiffnesses = [30,30, 40,40,40,40,40,40, 40,40,40,40,40,40, 60,60,60,60,60,60, 60,60,60,60,60,60]; + duration = 500; + intType = linear; + angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; + stabilize = false; + waitForStable = false; + leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; + },// move arms + {//line 3 + headAngles = [0,0]; + armsAngles = [90deg,10deg,0,0,-90deg,0, 35deg,0,105deg,35deg,90deg,0]; + legsAngles = [0deg,0deg,-30deg,70.0deg,-40deg,0deg, 0deg,0deg,-30deg,70deg,-40deg,0deg]; + stiffnesses = [30,30, 40,40,40,40,40,40, 40,40,40,40,40,40, 60,60,60,60,60,60, 60,60,60,60,60,60]; + duration = 400; + intType = sine; + angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; + stabilize = false; + waitForStable = false; + leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; + }, + {//in pose 2 + headAngles = [0,0]; + armsAngles = [90deg,10deg,0,0,-90deg,0, 81deg,-10deg,95deg,80deg,90deg,0]; + legsAngles = [0deg,0deg,-30deg,70.0deg,-40deg,0deg, 0deg,0deg,-30deg,70deg,-40deg,0deg]; + stiffnesses = [30,30, 40,40,40,40,40,40, 40,40,40,40,40,40, 60,60,60,60,60,60, 60,60,60,60,60,60]; + duration = 400; + intType = sine; + angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; + stabilize = false; + waitForStable = false; + leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; + }, + {//line 3.2 + headAngles = [0,0]; + armsAngles = [90deg,10deg,0,0,-90deg,0, 35deg,0,105deg,35deg,90deg,0]; + legsAngles = [0deg,0deg,-30deg,70.0deg,-40deg,0deg, 0deg,0deg,-30deg,70deg,-40deg,0deg]; + stiffnesses = [30,30, 40,40,40,40,40,40, 40,40,40,40,40,40, 60,60,60,60,60,60, 60,60,60,60,60,60]; + duration = 400; + intType = sine; + angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; + stabilize = false; + waitForStable = false; + leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; + }, + {//in pose 3 + headAngles = [0,0]; + armsAngles = [90deg,10deg,0,0,-90deg,0, 81deg,-10deg,95deg,80deg,90deg,0]; + legsAngles = [0deg,0deg,-30deg,70.0deg,-40deg,0deg, 0deg,0deg,-30deg,70deg,-40deg,0deg]; + stiffnesses = [30,30, 40,40,40,40,40,40, 40,40,40,40,40,40, 60,60,60,60,60,60, 60,60,60,60,60,60]; + duration = 400; intType = sine; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; + stabilize = false; + waitForStable = false; + leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; + }, + {//line 3.3 + headAngles = [0,0]; + armsAngles = [90deg,10deg,0,0,-90deg,0, 35deg,0,105deg,35deg,90deg,0]; + legsAngles = [0deg,0deg,-30deg,70.0deg,-40deg,0deg, 0deg,0deg,-30deg,70deg,-40deg,0deg]; + stiffnesses = [30,30, 40,40,40,40,40,40, 40,40,40,40,40,40, 60,60,60,60,60,60, 60,60,60,60,60,60]; + duration = 500; + intType = linear; + angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; + stabilize = false; + waitForStable = false; + leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; + }, + {//in pose 4 + headAngles = [0,0]; + armsAngles = [90deg,10deg,0,0,-90deg,0, 81deg,-10deg,95deg,80deg,90deg,0]; + legsAngles = [0deg,0deg,-30deg,70.0deg,-40deg,0deg, 0deg,0deg,-30deg,70deg,-40deg,0deg]; + stiffnesses = [30,30, 40,40,40,40,40,40, 40,40,40,40,40,40, 60,60,60,60,60,60, 60,60,60,60,60,60]; + duration = 400; + intType = linear; + angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { - headAngles = [20000,20000]; + headAngles = [0,0]; + armsAngles = [90deg,10deg,0,0,-90deg,0, 90deg,-30deg,0,0,90deg,0]; + legsAngles = [0,0,-13deg,35deg,-22deg,0, 0,0,-13deg,35deg,-22deg,0]; + stiffnesses = [30,30, 40,40,40,40,40,40, 40,40,40,40,40,40, 60,60,60,60,60,60, 60,60,60,60,60,60]; + duration = 500; + intType = linear; + angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; + stabilize = false; + waitForStable = false; + leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; + }, + {//end + headAngles = [0,0]; armsAngles = [90deg,11deg,0,90deg,-90deg,0, 90deg,-11deg,0,-90deg,90deg,0]; legsAngles = [0,0,-21deg,49deg,-27.5deg,0, 0,0,-21deg,49deg,-27.5deg,0]; stiffnesses = [40,40, 40,40,40,40,40,40, 40,40,40,40,40,40, 60,60,60,60,60,60, 60,60,60,60,60,60]; - duration = 100; - intType = sine; + duration = 1000; + intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = true; + leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; } ]; diff --git a/Config/KeyFrameEngine/cheering5.kfm b/Config/KeyFrameEngine/cheering5.kfm index 0d04ccc2..acecf9ac 100644 --- a/Config/KeyFrameEngine/cheering5.kfm +++ b/Config/KeyFrameEngine/cheering5.kfm @@ -1,5 +1,7 @@ //copy from stand keyFrameID = cheering5; +initialKeyFrame = true; +nextKeyFrameID = none; YstabilizationP = 0.1; YstabilizationI = 0; YstabilizationD = -0.02; @@ -15,12 +17,15 @@ keyFrames = [ duration = 400; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { headAngles = [20000,20000]; @@ -30,12 +35,15 @@ keyFrames = [ duration = 1000; intType = sine; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { headAngles = [20000,20000]; @@ -45,11 +53,14 @@ keyFrames = [ duration = 100; intType = sine; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = true; + fallDownProtection = none; + holdFallDownProtection = 12; } ]; diff --git a/Config/KeyFrameEngine/goalkeeperDefendLeft.kfm b/Config/KeyFrameEngine/goalkeeperDefendLeft.kfm index b30dde04..c1f728e6 100644 --- a/Config/KeyFrameEngine/goalkeeperDefendLeft.kfm +++ b/Config/KeyFrameEngine/goalkeeperDefendLeft.kfm @@ -1,4 +1,6 @@ keyFrameID = goalkeeperDefendLeft; +initialKeyFrame = true; +nextKeyFrameID = none; YstabilizationP = 0.1; YstabilizationI = 0; YstabilizationD = -0.02; @@ -15,171 +17,236 @@ keyFrames = [ duration = 40; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, //2.moving the right hand up to avoid singularity for the next position { headAngles = [-0.5deg,20.3deg]; - armsAngles = [23.8deg,76.9deg,-24.8deg,-0.9deg,-90deg,0deg, 41.8deg,-75.5deg,31.7deg,5.5deg,90deg,0deg]; + armsAngles = [23.8deg,76.9deg,-24.8deg,-0.9deg,-90deg,0deg, 90deg,-20deg,0,0deg,90deg,0]; legsAngles = [0deg,0.0deg,-21.3deg,48.9deg,-27.6deg,0.1deg, 0deg,0.0deg,-21.2deg,49.0deg,-27.6deg,0.0deg]; stiffnesses = [40,40, 70,70,70,70,70,70, 30,30,30,30,30,30, 100,100,100,100,100,100, 100,100,100,100,100,100]; duration = 200; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, //3.falling - arm pose1 { headAngles = [-0.5deg,20.3deg]; //ShoulderPitch, ShoulderRoll, ElbowYaw, ElbowRoll, WristYaw, Hand - armsAngles = [23.8deg,76.9deg,-24.8deg,-0.9deg,-90deg,0deg, 31.8deg,8.9deg,-1.1deg,68.2deg,90deg,0deg]; + //armsAngles = [23.8deg,76.9deg,-24.8deg,-0.9deg,-90deg,0deg, 31.8deg,8.9deg,-1.1deg,68.2deg,90deg,0deg]; + armsAngles = [23.8deg,76.9deg,-24.8deg,-0.9deg,-90deg,0deg, 90deg,-20deg,0,0deg,90deg,0]; //pose1 // HipYawPitch, HipRoll, HipPitch, KneePitch, AnklePitch, AnkleRoll - legsAngles = [0deg,0deg,-35deg,100deg,-65deg,0deg, 0deg,0.0deg,-25deg,50deg,-25deg,0.0deg]; - stiffnesses = [20,20, 70,70,70,70,70,70, 30,30,30,30,30,30, 100,100,100,100,100,100, 100,100,100,100,100,100]; + legsAngles = [0deg,0deg,-45deg,100deg,-65deg,0deg, 0deg,0.0deg,-25deg,50deg,-25deg,0.0deg]; + stiffnesses = [20,20, 70,70,70,70,70,70, 30,30,30,30,30,30, 100,100,100,100,100,100, 100,100,100,100,100,100]; duration = 200; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, //4.hold on fall - arm pose1 { headAngles = [-0.5deg,20.3deg]; //ShoulderPitch, ShoulderRoll, ElbowYaw, ElbowRoll, WristYaw, Hand - armsAngles = [23.8deg,76.9deg,-24.8deg,-0.9deg,-90deg,0deg, 31.8deg,8.9deg,-1.1deg,68.2deg,90deg,0deg]; + armsAngles = [23.8deg,76.9deg,-24.8deg,-0.9deg,-90deg,0deg, 90deg,-20deg,0,0deg,90deg,0]; // HipYawPitch, HipRoll, HipPitch, KneePitch, AnklePitch, AnkleRoll - legsAngles = [0deg,0deg,-35deg,100deg,-65deg,0deg, 0deg,0.0deg,-25deg,50deg,-25deg,0.0deg]; + legsAngles = [0deg,0deg,-45deg,100deg,-65deg,0deg, 0deg,0.0deg,-25deg,50deg,-25deg,0.0deg]; stiffnesses = [40,40, 70,70,70,70,70,70, 30,30,30,30,30,30, 50,50,50,50,50,50, 50,50,50,50,50,50]; duration = 150; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, //5.hold on fall - arm pose2 { headAngles = [-0.5deg,20.3deg]; //ShoulderPitch, ShoulderRoll, ElbowYaw, ElbowRoll, WristYaw, Hand - armsAngles = [-38.9deg,71.0deg,-24.7deg,-0.7deg,-90deg,0deg, 31.8deg,8.9deg,-1.1deg,68.2deg,90deg,0deg]; //pose2 + armsAngles = [-38.9deg,71.0deg,-24.7deg,-0.7deg,-90deg,0deg, 90deg,-20deg,0,0deg,90deg,0]; //pose2 // HipYawPitch, HipRoll, HipPitch, KneePitch, AnklePitch, AnkleRoll - legsAngles = [0deg,0deg,-35deg,100deg,-65deg,0deg, 0deg,0.0deg,-25deg,50deg,-25deg,0.0deg]; //Leipzig + legsAngles = [0deg,0deg,-45deg,100deg,-65deg,0deg, 0deg,0.0deg,-25deg,50deg,-25deg,0.0deg]; //Leipzig stiffnesses = [40,40, 70,70,70,70,70,70, 30,30,30,30,30,30, 50,50,50,50,50,50, 50,50,50,50,50,50]; - duration = 200; + duration = 180; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, //6.hold on fall - arm pose3 { headAngles = [-0.5deg,20.3deg]; //ShoulderPitch, ShoulderRoll, ElbowYaw, ElbowRoll, WristYaw, Hand - armsAngles = [-88.9deg,48.9deg,-24.8deg,-10.8deg,-90deg,0deg, 31.8deg,8.9deg,-1.1deg,68.2deg,90deg,0deg]; //pose3 + armsAngles = [-88.9deg,48.9deg,-24.8deg,-10.8deg,-90deg,0deg, 90deg,-20deg,0,0deg,90deg,0]; //pose3 // HipYawPitch, HipRoll, HipPitch, KneePitch, AnklePitch, AnkleRoll - legsAngles = [0deg,0deg,-35deg,100deg,-65deg,0deg, 0deg,0.0deg,-25deg,50deg,-25deg,0.0deg]; //Leipzig - stiffnesses = [40,40, 70,70,70,70,70,70, 30,30,30,30,30,30, 50,50,50,50,50,50, 50,50,50,50,50,50]; + legsAngles = [0deg,0deg,-45deg,100deg,-65deg,0deg, 0deg,0.0deg,-10deg,50deg,-25deg,0.0deg]; //Leipzig + stiffnesses = [40,40, 70,0,70,70,70,70, 30,30,30,30,30,30, 50,50,50,50,50,50, 50,50,50,50,50,50]; duration = 100; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, //7.hold on fall - arm pose4 { headAngles = [-0.5deg,20.3deg]; //ShoulderPitch, ShoulderRoll, ElbowYaw, ElbowRoll, WristYaw, Hand - armsAngles = [-90.0deg,14.0deg,9.9deg,-0.9deg,-90deg,0deg, 31.8deg,8.9deg,-1.1deg,68.2deg,90deg,0deg]; //pose4 + armsAngles = [-90.0deg,14.0deg,9.9deg,-0.9deg,-90deg,0deg, 90deg,-20deg,0,0deg,90deg,0]; //pose4 // HipYawPitch, HipRoll, HipPitch, KneePitch, AnklePitch, AnkleRoll - legsAngles = [0deg,0deg,-35deg,100deg,-65deg,0deg, 0deg,0.0deg,-25deg,50deg,-25deg,0.0deg]; //Leipzig - stiffnesses = [40,40, 70,70,70,70,70,70, 30,30,30,30,30,30, 50,50,50,50,50,50, 50,50,50,50,50,50]; + legsAngles = [0deg,0deg,-45deg,100deg,-65deg,0deg, 0deg,0.0deg,10deg,50deg,-25deg,0.0deg]; + stiffnesses = [40,40, 10,10,30,30,70,70, 30,30,30,30,30,30, 50,50,0,50,50,50, 50,50,50,50,50,50]; duration = 100; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, - //8.roll on back for stand up { headAngles = [-0.5deg,20.3deg]; //ShoulderPitch, ShoulderRoll, ElbowYaw, ElbowRoll, WristYaw, Hand - armsAngles = [-90.0deg,14.0deg,9.9deg,-0.9deg,-90deg,0deg, 31.8deg,8.9deg,-1.1deg,68.2deg,90deg,0deg]; //pose4 + //armsAngles = [-90.0deg,14.0deg,9.9deg,-0.9deg,-90deg,0deg, 31.8deg,8.9deg,-1.1deg,68.2deg,90deg,0deg]; //pose4 + armsAngles = [-90.0deg,14.0deg,9.9deg,-0.9deg,-90deg,0deg, 90deg,-20deg,0,0deg,90deg,0]; // HipYawPitch, HipRoll, HipPitch, KneePitch, AnklePitch, AnkleRoll - legsAngles = [0deg,-20deg,-88.0deg,120.0deg,-65.0deg,-22deg, 0deg,-30deg,10deg,0deg,50deg,22deg]; - stiffnesses = [40,40, 70,70,70,70,70,70, 30,30,30,30,30,30, 70,70,70,70,70,70, 70,70,70,70,70,70]; - duration = 500; - intType = linear; + legsAngles = [0deg,0deg,-35deg,100deg,-65deg,0deg, 0deg,0.0deg,10deg,50deg,-25deg,0.0deg]; + stiffnesses = [40,40, 10,10,30,30,70,70, 30,30,30,30,30,30, 50,50,0,50,50,50, 50,50,0,50,50,50]; + duration = 200; + intType = sine; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, - //9.roll on back for stand up + //8 roll right leg { headAngles = [-0.5deg,20.3deg]; //ShoulderPitch, ShoulderRoll, ElbowYaw, ElbowRoll, WristYaw, Hand - armsAngles = [-90deg,0deg,0deg,0deg,-90.0deg,0deg,90.5deg,-9.8deg,0deg,-90deg,90.0deg,0deg]; + //armsAngles = [-90.0deg,14.0deg,9.9deg,-0.9deg,-90deg,0deg, 90deg,-11deg,0deg,0deg,90deg,0deg]; //pose4 + armsAngles = [-31.2deg,48.6deg,19.0deg,-3.3deg,-90deg,0deg, 90deg,-20deg,0,0deg,90deg,0]; + //-31.2deg,48.6deg,19.0deg,-3.3deg,-90deg,0deg, // HipYawPitch, HipRoll, HipPitch, KneePitch, AnklePitch, AnkleRoll - legsAngles = [0deg,0.0deg,0deg,0deg,0deg,0.1deg, 0deg,0deg,-0deg,0deg,0deg,0.0deg]; - stiffnesses = [20,20, 70,70,70,70,70,70, 30,30,30,30,30,30, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 500; + //legsAngles = [0deg,-20deg,-88.0deg,120.0deg,-65.0deg,-22deg, 0deg,-30deg,30deg,0deg,50deg,22deg]; + //legsAngles = [0deg,0deg,-35deg,100deg,-65deg,0deg, 0deg,0.0deg,30deg,50deg,-25deg,0.0deg]; //left,right + //legsAngles = [0deg,0deg,-35deg,100deg,-65deg,0deg, 0deg,-10deg,30deg,0deg,50deg,10deg]; + //legsAngles = [0deg,0deg,-35deg,100deg,-65deg,0deg,-2.9deg,-19.1deg,25deg,40.6deg,1.7deg,22.6deg]; //keep this version + //legsAngles = [0deg,0.0deg,0deg,0deg,0deg,0.1deg, 0deg,0deg,-0deg,0deg,0deg,0.0deg]; //BH + legsAngles = [0deg,0deg,-35deg,100deg,-65deg,0deg, 0deg,0.0deg,10deg,50deg,-25deg,0.0deg]; + stiffnesses = [40,40, 100,100,100,100,100,100, 30,30,30,30,30,30, 70,70,0,70,70,70, 100,100,0,100,100,100]; + duration = 600; + intType = linear; + angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; + stabilize = false; + waitForStable = false; + leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; + }, //sweep + { + headAngles = [-0.5deg,20.3deg]; + //ShoulderPitch, ShoulderRoll, ElbowYaw, ElbowRoll, WristYaw, Hand + //armsAngles = [90deg,20deg,0deg,0deg,-90deg,0deg, 90deg,-11deg,0,0deg,90deg,0]; + armsAngles = [90.0deg,44.0deg,8.9deg,-2.9deg,-90deg, 0deg, 90deg,-11deg,0,0deg,90deg,0]; + // HipYawPitch, HipRoll, HipPitch, KneePitch, AnklePitch, AnkleRoll + //legsAngles = [0deg,-20deg,-88.0deg,120.0deg,-65.0deg,-22deg, 0deg,-30deg,30deg,0deg,50deg,22deg]; //** + //legsAngles = [0deg,0deg,-35deg,100deg,-65deg,0deg, 0deg,0.0deg,30deg,50deg,-25deg,0.0deg]; //left,right + //legsAngles = [0deg,0deg,-35deg,100deg,-65deg,0deg, 0deg,-20deg,30deg,0deg,50deg,22deg]; + //legsAngles = [0deg,0deg,-35deg,100deg,-65deg,0deg, -2.9deg,-19.1deg,15deg,40.6deg,1.7deg,22.6deg]; //keep this version + //legsAngles = [0deg,0.0deg,0deg,0deg,0deg,0.1deg, 0deg,0deg,-0deg,0deg,0deg,0.0deg]; //BH + legsAngles = [0,0,-21deg,49deg,-27.5deg,0, 0,0,-21deg,49deg,-27.5deg,0]; + stiffnesses = [40,40, 100,100,100,100,100,100, 30,30,30,30,30,30, 70,70,0,70,70,70, 100,100,100,100,100,100]; + duration = 400; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, - //10.sort arms for stand up + //9 stand { headAngles = [-0.5deg,20.3deg]; //ShoulderPitch, ShoulderRoll, ElbowYaw, ElbowRoll, WristYaw, Hand - armsAngles = [90.5deg,9.8deg,0deg,90deg,-90.0deg,0deg, 90.5deg,-9.8deg,0deg,-90deg,90.0deg,0deg]; + armsAngles = [90deg,11deg,0,0deg,-90deg,0, 90deg,-11deg,0,0deg,90deg,0]; // HipYawPitch, HipRoll, HipPitch, KneePitch, AnklePitch, AnkleRoll - legsAngles = [0deg,0.0deg,0deg,0deg,0deg,0.1deg, 0deg,0deg,-0deg,0deg,0deg,0.0deg]; - stiffnesses = [20,20, 70,70,70,70,70,70, 70,70,70,70,70,70, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 500; + legsAngles = [0,0,-21deg,49deg,-27.5deg,0, 0,0,-21deg,49deg,-27.5deg,0]; + legsAngles = [0,0,-21deg,49deg,-27.5deg,0, 0,0,-21deg,49deg,-27.5deg,0]; + stiffnesses = [20,20, 100,100,100,100,100,100, 30,30,30,30,30,30, 70,70,70,70,70,70, 100,100,100,100,100,100]; + duration = 300; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; } ]; diff --git a/Config/KeyFrameEngine/kickLeftFast.kfm b/Config/KeyFrameEngine/kickLeftFast.kfm index c3c5ef5a..fbc090e2 100644 --- a/Config/KeyFrameEngine/kickLeftFast.kfm +++ b/Config/KeyFrameEngine/kickLeftFast.kfm @@ -1,5 +1,7 @@ // kick with left foot keyFrameID = kickLeftFast; +initialKeyFrame = true; +nextKeyFrameID = none; YstabilizationP = 0; YstabilizationI = 0; YstabilizationD = 0; @@ -15,12 +17,15 @@ keyFrames = [ duration = 200; intType = sine; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = true; + activateFallDownProtection = false; stabilize = true; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = true; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // lean to side headAngles = [20000,20000]; @@ -30,12 +35,15 @@ keyFrames = [ duration = 700; intType = sine; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = true; + activateFallDownProtection = false; stabilize = true; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = true; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // lift foot headAngles = [20000,20000]; @@ -45,12 +53,15 @@ keyFrames = [ duration = 100; intType = sine; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = true; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // prepare kick headAngles = [20000,20000]; @@ -60,12 +71,15 @@ keyFrames = [ duration = 300; intType = sine; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = true; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // kick part 1 headAngles = [20000,20000]; @@ -75,12 +89,15 @@ keyFrames = [ duration = 60; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = true; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // kick part 2 headAngles = [20000,20000]; @@ -90,12 +107,15 @@ keyFrames = [ duration = 48; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = true; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // kick part 3 headAngles = [20000,20000]; @@ -105,12 +125,17 @@ keyFrames = [ duration = 36; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = true; hipYawErrorCompensation = 0; waitForStable = false; waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // kick part 4 headAngles = [20000,20000]; @@ -120,12 +145,15 @@ keyFrames = [ duration = 60; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = true; - hipYawErrorCompensation = 0; waitForStable = true; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // back to lift foot headAngles = [20000,20000]; @@ -135,12 +163,15 @@ keyFrames = [ duration = 500; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = true; - hipYawErrorCompensation = 0; waitForStable = true; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // back to lean headAngles = [20000,20000]; @@ -150,12 +181,15 @@ keyFrames = [ duration = 700; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = true; - hipYawErrorCompensation = 0; waitForStable = true; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // back to low stand headAngles = [20000,20000]; @@ -165,12 +199,15 @@ keyFrames = [ duration = 700; intType = sine; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // back to stand headAngles = [20000,20000]; @@ -180,12 +217,15 @@ keyFrames = [ duration = 300; intType = sine; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // copy for leavingPossible headAngles = [20000,20000]; @@ -195,11 +235,14 @@ keyFrames = [ duration = 100; intType = sine; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = true; + fallDownProtection = none; + holdFallDownProtection = 12; } ]; diff --git a/Config/KeyFrameEngine/kickLeftSlow.kfm b/Config/KeyFrameEngine/kickLeftSlow.kfm index 7b2e5aa5..b19bdd06 100644 --- a/Config/KeyFrameEngine/kickLeftSlow.kfm +++ b/Config/KeyFrameEngine/kickLeftSlow.kfm @@ -1,5 +1,7 @@ // kick with left foot keyFrameID = kickLeftSlow; +initialKeyFrame = true; +nextKeyFrameID = none; YstabilizationP = 0; YstabilizationI = 0; YstabilizationD = 0; @@ -15,12 +17,15 @@ keyFrames = [ duration = 200; intType = sine; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = true; + activateFallDownProtection = false; stabilize = true; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = true; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // lean to side headAngles = [20000,20000]; @@ -30,12 +35,15 @@ keyFrames = [ duration = 700; intType = sine; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = true; + activateFallDownProtection = false; stabilize = true; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = true; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // lift foot headAngles = [20000,20000]; @@ -45,12 +53,15 @@ keyFrames = [ duration = 100; intType = sine; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = true; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // prepare kick headAngles = [20000,20000]; @@ -60,12 +71,15 @@ keyFrames = [ duration = 300; intType = sine; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = true; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // kick part 1 headAngles = [20000,20000]; @@ -75,12 +89,15 @@ keyFrames = [ duration = 60; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = true; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // kick part 2 headAngles = [20000,20000]; @@ -90,12 +107,15 @@ keyFrames = [ duration = 48; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = true; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // kick part 3 headAngles = [20000,20000]; @@ -105,12 +125,15 @@ keyFrames = [ duration = 36; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = true; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // kick part 4 headAngles = [20000,20000]; @@ -120,12 +143,15 @@ keyFrames = [ duration = 60; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = true; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // back to lift foot headAngles = [20000,20000]; @@ -135,12 +161,15 @@ keyFrames = [ duration = 500; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = true; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // back to lean headAngles = [20000,20000]; @@ -150,12 +179,15 @@ keyFrames = [ duration = 700; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = true; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // back to low stand 1 headAngles = [20000,20000]; @@ -165,12 +197,15 @@ keyFrames = [ duration = 400; intType = sine; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = true; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // back to low stand 2 headAngles = [20000,20000]; @@ -180,12 +215,15 @@ keyFrames = [ duration = 300; intType = sine; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = true; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // back to stand headAngles = [20000,20000]; @@ -195,12 +233,15 @@ keyFrames = [ duration = 300; intType = sine; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = true; - hipYawErrorCompensation = 0; waitForStable = true; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // copy for leavingPossible headAngles = [20000,20000]; @@ -210,11 +251,14 @@ keyFrames = [ duration = 100; intType = sine; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = true; + fallDownProtection = none; + holdFallDownProtection = 12; } ]; diff --git a/Config/KeyFrameEngine/lying.kfm b/Config/KeyFrameEngine/lying.kfm new file mode 100644 index 00000000..cff419d2 --- /dev/null +++ b/Config/KeyFrameEngine/lying.kfm @@ -0,0 +1,29 @@ +keyFrameID = lying; +initialKeyFrame = true; +nextKeyFrameID = none; +YstabilizationP = -0.05; +YstabilizationI = 0; +YstabilizationD = -0.6; +XstabilizationP = 0; +XstabilizationI = 0; +XstabilizationD = 0; +keyFrames = [//stand copy + { //0deg,0deg, 90deg,10deg,100deg,0deg,-90deg,0, 90deg,-10deg,-100deg,0deg,90deg,0, 0,0,0deg,0deg,53deg,0, 0,0,0deg,0deg,53deg,0 + headAngles = [0deg,0deg]; + armsAngles = [90deg,10deg,100deg,0deg,-90deg,0, 90deg,-10deg,-100deg,0deg,90deg,0]; + legsAngles = [0,0,-21deg,49deg,-27.5deg,0, 0,0,-21deg,49deg,-27.5deg,0]; + stiffnesses = [40,40, 40,40,40,40,40,40, 40,40,40,40,40,40, 60,60,60,60,60,60, 60,60,60,60,60,60]; + duration = 1000; + intType = sine; + angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; + stabilize = false; + waitForStable = false; + leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; + } +]; diff --git a/Config/KeyFrameEngine/penaltyGoalieDiveLeft.kfm b/Config/KeyFrameEngine/penaltyGoalieDiveLeft.kfm index f9325fcd..c68b4eee 100644 --- a/Config/KeyFrameEngine/penaltyGoalieDiveLeft.kfm +++ b/Config/KeyFrameEngine/penaltyGoalieDiveLeft.kfm @@ -1,4 +1,6 @@ keyFrameID = penaltyGoalieDiveLeft; +initialKeyFrame = true; +nextKeyFrameID = none; YstabilizationP = 0.1; YstabilizationI = 0; YstabilizationD = -0.02; @@ -11,18 +13,22 @@ keyFrames = [ //HeadYaw, HeadPitch headAngles = [20000,20000]; //ShoulderPitch, ShoulderRoll, ElbowYaw, ElbowRoll, WristYaw, Hand - armsAngles = [50.0deg,45.0deg,0.0deg,-20.0deg,0.0deg,0.0deg, 50.0deg,-45.0deg,0.0deg,20.0deg,0.0deg,0.0deg]; + armsAngles = [50.0deg,45.0deg,0.0deg,-20.0deg,0.0deg,0.0deg, 50.0deg,-10.0deg,0.0deg,20.0deg,0.0deg,0.0deg]; //HipYawPitch, HipRoll, HipPitch, KneePitch, AnklePitch, AnkleRoll legsAngles = [0.0deg,0.0deg,-55.0deg,115.0deg,-60.0deg,0.0deg, 0.0deg,0.0deg,-55.0deg,115.0deg,-60.0deg,0.0deg]; - stiffnesses = [50,50, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 12; + stiffnesses = [50,50, 50,50,50,50,50,50, 50,50,50,50,50,50 ,60,60,60,60,60,60, 60,60,60,60,60,60]; + duration = 200; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; - stabilize = false; hipYawErrorCompensation = 0; + activateFallDownProtection = false; + stabilize = false; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, //speed up to ground (let fall) arm in pose 1 { @@ -31,18 +37,22 @@ keyFrames = [ //armsAngles = [-96.7deg,38.9deg,12.7deg,-33.9deg,-90deg,0deg, 30deg,20deg,0deg,70deg,90.0deg,0deg]; //armsAngles = [-100deg,55deg,0deg,0deg,-90.0deg,0deg, 30deg,20deg,0deg,70deg,90.0deg,0deg]; //armsAngles = [16.0deg,68.1deg,-9.8deg,-25deg,-90deg,0deg, 30deg,20deg,0deg,70deg,90.0deg,0deg]; // - armsAngles = [23.8deg,76.9deg,-24.8deg,-0.9deg,-90deg,0deg, 31.8deg,8.9deg,-1.1deg,68.2deg,90deg,0deg]; //pose1 + armsAngles = [23.8deg,76.9deg,-24.8deg,-0.9deg,-90deg,0deg, 31.8deg,8.9deg,-1.1deg,58.2deg,90deg,0deg]; //pose1 //HipYawPitch, HipRoll, HipPitch, KneePitch, AnklePitch, AnkleRoll legsAngles = [0.0deg,0.0deg,-55.0deg,115.0deg,-60.0deg,0.0deg, 0deg, 0.0deg,-45.0deg,100.0deg,-55.0deg,0deg]; stiffnesses = [20,20, 70,70,70,70,70,70, 30,30,30,30,30,30, 100,100,100,100,100,100, 100,100,100,100,100,100]; duration = 200; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; - stabilize = false; hipYawErrorCompensation = 0; + activateFallDownProtection = false; + stabilize = false; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, //hold on fall - body in pose1 { @@ -58,11 +68,15 @@ keyFrames = [ duration = 250; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; - stabilize = false; hipYawErrorCompensation = 0; + activateFallDownProtection = false; + stabilize = false; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, //hold on fall - arm in pose2 { @@ -72,15 +86,18 @@ keyFrames = [ // HipYawPitch, HipRoll, HipPitch, KneePitch, AnklePitch, AnkleRoll legsAngles = [0.0deg,0.0deg,-60.0deg,120.0deg,-60.0deg,0.0deg, 0deg, 0.0deg,-45.0deg,90.0deg,-55.0deg,0deg]; stiffnesses = [20,20, 50,50,50,50,50,50, 30,30,30,30,30,30, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 180; + duration = 220; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, //hold on fall - arm in pose3 { @@ -90,38 +107,124 @@ keyFrames = [ // HipYawPitch, HipRoll, HipPitch, KneePitch, AnklePitch, AnkleRoll legsAngles = [0.0deg,0.0deg,-60.0deg,120.0deg,-60.0deg,0.0deg, 0deg, 0.0deg,-45.0deg,90.0deg,-55.0deg,0deg]; stiffnesses = [20,20, 50,50,50,50,50,50, 30,30,30,30,30,30, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 200; + duration = 220; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, //hold on fall - arm in pose4 { headAngles = [-0.5deg,20.3deg]; //ShoulderPitch, ShoulderRoll, ElbowYaw, ElbowRoll, WristYaw, Hand - armsAngles = [-90.0deg,14.0deg,9.9deg,-0.9deg,-90deg,0deg, 31.8deg,8.9deg,-1.1deg,68.2deg,90deg,0deg]; //pose4 + armsAngles = [-90.0deg,20.0deg,9.9deg,-0.9deg,-90deg,0deg, 31.8deg,8.9deg,-1.1deg,68.2deg,90deg,0deg]; //pose4 // HipYawPitch, HipRoll, HipPitch, KneePitch, AnklePitch, AnkleRoll legsAngles = [0.0deg,0.0deg,-60.0deg,120.0deg,-60.0deg,0.0deg, 0deg, 0.0deg,-45.0deg,90.0deg,-55.0deg,0deg]; - stiffnesses = [20,20, 50,50,50,50,50,50, 30,30,30,30,30,30, 100,100,100,100,100,100, 100,100,100,100,100,100]; + stiffnesses = [20,20, 100,100,100,100,80,80, 30,30,30,30,30,30, 100,100,100,100,100,100, 100,100,100,100,100,100]; duration = 200; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; + stabilize = false; + waitForStable = false; + leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; + }, + { //0 + headAngles = [-0.5deg,20.3deg]; + //ShoulderPitch, ShoulderRoll, ElbowYaw, ElbowRoll, WristYaw, Hand + armsAngles = [-90.0deg,20.0deg,9.9deg,-0.9deg,-90deg,0deg, 31.8deg,8.9deg,-1.1deg,68.2deg,90deg,0deg]; //pose4 + // HipYawPitch, HipRoll, HipPitch, KneePitch, AnklePitch, AnkleRoll + legsAngles = [0.0deg,0.0deg,-60.0deg,120.0deg,-60.0deg,0.0deg, 0deg, 0.0deg,-45.0deg,90.0deg,-55.0deg,0deg]; + stiffnesses = [20,20, 100,100,100,100,80,80, 30,30,30,30,30,30, 100,100,100,100,100,100, 100,100,100,100,100,100]; + duration = 300; + intType = linear; + angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; + stabilize = false; + waitForStable = false; + leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; + }, + { //1 + headAngles = [-0.5deg,20.3deg]; + //ShoulderPitch, ShoulderRoll, ElbowYaw, ElbowRoll, WristYaw, Hand + //armsAngles = [20deg,20deg,10deg,0deg,-90deg,0deg, 90deg,-11deg,0,0deg,90deg,0]; + armsAngles = [-23.5deg,32.5deg,14.7deg,-5.7deg,-90deg,0deg, 90deg,-11deg,0,0deg,90deg,0]; + //23.1deg,29.3deg,-21.0deg,-2.4deg,-90deg,0deg, //1 + //-23.5deg,32.5deg,14.7deg,-5.7deg,-90deg,0deg, //3 + // HipYawPitch, HipRoll, HipPitch, KneePitch, AnklePitch, AnkleRoll + legsAngles = [0.0deg,0.0deg,-60.0deg,120.0deg,-60.0deg,0.0deg, 0deg, 0.0deg,20.0deg,90.0deg,-55.0deg,0deg]; + //legsAngles = [0deg,-20deg,-80.0deg,120.0deg,-65.0deg,-22deg, 0deg, 0.0deg,20.0deg,90.0deg,-55.0deg,0deg]; + stiffnesses = [20,20, 100,100,100,100,80,80, 30,30,30,30,30,30, 100,100,100,100,100,100, 100,100,100,100,100,100]; + duration = 300; + intType = linear; + angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; + stabilize = false; + waitForStable = false; + leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; + }, + { //2 + headAngles = [-0.5deg,20.3deg]; + //ShoulderPitch, ShoulderRoll, ElbowYaw, ElbowRoll, WristYaw, Hand + //armsAngles = [20deg,20deg,10deg,0deg,-90deg,0deg, 90deg,-11deg,0,0deg,90deg,0]; + //armsAngles = [72.7deg,34.0deg,-45.8deg,-4.5deg,-90deg,0deg, 90deg,-11deg,0,0deg,90deg,0]; + //67.0deg,45.5deg,-30.1deg,-6.3deg,-90deg,0deg, //2 + armsAngles = [61.0deg,44.0deg,8.9deg,-2.9deg,-90deg,0deg, 90deg,-11deg,0,0deg,90deg,0]; + // HipYawPitch, HipRoll, HipPitch, KneePitch, AnklePitch, AnkleRoll + //legsAngles = [0,0,-21deg,49deg,-27.5deg,0, 0,0,0deg,49deg,-27.5deg,0]; + legsAngles = [0.0deg,0.0deg,-60.0deg,120.0deg,-60.0deg,0.0deg, 0deg, 0.0deg,0.0deg,90.0deg,-55.0deg,0deg]; + stiffnesses = [20,20, 100,100,100,100,80,80, 30,30,30,30,30,30, 100,100,100,100,100,100, 100,100,100,100,100,100]; + duration = 300; + intType = linear; + angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; + }, + { //3 + headAngles = [-0.5deg,20.3deg]; + armsAngles = [90deg,11deg,0,0deg,-90deg,0, 90deg,-11deg,0,0deg,90deg,0]; + legsAngles = [0,0,-21deg,49deg,-27.5deg,0, 0,0,-21deg,49deg,-27.5deg,0]; + stiffnesses = [40,40, 80,80,80,80,80,80, 40,40,40,40,40,40, 60,60,60,60,60,60, 60,60,60,60,60,60]; + duration = 500; + intType = linear; + angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; + stabilize = true; + waitForStable = true; + leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; } ]; - - -//armsAngles = [23.8deg,76.9deg,-24.8deg,-0.9deg,-90deg,0deg, 79.2deg,-5.6deg,14.8deg,1.1deg,90deg,0deg]; pose1 -//armsAngles = [-38.9deg,71.0deg,-24.7deg,-0.7deg,-90deg,0deg, 79.6deg,-5.8deg,14.8deg,1.1deg,90deg,0deg]; pose2 -//armsAngles = [-88.9deg,48.9deg,-24.8deg,-10.8deg,-90deg,0deg, 78.8deg,-5.5deg,14.8deg,1.1deg,90deg,0deg]; pose3 -//armsAngles = [-90.0deg,14.0deg,9.9deg,-0.9deg,-90deg,0deg, 51.2deg,-6.2deg,-14.2deg,40.9deg,90deg,0deg]; pose4 diff --git a/Config/KeyFrameEngine/penaltyGoaliePrepareDive.kfm b/Config/KeyFrameEngine/penaltyGoaliePrepareDive.kfm index b9398468..ab4e1142 100644 --- a/Config/KeyFrameEngine/penaltyGoaliePrepareDive.kfm +++ b/Config/KeyFrameEngine/penaltyGoaliePrepareDive.kfm @@ -1,4 +1,6 @@ keyFrameID = penaltyGoaliePrepareDive; +initialKeyFrame = true; +nextKeyFrameID = none; YstabilizationP = 0.1; YstabilizationI = 0; YstabilizationD = -0.02; @@ -9,34 +11,40 @@ keyFrames = [ { headAngles = [20000,20000]; //ShoulderPitch, ShoulderRoll, ElbowYaw, ElbowRoll, WristYaw, Hand - armsAngles = [50.0deg,45.0deg,0.0deg,-20.0deg,0.0deg,0.0deg,50.0deg,-45.0deg,0.0deg,20.0deg,0.0deg,0.0deg]; + armsAngles = [50.0deg,11.0deg,0.0deg,-20.0deg,0.0deg,0.0deg,50.0deg,-11.0deg,0.0deg,20.0deg,0.0deg,0.0deg]; //HipYawPitch, HipRoll, HipPitch, KneePitch, AnklePitch, AnkleRoll - legsAngles = [0.0deg,0.0deg,-55.0deg,115.0deg,-60.0deg,0.0deg,0.0deg,0.0deg,-55.0deg,115.0deg,-60.0deg,0.0deg]; - stiffnesses = [50,50,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100]; + legsAngles = [0,0,-49deg,120deg,-67deg,0, 0,0,-49deg,120deg,-67deg,0]; + stiffnesses = [40,40, 40,40,40,40,40,40, 40,40,40,40,40,40, 50,50,50,50,50,50, 50,50,50,50,50,50]; duration = 1000; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, //duration of next line affects reaction time { headAngles = [20000,20000]; - armsAngles = [50.0deg,45.0deg,0.0deg,-20.0deg,0.0deg,0.0deg,50.0deg,-45.0deg,0.0deg,20.0deg,0.0deg,0.0deg]; - legsAngles = [0.0deg,0.0deg,-55.0deg,115.0deg,-60.0deg,0.0deg,0.0deg,0.0deg,-55.0deg,115.0deg,-60.0deg,0.0deg]; - stiffnesses = [50,50,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100]; - duration = 30; + armsAngles = [50.0deg,11.0deg,0.0deg,-20.0deg,0.0deg,0.0deg,50.0deg,-11.0deg,0.0deg,20.0deg,0.0deg,0.0deg]; + legsAngles = [0,0,-49deg,120deg,-67deg,0, 0,0,-49deg,120deg,-67deg,0]; + stiffnesses = [40,40, 40,40,40,40,40,40, 40,40,40,40,40,40, 10,0,10,0,0,0, 10,0,10,0,0,0]; + duration = 100; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; } ]; diff --git a/Config/KeyFrameEngine/playDead.kfm b/Config/KeyFrameEngine/playDead.kfm index 326f7073..66362c79 100644 --- a/Config/KeyFrameEngine/playDead.kfm +++ b/Config/KeyFrameEngine/playDead.kfm @@ -1,4 +1,6 @@ keyFrameID = playDead; +initialKeyFrame = true; +nextKeyFrameID = none; YstabilizationP = 0.1; YstabilizationI = 0; YstabilizationD = -0.02; @@ -7,18 +9,21 @@ XstabilizationI = 0; XstabilizationD = 0; keyFrames = [ { - headAngles = [10000,10000]; + headAngles = [0deg,10000]; armsAngles = [10000,10000,10000,10000,10000,10000, 10000,10000,10000,10000,10000,10000]; legsAngles = [10000,10000,10000,10000,10000,10000, 10000,10000,10000,10000,10000,10000]; stiffnesses = [0,0, 0,0,0,0,0,0, 0,0,0,0,0,0, 0,0,0,0,0,0, 0,0,0,0,0,0]; duration = 0; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; } -]; \ No newline at end of file +]; diff --git a/Config/KeyFrameEngine/saveFall.kfm b/Config/KeyFrameEngine/saveFall.kfm new file mode 100644 index 00000000..95d2442d --- /dev/null +++ b/Config/KeyFrameEngine/saveFall.kfm @@ -0,0 +1,47 @@ +keyFrameID = saveFall; +initialKeyFrame = true; +nextKeyFrameID = none; +YstabilizationP = 0; +YstabilizationI = 0; +YstabilizationD = 0; +XstabilizationP = 0; +XstabilizationI = 0; +XstabilizationD = 0; +keyFrames = [ + { + headAngles = [0deg,0deg]; + armsAngles = [77deg,20deg,0deg,-15deg,-90deg,0, 77deg,-20deg,0deg,15deg,90deg,0]; + legsAngles = [0deg,0deg,-40deg,105deg,-45deg,0deg, 0deg,0deg,-40deg,105deg,-45deg,0deg]; + stiffnesses = [30,30, 15,15,15,15,15,15, 15,15,15,15,15,15, 20,20,20,20,20,20, 20,20,20,20,20,20]; + duration = 0; + intType = linear; + angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; + stabilize = false; + waitForStable = false; + leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; + }, + { + headAngles = [0deg,0deg]; + armsAngles = [77deg,20deg,0deg,-15deg,-90deg,0, 77deg,-20deg,0deg,15deg,90deg,0]; + legsAngles = [0deg,0deg,-40deg,105deg,-45deg,0deg, 0deg,0deg,-40deg,105deg,-45deg,0deg]; + stiffnesses = [30,30, 15,15,15,15,15,15, 15,15,15,15,15,15, 20,20,20,20,20,20, 20,20,20,20,20,20]; + duration = 0; + intType = linear; + angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; + stabilize = false; + waitForStable = false; + leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; + } +]; diff --git a/Config/KeyFrameEngine/saveFallBack.kfm b/Config/KeyFrameEngine/saveFallBack.kfm new file mode 100644 index 00000000..744e80e3 --- /dev/null +++ b/Config/KeyFrameEngine/saveFallBack.kfm @@ -0,0 +1,47 @@ +keyFrameID = saveFallBack; +initialKeyFrame = true; +nextKeyFrameID = none; +YstabilizationP = 0; +YstabilizationI = 0; +YstabilizationD = 0; +XstabilizationP = 0; +XstabilizationI = 0; +XstabilizationD = 0; +keyFrames = [ + { + headAngles = [0deg,29deg]; + armsAngles = [123deg,12deg,17deg,-78deg,-50deg,0deg, 123deg,-12deg,-17deg,78deg,50deg,0deg]; + legsAngles = [0deg,0deg,-90deg,105deg,-45deg,0deg, 0deg,0deg,-90deg,105deg,-45deg,0deg]; + stiffnesses = [30,30, 15,15,15,20,15,15, 15,15,15,20,15,15, 20,20,20,20,20,20, 20,20,20,20,20,20]; + duration = 0; + intType = linear; + angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront = 30deg; + angleAtKeyFrameErrorBack = 30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; + stabilize = false; + waitForStable = false; + leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; + }, + { + headAngles = [0deg,29deg]; + armsAngles = [123deg,12deg,17deg,-78deg,-50deg,0deg, 123deg,-12deg,-17deg,78deg,50deg,0deg]; + legsAngles = [0deg,0deg,-90deg,105deg,-45deg,0deg, 0deg,0deg,-90deg,105deg,-45deg,0deg]; + stiffnesses = [30,30, 15,15,15,20,15,15, 15,15,15,20,15,15, 20,20,20,20,20,20, 20,20,20,20,20,20]; + duration = 0; + intType = linear; + angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront = 30deg; + angleAtKeyFrameErrorBack = 30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; + stabilize = false; + waitForStable = false; + leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; + } +]; diff --git a/Config/KeyFrameEngine/saveFallFront.kfm b/Config/KeyFrameEngine/saveFallFront.kfm new file mode 100644 index 00000000..096c0494 --- /dev/null +++ b/Config/KeyFrameEngine/saveFallFront.kfm @@ -0,0 +1,65 @@ +keyFrameID = saveFallFront; +initialKeyFrame = true; +nextKeyFrameID = none; +YstabilizationP = 0; +YstabilizationI = 0; +YstabilizationD = 0; +XstabilizationP = 0; +XstabilizationI = 0; +XstabilizationD = 0; +keyFrames = [ + { + headAngles = [0deg,-38deg]; + armsAngles = [90deg,10deg,100deg,0deg,-90deg,0, 90deg,-10deg,-100deg,0deg,90deg,0]; + legsAngles = [0deg,0deg,-24deg,105deg,-75deg,0deg, 0deg,0deg,-24deg,105deg,-75deg,0deg]; + stiffnesses = [100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 20,20,20,20,20,20, 20,20,20,20,20,20]; + duration = 0; + intType = linear; + angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; + stabilize = false; + waitForStable = false; + leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; + }, + { + headAngles = [0deg,-38deg]; + armsAngles = [5deg,10deg,100deg,-60deg,-90deg,0, 5deg,-10deg,-100deg,60deg,90deg,0]; + legsAngles = [0deg,0deg,-24deg,105deg,-75deg,0deg, 0deg,0deg,-24deg,105deg,-75deg,0deg]; + stiffnesses = [30,30, 15,15,15,15,15,15, 15,15,15,15,15,15, 20,20,20,20,20,20, 20,20,20,20,20,20]; + duration = 0; + intType = linear; + angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; + stabilize = false; + waitForStable = false; + leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; + }, + { + headAngles = [0deg,-38deg]; + armsAngles = [5deg,10deg,100deg,-60deg,-90deg,0, 5deg,-10deg,-100deg,60deg,90deg,0]; + legsAngles = [0deg,0deg,-24deg,105deg,-75deg,0deg, 0deg,0deg,-24deg,105deg,-75deg,0deg]; + stiffnesses = [30,30, 15,15,15,15,15,15, 15,15,15,15,15,15, 20,20,20,20,20,20, 20,20,20,20,20,20]; + duration = 0; + intType = linear; + angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; + stabilize = false; + waitForStable = false; + leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; + } +]; diff --git a/Config/KeyFrameEngine/sitDown.kfm b/Config/KeyFrameEngine/sitDown.kfm index bac3293d..dfa36521 100644 --- a/Config/KeyFrameEngine/sitDown.kfm +++ b/Config/KeyFrameEngine/sitDown.kfm @@ -1,4 +1,6 @@ keyFrameID = sitDown; +initialKeyFrame = true; +nextKeyFrameID = none; YstabilizationP = 0.1; YstabilizationI = 0; YstabilizationD = -0.02; @@ -14,12 +16,15 @@ keyFrames = [ duration = 2000; intType = sine; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { headAngles = [0,20deg]; @@ -29,12 +34,15 @@ keyFrames = [ duration = 500; intType = sine; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { headAngles = [10000,10000]; @@ -44,11 +52,14 @@ keyFrames = [ duration = 100; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; } ]; diff --git a/Config/KeyFrameEngine/stand.kfm b/Config/KeyFrameEngine/stand.kfm index a759a4df..6ad0c7cb 100644 --- a/Config/KeyFrameEngine/stand.kfm +++ b/Config/KeyFrameEngine/stand.kfm @@ -1,39 +1,47 @@ keyFrameID = stand; -YstabilizationP = 0.1; +initialKeyFrame = true; +nextKeyFrameID = none; +YstabilizationP = -0.05; YstabilizationI = 0; -YstabilizationD = -0.02; +YstabilizationD = -0.6; XstabilizationP = 0; XstabilizationI = 0; XstabilizationD = 0; keyFrames = [ { - headAngles = [20000,20000]; - armsAngles = [90deg,11deg,0,0deg,-90deg,0, 90deg,-11deg,0,0deg,90deg,0]; - legsAngles = [0,0,-21deg,49deg,-27.5deg,0, 0,0,-21deg,49deg,-27.5deg,0]; + headAngles = [0deg,20000]; + armsAngles = [90deg,10deg,0,0deg,-90deg,0, 90deg,-10deg,0,0deg,90deg,0]; + legsAngles = [0,0,-14.5deg,37.15deg,-22.7deg,0, 0,0,-14.5deg,37.15deg,-22.7deg,0]; stiffnesses = [40,40, 40,40,40,40,40,40, 40,40,40,40,40,40, 100,100,100,100,100,100, 100,100,100,100,100,100]; duration = 1000; intType = sine; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; + activateFallDownProtection = false; + stabilize = true; + waitForStable = true; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { headAngles = [20000,20000]; - armsAngles = [90deg,11deg,0,0deg,-90deg,0, 90deg,-11deg,0,0deg,90deg,0]; - legsAngles = [0,0,-21deg,49deg,-27.5deg,0, 0,0,-21deg,49deg,-27.5deg,0]; + armsAngles = [90deg,10deg,0,0deg,-90deg,0, 90deg,-10deg,0,0deg,90deg,0]; + legsAngles = [0,0,-14.5deg,37.15deg,-22.7deg,0, 0,0,-14.5deg,37.15deg,-22.7deg,0]; stiffnesses = [40,40, 40,40,40,40,40,40, 40,40,40,40,40,40, 60,60,60,60,60,60, 60,60,60,60,60,60]; duration = 100; intType = sine; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; + activateFallDownProtection = false; + stabilize = true; + waitForStable = true; leavingPossible = true; + fallDownProtection = none; + holdFallDownProtection = 12; } ]; diff --git a/Config/KeyFrameEngine/standHigh.kfm b/Config/KeyFrameEngine/standHigh.kfm index 756c66fc..3bd25316 100644 --- a/Config/KeyFrameEngine/standHigh.kfm +++ b/Config/KeyFrameEngine/standHigh.kfm @@ -1,5 +1,7 @@ //copy from stand keyFrameID = standHigh; +initialKeyFrame = true; +nextKeyFrameID = none; YstabilizationP = 0.1; YstabilizationI = 0; YstabilizationD = -0.02; @@ -8,19 +10,22 @@ XstabilizationI = 0; XstabilizationD = 0; keyFrames = [ { - headAngles = [20000,20000]; + headAngles = [0deg,20000]; armsAngles = [20000,20000,20000,20000,20000,20000, 20000,20000,20000,20000,20000,20000]; legsAngles = [20000,20000,20000,20000,20000,20000, 20000,20000,20000,20000,20000,20000]; stiffnesses = [40,40, 40,40,40,40,40,40, 40,40,40,40,40,40, 100,100,100,100,100,100, 100,100,100,100,100,100]; duration = 400; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { headAngles = [20000,20000]; @@ -30,12 +35,15 @@ keyFrames = [ duration = 1000; intType = sine; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { headAngles = [20000,20000]; @@ -45,11 +53,14 @@ keyFrames = [ duration = 100; intType = sine; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = true; + fallDownProtection = none; + holdFallDownProtection = 12; } -]; \ No newline at end of file +]; diff --git a/Config/KeyFrameEngine/standUpBackNao.kfm b/Config/KeyFrameEngine/standUpBackNao.kfm deleted file mode 100644 index 928cc814..00000000 --- a/Config/KeyFrameEngine/standUpBackNao.kfm +++ /dev/null @@ -1,224 +0,0 @@ -// copy from standUpBackNaoFast! -keyFrameID = standUpBackNao; -YstabilizationP = 0.1; -YstabilizationI = 0; -YstabilizationD = -0.02; -XstabilizationP = 0; -XstabilizationI = 0; -XstabilizationD = 0; -keyFrames = [ - { // pull legs up - headAngles = [0,22deg]; - armsAngles = [0deg,15deg,90deg,-90deg,-90deg,0deg, 0deg,-15deg,-90deg,90deg,90deg,0]; - legsAngles = [-2deg,13deg,-21deg,-5deg,0deg,0deg, -2deg,-13deg,-21deg,-5deg,0deg,0deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 400; - intType = linear; - angleAtKeyFrameTarget = -90deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { // ?? - headAngles = [0,22deg]; - armsAngles = [0deg,15deg,90deg,-90deg,-90deg,0, 0deg,-15deg,-90deg,90deg,90deg,0]; - legsAngles = [-2deg,13deg,-61deg,-5deg,2deg,-1deg, -2deg,-13deg,-61deg,-5deg,7deg,6deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 200; - intType = linear; - angleAtKeyFrameTarget = -90deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { // hold #2, moving arms to prepare ninja move - headAngles = [0,22deg]; - armsAngles = [60deg,50deg,80deg,-80deg,-90deg,0, 60deg,-50deg,-80deg,80deg,90deg,0]; - legsAngles = [0deg,13deg,-16deg,-5deg,0deg,5deg, 0deg,-13deg,-16deg,-5deg,0deg,-5deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 200; - intType = linear; - angleAtKeyFrameTarget = -90deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { // ninja move -> legs down and upper body up - headAngles = [0,22deg]; - armsAngles = [118deg,-12deg,80deg,2deg,-90deg,0, 118deg,12deg,-80deg,-2deg,90deg,0]; - legsAngles = [0deg,0deg,-80deg,-5deg,0deg,0deg, 0deg,0deg,-80deg,-5deg,0deg,0deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 500; - intType = linear; - angleAtKeyFrameTarget = -10deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - - - { // spread legs - headAngles = [0,22deg]; - armsAngles = [120deg,5deg,-70deg,2deg,-90deg,0, 120deg,-5deg,70deg,-2deg,90deg,0]; - legsAngles = [-55deg,15deg,-50deg,-5deg,10deg,-10deg, -55deg,-15deg,-50deg,-5deg,10deg,10deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 200; - intType = linear; - angleAtKeyFrameTarget = -10deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - - { // pull legs in a little - headAngles = [0,22deg]; - armsAngles = [120deg,-5deg,-85deg,-19deg,-90deg,0, 121deg,-1deg,87deg,-22deg,90deg,0]; - legsAngles = [-65deg,42deg,-80deg,76.5deg,49deg,-7deg, -65deg,-32.5deg,-84deg,82deg,47deg,1deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 300; - intType = linear; - angleAtKeyFrameTarget = -10deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { // push onto left leg - headAngles = [0,22deg]; - armsAngles = [116deg,-8deg,-85deg,-16deg,-90deg,0, 121deg,-40deg,99deg,-8deg,90deg,0]; - legsAngles = [-65deg,12deg,-30deg,121deg,-41deg,-19deg, -65deg,-20deg,-88deg,48deg,53deg,-1deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 500; - intType = linear; - angleAtKeyFrameTarget = 10deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { // copy of push onto left leg, todo: use waitForStable instead? - headAngles = [0,22deg]; - armsAngles = [116deg,-8deg,-85deg,-16deg,-90deg,0, 121deg,-40deg,99deg,-8deg,90deg,0]; - legsAngles = [-65deg,12deg,-30deg,121deg,-41deg,-19deg, -65deg,-20deg,-88deg,48deg,53deg,-1deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 100; - intType = linear; - angleAtKeyFrameTarget = -10deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - - { // lean on left leg - headAngles = [0,22deg]; - armsAngles = [100deg,21deg,-85deg,-15deg,-90deg,0, 107deg,-20deg,99deg,-9deg,90deg,0]; - legsAngles = [-65deg,4deg,-32deg,121deg,-58deg,10deg, -65deg,-15deg,-18deg,-5deg,53deg,4deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 500; - intType = linear; - angleAtKeyFrameTarget = -10deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { // pull right leg in - headAngles = [0,22deg]; - armsAngles = [90deg,21deg,-85deg,-15deg,-90deg,0, 99deg,0deg,99deg,-9deg,90deg,0]; - legsAngles = [-43.5deg,12deg,-55deg,121deg,-56deg,9deg, -43.5deg,13.5deg,-44deg,95deg,-16deg,15deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 300; - intType = linear; - angleAtKeyFrameTarget = -10deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { // to sitting position, wide legs - headAngles = [0,22deg]; - armsAngles = [87deg,19deg,-85deg,-14deg,-90deg,0, 81deg,-11deg,99deg,-8deg,90deg,0]; - legsAngles = [-33deg,-7deg,-60deg,121deg,-49deg,7.5deg, -33deg,9deg,-57deg,121deg,-51deg,-9deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 300; - intType = linear; - angleAtKeyFrameTarget = 0deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { // sitting position, legs parallel - headAngles = [0,22deg]; - armsAngles = [82deg,13deg,-84deg,-12deg,-90deg,0, 75deg,-7deg,99deg,-7deg,90deg,0]; - legsAngles = [0deg,-4deg,-53deg,121deg,-67deg,2deg, 0deg,6deg,-52deg,121deg,-67deg,-5deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 250; - intType = linear; - angleAtKeyFrameTarget = 0deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { // stand up copy. was different in mof!! - headAngles = [0,20deg]; - armsAngles = [90deg,11deg,0,90deg,-90deg,0, 90deg,-11deg,0,-90deg,90deg,0]; - legsAngles = [0,0,-21deg,49deg,-27.5deg,0, 0,0,-21deg,49deg,-27.5deg,0]; - stiffnesses = [40,40, 40,40,40,40,40,40, 40,40,40,40,40,40, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 500; - intType = sine; - angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = true; - stabilize = true; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { // stand up copy for leavingPossible=true - headAngles = [0,20deg]; - armsAngles = [90deg,11deg,0,90deg,-90deg,0, 90deg,-11deg,0,-90deg,90deg,0]; - legsAngles = [0,0,-21deg,49deg,-27.5deg,0, 0,0,-21deg,49deg,-27.5deg,0]; - stiffnesses = [40,40, 40,40,40,40,40,40, 40,40,40,40,40,40, 60,60,60,60,60,60, 60,60,60,60,60,60]; - duration = 100; - intType = linear; - angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = true; - stabilize = true; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = true; - } -]; \ No newline at end of file diff --git a/Config/KeyFrameEngine/standUpBackNaoFast.kfm b/Config/KeyFrameEngine/standUpBackNaoFast.kfm index b79cf1c2..5b6c20f4 100644 --- a/Config/KeyFrameEngine/standUpBackNaoFast.kfm +++ b/Config/KeyFrameEngine/standUpBackNaoFast.kfm @@ -1,223 +1,236 @@ keyFrameID = standUpBackNaoFast; -YstabilizationP = 0.1; +initialKeyFrame = true; +nextKeyFrameID = stand; +YstabilizationP = -0.05; YstabilizationI = 0; -YstabilizationD = -0.02; +YstabilizationD = -0.6; XstabilizationP = 0; XstabilizationI = 0; XstabilizationD = 0; keyFrames = [ { // push hip up, to get arms under body - headAngles = [0,22deg]; - armsAngles = [123deg,8deg,17deg,-78deg,-90deg,0deg, 123deg,-8deg,-17deg,78deg,90deg,0deg]; - legsAngles = [0deg,0deg,0deg,110deg,0deg,0deg, 0deg,0deg,0deg,110deg,0deg,0deg]; + //0deg, 30deg, 123deg, 8deg, 17deg, -88deg, -90deg, 0deg, 123deg, -8deg, -17deg, 88deg, 90deg, 0deg, 0deg, 0deg, 30deg, 90deg, 0deg, -0deg, 0deg, 0deg, 30deg, 90deg, 0deg, -0deg + headAngles = [0,30deg]; + armsAngles = [123deg,8deg,17deg,-88deg,-90deg,0deg, 123deg,-8deg,-17deg,88deg,90deg,0deg]; + legsAngles = [0deg,0deg,30deg,90deg,0deg,0deg, 0deg,0deg,30deg,90deg,0deg,0deg]; stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 300; + duration = 400; intType = linear; - angleAtKeyFrameTarget = -90deg; + angleAtKeyFrameTarget = -115deg; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, - { // pull legs up - headAngles = [0,22deg]; - armsAngles = [123deg,8deg,17deg,-78deg,-90deg,0deg, 123deg,-8deg,-17deg,78deg,90deg,0deg]; - legsAngles = [0deg,0deg,-88deg,-5deg,0deg,0deg, 0deg,0deg,-88deg,-5deg,0deg,0deg]; + { // strech legs 1 + //0,30deg, 123deg,8deg,17deg,-88deg,-90deg,0deg, 123deg,-8deg,-17deg,88deg,90deg,0deg, 0deg,0deg,15deg,20deg,53deg,0deg, 0deg,0deg,15deg,20deg,53deg,0deg + headAngles = [0,30deg]; + armsAngles = [123deg,8deg,17deg,-88deg,-90deg,0deg, 123deg,-8deg,-17deg,88deg,90deg,0deg]; + legsAngles = [0deg,0deg,15deg,20deg,53deg,0deg, 0deg,0deg,15deg,20deg,53deg,0deg]; stiffnesses = [40,40, 50,50,50,50,50,50, 50,50,50,50,50,50, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 400; + duration = 355; intType = linear; - angleAtKeyFrameTarget = -90deg; + angleAtKeyFrameTarget = -95deg; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, - { // hold #2, prepare ninja move - headAngles = [0,22deg]; - armsAngles = [123deg,8deg,17deg,-78deg,-90deg,0deg, 123deg,-8deg,-17deg,78deg,90deg,0deg]; - legsAngles = [0deg,13deg,-16deg,-5deg,60deg,5deg, 0deg,-13deg,-16deg,-5deg,60deg,-5deg]; + { // strech legs 2 + //0,30deg, 123deg,8deg,17deg,-88deg,-90deg,0deg, 123deg,-8deg,-17deg,88deg,90deg,0deg, 0deg,0deg,-20deg,20deg,55deg,0deg, 0deg,0deg,-20deg,20deg,55deg,0deg + headAngles = [0,30deg]; + armsAngles = [123deg,8deg,17deg,-88deg,-90deg,0deg, 123deg,-8deg,-17deg,88deg,90deg,0deg]; + legsAngles = [0deg,0deg,-20deg,20deg,55deg,0deg, 0deg,0deg,-20deg,20deg,55deg,0deg]; stiffnesses = [40,40, 50,50,50,50,50,50, 50,50,50,50,50,50, 100,100,100,100,100,100, 100,100,100,100,100,100]; duration = 200; intType = linear; - angleAtKeyFrameTarget = -90deg; - useAngleAtKeyFrameTarget = false; + angleAtKeyFrameTarget = -70deg; + angleAtKeyFrameErrorFront = 20deg; + angleAtKeyFrameErrorBack = 40deg; + useAngleAtKeyFrameTarget = true; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // ninja move -> legs down and upper body up + //0,22deg, 123deg,8deg,17deg,-60deg,-90deg,0deg, 123deg,-8deg,-17deg,60deg,90deg,0deg, 0deg,0deg,-65deg,60deg,50deg,0deg, 0deg,0deg,-65deg,60deg,50deg,0deg headAngles = [0,22deg]; armsAngles = [123deg,8deg,17deg,-60deg,-90deg,0deg, 123deg,-8deg,-17deg,60deg,90deg,0deg]; - legsAngles = [0deg,0deg,-80deg,-5deg,0deg,0deg, 0deg,0deg,-80deg,-5deg,0deg,0deg]; + legsAngles = [0deg,0deg,-65deg,60deg,50deg,0deg, 0deg,0deg,-65deg,60deg,50deg,0deg]; stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; duration = 500; intType = linear; - angleAtKeyFrameTarget = -10deg; - useAngleAtKeyFrameTarget = false; + angleAtKeyFrameTarget = -45deg; + angleAtKeyFrameErrorFront = 35deg; + angleAtKeyFrameErrorBack = 35deg; + useAngleAtKeyFrameTarget = true; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, - - { // spread legs + { // spread legs 1 + //0,22deg, 123deg,8deg,17deg,-30deg,-90deg,0deg, 123deg,-8deg,-17deg,30deg,90deg,0deg, -65deg,42deg,-80deg,76.5deg,49deg,-7deg, -65deg,-32.5deg,-84deg,82deg,47deg,1deg headAngles = [0,22deg]; - armsAngles = [120deg,5deg,-70deg,2deg,-90deg,0, 120deg,-5deg,70deg,-2deg,90deg,0]; - legsAngles = [-55deg,15deg,-50deg,-5deg,10deg,-10deg, -55deg,-15deg,-50deg,-5deg,10deg,10deg]; + armsAngles = [123deg,8deg,17deg,-30deg,-90deg,0deg, 123deg,-8deg,-17deg,30deg,90deg,0deg]; + legsAngles = [-65deg,42deg,-80deg,76.5deg,49deg,-7deg, -65deg,-32.5deg,-84deg,82deg,47deg,1deg]; stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 200; + duration = 450; intType = linear; - angleAtKeyFrameTarget = -10deg; - useAngleAtKeyFrameTarget = false; + angleAtKeyFrameTarget = -15deg; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = true; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, - { // pull legs in a little + + { // spread legs 2 + //0,22deg, 120deg,-5deg,-85deg,-19deg,-90deg,0, 121deg,-1deg,87deg,-22deg,90deg,0, -65deg,42deg,-80deg,76.5deg,49deg,-7deg, -65deg,-32.5deg,-84deg,82deg,47deg,1deg headAngles = [0,22deg]; armsAngles = [120deg,-5deg,-85deg,-19deg,-90deg,0, 121deg,-1deg,87deg,-22deg,90deg,0]; legsAngles = [-65deg,42deg,-80deg,76.5deg,49deg,-7deg, -65deg,-32.5deg,-84deg,82deg,47deg,1deg]; stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 300; - intType = linear; - angleAtKeyFrameTarget = -10deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { // push onto left leg - headAngles = [0,22deg]; - armsAngles = [116deg,-8deg,-85deg,-16deg,-90deg,0, 121deg,-40deg,99deg,-8deg,90deg,0]; - legsAngles = [-65deg,12deg,-30deg,121deg,-41deg,-19deg, -65deg,-20deg,-88deg,48deg,53deg,-1deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 500; + duration = 200; intType = linear; - angleAtKeyFrameTarget = 10deg; - useAngleAtKeyFrameTarget = false; + angleAtKeyFrameTarget = -6deg; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = true; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, - { // copy of push onto left leg, todo: use waitForStable instead? - headAngles = [0,22deg]; - armsAngles = [116deg,-8deg,-85deg,-16deg,-90deg,0, 121deg,-40deg,99deg,-8deg,90deg,0]; - legsAngles = [-65deg,12deg,-30deg,121deg,-41deg,-19deg, -65deg,-20deg,-88deg,48deg,53deg,-1deg]; + { // lean on left leg + headAngles = [0,30deg]; + armsAngles = [115.8deg,-7.6deg,0,0,-90deg,0, 120.7deg,-39.7deg,0,0,90deg,0]; + legsAngles = [-65.5deg,11.6deg,-35deg,123deg,-30.1deg,-10deg, -65.5deg,-30deg,-88deg,48.7deg,52.8deg,0]; stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 100; + duration = 400; intType = linear; - angleAtKeyFrameTarget = -10deg; - useAngleAtKeyFrameTarget = false; + angleAtKeyFrameTarget = 5deg; + angleAtKeyFrameErrorFront = 35deg; + angleAtKeyFrameErrorBack = 30deg; + useAngleAtKeyFrameTarget = true; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, - - { // lean on left leg - headAngles = [0,22deg]; - armsAngles = [100deg,21deg,-85deg,-15deg,-90deg,0, 107deg,-20deg,99deg,-9deg,90deg,0]; - legsAngles = [-65deg,4deg,-32deg,121deg,-58deg,10deg, -65deg,-15deg,-18deg,-5deg,53deg,4deg]; + { // squat + headAngles = [0,0]; + armsAngles = [90deg,30deg,0,0,-90deg,0, 90deg,-30deg,0,0,90deg,0]; + legsAngles = [-40deg,6.5deg,-60deg,123deg,-43deg,-14deg, -40deg,-30deg,-87deg,123deg,-27deg,22deg]; stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 500; - intType = linear; - angleAtKeyFrameTarget = -10deg; - useAngleAtKeyFrameTarget = false; + duration = 350; + intType = sine; + angleAtKeyFrameTarget = 20deg; + angleAtKeyFrameErrorFront = 35deg; + angleAtKeyFrameErrorBack = 30deg; + useAngleAtKeyFrameTarget = true; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, - { // pull right leg in - headAngles = [0,22deg]; - armsAngles = [90deg,21deg,-85deg,-15deg,-90deg,0, 99deg,0deg,99deg,-9deg,90deg,0]; - legsAngles = [-43.5deg,12deg,-55deg,121deg,-56deg,9deg, -43.5deg,13.5deg,-44deg,95deg,-16deg,15deg]; + { // squat 2 + headAngles = [0,0]; + armsAngles = [90deg,30deg,0,0,-90deg,0, 90deg,-30deg,0,0,90deg,0]; + legsAngles = [-40deg,6.5deg,-60deg,123deg,-38deg,-10deg, -40deg,-15deg,-87deg,123deg,-22deg,10deg]; stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 300; - intType = linear; - angleAtKeyFrameTarget = -10deg; - useAngleAtKeyFrameTarget = false; + duration = 100; + intType = sine; + angleAtKeyFrameTarget = 14deg; + angleAtKeyFrameErrorFront = 25deg; + angleAtKeyFrameErrorBack = 25deg; + useAngleAtKeyFrameTarget = true; + activateFallDownProtection = true; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, - { // to sitting position, wide legs - headAngles = [0,22deg]; - armsAngles = [87deg,19deg,-85deg,-14deg,-90deg,0, 81deg,-11deg,99deg,-8deg,90deg,0]; - legsAngles = [-33deg,-7deg,-60deg,121deg,-49deg,7.5deg, -33deg,9deg,-57deg,121deg,-51deg,-9deg]; + { // sit wide legs + headAngles = [0,0]; + armsAngles = [90deg,30deg,0,0,-90deg,0, 90deg,-30deg,0,0,90deg,0]; + legsAngles = [-40deg,0,-60deg,123deg,-41deg,0, -40deg,0,-60deg,123deg,-41deg,0]; stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; duration = 300; - intType = linear; - angleAtKeyFrameTarget = 0deg; - useAngleAtKeyFrameTarget = false; + intType = sine; + angleAtKeyFrameTarget = 10deg; + angleAtKeyFrameErrorFront = 25deg; + angleAtKeyFrameErrorBack = 20deg; + useAngleAtKeyFrameTarget = true; + activateFallDownProtection = true; stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; + waitForStable = true; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, - { // sitting position, legs parallel - headAngles = [0,22deg]; - armsAngles = [82deg,13deg,-84deg,-12deg,-90deg,0, 75deg,-7deg,99deg,-7deg,90deg,0]; - legsAngles = [0deg,-4deg,-53deg,121deg,-67deg,2deg, 0deg,6deg,-52deg,121deg,-67deg,-5deg]; + { // sit legs closed + headAngles = [0deg,0deg]; + armsAngles = [90deg,7deg,0,-2.5deg,-90deg,0, 90deg,-7deg,0,2.5deg,90deg,0]; + legsAngles = [0,0,-55deg,121deg,-67.8deg,0, 0,0,-55deg,121deg,-67.8deg,0]; stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 250; + duration = 400; intType = linear; - angleAtKeyFrameTarget = 0deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; + angleAtKeyFrameTarget = 2deg; + angleAtKeyFrameErrorFront = 20deg; + angleAtKeyFrameErrorBack = 20deg; + useAngleAtKeyFrameTarget = true; + activateFallDownProtection = false; + stabilize = true; + waitForStable = true; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // stand up copy. was different in mof!! headAngles = [0,20deg]; armsAngles = [90deg,11deg,0,90deg,-90deg,0, 90deg,-11deg,0,-90deg,90deg,0]; legsAngles = [0,0,-21deg,49deg,-27.5deg,0, 0,0,-21deg,49deg,-27.5deg,0]; stiffnesses = [40,40, 40,40,40,40,40,40, 40,40,40,40,40,40, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 500; + duration = 600; intType = sine; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront = 20deg; + angleAtKeyFrameErrorBack = 20deg; useAngleAtKeyFrameTarget = true; + activateFallDownProtection = true; stabilize = true; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; - }, - { // stand up copy for leavingPossible=true - headAngles = [0,20deg]; - armsAngles = [90deg,11deg,0,90deg,-90deg,0, 90deg,-11deg,0,-90deg,90deg,0]; - legsAngles = [0,0,-21deg,49deg,-27.5deg,0, 0,0,-21deg,49deg,-27.5deg,0]; - stiffnesses = [40,40, 40,40,40,40,40,40, 40,40,40,40,40,40, 60,60,60,60,60,60, 60,60,60,60,60,60]; - duration = 100; - intType = linear; - angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = true; - stabilize = true; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = true; + fallDownProtection = none; + holdFallDownProtection = 12; } ]; diff --git a/Config/KeyFrameEngine/standUpBackNaoFastOld.kfm b/Config/KeyFrameEngine/standUpBackNaoFastOld.kfm new file mode 100644 index 00000000..664ac49e --- /dev/null +++ b/Config/KeyFrameEngine/standUpBackNaoFastOld.kfm @@ -0,0 +1,257 @@ +keyFrameID = standUpBackNaoFastOld; +initialKeyFrame = true; +nextKeyFrameID = stand; +YstabilizationP = -0.05; +YstabilizationI = 0; +YstabilizationD = -0.6; +XstabilizationP = 0; +XstabilizationI = 0; +XstabilizationD = 0; +keyFrames = [ + { // push hip up, to get arms under body + //0,30deg, 123deg,8deg,17deg,-88deg,-90deg,0deg, 123deg,-8deg,-17deg,88deg,90deg,0deg, 0deg,0deg,30deg,90deg,0deg,0deg, 0deg,0deg,30deg,90deg,0deg,0deg + headAngles = [0,30deg]; + armsAngles = [123deg,8deg,17deg,-88deg,-90deg,0deg, 123deg,-8deg,-17deg,88deg,90deg,0deg]; + legsAngles = [0deg,0deg,30deg,90deg,0deg,0deg, 0deg,0deg,30deg,90deg,0deg,0deg]; + stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; + duration = 400; + intType = linear; + angleAtKeyFrameTarget = -115deg; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; + stabilize = false; + waitForStable = false; + leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; + }, + { // strech legs 1 + //0,30deg, 123deg,8deg,17deg,-88deg,-90deg,0deg, 123deg,-8deg,-17deg,88deg,90deg,0deg, 0deg,0deg,15deg,20deg,53deg,0deg, 0deg,0deg,15deg,20deg,53deg,0deg + headAngles = [0,30deg]; + armsAngles = [123deg,8deg,17deg,-88deg,-90deg,0deg, 123deg,-8deg,-17deg,88deg,90deg,0deg]; + legsAngles = [0deg,0deg,15deg,20deg,53deg,0deg, 0deg,0deg,15deg,20deg,53deg,0deg]; + stiffnesses = [40,40, 50,50,50,50,50,50, 50,50,50,50,50,50, 100,100,100,100,100,100, 100,100,100,100,100,100]; + duration = 200; + intType = linear; + angleAtKeyFrameTarget = -95deg; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; + stabilize = false; + waitForStable = false; + leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; + }, + { // strech legs 2 + //0,30deg, 123deg,8deg,17deg,-88deg,-90deg,0deg, 123deg,-8deg,-17deg,88deg,90deg,0deg, 0deg,0deg,-20deg,20deg,55deg,0deg, 0deg,0deg,-20deg,20deg,55deg,0deg + headAngles = [0,30deg]; + armsAngles = [123deg,8deg,17deg,-88deg,-90deg,0deg, 123deg,-8deg,-17deg,88deg,90deg,0deg]; + legsAngles = [0deg,0deg,-20deg,20deg,55deg,0deg, 0deg,0deg,-20deg,20deg,55deg,0deg]; + stiffnesses = [40,40, 50,50,50,50,50,50, 50,50,50,50,50,50, 100,100,100,100,100,100, 100,100,100,100,100,100]; + duration = 200; + intType = linear; + angleAtKeyFrameTarget = -70deg; + angleAtKeyFrameErrorFront = 35deg; + angleAtKeyFrameErrorBack = 35deg; + useAngleAtKeyFrameTarget = true; + activateFallDownProtection = false; + stabilize = false; + waitForStable = false; + leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; + }, + { // ninja move -> legs down and upper body up + //0,22deg, 123deg,8deg,17deg,-60deg,-90deg,0deg, 123deg,-8deg,-17deg,60deg,90deg,0deg, 0deg,0deg,-65deg,60deg,50deg,0deg, 0deg,0deg,-65deg,60deg,50deg,0deg + headAngles = [0,22deg]; + armsAngles = [123deg,8deg,17deg,-60deg,-90deg,0deg, 123deg,-8deg,-17deg,60deg,90deg,0deg]; + legsAngles = [0deg,0deg,-65deg,60deg,50deg,0deg, 0deg,0deg,-65deg,60deg,50deg,0deg]; + stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; + duration = 500; + intType = linear; + angleAtKeyFrameTarget = -48deg; + angleAtKeyFrameErrorFront = 35deg; + angleAtKeyFrameErrorBack = 35deg; + useAngleAtKeyFrameTarget = true; + activateFallDownProtection = false; + stabilize = false; + waitForStable = false; + leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; + }, + + + { // spread legs 1 + //0,22deg, 123deg,8deg,17deg,-30deg,-90deg,0deg, 123deg,-8deg,-17deg,30deg,90deg,0deg, -65deg,42deg,-80deg,76.5deg,49deg,-7deg, -65deg,-32.5deg,-84deg,82deg,47deg,1deg + headAngles = [0,22deg]; + armsAngles = [123deg,8deg,17deg,-30deg,-90deg,0deg, 123deg,-8deg,-17deg,30deg,90deg,0deg]; + legsAngles = [-65deg,42deg,-80deg,76.5deg,49deg,-7deg, -65deg,-32.5deg,-84deg,82deg,47deg,1deg]; + stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; + duration = 300; + intType = linear; + angleAtKeyFrameTarget = -15deg; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = true; + activateFallDownProtection = false; + stabilize = false; + waitForStable = false; + leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; + }, + + + { // spread legs 2 + //0,22deg, 120deg,-5deg,-85deg,-19deg,-90deg,0, 121deg,-1deg,87deg,-22deg,90deg,0, -65deg,42deg,-80deg,76.5deg,49deg,-7deg, -65deg,-32.5deg,-84deg,82deg,47deg,1deg + headAngles = [0,22deg]; + armsAngles = [120deg,-5deg,-85deg,-19deg,-90deg,0, 121deg,-1deg,87deg,-22deg,90deg,0]; + legsAngles = [-65deg,42deg,-80deg,76.5deg,49deg,-7deg, -65deg,-32.5deg,-84deg,82deg,47deg,1deg]; + stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; + duration = 300; + intType = linear; + angleAtKeyFrameTarget = -6deg; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = true; + activateFallDownProtection = false; + stabilize = false; + waitForStable = false; + leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; + }, + { // push onto left leg + //0,22deg, 116deg,-8deg,-85deg,-16deg,-90deg,0, 121deg,-40deg,99deg,-8deg,90deg,0, -65deg,12deg,-30deg,121deg,-41deg,-19deg, -65deg,-20deg,-88deg,48deg,53deg,-1deg + headAngles = [0,22deg]; + armsAngles = [116deg,-8deg,-85deg,-16deg,-90deg,0, 121deg,-40deg,99deg,-8deg,90deg,0]; + legsAngles = [-65deg,12deg,-30deg,121deg,-41deg,-19deg, -65deg,-20deg,-88deg,48deg,53deg,-1deg]; + stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; + duration = 500; + intType = linear; + angleAtKeyFrameTarget = 15deg; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = true; + activateFallDownProtection = false; + stabilize = false; + waitForStable = false; + leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; + }, + { // push onto left leg, wait for stable + headAngles = [0,22deg]; + armsAngles = [116deg,40deg,-85deg,-16deg,-90deg,0, 121deg,-40deg,99deg,-8deg,90deg,0]; + legsAngles = [-65deg,12deg,-30deg,121deg,-41deg,-19deg, -65deg,-20deg,-88deg,48deg,53deg,-1deg]; + stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; + duration = 200; + intType = linear; + angleAtKeyFrameTarget = 16deg; + angleAtKeyFrameErrorFront = 25deg; + angleAtKeyFrameErrorBack = 25deg; + useAngleAtKeyFrameTarget = true; + activateFallDownProtection = false; + stabilize = false; + waitForStable = true; + leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; + }, + + { // lean on left leg + headAngles = [0,22deg]; + armsAngles = [100deg,21deg,-85deg,-15deg,-90deg,0, 107deg,-20deg,99deg,-9deg,90deg,0]; + legsAngles = [-65deg,4deg,-32deg,121deg,-58deg,10deg, -65deg,-15deg,-18deg,-5deg,53deg,4deg]; + stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; + duration = 600; + intType = sine; + angleAtKeyFrameTarget = 11deg; + angleAtKeyFrameErrorFront = 35deg; + angleAtKeyFrameErrorBack = 20deg; + useAngleAtKeyFrameTarget = true; + activateFallDownProtection = false; + stabilize = false; + waitForStable = false; + leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; + }, + { // pull right leg in, ToDo: currently unstable + headAngles = [0,22deg]; + armsAngles = [90deg,21deg,-85deg,-15deg,-90deg,0, 99deg,0deg,99deg,-9deg,90deg,0]; + legsAngles = [-43.5deg,12deg,-55deg,121deg,-56deg,9deg, -43.5deg,13.5deg,-44deg,95deg,-16deg,15deg]; + stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; + duration = 300; + intType = linear; + angleAtKeyFrameTarget = 13deg; + angleAtKeyFrameErrorFront = 35deg; + angleAtKeyFrameErrorBack = 30deg; + useAngleAtKeyFrameTarget = true; + activateFallDownProtection = false; + stabilize = false; + waitForStable = false; + leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; + }, + { // to sitting position, wide legs + headAngles = [0,22deg]; + armsAngles = [87deg,19deg,-85deg,-14deg,-90deg,0, 81deg,-11deg,99deg,-8deg,90deg,0]; + legsAngles = [-33deg,-7deg,-60deg,121deg,-49deg,7.5deg, -33deg,9deg,-57deg,121deg,-51deg,-9deg]; + stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; + duration = 300; + intType = linear; + angleAtKeyFrameTarget = 15deg; + angleAtKeyFrameErrorFront = 35deg; + angleAtKeyFrameErrorBack = 20deg; + useAngleAtKeyFrameTarget = true; + activateFallDownProtection = true; + stabilize = false; + waitForStable = false; + leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; + }, + { // sitting position, legs parallel + headAngles = [0,22deg]; + armsAngles = [82deg,13deg,-84deg,-12deg,-90deg,0, 75deg,-7deg,99deg,-7deg,90deg,0]; + legsAngles = [0deg,-4deg,-53deg,121deg,-67deg,2deg, 0deg,6deg,-52deg,121deg,-67deg,-5deg]; + stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; + duration = 250; + intType = linear; + angleAtKeyFrameTarget = 0deg; + angleAtKeyFrameErrorFront = 25deg; + angleAtKeyFrameErrorBack = 25deg; + useAngleAtKeyFrameTarget = true; + activateFallDownProtection = true; + stabilize = true; + waitForStable = true; + leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; + }, + { // stand up copy. was different in mof!! + headAngles = [0,20deg]; + armsAngles = [90deg,11deg,0,90deg,-90deg,0, 90deg,-11deg,0,-90deg,90deg,0]; + legsAngles = [0,0,-21deg,49deg,-27.5deg,0, 0,0,-21deg,49deg,-27.5deg,0]; + stiffnesses = [40,40, 40,40,40,40,40,40, 40,40,40,40,40,40, 100,100,100,100,100,100, 100,100,100,100,100,100]; + duration = 600; + intType = sine; + angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront = 20deg; + angleAtKeyFrameErrorBack = 20deg; + useAngleAtKeyFrameTarget = true; + activateFallDownProtection = true; + stabilize = true; + waitForStable = false; + leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; + } +]; diff --git a/Config/KeyFrameEngine/standUpBackNaoMed.kfm b/Config/KeyFrameEngine/standUpBackNaoMed.kfm deleted file mode 100644 index ecbdbcc0..00000000 --- a/Config/KeyFrameEngine/standUpBackNaoMed.kfm +++ /dev/null @@ -1,223 +0,0 @@ -keyFrameID = standUpBackNaoMed; -YstabilizationP = 0.1; -YstabilizationI = 0; -YstabilizationD = -0.02; -XstabilizationP = 0; -XstabilizationI = 0; -XstabilizationD = 0; -keyFrames = [ - {// push hip up, to get arms under body - headAngles = [0,22deg]; - armsAngles = [123deg,8deg,17deg,-78deg,-90deg,0deg, 123deg,-8deg,-17deg,78deg,90deg,0deg]; - legsAngles = [0deg,0deg,0deg,110deg,0deg,0deg, 0deg,0deg,0deg,110deg,0deg,0deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 300; - intType = linear; - angleAtKeyFrameTarget = -90deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { // pull legs up - headAngles = [0,22deg]; - armsAngles = [123deg,8deg,17deg,-78deg,-90deg,0deg, 123deg,-8deg,-17deg,78deg,90deg,0deg]; - legsAngles = [0deg,0deg,-88deg,-5deg,0deg,0deg, 0deg,0deg,-88deg,-5deg,0deg,0deg]; - stiffnesses = [40,40, 50,50,50,50,50,50, 50,50,50,50,50,50, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 400; - intType = linear; - angleAtKeyFrameTarget = -90deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { // hold #2, prepare ninja move - headAngles = [0,22deg]; - armsAngles = [123deg,8deg,17deg,-78deg,-90deg,0deg, 123deg,-8deg,-17deg,78deg,90deg,0deg]; - legsAngles = [0deg,13deg,-16deg,-5deg,60deg,5deg, 0deg,-13deg,-16deg,-5deg,60deg,-5deg]; - stiffnesses = [40,40, 50,50,50,50,50,50, 50,50,50,50,50,50, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 200; - intType = linear; - angleAtKeyFrameTarget = -90deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { // ninja move -> legs down and upper body up - headAngles = [0,22deg]; - armsAngles = [123deg,8deg,17deg,-60deg,-90deg,0deg, 123deg,-8deg,-17deg,60deg,90deg,0deg]; - legsAngles = [0deg,0deg,-80deg,-5deg,0deg,0deg, 0deg,0deg,-80deg,-5deg,0deg,0deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 500; - intType = linear; - angleAtKeyFrameTarget = -10deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - - - { // spread legs - headAngles = [0,22deg]; - armsAngles = [120deg,5deg,-70deg,2deg,-90deg,0, 120deg,-5deg,70deg,-2deg,90deg,0]; - legsAngles = [-55deg,15deg,-50deg,-5deg,10deg,-10deg, -55deg,-15deg,-50deg,-5deg,10deg,10deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 200; - intType = linear; - angleAtKeyFrameTarget = -10deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - - { // pull legs in a little - headAngles = [0,0deg]; - armsAngles = [120deg,-5deg,-85deg,-19deg,-90deg,0, 121deg,-1deg,87deg,-22deg,90deg,0]; - legsAngles = [-65deg,42deg,-80deg,76.5deg,49deg,-7deg, -65deg,-32.5deg,-84deg,82deg,47deg,1deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 300; - intType = linear; - angleAtKeyFrameTarget = -10deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { // push onto left leg - headAngles = [0,-10deg]; - armsAngles = [116deg,-8deg,-85deg,-16deg,-90deg,0, 121deg,-40deg,99deg,-8deg,90deg,0]; - legsAngles = [-65deg,12deg,-30deg,121deg,-41deg,-19deg, -65deg,-20deg,-88deg,48deg,53deg,-1deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 500; - intType = linear; - angleAtKeyFrameTarget = 10deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { // copy of push onto left leg, todo: use waitForStable instead? - headAngles = [0,-10deg]; - armsAngles = [116deg,-8deg,-85deg,-16deg,-90deg,0, 121deg,-40deg,99deg,-8deg,90deg,0]; - legsAngles = [-65deg,12deg,-30deg,121deg,-41deg,-19deg, -65deg,-20deg,-88deg,48deg,53deg,-1deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 100; - intType = linear; - angleAtKeyFrameTarget = -10deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = true; - waitForTorsoTime = 0; - leavingPossible = false; - }, - - { // lean on left leg - headAngles = [0,22deg]; - armsAngles = [100deg,21deg,-85deg,-15deg,-90deg,0, 107deg,-20deg,99deg,-9deg,90deg,0]; - legsAngles = [-65deg,4deg,-32deg,121deg,-58deg,10deg, -65deg,-15deg,-18deg,-5deg,53deg,4deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 500; - intType = linear; - angleAtKeyFrameTarget = -10deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = true; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { // pull right leg in - headAngles = [0,22deg]; - armsAngles = [90deg,21deg,-85deg,-15deg,-90deg,0, 99deg,0deg,99deg,-9deg,90deg,0]; - legsAngles = [-43.5deg,12deg,-55deg,121deg,-56deg,9deg, -43.5deg,13.5deg,-44deg,95deg,-16deg,15deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 300; - intType = linear; - angleAtKeyFrameTarget = -10deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { // to sitting position, wide legs - headAngles = [0,22deg]; - armsAngles = [87deg,19deg,-85deg,-14deg,-90deg,0, 81deg,-11deg,99deg,-8deg,90deg,0]; - legsAngles = [-33deg,-7deg,-60deg,121deg,-49deg,7.5deg, -33deg,9deg,-57deg,121deg,-51deg,-9deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 300; - intType = linear; - angleAtKeyFrameTarget = 0deg; - useAngleAtKeyFrameTarget = true; - stabilize = true; - hipYawErrorCompensation = 0; - waitForStable = true; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { // sitting position, legs parallel - headAngles = [0,22deg]; - armsAngles = [82deg,13deg,-84deg,-12deg,-90deg,0, 75deg,-7deg,99deg,-7deg,90deg,0]; - legsAngles = [0deg,-4deg,-53deg,121deg,-67deg,2deg, 0deg,6deg,-52deg,121deg,-67deg,-5deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 250; - intType = linear; - angleAtKeyFrameTarget = 0deg; - useAngleAtKeyFrameTarget = true; - stabilize = true; - hipYawErrorCompensation = 0; - waitForStable = true; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { // stand up copy. was different in mof!! - headAngles = [0,20deg]; - armsAngles = [90deg,11deg,0,90deg,-90deg,0, 90deg,-11deg,0,-90deg,90deg,0]; - legsAngles = [0,0,-21deg,49deg,-27.5deg,0, 0,0,-21deg,49deg,-27.5deg,0]; - stiffnesses = [40,40, 40,40,40,40,40,40, 40,40,40,40,40,40, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 500; - intType = sine; - angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = true; - stabilize = true; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { // stand up copy for leavingPossible=true - headAngles = [0,20deg]; - armsAngles = [90deg,11deg,0,90deg,-90deg,0, 90deg,-11deg,0,-90deg,90deg,0]; - legsAngles = [0,0,-21deg,49deg,-27.5deg,0, 0,0,-21deg,49deg,-27.5deg,0]; - stiffnesses = [40,40, 40,40,40,40,40,40, 40,40,40,40,40,40, 60,60,60,60,60,60, 60,60,60,60,60,60]; - duration = 100; - intType = linear; - angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = true; - stabilize = true; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = true; - } -]; diff --git a/Config/KeyFrameEngine/standUpBackNaoSlow.kfm b/Config/KeyFrameEngine/standUpBackNaoSlow.kfm deleted file mode 100644 index 159a84fe..00000000 --- a/Config/KeyFrameEngine/standUpBackNaoSlow.kfm +++ /dev/null @@ -1,224 +0,0 @@ -// copy from standUpBackNaoFast! -keyFrameID = standUpBackNaoSlow; -YstabilizationP = 0.1; -YstabilizationI = 0; -YstabilizationD = -0.02; -XstabilizationP = 0; -XstabilizationI = 0; -XstabilizationD = 0; -keyFrames = [ - { // push hip up, to get arms under body - headAngles = [0,22deg]; - armsAngles = [123deg,8deg,17deg,-78deg,-90deg,0deg, 123deg,-8deg,-17deg,78deg,90deg,0deg]; - legsAngles = [0deg,0deg,0deg,110deg,0deg,0deg, 0deg,0deg,0deg,110deg,0deg,0deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 300; - intType = linear; - angleAtKeyFrameTarget = -90deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { // pull legs up - headAngles = [0,22deg]; - armsAngles = [123deg,8deg,17deg,-78deg,-90deg,0deg, 123deg,-8deg,-17deg,78deg,90deg,0deg]; - legsAngles = [0deg,0deg,-88deg,-5deg,0deg,0deg, 0deg,0deg,-88deg,-5deg,0deg,0deg]; - stiffnesses = [40,40, 50,50,50,50,50,50, 50,50,50,50,50,50, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 400; - intType = linear; - angleAtKeyFrameTarget = -90deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { // hold #2, prepare ninja move - headAngles = [0,22deg]; - armsAngles = [123deg,8deg,17deg,-78deg,-90deg,0deg, 123deg,-8deg,-17deg,78deg,90deg,0deg]; - legsAngles = [0deg,13deg,-16deg,-5deg,60deg,5deg, 0deg,-13deg,-16deg,-5deg,60deg,-5deg]; - stiffnesses = [40,40, 50,50,50,50,50,50, 50,50,50,50,50,50, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 200; - intType = linear; - angleAtKeyFrameTarget = -90deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { // ninja move -> legs down and upper body up - headAngles = [0,22deg]; - armsAngles = [123deg,8deg,17deg,-60deg,-90deg,0deg, 123deg,-8deg,-17deg,60deg,90deg,0deg]; - legsAngles = [0deg,0deg,-80deg,-5deg,0deg,0deg, 0deg,0deg,-80deg,-5deg,0deg,0deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 500; - intType = linear; - angleAtKeyFrameTarget = -10deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - - - { // spread legs - headAngles = [0,22deg]; - armsAngles = [120deg,5deg,-70deg,2deg,-90deg,0, 120deg,-5deg,70deg,-2deg,90deg,0]; - legsAngles = [-55deg,15deg,-50deg,-5deg,10deg,-10deg, -55deg,-15deg,-50deg,-5deg,10deg,10deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 500; - intType = linear; - angleAtKeyFrameTarget = -10deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - - { // pull legs in a little - headAngles = [0,22deg]; - armsAngles = [120deg,-5deg,-85deg,-19deg,-90deg,0, 121deg,-1deg,87deg,-22deg,90deg,0]; - legsAngles = [-65deg,42deg,-80deg,76.5deg,49deg,-7deg, -65deg,-32.5deg,-84deg,82deg,47deg,1deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 400; - intType = linear; - angleAtKeyFrameTarget = -10deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { // push onto left leg - headAngles = [0,22deg]; - armsAngles = [116deg,-8deg,-85deg,-16deg,-90deg,0, 121deg,-40deg,99deg,-8deg,90deg,0]; - legsAngles = [-65deg,12deg,-30deg,121deg,-41deg,-19deg, -65deg,-20deg,-88deg,48deg,53deg,-1deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 600; - intType = linear; - angleAtKeyFrameTarget = 10deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { // copy of push onto left leg, todo: use waitForStable instead? - headAngles = [0,22deg]; - armsAngles = [116deg,-8deg,-85deg,-16deg,-90deg,0, 121deg,-40deg,99deg,-8deg,90deg,0]; - legsAngles = [-65deg,12deg,-30deg,121deg,-41deg,-19deg, -65deg,-20deg,-88deg,48deg,53deg,-1deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 300; - intType = linear; - angleAtKeyFrameTarget = -10deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - - { // lean on left leg - headAngles = [0,22deg]; - armsAngles = [100deg,21deg,-85deg,-15deg,-90deg,0, 107deg,-20deg,99deg,-9deg,90deg,0]; - legsAngles = [-65deg,4deg,-32deg,121deg,-58deg,10deg, -65deg,-15deg,-18deg,-5deg,53deg,4deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 600; - intType = linear; - angleAtKeyFrameTarget = -10deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { // pull right leg in - headAngles = [0,22deg]; - armsAngles = [90deg,21deg,-85deg,-15deg,-90deg,0, 99deg,0deg,99deg,-9deg,90deg,0]; - legsAngles = [-43.5deg,12deg,-55deg,121deg,-56deg,9deg, -43.5deg,13.5deg,-44deg,95deg,-16deg,15deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 600; - intType = linear; - angleAtKeyFrameTarget = -10deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { // to sitting position, wide legs - headAngles = [0,22deg]; - armsAngles = [87deg,19deg,-85deg,-14deg,-90deg,0, 81deg,-11deg,99deg,-8deg,90deg,0]; - legsAngles = [-33deg,-7deg,-60deg,121deg,-49deg,7.5deg, -33deg,9deg,-57deg,121deg,-51deg,-9deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 500; - intType = linear; - angleAtKeyFrameTarget = 0deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { // sitting position, legs parallel - headAngles = [0,22deg]; - armsAngles = [82deg,13deg,-84deg,-12deg,-90deg,0, 75deg,-7deg,99deg,-7deg,90deg,0]; - legsAngles = [0deg,-4deg,-53deg,121deg,-67deg,2deg, 0deg,6deg,-52deg,121deg,-67deg,-5deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 500; - intType = linear; - angleAtKeyFrameTarget = 0deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { // stand up copy. was different in mof!! - headAngles = [0,20deg]; - armsAngles = [90deg,11deg,0,90deg,-90deg,0, 90deg,-11deg,0,-90deg,90deg,0]; - legsAngles = [0,0,-21deg,49deg,-27.5deg,0, 0,0,-21deg,49deg,-27.5deg,0]; - stiffnesses = [40,40, 40,40,40,40,40,40, 40,40,40,40,40,40, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 700; - intType = sine; - angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = true; - stabilize = true; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { // stand up copy for leavingPossible=true - headAngles = [0,20deg]; - armsAngles = [90deg,11deg,0,90deg,-90deg,0, 90deg,-11deg,0,-90deg,90deg,0]; - legsAngles = [0,0,-21deg,49deg,-27.5deg,0, 0,0,-21deg,49deg,-27.5deg,0]; - stiffnesses = [40,40, 40,40,40,40,40,40, 40,40,40,40,40,40, 60,60,60,60,60,60, 60,60,60,60,60,60]; - duration = 100; - intType = linear; - angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = true; - stabilize = true; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = true; - } -]; diff --git a/Config/KeyFrameEngine/standUpFrontNaoFast.kfm b/Config/KeyFrameEngine/standUpFrontNaoFast.kfm index c412c214..d0b0650c 100644 --- a/Config/KeyFrameEngine/standUpFrontNaoFast.kfm +++ b/Config/KeyFrameEngine/standUpFrontNaoFast.kfm @@ -1,192 +1,194 @@ keyFrameID = standUpFrontNaoFast; -YstabilizationP = 0.0; +initialKeyFrame = true; +nextKeyFrameID = stand; +YstabilizationP = -0.04; //-0.05; YstabilizationI = 0; -YstabilizationD = 0.00; +YstabilizationD = -0.45; // -0.6; XstabilizationP = 0; XstabilizationI = 0; XstabilizationD = 0; keyFrames = [ - { // stand but head back since we are lying on front + { // arms to the side headAngles = [0,-30deg]; - armsAngles = [90deg,11deg,0,90deg,-90deg,0, 90deg,-11deg,0,-90deg,90deg,0]; - legsAngles = [0,0,-21deg,49deg,-27.5deg,0, 0,0,-21deg,49deg,-27.5deg,0]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 200; + armsAngles = [68.5deg,71.9deg,-121.9deg,-4.6deg,-90deg,0deg, 84.6deg,-72.9deg,120.4deg,6.4deg,90deg,0]; + legsAngles = [0,0,-21.3deg,48.9deg,-27.6deg,0, 0,0,-21.2deg,49deg,-27.6deg,0]; + stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 40,40,40,40,40,40, 40,40,40,40,40,40]; + duration = 300; intType = linear; - angleAtKeyFrameTarget = 90deg; + angleAtKeyFrameTarget = 95deg; + angleAtKeyFrameErrorFront = 30deg; + angleAtKeyFrameErrorBack = 30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // pull legs in and arms to side - headAngles = [-4deg,-47deg]; - armsAngles = [80deg,73deg,-19deg,-3deg,-90deg,0, 83deg,-73deg,30deg,1deg,90deg,0]; - legsAngles = [-34deg,-1deg,-79deg,122deg,-69deg,-4deg, -34deg,7deg,-66deg,122deg,-69deg,-1deg]; + headAngles = [0,-30deg]; + armsAngles = [-13.9deg,68.2deg,-122.2deg,-11.1deg,-90deg,0, -13.5deg,-67.9deg,121.9deg,8deg,90deg,0]; + legsAngles = [-3.9deg,0.2deg,-84.6deg,123.7deg,-52.9deg,-2.3deg, -3.9deg,-0.7deg,-80.2deg,122deg,-47.9deg,2.2deg]; stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; duration = 200; intType = linear; - angleAtKeyFrameTarget = 100deg; + angleAtKeyFrameTarget = 105deg; + angleAtKeyFrameErrorFront = 30deg; + angleAtKeyFrameErrorBack = 30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, - { // use arms and legs for push-up? - headAngles = [-6deg,27deg]; - armsAngles = [28deg,74deg,-123deg,-4deg,-90deg,0, 22deg,-74deg,121deg,3deg,90deg,0]; - legsAngles = [-32deg,-21deg,-84deg,78deg,-65deg,-10deg, -32deg,10deg,-86deg,76deg,-68deg,7deg]; + { // plank + headAngles = [0,-30deg]; + armsAngles = [-13.9deg,68.2deg,-122.2deg,-65deg,-90deg,0, -13.5deg,-67.9deg,121.9deg,65deg,90deg,0]; + legsAngles = [0.3deg,-1.6deg,-57.6deg,41.7deg,-68deg,4.5deg, 0.3deg,-0.6deg,-57.2deg,42.7deg,-68deg,0.3deg]; stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 200; + duration = 355; intType = linear; - angleAtKeyFrameTarget = 100deg; + angleAtKeyFrameTarget = 110deg; + angleAtKeyFrameErrorFront = 30deg; + angleAtKeyFrameErrorBack = 30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, - { // push-up legs? - headAngles = [-5deg,30deg]; - armsAngles = [-47deg,11deg,-81deg,-80deg,-90deg,0, -49deg,-11deg,108deg,79deg,90deg,0]; - legsAngles = [-42deg,-15deg,-92deg,-4deg,12deg,-25deg, -42deg,11deg,-91deg,-6deg,11deg,26deg]; + { // push-up + // 0deg,-30deg, -39.64deg,-3.87deg,-90.62deg,-88.07deg,-87.54deg,0, -39.39deg,16.61deg,103.36deg,80.51deg,84.37deg,0, -28.4deg,-17.52deg,-92.59deg,-7.31deg,-10.54deg,-20.8deg, -28.4deg,17.52deg,-92.59deg,-7.31deg,-10.54deg,20.8deg + headAngles = [0deg,-30deg]; + armsAngles = [-39.64deg,-3.87deg,-90.62deg,-88.07deg,-87.54deg,0, -39.39deg,16.61deg,103.36deg,80.51deg,84.37deg,0]; + legsAngles = [-28.4deg,-17.52deg,-92.59deg,-7.31deg,-10.54deg,-20.8deg, -28.4deg,17.52deg,-92.59deg,-7.31deg,-10.54deg,20.8deg]; stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; duration = 250; intType = linear; - angleAtKeyFrameTarget = 100deg; + angleAtKeyFrameTarget = 123deg; + angleAtKeyFrameErrorFront = 30deg; + angleAtKeyFrameErrorBack = 30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, - { // push onto left leg - headAngles = [0,22deg]; - armsAngles = [80deg,30deg,-85deg,-16deg,-90deg,0, 90deg,-50deg,99deg,-8deg,90deg,0]; - legsAngles = [-53.5deg,-1.5deg,-61deg,58.5deg,-14.5deg,-19deg, -53.5deg,-5.5deg,-89.5deg,21deg,32deg,16.5deg]; + { // monkey sit + headAngles = [0,-30deg]; + armsAngles = [0deg,0.7deg,-123.67deg,-0.96deg,-89.12deg,0, 0deg,5.36deg,117.33deg,1.5deg,87.19deg,0]; + legsAngles = [-54.24deg,-19.8deg,-87.05deg,97.72deg,-23.53deg,5deg, -54.24deg,19.37deg,-90.83deg,82.78deg,8deg,6.92deg]; stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 200; + duration = 250; intType = linear; - angleAtKeyFrameTarget = -10deg; - useAngleAtKeyFrameTarget = false; + angleAtKeyFrameTarget = 65deg; + angleAtKeyFrameErrorFront = 60deg; + angleAtKeyFrameErrorBack = 20deg; + useAngleAtKeyFrameTarget = true; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; + waitForStable = true; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, - - { // push onto left leg - headAngles = [0,22deg]; - armsAngles = [116deg,40deg,-85deg,-16deg,-90deg,0, 121deg,-40deg,99deg,-8deg,90deg,0]; - legsAngles = [-65deg,12deg,-30deg,121deg,-41deg,-19deg, -65deg,-20deg,-88deg,48deg,53deg,-1deg]; + { // monkey sit 2 + headAngles = [0,-30deg]; + armsAngles = [0deg,0.7deg,-123.67deg,-0.96deg,-89.12deg,0, 0deg,5.36deg,117.33deg,1.5deg,87.19deg,0]; + legsAngles = [-54.24deg,-19.8deg,-87.05deg,110deg,-23.53deg,5deg, -54.24deg,19.37deg,-90.83deg,95deg,8deg,6.92deg]; stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; duration = 200; intType = linear; - angleAtKeyFrameTarget = -10deg; - useAngleAtKeyFrameTarget = false; + angleAtKeyFrameTarget = 58deg; + angleAtKeyFrameErrorFront = 30deg; + angleAtKeyFrameErrorBack = 20deg; + useAngleAtKeyFrameTarget = true; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = true; - waitForTorsoTime = 1000; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, - - { // lean on left leg - headAngles = [0,22deg]; - armsAngles = [100deg,21deg,-85deg,-15deg,-90deg,0, 107deg,-20deg,99deg,-9deg,90deg,0]; - legsAngles = [-65deg,4deg,-32deg,121deg,-58deg,10deg, -65deg,-15deg,-18deg,-5deg,53deg,4deg]; + { // squat + headAngles = [0deg,-30deg]; + armsAngles = [60deg,20deg,0deg,20000,20000,20000, 60deg,-20deg,0,20000,20000,20000]; + legsAngles = [-50deg,0,-70deg,100deg,-15deg,-5deg, -50deg,0,-70deg,100deg,-15deg,5deg]; stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 600; - intType = sine; - angleAtKeyFrameTarget = -10deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { // pull right leg in - headAngles = [0,22deg]; - armsAngles = [90deg,21deg,-85deg,-15deg,-90deg,0, 99deg,0deg,99deg,-9deg,90deg,0]; - legsAngles = [-43.5deg,12deg,-55deg,121deg,-56deg,9deg, -43.5deg,13.5deg,-44deg,95deg,-16deg,15deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 300; + duration = 250; intType = linear; - angleAtKeyFrameTarget = -10deg; - useAngleAtKeyFrameTarget = false; + angleAtKeyFrameTarget = 27deg; + angleAtKeyFrameErrorFront = 45deg; + angleAtKeyFrameErrorBack = 25deg; + useAngleAtKeyFrameTarget = true; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; + waitForStable = true; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, - { // to sitting position, wide legs - headAngles = [0,22deg]; - armsAngles = [87deg,19deg,-85deg,-14deg,-90deg,0, 81deg,-11deg,99deg,-8deg,90deg,0]; - legsAngles = [-33deg,-7deg,-60deg,121deg,-49deg,7.5deg, -33deg,9deg,-57deg,121deg,-51deg,-9deg]; + { // sit wide legs + headAngles = [0,0]; + armsAngles = [90deg,30deg,0,0,-90deg,0, 90deg,-30deg,0,0,90deg,0]; + legsAngles = [-40deg,0,-60deg,123deg,-41deg,0, -40deg,0,-60deg,123deg,-41deg,0]; stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; duration = 300; intType = linear; - angleAtKeyFrameTarget = 0deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; + angleAtKeyFrameTarget = 2deg; + angleAtKeyFrameErrorFront = 30deg; + angleAtKeyFrameErrorBack = 25deg; + useAngleAtKeyFrameTarget = true; + activateFallDownProtection = true; + stabilize = true; + waitForStable = true; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, - { // sitting position, legs parallel - headAngles = [0,22deg]; - armsAngles = [82deg,13deg,-84deg,-12deg,-90deg,0, 75deg,-7deg,99deg,-7deg,90deg,0]; - legsAngles = [0deg,-4deg,-53deg,121deg,-67deg,2deg, 0deg,6deg,-52deg,121deg,-67deg,-5deg]; + { // sit legs closed + headAngles = [0deg,0deg]; + armsAngles = [90deg,7deg,0,-2.5deg,-90deg,0, 90deg,-7deg,0,2.5deg,90deg,0]; + legsAngles = [0,0,-55deg,121deg,-67.8deg,0, 0,0,-55deg,121deg,-67.8deg,0]; stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 250; + duration = 500; intType = linear; angleAtKeyFrameTarget = 0deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; + angleAtKeyFrameErrorFront = 25deg; + angleAtKeyFrameErrorBack = 20deg; + useAngleAtKeyFrameTarget = true; + activateFallDownProtection = false; + stabilize = true; + waitForStable = true; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // stand up copy. was different in mof!! headAngles = [0,20deg]; armsAngles = [90deg,11deg,0,90deg,-90deg,0, 90deg,-11deg,0,-90deg,90deg,0]; legsAngles = [0,0,-21deg,49deg,-27.5deg,0, 0,0,-21deg,49deg,-27.5deg,0]; stiffnesses = [40,40, 40,40,40,40,40,40, 40,40,40,40,40,40, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 500; + duration = 600; intType = sine; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront = 20deg; + angleAtKeyFrameErrorBack = 20deg; useAngleAtKeyFrameTarget = true; + activateFallDownProtection = true; stabilize = true; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; - }, - { // stand up copy for leavingPossible=true - headAngles = [0,20deg]; - armsAngles = [90deg,11deg,0,90deg,-90deg,0, 90deg,-11deg,0,-90deg,90deg,0]; - legsAngles = [0,0,-21deg,49deg,-27.5deg,0, 0,0,-21deg,49deg,-27.5deg,0]; - stiffnesses = [40,40, 40,40,40,40,40,40, 40,40,40,40,40,40, 60,60,60,60,60,60, 60,60,60,60,60,60]; - duration = 100; - intType = linear; - angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = true; - stabilize = true; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = true; + fallDownProtection = none; + holdFallDownProtection = 12; } -]; \ No newline at end of file +]; + diff --git a/Config/KeyFrameEngine/standUpFrontNao.kfm b/Config/KeyFrameEngine/standUpFrontNaoFastOld.kfm similarity index 63% rename from Config/KeyFrameEngine/standUpFrontNao.kfm rename to Config/KeyFrameEngine/standUpFrontNaoFastOld.kfm index d930bbbe..2374e692 100644 --- a/Config/KeyFrameEngine/standUpFrontNao.kfm +++ b/Config/KeyFrameEngine/standUpFrontNaoFastOld.kfm @@ -1,25 +1,31 @@ -keyFrameID = standUpFrontNao; -YstabilizationP = 0.1; +keyFrameID = standUpFrontNaoFastOld; +initialKeyFrame = true; +nextKeyFrameID = stand; +YstabilizationP = -0.05; YstabilizationI = 0; -YstabilizationD = -0.02; +YstabilizationD = -0.6; XstabilizationP = 0; XstabilizationI = 0; XstabilizationD = 0; keyFrames = [ { // stand but head back since we are lying on front + // 0,-30deg, 90deg,11deg,0,90deg,-90deg,0, 90deg,-11deg,0,-90deg,90deg,0, 0,0,-21deg,49deg,-27.5deg,0, 0,0,-21deg,49deg,-27.5deg,0 headAngles = [0,-30deg]; armsAngles = [90deg,11deg,0,90deg,-90deg,0, 90deg,-11deg,0,-90deg,90deg,0]; legsAngles = [0,0,-21deg,49deg,-27.5deg,0, 0,0,-21deg,49deg,-27.5deg,0]; stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; duration = 200; intType = linear; - angleAtKeyFrameTarget = 90deg; + angleAtKeyFrameTarget = 92deg; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // pull legs in and arms to side headAngles = [-4deg,-47deg]; @@ -28,13 +34,16 @@ keyFrames = [ stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; duration = 200; intType = linear; - angleAtKeyFrameTarget = 100deg; + angleAtKeyFrameTarget = 110deg; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // use arms and legs for push-up? headAngles = [-6deg,27deg]; @@ -43,13 +52,16 @@ keyFrames = [ stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; duration = 200; intType = linear; - angleAtKeyFrameTarget = 100deg; + angleAtKeyFrameTarget = 116deg; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // push-up legs? headAngles = [-5deg,30deg]; @@ -58,76 +70,92 @@ keyFrames = [ stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; duration = 250; intType = linear; - angleAtKeyFrameTarget = 100deg; - useAngleAtKeyFrameTarget = false; + angleAtKeyFrameTarget = 120deg; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = true; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // push onto left leg + // 0,22deg, 80deg,30deg,-85deg,-16deg,-90deg,0, 90deg,-50deg,99deg,-8deg,90deg,0, -53.5deg,-1.5deg,-61deg,58.5deg,-14.5deg,-19deg, -53.5deg,-5.5deg,-89.5deg,21deg,32deg,16.5deg headAngles = [0,22deg]; armsAngles = [80deg,30deg,-85deg,-16deg,-90deg,0, 90deg,-50deg,99deg,-8deg,90deg,0]; - legsAngles = [-53.5deg,-1.5deg,-61deg,58.5deg,-14.5deg,-19deg, -53.5deg,-5.5deg,-89.5deg,21deg,32deg,16.5deg]; + legsAngles = [-53.5deg,-1.5deg,-61deg,90deg,-14.5deg,-19deg, -53.5deg,-5.5deg,-89.5deg,35deg,32deg,16.5deg]; stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; duration = 200; intType = linear; - angleAtKeyFrameTarget = -10deg; - useAngleAtKeyFrameTarget = false; + angleAtKeyFrameTarget = 10deg; + angleAtKeyFrameErrorFront = 110deg; + angleAtKeyFrameErrorBack = 110deg; + useAngleAtKeyFrameTarget = true; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, - { // push onto left leg - headAngles = [0,0deg]; + { // push onto left leg, wait for stable + headAngles = [0,22deg]; armsAngles = [116deg,40deg,-85deg,-16deg,-90deg,0, 121deg,-40deg,99deg,-8deg,90deg,0]; legsAngles = [-65deg,12deg,-30deg,121deg,-41deg,-19deg, -65deg,-20deg,-88deg,48deg,53deg,-1deg]; stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; duration = 200; intType = linear; - angleAtKeyFrameTarget = -10deg; - useAngleAtKeyFrameTarget = false; + angleAtKeyFrameTarget = 14deg; + angleAtKeyFrameErrorFront = 80deg; + angleAtKeyFrameErrorBack = 80deg; + useAngleAtKeyFrameTarget = true; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = true; - waitForTorsoTime = 1000; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // lean on left leg - headAngles = [0,-10deg]; + headAngles = [0,22deg]; armsAngles = [100deg,21deg,-85deg,-15deg,-90deg,0, 107deg,-20deg,99deg,-9deg,90deg,0]; legsAngles = [-65deg,4deg,-32deg,121deg,-58deg,10deg, -65deg,-15deg,-18deg,-5deg,53deg,4deg]; stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; duration = 600; intType = sine; - angleAtKeyFrameTarget = -10deg; - useAngleAtKeyFrameTarget = false; + angleAtKeyFrameTarget = 11deg; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = true; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = true; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // pull right leg in - headAngles = [0,-10deg]; + headAngles = [0,22deg]; armsAngles = [90deg,21deg,-85deg,-15deg,-90deg,0, 99deg,0deg,99deg,-9deg,90deg,0]; legsAngles = [-43.5deg,12deg,-55deg,121deg,-56deg,9deg, -43.5deg,13.5deg,-44deg,95deg,-16deg,15deg]; stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; duration = 300; intType = linear; - angleAtKeyFrameTarget = -10deg; - useAngleAtKeyFrameTarget = false; + angleAtKeyFrameTarget = 12deg; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = true; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // to sitting position, wide legs headAngles = [0,22deg]; @@ -136,13 +164,16 @@ keyFrames = [ stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; duration = 300; intType = linear; - angleAtKeyFrameTarget = 0deg; + angleAtKeyFrameTarget = 15deg; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = true; + activateFallDownProtection = true; stabilize = true; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // sitting position, legs parallel headAngles = [0,22deg]; @@ -152,41 +183,32 @@ keyFrames = [ duration = 250; intType = linear; angleAtKeyFrameTarget = 0deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; + angleAtKeyFrameErrorFront = 20deg; + angleAtKeyFrameErrorBack = 20deg; + useAngleAtKeyFrameTarget = true; + activateFallDownProtection = true; + stabilize = true; waitForStable = true; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // stand up copy. was different in mof!! headAngles = [0,20deg]; armsAngles = [90deg,11deg,0,90deg,-90deg,0, 90deg,-11deg,0,-90deg,90deg,0]; legsAngles = [0,0,-21deg,49deg,-27.5deg,0, 0,0,-21deg,49deg,-27.5deg,0]; stiffnesses = [40,40, 40,40,40,40,40,40, 40,40,40,40,40,40, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 500; + duration = 600; intType = sine; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront = 20deg; + angleAtKeyFrameErrorBack = 20deg; useAngleAtKeyFrameTarget = true; + activateFallDownProtection = true; stabilize = true; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; - }, - { // stand up copy for leavingPossible=true - headAngles = [0,20deg]; - armsAngles = [90deg,11deg,0,90deg,-90deg,0, 90deg,-11deg,0,-90deg,90deg,0]; - legsAngles = [0,0,-21deg,49deg,-27.5deg,0, 0,0,-21deg,49deg,-27.5deg,0]; - stiffnesses = [40,40, 40,40,40,40,40,40, 40,40,40,40,40,40, 60,60,60,60,60,60, 60,60,60,60,60,60]; - duration = 100; - intType = linear; - angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = true; - stabilize = true; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = true; + fallDownProtection = none; + holdFallDownProtection = 12; } ]; diff --git a/Config/KeyFrameEngine/standUpFrontNaoMed.kfm b/Config/KeyFrameEngine/standUpFrontNaoMed.kfm deleted file mode 100644 index d6e3b2cf..00000000 --- a/Config/KeyFrameEngine/standUpFrontNaoMed.kfm +++ /dev/null @@ -1,209 +0,0 @@ -keyFrameID = standUpFrontNaoMed; -YstabilizationP = 0.15; -YstabilizationI = 0; -YstabilizationD = -0.03; -XstabilizationP = 0; -XstabilizationI = 0; -XstabilizationD = 0; -keyFrames = [ - { // stand - headAngles = [0,20deg]; - armsAngles = [90deg,11deg,0,90deg,-90deg,0, 90deg,-11deg,0,-90deg,90deg,0]; - legsAngles = [0,0,-21deg,49deg,-27.5deg,0, 0,0,-21deg,49deg,-27.5deg,0]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 200; - intType = linear; - angleAtKeyFrameTarget = 90deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { // pull legs in and arms to side - headAngles = [-4deg,-47deg]; - armsAngles = [80deg,73deg,-19deg,-3deg,-90deg,0, 83deg,-73deg,30deg,1deg,90deg,0]; - legsAngles = [-34deg,-1deg,-79deg,122deg,-69deg,-4deg, -34deg,7deg,-66deg,122deg,-69deg,-1deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 200; - intType = linear; - angleAtKeyFrameTarget = 100deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { // use arms and legs for push-up? - headAngles = [-6deg,27deg]; - armsAngles = [28deg,74deg,-123deg,-4deg,-90deg,0, 22deg,-74deg,121deg,3deg,90deg,0]; - legsAngles = [-32deg,-21deg,-84deg,78deg,-65deg,-10deg, -32deg,10deg,-86deg,76deg,-68deg,7deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 200; - intType = linear; - angleAtKeyFrameTarget = 100deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { // push-up legs? - headAngles = [-5deg,30deg]; - armsAngles = [-47deg,11deg,-81deg,-80deg,-90deg,0, -49deg,-11deg,108deg,79deg,90deg,0]; - legsAngles = [-42deg,-15deg,-92deg,-4deg,12deg,-25deg, -42deg,11deg,-91deg,-6deg,11deg,26deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 300; - intType = linear; - angleAtKeyFrameTarget = 100deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { // push-up with arms and lean back with ankle/knee pitch - headAngles = [-5deg,30deg]; - armsAngles = [25.7deg,11.2deg,9.7deg,-4.0deg,-90deg,0deg, 33.8deg,-8.1deg,2.6deg,3.3deg,90deg,0deg]; - legsAngles = [-54.8deg,-14.1deg,-55.0deg,122.5deg,-69.6deg,-4.7deg, -54.8deg,11.1deg,-56.7deg,121.8deg,-69.4deg,5.7deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 300; - intType = linear; - angleAtKeyFrameTarget = 100deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - - { // lower CoM by folding knee/hip - headAngles = [-5deg,0deg]; - armsAngles = [40.0deg,-7.8deg,9.1deg,-4.0deg,-90deg,0deg, 40.3deg,-7.1deg,3.1deg,5.0deg,90deg,0deg]; - legsAngles = [-54.2deg,-19.8deg,-89.2deg,122.5deg,-45.2deg,1.7deg, -54.2deg,18.3deg,-87.4deg,121.7deg,-46.6deg,-0.1deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 500; - intType = linear; - angleAtKeyFrameTarget = 100deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - - { // pull upper body back with ankle pitch, also arms back - headAngles = [-5deg,0deg]; - armsAngles = [55.2deg,18.6deg,9.1deg,-3.9deg,-90deg,0deg, 55.2deg,-15.1deg,3.1deg,3.9deg,90deg,0deg]; - legsAngles = [-59.1deg,-19.4deg,-88.4deg,122.6deg,-14.8deg,14.5deg, -59.1deg,18.3deg,-86.0deg,121.8deg,-15.1deg,-11.7deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 500; - intType = linear; - angleAtKeyFrameTarget = 100deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { // pull upper body back with ankle pitch, also arms back - headAngles = [-5deg,0deg]; - armsAngles = [56.8deg,18.5deg,9.1deg,-3.2deg,-90deg,0deg, 56.1deg,15.6deg,3.2deg,3.2deg,90deg,0deg]; - legsAngles = [-60.1deg,-12.9deg,-52.6deg,122.6deg,-36.5deg,19.6deg, -60.1deg,13.2deg,-47.6deg,121.7deg,-38.8deg,-16.3deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 500; - intType = linear; - angleAtKeyFrameTarget = 100deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - - { // pull upper body back with ankle pitch, also arms back - headAngles = [-5deg,0deg]; - armsAngles = [62.0deg,18.8deg,9.1deg,-3.2deg,-90deg,0deg, 62.4deg,-18.8deg,3.2deg,3.4deg,90deg,0deg]; - legsAngles = [-51.3deg,-1.6deg,-46.9deg,122.7deg,-44.6deg,10.5deg, -51.3deg,2.5deg,-24.9deg,121.8deg,-64.2deg,-7.2deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 500; - intType = linear; - angleAtKeyFrameTarget = 100deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { // pull upper body back with ankle pitch, also arms back - headAngles = [-5deg,0deg]; - armsAngles = [67.0deg,18.7deg,9.1deg,-3.5deg,-90deg,0deg, 67.6deg,-18.1deg,3.2deg,3.1deg,90deg,0deg]; - legsAngles = [-22.0deg,-0.4deg,-42.2deg,120.4deg,-63.5deg,-7.4deg, -22.0deg,-0.3deg,-41.5deg,121.9deg,-69.4deg,-6.0deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 500; - intType = linear; - angleAtKeyFrameTarget = 100deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { // sitting position, legs parallel - headAngles = [0,22deg]; - armsAngles = [69.4deg,4.0deg,8.9deg,-4.0deg,-90deg,0deg, 69.1deg,-4.8deg,3.3deg,3.1deg,90deg,0deg]; - legsAngles = [0.2deg,-5.3deg,-49.2deg,122.7deg,-69.7deg,4.1deg, 0.2deg,3.1deg,-49.1deg,121.9deg,-69.5deg,-4.7deg]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 500; - intType = linear; - angleAtKeyFrameTarget = 0deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { // stand up copy. was different in mof!! - headAngles = [0,30deg]; - armsAngles = [90deg,30deg,0,0deg,-90deg,0, 90deg,-30deg,0,0deg,90deg,0]; - //legsAngles = [0,0,-45deg,71deg,-35deg,0, 0,0,-45deg,71deg,-35deg,0]; - legsAngles = [0,0,-21deg,49deg,-27.5deg,0, 0,0,-21deg,49deg,-27.5deg,0]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 1000; - intType = sine; - angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = true; - stabilize = true; - hipYawErrorCompensation = 0; - waitForStable = true; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { // stand up copy for leavingPossible=true - headAngles = [0,30deg]; - armsAngles = [90deg,30deg,0,0deg,-90deg,0, 90deg,-30deg,0,0deg,90deg,0]; - //legsAngles = [0,0,-45deg,71deg,-35deg,0, 0,0,-45deg,71deg,-35deg,0]; - legsAngles = [0,0,-21deg,49deg,-27.5deg,0, 0,0,-21deg,49deg,-27.5deg,0]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 100; - intType = linear; - angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = true; - stabilize = true; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = true; - } -]; \ No newline at end of file diff --git a/Config/KeyFrameEngine/standUpFrontNaoSlow.kfm b/Config/KeyFrameEngine/standUpFrontNaoSlowOld.kfm similarity index 71% rename from Config/KeyFrameEngine/standUpFrontNaoSlow.kfm rename to Config/KeyFrameEngine/standUpFrontNaoSlowOld.kfm index 5ab095d3..d94603b5 100644 --- a/Config/KeyFrameEngine/standUpFrontNaoSlow.kfm +++ b/Config/KeyFrameEngine/standUpFrontNaoSlowOld.kfm @@ -1,7 +1,9 @@ -keyFrameID = standUpFrontNaoSlow; -YstabilizationP = 0.15; +keyFrameID = standUpFrontNaoSlowOld; +initialKeyFrame = true; +nextKeyFrameID = stand; +YstabilizationP = -0.05; YstabilizationI = 0; -YstabilizationD = -0.03; +YstabilizationD = -0.6; XstabilizationP = 0; XstabilizationI = 0; XstabilizationD = 0; @@ -14,12 +16,15 @@ keyFrames = [ duration = 200; intType = linear; angleAtKeyFrameTarget = 90deg; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // pull legs in and arms to side headAngles = [-4deg,-47deg]; @@ -29,12 +34,15 @@ keyFrames = [ duration = 200; intType = linear; angleAtKeyFrameTarget = 100deg; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // use arms and legs for push-up? headAngles = [-6deg,27deg]; @@ -44,12 +52,15 @@ keyFrames = [ duration = 200; intType = linear; angleAtKeyFrameTarget = 100deg; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // push-up legs? headAngles = [-5deg,30deg]; @@ -58,13 +69,16 @@ keyFrames = [ stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; duration = 300; intType = linear; - angleAtKeyFrameTarget = 100deg; - useAngleAtKeyFrameTarget = false; + angleAtKeyFrameTarget = 120deg; + angleAtKeyFrameErrorFront = 40deg; + angleAtKeyFrameErrorBack = 40deg; + useAngleAtKeyFrameTarget = true; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // push-up with arms and lean back with ankle/knee pitch headAngles = [-5deg,30deg]; @@ -73,13 +87,16 @@ keyFrames = [ stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; duration = 300; intType = linear; - angleAtKeyFrameTarget = 100deg; - useAngleAtKeyFrameTarget = false; + angleAtKeyFrameTarget = 66deg; + angleAtKeyFrameErrorFront = 40deg; + angleAtKeyFrameErrorBack = 40deg; + useAngleAtKeyFrameTarget = true; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // lower CoM by folding knee/hip @@ -89,13 +106,16 @@ keyFrames = [ stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; duration = 1000; intType = linear; - angleAtKeyFrameTarget = 100deg; - useAngleAtKeyFrameTarget = false; + angleAtKeyFrameTarget = 63deg; + angleAtKeyFrameErrorFront = 40deg; + angleAtKeyFrameErrorBack = 40deg; + useAngleAtKeyFrameTarget = true; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // pull upper body back with ankle pitch, also arms back @@ -105,13 +125,16 @@ keyFrames = [ stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; duration = 500; intType = linear; - angleAtKeyFrameTarget = 100deg; - useAngleAtKeyFrameTarget = false; + angleAtKeyFrameTarget = 31deg; + angleAtKeyFrameErrorFront = 40deg; + angleAtKeyFrameErrorBack = 40deg; + useAngleAtKeyFrameTarget = true; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // pull upper body back with ankle pitch, also arms back headAngles = [-5deg,0deg]; @@ -120,13 +143,16 @@ keyFrames = [ stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; duration = 500; intType = linear; - angleAtKeyFrameTarget = 100deg; - useAngleAtKeyFrameTarget = false; + angleAtKeyFrameTarget = 11deg; + angleAtKeyFrameErrorFront = 40deg; + angleAtKeyFrameErrorBack = 40deg; + useAngleAtKeyFrameTarget = true; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // pull upper body back with ankle pitch, also arms back @@ -136,13 +162,16 @@ keyFrames = [ stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; duration = 500; intType = linear; - angleAtKeyFrameTarget = 100deg; - useAngleAtKeyFrameTarget = false; + angleAtKeyFrameTarget = 8deg; + angleAtKeyFrameErrorFront = 40deg; + angleAtKeyFrameErrorBack = 40deg; + useAngleAtKeyFrameTarget = true; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // pull upper body back with ankle pitch, also arms back headAngles = [-5deg,0deg]; @@ -151,13 +180,16 @@ keyFrames = [ stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; duration = 500; intType = linear; - angleAtKeyFrameTarget = 100deg; - useAngleAtKeyFrameTarget = false; + angleAtKeyFrameTarget = 7deg; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = true; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // sitting position, legs parallel headAngles = [0,22deg]; @@ -167,12 +199,15 @@ keyFrames = [ duration = 500; intType = linear; angleAtKeyFrameTarget = 0deg; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = true; + activateFallDownProtection = true; + stabilize = true; + waitForStable = true; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // stand up copy. was different in mof!! headAngles = [0,30deg]; @@ -183,27 +218,14 @@ keyFrames = [ duration = 1000; intType = sine; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront = 20deg; + angleAtKeyFrameErrorBack = 20deg; useAngleAtKeyFrameTarget = true; + activateFallDownProtection = true; stabilize = true; - hipYawErrorCompensation = 0; waitForStable = true; - waitForTorsoTime = 0; leavingPossible = false; - }, - { // stand up copy for leavingPossible=true - headAngles = [0,30deg]; - armsAngles = [90deg,30deg,0,0deg,-90deg,0, 90deg,-30deg,0,0deg,90deg,0]; - //legsAngles = [0,0,-45deg,71deg,-35deg,0, 0,0,-45deg,71deg,-35deg,0]; - legsAngles = [0,0,-21deg,49deg,-27.5deg,0, 0,0,-21deg,49deg,-27.5deg,0]; - stiffnesses = [40,40, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 100; - intType = linear; - angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = true; - stabilize = true; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = true; + fallDownProtection = none; + holdFallDownProtection = 12; } -]; \ No newline at end of file +]; diff --git a/Config/KeyFrameEngine/standUpSideNao.kfm b/Config/KeyFrameEngine/standUpSideNao.kfm index 0c61f8fc..422de819 100644 --- a/Config/KeyFrameEngine/standUpSideNao.kfm +++ b/Config/KeyFrameEngine/standUpSideNao.kfm @@ -1,4 +1,6 @@ keyFrameID = standUpSideNao; +initialKeyFrame = true; +nextKeyFrameID = none; YstabilizationP = 0.1; YstabilizationI = 0; YstabilizationD = -0.02; @@ -6,66 +8,104 @@ XstabilizationP = 0; XstabilizationI = 0; XstabilizationD = 0; keyFrames = [ - {// move legs diagonal to fall to front or back - headAngles = [20000,20000]; + {// safe sit + // 0,0, 90deg,11deg,0,90deg,-90deg,0, 90deg,-11deg,0,-90deg,90deg,0, 0deg,0deg,-90deg,110deg,-45deg,0deg, 0deg,0deg,-90deg,110deg,-45deg,0deg + headAngles = [0,0]; armsAngles = [90deg,11deg,0,90deg,-90deg,0, 90deg,-11deg,0,-90deg,90deg,0]; - legsAngles = [0deg,0deg,10deg,50deg,-60deg,44deg,0deg,0deg,-30deg,20deg,30deg,0deg]; - stiffnesses = [0,0, 0,0,0,90,0,0, 0,0,0,90,0,0, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 300; + // HipYawPitch, HipRoll, HipPitch, KneePitch, AnklePitch, AnkleRoll + legsAngles = [0deg,0deg,-90deg,110deg,-45deg,0deg, 0deg,0deg,-90deg,110deg,-45deg,0deg]; + stiffnesses = [0,0, 40,40,40,40,40,40, 40,40,40,40,40,40, 60,60,60,60,60,60, 60,60,60,60,60,60]; + duration = 200; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = true; + fallDownProtection = none; + holdFallDownProtection = 12; }, - { - headAngles = [20000,20000]; - armsAngles = [90deg,11deg,0,90deg,-90deg,0, 90deg,-11deg,0,-90deg,90deg,0]; - legsAngles = [0deg,27deg,10deg,50deg,-60deg,44deg,0deg,0deg,-30deg,20deg,30deg,0deg]; - stiffnesses = [0,0, 0,0,0,90,0,0, 0,0,0,90,0,0, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 400; - intType = sine; + {// move upper leg back + // 0,0, 90deg,11deg,0,90deg,-90deg,0, 90deg,-11deg,0,-90deg,90deg,0, 0deg,0deg,-90deg,110deg,-45deg,0deg, 0deg,0deg,0deg,60deg,40deg,0deg + headAngles = [0,0]; + armsAngles = [90deg,11deg,0,90deg,-90deg,0, 90deg,-11deg,0,-90deg,90deg,0]; + // HipYawPitch, HipRoll, HipPitch, KneePitch, AnklePitch, AnkleRoll + legsAngles = [0deg,0deg,-90deg,110deg,-45deg,0deg, 0deg,0deg,0deg,60deg,40deg,0deg]; + stiffnesses = [0,0, 40,40,40,40,40,40, 40,40,40,40,40,40, 60,60,60,60,60,60, 60,60,60,60,60,60]; + duration = 300; + intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = true; + leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, - { - headAngles = [20000,20000]; - armsAngles = [90deg,11deg,0,90deg,-90deg,0, 90deg,-11deg,0,-90deg,90deg,0]; - legsAngles = [0deg,27deg,-50deg,20deg,0deg,44deg,0deg,0deg,0deg,0deg,0deg,0deg]; - stiffnesses = [0,0, 60,0,0,90,0,0, 60,0,0,90,0,0, 100,100,100,100,100,100, 100,100,100,100,100,100]; + { // push knee to ground + headAngles = [-0.5deg,20.3deg]; + //ShoulderPitch, ShoulderRoll, ElbowYaw, ElbowRoll, WristYaw, Hand + armsAngles = [90deg,11deg,0,90deg,-90deg,0, 90deg,-11deg,0,-90deg,90deg,0]; + // HipYawPitch, HipRoll, HipPitch, KneePitch, AnklePitch, AnkleRoll + legsAngles = [-30deg,10deg,-88deg,110deg,-60deg,0deg, -30deg,-30deg,10deg,0deg,50deg,22deg]; + stiffnesses = [0,0, 40,40,40,40,40,40, 40,40,40,40,40,40, 60,60,60,60,60,60, 60,60,60,60,60,60]; duration = 400; - intType = sine; + intType = linear; + angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; + stabilize = false; + waitForStable = false; + leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; + }, + { // close legs + headAngles = [0deg,0deg]; + //ShoulderPitch, ShoulderRoll, ElbowYaw, ElbowRoll, WristYaw, Hand + armsAngles = [90deg,11deg,0,90deg,-90deg,0, 90deg,-11deg,0,-90deg,90deg,0]; + // HipYawPitch, HipRoll, HipPitch, KneePitch, AnklePitch, AnkleRoll + legsAngles = [-10deg,0deg,-50deg,80deg,-45deg,0deg, -10deg,-30deg,0deg,0deg,50deg,22deg]; + stiffnesses = [0,0, 40,40,40,40,40,40, 40,40,40,40,40,40, 30,30,30,30,30,30, 30,30,30,30,30,30]; + duration = 300; + intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { - // similar to stand up - + // similar to stand headAngles = [0,20deg]; armsAngles = [90deg,11deg,0,90deg,-90deg,0, 90deg,-11deg,0,-90deg,90deg,0]; legsAngles = [0,0,-21deg,49deg,-27.5deg,0, 0,0,-21deg,49deg,-27.5deg,0]; - stiffnesses = [100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100, 100,100,100,100,100,100]; + stiffnesses = [40,40, 60,60,60,60,60,60, 60,60,60,60,60,60, 50,50,50,50,50,50, 50,50,50,50,50,50]; duration = 200; intType = sine; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = true; + fallDownProtection = none; + holdFallDownProtection = 12; } ]; diff --git a/Config/KeyFrameEngine/standUpSideNaoGoalie.kfm b/Config/KeyFrameEngine/standUpSideNaoGoalie.kfm new file mode 100644 index 00000000..fe61c927 --- /dev/null +++ b/Config/KeyFrameEngine/standUpSideNaoGoalie.kfm @@ -0,0 +1,76 @@ +keyFrameID = standUpSideNaoGoalie; +initialKeyFrame = true; +nextKeyFrameID = none; +YstabilizationP = 0.1; +YstabilizationI = 0; +YstabilizationD = -0.02; +XstabilizationP = 0; +XstabilizationI = 0; +XstabilizationD = 0; +keyFrames = [ + // roll on back for stand up + { + headAngles = [-0.5deg,20.3deg]; + //ShoulderPitch, ShoulderRoll, ElbowYaw, ElbowRoll, WristYaw, Hand + armsAngles = [-90.0deg,14.0deg,9.9deg,-0.9deg,-90deg,0deg, 31.8deg,8.9deg,-1.1deg,68.2deg,90deg,0deg]; //pose4 + // HipYawPitch, HipRoll, HipPitch, KneePitch, AnklePitch, AnkleRoll + legsAngles = [-30deg,10deg,-88deg,120deg,-60deg,0deg, -30deg,-30deg,10deg,0deg,50deg,22deg]; + stiffnesses = [40,40, 70,70,70,70,70,70, 30,30,30,30,30,30, 70,70,70,70,70,70, 70,70,70,70,70,70]; + duration = 300; + intType = linear; + angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; + stabilize = false; + waitForStable = false; + leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; + }, + // straighten legs + { + headAngles = [-0.5deg,20.3deg]; + //ShoulderPitch, ShoulderRoll, ElbowYaw, ElbowRoll, WristYaw, Hand + armsAngles = [-15deg,0deg,0deg,0deg,-90.0deg,0deg, 51deg,-9.8deg,0deg,-90deg,90.0deg,0deg]; + // HipYawPitch, HipRoll, HipPitch, KneePitch, AnklePitch, AnkleRoll + legsAngles = [0deg,0.0deg,0deg,0deg,0deg,0.1deg, 0deg,0deg,-0deg,0deg,0deg,0.0deg]; + stiffnesses = [20,20, 70,70,70,70,70,70, 30,30,30,30,30,30, 60,60,60,60,60,60, 60,60,60,60,60,60]; + duration = 200; + intType = linear; + angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; + stabilize = false; + waitForStable = false; + leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; + }, + // sort arms for stand up + { + headAngles = [-0.5deg,20.3deg]; + //ShoulderPitch, ShoulderRoll, ElbowYaw, ElbowRoll, WristYaw, Hand + armsAngles = [90.5deg,9.8deg,0deg,90deg,-90.0deg,0deg, 90.5deg,-9.8deg,0deg,-90deg,90.0deg,0deg]; + // HipYawPitch, HipRoll, HipPitch, KneePitch, AnklePitch, AnkleRoll + legsAngles = [0deg,0.0deg,0deg,0deg,0deg,0.1deg, 0deg,0deg,-0deg,0deg,0deg,0.0deg]; + stiffnesses = [20,20, 70,70,70,70,70,70, 70,70,70,70,70,70, 100,100,100,100,100,100, 100,100,100,100,100,100]; + duration = 200; + intType = linear; + angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; + stabilize = false; + waitForStable = false; + leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; + } +]; + + diff --git a/Config/KeyFrameEngine/test.kfm b/Config/KeyFrameEngine/test.kfm new file mode 100644 index 00000000..3c05a5a1 --- /dev/null +++ b/Config/KeyFrameEngine/test.kfm @@ -0,0 +1,29 @@ +keyFrameID = test; +initialKeyFrame = true; +nextKeyFrameID = none; +YstabilizationP = -0.05; +YstabilizationI = 0; +YstabilizationD = -0.6; +XstabilizationP = 0; +XstabilizationI = 0; +XstabilizationD = 0; +keyFrames = [ + { + headAngles = [0,0]; + armsAngles = [90deg,11deg,0,0deg,-90deg,0, 90deg,-11deg,0,0deg,90deg,0]; + legsAngles = [0,0,-21deg,49deg,-27.5deg,0, 0,0,-21deg,49deg,-27.5deg,0]; + stiffnesses = [40,40, 40,40,40,40,40,40, 40,40,40,40,40,40, 30,30,30,30,30,30, 30,30,30,30,30,30]; + duration = 2000; + intType = sine; + angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; + stabilize = false; + waitForStable = false; + leavingPossible = true; + fallDownProtection = none; + holdFallDownProtection = 12; + } +]; diff --git a/Config/KeyFrameEngine/testUnstiff.kfm b/Config/KeyFrameEngine/testUnstiff.kfm new file mode 100644 index 00000000..2668754f --- /dev/null +++ b/Config/KeyFrameEngine/testUnstiff.kfm @@ -0,0 +1,29 @@ +keyFrameID = testUnstiff; +initialKeyFrame = true; +nextKeyFrameID = none; +YstabilizationP = 0.1; +YstabilizationI = 0; +YstabilizationD = -0.02; +XstabilizationP = 0; +XstabilizationI = 0; +XstabilizationD = 0; +keyFrames = [ + { + headAngles = [10000,10000]; + armsAngles = [10000,10000,10000,10000,10000,10000, 10000,10000,10000,10000,10000,10000]; + legsAngles = [10000,10000,10000,10000,10000,10000, 10000,10000,10000,10000,10000,10000]; + stiffnesses = [0,0, 0,0,0,0,0,0, 0,0,0,0,0,0, 0,0,0,0,0,0, 0,0,0,0,0,0]; + duration = 0; + intType = linear; + angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; + useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; + stabilize = false; + waitForStable = false; + leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; + } +]; diff --git a/Config/KeyFrameEngine/wave_left.kfm b/Config/KeyFrameEngine/wave_left.kfm index 9c4b59b6..0a53ee5e 100644 --- a/Config/KeyFrameEngine/wave_left.kfm +++ b/Config/KeyFrameEngine/wave_left.kfm @@ -1,5 +1,7 @@ //copy from stand keyFrameID = wave_left; +initialKeyFrame = true; +nextKeyFrameID = none; YstabilizationP = 0.1; YstabilizationI = 0; YstabilizationD = -0.02; @@ -15,12 +17,15 @@ keyFrames = [ duration = 400; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { headAngles = [20000,20000]; @@ -30,12 +35,15 @@ keyFrames = [ duration = 1000; intType = sine; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { headAngles = [20000,20000]; @@ -45,12 +53,15 @@ keyFrames = [ duration = 500; intType = sine; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { headAngles = [20000,20000]; @@ -60,12 +71,15 @@ keyFrames = [ duration = 500; intType = sine; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { headAngles = [20000,20000]; @@ -75,12 +89,15 @@ keyFrames = [ duration = 500; intType = sine; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { headAngles = [20000,20000]; @@ -90,12 +107,15 @@ keyFrames = [ duration = 500; intType = sine; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { headAngles = [20000,20000]; @@ -105,12 +125,15 @@ keyFrames = [ duration = 500; intType = sine; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { headAngles = [20000,20000]; @@ -120,12 +143,15 @@ keyFrames = [ duration = 500; intType = sine; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { headAngles = [20000,20000]; @@ -135,12 +161,15 @@ keyFrames = [ duration = 500; intType = sine; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { headAngles = [20000,20000]; @@ -150,12 +179,15 @@ keyFrames = [ duration = 500; intType = sine; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { headAngles = [20000,20000]; @@ -165,12 +197,15 @@ keyFrames = [ duration = 500; intType = sine; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { headAngles = [20000,20000]; @@ -180,12 +215,15 @@ keyFrames = [ duration = 500; intType = sine; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { headAngles = [20000,20000]; @@ -195,12 +233,15 @@ keyFrames = [ duration = 1000; intType = sine; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = true; + fallDownProtection = none; + holdFallDownProtection = 12; } ]; diff --git a/Config/KeyFrameEngine/wave_right.kfm b/Config/KeyFrameEngine/wave_right.kfm deleted file mode 100644 index ca85d89a..00000000 --- a/Config/KeyFrameEngine/wave_right.kfm +++ /dev/null @@ -1,191 +0,0 @@ -//copy from stand -keyFrameID = wave_right; -YstabilizationP = 0.1; -YstabilizationI = 0; -YstabilizationD = -0.02; -XstabilizationP = 0; -XstabilizationI = 0; -XstabilizationD = 0; -keyFrames = [ - { - headAngles = [20000,20000]; - armsAngles = [20000,20000,20000,20000,20000,20000, 20000,20000,20000,20000,20000,20000]; - legsAngles = [20000,20000,20000,20000,20000,20000, 20000,20000,20000,20000,20000,20000]; - stiffnesses = [40,40, 40,40,40,40,40,40, 40,40,40,40,40,40, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 400; - intType = linear; - angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { - headAngles = [20000,20000]; - armsAngles = [90deg,30deg,0,0,-90deg,0,-40deg,-50deg,55deg,0,0,-60deg]; - legsAngles = [0,0,-21deg,49deg,-27.5deg,0,0,0,-21deg,49deg,-27.5deg,0]; - stiffnesses = [40,40, 40,40,40,40,40,40, 40,40,40,40,40,40, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 1000; - intType = sine; - angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { - headAngles = [20000,20000]; - armsAngles = [90deg,30deg,0,0,-90deg,0,-40deg,-50deg,55deg,95deg,0,60deg]; - legsAngles = [0,0,-21deg,49deg,-27.5deg,0,0,0,-21deg,49deg,-27.5deg,0]; - stiffnesses = [40,40, 40,40,40,40,40,40, 40,40,40,40,40,40, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 500; - intType = sine; - angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { - headAngles = [20000,20000]; - armsAngles = [90deg,30deg,0,0,-90deg,0,-40deg,-50deg,55deg,0,0,-60deg]; - legsAngles = [0,0,-21deg,49deg,-27.5deg,0,0,0,-21deg,49deg,-27.5deg,0]; - stiffnesses = [40,40, 40,40,40,40,40,40, 40,40,40,40,40,40, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 1000; - intType = sine; - angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { - headAngles = [20000,20000]; - armsAngles = [90deg,30deg,0,0,-90deg,0,-40deg,-50deg,55deg,95deg,0,60deg]; - legsAngles = [0,0,-21deg,49deg,-27.5deg,0,0,0,-21deg,49deg,-27.5deg,0]; - stiffnesses = [40,40, 40,40,40,40,40,40, 40,40,40,40,40,40, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 500; - intType = sine; - angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { - headAngles = [20000,20000]; - armsAngles = [90deg,30deg,0,0,-90deg,0,-40deg,-50deg,55deg,0,0,-60deg]; - legsAngles = [0,0,-21deg,49deg,-27.5deg,0,0,0,-21deg,49deg,-27.5deg,0]; - stiffnesses = [40,40, 40,40,40,40,40,40, 40,40,40,40,40,40, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 1000; - intType = sine; - angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { - headAngles = [20000,20000]; - armsAngles = [90deg,30deg,0,0,-90deg,0,-40deg,-50deg,55deg,95deg,0,60deg]; - legsAngles = [0,0,-21deg,49deg,-27.5deg,0,0,0,-21deg,49deg,-27.5deg,0]; - stiffnesses = [40,40, 40,40,40,40,40,40, 40,40,40,40,40,40, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 500; - intType = sine; - angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { - headAngles = [20000,20000]; - armsAngles = [90deg,30deg,0,0,-90deg,0,-40deg,-50deg,55deg,0,0,-60deg]; - legsAngles = [0,0,-21deg,49deg,-27.5deg,0,0,0,-21deg,49deg,-27.5deg,0]; - stiffnesses = [40,40, 40,40,40,40,40,40, 40,40,40,40,40,40, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 1000; - intType = sine; - angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { - headAngles = [20000,20000]; - armsAngles = [90deg,30deg,0,0,-90deg,0,-40deg,-50deg,55deg,95deg,0,60deg]; - legsAngles = [0,0,-21deg,49deg,-27.5deg,0,0,0,-21deg,49deg,-27.5deg,0]; - stiffnesses = [40,40, 40,40,40,40,40,40, 40,40,40,40,40,40, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 500; - intType = sine; - angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { - headAngles = [20000,20000]; - armsAngles = [90deg,30deg,0,0,-90deg,0,-40deg,-50deg,55deg,0,0,-60deg]; - legsAngles = [0,0,-21deg,49deg,-27.5deg,0,0,0,-21deg,49deg,-27.5deg,0]; - stiffnesses = [40,40, 40,40,40,40,40,40, 40,40,40,40,40,40, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 1000; - intType = sine; - angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { - headAngles = [20000,20000]; - armsAngles = [90deg,30deg,0,0,-90deg,0,-40deg,-50deg,55deg,95deg,0,60deg]; - legsAngles = [0,0,-21deg,49deg,-27.5deg,0,0,0,-21deg,49deg,-27.5deg,0]; - stiffnesses = [40,40, 40,40,40,40,40,40, 40,40,40,40,40,40, 100,100,100,100,100,100, 100,100,100,100,100,100]; - duration = 500; - intType = sine; - angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = false; - }, - { - headAngles = [20000,20000]; - armsAngles = [90deg,11deg,0,90deg,-90deg,0, 90deg,-11deg,0,-90deg,90deg,0]; - legsAngles = [0,0,-21deg,49deg,-27.5deg,0, 0,0,-21deg,49deg,-27.5deg,0]; - stiffnesses = [40,40, 40,40,40,40,40,40, 40,40,40,40,40,40, 60,60,60,60,60,60, 60,60,60,60,60,60]; - duration = 1000; - intType = sine; - angleAtKeyFrameTarget = 0; - useAngleAtKeyFrameTarget = false; - stabilize = false; - hipYawErrorCompensation = 0; - waitForStable = false; - waitForTorsoTime = 0; - leavingPossible = true; - } -]; - diff --git a/Config/KeyFrameEngine/wideStanceWithStandUp.kfm b/Config/KeyFrameEngine/wideStanceWithStandUp.kfm index d21417e8..1adcea2d 100644 --- a/Config/KeyFrameEngine/wideStanceWithStandUp.kfm +++ b/Config/KeyFrameEngine/wideStanceWithStandUp.kfm @@ -1,4 +1,6 @@ keyFrameID = wideStanceWithStandUp; +initialKeyFrame = true; +nextKeyFrameID = none; YstabilizationP = 0.1; YstabilizationI = 0; YstabilizationD = -0.02; @@ -14,12 +16,15 @@ keyFrames = [ duration = 200; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // endposition headAngles = [0,30deg]; @@ -29,12 +34,15 @@ keyFrames = [ duration = 150; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // hold endposition for 1 sec; todo: leave this if robot is not stable! headAngles = [0,30deg]; @@ -44,12 +52,15 @@ keyFrames = [ duration = 1000; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // begin stand up headAngles = [0,30deg]; @@ -59,12 +70,15 @@ keyFrames = [ duration = 500; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // pull legs in headAngles = [0,30deg]; @@ -74,12 +88,15 @@ keyFrames = [ duration = 300; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // lean on left leg headAngles = [0,30deg]; @@ -89,12 +106,15 @@ keyFrames = [ duration = 500; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // lean on left leg copy, todo: use waitForStable instead headAngles = [0,30deg]; @@ -104,12 +124,15 @@ keyFrames = [ duration = 100; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // lean on left leg #2, stretch right leg headAngles = [0,30deg]; @@ -119,12 +142,15 @@ keyFrames = [ duration = 500; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // pull right leg in, legs together a little headAngles = [0,30deg]; @@ -134,12 +160,15 @@ keyFrames = [ duration = 500; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // pull right leg in, legs together a little headAngles = [0,30deg]; @@ -149,12 +178,15 @@ keyFrames = [ duration = 550; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // legs together, sitting pose headAngles = [0,30deg]; @@ -164,12 +196,15 @@ keyFrames = [ duration = 350; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // wait for 250 ms, todo: replace with waitForStable headAngles = [0,30deg]; @@ -179,12 +214,15 @@ keyFrames = [ duration = 350; intType = linear; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = false; + activateFallDownProtection = false; stabilize = false; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; }, { // stand up copy. was different in mof!! headAngles = [0,20deg]; @@ -194,11 +232,14 @@ keyFrames = [ duration = 600; intType = sine; angleAtKeyFrameTarget = 0; + angleAtKeyFrameErrorFront =30deg; + angleAtKeyFrameErrorBack=30deg; useAngleAtKeyFrameTarget = true; + activateFallDownProtection = false; stabilize = true; - hipYawErrorCompensation = 0; waitForStable = false; - waitForTorsoTime = 0; leavingPossible = false; + fallDownProtection = none; + holdFallDownProtection = 12; } ]; diff --git a/Config/KickEngine/kickInner.kmc b/Config/KickEngine/kickInner.kmc deleted file mode 100644 index 9de437f3..00000000 --- a/Config/KickEngine/kickInner.kmc +++ /dev/null @@ -1,560 +0,0 @@ -footOrigin = { - x = 0; - y = 55; - z = -225; -}; -footRotOrigin = { - x = 0; - y = 0; - z = 0; -}; -armOrigin = { - x = 0; - y = 130; - z = 80; -}; -handRotOrigin = { - x = 0; - y = 0; - z = 0; -}; -comOrigin = { - x = 10; - y = 0; -}; -headOrigin = { - x = 0; - y = 0; -}; -kpx = 1; -kix = 0; -kdx = 0; -kpy = 1; -kiy = 0; -kdy = 0.005; -preview = 5; -loop = true; -autoComTra = true; -ignoreHead = true; -phaseParameters = [ - { - duration = 1000; - leftFootTra1 = { - x = 0; - y = 100; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = 100; - z = -225; - }; - leftFootRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftFootRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightFootTra1 = { - x = 0; - y = -10; - z = -225; - }; - rightFootTra2 = { - x = 0; - y = -10; - z = -225; - }; - rightFootRot1 = { - x = -0.1; - y = 0; - z = -0; - }; - rightFootRot2 = { - x = -0.1; - y = 0; - z = -0; - }; - leftArmTra1 = { - x = 0; - y = 180; - z = 80; - }; - leftArmTra2 = { - x = 0; - y = 180; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 0; - y = -130; - z = 80; - }; - rightArmTra2 = { - x = 0; - y = -130; - z = 80; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 500; - leftFootTra1 = { - x = -110; - y = 90; - z = -145; - }; - leftFootTra2 = { - x = -110; - y = 90; - z = -145; - }; - leftFootRot1 = { - x = -0.1; - y = 0; - z = 0; - }; - leftFootRot2 = { - x = -0.1; - y = 0; - z = 0; - }; - rightFootTra1 = { - x = 0; - y = -10; - z = -225; - }; - rightFootTra2 = { - x = 0; - y = -10; - z = -225; - }; - rightFootRot1 = { - x = -0.1; - y = 0; - z = 0; - }; - rightFootRot2 = { - x = -0.1; - y = 0; - z = -0; - }; - leftArmTra1 = { - x = 100; - y = 200; - z = 80; - }; - leftArmTra2 = { - x = 100; - y = 200; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 0; - y = -130; - z = 80; - }; - rightArmTra2 = { - x = 0; - y = -130; - z = 80; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 150; - leftFootTra1 = { - x = 140; - y = 85; - z = -160; - }; - leftFootTra2 = { - x = 140; - y = 85; - z = -160; - }; - leftFootRot1 = { - x = -0.05; - y = 0; - z = 0; - }; - leftFootRot2 = { - x = -0.05; - y = 0; - z = 0; - }; - rightFootTra1 = { - x = 0; - y = -10; - z = -225; - }; - rightFootTra2 = { - x = 0; - y = -10; - z = -225; - }; - rightFootRot1 = { - x = -0.05; - y = 0; - z = -0; - }; - rightFootRot2 = { - x = -0.05; - y = 0; - z = -0; - }; - leftArmTra1 = { - x = -150; - y = 200; - z = 80; - }; - leftArmTra2 = { - x = -150; - y = 200; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 0; - y = -130; - z = 80; - }; - rightArmTra2 = { - x = 0; - y = -130; - z = 80; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 700; - leftFootTra1 = { - x = 0; - y = 120; - z = -210; - }; - leftFootTra2 = { - x = 0; - y = 120; - z = -210; - }; - leftFootRot1 = { - x = -0.1; - y = 0; - z = 0; - }; - leftFootRot2 = { - x = -0.1; - y = 0; - z = 0; - }; - rightFootTra1 = { - x = 0; - y = -10; - z = -225; - }; - rightFootTra2 = { - x = 0; - y = -10; - z = -225; - }; - rightFootRot1 = { - x = -0.1; - y = 0; - z = -0; - }; - rightFootRot2 = { - x = -0.1; - y = 0; - z = -0; - }; - leftArmTra1 = { - x = 0; - y = 180; - z = 80; - }; - leftArmTra2 = { - x = 0; - y = 180; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 0; - y = -130; - z = 80; - }; - rightArmTra2 = { - x = 0; - y = -130; - z = 80; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 1000; - leftFootTra1 = { - x = 0; - y = 55; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = 55; - z = -225; - }; - leftFootRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftFootRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightFootTra1 = { - x = 0; - y = -55; - z = -225; - }; - rightFootTra2 = { - x = 0; - y = -55; - z = -225; - }; - rightFootRot1 = { - x = 0; - y = 0; - z = -0; - }; - rightFootRot2 = { - x = 0; - y = 0; - z = -0; - }; - leftArmTra1 = { - x = 0; - y = 130; - z = 80; - }; - leftArmTra2 = { - x = 0; - y = 130; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 0; - y = -130; - z = 80; - }; - rightArmTra2 = { - x = 0; - y = -130; - z = 80; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - } -]; diff --git a/Config/KickEngine/kickInnerFast.kmc b/Config/KickEngine/kickInnerFast.kmc deleted file mode 100644 index 40387b31..00000000 --- a/Config/KickEngine/kickInnerFast.kmc +++ /dev/null @@ -1,664 +0,0 @@ -footOrigin = { - x = 0; - y = 55; - z = -264; -}; -footRotOrigin = { - x = 0; - y = 0; - z = 0; -}; -armOrigin = { - x = 0; - y = 150; - z = -20; -}; -handRotOrigin = { - x = 0; - y = 0; - z = 0; -}; -comOrigin = { - x = 50; - y = 0; -}; -headOrigin = { - x = 0; - y = 0; -}; -kpx = 0; -kix = 0; -kdx = 0; -kpy = 0; -kiy = 0; -kdy = 0; -preview = 40; -loop = false; -autoComTra = false; -ignoreHead = true; -phaseParameters = [ - { - duration = 210; - leftFootTra1 = { - x = 0; - y = 55; - z = -234; - }; - leftFootTra2 = { - x = 0; - y = 55; - z = -234; - }; - leftFootRot1 = { - x = -0.1; - y = -0; - z = 0; - }; - leftFootRot2 = { - x = -0.1; - y = -0; - z = 0; - }; - rightFootTra1 = { - x = 0; - y = -55; - z = -234; - }; - rightFootTra2 = { - x = 0; - y = -55; - z = -234; - }; - rightFootRot1 = { - x = -0.05; - y = -0; - z = -0; - }; - rightFootRot2 = { - x = -0.05; - y = -0; - z = -0; - }; - leftArmTra1 = { - x = 0; - y = 155; - z = -20; - }; - leftArmTra2 = { - x = 0; - y = 155; - z = -20; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 0; - y = -155; - z = -20; - }; - rightArmTra2 = { - x = 0; - y = -155; - z = -20; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 50; - y = 0; - }; - comTra2 = { - x = 50; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 400; - leftFootTra1 = { - x = 0; - y = 65; - z = -234; - }; - leftFootTra2 = { - x = 0; - y = 65; - z = -234; - }; - leftFootRot1 = { - x = -0.1; - y = -0; - z = 0; - }; - leftFootRot2 = { - x = -0.1; - y = -0; - z = 0; - }; - rightFootTra1 = { - x = 0; - y = -20; - z = -234; - }; - rightFootTra2 = { - x = 0; - y = -20; - z = -234; - }; - rightFootRot1 = { - x = -0.05; - y = -0; - z = -0; - }; - rightFootRot2 = { - x = -0.05; - y = -0; - z = -0; - }; - leftArmTra1 = { - x = 0; - y = 175; - z = -20; - }; - leftArmTra2 = { - x = 0; - y = 175; - z = -20; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = -150; - y = -175; - z = -20; - }; - rightArmTra2 = { - x = -150; - y = -175; - z = -20; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 40; - y = 0; - }; - comTra2 = { - x = 40; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 140; - leftFootTra1 = { - x = -40; - y = 65; - z = -184; - }; - leftFootTra2 = { - x = -40; - y = 65; - z = -184; - }; - leftFootRot1 = { - x = -0.2; - y = 0.3; - z = 0; - }; - leftFootRot2 = { - x = -0.2; - y = 0.3; - z = 0; - }; - rightFootTra1 = { - x = 0; - y = -20; - z = -234; - }; - rightFootTra2 = { - x = 0; - y = -20; - z = -234; - }; - rightFootRot1 = { - x = -0.1; - y = -0; - z = 0; - }; - rightFootRot2 = { - x = -0.1; - y = -0; - z = -0; - }; - leftArmTra1 = { - x = 150; - y = 210; - z = -20; - }; - leftArmTra2 = { - x = 150; - y = 210; - z = -20; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = -150; - y = -175; - z = -20; - }; - rightArmTra2 = { - x = -150; - y = -175; - z = -20; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 40; - y = 0; - }; - comTra2 = { - x = 40; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 150; - leftFootTra1 = { - x = 80; - y = 85; - z = -184; - }; - leftFootTra2 = { - x = 110; - y = 85; - z = -184; - }; - leftFootRot1 = { - x = -0.2; - y = 0; - z = 0; - }; - leftFootRot2 = { - x = -0.2; - y = 0; - z = 0; - }; - rightFootTra1 = { - x = 0; - y = -20; - z = -234; - }; - rightFootTra2 = { - x = 0; - y = -20; - z = -234; - }; - rightFootRot1 = { - x = -0.05; - y = -0; - z = -0; - }; - rightFootRot2 = { - x = -0.05; - y = -0; - z = -0; - }; - leftArmTra1 = { - x = -150; - y = 210; - z = -20; - }; - leftArmTra2 = { - x = -150; - y = 210; - z = -20; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 150; - y = -175; - z = -20; - }; - rightArmTra2 = { - x = 150; - y = -175; - z = -20; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 40; - y = 0; - }; - comTra2 = { - x = 40; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 250; - leftFootTra1 = { - x = -0; - y = 75; - z = -224; - }; - leftFootTra2 = { - x = -0; - y = 75; - z = -224; - }; - leftFootRot1 = { - x = -0.1; - y = -0; - z = 0; - }; - leftFootRot2 = { - x = -0.1; - y = -0; - z = 0; - }; - rightFootTra1 = { - x = 0; - y = -40; - z = -234; - }; - rightFootTra2 = { - x = 0; - y = -40; - z = -234; - }; - rightFootRot1 = { - x = -0.05; - y = -0; - z = -0; - }; - rightFootRot2 = { - x = -0.05; - y = -0; - z = -0; - }; - leftArmTra1 = { - x = -150; - y = 210; - z = -20; - }; - leftArmTra2 = { - x = -150; - y = 210; - z = -20; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 150; - y = -175; - z = -20; - }; - rightArmTra2 = { - x = 150; - y = -175; - z = -20; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 40; - y = 0; - }; - comTra2 = { - x = 40; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 1000; - leftFootTra1 = { - x = 0; - y = 55; - z = -234; - }; - leftFootTra2 = { - x = 0; - y = 55; - z = -234; - }; - leftFootRot1 = { - x = 0; - y = -0; - z = 0; - }; - leftFootRot2 = { - x = 0; - y = -0; - z = 0; - }; - rightFootTra1 = { - x = 0; - y = -55; - z = -234; - }; - rightFootTra2 = { - x = 0; - y = -55; - z = -234; - }; - rightFootRot1 = { - x = 0; - y = -0; - z = -0; - }; - rightFootRot2 = { - x = 0; - y = -0; - z = -0; - }; - leftArmTra1 = { - x = 0; - y = 150; - z = -20; - }; - leftArmTra2 = { - x = 0; - y = 150; - z = -20; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 0; - y = -150; - z = -20; - }; - rightArmTra2 = { - x = 0; - y = -150; - z = -20; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 40; - y = 0; - }; - comTra2 = { - x = 40; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - } -]; diff --git a/Config/KickEngine/kickOuter.kmc b/Config/KickEngine/kickOuter.kmc deleted file mode 100644 index 66b02899..00000000 --- a/Config/KickEngine/kickOuter.kmc +++ /dev/null @@ -1,560 +0,0 @@ -footOrigin = { - x = 0; - y = 55; - z = -225; -}; -footRotOrigin = { - x = 0; - y = 0; - z = 0; -}; -armOrigin = { - x = 0; - y = 130; - z = 80; -}; -handRotOrigin = { - x = 0; - y = 0; - z = 0; -}; -comOrigin = { - x = 10; - y = 0; -}; -headOrigin = { - x = 0; - y = 0; -}; -kpx = 1; -kix = 0; -kdx = 0.001; -kpy = 1; -kiy = 0; -kdy = 0.005; -preview = 5; -loop = true; -autoComTra = true; -ignoreHead = true; -phaseParameters = [ - { - duration = 1000; - leftFootTra1 = { - x = 0; - y = 100; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = 100; - z = -225; - }; - leftFootRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftFootRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightFootTra1 = { - x = 0; - y = -10; - z = -225; - }; - rightFootTra2 = { - x = 0; - y = -10; - z = -225; - }; - rightFootRot1 = { - x = -0.1; - y = 0; - z = -0; - }; - rightFootRot2 = { - x = -0.1; - y = 0; - z = -0; - }; - leftArmTra1 = { - x = 0; - y = 180; - z = 80; - }; - leftArmTra2 = { - x = 0; - y = 180; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 0; - y = -130; - z = 80; - }; - rightArmTra2 = { - x = 0; - y = -130; - z = 80; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 500; - leftFootTra1 = { - x = -110; - y = 110; - z = -145; - }; - leftFootTra2 = { - x = -110; - y = 110; - z = -145; - }; - leftFootRot1 = { - x = -0.1; - y = 0.3; - z = 0; - }; - leftFootRot2 = { - x = -0.1; - y = 0.3; - z = 0; - }; - rightFootTra1 = { - x = 0; - y = -10; - z = -225; - }; - rightFootTra2 = { - x = 0; - y = -10; - z = -225; - }; - rightFootRot1 = { - x = -0.15; - y = 0; - z = 0; - }; - rightFootRot2 = { - x = -0.15; - y = 0; - z = -0; - }; - leftArmTra1 = { - x = 100; - y = 200; - z = 80; - }; - leftArmTra2 = { - x = 100; - y = 200; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 0; - y = -130; - z = 80; - }; - rightArmTra2 = { - x = 0; - y = -130; - z = 80; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 150; - leftFootTra1 = { - x = 140; - y = 110; - z = -160; - }; - leftFootTra2 = { - x = 140; - y = 110; - z = -160; - }; - leftFootRot1 = { - x = -0.1; - y = -0.1; - z = 0; - }; - leftFootRot2 = { - x = -0.1; - y = -0.1; - z = 0; - }; - rightFootTra1 = { - x = 0; - y = -10; - z = -225; - }; - rightFootTra2 = { - x = 0; - y = -10; - z = -225; - }; - rightFootRot1 = { - x = -0.1; - y = 0; - z = -0; - }; - rightFootRot2 = { - x = -0.1; - y = 0; - z = -0; - }; - leftArmTra1 = { - x = -150; - y = 200; - z = 80; - }; - leftArmTra2 = { - x = -150; - y = 200; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 0; - y = -130; - z = 80; - }; - rightArmTra2 = { - x = 0; - y = -130; - z = 80; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 700; - leftFootTra1 = { - x = 0; - y = 110; - z = -210; - }; - leftFootTra2 = { - x = 0; - y = 110; - z = -210; - }; - leftFootRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftFootRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightFootTra1 = { - x = 0; - y = -10; - z = -225; - }; - rightFootTra2 = { - x = 0; - y = -10; - z = -225; - }; - rightFootRot1 = { - x = -0.1; - y = 0; - z = -0; - }; - rightFootRot2 = { - x = -0.1; - y = 0; - z = -0; - }; - leftArmTra1 = { - x = 0; - y = 180; - z = 80; - }; - leftArmTra2 = { - x = 0; - y = 180; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 0; - y = -130; - z = 80; - }; - rightArmTra2 = { - x = 0; - y = -130; - z = 80; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 1000; - leftFootTra1 = { - x = 0; - y = 55; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = 55; - z = -225; - }; - leftFootRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftFootRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightFootTra1 = { - x = 0; - y = -55; - z = -225; - }; - rightFootTra2 = { - x = 0; - y = -55; - z = -225; - }; - rightFootRot1 = { - x = 0; - y = 0; - z = -0; - }; - rightFootRot2 = { - x = 0; - y = 0; - z = -0; - }; - leftArmTra1 = { - x = 0; - y = 130; - z = 80; - }; - leftArmTra2 = { - x = 0; - y = 130; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 0; - y = -130; - z = 80; - }; - rightArmTra2 = { - x = 0; - y = -130; - z = 80; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - } -]; diff --git a/Config/KickEngine/kickOuterFast.kmc b/Config/KickEngine/kickOuterFast.kmc deleted file mode 100644 index 3c13fcf2..00000000 --- a/Config/KickEngine/kickOuterFast.kmc +++ /dev/null @@ -1,664 +0,0 @@ -footOrigin = { - x = 0; - y = 55; - z = -225; -}; -footRotOrigin = { - x = 0; - y = 0; - z = 0; -}; -armOrigin = { - x = 0; - y = 150; - z = -20; -}; -handRotOrigin = { - x = 0; - y = 0; - z = 0; -}; -comOrigin = { - x = 50; - y = 0; -}; -headOrigin = { - x = 0; - y = 0; -}; -kpx = 0; -kix = 0; -kdx = 0; -kpy = 0; -kiy = 0; -kdy = 0; -preview = 40; -loop = false; -autoComTra = false; -ignoreHead = true; -phaseParameters = [ - { - duration = 210; - leftFootTra1 = { - x = 0; - y = 55; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = 55; - z = -225; - }; - leftFootRot1 = { - x = -0.1; - y = -0; - z = 0; - }; - leftFootRot2 = { - x = -0.1; - y = -0; - z = 0; - }; - rightFootTra1 = { - x = 0; - y = -55; - z = -225; - }; - rightFootTra2 = { - x = 0; - y = -55; - z = -225; - }; - rightFootRot1 = { - x = -0.05; - y = -0; - z = -0; - }; - rightFootRot2 = { - x = -0.05; - y = -0; - z = -0; - }; - leftArmTra1 = { - x = 0; - y = 155; - z = -20; - }; - leftArmTra2 = { - x = 0; - y = 155; - z = -20; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 0; - y = -155; - z = -20; - }; - rightArmTra2 = { - x = 0; - y = -155; - z = -20; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 50; - y = 0; - }; - comTra2 = { - x = 50; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 400; - leftFootTra1 = { - x = 0; - y = 65; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = 65; - z = -225; - }; - leftFootRot1 = { - x = -0.1; - y = -0; - z = 0; - }; - leftFootRot2 = { - x = -0.1; - y = -0; - z = 0; - }; - rightFootTra1 = { - x = 0; - y = -20; - z = -225; - }; - rightFootTra2 = { - x = 0; - y = -20; - z = -225; - }; - rightFootRot1 = { - x = -0.05; - y = -0; - z = -0; - }; - rightFootRot2 = { - x = -0.05; - y = -0; - z = -0; - }; - leftArmTra1 = { - x = 0; - y = 175; - z = -20; - }; - leftArmTra2 = { - x = 0; - y = 175; - z = -20; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = -150; - y = -175; - z = -20; - }; - rightArmTra2 = { - x = -150; - y = -175; - z = -20; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 40; - y = -2; - }; - comTra2 = { - x = 40; - y = -2; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 140; - leftFootTra1 = { - x = -40; - y = 110; - z = -175; - }; - leftFootTra2 = { - x = -40; - y = 110; - z = -175; - }; - leftFootRot1 = { - x = -0.2; - y = 0.3; - z = 0; - }; - leftFootRot2 = { - x = -0.2; - y = 0.3; - z = 0; - }; - rightFootTra1 = { - x = 0; - y = -20; - z = -225; - }; - rightFootTra2 = { - x = 0; - y = -20; - z = -225; - }; - rightFootRot1 = { - x = -0.1; - y = -0; - z = 0; - }; - rightFootRot2 = { - x = -0.1; - y = -0; - z = -0; - }; - leftArmTra1 = { - x = 150; - y = 210; - z = -20; - }; - leftArmTra2 = { - x = 150; - y = 210; - z = -20; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = -150; - y = -175; - z = -20; - }; - rightArmTra2 = { - x = -150; - y = -175; - z = -20; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 40; - y = -2; - }; - comTra2 = { - x = 40; - y = -2; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 150; - leftFootTra1 = { - x = 80; - y = 120; - z = -175; - }; - leftFootTra2 = { - x = 110; - y = 120; - z = -175; - }; - leftFootRot1 = { - x = -0.2; - y = 0; - z = 0; - }; - leftFootRot2 = { - x = -0.2; - y = 0; - z = 0; - }; - rightFootTra1 = { - x = 0; - y = -20; - z = -225; - }; - rightFootTra2 = { - x = 0; - y = -20; - z = -225; - }; - rightFootRot1 = { - x = -0.05; - y = -0; - z = -0; - }; - rightFootRot2 = { - x = -0.05; - y = -0; - z = -0; - }; - leftArmTra1 = { - x = -150; - y = 210; - z = -20; - }; - leftArmTra2 = { - x = -150; - y = 210; - z = -20; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 150; - y = -175; - z = -20; - }; - rightArmTra2 = { - x = 150; - y = -175; - z = -20; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 40; - y = -2; - }; - comTra2 = { - x = 40; - y = -2; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 250; - leftFootTra1 = { - x = -0; - y = 95; - z = -215; - }; - leftFootTra2 = { - x = -0; - y = 95; - z = -215; - }; - leftFootRot1 = { - x = -0.1; - y = -0; - z = 0; - }; - leftFootRot2 = { - x = -0.1; - y = -0; - z = 0; - }; - rightFootTra1 = { - x = 0; - y = -40; - z = -225; - }; - rightFootTra2 = { - x = 0; - y = -40; - z = -225; - }; - rightFootRot1 = { - x = -0.05; - y = -0; - z = -0; - }; - rightFootRot2 = { - x = -0.05; - y = -0; - z = -0; - }; - leftArmTra1 = { - x = -150; - y = 210; - z = -20; - }; - leftArmTra2 = { - x = -150; - y = 210; - z = -20; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 150; - y = -175; - z = -20; - }; - rightArmTra2 = { - x = 150; - y = -175; - z = -20; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 40; - y = -2; - }; - comTra2 = { - x = 40; - y = -2; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 1000; - leftFootTra1 = { - x = 0; - y = 55; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = 55; - z = -225; - }; - leftFootRot1 = { - x = -0.1; - y = -0; - z = 0; - }; - leftFootRot2 = { - x = -0.1; - y = -0; - z = 0; - }; - rightFootTra1 = { - x = 0; - y = -55; - z = -225; - }; - rightFootTra2 = { - x = 0; - y = -55; - z = -225; - }; - rightFootRot1 = { - x = 0.1; - y = -0; - z = -0; - }; - rightFootRot2 = { - x = 0.1; - y = -0; - z = -0; - }; - leftArmTra1 = { - x = 0; - y = 150; - z = -20; - }; - leftArmTra2 = { - x = 0; - y = 150; - z = -20; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 0; - y = -150; - z = -20; - }; - rightArmTra2 = { - x = 0; - y = -150; - z = -20; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 40; - y = 0; - }; - comTra2 = { - x = 40; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - } -]; diff --git a/Config/KickEngine/mostPowerfulKick.kmc b/Config/KickEngine/mostPowerfulKick.kmc deleted file mode 100644 index 933316f7..00000000 --- a/Config/KickEngine/mostPowerfulKick.kmc +++ /dev/null @@ -1,560 +0,0 @@ -footOrigin = { - x = 0; - y = 55; - z = -225; -}; -footRotOrigin = { - x = 0; - y = 0; - z = 0; -}; -armOrigin = { - x = 0; - y = 130; - z = 80; -}; -handRotOrigin = { - x = 0; - y = 0; - z = 0; -}; -comOrigin = { - x = 10; - y = 0; -}; -headOrigin = { - x = 0; - y = 0; -}; -kpx = 1; -kix = 0; -kdx = 0; -kpy = 1; -kiy = 0; -kdy = 0.005; -preview = 5; -loop = false; -autoComTra = true; -ignoreHead = true; -phaseParameters = [ - { - duration = 1000; - leftFootTra1 = { - x = 0; - y = 120; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = 120; - z = -225; - }; - leftFootRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftFootRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightFootTra1 = { - x = 0; - y = -17; - z = -225; - }; - rightFootTra2 = { - x = 0; - y = -17; - z = -225; - }; - rightFootRot1 = { - x = -0.1; - y = 0; - z = -0; - }; - rightFootRot2 = { - x = -0.1; - y = 0; - z = -0; - }; - leftArmTra1 = { - x = 0; - y = 130; - z = 80; - }; - leftArmTra2 = { - x = 0; - y = 130; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 0; - y = -130; - z = 80; - }; - rightArmTra2 = { - x = 0; - y = -180; - z = 80; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 500; - leftFootTra1 = { - x = -110; - y = 100; - z = -140; - }; - leftFootTra2 = { - x = -110; - y = 100; - z = -140; - }; - leftFootRot1 = { - x = -0.1; - y = 0.3; - z = 0; - }; - leftFootRot2 = { - x = -0.1; - y = 0.3; - z = 0; - }; - rightFootTra1 = { - x = 0; - y = -17; - z = -225; - }; - rightFootTra2 = { - x = 0; - y = -17; - z = -225; - }; - rightFootRot1 = { - x = -0.1; - y = 0; - z = 0; - }; - rightFootRot2 = { - x = -0.1; - y = 0; - z = -0; - }; - leftArmTra1 = { - x = 200; - y = 200; - z = 40; - }; - leftArmTra2 = { - x = 200; - y = 200; - z = 40; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = -150; - y = -180; - z = 40; - }; - rightArmTra2 = { - x = -150; - y = -180; - z = 40; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 120; - leftFootTra1 = { - x = 160; - y = 90; - z = -130; - }; - leftFootTra2 = { - x = 160; - y = 90; - z = -130; - }; - leftFootRot1 = { - x = -0.1; - y = -0.1; - z = 0; - }; - leftFootRot2 = { - x = -0.1; - y = -0.1; - z = 0; - }; - rightFootTra1 = { - x = 0; - y = -17; - z = -225; - }; - rightFootTra2 = { - x = 0; - y = -17; - z = -225; - }; - rightFootRot1 = { - x = -0.1; - y = 0; - z = -0; - }; - rightFootRot2 = { - x = -0.1; - y = 0; - z = -0; - }; - leftArmTra1 = { - x = -180; - y = 200; - z = 40; - }; - leftArmTra2 = { - x = -180; - y = 200; - z = 40; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 120; - y = -180; - z = 40; - }; - rightArmTra2 = { - x = 120; - y = -180; - z = 40; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 400; - leftFootTra1 = { - x = 120; - y = 90; - z = -180; - }; - leftFootTra2 = { - x = 120; - y = 90; - z = -180; - }; - leftFootRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftFootRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightFootTra1 = { - x = 0; - y = -17; - z = -225; - }; - rightFootTra2 = { - x = 0; - y = -17; - z = -225; - }; - rightFootRot1 = { - x = -0.1; - y = 0; - z = -0; - }; - rightFootRot2 = { - x = -0.1; - y = 0; - z = -0; - }; - leftArmTra1 = { - x = 0; - y = 200; - z = 80; - }; - leftArmTra2 = { - x = 0; - y = 200; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 0; - y = -200; - z = 80; - }; - rightArmTra2 = { - x = 0; - y = -200; - z = 80; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 300; - leftFootTra1 = { - x = 0; - y = 55; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = 55; - z = -225; - }; - leftFootRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftFootRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightFootTra1 = { - x = 0; - y = -55; - z = -225; - }; - rightFootTra2 = { - x = 0; - y = -55; - z = -225; - }; - rightFootRot1 = { - x = 0; - y = 0; - z = -0; - }; - rightFootRot2 = { - x = 0; - y = 0; - z = -0; - }; - leftArmTra1 = { - x = 0; - y = 180; - z = 80; - }; - leftArmTra2 = { - x = 0; - y = 180; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 0; - y = -160; - z = 80; - }; - rightArmTra2 = { - x = 0; - y = -160; - z = 80; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - } -]; diff --git a/Config/KickEngine/penaltyKickInner_left.kmc b/Config/KickEngine/penaltyKickInner_left.kmc deleted file mode 100644 index 7744b0e7..00000000 --- a/Config/KickEngine/penaltyKickInner_left.kmc +++ /dev/null @@ -1,664 +0,0 @@ -footOrigin = { - x = 0; - y = 55; - z = -225; -}; -footRotOrigin = { - x = 0; - y = 0; - z = 0; -}; -armOrigin = { - x = 0; - y = 130; - z = 80; -}; -handRotOrigin = { - x = 0; - y = 0; - z = 0; -}; -comOrigin = { - x = 10; - y = 0; -}; -headOrigin = { - x = 0; - y = 0; -}; -kpx = 0.5; -kix = 0; -kdx = 0.001; -kpy = 0.8; -kiy = 0; -kdy = 0.01; -preview = 5; -loop = false; -autoComTra = true; -ignoreHead = true; -phaseParameters = [ - { - duration = 1000; - leftFootTra1 = { - x = 0; - y = 15; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = 15; - z = -225; - }; - leftFootRot1 = { - x = -0.1; - y = -0; - z = -0; - }; - leftFootRot2 = { - x = -0.1; - y = -0; - z = -0; - }; - rightFootTra1 = { - x = 0; - y = -90; - z = -225; - }; - rightFootTra2 = { - x = 0; - y = -90; - z = -225; - }; - rightFootRot1 = { - x = -0.1; - y = -0; - z = 0; - }; - rightFootRot2 = { - x = -0.1; - y = -0; - z = 0; - }; - leftArmTra1 = { - x = 0; - y = 140; - z = 80; - }; - leftArmTra2 = { - x = 0; - y = 140; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 0; - y = -180; - z = 60; - }; - rightArmTra2 = { - x = 0; - y = -180; - z = 60; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 700; - leftFootTra1 = { - x = 0; - y = 15; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = 15; - z = -225; - }; - leftFootRot1 = { - x = -0.1; - y = -0; - z = 0; - }; - leftFootRot2 = { - x = -0.1; - y = -0; - z = -0; - }; - rightFootTra1 = { - x = -110; - y = -90; - z = -145; - }; - rightFootTra2 = { - x = -110; - y = -90; - z = -145; - }; - rightFootRot1 = { - x = -0.1; - y = -0.3; - z = 0; - }; - rightFootRot2 = { - x = -0.1; - y = -0.3; - z = 0; - }; - leftArmTra1 = { - x = -50; - y = 150; - z = 80; - }; - leftArmTra2 = { - x = -50; - y = 150; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 100; - y = -200; - z = 70; - }; - rightArmTra2 = { - x = 100; - y = -200; - z = 70; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 150; - leftFootTra1 = { - x = 0; - y = 15; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = 15; - z = -225; - }; - leftFootRot1 = { - x = -0.1; - y = -0; - z = -0; - }; - leftFootRot2 = { - x = -0.1; - y = -0; - z = -0; - }; - rightFootTra1 = { - x = 140; - y = -80; - z = -160; - }; - rightFootTra2 = { - x = 140; - y = -80; - z = -160; - }; - rightFootRot1 = { - x = -0.1; - y = 0.1; - z = 0; - }; - rightFootRot2 = { - x = -0.1; - y = 0.1; - z = 0; - }; - leftArmTra1 = { - x = 50; - y = 130; - z = 80; - }; - leftArmTra2 = { - x = 50; - y = 130; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = -150; - y = -200; - z = 80; - }; - rightArmTra2 = { - x = -150; - y = -200; - z = 80; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 500; - leftFootTra1 = { - x = 0; - y = 5; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = 5; - z = -225; - }; - leftFootRot1 = { - x = -0.1; - y = -0; - z = -0; - }; - leftFootRot2 = { - x = -0.1; - y = -0; - z = -0; - }; - rightFootTra1 = { - x = 0; - y = -120; - z = -200.584; - }; - rightFootTra2 = { - x = 0; - y = -120; - z = -200.584; - }; - rightFootRot1 = { - x = -0.1; - y = -0; - z = 0; - }; - rightFootRot2 = { - x = -0.1; - y = -0; - z = 0; - }; - leftArmTra1 = { - x = 0; - y = 130; - z = 80; - }; - leftArmTra2 = { - x = 0; - y = 130; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 0; - y = -180; - z = 80; - }; - rightArmTra2 = { - x = 0; - y = -180; - z = 80; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 200; - leftFootTra1 = { - x = 0; - y = 5; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = 5; - z = -225; - }; - leftFootRot1 = { - x = -0.05; - y = -0; - z = -0; - }; - leftFootRot2 = { - x = -0.05; - y = -0; - z = -0; - }; - rightFootTra1 = { - x = 0; - y = -110; - z = -200; - }; - rightFootTra2 = { - x = 0; - y = -110; - z = -200; - }; - rightFootRot1 = { - x = -0.05; - y = -0; - z = 0; - }; - rightFootRot2 = { - x = -0.05; - y = -0; - z = 0; - }; - leftArmTra1 = { - x = 0; - y = 130; - z = 80; - }; - leftArmTra2 = { - x = 0; - y = 130; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 0; - y = -180; - z = 80; - }; - rightArmTra2 = { - x = 0; - y = -180; - z = 80; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 1000; - leftFootTra1 = { - x = 0; - y = 55; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = 55; - z = -225; - }; - leftFootRot1 = { - x = 0; - y = -0; - z = -0; - }; - leftFootRot2 = { - x = 0; - y = -0; - z = -0; - }; - rightFootTra1 = { - x = 0; - y = -55; - z = -225; - }; - rightFootTra2 = { - x = 0; - y = -55; - z = -225; - }; - rightFootRot1 = { - x = 0; - y = -0; - z = 0; - }; - rightFootRot2 = { - x = 0; - y = -0; - z = 0; - }; - leftArmTra1 = { - x = 0; - y = 130; - z = 80; - }; - leftArmTra2 = { - x = 0; - y = 130; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 0; - y = -130; - z = 80; - }; - rightArmTra2 = { - x = 0; - y = -130; - z = 80; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - } -]; diff --git a/Config/KickEngine/penaltyKickInner_right.kmc b/Config/KickEngine/penaltyKickInner_right.kmc deleted file mode 100644 index 53b14b93..00000000 --- a/Config/KickEngine/penaltyKickInner_right.kmc +++ /dev/null @@ -1,664 +0,0 @@ -footOrigin = { - x = 0; - y = 55; - z = -225; -}; -footRotOrigin = { - x = 0; - y = 0; - z = 0; -}; -armOrigin = { - x = 0; - y = 130; - z = 80; -}; -handRotOrigin = { - x = 0; - y = 0; - z = 0; -}; -comOrigin = { - x = 10; - y = 0; -}; -headOrigin = { - x = 0; - y = 0; -}; -kpx = 0.5; -kix = 0; -kdx = 0.001; -kpy = 0.8; -kiy = 0; -kdy = 0.01; -preview = 5; -loop = false; -autoComTra = true; -ignoreHead = true; -phaseParameters = [ - { - duration = 1000; - leftFootTra1 = { - x = 0; - y = 5; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = 5; - z = -225; - }; - leftFootRot1 = { - x = -0.1; - y = -0; - z = -0; - }; - leftFootRot2 = { - x = -0.1; - y = -0; - z = -0; - }; - rightFootTra1 = { - x = 0; - y = -90; - z = -225; - }; - rightFootTra2 = { - x = 0; - y = -90; - z = -225; - }; - rightFootRot1 = { - x = -0.1; - y = -0; - z = 0; - }; - rightFootRot2 = { - x = -0.1; - y = -0; - z = 0; - }; - leftArmTra1 = { - x = 0; - y = 140; - z = 80; - }; - leftArmTra2 = { - x = 0; - y = 140; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 0; - y = -180; - z = 60; - }; - rightArmTra2 = { - x = 0; - y = -180; - z = 60; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 700; - leftFootTra1 = { - x = 0; - y = 5; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = 5; - z = -225; - }; - leftFootRot1 = { - x = -0.1; - y = -0; - z = 0; - }; - leftFootRot2 = { - x = -0.1; - y = -0; - z = -0; - }; - rightFootTra1 = { - x = -110; - y = -90; - z = -145; - }; - rightFootTra2 = { - x = -110; - y = -90; - z = -145; - }; - rightFootRot1 = { - x = -0.1; - y = -0.3; - z = 0; - }; - rightFootRot2 = { - x = -0.1; - y = -0.3; - z = 0; - }; - leftArmTra1 = { - x = -50; - y = 150; - z = 80; - }; - leftArmTra2 = { - x = -50; - y = 150; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 100; - y = -200; - z = 70; - }; - rightArmTra2 = { - x = 100; - y = -200; - z = 70; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 150; - leftFootTra1 = { - x = 0; - y = 5; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = 5; - z = -225; - }; - leftFootRot1 = { - x = -0.1; - y = 0; - z = -0; - }; - leftFootRot2 = { - x = -0.1; - y = 0; - z = -0; - }; - rightFootTra1 = { - x = 140; - y = -80; - z = -160; - }; - rightFootTra2 = { - x = 140; - y = -80; - z = -160; - }; - rightFootRot1 = { - x = -0.1; - y = 0.1; - z = 0; - }; - rightFootRot2 = { - x = -0.1; - y = 0.1; - z = 0; - }; - leftArmTra1 = { - x = 50; - y = 130; - z = 80; - }; - leftArmTra2 = { - x = 50; - y = 130; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = -150; - y = -200; - z = 80; - }; - rightArmTra2 = { - x = -150; - y = -200; - z = 80; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 500; - leftFootTra1 = { - x = 0; - y = -5; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = -5; - z = -225; - }; - leftFootRot1 = { - x = -0.1; - y = -0; - z = -0; - }; - leftFootRot2 = { - x = -0.1; - y = -0; - z = -0; - }; - rightFootTra1 = { - x = 0; - y = -120; - z = -200.584; - }; - rightFootTra2 = { - x = 0; - y = -120; - z = -200.584; - }; - rightFootRot1 = { - x = -0.1; - y = -0; - z = 0; - }; - rightFootRot2 = { - x = -0.1; - y = -0; - z = 0; - }; - leftArmTra1 = { - x = 0; - y = 130; - z = 80; - }; - leftArmTra2 = { - x = 0; - y = 130; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 0; - y = -180; - z = 80; - }; - rightArmTra2 = { - x = 0; - y = -180; - z = 80; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 200; - leftFootTra1 = { - x = 0; - y = -5; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = -5; - z = -225; - }; - leftFootRot1 = { - x = -0.05; - y = -0; - z = -0; - }; - leftFootRot2 = { - x = -0.05; - y = -0; - z = -0; - }; - rightFootTra1 = { - x = 0; - y = -110; - z = -200; - }; - rightFootTra2 = { - x = 0; - y = -110; - z = -200; - }; - rightFootRot1 = { - x = -0.05; - y = -0; - z = 0; - }; - rightFootRot2 = { - x = -0.05; - y = -0; - z = 0; - }; - leftArmTra1 = { - x = 0; - y = 130; - z = 80; - }; - leftArmTra2 = { - x = 0; - y = 130; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 0; - y = -180; - z = 80; - }; - rightArmTra2 = { - x = 0; - y = -180; - z = 80; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 1000; - leftFootTra1 = { - x = 0; - y = 55; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = 55; - z = -225; - }; - leftFootRot1 = { - x = 0; - y = -0; - z = -0; - }; - leftFootRot2 = { - x = 0; - y = -0; - z = -0; - }; - rightFootTra1 = { - x = 0; - y = -55; - z = -225; - }; - rightFootTra2 = { - x = 0; - y = -55; - z = -225; - }; - rightFootRot1 = { - x = 0; - y = -0; - z = 0; - }; - rightFootRot2 = { - x = 0; - y = -0; - z = 0; - }; - leftArmTra1 = { - x = 0; - y = 130; - z = 80; - }; - leftArmTra2 = { - x = 0; - y = 130; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 0; - y = -130; - z = 80; - }; - rightArmTra2 = { - x = 0; - y = -130; - z = 80; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - } -]; diff --git a/Config/KickEngine/penaltyKickMiddle_left.kmc b/Config/KickEngine/penaltyKickMiddle_left.kmc deleted file mode 100644 index 63c0648a..00000000 --- a/Config/KickEngine/penaltyKickMiddle_left.kmc +++ /dev/null @@ -1,664 +0,0 @@ -footOrigin = { - x = 0; - y = 55; - z = -225; -}; -footRotOrigin = { - x = 0; - y = 0; - z = 0; -}; -armOrigin = { - x = 0; - y = 130; - z = 80; -}; -handRotOrigin = { - x = 0; - y = 0; - z = 0; -}; -comOrigin = { - x = 10; - y = 0; -}; -headOrigin = { - x = 0; - y = 0; -}; -kpx = 0.5; -kix = 0; -kdx = 0.001; -kpy = 0.8; -kiy = 0; -kdy = 0.01; -preview = 5; -loop = false; -autoComTra = true; -ignoreHead = true; -phaseParameters = [ - { - duration = 1000; - leftFootTra1 = { - x = 0; - y = 12.5; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = 12.5; - z = -225; - }; - leftFootRot1 = { - x = -0.1; - y = -0; - z = -0; - }; - leftFootRot2 = { - x = -0.1; - y = -0; - z = -0; - }; - rightFootTra1 = { - x = 0; - y = -90; - z = -225; - }; - rightFootTra2 = { - x = 0; - y = -90; - z = -225; - }; - rightFootRot1 = { - x = -0.1; - y = -0; - z = 0; - }; - rightFootRot2 = { - x = -0.1; - y = -0; - z = 0; - }; - leftArmTra1 = { - x = 0; - y = 140; - z = 80; - }; - leftArmTra2 = { - x = 0; - y = 140; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 0; - y = -180; - z = 60; - }; - rightArmTra2 = { - x = 0; - y = -180; - z = 60; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 700; - leftFootTra1 = { - x = 0; - y = 12.5; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = 12.5; - z = -225; - }; - leftFootRot1 = { - x = -0.1; - y = -0; - z = 0; - }; - leftFootRot2 = { - x = -0.1; - y = -0; - z = -0; - }; - rightFootTra1 = { - x = -110; - y = -90; - z = -145; - }; - rightFootTra2 = { - x = -110; - y = -90; - z = -145; - }; - rightFootRot1 = { - x = -0.1; - y = -0.3; - z = 0; - }; - rightFootRot2 = { - x = -0.1; - y = -0.3; - z = 0; - }; - leftArmTra1 = { - x = -50; - y = 150; - z = 80; - }; - leftArmTra2 = { - x = -50; - y = 150; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 100; - y = -190; - z = 70; - }; - rightArmTra2 = { - x = 100; - y = -190; - z = 70; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 150; - leftFootTra1 = { - x = 0; - y = 7.5; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = 7.5; - z = -225; - }; - leftFootRot1 = { - x = -0.1; - y = -0; - z = -0; - }; - leftFootRot2 = { - x = -0.1; - y = -0; - z = -0; - }; - rightFootTra1 = { - x = 140; - y = -90; - z = -160; - }; - rightFootTra2 = { - x = 140; - y = -90; - z = -160; - }; - rightFootRot1 = { - x = -0.1; - y = 0.1; - z = 0; - }; - rightFootRot2 = { - x = -0.1; - y = 0.1; - z = 0; - }; - leftArmTra1 = { - x = 50; - y = 130; - z = 80; - }; - leftArmTra2 = { - x = 50; - y = 130; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = -150; - y = -190; - z = 80; - }; - rightArmTra2 = { - x = -150; - y = -190; - z = 80; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 500; - leftFootTra1 = { - x = 0; - y = 2.5; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = 2.5; - z = -225; - }; - leftFootRot1 = { - x = -0.1; - y = -0; - z = -0; - }; - leftFootRot2 = { - x = -0.1; - y = -0; - z = -0; - }; - rightFootTra1 = { - x = 0; - y = -120; - z = -200.584; - }; - rightFootTra2 = { - x = 0; - y = -120; - z = -200.584; - }; - rightFootRot1 = { - x = -0.1; - y = -0; - z = 0; - }; - rightFootRot2 = { - x = -0.1; - y = -0; - z = 0; - }; - leftArmTra1 = { - x = 0; - y = 130; - z = 80; - }; - leftArmTra2 = { - x = 0; - y = 130; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 0; - y = -180; - z = 80; - }; - rightArmTra2 = { - x = 0; - y = -180; - z = 80; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 200; - leftFootTra1 = { - x = 0; - y = -2.5; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = -2.5; - z = -225; - }; - leftFootRot1 = { - x = -0.05; - y = -0; - z = -0; - }; - leftFootRot2 = { - x = -0.05; - y = -0; - z = -0; - }; - rightFootTra1 = { - x = 0; - y = -110; - z = -200; - }; - rightFootTra2 = { - x = 0; - y = -110; - z = -200; - }; - rightFootRot1 = { - x = -0.05; - y = -0; - z = 0; - }; - rightFootRot2 = { - x = -0.05; - y = -0; - z = 0; - }; - leftArmTra1 = { - x = 0; - y = 130; - z = 80; - }; - leftArmTra2 = { - x = 0; - y = 130; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 0; - y = -180; - z = 80; - }; - rightArmTra2 = { - x = 0; - y = -180; - z = 80; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 1000; - leftFootTra1 = { - x = 0; - y = 55; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = 55; - z = -225; - }; - leftFootRot1 = { - x = 0; - y = -0; - z = -0; - }; - leftFootRot2 = { - x = 0; - y = -0; - z = -0; - }; - rightFootTra1 = { - x = 0; - y = -55; - z = -225; - }; - rightFootTra2 = { - x = 0; - y = -55; - z = -225; - }; - rightFootRot1 = { - x = 0; - y = -0; - z = 0; - }; - rightFootRot2 = { - x = 0; - y = -0; - z = 0; - }; - leftArmTra1 = { - x = 0; - y = 130; - z = 80; - }; - leftArmTra2 = { - x = 0; - y = 130; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 0; - y = -130; - z = 80; - }; - rightArmTra2 = { - x = 0; - y = -130; - z = 80; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - } -]; diff --git a/Config/KickEngine/penaltyKickMiddle_right.kmc b/Config/KickEngine/penaltyKickMiddle_right.kmc deleted file mode 100644 index 7c7bf3c4..00000000 --- a/Config/KickEngine/penaltyKickMiddle_right.kmc +++ /dev/null @@ -1,664 +0,0 @@ -footOrigin = { - x = 0; - y = 55; - z = -225; -}; -footRotOrigin = { - x = 0; - y = 0; - z = 0; -}; -armOrigin = { - x = 0; - y = 130; - z = 80; -}; -handRotOrigin = { - x = 0; - y = 0; - z = 0; -}; -comOrigin = { - x = 10; - y = 0; -}; -headOrigin = { - x = 0; - y = 0; -}; -kpx = 0.5; -kix = 0; -kdx = 0.001; -kpy = 0.8; -kiy = 0; -kdy = 0.01; -preview = 5; -loop = true; -autoComTra = true; -ignoreHead = true; -phaseParameters = [ - { - duration = 1000; - leftFootTra1 = { - x = 0; - y = 5; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = 5; - z = -225; - }; - leftFootRot1 = { - x = -0.1; - y = -0; - z = -0; - }; - leftFootRot2 = { - x = -0.1; - y = -0; - z = -0; - }; - rightFootTra1 = { - x = 0; - y = -90; - z = -225; - }; - rightFootTra2 = { - x = 0; - y = -90; - z = -225; - }; - rightFootRot1 = { - x = -0.1; - y = -0; - z = 0; - }; - rightFootRot2 = { - x = -0.1; - y = -0; - z = 0; - }; - leftArmTra1 = { - x = 0; - y = 140; - z = 80; - }; - leftArmTra2 = { - x = 0; - y = 140; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 0; - y = -180; - z = 60; - }; - rightArmTra2 = { - x = 0; - y = -180; - z = 60; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 700; - leftFootTra1 = { - x = 0; - y = 5; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = 5; - z = -225; - }; - leftFootRot1 = { - x = -0.1; - y = -0; - z = 0; - }; - leftFootRot2 = { - x = -0.1; - y = -0; - z = -0; - }; - rightFootTra1 = { - x = -110; - y = -90; - z = -145; - }; - rightFootTra2 = { - x = -110; - y = -90; - z = -145; - }; - rightFootRot1 = { - x = -0.1; - y = -0.3; - z = 0; - }; - rightFootRot2 = { - x = -0.1; - y = -0.3; - z = 0; - }; - leftArmTra1 = { - x = -50; - y = 150; - z = 80; - }; - leftArmTra2 = { - x = -50; - y = 150; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 100; - y = -190; - z = 70; - }; - rightArmTra2 = { - x = 100; - y = -190; - z = 70; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 150; - leftFootTra1 = { - x = 0; - y = 0; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = 0; - z = -225; - }; - leftFootRot1 = { - x = -0.1; - y = -0; - z = -0; - }; - leftFootRot2 = { - x = -0.1; - y = -0; - z = -0; - }; - rightFootTra1 = { - x = 140; - y = -90; - z = -160; - }; - rightFootTra2 = { - x = 140; - y = -90; - z = -160; - }; - rightFootRot1 = { - x = -0.1; - y = 0.1; - z = 0; - }; - rightFootRot2 = { - x = -0.1; - y = 0.1; - z = 0; - }; - leftArmTra1 = { - x = 50; - y = 130; - z = 80; - }; - leftArmTra2 = { - x = 50; - y = 130; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = -150; - y = -190; - z = 80; - }; - rightArmTra2 = { - x = -150; - y = -190; - z = 80; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 500; - leftFootTra1 = { - x = 0; - y = -5; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = -5; - z = -225; - }; - leftFootRot1 = { - x = -0.1; - y = -0; - z = -0; - }; - leftFootRot2 = { - x = -0.1; - y = -0; - z = -0; - }; - rightFootTra1 = { - x = 0; - y = -120; - z = -200.584; - }; - rightFootTra2 = { - x = 0; - y = -120; - z = -200.584; - }; - rightFootRot1 = { - x = -0.1; - y = -0; - z = 0; - }; - rightFootRot2 = { - x = -0.1; - y = -0; - z = 0; - }; - leftArmTra1 = { - x = 0; - y = 130; - z = 80; - }; - leftArmTra2 = { - x = 0; - y = 130; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 0; - y = -180; - z = 80; - }; - rightArmTra2 = { - x = 0; - y = -180; - z = 80; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 200; - leftFootTra1 = { - x = 0; - y = -5; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = -5; - z = -225; - }; - leftFootRot1 = { - x = -0.05; - y = -0; - z = -0; - }; - leftFootRot2 = { - x = -0.05; - y = -0; - z = -0; - }; - rightFootTra1 = { - x = 0; - y = -110; - z = -200; - }; - rightFootTra2 = { - x = 0; - y = -110; - z = -200; - }; - rightFootRot1 = { - x = -0.05; - y = -0; - z = 0; - }; - rightFootRot2 = { - x = -0.05; - y = -0; - z = 0; - }; - leftArmTra1 = { - x = 0; - y = 130; - z = 80; - }; - leftArmTra2 = { - x = 0; - y = 130; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 0; - y = -180; - z = 80; - }; - rightArmTra2 = { - x = 0; - y = -180; - z = 80; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 1000; - leftFootTra1 = { - x = 0; - y = 55; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = 55; - z = -225; - }; - leftFootRot1 = { - x = 0; - y = -0; - z = -0; - }; - leftFootRot2 = { - x = 0; - y = -0; - z = -0; - }; - rightFootTra1 = { - x = 0; - y = -55; - z = -225; - }; - rightFootTra2 = { - x = 0; - y = -55; - z = -225; - }; - rightFootRot1 = { - x = 0; - y = -0; - z = 0; - }; - rightFootRot2 = { - x = 0; - y = -0; - z = 0; - }; - leftArmTra1 = { - x = 0; - y = 130; - z = 80; - }; - leftArmTra2 = { - x = 0; - y = 130; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 0; - y = -130; - z = 80; - }; - rightArmTra2 = { - x = 0; - y = -130; - z = 80; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - } -]; diff --git a/Config/KickEngine/penaltyKickOuter_left.kmc b/Config/KickEngine/penaltyKickOuter_left.kmc deleted file mode 100644 index 583cd217..00000000 --- a/Config/KickEngine/penaltyKickOuter_left.kmc +++ /dev/null @@ -1,664 +0,0 @@ -footOrigin = { - x = 0; - y = 55; - z = -225; -}; -footRotOrigin = { - x = 0; - y = 0; - z = 0; -}; -armOrigin = { - x = 0; - y = 130; - z = 80; -}; -handRotOrigin = { - x = 0; - y = 0; - z = 0; -}; -comOrigin = { - x = 10; - y = 0; -}; -headOrigin = { - x = 0; - y = 0; -}; -kpx = 0.5; -kix = 0; -kdx = 0.001; -kpy = 0.8; -kiy = 0; -kdy = 0.01; -preview = 5; -loop = false; -autoComTra = true; -ignoreHead = true; -phaseParameters = [ - { - duration = 1000; - leftFootTra1 = { - x = 0; - y = 10; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = 10; - z = -225; - }; - leftFootRot1 = { - x = -0.1; - y = -0; - z = -0; - }; - leftFootRot2 = { - x = -0.1; - y = -0; - z = -0; - }; - rightFootTra1 = { - x = 0; - y = -90; - z = -225; - }; - rightFootTra2 = { - x = 0; - y = -90; - z = -225; - }; - rightFootRot1 = { - x = -0.1; - y = -0; - z = 0; - }; - rightFootRot2 = { - x = -0.1; - y = -0; - z = 0; - }; - leftArmTra1 = { - x = 0; - y = 140; - z = 80; - }; - leftArmTra2 = { - x = 0; - y = 140; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 0; - y = -180; - z = 60; - }; - rightArmTra2 = { - x = 0; - y = -180; - z = 60; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 700; - leftFootTra1 = { - x = 0; - y = 10; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = 10; - z = -225; - }; - leftFootRot1 = { - x = -0.1; - y = -0; - z = 0; - }; - leftFootRot2 = { - x = -0.1; - y = -0; - z = -0; - }; - rightFootTra1 = { - x = -110; - y = -90; - z = -145; - }; - rightFootTra2 = { - x = -110; - y = -90; - z = -145; - }; - rightFootRot1 = { - x = -0.1; - y = -0.3; - z = 0; - }; - rightFootRot2 = { - x = -0.1; - y = -0.3; - z = 0; - }; - leftArmTra1 = { - x = -50; - y = 150; - z = 80; - }; - leftArmTra2 = { - x = -50; - y = 150; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 100; - y = -185; - z = 70; - }; - rightArmTra2 = { - x = 100; - y = -185; - z = 70; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 150; - leftFootTra1 = { - x = 0; - y = 5; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = 5; - z = -225; - }; - leftFootRot1 = { - x = -0.1; - y = -0; - z = -0; - }; - leftFootRot2 = { - x = -0.1; - y = -0; - z = -0; - }; - rightFootTra1 = { - x = 140; - y = -100; - z = -160; - }; - rightFootTra2 = { - x = 140; - y = -100; - z = -160; - }; - rightFootRot1 = { - x = -0.1; - y = 0.1; - z = 0; - }; - rightFootRot2 = { - x = -0.1; - y = 0.1; - z = 0; - }; - leftArmTra1 = { - x = 50; - y = 130; - z = 80; - }; - leftArmTra2 = { - x = 50; - y = 130; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = -150; - y = -185; - z = 80; - }; - rightArmTra2 = { - x = -150; - y = -185; - z = 80; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 500; - leftFootTra1 = { - x = 0; - y = 0; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = 0; - z = -225; - }; - leftFootRot1 = { - x = -0.1; - y = -0; - z = -0; - }; - leftFootRot2 = { - x = -0.1; - y = -0; - z = -0; - }; - rightFootTra1 = { - x = 0; - y = -110; - z = -200.584; - }; - rightFootTra2 = { - x = 0; - y = -110; - z = -200.584; - }; - rightFootRot1 = { - x = -0.1; - y = -0; - z = 0; - }; - rightFootRot2 = { - x = -0.1; - y = -0; - z = 0; - }; - leftArmTra1 = { - x = 0; - y = 130; - z = 80; - }; - leftArmTra2 = { - x = 0; - y = 130; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 0; - y = -180; - z = 80; - }; - rightArmTra2 = { - x = 0; - y = -180; - z = 80; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 200; - leftFootTra1 = { - x = 0; - y = 0; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = 0; - z = -225; - }; - leftFootRot1 = { - x = -0.05; - y = -0; - z = -0; - }; - leftFootRot2 = { - x = -0.05; - y = -0; - z = -0; - }; - rightFootTra1 = { - x = 0; - y = -100; - z = -200; - }; - rightFootTra2 = { - x = 0; - y = -100; - z = -200; - }; - rightFootRot1 = { - x = -0.05; - y = -0; - z = 0; - }; - rightFootRot2 = { - x = -0.05; - y = -0; - z = 0; - }; - leftArmTra1 = { - x = 0; - y = 130; - z = 80; - }; - leftArmTra2 = { - x = 0; - y = 130; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 0; - y = -180; - z = 80; - }; - rightArmTra2 = { - x = 0; - y = -180; - z = 80; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 1000; - leftFootTra1 = { - x = 0; - y = 55; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = 55; - z = -225; - }; - leftFootRot1 = { - x = 0; - y = -0; - z = -0; - }; - leftFootRot2 = { - x = 0; - y = -0; - z = -0; - }; - rightFootTra1 = { - x = 0; - y = -55; - z = -225; - }; - rightFootTra2 = { - x = 0; - y = -55; - z = -225; - }; - rightFootRot1 = { - x = 0; - y = -0; - z = 0; - }; - rightFootRot2 = { - x = 0; - y = -0; - z = 0; - }; - leftArmTra1 = { - x = 0; - y = 130; - z = 80; - }; - leftArmTra2 = { - x = 0; - y = 130; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 0; - y = -130; - z = 80; - }; - rightArmTra2 = { - x = 0; - y = -130; - z = 80; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - } -]; diff --git a/Config/KickEngine/penaltyKickOuter_right.kmc b/Config/KickEngine/penaltyKickOuter_right.kmc deleted file mode 100644 index a17c7442..00000000 --- a/Config/KickEngine/penaltyKickOuter_right.kmc +++ /dev/null @@ -1,664 +0,0 @@ -footOrigin = { - x = 0; - y = 55; - z = -225; -}; -footRotOrigin = { - x = 0; - y = 0; - z = 0; -}; -armOrigin = { - x = 0; - y = 130; - z = 80; -}; -handRotOrigin = { - x = 0; - y = 0; - z = 0; -}; -comOrigin = { - x = 10; - y = 0; -}; -headOrigin = { - x = 0; - y = 0; -}; -kpx = 0.5; -kix = 0; -kdx = 0.001; -kpy = 0.8; -kiy = 0; -kdy = 0.01; -preview = 5; -loop = false; -autoComTra = true; -ignoreHead = true; -phaseParameters = [ - { - duration = 1000; - leftFootTra1 = { - x = 0; - y = 3.5; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = 3.5; - z = -225; - }; - leftFootRot1 = { - x = -0.1; - y = -0; - z = -0; - }; - leftFootRot2 = { - x = -0.1; - y = -0; - z = -0; - }; - rightFootTra1 = { - x = 0; - y = -90; - z = -225; - }; - rightFootTra2 = { - x = 0; - y = -90; - z = -225; - }; - rightFootRot1 = { - x = -0.1; - y = -0; - z = 0; - }; - rightFootRot2 = { - x = -0.1; - y = -0; - z = 0; - }; - leftArmTra1 = { - x = 0; - y = 140; - z = 80; - }; - leftArmTra2 = { - x = 0; - y = 140; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 0; - y = -180; - z = 60; - }; - rightArmTra2 = { - x = 0; - y = -180; - z = 60; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 700; - leftFootTra1 = { - x = 0; - y = 3.5; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = 3.5; - z = -225; - }; - leftFootRot1 = { - x = -0.1; - y = -0; - z = 0; - }; - leftFootRot2 = { - x = -0.1; - y = -0; - z = -0; - }; - rightFootTra1 = { - x = -110; - y = -90; - z = -145; - }; - rightFootTra2 = { - x = -110; - y = -90; - z = -145; - }; - rightFootRot1 = { - x = -0.1; - y = -0.3; - z = 0; - }; - rightFootRot2 = { - x = -0.1; - y = -0.3; - z = 0; - }; - leftArmTra1 = { - x = -50; - y = 150; - z = 80; - }; - leftArmTra2 = { - x = -50; - y = 150; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 100; - y = -185; - z = 70; - }; - rightArmTra2 = { - x = 100; - y = -185; - z = 70; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 150; - leftFootTra1 = { - x = 0; - y = -1.5; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = -1.5; - z = -225; - }; - leftFootRot1 = { - x = -0.1; - y = -0; - z = -0; - }; - leftFootRot2 = { - x = -0.1; - y = -0; - z = -0; - }; - rightFootTra1 = { - x = 140; - y = -100; - z = -160; - }; - rightFootTra2 = { - x = 140; - y = -100; - z = -160; - }; - rightFootRot1 = { - x = -0.1; - y = 0.1; - z = 0; - }; - rightFootRot2 = { - x = -0.1; - y = 0.1; - z = 0; - }; - leftArmTra1 = { - x = 50; - y = 130; - z = 80; - }; - leftArmTra2 = { - x = 50; - y = 130; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = -150; - y = -185; - z = 80; - }; - rightArmTra2 = { - x = -150; - y = -185; - z = 80; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 500; - leftFootTra1 = { - x = 0; - y = -6.5; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = -6.5; - z = -225; - }; - leftFootRot1 = { - x = -0.1; - y = -0; - z = -0; - }; - leftFootRot2 = { - x = -0.1; - y = -0; - z = -0; - }; - rightFootTra1 = { - x = 0; - y = -110; - z = -200.584; - }; - rightFootTra2 = { - x = 0; - y = -110; - z = -200.584; - }; - rightFootRot1 = { - x = -0.1; - y = -0; - z = 0; - }; - rightFootRot2 = { - x = -0.1; - y = -0; - z = 0; - }; - leftArmTra1 = { - x = 0; - y = 130; - z = 80; - }; - leftArmTra2 = { - x = 0; - y = 130; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 0; - y = -180; - z = 80; - }; - rightArmTra2 = { - x = 0; - y = -180; - z = 80; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 200; - leftFootTra1 = { - x = 0; - y = -6.5; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = -6.5; - z = -225; - }; - leftFootRot1 = { - x = -0.05; - y = -0; - z = -0; - }; - leftFootRot2 = { - x = -0.05; - y = -0; - z = -0; - }; - rightFootTra1 = { - x = 0; - y = -100; - z = -200; - }; - rightFootTra2 = { - x = 0; - y = -100; - z = -200; - }; - rightFootRot1 = { - x = -0.05; - y = -0; - z = 0; - }; - rightFootRot2 = { - x = -0.05; - y = -0; - z = 0; - }; - leftArmTra1 = { - x = 0; - y = 130; - z = 80; - }; - leftArmTra2 = { - x = 0; - y = 130; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 0; - y = -180; - z = 80; - }; - rightArmTra2 = { - x = 0; - y = -180; - z = 80; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - }, - { - duration = 1000; - leftFootTra1 = { - x = 0; - y = 55; - z = -225; - }; - leftFootTra2 = { - x = 0; - y = 55; - z = -225; - }; - leftFootRot1 = { - x = 0; - y = -0; - z = -0; - }; - leftFootRot2 = { - x = 0; - y = -0; - z = -0; - }; - rightFootTra1 = { - x = 0; - y = -55; - z = -225; - }; - rightFootTra2 = { - x = 0; - y = -55; - z = -225; - }; - rightFootRot1 = { - x = 0; - y = -0; - z = 0; - }; - rightFootRot2 = { - x = 0; - y = -0; - z = 0; - }; - leftArmTra1 = { - x = 0; - y = 130; - z = 80; - }; - leftArmTra2 = { - x = 0; - y = 130; - z = 80; - }; - leftHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - leftHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - rightArmTra1 = { - x = 0; - y = -130; - z = 80; - }; - rightArmTra2 = { - x = 0; - y = -130; - z = 80; - }; - rightHandRot1 = { - x = 0; - y = 0; - z = 0; - }; - rightHandRot2 = { - x = 0; - y = 0; - z = 0; - }; - comTra1 = { - x = 10; - y = 0; - }; - comTra2 = { - x = 10; - y = 0; - }; - headTra1 = { - x = 0; - y = 0; - }; - headTra2 = { - x = 0; - y = 0; - }; - odometryOffset = { - x = 0; - y = 0; - z = 0; - }; - } -]; diff --git a/Config/KickEngine/kickMiddle.kmc b/Config/Kicks/KickEngine/kickMiddle.kmc similarity index 95% rename from Config/KickEngine/kickMiddle.kmc rename to Config/Kicks/KickEngine/kickMiddle.kmc index be3f0d60..8f62a18d 100644 --- a/Config/KickEngine/kickMiddle.kmc +++ b/Config/Kicks/KickEngine/kickMiddle.kmc @@ -1,3 +1,17 @@ +horizontalInaccuracy = 1.3; +distanceAdjustable = false; +kickBlind = false; +kickWithLeftCondition = onLeftSide; +switchKickFoot = false; +ballOffset = { + x = 0.19; + y = 0.055; +}; +kickAngle = 0deg; +kickDistance = [ + 3.7, + 4.7 +]; footOrigin = { x = 0; y = 55; @@ -26,12 +40,12 @@ headOrigin = { x = 0; y = 0; }; -kpx = 1; +kpx = 1.3; kix = 0; -kdx = 0; -kpy = 1; +kdx = 0.0075; +kpy = 1.3; kiy = 0; -kdy = 0.01; +kdy = 0.0075; preview = 5; loop = false; autoComTra = true; @@ -39,6 +53,7 @@ ignoreHead = true; phaseParameters = [ { duration = 500; + kick = false; leftFootTra1 = { x = 0; y = 100; @@ -143,6 +158,7 @@ phaseParameters = [ }, { duration = 500; + kick = false; leftFootTra1 = { x = -130; y = 100; @@ -247,6 +263,7 @@ phaseParameters = [ }, { duration = 80; + kick = false; leftFootTra1 = { x = 150; y = 100; @@ -351,6 +368,7 @@ phaseParameters = [ }, { duration = 700; + kick = false; leftFootTra1 = { x = 0; y = 100; @@ -455,6 +473,7 @@ phaseParameters = [ }, { duration = 1000; + kick = false; leftFootTra1 = { x = 0; y = 55; diff --git a/Config/KickEngine/kickMiddleFast.kmc b/Config/Kicks/KickEngine/kickMiddleFast.kmc similarity index 96% rename from Config/KickEngine/kickMiddleFast.kmc rename to Config/Kicks/KickEngine/kickMiddleFast.kmc index 97b6d586..7e92f029 100644 --- a/Config/KickEngine/kickMiddleFast.kmc +++ b/Config/Kicks/KickEngine/kickMiddleFast.kmc @@ -1,3 +1,15 @@ +horizontalInaccuracy = 1.5; +distanceAdjustable = false; +kickBlind = false; +kickWithLeftCondition = onLeftSide; +switchKickFoot = false; +ballOffset = { + x = 0.190; + y = 0.055; +}; +kickAngle = 0deg; +kickDistance = [2.5, 4.2]; + footOrigin = { x = 0; y = 55; @@ -39,6 +51,7 @@ ignoreHead = true; phaseParameters = [ { duration = 24; + kick = false; leftFootTra1 = { x = 0; y = 55; @@ -143,6 +156,7 @@ phaseParameters = [ }, { duration = 400; + kick = false; leftFootTra1 = { x = 0; y = 65; @@ -247,6 +261,7 @@ phaseParameters = [ }, { duration = 140; + kick = false; leftFootTra1 = { x = -40; y = 95; @@ -351,6 +366,7 @@ phaseParameters = [ }, { duration = 150; + kick = false; leftFootTra1 = { x = 80; y = 100; @@ -455,6 +471,7 @@ phaseParameters = [ }, { duration = 250; + kick = false; leftFootTra1 = { x = -0; y = 80; @@ -559,6 +576,7 @@ phaseParameters = [ }, { duration = 1000; + kick = false; leftFootTra1 = { x = 0; y = 40; diff --git a/Config/keeperKick45 b/Config/Kicks/WalkingEngine/keeperKick45.cfg similarity index 78% rename from Config/keeperKick45 rename to Config/Kicks/WalkingEngine/keeperKick45.cfg index 63408f75..fd6ae66d 100644 --- a/Config/keeperKick45 +++ b/Config/Kicks/WalkingEngine/keeperKick45.cfg @@ -1,13 +1,19 @@ +horizontalInaccuracy = 1.31; +distanceAdjustable = false; +kickBlind = false; +kickWithLeftCondition = onLeftSide; +switchKickFoot = true; + ballOffset = { x = 0.175; y = 0.05; //adjust the speed of the kick using this variable }; kickAngle = 45deg; -kickDistance = [0, 4]; -translationThresholdXFront = 0.015; -translationThresholdXBack = 0.015; -translationThresholdY = 0.010; -rotationThreshold = 10deg; +kickDistance = [0, 4]; //todo das ist nicht richtig +translationThresholdXFront = 0.020; +translationThresholdXBack = 0.020; +translationThresholdY = 0.015; +rotationThreshold = 12.5deg; timeUntilKickHackHip = 0; kickHackDurationHip = 0; @@ -16,6 +22,8 @@ kickHackHipAngle = 0deg; timeUntilKickHackKnee = 0; kickHackDurationKnee = 0; kickHackKneeAngle = 0deg; +kickHackKneeIntensity = 1.0; +ankleCompensationMultiplier=1.0; steps = [ { diff --git a/Config/Kicks/WalkingEngine/kickHack.cfg b/Config/Kicks/WalkingEngine/kickHack.cfg new file mode 100644 index 00000000..daf7035a --- /dev/null +++ b/Config/Kicks/WalkingEngine/kickHack.cfg @@ -0,0 +1,52 @@ +horizontalInaccuracy = 0.30; +distanceAdjustable = false; +kickBlind = false; +kickWithLeftCondition = onLeftSide; +switchKickFoot = false; + +ballOffset = { + x = 0.18; + y = 0.05; +}; +kickAngle = 0; +kickDistance = [0.5, 1.0]; +translationThresholdXFront = 0.025; +translationThresholdXBack = 0.015; +translationThresholdY = 0.025; +rotationThreshold = 7.5deg; + +timeUntilKickHackHip = 0; +kickHackDurationHip = 0; +kickHackHipAngle = 0deg; + +timeUntilKickHackKnee = 0.244; +kickHackDurationKnee = 5; +kickHackKneeAngle = -7deg; +kickHackKneeIntensity = 1.0; + +ankleCompensationMultiplier = 0.75; + +steps = [ + { + footPos = [ + { + rotation = 0; + translation = {x = 0.0; y = 0;}; + }, + { + rotation = 0; + translation = {x = 0.0; y = 0;}; + } + ]; + duration = 250; + onFloor = [false, true]; + kick = true; + swingFootTraj = [ + {x = 0.0; y = 0.0; z = 0.0;}, + {x = 0.0; y = 0.0; z = 0.025;}, + {x = 0.025; y = 0.0; z = 0.025;}, + {x = 0.0; y = 0.0; z = 0.025;}, + {x = 0.0; y = 0.0; z = 0.0;} + ]; + } +]; diff --git a/Config/Kicks/WalkingEngine/kickHackLong.cfg b/Config/Kicks/WalkingEngine/kickHackLong.cfg new file mode 100644 index 00000000..c65dba81 --- /dev/null +++ b/Config/Kicks/WalkingEngine/kickHackLong.cfg @@ -0,0 +1,54 @@ +horizontalInaccuracy = 0.30; +distanceAdjustable = false; +kickBlind = false; +kickWithLeftCondition = onLeftSide; +switchKickFoot = false; + +ballOffset = { + x = 0.19; + y = 0.05; +}; +kickAngle = 0; +kickDistance = [1.5, 2.2]; +translationThresholdXFront = 0.025; +translationThresholdXBack = 0.015; +translationThresholdY = 0.025; +rotationThreshold = 7.5deg; + +timeUntilKickHackHip = 0; +kickHackDurationHip = 0; +kickHackHipAngle = 0deg; + +timeUntilKickHackKnee = 0.244; +kickHackDurationKnee = 5; +kickHackKneeAngle = 0deg; +kickHackKneeIntensity = 1.0; + +kickHackKneeIntensity = 0.4; + +ankleCompensationMultiplier = 0.75; + +steps = [ + { + footPos = [ + { + rotation = 0; + translation = {x = 0.0; y = 0;}; + }, + { + rotation = 0; + translation = {x = 0.0; y = 0;}; + } + ]; + duration = 250; + onFloor = [false, true]; + kick = true; + swingFootTraj = [ + {x = 0.0; y = 0.0; z = 0.0;}, + {x = 0.0; y = 0.0; z = 0.025;}, + {x = 0.025; y = 0.0; z = 0.025;}, + {x = 0.0; y = 0.0; z = 0.025;}, + {x = 0.0; y = 0.0; z = 0.0;} + ]; + } +]; diff --git a/Config/Kicks/WalkingEngine/kickHackShort.cfg b/Config/Kicks/WalkingEngine/kickHackShort.cfg new file mode 100644 index 00000000..3859916c --- /dev/null +++ b/Config/Kicks/WalkingEngine/kickHackShort.cfg @@ -0,0 +1,53 @@ +horizontalInaccuracy = 0.30; +distanceAdjustable = false; +kickBlind = false; +kickWithLeftCondition = onLeftSide; +switchKickFoot = false; + +ballOffset = { + x = 0.19; + y = 0.05; +}; +kickAngle = 0; +kickDistance = [1.5, 2.2]; +translationThresholdXFront = 0.025; +translationThresholdXBack = 0.015; +translationThresholdY = 0.025; +rotationThreshold = 7.5deg; + +timeUntilKickHackHip = 0; +kickHackDurationHip = 0; +kickHackHipAngle = 0deg; + +timeUntilKickHackKnee = 0.244; +kickHackDurationKnee = 5; +kickHackKneeAngle = 0deg; + +kickHackKneeIntensity = 0.4; + +ankleCompensationMultiplier = 0.75; + +steps = [ + { + footPos = [ + { + rotation = 0; + translation = {x = 0.0; y = 0;}; + }, + { + rotation = 0; + translation = {x = 0.0; y = 0;}; + } + ]; + duration = 250; + onFloor = [false, true]; + kick = true; + swingFootTraj = [ + {x = 0.0; y = 0.0; z = 0.0;}, + {x = 0.0; y = 0.0; z = 0.025;}, + {x = 0.025; y = 0.0; z = 0.025;}, + {x = 0.0; y = 0.0; z = 0.025;}, + {x = 0.0; y = 0.0; z = 0.0;} + ]; + } +]; diff --git a/Config/rotateKick45 b/Config/Kicks/WalkingEngine/rotateKick45.cfg similarity index 79% rename from Config/rotateKick45 rename to Config/Kicks/WalkingEngine/rotateKick45.cfg index 07b2da7a..b66e617a 100644 --- a/Config/rotateKick45 +++ b/Config/Kicks/WalkingEngine/rotateKick45.cfg @@ -1,13 +1,19 @@ +horizontalInaccuracy = 1.31; +distanceAdjustable = false; +kickBlind = false; +kickWithLeftCondition = onLeftSide; +switchKickFoot = true; + ballOffset = { x = 0.175; y = 0.02; //adjust the speed of the kick using this variable }; kickAngle = 45deg; -kickDistance = [0, 1.45]; -translationThresholdXFront = 0.015; -translationThresholdXBack = 0.015; -translationThresholdY = 0.015; -rotationThreshold = 10deg; +kickDistance = [0.8, 2.3]; +translationThresholdXFront = 0.020; +translationThresholdXBack = 0.020; +translationThresholdY = 0.020; +rotationThreshold = 12.5deg; timeUntilKickHackHip = 0; kickHackDurationHip = 0; @@ -16,6 +22,8 @@ kickHackHipAngle = 0deg; timeUntilKickHackKnee = 0; kickHackDurationKnee = 0; kickHackKneeAngle = 0deg; +kickHackKneeIntensity = 1.0; +ankleCompensationMultiplier=1.0; steps = [ { diff --git a/Config/sideKickOuter45 b/Config/Kicks/WalkingEngine/sideKickOuter45.cfg similarity index 76% rename from Config/sideKickOuter45 rename to Config/Kicks/WalkingEngine/sideKickOuter45.cfg index 4899fc59..6fb6573e 100644 --- a/Config/sideKickOuter45 +++ b/Config/Kicks/WalkingEngine/sideKickOuter45.cfg @@ -1,13 +1,19 @@ +horizontalInaccuracy = 0.7; +distanceAdjustable = false; +kickBlind = true; +kickWithLeftCondition = leftFootIsClosest; +switchKickFoot = false; + ballOffset = { x = 0.12; y = 0.14; }; -kickAngle = 45deg; -kickDistance = [0, 1.35]; -translationThresholdXFront = 0.015; -translationThresholdXBack = 0.03; -translationThresholdY = 0.03; -rotationThreshold = 10deg; +kickAngle = 65deg; +kickDistance = [1.0, 1.6]; +translationThresholdXFront = 0.020; +translationThresholdXBack = 0.035; +translationThresholdY = 0.035; +rotationThreshold = 12.5deg; timeUntilKickHackHip = 0; kickHackDurationHip = 0; @@ -16,6 +22,9 @@ kickHackHipAngle = 0deg; timeUntilKickHackKnee = 0; kickHackDurationKnee = 0; kickHackKneeAngle = 0deg; +kickHackKneeIntensity = 1.0; + +ankleCompensationMultiplier = 1; steps = [ { diff --git a/Config/sideKickOuterFoot b/Config/Kicks/WalkingEngine/sideKickOuterFoot.cfg similarity index 75% rename from Config/sideKickOuterFoot rename to Config/Kicks/WalkingEngine/sideKickOuterFoot.cfg index a96f1476..ffffb633 100644 --- a/Config/sideKickOuterFoot +++ b/Config/Kicks/WalkingEngine/sideKickOuterFoot.cfg @@ -1,21 +1,29 @@ +horizontalInaccuracy = 0.45; +distanceAdjustable = false; +kickBlind = true; +kickWithLeftCondition = leftFootIsClosest; +switchKickFoot = false; + ballOffset = { x = 0.00; y = 0.18; }; kickAngle = 90deg; -kickDistance = [0, 1.4]; -translationThresholdXFront = 0.03; -translationThresholdXBack = 0.02; -translationThresholdY = 0.02; -rotationThreshold = 4deg; +kickDistance = [1.1, 1.9]; +translationThresholdXFront = 0.035; +translationThresholdXBack = 0.025; +translationThresholdY = 0.025; +rotationThreshold = 6.5deg; timeUntilKickHackHip = 0; kickHackDurationHip = 0; kickHackHipAngle = 0deg; +kickHackKneeIntensity = 1.0; timeUntilKickHackKnee = 0; kickHackDurationKnee = 0; kickHackKneeAngle = 0deg; +ankleCompensationMultiplier=1.0; steps = [ { //Frame 1 diff --git a/Config/Logs/.gitignore b/Config/Logs/.gitignore deleted file mode 100644 index 72e8ffc0..00000000 --- a/Config/Logs/.gitignore +++ /dev/null @@ -1 +0,0 @@ -* diff --git a/Config/Robots/Nao/Body/modules.cfg b/Config/Robots/Nao/Body/modules.cfg index e6dc4dbb..ffdd3f6e 100644 --- a/Config/Robots/Nao/Body/modules.cfg +++ b/Config/Robots/Nao/Body/modules.cfg @@ -2,4 +2,5 @@ representationProviders = [ {representation = AudioData; provider = default;}, {representation = JPEGImage; provider = default;}, {representation = JPEGImageUpper; provider = default;}, + {representation = TeamCommSocket; provider = TeamCommLocalSocketProvider;}, ]; diff --git a/Config/Robots/Nao/Body/motionSettings.cfg b/Config/Robots/Nao/Body/motionSettings.cfg index 4f1a3dd3..6b16b6c4 100644 --- a/Config/Robots/Nao/Body/motionSettings.cfg +++ b/Config/Robots/Nao/Body/motionSettings.cfg @@ -1,10 +1,10 @@ comHeight = 262.0; -standUpMotionFront = standUpFrontNaoFast; -standUpMotionFrontSafe = standUpFrontNaoMed; -standUpMotionFrontSafest = standUpFrontNaoSlow; -standUpMotionBack = standUpBackNaoFast; -standUpMotionBackSafe = standUpBackNaoMed; -standUpMotionBackSafest = standUpBackNaoSlow; +standUpMotionFrontA = standUpFrontNaoFast; +standUpMotionFrontB = standUpFrontNaoFastOld; +standUpMotionFrontC = standUpFrontNaoSlowOld; +standUpMotionBackA = standUpBackNaoFast; +standUpMotionBackB = standUpBackNaoFastOld; +standUpMotionBackC = none; leaveWalkForDive = true; specialActionBalanceList = { specialActionBalanceEntries = [ ]; diff --git a/Config/Robots/Nao/Body/odometryCorrectionTables.cfg b/Config/Robots/Nao/Body/odometryCorrectionTables.cfg index 9436babd..f569858f 100644 --- a/Config/Robots/Nao/Body/odometryCorrectionTables.cfg +++ b/Config/Robots/Nao/Body/odometryCorrectionTables.cfg @@ -1,56 +1,56 @@ backCorrectionTablePreview = { odometryTable = [ - { speed = 25; multiplier = 0.62;}, - { speed = 50; multiplier = 0.62;}, - { speed = 75; multiplier = 0.59;} + { speed = 25; multiplier = 0.57;}, + { speed = 50; multiplier = 0.57;}, + { speed = 75; multiplier = 0.54;} ]; }; forwardCorrectionTablePreview = { odometryTable = [ - { speed = 50; multiplier = 0.61;}, - { speed = 100; multiplier = 0.63;}, - { speed = 200; multiplier = 0.71;} + { speed = 50; multiplier = 0.56;}, + { speed = 100; multiplier = 0.58;}, + { speed = 200; multiplier = 0.66;} ]; }; sideCorrectionTablePreview = { odometryTable = [ - { speed = 50; multiplier = 0.90;}, - { speed = 100; multiplier = 0.89;} + { speed = 50; multiplier = 0.85;}, + { speed = 100; multiplier = 0.84;} ]; }; backCorrectionTable = { odometryTable = [ - { speed = 25; multiplier = 0.62;}, - { speed = 50; multiplier = 0.62;}, - { speed = 75; multiplier = 0.59;} + { speed = 25; multiplier = 0.57;}, + { speed = 50; multiplier = 0.57;}, + { speed = 75; multiplier = 0.54;} ]; }; forwardCorrectionTable = { odometryTable = [ - { speed = 50; multiplier = 0.6;}, - { speed = 100; multiplier = 0.63;}, - { speed = 200; multiplier = 0.71;} + { speed = 50; multiplier = 0.55;}, + { speed = 100; multiplier = 0.58;}, + { speed = 200; multiplier = 0.66;} ]; }; sideCorrectionTable = { odometryTable = [ - { speed = 50; multiplier = 0.90;}, - { speed = 100; multiplier = 0.89;} + { speed = 50; multiplier = 0.85;}, + { speed = 100; multiplier = 0.84;} ]; }; rotCorrectionTable = { odometryTable = [ - { speed = 0.4; multiplier = 1;}, - { speed = 0.8; multiplier = 1;} + { speed = 0.4; multiplier = 0.95;}, + { speed = 0.8; multiplier = 0.95;} ]; }; rot2DCorrectionTable = { odometryTable = [ - { speedX = 0; speedR = 0.2; multiplier = 1;}, - { speedX = 0; speedR = 0.4; multiplier = 1;}, - { speedX = 0; speedR = 0.8; multiplier = 1;}, - { speedX = 200; speedR = 0.2; multiplier = 0.71;}, - { speedX = 200; speedR = 0.4; multiplier = 0.71;}, - { speedX = 200; speedR = 0.8; multiplier = 0.71;}, + { speedX = 0; speedR = 0.2; multiplier = 0.95;}, + { speedX = 0; speedR = 0.4; multiplier = 0.95;}, + { speedX = 0; speedR = 0.8; multiplier = 0.95;}, + { speedX = 200; speedR = 0.2; multiplier = 0.66;}, + { speedX = 200; speedR = 0.4; multiplier = 0.66;}, + { speedX = 200; speedR = 0.8; multiplier = 0.66;}, ]; }; diff --git a/Config/Robots/Nao/Body/robotDimensions.cfg b/Config/Robots/Nao/Body/robotDimensions.cfg index 42bc349b..fe6647f5 100644 --- a/Config/Robots/Nao/Body/robotDimensions.cfg +++ b/Config/Robots/Nao/Body/robotDimensions.cfg @@ -21,4 +21,4 @@ imuOffset = {x = 0; y = 0; z = 85;}; footFront = 104; footBack = 54; footOuter = 44; -footInner = 44; \ No newline at end of file +footInner = 44; diff --git a/Config/Robots/Nao/Body/usConfiguration.cfg b/Config/Robots/Nao/Body/usConfiguration.cfg deleted file mode 100644 index 4cccaba0..00000000 --- a/Config/Robots/Nao/Body/usConfiguration.cfg +++ /dev/null @@ -1,22 +0,0 @@ -min = 230; -max = 1400; -leftToLeft = { - rotation = 22.5deg; - translation = {x = 50.7; y = 37.85;}; - openingAngle = 90deg; -}; -rightToRight = { - rotation = -22.5deg; - translation = {x = 50.7; y = -37.85;}; - openingAngle = 90deg; -}; -leftToRight = { - rotation = -2.5deg; - translation = {x = 50.7; y = -3.75;}; - openingAngle = 90deg; -}; -rightToLeft = { - rotation = 2.5deg; - translation = {x = 50.7; y = 3.75;}; - openingAngle = 90deg; -}; \ No newline at end of file diff --git a/Config/Robots/V6/csConverter2019.cfg b/Config/Robots/V6/csConverter2019.cfg index f4ae2c26..dc04c5d2 100644 --- a/Config/Robots/V6/csConverter2019.cfg +++ b/Config/Robots/V6/csConverter2019.cfg @@ -7,33 +7,50 @@ legJointBalanceParams = { targetAngleX = 0deg; targetAngleY = 0deg; ankleParams = { - pidMultiplicatorX = 2.5; - p_x = 0.2; + zeroPidMultiplicatorX = 0.125; + pidMultiplicatorX = 0.25; + p_x = 0.5; i_x = 0; - d_x = 5; - pidMultiplicatorY = 3; - p_y = -0.05; + d_x = 2.75; + zeroPidMultiplicatorY = 0.375; + pidMultiplicatorY = 1; + p_y = -0.1; i_y = 0; - d_y = -2; + d_y = -2.75; comX_p = 0; }; hipParams = { - pidMultiplicatorX = 2.5; - p_x = -0.2; + zeroPidMultiplicatorX = 0.125; + pidMultiplicatorX = 0.25; + p_x = -0.5; i_x = 0; - d_x = -3; - pidMultiplicatorY = 3; - p_y = -0.05; + d_x = -2.75; + zeroPidMultiplicatorY = 0.375; + pidMultiplicatorY = 1; + p_y = -0.1; i_y = 0; - d_y = -2; + d_y = -2.75; comX_p = 0; }; - ankleHipRatioX = 0.5; - ankleHipRatioY = 0.5; + ankleHipRatioX = 0.7; + ankleHipRatioY = 0.7; +}; +jointErrorInfluenceParams = { + zeroPidMultiplicatorX = 1; + pidMultiplicatorX = 1; + p_x = 0.01; + i_x = 0; + d_x = 0.0001; + zeroPidMultiplicatorY = 1; + pidMultiplicatorY = 1; + p_y = 0.01; + i_y = 0; + d_y = 0.0001; + comX_p = 0; }; comShiftParameters = { - accXAlpha = -0.5; + accXAlpha = -0.75; stepAccAlpha = 0; accInterpolTime = 200; }; -rotateLegWithBody = false; +rotateLegWithBody = true; diff --git a/Config/Robots/V6/flipmObserverGains.cfg b/Config/Robots/V6/flipmObserverGains.cfg index 09ba0f50..94ee6081 100644 --- a/Config/Robots/V6/flipmObserverGains.cfg +++ b/Config/Robots/V6/flipmObserverGains.cfg @@ -14,9 +14,9 @@ sensorControlRatioObserverY = [ ]; maxDiffCoMClip = 0.09; maxDiffACCClip = 3.5; -CoM1Provider = MRE_CoM; -ACC1Provider = ZMP_Acc; -CoM2Provider = IMU_CoM; +CoM1Provider = MRECoM; +ACC1Provider = ZMPAcc; +CoM2Provider = IMUCoM; CoM1Delay = 2; CoM1OffsetX = 0; CoM1OffsetY = 0; diff --git a/Config/Robots/V6/flipmParamsProvider.cfg b/Config/Robots/V6/flipmParamsProvider.cfg index 86ac090d..3f9f9e86 100644 --- a/Config/Robots/V6/flipmParamsProvider.cfg +++ b/Config/Robots/V6/flipmParamsProvider.cfg @@ -1,20 +1,15 @@ useRCS = false; +autoRecalculate = true; duplicateXParams = true; -DARE_maxIterations = 1000000; -DARE_threshold = 1e-06; -threadPriority = 0; -useRobustIterationMethod = false; paramsX = { - m = 0.5; + m = 2; M = 5.5; - g = 9.81; z_h = 0.27; - dt = 0.012; - D = 5000; - E = 300; - Qe = 100; - Qx = 100; - R = 1e-10; + D = 2500; + E = 200; + Qe = 1; + Qx = 0; + R = 1e-06; Ql = { cols = [ { @@ -108,9 +103,7 @@ paramsX = { paramsY = { m = 3.5; M = 5.5; - g = 9.81; z_h = 0.26; - dt = 0.01; D = 1000; E = 200; Qe = 100; diff --git a/Config/Robots/V6/flipmStateProvider.cfg b/Config/Robots/V6/flipmStateProvider.cfg new file mode 100644 index 00000000..c3a2e487 --- /dev/null +++ b/Config/Robots/V6/flipmStateProvider.cfg @@ -0,0 +1,90 @@ +useRCS = false; +anglesource = imuModel; +acc1Filter = 1; +acc2Filter = 0.25; +vel1Filter = 1; +vel2Filter = 0.5; +xParams = { + gainsFactor = 0.15; + gains = { + elems = [ + 0, + 0, + 0, + 0.35, + 0, + 0 + ]; + }; + scale = { + elems = [ + 1, + 1, + 1, + 1, + 1, + 1 + ]; + }; + offset = { + elems = [ + 0, + 0, + 0, + 0, + 0, + 0 + ]; + }; +}; +yParams = { + gainsFactor = 0.15; + gains = { + elems = [ + 0, + 0, + 0, + 0.35, + 0, + 0 + ]; + }; + scale = { + elems = [ + 1, + 1, + 1, + 1, + 1, + 1 + ]; + }; + offset = { + elems = [ + 0, + 0, + 0, + 0, + 0, + 0 + ]; + }; +}; +delays = { + elems = [ + 0, + 0, + 0, + 0, + 0, + 9 + ]; +}; +providers = { + CoM1Provider = MRECoM; + Vel1Provider = integratedCoM; + Acc1Provider = integratedAcc; + CoM2Provider = IMUCoM; + Vel2Provider = integratedCoM; + Acc2Provider = IMUAcc; +}; diff --git a/Config/Robots/V6/walkCalibration.cfg b/Config/Robots/V6/walkCalibration.cfg index a23b8520..bc136287 100644 --- a/Config/Robots/V6/walkCalibration.cfg +++ b/Config/Robots/V6/walkCalibration.cfg @@ -12,10 +12,9 @@ legJointCalibration = [ 0deg, 0deg ]; -comOffset = { - x = 0; - y = 0; - z = 0; +fieldInclination = { + x = 0deg; + y = 0deg; }; imuAngleOffsets = [ { diff --git a/Config/Robots/V6/walkingParamsFLIPM.cfg b/Config/Robots/V6/walkingParamsFLIPM.cfg index ac109ff4..5f67d44f 100644 --- a/Config/Robots/V6/walkingParamsFLIPM.cfg +++ b/Config/Robots/V6/walkingParamsFLIPM.cfg @@ -1,17 +1,17 @@ acceleration = { - maxAccR = 60deg; - maxAccXForward = 0.15; + maxAccR = 45deg; + maxAccXForward = 0.1; maxAccXBackward = 0.08; maxAccY = 0.15; }; comOffsets = { - tiltFixed = 0; + tiltFixed = 0deg; tiltSpeedDependent = [ - 0, - -0.003 + 0deg, + 0deg ]; - xArmContact = 0.005; - xFixed = 0.020; + xArmContact = 0.0075; + xFixed = 0.02; xSpeedDependent = 0; yArmContact = [ 0, @@ -19,20 +19,20 @@ comOffsets = { ]; yFixed = 0; ySpeedDependent = [ - 0.0025, - -0.003 + 0.0055, + -0.006 ]; }; footMovement = { doubleSupportRatio = 0.05; - footPitch = -1deg; + footPitch = 0deg; footRoll = 0deg; footPitchPD = [ - 0, - 0 + 0.5, + 0.025 ]; footYDistance = 0.05; - leadingSideStepSpeedUp = 1.2; + leadingSideStepSpeedUp = 1.25; forwardPolygon = [ 0, 0.1, @@ -58,8 +58,8 @@ footMovement = { 0.25, 0.75, 1, - 0.5, - 0.25 + 0.75, + 0.1 ]; polygonLeft = [ 0, @@ -73,12 +73,12 @@ footMovement = { 0, 0 ]; - maxStepDuration = 0.55; + maxStepDuration = 0.65; minStepDuration = 0.45; stepHeight = [ 0.025, - 0.030, - 0.035 + 0.025, + 0.025 ]; }; jointCalibration = { @@ -123,30 +123,33 @@ jointCalibration = { 0 ]; }; -jointSensorDelayFrames = 5; +jointSensorDelayFrames = 3; imuSensorDelayFrames = 1; -outFilterOrder = 5; +outFilterOrder = 3; speedLimits = { - speedFactor = 0.75; - r = 75deg; - rOnly = 90deg; - xBackward = 200; - xForward = 250; - xForwardArmContact = 220; - xForwardOmni = 200; - y = 220; - yArmContact = 200; + speedFactor = 1; + r = 45deg; + rOnly = 75deg; + xBackward = 180; + xForward = 220; + xForwardArmContact = 200; + xForwardOmni = 180; + y = 180; + yArmContact = 150; }; walkTransition = { crouchingDownPhaseLength = 10; fallDownAngleMinMaxX = [ - -4deg, - 4deg + -1.75deg, + 1.75deg ]; fallDownAngleMinMaxY = [ - -2deg, - 4deg + -1.5deg, + 3.0deg ]; stopSpeedThresholdX = 0.01; stopSpeedThresholdY = 0.01; + fallDownAngleFront = 26deg; + fallDownAngleSide = 24deg; + fallDownAngleBack = -20deg; }; diff --git a/Config/Scenes/Game.ros2 b/Config/Scenes/Game.ros2 index 062b5361..a5adb8d9 100644 --- a/Config/Scenes/Game.ros2 +++ b/Config/Scenes/Game.ros2 @@ -12,7 +12,7 @@ - + @@ -42,22 +42,22 @@ - + - + - + - + diff --git a/Config/Scenes/Game7vs7.con b/Config/Scenes/Game7vs7.con new file mode 100644 index 00000000..92756bf4 --- /dev/null +++ b/Config/Scenes/Game7vs7.con @@ -0,0 +1,7 @@ +call Includes/Normal + +dr debugDrawing3d:representation:RobotPose +call Includes/Behavior3D + +set threads:cognition:config numWorkers = 1; +set threads:motion:config numWorkers = 1; \ No newline at end of file diff --git a/Config/Scenes/Game7vs7.ros2 b/Config/Scenes/Game7vs7.ros2 new file mode 100644 index 00000000..225905e5 --- /dev/null +++ b/Config/Scenes/Game7vs7.ros2 @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Config/Scenes/Game7vs7DummyOpponents.con b/Config/Scenes/Game7vs7DummyOpponents.con new file mode 100644 index 00000000..92756bf4 --- /dev/null +++ b/Config/Scenes/Game7vs7DummyOpponents.con @@ -0,0 +1,7 @@ +call Includes/Normal + +dr debugDrawing3d:representation:RobotPose +call Includes/Behavior3D + +set threads:cognition:config numWorkers = 1; +set threads:motion:config numWorkers = 1; \ No newline at end of file diff --git a/Config/Scenes/Game7vs7DummyOpponents.ros2 b/Config/Scenes/Game7vs7DummyOpponents.ros2 new file mode 100644 index 00000000..702a3ffb --- /dev/null +++ b/Config/Scenes/Game7vs7DummyOpponents.ros2 @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Config/Scenes/GameDummyOpponentsFast.con b/Config/Scenes/GameDummyOpponentsFast.con index d4fa3495..e21bae38 100644 --- a/Config/Scenes/GameDummyOpponentsFast.con +++ b/Config/Scenes/GameDummyOpponentsFast.con @@ -1,6 +1,7 @@ call Includes/Fast dr debugDrawing3d:representation:RobotPose +call Includes/Behavior3D set threads:cognition:config numWorkers = 1; set threads:motion:config numWorkers = 1; \ No newline at end of file diff --git a/Config/Scenes/GameFast.con b/Config/Scenes/GameFast.con index 7346dbd1..7f0561b7 100644 --- a/Config/Scenes/GameFast.con +++ b/Config/Scenes/GameFast.con @@ -1,6 +1,7 @@ call Includes/Fast dr debugDrawing3d:representation:RobotPose +call Includes/Behavior3D set threads:cognition:config numWorkers = 1; set threads:motion:config numWorkers = 1; diff --git a/Config/Scenes/GameFast.ros2 b/Config/Scenes/GameFast.ros2 index 5735c291..95b4b59c 100644 --- a/Config/Scenes/GameFast.ros2 +++ b/Config/Scenes/GameFast.ros2 @@ -12,7 +12,7 @@ - + @@ -42,22 +42,22 @@ - + - + - + - + diff --git a/Config/Scenes/GameFast7vs7.con b/Config/Scenes/GameFast7vs7.con new file mode 100644 index 00000000..7f0561b7 --- /dev/null +++ b/Config/Scenes/GameFast7vs7.con @@ -0,0 +1,7 @@ +call Includes/Fast + +dr debugDrawing3d:representation:RobotPose +call Includes/Behavior3D + +set threads:cognition:config numWorkers = 1; +set threads:motion:config numWorkers = 1; diff --git a/Config/Scenes/GameFast7vs7.ros2 b/Config/Scenes/GameFast7vs7.ros2 new file mode 100644 index 00000000..b65b9740 --- /dev/null +++ b/Config/Scenes/GameFast7vs7.ros2 @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Config/Scenes/GameFast7vs7DummyOpponents.con b/Config/Scenes/GameFast7vs7DummyOpponents.con new file mode 100644 index 00000000..7f0561b7 --- /dev/null +++ b/Config/Scenes/GameFast7vs7DummyOpponents.con @@ -0,0 +1,7 @@ +call Includes/Fast + +dr debugDrawing3d:representation:RobotPose +call Includes/Behavior3D + +set threads:cognition:config numWorkers = 1; +set threads:motion:config numWorkers = 1; diff --git a/Config/Scenes/GameFast7vs7DummyOpponents.ros2 b/Config/Scenes/GameFast7vs7DummyOpponents.ros2 new file mode 100644 index 00000000..e6372e26 --- /dev/null +++ b/Config/Scenes/GameFast7vs7DummyOpponents.ros2 @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Config/Scenes/GameFast7vs7PerceptOracle.con b/Config/Scenes/GameFast7vs7PerceptOracle.con new file mode 100644 index 00000000..780b51fd --- /dev/null +++ b/Config/Scenes/GameFast7vs7PerceptOracle.con @@ -0,0 +1,7 @@ +call Includes/PerceptOracle + +dr debugDrawing3d:representation:RobotPose +call Includes/Behavior3D + +set threads:cognition:config numWorkers = 1; +set threads:motion:config numWorkers = 1; diff --git a/Config/Scenes/GameFast7vs7PerceptOracle.ros2 b/Config/Scenes/GameFast7vs7PerceptOracle.ros2 new file mode 100644 index 00000000..b65b9740 --- /dev/null +++ b/Config/Scenes/GameFast7vs7PerceptOracle.ros2 @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Config/Scenes/Includes/AutoCalibrator.con b/Config/Scenes/Includes/AutoCalibrator.con new file mode 100644 index 00000000..40cf60d7 --- /dev/null +++ b/Config/Scenes/Includes/AutoCalibrator.con @@ -0,0 +1,26 @@ +vd representation:WalkCalibration +vd parameters:AutoCalibrator + +########################## +# Auto Calibration Plots # +########################## +vp CoPError.x 249 -0.071 0.03 +vpd CoPError.x module:AutoCalibrator:coPError.x green + +vp CoPError.y 249 -0.03 0.03 +vpd CoPError.y module:AutoCalibrator:coPError.y red + +vp bodyGravityError 249 -0.01 0.01 +vpd bodyGravityError module:AutoCalibrator:bodyGravityError.x green + +vp gyroVariance 249 -0.01 0.01 +vpd gyroVariance module:AutoCalibrator:gyroVariance green +vpd gyroVariance module:AutoCalibrator:gyroThreshold red + +######### +# Echos # +######### +echo dr module:AutoCalibrator:resetCalibration +echo dr module:AutoCalibrator:calibrateBodyAngle +echo dr module:AutoCalibrator:calibrateWalk +echo dr module:AutoCalibrator:saveCalibration diff --git a/Config/Scenes/Includes/Ballchaser.con b/Config/Scenes/Includes/Ballchaser.con index 886187b4..8e3c83c8 100644 --- a/Config/Scenes/Includes/Ballchaser.con +++ b/Config/Scenes/Includes/Ballchaser.con @@ -1,16 +1,30 @@ call Includes/Behavior call Includes/Behavior3D + + vd representation:Ballchaser -vfd worldState -vfd worldState representation:RobotMap -echo vfd worldState module:HeatMapProvider:HeatMap -vfd worldState behavior:BallchaserProvider:KickManager:KickRange -vfd worldState behavior:BallchaserProvider:KickManager:Blocked -vfd worldState behavior:BallchaserProvider:KickManager:TargetFree -vfd worldState behavior:KickManager:ExecutableKicks:Freely +vfd worldState representation:RobotMap + + + +echo Draw HeatMap: +echo vfd worldState module:HeatMapProvider + + +echo Draw Danger-Area: +echo vfd worldState behavior:BallchaserProvider:KickManager:Danger + +echo Draw KickWheel: +echo vfd worldState behavior:KickWheelProvider:kickWheel + +echo Draw KickRange: +echo worldState behavior:BallchaserProvider:KickManager:KickRange +vfd worldState behavior:KickManager:ExecutableKicks:Freely vfd worldState behavior:KickManager:ExecutableKicks:InGrid off -vfd worldState behavior:KickManager:ExecutableKicks:Width \ No newline at end of file +vfd worldState behavior:KickManager:ExecutableKicks:Width +echo Draw TargetArea: +echo vfd worldState behavior:KickManager:ExecutableKicks:TargetArea \ No newline at end of file diff --git a/Config/Scenes/Includes/Behavior3D.con b/Config/Scenes/Includes/Behavior3D.con index 4522cd60..136a2540 100644 --- a/Config/Scenes/Includes/Behavior3D.con +++ b/Config/Scenes/Includes/Behavior3D.con @@ -1,3 +1,4 @@ +dr debugDrawing3d:representation:BallChaserDecision dr debugDrawing3d:representation:RoleSymbols dr debugDrawing3d:representation:Path dr debugDrawing3d:representation:RoleAssignment diff --git a/Config/Scenes/Includes/CNN-Log.con b/Config/Scenes/Includes/CNN-Log.con deleted file mode 100644 index 4bdb2212..00000000 --- a/Config/Scenes/Includes/CNN-Log.con +++ /dev/null @@ -1,8 +0,0 @@ -mr BallPercept CLIPBallPerceptor -vid upper module:CLIPBallPerceptor:testCircles:upper -vid lower module:CLIPBallPerceptor:testCircles:lower -log remove idYoloInput -log remove idYoloInputUpper -log once -get parameters:CLIPBallPerceptor -echo set parameters:CLIPBallPerceptor addExtraScan = true; numberOfScanLines = 16; useCNN = true; useCNNOnly = true; logTestCircles = false; minFittingPoints = 5; minFittingPointsForSafeBall = 8; maxFarPointsOnHull = 0; minDistFromImageBorder = 4; maxColorDiff = 30; maxColorJumpDiff = 15; minNumberOfYJumps = 8; validityFactor = 20; minValidity = 0.65; lowestValidity = 0.5; lowerImageUpperBorderDistanceFactor = 1.5; minRadiusInImage = 7; useRobotPose = true; allowBallObstacleOverlap = true; minScore = 0.999999; minScoreUpper = 0.9999; yoloOnlyThreshold = 0.75; yoloFallbackThreshold = 0.5; logPositives = false; useBallValidity = false; cnnIndex = 2; diff --git a/Config/Scenes/Includes/CNN-PNGs.con b/Config/Scenes/Includes/CNN-PNGs.con deleted file mode 100644 index 552267b5..00000000 --- a/Config/Scenes/Includes/CNN-PNGs.con +++ /dev/null @@ -1,7 +0,0 @@ -log stop -log goto 1 -log once -set parameters:CLIPBallPerceptor addExtraScan = true; numberOfScanLines = 16; useCNN = true; useCNNOnly = true; logTestCircles = false; minFittingPoints = 5; minFittingPointsForSafeBall = 8; maxFarPointsOnHull = 0; minDistFromImageBorder = 4; maxColorDiff = 30; maxColorJumpDiff = 15; minNumberOfYJumps = 8; validityFactor = 20; minValidity = 0.65; lowestValidity = 0.5; lowerImageUpperBorderDistanceFactor = 1.5; minRadiusInImage = 8; useRobotPose = true; allowBallObstacleOverlap = true; minScore = 0.99; logPositives = true; useBallValidity = false; cnnIndex = 1; -echo vid upper module:CLIPBallPerceptor:testCircles:upper -set representation:FallDownState state = upright; direction = none; sidewards = noot; odometryRotationOffset = 0; -log start diff --git a/Config/Scenes/Includes/CNN-Remote.con b/Config/Scenes/Includes/CNN-Remote.con deleted file mode 100644 index 291d9deb..00000000 --- a/Config/Scenes/Includes/CNN-Remote.con +++ /dev/null @@ -1,6 +0,0 @@ -call Includes/JPEGImageViews -vid upper module:CLIPBallPerceptor:testCircles:upper -vid lower module:CLIPBallPerceptor:testCircles:lower -get parameters:CLIPBallPerceptor -echo set parameters:CLIPBallPerceptor addExtraScan = true; numberOfScanLines = 16; useCNN = true; useCNNOnly = true; logTestCircles = false; minFittingPoints = 5; minFittingPointsForSafeBall = 8; maxFarPointsOnHull = 0; minDistFromImageBorder = 4; maxColorDiff = 30; maxColorJumpDiff = 15; minNumberOfYJumps = 8; validityFactor = 20; minValidity = 0.65; lowestValidity = 0.5; lowerImageUpperBorderDistanceFactor = 1.5; minRadiusInImage = 7; useRobotPose = true; allowBallObstacleOverlap = true; minScore = 0.999999; minScoreUpper = 0.9999; yoloOnlyThreshold = 0.75; yoloFallbackThreshold = 0.5; logPositives = false; useBallValidity = false; cnnIndex = 2; -get parameters:CLIPPreprocessor \ No newline at end of file diff --git a/Config/Scenes/Includes/CSVLogger.con b/Config/Scenes/Includes/CSVLogger.con deleted file mode 100644 index 7a3614d3..00000000 --- a/Config/Scenes/Includes/CSVLogger.con +++ /dev/null @@ -1,9 +0,0 @@ -echo === CSVLogs are saved to /Config/Logs/CSVLogger/ === -echo -echo dr representation:JointSensorData:CSVLog -echo dr representation:InertialSensorData:CSVLog -echo dr representation:FsrSensorData:CSVLog -echo -echo dr representation:JointSensorData:CSVLog off -echo dr representation:InertialSensorData:CSVLog off -echo dr representation:FsrSensorData:CSVLog off \ No newline at end of file diff --git a/Config/Scenes/Includes/CustomSteps.con b/Config/Scenes/Includes/CustomSteps.con deleted file mode 100644 index 214db797..00000000 --- a/Config/Scenes/Includes/CustomSteps.con +++ /dev/null @@ -1,25 +0,0 @@ - -echo set representation:MotionRequest motion = walk; specialActionRequest = { specialAction = stand; mirror = false; }; walkRequest = { requestType = speed; rotationType = irrelevant; request = { rotation = 0deg; translation = { x = 1; y = 0; }; }; stepRequest = none; }; kickRequest = { kickMotionType = none; mirror = false; dynamical = false; armsBackFix = true; dynPoints = []; kickTarget = { x = 1000; y = 0; }; }; -echo set representation:MotionRequest motion = walk; specialActionRequest = { specialAction = stand; mirror = false; }; walkRequest = { requestType = speed; rotationType = irrelevant; request = { rotation = 0deg; translation = { x = 1; y = 0; }; }; stepRequest = kickHackLong; }; kickRequest = { kickMotionType = none; mirror = false; dynamical = false; armsBackFix = true; dynPoints = []; kickTarget = { x = 1000; y = 0; }; }; -echo set representation:MotionRequest motion = walk; specialActionRequest = { specialAction = stand; mirror = false; }; walkRequest = { requestType = speed; rotationType = irrelevant; request = { rotation = 0deg; translation = { x = 1; y = 0; }; }; stepRequest = none; }; kickRequest = { kickMotionType = none; mirror = false; dynamical = false; armsBackFix = true; dynPoints = []; kickTarget = { x = 1000; y = 0; }; }; -echo set representation:MotionRequest motion = walk; specialActionRequest = { specialAction = stand; mirror = false; }; walkRequest = { requestType = speed; rotationType = irrelevant; request = { rotation = 0deg; translation = { x = 0; y = 0; }; }; stepRequest = none; }; kickRequest = { kickMotionType = none; mirror = false; dynamical = false; armsBackFix = true; dynPoints = []; kickTarget = { x = 1000; y = 0; }; }; -echo dr module:PatternGenerator2017:loadCustomSteps - -echo -echo vd parameters:LimbCombinator -vd parameters:LimbCombinator - -echo -echo vp lKneePitch 200 0 2 -echo vpd lKneePitch module:MotionCombinator:leftLegAngles[3] black - -echo vp rKneePitch 200 0 2 -echo vpd rKneePitch module:MotionCombinator:rightLegAngles[3] black - -vp lKneePitch 200 0 2 -vpd lKneePitch module:MotionCombinator:leftLegAngles[3] black - -vp rKneePitch 200 0 2 -vpd rKneePitch module:MotionCombinator:rightLegAngles[3] black - -echo dr module:PatternGenerator2017:loadCustomSteps diff --git a/Config/Scenes/Includes/CustomStepsSimulator.con b/Config/Scenes/Includes/CustomStepsSimulator.con deleted file mode 100644 index 600547d0..00000000 --- a/Config/Scenes/Includes/CustomStepsSimulator.con +++ /dev/null @@ -1,27 +0,0 @@ - -echo set representation:MotionRequest motion = walk; specialActionRequest = { specialAction = stand; mirror = false; }; walkRequest = { requestType = speed; rotationType = irrelevant; request = { rotation = 0deg; translation = { x = 1; y = 0; }; }; stepRequest = none; }; kickRequest = { kickMotionType = none; mirror = false; dynamical = false; armsBackFix = true; dynPoints = []; kickTarget = { x = 1000; y = 0; }; }; -echo mv 0 0 320 0 0 180 -echo mvb -180 -50 50 -echo set representation:MotionRequest motion = walk; specialActionRequest = { specialAction = stand; mirror = false; }; walkRequest = { requestType = speed; rotationType = irrelevant; request = { rotation = 0deg; translation = { x = 1; y = 0; }; }; stepRequest = kickHackLong; }; kickRequest = { kickMotionType = none; mirror = false; dynamical = false; armsBackFix = true; dynPoints = []; kickTarget = { x = 1000; y = 0; }; }; -echo set representation:MotionRequest motion = walk; specialActionRequest = { specialAction = stand; mirror = false; }; walkRequest = { requestType = speed; rotationType = irrelevant; request = { rotation = 0deg; translation = { x = 1; y = 0; }; }; stepRequest = none; }; kickRequest = { kickMotionType = none; mirror = false; dynamical = false; armsBackFix = true; dynPoints = []; kickTarget = { x = 1000; y = 0; }; }; -echo set representation:MotionRequest motion = walk; specialActionRequest = { specialAction = stand; mirror = false; }; walkRequest = { requestType = speed; rotationType = irrelevant; request = { rotation = 0deg; translation = { x = 0; y = 0; }; }; stepRequest = none; }; kickRequest = { kickMotionType = none; mirror = false; dynamical = false; armsBackFix = true; dynPoints = []; kickTarget = { x = 1000; y = 0; }; }; -echo dr module:PatternGenerator2017:loadCustomSteps - -echo -echo vd parameters:LimbCombinator -vd parameters:LimbCombinator - -echo -echo vp lKneePitch 200 0 2 -echo vpd lKneePitch module:MotionCombinator:leftLegAngles[3] black - -echo vp rKneePitch 200 0 2 -echo vpd rKneePitch module:MotionCombinator:rightLegAngles[3] black - -vp lKneePitch 200 0 2 -vpd lKneePitch module:MotionCombinator:leftLegAngles[3] black - -vp rKneePitch 200 0 2 -vpd rKneePitch module:MotionCombinator:rightLegAngles[3] black - -echo dr module:PatternGenerator2017:loadCustomSteps diff --git a/Config/Scenes/Includes/DebugResponses.con b/Config/Scenes/Includes/DebugResponses.con index a93ebf36..def7ace7 100644 --- a/Config/Scenes/Includes/DebugResponses.con +++ b/Config/Scenes/Includes/DebugResponses.con @@ -5,10 +5,9 @@ dr representation:FsrSensorData dr representation:InertialSensorData dr representation:KeyStates dr representation:SystemSensorData -dr representation:UsSensorData +dr representation:SonarSensorData dr representation:JointSensorData # JointView and SensorView need dr representation:JointRequest -#dr representation:USRequest dr representation:TeamCommSenderOutput diff --git a/Config/Scenes/Includes/FallDownReduction.con b/Config/Scenes/Includes/FallDownReduction.con deleted file mode 100644 index 66c7231a..00000000 --- a/Config/Scenes/Includes/FallDownReduction.con +++ /dev/null @@ -1,3 +0,0 @@ -vp fallDownAngleReduction 200 0.0 6.0 -vpd fallDownAngleReduction module:PatternGenerator2017:fallDownAngleReduction.x green -vpd fallDownAngleReduction module:PatternGenerator2017:fallDownAngleReduction.y red diff --git a/Config/Scenes/Includes/Fast.con b/Config/Scenes/Includes/Fast.con index 1c36a901..d50280d8 100644 --- a/Config/Scenes/Includes/Fast.con +++ b/Config/Scenes/Includes/Fast.con @@ -19,11 +19,13 @@ mr RobotMap OracledWorldModelProvider mr GroundTruthRobotPose OracledWorldModelProvider mr GroundTruthBallModel OracledWorldModelProvider mr GroundTruthMultipleBallModel OracledWorldModelProvider +mr BallPercept OracledPerceptsProvider +mr MultipleBallPercept OracledPerceptsProvider mr FrameInfo CognitionLogDataProvider mr JointSensorData MotionLogDataProvider mr InertialSensorData MotionLogDataProvider mr KeyStates MotionLogDataProvider -mr UsSensorData MotionLogDataProvider +mr SonarSensorData MotionLogDataProvider mr FrameInfo MotionLogDataProvider # Fast, because we do not compute images @@ -31,6 +33,40 @@ ci off mr Image default mr ImageUpper default +# CMCorrector requirement => default +mr CLIPFieldLinesPercept default + +# BehaviorControl requirement => default +mr SideConfidence default +mr RemoteBallModel default + +# TeamCommDataPacker requirement => default +mr LocalRobotMap default + +# CognitionMindfulness requirement => default +mr FieldColors default +mr FieldColorsUpper default + +# Deactivate modeling modules +mr off SelfLocator2017 +mr off KalmanMultiRobotMapProvider +mr off LineMatcher +mr off BallModelProvider + +# Deactivate image processing modules +mr off RobotsPerceptProvider +mr off JerseyColorDetector +mr off RobotOrientationDetector +mr off RobotClassifier +mr off CLIPBallPerceptor +mr off PenaltyCrossClassifier +mr off YoloRobotDetector +mr off CLIPLineFinder +mr off CLIPPreprocessor +mr off TfliteInterpreterProvider +mr off FieldColorProvider +mr off BodyContourProvider + # Deactivate unused modules mr RobotHealth default mr Thumbnail off @@ -38,28 +74,6 @@ mr ThumbnailUpper off mr LowFrameRateImage off mr LowFrameRateImageUpper off -# Deactivate modules depending on images -mr RobotsPercept default -mr RobotsPerceptUpper default -mr CLIPCenterCirclePercept default -mr CLIPGoalPercept default -mr CLIPFieldLinesPercept default -mr PenaltyCrossPercept default -mr SideConfidence default -mr BallHypothesesYolo default -mr PenaltyCrossHypothesesYolo default -mr BallPercept default -mr MultipleBallPercept default -mr ProcessedBallPatches default -mr BallPercept OracledPerceptsProvider -mr MultipleBallPercept OracledPerceptsProvider -mr CLIPPointsPercept off -mr ObstacleBasePoints off -mr BallSpots off -mr BodyContour off -mr BodyContourUpper off -mr YoloInput off -mr YoloInputUpper off mr PNGImageDummy default call Includes/DebugResponses diff --git a/Config/Scenes/Includes/FieldCoverageView.con b/Config/Scenes/Includes/FieldCoverageView.con deleted file mode 100644 index 0d281a8a..00000000 --- a/Config/Scenes/Includes/FieldCoverageView.con +++ /dev/null @@ -1,3 +0,0 @@ -# field views -vf fieldCoverage -vfd fieldCoverage representation:FieldCoverage diff --git a/Config/Scenes/Includes/ImageWriterPNG.con b/Config/Scenes/Includes/ImageWriterPNG.con deleted file mode 100644 index d06a1e7b..00000000 --- a/Config/Scenes/Includes/ImageWriterPNG.con +++ /dev/null @@ -1,5 +0,0 @@ -echo mr SequenceImageUpper default -echo mr SequenceImage default -echo mr ProcessedBallPatches CLIPBallPerceptor -echo mr PNGImageDummy ImageWriterPNG -echo vd parameters:ImageWriterPNG diff --git a/Config/Scenes/Includes/InertialSensorData.con b/Config/Scenes/Includes/InertialSensorData.con deleted file mode 100644 index 9549adf2..00000000 --- a/Config/Scenes/Includes/InertialSensorData.con +++ /dev/null @@ -1,13 +0,0 @@ -vp Gyro 200 -50.0 50.0 -vpd Gyro representation:InertialSensorData:gyro:x red -vpd Gyro representation:InertialSensorData:gyro:y green -vpd Gyro representation:InertialSensorData:gyro:z blue - -vp Acc 200 -10.0 20.0 -vpd Acc representation:InertialSensorData:acc:x red -vpd Acc representation:InertialSensorData:acc:y green -vpd Acc representation:InertialSensorData:acc:z blue - -vp Angle 200 -5.0 7.0 -vpd Angle representation:InertialSensorData:angle:x red -vpd Angle representation:InertialSensorData:angle:y green \ No newline at end of file diff --git a/Config/Scenes/Includes/KalmanMultiRobotMap.con b/Config/Scenes/Includes/KalmanMultiRobotMap.con new file mode 100644 index 00000000..7c7e3d07 --- /dev/null +++ b/Config/Scenes/Includes/KalmanMultiRobotMap.con @@ -0,0 +1,35 @@ +call Includes/FieldViewsCreate +call Includes/FieldViewsPercepts + +mr RobotsHypothesesYolo YoloRobotDetector +mr RobotsHypothesesYoloUpper YoloRobotDetector +mr ProcessedRobotsHypotheses RobotClassifier +mr RobotsPerceptClassified RobotClassifier +mr RobotsPerceptTeam JerseyColorDetector +mr RobotsPerceptOrientation RobotOrientationDetector +mr RobotsPercept RobotsPerceptProvider + +vd representation:RobotsPercept +vd parameters:KalmanMultiRobotMapProvider + +echo dr module:KalmanMultiRobotMapProvider:reset +echo +echo === relative drawings === +echo vfd worldState origin:RobotPose +echo +echo vfd worldState module:KalmanMultiRobotMapProvider:perceptBuffer +echo +echo vfd worldState module:KalmanMultiRobotMapProvider:localHypotheses +echo vfd worldState module:KalmanMultiRobotMapProvider:remoteHypotheses +echo vfd worldState module:KalmanMultiRobotMapProvider:mergedHypotheses +echo +echo vfd worldState module:KalmanMultiRobotMapProvider:hypothesisMerge +echo vfd worldState module:KalmanMultiRobotMapProvider:perceptCreate +echo vfd worldState module:KalmanMultiRobotMapProvider:perceptMerge +echo +echo vfd worldState module:KalmanMultiRobotMapProvider:sonarPercept +echo vfd worldState module:KalmanMultiRobotMapProvider:sonarPerceptLeft +echo vfd worldState module:KalmanMultiRobotMapProvider:sonarPerceptRight +echo +echo === back to global coordinates === +echo vfd worldState origin:Reset \ No newline at end of file diff --git a/Config/Scenes/Includes/MotionMindfulness.con b/Config/Scenes/Includes/MotionMindfulness.con deleted file mode 100644 index 79711202..00000000 --- a/Config/Scenes/Includes/MotionMindfulness.con +++ /dev/null @@ -1,14 +0,0 @@ -vp stepTime 5000 100 600 -vpd stepTime representation:MotionState:stepTime red - -vd representation:MotionState -vd representation:WalkingEngineParams -vd representation:SpeedInfo - -vp walkingDirection 2000 -10.1 10.1 -vpd walkingDirection module:MotionMindfulness:walkingForwardBackward green -vpd walkingDirection module:MotionMindfulness:walkingSidewards red - -vp fallDownSpeedReduction 2000 1.0 2.0 -vpd fallDownSpeedReduction module:PatternGenerator2017:fallDownAngleReduction.x green -vpd fallDownSpeedReduction module:PatternGenerator2017:fallDownAngleReduction.y red diff --git a/Config/Scenes/Includes/MultipleBallModel.con b/Config/Scenes/Includes/MultipleBallModel.con deleted file mode 100644 index 13fc98d5..00000000 --- a/Config/Scenes/Includes/MultipleBallModel.con +++ /dev/null @@ -1,14 +0,0 @@ -# relative drawings -vfd worldState origin:RobotPose -vfd worldState representation:MultipleBallModel:endPosition -vfd worldState representation:MultipleBallModel - -echo -echo ================= Covariances =========================== -echo vfd worldState origin:RobotPose -echo vfd worldState module:BallModelProvider:local:hypotheses -echo vfd worldState module:BallModelProvider:local:velocities -echo vfd worldState module:BallModelProvider:local:covariances -echo -echo vfd worldState origin:Reset -echo vfd worldState module:BallModelProvider:remote:hypotheses \ No newline at end of file diff --git a/Config/Scenes/Includes/MultipleBallPercept.con b/Config/Scenes/Includes/MultipleBallPercept.con deleted file mode 100644 index c1e91b98..00000000 --- a/Config/Scenes/Includes/MultipleBallPercept.con +++ /dev/null @@ -1,9 +0,0 @@ -# from now, relative to estimated robot pose -vfd worldState origin:RobotPose -vfd worldState representation:MultipleBallPercept:Field - -# back to global coordinates -vfd worldState origin:Reset - -vid lower representation:MultipleBallPercept:Image:Lower -vid upper representation:MultipleBallPercept:Image:Upper \ No newline at end of file diff --git a/Config/Scenes/Includes/NaoV6H25.rsi2 b/Config/Scenes/Includes/NaoV6H25.rsi2 index 7bc4a93e..d498a9f3 100644 --- a/Config/Scenes/Includes/NaoV6H25.rsi2 +++ b/Config/Scenes/Includes/NaoV6H25.rsi2 @@ -51,7 +51,7 @@ - + @@ -64,7 +64,7 @@ - + @@ -97,7 +97,7 @@ - + @@ -110,7 +110,7 @@ - + @@ -129,7 +129,7 @@ - + @@ -140,7 +140,7 @@ - + @@ -159,7 +159,7 @@ - + @@ -188,7 +188,7 @@ - + @@ -201,7 +201,7 @@ - + @@ -220,7 +220,7 @@ - + @@ -231,7 +231,7 @@ - + @@ -250,7 +250,7 @@ - + @@ -279,7 +279,7 @@ - + @@ -292,7 +292,7 @@ - + @@ -309,7 +309,7 @@ - + @@ -328,7 +328,7 @@ - + @@ -347,7 +347,7 @@ - + @@ -360,7 +360,7 @@ - + @@ -391,7 +391,7 @@ - + @@ -404,7 +404,7 @@ - + @@ -422,7 +422,7 @@ - + @@ -441,7 +441,7 @@ - + @@ -460,7 +460,7 @@ - + @@ -473,7 +473,7 @@ - + @@ -615,9 +615,9 @@ - - - + + + @@ -121616,4 +121616,4 @@ - + \ No newline at end of file diff --git a/Config/Scenes/Includes/Normal.con b/Config/Scenes/Includes/Normal.con index b5a78861..4ae3ebe9 100644 --- a/Config/Scenes/Includes/Normal.con +++ b/Config/Scenes/Includes/Normal.con @@ -19,7 +19,7 @@ mr FrameInfo CognitionLogDataProvider mr JointSensorData MotionLogDataProvider mr InertialSensorData MotionLogDataProvider mr KeyStates MotionLogDataProvider -mr UsSensorData MotionLogDataProvider +mr SonarSensorData MotionLogDataProvider mr FrameInfo MotionLogDataProvider mr GroundTruthWorldState CognitionLogDataProvider mr GroundTruthRobotPose OracledWorldModelProvider diff --git a/Config/Scenes/Includes/PerceptOracle.con b/Config/Scenes/Includes/PerceptOracle.con index b2dc4acd..46aae57b 100644 --- a/Config/Scenes/Includes/PerceptOracle.con +++ b/Config/Scenes/Includes/PerceptOracle.con @@ -23,7 +23,7 @@ mr FrameInfo CognitionLogDataProvider mr JointSensorData MotionLogDataProvider mr InertialSensorData MotionLogDataProvider mr KeyStates MotionLogDataProvider -mr UsSensorData MotionLogDataProvider +mr SonarSensorData MotionLogDataProvider mr FrameInfo MotionLogDataProvider # Fast, because we do not compute images @@ -31,15 +31,12 @@ ci off mr Image default mr ImageUpper default -# Deactivate unused modules -mr RobotHealth default -mr Thumbnail off -mr ThumbnailUpper off -mr LowFrameRateImage off -mr LowFrameRateImageUpper off +# CognitionMindfulness requirement => default +mr FieldColors default +mr FieldColorsUpper default +# Enable percepts mr RobotsPercept OracledPerceptsProvider -mr RobotsPerceptUpper OracledPerceptsProvider mr CLIPCenterCirclePercept OracledPerceptsProvider mr CLIPGoalPercept OracledPerceptsProvider mr CLIPFieldLinesPercept OracledPerceptsProvider @@ -47,20 +44,27 @@ mr PenaltyCrossPercept OracledPerceptsProvider mr BallPercept OracledPerceptsProvider mr MultipleBallPercept OracledPerceptsProvider -mr BallHypothesesYolo default -mr PenaltyCrossHypothesesYolo default -mr BallPercept default -mr MultipleBallPercept default -mr ProcessedBallPatches default -mr CLIPPointsPercept off -mr ObstacleBasePoints off -mr BallSpots off -mr BodyContour off -mr BodyContourUpper off -mr FieldColors default -mr FieldColorsUpper default -mr YoloInput off -mr YoloInputUpper off +# Deactivate image processing modules +mr off RobotsPerceptProvider +mr off JerseyColorDetector +mr off RobotOrientationDetector +mr off RobotClassifier +mr off CLIPBallPerceptor +mr off PenaltyCrossClassifier +mr off YoloRobotDetector +mr off CLIPLineFinder +mr off CLIPPreprocessor +mr off TfliteInterpreterProvider +mr off FieldColorProvider +mr off BodyContourProvider + +# Deactivate unused modules +mr RobotHealth default +mr Thumbnail off +mr ThumbnailUpper off +mr LowFrameRateImage off +mr LowFrameRateImageUpper off + mr PNGImageDummy default call Includes/DebugResponses diff --git a/Config/Scenes/Includes/Perception.con b/Config/Scenes/Includes/Perception.con index db8b911e..78e2c9cd 100644 --- a/Config/Scenes/Includes/Perception.con +++ b/Config/Scenes/Includes/Perception.con @@ -11,5 +11,5 @@ vid upper representation:CLIPFieldLinesPercept:Image:Upper vid upper representation:PenaltyCrossPercept:Image:Upper vid upper representation:BallPercept:Image:Upper #vid upper representation:CLIPGoalPercept:Image:Upper -vid upper representation:RobotsPerceptUpper:Image:Upper +vid upper representation:RobotsPercept:Image:Upper vid upper representation:BodyContourUpper diff --git a/Config/Scenes/Includes/PreviewTest.con b/Config/Scenes/Includes/PreviewTest.con deleted file mode 100644 index de73777f..00000000 --- a/Config/Scenes/Includes/PreviewTest.con +++ /dev/null @@ -1,19 +0,0 @@ -mr RobotPose OdometryOnlySelfLocator -set module:CSConverter:params filter_alpha = 0.1; standup_fac = 0.7; lower_CoM_fac = 0.1; odometryVariant = 1; -#dr module:OdometrOnlySelfLocator:resetReferenceOdometry -vp offset 200 -50 100 -vpd offset module:MotionCombinator:offsetToRobotPoseAfterPreview.x red -vpd offset module:MotionCombinator:offsetToRobotPoseAfterPreview.y green - -vp RobotPoseAfterPreview.x 200 0 500 -vpd RobotPoseAfterPreview.x module:Predictor:robotPose.x red -vpd RobotPoseAfterPreview.x module:Predictor:robotPoseAfterPreview.x green - -vp RobotPoseAfterPreview.y 200 0 500 -vpd RobotPoseAfterPreview.y module:Predictor:robotPose.y red -vpd RobotPoseAfterPreview.y module:Predictor:robotPoseAfterPreview.y green - -get representation:MotionRequest -echo set representation:MotionRequest motion = walk; specialActionRequest = { specialAction = standHigh; mirror = false; }; walkRequest = { requestType = speed; request = { rotation = 0deg; translation = { x = 0; y = 0; }; }; standType = doubleSupport; kickStrength = 0; kickDirection = 0; stepRequest = none; }; kickRequest = { kickMotionType = none; mirror = false; dynamical = false; dynPoints = []; kickTarget = { x = 1000; y = 0; }; }; -echo set representation:MotionRequest motion = walk; specialActionRequest = { specialAction = standHigh; mirror = false; }; walkRequest = { requestType = speed; request = { rotation = 0deg; translation = { x = 100; y = 0; }; }; standType = doubleSupport; kickStrength = 0; kickDirection = 0; stepRequest = none; }; kickRequest = { kickMotionType = none; mirror = false; dynamical = false; dynPoints = []; kickTarget = { x = 1000; y = 0; }; }; -echo set representation:MotionRequest motion = walk; specialActionRequest = { specialAction = standHigh; mirror = false; }; walkRequest = { requestType = speed; request = { rotation = 0deg; translation = { x = 0; y = 0; }; }; standType = doubleSupport; kickStrength = 0; kickDirection = 0; stepRequest = none; }; kickRequest = { kickMotionType = none; mirror = false; dynamical = false; dynPoints = []; kickTarget = { x = 1000; y = 0; }; }; diff --git a/Config/Scenes/Includes/Sonar.con b/Config/Scenes/Includes/Sonar.con new file mode 100644 index 00000000..da0fb6a4 --- /dev/null +++ b/Config/Scenes/Includes/Sonar.con @@ -0,0 +1,20 @@ +vd representation:SonarSensorData +vd representation:SonarPercept +vd representation:SonarConfiguration +vfd worldState origin:Reset +vfd worldState representation:SonarPercept:Field on +vd parameters:SonarDetector + +vp SonarLeft 200 0 1000 +vpd SonarLeft perception:SonarPercept:left:raw orange +vpd SonarLeft perception:SonarPercept:left:movingAverage red +vpd SonarLeft perception:SonarPercept:left:exponential blue +vpd SonarLeft perception:SonarPercept:left:minMaxExponential green +vpd SonarLeft perception:SonarPercept:left:kalman purple + +vp SonarRight 200 0 1000 +vpd SonarRight perception:SonarPercept:right:raw orange +vpd SonarRight perception:SonarPercept:right:movingAverage red +vpd SonarRight perception:SonarPercept:right:exponential blue +vpd SonarRight perception:SonarPercept:right:minMaxExponential green +vpd SonarRight perception:SonarPercept:right:kalman purple diff --git a/Config/Scenes/Includes/WhistleDbg.con b/Config/Scenes/Includes/WhistleDbg.con index ae258879..7fa245dd 100644 --- a/Config/Scenes/Includes/WhistleDbg.con +++ b/Config/Scenes/Includes/WhistleDbg.con @@ -1,4 +1,24 @@ vp whistle 1000 0 1 vpd whistle representation:Whistle:confidence blue vpd whistle representation:Whistle:detected red -vi FFT \ No newline at end of file +vpd whistle representation:Whistle:threshold black +vi FFT + +vp nnConfidence 1000 0 1 +vpd nnConfidence representation:Whistle:nnConfidence red +vpd nnConfidence representation:Whistle:nnConfidenceWeighted blue + +vp pmConfidence 1000 0 1 +vpd pmConfidence representation:Whistle:pmConfidence green +vpd pmConfidence representation:Whistle:pmConfidenceWeighted blue + +vp relLimitCount 1000 0 1 +vpd relLimitCount representation:Whistle:relLimitCount black +vpd relLimitCount representation:Whistle:relLimitCountWeighted blue + +vp debugAmp 1000 0 10 +vpd debugAmp representation:Whistle:meanAmp blue +vpd debugAmp representation:Whistle:minAmp green +vpd debugAmp representation:Whistle:maxAmp red + +vd representation:WhistleDortmund diff --git a/Config/Scenes/ReplayRobot.con b/Config/Scenes/ReplayRobot.con index f2f88509..cb590656 100644 --- a/Config/Scenes/ReplayRobot.con +++ b/Config/Scenes/ReplayRobot.con @@ -37,6 +37,13 @@ log mr log pause log once +# disable team communication +mr TimeSynchronization default +mr TimeOffsets off +mr TeamCommSenderOutput default +mr TeamCommInput off +mr TeamCommSocket off + #enable behavior graph dr representation:ActivationGraph diff --git a/Config/Scenes/SingleRobotDummyOpponents.ros2 b/Config/Scenes/SingleRobotDummyOpponents.ros2 index c14c26ce..51d2a6b2 100644 --- a/Config/Scenes/SingleRobotDummyOpponents.ros2 +++ b/Config/Scenes/SingleRobotDummyOpponents.ros2 @@ -36,7 +36,7 @@ - + diff --git a/Config/Scenes/SingleRobotDummyOpponentsFast.ros2 b/Config/Scenes/SingleRobotDummyOpponentsFast.ros2 index 038f20e2..2b494185 100644 --- a/Config/Scenes/SingleRobotDummyOpponentsFast.ros2 +++ b/Config/Scenes/SingleRobotDummyOpponentsFast.ros2 @@ -39,19 +39,19 @@ - + - + - + - + diff --git a/Config/Scenes/TestScene.con b/Config/Scenes/TestScene.con index f13c1620..69fd0d97 100644 --- a/Config/Scenes/TestScene.con +++ b/Config/Scenes/TestScene.con @@ -1,3 +1,3 @@ -call Includes/Fast +call Includes/PerceptOracle dt off gc playing diff --git a/Config/Scenes/TestScene.ros2 b/Config/Scenes/TestScene.ros2 index 1390924e..5661fa67 100644 --- a/Config/Scenes/TestScene.ros2 +++ b/Config/Scenes/TestScene.ros2 @@ -23,15 +23,15 @@ - + - + - + diff --git a/Config/Scenes/TwoPlayers.ros2 b/Config/Scenes/TwoPlayers.ros2 index e490488e..73bd20c8 100644 --- a/Config/Scenes/TwoPlayers.ros2 +++ b/Config/Scenes/TwoPlayers.ros2 @@ -6,7 +6,7 @@ - + @@ -16,7 +16,7 @@ - + @@ -24,7 +24,7 @@ - + @@ -38,19 +38,19 @@ - + - + - + - + diff --git a/Config/Sounds/Whistle/.gitignore b/Config/Sounds/Whistle/.gitignore new file mode 100644 index 00000000..9dc2510e --- /dev/null +++ b/Config/Sounds/Whistle/.gitignore @@ -0,0 +1,3 @@ +*.raw +*.wav +*.csv \ No newline at end of file diff --git a/Config/Sounds/alarm.wav b/Config/Sounds/alarm.wav new file mode 100644 index 0000000000000000000000000000000000000000..5ea0db51373b05ace61f35a49eccb226525cd47a GIT binary patch literal 708148 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�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

+uQ$e|DB;p|lK_Vtd&Ll-Na907?-Fpuyx} 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 I+23D*KlUtCc>n+a literal 0 HcmV?d00001 diff --git a/Config/Voices/.gitignore b/Config/Voices/.gitignore deleted file mode 100644 index 5e4debcc..00000000 --- a/Config/Voices/.gitignore +++ /dev/null @@ -1 +0,0 @@ -* diff --git a/Config/WhistleDet_precision_0.9900_recall_0.6050_threshold_0.85.tflite b/Config/WhistleDet_precision_0.9900_recall_0.6050_threshold_0.85.tflite deleted file mode 100644 index 8bcebcf5602150b9068a4719ce6cbc67a840124f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 899376 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

Q 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%0D57TWsZ;lUFrzgTQ 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

#7omj?)xQOKD1SplQa4P$LUhd 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

Ud)pK~mGz38JGfGXeYu=7YB}s* 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 IAo& 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

roxDDJr)pVU8aZ#BM?A0zdNK6_`O9$zV3Ju6x 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 R<75%?6C>aK(4UCN{{=t+jt2k$ diff --git a/Config/WhistleNetMk16.tflite b/Config/WhistleNetMk16.tflite new file mode 100644 index 0000000000000000000000000000000000000000..eaac8d09b23c9068907a075263b75374d802245e GIT binary patch literal 193960 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
+ System archive (recommended) + + * Download [the system archive](https://tu-dortmund.sciebo.de/s/hbHlz2syCDtfCjS). + * Open a (non-admin) command prompt and import the downloaded .tgz file (adjust path to your needs): + ``` + cd %HOMEPATH% + wsl --import Ubuntu-22.04 ubuntu-2204 Downloads\WSL-Ubuntu-22.04-NaoDevils.tgz --version 1 + ``` + * Done! +
+ + *
+ Manual installation (not recommended) + + * Open a (non-admin) command prompt and execute: + ``` + wsl --set-default-version 1 + wsl --install -d Ubuntu-22.04 + ``` + * **Notes**: + * WSL1 and WSL2 both work, however WSL1 is highly recommended due to frequent Windows file system acccess. + * You can check the currently used WSL version using `wsl --list --verbose`. + * You can also convert an existing distribution from WSL 2 to WSL 1 afterwards using `wsl --set-version Ubuntu-22.04 1`. + * Open Ubuntu bash if not opened automatically (type `bash` in command line). + * Update system and install packages: + + ``` + sudo apt update + sudo apt dist-upgrade + + sudo apt install --no-install-recommends clang clang-14 ccache cmake make git ninja-build python3-pip pkg-config clang-format dos2unix + pip3 install "conan==1.*" + ``` +
- ``` - sudo apt update - sudo apt dist-upgrade - - sudo apt install --no-install-recommends clang clang-14 ccache cmake make git ninja-build python3-pip pkg-config clang-format dos2unix - pip3 install conan - ``` * In Visual Studio, select **Dorsh.exe** as startup item and run the application. * Select the checkbox of any robot and click "deploy" in the lower toolbar. * Check the console output and make sure everything compiles correctly. -* If you are not connected to a robot yet, the command will fail at the end with an error message "Antman is not reachable". +* If you are not connected to a robot yet, the command will fail at the end with an error message "\ is not reachable". ### Ubuntu 22.04 @@ -86,7 +111,7 @@ You can choose between ``` sudo apt install --no-install-recommends clang clang-14 ccache cmake make git ninja-build python3-pip pkg-config clang-format dos2unix libgl-dev libglu1-mesa-dev libopengl-dev libegl-dev libasound-dev - pip3 install conan + pip3 install "conan==1.*" # make conan available in path (login again should also work) source ~/.profile @@ -119,7 +144,7 @@ Mac support (Intel and Apple M1/M2) is in an experimental state and has been tes * Then, installed some basic requirements for development: ``` -brew install cmake conan ninja +brew install cmake conan@1.60 ninja ``` * You are now ready to build SimRobot as described for Linux using CMake. @@ -136,7 +161,7 @@ brew install cmake conan ninja Create a [Nao Devils system image](https://github.com/NaoDevils/NaoImage) using the `generate_naodevils.sh` script. You can either * generate a ready-to-run version of our robot software (pass this framework directory using the `-f` parameter) or - * generate a base version that only contain the Ubuntu base image and has to be deployed using Dorsh later (omit the `-f` parameter). + * generate a base version that only contains the Ubuntu base image and has to be deployed using Dorsh later (omit the `-f` parameter). ## Deploy a robot diff --git a/Src/CMakeLists.txt b/Src/CMakeLists.txt index 409d9a5e..a9198dc3 100644 --- a/Src/CMakeLists.txt +++ b/Src/CMakeLists.txt @@ -6,7 +6,7 @@ find_package(Threads REQUIRED) find_package(Eigen3 3.4 REQUIRED) find_package(libjpeg-turbo 2.1 REQUIRED) find_package(nlohmann_json 3.11 REQUIRED) -find_package(Taskflow 3.5 REQUIRED) +find_package(Taskflow 3.6 REQUIRED) find_package(Snappy 1.1 REQUIRED) find_package(protobuf 3.21 REQUIRED) find_package(kissfft 131.1 REQUIRED) @@ -15,7 +15,6 @@ find_package(portaudio 19.7 REQUIRED) find_package(flite 2.2 REQUIRED) if(BUILD_ROBOT) - find_package(ALSA REQUIRED) find_library(LIBRT rt REQUIRED) add_executable(${PROJECT_NAME} "") @@ -78,7 +77,6 @@ target_link_libraries(${PROJECT_NAME} if(BUILD_ROBOT) target_link_libraries(${PROJECT_NAME} PRIVATE - ALSA::ALSA ${LIBRT} ) diff --git a/Src/Controller/AudioPlayer.cpp b/Src/Controller/AudioPlayer.cpp index 7df26f4f..a687f55e 100644 --- a/Src/Controller/AudioPlayer.cpp +++ b/Src/Controller/AudioPlayer.cpp @@ -90,9 +90,11 @@ void AudioPlayer::play(const AudioData& audioData) void AudioPlayer::Pimpl::init(int sampleRate, int channelCount) { + [[maybe_unused]] PaError paerr; if (!initialized) { - VERIFY(Pa_Initialize() == paNoError); + paerr = Pa_Initialize(); + assert(paerr == paNoError); } outputParameters.device = Pa_GetDefaultOutputDevice(); @@ -106,5 +108,6 @@ void AudioPlayer::Pimpl::init(int sampleRate, int channelCount) this->sampleRate = sampleRate; - VERIFY(Pa_OpenStream(&stream, nullptr, &outputParameters, this->sampleRate, paFramesPerBufferUnspecified, 0, nullptr, nullptr) == paNoError); + paerr = Pa_OpenStream(&stream, nullptr, &outputParameters, this->sampleRate, paFramesPerBufferUnspecified, 0, nullptr, nullptr); + assert(paerr == paNoError); } diff --git a/Src/Controller/ConsoleRoboCupCtrl.cpp b/Src/Controller/ConsoleRoboCupCtrl.cpp index bcfd54a6..7c6c284b 100644 --- a/Src/Controller/ConsoleRoboCupCtrl.cpp +++ b/Src/Controller/ConsoleRoboCupCtrl.cpp @@ -439,21 +439,26 @@ void ConsoleRoboCupCtrl::help(In& stream) list(" kfm : Send local key frame motions to the robot. ", pattern, true); list(" log start | stop | clear | save | full | jpeg : Record log file and (de)activate image compression.", pattern, true); list(" log saveAudio : Save audio data from log.", pattern, true); + list(" log saveTrueWhistleAudio (): Save true whistle audio data from log.", pattern, true); + list(" log saveFalseWhistleAudio (): Save false positive whistle audio data from log.", pattern, true); + list(" log export {}: Export the data in the log to json file, optionally limited to the given representation IDs.", pattern, true); list(" log saveImages [raw] : Save images from log.", pattern, true); list(" log saveTiming : Save timing data from log to csv.", pattern, true); list(" log ? [] | load | ( keep | remove ) {} | ( keepFrames | removeFrames ) : Load, filter, and display information about log file.", pattern, true); - list(" log start | pause | stop | forward [image] | backward [image] | repeat | goto | cycle | once | fast_forward | fast_rewind : Replay log file.", pattern, true); + list(" log start | pause | stop | forward [image] | backward [image] | repeat | goto | cycle | once | fast_forward [image] | fast_rewind [image] : Replay log file.", pattern, true); list(" msg off | on | log | enable | disable : Switch output of text messages on or off. Log text messages to a file. Switch message handling on or off.", pattern, true); list(" mr ? [] | modules [] | save | ( ? [] | | off ) : Send module request.", pattern, true); list(" mv [ ] : Move the selected simulated robot to the given position.", pattern, true); list(" mvb : Move the ball to the given position.", pattern, true); + list(" kiba : Kicks the ball in the direction given by angle having the given velocity. ", pattern, true); list(" poll : Poll for all available debug requests and drawings. ", pattern, true); list(" pr none | ballHolding | playerPushing | inactivePlayer | illegalDefender | leavingTheField | playingWithHands | requestForPickup : Penalize robot.", pattern, true); list(" qfr queue | replace | reject | collect | save [] : Send queue fill request.", pattern, true); list(" set ? [] | ( ? | unchanged | ) : Change debug data or show its specification.", pattern, true); list(" save ? [] | [] : Save debug data to a configuration file.", pattern, true); + list(" sleep : Sleeps for the given number of frames and delays the following commands. ", pattern, true); list(" si reset | (upper | lower) [number] : Save the upper/lower cams image.", pattern, true); list(" v3 ? [] | [jpeg] [upper] [] : Add a set of 3-D views for a certain image.", pattern, true); list(" vd on | off : Show debug data in a window or switch sending it off.", pattern, true); @@ -618,11 +623,14 @@ void ConsoleRoboCupCtrl::createCompletion() "jc press", "jc release", "js", + "kiba", "kfm", "log start", "log stop", "log save", "log saveAudio", + "log saveTrueWhistleAudio", + "log saveFalseWhistleAudio", "log saveImages", "log saveImages raw", "log saveTiming", @@ -644,6 +652,7 @@ void ConsoleRoboCupCtrl::createCompletion() "log fast_rewind", "log keepFrames", "log removeFrames", + "log export", "mr modules", "mr save", "msg off", @@ -708,6 +717,12 @@ void ConsoleRoboCupCtrl::createCompletion() if (std::find(m.representations.begin(), m.representations.end(), r) != m.representations.end()) completion.insert(std::string("mr ") + r + " " + m.name); } + + for (const auto& m : moduleInfo->modules) + { + completion.insert(std::string("mr off ") + m.name); + completion.insert(std::string("mr default ") + m.name); + } } if (debugRequestTable) diff --git a/Src/Controller/GameController.cpp b/Src/Controller/GameController.cpp index 477f5834..1a7d444d 100644 --- a/Src/Controller/GameController.cpp +++ b/Src/Controller/GameController.cpp @@ -39,9 +39,15 @@ GameController::GameController() gameInfo.kickingTeam = 1; gameInfo.secsRemaining = durationOfHalf; teamInfos[TEAM_BLUE].teamNumber = 1; - teamInfos[TEAM_BLUE].teamColour = TEAM_BLUE; + teamInfos[TEAM_BLUE].fieldPlayerColour = TEAM_BLUE; + teamInfos[TEAM_BLUE].goalkeeperColour = TEAM_BLUE; + teamInfos[TEAM_BLUE].goalkeeper = 1; + teamInfos[TEAM_BLUE].messageBudget = messageBudget; teamInfos[TEAM_RED].teamNumber = 2; - teamInfos[TEAM_RED].teamColour = TEAM_RED; + teamInfos[TEAM_RED].fieldPlayerColour = TEAM_RED; + teamInfos[TEAM_RED].goalkeeperColour = TEAM_RED; + teamInfos[TEAM_RED].goalkeeper = 1; + teamInfos[TEAM_RED].messageBudget = messageBudget; } void GameController::registerSimulatedRobot(int robot, SimulatedRobot& simulatedRobot) @@ -49,6 +55,7 @@ void GameController::registerSimulatedRobot(int robot, SimulatedRobot& simulated ASSERT(!robots[robot].simulatedRobot); robots[robot].simulatedRobot = &simulatedRobot; robots[robot].info.number = robot % (numOfRobots / 2) + 1; + robots[robot].info.penalty = PENALTY_NONE; if (fieldDimensions.xPosOwnPenaltyMark == 0.f) fieldDimensions.load(); } @@ -70,9 +77,6 @@ bool GameController::handleGlobalCommand(const std::string& command) if (gameInfo.state == STATE_INITIAL) gameInfo.timeFirstReadyState = SystemCall::getCurrentSystemTime(); gameInfo.state = STATE_READY; - for (int i = 0; i < numOfRobots; ++i) - if (robots[i].info.penalty) - handleRobotCommand(i, "none"); timeWhenStateBegan = SystemCall::getCurrentSystemTime(); return true; } @@ -90,8 +94,6 @@ bool GameController::handleGlobalCommand(const std::string& command) else { gameInfo.state = STATE_SET; - for (int i = 0; i < numOfRobots; ++i) - robots[i].info.penalty = none; if (automatic) { @@ -113,8 +115,6 @@ bool GameController::handleGlobalCommand(const std::string& command) else { gameInfo.state = STATE_SET; - for (int i = 0; i < numOfRobots; ++i) - robots[i].info.penalty = none; gameInfo.secsRemaining = durationOfPS; timeWhenHalfStarted = 0; @@ -526,16 +526,16 @@ void GameController::referee() for (int i = 0; i < numOfRobots; ++i) { Robot& r = robots[i]; - if (r.info.penalty) + + RoboCup::RobotInfo& tr = teamInfos[i * 2 / numOfRobots].players[i % (numOfRobots / 2)]; + + if (r.info.penalty != PENALTY_NONE && r.info.penalty != PENALTY_SUBSTITUTE && r.info.penalty != PENALTY_SPL_REQUEST_FOR_PICKUP) { r.info.secsTillUnpenalised = (uint8_t)(std::max(int(45 - SystemCall::getTimeSince(r.timeWhenPenalized) / 1000), 0)); - RoboCup::RobotInfo& tr = teamInfos[i * 2 / numOfRobots].players[i % (numOfRobots / 2)]; - tr.secsTillUnpenalised = r.info.secsTillUnpenalised; if (automatic && r.info.secsTillUnpenalised <= 0) { r.info.penalty = PENALTY_NONE; - tr.penalty = PENALTY_NONE; ASSERT(r.simulatedRobot); Vector2f ballPos; @@ -543,6 +543,8 @@ void GameController::referee() placeForPenalty(i, fieldDimensions.xPosOpponentPenaltyMark, ballPos.y() >= 0 ? fieldDimensions.yPosRightSideline : fieldDimensions.yPosLeftSideline, ballPos.y() >= 0 ? pi_2 : -pi_2); } } + tr.secsTillUnpenalised = r.info.secsTillUnpenalised; + tr.penalty = r.info.penalty; } if (automatic) @@ -885,15 +887,16 @@ void GameController::writeGameInfo(Out& stream) gameInfo.secsRemaining = (uint16_t)(durationOfPS - SystemCall::getTimeSince(timeWhenHalfStarted) / 1000); } gameInfo.timeLastPackageReceived = SystemCall::getCurrentSystemTime(); + gameInfo.controllerConnected = true; stream << gameInfo; } void GameController::writeTeamInfo(TeamInfo& teamInfo, Out& stream) { SYNC; - // starting value: 1200 packages or 1680 for 7v7 (see rules and GC 2022) + // starting value: 1200 packages (see rules and GC 2023) if (gameInfo.state == STATE_INITIAL || gameInfo.state == STATE_FINISHED) - teamInfo.messageBudget = (gameInfo.competitionType == COMPETITION_TYPE_7V7) ? 1680 : 1200; + teamInfo.messageBudget = messageBudget; teamInfo.teamPort = Global::getSettings().teamPort; @@ -927,12 +930,14 @@ void GameController::writeRobotInfo(int robot, Out& stream) stream << r.info; } -void GameController::setTeamColors(uint8_t firstTeamColor, uint8_t secondTeamColor) +void GameController::setTeamColors(uint8_t firstFieldPlayerColor, uint8_t firstGoalkeeperColor, uint8_t secondFieldPlayerColor, uint8_t secondGoalkeeperColor) { teamInfos[0].teamNumber = 1; - teamInfos[0].teamColour = firstTeamColor; + teamInfos[0].fieldPlayerColour = firstFieldPlayerColor; + teamInfos[0].goalkeeperColour = firstGoalkeeperColor; teamInfos[1].teamNumber = 2; - teamInfos[1].teamColour = secondTeamColor; + teamInfos[1].fieldPlayerColour = secondFieldPlayerColor; + teamInfos[1].goalkeeperColour = secondGoalkeeperColor; } void GameController::addCompletion(std::set& completion) const diff --git a/Src/Controller/GameController.h b/Src/Controller/GameController.h index 8c2255af..8e3469e3 100644 --- a/Src/Controller/GameController.h +++ b/Src/Controller/GameController.h @@ -34,7 +34,11 @@ class GameController Pose2f lastPose; bool manuallyPlaced; - Robot() : simulatedRobot(0), timeWhenPenalized(0), manuallyPlaced(false) {} + Robot() : simulatedRobot(0), timeWhenPenalized(0), manuallyPlaced(false) + { + // Disable robot by default + info.penalty = PENALTY_SUBSTITUTE; + } }; ENUM(Penalty, @@ -54,6 +58,7 @@ class GameController DECLARE_SYNC; static const int numOfRobots = MAX_NUM_PLAYERS * 2; static const int numOfFieldPlayers = numOfRobots / 2 - 2; // Keeper, Substitute + static constexpr uint16_t messageBudget = 1200; static const int durationOfHalf = 600; static const int durationOfPS = 45; /**< duration of one penalty shootout attemp. */ static const float footLength; /**< foot length for position check and manual placement at center circle. */ @@ -62,7 +67,7 @@ class GameController static Pose2f lastBallContactPose; /**< Position were the last ball contact of a robot took place, orientation is toward opponent goal (0/180 degress). */ static int timeOfLastBallContact; /**Time when the last ball contact occured*/ static FieldDimensions fieldDimensions; - GameInfo gameInfo; + RawGameInfo gameInfo; TeamInfo teamInfos[2]; unsigned timeWhenHalfStarted; unsigned timeOfLastDropIn; @@ -259,7 +264,7 @@ class GameController */ void writeOpponentTeamInfo(int robot, Out& stream); - void setTeamColors(uint8_t firstTeamColor, uint8_t secondTeamColor); + void setTeamColors(uint8_t firstFieldPlayerColor, uint8_t firstGoalkeeperColor, uint8_t secondFieldPlayerColor, uint8_t secondGoalkeeperColor); /** * Write the current information of a certain robot to the diff --git a/Src/Controller/LocalRobot.cpp b/Src/Controller/LocalRobot.cpp index 20fa7178..4fe18914 100644 --- a/Src/Controller/LocalRobot.cpp +++ b/Src/Controller/LocalRobot.cpp @@ -56,8 +56,8 @@ bool LocalRobot::main() debugOut.out.finishMessage(idFsrSensorData); debugOut.out.bin << inertialSensorData; debugOut.out.finishMessage(idInertialSensorData); - debugOut.out.bin << usSensorData; - debugOut.out.finishMessage(idUsSensorData); + debugOut.out.bin << sonarSensorData; + debugOut.out.finishMessage(idSonarSensorData); debugOut.out.bin << odometryData; debugOut.out.finishMessage(idGroundTruthOdometryData); debugOut.out.bin << ++ping; @@ -143,6 +143,8 @@ void LocalRobot::update() simulatedRobot.moveRobot(movePos, Vector3f::Zero(), false); else if (moveOp == moveBallPosition) simulatedRobot.moveBall(movePos); + else if (moveOp == kickBallVelocity) + simulatedRobot.setBallVelocity(Vector3f(kickVelocity * std::cos(kickAngle), kickVelocity * std::sin(kickAngle), 0)); moveOp = noMove; } } @@ -178,7 +180,7 @@ void LocalRobot::update() simulatedRobot.getRobotPose(robotPose); simulatedRobot.getOdometryData(robotPose, odometryData); - simulatedRobot.getSensorData(fsrSensorData, inertialSensorData, usSensorData, usRequest); + simulatedRobot.getSensorData(fsrSensorData, inertialSensorData, sonarSensorData); simulatedRobot.getAndSetJointData(jointRequest, jointSensorData); } diff --git a/Src/Controller/LocalRobot.h b/Src/Controller/LocalRobot.h index 7b8f46b0..2435534d 100644 --- a/Src/Controller/LocalRobot.h +++ b/Src/Controller/LocalRobot.h @@ -14,7 +14,7 @@ #include "Representations/Infrastructure/SensorData/FsrSensorData.h" #include "Representations/Infrastructure/SensorData/InertialSensorData.h" #include "Representations/Infrastructure/SensorData/JointSensorData.h" -#include "Representations/Infrastructure/SensorData/UsSensorData.h" +#include "Representations/Infrastructure/SensorData/SonarSensorData.h" #include "Representations/MotionControl/OdometryData.h" #include "SimulatedRobot.h" @@ -35,7 +35,7 @@ class LocalRobot : public RobotConsole FsrSensorData fsrSensorData; /**< The simulated inertia sensor data sent to the robot code. */ JointSensorData jointSensorData; /**< The simulated joint measurements sent to the robot code. */ InertialSensorData inertialSensorData; /**< The simulated inertia sensor data sent to the robot code. */ - UsSensorData usSensorData; /**< The simulated sonar sensor data sent to the robot code. */ + SonarSensorData sonarSensorData; /**< The simulated sonar sensor data sent to the robot code. */ Pose2f robotPose = Pose2f(); /**< The robot's pose, used for some internal computations. */ GroundTruthWorldState worldState; /**< The current world state of the simulation scene, sent to the robot code. */ GroundTruthOdometryData odometryData; /**< The simulated odometry data sent to the robot code. */ diff --git a/Src/Controller/LogPlayer.cpp b/Src/Controller/LogPlayer.cpp index 1c1cce9e..898ea097 100644 --- a/Src/Controller/LogPlayer.cpp +++ b/Src/Controller/LogPlayer.cpp @@ -8,18 +8,28 @@ #include #include "LogPlayer.h" +#include "Representations/AnnotationInfo.h" #include "Representations/Infrastructure/AudioData.h" #include "Representations/Infrastructure/LowFrameRateImage.h" #include "Representations/Infrastructure/SequenceImage.h" +#include "Representations/Infrastructure/SensorData/FsrSensorData.h" +#include "Representations/Sensing/JoinedIMUData.h" #include "Platform/SystemCall.h" #include "Platform/BHAssert.h" #include "Platform/File.h" #include "Tools/MessageQueue/LogFileFormat.h" +#include "Tools/Module/Blackboard.h" +#include "Tools/Streams/Streamable.h" +#include "Tools/Debugging/DebugDataStreamer.h" +#include +#include #include #include #include #include + using namespace std; +using json = nlohmann::json; LogPlayer::LogPlayer(MessageQueue& targetQueue) : targetQueue(targetQueue), streamHandler(nullptr) { @@ -99,7 +109,7 @@ bool LogPlayer::open(const char* fileName) } stop(); - countFramesAndMessages(); + std::tie(numberOfFrames, numberOfMessagesWithinCompleteFrames) = queue.countFramesAndMessages(); createFrameIndex(); return true; } @@ -427,7 +437,8 @@ void LogPlayer::keep(MessageID* messageIDs) ++m; } } - countFramesAndMessages(); + std::tie(numberOfFrames, numberOfMessagesWithinCompleteFrames) = queue.countFramesAndMessages(); + if (!frameIndex.empty()) createFrameIndex(); } @@ -451,7 +462,7 @@ void LogPlayer::keep(int startFrame, int endFrame) for (temp.currentMessageNumber = begin; temp.currentMessageNumber < end; ++temp.currentMessageNumber) temp.copyMessage(temp.currentMessageNumber, *this); - countFramesAndMessages(); + std::tie(numberOfFrames, numberOfMessagesWithinCompleteFrames) = queue.countFramesAndMessages(); if (!frameIndex.empty()) createFrameIndex(); } @@ -475,7 +486,7 @@ void LogPlayer::remove(MessageID* messageIDs) if (!*m) temp.copyMessage(temp.currentMessageNumber, *this); } - countFramesAndMessages(); + std::tie(numberOfFrames, numberOfMessagesWithinCompleteFrames) = queue.countFramesAndMessages(); if (!frameIndex.empty()) createFrameIndex(); } @@ -502,7 +513,7 @@ void LogPlayer::remove(int startFrame, int endFrame) for (temp.currentMessageNumber = end; temp.currentMessageNumber < temp.getNumberOfMessages(); ++temp.currentMessageNumber) temp.copyMessage(temp.currentMessageNumber, *this); - countFramesAndMessages(); + std::tie(numberOfFrames, numberOfMessagesWithinCompleteFrames) = queue.countFramesAndMessages(); if (!frameIndex.empty()) createFrameIndex(); } @@ -549,22 +560,6 @@ void LogPlayer::createFrameIndex() } } -void LogPlayer::countFramesAndMessages() -{ - queue.numberOfMessages = 0; - numberOfFrames = 0; - for (queue.selectedMessageForReadingPosition = 0; queue.selectedMessageForReadingPosition < queue.usedSize; - queue.selectedMessageForReadingPosition += queue.getMessageSize() + queue.headerSize) - { - ++queue.numberOfMessages; - if (queue.getMessageID() == idProcessFinished) - { - ++numberOfFrames; - numberOfMessagesWithinCompleteFrames = queue.numberOfMessages + 1; - } - } -} - std::string LogPlayer::expandImageFileName(const char* fileName, int imageNumber) { std::string name(fileName); @@ -729,7 +724,7 @@ bool LogPlayer::writeTimingData(const std::string& fileName) return true; } -bool LogPlayer::saveAudioFile(const char* fileName) +bool LogPlayer::writeAudioFile(const char* fileName, std::vector audioCandidates) { OutBinaryFile stream(fileName); if (!stream.exists()) @@ -737,9 +732,9 @@ bool LogPlayer::saveAudioFile(const char* fileName) int frames = 0; AudioData audioData; - for (currentMessageNumber = 0; currentMessageNumber < getNumberOfMessages(); ++currentMessageNumber) + for (size_t i = 0; i < audioCandidates.size(); i++) { - queue.setSelectedMessageForReading(currentMessageNumber); + queue.setSelectedMessageForReading(audioCandidates[i]); if (queue.getMessageID() == idAudioData) { in.bin >> audioData; @@ -784,9 +779,9 @@ bool LogPlayer::saveAudioFile(const char* fileName) header->subchunk2Size = frames * audioData.channels * sizeof(float); char* p = (char*)(header + 1); - for (currentMessageNumber = 0; currentMessageNumber < getNumberOfMessages(); ++currentMessageNumber) + for (size_t i = 0; i < audioCandidates.size(); i++) { - queue.setSelectedMessageForReading(currentMessageNumber); + queue.setSelectedMessageForReading(audioCandidates[i]); if (queue.getMessageID() == idAudioData) { in.bin >> audioData; @@ -803,6 +798,360 @@ bool LogPlayer::saveAudioFile(const char* fileName) return true; } +bool LogPlayer::saveAudioFile(const char* fileName) +{ + AudioData audioData; + std::vector audioCandidates; + for (currentMessageNumber = 0; currentMessageNumber < getNumberOfMessages(); ++currentMessageNumber) + { + queue.setSelectedMessageForReading(currentMessageNumber); + if (queue.getMessageID() == idAudioData) + { + audioCandidates.push_back(currentMessageNumber); + } + } + + return writeAudioFile(fileName, audioCandidates); +} + +int LogPlayer::saveTrueWhistleAudioFile(const char* fileName, bool split) +{ + int part = 0; + AudioData audioData; + RawGameInfo rawGameInfo; + OwnTeamInfo ownTeamInfo; + OpponentTeamInfo oppenentTeamInfo; + int lastRawGameInfo = 0; + int lastOwnTeamInfoScore = 0; + int lastOppenentTeamInfoScore = 0; + std::vector messageNumberPlay; + std::vector messageNumberGoal; + std::vector messageNumberAudio; + for (currentMessageNumber = 0; currentMessageNumber < getNumberOfMessages(); ++currentMessageNumber) + { + queue.setSelectedMessageForReading(currentMessageNumber); + if (queue.getMessageID() == idRawGameInfo) + { + in.bin >> rawGameInfo; + if (rawGameInfo.state == STATE_PLAYING && lastRawGameInfo == STATE_SET) + messageNumberPlay.push_back(currentMessageNumber); + lastRawGameInfo = rawGameInfo.state; + } + + if (queue.getMessageID() == idOwnTeamInfo) + { + in.bin >> ownTeamInfo; + if (ownTeamInfo.score > lastOwnTeamInfoScore) + messageNumberGoal.push_back(currentMessageNumber); + lastOwnTeamInfoScore = ownTeamInfo.score; + } + + if (queue.getMessageID() == idOpponentTeamInfo) + { + in.bin >> oppenentTeamInfo; + if (oppenentTeamInfo.score > lastOppenentTeamInfoScore) + messageNumberGoal.push_back(currentMessageNumber); + lastOppenentTeamInfoScore = oppenentTeamInfo.score; + } + + if (queue.getMessageID() == idAudioData) + { + messageNumberAudio.push_back(currentMessageNumber); + } + } + + std::vector audioCandidates; + RingBuffer windowLeft; + for (size_t i = 0; i < messageNumberPlay.size(); i++) + { + int id = messageNumberPlay[i]; + int after = 10; + std::vector windowRight; + for (size_t j = 0; j < messageNumberAudio.size(); j++) + { + if (messageNumberAudio[j] > id) + { + if (after > 0) + { + windowRight.push_back(messageNumberAudio[j]); + after--; + } + else + { + break; + } + } + else + { + windowLeft.push_front(messageNumberAudio[j]); + } + } + + for (ptrdiff_t j = windowLeft.size() - 1; j >= 0; j--) + { + audioCandidates.push_back(windowLeft[j]); + } + windowLeft.clear(); + + for (size_t j = 0; j < windowRight.size(); j++) + { + audioCandidates.push_back(windowRight[j]); + } + + if (split) + { + if (audioCandidates.size() > 0) + { + std::string partName = fileName; + partName.insert(partName.size() - 4, "_part_" + std::to_string(part)); + part++; + bool ret = writeAudioFile(partName.c_str(), audioCandidates); + + if (!ret) + return 2; + + audioCandidates.clear(); + } + } + } + + RingBuffer goalWindowLeft; + for (size_t i = 0; i < messageNumberGoal.size(); i++) + { + int id = messageNumberGoal[i]; + int after = 10; + std::vector goalWindowRight; + for (size_t j = 0; j < messageNumberAudio.size(); j++) + { + if (messageNumberAudio[j] > id) + { + if (after > 0) + { + goalWindowRight.push_back(messageNumberAudio[j]); + after--; + } + else + { + break; + } + } + else + { + goalWindowLeft.push_front(messageNumberAudio[j]); + } + } + + for (ptrdiff_t j = goalWindowLeft.size() - 1; j >= 0; j--) + { + audioCandidates.push_back(goalWindowLeft[j]); + } + goalWindowLeft.clear(); + + for (size_t j = 0; j < goalWindowRight.size(); j++) + { + audioCandidates.push_back(goalWindowRight[j]); + } + + if (split) + { + if (audioCandidates.size() > 0) + { + std::string partName = fileName; + partName.insert(partName.size() - 4, "_part_" + std::to_string(part)); + part++; + bool ret = writeAudioFile(partName.c_str(), audioCandidates); + + if (!ret) + return 2; + + audioCandidates.clear(); + } + } + } + + if (messageNumberGoal.size() + messageNumberPlay.size() == 0) + return 1; + + if (!split) + { + bool ret = writeAudioFile(fileName, audioCandidates); + + if (ret) + return 0; + else + return 2; + } + + return 0; +} + +int LogPlayer::saveFalseWhistleAudioFile(const char* fileName, bool split) +{ + int part = 0; + AudioData audioData; + RawGameInfo rawGameInfo; + OwnTeamInfo ownTeamInfo; + OpponentTeamInfo oppenentTeamInfo; + std::vector annotation; + int lastRawGameInfo = 0; + int lastOwnTeamInfoScore = 0; + int lastOppenentTeamInfoScore = 0; + std::vector messageNumberDetection; + std::vector messageNumberFalsePositive; + std::vector messageNumberPlay; + std::vector messageNumberGoal; + std::vector messageNumberAudio; + std::vector annotationCount; + annotationCount.push_back(0); + int annotationIdx = 0; + int lastMessageID = 0; + for (currentMessageNumber = 0; currentMessageNumber < getNumberOfMessages(); ++currentMessageNumber) + { + queue.setSelectedMessageForReading(currentMessageNumber); + if (queue.getMessageID() == idAnnotation) // find detected whistle + { + annotation.resize(queue.getMessageSize()); + in.bin.read(&annotation[0], queue.getMessageSize()); + std::string anno(annotation.begin(), annotation.end()); + if (anno.find("Whistle was detected.") != std::string::npos) + { + messageNumberDetection.push_back(currentMessageNumber); + } + annotationCount[annotationIdx]++; + } + else + { + if (lastMessageID == idAnnotation) + { + annotationCount.push_back(0); + annotationIdx++; + } + } + + // find true whistle + if (queue.getMessageID() == idRawGameInfo) + { + in.bin >> rawGameInfo; + if (rawGameInfo.state == STATE_PLAYING && lastRawGameInfo == STATE_SET) + messageNumberPlay.push_back(currentMessageNumber); + lastRawGameInfo = rawGameInfo.state; + } + + if (queue.getMessageID() == idOwnTeamInfo) + { + in.bin >> ownTeamInfo; + if (ownTeamInfo.score > lastOwnTeamInfoScore) + messageNumberGoal.push_back(currentMessageNumber); + lastOwnTeamInfoScore = ownTeamInfo.score; + } + + if (queue.getMessageID() == idOpponentTeamInfo) + { + in.bin >> oppenentTeamInfo; + if (oppenentTeamInfo.score > lastOppenentTeamInfoScore) + messageNumberGoal.push_back(currentMessageNumber); + lastOppenentTeamInfoScore = oppenentTeamInfo.score; + } + + if (queue.getMessageID() == idAudioData) + { + messageNumberAudio.push_back(currentMessageNumber); + } + + lastMessageID = queue.getMessageID(); + } + // save only false positives + for (size_t detectedWhistleID = 0; detectedWhistleID < messageNumberDetection.size(); detectedWhistleID++) + { + for (size_t currentMessageNumberPlay = 0; currentMessageNumberPlay < messageNumberPlay.size(); currentMessageNumberPlay++) + { + if (currentMessageNumberPlay < detectedWhistleID - (MessageID::numOfDataMessageIDs + annotationCount[detectedWhistleID]) + || currentMessageNumberPlay > detectedWhistleID + MessageID::numOfDataMessageIDs + annotationCount[detectedWhistleID]) + { + messageNumberFalsePositive.push_back(static_cast(detectedWhistleID)); + } + } + for (size_t currentMessageNumberGoal = 0; currentMessageNumberGoal < messageNumberGoal.size(); currentMessageNumberGoal++) + { + if (currentMessageNumberGoal < detectedWhistleID - (MessageID::numOfDataMessageIDs + annotationCount[detectedWhistleID]) + || currentMessageNumberGoal > detectedWhistleID + MessageID::numOfDataMessageIDs + annotationCount[detectedWhistleID]) + { + messageNumberFalsePositive.push_back(static_cast(detectedWhistleID)); + } + } + } + + std::vector audioCandidates; + RingBuffer windowLeft; + for (size_t i = 0; i < messageNumberFalsePositive.size(); i++) + { + int id = messageNumberFalsePositive[i]; + int after = 10; + std::vector windowRight; + for (size_t j = 0; j < messageNumberAudio.size(); j++) + { + if (messageNumberAudio[j] > id) + { + if (after > 0) + { + windowRight.push_back(messageNumberAudio[j]); + after--; + } + else + { + break; + } + } + else + { + windowLeft.push_front(messageNumberAudio[j]); + } + } + + for (ptrdiff_t j = windowLeft.size() - 1; j >= 0; j--) + { + audioCandidates.push_back(windowLeft[j]); + } + windowLeft.clear(); + + for (size_t j = 0; j < windowRight.size(); j++) + { + audioCandidates.push_back(windowRight[j]); + } + + if (split) + { + if (audioCandidates.size() > 0) + { + std::string partName = fileName; + partName.insert(partName.size() - 4, "_part_" + std::to_string(part)); + part++; + bool ret = writeAudioFile(partName.c_str(), audioCandidates); + + if (!ret) + return 2; + + audioCandidates.clear(); + } + } + } + + if (messageNumberFalsePositive.size() == 0) + return 1; + + if (!split) + { + bool ret = writeAudioFile(fileName, audioCandidates); + + if (ret) + return 0; + else + return 2; + } + + return 0; +} + void LogPlayer::replayStreamSpecification() { if (streamHandler && !streamSpecificationReplayed) @@ -812,3 +1161,42 @@ void LogPlayer::replayStreamSpecification() streamSpecificationReplayed = true; } } + +void LogPlayer::export_data(const std::string& file, const std::list& ids) +{ + std::ofstream f(File::getBHDir() + std::string("/Config/Logs/" + file)); + json jOut; + for (currentMessageNumber = 0; currentMessageNumber < getNumberOfMessages(); ++currentMessageNumber) + { + queue.setSelectedMessageForReading(currentMessageNumber); + if (ids.size() == 0 || std::find(ids.begin(), ids.end(), ::getName(queue.getMessageID())) != ids.end()) + { + StreamHandler currentStreamHandler; + + const char* type = ::getName(in.getMessageID()) + 2; + const char* t = streamHandler->getString(type); + if (streamHandler->specification.find(t) != streamHandler->specification.end()) + { + OutJSONSize outJSONSize(true); + { + DebugDataStreamer streamer(*streamHandler, in.bin, type); + outJSONSize << streamer; + // outJSONSize.finish(); + in.resetReadPosition(); + } + + std::vector jsonBuffer(outJSONSize.getSize() + 1, 0); + { + OutJSONMemory outJSON(jsonBuffer.data(), true); + DebugDataStreamer streamer(*streamHandler, in.bin, type); + outJSON << streamer; + // outJSON.finish(); + } + + jsonBuffer[outJSONSize.getSize()] = 0; + jOut.push_back({{"id", ::getName(queue.getMessageID())}, {"data", json::parse(jsonBuffer.data())}}); + } + } + } + f << std::setw(4) << jOut; +} diff --git a/Src/Controller/LogPlayer.h b/Src/Controller/LogPlayer.h index a86ca005..2050dcd5 100644 --- a/Src/Controller/LogPlayer.h +++ b/Src/Controller/LogPlayer.h @@ -13,6 +13,7 @@ #include "Representations/Infrastructure/JPEGImage.h" #include "Tools/MessageQueue/MessageQueue.h" #include "Tools/Streams/StreamHandler.h" +#include /** * @class LogPlayer @@ -105,6 +106,26 @@ class LogPlayer : public MessageQueue */ bool saveAudioFile(const char* fileName); + /** + * Writes audio data which should contain the whistle in the log player queue to a single wav file. + * @param fileName the name of the file to write + * @return 0 if the writing was successful, 1 if empty, 2 on error + */ + int saveTrueWhistleAudioFile(const char* fileName, bool split); + + /** + * Export all (predefined) representations to a file in json format. + * @param fileName the name of the file to write + */ + void export_data(const std::string& file, const std::list& ids); + + /** + * Writes audio data which should not contain the whistle in the log player queue to a single wav file. + * @param fileName the name of the file to write + * @return 0 if the writing was successful, 1 if empty, 2 on error + */ + int saveFalseWhistleAudioFile(const char* fileName, bool split); + /** * Writes all images in the log player queue to a bunch of image files (.png). * @param raw Save color unconverted @@ -200,9 +221,12 @@ class LogPlayer : public MessageQueue StreamHandler* streamHandler; /**< The stream specification of the log file entries. */ /** - * The method counts the number of frames. + * Writes selected audio data in the log player queue to a single wav file. + * @param fileName the name of the file to write + * @param audioCandidates indices of audio messageIDs in queue + * @return true if writing was successful */ - void countFramesAndMessages(); + bool writeAudioFile(const char* fileName, std::vector audioCandidates); /** * Creates the index of the first message numbers of all frames. diff --git a/Src/Controller/Platform/Windows/Joystick.cpp b/Src/Controller/Platform/Windows/Joystick.cpp index e580b1b6..4a33a785 100644 --- a/Src/Controller/Platform/Windows/Joystick.cpp +++ b/Src/Controller/Platform/Windows/Joystick.cpp @@ -10,22 +10,23 @@ #include "Joystick.h" #include "Platform/BHAssert.h" +#include unsigned int Joystick::usedJoysticks = 0; -Joystick::Joystick() : joystickId(-1) {} +Joystick::Joystick() : joystickId(std::numeric_limits::max()) {} bool Joystick::init() { - ASSERT(joystickId == -1); + ASSERT(joystickId == std::numeric_limits::max()); for (int i = 0; i < 32; ++i) if (!(usedJoysticks & (1 << i))) { usedJoysticks |= 1 << i; joystickId = i; buttonEvents[0] = buttonEvents[1] = buttonState[0] = buttonState[1] = 0; - for (int i = 0; i < numOfAxes; ++i) - axisState[i] = 32767; + for (int j = 0; j < numOfAxes; ++j) + axisState[j] = 32767; return true; } return false; @@ -38,16 +39,16 @@ Joystick::~Joystick() void Joystick::deactivate() { - if (joystickId != -1) + if (joystickId != std::numeric_limits::max()) { usedJoysticks &= ~(1 << joystickId); - joystickId = -1; + joystickId = std::numeric_limits::max(); } } bool Joystick::update() { - if (joystickId == -1) + if (joystickId == std::numeric_limits::max()) return false; // poll for new events @@ -101,7 +102,7 @@ bool Joystick::update() bool Joystick::getNextEvent(unsigned int& buttonId, bool& pressed) { - ASSERT(joystickId != -1); + ASSERT(joystickId != std::numeric_limits::max()); if (buttonEvents[0] == 0 && buttonEvents[1] == 0) return false; @@ -124,14 +125,14 @@ bool Joystick::getNextEvent(unsigned int& buttonId, bool& pressed) float Joystick::getAxisState(unsigned int axisId) const { - ASSERT(joystickId != -1); + ASSERT(joystickId != std::numeric_limits::max()); ASSERT(axisId < numOfAxes); return (32767 - int(axisState[axisId])) / 32767.f; } bool Joystick::isButtonPressed(unsigned int buttonId) const { - ASSERT(joystickId != -1); + ASSERT(joystickId != std::numeric_limits::max()); ASSERT(buttonId < numOfButtons); return buttonState[buttonId / 32] & (1 << (buttonId % 32)) ? true : false; } diff --git a/Src/Controller/Representations/TimeInfo.h b/Src/Controller/Representations/TimeInfo.h index 57beda0b..885544a6 100644 --- a/Src/Controller/Representations/TimeInfo.h +++ b/Src/Controller/Representations/TimeInfo.h @@ -28,7 +28,7 @@ class TimeInfo private: enum { - ringBufferSize = 100 + ringBufferSize = 1000 }; public: diff --git a/Src/Controller/RoboCupCtrl.cpp b/Src/Controller/RoboCupCtrl.cpp index 0a787941..dee6734d 100644 --- a/Src/Controller/RoboCupCtrl.cpp +++ b/Src/Controller/RoboCupCtrl.cpp @@ -66,104 +66,7 @@ bool RoboCupCtrl::compile() ballGeom->registerCollisionCallback(*this); } - std::map ownColorCounter; - std::map oppColorCounter; - - std::map teamColorToEnumMap{ - {"nao-blue", 0}, - {"nao-red", 1}, - {"nao-yellow", 2}, - {"nao-black", 3}, - {"nao-white", 4}, - {"nao-green", 5}, - {"nao-orange", 6}, - {"nao-purple", 7}, - {"nao-brown", 8}, - {"nao-gray", 9}, - }; - - for (int i = 0; i < 2; i++) - { - SimRobot::Object* group; - if (i == 0) // Iterate "robots" group: - group = RoboCupCtrl::application->resolveObject("RoboCup.robots", SimRobotCore2::compound); - else // Iterate "extras" group: - group = RoboCupCtrl::application->resolveObject("RoboCup.extras", SimRobotCore2::compound); - - for (unsigned currentRobot = 0, count = RoboCupCtrl::application->getObjectChildCount(*group); currentRobot < count; ++currentRobot) - { - SimRobot::Object* robot = (SimRobot::Object*)RoboCupCtrl::application->getObjectChild(*group, currentRobot); - - QVector parts; - parts.resize(1); - parts[0] = "naoTorsoV6"; - SimRobotCore2::Appearance* torsoAppearance = dynamic_cast(RoboCupCtrl::application->resolveObject(parts, robot, SimRobotCore2::appearance)); - if (torsoAppearance != nullptr) - { - const QString& fullNameTeamColor = RoboCupCtrl::application->getObjectChild(*torsoAppearance, 0u)->getFullName(); - const std::string baseNameTeamColor = fullNameTeamColor.mid(fullNameTeamColor.lastIndexOf('.') + 1).toUtf8().constData(); - - int robotColorEnum = -1; - if (teamColorToEnumMap.find(baseNameTeamColor) != teamColorToEnumMap.end()) - { - robotColorEnum = teamColorToEnumMap[baseNameTeamColor]; - } - else - ASSERT(false); - - QString robotNumberString(robot->getFullName()); - int pos = robotNumberString.lastIndexOf('.'); - robotNumberString.remove(0, pos + 6); - int robotNumber = robotNumberString.toInt() - 1; - - std::map& enumColorMap = robotNumber < MAX_NUM_PLAYERS ? ownColorCounter : oppColorCounter; - if (enumColorMap.find(robotColorEnum) == enumColorMap.end()) - { - if (i == 0) - enumColorMap[robotColorEnum] = 2; // Active robots count double compared to dummys - else - enumColorMap[robotColorEnum] = 1; - } - else - { - if (i == 0) - enumColorMap[robotColorEnum] += 2; // Active robots count double compared to dummys - else - enumColorMap[robotColorEnum] += 1; - } - } - } - } - uint8_t ownTeamColor, oppTeamColour; - if (!ownColorCounter.empty()) - { - std::map::iterator majorOwnColorIt = std::max_element(ownColorCounter.begin(), - ownColorCounter.end(), - [](const auto& x, const auto& y) - { - return x.second < y.second; - }); - int majorOwnColor = majorOwnColorIt->first; - ownTeamColor = majorOwnColor; - } - else - ownTeamColor = 2; // Fallback to yellow - - if (!oppColorCounter.empty()) - { - std::map::iterator majorOppColorIt = std::max_element(oppColorCounter.begin(), - oppColorCounter.end(), - [](const auto& x, const auto& y) - { - return x.second < y.second; - }); - int majorOppColor = majorOppColorIt->first; - oppTeamColour = majorOppColor; - } - else - oppTeamColour = 3; // Fallback to black - - gameController.setTeamColors(ownTeamColor, oppTeamColour); + updateColors(); return true; } @@ -225,8 +128,8 @@ SimRobot::Object* RoboCupCtrl::addCategory(const QString& name, const QString& p { int lio = parentName.lastIndexOf('.'); QString subParentName = parentName.mid(0, lio); - QString name = parentName.mid(lio + 1); - parent = addCategory(name, subParentName); + QString category = parentName.mid(lio + 1); + parent = addCategory(category, subParentName); } return addCategory(name, parent); } @@ -303,6 +206,83 @@ std::string RoboCupCtrl::getRobotName() const return robotName.substr(robotName.rfind('.') + 1); } +void RoboCupCtrl::updateColors() +{ + std::map ownFieldPlayerCounter, oppFieldPlayerCounter; + uint8_t ownFieldPlayerColor = TEAM_YELLOW, ownGoalkeeperColor = TEAM_RED, oppFieldPlayerColor = TEAM_BLACK, oppGoalkeeperColor = TEAM_BLUE; + + const std::map teamColorToEnumMap{ + {"nao-blue", 0}, + {"nao-red", 1}, + {"nao-yellow", 2}, + {"nao-black", 3}, + {"nao-white", 4}, + {"nao-green", 5}, + {"nao-orange", 6}, + {"nao-purple", 7}, + {"nao-brown", 8}, + {"nao-gray", 9}, + }; + + for (int i = 0; i < 2; i++) + { + SimRobot::Object* group; + if (i == 0) // Iterate "robots" group: + group = RoboCupCtrl::application->resolveObject("RoboCup.robots", SimRobotCore2::compound); + else // Iterate "extras" group: + group = RoboCupCtrl::application->resolveObject("RoboCup.extras", SimRobotCore2::compound); + + for (unsigned currentRobot = 0, count = RoboCupCtrl::application->getObjectChildCount(*group); currentRobot < count; ++currentRobot) + { + SimRobot::Object* robot = (SimRobot::Object*)RoboCupCtrl::application->getObjectChild(*group, currentRobot); + + SimRobotCore2::Appearance* torsoAppearance = dynamic_cast(RoboCupCtrl::application->resolveObject({"naoTorsoV6"}, robot, SimRobotCore2::appearance)); + if (torsoAppearance) + { + const QString& fullNameTeamColor = RoboCupCtrl::application->getObjectChild(*torsoAppearance, 0u)->getFullName(); + const std::string baseNameTeamColor = fullNameTeamColor.mid(fullNameTeamColor.lastIndexOf('.') + 1).toUtf8().constData(); + const int robotColorEnum = teamColorToEnumMap.at(baseNameTeamColor); + + QString robotNumberString(robot->getFullName()); + const int pos = robotNumberString.lastIndexOf('.'); + robotNumberString.remove(0, pos + 6); + const int robotNumber = robotNumberString.toInt() - 1; + + if (robotNumber == 0) // is own goalie + ownGoalkeeperColor = robotColorEnum; + else if (robotNumber == MAX_NUM_PLAYERS) // is opp goalie + oppGoalkeeperColor = robotColorEnum; + else + { + std::map& enumColorMap = robotNumber < MAX_NUM_PLAYERS ? ownFieldPlayerCounter : oppFieldPlayerCounter; + + if (enumColorMap.find(robotColorEnum) == enumColorMap.end()) + enumColorMap[robotColorEnum] = 0; + + enumColorMap[robotColorEnum] += (i == 0) ? 2 : 1; // Active robots count double compared to dummys + } + } + } + } + + const auto valueLessThan = [](const auto& x, const auto& y) + { + return x.second < y.second; + }; + if (!ownFieldPlayerCounter.empty()) + { + const auto majorOwnColorIt = std::max_element(ownFieldPlayerCounter.begin(), ownFieldPlayerCounter.end(), valueLessThan); + ownFieldPlayerColor = majorOwnColorIt->first; + } + if (!oppFieldPlayerCounter.empty()) + { + const auto majorOppColorIt = std::max_element(oppFieldPlayerCounter.begin(), oppFieldPlayerCounter.end(), valueLessThan); + oppFieldPlayerColor = majorOppColorIt->first; + } + + gameController.setTeamColors(ownFieldPlayerColor, ownGoalkeeperColor, oppFieldPlayerColor, oppGoalkeeperColor); +} + unsigned RoboCupCtrl::getTime() const { if (simTime) diff --git a/Src/Controller/RoboCupCtrl.h b/Src/Controller/RoboCupCtrl.h index 7ad4a907..8fee91bd 100644 --- a/Src/Controller/RoboCupCtrl.h +++ b/Src/Controller/RoboCupCtrl.h @@ -125,6 +125,11 @@ class RoboCupCtrl : public SimRobot::Module, public SimRobotCore2::CollisionCall */ void stop(); + /** + * Updates jersey colors of goalkeeper and fieldplayers. + */ + void updateColors(); + private: QList views; /**< List of registered views */ }; diff --git a/Src/Controller/RobotConsole.cpp b/Src/Controller/RobotConsole.cpp index 25fcf079..7248ede1 100644 --- a/Src/Controller/RobotConsole.cpp +++ b/Src/Controller/RobotConsole.cpp @@ -368,9 +368,9 @@ bool RobotConsole::handleMessage(InMessage& message) message.bin >> systemSensorData; return true; } - case idUsSensorData: + case idSonarSensorData: { - message.bin >> usSensorData; + message.bin >> sonarSensorData; return true; } case idAudioData: @@ -631,24 +631,6 @@ bool RobotConsole::handleMessage(InMessage& message) case idLogResponse: logAcknowledged = true; return true; - case idRobotPose: - { - RobotPoseCompressed robotPoseCompressed; - message.bin >> robotPoseCompressed; - robotPose = robotPoseCompressed; - robotPoseReceived = SystemCall::getCurrentSystemTime(); - return true; - } - case idBallModel: - { - BallModelCompressed ballModelCompressed; - message.bin >> ballModelCompressed; - ballModel = ballModelCompressed; - if (ballModel.timeWhenLastSeen) - // ballModel.timeWhenLastSeen = ctrl->ntp.getRemoteTimeInLocalTime(ballModel.timeWhenLastSeen); - ballModelReceived = SystemCall::getCurrentSystemTime(); - return true; - } case idTeamBallModel: message.bin >> teamBallModel; teamBallModelReceived = SystemCall::getCurrentSystemTime(); @@ -756,8 +738,16 @@ void RobotConsole::update() lines.clear(); if (handleConsoleLine(temp.front())) { - temp.pop_front(); - lines.splice(lines.end(), temp); + if (sleepTimer == 0) + { + temp.pop_front(); + lines.splice(lines.end(), temp); + } + else + { + lines = temp; + break; + } } else { @@ -1021,6 +1011,14 @@ bool RobotConsole::handleConsoleLine(const std::string& line, bool fromCall) return false; result = moveBall(stream); } + else if (command == "kiba") + { + result = kickBall(stream); + } + else if (command == "sleep") + { + result = sleepConsole(stream); + } else if (command == "poll") result = repoll(stream); else if (command == "pr" && mode == SystemCall::simulatedRobot) @@ -1289,6 +1287,50 @@ bool RobotConsole::log(In& stream, bool first) return logPlayer.saveAudioFile(name.c_str()); } } + else if (command == "saveTrueWhistleAudio") + { + SYNC; + bool split = false; + std::string name; + stream >> name >> split; + if (name.size() == 0) + return false; + else + { + if ((int)name.rfind('.') <= (int)name.find_last_of("\\/")) + name = name + ".wav"; + if (name[0] != '/' && name[0] != '\\' && (name.size() < 2 || name[1] != ':')) + name = std::string("Sounds\\Whistle\\") + name; + int ret = logPlayer.saveTrueWhistleAudioFile(name.c_str(), split); + if (ret == 1) + ctrl->printLn("Potentially no true whistles in log"); + if (ret == 2) + ctrl->printLn("Unable to create file"); + return true; + } + } + else if (command == "saveFalseWhistleAudio") + { + SYNC; + bool split = false; + std::string name; + stream >> name >> split; + if (name.size() == 0) + return false; + else + { + if ((int)name.rfind('.') <= (int)name.find_last_of("\\/")) + name = name + ".wav"; + if (name[0] != '/' && name[0] != '\\' && (name.size() < 2 || name[1] != ':')) + name = std::string("Sounds\\Whistle\\") + name; + int ret = logPlayer.saveFalseWhistleAudioFile(name.c_str(), split); + if (ret == 1) + ctrl->printLn("Potentially no false whistles in log"); + if (ret == 2) + ctrl->printLn("Unable to create file"); + return true; + } + } else if (command == "saveImages") { SYNC; @@ -1521,11 +1563,16 @@ bool RobotConsole::log(In& stream, bool first) } else if (command == "fast_forward") { + std::string opt; + stream >> opt; + //backup state, gotoFrame will change the state. LogPlayer::LogPlayerState state = logPlayer.state; int frame = logPlayer.currentFrameNumber + 100; logPlayer.gotoFrame(std::max<>(std::min<>(frame, logPlayer.numberOfFrames - 1), 0)); + if (opt == "image") + logPlayer.stepImageForward(); if (state == LogPlayer::playing) { //if the state was playing before, continue playing @@ -1535,11 +1582,16 @@ bool RobotConsole::log(In& stream, bool first) } else if (command == "fast_rewind") { + std::string opt; + stream >> opt; + //backup state, gotoFrame will change the state. LogPlayer::LogPlayerState state = logPlayer.state; int frame = logPlayer.currentFrameNumber - 100; logPlayer.gotoFrame(std::max<>(std::min<>(frame, logPlayer.numberOfFrames - 1), 0)); + if (opt == "image") + logPlayer.stepImageBackward(); if (state == LogPlayer::playing) { //if the state was playing before, continue playing @@ -1547,6 +1599,27 @@ bool RobotConsole::log(In& stream, bool first) } return true; } + else if (command == "export") + { + std::string name, par; + std::list ids; + stream >> name; + + while (!stream.eof()) + { + stream >> par; + ids.push_back(par); + } + + + if (name.size() == 0) + return false; + else + { + logPlayer.export_data(name, ids); + return true; + } + } } return false; } @@ -1703,12 +1776,12 @@ bool RobotConsole::set(In& stream) line = "value = " + line + ";"; MessageQueue errors; Global::theDebugOut = &errors.out; - InMapMemory stream(line.c_str(), line.size()); - if (!stream.eof()) + InMapMemory lineStream(line.c_str(), line.size()); + if (!lineStream.eof()) { debugOut.out.bin << debugRequestTable.debugRequests[i].description.substr(11) << char(1); DebugDataStreamer streamer(streamHandler, debugOut.out.bin, j->second.first, singleValue ? "value" : 0); - stream >> streamer; + lineStream >> streamer; if (errors.isEmpty()) { debugOut.out.finishMessage(idDebugDataChangeRequest); @@ -1926,8 +1999,36 @@ bool RobotConsole::moduleRequest(In& stream) } else if (representation == "save") { - OutMapFile stream("modules.cfg"); - moduleInfo.sendRequest(stream, true); + OutMapFile modulesStream("modules.cfg"); + moduleInfo.sendRequest(modulesStream, true); + return true; + } + else if (representation == "off" || representation == "default") + { + if (std::find(moduleInfo.modules.begin(), moduleInfo.modules.end(), module) == moduleInfo.modules.end()) + return false; + + for (auto i = moduleInfo.config.representationProviders.begin(); i != moduleInfo.config.representationProviders.end();) + { + if (i->provider == module) + { + if (representation == "off") + { + i = moduleInfo.config.representationProviders.erase(i); + continue; + } + else + i->provider = representation; + } + ++i; + } + + moduleInfo.timeStamp = SystemCall::getCurrentSystemTime() + ++mrCounter; + debugOut.out.bin << moduleInfo.timeStamp; + moduleInfo.sendRequest(debugOut.out.bin); + debugOut.out.finishMessage(idModuleRequest); + polled[idDebugResponse] = polled[idDrawingManager] = polled[idDrawingManager3D] = false; + logPlayer.streamSpecificationReplayed = false; return true; } else @@ -2016,6 +2117,32 @@ bool RobotConsole::moveBall(In& stream) return true; } +bool RobotConsole::kickBall(In& stream) +{ + SYNC; + float kickAngleDeg; + stream >> kickAngleDeg >> kickVelocity; + kickAngle = Angle::fromDegrees(kickAngleDeg); + moveOp = kickBallVelocity; + return true; +} + +bool RobotConsole::sleepConsole(In& stream) +{ + SYNC; + unsigned sleepFrames; + stream >> sleepFrames; + if (sleepTimer == 0) + { + sleepTimer = sleepFrames; + } + else + { + sleepTimer--; + } + return true; +} + void RobotConsole::printLn(const std::string& line) { if (nullptr != ctrl) @@ -2344,7 +2471,7 @@ bool RobotConsole::kickView() { kickViewSet = true; ctrl->addView( - new KickView(robotName + ".KikeView", *this, motionRequest, jointSensorData, jointCalibration, robotDimensions, printBuffer, (SimRobotCore2::Body*)ctrl->application->resolveObject(robotFullName, SimRobotCore2::body)), + new KickView(robotName + ".KickView", *this, motionRequest, jointSensorData, jointCalibration, robotDimensions, (SimRobotCore2::Body*)ctrl->application->resolveObject(robotFullName, SimRobotCore2::body)), robotName); return true; } @@ -2354,7 +2481,7 @@ bool RobotConsole::kickView() QString puppetName("RoboCup.puppets." + robotName); ctrl->addView( - new KickView(robotName + ".KikeView", *this, motionRequest, jointSensorData, jointCalibration, robotDimensions, printBuffer, (SimRobotCore2::Body*)ctrl->application->resolveObject(puppetName, SimRobotCore2::body)), + new KickView(robotName + ".KickView", *this, motionRequest, jointSensorData, jointCalibration, robotDimensions, (SimRobotCore2::Body*)ctrl->application->resolveObject(puppetName, SimRobotCore2::body)), robotName); return true; } diff --git a/Src/Controller/RobotConsole.h b/Src/Controller/RobotConsole.h index 6a1b80c0..f74ebde5 100644 --- a/Src/Controller/RobotConsole.h +++ b/Src/Controller/RobotConsole.h @@ -18,13 +18,12 @@ #include "Representations/Configuration/RobotDimensions.h" #include "Representations/Infrastructure/JointRequest.h" #include "Representations/Infrastructure/RobotHealth.h" -#include "Representations/Infrastructure/USRequest.h" #include "Representations/Infrastructure/SensorData/FsrSensorData.h" #include "Representations/Infrastructure/SensorData/InertialSensorData.h" #include "Representations/Infrastructure/SensorData/JointSensorData.h" #include "Representations/Infrastructure/SensorData/KeyStates.h" #include "Representations/Infrastructure/SensorData/SystemSensorData.h" -#include "Representations/Infrastructure/SensorData/UsSensorData.h" +#include "Representations/Infrastructure/SensorData/SonarSensorData.h" #include "Representations/Modeling/BallModel.h" #include "Representations/Modeling/TeamBallModel.h" #include "Representations/Modeling/RobotPose.h" @@ -131,16 +130,19 @@ class RobotConsole : public Process InertialSensorData inertialSensorData; /**< The most current set of inertial sensor data received from the robot code. */ KeyStates keyStates; /**< The most current set of key states received from the robot code. */ SystemSensorData systemSensorData; /**< The most current set of system sensor data received from the robot code. */ - UsSensorData usSensorData; /**< The most current set of us sensor data received from the robot code. */ + SonarSensorData sonarSensorData; /**< The most current set of sonar sensor data received from the robot code. */ enum MoveOp { noMove, movePosition, moveBoth, - moveBallPosition + moveBallPosition, + kickBallVelocity } moveOp = noMove; /**< The move operation to perform. */ Vector3f movePos = Vector3f::Zero(); /**< The position the robot is moved to. */ Vector3f moveRot = Vector3f::Zero(); /**< The rotation the robot is moved to. */ + Angle kickAngle; /**< The angle giving the direction of the kicked ball. */ + float kickVelocity; /**< The velocity the ball is kicked with. */ RobotPose robotPose; /**< Robot pose from team communication. */ BallModel ballModel; /**< Ball model from team communication. */ TeamBallModel teamBallModel; /**< combined ball information from team communication */ @@ -165,11 +167,11 @@ class RobotConsole : public Process int mrCounter = 0; /**< Counts the number of mr commands. */ JointCalibration jointCalibration; /**< The joint calibration received from the robot code. */ RobotDimensions robotDimensions; /**< The robotDimensions received from the robot code. */ - USRequest usRequest; /**< The current us request received from the robot code (for simulation). */ std::string printBuffer; /**< Buffer used for command get. */ char drawingsViaProcess = 'b'; /** Which process is used to provide field and 3D drawings */ std::unordered_map annotationInfos; unsigned frame = 0; /**< Current frame number */ + unsigned sleepTimer = 0; /**< sleep frames counter */ public: class ImagePtr @@ -305,7 +307,7 @@ class RobotConsole : public Process bool joystickExecCommand(const std::string&); /**< Exec command and optionally output trace to console. */ unsigned maxPlotSize = 0; /**< The maximum number of data points to remember for plots. */ - bool kickViewSet = false; /**Indicator if there is already a KikeView, we need it just once */ + bool kickViewSet = false; /**Indicator if there is already a KickView, we need it just once */ int imageSaveNumber = 0; /**< A counter for generating image file names. */ std::map> observerMsgpackData; std::map observerDataFinished; @@ -479,6 +481,8 @@ class RobotConsole : public Process bool moduleRequest(In&); bool moveRobot(In&); bool moveBall(In&); + bool kickBall(In&); + bool sleepConsole(In&); bool view3D(In& stream); bool viewField(In& stream); bool viewData(In& stream); /**< Creates a new representation view. Stream should contain the name of the debug data to display. */ diff --git a/Src/Controller/SimulatedRobot.cpp b/Src/Controller/SimulatedRobot.cpp index 2c0c5f68..0d59b90b 100644 --- a/Src/Controller/SimulatedRobot.cpp +++ b/Src/Controller/SimulatedRobot.cpp @@ -19,11 +19,10 @@ #include "Representations/Infrastructure/GroundTruthWorldState.h" #include "Representations/Infrastructure/Image.h" #include "Representations/Infrastructure/JointRequest.h" -#include "Representations/Infrastructure/USRequest.h" #include "Representations/Infrastructure/SensorData/FsrSensorData.h" #include "Representations/Infrastructure/SensorData/InertialSensorData.h" #include "Representations/Infrastructure/SensorData/JointSensorData.h" -#include "Representations/Infrastructure/SensorData/UsSensorData.h" +#include "Representations/Infrastructure/SensorData/SonarSensorData.h" #include "Representations/MotionControl/OdometryData.h" #include "Tools/Math/BHMath.h" #include "Tools/Math/Eigen.h" @@ -146,13 +145,13 @@ void SimulatedRobot::init(SimRobot::Object* robot) // sonars parts[0] = "SonarLeft.distance"; - leftUsSensor = dynamic_cast(application->resolveObject(parts, robot, SimRobotCore2::sensorPort)); + leftSonarSensor = dynamic_cast(application->resolveObject(parts, robot, SimRobotCore2::sensorPort)); parts[0] = "SonarRight.distance"; - rightUsSensor = dynamic_cast(application->resolveObject(parts, robot, SimRobotCore2::sensorPort)); + rightSonarSensor = dynamic_cast(application->resolveObject(parts, robot, SimRobotCore2::sensorPort)); parts[0] = "SonarCenterLeft.distance"; - centerLeftUsSensor = dynamic_cast(application->resolveObject(parts, robot, SimRobotCore2::sensorPort)); + centerLeftSonarSensor = dynamic_cast(application->resolveObject(parts, robot, SimRobotCore2::sensorPort)); parts[0] = "SonarCenterRight.distance"; - centerRightUsSensor = dynamic_cast(application->resolveObject(parts, robot, SimRobotCore2::sensorPort)); + centerRightSonarSensor = dynamic_cast(application->resolveObject(parts, robot, SimRobotCore2::sensorPort)); // load calibration InMapFile stream("jointCalibration.cfg"); @@ -168,14 +167,14 @@ void SimulatedRobot::init(SimRobot::Object* robot) SimRobot::Object* group = application->resolveObject("RoboCup.robots", SimRobotCore2::compound); for (unsigned currentRobot = 0, count = application->getObjectChildCount(*group); currentRobot < count; ++currentRobot) { - SimRobot::Object* robot = application->getObjectChild(*group, currentRobot); - const int number = getNumber(robot); + SimRobot::Object* otherRobot = application->getObjectChild(*group, currentRobot); + const int number = getNumber(otherRobot); if (number != robotNumber) { if (number <= MAX_NUM_PLAYERS) - blueRobots.push_back(robot); + blueRobots.push_back(otherRobot); else - redRobots.push_back(robot); + redRobots.push_back(otherRobot); } } @@ -191,21 +190,21 @@ void SimulatedRobot::init(SimRobot::Object* robot) group = application->resolveObject("RoboCup.extras", SimRobotCore2::compound); for (unsigned currentRobot = 0, count = application->getObjectChildCount(*group); currentRobot < count; ++currentRobot) { - SimRobot::Object* robot = application->getObjectChild(*group, currentRobot); - const int number = getNumber(robot); + SimRobot::Object* extraRobot = application->getObjectChild(*group, currentRobot); + const int number = getNumber(extraRobot); if (number != robotNumber) { if (number <= MAX_NUM_PLAYERS) - blueRobots.push_back(robot); + blueRobots.push_back(extraRobot); else - redRobots.push_back(robot); + redRobots.push_back(extraRobot); } } - // read UsConfiguration - InMapFile usStream("usConfiguration.cfg"); - ASSERT(usStream.exists()); - usStream >> usConfig; + // read SonarConfiguration + InMapFile sonarStream("sonarConfiguration.cfg"); + ASSERT(sonarStream.exists()); + sonarStream >> sonarConfig; // read RobotDimensions InMapFile rdStream("robotDimensions.cfg"); @@ -446,7 +445,7 @@ void SimulatedRobot::toggleCamera() activeCameras[activeCameraIndex] = dynamic_cast(cameraSensor); } -void SimulatedRobot::getSensorData(FsrSensorData& fsrSensorData, InertialSensorData& inertialSensorData, UsSensorData& usSensorData, const USRequest& usRequest) +void SimulatedRobot::getSensorData(FsrSensorData& fsrSensorData, InertialSensorData& inertialSensorData, SonarSensorData& sonarSensorData) { ASSERT(robot); @@ -534,63 +533,13 @@ void SimulatedRobot::getSensorData(FsrSensorData& fsrSensorData, InertialSensorD inertialSensorData.angle.y() = axis[1] * w; } - // ultrasonic (model approximation. not absolutely correct in reality) - static const float scale = 1000; //meter to millimeter - if (usRequest.receiveMode != -1) - { - usSensorData.actuatorMode = static_cast(usRequest.receiveMode); - - //FIXME simulate additional sensor values - usSensorData.left.fill(usConfig.max); - usSensorData.right.fill(usConfig.max); + //FIXME simulate additional sensor values - switch (usSensorData.actuatorMode) - { - case UsSensorData::leftToLeft: - { - const float leftUsValue = dynamic_cast(leftUsSensor)->getValue().floatValue * scale; - usSensorData.left[0] = addUsJitter(leftUsValue); - break; - } - case UsSensorData::leftToRight: - { - const float centerRightUsValue = dynamic_cast(centerRightUsSensor)->getValue().floatValue * scale; - usSensorData.right[0] = addUsJitter(centerRightUsValue); - break; - } - case UsSensorData::rightToLeft: - { - const float centerLeftUsValue = dynamic_cast(centerLeftUsSensor)->getValue().floatValue * scale; - usSensorData.left[0] = addUsJitter(centerLeftUsValue); - break; - } - case UsSensorData::rightToRight: - { - const float rightUsValue = dynamic_cast(rightUsSensor)->getValue().floatValue * scale; - usSensorData.right[0] = addUsJitter(rightUsValue); - break; - } - case UsSensorData::bothToSame: - { - const float leftUsValue = dynamic_cast(leftUsSensor)->getValue().floatValue * scale; - const float rightUsValue = dynamic_cast(rightUsSensor)->getValue().floatValue * scale; - usSensorData.left[0] = addUsJitter(leftUsValue); - usSensorData.right[0] = addUsJitter(rightUsValue); - break; - } - case UsSensorData::bothToOther: - { - const float centerLeftUsValue = dynamic_cast(centerLeftUsSensor)->getValue().floatValue * scale; - const float centerRightUsValue = dynamic_cast(centerRightUsSensor)->getValue().floatValue * scale; - usSensorData.left[0] = addUsJitter(centerLeftUsValue); - usSensorData.right[0] = addUsJitter(centerRightUsValue); - break; - } - default: - ASSERT(false); - } - usSensorData.timeStamp = SystemCall::getCurrentSystemTime(); - } + const float leftSonarDistanceM = dynamic_cast(leftSonarSensor)->getValue().floatValue; + const float rightSonarDistanceM = dynamic_cast(rightSonarSensor)->getValue().floatValue; + sonarSensorData.leftDistanceM = addSonarJitter(leftSonarDistanceM); + sonarSensorData.rightDistanceM = addSonarJitter(rightSonarDistanceM); + sonarSensorData.timestamp = SystemCall::getCurrentSystemTime(); } void SimulatedRobot::moveRobot(const Vector3f& pos, const Vector3f& rot, bool changeRotation) @@ -609,6 +558,8 @@ void SimulatedRobot::moveRobot(const Vector3f& pos, const Vector3f& rot, bool ch } else dynamic_cast(robot)->move(&position.x()); + + dynamic_cast(robot)->resetDynamics(); } void SimulatedRobot::enablePhysics(bool enable) @@ -624,6 +575,11 @@ void SimulatedRobot::moveBall(const Vector3f& pos, bool resetDynamics) dynamic_cast(ball)->resetDynamics(); } +void SimulatedRobot::setBallVelocity(const Vector3f& velocity) +{ + dynamic_cast(ball)->setVelocity(velocity.x(), velocity.y(), velocity.z()); +} + Vector2f SimulatedRobot::getPosition(SimRobot::Object* obj) { const float* position = dynamic_cast(obj)->getPosition(); @@ -682,16 +638,18 @@ void SimulatedRobot::getPose3f(SimRobot::Object* obj, Pose3f& pose3f) const pose3f.rotation = rot; } -float SimulatedRobot::addUsJitter(float value) +float SimulatedRobot::addSonarJitter(float value) { - if (value >= usConfig.max) // Nothing was measured + float sonarConfigMinInMeters = sonarConfig.minDistanceMm / 1000; + float sonarConfigMaxInMeters = sonarConfig.maxDistanceMm / 1000; + if (value >= sonarConfigMaxInMeters) // Nothing was measured return value; else { if (randomFloat() <= 0.1f) - return usConfig.max; // In 10% of all cases nothing is measured. + return sonarConfigMaxInMeters; // In 10% of all cases nothing is measured. else - return std::max(usConfig.min, value) + randomFloat(-10.f, 10.f); + return std::max(sonarConfigMinInMeters, value) + randomFloat(-0.01f, 0.01f); } } diff --git a/Src/Controller/SimulatedRobot.h b/Src/Controller/SimulatedRobot.h index a2b3cb05..66b84e08 100644 --- a/Src/Controller/SimulatedRobot.h +++ b/Src/Controller/SimulatedRobot.h @@ -9,7 +9,7 @@ #include #include "Representations/Configuration/JointCalibration.h" -#include "Representations/Configuration/UsConfiguration.h" +#include "Representations/Configuration/SonarConfiguration.h" #include "Representations/Configuration/RobotDimensions.h" #include "Representations/Infrastructure/CameraInfo.h" #include "Representations/Infrastructure/CameraIntrinsics.h" @@ -32,8 +32,7 @@ struct ImageUpper; struct InertialSensorData; struct JointRequest; struct OdometryData; -struct USRequest; -struct UsSensorData; +struct SonarSensorData; struct Pose2f; /** @@ -61,10 +60,10 @@ class SimulatedRobot SimRobot::Object* lowerCameraSensor; /**< The handle to the sensor port of the lower camera. */ SimRobot::Object* accSensor; /**< The handle to the sensor port of the virtual accelerometer. */ SimRobot::Object* gyroSensor; /**< The handle to the sensor port of the virtual gyrosope. */ - SimRobot::Object* leftUsSensor; /** The handle to the sensor port of the virtual us sensor */ - SimRobot::Object* rightUsSensor; /** The handle to the sensor port of the virtual us sensor */ - SimRobot::Object* centerLeftUsSensor; /** The handle to the sensor port of the virtual us sensor */ - SimRobot::Object* centerRightUsSensor; /** The handle to the sensor port of the virtual us sensor */ + SimRobot::Object* leftSonarSensor; /** The handle to the sensor port of the virtual sonar sensor */ + SimRobot::Object* rightSonarSensor; /** The handle to the sensor port of the virtual sonar sensor */ + SimRobot::Object* centerLeftSonarSensor; /** The handle to the sensor port of the virtual sonar sensor */ + SimRobot::Object* centerRightSonarSensor; /** The handle to the sensor port of the virtual sonar sensor */ static SimRobotCore2::SensorPort* activeCameras[MAX_NUM_PLAYERS * 2]; /**< An array of all activated cameras */ static unsigned activeCameraCount; /**< Total count of constructed cameras */ @@ -75,7 +74,7 @@ class SimulatedRobot CameraInfo lowerCameraInfo; /**< Information about the lower camera. */ CameraIntrinsics cameraIntrinsics; CameraResolution cameraResolution; - UsConfiguration usConfig; + SonarConfiguration sonarConfig; RobotDimensions robotDimensions; template class JointParameters : public Streamable @@ -211,10 +210,9 @@ class SimulatedRobot /** * Determines the sensor data of the simulated robot. * @param inertialSensorData The determined inertial sensor data. - * @param usSensorData The determined usSensorData sensor data. - * @param usRequest The request that determines which sonars are read. + * @param sonarSensorData The determined sonarSensorData sensor data. */ - void getSensorData(FsrSensorData& fsrSensorData, InertialSensorData& inertialSensorData, UsSensorData& usSensorData, const USRequest& usRequest); + void getSensorData(FsrSensorData& fsrSensorData, InertialSensorData& inertialSensorData, SonarSensorData& sonarSensorData); /** * Moves and rotates the robot to an absolute pose @@ -237,6 +235,12 @@ class SimulatedRobot */ static void moveBall(const Vector3f& pos, bool resetDynamics = false); + /** + * Sets the velocity of the ball to the given value + * @param velocity The velocity of the ball + */ + static void setBallVelocity(const Vector3f& velocity); + /** * Determines the two-dimensional position of a SimRobot object without team color rotation. * @param obj The object of which the position will be determined. @@ -254,7 +258,7 @@ class SimulatedRobot /** * Adds jitter to a sensor value. */ - float addUsJitter(float value); + float addSonarJitter(float value); /** * Determines the two-dimensional pose of a SimRobot object without team color rotation. diff --git a/Src/Controller/Views/DataView/DataWidget.cpp b/Src/Controller/Views/DataView/DataWidget.cpp index 0ec4c60a..63df1fc3 100644 --- a/Src/Controller/Views/DataView/DataWidget.cpp +++ b/Src/Controller/Views/DataView/DataWidget.cpp @@ -15,8 +15,9 @@ DataWidget::DataWidget(DataView& view, QtVariantPropertyManager& manager) pSetAction = new QAction(QIcon(":/Icons/upload.png"), tr("&Set"), this); pSetAction->setText("Set"); - pSetAction->setToolTip("Overwrite data on robot"); + pSetAction->setToolTip("Overwrite data on robot (F9)"); pSetAction->setEnabled(false); + pSetAction->setShortcut(QKeySequence(Qt::Key_F9)); pUnchangedAction = new QAction(QIcon(":/Icons/arrow_undo.png"), tr("&Unchanged"), this); pUnchangedAction->setToolTip("Switch back to using original data on robot"); pUnchangedAction->setEnabled(false); diff --git a/Src/Controller/Views/DataView/PropertyManager.h b/Src/Controller/Views/DataView/PropertyManager.h index 7e5ea318..34f9f00d 100644 --- a/Src/Controller/Views/DataView/PropertyManager.h +++ b/Src/Controller/Views/DataView/PropertyManager.h @@ -99,7 +99,7 @@ private slots: /** * QtVariantProperties don't know anything about the type of the data they contain. - * Therefore they cannot store the value internally. Instead the Manager stores + * Therefore they cannot store the value internally. Instead the KickManager stores * the values for all properties. */ QMap theValues; diff --git a/Src/Controller/Views/ImageView.cpp b/Src/Controller/Views/ImageView.cpp index 09f6fd35..3abc8f71 100644 --- a/Src/Controller/Views/ImageView.cpp +++ b/Src/Controller/Views/ImageView.cpp @@ -171,13 +171,13 @@ void ImageWidget::copyImage(const Image& srcImage) } if (imageView.gain != 1.f) { - unsigned* p = (unsigned*)imageData->bits(); + p = (unsigned*)imageData->bits(); float gain = imageView.gain; for (unsigned* pEnd = p + width * height; p < pEnd; ++p) { - int r = (int)(gain * (float)((*p >> 16) & 0xff)); - int g = (int)(gain * (float)((*p >> 8) & 0xff)); - int b = (int)(gain * (float)((*p) & 0xff)); + r = (int)(gain * (float)((*p >> 16) & 0xff)); + g = (int)(gain * (float)((*p >> 8) & 0xff)); + b = (int)(gain * (float)((*p) & 0xff)); *p++ = (r < 0 ? 0 : r > 255 ? 255 : r) << 16 | (g < 0 ? 0 : g > 255 ? 255 : g) << 8 | (b < 0 ? 0 : b > 255 ? 255 : b) | 0xff000000; } @@ -271,8 +271,7 @@ void ImageWidget::mouseMoveEvent(QMouseEvent* event) { Image* image = 0; RobotConsole::Images& currentImages = imageView.console.camImages; - RobotConsole::Images::const_iterator i = currentImages.find(imageView.background); - if (i != currentImages.end()) + if (auto i = currentImages.find(imageView.background); i != currentImages.end()) image = i->second.image; if (image && pos.rx() >= 0 && pos.ry() >= 0 && pos.rx() < image->width && pos.ry() < image->height) { diff --git a/Src/Controller/Views/JointView.cpp b/Src/Controller/Views/JointView.cpp index 65e7b0dd..498c5b49 100644 --- a/Src/Controller/Views/JointView.cpp +++ b/Src/Controller/Views/JointView.cpp @@ -44,6 +44,7 @@ class JointWidget : public QWidget QRect paintRectField3; QRect paintRectField4; QRect paintRectField5; + QRect paintRectField6; public: JointWidget(JointView& jointView, QHeaderView* headerView, QWidget* parent); @@ -54,7 +55,7 @@ class JointWidget : public QWidget virtual void saveLayout(); private: - void print(const char* name, const char* value1, const char* value2, const char* value3, const char* value4, const char* value5); + void print(const char* number, const char* name, const char* value1, const char* value2, const char* value3, const char* value4, const char* value5); void newSection(); QSize sizeHint() const { return QSize(260, 400); } }; @@ -134,8 +135,9 @@ void JointWidget::paintEvent(QPaintEvent* event) paintRectField3 = QRect(headerView->sectionViewportPosition(3) + textOffset, 0, headerView->sectionSize(3) - textOffset * 2, lineSpacing); paintRectField4 = QRect(headerView->sectionViewportPosition(4) + textOffset, 0, headerView->sectionSize(4) - textOffset * 2, lineSpacing); paintRectField5 = QRect(headerView->sectionViewportPosition(5) + textOffset, 0, headerView->sectionSize(5) - textOffset * 2, lineSpacing); + paintRectField6 = QRect(headerView->sectionViewportPosition(6) + textOffset, 0, headerView->sectionSize(6) - textOffset * 2, lineSpacing); { - char request[32], sensor[32], load[32], temp[32], stiffness[32]; + char request[32], sensor[32], load[32], temp[32], stiffness[32], number[32]; SYNC_WITH(jointView.console); const JointSensorData& jointSensorData(jointView.jointSensorData); const JointRequest& jointRequest(jointView.jointRequest); @@ -156,14 +158,15 @@ void JointWidget::paintEvent(QPaintEvent* event) jointSensorData.currents[i] == SensorData::off ? (void)strcpy(load, "off") : (void)sprintf(load, "%d mA", jointSensorData.currents[i]); jointSensorData.temperatures[i] == 0 ? (void)strcpy(temp, "off") : (void)sprintf(temp, "%d °C", jointSensorData.temperatures[i]); jointRequest.stiffnessData.stiffnesses[i] == StiffnessData::useDefault ? (void)strcpy(stiffness, "?") : (void)sprintf(stiffness, "%d %%", jointRequest.stiffnessData.stiffnesses[i]); - print(Joints::getName(static_cast(i)), request, sensor, load, temp, stiffness); + (void)sprintf(number, "%d", i); + print(number, Joints::getName(static_cast(i)), request, sensor, load, temp, stiffness); } } painter.end(); setMinimumHeight(paintRectField1.top()); } -void JointWidget::print(const char* name, const char* value1, const char* value2, const char* value3, const char* value4, const char* value5) +void JointWidget::print(const char* number, const char* name, const char* value1, const char* value2, const char* value3, const char* value4, const char* value5) { if (fillBackground) { @@ -171,18 +174,20 @@ void JointWidget::print(const char* name, const char* value1, const char* value2 painter.drawRect(paintRect.left(), paintRectField1.top(), paintRect.width(), paintRectField1.height()); painter.setPen(fontPen); } - painter.drawText(paintRectField0, Qt::TextSingleLine | Qt::AlignVCenter, tr(name)); - painter.drawText(paintRectField1, Qt::TextSingleLine | Qt::AlignVCenter | Qt::AlignRight, tr(value1)); - painter.drawText(paintRectField2, Qt::TextSingleLine | Qt::AlignVCenter | Qt::AlignRight, tr(value2)); - painter.drawText(paintRectField3, Qt::TextSingleLine | Qt::AlignVCenter | Qt::AlignRight, tr(value3)); - painter.drawText(paintRectField4, Qt::TextSingleLine | Qt::AlignVCenter | Qt::AlignRight, tr(value4)); - painter.drawText(paintRectField5, Qt::TextSingleLine | Qt::AlignVCenter | Qt::AlignRight, tr(value5)); + painter.drawText(paintRectField0, Qt::TextSingleLine | Qt::AlignVCenter, tr(number)); + painter.drawText(paintRectField1, Qt::TextSingleLine | Qt::AlignVCenter, tr(name)); + painter.drawText(paintRectField2, Qt::TextSingleLine | Qt::AlignVCenter | Qt::AlignRight, tr(value1)); + painter.drawText(paintRectField3, Qt::TextSingleLine | Qt::AlignVCenter | Qt::AlignRight, tr(value2)); + painter.drawText(paintRectField4, Qt::TextSingleLine | Qt::AlignVCenter | Qt::AlignRight, tr(value3)); + painter.drawText(paintRectField5, Qt::TextSingleLine | Qt::AlignVCenter | Qt::AlignRight, tr(value4)); + painter.drawText(paintRectField6, Qt::TextSingleLine | Qt::AlignVCenter | Qt::AlignRight, tr(value5)); paintRectField0.moveTop(paintRectField0.top() + lineSpacing); paintRectField1.moveTop(paintRectField1.top() + lineSpacing); paintRectField2.moveTop(paintRectField2.top() + lineSpacing); paintRectField3.moveTop(paintRectField3.top() + lineSpacing); paintRectField4.moveTop(paintRectField4.top() + lineSpacing); paintRectField5.moveTop(paintRectField5.top() + lineSpacing); + paintRectField6.moveTop(paintRectField6.top() + lineSpacing); fillBackground = !fillBackground; } @@ -196,6 +201,7 @@ void JointWidget::newSection() paintRectField3.moveTop(paintRectField3.top() + 1); paintRectField4.moveTop(paintRectField4.top() + 1); paintRectField5.moveTop(paintRectField5.top() + 1); + paintRectField6.moveTop(paintRectField6.top() + 1); fillBackground = false; } @@ -203,6 +209,7 @@ JointHeaderedWidget::JointHeaderedWidget(JointView& sensorView, RobotConsole& co { QStringList headerLabels; headerLabels + << "Number" << "Joint" << "Request" << "Sensor" @@ -218,6 +225,7 @@ JointHeaderedWidget::JointHeaderedWidget(JointView& sensorView, RobotConsole& co headerView->resizeSection(3, 50); headerView->resizeSection(4, 50); headerView->resizeSection(5, 50); + headerView->resizeSection(6, 50); jointWidget = new JointWidget(sensorView, headerView, this); setWidget(jointWidget); } diff --git a/Src/Controller/Views/KickView/KickView.cpp b/Src/Controller/Views/KickView/KickView.cpp index 94077493..a93cc41b 100644 --- a/Src/Controller/Views/KickView/KickView.cpp +++ b/Src/Controller/Views/KickView/KickView.cpp @@ -216,16 +216,15 @@ void KickViewHeaderedWidget::newButtonClicked() void KickViewHeaderedWidget::loadButtonClicked() { char dirname[260]; - sprintf(dirname, "%s/Config/KickEngine/", File::getBHDir()); + sprintf(dirname, "%s/Config/Kicks/KickEngine/", File::getBHDir()); fileName = QFileDialog::getOpenFileName(this, tr("Open Kick Motion"), dirname, tr("Kick Motion Config Files (*.kmc)")); - QString name; - name = fileName.remove(0, fileName.lastIndexOf("/", fileName.lastIndexOf("/") - 1) + 1); - InMapFile stream(name.toUtf8().constData()); + InMapFile stream(fileName.toStdString()); if (stream.exists()) { stream >> parameters; - name = name.remove(0, name.lastIndexOf("/") + 1); + QString name = fileName; + name.remove(0, name.lastIndexOf("/") + 1); strcpy(parameters.name, name.remove(name.lastIndexOf("."), name.length()).toUtf8().constData()); parameters.initFirstPhase(); @@ -246,10 +245,11 @@ void KickViewHeaderedWidget::saveAsButtonClicked() if (fileName.begin() != fileName.end()) { - QString temp = fileName.remove(0, fileName.lastIndexOf("/", fileName.lastIndexOf("/") - 1) + 1); + QString temp = fileName; + temp.remove(0, temp.lastIndexOf("/", temp.lastIndexOf("/") - 1) + 1); temp = temp.remove(0, temp.lastIndexOf("/") + 1); strcpy(parameters.name, temp.remove(temp.lastIndexOf("."), temp.length()).toUtf8().constData()); - writeParametersToFile(fileName.toUtf8().constData()); + writeParametersToFile(fileName.toStdString()); undo.clear(); redo.clear(); emit undoAvailable(false); @@ -260,9 +260,7 @@ void KickViewHeaderedWidget::saveButtonClicked() { if (fileName.begin() != fileName.end() && fileName != QString("newKick.kmc")) { - QString name; - name = fileName.remove(0, fileName.lastIndexOf("/", fileName.lastIndexOf("/") - 1) + 1); - writeParametersToFile(name.toUtf8().constData()); + writeParametersToFile(fileName.toStdString()); undo.clear(); redo.clear(); emit undoAvailable(false); @@ -367,16 +365,9 @@ void KickViewHeaderedWidget::redoChanges() emit redoAvailable(false); } -KickView::KickView(const QString& fullName, - RobotConsole& console, - const MotionRequest& motionRequest, - const JointAngles& jointAngles, - const JointCalibration& jointCalibration, - const RobotDimensions& robotDimensions, - const std::string& mr, - SimRobotCore2::Body* robot) - : fullName(fullName), console(console), motionRequest(motionRequest), jointAngles(jointAngles), jointCalibration(jointCalibration), robotDimensions(robotDimensions), - motionRequestCommand(mr), robot(robot) +KickView::KickView( + const QString& fullName, RobotConsole& console, const MotionRequest& motionRequest, const JointAngles& jointAngles, const JointCalibration& jointCalibration, const RobotDimensions& robotDimensions, SimRobotCore2::Body* robot) + : fullName(fullName), console(console), motionRequest(motionRequest), jointAngles(jointAngles), jointCalibration(jointCalibration), robotDimensions(robotDimensions), robot(robot) { } diff --git a/Src/Controller/Views/KickView/KickView.h b/Src/Controller/Views/KickView/KickView.h index 186306d1..90c91b99 100644 --- a/Src/Controller/Views/KickView/KickView.h +++ b/Src/Controller/Views/KickView/KickView.h @@ -34,7 +34,6 @@ class KickView : public SimRobot::Object const JointAngles& jointAngles, const JointCalibration& jointCalibration, const RobotDimensions& robotDimensions, - const std::string& mr, SimRobotCore2::Body* robot); QString fullName; @@ -44,7 +43,6 @@ class KickView : public SimRobot::Object const JointAngles& jointAngles; const JointCalibration& jointCalibration; const RobotDimensions& robotDimensions; - const std::string& motionRequestCommand; SimRobotCore2::Body* robot; private: diff --git a/Src/Controller/Views/KickView/KickViewGLWidget.cpp b/Src/Controller/Views/KickView/KickViewGLWidget.cpp index 209dee6b..0c246fe5 100644 --- a/Src/Controller/Views/KickView/KickViewGLWidget.cpp +++ b/Src/Controller/Views/KickView/KickViewGLWidget.cpp @@ -2860,12 +2860,6 @@ void KickViewGLWidget::paintGL() originRot << r[0][0], r[0][1], r[0][2], r[1][0], r[1][1], r[1][2], r[2][0], r[2][1], r[2][2]; originRot = RotationMatrix::aroundZ(originRot.getZAngle()); - if (widget.getString > 0) - { - widget.commands.push_back("_get2 representation:MotionRequest"); - widget.getString--; - } - if (!widget.commands.empty()) { if (widget.commands[0] == " ") @@ -3382,8 +3376,8 @@ void KickViewGLWidget::mouseMoveEvent(QMouseEvent* event) { Vector3f newTarPos, camPos, camTarget, translationVec(0, 0, 0); renderer.getCamera(camPos.data(), camTarget.data()); - const float* p = kickView.robot->getPosition(); - newTarPos = Vector3f(p[0], p[1], p[2]); + const float* robotPosition = kickView.robot->getPosition(); + newTarPos = Vector3f(robotPosition[0], robotPosition[1], robotPosition[2]); camPos -= camTarget; camPos += newTarPos; renderer.setCamera(camPos.data(), newTarPos.data()); diff --git a/Src/Controller/Views/KickView/KickViewWidget.cpp b/Src/Controller/Views/KickView/KickViewWidget.cpp index 20fcc29f..64984780 100644 --- a/Src/Controller/Views/KickView/KickViewWidget.cpp +++ b/Src/Controller/Views/KickView/KickViewWidget.cpp @@ -29,7 +29,7 @@ KickViewWidget::KickViewWidget(KickView& kickView, KickEngineParameters& parameters, KickViewHeaderedWidget* parent) : QWidget(parent), parent(parent), glWidget(new KickViewGLWidget(kickView, parameters, this)), kickView(kickView), parameters(parameters), phaseDrawings(true), singleDraw(false), - reachedDraw(false), tra2dWindows(false), tra1dWindows(false), velocityWindows(false), accelWindows(false), followMode(false), dragPlane(0, 1, 0), getString(4), ghost(0), mirror(false) + reachedDraw(false), tra2dWindows(false), tra1dWindows(false), velocityWindows(false), accelWindows(false), followMode(false), dragPlane(0, 1, 0), ghost(0), mirror(false) { //Vertical Layouts QVBoxLayout* vbox = new QVBoxLayout(this); @@ -752,28 +752,6 @@ void KickViewWidget::addPhaseAfterActual() tabber->setCurrentIndex(phaseNumber + 1); } -std::string KickViewWidget::floatToStr(const float& f) -{ - char temp[100]; - sprintf(temp, "%.3f", f); - return temp; -} - -std::string KickViewWidget::intToStr(const int& i) -{ - char temp[100]; - sprintf(temp, "%i", i); - return temp; -} - -std::string KickViewWidget::boolToStr(const bool& b) -{ - if (b) - return "true"; - else - return "false"; -} - void KickViewWidget::setMirrored(int state) { mirror = (state == Qt::Checked); @@ -784,15 +762,22 @@ void KickViewWidget::playWholeMotion() if (parameters.numberOfPhases > 0) { playMotion(parameters.numberOfPhases); - std::stringstream todo(std::stringstream::in | std::stringstream::out); - std::string::size_type idxMotion = kickView.motionRequestCommand.find("motion"); - std::string::size_type idxSpecialAction = kickView.motionRequestCommand.find("specialActionRequest", idxMotion); - std::string::size_type idxKickMotion = kickView.motionRequestCommand.find("kickMotionType", idxSpecialAction); - std::string::size_type idxDynamical = kickView.motionRequestCommand.find("dynamical", idxKickMotion); - todo << kickView.motionRequestCommand.substr(0, idxMotion + 9) << "kick; "; - todo << kickView.motionRequestCommand.substr(idxSpecialAction, idxKickMotion + 17 - idxSpecialAction) << "newKick; mirror = "; - todo << (mirror ? "true" : "false") << "; " << kickView.motionRequestCommand.substr(idxDynamical); - commands.push_back(todo.str()); + + if (kickView.motionRequest.motion == MotionRequest::Motion::kick) + { + OUTPUT_WARNING("Please switch to STAND before you can replay another kick."); + } + + MotionRequest mr(kickView.motionRequest); + mr.motion = MotionRequest::Motion::kick; + KickRequest kr(kickView.motionRequest.kickRequest); + kr.kickMotionType = KickRequest::KickMotionID::newKick; + kr.mirror = mirror; + mr.kickRequest = kr; + + std::string setMotionRequest = "set representation:MotionRequest "; + setMotionRequest += serializeStreamable(mr); + commands.push_back(setMotionRequest); } } @@ -803,117 +788,63 @@ void KickViewWidget::playMotionTilActive() { playMotion(numberOfPhases); - std::stringstream todo(std::stringstream::in | std::stringstream::out); - std::string::size_type idxMotion = kickView.motionRequestCommand.find("motion"); - std::string::size_type idxSpecialAction = kickView.motionRequestCommand.find("specialActionRequest", idxMotion); - std::string::size_type idxKickMotion = kickView.motionRequestCommand.find("kickMotionType", idxSpecialAction); - std::string::size_type idxDynamical = kickView.motionRequestCommand.find("dynamical", idxKickMotion); - todo << kickView.motionRequestCommand.substr(0, idxMotion + 9) << "kick; "; - todo << kickView.motionRequestCommand.substr(idxSpecialAction, idxKickMotion + 17 - idxSpecialAction) << "newKick; mirror = "; - todo << (mirror ? "true" : "false") << "; " << kickView.motionRequestCommand.substr(idxDynamical); - commands.push_back(todo.str()); + if (kickView.motionRequest.motion == MotionRequest::Motion::kick) + { + OUTPUT_WARNING("Please switch to STAND before you can replay another kick."); + } + + MotionRequest mr(kickView.motionRequest); + mr.motion = MotionRequest::Motion::kick; + KickRequest kr(kickView.motionRequest.kickRequest); + kr.kickMotionType = KickRequest::KickMotionID::newKick; + kr.mirror = mirror; + mr.kickRequest = kr; + + std::string setMotionRequest = "set representation:MotionRequest "; + setMotionRequest += serializeStreamable(mr); + commands.push_back(setMotionRequest); } } -void KickViewWidget::playMotion(int phase) +std::string KickViewWidget::serializeStreamable(const Streamable& streamable) { - std::string setNewKickMotion = "set module:KickEngine:newKickMotion footOrigin = { x = " + floatToStr(parameters.footOrigin.x()) + "; y = " + floatToStr(parameters.footOrigin.y()) - + "; z = " + floatToStr(parameters.footOrigin.z()) + "; }; footRotOrigin = { x = " + floatToStr(parameters.footRotOrigin.x()) - + "; y = " + floatToStr(parameters.footRotOrigin.y()) + "; z = " + floatToStr(parameters.footRotOrigin.z()) + "; }; armOrigin = { x = " + floatToStr(parameters.armOrigin.x()) - + "; y = " + floatToStr(parameters.armOrigin.y()) + "; z = " + floatToStr(parameters.armOrigin.z()) + "; }; handRotOrigin = { x = " + floatToStr(parameters.handRotOrigin.x()) - + "; y = " + floatToStr(parameters.handRotOrigin.y()) + "; z = " + floatToStr(parameters.handRotOrigin.z()) + "; }; comOrigin = { x = " + floatToStr(parameters.comOrigin.x()) - + "; y = " + floatToStr(parameters.comOrigin.y()) + "; }; headOrigin = { x = " + floatToStr(parameters.headOrigin.x()) + "; y = " + floatToStr(parameters.headOrigin.y()) - + "; }; kpx = " + floatToStr(parameters.kpx) + "; kix = " + floatToStr(parameters.kix) + "; kdx = " + floatToStr(parameters.kdx) + "; kpy = " + floatToStr(parameters.kpy) - + "; kiy = " + floatToStr(parameters.kiy) + "; kdy = " + floatToStr(parameters.kdy) + "; preview = " + floatToStr(parameters.preview) + "; loop = " + boolToStr(parameters.loop) - + "; autoComTra = " + boolToStr(parameters.autoComTra) + "; ignoreHead = " + boolToStr(parameters.ignoreHead) + "; phaseParameters = ["; - - for (int i = 0; i < phase; i++) - { - if (i) - setNewKickMotion += ", "; - setNewKickMotion += " { duration = " + intToStr(parameters.phaseParameters[i].duration) + "; " - + "leftFootTra1 = { x = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::leftFootTra][1].x()) - + "; y = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::leftFootTra][1].y()) - + "; z = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::leftFootTra][1].z()) + "; }; " - + "leftFootTra2 = { x = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::leftFootTra][2].x()) - + "; y = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::leftFootTra][2].y()) - + "; z = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::leftFootTra][2].z()) + "; }; " - + "leftFootRot1 = { x = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::leftFootRot][1].x()) - + "; y = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::leftFootRot][1].y()) - + "; z = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::leftFootRot][1].z()) + "; }; " - + "leftFootRot2 = { x = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::leftFootRot][2].x()) - + "; y = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::leftFootRot][2].y()) - + "; z = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::leftFootRot][2].z()) + "; }; " - + "rightFootTra1 = { x = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::rightFootTra][1].x()) - + "; y = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::rightFootTra][1].y()) - + "; z = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::rightFootTra][1].z()) + "; }; " - + "rightFootTra2 = { x = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::rightFootTra][2].x()) - + "; y = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::rightFootTra][2].y()) - + "; z = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::rightFootTra][2].z()) + "; }; " - + "rightFootRot1 = { x = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::rightFootRot][1].x()) - + "; y = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::rightFootRot][1].y()) - + "; z = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::rightFootRot][1].z()) + "; }; " - + "rightFootRot2 = { x = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::rightFootRot][2].x()) - + "; y = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::rightFootRot][2].y()) - + "; z = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::rightFootRot][2].z()) + "; }; " - + "leftArmTra1 = { x = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::leftArmTra][1].x()) - + "; y = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::leftArmTra][1].y()) - + "; z = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::leftArmTra][1].z()) + "; }; " - + "leftArmTra2 = { x = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::leftArmTra][2].x()) - + "; y = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::leftArmTra][2].y()) - + "; z = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::leftArmTra][2].z()) + "; }; " - + "leftHandRot1 = { x = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::leftHandRot][1].x()) - + "; y = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::leftHandRot][1].y()) - + "; z = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::leftHandRot][1].z()) + "; }; " - + "leftHandRot2 = { x = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::leftHandRot][2].x()) - + "; y = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::leftHandRot][2].y()) - + "; z = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::leftHandRot][2].z()) + "; }; " - + "rightArmTra1 = { x = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::rightArmTra][1].x()) - + "; y = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::rightArmTra][1].y()) - + "; z = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::rightArmTra][1].z()) + "; }; " - + "rightArmTra2 = { x = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::rightArmTra][2].x()) - + "; y = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::rightArmTra][2].y()) - + "; z = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::rightArmTra][2].z()) + "; }; " - + "rightHandRot1 = { x = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::rightHandRot][1].x()) - + "; y = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::rightHandRot][1].y()) - + "; z = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::rightHandRot][1].z()) + "; }; " - + "rightHandRot2 = { x = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::rightHandRot][2].x()) - + "; y = " + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::rightHandRot][2].y()) + "; z = " - + floatToStr(parameters.phaseParameters[i].controlPoints[Phase::rightHandRot][2].z()) + "; }; " + "comTra1 = { x = " + floatToStr(parameters.phaseParameters[i].comTra[1].x()) - + "; y = " + floatToStr(parameters.phaseParameters[i].comTra[1].y()) + "; }; " + "comTra2 = { x = " + floatToStr(parameters.phaseParameters[i].comTra[2].x()) - + "; y = " + floatToStr(parameters.phaseParameters[i].comTra[2].y()) + "; }; " + "headTra1 = { x = " + floatToStr(parameters.phaseParameters[i].headTra[1].x()) - + "; y = " + floatToStr(parameters.phaseParameters[i].headTra[1].y()) + "; }; " + "headTra2 = { x = " + floatToStr(parameters.phaseParameters[i].headTra[2].x()) - + "; y = " + floatToStr(parameters.phaseParameters[i].headTra[2].y()) + "; }; " + "odometryOffset = { x = " + floatToStr(parameters.phaseParameters[i].odometryOffset.x()) - + "; y = " + floatToStr(parameters.phaseParameters[i].odometryOffset.y()) + "; z = " + floatToStr(parameters.phaseParameters[i].odometryOffset.z()) + "; }; }"; - } + OutMapSize size(true); + size << streamable; + char* buf = new char[size.getSize()]; + OutMapMemory memory(buf, true); + memory << streamable; + buf[size.getSize() - 1] = 0; // overwrite final space + return buf; +} - setNewKickMotion += " ];"; + +void KickViewWidget::playMotion(int phase) +{ + KickEngineParameters kep(parameters); + kep.phaseParameters.resize(phase); + std::string setNewKickMotion = "set module:KickEngine:newKickMotion "; + setNewKickMotion += serializeStreamable(kep); commands.push_back(setNewKickMotion); } void KickViewWidget::resetRobot() { - std::string moveRobot = "mv 0 0 300 0 0 180"; + std::string moveRobot = "mv 0 0 320 0 0 0"; commands.push_back(moveRobot); commands.push_back(" "); } void KickViewWidget::standRobot() { - std::string command = kickView.motionRequestCommand; - std::string firstOfcom = command.substr(0, command.find("motion") + 9); - std::string temp = command.substr(command.find("motion") + 10, command.size() - command.find("motion") + 10); - std::string endOfcom = temp.substr(temp.find_first_of(";"), temp.size()); - - if (endOfcom.find("playDead") != std::string::npos) - { - endOfcom = endOfcom.replace(endOfcom.find("playDead"), 8, "stand"); - } - - std::string stand = firstOfcom + "specialAction" + endOfcom; - - - commands.push_back(stand); + MotionRequest mr(kickView.motionRequest); + mr.motion = MotionRequest::Motion::specialAction; + SpecialActionRequest sar(kickView.motionRequest.specialActionRequest); + sar.specialAction = SpecialActionRequest::SpecialActionID::stand; + mr.specialActionRequest = sar; + + std::string setMotionRequest = "set representation:MotionRequest "; + setMotionRequest += serializeStreamable(mr); + commands.push_back(setMotionRequest); commands.push_back(" "); } @@ -1185,21 +1116,23 @@ QStandardItem* KickViewWidget::makeValue(QVariant var) void KickViewWidget::addControlPoints(QStandardItem* item) { - QList list; - list.append(makeLabel(QString("Point0"))); - list.append(makeLabel(QString(""))); - - for (int j = 0; j < 3; j++) { - QStandardItem* it = new QStandardItem(); - it->setData((double)0.0, Qt::DisplayRole); - it->setDragEnabled(false); - it->setDropEnabled(false); - it->setEditable(false); - list.append(it); - } + QList list; + list.append(makeLabel(QString("Point0"))); + list.append(makeLabel(QString(""))); - item->appendRow(list); + for (int j = 0; j < 3; j++) + { + QStandardItem* it = new QStandardItem(); + it->setData((double)0.0, Qt::DisplayRole); + it->setDragEnabled(false); + it->setDropEnabled(false); + it->setEditable(false); + list.append(it); + } + + item->appendRow(list); + } for (unsigned int k = 1; k < Phase::numOfPoints; k++) { @@ -1873,35 +1806,33 @@ void KickViewWidget::recordPose() if (phaseNumber > -1) { - Pose3f p; - if (lhtra->isChecked()) { - p = KickViewMath::calculateHandPos(jointAngles, Joints::lShoulderPitch, kickView.robotDimensions); + const Vector3f p = KickViewMath::calculateHandPos(jointAngles, Joints::lShoulderPitch, kickView.robotDimensions).translation; - parameters.phaseParameters[phaseNumber].controlPoints[Phase::leftArmTra][2] = p.translation; - parameters.phaseParameters[phaseNumber].controlPoints[Phase::leftArmTra][1] = p.translation; + parameters.phaseParameters[phaseNumber].controlPoints[Phase::leftArmTra][2] = p; + parameters.phaseParameters[phaseNumber].controlPoints[Phase::leftArmTra][1] = p; } if (rhtra->isChecked()) { - p = KickViewMath::calculateHandPos(jointAngles, Joints::rShoulderPitch, kickView.robotDimensions); + const Vector3f p = KickViewMath::calculateHandPos(jointAngles, Joints::rShoulderPitch, kickView.robotDimensions).translation; - parameters.phaseParameters[phaseNumber].controlPoints[Phase::rightArmTra][2] = p.translation; - parameters.phaseParameters[phaseNumber].controlPoints[Phase::rightArmTra][1] = p.translation; + parameters.phaseParameters[phaseNumber].controlPoints[Phase::rightArmTra][2] = p; + parameters.phaseParameters[phaseNumber].controlPoints[Phase::rightArmTra][1] = p; } if (lhrot->isChecked()) { - p = KickViewMath::calculateHandPos(jointAngles, Joints::lShoulderPitch, kickView.robotDimensions); + const Vector3f p = KickViewMath::calculateHandPos(jointAngles, Joints::lShoulderPitch, kickView.robotDimensions).rotation.getPackedAngleAxisFaulty(); - parameters.phaseParameters[phaseNumber].controlPoints[Phase::leftHandRot][2] = p.rotation.getPackedAngleAxisFaulty(); - parameters.phaseParameters[phaseNumber].controlPoints[Phase::leftHandRot][1] = p.rotation.getPackedAngleAxisFaulty(); + parameters.phaseParameters[phaseNumber].controlPoints[Phase::leftHandRot][2] = p; + parameters.phaseParameters[phaseNumber].controlPoints[Phase::leftHandRot][1] = p; } if (rhrot->isChecked()) { - p = KickViewMath::calculateHandPos(jointAngles, Joints::rShoulderPitch, kickView.robotDimensions); + const Pose3f p = KickViewMath::calculateHandPos(jointAngles, Joints::rShoulderPitch, kickView.robotDimensions); parameters.phaseParameters[phaseNumber].controlPoints[Phase::rightHandRot][2] = p.rotation.getPackedAngleAxisFaulty(); parameters.phaseParameters[phaseNumber].controlPoints[Phase::rightHandRot][1] = p.rotation.getPackedAngleAxisFaulty(); @@ -1959,17 +1890,17 @@ void KickViewWidget::recordPose() if (lftra->isChecked()) { - p = KickViewMath::calculateFootPos(jointAngles, Joints::lHipYawPitch, kickView.robotDimensions).translation; + const Vector3f p = KickViewMath::calculateFootPos(jointAngles, Joints::lHipYawPitch, kickView.robotDimensions).translation; - parameters.phaseParameters[phaseNumber].controlPoints[Phase::leftFootTra][2] = p.translation; - parameters.phaseParameters[phaseNumber].controlPoints[Phase::leftFootTra][1] = p.translation; + parameters.phaseParameters[phaseNumber].controlPoints[Phase::leftFootTra][2] = p; + parameters.phaseParameters[phaseNumber].controlPoints[Phase::leftFootTra][1] = p; } if (rftra->isChecked()) { - p = KickViewMath::calculateFootPos(jointAngles, Joints::rHipYawPitch, kickView.robotDimensions).translation; + const Vector3f p = KickViewMath::calculateFootPos(jointAngles, Joints::rHipYawPitch, kickView.robotDimensions).translation; - parameters.phaseParameters[phaseNumber].controlPoints[Phase::rightFootTra][2] = p.translation; - parameters.phaseParameters[phaseNumber].controlPoints[Phase::rightFootTra][1] = p.translation; + parameters.phaseParameters[phaseNumber].controlPoints[Phase::rightFootTra][2] = p; + parameters.phaseParameters[phaseNumber].controlPoints[Phase::rightFootTra][1] = p; } updateCommon(); diff --git a/Src/Controller/Views/KickView/KickViewWidget.h b/Src/Controller/Views/KickView/KickViewWidget.h index d22979a6..b14d60bd 100644 --- a/Src/Controller/Views/KickView/KickViewWidget.h +++ b/Src/Controller/Views/KickView/KickViewWidget.h @@ -104,9 +104,7 @@ private slots: KickView& kickView; - std::string floatToStr(const float& f); - std::string boolToStr(const bool& b); - std::string intToStr(const int& i); + std::string serializeStreamable(const Streamable& streamable); //Editor TabWidget* tabber; @@ -132,8 +130,7 @@ private slots: Vector3f dragPlane = Vector3f::Zero(); //plane for the 3D View where a selected point is moved Selected selectedPoint; //Infos about the actual selected Point - int getString, - ghost; //set the opacitiy of the robot model + int ghost; //set the opacitiy of the robot model std::vector commands; diff --git a/Src/Controller/Views/SensorView.cpp b/Src/Controller/Views/SensorView.cpp index 2bbc6fd5..b9710566 100644 --- a/Src/Controller/Views/SensorView.cpp +++ b/Src/Controller/Views/SensorView.cpp @@ -23,7 +23,7 @@ #include "Representations/Infrastructure/SensorData/InertialSensorData.h" #include "Representations/Infrastructure/SensorData/KeyStates.h" #include "Representations/Infrastructure/SensorData/SystemSensorData.h" -#include "Representations/Infrastructure/SensorData/UsSensorData.h" +#include "Representations/Infrastructure/SensorData/SonarSensorData.h" #include diff --git a/Src/Modules/BehaviorControl/CABSL/BehaviorControl.cpp b/Src/Modules/BehaviorControl/CABSL/BehaviorControl.cpp index 102c31e8..717c2e6d 100644 --- a/Src/Modules/BehaviorControl/CABSL/BehaviorControl.cpp +++ b/Src/Modules/BehaviorControl/CABSL/BehaviorControl.cpp @@ -5,9 +5,10 @@ * @author Tim Laue */ -#include "Libraries.h" +#include "BehaviorControl.h" #include "Tools/Streams/InStreams.h" #include "Tools/Debugging/Annotation.h" +#include "Modules/BehaviorControl/BehaviorHelper.h" #ifdef __INTELLISENSE__ #define INTELLISENSE_PREFIX NDBehavior::Behavior:: @@ -19,7 +20,7 @@ namespace NDBehavior /** * The wrapper for the behavior options. */ - class Behavior : public Cabsl, public Libraries + class Behavior : public Cabsl, public BehaviorBase { #include "Options.h" public: @@ -34,7 +35,7 @@ namespace NDBehavior * Constructor. * @param base The blackboard configured for this module. */ - Behavior(const BehaviorControlBase& base, BehaviorOutput& behaviorOutput) : Cabsl(&behaviorOutput.theActivationGraph), Libraries(base, behaviorOutput) {} + Behavior(const BehaviorControlBase& base, BehaviorOutput& behaviorOutput) : Cabsl(&behaviorOutput.theActivationGraph), BehaviorBase(base, behaviorOutput) {} /** * Executes one behavior cycle. @@ -43,27 +44,44 @@ namespace NDBehavior void execute(const std::vector& roots) { beginFrame(theFrameInfo.time); - preProcessLibraries(); for (std::vector::const_iterator i = roots.begin(); i != roots.end(); ++i) Cabsl::execute(*i); - postProcessLibraries(); endFrame(); } - void Positioning(bool preciseArrival = true) + void Positioning(bool preciseArrival = true, bool ballchaser = false) { - GoToFieldCoordinates(thePositioningSymbols.optPosition, - thePositioningSymbols.thresholdXFront, - thePositioningSymbols.thresholdXBack, - thePositioningSymbols.thresholdY, - thePositioningSymbols.thresholdRotation, - thePositioningSymbols.stopAtTarget, - thePositioningSymbols.previewArrival, - preciseArrival); + if (ballchaser) + { + GoToFieldCoordinates(thePositioningAndKickSymbols.optPosition, + thePositioningAndKickSymbols.thresholdXFront, + thePositioningAndKickSymbols.thresholdXBack, + thePositioningAndKickSymbols.thresholdY, + thePositioningAndKickSymbols.thresholdRotation, + thePositioningAndKickSymbols.stopAtTarget, + thePositioningAndKickSymbols.previewArrival, + preciseArrival); + } + else + { + GoToFieldCoordinates(thePositioningSymbols.optPosition, + thePositioningSymbols.thresholdXFront, + thePositioningSymbols.thresholdXBack, + thePositioningSymbols.thresholdY, + thePositioningSymbols.thresholdRotation, + thePositioningSymbols.stopAtTarget, + thePositioningSymbols.previewArrival, + preciseArrival); + } } }; + + void BehaviorBase::operator=(const BehaviorBase& other) + { + memcpy((void*)this, (void*)&other, sizeof(*this)); + } } // namespace NDBehavior using namespace NDBehavior; @@ -99,7 +117,7 @@ class BehaviorControl : public BehaviorControlBase behaviorData.role = theRoleSymbols.role; behaviorData.lastRole = theRoleSymbols.lastRole; behaviorData.roleSuggestions = theRoleSymbols.roleSuggestions; - behaviorData.playerNumberToBall = theBallChaserDecision.playerNumberToBall; + behaviorData.playerNumberToBall = static_cast(theBallChaserDecision.playerNumberToBall); behaviorData.ownTimeToBall = theBallChaserDecision.ownTimeToBall; behaviorData.ballPositionField = theBallSymbols.ballPositionField; behaviorData.ballPositionFieldPredicted = theBallSymbols.ballPositionFieldPredicted; @@ -107,8 +125,6 @@ class BehaviorControl : public BehaviorControlBase behaviorData.timeSinceBallWasSeen = theBallSymbols.timeSinceLastSeen; behaviorData.kickTarget = theMotionRequest.kickRequest.kickTarget; - addPassiveRolePositions(behaviorData); - if (behaviorData.role != BehaviorData::RoleAssignment::noRole && roleFromLastFrame != BehaviorData::RoleAssignment::noRole && roleFromLastFrame != behaviorData.role) ANNOTATION("RoleSymbols", "Role changed from " << BehaviorData::getName(roleFromLastFrame) << " to " << BehaviorData::getName(behaviorData.role)); roleFromLastFrame = behaviorData.role; @@ -131,17 +147,6 @@ class BehaviorControl : public BehaviorControlBase /** Updates the arm motion request by copying it from the behavior */ void update(HeadControlRequest& headControlRequest) { headControlRequest = theHeadControlRequest; } - void addPassiveRolePositions(BehaviorData& behaviorData) - { - behaviorData.passiveRolePositions.clear(); - behaviorData.passiveRolePositions.emplace_back(BehaviorData::PassiveRolePosition(BehaviorData::RoleAssignment::backupBallchaser, theBackupBallchaser.optPosition.translation)); - behaviorData.passiveRolePositions.emplace_back(BehaviorData::PassiveRolePosition(BehaviorData::RoleAssignment::center, theCenter.optPosition.translation)); - behaviorData.passiveRolePositions.emplace_back(BehaviorData::PassiveRolePosition(BehaviorData::RoleAssignment::defenderLeft, theDefenderLeft.optPosition.translation)); - behaviorData.passiveRolePositions.emplace_back(BehaviorData::PassiveRolePosition(BehaviorData::RoleAssignment::defenderRight, theDefenderRight.optPosition.translation)); - behaviorData.passiveRolePositions.emplace_back(BehaviorData::PassiveRolePosition(BehaviorData::RoleAssignment::defenderSingle, theDefenderSingle.optPosition.translation)); - behaviorData.passiveRolePositions.emplace_back(BehaviorData::PassiveRolePosition(BehaviorData::RoleAssignment::receiver, theReceiver.optPosition.translation)); - } - Parameters parameters; /**< The root options. */ BehaviorData theBehaviorData; BehaviorLEDRequest theBehaviorLEDRequest; diff --git a/Src/Modules/BehaviorControl/CABSL/BehaviorControl.h b/Src/Modules/BehaviorControl/CABSL/BehaviorControl.h index 79ce00af..fadbdf1f 100644 --- a/Src/Modules/BehaviorControl/CABSL/BehaviorControl.h +++ b/Src/Modules/BehaviorControl/CABSL/BehaviorControl.h @@ -7,76 +7,80 @@ #pragma once +#include "Libraries/HelperFunctions.h" #include "Representations/BehaviorControl/ActivationGraph.h" #include "Representations/BehaviorControl/BallChaserDecision.h" +#include "Representations/BehaviorControl/BallSearch.h" #include "Representations/BehaviorControl/BallSymbols.h" #include "Representations/BehaviorControl/BehaviorConfiguration.h" -#include "Representations/BehaviorControl/BehaviorLEDRequest.h" #include "Representations/BehaviorControl/BehaviorData.h" -#include "Representations/BehaviorControl/GoalSymbols.h" +#include "Representations/BehaviorControl/BehaviorLEDRequest.h" #include "Representations/BehaviorControl/GameSymbols.h" +#include "Representations/BehaviorControl/GoalSymbols.h" #include "Representations/BehaviorControl/HeadControlRequest.h" #include "Representations/BehaviorControl/KeySymbols.h" +#include "Representations/BehaviorControl/PSGoalieTrigger.h" +#include "Representations/BehaviorControl/RoleSelection.h" +#include "Representations/BehaviorControl/RoleSymbols.h" +#include "Representations/BehaviorControl/RoleSymbols/BackupBallchaser.h" #include "Representations/BehaviorControl/RoleSymbols/Ballchaser.h" -#include "Representations/BehaviorControl/PositioningSymbols.h" #include "Representations/BehaviorControl/RoleSymbols/BallchaserKeeper.h" -#include "Representations/BehaviorControl/RoleSymbols/Keeper.h" -#include "Representations/BehaviorControl/RoleSymbols/ReplacementKeeper.h" -#include "Representations/BehaviorControl/RoleSymbols/BackupBallchaser.h" #include "Representations/BehaviorControl/RoleSymbols/Center.h" #include "Representations/BehaviorControl/RoleSymbols/DefenderLeft.h" #include "Representations/BehaviorControl/RoleSymbols/DefenderRight.h" #include "Representations/BehaviorControl/RoleSymbols/DefenderSingle.h" +#include "Representations/BehaviorControl/RoleSymbols/Keeper.h" +#include "Representations/BehaviorControl/RoleSymbols/PositioningSymbols.h" +#include "Representations/BehaviorControl/RoleSymbols/PositioningAndKickSymbols.h" #include "Representations/BehaviorControl/RoleSymbols/Receiver.h" -#include "Representations/BehaviorControl/RoleSymbols.h" -#include "Representations/BehaviorControl/RoleSelection.h" +#include "Representations/BehaviorControl/RoleSymbols/LeftWing.h" +#include "Representations/BehaviorControl/RoleSymbols/RightWing.h" +#include "Representations/BehaviorControl/RoleSymbols/ReplacementKeeper.h" #include "Representations/BehaviorControl/TacticSymbols.h" -#include "Representations/BehaviorControl/PSGoalieTrigger.h" -#include "Representations/BehaviorControl/BallSearch.h" #include "Representations/Configuration/CameraCalibration.h" #include "Representations/Configuration/FieldDimensions.h" #include "Representations/Configuration/HeadLimits.h" #include "Representations/Configuration/MotionSettings.h" #include "Representations/Configuration/RobotDimensions.h" +#include "Representations/Infrastructure/CMCorrectorStatus.h" #include "Representations/Infrastructure/CameraInfo.h" #include "Representations/Infrastructure/FrameInfo.h" #include "Representations/Infrastructure/GameInfo.h" #include "Representations/Infrastructure/Image.h" #include "Representations/Infrastructure/JointAngles.h" +#include "Representations/Infrastructure/NetworkStatus.h" #include "Representations/Infrastructure/RobotInfo.h" -#include "Representations/Infrastructure/TeamInfo.h" -#include "Representations/Infrastructure/TeammateData.h" #include "Representations/Infrastructure/SensorData/KeyStates.h" -#include "Representations/Infrastructure/CMCorrectorStatus.h" #include "Representations/Infrastructure/TeamCommSenderOutput.h" +#include "Representations/Infrastructure/TeamInfo.h" +#include "Representations/Infrastructure/TeammateData.h" #include "Representations/Infrastructure/USBSettings.h" #include "Representations/Infrastructure/USBStatus.h" -#include "Representations/Infrastructure/NetworkStatus.h" #include "Representations/Modeling/BallModel.h" #include "Representations/Modeling/DangerMap.h" +#include "Representations/Modeling/RemoteBallModel.h" #include "Representations/Modeling/RobotMap.h" #include "Representations/Modeling/RobotPose.h" #include "Representations/Modeling/SideConfidence.h" -#include "Representations/Modeling/RemoteBallModel.h" #include "Representations/Modeling/WhistleDortmund.h" #include "Representations/MotionControl/HeadJointRequest.h" #include "Representations/MotionControl/KickEngineOutput.h" #include "Representations/MotionControl/MotionInfo.h" -#include "Representations/MotionControl/MotionState.h" #include "Representations/MotionControl/MotionSelection.h" +#include "Representations/MotionControl/MotionState.h" #include "Representations/MotionControl/SpeedInfo.h" +#include "Representations/MotionControl/SpecialActionsOutput.h" #include "Representations/MotionControl/WalkCalibration.h" #include "Representations/MotionControl/WalkingEngineOutput.h" #include "Representations/MotionControl/WalkingEngineParams.h" -#include "Representations/Perception/CameraMatrix.h" -#include "Representations/Perception/CLIPGoalPercept.h" #include "Representations/Perception/CLIPFieldLinesPercept.h" +#include "Representations/Perception/CLIPGoalPercept.h" +#include "Representations/Perception/CameraMatrix.h" #include "Representations/Sensing/ArmContact.h" #include "Representations/Sensing/FallDownState.h" #include "Representations/Sensing/GroundContactState.h" #include "Representations/Sensing/RobotModel.h" #include "Representations/Sensing/TorsoMatrix.h" -#include "Libraries/HelperFunctions.h" #include "Tools/Debugging/Annotation.h" #include "Tools/Math/Geometry.h" #include "Tools/Math/Random.h" @@ -113,6 +117,8 @@ MODULE(BehaviorControl, REQUIRES(DefenderRight), REQUIRES(DefenderSingle), REQUIRES(Receiver), + REQUIRES(LeftWing), + REQUIRES(RightWing), REQUIRES(KickEngineOutput), REQUIRES(MotionInfo), REQUIRES(MotionState), @@ -121,6 +127,7 @@ MODULE(BehaviorControl, REQUIRES(OpponentTeamInfo), REQUIRES(OwnTeamInfo), REQUIRES(PositioningSymbols), + REQUIRES(PositioningAndKickSymbols), REQUIRES(RawGameInfo), REQUIRES(RemoteBallModel), REQUIRES(RobotDimensions), @@ -135,6 +142,7 @@ MODULE(BehaviorControl, REQUIRES(TacticSymbols), REQUIRES(TeammateData), REQUIRES(SpeedInfo), + REQUIRES(SpecialActionsOutput), REQUIRES(WalkingEngineOutput), REQUIRES(WalkingEngineParams), REQUIRES(WalkCalibration), @@ -206,5 +214,11 @@ namespace NDBehavior * @param other The object that is copied. */ BehaviorBase(const BehaviorBase& other) : BehaviorOutput(other) {} + + /** + * Assignment operator, because the standard operator is not accepted by the compiler. + * @param other The instance that is cloned. + */ + void operator=(const BehaviorBase& other); }; } // namespace NDBehavior diff --git a/Src/Modules/BehaviorControl/CABSL/BehaviorParameters.h b/Src/Modules/BehaviorControl/CABSL/BehaviorParameters.h index 83e62675..bebb09b1 100644 --- a/Src/Modules/BehaviorControl/CABSL/BehaviorParameters.h +++ b/Src/Modules/BehaviorControl/CABSL/BehaviorParameters.h @@ -54,6 +54,7 @@ STREAMABLE(BehaviorParameters, (float)(80.f) relativeYminBallPosition, (float)(350.f) relativeYmaxBallPosition, (bool)(true) useDive, // use dive motion? + (bool)(false) goalieForEvents, (bool)(true) goalieUseIntercept, (bool)(false) goalieInterceptOnly, (bool)(true) useBallInterception, // if true and the ball moves towards robot's y axis he will attempt an interception diff --git a/Src/Modules/BehaviorControl/CABSL/Libraries.cpp b/Src/Modules/BehaviorControl/CABSL/Libraries.cpp deleted file mode 100644 index 9895ddea..00000000 --- a/Src/Modules/BehaviorControl/CABSL/Libraries.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/** - * The file implements a class that instantiates all libraries. - * @author Thomas Röfer - */ - -#include -#include "Libraries.h" - -namespace NDBehavior -{ - CycleLocal Libraries::theInstance(nullptr); - - Libraries::Libraries(const BehaviorControlBase& base, BehaviorOutput& behaviorOutput) : BehaviorBase((theInstance = this, base), behaviorOutput) {} - - Libraries::~Libraries() - { - theInstance.reset(); - } - - void Libraries::operator=(const Libraries& other) - { - memcpy((void*)this, (void*)&other, sizeof(*this)); - } - - void Libraries::preProcessLibraries() - { - for (LibraryBase* library : libraries) - library->preProcess(); - } - - void Libraries::postProcessLibraries() - { - for (LibraryBase* library : libraries) - library->postProcess(); - } -} // namespace NDBehavior diff --git a/Src/Modules/BehaviorControl/CABSL/Libraries.h b/Src/Modules/BehaviorControl/CABSL/Libraries.h deleted file mode 100644 index 9da32dd9..00000000 --- a/Src/Modules/BehaviorControl/CABSL/Libraries.h +++ /dev/null @@ -1,41 +0,0 @@ -/** - * The file declares a class that instantiates all libraries. - * @author Thomas Röfer - */ - -#pragma once - -#include "LibraryBase.h" -#include "Tools/ProcessFramework/CycleLocal.h" - -namespace NDBehavior -{ -#include "Libraries/LibTactic.h" - - class Libraries : public BehaviorBase - { - private: - static CycleLocal theInstance; - std::vector libraries; /**< All the member libraries of this class. */ - - public: - LibTactic libTactic; /**< Contains calculations for tactics like KickoffDefense */ - - Libraries(const BehaviorControlBase& base, BehaviorOutput& behaviorOutput); - ~Libraries(); - - /** - * Assignment operator, because the standard operator is not accepted by the compiler. - * @param other The instance that is cloned. - */ - void operator=(const Libraries& other); - - /** Calls the preProcess() method of each member library */ - void preProcessLibraries(); - - /** Calls the postProcess() method of each member library */ - void postProcessLibraries(); - - friend class LibraryBase; - }; -} // namespace NDBehavior diff --git a/Src/Modules/BehaviorControl/CABSL/Libraries/HelperFunctions.h b/Src/Modules/BehaviorControl/CABSL/Libraries/HelperFunctions.h index 8da71d20..8a1c7fa4 100644 --- a/Src/Modules/BehaviorControl/CABSL/Libraries/HelperFunctions.h +++ b/Src/Modules/BehaviorControl/CABSL/Libraries/HelperFunctions.h @@ -23,9 +23,9 @@ class HelperFunctions { for (auto& mate : teammateData.teammates) { - if (mate.status >= Teammate::ACTIVE) + if (mate.status >= TeammateReceived::Status::ACTIVE) { - const Pose2f& pose = mate.pose; + const Pose2f& pose = mate.robotPose; if ((pose.translation - pos).norm() < dist) { return true; @@ -39,8 +39,8 @@ class HelperFunctions static void addActivePlayerPositions(const TeammateData& teammateData, std::vector& positions) { for (auto& mate : teammateData.teammates) - if (mate.status >= Teammate::ACTIVE) - positions.push_back(mate.pose); + if (mate.status >= TeammateReceived::Status::ACTIVE) + positions.push_back(mate.robotPose); } /* calculates the distance in mm adding penalties for bad approaches (obstacles, from side, ...) */ diff --git a/Src/Modules/BehaviorControl/CABSL/Libraries/LibTactic.cpp b/Src/Modules/BehaviorControl/CABSL/Libraries/LibTactic.cpp deleted file mode 100644 index d25b8c06..00000000 --- a/Src/Modules/BehaviorControl/CABSL/Libraries/LibTactic.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include "../LibraryBase.h" -#include -#include - - -namespace NDBehavior -{ -#include "LibTactic.h" - - LibTactic::LibTactic() {} - - void LibTactic::preProcess() - { - /* Option Playing */ - /* Check if interception would be possible - - - Vector2f endPosition = BallPhysics::getEndPosition(Vector2f::Zero(), theBallSymbols.ballVelocityRelative, theBallModel.friction); - - interceptBallPossible = theBehaviorConfiguration.behaviorParameters.useBallInterception - && theBallSymbols.ballWasSeen - && std::abs(theBallSymbols.yPosWhenBallReachesOwnYAxis) < 500.f - && (theBallSymbols.ballPositionRelativePredicted.x() < theBehaviorConfiguration.behaviorParameters.minXPositionForIntercept || endPosition.x() < -1500.f) - && theBallSymbols.ballVelocityRelative.x() < -200.f;*/ - - - /*Calculate kickWithOuterFoot*/ - Angle kickDirection = theRobotPoseAfterPreview.rotation + theBallSymbols.ballPositionRelative.angle(); - kickDirection = kickDirection.normalize(); - - Pose2f ballPositionAfterKick((theBallSymbols.ballPositionField - theRobotPoseAfterPreview.translation).angle(), theBallSymbols.ballPositionField); - ballPositionAfterKick.translate(1500.f, 0.f); - bool ballIsOut = !theFieldDimensions.isInsideField(ballPositionAfterKick.translation); - bool ownKickoffSetPlay = theGameSymbols.ownKickOff && theGameSymbols.currentSetPlay != SET_PLAY_NONE; - - kickWithOuterFoot = (theBehaviorConfiguration.behaviorParameters.useBlindSideKick //Aktuelle Position - && !ownKickoffSetPlay && theBallSymbols.timeSinceLastSeen < 2000 && std::abs(theBallSymbols.ballPositionRelative.x()) < theBehaviorConfiguration.behaviorParameters.relativeXBallPosition - && std::abs(theBallSymbols.ballPositionRelative.y()) > theBehaviorConfiguration.behaviorParameters.relativeYminBallPosition - && std::abs(theBallSymbols.ballPositionRelative.y()) < theBehaviorConfiguration.behaviorParameters.relativeYmaxBallPosition && !ballIsOut - && std::abs(kickDirection) < 100_deg && theFrameInfo.getTimeSince(timeStampLastWalkKickExecution) > 2000 && theFrameInfo.getTimeSince(timeStampLastDribbleExecution) > 2000 - && theSpeedInfo.stepsSinceLastCustomStep > theBehaviorConfiguration.behaviorParameters.minStepsBetweenWalkKicks); - } - - void LibTactic::postProcess() {} -} // namespace NDBehavior diff --git a/Src/Modules/BehaviorControl/CABSL/Libraries/LibTactic.h b/Src/Modules/BehaviorControl/CABSL/Libraries/LibTactic.h deleted file mode 100644 index c1a09186..00000000 --- a/Src/Modules/BehaviorControl/CABSL/Libraries/LibTactic.h +++ /dev/null @@ -1,22 +0,0 @@ -/** -* @file LibTactic.h -*/ - -class LibTactic : public LibraryBase -{ -public: - /** Constructor for initializing all members*/ - LibTactic(); - - void preProcess() override; - - void postProcess() override; - - //Values for Option Playing - bool blockBall = false; - bool kickWithOuterFoot = false; - bool interceptBallPossible = false; - - unsigned timeStampLastWalkKickExecution = 0; - unsigned timeStampLastDribbleExecution = 0; -}; \ No newline at end of file diff --git a/Src/Modules/BehaviorControl/CABSL/LibraryBase.cpp b/Src/Modules/BehaviorControl/CABSL/LibraryBase.cpp deleted file mode 100644 index de734452..00000000 --- a/Src/Modules/BehaviorControl/CABSL/LibraryBase.cpp +++ /dev/null @@ -1,15 +0,0 @@ -/** - * The file implements the base class for all behavior libraries. - * If a library is used by another library, it must be added here. - * @author Thomas Röfer - */ - -#include "Libraries.h" - -namespace NDBehavior -{ - LibraryBase::LibraryBase() : BehaviorBase(**Libraries::theInstance), libTactic((*Libraries::theInstance)->libTactic) - { - (*Libraries::theInstance)->libraries.push_back(this); - } -} // namespace NDBehavior diff --git a/Src/Modules/BehaviorControl/CABSL/LibraryBase.h b/Src/Modules/BehaviorControl/CABSL/LibraryBase.h deleted file mode 100644 index cdcf938c..00000000 --- a/Src/Modules/BehaviorControl/CABSL/LibraryBase.h +++ /dev/null @@ -1,39 +0,0 @@ -/** - * The file declares the base class for all behavior libraries. - * If a library is used by another library, it must be added here. - * @author Thomas Röfer - */ - -#pragma once - -#include "BehaviorControl.h" - -namespace NDBehavior -{ - class LibTactic; - - class LibraryBase : public BehaviorBase - { - public: - LibTactic& libTactic; /**< Contains miscellaneous helper methods */ - - /** - * The default constructor initializes all references with the actual libraries. - * Note that these libraries may not be initialzed yet, so do not call them - * during construction. - */ - LibraryBase(); - - /** - * The method is called each time before a behavior cycle is executed. - * It is supposed to be overridden. - */ - virtual void preProcess() {} - - /** - * The method is called each time after a behavior cycle was executed. - * It is supposed to be overridden. - */ - virtual void postProcess() {} - }; -} // namespace NDBehavior diff --git a/Src/Modules/BehaviorControl/CABSL/Options.h b/Src/Modules/BehaviorControl/CABSL/Options.h index f9a19328..24b6d659 100644 --- a/Src/Modules/BehaviorControl/CABSL/Options.h +++ b/Src/Modules/BehaviorControl/CABSL/Options.h @@ -14,7 +14,6 @@ #include "Options/Skills/Walk.h" #include "Options/Skills/Cheering.h" #include "Options/Skills/DecideKick.h" -#include "Options/Skills/GetInPosition.h" // Ball Search #include "Options/Skills/BallSearch/KeeperBallSearch.h" @@ -48,3 +47,10 @@ /*** Auto Calibration ***/ #include "Options/AutoCalibration/AutoCalibrate.h" + +/*** Safety Behavior ***/ +#include "Options/Controls/RobotSafetyControl.h" +#include "Options/Skills/RobotSafetyShutdown.h" + +#include "Options/Testing/Testing.h" +#include "Options/Testing/TestingUnstiff.h" diff --git a/Src/Modules/BehaviorControl/CABSL/Options/BodyControl.h b/Src/Modules/BehaviorControl/CABSL/Options/BodyControl.h index a066221d..51fa6133 100644 --- a/Src/Modules/BehaviorControl/CABSL/Options/BodyControl.h +++ b/Src/Modules/BehaviorControl/CABSL/Options/BodyControl.h @@ -68,7 +68,9 @@ option(BodyControl) action { theBehaviorData.soccerState = BehaviorData::positioning; - GetInPosition(); + Positioning(false, + theBallChaserDecision.playerNumberToBall == theRobotInfo.number && theRoleSymbols.role != BehaviorData::RoleAssignment::keeper + && theRoleSymbols.role != BehaviorData::RoleAssignment::replacementKeeper); } } @@ -91,11 +93,12 @@ option(BodyControl) transition {} action { + if (theBehaviorData.behaviorState == BehaviorData::testingJoints) + theBehaviorData.behaviorState = BehaviorData::game; // monitor walk kick executions if (theMotionInfo.customStepKickInPreview) { timeStampLastWalkKickExecution = theFrameInfo.time; - libTactic.timeStampLastWalkKickExecution = timeStampLastWalkKickExecution; } // Our defense does not move instantly if not alone, to make sure no false whistle was detected (WhistleHandlerDortmund changes state back) @@ -106,7 +109,7 @@ option(BodyControl) { Walk(WalkRequest::speed, 0, 0, 0); } - else if (theGameSymbols.timeSinceLastPenalty < 5000) // Initially theGameSymbols.timeSinceLastPenalty = 100000. + else if (theGameSymbols.timeSinceLastPenalty < 10000 && theRobotPose.validity < 0.7f) // Initially theGameSymbols.timeSinceLastPenalty = 100000. { theHeadControlRequest.controlType = HeadControlRequest::localize; Walk(WalkRequest::speed, 0, 0, 0); diff --git a/Src/Modules/BehaviorControl/CABSL/Options/Controls/RobotSafetyControl.h b/Src/Modules/BehaviorControl/CABSL/Options/Controls/RobotSafetyControl.h new file mode 100644 index 00000000..29eea9aa --- /dev/null +++ b/Src/Modules/BehaviorControl/CABSL/Options/Controls/RobotSafetyControl.h @@ -0,0 +1,44 @@ +/* +** Option to control stafety behavior differently for different malfunctions. +*/ +option(RobotSafetyControl) +{ + common_transition + { + if ((theGameInfo.state != STATE_PLAYING && theGameInfo.state != STATE_SET && theGameInfo.state != STATE_READY) || theRobotInfo.penalty != PENALTY_NONE) + goto wait; + } + + initial_state(wait) + { + transition + { + if ((theGameInfo.state == STATE_PLAYING || theGameInfo.state == STATE_SET || theGameInfo.state == STATE_READY) && theRobotInfo.penalty == PENALTY_NONE) + goto standing; + } + action {} + } + + state(standing) + { + transition + { + if (!theMotionState.jointStatus.usableLegs) + goto safety_shutdown; + } + action {} + } + + state(safety_shutdown) + { + transition + { + if (action_done) + goto safety_shutdown; + } + action + { + RobotSafetyShutdown(); + } + } +} diff --git a/Src/Modules/BehaviorControl/CABSL/Options/Playing.h b/Src/Modules/BehaviorControl/CABSL/Options/Playing.h index 28898db7..47daff1b 100644 --- a/Src/Modules/BehaviorControl/CABSL/Options/Playing.h +++ b/Src/Modules/BehaviorControl/CABSL/Options/Playing.h @@ -8,15 +8,13 @@ option(Playing) { common_transition { - if (libTactic.interceptBallPossible && theRoleSymbols.role != BehaviorData::RoleAssignment::keeper && theRoleSymbols.role == BehaviorData::RoleAssignment::replacementKeeper) - goto interceptBall; + /*if (libTactic.interceptBallPossible && theRoleSymbols.role != BehaviorData::RoleAssignment::keeper && theRoleSymbols.role == BehaviorData::RoleAssignment::replacementKeeper) + goto interceptBall;*/ + + // some roles have special behavior handled in their own options switch (theRoleSymbols.role) { - case BehaviorData::ballchaser: - // chase the ball and try to score a goal - goto ballchaser; - break; case BehaviorData::keeper: // defend the own goal goto keeper; @@ -26,8 +24,10 @@ option(Playing) goto replacementKeeper; break; default: - // no special role behavior needed => walk to position given in PositioningSymbols - goto positioning; + if (theBallChaserDecision.playerNumberToBall == theRobotInfo.number) + goto ballchaser; + else + goto positioning; } } @@ -42,6 +42,7 @@ option(Playing) } } + /* state(interceptBall) { transition {} @@ -52,6 +53,7 @@ option(Playing) SystemCall::playSound("doh.wav"); } } + */ /** * Walk to the position provided by the PositioningSymbols. diff --git a/Src/Modules/BehaviorControl/CABSL/Options/Roles/Ballchaser.h b/Src/Modules/BehaviorControl/CABSL/Options/Roles/Ballchaser.h index e0dfe843..5a8a3847 100644 --- a/Src/Modules/BehaviorControl/CABSL/Options/Roles/Ballchaser.h +++ b/Src/Modules/BehaviorControl/CABSL/Options/Roles/Ballchaser.h @@ -5,8 +5,6 @@ option(Ballchaser) { transition { - if (libTactic.kickWithOuterFoot) - goto kickWithOuterFoot; if (theBallSymbols.ballProbablyCloseButNotSeen) goto handleBallLost; if (theTacticSymbols.interceptBall) @@ -14,30 +12,11 @@ option(Ballchaser) } action { - Positioning(theBallchaser.kickType != MotionRequest::KickType::walkKick || theBallchaser.walkKickType != WalkRequest::StepRequest::any); + Positioning(theBallchaser.kickType != MotionRequest::KickType::walkKick, true); DecideKick(); } } - state(kickWithOuterFoot) - { - transition - { - if (state_time > 3000 || theMotionInfo.walkRequest.stepRequest != WalkRequest::StepRequest::none) - goto positioning; - } - action - { - Positioning(); - - theMotionRequest.walkRequest.stepRequest = WalkRequest::StepRequest::sideKickOuterFoot; - theMotionRequest.kickRequest.mirror = (theBallSymbols.ballPositionRelative.y() < -20); - - timeStampLastWalkKickExecution = theFrameInfo.time; - libTactic.timeStampLastWalkKickExecution = timeStampLastWalkKickExecution; - } - } - state(handleBallLost) { transition diff --git a/Src/Modules/BehaviorControl/CABSL/Options/Roles/BallchaserKeeper.h b/Src/Modules/BehaviorControl/CABSL/Options/Roles/BallchaserKeeper.h index c93aea61..fc7b42f8 100644 --- a/Src/Modules/BehaviorControl/CABSL/Options/Roles/BallchaserKeeper.h +++ b/Src/Modules/BehaviorControl/CABSL/Options/Roles/BallchaserKeeper.h @@ -31,7 +31,7 @@ option(BallchaserKeeper) theBehaviorData.soccerState = BehaviorData::controlBall; // walk to the ball and kick it if its in a critical position if (gotoFieldCoordinatesFinished && theBallSymbols.ballWasSeen) - WalkKick(theKeeper.optKickTarget, WalkRequest::any); + WalkKick(theKeeper.optKickTarget, theKeeper.optPosition, WalkRequest::any); } } diff --git a/Src/Modules/BehaviorControl/CABSL/Options/Roles/Keeper.h b/Src/Modules/BehaviorControl/CABSL/Options/Roles/Keeper.h index 31e071d8..5cf1da0d 100644 --- a/Src/Modules/BehaviorControl/CABSL/Options/Roles/Keeper.h +++ b/Src/Modules/BehaviorControl/CABSL/Options/Roles/Keeper.h @@ -4,7 +4,8 @@ option(Keeper) common_transition { //If ball was last seen close to the goal, then search for ball - if (theKeeper.ballSearchState != Keeper::KeeperBallSearchState::none && theGameInfo.setPlay != SET_PLAY_PENALTY_KICK && theGameInfo.gamePhase != GAME_PHASE_PENALTYSHOOT) + if (theKeeper.ballSearchState != Keeper::KeeperBallSearchState::none && theGameInfo.setPlay != SET_PLAY_PENALTY_KICK && theGameInfo.gamePhase != GAME_PHASE_PENALTYSHOOT + && (!theBehaviorConfiguration.behaviorParameters.goalieForEvents || theKeeper.ballSearchState != Keeper::KeeperBallSearchState::wait)) { goto search_for_ball; } @@ -32,8 +33,10 @@ option(Keeper) action { //Go to a position relativ to the ball in own penalty area - if ((theGameInfo.setPlay == SET_PLAY_PENALTY_KICK || theGameInfo.gamePhase == GAME_PHASE_PENALTYSHOOT) && !theGameSymbols.ownKickOff) + if ((theGameInfo.setPlay == SET_PLAY_PENALTY_KICK || theGameInfo.gamePhase == GAME_PHASE_PENALTYSHOOT)) SpecialAction(SpecialActionRequest::SpecialActionID::penaltyGoaliePrepareDive); + else if (theBehaviorConfiguration.behaviorParameters.goalieForEvents) //bigger threshold for the goalie to prevent too much positioning in PrepareDive + GoToFieldCoordinates(Pose2f(0_deg, theFieldDimensions.xPosOwnGroundline + 70.f, theFieldDimensions.yPosCenterGoal), 150.f, 100.f, 200.f, 25_deg, true, true); else Positioning(); // Ingmar, 15.07.2020: RotationType towardsBall was used here, has to be reimplemented if it is ever to be used. @@ -96,8 +99,9 @@ option(Keeper) action { + Positioning(theKeeper.useLongKick); - if (action_done) + if (action_done && theBallSymbols.ballWasSeen) { theMotionRequest.kickRequest.kickTarget = theKeeper.optKickTarget; theMotionRequest.kickRequest.mirror = theBallSymbols.ballPositionRelative.y() < 0; diff --git a/Src/Modules/BehaviorControl/CABSL/Options/Roles/ReplacementKeeper.h b/Src/Modules/BehaviorControl/CABSL/Options/Roles/ReplacementKeeper.h index c1171d66..279ccf2f 100644 --- a/Src/Modules/BehaviorControl/CABSL/Options/Roles/ReplacementKeeper.h +++ b/Src/Modules/BehaviorControl/CABSL/Options/Roles/ReplacementKeeper.h @@ -13,7 +13,7 @@ option(ReplacementKeeper) if (theReplacementKeeper.kickIt && gotoFieldCoordinatesFinished) { - WalkKick(Pose2f(thePositioningSymbols.optPosition).translate(3000.f, 0.f).translation, WalkRequest::any); + WalkKick(Pose2f(thePositioningSymbols.optPosition).translate(1500.f, 0.f).translation, thePositioningSymbols.optPosition, WalkRequest::any); } } } diff --git a/Src/Modules/BehaviorControl/CABSL/Options/Skills/BallSearch/KeeperBallSearch.h b/Src/Modules/BehaviorControl/CABSL/Options/Skills/BallSearch/KeeperBallSearch.h index 8c45540b..d74c1f0b 100644 --- a/Src/Modules/BehaviorControl/CABSL/Options/Skills/BallSearch/KeeperBallSearch.h +++ b/Src/Modules/BehaviorControl/CABSL/Options/Skills/BallSearch/KeeperBallSearch.h @@ -13,7 +13,10 @@ option(KeeperBallSearch) } if (theKeeper.ballSearchState == Keeper::KeeperBallSearchState::movingBall) { - goto search_for_ball_moving; + if (theBallSymbols.ballPositionRelative.y() > 0.f) + goto search_for_ball_moving_left; + else + goto search_for_ball_moving_right; } if (theKeeper.ballSearchState == Keeper::KeeperBallSearchState::locate) { @@ -32,28 +35,6 @@ option(KeeperBallSearch) } } - state(search_for_ball_moving) - { - transition - { - if (theKeeper.ballSearchState != Keeper::KeeperBallSearchState::movingBall) - { - goto search_for_ball; - } - if (action_done) - { - goto search_for_ball_moving_right; - } - } - action - { - GoToFieldCoordinates(Pose2f(theKeeper.optPosition.rotation - 45, theKeeper.optPosition.translation), 100, 100, 100, 10_deg, false, true); - theHeadControlRequest.controlType = HeadControlRequest::direct; - theHeadControlRequest.pan = 0_deg; - theHeadControlRequest.tilt = 30_deg; - } - } - state(search_for_ball_moving_left) { transition @@ -69,9 +50,9 @@ option(KeeperBallSearch) } action { - GoToFieldCoordinates(Pose2f(theKeeper.optPosition.rotation - 45, theKeeper.optPosition.translation), 100, 100, 100, 10_deg, false, true); + GoToFieldCoordinates(Pose2f(theKeeper.optPosition.rotation + 45_deg, theKeeper.optPosition.translation), 100, 100, 100, 10_deg, false, true); theHeadControlRequest.controlType = HeadControlRequest::direct; - theHeadControlRequest.pan = 0_deg; + theHeadControlRequest.pan = 45_deg; theHeadControlRequest.tilt = 30_deg; } } @@ -91,7 +72,7 @@ option(KeeperBallSearch) } action { - GoToFieldCoordinates(Pose2f(theKeeper.optPosition.rotation + 45, theKeeper.optPosition.translation), 100, 100, 100, 10_deg, false, true); + GoToFieldCoordinates(Pose2f(theKeeper.optPosition.rotation - 45_deg, theKeeper.optPosition.translation), 100, 100, 100, 10_deg, false, true); theHeadControlRequest.controlType = HeadControlRequest::direct; theHeadControlRequest.pan = -45_deg; theHeadControlRequest.tilt = 30_deg; @@ -154,4 +135,4 @@ option(KeeperBallSearch) GoToFieldCoordinates(Pose2f(theKeeper.optPosition.rotation + 180, theKeeper.optPosition.translation), 100, 100, 100, 10_deg, false, true); } } -} \ No newline at end of file +} diff --git a/Src/Modules/BehaviorControl/CABSL/Options/Skills/Cheering.h b/Src/Modules/BehaviorControl/CABSL/Options/Skills/Cheering.h index 5f00add8..09f01ed0 100644 --- a/Src/Modules/BehaviorControl/CABSL/Options/Skills/Cheering.h +++ b/Src/Modules/BehaviorControl/CABSL/Options/Skills/Cheering.h @@ -5,7 +5,7 @@ option(Cheering) transition { if (theOwnTeamInfo.score - theOpponentTeamInfo.score > 0 && theFallDownState.state == FallDownState::upright && state_time < 3000 && theRobotInfo.penalty == PENALTY_NONE - && theGameInfo.gamePhase != GAME_PHASE_PENALTYSHOOT) + && theGameInfo.gamePhase != GAME_PHASE_PENALTYSHOOT && !theGameInfo.firstHalf) goto cheering; else goto sit; @@ -22,14 +22,16 @@ option(Cheering) } action { + // TODO start fire eyes + // Choose a random cheering motion float r = randomFloat(); - if (r < 0.33f) + if (r < 0.20f) + SpecialAction(SpecialActionRequest::cheering4); + else if (r < 0.50f) SpecialAction(SpecialActionRequest::cheering3); - else if (r < 0.66f) - SpecialAction(SpecialActionRequest::cheering2); else - SpecialAction(SpecialActionRequest::cheering1); + SpecialAction(SpecialActionRequest::cheering2); } } diff --git a/Src/Modules/BehaviorControl/CABSL/Options/Skills/DecideKick.h b/Src/Modules/BehaviorControl/CABSL/Options/Skills/DecideKick.h index a9198f5a..98a132ec 100644 --- a/Src/Modules/BehaviorControl/CABSL/Options/Skills/DecideKick.h +++ b/Src/Modules/BehaviorControl/CABSL/Options/Skills/DecideKick.h @@ -2,20 +2,30 @@ option(DecideKick) { common_transition { - if (theBallchaser.kickType == MotionRequest::dribble) - goto dribble; - else if (gotoFieldCoordinatesFinished && theBallSymbols.ballWasSeen) + const bool arrivedAtTarget = gotoFieldCoordinatesFinished; + const bool seesBall = theBallSymbols.ballWasSeen; + const bool mayKickBlindAndHasNotKickedRecently = thePositioningAndKickSymbols.kickBlind && theFrameInfo.time > (theSpeedInfo.lastCustomStepTimestamp + 500) && theBallModel.validity > 0.f; + + if (arrivedAtTarget && (seesBall || mayKickBlindAndHasNotKickedRecently)) { - if (theBallchaser.kickType == MotionRequest::longKick) - goto longKick; - else if (theBallchaser.kickType == MotionRequest::walkKick && theSpeedInfo.stepsSinceLastCustomStep > theBehaviorConfiguration.behaviorParameters.minStepsBetweenWalkKicks - && theBallchaser.walkKickType != WalkRequest::StepRequest::none) + const bool dribbleSelected = thePositioningAndKickSymbols.kickType == MotionRequest::dribble; + const bool walkKickSelected = thePositioningAndKickSymbols.kickType == MotionRequest::walkKick && thePositioningAndKickSymbols.walkKickType != WalkRequest::StepRequest::none; + const bool longKickSelected = thePositioningAndKickSymbols.kickType == MotionRequest::longKick; + + if (dribbleSelected) + { + goto dribble; + } + else if (walkKickSelected && theSpeedInfo.stepsSinceLastCustomStep > theBehaviorConfiguration.behaviorParameters.minStepsBetweenWalkKicks) + { goto walkKick; - else - goto noKickSelected; + } + else if (longKickSelected) + { + goto longKick; + } } - else - goto noKickSelected; + goto noKickSelected; } initial_state(noKickSelected) { @@ -27,7 +37,7 @@ option(DecideKick) transition {} action { - Dribble(theBallchaser.optPosition); + Dribble(thePositioningAndKickSymbols.optPosition); } } state(longKick) @@ -35,7 +45,7 @@ option(DecideKick) transition {} action { - LongKick(theBallSymbols.ballPositionRelative.y() < 0, false, theBallchaser.kickTarget, theBallchaser.longKickType); + LongKick(theBallSymbols.ballPositionRelative.y() < 0, false, thePositioningAndKickSymbols.kickTarget, thePositioningAndKickSymbols.longKickType); } } state(walkKick) @@ -43,7 +53,7 @@ option(DecideKick) transition {} action { - WalkKick(theBallchaser.kickTarget, theBallchaser.walkKickType); + WalkKick(thePositioningAndKickSymbols.kickTarget, thePositioningAndKickSymbols.optPosition, thePositioningAndKickSymbols.walkKickType, thePositioningAndKickSymbols.mirrorKick); } } -} \ No newline at end of file +} diff --git a/Src/Modules/BehaviorControl/CABSL/Options/Skills/Dive.h b/Src/Modules/BehaviorControl/CABSL/Options/Skills/Dive.h index d79e089b..b994001a 100644 --- a/Src/Modules/BehaviorControl/CABSL/Options/Skills/Dive.h +++ b/Src/Modules/BehaviorControl/CABSL/Options/Skills/Dive.h @@ -4,7 +4,8 @@ option(Dive) { transition { - bool isPenaltyKick = theGameSymbols.currentSetPlay == SET_PLAY_PENALTY_KICK || theGameInfo.gamePhase == GAME_PHASE_PENALTYSHOOT; + bool isPenaltyKick = theGameSymbols.currentSetPlay == SET_PLAY_PENALTY_KICK || theGameInfo.gamePhase == GAME_PHASE_PENALTYSHOOT + || (theGameInfo.state == STATE_PLAYING && theBehaviorConfiguration.behaviorParameters.goalieForEvents); if (isPenaltyKick) { @@ -16,8 +17,8 @@ option(Dive) if (theBehaviorConfiguration.behaviorParameters.useDive && std::abs(theBallSymbols.yPosWhenBallReachesOwnYAxis) > 300 && std::abs(theRobotPoseAfterPreview.rotation) < 30_deg && theRobotInfo.number == 1) goto dive; - else - goto block; + //else + //goto block; } } action {} @@ -47,6 +48,7 @@ option(Dive) else { SpecialAction(SpecialActionRequest::goalkeeperDefendLeft, false); + theMotionRequest.GoalieIsDiving = true; } } else @@ -59,36 +61,38 @@ option(Dive) else { SpecialAction(SpecialActionRequest::goalkeeperDefendLeft, true); + theMotionRequest.GoalieIsDiving = true; } } } } - state(block) - { - transition - { - if ((theBallSymbols.ballPositionRelativePredicted.x() > 0.f && state_time > 1000) || state_time > 2000) + /*state(block) + { + transition { - goto finished; - } - } - action - { - if (theBehaviorConfiguration.behaviorParameters.behaviorTestmode) // Use save motions for testing - { - if (state_time == 0) - SystemCall::text2Speech("Wide stance"); + if ((theBallSymbols.ballPositionRelativePredicted.x() > 0.f && state_time > 1000) || state_time > 2000) + { + goto finished; + } } - else + action { - SpecialAction(SpecialActionRequest::wideStanceWithStandUp, false); + if(theBehaviorConfiguration.behaviorParameters.behaviorTestmode) // Use save motions for testing + { + if (state_time == 0) + SystemCall::text2Speech("Wide stance"); + } + else + { + SpecialAction(SpecialActionRequest::wideStanceWithStandUp, false); + } + theHeadControlRequest.controlType = HeadControlRequest::direct; + theHeadControlRequest.pan = 0_deg; + theHeadControlRequest.tilt = 30_deg; + } - theHeadControlRequest.controlType = HeadControlRequest::direct; - theHeadControlRequest.pan = 0_deg; - theHeadControlRequest.tilt = 30_deg; - } - } + }*/ state(penalty) { @@ -106,9 +110,15 @@ option(Dive) if (state_time == 0) SystemCall::text2Speech("Penalty left"); } - else + else if (theMotionInfo.motion == MotionRequest::Motion::specialAction && theMotionInfo.specialActionRequest.specialAction == SpecialActionRequest::SpecialActionID::penaltyGoaliePrepareDive) { SpecialAction(SpecialActionRequest::penaltyGoalieDiveLeft, false); + theMotionRequest.GoalieIsDiving = true; + } + else + { + SpecialAction(SpecialActionRequest::goalkeeperDefendLeft, false); + theMotionRequest.GoalieIsDiving = true; } } else @@ -118,9 +128,15 @@ option(Dive) if (state_time == 0) SystemCall::text2Speech("Penalty right"); } - else + else if (theMotionInfo.motion == MotionRequest::Motion::specialAction && theMotionInfo.specialActionRequest.specialAction == SpecialActionRequest::SpecialActionID::penaltyGoaliePrepareDive) { SpecialAction(SpecialActionRequest::penaltyGoalieDiveLeft, true); + theMotionRequest.GoalieIsDiving = true; + } + else + { + SpecialAction(SpecialActionRequest::goalkeeperDefendLeft, true); + theMotionRequest.GoalieIsDiving = true; } } } diff --git a/Src/Modules/BehaviorControl/CABSL/Options/Skills/Dribble.h b/Src/Modules/BehaviorControl/CABSL/Options/Skills/Dribble.h index 9a34890d..7acbf144 100644 --- a/Src/Modules/BehaviorControl/CABSL/Options/Skills/Dribble.h +++ b/Src/Modules/BehaviorControl/CABSL/Options/Skills/Dribble.h @@ -1,7 +1,6 @@ option(Dribble, (Pose2f) optPosition) { timeStampLastDribbleExecution = theFrameInfo.time; - libTactic.timeStampLastDribbleExecution = timeStampLastDribbleExecution; initial_state(dribble) { transition {} diff --git a/Src/Modules/BehaviorControl/CABSL/Options/Skills/GetInPosition.h b/Src/Modules/BehaviorControl/CABSL/Options/Skills/GetInPosition.h deleted file mode 100644 index a1e4dacb..00000000 --- a/Src/Modules/BehaviorControl/CABSL/Options/Skills/GetInPosition.h +++ /dev/null @@ -1,32 +0,0 @@ -option(GetInPosition) -{ - initial_state(positioning) - { - transition - { - // keeper should not be off by 40cm (see below) during penalty kicks/shootout - const bool isPenaltyKeeper = (theRoleSymbols.role == BehaviorData::RoleAssignment::keeper || theRoleSymbols.role == BehaviorData::RoleAssignment::replacementKeeper) - && (theGameInfo.setPlay == SET_PLAY_PENALTY_KICK || theGameInfo.gamePhase == GAME_PHASE_PENALTYSHOOT); - if (action_done && !isPenaltyKeeper) - goto waitForSet; - } - action - { - GoToFieldCoordinates( - thePositioningSymbols.optPosition, thePositioningSymbols.thresholdXFront, thePositioningSymbols.thresholdXBack, thePositioningSymbols.thresholdY, thePositioningSymbols.thresholdRotation, true, true); - } - } - - state(waitForSet) - { - transition - { - if (thePositioningSymbols.distanceToOptPosition >= 400.f || thePositioningSymbols.inIllegalPosition) - goto positioning; - } - action - { - Stand(); - } - } -} diff --git a/Src/Modules/BehaviorControl/CABSL/Options/Skills/GoToFieldCoordinates.h b/Src/Modules/BehaviorControl/CABSL/Options/Skills/GoToFieldCoordinates.h index 540e9f1e..0fd708ac 100644 --- a/Src/Modules/BehaviorControl/CABSL/Options/Skills/GoToFieldCoordinates.h +++ b/Src/Modules/BehaviorControl/CABSL/Options/Skills/GoToFieldCoordinates.h @@ -25,7 +25,6 @@ option(GoToFieldCoordinates, float thresh_x_back_mod = thresh_x_back; float thresh_y_mod = thresh_y; Angle thresh_rot_mod = thresh_rot; - if (preciseArrival) { thresh_x_front_mod = std::min(theBehaviorConfiguration.gotoBaseThresh, thresh_x_front_mod); @@ -50,11 +49,19 @@ option(GoToFieldCoordinates, gotoFieldCoordinatesFinished = true; transition { - float alpha = std::min(((float)state_time) / theBehaviorConfiguration.gotoThreshMaxTime, 1.f); - float thresh_x_front_mod = std::min(theBehaviorConfiguration.gotoBaseThresh, thresh_x_front) * (1 - alpha) + std::max(theBehaviorConfiguration.gotoBaseThresh, thresh_x_front) * alpha; - float thresh_x_back_mod = std::min(theBehaviorConfiguration.gotoBaseThresh, thresh_x_back) * (1 - alpha) + std::max(theBehaviorConfiguration.gotoBaseThresh, thresh_x_back) * alpha; - float thresh_y_mod = std::min(theBehaviorConfiguration.gotoBaseThresh, thresh_y) * (1 - alpha) + std::max(theBehaviorConfiguration.gotoBaseThresh, thresh_y) * alpha; - Angle thresh_rot_mod = std::min(theBehaviorConfiguration.gotoBaseThreshRot, thresh_rot) * (1 - alpha) + std::max(theBehaviorConfiguration.gotoBaseThreshRot, thresh_rot) * alpha; + float thresh_x_front_mod = thresh_x_front; + float thresh_x_back_mod = thresh_x_back; + float thresh_y_mod = thresh_y; + Angle thresh_rot_mod = thresh_rot; + if (preciseArrival) + { + float alpha = std::min(((float)state_time) / theBehaviorConfiguration.gotoThreshMaxTime, 1.f); + thresh_x_front_mod = std::min(theBehaviorConfiguration.gotoBaseThresh, thresh_x_front) * (1 - alpha) + std::max(theBehaviorConfiguration.gotoBaseThresh, thresh_x_front) * alpha; + thresh_x_back_mod = std::min(theBehaviorConfiguration.gotoBaseThresh, thresh_x_back) * (1 - alpha) + std::max(theBehaviorConfiguration.gotoBaseThresh, thresh_x_back) * alpha; + thresh_y_mod = std::min(theBehaviorConfiguration.gotoBaseThresh, thresh_y) * (1 - alpha) + std::max(theBehaviorConfiguration.gotoBaseThresh, thresh_y) * alpha; + thresh_rot_mod = std::min(theBehaviorConfiguration.gotoBaseThreshRot, thresh_rot) * (1 - alpha) + std::max(theBehaviorConfiguration.gotoBaseThreshRot, thresh_rot) * alpha; + } + ELLIPSE("behavior:GoToFieldCoordinates:Thresholds", Vector2f(optPosition.translation.x() + (thresh_x_front_mod - thresh_x_back_mod) / 2, optPosition.translation.y()), (thresh_x_front_mod + thresh_x_back_mod) / 2, @@ -78,6 +85,8 @@ option(GoToFieldCoordinates, if (state_time > 3000 && ((theRoleSymbols.role != BehaviorData::keeper && theRoleSymbols.role != BehaviorData::replacementKeeper) || theGameInfo.state != STATE_PLAYING) && ((theBallSymbols.timeSinceLastSeenByTeam < 8000.f && theBallSymbols.ballPositionRelative.norm() > 1300.f) || theGameInfo.state != STATE_PLAYING)) Stand(); + else if ((state_time > 3000) && (theRoleSymbols.role == BehaviorData::keeper) && theBehaviorConfiguration.behaviorParameters.goalieForEvents) + SpecialAction(SpecialActionRequest::SpecialActionID::penaltyGoaliePrepareDive); else Walk(WalkRequest::speed, 0, 0, 0); } diff --git a/Src/Modules/BehaviorControl/CABSL/Options/Skills/RobotSafetyShutdown.h b/Src/Modules/BehaviorControl/CABSL/Options/Skills/RobotSafetyShutdown.h new file mode 100644 index 00000000..b59d5fab --- /dev/null +++ b/Src/Modules/BehaviorControl/CABSL/Options/Skills/RobotSafetyShutdown.h @@ -0,0 +1,31 @@ +/** This option lets the robot sit down in case of leg joint malfunktions. */ +option(RobotSafetyShutdown) +{ + initial_state(broken_leg) + { + transition + { + if ((theBehaviorData.soccerState == BehaviorData::safetyShutdown || !theMotionState.jointStatus.usableLegs) && theBehaviorData.behaviorState != BehaviorData::testingJoints + && theGameInfo.state != STATE_INITIAL) + { + goto error_message; + } + } + action {} + } + + + target_state(error_message) + { + transition {} + action + { + if (theBehaviorData.soccerState != BehaviorData::safetyShutdown) + { + SystemCall::playSound("alarm.wav"); + SystemCall::text2Speech("Haelp, Leg broken."); + theBehaviorData.soccerState = BehaviorData::safetyShutdown; + } + } + } +} diff --git a/Src/Modules/BehaviorControl/CABSL/Options/Skills/StandUp.h b/Src/Modules/BehaviorControl/CABSL/Options/Skills/StandUp.h index 88edddcf..c4dffd4a 100644 --- a/Src/Modules/BehaviorControl/CABSL/Options/Skills/StandUp.h +++ b/Src/Modules/BehaviorControl/CABSL/Options/Skills/StandUp.h @@ -1,7 +1,29 @@ /** This option lets the robot stand up when it has fallen down. */ option(StandUp) { - initial_state(lying) + initial_state(falling) + { + transition + { + if (theFallDownState.state == FallDownState::flying) + { + goto standing; + } + else if (theFallDownState.standUpOnlyWhenLyingStill) + { + if (theFallDownState.state == FallDownState::onGroundLyingStill) + goto lying; + } + else + { + if (theFallDownState.state == FallDownState::onGround) + goto lying; + } + } + action {} + } + + state(lying) { transition { @@ -9,6 +31,8 @@ option(StandUp) goto lyingOnBack; else if (theFallDownState.direction == FallDownState::front) goto lyingOnFront; + else if (theMotionRequest.GoalieIsDiving && (theFallDownState.direction == FallDownState::left || theFallDownState.direction == FallDownState::right)) + goto lyingOnSideGoalie; else if (theFallDownState.direction == FallDownState::left || theFallDownState.direction == FallDownState::right) goto lyingOnSide; else @@ -28,12 +52,8 @@ option(StandUp) } action { - if (!theMotionState.standUpStatus.standUp[theMotionSettings.standUpMotionBack]) - SpecialAction(theMotionSettings.standUpMotionBack, false); - else if (!theMotionState.standUpStatus.standUp[theMotionSettings.standUpMotionBackSafe]) - SpecialAction(theMotionSettings.standUpMotionBackSafe, false); - else - SpecialAction(theMotionSettings.standUpMotionBackSafest, false); + theMotionRequest.GoalieIsDiving = false; + SpecialAction(theMotionState.standUpStatus.bestStandUpMotionBack, false); } } @@ -48,12 +68,8 @@ option(StandUp) } action { - if (!theMotionState.standUpStatus.standUp[theMotionSettings.standUpMotionFront]) - SpecialAction(theMotionSettings.standUpMotionFront, false); - else if (!theMotionState.standUpStatus.standUp[theMotionSettings.standUpMotionFrontSafe]) - SpecialAction(theMotionSettings.standUpMotionFrontSafe, false); - else - SpecialAction(theMotionSettings.standUpMotionFrontSafest, false); + theMotionRequest.GoalieIsDiving = false; + SpecialAction(theMotionState.standUpStatus.bestStandUpMotionFront, false); } } @@ -81,29 +97,31 @@ option(StandUp) } } - state(standing_up) + state(lyingOnSideGoalie) { transition { - if (theMotionInfo.motion != MotionRequest::specialAction && theFallDownState.state == FallDownState::onGround) + if (state_time > 3000) // TODO: check this + { + goto lying; + } + if (state_time > 1000) { if (theFallDownState.direction == FallDownState::back) goto lyingOnBack; else if (theFallDownState.direction == FallDownState::front) goto lyingOnFront; - else if (theFallDownState.direction == FallDownState::left || theFallDownState.direction == FallDownState::right) - goto lyingOnSide; + else if (theFallDownState.state == FallDownState::upright) + goto standing_up; } - else if (theMotionInfo.motion == MotionRequest::walk) - goto standing; } action { - Walk(WalkRequest::speed, 0, 0, 0); + SpecialAction(SpecialActionRequest::standUpSideNaoGoalie, theFallDownState.direction == FallDownState::right); } } - target_state(standing) + state(standing_up) { transition { @@ -116,10 +134,33 @@ option(StandUp) else if (theFallDownState.direction == FallDownState::left || theFallDownState.direction == FallDownState::right) goto lyingOnSide; } + else if (theSpecialActionsOutput.isFallProtectionNeeded) + goto falling; + else + goto standing; } - action + action {} + } + + target_state(standing) + { + transition { - Walk(WalkRequest::speed, 0, 0, 0); + if (theFallDownState.state != FallDownState::flying) + { + if (theMotionInfo.motion != MotionRequest::specialAction && theFallDownState.state == FallDownState::onGround) + { + if (theFallDownState.direction == FallDownState::back) + goto lyingOnBack; + else if (theFallDownState.direction == FallDownState::front) + goto lyingOnFront; + else if (theFallDownState.direction == FallDownState::left || theFallDownState.direction == FallDownState::right) + goto lyingOnSide; + } + else if (theSpecialActionsOutput.isFallProtectionNeeded) + goto falling; + } } + action {} } } diff --git a/Src/Modules/BehaviorControl/CABSL/Options/Skills/WalkKick.h b/Src/Modules/BehaviorControl/CABSL/Options/Skills/WalkKick.h index 0d9c62b0..eb37ac97 100644 --- a/Src/Modules/BehaviorControl/CABSL/Options/Skills/WalkKick.h +++ b/Src/Modules/BehaviorControl/CABSL/Options/Skills/WalkKick.h @@ -1,4 +1,4 @@ -option(WalkKick, (Vector2f) kickTarget, ((WalkRequest) StepRequest)(WalkRequest::StepRequest::none) stepRequest) +option(WalkKick, (Vector2f) kickTarget, (Pose2f) kickPose, ((WalkRequest) StepRequest)(WalkRequest::StepRequest::none) stepRequest, (bool)(false) mirror) { initial_state(kick) { @@ -6,6 +6,8 @@ option(WalkKick, (Vector2f) kickTarget, ((WalkRequest) StepRequest)(WalkRequest: { theMotionRequest.walkRequest.stepRequest = stepRequest; theMotionRequest.kickRequest.kickTarget = kickTarget; + theMotionRequest.kickRequest.kickPose = kickPose; + theMotionRequest.kickRequest.mirror = mirror; } } } \ No newline at end of file diff --git a/Src/Modules/BehaviorControl/CABSL/Options/Start.h b/Src/Modules/BehaviorControl/CABSL/Options/Start.h index a4d0b520..3408bf04 100644 --- a/Src/Modules/BehaviorControl/CABSL/Options/Start.h +++ b/Src/Modules/BehaviorControl/CABSL/Options/Start.h @@ -1,12 +1,21 @@ option(Start) { + common_transition + { + // check for malfunctions + if (theBehaviorData.soccerState == BehaviorData::safetyShutdown) + { + goto malfunction; + } + } + initial_state(playDead) { transition { if ((SystemCall::getMode() == SystemCall::simulatedRobot) // Don't wait for the button in SimRobot || (Global::getSettings().recover) // Skip playDead state at a restart after a crash - || (theRobotInfo.transitionToFramework == 1.f)) // ndevilsbase starts transition to framework (chest button was pressed once) + || (theRobotInfo.transitionToFramework > 0.f)) // ndevilsbase starts transition to framework (chest button was pressed once) goto startBehavior; } action @@ -27,6 +36,18 @@ option(Start) SystemCall::text2Speech("Starting calibration"); goto autoCalibrate; } + if (theFrameInfo.getTimeSince(theKeySymbols.lastTimeNotPressed[KeyStates::headRear]) > 100 + && theFrameInfo.getTimeSince(theKeySymbols.lastTimeNotPressed[KeyStates::chest]) > 100 && theGameInfo.state == STATE_INITIAL) + { + SystemCall::text2Speech("Testing Joints"); + goto testing; + } + if (theFrameInfo.getTimeSince(theKeySymbols.lastTimeNotPressed[KeyStates::headMiddle]) > 100 + && theFrameInfo.getTimeSince(theKeySymbols.lastTimeNotPressed[KeyStates::chest]) > 100 && theGameInfo.state == STATE_INITIAL) + { + SystemCall::text2Speech("Testing Joints unstiff"); + goto testingUnstiff; + } // Penalty shootout and robot is penalized if ((Global::getSettings().gameMode == Settings::GameMode::penaltyShootout) && (theRobotInfo.penalty != PENALTY_NONE)) goto sitDownPenalty; @@ -49,6 +70,7 @@ option(Start) SoundControl(); // Does nothing yet but tells the role BodyControl(); // Handles all other parts of the game StandUpControl(); // Triggers stand up motions when robot falls + RobotSafetyControl(); // Triggers safety motion when robot has a broken leg joint theBehaviorData.behaviorState = BehaviorData::BehaviorState::game; } } @@ -66,7 +88,55 @@ option(Start) } action { - AutoCalibrate(); + RobotSafetyControl(); + if (theBehaviorData.soccerState != BehaviorData::safetyShutdown) + AutoCalibrate(); + } + } + state(malfunction) + { + transition {} + action + { + if (theFallDownState.state == FallDownState::upright) + { + SpecialAction(SpecialActionRequest::sitDown, false); + } + else + { + SpecialAction(SpecialActionRequest::playDead, false); + } + } + } + + state(testing) + { + transition + { + // ndevilsbase stopped framework (chest button was pressed three times or all head buttons pressed 1 sec) + if (theRobotInfo.transitionToFramework == 0.f) + goto sitDown; + else if (action_done && state_time > 200) // calibration finished + goto startBehavior; + } + action + { + Testing(); + } + } + state(testingUnstiff) + { + transition + { + // ndevilsbase stopped framework (chest button was pressed three times or all head buttons pressed 1 sec) + if (theRobotInfo.transitionToFramework == 0.f) + goto sitDown; + else if (action_done && state_time > 200) // calibration finished + goto startBehavior; + } + action + { + TestingUnstiff(); } } @@ -75,7 +145,7 @@ option(Start) { transition { - if (state_time > 4000) + if (state_time > 1000) goto stopBehavior; } action @@ -127,13 +197,14 @@ option(Start) transition { // ndevilsbase starts transition to framework (chest button was pressed once) - if (theRobotInfo.transitionToFramework == 1.f) + if (theRobotInfo.transitionToFramework > 0.f) goto startBehavior; } action { SpecialAction(SpecialActionRequest::sitDown); - theBehaviorData.soccerState = BehaviorData::SoccerState::waiting; + if (theBehaviorData.soccerState != BehaviorData::safetyShutdown) + theBehaviorData.soccerState = BehaviorData::SoccerState::waiting; theBehaviorData.behaviorState = BehaviorData::game; } } diff --git a/Src/Modules/BehaviorControl/CABSL/Options/Testing/Testing.h b/Src/Modules/BehaviorControl/CABSL/Options/Testing/Testing.h new file mode 100644 index 00000000..0bbe644d --- /dev/null +++ b/Src/Modules/BehaviorControl/CABSL/Options/Testing/Testing.h @@ -0,0 +1,39 @@ +// Testing Joints + +option(Testing) +{ + initial_state(startTesting) + { + transition + { + if (state_time > 1000) + goto jointTesting; + } + action + { + theBehaviorData.behaviorState = BehaviorData::testingJoints; + } + } + + state(jointTesting) + { + transition + { + if (state_time > 300000) + goto endTesting; + } + action + { + SpecialAction(SpecialActionRequest::test, false); + } + } + + target_state(endTesting) + { + transition {} + action + { + theBehaviorData.behaviorState = BehaviorData::game; + } + } +} diff --git a/Src/Modules/BehaviorControl/CABSL/Options/Testing/TestingUnstiff.h b/Src/Modules/BehaviorControl/CABSL/Options/Testing/TestingUnstiff.h new file mode 100644 index 00000000..0741266d --- /dev/null +++ b/Src/Modules/BehaviorControl/CABSL/Options/Testing/TestingUnstiff.h @@ -0,0 +1,39 @@ +// Testing Joints part 2 + +option(TestingUnstiff) +{ + initial_state(startTestingUnstiff) + { + transition + { + if (state_time > 1000) + goto jointTestingUnstiff; + } + action + { + theBehaviorData.behaviorState = BehaviorData::testingJoints; + } + } + + state(jointTestingUnstiff) + { + transition + { + if (state_time > 300000) + goto endTestingUnstiff; + } + action + { + SpecialAction(SpecialActionRequest::testUnstiff, false); + } + } + + target_state(endTestingUnstiff) + { + transition {} + action + { + theBehaviorData.behaviorState = BehaviorData::game; + } + } +} diff --git a/Src/Modules/BehaviorControl/HeadControl/HeadControl.cpp b/Src/Modules/BehaviorControl/HeadControl/HeadControl.cpp index 4b1fa448..1e387efd 100644 --- a/Src/Modules/BehaviorControl/HeadControl/HeadControl.cpp +++ b/Src/Modules/BehaviorControl/HeadControl/HeadControl.cpp @@ -55,6 +55,8 @@ void HeadControl::handleLocalizeBall() void HeadControl::update(HeadAngleRequest& headAngleRequest) { + if (theFallDownState.state != FallDownState::upright) + return; isLookingDown = false; timeForHeadMovement = 2000; @@ -153,7 +155,7 @@ void HeadControl::update(HeadAngleRequest& headAngleRequest) ColorRGBA::black); DRAWTEXT("module:HeadControl:targetQueue", targetQueue.at(i).pointOnField.x(), targetQueue.at(i).pointOnField.y() + 10, 60, ColorRGBA::red, targetQueue.size() - i); if (i > 0) - LINE("module:HeadControl:targetQueue", + ARROW("module:HeadControl:targetQueue", targetQueue.at(i - 1).pointOnField.x(), targetQueue.at(i - 1).pointOnField.y(), targetQueue.at(i).pointOnField.x(), @@ -167,6 +169,8 @@ void HeadControl::update(HeadAngleRequest& headAngleRequest) void HeadControl::lookAtPointOnField(const Vector2f& point, Angle speed, bool waitForReached, int timeAtTarget) { + if (theFallDownState.state == FallDownState::onGround) + return; Angle angleToPointRelative = Transformation::fieldToRobot(theRobotPose, point).angle(); resetQueue(); PointOfInterest newPOI; @@ -211,7 +215,7 @@ void HeadControl::lookAtBall(bool lookAtPercepts) { Angle angle = 75_deg; headControlState = ballSweepWide; - sweepField(-angle, angle, 1000, true, headSpeedOpt); + sweepField(angle, -angle, 1000, true, headSpeedOpt); return; } @@ -240,7 +244,7 @@ void HeadControl::lookAtBall(bool lookAtPercepts) if (std::abs(angleToBall) < 0.4f && ballDistance < 2500 && ballDistance > 400 && timeSinceBallWasSeen > 500 && theBallSymbols.ballVelocityRelative.norm() < 100) { headControlState = ballSweep; - sweepField(angleToBall - 0.3f, angleToBall + 0.3f, ballLostSweepFieldDistance, true, headSpeedOpt); + sweepField(angleToBall + 20_deg, angleToBall - 20_deg, ballLostSweepFieldDistance, true, headSpeedOpt); return; } @@ -249,13 +253,13 @@ void HeadControl::lookAtBall(bool lookAtPercepts) if (ballDistance < 1000) { headControlState = ballSweepWide; - sweepField(-60_deg, 60_deg, ballLostSweepFieldDistance, false, headSpeedOpt, false); + sweepField(60_deg, -60_deg, ballLostSweepFieldDistance, false, headSpeedOpt, false); return; } else { headControlState = ballSweep; - sweepField(-0.3f, 0.3f, ballLostSweepFieldDistance, true, headSpeedOpt); + sweepField(20_deg, -20_deg, ballLostSweepFieldDistance, true, headSpeedOpt); return; } } @@ -299,7 +303,7 @@ void HeadControl::handleBallLost() else*/ { headControlState = ballLost; - sweepField(1.f, -1.f, ballLostSweepFieldDistance, false, 0.5f * (headSpeedOpt + headSpeedMax), true, true); + sweepField(60_deg, -60_deg, ballLostSweepFieldDistance, false, 0.5f * (headSpeedOpt + headSpeedMax), true, true); } } @@ -391,12 +395,20 @@ void HeadControl::calculatePointsOfInterest() handleReadyState(); break; case STATE_SET: - if (theRobotPose.validity > 0.5f && theRobotPose.translation.x() > -1500 && theBallSymbols.ballWasSeen) - lookAtBall(false); - else + if (theGameInfo.setPlay == SET_PLAY_NONE && (theGameInfo.gamePhase == GAME_PHASE_NORMAL || theGameInfo.gamePhase == GAME_PHASE_OVERTIME)) { headControlState = set; - sweepField(-pi_4, pi_4, theRobotPose.translation.norm(), true, headSpeedOpt, false); + sweepField(45_deg, -45_deg, theRobotPose.translation.norm(), true, headSpeedOpt, false); + } + else + { + if (theRobotPose.validity > 0.5f && theRobotPose.translation.x() > -1500 && theBallSymbols.ballWasSeen) + lookAtBall(false); + else + { + headControlState = set; + sweepField(45_deg, -45_deg, theRobotPose.translation.norm(), true, headSpeedOpt, false); + } } break; case STATE_PLAYING: @@ -432,7 +444,7 @@ void HeadControl::handleReadyState() else { headControlState = ready; - sweepField(-45_deg, 45_deg, 3000, true, headSpeedOpt, false, false); + sweepField(45_deg, -45_deg, 3000, true, headSpeedOpt, false, false); return; } @@ -496,16 +508,12 @@ void HeadControl::fillTargetQueue(const bool waitForReached, std::vector theOpponentTeamInfo.score; - int timeRemaining = 30 /* TODO Constant */ - theGameSymbols.timeSinceSetPlayStarted; + // TODO HeadControl doesnt line up with body control!!! In BallchaserProvider waitInOwnSetPlay + // if we are ballchaser, have reached our position, we are in our own set play and we have still time remaining // -> look around - if (theBehaviorData.role == BehaviorData::ballchaser && theSpeedInfo.speed == Pose2f(0, Vector2f::Zero()) && theGameSymbols.ownKickOff - && (theGameInfo.setPlay == SET_PLAY_KICK_IN || theGameInfo.setPlay == SET_PLAY_CORNER_KICK || theGameInfo.setPlay == SET_PLAY_GOAL_KICK || theGameInfo.setPlay == SET_PLAY_PUSHING_FREE_KICK) - && timeRemaining > (winning ? WINNING_WAIT_TIME : LOOSING_WAIT_TIME)) + if (theBehaviorData.playerNumberToBall == theRobotInfo.number && theSpeedInfo.speed == Pose2f(0, Vector2f::Zero()) && theGameSymbols.ownKickOff + && (theGameInfo.setPlay == SET_PLAY_KICK_IN || theGameInfo.setPlay == SET_PLAY_CORNER_KICK || theGameInfo.setPlay == SET_PLAY_GOAL_KICK || theGameInfo.setPlay == SET_PLAY_PUSHING_FREE_KICK)) { sweepField(45_deg, -45_deg, 3000, false, headSpeedOpt); return; @@ -617,7 +625,7 @@ void HeadControl::handlePlayingState() } break; default: - sweepField(-60_deg, 60_deg, 1000, true, headSpeedOpt); + sweepField(60_deg, -60_deg, 1000, true, headSpeedOpt); break; } @@ -701,7 +709,7 @@ void HeadControl::handleGoalie() //OUTPUT_TEXT("Keeper sweep wide"); int timeSinceState = theFrameInfo.getTimeSince(timeStampSwitchedToNewState); if (timeSinceState < 3000) - sweepField(-60_deg, 60_deg, 250, false, headSpeedOpt, false, false); + sweepField(80_deg, -80_deg, 250, false, headSpeedOpt, false, false); // else { int secondsInState = (timeSinceState - 3000) / 1000; @@ -724,7 +732,7 @@ void HeadControl::handleGoalie() if (inGoto && ballBehindMe && theBallSymbols.ballPositionRelative.norm() < 1500) { - sweepField(0.8f, -0.8f, 3000, false, headSpeedOpt); + sweepField(45_deg, -45_deg, 3000, false, headSpeedOpt); // } // if ball is not moving fast and far away, try to stabilize localization, TO TEST! else if (ballSpeed < 100 && theBallSymbols.timeSinceLastSeenByTeam < 3000) @@ -829,12 +837,12 @@ void HeadControl::lookAtRobots() } if (poses.empty()) { - poses.push_back(Pose2f(0, Transformation::robotToField(theRobotPose, Vector2f(3000, 3000)))); - poses.push_back(Pose2f(0, Transformation::robotToField(theRobotPose, Vector2f(1000, -1000)))); + poses.emplace_back(0, Transformation::robotToField(theRobotPose, Vector2f(3000, 3000))); + poses.emplace_back(0, Transformation::robotToField(theRobotPose, Vector2f(1000, -1000))); } else if (poses.size() == 1) { - poses.push_back(Transformation::robotToField(theRobotPose, Vector2f(1000.f, sgn(Transformation::fieldToRobot(theRobotPose, poses[0].translation).y()) * -1000.f))); + poses.emplace_back(0, Transformation::robotToField(theRobotPose, Vector2f(1000.f, sgn(Transformation::fieldToRobot(theRobotPose, poses[0].translation).y()) * -1000.f))); } resetQueue(); std::sort(poses.begin(), poses.end(), sortRobots(this)); @@ -864,7 +872,8 @@ void HeadControl::lookDown() void HeadControl::moveHead() { const bool direct = (theHeadControlRequest.controlType == HeadControlRequest::direct || targetQueue.empty()); - + if (theFallDownState.state == FallDownState::onGround) + return; // from headmotionengine Angle lastPan = theJointSensorData.angles[Joints::headYaw]; if (lastPan == JointAngles::off) @@ -912,6 +921,8 @@ void HeadControl::moveHead() void HeadControl::sweepField(const Angle leftAngle, const Angle rightAngle, const float distance, const bool forceSweepType, const Angle speed, bool sweepNearestFirst, bool waitForReached) { + if (theFallDownState.state == FallDownState::onGround) + return; if (targetQueue.empty() || (lastControlType != theHeadControlRequest.controlType && !forceSweepType) || headControlState != lastHeadControlState) //|| robotState != lastRobotState) TODO: check this { diff --git a/Src/Modules/BehaviorControl/HeadControl/HeadControl.h b/Src/Modules/BehaviorControl/HeadControl/HeadControl.h index 65c50ff2..61154698 100644 --- a/Src/Modules/BehaviorControl/HeadControl/HeadControl.h +++ b/Src/Modules/BehaviorControl/HeadControl/HeadControl.h @@ -16,7 +16,7 @@ #include "Representations/BehaviorControl/BehaviorData.h" #include "Representations/BehaviorControl/BallSymbols.h" #include "Representations/BehaviorControl/GameSymbols.h" -#include "Representations/BehaviorControl/PositioningSymbols.h" +#include "Representations/BehaviorControl/RoleSymbols/PositioningSymbols.h" #include "Representations/Configuration/CameraCalibration.h" #include "Representations/Configuration/FieldDimensions.h" #include "Representations/Configuration/JointCalibration.h" @@ -40,6 +40,7 @@ #include "Representations/Perception/CameraMatrix.h" #include "Representations/Sensing/RobotModel.h" #include "Representations/Sensing/TorsoMatrix.h" +#include "Representations/Sensing/FallDownState.h" MODULE(HeadControl, @@ -51,12 +52,12 @@ MODULE(HeadControl, REQUIRES(CameraMatrix), REQUIRES(CameraMatrixUpper), REQUIRES(FieldDimensions), + REQUIRES(FallDownState), REQUIRES(JointSensorData), REQUIRES(FrameInfo), REQUIRES(GameInfo), REQUIRES(HeadControlRequest), REQUIRES(JointCalibration), - REQUIRES(TeamBallModel), REQUIRES(MotionInfo), REQUIRES(MotionRequest), REQUIRES(RobotDimensions), diff --git a/Src/Modules/BehaviorControl/JoystickControl/JoystickControl.cpp b/Src/Modules/BehaviorControl/JoystickControl/JoystickControl.cpp index 49e9ff86..1e4ab36d 100644 --- a/Src/Modules/BehaviorControl/JoystickControl/JoystickControl.cpp +++ b/Src/Modules/BehaviorControl/JoystickControl/JoystickControl.cpp @@ -13,7 +13,7 @@ MAKE_MODULE(JoystickControl, behaviorControl) -JoystickControl::JoystickControl() : lastExecuteTimeStamp(0), m_inCustomStepTest(false) +JoystickControl::JoystickControl() : m_inCustomStepTest(false) { initialize(); } @@ -30,15 +30,11 @@ void JoystickControl::update(ArmContact& armContact) void JoystickControl::update(HeadControlRequest& headControlRequest) { - execute(); - headControlRequest = localHeadControlRequest; } void JoystickControl::update(MotionRequest& motionRequest) { - execute(); - motionRequest = localMotionRequest; } @@ -66,15 +62,15 @@ void JoystickControl::initialize() // Robot starts with centered head. localHeadControlRequest.controlType = HeadControlRequest::direct; - localHeadControlRequest.pan = 0.f; - localHeadControlRequest.tilt = 0.f; + localHeadControlRequest.pan = 0_deg; + localHeadControlRequest.tilt = 0_deg; // Robot starts playing dead. m_standing = false; playDead(); localMotionRequest.walkRequest.requestType = WalkRequest::speed; localMotionRequest.walkRequest.request.translation << 0.f, 0.f; - localMotionRequest.walkRequest.request.rotation = 0.f; + localMotionRequest.walkRequest.request.rotation = 0_deg; localMotionRequest.kickRequest.kickMotionType = KickRequest::none; // No action running. @@ -82,23 +78,25 @@ void JoystickControl::initialize() m_actionEndTime = 0; } -void JoystickControl::execute() +void JoystickControl::execute(tf::Subflow&) { - // Run this method only once per frame. - if (lastExecuteTimeStamp == theFrameInfo.time) - return; - - lastExecuteTimeStamp = theFrameInfo.time; - MODIFY("module:JoystickControl:parameters", parameters); MODIFY("module:JoystickControl:deviceParameters", parameters.device); - // Read all pending joystick events. - parseJoystickEvents(); + if (theRobotInfo.transitionToFramework >= 0.f) + { + // Read all pending joystick events. + parseJoystickEvents(); - // Process joystick events. - generateHeadControlRequest(); - generateMotionRequest(); + // Process joystick events. + generateHeadControlRequest(); + generateMotionRequest(); + } + else + { + m_standing = false; + playDead(); + } } void JoystickControl::parseJoystickEvents() @@ -188,6 +186,7 @@ void JoystickControl::processChestButton() // Change standing state. if (standing()) { + m_standing = false; sitDown(); // Reset joystick connection. @@ -198,10 +197,9 @@ void JoystickControl::processChestButton() } else { + m_standing = true; stand(); } - - m_standing = !m_standing; } } @@ -262,7 +260,7 @@ void JoystickControl::generateMotionRequest() if (m_inCustomStepTest) { - localMotionRequest.walkRequest.request.translation[0] = 0.5f; + localMotionRequest.walkRequest.request.translation[0] = 1.f; } // Evaluate button press if available. @@ -492,12 +490,12 @@ void JoystickControl::playDead() void JoystickControl::stand() { - specialAction(SpecialActionRequest::stand, false, 5000); + specialAction(SpecialActionRequest::stand, false, 3500); } void JoystickControl::sitDown() { - specialAction(SpecialActionRequest::sitDown, false, 3000); + specialAction(SpecialActionRequest::sitDown, false, 2500); } void JoystickControl::standUpFallen() @@ -512,19 +510,15 @@ void JoystickControl::standUpFallen() // Choose appropriate stand up action if (theFallDownState.state == FallDownState::onGround) { - int duration = 1000; if (theFallDownState.direction == FallDownState::back) { - specialAction(SpecialActionRequest::standUpBackNao, false, 5500); - duration = 5500; + specialAction(theMotionState.standUpStatus.bestStandUpMotionBack, false, 5000); } else if (theFallDownState.direction == FallDownState::front) { - specialAction(SpecialActionRequest::standUpFrontNao, false, 3100); - duration = 3100; + specialAction(theMotionState.standUpStatus.bestStandUpMotionFront, false, 2800); } - // Set duration of action. - m_actionEndTime = theFrameInfo.time + duration; + // Set the action running. m_actionRunning = true; } else @@ -547,6 +541,6 @@ void JoystickControl::kick(bool kickLeft) localMotionRequest.kickRequest.dynamical = true; localMotionRequest.kickRequest.kickTarget << 1000.f, 0.f; // Set duration of action. - m_actionEndTime = theFrameInfo.time + 1000; + m_actionEndTime = theFrameInfo.time + 500; m_actionRunning = true; } diff --git a/Src/Modules/BehaviorControl/JoystickControl/JoystickControl.h b/Src/Modules/BehaviorControl/JoystickControl/JoystickControl.h index 2c146f5d..ee61229a 100644 --- a/Src/Modules/BehaviorControl/JoystickControl/JoystickControl.h +++ b/Src/Modules/BehaviorControl/JoystickControl/JoystickControl.h @@ -15,8 +15,11 @@ // REQUIRES #include "Representations/Sensing/FallDownState.h" #include "Representations/Infrastructure/FrameInfo.h" +#include "Representations/Infrastructure/RobotInfo.h" #include "Representations/Infrastructure/SensorData/KeyStates.h" #include "Representations/MotionControl/KickEngineOutput.h" +#include "Representations/Configuration/MotionSettings.h" +#include "Representations/MotionControl/MotionState.h" // PROVIDES #include "Representations/Sensing/ArmContact.h" #include "Representations/BehaviorControl/HeadControlRequest.h" @@ -34,12 +37,16 @@ MODULE(JoystickControl, REQUIRES(FallDownState), REQUIRES(FrameInfo), + REQUIRES(RobotInfo), REQUIRES(KeyStates), + REQUIRES(MotionSettings), + REQUIRES(MotionState), USES(KickEngineOutput), PROVIDES(ArmContact), // Provide ArmContact to prevent moving arms in front of obstacles. PROVIDES(HeadControlRequest), - PROVIDES(MotionRequest) + PROVIDES(MotionRequest), + HAS_PREEXECUTION ); @@ -83,7 +90,7 @@ class JoystickControl : public JoystickControlBase /** * Main entry point for execution, runs all neccessary methods to update the provided representations. */ - void execute(); + void execute(tf::Subflow&); /** * Reads all pending joystick events. @@ -201,10 +208,6 @@ class JoystickControl : public JoystickControlBase /// The \c MotionRequest is saved in this class member. MotionRequest localMotionRequest; - /// Time stamp of last execution of the method \c executeCommonCode(). - /// Used to execute it only once per frame. - unsigned lastExecuteTimeStamp; - /// true, if the robot stands. /// Only standing the robot is able to do all the other actions. bool m_standing; diff --git a/Src/Modules/BehaviorControl/LEDHandler/LEDHandler.cpp b/Src/Modules/BehaviorControl/LEDHandler/LEDHandler.cpp index 48e1e042..652dee48 100644 --- a/Src/Modules/BehaviorControl/LEDHandler/LEDHandler.cpp +++ b/Src/Modules/BehaviorControl/LEDHandler/LEDHandler.cpp @@ -19,12 +19,35 @@ void LEDHandler::update(LEDRequest& ledRequest) setFrameworkInactiveLEDs(ledRequest); else if (theBehaviorData.behaviorState >= BehaviorData::BehaviorState::firstCalibrationState) setCalibrationLEDs(ledRequest); + else if (theBehaviorData.behaviorState == BehaviorData::BehaviorState::testingJoints) + { + setTestLEDs(ledRequest); + } else { setGameLEDs(ledRequest); } // whistle detection always overrides stuff setWhistleDetectionLEDs(ledRequest); + // flying state as well + setFlyingStateInfo(ledRequest); + setDamagedJoints(ledRequest); + + if (theSystemSensorData.chargingStatus) + { + if (theSystemSensorData.batteryLevel > 0.95f) + { + setRandomizedFireEyes(ledRequest, BehaviorLEDRequest::darkred, BehaviorLEDRequest::darkyellow, BehaviorLEDRequest::yellow); + } + else + { + setRandomizedFireEyes(ledRequest, BehaviorLEDRequest::darkblue, BehaviorLEDRequest::cyan, BehaviorLEDRequest::white); + } + // setDynamicRainbowEyes(ledRequest); + // setStaticRainbowEyes(ledRequest); + // setRotatingEyesTwoColors(ledRequest, BehaviorLEDRequest::blue, BehaviorLEDRequest::magenta, 5); + // setRotatingEyesThreeColors(ledRequest, BehaviorLEDRequest::red, BehaviorLEDRequest::yellow, BehaviorLEDRequest::orange, 5); // fire eyes + } } void LEDHandler::setFrameworkInactiveLEDs(LEDRequest& ledRequest) {} @@ -66,6 +89,43 @@ void LEDHandler::setCalibrationLEDs(LEDRequest& ledRequest) } } } +void LEDHandler::setTestLEDs(LEDRequest& ledRequest) +{ + // set chest to purple as per GORE 2021 rules + ledRequest.ledStates[LEDRequest::chestBlue] = LEDRequest::on; + ledRequest.ledStates[LEDRequest::chestGreen] = LEDRequest::on; + + // use some of the default LEDs + setDetectionInfo(ledRequest); + setBatteryInfo(ledRequest); + setLocaInfo(ledRequest); + // left ear only displays (gc) connection info, not teammate info + LEDRequest::LEDState gcState = (theFrameInfo.getTimeSince(theGameInfo.timeLastPackageReceived) > 2000) ? LEDRequest::blinking : LEDRequest::on; + for (int i = 0; i < 5; i++) + { + ledRequest.ledStates[LEDRequest::earsLeft0Deg + 2 * i] = gcState; + ledRequest.ledStates[LEDRequest::earsLeft36Deg + 2 * i + 1] = LEDRequest::on; + } + + if (theBehaviorData.behaviorState == BehaviorData::BehaviorState::calibrateCameraMatrix) + { + if (theCMCorrectorStatus.state == CMCorrectorStatus::CalibrationState::captureUpper || theCMCorrectorStatus.state == CMCorrectorStatus::CalibrationState::captureLower) + { + const float progress = theCMCorrectorStatus.progress; + + for (int i = 0; i < 12; ++i) + { + const float threshold = i / 12.f; + ledRequest.ledStates[LEDRequest::LED::headLedRearLeft0 + i] = progress > threshold ? LEDRequest::LEDState::on : LEDRequest::LEDState::off; + } + } + else + { + for (int i = 0; i < 12; ++i) + ledRequest.ledStates[LEDRequest::LED::headLedRearLeft0 + i] = LEDRequest::LEDState::blinking; + } + } +} void LEDHandler::setWhistleDetectionLEDs(LEDRequest& ledRequest) { @@ -92,6 +152,361 @@ void LEDHandler::setGameLEDs(LEDRequest& ledRequest) setMotionInfo(ledRequest); } +void LEDHandler::setRandomizedFireEyes(LEDRequest& ledRequest, BehaviorLEDRequest::EyeColor col1, BehaviorLEDRequest::EyeColor col2, BehaviorLEDRequest::EyeColor col3) +{ + if (lastUpdate % 7 == 0) + { + col1LEDs = {0, 0, (rand() % 2 > 0), (rand() % 2 > 0) || (rand() % 2 > 0) || (rand() % 2 > 0), 1, (rand() % 2 > 0) || (rand() % 2 > 0) || (rand() % 2 > 0), (rand() % 2 > 0), 0}; + col2LEDs = {(rand() % 2 > 0), (rand() % 2 > 0), (rand() % 2 > 0) || (rand() % 2 > 0), 1, 1, 1, (rand() % 2 > 0) || (rand() % 2 > 0), (rand() % 2 > 0)}; + col3LEDs = {1, 1, 1, 1, 1, 1, 1, 1}; + lastUpdate = 0; + } + + static const int numOfLEDsPerColor = LEDRequest::faceLeftRed315Deg - LEDRequest::faceLeftRed0Deg + 1; + for (int i = 0; i < numOfLEDsPerColor; i++) + { + if (col1LEDs[i]) + { + setLEDInBothEyesToColor(ledRequest, col1, i, true); + } + else if (col2LEDs[i]) + { + setLEDInBothEyesToColor(ledRequest, col2, i, true); + } + else if (col3LEDs[i]) + { + setLEDInBothEyesToColor(ledRequest, col3, i, true); + } + } + + lastUpdate++; +} + +void LEDHandler::setStaticFireEyes(LEDRequest& ledRequest, BehaviorLEDRequest::EyeColor col1, BehaviorLEDRequest::EyeColor col2, BehaviorLEDRequest::EyeColor col3) +{ + static const int numOfLEDsPerColor = LEDRequest::faceLeftRed315Deg - LEDRequest::faceLeftRed0Deg + 1; + + col1LEDs = {0, 0, 0, 0, 1, 1, 1, 0}; + col2LEDs = {0, 1, 1, 1, 0, 0, 0, 1}; + col3LEDs = {1, 0, 0, 0, 0, 0, 0, 0}; + + if (rotationState < 3) + { + col1LEDs = {0, 0, 0, 0, 1, 0, 0, 0}; + col2LEDs = {0, 0, 1, 1, 0, 1, 1, 0}; + col3LEDs = {1, 1, 0, 0, 0, 0, 0, 1}; + rotationState++; + } + else if (rotationState < 8) + { + col1LEDs = {0, 0, 0, 1, 1, 1, 0, 0}; + col2LEDs = {0, 1, 1, 0, 0, 0, 1, 0}; + col3LEDs = {1, 0, 0, 0, 0, 0, 0, 1}; + rotationState++; + } + else if (rotationState < 13) + { + col1LEDs = {0, 0, 0, 1, 1, 1, 0, 0}; + col2LEDs = {0, 0, 1, 0, 0, 0, 1, 1}; + col3LEDs = {1, 1, 0, 0, 0, 0, 0, 0}; + rotationState++; + } + else if (rotationState < 18) + { + col1LEDs = {0, 0, 1, 1, 1, 1, 0, 0}; + col2LEDs = {0, 1, 0, 0, 0, 0, 1, 0}; + col3LEDs = {1, 0, 0, 0, 0, 0, 0, 1}; + rotationState++; + } + else if (rotationState < 23) + { + col1LEDs = {0, 0, 1, 1, 1, 1, 0, 0}; + col2LEDs = {1, 0, 1, 0, 0, 0, 1, 1}; + col3LEDs = {0, 1, 0, 0, 0, 0, 0, 0}; + rotationState++; + } + else if (rotationState < 28) + { + col1LEDs = {0, 0, 1, 1, 1, 1, 1, 0}; + col2LEDs = {0, 1, 0, 0, 0, 0, 0, 0}; + col3LEDs = {1, 0, 0, 0, 0, 0, 0, 1}; + rotationState++; + } + else if (rotationState < 33) + { + col1LEDs = {0, 0, 1, 1, 1, 1, 0, 0}; + col2LEDs = {1, 0, 1, 0, 0, 0, 1, 1}; + col3LEDs = {0, 1, 0, 0, 0, 0, 0, 0}; + rotationState++; + } + else if (rotationState < 38) + { + col1LEDs = {0, 0, 1, 1, 1, 1, 0, 0}; + col2LEDs = {0, 1, 0, 0, 0, 0, 1, 0}; + col3LEDs = {1, 0, 0, 0, 0, 0, 0, 1}; + rotationState++; + } + else if (rotationState < 43) + { + col1LEDs = {0, 0, 0, 1, 1, 1, 0, 0}; + col2LEDs = {0, 1, 1, 0, 0, 0, 1, 0}; + col3LEDs = {1, 0, 0, 0, 0, 0, 0, 1}; + rotationState = rotationState == 42 ? 0 : rotationState + 1; + } + + for (int i = 0; i < numOfLEDsPerColor; i++) + { + if (col1LEDs[i]) + { + setLEDInBothEyesToColor(ledRequest, col1, i, true); + } + else if (col2LEDs[i]) + { + setLEDInBothEyesToColor(ledRequest, col2, i, true); + } + else if (col3LEDs[i]) + { + setLEDInBothEyesToColor(ledRequest, col3, i, true); + } + } +} + +void LEDHandler::setDynamicRainbowEyes(LEDRequest& ledRequest) +{ + setLEDInBothEyesToColor(ledRequest, BehaviorLEDRequest::white, (rotationState / 10) % 8, true); + setLEDInBothEyesToColor(ledRequest, BehaviorLEDRequest::yellow, (rotationState / 10 + 1) % 8, true); + setLEDInBothEyesToColor(ledRequest, BehaviorLEDRequest::orange, (rotationState / 10 + 2) % 8, true); + setLEDInBothEyesToColor(ledRequest, BehaviorLEDRequest::red, (rotationState / 10 + 3) % 8, true); + setLEDInBothEyesToColor(ledRequest, BehaviorLEDRequest::magenta, (rotationState / 10 + 4) % 8, true); + setLEDInBothEyesToColor(ledRequest, BehaviorLEDRequest::cyan, (rotationState / 10 + 5) % 8, true); + setLEDInBothEyesToColor(ledRequest, BehaviorLEDRequest::blue, (rotationState / 10 + 6) % 8, true); + setLEDInBothEyesToColor(ledRequest, BehaviorLEDRequest::green, (rotationState / 10 + 7) % 8, true); + rotationState++; + if (rotationState == 79) + rotationState = 0; +} + +void LEDHandler::setStaticRainbowEyes(LEDRequest& ledRequest) +{ + setLEDInBothEyesToColor(ledRequest, BehaviorLEDRequest::white, 0, true); + setLEDInBothEyesToColor(ledRequest, BehaviorLEDRequest::yellow, 1, true); + setLEDInBothEyesToColor(ledRequest, BehaviorLEDRequest::orange, 2, true); + setLEDInBothEyesToColor(ledRequest, BehaviorLEDRequest::red, 3, true); + setLEDInBothEyesToColor(ledRequest, BehaviorLEDRequest::magenta, 4, true); + setLEDInBothEyesToColor(ledRequest, BehaviorLEDRequest::cyan, 5, true); + setLEDInBothEyesToColor(ledRequest, BehaviorLEDRequest::blue, 6, true); + setLEDInBothEyesToColor(ledRequest, BehaviorLEDRequest::green, 7, true); + rotationState++; +} + +void LEDHandler::setRotatingEyesTwoColors(LEDRequest& ledRequest, BehaviorLEDRequest::EyeColor col1, BehaviorLEDRequest::EyeColor col2, int duration, bool mirrored) +{ + static const int numOfLEDsPerColor = LEDRequest::faceLeftRed315Deg - LEDRequest::faceLeftRed0Deg + 1; + + std::vector firstColorLEDs = {0, 0, 0, 0, 0, 0, 0, 0}; + + if (rotationState < duration) + { + firstColorLEDs = {1, 0, 1, 0, 1, 0, 1, 0}; + rotationState++; + } + else if (rotationState < 2 * duration) + { + firstColorLEDs = {0, 1, 0, 1, 0, 1, 0, 1}; + rotationState = rotationState == 2 * duration - 1 ? 0 : rotationState + 1; + } + + for (int i = 0; i < numOfLEDsPerColor; i++) + { + if (firstColorLEDs[i]) + { + setLEDInBothEyesToColor(ledRequest, col1, i, mirrored); + } + else + { + setLEDInBothEyesToColor(ledRequest, col2, i, mirrored); + } + } +} + +void LEDHandler::setRotatingEyesThreeColors( + LEDRequest& ledRequest, BehaviorLEDRequest::EyeColor col1, BehaviorLEDRequest::EyeColor col2, BehaviorLEDRequest::EyeColor transition, int duration, bool mirrored) +{ + static const int numOfLEDsPerColor = LEDRequest::faceLeftRed315Deg - LEDRequest::faceLeftRed0Deg + 1; + + std::vector firstColorLEDs = {0, 0, 0, 0, 0, 0, 0, 0}; + std::vector secondColorLEDs = {0, 0, 0, 0, 0, 0, 0, 0}; + std::vector transitionColorLEDs = {0, 0, 0, 0, 0, 0, 0, 0}; + + if (rotationState < duration) + { + firstColorLEDs = {1, 0, 0, 0, 1, 0, 0, 0}; + secondColorLEDs = {0, 0, 1, 0, 0, 0, 1, 0}; + transitionColorLEDs = {0, 1, 0, 1, 0, 1, 0, 1}; + rotationState++; + } + else if (rotationState < 2 * duration) + { + firstColorLEDs = {0, 1, 0, 0, 0, 1, 0, 0}; + secondColorLEDs = {0, 0, 0, 1, 0, 0, 0, 1}; + transitionColorLEDs = {1, 0, 1, 0, 1, 0, 1, 0}; + rotationState++; + } + else if (rotationState < 3 * duration) + { + firstColorLEDs = {0, 0, 1, 0, 0, 0, 1, 0}; + secondColorLEDs = {1, 0, 0, 0, 1, 0, 0, 0}; + transitionColorLEDs = {0, 1, 0, 1, 0, 1, 0, 1}; + rotationState++; + } + else if (rotationState < 4 * duration) + { + firstColorLEDs = {0, 0, 0, 1, 0, 0, 0, 1}; + secondColorLEDs = {0, 1, 0, 0, 0, 1, 0, 0}; + transitionColorLEDs = {1, 0, 1, 0, 1, 0, 1, 0}; + rotationState = rotationState == 4 * duration - 1 ? 0 : rotationState + 1; + } + + for (int i = 0; i < numOfLEDsPerColor; i++) + { + if (firstColorLEDs[i]) + { + setLEDInBothEyesToColor(ledRequest, col1, i, mirrored); + } + else if (secondColorLEDs[i]) + { + setLEDInBothEyesToColor(ledRequest, col2, i, mirrored); + } + else if (transitionColorLEDs[i]) + { + setLEDInBothEyesToColor(ledRequest, transition, i, mirrored); + } + } +} + +void LEDHandler::setLEDInBothEyesToColor(LEDRequest& ledRequest, BehaviorLEDRequest::EyeColor col, int led, bool mirrored) +{ + if (led < 0 || led > 7) + return; + + LEDRequest::LED firstLeft = LEDRequest::faceLeftRed0Deg; + LEDRequest::LED firstRight = LEDRequest::faceRightRed0Deg; + + static const int redOffsetLeft = 0; + static const int greenOffsetLeft = LEDRequest::faceLeftGreen0Deg - LEDRequest::faceLeftRed0Deg; + static const int blueOffsetLeft = LEDRequest::faceLeftBlue0Deg - LEDRequest::faceLeftRed0Deg; + + static const int redOffsetRight = 0; + static const int greenOffsetRight = LEDRequest::faceRightGreen0Deg - LEDRequest::faceRightRed0Deg; + static const int blueOffsetRight = LEDRequest::faceRightBlue0Deg - LEDRequest::faceRightRed0Deg; + + static const int numOfLEDsPerColor = LEDRequest::faceLeftRed315Deg - LEDRequest::faceLeftRed0Deg + 1; + + switch (col) + { + case BehaviorLEDRequest::defaultColor: + ledRequest.ledStates[firstLeft + redOffsetLeft + led] = LEDRequest::off; + ledRequest.ledStates[firstLeft + greenOffsetLeft + led] = LEDRequest::off; + ledRequest.ledStates[firstLeft + blueOffsetLeft + led] = LEDRequest::off; + break; + case BehaviorLEDRequest::darkred: + ledRequest.ledStates[firstLeft + redOffsetLeft + led] = LEDRequest::half; + ledRequest.ledStates[firstLeft + greenOffsetLeft + led] = LEDRequest::off; + ledRequest.ledStates[firstLeft + blueOffsetLeft + led] = LEDRequest::off; + break; + case BehaviorLEDRequest::red: + ledRequest.ledStates[firstLeft + redOffsetLeft + led] = LEDRequest::on; + ledRequest.ledStates[firstLeft + greenOffsetLeft + led] = LEDRequest::off; + ledRequest.ledStates[firstLeft + blueOffsetLeft + led] = LEDRequest::off; + break; + case BehaviorLEDRequest::green: + ledRequest.ledStates[firstLeft + redOffsetLeft + led] = LEDRequest::off; + ledRequest.ledStates[firstLeft + greenOffsetLeft + led] = LEDRequest::on; + ledRequest.ledStates[firstLeft + blueOffsetLeft + led] = LEDRequest::off; + break; + case BehaviorLEDRequest::darkgreen: + ledRequest.ledStates[firstLeft + redOffsetLeft + led] = LEDRequest::off; + ledRequest.ledStates[firstLeft + greenOffsetLeft + led] = LEDRequest::half; + ledRequest.ledStates[firstLeft + blueOffsetLeft + led] = LEDRequest::off; + break; + case BehaviorLEDRequest::blue: + ledRequest.ledStates[firstLeft + redOffsetLeft + led] = LEDRequest::off; + ledRequest.ledStates[firstLeft + greenOffsetLeft + led] = LEDRequest::off; + ledRequest.ledStates[firstLeft + blueOffsetLeft + led] = LEDRequest::on; + break; + case BehaviorLEDRequest::white: + ledRequest.ledStates[firstLeft + redOffsetLeft + led] = LEDRequest::on; + ledRequest.ledStates[firstLeft + greenOffsetLeft + led] = LEDRequest::on; + ledRequest.ledStates[firstLeft + blueOffsetLeft + led] = LEDRequest::on; + break; + case BehaviorLEDRequest::grey: + ledRequest.ledStates[firstLeft + redOffsetLeft + led] = LEDRequest::half; + ledRequest.ledStates[firstLeft + greenOffsetLeft + led] = LEDRequest::half; + ledRequest.ledStates[firstLeft + blueOffsetLeft + led] = LEDRequest::half; + break; + case BehaviorLEDRequest::magenta: + ledRequest.ledStates[firstLeft + redOffsetLeft + led] = LEDRequest::on; + ledRequest.ledStates[firstLeft + greenOffsetLeft + led] = LEDRequest::off; + ledRequest.ledStates[firstLeft + blueOffsetLeft + led] = LEDRequest::on; + break; + case BehaviorLEDRequest::violet: + ledRequest.ledStates[firstLeft + redOffsetLeft + led] = LEDRequest::half; + ledRequest.ledStates[firstLeft + greenOffsetLeft + led] = LEDRequest::off; + ledRequest.ledStates[firstLeft + blueOffsetLeft + led] = LEDRequest::on; + break; + case BehaviorLEDRequest::darkyellow: + ledRequest.ledStates[firstLeft + redOffsetLeft + led] = LEDRequest::on; + ledRequest.ledStates[firstLeft + greenOffsetLeft + led] = LEDRequest::half; + ledRequest.ledStates[firstLeft + blueOffsetLeft + led] = LEDRequest::off; + break; + case BehaviorLEDRequest::yellow: + ledRequest.ledStates[firstLeft + redOffsetLeft + led] = LEDRequest::on; + ledRequest.ledStates[firstLeft + greenOffsetLeft + led] = LEDRequest::on; + ledRequest.ledStates[firstLeft + blueOffsetLeft + led] = LEDRequest::off; + break; + case BehaviorLEDRequest::cyan: + ledRequest.ledStates[firstLeft + redOffsetLeft + led] = LEDRequest::off; + ledRequest.ledStates[firstLeft + greenOffsetLeft + led] = LEDRequest::half; + ledRequest.ledStates[firstLeft + blueOffsetLeft + led] = LEDRequest::on; + break; + case BehaviorLEDRequest::darkblue: + ledRequest.ledStates[firstLeft + redOffsetLeft + led] = LEDRequest::off; + ledRequest.ledStates[firstLeft + greenOffsetLeft + led] = LEDRequest::off; + ledRequest.ledStates[firstLeft + blueOffsetLeft + led] = LEDRequest::half; + break; + case BehaviorLEDRequest::orange: + ledRequest.ledStates[firstLeft + redOffsetLeft + led] = LEDRequest::on; + ledRequest.ledStates[firstLeft + greenOffsetLeft + led] = LEDRequest::half; + ledRequest.ledStates[firstLeft + blueOffsetLeft + led] = LEDRequest::off; + break; + default: + ASSERT(false); + break; + } + + if (mirrored) + { + if (led == 0) + { + ledRequest.ledStates[firstRight + redOffsetRight + numOfLEDsPerColor - 8] = ledRequest.ledStates[firstLeft + redOffsetLeft + led]; + ledRequest.ledStates[firstRight + greenOffsetRight + numOfLEDsPerColor - 8] = ledRequest.ledStates[firstLeft + greenOffsetLeft + led]; + ledRequest.ledStates[firstRight + blueOffsetRight + numOfLEDsPerColor - 8] = ledRequest.ledStates[firstLeft + blueOffsetLeft + led]; + } + else + { + ledRequest.ledStates[firstRight + redOffsetRight + numOfLEDsPerColor - led] = ledRequest.ledStates[firstLeft + redOffsetLeft + led]; + ledRequest.ledStates[firstRight + greenOffsetRight + numOfLEDsPerColor - led] = ledRequest.ledStates[firstLeft + greenOffsetLeft + led]; + ledRequest.ledStates[firstRight + blueOffsetRight + numOfLEDsPerColor - led] = ledRequest.ledStates[firstLeft + blueOffsetLeft + led]; + } + } + else + { + ledRequest.ledStates[firstRight + redOffsetRight + led] = ledRequest.ledStates[firstLeft + redOffsetLeft + led]; + ledRequest.ledStates[firstRight + greenOffsetRight + led] = ledRequest.ledStates[firstLeft + greenOffsetLeft + led]; + ledRequest.ledStates[firstRight + blueOffsetRight + led] = ledRequest.ledStates[firstLeft + blueOffsetLeft + led]; + } +} + /** Battery info to right ear. */ void LEDHandler::setBatteryInfo(LEDRequest& ledRequest) { @@ -103,7 +518,7 @@ void LEDHandler::setBatteryInfo(LEDRequest& ledRequest) /** Connection info to left ear. */ void LEDHandler::setConnectionInfo(LEDRequest& ledRequest) { - LEDRequest::LEDState gcState = (theFrameInfo.getTimeSince(theGameInfo.timeLastPackageReceived) > 2000) ? LEDRequest::blinking : LEDRequest::on; + LEDRequest::LEDState gcState = theGameInfo.controllerConnected ? LEDRequest::blinking : LEDRequest::on; int numberOfConnectedTeammates = std::min(static_cast(theTeammateData.teammates.size()), 4); for (int i = 0; i < numberOfConnectedTeammates + 1; i++) @@ -132,35 +547,47 @@ void LEDHandler::setDetectionInfo(LEDRequest& ledRequest) /** Set role info to right eye. */ void LEDHandler::setRoleInfo(LEDRequest& ledRequest) { + LEDRequest::LEDState state = theBehaviorData.playerNumberToBall == theRobotInfo.number ? LEDRequest::fastBlinking : LEDRequest::on; + + setEyeColor(ledRequest, false, BehaviorLEDRequest::white, LEDRequest::off); + switch (theRoleSymbols.role) { case BehaviorData::keeper: - setEyeColor(ledRequest, false, BehaviorLEDRequest::blue, LEDRequest::on); + setEyeColor(ledRequest, false, BehaviorLEDRequest::blue, state); break; case BehaviorData::defenderLeft: - setEyeColor(ledRequest, false, BehaviorLEDRequest::green, LEDRequest::on, true); + setEyeColor(ledRequest, false, BehaviorLEDRequest::green, state, true); break; case BehaviorData::defenderRight: - setEyeColor(ledRequest, false, BehaviorLEDRequest::green, LEDRequest::on, false, true); - break; - case BehaviorData::ballchaserKeeper: - case BehaviorData::ballchaser: - setEyeColor(ledRequest, false, BehaviorLEDRequest::red, LEDRequest::on); + setEyeColor(ledRequest, false, BehaviorLEDRequest::green, state, false, true); break; case BehaviorData::backupBallchaser: - setEyeColor(ledRequest, false, BehaviorLEDRequest::magenta, LEDRequest::on); + setEyeColor(ledRequest, false, BehaviorLEDRequest::magenta, state); break; case BehaviorData::defenderSingle: - setEyeColor(ledRequest, false, BehaviorLEDRequest::yellow, LEDRequest::on); + setEyeColor(ledRequest, false, BehaviorLEDRequest::green, state); break; case BehaviorData::center: - setEyeColor(ledRequest, false, BehaviorLEDRequest::white, LEDRequest::on); + setEyeColor(ledRequest, false, BehaviorLEDRequest::white, state); break; case BehaviorData::replacementKeeper: - setEyeColor(ledRequest, false, BehaviorLEDRequest::cyan, LEDRequest::on); + setEyeColor(ledRequest, false, BehaviorLEDRequest::cyan, state); break; case BehaviorData::receiver: - setEyeColor(ledRequest, false, BehaviorLEDRequest::magenta, LEDRequest::blinking); + setEyeColor(ledRequest, false, BehaviorLEDRequest::magenta, state); + break; + case BehaviorData::leftWing: + setEyeColor(ledRequest, false, BehaviorLEDRequest::orange, state, true); + break; + case BehaviorData::rightWing: + setEyeColor(ledRequest, false, BehaviorLEDRequest::orange, state, false, true); + break; + case BehaviorData::frontWing: + setEyeColor(ledRequest, false, BehaviorLEDRequest::red, state); + break; + case BehaviorData::backWing: + setEyeColor(ledRequest, false, BehaviorLEDRequest::yellow, state); break; case BehaviorData::noRole: //off @@ -170,12 +597,34 @@ void LEDHandler::setRoleInfo(LEDRequest& ledRequest) } } +/** Set feedback for flying state to eyes. */ +void LEDHandler::setFlyingStateInfo(LEDRequest& ledRequest) +{ + if (theFallDownState.state == FallDownState::flying) + { + setEyeColor(ledRequest, false, BehaviorLEDRequest::white, LEDRequest::off); + setEyeColor(ledRequest, true, BehaviorLEDRequest::white, LEDRequest::off); + setEyeColor(ledRequest, false, BehaviorLEDRequest::orange, LEDRequest::on); + setEyeColor(ledRequest, true, BehaviorLEDRequest::orange, LEDRequest::on); + } +} + +/** Set feedback for broken Joints to eyes. */ +void LEDHandler::setDamagedJoints(LEDRequest& ledRequest) +{ + if (theBehaviorData.soccerState == BehaviorData::safetyShutdown) + { + setRandomizedFireEyes(ledRequest, BehaviorLEDRequest::darkblue, BehaviorLEDRequest::violet, BehaviorLEDRequest::magenta); + } +} + /** Set game state info to chest button. */ void LEDHandler::setGameStateInfo(LEDRequest& ledRequest) { if (theRobotInfo.penalty != PENALTY_NONE) { ledRequest.ledStates[LEDRequest::chestRed] = LEDRequest::on; + setStaticFireEyes(ledRequest, BehaviorLEDRequest::darkred, BehaviorLEDRequest::orange, BehaviorLEDRequest::yellow); return; } @@ -242,7 +691,13 @@ void LEDHandler::setEyeColor(LEDRequest& ledRequest, bool left, BehaviorLEDReque if (onlyRight) useLED = {1, 0, 0, 0, 1, 1, 1, 1}; - LEDRequest::LEDState halfState = s == LEDRequest::off ? LEDRequest::off : LEDRequest::half; + LEDRequest::LEDState halfState = LEDRequest::off; + if (s == LEDRequest::on) + halfState = LEDRequest::half; + else if (s == LEDRequest::blinking) + halfState = LEDRequest::halfBlinking; + else if (s == LEDRequest::fastBlinking) + halfState = LEDRequest::halfFastBlinking; switch (col) { @@ -253,10 +708,18 @@ void LEDHandler::setEyeColor(LEDRequest& ledRequest, bool left, BehaviorLEDReque for (int i = 0; i < numOfLEDsPerColor; i++) ledRequest.ledStates[first + redOffset + i] = useLED[i] ? s : LEDRequest::blinking; break; + case BehaviorLEDRequest::darkred: + for (int i = 0; i < numOfLEDsPerColor; i++) + ledRequest.ledStates[first + redOffset + i] = useLED[i] ? halfState : LEDRequest::halfBlinking; + break; case BehaviorLEDRequest::green: for (int i = 0; i < numOfLEDsPerColor; i++) ledRequest.ledStates[first + greenOffset + i] = useLED[i] ? s : LEDRequest::blinking; break; + case BehaviorLEDRequest::darkgreen: + for (int i = 0; i < numOfLEDsPerColor; i++) + ledRequest.ledStates[first + greenOffset + i] = useLED[i] ? halfState : LEDRequest::halfBlinking; + break; case BehaviorLEDRequest::blue: for (int i = 0; i < numOfLEDsPerColor; i++) ledRequest.ledStates[first + blueOffset + i] = useLED[i] ? s : LEDRequest::blinking; @@ -269,29 +732,53 @@ void LEDHandler::setEyeColor(LEDRequest& ledRequest, bool left, BehaviorLEDReque for (int i = 0; i < numOfLEDsPerColor; i++) ledRequest.ledStates[first + blueOffset + i] = useLED[i] ? s : LEDRequest::blinking; break; + case BehaviorLEDRequest::grey: + for (int i = 0; i < numOfLEDsPerColor; i++) + ledRequest.ledStates[first + redOffset + i] = useLED[i] ? halfState : LEDRequest::halfBlinking; + for (int i = 0; i < numOfLEDsPerColor; i++) + ledRequest.ledStates[first + greenOffset + i] = useLED[i] ? halfState : LEDRequest::halfBlinking; + for (int i = 0; i < numOfLEDsPerColor; i++) + ledRequest.ledStates[first + blueOffset + i] = useLED[i] ? halfState : LEDRequest::halfBlinking; + break; case BehaviorLEDRequest::magenta: for (int i = 0; i < numOfLEDsPerColor; i++) ledRequest.ledStates[first + redOffset + i] = useLED[i] ? s : LEDRequest::blinking; for (int i = 0; i < numOfLEDsPerColor; i++) ledRequest.ledStates[first + blueOffset + i] = useLED[i] ? s : LEDRequest::blinking; break; + case BehaviorLEDRequest::violet: + for (int i = 0; i < numOfLEDsPerColor; i++) + ledRequest.ledStates[first + redOffset + i] = useLED[i] ? halfState : LEDRequest::halfBlinking; + for (int i = 0; i < numOfLEDsPerColor; i++) + ledRequest.ledStates[first + blueOffset + i] = useLED[i] ? s : LEDRequest::blinking; + break; + case BehaviorLEDRequest::darkyellow: + for (int i = 0; i < numOfLEDsPerColor; i++) + ledRequest.ledStates[first + greenOffset + i] = useLED[i] ? s : LEDRequest::blinking; + for (int i = 0; i < numOfLEDsPerColor; i++) + ledRequest.ledStates[first + redOffset + i] = useLED[i] ? halfState : LEDRequest::halfBlinking; + break; case BehaviorLEDRequest::yellow: for (int i = 0; i < numOfLEDsPerColor; i++) ledRequest.ledStates[first + greenOffset + i] = useLED[i] ? s : LEDRequest::blinking; for (int i = 0; i < numOfLEDsPerColor; i++) - ledRequest.ledStates[first + redOffset + i] = useLED[i] ? halfState : LEDRequest::blinking; + ledRequest.ledStates[first + redOffset + i] = useLED[i] ? s : LEDRequest::blinking; break; case BehaviorLEDRequest::cyan: for (int i = 0; i < numOfLEDsPerColor; i++) - ledRequest.ledStates[first + greenOffset + i] = useLED[i] ? halfState : LEDRequest::blinking; + ledRequest.ledStates[first + greenOffset + i] = useLED[i] ? halfState : LEDRequest::halfBlinking; for (int i = 0; i < numOfLEDsPerColor; i++) ledRequest.ledStates[first + blueOffset + i] = useLED[i] ? s : LEDRequest::blinking; break; + case BehaviorLEDRequest::darkblue: + for (int i = 0; i < numOfLEDsPerColor; i++) + ledRequest.ledStates[first + blueOffset + i] = useLED[i] ? halfState : LEDRequest::halfBlinking; + break; case BehaviorLEDRequest::orange: for (int i = 0; i < numOfLEDsPerColor; i++) ledRequest.ledStates[first + redOffset + i] = useLED[i] ? s : LEDRequest::blinking; for (int i = 0; i < numOfLEDsPerColor; i++) - ledRequest.ledStates[first + greenOffset + i] = useLED[i] ? s : LEDRequest::blinking; + ledRequest.ledStates[first + greenOffset + i] = useLED[i] ? halfState : LEDRequest::halfBlinking; break; default: ASSERT(false); @@ -345,10 +832,11 @@ void LEDHandler::setMotionInfo(LEDRequest& ledRequest) ledRequest.ledStates[g] = LEDRequest::on; ledRequest.ledStates[b] = LEDRequest::off; break; - case WalkRequest::StepRequest::rotateKick45: // GREEN + case WalkRequest::StepRequest::rotateKick45: // CYAN + case WalkRequest::StepRequest::keeperKick45: // CYAN ledRequest.ledStates[r] = LEDRequest::off; ledRequest.ledStates[g] = LEDRequest::on; - ledRequest.ledStates[b] = LEDRequest::off; + ledRequest.ledStates[b] = LEDRequest::on; break; case WalkRequest::StepRequest::sideKickOuter45: // BLUE ledRequest.ledStates[r] = LEDRequest::off; diff --git a/Src/Modules/BehaviorControl/LEDHandler/LEDHandler.h b/Src/Modules/BehaviorControl/LEDHandler/LEDHandler.h index ac27f891..4104956a 100644 --- a/Src/Modules/BehaviorControl/LEDHandler/LEDHandler.h +++ b/Src/Modules/BehaviorControl/LEDHandler/LEDHandler.h @@ -23,11 +23,14 @@ #include "Representations/Modeling/WhistleDortmund.h" #include "Representations/BehaviorControl/GameSymbols.h" #include "Representations/MotionControl/StandEngineOutput.h" +#include "Representations/MotionControl/MotionState.h" +#include "Representations/Sensing/FallDownState.h" MODULE(LEDHandler, REQUIRES(BallModel), REQUIRES(BallSymbols), REQUIRES(BehaviorData), + REQUIRES(MotionState), REQUIRES(BehaviorLEDRequest), REQUIRES(RoleSymbols), REQUIRES(FrameInfo), @@ -43,6 +46,7 @@ MODULE(LEDHandler, REQUIRES(GameSymbols), REQUIRES(StandEngineOutput), REQUIRES(CMCorrectorStatus), + REQUIRES(FallDownState), PROVIDES(LEDRequest) ); @@ -55,14 +59,32 @@ class LEDHandler : public LEDHandlerBase void setFrameworkInactiveLEDs(LEDRequest& ledRequest); void setCalibrationLEDs(LEDRequest& ledRequest); + void setTestLEDs(LEDRequest& ledRequest); void setWhistleDetectionLEDs(LEDRequest& ledRequest); void setGameLEDs(LEDRequest& ledRequest); + void setStaticFireEyes(LEDRequest& ledRequest, BehaviorLEDRequest::EyeColor col1, BehaviorLEDRequest::EyeColor col2, BehaviorLEDRequest::EyeColor col3); + void setRandomizedFireEyes(LEDRequest& ledRequest, BehaviorLEDRequest::EyeColor col1, BehaviorLEDRequest::EyeColor col2, BehaviorLEDRequest::EyeColor col3); + void setDynamicRainbowEyes(LEDRequest& ledRequest); + void setStaticRainbowEyes(LEDRequest& ledRequest); + // duration means number of update calls + void setRotatingEyesTwoColors(LEDRequest& ledRequest, BehaviorLEDRequest::EyeColor col1, BehaviorLEDRequest::EyeColor col2, int duration, bool mirrored); + void setRotatingEyesThreeColors(LEDRequest& ledRequest, BehaviorLEDRequest::EyeColor col1, BehaviorLEDRequest::EyeColor col2, BehaviorLEDRequest::EyeColor transition, int duration, bool mirrored); + // led must be between 0 and 7: 0=N, 1=NW, 2=W, 3=SW, 4=S, 5=SE, 6=E, 7=NE + void setLEDInBothEyesToColor(LEDRequest& ledRequest, BehaviorLEDRequest::EyeColor col, int led, bool mirrored); void setBatteryInfo(LEDRequest& ledRequest); /**< Battery info to right ear. */ void setConnectionInfo(LEDRequest& ledRequest); /**< Connection info to left ear. */ void setDetectionInfo(LEDRequest& ledRequest); /**< Set perception info to left eye. */ void setRoleInfo(LEDRequest& ledRequest); /**< Set role info to right eye. */ + void setFlyingStateInfo(LEDRequest& ledRequest); /**< Set feedback for flying state to eyes. */ + void setDamagedJoints(LEDRequest& ledRequest); /**< Set feedback for broken Joints to eyes. */ void setGameStateInfo(LEDRequest& ledRequest); /**< Set game state info to chest button. */ void setLocaInfo(LEDRequest& ledRequest); /**< Set loca info on head. */ void setMotionInfo(LEDRequest& ledRequest); /**< Set motion info to feet. */ void setObstacleInfo(LEDRequest& ledRequest); /**< Set obstacle info to feet. */ + + int rotationState = 0; + int lastUpdate = 0; + std::vector col1LEDs = {0, 0, 0, 0, 1, 1, 1, 0}; + std::vector col2LEDs = {0, 1, 1, 1, 0, 0, 0, 1}; + std::vector col3LEDs = {1, 1, 1, 1, 1, 1, 1, 1}; }; diff --git a/Src/Modules/BehaviorControl/TacticControl/BallChaserDecisionProvider.cpp b/Src/Modules/BehaviorControl/TacticControl/BallChaserDecisionProvider.cpp index 6c56b058..1d8dca64 100644 --- a/Src/Modules/BehaviorControl/TacticControl/BallChaserDecisionProvider.cpp +++ b/Src/Modules/BehaviorControl/TacticControl/BallChaserDecisionProvider.cpp @@ -21,9 +21,25 @@ */ void BallChaserDecisionProvider::update(BallChaserDecision& ballChaserDecision) { + ballChaserDecision.dynamic = false; + + if (theGameInfo.gamePhase == GAME_PHASE_PENALTYSHOOT) + { + ballChaserDecision.playerNumberToBall = theGameInfo.kickingTeam == theOwnTeamInfo.teamNumber ? theRobotInfo.number : 0; + return; + } + // do not update decision when ball is lost if (theGameInfo.state == STATE_PLAYING && theBallSymbols.timeSinceLastSeenByTeam > 3000) return; + + if (!theTeammateData.wlanOK) + { + decideLocal(ballChaserDecision); + return; + } + ballChaserDecision.dynamic = true; + // Update member variables updateBallBehindMe(); @@ -34,8 +50,8 @@ void BallChaserDecisionProvider::update(BallChaserDecision& ballChaserDecision) if (ballChaserDecision.playerNumberToBall == 0) ballChaserDecision.playerNumberToBall = theRobotInfo.number; - if ( //theTeammateData.messageBudgetFactor < 1.f || - (theGameInfo.state == STATE_READY && theGameSymbols.timeSinceGameState > 10000)) + // Keep ballchaser decision during ready + if (theTacticSymbols.keepRoleAssignment) return; int localBallChaserNumber = calcBallChaserNumber(ballChaserDecision, true); @@ -43,9 +59,10 @@ void BallChaserDecisionProvider::update(BallChaserDecision& ballChaserDecision) * Ignore team decision if local decision is better. This is the case when: * 1. we are the local decision * 2. the goalie is currently not the team decision + * (removed, otherwise nobody will go to the ball if the goalie is penalized or cannot stand up) * 3. our ball percept is newer than the team's OR we are goalie */ - if (teamBallChaserNumber != localBallChaserNumber && localBallChaserNumber == theRobotInfo.number && teamBallChaserNumber != 1 + if (teamBallChaserNumber != localBallChaserNumber && localBallChaserNumber == theRobotInfo.number && (theBallSymbols.timeSinceLastSeen < theFrameInfo.getTimeSince(teamBallLastSeen) || theRobotInfo.number == 1)) { ballChaserDecision.playerNumberToBall = localBallChaserNumber; @@ -61,6 +78,59 @@ void BallChaserDecisionProvider::update(BallChaserDecision& ballChaserDecision) } } +void BallChaserDecisionProvider::decideLocal(BallChaserDecision& ballChaserDecision) +{ + if (theGameInfo.controllerConnected) + { + const auto isActive = [](const RoboCup::RobotInfo& robotInfo) + { + return robotInfo.penalty == PENALTY_NONE; + }; + + // Highest player number will be playerNumberToBall during initial/ready/set + if (theGameInfo.state == STATE_INITIAL || theGameInfo.state == STATE_READY || theGameInfo.state == STATE_SET) + { + for (unsigned char player = MAX_NUM_PLAYERS; player > 0; --player) + { + if (isActive(theOwnTeamInfo.players[player - 1])) + { + ballChaserDecision.playerNumberToBall = player; + return; + } + } + } + // I will be playerNumberToBall if I am the only one + else if (std::count_if(theOwnTeamInfo.players, theOwnTeamInfo.players + MAX_NUM_PLAYERS - 1, isActive) == 1) + { + ballChaserDecision.playerNumberToBall = theRobotInfo.number; + return; + } + } + // If no game controller is connected in initial/ready/set, assume 7 robots are active and the highest number will be playerNumberToBall. + else if (theGameInfo.state == STATE_INITIAL || theGameInfo.state == STATE_READY || theGameInfo.state == STATE_SET) + { + ballChaserDecision.playerNumberToBall = MAX_NUM_PLAYERS; + return; + } + + // Go to ball if no teammate is near and close enough + float ballDistance = theBallSymbols.ballPositionRelative.norm(); + const auto friendNearBall = [&](const RobotMapEntry& entry) + { + return entry.robotType == RobotEstimate::RobotType::teammateRobot && (entry.pose.translation - theBallSymbols.ballPositionField).norm() < maxBallDistanceForTeammateToBeNear; + }; + const bool teammateNearBall = std::find_if(theRobotMap.robots.begin(), theRobotMap.robots.end(), friendNearBall) != theRobotMap.robots.end(); + + if (!teammateNearBall && ballDistance < (ballChaserDecision.playerNumberToBall == theRobotInfo.number ? maxBallDistanceForLocalDecision + 500.f : maxBallDistanceForLocalDecision)) + { + ballChaserDecision.playerNumberToBall = theRobotInfo.number; + } + else + { + ballChaserDecision.playerNumberToBall = 0; + } +} + /** * \brief Determine whether the ball is behind the robot. * @@ -94,15 +164,15 @@ void BallChaserDecisionProvider::updateIsBallBehindPositionForMates(Vector2f loc if (mate.behaviorData.role != BehaviorData::keeper) { // Calculation of isBallBehindPosition analog to own calculation. - Vector2f mateCurrentBallPos = mate.behaviorData.ballPositionField.cast(); - Vector2f matePredictedBallPos = mate.behaviorData.ballPositionFieldPredicted.cast(); - Vector2f mateBallPosition = getBallPosForDecision(mateCurrentBallPos, matePredictedBallPos); + Vector2f mateCurrentBallPos = mate.behaviorData.ballPositionField; + // Passing mateCurrentBallPos twice here, if current or predicted position is used depends on sender + Vector2f mateBallPosition = getBallPosForDecision(mateCurrentBallPos, mateCurrentBallPos); // useLocalBallModelForDecision == true => use my own ball model if (useLocalBallModelForDecision || (theGameInfo.state == STATE_READY && theGameInfo.setPlay != SET_PLAY_PENALTY_KICK)) mateBallPosition = localBallModelsPos; // calculate isBallBehind for mate using selected ball position as reference - bool isBallBehindMate = isBallBehindRobot(mate.number, mate.pose.translation, mateBallPosition); - isBallBehindPosition[mate.number] = isBallBehindMate; + bool isBallBehindMate = isBallBehindRobot(mate.playerNumber, mate.robotPose.translation, mateBallPosition); + isBallBehindPosition[mate.playerNumber] = isBallBehindMate; } } // the keeper never gets penalized if the ball is behind him @@ -150,11 +220,11 @@ bool BallChaserDecisionProvider::isBallBehindRobot(int robotNumber, Vector2f rob { // isBehindMeDistance acts as hysteresis for the switch of the isBallBehindMe boolean. // It is at minimum -250, since we dont want to think the ball is behind us while dribbling. - float isBehindDistance = (isBallBehindPosition[robotNumber] ? timeToBallParams.ballBehindRobotHysteresis : 0.f); + float isBehindDistance = (isBallBehindPosition[robotNumber] ? 0.f : timeToBallParams.ballBehindRobotHysteresis); // We want to know if the robot would have to go around the ball, this is why we use ball to goal center as intended direction - Pose2f ballPose((Vector2f(theFieldDimensions.xPosOpponentGroundline, 0.f) - ballPosition).angle(), ballPosition.x(), ballPosition.y()); - Vector2f ballInBallPoseCoordinates = Transformation::fieldToRobot(ballPose, ballPosition); - bool isBallBehind = ballInBallPoseCoordinates.x() < (robotPosition.x() + isBehindDistance); + Pose2f ballPose((Vector2f(theFieldDimensions.xPosOpponentGroundline, 0.f) - ballPosition).angle(), ballPosition); + Vector2f robotInBallPoseCoordinates = Transformation::fieldToRobot(ballPose, robotPosition); + bool isBallBehind = robotInBallPoseCoordinates.x() > isBehindDistance; return isBallBehind; } @@ -183,19 +253,11 @@ bool BallChaserDecisionProvider::posInOwnPenaltyArea(Vector2f position, float bo std::tuple BallChaserDecisionProvider::getNewestTeamBallChaserNumber() { - const Teammate* newestEventMessage = theTeammateData.getNewestEventMessage(TeamCommEvents::SendReason::newRolesAssigned); + const Teammate* newestEventMessage = theTeammateData.getNewestEventMessage(TeamCommEvents::SendReason::newBallchaser); if (!newestEventMessage) return {0, 0}; - const auto& suggestions = newestEventMessage->behaviorData.roleSuggestions; - for (int playerNum = 0; playerNum < static_cast(suggestions.size()); ++playerNum) - { - if (suggestions[playerNum] == BehaviorData::RoleAssignment::ballchaser - || (newestEventMessage->behaviorData.role == BehaviorData::keeper && newestEventMessage->behaviorData.soccerState == BehaviorData::SoccerState::controlBall)) - return {playerNum, newestEventMessage->ball.timeWhenLastSeen}; - } - - return {0, 0}; + return {newestEventMessage->behaviorData.playerNumberToBall, newestEventMessage->ballModel.timeWhenLastSeen}; } /** @@ -215,7 +277,7 @@ int BallChaserDecisionProvider::calcBallChaserNumber(BallChaserDecision& ballCha // If the robots role is ballchaser, it had the ball last. bool wasBallMine = theRobotInfo.number == ballChaserDecision.playerNumberToBall; // Time to reach ball for each robot number. - std::vector timesToReachBall(MAX_NUM_PLAYERS + 1, std::numeric_limits::max()); + std::vector timesToReachBall(MAX_NUM_PLAYERS + 1, std::numeric_limits::infinity()); float penaltyAreaModifier = (ballWasInPenaltyArea ? 350.f : 150.f); ballWasInPenaltyArea = posInOwnPenaltyArea(ballPositionField, penaltyAreaModifier); @@ -224,14 +286,16 @@ int BallChaserDecisionProvider::calcBallChaserNumber(BallChaserDecision& ballCha theRobotInfo.number, wasBallMine, theFallDownState.state == FallDownState::upright, - theBallSymbols.timeSinceLastSeen > timeToBallParams.notSeenTime); + theBallSymbols.timeSinceLastSeen > timeToBallParams.notSeenTime, + 0); + // The keeper is not allowed to chase the ball outside of the penalty area if (theRobotInfo.penalty == PENALTY_NONE && (theRobotInfo.number != 1 || ballWasInPenaltyArea)) timesToReachBall[theRobotInfo.number] = ballChaserDecision.ownTimeToBall; for (auto& mate : theTeammateData.teammates) { - timesToReachBall[mate.number] = getDistanceToBallForMate(mate, useLocalBall, ballPositionField); + timesToReachBall[mate.playerNumber] = getDistanceToBallForMate(mate, useLocalBall, ballPositionField); } int closestPlayerNumber = int(std::min_element(timesToReachBall.begin(), timesToReachBall.end()) - timesToReachBall.begin()); @@ -252,11 +316,12 @@ int BallChaserDecisionProvider::calcBallChaserNumber(BallChaserDecision& ballCha * correspond to the player number of the team mates. This means the distance for the teammate * with the player number n will be stored in the n-th element. */ -float BallChaserDecisionProvider::getDistanceToBallForMate(Teammate mate, bool useLocalBall, Vector2f localBallPosition) +float BallChaserDecisionProvider::getDistanceToBallForMate(const TeammateReceived& mate, bool useLocalBall, Vector2f localBallPosition) { - bool wasMateBallChaser = std::get<0>(getNewestTeamBallChaserNumber()) == mate.number; + bool wasMateBallChaser = std::get<0>(getNewestTeamBallChaserNumber()) == mate.playerNumber; // Choose the ball position on which the decision for the team mate is based. - Vector2f mateBallPosition = getBallPosForDecision(mate.behaviorData.ballPositionField.cast(), mate.behaviorData.ballPositionFieldPredicted.cast()); + // Passing ballPositionField twice here, if current or predicted position is used depends on sender + Vector2f mateBallPosition = getBallPosForDecision(mate.behaviorData.ballPositionField, mate.behaviorData.ballPositionField); if (useLocalBall) mateBallPosition = localBallPosition; @@ -264,10 +329,11 @@ float BallChaserDecisionProvider::getDistanceToBallForMate(Teammate mate, bool u float matePenaltyAreaModifier = (wasMateBallChaser ? 350.f : 150.f); bool mateBallInPenaltyArea = posInOwnPenaltyArea(mateBallPosition, matePenaltyAreaModifier); - float mateDistance = - calcDistanceToBall(mate.pose, Pose2f(mateBallPosition), mate.number, wasMateBallChaser, mate.isUpright, mate.behaviorData.timeSinceBallWasSeen > timeToBallParams.notSeenTime); + const int timeSinceBallSeen = static_cast(mate.sendTimestamp - mate.ballModel.timeWhenLastSeen); + const bool ballNotSeen = theFrameInfo.getTimeSince(mate.sendTimestamp) < timeToBallParams.notSeenTime && timeSinceBallSeen > timeToBallParams.notSeenTime; + float mateDistance = calcDistanceToBall(mate.robotPose, Pose2f(mateBallPosition), mate.playerNumber, wasMateBallChaser, !mate.fallen, ballNotSeen, theFrameInfo.getTimeSince(mate.sendTimestamp)); - bool mateIsValid = mate.status >= Teammate::ACTIVE && (mate.number != 1 || mateBallInPenaltyArea); + bool mateIsValid = mate.status >= TeammateReceived::ACTIVE && (mate.playerNumber != 1 || mateBallInPenaltyArea); return mateIsValid ? mateDistance : std::numeric_limits::max(); } @@ -286,11 +352,29 @@ float BallChaserDecisionProvider::getDistanceToBallForMate(Teammate mate, bool u * * \return The distance between the from pose and the target pose. */ -float BallChaserDecisionProvider::calcDistanceToBall(const Pose2f& fromPose, const Pose2f& targetOnField, int playerNumber, bool wasBallChaser, bool upright, bool notSeen) +float BallChaserDecisionProvider::calcDistanceToBall(const Pose2f& fromPose, const Pose2f& targetOnField, int playerNumber, bool wasBallChaser, bool upright, bool notSeen, unsigned timeSinceLastUpdate) { + // The keeper should never be the ballchaser in ready when other options are available. + // Otherwise, we cannot replace a specific role by the ballchaser in RoleDynamicProvider. + if ((theGameInfo.state == STATE_READY || theGameInfo.state == STATE_INITIAL) && playerNumber == 1) + return std::numeric_limits::max(); // We cannot use infinity here. Otherwise, the robot will not be considered as a last choice. + const Vector2f targetRelative = Transformation::fieldToRobot(fromPose, targetOnField.translation); float timeToBall = targetRelative.norm(); + // Assume the ballchaser goes to the ball and updates its position only every eventConfig.playerMovedEventDistanceForBallchaser (currently 500mm) + if (wasBallChaser && upright && !notSeen) + { + // Assume the robot walks at about 80% speed on average + float walkDistanceSinceUpdate = 0.8f * theWalkingEngineParams.speedLimits.xForward * timeSinceLastUpdate / 1000.f; + + // The robot cannot move more than 500mm without sending a message + walkDistanceSinceUpdate = std::min(500.f, walkDistanceSinceUpdate); + + // The robot cannot come closer than 200mm to the ball + timeToBall = std::max(200.f, timeToBall - walkDistanceSinceUpdate); + } + switch (timeToBallParams.type) { case TimeToBallParams::distanceAndPosition: @@ -310,6 +394,10 @@ float BallChaserDecisionProvider::calcDistanceToBall(const Pose2f& fromPose, con // add penalties based on the robots current situation. timeToBall += getStatusPenalty(fromPose, targetOnField, playerNumber, wasBallChaser, upright, notSeen); + // Keeper should stay in the goal if other players are near + if (playerNumber == 1 && theGameInfo.setPlay == SET_PLAY_GOAL_KICK && theGameInfo.kickingTeam == theOwnTeamInfo.teamNumber) + timeToBall += timeToBallParams.keeperPenaltyDuringOwnGoalKick; + return timeToBall; } @@ -401,10 +489,6 @@ float BallChaserDecisionProvider::getStatusPenalty(const Pose2f& fromPose, const + (timeToBallParams.penaltyBallBehindFactorOpponentGroundline - timeToBallParams.penaltyBallBehindFactorOwnGroundline) * xPosFactor; ; - if (playerNumber == 3) - { - isBallBehindPosition[playerNumber] = true; - } penalty += (isBallBehindPosition[playerNumber] ? specificBehindPenalty * behindBallPenaltyFactor : 0.f) - (wasBallChaser ? distanceHysteresis : 0.f); if (!upright && notSeen) @@ -417,60 +501,4 @@ float BallChaserDecisionProvider::getStatusPenalty(const Pose2f& fromPose, const return penalty; } -/** - * \brief Checks if the remote ballchaser decision should be overwritten with the local decision. - * - * The local decision should be used if the ball position that was used to make the remote decision - * differs too much from the local ball model's predicted position. This means the ball is moving - * fast and the remote decision might already or soon be outdated. In this case the robot may - * overwrite the remote decision with the local decision which uses much more recent information. - * - * \param remoteDecisionBallPos The ball position that was used to make the remote decision. - * \param ballChaserDecision The local copy of the BallChaserDecision representation. - * - * \return True if the local decision should be used, false if the remote decision is ok. -*/ -bool BallChaserDecisionProvider::useLocalDecision(BallChaserDecision& ballChaserDecision, Vector2f remoteDecisionBallPos) -{ - //float ballPositionDiff = (theBallSymbols.ballPositionFieldPredicted - remoteDecisionBallPos).norm(); - //bool remoteBallIsOutdated = ballPositionDiff > useLocalBallModelWhenDifferenceGreater; - - bool ballIsClose = theBallSymbols.ballPositionRelative.norm() < useLocalBallModelBelowDistance; - - for (const auto& mate : theTeammateData.teammates) - if (mate.status < Teammate::Status::ACTIVE && ballChaserDecision.playerNumberToBall == mate.number) - return true; - - bool shouldUseLocalDecision = (theGameInfo.state == STATE_PLAYING - && useLocalBallModelWhenNear - /*&& remoteBallIsOutdated*/ - && ballIsClose && !doMatesIgnoreRemoteDecision(ballChaserDecision)); - return shouldUseLocalDecision; -} - -/** - * \brief Check if a team mate with lower number made a decision that differs from the team decision. - * - * This is done by checking if the mate assigned itself the ballchaser role even though the remote - * decision selected another robot. - * - * \param ballChaserDecision The local copy of the BallChaserDecision representation - * - * \returns True if a mate with different decision was found, false otherwise. -*/ -bool BallChaserDecisionProvider::doMatesIgnoreRemoteDecision(BallChaserDecision& ballChaserDecision) -{ - for (auto& mate : theTeammateData.teammates) - { - bool mateHasLowerNumberThanMe = mate.number < theRobotInfo.number; - bool mateWantsToChaseTheBall = (mate.behaviorData.role == BehaviorData::ballchaser || mate.behaviorData.role == BehaviorData::ballchaserKeeper); - bool mateIsNotOfficialBallchaser = ballChaserDecision.playerNumberToBall != mate.number; - - if (mateHasLowerNumberThanMe && mateWantsToChaseTheBall && mateIsNotOfficialBallchaser) - return true; - } - // there was no mate that satisfied the conditions - return false; -} - MAKE_MODULE(BallChaserDecisionProvider, behaviorControl) diff --git a/Src/Modules/BehaviorControl/TacticControl/BallChaserDecisionProvider.h b/Src/Modules/BehaviorControl/TacticControl/BallChaserDecisionProvider.h index 2fb63ec5..f0654273 100644 --- a/Src/Modules/BehaviorControl/TacticControl/BallChaserDecisionProvider.h +++ b/Src/Modules/BehaviorControl/TacticControl/BallChaserDecisionProvider.h @@ -14,6 +14,7 @@ #include "Representations/Configuration/FieldDimensions.h" #include "Representations/MotionControl/WalkingEngineParams.h" #include "Representations/Infrastructure/GameInfo.h" +#include "Representations/Infrastructure/TeamInfo.h" #include "Representations/BehaviorControl/GameSymbols.h" #include "Representations/Infrastructure/RobotInfo.h" #include "Representations/Infrastructure/TeammateData.h" @@ -23,6 +24,7 @@ #include "Representations/Modeling/RobotPose.h" #include "Representations/Sensing/FallDownState.h" #include "Representations/BehaviorControl/BallChaserDecision.h" +#include "Representations/BehaviorControl/TacticSymbols.h" STREAMABLE(TimeToBallParams, ENUM(TimeToBallCalculationType, @@ -31,7 +33,6 @@ STREAMABLE(TimeToBallParams, path, distanceAndPosition ), - (bool)(true) useDistanceBasedDecision, (bool)(true) usePredictedBallPosition, (TimeToBallCalculationType)(distance) type, (float)(5.f) isBallMineHysteresisFactor, @@ -52,7 +53,8 @@ STREAMABLE(TimeToBallParams, (float)(4000.f) distancePenaltyBallBehindRobotFullDistance, (float)(1.f) targetDistanceRobotRotFactor, (float)(3.f) penaltyBallBehindFactorOwnGroundline, - (float)(1.f) penaltyBallBehindFactorOpponentGroundline + (float)(1.f) penaltyBallBehindFactorOpponentGroundline, + (float)(1000.f) keeperPenaltyDuringOwnGoalKick ); MODULE(BallChaserDecisionProvider, @@ -68,15 +70,16 @@ MODULE(BallChaserDecisionProvider, REQUIRES(RobotPoseAfterPreview), REQUIRES(FallDownState), REQUIRES(FrameInfo), + REQUIRES(OwnTeamInfo), USES(RoleSymbols), + REQUIRES(TacticSymbols), PROVIDES(BallChaserDecision), LOADS_PARAMETERS(, (TimeToBallParams) timeToBallParams, (bool)(false) useLocalBallModelForDecision, (bool)(true) useLocalBallModelWhenNear, - (float)(700.f) useLocalBallModelWhenDifferenceGreater, - (float)(2000.f) useLocalBallModelBelowDistance, - (float)(100.f) maxSpeedToBeBallChaserKeeper //[mm/s] + (float)(1000.f) maxBallDistanceForLocalDecision, + (float)(750.f) maxBallDistanceForTeammateToBeNear ) ); @@ -94,6 +97,8 @@ class BallChaserDecisionProvider : public BallChaserDecisionProviderBase /** Main update funtion, called every frame. */ void update(BallChaserDecision& ballChaserDecision); + void decideLocal(BallChaserDecision& ballChaserDecision); + /** Update member variable isBallBehindPosition[]. */ void updateBallBehindMe(); @@ -115,10 +120,10 @@ class BallChaserDecisionProvider : public BallChaserDecisionProviderBase int calcBallChaserNumber(BallChaserDecision& ballChaserDecision, bool useLocalBallModelForDecision); /** Calculates the distance to the ball for all teammates. */ - float getDistanceToBallForMate(Teammate mate, bool useLocalBallModelForDecision, Vector2f localBallPosition); + float getDistanceToBallForMate(const TeammateReceived& mate, bool useLocalBallModelForDecision, Vector2f localBallPosition); /** Calculates distance between a player to a position (here the ball position). */ - float calcDistanceToBall(const Pose2f& fromPose, const Pose2f& targetOnField, int playerNumber, bool wasBallChaser, bool upright, bool notSeen); + float calcDistanceToBall(const Pose2f& fromPose, const Pose2f& targetOnField, int playerNumber, bool wasBallChaser, bool upright, bool notSeen, unsigned timeSinceLastUpdate); /** Calculates a distance penalty based on the rotation difference between two poses. */ float getRotationPenalty(const Pose2f& fromPose, const Pose2f& targetOnField); @@ -129,11 +134,6 @@ class BallChaserDecisionProvider : public BallChaserDecisionProviderBase /** Calculates a distance penalty based on the current situation of the robot. */ float getStatusPenalty(const Pose2f& fromPose, const Pose2f& targetOnField, int playerNumber, bool wasBallChaser, bool upright, bool notSeen); - /** Checks if the teams decision should be ignored in favor of the robot's local decision. */ - bool useLocalDecision(BallChaserDecision& ballChaserDecision, Vector2f remoteDecisionBallPos); - /** Checks if any other team member wants to overwrite ball chaser decision. */ - bool doMatesIgnoreRemoteDecision(BallChaserDecision& ballChaserDecision); - // member variables // ---------------- bool isBallBehindPosition[MAX_NUM_PLAYERS + 1]; diff --git a/Src/Modules/BehaviorControl/TacticControl/BallSearchProvider.cpp b/Src/Modules/BehaviorControl/TacticControl/BallSearchProvider.cpp index c1dbeac3..e13825a5 100644 --- a/Src/Modules/BehaviorControl/TacticControl/BallSearchProvider.cpp +++ b/Src/Modules/BehaviorControl/TacticControl/BallSearchProvider.cpp @@ -27,9 +27,9 @@ void BallSearchProvider::update(BallSearch& ballsearch) lastBallPositionField = Vector2f::Zero(); for (auto& mate : theTeammateData.teammates) - if (mate.behaviorData.timeSinceBallWasSeen < timeSinceBallLastSeen) + if (theFrameInfo.getTimeSince(mate.ballModel.timeWhenLastSeen) < timeSinceBallLastSeen) { - timeSinceBallLastSeen = mate.behaviorData.timeSinceBallWasSeen; + timeSinceBallLastSeen = theFrameInfo.getTimeSince(mate.ballModel.timeWhenLastSeen); lastBallPositionField = mate.behaviorData.ballPositionField.cast(); } @@ -66,7 +66,7 @@ void BallSearchProvider::update(BallSearch& ballsearch) || lastBallPositionField.x() >= theFieldDimensions.xPosOwnGroundline + centerSearchStart * 2 * theFieldDimensions.xPosOpponentGroundline) { for (auto& role : theRoleSelection.selectedRoles) - if (role == BehaviorData::ballchaser || role == BehaviorData::receiver) + if (role == BehaviorData::leftWing || role == BehaviorData::rightWing || role == BehaviorData::receiver) { ballsearch.rolesInBallSearch.push_back(BehaviorData::RoleAssignment(role)); offenseInBallSearch++; @@ -181,26 +181,8 @@ void BallSearchProvider::fillBallSearchPositions(BallSearch& ballsearch) // no one break; } + // offensive positions - if (timeSinceBallSearchStarted < 30000) - { - for (size_t i = 0; i < ballsearch.rolesInBallSearch.size(); i++) - { - const BehaviorData::RoleAssignment& role = ballsearch.rolesInBallSearch[i]; - if (role == BehaviorData::ballchaser) - { - ballsearch.ballSearchPositions[i].poses.push_back(Pose2f(45_deg, - std::max(theFieldDimensions.xPosOwnGroundline, std::min(lastBallPositionField.x() + 500.f, theFieldDimensions.xPosOpponentGroundline)), - std::max(theFieldDimensions.yPosRightSideline, std::min(lastBallPositionField.y() + 1000.f, theFieldDimensions.yPosLeftSideline)))); - ballsearch.ballSearchPositions[i].poses.push_back(Pose2f(-45_deg, - std::max(theFieldDimensions.xPosOwnGroundline, std::min(lastBallPositionField.x() + 500.f, theFieldDimensions.xPosOpponentGroundline)), - std::min(theFieldDimensions.yPosLeftSideline, std::max(lastBallPositionField.y() - 1000.f, theFieldDimensions.yPosRightSideline)))); - ballsearch.ballSearchPositions[i].poses.push_back(Pose2f(175_deg, - std::min(theFieldDimensions.xPosOpponentGroundline, std::max(lastBallPositionField.x() - 1000.f, theFieldDimensions.xPosOwnGroundline)), - std::max(theFieldDimensions.yPosRightSideline, std::min(lastBallPositionField.y(), theFieldDimensions.yPosLeftSideline)))); - } - } - } switch (offenseInBallSearch) { case 1: @@ -208,7 +190,7 @@ void BallSearchProvider::fillBallSearchPositions(BallSearch& ballsearch) for (size_t i = 0; i < ballsearch.rolesInBallSearch.size(); i++) { const BehaviorData::RoleAssignment& role = ballsearch.rolesInBallSearch[i]; - if (role == BehaviorData::receiver || role == BehaviorData::ballchaser) + if (role == BehaviorData::receiver) { ballsearch.ballSearchPositions[i].poses.push_back( Pose2f(45_deg, theFieldDimensions.xPosOwnGroundline + (centerSearchStart + 1.f) * theFieldDimensions.xPosOpponentGroundline, theFieldDimensions.yPosLeftSideline * 0.75f)); @@ -226,6 +208,8 @@ void BallSearchProvider::fillBallSearchPositions(BallSearch& ballsearch) break; } case 2: + [[fallthrough]]; // TODO: add 3 positions including rightWing + case 3: { for (size_t i = 0; i < ballsearch.rolesInBallSearch.size(); i++) { @@ -244,7 +228,7 @@ void BallSearchProvider::fillBallSearchPositions(BallSearch& ballsearch) Pose2f(-135_deg, theFieldDimensions.xPosOwnGroundline + centerSearchStart * 2.f * theFieldDimensions.xPosOpponentGroundline, theFieldDimensions.yPosLeftSideline * 0.15f)); } } - else if (role == BehaviorData::ballchaser) + else if (role == BehaviorData::leftWing || role == BehaviorData::rightWing) { ballsearch.ballSearchPositions[i].poses.push_back( Pose2f(45_deg, theFieldDimensions.xPosOwnGroundline + (centerSearchStart + 1.f) * theFieldDimensions.xPosOpponentGroundline, theFieldDimensions.yPosRightSideline * 0.8f)); @@ -265,6 +249,28 @@ void BallSearchProvider::fillBallSearchPositions(BallSearch& ballsearch) // no one break; } + + // special case: ballchaser goes to last ball position + if (timeSinceBallSearchStarted < 30000) + { + for (size_t i = 0; i < ballsearch.rolesInBallSearch.size(); i++) + { + const BehaviorData::RoleAssignment& role = ballsearch.rolesInBallSearch[i]; + if (role == theRoleSymbols.role && theRobotInfo.number == theBallChaserDecision.playerNumberToBall) + { + ballsearch.ballSearchPositions[i].poses.clear(); + ballsearch.ballSearchPositions[i].poses.push_back(Pose2f(45_deg, + std::max(theFieldDimensions.xPosOwnGroundline, std::min(lastBallPositionField.x() + 500.f, theFieldDimensions.xPosOpponentGroundline)), + std::max(theFieldDimensions.yPosRightSideline, std::min(lastBallPositionField.y() + 1000.f, theFieldDimensions.yPosLeftSideline)))); + ballsearch.ballSearchPositions[i].poses.push_back(Pose2f(-45_deg, + std::max(theFieldDimensions.xPosOwnGroundline, std::min(lastBallPositionField.x() + 500.f, theFieldDimensions.xPosOpponentGroundline)), + std::min(theFieldDimensions.yPosLeftSideline, std::max(lastBallPositionField.y() - 1000.f, theFieldDimensions.yPosRightSideline)))); + ballsearch.ballSearchPositions[i].poses.push_back(Pose2f(175_deg, + std::min(theFieldDimensions.xPosOpponentGroundline, std::max(lastBallPositionField.x() - 1000.f, theFieldDimensions.xPosOwnGroundline)), + std::max(theFieldDimensions.yPosRightSideline, std::min(lastBallPositionField.y(), theFieldDimensions.yPosLeftSideline)))); + } + } + } } -MAKE_MODULE(BallSearchProvider, behaviorControl) \ No newline at end of file +MAKE_MODULE(BallSearchProvider, behaviorControl) diff --git a/Src/Modules/BehaviorControl/TacticControl/BallSearchProvider.h b/Src/Modules/BehaviorControl/TacticControl/BallSearchProvider.h index 0175d0cd..6d73e710 100644 --- a/Src/Modules/BehaviorControl/TacticControl/BallSearchProvider.h +++ b/Src/Modules/BehaviorControl/TacticControl/BallSearchProvider.h @@ -12,11 +12,14 @@ #include "Representations/BehaviorControl/BallSymbols.h" #include "Representations/BehaviorControl/GameSymbols.h" #include "Representations/BehaviorControl/RoleSelection.h" +#include "Representations/BehaviorControl/BallChaserDecision.h" +#include "Representations/BehaviorControl/RoleSymbols.h" #include "Representations/Configuration/FieldDimensions.h" #include "Representations/Modeling/RobotPose.h" #include "Representations/Infrastructure/TeammateData.h" #include "Representations/Infrastructure/FrameInfo.h" #include "Representations/Infrastructure/GameInfo.h" +#include "Representations/Infrastructure/RobotInfo.h" #include "Representations/Modeling/RobotMap.h" MODULE(BallSearchProvider, @@ -29,6 +32,9 @@ MODULE(BallSearchProvider, REQUIRES(RoleSelection), REQUIRES(TeammateData), REQUIRES(RobotMap), + REQUIRES(RoleSymbols), + REQUIRES(BallChaserDecision), + REQUIRES(RobotInfo), PROVIDES(BallSearch), LOADS_PARAMETERS(, (int)(10000) timeUntilWholeFieldSearchAfterLost, diff --git a/Src/Modules/BehaviorControl/TacticControl/BallSymbolsProvider.cpp b/Src/Modules/BehaviorControl/TacticControl/BallSymbolsProvider.cpp index 426ddf6c..44e1758a 100644 --- a/Src/Modules/BehaviorControl/TacticControl/BallSymbolsProvider.cpp +++ b/Src/Modules/BehaviorControl/TacticControl/BallSymbolsProvider.cpp @@ -7,12 +7,14 @@ */ #include "BallSymbolsProvider.h" -#include "Tools/Debugging/Modify.h" +#include "Tools/Debugging/Annotation.h" #include "Tools/Debugging/DebugDrawings.h" +#include "Tools/Debugging/Modify.h" #include "Tools/Math/Geometry.h" #include "Tools/Math/Transformation.h" #include "Tools/Modeling/BallPhysics.h" -#include "Tools/Debugging/Annotation.h" +#include +#include void BallSymbolsProvider::update(BallSymbols& ballSymbols) { @@ -41,19 +43,44 @@ void BallSymbolsProvider::update(BallSymbols& ballSymbols) chooseBallModel(localBallSymbols, currentlyUsedBallModel); // ball to field coordinates - localBallSymbols.ballPositionField = Transformation::robotToField(theRobotPoseAfterPreview, currentlyUsedBallModel.estimate.position); - localBallSymbols.ballPositionRelative = currentlyUsedBallModel.estimate.position; - localBallSymbols.ballVelocityRelative = currentlyUsedBallModel.estimate.velocity; + if (theGameInfo.state == STATE_READY) + { + // some roles use the ball for positioning during ready + if (theGameInfo.gamePhase == GAME_PHASE_PENALTYSHOOT || theGameInfo.setPlay == SET_PLAY_PENALTY_KICK) + { + const float xPosPenaltyMark = theGameInfo.kickingTeam == theOwnTeamInfo.teamNumber ? theFieldDimensions.xPosOpponentPenaltyMark : theFieldDimensions.xPosOwnPenaltyMark; + localBallSymbols.ballPositionField << xPosPenaltyMark, theFieldDimensions.yPosCenterGoal; + } + else + localBallSymbols.ballPositionField.setZero(); + + localBallSymbols.ballPositionFieldPredicted = localBallSymbols.ballPositionField; + localBallSymbols.ballPositionRelative = Transformation::fieldToRobot(theRobotPoseAfterPreview, localBallSymbols.ballPositionField); + localBallSymbols.ballPositionRelativePredicted = localBallSymbols.ballPositionRelative; + localBallSymbols.ballPositionRelativeWOPreview = localBallSymbols.ballPositionField; + localBallSymbols.ballVelocityRelative.setZero(); + localBallSymbols.ballVelocityRelativeWOPreview.setZero(); + } + else + { + localBallSymbols.ballPositionField = Transformation::robotToField(theRobotPoseAfterPreview, currentlyUsedBallModel.estimate.position); + localBallSymbols.ballPositionRelative = currentlyUsedBallModel.estimate.position; + localBallSymbols.ballVelocityRelative = currentlyUsedBallModel.estimate.velocity; - // predicted ball position - setPredictedBallPosition(localBallSymbols); + // predicted ball position + setPredictedBallPosition(localBallSymbols); + } ballWasSeenRecently = localBallSymbols.timeSinceLastSeen < timeForBallWasSeen; ballIsRolling = currentlyUsedBallModel.estimate.velocity.norm() > 50.f; // Ball is faster than 50 mm/s - ballIsMovingTowardsTheRobot = ballIsRolling && sgn(currentlyUsedBallModel.estimate.velocity.x()) != sgn(currentlyUsedBallModel.estimate.position.x()); + Vector2f vecBalltoRobot = theRobotPoseAfterPreview.translation - localBallSymbols.ballPositionField; + Angle difBallRobot = 180_deg; + if (localBallSymbols.ballVelocityRelative.norm() > 0 && vecBalltoRobot.norm() > 0) + difBallRobot = std::abs(localBallSymbols.ballVelocityRelative.angleTo(vecBalltoRobot)); + bool ballIsMovingTowardsTheRobot = ballIsRolling && difBallRobot < 70_deg; // for diving / ball stopping - if (ballWasSeenRecently && ballIsMovingTowardsTheRobot) + if (ballWasSeenRecently && ballIsMovingTowardsTheRobot && ballSymbols.ballPositionField.x() > theFieldDimensions.xPosOwnGroundline) // getYPosWhenBallReachesOwnGroundLine calc doesn't make sense otherwise { localBallSymbols.yPosWhenBallReachesOwnYAxis = getYPosWhenBallReachesOwnYAxis(); localBallSymbols.yPosWhenBallReachesGroundLine = getYPosWhenBallReachesOwnGroundLine(localBallSymbols); @@ -72,7 +99,7 @@ void BallSymbolsProvider::update(BallSymbols& ballSymbols) const int bufferZoneGoal = (localBallSymbols.ballInOwnGoalArea ? 300 : 200); localBallSymbols.ballInOwnGoalArea = (std::abs(localBallSymbols.ballPositionField.y()) < theFieldDimensions.yPosLeftGoalArea + bufferZoneGoal) && (localBallSymbols.ballPositionField.x() < theFieldDimensions.xPosOwnGoalArea + bufferZoneGoal); - const int bufferZonePenalty = (localBallSymbols.ballInOwnGoalArea ? 300 : 200); + const int bufferZonePenalty = (localBallSymbols.ballInOwnGoalArea ? 300 : 100); localBallSymbols.ballInOwnPenaltyArea = (std::abs(localBallSymbols.ballPositionField.y()) < theFieldDimensions.yPosLeftPenaltyArea + bufferZonePenalty) && (localBallSymbols.ballPositionField.x() < theFieldDimensions.xPosOwnPenaltyArea + bufferZonePenalty); @@ -133,26 +160,32 @@ void BallSymbolsProvider::chooseBallModel(BallSymbols& ballSymbols, BallModel& c void BallSymbolsProvider::setPredictedBallPosition(BallSymbols& ballSymbols) { - // Avoid predicted ball ? Per default yes, but not if kicking.. - if (theMotionInfo.walkRequest.stepRequest != WalkRequest::StepRequest::none || theMotionInfo.motion == MotionRequest::kick) // kick will be executed by motion + if (KickUtils::isBallKicked(theMotionInfo)) // kick will be executed by motion { timeWhenLastKickTriggered = theFrameInfo.time; - Vector2f vectorBallToKickTarget = theMotionInfo.kickRequest.kickTarget - ballSymbols.ballPositionField; - ballSymbols.ballPositionFieldPredicted = ballSymbols.ballPositionField + vectorBallToKickTarget.normalize(500.f); // small step should be good enough TODO: only one time? Use Dominiks Prediction - ballSymbols.ballPositionRelativePredicted = Transformation::fieldToRobot(theRobotPoseAfterPreview, ballSymbols.ballPositionFieldPredicted); ballSymbols.avoidBall = false; + return; } - else if (theFrameInfo.getTimeSince(timeWhenLastKickTriggered) < ballPredictionTimeHorizon) // keep predicted position for some time after kick + + if (theFrameInfo.getTimeSince(timeWhenLastKickTriggered) < ballPredictionTimeHorizon) // keep predicted position for some time after kick { ballSymbols.ballPositionRelativePredicted = Transformation::fieldToRobot(theRobotPoseAfterPreview, ballSymbols.ballPositionFieldPredicted); ballSymbols.avoidBall = false; + return; + } + + const float MIN_VELOCITY_X = 5.f; + const float MIN_VELOCITY_Y = 10.f; + if (std::abs(ballSymbols.ballVelocityRelative.x()) < MIN_VELOCITY_X && std::abs(ballSymbols.ballVelocityRelative.y()) < MIN_VELOCITY_Y) + { + ballSymbols.ballPositionRelativePredicted = ballSymbols.ballPositionRelative; } else { ballSymbols.ballPositionRelativePredicted = BallPhysics::getEndPosition(ballSymbols.ballPositionRelative, ballSymbols.ballVelocityRelative, theBallModelAfterPreview.friction); - ballSymbols.ballPositionFieldPredicted = Transformation::robotToField(theRobotPoseAfterPreview, ballSymbols.ballPositionRelativePredicted); - ballSymbols.avoidBall = true; } + ballSymbols.ballPositionFieldPredicted = Transformation::robotToField(theRobotPoseAfterPreview, ballSymbols.ballPositionRelativePredicted); + ballSymbols.avoidBall = true; } bool BallSymbolsProvider::calculateBallCouldHaveBeenSeen(BallSymbols& ballSymbols) @@ -234,27 +267,16 @@ bool BallSymbolsProvider::calculateBallBlockable(BallSymbols& ballSymbols) bool BallSymbolsProvider::calculateBallProbablyCloseButNotSeen(BallSymbols& ballSymbols) { - // compare current and last fall down state to detect successful stand ups - fallDownStateLastFrame = fallDownStateThisFrame; - fallDownStateThisFrame = theFallDownState.state; - if (fallDownStateLastFrame == FallDownState::standingUp && fallDownStateThisFrame == FallDownState::upright) - timeOfLastStandUp = theFrameInfo.time; - bool lastBallWasClose = currentlyUsedBallModel.lastPerception.norm() < 1000; - //bool lastBallWasNotBehindTheRobot = currentlyUsedBallModel.lastPerception.x() > 50; - int timeForBallWasSeenBonus = static_cast(toDegrees(std::abs(theBallModelAfterPreview.estimate.position.angle())) * 10); - bool ballWasNotSeenRecently = ballSymbols.timeSinceLastSeen > (timeForBallWasSeen + timeForBallWasSeenBonus); - bool ballLostStartedRecently = std::min(ballSymbols.timeSinceLastSeen, theFrameInfo.getTimeSince(timeOfLastStandUp)) < 10000; - bool ballWasNotGoneForLong = ballSymbols.timeSinceLastSeen < 16000; - bool isRobotUprightAndNotKicking = fallDownStateThisFrame == FallDownState::upright && theMotionSelection.targetMotion != MotionRequest::kick - && theMotionSelection.walkRequest.stepRequest == WalkRequest::none; - - // if ballLost is true the ballchaser rotates find a recently lost ball - return lastBallWasClose && - //lastBallWasNotBehindTheRobot && // if it was seen next or behind me, handle differently; robot should turn or walk to model target - ballWasNotSeenRecently && ballLostStartedRecently && // rotate for 10 seconds after losing the ball or getting up after a fall - ballWasNotGoneForLong && // if ball has been lost for too long its probably gone and rotating is unecessary - isRobotUprightAndNotKicking; + + int timeForBallWasSeenBonus = (int)toDegrees(std::abs(theBallModelAfterPreview.estimate.position.angle())) * 10; + int totalTimeForBallWasSeen = (int)timeForBallWasSeen + timeForBallWasSeenBonus; + + bool ballSeenRecently = ballSymbols.timeSinceLastSeen < (totalTimeForBallWasSeen + 20000); + bool ballSeenVeryRecently = ballSymbols.timeSinceLastSeen < (totalTimeForBallWasSeen + 2000); + ASSERT(!(!ballSeenRecently && ballSeenVeryRecently)); + + return lastBallWasClose && ballSeenRecently && !ballSeenVeryRecently; } float BallSymbolsProvider::getYPosWhenBallReachesOwnYAxis() diff --git a/Src/Modules/BehaviorControl/TacticControl/BallSymbolsProvider.h b/Src/Modules/BehaviorControl/TacticControl/BallSymbolsProvider.h index e6203e6d..27a4d0bf 100644 --- a/Src/Modules/BehaviorControl/TacticControl/BallSymbolsProvider.h +++ b/Src/Modules/BehaviorControl/TacticControl/BallSymbolsProvider.h @@ -31,6 +31,7 @@ #include "Representations/MotionControl/MotionSelection.h" #include "Representations/Perception/CameraMatrix.h" #include "Representations/Sensing/FallDownState.h" +#include "Representations/Sensing/RobotModel.h" MODULE(BallSymbolsProvider, REQUIRES(BehaviorConfiguration), @@ -48,6 +49,7 @@ MODULE(BallSymbolsProvider, REQUIRES(RemoteBallModel), REQUIRES(OwnTeamInfo), REQUIRES(RobotMap), + REQUIRES(RobotModel), REQUIRES(RobotPose), REQUIRES(RobotPoseAfterPreview), REQUIRES(TeammateData), @@ -85,15 +87,10 @@ class BallSymbolsProvider : public BallSymbolsProviderBase bool ballWasSeenRecently = false; bool ballIsRolling = false; - bool ballIsMovingTowardsTheRobot = false; unsigned timeWhenBallHitTriggered = 0; unsigned timeWhenBallBlockableTriggered = 0; unsigned timeWhenLastKickTriggered = 0; - unsigned timeOfLastStandUp = 0; - - FallDownState::State fallDownStateLastFrame = FallDownState::undefined; - FallDownState::State fallDownStateThisFrame = FallDownState::undefined; void chooseBallModel(BallSymbols& ballSymbols, BallModel& currentlyUsedBallModel); void setPredictedBallPosition(BallSymbols& ballSymbols); diff --git a/Src/Modules/BehaviorControl/TacticControl/GameSymbolsProvider.cpp b/Src/Modules/BehaviorControl/TacticControl/GameSymbolsProvider.cpp index db857a52..404f8c0c 100644 --- a/Src/Modules/BehaviorControl/TacticControl/GameSymbolsProvider.cpp +++ b/Src/Modules/BehaviorControl/TacticControl/GameSymbolsProvider.cpp @@ -29,7 +29,7 @@ void GameSymbolsProvider::update(GameSymbols& gameSymbols) ballStartCounter = 0.f; ballAtStart = Vector2f(0, 0); timeWhenChangedToPlayingState = theFrameInfo.time; - if (theFrameInfo.getTimeSince(theGameInfo.timeLastPackageReceived) < 5000 && (lastSecsRemaining - 10 > theGameInfo.secsRemaining)) + if (theGameInfo.controllerConnected && (lastSecsRemaining - 10 > theGameInfo.secsRemaining)) timeWhenChangedToPlayingState = theFrameInfo.time - (600 - theGameInfo.secsRemaining) * 1000; else gameSymbols.kickoffInProgress = true; @@ -51,7 +51,7 @@ void GameSymbolsProvider::update(GameSymbols& gameSymbols) if (gameSymbols.kickoffInProgress) { const Teammate* teammate = theTeammateData.getNewestEventMessage(TeamCommEvents::SendReason::kickOffFinished); - if (teammate && theFrameInfo.getTimeSince(teammate->timeWhenSent) < 1000) + if (teammate && theFrameInfo.getTimeSince(teammate->sendTimestamp) < 1000) gameSymbols.kickoffInProgress = false; if (gameSymbols.timeSincePlayingState > 2000 && theBallSymbols.ballWasSeen && theBallSymbols.ballWasSeen && ballPositionField.norm() > 400.f) { @@ -98,7 +98,7 @@ void GameSymbolsProvider::update(GameSymbols& gameSymbols) gameSymbols.timeSinceLastPenalty = theFrameInfo.getTimeSince(lastPenalizedTime); - gameSymbols.allowedInPenaltyArea = calcAllowedInOwnPenaltyArea(); + gameSymbols.allowedInGoalArea = calcAllowedInOwnGoalArea(); // update set play state and handle changes bool setPlayChanged = lastSetPlay != theGameInfo.setPlay; // set play changed since last frame @@ -131,30 +131,31 @@ void GameSymbolsProvider::update(GameSymbols& gameSymbols) } /** -* \brief Calculates whether or not, this robot is allowed in the own teams penalty area. +* \brief Calculates whether or not, this robot is allowed in the own teams goal area. * -* The robot is allowed in its penalty area, if all of the following conditions are true: +* The robot is allowed in its goal area, if all of the following conditions are true: * - It is either the keeper, replacementKeeper, defenderSingle or ballchaser. -* - Less than three other robots are currently positioned in the penalty area or the robot is -* already in the penalty area. +* - Less than three other robots are currently positioned in the goal area or the robot is +* already in the goal area. * -* \return true, if the robot is allowed in the penalty area, false otherwise. +* \return true, if the robot is allowed in the goal area, false otherwise. */ -bool GameSymbolsProvider::calcAllowedInOwnPenaltyArea() +bool GameSymbolsProvider::calcAllowedInOwnGoalArea() { - std::unordered_set privilegedRoles = {BehaviorData::keeper, BehaviorData::replacementKeeper, BehaviorData::defenderSingle, BehaviorData::ballchaser}; - Vector2f penAreaBottomLeft(theFieldDimensions.xPosOwnGroundline, theFieldDimensions.yPosRightPenaltyArea); - Vector2f penAreaTopRight(theFieldDimensions.xPosOwnPenaltyArea, theFieldDimensions.yPosLeftPenaltyArea); - int numOfTeammatesInPenaltyArea = 0; + std::unordered_set privilegedRoles = { + BehaviorData::keeper, BehaviorData::replacementKeeper, BehaviorData::defenderSingle, BehaviorData::defenderLeft, BehaviorData::defenderRight}; + Vector2f goalAreaBottomLeft(theFieldDimensions.xPosOwnGroundline, theFieldDimensions.yPosRightGoalArea); + Vector2f goalAreaTopRight(theFieldDimensions.xPosOwnGoalArea, theFieldDimensions.yPosLeftGoalArea); + int numOfTeammatesInGoalArea = 0; for (auto const& teammate : theTeammateData.teammates) { - if (Geometry::isPointInsideRectangle(penAreaBottomLeft, penAreaTopRight, teammate.pose.translation)) - numOfTeammatesInPenaltyArea++; + if (Geometry::isPointInsideRectangle(goalAreaBottomLeft, goalAreaTopRight, teammate.robotPose.translation)) + numOfTeammatesInGoalArea++; } bool hasPrivilegedRole = privilegedRoles.find(theRoleSymbols.role) != privilegedRoles.end(); - bool penAreaIsOccupied = numOfTeammatesInPenaltyArea >= 3; - bool robotIsInPenArea = Geometry::isPointInsideRectangle(penAreaBottomLeft, penAreaTopRight, theRobotPoseAfterPreview.translation); - return hasPrivilegedRole && (!penAreaIsOccupied || robotIsInPenArea); + bool goalAreaIsOccupied = numOfTeammatesInGoalArea >= 3; + bool robotIsInGoalArea = Geometry::isPointInsideRectangle(goalAreaBottomLeft, goalAreaTopRight, theRobotPoseAfterPreview.translation); + return hasPrivilegedRole && (!goalAreaIsOccupied || robotIsInGoalArea); } MAKE_MODULE(GameSymbolsProvider, behaviorControl) diff --git a/Src/Modules/BehaviorControl/TacticControl/GameSymbolsProvider.h b/Src/Modules/BehaviorControl/TacticControl/GameSymbolsProvider.h index 809f0a78..dc8a1a38 100644 --- a/Src/Modules/BehaviorControl/TacticControl/GameSymbolsProvider.h +++ b/Src/Modules/BehaviorControl/TacticControl/GameSymbolsProvider.h @@ -63,7 +63,7 @@ class GameSymbolsProvider : public GameSymbolsProviderBase int lastKickingTeam = -1; void update(GameSymbols& gameSymbols); - bool calcAllowedInOwnPenaltyArea(); + bool calcAllowedInOwnGoalArea(); private: int counter = 0; diff --git a/Src/Modules/BehaviorControl/TacticControl/HeatMapProvider/HeatMapProvider.cpp b/Src/Modules/BehaviorControl/TacticControl/HeatMapProvider/HeatMapProvider.cpp new file mode 100644 index 00000000..46594bf9 --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/HeatMapProvider/HeatMapProvider.cpp @@ -0,0 +1,81 @@ +#include "HeatMapProvider.h" +#include "Tools/Math/Transformation.h" +#include +#include +#include +#include + +HeatMapProvider::HeatMapProvider() = default; + +void draw(std::vector positionVector, std::vector heatVector); + +void HeatMapProvider::execute(tf::Subflow& subflow) +{ + const auto [teammateRobots, opponentRobots] = HeatMapUtils::getTeammateAndOtherRobots(theRobotMap); + + subflow + .for_each_index(0, + HeatMap::CELL_COUNT, + 1, + [&, teammateRobots = teammateRobots, opponentRobots = opponentRobots](const int index) + { + const Vector2f fieldPosition = HeatMap::indexToField(index, theFieldDimensions); + + if (firstUpdate) + { + localHeatMapCollection.sidesHeatMap.setHeat(std::pow(HeatMapUtils::getSidesHeat(fieldPosition, theFieldDimensions), 2.f), index, theFieldDimensions); + localHeatMapCollection.goalsHeatMap.setHeat(HeatMapUtils::getGoalsHeat(fieldPosition, theFieldDimensions), index, theFieldDimensions); + } + + const auto [teammatesKickHeat, teammatesGoalKickHeat] = + HeatMapUtils::getRobotHeatForPosition(fieldPosition, teammateRobots, FieldUtils::getOpponentGoalCenter(theFieldDimensions), theFieldDimensions); + const auto [opponentsKickHeat, opponentsGoalKickHeat] = + HeatMapUtils::getRobotHeatForPosition(fieldPosition, opponentRobots, FieldUtils::getOwnGoalCenter(theFieldDimensions), theFieldDimensions); + + + if (kickHeatTakeNewPercent > 0.99f) + { + teammatesKickHeatMap.setHeat(teammatesKickHeat, index, theFieldDimensions); + localHeatMapCollection.opponentKickHeatMap.setHeat(opponentsKickHeat, index, theFieldDimensions); + } + else + { + teammatesKickHeatMap.updateHeat(kickHeatTakeNewPercent, teammatesKickHeat, index, theFieldDimensions); + localHeatMapCollection.opponentKickHeatMap.updateHeat(kickHeatTakeNewPercent, opponentsKickHeat, index, theFieldDimensions); + } + + if (goalKickHeatTakeNewPercent > 0.99f) + { + teammatesGoalKickHeatMap.setHeat(teammatesGoalKickHeat, index, theFieldDimensions); + localHeatMapCollection.opponentGoalKickHeatMap.setHeat(opponentsGoalKickHeat, index, theFieldDimensions); + } + else + { + teammatesGoalKickHeatMap.updateHeat(goalKickHeatTakeNewPercent, teammatesGoalKickHeat, index, theFieldDimensions); + localHeatMapCollection.opponentGoalKickHeatMap.updateHeat(goalKickHeatTakeNewPercent, opponentsGoalKickHeat, index, theFieldDimensions); + } + + // Apply instant heat + const std::vector selfPose = {Pose2f(theRobotPose.rotation, theBallSymbols.ballPositionField)}; // Use Ball Position since it's the robots position when he will kick the ball + const auto [selfKickHeat, selfGoalKickHeat] = HeatMapUtils::getRobotHeatForPosition(fieldPosition, selfPose, FieldUtils::getOpponentGoalCenter(theFieldDimensions), theFieldDimensions); + const float teamKickHeat = std::max(teammatesKickHeatMap.getHeat(index), selfKickHeat); + const float teamGoalKickHeat = std::max(teammatesGoalKickHeatMap.getHeat(index), selfGoalKickHeat); + localHeatMapCollection.teamKickHeatMap.setHeat(teamKickHeat, index, theFieldDimensions); + localHeatMapCollection.teamGoalKickHeatMap.setHeat(teamGoalKickHeat, index, theFieldDimensions); + }) + .name("UpdateHeat [HeatMapProvider]"); +} + +void HeatMapProvider::update(HeatMapCollection& heatMapCollection) +{ + heatMapCollection = localHeatMapCollection; + firstUpdate = false; + + DECLARE_DEBUG_DRAWING(DRAW_HEAT_MAP, "drawingOnField"); + COMPLEX_DRAWING(DRAW_HEAT_MAP) + { + HeatMapUtils::draw(heatMapCollection.opponentGoalKickHeatMap, true, theFieldDimensions); + } +} + +MAKE_MODULE(HeatMapProvider, modeling) diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/HeatMapProvider.h b/Src/Modules/BehaviorControl/TacticControl/HeatMapProvider/HeatMapProvider.h similarity index 85% rename from Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/HeatMapProvider.h rename to Src/Modules/BehaviorControl/TacticControl/HeatMapProvider/HeatMapProvider.h index b6348bd1..8438a8d9 100644 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/HeatMapProvider.h +++ b/Src/Modules/BehaviorControl/TacticControl/HeatMapProvider/HeatMapProvider.h @@ -22,6 +22,7 @@ #include "Representations/Modeling/HeatMapCollection.h" MODULE(HeatMapProvider, + REQUIRES(BallSymbols), REQUIRES(BallModel), REQUIRES(FieldDimensions), REQUIRES(FrameInfo), @@ -29,11 +30,11 @@ MODULE(HeatMapProvider, REQUIRES(RobotMap), REQUIRES(RobotPose), REQUIRES(TeammateData), - + HAS_PREEXECUTION, PROVIDES(HeatMapCollection), LOADS_PARAMETERS(, - (float)(0.005f) takeNewPercent, + (float)(0.005f) kickHeatTakeNewPercent, (float)(0.025f) goalKickHeatTakeNewPercent ) ); @@ -42,12 +43,12 @@ class HeatMapProvider : public HeatMapProviderBase { public: HeatMapProvider(); + void execute(tf::Subflow&); void update(HeatMapCollection& heatMapCollection); private: - void updateHeat(HeatMapCollection& heatMapCollection); - bool firstUpdate = true; - HeatMap sidesHeatMap; - HeatMap opponentsGoalHeatMap; + HeatMap teammatesKickHeatMap; + HeatMap teammatesGoalKickHeatMap; + HeatMapCollection localHeatMapCollection; }; diff --git a/Src/Modules/BehaviorControl/TacticControl/HeatMapProvider/HeatMapUtils.h b/Src/Modules/BehaviorControl/TacticControl/HeatMapProvider/HeatMapUtils.h new file mode 100644 index 00000000..fb1356ad --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/HeatMapProvider/HeatMapUtils.h @@ -0,0 +1,158 @@ +#pragma once + +#include "Representations/Modeling/HeatMapCollection.h" +#include "Representations/Configuration/FieldDimensions.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Utils/MathUtils.h" +#include "Representations/Modeling/RobotMap.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Utils/PositionUtils.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Utils/FieldUtils.h" + +class HeatMapUtils +{ +public: + static float getSidesHeat(const Vector2f& point, const FieldDimensions& theFieldDimensions) + { + const float x = MathUtils::clamp_f(point.x(), theFieldDimensions.xPosOwnGroundline, theFieldDimensions.xPosOpponentGroundline); + const float y = MathUtils::clamp_f(point.y(), theFieldDimensions.yPosRightSideline, theFieldDimensions.yPosLeftSideline); + + const float oppXDistance = theFieldDimensions.xPosOpponentGroundline - x; + const float ownXDistance = x - theFieldDimensions.xPosOwnGroundline; + const float leftYDistance = theFieldDimensions.yPosLeftSideline - y; + const float rightYDistance = y - theFieldDimensions.yPosRightSideline; + const float minDistance = std::min(oppXDistance, std::min(ownXDistance, std::min(leftYDistance, rightYDistance))); + + return 1 - minDistance / theFieldDimensions.xPosOpponentGroundline; + } + + static float getGoalsHeat(const Vector2f& point, const FieldDimensions& theFieldDimensions) + { + const float EXPONENT = 0.5f; + const float MAX_DISTANCE = theFieldDimensions.xPosOpponentFieldBorder * 0.9f; + + const bool onOpponentsSide = point.x() > 0; + + const Vector2f goalCenter = onOpponentsSide ? FieldUtils::getOpponentGoalCenter(theFieldDimensions) : FieldUtils::getOwnGoalCenter(theFieldDimensions); + const float goalDistance = std::min(Geometry::distance(point, goalCenter), MAX_DISTANCE); + + const float fraction = std::pow(goalDistance / MAX_DISTANCE, EXPONENT); + const float heat = onOpponentsSide ? (1 - fraction) : fraction; + const float fieldSideAdjustedHeat = (onOpponentsSide ? 0.5f : 0.f) + 0.5f * heat; + + return fieldSideAdjustedHeat; + } + + static std::array, 2> getTeammateAndOtherRobots(const RobotMap& theRobotMap) + { + std::array, 2> ret; + + for (const auto& robot : theRobotMap.robots) + { + std::vector& robots = (robot.robotType == RobotEstimate::teammateRobot ? ret[0] : ret[1]); + robots.emplace_back(robot.pose); + } + + return ret; + } + + static std::tuple getRobotHeatForPosition(const Vector2f& cellPosition, const std::vector& robotPoses, const Vector2f& goalCenter, const FieldDimensions& theFieldDimensions) + { + if (robotPoses.empty()) + { + return {0.5f, 0.5f}; + } + + const float HALF_STEP_SIZE_X = HeatMap::getStepSizeX(theFieldDimensions) / 2.f; + const float HALF_STEP_SIZE_Y = HeatMap::getStepSizeY(theFieldDimensions) / 2.f; + const float MAX_DISTANCE = FieldUtils::getMaxDistanceOnField(theFieldDimensions); + const Angle MAX_GOAL_KICK_HEAT_ANGLE = 180_deg; + const float MAX_GOAL_KICK_HEAT_DISTANCE = 4000.f; + + float minDistance = MAX_DISTANCE; + float maxGoalKickHeat = 0.f; + + for (const Pose2f& robotPose : robotPoses) + { + float distance = Geometry::distance(robotPose.translation, cellPosition); + + if (HeatMap::isInCell(robotPose.translation, cellPosition, HALF_STEP_SIZE_X, HALF_STEP_SIZE_Y)) // Because the position is not perfect and we want to be safe + { + return {1.0f, 1.0f}; + } + + // find minDistance + if (distance < minDistance) + { + minDistance = distance; + } + + // find minDistanceToGoalKick + const Angle cellPositionToGoalAngle = (goalCenter - cellPosition).angle(); + const Angle robotPositionToCellPositionAngle = (cellPosition - robotPose.translation).angle(); + const Angle angleDiff = MathUtils::getAngleSmallestDiff(cellPositionToGoalAngle, robotPositionToCellPositionAngle); + const float angleMultiplier = std::pow(std::max(0.f, 1.f - angleDiff / MAX_GOAL_KICK_HEAT_ANGLE), 1 / 2.f); + const float distanceMultiplier = std::pow(std::max(0.f, 1.f - distance / MAX_GOAL_KICK_HEAT_DISTANCE), 1.55f); + const float goalKickHeat = angleMultiplier * distanceMultiplier; + if (goalKickHeat > maxGoalKickHeat) + { + maxGoalKickHeat = goalKickHeat; + } + } + + float maxKickHeat = 1 - minDistance / MAX_DISTANCE; + maxKickHeat = std::pow(MathUtils::clamp_f(maxKickHeat, 0, 1), 2.f); + + ASSERT(maxGoalKickHeat >= 0.f); + ASSERT(maxGoalKickHeat <= 1.f); + + return {maxKickHeat, maxGoalKickHeat}; + } + + // DRAW ============================================================================================================== + + static void draw(const HeatMap& heatMap, const bool stretchColors, const FieldDimensions& theFieldDimensions) + { + const float offsetX = HeatMap::getStepSizeX(theFieldDimensions) / 2; + const float offsetY = HeatMap::getStepSizeY(theFieldDimensions) / 2; + + std::vector drawFieldPositionVector; + std::vector heatVector; + for (int i = 0; i < HeatMap::CELL_COUNT; i++) + { + const Vector2f fieldPosition = HeatMap::indexToField(i, theFieldDimensions); + const Vector2f drawFieldPosition = {fieldPosition.x() - offsetX, fieldPosition.y() - offsetY}; + drawFieldPositionVector.push_back(drawFieldPosition); + const float heat = heatMap.getHeat(i); + heatVector.push_back(heat); + } + draw(drawFieldPositionVector, heatVector, stretchColors, theFieldDimensions); + } + + static void draw(const std::vector& positionVector, std::vector heatVector, const bool stretchColors, const FieldDimensions& theFieldDimensions) + { + if (stretchColors) + { + MathUtils::stretch(heatVector); + } + + const int stepSizeX = (int)HeatMap::getStepSizeX(theFieldDimensions); + const int stepSizeY = (int)HeatMap::getStepSizeY(theFieldDimensions); + for (int i = 0; i < HeatMap::CELL_COUNT; i++) + { + const Vector2f position = positionVector.at(i); + const float heat = heatVector.at(i); + + const int alpha = 155; + + RECTANGLE2(DRAW_HEAT_MAP, + Vector2i((int)position.x(), (int)position.y()), + stepSizeX, + stepSizeY, + 0, + 10, + Drawings::noBrush, + ColorRGBA(static_cast(255 - alpha), static_cast(alpha), 0), + Drawings::solidBrush, + ColorRGBA((unsigned char)(255 * (1 - heat)), (unsigned char)(255 * heat), 0, alpha)); + } + } +}; diff --git a/Src/Modules/BehaviorControl/TacticControl/KickWheelProvider/KickWheelProvider.cpp b/Src/Modules/BehaviorControl/TacticControl/KickWheelProvider/KickWheelProvider.cpp new file mode 100644 index 00000000..ebaae8ce --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/KickWheelProvider/KickWheelProvider.cpp @@ -0,0 +1,154 @@ +#include "KickWheelProvider.h" +#include "Tools/Math/Transformation.h" +#include +#include + +KickWheelProvider::KickWheelProvider() = default; + +void KickWheelProvider::update(KickWheel& kickWheel) +{ + DECLARE_DEBUG_DRAWING("behavior:KickWheelProvider:kickWheel", "drawingOnField"); + + if (kickWheel.angles.empty()) + { + const Angle STEP_ANGLE = 1_deg; + for (Angle angle = -180_deg; angle < 180_deg; angle = angle + STEP_ANGLE) + { + kickWheel.angles.push_back(angle); + } + } + + Vector2f ballPosition = theBallSymbols.ballPositionField; + const std::vector obstacles = getObstacles(); + + kickWheel.blockedDistances.clear(); + kickWheel.outsideDistances.clear(); + for (const Angle angle : kickWheel.angles) + { + const float blockedDistance = getBlockedDistance(angle, ballPosition, obstacles); + kickWheel.blockedDistances.push_back(blockedDistance); + const float outsideDistance = getOutsideDistance(angle, ballPosition, theFieldDimensions); + kickWheel.outsideDistances.push_back(outsideDistance); + } + + kickWheel.draw(ballPosition); +} + +std::vector KickWheelProvider::getObstacles() const +{ + std::vector obstacles = {}; + + // Add robots as obstacles + const float ROBOT_RADIUS = 100.f; + for (auto& robot : theRobotMap.robots) + { + obstacles.emplace_back(robot.pose.translation, ROBOT_RADIUS); + } + + // Add goal posts as obstacles + const float goalPostRadius = theFieldDimensions.goalPostRadius; + const Vector2f ownLeftGoalPost = {theFieldDimensions.xPosOwnGoalPost, theFieldDimensions.yPosLeftGoal}; + const Vector2f ownRightGoalPost = {theFieldDimensions.xPosOwnGoalPost, theFieldDimensions.yPosRightGoal}; + const Vector2f opponentLeftGoalPost = {theFieldDimensions.xPosOpponentGoalPost, theFieldDimensions.yPosLeftGoal}; + const Vector2f opponentRightGoalPost = {theFieldDimensions.xPosOpponentGoalPost, theFieldDimensions.yPosRightGoal}; + obstacles.emplace_back(ownLeftGoalPost, goalPostRadius); + obstacles.emplace_back(ownRightGoalPost, goalPostRadius); + obstacles.emplace_back(opponentLeftGoalPost, goalPostRadius); + obstacles.emplace_back(opponentRightGoalPost, goalPostRadius); + + return obstacles; +} + +float KickWheelProvider::getBlockedDistance(const Angle angle, const Vector2f& ballPosition, const std::vector& obstacles) +{ + const Vector2f direction = MathUtils::angleToVector(angle); + const Geometry::Line line = {ballPosition, direction}; + + float distance = std::numeric_limits::max(); + for (const Geometry::Circle& obstacle : obstacles) + { + Vector2f blockedTarget = {}; + Vector2f dummy = {}; + if (Geometry::getIntersectionOfLineAndCircle(line, obstacle, dummy, blockedTarget)) + { + const float firstIntersectionAngle = (blockedTarget - ballPosition).angle(); + if (MathUtils::getLargerMinusSmallerAngleDiff(angle, firstIntersectionAngle) > 5_deg) + { + // Intersection on wrong side + continue; + } + const float firstIntersectionDistance = Geometry::distance(ballPosition, blockedTarget); + if (firstIntersectionDistance < distance) + { + distance = firstIntersectionDistance; + } + } + } + return distance; +} + +/** + * @param ballPosition has to be inside field + */ +float KickWheelProvider::getOutsideDistance(const Angle angle, const Vector2f& ballPosition, const FieldDimensions& theFieldDimensions) +{ + const Vector2f direction = MathUtils::angleToVector(angle); + + if (FieldUtils::isInDirectionOfOpponentsGoal(ballPosition, direction, theFieldDimensions)) + { + return std::numeric_limits::max(); + } + + const float MAX_DISTANCE = 3 * theFieldDimensions.xPosOpponentGroundline; + const Geometry::Line directionLine = {ballPosition, direction.normalized(MAX_DISTANCE)}; + + /* + * 0 = in field, + * -1 = outside at least one border and no border to kick in over, + * 1 = outside at least one border and at least one border to kick in over + * Can go from 0 to -1 or 1 and from -1 to 1 but never leave 1! + */ + int ballOutsideFieldFlag = 0; + float minIntersectionDistance = std::numeric_limits::max(); + + for (const auto& rawFieldLine : theFieldDimensions.fieldBorder.lines) + { + const Vector2f maxDistanceDirection = (rawFieldLine.to - rawFieldLine.from).normalized(MAX_DISTANCE); + const Geometry::Line fieldLine = {Vector2f(rawFieldLine.from - maxDistanceDirection), 2 * maxDistanceDirection}; + + if (Geometry::isPointLeftOfLine(ballPosition, fieldLine)) + { + float intersectionFactor; + const bool intersected = Geometry::getIntersectionOfRaysFactor(directionLine, fieldLine, intersectionFactor); + if (intersected && intersectionFactor > 0) + { + const float intersectionDistance = intersectionFactor * MAX_DISTANCE; + minIntersectionDistance = std::min(minIntersectionDistance, intersectionDistance); + } + } + else + { + if (ballOutsideFieldFlag != 1) + { + const Angle leftAngle = (rawFieldLine.from - ballPosition).angle(); + const Angle rightAngle = (rawFieldLine.to - ballPosition).angle(); + const bool towardsField = MathUtils::isBetweenAngles(angle, leftAngle, rightAngle); + if (towardsField) + { + ballOutsideFieldFlag = 1; + } + else + { + ballOutsideFieldFlag = -1; + } + } + } + } + if (ballOutsideFieldFlag == -1) + { + return 0.f; + } + return minIntersectionDistance; +} + +MAKE_MODULE(KickWheelProvider, modeling) diff --git a/Src/Modules/BehaviorControl/TacticControl/KickWheelProvider/KickWheelProvider.h b/Src/Modules/BehaviorControl/TacticControl/KickWheelProvider/KickWheelProvider.h new file mode 100644 index 00000000..bb8e8a9f --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/KickWheelProvider/KickWheelProvider.h @@ -0,0 +1,30 @@ +#pragma once + +#include "Representations/Configuration/FieldDimensions.h" +#include "Representations/BehaviorControl/BallSymbols.h" +#include "Representations/Modeling/RobotMap.h" +#include "Representations/Modeling/KickWheel.h" + +MODULE(KickWheelProvider, + REQUIRES(BallSymbols), + REQUIRES(FieldDimensions), + REQUIRES(RobotMap), + + PROVIDES(KickWheel), + + LOADS_PARAMETERS(, + (float)(0.005f) dummy + ) +); + +class KickWheelProvider : public KickWheelProviderBase +{ +public: + KickWheelProvider(); + + void update(KickWheel& kickWheel) override; + + [[nodiscard]] inline std::vector getObstacles() const; + [[nodiscard]] static float getBlockedDistance(Angle angle, const Vector2f& ballPosition, const std::vector& obstacles); + [[nodiscard]] static float getOutsideDistance(Angle angle, const Vector2f& ballPosition, const FieldDimensions& theFieldDimensions); +}; diff --git a/Src/Modules/BehaviorControl/TacticControl/KicksProvider/Enums/KickWithLeftCondition.h b/Src/Modules/BehaviorControl/TacticControl/KicksProvider/Enums/KickWithLeftCondition.h new file mode 100644 index 00000000..ba84cd80 --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/KicksProvider/Enums/KickWithLeftCondition.h @@ -0,0 +1,16 @@ +#pragma once + +#include "Representations/Infrastructure/SensorData/InertialSensorData.h" +#include "Tools/Math/Angle.h" +#include "Tools/Math/Eigen.h" +#include "Tools/Streams/AutoStreamable.h" + +namespace KickInfos +{ + ENUM(KickWithLeftCondition, + onLeftSide, + onRightSide, + leftFootIsClosest, + rightFootIsClosest + ); +} diff --git a/Src/Modules/BehaviorControl/TacticControl/KicksProvider/Enums/Side.h b/Src/Modules/BehaviorControl/TacticControl/KicksProvider/Enums/Side.h new file mode 100644 index 00000000..bc3d9a3e --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/KicksProvider/Enums/Side.h @@ -0,0 +1,8 @@ +#pragma once + +enum class Side +{ + LEFT, + NONE, + RIGHT +}; \ No newline at end of file diff --git a/Src/Modules/BehaviorControl/TacticControl/KicksProvider/Kick.cpp b/Src/Modules/BehaviorControl/TacticControl/KicksProvider/Kick.cpp new file mode 100644 index 00000000..5d95809e --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/KicksProvider/Kick.cpp @@ -0,0 +1,59 @@ +#include "Kick.h" + +#include "Modules/BehaviorControl/TacticControl/KicksProvider/KickWithLeftUtils.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Enums/SelectedFoot.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Utils/KickUtils.h" + +bool Kick::getKickWithLeft(const Pose2f& playerPose, const Vector2f& ballPosition, const Vector2f& targetPosition, const SelectedFoot currentSelectedFoot) const +{ + switch (kickWithLeftCondition) + { + case KickInfos::KickWithLeftCondition::onLeftSide: + return KickWithLeftUtils::getKickWithLeftIfOnLeftSide(playerPose, ballPosition, targetPosition, currentSelectedFoot); + case KickInfos::KickWithLeftCondition::onRightSide: + return KickWithLeftUtils::getKickWithLeftIfOnRightSide(playerPose, ballPosition, targetPosition, currentSelectedFoot); + case KickInfos::KickWithLeftCondition::leftFootIsClosest: + return KickWithLeftUtils::getKickWithLeftIfLeftFootIsClosest(playerPose, ballPosition, targetPosition, currentSelectedFoot); + case KickInfos::KickWithLeftCondition::rightFootIsClosest: + return KickWithLeftUtils::getKickWithLeftIfRightFootIsClosest(playerPose, ballPosition, targetPosition, currentSelectedFoot); + default: + throw std::exception(); + } +} + +bool Kick::kickWithLeftToMirror(bool kickWithLeft) const +{ + if (switchKickFoot) + { + return kickWithLeft; + } + else + { + return !kickWithLeft; + } +} + +bool Kick::getKickWithLeftToTurnLeft(bool kickWithLeft) const +{ + if (switchKickFoot) + { + return kickWithLeft; + } + else + { + return !kickWithLeft; + } +} + +Pose2f Kick::getKickPose(const Vector2f& ballPosition, const Vector2f& targetPosition, bool kickWithLeft) const +{ + const bool turnLeft = getKickWithLeftToTurnLeft(kickWithLeft); + + const Angle ballToTargetAngle = (targetPosition - ballPosition).angle(); + const Angle optFieldAngleUnNormalized = ballToTargetAngle + (turnLeft ? 1.f : -1.f) * optAngle; + const Angle optFieldAngle = Angle::normalize(optFieldAngleUnNormalized); + + const bool mirror = kickWithLeftToMirror(kickWithLeft); + + return KickUtils::getKickPose(optFieldAngle, ballPosition, mirror, optXDistanceToBall, optYDistanceToBall); +} \ No newline at end of file diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/Kick.h b/Src/Modules/BehaviorControl/TacticControl/KicksProvider/Kick.h similarity index 53% rename from Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/Kick.h rename to Src/Modules/BehaviorControl/TacticControl/KicksProvider/Kick.h index 411a33c3..5b6b8caa 100644 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/Kick.h +++ b/Src/Modules/BehaviorControl/TacticControl/KicksProvider/Kick.h @@ -1,18 +1,32 @@ #pragma once -#include "Representations/BehaviorControl/RoleSymbols/Ballchaser.h" - -#include +#include "Modules/BehaviorControl/TacticControl/KicksProvider/Enums/KickWithLeftCondition.h" +#include "Modules/BehaviorControl/TacticControl/KicksProvider/Enums/Side.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Enums/DistanceRequirement.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Enums/SelectedFoot.h" +#include "Representations/BehaviorControl/RoleSymbols/PositioningAndKickSymbols.h" #include "Tools/Math/Eigen.h" -#include "Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Models/DistanceRequirement.h" +#include class Kick { public: - Kick(std::string name, const float minDistance, const float maxDistance, const bool distanceAdjustable, const float time, const float horizontalInaccuracy, const bool kickBlind, const Angle& optAngle, const float optXDistanceToBall, const float optYDistanceToBall) + Kick(std::string name, + const float minDistance, + const float maxDistance, + const bool distanceAdjustable, + const float time, + const float horizontalInaccuracy, + const bool kickBlind, + KickInfos::KickWithLeftCondition kickWithLeftCondition, + const bool switchKickFoot, + const Angle& optAngle, + const float optXDistanceToBall, + const float optYDistanceToBall) : minDistance(minDistance), maxDistance(maxDistance), distanceAdjustable(distanceAdjustable), time(time), verticalInaccuracy(maxDistance - minDistance), - horizontalInaccuracy(horizontalInaccuracy), inaccuracy((verticalInaccuracy + horizontalInaccuracy * 1000.f / maxDistance) / 2), kickBlind(kickBlind), optAngle(optAngle), - optXDistanceToBall(optXDistanceToBall), optYDistanceToBall(optYDistanceToBall), name(std::move(name)) + horizontalInaccuracy(horizontalInaccuracy), inaccuracy((verticalInaccuracy + horizontalInaccuracy * 1000.f / maxDistance) / 2), kickBlind(kickBlind), + kickWithLeftCondition(kickWithLeftCondition), switchKickFoot(switchKickFoot), optAngle(optAngle), optXDistanceToBall(optXDistanceToBall), + optYDistanceToBall(optYDistanceToBall), name(std::move(name)) { } virtual ~Kick() = default; @@ -46,6 +60,15 @@ class Kick return from + direction * realisticDistance; } + [[nodiscard]] float getRealisticDistance() const { return (minDistance + maxDistance) / 2; } + + [[nodiscard]] float getRealisticDistance(const bool hysteresis, const bool biggerIsBetter) const + { + const float hysteresisMinDistance = getMinDistance(hysteresis, biggerIsBetter); + const float hysteresisMaxDistance = getMaxDistance(hysteresis, biggerIsBetter); + return (hysteresisMinDistance + hysteresisMaxDistance) / 2; + } + [[nodiscard]] float getRealisticDistance(float distance, const bool hysteresis, const bool biggerIsBetter) const { const float hysteresisMinDistance = getMinDistance(hysteresis, biggerIsBetter); @@ -68,18 +91,25 @@ class Kick } } - [[nodiscard]] Angle getOptAngleField(const Vector2f& playerPosition, const Vector2f& ballPosition, const Vector2f& targetPosition) const + [[nodiscard]] virtual bool getKickWithLeft(const Pose2f& playerPose, const Vector2f& ballPosition, const Vector2f& targetPosition, const SelectedFoot currentSelectedFoot) const; + + [[nodiscard]] virtual bool mirrorToKickWithLeft(bool mirror) const { - const bool playerOnLeftSideOfKickDirection = Geometry::isPointLeftOfLine(playerPosition, ballPosition, targetPosition); - const Angle ballToTargetAngle = (targetPosition - ballPosition).angle(); - const Angle optFieldAngleUnnormalized = ballToTargetAngle + (playerOnLeftSideOfKickDirection ? -1.f : 1.f) * optAngle; - const Angle optFieldAngle = Angle::normalize(optFieldAngleUnnormalized); - return optFieldAngle; + const bool same = kickWithLeftToMirror(true); + return same ? mirror : !mirror; } - [[nodiscard]] virtual Pose2f getKickPose(const Pose2f& playerPose, const Vector2f& ballPosition, const Vector2f& targetPosition, bool leftFootClosestToBall) const = 0; + [[nodiscard]] virtual bool kickWithLeftToMirror(bool kickWithLeft) const; + + [[nodiscard]] virtual bool getKickWithLeftToTurnLeft(bool kickWithLeft) const; + + [[nodiscard]] virtual Pose2f getKickPose(const Vector2f& ballPosition, const Vector2f& targetPosition, bool kickWithLeft) const; + + virtual void perform(PositioningAndKickSymbols& pakSymbols, const Pose2f& kickPose, bool kickPoseMirrored, const Vector2f& targetPosition, bool start) const = 0; + + [[nodiscard]] Angle getOptAngle() const { return optAngle; } - virtual void perform(Ballchaser& ballchaser, const Pose2f& kickPose, const Vector2f& targetPosition, bool start) const = 0; + [[nodiscard]] bool isSwitchKickFoot() const { return switchKickFoot; } protected: const float minDistance; @@ -90,6 +120,8 @@ class Kick const float horizontalInaccuracy; const float inaccuracy; const bool kickBlind; + const KickInfos::KickWithLeftCondition kickWithLeftCondition; + const bool switchKickFoot; const Angle optAngle; const float optXDistanceToBall; const float optYDistanceToBall; diff --git a/Src/Modules/BehaviorControl/TacticControl/KicksProvider/KickUtils.h b/Src/Modules/BehaviorControl/TacticControl/KicksProvider/KickUtils.h new file mode 100644 index 00000000..c1a8da1c --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/KicksProvider/KickUtils.h @@ -0,0 +1,83 @@ +#pragma once + +#include "Modules/MotionControl/KickEngine/KickEngineParameters.h" +#include "Modules/MotionControl/DortmundWalkingEngine/StepData.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Enums/SelectedFoot.h" +#include "Tools/Math/Geometry.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Utils/MathUtils.h" + +class KickUtils +{ +public: + static bool getKickWithLeftIfOnRightSide(const Pose2f& playerPose, const Vector2f& ballPosition, const Vector2f& targetPosition, SelectedFoot currentSelectedFoot) + { + return !getKickWithLeftIfOnLeftSide(playerPose, ballPosition, targetPosition, currentSelectedFoot); + } + + static bool getKickWithLeftIfOnLeftSide(const Pose2f& playerPose, const Vector2f& ballPosition, const Vector2f& targetPosition, SelectedFoot currentSelectedFoot) + { + float hysteresis; + switch (currentSelectedFoot) + { + case SelectedFoot::LEFT: + hysteresis = -202500.f; // 450 * 450 since it has to be squared, so 50cm in each direction + break; + case SelectedFoot::NONE: + hysteresis = 0.f; + break; + case SelectedFoot::RIGHT: + hysteresis = 202500.f; + break; + default: + throw std::exception(); + } + return !Geometry::isPointLeftOfLine(playerPose.translation, ballPosition, targetPosition, hysteresis); + } + + static bool getKickWithLeftIfInnerFoot(const Pose2f& playerPose, const Vector2f& ballPosition, const Vector2f& targetPosition, SelectedFoot currentSelectedFoot) + { + const Vector2f direction = Vector2f(targetPosition - ballPosition).rotateLeft(); + const Geometry::Line kickDecisionLine = {ballPosition, direction}; + + if (Geometry::distance(playerPose.translation, ballPosition) < 750.f) + { + const Angle angleDiff = MathUtils::getAngleDiff(playerPose.rotation, direction.angle()); + float hysteresisMultiplier; + switch (currentSelectedFoot) + { + case SelectedFoot::LEFT: + hysteresisMultiplier = 1.05f; + break; + case SelectedFoot::NONE: + hysteresisMultiplier = 1.f; + break; + case SelectedFoot::RIGHT: + hysteresisMultiplier = 0.95f; + break; + default: + throw std::exception(); + } + const Angle angleDiffWithHysteresis = angleDiff * hysteresisMultiplier; + return angleDiffWithHysteresis > 90_deg; + } + else + { + float hysteresis; + switch (currentSelectedFoot) + { + case SelectedFoot::LEFT: + hysteresis = 202500.f; // 450 * 450 since it has to be squared, so 50cm in each direction + break; + case SelectedFoot::NONE: + hysteresis = 0.f; + break; + case SelectedFoot::RIGHT: + hysteresis = -202500.f; + break; + default: + throw std::exception(); + } + return Geometry::isPointLeftOfLine(playerPose.translation, ballPosition, targetPosition, hysteresis); + } + } +}; diff --git a/Src/Modules/BehaviorControl/TacticControl/KicksProvider/KickWithLeftUtils.h b/Src/Modules/BehaviorControl/TacticControl/KicksProvider/KickWithLeftUtils.h new file mode 100644 index 00000000..4b498209 --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/KicksProvider/KickWithLeftUtils.h @@ -0,0 +1,156 @@ +#pragma once + +#include "Modules/MotionControl/KickEngine/KickEngineParameters.h" +#include "Modules/MotionControl/DortmundWalkingEngine/StepData.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Ranges/Cone.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Enums/SelectedFoot.h" +#include "Tools/Math/Geometry.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Utils/MathUtils.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Logs/KickDrawings.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Utils/TacticUtils.h" + +class KickWithLeftUtils +{ +public: + static SelectedFoot getSelectedFootBasedOnDontRunInto(const Vector2f& ballPosition, const Vector2f& targetPosition, const Vector2f& opponentPosition) + { + const Pose2f ballPose = Pose2f((targetPosition - ballPosition).angle(), ballPosition); + const Angle ballToOpponentAngle = Transformation::fieldToRobot(ballPose, opponentPosition).angle(); + + const Angle pickSideToNotRunIntoOpponentAngle = 55_deg; + if (ballToOpponentAngle > pickSideToNotRunIntoOpponentAngle) + { + return SelectedFoot::LEFT; + } + if (ballToOpponentAngle < -pickSideToNotRunIntoOpponentAngle) + { + return SelectedFoot::RIGHT; + } + return SelectedFoot::NONE; + } + + static SelectedFoot getSelectedFootBasedOnMaximumBlock(const Pose2f& playerPose, const Vector2f& ballPosition, const Vector2f& targetPosition, const Vector2f& opponentPosition, const Cone& defenseCone) + { + const Pose2f ballPose = Pose2f((targetPosition - ballPosition).angle(), ballPosition); + const Angle ballToOpponentAngle = Transformation::fieldToRobot(ballPose, opponentPosition).angle(); + + const Angle pickSideToNotRunIntoOpponentAngle = 55_deg; + if (ballToOpponentAngle > pickSideToNotRunIntoOpponentAngle) + { + return SelectedFoot::LEFT; + } + if (ballToOpponentAngle < -pickSideToNotRunIntoOpponentAngle) + { + return SelectedFoot::RIGHT; + } + return SelectedFoot::NONE; + } + + static SelectedFoot getKickWithLeftBasedOn1v1(const Vector2f& ballPosition, const Vector2f& targetPosition, const Vector2f& opponentPosition) + { + const Pose2f ballPose = Pose2f((targetPosition - ballPosition).angle(), ballPosition); + const Angle ballToOpponentAngle = Transformation::fieldToRobot(ballPose, opponentPosition).angle(); + + const Angle pickSideToNotRunIntoOpponentAngle = 55_deg; + if (ballToOpponentAngle > pickSideToNotRunIntoOpponentAngle) + { + OUTPUT_TEXT("dontRunInto: left"); + return SelectedFoot::LEFT; + } + if (ballToOpponentAngle < -pickSideToNotRunIntoOpponentAngle) + { + OUTPUT_TEXT("dontRunInto: right"); + return SelectedFoot::RIGHT; + } + + const Angle pickSideToKickBySideOpponentAngle = 10_deg; + if (ballToOpponentAngle > pickSideToKickBySideOpponentAngle) + { + OUTPUT_TEXT("kickBySide: right"); + return SelectedFoot::RIGHT; + } + if (ballToOpponentAngle < -pickSideToKickBySideOpponentAngle) + { + OUTPUT_TEXT("kickBySide: left"); + return SelectedFoot::LEFT; + } + + OUTPUT_TEXT("none"); + return SelectedFoot::NONE; + } + + static bool getKickWithLeftIfOnRightSide(const Pose2f& playerPose, const Vector2f& ballPosition, const Vector2f& targetPosition, SelectedFoot currentSelectedFoot) + { + return !getKickWithLeftIfOnLeftSide(playerPose, ballPosition, targetPosition, currentSelectedFoot); + } + + static bool getKickWithLeftIfOnLeftSide(const Pose2f& playerPose, const Vector2f& ballPosition, const Vector2f& targetPosition, SelectedFoot currentSelectedFoot) + { + float hysteresis; + switch (currentSelectedFoot) + { + case SelectedFoot::LEFT: + hysteresis = -202500.f; // 450 * 450 since it has to be squared, so 50cm in each direction + break; + case SelectedFoot::NONE: + hysteresis = 0.f; + break; + case SelectedFoot::RIGHT: + hysteresis = 202500.f; + break; + default: + throw std::exception(); + } + return !Geometry::isPointLeftOfLine(playerPose.translation, ballPosition, targetPosition, hysteresis); + } + + static bool getKickWithLeftIfRightFootIsClosest(const Pose2f& playerPose, const Vector2f& ballPosition, const Vector2f& targetPosition, SelectedFoot currentSelectedFoot) + { + return !getKickWithLeftIfLeftFootIsClosest(playerPose, ballPosition, targetPosition, currentSelectedFoot); + } + + static bool getKickWithLeftIfLeftFootIsClosest(const Pose2f& playerPose, const Vector2f& ballPosition, const Vector2f& targetPosition, SelectedFoot currentSelectedFoot) + { + if (Geometry::distance(playerPose.translation, ballPosition) < 750.f) + { + const Vector2f direction = Vector2f(targetPosition - ballPosition).rotateLeft(); + const Angle angleDiff = MathUtils::getLargerMinusSmallerAngleDiff(playerPose.rotation, direction.angle()); + float hysteresisMultiplier; + switch (currentSelectedFoot) + { + case SelectedFoot::LEFT: + hysteresisMultiplier = 1.05f; + break; + case SelectedFoot::NONE: + hysteresisMultiplier = 1.f; + break; + case SelectedFoot::RIGHT: + hysteresisMultiplier = 0.95f; + break; + default: + throw std::exception(); + } + const Angle angleDiffWithHysteresis = angleDiff * hysteresisMultiplier; + return angleDiffWithHysteresis > 90_deg; + } + else + { + float hysteresis; + switch (currentSelectedFoot) + { + case SelectedFoot::LEFT: + hysteresis = 202500.f; // 450 * 450 since it has to be squared, so 45cm in each direction + break; + case SelectedFoot::NONE: + hysteresis = 0.f; + break; + case SelectedFoot::RIGHT: + hysteresis = -202500.f; + break; + default: + throw std::exception(); + } + return Geometry::isPointLeftOfLine(playerPose.translation, ballPosition, targetPosition, hysteresis); + } + } +}; diff --git a/Src/Modules/BehaviorControl/TacticControl/KicksProvider/KicksProvider.cpp b/Src/Modules/BehaviorControl/TacticControl/KicksProvider/KicksProvider.cpp new file mode 100644 index 00000000..16609691 --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/KicksProvider/KicksProvider.cpp @@ -0,0 +1,184 @@ +#include "KicksProvider.h" + +#include "Modules/BehaviorControl/TacticControl/KicksProvider/Types/Dribble.h" +#include "Modules/BehaviorControl/TacticControl/KicksProvider/Types/KickEngineKick.h" +#include "Modules/MotionControl/DortmundWalkingEngine/StepData.h" + +KicksProvider::KicksProvider() {} + +void KicksProvider::update(Kicks& kicks) +{ + if (loadedIntoRepresentation) + { + return; + } + kicks.kickEngineParametersVector = loadKickEngineParameters(); + kicks.customStepFileVector = loadCustomStepFiles(); + loadedIntoRepresentation = true; +} + +std::vector> KicksProvider::createKicks( + const std::vector& kickEngineParametersVector, const std::vector& customStepFileVector, const std::vector& kickNames) +{ + std::vector> kicksVector = {}; + for (const auto& name : kickNames) + { + if ("dribble" == name) + { + kicksVector.push_back(std::make_unique()); + continue; + } + bool found = false; + for (const KickEngineParameters& kickEngineParameters : kickEngineParametersVector) + { + if (kickEngineParameters.name == name) + { + const KickEngineKick kick = KickEngineKick(kickEngineParameters); + kicksVector.push_back(std::make_unique(kick)); + found = true; + break; + } + } + if (found) + { + continue; + } + for (const CustomStepsFile& customStepsFile : customStepFileVector) + { + if (customStepsFile.name == name) + { + const WalkingEngineKick kick = WalkingEngineKick(customStepsFile); + kicksVector.push_back(std::make_unique(kick)); + found = true; + break; + } + } + if (!found) + { + OUTPUT_WARNING("Kick with name " + name + " required but missing in the loaded folders!"); + } + } + return kicksVector; +} + +std::vector> KicksProvider::createKicks( + const std::vector& kickEngineParametersVector, const std::vector& customStepFileVector, const bool dribble) +{ + std::vector> kicksVector = {}; + for (const KickEngineParameters& kickEngineParameters : kickEngineParametersVector) + { + const KickEngineKick kick = KickEngineKick(kickEngineParameters); + kicksVector.push_back(std::make_unique(kick)); + } + for (const CustomStepsFile& customStepsFile : customStepFileVector) + { + const WalkingEngineKick kick = WalkingEngineKick(customStepsFile); + kicksVector.push_back(std::make_unique(kick)); + } + if (dribble) + { + kicksVector.push_back(std::make_unique()); + } + return kicksVector; +} + +// KickEngineParameters ============================================================================================================================================================ + +std::vector KicksProvider::loadKickEngineParameters() +{ + std::vector params = {}; + const std::string dirname = std::string(File::getBHDir()) + KICK_ENGINE_PATH + "/"; + for (const auto& file : std::filesystem::directory_iterator(dirname)) + { + if (auto parametersOptional = loadKickEngineParameter(file)) + { + params.push_back(parametersOptional.value()); + } + } + return params; +} + +std::optional KicksProvider::loadKickEngineParameter(const std::filesystem::directory_entry& file) +{ + const std::string fileName = file.path().filename().string(); + + if (fileName == "ignore") + { + return {}; + } + + if (!file.is_regular_file() || fileName.substr(fileName.size() - 4) != ".kmc") + { + OUTPUT_TEXT("Warning: Wrong file format for " << file.path().string()); + fprintf(stderr, "Warning: Wrong file format for %s \n", file.path().string().c_str()); + return {}; + } + + InMapFile stream(file.path().string()); + ASSERT(stream.exists()); + + KickEngineParameters parameters; + stream >> parameters; + + strcpy(parameters.name, fileName.substr(0, fileName.size() - 4).c_str()); + parameters.type = KickRequest::getKickMotionFromName(parameters.name); + + if (parameters.type < KickRequest::none) + { + return parameters; + } + else + { + OUTPUT_TEXT("Warning: KickRequest is missing the id for " << parameters.name); + fprintf(stderr, "Warning: KickRequest is missing the id for %s \n", parameters.name); + return {}; + } +} + +// StepFiles ===================================================================================================================================================================== + +std::vector KicksProvider::loadCustomStepFiles() +{ + std::vector stepFiles = {}; + const std::string dirname = std::string(File::getBHDir()) + WALK_ENGINE_PATH + "/"; + for (const auto& file : std::filesystem::directory_iterator(dirname)) + { + if (auto stepFilesOptional = loadCustomStepFile(file)) + { + stepFiles.push_back(stepFilesOptional.value()); + } + } + return stepFiles; +} + +std::optional KicksProvider::loadCustomStepFile(const std::filesystem::directory_entry& file) +{ + const std::string fileNameWithType = file.path().filename().string(); + const std::string fileNameWithoutType = fileNameWithType.substr(0, fileNameWithType.size() - 4); + + if (fileNameWithType == "ignore") + { + return {}; + } + + if (!file.is_regular_file() || fileNameWithType.substr(fileNameWithType.size() - 4) != ".cfg") + { + OUTPUT(idText, text, std::string("Unable to load: ") << fileNameWithType); + return {}; + } + + CustomStepsFile stepFile; + InMapFile stream(file.path().string()); + ASSERT(stream.exists()); + stream >> stepFile; + + strcpy(stepFile.name, fileNameWithoutType.c_str()); + stepFile.stepRequest = WalkRequest::getStepRequestFromName(fileNameWithoutType.c_str()); + + // Must begin with single support + ASSERT(stepFile.steps.empty() || !(stepFile.steps.front().onFloor[0] && stepFile.steps.front().onFloor[1])); + + return stepFile; +} + +MAKE_MODULE(KicksProvider, modeling) diff --git a/Src/Modules/BehaviorControl/TacticControl/KicksProvider/KicksProvider.h b/Src/Modules/BehaviorControl/TacticControl/KicksProvider/KicksProvider.h new file mode 100644 index 00000000..9bb9e203 --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/KicksProvider/KicksProvider.h @@ -0,0 +1,47 @@ +#pragma once + +#include "Modules/MotionControl/DortmundWalkingEngine/StepData.h" +#include "Modules/MotionControl/KickEngine/KickEngineParameters.h" +#include "Representations/Modeling/Kicks.h" + +#include +#include +#include +#include +#include +#include + +MODULE(KicksProvider, + + PROVIDES(Kicks), + + LOADS_PARAMETERS(, + (float)(0.005f) dummyParameter + ) +); + +class KicksProvider : public KicksProviderBase +{ + +public: + inline static const std::string KICK_ENGINE_PATH = "/Config/Kicks/KickEngine"; + inline static const std::string WALK_ENGINE_PATH = "/Config/Kicks/WalkingEngine"; + + KicksProvider(); + + void update(Kicks& kicks) override; + + static std::vector loadKickEngineParameters(); + static std::vector loadCustomStepFiles(); + + static std::vector> createKicks( + const std::vector& kickEngineParametersVector, const std::vector& customStepFileVector, const std::vector& kickNames); + static std::vector> createKicks( + const std::vector& kickEngineParametersVector, const std::vector& customStepFileVector, const bool dribble); + +private: + static std::optional loadKickEngineParameter(const std::filesystem::directory_entry& file); + static std::optional loadCustomStepFile(const std::filesystem::directory_entry& file); + + bool loadedIntoRepresentation = false; +}; diff --git a/Src/Modules/BehaviorControl/TacticControl/KicksProvider/Types/Dribble.h b/Src/Modules/BehaviorControl/TacticControl/KicksProvider/Types/Dribble.h new file mode 100644 index 00000000..762a43f4 --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/KicksProvider/Types/Dribble.h @@ -0,0 +1,24 @@ +#pragma once + +#include "Modules/BehaviorControl/TacticControl/KicksProvider/Kick.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Utils/KickUtils.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Utils/ThresholdUtils.h" +#include "Representations/BehaviorControl/RoleSymbols/PositioningAndKickSymbols.h" + +class Dribble : public Kick +{ + +public: + explicit Dribble() : Kick("Dribble", 200.f, 500.f, false, 1.f, 200.f, false, KickInfos::KickWithLeftCondition::onLeftSide, false, 0_deg, 200.f, 0.f) {} + + void perform(PositioningAndKickSymbols& pakSymbols, const Pose2f& kickPose, const bool kickPoseMirrored, const Vector2f& targetPosition, const bool start) const override + { + ThresholdUtils::setThresholdsForDribble(pakSymbols); + pakSymbols.optPosition = kickPose; + pakSymbols.kickTarget = targetPosition; + pakSymbols.kickType = MotionRequest::KickType::dribble; + pakSymbols.longKickType = KickRequest::KickMotionID::kickMiddleFast; + pakSymbols.mirrorKick = kickPoseMirrored; + pakSymbols.kickBlind = kickBlind; + }; +}; diff --git a/Src/Modules/BehaviorControl/TacticControl/KicksProvider/Types/KickEngineKick.h b/Src/Modules/BehaviorControl/TacticControl/KicksProvider/Types/KickEngineKick.h new file mode 100644 index 00000000..a3e7833b --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/KicksProvider/Types/KickEngineKick.h @@ -0,0 +1,54 @@ +#pragma once + +#include "Modules/BehaviorControl/TacticControl/KicksProvider/Kick.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Utils/KickUtils.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Utils/ThresholdUtils.h" +#include "Modules/MotionControl/KickEngine/KickEngineParameters.h" + +class KickEngineKick : public Kick +{ + KickRequest::KickMotionID longKickType; + +public: + explicit KickEngineKick(const KickEngineParameters& kickEngineParameters) + : Kick(kickEngineParameters.name, + kickEngineParameters.kickDistance[0] * 1000.f, + kickEngineParameters.kickDistance[1] * 1000.f, + kickEngineParameters.distanceAdjustable, + calculateKickTime(kickEngineParameters), + kickEngineParameters.horizontalInaccuracy * 1000.f, + kickEngineParameters.kickBlind, + kickEngineParameters.kickWithLeftCondition, + kickEngineParameters.switchKickFoot, + kickEngineParameters.kickAngle, + kickEngineParameters.ballOffset.x() * 1000.f, + kickEngineParameters.ballOffset.y() * 1000.f) + { + longKickType = kickEngineParameters.type; + } + + static float calculateKickTime(const KickEngineParameters& kickEngineParameters) + { + unsigned int time = 0; + for (const auto& phase : kickEngineParameters.phaseParameters) + { + if (phase.kick) + { + break; + } + time += phase.duration; + } + return (float)time / 1000.f; + } + + void perform(PositioningAndKickSymbols& pakSymbols, const Pose2f& kickPose, const bool kickPoseMirrored, const Vector2f& targetPosition, const bool start) const override + { + ThresholdUtils::setThresholdsForLongKick(pakSymbols); + pakSymbols.optPosition = kickPose; + pakSymbols.kickTarget = targetPosition; + pakSymbols.kickType = MotionRequest::KickType::longKick; + pakSymbols.longKickType = longKickType; + pakSymbols.mirrorKick = kickPoseMirrored; + pakSymbols.kickBlind = kickBlind; + } +}; diff --git a/Src/Modules/BehaviorControl/TacticControl/KicksProvider/Types/WalkingEngineKick.h b/Src/Modules/BehaviorControl/TacticControl/KicksProvider/Types/WalkingEngineKick.h new file mode 100644 index 00000000..be4d15d7 --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/KicksProvider/Types/WalkingEngineKick.h @@ -0,0 +1,54 @@ +#pragma once + +#include "Modules/BehaviorControl/TacticControl/KicksProvider/Kick.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Utils/KickUtils.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Utils/ThresholdUtils.h" +#include "Modules/MotionControl/DortmundWalkingEngine/StepData.h" + +class WalkingEngineKick : public Kick +{ + WalkRequest::StepRequest walkKickType; + +public: + explicit WalkingEngineKick(const CustomStepsFile& customStepsFile) + : Kick(customStepsFile.name, + customStepsFile.kickDistance[0] * 1000.f, + customStepsFile.kickDistance[1] * 1000.f, + customStepsFile.distanceAdjustable, + calculateKickTime(customStepsFile), + customStepsFile.horizontalInaccuracy * 1000.f, + customStepsFile.kickBlind, + customStepsFile.kickWithLeftCondition, + customStepsFile.switchKickFoot, + customStepsFile.kickAngle, + customStepsFile.ballOffset.x() * 1000.f, + customStepsFile.ballOffset.y() * 1000.f) + { + walkKickType = customStepsFile.stepRequest; + } + + static float calculateKickTime(const CustomStepsFile& customStepsFile) + { + int time = 0; + for (const auto& step : customStepsFile.steps) + { + if (step.kick) + { + break; + } + time += step.duration; + } + return (float)time / 10.f; + } + + void perform(PositioningAndKickSymbols& pakSymbols, const Pose2f& kickPose, const bool kickPoseMirrored, const Vector2f& targetPosition, const bool start) const override + { + ThresholdUtils::setThresholdsForAnyKick(pakSymbols); + pakSymbols.optPosition = kickPose; + pakSymbols.kickTarget = targetPosition; + pakSymbols.kickType = MotionRequest::KickType::walkKick; + pakSymbols.walkKickType = walkKickType; + pakSymbols.mirrorKick = kickPoseMirrored; + pakSymbols.kickBlind = kickBlind; + } +}; diff --git a/Src/Modules/BehaviorControl/TacticControl/PositioningSymbolsProvider.cpp b/Src/Modules/BehaviorControl/TacticControl/PositioningSymbolsProvider.cpp index 08e187c7..97dc2ac2 100644 --- a/Src/Modules/BehaviorControl/TacticControl/PositioningSymbolsProvider.cpp +++ b/Src/Modules/BehaviorControl/TacticControl/PositioningSymbolsProvider.cpp @@ -20,7 +20,7 @@ void PositioningSymbolsProvider::update(PositioningSymbols& positioningSymbols) { useBallSearchPosition = true; // select closest - if (!wasInBallSearch && theRoleSymbols.role != BehaviorData::ballchaser) + if (!wasInBallSearch && theBallChaserDecision.playerNumberToBall != theRobotInfo.number) selectClosestBallSearchPosition(positioningSymbols.optPosition); else { @@ -63,96 +63,46 @@ void PositioningSymbolsProvider::update(PositioningSymbols& positioningSymbols) switch (theRoleSymbols.role) { case BehaviorData::keeper: - positioningSymbols.optPosition = theKeeper.optPosition; - positioningSymbols.thresholdXBack = theKeeper.thresholdXBack; - positioningSymbols.thresholdXFront = theKeeper.thresholdXFront; - positioningSymbols.thresholdY = theKeeper.thresholdY; - positioningSymbols.thresholdRotation = theKeeper.thresholdRotation; - positioningSymbols.stopAtTarget = theKeeper.stopAtTarget; - positioningSymbols.previewArrival = theKeeper.previewArrival; + positioningSymbols = theKeeper; break; case BehaviorData::defenderRight: - positioningSymbols.optPosition = theDefenderRight.optPosition; - positioningSymbols.thresholdXBack = theDefenderRight.thresholdXBack; - positioningSymbols.thresholdXFront = theDefenderRight.thresholdXFront; - positioningSymbols.thresholdY = theDefenderRight.thresholdY; - positioningSymbols.thresholdRotation = theDefenderRight.thresholdRotation; - positioningSymbols.stopAtTarget = theDefenderRight.stopAtTarget; - positioningSymbols.previewArrival = theDefenderRight.previewArrival; + positioningSymbols = theDefenderRight; break; case BehaviorData::defenderLeft: - positioningSymbols.optPosition = theDefenderLeft.optPosition; - positioningSymbols.thresholdXBack = theDefenderLeft.thresholdXBack; - positioningSymbols.thresholdXFront = theDefenderLeft.thresholdXFront; - positioningSymbols.thresholdY = theDefenderLeft.thresholdY; - positioningSymbols.thresholdRotation = theDefenderLeft.thresholdRotation; - positioningSymbols.stopAtTarget = theDefenderLeft.stopAtTarget; - positioningSymbols.previewArrival = theDefenderLeft.previewArrival; + positioningSymbols = theDefenderLeft; break; case BehaviorData::defenderSingle: - positioningSymbols.optPosition = theDefenderSingle.optPosition; - positioningSymbols.thresholdXBack = theDefenderSingle.thresholdXBack; - positioningSymbols.thresholdXFront = theDefenderSingle.thresholdXFront; - positioningSymbols.thresholdY = theDefenderSingle.thresholdY; - positioningSymbols.thresholdRotation = theDefenderSingle.thresholdRotation; - positioningSymbols.stopAtTarget = theDefenderSingle.stopAtTarget; - positioningSymbols.previewArrival = theDefenderSingle.previewArrival; + positioningSymbols = theDefenderSingle; break; case BehaviorData::center: - positioningSymbols.optPosition = theCenter.optPosition; - positioningSymbols.thresholdXBack = theCenter.thresholdXBack; - positioningSymbols.thresholdXFront = theCenter.thresholdXFront; - positioningSymbols.thresholdY = theCenter.thresholdY; - positioningSymbols.thresholdRotation = theCenter.thresholdRotation; - positioningSymbols.stopAtTarget = theCenter.stopAtTarget; - positioningSymbols.previewArrival = theCenter.previewArrival; + positioningSymbols = theCenter; break; case BehaviorData::receiver: - positioningSymbols.optPosition = theReceiver.optPosition; - positioningSymbols.thresholdXBack = theReceiver.thresholdXBack; - positioningSymbols.thresholdXFront = theReceiver.thresholdXFront; - positioningSymbols.thresholdY = theReceiver.thresholdY; - positioningSymbols.thresholdRotation = theReceiver.thresholdRotation; - positioningSymbols.stopAtTarget = theReceiver.stopAtTarget; - positioningSymbols.previewArrival = theReceiver.previewArrival; + positioningSymbols = theReceiver; break; case BehaviorData::replacementKeeper: - positioningSymbols.optPosition = theReplacementKeeper.optPosition; - positioningSymbols.thresholdXBack = theReplacementKeeper.thresholdXBack; - positioningSymbols.thresholdXFront = theReplacementKeeper.thresholdXFront; - positioningSymbols.thresholdY = theReplacementKeeper.thresholdY; - positioningSymbols.thresholdRotation = theReplacementKeeper.thresholdRotation; - positioningSymbols.stopAtTarget = theReplacementKeeper.stopAtTarget; - positioningSymbols.previewArrival = theReplacementKeeper.previewArrival; + positioningSymbols = theReplacementKeeper; break; - case BehaviorData::ballchaserKeeper: - positioningSymbols.optPosition = theBallchaserKeeper.optPosition; - positioningSymbols.thresholdXBack = theBallchaserKeeper.thresholdXBack; - positioningSymbols.thresholdXFront = theBallchaserKeeper.thresholdXFront; - positioningSymbols.thresholdY = theBallchaserKeeper.thresholdY; - positioningSymbols.thresholdRotation = theBallchaserKeeper.thresholdRotation; - positioningSymbols.stopAtTarget = theBallchaserKeeper.stopAtTarget; - positioningSymbols.previewArrival = theBallchaserKeeper.previewArrival; + case BehaviorData::leftWing: + positioningSymbols = theLeftWing; + break; + case BehaviorData::rightWing: + positioningSymbols = theRightWing; + break; + case BehaviorData::backWing: + positioningSymbols = theBackWing; + break; + case BehaviorData::frontWing: + positioningSymbols = theFrontWing; break; case BehaviorData::backupBallchaser: - positioningSymbols.optPosition = theBackupBallchaser.optPosition; - positioningSymbols.thresholdXBack = theBackupBallchaser.thresholdXBack; - positioningSymbols.thresholdXFront = theBackupBallchaser.thresholdXFront; - positioningSymbols.thresholdY = theBackupBallchaser.thresholdY; - positioningSymbols.thresholdRotation = theBackupBallchaser.thresholdRotation; - positioningSymbols.stopAtTarget = theBackupBallchaser.stopAtTarget; - positioningSymbols.previewArrival = theBackupBallchaser.previewArrival; + positioningSymbols = theBackupBallchaser; break; default: //Ballchaser - positioningSymbols.optPosition = theBallchaser.optPosition; - positioningSymbols.thresholdXBack = theBallchaser.thresholdXBack; - positioningSymbols.thresholdXFront = theBallchaser.thresholdXFront; - positioningSymbols.thresholdY = theBallchaser.thresholdY; - positioningSymbols.thresholdRotation = theBallchaser.thresholdRotation; - positioningSymbols.stopAtTarget = theBallchaser.stopAtTarget; - positioningSymbols.previewArrival = theBallchaser.previewArrival; + positioningSymbols = theBallchaser; break; } + static_assert(BehaviorData::RoleAssignment::numOfRoleAssignments == 13, "Missing role!"); positioningSymbols.distanceToOptPosition = (positioningSymbols.optPosition.translation - theRobotPoseAfterPreview.translation).norm(); @@ -164,6 +114,14 @@ void PositioningSymbolsProvider::update(PositioningSymbols& positioningSymbols) positioningSymbols.inIllegalPosition = inOpponentHalf || notOnField || illegalyInCenterCircle; } +void PositioningSymbolsProvider::update(PositioningAndKickSymbols& positioningAndKickSymbols) +{ + positioningAndKickSymbols = theBallchaser; + + // TODO Add Keeper + // positioningAndKickSymbols = theKeeper; +} + void PositioningSymbolsProvider::selectClosestBallSearchPosition(Pose2f& position) { ASSERT(ballSearchRoleIndex < static_cast(theBallSearch.ballSearchPositions.size())); diff --git a/Src/Modules/BehaviorControl/TacticControl/PositioningSymbolsProvider.h b/Src/Modules/BehaviorControl/TacticControl/PositioningSymbolsProvider.h index 86d00f8b..8ffaa31b 100644 --- a/Src/Modules/BehaviorControl/TacticControl/PositioningSymbolsProvider.h +++ b/Src/Modules/BehaviorControl/TacticControl/PositioningSymbolsProvider.h @@ -10,25 +10,32 @@ #pragma once -#include "Tools/Module/Module.h" -#include "Representations/BehaviorControl/RoleSymbols.h" -#include "Representations/Modeling/RobotPose.h" -#include "Representations/Configuration/FieldDimensions.h" #include "Representations/BehaviorControl/BallSearch.h" #include "Representations/BehaviorControl/GameSymbols.h" -#include "Representations/BehaviorControl/PositioningSymbols.h" -#include "Representations/BehaviorControl/TacticSymbols.h" -#include "Representations/BehaviorControl/RoleSymbols/DefenderRight.h" -#include "Representations/BehaviorControl/RoleSymbols/DefenderLeft.h" -#include "Representations/BehaviorControl/RoleSymbols/Center.h" -#include "Representations/BehaviorControl/RoleSymbols/Keeper.h" +#include "Representations/BehaviorControl/RoleSymbols.h" +#include "Representations/BehaviorControl/RoleSymbols/BackupBallchaser.h" #include "Representations/BehaviorControl/RoleSymbols/Ballchaser.h" +#include "Representations/BehaviorControl/RoleSymbols/BallchaserKeeper.h" +#include "Representations/BehaviorControl/RoleSymbols/Center.h" +#include "Representations/BehaviorControl/RoleSymbols/DefenderLeft.h" +#include "Representations/BehaviorControl/RoleSymbols/DefenderRight.h" #include "Representations/BehaviorControl/RoleSymbols/DefenderSingle.h" +#include "Representations/BehaviorControl/RoleSymbols/Keeper.h" +#include "Representations/BehaviorControl/RoleSymbols/PositioningSymbols.h" +#include "Representations/BehaviorControl/RoleSymbols/PositioningAndKickSymbols.h" #include "Representations/BehaviorControl/RoleSymbols/Receiver.h" -#include "Representations/BehaviorControl/RoleSymbols/BackupBallchaser.h" -#include "Representations/BehaviorControl/RoleSymbols/BallchaserKeeper.h" #include "Representations/BehaviorControl/RoleSymbols/ReplacementKeeper.h" +#include "Representations/BehaviorControl/RoleSymbols/LeftWing.h" +#include "Representations/BehaviorControl/RoleSymbols/RightWing.h" +#include "Representations/BehaviorControl/RoleSymbols/FrontWing.h" +#include "Representations/BehaviorControl/RoleSymbols/BackWing.h" +#include "Representations/BehaviorControl/TacticSymbols.h" +#include "Representations/BehaviorControl/BallChaserDecision.h" +#include "Representations/Configuration/FieldDimensions.h" #include "Representations/Infrastructure/GameInfo.h" +#include "Representations/Infrastructure/RobotInfo.h" +#include "Representations/Modeling/RobotPose.h" +#include "Tools/Module/Module.h" MODULE(PositioningSymbolsProvider, REQUIRES(RoleSymbols), @@ -46,9 +53,17 @@ MODULE(PositioningSymbolsProvider, REQUIRES(BackupBallchaser), REQUIRES(BallchaserKeeper), REQUIRES(ReplacementKeeper), + REQUIRES(LeftWing), + REQUIRES(RightWing), + REQUIRES(BackWing), + REQUIRES(FrontWing), REQUIRES(TacticSymbols), REQUIRES(GameInfo), - PROVIDES(PositioningSymbols) + REQUIRES(BallChaserDecision), + REQUIRES(RobotInfo), + + PROVIDES(PositioningSymbols), + PROVIDES(PositioningAndKickSymbols) ); @@ -64,7 +79,8 @@ class PositioningSymbolsProvider : public PositioningSymbolsProviderBase PositioningSymbolsProvider() {} /** Updates some of the symbols */ - void update(PositioningSymbols& positioningSymbols); + void update(PositioningSymbols& positioningSymbols) override; + void update(PositioningAndKickSymbols& positioningAndKickSymbols) override; private: void selectClosestBallSearchPosition(Pose2f& position); diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleDynamicProvider.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleDynamicProvider.cpp index 6a2b4bf0..3ab45220 100644 --- a/Src/Modules/BehaviorControl/TacticControl/RoleDynamicProvider.cpp +++ b/Src/Modules/BehaviorControl/TacticControl/RoleDynamicProvider.cpp @@ -15,27 +15,25 @@ void RoleDynamicProvider::update(RoleSymbols& roleSymbols) DECLARE_DEBUG_DRAWING3D("representation:RoleSymbols", "robot"); DECLARE_DEBUG_DRAWING3D("representation:RoleAssignment", "field"); BehaviorData::RoleAssignment lastRole = roleSymbols.role; - roleSymbols.roleSuggestions.clear(); - for (int i = 0; i <= MAX_NUM_PLAYERS; i++) - roleSymbols.roleSuggestions.push_back(BehaviorData::noRole); + roleSymbols.roleSuggestions.assign(MAX_NUM_PLAYERS + 1, BehaviorData::RoleAssignment::noRole); + roleSymbols.dynamic = false; + + updateRolePositions(); if (theRobotInfo.penalty != PENALTY_NONE) return; if (theGameInfo.gamePhase == GAME_PHASE_PENALTYSHOOT) { - roleSymbols.role = theGameInfo.kickingTeam == theOwnTeamInfo.teamNumber ? BehaviorData::RoleAssignment::ballchaser : BehaviorData::RoleAssignment::keeper; + roleSymbols.role = theGameInfo.kickingTeam == theOwnTeamInfo.teamNumber ? BehaviorData::RoleAssignment::receiver : BehaviorData::RoleAssignment::keeper; roleSymbols.roleSuggestions[theRobotInfo.number] = roleSymbols.role; } + else if (!theTeammateData.wlanOK && useStaticAssignmentNoWifi) + { + setStaticAssignment(roleSymbols); + } else { - if (!theTeammateData.wlanOK && useStaticAssignmentNoWifi) - { - // if no wifi, set static role using 2021 robot setup - setStaticAssignment(roleSymbols); - return; - } - addRoles(roleSymbols); getCurrentTeamStatus(); @@ -43,6 +41,8 @@ void RoleDynamicProvider::update(RoleSymbols& roleSymbols) findBestRoleAssignment(roleSymbols); takeNewestRoleAssignment(roleSymbols); + + roleSymbols.dynamic = true; } if (roleSymbols.role != lastRole) @@ -50,56 +50,43 @@ void RoleDynamicProvider::update(RoleSymbols& roleSymbols) ASSERT(roleSymbols.role < BehaviorData::numOfRoleAssignments); - declareDebugDrawing(roleSymbols); -} - -/** - * \brief Declares a drawing visualizing the role distribution in the simulator. - * - * Each robots player number will appear above its head with the color representing the role. - * - * \param roleSymbols A copy of the current RoleSymbols representation. - */ -void RoleDynamicProvider::declareDebugDrawing(RoleSymbols& roleSymbols) -{ - DEBUG_DRAWING3D("representation:RoleSymbols", "robot") + COMPLEX_DRAWING3D("representation:RoleAssignment") { - // default color: keeper - ColorRGBA digitColor = ColorRGBA::blue; - switch (roleSymbols.role) + if (const Teammate* teammate = theTeammateData.getNewestEventMessage(TeamCommEvents::SendReason::newRolesAssigned)) { - case BehaviorData::ballchaser: - digitColor = ColorRGBA::red; - break; - case BehaviorData::defenderLeft: - digitColor = ColorRGBA::green; - break; - case BehaviorData::defenderRight: - digitColor = ColorRGBA::green; - break; - case BehaviorData::defenderSingle: - digitColor = ColorRGBA::yellow; - break; - case BehaviorData::center: - digitColor = ColorRGBA::white; - break; - case BehaviorData::backupBallchaser: - digitColor = ColorRGBA::magenta; - break; - case BehaviorData::receiver: - digitColor = ColorRGBA::orange; - break; - case BehaviorData::replacementKeeper: - digitColor = ColorRGBA::cyan; - break; - default: - break; + const auto& suggestions = teammate->behaviorData.roleSuggestions; + if (teammate->playerNumber == theRobotInfo.number) + { + for (unsigned char player = 1; player < suggestions.size(); ++player) + { + if (suggestions[player] == BehaviorData::RoleAssignment::keeper || suggestions[player] == BehaviorData::RoleAssignment::noRole) + continue; + + const ColorRGBA color = theOwnTeamInfo.teamNumber == 1 ? ColorRGBA::blue : ColorRGBA::red; + + const Vector2f drawOffset(10.f, 10.f); //add an small offset for drawing, otherwise the yellow path lines will may be visible + const Vector2f currentPosition = robotPoses[player].translation + drawOffset; + const Vector3f currentPosition3f(currentPosition.x(), currentPosition.y(), 0.f); + + DRAWDIGIT3D("representation:RoleAssignment", player, currentPosition3f + Vector3f(0.f, 0.f, 50.f), 20, 2, color); + + const Vector2f rolePassivePosition = rolePositions[suggestions[player]] + drawOffset; + const Vector3f rolePassivePosition3f(rolePassivePosition.x(), rolePassivePosition.y(), 0.f); + + LINE3D("representation:RoleAssignment", + currentPosition3f.x(), + currentPosition3f.y(), + currentPosition3f.z(), + rolePassivePosition3f.x(), + rolePassivePosition3f.y(), + rolePassivePosition3f.z(), + 2.f, + color); + const Vector3f diff = (rolePassivePosition3f - currentPosition3f).normalized(); + CYLINDERARROW3D("representation:RoleAssignment", rolePassivePosition3f - diff * 50.f, rolePassivePosition3f + diff * 50.f, 2.f, 90.f, 25.f, color); + } + } } - int pNumber = theRobotInfo.number; - float centerDigit = (pNumber > 1) ? 50.f : 0; - DRAWDIGIT3D("representation:RoleSymbols", pNumber, Vector3f(centerDigit, 0.f, 600), 100, 8, digitColor); - Pose3f origin(0, 0, 370); - origin.rotateY(90_deg); } } @@ -118,66 +105,64 @@ void RoleDynamicProvider::getCurrentTeamStatus() playerNumbers.clear(); // get information for myself - bool iAmBallChaser = theRobotInfo.number == theBallChaserDecision.playerNumberToBall; - bool iAmKeeper = theRobotInfo.number == 1; - if (!iAmBallChaser && !iAmKeeper && theRobotInfo.penalty == PENALTY_NONE) - { + const bool iAmKeeper = theRobotInfo.number == 1; + const bool inReady = (theGameInfo.state == STATE_INITIAL || theGameInfo.state == STATE_READY); + const bool iAmBallchaserInReady = inReady && theRobotInfo.number == theBallChaserDecision.playerNumberToBall; + if (!iAmKeeper && !iAmBallchaserInReady && theRobotInfo.penalty == PENALTY_NONE) playerNumbers.push_back(theRobotInfo.number); - passiveRolePositions[theRobotInfo.number].clear(); - addMyPassiveRolePositions(); - robotPoses[theRobotInfo.number] = theRobotPoseAfterPreview; - } + robotPoses[theRobotInfo.number] = theRobotPoseAfterPreview; + // get information for teammates for (auto& mate : theTeammateData.teammates) { - bool mateIsBallChaser = mate.number == theBallChaserDecision.playerNumberToBall; - bool mateIsKeeper = mate.number == 1; - if (!mateIsBallChaser && !mateIsKeeper && mate.status >= Teammate::Status::ACTIVE) - { - playerNumbers.push_back(mate.number); - passiveRolePositions[mate.number].clear(); - for (const auto& prp : mate.behaviorData.passiveRolePositions) - passiveRolePositions[mate.number].emplace_back(BehaviorData::PassiveRolePosition(prp.role, prp.position)); - robotPoses[mate.number] = mate.pose; - } + const bool mateIsKeeper = mate.playerNumber == 1; + const bool mateIsBallchaserInReady = inReady && mate.playerNumber == theBallChaserDecision.playerNumberToBall; + if (!mateIsKeeper && !mateIsBallchaserInReady && mate.status >= TeammateReceived::Status::ACTIVE) + playerNumbers.push_back(mate.playerNumber); + robotPoses[mate.playerNumber] = mate.robotPose; } } void RoleDynamicProvider::setStaticAssignment(RoleSymbols& roleSymbols) { - float ballDistance = theBallSymbols.ballPositionRelative.norm(); - bool teammateNearBall = false; - for (const auto& robot : theRobotMap.robots) + // fill players from lowest number to highest + addRoles(roleSymbols); + + robotPoses.fill(Pose2f()); + + auto role = roles.begin(); + for (unsigned char player = 1; player <= MAX_NUM_PLAYERS; ++player) { - if (robot.robotType == RobotEstimate::RobotType::teammateRobot && (robot.pose.translation - theBallSymbols.ballPositionField).norm() < 750.f) - teammateNearBall = true; + if (theRoleSelection.selectedRoles.size() != MAX_NUM_PLAYERS && theOwnTeamInfo.players[player - 1].penalty != PENALTY_NONE) + continue; + + if (roleSymbols.roleSuggestions[player] == BehaviorData::RoleAssignment::noRole) + { + ASSERT(role != roles.end()); + roleSymbols.roleSuggestions[player] = *role; + ++role; + } } - float maxBallDistance = (theRobotInfo.number == 5) ? 3000.f : 2000.f; - if (theRobotInfo.number == 1) - roleSymbols.role = BehaviorData::RoleAssignment::keeper; - else if ((theGameInfo.state == STATE_PLAYING && !teammateNearBall && ballDistance < ((roleSymbols.role == BehaviorData::RoleAssignment::ballchaser) ? (maxBallDistance + 500.f) : maxBallDistance)) - || (theGameInfo.state != STATE_PLAYING && theRobotInfo.number == 4)) - roleSymbols.role = BehaviorData::RoleAssignment::ballchaser; - else if (theRobotInfo.number == 2) - roleSymbols.role = BehaviorData::RoleAssignment::defenderLeft; - else if (theRobotInfo.number == 3) - roleSymbols.role = BehaviorData::RoleAssignment::defenderRight; - else if (theRobotInfo.number == 4) - roleSymbols.role = BehaviorData::RoleAssignment::center; - else - roleSymbols.role = BehaviorData::RoleAssignment::receiver; + if (role != roles.end()) + { + OUTPUT_WARNING("RoleDynamicProvider: There a more roles available than players for static assignment!"); + } + + roleSymbols.role = roleSymbols.roleSuggestions[theRobotInfo.number]; } void RoleDynamicProvider::addRoles(RoleSymbols& roleSymbols) { roles.clear(); + const bool inReady = theGameInfo.state == STATE_INITIAL || theGameInfo.state == STATE_READY; + const BehaviorData::RoleAssignment ballchaserRole = theGameInfo.kickingTeam == theOwnTeamInfo.teamNumber ? theRoleSelection.ballchaserDuringOwnKickoff : theRoleSelection.ballchaserDuringOppKickoff; for (const auto& role : theRoleSelection.selectedRoles) { - // keeper and ballchaser are only roles not dynamically assigned + // keeper is only role not dynamically assigned if (role == BehaviorData::keeper) roleSymbols.roleSuggestions[1] = role; - else if (role == BehaviorData::ballchaser && theBallChaserDecision.playerNumberToBall != 1) - roleSymbols.roleSuggestions[theBallChaserDecision.playerNumberToBall] = BehaviorData::ballchaser; + else if (inReady && role == ballchaserRole && theBallChaserDecision.playerNumberToBall != 1) + roleSymbols.roleSuggestions[theBallChaserDecision.playerNumberToBall] = role; else roles.push_back(role); } @@ -185,9 +170,9 @@ void RoleDynamicProvider::addRoles(RoleSymbols& roleSymbols) void RoleDynamicProvider::findBestRoleAssignment(RoleSymbols& roleSymbols) { - int playerCount = (int)roles.size(); + int playerCount = static_cast(roles.size()); if (playerCount < 1) - return; // nothing to do; i.e. only keeper and ballchaser were assigned + return; // nothing to do; i.e. only keeper was assigned if (playerNumbers.size() != roles.size()) { OUTPUT_ERROR("RoleDynamicProvider: role number (" << playerNumbers.size() << ") and player count (" << playerCount << ") different!"); @@ -204,15 +189,13 @@ void RoleDynamicProvider::findBestRoleAssignment(RoleSymbols& roleSymbols) // get next permutation of possible player to position assignments std::next_permutation(playerNumbers.begin(), playerNumbers.end()); - std::vector currentWalkDistances; // walking distances for this assignment - for (int p = 0; p < playerCount; p++) - currentWalkDistances.push_back(0.f); + std::vector currentWalkDistances(playerCount, 0.f); // walking distances for this assignment for (int j = 0; j < playerCount; j++) { - Vector2f optPosition = robotPoses[j].translation; - getPositionForRoleAndPlayer(roles[j], playerNumbers[j], optPosition); + Vector2f optPosition = rolePositions[roles[j]]; currentWalkDistances.at(j) = (robotPoses[playerNumbers[j]].translation - optPosition).norm(); } + std::sort(currentWalkDistances.begin(), currentWalkDistances.end(), std::greater<>()); for (int p = 0; p < playerCount; p++) { @@ -228,78 +211,123 @@ void RoleDynamicProvider::findBestRoleAssignment(RoleSymbols& roleSymbols) for (size_t i = 0; i < roles.size(); i++) roleSymbols.roleSuggestions[optAssignment[i]] = roles[i]; - if (theRobotInfo.number == theBallChaserDecision.playerNumberToBall) - { - DEBUG_DRAWING3D("representation:RoleAssignment", "field") - { - for (size_t i = 0; i < roles.size(); i++) - { - Vector2f rolePassivePosition = robotPoses[i].translation; - const int playerNum = optAssignment[i]; - const float drawOffset = 50.f; //add an small offset for drawing, otherwise the yellow path lines will may be visible - getPositionForRoleAndPlayer(roles[i], playerNum, rolePassivePosition); - LINE3D("representation:RoleAssignment", - robotPoses[playerNum].translation.x() + drawOffset, - robotPoses[playerNum].translation.y() + drawOffset, - 1.f, - rolePassivePosition.x() + drawOffset, - rolePassivePosition.y() + drawOffset, - 1.f, - 2.f, - ColorRGBA::red); - Vector2f diff = Vector2f(rolePassivePosition.x() - robotPoses[playerNum].translation.x(), rolePassivePosition.y() - robotPoses[playerNum].translation.y()).normalized(); - Vector3f from = Vector3f(rolePassivePosition.x() + drawOffset - 50 * diff.x(), rolePassivePosition.y() + drawOffset - 50 * diff.y(), 2.f); - Vector3f to = Vector3f(rolePassivePosition.x() + drawOffset + 50 * diff.x(), rolePassivePosition.y() + drawOffset + 50 * diff.y(), 1.f); - CYLINDERARROW3D("representation:RoleAssignment", from, to, 2.f, 90.f, 25.f, ColorRGBA::red); - } - } - } + compareToLastAssignment(roleSymbols, bestWalkDistances); } -void RoleDynamicProvider::addMyPassiveRolePositions() +void RoleDynamicProvider::updateRolePositions() { - passiveRolePositions[theRobotInfo.number].emplace_back(BehaviorData::PassiveRolePosition(BehaviorData::RoleAssignment::backupBallchaser, theBackupBallchaser.optPosition.translation)); - passiveRolePositions[theRobotInfo.number].emplace_back(BehaviorData::PassiveRolePosition(BehaviorData::RoleAssignment::center, theCenter.optPosition.translation)); - passiveRolePositions[theRobotInfo.number].emplace_back(BehaviorData::PassiveRolePosition(BehaviorData::RoleAssignment::defenderLeft, theDefenderLeft.optPosition.translation)); - passiveRolePositions[theRobotInfo.number].emplace_back(BehaviorData::PassiveRolePosition(BehaviorData::RoleAssignment::defenderRight, theDefenderRight.optPosition.translation)); - passiveRolePositions[theRobotInfo.number].emplace_back(BehaviorData::PassiveRolePosition(BehaviorData::RoleAssignment::defenderSingle, theDefenderSingle.optPosition.translation)); - passiveRolePositions[theRobotInfo.number].emplace_back(BehaviorData::PassiveRolePosition(BehaviorData::RoleAssignment::receiver, theReceiver.optPosition.translation)); + rolePositions[BehaviorData::RoleAssignment::keeper] = theKeeper.optPosition.translation; + rolePositions[BehaviorData::RoleAssignment::defenderRight] = theDefenderRight.optPosition.translation; + rolePositions[BehaviorData::RoleAssignment::defenderLeft] = theDefenderLeft.optPosition.translation; + rolePositions[BehaviorData::RoleAssignment::defenderSingle] = theDefenderSingle.optPosition.translation; + rolePositions[BehaviorData::RoleAssignment::backupBallchaser] = theBackupBallchaser.optPosition.translation; + rolePositions[BehaviorData::RoleAssignment::replacementKeeper] = theReplacementKeeper.optPosition.translation; + rolePositions[BehaviorData::RoleAssignment::center] = theCenter.optPosition.translation; + rolePositions[BehaviorData::RoleAssignment::receiver] = theReceiver.optPosition.translation; + rolePositions[BehaviorData::RoleAssignment::leftWing] = theLeftWing.optPosition.translation; + rolePositions[BehaviorData::RoleAssignment::rightWing] = theRightWing.optPosition.translation; + rolePositions[BehaviorData::RoleAssignment::frontWing] = theFrontWing.optPosition.translation; + rolePositions[BehaviorData::RoleAssignment::backWing] = theBackWing.optPosition.translation; + static_assert(BehaviorData::RoleAssignment::numOfRoleAssignments == 13, "Missing role!"); } -void RoleDynamicProvider::getPositionForRoleAndPlayer(const BehaviorData::RoleAssignment role, const int playerNumber, Vector2f& position) +void RoleDynamicProvider::takeNewestRoleAssignment(RoleSymbols& roleSymbols) { - for (size_t i = 0; i < passiveRolePositions[playerNumber].size(); i++) - if (passiveRolePositions[playerNumber][i].role == role) - { - position = passiveRolePositions[playerNumber][i].position.cast(); - return; - } + const Teammate* teammate = theTeammateData.getNewestEventMessage(TeamCommEvents::SendReason::newBallchaser); + + const bool isOrWasStriker = theBallChaserDecision.playerNumberToBall == theRobotInfo.number || (teammate && teammate->behaviorData.playerNumberToBall == theRobotInfo.number); + + const Teammate* teammateRoles = theTeammateData.getNewestEventMessage(TeamCommEvents::SendReason::newRolesAssigned); + if (isOrWasStriker || !teammate || !teammateRoles) + { + if (teammateRoles) + roleSymbols.role = teammateRoles->behaviorData.roleSuggestions[theRobotInfo.number]; + else + roleSymbols.role = roleSymbols.roleSuggestions[theRobotInfo.number]; + } + else + { + roleSymbols.role = teammateRoles->behaviorData.roleSuggestions[theRobotInfo.number]; + roleSymbols.roleSuggestions = teammateRoles->behaviorData.roleSuggestions; + } } -void RoleDynamicProvider::takeNewestRoleAssignment(RoleSymbols& roleSymbols) +void RoleDynamicProvider::compareToLastAssignment(RoleSymbols& roleSymbols, const std::vector& bestWalkDistances) { + // Always use the best assignment when in initial + if (theGameInfo.state == STATE_INITIAL) + return; + const Teammate* teammate = theTeammateData.getNewestEventMessage(TeamCommEvents::SendReason::newRolesAssigned); - if (teammate) + + if (!teammate) + return; + + const auto& currentSuggestions = teammate->behaviorData.roleSuggestions; + + // check if player count is still the same { - const auto& suggestions = teammate->behaviorData.roleSuggestions; - for (int playerNum = 0; playerNum < static_cast(suggestions.size()); ++playerNum) + const auto hasRole = [](const BehaviorData::RoleAssignment role) { - if (suggestions[playerNum] == BehaviorData::RoleAssignment::ballchaser) - lastPlayerNumberToBall = playerNum; - } + return role != BehaviorData::RoleAssignment::noRole; + }; + const size_t numberOfRoles = std::count_if(currentSuggestions.begin(), currentSuggestions.end(), hasRole); + + if (numberOfRoles != theRoleSelection.selectedRoles.size()) + return; } - const bool isOrWasStriker = theBallChaserDecision.playerNumberToBall == theRobotInfo.number || lastPlayerNumberToBall == theRobotInfo.number; - lastPlayerNumberToBall = theBallChaserDecision.playerNumberToBall; - if (isOrWasStriker || !teammate) + // check if the same roles are assigned { - roleSymbols.role = roleSymbols.roleSuggestions[theRobotInfo.number]; + const bool sameRoles = std::all_of(currentSuggestions.begin(), + currentSuggestions.end(), + [&](const BehaviorData::RoleAssignment role) + { + if (role == BehaviorData::RoleAssignment::noRole) + return true; + return std::find(theRoleSelection.selectedRoles.begin(), theRoleSelection.selectedRoles.end(), role) != theRoleSelection.selectedRoles.end(); + }); + + if (!sameRoles) + return; } - else + + // check if the same players are active { - roleSymbols.role = teammate->behaviorData.roleSuggestions[theRobotInfo.number]; - roleSymbols.roleSuggestions = teammate->behaviorData.roleSuggestions; + std::unordered_set playersToCheck; + for (const Teammate& mate : theTeammateData.teammates) + playersToCheck.insert(mate.playerNumber); + for (int player = 0; player < static_cast(currentSuggestions.size()); ++player) + if (currentSuggestions[player] != BehaviorData::RoleAssignment::noRole) + playersToCheck.erase(player); + + if (!playersToCheck.empty()) + return; } + + // check if the correct role is replaced by the ballchaser during ready + { + const bool inReady = (theGameInfo.state == STATE_INITIAL || theGameInfo.state == STATE_READY); + const BehaviorData::RoleAssignment ballchaserRole = theGameInfo.kickingTeam == theOwnTeamInfo.teamNumber ? theRoleSelection.ballchaserDuringOwnKickoff : theRoleSelection.ballchaserDuringOppKickoff; + if (inReady && currentSuggestions[theBallChaserDecision.playerNumberToBall] != ballchaserRole) + return; + } + + // check if best max distance is much better than current max distance + std::vector currentWalkDistances; + for (unsigned char p = 0; p < currentSuggestions.size(); ++p) + { + if (currentSuggestions[p] != BehaviorData::RoleAssignment::noRole && currentSuggestions[p] != BehaviorData::RoleAssignment::keeper) + currentWalkDistances.emplace_back((robotPoses[p].translation - rolePositions[currentSuggestions[p]]).norm()); + } + + const float currentMaxDistance = *std::max_element(currentWalkDistances.begin(), currentWalkDistances.end()); + + // do not care about distance in ready + if (!theTacticSymbols.keepRoleAssignment && currentMaxDistance > bestWalkDistances[0] + minDistanceDiffForNewRoleAssignment) + return; + + roleSymbols.roleSuggestions = currentSuggestions; } MAKE_MODULE(RoleDynamicProvider, behaviorControl) diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleDynamicProvider.h b/Src/Modules/BehaviorControl/TacticControl/RoleDynamicProvider.h index a1fafc40..22b02c75 100644 --- a/Src/Modules/BehaviorControl/TacticControl/RoleDynamicProvider.h +++ b/Src/Modules/BehaviorControl/TacticControl/RoleDynamicProvider.h @@ -21,6 +21,11 @@ #include "Representations/BehaviorControl/RoleSymbols/Receiver.h" #include "Representations/BehaviorControl/RoleSymbols/ReplacementKeeper.h" #include "Representations/BehaviorControl/RoleSymbols/Ballchaser.h" +#include "Representations/BehaviorControl/RoleSymbols/LeftWing.h" +#include "Representations/BehaviorControl/RoleSymbols/RightWing.h" +#include "Representations/BehaviorControl/RoleSymbols/FrontWing.h" +#include "Representations/BehaviorControl/RoleSymbols/BackWing.h" +#include "Representations/BehaviorControl/TacticSymbols.h" #include "Representations/Configuration/FieldDimensions.h" #include "Representations/Infrastructure/GameInfo.h" #include "Representations/Infrastructure/RobotInfo.h" @@ -51,6 +56,10 @@ MODULE(RoleDynamicProvider, REQUIRES(DefenderLeft), REQUIRES(DefenderRight), REQUIRES(DefenderSingle), + REQUIRES(LeftWing), + REQUIRES(RightWing), + REQUIRES(FrontWing), + REQUIRES(BackWing), REQUIRES(Keeper), REQUIRES(Receiver), REQUIRES(ReplacementKeeper), @@ -64,9 +73,11 @@ MODULE(RoleDynamicProvider, REQUIRES(RobotPoseAfterPreview), REQUIRES(FallDownState), REQUIRES(OwnTeamInfo), + REQUIRES(TacticSymbols), PROVIDES(RoleSymbols), - DEFINES_PARAMETERS(, - (bool)(true) useStaticAssignmentNoWifi + LOADS_PARAMETERS(, + (bool)(true) useStaticAssignmentNoWifi, + (float)(1000.f) minDistanceDiffForNewRoleAssignment ) ); @@ -78,17 +89,7 @@ class RoleDynamicProvider : public RoleDynamicProviderBase { public: /** Constructor */ - RoleDynamicProvider() - { - passiveRolePositions.clear(); - robotPoses.clear(); - for (int i = 0; i <= MAX_NUM_PLAYERS; i++) - { - BehaviorData::PassiveRolePositionVector prpv; - passiveRolePositions.push_back(prpv); - robotPoses.emplace_back(Pose2f()); - } - } + RoleDynamicProvider() { rolePositions.fill(Vector2f::Zero()); } private: /** Updates some of the symbols */ @@ -97,28 +98,26 @@ class RoleDynamicProvider : public RoleDynamicProviderBase /** Find the player numbers and current positions of the active players in the team. */ void getCurrentTeamStatus(); - /** Declare drawing for the role selection in the simulator. */ - void declareDebugDrawing(RoleSymbols& roleSymbols); - /** * @brief In case of no wifi, static assignment except for ball chaser * @param roleSymbols */ void setStaticAssignment(RoleSymbols& roleSymbols); + bool roleChangeNeccessary(RoleSymbols& roleSymbols); + void addRoles(RoleSymbols& roleSymbols); void findBestRoleAssignment(RoleSymbols& roleSymbols); - void addMyPassiveRolePositions(); - - void getPositionForRoleAndPlayer(const BehaviorData::RoleAssignment role, const int playerNumber, Vector2f& position); + void compareToLastAssignment(RoleSymbols& roleSymbols, const std::vector& bestWalkDistances); void takeNewestRoleAssignment(RoleSymbols& roleSymbols); + void updateRolePositions(); + std::vector roles; // roles that should be assigned in role selection - std::vector passiveRolePositions; // collecting all passive role suggestions + std::array rolePositions; std::vector playerNumbers; // all player numbers in the pool for role assignment - std::vector robotPoses; // all player positions - int lastPlayerNumberToBall = 0; + std::array robotPoses; // all player positions }; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/BackWingProvider.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/BackWingProvider.cpp new file mode 100644 index 00000000..b35b9bd1 --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/BackWingProvider.cpp @@ -0,0 +1,111 @@ +#include "BackWingProvider.h" +#include "Tools/Debugging/DebugDrawings3D.h" +#include "Utils/PositionUtils.h" +#include "Utils/ThresholdUtils.h" +#include "Utils/MathUtils.h" + +void BackWingProvider::update(BackWing& role) +{ + role.stopAtTarget = true; + + decide(role, theBallSymbols, theFieldDimensions, theGameInfo, theGameSymbols); +} + +void BackWingProvider::stateReady_kickOff_own(BackWing& role, const Vector2f& ballPosition) +{ + regularPlay(role); +} + +void BackWingProvider::stateReady_kickOff_opponent(BackWing& role, const Vector2f& ballPosition) +{ + ThresholdUtils::setThreshholdsHeigh(role); + const float x = -theFieldDimensions.xPosOpponentPenaltyArea / 1.2f; + const float y = 500.f; + PositionUtils::setPosition(role, x, y); + PositionUtils::turnTowardsBall(role, theBallSymbols); +} + +float BackWingProvider::goalKick_own(BackWing& role, bool left) +{ + regularPlay(role); + return 0.f; +} + +float BackWingProvider::goalKick_opponent(BackWing& role, bool left) +{ + regularPlay(role); + return 0; +} + +float BackWingProvider::pushingFreeKick_own(BackWing& role) +{ + regularPlay(role); + return 0; +} + +float BackWingProvider::pushingFreeKick_opponent(BackWing& role) +{ + regularPlay(role); + return 0; +} + +float BackWingProvider::cornerKick_own(BackWing& role, const Vector2f& cornerKickPosition, bool left) +{ + regularPlay(role); + return 0; +} + +float BackWingProvider::cornerKick_opponent(BackWing& role, const Vector2f& cornerKickPosition, bool left) +{ + regularPlay(role); + return 0; +} + +float BackWingProvider::kickIn_own(BackWing& role, bool left) +{ + regularPlay(role); + return 0; +} + +float BackWingProvider::kickIn_opponent(BackWing& role, bool left) +{ + regularPlay(role); + return 0; +} + +float BackWingProvider::stateReady_penaltyKick_own(BackWing& role) +{ + regularPlay(role); + return 0; +} + +float BackWingProvider::stateReady_penaltyKick_opponent(BackWing& role) +{ + bool robotIsLeft = (theRobotPose.translation.y() >= 0.f); + float targetPosY = (robotIsLeft ? 1.f : -1.f) * (theFieldDimensions.yPosLeftPenaltyArea + 400.f); + + role.optPosition.translation.x() = theFieldDimensions.xPosOwnPenaltyArea - theFieldDimensions.xPosOwnPenaltyArea / 2; + role.optPosition.translation.y() = targetPosY; + PositionUtils::turnTowardsBall(role, theBallSymbols); + + role.thresholdRotation = 10_deg; + role.thresholdXBack = 50.f; + role.thresholdXFront = 100.f; + role.thresholdY = 100.f; + role.stopAtTarget = true; + role.previewArrival = true; + return 0; +} + +void BackWingProvider::regularPlay(BackWing& role) +{ + ThresholdUtils::setThreshholdsExtremeHeigh(role, theTacticSymbols.activity); + + PositionUtils::turnTowardsBall(role, theBallSymbols); + + const float x = MathUtils::clamp_f(theBallSymbols.ballPositionField.x() - 2000.f, theFieldDimensions.xPosOwnPenaltyArea, theFieldDimensions.xPosOpponentPenaltyArea / 2); + const float y = 500.f; + PositionUtils::setPosition(role, x, y); +} + +MAKE_MODULE(BackWingProvider, behaviorControl) diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/BackWingProvider.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/BackWingProvider.h new file mode 100644 index 00000000..8266446d --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/BackWingProvider.h @@ -0,0 +1,76 @@ +#pragma once + +#include +#include "Tools/Module/Module.h" +#include "Modules/BehaviorControl/CABSL/BehaviorParameters.h" +#include "Representations/Configuration/FieldDimensions.h" +#include "Representations/BehaviorControl/BallSymbols.h" +#include "Representations/BehaviorControl/GameSymbols.h" +#include "Representations/BehaviorControl/GoalSymbols.h" +#include "Representations/BehaviorControl/RoleSelection.h" +#include "Representations/BehaviorControl/BallChaserDecision.h" +#include "Representations/BehaviorControl/BehaviorConfiguration.h" +#include "Representations/BehaviorControl/RoleSymbols/BackWing.h" +#include "Representations/BehaviorControl/RoleSymbols/Receiver.h" +#include "Representations/BehaviorControl/RoleSymbols/Ballchaser.h" +#include "Representations/BehaviorControl/RoleSymbols/BallchaserKeeper.h" +#include "Representations/BehaviorControl/RoleSymbols/PositioningSymbols.h" +#include "Representations/BehaviorControl/TacticSymbols.h" +#include "Representations/Infrastructure/FrameInfo.h" +#include "Representations/Infrastructure/GameInfo.h" +#include "Representations/Infrastructure/RobotInfo.h" +#include "Representations/Infrastructure/TeamInfo.h" +#include "Representations/Infrastructure/TeammateData.h" +#include "Representations/BehaviorControl/BehaviorData.h" +#include "Representations/Modeling/DangerMap.h" +#include "Representations/Modeling/RobotMap.h" +#include "Representations/Modeling/RobotPose.h" +#include "Representations/MotionControl/WalkingEngineParams.h" +#include "Tools/Settings.h" +#include "RoleProvider.h" + +MODULE(BackWingProvider, + REQUIRES(BallSymbols), + REQUIRES(BehaviorConfiguration), + REQUIRES(DangerMap), + REQUIRES(FrameInfo), + REQUIRES(FieldDimensions), + REQUIRES(GameInfo), + REQUIRES(GameSymbols), + REQUIRES(GoalSymbols), + REQUIRES(OwnTeamInfo), + REQUIRES(OpponentTeamInfo), + REQUIRES(RobotInfo), + REQUIRES(RobotMap), + REQUIRES(RobotPose), + REQUIRES(RobotPoseAfterPreview), + REQUIRES(RoleSelection), + REQUIRES(Receiver), + REQUIRES(TeammateData), + REQUIRES(TacticSymbols), + REQUIRES(WalkingEngineParams), + USES(PositioningSymbols), + PROVIDES(BackWing) +); + +class BackWingProvider : public BackWingProviderBase, public RoleProvider +{ + +public: + void update(BackWing& role) override; + +private: + void stateReady_kickOff_own(BackWing& role, const Vector2f& ballPosition) override; + void stateReady_kickOff_opponent(BackWing& role, const Vector2f& ballPosition) override; + float goalKick_own(BackWing& role, bool left) override; + float goalKick_opponent(BackWing& role, bool left) override; + float pushingFreeKick_own(BackWing& role) override; + float pushingFreeKick_opponent(BackWing& role) override; + float cornerKick_own(BackWing& role, const Vector2f& cornerKickPosition, bool left) override; + float cornerKick_opponent(BackWing& role, const Vector2f& cornerKickPosition, bool left) override; + float kickIn_own(BackWing& role, bool left) override; + float kickIn_opponent(BackWing& role, bool left) override; + float stateReady_penaltyKick_own(BackWing& role) override; + float stateReady_penaltyKick_opponent(BackWing& role) override; + void regularPlay(BackWing& role) override; +}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/BallchaserProvider.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/BallchaserProvider.cpp new file mode 100644 index 00000000..fe7acd14 --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/BallchaserProvider.cpp @@ -0,0 +1,467 @@ +#include "BallchaserProvider.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +BallchaserProvider::BallchaserProvider() : regularPlayObjectivesManager(&logger) +{ + init(); +} + +void BallchaserProvider::init() +{ + regularPlayObjectivesManager.clear(); + regularPlayObjectivesManager.add(std::make_unique(this, logger)); + // Inner lists can always be left so the GoalObjective will always be tried, even if the OneVsOneObjective forbids to leave + std::unique_ptr> objectiveListPointer = std::make_unique>(&logger); + //objectiveListPointer->add(std::make_unique(this, logger)); + objectiveListPointer->add(std::make_unique(this, logger)); + regularPlayObjectivesManager.add(std::move(objectiveListPointer)); + + setPlayKickManager = {}; + + auto kickEngineParameters = KicksProvider::loadKickEngineParameters(); + auto customStepFiles = KicksProvider::loadCustomStepFiles(); + kickOffKicks = KicksProvider::createKicks(kickEngineParameters, customStepFiles, kickOffKickNames); + kickInKicks = KicksProvider::createKicks(kickEngineParameters, customStepFiles, kickInKickNames); + cornerKicks = KicksProvider::createKicks(kickEngineParameters, customStepFiles, cornerKickNames); + goalKicks = KicksProvider::createKicks(kickEngineParameters, customStepFiles, goalKickNames); + penaltyKicks = KicksProvider::createKicks(kickEngineParameters, customStepFiles, penaltyKickNames); + pushingFreeKicks = KicksProvider::createKicks(kickEngineParameters, customStepFiles, pushingFreeKickNames); +} + +void BallchaserProvider::update(Ballchaser& ballchaser) +{ + DECLARE_DEBUG_DRAWING(DRAW_KICK_RANGE_NAME, "drawingOnField"); + DECLARE_DEBUG_DRAWING(DRAW_KICK_DANGER_NAME, "drawingOnField"); + DECLARE_DEBUG_DRAWING(DRAW_EXECUTABLE_KICKS_IN_GRID, "drawingOnField"); + DECLARE_DEBUG_DRAWING(DRAW_EXECUTABLE_KICKS_FREELY, "drawingOnField"); + DECLARE_DEBUG_DRAWING(DRAW_EXECUTABLE_KICK_TARGET_AREA, "drawingOnField"); + DECLARE_DEBUG_DRAWING(DRAW_KICK_MIN_WIDTH, "drawingOnField"); + DECLARE_DEBUG_DRAWING(DRAW_TEST, "drawingOnField"); + + DEBUG_RESPONSE_ONCE("module:BallchaserProvider:default") // Command: "dr module:BallchaserProvider:default" + { + testBehavior = false; + init(); + } + std::string kickToCenterTestKickName; + MODIFY_ONCE("module:BallchaserProvider:kickToCenterTest", kickToCenterTestKickName); // Command: "set module:BallchaserProvider:kickToCenterTest kickHack" + if (!kickToCenterTestKickName.empty()) + { + testBehavior = true; + regularPlayObjectivesManager.clear(); + regularPlayObjectivesManager.add(std::make_unique(this, logger, kickToCenterTestKickName)); + } + std::string kickBallTestKickName; + MODIFY_ONCE("module:BallchaserProvider:kickBallTest", kickBallTestKickName); // Example command: "set module:BallchaserProvider:kickBallTest kickHack" + if (!kickBallTestKickName.empty()) + { + testBehavior = true; + regularPlayObjectivesManager.clear(); + regularPlayObjectivesManager.add(std::make_unique(this, logger, kickBallTestKickName)); + } + std::string kickInPositionTestKickName; + MODIFY_ONCE("module:BallchaserProvider:kickInPositionTest", kickInPositionTestKickName); // Example command: "set module:BallchaserProvider:kickInPositionTest kickHack" + if (!kickInPositionTestKickName.empty()) + { + testBehavior = true; + regularPlayObjectivesManager.clear(); + regularPlayObjectivesManager.add(std::make_unique(this, logger, kickInPositionTestKickName)); + } + + // Reset + ballchaser.kickType = MotionRequest::KickType::walkKick; + ballchaser.walkKickType = WalkRequest::StepRequest::none; + // Reset Logs + ballchaser.log_currState = "None"; + ballchaser.log_currObj = "None"; + ballchaser.log_danger = "None"; + ballchaser.log_kickName = "None"; + logger.clear(); + + // Update + updateVariables(ballchaser); + + // Set + ballchaser.stopAtTarget = theGameInfo.state != STATE_PLAYING; + ballchaser.previewArrival = previewArrival; + if (testBehavior) + { + regularPlay(ballchaser); + } + else + { + decide(ballchaser, theBallSymbols, theFieldDimensions, theGameInfo, theGameSymbols); + } + // Set Logs + ballchaser.log_toBallDistance = std::to_string(Geometry::distance(theRobotPoseAfterPreview.translation, ballPosition)); + ballchaser.log_obj1 = logger.get(0); + ballchaser.log_obj2 = logger.get(1); + ballchaser.log_obj3 = logger.get(2); + ballchaser.log_obj4 = logger.get(3); + ballchaser.log_obj5 = logger.get(4); +} + +void BallchaserProvider::stateReady_kickOff_own(Ballchaser& positioningSymbols, const Vector2f& kickOffPosition) +{ + setPlayKickManager.stop(); + + danger = Danger::IMPOSSIBLE; + positioningSymbols.log_danger = "None"; + + PositionUtils::setPosition(positioningSymbols, -500.f, 0); + PositionUtils::turnToPosition(positioningSymbols, kickOffPosition); + ThresholdUtils::setThreshholdsLow(positioningSymbols); +} + +void BallchaserProvider::statePlaying_kickOff_own(Ballchaser& positioningSymbols, const Vector2f& kickOffPosition) +{ + danger = Danger::IMPOSSIBLE; + positioningSymbols.log_danger = "None"; + + const Filterer filterer = Filterer().filterTooFarBack(0.f).filterOutside().filterBlocked(); + const Factors factors = {0.3f, + 0.f, + 1.f, // Field + 0.01f, + 0.3f, // Team + -0.01f, + -0.3f, // Opponent + 0.f, + 0.f, + 0.f, // Pose + 0.f, + 0.f}; // Kick + const std::optional currentKick = setPlayKickManager.getCurrentKick(ballPosition); + + auto selectedKickOptional = SelectFunctions::createAndFilterAndSelect( + theRobotPoseAfterPreview, ballPosition, KickUtils::unpack(kickOffKicks), currentKick, filterer, factors, theFieldDimensions, theHeatMapCollection, theKickWheel, theRobotMap, theTacticSymbols); + + if (selectedKickOptional.has_value()) + { + setPlayKickManager.kickTo(positioningSymbols, selectedKickOptional.value(), theFrameInfo); + } + else + { + // TODO Warning only if ballchaser if (theRoleSymbols.role == Ballchaser) + //OUTPUT_WARNING("KickOff not possible!"); + } +} + +void BallchaserProvider::stateReady_kickOff_opponent(Ballchaser& positioningSymbols, const Vector2f& kickOffPosition) +{ + positioningSymbols.optPosition.translation.x() = -theFieldDimensions.centerCircleRadius - theBehaviorConfiguration.behaviorParameters.kickOffLineDistance; + positioningSymbols.optPosition.translation.y() = 0; + positioningSymbols.optPosition.rotation = 0; + positioningSymbols.thresholdXFront = 50; + positioningSymbols.thresholdXBack = 50; + positioningSymbols.thresholdY = 25; +} + +float BallchaserProvider::goalKick_own(Ballchaser& positioningSymbols, bool left) +{ + danger = Danger::IMPOSSIBLE; + positioningSymbols.log_danger = "None"; + + const Vector2f waitPos = Vector2f(theFieldDimensions.xPosOwnGoalArea - 300.f, left ? theFieldDimensions.yPosLeftGoalArea : theFieldDimensions.yPosRightGoalArea); + const float minX = theFieldDimensions.xPosOwnGoalArea + 300.f; + actInOwnSetPlay(positioningSymbols, waitPos, minX, 0.5f, KickUtils::unpack(goalKicks)); + return 0.f; +} + +float BallchaserProvider::goalKick_opponent(Ballchaser& positioningSymbols, bool left) +{ + defendOwnGoal(positioningSymbols, left); + return 0.f; +} + +float BallchaserProvider::pushingFreeKick_own(Ballchaser& positioningSymbols) +{ + danger = Danger::IMPOSSIBLE; + positioningSymbols.log_danger = "None"; + + const Vector2f opponentGoalCenter = FieldUtils::getOpponentGoalCenter(theFieldDimensions); + const Vector2f ballToOpponentGoalCenter = (opponentGoalCenter - ballPosition).normalized(); + const Vector2f waitPosition = ballPosition - 300 * ballToOpponentGoalCenter; + const float minX = MathUtils::clamp_f(ballPosition.x(), theFieldDimensions.xPosOwnPenaltyArea + 200.f, theFieldDimensions.xPosOpponentGoalArea - 200.f); + actInOwnSetPlay(positioningSymbols, waitPosition, minX, 0.3f, KickUtils::unpack(kickInKicks)); + return 0.f; +} + +float BallchaserProvider::pushingFreeKick_opponent(Ballchaser& positioningSymbols) +{ + defendOwnGoal(positioningSymbols, ballOnLeftSide); + return 0.f; +} + +float BallchaserProvider::cornerKick_own(Ballchaser& positioningSymbols, const Vector2f& cornerKickPosition, bool left) +{ + danger = Danger::IMPOSSIBLE; + positioningSymbols.log_danger = "None"; + + Vector2f waitPos = Vector2f(theFieldDimensions.xPosOpponentGroundline + 200.f, left ? theFieldDimensions.yPosLeftSideline + 200.f : theFieldDimensions.yPosRightSideline - 200.f); + const float minX = theFieldDimensions.xPosOpponentPenaltyArea - 1000.f; + actInOwnSetPlay(positioningSymbols, waitPos, minX, 0.2f, KickUtils::unpack(cornerKicks)); + return 0.f; +} + +float BallchaserProvider::cornerKick_opponent(Ballchaser& positioningSymbols, const Vector2f& cornerKickPosition, bool left) +{ + actInOpponentsSetPlay(positioningSymbols, + left, + [this](const Vector2f& robotPosition) + { + return (abs(robotPosition.y()) >= abs(theFieldDimensions.yPosLeftSideline) + 60.f && robotPosition.x() < theFieldDimensions.xPosOwnGroundline + 20.f); + }); + return 0.f; +} + +float BallchaserProvider::kickIn_own(Ballchaser& positioningSymbols, bool left) //TODO: Wait for 10 seconds if time permits before shooting. Longer if we are winning. RoleProvider. 1vs1. +{ + danger = Danger::IMPOSSIBLE; + positioningSymbols.log_danger = "None"; + + Vector2f waitPos = Vector2f(theBallSymbols.ballPositionField.x(), left ? theFieldDimensions.yPosLeftSideline + 300.f : theFieldDimensions.yPosRightSideline - 300.f); + const float minX = MathUtils::clamp_f(ballPosition.x() - 500.f, theFieldDimensions.xPosOwnPenaltyArea + 200.f, theFieldDimensions.xPosOpponentGoalArea - 200.f); + actInOwnSetPlay(positioningSymbols, waitPos, minX, 0.3f, KickUtils::unpack(kickInKicks)); + return 0.f; +} + +float BallchaserProvider::kickIn_opponent(Ballchaser& positioningSymbols, bool left) +{ + actInOpponentsSetPlay(positioningSymbols, + left, + [this](const Vector2f& robotPosition) + { + return abs(robotPosition.y()) >= abs(theFieldDimensions.yPosLeftSideline) - 20.f; + }); + return 0.f; +} + +float BallchaserProvider::stateReady_penaltyKick_own(Ballchaser& ballchaser) +{ + setPlayKickManager.stop(); + + danger = Danger::IMPOSSIBLE; + ballchaser.log_danger = "None"; + + const Vector2f penaltyMarkPosition = {theFieldDimensions.xPosOpponentPenaltyMark, 0.f}; // TODo put Vector into theFieldDimensions + const Vector2f position = {penaltyMarkPosition.x() - 500.f, penaltyMarkPosition.y()}; + ThresholdUtils::setThreshholdsMedium(ballchaser); + PositionUtils::setPosition(ballchaser, position); + PositionUtils::turnToPosition(ballchaser, position); + return 0.f; +} + +float BallchaserProvider::statePlaying_penaltyKick_own(Ballchaser& ballchaser) +{ + danger = Danger::IMPOSSIBLE; + ballchaser.log_danger = "None"; + + const Vector2f leftGoalPostPosition = {theFieldDimensions.xPosOpponentGroundline, theFieldDimensions.yPosLeftGoal - theFieldDimensions.goalPostRadius}; + const Vector2f rightGoalPostPosition = {theFieldDimensions.xPosOpponentGroundline, theFieldDimensions.yPosRightGoal + theFieldDimensions.goalPostRadius}; + + const KickLine kickLine = {ballPosition, leftGoalPostPosition, rightGoalPostPosition, DistanceRequirement::mayShorterOrFurther, 0.f}; // only use kicks that should go far enough + Filterer filterer = Filterer().filterOutsideKickRange(kickLine).filterBlocked(); + const Factors factors = {1.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; + const auto currentKick = setPlayKickManager.getCurrentKick(ballPosition); + + const auto selectableKick = SelectFunctions::createAndFilterAndSelect( + theRobotPoseAfterPreview, ballPosition, KickUtils::unpack(penaltyKicks), currentKick, filterer, factors, theFieldDimensions, theHeatMapCollection, theKickWheel, theRobotMap, theTacticSymbols); + + if (selectableKick.has_value()) + { + setPlayKickManager.kickTo(ballchaser, selectableKick.value(), theFrameInfo); + } + else + { + // TODO Warning only if Ballchaser + } + return 0.f; +} + +float BallchaserProvider::stateReady_penaltyKick_opponent(Ballchaser& positioningSymbols) +{ + positioningSymbols.optPosition.translation.x() = theFieldDimensions.xPosOwnPenaltyArea + 300.f; + positioningSymbols.optPosition.translation.y() = theFieldDimensions.yPosKickOffPoint + 800.f; + const Vector2f penaltyCross{theFieldDimensions.xPosOwnPenaltyMark, theFieldDimensions.yPosKickOffPoint}; + positioningSymbols.optPosition.rotation = (penaltyCross - positioningSymbols.optPosition.translation).angle(); + return 0.f; +} + +void BallchaserProvider::regularPlay(Ballchaser& positioningSymbols) +{ + regularPlayObjectivesManager.performObjective(positioningSymbols); +} + +// Helper ========================================================================================================================================================================== + +bool BallchaserProvider::waitInOwnSetPlay(Ballchaser& positioningSymbols, const Vector2f& waitingPosition) +{ + // TODO HeadControl: The Robot only looks around once + + const int SET_PLAY_DURATION = 30; // TODO Variable + const int MIN_REMAINING_TIME = 7; + const int MAX_LOOSING_WAIT_TIME = 10; // Should be enough time to look around at least once + const int MAX_EVEN_WAIT_TIME = 12; + const float WAIT_POSE_DISTANCE_THRESHOLD = 100.f; + + const int remainingTime = SET_PLAY_DURATION - theGameSymbols.timeSinceSetPlayStarted; + + if (remainingTime >= SET_PLAY_DURATION - 1) // Used to reset when new setPlay + { + startedSetPlayWaitingRemainingTime = 0; + } + + if (remainingTime < MIN_REMAINING_TIME) + { + return false; + } + + const int timePassedSinceStartWaiting = startedSetPlayWaitingRemainingTime - remainingTime; + if (theOwnTeamInfo.score < theOpponentTeamInfo.score && timePassedSinceStartWaiting > MAX_LOOSING_WAIT_TIME) + { + return false; + } + if (theOwnTeamInfo.score == theOpponentTeamInfo.score && timePassedSinceStartWaiting > MAX_EVEN_WAIT_TIME) + { + return false; + } + + if (startedSetPlayWaitingRemainingTime == 0 && Geometry::distance(theRobotPose.translation, waitingPosition) < WAIT_POSE_DISTANCE_THRESHOLD) + { + startedSetPlayWaitingRemainingTime = remainingTime; + } + + PositionUtils::setPosition(positioningSymbols, waitingPosition); + PositionUtils::turnTowardsBall(positioningSymbols, theBallSymbols); + positioningSymbols.stopAtTarget = true; + return true; +} + +void BallchaserProvider::actInOwnSetPlay(Ballchaser& ballchaser, const Vector2f& waitPosition, const float minX, const float goalsHeatFactor, const std::vector& kicks) +{ + if (waitInOwnSetPlay(ballchaser, waitPosition)) + { + return; + } + Filterer filterer = Filterer().filterTooFarBack(minX).filterOutside().filterBlocked(); + const Factors factors = {0.5f, 0.f, goalsHeatFactor, 0.1f, 1.f, -0.5f, -0.5f, 0.f, 0.f, 0.f, 0.f, 0.f}; + const auto currentKick = setPlayKickManager.getCurrentKick(ballPosition); + const auto selectableKick = SelectFunctions::createAndFilterAndSelect( + theRobotPoseAfterPreview, ballPosition, kicks, currentKick, filterer, factors, theFieldDimensions, theHeatMapCollection, theKickWheel, theRobotMap, theTacticSymbols); + if (selectableKick.has_value()) + { + setPlayKickManager.kickTo(ballchaser, selectableKick.value(), theFrameInfo); + } + else + { + // TODO Nur ausgeben wenn ballchaser + // OUTPUT_WARNING("Free kick not possible!"); + } +} + +void BallchaserProvider::actInOpponentsSetPlay(Ballchaser& ballchaser, const bool left, const std::function& isRobotPoseKickerPose) +{ + Vector2f kickerPose; + int numberOfRobotsDetected = 0; + for (const RobotMapEntry& robot : theRobotMap.robots) + { + const Vector2f robotPosition = robot.pose.translation; + if ((robotPosition - ballPosition).norm() < 400.f && isRobotPoseKickerPose(robotPosition)) + { + numberOfRobotsDetected++; + if (numberOfRobotsDetected > 1) + { + break; + } + kickerPose = robotPosition; + } + } + if (numberOfRobotsDetected == 1) + { + const Vector2f kickerToBall = ballPosition - kickerPose; + float distanceFromKickerToBall = (kickerToBall).norm(); + const Vector2f targetPos = ballPosition + kickerToBall * oppSetPlayBallDistance / distanceFromKickerToBall; + PositionUtils::setPosition(ballchaser, targetPos); + PositionUtils::turnTowardsBall(ballchaser, theBallSymbols); + } + else + { + defendOwnGoal(ballchaser, left); + } +} + +void BallchaserProvider::defendOwnGoal(Ballchaser& ballchaser, const bool left) +{ + const Vector2f targetToCover{theFieldDimensions.xPosOwnGroundline, + left ? (theFieldDimensions.yPosCenterGoal + theFieldDimensions.yPosRightGoal) / 2.f : (theFieldDimensions.yPosCenterGoal + theFieldDimensions.yPosLeftGoal) / 2.f}; + const Vector2f ballToOwnGoal{targetToCover - ballPosition}; + Vector2f position{ballPosition + ballToOwnGoal.normalized(oppSetPlayBallDistance)}; + position.x() = std::max(position.x(), theFieldDimensions.xPosOwnGroundline + 300.f); + + ballchaser.optPosition.translation = position; + ballchaser.optPosition.rotation = (ballPosition - position).angle(); +} + +// Update to avoid hysteresis ====================================================================================================================================================== + +void BallchaserProvider::updateVariables(Ballchaser& ballchaser) +{ + // Update ballPosition + ballPosition = BallUtils::getBallPosition(theBallSymbols, theFrameInfo, theRobotPoseAfterPreview); + + // Update ballOnLeftSide + if (ballOnLeftSide && ballPosition.y() < -250.f) + ballOnLeftSide = false; + else if (!ballOnLeftSide && ballPosition.y() > 250.f) + ballOnLeftSide = true; + + // Update danger + if (isDanger(450.f, danger == Danger::HIGH)) + { + ballchaser.log_danger = "High"; + danger = Danger::HIGH; + } + else if (isDanger(800.f, danger == Danger::MEDIUM)) + { + ballchaser.log_danger = "Medium"; + danger = Danger::MEDIUM; + } + else if (isDanger(1200.f, danger == Danger::LOW)) + { + ballchaser.log_danger = "Low"; + danger = Danger::LOW; + } + else + { + ballchaser.log_danger = "None"; + danger = Danger::NONE; + } +} + +bool BallchaserProvider::isDanger(float dangerDistance, const bool hysteresis) const +{ + dangerDistance = dangerDistance * (hysteresis ? 1.2f : 1.f); + for (const auto& robot : theRobotMap.robots) + { + if (robot.robotType != RobotEstimate::teammateRobot) + { + const float robotDistance = Geometry::distance(ballPosition, robot.pose.translation); + if (robotDistance < dangerDistance) + { + return true; + } + } + } + return false; +} + +MAKE_MODULE(BallchaserProvider, behaviorControl) diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/BallchaserProvider.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/BallchaserProvider.h similarity index 64% rename from Src/Modules/BehaviorControl/TacticControl/RoleProvider/BallchaserProvider.h rename to Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/BallchaserProvider.h index 4e52dbf8..7715ab03 100644 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/BallchaserProvider.h +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/BallchaserProvider.h @@ -3,110 +3,114 @@ * * Declaration of class BallchaserProvider. * Provides optimal kick position and kick for current situation if robot is the ball chaser. -* Uses only walking engine kicks and dribbling. * All positions are in world coordinates with a positioning angle in degrees (for CABSL). * Kick target is in world coordinates here (gets translated to relative in CABSL). -* */ #pragma once -#include "Tools/Module/Module.h" #include "Modules/BehaviorControl/CABSL/BehaviorParameters.h" -#include "Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/TestObjective.h" -#include "Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/GoalObjective.h" -#include "Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/MoveObjective.h" -#include "Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/OneVsOneObjective.h" -#include "Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Objective.h" -#include "Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/ObjectivesManager.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/GoalObjective.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/Test/KickBallTestObjective.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/MoveObjective.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/Test/KickToCenterTestObjective.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Enums/Danger.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Logs/BehaviorLogger.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/ObjectivesManager/Objective.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/ObjectivesManager/ObjectivesManager.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/RoleProvider.h" #include "Modules/BehaviorControl/TacticControl/RoleProvider/Utils/BallUtils.h" -#include -#include "Representations/Configuration/FieldDimensions.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Utils/PositionUtils.h" #include "Representations/BehaviorControl/BallSymbols.h" +#include "Representations/BehaviorControl/BehaviorConfiguration.h" +#include "Representations/BehaviorControl/BehaviorData.h" #include "Representations/BehaviorControl/GameSymbols.h" #include "Representations/BehaviorControl/GoalSymbols.h" #include "Representations/BehaviorControl/RoleSelection.h" -#include "Representations/BehaviorControl/BehaviorConfiguration.h" #include "Representations/BehaviorControl/RoleSymbols/Ballchaser.h" +#include "Representations/BehaviorControl/RoleSymbols/PositioningSymbols.h" #include "Representations/BehaviorControl/RoleSymbols/Receiver.h" -#include "Representations/BehaviorControl/PositioningSymbols.h" #include "Representations/BehaviorControl/TacticSymbols.h" +#include "Representations/Configuration/FieldDimensions.h" #include "Representations/Infrastructure/FrameInfo.h" #include "Representations/Infrastructure/GameInfo.h" #include "Representations/Infrastructure/RobotInfo.h" #include "Representations/Infrastructure/TeamInfo.h" #include "Representations/Infrastructure/TeammateData.h" -#include "Representations/BehaviorControl/BehaviorData.h" #include "Representations/Modeling/DangerMap.h" #include "Representations/Modeling/HeatMapCollection.h" +#include "Representations/Modeling/KickWheel.h" #include "Representations/Modeling/RobotMap.h" #include "Representations/Modeling/RobotPose.h" #include "Representations/MotionControl/WalkingEngineParams.h" -#include "RoleProvider.h" +#include "Tools/Module/Module.h" #include "Tools/Settings.h" -#include "Modules/BehaviorControl/TacticControl/RoleProvider/Logs/BehaviorLogger.h" -#include // TODO Remove Redundancy with Utils -#include MODULE(BallchaserProvider, REQUIRES(BallSymbols), REQUIRES(BehaviorConfiguration), REQUIRES(DangerMap), - REQUIRES(FrameInfo), REQUIRES(FieldDimensions), + REQUIRES(FrameInfo), REQUIRES(GameInfo), REQUIRES(GameSymbols), REQUIRES(GoalSymbols), - REQUIRES(OwnTeamInfo), + REQUIRES(HeatMapCollection), + REQUIRES(KickWheel), + REQUIRES(MotionInfo), REQUIRES(OpponentTeamInfo), + REQUIRES(OwnTeamInfo), + REQUIRES(Receiver), REQUIRES(RobotInfo), REQUIRES(RobotMap), - REQUIRES(LocalRobotMap), + REQUIRES(RobotModel), REQUIRES(RobotPose), REQUIRES(RobotPoseAfterPreview), REQUIRES(RoleSelection), - REQUIRES(Receiver), - REQUIRES(TeammateData), REQUIRES(TacticSymbols), + REQUIRES(TeammateData), REQUIRES(WalkingEngineParams), - REQUIRES(HeatMapCollection), USES(PositioningSymbols), PROVIDES(Ballchaser), LOADS_PARAMETERS(, - - (float)(50.f) footDecisionHysteresis, /**< Min distance difference to change kick foot */ - (bool)(true) previewArrival, /**< If true, the robot will use preview for decision if robot has arrived (faster kick trigger). */ - (bool)(true) usePredictedBallPosition, - (bool)(true) playAgainstDribbleTeam, - - (float)(300.f) penaltyKickOpponentPositionX, - (float)(800.f) penaltyKickOpponentPositionY, - - (float)(1000.f) oppSetPlayBallDistance, - - (float)(1200.f) moveObjective_closeToMoveLineDistance, - (float)(500.f) moveObjective_sidesToBallOffset, - (float)(-20.f) moveObjective_oppGoalLineXOffset, - (float)(-50.f) moveObjective_oppPenaltyAreaXOffset - ) -); + (bool)(true) previewArrival, /**< If true, the robot will use preview for decision if robot has arrived (faster kick trigger). */ + + (std::vector) moveObjectiveNoDangerKickNames, + (std::vector) moveObjectiveDangerKickNames, + (std::vector) oneVsOneObjectiveQuickKickNames, + (std::vector) oneVsOneObjectiveTrickKickNames, + (std::vector) goalObjectiveNoDangerKickNames, + (std::vector) goalObjectiveDangerKickNames, + (std::vector) kickOffKickNames, + (std::vector) kickInKickNames, + (std::vector) cornerKickNames, + (std::vector) goalKickNames, + (std::vector) penaltyKickNames, + (std::vector) pushingFreeKickNames, + + (Angle)(30_deg) oneVsOneObjective_quickKickConeSize, + + (float)(1000.f) oppSetPlayBallDistance +)); class BallchaserProvider : public BallchaserProviderBase, public RoleProvider { public: BallchaserProvider(); + void init(); Vector2f ballPosition = Vector2f::Zero(); bool ballOnLeftSide = false; - bool kickWithLeft = false; Danger danger = Danger::NONE; - int timeArrivedInSetPlayWaiting = 0; + int startedSetPlayWaitingRemainingTime = 0; private: + void update(Ballchaser& ballchaser) override; + void stateReady_kickOff_own(Ballchaser& positioningSymbols, const Vector2f& kickOffPosition) override; void stateReady_kickOff_opponent(Ballchaser& positioningSymbols, const Vector2f& kickOffPosition) override; void statePlaying_kickOff_own(Ballchaser& positioningSymbols, const Vector2f& kickOffPosition) override; @@ -123,22 +127,24 @@ class BallchaserProvider : public BallchaserProviderBase, public RoleProvider& kicks); bool waitInOwnSetPlay(Ballchaser& positioningSymbols, const Vector2f& waitingPosition); - void freeKick(Ballchaser& ballchaser, const Vector2f& waitPosition, const float minX, const float opponentGoalHeatFactor); + void actInOpponentsSetPlay(Ballchaser& ballchaser, bool left, const std::function& isRobotPoseKickerPose); + void defendOwnGoal(Ballchaser& ballchaser, bool leftSide); - void update(Ballchaser& ballchaser) override; - void updateVariables(); - void updateKickWithLeft(); - void updateBallPosition(const Vector2f* useBallPosition); - void updateDanger(Ballchaser& ballchaser); + void updateVariables(Ballchaser& ballchaser); + [[nodiscard]] bool isDanger(float dangerDistance, bool hysteresis) const; ObjectivesManager regularPlayObjectivesManager; KickManager setPlayKickManager; std::vector> kickOffKicks; std::vector> kickInKicks; std::vector> cornerKicks; - std::vector> goalKick_Kicks; + std::vector> goalKicks; std::vector> penaltyKicks; + std::vector> pushingFreeKicks; BehaviorLogger logger; + + bool testBehavior = false; }; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/BallchaserUtils.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/BallchaserUtils.h similarity index 85% rename from Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/BallchaserUtils.h rename to Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/BallchaserUtils.h index 4da17ea1..a2e379a9 100644 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/BallchaserUtils.h +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/BallchaserUtils.h @@ -2,8 +2,8 @@ #include "Representations/BehaviorControl/RoleSymbols/Center.h" #include "Representations/Infrastructure/FrameInfo.h" -#include "FieldUtils.h" -#include "HysterUtils.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Utils/FieldUtils.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Utils/HysteresisUtils.h" class BallchaserUtils { diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/GoalObjective.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/GoalObjective.cpp new file mode 100644 index 00000000..8fd0e4ed --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/GoalObjective.cpp @@ -0,0 +1,295 @@ +#include "GoalObjective.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GoalObjective::GoalObjective(BallchaserProvider* role, BehaviorLogger& logger) : Objective("GoalObjective", role, logger), kickManager() +{ + auto kickEngineParameters = KicksProvider::loadKickEngineParameters(); + auto customStepFiles = KicksProvider::loadCustomStepFiles(); + noDangerKicks = KicksProvider::createKicks(kickEngineParameters, customStepFiles, role->goalObjectiveNoDangerKickNames); + dangerKicks = KicksProvider::createKicks(kickEngineParameters, customStepFiles, role->goalObjectiveDangerKickNames); +} + +bool GoalObjective::enterCondition() +{ + const Angle MAX_ANGLE = 74_deg; // TODO Constant also used in ExecutableKicks to filter + const bool leftAngleTooSharp = HysteresisUtils::compareAbsolutes(openingAngleTooSharp, MAX_ANGLE, -role->theGoalSymbols.leftAngleBallToOppGoalWC, 2_deg); + const bool rightAngleTooSharp = HysteresisUtils::compareAbsolutes(openingAngleTooSharp, MAX_ANGLE, role->theGoalSymbols.rightAngleBallToOppGoalWC, 2_deg); + openingAngleTooSharp = leftAngleTooSharp || rightAngleTooSharp; + if (openingAngleTooSharp) + { + logger.addFailedReason("openingAngleTooSharp"); + return false; + } + else + { + return true; + } +} + +bool GoalObjective::perform(Ballchaser& ballchaser) +{ + updateGoalDistance(); + + switch (goalDistance) + { + case VERY_FAR: + { + logger.addFailedReason("TooFar"); + return false; + } + case FAR: + { + return kickToGoalCenter(ballchaser); + } + case NORMAL: + case CLOSE: + case VERY_CLOSE: + return kickBestToGoal(ballchaser); + default: + throw std::runtime_error("Unknown Enum Value!"); + } +} + +void GoalObjective::updateGoalDistance() +{ + const float ballToGoalLineDistance = FieldUtils::getDistanceToGoalLine(role->ballPosition, role->theFieldDimensions); + GoalDistance newGoalDistance; + if (isVeryCloseToGoal()) + { + logger.addNote("VeryCloseToGoal"); + newGoalDistance = VERY_CLOSE; + } + else if (HysteresisUtils::comparePossibleNegatives(goalDistance == CLOSE, ballToGoalLineDistance, 1000.f, 200.f)) + { + logger.addNote("CloseToGoal"); + newGoalDistance = CLOSE; + } + else if (HysteresisUtils::comparePossibleNegatives(goalDistance == NORMAL, ballToGoalLineDistance, 2300.f, 200.f)) + { + logger.addNote("NormalToGoal"); + newGoalDistance = NORMAL; + } + else if (HysteresisUtils::comparePossibleNegatives(goalDistance == FAR, ballToGoalLineDistance, 5000.f, 200.f)) + { + logger.addNote("FarFromGoal"); + newGoalDistance = FAR; + } + else + { + logger.addNote("VeryFarFromGoal"); + newGoalDistance = VERY_FAR; + } + goalDistance = newGoalDistance; +} + +bool GoalObjective::isVeryCloseToGoal() const +{ + const Vector2f leftGoalPostPosition = {role->theFieldDimensions.xPosOpponentGroundline, role->theFieldDimensions.yPosLeftGoal}; + const Vector2f rightGoalPostPosition = {role->theFieldDimensions.xPosOpponentGroundline, role->theFieldDimensions.yPosRightGoal}; + + const bool hysteresis = goalDistance == VERY_CLOSE; + + if (!HysteresisUtils::comparePossibleNegatives(hysteresis, role->ballPosition.y(), leftGoalPostPosition.y(), 0.f, 50.f)) + { + return false; + } + + if (!HysteresisUtils::comparePossibleNegatives(hysteresis, rightGoalPostPosition.y(), role->ballPosition.y(), 0.f, 50.f)) + { + return false; + } + + const Geometry::Line goalLine = {leftGoalPostPosition, (rightGoalPostPosition - leftGoalPostPosition).normalized()}; + const float ballToGoalDistance = Geometry::getDistanceToLine(goalLine, role->ballPosition); + return HysteresisUtils::comparePossibleNegatives(hysteresis, ballToGoalDistance, 200.f, 100.f); +} + +bool GoalObjective::kickToGoalCenter(Ballchaser& ballchaser) +{ + const Vector2f leftGoalPostPosition = {role->theFieldDimensions.xPosOpponentGroundline, role->theFieldDimensions.yPosLeftGoal - role->theFieldDimensions.goalPostRadius}; + const Vector2f rightGoalPostPosition = {role->theFieldDimensions.xPosOpponentGroundline, role->theFieldDimensions.yPosRightGoal + role->theFieldDimensions.goalPostRadius}; + + bool optimizeForTime; + std::vector kicks; + switch (role->danger) + { + case Danger::HIGH: + optimizeForTime = true; + kicks = KickUtils::unpack(dangerKicks); + logger.addNote("UseDangerKicks"); + break; + case Danger::MEDIUM: + case Danger::LOW: + case Danger::NONE: + case Danger::IMPOSSIBLE: + optimizeForTime = false; + kicks = KickUtils::unpack(noDangerKicks); + logger.addNote("UseNoDangerKicks"); + break; + default: + throw std::logic_error(""); + } + + const Vector2f goalCenter = (leftGoalPostPosition + rightGoalPostPosition) / 2; + const float ballToGoalCenterDistance = Geometry::distance(goalCenter, role->ballPosition); + std::vector enoughRangeKicks; + for (auto kick : kicks) + { + if (kick->getRealisticDistance() > ballToGoalCenterDistance) + { + enoughRangeKicks.push_back(kick); + } + } + + const KickCone kickCone = {role->ballPosition, (leftGoalPostPosition - role->ballPosition).angle(), (rightGoalPostPosition - role->ballPosition).angle()}; + const Filterer filterer = Filterer().filterBlocked().filterOutsideKickRange(kickCone); + std::function customScoreFunction = [&leftGoalPostPosition, &goalCenter](const KickPlan& kickPlan) + { + const Geometry::Line goalLine = {leftGoalPostPosition, goalCenter - leftGoalPostPosition}; + const Geometry::Line kickLine = { + kickPlan.selectablePose.selectableTarget.ballPosition, kickPlan.selectablePose.selectableTarget.target - kickPlan.selectablePose.selectableTarget.ballPosition}; + Vector2f intersection; + VERIFY(Geometry::getIntersectionOfLines(goalLine, kickLine, intersection)); + const float distanceToCenter = std::abs(intersection.y()); // center is at y = 0 + const float maxDistanceToCenter = leftGoalPostPosition.y(); // center is at y = 0 + return 2 * (1 - distanceToCenter / maxDistanceToCenter); + }; + const Factors factors = Factors(0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, optimizeForTime ? -1.f : 0.f, optimizeForTime ? 0.f : -1.f).addCustomScoreFunction(customScoreFunction); + const auto selectableKickOptional = SelectFunctions::createAndFilterAndSelect(role->theRobotPoseAfterPreview, + role->ballPosition, + enoughRangeKicks, + kickManager.getCurrentKick(role->ballPosition), + filterer, + factors, + role->theFieldDimensions, + role->theHeatMapCollection, + role->theKickWheel, + role->theRobotMap, + role->theTacticSymbols); + + const bool suc = selectableKickOptional.has_value(); + if (suc) + { + kickManager.kickTo(ballchaser, selectableKickOptional.value(), role->theFrameInfo); + } + logger.addReason(suc, "KickInRangeCenter"); + return suc; +} + +bool GoalObjective::kickBestToGoal(Ballchaser& ballchaser) +{ + const Vector2f leftGoalPostPosition = {role->theFieldDimensions.xPosOpponentGroundline, role->theFieldDimensions.yPosLeftGoal - role->theFieldDimensions.goalPostRadius}; + const Vector2f rightGoalPostPosition = {role->theFieldDimensions.xPosOpponentGroundline, role->theFieldDimensions.yPosRightGoal + role->theFieldDimensions.goalPostRadius}; + + Vector2f leftTarget = leftGoalPostPosition; + Vector2f rightTarget = rightGoalPostPosition; + + float widthFactor = -1000.f; // These values will lead to an assertion error if not overwritten + float timeFactor = 1000.f; + std::vector kicks; + switch (goalDistance) + { + case VERY_CLOSE: + { + leftTarget = {leftGoalPostPosition.x() + 150.f, leftGoalPostPosition.y()}; + rightTarget = {rightGoalPostPosition.x() + 150.f, rightGoalPostPosition.y()}; + widthFactor = 0.f; // Important, so the target doesn't change if the goalKeeper comes close + logger.addNote("VeryClose->IgnoreKickWidth"); + break; + } + case CLOSE: + { + switch (role->danger) + { + case Danger::HIGH: + case Danger::MEDIUM: + widthFactor = 0.f; // Important, so the target doesn't change if the goalKeeper comes close + logger.addNote("CloseAndHighDanger->IgnoreKickWidth"); + break; + case Danger::LOW: + case Danger::NONE: + case Danger::IMPOSSIBLE: + widthFactor = 0.5f; + break; + default: + throw std::logic_error(""); + } + break; + } + case NORMAL: + widthFactor = 1.f; + break; + case FAR: + widthFactor = 1.f; + break; + case VERY_FAR: + widthFactor = 1.f; + break; + default: + throw std::logic_error(""); + } + switch (role->danger) + { + case Danger::HIGH: + timeFactor = -2.f; + kicks = KickUtils::unpack(dangerKicks); + logger.addNote("OnlyDangerKicks"); + break; + case Danger::MEDIUM: + timeFactor = -1.f; + kicks = KickUtils::unpack(noDangerKicks); + logger.addNote("OnlyDangerKicks"); + break; + case Danger::LOW: + timeFactor = -0.2f; + kicks = KickUtils::unpack(noDangerKicks); + logger.addNote("OnlyNoDangerKicks"); + break; + case Danger::NONE: + timeFactor = -0.1f; + kicks = KickUtils::unpack(noDangerKicks); + logger.addNote("OnlyNoDangerKicks"); + break; + case Danger::IMPOSSIBLE: + timeFactor = 0.f; + kicks = KickUtils::unpack(noDangerKicks); + logger.addNote("OnlyNoDangerKicks"); + break; + default: + throw std::logic_error(""); + } + + const KickLine kickLine = {role->ballPosition, leftTarget, rightTarget, mustFurther, 300.f}; + const Filterer filterer = Filterer().filterOutsideKickRange(kickLine).filterBlocked(); + const Factors factors = {widthFactor, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.1f, 0.f, 0.f, timeFactor, 0.f}; + const auto selectableKickOptional = SelectFunctions::createAndFilterAndSelect( + role->theRobotPoseAfterPreview, role->ballPosition, kicks, kickManager.getCurrentKick(role->ballPosition), filterer, factors, role->theFieldDimensions, role->theHeatMapCollection, role->theKickWheel, role->theRobotMap, role->theTacticSymbols); + + if (selectableKickOptional.has_value()) + { + kickManager.kickTo(ballchaser, selectableKickOptional.value(), role->theFrameInfo); + logger.addSuccessReason("BestGoalKick"); + return true; + } + else + { + logger.addFailedReason("BestGoalKick"); + return false; + } +} + +void GoalObjective::postprocess() +{ + kickManager.stop(); + Objective::postprocess(); +} diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/GoalObjective.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/GoalObjective.h similarity index 66% rename from Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/GoalObjective.h rename to Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/GoalObjective.h index 14f8bdff..a70be5e6 100644 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/GoalObjective.h +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/GoalObjective.h @@ -1,11 +1,8 @@ #pragma once -#include -#include -#include -#include -#include "Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Objective.h" -#include "Modules/BehaviorControl/TacticControl/RoleProvider/BallchaserProvider.h" +#include +#include "Modules/BehaviorControl/TacticControl/RoleProvider/ObjectivesManager/Objective.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/BallchaserProvider.h" #include "Representations/BehaviorControl/RoleSymbols/Ballchaser.h" #include "Modules/BehaviorControl/TacticControl/RoleProvider/Utils/FieldUtils.h" #include "Modules/BehaviorControl/TacticControl/RoleProvider/Utils/KickUtils.h" @@ -19,6 +16,7 @@ class GoalObjective : public Objective GoalObjective(BallchaserProvider* role, BehaviorLogger& logger); bool enterCondition() override; bool perform(Ballchaser& ballchaser) override; + void postprocess() override; enum GoalDistance { @@ -31,13 +29,13 @@ class GoalObjective : public Objective private: KickManager kickManager; - std::vector> kicks = {}; + std::vector> dangerKicks = {}; + std::vector> noDangerKicks = {}; bool openingAngleTooSharp = false; GoalDistance goalDistance = FAR; - void update(); - - bool isVeryCloseToGoal(const Vector2f& leftGoalPostPosition, const Vector2f& rightGoalPostPosition, float ballToGoalDistance); + void updateGoalDistance(); + bool isVeryCloseToGoal() const; bool kickToGoalCenter(Ballchaser& ballchaser); diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/MoveObjective.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/MoveObjective.cpp new file mode 100644 index 00000000..c9211271 --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/MoveObjective.cpp @@ -0,0 +1,180 @@ +#include "MoveObjective.h" + +#include +#include +#include +#include +#include +#include + +MoveObjective::MoveObjective(BallchaserProvider* role, BehaviorLogger& logger) : Objective("MoveObjective", role, logger) +{ + const auto kickEngineParameter = KicksProvider::loadKickEngineParameters(); + const auto customStepFiles = KicksProvider::loadCustomStepFiles(); + noDangerKicks = KicksProvider::createKicks(kickEngineParameter, customStepFiles, role->moveObjectiveNoDangerKickNames); + dangerKicks = KicksProvider::createKicks(KicksProvider::loadKickEngineParameters(), KicksProvider::loadCustomStepFiles(), role->moveObjectiveDangerKickNames); +} + +bool MoveObjective::perform(Ballchaser& ballchaser) +{ + auto kicks = getKicks(role->danger); + + const Vector2f opponentGoalCenter = FieldUtils::getOpponentGoalCenter(role->theFieldDimensions); + const float distanceToOpponentGoalCenter = FieldUtils::getDistanceToGoalLine(role->ballPosition, role->theFieldDimensions); + const Vector2f ownLeftGoalPost = {role->theFieldDimensions.xPosOwnGroundline, role->theFieldDimensions.yPosLeftGoal}; + const Vector2f ownRightGoalPost = {role->theFieldDimensions.xPosOwnGroundline, role->theFieldDimensions.yPosRightGoal}; + const Vector2f opponentLeftGoalPost = {role->theFieldDimensions.xPosOpponentGroundline, role->theFieldDimensions.yPosLeftGoal}; + const Vector2f opponentRightGoalPost = {role->theFieldDimensions.xPosOpponentGroundline, role->theFieldDimensions.yPosRightGoal}; + + Filterer filterer = {}; + + if (distanceToOpponentGoalCenter < 50.f) // TODO Hysteresis + { + filterer.filterLeft(opponentLeftGoalPost.y()).filterRight(opponentRightGoalPost.y()).filterTooFarBack(role->ballPosition.x()); + } + else + { + const float OWN_SIDE_X = role->theFieldDimensions.xPosOwnPenaltyArea; + const float OPPONENT_SIDE_X = (0.f + role->theFieldDimensions.xPosOpponentPenaltyArea) / 2.f; + + Vector2f linePoint1 = role->ballPosition; + Vector2f linePoint2; + + Vector2f ownSideMoveInDirection = {1.f, 0}; + Vector2f opponentSideMoveInDirection = opponentGoalCenter - role->ballPosition; + + const bool ballOnOpponentSide = role->ballPosition.x() > OPPONENT_SIDE_X; + const bool ballBetweenSides = !ballOnOpponentSide && role->ballPosition.x() > OWN_SIDE_X; + + if (ballOnOpponentSide) + { + linePoint2 = role->ballPosition + Vector2f(opponentSideMoveInDirection).rotateLeft(); + if (Geometry::isPointLeftOfLine(opponentLeftGoalPost, linePoint1, linePoint2)) + { + linePoint2 = opponentLeftGoalPost; + } + else if (Geometry::isPointLeftOfLine(opponentRightGoalPost, linePoint1, linePoint2)) + { + linePoint2 = linePoint1; + linePoint1 = opponentRightGoalPost; + } + } + else if (ballBetweenSides) + { + const float opponentSideFactor = (role->ballPosition.x() - OWN_SIDE_X) / (OPPONENT_SIDE_X - OWN_SIDE_X); + const Vector2f moveInDirection = (1 - opponentSideFactor) * ownSideMoveInDirection + opponentSideFactor * opponentSideMoveInDirection; + linePoint2 = role->ballPosition + Vector2f(moveInDirection).rotateLeft(); + } + + if (ballOnOpponentSide || ballBetweenSides) // TODO Hysteresis!? + { + filterer.filterTooFarBack(std::min(role->ballPosition.x() - 500.f, role->theFieldDimensions.xPosOpponentPenaltyArea)).filterLeftOfLine(linePoint1, linePoint2); + } + else + { + filterer.filterToOwnGoal(role->ballPosition, role->theFieldDimensions); + } + } + + filterer.filterOutside() + .filterKickPoseBlockedByGoalPost(role->theFieldDimensions) + .filterKickPoseBlockedByRobots(role->theRobotMap) + .filterInFrontOfOwnGoal(role->theFieldDimensions) + .filterCloseToFieldLine(role->theFieldDimensions); + + const Factors factors = {getWidthFactor(role->danger), + 0.f, + 1.f, // Field + 0.01f, + 0.2f, // Team + -0.01f, + -0.2f, // Opponent + 0.1f, + 0.f, + 0.f, // Pose + getTimeFactor(role->danger), + 0.f}; // Kick + + const std::optional kickPlanOptional = SelectFunctions::createAndFilterAndSelect( + role->theRobotPoseAfterPreview, role->ballPosition, kicks, kickManager.getCurrentKick(role->ballPosition), filterer, factors, role->theFieldDimensions, role->theHeatMapCollection, role->theKickWheel, role->theRobotMap, role->theTacticSymbols); + + if (kickPlanOptional.has_value()) + { + kickManager.kickTo(ballchaser, kickPlanOptional.value(), role->theFrameInfo); + logger.addSuccessReason("KickBestWithFactors"); + return true; + } + else + { + logger.addFailedReason("KickBestWithFactors"); + logger.addSuccessReason("Defend"); + const Vector2f defensivePosition = BallchaserUtils::getWaitPosition(role->ballPosition, role->theFieldDimensions); + PositionUtils::setPosition(ballchaser, defensivePosition); + return true; + } +} + +std::vector MoveObjective::getKicks(const Danger danger) +{ + switch (danger) + { + case Danger::HIGH: + return KickUtils::unpack(dangerKicks); + case Danger::MEDIUM: + case Danger::LOW: + case Danger::NONE: + case Danger::IMPOSSIBLE: + return KickUtils::unpack(noDangerKicks); + default: + throw std::logic_error(""); + } +} + +float MoveObjective::getWidthFactor(const Danger danger) +{ + switch (danger) // TODO Use enemy goal shoot probability to calculate danger + { + case Danger::HIGH: + return 0.f; + case Danger::MEDIUM: + return 0.f; + case Danger::LOW: + return 0.1f; + case Danger::NONE: + return 0.15f; + case Danger::IMPOSSIBLE: + return 0.15f; + default: + throw std::logic_error(""); + } +} + +float MoveObjective::getTimeFactor(const Danger danger) +{ + switch (danger) // TODO Use enemy goal shoot probability to calculate danger + { + case Danger::HIGH: + return -1.f; + case Danger::MEDIUM: + return -0.5f; + case Danger::LOW: + return -0.1f; + case Danger::NONE: + return 0.f; + case Danger::IMPOSSIBLE: + return 0.f; + default: + throw std::logic_error(""); + } +} + +bool MoveObjective::leaveCondition() const +{ + return true; +} + +void MoveObjective::postprocess() +{ + kickManager.stop(); + Objective::postprocess(); +} diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/MoveObjective.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/MoveObjective.h similarity index 57% rename from Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/MoveObjective.h rename to Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/MoveObjective.h index c6015170..e14b58f2 100644 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/MoveObjective.h +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/MoveObjective.h @@ -1,13 +1,11 @@ #pragma once -#include "Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Objective.h" -#include "Modules/BehaviorControl/TacticControl/RoleProvider/BallchaserProvider.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/ObjectivesManager/Objective.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/BallchaserProvider.h" #include "Representations/BehaviorControl/RoleSymbols/Ballchaser.h" #include "Modules/BehaviorControl/TacticControl/RoleProvider/Utils/KickUtils.h" -#include -#include -#include -#include +#include +#include class BallchaserProvider; struct Ballchaser; @@ -17,19 +15,16 @@ class MoveObjective : public Objective public: MoveObjective(BallchaserProvider* role, BehaviorLogger& logger); bool perform(Ballchaser& ballchaser) override; + [[nodiscard]] bool leaveCondition() const override; + void postprocess() override; private: - KickManager kickManager; - std::vector> kicks = {}; - - bool ballCloseToMoveLine = false; - bool ballLeftOfPenaltyArea = false; - bool ballRightOfPenaltyArea = false; - bool ballBehindMoveLine = false; + std::vector getKicks(Danger danger); - void update(); + static float getWidthFactor(Danger danger); + static float getTimeFactor(Danger danger); - KickRange getKickRange(); - - float getMoveToLineX(); + KickManager kickManager; + std::vector> noDangerKicks = {}; + std::vector> dangerKicks = {}; }; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/Test/KickBallTestObjective.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/Test/KickBallTestObjective.cpp new file mode 100644 index 00000000..bcc52283 --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/Test/KickBallTestObjective.cpp @@ -0,0 +1,33 @@ +#include "KickBallTestObjective.h" + +#include +#include +#include +#include +#include +#include + +KickBallTestObjective::KickBallTestObjective(BallchaserProvider* role, BehaviorLogger& logger, const std::string& kickName) : Objective("TestObjective", role, logger) +{ + std::vector kickNames = {}; + kickNames.emplace_back(kickName); + kicks = KicksProvider::createKicks(KicksProvider::loadKickEngineParameters(), KicksProvider::loadCustomStepFiles(), kickNames); +} + +bool KickBallTestObjective::perform(Ballchaser& ballchaser) +{ + const Filterer filterer = Filterer(); + const Factors factors = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, -1.f, 0.f}; + const auto currentKick = kickManager.getCurrentKick(role->ballPosition); + const auto kickPlanOptional = SelectFunctions::createAndFilterAndSelect( + role->theRobotPoseAfterPreview, role->ballPosition, KickUtils::unpack(kicks), currentKick, filterer, factors, role->theFieldDimensions, role->theHeatMapCollection, role->theKickWheel, role->theRobotMap, role->theTacticSymbols); + if (kickPlanOptional.has_value()) + { + kickManager.kickTo(ballchaser, kickPlanOptional.value(), role->theFrameInfo); + } + else + { + OUTPUT_WARNING("Kick not possible!"); + } + return true; +} diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/TestObjective.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/Test/KickBallTestObjective.h similarity index 58% rename from Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/TestObjective.h rename to Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/Test/KickBallTestObjective.h index 04857ad2..b8239e2b 100644 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/TestObjective.h +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/Test/KickBallTestObjective.h @@ -1,23 +1,20 @@ #pragma once -#include "Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Objective.h" -#include "Modules/BehaviorControl/TacticControl/RoleProvider/BallchaserProvider.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/ObjectivesManager/Objective.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/BallchaserProvider.h" #include "Representations/BehaviorControl/RoleSymbols/Ballchaser.h" #include "Modules/BehaviorControl/TacticControl/RoleProvider/Utils/KickUtils.h" -#include -#include -#include -#include +#include +#include class BallchaserProvider; struct Ballchaser; -class TestObjective : public Objective +class KickBallTestObjective : public Objective { public: - TestObjective(BallchaserProvider* role, BehaviorLogger& logger); + KickBallTestObjective(BallchaserProvider* role, BehaviorLogger& logger, const std::string& kickName); bool perform(Ballchaser& ballchaser) override; - bool leaveCondition() const override; private: KickManager kickManager = {}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/Test/KickInPositionTestObjective.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/Test/KickInPositionTestObjective.cpp new file mode 100644 index 00000000..1b996e1e --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/Test/KickInPositionTestObjective.cpp @@ -0,0 +1,38 @@ +#include "KickInPositionTestObjective.h" + +#include +#include +#include + +KickInPositionTestObjective::KickInPositionTestObjective(BallchaserProvider* role, BehaviorLogger& logger, const std::string& kickName) : Objective("TestObjective", role, logger) +{ + std::vector kickNames = {}; + kickNames.emplace_back(kickName); + kicks = KicksProvider::createKicks(KicksProvider::loadKickEngineParameters(), KicksProvider::loadCustomStepFiles(), kickNames); +} + +bool KickInPositionTestObjective::perform(Ballchaser& ballchaser) +{ + if (KickUtils::isBallKicked(role->theMotionInfo)) + { + kicked = true; + return true; + } + const Vector2f littleOffCurrentPosition = {role->theRobotPoseAfterPreview.translation.x() + 1, role->theRobotPoseAfterPreview.translation.y()}; + const Pose2f littleOffCurrentPose = Pose2f(role->theRobotPoseAfterPreview.rotation, littleOffCurrentPosition); + if (kicked) + { + ballchaser.optPosition = littleOffCurrentPose; + kicked = false; + } + else + { + const SelectableKick selectableKick = {kicks.at(0).get()}; + const Vector2f target = {0, 0}; + const SelectableTarget selectableTarget = {selectableKick, role->ballPosition, target}; + const SelectablePose selectablePose = {role->theRobotPoseAfterPreview, selectableTarget, littleOffCurrentPose, true}; + const KickPlan kickPlan = {selectablePose, true}; + kickManager.kickTo(ballchaser, kickPlan, role->theFrameInfo); + } + return true; +} diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/Test/KickInPositionTestObjective.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/Test/KickInPositionTestObjective.h new file mode 100644 index 00000000..3419785f --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/Test/KickInPositionTestObjective.h @@ -0,0 +1,23 @@ +#pragma once + +#include "Modules/BehaviorControl/TacticControl/RoleProvider/ObjectivesManager/Objective.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/BallchaserProvider.h" +#include "Representations/BehaviorControl/RoleSymbols/Ballchaser.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Utils/KickUtils.h" +#include +#include + +class BallchaserProvider; +struct Ballchaser; +class KickInPositionTestObjective : public Objective +{ + +public: + KickInPositionTestObjective(BallchaserProvider* role, BehaviorLogger& logger, const std::string& kickName); + bool perform(Ballchaser& ballchaser) override; + +private: + KickManager kickManager = {}; + std::vector> kicks = {}; + bool kicked = false; +}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/Test/KickToCenterTestObjective.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/Test/KickToCenterTestObjective.cpp new file mode 100644 index 00000000..5445cf4b --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/Test/KickToCenterTestObjective.cpp @@ -0,0 +1,56 @@ +#include "KickToCenterTestObjective.h" + +#include +#include +#include +#include +#include +#include +#include + + +KickToCenterTestObjective::KickToCenterTestObjective(BallchaserProvider* role, BehaviorLogger& logger, const std::string& kickName) : Objective("TestObjective", role, logger) +{ + std::vector kickNames = {}; + kickNames.emplace_back(kickName); + kicks = KicksProvider::createKicks(KicksProvider::loadKickEngineParameters(), KicksProvider::loadCustomStepFiles(), kickNames); +} + +bool KickToCenterTestObjective::perform(Ballchaser& ballchaser) +{ + const Filterer filterer = {}; + const Factors factors = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, -1.f}; + const Vector2f targetPosition = Vector2f(0, 0); + const std::optional selectableKickOptional = TargetFunctions::getKickPlanForTarget(role->theRobotPoseAfterPreview, + role->ballPosition, + targetPosition, + DistanceRequirement::mayShorterOrFurther, + KickUtils::unpack(kicks), + kickManager.getCurrentKick(role->ballPosition), + filterer, + factors, + role->theFieldDimensions, + role->theHeatMapCollection, + role->theRobotMap, + role->theTacticSymbols, + role->theKickWheel); + if (selectableKickOptional.has_value()) + { + kickManager.kickTo(ballchaser, selectableKickOptional.value(), role->theFrameInfo); + return true; + } + logger.addSuccessReason("Cant Kick!"); + const Vector2f defensivePosition = BallchaserUtils::getWaitPosition(role->ballPosition, role->theFieldDimensions); + PositionUtils::setPosition(ballchaser, defensivePosition); + return true; +} + +bool KickToCenterTestObjective::leaveCondition() const +{ + return false; +} + +void KickToCenterTestObjective::postprocess() +{ + Objective::postprocess(); +} diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/Test/KickToCenterTestObjective.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/Test/KickToCenterTestObjective.h new file mode 100644 index 00000000..a18994eb --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/Test/KickToCenterTestObjective.h @@ -0,0 +1,23 @@ +#pragma once + +#include "Modules/BehaviorControl/TacticControl/RoleProvider/ObjectivesManager/Objective.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/BallchaserProvider.h" +#include "Representations/BehaviorControl/RoleSymbols/Ballchaser.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Utils/KickUtils.h" +#include + +class BallchaserProvider; +struct Ballchaser; +class KickToCenterTestObjective : public Objective +{ + +public: + KickToCenterTestObjective(BallchaserProvider* role, BehaviorLogger& logger, const std::string& kickName); + bool perform(Ballchaser& ballchaser) override; + bool leaveCondition() const override; + void postprocess() override; + +private: + KickManager kickManager = {}; + std::vector> kicks = {}; +}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/TrickKickObjective.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/TrickKickObjective.cpp new file mode 100644 index 00000000..129d244a --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/TrickKickObjective.cpp @@ -0,0 +1,182 @@ +#include "TrickKickObjective.h" + +#include +#include +#include +#include +#include +#include +#include + +TickKickObjective::TickKickObjective(BallchaserProvider* role, BehaviorLogger& logger) : Objective("TrickKickObjective", role, logger), kickManager() +{ + auto kickEngineParameters = KicksProvider::loadKickEngineParameters(); + auto customStepFiles = KicksProvider::loadCustomStepFiles(); + kicks = KicksProvider::createKicks(kickEngineParameters, customStepFiles, role->oneVsOneObjectiveTrickKickNames); +} + +bool TickKickObjective::enterCondition() +{ + logger.startInner("enter"); + + if (role->theTacticSymbols.closeToBallOpponentRobotNumber == 0) + { + logger.addNote("NoOpponentsCloseToBall"); + logger.stopInner(); + return false; + } + else + { + logger.addNote("OpponentsCloseToBall"); + } + + if (!isBlockingImportantKickForOpponent()) + { + logger.addNote("NotBlocking"); + logger.stopInner(); + return false; + } + else + { + logger.addNote("Blocking"); + } + + logger.stopInner(); + return true; +} + +bool TickKickObjective::isBlockingImportantKickForOpponent() +{ + const Pose2f& playerPose = role->theRobotPoseAfterPreview; + const Vector2f& ballPosition = role->theBallSymbols.ballPositionField; + const Vector2f& opponentPosition = role->theTacticSymbols.closeToBallOpponentRobot.translation; + const Vector2f& opponentToBall = ballPosition - opponentPosition; + const Cone defensiveCone = TacticUtils::getDefenseCone(100_deg, role->ballPosition, role->theFieldDimensions); + + // Draw + const Vector2f leftAnglePoint = ballPosition + MathUtils::angleToVector(defensiveCone.left).normalized(1000.f); + const Vector2f rightAnglePoint = ballPosition + MathUtils::angleToVector(defensiveCone.right).normalized(1000.f); + LINE(DRAW_KICK_RANGE_NAME, ballPosition.x(), ballPosition.y(), leftAnglePoint.x(), leftAnglePoint.y(), 20, Drawings::solidPen, ColorRGBA::red); + LINE(DRAW_KICK_RANGE_NAME, ballPosition.x(), ballPosition.y(), rightAnglePoint.x(), rightAnglePoint.y(), 20, Drawings::solidPen, ColorRGBA::green); + + // Is opponent playing in wrong direction + const Angle opponentAngle = (ballPosition - opponentPosition).angle(); + const Angle opponentLeftAngle = Angle(180_deg + defensiveCone.right).normalize(); + const Angle opponentRightAngle = Angle(180_deg + defensiveCone.left).normalize(); + if (!MathUtils::isBetweenAngles(opponentAngle, opponentLeftAngle, opponentRightAngle)) + { + logger.addNote("OpponentWrongSideOfDefenseLine"); + return false; + } + + // Is player playing in wrong direction + const Angle playerAngle = (playerPose.translation - ballPosition).angle(); + if (!defensiveCone.istInside(playerAngle)) + { + logger.addNote("PlayerWrongSideOfDefenseLine"); + return false; + } + + // Is opponent target outside field + const float OPPONENT_POSSIBLE_KICK_DISTANCE = 1500.f; + const Vector2f opponentsPossibleTarget = ballPosition + opponentToBall.normalized(OPPONENT_POSSIBLE_KICK_DISTANCE); + if (!role->theFieldDimensions.isBallInsideField(opponentsPossibleTarget)) + { + logger.addNote("OpponentTargetOutsideField"); + return false; + } + + // Is player standing in opponents kick cone + const float ROBOT_RADIUS = 150.f; + const Vector2f opponentLeftSide = opponentPosition + opponentToBall.normalized(ROBOT_RADIUS).rotateLeft(); + const Vector2f opponentRightSide = opponentPosition + opponentToBall.normalized(ROBOT_RADIUS).rotateRight(); + const Angle opponentsConeLeftAngle = (ballPosition - opponentRightSide).angle(); + const Angle opponentsConeRightAngle = (ballPosition - opponentLeftSide).angle(); + const Angle ballToPlayerAngle = (playerPose.translation - ballPosition).angle(); + // + const Vector2f opponentLeftSideEndpoint = opponentLeftSide + (ballPosition - opponentLeftSide).normalized(3000.f); + const Vector2f opponentRightSideEndpoint = opponentRightSide + (ballPosition - opponentRightSide).normalized(3000.f); + LINE(DRAW_KICK_RANGE_NAME, opponentLeftSide.x(), opponentLeftSide.y(), opponentLeftSideEndpoint.x(), opponentLeftSideEndpoint.y(), 20, Drawings::solidPen, ColorRGBA::blue); + LINE(DRAW_KICK_RANGE_NAME, opponentRightSide.x(), opponentRightSide.y(), opponentRightSideEndpoint.x(), opponentRightSideEndpoint.y(), 20, Drawings::solidPen, ColorRGBA::blue); + // + if (!MathUtils::isBetweenAngles(ballToPlayerAngle, opponentsConeLeftAngle, opponentsConeRightAngle)) + { + logger.addNote("PlayerNotInOpponentsKickCone"); + return false; + } + + return true; +} + +bool TickKickObjective::perform(Ballchaser& ballchaser) +{ + if (performTrickKick(ballchaser)) + { + logger.addSuccessReason("TrickKick"); + return true; + } + else + { + logger.addFailedReason("TrickKick"); + return false; + } +} + +/** + * Stand in front of opponent and try tricks with angled kicks + */ +bool TickKickObjective::performTrickKick(Ballchaser& ballchaser) +{ + const Pose2f playerPose = role->theRobotPoseAfterPreview; + const Vector2f ballPosition = role->theBallSymbols.ballPositionField; + const auto [leftAngle, rightAngle] = TacticUtils::getDefenseCone(180_deg, role->ballPosition, role->theFieldDimensions); + + const Filterer filterer = + Filterer() + //.filterTooHighRotationToKick(playerPose.rotation, 15_deg) + .filterOutside() + //.filterBlocked(role->theKickWheel) + .filterBetweenAngles(role->ballPosition, leftAngle, rightAngle); + const Factors factors = {0.f, 0.f, 1.f, 1.f, 1.f, -1.f, -1.f, 1.f, 0.f, 1.f, -10.f, 0.f}; + auto selectableKickOptional = SelectFunctions::createAndFilterAndSelect(role->theRobotPoseAfterPreview, + role->ballPosition, + KickUtils::unpack(kicks), + kickManager.getCurrentKick(role->ballPosition), + filterer, + factors, + role->theFieldDimensions, + role->theHeatMapCollection, + role->theKickWheel, + role->theRobotMap, + role->theTacticSymbols); + if (selectableKickOptional.has_value()) + { + kickManager.kickTo(ballchaser, selectableKickOptional.value(), role->theFrameInfo); + return true; + } + else + { + kickManager.stop(); + return false; + } +} + +bool TickKickObjective::leaveCondition() const +{ + const float distanceToBall = Geometry::distance(role->ballPosition, role->theRobotPoseAfterPreview.translation); + if (distanceToBall > 500.f) + { + return true; + } + if (KickUtils::isBallKicked(role->theMotionInfo)) + { + return true; + } + return false; +} + +void TickKickObjective::postprocess() +{ + Objective::postprocess(); + kickManager.stop(); +} diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/TrickKickObjective.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/TrickKickObjective.h new file mode 100644 index 00000000..cb16895c --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/TrickKickObjective.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include "Modules/BehaviorControl/TacticControl/RoleProvider/ObjectivesManager/Objective.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Ballchaser/BallchaserProvider.h" +#include "Representations/BehaviorControl/RoleSymbols/Ballchaser.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Utils/FieldUtils.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Utils/KickUtils.h" + +class BallchaserProvider; +struct Ballchaser; +class TickKickObjective : public Objective +{ +public: + TickKickObjective(BallchaserProvider* role, BehaviorLogger& logger); + bool enterCondition() override; + bool perform(Ballchaser& ballchaser) override; + [[nodiscard]] bool leaveCondition() const override; + void postprocess() override; + +private: + KickManager kickManager; + std::vector> kicks = {}; + + bool isBlockingImportantKickForOpponent(); + bool performTrickKick(Ballchaser& ballchaser); +}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/BallchaserProvider.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/BallchaserProvider.cpp deleted file mode 100644 index 0c570eca..00000000 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/BallchaserProvider.cpp +++ /dev/null @@ -1,393 +0,0 @@ -#include "BallchaserProvider.h" - -#include -#include -#include - -BallchaserProvider::BallchaserProvider() : regularPlayObjectivesManager(logger) -{ - //regularPlayObjectivesManager.add(std::make_unique(this, logger)); - regularPlayObjectivesManager.add(std::make_unique(this, logger)); - regularPlayObjectivesManager.add(std::make_unique(this, logger)); - regularPlayObjectivesManager.add(std::make_unique(this, logger)); - regularPlayObjectivesManager.add(std::make_unique(this, logger)); - - kickOffKicks.push_back(std::make_unique()); - kickOffKicks.push_back(std::make_unique()); - kickOffKicks.push_back(std::make_unique()); - - kickInKicks.push_back(std::make_unique()); - kickInKicks.push_back(std::make_unique()); - kickInKicks.push_back(std::make_unique()); - - cornerKicks.push_back(std::make_unique()); - cornerKicks.push_back(std::make_unique()); - cornerKicks.push_back(std::make_unique()); - - goalKick_Kicks.push_back(std::make_unique()); - goalKick_Kicks.push_back(std::make_unique()); - goalKick_Kicks.push_back(std::make_unique()); - - penaltyKicks.push_back(std::make_unique()); -} - -void BallchaserProvider::update(Ballchaser& ballchaser) -{ - DECLARE_DEBUG_DRAWING(DRAW_KICK_MANAGER_ACTIVATE_RANGE_NAME, "drawingOnField"); - DECLARE_DEBUG_DRAWING(DRAW_KICK_RANGE_NAME, "drawingOnField"); - DECLARE_DEBUG_DRAWING(DRAW_KICK_DANGER_NAME, "drawingOnField"); - DECLARE_DEBUG_DRAWING("behavior:BallchaserProvider:KickManager:Blocked", "drawingOnField"); - DECLARE_DEBUG_DRAWING("behavior:BallchaserProvider:KickManager:TargetFree", "drawingOnField"); - DECLARE_DEBUG_DRAWING(DRAW_EXECUTABLE_KICKS_IN_GRID, "drawingOnField"); - DECLARE_DEBUG_DRAWING(DRAW_EXECUTABLE_KICKS_FREELY, "drawingOnField"); - DECLARE_DEBUG_DRAWING(DRAW_EXECUTABLE_KICK_TARGET_AREA, "drawingOnField"); - DECLARE_DEBUG_DRAWING(DRAW_KICK_MIN_WIDTH, "drawingOnField"); - - // Reset - ballchaser.kickType = MotionRequest::KickType::walkKick; - ballchaser.walkKickType = WalkRequest::StepRequest::none; - danger = Danger::IMPOSSIBLE; - // Reset Logs - ballchaser.log_currState = "None"; - ballchaser.log_currObj = "None"; - ballchaser.log_danger = "None"; - ballchaser.log_kickName = "None"; - logger.clear(); - - // Update - updateVariables(); - setPlayKickManager.update(ballPosition, theRobotPoseAfterPreview, theWalkingEngineParams); - - // Set - ballchaser.stopAtTarget = false; - decide(ballchaser, theBallSymbols, theFieldDimensions, theGameInfo, theGameSymbols); - ballchaser.previewArrival = previewArrival; - // Set Logs - ballchaser.log_toBallDistance = std::to_string(Geometry::distance(theRobotPoseAfterPreview.translation, ballPosition)); - ballchaser.log_obj1 = logger.get(0); - ballchaser.log_obj2 = logger.get(1); - ballchaser.log_obj3 = logger.get(2); - ballchaser.log_obj4 = logger.get(3); - ballchaser.log_obj5 = logger.get(4); -} - -void BallchaserProvider::stateReady_kickOff_own(Ballchaser& positioningSymbols, const Vector2f& kickOffPosition) -{ - PositionUtils::setPosition(positioningSymbols, -500.f, 0); - PositionUtils::turnToPosition(positioningSymbols, kickOffPosition); - ThresholdUtils::setThreshholdsLow(positioningSymbols); -} - -void BallchaserProvider::statePlaying_kickOff_own(Ballchaser& positioningSymbols, const Vector2f& kickOffPosition) -{ - ExecutableKicks executableKicks = - setPlayKickManager.getExecutableKicks(theRobotPoseAfterPreview, ballPosition, KickUtils::unpack(kickOffKicks), theFieldDimensions, theHeatMapCollection, theRobotMap) - .filterTooFarBack(-500.f) - .filterOutside(theFieldDimensions) - .filterBlocked(theFieldDimensions, theRobotMap) - .reduceToBest(0.f, 0.f, 1.f, 0.f, 0.5f, 0.f, 0.f, 1.f, 0.f, -0.5f, -0.5f, kickWithLeft, theFieldDimensions, theHeatMapCollection, theRobotMap); - if (executableKicks.hasBest()) - { - setPlayKickManager.kickTo(positioningSymbols, executableKicks); - } - else - { - // TODO Warning only if ballchaser if (theRoleSymbols.role == Ballchaser) - //OUTPUT_WARNING("KickOff not possible!"); - } -} - -void BallchaserProvider::stateReady_kickOff_opponent(Ballchaser& positioningSymbols, const Vector2f& kickOffPosition) -{ - positioningSymbols.optPosition.translation.x() = -theFieldDimensions.centerCircleRadius - theBehaviorConfiguration.behaviorParameters.kickOffLineDistance; - positioningSymbols.optPosition.translation.y() = 0; - positioningSymbols.optPosition.rotation = 0; - positioningSymbols.thresholdXFront = 50; - positioningSymbols.thresholdXBack = 50; - positioningSymbols.thresholdY = 25; -} - -float BallchaserProvider::goalKick_own(Ballchaser& positioningSymbols, bool left) -{ - const Vector2f waitPos = Vector2f(theFieldDimensions.xPosOwnGoalArea - 300.f, left ? theFieldDimensions.yPosLeftGoalArea : theFieldDimensions.yPosRightGoalArea); - const float minX = theFieldDimensions.xPosOwnGoalArea + 300.f; - freeKick(positioningSymbols, waitPos, minX, 0.5f); - return 0.f; -} - -float BallchaserProvider::goalKick_opponent(Ballchaser& positioningSymbols, bool left) -{ - const Vector2f targetToCover{theFieldDimensions.xPosOwnGroundline, - left ? (theFieldDimensions.yPosCenterGoal + theFieldDimensions.yPosRightGoal) / 2.f : (theFieldDimensions.yPosCenterGoal + theFieldDimensions.yPosLeftGoal) / 2.f}; - const Vector2f ballToOwnGoal{targetToCover - ballPosition}; - Vector2f position{ballPosition + ballToOwnGoal.normalized(oppSetPlayBallDistance)}; - position.x() = std::max(position.x(), theFieldDimensions.xPosOwnGroundline + 300.f); - - positioningSymbols.optPosition.translation = position; - positioningSymbols.optPosition.rotation = (ballPosition - position).angle(); - return 0.f; -} - -float BallchaserProvider::pushingFreeKick_own(Ballchaser& positioningSymbols) -{ - regularPlayObjectivesManager.executeObjective(positioningSymbols); - return 0.f; -} - -float BallchaserProvider::pushingFreeKick_opponent(Ballchaser& positioningSymbols) -{ - goalKick_opponent(positioningSymbols, ballOnLeftSide); // TODO - return 0.f; -} - -float BallchaserProvider::cornerKick_own(Ballchaser& positioningSymbols, const Vector2f& cornerKickPosition, bool left) -{ - Vector2f waitPos = Vector2f(theFieldDimensions.xPosOpponentGroundline + 200.f, left ? theFieldDimensions.yPosLeftSideline + 200.f : theFieldDimensions.yPosRightSideline - 200.f); - const float minX = theFieldDimensions.xPosOpponentPenaltyArea - 1000.f; - freeKick(positioningSymbols, waitPos, minX, 0.2f); - return 0.f; -} - -float BallchaserProvider::cornerKick_opponent(Ballchaser& positioningSymbols, const Vector2f& cornerKickPosition, bool left) -{ - Vector2f kickerPose; - int nmbrDetected = 0; - Vector2f ballPosition = theBallSymbols.ballPositionField; - for (RobotMapEntry robot : theRobotMap.robots) - { - Vector2f robotPose = robot.pose.translation; - if ((robotPose - ballPosition).norm() < 400.f && (abs(robotPose.y()) >= abs(theFieldDimensions.yPosLeftSideline) + 60.f && robotPose.x() < theFieldDimensions.xPosOwnGroundline + 20.f)) - { - kickerPose = robotPose; - nmbrDetected++; - } - } - if (nmbrDetected == 1) //TODO: time hysteresis - { - Vector2f kickerToBall = ballPosition - kickerPose; - kickerToBall.angle(); - float distanceFromKickerToBall = (kickerToBall).norm(); - Vector2f targetPos = ballPosition + kickerToBall * oppSetPlayBallDistance / distanceFromKickerToBall; - positioningSymbols.optPosition.translation = targetPos; - PositionUtils::turnTowardsBall(positioningSymbols, theBallSymbols); - } - else - { - goalKick_opponent(positioningSymbols, left); - } - - return 0.f; -} - -float BallchaserProvider::kickIn_own(Ballchaser& positioningSymbols, bool left) //TODO: Wait for 10 seconds if time permits before shooting. Longer if we are winning. RoleProvider. 1vs1. -{ - Vector2f waitPos = Vector2f(theBallSymbols.ballPositionField.x(), left ? theFieldDimensions.yPosLeftSideline + 300.f : theFieldDimensions.yPosRightSideline - 300.f); - const float minX = MathUtils::clamp_f(ballPosition.x() - 500.f, theFieldDimensions.xPosOwnPenaltyArea + 200.f, theFieldDimensions.xPosOpponentGoalArea - 200.f); - freeKick(positioningSymbols, waitPos, minX, 0.3f); - return 0.f; -} - -float BallchaserProvider::kickIn_opponent(Ballchaser& positioningSymbols, bool left) -{ - Vector2f kickerPose; - int nmbrDetected = 0; - Vector2f ballPosition = theBallSymbols.ballPositionField; - for (RobotMapEntry robot : theRobotMap.robots) - { - Vector2f robotPose = robot.pose.translation; - if ((robotPose - ballPosition).norm() < 400.f && abs(robotPose.y()) >= abs(theFieldDimensions.yPosLeftSideline) - 20.f) - { - kickerPose = robotPose; - nmbrDetected++; - } - } - if (nmbrDetected == 1) //TODO: time hysteresis - { - Vector2f kickerToBall = ballPosition - kickerPose; - kickerToBall.angle(); - float distanceFromKickerToBall = (kickerToBall).norm(); - Vector2f targetPos = ballPosition + kickerToBall * oppSetPlayBallDistance / distanceFromKickerToBall; - positioningSymbols.optPosition.translation = targetPos; - PositionUtils::turnTowardsBall(positioningSymbols, theBallSymbols); - } - else - { - goalKick_opponent(positioningSymbols, left); - } - - return 0.f; -} - -float BallchaserProvider::stateReady_penaltyKick_own(Ballchaser& ballchaser) -{ - const Vector2f penaltyMarkPosition = {theFieldDimensions.xPosOpponentPenaltyMark, 0.f}; // TODo put Vector into theFieldDimensions - const Vector2f position = {penaltyMarkPosition.x() - 200.f, penaltyMarkPosition.y()}; - ThresholdUtils::setThreshholdsMedium(ballchaser); - PositionUtils::setPosition(ballchaser, position); - PositionUtils::turnToPosition(ballchaser, position); - return 0.f; -} - -float BallchaserProvider::statePlaying_penaltyKick_own(Ballchaser& ballchaser) -{ - const Vector2f leftGoalPostPosition = {theFieldDimensions.xPosOpponentGroundline, theFieldDimensions.yPosLeftGoal - theFieldDimensions.goalPostRadius}; - const Vector2f rightGoalPostPosition = {theFieldDimensions.xPosOpponentGroundline, theFieldDimensions.yPosRightGoal + theFieldDimensions.goalPostRadius}; - ExecutableKicks executableKicks = - setPlayKickManager - .getExecutableKicks(theRobotPoseAfterPreview, {ballPosition, leftGoalPostPosition, rightGoalPostPosition, DistanceRequirement::mustFurther}, 10, KickUtils::unpack(penaltyKicks), theFieldDimensions, theHeatMapCollection, theRobotMap) - .filterBlocked(theFieldDimensions, theRobotMap) - .reduceToBest(0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, kickWithLeft, theFieldDimensions, theHeatMapCollection, theRobotMap); - if (executableKicks.hasBest()) - { - setPlayKickManager.kickTo(ballchaser, executableKicks); - } - else - { - // TODO Warning only if Ballchaser - } - return 0.f; -} - -float BallchaserProvider::stateReady_penaltyKick_opponent(Ballchaser& positioningSymbols) -{ - positioningSymbols.optPosition.translation.x() = theFieldDimensions.xPosOwnPenaltyArea + penaltyKickOpponentPositionX; - positioningSymbols.optPosition.translation.y() = theFieldDimensions.yPosKickOffPoint + penaltyKickOpponentPositionY; - const Vector2f penaltyCross{theFieldDimensions.xPosOwnPenaltyMark, theFieldDimensions.yPosKickOffPoint}; - positioningSymbols.optPosition.rotation = (penaltyCross - positioningSymbols.optPosition.translation).angle(); - return 0.f; -} - -void BallchaserProvider::regularPlay(Ballchaser& positioningSymbols) -{ - /* TODO - if (goalKeepegehtZumBall und verbündeter Roboter ist nahe Ball) - { - stelle Tor zu; mit Kopf zum ball; - return; - } - */ - updateDanger(positioningSymbols); - regularPlayObjectivesManager.executeObjective(positioningSymbols); -} - -bool BallchaserProvider::waitInOwnSetPlay(Ballchaser& positioningSymbols, const Vector2f& waitingPosition) -{ - const int MAX_WAIT_TIME = 8; // TODO Constant - const int MAX_LOOSING_WAIT_TIME = 5; - const float WAIT_POSE_THRESHOLD = 100.f; - bool winning = theOwnTeamInfo.score > theOpponentTeamInfo.score; - int timeRemaining = 30 /* TODO Constant */ - theGameSymbols.timeSinceSetPlayStarted; - - if (timeRemaining >= 29) - { - timeArrivedInSetPlayWaiting = 0; - } - - if (timeRemaining > MAX_WAIT_TIME && (winning || timeArrivedInSetPlayWaiting - timeRemaining < MAX_LOOSING_WAIT_TIME)) - { - // TODO Head Control - positioningSymbols.stopAtTarget = true; - - PositionUtils::setPosition(positioningSymbols, waitingPosition); - PositionUtils::turnTowardsBall(positioningSymbols, theBallSymbols); - if (timeArrivedInSetPlayWaiting == 0 && (theRobotPose.translation - waitingPosition).norm() < WAIT_POSE_THRESHOLD) - { - timeArrivedInSetPlayWaiting = timeRemaining; - } - return true; - } - positioningSymbols.stopAtTarget = false; - return false; -} - -void BallchaserProvider::freeKick(Ballchaser& ballchaser, const Vector2f& waitPosition, const float minX, const float opponentGoalHeatFactor) -{ - if (waitInOwnSetPlay(ballchaser, waitPosition)) - { - return; - } - ExecutableKicks executableKicks = - setPlayKickManager.getExecutableKicks(theRobotPoseAfterPreview, ballPosition, KickUtils::unpack(kickInKicks), theFieldDimensions, theHeatMapCollection, theRobotMap) - .filterTooFarBack(minX) - .filterOutside(theFieldDimensions) // TODO Also filter opponent Goal!!! - .filterBlocked(theFieldDimensions, theRobotMap) - .reduceToBest(0.f, 0.f, 0.5f, 0.f, opponentGoalHeatFactor, 0.f, 0.1f, 1.f, 0.f, -0.5f, -0.5f, kickWithLeft, theFieldDimensions, theHeatMapCollection, theRobotMap); - if (executableKicks.hasBest()) - { - setPlayKickManager.kickTo(ballchaser, executableKicks); - } - else - { - // TODO Nur ausgeben wenn ballchaser - //OUTPUT_WARNING("Free kick not possible!"); - } -} - -// update to avoid hysteresis ============================================================================================================================================= - -void BallchaserProvider::updateVariables() -{ - updateBallPosition(nullptr); - - if (ballOnLeftSide && ballPosition.y() < -250.f) - ballOnLeftSide = false; - else if (!ballOnLeftSide && ballPosition.y() > 250.f) - ballOnLeftSide = true; - - updateKickWithLeft(); -} - -void BallchaserProvider::updateKickWithLeft() -{ - const Pose2f leftFoot = Pose2f(theRobotPoseAfterPreview).translate(0.f, theWalkingEngineParams.footMovement.footYDistance); - const Pose2f rightFoot = Pose2f(theRobotPoseAfterPreview).translate(0.f, -theWalkingEngineParams.footMovement.footYDistance); - - const float leftDistance = (ballPosition - leftFoot.translation).norm(); - const float rightDistance = (ballPosition - rightFoot.translation).norm(); - - if (kickWithLeft && rightDistance < leftDistance - footDecisionHysteresis) - kickWithLeft = false; - else if (!kickWithLeft && leftDistance < rightDistance - footDecisionHysteresis) - kickWithLeft = true; -} - -void BallchaserProvider::updateBallPosition(const Vector2f* useBallPosition) -{ - if (useBallPosition == nullptr) - { - ballPosition = BallUtils::getBallPosition(usePredictedBallPosition, theBallSymbols, theFrameInfo); - } - else - { - ballPosition = *useBallPosition; - } -} - -void BallchaserProvider::updateDanger(Ballchaser& ballchaser) -{ - if (DangerUtils::isDanger(theRobotPoseAfterPreview.translation, ballPosition, 1.f, danger == Danger::HIGH, theDangerMap, theFieldDimensions, theRobotMap)) - { - ballchaser.log_danger = "High"; - danger = Danger::HIGH; - return; - } - - if (DangerUtils::isDanger(theRobotPoseAfterPreview.translation, ballPosition, 1.5f, danger == Danger::MEDIUM, theDangerMap, theFieldDimensions, theRobotMap)) - { - ballchaser.log_danger = "Medium"; - danger = Danger::MEDIUM; - return; - } - - if (DangerUtils::isDanger(theRobotPoseAfterPreview.translation, ballPosition, 2.0f, danger == Danger::LOW, theDangerMap, theFieldDimensions, theRobotMap)) - { - ballchaser.log_danger = "Low"; - danger = Danger::LOW; - return; - } - - ballchaser.log_danger = "None"; - danger = Danger::NONE; -} - -MAKE_MODULE(BallchaserProvider, behaviorControl) diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/CenterProvider.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/CenterProvider.cpp new file mode 100644 index 00000000..0a988219 --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/CenterProvider.cpp @@ -0,0 +1,118 @@ +/** +* @file CenterProvider.cpp +* +* Implementation of class CenterProvider. +* +*/ + +#include "CenterProvider.h" +#include "Tools/Debugging/DebugDrawings3D.h" +#include "Utils/PositionUtils.h" +#include "Utils/ThresholdUtils.h" +#include "Utils/MathUtils.h" + +void CenterProvider::update(Center& role) +{ + role.stopAtTarget = true; + + decide(role, theBallSymbols, theFieldDimensions, theGameInfo, theGameSymbols); +} + +void CenterProvider::stateReady_kickOff_own(Center& role, const Vector2f& ballPosition) +{ + regularPlay(role); +} + +void CenterProvider::stateReady_kickOff_opponent(Center& role, const Vector2f& ballPosition) +{ + ThresholdUtils::setThreshholdsHeigh(role); + const float x = -theFieldDimensions.xPosOpponentPenaltyArea / 1.2f; + const float y = 500.f; + PositionUtils::setPosition(role, x, y); + PositionUtils::turnTowardsBall(role, theBallSymbols); +} + +float CenterProvider::goalKick_own(Center& role, bool left) +{ + regularPlay(role); + return 0; +} + +float CenterProvider::goalKick_opponent(Center& role, bool left) +{ + regularPlay(role); + return 0; +} + +float CenterProvider::pushingFreeKick_own(Center& role) +{ + regularPlay(role); + return 0; +} + +float CenterProvider::pushingFreeKick_opponent(Center& role) +{ + regularPlay(role); + return 0; +} + +float CenterProvider::cornerKick_own(Center& role, const Vector2f& cornerKickPosition, bool left) +{ + regularPlay(role); + return 0; +} + +float CenterProvider::cornerKick_opponent(Center& role, const Vector2f& cornerKickPosition, bool left) +{ + regularPlay(role); + return 0; +} + +float CenterProvider::kickIn_own(Center& role, bool left) +{ + regularPlay(role); + return 0; +} + +float CenterProvider::kickIn_opponent(Center& role, bool left) +{ + regularPlay(role); + return 0; +} + +float CenterProvider::stateReady_penaltyKick_own(Center& role) +{ + regularPlay(role); + return 0; +} + +float CenterProvider::stateReady_penaltyKick_opponent(Center& role) +{ + bool robotIsLeft = (theRobotPose.translation.y() >= 0.f); + float targetPosY = (robotIsLeft ? 1.f : -1.f) * (theFieldDimensions.yPosLeftPenaltyArea + 400.f); + + role.optPosition.translation.x() = theFieldDimensions.xPosOwnPenaltyArea - theFieldDimensions.xPosOwnPenaltyArea / 2; + role.optPosition.translation.y() = targetPosY; + PositionUtils::turnTowardsBall(role, theBallSymbols); + + role.thresholdRotation = 10_deg; + role.thresholdXBack = 50.f; + role.thresholdXFront = 100.f; + role.thresholdY = 100.f; + role.stopAtTarget = true; + role.previewArrival = true; + return 0; +} + +void CenterProvider::regularPlay(Center& role) +{ + ThresholdUtils::setThreshholdsExtremeHeigh(role, theTacticSymbols.activity); + + PositionUtils::turnTowardsBall(role, theBallSymbols); + + const float x = MathUtils::clamp_f(theBallSymbols.ballPositionField.x() - 1000.f, theFieldDimensions.xPosOwnPenaltyArea + 500.f, theFieldDimensions.xPosOpponentPenaltyArea); + const float y = 500.f; + PositionUtils::setPosition(role, x, y); +} + +MAKE_MODULE(CenterProvider, behaviorControl) diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/CenterProvider.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/CenterProvider.h new file mode 100644 index 00000000..bbbb7b9d --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/CenterProvider.h @@ -0,0 +1,76 @@ +#pragma once + +#include +#include "Tools/Module/Module.h" +#include "Modules/BehaviorControl/CABSL/BehaviorParameters.h" +#include "Representations/Configuration/FieldDimensions.h" +#include "Representations/BehaviorControl/BallSymbols.h" +#include "Representations/BehaviorControl/GameSymbols.h" +#include "Representations/BehaviorControl/GoalSymbols.h" +#include "Representations/BehaviorControl/RoleSelection.h" +#include "Representations/BehaviorControl/BallChaserDecision.h" +#include "Representations/BehaviorControl/BehaviorConfiguration.h" +#include "Representations/BehaviorControl/RoleSymbols/Center.h" +#include "Representations/BehaviorControl/RoleSymbols/Receiver.h" +#include "Representations/BehaviorControl/RoleSymbols/Ballchaser.h" +#include "Representations/BehaviorControl/RoleSymbols/BallchaserKeeper.h" +#include "Representations/BehaviorControl/RoleSymbols/PositioningSymbols.h" +#include "Representations/BehaviorControl/TacticSymbols.h" +#include "Representations/Infrastructure/FrameInfo.h" +#include "Representations/Infrastructure/GameInfo.h" +#include "Representations/Infrastructure/RobotInfo.h" +#include "Representations/Infrastructure/TeamInfo.h" +#include "Representations/Infrastructure/TeammateData.h" +#include "Representations/BehaviorControl/BehaviorData.h" +#include "Representations/Modeling/DangerMap.h" +#include "Representations/Modeling/RobotMap.h" +#include "Representations/Modeling/RobotPose.h" +#include "Representations/MotionControl/WalkingEngineParams.h" +#include "Tools/Settings.h" +#include "RoleProvider.h" + +MODULE(CenterProvider, + REQUIRES(BallSymbols), + REQUIRES(BehaviorConfiguration), + REQUIRES(DangerMap), + REQUIRES(FrameInfo), + REQUIRES(FieldDimensions), + REQUIRES(GameInfo), + REQUIRES(GameSymbols), + REQUIRES(GoalSymbols), + REQUIRES(OwnTeamInfo), + REQUIRES(OpponentTeamInfo), + REQUIRES(RobotInfo), + REQUIRES(RobotMap), + REQUIRES(RobotPose), + REQUIRES(RobotPoseAfterPreview), + REQUIRES(RoleSelection), + REQUIRES(Receiver), + REQUIRES(TeammateData), + REQUIRES(TacticSymbols), + REQUIRES(WalkingEngineParams), + USES(PositioningSymbols), + PROVIDES(Center) +); + +class CenterProvider : public CenterProviderBase, public RoleProvider
+{ + +public: + void update(Center& role) override; + +private: + void stateReady_kickOff_own(Center& role, const Vector2f& ballPosition) override; + void stateReady_kickOff_opponent(Center& role, const Vector2f& ballPosition) override; + float goalKick_own(Center& role, bool left) override; + float goalKick_opponent(Center& role, bool left) override; + float pushingFreeKick_own(Center& role) override; + float pushingFreeKick_opponent(Center& role) override; + float cornerKick_own(Center& role, const Vector2f& cornerKickPosition, bool left) override; + float cornerKick_opponent(Center& role, const Vector2f& cornerKickPosition, bool left) override; + float kickIn_own(Center& role, bool left) override; + float kickIn_opponent(Center& role, bool left) override; + float stateReady_penaltyKick_own(Center& role) override; + float stateReady_penaltyKick_opponent(Center& role) override; + void regularPlay(Center& role) override; +}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/DefenderLeftProvider.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/DefenderLeftProvider.cpp index 215f1fcf..d8dd2589 100644 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/DefenderLeftProvider.cpp +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/DefenderLeftProvider.cpp @@ -12,12 +12,28 @@ void DefenderLeftProvider::update(DefenderLeft& positioningSymbols) // stay in one spot if ball is far away from own goal getStandardPosition(positioningSymbols); + // leave penalty area for penalty kick + if (theGameInfo.setPlay == SET_PLAY_PENALTY_KICK && !theGameSymbols.ownKickOff) + { + positioningSymbols.optPosition.translation.x() = theFieldDimensions.xPosOwnPenaltyArea + theFieldDimensions.xPosOwnPenaltyArea / 3; + positioningSymbols.optPosition.translation.y() = theFieldDimensions.yPosLeftPenaltyArea + 400.f; + positioningSymbols.optPosition.rotation = (Vector2f(theFieldDimensions.xPosOwnGroundline, 0.f) - positioningSymbols.optPosition.translation).angle(); + + positioningSymbols.thresholdRotation = 10_deg; + positioningSymbols.thresholdXBack = 50.f; + positioningSymbols.thresholdXFront = 100.f; + positioningSymbols.thresholdY = 100.f; + positioningSymbols.stopAtTarget = true; + positioningSymbols.previewArrival = true; + return; + } + // only check for alternative Position when in playing, otherwise keep standard position if (theGameInfo.state == STATE_PLAYING) { - // if set play is ongoing, get to predefined position - if (theGameInfo.setPlay != SET_PLAY_NONE) + if (theGameInfo.setPlay != SET_PLAY_NONE && theGameInfo.setPlay != SET_PLAY_PENALTY_KICK) { + // if set play is ongoing, get to predefined position if (calculateSetPlayPosition(positioningSymbols)) return; } @@ -96,8 +112,8 @@ bool DefenderLeftProvider::calculateSetPlayPosition(DefenderLeft& positioningSym else // wrong side of the ball for default behavior { useStandardPosition = false; - positioningSymbols.optPosition.translation = theRobotPoseAfterPreview.translation + vectorFromBallPosition.normalize(minDistanceToBall); - positioningSymbols.optPosition.rotation = (positioningSymbols.optPosition.translation - theBallSymbols.ballPositionField).angle(); + //positioningSymbols.optPosition.translation = theRobotPoseAfterPreview.translation + vectorFromBallPosition.normalize(minDistanceToBall); + //positioningSymbols.optPosition.rotation = (positioningSymbols.optPosition.translation - theBallSymbols.ballPositionField).angle(); } } positioningSymbols.thresholdRotation = 10_deg; @@ -194,4 +210,4 @@ void DefenderLeftProvider::avoidBallchaserConflict(DefenderLeft& positioningSymb } -MAKE_MODULE(DefenderLeftProvider, behaviorControl) \ No newline at end of file +MAKE_MODULE(DefenderLeftProvider, behaviorControl) diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/DefenderRightProvider.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/DefenderRightProvider.cpp index 730f438d..29ea19e3 100644 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/DefenderRightProvider.cpp +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/DefenderRightProvider.cpp @@ -12,12 +12,28 @@ void DefenderRightProvider::update(DefenderRight& positioningSymbols) // stay in one spot if ball is far away from own goal getStandardPosition(positioningSymbols); + // leave penalty area for penalty kick + if (theGameInfo.setPlay == SET_PLAY_PENALTY_KICK && !theGameSymbols.ownKickOff) + { + positioningSymbols.optPosition.translation.x() = theFieldDimensions.xPosOwnPenaltyArea + theFieldDimensions.xPosOwnPenaltyArea / 3; + positioningSymbols.optPosition.translation.y() = theFieldDimensions.yPosRightPenaltyArea - 400.f; + positioningSymbols.optPosition.rotation = (Vector2f(theFieldDimensions.xPosOwnGroundline, 0.f) - positioningSymbols.optPosition.translation).angle(); + + positioningSymbols.thresholdRotation = 10_deg; + positioningSymbols.thresholdXBack = 50.f; + positioningSymbols.thresholdXFront = 100.f; + positioningSymbols.thresholdY = 100.f; + positioningSymbols.stopAtTarget = true; + positioningSymbols.previewArrival = true; + return; + } + // only check for alternative Position when in playing, otherwise keep standard position if (theGameInfo.state == STATE_PLAYING) { - // if set play is ongoing, get to predefined position - if (theGameInfo.setPlay != SET_PLAY_NONE) + if (theGameInfo.setPlay != SET_PLAY_NONE && theGameInfo.setPlay != SET_PLAY_PENALTY_KICK) { + // if set play is ongoing, get to predefined position if (calculateSetPlayPosition(positioningSymbols)) return; } @@ -94,8 +110,8 @@ bool DefenderRightProvider::calculateSetPlayPosition(DefenderRight& positioningS else // wrong side of the ball for default behavior { useStandardPosition = false; - positioningSymbols.optPosition.translation = theRobotPoseAfterPreview.translation + vectorFromBallPosition.normalize(minDistanceToBall); - positioningSymbols.optPosition.rotation = (positioningSymbols.optPosition.translation - theBallSymbols.ballPositionField).angle(); + // positioningSymbols.optPosition.translation = theRobotPoseAfterPreview.translation + vectorFromBallPosition.normalize(minDistanceToBall); + //positioningSymbols.optPosition.rotation = (positioningSymbols.optPosition.translation - theBallSymbols.ballPositionField).angle(); } } positioningSymbols.thresholdRotation = 10_deg; @@ -191,4 +207,4 @@ void DefenderRightProvider::avoidBallchaserConflict(DefenderRight& positioningSy } } -MAKE_MODULE(DefenderRightProvider, behaviorControl) \ No newline at end of file +MAKE_MODULE(DefenderRightProvider, behaviorControl) diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/DefenderSingleProvider.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/DefenderSingleProvider.cpp index 3bfc4409..0b48c19b 100644 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/DefenderSingleProvider.cpp +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/DefenderSingleProvider.cpp @@ -137,7 +137,7 @@ float DefenderSingleProvider::stateReady_penaltyKick_opponent(DefenderSingle& po //Get out of own penalty area float targetPosX = theFieldDimensions.xPosOwnGoalArea; bool robotIsLeft = (theRobotPose.translation.y() >= 0.f); - float targetPosY = (robotIsLeft ? 1.f : -1.f) * (theFieldDimensions.yPosLeftPenaltyArea + 200.f); + float targetPosY = (robotIsLeft ? 1.f : -1.f) * (theFieldDimensions.yPosLeftPenaltyArea + 400.f); Angle targetRotation = robotIsLeft ? -90_deg : 90_deg; positioningSymbols.optPosition.translation = Vector2f(targetPosX, targetPosY); positioningSymbols.optPosition.rotation = targetRotation; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/DefenderSingleProvider.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/DefenderSingleProvider.h index 455d9c10..954e718d 100644 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/DefenderSingleProvider.h +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/DefenderSingleProvider.h @@ -27,7 +27,6 @@ #include "Utils/PositionUtils.h" #include "Utils/TeamUtils.h" #include "Utils/ThresholdUtils.h" -#include "Utils/FrontUtils.h" #include "RoleProvider.h" #include diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/FrontWingProvider.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/FrontWingProvider.cpp new file mode 100644 index 00000000..4c281e2a --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/FrontWingProvider.cpp @@ -0,0 +1,102 @@ +#include "FrontWingProvider.h" +#include "Tools/Debugging/DebugDrawings3D.h" +#include "Utils/PositionUtils.h" +#include "Utils/ThresholdUtils.h" +#include "Utils/MathUtils.h" + +void FrontWingProvider::update(FrontWing& role) +{ + role.stopAtTarget = true; + + decide(role, theBallSymbols, theFieldDimensions, theGameInfo, theGameSymbols); +} + +void FrontWingProvider::stateReady_kickOff_own(FrontWing& role, const Vector2f& ballPosition) +{ + PositionUtils::setPosition(role, -500.f, 0); + PositionUtils::turnToPosition(role, ballPosition); + ThresholdUtils::setThreshholdsLow(role); +} + +void FrontWingProvider::stateReady_kickOff_opponent(FrontWing& role, const Vector2f& ballPosition) +{ + role.optPosition.translation.x() = -theFieldDimensions.centerCircleRadius - theBehaviorConfiguration.behaviorParameters.kickOffLineDistance; + role.optPosition.translation.y() = 0; + role.optPosition.rotation = 0; + role.thresholdXFront = 50; + role.thresholdXBack = 50; + role.thresholdY = 25; +} + +float FrontWingProvider::goalKick_own(FrontWing& role, bool left) +{ + regularPlay(role); + return 0.f; +} + +float FrontWingProvider::goalKick_opponent(FrontWing& role, bool left) +{ + regularPlay(role); + return 0; +} + +float FrontWingProvider::pushingFreeKick_own(FrontWing& role) +{ + regularPlay(role); + return 0; +} + +float FrontWingProvider::pushingFreeKick_opponent(FrontWing& role) +{ + regularPlay(role); + return 0; +} + +float FrontWingProvider::cornerKick_own(FrontWing& role, const Vector2f& cornerKickPosition, bool left) +{ + regularPlay(role); + return 0; +} + +float FrontWingProvider::cornerKick_opponent(FrontWing& role, const Vector2f& cornerKickPosition, bool left) +{ + regularPlay(role); + return 0; +} + +float FrontWingProvider::kickIn_own(FrontWing& role, bool left) +{ + regularPlay(role); + return 0; +} + +float FrontWingProvider::kickIn_opponent(FrontWing& role, bool left) +{ + regularPlay(role); + return 0; +} + +float FrontWingProvider::stateReady_penaltyKick_own(FrontWing& role) +{ + regularPlay(role); + return 0; +} + +float FrontWingProvider::stateReady_penaltyKick_opponent(FrontWing& role) +{ + regularPlay(role); + return 0; +} + +void FrontWingProvider::regularPlay(FrontWing& role) +{ + ThresholdUtils::setThreshholdsExtremeHeigh(role, theTacticSymbols.activity); + + PositionUtils::turnTowardsBall(role, theBallSymbols); + + const float x = MathUtils::clamp_f(theBallSymbols.ballPositionField.x() + 2000.f, -theFieldDimensions.xPosOpponentPenaltyArea / 2, theFieldDimensions.xPosOpponentPenaltyArea); + const float y = -500.f; + PositionUtils::setPosition(role, x, y); +} + +MAKE_MODULE(FrontWingProvider, behaviorControl) diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/FrontWingProvider.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/FrontWingProvider.h new file mode 100644 index 00000000..5fe7da12 --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/FrontWingProvider.h @@ -0,0 +1,76 @@ +#pragma once + +#include +#include "Tools/Module/Module.h" +#include "Modules/BehaviorControl/CABSL/BehaviorParameters.h" +#include "Representations/Configuration/FieldDimensions.h" +#include "Representations/BehaviorControl/BallSymbols.h" +#include "Representations/BehaviorControl/GameSymbols.h" +#include "Representations/BehaviorControl/GoalSymbols.h" +#include "Representations/BehaviorControl/RoleSelection.h" +#include "Representations/BehaviorControl/BallChaserDecision.h" +#include "Representations/BehaviorControl/BehaviorConfiguration.h" +#include "Representations/BehaviorControl/RoleSymbols/FrontWing.h" +#include "Representations/BehaviorControl/RoleSymbols/Receiver.h" +#include "Representations/BehaviorControl/RoleSymbols/Ballchaser.h" +#include "Representations/BehaviorControl/RoleSymbols/BallchaserKeeper.h" +#include "Representations/BehaviorControl/RoleSymbols/PositioningSymbols.h" +#include "Representations/BehaviorControl/TacticSymbols.h" +#include "Representations/Infrastructure/FrameInfo.h" +#include "Representations/Infrastructure/GameInfo.h" +#include "Representations/Infrastructure/RobotInfo.h" +#include "Representations/Infrastructure/TeamInfo.h" +#include "Representations/Infrastructure/TeammateData.h" +#include "Representations/BehaviorControl/BehaviorData.h" +#include "Representations/Modeling/DangerMap.h" +#include "Representations/Modeling/RobotMap.h" +#include "Representations/Modeling/RobotPose.h" +#include "Representations/MotionControl/WalkingEngineParams.h" +#include "Tools/Settings.h" +#include "RoleProvider.h" + +MODULE(FrontWingProvider, + REQUIRES(BallSymbols), + REQUIRES(BehaviorConfiguration), + REQUIRES(DangerMap), + REQUIRES(FrameInfo), + REQUIRES(FieldDimensions), + REQUIRES(GameInfo), + REQUIRES(GameSymbols), + REQUIRES(GoalSymbols), + REQUIRES(OwnTeamInfo), + REQUIRES(OpponentTeamInfo), + REQUIRES(RobotInfo), + REQUIRES(RobotMap), + REQUIRES(RobotPose), + REQUIRES(RobotPoseAfterPreview), + REQUIRES(RoleSelection), + REQUIRES(Receiver), + REQUIRES(TeammateData), + REQUIRES(TacticSymbols), + REQUIRES(WalkingEngineParams), + USES(PositioningSymbols), + PROVIDES(FrontWing) +); + +class FrontWingProvider : public FrontWingProviderBase, public RoleProvider +{ + +public: + void update(FrontWing& role) override; + +private: + void stateReady_kickOff_own(FrontWing& role, const Vector2f& ballPosition) override; + void stateReady_kickOff_opponent(FrontWing& role, const Vector2f& ballPosition) override; + float goalKick_own(FrontWing& role, bool left) override; + float goalKick_opponent(FrontWing& role, bool left) override; + float pushingFreeKick_own(FrontWing& role) override; + float pushingFreeKick_opponent(FrontWing& role) override; + float cornerKick_own(FrontWing& role, const Vector2f& cornerKickPosition, bool left) override; + float cornerKick_opponent(FrontWing& role, const Vector2f& cornerKickPosition, bool left) override; + float kickIn_own(FrontWing& role, bool left) override; + float kickIn_opponent(FrontWing& role, bool left) override; + float stateReady_penaltyKick_own(FrontWing& role) override; + float stateReady_penaltyKick_opponent(FrontWing& role) override; + void regularPlay(FrontWing& role) override; +}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/HeatMapProvider/HeatMapProvider.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/HeatMapProvider/HeatMapProvider.cpp new file mode 100644 index 00000000..0550187b --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/HeatMapProvider/HeatMapProvider.cpp @@ -0,0 +1,81 @@ +#include "HeatMapProvider.h" +#include "Tools/Math/Transformation.h" +#include +#include +#include +#include + +HeatMapProvider::HeatMapProvider() = default; + +void draw(std::vector positionVector, std::vector heatVector); + +void HeatMapProvider::execute(tf::Subflow& subflow) +{ + const auto [teammateRobots, opponentRobots] = HeatMapUtils::getTeammateAndOtherRobots(theRobotMap); + + subflow + .for_each_index(0, + HeatMap::CELL_COUNT, + 1, + [&, teammateRobots = teammateRobots, opponentRobots = opponentRobots](const int index) + { + const Vector2f fieldPosition = HeatMap::indexToField(index, theFieldDimensions); + + if (firstUpdate) + { + localHeatMapCollection.sidesHeatMap.setHeat(std::pow(HeatMapUtils::getSidesHeat(fieldPosition, theFieldDimensions), 2.f), index, theFieldDimensions); + localHeatMapCollection.goalsHeatMap.setHeat(HeatMapUtils::getGoalsHeat(fieldPosition, theFieldDimensions), index, theFieldDimensions); + } + + const auto [teammatesKickHeat, teammatesGoalKickHeat] = + HeatMapUtils::getRobotHeatForPosition(fieldPosition, teammateRobots, FieldUtils::getOpponentGoalCenter(theFieldDimensions), theFieldDimensions); + const auto [opponentsKickHeat, opponentsGoalKickHeat] = + HeatMapUtils::getRobotHeatForPosition(fieldPosition, opponentRobots, FieldUtils::getOwnGoalCenter(theFieldDimensions), theFieldDimensions); + + + if (kickHeatTakeNewPercent > 0.99f) + { + teammatesKickHeatMap.setHeat(teammatesKickHeat, index, theFieldDimensions); + localHeatMapCollection.opponentKickHeatMap.setHeat(opponentsKickHeat, index, theFieldDimensions); + } + else + { + teammatesKickHeatMap.updateHeat(kickHeatTakeNewPercent, teammatesKickHeat, index, theFieldDimensions); + localHeatMapCollection.opponentKickHeatMap.updateHeat(kickHeatTakeNewPercent, opponentsKickHeat, index, theFieldDimensions); + } + + if (goalKickHeatTakeNewPercent > 0.99f) + { + teammatesGoalKickHeatMap.setHeat(teammatesGoalKickHeat, index, theFieldDimensions); + localHeatMapCollection.opponentGoalKickHeatMap.setHeat(opponentsGoalKickHeat, index, theFieldDimensions); + } + else + { + teammatesGoalKickHeatMap.updateHeat(goalKickHeatTakeNewPercent, teammatesGoalKickHeat, index, theFieldDimensions); + localHeatMapCollection.opponentGoalKickHeatMap.updateHeat(goalKickHeatTakeNewPercent, opponentsGoalKickHeat, index, theFieldDimensions); + } + + // Apply instant heat + const std::vector selfPose = {Pose2f(theRobotPose.rotation, theBallSymbols.ballPositionField)}; // Use Ball Position since it's the robots position when he will kick the ball + const auto [selfKickHeat, selfGoalKickHeat] = HeatMapUtils::getRobotHeatForPosition(fieldPosition, selfPose, FieldUtils::getOpponentGoalCenter(theFieldDimensions), theFieldDimensions); + const float teamKickHeat = std::max(teammatesKickHeatMap.getHeat(index), selfKickHeat); + const float teamGoalKickHeat = std::max(teammatesGoalKickHeatMap.getHeat(index), selfGoalKickHeat); + localHeatMapCollection.teamKickHeatMap.setHeat(teamKickHeat, index, theFieldDimensions); + localHeatMapCollection.teamGoalKickHeatMap.setHeat(teamGoalKickHeat, index, theFieldDimensions); + }) + .name("UpdateHeat [HeatMapProvider]"); +} + +void HeatMapProvider::update(HeatMapCollection& heatMapCollection) +{ + heatMapCollection = localHeatMapCollection; + firstUpdate = false; + + DECLARE_DEBUG_DRAWING(DRAW_HEAT_MAP, "drawingOnField"); + COMPLEX_DRAWING(DRAW_HEAT_MAP) + { + HeatMapUtils::draw(heatMapCollection.teamGoalKickHeatMap, true, theFieldDimensions); + } +} + +MAKE_MODULE(HeatMapProvider, modeling) diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KeeperProvider.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KeeperProvider.cpp index 69c8ba83..0cb53064 100644 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KeeperProvider.cpp +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KeeperProvider.cpp @@ -94,7 +94,7 @@ void KeeperProvider::regularPlay(Keeper& keeper) keeper.ballSearchState = Keeper::KeeperBallSearchState::wait; // default state searchForBall(keeper); } - else if (theBallChaserDecision.playerNumberToBall == 1 && theBallSymbols.ballInOwnPenaltyArea && theGameInfo.setPlay == SET_PLAY_NONE && theGameInfo.gamePhase != GAME_PHASE_PENALTYSHOOT) + else if (theBallChaserDecision.playerNumberToBall == 1 && theGameInfo.setPlay != SET_PLAY_PENALTY_KICK && theBallSymbols.ballInOwnPenaltyArea && theGameInfo.gamePhase != GAME_PHASE_PENALTYSHOOT) { keeperState = KeeperState::chaseBall; keeper.isBallchaser = true; @@ -147,12 +147,13 @@ void KeeperProvider::updateDecisionVariables(Keeper& keeper) //**** CALCULATE isSupported & ballPositionSupporter for (auto& mate : theTeammateData.teammates) { - if (mate.status >= Teammate::ACTIVE && mate.isUpright && HelperFunctions::calcDistanceModified(theBehaviorConfiguration, theRobotMap, true, mate.pose, theBallSymbols.ballPositionField) < 1500) + if (mate.status == TeammateReceived::Status::FULLY_ACTIVE + && HelperFunctions::calcDistanceModified(theBehaviorConfiguration, theRobotMap, true, mate.robotPose, Pose2f(0, theBallSymbols.ballPositionField)) < 1500) { isSupported = true; - if (mate.ball.timeWhenLastSeen < 3000) + if (theFrameInfo.getTimeSince(mate.ballModel.timeWhenLastSeen) < 3000) { - ballPositionSupporter = Transformation::robotToField(mate.pose, mate.ball.estimate.position); + ballPositionSupporter = Transformation::robotToField(mate.robotPose, mate.ballModel.position); supporterSeenBall = true; } else @@ -167,14 +168,17 @@ void KeeperProvider::updateDecisionVariables(Keeper& keeper) //**** CALCULATE catchBall //Is true if the goalie should catch the ball (dive/wideStance) + Vector2f vecRobotToBall = theBallSymbols.ballPositionField - theRobotPoseAfterPreview.translation; + float nearestBallDistance = Geometry::getDistanceToLine(Geometry::Line(theBallSymbols.ballPositionRelative, theBallSymbols.ballVelocityRelative), Vector2f::Zero()); keeper.catchBall = /*theMotionSelection.ratios[MotionRequest::walk] == 1.f //dont trigger while in special action (dive/standup/..) && */ theRobotPoseAfterPreview.translation.x() < (theFieldDimensions.xPosOwnPenaltyArea + 100) // only in goal area && std::abs(theRobotPoseAfterPreview.translation.y()) < theFieldDimensions.yPosLeftPenaltyArea - 100 // only in goal area && theBallSymbols.timeSinceLastSeen < 3000 //ball should be seen in the last 3 seconds - && theBallSymbols.ballPositionFieldPredicted.x() < keeper.optPosition.translation.x() // must stop behind robot - && std::abs(theBallSymbols.yPosWhenBallReachesOwnYAxis) < maxDiveDistance // reachable with dive - && std::abs(theBallSymbols.yPosWhenBallReachesGroundLine) < theFieldDimensions.yPosLeftGoal + 100.f // within goal posts + && theBallSymbols.ballPositionFieldPredicted.x() < theRobotPoseAfterPreview.translation.x() // must stop behind robot + && std::abs(nearestBallDistance) < maxDiveDistance + && (std::abs(theBallSymbols.yPosWhenBallReachesGroundLine) < theFieldDimensions.yPosLeftGoal + 100.f + || (std::abs(vecRobotToBall.angle()) > 70_deg && std::abs(theBallSymbols.yPosWhenBallReachesGroundLine) < theFieldDimensions.yPosLeftGoal + 1000.f)) // within goal posts && theBallSymbols.ballVelocityRelative.norm() > moveWithBallSpeed; // do not dive for slow ball //**** CALCULATE timeOfLastDive @@ -257,7 +261,8 @@ void KeeperProvider::updateKeeper(Keeper& keeper) { calcOptPlayingPosition(keeper); // ball is far away and not moving, up thresholds to keep robot still - if (theBallSymbols.ballVelocityRelative.norm() < moveWithBallSpeed && theBallSymbols.ballPositionRelative.norm() > moveWithBallDistance) + if ((theBallSymbols.ballVelocityRelative.norm() < moveWithBallSpeed && theBallSymbols.ballPositionRelative.norm() > moveWithBallDistance) + || theBehaviorConfiguration.behaviorParameters.goalieForEvents) { keeper.thresholdXBack = 100.f; keeper.thresholdXFront = 100.f; @@ -300,7 +305,7 @@ void KeeperProvider::calcOptPlayingPosition(Keeper& keeper) // if the ball is rolling fast, not within our penalty area and predicted to land in our goal, intercept and/or block bool interceptBall = false; - if (theMotionSelection.ratios[MotionRequest::walk] == 1.f //dont trigger while in special action (dive/standup/..) + /*if (theMotionSelection.ratios[MotionRequest::walk] == 1.f //dont trigger while in special action (dive/standup/..) && theBallSymbols.ballPositionFieldPredicted.x() < theRobotPoseAfterPreview.translation.x() // ball will be behind us && !theBallSymbols.ballInOwnGoalArea // not already in goal area && std::abs(theBallSymbols.yPosWhenBallReachesGroundLine) < theFieldDimensions.yPosLeftGoal + 100.f && theBallSymbols.ballVelocityRelative.norm() > moveWithBallSpeed) // moving fast @@ -308,7 +313,7 @@ void KeeperProvider::calcOptPlayingPosition(Keeper& keeper) if (theRobotPoseAfterPreview.translation.x() < (theFieldDimensions.xPosOwnPenaltyArea + 100) // only in goal area && std::abs(theRobotPoseAfterPreview.translation.y()) < theFieldDimensions.yPosLeftPenaltyArea - 100 // only in goal area && theBallSymbols.ballPositionRelative.norm() < maxBallDistanceForBlock && theBallSymbols.ballPositionRelative.norm() > minBallDistanceForBlock - && theBallSymbols.ballPositionField.x() > theRobotPoseAfterPreview.translation.x() && theBallSymbols.yPosWhenBallReachesOwnYAxis < yReachableWithDive + && theBallSymbols.ballPositionField.x() > theRobotPoseAfterPreview.translation.x() && std::abs (theBallSymbols.yPosWhenBallReachesOwnYAxis) < yReachableWithDive && theFrameInfo.getTimeSince(lastBlockTimeStamp) > timeBetweenBlockMotions) { keeper.catchBall = true; @@ -320,7 +325,7 @@ void KeeperProvider::calcOptPlayingPosition(Keeper& keeper) interceptBall = true; return; } - } + }*/ // keep robot near own goal if (!interceptBall) @@ -339,7 +344,7 @@ void KeeperProvider::updateBallchaserKeeper(Keeper& keeper) /* ** calculation of the kick position of the goalie. Tries kick as soon as possible but ofc not into own goal.. */ - keeper.optPosition = theBallSymbols.ballPositionField; + keeper.optPosition = Pose2f(0, theBallSymbols.ballPositionField); static const Vector2f leftGoalPost(theFieldDimensions.xPosOwnGroundline, theFieldDimensions.yPosLeftGoal); static const Vector2f rightGoalPost(theFieldDimensions.xPosOwnGroundline, theFieldDimensions.yPosRightGoal); @@ -363,10 +368,10 @@ void KeeperProvider::updateBallchaserKeeper(Keeper& keeper) keeper.thresholdXBack = 30; keeper.thresholdXFront = 30; keeper.thresholdY = 30; - keeper.optKickTarget = Pose2f(keeper.optPosition).translate(3000.f, 0.f).translation; + keeper.optKickTarget = Pose2f(keeper.optPosition).translate(2000.f, 0.f).translation; // TODO: select kick depending on situation keeper.useLongKick = false; - keeper.walkKick = WalkRequest::StepRequest::kickHackLong; + keeper.walkKick = WalkRequest::StepRequest::any; keeper.stopAtTarget = false; keeper.previewArrival = true; } diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KeeperProvider.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KeeperProvider.h index c9a1ccc6..99557185 100644 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KeeperProvider.h +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KeeperProvider.h @@ -14,8 +14,8 @@ #include "Tools/Module/Module.h" #include "Modules/BehaviorControl/CABSL/BehaviorParameters.h" -#include "Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Objective.h" -#include "Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/ObjectivesManager.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/ObjectivesManager/Objective.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/ObjectivesManager/ObjectivesManager.h" #include "Modules/BehaviorControl/TacticControl/RoleProvider/Utils/BallUtils.h" #include #include "Representations/Configuration/FieldDimensions.h" @@ -122,7 +122,7 @@ class KeeperProvider : public KeeperProviderBase, public RoleProvider bool isSupported = false; bool supporterSeenBall = false; Vector2f ballPositionSupporter = Vector2f::Zero(); - unsigned lastBlockTimeStamp = 0; + //unsigned lastBlockTimeStamp = 0; bool kickLeft = false; private: diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/ExecutableKicks/ExecutableKick.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/ExecutableKicks/ExecutableKick.cpp deleted file mode 100644 index e9907ca0..00000000 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/ExecutableKicks/ExecutableKick.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "ExecutableKick.h" - -std::ostream& operator<<(std::ostream& os, const ExecutableKick& e) -{ - return (os << "BestKick: " - << ", widthSet=" << e.widthSet << ", width=" << e.width); -} \ No newline at end of file diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/ExecutableKicks/ExecutableKick.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/ExecutableKicks/ExecutableKick.h deleted file mode 100644 index 1f7a4f5a..00000000 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/ExecutableKicks/ExecutableKick.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include - -#include -#include -#include "Tools/Math/Eigen.h" -#include "Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Models/DistanceRequirement.h" -#include "Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/Kick.h" - -class ExecutableKick -{ -public: - ExecutableKick(Kick* kick, Vector2f target, const bool hysteresis) : kick(kick), target(std::move(target)), hysteresis(hysteresis) {} - virtual ~ExecutableKick() = default; - - void setWidth(const float newWidth) - { - width = newWidth; - widthSet = true; - } - - [[nodiscard]] float getWidth() const - { - if (!widthSet) - { - throw std::logic_error("Calculate width before check if blocked!"); - } - return width; - } - - [[nodiscard]] bool isWidthSet() const { return widthSet; } - - friend std::ostream& operator<<(std::ostream& os, const ExecutableKick& e); - - [[nodiscard]] std::string toString() const - { - std::stringstream ss; - ss << (*this); - return ss.str(); - } - - Kick* kick; - Vector2f target; - bool hysteresis; - - float width = 0; - bool widthSet = false; -}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/ExecutableKicks/ExecutableKicks.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/ExecutableKicks/ExecutableKicks.cpp deleted file mode 100644 index c1460e61..00000000 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/ExecutableKicks/ExecutableKicks.cpp +++ /dev/null @@ -1,402 +0,0 @@ -#include "ExecutableKicks.h" - -#include -#include -#include - -void ExecutableKicks::insert(const ExecutableKicks& other) -{ - insert(other.executableKicks); -} - -void ExecutableKicks::insert(const std::vector& other) -{ - executableKicks.insert(end(executableKicks), begin(other), end(other)); -} - -ExecutableKicks ExecutableKicks::filterOutside(const FieldDimensions& theFieldDimensions) -{ - return filter( - [this, &theFieldDimensions](const ExecutableKick& executableKick) - { - const Vector2f direction = (executableKick.target - ballPosition).normalized(); - return KickUtils::isKickToOutside(executableKick.kick, ballPosition, direction, executableKick.hysteresis, theFieldDimensions); - }); -} - -ExecutableKicks ExecutableKicks::filterTooFarBack(float minX) -{ - return filter( - [&minX](const ExecutableKick& executableKick) - { - return executableKick.target.x() < minX; - }); -} - -ExecutableKicks ExecutableKicks::filterLeft(float maxY) -{ - return filter( - [&maxY](const ExecutableKick& executableKick) - { - return executableKick.target.x() > maxY; - }); -} - -ExecutableKicks ExecutableKicks::filterRight(float minY) -{ - return filter( - [&minY](const ExecutableKick& executableKick) - { - return executableKick.target.x() < minY; - }); -} - -ExecutableKicks ExecutableKicks::filterBlocked(const FieldDimensions& theFieldDimensions, const RobotMap& theRobotMap) -{ - for (ExecutableKick& executableKick : executableKicks) - { - ASSERT(executableKick.widthSet == false); - const float width = KickUtils::getMinKickToObstaclesDistance(ballPosition, executableKick.target, theFieldDimensions, theRobotMap); - executableKick.setWidth(width); - } - return filter( - [&theFieldDimensions](const ExecutableKick& executableKick) - { - if (!executableKick.widthSet) - { - throw std::logic_error("Calculate width before check if blocked!"); - } - const float ballRadius = theFieldDimensions.ballRadius; - const float robotRadius = 140.f; // TODO Constants - const float minWidth = (ballRadius + robotRadius) + (executableKick.hysteresis ? 0.f : 20.f); - return executableKick.width < minWidth; - }); -} - -ExecutableKicks ExecutableKicks::filterTooSharpForGoalKickAngles(const FieldDimensions& theFieldDimensions) -{ - const Angle MAX_ANGLE = 74_deg; // TODO Constant also used in GoalObjective - - const Geometry::Line leftFieldLine = {Vector2f(theFieldDimensions.xPosOpponentFieldBorder, theFieldDimensions.yPosLeftSideline), Vector2f(-1.f, 0.f)}; - const Vector2f leftGoalPost = {theFieldDimensions.xPosOpponentGoalPost, theFieldDimensions.yPosLeftGoal}; - const Geometry::Line leftAngleLine = {leftGoalPost, MathUtils::angleToVector(-MAX_ANGLE)}; - Vector2f leftOutsideIntersection; - VERIFY(Geometry::getIntersectionOfLines(leftFieldLine, leftAngleLine, leftOutsideIntersection)); - - const Geometry::Line rightFieldLine = {Vector2f(theFieldDimensions.xPosOpponentFieldBorder, theFieldDimensions.yPosRightSideline), Vector2f(-1.f, 0.f)}; - const Vector2f rightGoalPost = {theFieldDimensions.xPosOpponentGoalPost, theFieldDimensions.yPosRightGoal}; - const Geometry::Line rightAngleLine = {rightGoalPost, MathUtils::angleToVector(MAX_ANGLE)}; - Vector2f rightOutsideIntersection; - VERIFY(Geometry::getIntersectionOfLines(rightFieldLine, rightAngleLine, rightOutsideIntersection)); - - return filter( - [&leftGoalPost, &leftOutsideIntersection, &rightGoalPost, &rightOutsideIntersection](const ExecutableKick& executableKick) - { - const bool left = executableKick.target.x() < 0; - if (left) - { - return Geometry::isPointLeftOfLine(executableKick.target, leftOutsideIntersection, leftGoalPost); - } - else - { - return Geometry::isPointLeftOfLine(executableKick.target, rightGoalPost, rightOutsideIntersection); - } - }); -} - -ExecutableKicks ExecutableKicks::reduceToBest(const float timeFactor, - const float inaccuracyFactor, - const float widthFactor, - const float sidesHeatFactor, - const float opponentsGoalHeatFactor, - const float alliesHeatFactor, - const float alliesMaxHeatFactor, - const float alliesGoalKickHeatFactor, - const float opponentsHeatFactor, - const float opponentsMaxHeatFactor, - const float opponentsGoalKickHeatFactor, - const bool leftFootClosestToBall, - const FieldDimensions& theFieldDimensions, - const HeatMapCollection& theHeatMapCollection, - const RobotMap& theRobotMap) -{ - ASSERT(timeFactor <= 0); - ASSERT(inaccuracyFactor <= 0); - ASSERT(widthFactor >= 0); - // sidesHeatFactor depends on situation - ASSERT(opponentsGoalHeatFactor >= 0); - ASSERT(alliesHeatFactor >= 0); - ASSERT(alliesMaxHeatFactor >= 0); - ASSERT(alliesGoalKickHeatFactor >= 0); - ASSERT(opponentsHeatFactor <= 0); - ASSERT(opponentsMaxHeatFactor <= 0); - ASSERT(opponentsGoalKickHeatFactor <= 0); - - if (executableKicks.empty()) - { - return *this; - } - - const bool useWidth = !MathUtils::isEqual(0.f, widthFactor); - - const bool useSidesHeat = !MathUtils::isEqual(0.f, sidesHeatFactor); - const bool useOpponentsGoalHeat = !MathUtils::isEqual(0.f, opponentsGoalHeatFactor); - const bool useAlliesHeat = !MathUtils::isEqual(0.f, alliesHeatFactor); - const bool useAlliesMaxHeat = !MathUtils::isEqual(0.f, alliesMaxHeatFactor); - const bool useAlliesGoalKickHeat = !MathUtils::isEqual(0.f, alliesGoalKickHeatFactor); - const bool useOpponentsHeat = !MathUtils::isEqual(0.f, opponentsHeatFactor); - const bool useOpponentsMaxHeat = !MathUtils::isEqual(0.f, opponentsMaxHeatFactor); - const bool useOpponentsGoalKickHeat = !MathUtils::isEqual(0.f, opponentsGoalKickHeatFactor); - const bool useHeatMapScore = useSidesHeat || useOpponentsGoalHeat || useAlliesHeat || useAlliesMaxHeat || useAlliesGoalKickHeat || useOpponentsHeat || useOpponentsMaxHeat - || useOpponentsGoalKickHeat; //TODO Or outside field? - - std::map kickToNrMap = {}; - int nr = 1; - for (ExecutableKick& executableKick : executableKicks) - { - if (kickToNrMap.count(executableKick.kick) == 0) - { - kickToNrMap.insert({executableKick.kick, nr}); - } - } - - std::map indexHeatScoreMap = {}; - if (useHeatMapScore) - { - std::map indexAreaMap = {}; - for (ExecutableKick& executableKick : executableKicks) - { - const int index = HeatMap::fieldToIndex(executableKick.target, theFieldDimensions); - const int key = index * kickToNrMap.at(executableKick.kick); - if (indexAreaMap.count(key) == 0) - { - const HeatMap::Area area = HeatMap::getArea(executableKick.target, executableKick.kick->getInaccuracy(), theFieldDimensions); - indexAreaMap.insert({key, area}); - } - } - - for (auto [key, area] : indexAreaMap) - { - const auto sidesHeatScore = useSidesHeat ? (sidesHeatFactor * theHeatMapCollection.sidesHeatMap.getHeat(area, theFieldDimensions)) : 0; - const auto opponentsGoalHeatScore = useOpponentsGoalHeat ? (opponentsGoalHeatFactor * theHeatMapCollection.opponentsGoalHeatMap.getHeat(area, theFieldDimensions)) : 0; - const auto alliesHeatScore = useAlliesHeat ? (alliesHeatFactor * theHeatMapCollection.alliesHeatMap.getHeat(area, theFieldDimensions)) : 0; - const auto alliesMaxHeatScore = useAlliesMaxHeat ? (alliesMaxHeatFactor * theHeatMapCollection.alliesMaxHeatMap.getHeat(area, theFieldDimensions)) : 0; - const auto alliesGoalKickHeatScore = useAlliesGoalKickHeat ? (alliesGoalKickHeatFactor * theHeatMapCollection.alliesGoalKickHeatMap.getHeat(area, theFieldDimensions)) : 0; - const auto opponentsHeatScore = useOpponentsHeat ? (opponentsHeatFactor * theHeatMapCollection.opponentsHeatMap.getHeat(area, theFieldDimensions)) : 0; - const auto opponentsMaxHeatScore = useOpponentsMaxHeat ? (opponentsMaxHeatFactor * theHeatMapCollection.opponentsMaxHeatMap.getHeat(area, theFieldDimensions)) : 0; - const auto opponentsGoalKickHeatScore = useOpponentsGoalKickHeat ? (opponentsGoalKickHeatFactor * theHeatMapCollection.opponentsGoalKickHeatMap.getHeat(area, theFieldDimensions)) : 0; - - float heatScore = sidesHeatScore + opponentsGoalHeatScore + alliesHeatScore + alliesMaxHeatScore + alliesGoalKickHeatScore + opponentsHeatScore + opponentsMaxHeatScore + opponentsGoalKickHeatScore; - - indexHeatScoreMap.insert({key, heatScore}); - } - } - - const float distanceToKick = Geometry::distance(playerPose.translation, ballPosition); - const bool useHysteresis = distanceToKick < 1500.f; // TODO Constant - - float maxScore = -std::numeric_limits::infinity(); - ExecutableKick* maxScoreExecutableKick = nullptr; - float maxScoreWidth = 0; - - std::vector draw_targets; - draw_targets.reserve(executableKicks.size()); - std::vector draw_scores; - draw_scores.reserve(executableKicks.size()); - - for (ExecutableKick& executableKick : executableKicks) - { - float score = 0; - - const float time = KickUtils::getKickTime(*executableKick.kick, playerPose, ballPosition, executableKick.target, false, leftFootClosestToBall); - const float maxTime = 20.f; // TODO Constant - if (time > maxTime) - { - OUTPUT_WARNING("Assumed maxTime is lower than calculated time for kick!"); - } - const float timeScore = timeFactor * std::min(time, maxTime) / maxTime; - score += timeScore; - - const float inaccuracy = executableKick.kick->getHorizontalInaccuracy(); - const float maxInaccuracy = 2000.f; // TODO Constant - const float inaccuracyScore = inaccuracyFactor * std::min(inaccuracy, maxInaccuracy) / maxInaccuracy; - score += inaccuracyScore; - - float width = 0; - if (useWidth) - { - if (executableKick.widthSet) - { - width = executableKick.width; - } - else - { - width = KickUtils::getMinKickToObstaclesDistance(ballPosition, executableKick.target, theFieldDimensions, theRobotMap); - executableKick.setWidth(width); - } - const float maxWidth = 750.f; // TODO Constant - const float widthValue = std::pow(MathUtils::clamp_f(width, 0.f, maxWidth) / maxWidth, 1 / 3.f); // TODO Constant - const float widthScore = widthFactor * widthValue; - score += widthScore; - } - - if (useHeatMapScore) - { - const int index = HeatMap::fieldToIndex(executableKick.target, theFieldDimensions); - const int key = index * kickToNrMap.at(executableKick.kick); - score += indexHeatScoreMap.at(key); - } - - const float scoreWithoutHysteresis = score; - score = (useHysteresis && executableKick.hysteresis) ? HysterUtils::makeBigger(score, 1.f, 1.2f) : score; - - if (score > maxScore) - { - maxScore = score; - maxScoreExecutableKick = &executableKick; - maxScoreWidth = width; - } - - draw_targets.push_back(executableKick.target); - draw_scores.push_back(scoreWithoutHysteresis); - } - - if (maxScoreExecutableKick == nullptr) - { - return {}; - } - - ExecutableKick maxScoreExecutableKickCopy = *maxScoreExecutableKick; - - drawInGrid(draw_targets, draw_scores, theFieldDimensions); - drawFree(draw_targets, draw_scores); - drawKickMinObstacleWidth(ballPosition, maxScoreExecutableKickCopy.target, maxScoreWidth); - - executableKicks.clear(); - executableKicks.push_back(maxScoreExecutableKickCopy); - return *this; -} - -bool ExecutableKicks::hasBest() const -{ - if (executableKicks.empty()) - { - return false; - } - ASSERT(executableKicks.size() == 1); - return true; -} - -ExecutableKick ExecutableKicks::getBest() const -{ - ASSERT(executableKicks.size() == 1); - return executableKicks.at(0); -} - -ExecutableKicks ExecutableKicks::filter(const std::function& isToRemove) -{ - executableKicks.erase(std::remove_if(executableKicks.begin(), executableKicks.end(), isToRemove), executableKicks.end()); - return *this; -} - -void ExecutableKicks::drawInGrid(const std::vector& draw_targets, const std::vector& draw_scores, const FieldDimensions& theFieldDimensions) -{ - COMPLEX_DRAWING(DRAW_EXECUTABLE_KICKS_IN_GRID) - { - const bool STRETCH = false; - - std::vector draw_indexes = {}; - for (const Vector2f& draw_target : draw_targets) - { - const int draw_index = HeatMap::fieldToIndex(draw_target, theFieldDimensions); - draw_indexes.push_back(draw_index); - } - - std::vector unique_draw_indexes = {}; - std::vector unique_draw_scores = {}; - for (size_t i = 0; i < draw_indexes.size(); i++) - { - const int index = draw_indexes.at(i); - const float score = draw_scores.at(i); - - bool foundEqualTarget = false; - for (size_t j = 0; j < unique_draw_indexes.size(); j++) - { - const int uniqueIndex = unique_draw_indexes.at(j); - const float uniqueScore = unique_draw_scores.at(j); - - if (index == uniqueIndex) - { - if (score > uniqueScore) - { - unique_draw_scores.at(j) = score; - } - foundEqualTarget = true; - break; - } - } - if (!foundEqualTarget) - { - unique_draw_indexes.push_back(index); - unique_draw_scores.push_back(score); - } - } - - if (STRETCH) - { - MathUtils::stretch(unique_draw_scores); - } - - const size_t size = unique_draw_scores.size(); - for (size_t i = 0; i < size; ++i) - { - const Vector2f draw_target = HeatMap::indexToField(unique_draw_indexes[i], theFieldDimensions); - float draw_score = unique_draw_scores[i]; - if (!STRETCH) - { - draw_score = (MathUtils::clamp_f(draw_score, -1.5f, 1.5f) + 1.5f) / 3.f; - } - CIRCLE(DRAW_EXECUTABLE_KICKS_IN_GRID, draw_target.x(), draw_target.y(), 50, 0, Drawings::noPen, ColorRGBA::black, Drawings::solidBrush, ColorRGBA((char)(255 * (1 - draw_score)), (char)(255 * draw_score), 0)); - } - } -} - -void ExecutableKicks::drawFree(const std::vector& draw_targets, std::vector& draw_scores) -{ - COMPLEX_DRAWING(DRAW_EXECUTABLE_KICKS_FREELY) - { - const bool STRETCH = false; - - if (STRETCH) - { - MathUtils::stretch(draw_scores); - } - - const size_t size = draw_targets.size(); - for (size_t i = 0; i < size; ++i) - { - const Vector2f& draw_target = draw_targets.at(i); - float draw_score = draw_scores.at(i); - if (!STRETCH) - { - draw_score = (MathUtils::clamp_f(draw_score, -1.5f, 1.5f) + 1.5f) / 3.f; - } - CIRCLE(DRAW_EXECUTABLE_KICKS_FREELY, draw_target.x(), draw_target.y(), 30, 0, Drawings::noPen, ColorRGBA::black, Drawings::solidBrush, ColorRGBA((char)(255 * (1 - draw_score)), (char)(255 * draw_score), 0)); - } - } -} - -void ExecutableKicks::drawKickMinObstacleWidth(const Vector2f& ballPosition, const Vector2f& targetPosition, const float width) -{ - Vector2f w = Vector2f(targetPosition - ballPosition).normalized() * width; - w = w.rotate(90_deg); - - const Vector2f p11 = ballPosition - w; - const Vector2f p12 = ballPosition + w; - - const Vector2f p21 = targetPosition - w; - const Vector2f p22 = targetPosition + w; - - LINE(DRAW_KICK_MIN_WIDTH, p11.x(), p11.y(), p21.x(), p21.y(), 10, Drawings::solidPen, ColorRGBA::orange); - LINE(DRAW_KICK_MIN_WIDTH, p12.x(), p12.y(), p22.x(), p22.y(), 10, Drawings::solidPen, ColorRGBA::orange); -} diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/ExecutableKicks/ExecutableKicks.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/ExecutableKicks/ExecutableKicks.h deleted file mode 100644 index f8e016ce..00000000 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/ExecutableKicks/ExecutableKicks.h +++ /dev/null @@ -1,70 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include "Tools/Math/Geometry.h" -#include "ExecutableKick.h" - -class ExecutableKicks -{ - -public: - ExecutableKicks(Pose2f playerPose, Vector2f ballPosition, std::vector executableKicks) - : playerPose(std::move(playerPose)), ballPosition(std::move(ballPosition)), executableKicks(std::move(executableKicks)) - { - } - ExecutableKicks() {} - virtual ~ExecutableKicks() = default; - - void insert(const ExecutableKicks& other); - - void insert(const std::vector& other); - - ExecutableKicks filterBlocked(const FieldDimensions& theFieldDimensions, const RobotMap& theRobotMap); - - ExecutableKicks filterOutside(const FieldDimensions& theFieldDimensions); - - ExecutableKicks filterTooFarBack(float minX); - - ExecutableKicks filterLeft(float maxY); - - ExecutableKicks filterRight(float minY); - - ExecutableKicks filterTooSharpForGoalKickAngles(const FieldDimensions& theFieldDimensions); - - ExecutableKicks reduceToBest(float timeFactor, - float inaccuracyFactor, - float widthFactor, - float sidesHeatFactor, - float opponentsGoalHeatFactor, - float alliesHeatFactor, - float alliesMaxHeatFactor, - float alliesGoalKickHeatFactor, - float opponentsHeatFactor, - float opponentsMaxHeatFactor, - float opponentsGoalKickHeatFactor, - bool leftFootClosestToBall, - const FieldDimensions& theFieldDimensions, - const HeatMapCollection& theHeatMapCollection, - const RobotMap& theRobotMap); - - [[nodiscard]] bool hasBest() const; - - [[nodiscard]] ExecutableKick getBest() const; - - const Pose2f playerPose; - const Vector2f ballPosition = Vector2f::Zero(); - -private: - std::vector executableKicks; - - ExecutableKicks filter(const std::function& isToRemove); - - static void drawFree(const std::vector& draw_targets, std::vector& draw_scores); - - static void drawInGrid(const std::vector& draw_targets, const std::vector& draw_scores, const FieldDimensions& theFieldDimensions); - - static void drawKickMinObstacleWidth(const Vector2f& ballPosition, const Vector2f& targetPosition, float width); -}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/HeatMapProvider.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/HeatMapProvider.cpp deleted file mode 100644 index ac7ae2aa..00000000 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/HeatMapProvider.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include -#include "Tools/Math/Transformation.h" -#include "HeatMapProvider.h" - -HeatMapProvider::HeatMapProvider() = default; - -void draw(std::vector positionVector, std::vector heatVector); - -void HeatMapProvider::update(HeatMapCollection& heatMapCollection) -{ - updateHeat(heatMapCollection); - - DECLARE_DEBUG_DRAWING("module:HeatMapProvider:HeatMap", "drawingOnField"); - HeatUtils::draw(heatMapCollection.opponentsGoalKickHeatMap, theFieldDimensions); -} - -void HeatMapProvider::updateHeat(HeatMapCollection& heatMapCollection) -{ - for (int index = 0; index < HeatMap::CELL_COUNT; index++) - { - const Vector2f fieldPosition = HeatMap::indexToField(index, theFieldDimensions); - - if (firstUpdate) - { - sidesHeatMap.setHeat(std::pow(HeatUtils::getSidesHeat(fieldPosition, theFieldDimensions), 2.f), index, theFieldDimensions); - opponentsGoalHeatMap.setHeat(HeatUtils::getOpponentsGoalHeat(fieldPosition, theFieldDimensions), index, theFieldDimensions); - } - - heatMapCollection.sidesHeatMap = sidesHeatMap; - heatMapCollection.opponentsGoalHeatMap = opponentsGoalHeatMap; - - auto [alliesHeat, alliesMaxHeat, alliesGoalKickHeat, opponentsHeat, opponentsMaxHeat, opponentsGoalKickHeat] = HeatUtils::getRobotHeat(fieldPosition, theFieldDimensions, theRobotMap, theRobotPose); - - if (takeNewPercent == 1.f) - { - heatMapCollection.alliesHeatMap.setHeat(alliesHeat, index, theFieldDimensions); - heatMapCollection.alliesMaxHeatMap.setHeat(alliesMaxHeat, index, theFieldDimensions); - heatMapCollection.opponentsHeatMap.setHeat(opponentsHeat, index, theFieldDimensions); - heatMapCollection.opponentsMaxHeatMap.setHeat(opponentsMaxHeat, index, theFieldDimensions); - } - else - { - heatMapCollection.alliesHeatMap.updateHeat(takeNewPercent, alliesHeat, index, theFieldDimensions); - heatMapCollection.alliesMaxHeatMap.updateHeat(takeNewPercent, alliesMaxHeat, index, theFieldDimensions); - heatMapCollection.opponentsHeatMap.updateHeat(takeNewPercent, opponentsHeat, index, theFieldDimensions); - heatMapCollection.opponentsMaxHeatMap.updateHeat(takeNewPercent, opponentsMaxHeat, index, theFieldDimensions); - } - - if (goalKickHeatTakeNewPercent == 1.f) - { - heatMapCollection.alliesGoalKickHeatMap.setHeat(alliesGoalKickHeat, index, theFieldDimensions); - heatMapCollection.opponentsGoalKickHeatMap.setHeat(opponentsGoalKickHeat, index, theFieldDimensions); - } - else - { - heatMapCollection.alliesGoalKickHeatMap.updateHeat(goalKickHeatTakeNewPercent, alliesGoalKickHeat, index, theFieldDimensions); - heatMapCollection.opponentsGoalKickHeatMap.updateHeat(goalKickHeatTakeNewPercent, opponentsGoalKickHeat, index, theFieldDimensions); - } - } - - firstUpdate = false; -} - -MAKE_MODULE(HeatMapProvider, modeling) diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/KickManager.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/KickManager.cpp deleted file mode 100644 index e772d8f5..00000000 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/KickManager.cpp +++ /dev/null @@ -1,268 +0,0 @@ -#include "KickManager.h" - -#include "Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/Kick.h" -#include "Tools/Math/Eigen.h" -#include "Representations/BehaviorControl/RoleSymbols/Ballchaser.h" -#include -#include -#include -#include -#include -#include -#include - -void KickManager::update(const Vector2f& ballPosition, const RobotPoseAfterPreview& theRobotPoseAfterPreview, const WalkingEngineParams& theWalkingEngineParams) -{ - // updateLeftFootClosestToBall - - const float footDecisionHysteresis = 50.f; // TODO Constant - - const Pose2f leftFoot = Pose2f(theRobotPoseAfterPreview).translate(0.f, theWalkingEngineParams.footMovement.footYDistance); - const Pose2f rightFoot = Pose2f(theRobotPoseAfterPreview).translate(0.f, -theWalkingEngineParams.footMovement.footYDistance); - - const float leftDistance = (ballPosition - leftFoot.translation).norm(); - const float rightDistance = (ballPosition - rightFoot.translation).norm(); - - if (leftFootClosestToBall && rightDistance < leftDistance - footDecisionHysteresis) - leftFootClosestToBall = false; - else if (!leftFootClosestToBall && leftDistance < rightDistance - footDecisionHysteresis) - leftFootClosestToBall = true; - - // ball moved - const Vector2f ballMoved = (ballPosition - currentBallPosition); - if (ballMoved.norm() > 200.f) // TODO Constant - { - stop(); - } -} - -bool KickManager::kickInRangeCenter(Ballchaser& ballchaser, - const Pose2f& playerPose, - const Vector2f& ballPosition, - const Vector2f& target1, - const Vector2f& target2, - const int stepSize, - const DistanceRequirement distanceRequirement, - const bool optimizeForTime, - const float minKickWidth, - const float minFreeAroundTarget, - std::vector kicks, - const FieldDimensions& theFieldDimensions, - const RobotMap& theRobotMap) -{ - const Vector2f target1ToTarget2 = target2 - target1; - const float target1ToTarget2Distance = target1ToTarget2.norm(); - const int optAhead = (int)(target1ToTarget2Distance / 2); - return kickCloseToOptimalPosition(ballchaser, - playerPose, - ballPosition, - Geometry::Line(target1, target1ToTarget2.normalized()), - 0, - optAhead, - (int)target1ToTarget2Distance, - stepSize, - distanceRequirement, - optimizeForTime, - minKickWidth, - minFreeAroundTarget, - std::move(kicks), - theFieldDimensions, - theRobotMap); -} - -bool KickManager::kickCloseToOptimalPosition(Ballchaser& ballchaser, - const Pose2f& playerPose, - const Vector2f& ballPosition, - const Geometry::Line& line, - const int minAhead, - const int optimalAhead, - const int maxAhead, - const int stepSize, - const DistanceRequirement distanceRequirement, - const bool optimizeForTime, - const float minKickWidth, - const float minFreeAroundTarget, - std::vector kicks, - const FieldDimensions& theFieldDimensions, - const RobotMap& theRobotMap) -{ - ASSERT(minAhead <= optimalAhead); - ASSERT(optimalAhead <= maxAhead); - ASSERT(stepSize > 0); - ASSERT(minKickWidth >= 0); - ASSERT(minFreeAroundTarget >= 0); - - KickUtils::drawKickRange(ballPosition, line, minAhead, maxAhead); - - Vector2f target; - int stepSizeSum = 0; - bool testedNewTarget = true; - while (testedNewTarget) - { - testedNewTarget = false; - for (int neg = -1; neg <= 1; neg = neg + 2) - { - int ahead = optimalAhead + neg * stepSizeSum; - - if (minAhead > ahead) - { - continue; - } - if (ahead > maxAhead) - { - continue; - } - testedNewTarget = true; - - target = line.base + ahead * line.direction; - - auto [kick, realisticTarget] = getBestKick(playerPose, ballPosition, target, distanceRequirement, optimizeForTime, minKickWidth, minFreeAroundTarget, kicks, theFieldDimensions, theRobotMap); - - if (kick != nullptr) - { - kickTo(ballchaser, playerPose, ballPosition, realisticTarget, *kick); - return true; - } - - stepSizeSum += stepSize; - } - } - return false; -} - -std::tuple KickManager::getBestKick(const Pose2f& playerPose, - const Vector2f& ballPosition, - const Vector2f& targetPosition, - const DistanceRequirement distanceRequirement, - const bool optimizeForTime, - const float minKickWidth, - const float minFreeAroundTarget, - std::vector& kicks, - const FieldDimensions& theFieldDimensions, - const RobotMap& theRobotMap) -{ - const float distance = Geometry::distance(ballPosition, targetPosition); - - float minTime = std::numeric_limits::max(); - Kick* minTimeKick = nullptr; - Vector2f minTimeRealisticTarget; - - float minInaccuracy = std::numeric_limits::max(); - Kick* minInaccuracyKick = nullptr; - Vector2f minInaccuracyRealisticTarget; - - for (Kick* kick : kicks) - { - const Vector2f realisticTargetPosition = kick->getRealisticTarget(ballPosition, targetPosition); - - const bool hysteresis = isCurrent(kick, realisticTargetPosition); - - if (!KickUtils::fulfillsDistanceRequirements(*kick, distance, distanceRequirement, hysteresis)) - { - continue; - } - - if (BlockUtils::isKickBlocked(ballPosition, realisticTargetPosition, minKickWidth, hysteresis, theFieldDimensions, theRobotMap)) - { - continue; - } - - if (minFreeAroundTarget > 0 && BlockUtils::isTargetControlledByOpponent(ballPosition, realisticTargetPosition, minFreeAroundTarget, hysteresis, theRobotMap)) - { - continue; - } - - const float kickTime = KickUtils::getKickTime(*kick, playerPose, ballPosition, targetPosition, hysteresis, leftFootClosestToBall); - - float kickInaccuracy = kick->getHorizontalInaccuracy(); - - if (kickTime < minTime || (kickTime == minTime && (minTimeKick == nullptr || kickInaccuracy < minTimeKick->getHorizontalInaccuracy()))) - { - minTime = kickTime; - minTimeKick = kick; - minTimeRealisticTarget = realisticTargetPosition; - } - - if (kickInaccuracy < minInaccuracy || (kickInaccuracy == minInaccuracy && (minInaccuracyKick == nullptr || kickTime < minInaccuracyKick->getTime(hysteresis)))) - { - minInaccuracy = kickInaccuracy; - minInaccuracyKick = kick; - minInaccuracyRealisticTarget = realisticTargetPosition; - } - } - - if (optimizeForTime) - { - return std::tuple{minTimeKick, minTimeRealisticTarget}; - } - else - { - return std::tuple{minInaccuracyKick, minInaccuracyRealisticTarget}; - } -} - -void KickManager::stop() -{ - ANNOTATION("KickManagerStop", "The KickManager stopped the current kick!"); - currentKick = nullptr; -} - -void KickManager::kickTo(Ballchaser& ballchaser, const ExecutableKicks& executableKicks) -{ - ASSERT(executableKicks.hasBest()); - ExecutableKick best = executableKicks.getBest(); - kickTo(ballchaser, executableKicks.playerPose, executableKicks.ballPosition, best.target, *best.kick); -} - -void KickManager::kickTo(Ballchaser& ballchaser, const Pose2f& playerPose, const Vector2f& ballPosition, const Vector2f& targetPosition, Kick& kick) -{ - CIRCLE(DRAW_EXECUTABLE_KICK_TARGET_AREA, targetPosition.x(), targetPosition.y(), kick.getInaccuracy(), 12, Drawings::solidPen, ColorRGBA::blue, Drawings::noBrush, ColorRGBA::blue); - ballchaser.log_kickName = kick.getName(); - - Pose2f useKickPose = currentKickPose; - if (currentKick && isCurrent(&kick, targetPosition)) - { - const Vector2f ballMoved = (ballPosition - currentBallPosition); - useKickPose = {useKickPose.rotation, useKickPose.translation + ballMoved}; - } - else - { - currentKick = &kick; - currentKickPose = currentKick->getKickPose(playerPose, ballPosition, targetPosition, leftFootClosestToBall); - currentBallPosition = ballPosition; - currentKickTarget = targetPosition; - currentKickStarted = false; - } - - const Angle optAngle = (useKickPose.translation - ballPosition).angle(); - const Angle leftOptAngle = optAngle + 45_deg; - const Angle rightOptAngle = optAngle - 45_deg; - const Angle currentAngle = (playerPose.translation - ballPosition).angle(); - currentKickStarted = currentKickStarted || (rightOptAngle < currentAngle && currentAngle < leftOptAngle); - - const Vector2f leftOptPosition = ballPosition + MathUtils::angleToVector(leftOptAngle) * 1000.f; - const Vector2f rightOptPosition = ballPosition + MathUtils::angleToVector(rightOptAngle) * 1000.f; - const Vector2f currentPosition = ballPosition + MathUtils::angleToVector(currentAngle) * 1000.f; - LINE(DRAW_KICK_MANAGER_ACTIVATE_RANGE_NAME, ballPosition.x(), ballPosition.y(), leftOptPosition.x(), leftOptPosition.y(), 10, Drawings::solidPen, ColorRGBA::cyan); - LINE(DRAW_KICK_MANAGER_ACTIVATE_RANGE_NAME, ballPosition.x(), ballPosition.y(), rightOptPosition.x(), rightOptPosition.y(), 10, Drawings::solidPen, ColorRGBA::cyan); - LINE(DRAW_KICK_MANAGER_ACTIVATE_RANGE_NAME, ballPosition.x(), ballPosition.y(), currentPosition.x(), currentPosition.y(), 10, Drawings::solidPen, ColorRGBA::green); - - currentKick->perform(ballchaser, useKickPose, currentKickTarget, currentKickStarted); -} - -bool KickManager::isActive() -{ - return currentKick != nullptr; -} - -bool KickManager::isCurrent(const Kick* kick, const Vector2f& kickTarget) -{ - if (kick == currentKick) - { - return Geometry::distance(kickTarget, currentKickTarget) < 180.f; // TODO CONST - } - else - { - return false; - } -} diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/KickManager.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/KickManager.h deleted file mode 100644 index 3c29e350..00000000 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/KickManager.h +++ /dev/null @@ -1,114 +0,0 @@ -#pragma once - -#include -#include "Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/Kick.h" -#include "Tools/Math/Eigen.h" -#include "Representations/BehaviorControl/RoleSymbols/Ballchaser.h" -#include "Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/Kick.h" -#include "Modules/BehaviorControl/TacticControl/RoleProvider/Kick/ExecutableKicks/ExecutableKick.h" -#include "Modules/BehaviorControl/TacticControl/RoleProvider/Kick/ExecutableKicks/ExecutableKicks.h" -#include -#include -#include -#include -#include - -class KickManager -{ - -public: - KickManager() = default; - ~KickManager() = default; - - // Direct kick selection ============================================================================================= - - bool kickInRangeCenter(Ballchaser& ballchaser, - const Pose2f& playerPose, - const Vector2f& ballPosition, - const Vector2f& target1, - const Vector2f& target2, - int stepSize, - DistanceRequirement distanceRequirement, - bool optimizeForTime, - float minKickWidth, - float minFreeAroundTarget, - std::vector kicks, - const FieldDimensions& theFieldDimensions, - const RobotMap& theRobotMap); - /** - * Starts at the optimalAhead Position on the line. Tries left and right onside the line around this position until - * a valid kick is found. - * @return If a kick is found, kick to it and return true, else return false. - */ - bool kickCloseToOptimalPosition(Ballchaser& ballchaser, - const Pose2f& playerPose, - const Vector2f& ballPosition, - const Geometry::Line& line, - int minAhead, - int optimalAhead, - int maxAhead, - int stepSize, - DistanceRequirement distanceRequirement, - bool optimizeForTime, - float minKickWidth, - float minFreeAroundTarget, - std::vector kicks, - const FieldDimensions& theFieldDimensions, - const RobotMap& theRobotMap); - - std::tuple getBestKick(const Pose2f& playerPose, - const Vector2f& ballPosition, - const Vector2f& targetPosition, - DistanceRequirement distanceRequirement, - bool optimizeForTime, - float minKickWidth, - float minFreeAroundTarget, - std::vector& kicks, - const FieldDimensions& theFieldDimensions, - const RobotMap& theRobotMap); - - // Generate object to select kick ==================================================================================== - - ExecutableKicks getExecutableKicks(const Pose2f& playerPose, - const KickRange& kickRange, - int stepSizeOrZeroForAngle, - std::vector kicks, - const FieldDimensions& theFieldDimensions, - const HeatMapCollection& theHeatMapCollection, - const RobotMap& theRobotMap); - - std::vector getExecutableKicks(const Pose2f& playerPose, - const Vector2f& ballPosition, - const Vector2f& targetPosition, - DistanceRequirement distanceRequirement, - std::vector kicks, - const FieldDimensions& theFieldDimensions, - const HeatMapCollection& theHeatMapCollection, - const RobotMap& theRobotMap); - - ExecutableKicks getExecutableKicks( - const Pose2f& playerPose, const Vector2f& ballPosition, std::vector kicks, const FieldDimensions& theFieldDimensions, const HeatMapCollection& theHeatMapCollection, const RobotMap& theRobotMap); - - void kickTo(Ballchaser& ballchaser, const ExecutableKicks& executableKicks); - - // Interact with the kickManager ===================================================================================== - - void stop(); - - void update(const Vector2f& ballPosition, const RobotPoseAfterPreview& theRobotPoseAfterPreview, const WalkingEngineParams& theWalkingEngineParams); - - void kickTo(Ballchaser& ballchaser, const Pose2f& playerPose, const Vector2f& ballPosition, const Vector2f& targetPosition, Kick& kick); - - bool isActive(); - -private: - bool isCurrent(const Kick* kick, const Vector2f& kickTarget); - - Kick* currentKick = nullptr; - Pose2f currentKickPose; - Vector2f currentBallPosition; - Vector2f currentKickTarget; - bool currentKickStarted = false; - - bool leftFootClosestToBall = false; -}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/KickManager_ExecutableKick.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/KickManager_ExecutableKick.cpp deleted file mode 100644 index 01e6943b..00000000 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/KickManager_ExecutableKick.cpp +++ /dev/null @@ -1,94 +0,0 @@ -#include "KickManager.h" - -#include -#include -#include - -#include -#include "Tools/Math/Geometry.h" -#include "Representations/Modeling/RobotMap.h" -#include "Modules/BehaviorControl/TacticControl/RoleProvider/Kick/ExecutableKicks/ExecutableKick.h" -#include "Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Models/KickRange.h" - -ExecutableKicks KickManager::getExecutableKicks( - const Pose2f& playerPose, const Vector2f& ballPosition, std::vector kicks, const FieldDimensions& theFieldDimensions, const HeatMapCollection& theHeatMapCollection, const RobotMap& theRobotMap) -{ - std::vector targets = {}; - targets.emplace_back(ballPosition.x(), ballPosition.y() + 500.f); - targets.emplace_back(ballPosition.x() + 500.f, ballPosition.y()); - targets.emplace_back(ballPosition.x(), ballPosition.y() - 500.f); - targets.emplace_back(ballPosition.x() - 500.f, ballPosition.y()); - targets.emplace_back(ballPosition.x(), ballPosition.y() + 500.f); - return getExecutableKicks(playerPose, {ballPosition, targets, DistanceRequirement::mayShorterOrFurther}, 0, kicks, theFieldDimensions, theHeatMapCollection, theRobotMap); -} - -ExecutableKicks KickManager::getExecutableKicks( - const Pose2f& playerPose, const KickRange& kickRange, const int stepSizeOrZeroForAngle, std::vector kicks, const FieldDimensions& theFieldDimensions, const HeatMapCollection& theHeatMapCollection, const RobotMap& theRobotMap) -{ - const size_t targetCount = kickRange.targets.size(); - - ASSERT(targetCount >= 2); - - ExecutableKicks executableKicks = {playerPose, kickRange.ballPosition, {}}; - - for (size_t i = 1; i < targetCount; i++) - { - const Vector2f& target1 = kickRange.targets.at(i - 1); - const Vector2f& target2 = kickRange.targets.at(i); - const DistanceRequirement& distanceRequirement = kickRange.distanceRequirements.at(i - 1); - - std::vector targets = stepSizeOrZeroForAngle <= 0 - ? KickUtils::getTargetsWithEvenAngle(kickRange.ballPosition, target1, target2) - : KickUtils::getTargetsWithEvenDistance(target1, target2, stepSizeOrZeroForAngle); - auto straightAheadTargetOptional = KickUtils::getStraightAheadTarget(playerPose.translation, kickRange.ballPosition, target1, target2); - if (straightAheadTargetOptional.has_value()) - { - targets.push_back(straightAheadTargetOptional.value()); - } - - for (const Vector2f& target : targets) - { - std::vector executableKicksForTarget = - getExecutableKicks(playerPose, kickRange.ballPosition, target, distanceRequirement, kicks, theFieldDimensions, theHeatMapCollection, theRobotMap); - - executableKicks.insert(executableKicksForTarget); - } - - KickUtils::drawKickInfos(kickRange.ballPosition, target1, target2); - } - - return executableKicks; -} - -std::vector KickManager::getExecutableKicks(const Pose2f& playerPose, - const Vector2f& ballPosition, - const Vector2f& targetPosition, - const DistanceRequirement distanceRequirement, - std::vector kicks, - const FieldDimensions& theFieldDimensions, - const HeatMapCollection& theHeatMapCollection, - const RobotMap& theRobotMap) -{ - const float distance = Geometry::distance(ballPosition, targetPosition); - - std::vector executableKicks = {}; - - for (Kick* kick : kicks) - { - // TODO If adjustableKicks are added, use another loop here - - Vector2f kickTargetPosition = kick->getRealisticTarget(ballPosition, targetPosition); - - const bool hysteresis = isCurrent(kick, kickTargetPosition); - - if (!KickUtils::fulfillsDistanceRequirements(*kick, distance, distanceRequirement, hysteresis)) - { - continue; - } - - const ExecutableKick executableKick = {kick, kickTargetPosition, hysteresis}; - - executableKicks.push_back(executableKick); - } - return executableKicks; -} diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/FastLongKick.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/FastLongKick.h deleted file mode 100644 index 7a87f77c..00000000 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/FastLongKick.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include "Kick.h" -#include -#include - -class FastLongKick : public Kick -{ - -public: - explicit FastLongKick() : Kick("FastLongKick", 2500.f, 3400.f, false, 3.f, 1800.f, false, 0_deg, 190.f, 55.f) {} - - void perform(Ballchaser& ballchaser, const Pose2f& kickPose, const Vector2f& targetPosition, const bool start) const override - { - ThresholdUtils::setThresholdsForLongKick(ballchaser); - ballchaser.optPosition = kickPose; - ballchaser.kickTarget = targetPosition; - ballchaser.kickType = MotionRequest::KickType::longKick; - ballchaser.longKickType = KickRequest::KickMotionID::kickMiddleFast; - }; - - [[nodiscard]] Pose2f getKickPose(const Pose2f& playerPose, const Vector2f& ballPosition, const Vector2f& targetPosition, bool leftFootClosestToBall) const override - { - return KickUtils::getKickPose(getOptAngleField(playerPose.translation, ballPosition, targetPosition), ballPosition, leftFootClosestToBall, optXDistanceToBall, optYDistanceToBall); - } -}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/KickHack.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/KickHack.h deleted file mode 100644 index 040f9003..00000000 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/KickHack.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include "Kick.h" -#include -#include - -class KickHack : public Kick -{ - -public: - explicit KickHack() : Kick("KickHack", 700.f, 950.f, false, 1.21f, 250.f, false, 0_deg, 160.f, 55.f) {} - - void perform(Ballchaser& ballchaser, const Pose2f& kickPose, const Vector2f& targetPosition, const bool start) const override - { - ThresholdUtils::setThresholdsForAnyKick(ballchaser); - ballchaser.optPosition = kickPose; - ballchaser.kickType = MotionRequest::KickType::walkKick; - ballchaser.kickTarget = targetPosition; - ballchaser.walkKickType = start ? WalkRequest::StepRequest::any : WalkRequest::StepRequest::none; - } - - [[nodiscard]] Pose2f getKickPose(const Pose2f& playerPose, const Vector2f& ballPosition, const Vector2f& targetPosition, bool leftFootClosestToBall) const override - { - return KickUtils::getKickPose(getOptAngleField(playerPose.translation, ballPosition, targetPosition), ballPosition, leftFootClosestToBall, optXDistanceToBall, optYDistanceToBall); - } -}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/KickHackLong.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/KickHackLong.h deleted file mode 100644 index 1af7f5f8..00000000 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/KickHackLong.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include "Kick.h" -#include -#include - -class KickHackLong : public Kick -{ -public: - explicit KickHackLong() : Kick("KickHackLong", 1000.f, 1300.f, false, 1.21f, 350.f, false, 0_deg, 180.f, 55.f) {} - - void perform(Ballchaser& ballchaser, const Pose2f& kickPose, const Vector2f& targetPosition, const bool start) const override - { - ThresholdUtils::setThresholdsForAnyKick(ballchaser); - ballchaser.optPosition = kickPose; - ballchaser.kickType = MotionRequest::KickType::walkKick; - ballchaser.kickTarget = targetPosition; - ballchaser.walkKickType = start ? WalkRequest::StepRequest::any : WalkRequest::StepRequest::none; - } - - [[nodiscard]] Pose2f getKickPose(const Pose2f& playerPose, const Vector2f& ballPosition, const Vector2f& targetPosition, bool leftFootClosestToBall) const override - { - return KickUtils::getKickPose(getOptAngleField(playerPose.translation, ballPosition, targetPosition), ballPosition, leftFootClosestToBall, optXDistanceToBall, optYDistanceToBall); - } -}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/KickHackVeryLong.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/KickHackVeryLong.h deleted file mode 100644 index 6312f050..00000000 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/KickHackVeryLong.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include "Kick.h" -#include -#include - -class KickHackVeryLong : public Kick -{ -public: - explicit KickHackVeryLong() : Kick("KickHackVeryLong", 2500.f, 4000.f, false, 1.92f, 700.f, false, 0_deg, 250.f, 45.f) {} - - void perform(Ballchaser& ballchaser, const Pose2f& kickPose, const Vector2f& targetPosition, const bool start) const override - { - ThresholdUtils::setThresholdsForAnyKick(ballchaser); - ballchaser.optPosition = kickPose; - ballchaser.kickType = MotionRequest::KickType::walkKick; - ballchaser.kickTarget = targetPosition; - ballchaser.walkKickType = start ? WalkRequest::StepRequest::any : WalkRequest::StepRequest::none; - } - - [[nodiscard]] Pose2f getKickPose(const Pose2f& playerPose, const Vector2f& ballPosition, const Vector2f& targetPosition, bool leftFootClosestToBall) const override - { - return KickUtils::getKickPose(getOptAngleField(playerPose.translation, ballPosition, targetPosition), ballPosition, leftFootClosestToBall, optXDistanceToBall, optYDistanceToBall); - } -}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/Rotate45Kick.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/Rotate45Kick.h deleted file mode 100644 index 22d47145..00000000 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/Rotate45Kick.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "Kick.h" -#include - -class Rotate45Kick : public Kick -{ - -public: - explicit Rotate45Kick() : Kick("Rotate45Kick", 1100.f, 1350.f, false, 1.3f, 400.f, false, 45_deg, 175.f, 20.f) {} - - void perform(Ballchaser& ballchaser, const Pose2f& kickPose, const Vector2f& targetPosition, const bool start) const override - { - ThresholdUtils::setThresholdsForAnyKick(ballchaser); - ballchaser.optPosition = kickPose; - ballchaser.kickType = MotionRequest::KickType::walkKick; - ballchaser.kickTarget = targetPosition; - ballchaser.walkKickType = start ? WalkRequest::StepRequest::any : WalkRequest::StepRequest::none; - } - - [[nodiscard]] Pose2f getKickPose(const Pose2f& playerPose, const Vector2f& ballPosition, const Vector2f& targetPosition, bool leftFootClosestToBall) const override - { - const bool playerOnLeftSideOfKickDirection = Geometry::isPointLeftOfLine(playerPose.translation, ballPosition, targetPosition); - - const Pose2f ignoreCurrentRotation_playerPose = {(ballPosition - playerPose.translation).angle(), playerPose.translation}; - const Vector2f ballToTarget = targetPosition - ballPosition; - const Vector2f robotView_ballToTarget = Transformation::fieldToRobot(ignoreCurrentRotation_playerPose, ballToTarget); - const Angle robotView_ballToTargetAngle = robotView_ballToTarget.angle(); - bool kickWithRight = robotView_ballToTargetAngle > 0; - - return KickUtils::getKickPose(ballPosition, targetPosition, !playerOnLeftSideOfKickDirection, kickWithRight, optAngle, optXDistanceToBall, optYDistanceToBall); - } -}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/RotateKick45Long.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/RotateKick45Long.h deleted file mode 100644 index 120b8a96..00000000 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/RotateKick45Long.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "Kick.h" -#include - -class RotateKick45Long : public Kick -{ - -public: - explicit RotateKick45Long() : Kick("RotateKick45Long", 100.f, 6000.f, false, 2.4f, 600.f, false, 45_deg, 175.f, 20.f) {} - - void perform(Ballchaser& ballchaser, const Pose2f& kickPose, const Vector2f& targetPosition, const bool start) const override - { - ThresholdUtils::setThresholdsForAnyKick(ballchaser); - ballchaser.optPosition = kickPose; - ballchaser.kickType = MotionRequest::KickType::walkKick; - ballchaser.kickTarget = targetPosition; - ballchaser.walkKickType = start ? WalkRequest::StepRequest::any : WalkRequest::StepRequest::none; - } - - [[nodiscard]] Pose2f getKickPose(const Pose2f& playerPose, const Vector2f& ballPosition, const Vector2f& targetPosition, bool leftFootClosestToBall) const override - { - const bool playerOnLeftSideOfKickDirection = Geometry::isPointLeftOfLine(playerPose.translation, ballPosition, targetPosition); - - const Pose2f ignoreCurrentRotation_playerPose = {(ballPosition - playerPose.translation).angle(), playerPose.translation}; - const Vector2f ballToTarget = targetPosition - ballPosition; - const Vector2f robotView_ballToTarget = Transformation::fieldToRobot(ignoreCurrentRotation_playerPose, ballToTarget); - const Angle robotView_ballToTargetAngle = robotView_ballToTarget.angle(); - bool kickWithRight = robotView_ballToTargetAngle > 0; - - return KickUtils::getKickPose(ballPosition, targetPosition, !playerOnLeftSideOfKickDirection, kickWithRight, optAngle, optXDistanceToBall, optYDistanceToBall); - } -}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/SideKickOuter45.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/SideKickOuter45.h deleted file mode 100644 index 02fd9c89..00000000 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/SideKickOuter45.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include "Kick.h" -#include -#include - -class SideKickOuter45 : public Kick -{ - -public: - explicit SideKickOuter45() : Kick("SideKickOuter45", 900.f, 1250.f, false, 1.3f, 400.f, true, 45_deg, 120.f, 140.f) {} - - void perform(Ballchaser& ballchaser, const Pose2f& kickPose, const Vector2f& targetPosition, const bool start) const override - { - ThresholdUtils::setThresholdsForAnyKick(ballchaser); - ballchaser.optPosition = kickPose; - ballchaser.kickType = MotionRequest::KickType::walkKick; - ballchaser.kickTarget = targetPosition; - ballchaser.walkKickType = start ? WalkRequest::StepRequest::any : WalkRequest::StepRequest::none; - } - - [[nodiscard]] Pose2f getKickPose(const Pose2f& playerPose, const Vector2f& ballPosition, const Vector2f& targetPosition, bool leftFootClosestToBall) const override - { - const bool playerOnLeftSideOfKickDirection = Geometry::isPointLeftOfLine(playerPose.translation, ballPosition, targetPosition); - - return KickUtils::getKickPose(ballPosition, targetPosition, !playerOnLeftSideOfKickDirection, playerOnLeftSideOfKickDirection, optAngle, optXDistanceToBall, optYDistanceToBall); - } -}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/SideKickOuterFoot.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/SideKickOuterFoot.h deleted file mode 100644 index 1716dabe..00000000 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/SideKickOuterFoot.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include "Kick.h" -#include "Tools/Math/Transformation.h" -#include -#include - -class SideKickOuterFoot : public Kick -{ - -public: - explicit SideKickOuterFoot() : Kick("SideKickOuterFoot", 1100.f, 1300.f, false, 1.5f, 450.f, true, 90_deg, 0.f, 180.f) {} - - void perform(Ballchaser& ballchaser, const Pose2f& kickPose, const Vector2f& targetPosition, const bool start) const override - { - ThresholdUtils::setThresholdsForAnyKick(ballchaser); - ballchaser.optPosition = kickPose; - ballchaser.kickType = MotionRequest::KickType::walkKick; - ballchaser.kickTarget = targetPosition; - ballchaser.walkKickType = start ? WalkRequest::StepRequest::any : WalkRequest::StepRequest::none; - } - - [[nodiscard]] Pose2f getKickPose(const Pose2f& playerPose, const Vector2f& ballPosition, const Vector2f& targetPosition, bool leftFootClosestToBall) const override - { - const bool playerOnLeftSideOfKickDirection = Geometry::isPointLeftOfLine(playerPose.translation, ballPosition, targetPosition); - - return KickUtils::getKickPose(ballPosition, targetPosition, !playerOnLeftSideOfKickDirection, playerOnLeftSideOfKickDirection, optAngle, optXDistanceToBall, optYDistanceToBall); - } -}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/SlowLongKick.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/SlowLongKick.h deleted file mode 100644 index 1240b96f..00000000 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/SlowLongKick.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include -#include -#include "Kick.h" - -class SlowLongKick : public Kick -{ - -public: - explicit SlowLongKick() : Kick("SlowLongKick", 4000.f, 5700.f, false, 3.5f, 1300.f, false, 0_deg, 190.f, 55.f) {} - - void perform(Ballchaser& ballchaser, const Pose2f& kickPose, const Vector2f& targetPosition, const bool start) const override - { - ThresholdUtils::setThresholdsForLongKick(ballchaser); - ballchaser.optPosition = kickPose; - ballchaser.kickTarget = targetPosition; - ballchaser.kickType = MotionRequest::KickType::longKick; - ballchaser.longKickType = KickRequest::KickMotionID::kickMiddle; - } - - [[nodiscard]] Pose2f getKickPose(const Pose2f& playerPose, const Vector2f& ballPosition, const Vector2f& targetPosition, bool leftFootClosestToBall) const override - { - return KickUtils::getKickPose(getOptAngleField(playerPose.translation, ballPosition, targetPosition), ballPosition, leftFootClosestToBall, optXDistanceToBall, optYDistanceToBall); - } -}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Models/KickRange.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Models/KickRange.h deleted file mode 100644 index 378419ec..00000000 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Models/KickRange.h +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once - -#include -#include -#include "DistanceRequirement.h" -#include "Tools/Math/Eigen.h" - -class KickRange -{ - -public: - KickRange(Vector2f ballPosition, const Angle angle1, const Angle angle2, const float targetsDistance, DistanceRequirement distanceRequirement) - : ballPosition(std::move(ballPosition)) - { - targets = {}; - targets.emplace_back(this->ballPosition + MathUtils::angleToVector(angle1).normalized() * targetsDistance); - targets.emplace_back(this->ballPosition + MathUtils::angleToVector(angle2).normalized() * targetsDistance); - distanceRequirements = {}; - distanceRequirements.push_back(distanceRequirement); - } - - KickRange(Vector2f ballPosition, Vector2f target1, Vector2f target2, DistanceRequirement distanceRequirement) : ballPosition(std::move(ballPosition)) - { - targets = {}; - targets.push_back(std::move(target1)); - targets.push_back(std::move(target2)); - distanceRequirements = {}; - distanceRequirements.push_back(distanceRequirement); - } - - KickRange(Vector2f ballPosition, std::vector targets, DistanceRequirement distanceRequirement) : ballPosition(std::move(ballPosition)), targets(std::move(targets)) - { - const size_t count = this->targets.size() - 1; - distanceRequirements = {}; - for (size_t i = 0; i < count; ++i) - { - distanceRequirements.push_back(distanceRequirement); - } - } - - KickRange(Vector2f ballPosition, std::vector targets, std::vector distanceRequirements) - : ballPosition(std::move(ballPosition)), targets(std::move(targets)), distanceRequirements(std::move(distanceRequirements)) - { - ASSERT(this->targets.size() == this->distanceRequirements.size() + 1); - } - - virtual ~KickRange() = default; - - Vector2f ballPosition; - std::vector targets; - std::vector distanceRequirements; -}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Models/Optimize.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Models/Optimize.h deleted file mode 100644 index e5d286bd..00000000 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Models/Optimize.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -enum Optimize -{ - forTime, - forPrecisionWithoutDanger, - forPrecisionWithDanger, -}; \ No newline at end of file diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Constants.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Constants.h new file mode 100644 index 00000000..88c2758d --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Constants.h @@ -0,0 +1,2 @@ + +#define MAX_DISTANCE_FOR_HIGH_HYSTERESIS 300.f \ No newline at end of file diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Models/Danger.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Enums/Danger.h similarity index 78% rename from Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Models/Danger.h rename to Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Enums/Danger.h index 5744adc2..ca52cc17 100644 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Models/Danger.h +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Enums/Danger.h @@ -1,6 +1,6 @@ #pragma once -enum Danger +enum class Danger { IMPOSSIBLE, NONE, diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Models/DistanceRequirement.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Enums/DistanceRequirement.h similarity index 93% rename from Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Models/DistanceRequirement.h rename to Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Enums/DistanceRequirement.h index 76ffc3ce..af941ad3 100644 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Models/DistanceRequirement.h +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Enums/DistanceRequirement.h @@ -3,11 +3,11 @@ enum DistanceRequirement { mayShorterOrFurther, - onPoint, - mustFurther, - shouldFurther, mayFurther, - mustShorter, - shouldShorter, mayShorter, + shouldFurther, + shouldShorter, + mustFurther, + mustShorter, + onPoint }; \ No newline at end of file diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Enums/SelectedFoot.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Enums/SelectedFoot.h new file mode 100644 index 00000000..befa9a29 --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Enums/SelectedFoot.h @@ -0,0 +1,8 @@ +#pragma once + +enum class SelectedFoot +{ + LEFT, + NONE, + RIGHT +}; \ No newline at end of file diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/DrawFunctions.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/DrawFunctions.h new file mode 100644 index 00000000..9528d2fe --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/DrawFunctions.h @@ -0,0 +1,109 @@ +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Logs/KickDrawings.h" +#include "Representations/Modeling/HeatMap.h" + +class DrawFunctions +{ + +public: + static void drawInGrid(const std::vector& draw_targets, const std::vector& draw_scores, const FieldDimensions& theFieldDimensions) + { + COMPLEX_DRAWING(DRAW_EXECUTABLE_KICKS_IN_GRID) + { + const bool STRETCH = false; + + std::vector draw_indexes = {}; + for (const Vector2f& draw_target : draw_targets) + { + const int draw_index = HeatMap::fieldToIndex(draw_target, theFieldDimensions); + draw_indexes.push_back(draw_index); + } + + std::vector unique_draw_indexes = {}; + std::vector unique_draw_scores = {}; + for (size_t i = 0; i < draw_indexes.size(); i++) + { + const int index = draw_indexes.at(i); + const float score = draw_scores.at(i); + + bool foundEqualTarget = false; + for (size_t j = 0; j < unique_draw_indexes.size(); j++) + { + const int uniqueIndex = unique_draw_indexes.at(j); + const float uniqueScore = unique_draw_scores.at(j); + + if (index == uniqueIndex) + { + if (score > uniqueScore) + { + unique_draw_scores.at(j) = score; + } + foundEqualTarget = true; + break; + } + } + if (!foundEqualTarget) + { + unique_draw_indexes.push_back(index); + unique_draw_scores.push_back(score); + } + } + + if (STRETCH) + { + MathUtils::stretch(unique_draw_scores); + } + + const size_t size = unique_draw_scores.size(); + for (size_t i = 0; i < size; ++i) + { + const Vector2f draw_target = HeatMap::indexToField(unique_draw_indexes[i], theFieldDimensions); + float draw_score = unique_draw_scores[i]; + if (!STRETCH) + { + draw_score = (MathUtils::clamp_f(draw_score, -1.5f, 1.5f) + 1.5f) / 3.f; + } + CIRCLE(DRAW_EXECUTABLE_KICKS_IN_GRID, draw_target.x(), draw_target.y(), 50, 0, Drawings::noPen, ColorRGBA::black, Drawings::solidBrush, ColorRGBA((char)(255 * (1 - draw_score)), (char)(255 * draw_score), 0)); + } + } + } + + static void drawFree(const std::vector& draw_targets, std::vector& draw_scores) + { + COMPLEX_DRAWING(DRAW_EXECUTABLE_KICKS_FREELY) + { + const bool STRETCH = true; + + if (STRETCH) + { + MathUtils::stretch(draw_scores); + } + + const size_t size = draw_targets.size(); + for (size_t i = 0; i < size; ++i) + { + const Vector2f& draw_target = draw_targets.at(i); + float draw_score = draw_scores.at(i); + if (!STRETCH) + { + draw_score = (MathUtils::clamp_f(draw_score, -1.5f, 1.5f) + 1.5f) / 3.f; + } + CIRCLE(DRAW_EXECUTABLE_KICKS_FREELY, draw_target.x(), draw_target.y(), 30, 0, Drawings::noPen, ColorRGBA::black, Drawings::solidBrush, ColorRGBA((char)(255 * (1 - draw_score)), (char)(255 * draw_score), 0)); + } + } + } + + static void drawKickMinObstacleWidth(const Vector2f& ballPosition, const Vector2f& targetPosition, const float width) + { + Vector2f w = Vector2f(targetPosition - ballPosition).normalized() * width; + w = w.rotate(90_deg); + + const Vector2f p11 = ballPosition - w; + const Vector2f p12 = ballPosition + w; + + const Vector2f p21 = targetPosition - w; + const Vector2f p22 = targetPosition + w; + + LINE(DRAW_KICK_MIN_WIDTH, p11.x(), p11.y(), p21.x(), p21.y(), 10, Drawings::solidPen, ColorRGBA::orange); + LINE(DRAW_KICK_MIN_WIDTH, p12.x(), p12.y(), p22.x(), p22.y(), 10, Drawings::solidPen, ColorRGBA::orange); + } +}; \ No newline at end of file diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/ScoreFunctions.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/ScoreFunctions.cpp new file mode 100644 index 00000000..f1fe9f82 --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/ScoreFunctions.cpp @@ -0,0 +1,251 @@ +#include "ScoreFunctions.h" + +void ScoreFunctions::scoreKickPlan( + KickPlan& kickPlan, const Factors& factors, const FieldDimensions& theFieldDimensions, const HeatMapCollection& theHeatMapCollection, const RobotMap& theRobotMap, const TacticSymbols& theTacticSymbols) +{ + kickPlan.selectablePose.selectableTarget.selectableKick.score = scoreKick(kickPlan.selectablePose.selectableTarget.selectableKick.kick, factors.timeFactor, factors.inaccuracyFactor); + kickPlan.selectablePose.selectableTarget.score = scoreTarget(kickPlan.selectablePose.selectableTarget, + factors.widthFactor, + factors.sidesHeatFactor, + factors.goalsHeatFactor, + factors.teammatesKickHeatFactor, + factors.teammatesGoalKickHeatFactor, + factors.opponentsKickHeatFactor, + factors.opponentsGoalKickHeatFactor, + theFieldDimensions, + theHeatMapCollection, + theRobotMap); + kickPlan.selectablePose.score = scorePose(kickPlan.selectablePose, factors.dontRuntIntoFactor, factors.blockDefensiveCone, factors.blockOpponentFactor, factors.timeFactor, theTacticSymbols); + kickPlan.score = kickPlan.selectablePose.selectableTarget.selectableKick.score + kickPlan.selectablePose.selectableTarget.score + kickPlan.selectablePose.score; + for (const auto& customScoreFunction : factors.customScoreFunctions) + { + kickPlan.score += customScoreFunction(kickPlan); + } +} + +float ScoreFunctions::scoreTarget(const SelectableTarget& selectableTarget, + const float widthFactor, + const float sidesHeatFactor, + const float goalsHeatFactor, + const float teammatesKickHeatFactor, + const float teammatesGoalKickHeatFactor, + const float opponentsKickHeatFactor, + const float opponentsGoalKickHeatFactor, + const FieldDimensions& theFieldDimensions, + const HeatMapCollection& theHeatMapCollection, + const RobotMap& theRobotMap) +{ + ASSERT(widthFactor >= 0); + // sidesHeatFactor depends on situation + ASSERT(goalsHeatFactor >= 0); + ASSERT(teammatesKickHeatFactor >= 0); + ASSERT(teammatesGoalKickHeatFactor >= 0); + ASSERT(opponentsKickHeatFactor <= 0); + ASSERT(opponentsGoalKickHeatFactor <= 0); + + float score = 0; + if (!MathUtils::isEqual(0.f, widthFactor)) + { + const float widthScore = getWidthScore(selectableTarget.ballPosition, selectableTarget.target, widthFactor, theFieldDimensions, theRobotMap); + score += widthScore; + } + const int index = HeatMap::fieldToIndex(selectableTarget.target, theFieldDimensions); + score += sidesHeatFactor * theHeatMapCollection.sidesHeatMap.getHeat(index); + score += goalsHeatFactor * theHeatMapCollection.goalsHeatMap.getHeat(index); + score += teammatesKickHeatFactor * theHeatMapCollection.teamKickHeatMap.getHeat(index); + score += teammatesGoalKickHeatFactor * theHeatMapCollection.teamGoalKickHeatMap.getHeat(index); + score += opponentsKickHeatFactor * theHeatMapCollection.opponentKickHeatMap.getHeat(index); + score += opponentsGoalKickHeatFactor * theHeatMapCollection.opponentGoalKickHeatMap.getHeat(index); + return score; +} + +float ScoreFunctions::getWidthScore(const Vector2f& ballPosition, const Vector2f& targetPosition, const float widthFactor, const FieldDimensions& theFieldDimensions, const RobotMap& theRobotMap) +{ + const float MAX_WIDTH = 750.f; + const float width = KickUtils::getMinKickToObstaclesDistance(ballPosition, targetPosition, theFieldDimensions, theRobotMap); + const float widthValue = std::pow(MathUtils::clamp_f(width, 0.f, MAX_WIDTH) / MAX_WIDTH, 1 / 3.f); // TODO Constant + return widthFactor * widthValue; +} + +float ScoreFunctions::scorePose( + const SelectablePose& selectablePose, const float dontRuntIntoFactor, const float blockDefensiveCone, const float blockOpponentFactor, const float timeFactor, const TacticSymbols& theTacticSymbols) +{ + float score = 0; + score += getDontRuntIntoScore(selectablePose, dontRuntIntoFactor, theTacticSymbols); + score += getBlockDefensiveConeScore(selectablePose, blockDefensiveCone, theTacticSymbols); + score += getBlockOpponentScore(selectablePose, blockOpponentFactor, theTacticSymbols); + score += getTimeScore(selectablePose, timeFactor); + return score; +} + +float ScoreFunctions::getDontRuntIntoScore(const SelectablePose& selectablePose, const float dontRuntIntoFactor, const TacticSymbols& theTacticSymbols) +{ + ASSERT(dontRuntIntoFactor >= 0); + + const Vector2f& ballPosition = selectablePose.selectableTarget.ballPosition; + const Vector2f& targetPosition = selectablePose.selectableTarget.target; + Kick* kick = selectablePose.selectableTarget.selectableKick.kick; + + float score = 0; + + if (theTacticSymbols.closeToBallRobotNumber > 0 && kick->getOptAngle() < 10_deg) + { + const Vector2f& robotPosition = theTacticSymbols.closeToBallRobot.translation; + + const Pose2f kickLinePose = Pose2f((targetPosition - ballPosition).angle(), ballPosition); + const Angle kickLineToRobotAngle = Transformation::fieldToRobot(kickLinePose, robotPosition).angle(); + const Angle absKickLineToRobotAngle = std::abs(kickLineToRobotAngle); + + const Angle A_MIN = 50_deg; + const Angle MAX_DYNAMIC_START = 130_deg; + const Angle MAX_DYNAMIC_END = 170_deg; + + if (absKickLineToRobotAngle < A_MIN) + { + // Is ahead, give score for both feet + score += dontRuntIntoFactor; + } + else + { + float value; + if (absKickLineToRobotAngle < MAX_DYNAMIC_START) + { + value = 1.f; + } + else + { + value = std::max(0.f, 1 - (absKickLineToRobotAngle - MAX_DYNAMIC_START) / (MAX_DYNAMIC_END - MAX_DYNAMIC_START)); + ASSERT(value <= 1.f); + } + const float dontRunIntoScore = dontRuntIntoFactor * value; + if (kickLineToRobotAngle > 0.f) + { + // Is left, give score for left foot + score += selectablePose.kickWithLeft ? dontRunIntoScore : 0.f; + } + else + { + // Is right, give score for right foot + score += selectablePose.kickWithLeft ? 0.f : dontRunIntoScore; + } + } + } + return score; +} + +float ScoreFunctions::getBlockDefensiveConeScore(const SelectablePose& selectablePose, const float blockDefensiveConeFactor, const TacticSymbols& theTacticSymbols) +{ + if (MathUtils::isEqual(blockDefensiveConeFactor, 0.f)) + { + return 0.f; + } + ASSERT(blockDefensiveConeFactor >= 0.f); + + if (theTacticSymbols.closeToBallOpponentRobotNumber == 0) + { + return 0.f; + } + const Vector2f& ballPosition = selectablePose.selectableTarget.ballPosition; + const Angle ballToKickPoseAngle = (selectablePose.pose.translation - ballPosition).angle(); + return MathUtils::getCloseToMiddlePercent(ballToKickPoseAngle, theTacticSymbols.defensiveCone.left, theTacticSymbols.defensiveCone.right); +} + +/** + * @return Higher score the closer the robot is to the opposite site of the opponent + */ +float ScoreFunctions::getBlockOpponentScore(const SelectablePose& selectablePose, const float blockOpponentFactor, const TacticSymbols& theTacticSymbols) +{ + if (MathUtils::isEqual(blockOpponentFactor, 0.f)) + { + return 0.f; + } + ASSERT(blockOpponentFactor >= 0.f); + + if (theTacticSymbols.closeToBallOpponentRobotNumber != 1) + { + return 0.f; + } + + const Vector2f& ballPosition = selectablePose.selectableTarget.ballPosition; + + const Angle ballToKickPoseAngle = (selectablePose.pose.translation - ballPosition).angle(); + const Angle ballToOpponentAngle = (theTacticSymbols.closeToBallOpponentRobot.translation - ballPosition).angle(); + + const Angle diff = MathUtils::getAngleSmallestDiff(ballToKickPoseAngle, ballToOpponentAngle); + const float value = diff / 180_deg; + return blockOpponentFactor * value; +} + +float ScoreFunctions::getTimeScore(const SelectablePose& selectablePose, const float timeFactor) +{ + ASSERT(timeFactor <= 0.f); + + const Pose2f playerPose = selectablePose.playerPose; + const Vector2f ballPosition = selectablePose.selectableTarget.ballPosition; + + const float MAX_TIME = 18.f; + const float time = PathUtils::getPathTime(playerPose, selectablePose.pose, ballPosition); + if (time > MAX_TIME) + { + OUTPUT_WARNING("The constant value MAX_TIME=" << MAX_TIME << " < time=" << time << "! Increase MAX_TIME!"); + return timeFactor; + } + return timeFactor * time / MAX_TIME; +} + +float ScoreFunctions::scoreKick(const Kick* kick, float timeFactor, float inaccuracyFactor) +{ + ASSERT(timeFactor <= 0); + ASSERT(inaccuracyFactor <= 0); + + float score = 0; + + const float MAX_TIME = 5.f; + const float time = kick->getTime(false); + if (time > MAX_TIME) + { + OUTPUT_WARNING("The constant value MAX_TIME=" << MAX_TIME << " < time=" << time << "! Increase MAX_TIME!"); + return timeFactor; + } + score += timeFactor * time / MAX_TIME; + + const float MAX_INACCURACY = 2000.f; + const float inaccuracy = kick->getHorizontalInaccuracy(); + score += inaccuracyFactor * std::min(inaccuracy, MAX_INACCURACY) / MAX_INACCURACY; + + return score; +} + +float ScoreFunctions::applyHysteresis(const float score, const float distanceToKick) +{ + const float MAX_FOR_HIGH_HYSTERESIS = MAX_DISTANCE_FOR_HIGH_HYSTERESIS; + const float HIGH_HYSTERESIS_ADD = 2.f; + const float HIGH_HYSTERESIS_MULTIPLIER = 3.f; + + const float MAX_FOR_MEDIUM_HYSTERESIS = 500.f; + const float MEDIUM_HYSTERESIS_ADD = 0.5f; + const float MEDIUM_HYSTERESIS_MULTIPLIER = 1.5f; + + const float LOW_HYSTERESIS_ADD = 0.01f; + const float LOW_HYSTERESIS_MULTIPLIER = 1.01f; + + float hysteresisAdd; + float hysteresisMultiplier; + if (distanceToKick < MAX_FOR_HIGH_HYSTERESIS) + { + hysteresisAdd = HIGH_HYSTERESIS_ADD; + hysteresisMultiplier = HIGH_HYSTERESIS_MULTIPLIER; + } + else if (distanceToKick < MAX_FOR_MEDIUM_HYSTERESIS) + { + hysteresisAdd = MEDIUM_HYSTERESIS_ADD; + hysteresisMultiplier = MEDIUM_HYSTERESIS_MULTIPLIER; + } + else + { + hysteresisAdd = LOW_HYSTERESIS_ADD; + hysteresisMultiplier = LOW_HYSTERESIS_MULTIPLIER; + } + + return HysteresisUtils::makeBigger(score, hysteresisAdd, hysteresisMultiplier); +} \ No newline at end of file diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/ScoreFunctions.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/ScoreFunctions.h new file mode 100644 index 00000000..7e616cff --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/ScoreFunctions.h @@ -0,0 +1,56 @@ +#pragma once + +#include "Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Constants.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Selectables/KickPlan.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Selectables/SelectablePose.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Selectables/SelectableTarget.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Utils/HysteresisUtils.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Utils/PathUtils.h" +#include "Representations/Configuration/FieldDimensions.h" +#include "Representations/Modeling/HeatMapCollection.h" +#include "Representations/Modeling/KickWheel.h" +#include "Representations/Modeling/RobotMap.h" +#include "Tools/Math/Geometry.h" +#include "Tools/Math/Transformation.h" +#include +#include +#include +#include +#include + +class ScoreFunctions +{ + +public: + static void scoreKickPlan( + KickPlan& kickPlan, const Factors& factors, const FieldDimensions& theFieldDimensions, const HeatMapCollection& theHeatMapCollection, const RobotMap& theRobotMap, const TacticSymbols& theTacticSymbols); + + static float scoreTarget(const SelectableTarget& selectableTarget, + const float widthFactor, + const float sidesHeatFactor, + const float goalsHeatFactor, + const float teammatesKickHeatFactor, + const float teammatesGoalKickHeatFactor, + const float opponentsKickHeatFactor, + const float opponentsGoalKickHeatFactor, + const FieldDimensions& theFieldDimensions, + const HeatMapCollection& theHeatMapCollection, + const RobotMap& theRobotMap); + + static float getWidthScore(const Vector2f& ballPosition, const Vector2f& targetPosition, const float widthFactor, const FieldDimensions& theFieldDimensions, const RobotMap& theRobotMap); + + static float scorePose( + const SelectablePose& selectablePose, const float dontRuntIntoFactor, const float blockDefensiveCone, const float blockOpponentFactor, const float timeFactor, const TacticSymbols& theTacticSymbols); + + static float getDontRuntIntoScore(const SelectablePose& selectablePose, const float dontRuntIntoFactor, const TacticSymbols& theTacticSymbols); + + static float getBlockDefensiveConeScore(const SelectablePose& selectablePose, const float blockDefensiveConeFactor, const TacticSymbols& theTacticSymbols); + + static float getBlockOpponentScore(const SelectablePose& selectablePose, const float blockOpponentFactor, const TacticSymbols& theTacticSymbols); + + static float getTimeScore(const SelectablePose& selectablePose, const float timeFactor); + + static float scoreKick(const Kick* kick, float timeFactor, float inaccuracyFactor); + + static float applyHysteresis(const float score, const float distanceToKick); +}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/SelectFunctions.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/SelectFunctions.h new file mode 100644 index 00000000..8e4cbffd --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/SelectFunctions.h @@ -0,0 +1,56 @@ +#pragma once + +#include "Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/DrawFunctions.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/ScoreFunctions.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/CurrentKick.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Factors.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Filterer/Filterer.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Selectables/KickPlan.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Selectables/SelectablePose.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Selectables/SelectableTarget.h" +#include "Representations/Modeling/KickWheel.h" +#include "Tools/Math/Geometry.h" +#include "Tools/Math/Transformation.h" +#include +#include +#include +#include + +class SelectFunctions +{ + +public: + static std::optional createAndFilterAndSelect(const Pose2f& playerPose, + const Vector2f& ballPosition, + const std::vector& kicks, + const std::optional& currentKick, + const Filterer& filterer, + const Factors& factors, + const FieldDimensions& theFieldDimensions, + const HeatMapCollection& theHeatMapCollection, + const KickWheel& theKickWheel, + const RobotMap& theRobotMap, + const TacticSymbols& theTacticSymbols); + + static std::optional filterAndSelect(std::vector& kickPlans, + const Filterer& filterer, + const Factors& factors, + const FieldDimensions& theFieldDimensions, + const HeatMapCollection& theHeatMapCollection, + const RobotMap& theRobotMap, + const TacticSymbols& theTacticSymbols, + const KickWheel& theKickWheel); + +private: + static std::vector getSelectableDirections(const Vector2f& ballPosition, const Filterer& filterer, const KickWheel& theKickWheel); + + static void addSelectableTargets(std::vector& selectableTargets, const SelectableDirection& selectableDirection, Kick* kick, const Filterer& filterer); + static void addSelectableTargetsFromStaticKick( + std::vector& selectableTargets, const SelectableDirection& selectableDirection, const SelectableKick& selectableKick, const Filterer& filterer); + static void addSelectableTargetsFromAdjustableKick(std::vector& selectableTargets, const SelectableDirection& selectableDirection, Kick* kick, const Filterer& filterer); + static void addSelectableTargetsForDistance( + std::vector& selectableTargets, const float targetDistance, const SelectableDirection& selectableDirection, const SelectableKick& selectableKick, const Filterer& filterer); + + static std::optional getBestSelectablePose( + const Pose2f& playerPose, const SelectableTarget& selectableTarget, const Filterer& filterer, const Factors& factors, const TacticSymbols& theTacticSymbols); +}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/SelectFunctions_.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/SelectFunctions_.cpp new file mode 100644 index 00000000..e201b4f3 --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/SelectFunctions_.cpp @@ -0,0 +1,192 @@ +#include "SelectFunctions.h" + +#include +#include + +std::optional SelectFunctions::createAndFilterAndSelect(const Pose2f& playerPose, + const Vector2f& ballPosition, + const std::vector& kicks, + const std::optional& currentKick, + const Filterer& filterer, + const Factors& factors, + const FieldDimensions& theFieldDimensions, + const HeatMapCollection& theHeatMapCollection, + const KickWheel& theKickWheel, + const RobotMap& theRobotMap, + const TacticSymbols& theTacticSymbols) +{ + std::vector selectableDirections = getSelectableDirections(ballPosition, filterer, theKickWheel); + if (selectableDirections.empty()) + { + return {}; + } + + const std::vector> selectableKickFilters = filterer.getEfficientlyOrderedSelectableKickFilters(); + + std::vector draw_targets = {}; + std::vector draw_scores = {}; + std::optional bestKickPlan = {}; + + for (Kick* kick : kicks) + { + SelectableKick selectableKick = {kick}; + bool valid = true; + for (const auto& filter : selectableKickFilters) + { + if (filter(selectableKick)) + { + valid = false; + break; + } + } + if (!valid) + { + continue; + } + + std::vector selectableTargets = {}; + for (const SelectableDirection& selectableDirection : selectableDirections) + { + addSelectableTargets(selectableTargets, selectableDirection, kick, filterer); + } + if (selectableTargets.empty()) + { + continue; + } + + std::vector selectablePoses = {}; + for (SelectableTarget& selectableTarget : selectableTargets) + { + auto selectablePoseOptional = getBestSelectablePose(playerPose, selectableTarget, filterer, factors, theTacticSymbols); + if (selectablePoseOptional.has_value()) + { + selectablePoses.push_back(selectablePoseOptional.value()); + } + } + + for (SelectablePose& selectablePose : selectablePoses) + { + KickPlan kickPlan = {selectablePose, false}; + ScoreFunctions::scoreKickPlan(kickPlan, factors, theFieldDimensions, theHeatMapCollection, theRobotMap, theTacticSymbols); + + draw_targets.push_back(kickPlan.selectablePose.selectableTarget.target); + draw_scores.push_back(kickPlan.score); + + if (!bestKickPlan.has_value() || kickPlan.score > bestKickPlan->score) + { + bestKickPlan = kickPlan; + } + } + } + + DrawFunctions::drawFree(draw_targets, draw_scores); + + if (currentKick.has_value()) + { + std::vector kickPlans = {}; + kickPlans.emplace_back(currentKick.value().toKickPlan(playerPose, ballPosition)); + auto currentKickPlanOptional = filterAndSelect(kickPlans, filterer, factors, theFieldDimensions, theHeatMapCollection, theRobotMap, theTacticSymbols, theKickWheel); + if (currentKickPlanOptional.has_value()) + { + if (bestKickPlan.has_value()) + { + auto currentKickPlan = currentKickPlanOptional.value(); + const float distanceToKick = Geometry::distance(playerPose.translation, ballPosition); + currentKickPlan.score = ScoreFunctions::applyHysteresis(currentKickPlan.score, distanceToKick); + if (currentKickPlan.score > bestKickPlan->score) + { + return currentKickPlanOptional; + } + } + else + { + return currentKickPlanOptional; + } + } + } + + return bestKickPlan; +} + +std::optional SelectFunctions::filterAndSelect(std::vector& kickPlans, + const Filterer& filterer, + const Factors& factors, + const FieldDimensions& theFieldDimensions, + const HeatMapCollection& theHeatMapCollection, + const RobotMap& theRobotMap, + const TacticSymbols& theTacticSymbols, + const KickWheel& theKickWheel) +{ + const std::vector> selectableKickFilters = filterer.getEfficientlyOrderedSelectableKickFilters(); + const std::vector> selectableDirectionFilters = filterer.getEfficientlyOrderedSelectableDirectionFilters(); + const std::vector> selectableTargetFilters = filterer.getEfficientlyOrderedSelectableTargetFilters(); + const std::vector> selectablePoseFilters = filterer.getEfficientlyOrderedSelectablePoseFilters(); + + std::optional bestKickPlan = {}; + for (auto& kickPlan : kickPlans) + { + bool valid = true; + for (const auto& filter : selectableKickFilters) + { + if (filter(kickPlan.selectablePose.selectableTarget.selectableKick)) + { + valid = false; + break; + } + } + if (!valid) + { + continue; + } + const Angle angle = (kickPlan.selectablePose.selectableTarget.target - kickPlan.selectablePose.selectableTarget.ballPosition).angle(); + const DistanceInfo distanceInfo = theKickWheel.getDistance(angle, kickPlan.hysteresis); + const float requiredDistance = Geometry::distance(kickPlan.selectablePose.selectableTarget.ballPosition, kickPlan.selectablePose.selectableTarget.target); + if (distanceInfo.distance < requiredDistance) + { + continue; + } + for (const auto& filter : selectableDirectionFilters) + { + SelectableDirection selectableDirection = {kickPlan.selectablePose.selectableTarget.ballPosition, angle, distanceInfo.distance, distanceInfo.distanceBlocked, distanceInfo.distanceOutside}; + if (filter(selectableDirection)) + { + valid = false; + break; + } + } + if (!valid) + { + continue; + } + for (const auto& filter : selectableTargetFilters) + { + if (filter(kickPlan.selectablePose.selectableTarget)) + { + valid = false; + break; + } + } + if (!valid) + { + continue; + } + for (const auto& filter : selectablePoseFilters) + { + if (filter(kickPlan.selectablePose)) + { + valid = false; + break; + } + } + if (!valid) + { + continue; + } + ScoreFunctions::scoreKickPlan(kickPlan, factors, theFieldDimensions, theHeatMapCollection, theRobotMap, theTacticSymbols); + if (!bestKickPlan.has_value() || kickPlan.score > bestKickPlan.value().score) + { + bestKickPlan = kickPlan; + } + } + return bestKickPlan; +} \ No newline at end of file diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/SelectFunctions_Direction.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/SelectFunctions_Direction.cpp new file mode 100644 index 00000000..cfa07db3 --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/SelectFunctions_Direction.cpp @@ -0,0 +1,55 @@ +#include "SelectFunctions.h" + +std::vector SelectFunctions::getSelectableDirections(const Vector2f& ballPosition, const Filterer& filterer, const KickWheel& theKickWheel) +{ + std::vector selectableDirections = {}; + const int max_i = (int)theKickWheel.blockedDistances.size(); + for (int i = 0; i < max_i; i++) + { + float distance = std::numeric_limits::max(); + + bool distanceBlocked = false; + bool distanceOutside = false; + + const float blockedDistance = theKickWheel.blockedDistances.at(i); + if (blockedDistance < distance) + { + if (filterer.isFilterBlocked()) + { + continue; + } + distance = blockedDistance; + distanceBlocked = true; + } + + const float outsideDistance = theKickWheel.outsideDistances.at(i); + if (outsideDistance < distance) + { + distanceBlocked = false; + if (filterer.isFilterOutside()) + { + distance = outsideDistance; + } + distanceOutside = true; + } + + SelectableDirection selectableDirection = {ballPosition, theKickWheel.angles.at(i), distance, distanceBlocked, distanceOutside}; + + bool remove = false; + for (const auto& filter : filterer.getEfficientlyOrderedSelectableDirectionFilters()) + { + if ((filter)(selectableDirection)) + { + remove = true; + break; + } + } + if (remove) + { + continue; + } + + selectableDirections.push_back(selectableDirection); + }; + return selectableDirections; +} \ No newline at end of file diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/SelectFunctions_Pose.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/SelectFunctions_Pose.cpp new file mode 100644 index 00000000..11820c7a --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/SelectFunctions_Pose.cpp @@ -0,0 +1,48 @@ +#include "SelectFunctions.h" + +std::optional SelectFunctions::getBestSelectablePose( + const Pose2f& playerPose, const SelectableTarget& selectableTarget, const Filterer& filterer, const Factors& factors, const TacticSymbols& theTacticSymbols) +{ + const Pose2f pose1 = selectableTarget.selectableKick.kick->getKickPose(selectableTarget.ballPosition, selectableTarget.target, true); + const Pose2f pose2 = selectableTarget.selectableKick.kick->getKickPose(selectableTarget.ballPosition, selectableTarget.target, false); + SelectablePose selectablePose1 = {playerPose, selectableTarget, pose1, true}; + SelectablePose selectablePose2 = {playerPose, selectableTarget, pose2, false}; + bool pose1Valid = true; + bool pose2Valid = true; + for (const auto& filter : filterer.getEfficientlyOrderedSelectablePoseFilters()) + { + if (pose1Valid && filter(selectablePose1)) + { + pose1Valid = false; + } + if (pose2Valid && filter(selectablePose2)) + { + pose2Valid = false; + } + } + + if (pose1Valid && pose2Valid) + { + selectablePose1.score = ScoreFunctions::scorePose(selectablePose1, factors.dontRuntIntoFactor, factors.blockDefensiveCone, factors.blockOpponentFactor, factors.timeFactor, theTacticSymbols); + selectablePose2.score = ScoreFunctions::scorePose(selectablePose2, factors.dontRuntIntoFactor, factors.blockDefensiveCone, factors.blockOpponentFactor, factors.timeFactor, theTacticSymbols); + if (selectablePose1.score > selectablePose2.score) + { + return selectablePose1; + } + else + { + return selectablePose2; + } + } + if (pose1Valid) + { + selectablePose1.score = ScoreFunctions::scorePose(selectablePose1, factors.dontRuntIntoFactor, factors.blockDefensiveCone, factors.blockOpponentFactor, factors.timeFactor, theTacticSymbols); + return selectablePose1; + } + if (pose2Valid) + { + selectablePose2.score = ScoreFunctions::scorePose(selectablePose2, factors.dontRuntIntoFactor, factors.blockDefensiveCone, factors.blockOpponentFactor, factors.timeFactor, theTacticSymbols); + return selectablePose2; + } + return {}; +} \ No newline at end of file diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/SelectFunctions_Target.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/SelectFunctions_Target.cpp new file mode 100644 index 00000000..c22e0203 --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/SelectFunctions_Target.cpp @@ -0,0 +1,82 @@ +#include "SelectFunctions.h" + +void SelectFunctions::addSelectableTargets(std::vector& selectableTargets, const SelectableDirection& selectableDirection, Kick* kick, const Filterer& filterer) +{ + if (kick->isDistanceAdjustable()) + { + addSelectableTargetsFromAdjustableKick(selectableTargets, selectableDirection, kick, filterer); + } + else + { + addSelectableTargetsFromStaticKick(selectableTargets, selectableDirection, kick, filterer); + } +} + +void SelectFunctions::addSelectableTargetsFromAdjustableKick(std::vector& selectableTargets, const SelectableDirection& selectableDirection, Kick* kick, const Filterer& filterer) +{ + const int DISTANCE_STEPS_COUNT = 5; + + float maxDistance; + if (selectableDirection.distanceOutside) + { + const float MIN_DISTANCE_TO_BORDER = 200.f; + maxDistance = selectableDirection.distance - MIN_DISTANCE_TO_BORDER; + } + else + { + maxDistance = std::min(selectableDirection.distance, kick->getMaxDistance(false, true)); + } + const float distanceStepSize = maxDistance / DISTANCE_STEPS_COUNT; + for (float targetDistance = kick->getMinDistance(false, false); targetDistance < maxDistance; targetDistance = targetDistance + distanceStepSize) + { + addSelectableTargetsForDistance(selectableTargets, targetDistance, selectableDirection, kick, filterer); + } +} + +void SelectFunctions::addSelectableTargetsFromStaticKick( + std::vector& selectableTargets, const SelectableDirection& selectableDirection, const SelectableKick& selectableKick, const Filterer& filterer) +{ + float targetDistance; + if (selectableDirection.distanceOutside) + { + const float minDistance = selectableKick.kick->getMinDistance(false, false); + const float maxDistance = selectableKick.kick->getMaxDistance(false, false); + const float probablyNotTooFarDistance = maxDistance + (maxDistance - minDistance) / 2; // TODO Hysteresis + if (selectableDirection.distance < probablyNotTooFarDistance) + { + return; + } + targetDistance = selectableKick.kick->getRealisticDistance(); + } + else + { + if (!selectableDirection.distanceBlocked && selectableDirection.distance < selectableKick.kick->getMinDistance(false, false)) // TODO Hysteresis + { + return; + } + targetDistance = std::min(selectableDirection.distance, selectableKick.kick->getRealisticDistance()); + } + + addSelectableTargetsForDistance(selectableTargets, targetDistance, selectableDirection, selectableKick, filterer); +} + +void SelectFunctions::addSelectableTargetsForDistance( + std::vector& selectableTargets, const float targetDistance, const SelectableDirection& selectableDirection, const SelectableKick& selectableKick, const Filterer& filterer) +{ + Vector2f targetPosition = selectableDirection.ballPosition + targetDistance * selectableDirection.direction; + SelectableTarget selectableTarget = {selectableKick, selectableDirection.ballPosition, targetPosition}; + bool remove = false; + for (const auto& filter : filterer.getEfficientlyOrderedSelectableTargetFilters()) + { + if ((filter)(selectableTarget)) + { + remove = true; + break; + } + } + if (remove) + { + return; + } + selectableTargets.push_back(selectableTarget); +} \ No newline at end of file diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/TargetFunctions.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/TargetFunctions.h new file mode 100644 index 00000000..0873d2e6 --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/TargetFunctions.h @@ -0,0 +1,68 @@ +#include "Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/KickManager.h" + +#include "Modules/BehaviorControl/TacticControl/KicksProvider/Kick.h" +#include "Modules/BehaviorControl/TacticControl/KicksProvider/KickWithLeftUtils.h" +#include "SelectFunctions.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Factors.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Filterer/Filterer.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Utils/KickUtils.h" +#include "Representations/MotionControl/WalkingEngineParams.h" +#include "Tools/Math/Eigen.h" +#include +#include + +class TargetFunctions +{ + +public: + static std::optional getKickPlanForTarget(const Pose2f& playerPose, + const Vector2f& ballPosition, + const Vector2f& targetPosition, + const DistanceRequirement distanceRequirement, + const std::vector& kicks, + const std::optional& currentKick, + const Filterer& filterer, + const Factors& factors, + const FieldDimensions& theFieldDimensions, + const HeatMapCollection& theHeatMapCollection, + const RobotMap& theRobotMap, + const TacticSymbols& theTacticSymbols, + const KickWheel& theKickWheel) + { + const float distance = Geometry::distance(ballPosition, targetPosition); + + std::vector kickPlans = {}; + + for (Kick* kick : kicks) + { + if (!KickUtils::fulfillsDistanceRequirements(*kick, distance, distanceRequirement, false)) + { + continue; + } + + SelectableKick selectableKick = {kick}; + + Vector2f kickTargetPosition = kick->getRealisticTarget(ballPosition, targetPosition); + SelectableTarget selectableTarget = {selectableKick, ballPosition, targetPosition}; + + Pose2f kickPose1 = kick->getKickPose(ballPosition, kickTargetPosition, true); + Pose2f kickPose2 = kick->getKickPose(ballPosition, kickTargetPosition, false); + SelectablePose selectablePose1 = {playerPose, selectableTarget, kickPose1, true}; + SelectablePose selectablePose2 = {playerPose, selectableTarget, kickPose2, false}; + kickPlans.emplace_back(selectablePose1, false); + kickPlans.emplace_back(selectablePose2, false); + } + + if (currentKick.has_value()) + { + const float oldToNewTargetDistance = Geometry::distance(targetPosition, currentKick->target); + const bool isSameTarget = oldToNewTargetDistance > 300.f; + if (isSameTarget && KickUtils::fulfillsDistanceRequirements(*currentKick->kick, distance, distanceRequirement, true)) + { + kickPlans.emplace_back(currentKick.value().toKickPlan(playerPose, ballPosition)); + } + } + + return SelectFunctions::filterAndSelect(kickPlans, filterer, factors, theFieldDimensions, theHeatMapCollection, theRobotMap, theTacticSymbols, theKickWheel); + } +}; \ No newline at end of file diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/CurrentKick.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/CurrentKick.h new file mode 100644 index 00000000..0043c439 --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/CurrentKick.h @@ -0,0 +1,30 @@ +#pragma once + +#include "Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Selectables/KickPlan.h" +#include "Tools/Math/Geometry.h" +#include +#include +#include +#include + +class CurrentKick +{ + +public: + CurrentKick() : target(Vector2f()), kick(nullptr), kickWithLeft(false) {} + + CurrentKick(const Vector2f& target, Kick* kick, const bool kickWithLeft) : target(std::move(target)), kick(kick), kickWithLeft(kickWithLeft) {} + + [[nodiscard]] KickPlan toKickPlan(const Pose2f& playerPose, const Vector2f& ballPosition) const + { + SelectableKick selectableKick = {kick}; + SelectableTarget selectableTarget = {selectableKick, ballPosition, target}; + Pose2f kickPose = kick->getKickPose(ballPosition, target, kickWithLeft); + SelectablePose selectablePose = {playerPose, selectableTarget, kickPose, kickWithLeft}; + return {selectablePose, true}; + } + + Vector2f target; + Kick* kick; + bool kickWithLeft; +}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Factors.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Factors.h new file mode 100644 index 00000000..71c9de99 --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Factors.h @@ -0,0 +1,56 @@ +#pragma once + +#include "Selectables/KickPlan.h" +#include "../../../../../../Tools/Math/Geometry.h" +#include +#include +#include +#include + +class Factors +{ + +public: + Factors(const float widthFactor, + const float sidesHeatFactor, + const float goalsHeatFactor, + const float teammatesKickHeatFactor, + const float teammatesGoalKickHeatFactor, + const float opponentsKickHeatFactor, + const float opponentsGoalKickHeatFactor, + const float dontRuntIntoFactor, + const float blockDefensiveCone, + const float blockOpponentFactor, + const float timeFactor, + const float inaccuracyFactor) + : widthFactor(widthFactor), sidesHeatFactor(sidesHeatFactor), goalsHeatFactor(goalsHeatFactor), teammatesKickHeatFactor(teammatesKickHeatFactor), + teammatesGoalKickHeatFactor(teammatesGoalKickHeatFactor), opponentsKickHeatFactor(opponentsKickHeatFactor), opponentsGoalKickHeatFactor(opponentsGoalKickHeatFactor), + dontRuntIntoFactor(dontRuntIntoFactor), blockDefensiveCone(blockDefensiveCone), blockOpponentFactor(blockOpponentFactor), timeFactor(timeFactor), inaccuracyFactor(inaccuracyFactor) + { + ASSERT(MathUtils::isEqual(widthFactor, 0.f) || MathUtils::isEqual(sidesHeatFactor, 0.f) || MathUtils::isEqual(goalsHeatFactor, 0.f) + || MathUtils::isEqual(teammatesKickHeatFactor, 0.f) || MathUtils::isEqual(teammatesGoalKickHeatFactor, 0.f) || MathUtils::isEqual(opponentsKickHeatFactor, 0.f) + || MathUtils::isEqual(opponentsGoalKickHeatFactor, 0.f) || MathUtils::isEqual(dontRuntIntoFactor, 0.f) || MathUtils::isEqual(blockDefensiveCone, 0.f) + || MathUtils::isEqual(blockOpponentFactor, 0.f) || MathUtils::isEqual(timeFactor, 0.f) || MathUtils::isEqual(inaccuracyFactor, 0.f)); + } + + Factors& addCustomScoreFunction(std::function& customScoreFunction) + { + customScoreFunctions.push_back(customScoreFunction); + return *this; + } + + const float widthFactor; + const float sidesHeatFactor; + const float goalsHeatFactor; + const float teammatesKickHeatFactor; + const float teammatesGoalKickHeatFactor; + const float opponentsKickHeatFactor; + const float opponentsGoalKickHeatFactor; + const float dontRuntIntoFactor; + const float blockDefensiveCone; + const float blockOpponentFactor; + const float timeFactor; + const float inaccuracyFactor; + + std::vector> customScoreFunctions = {}; +}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Filterer/Filterer.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Filterer/Filterer.cpp new file mode 100644 index 00000000..bdf2a169 --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Filterer/Filterer.cpp @@ -0,0 +1,81 @@ +#include "Filterer.h" + +[[nodiscard]] std::vector> Filterer::getEfficientlyOrderedSelectableKickFilters() const +{ + std::vector> efficientlyOrderedFilters = {}; + return efficientlyOrderedFilters; +} + +[[nodiscard]] std::vector> Filterer::getEfficientlyOrderedSelectableDirectionFilters() const +{ + std::vector> efficientlyOrderedFilters = {}; + + if (betweenAnglesFilter) + { + efficientlyOrderedFilters.push_back(betweenAnglesFilter); + } + if (outsideKickRangeDirectionFilter) + { + efficientlyOrderedFilters.push_back(outsideKickRangeDirectionFilter); + } + if (toOwnGoalFilter) + { + efficientlyOrderedFilters.push_back(toOwnGoalFilter); + } + + return efficientlyOrderedFilters; +} + +[[nodiscard]] std::vector> Filterer::getEfficientlyOrderedSelectableTargetFilters() const +{ + std::vector> efficientlyOrderedFilters = {}; + + if (tooFarBackFilter) + { + efficientlyOrderedFilters.push_back(tooFarBackFilter); + } + if (leftFilter) + { + efficientlyOrderedFilters.push_back(leftFilter); + } + if (rightFilter) + { + efficientlyOrderedFilters.push_back(rightFilter); + } + if (outsideKickRangeTargetFilter) + { + efficientlyOrderedFilters.push_back(outsideKickRangeTargetFilter); + } + if (tooHighRotationToKickFilter) + { + efficientlyOrderedFilters.push_back(tooHighRotationToKickFilter); + } + if (leftOfLineFilter) + { + efficientlyOrderedFilters.push_back(leftOfLineFilter); + } + if (closeToFieldLineFilter) + { + efficientlyOrderedFilters.push_back(closeToFieldLineFilter); + } + if (inFrontOfOwnGoalFilter) + { + efficientlyOrderedFilters.push_back(inFrontOfOwnGoalFilter); + } + + return efficientlyOrderedFilters; +} + +[[nodiscard]] std::vector> Filterer::getEfficientlyOrderedSelectablePoseFilters() const +{ + std::vector> efficientlyOrderedFilters = {}; + if (poseBlockedByRobotFilter) + { + efficientlyOrderedFilters.push_back(poseBlockedByRobotFilter); + } + if (poseBlockedByGoalPostFilter) + { + efficientlyOrderedFilters.push_back(poseBlockedByGoalPostFilter); + } + return efficientlyOrderedFilters; +} diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Filterer/Filterer.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Filterer/Filterer.h new file mode 100644 index 00000000..78fe7b93 --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Filterer/Filterer.h @@ -0,0 +1,75 @@ +#pragma once + +#include "Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Ranges/KickRange.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Selectables/KickPlan.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Selectables/SelectableDirection.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Selectables/SelectablePose.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Selectables/SelectableTarget.h" +#include "Representations/Configuration/FieldDimensions.h" +#include "Representations/Modeling/KickWheel.h" +#include "Representations/Modeling/RobotMap.h" +#include +#include +#include +#include + +class Filterer +{ + +public: + [[nodiscard]] std::vector> getEfficientlyOrderedSelectableKickFilters() const; + [[nodiscard]] std::vector> getEfficientlyOrderedSelectableDirectionFilters() const; + [[nodiscard]] std::vector> getEfficientlyOrderedSelectableTargetFilters() const; + [[nodiscard]] std::vector> getEfficientlyOrderedSelectablePoseFilters() const; + + [[nodiscard]] bool isFilterBlocked() const { return blockedFilter; } + + [[nodiscard]] bool isFilterOutside() const { return outsideFilter; } + + Filterer& filterBlocked() + { + blockedFilter = true; + return *this; + } + + Filterer& filterOutside() + { + outsideFilter = true; + return *this; + } + + Filterer& filterOutsideKickRange(const KickRange& kickRange); + Filterer& filterBetweenAngles(const Vector2f& ballPosition, Angle leftAngle, Angle rightAngle); + Filterer& filterToOwnGoal(const Vector2f& ballPosition, const FieldDimensions& theFieldDimensions); + + Filterer& filterTooHighRotationToKick(Angle currentRotation, Angle tooHighRotation); + Filterer& filterInFrontOfOwnGoal(const FieldDimensions& selectableTarget); + Filterer& filterTooFarBack(float minX); + Filterer& filterLeft(float maxY); + Filterer& filterRight(float minY); + Filterer& filterLeftOfLine(const Vector2f& linePoint1, const Vector2f& linePoint2); + Filterer& filterCloseToFieldLine(const FieldDimensions& theFieldDimensions); + + Filterer& filterKickPoseBlockedByRobots(const RobotMap& theRobotMap); + Filterer& filterKickPoseBlockedByGoalPost(const FieldDimensions& theFieldDimensions); + +private: + bool blockedFilter = false; + bool outsideFilter = false; + + std::function outsideKickRangeDirectionFilter; + std::function betweenAnglesFilter; + std::function toOwnGoalFilter; + + std::function outsideKickRangeTargetFilter; + std::function tooHighRotationToKickFilter; + std::function inFrontOfOwnGoalFilter; + std::function tooFarBackFilter; + std::function leftFilter; + std::function rightFilter; + std::function leftOfLineFilter; + std::function closeToFieldLineFilter; + + std::function poseBlockedByRobotFilter; + std::function poseBlockedByGoalPostFilter; +}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Filterer/Filterer_Direction.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Filterer/Filterer_Direction.cpp new file mode 100644 index 00000000..6206658e --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Filterer/Filterer_Direction.cpp @@ -0,0 +1,56 @@ +#include "Filterer.h" + +#include "Tools/Math/Geometry.h" + +Filterer& Filterer::filterOutsideKickRange(const KickRange& kickRange) // todo cant copy +{ + outsideKickRangeDirectionFilter = [&kickRange](const SelectableDirection& selectableDirection) + { + return !kickRange.istValidDirection(selectableDirection.angle); + }; + outsideKickRangeTargetFilter = [&kickRange](const SelectableTarget& selectableTarget) + { + return !kickRange.istValidTarget(selectableTarget.target); + }; + return *this; +} + +Filterer& Filterer::filterBetweenAngles(const Vector2f& ballPosition, const Angle leftAngle, const Angle rightAngle) +{ + ASSERT(leftAngle > -181_deg); + ASSERT(leftAngle < 181_deg); + ASSERT(rightAngle > -181_deg); + ASSERT(rightAngle < 181_deg); + ASSERT(MathUtils::getAngleSmallestDiff(leftAngle, rightAngle) < 181_deg); + betweenAnglesFilter = [ballPosition, leftAngle, rightAngle](const SelectableDirection& selectableDirection) + { + return MathUtils::isBetweenAngles(selectableDirection.angle, leftAngle, rightAngle); + }; + return *this; +} + +Filterer& Filterer::filterToOwnGoal(const Vector2f& ballPosition, const FieldDimensions& theFieldDimensions) +{ + const Vector2f leftGoalPostPosition = {theFieldDimensions.xPosOwnGroundline, theFieldDimensions.yPosLeftGoal + 500.f}; + const Vector2f rightGoalPostPosition = {theFieldDimensions.xPosOwnGroundline, theFieldDimensions.yPosRightGoal - 500.f}; + + const Angle leftGoalPostAngle = (leftGoalPostPosition - ballPosition).angle(); + const Angle rightGoalPostAngle = (rightGoalPostPosition - ballPosition).angle(); + + ASSERT(leftGoalPostAngle > -181_deg); + ASSERT(leftGoalPostAngle < 181_deg); + ASSERT(rightGoalPostAngle > -181_deg); + ASSERT(rightGoalPostAngle < 181_deg); + ASSERT(MathUtils::getAngleSmallestDiff(leftGoalPostAngle, rightGoalPostAngle) < 181_deg); + + toOwnGoalFilter = [leftGoalPostAngle, rightGoalPostAngle](const SelectableDirection& selectableDirection) + { + if (std::abs(leftGoalPostAngle) < 90_deg) + { + ASSERT(std::abs(rightGoalPostAngle) < 90_deg); + return false; // is behind goal line + } + return MathUtils::isBetweenAngles(selectableDirection.angle, rightGoalPostAngle, leftGoalPostAngle); + }; + return *this; +} diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Filterer/Filterer_Kick.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Filterer/Filterer_Kick.cpp new file mode 100644 index 00000000..e69de29b diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Filterer/Filterer_Pose.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Filterer/Filterer_Pose.cpp new file mode 100644 index 00000000..a105ecaa --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Filterer/Filterer_Pose.cpp @@ -0,0 +1,58 @@ +#include "Tools/Math/Geometry.h" +#include "Filterer.h" + +Filterer& Filterer::filterKickPoseBlockedByRobots(const RobotMap& theRobotMap) +{ + poseBlockedByRobotFilter = [&theRobotMap](SelectablePose& selectablePose) + { + const float ROBOT_RADIUS = 200.f; // TODO Constant + const float adjustedRobotRadius = ROBOT_RADIUS * 5 / 8; // Should be small to avoid accidental not kicking + const float minRobotDistance = 2 * adjustedRobotRadius; + for (const auto& robot : theRobotMap.robots) + { + const float distance = Geometry::distance(robot.pose.translation, selectablePose.pose.translation); + if (distance < minRobotDistance) + { + return true; + } + } + return false; + }; + return *this; +} + +Filterer& Filterer::filterKickPoseBlockedByGoalPost(const FieldDimensions& theFieldDimensions) +{ + poseBlockedByRobotFilter = [&theFieldDimensions](SelectablePose& selectablePose) + { + const float robotRadius = 150.f; + const float offset = theFieldDimensions.goalPostRadius + robotRadius; + const float fieldMaxX = theFieldDimensions.xPosOpponentGroundline - robotRadius; + const float leftGoalPost_left = theFieldDimensions.yPosLeftGoal + offset; + const float leftGoalPost_right = theFieldDimensions.yPosLeftGoal - offset; + const float rightGoalPost_left = theFieldDimensions.yPosRightGoal + offset; + const float rightGoalPost_right = theFieldDimensions.yPosRightGoal - offset; + + const Vector2f kickPosition = selectablePose.pose.translation; + const float x = kickPosition.x(); + if (-fieldMaxX < x && x < fieldMaxX) + { + return false; // Is in field in x direction + } + const float y = kickPosition.y(); + if (y > leftGoalPost_left) + { + return false; + } + if (leftGoalPost_right > y && y > rightGoalPost_left) + { + return false; + } + if (rightGoalPost_right > y) + { + return false; + } + return true; + }; + return *this; +} diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Filterer/Filterer_Target.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Filterer/Filterer_Target.cpp new file mode 100644 index 00000000..14527103 --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Filterer/Filterer_Target.cpp @@ -0,0 +1,180 @@ +#include "Tools/Math/Geometry.h" +#include "Filterer.h" +#include +#include + +Filterer& Filterer::filterTooHighRotationToKick(const Angle currentRotation, const Angle tooHighRotation) +{ + tooHighRotationToKickFilter = [currentRotation, tooHighRotation](const SelectableTarget& selectableTarget) + { + const Angle rotationDiff = MathUtils::getLargerMinusSmallerAngleDiff(selectableTarget.selectableKick.kick->getOptAngle(), currentRotation); + return rotationDiff > tooHighRotation; + }; + return *this; +} + +Filterer& Filterer::filterInFrontOfOwnGoal(const FieldDimensions& theFieldDimensions) +{ + inFrontOfOwnGoalFilter = [&theFieldDimensions](const SelectableTarget& selectableTarget) + { + const float x = selectableTarget.target.x(); + const float y = selectableTarget.target.y(); + + if (x > (theFieldDimensions.xPosOwnGroundline + theFieldDimensions.xPosOwnGoalArea) / 2) + { + return false; + } + if (y > theFieldDimensions.yPosLeftGoal + theFieldDimensions.goalPostRadius) + { + return false; + } + if (theFieldDimensions.yPosRightGoal - theFieldDimensions.goalPostRadius > y) + { + return false; + } + return true; + }; + return *this; +} + +Filterer& Filterer::filterTooFarBack(float minX) +{ + tooFarBackFilter = [minX](const SelectableTarget& selectableTarget) + { + return selectableTarget.target.x() < minX; + }; + return *this; +} + +Filterer& Filterer::filterLeft(float maxY) +{ + leftFilter = [maxY](const SelectableTarget& selectableTarget) + { + return selectableTarget.target.y() > maxY; + }; + return *this; +} + +Filterer& Filterer::filterRight(float minY) +{ + rightFilter = [minY](SelectableTarget& selectableTarget) + { + return selectableTarget.target.y() < minY; + }; + return *this; +} + +Filterer& Filterer::filterLeftOfLine(const Vector2f& linePoint1, const Vector2f& linePoint2) +{ + leftOfLineFilter = [linePoint1, linePoint2](const SelectableTarget& selectableTarget) + { + return Geometry::isPointLeftOfLine(selectableTarget.target, linePoint1, linePoint2); + }; + return *this; +} + +Filterer& Filterer::filterCloseToFieldLine(const FieldDimensions& theFieldDimensions) +{ + closeToFieldLineFilter = [&theFieldDimensions](const SelectableTarget& selectableTarget) + { + const float MIN_DISTANCE_TO_FIELD_LINE = 400.f; + const Angle MIN_UNPROBLEMATIC_ANGLE = 5_deg; + + const bool leftCloseToFieldLine = selectableTarget.target.y() > theFieldDimensions.yPosLeftSideline - MIN_DISTANCE_TO_FIELD_LINE; + const bool rightCloseToFieldLine = theFieldDimensions.yPosRightSideline + MIN_DISTANCE_TO_FIELD_LINE > selectableTarget.target.y(); + const Angle kickAngle = (selectableTarget.target - selectableTarget.ballPosition).angle(); + if (leftCloseToFieldLine && (kickAngle < -180_deg + MIN_UNPROBLEMATIC_ANGLE || -MIN_UNPROBLEMATIC_ANGLE < kickAngle)) + { + return true; + } + if (rightCloseToFieldLine && (kickAngle > 180_deg - MIN_UNPROBLEMATIC_ANGLE || MIN_UNPROBLEMATIC_ANGLE > kickAngle)) + { + return true; + } + return false; + }; + return *this; + + /* + * TODO Also filter close to front and back field lines + * + closeToFieldLineFilter = [&theFieldDimensions](const SelectableTarget& selectableTarget) + { + const float MIN_DISTANCE_TO_FIELD_LINE = 250.f; + const Angle MIN_UNPROBLEMATIC_ANGLE = 5_deg; + const Angle MIN_GOAL_POST_TO_BALL_ANGLE = 100_deg; + + const Angle shotAngle = (selectableTarget.target - selectableTarget.ballPosition).angle(); + + const bool leftCloseToFieldLine = selectableTarget.target.y() > theFieldDimensions.yPosLeftSideline - MIN_DISTANCE_TO_FIELD_LINE; + const bool shotAlongLeftFieldLine = shotAngle < -180_deg + MIN_UNPROBLEMATIC_ANGLE || -MIN_UNPROBLEMATIC_ANGLE < shotAngle; + if (leftCloseToFieldLine && shotAlongLeftFieldLine) + { + return true; + } + + const bool rightCloseToFieldLine = theFieldDimensions.yPosRightSideline + MIN_DISTANCE_TO_FIELD_LINE > selectableTarget.target.y(); + const bool shotAlongRightFieldLine = rightCloseToFieldLine && (shotAngle > 180_deg - MIN_UNPROBLEMATIC_ANGLE || MIN_UNPROBLEMATIC_ANGLE > shotAngle); + if (rightCloseToFieldLine && shotAlongRightFieldLine) + { + return true; + } + + const bool opponentSide = selectableTarget.target.x() > 0.f; + const bool leftSide = selectableTarget.target.y() > 0.f; + + const Vector2f goalPost = opponentSide + ? (leftSide ? FieldUtils::getOpponentGoalLeftPost(theFieldDimensions) : FieldUtils::getOpponentGoalRightPost(theFieldDimensions)) + : (leftSide ? FieldUtils::getOwnGoalLeftPost(theFieldDimensions) : FieldUtils::getOwnGoalRightPost(theFieldDimensions)); + const Angle goalPostToBallAngle = (selectableTarget.ballPosition - goalPost).angle(); + + if (opponentSide) + { + bool opponentSideCloseToFieldLine; + if (leftSide) + { + opponentSideCloseToFieldLine = goalPostToBallAngle > 0_deg && goalPostToBallAngle < MIN_GOAL_POST_TO_BALL_ANGLE; + } + else + { + opponentSideCloseToFieldLine = goalPostToBallAngle < 0_deg && goalPostToBallAngle > -MIN_GOAL_POST_TO_BALL_ANGLE; + } + } + else + { + bool ownSideCloseToFieldLine; + if (leftSide) + { + ownSideCloseToFieldLine = goalPostToBallAngle < 180_deg && goalPostToBallAngle > 180_deg - MIN_GOAL_POST_TO_BALL_ANGLE; + } + else + { + ownSideCloseToFieldLine = goalPostToBallAngle > -180_deg && goalPostToBallAngle < -MIN_GOAL_POST_TO_BALL_ANGLE; + } + } + + + + + + + if (selectableTarget.target.y() > 0.f) + { + const Angle leftGoalPostToBallAngle = (selectableTarget.ballPosition - goalPost).angle(); + const bool leftGoalCloseToFieldLine = leftGoalPostToBallAngle > 0_deg && leftGoalPostToBallAngle < MIN_GOAL_POST_TO_BALL_ANGLE; + } + else + { + const Vector2f rightGoalPost = selectableTarget.target.x() < 0.f ? FieldUtils::getOwnGoalRightPost(theFieldDimensions) : FieldUtils::getOpponentGoalRightPost(theFieldDimensions); + const Angle rightGoalPostToBallAngle = (selectableTarget.ballPosition - rightGoalPost).angle(); + if (rightGoalPostToBallAngle < 0_deg && rightGoalPostToBallAngle > -MIN_GOAL_POST_TO_BALL_ANGLE) + { + return true; + } + } + + return false; + }; + return *this; + */ +} diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/KickManager.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/KickManager.cpp new file mode 100644 index 00000000..bb5b4222 --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/KickManager.cpp @@ -0,0 +1,71 @@ +#include "KickManager.h" + +#include "Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Constants.h" +#include "Modules/BehaviorControl/TacticControl/KicksProvider/Kick.h" +#include "Representations/MotionControl/WalkingEngineParams.h" +#include "Tools/Debugging/Annotation.h" +#include "Tools/Math/Eigen.h" + +bool KickManager::isActive() const +{ + return currentKick.kick != nullptr; +} + +std::optional KickManager::getCurrentKick(const Vector2f& ballPosition) +{ + ASSERT(std::isnormal(ballPosition.x()) || ballPosition.x() == 0.0f); + ASSERT(std::isnormal(ballPosition.y()) || ballPosition.y() == 0.0f); + + if (!isActive()) + { + return {}; + } + + const float STOP_KICK_IF_BALL_MOVED_DISTANCE = 200.f; + + const Vector2f ballMoved = (ballPosition - currentKickBallPosition); + if (ballMoved.norm() > STOP_KICK_IF_BALL_MOVED_DISTANCE) + { + ANNOTATION("KickManager", "BallMoved! Stop " << currentKick.kick->getName()); + stop(); + return {}; + } + + return currentKick; +} + +void KickManager::stop() +{ + if (isActive()) + { + //ANNOTATION("KickManager", "Stopped " << currentKick->getName()); + currentKick = {}; + currentKickTime = 0; + currentKickBallPosition = {}; + } +} + +void KickManager::kickTo(PositioningAndKickSymbols& pakSymbols, const KickPlan& kickPlan, const FrameInfo& theFrameInfo) +{ + const unsigned REFRESH_CURRENT_KICK_AFTER_DURATION = 2000; // milliseconds + + const SelectablePose& selectablePose = kickPlan.selectablePose; + const Vector2f& target = selectablePose.selectableTarget.target; + Kick* kick = selectablePose.selectableTarget.selectableKick.kick; + + // Debug Drawings and Annotations + CIRCLE(DRAW_EXECUTABLE_KICK_TARGET_AREA, target.x(), target.y(), kick->getInaccuracy(), 12, Drawings::solidPen, ColorRGBA::blue, Drawings::noBrush, ColorRGBA::blue); + pakSymbols.log_kickName = kick->getName(); + + // Set values + kick->perform(pakSymbols, selectablePose.pose, kick->kickWithLeftToMirror(selectablePose.kickWithLeft), target, + true); // TODO remove variable start- + + // Save new values + currentKick = {target, kick, selectablePose.kickWithLeft}; + if (!isActive() || theFrameInfo.time - currentKickTime > REFRESH_CURRENT_KICK_AFTER_DURATION) + { + currentKickTime = theFrameInfo.time; + currentKickBallPosition = selectablePose.selectableTarget.ballPosition; + } +} diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/KickManager.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/KickManager.h new file mode 100644 index 00000000..69a3116b --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/KickManager.h @@ -0,0 +1,37 @@ +#pragma once + +#include "Modules/BehaviorControl/TacticControl/KicksProvider/Kick.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/CurrentKick.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Selectables/KickPlan.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Logs/BehaviorLogger.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Utils/KickUtils.h" +#include "Representations/BehaviorControl/RoleSymbols/PositioningAndKickSymbols.h" +#include "Representations/Infrastructure/FrameInfo.h" +#include "Representations/Modeling/RobotPose.h" +#include "Representations/MotionControl/WalkingEngineParams.h" +#include "Tools/Math/Eigen.h" +#include "optional" +#include "stdexcept" + +/** + * 1. Remembers last kick decision + * 2. Provides methods for complicated kick operations + */ +class KickManager +{ + +public: + KickManager() = default; + ~KickManager() = default; + + [[nodiscard]] bool isActive() const; + void stop(); + std::optional getCurrentKick(const Vector2f& ballPosition); + void kickTo(PositioningAndKickSymbols& pakSymbols, const KickPlan& kickPlan, const FrameInfo& theFrameInfo); + +private: + // Save current Kick to decide if hysteresis should be applied + CurrentKick currentKick = {}; + unsigned currentKickTime = 0; + Vector2f currentKickBallPosition = {}; +}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Ranges/Cone.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Ranges/Cone.h new file mode 100644 index 00000000..4af43ee6 --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Ranges/Cone.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Utils/MathUtils.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Logs/KickDrawings.h" + +STREAMABLE(Cone, + + Cone(const Angle& left, const Angle& right) + { + this->left = left; + this->right = right; + }; + + Cone() + { + this->left = 180_deg; + this->right = -180_deg; + }; + + [[nodiscard]] bool istInside(const Angle& angle) const + { + return MathUtils::isBetweenAngles(angle, left, right); + }; + + void draw(const Vector2f& position, const float length) const + { + const Vector2f leftPosition = position + length * MathUtils::angleToVector(left); + const Vector2f rightPosition = position + length * MathUtils::angleToVector(right); + LINE(DRAW_KICK_RANGE_NAME, position.x(), position.y(), leftPosition.x(), leftPosition.y(), 20, Drawings::solidPen, ColorRGBA::blue); + LINE(DRAW_KICK_RANGE_NAME, position.x(), position.y(), rightPosition.x(), rightPosition.y(), 20, Drawings::solidPen, ColorRGBA::blue); + }, + + (Angle)(0) left, + (Angle)(0) right +); \ No newline at end of file diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Ranges/KickCone.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Ranges/KickCone.h new file mode 100644 index 00000000..2d03fbaa --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Ranges/KickCone.h @@ -0,0 +1,31 @@ +#pragma once + +#include "KickRange.h" +#include "Modules/BehaviorControl/TacticControl/KicksProvider/KickWithLeftUtils.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Enums/DistanceRequirement.h" +#include +#include + +class KickCone : public KickRange +{ + +public: + KickCone(const Vector2f& ballPosition, Angle left, Angle right) : KickRange(ballPosition), left(left), right(right) { draw(); } + + explicit KickCone(const Vector2f& ballPosition) : KickRange(ballPosition), left(180_deg), right(-180_deg) {} + + [[nodiscard]] bool istValidDirection(const Angle& angle) const override { return MathUtils::isBetweenAngles(angle, left, right); } + + [[nodiscard]] bool istValidTarget(const Vector2f& target) const override { return true; } + + void draw() const override + { + const Vector2f leftPosition = ballPosition + MathUtils::angleToVector(left) * 1000; + const Vector2f rightPosition = ballPosition + MathUtils::angleToVector(right) * 1000; + KickRange::draw(ballPosition, leftPosition); + KickRange::draw(ballPosition, rightPosition); + } + + Angle left; + Angle right; +}; \ No newline at end of file diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Ranges/KickLine.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Ranges/KickLine.h new file mode 100644 index 00000000..a13677fe --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Ranges/KickLine.h @@ -0,0 +1,69 @@ +#pragma once + +#include "KickRange.h" +#include "Modules/BehaviorControl/TacticControl/KicksProvider/KickWithLeftUtils.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Enums/DistanceRequirement.h" +#include +#include + +class KickLine : public KickRange +{ + +public: + KickLine(const Vector2f& ballPosition, const Vector2f& point1, const Vector2f& point2, const DistanceRequirement distanceRequirement, const float additionalDistance) + : KickRange(ballPosition), point1(point1), point2(point2), distanceRequirement(distanceRequirement), additionalDistance(additionalDistance) + { + this->point1 = point1; + this->point2 = point2; + + draw(); + } + + [[nodiscard]] bool istValidDirection(const Angle& angle) const override + { + const Angle point1Angle = (point1 - ballPosition).angle(); + const Angle point2Angle = (point2 - ballPosition).angle(); + const auto [leftAngle, rightAngle] = MathUtils::getLeftAndRightAngle(point1Angle, point2Angle); + return MathUtils::isBetweenAngles(angle, leftAngle, rightAngle); + } + + /** + * assumes the direction is valid + */ + [[nodiscard]] bool istValidTarget(const Vector2f& target) const override + { + ASSERT(istValidDirection((target - ballPosition).angle())); + const Geometry::Line line1 = {point1, point2 - point1}; + const Geometry::Line line2 = {ballPosition, target - ballPosition}; + Vector2f intersection; + VERIFY(Geometry::getIntersectionOfLines(line1, line2, intersection)); // otherwise should get filtered out with the test for valid direction + + const float targetDistance = Geometry::distance(ballPosition, target); + const float intersectionDistance = Geometry::distance(ballPosition, intersection); + + switch (distanceRequirement) + { + case mayShorterOrFurther: + return true; + case onPoint: + return intersectionDistance - additionalDistance <= targetDistance && targetDistance <= intersectionDistance + additionalDistance; + case mustFurther: + return targetDistance >= intersectionDistance + additionalDistance; + case mustShorter: + return targetDistance <= intersectionDistance - additionalDistance; + default: + throw std::logic_error(""); + } + } + + void draw() const override + { + KickRange::draw(ballPosition, point1); + KickRange::draw(ballPosition, point2); + } + + Vector2f point1; + Vector2f point2; + DistanceRequirement distanceRequirement; + float additionalDistance; // depending on the distanceRequirement a distance that is allowed or required on one or both directions +}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Ranges/KickRange.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Ranges/KickRange.h new file mode 100644 index 00000000..15b33a83 --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Ranges/KickRange.h @@ -0,0 +1,38 @@ +#pragma once + +#include "Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Enums/DistanceRequirement.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Selectables/KickPlan.h" +#include "Representations/BehaviorControl/TacticSymbols.h" +#include "Tools/Math/Eigen.h" +#include +#include + +class KickPlan; +class KickRange +{ + +public: + explicit KickRange(const Vector2f& ballPosition) : ballPosition(ballPosition) {} + + [[nodiscard]] virtual bool istValidDirection(const Angle& angle) const = 0; + + [[nodiscard]] virtual bool istValidTarget(const Vector2f& target) const = 0; + + virtual void draw() const = 0; + + static void draw(const Vector2f& ballPosition, const Geometry::Line& line, const int minAhead, const int maxAhead) + { + const Vector2f& target1 = line.base + line.direction * minAhead; + const Vector2f& target2 = line.base + line.direction * maxAhead; + draw(ballPosition, target1); + draw(ballPosition, target2); + } + + static void draw(const Vector2f& ballPosition, const Vector2f& target) + { + LINE(DRAW_KICK_RANGE_NAME, ballPosition.x(), ballPosition.y(), target.x(), target.y(), 20, Drawings::solidPen, ColorRGBA::blue); + } + +protected: + const Vector2f ballPosition; +}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Selectables/KickPlan.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Selectables/KickPlan.h new file mode 100644 index 00000000..85265cd4 --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Selectables/KickPlan.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include "Tools/Math/Eigen.h" +#include "SelectablePose.h" + +class KickPlan +{ + +public: + KickPlan(const SelectablePose& selectablePose, const bool hysteresis) : selectablePose(std::move(selectablePose)), hysteresis(hysteresis) {} + + void calculateScore() { score = selectablePose.score + selectablePose.selectableTarget.score + selectablePose.selectableTarget.selectableKick.score; } + + SelectablePose selectablePose; + float score = 0.f; + bool hysteresis; +}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Selectables/SelectableDirection.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Selectables/SelectableDirection.h new file mode 100644 index 00000000..3e640ae6 --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Selectables/SelectableDirection.h @@ -0,0 +1,33 @@ +#pragma once + +#include "Modules/BehaviorControl/TacticControl/KicksProvider/Kick.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Utils/MathUtils.h" +#include "Tools/Math/Geometry.h" +#include +#include +#include +#include + +class SelectableDirection +{ + +public: + SelectableDirection(const Vector2f& ballPosition, const Angle angle, const float distance, const bool distanceBlocked, const bool distanceOutside) + : ballPosition(ballPosition), angle(angle), distance(distance), distanceBlocked(distanceBlocked), distanceOutside(distanceOutside) + { + ASSERT(!(distanceBlocked && distanceOutside)); + direction = MathUtils::angleToVector(angle); + } + + Vector2f ballPosition; + + Angle angle; + Vector2f direction; + + float distance; + + bool distanceBlocked; + bool distanceOutside; + + float score; +}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Selectables/SelectableKick.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Selectables/SelectableKick.h new file mode 100644 index 00000000..3d3e2b7d --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Selectables/SelectableKick.h @@ -0,0 +1,17 @@ +#pragma once + +#include "Modules/BehaviorControl/TacticControl/KicksProvider/Kick.h" +#include +#include +#include +#include + +class SelectableKick +{ + +public: + SelectableKick(Kick* kick) : kick(kick) {} + + Kick* kick; + float score; +}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Selectables/SelectablePose.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Selectables/SelectablePose.h new file mode 100644 index 00000000..3e10f7c3 --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Selectables/SelectablePose.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include +#include +#include +#include "SelectableTarget.h" + +class SelectablePose +{ + +public: + SelectablePose(const Pose2f& playerPose, const SelectableTarget& selectableTarget, const Pose2f& pose, const bool kickWithLeft) + : playerPose(playerPose), selectableTarget(std::move(selectableTarget)), pose(std::move(pose)), kickWithLeft(kickWithLeft) + { + } + + Pose2f playerPose; + SelectableTarget selectableTarget; + Pose2f pose; + bool kickWithLeft; + float score; +}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Selectables/SelectableTarget.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Selectables/SelectableTarget.h new file mode 100644 index 00000000..18cac80c --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Selectables/SelectableTarget.h @@ -0,0 +1,23 @@ +#pragma once + +#include "SelectableKick.h" +#include "Tools/Math/Geometry.h" +#include +#include +#include +#include + +class SelectableTarget +{ + +public: + SelectableTarget(const SelectableKick selectableKick, const Vector2f& ballPosition, const Vector2f& target) + : selectableKick(selectableKick), ballPosition(ballPosition), target(target) + { + } + + SelectableKick selectableKick; + Vector2f ballPosition; + Vector2f target; + float score; +}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/LeftWingProvider.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/LeftWingProvider.cpp new file mode 100644 index 00000000..a78b9cd1 --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/LeftWingProvider.cpp @@ -0,0 +1,105 @@ +#include +#include "LeftWingProvider.h" +#include "Tools/Debugging/DebugDrawings3D.h" +#include "Utils/PositionUtils.h" +#include "Utils/ThresholdUtils.h" + +void LeftWingProvider::update(LeftWing& role) +{ + role.stopAtTarget = true; + + decide(role, theBallSymbols, theFieldDimensions, theGameInfo, theGameSymbols); +} + +void LeftWingProvider::stateReady_kickOff_own(LeftWing& role, const Vector2f& ballPosition) +{ + ThresholdUtils::setThreshholdsHeigh(role); + const float x = -theFieldDimensions.xPosOpponentPenaltyArea / 5; + const float y = theFieldDimensions.yPosLeftPenaltyArea; + PositionUtils::setPosition(role, x, y); + PositionUtils::turnToPosition(role, {700.f, 0.f}); +} + +void LeftWingProvider::stateReady_kickOff_opponent(LeftWing& role, const Vector2f& ballPosition) +{ + ThresholdUtils::setThreshholdsHeigh(role); + const float x = -theFieldDimensions.xPosOpponentPenaltyArea / 2; + const float y = theFieldDimensions.yPosLeftPenaltyArea; + PositionUtils::setPosition(role, x, y); + PositionUtils::turnTowardsBall(role, theBallSymbols); +} + +float LeftWingProvider::goalKick_own(LeftWing& role, bool left) +{ + regularPlay(role); + return 0.f; +} + +float LeftWingProvider::goalKick_opponent(LeftWing& role, bool left) +{ + regularPlay(role); + return 0; +} + +float LeftWingProvider::pushingFreeKick_own(LeftWing& role) +{ + regularPlay(role); + return 0; +} + +float LeftWingProvider::pushingFreeKick_opponent(LeftWing& role) +{ + regularPlay(role); + return 0; +} + +float LeftWingProvider::cornerKick_own(LeftWing& role, const Vector2f& cornerKickPosition, bool left) +{ + regularPlay(role); + return 0; +} + +float LeftWingProvider::cornerKick_opponent(LeftWing& role, const Vector2f& cornerKickPosition, bool left) +{ + regularPlay(role); + return 0; +} + +float LeftWingProvider::kickIn_own(LeftWing& role, bool left) +{ + regularPlay(role); + return 0; +} + +float LeftWingProvider::kickIn_opponent(LeftWing& role, bool left) +{ + regularPlay(role); + return 0; +} + +float LeftWingProvider::stateReady_penaltyKick_own(LeftWing& role) +{ + regularPlay(role); + return 0; +} + +float LeftWingProvider::stateReady_penaltyKick_opponent(LeftWing& role) +{ + regularPlay(role); + return 0; +} + +void LeftWingProvider::regularPlay(LeftWing& role) +{ + ThresholdUtils::setThreshholdsExtremeHeigh(role, theTacticSymbols.activity); + + PositionUtils::turnTowardsBall(role, theBallSymbols); + + float x = theBallSymbols.ballPositionField.x() + 1000.f - 2000.f * (float)theTacticSymbols.defensiveBehavior; + x = MathUtils::clamp_f(x, theFieldDimensions.xPosOwnPenaltyArea * 2 / 3, theFieldDimensions.xPosOpponentPenaltyArea * 2 / 3); + const float yOffset = theFieldDimensions.yPosLeftSideline / 5; + const float y = theFieldDimensions.yPosLeftSideline - yOffset - yOffset * (float)theTacticSymbols.defensiveBehavior; + PositionUtils::setPosition(role, x, y); +} + +MAKE_MODULE(LeftWingProvider, behaviorControl) diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/LeftWingProvider.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/LeftWingProvider.h new file mode 100644 index 00000000..b174a644 --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/LeftWingProvider.h @@ -0,0 +1,76 @@ +#pragma once + +#include +#include "Tools/Module/Module.h" +#include "Modules/BehaviorControl/CABSL/BehaviorParameters.h" +#include "Representations/Configuration/FieldDimensions.h" +#include "Representations/BehaviorControl/BallSymbols.h" +#include "Representations/BehaviorControl/GameSymbols.h" +#include "Representations/BehaviorControl/GoalSymbols.h" +#include "Representations/BehaviorControl/RoleSelection.h" +#include "Representations/BehaviorControl/BallChaserDecision.h" +#include "Representations/BehaviorControl/BehaviorConfiguration.h" +#include "Representations/BehaviorControl/RoleSymbols/LeftWing.h" +#include "Representations/BehaviorControl/RoleSymbols/Receiver.h" +#include "Representations/BehaviorControl/RoleSymbols/Ballchaser.h" +#include "Representations/BehaviorControl/RoleSymbols/BallchaserKeeper.h" +#include "Representations/BehaviorControl/RoleSymbols/PositioningSymbols.h" +#include "Representations/BehaviorControl/TacticSymbols.h" +#include "Representations/Infrastructure/FrameInfo.h" +#include "Representations/Infrastructure/GameInfo.h" +#include "Representations/Infrastructure/RobotInfo.h" +#include "Representations/Infrastructure/TeamInfo.h" +#include "Representations/Infrastructure/TeammateData.h" +#include "Representations/BehaviorControl/BehaviorData.h" +#include "Representations/Modeling/DangerMap.h" +#include "Representations/Modeling/RobotMap.h" +#include "Representations/Modeling/RobotPose.h" +#include "Representations/MotionControl/WalkingEngineParams.h" +#include "Tools/Settings.h" +#include "RoleProvider.h" + +MODULE(LeftWingProvider, + REQUIRES(BallSymbols), + REQUIRES(BehaviorConfiguration), + REQUIRES(DangerMap), + REQUIRES(FrameInfo), + REQUIRES(FieldDimensions), + REQUIRES(GameInfo), + REQUIRES(GameSymbols), + REQUIRES(GoalSymbols), + REQUIRES(OwnTeamInfo), + REQUIRES(OpponentTeamInfo), + REQUIRES(RobotInfo), + REQUIRES(RobotMap), + REQUIRES(RobotPose), + REQUIRES(RobotPoseAfterPreview), + REQUIRES(RoleSelection), + REQUIRES(Receiver), + REQUIRES(TeammateData), + REQUIRES(TacticSymbols), + REQUIRES(WalkingEngineParams), + USES(PositioningSymbols), + PROVIDES(LeftWing) +); + +class LeftWingProvider : public LeftWingProviderBase, public RoleProvider +{ + +public: + void update(LeftWing& role) override; + +private: + void stateReady_kickOff_own(LeftWing& role, const Vector2f& ballPosition) override; + void stateReady_kickOff_opponent(LeftWing& role, const Vector2f& ballPosition) override; + float goalKick_own(LeftWing& role, bool left) override; + float goalKick_opponent(LeftWing& role, bool left) override; + float pushingFreeKick_own(LeftWing& role) override; + float pushingFreeKick_opponent(LeftWing& role) override; + float cornerKick_own(LeftWing& role, const Vector2f& cornerKickPosition, bool left) override; + float cornerKick_opponent(LeftWing& role, const Vector2f& cornerKickPosition, bool left) override; + float kickIn_own(LeftWing& role, bool left) override; + float kickIn_opponent(LeftWing& role, bool left) override; + float stateReady_penaltyKick_own(LeftWing& role) override; + float stateReady_penaltyKick_opponent(LeftWing& role) override; + void regularPlay(LeftWing& role) override; +}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Logs/KickDrawings.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Logs/KickDrawings.h index 5c6d333c..99e08520 100644 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Logs/KickDrawings.h +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Logs/KickDrawings.h @@ -1,9 +1,11 @@ #pragma once +#define DRAW_HEAT_MAP "module:HeatMapProvider" + #define DRAW_KICK_DANGER_NAME "behavior:BallchaserProvider:KickManager:Danger" #define DRAW_KICK_RANGE_NAME "behavior:BallchaserProvider:KickManager:KickRange" -#define DRAW_KICK_MANAGER_ACTIVATE_RANGE_NAME "behavior:KickManager:ActivateRange" #define DRAW_EXECUTABLE_KICKS_IN_GRID "behavior:KickManager:ExecutableKicks:InGrid" #define DRAW_EXECUTABLE_KICKS_FREELY "behavior:KickManager:ExecutableKicks:Freely" #define DRAW_EXECUTABLE_KICK_TARGET_AREA "behavior:KickManager:ExecutableKicks:TargetArea" -#define DRAW_KICK_MIN_WIDTH "behavior:KickManager:ExecutableKicks:Width" \ No newline at end of file +#define DRAW_KICK_MIN_WIDTH "behavior:KickManager:ExecutableKicks:Width" +#define DRAW_TEST "behavior:KickManager:ExecutableKicks:Test" \ No newline at end of file diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/GoalObjective.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/GoalObjective.cpp deleted file mode 100644 index 272acb82..00000000 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/GoalObjective.cpp +++ /dev/null @@ -1,232 +0,0 @@ -#include "GoalObjective.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -GoalObjective::GoalObjective(BallchaserProvider* role, BehaviorLogger& logger) : Objective("GoalObjective", role, logger), kickManager() -{ - //kicks.push_back(std::make_unique()); - kicks.push_back(std::make_unique()); - kicks.push_back(std::make_unique()); - kicks.push_back(std::make_unique()); - kicks.push_back(std::make_unique()); - kicks.push_back(std::make_unique()); - //kicks.push_back(std::make_unique()); -} - -bool GoalObjective::enterCondition() -{ - const Angle MAX_ANGLE = 74_deg; // TODO Constant also used in ExecutableKicks to filter - const bool leftAngleTooSharp = HysterUtils::compareAbsolutes(openingAngleTooSharp, MAX_ANGLE, -role->theGoalSymbols.leftAngleBallToOppGoalWC, 2_deg); - const bool rightAngleTooSharp = HysterUtils::compareAbsolutes(openingAngleTooSharp, MAX_ANGLE, role->theGoalSymbols.rightAngleBallToOppGoalWC, 2_deg); - openingAngleTooSharp = leftAngleTooSharp || rightAngleTooSharp; - if (openingAngleTooSharp) - { - logger.addFailedReason("openingAngleTooSharp"); - return false; - } - else - { - return true; - } -} - -bool GoalObjective::perform(Ballchaser& ballchaser) -{ - update(); - - if (goalDistance == VERY_FAR) - { - logger.addFailedReason("TooFar"); - return false; - } - if (goalDistance == FAR) // && role->danger != HIGH - { - return kickToGoalCenter(ballchaser); - } - return kickBestToGoal(ballchaser); -} - -void GoalObjective::update() -{ - kickManager.update(role->ballPosition, role->theRobotPoseAfterPreview, role->theWalkingEngineParams); - - const Vector2f leftGoalPostPosition = {role->theFieldDimensions.xPosOpponentGroundline, role->theFieldDimensions.yPosLeftGoal}; - const Vector2f rightGoalPostPosition = {role->theFieldDimensions.xPosOpponentGroundline, role->theFieldDimensions.yPosRightGoal}; - const Geometry::Line goalPostLine = {leftGoalPostPosition, (rightGoalPostPosition - leftGoalPostPosition).normalized()}; - const float ballToGoalDistance = Geometry::getDistanceToLine(goalPostLine, role->ballPosition); - - GoalDistance newGoalDistance; - - if (isVeryCloseToGoal(leftGoalPostPosition, rightGoalPostPosition, ballToGoalDistance)) - { - logger.addNote("VeryCloseToGoal"); - newGoalDistance = VERY_CLOSE; - } - else if (HysterUtils::comparePossibleNegatives(goalDistance == CLOSE, ballToGoalDistance, 1000.f, 200.f)) - { - logger.addNote("CloseToGoal"); - newGoalDistance = CLOSE; - } - else if (HysterUtils::comparePossibleNegatives(goalDistance == NORMAL, ballToGoalDistance, 2300.f, 200.f)) - { - logger.addNote("NormalToGoal"); - newGoalDistance = NORMAL; - } - else if (HysterUtils::comparePossibleNegatives(goalDistance == FAR, ballToGoalDistance, 4000.f, 200.f)) - { - logger.addNote("FarFromGoal"); - newGoalDistance = FAR; - } - else - { - logger.addNote("VeryFarFromGoal"); - newGoalDistance = VERY_FAR; - } - - goalDistance = newGoalDistance; - /* Might be a good Idea if the different distances are actually used - if (newGoalDistance != goalDistance) - { - goalDistance = newGoalDistance; - kickManager.stop(); - } - */ -} - -bool GoalObjective::isVeryCloseToGoal(const Vector2f& leftGoalPostPosition, const Vector2f& rightGoalPostPosition, const float ballToGoalDistance) -{ - const bool hysteresis = goalDistance == VERY_CLOSE; - if (!HysterUtils::comparePossibleNegatives(hysteresis, ballToGoalDistance, 200.f, 100.f)) - { - return false; - } - - if (!HysterUtils::comparePossibleNegatives(hysteresis, role->ballPosition.y(), leftGoalPostPosition.y(), 0.f, 50.f)) - { - return false; - } - - if (!HysterUtils::comparePossibleNegatives(hysteresis, rightGoalPostPosition.y(), role->ballPosition.y(), 0.f, 50.f)) - { - return false; - } - - return true; -} - -bool GoalObjective::kickToGoalCenter(Ballchaser& ballchaser) -{ - const Vector2f leftGoalPostPosition = {role->theFieldDimensions.xPosOpponentGroundline, role->theFieldDimensions.yPosLeftGoal - role->theFieldDimensions.goalPostRadius}; - const Vector2f rightGoalPostPosition = {role->theFieldDimensions.xPosOpponentGroundline, role->theFieldDimensions.yPosRightGoal + role->theFieldDimensions.goalPostRadius}; - Optimize optimize; - switch (role->danger) - { - case HIGH: - case MEDIUM: - case LOW: - optimize = forTime; - break; - case NONE: - case IMPOSSIBLE: - optimize = forPrecisionWithoutDanger; - break; - default: - throw std::logic_error(""); - } - bool suc = kickManager.kickInRangeCenter(ballchaser, - role->theRobotPoseAfterPreview.translation, - role->ballPosition, - leftGoalPostPosition, - rightGoalPostPosition, - 10, - DistanceRequirement::shouldFurther, - optimize, - 100.f, - 0.f, - KickUtils::unpack(kicks), - role->theFieldDimensions, - role->theRobotMap); - logger.addReason(suc, "KickInRangeCenter"); - return suc; -} - -bool GoalObjective::kickBestToGoal(Ballchaser& ballchaser) -{ - const Vector2f leftGoalPostPosition = {role->theFieldDimensions.xPosOpponentGroundline, role->theFieldDimensions.yPosLeftGoal - role->theFieldDimensions.goalPostRadius}; - const Vector2f rightGoalPostPosition = {role->theFieldDimensions.xPosOpponentGroundline, role->theFieldDimensions.yPosRightGoal + role->theFieldDimensions.goalPostRadius}; - - Vector2f leftTarget; - Vector2f rightTarget; - switch (goalDistance) - { - case VERY_CLOSE: - { - leftTarget = {leftGoalPostPosition.x() + 300.f, leftGoalPostPosition.y()}; - rightTarget = {rightGoalPostPosition.x() + 300.f, rightGoalPostPosition.y()}; - break; - } - case CLOSE: - case NORMAL: - case FAR: - case VERY_FAR: - { - leftTarget = leftGoalPostPosition; - rightTarget = rightGoalPostPosition; - break; - } - default: - throw std::logic_error(""); - } - - DistanceRequirement distanceRequirement = DistanceRequirement::mustFurther; - float timeFactor; - switch (role->danger) - { - case HIGH: - distanceRequirement = DistanceRequirement::mayShorterOrFurther; - timeFactor = -2.f; - break; - case MEDIUM: - timeFactor = -1.5f; - break; - case LOW: - timeFactor = -1.f; - break; - case NONE: - timeFactor = -0.5f; - break; - case IMPOSSIBLE: - timeFactor = 0.f; - break; - default: - throw std::logic_error(""); - } - - ExecutableKicks executableKicks = - kickManager - .getExecutableKicks( - role->theRobotPoseAfterPreview, {role->ballPosition, leftTarget, rightTarget, distanceRequirement}, 10, KickUtils::unpack(kicks), role->theFieldDimensions, role->theHeatMapCollection, role->theRobotMap) - .filterBlocked(role->theFieldDimensions, role->theRobotMap) - .reduceToBest(timeFactor, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, role->kickWithLeft, role->theFieldDimensions, role->theHeatMapCollection, role->theRobotMap); - - if (executableKicks.hasBest()) - { - ExecutableKick executableKick = executableKicks.getBest(); - kickManager.kickTo(ballchaser, role->theRobotPoseAfterPreview, executableKicks.ballPosition, executableKick.target, *executableKick.kick); - logger.addSuccessReason("BestGoalKick"); - return true; - } - else - { - logger.addFailedReason("BestGoalKick"); - return false; - } -} diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/MoveObjective.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/MoveObjective.cpp deleted file mode 100644 index 58af5612..00000000 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/MoveObjective.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include "MoveObjective.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "Modules/BehaviorControl/TacticControl/RoleProvider/Utils/HysterUtils.h" - -MoveObjective::MoveObjective(BallchaserProvider* role, BehaviorLogger& logger) : Objective("MoveObjective", role, logger) -{ - //kickManager.add(std::make_unique()); - //kicks.push_back(std::make_unique()); - kicks.push_back(std::make_unique()); - kicks.push_back(std::make_unique()); - kicks.push_back(std::make_unique()); - kicks.push_back(std::make_unique()); - kicks.push_back(std::make_unique()); - //kicks.push_back(std::make_unique()); -} - -bool MoveObjective::perform(Ballchaser& ballchaser) -{ - update(); - - float timeFactor; - switch (role->danger) - { - case HIGH: - OUTPUT_ERROR("Should not get reached! Start 1v1 instead!"); - case MEDIUM: - timeFactor = -1.f; - break; - case LOW: - timeFactor = -0.5f; - break; - case NONE: - timeFactor = -0.3f; - break; - case IMPOSSIBLE: - timeFactor = 0.f; - break; - default: - throw std::logic_error(""); - } - - ExecutableKicks executableKicks = - kickManager - .getExecutableKicks(role->theRobotPoseAfterPreview, role->ballPosition, KickUtils::unpack(kicks), role->theFieldDimensions, role->theHeatMapCollection, role->theRobotMap) - .filterTooFarBack(std::min(role->ballPosition.x() + 500.f, role->theFieldDimensions.xPosOpponentPenaltyArea)) - .filterOutside(role->theFieldDimensions) - .filterBlocked(role->theFieldDimensions, role->theRobotMap) - .reduceToBest(timeFactor, 0.f, 0.5f, -0.1f, 1.f, 0.f, 0.f, 1.f, 0.f, -0.2f, -1.f, role->kickWithLeft, role->theFieldDimensions, role->theHeatMapCollection, role->theRobotMap); - - bool suc = executableKicks.hasBest(); - if (suc) - { - kickManager.kickTo(ballchaser, executableKicks); - } - logger.addReason(suc, "KickBestWithFactors"); - return suc; -} - -void MoveObjective::update() -{ - kickManager.update(role->ballPosition, role->theRobotPoseAfterPreview, role->theWalkingEngineParams); -} diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/NoDeadlockObjective.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/NoDeadlockObjective.cpp deleted file mode 100644 index df33ecd0..00000000 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/NoDeadlockObjective.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "NoDeadlockObjective.h" - -#include -#include - -NoDeadlockObjective::NoDeadlockObjective(BallchaserProvider* role, BehaviorLogger& logger) : Objective("NoDeadlockObjective", role, logger) {} - -bool NoDeadlockObjective::perform(Ballchaser& ballchaser) -{ - const Vector2f defensivePosition = BallchaserUtils::getWaitPosition(role->ballPosition, role->theFieldDimensions); - PositionUtils::setPosition(ballchaser, defensivePosition); - return true; -} diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/NoDeadlockObjective.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/NoDeadlockObjective.h deleted file mode 100644 index bb341402..00000000 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/NoDeadlockObjective.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include "Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Objective.h" -#include "Modules/BehaviorControl/TacticControl/RoleProvider/BallchaserProvider.h" -#include "Representations/BehaviorControl/RoleSymbols/Ballchaser.h" - -class BallchaserProvider; -struct Ballchaser; -class NoDeadlockObjective : public Objective -{ - -public: - NoDeadlockObjective(BallchaserProvider* role, BehaviorLogger& logger); - bool perform(Ballchaser& ballchaser) override; -}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/OneVsOneObjective.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/OneVsOneObjective.cpp deleted file mode 100644 index 33736f3b..00000000 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/OneVsOneObjective.cpp +++ /dev/null @@ -1,202 +0,0 @@ -#include "OneVsOneObjective.h" - -#include -#include -#include -#include -#include -#include -#include - -OneVsOneObjective::OneVsOneObjective(BallchaserProvider* role, BehaviorLogger& logger) : Objective("OneVsOneObjective", role, logger) -{ - kicks.push_back(std::make_unique()); - kicks.push_back(std::make_unique()); - kicks.push_back(std::make_unique()); - kicks.push_back(std::make_unique()); - //kicks.push_back(std::make_unique()); -} - -bool OneVsOneObjective::enterCondition() -{ - if (role->danger == Danger::HIGH) - { - logger.addSuccessReason("HighDanger"); - return true; - } - else - { - logger.addFailedReason("NotHighDanger"); - return false; - } -} - -void OneVsOneObjective::preprocess() -{ - Objective::preprocess(); -} - -bool OneVsOneObjective::perform(Ballchaser& ballchaser) -{ - kickManager.update(role->ballPosition, role->theRobotPoseAfterPreview, role->theWalkingEngineParams); - - if (role->playAgainstDribbleTeam) - { - logger.addNote("JumpToDefendIfAgainstDribblingTeam"); - } - else - { - if (kickBallInCone(ballchaser, kickManager.isActive() ? 40_deg : 30_deg)) - { - logger.addSuccessReason("KickInSmallerCone"); - return true; - } - else - { - logger.addFailedReason("KickInSmallerCone"); - } - } - - updateTimer(); - - if (!startTimeSet) - { - defend(ballchaser); - logger.addNote("TimerNotStarted"); - logger.addSuccessReason("WalkToBall"); - return true; - } - - if (!isTimeout()) - { - defend(ballchaser); - logger.addSuccessReason("Defend"); - return true; - } - - if (kickBallInCone(ballchaser, kickManager.isActive() ? 60_deg : 50_deg)) - { - logger.addSuccessReason("KickInBiggerCone"); - return true; - } - else - { - defend(ballchaser); - logger.addFailedReason("KickInBiggerCone"); - logger.addSuccessReason("Defend"); - return true; - } -} - -void OneVsOneObjective::updateTimer() -{ - if (startTimeSet) - { - if (!isInBallArea(startTimeBallPosition)) - { - logger.addNote("ballMoved->stopTimer"); - startTimeSet = false; - return; - } - if (!isInBallArea(role->theRobotPoseAfterPreview.translation)) - { - logger.addNote("playerMoved->stopTimer"); - startTimeSet = false; - return; - } - logger.addNote("TimerRunning"); - startTimeBallPosition = role->ballPosition; - return; - } - logger.addNote("TimerNotRunning"); - if (isInBallArea(role->theRobotPoseAfterPreview.translation)) - { - logger.addNote("playerCloseToBall->startTimer"); - startTime = role->theFrameInfo.time; - startTimeSet = true; - startTimeBallPosition = role->ballPosition; - } -} - -bool OneVsOneObjective::isInBallArea(const Vector2f& position) -{ - Geometry::Circle closeToBallCircle = Geometry::Circle(role->ballPosition, 500.f); // TODO Constant - return Geometry::isPointInCircle(closeToBallCircle, position); -} - -bool OneVsOneObjective::isTimeout() -{ - const bool timeOut = role->theFrameInfo.getTimeSince(startTime) > 5000; - if (timeOut) - { - logger.addNote("Timeout"); - return true; - } - else - { - logger.addNote("NoTimeout"); - return false; - } -} - -bool OneVsOneObjective::kickBallInCone(Ballchaser& ballchaser, const Angle toPlayerMaxAngle) -{ - const Angle TO_OPPONENT_SIDE_MAX_ANGLE = 95_deg; - const Angle MIN_ANGLE_SIZE = 10_deg; - - playerIsLeftOfBall = HysterUtils::comparePossibleNegatives(playerIsLeftOfBall, role->ballPosition.y(), role->theRobotPoseAfterPreview.translation.y(), 200.f); // TODO Constant - - const Range range = Range(-TO_OPPONENT_SIDE_MAX_ANGLE, TO_OPPONENT_SIDE_MAX_ANGLE); - const Angle currentAngle = (role->ballPosition - role->theRobotPoseAfterPreview.translation).angle(); - - Angle angle1 = currentAngle + toPlayerMaxAngle; - angle1 = range.limit(angle1); - - Angle angle2 = currentAngle - toPlayerMaxAngle; - angle2 = range.limit(angle2); - angle2 = std::min(Angle(angle1 - MIN_ANGLE_SIZE), angle2); - - const KickRange kickRange = {role->ballPosition, angle1, angle2, 1000.f, DistanceRequirement::mayShorterOrFurther}; - - ExecutableKicks executableKicks = - kickManager - .getExecutableKicks(role->theRobotPoseAfterPreview, kickRange, 10, KickUtils::unpack(kicks), role->theFieldDimensions, role->theHeatMapCollection, role->theRobotMap) - .filterOutside(role->theFieldDimensions) - .filterBlocked(role->theFieldDimensions, role->theRobotMap) - .reduceToBest(-1.f, 0.f, 0.f, 0.f, 0.1f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, role->kickWithLeft, role->theFieldDimensions, role->theHeatMapCollection, role->theRobotMap); - - if (executableKicks.hasBest()) - { - kickManager.kickTo(ballchaser, executableKicks); - return true; - } - return false; -} - -bool OneVsOneObjective::justKick(Ballchaser& ballchaser) -{ - ExecutableKicks executableKicks = - kickManager - .getExecutableKicks(role->theRobotPoseAfterPreview, role->ballPosition, KickUtils::unpack(kicks), role->theFieldDimensions, role->theHeatMapCollection, role->theRobotMap) - .filterOutside(role->theFieldDimensions) - .reduceToBest(-1.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, role->kickWithLeft, role->theFieldDimensions, role->theHeatMapCollection, role->theRobotMap); - - const bool suc = executableKicks.hasBest(); - if (suc) - { - kickManager.kickTo(ballchaser, executableKicks); - } - return suc; -} - -void OneVsOneObjective::defend(Ballchaser& ballchaser) -{ - PositionUtils::setPosition(ballchaser, BallchaserUtils::getWaitPosition(role->ballPosition, role->theFieldDimensions)); - PositionUtils::turnToPosition(ballchaser, role->ballPosition); -} - -void OneVsOneObjective::postprocess() -{ - Objective::postprocess(); - startTimeSet = false; -} diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/OneVsOneObjective.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/OneVsOneObjective.h deleted file mode 100644 index ac30a4fc..00000000 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/OneVsOneObjective.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include "Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Objective.h" -#include "Modules/BehaviorControl/TacticControl/RoleProvider/BallchaserProvider.h" -#include "Representations/BehaviorControl/RoleSymbols/Ballchaser.h" - -class BallchaserProvider; -struct Ballchaser; -class OneVsOneObjective : public Objective -{ - -public: - OneVsOneObjective(BallchaserProvider* role, BehaviorLogger& logger); - bool enterCondition() override; - void preprocess() override; - bool perform(Ballchaser& ballchaser) override; - void postprocess() override; - -private: - KickManager kickManager; - std::vector> kicks; - - bool startTimeSet = false; - unsigned startTime = 0; - Vector2f startTimeBallPosition; - - bool playerIsLeftOfBall = false; - - bool kickBallInCone(Ballchaser& ballchaser, Angle toPlayerMaxAngle); - - bool isInBallArea(const Vector2f& ballPosition); - - bool isTimeout(); - - bool justKick(Ballchaser& ballchaser); - - void defend(Ballchaser& ballchaser); - - void updateTimer(); -}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/TestObjective.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/TestObjective.cpp deleted file mode 100644 index d63c3f6d..00000000 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/TestObjective.cpp +++ /dev/null @@ -1,69 +0,0 @@ -#include "TestObjective.h" - -#include -#include -#include - -TestObjective::TestObjective(BallchaserProvider* role, BehaviorLogger& logger) : Objective("TestObjective", role, logger) -{ - // kicks.push_back(std::make_unique()); - // kicks.push_back(std::make_unique()); - - // kicks.push_back(std::make_unique()); - // kicks.push_back(std::make_unique()); - kicks.push_back(std::make_unique()); - // kicks.push_back(std::make_unique()); - //kicks.push_back(std::make_unique()); -} - -bool TestObjective::perform(Ballchaser& ballchaser) -{ - kickManager.update(role->ballPosition, role->theRobotPoseAfterPreview, role->theWalkingEngineParams); - - /* - PositionUtils::setPosition(ballchaser, {200, 0}); - PositionUtils::turnToPosition(ballchaser, {0, 0}); - return true; - */ - - /* - const Vector2f targetPosition = Vector2f(0, 0); - std::vector executableKicks = kickManager.getExecutableKicks( - role->theRobotPoseAfterPreview, - role->ballPosition, - targetPosition, - DistanceRequirement::mayShorterOrFurther, - KickUtils::unpack(kicks), - role->theFieldDimensions, - role->theHeatMapCollection, - role->theRobotMap); - if (executableKicks.empty()) - { - return false; - } - kickManager.kickTo( - ballchaser, - role->theRobotPoseAfterPreview, - role->ballPosition, - executableKicks.at(0).target, - *executableKicks.at(0).kick); - return true; - */ - - ExecutableKicks executableKicks = - kickManager - .getExecutableKicks(role->theRobotPoseAfterPreview, role->ballPosition, KickUtils::unpack(kicks), role->theFieldDimensions, role->theHeatMapCollection, role->theRobotMap) - .filterOutside(role->theFieldDimensions) - .filterBlocked(role->theFieldDimensions, role->theRobotMap) - .reduceToBest(-1, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, role->kickWithLeft, role->theFieldDimensions, role->theHeatMapCollection, role->theRobotMap); - if (executableKicks.hasBest()) - { - kickManager.kickTo(ballchaser, executableKicks); - } - return true; -} - -bool TestObjective::leaveCondition() const -{ - return false; -} diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/ObjectivesManager.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/ObjectivesManager.h deleted file mode 100644 index 90578998..00000000 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/ObjectivesManager.h +++ /dev/null @@ -1,116 +0,0 @@ -#pragma once - -#include "Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Objective.h" -#include "Modules/BehaviorControl/TacticControl/RoleProvider/Logs/BehaviorLogger.h" - -template class ObjectivesManager -{ - -public: - explicit ObjectivesManager(BehaviorLogger& logger) : logger(logger) {} - ~ObjectivesManager() = default; - - void executeObjective(PS& positioningSymbols) - { - bool executed = executeCurrentObjective(positioningSymbols); - - if (!executed) - { - findAndExecuteNewObjective(positioningSymbols); - } - - if (currentObjective) - { - positioningSymbols.log_currObj = currentObjective->getName(); - } - else - { - positioningSymbols.log_currObj = "None"; - } - } - - /** - * @brief add most important objective first - */ - void add(std::unique_ptr> objective) { objectives.push_back(std::move(objective)); } - -private: - bool executeCurrentObjective(PS& positioningSymbols) - { - if (currentObjective) - { - if (currentObjective->leaveCondition()) - { - return false; - } - else - { - logger.start(currentObjective->getName()); - if (currentObjective->perform(positioningSymbols)) - { - logger.stop(); - return true; - } - else - { - stopObjective(currentObjective); - logger.stop(); - return false; - } - } - } - return false; - } - - void findAndExecuteNewObjective(PS& positioningSymbols) - { - for (const auto& objective : objectives) - { - logger.start(objective->getName()); - if (objective->enterCondition()) - { - if (objective.get() != currentObjective) - { - objective.get()->preprocess(); - } - if (objective.get()->perform(positioningSymbols)) - { - setCurrentObjective(objective.get()); - logger.stop(); - return; - } - else - { - stopObjective(objective.get()); - } - } - logger.stop(); - } - } - - void setCurrentObjective(Objective* objective) - { - if (currentObjective) - { - if (currentObjective == objective) - { - return; - } - currentObjective->postprocess(); - } - currentObjective = objective; - } - - void stopObjective(Objective* objective) - { - objective->postprocess(); - if (currentObjective == objective) - { - currentObjective = nullptr; - } - } - - Objective* currentObjective = nullptr; - std::vector>> objectives; - BehaviorLogger& logger; -}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Objective.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/ObjectivesManager/Objective.h similarity index 54% rename from Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Objective.h rename to Src/Modules/BehaviorControl/TacticControl/RoleProvider/ObjectivesManager/Objective.h index d6395352..45bb6bb0 100644 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Objectives/Objective.h +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/ObjectivesManager/Objective.h @@ -2,25 +2,19 @@ #include #include "Modules/BehaviorControl/TacticControl/RoleProvider/Logs/BehaviorLogger.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/ObjectivesManager/ObjectiveBase.h" -template class Objective +template class Objective : public ObjectiveBase { public: - Objective(std::string name, const P* role, BehaviorLogger& logger) : role(role), logger(logger), name(std::move(name)) {} + Objective(std::string name, const P* role, BehaviorLogger& logger) : ObjectiveBase(name), role(role), logger(logger) {} virtual ~Objective() = default; - virtual bool enterCondition() { return true; } virtual void preprocess() { isActive = true; } - virtual bool perform(R& representation) = 0; - [[nodiscard]] virtual bool leaveCondition() const { return true; } virtual void postprocess() { isActive = false; } - std::string getName() { return name; } protected: const P* role; bool isActive = false; BehaviorLogger& logger; - -private: - const std::string name; }; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/ObjectivesManager/ObjectiveBase.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/ObjectivesManager/ObjectiveBase.h new file mode 100644 index 00000000..8dd6babb --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/ObjectivesManager/ObjectiveBase.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Logs/BehaviorLogger.h" + +template class ObjectiveBase +{ +public: + ObjectiveBase(const std::string name) : name(name) {} + virtual ~ObjectiveBase() = default; + + virtual bool enterCondition() { return true; } + virtual void preprocess() {} + virtual bool perform(R& representation) = 0; + [[nodiscard]] virtual bool leaveCondition() const { return true; } + virtual void postprocess() {} + std::string getRawName() { return name; } + virtual std::string getName() { return name; } + +protected: + const std::string name; +}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/ObjectivesManager/ObjectivesList.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/ObjectivesManager/ObjectivesList.h new file mode 100644 index 00000000..ed15068d --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/ObjectivesManager/ObjectivesList.h @@ -0,0 +1,145 @@ +#pragma once + +#include +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Logs/BehaviorLogger.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/ObjectivesManager/ObjectiveBase.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/ObjectivesManager/ObjectivesManager.h" + +template class ObjectivesList : public ObjectiveBase +{ +public: + ObjectivesList(BehaviorLogger* logger) : ObjectiveBase("List"), logger(logger) {} + + void add(std::unique_ptr> objective) { objectives.push_back(std::move(objective)); } + + void clear() + { + objectives.clear(); + currentObjective = nullptr; + } + + bool enterCondition() override { return true; } + + void preprocess() override {} + + bool perform(R& representation) override + { + if (currentObjective == nullptr || currentObjective->leaveCondition()) + { + findAndPerformObjective(representation); + } + else + { + bool executed = performCurrentObjective(representation); + if (!executed) + { + findAndPerformObjective(representation); + } + } + + if (currentObjective) + { + representation.log_currObj = currentObjective->getName(); + return true; + } + else + { + representation.log_currObj = "None"; + return false; + } + } + + [[nodiscard]] bool leaveCondition() const override { return true; } + + void postprocess() override + { + if (currentObjective == nullptr) + { + return; + } + stopObjective(currentObjective); + } + + std::string getName() override + { + if (currentObjective == nullptr) + { + return ""; + } + return currentObjective->getName(); + } + +private: + bool performCurrentObjective(R& positioningSymbols) + { + ASSERT(currentObjective); + + logger->start(currentObjective->getName()); + if (currentObjective->perform(positioningSymbols)) + { + logger->stop(); + return true; + } + else + { + stopObjective(currentObjective); + logger->stop(); + return false; + } + } + + void findAndPerformObjective(R& positioningSymbols) + { + for (const auto& objective : objectives) + { + logger->start(objective->getRawName()); + if (!objective->enterCondition()) + { + logger->stop(); + continue; + } + if (objective.get() != currentObjective) + { + objective.get()->preprocess(); + } + if (objective.get()->perform(positioningSymbols)) + { + logger->stop(); + setCurrentObjective(objective.get()); + return; + } + stopObjective(objective.get()); + logger->stop(); + } + } + + void setCurrentObjective(ObjectiveBase* objective) + { + if (currentObjective) + { + if (currentObjective == objective) + { + return; + } + stopObjective(currentObjective); + } + currentObjective = objective; + } + + void stopObjective(ObjectiveBase* objective) + { + objective->postprocess(); + if (currentObjective == objective) + { + ANNOTATION("ObjectivesManager", "Stopped currentObjective: " << objective->getName()); + currentObjective = nullptr; + } + } + +protected: + ObjectiveBase* currentObjective = nullptr; + +private: + std::vector>> objectives; + BehaviorLogger* logger; +}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/ObjectivesManager/ObjectivesManager.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/ObjectivesManager/ObjectivesManager.h new file mode 100644 index 00000000..3cb74e33 --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/ObjectivesManager/ObjectivesManager.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include "Modules/BehaviorControl/TacticControl/RoleProvider/ObjectivesManager/Objective.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/ObjectivesManager/ObjectivesList.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Logs/BehaviorLogger.h" + +template class ObjectivesManager +{ + +public: + explicit ObjectivesManager(BehaviorLogger* logger) : objectiveList(logger), logger(logger) {} + ~ObjectivesManager() = default; + + void performObjective(R& positioningSymbols) { objectiveList.perform(positioningSymbols); } + + void add(std::unique_ptr> objective) { objectiveList.add(std::move(objective)); } + + void clear() { objectiveList.clear(); } + + void stop() { objectiveList.postprocess(); } + +private: + ObjectivesList objectiveList; + BehaviorLogger* logger; +}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/ObjectivesManager/ObjectivesSwitch.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/ObjectivesManager/ObjectivesSwitch.h new file mode 100644 index 00000000..ce39c96f --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/ObjectivesManager/ObjectivesSwitch.h @@ -0,0 +1,51 @@ +#pragma once + +#include +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Logs/BehaviorLogger.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/ObjectivesManager/ObjectiveBase.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/ObjectivesManager/ObjectivesManager.h" + +template class ObjectivesSwitch : public Objective +{ +public: + ObjectivesSwitch(BehaviorLogger& logger, const ObjectivesManager& objectivesList, const std::function

& isSwitchAllowed) + : ObjectiveBase(), objectivesList(std::move(objectivesList)), isSwitchAllowed(std::move(isSwitchAllowed)) + { + } + + bool enterCondition() override { return objectivesList.enterCondition(); } + + void preprocess() override + { + objectivesList.preprocess(); + Objective::preprocess(); + } + + bool perform(R& representation) override { return objectivesList.perform(representation); } + + [[nodiscard]] bool leaveCondition() const override + { + const bool leave = objectivesList.leaveCondition(); + if (leave) + { + return isSwitchAllowed(this->role); + } + else + { + return false; + } + } + + void postprocess() override + { + objectivesList.postprocess(); + setSwitchToNeutral(); + Objective::postprocess(); + } + +private: + void setSwitchToNeutral() { objectivesList.currentObjective = nullptr; } + + ObjectivesManager objectivesList; + const std::function

& isSwitchAllowed; +}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/BackupBallchaserProvider.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/OldRoles/BackupBallchaserProvider.cpp similarity index 94% rename from Src/Modules/BehaviorControl/TacticControl/RoleProvider/BackupBallchaserProvider.cpp rename to Src/Modules/BehaviorControl/TacticControl/RoleProvider/OldRoles/BackupBallchaserProvider.cpp index 3b06c57a..f8e3576c 100644 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/BackupBallchaserProvider.cpp +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/OldRoles/BackupBallchaserProvider.cpp @@ -44,12 +44,11 @@ void BackupBallchaserProvider::getFollowPosition(BackupBallchaser& positioningSy { // assume ballchaser always positions in front of the ball Vector2f ballChaserPosition = theBallchaser.optPosition.translation; - for (auto& mate : theTeammateData.teammates) - { - if (mate.behaviorData.role == BehaviorData::ballchaser) - ballChaserPosition = mate.pose.translation; - } - positioningSymbols.optPosition = ballChaserPosition; + const Teammate* teammate = theTeammateData.getPlayer(static_cast(theBallChaserDecision.playerNumberToBall)); + if (teammate) + ballChaserPosition = teammate->robotPose.translation; + + positioningSymbols.optPosition.translation = ballChaserPosition; Angle toBallRotation = (theBallSymbols.ballPositionField - theRobotPose.translation).angle(); positioningSymbols.optPosition.rotation = toBallRotation; // back a little @@ -121,4 +120,4 @@ void BackupBallchaserProvider::getSetPlayPosition(BackupBallchaser& positioningS } } -MAKE_MODULE(BackupBallchaserProvider, behaviorControl) \ No newline at end of file +MAKE_MODULE(BackupBallchaserProvider, behaviorControl) diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/BackupBallchaserProvider.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/OldRoles/BackupBallchaserProvider.h similarity index 95% rename from Src/Modules/BehaviorControl/TacticControl/RoleProvider/BackupBallchaserProvider.h rename to Src/Modules/BehaviorControl/TacticControl/RoleProvider/OldRoles/BackupBallchaserProvider.h index bdaca337..7d9bb1ad 100644 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/BackupBallchaserProvider.h +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/OldRoles/BackupBallchaserProvider.h @@ -14,6 +14,7 @@ #include "Representations/BehaviorControl/GameSymbols.h" #include "Representations/BehaviorControl/RoleSymbols/BackupBallchaser.h" #include "Representations/BehaviorControl/RoleSymbols/Ballchaser.h" +#include "Representations/BehaviorControl/BallChaserDecision.h" #include "Representations/Configuration/FieldDimensions.h" #include "Representations/Infrastructure/TeamInfo.h" #include "Representations/Infrastructure/TeammateData.h" @@ -29,6 +30,7 @@ MODULE(BackupBallchaserProvider, REQUIRES(OwnTeamInfo), REQUIRES(RobotPose), REQUIRES(TeammateData), + REQUIRES(BallChaserDecision), PROVIDES(BackupBallchaser), LOADS_PARAMETERS(, (float)(1000.f) xDistanceToBallchaser diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/BallchaserKeeperProvider.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/OldRoles/BallchaserKeeperProvider.cpp similarity index 93% rename from Src/Modules/BehaviorControl/TacticControl/RoleProvider/BallchaserKeeperProvider.cpp rename to Src/Modules/BehaviorControl/TacticControl/RoleProvider/OldRoles/BallchaserKeeperProvider.cpp index 444e53eb..1e1b08de 100644 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/BallchaserKeeperProvider.cpp +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/OldRoles/BallchaserKeeperProvider.cpp @@ -12,7 +12,7 @@ void BallchaserKeeperProvider::update(BallchaserKeeper& positioningSymbols) /* ** calculation of the kick position of the goalie. Tries kick as soon as possible but ofc not into own goal.. */ - positioningSymbols.optPosition = theBallSymbols.ballPositionField; + positioningSymbols.optPosition.translation = theBallSymbols.ballPositionField; float optDistanceToBallY = sgn(theBallSymbols.ballPositionField.y()) * (theBehaviorConfiguration.optDistanceToBallY); // TODO static const Vector2f leftGoalPost(theFieldDimensions.xPosOwnGroundline, theFieldDimensions.yPosLeftGoal); @@ -36,4 +36,4 @@ void BallchaserKeeperProvider::update(BallchaserKeeper& positioningSymbols) positioningSymbols.previewArrival = true; } -MAKE_MODULE(BallchaserKeeperProvider, behaviorControl) \ No newline at end of file +MAKE_MODULE(BallchaserKeeperProvider, behaviorControl) diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/BallchaserKeeperProvider.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/OldRoles/BallchaserKeeperProvider.h similarity index 100% rename from Src/Modules/BehaviorControl/TacticControl/RoleProvider/BallchaserKeeperProvider.h rename to Src/Modules/BehaviorControl/TacticControl/RoleProvider/OldRoles/BallchaserKeeperProvider.h diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/CenterProvider2022.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/OldRoles/CenterProviderOld.cpp similarity index 73% rename from Src/Modules/BehaviorControl/TacticControl/RoleProvider/CenterProvider2022.cpp rename to Src/Modules/BehaviorControl/TacticControl/RoleProvider/OldRoles/CenterProviderOld.cpp index d5a0201e..ef182087 100644 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/CenterProvider2022.cpp +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/OldRoles/CenterProviderOld.cpp @@ -5,11 +5,11 @@ * */ -#include "CenterProvider2022.h" +#include "CenterProviderOld.h" -void CenterProvider2022::update(Center& positioningSymbols) +void CenterProviderOld::update(Center& positioningSymbols) { - DECLARE_DEBUG_DRAWING("behavior:CenterProvider2022:debugPositioning", "drawingOnField"); + DECLARE_DEBUG_DRAWING("behavior:CenterProvider:debugPositioning", "drawingOnField"); bool isStateSetOrPlaying = theGameInfo.state == STATE_SET || theGameInfo.state == STATE_PLAYING; Vector2f ballPosition = isStateSetOrPlaying ? theBallSymbols.ballPositionFieldPredicted : Vector2f::Zero(); @@ -18,21 +18,21 @@ void CenterProvider2022::update(Center& positioningSymbols) decide(positioningSymbols, theBallSymbols, theFieldDimensions, theGameInfo, theGameSymbols); } -void CenterProvider2022::stateReady_kickOff_own(Center& positioningSymbols, const Vector2f& ballPosition) +void CenterProviderOld::stateReady_kickOff_own(Center& positioningSymbols, const Vector2f& ballPosition) { ThresholdUtils::setThreshholdsHeigh(positioningSymbols); PositionUtils::setPosition(positioningSymbols, -500, -1500); PositionUtils::turnToPosition(positioningSymbols, ballPosition); } -void CenterProvider2022::stateReady_kickOff_opponent(Center& positioningSymbols, const Vector2f& ballPosition) +void CenterProviderOld::stateReady_kickOff_opponent(Center& positioningSymbols, const Vector2f& ballPosition) { ThresholdUtils::setThreshholdsHeigh(positioningSymbols); PositionUtils::setPosition(positioningSymbols, -1800, -700); PositionUtils::turnToPosition(positioningSymbols, ballPosition); } -float CenterProvider2022::goalKick_own(Center& positioningSymbols, bool left) +float CenterProviderOld::goalKick_own(Center& positioningSymbols, bool left) { ThresholdUtils::setThreshholdsHeigh(positioningSymbols); PositionUtils::setPosition(positioningSymbols, theBehaviorConfiguration.behaviorParameters.centerPositionConstraints.minX, 0); @@ -40,7 +40,7 @@ float CenterProvider2022::goalKick_own(Center& positioningSymbols, bool left) return 1.f; } -float CenterProvider2022::goalKick_opponent(Center& positioningSymbols, bool left) +float CenterProviderOld::goalKick_opponent(Center& positioningSymbols, bool left) { ThresholdUtils::setThreshholdsHeigh(positioningSymbols); PositionUtils::setPosition(positioningSymbols, 0, -1000.f); @@ -48,21 +48,21 @@ float CenterProvider2022::goalKick_opponent(Center& positioningSymbols, bool lef return 1.f; } -float CenterProvider2022::pushingFreeKick_own(Center& positioningSymbols) +float CenterProviderOld::pushingFreeKick_own(Center& positioningSymbols) { ThresholdUtils::setThreshholdsHeigh(positioningSymbols); regularPlay(positioningSymbols); return 0; } -float CenterProvider2022::pushingFreeKick_opponent(Center& positioningSymbols) +float CenterProviderOld::pushingFreeKick_opponent(Center& positioningSymbols) { ThresholdUtils::setThreshholdsHeigh(positioningSymbols); regularPlay(positioningSymbols); return 0; } -float CenterProvider2022::cornerKick_own(Center& positioningSymbols, const Vector2f& cornerKickPosition, bool left) +float CenterProviderOld::cornerKick_own(Center& positioningSymbols, const Vector2f& cornerKickPosition, bool left) { ThresholdUtils::setThreshholdsHeigh(positioningSymbols); //PositionUtils::setPosition(positioningSymbols, @@ -74,7 +74,7 @@ float CenterProvider2022::cornerKick_own(Center& positioningSymbols, const Vecto return 1.f; } -float CenterProvider2022::cornerKick_opponent(Center& positioningSymbols, const Vector2f& cornerKickPosition, bool left) +float CenterProviderOld::cornerKick_opponent(Center& positioningSymbols, const Vector2f& cornerKickPosition, bool left) { ThresholdUtils::setThreshholdsHeigh(positioningSymbols); //PositionUtils::setPosition(positioningSymbols, @@ -85,7 +85,7 @@ float CenterProvider2022::cornerKick_opponent(Center& positioningSymbols, const return 1.f; } -float CenterProvider2022::kickIn_own(Center& positioningSymbols, bool left) +float CenterProviderOld::kickIn_own(Center& positioningSymbols, bool left) { ThresholdUtils::setThreshholdsHeigh(positioningSymbols); // receiver should be helping out offensively so the center should be a kick target/supporter at roughly the same x pos @@ -97,7 +97,7 @@ float CenterProvider2022::kickIn_own(Center& positioningSymbols, bool left) return 1.f; } -float CenterProvider2022::kickIn_opponent(Center& positioningSymbols, bool left) +float CenterProviderOld::kickIn_opponent(Center& positioningSymbols, bool left) { ThresholdUtils::setThreshholdsHeigh(positioningSymbols); //setPassivePosition(positioningSymbols, theBallSymbols.ballPositionFieldPredicted); @@ -112,7 +112,7 @@ float CenterProvider2022::kickIn_opponent(Center& positioningSymbols, bool left) return 1.f; } -float CenterProvider2022::stateReady_penaltyKick_own(Center& positioningSymbols) +float CenterProviderOld::stateReady_penaltyKick_own(Center& positioningSymbols) { ThresholdUtils::setThreshholdsHeigh(positioningSymbols); setPassivePosition(positioningSymbols, theBallSymbols.ballPositionFieldPredicted); @@ -120,12 +120,12 @@ float CenterProvider2022::stateReady_penaltyKick_own(Center& positioningSymbols) return 1.f; } -float CenterProvider2022::stateReady_penaltyKick_opponent(Center& positioningSymbols) +float CenterProviderOld::stateReady_penaltyKick_opponent(Center& positioningSymbols) { return stateReady_penaltyKick_own(positioningSymbols); } -void CenterProvider2022::regularPlay(Center& positioningSymbols) +void CenterProviderOld::regularPlay(Center& positioningSymbols) { Vector2f ballPosition = theBallSymbols.ballPositionFieldPredicted; if (theTacticSymbols.defensiveBehavior) @@ -143,14 +143,14 @@ void CenterProvider2022::regularPlay(Center& positioningSymbols) // set position =========================================================================================================== -void CenterProvider2022::setPassivePosition(Center& positioningSymbols, const Vector2f& ballPositionField) +void CenterProviderOld::setPassivePosition(Center& positioningSymbols, const Vector2f& ballPositionField) { ThresholdUtils::setThreshholdsMedium(positioningSymbols); PositionUtils::setPosition(positioningSymbols, theBehaviorConfiguration.behaviorParameters.centerPositionConstraints.minX, 0); PositionUtils::turnToPosition(positioningSymbols, ballPositionField); } -void CenterProvider2022::setAgressivePlayingPosition(Center& positioningSymbols, const Vector2f& ballPositionField) +void CenterProviderOld::setAgressivePlayingPosition(Center& positioningSymbols, const Vector2f& ballPositionField) { ThresholdUtils::setThreshholdsMedium(positioningSymbols); @@ -178,8 +178,8 @@ void CenterProvider2022::setAgressivePlayingPosition(Center& positioningSymbols, yPos = realBallChaserPosition.y(); } - // prevent robot from leaving the field or entering the penalty area - xPos = xPos < 0.8f * theFieldDimensions.xPosOwnPenaltyArea ? 0.8f * theFieldDimensions.xPosOwnPenaltyArea : xPos; + // prevent robot from leaving the field or interfering with the defenders + xPos = std::max(theFieldDimensions.xPosOwnPenaltyMark, xPos); yPos = yPos >= theFieldDimensions.yPosLeftSideline ? theFieldDimensions.yPosLeftSideline - 100 : yPos; yPos = yPos <= theFieldDimensions.yPosRightSideline ? theFieldDimensions.yPosRightSideline + 100 : yPos; @@ -192,7 +192,7 @@ void CenterProvider2022::setAgressivePlayingPosition(Center& positioningSymbols, } } -void CenterProvider2022::setDefensivePlayingPosition(Center& positioningSymbols, const Vector2f& ballPositionField) +void CenterProviderOld::setDefensivePlayingPosition(Center& positioningSymbols, const Vector2f& ballPositionField) { ThresholdUtils::setThreshholdsLow(positioningSymbols); float destinationY = ballPositionField.y() / 2.f; @@ -204,28 +204,21 @@ void CenterProvider2022::setDefensivePlayingPosition(Center& positioningSymbols, positioningSymbols.keeperPosKnown = true; positioningSymbols.keeperPosY = keeperPosition.y(); - if (std::optional defenderPositionOptional = TeamUtils::getDesiredTeammatePosition(theTeammateData, BehaviorData::defenderSingle)) + Vector2f defenderPosition = theDefenderSingle.optPosition.translation; + positioningSymbols.defenderPosKnown = true; + positioningSymbols.defenderPosY = defenderPosition.y(); + + float yDistanceKeeperDefender = (keeperPosition.y() > defenderPosition.y()) ? (keeperPosition.y() - defenderPosition.y()) : (defenderPosition.y() - keeperPosition.y()); + + if (defenderPosition.y() > 0) { - Vector2f defenderPosition = *defenderPositionOptional; - positioningSymbols.defenderPosKnown = true; - positioningSymbols.defenderPosY = defenderPosition.y(); - - float yDistanceKeeperDefender = (keeperPosition.y() > defenderPosition.y()) ? (keeperPosition.y() - defenderPosition.y()) : (defenderPosition.y() - keeperPosition.y()); - - if (defenderPosition.y() > 0) - { - // defender is left of goal center, center should be right - destinationY = destinationY - yDistanceKeeperDefender; - } - else - { - // defender is right of goal center, center should be left - destinationY = destinationY + yDistanceKeeperDefender; - } + // defender is left of goal center, center should be right + destinationY = destinationY - yDistanceKeeperDefender; } else { - positioningSymbols.defenderPosKnown = false; + // defender is right of goal center, center should be left + destinationY = destinationY + yDistanceKeeperDefender; } // positioning between goal and ball without blocking only the keeper's view @@ -252,7 +245,7 @@ void CenterProvider2022::setDefensivePlayingPosition(Center& positioningSymbols, /** * @brief Avoids constant changes of the value */ -void CenterProvider2022::updateBallIsLeft(const Vector2f& ballPosition) +void CenterProviderOld::updateBallIsLeft(const Vector2f& ballPosition) { if (ballIsLeft && ballPosition.y() < -250.f) ballIsLeft = false; @@ -260,4 +253,4 @@ void CenterProvider2022::updateBallIsLeft(const Vector2f& ballPosition) ballIsLeft = true; } -MAKE_MODULE(CenterProvider2022, behaviorControl) +MAKE_MODULE(CenterProviderOld, behaviorControl) diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/CenterProvider2022.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/OldRoles/CenterProviderOld.h similarity index 81% rename from Src/Modules/BehaviorControl/TacticControl/RoleProvider/CenterProvider2022.h rename to Src/Modules/BehaviorControl/TacticControl/RoleProvider/OldRoles/CenterProviderOld.h index 68041c66..bc11e2ee 100644 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/CenterProvider2022.h +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/OldRoles/CenterProviderOld.h @@ -1,5 +1,5 @@ /** -* \file CenterProvider2022.h +* \file CenterProvider.h * The file declares a class that containts data about the desired position of the center on the field. */ @@ -14,19 +14,19 @@ #include "Representations/BehaviorControl/RoleSymbols/BallchaserKeeper.h" #include "Representations/BehaviorControl/RoleSymbols/Center.h" #include "Representations/BehaviorControl/TacticSymbols.h" +#include "Representations/BehaviorControl/RoleSymbols/DefenderSingle.h" #include "Representations/Configuration/FieldDimensions.h" #include "Representations/Infrastructure/GameInfo.h" #include "Representations/Infrastructure/TeammateData.h" -#include "Utils/AvoidUtils.h" -#include "Utils/PositionUtils.h" -#include "Utils/TeamUtils.h" -#include "Utils/ThresholdUtils.h" -#include "Utils/FieldUtils.h" -#include "Utils/FrontUtils.h" -#include "RoleProvider.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Utils/AvoidUtils.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Utils/PositionUtils.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Utils/TeamUtils.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Utils/ThresholdUtils.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Utils/FieldUtils.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/RoleProvider.h" #include -MODULE(CenterProvider2022, +MODULE(CenterProviderOld, REQUIRES(Ballchaser), // for avoidance REQUIRES(BallchaserKeeper), // for avoidance REQUIRES(BallChaserDecision), // for avoidance @@ -41,6 +41,7 @@ MODULE(CenterProvider2022, PROVIDES(Center), REQUIRES(RobotPose), REQUIRES(RobotMap), + REQUIRES(DefenderSingle), LOADS_PARAMETERS(, (float)(2400.f) setPlayOppCornerKickDistanceX, (float)(1000.f) setPlayOppCornerKickDistanceY, @@ -56,16 +57,16 @@ MODULE(CenterProvider2022, /** -* @class CenterProvider2022 +* @class CenterProvider * Symbols for new role behavior 2019 */ -class CenterProvider2022 : public CenterProvider2022Base, public RoleProvider

+class CenterProviderOld : public CenterProviderOldBase, public RoleProvider
{ public: /** Constructor */ - CenterProvider2022() {} + CenterProviderOld() {} private: void update(Center& positioningSymbols) override; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/ReceiverProvider.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/OldRoles/ReceiverProvider.cpp similarity index 95% rename from Src/Modules/BehaviorControl/TacticControl/RoleProvider/ReceiverProvider.cpp rename to Src/Modules/BehaviorControl/TacticControl/RoleProvider/OldRoles/ReceiverProvider.cpp index 31be22ea..63f72651 100644 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/ReceiverProvider.cpp +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/OldRoles/ReceiverProvider.cpp @@ -224,7 +224,6 @@ void ReceiverProvider::calcSupportBallchaserPosition(Receiver& positioningSymbol void ReceiverProvider::checkHeat(Vector2f positionToTry, Receiver& positioningSymbols) //TODO: Small Modifier for closeness to penalty line? { float const stepSize = 200.f; - float const heatRadius = 300.f; int const nmbrSteps = 3; int const nmbrSkippedSteps = 2; @@ -237,9 +236,8 @@ void ReceiverProvider::checkHeat(Vector2f positionToTry, Receiver& positioningSy Vector2f positionToCheck = positionToTry + Vector2f(stepSize * xStep, stepSize * yStep); if (!FieldUtils::isIllegal(positionToCheck, theFieldDimensions)) { - const HeatMap::Area area = HeatMap::getArea(positionToCheck, heatRadius, theFieldDimensions); - const auto sidesHeatScore = (-sidesHeatFactor * theHeatMapCollection.sidesHeatMap.getHeat(area, theFieldDimensions)); - const auto opponentsHeatScore = (-opponentsHeatFactor * theHeatMapCollection.opponentsMaxHeatMap.getHeat(area, theFieldDimensions)); + const auto sidesHeatScore = (-sidesHeatFactor * theHeatMapCollection.sidesHeatMap.getHeat(positionToCheck, theFieldDimensions)); + const auto opponentsHeatScore = (-opponentsHeatFactor * theHeatMapCollection.opponentKickHeatMap.getHeat(positionToCheck, theFieldDimensions)); float stepScore = sidesHeatScore + opponentsHeatScore; if (stepScore > maxStepScore) { @@ -311,14 +309,12 @@ void ReceiverProvider::calcEvadeBallchaserPosition(Receiver& positioningSymbols) void ReceiverProvider::updateBallchaserData() { - for (auto& mate : theTeammateData.teammates) + const Teammate* teammate = theTeammateData.getPlayer(static_cast(theBallChaserDecision.playerNumberToBall)); + if (teammate) { - if (mate.behaviorData.role == BehaviorData::RoleAssignment::ballchaser) - { - ballchaserPose = mate.pose; - ballchaserAction = mate.behaviorData.soccerState; - ballchaserKickTarget = mate.behaviorData.kickTarget.cast(); - } + ballchaserPose = teammate->robotPose; + ballchaserAction = teammate->behaviorData.soccerState; + ballchaserKickTarget = teammate->behaviorData.kickTarget; } } diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/ReceiverProvider.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/OldRoles/ReceiverProvider.h similarity index 94% rename from Src/Modules/BehaviorControl/TacticControl/RoleProvider/ReceiverProvider.h rename to Src/Modules/BehaviorControl/TacticControl/RoleProvider/OldRoles/ReceiverProvider.h index 5113b7f8..5add10e9 100644 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/ReceiverProvider.h +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/OldRoles/ReceiverProvider.h @@ -13,6 +13,7 @@ #include "Representations/BehaviorControl/BallSymbols.h" #include "Representations/BehaviorControl/GameSymbols.h" #include "Representations/BehaviorControl/TacticSymbols.h" +#include "Representations/BehaviorControl/BallChaserDecision.h" #include "Representations/BehaviorControl/RoleSymbols/Receiver.h" #include "Representations/Configuration/FieldDimensions.h" #include "Representations/Infrastructure/FrameInfo.h" @@ -21,8 +22,8 @@ #include "Representations/Infrastructure/TeamInfo.h" #include "Representations/Infrastructure/TeammateData.h" #include "Representations/Modeling/RobotPose.h" -#include "RoleProvider.h" -#include "Utils/PositionUtils.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/RoleProvider.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Utils/PositionUtils.h" #include "Representations/Modeling/RobotMap.h" #include "Representations/Modeling/HeatMapCollection.h" #include "Modules/BehaviorControl/TacticControl/RoleProvider/Utils/FieldUtils.h" @@ -30,7 +31,6 @@ MODULE(ReceiverProvider, REQUIRES(BallSymbols), REQUIRES(RobotMap), - REQUIRES(LocalRobotMap), REQUIRES(HeatMapCollection), REQUIRES(FieldDimensions), REQUIRES(FrameInfo), @@ -41,6 +41,7 @@ MODULE(ReceiverProvider, REQUIRES(TacticSymbols), REQUIRES(OwnTeamInfo), REQUIRES(TeammateData), + REQUIRES(BallChaserDecision), PROVIDES(Receiver), LOADS_PARAMETERS(, (float)(200.f) minXPosition, diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/ReplacementKeeperProvider.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/OldRoles/ReplacementKeeperProvider.cpp similarity index 94% rename from Src/Modules/BehaviorControl/TacticControl/RoleProvider/ReplacementKeeperProvider.cpp rename to Src/Modules/BehaviorControl/TacticControl/RoleProvider/OldRoles/ReplacementKeeperProvider.cpp index fba0d55e..bc934b0c 100644 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/ReplacementKeeperProvider.cpp +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/OldRoles/ReplacementKeeperProvider.cpp @@ -49,7 +49,7 @@ void ReplacementKeeperProvider::update(ReplacementKeeper& positioningSymbols) positioningSymbols.thresholdXFront = 150.f; positioningSymbols.thresholdY = 100.f; positioningSymbols.thresholdRotation = 10_deg; - positioningSymbols.stopAtTarget = true; + positioningSymbols.stopAtTarget = false; positioningSymbols.previewArrival = true; } } @@ -107,10 +107,10 @@ void ReplacementKeeperProvider::calcOptPosition(ReplacementKeeper& positioningSy if (interceptBall) positioningSymbols.optPosition.rotation = theRobotPoseAfterPreview.rotation + Angle::normalize(theBallSymbols.ballVelocityRelative.angle() + pi); if ((!interceptBall && !positioningSymbols.blockBall && (ownGoalCenter - theBallSymbols.ballPositionField).norm() < 2000 && theBallSymbols.ballInOwnPenaltyArea) - || theBallSymbols.ballPositionRelative.norm() < 800) + || (theBallChaserDecision.playerNumberToBall == theRobotInfo.number && theRoleSymbols.role == BehaviorData::RoleAssignment::replacementKeeper)) { positioningSymbols.kickIt = true; - positioningSymbols.optPosition = theBallSymbols.ballPositionField; + positioningSymbols.optPosition.translation = theBallSymbols.ballPositionField; float optDistanceToBallY = sgn(theBallSymbols.ballPositionField.y()) * (theBehaviorConfiguration.optDistanceToBallY); // TODO static const Vector2f leftGoalPost(theFieldDimensions.xPosOwnGroundline, theFieldDimensions.yPosLeftGoal); @@ -131,7 +131,8 @@ void ReplacementKeeperProvider::calcOptPosition(ReplacementKeeper& positioningSy positioningSymbols.thresholdXFront = 70; positioningSymbols.thresholdY = 40; positioningSymbols.previewArrival = true; + positioningSymbols.stopAtTarget = false; } } -MAKE_MODULE(ReplacementKeeperProvider, behaviorControl) \ No newline at end of file +MAKE_MODULE(ReplacementKeeperProvider, behaviorControl) diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/ReplacementKeeperProvider.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/OldRoles/ReplacementKeeperProvider.h similarity index 83% rename from Src/Modules/BehaviorControl/TacticControl/RoleProvider/ReplacementKeeperProvider.h rename to Src/Modules/BehaviorControl/TacticControl/RoleProvider/OldRoles/ReplacementKeeperProvider.h index 798574a9..905a79d2 100644 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/ReplacementKeeperProvider.h +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/OldRoles/ReplacementKeeperProvider.h @@ -14,9 +14,12 @@ #include "Representations/BehaviorControl/BehaviorConfiguration.h" #include "Representations/BehaviorControl/GoalSymbols.h" #include "Representations/BehaviorControl/RoleSymbols/ReplacementKeeper.h" +#include "Representations/BehaviorControl/BallChaserDecision.h" +#include "Representations/BehaviorControl/RoleSymbols.h" #include "Representations/Configuration/FieldDimensions.h" #include "Representations/Infrastructure/FrameInfo.h" #include "Representations/Infrastructure/GameInfo.h" +#include "Representations/Infrastructure/RobotInfo.h" #include "Representations/Modeling/RobotPose.h" MODULE(ReplacementKeeperProvider, @@ -27,6 +30,9 @@ MODULE(ReplacementKeeperProvider, REQUIRES(GameInfo), REQUIRES(GoalSymbols), REQUIRES(RobotPoseAfterPreview), + REQUIRES(BallChaserDecision), + REQUIRES(RobotInfo), + USES(RoleSymbols), // This is not a perfect solution, but we need to know if we are allowed to go to the ball for the chase ball decision PROVIDES(ReplacementKeeper), LOADS_PARAMETERS(, (float)(250.f) minBallDistanceForBlock, diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/RightWingProvider.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/RightWingProvider.cpp new file mode 100644 index 00000000..9f14bea6 --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/RightWingProvider.cpp @@ -0,0 +1,105 @@ +#include +#include "RightWingProvider.h" +#include "Tools/Debugging/DebugDrawings3D.h" +#include "Utils/PositionUtils.h" +#include "Utils/ThresholdUtils.h" + +void RightWingProvider::update(RightWing& role) +{ + role.stopAtTarget = true; + + decide(role, theBallSymbols, theFieldDimensions, theGameInfo, theGameSymbols); +} + +void RightWingProvider::stateReady_kickOff_own(RightWing& role, const Vector2f& ballPosition) +{ + ThresholdUtils::setThreshholdsHeigh(role); + const float x = -theFieldDimensions.xPosOpponentPenaltyArea / 5; + const float y = theFieldDimensions.yPosRightPenaltyArea; + PositionUtils::setPosition(role, x, y); + PositionUtils::turnToPosition(role, {700.f, 0.f}); +} + +void RightWingProvider::stateReady_kickOff_opponent(RightWing& role, const Vector2f& ballPosition) +{ + ThresholdUtils::setThreshholdsHeigh(role); + const float x = -theFieldDimensions.xPosOpponentPenaltyArea / 2; + const float y = theFieldDimensions.yPosRightPenaltyArea; + PositionUtils::setPosition(role, x, y); + PositionUtils::turnTowardsBall(role, theBallSymbols); +} + +float RightWingProvider::goalKick_own(RightWing& role, bool left) +{ + regularPlay(role); + return 0.f; +} + +float RightWingProvider::goalKick_opponent(RightWing& role, bool left) +{ + regularPlay(role); + return 0; +} + +float RightWingProvider::pushingFreeKick_own(RightWing& role) +{ + regularPlay(role); + return 0; +} + +float RightWingProvider::pushingFreeKick_opponent(RightWing& role) +{ + regularPlay(role); + return 0; +} + +float RightWingProvider::cornerKick_own(RightWing& role, const Vector2f& cornerKickPosition, bool left) +{ + regularPlay(role); + return 0; +} + +float RightWingProvider::cornerKick_opponent(RightWing& role, const Vector2f& cornerKickPosition, bool left) +{ + regularPlay(role); + return 0; +} + +float RightWingProvider::kickIn_own(RightWing& role, bool left) +{ + regularPlay(role); + return 0; +} + +float RightWingProvider::kickIn_opponent(RightWing& role, bool left) +{ + regularPlay(role); + return 0; +} + +float RightWingProvider::stateReady_penaltyKick_own(RightWing& role) +{ + regularPlay(role); + return 0; +} + +float RightWingProvider::stateReady_penaltyKick_opponent(RightWing& role) +{ + regularPlay(role); + return 0; +} + +void RightWingProvider::regularPlay(RightWing& role) +{ + ThresholdUtils::setThreshholdsExtremeHeigh(role, theTacticSymbols.activity); + + PositionUtils::turnTowardsBall(role, theBallSymbols); + + float x = theBallSymbols.ballPositionField.x() + 1000.f - 2000.f * (float)theTacticSymbols.defensiveBehavior; + x = MathUtils::clamp_f(x, theFieldDimensions.xPosOwnPenaltyArea * 2 / 3, theFieldDimensions.xPosOpponentPenaltyArea * 2 / 3); + const float yOffset = theFieldDimensions.yPosLeftSideline / 5; + const float y = theFieldDimensions.yPosRightSideline + yOffset + yOffset * (float)theTacticSymbols.defensiveBehavior; + PositionUtils::setPosition(role, x, y); +} + +MAKE_MODULE(RightWingProvider, behaviorControl) diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/RightWingProvider.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/RightWingProvider.h new file mode 100644 index 00000000..934c1631 --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/RightWingProvider.h @@ -0,0 +1,76 @@ +#pragma once + +#include +#include "Tools/Module/Module.h" +#include "Modules/BehaviorControl/CABSL/BehaviorParameters.h" +#include "Representations/Configuration/FieldDimensions.h" +#include "Representations/BehaviorControl/BallSymbols.h" +#include "Representations/BehaviorControl/GameSymbols.h" +#include "Representations/BehaviorControl/GoalSymbols.h" +#include "Representations/BehaviorControl/RoleSelection.h" +#include "Representations/BehaviorControl/BallChaserDecision.h" +#include "Representations/BehaviorControl/BehaviorConfiguration.h" +#include "Representations/BehaviorControl/RoleSymbols/RightWing.h" +#include "Representations/BehaviorControl/RoleSymbols/Receiver.h" +#include "Representations/BehaviorControl/RoleSymbols/Ballchaser.h" +#include "Representations/BehaviorControl/RoleSymbols/BallchaserKeeper.h" +#include "Representations/BehaviorControl/RoleSymbols/PositioningSymbols.h" +#include "Representations/BehaviorControl/TacticSymbols.h" +#include "Representations/Infrastructure/FrameInfo.h" +#include "Representations/Infrastructure/GameInfo.h" +#include "Representations/Infrastructure/RobotInfo.h" +#include "Representations/Infrastructure/TeamInfo.h" +#include "Representations/Infrastructure/TeammateData.h" +#include "Representations/BehaviorControl/BehaviorData.h" +#include "Representations/Modeling/DangerMap.h" +#include "Representations/Modeling/RobotMap.h" +#include "Representations/Modeling/RobotPose.h" +#include "Representations/MotionControl/WalkingEngineParams.h" +#include "Tools/Settings.h" +#include "RoleProvider.h" + +MODULE(RightWingProvider, + REQUIRES(BallSymbols), + REQUIRES(BehaviorConfiguration), + REQUIRES(DangerMap), + REQUIRES(FrameInfo), + REQUIRES(FieldDimensions), + REQUIRES(GameInfo), + REQUIRES(GameSymbols), + REQUIRES(GoalSymbols), + REQUIRES(OwnTeamInfo), + REQUIRES(OpponentTeamInfo), + REQUIRES(RobotInfo), + REQUIRES(RobotMap), + REQUIRES(RobotPose), + REQUIRES(RobotPoseAfterPreview), + REQUIRES(RoleSelection), + REQUIRES(Receiver), + REQUIRES(TeammateData), + REQUIRES(TacticSymbols), + REQUIRES(WalkingEngineParams), + USES(PositioningSymbols), + PROVIDES(RightWing) +); + +class RightWingProvider : public RightWingProviderBase, public RoleProvider +{ + +public: + void update(RightWing& role) override; + +private: + void stateReady_kickOff_own(RightWing& role, const Vector2f& ballPosition) override; + void stateReady_kickOff_opponent(RightWing& role, const Vector2f& ballPosition) override; + float goalKick_own(RightWing& role, bool left) override; + float goalKick_opponent(RightWing& role, bool left) override; + float pushingFreeKick_own(RightWing& role) override; + float pushingFreeKick_opponent(RightWing& role) override; + float cornerKick_own(RightWing& role, const Vector2f& cornerKickPosition, bool left) override; + float cornerKick_opponent(RightWing& role, const Vector2f& cornerKickPosition, bool left) override; + float kickIn_own(RightWing& role, bool left) override; + float kickIn_opponent(RightWing& role, bool left) override; + float stateReady_penaltyKick_own(RightWing& role) override; + float stateReady_penaltyKick_opponent(RightWing& role) override; + void regularPlay(RightWing& role) override; +}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/RoleProvider.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/RoleProvider.h index 534a6b49..086f693a 100644 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/RoleProvider.h +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/RoleProvider.h @@ -1,11 +1,11 @@ #pragma once -#include "Representations/BehaviorControl/PositioningSymbols.h" -#include -#include +#include "Representations/BehaviorControl/RoleSymbols/PositioningSymbols.h" #include -#include #include +#include +#include +#include template class RoleProvider { @@ -17,6 +17,8 @@ template class RoleProvider // START :: COPY THESE TO CREATE A NEW ROLE ========================================================================== // Some of these methods return a float: These are the seconds for which the method continues to get executed after // the setPlay finishes + virtual void stateInitial_kickOff_own(PS& positioningSymbols, const Vector2f& ballPosition) { stateReady_kickOff_own(positioningSymbols, ballPosition); } + virtual void stateInitial_kickOff_opponent(PS& positioningSymbols, const Vector2f& ballPosition) { stateReady_kickOff_opponent(positioningSymbols, ballPosition); } virtual void stateReady_kickOff_own(PS& positioningSymbols, const Vector2f& ballPosition) = 0; virtual void stateReady_kickOff_opponent(PS& positioningSymbols, const Vector2f& ballPosition) = 0; virtual void statePlaying_kickOff_own(PS& positioningSymbols, const Vector2f& ballPosition) { stateReady_kickOff_own(positioningSymbols, ballPosition); } @@ -72,9 +74,26 @@ template class RoleProvider switch (theGameInfo.state) { case STATE_INITIAL: - stateInitial(positioningSymbols); - positioningSymbols.log_currState = "STATE_INITIAL"; + { + bool stop = stateInitial(positioningSymbols); + if (stop) + { + positioningSymbols.log_currState = "STATE_INITIAL"; + return; + } + + if (theGameSymbols.ownKickOff) + { + stateInitial_kickOff_own(positioningSymbols, Vector2f::Zero()); + positioningSymbols.log_currState = "STATE_INITIAL stateInitial_kickOff_own"; + } + else + { + stateInitial_kickOff_opponent(positioningSymbols, Vector2f::Zero()); + positioningSymbols.log_currState = "STATE_INITIAL stateInitial_kickOff_opponent"; + } return; + } case STATE_READY: { bool stop = stateReady(positioningSymbols); @@ -135,7 +154,7 @@ template class RoleProvider return; } - if (theGameSymbols.kickoffInProgress && theGameSymbols.timeSincePlayingState < 10000) + if (theGameSymbols.kickoffInProgress && theGameSymbols.timeSincePlayingState < 10000 && theGameInfo.setPlay == SET_PLAY_NONE) // TODO KickOff time constant { if (theGameSymbols.ownKickOff) { @@ -243,7 +262,7 @@ template class RoleProvider // START :: STATE METHODS ============================================================================================ // Executed for the specified state. If true is returned no other method for this state will get called (e.g. if // statePlaying returns true the regularPlay method won't get called). These methods should not be needed. - virtual void stateInitial(PS& positioningSymbols) {} + virtual bool stateInitial(PS& positioningSymbols) { return false; } virtual bool stateReady(PS& positioningSymbols) { return false; } virtual void stateSet(PS& positioningSymbols) {} virtual bool statePlaying(PS& positioningSymbols) { return false; } diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/AvoidUtils.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/AvoidUtils.h index 6f71544b..8d2b90d1 100644 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/AvoidUtils.h +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/AvoidUtils.h @@ -62,7 +62,7 @@ class AvoidUtils // This method assumes that the desired position has already been calculated!! for (auto& mate : TeamUtils::getPlayingTeammates(theTeammateData)) { - avoid(positioningSymbols.optPosition.translation, mate.pose.translation, theBehaviorConfiguration.behaviorParameters.positionConflictDistance); + avoid(positioningSymbols.optPosition.translation, mate.robotPose.translation, theBehaviorConfiguration.behaviorParameters.positionConflictDistance); } } }; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/BallUtils.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/BallUtils.h index 45aa9b4f..b362a21e 100644 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/BallUtils.h +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/BallUtils.h @@ -1,19 +1,44 @@ #pragma once +#include "Representations/BehaviorControl/BallSymbols.h" #include "Representations/BehaviorControl/RoleSymbols/Center.h" +#include "Representations/Infrastructure/FrameInfo.h" #include "Representations/Infrastructure/TeammateData.h" #include "Representations/Modeling/RobotMap.h" -#include "Representations/Infrastructure/FrameInfo.h" -#include "Representations/BehaviorControl/BallSymbols.h" +#include +#include "Representations/Configuration/FieldDimensions.h" class BallUtils { public: - static Vector2f getBallPosition(bool predict, const BallSymbols& theBallSymbols, const FrameInfo& theFrameInfo) + static Vector2f getBallPosition(const BallSymbols& theBallSymbols, const FrameInfo& theFrameInfo, const RobotPoseAfterPreview& theRobotPoseAfterPreview) { - Vector2f ballPosition = predict ? theBallSymbols.ballPositionFieldPredicted : theBallSymbols.ballPositionField; + // changes to the predicted position made this method useless. todo delete + return theBallSymbols.ballPositionFieldPredicted; + + /* + const float ONLY_PREDICTED_DISTANCE = 2000.f; + const float ONLY_REAL_DISTANCE = 1000.f; + if (theBallSymbols.timeSinceLastSeenByTeam == static_cast(theFrameInfo.time)) // never seen -> assume center position - ballPosition = Vector2f::Zero(); + { + return Vector2f::Zero(); + } + + const float distanceToBall = Geometry::distance(theRobotPoseAfterPreview.translation, theBallSymbols.ballPositionField); + if (distanceToBall < ONLY_REAL_DISTANCE) + { + return theBallSymbols.ballPositionField; + } + if (distanceToBall > ONLY_PREDICTED_DISTANCE) + { + return theBallSymbols.ballPositionFieldPredicted; + } + const float multiplier = (distanceToBall - ONLY_REAL_DISTANCE) / (ONLY_PREDICTED_DISTANCE - ONLY_REAL_DISTANCE); + const Vector2f ballPosition = multiplier * theBallSymbols.ballPositionFieldPredicted + (1-multiplier) * theBallSymbols.ballPositionField; + ASSERT(std::isnormal(ballPosition.x()) || ballPosition.x() == 0.0f); + ASSERT(std::isnormal(ballPosition.y()) || ballPosition.y() == 0.0f); return ballPosition; + */ } }; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/BlockUtils.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/BlockUtils.cpp deleted file mode 100644 index 2206d389..00000000 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/BlockUtils.cpp +++ /dev/null @@ -1,191 +0,0 @@ -#include "BlockUtils.h" - -#include -#include "Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/Kick.h" -#include "Representations/Modeling/RobotMap.h" -#include "Representations/BehaviorControl/BehaviorData.h" -#include "Representations/Infrastructure/TeammateData.h" -#include "Representations/BehaviorControl/GameSymbols.h" -#include "Representations/Configuration/FieldDimensions.h" - -bool BlockUtils::isKickBlocked( - const Vector2f& ballPosition, const Vector2f& targetPosition, const float minKickWidth, const bool hysteresis, const FieldDimensions& theFieldDimensions, const RobotMap& theRobotMap) -{ - const float hysteresisMult = hysteresis ? 0.9f : 1.f; - - const float ballRadius = theFieldDimensions.ballRadius * hysteresisMult; - const float robotRadius = 140.f * hysteresisMult; // TODO Constants - - const float minKickRadius = minKickWidth / 2 * hysteresisMult; - - const Vector2f ballToTarget = (targetPosition - ballPosition); - const Vector2f ballToTargetNormalized = ballToTarget.normalized(); - const float ballToTargetDistance = ballToTarget.norm(); - const Geometry::Line ballToTargetLine = {ballPosition, ballToTargetNormalized}; - const Geometry::Line behindBallLine = {ballPosition, Vector2f(ballToTargetNormalized).rotateLeft()}; - - for (auto& robot : theRobotMap.robots) - { - const Vector2f position = robot.pose.translation; - - const bool behindBall = Geometry::isPointLeftOfLine(position, behindBallLine); - if (behindBall) - { - continue; - } - - float radius = robotRadius + minKickRadius + ballRadius; - - if (ballToTargetDistance < Geometry::distance(ballPosition, position) - radius) - { - // Robot ist too far away to block - continue; - } - - Geometry::Circle robotReach = {position, radius}; - - Vector2f intersection1; - Vector2f intersection2; - const int intersections = Geometry::getIntersectionOfLineAndCircle(ballToTargetLine, robotReach, intersection1, intersection2); - if (intersections > 0) - { - CIRCLE("behavior:BallchaserProvider:KickManager:Blocked", robot.pose.translation.x(), robot.pose.translation.y(), radius, 20, Drawings::solidPen, ColorRGBA::red, Drawings::solidBrush, ColorRGBA::orange); - return true; - } - } - LINE("behavior:BallchaserProvider:KickManager:Blocked", ballPosition.x(), ballPosition.y(), targetPosition.x(), targetPosition.y(), 5, Drawings::solidPen, ColorRGBA::green); - return false; -} - -bool BlockUtils::isTargetControlledByOpponent(const Vector2f& ballPosition, const Vector2f& targetPosition, const float minFreeAroundTarget, const bool hysteresis, const RobotMap& theRobotMap) -{ - const float hysteresisMult = hysteresis ? 1.f : 1.1f; // TODO Constant - - const float degree0_minFreeAroundTarget = minFreeAroundTarget * hysteresisMult; - const float degree70_minFreeAroundTarget = minFreeAroundTarget * 0.7f * hysteresisMult; // TODO Constant - const float degree120_minFreeAroundTarget = minFreeAroundTarget * 0.4f * hysteresisMult; // TODO Constant - - Geometry::Circle degree0_saveTargetArea = {targetPosition, degree0_minFreeAroundTarget}; - Geometry::Circle degree70_saveTargetArea = {targetPosition, degree70_minFreeAroundTarget}; - Geometry::Circle degree120_saveTargetArea = {targetPosition, degree120_minFreeAroundTarget}; - - const Vector2f ballToTarget = (targetPosition - ballPosition).normalized(); - const Geometry::Line behindBallLine = {ballPosition, Vector2f(ballToTarget).rotateLeft()}; - - for (auto& robot : theRobotMap.robots) - { - if (robot.robotType == RobotEstimate::teammateRobot) - { - continue; - } - - const Vector2f position = robot.pose.translation; - - const bool behindBall = Geometry::isPointLeftOfLine(position, behindBallLine); - if (behindBall) - { - continue; - } - - if (Geometry::isPointInCircle(degree0_saveTargetArea, position)) - { - LINE("behavior:BallchaserProvider:KickManager:TargetFree", ballPosition.x(), ballPosition.y(), targetPosition.x(), targetPosition.y(), 5, Drawings::solidPen, ColorRGBA::red); - CIRCLE("behavior:BallchaserProvider:KickManager:TargetFree", position.x(), position.y(), degree0_minFreeAroundTarget, 20, Drawings::dashedPen, ColorRGBA::red, Drawings::noBrush, ColorRGBA::red); - return true; - } - - /* TODO Use of RobotMap includes Pose of enemies - const Angle robotToTargetAngle = fabs(PositionUtils::getRelativeTurnToTargetAngle(robot.pose, targetPosition)); - - if (robotToTargetAngle > 120_deg) - { - if (Geometry::isPointInCircle(degree120_saveTargetArea, position)) - { - LINE("behavior:BallchaserProvider:KickManager:TargetFree", - ballPosition.x(), ballPosition.y(), targetPosition.x(), targetPosition.y(), 5, - Drawings::solidPen, ColorRGBA::yellow); - CIRCLE("behavior:BallchaserProvider:KickManager:TargetFree", - position.x(), position.y(), degree120_minFreeAroundTarget, 20, - Drawings::dashedPen, ColorRGBA::yellow, - Drawings::noBrush, ColorRGBA::yellow); - return true; - } - } - else if (robotToTargetAngle > 70_deg) - { - if (Geometry::isPointInCircle(degree70_saveTargetArea, position)) - { - LINE("behavior:BallchaserProvider:KickManager:TargetFree", - ballPosition.x(), ballPosition.y(), targetPosition.x(), targetPosition.y(), 5, - Drawings::solidPen, ColorRGBA::orange); - CIRCLE("behavior:BallchaserProvider:KickManager:TargetFree", - position.x(), position.y(), degree70_minFreeAroundTarget, 20, - Drawings::dashedPen, ColorRGBA::orange, - Drawings::noBrush, ColorRGBA::orange); - return true; - } - } - else - { - if (Geometry::isPointInCircle(degree0_saveTargetArea, position)) - { - LINE("behavior:BallchaserProvider:KickManager:TargetFree", - ballPosition.x(), ballPosition.y(), targetPosition.x(), targetPosition.y(), 5, - Drawings::solidPen, ColorRGBA::red); - CIRCLE("behavior:BallchaserProvider:KickManager:TargetFree", - position.x(), position.y(), degree0_minFreeAroundTarget, 20, - Drawings::dashedPen, ColorRGBA::red, - Drawings::noBrush, ColorRGBA::red); - return true; - } - } - */ - } - LINE("behavior:BallchaserProvider:KickManager:TargetFree", ballPosition.x(), ballPosition.y(), targetPosition.x(), targetPosition.y(), 5, Drawings::solidPen, ColorRGBA::green); - return false; -} - -/** -* @brief brief Calculates for all opponent robots that are kind of close to opponent goal, if they block a goal kick. -*/ -bool BlockUtils::isKickBlockedOld( - const Vector2f& ballPosition, const Vector2f& targetPosition, const float minOpponentToBallDistance, const float minOpeningAngleForDirectKick, const float robotRadius, const RobotMap& theRobotMap) -{ - // Ball position on field with angle to opponent goal. - Pose2f ballPosistionWCAngleToOppGoalCenter((targetPosition - ballPosition).angle(), ballPosition); - - // For each robot (only opponents) of the robot map: - for (auto& robot : theRobotMap.robots) - { - if (robot.robotType != RobotEstimate::teammateRobot) - { - // The position of the opponent robot from the robot map. - Vector2f opponentRobot = robot.pose.translation; - - Vector2f ballPositionRelativeToRobot = Transformation::fieldToRobot(ballPosistionWCAngleToOppGoalCenter, opponentRobot); - float distOpponentToBall = ballPositionRelativeToRobot.norm(); // Distance between opponent robot and ball. - Angle angleOpponentToBall = ballPositionRelativeToRobot.angle(); // Angle opponent robot to ball. - - // Check close opponent robots that are near the line from ball to kick targetPosition - if (distOpponentToBall < minOpponentToBallDistance // Distance smaller than max distance to block goal, with hysteresis. - //&& (opponentRobot - targetPosition).norm() - // > theFieldDimensions.yPosLeftGoal * 2 - (goalKickBlocked ? 250.f : 0.f) // Opponent is close to opponent goal, with hysteresis. - && std::abs(angleOpponentToBall) < 90_deg) // Angle from opponent to ball is not more than 90 degrees. - { - float g = std::abs(distOpponentToBall * std::sin(angleOpponentToBall)); // Opposite side of triangle ball - vecToGoal - robot. - float a = std::abs(distOpponentToBall * std::cos(angleOpponentToBall)); // Adjacent side of triangle ball - vecToGoal - robot. - - // The allowed angle of the corridor increases with the distance to the robot. - // float maxCorridorAngle = minOpeningAngleForDirectKick + (distOpponentToBall / minOpponentToBallDistance) * minOpeningAngleForDirectKick; - - // Calculate the opposite side of the triangle ball - vecToGoal - corridor edge. - float minDistToBallToGoalVector = a * std::tan(minOpeningAngleForDirectKick); - if (g - robotRadius < minDistToBallToGoalVector) - { - return true; - } - } - } - } - return false; -} \ No newline at end of file diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/BlockUtils.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/BlockUtils.h deleted file mode 100644 index 5913db7c..00000000 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/BlockUtils.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include "Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/Kick.h" -#include "Representations/Modeling/RobotMap.h" - -class BlockUtils -{ -public: - static bool isKickBlocked(const Vector2f& ballPosition, const Vector2f& targetPosition, float minKickWidth, bool hysteresis, const FieldDimensions& theFieldDimensions, const RobotMap& theRobotMap); - - static bool isTargetControlledByOpponent(const Vector2f& ballPosition, const Vector2f& targetPosition, float minFreeAroundTarget, bool hysteresis, const RobotMap& theRobotMap); - - /** - * @brief brief Calculates for all opponent robots that are kind of close to opponent goal, if they block a goal kick. - */ - static bool isKickBlockedOld( - const Vector2f& ballPosition, const Vector2f& targetPosition, float minOpponentToBallDistance, float minOpeningAngleForDirectKick, float robotRadius, const RobotMap& theRobotMap); -}; \ No newline at end of file diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/DangerUtils.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/DangerUtils.h deleted file mode 100644 index 71bca190..00000000 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/DangerUtils.h +++ /dev/null @@ -1,86 +0,0 @@ -#pragma once - -#include -#include -#include "Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/Kick.h" -#include "Modules/BehaviorControl/TacticControl/RoleProvider/Logs/KickDrawings.h" -#include "Representations/Modeling/RobotMap.h" -#include "Representations/BehaviorControl/BehaviorData.h" -#include "Representations/Infrastructure/TeammateData.h" -#include "Representations/BehaviorControl/GameSymbols.h" - -class DangerUtils -{ - -public: - static bool isDanger( - const Vector2f& playerPosition, const Vector2f& ballPosition, const float distanceMultiplier, const bool hysteresis, const DangerMap& theDangerMap, const FieldDimensions& theFieldDimensions, const RobotMap& theRobotMap) - { - if (isThereATroublemaker(ballPosition, distanceMultiplier, hysteresis, theRobotMap)) - { - return true; - } - - const float dangerDistance = distanceMultiplier * (hysteresis ? 1.2f : 1.f) * 500.f; // TODO Constant - const float dangerThreshold = 0.1f; // TODO Constant - if (theDangerMap.getDangerAt(ballPosition, theFieldDimensions, dangerDistance) > dangerThreshold) - { - return true; - } - - return false; - } - - static bool isThereATroublemaker(const Vector2f& ballPosition, const float distanceMultiplier, const bool hysteresis, const RobotMap& theRobotMap) - { - const float dangerRadiusFrontX = distanceMultiplier * (hysteresis ? 1.2f : 1.f) * 600.f; // TODO Constants - const float dangerRadiusBackX = dangerRadiusFrontX; - const float danderRadiusY = dangerRadiusFrontX; - - const Vector2f rectangleBottomLeftCorner = ballPosition - Vector2f(dangerRadiusBackX, danderRadiusY); - const Vector2f rectangleTopRightCorner = ballPosition + Vector2f(dangerRadiusFrontX, danderRadiusY); - - return isDangerInRectangle(rectangleBottomLeftCorner, rectangleTopRightCorner, theRobotMap); - } - - static bool isThereATroublemaker(const Vector2f& ballPosition, const Angle& ballToTargetAngle, const RobotMap& theRobotMap) - { - const float dangerRadiusBackX = 300.f; // TODO Constants - const float dangerRadiusFrontX = 1000.f; // TODO Constants - const float danderRadiusY = 800.f; // TODO Constants - - const Vector2f rectangleBottomLeftCorner = ballPosition - Vector2f(dangerRadiusBackX, danderRadiusY).rotate(ballToTargetAngle); - const Vector2f rectangleTopRightCorner = ballPosition + Vector2f(dangerRadiusFrontX, danderRadiusY).rotate(ballToTargetAngle); - - return isDangerInRectangle(rectangleBottomLeftCorner, rectangleTopRightCorner, theRobotMap); - } - -private: - static bool isDangerInRectangle(const Vector2f& rectangleBottomLeftCorner, const Vector2f& rectangleTopRightCorner, const RobotMap& theRobotMap) - { - const Vector2f topLeftCorner = {rectangleBottomLeftCorner.x(), rectangleTopRightCorner.y()}; - const Vector2f bottomRightCorner = {rectangleTopRightCorner.x(), rectangleBottomLeftCorner.y()}; - LINE(DRAW_KICK_DANGER_NAME, rectangleBottomLeftCorner.x(), rectangleBottomLeftCorner.y(), topLeftCorner.x(), topLeftCorner.y(), 50, Drawings::solidPen, ColorRGBA::orange); - LINE(DRAW_KICK_DANGER_NAME, topLeftCorner.x(), topLeftCorner.y(), rectangleTopRightCorner.x(), rectangleTopRightCorner.y(), 50, Drawings::solidPen, ColorRGBA::orange); - LINE(DRAW_KICK_DANGER_NAME, rectangleTopRightCorner.x(), rectangleTopRightCorner.y(), bottomRightCorner.x(), bottomRightCorner.y(), 50, Drawings::solidPen, ColorRGBA::orange); - LINE(DRAW_KICK_DANGER_NAME, bottomRightCorner.x(), bottomRightCorner.y(), rectangleBottomLeftCorner.x(), rectangleBottomLeftCorner.y(), 50, Drawings::solidPen, ColorRGBA::orange); - - bool trouble = false; - - for (auto& robot : theRobotMap.robots) - { - const Vector2f position = robot.pose.translation; - - if (robot.robotType == RobotEstimate::teammateRobot) - { - continue; - } - - if (Geometry::isPointInsideRectangle(rectangleBottomLeftCorner, rectangleTopRightCorner, position)) - { - trouble = true; - } - } - return trouble; - } -}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/FieldUtils.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/FieldUtils.h index d4f0c1e7..b9a74519 100644 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/FieldUtils.h +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/FieldUtils.h @@ -48,12 +48,29 @@ class FieldUtils return Vector2f(theFieldDimensions.xPosOpponentGroundline, theFieldDimensions.yPosCenterGoal); } + static Vector2f getOwnGoalLeftPost(const FieldDimensions& theFieldDimensions) { return Vector2f(theFieldDimensions.xPosOwnGroundline, theFieldDimensions.yPosLeftGoal); } + + static Vector2f getOwnGoalRightPost(const FieldDimensions& theFieldDimensions) { return Vector2f(theFieldDimensions.xPosOwnGroundline, theFieldDimensions.yPosRightGoal); } + static Vector2f getOwnGoalCenter(const FieldDimensions& theFieldDimensions) { return Vector2f(theFieldDimensions.xPosOwnGroundline, theFieldDimensions.yPosCenterGoal); } static bool isOnOwnSide(const Vector2f& position) { return isOnOwnSide(position.x()); } static bool isOnOwnSide(const float x) { return x < 0; } + static bool isIntoOpponentsGoal(const Vector2f& origin, const Vector2f& target, const FieldDimensions& theFieldDimensions) + { + const Vector2f leftGoalPostPosition = {theFieldDimensions.xPosOpponentGroundline, theFieldDimensions.yPosLeftGoal}; + const Vector2f rightGoalPostPosition = {theFieldDimensions.xPosOpponentGroundline, theFieldDimensions.yPosRightGoal}; + return Geometry::checkIntersectionOfLines(origin, target, leftGoalPostPosition, rightGoalPostPosition); + } + + static bool isInDirectionOfOpponentsGoal(const Vector2f& origin, const Vector2f& direction, const FieldDimensions& theFieldDimensions) + { + const Vector2f target = origin + 2 * theFieldDimensions.xPosOpponentGroundline * direction; + return isIntoOpponentsGoal(origin, target, theFieldDimensions); + } + static float getDistanceToGoalLine(const Vector2f& ballPosition, const FieldDimensions& theFieldDimensions) { float goalPostInnerOffset = 10.f; // TODO Constant diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/FrontUtils.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/FrontUtils.h deleted file mode 100644 index eb218b6a..00000000 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/FrontUtils.h +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once - -#include "TeamUtils.h" -#include "PositionUtils.h" -#include "Representations/BehaviorControl/RoleSymbols/Center.h" -#include "Representations/Infrastructure/TeammateData.h" -#include "Representations/Modeling/RobotMap.h" - -class FrontUtils -{ -public: - static void freeSight(Vector2f& position, const RobotMap& theRobotMap, const Vector2f& freeSightOnPosition, const float& maxDistanceFromCurrentPosition) - { - const int MAX_STEPS = 10; - const float checkStepsWidth = 30.f; - const float freeSigtDistance = 200.f; - - Vector2f toSightVector = freeSightOnPosition - position; - toSightVector.normalize(); - const Vector2f toRoboRightNormVector = turnLeft(toSightVector); - - Vector2f testPosition = Vector2f(position); - bool freeSight = true; - - for (int left = -1; left <= 1; left = left + 2) - { - for (int i = 0; i <= MAX_STEPS; i++) // start with 0 to test current posisiton - { - if (i == 0 && left == 1) // skip second test of current position - { - continue; - } - - for (auto& robot : theRobotMap.robots) - { - testPosition = Vector2f(position); - testPosition += left * i * toRoboRightNormVector * checkStepsWidth; - - if (PositionUtils::isPathBlocked(testPosition, freeSightOnPosition, robot.pose.translation, freeSigtDistance)) - { - freeSight = false; - break; - } - } - } - } - - if (freeSight) - { - position.x() = testPosition.x(); - position.y() = testPosition.y(); - } - } - - static Vector2f turnLeft(const Vector2f& vec) // TODO Auslagern - { - return -turnRight(vec); - } - - static Vector2f turnRight(const Vector2f& vec) // TODO Auslagern - { - return Vector2f(-vec.y(), vec.x()); - } -}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/HeatUtils.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/HeatUtils.h deleted file mode 100644 index 7c0a2dd8..00000000 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/HeatUtils.h +++ /dev/null @@ -1,164 +0,0 @@ -#pragma once - -#include -#include "Representations/Configuration/FieldDimensions.h" -#include "MathUtils.h" -#include "Representations/Modeling/RobotMap.h" -#include "PositionUtils.h" -#include "FieldUtils.h" - -class HeatUtils -{ -public: - static float getSidesHeat(const Vector2f& point, const FieldDimensions& theFieldDimensions) - { - const float x = MathUtils::clamp_f(point.x(), theFieldDimensions.xPosOwnGroundline, theFieldDimensions.xPosOpponentGroundline); - const float y = MathUtils::clamp_f(point.y(), theFieldDimensions.yPosRightSideline, theFieldDimensions.yPosLeftSideline); - - const float oppXDistance = theFieldDimensions.xPosOpponentGroundline - x; - const float ownXDistance = x - theFieldDimensions.xPosOwnGroundline; - const float leftYDistance = theFieldDimensions.yPosLeftSideline - y; - const float rightYDistance = y - theFieldDimensions.yPosRightSideline; - const float minDistance = std::min(oppXDistance, std::min(ownXDistance, std::min(leftYDistance, rightYDistance))); - - return 1 - minDistance / theFieldDimensions.xPosOpponentGroundline; - } - - static float getOpponentsGoalHeat(const Vector2f& point, const FieldDimensions& theFieldDimensions) - { - const Vector2f opponentGoalCenter = FieldUtils::getOpponentGoalCenter(theFieldDimensions); - const float opponentsGoalHeat = Geometry::distance(point, opponentGoalCenter); - return 1 - (opponentsGoalHeat / (2 * theFieldDimensions.xPosOpponentGoal)); - } - - static std::tuple getRobotHeat(const Vector2f& position, const FieldDimensions& theFieldDimensions, const RobotMap& theRobotMap, const RobotPose& theRobotPose) - { - std::vector allies = {}; - //allies.emplace_back(theRobotPose.translation); - std::vector opponents = {}; - for (auto& robot : theRobotMap.robots) - { - if (robot.robotType == RobotEstimate::teammateRobot) - allies.emplace_back(robot.pose); - else - opponents.emplace_back(robot.pose); - } - - auto [alliesHeat, alliesMaxHeat, alliesGoalKickHeat] = getHeat(allies, position, FieldUtils::getOpponentGoalCenter(theFieldDimensions), theFieldDimensions); - auto [opponentsHeat, opponentsMaxHeat, opponentsGoalKickHeat] = getHeat(opponents, position, FieldUtils::getOwnGoalCenter(theFieldDimensions), theFieldDimensions); - - return {alliesHeat, alliesMaxHeat, alliesGoalKickHeat, opponentsHeat, opponentsMaxHeat, opponentsGoalKickHeat}; - } - - static std::tuple getHeat(const std::vector& robotPoses, const Vector2f& position, const Vector2f& goalCenter, const FieldDimensions& theFieldDimensions) - { - if (robotPoses.empty()) - { - return {0.5f, 0.5f, 0.5f}; - } - - // TODO ADD if angle of robots known MAX_TURN_DISTANCE const float MAX_TURN_DISTANCE = 2500.f; // TODO Constant - const float MAX_WALK_AROUND_BALL_DISTANCE = 2000.f; // TODO Constant - - const float maxDistance = FieldUtils::getMaxDistanceOnField(theFieldDimensions); - const float maxDistanceToGoalKick = maxDistance + /*TODO ADD if angle of robots known MAX_TURN_DISTANCE +*/ MAX_WALK_AROUND_BALL_DISTANCE; - - float distanceSum = 0; - float distanceMin = maxDistance; - float distanceToGoalKickMin = maxDistanceToGoalKick; - - for (const Pose2f& robotPose : robotPoses) - { - float distance = Geometry::distance(robotPose.translation, position); - - distanceSum += distance; - - if (distance < distanceMin) - { - distanceMin = distance; - } - - const float turnDistanceToGoalKick = getTurnAroundPositionDistance(robotPose.translation, position, goalCenter, MAX_WALK_AROUND_BALL_DISTANCE); - const float canKickDistance = std::max(distance, 500.f); - const float distanceToGoalKick = canKickDistance + turnDistanceToGoalKick; - if (distanceToGoalKick < distanceToGoalKickMin) - { - distanceToGoalKickMin = distanceToGoalKick; - } - } - - const float distanceAverage = distanceSum / (float)robotPoses.size(); - float heat = 1 - distanceAverage / maxDistance; - heat = std::pow(MathUtils::clamp_f(heat, 0, 1), 2.f); // TODO Constant - - float maxHeat = 1 - distanceMin / maxDistance; - maxHeat = std::pow(MathUtils::clamp_f(maxHeat, 0, 1), 2.f); // TODO Constant - - float goalKickHeat = 1 - distanceToGoalKickMin / maxDistanceToGoalKick; - goalKickHeat = std::pow(MathUtils::clamp_f(goalKickHeat, 0.f, 1.f), 2.f); // TODO Constant - - return {heat, maxHeat, goalKickHeat}; - } - - static float getTurnDistanceToBall(const Vector2f& playerPosition, const Vector2f& ballPosition, const float maxTurnToDistance) - { - const Angle turnToTargetAngleDiff = std::fabs(PositionUtils::getTurnRobotByAngle(playerPosition, ballPosition)); - return (turnToTargetAngleDiff / 180_deg) * maxTurnToDistance; - } - - /** - * @return The distance the player has to walk around the turnAroundPosition to face the turnToPosition - */ - static float getTurnAroundPositionDistance(const Vector2f& playerPosition, const Vector2f& turnAroundPosition, const Vector2f& turnToPosition, const float maxWalkAroundDistance) - { - const Pose2f assumedPose = {(turnAroundPosition - playerPosition).angle(), turnAroundPosition}; - const Angle angleDiff = std::fabs(PositionUtils::getTurnRobotByAngle(assumedPose, turnToPosition)); - return (angleDiff / 180_deg) * maxWalkAroundDistance; - } - - // DRAW ============================================================================================================== - - static void draw(const HeatMap& heatMap, const FieldDimensions& theFieldDimensions) - { - const float offsetX = HeatMap::getStepSizeX(theFieldDimensions) / 2; - const float offsetY = HeatMap::getStepSizeY(theFieldDimensions) / 2; - - std::vector drawFieldPositionVector; - std::vector heatVector; - for (int i = 0; i < HeatMap::CELL_COUNT; i++) - { - const Vector2f fieldPosition = HeatMap::indexToField(i, theFieldDimensions); - const Vector2f drawFieldPosition = {fieldPosition.x() - offsetX, fieldPosition.y() - offsetY}; - drawFieldPositionVector.push_back(drawFieldPosition); - const float heat = heatMap.getHeat(i); - heatVector.push_back(heat); - } - draw(drawFieldPositionVector, heatVector, theFieldDimensions); - } - - static void draw(std::vector positionVector, std::vector heatVector, const FieldDimensions& theFieldDimensions) - { - //MathUtils::stretch(heatVector); - - const int stepSizeX = (int)HeatMap::getStepSizeX(theFieldDimensions); - const int stepSizeY = (int)HeatMap::getStepSizeY(theFieldDimensions); - for (int i = 0; i < HeatMap::CELL_COUNT; i++) - { - const Vector2f position = positionVector.at(i); - const float heat = heatVector.at(i); - - const int alpha = 155; - - RECTANGLE2("module:HeatMapProvider:HeatMap", - Vector2i((int)position.x(), (int)position.y()), - stepSizeX, - stepSizeY, - 0, - 10, - Drawings::noBrush, - ColorRGBA(static_cast(255 - alpha), static_cast(alpha), 0), - Drawings::solidBrush, - ColorRGBA((unsigned char)(255 * (1 - heat)), (unsigned char)(255 * heat), 0, alpha)); - } - } -}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/HysterUtils.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/HysterUtils.h deleted file mode 100644 index 80ce89a3..00000000 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/HysterUtils.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -class HysterUtils // Hysteresis -{ -public: - static bool compareAbsolutes(const bool current, const float trueIfSmall, const float trueIfBig, const float addToOneMultiplier) - { - if (current) - return trueIfSmall < trueIfBig * (1 + addToOneMultiplier); - else - return trueIfSmall * (1 + addToOneMultiplier) < trueIfBig; - } - - static bool comparePossibleNegatives(const bool current, const float trueIfSmall, const float trueIfBig, const float addOrSubtractFromBig) - { - if (current) - return trueIfSmall < trueIfBig + addOrSubtractFromBig; - else - return trueIfSmall < trueIfBig - addOrSubtractFromBig; - } - - static bool comparePossibleNegatives(const bool current, const float trueIfSmall, const float trueIfBig, const float addFalse, const float addTrue) - { - if (current) - return trueIfSmall < trueIfBig + addTrue; - else - return trueIfSmall + addFalse < trueIfBig; - } - - static float makeBigger(const float value, const float addition, const float multiplierOrDivisor) - { - if (value < 0) - return addition + value / multiplierOrDivisor; - else - return addition + value * multiplierOrDivisor; - } -}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/HysteresisUtils.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/HysteresisUtils.h new file mode 100644 index 00000000..b7226f3a --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/HysteresisUtils.h @@ -0,0 +1,37 @@ +#pragma once + +class HysteresisUtils +{ +public: + static bool compareAbsolutes(const bool current, const float returnTrueIfSmall, const float returnTrueIfBig, const float addToOneMultiplier) + { + if (current) + return returnTrueIfSmall < returnTrueIfBig * (1 + addToOneMultiplier); + else + return returnTrueIfSmall * (1 + addToOneMultiplier) < returnTrueIfBig; + } + + static bool comparePossibleNegatives(const bool current, const float returnTrueIfSmall, const float returnTrueIfBig, const float addOrSubtractFromBig) + { + if (current) + return returnTrueIfSmall < returnTrueIfBig + addOrSubtractFromBig; + else + return returnTrueIfSmall < returnTrueIfBig - addOrSubtractFromBig; + } + + static bool comparePossibleNegatives(const bool current, const float returnTrueIfSmall, const float returnTrueIfBig, const float addIfCurrentIsFalse, const float addIfCurrentIsTrue) + { + if (current) + return returnTrueIfSmall < returnTrueIfBig + addIfCurrentIsTrue; + else + return returnTrueIfSmall + addIfCurrentIsFalse < returnTrueIfBig; + } + + static float makeBigger(const float value, const float addition, const float multiplierOrDivisor) + { + if (value < 0) + return addition + value / multiplierOrDivisor; + else + return addition + value * multiplierOrDivisor; + } +}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/KickUtils.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/KickUtils.cpp index 023c928e..7aed02ef 100644 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/KickUtils.cpp +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/KickUtils.cpp @@ -1,30 +1,29 @@ #include "KickUtils.h" -#include -#include -#include "Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/Kick.h" -#include "Representations/Modeling/RobotMap.h" +#include "MathUtils.h" +#include "Modules/BehaviorControl/TacticControl/KicksProvider/Kick.h" #include "Representations/BehaviorControl/BehaviorData.h" #include "Representations/BehaviorControl/GameSymbols.h" -#include "DangerUtils.h" -#include #include "Representations/Configuration/FieldDimensions.h" -#include "MathUtils.h" +#include "Representations/Modeling/RobotMap.h" +#include "Representations/MotionControl/MotionInfo.h" +#include "Representations/Sensing/RobotModel.h" +#include + +Pose2f KickUtils::getKickPose(const Angle& robotRotation, const Vector2f& ballPosition, const bool mirror, const float afterRotation_optDistanceToBallX, const float afterRotation_optDistanceToBallY) +{ + Pose2f pose(robotRotation, ballPosition); + pose = pose.translate(-afterRotation_optDistanceToBallX, (mirror ? +1.f : -1.f) * afterRotation_optDistanceToBallY); + return pose; +} Pose2f KickUtils::getKickPose( - const Vector2f& ballPosition, const Vector2f& targetPosition, const bool rotateLeft, const bool ballLeftFoot, const Angle optAngle, const float afterRotation_optDistanceToBallX, const float afterRotation_optDistanceToBallY) + const Vector2f& ballPosition, const Vector2f& targetPosition, const bool rotateLeft, const bool mirror, const Angle optAngle, const float afterRotation_optDistanceToBallX, const float afterRotation_optDistanceToBallY) { const Angle ballToTargetAngle = (targetPosition - ballPosition).angle(); const Angle field_angleUnnormalized = ballToTargetAngle + (rotateLeft ? 1.f : -1.f) * optAngle; const Angle field_angle = Angle::normalize(field_angleUnnormalized); - return getKickPose(field_angle, ballPosition, ballLeftFoot, afterRotation_optDistanceToBallX, afterRotation_optDistanceToBallY); -} - -Pose2f KickUtils::getKickPose(const Angle& robotRotation, const Vector2f& ballPosition, const bool ballLeftFoot, const float afterRotation_optDistanceToBallX, const float afterRotation_optDistanceToBallY) -{ - Pose2f pose(robotRotation, ballPosition); - pose = pose.translate(-afterRotation_optDistanceToBallX, (ballLeftFoot ? -1.f : +1.f) * afterRotation_optDistanceToBallY); - return pose; + return getKickPose(field_angle, ballPosition, mirror, afterRotation_optDistanceToBallX, afterRotation_optDistanceToBallY); } Angle KickUtils::getFastestReachableKickAngleBetweenTargets(const Vector2f& playerPosition, const Vector2f& ballPosition, const Vector2f& target1, const Vector2f& target2) @@ -45,35 +44,6 @@ Angle KickUtils::getFastestReachableKickAngleBetweenTargets(const Vector2f& play return kickAngleLimit.limit(playerToBallAngle); } -float KickUtils::getKickTime(const Kick& kick, const Pose2f& playerPose, const Vector2f& ballPosition, const Vector2f& targetPosition, const bool hysteresis, const bool leftFootClosestToBall) -{ - const Angle TURN_ANGLE_PER_SECOND = 15_deg; // TODO Constant - const float WALK_MM_PER_SECOND = 150.f; // TODO Constant - const float MAX_DISTANCE = 300.f; - - const Pose2f kickPose = kick.getKickPose(playerPose, ballPosition, targetPosition, leftFootClosestToBall); - - float time = kick.getTime(hysteresis); - - const float mult = MathUtils::clamp_f((Geometry::distance(playerPose.translation, kickPose.translation) - 200) / 200.f, 0.f, 1.f); - - const Angle optAroundBallAngle = (ballPosition - kickPose.translation).angle(); - const Angle currentAroundBallAngle = (ballPosition - playerPose.translation).angle(); - const float aroundBallAngleDiff = MathUtils::getAngleDiff(optAroundBallAngle, currentAroundBallAngle, hysteresis); - time += mult * (aroundBallAngleDiff / TURN_ANGLE_PER_SECOND); - - const Angle optToBallAngle = kickPose.rotation; - const Angle currentToBallAngle = playerPose.rotation; - const float toBallAngleDiff = MathUtils::getAngleDiff(optToBallAngle, currentToBallAngle, hysteresis); - time += (1 - mult) * (toBallAngleDiff / TURN_ANGLE_PER_SECOND); - - const float distance = MathUtils::clamp_f(Geometry::distance(playerPose.translation, kickPose.translation), 0.f, MAX_DISTANCE); - const float walkTime = distance / WALK_MM_PER_SECOND; - time += walkTime; - - return time; -} - bool KickUtils::fulfillsDistanceRequirements(const Kick& kick, const float targetDistance, const DistanceRequirement distanceRequirement, const bool hysteresis) { const int iTargetDistance = (int)targetDistance; @@ -108,35 +78,22 @@ bool KickUtils::fulfillsDistanceRequirements(const Kick& kick, const float targe } } -bool KickUtils::isKickToOutside(const Kick* kick, const Vector2f& ballPosition, const Vector2f& direction, const bool hysteresis, const FieldDimensions& theFieldDimensions) -{ - float kickDistance; - if (kick->isDistanceAdjustable()) - { - kickDistance = kick->getMinDistance(hysteresis, false); // TODO + Inaccuracy ? - } - else - { - kickDistance = kick->getMaxDistance(hysteresis, false); - } - const Vector2f worstCaseTargetPosition = ballPosition + direction * kickDistance; - return !theFieldDimensions.isInsideField(worstCaseTargetPosition); -} - float KickUtils::getMinKickToObstaclesDistance(const Vector2f& ballPosition, const Vector2f& targetPosition, const FieldDimensions& theFieldDimensions, const RobotMap& theRobotMap) { - return std::min(getMinKickToGoalPostDistance(ballPosition, targetPosition, theFieldDimensions), - std::min(getMinRobotToKickDistance(ballPosition, targetPosition, theRobotMap), getMinFieldBorderToKickDistance(ballPosition, targetPosition, theFieldDimensions))); + const float minToGoalPostDistance = getMinKickToGoalPostDistance(ballPosition, targetPosition, theFieldDimensions); + const float minToRobotDistance = getMinRobotToKickDistance(ballPosition, targetPosition, theRobotMap); + const float minToFieldBorder = getMinFieldBorderToKickDistance(ballPosition, targetPosition, theFieldDimensions); + return std::min(minToGoalPostDistance, std::min(minToRobotDistance, minToFieldBorder)); } float KickUtils::getMinKickToGoalPostDistance(const Vector2f& ballPosition, const Vector2f& targetPosition, const FieldDimensions& theFieldDimensions) { const float goalPostRadius = theFieldDimensions.goalPostRadius; - const Vector2f ownLeftGoalPost = {theFieldDimensions.xPosOwnGoalPost, theFieldDimensions.yPosLeftGoal - goalPostRadius}; - const Vector2f ownRightGoalPost = {theFieldDimensions.xPosOwnGoalPost, theFieldDimensions.yPosRightGoal + goalPostRadius}; - const Vector2f opponentLeftGoalPost = {theFieldDimensions.xPosOpponentGoalPost, theFieldDimensions.yPosLeftGoal - goalPostRadius}; - const Vector2f opponentRightGoalPost = {theFieldDimensions.xPosOpponentGoalPost, theFieldDimensions.yPosRightGoal + goalPostRadius}; + const Vector2f ownLeftGoalPost = {theFieldDimensions.xPosOwnGoalPost, theFieldDimensions.yPosLeftGoal}; + const Vector2f ownRightGoalPost = {theFieldDimensions.xPosOwnGoalPost, theFieldDimensions.yPosRightGoal}; + const Vector2f opponentLeftGoalPost = {theFieldDimensions.xPosOpponentGoalPost, theFieldDimensions.yPosLeftGoal}; + const Vector2f opponentRightGoalPost = {theFieldDimensions.xPosOpponentGoalPost, theFieldDimensions.yPosRightGoal}; std::vector goalPostPositions = {}; goalPostPositions.push_back(ownLeftGoalPost); @@ -144,17 +101,18 @@ float KickUtils::getMinKickToGoalPostDistance(const Vector2f& ballPosition, cons goalPostPositions.push_back(opponentLeftGoalPost); goalPostPositions.push_back(opponentRightGoalPost); - return getMinDistance(ballPosition, targetPosition, goalPostPositions); + return getMinDistance(ballPosition, targetPosition, goalPostPositions) - goalPostRadius; } float KickUtils::getMinRobotToKickDistance(const Vector2f& ballPosition, const Vector2f& targetPosition, const RobotMap& theRobotMap) { + const float ROBOT_RADIUS = 140.f / 2.f; std::vector robotPositions = {}; for (auto& robot : theRobotMap.robots) { robotPositions.push_back(robot.pose.translation); } - return getMinDistance(ballPosition, targetPosition, robotPositions); + return getMinDistance(ballPosition, targetPosition, robotPositions) - ROBOT_RADIUS; } float KickUtils::getMinDistance(const Vector2f& ballPosition, const Vector2f& targetPosition, const std::vector& obstacles) @@ -190,16 +148,22 @@ float KickUtils::getMinDistance(const Vector2f& ballPosition, const Vector2f& ta float KickUtils::getMinFieldBorderToKickDistance(const Vector2f& ballPosition, const Vector2f& targetPosition, const FieldDimensions& theFieldDimensions) { - const Vector2f oppLeftGoalPost = {theFieldDimensions.xPosOpponentGoalPost, theFieldDimensions.yPosLeftGoal}; - const Vector2f oppRightGoalPost = {theFieldDimensions.xPosOpponentGoalPost, theFieldDimensions.yPosRightGoal}; - bool inOpponentGoal = Geometry::checkIntersectionOfLines(ballPosition, targetPosition, oppLeftGoalPost, oppRightGoalPost); - if (inOpponentGoal) + if (FieldUtils::isIntoOpponentsGoal(ballPosition, targetPosition, theFieldDimensions)) + { + return std::numeric_limits::max(); + } + + const float targetPositionX = targetPosition.x(); + const float absTargetPositionY = std::abs(targetPosition.y()); + + // Is onto opponents goal line + if (targetPositionX > 0.f && targetPositionX < theFieldDimensions.xPosOpponentGroundline + 50.f && absTargetPositionY < theFieldDimensions.yPosLeftGoal) { - return std::numeric_limits::infinity(); + return std::numeric_limits::max(); } - const float yToSides = std::max(0.f, theFieldDimensions.yPosLeftSideline - std::abs(targetPosition.y())); - const float xToGroundLines = std::max(0.f, theFieldDimensions.xPosOpponentGroundline - std::abs(targetPosition.x())); + const float yToSides = std::max(0.f, theFieldDimensions.yPosLeftSideline - absTargetPositionY); + const float xToGroundLines = std::max(0.f, theFieldDimensions.xPosOpponentGroundline - std::abs(targetPositionX)); return std::min(yToSides, xToGroundLines); } @@ -317,3 +281,53 @@ std::vector KickUtils::unpack(const std::vector>& k } return unpackedKicks; } + +std::vector KickUtils::unpack(const std::vector>& kicks1, const std::vector>& kicks2) +{ + std::vector unpackedKicks; + for (const auto& kickUniquePointer : kicks1) + { + Kick* kick = kickUniquePointer.get(); + unpackedKicks.push_back(kick); + } + for (const auto& kickUniquePointer : kicks2) + { + Kick* kick = kickUniquePointer.get(); + unpackedKicks.push_back(kick); + } + return unpackedKicks; +} + +/** + * Use robot model's foot positions intersecting with best hypothesis + */ +bool KickUtils::isBallTouched(const Vector2f& relativeBallPosition, const RobotModel& theRobotModel, const FieldDimensions& theFieldDimensions) +{ + const Pose3f& kickFoot = (theRobotModel.soleLeft.translation.z() > theRobotModel.soleRight.translation.z()) ? theRobotModel.soleLeft : theRobotModel.soleRight; + + const float footCenterYToBallDistance = relativeBallPosition.y() - kickFoot.translation.y(); + + const float footCenterXToBallDistance = relativeBallPosition.x() - kickFoot.translation.x(); + const float footFrontXToBallDistance = footCenterXToBallDistance - 104.f; + + /* + * The values used for calculation are fluctuating heavily. To avoid false + * positives and because false negatives can be tolerated the ballRadius is + * reduced + */ + const float saferBallRadius = theFieldDimensions.ballRadius - 20.f; + + const bool ballTouched = std::abs(footCenterYToBallDistance) < saferBallRadius && std::abs(footFrontXToBallDistance) < saferBallRadius; + + return ballTouched; +} + +/** + * Two ways to trigger a kick are handled here: kick engine or in walk kick + */ +bool KickUtils::isBallKicked(const MotionInfo& theMotionInfo) +{ + const bool customStepKickStartedAndCantGetInterrupted = theMotionInfo.motion == MotionRequest::Motion::walk && theMotionInfo.walkKicking; + const bool kickEngineKickStartedAndCantGetInterrupted = theMotionInfo.motion == MotionRequest::kick; + return customStepKickStartedAndCantGetInterrupted || kickEngineKickStartedAndCantGetInterrupted; +} diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/KickUtils.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/KickUtils.h index 602fa347..d3441042 100644 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/KickUtils.h +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/KickUtils.h @@ -1,19 +1,20 @@ #pragma once -#include -#include -#include "Modules/BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/Kick.h" +#include "FieldUtils.h" +#include "Modules/BehaviorControl/TacticControl/KicksProvider/Kick.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Logs/KickDrawings.h" #include "Representations/Modeling/RobotMap.h" +#include "Representations/MotionControl/MotionInfo.h" +#include "Representations/Sensing/RobotModel.h" +#include class KickUtils { public: - static Pose2f getKickPose(const Angle& robotRotation, const Vector2f& ballPosition, bool kickWithLeft, float afterRotation_optDistanceToBallX, float afterRotation_optDistanceToBallY); - static Angle getFastestReachableKickAngleBetweenTargets(const Vector2f& playerPosition, const Vector2f& ballPosition, const Vector2f& target1, const Vector2f& target2); - static float getKickTime(const Kick& kick, const Pose2f& playerPose, const Vector2f& ballPosition, const Vector2f& targetPosition, bool hysteresis, bool leftFootClosestToBall); + static float getKickTime(const Kick& kick, const Pose2f& playerPose, const Vector2f& ballPosition, const Pose2f& kickPose); static bool fulfillsDistanceRequirements(const Kick& kick, float targetDistance, DistanceRequirement distanceRequirement, bool hysteresis); @@ -21,8 +22,6 @@ class KickUtils static std::tuple getLeftAndRightTarget(const Vector2f& position, const Vector2f& target1, const Vector2f& target2); - static bool isKickToOutside(const Kick* kick, const Vector2f& ballPosition, const Vector2f& direction, bool hysteresis, const FieldDimensions& theFieldDimensions); - static std::vector getTargetsWithEvenDistance(const Vector2f& target1, const Vector2f& target2, int stepSize); static std::vector getTargetsWithEvenAngle(const Vector2f& ballPosition, const Vector2f& target1, const Vector2f& target2); @@ -35,22 +34,17 @@ class KickUtils static float getMinFieldBorderToKickDistance(const Vector2f& ballPosition, const Vector2f& targetPosition, const FieldDimensions& theFieldDimensions); - static Pose2f getKickPose(const Vector2f& ballPosition, const Vector2f& targetPosition, bool rotateLeft, bool ballLeftFoot, Angle optAngle, float afterRotation_optDistanceToBallX, float afterRotation_optDistanceToBallY); + static bool isLeftOfKickLine(const Vector2f& playerPosition, const Vector2f& ballPosition, const Vector2f& targetPosition, bool currentIsLeftOfLine); + + static Pose2f getKickPose(const Angle& robotRotation, const Vector2f& ballPosition, bool mirror, float afterRotation_optDistanceToBallX, float afterRotation_optDistanceToBallY); + + static Pose2f getKickPose(const Vector2f& ballPosition, const Vector2f& targetPosition, bool rotateLeft, bool mirror, Angle optAngle, float afterRotation_optDistanceToBallX, float afterRotation_optDistanceToBallY); static std::vector unpack(const std::vector>& kicks); + static std::vector unpack(const std::vector>& kicks1, const std::vector>& kicks2); - static void drawKickRange(const Vector2f& ballPosition, const Geometry::Line& line, const int minAhead, const int maxAhead) - { - const Vector2f& target1 = line.base + line.direction * minAhead; - const Vector2f& target2 = line.base + line.direction * maxAhead; - drawKickInfos(ballPosition, target1, target2); - } - - static void drawKickInfos(const Vector2f& ballPosition, const Vector2f& target1, const Vector2f& target2) - { - LINE("behavior:BallchaserProvider:KickManager:KickRange", ballPosition.x(), ballPosition.y(), target1.x(), target1.y(), 20, Drawings::solidPen, ColorRGBA::blue); - LINE("behavior:BallchaserProvider:KickManager:KickRange", ballPosition.x(), ballPosition.y(), target2.x(), target2.y(), 20, Drawings::solidPen, ColorRGBA::blue); - } + static bool isBallTouched(const Vector2f& relativeBallPosition, const RobotModel& theRobotModel, const FieldDimensions& theFieldDimensions); + static bool isBallKicked(const MotionInfo& theMotionInfo); private: static float getMinDistance(const Vector2f& ballPosition, const Vector2f& targetPosition, const std::vector& obstacles); diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/MathUtils.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/MathUtils.h index 969e7b55..c02061e5 100644 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/MathUtils.h +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/MathUtils.h @@ -34,11 +34,43 @@ class MathUtils } } + inline static Vector2f angleToNormalizedVector(const Angle angle) + { + const Vector2f vector = angleToVector(angle); + if (angle < 0) + { + return -vector; + } + else + { + return vector; + } + } + /** * @return a vector that is not normalized */ inline static Vector2f angleToVector(const Angle angle) { return {(float)std::cos(angle), (float)std::sin(angle)}; } + /** + * Assumes every angle is normalized and that the difference is a maximum of 180_deg + */ + inline static bool isBetweenAngles(Angle angle, Angle leftAngle, Angle rightAngle) + { + ASSERT(angle > -181_deg); + ASSERT(angle < 181_deg); + ASSERT(leftAngle > -181_deg); + ASSERT(leftAngle < 181_deg); + ASSERT(rightAngle > -181_deg); + ASSERT(rightAngle < 181_deg); + ASSERT(getAngleSmallestDiff(leftAngle, rightAngle) < 181_deg); + if (leftAngle > rightAngle) + { + return leftAngle > angle && angle > rightAngle; + } + return angle < leftAngle || angle > rightAngle; + } + inline static Angle getLeftAngle(Angle angle1, Angle angle2) { angle1 = angle1.normalize(); @@ -59,9 +91,90 @@ class MathUtils return angle1 > angle2 ? angle1 : angle2; } - inline static float getAngleDiff(const Angle& angle1, const Angle& angle2, const bool hysteresis) + inline static std::tuple getLeftAndRightAngle(Angle angle1, Angle angle2) + { + angle1 = angle1.normalize(); + angle2 = angle2.normalize(); + + const Angle angleDiff = std::fabs(Angle(angle1 - angle2)); + + if (angle1 > 0 && angle2 < 0) + { + if (angleDiff < 180_deg) + { + return {angle1, angle2}; + } + else + { + return {angle2, angle1}; + } + } + + if (angle2 > 0 && angle1 < 0) + { + if (angleDiff < 180_deg) + { + return {angle2, angle1}; + } + else + { + return {angle1, angle2}; + } + } + + if (angle1 > angle2) + { + return {angle1, angle2}; + } + else + { + return {angle2, angle1}; + } + } + + inline static Angle getLargerMinusSmallerAngleDiff(const Angle& angle1, const Angle& angle2) + { + Angle smallValue; + Angle largeValue; + if (angle1 < angle2) + { + smallValue = angle1; + largeValue = angle2; + } + else + { + smallValue = angle2; + largeValue = angle1; + } + return largeValue - smallValue; + } + + inline static Angle getAngleSmallestDiff(const Angle& angle1, const Angle& angle2) + { + const Angle largerMinusSmallerAngleDiff = getLargerMinusSmallerAngleDiff(angle1, angle2); + if (largerMinusSmallerAngleDiff > 180_deg) + { + return 360_deg - largerMinusSmallerAngleDiff; + } + return largerMinusSmallerAngleDiff; + } + + inline static Angle getLeftToRightAngleDiff(const Angle& leftAngle, const Angle& rightAngle) + { + if (leftAngle < rightAngle) + { + return rightAngle - leftAngle; + } + return 360_deg - (leftAngle - rightAngle); + } + + inline static float getCloseToMiddlePercent(const Angle angle, const Angle rangeLeftAngle, const Angle rangeRightAngle) { - return std::fabs(Angle::normalize(angle1 - angle2)) * (hysteresis ? 0.9f : 1.f); + const Angle coneDiff = MathUtils::getLeftToRightAngleDiff(rangeLeftAngle, rangeRightAngle); // todo calculate in cone once + const Angle halfConeDiff = coneDiff / 2; + const Angle middle = Angle(rangeLeftAngle - halfConeDiff).normalize(); + const Angle kickAngleToMiddleDiff = MathUtils::getAngleSmallestDiff(angle, middle); + return std::max(0.f, 1.f - kickAngleToMiddleDiff / halfConeDiff); } inline static bool isEqual(const float f1, const float f2) { return std::fabs(f1 - f2) < std::numeric_limits::epsilon(); } diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/PassUtils.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/PassUtils.h deleted file mode 100644 index a4fb1403..00000000 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/PassUtils.h +++ /dev/null @@ -1,115 +0,0 @@ -#pragma once - -#include -#include "Representations/Modeling/RobotMap.h" -#include "Representations/BehaviorControl/BehaviorData.h" -#include "Representations/Infrastructure/TeammateData.h" -#include "Representations/BehaviorControl/GameSymbols.h" -#include "Representations/BehaviorControl/BehaviorConfiguration.h" -#include "Representations/Configuration/FieldDimensions.h" -#include "FieldUtils.h" - -class PassUtils -{ - -public: - /** - * @param passPosition Position of the player who shall receive the pass - */ - static std::tuple getPassInfo( - const Vector2f& ballPosition, const Vector2f& passTarget, const bool kickWithLeft, const BehaviorConfiguration& theBehaviorConfiguration, const FieldDimensions& theFieldDimensions, const RobotPoseAfterPreview& theRobotPoseAfterPreview) - { - // TODO: Check free space / kick corridor / team mate target position - // TODO: check stability - - const Vector2f goalCenter = FieldUtils::getOpponentGoalCenter(theFieldDimensions); - const Vector2f passTargetToGoal{goalCenter - passTarget}; - const Vector2f kickTarget{passTarget + passTargetToGoal.normalized(400.f)}; //TODO Constant - - const Vector2f minPassTarget{kickTarget - passTargetToGoal.normalized(200.f)}; //TODO Constant - const Vector2f maxPassTarget{kickTarget + passTargetToGoal.normalized(200.f)}; //TODO Constant - const Angle minPassTargetToBallAngle = (minPassTarget - ballPosition).angle(); - const Angle maxPassTargetToBallAngle = (maxPassTarget - ballPosition).angle(); - const Rangef kickAngleLimit(std::min(minPassTargetToBallAngle, maxPassTargetToBallAngle), std::max(minPassTargetToBallAngle, maxPassTargetToBallAngle)); - - const Pose2f kickFootPose = {}; //todo theRobotPoseAfterPreview * Vector2f{ 0.f, (kickWithLeft ? +1.f : -1.f) * theBehaviorConfiguration.optDistanceToBallY }; - Angle kickTargetAngle = (ballPosition - kickFootPose.translation).angle(); - kickTargetAngle = kickAngleLimit.limit(kickTargetAngle); // TODO Isnt the result always the kickTargetAngle? - - return {kickFootPose, kickTarget, kickTargetAngle}; - } - - static std::tuple getPassInfo(const Vector2f& ballPosition, - const Vector2f& passTarget, - const bool kickWithLeft, - const float optXDistanceToBall, - const float optYDistanceToBall, - const BehaviorConfiguration& theBehaviorConfiguration, - const FieldDimensions& theFieldDimensions, - const RobotPoseAfterPreview& theRobotPoseAfterPreview) - { - auto [kickFootPose, kickTarget, kickTargetAngle] = getPassInfo(ballPosition, passTarget, kickWithLeft, theBehaviorConfiguration, theFieldDimensions, theRobotPoseAfterPreview); - Pose2f kickPose = getKickPose(ballPosition, kickTargetAngle, kickWithLeft, optXDistanceToBall, optYDistanceToBall); - return {kickPose, kickTarget}; - } - - static Pose2f getKickPose(const Vector2f& ballPosition, const Angle kickAngle, const bool kickWithLeft, const float optXDistanceToBall, const float optYDistanceToBall) - { - Pose2f kickPose = Pose2f(kickAngle, ballPosition); - kickPose.translate(-optXDistanceToBall, (kickWithLeft ? -1.f : +1.f) * optYDistanceToBall); - return kickPose; - } - - /** - * @return a pass targetPosition if possible, or leaves the vector as is otherwise - */ - static bool calcPassTarget(Vector2f& targetPosition, - const float& maxKickDistance, - const float& kickoffPassXOffset, - const GameSymbols& theGameSymbols, - const RobotMap& theRobotMap, - const RobotPoseAfterPreview& theRobotPoseAfterPreview, - const TeammateData& theTeammateData) - { - // search for good positioned team mate - bool targetFound = false; - for (auto& mate : theTeammateData.teammates) - { - const bool ownKickOff = theGameSymbols.kickoffInProgress && theGameSymbols.ownKickOff; - const float safeXPosition = std::min(0.f, theRobotPoseAfterPreview.translation.x()) - (ownKickOff ? 500.f : 0.f); - const bool matePositionSafe = mate.pose.translation.x() > safeXPosition; - const bool isKeeper = mate.behaviorData.role == BehaviorData::keeper || mate.behaviorData.role == BehaviorData::replacementKeeper; - const bool positionReachable = (mate.pose.translation - theRobotPoseAfterPreview.translation).norm() < maxKickDistance; - - if (!isKeeper && mate.status == Teammate::FULLY_ACTIVE && mate.pose.validity > 0.6 && matePositionSafe && positionReachable) - { - Vector2f toTarget(mate.pose.translation - theRobotPoseAfterPreview.translation); - float angleToTarget = toTarget.angle(); - float distanceToTarget = toTarget.norm(); - bool foundObstacle = false; - for (auto& robot : theRobotMap.robots) - { - Vector2f toRobot(robot.pose.translation - theRobotPoseAfterPreview.translation); - float angleToRobot = toRobot.angle(); - float distanceToRobot = toRobot.norm(); - if ((distanceToTarget < distanceToRobot - 500) && std::abs(Angle::normalize(angleToTarget - angleToRobot)) < 30_deg) - { - foundObstacle = true; - break; - } - } - if (!foundObstacle && (!targetFound || targetPosition.x() < mate.pose.translation.x())) - { - targetPosition = mate.pose.translation; - targetFound = true; - } - } - } - // no good pass targetPosition, do not touch original targetPosition! - - if (targetFound && theGameSymbols.kickoffInProgress) - targetPosition.x() += kickoffPassXOffset; - - return targetFound; - } -}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/PathUtils.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/PathUtils.h new file mode 100644 index 00000000..587158ea --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/PathUtils.h @@ -0,0 +1,77 @@ +#pragma once + +#include "Tools/Math/Angle.h" +#include "MathUtils.h" +#include "Tools/Math/Geometry.h" + +class PathUtils +{ + +public: + static float getPathTime(const Pose2f& currentPose, const Pose2f& targetPose, const Vector2f& ballPosition) + { + const Angle IF_HIGHER_WALK_CURVE_ANGLE = 90_deg; + const Angle MAX_WALK_CURVE_ANGLE = 180_deg; + + const Angle currentToBallAngle = Angle::normalize((ballPosition - currentPose.translation).angle()); + const Angle ballToTargetAngle = Angle::normalize((ballPosition - targetPose.translation).angle()); + const Angle aroundBallAngleDiff = MathUtils::getAngleSmallestDiff(currentToBallAngle, ballToTargetAngle); + + const float aroundBallFactor = MathUtils::clamp_f((aroundBallAngleDiff - IF_HIGHER_WALK_CURVE_ANGLE) / (MAX_WALK_CURVE_ANGLE - IF_HIGHER_WALK_CURVE_ANGLE), 0.f, 1.f); + + const float turnTime = getTurnTime(currentPose, targetPose, aroundBallFactor); + const float distanceTime = getDistanceTime(currentPose.translation, targetPose.translation, ballPosition, aroundBallFactor); + + /* + const float t = turnTime + distanceTime; + std::ostringstream s; + s << turnTime << ", " << distanceTime << ", " << t; + std::string var = s.str(); + OUTPUT_TEXT(var); + */ + + return turnTime + distanceTime; + } + +private: + static float getTurnTime(const Pose2f& currentPose, const Pose2f& targetPose, const float aroundBallFactor) + { + const Angle TURN_ANGLE_PER_SECOND = 25_deg; // TODO Constants + const float MIN_TURN_TO_WALK_DISTANCE = 300.f; + const float MAX_TURN_TO_WALK_DISTANCE = 500.f; + + const float maxTurnTime = 180_deg / TURN_ANGLE_PER_SECOND; + if (aroundBallFactor > 0.99f) + { + return maxTurnTime; + } + + const float turnMultiplier = MathUtils::clamp_f( + (Geometry::distance(currentPose.translation, targetPose.translation) - MIN_TURN_TO_WALK_DISTANCE) / (MAX_TURN_TO_WALK_DISTANCE - MIN_TURN_TO_WALK_DISTANCE), 0.f, 1.f); + + // Turn and walk. Don't calculate the time it takes to turn to walk because it's almost the same for every pose + const Angle walkAngle = (targetPose.translation - currentPose.translation).angle(); + const Angle fromWalk_toTarget_angleDiff = MathUtils::getLargerMinusSmallerAngleDiff(walkAngle, targetPose.rotation); + const float fromWalk_toTarget_time = fromWalk_toTarget_angleDiff / TURN_ANGLE_PER_SECOND; + + // Close enough to just walk (no need to turn since its so close the robot will walk sideways or backward) + const Angle fromCurrent_toTarget_angleDiff = MathUtils::getLargerMinusSmallerAngleDiff(currentPose.rotation, targetPose.rotation); + const float fromCurrent_toTarget_time = fromCurrent_toTarget_angleDiff / TURN_ANGLE_PER_SECOND; + + float time = turnMultiplier * fromWalk_toTarget_time + (1 - turnMultiplier) * fromCurrent_toTarget_time; + + return (1 - aroundBallFactor) * time + aroundBallFactor * maxTurnTime; + } + + static float getDistanceTime(const Vector2f& playerCurrentPosition, const Vector2f& playerTargetPosition, const Vector2f& ballPosition, const float aroundBallFactor) + { + const float MAX_AIR_DISTANCE = 500.f; + const float MAX_CURVE_DISTANCE = 200.f; + const float WALK_MM_PER_SECOND = 150.f; // TODO Constant + + const float airDistance = Geometry::distance(playerCurrentPosition, playerTargetPosition); + const float clampedAirDistance = std::min(airDistance, MAX_AIR_DISTANCE); + const float clampedCurvedDistance = aroundBallFactor * MAX_CURVE_DISTANCE; + return (clampedAirDistance + clampedCurvedDistance) / WALK_MM_PER_SECOND; + } +}; \ No newline at end of file diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/PositionUtils.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/PositionUtils.cpp deleted file mode 100644 index ba45ec00..00000000 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/PositionUtils.cpp +++ /dev/null @@ -1,81 +0,0 @@ -#include "PositionUtils.h" - -Angle PositionUtils::getTurnRobotByAngle(const Pose2f& robotPose, const Vector2f& targetPosition) -{ - return Transformation::fieldToRobot(robotPose, targetPosition).angle(); -} - -Pose2f PositionUtils::getCurrentKickFootPose(const bool kickWithLeft, const BehaviorConfiguration& theBehaviorConfiguration, const RobotPoseAfterPreview& theRobotPoseAfterPreview) -{ - return {theRobotPoseAfterPreview.rotation, theRobotPoseAfterPreview * Vector2f{0.f, (kickWithLeft ? +1.f : -1.f) * theBehaviorConfiguration.optDistanceToBallY}}; -} - -Angle PositionUtils::getTurnToBallAngle(const Vector2f& ownPosition, const BallSymbols& theBallSymbols) -{ - return getTurnToPositionAngle(ownPosition, theBallSymbols.ballPositionFieldPredicted); -} - -Angle PositionUtils::getTurnToPositionAngle(const Vector2f& ownPosition, const Vector2f& turnToPosition) -{ - return (turnToPosition - ownPosition).angle(); -} - -Vector2f PositionUtils::getPosition(const bool& xOnEnemySide, const float& x, const bool& yOnLeftSide, const float& y) -{ - return Vector2f(xOnEnemySide ? x : -x, yOnLeftSide ? y : -y); -} - -Vector2f PositionUtils::getPosition(const float& x, const bool& yOnLeftSide, const float& y) -{ - return getPosition(true, x, yOnLeftSide, y); -} - -Vector2f PositionUtils::getPosition(const bool& xOnEnemySide, const float& x, const float& y) -{ - return getPosition(xOnEnemySide, x, true, y); -} - -bool PositionUtils::isPathBlocked(const Vector2f& startPosition, const Vector2f& targetPosition, const Vector2f& blockedPosition, const float& blockDistance) -{ - const Vector2f startToTargetVector = startPosition - targetPosition; - const float fromPathToBlockedPositionDistance = Geometry::getDistanceToEdge(Geometry::Line(targetPosition, startToTargetVector), blockedPosition); - return fromPathToBlockedPositionDistance < blockDistance; -} - -// =================================================================================================================== - -void PositionUtils::turnTowardsBall(PositioningSymbols& positioningSymbols, const BallSymbols& theBallSymbols) -{ - turnToPosition(positioningSymbols, theBallSymbols.ballPositionFieldPredicted); -} - -void PositionUtils::turnToPosition(PositioningSymbols& positioningSymbols, const Vector2f& turnToPosition) -{ - positioningSymbols.optPosition.rotation = getTurnToPositionAngle(positioningSymbols.optPosition.translation, turnToPosition); -} - -void PositionUtils::setPosition(PositioningSymbols& positioningSymbols, const bool& xOnEnemySide, const float& x, const bool& yOnLeftSide, const float& y) -{ - positioningSymbols.optPosition.translation = getPosition(xOnEnemySide, x, yOnLeftSide, y); -} - -void PositionUtils::setPosition(PositioningSymbols& positioningSymbols, const float& x, const bool& yOnLeftSide, const float& y) -{ - setPosition(positioningSymbols, true, x, yOnLeftSide, y); -} - -void PositionUtils::setPosition(PositioningSymbols& positioningSymbols, const bool& xOnEnemySide, const float& x, const float& y) -{ - setPosition(positioningSymbols, xOnEnemySide, x, true, y); -} - -void PositionUtils::setPosition(PositioningSymbols& positioningSymbols, const float& x, const float& y) -{ - positioningSymbols.optPosition.translation.x() = x; - positioningSymbols.optPosition.translation.y() = y; -} - -void PositionUtils::setPosition(PositioningSymbols& positioningSymbols, const Vector2f& pos) -{ - positioningSymbols.optPosition.translation = pos; -} diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/PositionUtils.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/PositionUtils.h index e7b750bc..be5f0e8a 100644 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/PositionUtils.h +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/PositionUtils.h @@ -1,8 +1,8 @@ #pragma once #include -#include #include +#include #include class PositionUtils @@ -16,13 +16,13 @@ class PositionUtils static Angle getTurnToPositionAngle(const Vector2f& ownPosition, const Vector2f& turnToPosition); - static Vector2f getPosition(const bool& xOnEnemySide, const float& x, const bool& yOnLeftSide, const float& y); + static Vector2f getPosition(const bool xOnEnemySide, const float x, const bool yOnLeftSide, const float y); - static Vector2f getPosition(const float& x, const bool& yOnLeftSide, const float& y); + static Vector2f getPosition(const float x, const bool yOnLeftSide, const float y); - static Vector2f getPosition(const bool& xOnEnemySide, const float& x, const float& y); + static Vector2f getPosition(const bool xOnEnemySide, const float x, const float y); - static bool isPathBlocked(const Vector2f& startPosition, const Vector2f& targetPosition, const Vector2f& blockedPosition, const float& blockDistance); + static bool isPathBlocked(const Vector2f& startPosition, const Vector2f& targetPosition, const Vector2f& blockedPosition, const float blockDistance); // =================================================================================================================== @@ -30,13 +30,93 @@ class PositionUtils static void turnToPosition(PositioningSymbols& positioningSymbols, const Vector2f& turnToPosition); - static void setPosition(PositioningSymbols& positioningSymbols, const bool& xOnEnemySide, const float& x, const bool& yOnLeftSide, const float& y); + static void setPosition(PositioningSymbols& positioningSymbols, const bool xOnEnemySide, const float x, const bool yOnLeftSide, const float y); - static void setPosition(PositioningSymbols& positioningSymbols, const float& x, const bool& yOnLeftSide, const float& y); + static void setPosition(PositioningSymbols& positioningSymbols, const float x, const bool yOnLeftSide, const float y); - static void setPosition(PositioningSymbols& positioningSymbols, const bool& xOnEnemySide, const float& x, const float& y); + static void setPosition(PositioningSymbols& positioningSymbols, const bool xOnEnemySide, const float x, const float y); - static void setPosition(PositioningSymbols& positioningSymbols, const float& x, const float& y); + static void setPosition(PositioningSymbols& positioningSymbols, const float x, const float y); static void setPosition(PositioningSymbols& positioningSymbols, const Vector2f& pos); }; + +inline Angle PositionUtils::getTurnRobotByAngle(const Pose2f& robotPose, const Vector2f& targetPosition) +{ + return Angle::normalize((targetPosition - robotPose.translation).angle() - robotPose.rotation); +} + +inline Pose2f PositionUtils::getCurrentKickFootPose(const bool kickWithLeft, const BehaviorConfiguration& theBehaviorConfiguration, const RobotPoseAfterPreview& theRobotPoseAfterPreview) +{ + return Pose2f(theRobotPoseAfterPreview.rotation, theRobotPoseAfterPreview * Vector2f{0.f, (kickWithLeft ? +1.f : -1.f) * theBehaviorConfiguration.optDistanceToBallY}); +} + +inline Angle PositionUtils::getTurnToBallAngle(const Vector2f& ownPosition, const BallSymbols& theBallSymbols) +{ + return getTurnToPositionAngle(ownPosition, theBallSymbols.ballPositionFieldPredicted); +} + +inline Angle PositionUtils::getTurnToPositionAngle(const Vector2f& ownPosition, const Vector2f& turnToPosition) +{ + return (turnToPosition - ownPosition).angle(); +} + +inline Vector2f PositionUtils::getPosition(const bool xOnEnemySide, const float x, const bool yOnLeftSide, const float y) +{ + return Vector2f(xOnEnemySide ? x : -x, yOnLeftSide ? y : -y); +} + +inline Vector2f PositionUtils::getPosition(const float x, const bool yOnLeftSide, const float y) +{ + return getPosition(true, x, yOnLeftSide, y); +} + +inline Vector2f PositionUtils::getPosition(const bool xOnEnemySide, const float x, const float y) +{ + return getPosition(xOnEnemySide, x, true, y); +} + +inline bool PositionUtils::isPathBlocked(const Vector2f& startPosition, const Vector2f& targetPosition, const Vector2f& blockedPosition, const float blockDistance) +{ + const Vector2f startToTargetVector = startPosition - targetPosition; + const float fromPathToBlockedPositionDistance = Geometry::getDistanceToEdge(Geometry::Line(targetPosition, startToTargetVector), blockedPosition); + return fromPathToBlockedPositionDistance < blockDistance; +} + +// =================================================================================================================== + +inline void PositionUtils::turnTowardsBall(PositioningSymbols& positioningSymbols, const BallSymbols& theBallSymbols) +{ + turnToPosition(positioningSymbols, theBallSymbols.ballPositionField); +} + +inline void PositionUtils::turnToPosition(PositioningSymbols& positioningSymbols, const Vector2f& turnToPosition) +{ + positioningSymbols.optPosition.rotation = getTurnToPositionAngle(positioningSymbols.optPosition.translation, turnToPosition); +} + +inline void PositionUtils::setPosition(PositioningSymbols& positioningSymbols, const bool xOnEnemySide, const float x, const bool yOnLeftSide, const float y) +{ + positioningSymbols.optPosition.translation = getPosition(xOnEnemySide, x, yOnLeftSide, y); +} + +inline void PositionUtils::setPosition(PositioningSymbols& positioningSymbols, const float x, const bool yOnLeftSide, const float y) +{ + setPosition(positioningSymbols, true, x, yOnLeftSide, y); +} + +inline void PositionUtils::setPosition(PositioningSymbols& positioningSymbols, const bool xOnEnemySide, const float x, const float y) +{ + setPosition(positioningSymbols, xOnEnemySide, x, true, y); +} + +inline void PositionUtils::setPosition(PositioningSymbols& positioningSymbols, const float x, const float y) +{ + positioningSymbols.optPosition.translation.x() = x; + positioningSymbols.optPosition.translation.y() = y; +} + +inline void PositionUtils::setPosition(PositioningSymbols& positioningSymbols, const Vector2f& pos) +{ + positioningSymbols.optPosition.translation = pos; +} diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/TacticUtils.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/TacticUtils.h new file mode 100644 index 00000000..ef8ca5f8 --- /dev/null +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/TacticUtils.h @@ -0,0 +1,46 @@ +#pragma once + +#include "MathUtils.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Ranges/Cone.h" +#include "Modules/BehaviorControl/TacticControl/RoleProvider/Utils/FieldUtils.h" +#include "Tools/Math/Angle.h" +#include "Tools/Math/Geometry.h" + +class TacticUtils +{ + +public: + [[nodiscard]] static Cone getDefenseCone(const Angle defenseWidth, const Vector2f& ballPosition, const FieldDimensions& theFieldDimensions) + { + ASSERT(0_deg < defenseWidth && defenseWidth < 360_deg); + + const Angle step = defenseWidth / 2; + Angle leftAngle; + Angle rightAngle; + + const bool ballOnOwnSide = ballPosition.x() < 0.f; + if (ballOnOwnSide) + { + const Vector2f ownGoalCenter = FieldUtils::getOwnGoalCenter(theFieldDimensions); + const Angle ownGoalCenterAngle = (ownGoalCenter - ballPosition).angle(); + leftAngle = Angle(ownGoalCenterAngle + step).normalize(); + rightAngle = Angle(ownGoalCenterAngle - step).normalize(); + + const Vector2f ownLeftGoalPost = {theFieldDimensions.xPosOwnGroundline, theFieldDimensions.yPosLeftGoal}; + const Vector2f ownRightGoalPost = {theFieldDimensions.xPosOwnGroundline, theFieldDimensions.yPosRightGoal}; + const Angle ownLeftGoalPostAngle = (ownLeftGoalPost - ballPosition).angle(); + const Angle ownRightGoalPostAngle = (ownRightGoalPost - ballPosition).angle(); + const auto [ll, lr] = MathUtils::getLeftAndRightAngle(leftAngle, ownRightGoalPostAngle); + const auto [rl, rr] = MathUtils::getLeftAndRightAngle(rightAngle, ownLeftGoalPostAngle); + leftAngle = ll; + rightAngle = rr; + } + else + { + rightAngle = 180_deg - step; + leftAngle = -180_deg + step; + } + + return {leftAngle, rightAngle}; + } +}; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/TeamUtils.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/TeamUtils.h index 5a91f597..5adc1743 100644 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/TeamUtils.h +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/TeamUtils.h @@ -9,68 +9,39 @@ class TeamUtils public: static std::optional getRealBallChaserPosition(const BallChaserDecision& theBallChaserDecision, const TeammateData& theTeammateData) { - Vector2f realBallChaserPosition; - bool foundBallchaser = false; - for (auto& mate : theTeammateData.teammates) - { - if (mate.number == theBallChaserDecision.playerNumberToBall) - { - realBallChaserPosition = mate.pose.translation; - foundBallchaser = true; - break; - } - } - if (foundBallchaser) + if (theTeammateData.myself.playerNumber == theBallChaserDecision.playerNumberToBall) { - return std::optional(realBallChaserPosition); + return theTeammateData.myself.robotPose.translation; } - else - { - return std::optional(); // TODO. Maybe use as backup? theBallchaser.optPosition.translation; - } - } - - static std::optional getTeammatePosition(const TeammateData& theTeammateData, BehaviorData::RoleAssignment role) - { - Vector2f teammatePosition; - bool foundTeammate = false; for (auto& mate : theTeammateData.teammates) { - if (mate.behaviorData.role == role) + if (mate.playerNumber == theBallChaserDecision.playerNumberToBall) { - teammatePosition = mate.pose.translation; - foundTeammate = true; - break; + return mate.robotPose.translation; } } - if (foundTeammate) - { - return std::optional(teammatePosition); - } - else - { - return std::optional(); - } + + return {}; } - static std::optional getDesiredTeammatePosition(const TeammateData& theTeammateData, BehaviorData::RoleAssignment role) + static std::optional getTeammatePosition(const TeammateData& theTeammateData, BehaviorData::RoleAssignment role) { - Vector2f desiredPosition; + Vector2f teammatePosition; bool foundTeammate = false; for (auto& mate : theTeammateData.teammates) { if (mate.behaviorData.role == role) { - desiredPosition = mate.walkRequest.request.translation; + teammatePosition = mate.robotPose.translation; foundTeammate = true; break; } } if (foundTeammate) { - return std::optional(desiredPosition); + return std::optional(teammatePosition); } else { @@ -83,7 +54,7 @@ class TeamUtils std::vector playingTeammates; for (auto& mate : theTeammateData.teammates) { - if (mate.status >= Teammate::ACTIVE) // not penalized + if (mate.status >= TeammateReceived::Status::ACTIVE) // not penalized { playingTeammates.push_back(mate); } diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/ThresholdUtils.h b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/ThresholdUtils.h index e9d5dc15..c22b7e1c 100644 --- a/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/ThresholdUtils.h +++ b/Src/Modules/BehaviorControl/TacticControl/RoleProvider/Utils/ThresholdUtils.h @@ -1,6 +1,6 @@ #pragma once -#include +#include class ThresholdUtils { @@ -29,6 +29,15 @@ class ThresholdUtils positioningSymbols.thresholdY = 250.f; } + static void setThreshholdsExtremeHeigh(PositioningSymbols& positioningSymbols, const float activity) + { + positioningSymbols.thresholdRotation = 15_deg + (1 - activity) * 30_deg; + const float threshold = 500.f + (1 - activity) * 2000.f; + positioningSymbols.thresholdXBack = threshold; + positioningSymbols.thresholdXFront = threshold; + positioningSymbols.thresholdY = threshold; + } + static void setThreshholdsCustom(PositioningSymbols& positioningSymbols, Angle threshRot, float threshXBack, float threshXFront, float threshY) { positioningSymbols.thresholdRotation = threshRot; @@ -37,19 +46,27 @@ class ThresholdUtils positioningSymbols.thresholdY = threshY; } - static void setThresholdsForAnyKick(Ballchaser& ballchaser) + static void setThresholdsForAnyKick(PositioningAndKickSymbols& pakSymbols) + { + pakSymbols.thresholdXFront = 100.f; // 50.f; // todo changed for gore2023 in commit "higher any kick thresholds" + pakSymbols.thresholdXBack = 300.f; // 200.f; + pakSymbols.thresholdY = 200.f; // 150.f; + pakSymbols.thresholdRotation = 40_deg; // 30_deg; + } + + static void setThresholdsForDribble(PositioningAndKickSymbols& pakSymbols) { - ballchaser.thresholdXFront = 50.f; - ballchaser.thresholdXBack = 200.f; - ballchaser.thresholdY = 150.f; - ballchaser.thresholdRotation = 30_deg; + pakSymbols.thresholdXFront = 200.f; + pakSymbols.thresholdXBack = 300.f; + pakSymbols.thresholdY = 100.f; + pakSymbols.thresholdRotation = 10_deg; } - static void setThresholdsForLongKick(Ballchaser& ballchaser) + static void setThresholdsForLongKick(PositioningAndKickSymbols& pakSymbols) { - ballchaser.thresholdXFront = 20.f; // TODO Should bespecified somewhere in the kick - ballchaser.thresholdXBack = 30.f; - ballchaser.thresholdY = 15.f; - ballchaser.thresholdRotation = 5_deg; + pakSymbols.thresholdXFront = 20.f; // TODO Should bespecified somewhere in the kick + pakSymbols.thresholdXBack = 30.f; + pakSymbols.thresholdY = 15.f; + pakSymbols.thresholdRotation = 5_deg; } }; diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleSelectionProvider.cpp b/Src/Modules/BehaviorControl/TacticControl/RoleSelectionProvider.cpp index 5d962b22..548ef62d 100644 --- a/Src/Modules/BehaviorControl/TacticControl/RoleSelectionProvider.cpp +++ b/Src/Modules/BehaviorControl/TacticControl/RoleSelectionProvider.cpp @@ -1,6 +1,22 @@ #include "RoleSelectionProvider.h" #include +RoleSelectionProvider::RoleSelectionProvider() +{ + const auto verify = [](const std::array& selection) + { + for (size_t i = 0; i < selection.size(); ++i) + { + ASSERT(selection[i].selectedRoles.size() == i + 1); + ASSERT(std::find(selection[i].selectedRoles.begin(), selection[i].selectedRoles.end(), selection[i].ballchaserDuringOppKickoff) != selection[i].selectedRoles.end()); + ASSERT(std::find(selection[i].selectedRoles.begin(), selection[i].selectedRoles.end(), selection[i].ballchaserDuringOwnKickoff) != selection[i].selectedRoles.end()); + ASSERT(std::find(selection[i].selectedRoles.begin(), selection[i].selectedRoles.end(), BehaviorData::RoleAssignment::noRole) == selection[i].selectedRoles.end()); + } + }; + verify(defensive); + verify(offensive); +} + void RoleSelectionProvider::update(RoleSelection& roleSelection) { selectRoles(roleSelection); @@ -18,39 +34,15 @@ void RoleSelectionProvider::update(RoleSelection& roleSelection) * @param defensiveBehavior Should the more defensive role selection be chosen? * @param closeToOwnGoal Is the ball located in front of the OffenseLine? */ -BehaviorData::RobotRoleAssignmentVector RoleSelectionProvider::makeFundamentalSelection(int numberOfFieldPlayers, bool defensiveBehavior) +RoleSelection RoleSelectionProvider::makeFundamentalSelection(int numberOfFieldPlayers, bool defensiveBehavior) { - switch (numberOfFieldPlayers) + const auto& selection = defensiveBehavior ? defensive : offensive; + if (numberOfFieldPlayers < 1 || numberOfFieldPlayers > static_cast(selection.size())) { - case 0: - // no additional roles needed => return empty vector - return BehaviorData::RobotRoleAssignmentVector(); - case 1: // one active field player - if (defensiveBehavior) - return selectionDefensive1; // defensive behavior - else - return selectionOffensive1; // offensive behavior - case 2: // two active field players - if (defensiveBehavior) - return selectionDefensive2; - else - return selectionOffensive2; - case 3: // three active field players - if (defensiveBehavior) - return selectionDefensive3; - else - return selectionOffensive3; - case 4: // four active field players - if (defensiveBehavior) - return selectionDefensive4; - else - return selectionOffensive4; - default: - // this case should never be entered - // if it does anyway return most defensive selection for four players and pray - return selectionDefensive4; - break; + OUTPUT_ERROR("RoleSelectionProvider: Invalid number of field players!"); + return selection.back(); } + return selection[numberOfFieldPlayers - 1]; } /** @@ -86,15 +78,29 @@ bool RoleSelectionProvider::substituteRoles(BehaviorData::RoleAssignment toRepla bool RoleSelectionProvider::isKeeperAvailable() { // the keeper needs to check its own penalty seperately because it is not in its own teammateData - if (theRobotInfo.number == 1 && theRobotInfo.penalty == PENALTY_NONE) - return true; - for (auto& mate : theTeammateData.teammates) + if (theRobotInfo.number == 1) + return theRobotInfo.penalty == PENALTY_NONE; + + if (theTeammateData.wlanOK) + { + for (auto& mate : theTeammateData.teammates) + { + if (mate.playerNumber == 1 && mate.status >= TeammateReceived::Status::ACTIVE) + return true; + } + // if no active keeper has been found return false + return false; + } + // Fallback to game controller data + else if (theGameInfo.controllerConnected) + { + return theOwnTeamInfo.players[0].penalty == PENALTY_NONE; + } + // Fallback to 7 players + else { - if (mate.number == 1 && mate.status >= Teammate::ACTIVE) - return true; + return true; } - // if no active keeper has been found return false - return false; } /** @@ -133,9 +139,16 @@ void RoleSelectionProvider::fillRoleSelection(RoleSelection& roleSelection, int { if (rolesNeeded > 0) { - BehaviorData::RobotRoleAssignmentVector fundamentalSelection = makeFundamentalSelection(rolesNeeded, theTacticSymbols.defensiveBehavior); + RoleSelection fundamentalSelection = makeFundamentalSelection(rolesNeeded, theTacticSymbols.defensiveBehavior); // append selected roles - roleSelection.selectedRoles.insert(roleSelection.selectedRoles.end(), fundamentalSelection.begin(), fundamentalSelection.end()); + roleSelection.selectedRoles.insert(roleSelection.selectedRoles.end(), fundamentalSelection.selectedRoles.begin(), fundamentalSelection.selectedRoles.end()); + roleSelection.ballchaserDuringOppKickoff = fundamentalSelection.ballchaserDuringOppKickoff; + roleSelection.ballchaserDuringOwnKickoff = fundamentalSelection.ballchaserDuringOwnKickoff; + } + else + { + roleSelection.ballchaserDuringOppKickoff = BehaviorData::RoleAssignment::noRole; + roleSelection.ballchaserDuringOwnKickoff = BehaviorData::RoleAssignment::noRole; } } @@ -146,28 +159,15 @@ void RoleSelectionProvider::fillRoleSelection(RoleSelection& roleSelection, int * First one of the predefined (fundamental) role selections given in the config is chosen * based on two factors: * - number of active field players -* - tactical alignment of the team ("defensiv" or "offensiv") +* - tactical alignment of the team ("defensive" or "offensive") * * The chosen role selection will the be tweaked based on the current situation. */ void RoleSelectionProvider::selectRoles(RoleSelection& roleSelection) { roleSelection.selectedRoles.clear(); - // decide which type of ballchaser is needed (standard ballchaser or ballchaserKeeper) - BehaviorData::RoleAssignment chaserRole; - // only keeper on field => no ballchaser role deployed - if (theBallChaserDecision.playerNumberToBall == 1) - chaserRole = BehaviorData::keeper; - else - chaserRole = BehaviorData::ballchaser; int rolesNeeded = theTacticSymbols.numberOfActiveFieldPlayers; - // if ballchaser is active reduce number of needed roles (since one field player chases the ball) - if (chaserRole == BehaviorData::ballchaser) - { - roleSelection.selectedRoles.push_back(chaserRole); - rolesNeeded--; - } if (isKeeperAvailable()) roleSelection.selectedRoles.push_back(BehaviorData::keeper); else if (isReplacementKeeperNeeded(roleSelection)) diff --git a/Src/Modules/BehaviorControl/TacticControl/RoleSelectionProvider.h b/Src/Modules/BehaviorControl/TacticControl/RoleSelectionProvider.h index 0518268f..a9c194ce 100644 --- a/Src/Modules/BehaviorControl/TacticControl/RoleSelectionProvider.h +++ b/Src/Modules/BehaviorControl/TacticControl/RoleSelectionProvider.h @@ -55,17 +55,12 @@ MODULE(RoleSelectionProvider, REQUIRES(TacticSymbols), REQUIRES(TeammateData), PROVIDES(RoleSelection), - LOADS_PARAMETERS(, + LOADS_PARAMETERS( + , (bool)(true) forceReceiverOnOwnKickoff, (bool)(true) enableReplacementKeeper, - ((BehaviorData) RobotRoleAssignmentVector) selectionDefensive1, - ((BehaviorData) RobotRoleAssignmentVector) selectionOffensive1, - ((BehaviorData) RobotRoleAssignmentVector) selectionDefensive2, - ((BehaviorData) RobotRoleAssignmentVector) selectionOffensive2, - ((BehaviorData) RobotRoleAssignmentVector) selectionDefensive3, - ((BehaviorData) RobotRoleAssignmentVector) selectionOffensive3, - ((BehaviorData) RobotRoleAssignmentVector) selectionDefensive4, - ((BehaviorData) RobotRoleAssignmentVector) selectionOffensive4 + (std::array) defensive, + (std::array) offensive ) ); @@ -77,14 +72,14 @@ class RoleSelectionProvider : public RoleSelectionProviderBase { public: /** Constructor */ - RoleSelectionProvider() { inOwnKickoffSituation = false; } + RoleSelectionProvider(); private: /** Updates some of the symbols */ void update(RoleSelection& roleSelection); /** Take the fundamental role selection from the config based on various factors*/ - BehaviorData::RobotRoleAssignmentVector makeFundamentalSelection(int numberOfFieldPlayers, bool defensiveBehavior); + RoleSelection makeFundamentalSelection(int numberOfFieldPlayers, bool defensiveBehavior); /** Removes a role from the role selection and replaces it with another. */ bool substituteRoles(BehaviorData::RoleAssignment toReplace, BehaviorData::RoleAssignment substitute, BehaviorData::RobotRoleAssignmentVector& roleSelection); @@ -111,5 +106,5 @@ class RoleSelectionProvider : public RoleSelectionProviderBase void deployReceiverInKickoff(RoleSelection& roleSelection); // vvv--- Attributes used to keep track of calculation results over several frames. ---vvv - bool inOwnKickoffSituation; + bool inOwnKickoffSituation = false; }; diff --git a/Src/Modules/BehaviorControl/TacticControl/TacticProvider.cpp b/Src/Modules/BehaviorControl/TacticControl/TacticProvider.cpp index 708504ca..92688a33 100644 --- a/Src/Modules/BehaviorControl/TacticControl/TacticProvider.cpp +++ b/Src/Modules/BehaviorControl/TacticControl/TacticProvider.cpp @@ -1,5 +1,9 @@ #include "TacticProvider.h" #include +#include +#include +#include +#include void TacticProvider::update(TacticSymbols& tacticSymbols) { @@ -11,7 +15,10 @@ void TacticProvider::update(TacticSymbols& tacticSymbols) calcNumberOfActiveFieldPlayers(tacticSymbols); // Decides between defensive/offensive behavior based on the direction in which the ball has moved last. tacticSymbols.defensiveBehavior = decideDefensiveBehavior(); - return; + tacticSymbols.activity = decideActivity(); + decideFightForBall(tacticSymbols); + decideDefensiveCone(tacticSymbols); + tacticSymbols.keepRoleAssignment = theGameInfo.state == STATE_READY && theGameSymbols.timeSinceGameState > static_cast(timeTillKeepRoleAssignmentInReady); } /** @@ -22,11 +29,28 @@ void TacticProvider::update(TacticSymbols& tacticSymbols) */ void TacticProvider::calcNumberOfActiveFieldPlayers(TacticSymbols& tacticSymbols) { - tacticSymbols.numberOfActiveFieldPlayers = (theRobotInfo.number == 1 || theRobotInfo.penalty != PENALTY_NONE) ? 0 : 1; // myself - for (auto& mate : theTeammateData.teammates) + if (theTeammateData.wlanOK) { - if (mate.status >= Teammate::ACTIVE && mate.number != 1) - tacticSymbols.numberOfActiveFieldPlayers++; + tacticSymbols.numberOfActiveFieldPlayers = (theRobotInfo.number == 1 || theRobotInfo.penalty != PENALTY_NONE) ? 0 : 1; // myself + for (auto& mate : theTeammateData.teammates) + { + if (mate.status >= TeammateReceived::Status::ACTIVE && mate.playerNumber != 1) + tacticSymbols.numberOfActiveFieldPlayers++; + } + } + else if (theGameInfo.controllerConnected) + { + const auto isActive = [](const RoboCup::RobotInfo& info) + { + return info.penalty == PENALTY_NONE; + }; + + // Skip keeper (player == 0) + tacticSymbols.numberOfActiveFieldPlayers = static_cast(std::count_if(std::begin(theOwnTeamInfo.players) + 1, std::begin(theOwnTeamInfo.players) + MAX_NUM_PLAYERS, isActive)); + } + else + { + tacticSymbols.numberOfActiveFieldPlayers = MAX_NUM_PLAYERS - 1; } } @@ -38,10 +62,13 @@ void TacticProvider::calcNumberOfActiveFieldPlayers(TacticSymbols& tacticSymbols */ bool TacticProvider::decideDefensiveBehavior() { - int scoreDiff = theOwnTeamInfo.score - theOpponentTeamInfo.score; - - // always play defensively when leading - if (scoreDiff > 0) + const bool setPlayOwn = (theGameSymbols.ownKickOff && theGameSymbols.currentSetPlay != SET_PLAY_NONE); + const bool setPlayOpponent = (!theGameSymbols.ownKickOff && theGameSymbols.currentSetPlay != SET_PLAY_NONE); + if (setPlayOwn) + { + defensiveBehavior = false; + } + else if (setPlayOpponent) { defensiveBehavior = true; } @@ -57,12 +84,44 @@ bool TacticProvider::decideDefensiveBehavior() defensiveBehavior = true; } } - return defensiveBehavior; } +float TacticProvider::decideActivity() +{ + // Activity is high after kickOff and setPlays + const bool kickOffOngoing = theGameSymbols.kickoffInProgress && theGameSymbols.timeSincePlayingState < 10000; // TODO KickOff time constant + const bool setPlayOngoing = theGameInfo.setPlay != SET_PLAY_NONE; + if (kickOffOngoing || setPlayOngoing) + { + activity = 1; + return activity; + } + + // Activity is high if ball direction changed + if (lastDirection != currentDirection) + { + activity = 1.f; + return activity; + } + + // Activity is high if ball side changed + if (lastSide != currentSide) + { + activity = 1.f; + return activity; + } + + // Else activity gets decreased + const float ACTIVITY_LOSS = 0.001f; + activity = std::max(activity - ACTIVITY_LOSS, 0.f); + return activity; +} + void TacticProvider::getBallDirection() { + lastDirection = currentDirection; + // compute previous and current location of the ball TacticProvider::BallSide previousSide = TacticProvider::currentSide; getBallSide(); @@ -73,25 +132,24 @@ void TacticProvider::getBallDirection() || (previousSide == TacticProvider::BallSide::center && newSide == TacticProvider::BallSide::front)) { // offensive direction - TacticProvider::currentDirection = TacticProvider::BallDirection::towardsEnemySide; - TacticProvider::directionChanged = true; + currentDirection = TacticProvider::BallDirection::towardsEnemySide; } else if ((previousSide == TacticProvider::BallSide::front && newSide == TacticProvider::BallSide::center) || (previousSide == TacticProvider::BallSide::center && newSide == TacticProvider::BallSide::back)) { // defensive direction - TacticProvider::currentDirection = TacticProvider::BallDirection::towardsOwnSide; - TacticProvider::directionChanged = true; + currentDirection = TacticProvider::BallDirection::towardsOwnSide; } else { // no change of direction - TacticProvider::directionChanged = false; } } void TacticProvider::getBallSide() { + lastSide = currentSide; + Vector2f ballPosition = theBallSymbols.ballPositionField; if (ballPosition.x() < 0.24 * theFieldDimensions.xPosOwnGroundline) @@ -142,4 +200,51 @@ bool TacticProvider::decideKickoffDirection(TacticSymbols& tacticSymbols) return (tacticSymbols.numberOfLeftOwnKickOffSuccess >= tacticSymbols.numberOfRightOwnKickOffSuccess); } +void TacticProvider::decideFightForBall(TacticSymbols& tacticSymbols) +{ + const int CLOSE_DISTANCE = 500; + const bool hysteresis = tacticSymbols.closeToBallRobotNumber > 0; + const float hysteresisMultiplier = hysteresis ? 1.1f : 1.f; + const int adjustedCloseDistance = (int)(hysteresisMultiplier * CLOSE_DISTANCE); + + const Vector2f& ballPosition = theBallSymbols.ballPositionField; + + tacticSymbols.closeToBallRobotNumber = 0; + tacticSymbols.closeToBallOpponentRobotNumber = 0; + int minDistance = adjustedCloseDistance; + + for (const auto& robot : theRobotMap.robots) + { + const int distance = (int)Geometry::distance(robot.pose.translation, ballPosition); + const bool opponentRobot = robot.robotType != RobotEstimate::RobotType::teammateRobot; + + if (distance <= minDistance) + { + minDistance = distance; + + tacticSymbols.closeToBallRobotNumber++; + tacticSymbols.closeToBallRobot = robot.pose; + if (opponentRobot) + { + tacticSymbols.closeToBallOpponentRobotNumber++; + tacticSymbols.closeToBallOpponentRobot = robot.pose; + } + } + else if (distance <= adjustedCloseDistance) + { + tacticSymbols.closeToBallRobotNumber++; + if (opponentRobot) + { + tacticSymbols.closeToBallOpponentRobotNumber++; + } + } + } +} + +void TacticProvider::decideDefensiveCone(TacticSymbols& theTacticSymbols) +{ + const Vector2f ballPosition = BallUtils::getBallPosition(theBallSymbols, theFrameInfo, theRobotPoseAfterPreview); + theTacticSymbols.defensiveCone = TacticUtils::getDefenseCone(180_deg, ballPosition, theFieldDimensions); +} + MAKE_MODULE(TacticProvider, behaviorControl) diff --git a/Src/Modules/BehaviorControl/TacticControl/TacticProvider.h b/Src/Modules/BehaviorControl/TacticControl/TacticProvider.h index 97d2b7d2..7c6affaa 100644 --- a/Src/Modules/BehaviorControl/TacticControl/TacticProvider.h +++ b/Src/Modules/BehaviorControl/TacticControl/TacticProvider.h @@ -10,14 +10,10 @@ #pragma once #include "Tools/Module/Module.h" -#include "Modules/BehaviorControl/CABSL/BehaviorParameters.h" #include "Representations/Configuration/FieldDimensions.h" -#include "Representations/BehaviorControl/BallChaserDecision.h" #include "Representations/BehaviorControl/BallSymbols.h" #include "Representations/BehaviorControl/GameSymbols.h" -#include "Representations/BehaviorControl/GoalSymbols.h" #include "Representations/BehaviorControl/BehaviorConfiguration.h" -#include "Representations/BehaviorControl/RoleSymbols.h" #include "Representations/BehaviorControl/TacticSymbols.h" #include "Representations/Infrastructure/FrameInfo.h" #include "Representations/Infrastructure/GameInfo.h" @@ -25,36 +21,28 @@ #include "Representations/Infrastructure/TeamInfo.h" #include "Representations/Infrastructure/TeammateData.h" #include "Representations/BehaviorControl/BehaviorData.h" -#include "Representations/Modeling/BallModel.h" -#include "Representations/Modeling/DangerMap.h" -#include "Representations/Modeling/RemoteBallModel.h" #include "Representations/Modeling/RobotMap.h" #include "Representations/Modeling/RobotPose.h" -#include "Representations/Sensing/FallDownState.h" #include "Tools/Settings.h" MODULE(TacticProvider, - REQUIRES(BallModel), - REQUIRES(BallModelAfterPreview), - REQUIRES(BallChaserDecision), REQUIRES(BallSymbols), REQUIRES(BehaviorConfiguration), - REQUIRES(DangerMap), - REQUIRES(FallDownState), REQUIRES(FrameInfo), REQUIRES(FieldDimensions), REQUIRES(GameInfo), REQUIRES(GameSymbols), - REQUIRES(GoalSymbols), REQUIRES(OwnTeamInfo), REQUIRES(OpponentTeamInfo), - REQUIRES(RemoteBallModel), REQUIRES(RobotInfo), REQUIRES(RobotMap), REQUIRES(RobotPose), REQUIRES(RobotPoseAfterPreview), REQUIRES(TeammateData), - PROVIDES(TacticSymbols) + PROVIDES(TacticSymbols), + LOADS_PARAMETERS(, + (unsigned)(5000) timeTillKeepRoleAssignmentInReady + ) ); /** @@ -79,9 +67,12 @@ class TacticProvider : public TacticProviderBase /** vvv--- methods for tactical decisions ---vvv */ void calcNumberOfActiveFieldPlayers(TacticSymbols& tacticSymbols); bool decideDefensiveBehavior(); + float decideActivity(); void getBallDirection(); void getBallSide(); bool decideKickoffDirection(TacticSymbols& tacticSymbols); + void decideFightForBall(TacticSymbols& tacticSymbols); + void decideDefensiveCone(TacticSymbols& theTacticSymbols); enum class BallSide { @@ -97,11 +88,14 @@ class TacticProvider : public TacticProviderBase }; // vvv--- Attributes used to keep track of calculation results over several frames. ---vvv + BallSide lastSide = BallSide::center; BallSide currentSide = BallSide::center; + BallDirection lastDirection = BallDirection::towardsEnemySide; BallDirection currentDirection = BallDirection::towardsEnemySide; - bool directionChanged = false; bool defensiveBehavior = false; + float activity = 1.f; + bool lastKickoffWasOwn; int lastOwnScore; int lastOpponentScore; diff --git a/Src/Modules/BehaviorControl/TacticControl/TestProvider/PathToSpeedPressToStop.h b/Src/Modules/BehaviorControl/TacticControl/TestProvider/PathToSpeedPressToStop.h index 2fb002c0..a92d8d7e 100644 --- a/Src/Modules/BehaviorControl/TacticControl/TestProvider/PathToSpeedPressToStop.h +++ b/Src/Modules/BehaviorControl/TacticControl/TestProvider/PathToSpeedPressToStop.h @@ -1,26 +1,26 @@ #pragma once +#include "Modules/Modeling/PathProvider/SimplePathProvider.h" // for parameters #include "Representations/BehaviorControl/BallSymbols.h" #include "Representations/BehaviorControl/BehaviorConfiguration.h" +#include "Representations/BehaviorControl/BehaviorData.h" #include "Representations/BehaviorControl/GameSymbols.h" #include "Representations/BehaviorControl/RoleSymbols.h" -#include "Representations/BehaviorControl/PositioningSymbols.h" -#include "Representations/BehaviorControl/BehaviorData.h" +#include "Representations/BehaviorControl/RoleSymbols/PositioningSymbols.h" #include "Representations/Configuration/FieldDimensions.h" #include "Representations/Infrastructure/FrameInfo.h" #include "Representations/Infrastructure/GameInfo.h" #include "Representations/Infrastructure/LiveConfigurationState.h" #include "Representations/Infrastructure/RobotInfo.h" +#include "Representations/Infrastructure/TeammateData.h" #include "Representations/Modeling/BallModel.h" +#include "Representations/Modeling/Path.h" #include "Representations/Modeling/RobotMap.h" #include "Representations/Modeling/RobotPose.h" -#include "Representations/Modeling/Path.h" #include "Representations/MotionControl/MotionRequest.h" -#include "Representations/MotionControl/WalkingEngineParams.h" #include "Representations/MotionControl/MotionState.h" -#include "Representations/Infrastructure/TeammateData.h" +#include "Representations/MotionControl/WalkingEngineParams.h" #include "Tools/Module/Module.h" -#include "Modules/Modeling/PathProvider/SimplePathProvider.h" // for parameters MODULE(PathToSpeedPressToStop, REQUIRES(BallSymbols), diff --git a/Src/Modules/BehaviorControl/TacticControl/TestProvider/RoleTestProvider.cpp b/Src/Modules/BehaviorControl/TacticControl/TestProvider/RoleTestProvider.cpp index 9acf5900..e8d0764d 100644 --- a/Src/Modules/BehaviorControl/TacticControl/TestProvider/RoleTestProvider.cpp +++ b/Src/Modules/BehaviorControl/TacticControl/TestProvider/RoleTestProvider.cpp @@ -46,8 +46,8 @@ void RoleTestProvider::getCurrentTeamStatus(std::vector& playerNumbers) // get information for teammates for (auto& mate : theTeammateData.teammates) { - playerNumbers.push_back(mate.number); + playerNumbers.push_back(mate.playerNumber); } } -MAKE_MODULE(RoleTestProvider, behaviorControl) \ No newline at end of file +MAKE_MODULE(RoleTestProvider, behaviorControl) diff --git a/Src/Modules/CMakeLists.txt b/Src/Modules/CMakeLists.txt index 684257a9..d476d441 100755 --- a/Src/Modules/CMakeLists.txt +++ b/Src/Modules/CMakeLists.txt @@ -5,13 +5,7 @@ target_sources(${PROJECT_NAME} BehaviorControl/CABSL/BehaviorControl.cpp BehaviorControl/CABSL/BehaviorControl.h BehaviorControl/CABSL/BehaviorParameters.h - BehaviorControl/CABSL/Libraries.cpp - BehaviorControl/CABSL/Libraries.h BehaviorControl/CABSL/Libraries/HelperFunctions.h - BehaviorControl/CABSL/Libraries/LibTactic.cpp - BehaviorControl/CABSL/Libraries/LibTactic.h - BehaviorControl/CABSL/LibraryBase.cpp - BehaviorControl/CABSL/LibraryBase.h BehaviorControl/CABSL/Options.h BehaviorControl/CABSL/Options/AutoCalibration/AutoCalibrate.h BehaviorControl/CABSL/Options/BodyControl.h @@ -35,7 +29,6 @@ target_sources(${PROJECT_NAME} BehaviorControl/CABSL/Options/Skills/DecideKick.h BehaviorControl/CABSL/Options/Skills/Dive.h BehaviorControl/CABSL/Options/Skills/Dribble.h - BehaviorControl/CABSL/Options/Skills/GetInPosition.h BehaviorControl/CABSL/Options/Skills/GoToFieldCoordinates.h BehaviorControl/CABSL/Options/Skills/GoToRelativeCoordinates.h BehaviorControl/CABSL/Options/Skills/InterceptBall.h @@ -47,6 +40,9 @@ target_sources(${PROJECT_NAME} BehaviorControl/CABSL/Options/Skills/StandUp.h BehaviorControl/CABSL/Options/Skills/Walk.h BehaviorControl/CABSL/Options/Skills/WalkKick.h + BehaviorControl/CABSL/Options/Skills/RobotSafetyShutdown.h + BehaviorControl/CABSL/Options/Testing/Testing.h + BehaviorControl/CABSL/Options/Testing/TestingUnstiff.h BehaviorControl/CABSL/Options/Start.h BehaviorControl/HeadControl/HeadControl.cpp BehaviorControl/HeadControl/HeadControl.h @@ -60,6 +56,11 @@ target_sources(${PROJECT_NAME} BehaviorControl/JoystickControl/JoystickState.h BehaviorControl/LEDHandler/LEDHandler.cpp BehaviorControl/LEDHandler/LEDHandler.h + BehaviorControl/TacticControl/HeatMapProvider/HeatMapProvider.cpp + BehaviorControl/TacticControl/HeatMapProvider/HeatMapProvider.h + BehaviorControl/TacticControl/HeatMapProvider/HeatMapUtils.h + BehaviorControl/TacticControl/KickWheelProvider/KickWheelProvider.cpp + BehaviorControl/TacticControl/KickWheelProvider/KickWheelProvider.h BehaviorControl/TacticControl/BallChaserDecisionProvider.cpp BehaviorControl/TacticControl/BallChaserDecisionProvider.h BehaviorControl/TacticControl/BallSearchProvider.cpp @@ -78,82 +79,112 @@ target_sources(${PROJECT_NAME} BehaviorControl/TacticControl/PositioningSymbolsProvider.h BehaviorControl/TacticControl/RoleDynamicProvider.cpp BehaviorControl/TacticControl/RoleDynamicProvider.h - BehaviorControl/TacticControl/RoleProvider/BackupBallchaserProvider.cpp - BehaviorControl/TacticControl/RoleProvider/BackupBallchaserProvider.h - BehaviorControl/TacticControl/RoleProvider/BallchaserKeeperProvider.cpp - BehaviorControl/TacticControl/RoleProvider/BallchaserKeeperProvider.h - BehaviorControl/TacticControl/RoleProvider/BallchaserProvider.cpp - BehaviorControl/TacticControl/RoleProvider/BallchaserProvider.h - BehaviorControl/TacticControl/RoleProvider/CenterProvider2022.cpp - BehaviorControl/TacticControl/RoleProvider/CenterProvider2022.h + BehaviorControl/TacticControl/RoleProvider/OldRoles/BackupBallchaserProvider.cpp + BehaviorControl/TacticControl/RoleProvider/OldRoles/BackupBallchaserProvider.h + BehaviorControl/TacticControl/RoleProvider/OldRoles/BallchaserKeeperProvider.cpp + BehaviorControl/TacticControl/RoleProvider/OldRoles/BallchaserKeeperProvider.h + BehaviorControl/TacticControl/RoleProvider/Ballchaser/BallchaserProvider.cpp + BehaviorControl/TacticControl/RoleProvider/Ballchaser/BallchaserProvider.h + BehaviorControl/TacticControl/RoleProvider/OldRoles/CenterProviderOld.cpp + BehaviorControl/TacticControl/RoleProvider/OldRoles/CenterProviderOld.h + BehaviorControl/TacticControl/RoleProvider/BackWingProvider.cpp + BehaviorControl/TacticControl/RoleProvider/BackWingProvider.h + BehaviorControl/TacticControl/RoleProvider/CenterProvider.cpp + BehaviorControl/TacticControl/RoleProvider/CenterProvider.h BehaviorControl/TacticControl/RoleProvider/DefenderLeftProvider.cpp BehaviorControl/TacticControl/RoleProvider/DefenderLeftProvider.h BehaviorControl/TacticControl/RoleProvider/DefenderRightProvider.cpp BehaviorControl/TacticControl/RoleProvider/DefenderRightProvider.h BehaviorControl/TacticControl/RoleProvider/DefenderSingleProvider.cpp BehaviorControl/TacticControl/RoleProvider/DefenderSingleProvider.h + BehaviorControl/TacticControl/RoleProvider/FrontWingProvider.cpp + BehaviorControl/TacticControl/RoleProvider/FrontWingProvider.h BehaviorControl/TacticControl/RoleProvider/KeeperProvider.cpp BehaviorControl/TacticControl/RoleProvider/KeeperProvider.h BehaviorControl/TacticControl/RoleProvider/KeeperProvider.cpp BehaviorControl/TacticControl/RoleProvider/KeeperProvider.h - BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/FastLongKick.h - BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/KickHack.h - BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/KickHackLong.h - BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/Rotate45Kick.h - BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/SideKickOuter45.h - BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/SideKickOuterFoot.h - BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/SlowLongKick.h - BehaviorControl/TacticControl/RoleProvider/Kick/Models/Danger.h - BehaviorControl/TacticControl/RoleProvider/Kick/Models/DistanceRequirement.h - BehaviorControl/TacticControl/RoleProvider/Kick/ExecutableKicks/ExecutableKick.cpp - BehaviorControl/TacticControl/RoleProvider/Kick/ExecutableKicks/ExecutableKick.h - BehaviorControl/TacticControl/RoleProvider/Kick/ExecutableKicks/ExecutableKicks.cpp - BehaviorControl/TacticControl/RoleProvider/Kick/ExecutableKicks/ExecutableKicks.h - BehaviorControl/TacticControl/RoleProvider/Kick/HeatMapProvider.cpp - BehaviorControl/TacticControl/RoleProvider/Kick/HeatMapProvider.h - BehaviorControl/TacticControl/RoleProvider/Kick/Kicks/Kick.h - BehaviorControl/TacticControl/RoleProvider/Kick/KickManager.cpp - BehaviorControl/TacticControl/RoleProvider/Kick/KickManager.h - BehaviorControl/TacticControl/RoleProvider/Kick/KickManager_ExecutableKick.cpp - BehaviorControl/TacticControl/RoleProvider/Kick/Models/KickRange.h - BehaviorControl/TacticControl/RoleProvider/Kick/Models/Optimize.h + BehaviorControl/TacticControl/KicksProvider/Types/Dribble.h + BehaviorControl/TacticControl/KicksProvider/Kick.cpp + BehaviorControl/TacticControl/KicksProvider/KicksProvider.cpp + BehaviorControl/TacticControl/KicksProvider/KicksProvider.h + BehaviorControl/TacticControl/KicksProvider/Types/KickEngineKick.h + BehaviorControl/TacticControl/KicksProvider/KickWithLeftUtils.h + BehaviorControl/TacticControl/KicksProvider/Enums/KickWithLeftCondition.h + BehaviorControl/TacticControl/KicksProvider/Enums/Side.h + BehaviorControl/TacticControl/KicksProvider/Types/WalkingEngineKick.h + BehaviorControl/TacticControl/RoleProvider/KickManager/Enums/DistanceRequirement.h + BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Filterer/Filterer.cpp + BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Filterer/Filterer.h + BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Filterer/Filterer_Direction.cpp + BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Filterer/Filterer_Pose.cpp + BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Filterer/Filterer_Target.cpp + BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Filterer/Filterer_Kick.cpp + BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Factors.h + BehaviorControl/TacticControl/RoleProvider/KickManager/Models/CurrentKick.h + BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/ScoreFunctions.cpp + BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/ScoreFunctions.h + BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/SelectFunctions.h + BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/SelectFunctions_.cpp + BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/SelectFunctions_Direction.cpp + BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/SelectFunctions_Pose.cpp + BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/SelectFunctions_Target.cpp + BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/DrawFunctions.h + BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Selectables/KickPlan.h + BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Selectables/SelectableDirection.h + BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Selectables/SelectableKick.h + BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Selectables/SelectablePose.h + BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Selectables/SelectableTarget.h + BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Ranges/Cone.h + BehaviorControl/TacticControl/RoleProvider/KickManager/Enums/Danger.h + BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Ranges/KickCone.h + BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Ranges/KickLine.h + BehaviorControl/TacticControl/RoleProvider/KickManager/Models/Ranges/KickRange.h + BehaviorControl/TacticControl/RoleProvider/KickManager/Enums/SelectedFoot.h + BehaviorControl/TacticControl/RoleProvider/KickManager/Constants.h + BehaviorControl/TacticControl/RoleProvider/KickManager/Models/KickManager.cpp + BehaviorControl/TacticControl/RoleProvider/KickManager/Models/KickManager.h + BehaviorControl/TacticControl/RoleProvider/KickManager/Functions/TargetFunctions.h BehaviorControl/TacticControl/RoleProvider/Logs/BehaviorLogger.cpp BehaviorControl/TacticControl/RoleProvider/Logs/BehaviorLogger.h BehaviorControl/TacticControl/RoleProvider/Logs/KickDrawings.h - BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/TestObjective.cpp - BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/TestObjective.h - BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/GoalObjective.cpp - BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/GoalObjective.h - BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/NoDeadlockObjective.cpp - BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/NoDeadlockObjective.h - BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/OneVsOneObjective.cpp - BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/OneVsOneObjective.h - BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/MoveObjective.cpp - BehaviorControl/TacticControl/RoleProvider/Objectives/Ballchaser/MoveObjective.h - BehaviorControl/TacticControl/RoleProvider/Objectives/Objective.h - BehaviorControl/TacticControl/RoleProvider/Objectives/ObjectivesManager.h + BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/Test/KickInPositionTestObjective.cpp + BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/Test/KickInPositionTestObjective.h + BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/Test/KickBallTestObjective.cpp + BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/Test/KickBallTestObjective.h + BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/Test/KickToCenterTestObjective.cpp + BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/Test/KickToCenterTestObjective.h + BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/GoalObjective.cpp + BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/GoalObjective.h + BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/MoveObjective.cpp + BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/MoveObjective.h + BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/TrickKickObjective.cpp + BehaviorControl/TacticControl/RoleProvider/Ballchaser/Objectives/TrickKickObjective.h + BehaviorControl/TacticControl/RoleProvider/ObjectivesManager/Objective.h + BehaviorControl/TacticControl/RoleProvider/ObjectivesManager/ObjectiveBase.h + BehaviorControl/TacticControl/RoleProvider/ObjectivesManager/ObjectivesList.h + BehaviorControl/TacticControl/RoleProvider/ObjectivesManager/ObjectivesManager.h + BehaviorControl/TacticControl/RoleProvider/ObjectivesManager/ObjectivesSwitch.h BehaviorControl/TacticControl/RoleProvider/Utils/AvoidUtils.h - BehaviorControl/TacticControl/RoleProvider/Utils/BallchaserUtils.h + BehaviorControl/TacticControl/RoleProvider/Ballchaser/BallchaserUtils.h BehaviorControl/TacticControl/RoleProvider/Utils/BallUtils.h - BehaviorControl/TacticControl/RoleProvider/Utils/BlockUtils.cpp - BehaviorControl/TacticControl/RoleProvider/Utils/BlockUtils.h - BehaviorControl/TacticControl/RoleProvider/Utils/DangerUtils.h BehaviorControl/TacticControl/RoleProvider/Utils/FieldUtils.h - BehaviorControl/TacticControl/RoleProvider/Utils/FrontUtils.h - BehaviorControl/TacticControl/RoleProvider/Utils/HeatUtils.h - BehaviorControl/TacticControl/RoleProvider/Utils/HysterUtils.h + BehaviorControl/TacticControl/RoleProvider/Utils/HysteresisUtils.h BehaviorControl/TacticControl/RoleProvider/Utils/KickUtils.cpp BehaviorControl/TacticControl/RoleProvider/Utils/KickUtils.h BehaviorControl/TacticControl/RoleProvider/Utils/MathUtils.h - BehaviorControl/TacticControl/RoleProvider/Utils/PassUtils.h - BehaviorControl/TacticControl/RoleProvider/Utils/PositionUtils.cpp + BehaviorControl/TacticControl/RoleProvider/Utils/PathUtils.h BehaviorControl/TacticControl/RoleProvider/Utils/PositionUtils.h + BehaviorControl/TacticControl/RoleProvider/Utils/TacticUtils.h BehaviorControl/TacticControl/RoleProvider/Utils/TeamUtils.h BehaviorControl/TacticControl/RoleProvider/Utils/ThresholdUtils.h - BehaviorControl/TacticControl/RoleProvider/ReceiverProvider.cpp - BehaviorControl/TacticControl/RoleProvider/ReceiverProvider.h - BehaviorControl/TacticControl/RoleProvider/ReplacementKeeperProvider.cpp - BehaviorControl/TacticControl/RoleProvider/ReplacementKeeperProvider.h + BehaviorControl/TacticControl/RoleProvider/LeftWingProvider.cpp + BehaviorControl/TacticControl/RoleProvider/LeftWingProvider.h + BehaviorControl/TacticControl/RoleProvider/RightWingProvider.cpp + BehaviorControl/TacticControl/RoleProvider/RightWingProvider.h + BehaviorControl/TacticControl/RoleProvider/OldRoles/ReceiverProvider.cpp + BehaviorControl/TacticControl/RoleProvider/OldRoles/ReceiverProvider.h + BehaviorControl/TacticControl/RoleProvider/OldRoles/ReplacementKeeperProvider.cpp + BehaviorControl/TacticControl/RoleProvider/OldRoles/ReplacementKeeperProvider.h BehaviorControl/TacticControl/RoleProvider/RoleProvider.h BehaviorControl/TacticControl/RoleSelectionProvider.cpp BehaviorControl/TacticControl/RoleSelectionProvider.h @@ -171,8 +202,12 @@ target_sources(${PROJECT_NAME} Configuration/MotionConfigurationDataProvider.h Infrastructure/CameraProviderV6.cpp Infrastructure/CameraProviderV6.h + Infrastructure/JointRequestProvider.cpp + Infrastructure/JointRequestProvider.h Infrastructure/CognitionLogDataProvider.cpp Infrastructure/CognitionLogDataProvider.h + Infrastructure/HealthScoreProvider.cpp + Infrastructure/HealthScoreProvider.h Infrastructure/JPEGImageProvider.cpp Infrastructure/JPEGImageProvider.h Infrastructure/LiveConfigurationProvider.cpp @@ -202,6 +237,8 @@ target_sources(${PROJECT_NAME} Infrastructure/Network/EventManager.cpp Infrastructure/Network/TeamCommDataPacker.cpp Infrastructure/Network/TeamCommDataPacker.h + Infrastructure/Network/TeamCommLocalSocketProvider.cpp + Infrastructure/Network/TeamCommLocalSocketProvider.h Infrastructure/Network/TeamCommReceiver.cpp Infrastructure/Network/TeamCommReceiver.h Infrastructure/Network/TeamCommSender.cpp @@ -307,6 +344,8 @@ target_sources(${PROJECT_NAME} MotionControl/DortmundWalkingEngine/FLIPMObserver.h MotionControl/DortmundWalkingEngine/FLIPMParamsProvider.cpp MotionControl/DortmundWalkingEngine/FLIPMParamsProvider.h + MotionControl/DortmundWalkingEngine/FLIPMStateProvider.cpp + MotionControl/DortmundWalkingEngine/FLIPMStateProvider.h MotionControl/DortmundWalkingEngine/GyroTiltController.cpp MotionControl/DortmundWalkingEngine/GyroTiltController.h MotionControl/DortmundWalkingEngine/IMUCoMProvider.cpp @@ -324,8 +363,11 @@ target_sources(${PROJECT_NAME} MotionControl/DortmundWalkingEngine/spline.h MotionControl/HeadMotionEngine.cpp MotionControl/HeadMotionEngine.h + MotionControl/JointErrorCalc.h + MotionControl/JointErrorCalc.cpp MotionControl/KeyFrameEngine/KeyFrameEngine.cpp MotionControl/KeyFrameEngine/KeyFrameEngine.h + MotionControl/KickEngine/DynPoint.h MotionControl/KickEngine/KickEngine.cpp MotionControl/KickEngine/KickEngine.h MotionControl/KickEngine/KickEngineData.cpp @@ -372,8 +414,12 @@ target_sources(${PROJECT_NAME} Perception/CoordinateSystemProvider.h Perception/ImageWriterPNG.cpp Perception/ImageWriterPNG.h + Perception/JerseyColorDetector.cpp + Perception/JerseyColorDetector.h Perception/OracledPerceptsProvider.cpp Perception/OracledPerceptsProvider.h + Perception/PenaltyCrossClassifier.cpp + Perception/PenaltyCrossClassifier.h Perception/PNGLogger.cpp Perception/PNGLogger.h Perception/PNGLoggerSequence.cpp @@ -382,19 +428,29 @@ target_sources(${PROJECT_NAME} Perception/PSKickDetector.h Perception/RobotCameraMatrixProvider.cpp Perception/RobotCameraMatrixProvider.h + Perception/RobotOrientationDetector.cpp + Perception/RobotOrientationDetector.h + Perception/RobotsPerceptProvider.cpp + Perception/RobotsPerceptProvider.h + Perception/SonarDetector.cpp + Perception/SonarDetector.h + Perception/RobotClassifier.cpp + Perception/RobotClassifier.h Perception/TFlite.h Perception/YoloRobotDetector.cpp Perception/YoloRobotDetector.h Sensing/ArmContactProvider.cpp Sensing/ArmContactProvider.h + Sensing/BrokenJointDetector.cpp + Sensing/BrokenJointDetector.h Sensing/CoMRCSProvider.cpp Sensing/CoMRCSProvider.h Sensing/CoPProvider.cpp Sensing/CoPProvider.h Sensing/GroundContactDetector.cpp Sensing/GroundContactDetector.h - Sensing/GyroFallDownStateDetector.cpp - Sensing/GyroFallDownStateDetector.h + Sensing/SimpleFallDownStateDetector.cpp + Sensing/SimpleFallDownStateDetector.h Sensing/InertialDataFilter.cpp Sensing/InertialDataFilter.h Sensing/JoinedIMUDataProvider.cpp diff --git a/Src/Modules/Configuration/CognitionConfigurationDataProvider.cpp b/Src/Modules/Configuration/CognitionConfigurationDataProvider.cpp index 67d73417..60036cd0 100644 --- a/Src/Modules/Configuration/CognitionConfigurationDataProvider.cpp +++ b/Src/Modules/Configuration/CognitionConfigurationDataProvider.cpp @@ -113,7 +113,7 @@ void CognitionConfigurationDataProvider::update(FieldDimensions& fieldDimensions fieldDimensions.loadFromJsonFile(theUSBStatus.path + "/field_dimensions.json"); } - fieldDimensions.drawPolygons(theOwnTeamInfo.teamColour); + fieldDimensions.drawPolygons(theOwnTeamInfo.fieldPlayerColour); } void CognitionConfigurationDataProvider::update(CameraCalibration& cameraCalibration) diff --git a/Src/Modules/Configuration/MotionConfigurationDataProvider.cpp b/Src/Modules/Configuration/MotionConfigurationDataProvider.cpp index c7608c66..4eebbfd7 100644 --- a/Src/Modules/Configuration/MotionConfigurationDataProvider.cpp +++ b/Src/Modules/Configuration/MotionConfigurationDataProvider.cpp @@ -20,7 +20,7 @@ MotionConfigurationDataProvider::MotionConfigurationDataProvider() readOdometryCorrectionTables(); readRobotDimensions(); readStiffnessSettings(); - readUsConfiguration(); + readSonarConfiguration(); } void MotionConfigurationDataProvider::update(FieldDimensions& fieldDimensions) @@ -103,12 +103,12 @@ void MotionConfigurationDataProvider::update(StiffnessSettings& stiffnessSetting } } -void MotionConfigurationDataProvider::update(UsConfiguration& usConfiguration) +void MotionConfigurationDataProvider::update(SonarConfiguration& sonarConfiguration) { - if (theUsConfiguration) + if (theSonarConfiguration) { - usConfiguration = *theUsConfiguration; - theUsConfiguration.reset(); + sonarConfiguration = *theSonarConfiguration; + theSonarConfiguration.reset(); } } @@ -207,14 +207,14 @@ void MotionConfigurationDataProvider::readStiffnessSettings() } } -void MotionConfigurationDataProvider::readUsConfiguration() +void MotionConfigurationDataProvider::readSonarConfiguration() { - ASSERT(!theUsConfiguration); + ASSERT(!theSonarConfiguration); - InMapFile stream("usConfiguration.cfg"); + InMapFile stream("sonarConfiguration.cfg"); if (stream.exists()) { - theUsConfiguration = std::make_unique(); - stream >> *theUsConfiguration; + theSonarConfiguration = std::make_unique(); + stream >> *theSonarConfiguration; } } diff --git a/Src/Modules/Configuration/MotionConfigurationDataProvider.h b/Src/Modules/Configuration/MotionConfigurationDataProvider.h index 43fc19d5..d82b6a46 100644 --- a/Src/Modules/Configuration/MotionConfigurationDataProvider.h +++ b/Src/Modules/Configuration/MotionConfigurationDataProvider.h @@ -12,7 +12,7 @@ #include "Representations/Configuration/MassCalibration.h" #include "Representations/Configuration/MotionSettings.h" #include "Representations/Configuration/RobotDimensions.h" -#include "Representations/Configuration/UsConfiguration.h" +#include "Representations/Configuration/SonarConfiguration.h" #include "Representations/Configuration/FieldDimensions.h" #include "Representations/Configuration/OdometryCorrectionTable.h" #include "Representations/Infrastructure/StiffnessData.h" @@ -28,7 +28,7 @@ MODULE(MotionConfigurationDataProvider, PROVIDES(MotionSettings), PROVIDES(OdometryCorrectionTables), PROVIDES(RobotDimensions), - PROVIDES(UsConfiguration), + PROVIDES(SonarConfiguration), PROVIDES_WITHOUT_MODIFY(FieldDimensions) ); @@ -41,7 +41,7 @@ class MotionConfigurationDataProvider : public MotionConfigurationDataProviderBa std::unique_ptr theMassCalibration = nullptr; std::unique_ptr theMotionSettings = nullptr; std::unique_ptr theRobotDimensions = nullptr; - std::unique_ptr theUsConfiguration = nullptr; + std::unique_ptr theSonarConfiguration = nullptr; std::unique_ptr theFieldDimensions = nullptr; std::unique_ptr theOdometryCorrectionTables = nullptr; @@ -55,7 +55,7 @@ class MotionConfigurationDataProvider : public MotionConfigurationDataProviderBa void update(OdometryCorrectionTables& odometryCorrectionTables); void update(RobotDimensions& robotDimensions); void update(StiffnessSettings& stiffnessSettings); - void update(UsConfiguration& usConfiguration); + void update(SonarConfiguration& sonarConfiguration); void readFieldDimensions(); void readJointCalibration(); @@ -65,7 +65,7 @@ class MotionConfigurationDataProvider : public MotionConfigurationDataProviderBa void readOdometryCorrectionTables(); void readRobotDimensions(); void readStiffnessSettings(); - void readUsConfiguration(); + void readSonarConfiguration(); public: MotionConfigurationDataProvider(); diff --git a/Src/Modules/Infrastructure/CameraProviderV6.cpp b/Src/Modules/Infrastructure/CameraProviderV6.cpp index 3ad6a9ef..6c4f2da2 100644 --- a/Src/Modules/Infrastructure/CameraProviderV6.cpp +++ b/Src/Modules/Infrastructure/CameraProviderV6.cpp @@ -83,7 +83,6 @@ void CameraProviderV6::update(Image& image) } ASSERT(image.timeStamp >= lastImageTimeStamp); lastImageTimeStamp = image.timeStamp; - MODIFY("module:CameraProviderV6:fullSize", image.isFullSize); // Update current time only if update method is called. // This prevents camera reset in case another module provides the images (e.g. ImageLogDataProvider). @@ -137,7 +136,6 @@ void CameraProviderV6::update(ImageUpper& imageUpper) ASSERT(imageUpper.timeStamp >= lastImageUpperTimeStamp); lastImageUpperTimeStamp = imageUpper.timeStamp; - MODIFY("module:CameraProviderV6:fullSize", imageUpper.isFullSize); // Update current time only if update method is called. // This prevents camera reset in case another module provides the images (e.g. ImageLogDataProvider). @@ -404,6 +402,13 @@ void CameraProviderV6::waitForFrameData2() { printf("Reset!\n"); VERIFY(++resetCounter <= 3); + /*if (++resetCounter > 3) + { + if (resetUpper) + upperCameraInfo.usable = false; + if (resetLower) + lowerCameraInfo.usable = false; + }*/ SystemCall::execute("/bin/bash -c '[ -f /usr/libexec/reset-cameras.sh ] && /usr/libexec/reset-cameras.sh toggle; [ -f /opt/aldebaran/libexec/reset-cameras.sh ] && /opt/aldebaran/libexec/reset-cameras.sh toggle; '"); std::this_thread::sleep_for(std::chrono::milliseconds(2000)); } diff --git a/Src/Modules/Infrastructure/CognitionLogDataProvider.h b/Src/Modules/Infrastructure/CognitionLogDataProvider.h index 5df7fa50..9411128a 100644 --- a/Src/Modules/Infrastructure/CognitionLogDataProvider.h +++ b/Src/Modules/Infrastructure/CognitionLogDataProvider.h @@ -10,8 +10,10 @@ #include "Representations/BehaviorControl/ActivationGraph.h" #include "Representations/BehaviorControl/BallSymbols.h" #include "Representations/BehaviorControl/BehaviorData.h" +#include "Representations/MotionControl/HeadAngleRequest.h" #include "Representations/BehaviorControl/HeadControlRequest.h" -#include "Representations/BehaviorControl/PositioningSymbols.h" +#include "Representations/BehaviorControl/RoleSymbols/Ballchaser.h" +#include "Representations/BehaviorControl/RoleSymbols/PositioningSymbols.h" #include "Representations/Infrastructure/AudioData.h" #include "Representations/Infrastructure/FrameInfo.h" #include "Representations/Infrastructure/GameInfo.h" @@ -19,13 +21,13 @@ #include "Representations/Infrastructure/Image.h" #include "Representations/Infrastructure/JPEGImage.h" #include "Representations/Infrastructure/LowFrameRateImage.h" -#include "Representations/Infrastructure/SequenceImage.h" -#include "Representations/Infrastructure/YoloInput.h" #include "Representations/Infrastructure/RobotHealth.h" #include "Representations/Infrastructure/RobotInfo.h" +#include "Representations/Infrastructure/SensorData/JointSensorData.h" +#include "Representations/Infrastructure/SequenceImage.h" #include "Representations/Infrastructure/TeamInfo.h" #include "Representations/Infrastructure/TeammateData.h" -#include "Representations/Infrastructure/SensorData/JointSensorData.h" +#include "Representations/Infrastructure/YoloInput.h" #include "Representations/Modeling/BallModel.h" #include "Representations/Modeling/RemoteBallModel.h" #include "Representations/Modeling/RobotMap.h" @@ -37,12 +39,12 @@ #include "Representations/MotionControl/OdometryData.h" #include "Representations/Perception/BallPercept.h" #include "Representations/Perception/BodyContour.h" -#include "Representations/Perception/CameraMatrix.h" -#include "Representations/Perception/CenterCirclePercept.h" #include "Representations/Perception/CLIPFieldLinesPercept.h" #include "Representations/Perception/CLIPGoalPercept.h" -#include "Representations/Perception/PenaltyCrossPercept.h" +#include "Representations/Perception/CameraMatrix.h" +#include "Representations/Perception/CenterCirclePercept.h" #include "Representations/Perception/ImageCoordinateSystem.h" +#include "Representations/Perception/PenaltyCrossPercept.h" #include "Representations/Perception/RobotsPercept.h" #include "Representations/Sensing/GroundContactState.h" #include "Tools/Debugging/DebugImages.h" @@ -60,6 +62,7 @@ MODULE(CognitionLogDataProvider, PROVIDES_CONCURRENT(BallModel), PROVIDES_CONCURRENT(BallPercept), PROVIDES_CONCURRENT(MultipleBallPercept), + PROVIDES_CONCURRENT(Ballchaser), PROVIDES_CONCURRENT(BallSymbols), PROVIDES_CONCURRENT(BehaviorData), PROVIDES_CONCURRENT(BodyContour), @@ -74,6 +77,7 @@ MODULE(CognitionLogDataProvider, PROVIDES_CONCURRENT(GameInfo), PROVIDES_CONCURRENT(GroundContactState), PROVIDES_CONCURRENT(GroundTruthWorldState), + PROVIDES_CONCURRENT(HeadAngleRequest), PROVIDES_CONCURRENT(HeadControlRequest), PROVIDES_CONCURRENT_WITHOUT_MODIFY(Image), PROVIDES_CONCURRENT_WITHOUT_MODIFY(ImageUpper), @@ -89,6 +93,7 @@ MODULE(CognitionLogDataProvider, PROVIDES_CONCURRENT(OwnTeamInfo), PROVIDES_CONCURRENT(PenaltyCrossPercept), PROVIDES_CONCURRENT(ProcessedBallPatches), + PROVIDES_CONCURRENT(ProcessedRobotsHypotheses), PROVIDES_CONCURRENT(PositioningSymbols), PROVIDES_CONCURRENT(RawGameInfo), PROVIDES_CONCURRENT(RemoteBallModel), @@ -96,7 +101,6 @@ MODULE(CognitionLogDataProvider, PROVIDES_CONCURRENT(RobotInfo), PROVIDES_CONCURRENT(RobotMap), PROVIDES_CONCURRENT(RobotsPercept), - PROVIDES_CONCURRENT(RobotsPerceptUpper), PROVIDES_CONCURRENT(RobotPose), PROVIDES_CONCURRENT_WITHOUT_MODIFY(RobotPoseHypotheses), PROVIDES_CONCURRENT(SideConfidence), @@ -123,6 +127,7 @@ class CognitionLogDataProvider : public CognitionLogDataProviderBase, public Log // No-op update stubs void update(ActivationGraph&) override {} void update(AudioData&) override {} + void update(Ballchaser&) override {} void update(BallModel&) override {} void update(BallPercept&) override {} void update(MultipleBallPercept&) override {} @@ -140,6 +145,7 @@ class CognitionLogDataProvider : public CognitionLogDataProviderBase, public Log void update(GameInfo&) override {} void update(GroundContactState&) override {} void update(GroundTruthWorldState&) override {} + void update(HeadAngleRequest&) override {} void update(HeadControlRequest&) override {} void update(JointSensorData&) override {} void update(MotionInfo&) override {} @@ -149,6 +155,7 @@ class CognitionLogDataProvider : public CognitionLogDataProviderBase, public Log void update(OwnTeamInfo&) override {} void update(PenaltyCrossPercept&) override {} void update(ProcessedBallPatches&) override {} + void update(ProcessedRobotsHypotheses&) override {} void update(PositioningSymbols&) override {} void update(RawGameInfo&) override {} void update(RemoteBallModel&) override {} @@ -157,7 +164,6 @@ class CognitionLogDataProvider : public CognitionLogDataProviderBase, public Log void update(RobotMap&) override {} void update(RobotPose&) override {} void update(RobotsPercept&) override {} - void update(RobotsPerceptUpper&) override {} void update(SideConfidence&) override {} void update(TeamBallModel&) override {} void update(TeammateData&) override {} diff --git a/Src/Modules/Infrastructure/HealthScoreProvider.cpp b/Src/Modules/Infrastructure/HealthScoreProvider.cpp new file mode 100644 index 00000000..c4bde103 --- /dev/null +++ b/Src/Modules/Infrastructure/HealthScoreProvider.cpp @@ -0,0 +1,75 @@ +/** +* @file Modules/Infrastructure/HealthScoreProvider.h +* This file implements a module that provides information about the robot's health. +* @author Dominik Brämer +*/ + + +#include "HealthScoreProvider.h" + +HealthScoreProvider::HealthScoreProvider() +{ + jointsProbablyBad = false; +} + +void HealthScoreProvider::update(HealthScore& healthScore) +{ + healthScore.top = false; + healthScore.usable = false; + healthScore.miserable = false; + + bool batteryBroken = theCognitionState.batteryStatus.batteryBroken; + bool imuBroken = checkImu(); + bool fsrBroken = !theMotionState.fsrStatus.usableLeft && !theMotionState.fsrStatus.usableRight; + bool cameraBroken = !theCameraInfo.usable && !theCameraInfoUpper.usable; // always true needs changes in CameraProviderV6.cpp + + bool jointsBad = checkJoints(); + bool legsUsable = theMotionState.jointStatus.usableLegs; + + float firstOrderFailure = float(0.5f * jointsBad + 0.5f * !legsUsable); + float secondOrderFailure = float(batteryBroken + imuBroken + fsrBroken + cameraBroken); + healthScore.score = std::min(1.f, firstOrderFailure + secondOrderFailure); + + if (healthScore.score > 0.66 && healthScore.score <= 1) + healthScore.miserable = true; + if (healthScore.score > 0.33 && healthScore.score <= 0.66) + healthScore.usable = true; + if (healthScore.score >= 0 && healthScore.score <= 0.33) + healthScore.top = true; +} + +bool HealthScoreProvider::checkImu() +{ + bool accBroken = std::any_of(theMotionState.imuStatus.accStatus.begin(), + theMotionState.imuStatus.accStatus.end(), + [](bool v) + { + return !v; + }); + bool gyroBroken = std::any_of(theMotionState.imuStatus.gyroStatus.begin(), + theMotionState.imuStatus.gyroStatus.end(), + [](bool v) + { + return !v; + }); + bool angleBroken = std::any_of(theMotionState.imuStatus.angleStatus.begin(), + theMotionState.imuStatus.angleStatus.end(), + [](bool v) + { + return !v; + }); + return accBroken || gyroBroken || angleBroken; +} + +bool HealthScoreProvider::checkJoints() +{ + float stumble = theMotionState.walkingStatus.stumble; + + if (stumble > 0.7) + jointsProbablyBad = true; + + return jointsProbablyBad; +} + + +MAKE_MODULE(HealthScoreProvider, cognitionInfrastructure) diff --git a/Src/Modules/Infrastructure/HealthScoreProvider.h b/Src/Modules/Infrastructure/HealthScoreProvider.h new file mode 100644 index 00000000..bd320c85 --- /dev/null +++ b/Src/Modules/Infrastructure/HealthScoreProvider.h @@ -0,0 +1,47 @@ +/** +* @file Modules/Infrastructure/HealthScoreProvider.h +* This file declares a module that provides information about the robot's health. +* @author Dominik Brämer +*/ + +#pragma once + +#include "Tools/Module/Module.h" + +#include "Representations/Perception/CognitionState.h" +#include "Representations/MotionControl/MotionState.h" +#include "Representations/Infrastructure/CameraInfo.h" + +#include "Representations/Infrastructure/HealthScore.h" + + +MODULE(HealthScoreProvider, + REQUIRES(MotionState), + REQUIRES(CognitionState), + REQUIRES(CameraInfo), + REQUIRES(CameraInfoUpper), + + PROVIDES(HealthScore) +); + +/** +* @class HealthScoreProvider +* A module that provides information about the robot's health +*/ +class HealthScoreProvider : public HealthScoreProviderBase +{ +public: + /** Constructor. */ + HealthScoreProvider(); + +private: + /** The main function, called every cycle + * @param healthScore The data struct to be filled + */ + void update(HealthScore& healthScore); + + bool checkImu(); + bool checkJoints(); + + bool jointsProbablyBad = false; +}; diff --git a/Src/Modules/Infrastructure/JointRequestProvider.cpp b/Src/Modules/Infrastructure/JointRequestProvider.cpp new file mode 100644 index 00000000..2dc72d7d --- /dev/null +++ b/Src/Modules/Infrastructure/JointRequestProvider.cpp @@ -0,0 +1,19 @@ +#include "JointRequestProvider.h" + +// Helpful for debugging +#include "Tools/Debugging/DebugDrawings.h" + +void JointRequestProvider::update(JointRequest& jointRequest) +{ + jointRequest = theRawJointRequest; + + for (size_t i = 0; i < jointRequest.angles.size(); ++i) + { + if (jointRequest.angles[i] != SensorData::off && i >= Joints::firstLeftLegJoint) + { + jointRequest.angles[i] += theWalkCalibration.legJointCalibration[i - Joints::firstLeftLegJoint]; + } + } +} + +MAKE_MODULE(JointRequestProvider, motionInfrastructure); diff --git a/Src/Modules/Infrastructure/JointRequestProvider.h b/Src/Modules/Infrastructure/JointRequestProvider.h new file mode 100644 index 00000000..848332b0 --- /dev/null +++ b/Src/Modules/Infrastructure/JointRequestProvider.h @@ -0,0 +1,26 @@ +/** + * @file JointRequestProvider.h + * @author Arne Moos + */ + +#pragma once + +#include "Tools/Module/Module.h" + +// Include all referenced representations here. +#include "Representations/Infrastructure/JointRequest.h" +#include "Representations/MotionControl/WalkCalibration.h" + + +MODULE(JointRequestProvider, + REQUIRES(RawJointRequest), + REQUIRES(WalkCalibration), + PROVIDES(JointRequest) +); + +class JointRequestProvider : public JointRequestProviderBase +{ +private: + // Add an update method for each PROVIDES representation here. + void update(JointRequest& jointRequest); +}; diff --git a/Src/Modules/Infrastructure/LowFrameRateImageProvider.cpp b/Src/Modules/Infrastructure/LowFrameRateImageProvider.cpp index 136045cb..4e702a72 100644 --- a/Src/Modules/Infrastructure/LowFrameRateImageProvider.cpp +++ b/Src/Modules/Infrastructure/LowFrameRateImageProvider.cpp @@ -54,7 +54,7 @@ void LowFrameRateImageProvider::updateImage(LowFrameRateImage& lfrImage, bool up { const Image& image = upper ? (Image&)theImageUpper : theImage; lfrImage.image.setImage(const_cast(image[0])); - lfrImage.image.setResolution(image.width, image.height, image.isFullSize); + lfrImage.image.setResolution(image.width, image.height); lfrImage.image.timeStamp = image.timeStamp; lfrImage.imageUpdated = true; } diff --git a/Src/Modules/Infrastructure/MotionLogDataProvider.h b/Src/Modules/Infrastructure/MotionLogDataProvider.h index 49746b7a..320a58f1 100644 --- a/Src/Modules/Infrastructure/MotionLogDataProvider.h +++ b/Src/Modules/Infrastructure/MotionLogDataProvider.h @@ -15,7 +15,7 @@ #include "Representations/Infrastructure/SensorData/JointSensorData.h" #include "Representations/Infrastructure/SensorData/KeyStates.h" #include "Representations/Infrastructure/SensorData/SystemSensorData.h" -#include "Representations/Infrastructure/SensorData/UsSensorData.h" +#include "Representations/Infrastructure/SensorData/SonarSensorData.h" #include "Representations/Infrastructure/JointRequest.h" #include "Representations/Sensing/FallDownState.h" #include "Representations/Sensing/InertialData.h" @@ -57,7 +57,7 @@ MODULE(MotionLogDataProvider, PROVIDES(RobotInfo), PROVIDES(SpeedRequest), PROVIDES(SystemSensorData), - PROVIDES(UsSensorData), + PROVIDES(SonarSensorData), PROVIDES(WalkCalibration) ); @@ -102,7 +102,7 @@ class MotionLogDataProvider : public MotionLogDataProviderBase, public LogDataPr void update(RobotInfo&) {} void update(SpeedRequest&) {} void update(SystemSensorData&) {} - void update(UsSensorData&) {} + void update(SonarSensorData&) {} void update(WalkCalibration&) {} /** diff --git a/Src/Modules/Infrastructure/NaoProviderV6.cpp b/Src/Modules/Infrastructure/NaoProviderV6.cpp index cf0d0aac..eb7c03b5 100644 --- a/Src/Modules/Infrastructure/NaoProviderV6.cpp +++ b/Src/Modules/Infrastructure/NaoProviderV6.cpp @@ -100,9 +100,11 @@ void NaoProviderV6::send() for (size_t i = 0; i < positions.size(); ++i) { if (positions[i] != SensorData::off) + { positions[i] += theJointDeCalibration.joints[i].offset + theJointCalibration.joints[i].offset; + } } - copyAndCastWithSourceMapping(theJointRequest.angles, actuators.positions, jointsToBase); + copyAndCastWithSourceMapping(positions, actuators.positions, jointsToBase); /* STIFFNESS */ // map int [0...100] to float [0.f...1.f] @@ -129,7 +131,9 @@ void NaoProviderV6::send() { const auto state = theLEDRequest.ledStates[i]; - leds[i] = (state == LEDRequest::on || (state == LEDRequest::blinking && on) || (state == LEDRequest::fastBlinking && fastOn)) ? 1.0f : (state == LEDRequest::half ? 0.5f : 0.0f); + leds[i] = (state == LEDRequest::on || (state == LEDRequest::blinking && on) || (state == LEDRequest::fastBlinking && fastOn)) + ? 1.0f + : ((state == LEDRequest::half || (state == LEDRequest::halfBlinking && on) || (state == LEDRequest::halfFastBlinking && fastOn)) ? 0.5f : 0.0f); } copyAndCastWithTargetMapping(leds, actuators.chestLEDs, chestLEDsFromBase); @@ -144,8 +148,8 @@ void NaoProviderV6::send() copyAndCastWithTargetMapping(leds, actuators.skullLEDs, skullLEDsFromBase); /* SONAR */ - actuators.sonars[0] = false; // unimplemented - actuators.sonars[1] = false; // unimplemented + actuators.sonars[0] = true; + actuators.sonars[1] = true; naoBody.closeActuators(); } @@ -159,12 +163,32 @@ void NaoProviderV6::update(FrameInfo& frameInfo) void NaoProviderV6::update(FsrSensorData& fsrSensorData) { const auto& fsr = naoBody.getSensors().fsr; + unsigned currentTime = theFrameInfo.time; copyAndCastWithTargetMapping(fsr, fsrSensorData.left, lFsrToBase); copyAndCastWithTargetMapping(fsr, fsrSensorData.right, rFsrToBase); fsrSensorData.leftTotal = std::accumulate(fsrSensorData.left.begin(), fsrSensorData.left.end(), 0.f); fsrSensorData.rightTotal = std::accumulate(fsrSensorData.right.begin(), fsrSensorData.right.end(), 0.f); + + // update fsrRef during set + const auto& angle = naoBody.getSensors().angle; + if (theGameInfo.state != STATE_SET) + { + updateFsrRef = true; + setState = false; + } + if (theGameInfo.state == STATE_SET && abs(angle[0]) < 10 && abs(angle[1]) < 10 && !setState) + { + setStarted = currentTime; + setState = true; + } + + if (theGameInfo.state == STATE_SET && abs(angle[0]) < 10 && abs(angle[1]) < 10 && currentTime - setStarted > 7000 && updateFsrRef) + { + fsrSensorData.fsrRef = fsrSensorData.leftTotal + fsrSensorData.rightTotal; + updateFsrRef = false; + } } void NaoProviderV6::update(InertialSensorData& inertialSensorData) @@ -232,7 +256,11 @@ void NaoProviderV6::update(JointSensorData& jointSensorData) copyAndCastWithTargetMapping(sensors.status, jointSensorData.status, jointsToBase); for (size_t i = 0; i < Joints::numOfJoints; i++) - jointSensorData.angles[i] -= theJointDeCalibration.joints[i].offset; + { + jointSensorData.angles[i] -= theJointCalibration.joints[i].offset; + if (i >= Joints::firstLeftLegJoint) + jointSensorData.angles[i] -= theWalkCalibration.legJointCalibration[i - Joints::firstLeftLegJoint]; + } jointSensorData.timestamp = theFrameInfo.time; } @@ -255,7 +283,15 @@ void NaoProviderV6::update(SystemSensorData& systemSensorData) systemSensorData.batteryCurrent = battery[NDData::Battery::current]; systemSensorData.batteryLevel = battery[NDData::Battery::charge]; systemSensorData.batteryTemperature = battery[NDData::Battery::temperature]; - // TODO: what about Battery::status? + systemSensorData.chargingStatus = !(static_cast(battery[NDData::Battery::status]) & 0b100000); +} + +void NaoProviderV6::update(SonarSensorData& sonarSensorData) +{ + const auto& sonar = naoBody.getSensors().sonar; + sonarSensorData.leftDistanceM = sonar.at(0); + sonarSensorData.rightDistanceM = sonar.at(1); + sonarSensorData.timestamp = theFrameInfo.time; } #endif // TARGET_ROBOT diff --git a/Src/Modules/Infrastructure/NaoProviderV6.h b/Src/Modules/Infrastructure/NaoProviderV6.h index 1d643f2d..bb49e9d5 100644 --- a/Src/Modules/Infrastructure/NaoProviderV6.h +++ b/Src/Modules/Infrastructure/NaoProviderV6.h @@ -21,6 +21,8 @@ #include "Representations/Infrastructure/SensorData/JointSensorData.h" #include "Representations/Infrastructure/SensorData/KeyStates.h" #include "Representations/Infrastructure/SensorData/SystemSensorData.h" +#include "Representations/Infrastructure/SensorData/SonarSensorData.h" +#include "Representations/MotionControl/WalkCalibration.h" #include "Tools/Module/Module.h" #include "Tools/RingBufferWithSum.h" #include "Tools/ProcessFramework/CycleLocal.h" @@ -35,11 +37,15 @@ MODULE(NaoProviderV6, PROVIDES(FrameInfo), REQUIRES(FrameInfo), + REQUIRES(GameInfo), + PROVIDES(FsrSensorData), PROVIDES(InertialSensorData), PROVIDES(JointSensorData), PROVIDES(KeyStates), PROVIDES(SystemSensorData), + PROVIDES(SonarSensorData), + USES(WalkCalibration), USES(JointRequest), // Will be accessed in send() LOADS_PARAMETERS(, (Angle)(0.00125_deg) gyroBiasMaxErrorPerFrame, @@ -63,6 +69,9 @@ class NaoProviderV6 : public NaoProviderV6Base RingBufferWithSum gyroBuffer{Vector3a::Zero()}; Vector3a gyroBias = Vector3a::Zero(); bool soundPlayed = false; + bool updateFsrRef = true; + bool setState = false; + unsigned setStarted = 0; unsigned lastBodyTemperatureReadTime = 0; static int firstSensorTimestamp; @@ -225,6 +234,7 @@ class NaoProviderV6 : public NaoProviderV6Base void update(JointSensorData& jointSensorData); void update(KeyStates& keyStates); void update(SystemSensorData& systemSensorData); + void update(SonarSensorData& sonarSensorData); /** The function sends a command to the Nao. */ void send(); @@ -256,6 +266,7 @@ class NaoProviderV6 : public NaoProviderV6Base void update(JointSensorData& jointSensorData) {} void update(KeyStates& keyStates) {} void update(SystemSensorData& systemSensorData) {} + void update(SonarSensorData& sonarSensorData) {} void send(); public: diff --git a/Src/Modules/Infrastructure/Network/EventManager.cpp b/Src/Modules/Infrastructure/Network/EventManager.cpp index 411e0074..afdc7c6b 100644 --- a/Src/Modules/Infrastructure/Network/EventManager.cpp +++ b/Src/Modules/Infrastructure/Network/EventManager.cpp @@ -4,24 +4,48 @@ MAKE_MODULE(EventManager, cognitionInfrastructure); void EventManager::update(TeamCommEvents& events) { - events.sendReasons.clear(); - checkForSendReasons(events.sendReasons); + events.sendReasons = getSendReasons(); const Teammate* newestTeammate = theTeammateData.getNewestTeammate(); const float messageBudgetFactor = std::min(1.f, 0.9f + static_cast(theFrameInfo.getTimeSince(theGameInfo.timeFirstReadyState)) / 900000.f); - events.sendThisFrame = theTeammateData.messageBudget >= 5 // 5 message buffer for safety - && (isTeamEventOldEnough(events.sendReasons) || isLocalEventOldEnough(events.sendReasons)) - && (theTeammateData.messageBudgetFactor >= messageBudgetFactor || !newestTeammate || theFrameInfo.getTimeSince(newestTeammate->timeWhenSent) > static_cast(globalSendLimitWhenMessageRateTooHigh)); + const bool isPenalized = theRobotInfo.penalty != PENALTY_NONE; + const bool eventOldEnough = isTeamEventOldEnough(events.sendReasons) || isLocalEventOldEnough(events.sendReasons); + + bool active = !isPenalized && eventOldEnough; + + // Send one more time when being penalized for ballchaser handover to another player + // Otherwise, no robot may take over if two players have a similiar distance. + const Teammate* teammate = theTeammateData.getNewestEventMessage(TeamCommEvents::SendReason::newBallchaser); + const bool wasStriker = teammate ? teammate->behaviorData.playerNumberToBall == theRobotInfo.number : true; + if (isPenalized && wasStriker && checkForNewBallchaser()) + { + active = true; + events.sendReasons = {TeamCommEvents::SendReason::newBallchaser}; + } + + const bool inMessageBudget = theTeammateData.messageBudgetFactor >= messageBudgetFactor || !newestTeammate + || theFrameInfo.getTimeSince(newestTeammate->sendTimestamp) > static_cast(globalSendLimitWhenMessageRateTooHigh); + + const bool allowedToSend = theTeammateData.messageBudget >= 5 // 5 message buffer for safety + && theRobotInfo.transitionToFramework == 1.f // framework active + && theGameInfo.gamePhase != GAME_PHASE_PENALTYSHOOT; + + events.sendThisFrame = active && allowedToSend && inMessageBudget; if (events.sendThisFrame) for (const auto sendReason : events.sendReasons) newestLocalUpdate[sendReason] = theFrameInfo.time; } -void EventManager::checkForSendReasons(std::vector& sendReasons) +std::vector EventManager::getSendReasons() { + std::vector sendReasons; + + if (checkForNewBallchaser()) + sendReasons.push_back(TeamCommEvents::SendReason::newBallchaser); + if (checkForNewRoleAssignment()) sendReasons.push_back(TeamCommEvents::SendReason::newRolesAssigned); @@ -43,26 +67,53 @@ void EventManager::checkForSendReasons(std::vector& if (checkForBallchaserFallDown()) sendReasons.push_back(TeamCommEvents::SendReason::ballchaserFallDown); - if (theGameSymbols.kickoffInProgress == false && wasKickOffInProgress && theRoleSymbols.role == BehaviorData::RoleAssignment::ballchaser) + if (theGameSymbols.kickoffInProgress == false && wasKickOffInProgress && theBehaviorData.playerNumberToBall == theRobotInfo.number) sendReasons.push_back(TeamCommEvents::SendReason::kickOffFinished); + if (checkForTimeResponses()) + sendReasons.push_back(TeamCommEvents::SendReason::timeResponse); + + // remember states for next frame wasKickOffInProgress = theGameSymbols.kickoffInProgress; - lastFallDownState = theFallDownState.state; + + return sendReasons; +} + +bool EventManager::checkForNewBallchaser() +{ + // do not send static/local ballchaser decision + if (!theBallChaserDecision.dynamic) + return false; + + // do not send position updates after 7s in ready + if (theTacticSymbols.keepRoleAssignment) + return false; + + const Teammate* teammate = theTeammateData.getNewestEventMessage(TeamCommEvents::SendReason::newBallchaser); + const bool wasStriker = teammate ? teammate->behaviorData.playerNumberToBall == theRobotInfo.number : true; + const bool isOrWasStriker = theBehaviorData.playerNumberToBall == theRobotInfo.number || wasStriker; + + return isOrWasStriker && (!teammate || theBehaviorData.playerNumberToBall != teammate->behaviorData.playerNumberToBall); } bool EventManager::checkForNewRoleAssignment() { - const Teammate* teammate = theTeammateData.getNewestEventMessage(TeamCommEvents::SendReason::newRolesAssigned); - const BehaviorData::RoleAssignment myTeamRole = teammate ? teammate->behaviorData.roleSuggestions[theRobotInfo.number] : BehaviorData::RoleAssignment::noRole; - const bool isOrWasStriker = theBehaviorData.role == BehaviorData::RoleAssignment::ballchaser || myTeamRole == BehaviorData::RoleAssignment::ballchaser; - const bool isOrWasKeeperBallChaser = theBehaviorData.role == BehaviorData::RoleAssignment::keeper - && (theBehaviorData.soccerState == BehaviorData::SoccerState::controlBall - || (teammate && teammate->number == theRobotInfo.number && teammate->behaviorData.soccerState == BehaviorData::SoccerState::controlBall)); - if ((isOrWasStriker || isOrWasKeeperBallChaser) && (!teammate || theBehaviorData.roleSuggestions != teammate->behaviorData.roleSuggestions)) + // do not send static role assignments + if (!theRoleSymbols.dynamic) + return false; + + const Teammate* teammate = theTeammateData.getNewestEventMessage(TeamCommEvents::SendReason::newBallchaser); + const bool wasStriker = teammate ? teammate->behaviorData.playerNumberToBall == theRobotInfo.number : false; + const bool isAndWasStriker = theBehaviorData.playerNumberToBall == theRobotInfo.number && wasStriker; + + const Teammate* teammateRole = theTeammateData.getNewestEventMessage(TeamCommEvents::SendReason::newRolesAssigned); + if (!teammateRole) return true; - return false; + const auto& teammateRoleSuggestions = teammateRole->behaviorData.roleSuggestions; + + return isAndWasStriker && theRoleSymbols.roleSuggestions != teammateRoleSuggestions; } bool EventManager::checkForPlayerMoved() @@ -72,21 +123,26 @@ bool EventManager::checkForPlayerMoved() if (theTeamCommSenderOutput.dataSent) lastSendPosition = theRobotPose; + + // do not send position updates after 7s in ready + if (theTacticSymbols.keepRoleAssignment) + return false; + const float distanceMoved = (theRobotPose.translation - lastSendPosition.translation).norm(); const float ballDistance = theBehaviorData.ballPositionRelative.norm(); - const float movedThreshold = (theBehaviorData.role == BehaviorData::RoleAssignment::ballchaser) + const float movedThreshold = (theBehaviorData.playerNumberToBall == theRobotInfo.number) ? eventConfig.playerMovedEventDistanceForBallchaser : ((ballDistance > eventConfig.playerMovedNearBallDistance) ? eventConfig.playerMovedEventDistance : eventConfig.playerMovedEventDistanceForBallchaser); - Vector2f penAreaBottomLeft(theFieldDimensions.xPosOwnGroundline, theFieldDimensions.yPosRightPenaltyArea); - Vector2f penAreaTopRight(theFieldDimensions.xPosOwnPenaltyArea, theFieldDimensions.yPosLeftPenaltyArea); - const bool positionUpdateByAllowedInPenaltyArea = Geometry::isPointInsideRectangle(penAreaBottomLeft, penAreaTopRight, theTeammateData.myself.pose.translation) - != Geometry::isPointInsideRectangle(penAreaBottomLeft, penAreaTopRight, theRobotPoseAfterPreview.translation) - && theFrameInfo.getTimeSince(theTeamCommSenderOutput.dataSentTimestamp) > static_cast(eventConfig.playerMovedEventIntervalPenaltyArea) && distanceMoved > 300.f; + Vector2f goalAreaBottomLeft(theFieldDimensions.xPosOwnGroundline, theFieldDimensions.yPosRightGoalArea); + Vector2f goalAreaTopRight(theFieldDimensions.xPosOwnGoalArea, theFieldDimensions.yPosLeftGoalArea); + const bool positionUpdateByAllowedInGoalArea = Geometry::isPointInsideRectangle(goalAreaBottomLeft, goalAreaTopRight, theTeammateData.myself.robotPose.translation) + != Geometry::isPointInsideRectangle(goalAreaBottomLeft, goalAreaTopRight, theRobotPoseAfterPreview.translation) + && theFrameInfo.getTimeSince(theTeamCommSenderOutput.dataSentTimestamp) > static_cast(eventConfig.playerMovedEventIntervalGoalArea) && distanceMoved > 300.f; const bool playerMoved = distanceMoved > movedThreshold; - if ((positionUpdateInitial || playerMoved || positionUpdateByAllowedInPenaltyArea) - && (theRobotPose.validity > eventConfig.playerMovedEventMinPoseValidity || theTeammateData.myself.timeWhenSent == 0 + if ((positionUpdateInitial || playerMoved || positionUpdateByAllowedInGoalArea) + && (theRobotPose.validity > eventConfig.playerMovedEventMinPoseValidity || theTeammateData.myself.sendTimestamp == 0 || (theRobotPose.validity > eventConfig.playerMovedEventMinPoseValidityInitial && theGameInfo.state == STATE_INITIAL))) return true; @@ -97,7 +153,7 @@ bool EventManager::checkForGoalDetected() { const bool goalDetected = theRawGameInfo.state == STATE_PLAYING && theGameInfo.state == STATE_READY; const Teammate* goalDetectedMate = theTeammateData.getNewestEventMessage(TeamCommEvents::SendReason::goalDetected); - if (goalDetected && (!goalDetectedMate || theFrameInfo.getTimeSince(goalDetectedMate->timeWhenSent) > static_cast(eventConfig.goalDetectedMinTimeDiff))) + if (goalDetected && (!goalDetectedMate || theFrameInfo.getTimeSince(goalDetectedMate->sendTimestamp) > static_cast(eventConfig.goalDetectedMinTimeDiff))) return true; return false; @@ -114,7 +170,7 @@ bool EventManager::checkForSymmetryLost() bool EventManager::checkForSymmetryUpdate() { const Teammate* teammateSymmetryLost = theTeammateData.getNewestEventMessage(TeamCommEvents::SendReason::symmetryLost); - if (teammateSymmetryLost && theFrameInfo.getTimeSince(theTeamCommSenderOutput.dataSentTimestamp) > 500 && teammateSymmetryLost->timeWhenSent > theTeamCommSenderOutput.dataSentTimestamp + if (teammateSymmetryLost && theFrameInfo.getTimeSince(theTeamCommSenderOutput.dataSentTimestamp) > 500 && teammateSymmetryLost->sendTimestamp > theTeamCommSenderOutput.dataSentTimestamp && theBehaviorData.timeSinceBallWasSeen < 1500) // the time since seen has to be in sync with the time used in SelfLocator2017! return true; @@ -141,12 +197,27 @@ bool EventManager::checkForBallMoved() bool EventManager::checkForBallchaserFallDown() { - if (theRoleSymbols.role == BehaviorData::RoleAssignment::ballchaser && theFallDownState.state != FallDownState::State::upright && lastFallDownState == FallDownState::State::upright) + if (theBehaviorData.playerNumberToBall == theRobotInfo.number && theFallDownState.state != FallDownState::State::upright && !theTeammateData.myself.fallen) return true; else return false; } +bool EventManager::checkForTimeResponses() +{ + // Limit this to a reasonable number of messages + if (theTeammateData.statistic.events[TeamCommEvents::SendReason::timeResponse] >= 100) + return false; + + for (const auto& req : theTimeSynchronization.receivedRequests) + { + // collect multiple requests over a period of one second + if (theFrameInfo.getTimeSince(req.received) > 1000) + return true; + } + return false; +} + bool EventManager::isTeamEventOldEnough(const std::vector& sendReasons) const { for (const TeamCommEvents::SendReason sendReason : sendReasons) @@ -157,7 +228,7 @@ bool EventManager::isTeamEventOldEnough(const std::vectortimeWhenSent) > static_cast(eventInterval.interval)) + if (!teammate || theFrameInfo.getTimeSince(teammate->sendTimestamp) > static_cast(eventInterval.interval)) return true; } } diff --git a/Src/Modules/Infrastructure/Network/EventManager.h b/Src/Modules/Infrastructure/Network/EventManager.h index d36656e1..540853a3 100644 --- a/Src/Modules/Infrastructure/Network/EventManager.h +++ b/Src/Modules/Infrastructure/Network/EventManager.h @@ -12,6 +12,8 @@ #include "Representations/BehaviorControl/BehaviorData.h" #include "Representations/BehaviorControl/GameSymbols.h" #include "Representations/BehaviorControl/RoleSymbols.h" +#include "Representations/BehaviorControl/RoleSymbols/PositioningSymbols.h" +#include "Representations/BehaviorControl/BallChaserDecision.h" #include "Representations/Configuration/FieldDimensions.h" #include "Representations/Infrastructure/FrameInfo.h" #include "Representations/Infrastructure/GameInfo.h" @@ -23,6 +25,8 @@ #include "Representations/Modeling/RobotPose.h" #include "Representations/Modeling/SideConfidence.h" #include "Representations/Sensing/FallDownState.h" +#include "Representations/Infrastructure/Time.h" +#include "Representations/BehaviorControl/TacticSymbols.h" MODULE(EventManager, @@ -41,6 +45,10 @@ MODULE(EventManager, REQUIRES(RoleSymbols), REQUIRES(SideConfidence), REQUIRES(TeammateData), + REQUIRES(PositioningSymbols), + REQUIRES(TimeSynchronization), + REQUIRES(BallChaserDecision), + REQUIRES(TacticSymbols), USES(TeamCommSenderOutput), PROVIDES(TeamCommEvents), LOADS_PARAMETERS( @@ -55,7 +63,7 @@ MODULE(EventManager, (float)(500.f) playerMovedEventDistanceForNearBall, (float)(1500.f) playerMovedNearBallDistance, (unsigned)(5000) playerMovedEventIntervalInitial, - (unsigned)(1000) playerMovedEventIntervalPenaltyArea, + (unsigned)(1000) playerMovedEventIntervalGoalArea, (float)(0.7f) playerMovedEventMinPoseValidity, (float)(0.4f) playerMovedEventMinPoseValidityInitial, (unsigned)(20000) goalDetectedMinTimeDiff, @@ -77,7 +85,8 @@ class EventManager : public EventManagerBase bool isTeamEventOldEnough(const std::vector& sendReasons) const; bool isLocalEventOldEnough(const std::vector& sendReasons) const; - void checkForSendReasons(std::vector& sendReasons); + std::vector getSendReasons(); + bool checkForNewBallchaser(); bool checkForNewRoleAssignment(); bool checkForPlayerMoved(); bool checkForGoalDetected(); @@ -85,10 +94,10 @@ class EventManager : public EventManagerBase bool checkForSymmetryUpdate(); bool checkForBallMoved(); bool checkForBallchaserFallDown(); + bool checkForTimeResponses(); // member variables std::array newestLocalUpdate{0}; Pose2f lastSendPosition; bool wasKickOffInProgress = false; - FallDownState::State lastFallDownState = FallDownState::State::upright; }; diff --git a/Src/Modules/Infrastructure/Network/TeamCommDataPacker.cpp b/Src/Modules/Infrastructure/Network/TeamCommDataPacker.cpp index e673874e..19f8a3f1 100644 --- a/Src/Modules/Infrastructure/Network/TeamCommDataPacker.cpp +++ b/Src/Modules/Infrastructure/Network/TeamCommDataPacker.cpp @@ -13,83 +13,27 @@ MAKE_MODULE(TeamCommDataPacker, cognitionInfrastructure) void TeamCommDataPacker::update(TeamCommOutput& teamCommOutput) { - MessageQueue queue; - queue.setSize(sizeof(RoboCup::SPLStandardMessage)); // isn't this too big? + DECLARE_PLOT("module:TeamCommDataPacker:messageSize"); teamCommOutput.sendThisFrame = false; if (theTeamCommEvents.sendThisFrame) { - const auto teamOutput = [&](MessageID id, const auto& representation) - { - queue.out.bin << representation; - queue.out.finishMessage(id); - }; - - // Ordering is important here: if message is too large, data is removed from last to first! - - // Own pose information and ball observation: - teamOutput(idTimeSynchronization, theTimeSynchronization); - teamOutput(idRobotPose, RobotPoseCompressed(theRobotPose)); - teamOutput(idSideConfidence, theSideConfidence); - teamOutput(idBallModel, BallModelCompressed(theBallModel)); - - // Information about the behavior (i.e. the robot's state and intentions) - teamOutput(idBehaviorData, theBehaviorData); - teamOutput(idTeamCommEvents, theTeamCommEvents); - teamOutput(idMotionRequest, WalkRequestCompressed(theMotionInfo.walkRequest)); - - // Robot status - //teamOutput(idRobotHealth, theRobotHealth); // Quite big and currently completely unused - - // Whistle detection state - teamOutput(idWhistleDortmund, theWhistleDortmund); - - // Speed info for remote robot map creation - teamOutput(idSpeedInfo, SpeedInfoCompressed(theSpeedInfo)); - - // Obstacle stuff last, since size is unknown and possibly large - teamOutput(idRobotMap, RobotMapCompressed(theRobotMap)); - teamOutput(idLocalRobotMap, LocalRobotMapCompressed(theLocalRobotMap)); - - // fill SPLStandardMessage header - teamCommOutput = fillStandardMessage(queue); - teamCommOutput.sendThisFrame = true; + Teammate teammate; + teammate.playerNumber = static_cast(theRobotInfo.number); + teammate.teamNumber = static_cast(theOwnTeamInfo.teamNumber); + teammate.sendTimestamp = SystemCall::getCurrentSystemTime(); + teammate.fallen = theFallDownState.state != FallDownState::State::upright; + teammate.timeSynchronization = theTimeSynchronization; + teammate.robotPose = RobotPoseCompressed(theRobotPose); + teammate.ballModel = BallModelCompressed(theBallModel); + teammate.behaviorData = BehaviorDataCompressed(theBehaviorData); + teammate.teamCommEvents = TeamCommEventsCompressed(theTeamCommEvents); + teammate.whistle = WhistleDortmundCompressed(theWhistleDortmund); + teammate.speedInfo = SpeedInfoCompressed(theSpeedInfo); + teammate.localRobotMap = RobotMapCompressed(theLocalRobotMap); + + teamCommOutput = teammate.toTeamCommData(); + PLOT("module:TeamCommDataPacker:messageSize", teamCommOutput.data.size()); } } - -TeamCommOutput TeamCommDataPacker::fillStandardMessage(MessageQueue& queue) -{ - TeamCommOutput message; - - // header time stamps will be filled in team handler - OutBinarySize sizeStream; - sizeStream << queue; - message.numOfDataBytes = static_cast(sizeStream.getSize() + sizeof(NaoDevilsHeader)); - while (message.numOfDataBytes > SPL_STANDARD_MESSAGE_DATA_SIZE) - { - queue.removeLastMessage(); - OutBinarySize sizeTest; - sizeTest << queue; - message.numOfDataBytes = static_cast(sizeTest.getSize() + sizeof(NaoDevilsHeader)); - OUTPUT_ERROR("TeamCommDataPacker: TeamComm package too large: removed one message"); - } - - BH_TRACE_MSG("after remove packages stuff"); - { - OutBinaryMemory memory(message.getNDData()); - memory << queue; - } - - message.playerNum = static_cast(theRobotInfo.number); - message.teamNum = static_cast(theOwnTeamInfo.teamNumber); - message.ballAge = theBallModel.timeWhenLastSeen ? static_cast(theFrameInfo.getTimeSince(theBallModel.timeWhenLastSeen)) / 1000.f : -1.f; // time in seconds - message.ball[0] = theBallModel.estimate.position.x(); - message.ball[1] = theBallModel.estimate.position.y(); - message.pose[0] = theRobotPose.translation.x(); - message.pose[1] = theRobotPose.translation.y(); - message.pose[2] = theRobotPose.rotation; - message.fallen = theFallDownState.state != FallDownState::upright; - - return message; -} diff --git a/Src/Modules/Infrastructure/Network/TeamCommDataPacker.h b/Src/Modules/Infrastructure/Network/TeamCommDataPacker.h index 559362a6..82dc55f1 100644 --- a/Src/Modules/Infrastructure/Network/TeamCommDataPacker.h +++ b/Src/Modules/Infrastructure/Network/TeamCommDataPacker.h @@ -7,59 +7,33 @@ #pragma once -#include "Tools/Module/Module.h" -#include "Representations/BehaviorControl/BehaviorData.h" -#include "Representations/BehaviorControl/RoleSymbols/Ballchaser.h" -#include "Representations/BehaviorControl/PositioningSymbols.h" -#include "Representations/Infrastructure/FrameInfo.h" -#include "Representations/Infrastructure/GameInfo.h" -#include "Representations/Infrastructure/TeamCommEvents.h" -#include "Representations/Infrastructure/TeammateData.h" -#include "Representations/Infrastructure/TeamInfo.h" -#include "Representations/Infrastructure/RobotHealth.h" #include "Representations/Infrastructure/RobotInfo.h" -#include "Representations/Infrastructure/TeamCommData.h" +#include "Representations/Infrastructure/TeamInfo.h" #include "Representations/Infrastructure/Time.h" -#include "Representations/Perception/RobotsPercept.h" -#include "Representations/Modeling/RobotMap.h" -#include "Representations/Modeling/SimpleRobotsDistributed.h" #include "Representations/Modeling/RobotPose.h" +#include "Representations/Modeling/BallModel.h" +#include "Representations/BehaviorControl/BehaviorData.h" +#include "Representations/Infrastructure/TeamCommEvents.h" #include "Representations/Modeling/WhistleDortmund.h" -#include "Representations/Sensing/GroundContactState.h" -#include "Representations/Sensing/FallDownState.h" -#include "Representations/Perception/CameraMatrix.h" -#include "Representations/MotionControl/MotionInfo.h" -#include "Representations/MotionControl/MotionRequest.h" #include "Representations/MotionControl/SpeedInfo.h" +#include "Representations/Modeling/RobotMap.h" +#include "Representations/Infrastructure/TeammateData.h" +#include "Representations/Infrastructure/TeamCommData.h" +#include "Representations/Sensing/FallDownState.h" +#include "Tools/Module/Module.h" MODULE(TeamCommDataPacker, + REQUIRES(RobotInfo), + REQUIRES(OwnTeamInfo), + REQUIRES(TimeSynchronization), + REQUIRES(RobotPose), REQUIRES(BallModel), REQUIRES(BehaviorData), - REQUIRES(CameraMatrix), - REQUIRES(FallDownState), - REQUIRES(FrameInfo), - REQUIRES(GameInfo), - REQUIRES(RawGameInfo), - REQUIRES(GroundContactState), - REQUIRES(Ballchaser), - REQUIRES(MotionInfo), - REQUIRES(MotionRequest), + REQUIRES(TeamCommEvents), + REQUIRES(WhistleDortmund), REQUIRES(SpeedInfo), - REQUIRES(OwnTeamInfo), - REQUIRES(PositioningSymbols), - REQUIRES(RobotHealth), - REQUIRES(RobotInfo), - REQUIRES(RobotsPercept), - REQUIRES(RobotsPerceptUpper), REQUIRES(LocalRobotMap), - REQUIRES(RobotMap), - REQUIRES(RobotPose), - REQUIRES(SideConfidence), - REQUIRES(SimpleRobotsDistributed), - REQUIRES(TeammateData), - REQUIRES(WhistleDortmund), - REQUIRES(TeamCommEvents), - REQUIRES(TimeSynchronization), + REQUIRES(FallDownState), PROVIDES(TeamCommOutput) ); @@ -80,5 +54,5 @@ class TeamCommDataPacker : public TeamCommDataPackerBase /** * Fills the SPLStandardMessage Header. */ - TeamCommOutput fillStandardMessage(MessageQueue& queue); + void fillStandardMessage(MessageQueue& queue, TeamCommData& message); }; diff --git a/Src/Modules/Infrastructure/Network/TeamCommLocalSocketProvider.cpp b/Src/Modules/Infrastructure/Network/TeamCommLocalSocketProvider.cpp new file mode 100644 index 00000000..39bd4226 --- /dev/null +++ b/Src/Modules/Infrastructure/Network/TeamCommLocalSocketProvider.cpp @@ -0,0 +1,77 @@ +#include "TeamCommLocalSocketProvider.h" + +#include "Tools/Build.h" +#include "Tools/Settings.h" +#include "Tools/Debugging/DebugDrawings.h" + + +MAKE_MODULE(TeamCommLocalSocketProvider, cognitionInfrastructure); + +std::array>, MAX_NUM_PLAYERS>, 2> TeamCommLocalSocketProvider::messages; +std::array, 2> TeamCommLocalSocketProvider::mutex; + +TeamCommLocalSocketProvider::~TeamCommLocalSocketProvider() +{ + ASSERT(theOwnTeamInfo.teamNumber == 1 || theOwnTeamInfo.teamNumber == 2); + + std::unique_lock l(mutex[theOwnTeamInfo.teamNumber - 1][theRobotInfo.number - 1]); + messages[theOwnTeamInfo.teamNumber - 1][theRobotInfo.number - 1].reset(); +} + +void TeamCommLocalSocketProvider::update(TeamCommSocket& teamCommSocket) +{ + teamCommSocket.send = [=](const TeamCommData& msg) + { + return this->send(msg); + }; + + teamCommSocket.receive = [=]() + { + return this->receive(); + }; +} + +bool TeamCommLocalSocketProvider::send(const TeamCommData& teamCommData) +{ + ASSERT(theOwnTeamInfo.teamNumber == 1 || theOwnTeamInfo.teamNumber == 2); + + std::unique_lock l(mutex[theOwnTeamInfo.teamNumber - 1][theRobotInfo.number - 1]); + auto& msg = messages[theOwnTeamInfo.teamNumber - 1][theRobotInfo.number - 1]; + if (msg) + { + ++std::get(*msg); + std::get(*msg) = teamCommData; + } + else + { + msg = std::make_tuple(1, TeamCommData(teamCommData)); + } + + return true; +} + +std::vector TeamCommLocalSocketProvider::receive() +{ + ASSERT(theOwnTeamInfo.teamNumber == 1 || theOwnTeamInfo.teamNumber == 2); + + std::vector ret; + auto m = mutex[theOwnTeamInfo.teamNumber - 1].begin(); + + const auto& teamMessages = messages[theOwnTeamInfo.teamNumber - 1]; + for (size_t num = 0; num < teamMessages.size(); ++num) + { + const auto& robotMessage = teamMessages[num]; + + std::shared_lock l(*m++); + if (robotMessage && std::get(*robotMessage) != lastMessage[num]) + { + lastMessage[num] = std::get(*robotMessage); + + TeamCommDataReceived& received = ret.emplace_back(); + received.data = std::get(*robotMessage).data; + received.receiveTimestamp = SystemCall::getCurrentSystemTime(); + } + } + + return ret; +} diff --git a/Src/Modules/Infrastructure/Network/TeamCommLocalSocketProvider.h b/Src/Modules/Infrastructure/Network/TeamCommLocalSocketProvider.h new file mode 100644 index 00000000..94dbdd0d --- /dev/null +++ b/Src/Modules/Infrastructure/Network/TeamCommLocalSocketProvider.h @@ -0,0 +1,44 @@ +/** + * @file TeamCommLocalSocketProvider.h + * This file provides a TeamCommSocket without network communication. + * @author Aaron Larisch + */ + +#pragma once + +#include "Tools/Module/Module.h" + +#include "Representations/Infrastructure/TeamCommSocket.h" +#include "Representations/Infrastructure/RobotInfo.h" +#include "Representations/Infrastructure/TeamInfo.h" +#include +#include +#include +#include +#include + +MODULE(TeamCommLocalSocketProvider, + REQUIRES(RobotInfo), + REQUIRES(OwnTeamInfo), + PROVIDES_WITHOUT_MODIFY(TeamCommSocket) +); + +class TeamCommLocalSocketProvider : public TeamCommLocalSocketProviderBase +{ +private: + // This static is intentional allowing communication between different instances of this module. + // In theory, we have to use a queue here, but since all robots are running synchronously, a single buffer per robot is fine. + static std::array>, MAX_NUM_PLAYERS>, 2> messages; + static std::array, 2> mutex; + + std::array lastMessage{0}; + +public: + ~TeamCommLocalSocketProvider(); + + bool send(const TeamCommData& teamCommData); + + std::vector receive(); + + void update(TeamCommSocket& teamCommSocket); +}; diff --git a/Src/Modules/Infrastructure/Network/TeamCommSender.cpp b/Src/Modules/Infrastructure/Network/TeamCommSender.cpp index 31af4daf..855e0392 100644 --- a/Src/Modules/Infrastructure/Network/TeamCommSender.cpp +++ b/Src/Modules/Infrastructure/Network/TeamCommSender.cpp @@ -4,10 +4,15 @@ MAKE_MODULE(TeamCommSender, cognitionInfrastructure); void TeamCommSender::update(TeamCommSenderOutput& teamCommDataSenderOutput) { - if (theTeamCommOutput.sendThisFrame && theRobotInfo.transitionToFramework == 1.f && theRobotInfo.penalty == PENALTY_NONE) - teamCommDataSenderOutput.dataSent = theTeamCommSocket.send(theTeamCommOutput); - else - teamCommDataSenderOutput.dataSent = false; + teamCommDataSenderOutput.dataSent = false; + + if (theTeamCommOutput.sendThisFrame) + { + if (size_t size = theTeamCommOutput.data.size(); size <= TeamCommOutput::maximumSize) + teamCommDataSenderOutput.dataSent = theTeamCommSocket.send(theTeamCommOutput); + else + OUTPUT_ERROR("TeamCommSender: Message too big! (" << size << " > " << TeamCommOutput::maximumSize << ")"); + } if (teamCommDataSenderOutput.dataSent) teamCommDataSenderOutput.dataSentTimestamp = theFrameInfo.time; diff --git a/Src/Modules/Infrastructure/Network/TeamCommSender.h b/Src/Modules/Infrastructure/Network/TeamCommSender.h index 75791b20..037dcb7d 100644 --- a/Src/Modules/Infrastructure/Network/TeamCommSender.h +++ b/Src/Modules/Infrastructure/Network/TeamCommSender.h @@ -15,7 +15,6 @@ #include "Representations/Infrastructure/TeamCommSenderOutput.h" MODULE(TeamCommSender, - REQUIRES(RobotInfo), REQUIRES(TeamCommOutput), REQUIRES(TeamCommSocket), REQUIRES(FrameInfo), diff --git a/Src/Modules/Infrastructure/Network/TeamCommUDPSocketProvider.cpp b/Src/Modules/Infrastructure/Network/TeamCommUDPSocketProvider.cpp index 04ad6acf..792cb56c 100644 --- a/Src/Modules/Infrastructure/Network/TeamCommUDPSocketProvider.cpp +++ b/Src/Modules/Infrastructure/Network/TeamCommUDPSocketProvider.cpp @@ -10,13 +10,16 @@ TeamCommUDPSocketProvider::TeamCommUDPSocketProvider() {} void TeamCommUDPSocketProvider::update(TeamCommSocket& teamCommSocket) { + bool local = Build::targetSimulator(); + MODIFY("module:TeamCommUDPSocketProvider:local", local); + int teamPort = 0; - if constexpr (Build::target == Build::Target::Simulator) + if (local) teamPort = Global::getSettings().teamPort; else teamPort = theOwnTeamInfo.teamPort; - if (this->port != teamPort) + if (this->port != teamPort || this->local != local) { if (this->port) { @@ -24,7 +27,7 @@ void TeamCommUDPSocketProvider::update(TeamCommSocket& teamCommSocket) OUTPUT_TEXT("TeamCommUDPSocketProvider: Set port to " << teamPort); } - if constexpr (Build::target == Build::Target::Simulator) + if (local) startLocal(teamPort); else start(teamPort, "10.0.255.255"); @@ -62,6 +65,7 @@ void TeamCommUDPSocketProvider::start(int port, const char* subnet) { ASSERT(!this->port); this->port = port; + this->local = false; socket.setBlocking(false); VERIFY(socket.setBroadcast(true)); @@ -81,77 +85,57 @@ bool TeamCommUDPSocketProvider::send(const TeamCommData& teamCommData) if (!port) return false; - // Plot usage of data buffer in percent: - DECLARE_PLOT("module:TeamCommUDPSocketProvider:standardMessageDataBufferUsageInPercent"); - const float usageInPercent = teamCommData.numOfDataBytes * 100.f / static_cast(SPL_STANDARD_MESSAGE_DATA_SIZE); - PLOT("module:TeamCommUDPSocketProvider:standardMessageDataBufferUsageInPercent", usageInPercent); - - TeamCommData sendTeamCommData = teamCommData; - sendTeamCommData.getNDHeader().sendTimestamp = SystemCall::getCurrentSystemTime(); - - return socket.write(sendTeamCommData.getData(), sendTeamCommData.getSize()); + return socket.write(teamCommData.data.data(), static_cast(teamCommData.data.size())); } -std::vector TeamCommUDPSocketProvider::receive() +std::vector TeamCommUDPSocketProvider::receive() { - std::vector messages; + std::vector messages; if (!port) return messages; // not started yet messages.reserve(maxNumOfTcMessages); - int size; + int size = 0; do { - TeamCommData message; + TeamCommDataReceived& message = messages.emplace_back(); - size = local ? socket.readLocal(message.getData(), sizeof(RoboCup::SPLStandardMessage)) : socket.read(message.getData(), sizeof(RoboCup::SPLStandardMessage), message.remoteIp); - if (size >= TeamCommData::headerSize && size <= static_cast(sizeof(RoboCup::SPLStandardMessage))) + // Allocate one byte more than necessary + message.data.resize(TeamCommDataReceived::maximumSize + 1); + + size = local + ? socket.readLocal(message.data.data(), static_cast(message.data.size())) + : socket.read(message.data.data(), static_cast(message.data.size()), *reinterpret_cast(message.remoteIp.data())); + if (size <= 0) { - // This could be much later than the package was received by the hardware. - // For our time measurements, this should be enough. - message.receiveTimestamp = SystemCall::getCurrentSystemTime(); - - if (messages.size() >= maxNumOfTcMessages) - { - OUTPUT_WARNING("Received too many packages, ignoring package from " << message.remoteIp); - return messages; - } - - if (checkMessage(message, size - TeamCommData::headerSize)) - messages.push_back(std::move(message)); + messages.pop_back(); + continue; } - } while (size > 0); - return messages; -} - -bool TeamCommUDPSocketProvider::checkMessage(const TeamCommData& msg, int realNumOfDataBytes) -{ - if (msg.playerNum < Global::getSettings().lowestValidPlayerNumber || msg.playerNum > Global::getSettings().highestValidPlayerNumber || msg.teamNum != theOwnTeamInfo.teamNumber) - return false; + // Check if message is bigger than expected + if (size > static_cast(TeamCommDataReceived::maximumSize)) + { + OUTPUT_WARNING("Received packet with invalid size from " << message.remoteIp[3] << "." << message.remoteIp[2] << "." << message.remoteIp[1] << "." << message.remoteIp[0]); + messages.pop_back(); + continue; + } - if (msg.numOfDataBytes != static_cast(realNumOfDataBytes)) - { - OUTPUT_WARNING("SPL Message: numOfDataBytes is '" << msg.numOfDataBytes << "' but realNumOfDataBytes is '" << realNumOfDataBytes << "'. Ignoring package..."); - return false; - } + // Adjust buffer to actual size + message.data.resize(size); - if (!msg.verify()) - { - OUTPUT_WARNING("Invalid package from ip " << msg.remoteIp << ". Ignoring package..."); - return false; - } + // This could be much later than the package was received by the hardware. + // For our time measurements, this should be enough. + message.receiveTimestamp = SystemCall::getCurrentSystemTime(); - if constexpr (Build::target == Build::Target::Robot) - { - if (msg.teamNum != theOwnTeamInfo.teamNumber) + if (messages.size() >= maxNumOfTcMessages) { - OUTPUT_WARNING("Received package from ip " << msg.remoteIp << " with team number '" << msg.teamNum << "'. Ignoring package..."); - return false; + OUTPUT_WARNING("Packet buffer is full, ignoring further ones (last from " + << message.remoteIp[3] << "." << message.remoteIp[2] << "." << message.remoteIp[1] << "." << message.remoteIp[0] << ")"); + return messages; } - } + } while (size > 0); - return true; + return messages; } diff --git a/Src/Modules/Infrastructure/Network/TeamCommUDPSocketProvider.h b/Src/Modules/Infrastructure/Network/TeamCommUDPSocketProvider.h index 50b38763..d680cf18 100644 --- a/Src/Modules/Infrastructure/Network/TeamCommUDPSocketProvider.h +++ b/Src/Modules/Infrastructure/Network/TeamCommUDPSocketProvider.h @@ -12,7 +12,6 @@ #include "Representations/Infrastructure/TeamCommSocket.h" #include "Representations/Infrastructure/RobotInfo.h" #include "Representations/Infrastructure/TeamInfo.h" -#include "Representations/Infrastructure/USBSettings.h" #include "Tools/Network/UdpComm.h" MODULE(TeamCommUDPSocketProvider, @@ -57,13 +56,7 @@ class TeamCommUDPSocketProvider : public TeamCommUDPSocketProviderBase * The method receives packages if available. * @return The number of bytes received. */ - std::vector receive(); - - /** - * Checks if a received message is a valid SPLStandardMessage. - * @return True, if message is valid. - */ - bool checkMessage(const TeamCommData& msg, int realNumOfDataBytes); + std::vector receive(); void update(TeamCommSocket& teamCommSocket); }; diff --git a/Src/Modules/Infrastructure/Network/TeammateDataProvider.cpp b/Src/Modules/Infrastructure/Network/TeammateDataProvider.cpp index ee577147..bc1acf91 100644 --- a/Src/Modules/Infrastructure/Network/TeammateDataProvider.cpp +++ b/Src/Modules/Infrastructure/Network/TeammateDataProvider.cpp @@ -1,8 +1,6 @@ /** * @file Modules/Infrastructure/TeammateDataProvider.cpp - * This file implements a temporary wrapper from the old to the new TeammateData structure. * @author Aaron Larisch - * @author Tim Laue */ #include "TeammateDataProvider.h" @@ -11,44 +9,56 @@ #include #include -/** - * This macro unpacks compressed representations. It reads - * representationCompressed from the MessageQueue and unpacks it into - * member. - */ -#define UNPACK(representation, member) \ - representation##Compressed the##representation##Compressed; \ - message.bin >> the##representation##Compressed; \ - teammate.member = static_cast(the##representation##Compressed); - void TeammateDataProvider::update(TeammateData& teammateData) { // reset message budget to GC value before adding messages if (theGameInfo.timeLastPackageReceived == theFrameInfo.time) teammateData.messageBudget = theOwnTeamInfo.messageBudget; + // in INITIAL, FINISHED and PENALTY_SHOOTOUT, message budget is ignored - // otherwise set to starting value, see Rules 2022 + // otherwise set to starting value, see Rules 2023 if (theGameInfo.state == STATE_INITIAL || theGameInfo.state == STATE_FINISHED || theGameInfo.gamePhase == GAME_PHASE_PENALTYSHOOT) - teammateData.messageBudget = (theGameInfo.competitionType == COMPETITION_TYPE_7V7) ? 1680 : 1200; + teammateData.messageBudget = 1200; - for (auto& message : theTeamCommInput.messages) + for (const auto& message : theTeamCommInput.messages) { - Teammate* teammate = nullptr; - if (message.playerNum == theRobotInfo.number) - teammate = &teammateData.myself; - else if (isLatencyOkay(message)) - teammate = &getTeammate(teammateData, message); - else - continue; + TeammateReceived* teammate = nullptr; + { + TeammateReceived newTeammate; + + if (!newTeammate.fromTeamCommData(message)) + continue; + + if (newTeammate.teamNumber != theOwnTeamInfo.teamNumber) + continue; + + newTeammate.updateTimestamps(theTimeOffsets); + + // Look for existing teammate + if (newTeammate.playerNumber == theRobotInfo.number) + teammate = &teammateData.myself; + else if (isLatencyOkay(newTeammate)) + teammate = teammateData.getPlayer(newTeammate.playerNumber); + else + continue; + + // Add new teammate if it doesn't + if (!teammate) + teammate = &teammateData.teammates.emplace_back(); + + *teammate = std::move(newTeammate); + } // Count valid team messages (including own), but only in READY, SET or PLAYING if (theGameInfo.state == STATE_READY || theGameInfo.state == STATE_SET || theGameInfo.state == STATE_PLAYING) { --teammateData.messageBudget; - ++messageCounts[message.playerNum]; - } - fillTeammate(message, *teammate); + ++teammateData.statistic.players[teammate->playerNumber - 1]; + ++teammateData.statistic.states[theGameInfo.state]; + for (TeamCommEvents::SendReason reason : teammate->teamCommEvents.sendReasons) + ++teammateData.statistic.events[reason]; + } // update newestEventMessages for (const auto reason : teammate->teamCommEvents.sendReasons) @@ -61,25 +71,19 @@ void TeammateDataProvider::update(TeammateData& teammateData) eventmessage = &teammateevent.message; } - if (teammate->timeWhenSent > eventmessage->timeWhenSent || (teammate->timeWhenSent == eventmessage->timeWhenSent && teammate->number < eventmessage->number)) + if (teammate->sendTimestamp > eventmessage->sendTimestamp || (teammate->sendTimestamp == eventmessage->sendTimestamp && teammate->playerNumber < eventmessage->playerNumber)) *eventmessage = *teammate; } } - // Overwrite teammate status with GameController information + // Update teammate status with GameController information for (auto& teammate : teammateData.teammates) - { - if (teammate.behaviorData.soccerState == BehaviorData::penalized || theOwnTeamInfo.players[teammate.number - 1].penalty != PENALTY_NONE) - teammate.status = Teammate::INACTIVE; - else if (!teammate.isUpright) - teammate.status = Teammate::ACTIVE; - else - teammate.status = Teammate::FULLY_ACTIVE; - } + teammate.updateStatus(theOwnTeamInfo); + teammateData.myself.updateStatus(theOwnTeamInfo); - const auto isActiveTeammate = [](const Teammate& teammate) + const auto isActiveTeammate = [](const TeammateReceived& teammate) { - return teammate.status != Teammate::INACTIVE; + return teammate.status != TeammateReceived::Status::INACTIVE; }; teammateData.numberOfActiveTeammates = static_cast(std::count_if(teammateData.teammates.begin(), teammateData.teammates.end(), isActiveTeammate)); @@ -101,12 +105,11 @@ void TeammateDataProvider::update(TeammateData& teammateData) teammateData.messageBudgetFactor = std::min(1.f, static_cast(teammateData.messageBudget) / totalSecsRemaining); } -bool TeammateDataProvider::isLatencyOkay(const TeamCommData& message) +bool TeammateDataProvider::isLatencyOkay(const TeammateReceived& teammate) { - unsigned int sendTimestamp = message.getNDHeader().sendTimestamp; - theTimeOffsets.convertRemoteTimeInLocalTime(sendTimestamp, message.playerNum); - - if (std::abs(static_cast(theFrameInfo.time) - static_cast(sendTimestamp)) > badWifiTimeout) + // Send timestamp may be in the future if time synchronization wasn't successful. + // Discard package here, otherwise we keep packages in newestEventMessages forever. + if (std::abs(static_cast(teammate.receiveTimestamp) - static_cast(teammate.sendTimestamp)) > badWifiTimeout) { wlanQuality = std::max(0.f, wlanQuality - 0.2f); return false; @@ -118,114 +121,4 @@ bool TeammateDataProvider::isLatencyOkay(const TeamCommData& message) } } -Teammate& TeammateDataProvider::getTeammate(TeammateData& teammateData, const TeamCommData& message) -{ - // Try to find the robot in the current list of robots: - for (auto& teammate : teammateData.teammates) - { - if (teammate.number == message.playerNum) - return teammate; - } - - // This seems to be a new robot that is not part of the list yet: - teammateData.teammates.emplace_back(); - Teammate& teammate = teammateData.teammates.back(); - return teammate; -} - - -void TeammateDataProvider::fillTeammate(InMessage& message, Teammate& teammate) const -{ - switch (message.getMessageID()) - { - case idRobotPose: - { - UNPACK(RobotPose, pose); - break; - } - case idWhistleDortmund: - { - message.bin >> teammate.whistle; - theTimeOffsets.convertRemoteTimeInLocalTime(teammate.whistle.lastDetectionTime, teammate.number); - break; - } - case idBallModel: - { - BallModel lastBallModel = teammate.ball; - UNPACK(BallModel, ball); - theTimeOffsets.convertRemoteTimeInLocalTime(teammate.ball.timeWhenLastSeen, teammate.number); - theTimeOffsets.convertRemoteTimeInLocalTime(teammate.ball.timeWhenDisappeared, teammate.number); - if (!teammate.isNDevilsPlayer && (teammate.ball.timeWhenLastSeen - lastBallModel.timeWhenLastSeen) < 16) - teammate.ball.lastPerception = lastBallModel.lastPerception; - break; - } - case idRobotsPercept: - { - UNPACK(RobotsPercept, robotsPercept); - break; - } - case idRobotsPerceptUpper: - { - UNPACK(RobotsPerceptUpper, robotsPerceptUpper); - break; - } - case idRobotMap: - { - UNPACK(RobotMap, robotMap); - break; - } - case idLocalRobotMap: - { - UNPACK(LocalRobotMap, localRobotMap); - break; - } - case idSideConfidence: - message.bin >> teammate.sideConfidence; - break; - case idBehaviorData: - message.bin >> teammate.behaviorData; - break; - case idSimpleRobotsDistributed: - message.bin >> teammate.simpleRobotsDistributed; - break; - case idMotionRequest: - { - UNPACK(WalkRequest, walkRequest); - break; - } - case idSpeedInfo: - { - UNPACK(SpeedInfo, speedInfo); - break; - } - case idTeamCommEvents: - message.bin >> teammate.teamCommEvents; - break; - } -} - -// Only sets upright and pose information, since ball information is in ball model. -// Could be merged in the future. -void TeammateDataProvider::fillTeammate(const TeamCommData& message, Teammate& teammate) const -{ - teammate.timeWhenLastPacketReceived = message.receiveTimestamp; - teammate.timeWhenSent = message.getNDHeader().sendTimestamp; - theTimeOffsets.convertRemoteTimeInLocalTime(teammate.timeWhenSent, message.playerNum); - teammate.isNDevilsPlayer = true; - teammate.number = message.playerNum; - - teammate.isUpright = !message.fallen; - teammate.pose.translation.x() = message.pose[0]; - teammate.pose.translation.y() = message.pose[1]; - teammate.pose.rotation = Angle::normalize(message.pose[2]); - - MessageQueue queue; - message.fillMessageQueue(queue); - queue.handleAllMessages( - [&](InMessage& message) - { - return fillTeammate(message, teammate); - }); -} - MAKE_MODULE(TeammateDataProvider, cognitionInfrastructure) diff --git a/Src/Modules/Infrastructure/Network/TeammateDataProvider.h b/Src/Modules/Infrastructure/Network/TeammateDataProvider.h index b27aed22..5e96754e 100644 --- a/Src/Modules/Infrastructure/Network/TeammateDataProvider.h +++ b/Src/Modules/Infrastructure/Network/TeammateDataProvider.h @@ -1,13 +1,10 @@ /** * @file Modules/Infrastructure/TeammateDataProvider.h - * This file declares a temporary wrapper from the old to the new TeammateData structure. * @author Aaron Larisch - * @author Tim Laue */ #pragma once -#include "Representations/Configuration/FieldDimensions.h" #include "Representations/Infrastructure/FrameInfo.h" #include "Representations/Infrastructure/GameInfo.h" #include "Representations/Infrastructure/RobotInfo.h" @@ -15,7 +12,6 @@ #include "Representations/Infrastructure/TeammateData.h" #include "Representations/Infrastructure/TeamCommData.h" #include "Representations/Infrastructure/Time.h" -#include "Representations/MotionControl/MotionInfo.h" #include "Representations/MotionControl/MotionRequest.h" #include "Representations/MotionControl/SpeedInfo.h" #include "Representations/Perception/CameraMatrix.h" @@ -23,26 +19,15 @@ MODULE(TeammateDataProvider, - REQUIRES(FieldDimensions), REQUIRES(FrameInfo), - REQUIRES(GameInfo), - REQUIRES(MotionInfo), REQUIRES(OwnTeamInfo), + REQUIRES(GameInfo), REQUIRES(RobotInfo), - REQUIRES(CameraMatrix), - REQUIRES(CameraMatrixUpper), REQUIRES(TeamCommInput), REQUIRES(TimeOffsets), - USES(MotionRequest), - USES(RobotPose), // to compare team model to own - USES(BallModel), // to compare team model to own PROVIDES(TeammateData), DEFINES_PARAMETERS(, - (int)(1000) sendInterval, /** < Time in ms between two messages that are sent to the teammates */ - (int)(4000) networkTimeout, /**< Time in ms after which teammates are considered as unconnected */ - (int)(2500) badWifiTimeout, /**< Time in ms after which a package is considered too old and discarded. */ - (bool)(false) useMixedTeamBallModel, - (float)(-0.5f) minSanityForTeammates + (int)(2500) badWifiTimeout /**< Time in ms after which a package is considered too old and discarded. */ ) ); @@ -55,7 +40,6 @@ class TeammateDataProvider : public TeammateDataProviderBase private: float wlanQuality = 1.f; /**< [0..1] Quality of wifi */ size_t lastNoOfTeammates = 0; - unsigned messageCounts[MAX_NUM_PLAYERS] = {0}; /**< Message Counter for all players for debugging. */ /** The main function, called every cycle * @param teammateData The data struct to be filled @@ -64,29 +48,8 @@ class TeammateDataProvider : public TeammateDataProviderBase /** * Check if package is too old (slow wireless). - * @param message Received package. + * @param message Received teammate. * @return If package is new enough. */ - bool isLatencyOkay(const TeamCommData& message); - - /** - * Returns the representation of the robot that has the "robotNumber". - * If the robot does not exist yet, the object will be created. - * @param message The message of the robot to look for. - */ - static Teammate& getTeammate(TeammateData& teammateData, const TeamCommData& message); - - /** - * The method is called for every incoming team message and fills a given team mate. - * @param message An interface to read the message from the queue. - * @param teammate The team mate to be filled. - */ - void fillTeammate(InMessage& message, Teammate& teammate) const; - - /** - * Fill Teammate using TeamCommData. - * @param message Team communication data - * @param teammate Team mate - */ - void fillTeammate(const TeamCommData& message, Teammate& teammate) const; + bool isLatencyOkay(const TeammateReceived& teammate); }; diff --git a/Src/Modules/Infrastructure/Network/TimeProvider.cpp b/Src/Modules/Infrastructure/Network/TimeProvider.cpp index 70d23b85..819159fe 100644 --- a/Src/Modules/Infrastructure/Network/TimeProvider.cpp +++ b/Src/Modules/Infrastructure/Network/TimeProvider.cpp @@ -4,6 +4,7 @@ #include "Tools/Settings.h" #include "Platform/BHAssert.h" #include "Tools/MessageQueue/MessageQueue.h" +#include "Representations/Infrastructure/TeammateData.h" #include MAKE_MODULE(TimeProvider, cognitionInfrastructure); @@ -16,39 +17,36 @@ void TimeProvider::execute(tf::Subflow&) for (const auto& message : theTeamCommInput.messages) { - MessageQueue queue; - message.fillMessageQueue(queue); - queue.handleAllMessages( - [&](InMessage& inmessage) - { - if (inmessage.getMessageID() == idTimeSynchronization) - { - TimeSynchronization ts; - inmessage.bin >> ts; + TeammateReceived teammate; + if (!teammate.fromTeamCommData(message)) + continue; + + if (teammate.teamNumber != theOwnTeamInfo.teamNumber) + continue; + + const TimeSynchronization& ts = teammate.timeSynchronization; - // answer on request or never answered before - if ((ts.requestFrom & (1 << (theRobotInfo.number - 1))) > 0 || !answered[message.playerNum - 1]) - { - auto& request = receivedRequests[message.playerNum]; - request.sent = message.getNDHeader().sendTimestamp; - request.received = message.receiveTimestamp; + // answer on request or never answered before + if ((ts.requestFrom & (1 << (theRobotInfo.number - 1))) > 0 || !answered[teammate.playerNumber - 1]) + { + auto& request = receivedRequests[teammate.playerNumber]; + request.sent = teammate.sendTimestamp; + request.received = message.receiveTimestamp; - answered[message.playerNum - 1] = true; - } + answered[teammate.playerNumber - 1] = true; + } - // collect roundtrips - for (const auto& request : ts.receivedRequests) - { - if (request.player == theRobotInfo.number) - { - auto& roundtrip = roundtrips[message.playerNum]; - roundtrip.request = request; - roundtrip.response.sent = message.getNDHeader().sendTimestamp; - roundtrip.response.received = message.receiveTimestamp; - } - } - } - }); + // collect roundtrips + for (const auto& request : ts.receivedRequests) + { + if (request.player == theRobotInfo.number) + { + auto& roundtrip = roundtrips[teammate.playerNumber]; + roundtrip.request = request; + roundtrip.response.sent = teammate.sendTimestamp; + roundtrip.response.received = message.receiveTimestamp; + } + } } } diff --git a/Src/Modules/Infrastructure/Network/TimeProvider.h b/Src/Modules/Infrastructure/Network/TimeProvider.h index d29b024c..8c6aebfa 100644 --- a/Src/Modules/Infrastructure/Network/TimeProvider.h +++ b/Src/Modules/Infrastructure/Network/TimeProvider.h @@ -9,6 +9,7 @@ #include "Tools/Module/Module.h" #include "Representations/Infrastructure/RoboCupGameControlData.h" #include "Representations/Infrastructure/RobotInfo.h" +#include "Representations/Infrastructure/TeamInfo.h" #include "Representations/Infrastructure/TeamCommData.h" #include "Representations/Infrastructure/Time.h" #include "Representations/Infrastructure/TeamCommSenderOutput.h" @@ -17,6 +18,7 @@ MODULE(TimeProvider, REQUIRES(RobotInfo), + REQUIRES(OwnTeamInfo), REQUIRES(TeamCommInput), REQUIRES(TimeOffsets), USES(TeamCommSenderOutput), diff --git a/Src/Modules/Infrastructure/PortAudioRecorder.cpp b/Src/Modules/Infrastructure/PortAudioRecorder.cpp index ce93f49a..6f369ae4 100644 --- a/Src/Modules/Infrastructure/PortAudioRecorder.cpp +++ b/Src/Modules/Infrastructure/PortAudioRecorder.cpp @@ -131,6 +131,11 @@ void PortAudioRecorder::update(AudioData& audioData) { OUTPUT_ERROR("PortAudioRecorder: Pa_ReadStream failed: " << Pa_GetErrorText(paerr) << "(" << paerr << ")"); audioData.samples.clear(); + + // restart stream immediately after timeout + if (paerr == paTimedOut) + Pa_StopStream(stream); + return; } audioData.isValid = true; diff --git a/Src/Modules/Infrastructure/RawGameInfoProvider.cpp b/Src/Modules/Infrastructure/RawGameInfoProvider.cpp index 7b76cd76..14a1a513 100644 --- a/Src/Modules/Infrastructure/RawGameInfoProvider.cpp +++ b/Src/Modules/Infrastructure/RawGameInfoProvider.cpp @@ -34,6 +34,9 @@ void RawGameInfoProvider::update(OwnTeamInfo& ownTeamInfo) lastTeamNumberUpdate = theUSBSettings.updateTimestamp; OUTPUT_TEXT("RawGameInfoProvider: Set team number " << ownTeamInfo.teamNumber << " and team port " << ownTeamInfo.teamPort << "!"); } + + if (ownTeamInfo.goalkeeper > 0 && ownTeamInfo.goalkeeper != 1) + OUTPUT_WARNING("RawGameInfoProvider: Player number " << ownTeamInfo.goalkeeper << " is supposed to be the goalkeeper, which is not supported!"); } void RawGameInfoProvider::update(RawGameInfo& rawGameInfo) @@ -44,6 +47,8 @@ void RawGameInfoProvider::update(RawGameInfo& rawGameInfo) rawGameInfo.oppTeamNumber = gameCtrlData.teams[gameCtrlData.teams[0].teamNumber == this->teamNumber ? 1 : 0].teamNumber; rawGameInfo.timeLastPackageReceived = lastReceivedTimeStamp; + rawGameInfo.controllerConnected = theFrameInfo.getTimeSince(rawGameInfo.timeLastPackageReceived) < gameControllerTimeout; + rawGameInfo.remoteIp = gameCtrlDataSender; if (lastGameState != rawGameInfo.state) { if (lastGameState == STATE_INITIAL && rawGameInfo.state == STATE_READY) @@ -100,8 +105,11 @@ void RawGameInfoProvider::execute(tf::Subflow&) if (gameCtrlData.teams[0].teamNumber == 0 && gameCtrlData.teams[1].teamNumber == 0) { gameCtrlData.teams[0].teamNumber = static_cast(this->teamNumber); - gameCtrlData.teams[0].teamColour = static_cast(ownColor); - gameCtrlData.teams[1].teamColour = static_cast(oppColor); + gameCtrlData.teams[0].fieldPlayerColour = ownColor; + gameCtrlData.teams[0].goalkeeperColour = ownKeeperColor; + gameCtrlData.teams[0].goalkeeper = keeper; + gameCtrlData.teams[1].fieldPlayerColour = oppColor; + gameCtrlData.teams[1].goalkeeperColour = oppKeeperColor; } else // already received packages; reset game and robot infos { @@ -136,7 +144,7 @@ bool RawGameInfoProvider::receive() int size; RoboCup::RoboCupGameControlData buffer; struct sockaddr_in from; - while (udp && (size = udp->read((char*)&buffer, sizeof(buffer), from)) > 0) + while (udp && (size = udp->read(reinterpret_cast(&buffer), sizeof(buffer), from)) > 0) { if (size == sizeof(buffer) && !std::memcmp(&buffer, GAMECONTROLLER_STRUCT_HEADER, 4) && buffer.version == GAMECONTROLLER_STRUCT_VERSION && (buffer.teams[0].teamNumber == this->teamNumber || buffer.teams[1].teamNumber == this->teamNumber)) @@ -148,6 +156,8 @@ bool RawGameInfoProvider::receive() udp->setTarget(inet_ntoa(gameControllerAddress), GAMECONTROLLER_RETURN_PORT); } + *reinterpret_cast(gameCtrlDataSender.data()) = ntohl(from.sin_addr.s_addr); + received = true; } } @@ -157,18 +167,18 @@ bool RawGameInfoProvider::receive() bool RawGameInfoProvider::send() { RoboCup::RoboCupGameControlReturnData returnPacket; - returnPacket.playerNum = (uint8_t)theRobotInfo.number; - returnPacket.teamNum = (uint8_t)this->teamNumber; + returnPacket.playerNum = static_cast(theRobotInfo.number); + returnPacket.teamNum = static_cast(this->teamNumber); returnPacket.fallen = theFallDownState.state != FallDownState::State::upright; returnPacket.pose[0] = theRobotPose.translation.x(); returnPacket.pose[1] = theRobotPose.translation.y(); returnPacket.pose[2] = theRobotPose.rotation; - returnPacket.ballAge = (theBallModel.timeWhenLastSeen == 0) ? -1.f : theFrameInfo.getTimeSince(theBallModel.timeWhenLastSeen); + returnPacket.ballAge = (theBallModel.timeWhenLastSeen == 0) ? -1.f : theFrameInfo.getTimeSince(theBallModel.timeWhenLastSeen) / 1000.f; returnPacket.ball[0] = theBallModel.estimate.position.x(); returnPacket.ball[1] = theBallModel.estimate.position.y(); - return !udp || udp->write((const char*)&returnPacket, sizeof(returnPacket)); + return !udp || udp->write(reinterpret_cast(&returnPacket), sizeof(returnPacket)); } void RawGameInfoProvider::initialize() @@ -176,7 +186,7 @@ void RawGameInfoProvider::initialize() udp = std::make_unique(); if (!udp->setBlocking(false) || !udp->setBroadcast(true) || !udp->bind("0.0.0.0", GAMECONTROLLER_DATA_PORT) || !udp->setLoopback(false)) { - fprintf(stderr, "libgamectrl: Could not open UDP port\n"); + fprintf(stderr, "RawGameInfoProvider: Could not open UDP port\n"); udp.reset(); // continue, because button interface will still work initialized = false; @@ -195,15 +205,16 @@ void RawGameInfoProvider::initialize() void RawGameInfoProvider::updateGameState() { bool timeOut = theFrameInfo.getTimeSince(lastReceivedTimeStamp) > gameControllerTimeout; - int timeInGameState = theFrameInfo.getTimeSince(timeStampGameStateChanged); - int timeSincePenaltyChange = theFrameInfo.getTimeSince(timeStampPenaltyChanged); ownPenalty = gameCtrlData.teams[gameCtrlData.teams[0].teamNumber == this->teamNumber ? 0 : 1].players[theRobotInfo.number - 1].penalty; - if (transitionToFramework < 1.f) + if (transitionToFramework == 0.f) { memset(&gameCtrlData, 0, sizeof(gameCtrlData)); + if (theKeyStates.pressed[KeyStates::chest]) + firstChestButtonPressed = true; } - else if (timeOut && timeInGameState > 1000 && timeSincePenaltyChange > 1000) + else if (timeOut && !firstChestButtonPressed && !lastChestButtonPressed && theKeyStates.pressed[KeyStates::chest] && !theKeyStates.pressed[KeyStates::headFront] + && !theKeyStates.pressed[KeyStates::headMiddle] && !theKeyStates.pressed[KeyStates::headRear]) { if (theBehaviorData.behaviorState >= BehaviorData::BehaviorState::firstCalibrationState) { @@ -216,11 +227,8 @@ void RawGameInfoProvider::updateGameState() { case STATE_INITIAL: { - if (chestButtonPressed && !theKeyStates.pressed[KeyStates::chest]) - { - gameCtrlData.state = STATE_PLAYING; - ownPenalty = PENALTY_MANUAL; - } + gameCtrlData.state = STATE_PLAYING; + ownPenalty = PENALTY_MANUAL; break; } case STATE_SET: @@ -234,15 +242,17 @@ void RawGameInfoProvider::updateGameState() gameCtrlData.state = STATE_PLAYING; case STATE_PLAYING: { - if (theKeyStates.pressed[KeyStates::chest]) - ownPenalty = (ownPenalty != PENALTY_NONE) ? PENALTY_NONE : PENALTY_MANUAL; + ownPenalty = (ownPenalty != PENALTY_NONE) ? PENALTY_NONE : PENALTY_MANUAL; } default: break; } } } - chestButtonPressed = theKeyStates.pressed[KeyStates::chest] && !theKeyStates.pressed[KeyStates::headFront] && !theKeyStates.pressed[KeyStates::headMiddle] - && !theKeyStates.pressed[KeyStates::headRear]; + if (!theKeyStates.pressed[KeyStates::chest]) + firstChestButtonPressed = false; + + lastChestButtonPressed = theKeyStates.pressed[KeyStates::chest]; + gameCtrlData.teams[gameCtrlData.teams[0].teamNumber == this->teamNumber ? 0 : 1].players[theRobotInfo.number - 1].penalty = ownPenalty; } diff --git a/Src/Modules/Infrastructure/RawGameInfoProvider.h b/Src/Modules/Infrastructure/RawGameInfoProvider.h index 4a7d26d3..524107b1 100644 --- a/Src/Modules/Infrastructure/RawGameInfoProvider.h +++ b/Src/Modules/Infrastructure/RawGameInfoProvider.h @@ -7,6 +7,7 @@ #pragma once #include +#include #if defined(MACOS) || defined(LINUX) #include "arpa/inet.h" @@ -48,9 +49,12 @@ MODULE(RawGameInfoProvider, ((RobotInfo) NaoType) naoBodyType, ///< H21, H25 ... ((RobotInfo) NaoType) naoHeadType, (std::string)("") robotConfig, - (unsigned)(TEAM_YELLOW) ownColor, - (unsigned)(TEAM_BLACK) oppColor, - (int)(2000) gameControllerTimeout, + (unsigned char)(TEAM_YELLOW) ownColor, + (unsigned char)(TEAM_RED) ownKeeperColor, + (unsigned char)(TEAM_BLACK) oppColor, + (unsigned char)(TEAM_BLUE) oppKeeperColor, + (unsigned char)(1) keeper, + (int)(5000) gameControllerTimeout, (int)(1000) aliveDelay ) ); @@ -88,10 +92,12 @@ class RawGameInfoProvider : public RawGameInfoProviderBase bool initialized = false; RoboCup::RoboCupGameControlData gameCtrlData; // local copy of all game control data + std::array gameCtrlDataSender{0}; uint8_t ownPenalty = PENALTY_NONE; unsigned timeStampGameStateChanged = 0; unsigned timeStampPenaltyChanged = 0; - bool chestButtonPressed = false; + bool lastChestButtonPressed = false; + bool firstChestButtonPressed = false; float transitionToFramework = 0.f; unsigned lastReceivedTimeStamp = 0; std::unique_ptr udp = nullptr; /**< The socket used to communicate. */ diff --git a/Src/Modules/Infrastructure/SequenceImageProvider.cpp b/Src/Modules/Infrastructure/SequenceImageProvider.cpp index a61a6f9c..3478d5ff 100644 --- a/Src/Modules/Infrastructure/SequenceImageProvider.cpp +++ b/Src/Modules/Infrastructure/SequenceImageProvider.cpp @@ -67,7 +67,7 @@ void SequenceImageProvider::updateImage(SequenceImage& lfrImage, bool upper) con { const Image& image = upper ? (Image&)theImageUpper : theImage; lfrImage.image.setImage(const_cast(image[0])); - lfrImage.image.setResolution(image.width, image.height, image.isFullSize); + lfrImage.image.setResolution(image.width, image.height); lfrImage.image.timeStamp = image.timeStamp; lfrImage.noInSequence = currentCounterOfConsecutiveFrames; } diff --git a/Src/Modules/Infrastructure/ThumbnailProvider.cpp b/Src/Modules/Infrastructure/ThumbnailProvider.cpp index 33ea17d0..738985ff 100644 --- a/Src/Modules/Infrastructure/ThumbnailProvider.cpp +++ b/Src/Modules/Infrastructure/ThumbnailProvider.cpp @@ -104,7 +104,7 @@ void ThumbnailProvider::shrinkNxN(const Image& srcImage, Thumbnail::ThumbnailIma memset(summs, 0, destImage.width * sizeof(pixelSum)); const Image::Pixel* pSrc; - Thumbnail::ThumbnailImage::PixelType* pDest; + Thumbnail::ThumbnailImage::PixelType* pDest = nullptr; pixelSum* pSumms; for (int y = 0; y < height; ++y) @@ -159,7 +159,7 @@ void ThumbnailProvider::shrink8x8SSE(const Image& srcImage, Thumbnail::Thumbnail memset(summs, 0, summsSize); const Image::Pixel* pSrc; - Thumbnail::ThumbnailImage::PixelType* pDest; + Thumbnail::ThumbnailImage::PixelType* pDest = nullptr; __m128i* pSumms; __m128i tmp; @@ -228,7 +228,7 @@ void ThumbnailProvider::shrink4x4SSE(const Image& srcImage, Thumbnail::Thumbnail memset(summs, 0, summsSize); const Image::Pixel* pSrc; - Thumbnail::ThumbnailImage::PixelType* pDest; + Thumbnail::ThumbnailImage::PixelType* pDest = nullptr; __m128i* pSumms; __m128i tmp; @@ -290,7 +290,7 @@ void ThumbnailProvider::shrinkGrayscaleNxN(const Image& srcImage, Thumbnail::Thu memset(summs, 0, destImage.width * sizeof(unsigned int)); const Image::Pixel* pSrc; - Thumbnail::ThumbnailImageGrayscale::PixelType* pDest; + Thumbnail::ThumbnailImageGrayscale::PixelType* pDest = nullptr; unsigned int* pSumms; for (int y = 0; y < height; ++y) @@ -354,7 +354,7 @@ void ThumbnailProvider::shrinkGrayscale8x8SSE(const Image& srcImage, Thumbnail:: memset(summs, 0, summsSize); const Image::Pixel* pSrc; - Thumbnail::ThumbnailImageGrayscale::PixelType* pDest; + Thumbnail::ThumbnailImageGrayscale::PixelType* pDest = nullptr; __m128i* pSumms; __m128i p0; @@ -379,8 +379,8 @@ void ThumbnailProvider::shrinkGrayscale8x8SSE(const Image& srcImage, Thumbnail:: p0 = _mm_loadu_si128(reinterpret_cast(pSrc)); p1 = _mm_loadu_si128(reinterpret_cast(pSrc + 4)); - p0 = SHUFFLE(p0, mMask); // y0 y1 y2 y3 0 0 0 0 0 0 0 0 0 0 0 0 - p1 = SHUFFLE(p1, mMask); // y4 y5 y6 y7 0 0 0 0 0 0 0 0 0 0 0 0 + p0 = _mm_shuffle_epi8(p0, mMask); // y0 y1 y2 y3 0 0 0 0 0 0 0 0 0 0 0 0 + p1 = _mm_shuffle_epi8(p1, mMask); // y4 y5 y6 y7 0 0 0 0 0 0 0 0 0 0 0 0 p0 = _mm_unpacklo_epi32(p0, p1); // y0 y1 y2 y3 y4 y5 y6 y7 0 0 0 0 0 0 0 0 p0 = _mm_unpacklo_epi8(p0, zero); // y0 y1 y2 y3 y4 y5 y6 y7 @@ -450,7 +450,7 @@ void ThumbnailProvider::shrinkGrayscale4x4SSE(const Image& srcImage, Thumbnail:: memset(summs, 0, summsSize); const Image::Pixel* pSrc; - Thumbnail::ThumbnailImageGrayscale::PixelType* pDest; + Thumbnail::ThumbnailImageGrayscale::PixelType* pDest = nullptr; __m128i* pSumms; __m128i p0; @@ -471,8 +471,8 @@ void ThumbnailProvider::shrinkGrayscale4x4SSE(const Image& srcImage, Thumbnail:: p0 = _mm_loadu_si128(reinterpret_cast(pSrc)); p1 = _mm_loadu_si128(reinterpret_cast(pSrc + 4)); - p0 = SHUFFLE(p0, mMask); // y0 y1 y2 y3 0 0 0 0 0 0 0 0 0 0 0 0 - p1 = SHUFFLE(p1, mMask); // y4 y5 y6 y7 0 0 0 0 0 0 0 0 0 0 0 0 + p0 = _mm_shuffle_epi8(p0, mMask); // y0 y1 y2 y3 0 0 0 0 0 0 0 0 0 0 0 0 + p1 = _mm_shuffle_epi8(p1, mMask); // y4 y5 y6 y7 0 0 0 0 0 0 0 0 0 0 0 0 p0 = _mm_unpacklo_epi32(p0, p1); // y0 y1 y2 y3 y4 y5 y6 y7 0 0 0 0 0 0 0 0 p0 = _mm_unpacklo_epi8(p0, zero); // y0 y1 y2 y3 y4 y5 y6 y7 diff --git a/Src/Modules/Infrastructure/USBMounter.cpp b/Src/Modules/Infrastructure/USBMounter.cpp index c900e2f2..a697c15b 100644 --- a/Src/Modules/Infrastructure/USBMounter.cpp +++ b/Src/Modules/Infrastructure/USBMounter.cpp @@ -32,61 +32,63 @@ void USBMounter::update(USBStatus& usbStatus) if constexpr (!Build::targetRobot()) { usbStatus.status = USBStatus::MountStatus::notMounted; - return; } - - usbStatus.path = mountPath; - usbStatus.logPath = mountPath + "/" + logDirectory; - - // apply initial status from constructor - if (initialStatus != USBStatus::MountStatus::inactive) + else { - usbStatus.status = initialStatus; - initialStatus = USBStatus::MountStatus::inactive; - } - // ignores transitionToFramework = 1 during initialization - if (theRobotInfo.transitionToFramework == 0.f) - active = true; - if (!active) - return; - - formatBehavior(); + usbStatus.path = mountPath; + usbStatus.logPath = mountPath + "/" + logDirectory; - switch (usbStatus.status) - { - case USBStatus::MountStatus::inactive: - if (theRobotInfo.transitionToFramework > 0.f) - { - usbStatus.status = USBStatus::MountStatus::mounting; - nextMountStatus = std::async(std::launch::async, &USBMounter::mount, this); - } - break; - case USBStatus::MountStatus::mounting: - case USBStatus::MountStatus::unmounting: - if (nextMountStatus.valid() && nextMountStatus.wait_for(std::chrono::seconds(0)) == std::future_status::ready) - usbStatus.status = nextMountStatus.get(); - - if (usbStatus.status == USBStatus::MountStatus::readOnly || usbStatus.status == USBStatus::MountStatus::readWrite) - usbStatus.mountTimestamp = theFrameInfo.time; - break; - case USBStatus::MountStatus::readOnly: - case USBStatus::MountStatus::readWrite: - if (theRobotInfo.transitionToFramework == 0.f) + // apply initial status from constructor + if (initialStatus != USBStatus::MountStatus::inactive) { - usbStatus.status = USBStatus::MountStatus::unmounting; - nextMountStatus = std::async(std::launch::async, &USBMounter::unmount, this); + usbStatus.status = initialStatus; + initialStatus = USBStatus::MountStatus::inactive; } - break; - case USBStatus::MountStatus::notMounted: + + // ignores transitionToFramework = 1 during initialization if (theRobotInfo.transitionToFramework == 0.f) + active = true; + if (!active) + return; + + formatBehavior(); + + switch (usbStatus.status) { - usbStatus.status = USBStatus::MountStatus::inactive; + case USBStatus::MountStatus::inactive: + if (theRobotInfo.transitionToFramework > 0.f) + { + usbStatus.status = USBStatus::MountStatus::mounting; + nextMountStatus = std::async(std::launch::async, &USBMounter::mount, this); + } + break; + case USBStatus::MountStatus::mounting: + case USBStatus::MountStatus::unmounting: + if (nextMountStatus.valid() && nextMountStatus.wait_for(std::chrono::seconds(0)) == std::future_status::ready) + usbStatus.status = nextMountStatus.get(); + + if (usbStatus.status == USBStatus::MountStatus::readOnly || usbStatus.status == USBStatus::MountStatus::readWrite) + usbStatus.mountTimestamp = theFrameInfo.time; + break; + case USBStatus::MountStatus::readOnly: + case USBStatus::MountStatus::readWrite: + if (theRobotInfo.transitionToFramework == 0.f) + { + usbStatus.status = USBStatus::MountStatus::unmounting; + nextMountStatus = std::async(std::launch::async, &USBMounter::unmount, this); + } + break; + case USBStatus::MountStatus::notMounted: + if (theRobotInfo.transitionToFramework == 0.f) + { + usbStatus.status = USBStatus::MountStatus::inactive; + } + break; + case USBStatus::MountStatus::unknown: + // TODO: handling + break; } - break; - case USBStatus::MountStatus::unknown: - // TODO: handling - break; } } diff --git a/Src/Modules/Infrastructure/WhistleHandlerDortmund.cpp b/Src/Modules/Infrastructure/WhistleHandlerDortmund.cpp index d6844194..c665b7f8 100644 --- a/Src/Modules/Infrastructure/WhistleHandlerDortmund.cpp +++ b/Src/Modules/Infrastructure/WhistleHandlerDortmund.cpp @@ -64,7 +64,7 @@ void WhistleHandlerDortmund::update(GameInfo& gameInfo) const bool localGoalDetected = theWhistleDortmund.lastDetectionTime > 0 && theFrameInfo.getTimeSince(theWhistleDortmund.lastDetectionTime) < 3000 && ((std::abs(localTimeDiffGoalToWhistle) < maxTimediffWhistleToGoal && theBallModel.validity > 0.7) || (std::abs(remoteTimeDiffGoalToWhistle) < maxTimediffWhistleToGoal && theRemoteBallModel.validity > 0.7)); - const bool remoteGoalDetected = goalDetectedMate && theWhistleDortmund.lastDetectionTime > 0 && goalDetectedMate->whistle.lastDetectionTime > 0 + const bool remoteGoalDetected = goalDetectedMate && theWhistleDortmund.lastDetectionTime > 0 && goalDetectedMate->whistle.lastDetectionTime > 1 && theFrameInfo.getTimeSince(theWhistleDortmund.lastDetectionTime) < timeWindow && theFrameInfo.getTimeSince(goalDetectedMate->whistle.lastDetectionTime) < timeWindow; if (theRawGameInfo.state == STATE_PLAYING && !inSetPlay && (localGoalDetected || remoteGoalDetected)) { diff --git a/Src/Modules/Modeling/DangerMapProvider/DangerMapProvider.cpp b/Src/Modules/Modeling/DangerMapProvider/DangerMapProvider.cpp index 25078a0d..dd649dff 100644 --- a/Src/Modules/Modeling/DangerMapProvider/DangerMapProvider.cpp +++ b/Src/Modules/Modeling/DangerMapProvider/DangerMapProvider.cpp @@ -44,7 +44,7 @@ void DangerMapProvider::updateDanger() // from team mate data for (auto& mate : theTeammateData.teammates) { - for (auto& robot : mate.robotMap.robots) + for (auto& robot : mate.localRobotMap.robots) { if (robot.robotType == RobotEstimate::teammateRobot) continue; @@ -63,4 +63,4 @@ bool DangerMapProvider::isViewBlocked(const RobotMap& robotMap, const Vector2f& return false; } -MAKE_MODULE(DangerMapProvider, modeling) \ No newline at end of file +MAKE_MODULE(DangerMapProvider, modeling) diff --git a/Src/Modules/Modeling/IMUModelProvider/IMUModelProvider.cpp b/Src/Modules/Modeling/IMUModelProvider/IMUModelProvider.cpp index 6b552770..778bb33d 100644 --- a/Src/Modules/Modeling/IMUModelProvider/IMUModelProvider.cpp +++ b/Src/Modules/Modeling/IMUModelProvider/IMUModelProvider.cpp @@ -72,18 +72,14 @@ void IMUModelProvider::update(IMUModel& imuModel) ukf_rot.generateSigmaPoints(); - Eigen::Vector3d u_rot(0, 0, 0); // TODO: use generated angular acceleration as control vector? - ukf_rot.predict(u_rot, theFrameInfo.cycleTime); + // TODO: use generated angular acceleration as control vector? + ukf_rot.predict(Vector3d::Zero(), theFrameInfo.cycleTime); // don't generate sigma points again because the process noise would be applied a second time // ukf.generateSigmaPoints(); - Eigen::Vector3d gyro; - gyro << theInertialSensorData.gyro.x(), theInertialSensorData.gyro.y(), theInertialSensorData.gyro.z(); - Eigen::Vector3d acceleration = Eigen::Vector3d(theInertialSensorData.acc.x(), theInertialSensorData.acc.y(), theInertialSensorData.acc.z()); - IMU_RotationMeasurement z; - z << acceleration.normalized(), gyro; + z << theInertialSensorData.acc.cast().normalized(), theInertialSensorData.gyro.cast(); if (isWalking) { @@ -103,7 +99,7 @@ void IMUModelProvider::update(IMUModel& imuModel) // TODO: Odometry as location measurement? // TODO: velocity of trunk in supfoot / local robot frame as velocity measurement // TODO: really needs bias removal or "calibration" of g - IMU_AccMeasurementGlobal z_acc = ukf_rot.state.getRotationAsQuaternion()._transformVector(acceleration); + const IMUAccMeasurementGlobal z_acc = ukf_rot.state.getRotationAsQuaternion()._transformVector(theInertialSensorData.acc.cast()); if (isWalking) ukf_acc_global.Q = Q_acc_walk; @@ -112,8 +108,8 @@ void IMUModelProvider::update(IMUModel& imuModel) ukf_acc_global.generateSigmaPoints(); - Eigen::Vector3d u_acc(0, 0, 0); // TODO: use generated jerk as control vector? - ukf_acc_global.predict(u_acc, theFrameInfo.cycleTime); + // TODO: use generated jerk as control vector? + ukf_acc_global.predict(Vector3d::Zero(), theFrameInfo.cycleTime); if (isWalking) { @@ -138,9 +134,8 @@ void IMUModelProvider::writeIMUData(IMUModel& imuModel) // global position data // TODO: check for correct integration // TODO: prediction or state? - imuModel.acceleration.x() = static_cast(ukf_acc_global.state.acceleration()(0, 0)); - imuModel.acceleration.y() = static_cast(ukf_acc_global.state.acceleration()(1, 0)); - imuModel.acceleration.z() = static_cast(ukf_acc_global.state.acceleration()(2, 0)) - theWalkCalibration.gravity; + imuModel.acceleration = ukf_acc_global.state.acceleration().col(0).cast(); + imuModel.acceleration.z() -= theWalkCalibration.gravity; imuModel.location += imuModel.velocity * theFrameInfo.cycleTime + imuModel.acceleration * theFrameInfo.cycleTime * theFrameInfo.cycleTime * 0.5f; imuModel.velocity += imuModel.acceleration * theFrameInfo.cycleTime; @@ -148,12 +143,11 @@ void IMUModelProvider::writeIMUData(IMUModel& imuModel) // the state we are estimating in ukf_rot is (X * cycleTime) ms in the past. so predict (X * cycleTime) ms as estimate for the real current state UKF, 6>> sensor_delay_corrected_rot = ukf_rot; sensor_delay_corrected_rot.generateSigmaPoints(); - Eigen::Vector3d u_rot(0, 0, 0); - sensor_delay_corrected_rot.predict(u_rot, theWalkingEngineParams.imuSensorDelayFrames * theFrameInfo.cycleTime); + sensor_delay_corrected_rot.predict(Vector3d::Zero(), theWalkingEngineParams.imuSensorDelayFrames * theFrameInfo.cycleTime); // store rotation in IMUData as a rotation vector - imuModel.rotation = eigenVectorToVector3D(sensor_delay_corrected_rot.state.rotation()).cast(); - RotationMatrix bodyIntoGlobalMapping(sensor_delay_corrected_rot.state.getRotationAsAngleAxisd().cast()); + imuModel.rotation = sensor_delay_corrected_rot.state.rotation().cast(); + const RotationMatrix bodyIntoGlobalMapping(sensor_delay_corrected_rot.state.getRotationAsAngleAxisd().cast()); /* * Note: the following code lines use the inverse mapping, i.e. globalIntoBodyMapping, by using the third row of bodyIntoGlobalMapping's matrix representation @@ -168,16 +162,15 @@ void IMUModelProvider::writeIMUData(IMUModel& imuModel) * this results in huge devation of the angles determined by atan2 because the projected y axis might end up in the second or third quadrant of the YZ plane */ - Eigen::Vector3d global_Z_in_body(bodyIntoGlobalMapping(2, 0), bodyIntoGlobalMapping(2, 1), bodyIntoGlobalMapping(2, 2)); - imuModel.orientation_rotvec = quaternionToVector3D(Eigen::Quaterniond::FromTwoVectors(global_Z_in_body, Eigen::Vector3d(0, 0, 1))).cast(); - RotationMatrix bodyIntoGlobalMappingWithoutZ(Eigen::Quaterniond::FromTwoVectors(global_Z_in_body, Eigen::Vector3d(0, 0, 1)).cast()); + const Vector3f global_Z_in_body = bodyIntoGlobalMapping.row(2); + const Quaternionf orientation_quaternion = Eigen::Quaternionf::FromTwoVectors(global_Z_in_body, Vector3f::UnitZ()); + imuModel.orientation_rotvec = quaternionToRotationVector(orientation_quaternion); + const RotationMatrix bodyIntoGlobalMappingWithoutZ(orientation_quaternion); - imuModel.orientation = Vector2a(static_cast(-atan2f(bodyIntoGlobalMappingWithoutZ(1, 2), bodyIntoGlobalMappingWithoutZ(1, 1))), - static_cast(-atan2f(-bodyIntoGlobalMappingWithoutZ(0, 2), bodyIntoGlobalMappingWithoutZ(0, 0)))); + imuModel.orientation << static_cast(-atan2f(bodyIntoGlobalMappingWithoutZ(1, 2), bodyIntoGlobalMappingWithoutZ(1, 1))), + static_cast(-atan2f(-bodyIntoGlobalMappingWithoutZ(0, 2), bodyIntoGlobalMappingWithoutZ(0, 0))); - imuModel.rotational_velocity.x() = static_cast(ukf_rot.state.rotational_velocity()(0, 0)); - imuModel.rotational_velocity.y() = static_cast(ukf_rot.state.rotational_velocity()(1, 0)); - imuModel.rotational_velocity.z() = static_cast(ukf_rot.state.rotational_velocity()(2, 0)); + imuModel.rotational_velocity = ukf_rot.state.rotational_velocity().col(0).cast(); PLOT("module:IMUModelProvider:State:location:x", imuModel.location.x()); PLOT("module:IMUModelProvider:State:location:y", imuModel.location.y()); diff --git a/Src/Modules/Modeling/IMUModelProvider/IMUModelProvider.h b/Src/Modules/Modeling/IMUModelProvider/IMUModelProvider.h index 77571509..5c0c0cc0 100644 --- a/Src/Modules/Modeling/IMUModelProvider/IMUModelProvider.h +++ b/Src/Modules/Modeling/IMUModelProvider/IMUModelProvider.h @@ -82,7 +82,7 @@ class IMUModelProvider : public IMUModelProviderBase /* filter for global acceleration */ UKF, 3>> ukf_acc_global; - typedef Measurement<3> IMU_AccMeasurementGlobal; + typedef Measurement<3> IMUAccMeasurementGlobal; Eigen::Matrix Q_acc; Eigen::Matrix Q_acc_walk; @@ -94,21 +94,12 @@ class IMUModelProvider : public IMUModelProviderBase bool isWalking = false; bool isActive = true, lastActive = true; - Eigen::Vector3d quaternionToRotationVector(const Eigen::Quaterniond& q) const + template static Eigen::Vector quaternionToRotationVector(const Eigen::Quaternion& q) { - Eigen::AngleAxisd temp(q); + const Eigen::AngleAxis temp(q); return temp.angle() * temp.axis(); } - Vector3d quaternionToVector3D(const Eigen::Quaterniond& q) const - { - Eigen::AngleAxisd temp(q); - Eigen::Vector3d temp2(temp.angle() * temp.axis()); - return Vector3d(temp2(0), temp2(1), temp2(2)); - } - - Vector3d eigenVectorToVector3D(const Eigen::Vector3d& v) const { return Vector3d(v(0), v(1), v(2)); } - Eigen::Quaterniond integrated; void reloadParameters(); diff --git a/Src/Modules/Modeling/IMUModelProvider/IMURotationState.h b/Src/Modules/Modeling/IMUModelProvider/IMURotationState.h index 002244ce..af8f2f8f 100644 --- a/Src/Modules/Modeling/IMUModelProvider/IMURotationState.h +++ b/Src/Modules/Modeling/IMUModelProvider/IMURotationState.h @@ -138,9 +138,9 @@ class RotationState : public Eigen::Matrix //public RotationStat { // calculate difference between the mean and the sigma points rotation by means of a rotation rotational_differences.clear(); - for (typename std::vector>::iterator i = rotations.begin(); i != rotations.end(); ++i) + for (typename std::vector>::iterator j = rotations.begin(); j != rotations.end(); ++j) { - Eigen::AngleAxis rotational_difference = Eigen::AngleAxis((*i) * mean.inverse()); + Eigen::AngleAxis rotational_difference = Eigen::AngleAxis((*j) * mean.inverse()); // store as rotation vector representation rotational_differences.push_back(rotational_difference.angle() * rotational_difference.axis()); @@ -148,9 +148,9 @@ class RotationState : public Eigen::Matrix //public RotationStat // average difference quaternions in their 3d vectorial representation (length = angle, direction = axis) Eigen::Vector3d averaged_rotational_difference = Eigen::Vector3d::Zero(); - for (typename std::vector>::iterator i = rotational_differences.begin(); i != rotational_differences.end(); ++i) + for (typename std::vector>::iterator j = rotational_differences.begin(); j != rotational_differences.end(); ++j) { - averaged_rotational_difference += *i; + averaged_rotational_difference += *j; } averaged_rotational_difference = 1.0 / static_cast(rotational_differences.size()) * averaged_rotational_difference; diff --git a/Src/Modules/Modeling/KalmanWorldModelGenerator/AngleModels/MultiKalmanModelAngle.h b/Src/Modules/Modeling/KalmanWorldModelGenerator/AngleModels/MultiKalmanModelAngle.h index d2284d04..d7294813 100644 --- a/Src/Modules/Modeling/KalmanWorldModelGenerator/AngleModels/MultiKalmanModelAngle.h +++ b/Src/Modules/Modeling/KalmanWorldModelGenerator/AngleModels/MultiKalmanModelAngle.h @@ -13,6 +13,7 @@ #include #include #include +#include #include "Tools/Streams/AutoStreamable.h" #include "Modules/Modeling/KalmanWorldModelGenerator/Models/KalmanPositionHypothesis.h" @@ -247,6 +248,8 @@ STREAMABLE(MultiKalmanModelAngle, */ std::size_t updateBestHypothesisIndexIfNecessary(); + void removeHypothesis(size_t index); + public: void setRobotPoseCameraMatrixAndFriction(const RobotPose& robotPose, const CameraMatrix& cameraMatrix, float friction); @@ -383,7 +386,7 @@ STREAMABLE(MultiKalmanModelAngle, private: /// Stores the index of the best hypothesis. /// This is updated by the method \c updateBestHypothesis(). - mutable std::size_t m_bestHypothesisIndex = -1; + mutable std::size_t m_bestHypothesisIndex = std::numeric_limits::max(); /// Stores the index of the best hypothesis from the last iteration. std::size_t m_lastBestHypothesisIndex = m_bestHypothesisIndex; diff --git a/Src/Modules/Modeling/KalmanWorldModelGenerator/AngleModels/MultiKalmanModelAngle_impl.h b/Src/Modules/Modeling/KalmanWorldModelGenerator/AngleModels/MultiKalmanModelAngle_impl.h index ff78661c..a97b731e 100644 --- a/Src/Modules/Modeling/KalmanWorldModelGenerator/AngleModels/MultiKalmanModelAngle_impl.h +++ b/Src/Modules/Modeling/KalmanWorldModelGenerator/AngleModels/MultiKalmanModelAngle_impl.h @@ -181,9 +181,12 @@ void MultiKalmanModelAngle::updateValidity(float { // If there is at least one real good hypothesis, reduce validity of other hypotheses faster. bool goodHypothesisExists = false; - if (bestHypothesis() && bestHypothesis()->validity >= goodValidityThreshold) + if constexpr (towardsOneModel) { - goodHypothesisExists = true; + if (bestHypothesis() && bestHypothesis()->validity >= goodValidityThreshold) + { + goodHypothesisExists = true; + } } // Update validity of all hypotheses. @@ -191,7 +194,7 @@ void MultiKalmanModelAngle::updateValidity(float { if (m_hypotheses[i].validity >= goodValidityThreshold) m_hypotheses[i].updateValidity(maxPerceptsPerSecond, weightOfPreviousValidity_goodHypotheses); - else if (towardsOneModel && goodHypothesisExists) + else if (goodHypothesisExists) m_hypotheses[i].updateValidity(maxPerceptsPerSecond, weightOfPreviousValidity / 2); else m_hypotheses[i].updateValidity(maxPerceptsPerSecond, weightOfPreviousValidity); @@ -218,6 +221,15 @@ template std::size_t MultiKalmanMo return m_bestHypothesisIndex; } +template void MultiKalmanModelAngle::removeHypothesis(size_t index) +{ + m_hypotheses.erase(m_hypotheses.begin() + index); + if (m_bestHypothesisIndex == index) + m_bestHypothesisIndex = std::numeric_limits::max(); + else if (index < m_bestHypothesisIndex && m_bestHypothesisIndex != std::numeric_limits::max()) + m_bestHypothesisIndex--; +} + template void MultiKalmanModelAngle::updateBestHypothesis() { // If there is no hypothesis, there cannot be a best one. @@ -237,7 +249,7 @@ template void MultiKalmanModelAngl } // Search for the best hypothesis. - size_t bestIndex = -1; + size_t bestIndex = std::numeric_limits::max(); float bestValidity = -1.f; float bestDistance = std::numeric_limits::infinity(); for (size_t i = 0; i < m_hypotheses.size(); i++) @@ -266,16 +278,12 @@ template void MultiKalmanModelAngl { // For changing the best hypothesis, the validity must be truly greater than the old one. // Hysteresis: Reduce validity of last best hypothesis. m_hypotheses[m_lastBestHypothesisIndex].validity -= decreaseValidityOnChangingBestHypothesis; - // Set new best hypothesis - m_bestHypothesisIndex = bestIndex; } } - else - { - // Set best hypothesis. - m_bestHypothesisIndex = bestIndex; - } } + + // Set best hypothesis. + m_bestHypothesisIndex = bestIndex; } //MARK: Kalman filter related methods @@ -404,14 +412,15 @@ void MultiKalmanModelAngle::cleanUpHypothesesOuts // Check if hypothesis is inside field border + threshold to allow a bit clearance for inaccuracy. if (distance > fieldBorderThreshold) { + if constexpr (!towardsOneModel) + { + removeHypothesis(i); + i--; + } // Do nothing if current hypothesis is the best one. - if (i != m_bestHypothesisIndex && m_bestHypothesisIndex < m_hypotheses.size() && m_hypotheses.size() > 1) + else if (i != m_bestHypothesisIndex && m_bestHypothesisIndex < m_hypotheses.size() && m_hypotheses.size() > 1) { - m_hypotheses.erase(m_hypotheses.begin() + i); - if (m_bestHypothesisIndex == i) - m_bestHypothesisIndex = static_cast(-1); // Maximum size_t number - else if (i < m_bestHypothesisIndex && m_bestHypothesisIndex != static_cast(-1)) - m_bestHypothesisIndex--; + removeHypothesis(i); i--; } else @@ -460,12 +469,17 @@ void MultiKalmanModelAngle::cleanUpHypothesesLowV { if (m_hypotheses[i].validity < validityThreshold && m_hypotheses[i].validity < bestValidity && m_hypotheses.size() > 1) { - m_hypotheses.erase(m_hypotheses.begin() + i); - if (m_bestHypothesisIndex == i) - m_bestHypothesisIndex = static_cast(-1); // Maximum size_t number - else if (i < m_bestHypothesisIndex && m_bestHypothesisIndex != static_cast(-1)) - m_bestHypothesisIndex--; - i--; + if constexpr (!towardsOneModel) + { + removeHypothesis(i); + i--; + } + // Do nothing if current hypothesis is the best one. + else if (i != m_bestHypothesisIndex && m_bestHypothesisIndex < m_hypotheses.size() && m_hypotheses.size() > 1) + { + removeHypothesis(i); + i--; + } } } } @@ -498,10 +512,7 @@ template void MultiKalmanModelAngl if (yDiff < mergeAngleDiff.y() && xDiff < mergeAngleDiff.x()) { m_hypotheses[m_bestHypothesisIndex].merge(actualHypothesis); - // Remove hypothesis i - m_hypotheses.erase(m_hypotheses.begin() + i); - if (i < m_bestHypothesisIndex && m_bestHypothesisIndex != static_cast(-1)) - m_bestHypothesisIndex--; + removeHypothesis(i); i--; } diff --git a/Src/Modules/Modeling/KalmanWorldModelGenerator/BallModelProvider/BallModelProvider.cpp b/Src/Modules/Modeling/KalmanWorldModelGenerator/BallModelProvider/BallModelProvider.cpp index efd72ebf..7a5cb885 100644 --- a/Src/Modules/Modeling/KalmanWorldModelGenerator/BallModelProvider/BallModelProvider.cpp +++ b/Src/Modules/Modeling/KalmanWorldModelGenerator/BallModelProvider/BallModelProvider.cpp @@ -4,8 +4,10 @@ */ #include "BallModelProvider.h" -#include "Tools/Math/Transformation.h" #include "Tools/Debugging/Annotation.h" +#include "Tools/Math/Transformation.h" +#include +#include MAKE_MODULE(BallModelProvider, modeling) @@ -14,25 +16,21 @@ MAKE_MODULE(BallModelProvider, modeling) void BallModelProvider::update(BallModel& ballModel) { - execute(); ballModel = m_localBallModel; } void BallModelProvider::update(MultipleBallModel& multipleBallModel) { - execute(); multipleBallModel = m_multipleBallModel; } void BallModelProvider::update(RemoteBallModel& remoteBallModel) { - execute(); remoteBallModel = m_remoteBallModel; } void BallModelProvider::update(TeamBallModel& teamBallModel) { - execute(); m_teamBallModelProvider.generateTeamBallModel(teamBallModel, m_localMultipleBallModel, m_remoteMultipleBallModel, theRobotPose, theFrameInfo); } @@ -49,9 +47,7 @@ void BallModelProvider::initialize() // Remote ball model. m_remoteBallModel.timeWhenLastSeen = 0; - m_remoteTimeSinceBallSeen = std::vector(7); // one value per teammate (index = player number) - for (size_t i = 0; i < m_remoteTimeSinceBallSeen.size(); i++) - m_remoteTimeSinceBallSeen[i] = 0; + m_remoteTimeSinceBallSeen.assign(MAX_NUM_PLAYERS + 1, 0); // one value per teammate (index = player number) // Team ball model. m_teamBallModelProvider.initialize(teamParameters); @@ -71,7 +67,7 @@ void BallModelProvider::initialize() } } -void BallModelProvider::execute() +void BallModelProvider::execute(tf::Subflow&) { for (int i = 0; i < JoinedIMUData::numOfInertialDataSources; i++) { @@ -101,12 +97,6 @@ void BallModelProvider::execute() // Init debug drawings. initDebugDrawing(); - // Only update once per step. - if (m_lastTimeStamp == theFrameInfo.time) - { - return; - } - if (theFieldDimensions.xPosOpponentGroundline != 0) { houghLineDetector.setParameters(anglePrecisionLineDetection, @@ -158,7 +148,6 @@ void BallModelProvider::execute() // --- Save current state for next iteration --- m_lastOdometryData = theOdometryData; - m_lastTimeStamp = theFrameInfo.time; } bool BallModelProvider::isBallInGoalBox(const Vector2f& positionOnField) @@ -336,33 +325,33 @@ void BallModelProvider::sensorUpdateRemote() for (size_t i = 0; i < theTeammateData.teammates.size(); ++i) { // Loop over all players which have sent data and are active. - if (theTeammateData.teammates[i].status == Teammate::FULLY_ACTIVE) + if (theTeammateData.teammates[i].status == TeammateReceived::Status::FULLY_ACTIVE) { // The ball was recently seen by this teammate. - bool ballSeenByTeamMate = m_remoteTimeSinceBallSeen[theTeammateData.teammates[i].number] < theTeammateData.teammates[i].ball.timeWhenLastSeen; + bool ballSeenByTeamMate = m_remoteTimeSinceBallSeen[theTeammateData.teammates[i].playerNumber] < theTeammateData.teammates[i].ballModel.timeWhenLastSeen; if (!ballSeenByTeamMate) continue; // Save time of last percept which was used to update multiple ball model. - m_remoteTimeSinceBallSeen[theTeammateData.teammates[i].number] = theTeammateData.teammates[i].ball.timeWhenLastSeen; + m_remoteTimeSinceBallSeen[theTeammateData.teammates[i].playerNumber] = theTeammateData.teammates[i].ballModel.timeWhenLastSeen; // Get ball position from player i in global coordinates. - Vector2f percept = Transformation::robotToField(theTeammateData.teammates[i].pose, theTeammateData.teammates[i].ball.estimate.position); - Vector2f velocity = Transformation::robotToFieldVelocity(theTeammateData.teammates[i].pose, theTeammateData.teammates[i].ball.estimate.velocity); + Vector2f percept = Transformation::robotToField(theTeammateData.teammates[i].robotPose, theTeammateData.teammates[i].ballModel.position); + Vector2f velocity = Transformation::robotToFieldVelocity(theTeammateData.teammates[i].robotPose, theTeammateData.teammates[i].ballModel.velocity); // Distance from robot to ball percept. - float distanceToPercept = theTeammateData.teammates[i].ball.estimate.position.norm(); - float robotPoseBasedValidity = theTeammateData.teammates[i].ball.validity * theTeammateData.teammates[i].pose.validity; + float distanceToPercept = theTeammateData.teammates[i].ballModel.position.norm(); + float robotPoseBasedValidity = theTeammateData.teammates[i].ballModel.validity * theTeammateData.teammates[i].robotPose.validity; // Run sensor update with global ball position. RemoteKalmanPositionHypothesis::TeammateInfo teammate(robotPoseBasedValidity, theFrameInfo.time); m_remoteMultipleBallModel.sensorUpdate(percept, distanceToPercept, &velocity, - theTeammateData.teammates[i].ball.timeWhenLastSeen, + theTeammateData.teammates[i].ballModel.timeWhenLastSeen, robotPoseBasedValidity, parameters.remote.Hypotheses_mergeAngleDiff, robotPoseBasedValidity, //parameters.remote.Hypotheses_initialValidityForNewHypotheses, kalmanNoiseMatrices, - theTeammateData.teammates[i].number, + theTeammateData.teammates[i].playerNumber, teammate); } } @@ -375,7 +364,7 @@ void BallModelProvider::sensorUpdateRemote() void BallModelProvider::handleGameState() { // Transition from SET to PLAYING: - if (m_lastGameState == STATE_SET && theGameInfo.state == STATE_PLAYING && parameters.State_SetToPlaying_addKickOffHypothesis) // This can be deactivated via config file. + if (m_lastGameState == STATE_SET && theGameInfo.state == STATE_PLAYING && parameters.State_SetToPlaying_addKickOffHypothesis && theGameInfo.gamePhase != GAME_PHASE_PENALTYSHOOT) // This can be deactivated via config file. { // Add a ball hypothesis at the kick off point. Vector2f kickOffPoint; @@ -456,14 +445,7 @@ void BallModelProvider::handleKick() if (m_localMultipleBallModel.bestHypothesis() != nullptr) { - // Use robot model's foot positions intersecting with best hypothesis - const Pose3f& kickFoot = (theRobotModel.soleLeft.translation.z() > theRobotModel.soleRight.translation.z()) ? theRobotModel.soleLeft : theRobotModel.soleRight; - Vector2f ballPosition = m_localMultipleBallModel.bestHypothesis()->kalman.position(); - bool footIntersectsWithModel = (ballPosition.y() - kickFoot.translation.y()) < theFieldDimensions.ballRadius && ballPosition.x() > 90.f - && (ballPosition.x() - kickFoot.translation.x()) < (180.f + theFieldDimensions.ballRadius); // 104 mm is foot length from base - - // Two ways to trigger a kick are handled here: kick engine or in walk kick. - bool kickTriggered = (theMotionInfo.motion == MotionRequest::Motion::kick || (theMotionInfo.motion == MotionRequest::Motion::walk && theMotionInfo.walkKicking)) && footIntersectsWithModel; + bool kickTriggered = KickUtils::isBallKicked(theMotionInfo); if (kickTriggered) { // If the ball hypothesis already has a high velocity, the kick must be @@ -585,16 +567,16 @@ void BallModelProvider::generateLocalBallModel() m_localBallModel.timeWhenLastSeen = bestHypothesis->timeWhenLastSeen; m_localBallModel.validity = bestHypothesis->validity; - m_localBallModel.seenPercentage = static_cast((static_cast(bestHypothesis->perceptsPerSecond()) / 30.f) * 100.f); m_localBallModel.lastPerception = bestHypothesis->lastPerception; } else { + if (m_localBallModel.timeWhenLastSeen > 0) + ANNOTATION("BallModelProvider", "WARNING: No bestHypothesis in generateLocalBallModel()"); + m_localBallModel.lastPerception = Vector2f(1000, 0); m_localBallModel.estimate = BallState(); m_localBallModel.timeWhenLastSeen = 0; m_localBallModel.validity = 0.f; - m_localBallModel.seenPercentage = 0; - m_localBallModel.lastPerception = Vector2f::Zero(); } m_localBallModel.friction = ballFriction; updateTimeWhenBallFirstDisappeared(bestHypothesis, m_localBallModel); // set ballModel.timeWhenDisappeared @@ -621,7 +603,6 @@ void BallModelProvider::generateMultipleBallModel() updateEstimatedBallState(kph, bm.estimate); // set ballModel.estimate bm.timeWhenLastSeen = kph.timeWhenLastSeen; bm.validity = kph.validity; - bm.seenPercentage = static_cast((static_cast(kph.perceptsPerSecond()) / 30.f) * 100.f); bm.lastPerception = kph.lastPerception; bm.friction = ballFriction; bm.timeWhenLastSeenByTeamMate = 0; diff --git a/Src/Modules/Modeling/KalmanWorldModelGenerator/BallModelProvider/BallModelProvider.h b/Src/Modules/Modeling/KalmanWorldModelGenerator/BallModelProvider/BallModelProvider.h index f691eae6..f0ea268f 100644 --- a/Src/Modules/Modeling/KalmanWorldModelGenerator/BallModelProvider/BallModelProvider.h +++ b/Src/Modules/Modeling/KalmanWorldModelGenerator/BallModelProvider/BallModelProvider.h @@ -77,6 +77,7 @@ MODULE(BallModelProvider, PROVIDES(MultipleBallModel), PROVIDES(RemoteBallModel), PROVIDES(TeamBallModel), + HAS_PREEXECUTION, LOADS_PARAMETERS(, /// Parameters used by \c BallModelProvider for local and remote ball model. @@ -114,17 +115,12 @@ class BallModelProvider : public BallModelProviderBase * Constructor. */ BallModelProvider() - : m_localMultipleBallModel(LOCAL_PERCEPT_DURATION), m_remoteMultipleBallModel(REMOTE_PERCEPT_DURATION), m_lastTimeStamp(0), m_lastGameState(STATE_INITIAL), - m_lastPenaltyState(PENALTY_NONE), m_lastMotionType(MotionRequest::Motion::specialAction), m_kickDetected(false), m_ballDisappeared(true), m_timeWhenBallFirstDisappeared(0) + : m_localMultipleBallModel(LOCAL_PERCEPT_DURATION), m_remoteMultipleBallModel(REMOTE_PERCEPT_DURATION), m_lastGameState(STATE_INITIAL), m_lastPenaltyState(PENALTY_NONE), + m_lastMotionType(MotionRequest::Motion::specialAction), m_kickDetected(false), m_ballDisappeared(true), m_timeWhenBallFirstDisappeared(0) { initialize(); } - /** - * Destructor. - */ - ~BallModelProvider() {} - // MARK: Update methods for provided representations. @@ -174,7 +170,7 @@ class BallModelProvider : public BallModelProviderBase * timestamp is checked in order to determine whether this method was already * executed in the current frame. */ - void execute(); + void execute(tf::Subflow&); /** * \brief Prediction @@ -341,12 +337,6 @@ class BallModelProvider : public BallModelProviderBase TeamBallModelProvider m_teamBallModelProvider; // ----- general variables ----- - /** - * Save timestamp when executing the method \c execute(), to run it only once - * per frame. - */ - unsigned m_lastTimeStamp; - /** * Save game state at each invocation of the method \c handleGameState(). This * is used to handle state transitions. diff --git a/Src/Modules/Modeling/KalmanWorldModelGenerator/Models/MultiKalmanModel.h b/Src/Modules/Modeling/KalmanWorldModelGenerator/Models/MultiKalmanModel.h index c63ec5be..7863edbe 100644 --- a/Src/Modules/Modeling/KalmanWorldModelGenerator/Models/MultiKalmanModel.h +++ b/Src/Modules/Modeling/KalmanWorldModelGenerator/Models/MultiKalmanModel.h @@ -241,6 +241,8 @@ STREAMABLE(MultiKalmanModel, * hypothesis exists. */ std::size_t updateBestHypothesisIndexIfNecessary(); + + void removeHypothesis(size_t index); public: /** @@ -371,7 +373,7 @@ STREAMABLE(MultiKalmanModel, private: /// Stores the index of the best hypothesis. /// This is updated by the method \c updateBestHypothesis(). - mutable std::size_t m_bestHypothesisIndex = -1; + mutable std::size_t m_bestHypothesisIndex = std::numeric_limits::max(); /// Stores the index of the best hypothesis from the last iteration. std::size_t m_lastBestHypothesisIndex = m_bestHypothesisIndex; diff --git a/Src/Modules/Modeling/KalmanWorldModelGenerator/Models/MultiKalmanModel_impl.h b/Src/Modules/Modeling/KalmanWorldModelGenerator/Models/MultiKalmanModel_impl.h index 711a50bd..2094dc50 100644 --- a/Src/Modules/Modeling/KalmanWorldModelGenerator/Models/MultiKalmanModel_impl.h +++ b/Src/Modules/Modeling/KalmanWorldModelGenerator/Models/MultiKalmanModel_impl.h @@ -123,9 +123,12 @@ void MultiKalmanModel::updateValidity(float maxPe { // If there is at least one real good hypothesis, reduce validity of other hypotheses faster. bool goodHypothesisExists = false; - if (bestHypothesis() && bestHypothesis()->validity >= goodValidityThreshold) + if constexpr (towardsOneModel) { - goodHypothesisExists = true; + if (bestHypothesis() && bestHypothesis()->validity >= goodValidityThreshold) + { + goodHypothesisExists = true; + } } // Update validity of all hypotheses. @@ -133,7 +136,7 @@ void MultiKalmanModel::updateValidity(float maxPe { if (m_hypotheses[i].validity >= goodValidityThreshold) m_hypotheses[i].updateValidity(maxPerceptsPerSecond, weightOfPreviousValidity_goodHypotheses); - else if (towardsOneModel && goodHypothesisExists) + else if (goodHypothesisExists) m_hypotheses[i].updateValidity(maxPerceptsPerSecond, weightOfPreviousValidity / 2); else m_hypotheses[i].updateValidity(maxPerceptsPerSecond, weightOfPreviousValidity); @@ -211,7 +214,7 @@ template void MultiKalmanModel::max(); float bestValidity = -1.f; for (size_t i = 0; i < m_hypotheses.size(); i++) { @@ -259,6 +262,15 @@ template std::size_t MultiKalmanMo return m_bestHypothesisIndex; } +template void MultiKalmanModel::removeHypothesis(size_t index) +{ + m_hypotheses.erase(m_hypotheses.begin() + index); + if (m_bestHypothesisIndex == index) + m_bestHypothesisIndex = std::numeric_limits::max(); + else if (index < m_bestHypothesisIndex && m_bestHypothesisIndex != std::numeric_limits::max()) + m_bestHypothesisIndex--; +} + template void MultiKalmanModel::increaseUncertainty(double positionFactor, double velocityFactor, bool onlyBestHypothesis) { @@ -307,11 +319,7 @@ void MultiKalmanModel::cleanUpHypothesesOutsideFi // Do nothing if current hypothesis is the best one. if (i != m_bestHypothesisIndex && m_bestHypothesisIndex < m_hypotheses.size()) { - m_hypotheses.erase(m_hypotheses.begin() + i); - if (m_bestHypothesisIndex == i) - m_bestHypothesisIndex = static_cast(-1); // Maximum size_t number - else if (i < m_bestHypothesisIndex && m_bestHypothesisIndex != static_cast(-1)) - m_bestHypothesisIndex--; + removeHypothesis(i); i--; } else @@ -357,11 +365,7 @@ void MultiKalmanModel::cleanUpHypothesesLowValidi { if (m_hypotheses[i].validity < validityThreshold && m_hypotheses[i].validity < bestValidity) { - m_hypotheses.erase(m_hypotheses.begin() + i); - if (m_bestHypothesisIndex == i) - m_bestHypothesisIndex = static_cast(-1); // Maximum size_t number - else if (i < m_bestHypothesisIndex && m_bestHypothesisIndex != static_cast(-1)) - m_bestHypothesisIndex--; + removeHypothesis(i); i--; } } @@ -391,10 +395,7 @@ void MultiKalmanModel::cleanUpHypothesesSimilarTo if (distance < minDistanceForSeparateHypotheses && (angle < minAngleForSeparateHypotheses || smallVelocities)) { m_hypotheses[m_bestHypothesisIndex].merge(m_hypotheses[i]); - // Remove hypothesis i - m_hypotheses.erase(m_hypotheses.begin() + i); - if (i < m_bestHypothesisIndex && m_bestHypothesisIndex != static_cast(-1)) - m_bestHypothesisIndex--; + removeHypothesis(i); i--; } } diff --git a/Src/Modules/Modeling/KalmanWorldModelGenerator/RobotMapProvider/KalmanMultiRobotMapProvider.cpp b/Src/Modules/Modeling/KalmanWorldModelGenerator/RobotMapProvider/KalmanMultiRobotMapProvider.cpp index d1f7bf58..e2846f0a 100644 --- a/Src/Modules/Modeling/KalmanWorldModelGenerator/RobotMapProvider/KalmanMultiRobotMapProvider.cpp +++ b/Src/Modules/Modeling/KalmanWorldModelGenerator/RobotMapProvider/KalmanMultiRobotMapProvider.cpp @@ -29,25 +29,129 @@ } \ } +// This anonymous namespace contains a named-parameter object for the performSensorUpdate function +namespace +{ + static const Vector2f defaultPosition; + static const Vector2a defaultAngle; + + template struct SensorUpdateType + { + RobotMap& robotMap_; + const Vector2f* measuredPositionRelative_ = &defaultPosition; + float measuredDistance_ = 0; + const Vector2f* measuredVelocity_ = nullptr; + RobotEstimate::RobotType desiredRobotType_ = RobotEstimate::RobotType::invalid; + unsigned timestamp_ = 0; + float perceptValidity_ = 0; + const Vector2a* maxAngleDiffToMerge_ = &defaultAngle; + float maxDistanceForDistanceBasedMerging_ = 0; + float maxDistanceToMerge_ = 0; + float initialValidityForNewHypothesis_ = 0; + const KalmanPositionTracking2D::KalmanMatrices::Noise& kalmanNoiseMatrices_; + + SensorUpdateType(RobotMap& rm, const KalmanPositionTracking2D::KalmanMatrices::Noise& noise) : robotMap_(rm), kalmanNoiseMatrices_(noise) {} + RobotMap& robotMap() const { return robotMap_; } + + const Vector2f& measuredPositionRelative() const { return *measuredPositionRelative_; } + SensorUpdateType& measuredPositionRelative(const Vector2f& mp) + { + measuredPositionRelative_ = ∓ + return *this; + } + + float measuredDistance() const { return measuredDistance_; } + SensorUpdateType& measuredDistance(float md) + { + measuredDistance_ = md; + return *this; + } + + const Vector2f* measuredVelocity() const { return measuredVelocity_; } + SensorUpdateType& measuredVelocity(const Vector2f* velocity) + { + measuredVelocity_ = velocity; + return *this; + } + + RobotEstimate::RobotType desiredRobotType() const { return desiredRobotType_; } + SensorUpdateType& desiredRobotType(RobotEstimate::RobotType type) + { + desiredRobotType_ = type; + return *this; + } + + unsigned timestamp() const { return timestamp_; } + SensorUpdateType& timestamp(unsigned time) + { + timestamp_ = time; + return *this; + } + + float perceptValidity() const { return perceptValidity_; } + SensorUpdateType& perceptValidity(float validity) + { + perceptValidity_ = validity; + return *this; + } + + const Vector2a& maxAngleDiffToMerge() const { return *maxAngleDiffToMerge_; } + SensorUpdateType& maxAngleDiffToMerge(const Vector2a& angleDiff) + { + maxAngleDiffToMerge_ = &angleDiff; + return *this; + } + + float maxDistanceForDistanceBasedMerging() const { return maxDistanceForDistanceBasedMerging_; } + SensorUpdateType& maxDistanceForDistanceBasedMerging(float maxDistance) + { + maxDistanceForDistanceBasedMerging_ = maxDistance; + return *this; + } + + float maxDistanceToMerge() const { return maxDistanceToMerge_; } + SensorUpdateType& maxDistanceToMerge(float maxDistance) + { + maxDistanceToMerge_ = maxDistance; + return *this; + } + + float initialValidityForNewHypothesis() const { return initialValidityForNewHypothesis_; } + SensorUpdateType& initialValidityForNewHypothesis(float validity) + { + initialValidityForNewHypothesis_ = validity; + return *this; + } + + const auto& kalmanNoiseMatrices() const { return kalmanNoiseMatrices_; } + template SensorUpdateType& kalmanNoiseMatrices(const Matrix& noise) + { + kalmanNoiseMatrices_ = noise; + return *this; + } + }; + template SensorUpdateType sensorUpdateArguments(RobotMap& map, const Matrix& noise) + { + return SensorUpdateType(map, noise); + } +} // namespace + // ========================== // UPDATE METHODS // ========================== void KalmanMultiRobotMapProvider::update(RobotMap& robotMap) { - execute(); robotMap = m_robotMap; } void KalmanMultiRobotMapProvider::update(LocalRobotMap& localRobotMap) { - execute(); localRobotMap = m_localRobotMap; } void KalmanMultiRobotMapProvider::update(RemoteRobotMap& remoteRobotMap) { - execute(); remoteRobotMap = m_remoteRobotMap; } @@ -56,12 +160,15 @@ void KalmanMultiRobotMapProvider::update(RemoteRobotMap& remoteRobotMap) // INITIALIZE // ========================== -void KalmanMultiRobotMapProvider::initialize() +void KalmanMultiRobotMapProvider::reset() { // Reset robot maps m_robotMap.reset(); + m_localKalmanRobotMap.clear(); m_localRobotMap.reset(); + m_remoteKalmanRobotMap.clear(); m_remoteRobotMap.reset(); + m_mergedKalmanRobotMap.clear(); } @@ -69,18 +176,14 @@ void KalmanMultiRobotMapProvider::initialize() // EXCUTE CODE // ========================== -void KalmanMultiRobotMapProvider::execute() +void KalmanMultiRobotMapProvider::execute(tf::Subflow&) { - if (theFrameInfo.time == m_lastTimeStamp) - return; - // Modify internal params modifyInternalParameters(); // Init debugging initDebugging(); - // Motion update (kalman maps) motionUpdate(); @@ -96,141 +199,192 @@ void KalmanMultiRobotMapProvider::execute() // Generate robot maps from kalman maps generateMaps(); - // Debugging doGlobalDebugging(); - - // Save last execution time - m_lastTimeStamp = theFrameInfo.time; + // --- Save current state for next iteration --- + m_lastOdometryData = theOdometryData; } void KalmanMultiRobotMapProvider::motionUpdate() { - m_localKalmanRobotMap.motionUpdate(theFrameInfo.time, parameters.friction); - m_remoteKalmanRobotMap.motionUpdate(theFrameInfo.time, parameters.friction); - m_mergedKalmanRobotMap.motionUpdate(theFrameInfo.time, parameters.friction); + // Calculate odometry change since last iteration. + Pose2f odometryOffset = theOdometryData - m_lastOdometryData; + + // Apply odometry offset to all local robot hypotheses. + m_localKalmanRobotMap.removeOdometry(odometryOffset); + m_remoteKalmanRobotMap.removeOdometry(odometryOffset); + m_mergedKalmanRobotMap.removeOdometry(odometryOffset); + + m_localKalmanRobotMap.setRobotPoseCameraMatrixAndFriction(theRobotPose, theCameraMatrixUpper, parameters.friction); + m_remoteKalmanRobotMap.setRobotPoseCameraMatrixAndFriction(theRobotPose, theCameraMatrixUpper, parameters.friction); + m_mergedKalmanRobotMap.setRobotPoseCameraMatrixAndFriction(theRobotPose, theCameraMatrixUpper, parameters.friction); + + + m_localKalmanRobotMap.motionUpdate(theFrameInfo.time); + m_remoteKalmanRobotMap.motionUpdate(theFrameInfo.time); + m_mergedKalmanRobotMap.motionUpdate(theFrameInfo.time); } void KalmanMultiRobotMapProvider::sensorUpdate() { + std::vector perceptsInThisFrame; + + // Update the perceptBuffer with the latest odometry + Pose2f odometryOffset = theOdometryData - m_lastOdometryData; + for (std::vector& rev : perceptBuffer) + { + for (RobotEstimate& re : rev) + { + //Vector2f& reOnField = Transformation::fieldToRobot(theRobotPose, re.locationOnField.translation); + Vector2f& reOnField = re.locationOnField.translation; + reOnField -= odometryOffset.translation; + reOnField.rotate(-odometryOffset.rotation); + } + } + // =============================== // Update local percepts // =============================== - for (const RobotEstimate& robot : theRobotsPerceptUpper.robots) - { - // Transform to field coordinates - Vector2f posOnField = Transformation::robotToField(theRobotPose, robot.locationOnField.translation); - // Update sensor - performSensorUpdate(m_localKalmanRobotMap, - posOnField, - robot.distance, - nullptr, - robot.robotType, - theFrameInfo.time, - robot.validity, - parameters.localPercept.hypotheses_minDistanceForNewHypothesis, - parameters.localPercept.hypotheses_initialValidityForNewHypotheses, - kalmanNoiseMatrices); - // Update merged - performSensorUpdate(m_mergedKalmanRobotMap, - posOnField, - robot.distance, - nullptr, - robot.robotType, - theFrameInfo.time, - robot.validity * parameters.mergedMapParameters.localPerceptInfluence, - parameters.localPercept.hypotheses_minDistanceForNewHypothesis, - parameters.localPercept.hypotheses_initialValidityForNewHypotheses, - kalmanNoiseMatrices, - theRobotInfo.number, - RemoteKalmanPositionHypothesis::TeammateInfo(robot.validity, theFrameInfo.time)); - } for (const RobotEstimate& robot : theRobotsPercept.robots) { - // Transform to field coordinates - Vector2f posOnField = Transformation::robotToField(theRobotPose, robot.locationOnField.translation); + perceptsInThisFrame.push_back(robot); // Update sensor - performSensorUpdate(m_localKalmanRobotMap, - posOnField, - robot.distance, - nullptr, - robot.robotType, - theFrameInfo.time, - robot.validity * parameters.mergedMapParameters.localPerceptInfluence, - parameters.localPercept.hypotheses_minDistanceForNewHypothesis, - parameters.localPercept.hypotheses_initialValidityForNewHypotheses, - kalmanNoiseMatrices); + performSensorUpdate( + sensorUpdateArguments(m_localKalmanRobotMap, kalmanNoiseMatrices) + .measuredPositionRelative(robot.locationOnField.translation) + .measuredDistance(robot.distance) + .desiredRobotType(robot.robotType) + .timestamp(theFrameInfo.time) + .perceptValidity(robot.validity) + .maxAngleDiffToMerge(parameters.localMapParameters.maxAngleDiffToMerge) + .maxDistanceForDistanceBasedMerging(parameters.localMapParameters.maxDistanceForDistanceBasedMerging) + .maxDistanceToMerge(parameters.localMapParameters.maxDistanceToMerge) + .initialValidityForNewHypothesis(parameters.localPercept.hypotheses_initialValidityForNewHypotheses)); // Update merged - performSensorUpdate(m_mergedKalmanRobotMap, - posOnField, - robot.distance, - nullptr, - robot.robotType, - theFrameInfo.time, - robot.validity, - parameters.localPercept.hypotheses_minDistanceForNewHypothesis, - parameters.localPercept.hypotheses_initialValidityForNewHypotheses, - kalmanNoiseMatrices, + performSensorUpdate( + sensorUpdateArguments(m_mergedKalmanRobotMap, kalmanNoiseMatrices) + .measuredPositionRelative(robot.locationOnField.translation) + .measuredDistance(robot.distance) + .desiredRobotType(robot.robotType) + .timestamp(theFrameInfo.time) + .perceptValidity(robot.validity * parameters.mergedMapParameters.localPerceptInfluence) + .maxAngleDiffToMerge(parameters.mergedMapParameters.maxAngleDiffToMerge) + .maxDistanceForDistanceBasedMerging(parameters.mergedMapParameters.maxDistanceForDistanceBasedMerging) + .maxDistanceToMerge(parameters.mergedMapParameters.maxDistanceToMerge) + .initialValidityForNewHypothesis(parameters.localPercept.hypotheses_initialValidityForNewHypotheses), theRobotInfo.number, RemoteKalmanPositionHypothesis::TeammateInfo(robot.validity, theFrameInfo.time)); } + perceptBuffer.push_front(perceptsInThisFrame); + DEBUG_DRAWING("module:KalmanMultiRobotMapProvider:perceptBuffer", "drawingOnField") + { + if (perceptBuffer.size() > 0) + { + for (const std::vector& rev : perceptBuffer) + { + for (const RobotEstimate& re : rev) + { + CIRCLE("module:KalmanMultiRobotMapProvider:perceptBuffer", + re.locationOnField.translation.x(), + re.locationOnField.translation.y(), + 25 /*distancePrecisionLineDetection / 2.f*/, + 2, + Drawings::solidPen, + ColorRGBA::white, + Drawings::solidBrush, + ColorRGBA(255, 255, 255, 100)); + } + } + } + } // =============================== // Update from teammate positions // =============================== - for (const Teammate& teammate : theTeammateData.teammates) + for (const TeammateReceived& teammate : theTeammateData.teammates) { // Loop over all players which have sent data and are active - if (teammate.status == Teammate::FULLY_ACTIVE) + if (teammate.status == TeammateReceived::Status::FULLY_ACTIVE) { // Calculate distance - auto relativePosition = Transformation::fieldToRobot(theRobotPose, teammate.pose.translation); + auto relativePosition = Transformation::fieldToRobot(theRobotPose, teammate.robotPose.translation); float distance = relativePosition.norm(); // Calculate speed const Pose2f& speed = teammate.speedInfo.speed; auto speed2d = Vector2f(speed.translation.x() * cos(speed.rotation), speed.translation.y() * sin(speed.rotation)); - auto relativeSpeed = Transformation::robotToFieldVelocity(teammate.pose, speed2d); + auto relativeSpeed = Transformation::robotToFieldVelocity(teammate.robotPose, speed2d); // Update remote - performSensorUpdate(m_remoteKalmanRobotMap, - teammate.pose.translation, - distance, - &relativeSpeed, - RobotEstimate::RobotType::teammateRobot, - teammate.timeWhenSent, - teammate.pose.validity * parameters.remoteMapParameters.teammatePositionInfluence, - parameters.remoteModel.hypotheses_minDistanceForNewHypothesis, - parameters.remoteModel.hypotheses_initialValidityForNewHypotheses, - kalmanNoiseMatrices, - teammate.number, - RemoteKalmanPositionHypothesis::TeammateInfo(teammate.pose.validity, teammate.timeWhenSent)); + performSensorUpdate( + sensorUpdateArguments(m_remoteKalmanRobotMap, kalmanNoiseMatrices) + .measuredPositionRelative(relativePosition) + .measuredDistance(distance) + .measuredVelocity(&relativeSpeed) + .desiredRobotType(RobotEstimate::RobotType::teammateRobot) + .timestamp(teammate.sendTimestamp) + .perceptValidity(teammate.robotPose.validity * parameters.remoteMapParameters.teammatePositionInfluence) + .maxAngleDiffToMerge(parameters.remoteMapParameters.maxAngleDiffToMerge) + .maxDistanceForDistanceBasedMerging(parameters.remoteMapParameters.maxDistanceForDistanceBasedMerging) + .maxDistanceToMerge(parameters.remoteMapParameters.maxDistanceToMerge) + .initialValidityForNewHypothesis(parameters.remoteModel.hypotheses_initialValidityForNewHypotheses), + teammate.playerNumber, + RemoteKalmanPositionHypothesis::TeammateInfo(teammate.robotPose.validity, teammate.sendTimestamp)); // Update merged - performSensorUpdate(m_mergedKalmanRobotMap, - teammate.pose.translation, - distance, - &relativeSpeed, - RobotEstimate::RobotType::teammateRobot, - teammate.timeWhenSent, - teammate.pose.validity * parameters.mergedMapParameters.teammatePositionInfluence, - parameters.remoteModel.hypotheses_minDistanceForNewHypothesis, - parameters.remoteModel.hypotheses_initialValidityForNewHypotheses, - kalmanNoiseMatrices, - teammate.number, - RemoteKalmanPositionHypothesis::TeammateInfo(teammate.pose.validity, teammate.timeWhenSent)); + performSensorUpdate( + sensorUpdateArguments(m_mergedKalmanRobotMap, kalmanNoiseMatrices) + .measuredPositionRelative(relativePosition) + .measuredDistance(distance) + .measuredVelocity(&relativeSpeed) + .desiredRobotType(RobotEstimate::RobotType::teammateRobot) + .timestamp(teammate.sendTimestamp) + .perceptValidity(teammate.robotPose.validity * parameters.mergedMapParameters.teammatePositionInfluence) + .maxAngleDiffToMerge(parameters.mergedMapParameters.maxAngleDiffToMerge) + .maxDistanceForDistanceBasedMerging(parameters.mergedMapParameters.maxDistanceForDistanceBasedMerging) + .maxDistanceToMerge(parameters.mergedMapParameters.maxDistanceToMerge) + .initialValidityForNewHypothesis(parameters.remoteModel.hypotheses_initialValidityForNewHypotheses), + teammate.playerNumber, + RemoteKalmanPositionHypothesis::TeammateInfo(teammate.robotPose.validity, teammate.sendTimestamp)); } } + // =============================== + // Update sonar percepts + // =============================== + for (const SonarEstimate& sonarEstimate : theSonarPercept.sonarEstimates) + { + // Calculate speed + Vector2f sonarEstimateRelativeSpeed = Vector2f(0, 0); + + performSensorUpdate( + sensorUpdateArguments(m_localKalmanRobotMap, kalmanNoiseMatrices) + .measuredPositionRelative(sonarEstimate.relativePosition.translation) + .measuredDistance(sonarEstimate.distance) + .measuredVelocity(&sonarEstimateRelativeSpeed) + .desiredRobotType(RobotEstimate::RobotType::unknownRobot) + .timestamp(sonarEstimate.timestamp) + .perceptValidity(sonarEstimate.validity) + .maxAngleDiffToMerge(parameters.localMapParameters.maxAngleDiffToMerge) + .maxDistanceForDistanceBasedMerging(parameters.localMapParameters.maxDistanceForDistanceBasedMerging) + .maxDistanceToMerge(parameters.localMapParameters.maxDistanceToMerge) + .initialValidityForNewHypothesis(parameters.localPercept.hypotheses_initialValidityForNewHypotheses)); + + DEBUG_DRAWING("module:KalmanMultiRobotMapProvider:sonarPercept", "drawingOnField") + { + Vector2f sonarEstimatePositionOnField = Transformation::robotToField(theRobotPose, sonarEstimate.relativePosition.translation); + CIRCLE("module:KalmanMultiRobotMapProvider:sonarPercept", sonarEstimatePositionOnField.x(), sonarEstimatePositionOnField.y(), 40, 2, Drawings::solidPen, ColorRGBA::yellow, Drawings::solidBrush, ColorRGBA::yellow); + } + } // =============================== // Update from teammate data // =============================== - for (const Teammate& teammate : theTeammateData.teammates) + for (const TeammateReceived& teammate : theTeammateData.teammates) { // Loop over all players which have sent data and are active - if (teammate.status == Teammate::FULLY_ACTIVE) + if (teammate.status == TeammateReceived::Status::FULLY_ACTIVE) { // ===== Update models ===== for (const auto& robotModel : teammate.localRobotMap.robots) @@ -239,39 +393,45 @@ void KalmanMultiRobotMapProvider::sensorUpdate() if ((robotModel.pose.translation - theRobotPose.translation).norm() > 200) { // Calculate distance + // Received, locale map of the teammate are in posOnField coordians auto relativePosition = Transformation::fieldToRobot(theRobotPose, robotModel.pose.translation); float distance = relativePosition.norm(); // Update remote - Vector2f percept = teammate.pose.translation; - performSensorUpdate(m_remoteKalmanRobotMap, - robotModel.pose.translation, - distance, - &robotModel.velocity, - robotModel.robotType, - teammate.timeWhenSent, - robotModel.validity * parameters.remoteMapParameters.teammateModelInfluence, - parameters.remoteModel.hypotheses_minDistanceForNewHypothesis, - parameters.remoteModel.hypotheses_initialValidityForNewHypotheses, - kalmanNoiseMatrices, - teammate.number, - RemoteKalmanPositionHypothesis::TeammateInfo(robotModel.validity, teammate.timeWhenSent)); + Vector2f percept = teammate.robotPose.translation; + performSensorUpdate( + sensorUpdateArguments(m_remoteKalmanRobotMap, kalmanNoiseMatrices) + .measuredPositionRelative(relativePosition) + .measuredDistance(distance) + // TC: Removed, because not transmitted .measuredVelocity(&robotModel.velocity) + .desiredRobotType(robotModel.robotType) + .timestamp(teammate.sendTimestamp) + .perceptValidity(robotModel.validity * parameters.remoteMapParameters.teammateModelInfluence) + .maxAngleDiffToMerge(parameters.remoteMapParameters.maxAngleDiffToMerge) + .maxDistanceForDistanceBasedMerging(parameters.remoteMapParameters.maxDistanceForDistanceBasedMerging) + .maxDistanceToMerge(parameters.remoteMapParameters.maxDistanceToMerge) + .initialValidityForNewHypothesis(parameters.remoteModel.hypotheses_initialValidityForNewHypotheses), + teammate.playerNumber, + RemoteKalmanPositionHypothesis::TeammateInfo(robotModel.validity, teammate.sendTimestamp)); // Update merged - performSensorUpdate(m_mergedKalmanRobotMap, - robotModel.pose.translation, - distance, - &robotModel.velocity, - robotModel.robotType, - teammate.timeWhenSent, - robotModel.validity * parameters.mergedMapParameters.teammateModelInfluence, - parameters.remoteModel.hypotheses_minDistanceForNewHypothesis, - parameters.remoteModel.hypotheses_initialValidityForNewHypotheses, - kalmanNoiseMatrices, - teammate.number, - RemoteKalmanPositionHypothesis::TeammateInfo(robotModel.validity, teammate.timeWhenSent)); + performSensorUpdate( + sensorUpdateArguments(m_mergedKalmanRobotMap, kalmanNoiseMatrices) + .measuredPositionRelative(relativePosition) + .measuredDistance(distance) + // TC: Removed, because not transmitted .measuredVelocity(&robotModel.velocity) + .desiredRobotType(robotModel.robotType) + .timestamp(teammate.sendTimestamp) + .perceptValidity(robotModel.validity * parameters.mergedMapParameters.teammateModelInfluence) + .maxAngleDiffToMerge(parameters.mergedMapParameters.maxAngleDiffToMerge) + .maxDistanceForDistanceBasedMerging(parameters.mergedMapParameters.maxDistanceForDistanceBasedMerging) + .maxDistanceToMerge(parameters.mergedMapParameters.maxDistanceToMerge) + .initialValidityForNewHypothesis(parameters.remoteModel.hypotheses_initialValidityForNewHypotheses), + teammate.playerNumber, + RemoteKalmanPositionHypothesis::TeammateInfo(robotModel.validity, teammate.sendTimestamp)); } } - + //Percepts are currently (as of November 2022) no longer sent around with + /* // ===== Update percepts ===== // Update from lower percepts for (const auto& percept : teammate.robotsPercept.robots) @@ -279,68 +439,73 @@ void KalmanMultiRobotMapProvider::sensorUpdate() // Check that is not me if ((percept.locationOnField.translation - theRobotPose.translation).norm() > 400) { + // Received, locale map of the teammate are in posOnField coordians + auto relativePosition = Transformation::fieldToRobot(theRobotPose, percept.locationOnField.translation); // Update remote - performSensorUpdate(m_remoteKalmanRobotMap, - percept.locationOnField.translation, - percept.distance, - nullptr, - percept.robotType, - percept.timestampFromImage, - percept.validity * parameters.remoteMapParameters.teammatePerceptInfluence, - parameters.remoteModel.hypotheses_minDistanceForNewHypothesis, - parameters.remoteModel.hypotheses_initialValidityForNewHypotheses, - kalmanNoiseMatrices, - teammate.number, - RemoteKalmanPositionHypothesis::TeammateInfo(percept.validity, percept.timestampFromImage)); + performSensorUpdate(sensorUpdateArguments(m_remoteKalmanRobotMap, kalmanNoiseMatrices) + .measuredPositionRelative(relativePosition) + .measuredDistance(percept.distance) + .desiredRobotType(percept.robotType) + .timestamp(percept.timestampFromImage) + .perceptValidity(percept.validity* parameters.remoteMapParameters.teammatePerceptInfluence) + .maxAngleDiffToMerge(parameters.remoteMapParameters.maxAngleDiffToMerge) + .maxDistanceForDistanceBasedMerging(parameters.remoteMapParameters.maxDistanceForDistanceBasedMerging) + .maxDistanceToMerge(parameters.remoteMapParameters.maxDistanceToMerge) + .initialValidityForNewHypothesis(parameters.remoteModel.hypotheses_initialValidityForNewHypotheses), + teammate.number, + RemoteKalmanPositionHypothesis::TeammateInfo(percept.validity, percept.timestampFromImage)); // Update merged - performSensorUpdate(m_mergedKalmanRobotMap, - percept.locationOnField.translation, - percept.distance, - nullptr, - percept.robotType, - percept.timestampFromImage, - percept.validity * parameters.remoteMapParameters.teammatePerceptInfluence, - parameters.remoteModel.hypotheses_minDistanceForNewHypothesis, - parameters.remoteModel.hypotheses_initialValidityForNewHypotheses, - kalmanNoiseMatrices, - teammate.number, - RemoteKalmanPositionHypothesis::TeammateInfo(percept.validity, percept.timestampFromImage)); + performSensorUpdate(sensorUpdateArguments(m_mergedKalmanRobotMap, kalmanNoiseMatrices) + .measuredPositionRelative(relativePosition) + .measuredDistance(percept.distance) + .desiredRobotType(percept.robotType) + .timestamp(percept.timestampFromImage) + .perceptValidity(percept.validity* parameters.mergedMapParameters.teammatePerceptInfluence) + .maxAngleDiffToMerge(parameters.mergedMapParameters.maxAngleDiffToMerge) + .maxDistanceForDistanceBasedMerging(parameters.mergedMapParameters.maxDistanceForDistanceBasedMerging) + .maxDistanceToMerge(parameters.mergedMapParameters.maxDistanceToMerge) + .initialValidityForNewHypothesis(parameters.remoteModel.hypotheses_initialValidityForNewHypotheses), + teammate.number, + RemoteKalmanPositionHypothesis::TeammateInfo(percept.validity, percept.timestampFromImage)); } } // Update from upper percepts for (const auto& percept : teammate.robotsPerceptUpper.robots) { + auto relativePosition = Transformation::fieldToRobot(theRobotPose, percept.locationOnField.translation); + // Check that is not me if ((percept.locationOnField.translation - theRobotPose.translation).norm() > 400) { // Update remote - performSensorUpdate(m_remoteKalmanRobotMap, - percept.locationOnField.translation, - percept.distance, - nullptr, - percept.robotType, - percept.timestampFromImage, - percept.validity * parameters.remoteMapParameters.teammatePerceptInfluence, - parameters.remoteModel.hypotheses_minDistanceForNewHypothesis, - parameters.remoteModel.hypotheses_initialValidityForNewHypotheses, - kalmanNoiseMatrices, - teammate.number, - RemoteKalmanPositionHypothesis::TeammateInfo(percept.validity, percept.timestampFromImage)); + performSensorUpdate(sensorUpdateArguments(m_remoteKalmanRobotMap, kalmanNoiseMatrices) + .measuredPositionRelative(relativePosition) + .measuredDistance(percept.distance) + .desiredRobotType(percept.robotType) + .timestamp(percept.timestampFromImage) + .perceptValidity(percept.validity * parameters.remoteMapParameters.teammatePerceptInfluence) + .maxAngleDiffToMerge(parameters.remoteMapParameters.maxAngleDiffToMerge) + .maxDistanceForDistanceBasedMerging(parameters.remoteMapParameters.maxDistanceForDistanceBasedMerging) + .maxDistanceToMerge(parameters.remoteMapParameters.maxDistanceToMerge) + .initialValidityForNewHypothesis(parameters.remoteModel.hypotheses_initialValidityForNewHypotheses), + teammate.number, + RemoteKalmanPositionHypothesis::TeammateInfo(percept.validity, percept.timestampFromImage)); // Update merged - performSensorUpdate(m_mergedKalmanRobotMap, - percept.locationOnField.translation, - percept.distance, - nullptr, - percept.robotType, - percept.timestampFromImage, - percept.validity * parameters.remoteMapParameters.teammatePerceptInfluence, - parameters.remoteModel.hypotheses_minDistanceForNewHypothesis, - parameters.remoteModel.hypotheses_initialValidityForNewHypotheses, - kalmanNoiseMatrices, - teammate.number, - RemoteKalmanPositionHypothesis::TeammateInfo(percept.validity, percept.timestampFromImage)); + performSensorUpdate(sensorUpdateArguments(m_mergedKalmanRobotMap, kalmanNoiseMatrices) + .measuredPositionRelative(relativePosition) + .measuredDistance(percept.distance) + .desiredRobotType(percept.robotType) + .timestamp(percept.timestampFromImage) + .perceptValidity(percept.validity * parameters.mergedMapParameters.teammatePerceptInfluence) + .maxAngleDiffToMerge(parameters.mergedMapParameters.maxAngleDiffToMerge) + .maxDistanceForDistanceBasedMerging(parameters.mergedMapParameters.maxDistanceForDistanceBasedMerging) + .maxDistanceToMerge(parameters.mergedMapParameters.maxDistanceToMerge) + .initialValidityForNewHypothesis(parameters.remoteModel.hypotheses_initialValidityForNewHypotheses), + teammate.number, + RemoteKalmanPositionHypothesis::TeammateInfo(percept.validity, percept.timestampFromImage)); } } + */ } } @@ -351,9 +516,9 @@ void KalmanMultiRobotMapProvider::sensorUpdate() void KalmanMultiRobotMapProvider::pruneHypotheses() { // Merge hypotheses - mergeHypotheses(m_localKalmanRobotMap); - mergeHypotheses(m_remoteKalmanRobotMap); - mergeHypotheses(m_mergedKalmanRobotMap); + mergeHypotheses(m_localKalmanRobotMap, parameters.localMapParameters.maxAngleDiffToMerge, parameters.localMapParameters.maxDistanceToMerge); + mergeHypotheses(m_remoteKalmanRobotMap, parameters.remoteMapParameters.maxAngleDiffToMerge, parameters.remoteMapParameters.maxDistanceToMerge); + mergeHypotheses(m_mergedKalmanRobotMap, parameters.mergedMapParameters.maxAngleDiffToMerge, parameters.mergedMapParameters.maxDistanceToMerge); // Prune because of validity m_localKalmanRobotMap.cleanUpHypothesesLowValidity(parameters.localPercept.cleanUpHypotheses_belowValidity, false); @@ -402,7 +567,11 @@ void KalmanMultiRobotMapProvider::generateMaps() for (unsigned i = 0; i < lSize; i++) { // Add to map - m_localRobotMap.robots.push_back(m_localKalmanRobotMap[i].getRobotMapEntry()); + // Convert positionRelative to positionOnField prior + auto mapEntry = m_localKalmanRobotMap[i].getRobotMapEntry(); + mapEntry.pose.translation = Transformation::robotToField(theRobotPose, mapEntry.pose.translation); + + m_localRobotMap.robots.push_back(mapEntry); } // Size of remote robot map @@ -415,99 +584,32 @@ void KalmanMultiRobotMapProvider::generateMaps() for (unsigned i = 0; i < rSize; i++) { // Add to map - m_remoteRobotMap.robots.push_back(m_remoteKalmanRobotMap[i].getRobotMapEntry()); + // Convert positionRelative to positionOnField prior + auto mapEntry = m_remoteKalmanRobotMap[i].getRobotMapEntry(); + mapEntry.pose.translation = Transformation::robotToField(theRobotPose, mapEntry.pose.translation); + + m_remoteRobotMap.robots.push_back(mapEntry); } // Clear old map m_robotMap.robots.clear(); // Fill map - if (parameters.useMergedMap) - { - // Size of merged robot map - auto mSize = m_mergedKalmanRobotMap.size(); - // Reserve size - m_robotMap.robots.reserve(mSize); - // Generate maps - for (unsigned i = 0; i < mSize; i++) - { - // Add to map - m_robotMap.robots.push_back(m_mergedKalmanRobotMap[i].getRobotMapEntry()); - } - } - else + // Size of merged robot map + auto mSize = m_mergedKalmanRobotMap.size(); + // Reserve size + m_robotMap.robots.reserve(mSize); + // Generate maps + for (unsigned i = 0; i < mSize; i++) { - // Remember which hypotheses of the remote map have already been inserted because they're similar to a local model - std::vector usedRemoteHypotheses; - - // Distance to closest - float distance = -1; - for (unsigned i = 0; i < m_localKalmanRobotMap.size(); i++) - { - // Save the hypothesis we're working on - const auto& hypothesis = m_localKalmanRobotMap[i]; - - // Find nearest pose in remote map - const auto* nearestHypothesis = findNearestHypothesis(m_remoteKalmanRobotMap, hypothesis.kalman.position(), distance, hypothesis.robotType()); - - // This can be treated as one robot? - if (nearestHypothesis && distance < parameters.mergedMapParameters.maxDistanceToMerge) - { - // Mark this hypothesis as "in map" - usedRemoteHypotheses.push_back(nearestHypothesis); - - // Get local and remote entry - RobotMapEntry localModel = hypothesis.getRobotMapEntry(); - RobotMapEntry remoteModel = nearestHypothesis->getRobotMapEntry(); - - // Combined entry - RobotMapEntry entry; - - // ====== "Merge" entries ===== - // Translation mean - entry.pose.translation = (localModel.pose.translation + remoteModel.pose.translation) / 2; - // Shift both rotations to [0, 2pi], divide by 2 (mean) and shift back to [-pi, pi]. Then normalize (should already be the case) - entry.pose.rotation = Angle::normalize((localModel.pose.rotation + remoteModel.pose.rotation + pi2) / 2 - pi); - // Detect robot type - auto localType = localModel.robotType; - // Use localType if not unknown (robot types have already been matched in findNearestHypothesis (only chance their not the same is localType is unknown) - entry.robotType = localType != RobotEstimate::RobotType::unknownRobot ? localType : remoteModel.robotType; - // Mean validity - entry.validity = (localModel.validity + remoteModel.validity) / 2; - // Mean velocity - entry.velocity = (localModel.velocity + remoteModel.velocity) / 2; - - // Add to map - m_robotMap.robots.push_back(std::move(entry)); - } - else - { - // Add to map - m_robotMap.robots.push_back(hypothesis.getRobotMapEntry()); - } - } - - // Remote map - for (unsigned i = 0; i < m_remoteKalmanRobotMap.size(); i++) - { - // Save the hypothesis we're working on - const auto& hypothesis = m_remoteKalmanRobotMap[i]; - - bool alreadyAdded = false; - // Check whether this robot has already been added - for (auto testHypo : usedRemoteHypotheses) - { - alreadyAdded = (testHypo == &hypothesis); - - if (alreadyAdded) - break; - } + // Add to map + // Convert positionRelative to positionOnField prior + auto mapEntry = m_mergedKalmanRobotMap[i].getRobotMapEntry(); + mapEntry.pose.translation = Transformation::robotToField(theRobotPose, mapEntry.pose.translation); - if (!alreadyAdded) - { - m_robotMap.robots.push_back(hypothesis.getRobotMapEntry()); - } - } + m_robotMap.robots.push_back(mapEntry); } + + //perceptBuffer.clear(); } @@ -522,6 +624,18 @@ void KalmanMultiRobotMapProvider::initDebugging() DECLARE_DEBUG_DRAWING("module:KalmanMultiRobotMapProvider:localHypotheses", "drawingOnField"); DECLARE_DEBUG_DRAWING("module:KalmanMultiRobotMapProvider:remoteHypotheses", "drawingOnField"); DECLARE_DEBUG_DRAWING("module:KalmanMultiRobotMapProvider:mergedHypotheses", "drawingOnField"); + + DECLARE_DEBUG_DRAWING("module:KalmanMultiRobotMapProvider:perceptBuffer", "drawingOnField"); + + DECLARE_DEBUG_DRAWING("module:KalmanMultiRobotMapProvider:perceptMerge", "drawingOnField"); + DECLARE_DEBUG_DRAWING("module:KalmanMultiRobotMapProvider:hypothesisMerge", "drawingOnField"); + DECLARE_DEBUG_DRAWING("module:KalmanMultiRobotMapProvider:perceptCreate", "drawingOnField"); + + //POC + DECLARE_DEBUG_DRAWING("module:KalmanMultiRobotMapProvider:sonarPerceptLeft", "drawingOnField"); + DECLARE_DEBUG_DRAWING("module:KalmanMultiRobotMapProvider:sonarPerceptRight", "drawingOnField"); + + DEBUG_RESPONSE_ONCE("module:KalmanMultiRobotMapProvider:reset") reset(); } void KalmanMultiRobotMapProvider::doGlobalDebugging() @@ -560,4 +674,221 @@ void KalmanMultiRobotMapProvider::updateValidities() parameters.localPercept.validity_weightOfPreviousValidity_goodHypotheses); } +void KalmanMultiRobotMapProvider::getAngles(Vector2a& angles, const Vector2f& relativePosition) +{ + const CameraMatrix& cameraMatrix = theCameraMatrixUpper; + angles.x() = std::atan2(relativePosition.y(), relativePosition.x()); + angles.y() = std::atan2(cameraMatrix.translation.z(), relativePosition.norm()); +} + +static Angle normalizeAngle(Angle a) +{ + return a.normalize(); +} + +template void KalmanMultiRobotMapProvider::mergeHypotheses(RobotMap& robotMap, const Vector2a& mergeAngleDiff, const float mergeDistance) +{ + // Iterate over hypotheses + for (unsigned i = 0; i < robotMap.size(); i++) + { + auto& iRobot = robotMap[i]; + // Skip hypotheses which are to be removed + if (iRobot.validity == 0) + continue; + Vector2a iRobotPositionRelativeAngles, hypothesisPositionRelativeAngles; + getAngles(iRobotPositionRelativeAngles, iRobot.kalman.position()); // iRobot.kalman.position()); + for (unsigned j = i + 1; j < robotMap.size(); j++) + { + auto& jRobot = robotMap[j]; + // Skip hypotheses which are to be removed + if (jRobot.validity == 0) + continue; + + getAngles(hypothesisPositionRelativeAngles, jRobot.kalman.position()); // jRobot.kalman.position()); + // Calculate head angles between the perception and the current hypothesis. + Angle yDiff = std::abs(normalizeAngle(iRobotPositionRelativeAngles.y() - hypothesisPositionRelativeAngles.y())); + Angle xDiff = std::abs(normalizeAngle(iRobotPositionRelativeAngles.x() - hypothesisPositionRelativeAngles.x())); + + // Get distance between measurements + float distance = Geometry::distance(iRobot.kalman.position(), jRobot.kalman.position()); + // Get differene in robot type + float robotTypeDifference = std::abs(iRobot.robotTypeValidity() - jRobot.robotTypeValidity()); + + // Check whether hypotheses close enough for merging + if (((yDiff < mergeAngleDiff.y() && xDiff < mergeAngleDiff.x()) || distance < mergeDistance) && robotTypeDifference < parameters.localMapParameters.maxRobotTypeDifferenceToMerge) + { + DEBUG_DRAWING("module:KalmanMultiRobotMapProvider:hypothesisMerge", "drawingOnField") + { + + bool isLocalMap = static_cast(&robotMap) == static_cast(&m_localKalmanRobotMap); + if (isLocalMap) + { + ARROW("module:KalmanMultiRobotMapProvider:hypothesisMerge", + jRobot.kalman.position().x(), + jRobot.kalman.position().y(), + iRobot.kalman.position().x(), + iRobot.kalman.position().y(), + 15, + Drawings::solidPen, + ColorRGBA::red); + } + } + // Merge + iRobot.merge(jRobot); + // Set validity of the other to 0 to prune it later + jRobot.validity = 0; + } + } + } +} + +template +typename RobotMap::HypothesisType* KalmanMultiRobotMapProvider::findNearestHypothesis( + RobotMap& robotMap, Vector2a& angleDiff, const Vector2f& measuredPositionRelative, float& distance, RobotEstimate::RobotType desiredRobotType) +{ + typename RobotMap::HypothesisType* nearestHypothesisAngle = nullptr; + Angle minAngleDiff = -1; + Angle minAngleDiffX = -1; + Angle minAngleDiffY = -1; + float minDistance = -1; + + Vector2a measuredPositionRelativeAngles, hypothesisPositionRelativeAngles; + getAngles(measuredPositionRelativeAngles, measuredPositionRelative); + + for (size_t i = 0; i < robotMap.size(); i++) + { + auto& actualHypothesis = robotMap[i]; + Vector2f positionRelative = actualHypothesis.kalman.position(); + //position = BallPhysics::propagateBallPosition(position, actualHypothesis.kalman.velocity(), 30.f / 1000.f, friction); + // Only consider hypotheses with the correct robot type. + if (!actualHypothesis.matchRobotType(desiredRobotType)) + continue; + + getAngles(hypothesisPositionRelativeAngles, positionRelative); + // Calculate distance from the perception to the current hypothesis. + float currentDistance = Geometry::distance(measuredPositionRelative, actualHypothesis.kalman.position()); + + // Calculate head angles between the perception and the current hypothesis. + Angle yDiff = std::abs(normalizeAngle(measuredPositionRelativeAngles.y() - hypothesisPositionRelativeAngles.y())); + Angle xDiff = std::abs(normalizeAngle(measuredPositionRelativeAngles.x() - hypothesisPositionRelativeAngles.x())); + + // Check whether the angle diff sum is smaller than the minimum of all previous hypotheses. + if (minAngleDiff < 0 || (xDiff + yDiff) < minAngleDiff) + { + nearestHypothesisAngle = &actualHypothesis; + minAngleDiff = (xDiff + yDiff); + minAngleDiffY = yDiff; + minAngleDiffX = xDiff; + minDistance = currentDistance; + } + } + + angleDiff.y() = minAngleDiffY; + angleDiff.x() = minAngleDiffX; + distance = minDistance; + + return nearestHypothesisAngle; +} + +template

1SaprJmLwTu{Dg@%RTlZ?&N1GX7>8 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�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 z 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 literal 0 HcmV?d00001 diff --git a/Config/rawGameInfoProvider.cfg b/Config/rawGameInfoProvider.cfg index 6b09111e..06263889 100644 --- a/Config/rawGameInfoProvider.cfg +++ b/Config/rawGameInfoProvider.cfg @@ -2,6 +2,9 @@ naoBodyType = H25; naoHeadType = H25; robotConfig = robot_id.json; ownColor = 2; +ownKeeperColor = 1; oppColor = 3; -gameControllerTimeout = 2000; +oppKeeperColor = 0; +keeper = 1; +gameControllerTimeout = 5000; aliveDelay = 1000; diff --git a/Config/robotClassifier.cfg b/Config/robotClassifier.cfg new file mode 100644 index 00000000..1c195815 --- /dev/null +++ b/Config/robotClassifier.cfg @@ -0,0 +1,14 @@ +classifierThreshold = 0.828; +classifierThresholdLower = 0.8; +classifierMargin = -0.1; +bboxMargin = 0.4; +nonMaximumSuppressionThreshold = 0.05; +maxClassficationsLower = 3; +maxClassficationsTotal = 13; +useBboxPrediction = false; +usePredictedHeight = false; +interpolatePredictedBbox = true; +interpolatePredictedBboxFactor = 0.7; +useGeometricHeight = true; +interpolateGeometricBbox = true; +interpolateGeometricBboxFactor = 0.5; diff --git a/Config/robotDimensions.cfg b/Config/robotDimensions.cfg index a508628a..2f059afa 100644 --- a/Config/robotDimensions.cfg +++ b/Config/robotDimensions.cfg @@ -34,3 +34,12 @@ footFront = 104; footBack = 54; footOuter = 44; footInner = 44; +leftSonarInfo = { + rotation = 22.5deg; + translation = {x = 50.7; y = 37.85;}; +}; +rightSonarInfo = { + rotation = -22.5deg; + translation = {x = 50.7; y = -37.85;}; +}; +sonarOpeningAngle = 60deg; diff --git a/Config/roleDynamicProvider.cfg b/Config/roleDynamicProvider.cfg new file mode 100644 index 00000000..e64073a7 --- /dev/null +++ b/Config/roleDynamicProvider.cfg @@ -0,0 +1,2 @@ +useStaticAssignmentNoWifi = true; +minDistanceDiffForNewRoleAssignment = 1000; diff --git a/Config/roleSelectionProvider.cfg b/Config/roleSelectionProvider.cfg index c0cdd091..9b4f8273 100644 --- a/Config/roleSelectionProvider.cfg +++ b/Config/roleSelectionProvider.cfg @@ -1,67 +1,120 @@ -// configuration of the role selection -forceReceiverOnOwnKickoff = true; +forceReceiverOnOwnKickoff = false; enableReplacementKeeper = true; - -// The fundamental role selection is based on two factors: -// - number of needed roles (excluding ballchaser and keeper since they will be added later) -// - tactical alignment of the team ("defensive" or "offensive") -// -// The RoleSelectionProvider will choose the correct selection from the following based on -// these factors and may tweak it based on the current situation. -// The selections are named after the two factors starting with the alignment and -// ending with the number of roles. - - -// selections for one player - -selectionDefensive1 = [ - defenderSingle +defensive = [ + { + ballchaserDuringOwnKickoff = defenderSingle; + ballchaserDuringOppKickoff = defenderSingle; + selectedRoles = [ + defenderSingle + ]; + }, + { + ballchaserDuringOwnKickoff = center; + ballchaserDuringOppKickoff = center; + selectedRoles = [ + defenderSingle, + center + ]; + }, + { + ballchaserDuringOwnKickoff = rightWing; + ballchaserDuringOppKickoff = rightWing; + selectedRoles = [ + defenderSingle, + backWing, + rightWing + ]; + }, + { + ballchaserDuringOwnKickoff = rightWing; + ballchaserDuringOppKickoff = rightWing; + selectedRoles = [ + defenderLeft, + defenderRight, + center, + rightWing + ]; + }, + { + ballchaserDuringOwnKickoff = center; + ballchaserDuringOppKickoff = leftWing; + selectedRoles = [ + defenderLeft, + defenderRight, + center, + leftWing, + rightWing + ]; + }, + { + ballchaserDuringOwnKickoff = frontWing; + ballchaserDuringOppKickoff = frontWing; + selectedRoles = [ + defenderLeft, + defenderRight, + backWing, + leftWing, + rightWing, + frontWing + ]; + } ]; - -selectionOffensive1 = [ - defenderSingle +offensive = [ + { + ballchaserDuringOwnKickoff = defenderSingle; + ballchaserDuringOppKickoff = defenderSingle; + selectedRoles = [ + defenderSingle + ]; + }, + { + ballchaserDuringOwnKickoff = center; + ballchaserDuringOppKickoff = center; + selectedRoles = [ + defenderSingle, + center + ]; + }, + { + ballchaserDuringOwnKickoff = center; + ballchaserDuringOppKickoff = center; + selectedRoles = [ + defenderSingle, + center, + rightWing + ]; + }, + { + ballchaserDuringOwnKickoff = center; + ballchaserDuringOppKickoff = center; + selectedRoles = [ + defenderSingle, + leftWing, + rightWing, + center + ]; + }, + { + ballchaserDuringOwnKickoff = frontWing; + ballchaserDuringOppKickoff = frontWing; + selectedRoles = [ + defenderLeft, + defenderRight, + leftWing, + rightWing, + frontWing + ]; + }, + { + ballchaserDuringOwnKickoff = frontWing; + ballchaserDuringOppKickoff = frontWing; + selectedRoles = [ + defenderLeft, + defenderRight, + backWing, + leftWing, + rightWing, + frontWing + ]; + } ]; - - -// selections for two players - -selectionDefensive2 = [ - defenderSingle, - center -]; - -selectionOffensive2 = [ - defenderSingle, - receiver -]; - - -// selections for three players - -selectionDefensive3 = [ - defenderSingle, - receiver, - center -]; - -selectionOffensive3 = [ - defenderSingle, - receiver, - center -]; - -// selections for four players - -selectionDefensive4 = [ - defenderLeft, - defenderRight, - center, - receiver -]; - -selectionOffensive4 = [ - defenderSingle, - backupBallchaser, - center, - receiver -]; \ No newline at end of file diff --git a/Config/rotateKick45Long b/Config/rotateKick45Long deleted file mode 100644 index f33ad340..00000000 --- a/Config/rotateKick45Long +++ /dev/null @@ -1,79 +0,0 @@ -ballOffset = { - x = 0.175; - y = 0.02; //adjust the speed of the kick using this variable -}; -kickAngle = 45deg; -kickDistance = [0, 6]; -translationThresholdXFront = 0.015; -translationThresholdXBack = 0.015; -translationThresholdY = 0.015; -rotationThreshold = 10deg; - -timeUntilKickHackHip = 0; -kickHackDurationHip = 0; -kickHackHipAngle = 0deg; - -timeUntilKickHackKnee = 0; -kickHackDurationKnee = 0; -kickHackKneeAngle = 0deg; - -steps = [ - { - footPos = [ - { - rotation = 0; - translation = {x = 0.0; y = 0;}; - }, - { - rotation = 0; - translation = {x = 0.0; y = 0;}; - } - ]; - duration = 250; - onFloor = [false, true]; - kick = false; - swingFootTraj = [ - ]; - }, - { - footPos = [ - { - rotation = 0; - translation = {x = 0.0; y = 0;}; - }, - { - rotation = 0; - translation = {x = 0.0; y = 0;}; - } - ]; - duration = 12; - onFloor = [true, true]; - kick = false; - swingFootTraj = [ - ]; - }, - { - footPos = [ - { - rotation = 0deg; - translation = {x = 0.0; y = 0.0;}; - }, - { - rotation = 0deg; - translation = {x = 0; y = 0;}; - } - ]; - duration = 240; - onFloor = [true, false]; - kick = true; - swingFootTraj = [ - {x = 0.0; y = 0.0; z = 0.0;}, - {x = 0.0; y = 0.0; z = 0.025;}, - {x = 0.10; y = 0.0; z = 0.025;}, - {x = 0.10; y = 0.07; z = 0.025;}, - {x = 0.0; y = 0.0; z = 0.025;}, - {x = 0.0; y = 0.0; z = 0.025;}, - {x = 0.0; y = 0.0; z = 0.0;} - ]; - } -]; diff --git a/Config/selfLocator2017.cfg b/Config/selfLocator2017.cfg index 1f811cc2..85731088 100644 --- a/Config/selfLocator2017.cfg +++ b/Config/selfLocator2017.cfg @@ -1,4 +1,5 @@ anglesource = imuModel; +gyroMaxVariance = 0.05deg; positionsByRules = { fieldPlayerPositionsOwnKickoff = [ { @@ -82,11 +83,12 @@ parameters = { maxVerticalAngleFullObservationWeight = 1.2; correlationFactorBetweenMeasurements = 0.5; influenceOfNewCenterCircleMeasurementOnPositionConfidence = 0.007; - influenceOfNewPenaltyCrossMeasurementOnPositionConfidence = 0.004; + influenceOfNewPenaltyCrossMeasurementOnPositionConfidence = 0.007; influenceOfNewGoalMeasurementOnPositionConfidence = 0.003; influenceOfNewLineMeasurementOnPositionConfidence = 0.005; influenceOfNewInfiniteLineMeasurementOnPositionConfidence = 0.003; - maxInfluenceOnPositionConfidencePenaltyCrossOnly = 0.2; + influenceOfNewPenaltyCrossWithLineMeasurementOnPositionConfidence = 0.05; + maxInfluenceOnPositionConfidencePenaltyCrossOnly = 0.66; use1stLevelUpdate = true; use2ndLevelUpdate = true; use3rdLevelUpdate = true; @@ -117,6 +119,7 @@ parameters = { useOdometryForSpawning = false; landmarkBasedHypothesesSpawn = spawnIfLostOrTracking; spawnWhilePositionTrackingWhenBestConfidenceBelowThisThreshold = 0.6; + spawnUniqueWhilePositionTrackingWhenBestConfidenceBelowThisThreshold = 0.75; confidenceIntervalForCheckOfHypothesis = 0.1; noAdditionalHypothesisAfterFallDown = 2; angleOfAdditionalHypothesisAfterFallDown = 30deg; @@ -126,7 +129,7 @@ parameters = { symmetryConfidenceWhenPositionedManually = 1; goalBaseConfidence = 0.8; centerCircleBaseConfidence = 0.8; - penaltyCrossBaseConfidence = 0.4; + penaltyCrossBaseConfidence = 0.8; lineBasedPositionConfidenceWhenPositionTracking = 0.35; limitSpawningToOwnSideTimeout = 15000; minDistanceBetweenFallDowns = 750; diff --git a/Config/settings.cfg b/Config/settings.cfg index 312a3785..c4960b66 100644 --- a/Config/settings.cfg +++ b/Config/settings.cfg @@ -1,6 +1,6 @@ naoVersion = V6; -teamNumber = 12; -teamPort = 10012; +teamNumber = 99; +teamPort = 10099; teamColor = yellow; playerNumber = 3; overlays = []; diff --git a/Config/sideKickInner45 b/Config/sideKickInner45 deleted file mode 100644 index f73f32da..00000000 --- a/Config/sideKickInner45 +++ /dev/null @@ -1,109 +0,0 @@ -ballOffset = { - x = 0.170; - y = 0.015; -}; -kickAngle = 45deg; -kickDistance = [0, inf]; -translationThresholdXFront = 0.01; -translationThresholdXBack = 0.015; -translationThresholdY = 0.025; -rotationThreshold = 10deg; - -timeUntilKickHackHip = 0; -kickHackDurationHip = 0; -kickHackHipAngle = 0deg; - -timeUntilKickHackKnee = 0; -kickHackDurationKnee = 0; -kickHackKneeAngle = 0deg; - -steps = [ - { - footPos = [ - { - rotation = 0; - translation = {x = 0; y = 0.08;}; - }, - { - rotation = 0; - translation = {x = 0.0; y = 0;}; - } - ]; - duration = 300; - onFloor = [false, true]; - kick = false; - swingFootTraj = [ - ]; - }, - { - footPos = [ - { - rotation = 0; - translation = {x = 0; y = 0;}; - }, - { - rotation = 0; - translation = {x = 0; y = 0;}; - } - ]; - duration = 10; - onFloor = [true, true]; - kick = false; - swingFootTraj = [ - ]; - }, - { - footPos = [ - { - rotation = 0.0; - translation = {x = 0; y = 0;}; - }, - { - rotation = 0; - translation = {x = 0.07; y = 0;}; - } - ]; - duration = 300; - onFloor = [true, false]; - kick = true; - swingFootTraj = [ - {x = 0; y = 0; z = 0;}, - {x = 0.045; y = -0.04; z = 0.013;}, - {x = 0; y = 0; z = 0;} - ]; - }, - { - footPos = [ - { - rotation = 0; - translation = {x = 0; y = 0;}; - }, - { - rotation = 0; - translation = {x = 0; y = 0;}; - } - ]; - duration = 10; - onFloor = [true, true]; - kick = false; - swingFootTraj = [ - ]; - }, - { - footPos = [ - { - rotation = 0; - translation = {x = -0.07; y = 0.0;}; - }, - { - rotation = 0; - translation = {x = 0.0; y = 0;}; - } - ]; - duration = 250; - onFloor = [false, true]; - kick = false; - swingFootTraj = [ - ]; - } -]; diff --git a/Config/sideKickInnerFoot b/Config/sideKickInnerFoot deleted file mode 100644 index 7a49b050..00000000 --- a/Config/sideKickInnerFoot +++ /dev/null @@ -1,76 +0,0 @@ -ballOffset = { - x = 0.15; - y = 0.03; -}; -kickAngle = 90deg; -kickDistance = [0, inf]; -translationThresholdXFront = 0.02; -translationThresholdXBack = 0.015; -translationThresholdY = 0.015; -rotationThreshold = 4deg; - -timeUntilKickHackHip = 0; -kickHackDurationHip = 0; -kickHackHipAngle = 0deg; - -timeUntilKickHackKnee = 0; -kickHackDurationKnee = 0; -kickHackKneeAngle = 0deg; - -steps = [ - { - footPos = [ - { - rotation = 0.8; - translation = {x = 0.04; y = 0.08;}; - }, - { - rotation = 0; - translation = {x = 0; y = 0;}; - } - ]; - duration = 250; - onFloor = [false, true]; - kick = false; - swingFootTraj = [ - ]; - }, - { - footPos = [ - { - rotation = 0; - translation = {x = 0; y = 0;}; - }, - { - rotation = 0; - translation = {x = 0; y = 0;}; - } - ]; - duration = 10; - onFloor = [true, true]; - kick = false; - swingFootTraj = [ - ]; - }, - { - footPos = [ - { - rotation = 0; - translation = {x = 0; y = 0;}; - }, - { - rotation = 0; - translation = {x = 0.08; y = 0;}; - } - ]; - duration = 300; - onFloor = [true, false]; - kick = true; - swingFootTraj = [ - {x = 0; y = 0; z = 0;}, - {x = 0.02; y = -0.04; z = 0.04;}, - {x = 0.02; y = -0.02; z = 0.04;}, - {x = 0; y = 0; z = 0;} - ]; - } -]; diff --git a/Config/sideKickOuterFront b/Config/sideKickOuterFront deleted file mode 100644 index aa2cbcff..00000000 --- a/Config/sideKickOuterFront +++ /dev/null @@ -1,52 +0,0 @@ -ballOffset = { - x = 0.1; - y = 0.18; -}; -kickAngle = 90deg; -kickDistance = [0, inf]; -translationThresholdXFront = 0.025; -translationThresholdXBack = 0.025; -translationThresholdY = 0.035; -rotationThreshold = 10deg; - -timeUntilKickHackHip = 0; -kickHackDurationHip = 0; -kickHackHipAngle = 0deg; - -timeUntilKickHackKnee = 0; -kickHackDurationKnee = 0; -kickHackKneeAngle = 0deg; - -steps = [ - { - footPos = [ - { - rotation = 0deg; - translation = { - x = 0.1; - y = 0.07; - }; - }, - { - rotation = 0deg; - translation = { - x = 0; - y = 0; - }; - } - ]; - duration = 270; - onFloor = [ - false, - true - ]; - kick = true; - swingFootTraj = [ - {x = 0; y = 0; z = 0;}, - {x = 0; y = -0.02; z = 0.02;}, - {x = 0; y = 0.00; z = 0.02;}, - {x = 0; y = 0.00; z = 0.02;}, - {x = 0; y = 0; z = 0;} - ]; - } -]; diff --git a/Config/simpleFallDownStateDetector.cfg b/Config/simpleFallDownStateDetector.cfg new file mode 100644 index 00000000..5f779626 --- /dev/null +++ b/Config/simpleFallDownStateDetector.cfg @@ -0,0 +1,24 @@ +fallDownAngleFront = 25deg; +fallDownAngleSide = 24deg; +fallDownAngleBack = -23deg; +standingUpTolerance = 20deg; +secondsOnGround = 0.75; +secondsUntilStandUp = 2; +secondsUntilHeldOnShoulder = 1.5; +fsrThreshold = 1.0; +fsrWeightOnGround = 0.75; +enableFlyingDetection = true; +minHeldOnShoulderAngle = 5deg; +maxHeldOnShoulderAngle = 45deg; +heldOnShoulderTolerance = 10deg; +accZThreshold = 0.25; +uprightAngleThreshold = 50deg; +fsrDbgThreshold = 3; +standUpOnlyWhenLyingStill = true; +secondsUntilLyingStill = 0.4; +maxLyingStillThreshold = 10; +anglesource = imuModel; +gyrosource = imuModel; +accsource = imuModel; +enableForcedUprightDebugMode = false; +directionGyroThreshold = 90; diff --git a/Config/sonarConfiguration.cfg b/Config/sonarConfiguration.cfg new file mode 100644 index 00000000..43d5837b --- /dev/null +++ b/Config/sonarConfiguration.cfg @@ -0,0 +1,2 @@ +minDistanceMm = 0; +maxDistanceMm = 600; diff --git a/Config/sonarDetector.cfg b/Config/sonarDetector.cfg new file mode 100644 index 00000000..0be84188 --- /dev/null +++ b/Config/sonarDetector.cfg @@ -0,0 +1,22 @@ +filterMode = movingAverage; +maxMergeDistance = 100; +minValidity = 0.3; +exponent = 0.2; +minMaxExponent = 0.2; +leftKalmanParameters = { + R = 10; + H = 1; + Q = 10; + P = 0; + K = 0; + filteredMeasurement = 0; +}; +rightKalmanParameters = { + R = 10; + H = 1; + Q = 10; + P = 0; + K = 0; + filteredMeasurement = 0; +}; +drawPlots = true; diff --git a/Config/specialActions.cfg b/Config/specialActions.cfg index 46552eb5..41147adf 100644 --- a/Config/specialActions.cfg +++ b/Config/specialActions.cfg @@ -86,11 +86,19 @@ specialActionInfos = [ type = none; isMotionStable = true; },{ - id = standUpWideStance; + id = test; + type = none; + isMotionStable = true; + },{ + id = testUnstiff; type = none; isMotionStable = false; },{ - id = standUpFrontNao; + id = lying; + type = none; + isMotionStable = false; + },{ + id = standUpWideStance; type = none; isMotionStable = false; },{ @@ -98,13 +106,29 @@ specialActionInfos = [ type = none; isMotionStable = false; },{ - id = standUpBackNao; + id = standUpFrontNaoFastOld; + type = none; + isMotionStable = false; + },{ + id = standUpFrontNaoSlowOld; + type = none; + isMotionStable = false; + },{ + id = standUpBackNaoFast; + type = none; + isMotionStable = false; + },{ + id = standUpBackNaoFastOld; type = none; isMotionStable = false; },{ id = standUpSideNao; type = none; isMotionStable = false; + },{ + id = standUpSideNaoGoalie; + type = none; + isMotionStable = false; },{ id = wideStance; type = none; @@ -112,7 +136,7 @@ specialActionInfos = [ },{ id = cheering1; type = none; - isMotionStable = false; + isMotionStable = true; },{ id = cheering2; type = none; @@ -134,8 +158,16 @@ specialActionInfos = [ type = none; isMotionStable = true; },{ - id = wave_right; + id = saveFallFront; type = none; - isMotionStable = true; + isMotionStable = false; + },{ + id = saveFall; + type = none; + isMotionStable = false; + },{ + id = saveFallBack; + type = none; + isMotionStable = false; }, ]; diff --git a/Config/standEngine.cfg b/Config/standEngine.cfg index e49339f0..73d36663 100644 --- a/Config/standEngine.cfg +++ b/Config/standEngine.cfg @@ -1,9 +1,11 @@ anglesource = imuModel; -targetAngle = -5deg; +targetAngle = -2.5deg; targetAngleDeviation = 0.2deg; targetAngleDeviationNoStiffness = 1.5deg; targetAngleOffsetStep = 0.4deg; targetAngleOffsetMax = 3.2deg; +pitchOffsetMin = -2deg; +pitchOffsetMax = 10deg; angleYpid = [ -0.002, 0, diff --git a/Config/tacticProvider.cfg b/Config/tacticProvider.cfg new file mode 100644 index 00000000..5ebf9292 --- /dev/null +++ b/Config/tacticProvider.cfg @@ -0,0 +1 @@ +timeTillKeepRoleAssignmentInReady = 5000; diff --git a/Config/tflite/ball/ball_position_v23_bs256-4984.tflite b/Config/tflite/ball/ball_position_v23_bs256-4984.tflite new file mode 100644 index 0000000000000000000000000000000000000000..51fc8236bd8f45594fdd499e27cb6275fbdeebda GIT binary patch literal 33912 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 QD7`n@h=28 zkmJZtX*XH^4=;Q{bUUkMbeR~Frz)|HDq```!yuwLS#r^#1p0M8C&^k* zqI<<@yuIq1=-BdJc>k;k`Kx9(=09!>d{=vmyDVfHH#KvZwK_Id{P^Mqs*Xv4OkaWI z%=27E{<9B(|8_*d`%^`C#LYnMg$=1H93zU+E<)EL{-G~V=Q7Yvmz}k71L1yqCw4XY z0kW_i6_p%QXU`>RP#rz9*l5ki#M7^H*{NS9u%{NBhp#(g(4e0+tK~8Q>URdhH8UlE zc+$eXiE$YRg96zYkh>^8cR4qB>rBxbcNJ!raG2w7#lqfScVPRq)iB}j0>(YD43`sc zrjKu>;49BkVp>=`@Xq-M3|6K9*HkM`{)~M@A3htL+BQc#HT*J;1)pH_n#Pg|r!;B# zvDd-gL;lo)w>z6ROmh-8l`xAy0yCKF53PCwQNhke^xV51hKZ!auy-=+_@@Y~ zd~li2aCnCQS@#X!n|%x%ztso34(`IbQxUPa^euKzyq;t6;Wy*@ zryN+P8WJyw3SJZ$1C!$usrUct!4|7*G=9oy`b^9Sk^IVu?Y&VzX&gSoINJMl;c?!3eOjI|cS_S7WKksmx0CR>J+I z1~W~o0n6B#!dq>0o3m$JeBB2BD_HBP$-+NP2k~g-2+qKfx#Y9QZ{aQ1r-YTc8XFj7 zgQPcS(lg$-v+^-I;O+VK?9bXkWTn^i>4UO)Ev z>7V$CZ%Oc<&Me6d;{s4l9p_w}*(Y)~2qGeecVRWHYLblFTWnBo7FE$Ykt27{iOSZ9 zsZ(uui4OFa(mmV(hu?=@(c29Rz~={_cyso8qT&q;)uC}rTgVY`|1cN1V5gXIhbvL* z`%19Xc?|EyIUmqowNKCD{GIG3=<;F=Ea32VkOC5%Vr!BYb7XCpT@Y zBCf|zLbo=2rKsxXpw|S4yPqr){?w{~or|x6rv;C}0&h2V<-l6WnvgQqtnx3LEw91c z)0DC|e_W9~E}cj&Nez}9QkxF81W2$&hr(dDTM1GhH0JIY8Y|JtlN0Zp5`cxTtjF~h zCSn0IZ#hY)J_T{pwlRef7_l{4ffXSHSG$tJ`zi5YMD7I0+T4yj>>S`+lR}U*Qi=Aq z>%+S<+n8D3rcu5g`8=yrmupQs&DizQ3hGetM3CC0LNNU4VDRHUG(XP}N_JhtXCD`$ zgkW7XrzM#@^Lmk3b>(HO%g~oeQncbG6o#r=y;VOU&O@L6SZuggxXi9cY~Gs9W=FfNj&7i|n5XppPX1 z_g8BZ&-`CApSFJ^+9Ovm`pZm&;_c7HJF_g%{fu@ed!Zw^9jD0fUUlG$RSz=x?^fZX zQ9H=Gb57vg!+-2r>nf@)M+KeRWRDMcZE)J&?Znb14eYuLrs9!hHR#^EmxTS8i|F3B zwFx_8^*n?fU zY&`5`-oET$mz;h`xbOQQbPn@I-uqlR)$(#|b3itg5ns!0$yNq$3$C-3 zMu*X=qId9D@mIL!l|V8pN=Mc=|HsKMmck(SEuwWzzp3RN1o!Ctixg3Kk`)Zb3mq+g zu+tn8*wJgpag$Fwfzttbg7&JRE3Zw4N?nn3nOcf*M9XgGk=-i_UKTU)~?N$ueXfo0JalONj&Wk)&D+NX2z4!lS_OYJlZ_l04r%vZtZ@3-T=%RE8EGXv)6 z5d(IL$tlR&F@o-lXrtn!B-E2x0Iz&GLrI(-v-ds6i#oH%x= z&JUw-Iyb$9$(^dov?m@RT6ZZCjeln{PtDs=eS10Mdf+MkrE@;8S)oKKKiT9oxNIz{ zQvAji=Gf5_Y!+f;WbZgIyQ#$8<6i6vm-#S#PCWg3$bcQ1V@On8TE>~{ypCBB`i7UB z;J_>k?=2nF@MU%?yTMGn4|8AsUEiCM32p8Jv7DmyI+f(e5bBMYEBYh%*X9l`&5Fx87jb! z+>7QEnxNw$GvM2`p{Ra!61(eDV(m?}cIrlxl-6i<#(3K%imFbf3ngnGfQ#bW_=GVU z%$NFQ%!_S{$sth;JUxFm%CoQLJ@L{ZNB>=9lbqbB-=0p)t-uM`?W^Ocz*Ao+jMf+0 znHDm9m7|Wk><+=k(i~v0=?1YeWQg$bt*6s}`@ydJ$C01QW#E&(3*Fiz5??l%#)1$` zq*8N~;SG#|OB75&@o5iccajNg(-VTN+YZnt8%G$2uWQI=tzf2tx`*d621O zi#Ydthz+MUfEQQ%fyDI>BI5NKg|9mJ{G|APK z9$d8mpZv*2v}(WsUD~z-*gkkKJ}zpavwkdQL=Ibo2HjGzc*i}8l|32w)+J*j58r|p zKTgA5r~Tq7)iZ#`?j!WYfEYB!Ulol8Tk!VnY#{=b`{)Oa8rb}2c|?cT07t!Q3iT~S zo$6C^!ZzRgNZ49V#O^JxWF8qLq33CDSucJLxG=)S2Lc^>fH93|qD?bujPL(1ca66w}kM?V|k5+j?f(K6>) zHokPXh#ovkhkyNxTsNsnnu-)9ZLvmZg~}L7+>WM0-Sg zA-VAcA+Wmzvd3iMr$;u!ql17wqv%J64JE@@&RoJo`z}Xuml>gJ(o8p64WXXbEtLDn zK{R@65i{R;F{@T}2;shm2=jsjIAhxm*tJX>UVB~3Y|cFiPu+sl3d$V})ol{_HPvFp z9y*vy=Tj`;vImoDEW+po4j?kI6o+ji(Zz7z2vNvGK_?JCO65`)TU} z$>jP<$)nM!)QNIMa&qk<^n^1+J@Wm^eH?L+dtI3)Y~8jD6~_j^^P%$xzlxPD>)E6M*zz9fxaGU!XhM)@Xl64(uOz%~IDPd@q8%_~R0k;Ts6^*K1i|&T+p$kqRncq=6TgTy z<32Gp5XmbTa;a!FlsiY0S2z9wxpZHU#79?=IXS&SQfF+4$|DBwk8`^blV^asTk7HK zxcP9Qo36xr?1fUlN4Rl*Gc25C&5T(t0vof&Am6Y&ZokG+ zr1b8Uc#nc5v{;h6K*_F=tC_f)05Vt0mUf6gz_%ApQ&oR=<7b5k_WXlMtKfg9s*=2_w(|Z@ za~zEEVAZ#1i{}sFPUyridS7^n9eu>Jm$|gXg{Sy}!-mBACfPf|;Fq*STNS(YZ7KJ{ zOm!mnr6L}`RgwOB=_b!Qn#O{y)`|j-{}vrSO^a3DM~dnD);PcTqj-jWr&wpcJD#MF zC61RGhy#}5oZ|KCX~%~l+?E^zhmfJmyxyV;E$JH<9r$7awewiCB(1e z@EMVTywe|+iOagV*p?}i9gQn|VM6}{Mlm~`Gs!|t_D-Rn(fVpcAmhXA!fT81^TuU$ z&}}K*dGI(kJieTobwyhkelU|so{`P_;vYw!wnk##Cr-rP{&KOO?Nm@wZzO(u9%ueo z6*0Cf=2TZ)L07GR&3V}FP-pbP$3AIul4GJrDIWQ;Nho;N1t(nT0Y7Z-GECYqy7nlZ z`zJm`^slf4)VCa9Y7c~=U0E#f3B5(wkBo9I7tdhQ4=NBQzS{x4e-bEu*ent$?4zv@ zX^Ga>s|lO2Ah%y9Jj&xhYTFqqS)m&=^5tP|s-Ey~5r=X96~#!LEHF}6g>I}!#4fCyN%gLF zBVzjV*+BVI!ec8h&?z~e#T8$3IL;3+LOj+Snr3xU#qY~el+cLTc1=KCG9Hg=LYw~pp6)K6eeRYhX$>J{;s_G8Q+Pi$6|lP#$hMMY{!)O`OMM#D%^X)ZBBp4+bC}1A|QD( z%6T`XQruU26jQL*<|*D0QDc|=;aC=@;crSPt}q=5RYMC!e}~VBU!ArWwe4+0`)#98 zbG-_?{Kju8HFF#|t#3fgeQG00sMIH-9)^o9X71)Ssyp$D*F}i7<`{`03bVMX|NPiZ zPkV_4o6^{_m8C@ALuFy-)dc!Us1-bQcr3Il3uE6*$VE@q6fvc)r6A0j1M_V%LFB3% zbng{0vAQvX>2!I7`uJZ^;ksLZo~b|=Crt&;f%U}Wtf#dLQ-W#h4+jaodB;G~uPNy9 ztaf%zQwDoucpBu;ev-N?BJ7L8HJE5o3*WI?RGZ>qa^%5hTd6j;L{&DA`u8%|DH4@2O@ke4V438ry$3hw`qrhi=;^(GS)i1d|7wflz5b zV|?`>IDmB^?t4R|z9SJv%wGq=9x1leY=m7ii%Y%p+CuHmC>IW=KdoEW6bh1GO`}4@ zuEdXaJJvOlVgp=OvIC4AyD#*ESWN1}fo=un>D>+*`CO&tifaj%vuf1I%V%IC?FfRW zW-z$#2eFPsf%KWsz&!67%l>rPfrRh5cx=`>VzDbGxf4BI6j$SiH!t%?pE@SsRq2TY zxwwWH&P+xbxl$S2t}Ru@$1DUK&AH(eQlZ< z>bGnr0;BSI#izZ|1=AVCQ^gGuWq5==6Zo4eG>FFr)aRhoy1DGc$-kWLX^3gv*0o4) z!7|KKD37G+EmQ$BO*p;18zn4V!0O5Er_CL5fppJt?y6}sapCC+XpL;%(Du3JgyOsN zAb0u%)+S0!czmfRoMm(Hrrww1evdf?H-r?4ew-W64m@yh(kMTQRw*4sB_$4wH2*8o z^PY&0IV>Rd+{VC?)w}VZhF8FG7YW$6{2=D(myVnSSEw}yr&1<*147oOoB{@uh;u(} zIOFQVkjQNu+^Av7lpQrE`updDj9N|N)3qzeW_}gBI$N39GJP^ZcQl}`&6~OB7hEU& zA1EL=^C5f~Y{7KB_h%Bcb=cPWmF%3Vh3Maou_A7g4~tqo*&nG%+?^}-)4HKw*?r&B z!Buq!_C_HALJmJ76w~4eY4c|MdU!7NQ8vFNj9e!crAOkfDsJE*rx5*Je3+;+i9!J( zG_g;b!o2N*DAPTX{OHI>6Z}X}m{|*A$3_t;E;XW_cf%Y%|7mpP;7s5+$&_m2tKmmV z77*hfHBk#W%LtDvN3j=b%Q!t_O2IGBGi<`Q1m5osPh#pZEdn>cO8Y6~5@+6whu=oC zXm8C!=+CK}sM!yQm#5OmE~E(Y`mRu=4YNU zKs>Px-CcE;xp3DM>?uBkGP8_`-Isqcja6OPLI;1)w~(R|e|2FyS5;HJd$wZ%haUjB z#9?fHusWgnvW5L-UH!J0Hmf`aLt~~9YsV>q$qN#|s+X^vf~-Z7fv?Bd zN+)G9Cn%Qq{pA+g-IPLZJ@E!luF@iE-rS+~?R>&q7(+poICtUE&toOJXXDv^g>N9Z zGnq)5{ZwMFn8s?ZF=V3`j{`w3y4hofC)jkGL{c!}vgGonV$x*KOzfXp4!P%_1(H`k zg)RDgPokRCg`TC!Q@UaHgyFIgry~*1>b4b!f#h(1V)oz!;(DhUBR8lDO9*6a$t*yHf_M8SwjD^n(Y=BG9Fhvl_>RJC@ z9$1zIVTHH(Xj-x(;KnxK-!E9;56|_X^A7Ut9_v~d82JLK(SzIS>6c|HOCBly45WDY4d3WE25Pz=}W6w2%=r5yl#A7zJ;)>Us!Kg+D zm^fe~R-iJN_LlL4*2+Y&TQ{OLYL{^3#}A3TG{(@mG1i1o@hGoYErh4vQH1R;Ud5Zu zOcHA`+T4HH*7VY=?#zy=VCs>-E^kHZEbQ^lUTjO0BJR3#E}Th;Kg8!ar{rX4!Ga&^w2VL|*kN>cxl;^X>d9NrkaLey4Oc;7Z48#7+xXuh!Yh(9U_|~Fi2Vk_lpS@gs8P8ew5`7(> z2zQ|goP}02D0|QckZvtqdSNyk-#-Ny*&&3?ne?$Hq)u~^f~7J>;0T3rO`M@ zcH(%hT8!%QD~Q+m&zOJhuHaJPW%k)e8#FYSgnri50RejoJe%Pc`iN!xMFlAete2- zoOZo2oBVm5FkZ(Ue7g(?^XaARpWg96@!w8@aN(q=eP zAJM6ZMPQT&f?uzG76-Sj7SAc)gO8R!#hzESGI3y#9!G8CF>ZTl+=}IenWlrnIdkzl zLuWawtmKH0*XmIG>?1YNp^&bom(xxeN3qk&eDX{KS6E;}I=NgyywpBNsAKvE%M7`~ zJrLF>4BEJk=bWSiXd)K17^>sJ{rAA4tmE|J1&zG75%NrA>=pcDkS_Liv$Ih2>RjFC zP#pHv@j!j?Pl}vgjeIxC!5Q06W1WMU zXu{%sSn|;f>f5h-RO%~)=}vMc((*cLw^$V2g5F`r6KZs6(PopTqF| z{dYv?&R5LsiIdrT@9rasv8nh^!fF$1g zDxs+S6}>&Fhh=S5kc2fJhYQWLL=VKJV2zm%tde_&jS?q`*AE9d@ABKQ@bX~R>?Q>^ zyxGod81-jux6UVc^(67$E&%P&1#ri`RH93G9o^Be0lU(}h|YE$=pTO-ABjCCx_#g? z@s81^UuCUB4F?BA+l%kPITiu*cc=R>Yqy~!t#l2#v$c@;w_BSzpp=BBWL)3{$11^= zb^}V`-6;I#TLr?n3i*DMPu6L z@iaa)$;S4MvLC#rBYhiyHcNHLzJVE(c3d7az4xYUUVv;Ky2fSPjTcJX=-rOHy&5OJ z;cXz^U$q&y^^Ae?GvCs|yF;jO$5-gjcL6=&{yX5wF9vIs8}OUMz3`-c167;ege1%6 zaep5-Vm}N-;S8e1s>h{7nCz3VAct15V}T0oDSHXLt6QF_2$>{Nxip>ipP$9Xe^Er; zd#;1}^aQ+fU?tNPxQv?5wgFDjG$w0gDOYL~$=2>EV}Cn-rebVT+2m8rRNILj;fwmu zI4T}1$?upZ4!QkYym_G=?(tQfm){Y;l^4FR5wkM$g%^mduS&C9f4P2WvtxVZ*`Oblgt>DW6#d!F_!k$VphCz#sGS7*hUm=9ZHi7&TG*`O7N zz=t(JvZ#G7I&&?a+5CRB)5VV(&`(yZ1GpZhy4V?Lg_(eN&-$pUW2wZP-!9a<2Voq= ziZ`fubv^rKbs%y79zbn+;ZVQiHuw7noK2Wyfzl#231@ck;kh3x*d0%fGoGsubD&=X zP+^m?o9pb6@A^c{+Af(DIHY2y&8yfiTM~#Z7ULx=cicjRrzSk>LcpKjJ`!q6AA?oJ zszkT*ML1hNQPjkKMkjQ}amDq!xV3i!ge^Cf5D2 z75yCQF_^-XI5jbWXRdSok`tLD9eqqtLliShcN|xrFCm!G*~qZv5L}s(02W=^$)*~} z=IT}50dB)W-u(hkrq#xl*!6cEe3xWGYZ+c5mhViZKZdR)pou%Il<&lUWh*c%5B#EU z-pypQU;o5Bf0;3DJztrCxAU03-2vdp(h62_iVl9*6lYeCH6R!5Q)M@JB+AO2LPvhn zWhK9hSWQA5#Kc~OS6)UD#~QS#);bMvwniT|3?0TkyJa(t$NBJv#Y?Q?k2(11_o;5@ zv}2sRN0J!#oeNpT)_J1ZLuvSe;z4fw?pwU{YDapAM9}qN8(pxt5;giQMJpOh=;z{J zjPu1I)~TZcyWmaY1;6Kr70>)-7OQn(wzAHRQC}V7*~W)X*2ONYd?{4 z&Xs`Q6j1Ece;}S&AZ&j<3in%-V?rkrY`AwDrgKJ>%20`fFY7}&5gq^YEkL}6?0aP{ ztp9!>ouBoACvAQ-2<7yg_?xeVNzw5xfu*ZHzu>Jp|IXhH{IErLVe{H^?AD@vY-5xa zKOQOz{Jod+=M}X|%-V$T!jCjU?N>74@oWasrcjOVtw|wg-D>7~_MIgCd^M1Q0l!-x@%p zeZSNsIe~BdRJFcigPLUS*;sz4Ra1RQe+TLFX`TRD5~yYWLD4tG4eYe(%@U74FS+SG z^~A`G8F2ZQ54^Hh+Vu^${KWaAmApA@JR3IP+K_cby*~F;3-^80M)vs5L1u=5JOA#9 zYl5GROUOKz&CUnD+44It_>c$T1GqjzqyFdWU*d14E`m?kEryj^!W4BUaevoc+A3V1 zuN8Pivflpyzs|gnY@Mkt$SXh3{`sxU-W@$GC^Nqy*tgXRf4%2xeZC(_e#U-1C=OmH&HK=`)0&+<=WXUyCp zZ4P_RzjH!D-trSkLdwrcio5GjwX&L^?rWzRnInu;8xgy5>=_>&%z48zL;#lZ=Ab8@a!(22A3b>ix0W+OB~{1y2>SZBRbldB2U*tjY7Vk`I-8C zapr>K4om8lc=ipd^Zd#Ds8$wUtCtRk{NSf8&XyPizk+G5X3{ln-Q0^yit7)yM2HJ5 zuLAehi{zc)OLVu%Pr;(q34%F!apbIT3H5qgNBHU&6{NAV%O!RFSbeMS|JUA^$JMm` z{a2DwloUymu`*N|Pv`8l_9+P=Ng^brNTN_mGF7HTqY$Msr3@+Rbk1IDA7r=)8AIHV zDKkkj{np~<-u&+K{CuB(o_~J(-uG+m^FC{RruErtuf00<+R9lxa=^!PmD%YANxW;- z9I$!Llk!u$E;55m2ZP?~t0a>;YzKP=w+IB010A$`ykc*~21^E5_i-$Hp)OjA2E$A2 zK5p5jK}_#2TV)}ua->p~6t+(#D}H?t;$|}&R(Ve|niOn|S4_}gLK5CF!RD?GhRr#m zWhWD4DjHk(V)MrG`p9x59Xl20uH-oPdKaMeAs1=L^6+G>M0{c%2X*#cVC6$HaC!#= zK}ykfx%S3GlKaue1QWvSB=Z|99X8(1W=Gtd;~-wJ4|utL0qf&3fJt)ziW%d>Jij;w zPEAN>ZIxq~g7g(k&XC7!;!tlGe=}L8pQnPAbJeikng*dfau3%(`=d}ht+FEWTYsra zXt~T}_Wkm-+h2sAb)^_Etsd)2R{pw}6R+mE8)ETJnz~6)q>R-jD}(xlk>YtzV|e?uAf|DeK20|EoCb#xuYsj)ktIBZJNt^jZ9meU{cgU2*B%5>$A>fZ6hG9b2}c3-0*boA=cY zWpCup#!5YQ!_OOZdFde!JWZL08PAUi`d=K)2n`0vBOhl9@^77oyPOMD?HwGWHZ0>lACh8Eks+()^MNb$nhqy% zRqWSK_i(iPWqj+%S7>p1C7K$sNWyW5*YXkz_AGfJc&)wGL8V)fprsM9GB0((xz~_A z=+;Zz>s&RwtR00M_4aTcOU%*z74Mknv0`TBiXAw7g$913eu8;A`jGvfc8y^1cuV%^ zu$|!Y?pHvvzN7es#a=$kKLQr$Ch#gj{rQa8DSY}-vApo(BVk?F1{88%1Kqt~$cp*d zFlG63lwrP#J>?(BHE3e&<{Bwk?XVG4+*pasE(=i3J2$-BX^438m(S>C9vSfRi#TCX zKYV?^C0|^8nY+UNk@&Y;%FT?bN1_3!71}Sd~9_j z?>#TST>o*J;P7)_K}1qNiAr7{;q(0ya9QI($pq$BdF6C3Cb8;FMT{MJ>iMk8em=5J z)ZqF7`NcQF^h6=pP&r)Qn4``VUfm>^Vm}S-DXA0cjQfK#=$Bf)#zIxT^pEr0*NM&` zG1`g0yxtL9Jfnl)s1Nw&@M_%r&V~(p-?W4`)-G`T;X}34YnlH^k zjn9gpVDv-q(o})(W_XR!NgfK7!lIbipa`s`ena4+{zTSMLq{~NXbv3Ye4N!??aDk{ z^g)z0Wg?@)n&MMIi*bAX1vu`;W?&>Qz3R5{m4XA3hB@)V;=vCjbz}q(>|7z%_I2c(EaEDP&TQfEkkMFvaG0>* z`4Din<`Uo7Rf7{>AI&~`uPZNJ1i5Yl&hz@COwpaRY)MnjN73ygKC+@9dr{qvw}J*X z5Y!&5mIcgD#YOBj^iU<9?e@uzZ7*s;Io+Nzx6)0ay4eS&aCWK$fAV4Co9{@Hjx-7^ zHfdG3K5HsB@OI;1%NdF1@`>!V52sN=oHHj#iRWEYBT?SqHNZVDh)bx*kQp5x!8c9V z0`9!o0X*mKaTxz`AI_eB4VaoqU~c*tAg`N+%A@O`WYjtI;FXK~NPkU`<28gYncD-0 zB%GCLC7bhpY76k(p37lI#spwdV<^gZSOH@WoI))zhvBvSyMmp~xC4BiyI+S?g7S<0vhD8UW0ozz)#h4=r}78sMtx#+8pa z^j2nIRK{QRQ>z&5t;Q!k%au*DD3H7f-&vvC!BnD>Sz#Z1Ba}^9`kXV38d8y7rN!w* z?ZA^gGGx2Xc3?w=8~Bq~KZ9LX$FZiBln?)`0P7VZ<)@$>-@EL(WL>y2yT$2iMW!Ri zjNKIP@U(dv*HKv_m6v5B@9o=wTlh;Det!*m`N;q|tXIMML-UyPeLQ(_+8xQMZ9)8n zXj4H=*K|p(`gXz0bZ3F^P7!y@3Ol488YNEZyO-U2T`s-FC!00d$y1}Q%yIVvywZX-_}$3K{0^PBvST}X@KN(8@^b4Wh#%|( zV+<47PU01UM;R?3M!yzIZD$AuH0JQ&#YX;C|HBfq($tBavQ`maLK0Ibk!Q{C~$o8=ns^u>;^X{&ehrLroX9@$#4|dLF zJPNNkPCri87N1l~3aUHFEt7*_mf{#zZ@wd3zPAP*|5ku2b3OPA{rd7V6#anFl-^*s zXn`b0m*)ez^{NOHI1AElDLdL|&E^tvnkL!(ZS{I_+7r|;hYj>_~O$C2z{Mc zwWXynan??();R-4zA0vRJ(9wXgBM}mY$YCbVkC1#HyOtiIf8e0pM#joWk^^vi5;f7 zn_GTmuITMy9p*hVjY~9RQGl@?GCNzyeH&+qK0fM+xC+3eS#0En`!_IqzT|_VuC{35 z_B?^+tP_lyV!CuxYAupg+`~ zG5VQloM!lPV$WA8`OyIdL+U(>;{1Uoo&c`kgqWv*zA z7ep}qIPhLx11_sYKC?j?nGj{b$C{}R5E0qp0uBJxp#_gGx5=Ah0?OQOv=P+1i?#3>8lMJsj930bb zCN7FIWSHrW-R#}k?r#A0%jz+_8;HA(eg@+P0Cs`dS=2J_A?`So1?v56!FuftfLG21I?W$|hcFI5 zbah9|U#viz2ByO9*(Qu`%?|iyzeIHYU=cht_7=>wTM6PX#4%gC)IeALLi}7Q7n}E* z&mM`>VYS}OVE=rW3rFQ&Wio@se4qLw(7h=hRqJR0+14pAtT!o$2K>yG#~lmc?@e7QI6~lR7dx99MBZ)iO@X z>_DR9ysukED=D5;Y+tv zT(lW0Jy(d(sX^Yd;NEY6MV1-pxh>Pt&*X;aS=ZyTT0OaFpJxV0)f&(E)hbE*L<@k{ zmLQ-wZW9=4912$IJd%|fd=bqFib1s-9>^ws$(1^-bmpqyEHHOP1nl6Tf|D-I1jnKp zWb3Q`L_x)ig*W%BGT&BxEq{O8j+wM`AZPWqMrPE%o;fUA%GJNxfS&l+!G_fic;|%< zj{EG%zuWNa1w*e%K=j9WSz^v-neS3d6yuu&K=A8zB&lbxTSLwc1uN)-nVe_<2Z*7!PR)xYj4;iIT_t! zv(SZW$1n3Ao!E~#y)g;J??qH8d^R`$HKkFjAV!v-sSs1c7Kf(jybkj7B#9K z)%5aV#Pg@%O(EsD_fl6lDl7_`+iwvSEnO;WsPTa#U(ewV=#2x5^**Bm-h+gHR+Nc~ zhWSI4;Pz#YQ%ixGwH|)o7>vHSyagNG=W}^GI zD)%IF&Fi6*{QG65CvIla6epsM+wO~QWik%7r*|=4uc95_MP-0w12yxbIar<%3aoa|=Wg97mqtHcho0@7CN+zVb-Z>~0=_N0hQNz;!n6_1z;fqk)T6LH z$|t{p@@eOQ)(ni4#znBQIKNb8R#6L>GkZP5cpQL-ELJ06uLwtmx8rut;~jKkbh%T5 zG#Tf1h0MX`3k+~$q238a_>NpB3j*W7gi%sdpFWPem*)u%y6VZE-d_j@`(2iW*o_BG z8QOST$Rj3eO+O~-+F9-n_XRjD83M&8=b>}%IgE`(S6Hywn0+k1!v!Wj0rgQ=!AHI4 zU~FM9<8&z!m}taHESA}VeuDvdhgfe2RovlvB^`Ec<_df@-50b+?s(vEBmDK5BU-GR z225=jv_5zT_eC9mbszHKTg5{h(0Yf;`W(i?3?HHgiRO3=`7KfQ`(jo;X#l^9> zpPLsr(IpqPD68S=2Zu2qHO9boSK2Z5QF#>`*7U%g^^XJJ>j`Md@^Z9g>w5fZ-xH?K z0*EG6dEtXM)xp{>SGcQR>$tJQ4RJ~KSHSMghSkpY$Zce@tOw(R_T;R?cV7id{4I`f z`*K9ExV|6kWAm8P9@vu^Y~~KDTC(757k7Ma>}lr6(HWv$ikk4$opcyKPFa{d7{K|DLw4K2B#aG|Ex{mb?!--?Y2IQ$`)sI?$8Ua zXGsCF*=Gc%#}{#vt!Ih~zc@ixfg$IS=EK>&Z{R4kGbA5m&NW z8>x&uAbZ!V6Ug(w4PJq9NbhSY6KddsG9RR)jFE-1uHGjc(w$VeX#>2O`CKV;;?gFW zYVRaY%ikTwr48klee4RBeGQX6*cAZu6l%f4Wq`Ts(!}KEEd$HmNJZ-`;K7!p}vQxSYO;pdt03tYTmv+0lOFw8L&qe1&mB(%}7GKhFX^xA?ee6>3WaxRcPBRD9+c$Hg?F~8W>{`*d ziEp`UugazVXZHcKbx(!!Zx=J|ZQVdG%|z}F`P=S{;bB~vez{|_%OWP*u`>wYwO7G2CdTfDMx3%FWj#$~t!O1n)aMl)6Qm;%gsI;Co3@8J{{6 zai5%#@3En<$Fe4o@rkamGUqaQUa}6t`cgFZ>@*Ork}3?g*pJSNv_%hB&csdMEYK_$ z9ef1=7`r$~sNvSN!YO8Gv%+Ch_+AhdmTA@|X{3rLm@#LG7gz-AlVWt*?8 z;)WY4K!*@b6ssB~GQHx3K3_GLz5fiEfG!>3^yU(W)gBu_x1*Pl_?SCqr<@^L{niw9 zxBo0sRn$f4*RzEC^HVs(r{{!GaZ^FW>quGm!qI3{#%j^+{LS2@eM3=IMigT>X9n}` z)g5N)k@ZZ7$zW;E6nhYeR&q{)Zd~?@v9f-7a*?b0X-+@;YQ+%c9kQLD47Z4I>{!dc;PF{-^!TH3245`H85E(ofB15gT!~=IQz-DvYoX7j2~8(O&Zvp zvyPa`rJPvC#q704Q@m;!jpn7`R`f2=x#XT`RtF1iW>_)zRJ*@ay|x1!d!|3UZTF6w zy8B$c@UVI#x;QZnx*!+y_;5to>#6NI`H8nog;z(; zG&2x;#7@LrBLZ-s zh2!gXaYs$RaYmy1;KGE>oJ+4pX4=aibHNql1;$1tYMjQjS!~*sdem&*o>i%Y{}VoTxG^XR%>IJWc8-iqGj*=_|>rqZ0~oeY|ZiIcwJye*8bF5zJJLnpb>ru zifbkS{}FFMSr`WoU;K*QZ{|RE)RQkZ*ux6-i;+#c24vNBJYRr(`9k#pAmEELpZnDY z__@7={R>uL|Lh^mfPQ-XCFkLs6?>kw<|ElqM&I{-~;b|uOqv3q&@4t zbuvG_Is&eKZ^gUpPGXhrzr`QdjKL%JbV7OuPl8#Kv+?UD8EY9P;rseoa#ka~+5I{m zct)lVzqL>V?jC#tZO5%aA>FMI=S^PLGNV6!5KVsP(`N-AcVQxv**J|)8}N>m`wbR* zJg$^%P?^u}8dky!I4#~<8paww)8M(%L%7q1bNKZUYgRmbB;OL%L7Jac23;!lgOXx< zHoI~Vt=Yr34&#&VnlM-v zfbU;f%BQ)!!->B6xMrA0EZqLsVS!c_`!)ClZ(T5)#Us~n&D)}&hQ)FA#=^t=+bS%R z`jo)l$qguMZwhOu5Xerp^G6nk&6rDjWBEIp6JRIx;~)Yq=5PAx^O*}j@BzM-yn*@= zJn7?MA{Q?pi_Q9WExn5rV)WdY^+#aY<- z#9f>_!k;&-Jjh<1AI~p2I)i=l)tl8VkK@gs#j{CCJDK3Z@qDFU20N&4B0nSMCVm?p zg~uOF#V5@7@xxyO{)$0&_W0LQyz$It+4H6S_)AISxXE8;fu;)!dFS#&a97z2PP}V7 zTeq{nSigE;g+#iSy&9j(KU1#eV>E5BU-omH;9rZ%YJ+h8YdIUX!4)|VGy+`?>aow^ z8?5S=g*Td4Xf$Fq5?pD|AB}Qm?Jat+@^mHm@bX0L)^G=% z)qTSFo5sMZV|PH4)lg(P6!5QvnqtPdQII0bVRz~6;Pua*fM@>P#5S*yvNL>+v$6XG zyy%fHiaTD-oiC2%CuPO6;++xT^Q2%lr{50Q%-Rs1`$LYEFZ4laQ}1FILtUt<`;5<= zcm{_gy})HpUm%SsXK>)wdai6t8k`$;AJ^3YyeOj(_L{MQpIb3PoVX-kFd|V~JjAJ% zKmL9luduNbgzDj}b#NNH+BgndtsTquG>u`qIIrUGCseYYAFZ%;lo~2{6j)J^(}_P- zs>3TCKY_1Q6vIo2Az*;g2j1dN9!x0@=ll64%P#Fv;yq4y!AXBaL)XF+nCW>K*6zj3 zr5#Dw!b?qjIJ0xbs~s+^e!@!LYSMeYMbiV#HXaG<4GVCHheX&pjbV$bvRIhE1!cVI z#KMvd__NVUex_CtyyM}{qa$aTRUg*kli>+$NK|*2AE?e7E%RebCSu(1_OG$%e|M?t zyY@xze%L}^?%t2d;)8tJiI;VfgX&DNyzl#+?DP%2_^*~jb_>~W_E&~P96f9jvm}3fj&NL}aZZQR$l7%pjM^Lkybqp1Bfe$n+f*3 z3Ox!uxxt^C;PxRiFcX;1d`Jm|MZ60<(7=HQIbA@tbt!C~w1)i{b`p;5beg=ZEQ~A4 zHG-}N`&nkz2r$j#Dqgj$9jlli!zYX{;QWpZ>}wW{#i?Sxd&m<|x#$MuGXI3zFB|br zSD(Y=&`mhI-Dy0+J(5?wScXr2;IVJfCETJbfv2*r!1YU=@GU_;oVn})wwk*iR(7nx z{=OktHT)&w^(FA-u|2F}y+7)3~r-z*__n;Gr9^9sA4LB@Xi{CwQIZBDUf-j6e z%gnyt$SoYx9p2la&E`F*$3FMYfc?XF;1*i}8~6V2TzK8I0!|f#LW@sZK;;E*MrBMd zl$F~AEl>I)QLHcW961gzH5!7)O%TI(y%vyv!MzD*{donIT9x2=-OaH)ZznkA)Rk3J z-;LC!G=YWoi`mPG43Ew)hqtc#!^ef8vX%SGnLO=c_~K>}(zHyM6~&GM8@*P6qM&|2 z^N-Q|+0M7&)2uY~xzlRc0OsJJaeFv6Bn|fK(~}*3bRrBi9EjPm$;=6x^(d^l0|;O6 zmKmpcUo>#X3+!^+4LwtpfXKlX$iQ6+A4GP=Y{mh&Raqa+-<601x!G*lu)|nsZBN|r zQUXWDzk-7IGTaAf;pfSVfpz{sWS21+X<`w;7o-^9(Zxl>7Q#>dVfdMPJHF`UCD^Oe z5v*gk6(z3R05&-;18)7)So8cf_@qFIuPuqdg@f0kogbRO`fXVZsTJxS`Y$-^;Ov`U+lf<_8vP z73lG1q5^#B<#6aD?T-5gZ-evBWS~p=>mhgg815RU4Esxr@Uh1su#=rFyl(IuiJecP zgrE#&#~^JyTI7%N6D2TSdn99a&j}3~RSkWYe1-2i*AK zM=qF3*))XIebUF9^Mg*VijCcctKwken7T>t?b_s7J7cf(gJIi317+nan_mN z7^cl1JFWtN;x^n|TEgycTm`=>nX{vF1w3;Z^7=hr;Zqr5Ao`RRKYc+lJQJ{nZCR_t z-!B}^7lrL&J8P@(UfuIxopdeR;Ifu~tvi8@*EVJ4uaw1m2TNI_@vdyyscx*bB$K^& zt_vH}PkG$!;}r{na3)fcjdkA=J1Owy74YI zve|H-ZlKH2)40wn30fDaT}H8$OQo3Wy9STFKbi+|(a<}{0^iaYjbj9g z?1GO!>=qMSCXE?~Cz-k65jRtD&N^M*cH>O`(}NrwxWof*OWnX57_{I6!#YFz(qu+D zr5Lw#+lZRC^arX7*MaW$#Tfr3i~1$lc8D+VtK!${pA=smRRCAVdWp@Rj*y2l*WnYp z40+8!E%|L`1OIKZx%`M$Cci({L_T{)XL%hs%^#d&EYFU!mX9cG&sP;L5*vo7$n|TV ziz{*Fc%X_;Q$h9tal6ysz$_Kr!VBaRj%2)r@ z)`PSStnSYS#%su@zAP7i@zxPX6_1l^4(=*{I^_Us|4q?p<~9wd*Sag=w2L9~mQK6n zN!iBYB=0@qEjCtiKB``PIlhZLr9o4^cw&2T$)=G`_y3Y@LS#$4W5h9w_KS<-4wIjq4RxN@Jh zQRVrJr{BJ9A)x#6j2S)B(`0DaJhJyqL{Ol`_dR!X2(O^f`RDFGbU&ERgrU3v0Y{#v z^{UKk5pmjPBXn*r)y12wMK3huTErex1~9)j>Wgw$`P(`V!Fi z(|vlD{jE!hwlg81KA$H%-9Ln$Q_%Lb9c|x-K%1Zo0sgyv8(R+&zSUM*pSJH!u!r!! z#w3$)^!rxOdQ?v@0$R5{LI1D+9+Pf_Z`DKVQyC@%oeAHnr!^)Ggrnb6(4w_2)n!aT zbDZW(+TXg`#H5b!^!tCtgtnvYjRiU#o0uFYe5(sLo%+lAIAM;ko@2;ZuQ)~D@z67(iK{Y(x;+Lts}e_R8aKd*sS zo#cz}@uBUgE`0)Z0_uASkz+ziawt(5gXjr?#`oU(aXnERTjNAaDwoQjvU(8EJ)_hJ zXw>vc=F{s?okX9;@Gl!j(b9x~wx!uhuTz?{t-et?^gG(WR1UpH|3!|X3E`=~R4cWs z^?IjmTR~+|+vq{_zLlrr20f?8Kmr$%^p{9yMG)u_a3pD_6DSiD5aT@vlnIhZBA5_V z5`}>T&q?rB5SS5g#CaEj8WMmN1cM0bh|(1V&13+`B`_lhCC=&)B#{fimVl0>CIn}Q zp<;q3B(UKGSBZhK1dYVL?F1eKq?*FAS7c8uVm>kRd(Tk&X@3P1p2lu`gnvZP%*ap= z{{<1VBgk%ML4ia4LqmmDwme9}y8W z&(bq!VX%LA(BE>zq+aLOZS659+#rHxB8OfNt#Yg!{YD4*&spplG%E_qGxJqdL9O zg@s1ade$zM^pmyAzuWNW7aL58Uh3B+!ng5D@Oyrlkha1%ZDZA+aNz`f3GYwJpN0zj ze%k*#ehK`>2Zj4D@DB(LYU|*_R@!d{bv2OapgHu{D)n? zsmL-gC_ExKYWBh)YtsH#ozn^D`nGK>`Vx-D*NX6M;_L9=$Ct{c&>+bAMP?tu(Y#3@ ze4D&+_|M|&;MX>+zvj(9Z7?EhD@5hb>$5lcK>boBd>g;)f6p&^FIe!4%wB|}c{7#p zZSuzcKl97}e~>q=zEZn7Hn#QEgm5%(8i`$P^5(Y{{hc>-OF^oO=4z`>6LP-n7oGH8 zP2(F(_%`vi`|smRhUM?~1X^DHMJAO)^M)gQo4m36&*E$M|Hzwv+TcyrVTj6~c|+$A z)GsmN+xTVsdw$VzwD1?1282r?$RPYmQvSTIZT~aB9RJm?zsFhI|3O~0#)8@%(9|{- zbiP9KYBb^7;ST!LW-Y#O7G7! zuXG6CCa-M%v-sQmYyAJdUjAvr!C!2kbE3WkTL@2|nObw8b>CIXG5!%_B16C5i|IT; zOrSt!VRSFp?%!u(G+OlN@O^GZPQE_}(eZ%FBggmoV>6LYb^Vn8_dJ3Op9>>sMS5-1 zd%h=GAE6&x?WS`+TLOmg^uGI3#$S8GS~}16U+5GVNMnJ8BEHkHUnY=@*&QUok-);cZCyQ|52qjE8*B5fjq3 zRnK&CK8>uYP^vW+^qG$4{s_Xi$^GBf^Y6J&<3ur?;PNjz>2n~}$q}9*<$vzm-#SV2 z@BU3ErI4(J(2uRY(PvHCFMSE$reFSx8!qh^4JzO-&_3Asi%cq8Ob|_YI`{Z#`)}Wa zeq2XXcB>u}a^Ccd9{OBKV^l}@HZl5bJ#Aw|eWUtX`^AKuhmbWKO1H+VGx;DU@FIL0 z-+nt6e&{eEGEV&>n?B=O5fl@ie*ZIW|9%srx99KqOGks&I8i%|$hrlkT5YENB_>cM zd>fm8d+h$9hstZ!Lv^nHMGtud``#bngm2RyzpdxL?+>bzuE|g{T74srd*Ac7p73q* z_upgo-F_;^nXHZcQx17l{4Qq@;oHdhF9v#=E3NrLV?t%p^%he6w2g8!4l4+6LCT-~ z>G(VMnjf*Ie!l!gE_sCgF1O}axxe+y@N_#LP+t`Ub+TA?X@C&8+(6iuKq(V z&9$6g9xk`V@P5D*sZ_Pb^$fZ8hxd7c2p57tps?d+)|DFrbyc|TJ z{QbWl>p8#d0q^%?FZb_D6F>90_1dD6Y2SGe*b?+5pjxR7mxvGanjwc;Ejg$21#)zI gLe78KsGv-y?%x7_@BtdcQaV1+)-*?GjA`+I01-2ofB*mh literal 0 HcmV?d00001 diff --git a/Config/tflite/robot/bbox_correctifier_y_rel_0.057.tflite b/Config/tflite/robot/bbox_correctifier_y_rel_0.057.tflite new file mode 100644 index 0000000000000000000000000000000000000000..1191b4f4dd5860ea3ec20cac81905834401e4950 GIT binary patch literal 25416 zcmc(|30w`||37|PMJc7UNU~RGS9hB;6D^j6w2(+CYqFFSk@loSC6%(3Ek$U#ckaxY zYb_K>WM8tCCE2&=e+IpITi)M~_viEY|K{;{nz?6Q=e3>lI_sS1b2uC~M`tTdjv+^% zqr=hUbmOR?G$oEadgh=s4kr=e7Q!VShtq_R=*Qv6q2DqH@(4YSx3{x-SXv>18bWZu z;?OxkvwZvmX8JA(4Ec*TqrJTyvG|;?GrIc+1O*59SPtW&=MH`|rS0u3U-tc+poL39 ze5@=i(X%XqH3IwY8sfvsVxPMsj2P$Q;ov%P)HoNPu?}u__I7S|$Oe_qb||6ug70YO z&k6Ebyd)@O&inwM(169ka~1?~F!cT#Jp5p)o$kil69 zvk|5vOhOosFcN`4;3AkK7$F!U=pb}MP(t7!yhXQGgeHVr2$v9Q5vmZR2qJ_Mgxv^R z5wZ~0AtWNqMqqtccle91-O%%wa##J!XWdX*e*_Lk@pBq7>vNvZNt`a9YYJOWx}XHM zY*?^7SvpHo3v*L*bU*TiKg-L))WXWt0zG`a!nU-xMjfkKcX97Cu=s79zos7JVja1G%W!d(RTRTo1fKj2&(LkX;y>T z^t}ocrCTxT#yg?wGMbsr*fI~lqrKUX4d!ucfT)*5IC6%GaBTc(Atf1v?U3n?&0cyO z^f>y(Mgs9|u_&N(7G_W!;SF-)yqmzQSOl#bjliL7H)w3#AOFx>i)m}h#!Di0!0~P- zxQzpWhnwZXlKvC0*}a|szwdbAC7J|Qu0|MAy@(^ep@dsMJ|UiFM0jgs#LgBs;>n6K z{MCIYf(ZeHYJLzAp)rIoZ%-g>P9DG~YJb4@eaGwg@%IqAwBhMavbf|HZac|imL5#S!I1@Q|lSv8emDh|BhFG?ZL#UvIm5o z;YnP1Vjp5YR!GdAG=Z2UuSg7{&)|8lHSNL^w6~7j9m2hjR zAsml=^Vf%XOL$^(D&!5?#Ka}06J*OuW>^Rxo-&)r7>=6(#XWlB$42azCXv>R)wW&0 zt(!4pE$m6y7Y)OJ!W@|P9j_7f>UJ6v4-r>y=@IqjxLtm=Jn_&wgwUO-PWZ}R#`nIj z$H$$SL=>5t6QhcP2(|Q)1O|C_lh5;rDUlP28((>`^~2=s1aN}hfcLqXhUc2~!d=%} z5JP7xOEoghndUcd@Ghv$W9vFgo47kE>x z8-4G>iAQ9?z8gBYBxVmh)R+(30-Le!`p1Cm8zs1ClR9W0UxVe{FBfXeErdN2V&E*7 znard6Hq7PKmCz$y049~&GIF}!z&b@1o=WLL-=hJLYVV}IwD&=OCL1Jg8V6o2X5cYX zB^Y3q30%gE1N@`0AaVhho*lD`+PF3WTu|uDJR2no^z>WlP+MOnuhj;=s*_4g+WSM= zVjJz$V8wLX#m7BQ4q|!~#l!brH)t?$HFnvGLtnf20<<0ZgauRrP%~djEet5f#+b;# zop>!(^Qag^53>=}^3$v&$0zTGnqA%-3!uhRhs4k1fz>%A~!nNG}bh+nh@G#>hw&j8f z4X64`;N6fS*e^j580w3Fsq0ZF=kb#AsQelas4h%yJtr~ITY!}; z)&Nb$;UF*MDD64t8g@HJ7p%;BDp_AM4G7}6m)9`O849*p-q$@!&-%1;IN-Jidya?U+CM|S|Vt5g$Y&mJOFbr(RH<~`WD>3Z-vcd(%6Rb^_9OfZ;Xrwcxv zy(_7-eu%AB=|V5%cE)#}b^z3r-O#%o$1)hUivWoj2cLqcA364*ANB` zmtx)aSqY~)uAyr+17NV51TOp772KIo42#znV?nM?w4JIdz3BXNm{?~FD}or|1(g=e zK58JyzTX!cq9n$6Jr$v5YCXNrpjz1L-VLm|IT{<3H4@BpXczKd7K6>In!xEu5nWvC z2DGBJK|?tulzG39eh^nL9OT#=GB4X{AIC7P)-DBmW!D26W#^0~*Yy)d`-ri?jWTc_ zipA{rlVQJ?%J5>G43t)E!c0Ed!tE!&)FrkfHjG^@^y??WY8o{`T@3_BHLt^UVFzLO z^K0#<`QLgQD%i23&YS$jn`~no}O}z@Q z!tvm2_$F#JZU9aTTImhyYe0bT0BlvSg7>@ZhZzg~po(%ZUEaI`e6+bQY>hn*ij?O; zog3r9Vwp$qZct~K|IvskQP~OJ78XNKsS{}BMAJqGmq7O^YvCHs1Rz@(485ZD!L7S)mG%tt& z=7}r8tlkOmk^jlasr2L zSHQmY2cWvY4KSYF84laJ7LGFO$?Q0$1()TAVXtaXx$N8sy01zC?E|d9jH&6EzgG#J z^lT8ACmMqpp56jlwdI)c%!rW&kMG%9}PE~=hKFLLxE~F z2Rp1k5o@EmgO$7V=8{g43V8lX9e4*O!%KDkaA;W-HpjdSa(vE!PPYeR@w={Ihlj7H#bFZK zQR_DR5Sv8rsd)ic^m~DE(;XRuwZ&A^!y0JNbs)&;X-SLc`GKy*cQHqq!;&H77En;> z0XDYC;wF0{gsH|7dPnE&U|-F*e&s~GB?!z(k@Rtn#u648&_^RX!C5<|0P(HcAjGf) zn%>z;#d%u`-yXJrZYm>z(eYYA-GY5!s=_+3%ViRFxrxH2wI*SeD!Pu`Pr_2JN zbaE$omY4_!4@rY%Nf0zr&*_x2UBHHl7`l3y8yFCi4-U2OB6oP_U@LaN22)pffn9#l z*zrm7pb#%28|_nI{riP5+bIi_eb|Dn9UY2&>enAWtpl)qogsV>m5I%soJ4nBzF9IT zV+zpQF^JZ2lA~Lz zw_`HT4WQhcqx7pawZdhj6YMKe2X7pX!_ngnF}!jN6gQuumjxc+J~c6?h6g0j$Bz4g z(|*S#^Rk=hnpNAW{?tV339yD2Y&TM2mUH3b3y-l^XY#O7^?8(u#u_^0x;`9!X(p|? zo{|{s(u1igRv`6Bp};k743PStqt$L?(l__2(dCjyP>Z{g?&4Yij>&RBi_%S6@8NOC zb6iI|jw+(>Z0ij7E(pSs8)skxo%HFk-RkHgcL%~VJ-D>2o(J4wr+~j-5zx69e7S++q-;%Iwfh_a<_FVcXe;9m~kAY<|BuLe}PR(eJ2jPut=mQpMSb^Sr zkg!P)zmO~d(VY*0&0AEM&>3Sv^~oe5HmWDaU$RH&pC1Yv-^b9!pDw^dV?uyNhzVSq zGYVGujRuqA-tpb8Zh%XJ*FyJJF~;B83m&f60%wd=2h6MzAk>xzOLSC0l*bad?`;Vj zDKm%>o;wa&@!P_UCy&76v7Mk_#wU7GbQdP|!bs5jH+#o` z9GZjoG7@0b6{Fy(%vj)Qy$^J9trWfpc?Loq&2W#ZLMU1r4Z@8Zz<6g9?EITds8aD3 zJ`cW##rs4;dyThH_qY?7`7E3cTs)LfP+A4EZAEmudJc41*b6$jmx1T~j!-9ht^(zE zM?)XE9YU&n1+dL0XqU_WAh5Ir>wd!u=Lc_wMT@6_gf}tp)kI_HIb^jkMp#0F=XW9g zX${@wSyyl$OM~UbN9g??2PhfL5jbw%fi0M-B&@iq1Md#J2MWZ?;3-92hV%X&Trv}Y zi?TPd?okQQ$hiu%ovEeN>S8fLkEtL4ub_r|yaa-kjmar5#U3nKx`eKoss*}5s=>i#AFzjNyU8n^o`XZ_ z>*2%WvT)1Yo^VTuFB~e&2HQ(jfy3Dfup@+o8{RztT!$@?k?jGe#*1NdjT=~#ISH@D zUNc`rd_c)B;;)$l#d zQ{I4Wa7n}-yl$uZtj!)tMVOjc$reDiK4xW_Y~%-&RjBo9kk8S{f03yUS4<}|=l@lPR@bcVJn zehd%q(SZ+*9VF+(??LaZ2cX4Oo$>555Qqog0z)l5;S=O%-^qjEqhW{X+5|PwuE_z` zhc95$A77Ee#Of1HN9Q@ zk#5c}f)_45ri(4P!2L=D)_P|v`OIN0)}GTBZW#5PUe$d+oxe04TQj>id|qKHlp54w z3pfnDPk|4XpE!kqMRs&jLJ`$iIv*~K7l4b3+VJ`lT`*Bk0MzCAR9X54(9*?~_PVb} z>mM|P;cM4H?}lX9_v0zRB;5q^HIZNkFs291+6s01m0(Y300ZR$K;wD`xPLaJOE+^s zv6TzuI9)>C@_Yy_$8Cd;HcGIT8*y0sH-Iq<8(KlERd3! ziw#(`fliSR!`4>&2=$dGVug=aON>U%!Jgi@3B%t}bk??9;U`*-7C#>gl+R0Gp>hv! zYt% zN%ZN;&RCzTbK$9iRH|W^3D9p&#)?N((hv7!0AhvMtfV>lUYZH`)$X>>(0RB#!n!%nmlbS; zF{#Q>_N9W*hk1yN9JGgSe(D8gtO})FFMjP;(A@V_@k-FwX)(rZRlpYob6N=wwT;A|U5uf{I*CFr?MA3t?E+%& z-NS4v+XX|PEyg|Hlmb;}9_)6+60Wz{j=d9%2JUgEX~mOEFj{v$uyA*S4?!w#_tP9^hS>Jn}RQj@=FqTAv1Wg(6JZB#ZXDY6({_IRfwMM9?XV zqM%t%W zLLa!wZW_$%n*ff?y#u;@+(b9Y@q~?>LCkDA4Q_f{0^rR{N9gN?r2Uupf1xx)D0j7K0r^EZL0Gk37Kz;o^xV!W*9eN}R)w65h z_}K(-YvpeGQA4UQ@xes6W6urjU{DH};dhCyo~Z+`4~Ddva!+`E)@I?V9gl@4L&j3O zbf*IA=%L{G^&TK-AVZ5!r9fBHp|E~g5U_YxOxxC$VN*U9VquR80k*I|oV}n6wq=kl zSbBOcbQ_RH@7zLyTzz>uF8?y!+CCUY*XhHd4rl3{HIWz?DKFfh&;^=!&w-AwmtaS` zFNaa1`EZCtgErYY7Fu z=D2VWd30bVpH4asBK;=eQ*+PcH^m66%J%6ZR)2Uah| zB>hfeZoRy1ol{S68w46H&y{T>IfgsBOjy;_|Y{@9UU?bS!zk5m@Dd69{q zh#5z1Ho71P;@k2bO46{JFniHn&r%yax1Wu!s1tqvtc74oMKK&ZY7uVx@C}0x@Q|z| zhtR=e)^a_itI#^uNxV8bPq@kG5U}X#AXwDf0~BV)l;57X(e`m-Jaj44#S*6J@pd;Z zU?z?03*_c5lw7#TsZi_F+lDt5h^cw62ap;D(t;Ac!7#QXZ~92R+MJkR^e_F;z0 zR`V2A>q=@D4dxfF_hF1b+!1&24YoB7ud67VwTJ74L5#oQ9IxVIbj6NMZOo<4tN1UU zSc+b#>j2PFIaT3+Bqp0S#OlHMlH^J&z5yEmUQQ3|~UZP`FUHEgi zqd7->G&}};X~#Yd_-wq@CQWq_&q;9zbv9I9S~0JYY?=}!CE{KKy{3(P$EZib%3NdY zkWZV)WwjePeB=yv`{F)n{q}TlM3hUOS4+h5hs8=JKX`9*f4LRD;C4L^AO|+^v^mN?$sXTigsZ7+U<1dRRIMizf+JhktZc<`SHx~k{UqI>VUnp^0G+58_qVR^gt-<8V&}jOSUL%-nS;Caz@I<0j4+UDx9wl{f4-Epu)s zi0V`eAIp`{qSNxQ*UB~+H?p3Ai~126R+lBMfzL$NN~^e$nPIT(qcOAS>0#z%-@dr! zscFQTeFoTumIt6)q7BxTWG}sO^_=wlf_S(z#)TL^pn^&=Y!Y7WvROFuxQ0+tJCC{% zQHFcST!sdDBH)@gN!o6=QCKZ=ow=hGLbsNr6MZeM1xAAg@@IM05t6rxQkYhZNAB?_ zT>Pgn{Gn>nd0pKkxwN^UzNUncyS`59R48UPwImV~-P_5BCsyOUsy&kM^Z@CzPU+G+ zHv0u3FN~#ceH?hsij&~&u^*Uo{YOcRt(>G_m@PhIN)~8&=8Q!yI4`;F7YL6}JpmR6 zbQaJ0Bp^1#f5P4Sa+q$Wqj5m)V&cua!-beU^TEzbu>Ij?DX*V8VcUH?JrwTXY4>tr z#vP1gk}C!fk6x)t_Agi|*p&H@(3NZ;?j4Zg=^JH;F1MzMXAjSj&Um$%zhd-KLhRy? zTdzDOr7osmO=YWz=}oHATeFAr2K3dntDItsqa_UK)T?Wy&B-HR^&l;BC~veR)9)}N zH1WZLhMO=KHPa+-B^J`N(PMF^*jVNoF-uUo(p-8_QY&mo+r*#FyNr=O!=!U#cH%}o z-q1sBZNzu(L&=czErLE>!=*RH<`SdD`|x|w{phvgI#|$aA7kO9E1ojqwupPgpYJi? z6k*0`{ymL(fxhra%G=07?otG3#^e(p`7J$8sZpjpIbk!cB_mj(H<>e*B z1RGB@&pJZOr9PJ)b-ynSIxs~#yJ@Fw!He~n|Ng^xR%yNP$m;gwHK>mipBav+xMhN)DYxLZ4S|f>lxFgg;12z~>Lzh4qC|YRH5`t0@W3+{sY`9T zPQ`*kL_FL2#rX5FO1R8haJVGjgSalUmZ-VcPkLH$GOgOxU66He5OMeN^urOQ@1;3m zT9T)gE_ko#Rm>x_cUo~UllTBisU{=HR2{m32U{GiSZ(UTL_OY#r+5Q59O(V)ORiwKRX!a(W5da$Ow4(zQ(GI1>7;-^qZ`HV%sbi0 zu6-qXeJw%U^JX(0VVT9>(aD6KuPDVU0{RN+5uUaSO(Si)jZYC3x=a!nW~;*d&WhNc zv?wTFnZ?^G%;MF-bE1=e`)ozq)v@l!<1zF71(H4=cJNP?lwb+DvK8a5Y_xsx>Xb+` zG=-Y?*ppnGBad&mB9LY&&~VZE&D?t39HC@fm8fU_bYg35FcUX!0%NgwACtd4M5w!I zHJm88UvYagpZh6l8+?6i8L&vUB%hr>4Oi$X!Ep=LO4bxPiw3+msHm&%PkKKX!{4=W z1a+^pkshLaRBS!`c7=hGr`T`d)?rVUT@_?lRFe}pIsBc#nVgR)lMi2+*}5tmwN0H_ zRWVp-fIYgv@Hdhd%MC{MWG00@5i43}SH$+$;^T)JBx`w&gfW!zs>5$E?+rs5o`8mMBZfVc4rK zIo2ijqRL0Nhy+DL@}S$!&P@CBY`#_sUfw_Uk@!r@FwqSEiLfU4fmm(GBxIK01q29p)GpE8%9NNLr2gRv%!f;IjLQ1ogIU1XXfwO+nlSf1HTa;yddGp?#c z_q@G$afuK(z10!N#6P9|KXU0MVtc+*`4-96Pv#YBIWH?NIMNV*=1Q$>9>^4onkal= zq%3s$IH&xObt-r9y#iZfv_g2YvQS{Fsm*jXQo%3tuSkrpFXeXI$g60J=t+k4>?^%6 zy_8&+Xpa}gHPL!~>OhJ0X_0uBF_xux$F?YWzV!XH3G|M-=h&m|@2E}oA93qqLf|_3 z3UDsjC#=!;W8S4L$KUTUmnM!mOV7JIkI1PxBfOCeY3Xhk-libHY+VP0`wbT2W6Fln z-s?sSeKuVbUTY||?W`BTo!mWF(5+&daNNiV{NB5=0PMV7aQ%6Zp$v1XfeB%hQaG_0a7}Wll`&R2YS1H+tTirTGx}sAmGdDU78@vnSQv0reNaHJH zSNBNJ<%u!gZ|FIAsMnwd zO;zC6m8~X4_e3z-unFJrL|aPkTZnJpz8PQeZn!Y7P99e86Ji=$L#1&?n{mO@gY-k~ zLtsTsscsn&(vh%I-zVs1kV;6asY5VXB36;j?G4>ftgTa~d;G_MnAy+_9VF2CrtPJz$j6dC@Y#Bf2cKVS6RhV+tx#RfBRE>$GH}~ zDNi1sA2|>^b?P~}l;8_o2~DXD{z*`nHjCDNZh$oc;w8dY;qJf^-~^dTfyP+Q59UaczOrS!grk6lv_#-}w%K5&eskC*qs8b(jTu{ZMg zx)Dp^`9rJeJ%*RyE!2KZdAXMPw0AvLDvig?y0p-JJN00$)0SZS-862LY9#G#dj~en zIw>TEDiO8ASL4H7W?^OO;gV$C06gXOP)3y~VX7`vatEy42ehZI2K*(-%;-zB;?=1DUx}#d}fUoHm9pqQ*!&uhf=4Hmw&n;rZC(7@8h! zxq!^NGM#wda|KvFdMuc*d@ofy^8{~ASs*Rx77my(y7;XjGR*Eg98P?a0Nt(k!nn@P zg7|PLD4DNF+-r$rj;*tlsE22gw??=DjXU}9M(I5w>hKYX{rOF}#V!{-;#$7g^JFgU zJ8KS+=Q$I{(yNH#3JW~_-4HNJvyi$wXP`81X`-<6O>M#2Ay@Gk`}a!jg$={@E2nU07*veF|j2GIgl@d16B;b*?8}H5=Dh((- zidW9chrMD3g7I$4X#;_*G$_)DK07xWXD(~dp2zl*pS+y$^=gU4+8PCH%h+PcRh?I$ zXyz-dN9AUG^aWLXAXUUvdM?Km(mOL%$?44cPBF}}05^Pi%LUlSswZ(SycLdU?~3n# zbPQPam@A%HCxUJQ8H{tb1cG)NyL)*W)>C>7S3Z_VZ$5NSAj&l)(AjkbxgbJw|_j9n@=KNiBmDY5j0i3NfjrOl*k zO$oiHzNhWTPMa#0$T}Vt^L4zcGpJ=yyD&;+^ikluhS!znn z!aZXh@Bq3LKRSFVHN#K}HgcM6@0SWiolkBBX>xPmM+;}ZdHxA-Z&4Q@wOuEev)qAO z65P|)cf|nyde711+rSY**?p)!%tqt!<+p7!2fOnI?cQD%z4xl*xXc+oqmxSdhv^H~ z*C_ClN(NV~7*kbob}4Q9iPWJTY!8z|XS}g-4=};~a%K4o-8R?`mv$o`98eebi1C$X zMqCz$9@ZzqD}9Iq4o3XL3vwz3_n9eN1`y z%g2M_v5h>9O@z;r_2uIYOV9ai=CSF{Q1qaP!flH_L*vIN7e*B#if?Wp^>C+2kgj(~H z0WawmHc~N{Z!pD<*Rp8xF!{Ic5caKAWlmK-v(yj zwz0aR1d}tounl)44)5;a4hPjSjY~4P!V?etar;5x=F-oH30BArwiO(> zX`2^!9SrK^CbXOa@ORnnl;_kgRC>x38@C;)qN%)siska}s3+1rl6@a_@LdO0nZOgR zf`AcTqUXCu*hbvz%^#3wO~_`7_%)dhOk<}{7^k{{Zp=KxT~IfMR_*(sLM~(y@o1Dg zR<-Oo?Q$*&qr1OnT(6Iiob34uS}rZ3R;PL66B~75KuvE#Uh^0hj>d7N9;Q#p zmlQ1c$RC^~Ay3Psh(9Q}6Q1gC_+}2~Ov)67iqkOy(R-eWifiA9oG(oj9k-fJh&M%o zrIXU=y6h1muhB2yBirlzv*S7NSbYstke&nC?FQ24jj^Egq6~c=my;ezQperH9#I3F zCQ}9N`|*Y&#o&dl;x=6#XNl3$xgy}vAs}TGXpHYqeP!7c|yytE`wD) zRfYWoF5tHHYUoUF#$)9fN_6{!C^3X;C_5FtKV6M^ z)X3sG?Q^l*z5~JjwBcmtrJ00s0*1c^wt`**r}0B zDShbed;J*Ck?PC|)i`iNJ_(yOW*E*5J}9{DzaD>cD3>03Ifg#rx(Z;;dbsZ1vvBW; zbkUXimDq=qH^E+oOH}O}c_}w6g&FTOo8EmdTrf5^oH?601Yde_KYo7bO_AQtPBk zhSXEd<@xm4H7_x(tcCoj^$NtYA+9jy$~^pF%}(JB_Y-1+0)X|~u>uONzJ|NUOk;+i zZ&B`^BV7cgq)LH$Qp!6!m zL~{qihf%wD`Tg@jUtdKA-n78)kA~8dggpZ;4}~(*mIx!fZo%O*6R8K*nv#b@FEIlf z3kBCd+R(*8bMXTLjImj@7RMtvM7!o{X_3}*-cdmyzOh+fFoSBr-Lev?UMqbFfx`}Z zM7bq?^r|}X()$?GeerH^z@Q&|d^Zw%m9heEJKG0KjZ$Tv+|U;WIdDj7@9h`t_kIs=AAd`)FiT>#^j2oZ_SlcFJw2bPcT&gk`q_AGi8WsAp#@VymccbO z+EQ`oQxIRS$~=YD(6l~~_t=L5YV&328(cBa2w>>$!))o@@%hr3&k`{xdsU+P>dRgj zdv4}xe#PO_yVoCfyRyY@;Ma6^KW&%rK1Nj)Q1kby;=U0Su6gGk<`SAk^<5qf4I|EC zu3u>hM4PdHE*J{{*!yWb&CBeH_FMZwx&48RUU8wsVMiGAA$}T`ksizNKE0tIDOfU- z){SIRFD-#KHR!AYZyVhF9j|D`*R)Pr1XH=&1^TQ!k7Z8rgIcyd;fcqiK+=&ay8oLl z%!=-dpoZ#3Y{I2xtk0~$jCSZw&|U7j@Y>5c%){?^t=unwiykGy`;jk!*kKi;TXh86 zrz`>1@!e_rC4E3`Q79(j9Kf^6!@-n`WpI!bNK_kg;oBR#;5k78W%K&m{$f#TDTrHd z&AgU+3T124Y4hbWjE9l}cCKLz-0EIN`F*9Gj?PLw_f{Zvr|9tq3~A$)T@4Z!4ecua zRAa_}yt!|Dc9$Xt1%_5V#CE{b3TamnjVS$_7sRiFAO1b zW8BC^j|Y(n&zfxw@`}Z|{oDEHEC)$m@1INNj7;G_lV2qsJ$o4WaKwX(jj^hHMjz*; zP8h+ze5_b}a#9Ij_uNK)(Md0IT!}y&GpUFl)Ki1p+<%-nb+0ae>NscKF-voP^-5Ji zw^2vPt91$djhn3b*=_5|xsR-PDFduY-0+NOM%ip}+&zY$y4gVN>G@pTYWIOJ&U`9f z|Kh#4+&hGS{#dbqtjQWOeZ3XA zV)HS+g=-plC{d5hU2v2ips|je-Q>nwcWFIOd;VDQHZ?V#OV6{sK`~v(+_DZoVtZC$G74xoiSSMUTZCC zs%jBu23M0lVd;FMpqJvA_3dP~;0k{w$QCbr_lRd*+m$DXZxdzb8IV!NDF?0L8g`dxVayY7goeE{o`k`Zvf1L}B zMF;&75t0$uvrzMW=LDhK=X0RW>>)seQUwlY6@oKD8zL$~2t%+(=!Q^@;ye+-4Z#rM zG5QW@H-bNcIYJv!qzWMxfk03}xP(|2AVeT^oB>_-z8!tW_W4p`&l7?AD6Ec~z5Ph4%(zA3dJvwU2>4gBk(Q9?kvpR46O3%`<^wtRa2tnWI z|EB*=l-{8~OUuf#LfHI`UWUzYh`_!()AmIl_8DC=9rUbh_S`Kx-ul_b8{hKFcH~FJ z$~pF3el{-)HXoaxJ?^QFpp0OE=vY6a>u>&8g}y&w^RTq6edY-0^Ae8BH~PQ%qvR_+ zOUKfiA?P8j{6_ybf0U#24u7z;tSnQ6ec$N+=8qd6fA$AU&)PE-fh`->?v`))|K^YS z@A7x}gU!e0AA-Qf8|x2KME5Iyth@3>K1<8mHy8mOJLXLLM*lZ|NWapvbS%9I0$Z2Y zexv`JKTe?Z4u7z;td7PA@EiT#{L%91XMeEttUX2uY@5K=>rdbE|IHtFzsuj@4>lj0 ze-J`f1lAt_(f!IFTd#hR&(gB?4MadkuQ@^A=>O)A+OPC19ZNp|p$Ed|Z}flj$3>Lh z;SZLU)v-Uq8I*2_?&y+X^<~S0efOmuQ2Tr)nmzm6VH5ke|loW6_b9wb9_~?=P~teD+<(`En)nuEReabSUX_ezt9A`LGus zA)*gDOK*t!38a)iLMzG;fuMvCiyGPpq)0b}WYoxLBIKgRLkD3i3Nn3!0;H@V!fsT_ zoDq(p%HxJ`7dbNoK^`Fk8P*@66gBkr2=%CubVJZVja>%YKp+I7K(9qkh(HzmEmGbe zp%ztiYlK^3k6gga!M<5Mj*l^WS~7lJz_l_fsHrI zmiBgZlVN?#`j!Q2zcm8uL*(Xm)~~m2w6`O(I1@vBLjqHc_`f(p@T1h9=JE#+u3%026t$%h0Ql`hnkyOTFwtP3k>j`7w)q- zz$b8t50`6c=FI+f4G3KFt?XGo?|&hWi};TGLEdg8Z^%E%n;GaE5)u&PW5Kg9a}5Zd z?Yl4lk$={iwT+cE^XWIAu`$kq?c4nkSf8>!L&fyDjIM;Vfov<_}NeFIP!P;JpNzl^NaXl^*Qkieaun%ui}TLkGT6s@nil^@x$_S zeEG9K*fDQ^RKeKt{IPu6kbGJ6?oaBC<^L#tzSXuriX=<3|0)pw(*CDF{8fF=MC)M4 zZJ+zt{;VGn*tx*(;?Vj2pU2_P27U7kTbF;Omnm8&L$Cg(7dyxB|4RR3Ip0Nk{fmCg z{6CFD_zQi8{DVFr5B^8`{3Z@v-u&zzc7F7`IFxVtlQ^{H{aS#Fe;bDu|6LsZrTu>thX(I{E>C0R&)>yiee<8k;m-#B-*H&}3%!i~q232R{5SO; z)wln!KXiQmv!B>`^Y7Y)w*Qqrzlp<(ztD%Bm;WvflOO$29CH7!IP^ekR!H36+Q|VZ z;dgPU^7zl=(ER^x9Gd@karl?^|4|(7L~C9@`jhPwe-(%9{K@;tpU2_P2K{p!vT^`g zd-_qYekkGlde5#ws35TPFOgn<-XGd>|0xc?=YQrG`t36tVjKxbSatdt2LI#o_n-Zu~;NA>#iMK^}n}uY6CRf#jb< z>AxM9ex1!({{JwW`%VEZB9o@hlCRqyKqbMe9F5_F>2wtV}i@gMN_3?uW3la*-@MbpNeA z|LOV*;?W`N$`7*Gbu(6$5}Moo3%h=vtA4eM^;yS!jD2Ro?rX5JLXfO~Vb{;H5HHp) zmiNK0bH5H*Xi4+4tXu!iuAj&L-`QpGbzamVi(Ox2?Q%xG{TFuqEDQ1Kuq){YS!`Wr zWr>iiKQFIejHSM_>-7(^*nI|8R)6H%Key`_vJkHhyV!3X*q;_IXbJnXthRq=*Uz$8LlD?l?r6)jKeuFGQrI0=);@H7Zp$>$0}FJ?a?wAr z$nWg@=(lnH{n{>Sg@Z%B$dg5R3ct>^I{b*1<3F!eUqU?bf8zP$I>P6*!5?rQ$p2dAw=o`K^-G)6evr@Zqp~(jk^Dck`RBRb zuWeTRI)CY~ncdT3ZMH`K|5KZPmid3%9RGuScCVDRxfIF&Q=5MtSO07?ThHJBAd?+4 zvo;$c|Np7YRzFQn{%&(eJh9(0AQ6AHh3tMXD>L_B$o$#Pzsu|x>vY5uYjevF^4Yys z*3bIL7k}#KpKbmUN30x=uk)vl`h>OsId>6ak(`U@{@b|y^*G1M>~lK1{bV+ONuZC# z?}w)UHv?aP|9@ie*Pm)P8|xb~c1hsxMSY|EItc!<2gmxG-GlS`IydU@0qYlkghW&Z zZvW(iZ+!oS24Bj6)#1ty^3m4o=XsD4DjWNMlK+dg^RK<6pJvbB%6#@pa1{> literal 0 HcmV?d00001 diff --git a/Config/tflite/robot/robot_classifier_rec_0.824_thr_0.828.tflite b/Config/tflite/robot/robot_classifier_rec_0.824_thr_0.828.tflite new file mode 100644 index 0000000000000000000000000000000000000000..b7fc4d062664cc2666360d3d146a45e73313d867 GIT binary patch literal 23712 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 literal 0 HcmV?d00001 diff --git a/Config/tfliteInterpreterProvider.cfg b/Config/tfliteInterpreterProvider.cfg index 2e275496..e8008397 100644 --- a/Config/tfliteInterpreterProvider.cfg +++ b/Config/tfliteInterpreterProvider.cfg @@ -1 +1,6 @@ -ballCNN = ball_position_v10_bs4096_19933.tflite; +inferenceMode = off; +ballCNN = tflite/ball/ball_position_v23_bs256-4984.tflite; +splittedBallCNN = [ + tflite/ball/ball_position_v23_early_exit_v2_bs256-5138_early_exit.tflite, + tflite/ball/ball_position_v23_early_exit_v2_bs256-5138_remaining.tflite +]; diff --git a/Config/timeProvider.cfg b/Config/timeProvider.cfg index aa0306e0..279d66b8 100644 --- a/Config/timeProvider.cfg +++ b/Config/timeProvider.cfg @@ -1 +1 @@ -requestRttThreshold = 100; +requestRttThreshold = 500; diff --git a/Config/usConfiguration.cfg b/Config/usConfiguration.cfg deleted file mode 100644 index 7cfb0aae..00000000 --- a/Config/usConfiguration.cfg +++ /dev/null @@ -1,22 +0,0 @@ -min = 230; -max = 2550; -leftToLeft = { - rotation = 22.5deg; - translation = {x = 50.7; y = 37.85;}; - openingAngle = 90deg; -}; -rightToRight = { - rotation = -22.5deg; - translation = {x = 50.7; y = -37.85;}; - openingAngle = 90deg; -}; -leftToRight = { - rotation = -2.5deg; - translation = {x = 50.7; y = -3.75;}; - openingAngle = 90deg; -}; -rightToLeft = { - rotation = 2.5deg; - translation = {x = 50.7; y = 3.75;}; - openingAngle = 90deg; -}; \ No newline at end of file diff --git a/Config/usControl.cfg b/Config/usControl.cfg deleted file mode 100644 index b540a859..00000000 --- a/Config/usControl.cfg +++ /dev/null @@ -1,6 +0,0 @@ -enable = false; -sendInterval = 100; -switchInterval = 100; -timeBetweenSendAndReceive = 65; -modes = [4, 5]; -stopOnPlayDead = true; diff --git a/Config/walkParamsProvider.cfg b/Config/walkParamsProvider.cfg index 7d92f5c7..eb8ad04a 100644 --- a/Config/walkParamsProvider.cfg +++ b/Config/walkParamsProvider.cfg @@ -1,13 +1,13 @@ -minXForward = 80.0; -maxXForward = 250.0; -minXForwardOmni = 70.0; -maxXForwardOmni = 230.0; -minXForwardArmContact = 60.0; -maxXForwardArmContact = 230.0; -minXBackward = 80.0; -maxXBackward = 150.0; -minY = 60.0; -maxY = 200.0; -minYArmContact = 50.0; -maxYArmContact = 175.0; +minXForward = 130.0; +maxXForward = 300.0; +minXForwardOmni = 120.0; +maxXForwardOmni = 280.0; +minXForwardArmContact = 120.0; +maxXForwardArmContact = 280.0; +minXBackward = 100.0; +maxXBackward = 200.0; +minY = 130.0; +maxY = 250.0; +minYArmContact = 120.0; +maxYArmContact = 220.0; speakOutChanges = false; diff --git a/Config/whistleDetector.cfg b/Config/whistleDetector.cfg index 8033e3ef..39ac6b88 100644 --- a/Config/whistleDetector.cfg +++ b/Config/whistleDetector.cfg @@ -1,13 +1,25 @@ -windowSize = 1024; -threshold = 0.55; +whistleNetPath = "/Config/WhistleNetMk16.tflite"; +micBrokenThreshold = 10; -minFreq = 2300; -maxFreq = 3000; +limit = 0.25; + +threshold = 0.45; +useAdaptiveThreshold = true; +adaptiveWindowSize = 25; +nnWeight = 0.8; +pmWeight = 0.6; +limitWeight = 0.40; +useWeightedPMConfidence = true; + +minFreq = 2500; +maxFreq = 3700; +freqCalibration = true; release = 3; -attack = 1; -attackTimeout = 2000; +attack = 2; +attackTimeout = 200; useHannWindowing = false; useNuttallWindowing = false; +eval = false; diff --git a/Config/yoloRobotDetector.cfg b/Config/yoloRobotDetector.cfg index 09fd83b8..dc0ca3f5 100644 --- a/Config/yoloRobotDetector.cfg +++ b/Config/yoloRobotDetector.cfg @@ -1,30 +1,14 @@ -lowerRobotThreshold = 0.65; +lowerRobotThreshold = 0.1; lowerBallThreshold = 0.0005; -lowerPenaltyCrossThreshold = 0.6; -lowerNMSThreshold = 0.1; -useLowerYoloHeight = false; - -upperRobotThreshold = 0.15; +lowerPenaltyCrossThreshold = 0.35; +lowerNMSThreshold = 1.0; +useLowerYoloHeight = true; +xOffset = 20; +useXOffset = true; +upperRobotThreshold = 0.01; upperBallThreshold = 0.0005; -upperPenaltyCrossThreshold = 1.0; -upperNMSThreshold = 0.1; -useUpperYoloHeight = false; - -robotClassifierThreshold = 0.7; -robotClassifierMargin = 2; -robotClassifierProject = true; - +upperPenaltyCrossThreshold = 1; +upperNMSThreshold = 1.0; +useUpperYoloHeight = true; useTFlite = true; - -upperFractionOfEstimate = 2.5; -gridSampleSize = 15; -maxColorDistance = 350.0; -minColorConfidence = 0.1; -greySaturationMax = 0.25; -blackLightnessMax = 0.25; -whiteLightnessMin = 0.75; -minAssignedPixelsPercentage = 0.05; -randRadiusPercentage = 0.05; -showAmplifiedColor = true; -showAssignmentInfo = false; -acceptBlackOpponent = false; +skipRobotNMS = true; diff --git a/Install/addRobot.sh b/Install/addRobot.sh old mode 100644 new mode 100755 index 82a731b2..e80b68bc --- a/Install/addRobot.sh +++ b/Install/addRobot.sh @@ -23,6 +23,7 @@ keyFile=/tmp/id_rsa_nao sshoptions="-i $keyFile -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=quiet -o ConnectTimeout=5 -o ControlMaster=auto -o ControlPath=~/.ssh/copyfiles-%r@%h:%p -o ControlPersist=60" cd "${basePath}" cp $keySource $keyFile +chmod 600 $keyFile if ssh $sshoptions root@$IP_ADDRESS '[ "$(hostname)" != "Nao" ]'; then echo "Already configured!"; diff --git a/License.txt b/License.txt index 2bcb2979..676c222e 100644 --- a/License.txt +++ b/License.txt @@ -1,635 +1,89 @@ Copyright (c) 2023 Nao Devils. All rights reserved. ************************************************************************* - For each Nao Devils code release from which parts are used in a - RoboCup competition, the usage must be announced in the SPL - mailing list (currently robocup-nao@lists.robocup.org) one month - before the first competition in which you are using it. The - announcement must name which parts of this code are used. - When using the full code release, it must also contain a description - of own contributions. + Preamble: Nao Devils releases most of the software it uses at RoboCup + competitions to allow teams participating in the Standard Platform + League that do not have the resources to develop a complete robot + soccer system on their own, but still have important contributions + to make to the goals of RoboCup. We intend to enable such teams to + benchmark their own scientific approaches in RoboCup competitions. + We also hope that the scientific community will benefit from their + work through the publication of their findings. + A second reason for Nao Devils releasing its code is that source code + is the most solid documentation of how problems were actually + solved. ************************************************************************* - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 +Parts of this distribution were not developed by Nao Devils. +This license doesn't apply to these parts, the rights of the +copyright owners remain. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. 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. + + 3. The end-user documentation included with the redistribution, if + any, must include the following acknowledgment: + "This product includes software developed by Nao Devils + (https://naodevils.de)." + Alternately, this acknowledgment may appear in the software + itself, if and wherever such third-party acknowledgments + normally appear. + + 4. To use parts of this Nao Devils code release in a RoboCup + competition, participating teams must fulfill the following + conditions (if applicable): + + - The usage of this version of the code release must be + announced on the SPL mailing list (currently + robocup-nao@lists.robocup.org) at least one month before + the first competition in which it is used. The announcement + shall name which parts of this code are used. + + If the code usage is not limited to single components, such + as the ball detection or the self-localization, but consists + of major parts of the Nao Devils framework, we consider the team + to use "derived software" and the following additional + conditions apply: + + - The announcement on the SPL mailing list shall also contain + a description of the own contribution that addresses the + criteria set by the SPL rule book (in the 2023 SPL rules + described in section A.1 of + https://spl.robocup.org/wp-content/uploads/SPL-Rules-2023.pdf). + + - For each year, the derived software as used in the last + RoboCup competition game in that year, including the + contributions previously announced, must be publicly + released as source code. The release must be licensed such + that other RoboCup teams can use it, although it may place + conditions on the use (such as this license). The release + must be published and announced on the SPL mailing list by + the end of that year. + + +THIS SOFTWARE IS PROVIDED BY NAO DEVILS ``AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +NAO DEVILS NOR ITS MEMBERS 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. - 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. ************************************************************************* diff --git a/Make/CMake/conan.cmake b/Make/CMake/conan.cmake index f9d59c10..bc553480 100644 --- a/Make/CMake/conan.cmake +++ b/Make/CMake/conan.cmake @@ -13,13 +13,16 @@ set(CONAN_CMAKE_SILENT_OUTPUT ON) # Prefer Conan packages over system packages set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) +if(BUILD_ROBOT) + set(CMAKE_BUILD_RPATH "$ORIGIN/lib") +endif() + if(CONAN_INSTALL) if(BUILD_ROBOT) list(APPEND CONAN_IMPORTS "lib, *.so* -> ./lib" ) - set(CMAKE_BUILD_RPATH "$ORIGIN/lib") else() list(APPEND CONAN_REQUIRES @@ -53,7 +56,7 @@ if(CONAN_INSTALL) snappy/1.1.9 nlohmann_json/3.11.2 libjpeg-turbo/2.1.4 - taskflow/3.5.0 + taskflow/3.6.0 protobuf/3.21.4 eigen/3.4.0 kissfft/131.1.0 diff --git a/Make/CMake/warnings.cmake b/Make/CMake/warnings.cmake index c9d50355..9e8ad394 100644 --- a/Make/CMake/warnings.cmake +++ b/Make/CMake/warnings.cmake @@ -20,6 +20,8 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") add_compile_options( - /W3 # warnings level 3 + /W4 # warnings level 4 + /wd4100 # unreferenced formal parameter + /wd4458 # declaration of 'x' hides class member ) endif() diff --git a/Make/Common/copyfiles b/Make/Common/copyfiles index 9852cb44..83fe4bd7 100755 --- a/Make/Common/copyfiles +++ b/Make/Common/copyfiles @@ -38,7 +38,7 @@ copy() REMOTE=$1 PLAYER=$2 - if [ ! -z $PLAYER ] && ( (( $PLAYER < 1 )) || (( $PLAYER > 6 )) ); then + if [ ! -z $PLAYER ] && (( $PLAYER < 1 )); then echo "Error: Player number is $PLAYER!" exit 1 fi diff --git a/README.md b/README.md index f2d7cb4c..932d9a4a 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,16 @@ -# Nao Devils Code Release 2022 +# Nao Devils Code Release 2023 -This is the Nao Devils Code Release 2022, originally based on [B-Human Code Release 2015](https://github.com/bhuman/BHumanCodeRelease/tree/coderelease2015). +This is the Nao Devils Code Release 2023, originally based on [B-Human Code Release 2015](https://github.com/bhuman/BHumanCodeRelease/tree/coderelease2015). ## Installation [Windows 10/11](#windows-1011), Linux (e.g., [Ubuntu 22.04](#ubuntu-2204)) and [macOS](#mac) (experimental) are supported natively. Build dependencies: -* Microsoft Visual C++ >= 14.34 (Visual Studio 2022 17.4) +* Microsoft Visual C++ >= 14.37 (Visual Studio 2022 17.7) * Clang >= 14 * CMake >= 3.20 -* [Conan](https://conan.io/) >= 1.52 +* [Conan](https://conan.io/) >= 1.58 **< 2.0** ### Windows 10/11 @@ -29,7 +29,7 @@ You can choose between * Install [Python](https://www.python.org/downloads/) and **make sure to check "Add Python to PATH" during setup**. -* Install [Conan](https://conan.io/downloads.html) package manager using `pip install conan` on the command line. +* Install [Conan](https://conan.io/downloads.html) package manager using `pip install "conan==1.*"` (**version 2 is not supported yet!**) on the command line. * Install Microsoft Visual Studio: * Download [Visual Studio Community 2022 from Microsoft](https://visualstudio.microsoft.com/de/vs/). @@ -47,32 +47,57 @@ You can choose between * Complete the basic installation steps above. * Install Windows Subsystem for Linux (**WSL 1**) running Ubuntu 22.04: - * The exact steps vary depending on your system version and its previous state. - * Open a command prompt and execute: + + * First, enable the required Windows feature if you haven't done that already. Therefore, **open a command prompt as administrator** and run: ``` - wsl --set-default-version 1 - wsl --install -d Ubuntu-22.04 + wsl --install --no-distribution ``` - * **Notes**: - * For the first installation, it may not be possible to set the default WSL version to 1 beforehand. In this case, execute the install command first to enable the Windows feature and try it again afterwards. - * The first WSL installation may require a system reboot. After rebooting, execute the install command again to continue the installation. - * WSL1 and WSL2 both work, however WSL1 is highly recommended due to frequent Windows file system acccess. - * You can check the currently used WSL version using `wsl --list --verbose`. - * You can also convert an existing distribution from WSL 2 to WSL 1 afterwards using `wsl --set-version Ubuntu-22.04 1`. -* Open Ubuntu bash if not opened automatically (type `bash` in command line). -* Update system and install packages: + (If you just get an overview about the available options, then the feature is probably already enabled.) + + You have to reboot your system afterwards to finish the installation. + + * Afterwards, you can either download the Ubuntu system and install the dependencies manually (not recommended) or import our pre-generated WSL archive (**recommended**): + + *