الارتقاء بالتكامل المستمر للروبوتات باستخدام محاكيات Gazebo: دراسة حالة
في مجال الروبوتات، حيث تكون الدقة والكفاءة أمرًا بالغ الأهمية، تلعب الأدوات والتقنيات دورًا مهمًا في تحسين المشاريع. ومع ذلك، تكمن إحدى أهم التحديات في ضمان جودة وموثوقية البرمجيات، لا سيما في سياق المشاريع المعقدة مثل الروبوتات القادرة على اتخاذ قرارات ذاتية في بيئات غير معروفة ومتغيرة ديناميكيًا.
علاوة على ذلك، فإن تعقيد البرمجيات الروبوتية لا يمكن التقليل من أهميته. فهو يشمل مجموعة واسعة من المجالات، بما في ذلك SLAM (التوطين ورسم الخرائط المتزامن)، والملاحة، والإدراك، والتكامل مع الأنظمة الخارجية، وحالات الروبوت، وغير ذلك. غالبًا ما تتضمن هذه المجالات فرقًا متعددة تعمل بشكل تعاوني، وكل منها يسلّم باستمرار شيفرة برمجية يجب أن تعمل بشكل صحيح في بيئات متنوعة. وبالتالي، فإن ضمان التحقق المستمر من برمجياتنا يمثل بحد ذاته تحديًا هائلًا.
لنستكشف فوائد هذا التكامل وكيف يمكن تنفيذه بنجاح على أرض الواقع، خاصة عند التعامل مع الروبوتات المصممة للملاحة الذاتية في بيئات متنوعة وديناميكية، حيث يكون الاختبار الشامل لسلوكها أمرًا بالغ الأهمية، وغالبًا ما تقصر طرق الاختبار التقليدية عن تحقيق ذلك.
بضع كلمات عن Gazebo ومحاكاة Gazebo
Gazebo هي أداة محاكاة قوية تلعب دورًا أساسيًا في مجال الروبوتات، لا سيما بالاقتران مع نظام تشغيل الروبوتات (ROS). يعمل كبيئة محاكاة ديناميكية ومتعددة الاستخدامات للروبوتات، مما يمكّن المهندسين والباحثين من إنشاء واختبار أنظمتهم الروبوتية افتراضيًا، كل ذلك دون الحاجة إلى سباقات لا نهاية لها إلى المختبر. في جوهره، Gazebo هو حل موفّر للوقت، يسمح للمطورين بالتكرار وتحسين إبداعاتهم في عالم محاكاة خالٍ من المخاطر.

من أبرز مزايا Gazebo تكامله السلس مع ROS، الإطار المعتمد على نطاق واسع لتطوير برمجيات الروبوتات. يتيح هذا التكامل لعلماء الروبوتات الاستفادة من كامل قوة ROS داخل البيئة المحاكاة. يعمل Gazebo كجسر بين العالمين المادي والافتراضي، مما يجعله أداة أساسية للنمذجة الأولية والاختبار وتحسين الأنظمة الروبوتية. فهو يمكّن المهندسين من تجربة تصاميم روبوتات مختلفة، وضبط خوارزميات التحكم بدقة، والتحقق من أنظمة الإدراك، مع الاستفادة في الوقت نفسه من منظومة ROS الواسعة من الحزم والمكتبات.
مما لا شك فيه أن أحد أكثر الجوانب قيمةً التي تستحق الذكر هو الطبيعة مفتوحة المصدر لـGazebo. تعزز هذه الأساس مجتمعًا مزدهرًا من المتحمسين، وتوفر أمثلة عملية للاستخدام، وتشجع التعاون. ولا تعزز هذه البيئة التعاونية إمكانية الوصول إلى هذه الأدوات فحسب، بل تسرّع أيضًا وتيرة تطوير الروبوتات. علاوة على ذلك، يجعل التكامل السلس مع ROS من Gazebo خيارًا مثاليًا لتطبيقات الروبوتات، مما يمكّن المستخدمين من إنشاء روبوتات افتراضية ومحاكاة المستشعرات والمشاركة في مجموعة واسعة من الأنشطة المتعلقة بالروبوتات التي تحركها الدقة والكفاءة.

