Как я могу реализовать многопоточный ПИД-контроллер в Java?
-
06-07-2019 - |
Вопрос
Я немного новичок в Java, так что еще не полностью осознал концепцию многопоточности. Я хотел бы создать класс PIDController, который позволит мне сделать это:
ControllerMethods methods = new ControllerMethods()
{
public long getError(long setpoint)
{
//get an input
}
public void setOutput(long value)
{
//do something
}
public void isComplete(long setpoint)
{
return getError() == 0;
}
};
PIDController motorPID = new PIDController(setpoint, kp, ki, kd, methods);
motorPID.run();
//runs the PID controller to completion (methods.isComplete() == true)
motorPID.run(false);
//starts the PID controller in a separate thread, allowing
//continual monitoring in the current thread
while(motorPID.isRunning())
{
//do something else
if(condition1)
motorPID.pause();
//pause the PID controller, preventing the integral from increasing
else if(condition2)
motorPID.stop();
}
Я разработал способ вычисления стандартных алгоритмов PID, но не могу понять, как обеспечить асинхронную функциональность.
Кто-нибудь может сказать мне, как я могу получить аналогичный API?
Решение
Вы уже реализовали метод run ()
для PIDController
, поэтому вам также следует реализовать интерфейс Runnable
:
class PIDController implements Runnable {
....
}
Теперь вы можете запустить свой PIDController асинхронно, вызвав:
pidControllerThread = new Thread( pidController );
pidControllerThread.start();
Для синхронизации (при необходимости) вы должны взглянуть на солнце руководство по параллелизму .
Другие советы
Безусловно, лучший механизм для прикрепления потоков к чему-либо - это отделение объекта, который выполняет работу, от объекта, который является потоком. Интерфейс Runnable может быть привлекательным, поскольку он позволяет людям передавать объект в конструктор Thread или Executor и запускать его. Однако если у вас есть требования к управлению жизненным циклом для вашего объекта, которые находятся за пределами «запуска к завершению», например, приостановка, то в большинстве случаев вы найдете более подходящим управление потоком внутри вашего объекта, чтобы вы знали, какие поток запущен (да, вы можете установить для объекта экземпляра Thread.currentThread () в записи для запуска, но ...).
Итак, я думаю, что у вас есть хорошее начало. Вам нужно добавить использование некоторой блокировки, чтобы помочь себе управлять pause () и другими средствами управления потоками.
public class PIDController { private final Object myLock = new Object(); private final ControllerMethods ctrl; private volatile Thread thread; private volatile Runner runner; private int pidInterval = 700; private final double setPoint, kp, ki, kd; public PIDController( double setPoint, double kp, double ki, double kd, ControllerMethods inst ) { this.ctrl = inst; this.setPoint = setPoint; this.kp = kp; this.ki = ki; this.kd = kd; } public void pause() { synchronized( myLock ) { if( runner.paused ) { throw new IllegalOperationException(this+": already paused"); } runner.paused = true; } } public void resume() { synchronized( myLock ) { if( !runner.paused ) { throw new IllegalOperationException(this+": already resumed"); } runner.paused = false; } } public bool isRunning() { return running; } public void start() { if( thread != null ) { throw new IllegalOperationException( this+": already running"); } myThread = new Thread( runner = new Runner() ); myThread.start(); } public void stop() { if( runner == null ) { throw new IllegalOperationException( this+": PID is not running"); } runner.running = false; if( runner.paused ) resume(); runner = null; } // It is important, anytime that you implement a stoppable Runnable, that // you include the "running" flag as a member of an innner instance class like // this so that when you ask this instance to stop, you can immediately restart // another instance and not have the two threads observing the same "running" flag private class Runner implements Runnable { volatile bool running = false, bool paused; public void run() { running = true; while( running ) { // do this at the top of the loop so that a wake from // pause will check running, before recomputing. reComputePID(); // Use the double check idiom to if( paused ) { synchronized( myLock ) { while( paused ) { myLock.wait(); } } } Thread.sleep( pidInterval ); } } } public void reComputePID() { ... } }