ניווט בעולם של Android Automotive והקישוריות העמוקה שלו למערכות הרכב יכול להיות מבוך, אך שכבת הפשטת הרכב (VHAL) מבטיחה לגשר על פער זה. בצלילה למאמר זה, תגלו את המהות של Android Automotive, האינטראקציה שלו עם חומרת הרכב והפוטנציאל של גישה ליישום VHAL באמצעות ROS 2. באמצעות חקר מקרה, המאמר שופך אור על האופן שבו צוותים יכולים לבצע במקביל עבודה ביעילות, להפחית עלויות ולשפר את איכות המוצר, כל זאת תוך ניווט באתגרים של הידור צולב באקוסיסטם של Android.

מבוא ל-Android Automotive

Android Automotive היא פלטפורמת Android בסיסית שמריצה אפליקציות Android של מערכת IVI המותקנות מראש, כמו גם אפליקציות Android אופציונליות של צד שני ושלישי. היא פועלת ישירות על החומרה של הרכב ויכולה לתקשר פיזית עם המכונית; לשם כך, Android Automotive דורשת גישה למאפיינים הספציפיים של הפלטפורמה, להתקנים היקפיים ולמערכות הרשת (למשל, CAN).

מנקודת המבט של אפליקציות Android Automotive, הגישה לפונקציונליות הספציפית לרכב מתבצעת דרך ה- Car API. מאחורי Car API קיימות שכבות הפשטת חומרה מרובות, כולל שכבת הפשטת הרכב (VHAL), אשר תוכננה במיוחד להתאמה לפונקציונליות הרכב, כגון HVAC, דלת או שליטה בהגדרות המושב.

ה-VHAL מגדיר סט של מאפיינים שיצרנים (OEMs) יכולים ליישם ומכיל מטא-נתונים של מאפיינים (לדוגמה, האם המאפיין הואint ואילו מצבי שינוי מותרים). ממשק ה-VHAL מבוסס על גישה (קריאה, כתיבה, הרשמה) למאפיין, שהוא הפשטה של פונקציה ספציפית.

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.

יישום VHAL: חיבור ברמה נמוכה