هل نحتاج حقًا إلى التكامل المستمر (CI) لتطوير البرمجيات في مجال الروبوتات؟
كان ذلك مجرد سؤال بلاغي لإيقاظك 😉 ولكن، بجدية أكبر، تؤدي التكامل المستمر (CI) في مجال الروبوتات بالفعل نفس الدور الجوهري الذي تؤديه في مشاريع البرمجيات الأخرى. فهي توفر تغذية راجعة سريعة لفريق التطوير، وتحافظ على جودة البرمجيات من خلال الاختبارات الآلية وإدارة الإصدارات، وتسهّل عمليات النشر الآلي. وكما هو الحال في أي مشروع، يتمثل أحد أهدافنا الأساسية في تجنب التراجع. في جوهر الأمر، نهدف إلى منع روبوتنا من فقدان أي من قدراته بشكل غير متوقع، وجعل عملية التسليم مستقرة وقابلة للتكرار.
ومع ذلك، يكمن الفرق في تعقيد مجال الروبوتات. في هذا المجال، نتعامل مع وفرة من السيناريوهات المحتملة التي قد تنشأ عندما يتفاعل الروبوت مع بيئة ديناميكية ومتغيرة باستمرار. وعلى عكس البرمجيات التقليدية، يجب أن تتكيف الروبوتات مع ظروف العالم الحقيقي، مما يضيف طبقة إضافية من التحدي. لا يقتصر التكامل المستمر (CI) في الروبوتات على الحفاظ على جودة الكود فحسب؛ بل يتعلق بضمان بقاء سلوك الروبوت متسقاً وموثوقاً وسط multitude من المواقف المحتملة. لذا، وبينما يخدم التكامل المستمر غرضاً مشابهاً، فإن أهميته في الروبوتات تصبح أكثر وضوحاً بسبب تعقيدات هذا المجال.
استكشاف التآزر: دمج محاكيات Gazebo في CI للروبوتات
يُعد التآزر بين Gazebo و CI أمرًا بالغ الأهمية لأنه يجمع بين فوائد الاختبار الواقعي في بيئات Gazebo الافتراضية وكفاءة الاختبار الآلي من خلال CI. يمكّن هذا التكامل خبراء الروبوتات من التحقق من أنظمتهم بشكل شامل وفعّال، مما يضمن قدرة الروبوتات على العمل بشكل موثوق في مواجهة التحديات العديدة التي يفرضها العالم الحقيقي.
يساعدنا إنشاء الاختبارات باستخدام بيئة محاكاة Gazebo على تجنب الأخطاء غير المتوقعة التي قد تحدث في أي مرحلة من مراحل المشروع. وهذا مهم لأن هذه المشاريع غالبًا ما تتضمن طبقات متعددة من التعقيد. خذ على سبيل المثال مكونات الملاحة أو رسم الخرائط مثل SLAM (التوطين ورسم الخرائط المتزامن). ففي حين قد تعمل هذه كمكونات منفصلة، إلا أنها غالبًا ما تعمل معًا لتحقيق هدف مشترك.
مع CI، نكتسب القدرة على إجراء اختبارات متعددة في وقت واحد في بيئة معزولة (حاوية Docker). ومن الأمثلة التي تُظهر فائدة هذا النهج تحسين الخوارزميات المستخدمة في الروبوتات. مع كل تكرار أو تحديث لخوارزمية معينة، نتوقع رؤية أداء متزايد. ويتيح لنا تشغيل مجموعة من الاختبارات في وقت واحد تقييم ما إذا كانت الخوارزمية الجديدة تتفوق على سابقتها من حيث الفعالية. والأكثر من ذلك، باستخدام تكامل Gazebo، نكتسب رؤية حول كيفية أداء هذه الخوارزميات ليس فقط في الاختبارات الاصطناعية بل أيضًا في العالم الافتراضي.
دمج Gazebo مع خط أنابيب التكامل المستمر للروبوتات: دليل تقني تفصيلي
ما نختبره وما نتوقعه
في حالة الاستخدام هذه، يتم عرض تكاملنا مع Gazebo مع التكامل المستمر من خلال مثال روبوتنا المستقل، الصورة الرمزيةباختصار، يخضع روبوت Avatar لمرحلة أولية من استكشاف الغرفة للحصول على خريطة لمحيطه. وتُعد هذه الخريطة الأساس لمزيد من الأنشطة، بما في ذلك التنقل والمزيد.
صُمّم البرنامج على جانب الروبوت باستخدام ROS2، مع تنفيذ المجالات ذات الصلة عبر العُقد، مثل SLAM والملاحة والإدراك وحالة الروبوت ووضع الروبوت والتكامل بين الروبوت والسحابة، بما يضمن الوظائف المذكورة سابقًا. وبالإضافة إلى ذلك، استخدمنا التقنيات والهياكل الموضحة أدناه.
يوضح الرسم البياني المقدم خطوات CI المباشرة المنفذة لهذا الجزء من المشروع:

