Manufaturação industrial
Internet das coisas industrial | Materiais industriais | Manutenção e reparo de equipamentos | Programação industrial |
home  MfgRobots >> Manufaturação industrial >  >> Manufacturing Technology >> Processo de manufatura

Tanque autônomo


Adicionando uma alternativa muito mais barata à referência de designs Carter e Kaya usando um conjunto Lego EV3.

Neste projeto, documentarei a construção de um veículo com esteiras feito com peças e motores Lego Technic, aprimorado com LiDAR e controlado por uma placa Jetson Nano executando o SDK Isaac mais recente. Vá para a Parte 8 ou 10 para uma demonstração completa da navegação autônoma.

O projeto consiste nos seguintes componentes:

Por que Isaac SDK e não ROS?

Por que peças Lego?

A escolha desse caminho levanta alguns desafios:

PARTE 1:Primeiros passos


1 Isaac SDK

2. Reconhecimento de voz (opcional)

3. Imagem Ev3dev

Baixe e atualize a imagem mais recente do EV3 (ev3dev-stretch) em um cartão microSD ou microSDHC. O formato MicroSDXC não é compatível com o bloco EV3.

4. Compilador cruzado ARM para ev3dev
  $ sudo apt-get install gcc-arm-linux-gnueabi g ++ - arm-linux-gnueabi  

Esta parte foi especialmente difícil de configurar corretamente. Ubuntu 18.04 (computador host e Jetson Nano) está usando GLIBC_2.28 enquanto ev3dev está usando Debian stretch e GLIBC_2.24. Qualquer coisa compilada com a configuração padrão do compilador arm-linux-gnueabi-g ++ era dependente de GLIBC_2.28 e não rodaria em EV3. A vinculação estática não funcionou, pois nada mais complexo do que um hello world estava causando segfaults. A solução que encontrei foi vincular tudo dinamicamente, exceto a biblioteca matemática. Você pode encontrar mais informações no arquivo jetson-ev3 / toolchain / CROSSTOOL. Outra solução é usar uma imagem docker do Debian 9.

5. Espaço de trabalho Jetson + EV3
  $ git clone https://github.com/andrei-ace/jetson-ev3.git  
  local_repository (
name ="com_nvidia_isaac",
path ="/ home / andrei / ml / isaac"
)
  • Edite jetson-ev3 / toolchain / CROSSTOOL e defina o caminho para o diretório onde este arquivo está.
  # edite com seu caminho para o conjunto de ferramentas 
linker_flag:"-L / home / andrei / ml / jetson-ev3 / conjunto de ferramentas"

6. Conecte Jetson Nano com EV3

Na próxima parte, postarei muitos comandos do Linux. Como há três sistemas envolvidos, irei publicá-los exatamente como seriam no meu terminal, o que significa:
  [email protected]:~ / ml / jetson-ev3 $ # this is run on my PC 
[email protected]:~ $ # this is on Jetson Nano
[e-mail protegido]:~ $ $ #esta in no EV3

Os IPs do meu Jetson Nano são 192.168.0.173 (Ethernet) e 192.168.0.218 (WiFi), então sempre que vir um comando usando esses valores substitua-os pelos seus.

Usei um cabo USB A para mini para conectar a placa Jetson ao bloco EV3 usando essas etapas.

Tente fazer o ssh no painel Jetson:
  [email protected]:~ $ ssh [email protected]  

A senha padrão é fabricante.

7. O tutorial de pingue-pongue

Isaac tem um tutorial explicando um Codelet muito simples. Eu sugiro fazer este tutorial primeiro. Ele irá apresentá-lo aos conceitos necessários para construir qualquer aplicativo em execução no Isaac.

Agora vá para o diretório jetson-ev3 / apps / ev3 / ping_pong /. Esta é uma versão modificada do tutorial anterior, com uma diferença, enviaremos o ping para o bloco EV3.

A maioria dos arquivos são familiares do tutorial anterior. Usaremos Cap’n Proto RPC para chamadas entre Jetson e EV3. Cap’n Proto é muito usado para comunicação entre vários componentes Isaac, por isso faz sentido usá-lo aqui. Para isso, precisamos de alguns novos arquivos:
  • jetson-ev3 / apps / ev3 / ping_pong / ping.capnp - define uma interface entre um cliente, que será executado no Isaac Robot Engine, e um servidor, que será executado no EV3.
  • jetson-ev3 / apps / ev3 / ping_pong / PongEv3Server.cpp este é o servidor que roda no bloco EV3
  • jetson-ev3 / apps / ev3 / ping_pong / Pong.cpp isso foi alterado para chamar o servidor Pong em execução no EV3