כדי לחבר את Car API עם המכונית הפיזית, יצרני ציוד מקורי (OEMs) חייבים ליישם את VHAL. בדרך כלל, הדבר נעשה באמצעות שפות ברמה נמוכה כמו C או C++ וספריות ייעודיות לחומרה כדי לחבר אותו לרשתות הרכב (CAN, LIN וכו') או להתקנים היקפיים של המערכת המחוברים באמצעות I2C או SPI.

גישה זו הופכת את המערכת לצמודה באופן הדוק לחומרה ופחות ניידת.

android automotive vhal ros

יישום VHAL: תקשורת ברמה גבוהה

דרך נוספת ליישם את ה-VHAL תהיה לנתק אותו מהמערכת הפיזית הבסיסית ולגשר על זרימת הנתונים באמצעות פרוטוקול תקשורת ברמה גבוהה יותר, כגון ROS 2 / DDS. בדרך זו, ניתן להפריד את היישום הספציפי לחומרה ל-ROS Nodes ולספק את נתוני הרכב ב-ROS Topics ו-Services. הדבר יביא יתרונות רבים, כגון מודולריזציה, תקשורת סטנדרטית עם החומרה ומספר כלי ROS 2 לסימולציה, בדיקות, דיבוג והצגת נתונים!

להלן דוגמה של צומת VHAL המתקשר עם מערכת ה-IVI של ה-OEM באמצעות ROS Topics/Services.

android automotive vhal ros

מקרה בוחן: חיבור VHAL ל-Android Automotive car API מעל ROS 2

ישנם שלושה גורמים שהופכים פיתוח מוצר להצלחה:

  • זמן הגעה לשוק,
  • מחיר
  • ואיכות.

והגישה השנייה שתוארה לעיל מובילה אותנו לאפשרויות רבות אף יותר בשלושת הגורמים הללו. כדי להפחית את המחיר ולהאיץ את פיתוח המוצר, צוותים עשויים לרצות לבצע את העבודה במקביל. אך ניתן להשיג זאת רק אם אין תלויות נוקשות בין חלקי המערכת. כאן, הודות לניתוק יישום ה-VHAL ממערכת ה-OEM, צוותי פיתוח יכולים להתחיל את העבודה מיד במקביל מבלי להמתין שמערכת ה-OEM תהיה מוכנה לשימוש ולבדיקה. חלקים מסוימים של מערכת ה-OEM עשויים אפילו לא להתקיים עדיין, וזה לא יחסום את הפיתוח כל עוד ממשקי הנתונים של ROS 2 (נושאים, שירותים) מוגדרים.

ניתוק ממערכת ה-OEM מוסיף גם לאיכות המוצר הכוללת, מכיוון שהוא הופך את המערכת כולה לניתנת לבדיקה רבה יותר. ניתן לבדוק אפליקציות Android בשלב מוקדם במערכת שלמה מנקודת המבט של האפליקציה, אך ללא צורך להרכיב כל רכיב בסיסי. ניתן פשוט לדמות ECU-ים של רכב כצמתי בדיקה של ROS 2:

android automotive vhal ros

מימוש

כל היישום והבדיקות במאמר זה בוצעו על ROS 2 Iron וגרסת AOSP 13.0.

כיצד מביאים את הפרויקט לידי מימוש? יש ליישם אותו בדרך זו או אחרת. הבה נבחן את האפשרויות ואיזו מהן ניתן לבחור כמתאימה ביותר.

אנו מניחים שה-VHAL יהיה צומת ROS 2. אפשר לחשוב ליישם רק צומת DDS שמתקשר דרך נושא DDS עם מערכת ה-OEM. אך בהמשך נראה ש-ROS 2 מביא את ההפשטה הנדרשת מעל פרוטוקול DDS כדי להוסיף תקשורת מוכוונת שירותים (מלבד סט עשיר של כלים וממשקי API אחרים).

הידור צולב של ROS 2

ROS 2 מגיע בדרך כלל מהודר מראש עבור הפצת Linux ספציפית, כמו Ubuntu, אך ניתן גם להידר אותו מהמקור. עם זאת, הידור צולב של ROS 2 עבור מערכות שאינן נתמכות אינו נראה כמשימה קלה. ובמקרה שלנו, Android אינו נתמך.

חלק מהפרויקטים נועדו לפשט את הקומפילציה הצולבת של ROS 2, כגון cross_compile, אך נכון לכתיבת מאמר זה, פרויקט זה כבר אינו נתמך. יתרה מכך, הוא אינו מספק קומפילציה צולבת אמיתית, אלא בנייה מדומה בתוך QEMU (שתחייב הרצת הבנייה בתוך מערכת Android המדומה).

מכיוון ש-Android אינה רק עוד הפצת GNU/Linux, עלינו לבנות לא רק את ROS 2 עצמו, אלא גם את כל התלויות. איננו יכולים פשוט להתקין אותן באמצעות מנהלי חבילות מערכת, למשל עם:

apt install <package>

Android נבדלת גם בפיתוח אפליקציות פלטפורמה מקוריות. בעוד שאפליקציות משתמש / צד שלישי עשויות להיבנות עם מערכות בנייה חיצוניות רבות, קוד הפלטפורמה נבנה ומורכב עם כלי בנייה ייעודיים ל-Android.

ב-AOSP קיימות שלוש מערכות בנייה:

  • מערכת הבנייה הוותיקה מבוססת Make,
  • Soong,
  • ומערכת הבנייה המבוססת על Bazel הקרובה.

מכיוון שבניית Make מוחלפת בהדרגה ב-Soong, היא לא תיבחן כאן. ו-Bazel עדיין אינו זמין במלואו עבור AOSP, לכן נתמקד רק במערכת הבנייה Soong.

ROS 2, לעומת זאת, משתמש ב-CMake כמערכת הבנייה הבסיסית, אשר מורחבת עם ament_cmake ו colcon. הוא נשען על חבילות מערכת כתלויות לבניית בסיס הקוד שלו. מכיוון שהוא משתמש ב-CMake, אספקת הגדרת CMake Toolchain לקומפילציה צולבת אמורה להיות פשוטה למדי. עלינו רק להשתמש ב-cross-toolchain המתאים של Android עבור היעד שלנו.

כיצד משלבים פרויקט חיצוני עם Soong?

אחת הדרכים יכולה להיות הוספת סקריפטי בנייה של Soong עבור כל חבילות ROS 2 הנדרשות, אך הדבר ידרוש עבודה רבה מדי ותחזוקה שוטפת. למרבה המזל, Soong תומך בשילוב ספריות שנבנו חיצונית כיעדים שלו ומאפשר למודולים של Android לקשר אותן, הן באופן דינמי והן באופן סטטי.

כדוגמה, כדי לכלול ספרייה סטטית חיצונית, מפתח חייב להגדיר מודול עם התצורה הבאה:

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

בספרייה הסטטית לעיל, קובץ הארכיון של הספרייה הבנויה מראש ממוקם ב- prebuilt/arm64/libexample.a, , וה-API הציבורי שלו headers location is exported with export_include_dirs, כך שמודולים אחרים יירשו את הנתיב הכלול באופן אוטומטי.

קישור סטטי או דינמי?

ROS 2 תומך בבנייה עם קישור דינמי וסטטי כאחד. מכיוון שכל ספרייה דינמית שנבנית עבור ROS 2 תדרוש הגדרת בנייה נפרדת ב-Soong, נראה שהקישור הסטטי מתאים יותר. כדי לבנות את חבילות ROS 2, אפשרות CMake BUILD_SHARED_LIBS יש להגדיר ל-OFF. ניתן להוסיף אותו בפשטות ל- colcon as one of the CMake args:

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

מהיכן משיגים את ה-cross-toolchain של Android?

אפשר לחשוב על שימוש ב-Android NDK, התומך בשימוש ב- CMake לבניית אפליקציות מקוריות. אבל יש מלכוד! מכיוון שאנו מתכננים להטמיע שירות פלטפורמה – VHAL, הוא יתקשר לספריות המקוריות של המערכת, ולא ניתן להשתמש ב-NDK כדי לבנות אותו. הסיבה לכך היא הצגתNamespaces לספריות Native. כתוצאה מכך, אם נשתמש ב-NDK, ניסיון לבנות קוד פלטפורמה איתו יסתיים ככל הנראה בשגיאות מקשר. ספריות ה-NDK המקוריות אינן תואמות ברמת ה-ABI לספריות המערכת המקוריות.

android automotive vhal ros

החל מגרסת AOSP 13.0_r61, שרשרת הכלים לקוד פלטפורמה מקורי ממוקמת ב-:

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

וה-sysroot ממוקם ב:

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

בשונה מ-GNU compiler collection, שבו לכל שילוב של host/target יש את סט הקבצים הבינאריים, הכותרות, הספריות וכו' שלו, Clang/LLVM הוא מעצם טבעו מהדר צולב (cross-compiler), כלומר סט אחד של תוכניות יכול לקמפל לכל ה-targets על ידי הגדרת -target option.

כדי לקבל את סט האפשרויות הספציפי לפלטפורמת היעד, נוכל להשתמש בעובדה ש-AOSP אינו מועבר במלואו ל-Soong, וחלק מהמודולים עדיין נשענים על Make. כדי לשמור על תאימות עם Make, AOSP משתמש בכלי הנקרא Kati, שהוא למעשה שיבוט של Make. Soong מתקשר עם Kati על ידי יצירת Make Vars ומייצר .mk קבצים בספריית הפלט out/soong/{Android, late, make_vars}-<product>.mk

הקובץ out/soong/make_vars-<product-suffix>.mk הוא המקום שבו מאוחסנים כל דגלי הקומפילציה והאפשרויות. עלינו רק לנתח את הנתונים וליצור את קובץ ה-toolchain של CMake. בואו ניישם זאת ב-Python.

כדי לאתר את היעד הספציפי out/soong/make_vars-<product-suffix>.mk קובץ, יש צורך ב-'product-suffix'. למרבה המזל, Soong שומר אותו ב-out/soong/soong.variables תחת מפתח “Make_suffix”, לדוגמה,

"Make_suffix": "-aosp_rpi4",

כדי לקבל את הנתיב של קובץ make_vars.mk, נוכל לעשות את הפעולות הבאות:

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')

כדי לקבל את כל המשתנים כרשימה ניתנת לאינדוקס, נוכל להשתמש בתבנית regex פשוטה יחסית:

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

לאחר שיש לנו את כל המידע, נוכל ליצור את קובץ ה-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)