في هذه المقالة، سنلقي نظرة فاحصة على خطوة محاكاة Gazebo. سنستكشف أيضاً أحد سيناريوهات الاختبار المنفذة في المحاكي الخاص بنا. ومن خلال ذلك، سنتعرف على قالب يمكن استخدامه لإضافة سيناريوهات أخرى. في مثالنا، سيركز سيناريو الاختبار على مرحلة استكشاف الروبوت.
لفهم السيناريو بشكل أفضل، يمكنك الرجوع إلى الصور الثلاث المقدمة أدناه. تُظهر الصورة الأولى بداية الاستكشاف، حيث تكون الخريطة مكتشفة جزئيًا فقط.
في الصورة الثانية، يمكنك ملاحظة عملية الاستكشاف الجارية، كما تشير البلاطات الخضراء علىRViz، مع تحديد المجالات التي تم استكشافها حتى هذه النقطة.
تُظهر الصورة الأخيرة ذروة الاستكشاف، حيث أصبحت جميع الحقول في RViz معروضة باللون الأخضر، مما يشير إلى الاكتشاف الكامل للمنطقة المرسومة. الآن، أصبح لدى روبوتنا معرفة دقيقة بأبعاد الغرفة ومواقع الجدران أو العوائق – لقد أكملنا الاستكشاف. الخطوة التالية لـ Avatar هي البحث عن العروض التوضيحية في الغرفة ثم الانتقال إليها. سيساعدنا دمج CI ومحاكاة Gazebo في تحسين خوارزمية الاستكشاف وإصلاح الأخطاء فيها، مما يضمن قدرة روبوتنا على التنقل عبر غرف مختلفة دون أن يعلق بين العوائق أو ينحصر عند جدار.



