שדרוג CI ברובוטיקה עם סימולציות Gazebo: מקרה בוחן
בתחום הרובוטיקה, שבו דיוק ויעילות הם קריטיים, לכלים ולטכנולוגיות תפקיד משמעותי בשיפור פרויקטים. עם זאת, אחד האתגרים המשמעותיים ביותר טמון בהבטחת האיכות והאמינות של התוכנה, במיוחד בהקשר של פרויקטים מורכבים כמו רובוטים בעלי יכולת לקבל החלטות עצמאיות בסביבות לא מוכרות ומשתנות באופן דינמי.
יתרה מזאת, המורכבות של המודרני תוכנה רובוטית לא ניתן להמעיט בחשיבותה. היא מקיפה מגוון רב של תחומים, כולל SLAM (מיקום ומיפוי סימולטניים), ניווט, תפיסה, אינטגרציה עם מערכות חיצוניות, מצבי רובוט ועוד. תחומים אלו כרוכים לעתים קרובות בעבודה משותפת של צוותים מרובים, שכל אחד מהם מספק באופן מתמשך קוד שחייב לפעול כראוי בסביבות מגוונות. הבטחת אימות מתמשך של התוכנה שלנו היא, לפיכך, אתגר עצום בפני עצמו.
הבה נבחן את היתרונות של אינטגרציה זו ואת האופן שבו ניתן ליישמה בהצלחה בפועל, במיוחד בכל הנוגע לרובוטים המיועדים לניווט אוטונומי בסביבות מגוונות ודינמיות, שבהן בדיקה יסודית של התנהגותם היא הכרחית, ושיטות בדיקה מסורתיות לרוב אינן מספיקות.
כמה מילים על Gazebo וסימולציות Gazebo
Gazebo הוא כלי סימולציה עוצמתי שממלא תפקיד מהותי בתחום הרובוטיקה, במיוחד בשילוב עם ה- Robot Operating System (ROS). הוא משמש כסביבת סימולציה דינמית ורבגונית עבור רובוטים, ומאפשר למהנדסים ולחוקרים ליצור ולבדוק את המערכות הרובוטיות שלהם באופן וירטואלי, וזאת מבלי לזנק שוב ושוב למעבדה. בעצם, Gazebo הוא פתרון שחוסך זמן, ומאפשר למפתחים לבצע איטרציות ולשפר את היצירות שלהם בעולם מדומה נטול סיכונים.

אחד המאפיינים הבולטים של Gazebo הוא האינטגרציה החלקה שלו עם ROS, המסגרת הנפוצה ביותר לפיתוח תוכנת רובוטים. אינטגרציה זו מאפשרת לרובוטיקאים לנצל את מלוא העוצמה של ROS בסביבת הסימולציה. Gazebo משמשת כגשר בין העולמות הפיזי והווירטואלי, מה שהופך אותה לכלי חיוני לאב-טיפוס, לבדיקה ולשיפור של מערכות רובוטיות. היא מאפשרת למהנדסים להתנסות בעיצובי רובוטים שונים, לכוונן אלגוריתמי בקרה ולאמת מערכות תפיסה, וכל זאת תוך כדי הנאה ממערכת האקוסיסטם הענפה של חבילות וספריות ROS.
ללא ספק, אחד ההיבטים היקרים ביותר שראוי לציין הוא האופי בקוד פתוח שלGazebo. בסיס זה מטפח קהילה משגשגת של חובבים, מספק דוגמאות שימוש מעשיות ומקדם שיתוף פעולה. סביבה שיתופית זו לא רק משפרת את הנגישות של כלים אלה אלא גם מאיצה את קצב הפיתוח הרובוטי. יתרה מכך, האינטגרציה החלקה עם ROS הופכת את Gazebo לבחירה אידיאלית עבור יישומי רובוטיקה, ומאפשרת למשתמשים ליצור רובוטים וירטואליים, לדמות חיישנים ולעסוק במגוון רחב של פעילויות הקשורות לרובוטיקה המונעות על ידי דיוק ויעילות.

