Tanque autónomo
Agregar una alternativa mucho más barata a la referencia de diseños de Carter y Kaya usando un juego Lego EV3.
En este proyecto, documentaré la construcción de un vehículo de orugas hecho con piezas y motores de Lego Technic, mejorado con LiDAR y controlado por una placa Jetson Nano que ejecuta el último Isaac SDK. Ir a la Parte 8 o 10 para una demostración completa de la navegación autónoma.
El proyecto consta de los siguientes componentes:
- Una NVIDIA Jetson Nano tablero de desarrollo:ejecuta Isaac Robot Engine
- Un bloque EV3 - controla los motores (incluido en el kit de robot EV3 31313 ) )
- Base con orugas:fabricada con piezas de Lego Technic y dos motores grandes (todo lo necesario se proporciona en el kit de robot EV3 31313 ). )
- YDLIDAR x4 LiDAR
- Pixy2 Cámara para visión
¿Por qué Isaac SDK y no ROS?
- Hay muchos tutoriales para ROS (Robot Operating System) pero casi ninguno para Isaac (además de los del SDK)
- Parece ser una mejor opción para Jetson Nano (fue creado para esta familia de hardware)
- Algoritmos robóticos avanzados desde la planificación hasta la percepción, la mayoría de ellos acelerados por GPU . Ésta es una parte importante. Sin la aceleración de la GPU, Jetson Nano no es diferente de una placa Raspberry Pi 4
- IsaacSim Unity3D es más fotorrealista que Gazebo, lo que mejorará los resultados pasando de la simulación a la realidad
¿Por qué piezas de Lego?
- Tengo muchos de ellos 🙂
- Las piezas son de alta calidad (buenos servos)
- No tengo una impresora 3D (le pedí a un amigo que imprimiera una funda Jetson Nano compatible con Lego, pero esa es la única parte impresa en 3D)
- No se requiere soldadura
Elegir este camino plantea algunos desafíos:
- El hardware de Lego no es compatible con Isaac SDK. Solo existen dos robots de referencia:Carter y Kaya. Ni siquiera el JetBot es compatible.
- Isaac SDK solo puede funcionar con una cantidad limitada de componentes de hardware
- Compilación para 3 destinos (x86-64, arm64 y armv5tejl)
- No todo es de código abierto
PARTE 1:Empezando
1. SDK de Isaac
- Siga los pasos que se describen aquí
- Intente ejecutar algunos de los ejemplos proporcionados en Isaac SDK para verificar si todo funciona
- ¡¡¡Asegúrate de usar la versión 2019.3 SDK !!!
2. Reconocimiento de voz (opcional)
- Instale CUDA 10.0, CUDNN 7.6.3 y TensorRT 6.0
- Instale TensorFlow 1.15.0 (acelerado por hardware)
3. Imagen de Ev3dev
Descargue y actualice la imagen más reciente para EV3 (ev3dev-stretch) en una tarjeta microSD o microSDHC. El formato MicroSDXC no es compatible con el bloque EV3.
4. Compilador cruzado ARM para ev3dev
$ sudo apt-get install gcc-arm-linux-gnueabi g ++ - arm-linux-gnueabi
Esta parte fue especialmente difícil de configurar correctamente. Ubuntu 18.04 (computadora host y Jetson Nano) usa GLIBC_2.28 mientras que ev3dev usa Debian stretch y GLIBC_2.24. Todo lo compilado con la configuración predeterminada del compilador arm-linux-gnueabi-g ++ dependía de GLIBC_2.28 y no se ejecutará en EV3. La vinculación estática no funcionó ya que nada más complejo que un hola mundo estaba causando segfaults. La solución que encontré fue vincular dinámicamente todo excepto la biblioteca matemática. Puede encontrar más información en el archivo jetson-ev3 / toolchain / CROSSTOOL. Otra solución es utilizar una imagen de Docker de Debian 9.
5. Espacio de trabajo Jetson + EV3
$ git clone https://github.com/andrei-ace/jetson-ev3.git
- Edite jetson-ev3 / WORKSPACE y establezca la ruta al SDK de Isaac.
local_repository (
name ="com_nvidia_isaac",
path ="/ home / andrei / ml / isaac"
)
- Edite jetson-ev3 / toolchain / CROSSTOOL y establezca la ruta al directorio donde se encuentra este archivo.
# edite con su ruta a la cadena de herramientas
linker_flag:"-L / home / andrei / ml / jetson-ev3 / toolchain"
6. Conecte Jetson Nano con EV3
En la siguiente parte, publicaré muchos comandos de Linux. Debido a que hay tres sistemas involucrados, los publicaré exactamente como se verían en mi terminal, es decir:
[email protected]:~ / ml / jetson-ev3 $ #this is run on my PC
[email protected]:~ $ #this is on Jetson Nano
[correo electrónico protegido]:~ $ $ # esto en EV3
Las direcciones IP de mi Jetson Nano son 192.168.0.173 (Ethernet) y 192.168.0.218 (WiFi), así que siempre que vea un comando que use esos valores reemplácelos con los suyos.
Usé un cable USB A a mini para conectar la placa Jetson con el bloque EV3 siguiendo estos pasos.
Intente ssh desde el tablero de Jetson:
[email protected]:~ $ ssh [email protected]
La contraseña predeterminada es maker.
7. El tutorial de ping-pong
Isaac tiene un tutorial que explica un Codelet muy simple. Sugiero hacer este tutorial primero. Le presentará los conceptos necesarios para crear cualquier aplicación que se ejecute en Isaac.
Ahora vaya al directorio jetson-ev3 / apps / ev3 / ping_pong /. Esta es una versión modificada del tutorial anterior, con un giro, enviaremos el ping al bloque EV3.
La mayoría de los archivos son familiares del tutorial anterior. Usaremos Cap'n Proto RPC para llamadas entre Jetson y EV3. Cap'n Proto se usa mucho para la comunicación entre varios componentes de Isaac, por lo que tiene sentido usarlo aquí. Para esto necesitamos algunos archivos nuevos:
- jetson-ev3 / apps / ev3 / ping_pong / ping.capnp:esto define una interfaz entre un cliente, que se ejecutará en Isaac Robot Engine, y un servidor, que se ejecutará en EV3.
- jetson-ev3 / apps / ev3 / ping_pong / PongEv3Server.cpp este es el servidor que se ejecuta en el bloque EV3
- jetson-ev3 / apps / ev3 / ping_pong / Pong.cpp esto se cambió para llamar al servidor Pong que se ejecuta en EV3
Compile el servidor ev3_pong:
[correo electrónico protegido]:~ / ml / jetson-ev3 $ bazel build --config =ev3dev // apps / ev3 / ping_pong:ev3_pong
Cópielo en EV3 usando scp primero en Jetson y luego en EV3.
Cree e implemente el ejemplo de ping-pong en Jetson:
[email protected]:~ / ml / jetson-ev3 $ /engine/build/deploy.sh --remote_user -p // apps / ev3 / ping_pong:ping_pong-pkg -d jetpack43 -h
Más información sobre cómo implementar y ejecutar sus aplicaciones en Jetson aquí.
Ejecute ambas aplicaciones:
[email protected]:~ $ ./ev3_pong ev3dev.local:9999
[email protected]:~ / deploy / andrei / ping_pong-pkg $. / apps / ev3 / ping_pong
Si todo funcionó, debería escuchar los mensajes enviados por el componente Ping al altavoz de EV3.
8.Controlar un motor de Isaac
Mismos principios, solo que un poco más complejos. Usé otro de los tutoriales de Isaac para interactuar con un motor EV3:
El tutorial utiliza una base de Segway RMP. Como no tengo uno por ahí o 10000 $ para comprar uno, creé un controlador que controlará los motores EV3. El código está aquí.
El servidor que se ejecuta en EV3 está aquí y se puede construir y ejecutar con el siguiente comando:
[email protected]:~ / ml / jetson-ev3 $ bazel build --config =ev3dev // packages / ev3 / ev3dev:ev3_control_server
[email protegido]:~ $ ./ev3_control_server ev3dev.local:9000
Usé el joystick virtual de Sight como se explica aquí.
9.DifferentialBase para EV3
El servidor Ev3ControlServer responderá a 2 llamadas:
- comando (cmd:Control):toma velocidades lineales y angulares como parámetros y controla ambos motores para lograr las velocidades solicitadas
- estado () -> (estado:dinámica); - devuelve las velocidades reales lineales y angulares del robot
La cinemática se explica con más detalle aquí y aquí.
Usé la aplicación de muestra proporcional_control_cpp para conducir el robot 1 my reportar los datos de Odometría (velocidades lineales y angulares) de EV3 en pulsos rotatorios (conteos de tacógrafos) por segundo. Usando la distancia de viaje calculada (por Isaac) y midiendo la distancia real, se me ocurrió una constante para ajustar los valores informados para que coincidan con los resultados reales. Esto funcionó bien y los resultados fueron reproducibles muchas veces y no solo en línea recta. También puede calcular estos valores utilizando el radio de la rueda (o pista en nuestro caso).
Parte 2:Construyendo el robot
La base es muy similar al EV3 Track3r de Lego, uno de los modelos oficiales del kit EV3:https://www.lego.com/biassets/bi/6124045.pdf
El caso de Jetson Nano es de aquí:https://github.com/3D-printable-lego-technic/PELA-blocks
Parte 3:Aplicaciones de Isaac
Una aplicación de Isaac consta de tres partes principales:
- gráfico - nodos:esta parte define todos los componentes que componen la aplicación. Un nodo también puede ser otro gráfico definido en otro archivo. El nodo "voice_detection" del ejemplo es un subgrafo.
- gráfico - bordes:esta parte define el flujo de mensajes entre los nodos. Un borde tiene una fuente y un objetivo. Por ejemplo, el comando detectado desde el nodo "voice_detection" (subgrafo) se enviará al componente que genera los objetivos.
- configuración:esta parte configura los nodos del gráfico
Aplicación de ejemplo:
{
"nombre":"voice_control",
"módulos":[
"// apps / ev3 / voice_control:voice_control_goal_generator ",
" @ com_nvidia_isaac // paquetes / navegación ",
" @ com_nvidia_isaac // paquetes / planificador "
],
" config_files ":[
" apps / ev3 / voice_control / model / isaac_vcd_model.metadata.json "
],
" config ":{
" 2d_ev3.ev3_hardware.ev3 ":{
" isaac.Ev3Driver ":{
" address ":" ev3dev.local ",
" puerto ":9000
}
},
" navigation.imu_odometry.odometry ":{
"DifferentialBaseWheelImuOdometry":{
"use_imu":falso
}
},
"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":{
"Detección de comandos de voz":{
"renderizador":"trama" ,
"atenúa":{
"ancho":400,
"alto":200
},
"canales":[
{
"nombre":"voice_control / voice_detection.voice_command_detector / isaac.audio.VoiceCommandConstruction / voice_command_id",
"activo":verdadero
}
]
}
}
}
}
},
"navigation.shared_robot_model":{
"SphericalRobotShapeComponent":{
"círculos":[
{"centro":[0.0, 0.0], "radio":0.075},
{"centro":[0.02, 0.03464], "radio":0.055},
{"centro":[0.02, -0.03464], "radio":0.055},
{"centro":[-0.04, 0.0], "radio":0.055},
{"centro":[0.0525, 0.09093 ], "radio":0.035},
{"centro":[0.0525, -0.09093], "radio":0.035},
{"centro":[-0.105, 0.0], "radio ":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":{
"nodos":[
{
"nombre":"componentes de control de voz",
"componentes":[
{
"nombre":"mensaje_ledger",
"tipo":"isaac ::alice ::MessageLedger"
},
{
"name":"goal_generator",
"type":"isaac ::VoiceControlGoalGenerator"
}
]
},
{
"name":"voice_detection",
"subgraph":"apps / ev3 / voice_control / voice_command_detection.subgraph.json"
},
{
"nombre":"2d_ev3",
"subgraph":"apps / ev3 / 2d_ev3.subgraph.json"
},
{
"nombre":"navegación",
"subgraph":"@ com_nvidia_isaac // packages / navigation / apps / diferencial_base_navigation.subgraph.json"
},
{
"name":"commander",
"subgraph":"@ com_nvidia_isaac // paquetes / navegación / apps / diferencial_base_commander.subgraph.json "
}
],
" bordes ":[
{
" fuente ":" voice_detection.subgraph / interface / installed_command " ,
"target":"voice_control_components / goal_generator / selected_command"
},
{
"source":"voice_control_components / goal_generator / goal",
"target" :"navigation.subgraph / interface / goal"
},
{
"source":"2d_ev3.subgraph / interface / base_state",
"target":"navegación. subgraph / interface / state "
},
{
" source ":" navigation.subgraph / interface / command ",
" target ":" commander.subgraph / interface / control "
},
{
" fuente ":" commander.subgraph / interface / command ",
" target ":" 2d_ev3.subgraph / interface / base_command "
},
{
"fuente":"2d_ev3.subgraph / interface / flatscan",
"target":"navigation.subgraph / interface / flatscan_for_localization"
},
{
"fuente":"2d_ev3.subgraph / interface / flatscan",
"target":"navegación. subgraph / interface / flatscan_for_obstacles "
}
]
}
}
Subgrafo de ejemplo:
{
"módulos":[
"@ com_nvidia_isaac // paquetes / audio",
"@ com_nvidia_isaac // paquetes / ml:tensorflow "
],
" gráfico ":{
" nodos ":[
{
" nombre ":" subgrafo ",
" componentes ":[
{
" nombre ":" mensaje_ledger ",
" tipo ":" isaac ::alice ::MessageLedger "
},
{
"nombre":"interfaz",
"tipo":"isaac ::alice ::Subgraph"
}
]
},
{
"nombre":"audio_capture",
"componentes":[
{
"nombre":"ml",
"tipo":"isaac ::alice ::MessageLedger "
},
{
" nombre ":" isaac.audio.AudioCapture ",
" tipo ":" isaac ::audio ::AudioCapture "
}
]
},
{
"nombre":"voice_command_detector",
"componentes":[
{
" nombre ":" ml ",
" tipo ":" isaac ::alice ::MessageLedger "
},
{
" nombre ":" isaac.audio.VoiceCommandFeatureExtraction " ,
"tipo":"isaac ::audio ::VoiceCommandFeatureExtraction"
},
{
" nombre ":" isaac.ml.TensorflowInference ",
" tipo ":" isaac ::ml ::TensorflowInference "
},
{
" nombre ":" isaac. audio.VoiceCommandConstruction ",
" tipo ":" isaac ::audio ::VoiceCommandConstruction "
}
]
}
],
" bordes " :[
{
"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 / installed_command",
"target":"subgraph / interface / installed _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,
"mínimo_tiempo_entre_inferencias":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
}
}
}
}
Un subgrafo se puede reutilizar en muchas aplicaciones. De hecho, la pila de navegación de isaac se usa como un subgrafo.
Parte 4:Ejecución de Isaac Apps en EV3
El controlador (jetson-ev3 / packages / ev3 / BUILD) responde a los mismos comandos que el controlador básico del Segway RMP. Eso significa que funcionará con muchas aplicaciones que funcionan en Kaya o Carter, ¡lo que la convierte en una tercera opción y mucho más barata!
Adapté algunas de las aplicaciones creadas para mostrar los bots de Carter y Kaya:
- Aplicación de joystick:controla un robot DifferentialBase con un joystick. Tiene un LiDAR para generar mapas locales
- gmapping distribuido:ev3 y host del robot Kaya - esto permite crear un GMap usando el robot EV3 y YDLIDAR X4.
- navegación completa:agregué subgráficos para el hardware y la navegación 2D para el robot EV3 para que puedan ser utilizados por otras aplicaciones tan fácil como usar Carter o Kaya.
Parte 5:Odometría
Para correr en modo autónomo es importante tener una buena odometría. Se utiliza para estimar la posición del robot a lo largo del tiempo. Ajustémoslo con la aplicación ev3:
[correo electrónico protegido]:~ / ml / jetson-ev3 $ ./engine/build/deploy.sh --remote_user andrei -p // apps / ev3:ev3 -pkg -d jetpack43 -h 192.168.0.173
[correo electrónico protegido]:~ $ brickrun ./ev3_control_server ev3dev.local:9000
[correo electrónico protegido]:~ / deploy / andrei / ev3-pkg $ ./apps/ev3/ev3 --graph ./apps/assets/maps/map.graph.json --config ./apps/assets/maps/map.config.json
Necesitamos estimar dos cosas:
- velocidad lineal
- velocidad angular
Las fórmulas para velocidades lineales y angulares son:
Encontrar la velocidad angular es fácil:es la diferencia de los motores derecho e izquierdo dividida por la longitud base.
Encontrar la velocidad lineal es un poco más complejo. Tenemos 3 casos:
- cuando ambas velocidades del motor son iguales, la velocidad lineal es igual a la velocidad derecha (y la velocidad izquierda)
- cuando la velocidad del motor izquierdo es opuesta a la velocidad del motor derecho, la velocidad lineal es 0, el tanque girará en su lugar
- cuando la velocidad del motor izquierdo es 0 (el caso descrito a la derecha). La velocidad lineal es la mitad de la velocidad correcta (el centro del robot se desplaza en un arco más pequeño).
Experimento de velocidad angular:
Usaremos el control manual para rotar el robot 360 grados en su lugar. Esto se hace moviendo los motores izquierdo y derecho a velocidades opuestas. Conociendo las velocidades de ambos motores podemos calcular la velocidad angular.
Intentémoslo:
Experimento de velocidades angulares y lineales:
Conduciré el tanque y, al final, intentaré volver a colocarlo en la ubicación inicial. Los datos de odometría deberían estar lo más cerca posible de 0 al final si estamos calculando las velocidades correctamente.
Parte 6:Reunir todo
Ok, ¿llegamos tan lejos solo para tener un costoso tanque RC? No, podemos usar todas las diferentes partes de Isaac ahora. Emitir comandos de voz por ejemplo y conseguir que el robot se mueva de forma autónoma. Compruebe el control de voz para ver un ejemplo de esto.
Utiliza las gemas de audio y aprendizaje automático de Isaac. ¿Qué es una joya? Como se indica en el manual:"GEM:una colección de algoritmos robóticos desde la planificación hasta la percepción, la mayoría de ellos acelerados por GPU".
Entrené a mi propio RNN siguiendo los pasos explicados en este tutorial. Solo asegúrese de tener una gran cantidad de datos, especialmente para el caso de palabras clave desconocidas / silencio / ruido aleatorio.
Entrené el mío para reconocer 3 palabras:"jetson", "izquierda" y "derecha". Puede encontrar el modelo guardado aquí. Con estas 3 palabras podemos componer 2 comandos:“jetson left” y “jetson right”.
La parte de detección se describe aquí, en su propio subgrafo, lista para ser utilizada y reutilizada.
Básicamente, lo que hace es escuchar el micrófono y, si se capta uno de los comandos, generará un voice_command_id. Utiliza el RNN previamente entrenado para eso.
Podemos tomar ese comando_detectado y pasarlo a nuestro propio Codelet:
{
"source":"voice_detection.subgraph / interface / installed_command",
"target":"voice_control_components / goal_generator / installed_command"
}
desde el Codelet podemos generar un Goal y publicarlo:
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 ("robot");
ToProto (Pose2d ::Rotación ( 90), goal_proto.initGoal ());
tx_goal (). Publish ();
Esto establece el objetivo de girar el robot hacia la izquierda 90 grados. Podemos establecer diferentes objetivos en diferentes marcos. Podría haber sido ir a una coordenada en el marco del "mundo", como las coordenadas de la cocina. Podría haber estado configurando un Pose2 ::Translate (1.0, 0) en el marco del robot para hacer avanzar al robot con 1 metro.
Y de ahí pasamos el objetivo al Global Planner.
{
"source":"voice_control_components / goal_generator / goal",
"target":"navigation.subgraph / interface / goal"
}
Donde ocurre toda la magia:
Desafortunadamente, solo funcionará en modo de 10 W, no de 5 W, lo cual es demasiado para mi batería. En el modo de 5 W, la inferencia tarda demasiado:
Intenté con RNN más pequeños y aumenté de 2 núcleos de CPU disponibles (nvpmodel -m 1) a 3, pero no ayudó mucho. Disminuyó el tiempo a 30 ms para la inferencia, aún demasiado tiempo para obtener resultados precisos.
Parte 7:Mapeo
Para crear un mapa, necesitamos ejecutar una instancia de Isaac en Jetson y otra en la computadora host. El mapeo requiere muchos recursos, más de los que Jetson Nano puede manejar.
[correo electrónico protegido]:~ / ml / jetson-ev3 $ ./engine/build/deploy.sh --remote_user andrei -p // apps / ev3:gmapping_distributed_ev3 -pkg -d jetpack43 -h 192.168.0.218
[correo electrónico protegido]:~ / deploy / andrei / gmapping_distributed_ev3-pkg $ ./apps/ev3/gmapping_distributed_ev3
[correo electrónico protegido]:~ / ml / jetson-ev3 $ bazel ejecutar aplicaciones / ev3:gmapping_distributed_host
No olvide cambiar el archivo apps / ev3 / gmapping_distributed_host.app.json con su IP de Jetson:
"tcp_subscriber":{
"isaac.alice.TcpSubscriber":{
"puerto":5000,
"host" :"192.168.0.218"
}
}
Fuente:Autonomous Tank
Proceso de manufactura
- AI de conducción autónoma para el recolector de basura de burro
- Día 23 del tanque:alcance y rumbo
- Robot autónomo cuadrúpedo JQR
- Preparándose para un futuro autónomo
- Modelo de tanque de batalla mecanizado por CNC
- Robot autónomo abre puertas
- Ensambladores Autónomos Ensamble
- ¿Qué es un tanque de reserva?
- Consejos de seguridad para la soldadura de tanques de combustible
- ¿Cómo arreglo una fuga de gasolina en mi auto?
- La Tecnología de los Vehículos Autónomos