Atividade #609
FechadaImplementação de KD-Tree para otimização do RRT
Adicionado por Luciano Barreira mais de 7 anos atrás. Atualizado quase 7 anos atrás.
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
CG_RangeKDtrees.pdf (394 KB) CG_RangeKDtrees.pdf | Nicolas Oliveira, 05/03/2018 19:09 h | ||
nearest_search.pdf (868 KB) nearest_search.pdf | Nicolas Oliveira, 05/03/2018 19:09 h | ||
CreateKD-Tree.PNG (49,8 KB) CreateKD-Tree.PNG | Nicolas Oliveira, 05/03/2018 20:01 h | ||
KD-Tree.PNG (41,5 KB) KD-Tree.PNG | Nicolas Oliveira, 05/03/2018 20:01 h | ||
buildkdtreeCode.PNG (103 KB) buildkdtreeCode.PNG | Nicolas Oliveira, 05/03/2018 20:01 h | ||
buildkdtree.PNG (62,3 KB) buildkdtree.PNG | Nicolas Oliveira, 05/03/2018 21:03 h | ||
KD-TreeLabView.PNG (12,5 KB) KD-TreeLabView.PNG | Nicolas Oliveira, 08/03/2018 00:03 h | ||
ex.PNG (7,86 KB) ex.PNG | Nicolas Oliveira, 08/03/2018 00:03 h | ||
NN.PNG (77,6 KB) NN.PNG | Nicolas Oliveira, 15/03/2018 01:07 h | ||
BB.PNG (30,2 KB) BB.PNG | Nicolas Oliveira, 15/03/2018 01:18 h | ||
Write Line to File.jpg (19,5 KB) Write Line to File.jpg | Lucas Germano, 16/03/2018 11:32 h | ||
WriteLine to File.vi (13,6 KB) WriteLine to File.vi | Lucas Germano, 16/03/2018 11:33 h | ||
tree.PNG (26,3 KB) tree.PNG | Nicolas Oliveira, 18/03/2018 03:56 h | ||
tree1.PNG (32,4 KB) tree1.PNG | Nicolas Oliveira, 18/03/2018 03:56 h | ||
code.PNG (33,2 KB) code.PNG | Nicolas Oliveira, 18/03/2018 03:57 h |
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á mais de 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á mais de 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