האם באמת נדרש CI לפיתוח תוכנה ברובוטיקה?
זו הייתה רק שאלה רטורית כדי להעיר אותך 😉 אבל ברצינות רבה יותר, ל-CI ברובוטיקה אכן תפקיד יסודי זהה לזה שהוא ממלא בפרויקטי תוכנה אחרים. הוא מספק משוב מהיר לצוות הפיתוח, שומר על איכות התוכנה באמצעות בדיקות אוטומטיות, ניהול גרסאות ומאפשר פריסות אוטומטיות. בדומה לכל פרויקט, אחד היעדים העיקריים שלנו הוא למנוע רגרסיה. בעצם, אנו שואפים למנוע מהרובוט שלנו לאבד באופן בלתי צפוי יכולת כלשהי, ולהפוך את תהליך האספקה ליציב וחזרתי.
עם זאת, ההבדל טמון במורכבות הרובוטיקה. בתחום זה, אנו מתמודדים עם שפע של תרחישים פוטנציאליים שעלולים להתעורר כאשר רובוט מקיים אינטראקציה עם סביבה דינמית ומשתנה תדיר. שלא כמו תוכנה מסורתית, רובוטים חייבים להסתגל לתנאי העולם האמיתי, מה שמוסיף שכבה נוספת של אתגר. CI ברובוטיקה אינו עוסק רק בשמירה על איכות הקוד; הוא עוסק בהבטחה שהתנהגות הרובוט תישאר עקבית ואמינה בתוך שלל מצבים אפשריים. לכן, בעוד ש-CI משרת מטרה דומה, חשיבותו ברובוטיקה נעשית בולטת אף יותר בשל המורכבויות של התחום.
חקירת הסינרגיה: שילוב סימולציות Gazebo ב-CI של רובוטיקה
הסינרגיה בין Gazebo ל-CI היא קריטית מכיוון שהיא משלבת את היתרונות של בדיקה ריאלית בסביבות הווירטואליות של Gazebo עם היעילות של בדיקה אוטומטית באמצעות CI. אינטגרציה זו מעצימה רובוטיקאים לאמת את המערכות שלהם באופן מקיף ויעיל, ומבטיחה שרובוטים יוכלו לפעול באופן אמין לנוכח האתגרים הרבים שמציג העולם האמיתי.
יצירת בדיקות עם סביבת סימולציות Gazebo מסייעת לנו להימנע משגיאות בלתי צפויות שעלולות להתרחש בכל שלב של פרויקט. הדבר חשוב מכיוון שפרויקטים אלה כוללים לעתים קרובות מספר שכבות של מורכבות. קחו, למשל, רכיבי ניווט או מיפוי כגון SLAM (מיקום ומיפוי סימולטניים). בעוד שאלה עשויים לפעול כצמתים נפרדים, לעתים קרובות הם פועלים יחד להשגת מטרה משותפת.
עם CI, אנו משיגים את היכולת לבצע בדיקות מרובות בו-זמנית בסביבה מבודדת (מכולת Docker). דוגמה אחת הממחישה את היתרון של גישה זו היא שיפור האלגוריתמים המשמשים ברובוטיקה. עם כל איטרציה או עדכון של אלגוריתם נתון, אנו מצפים לראות ביצועים משופרים. הרצת מערך בדיקות בו-זמנית מאפשרת לנו להעריך האם אלגוריתם חדש עולה על קודמו מבחינת אפקטיביות. יתרה מזאת, באמצעות שילוב Gazebo אנו מקבלים תובנה כיצד אלגוריתמים אלו מתפקדים לא רק בבדיקות סינתטיות אלא גם בעולם הווירטואלי.
שילוב Gazebo בצינור CI רובוטי: סקירה טכנית מפורטת
מה אנחנו בודקים ומה אנחנו מצפים
במקרה שימוש זה, האינטגרציה שלנו של Gazebo עם CI מודגמת באמצעות הדוגמה של הרובוט האוטונומי שלנו, אווטארבקצרה, רובוט ה-Avatar עובר שלב ראשוני של חקר החדר כדי להשיג מפה של סביבתו. מפה זו משמשת כבסיס לפעילויות נוספות, כולל ניווט ועוד.
התוכנה בצד הרובוט נבנתה באמצעות ROS2, עם יישום של תחומים רלוונטיים דרך צמתים, כגון SLAM, ניווט, תפיסה, סטטוס רובוט, מצב רובוט ואינטגרציה בין הרובוט לענן, תוך הבטחת הפונקציונליות שהוזכרה קודם לכן. בנוסף, השתמשנו בטכנולוגיות ובמבנים המוצגים להלן.
תרשים מוצג ממחיש את שלבי ה-CI הפשוטים שיושמו עבור חלק זה של הפרויקט:

