# KOP底盤撰寫 使用 Neo (SparkMax) 馬達為範例 ### 前置觀念 1. 左右邊各為一個 Module (程式模塊) 2. 透過搖桿控制 前後、左右 ## 常數設置 1. 於 `src\main\java\frc\robot\DeviceId.java` 寫入馬達ID(編號) ```java package frc.robot; public class DeviceId { public static final class DriveMotor { public static final int FRONT_LEFT = 1; public static final int FRONT_RIGHT = 3; public static final int BACK_LEFT = 2; public static final int BACK_RIGHT = 4; } } ``` 2. 於 `src\main\java\frc\robot\Constants.java` 寫入馬達前進和旋轉最大速度與 Deadband > Deadband 用於防止搖桿無法完全歸零, 搖桿輸入需大於 Deadband 否則視為 0 ```java public final class Drive { public static final double MAX_SPEED = 0.5; // 底盤最大速度 (%) public static final double MAX_TURN_SPEED = 0.7; public static final double DEAD_BAND = 0.05; // 當前面的值小於0.05則視為0 } ``` 3. 一樣於 `Constants` 中寫入馬達的正反轉 (同一邊的馬達轉向相同) ```java public final class MotorReverse { public static final boolean FRONT_LEFT = false; public static final boolean FRONT_RIGHT = true; public static final boolean BACK_LEFT = false; public static final boolean BACK_RIGHT = true; } ``` ## 模組化 如果一組機構中使用多個相同作用的馬達, 模組可以使程式更加簡潔 1. 於 `src\man\java\frc\robot\subsystems` 創建 `DriveMotorModule.java` 2. 引入函示庫 ```java package frc.robot.subsystems; import com.revrobotics.spark.SparkMax; import com.revrobotics.spark.SparkBase.PersistMode; import com.revrobotics.spark.SparkBase.ResetMode; import com.revrobotics.spark.SparkLowLevel.MotorType; import com.revrobotics.spark.config.SparkBaseConfig.IdleMode; import com.revrobotics.spark.config.SparkMaxConfig; import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import frc.robot.lib.helpers.IDashboardProvider; ``` 3. 於 `DriveMotorModule` 宣告單顆馬達 ```java public class DriveModule { private final SparkMax frontSpark; // 模塊前面的馬達 private final SparkMax backSpark; // 模塊後面的馬達 public DriveMotorModule( int frontId, int backId, boolean frontReverse, boolean backReverse ) { // 創建馬達 this.frontSpark = new SparkMax(frontId, MotorType.kBrushless); this.backSpark = new SparkMax(backId, MotorType.kBrushless); // 前面跟後面的 設定 SparkMaxConfig frontConfig = new SparkMaxConfig(); frontConfig .idleMode(IdleMode.kCoast) .inverted(frontReverse) .smartCurrentLimit(30); SparkMaxConfig backConfig = new SparkMaxConfig(); backConfig .idleMode(IdleMode.kCoast) .inverted(backReverse) .smartCurrentLimit(30); // 馬達 Apply設定 this.frontSpark.configure(frontConfig, ResetMode.kResetSafeParameters, PersistMode.kPersistParameters); this.backSpark.configure(backConfig, ResetMode.kResetSafeParameters, PersistMode.kPersistParameters); } } ``` 4. 寫入轉動與停止方法 ```java public void setDesiredState(double speed) { this.frontSpark.set(speed); this.backSpark.set(speed); } public void stop() { this.frontSpark.stopMotor(); this.backSpark.stopMotor(); } ``` ## 創建兩個模塊的底盤 完成 DriveMotorModule 後可於 Subsystem 中使用 1. 於 `src\man\java\frc\robot\subsystems` 創建 `DriveMotorSubsystem.java` 2. 引入函式庫 ```java package frc.robot.subsystems; import edu.wpi.first.wpilibj2.command.SubsystemBase; import frc.robot.Constants.MotorReverse; import frc.robot.DeviceId.DriveMotor; ``` 3. 於 `DriveMotorSubsystem` 宣告四顆馬達 ```java public class DriveMotorSubsystem extends SubsystemBase { private final DriveMotorModule leftModule; private final DriveMotorModule rightModule; public DriveMotorSubsystem() { // 創建 左右邊角塊 this.leftModule = new DriveMotorModule(DriveMotor.FRONT_LEFT, DriveMotor.BACK_LEFT, MotorReverse.FRONT_LEFT, MotorReverse.BACK_LEFT ); this.rightModule = new DriveMotorModule(DriveMotor.FRONT_RIGHT, DriveMotor.BACK_RIGHT, MotorReverse.FRONT_RIGHT, MotorReverse.BACK_RIGHT ); } } ``` 4. 創建讓 KOP 移動的方法, 左右兩邊各需要一個速度讓底盤旋轉,故有 left 和 right Speed ```java public void move(double leftSpeed, double rightSpeed) { this.leftModule.setDesiredState(leftSpeed); this.rightModule.setDesiredState(rightSpeed); } ``` 5. 創建讓底盤停止的方法 ```java public void stopModules() { this.leftModule.stop(); this.rightModule.stop(); } ``` ## 控制底盤 1. 於 `src\main\java\frc\robot` 創建 `GamepadJoystick.java` , 用於搖桿操作 ```java package frc.robot; import edu.wpi.first.wpilibj.XboxController; public class GamepadJoystick extends XboxController { public GamepadJoystick(int port) { super(port); } public static final int CONTROLLER_PORT = 0; } ``` 2. 於 `src\main\java\frc\robot\commands` 創建 `DriveCmd.java` 3. 引入函式庫 ```java package frc.robot.commands; import edu.wpi.first.math.MathUtil; import edu.wpi.first.wpilibj.XboxController; import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import edu.wpi.first.wpilibj2.command.Command; import frc.robot.Constants.Drive; import frc.robot.subsystems.DriveSubsystem; ``` 3. 於 `DriveCmd` 導入 `DriveMotorSubsystem` 與搖桿 ```java public class DriveCmd extends Command { private final DriveSubsystem driveSubsystem; private final XboxController controller; public DriveCmd(DriveSubsystem driveSubsystem, XboxController controller) { this.driveSubsystem = driveSubsystem; this.controller = controller; this.addRequirements(this.driveSubsystem); } } ``` ### Arcade Drive 透過運算將移動與轉向的控制分開 ```java @Override public void execute() { double driveSpeed = -MathUtil.applyDeadband(this.controller.getLeftY(), Drive.DEAD_BAND) * Drive.MAX_SPEED; double turnSpeed = MathUtil.applyDeadband(this.controller.getRightX(), Drive.DEAD_BAND) * Drive.MAX_TURN_SPEED; double leftSpeed = driveSpeed + turnSpeed; double rightSpeed = driveSpeed - turnSpeed; this.driveSubsystem.move(leftSpeed, rightSpeed); SmartDashboard.putNumber("LeftSpeed", leftSpeed); SmartDashboard.putNumber("RightSpeed", rightSpeed); } ``` 5. 於 `end()` 寫入指令執行完成後動作 ```java @Override public void end(boolean interrupted) { this.driveMotorSubsystem.stopModules(); } ``` 6. 於 `isFinished()` 回傳 `false` 表示程式 Command 不停止 ```java @Override public boolean isFinished() { return false; } ``` 7. 把 `DriveCmd.java` 寫入 `RobotContainer.java` ```java package frc.robot; import edu.wpi.first.wpilibj.XboxController; import edu.wpi.first.wpilibj2.command.Command; import frc.robot.commands.DriveCmd; import frc.robot.subsystems.DriveSubsystem; public class RobotContainer { private final GamepadJoystick joystick = new GamepadJoystick(GamepadJoystick.CONTROLLER_PORT); private final DriveMotorSubsystem driveMotorSubsystem = new DriveMotorSubsystem(); private final DriveJoystickCmd driveJoystickCmd = new DriveJoystickCmd(driveMotorSubsystem, joystick); public RobotContainer() { this.driveMotorSubsystem.setDefaultCommand(this.driveJoystickCmd); } public Command getAutonomousCommand() { return null; } } ```