Atividade #609
FechadaImplementação de KD-Tree para otimização do RRT
Descrição
As equipes que usam RRT como algoritmo de planejamento de trajetória otimizam a checagem de vizinho mais próximo da árvore ( que originalmente é feita com uma varredura completa da árvore, cuja complexidade computacional é ´O(n)´ , sendo ´n´ a quantidade de nós da árvore ) utilizando um método de clusterização da árvore chamado KD-Tree (árvore K dimensional). Segundo [este](http://www4.cs.umanitoba.ca/~jacky/Robotics/Papers/Bruce-RealTimeRandomizedPathPlanning.pdf) paper da CMDragons, a otimização permite que o algoritmo seja utilizado para pelo menos 5 robôs com folga de recursos computacionais para os algoritmos de IA.
Arquivos
Atualizado por Nicolas Oliveira há mais de 7 anos
- Arquivo nearest_search.pdf nearest_search.pdf adicionado
- Arquivo CG_RangeKDtrees.pdf CG_RangeKDtrees.pdf adicionado
Para estudo foi utilizado o seguinte [link](https://salzis.wordpress.com/2014/06/28/kd-tree-and-nearest-neighbor-nn-search-2d-case/) e os documentos anexos.
Entendi como a kd-tree funciona e tenho uma ideia para sua implementação. Vou gerar uma wiki no redmine sobre isso.
Atualizado por Luiz Renault Leite Rodrigues há mais de 7 anos
Colocar resumo na tarefa sobre o funcionamento.
Atualizado por Nicolas Oliveira há mais de 7 anos
- Arquivo buildkdtreeCode.PNG buildkdtreeCode.PNG adicionado
- Arquivo CreateKD-Tree.PNG CreateKD-Tree.PNG adicionado
- Arquivo KD-Tree.PNG KD-Tree.PNG adicionado
- Arquivo buildkdtree.PNG buildkdtree.PNG adicionado
O primeiro passo foi entender como uma KD-Tree é criada. A ideia principal é fazer uma busca binária só que em 2D. Pra isso vamos dividindo o ambiente em dois por X e por Y alternadamente. Os pontos restandes de cada lado dessas regiões são ordenados com relação ao eixo em que serão divididos.
De acordo com as imagens abaixo:


Podemos gerar esta árvore utilizando a implementação do pseudo código abaixo:

Atualizado por Luciano Barreira há mais de 7 anos
Encontrei [esta](https://www.geeksforgeeks.org/k-dimensional-tree/) referência muito boa do GeeksForGeeks.
A ideia é utilizar a subrotina de inserção da árvore, pois se construíssemos a KD-Tree (nesse caso, 2D-Tree) toda vez que formos buscar pelo nearest, teríamos uma complexidade maior até que a atual, o que não é interessante já que queremos otimizar o processo. Dado que sabemos montar, inserir e buscar na KD-Tree (depois discutimos como implementar as subrotinas no LabView), minha sugestão de algoritmo, em alto nível, é:
- inicializa a 2D-Tree ``2Dtree``
- insere ``starting_point``
No loop de expansão do ``RRT``, para cada nó aleatório ``random_node``, fazemos:
- busca pelo vizinho ``neighbour`` mais próximo de ``random_node`` na ``2DTree``, utilizando rotina de busca
- gera nó auxiliar ``candidate`` na direção de ``neighbour`` a um passo ``r`` (expansão do ``RRT``). Note que estamos usando r como passo espacial, uma sugestão futura é utilizar um passo de tempo pela lógica do perfil bang-bang.
- checa se ``candidate`` está dentro de algum obstáculo
- se não
- expande ``RRT`` colocando ``candidate`` no RRT como filho de ``neighbour``
- expande ``2DTree`` inserindo ``candidate`` a partir de ``neighbour`` (não é necessário inserir a partir da raíz da ``2DTree``, uma vez que esta insersão resultaria em inserir a partir do ``neighbour``), consoante lógica de inserção da kd-tree
- se sim, continua
- checa se ``candidate`` está a uma distância ``d`` do ``goal_point`` (isso também pode ser levemente otimizado comparando as somas dos quadrados das diferenças das coordenadas, em vez de as raízes quadradas disto, mas isso é superotimizar).
- se sim
- insere ``goal_point`` no RRT como filho de ``candidate`` e termina o processo
Atualizado por Nicolas Oliveira há mais de 7 anos
- Arquivo KD-TreeLabView.PNG KD-TreeLabView.PNG adicionado
- Arquivo ex.PNG ex.PNG adicionado
Criada a VI para inserir pontos na KD-tree, ou construí-la do zero.
O fato de eu não saber como utilizar, ou se posso utilizar, ponteiros no labview dificultou bastante, já que todas as implementações se utilizavam disso.
Então criei uma estrutura de kD-tree com 3 dimensões (x,y,F), rebeca vai utilizar terceira dimenção, custo, no A*, mais o indice de seu filho a esquerda e a direita nessa ordem.
A arvore começa vazia e toda alguém é inserido e reservado espaço para seus filhos nos índices size e size+1 da árvore, colocando valor de infinito.
Ex: (3, 6), (17, 15), (13, 15), (6, 12), (9, 1), (2, 7), (10, 19) essa sequência gera:
Essa ávore

Fica assim

Sou capaz de dizer também em que nó quero começar a inserção. Isso será usado para que possa começar a inserção pelo neirest.
Atualizado por Nicolas Oliveira há mais de 7 anos
- Título alterado de Estudar implementações de KD-Tree para otimização do RRT para Implementação de KD-Tree para otimização do RRT
Atualizado por Nicolas Oliveira há mais de 7 anos
A ideia de utilizar os indices sempre como 2k e 2k+1, comum na construção de árvores binárias, foi abandonada pois necessitáriamos alocar muita memória desnecessária. Dessa maneira implementada n corremos risco nenhum de estouro de memória.
Atualizado por Luciano Barreira há mais de 7 anos
[este](https://forums.ni.com/t5/Example-Program-Drafts/Advanced-Data-Structures-in-LabVIEW/ta-p/3519662) link, tirado do submundo dos fóruns da NI, pode ajudar. Tem várias implementações abstraídas de árvore, fila, lista encadeada e grafos.
Atualizado por Nicolas Oliveira há mais de 7 anos
Dei uma olhada nas VIs que vem junto com essas implementações. Mas elas são bem complexas, porque mexem com o labview de uma forma que não estamos acostumados. Existe até um de shortest path, mas o tempo de execução me pareceu bem longo.
Talvez o ideal seja continuar usando nossos algoritmos já implementados porém mudar as estruturas para as dessa "biblioteca".
Atualizado por Luciano Barreira há mais de 7 anos
Nicolas Oliveira escreveu:
Dei uma olhada nas VIs que vem junto com essas implementações. Mas elas são bem complexas, porque mexem com o labview de uma forma que não estamos acostumados. Existe até um de shortest path, mas o tempo de execução me pareceu bem longo.
Talvez o ideal seja continuar usando nossos algoritmos já implementados porém mudar as estruturas para as dessa "biblioteca".
A ideia não é necessariamente usar essas VIs, mas usar as soluções de implementações delas em nossas soluções. São bibliotecas de desenvolvedores da NI, pelo que eu entendi.
Atualizado por Nicolas Oliveira há aproximadamente 7 anos
A implementação do Nearest Neighbor está sendo feita utilizando este [link](https://www.cs.cmu.edu/~ckingsf/bioinfo-lectures/kdtrees.pdf) como referência. Pseudo código do NN:

Onde uma da principais vantagens é o cálculo da BoundingBox (BB), de acordo com o desenho abaixo. Isso garante que sub-árvores irrelevantes não sejam visitadas.

Para o cálculo da BB foram implementadas as VIs de findMin e findMax, para achar os nós com as menores ou maiores coordenadas, respectivamente, tanto em x ou em y. Assim temos as fronteiras da BB. Caso a distância do Target a BB seja maior do que a Best_dist não existirá nenhum ponto promissor nessa BB. O que preciso ajeitar é como calcular a distância um ponto a um retângulo.
Caso eu esteja em uma sub-árvore promissora eu visitarei o melhor lado primeiro, o que também dá mais velocidade ao algoritmo.
Atualizado por Nicolas Oliveira há aproximadamente 7 anos
Estou tendo problemas também para atualizar as variáveis globais.
Atualizado por Nicolas Oliveira há aproximadamente 7 anos
Implementei as VIs para cálculo da distância a BoundingBox e para decidir através desse resultado se é essa subTree é promissora ou não.
A VI do nearest está pronta, porém estou tendo problemas nas variáveis globais do código. A VI é recursiva, porém elas só estão sendo atualizadas na primeira iteração. E estou com muita dificuldade em debbugar isso. Resolvendo isso, tecnicamente, a busca do nearest point pela KD-tree vai estar pronta.
Atualizado por Luciano Barreira há aproximadamente 7 anos
Nicolas Oliveira escreveu:
A implementação do Nearest Neighbor está sendo feita utilizando este [link](https://www.cs.cmu.edu/~ckingsf/bioinfo-lectures/kdtrees.pdf) como referência. Pseudo código do NN:

Onde uma da principais vantagens é o cálculo da BoundingBox (BB), de acordo com o desenho abaixo. Isso garante que sub-árvores irrelevantes não sejam visitadas.

Para o cálculo da BB foram implementadas as VIs de findMin e findMax, para achar os nós com as menores ou maiores coordenadas, respectivamente, tanto em x ou em y. Assim temos as fronteiras da BB. Caso a distância do Target a BB seja maior do que a Best_dist não existirá nenhum ponto promissor nessa BB. O que preciso ajeitar é como calcular a distância um ponto a um retângulo.
Caso eu esteja em uma sub-árvore promissora eu visitarei o melhor lado primeiro, o que também dá mais velocidade ao algoritmo.
Massa. Diferente de outras árvores, não basta pesquisar pelo elemento na KD-Tree e inserir onde ele "estaria". Mesmo assim, como explica no link, o caso médio fica O(altura da árvore).
Próximos passos:
- revisão da implementação, testes com o debugger visual. Testar com várias configurações diferentes, observando a trajetória no debugger visual. Testar uma unidade dessas é meio complicado, não consegui pensar numa forma melhor.
- benchmarking da VI. Podemos medir o quão melhorou. Dado um frame de jogo, qual o tempo de execução da VI, tanto usando o performance tools (tempo de execução de um frame) do LabView quando usando um FPS counter que nos retorne a frequência obtida, rodando apenas a VI do cálculo da trajetória em loop com um frame dummy. Imagino que o FPS counter seja uma medida mais confiável pois há o bias do waypoint cache no cálculo do RRT, que estaria vazio em uma única execução.
- teste em jogo simulado (mesmo sem navegação ainda, dá pra visualizar a trajetória e medir se a performance está pesando em um jogo dinâmico)
E após a implementação da navegação:
- teste com 1 robô real
- teste com vários robôs reais
Atualizado por Lucas Germano há aproximadamente 7 anos
- Arquivo Write Line to File.jpg Write Line to File.jpg adicionado
- Arquivo WriteLine to File.vi WriteLine to File.vi adicionado
Ontem eu e o Nicolas conseguimos debugar a KD-tree imprimindo vários valores num arquivo .txt. O debug de uma VI recursiva estava sendo muito difícil só pelos probes, principalmente se analisarmos a NearestNeighbor.vi, que é um VI que utiliza uma recursão para outras duas funções, daí utilizamos a seguinte ideia para imprimir linhas de debug no arquivo (não é uma coisa tão trivial quanto outras linguagens de programação):

A vi também está disponível em anexo.
Vimos que estava passando um valor NaN, devido a um parâmetro que estava faltando. Quando corrigimos, a KD-tree funcionou!!!
Atualizado por Luciano Barreira há aproximadamente 7 anos
Lucas Germano escreveu:
Ontem eu e o Nicolas conseguimos debugar a KD-tree imprimindo vários valores num arquivo .txt...
Parabéns. Mas o que seriam esses "vários valores" ?? Qual era o problema ?
Atualizado por Nicolas Oliveira há aproximadamente 7 anos
Valores impressos no .txt (usar como referência o pseudo código na tarefa):
- ditancia(Q,BB)
- best_dist
- NóAtual
- ditancia(Q,NóAtual)>best_dist
- best
- Q
- deepth
Percebemos que distancia de Q ao nóAtual era sempre um NaN e os problemas nos outros valores eram devidos a isso. O erro era q o Q (chamado de target na VI) não era uma variável global nem uma entrada, então n estava sendo passado seu valor certo para as chamadas recursivas.
Atualizado por Nicolas Oliveira há aproximadamente 7 anos
- Arquivo tree1.PNG tree1.PNG adicionado
- Arquivo tree.PNG tree.PNG adicionado
- Arquivo code.PNG code.PNG adicionado
A primeira implementação da KD tree segui o pseudo-código da imagem abaixo a partir de uma árvore do tipo abaixo também. Porém ao utilizá-lo no RRT o desempenho do nosso software caiu muito, levando até 1s para q uma trajetória simples fosse calculada.


Sendo assim, estudando um pouco, vi que há outra maneira de se escrever a KD-tree, descrita abaixo. Mas ela normalmente não é usada quando desejamos inserir nós, mas sim montar toda árvore de uma vez. Porém como não achei qualquer restrição para isso a implementei.


Dessa vez de uma forma bem mais organizada inclusive com o código todo comentado e utilizando um cluster para designar um nó. Porém o desempenho foi muito parecido. Para um trajetória que é calculada em 5ms pelo RRT normal, demora cerca de 0.5s utilizando-se a kdtree.
Fazendo algumas pesquisas sobre como aumentar a performance do labview vi que ele n lida bem com recursão e além disso ficou claro que ainda não sabemos bem como utilizar as ferramentas que o labview disponibiliza para isso.
Mas não sei se o labview pode chegar perto do desempenho de um biblioteca otimizada por exemplo.
Atualizado por Luiz Renault Leite Rodrigues há aproximadamente 7 anos
O desempenho pode chegar muito perto.
De que tipo de recursão está falando.
Hoje não temos nenhuma preocupação de desempenho. Por exemplo, a depuração está habilitada em todas as VIs.
VIs críticas podem ser implementadas como Inline, evitando chamadas desnecessárias e troca de contexto.
Atualizado por Nicolas Oliveira há aproximadamente 7 anos
Recursão normal, como a do código acima. Não conheço outros tipos rs. Já que li aqui que não podemos transformar VIs recursivas em inline ou subrotina. Acho q seria bom desligar o debug nessas VIs também para aumentar o desempenho.
O que ficou estranho pra mim foi que o desempenho caiu MUITO para algo que deveria melhorar.
Acho que começarmos a nos preocupar com o desempenho e fazer um levantamento das VIs críticas. Talvez diminuindo o consumo delas consigamos utilizar o RRT sem kD-tree por enquanto.
Atualizado por Nicolas Oliveira há aproximadamente 7 anos
Eu escondi os controls para não mostrar no painel frontal e tentar melhorar o desempenho. Mas segundo esse [link](http://zone.ni.com/reference/en-XX/help/371361H-01/lvconcepts/vi_execution_speed/) mesmo escondidos os controls ainda vão sendo atualizados. Vou pesquisar mais formas de melhorar a performance de VIs recursivas.
Atualizado por Gabriel Borges da Conceição há quase 7 anos
- Situação alterado de Em andamento para Resolvida
Atualizado por Gabriel Borges da Conceição há quase 7 anos
- Situação alterado de Resolvida para Fechada