חלק מהפרטים הנדרשים להגדרת שרשרת הכלים של CMake קבורים קצת יותר לעומק, למשל ה- CMAKE_SYSROOT יש לחלץ את הנתיב מתוך SOONG_CLANG_HOST_GLOBAL_CFLAGS וכמה הגדרות נוספות יש להוסיף ידנית, למשל ספריית C של אנדרואיד – Bionic כולל תיקיות או הגדרות גלובליות מסוימות.

לפרטים, אנא עיינו בתסריט המלא, אשר ניתן למצוא כאן.

ניתן להשתמש בקלות בקובץ שרשרת הכלים שנוצר עם colcon לבנות את ROS 2:

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

תקציר

בעוד שהגישה התיאורטית לקומפילציה צולבת של ROS 2 עבור Android מבטיחה, היישום המעשי עשוי להציג אתגרים מסוימים, במיוחד בכל הנוגע לקומפילציה של כל התלויות הנדרשות. כמובן, קיימות דרכים פוטנציאליות רבות שחוקרים ומהנדסים יכולים לנקוט כדי להשיג תאימות ופונקציונליות מלאה.

עם זאת, אין להתעלם מקיומו של פתרון אלגנטי יותר ויעיל פוטנציאלית יותר עבור אלו המבקשים לשלב את ROS 2 עם מערכות שאינן נתמכות: micro-ROS. פרויקט חדשני זה מציע גישה מותאמת הרבה יותר לקומפילציה צולבת על פני פלטפורמות שונות. בחלק הבא של סדרה זו, נעמיק ב-micro-ROS, נחקור את יכולותיו וננסה לבנות ולהריץ אותו על Android.

