Naviguer dans l'univers d'Android Automotive et de sa connectivité profonde avec les systèmes embarqués peut ressembler à un labyrinthe, mais la Vehicle Abstraction Layer (VHAL) promet de combler cet écart. En parcourant cet article, vous découvrirez l'essence d'Android Automotive, son interaction avec le matériel du véhicule et le potentiel d'une approche d'implémentation de la VHAL utilisant ROS 2. À travers une étude de cas, l'article met en lumière comment les équipes peuvent paralléliser efficacement leur travail, réduire les coûts et améliorer la qualité des produits, tout en naviguant dans les défis de la compilation croisée dans l'écosystème Android.

Introduction à Android Automotive

Android Automotive est une plateforme Android de base qui exécute des applications Android préinstallées du système IVI, ainsi que des applications Android tierces et de second niveau optionnelles. Elle fonctionne directement sur le matériel embarqué du véhicule et peut interagir physiquement avec la voiture ; pour ce faire, Android Automotive nécessite l'accès aux spécificités de la plateforme, aux périphériques matériels et aux réseaux (par exemple, CAN).

Du point de vue des applications Android Automotive, l'accès aux fonctionnalités spécifiques au véhicule est réalisé via le Car API. Derrière l'API Car, il existe plusieurs couches d'abstraction matérielle, notamment la Vehicle Abstraction Layer (VHAL), conçue spécifiquement pour correspondre aux fonctionnalités du véhicule, par exemple le contrôle de la configuration du HVAC, des portes ou des sièges.

Le VHAL définit un ensemble de propriétés que les OEM peuvent implémenter et contient des métadonnées de propriété (par exemple, si la propriété est unint et quels modes de modification sont autorisés). L'interface VHAL repose sur l'accès (lecture, écriture, abonnement) à une propriété, qui est une abstraction d'une fonction spécifique.

Diagram of Android Automotive architecture showing three app layers: system apps, OEM apps, and third-party apps. These connect through Android Framework API and Car API to Android System Services and Car Service, which interact with traditional Android HALs and the Vehicle HAL. Colour coding indicates responsibility: Android, OEM, and third party.

Implémentation de VHAL : connexion de bas niveau

Pour connecter l'API Car avec le véhicule physique, les OEM doivent implémenter le VHAL. Généralement, cela se fait en utilisant des langages de bas niveau comme C ou C++ et des bibliothèques spécifiques au matériel pour le connecter aux réseaux du véhicule (CAN, LIN, etc.) ou aux périphériques système connectés via I2C ou SPI.

Cette approche rend le système étroitement couplé au matériel et moins portable.

android automotive vhal ros

Implémentation du VHAL : communication de haut niveau

Une autre façon de mettre en œuvre le VHAL consisterait à le découpler du système physique sous-jacent et à relier le flux de données via un protocole de communication de plus haut niveau, tel que ROS 2 / DDS. Ainsi, l'implémentation spécifique au matériel pourrait être séparée en nœuds ROS et les données du véhicule fournies via des topics et services ROS. Cela apporterait de nombreux avantages, tels que la modularisation, une communication standardisée avec le matériel et plusieurs outils ROS 2 pour la simulation, les tests, le débogage et la visualisation des données !

Ci-dessous se trouve un exemple du nœud VHAL qui communique avec le système IVI du constructeur via des ROS Topics/Services.

android automotive vhal ros

Étude de cas : Connecter VHAL à l'API car d'Android Automotive au-dessus de ROS 2

Trois facteurs conditionnent la réussite du développement de produit :

  • time-to-market,
  • prix
  • et de la qualité.

Et la seconde approche décrite ci-dessus nous ouvre encore plus de possibilités sur ces trois facteurs. Pour réduire les coûts et accélérer le développement produit, les équipes peuvent souhaiter paralléliser le travail. Mais cela ne peut être réalisé que s'il n'existe pas de dépendances strictes entre les parties du système. Ici, grâce au découplage de l'implémentation VHAL du système OEM, les équipes de développement peuvent commencer le travail immédiatement en parallèle sans attendre que le système OEM soit prêt à être utilisé et testé. Certaines parties du système OEM peuvent même ne pas encore exister, et cela ne bloquerait pas le développement tant que les interfaces de données ROS 2 (topics, services) sont définies.