بضع كلمات حول محاكيات Gazebo والتحضير للتكامل المستمر (CI)
لتشغيل Gazebo في بيئة التكامل المستمر (CI)، من الضروري فهم كيفية عمل Gazebo. يتكون Gazebo من وحدتين رئيسيتين: 'gzclient' و'gzserver'. يتولى 'gzclient' بشكل أساسي جانب العرض المرئي للمحاكاة على أجهزتنا. كما يمكنك التخمين، لن تكون هذه الوحدة مطلوبة لأغراض التكامل المستمر.
المكوّن الأساسي لإعداد التكامل المستمر (CI) لدينا هو 'gzserver'، الذي يعمل في وضع بلا واجهة رسومية (headless mode). هذه الوحدة مسؤولة عن تنفيذ محاكاة العالم بالكامل، وسيتم استخدامها في عملية الاختبار لدينا.
الأطر التي سنستخدمها
البرنامج المصمم لتشغيل Gazebo، junto مع جميع العقد، مكتوب بلغة Python (المشغّل). يبسّط هذا المشغّل القائم على 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" للإبلاغ عن تقدم استكشافه. ولتحقيق ذلك، سنحتاج إلى كتابة مشترك عقدة بسيط مسؤول عن مراقبة حالة الاستكشاف:
// اضبط على 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) { // إنشاء موضع بداية الروبوت (صالح) 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}); // بدء الاختبار لعالم "rectangle_demos" run_scan_test("rectangle_demos", RobotValidPositions); }
تهيئة خط أنابيب التكامل المستمر
بعد إعداد الاختبارات، نعتزم رفع تعديلاتنا إلى المستودع للتحقق من وظائفها؛ لذلك، حان الوقت لتهيئة التكامل المستمر (CI) لدينا على GitLab. تتضمن الخطوة الأولى اختيار صورة – والخيار الأمثل هو صورة مثبتة مسبقًا مع التبعيات مثل ROS أو Gazebo لتجنب عمليات التثبيت المتكررة. في حالتنا، لدينا صورة مُعدة مسبقًا مخزنة على الخادم:
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. بالإضافة إلى ذلك، سيتم تخزين السجلات المُنشأة أثناء الاختبار في 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: [----------] اختبار واحد من 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: [----------] اختبار واحد من test_exploration (الإجمالي 113240 ms)
1: [----------] إنهاء بيئة الاختبار العامة.
1: [==========] تم تشغيل اختبار واحد من مجموعة اختبار واحدة. (الإجمالي 113240 ms)
1: [ PASSED ] اختبار واحد.
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 تخزين المنتجات النهائية لخط الأنابيب، مما يتيح، كما هو موضح في 'تكامل CI'، ليس فقط تخزين تقارير gtest ولكن أيضًا تخزين سجلات rosbag.
الملخص
فوائد ربط Gazebo بالتكامل المستمر
يمنحنا دمج التكامل المستمر مع Gazebo قدرة قوية على تطوير اختبارات تعمل كضمانات ضد الانحدار في المشروع. يتيح لنا هذا التآزر تقييم العديد من السيناريوهات في وقت واحد دون الحاجة إلى أجهزة فعلية. بل يمكننا حتى تشغيل اختبارات ليلية تستكشف حالات قصوى، مثل تغيير المواضع الأولية للروبوت، أو محاكاة Gazebo التي توفر بيئات افتراضية مختلفة، أو حتى استخدام نماذج روبوتات مختلفة في بيئات متنوعة.
من أهم مزايا هذا النهج فعاليته من حيث التكلفة. ففي بيئة الفريق التعاونية، لم نعد بحاجة إلى روبوت فعلي لكل عضو في الفريق، وهو ما يُترجم إلى وفورات كبيرة وتحسين لاستخدام الموارد في المشروع.
تظل خطة دمج CI مع Gazebo مماثلة لمشاريع أخرى متنوعة. وأفضل مثال على ذلك الطائرات بدون طيار، التي يدعمها Gazebo أيضاً. تحدد قدرات Gazebo في المقام الأول حدودنا، وهي واسعة النطاق بشكل مثير للإعجاب.
هل تبحث عن متخصصين في الروبوتات؟
تعرّف على كيفية دعمنا لمنتجك القادم – اطّلع على خدمات الروبوتات، ولا تتردد في التواصل معنا في حال وجود أي أسئلة.
الأسئلة الشائعة
التكامل المستمر في الروبوتات هو ممارسة البناء والاختبار والتحقق من صحة البرمجيات الروبوتية تلقائياً كلما أُدخلت تغييرات. تتيح خطوط أنابيب التكامل المستمر للفرق اكتشاف مشكلات التكامل مبكراً والتحقق من سلوك الروبوت باستخدام الاختبارات الآلية والمحاكاة.
تتيح المحاكاة لفرق الروبوتات اختبار السلوك دون الحاجة إلى أجهزة مادية. أدوات مثل Gazebo تجعل من الممكن تشغيل سيناريوهات قابلة للتكرار في خط أنابيب CI، مما يتيح التحقق الآلي من خوارزميات الملاحة أو الاستكشاف أو الإدراك.
يوفر Gazebo بيئة محاكاة واقعية للروبوتات والمستشعرات. عند دمجه في خط أنابيب CI، يمكن لمحاكاات Gazebo تنفيذ سيناريوهات الاختبار تلقائياً والإبلاغ عن النتائج، مما يساعد الفرق على التحقق من تغييرات البرمجيات قبل النشر.
تحسّن خطوط أنابيب CI الموثوقية وسرعة التطوير. وتساعد عمليات البناء والاختبارات والمحاكاة الآلية في اكتشاف المشكلات مبكرًا، وتقليل جهد الاختبار اليدوي، وضمان سلوك برمجيات الروبوتات بشكل صحيح عبر التحديثات المختلفة.
arrow_circle_rightاتصل بنا
حوّل قطاعك بحلول الروبوتات. تواصل مع خبيرنا لمعرفة المزيد
arrow_circle_right مقالات أخرى