Compile o servidor ev3_pong:
  [email protected]:~ / ml / jetson-ev3 $ bazel build --config =ev3dev // apps / ev3 / ping_pong:ev3_pong  

Copie-o para EV3 usando scp primeiro para Jetson e depois para EV3.

Crie e implante o exemplo de pingue-pongue no Jetson:
  [email protected]:~ / ml / jetson-ev3 $  /engine/build/deploy.sh --remote_user  -p // apps / ev3 / ping_pong:ping_pong-pkg -d jetpack43 -h   

Mais informações sobre como implantar e executar seus aplicativos no Jetson aqui.

Execute os dois aplicativos:
  [email protected]:~ $ ./ev3_pong ev3dev.local:9999 
[email protected]:~ / deploy / andrei / ping_pong-pkg $. / apps / ev3 / ping_pong

Se tudo funcionar, você deve ouvir as mensagens enviadas pelo componente Ping no alto-falante do EV3.

8. Controlando um motor de Isaac

Mesmos princípios, apenas um pouco mais complexos. Usei outro dos tutoriais de Isaac para interagir com um motor EV3:

O tutorial usa uma base Segway RMP. Como não tenho um por aí ou $ 10.000 para comprar um, criei um driver que controlará os motores EV3. O código está aqui.

O servidor que roda EV3 está aqui e pode ser construído e executado com o seguinte comando:
  [email protected]:~ / ml / jetson-ev3 $ bazel build --config =ev3dev // packages / ev3 / ev3dev:ev3_control_server 
[email protegido]:~ $ ./ev3_control_server ev3dev.local:9000

Usei o joystick virtual da Visão conforme explicado aqui.

9.DifferentialBase para EV3

O servidor Ev3ControlServer responderá a 2 chamadas:
  • command (cmd:Control) - toma as velocidades lineares e angulares como parâmetros e controla os dois motores para atingir as velocidades solicitadas
  • state () -> (state:Dynamics); - retorna as velocidades lineares e angulares reais do robô

A cinemática é explicada com mais detalhes aqui e aqui.

Eu usei o aplicativo de amostra proporcional_control_cpp para conduzir o robô por 1 me relatar os dados de Odometria do EV3 (velocidades linear e angular) em pulsos rotativos (contagens de tacômetro) por segundo. Usando a distância de viagem calculada (por Isaac) e medindo a distância real, cheguei a uma constante para ajustar os valores relatados para que correspondam aos resultados reais. Funcionou bem e os resultados foram reproduzíveis muitas vezes e não apenas em linhas retas. Você também pode calcular esses valores usando o raio da roda (ou trilha, em nosso caso).

Parte 2:Construindo o robô


A base é muito semelhante ao EV3 Track3r da Lego, um dos modelos oficiais do kit EV3:https://www.lego.com/biassets/bi/6124045.pdf

O caso do Jetson Nano é aqui:https://github.com/3D-printable-lego-technic/PELA-blocks

Parte 3:Isaac Apps


Um aplicativo Isaac é composto por três partes principais:
  • gráfico - nós:esta parte define todos os componentes que fazem o aplicativo. Um nó também pode ser outro gráfico definido em outro arquivo. O nó “voice_detection” do exemplo é um subgráfico.
  • gráfico - bordas:Esta parte define o fluxo de mensagens entre os nós. Uma borda tem uma origem e um destino. Por exemplo, o comando detectado do nó “voice_detection” (subgrafo) será enviado ao componente que gera metas.
  • configuração - esta parte configura os nós do gráfico