Le découplage du système OEM contribue également à la qualité globale du produit, car il rend l'ensemble du système plus testable. Les applications Android peuvent être testées très tôt dans un système complet du point de vue de l'application, mais sans avoir besoin d'assembler chaque composant sous-jacent. Les ECU du véhicule peuvent simplement être simulés en tant que nœuds de test ROS 2 :

android automotive vhal ros

Réalisation

Toute la mise en œuvre et les tests dans cet article ont été réalisés sur ROS 2 Iron et la version 13.0 d'AOSP.

Comment donner vie au projet ? Il doit être mis en œuvre d'une manière ou d'une autre. Explorons les possibilités et déterminons laquelle peut être choisie comme la plus appropriée.

Nous supposons que le VHAL sera un nœud ROS 2. On pourrait envisager de mettre en œuvre simplement un nœud DDS qui communique via un topic DDS avec le système du constructeur. Mais plus tard, nous verrons que ROS 2 apporte l'abstraction nécessaire au-dessus du protocole DDS pour ajouter une communication orientée services (en plus d'un riche ensemble d'outils et d'autres API).

Compilation croisée de ROS 2

ROS 2 est généralement fourni précompilé pour une distribution Linux spécifique, comme Ubuntu, mais peut également être compilé à partir des sources. Cependant, la compilation croisée de ROS 2 pour des systèmes non pris en charge ne semble pas être une tâche facile. Et Android n'est pas pris en charge dans notre cas.