התחילו את פרויקט הרובוטיקה הבא שלכם עם Spyrosoft

מחפש חברה שאליה תוכל להפקיד את המוצר שלך? כאן תוכל למצוא מידע נוסף על היקףשירותי רובוטיקה מקצועייםוכיצד אנו מביאים רעיונות לידי מימוש.

שכבת הפשטת הרכב (VHAL) משמשת כגשר קריטי בין מערכת ההפעלה Android Automotive לחומרה הפיזית של הרכב. היא מגדירה סט מתוקנן של מאפיינים שיצרני ציוד מקורי (OEMs) יכולים ליישם כדי לשלוט בפונקציות רכב ספציפיות, כגון חימום, אוורור ומיזוג אוויר (HVAC), מנעולי דלתות או תצורות מושבים. על ידי הפשטת פרטי החומרה הללו, ה-VHAL מאפשר לאפליקציות Android לתקשר בצורה חלקה עם הרשתות הבסיסיות של הרכב, כגון Controller Area Network (CAN) או Local Interconnect Network (LIN), דרך ה-Car API.

באופן מסורתי, יצרני ציוד מקורי (OEMs) מיישמים את ה-VHAL באמצעות שפות תכנות ברמה נמוכה כמו C או C++, מה שמצמיד את המערכת באופן הדוק לחומרה ספציפית ומפחית את הניידות. לעומת זאת, יישום ברמה גבוהה באמצעות Robot Operating System 2 (ROS 2) או Data Distribution Service (DDS) מנתק את ה-VHAL מהמערכת הפיזית. בגישה זו, פונקציות ספציפיות לחומרה מופרדות לצמתי ROS, ונתוני הרכב מועברים באמצעות נושאים ושירותים של ROS. מודולריזציה זו מתקננת את התקשורת ומספקת למפתחים כלים חזקים לסימולציה, בדיקה והצגת נתונים.

ניתוק ה-VHAL ממערכת ה-OEM באמצעות ROS 2 מאיץ באופן משמעותי את פיתוח המוצר ומשפר את האיכות הכוללת. הוא מאפשר לצוותי הנדסה לבצע במקביל את זרימות העבודה שלהם; מפתחים יכולים להתחיל לבנות ולבדוק אפליקציות Android באופן מיידי, מבלי להמתין להרכבה מלאה של חומרת ה-OEM הפיזית. יתרה מזאת, גישה זו משפרת את יכולת הבדיקה. יחידות בקרה אלקטרוניות של הרכב (ECUs) יכולות להיות מדומות בקלות כצמתי בדיקה של ROS 2, מה שמאפשר בדיקת מערכת מקיפה בשלב מוקדם של מחזור החיים של הפיתוח, אשר בסופו של דבר מפחית עלויות וזמן הגעה לשוק.

הידור חוצה-פלטפורמות של ROS 2 עבור Android Automotive מציב אתגרים ייחודיים מכיוון ש-Android אינה הפצת GNU/Linux סטנדרטית. מפתחים אינם יכולים פשוט להשתמש במנהלי חבילות מערכת כדי להתקין תלויות. יתרה מזאת, קוד פלטפורמת Android מסתמך על מערכות בנייה ספציפיות, בעיקר Soong, ולא על מערכת CMake המשמשת את ROS 2. כדי לשלב בהצלחה את ROS 2, מהנדסים חייבים ליצור קובץ toolchain של CMake מותאם אישית שמנתח דגלי מהדר מסביבת הבנייה של Android Open Source Project (AOSP). בנוסף, בשל הגבלות מרחב שמות של ספריות מקוריות, קישור סטטי מועדף בדרך כלל על פני קישור דינמי כדי למנוע שגיאות אי-תאימות ABI.

כן, micro-ROS מהווה חלופה ברת-קיימא במיוחד לשילוב מסגרות רובוטיות עם מערכות שאינן נתמכות או מוגבלות במשאבים. אמנם קומפילציה צולבת של מסגרת ROS 2 הסטנדרטית עבור Android אפשרית, אך היא דורשת פתרונות עוקפים מורכבים בכל הנוגע לתלויות ולמערכות בנייה. Micro-ROS מציע גישה מותאמת ואלגנטית יותר שתוכננה במיוחד עבור מיקרו-בקרים ומערכות משובצות. הוא מפשט את תהליך הקומפילציה הצולבת על פני פלטפורמות שונות, מה שהופך אותו למועמד מצוין ליישומי Android Automotive VHAL עתידיים.