179 lines
7.5 KiB
C#
179 lines
7.5 KiB
C#
using AgroBase.Models;
|
|
using AgroBase.Services;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
|
|
public class MPCControllerAprimorado
|
|
{
|
|
private double distanciaPrevisao = 2.0;
|
|
private int horizonte;
|
|
private double maxAnguloDirecao;
|
|
private double maxAngulo4Rodas;
|
|
private double velocidadeMin;
|
|
private double velocidadeMax;
|
|
|
|
private double pesoOrientacao = 2.5;
|
|
private double pesoLateral = 1.5;
|
|
private double pesoDistancia = 0.8;
|
|
private double pesoSuavidade = 0.5;
|
|
private double limiteErroOrientacao = 45.0;
|
|
private double limiteErroLateral = 2.5; // metros
|
|
|
|
private DateTime ultimaAtualizacao = DateTime.MinValue;
|
|
|
|
public MPCControllerAprimorado()
|
|
{
|
|
var op = Variaveis.OperacaoEmAndamento;
|
|
|
|
this.maxAnguloDirecao = op.Parametros?.Controle?.DirAnguloMaximo ?? 30.0;
|
|
this.maxAngulo4Rodas = this.maxAnguloDirecao * 0.8;
|
|
this.velocidadeMin = op.Parametros?.Controle?.MovVelocidadeCErvasPercent ?? 20.0;
|
|
this.velocidadeMax = op.Parametros?.Controle?.MovVelocidadeSErvasPercent ?? 100.0;
|
|
}
|
|
|
|
public (double anguloControle, Enums.TipoMovimentoDirecional modo) CalcularControle(GPSModel posicalAtual, double velocidadeAtual, List<GPSModel> proximaTrajetoria, Enums.TipoMovimentoDirecional[] modosPermitidos)
|
|
{
|
|
if (proximaTrajetoria == null || proximaTrajetoria.Count < 2)
|
|
return (0, Enums.TipoMovimentoDirecional.RodasDianteiras);
|
|
|
|
if (proximaTrajetoria.Count > 3)
|
|
{
|
|
proximaTrajetoria = new List<GPSModel>(proximaTrajetoria.Take(3));
|
|
}
|
|
|
|
if (ultimaAtualizacao == DateTime.MinValue)
|
|
{
|
|
ultimaAtualizacao = DateTime.Now.AddMilliseconds(-(1.0 / GPSService.TaxaAmostragemHz));
|
|
}
|
|
|
|
double dt = (DateTime.Now - ultimaAtualizacao).TotalSeconds;
|
|
double tempoMax = (1.0 / GPSService.TaxaAmostragemHz) * 1.5;
|
|
dt = Math.Max(0.5, Math.Min(dt, tempoMax));
|
|
|
|
double velMs = FuncoesMatematicas.CalculaVelocidadeMsPercentual(velocidadeAtual);
|
|
double passoDist = velMs * dt;
|
|
|
|
horizonte = Math.Min(Convert.ToInt32(distanciaPrevisao / passoDist), proximaTrajetoria.Count);
|
|
|
|
var candidatos = GerarCandidatos(modosPermitidos);
|
|
double melhorCusto = double.MaxValue;
|
|
(double angulo, Enums.TipoMovimentoDirecional modo) melhorControle = (0, Enums.TipoMovimentoDirecional.RodasDianteiras);
|
|
|
|
foreach (var candidato in candidatos)
|
|
{
|
|
double custo = SimularTrajetoriaComReavaliacao(posicalAtual, proximaTrajetoria, posicalAtual.AnguloCarroDefinido, candidato.angulo, candidato.modo, velMs, dt);
|
|
//Console.WriteLine($"Candidato: {candidato.angulo}, Custo: {custo.ToString()}");
|
|
if (custo < melhorCusto)
|
|
{
|
|
melhorCusto = custo;
|
|
melhorControle = candidato;
|
|
}
|
|
}
|
|
|
|
ultimaAtualizacao = DateTime.Now;
|
|
return melhorControle;
|
|
}
|
|
|
|
private List<(double angulo, Enums.TipoMovimentoDirecional modo)> GerarCandidatos(Enums.TipoMovimentoDirecional[] modos)
|
|
{
|
|
var candidatos = new List<(double, Enums.TipoMovimentoDirecional)>();
|
|
|
|
foreach (var modo in modos)
|
|
{
|
|
double maxAng = (modo == Enums.TipoMovimentoDirecional.MovimentoArco || modo == Enums.TipoMovimentoDirecional.MovimentoDiagonal)
|
|
? maxAngulo4Rodas : maxAnguloDirecao;
|
|
|
|
for (double ang = -maxAng; ang <= maxAng; ang += 5.0)
|
|
{
|
|
candidatos.Add((ang, modo));
|
|
}
|
|
}
|
|
|
|
return candidatos;
|
|
}
|
|
|
|
private double SimularTrajetoriaComReavaliacao(GPSModel posicaoInicial, List<GPSModel> trajeto, double anguloInicial, double anguloControleInicial, Enums.TipoMovimentoDirecional modoInicial, double velocidadeMs, double dt)
|
|
{
|
|
var op = Variaveis.OperacaoEmAndamento;
|
|
|
|
double custoTotal = 0;
|
|
GPSModel posAtual = posicaoInicial;
|
|
double anguloAtual = anguloInicial;
|
|
double anguloControle = anguloControleInicial;
|
|
Enums.TipoMovimentoDirecional modo = modoInicial;
|
|
|
|
posAtual = VariaveisEquipamento.SimularNovaPosicao(modoInicial, anguloControleInicial, velocidadeMs, dt, anguloAtual, posAtual);
|
|
anguloAtual = posAtual.OrientacaoReal;
|
|
|
|
for (int i = 0; i < horizonte; i++)
|
|
{
|
|
double melhorCustoIteracao = double.MaxValue;
|
|
|
|
foreach (var candidato in GerarCandidatos(new[] { modo }))
|
|
{
|
|
var novaPos = VariaveisEquipamento.SimularNovaPosicao(candidato.modo, candidato.angulo, velocidadeMs, dt, anguloAtual, posAtual);
|
|
var pontoAlvo = trajeto[Math.Min(i + 1, trajeto.Count - 1)];
|
|
|
|
double orientacaoAlvo = GPSUtils.CalcularOrientacao(posAtual, pontoAlvo);
|
|
|
|
double d0 = GPSUtils.DistanciaEntrePontos(posAtual, novaPos);
|
|
double a0 = GPSUtils.CalcularOrientacao(posAtual, novaPos);
|
|
|
|
double d1 = GPSUtils.DistanciaEntrePontos(posAtual, pontoAlvo);
|
|
double a1 = GPSUtils.CalcularOrientacao(posAtual, pontoAlvo);
|
|
|
|
double d2 = GPSUtils.DistanciaEntrePontos(novaPos, pontoAlvo);
|
|
double a2 = GPSUtils.CalcularOrientacao(novaPos, pontoAlvo);
|
|
|
|
double erroDist = d2 - d1;
|
|
double erroOri = Math.Abs(GPSUtils.CalcularDiferencaAngulo(novaPos.OrientacaoReal, orientacaoAlvo));
|
|
|
|
(GPSModel ponto, int idx) = GPSUtils.PontoMaisProximoTrechoComIndice(posAtual, trajeto);
|
|
int idxNovoPonto = op.Trajetoria.PontoAtual.idxPonto + idx;
|
|
PontoTrajetoriaModel pontoAtual = op.Trajetoria._TrajetoriaFixa[idxNovoPonto].Clone();
|
|
|
|
int idxRuaEsquerda = op.Trajetoria._Corredores[pontoAtual.idxCorredor].idxRuaEsquerda;
|
|
double distEsq = op.Trajetoria.CalcularDistanciaLateral(true, posAtual, idxRuaEsquerda, pontoAtual.Posicao);
|
|
|
|
int idxRuaDireita = op.Trajetoria._Corredores[pontoAtual.idxCorredor].idxRuaDireita;
|
|
double distDir = op.Trajetoria.CalcularDistanciaLateral(false, posAtual, idxRuaDireita, pontoAtual.Posicao);
|
|
|
|
double erroLat = Math.Abs(distEsq - distDir);
|
|
|
|
double deltaAngulo = Math.Abs(GPSUtils.CalcularDiferencaAngulo(novaPos.OrientacaoReal, anguloAtual));
|
|
|
|
//Console.WriteLine($"Angulo Inicial: {anguloControleInicial}, Passo: {i}, Candidato: {candidato.angulo}, Erro lateral: {erroLat}, Erro orientacao: {erroOri}");
|
|
|
|
if (false && (erroOri > limiteErroOrientacao || erroLat > limiteErroLateral))
|
|
{
|
|
continue; // Ignora este candidato
|
|
}
|
|
|
|
double custo = (erroOri * pesoOrientacao) + (erroLat * pesoLateral) + (erroDist * pesoDistancia) + (deltaAngulo * pesoSuavidade);
|
|
|
|
if (custo < melhorCustoIteracao)
|
|
{
|
|
melhorCustoIteracao = custo;
|
|
posAtual = novaPos;
|
|
anguloAtual = novaPos.OrientacaoReal;
|
|
anguloControle = candidato.angulo;
|
|
modo = candidato.modo;
|
|
|
|
//Console.WriteLine($"Angulo Inicial: {anguloControleInicial}, Passo: {i}, Candidato: {candidato.angulo}, Custo: {custo}, Erro lateral: {erroLat}, Erro orientacao: {erroOri}, Erro distancia: {erroDist}");
|
|
}
|
|
}
|
|
|
|
custoTotal += melhorCustoIteracao;
|
|
|
|
if (melhorCustoIteracao == double.MaxValue)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return custoTotal;
|
|
}
|
|
|
|
}
|