Как я могу реализовать многопоточный ПИД-контроллер в Java?

Я немного новичок в 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?

2 ответа

Решение

Вы уже реализовали run() метод для PIDController поэтому вы должны также реализовать Runnable интерфейс:

class PIDController implements Runnable {
    ....
}

Теперь вы можете запустить свой PIDController асинхронно, вызвав:

pidControllerThread = new Thread( pidController );
pidControllerThread.start();

Для синхронизации (при необходимости) вы должны взглянуть на руководство по параллельности солнца.

Безусловно, лучший механизм для прикрепления потоков к чему-либо - это отделение объекта, который выполняет работу, от объекта, который является потоком. Интерфейс Runnable может быть привлекательным, поскольку он позволяет людям передавать объект в конструктор Thread или Executor и запускать его. Однако, если у вас есть требования к управлению жизненным циклом для вашего объекта, которые выходят за рамки "запуска до завершения", например, приостановки, то в большинстве случаев вы найдете более подходящим управление потоком внутри вашего объекта, чтобы вы знали, какой поток работает (да, вы можете установить объект экземпляра в Thread.currentThread () на входе для запуска, но...).

Итак, я думаю, что у вас есть хорошее начало. Вы должны добавить использование некоторой блокировки, чтобы помочь себе управлять pause () и другими средствами управления потоками.

    открытый класс PIDController {
        private final Object myLock = new Object(); приватный финал ControllerMethods ctrl; частная летучая нить; частный летучий раннер;
        private int pidInterval = 700; закрытый финал двойной setPoint, кп, ки, кд;

        public PIDController(двойной setPoint, двойной kp, двойной ki, двойной kd, экземпляр ControllerMethods) {
            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+": уже приостановлено");
                }
                runner.paused = true;
            }
        }

        public void resume() {
            synchronized( myLock) {
                if(!runner.paused) {
                    throw new IllegalOperationException(this+": уже возобновлено");
                }
                runner.paused = false;
            }
        }

        public bool isRunning() {
            return running;
        }

        public void start() {
            if( thread!= null) {
                throw new IllegalOperationException( this+": уже запущен");
            }
            myThread = new Thread( runner = new Runner());
            myThread.start();
        }

        public void stop() {
            if( runner == null) {
                throw new IllegalOperationException( this+": PID не запущен");
            }
            runner.running = false;
            if( runner.paused)
                resume(); бегун = ноль;
        }


        // Важно, чтобы в любое время, когда вы реализуете Runnable, который можно остановить, // вы включаете флаг "running" в качестве члена класса экземпляра innner, такого как // this, чтобы при запросе остановки этого экземпляра вы могли немедленно перезапустить // другой экземпляр и не иметь двух потоков, наблюдающих один и тот же флаг "бег" частного класса Runner реализует Runnable {
            volatile bool running = false, bool paused;
            public void run() {
                running = true;
                while( running) {
                    // сделать это в верхней части цикла, чтобы при пробуждении из // pause проверялось выполнение перед повторным вычислением.
                    reComputePID();

                    // Используем идиому двойной проверки для if( paused) {
                        synchronized( myLock) {
                            while( paused) {
                                myLock.wait();
                            }
                        }
                    }
                    Thread.sleep( pidInterval);
                }
            }
        }

        public void reComputePID() {
            ...
        }
    }
Другие вопросы по тегам