Ограничить направление с помощью линейного программирования (симплекс) - PullRequest
0 голосов
/ 12 сентября 2018

Я хочу сделать модульную 2D игру на космическом корабле. Пользователь может использовать строительные блоки для создания корабля. Блоки могут быть квадратами, а также другими фигурами, которые не помещаются в сетку. Чтобы вычислить, какие двигатели должны запускаться с пользовательским вводом, я использую ojAlgo для решения задачи линейного программирования. Пользовательский ввод будет выглядеть примерно так: [x: 1, y: 0, z 0], где a 1 - максимальная тяга, которая может быть дана в направлении, и -1 - то же самое для другой стороны. Z здесь вращение.

У меня две проблемы:
Первое связано с тем, как я вычисляю x и y отдельно:

final ExpressionsBasedModel tmpModel = new ExpressionsBasedModel();
final Expression expressionX = tmpModel.addExpression("x").weight(1); //This is the user input.
final Expression expressionY = tmpModel.addExpression("y").weight(0).level(0); //Level means lower == 0 and upper == 0
final Expression expressionZ = tmpModel.addExpression("z").weight(0).level(0);

for (int i = 0; i < size; i++) {
    ThrusterBlock thrusterBlock = blockStash.thrusterList.get(i);
    final Variable thrusterVar = Variable.make("th_" + i);
    thrusterVar.lower(0).upper(1);
    tmpModel.addVariable(thrusterVar);

    expressionX.setLinearFactor(thrusterVar, thrusterBlock.displacement.x);
    expressionY.setLinearFactor(thrusterVar, thrusterBlock.displacement.y);
    expressionZ.setLinearFactor(thrusterVar, thrusterBlock.displacement.z); 
    //The thruster displacement is the calculated thrust in the given direction
}

Optimisation.Result tmpResult = tmpModel.maximise(); //To get the maximum weight (& thrust) out.

Это работает, потому что я могу установить вес> 0 или <0, и для каждого двигателя будет возвращаться значение между 0 и 1. При компоновке с 9 двигателями это может быть выходом: </p>

OPTIMAL 1.203 @ [1.0, 1.0, 0.0, 0.0, 0.32, 0.32, 1.0, 1.0, 1.0]
[
    0th [x: 0.401, y: 0.0 , z: -0.022]
    1th [x: 0.401, y: 0.0 , z: -0.022]
    2th [x: -0.401, y: 0.0 , z: -0.027]
    3th [x: -0.401, y: 0.0 , z: -0.027]
    4th [x: 0.0, y: 0.401 , z: 0.025]
    5th [x: 0.0, y: -0.401 , z: 0.025]
    6th [x: 0.0, y: -0.401 , z: 0.025]
    7th [x: 0.0, y: 0.401 , z: 0.025]
    8th [x: 0.401, y: 0.0 , z: -0.022]
]

Проблема в том, что я не могу использовать эту систему для перемещения по диагонали (например, 40 °), потому что

final Expression expressionX = tmpModel.addExpression("x").weight(1);
final Expression expressionY = tmpModel.addExpression("y").weight(1);

Не будет ограничивать уравнение соотношением x: y 1: 1, но найду наиболее выгодное решение. Я не могу использовать уровень здесь, потому что я не знаю максимальную тягу для обоих. Что было бы лучшим способом ограничить x & y, чтобы я мог использовать градусы?

Второй вопрос:
Когда я хочу, чтобы корабль повернулся, он поворачивается примерно на 45 °, а затем поворачивает назад. Хотя система управления показывает, что те же двигатели ведут огонь. Я уверен, что есть проблема с тем, как я вычисляю силу, но мой опыт с вычислениями углов низок Я думаю, что ошибку можно найти здесь:

public Vector2 getForce(float power, boolean relative){
   float force = maxThrust * power;
   float angle = toRad(this.relativePosition.z) + (relative ? 0 : this.blockCluster.getPos().z);
   //relativePosition.z is the orientation of the block in degree and this.blockCluster.getPos().z is the ship rotation in radians
   temp.x = force * ((float) Math.cos(angle)) * -1;
   temp.y = force * ((float) Math.sin(angle));
   return temp;
}

Я думаю, это как-то связано с тем, что Косинус и Синус возвращаются только на 90 °, но я не уверен.

Наконец, любая оптимизация будет принята с благодарностью, поскольку она может выполняться для нескольких кадров подряд. Я сохраню результаты расчета, если он находится на медленной стороне.

Спасибо за ваше время!

ps: перенесено из 'Game Development', потому что реализация более общая, чем сценарий использования.

...