Motion Profiling ve PID Control’ü ProfiledPIDController ile Birleştirme

Not

ProfiledPIDController sınıfının command-based framework Çerçevesine uygulanmasına ilişkin bir kılavuz için, bkz: Komut Tabanlı Hareket Profilleme ve PID’yi Birleştirme.

Önceki makalede, trapezoidal hareket profili oluşturmak ve kullanmak için `` TrapezoidProfile`` sınıfının nasıl kullanılacağını gördük. Bu makaledeki örnek kod, bir “akıllı” motor kontrol cihazının harici PID kontrol özelliği ile TrapezoidProfile sınıfını manuel olarak oluşturmayı gösterir.

Bu işlevsellik kombinasyonu (bunları takip etmek için bir PID kontrolörü ile birleştirilmiş ayar noktaları oluşturmak için bir hareket profili) son derece yaygındır. Bunu kolaylaştırmak için WPILib, bu iki işlevi birleştirme işinin çoğunu yapan bir ProfiledPIDController sınıfıyla (Java <https://first.wpi.edu/wpilib/allwpilib/docs/release/java/edu/wpi/first/wpilibj/controller/ProfiledPIDController.html> __, C ++ <https://first.wpi.edu/wpilib/allwpilib/docs/release/cpp/classfrc_1_1ProfiledPIDController.html> __) gelir. `` ProfiledPIDController`` in API’si, `` PIDController`` in API’sine çok benzer ve kullanıcıların kodlarında çok az değişiklik yaparak PID kontrollü bir mekanizmaya hareket profili eklemesine izin verir.

ProfiledPIDController sınıfını kullanma

Not

C ++ ‘da, ``ProfiledPIDController``sınıfı, açısal veya doğrusal olabilen mesafe ölçümleri için kullanılan birim türüne göre şablonlanır. İletilen değerler * zorunlu * mesafe birimleriyle tutarlı birimlere sahip olmalıdır, aksi takdirde derleme zamanı hatası atılır. C ++ birimleri hakkında daha fazla bilgi için bkz C ++ Ünite Kitaplığı..

Not

ProfiledPIDController işlevinin çoğu, `` PIDController`` ile etkin bir şekilde aynıdır. Buna göre, bu makale yalnızca hareket profili oluşturma işlevini barındırmak için büyük ölçüde değiştirilmiş özellikleri kapsayacaktır. Standart PIDController özellikleri hakkında bilgi için bkz .: ref: docs / software / advanced-controls / controllers / pidcontroller: PID Control in WPILib.

Profilli PIDController Oluşturma

Not

C ++ genellikle iç sınıfların türünü çıkarabilir ve bu nedenle basit bir başlatıcı listesi (sınıf adı olmadan) parametre olarak gönderilebilir. Tam sınıf adı, netlik açısından aşağıdaki örnekte verilmiştir.

Bir ProfiledPIDController oluşturmak, creating a PIDController oluşturmak ile neredeyse aynıdır. Tek fark, dahili olarak oluşturulan `` TrapezoidProfile ‘’ örneklerine otomatik olarak iletilecek olan bir dizi trapezoidal profile constraints, sağlama ihtiyacıdır:

// Creates a ProfiledPIDController
// Max velocity is 5 meters per second
// Max acceleration is 10 meters per second
ProfiledPIDController controller = new ProfiledPIDController(
  kP, kI, kD,
  new TrapezoidProfile.Constraints(5, 10));

Hedef ve Ayar Noktası

Standart bir `` PIDController`` ile `` ProfiledPIDController`` arasındaki en büyük fark, kontrol döngüsünün gerçek* ayar noktasının * kullanıcı tarafından doğrudan belirtilmemesidir. Bunun yerine, kullanıcı bir *hedef * konumu veya durumu belirtir ve kontrolör için ayar noktası, mevcut durum ile hedef arasında oluşturulan hareket profilinden otomatik olarak hesaplanır. Dolayısıyla, kullanıcı tarafı arama çoğunlukla aynı görünürken:

// Calculates the output of the PID algorithm based on the sensor reading
// and sends it to a motor
motor.set(controller.calculate(encoder.getDistance(), goal));

Belirtilen hedef değeri (sıfırdan farklı bir hız isteniyorsa, bir konum değeri veya bir TrapezProfile.State olabilir), döngünün zorunlu akım *ayar noktası değildir - daha ziyade, oluşturulan profil sona erdiğinde *nihai * ayar noktasıdır.

Ayar Noktasını Alma / Kullanma

ProfiledPIDController hedefi ayar noktasından farklı olduğundan, genellikle kontrolörün mevcut ayar noktasını sorgulamak istenir (örneğin, kullanılacak değerleri almak için: ref: ileri besleme <docs/software/advanced-controls/controllers/combining-feedforward-feedback:Using Feedforward Components with PID>). Bu, getSetpoint () yöntemi ile yapılabilir.

Döndürülen ayar noktası daha sonra aşağıdaki örnekte olduğu gibi kullanılabilir:

double lastSpeed = 0;
double lastTime = Timer.getFPGATimestamp();

// Controls a simple motor's position using a SimpleMotorFeedforward
// and a ProfiledPIDController
public void goToPosition(double goalPosition) {
  double acceleration = (controller.getSetpoint().velocity - lastSpeed) / (Timer.getFPGATimestamp() - lastTime)
  motor.setVoltage(
      controller.calculate(encoder.getDistance(), goalPosition)
      + feedforward.calculate(controller.getSetpoint().velocity, acceleration));
  lastSpeed = controller.getSetpoint().velocity;
  lastTime = Timer.getFPGATimestamp();
}

Tam Kullanım Örneği

ElevatorProfilePID örnek projesinde ProfiledPIDController kullanımının daha eksiksiz bir örneği verilmiştir (Java <https://github.com/wpilibsuite/allwpilib/tree/main/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/elevatorprofiledpid> __, C ++ <https://github.com/wpilibsuite/allwpilib/tree/main/wpilibcExamples/src/main/cpp/examples/ElevatorProfiledPID/cpp> __):

 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package edu.wpi.first.wpilibj.examples.elevatorprofiledpid;

import edu.wpi.first.wpilibj.Encoder;
import edu.wpi.first.wpilibj.Joystick;
import edu.wpi.first.wpilibj.PWMVictorSPX;
import edu.wpi.first.wpilibj.SpeedController;
import edu.wpi.first.wpilibj.TimedRobot;
import edu.wpi.first.wpilibj.controller.ProfiledPIDController;
import edu.wpi.first.wpilibj.trajectory.TrapezoidProfile;

public class Robot extends TimedRobot {
  private static double kDt = 0.02;

  private final Joystick m_joystick = new Joystick(1);
  private final Encoder m_encoder = new Encoder(1, 2);
  private final SpeedController m_motor = new PWMVictorSPX(1);

  // Create a PID controller whose setpoint's change is subject to maximum
  // velocity and acceleration constraints.
  private final TrapezoidProfile.Constraints m_constraints =
      new TrapezoidProfile.Constraints(1.75, 0.75);
  private final ProfiledPIDController m_controller =
      new ProfiledPIDController(1.3, 0.0, 0.7, m_constraints, kDt);

  @Override
  public void robotInit() {
    m_encoder.setDistancePerPulse(1.0 / 360.0 * 2.0 * Math.PI * 1.5);
  }

  @Override
  public void teleopPeriodic() {
    if (m_joystick.getRawButtonPressed(2)) {
      m_controller.setGoal(5);
    } else if (m_joystick.getRawButtonPressed(3)) {
      m_controller.setGoal(0);
    }

    // Run controller and update motor output
    m_motor.set(m_controller.calculate(m_encoder.getDistance()));
  }
}