##### tags:`Limelight Code` FRC 程式 === --- ## Limelight 1.在2024的比賽中有16個Apriltag,顯然地不可能每個按鈕都單獨拉一個AprilTag寫,按鈕不可能這麼多,實際上就算真的寫了16個按鈕Driver也記不起來,所以我們要想一個簡略的判斷方式以方便我們維護程式跟除錯。 2.因為這季沒有要用trap所以stage的Apriltag是可以不用對的,也就是說我們可以只需要對Amp,Source,Speaker就好,那這樣一個簡易的想法就出來了: **我們的判斷式主要有兩層,第一層式判斷紅藍方,第二層是判斷目標名稱**,那麼一個簡易的判斷想法就出來了 ```java if(getTagID() == 7 || getTagID() == 4){ TagName = "Speaker"; } else if(getTagID() == 5 || getTagID() == 6){ TagName = "Amp"; } else if(getTagID() == 1 || getTagID() == 2 || getTagID() == 9 || getTagID() == 10){ TagName = "LoadingStation"; } if(getTagID() == 3 || getTagID() == 4 || getTagID() == 5 || getTagID() == 9 || getTagID() == 10){ TagAlliance = "Red"; } else if(getTagID() == 1 || getTagID() == 2 || getTagID() == 6 || getTagID() == 7 || getTagID() == 8){ TagAlliance = "Blue"; } ``` 但要是這樣就直接丟給```Comand```的話整個判斷就太醜了,因此我們需要一個```Struct```來存取同樣是字串的紅藍方跟目標名稱,但問題來了: `JAVA`沒有```Struct```,所以我們建造一個名為```Student```的類別來解決這個問題。 ```java package frc.robot; import java.util.HashMap; public class Student { private HashMap<String, String> data; public Student(String tagAlliance, String tagName) { data = new HashMap<>(); data.put("TagAlliance", tagAlliance); data.put("TagName", tagName); } // Getter for TagAlliance public String getTagAlliance() { return data.get("TagAlliance"); } // Setter for TagAlliance public void setTagAlliance(String tagAlliance) { data.put("TagAlliance", tagAlliance); } // Getter for TagName public String getTagName() { return data.get("TagName"); } // Setter for TagName public void setTagName(String tagName) { data.put("TagName", tagName); } } ``` 有了這個`Student`我們再使用集合物件優化程式碼就可以用一個新`Function` 如下: ```java public Student getTag() { if (getTagID() <= 0 || getTagID() > 10) {//避免NullPointerException return null; } HashMap<Integer, Student> tagMap = new HashMap<>(); tagMap.put(3, new Student("Red", "Speaker")); tagMap.put(4, new Student("Red", "Speaker")); tagMap.put(5, new Student("Red", "Amp")); tagMap.put(9, new Student("Red", "Source")); tagMap.put(10, new Student("Red", "Source")); tagMap.put(1, new Student("Blue", "Source")); tagMap.put(2, new Student("Blue", "Source")); tagMap.put(6, new Student("Blue", "Amp")); tagMap.put(7, new Student("Blue", "Speaker")); tagMap.put(8, new Student("Blue", "Speaker")); return tagMap.getOrDefault(getTagID(), new Student("", "")); } ``` 這樣的話就可以直接在Command裡面使用了,具體如下: ```java package frc.robot.Apriltag; import edu.wpi.first.wpilibj.DriverStation; import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import edu.wpi.first.wpilibj2.command.Command; import frc.robot.subsystems.SwerveSubsystem; public class LimelightCommand extends Command{ private final SwerveSubsystem m_drive; private final LimelightSubsystem limelight; private final double Target = 0.8; private final double Zkp = 0.65; private final double Xkp = 0.3; private final double Yawkp = 0.5; private final String Name; public LimelightCommand(SwerveSubsystem m_drive,LimelightSubsystem limelight,String Name){ this.limelight = limelight; this.m_drive = m_drive; this.Name = limelight.getTag().getTagName(); addRequirements(m_drive); } @Override public void initialize(){ limelight.setLEDMode("On"); limelight.setCameraMode(0); } @Override public void execute(){ if (limelight != null && limelight.getTv() != 0 && DriverStation.getAlliance() != null && limelight.getTag() != null && !DriverStation.getAlliance().toString().isEmpty() && !limelight.getTag().getTagAlliance().isEmpty()) { String alliance = DriverStation.getAlliance().toString(); String tagAlliance = limelight.getTag().getTagAlliance(); if (alliance.equals(tagAlliance)) { switch (Name) { case "Speaker": m_drive.drive((Target + limelight.getTargetSpaceRobotPose()[2])*Zkp, limelight.getTargetSpaceRobotPose()[0]*Xkp, limelight.getTargetSpaceRobotPose()[5]*Yawkp, true, true); SmartDashboard.putString("goal", "Speaker"); break; case "Amp": m_drive.drive((Target + limelight.getTargetSpaceRobotPose()[2])*Zkp, limelight.getTargetSpaceRobotPose()[0]*Xkp, limelight.getTargetSpaceRobotPose()[5]*Yawkp, true, true); SmartDashboard.putString("goal", "Amp"); break; case "Source": m_drive.drive((Target + limelight.getTargetSpaceRobotPose()[2])*Zkp, limelight.getTargetSpaceRobotPose()[0]*Xkp, limelight.getTargetSpaceRobotPose()[5]*Yawkp, true, true); SmartDashboard.putString("goal", "Source"); break; default: break; } } else { SmartDashboard.putString("goal", "Alliance color not the teamcolor"); } } else { SmartDashboard.putString("goal", "NoGoalNow!!!!!"); } } @Override public void end(boolean interrupted){ System.out.println("Limelightend"); } @Override public boolean isFinished(){ return false; } } ``` 但這個程式仍然有許多問題,像是使用Switch本身會讓程式結構變得攏長,不方便維護,跟擴展性極低......,雖然我們不能使用多型來編寫這個Limelight程式,但我們可以運用其他方法來解決這些問題: 首先我們先把我們的getTag```Function```變成這種形式,這樣才能最大化運用集合物件的概念,我們的目的是希望Limelight在看到Tag時能夠知道它的屬性,顯然上一版忘記了這個設計初衷,所以我們讓Limelight需要看到的ID都加上它的屬性,並讓Limelight在看到TagID的時候可以直接回傳它的屬性,而我們也在此加上了保護,為了避免在我們看到沒有編寫屬性的ID時系統會跳NullPointerException我們加了getOrDefault讓它直接回傳空的屬性 ```java public Student getTag() { HashMap<Integer, Student> tagMap = new HashMap<>(); tagMap.put(3, new Student("red", "Speaker")); tagMap.put(4, new Student("red", "Speaker")); tagMap.put(5, new Student("red", "Amp")); tagMap.put(9, new Student("red", "Source")); tagMap.put(10, new Student("red", "Source")); tagMap.put(1, new Student("blue", "Source")); tagMap.put(2, new Student("blue", "Source")); tagMap.put(6, new Student("blue", "Amp")); tagMap.put(7, new Student("blue", "Speaker")); tagMap.put(8, new Student("blue", "Speaker")); tagMap.getOrDefault((int) getTagID(), new Student("", ""));// 避免NullPointerException return tagMap.get((int) getTagID()); } ``` 之後我們在student這個類別裡加上一個```Function```來接收Limelight看到的Tag的屬性 ```java public String[] CorrespondKey() { return new String[] { Fiedcharacteristic.get("TagAlliance"), Fiedcharacteristic.get("TagName") }; } ``` 然後我們就可以把我們的判斷式縮減成這樣一行,就能夠成功解決之前的問題 ```java String[] TagField = limelight.getTag().CorrespondKey(); String Nowalliance = DriverStation.getAlliance().toString(); if (TagField == new String[] { Nowalliance, Name }) { m_drive.drive((Target + limelight.getTargetSpaceRobotPose()[2]) * Zkp, limelight.getTargetSpaceRobotPose()[0] * Xkp, limelight.getTargetSpaceRobotPose()[5] * Yawkp, true, true); } ```