Aplicativo de exemplo:
  {
"name":"voice_control",
"modules":[
"// apps / ev3 / voice_control:voice_control_goal_generator ",
" @ com_nvidia_isaac // pacotes / navegação ",
" @ com_nvidia_isaac // pacotes / planejador "
],
" config_files ":[
" apps / ev3 / voice_control / model / isaac_vcd_model.metadata.json "
],
" config ":{
" 2d_ev3.ev3_hardware.ev3 ":{
" isaac.Ev3Driver ":{
" endereço ":" ev3dev.local ",
" porta ":9000
}
},
" navigation.imu_odometry.odometry ":{
"DifferentialBaseWheelImuOdometry":{
"use_imu":false
}
},
"commander.robot_remote":{
"isaac.navigation.RobotRemoteControl ":{
" angular_speed_max ":0,6,
" linear_speed_max ":0,3
}
},
" websight ":{
" WebsightServer ":{
"webroot":"external / com_nvidia_isaac / packages / sight / webroot",
"ui_config":{
"windows":{
"Detecção de comando de voz":{
"renderer":"plot" ,
"escurece":{
"largura":400,
"altura":200
},
"canais":[
{
"nome":"voice_control / voice_detection.voice_command_detector / isaac.audio.VoiceCommandConstruction / voice_command_id",
"ativo":verdadeiro
}
]
}
}
}
}
},
"navigation.shared_robot_model":{
"SphericalRobotShapeComponent":{
"círculos":[
{"centro":[0,0, 0,0], "raio":0,075},
{"centro":[0,02, 0,03464], "raio":0,055},
{"centro":[0,02, -0,03464], "raio":0,055},
{"centro":[-0,04, 0,0], "raio":0,055},
{"centro":[0,0525, 0,09093 ], "raio":0,035},
{"centro":[0,0525, -0,09093], "raio":0,035},
{"centro":[-0,105, 0,0], "raio ":0,035}
]
}
},
" navigation.control.lqr ":{
" isaac.planner.DifferentialBaseLqrPlanner ":{
"manual_mode_channel":"commander.robot_remote / isaac.navigation.RobotRemoteControl / manual_mode"
}
},
"navigation.control.control":{
"isaac.planner.DifferentialBaseControl":{
"manual_mode_channel":"commander.robot_remote / isaac.navigation.RobotRemoteControl / manual_mode"
}
}
},
"gráfico":{
"nós":[
{
"nome":"voz_control_componentes",
"componentes":[
{
"nome":"message_ledger",
"tipo":"isaac ::alice ::MessageLedger"
},
{
"nome":"goal_generator",
"tipo":"isaac ::VoiceControlGoalGenerator"
}
]
},
{
"name":"voice_detection",
"subgraph":"apps / ev3 / voice_control / voice_command_detection.subgraph.json"
},
{
"nome":"2d_ev3",
"subgráfico":"apps / ev3 / 2d_ev3.subgraph.json"
},

{
"nome":"navegação",
"subgraph":"@ com_nvidia_isaac // packages / navigation / apps / different_base_navigation.subgraph.json"
},
{
"name":"commander",
"subgráfico":"@ com_nvidia_isaac // pacotes / navegação / apps / diferencial_base_commander.subgraph.json "
}
],
" bordas ":[
{
" fonte ":" voice_detection.subgraph / interface /ected_command " ,
"target":"voice_control_components / goal_generator /ected_command"
},
{
"source":"voice_control_components / goal_generator / goal",
"target" :"navigation.subgraph / interface / goal"
},
{
"source":"2d_ev3.subgraph / interface / base_state",
"target":"navegação. subgrafo / interface / estado "
},
{
" fonte ":" navigation.subgraph / interface / command ",
" target ":" commander.subgraph / interface / controle "
},
{
" source ":" commander.subgraph / interface / command ",
" target ":" 2d_ev3.subgraph / interface / base_command "
},
{
"fonte":"2d_ev3.subgraph / interface / flatscan",
"target":"navigation.subgraph / interface / flatscan_for_localization"
},
{
"fonte":"2d_ev3.subgraph / interface / flatscan",
"destino":"navegação. subgrafo / interface / flatscan_for_obstacles "
}
]
}
}