במאמר זה, נתבונן מקרוב בשלב הסימולציות ב-Gazebo. נחקור גם אחד מתרחישי הבדיקה שהופעלו בסימולטור שלנו. באמצעות כך, נתוודע לתבנית שניתן להשתמש בה כדי להוסיף תרחישים אחרים. בדוגמה שלנו, תרחיש הבדיקה יתמקד בשלב החקירה של הרובוט.
כדי להבין טוב יותר את התרחיש, ניתן לעיין בשלוש התמונות המופיעות להלן. התמונה הראשונה מציגה את תחילת החקירה, שבה המפה מתגלה רק באופן חלקי.
בתמונה השנייה, ניתן לראות את תהליך החקירה המתמשך, כפי שמצוין על ידי האריחים הירוקים עלRViz, תוך סימון האזורים שנחקרו עד לנקודה זו.
התמונה האחרונה מציגה את שיאו של תהליך החקירה, כאשר כל השדות ב-RViz מוצגים כעת בירוק, מה שמעיד על גילוי מלא של האזור הממופה. כעת, לרובוט שלנו יש ידע מדויק על מידות החדר ועל מיקומי הקירות או המכשולים – השלמנו את החקירה. הצעד הבא עבור ה-Avatar הוא לחפש הדגמות בחדר ולאחר מכן לנווט אליהן. השילוב של CI וסימולציות Gazebo יעזור לנו לייעל ולתקן באגים באלגוריתם החקירה, תוך הבטחה שהרובוט שלנו יוכל לעבור בין חדרים שונים מבלי להילכד בין מכשולים או להיתקע בקיר.