Certains projets visaient à simplifier la compilation croisée de ROS 2, comme cross_compile, mais au moment de la rédaction de cet article, ce projet est déjà obsolète. De plus, il ne fournit même pas une véritable compilation croisée, mais plutôt une compilation émulée dans QEMU (ce qui nécessiterait d'exécuter la compilation au sein du système Android émulé).

Parce qu'Android n'est pas une simple distribution GNU/Linux, nous devons non seulement compiler ROS 2 lui-même, mais aussi toutes ses dépendances. Nous ne pouvons pas simplement les installer avec les gestionnaires de paquets système, par exemple avec :

apt install <package>

Android se distingue également par le développement d'applications natives de la plateforme. Bien que les applications utilisateur / tierces puissent être construites avec de nombreux systèmes de build externes, le code de la plateforme est construit et assemblé avec des outils de build spécifiques à Android.

Dans AOSP, il existe trois systèmes de build :

  • le système de build hérité basé sur Make,
  • Soong,
  • et le futur système de build basé sur Bazel.

Comme la compilation basée sur Make est progressivement remplacée par Soong, elle ne sera pas prise en compte ici. Et Bazel n'est pas encore entièrement disponible pour AOSP, nous nous concentrerons donc uniquement sur le système de compilation Soong.

ROS 2, en revanche, utilise CMake comme système de construction de base, qui est étendu avec ament_cmake et colcon. Il s'appuie sur des paquets système comme dépendances pour compiler sa base de code. Comme il utilise CMake, fournir une définition de CMake Toolchain pour la compilation croisée devrait être relativement simple. Il nous suffit d'utiliser la chaîne d'outils croisée Android appropriée pour notre cible.

Comment intégrer un projet externe avec Soong ?

Une solution consisterait à ajouter des scripts de build Soong pour tous les paquets ROS 2 requis, mais cela représenterait tout simplement trop de travail et nécessiterait une maintenance régulière. Heureusement, Soong prend en charge l'intégration de bibliothèques compilées en externe en tant que cibles et permet aux modules Android de les lier, de manière dynamique et statique.

À titre d'exemple, pour inclure une bibliothèque statique externe, un développeur doit définir un module avec la configuration suivante :

package { } cc_prebuilt_library_static { name: "vendor.spyrosoft.libexample", vendor: true, export_include_dirs: [ "include/libexample" ], srcs: ["prebuilt/arm64/libexample.a"], strip: { none:true, }, }

Dans la bibliothèque statique ci-dessus, le fichier d'archive de la bibliothèque précompilée se trouve dans le prebuilt/arm64/libexample.a, , et son API publique headers location is exported with export_include_dirs, ainsi les autres modules hériteront automatiquement du chemin inclus.

Liaison statique ou dynamique ?

ROS 2 prend en charge la compilation avec liaison dynamique et statique. Comme chaque bibliothèque dynamique compilée pour ROS 2 nécessiterait une définition de build Soong distincte, la liaison statique semble mieux adaptée. Pour compiler les paquets ROS 2, une option CMake BUILD_SHARED_LIBS doit être défini surOFF. Il peut simplement être ajouté au colcon as one of the CMake args:

$ colcon build --cmake-args "BUILD_SHARED_LIBS=OFF" ...

Où obtenons-nous la chaîne d'outils croisés Android ?

On pourrait envisager d'utiliser les NDK, qui prend en charge l'utilisation de CMake pour construire des applications natives. Mais il y a un hic ! Comme nous prévoyons d'implémenter un service de plateforme – VHAL, celui-ci sera lié aux bibliothèques natives du système, et le NDK ne peut pas être utilisé pour le construire. La raison en est l'introduction deEspaces de noms pour les bibliothèques natives. Par conséquent, si nous utilisions le NDK, toute tentative de compilation d'un code de plateforme avec celui-ci aboutirait très probablement à des erreurs de l'éditeur de liens. Les bibliothèques natives du NDK sont incompatibles au niveau de l'ABI avec les bibliothèques natives du système.

android automotive vhal ros

À partir de la version AOSP 13.0_r61, la chaîne d'outils pour le code natif de la plateforme se trouve dans :

prebuilts/clang/host/linux-x86/clang-r450784d/

et le sysroot se trouve dans :

prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.17-4.8/sysroot

Contrairement à la GNU compiler collection, où chaque combinaison hôte/cible possède son propre ensemble de binaires, d'en-têtes, de bibliothèques, etc., Clang/LLVM est nativement un compilateur croisé, ce qui signifie qu'un seul ensemble de programmes peut compiler vers toutes les cibles en définissant le -target option.

Pour obtenir l'ensemble des options spécifiques à la plateforme cible, nous pouvons exploiter le fait qu'AOSP n'est pas entièrement migré vers Soong, et que certains modules reposent encore sur Make. Pour maintenir la compatibilité avec Make, AOSP utilise un outil appelé Kati, qui est un clone de Make. Soong communique avec Kati en générant des Make Vars et génère .mk fichiers dans le répertoire de sortie out/soong/{Android, late, make_vars}-<product>.mk

Le fichier out/soong/make_vars-<product-suffix>.mk c'est là que tous les indicateurs et options du compilateur sont stockés. Il nous suffit d'analyser les données et de générer le fichier de chaîne d'outils CMake. Implémentons-le en Python.

Pour localiser la cible spécifique out/soong/make_vars-<product-suffix>.mk fichier, un « product-suffix » est nécessaire. Heureusement, Soong le stocke dansout/soong/soong.variables sous la clé « Make_suffix », par ex.,

"Make_suffix": "-aosp_rpi4",

Pour obtenir le chemin du fichier make_vars.mk, nous pouvons procéder comme suit :

def get_makefile_path(): with open('out/soong/soong.variables', 'r') as soong_vars: vars = json.load(soong_vars) make_suffix = vars['Make_suffix'] return os.path.abspath(f'out/soong/make_vars{vars['Make_suffix']}.mk')

Pour obtenir toutes les variables sous forme de liste indexable, nous pouvons utiliser un modèle regex relativement simple :

def parse_makefile(file_path): variables = {} variable_assignment_regex = re.compile(r'^([a-zA-Z0-9_]+)\s*:=\s*(.*)$') with open(file_path, 'r') as makefile: for line in makefile: # Remove comments line = line.split('#')[0].strip() # Match the line against the regular expression match = variable_assignment_regex.match(line) if match: variables[match.group(1)] = match.group(2) return variables

Une fois que nous disposons de toutes les informations, nous pouvons générer le fichier CMake Toolchain :

toolchain_file_content = f"""# # Without that flag CMake is not able to pass test compilation check set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR ARM) set(CMAKE_SYSTEM_VERSION 31) set(CMAKE_SYSROOT {root_path + find_in('--sysroot ', mk_vars['SOONG_CLANG_HOST_GLOBAL_CFLAGS'])}) set(CMAKE_ANDROID_STANDALONE_TOOLCHAIN {root_path + find_in('--gcc-toolchain=', mk_vars['SOONG_CLANG_HOST_GLOBAL_CFLAGS'])}) set(TOOLCHAIN_TRIPLE {mk_vars['SOONG_CLANG_CONFIG_arm64_TARGET_TRIPLE']}) # specify the cross compiler set(CMAKE_ASM_COMPILER {root_path + mk_vars['SOONG_CLANG']} CACHE STRING "") SET(CMAKE_ASM_COMPILER_TARGET ${{TOOLCHAIN_TRIPLE}}) set(CMAKE_C_COMPILER {root_path + mk_vars['SOONG_CLANG']} CACHE STRING "") set(CMAKE_C_COMPILER_TARGET ${{TOOLCHAIN_TRIPLE}}) set(CMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN ${{CMAKE_ANDROID_STANDALONE_TOOLCHAIN}}) set(CMAKE_CXX_COMPILER {root_path + mk_vars['SOONG_CLANG_CXX']} CACHE STRING "") set(CMAKE_CXX_COMPILER_TARGET ${{TOOLCHAIN_TRIPLE}}) set(CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN ${{CMAKE_ANDROID_STANDALONE_TOOLCHAIN}}) # specify the linker set(CMAKE_C_LINK_EXECUTABLE {root_path + mk_vars['SOONG_TARGET_LD']} CACHE STRING "") set(CMAKE_CXX_LINK_EXECUTABLE {root_path + mk_vars['SOONG_TARGET_LD']} CACHE STRING "") set(CMAKE_LINKER {root_path + mk_vars['SOONG_TARGET_LD']} CACHE STRING "") set(CMAKE_C_FLAGS_INIT "{mk_vars['SOONG_CLANG_TARGET_GLOBAL_CFLAGS']}" CACHE STRING "") set(CMAKE_CXX_FLAGS_INIT "{mk_vars['SOONG_CLANG_TARGET_GLOBAL_CPPFLAGS']}" CACHE STRING "") set(CMAKE_C_FLAGS_DEBUG "-O0 -g" CACHE INTERNAL "") set(CMAKE_C_FLAGS_RELEASE "-Os -DNDEBUG" CACHE INTERNAL "") set(CMAKE_CXX_FLAGS_DEBUG "${{CMAKE_C_FLAGS_DEBUG}}" CACHE INTERNAL "") set(CMAKE_CXX_FLAGS_RELEASE "${{CMAKE_C_FLAGS_RELEASE}}" CACHE INTERNAL "") set(CMAKE_C_STANDARD_INCLUDE_DIRECTORIES {root_path + "bionic/libc/include"} {root_path + "bionic/libc/kernel/uapi"} {root_path + "bionic/libc/kernel/android/scsi"} {root_path + "bionic/libc/kernel/android/uapi"} {root_path + "bionic/libc/kernel/uapi/asm-arm64"} {root_path + "external/libcxx/include"} ) set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES ${{CMAKE_C_STANDARD_INCLUDE_DIRECTORIES}} ${{CMAKE_SYSROOT}}/usr/include/x86_64-linux-gnu/ {root_path + '/synergycar/aosp/external/libcxx/include'} ) add_compile_options(-fPIC) add_compile_definitions(__ANDROID_API__=31 __ANDROID__) add_link_options({mk_vars['SOONG_CLANG_TARGET_GLOBAL_LLDFLAGS']}) link_directories({root_path + "prebuilts/runtime/mainline/runtime/sdk/android/arm/lib/"}) # don't search for programs in the build host directories set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) # search for libraries and headers in the target directories set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) """ # Write the CMake toolchain file with open('android_toolchain.cmake', 'w') as toolchain_file: toolchain_file.write(toolchain_file_content)

Certains détails nécessaires à la définition de la chaîne d'outils CMake sont enfouis un peu plus profondément, par exemple, le CMAKE_SYSROOT le chemin doit être extrait de SOONG_CLANG_HOST_GLOBAL_CFLAGS et certains paramètres supplémentaires doivent être ajoutés manuellement, par exemple, la bibliothèque C d'Android – Bionic inclure des répertoires ou des définitions globales.

Pour plus de détails, veuillez vous référer au script complet, disponible ici.

Le fichier de chaîne d'outils généré peut être facilement utilisé avec colcon pour construire le ROS 2 :

$ colcon build --cmake-args "BUILD_SHARED_LIBS=OFF -DCMAKE_TOOLCHAIN_FILE=aosp_toolchain.cmake"

Résumé

Bien que l'approche théorique de la compilation croisée de ROS 2 pour Android soit prometteuse, l'application pratique peut présenter certains défis, notamment lorsqu'il s'agit de compiler toutes les dépendances requises. Bien entendu, il existe de nombreuses pistes potentielles que les chercheurs et les ingénieurs peuvent suivre pour atteindre une compatibilité et une fonctionnalité complètes.

Cependant, nous ne pouvons ignorer l'existence d'une solution plus élégante et potentiellement plus efficace pour ceux qui cherchent à intégrer ROS 2 avec des systèmes non pris en charge : micro-ROS. Ce projet innovant offre une approche bien plus adaptée de la compilation croisée sur diverses plateformes. Dans le prochain volet de cette série, nous approfondirons micro-ROS, en explorant ses capacités et en tentant de le compiler et de l'exécuter sur Android.

Démarrez votre prochain projet de robotique avec Spyrosoft

Vous cherchez une entreprise à qui confier votre produit ? Vous trouverez ici plus d'informations sur l'étendue de nosservices de robotique professionnelleet comment nous donnons vie aux idées.

La couche d'abstraction du véhicule (VHAL) constitue un pont essentiel entre le système d'exploitation Android Automotive et le matériel physique d'un véhicule. Elle définit un ensemble standardisé de propriétés que les fabricants d'équipements d'origine (OEM) peuvent implémenter pour contrôler des fonctions spécifiques du véhicule, telles que le chauffage, la ventilation et la climatisation (HVAC), les serrures de portes ou les configurations de sièges. En faisant abstraction de ces spécificités matérielles, la VHAL permet aux applications Android d'interagir de manière transparente avec les réseaux sous-jacents du véhicule, tels que le Controller Area Network (CAN) ou le Local Interconnect Network (LIN), via l'API Car.

Traditionnellement, les OEM implémentent le VHAL à l'aide de langages de programmation de bas niveau tels que C ou C++, ce qui couple étroitement le système à un matériel spécifique et réduit la portabilité. À l'inverse, une implémentation de haut niveau utilisant le Robot Operating System 2 (ROS 2) ou le Data Distribution Service (DDS) découple le VHAL du système physique. Dans cette approche, les fonctions spécifiques au matériel sont séparées en nœuds ROS, et les données du véhicule sont communiquées via des topics et des services ROS. Cette modularisation standardise la communication et fournit aux développeurs des outils robustes pour la simulation, les tests et la visualisation des données.

Découpler la VHAL du système du constructeur à l'aide de ROS 2 accélère considérablement le développement des produits et améliore la qualité globale. Cela permet aux équipes d'ingénierie de paralléliser leurs flux de travail ; les développeurs peuvent commencer à construire et à tester des applications Android immédiatement, sans attendre que le matériel physique du constructeur soit entièrement assemblé. En outre, cette approche améliore la testabilité. Les unités de contrôle électronique (ECU) du véhicule peuvent être facilement simulées en tant que nœuds de test ROS 2, permettant des tests système complets dès le début du cycle de développement, ce qui réduit en fin de compte les coûts et le time-to-market.

La compilation croisée de ROS 2 pour Android Automotive présente des défis distincts, car Android n'est pas une distribution GNU/Linux standard. Les développeurs ne peuvent pas simplement utiliser les gestionnaires de paquets système pour installer les dépendances. De plus, le code de la plateforme Android repose sur des systèmes de build spécifiques, principalement Soong, plutôt que sur le système CMake utilisé par ROS 2. Pour intégrer avec succès ROS 2, les ingénieurs doivent générer un fichier de chaîne d'outils CMake personnalisé qui analyse les indicateurs du compilateur à partir de l'environnement de build de l'Android Open Source Project (AOSP). En outre, en raison des restrictions de namespace des bibliothèques natives, la liaison statique est généralement préférée à la liaison dynamique pour éviter les erreurs d'incompatibilité ABI.

Oui, micro-ROS constitue une alternative tout à fait viable pour intégrer des frameworks robotiques avec des systèmes non pris en charge ou à ressources limitées. Bien que la compilation croisée du framework ROS 2 standard pour Android soit possible, elle nécessite des contournements complexes concernant les dépendances et les systèmes de build. Micro-ROS offre une approche plus adaptée et élégante, conçue spécifiquement pour les microcontrôleurs et les systèmes embarqués. Il simplifie le processus de compilation croisée sur diverses plateformes, ce qui en fait un excellent candidat pour les futures implémentations d'Android Automotive VHAL.