Subgrafo de exemplo:
  {
"modules":[
"@ com_nvidia_isaac // packages / audio",
"@ com_nvidia_isaac // packages / ml:tensorflow "
],
" gráfico ":{
" nós ":[
{
" nome ":" subgrafo ",
" componentes ":[
{
" nome ":" message_ledger ",
" tipo ":" isaac ::alice ::MessageLedger "
},
{
"nome":"interface",
"tipo":"isaac ::alice ::Subgrafo"
}
]
},
{
"nome":"audio_capture",
"componentes":[
{
"nome":"ml",
"tipo":"isaac ::alice ::MessageLedger "
},
{
" nome ":" isaac.audio.AudioCapture ",
" type ":" isaac ::audio ::AudioCapture "
}
]
},
{
"nome":"voice_command_detector",
"componentes":[
{
" nome ":" ml ",
" tipo ":" isaac ::alice ::MessageLedger "
},
{
" nome ":" isaac.audio.VoiceCommandFeatureExtraction " ,
"tipo":"isaac ::audio ::VoiceCommandFeatureExtraction"
},
{
" nome ":" isaac.ml.TensorflowInference ",
" tipo ":" isaac ::ml ::TensorflowInference "
},
{
" nome ":" isaac. audio.VoiceCommandConstruction ",
" type ":" isaac ::audio ::VoiceCommandConstruction "
}
]
}
],
" bordas " :[
{
"source":"audio_capture / isaac.audio.AudioCapture / audio_capture",
"target":"voice_command_detector / isaac.audio.VoiceCommandFeatureExtraction / audio_packets"
},
{
"source":"voice_command_detector / isaac.audio.VoiceCommandFeatureExtraction / feature_tensors",
"target":"voice_command_detector / isaac.ml.TensorflowInference / input_tensors"
},
{
"source":"voice_command_detector / isaac.ml.TensorflowInference / output_tensors",
"target":"voice_command_detector / isaac.audio.VoiceCommandConstruction / keyword_probabilities"
},
{
"source":"voice_command_detector / isaac.audio.VoiceCommandConstruction /usted_command",
"target":"subgraph / interface /ected _command "
}
]
},
" config ":{
" audio_capture ":{
" isaac.audio.AudioCapture ":{
"sample_rate":16000,
"num_channels":1,
"audio_frame_in_milliseconds":100,
"ticks_per_frame":5
}
},
"voice_command_detector":{
"isaac.audio.VoiceCommandFeatureExtraction":{
"audio_channel_index":0,
"minimum_time_between_inferences":0,1
},
"isaac.ml.TensorflowInference":{
"model_file_path":"apps / ev3 / voice_control / model / isaac_vcd_model.pb",
"config_file_path":"apps / ev3 / voice_control / model / isaac_vcd_config. pb "
},
" isaac.audio.VoiceCommandConstruction ":{
" command_list ":[
" jetson ",
" jetson left ",
"jetson right"
],
"command_ids":[0, 1, 2],
"max_frames_allowed_after_keyword_detected":14
}
}
}
}

Um subgráfico pode ser reutilizado em muitos aplicativos. Na verdade, a pilha de navegação do isaac é usada como um subgráfico.

Parte 4:Executando Isaac Apps no EV3


O driver (jetson-ev3 / packages / ev3 / BUILD) responde aos mesmos comandos que o driver básico do Segway RMP. Isso significa que funcionará com muitos aplicativos que funcionam no Kaya ou Carter, tornando-o uma terceira opção e muito mais barato!

Adaptei alguns dos aplicativos criados para mostrar os bots Carter e Kaya:
  • app de joystick - controla um robô DifferentialBase com um joystick. Possui um LiDAR para geração de mapas locais
  • gmapping distribuído:ev3 e host do robô Kaya - permite criar um GMap usando o robô EV3 e YDLIDAR X4.
  • navegação completa - adicionei subgráficos para hardware e navegação 2D para o robô EV3 para que possam ser usados ​​por outros aplicativos tão facilmente quanto usar Carter ou Kaya.

Parte 5:Odometria


Para correr no modo autônomo é importante ter uma boa odometria. Isso é usado para estimar a posição do robô ao longo do tempo. Vamos ajustar usando o aplicativo ev3:
  [email protected]:~ / ml / jetson-ev3 $ ./engine/build/deploy.sh --remote_user andrei -p // apps / ev3:ev3 -pkg -d jetpack43 -h 192.168.0.173 

[email protegido]:~ $ brickrun ./ev3_control_server ev3dev.local:9000

[email protegido]:~ / deploy / andrei / ev3-pkg $ ./apps/ev3/ev3 --graph ./apps/assets/maps/map.graph.json --config ./apps/assets/maps/map.config.json

Precisamos estimar duas coisas:
  • velocidade linear
  • velocidade angular

As fórmulas para velocidades lineares e angulares são:

Encontrar a velocidade angular é fácil:é a diferença entre os motores direito e esquerdo dividida pelo comprimento base.

Encontrar a velocidade linear é um pouco mais complexo. Temos 3 casos:
  • quando ambas as velocidades do motor são iguais - a velocidade linear é igual à velocidade certa (e à esquerda)
  • quando a velocidade do motor esquerdo é oposta à velocidade do motor direito, a velocidade linear é 0, o tanque girará no lugar
  • quando a velocidade do motor esquerdo é 0 (caso descrito à direita). A velocidade linear é a metade da velocidade certa (o centro do robô se move em um arco menor).

Experimento de velocidade angular:

Usaremos o controle manual para girar o robô 360 graus no lugar. Isso é feito movendo os motores esquerdo e direito em velocidades opostas. Conhecendo as velocidades de ambos os motores, podemos calcular a velocidade angular.

Vamos tentar:

Experimento de velocidades angulares e lineares:

Vou dar uma volta com o tanque e, no final, tento colocá-lo de volta no ponto de partida. Os dados da odometria devem ser o mais próximo possível de 0 no final se estivermos computando as velocidades corretamente.