כמה מילים על סימולציות Gazebo והכנה ל-CI
כדי להפעיל את Gazebo בסביבת אינטגרציה רציפה (CI), חיוני להבין כיצד Gazebo פועל. Gazebo מורכב משני מודולים עיקריים: 'gzclient' ו-'gzserver'. 'gzclient' מטפל בעיקר בהיבט הוויזואליזציה של הסימולציה על החומרה שלנו. כפי שניתן לנחש, מודול זה לא יידרש למטרות CI.
הרכיב החיוני להגדרת ה-CI שלנו הוא 'gzserver', הפועל במצב headless ללא ממשק גרפי. מודול זה אחראי לביצוע סימולציית העולם כולה, והוא ישמש בתהליך הבדיקות שלנו.
מסגרות עבודה שבהן נשתמש
התוכנית שנועדה להפעיל את Gazebo, יחד עם כל הצמתים (nodes), כתובה בתסריט Python (launcher). מפעיל מבוסס Python זה מייעל את ניהול התהליכים במהלך הבדיקות. פרויקט Avatar פותח באמצעות מסגרת ROS2 בגרסת Galactic שלה, מה שהעניק לנו גמישות בבחירת שפת תכנות לצרכינו.
לצורך כתיבת בדיקות, בחרתי להשתמש ב-C++ יחד עם מסגרת הבדיקות של Google. עם זאת, אין מגבלות המונעות מאיתנו להשתמש בחלופות כגון Python או לאמץ מסגרת בדיקות אחרת למשימה זו.
בדיקת יישום
כדי לבדוק את זרימת העבודה שלנו, עלינו להפעיל מפעיל מבוסס Python.
משגר זה אחראי להפעלת צמתים השולטים בהיבטים שונים של הרובוט, כגוןNavigation2 לתנועת רובוטים ו- Slam Toolbox למיפוי חללים.
בנוסף, הוא מפעיל עולם וירטואלי ב-Gazebo. להלן תמצאו את הקוד עבור מפעיל זה, יחד עם תיאורים קצרים של הפונקציות שלו:
# מפעיל עם עולם וירטואלי של gazebo
def launchers():
return [
IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(get_package_share_directory('virtual_world'), 'launch', 'avatar_launch.py')),
launch_arguments={x: LaunchConfiguration(x) for x in (
'x_pose', 'y_pose', 'world', 'use_rviz')}.items(),
condition=IfCondition(LaunchConfiguration('use_virtual_world')))
]
# ארגומנטים של מפעיל
def launch_args():
launcher_root = get_package_share_directory('avatar_robot')
return [
# ארגומנטים ברירת מחדל
DeclareLaunchArgument(
'params_file',
default_value=os.path.join(
launcher_root, 'params', 'sim_diamond.yaml'), # פרמטרים לסימולציה
description='נתיב מלא לקובץ הפרמטרים של ROS2 לשימוש בכל הצמתים המופעלים'),
DeclareLaunchArgument(
'use_virtual_world',
default_value='True',
description='הפעל מפעיל עולם וירטואלי'),
DeclareLaunchArgument(
'use_gzclient',
default_value='False', # אל תשתמש ב-gzclient - נדרש רק gzserver
description='הפעל לקוח Gazebo'),
DeclareLaunchArgument(
'use_rviz',
default_value='True',
description='הפעל RViz'),
DeclareLaunchArgument('world', default_value='diamond_demos'), # שם העולם
DeclareLaunchArgument('x_pose', default_value='2.10'), # מיקום התחלתי של הרובוט (x)
DeclareLaunchArgument('y_pose', default_value='1.62'), # מיקום התחלתי של הרובוט (y)
]
def generate_launch_description():
params_file = LaunchConfiguration('params_file')
custom_map_path = LaunchConfiguration('custom_map_path')
return LaunchDescription(
launch_args() + [
cmd_slam_bringup(params_file),
cmd_nav2_bringup(params_file, custom_map_path),
cmd_nav_node(params_file),
cmd_visualization_node(params_file),
cmd_calc_node(params_file),
cmd_exploration_node(params_file),
] + launchers()
)
כדי להריץ את המשגר הזה בבדיקה שלנו, ניצור עטיפת C++ פשוטה לניהולו:
class ProcessManager {
public:
// סיום התהליך אם הוא פועל
~ProcessManager() {
if (m_pid.has_value()) {
killProcess();
}
}
// בדיקה האם התהליך פועל
bool isProcessRunning() const {
return m_pid.has_value() && kill(m_pid.value(), 0) == 0;
}
// סיום התהליך והמתנה לו
bool killProcess(int sig = SIGINT) {
if (m_pid.has_value() == false) {
return true;
}
kill(m_pid.value(), sig);
waitpid(m_pid.value(), nullptr, 0);
const bool isStillRunning = isProcessRunning();
m_pid.reset();
return isStillRunning;
}
// פיצול התוכנית והפעלת תהליך חדש
template<typename ... Args>
pid_t runProcess(const char * program, Args ... args) {
static_assert(std::conjunction_v<std::is_same<const char *, Args>...>);
if (m_pid.has_value()) {
return m_pid.value();
}
const pid_t pid = fork();
if (pid == 0) {
execlp(program, program, args ..., nullptr);
}
m_pid = pid;
return pid;
}
private:
std::optional<pid_t> m_pid;
};
לאחר שהגדרנו את הארגומנטים הנדרשים עבור מפעיל זה, נוכל להפעיל אותו בקוד:
// נושא סטטוס חקר static constexpr auto avatarTilesTopic = "avatar_tiles"; // פסקי זמן static constexpr std::int64_t LOOP_TIMEOUT_IN_SEC = 5; // 5 שניות // החקר אמור להימשך עד 3 דקות static constexpr std::int64_t EXPLORATION_TIMEOUT_IN_SEC = 3 * 60; // 3 דקות // מיקום התחלתי אקראי של הרובוט (חייב להיות חוקי) const auto randomPosition = RobotValidPositions.getRandomPoint(); // ארגומנטים של המשגר const std::string xStartRobArg = "x_pose:=" + std::to_string(randomPosition.x); const std::string yStartRobArg = "y_pose:=" + std::to_string(randomPosition.y); const std::string worldArg = "world:=" + worldName; const std::string rvizArg = "use_rviz:=False"; // מנהל המשגר test_utils::ProcessManager avatarLauncher; // הפעלת צומת avatar avatarLauncher.runProcess( "ros2", "launch", "avatar_robot", "simulation_launch.py", rvizArg.c_str(), worldArg.c_str(), xStartRobArg.c_str(), yStartRobArg.c_str()); // בדיקה אם התהליך פועל ASSERT_TRUE(avatarLauncher.isProcessRunning()) << "Cannot start avatar launcher";
השלב הבא הוא יישום בדיקת סטטוס חקר. הרובוט מפרסם באופן שיטתי הודעות בנושא "avatar_tiles" כדי לדווח על התקדמות החקר שלו. כדי להשיג זאת, נצטרך לכתוב מנוי node פשוט האחראי לניטור סטטוס החקר:
// יש להגדיר כ-true אם יש לסיים את כל התהליכים
std::atomic_bool stopThreads = false;
// אתחול ros
rclcpp::init(0, nullptr);
// בדיקה האם הסריקה הושלמה
std::atomic_bool isExplorationFinished = false;
auto processTiles = [&](const tiles_avatar_msgs::msg::Tiles & tiles) {
// בדיקה האם כל האריחים עובדו
if (std::all_of(
std::begin(tiles.tiles), std::end(tiles.tiles),
[](decltype(tiles.tiles)::value_type tile) {
return static_cast<utils::TileState>(tile.state) == utils::TileState::eProcessed;
})) {
// אם כל האריחים עובדו, הסריקה הושלמה
isExplorationFinished = true;
}
};
// יצירת צומת ROS ורישום לנושא
auto temporaryNode = std::make_shared<rclcpp::Node>("test_scan_entire_room_node");
auto subscription = rclcpp::create_subscription<tiles_avatar_msgs::msg::Tiles>(
temporaryNode, avatarTilesTopic, 10, processTiles);
בשלב זה, אנו פשוט ממתינים להשלמת החקירה ובוחנים האם תוצאות הבדיקה עומדות בציפיות שלנו:
// המתנה לסיום החקר std::future<void> explorationWaitFuture = std::async( std::launch::async, [&]() { while (isExplorationFinished != true && stopThreads == false) { std::this_thread::sleep_for(std::chrono::seconds{LOOP_TIMEOUT_IN_SEC}); } }); // כיבוי צומת ה-ROS לאחר השלמת החקר std::future<void> rosShutdown = std::async( std::launch::async, [&]() -> void { explorationWaitFuture.wait_for(std::chrono::seconds{EXPLORATION_TIMEOUT_IN_SEC}); rclcpp::shutdown(); }); // סיבוב הצומת עד שהחקר פועל rclcpp::spin_until_future_complete( temporaryNode, explorationWaitFuture, std::chrono::seconds{EXPLORATION_TIMEOUT_IN_SEC}); // בדיקה אם חרג מזמן הקצוב const bool isTimeout = std::future_status::timeout == explorationWaitFuture.wait_for( std::chrono::seconds( 0)); EXPECT_FALSE(isTimeout) << "After " << EXPLORATION_TIMEOUT_IN_SEC << " seconds, the robot has not finished exploration"; // בדיקה אם החקר הצליח EXPECT_TRUE(isExplorationFinished) << "Robot cannot finish exploration."; // הריגת המשגר avatarLauncher.killProcess(); // בדיקה אם המשגר נהרג EXPECT_FALSE(avatarLauncher.isProcessRunning()) << "Can't stop avatar launcher"; // המתנה לסיום כל המשימות האסינכרוניות stopThreads = true; explorationWaitFuture.wait(); rosShutdown.wait();
לבסוף, אנו מסיימים בקריאה לפונקציות במסגרת GoogleTest:
TEST(test_exploration, test_scan_rectangle_demos) { // Create robot starting position (valid) test_utils::ValidPosition RobotValidPositions; RobotValidPositions.addBox(Point{1.12, 4.54}, Point{1.64, 0.59}); RobotValidPositions.addBox(Point{2.32, 4.13}, Point{3.8, 1.11}); RobotValidPositions.addBox(Point{3.92, 4.72}, Point{4.61, 3.65}); RobotValidPositions.addBox(Point{3.51, 1.88}, Point{4.52, 0.43}); // Start test for "rectangle_demos" world run_scan_test("rectangle_demos", RobotValidPositions); }
הגדרת צינור ה-CI
לאחר שהכנו את הבדיקות, בכוונתנו להעלות את השינויים שלנו למאגר כדי לאמת את תפקודם; לכן, הגיע הזמן להגדיר את ה-CI שלנו ב-GitLab. השלב הראשוני כרוך בבחירת image — הבחירה האופטימלית היא כזו שמותקנות בה מראש תלויות כמו ROS או Gazebo כדי למנוע התקנות חוזרות. במקרה שלנו, יש לנו image מוכן מראש המאוחסן בשרת:
default: image: synergy/ros_galactic:3.2
כמו בתרשים הזרימה של CI, הוא יכלול 5 שלבים:
stages: - static_code_analysis - build - test - simulation_test - deploy
לכל שלב יש משימה מעט שונה, אך הבה נתמקד בזו שמעניינת אותנו ביותר – simulation_test:
simulation_test: stage: simulation_test script: - . install/setup.sh - colcon test --packages-up-to exploration_sim - colcon test-result --all artifacts: when: always expire_in: '1 day' paths: - build/**/test_results/**/*.xml - install/ reports: junit: build/**/test_results/**/*.xml
בשלב זה נבדקת חבילה בודדת – חקר – באמצעות סימולציות Gazebo. בנוסף, היומנים (logs) שנוצרים במהלך הבדיקה יישמרו ב-GitLab למשך יום אחד.
שימוש ב-GitLab, ROS ו-CI מפשט באופן משמעותי את תהליך ייזום ואיסוף הבדיקות.
תוצאת בדיקה
לאחר היישום, יש לאמת את תקינות הבדיקה שלנו ולהעריך האם הרובוט עבר אותה בהצלחה. לשם כך, אנו מעלים את הקוד למאגר ה-Git שלנו וממתינים לתוצאות הבדיקה. כפי שממחישים הלוגים המוצגים להלן, הרובוט שלנו עבר את הבדיקות בהצלחה, כאשר החקירה הושלמה תוך 113 שניות.
התחל 1: test_scan_entire_room
1: פקודת בדיקה: /usr/bin/python3 "-u" "/opt/ros/galactic/share/ament_cmake_test/cmake/run_test.py" "/builds/robotics/avatar/avatar_robot/build/test_scan_entire_room/test_results/test_scan_entire_room/test_scan_entire_room.gtest.xml" "--package-name" "test_scan_entire_room" "--output-file" "/builds/robotics/avatar/avatar_robot/build/test_scan_entire_room/ament_cmake_gtest/test_scan_entire_room.txt" "--command" "/builds/robotics/avatar/avatar_robot/build/test_scan_entire_room/test_scan_entire_room" "--gtest_output=xml:/builds/robotics/avatar/avatar_robot/build/test_scan_entire_room/test_results/test_scan_entire_room/test_scan_entire_room.gtest.xml"
1: פסק זמן של בדיקה מחושב להיות: 2700
1: -- run_test.py: מפעיל את הפקודה הבאה ב-'/builds/robotics/avatar/avatar_robot/build/test_scan_entire_room':
1: - /builds/robotics/avatar/avatar_robot/build/test_scan_entire_room/test_scan_entire_room --gtest_output=xml:/builds/robotics/avatar/avatar_robot/build/test_scan_entire_room/test_results/test_scan_entire_room/test_scan_entire_room.gtest.xml
1: מפעיל main() מתוך /opt/ros/galactic/src/gtest_vendor/src/gtest_main.cc
1: [==========] מפעיל בדיקה 1 מתוך חבילת בדיקות 1.
1: [----------] הגדרת סביבת בדיקה גלובלית
1: [----------] בדיקה 1 מתוך test_exploration
1: [ RUN ] test_exploration.test_scan_rectangle_demos
1: [INFO] [launch]: כל קבצי היומן נמצאים תחת /root/.ros/log/2023-09-25-13-00-42-781057-runner-pyozp9em-project-81-concurrent-0jp5zf-718
1: [INFO] [launch]: רמת פירוט ברירת המחדל של רישום יומן מוגדרת ל-INFO
... ... ...
1: [INFO] [controller_server-6]: התהליך הסתיים באופן תקין [pid 743]
1: [INFO] [bt_navigator-9]: התהליך הסתיים באופן תקין [pid 749]
1: [INFO] [gzserver-19]: התהליך הסתיים באופן תקין [pid 909]
1: [ OK ] test_exploration.test_scan_rectangle_demos (113240 ms)
1: [----------] בדיקה 1 מתוך test_exploration (סה"כ 113240 ms)
1: [----------] פירוק סביבת בדיקה גלובלית
1: [==========] בדיקה 1 מתוך חבילת בדיקות 1 הורצה. (סה"כ 113240 ms)
1: [ PASSED ] בדיקה 1.
1: -- run_test.py: קוד החזרה 0
1: -- run_test.py: הזרקת קידומת שם מחלקה לקובץ תוצאת gtest '/builds/robotics/avatar/avatar_robot/build/test_scan_entire_room/test_results/test_scan_entire_room/test_scan_entire_room.gtest.xml'
1: -- run_test.py: אימות קובץ תוצאה '/builds/robotics/avatar/avatar_robot/build/test_scan_entire_room/test_results/test_scan_entire_room/test_scan_entire_room.gtest.xml'
1/5 בדיקה #1: test_scan_entire_room ............ עברה 113.29 sec
סיכום היישום
המימוש המוצג כאן מדגים שיטה מהירה ופשוטה לשילוב סימולציות Gazebo עם CI. הודות לכך, אנו יכולים להגן על הפרויקט שלנו מפני רגרסיה.
היבט בולט אחד של יישום זה הוא יכולת ההסתגלות שלו. ניתן לשנות אותו בקלות כדי להתאים לתרחישים שונים, כולל הוספת מקרי בדיקה חדשים, דגמי רובוטים שונים או עולמות וירטואליים חלופיים. כדי להכניס מגוון לבדיקות, אנו יכולים להשתמש במשתנים אקראיים, כגון שינוי במיקום ההתחלתי של הרובוט, ובכך להבטיח שכל בדיקה תהיה שונה במקצת מהקודמת.
בנוסף, שקלו להשתמש ב-rosbag להקליט נושאים. יכולת הקלטה זו יכולה להיות בעלת ערך רב במקרה של שגיאה, מכיוון שהיא מאפשרת השמעה וניתוח בקלות—משאב שימושי במיוחד כאשר מתמודדים עם בעיות ספורדיות או נדירות שיש לשחזר ולחקור.
GitLab מאפשר לכם לאחסן ארטיפקטים עבור צינור (pipeline), ובכך לאפשר, כפי שהודגם ב-'CI Integration', לא רק לאחסן דוחות gtest אלא גם לאחסן רשומות rosbag.
תקציר
היתרונות של חיבור Gazebo ל-CI
שילוב CI עם Gazebo מקנה לנו יכולת עוצמתית לפתח בדיקות המשמשות כמגן מפני רגרסיה בפרויקט. סינרגיה זו מאפשרת לנו להעריך בו-זמנית תרחישים רבים ללא צורך בחומרה פיזית. אנו יכולים אף להריץ בדיקות לילה החוקרות מקרי קצה, כגון שינוי מיקומי ההתחלה של הרובוט, סימולציות Gazebo המציעות סביבות וירטואליות שונות, או אף שימוש במודלים שונים של רובוטים בסביבות שונות.
אחד היתרונות החשובים ביותר של גישה זו הוא עלותה האפקטיבית. בסביבת צוות שיתופית, איננו נדרשים עוד לרובוט פיזי עבור כל חבר צוות, מה שמתורגם לחיסכון משמעותי ולאופטימיזציה של משאבים עבור הפרויקט.
התוכנית לשילוב CI עם Gazebo נותרת אנלוגית לפרויקטים רבים אחרים. הדוגמה הטובה ביותר היא רחפנים, שגם אותם Gazebo תומך. היכולות של Gazebo מגדירות בעיקר את המגבלות שלנו, והן רחבות באופן מרשים בהיקפן.
מחפשים מומחי רובוטיקה?
גלו כיצד אנו יכולים לתמוך במוצר הבא שלכם – עיינו ב שירותי רובוטיקה, ואל תהססו ליצור קשר בכל שאלה.
שאלות נפוצות
אינטגרציה רציפה ברובוטיקה היא הנוהג של בנייה, בדיקה ואימות אוטומטיים של תוכנת רובוט בכל פעם שמוכנסים שינויים. צינורות CI מאפשרים לצוותים לזהות בעיות אינטגרציה בשלב מוקדם ולאמת את התנהגות הרובוט באמצעות בדיקות אוטומטיות וסימולציות.
סימולציות מאפשרות לצוותי רובוטיקה לבדוק התנהגות ללא צורך בחומרה פיזית. כלים כגון Gazebo מאפשרים להריץ תרחישים ניתנים לשחזור בצינור CI, ובכך לאפשר אימות אוטומטי של אלגוריתמי ניווט, חקר או תפיסה.
Gazebo מספק סביבת סימולציה ריאליסטית לרובוטים וחיישנים. כאשר הוא משולב בצינור CI, סימולציות Gazebo יכולות להריץ תרחישי בדיקה באופן אוטומטי ולדווח על תוצאות, ולסייע לצוותים לאמת שינויים בתוכנה לפני הפריסה.
צינורות CI משפרים את האמינות ואת מהירות הפיתוח. בנייה, בדיקות וסימולציות אוטומטיות מסייעות לזהות בעיות בשלב מוקדם, להפחית את המאמץ הידני בבדיקות ולהבטיח שתוכנת הרובוטיקה מתנהגת כראוי לאורך עדכונים שונים.
arrow_circle_rightצרו קשר
שנו את התעשייה שלכם עם פתרונות רובוטיקה. צרו קשר עם המומחה שלנו כדי לגלות עוד
arrow_circle_right מאמרים נוספים