Parte 6:Reunindo tudo


Ok, então chegamos até agora apenas para ter um tanque RC caro? Não, podemos usar todas as diferentes peças de Isaac agora. Emitir comandos de voz, por exemplo, para fazer o robô se mover de forma autônoma. Verifique o voice_control para obter um exemplo disso.

Ele usa as joias de áudio e aprendizado de máquina de Isaac. O que é uma joia? Conforme afirmado no manual:“GEMs:uma coleção de algoritmos de robótica do planejamento à percepção, a maioria deles com aceleração por GPU.”

Treinei meu próprio RNN seguindo as etapas explicadas neste tutorial. Apenas certifique-se de ter muitos dados, especialmente para o caso de palavras-chave desconhecidas / silêncio / ruído aleatório.

Eu treinei o meu para reconhecer 3 palavras:“jetson”, “esquerda” e “direita”. Você pode encontrar o modelo salvo aqui. Com estas 3 palavras podemos compor 2 comandos:“jetson left” e “jetson right”.

A parte de detecção é descrita aqui, em seu próprio subgrafo, pronta para ser usada e reutilizada.

Basicamente, o que ele faz é ouvir o microfone e se um dos comandos for captado, ele emitirá um voice_command_id. Para isso, usa o RNN previamente treinado.

Podemos pegar esseected_command e passá-lo para nosso próprio Codelet:
  {
"source":"voice_detection.subgraph / interface /ected_command",
"target":"voice_control_components / goal_generator /ected_command"
}

a partir do Codelet, podemos gerar uma meta e publicá-la:
  auto proto =rx_detected_command (). getProto (); 
int id =proto.getCommandId ();
auto goal_proto =tx_goal (). initProto ();
goal_proto.setStopRobot (true);
goal_proto.setTolerance (0.1);
goal_proto.setGoalFrame ("robô");
ToProto (Pose2d ::Rotation ( 90), goal_proto.initGoal ());
tx_goal (). Publish ();

Isso define uma meta de girar o robô para a esquerda em 90 graus. Podemos definir objetivos diferentes em quadros diferentes. Pode ter sido para ir para uma coordenada no quadro “mundo”, como as coordenadas da cozinha. Pode ter sido definindo um Pose2 ::Translate (1.0, 0) na estrutura do robô para avançar o robô com 1 metro.

E a partir daí passamos a meta para o Planejador Global.
  {
"source":"voice_control_components / goal_generator / goal",
"target":"navigation.subgraph / interface / goal"
}

Onde toda a magia acontece:

Infelizmente ele só funcionará no modo de 10 W, não 5 W, o que é um pouco demais para a minha bateria. No modo 5W, a inferência demora muito:

Eu tentei com RNNs menores e aumentando de 2 núcleos de CPU disponíveis (nvpmodel -m 1) para 3, mas não ajudou muito. Ele diminuiu o tempo para 30 ms para a inferência, ainda muito longo para resultados precisos.

Parte 7:Mapeamento


Para criar um mapa, precisamos executar uma instância de Isaac no Jetson e uma no computador host. O mapeamento consome muitos recursos, mais do que o Jetson Nano pode suportar.
  [email protected]:~ / ml / jetson-ev3 $ ./engine/build/deploy.sh --remote_user andrei -p // apps / ev3:gmapping_distributed_ev3 -pkg -d jetpack43 -h 192.168.0.218 

[email protegido]:~ / deploy / andrei / gmapping_distributed_ev3-pkg $ ./apps/ev3/gmapping_distributed_ev3

[email protegido]:~ / ml / jetson-ev3 $ bazel executar apps / ev3:gmapping_distributed_host

Não se esqueça de alterar o arquivo apps / ev3 / gmapping_distributed_host.app.json com seu IP Jetson:
  "tcp_subscriber":{
"isaac.alice.TcpSubscriber":{
"porta":5000,
"host" :"192.168.0.218"
}
}

Fonte:Tanque Autônomo

Processo de manufatura

  1. Autonomous Driving AI para Donkey Car Garbage Collector
  2. Tanque Dia 23:Alcance e rumo
  3. Robô autônomo quadrúpede JQR
  4. Preparando-se para um futuro autônomo
  5. Modelo de tanque de batalha usinado em CNC
  6. Robô autônomo abre portas
  7. Montadoras Autônomas
  8. O que é um tanque de reserva?
  9. Dicas de segurança para soldagem de tanques de combustível
  10. Como consertar um vazamento de gás no meu carro?