الأدلة التقنية

كيف تبني منصة قيادة وتحكم لأسطول طائرات مسيرة

كيف تجمع القياس عن بعد والسجلات والفيديو من طائرات مصنعين مختلفين في منصة واحدة تراقب منها الأسطول لحظة بلحظة.

كيف تبني منصة قيادة وتحكم لأسطول طائرات مسيرة

لنفرض أن جهتك تملك أسطولاً من الطائرات المسيرة، بعضها يعمل بالطيار الآلي PX4، وبعضها بـ ArduPilot، وبعضها من DJI مع محطة الإقلاع والهبوط الآلية DJI Dock، وطائرتان من شركة ثالثة لها سحابتها الخاصة، والمطلوب شاشة واحدة في غرفة العمليات ترى عليها كل طائرة في مكانها الآن، وتسمع منها التنبيه قبل أن تنفد البطارية، وترى فيديو الكاميرا مباشرة، ثم بعد الهبوط تجد سجل كل طيران وملف الفيديو الكامل في مكان واحد. وقد يبدو الأمر من الوهلة الأولى برنامجاً يقرأ من كل طائرة ويرسم على خريطة، ولكن في الواقع كل مصنع يتحدث لغة مختلفة، والرابط بين الطائرة والأرض يضعف وينقطع، والساعات لا تتفق، والفيديو وحده أثقل من كل شيء آخر مجتمعاً.

وهذا المقال هو الأول في سلسلة عن منصات عمليات الطائرات المسيرة:

  • كيف تبني منصة قيادة وتحكم لأسطول طائرات مسيرة (الموضوع الحالي): المعمارية والقرارات.
  • التجربة العملية: طائرات PX4 وArduPilot في محاكاة، وطائرة من «مصنع X» عبر MQTT، حتى الخريطة الحية وقياس تأخير الفيديو.

ونفترض أنك قرأت مقال برمجة الطائرات مع PX4 وMAVSDK الذي شرحنا فيه بروتوكول MAVLink، ومقال المنصات الذي عرفنا فيه مكتبات DJI الثلاث، فلن نعيد شرحهما هنا، وإنما نبني فوقهما.

وسوف نناقش في هذا المقال ما يلي:

  • أنواع البيانات الثلاثة التي تأتي من الطائرة، ولماذا لا تمر كلها في طريق واحد.
  • المعمارية الكاملة: المكونات والجداول والواجهات وتقدير السعة ومراحل النمو والأعطال.
  • الطرف Edge والاتصال والبوابة التي تترجم لغة كل مصنع إلى مخطط واحد.
  • الوقت: لماذا تحمل كل قراءة ثلاثة أوقات.
  • السجلات بعد الهبوط، والفيديو المباشر والملف الأصلي.
  • الأمان: توقيع رسائل MAVLink 2 والشهادات وفصل العملاء والتدقيق، ثم التعريف عن بعد Remote ID.

ثلاثة أنواع من البيانات، وثلاثة طرق

أول خطأ في تصميم هذه المنصات أن تعامل كل ما يخرج من الطائرة كأنه نوع واحد، والسبب أن ما يخرج منها في الحقيقة ثلاثة أنواع تختلف في الحجم والسرعة المطلوبة وما يعنيه ضياعها، والصورة التالية تبين الطرق الثلاثة:

ثلاثة أشرطة: الأزرق للقياس عن بعد Telemetry من الراديو أو 4G إلى البوابة ثم الوسيط ثم الخريطة الحية في أقل من ثانية، والأخضر لسجل الطيران ULog أو DataFlash بعد الهبوط إلى واجهة الرفع ثم التخزين والمحلل ثم التحليل، والبرتقالي للفيديو بنسخة خفيفة عبر SRT أو RTSP إلى سيرفر الوسائط ثم WebRTC إلى جدار الفيديو، والملف الأصلي 4K إلى أرشيف S3 بعد الهبوط
القياس عن بعد يريد السرعة، والسجل يريد الاكتمال، والفيديو يريد عرض نطاق Bandwidth

والفرق بين الثلاثة كما يلي:

النوعالحجم والمعدلمتى يصلماذا يعني ضياعه
القياس عن بعد Telemetryرسائل صغيرة، من مرة إلى عشرات المرات في الثانيةالآن، أو بعد عودة الرابطقراءة قديمة ضاعت لا تهم كثيراً، أما آخر موقع فيهم جداً
سجل الطيران Flight Logكل الحساسات بمعدل أعلى بكثير، ميجابايتات لكل دقيقة طيرانبعد الهبوطلا يجوز أن يضيع منه شيء، لأنه ما تعود إليه بعد أي حادث
الفيديونسخة مباشرة بمعدل بت منخفض، وملف أصلي أكبر بكثير على بطاقة الذاكرةالنسخة الآن، والملف بعد الهبوطإطار ضاع في البث لا يهم، والملف الأصلي هو الدليل

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

المعمارية الكاملة: كيف تتصل الأجزاء ببعضها؟

الصورة التالية تبين المنصة كلها، من الطائرات على اليمين إلى شاشات غرفة العمليات على اليسار:

في اليمين الطرف Edge فيه طائرة PX4 أو ArduPilot وطائرة DJI مع Pilot 2 أو Dock ومحطة مصنع X وكاميرا الطائرة وبطاقة الذاكرة، ثم بوابة Edge gateway فيها محول MAVLink 2 ومحول DJI Cloud API ومحول مصنع X وصندوق التخزين ثم الإرسال، وتحتها سيرفر الوسائط MediaMTX، ثم الوسيط NATS JetStream، ثم الخدمات الأساسية وTimescaleDB وتخزين S3، وفي اليسار غرفة العمليات عبر WebSocket وجدار الفيديو عبر WebRTC والتحليل بعد الطيران، وسهم متقطع من بطاقة الذاكرة إلى التخزين بعد الهبوط عبر HTTPS
القياس عن بعد يمر عبر البوابة والوسيط، والفيديو له طريقه، وما على بطاقة الذاكرة يصل بعد الهبوط

المكونات ومسؤولية كل منها

المكونمسؤوليتهأداة مفتوحة المصدر مقترحةلماذا هي
البوابة Edge Gatewayتستقبل لغة كل مصنع وتحولها إلى المخطط الموحد، وتحفظ الرسائل على القرص عند انقطاع الرابطخدمة تكتبها أنت بمكتبة pymavlink (LGPL-3.0) أو MAVSDK (BSD-3-Clause)المحولات هي ما يميز منصتك، ولا توجد أداة جاهزة تعرف كل المصنعين
الوسيط Brokerيستقبل القراءات الموحدة ويحفظها ويوزعها على من يحتاجها، ويستقبل MQTT من الأجهزة التي لا تعرف غيرهNATS مع JetStream (Apache-2.0)خفيف، وفيه مستمع MQTT داخلي، والحفظ وإعادة القراءة Replay في نفس السيرفر
الخدمات الأساسية Coreالحالة الحية لكل طائرة، والتنبيهات، والكتابة في قاعدة البيانات، واستقبال السجلاتخدمة تكتبها أنتمنطق عملك أنت
قاعدة القراءاتالقراءات مرتبة بالزمن، والاستعلام بالفتراتTimescaleDB (الجزء الأساسي Apache-2.0 والضغط بالرخصة الخاصة TSL)PostgreSQL نفسه، فتجمع القراءات ببيانات الطائرات والطيارين في استعلام واحد
تخزين الملفاتالسجلات والفيديو الأصلي والتسجيلاتتخزين متوافق مع S3 على سيرفركملفات كبيرة تكتب مرة وتقرأ قليلاً
سيرفر الوسائطيستقبل الفيديو بأي بروتوكول، ويوزعه عبر WebRTC، ويسجلهMediaMTX (MIT)يستقبل RTSP وRTMP وSRT وWebRTC في برنامج واحد، كما في مقال البث المباشر
الواجهةخريطة حية وتنبيهات وجدار فيديوMapLibre مع خرائط على سيرفرك (BSD-3-Clause)لا تخرج مواقع طائراتك إلى مزود خرائط خارجي
التحليل بعد الطيرانرسوم القراءات والسجلاتGrafana (AGPL-3.0)يقرأ PostgreSQL مباشرة

نموذج البيانات

الجداول الرئيسية كما يلي، وهي نفسها التي نستخدمها في التجربة العملية:

-- one row per normalized reading; the key makes a replayed reading harmless
CREATE TABLE telemetry (
  ts timestamptz NOT NULL,          -- when the drone measured it (event time)
  drone_id text NOT NULL,
  seq bigint, vendor text,
  rx_ts timestamptz,                -- when the gateway received it
  lat double precision, lon double precision,
  alt_amsl_m real, alt_rel_m real, gs_ms real, vs_ms real, heading_deg real,
  battery_pct real, mode text, armed boolean,
  gnss_fix smallint, gnss_sats smallint, gnss_eph_m real,
  stored_at timestamptz DEFAULT now(),   -- when the cloud stored it
  PRIMARY KEY (drone_id, ts));
SELECT create_hypertable('telemetry', by_range('ts', INTERVAL '1 day'));

-- the file itself lives in S3; the database keeps where it is and what it says
CREATE TABLE flight_logs (
  id bigserial PRIMARY KEY, drone_id text NOT NULL, kind text NOT NULL,   -- px4-ulog, ardupilot-bin, ...
  file_name text, size_bytes bigint, started_at timestamptz, duration_s real,
  summary jsonb, uploaded_at timestamptz DEFAULT now());

-- registry and audit (not in the lab)
CREATE TABLE drones (drone_id text PRIMARY KEY, tenant_id text NOT NULL, vendor text, serial text,
  remote_id text, gateway_id text, registered_at timestamptz);
CREATE TABLE commands (id bigserial PRIMARY KEY, drone_id text, issued_by text, command jsonb,
  issued_at timestamptz DEFAULT now(), result text);

في الإعداد أعلاه لاحظ التالي:

  • المفتاح (drone_id, ts) هو ما يجعل إعادة إرسال القراءة نفسها بعد انقطاع الرابط بلا أثر، فالقراءة المكررة لا تدخل مرة ثانية.
  • الأوقات الثلاثة ts وrx_ts وstored_at لكل منها استخدام، كما سوف نرى في قسم الوقت.
  • الجدول commands هو سجل التدقيق Audit Trail لكل أمر أرسل إلى طائرة، وقد شرحنا تصميم هذا النوع من الجداول في مقال سجل التدقيق.

الواجهات الرئيسية

الواجهةمن يستخدمهاماذا تفعل
tlm.<drone_id> (موضوع في الوسيط)البوابات تكتب، والخدمات تقرأقراءة واحدة بالمخطط الموحد، مع معرف رسالة Nats-Msg-Id يمنع تكرارها
vendorx/<sn>/telemetry (MQTT)محطات مصنع Xرسائل JSON بصيغة المصنع، يحولها محوله إلى tlm.<drone_id>
GET /wsشاشة غرفة العملياتالحالة الحية والتنبيهات مرتين في الثانية عبر WebSocket
POST /api/logsتطبيق المحطة بعد الهبوطرفع سجل ULog أو .bin، فيحفظ ويحلل ويسجل في flight_logs
POST /api/drones/{id}/commandsالمشغلأمر مثل العودة RTL، بصلاحية ومفتاح عدم التكرار Idempotency Key، ويكتب في commands قبل أن يرسل
POST /{path}/whepجدار الفيديوتشغيل الفيديو عبر WebRTC بطلب WHEP واحد إلى سيرفر الوسائط

تقدير السعة

الأرقام التالية من افتراضات نذكرها صراحة، فغير الأرقام بأرقام جهتك وأعد الحساب:

  • افتراض: 300 طائرة، منها 100 في الجو في ساعة الذروة، وكل طائرة في الجو تطير 6 ساعات في اليوم في المتوسط.
  • افتراض: قراءتان في الثانية لكل طائرة إلى السحابة، وحجم القراءة الموحدة قرابة 400 بايت بصيغة JSON، فالقراءة الحقيقية التي سوف تراها في قسم المخطط 382 بايتاً.
  • افتراض: فيديو مباشر بدقة 1280×720 ومعدل 1.5 ميجابت في الثانية، وهي قيمة الجودة «HD» في توثيق DJI Cloud API كما سيأتي.

والحساب كما يلي:

البندالحسابالنتيجة
القراءات في الثانية في الذروة100 × 2200 رسالة في الثانية
عرض النطاق للقياس عن بعد200 × 400 بايتقرابة 80 كيلوبايت في الثانية، أي أقل من 1 ميجابت
صفوف في اليوم100 × 2 × 3600 × 64.32 مليون صف
حجمها قبل الضغط4.32 مليون × 200 بايت للصف مع الفهرس، وهو ما قسناه في التجربة: 199 بايتاً للصفقرابة 0.9 جيجابايت في اليوم، و315 جيجابايت في السنة
استقبال الفيديو المباشر100 × 1.5 ميجابت150 ميجابت في الثانية
تسجيل النسخة المباشرة150 ميجابت ÷ 8 × 3600 × 6قرابة 405 جيجابايت في اليوم
توزيع الفيديو للمشاهدين10 مشغلين × 6 طائرات لكل شاشة × 1.5 ميجابت90 ميجابت في الثانية خارجة من سيرفر الوسائط

والنتيجة التي تهمك أن القياس عن بعد صغير جداً حتى مع مئات الطائرات، فسيرفر واحد يكفيه، وأن الفيديو هو الذي يحدد حجم الشبكة والتخزين، لذلك ضع للتسجيلات مدة حفظ Retention واضحة من اليوم الأول. وقد قسنا في مقال قواعد بيانات السلاسل الزمنية كم يوفر ضغط TimescaleDB على بيانات مشابهة، فارجع إليه قبل أن تحسب السنة الكاملة.

مراحل النمو

الجزءالبداية: سيرفر أو اثنانآلاف الطائراتعشرات الآلاف ومناطق متعددة
البواباتبوابة واحدة لكل محطة تحكم، والمحولات في نفس البرنامجبوابة لكل موقع، ومحول لكل مصنع كخدمة مستقلةبوابات في كل منطقة قريبة من الطائرات
الوسيطNATS واحد مع JetStreamعنقود Cluster من ثلاثة سيرفراتعنقود في كل منطقة مع ربطها ببعض، أو Kafka للتحليل طويل الأمد
القراءاتTimescaleDB واحدنسخة قراءة Read Replica للوحات، وضغط القديمتقسيم حسب العميل أو المنطقة
الفيديوMediaMTX واحدسيرفر وسائط لكل موقع، والتسجيل إلى S3توزيع عبر عدة سيرفرات SFU وTURN كما في مقال TURN

ولا تبن في اليوم الأول ما يلي: Kafka، أو قاعدة بيانات لكل نوع من البيانات، أو تحليل الفيديو بالذكاء الاصطناعي، أو تطبيق جوال للمشغلين. والسبب أن المشكلة الحقيقية في البداية هي المحولات والوقت والرابط الضعيف، وكل ما عدا ذلك يضاف عندما تظهر الحاجة.

ماذا لو تعطل جزء؟

ماذا لو تعطل…؟الأثركيف تخففه
رابط الطائرة بالأرضالخريطة تتجمد على آخر موقعتنبيه «لا توجد بيانات منذ X ثانية» واضح، وحماية فقدان الرابط في الطيار الآلي نفسه وليس في المنصة
رابط البوابة بالسحابةلا يصل شيء إلى غرفة العملياتالتخزين ثم الإرسال Store-and-Forward على قرص البوابة، والإرسال بالترتيب مع معرف يمنع التكرار
الوسيطتتوقف الخريطة والتنبيهاتعنقود من ثلاثة، والبوابات تحفظ حتى يعود
قاعدة البياناتتتوقف الكتابة والتحليل، ولا تتوقف الخريطة الحيةالوسيط يحفظ الرسائل حتى يعود الكاتب ويكمل من حيث توقف
سيرفر الوسائطيتوقف الفيديو المباشرالملف الأصلي ما زال على بطاقة الذاكرة، ويرفع بعد الهبوط
محول مصنع غير صيغتهأرقام خاطئة بصمتفحص الوحدات والوقت، وعزل الرسائل التي لا تطابق المخطط بدل قبولها

الطرف Edge: من يتحدث مع السحابة؟

الطائرة نفسها في الغالب لا تتحدث مع سيرفرك مباشرة، وإنما يتحدث نيابة عنها جهاز على الأرض أو على متنها، وهذا الجهاز يختلف باختلاف المنصة كما يلي:

  • محطة التحكم الأرضية GCS: مع PX4 وArduPilot تصل رسائل MAVLink عبر الراديو إلى حاسوب المحطة، وعليه تعمل البوابة جنباً إلى جنب مع QGroundControl.
  • الحاسوب المرافق Companion Computer: حاسوب صغير على الطائرة نفسها، وإذا كان معه رابط 4G أو 5G فالبوابة تعمل عليه وتتصل بالسحابة مباشرة دون محطة أرضية، وقد شرحناه في مقال التطوير مع ROS 2.
  • تطبيق DJI Pilot 2 أو DJI Dock: في منصة DJI يتصل جهاز التحكم أو المحطة الآلية بسيرفرك عبر Cloud API، والطائرة نفسها تتصل بهما برابط DJI الخاص.
  • سحابة المصنع: بعض المصنعين لا يعطونك إلا واجهة REST أو MQTT من سحابتهم، فتكون البوابة عندك مشتركاً في سحابتهم.

الرابط الضعيف: التخزين ثم الإرسال

الطائرة تطير بعيداً عن الأبراج، وتدخل خلف مبنى أو جبل، وقد تعتمد على رابط عبر الأقمار الصناعية بتكلفة لكل ميجابايت، لذلك صمم المنصة على أن الرابط سوف ينقطع وليس على أنه قد ينقطع. وأنواع الروابط الشائعة كما يلي:

  • رابط الراديو المباشر: بين الطائرة والمحطة، ومداه محدود بخط النظر.
  • الشبكة الخلوية 4G و5G: من الحاسوب المرافق أو من المحطة إلى الإنترنت، وتغطيتها في الجو تختلف عن الأرض.
  • الأقمار الصناعية: للمواقع البعيدة، وفيها تأخير أكبر وتكلفة أعلى، فترسل القياس عن بعد فقط وتؤجل الفيديو.

والحل البسيط أن ترسل البوابة كل قراءة مباشرة إلى السحابة، فإذا فشل الإرسال تجاهلتها، وهذا يعني أن كل دقيقة انقطاع ثقب في سجل القراءات. والحل الأفضل هو التخزين ثم الإرسال Store-and-Forward: تكتب البوابة القراءة التي فشل إرسالها في ملف على القرص، ثم ترسلها بالترتيب عندما يعود الرابط، وكل قراءة تحمل معرفاً ثابتاً، فإذا أعيد إرسالها لم تخزن مرتين. ويجدر الإشارة هنا إلى أمرين قسناهما في التجربة العملية: الأول أن المكتبات تحفظ بعض الرسائل في الذاكرة أثناء الانقطاع، ولكن هذه الذاكرة تضيع إذا أعيد تشغيل البوابة، والثاني أن إرسال المخزن رسالة بعد رسالة وانتظار التأكيد لكل واحدة قد يكون أبطأ من وصول القراءات الجديدة، فلا يفرغ المخزن أبداً.

محول لكل مصنع، ومخطط واحد بعده

كل ما بعد البوابة يجب ألا يعرف من صنع الطائرة، وهذا هو دور المحولات Adapters: محول لكل لغة، يحولها إلى مخطط واحد.

الطياران الآليان يتحدثان MAVLink، والرسائل التي تكفي للخريطة والتنبيهات قليلة:

الرسالةماذا تأخذ منها
HEARTBEATالوضع والتسليح ونوع الطيار الآلي
GLOBAL_POSITION_INTالموقع والارتفاع والسرعة الرأسية
GPS_RAW_INTحالة GNSS وعدد الأقمار والدقة
SYS_STATUSالبطارية
SYSTEM_TIMEربط ساعة الطائرة بالتاريخ

وهناك فخ يقع فيه كثيرون: رقم النظام System ID في MAVLink ليس هوية الطائرة، فكل طائرة PX4 جديدة تأتي بالرقم 1، وطائرتان على البوابة نفسها قد تقولان الشيء نفسه، لذلك اربط الهوية بالرابط نفسه، أي بالمنفذ أو العنوان أو الرقم التسلسلي، وسجلها في جدول drones. وفرق آخر رأيناه في التجربة: ArduPilot لا يرسل أغلب الرسائل على رابط جديد حتى تطلبها منه، وPX4 يرسل أغلبها افتراضياً ولكنه لم يرسل SYSTEM_TIME حتى طلبناها.

DJI Cloud API: مواضيع MQTT

في منصة DJI يتصل تطبيق Pilot 2 أو DJI Dock بوسيط MQTT تملكه أنت، وتحدد صفحة تعريف المواضيع في مستودع توثيق Cloud API المواضيع التالية، وفيها {device_sn} الرقم التسلسلي للجهاز و{gateway_sn} الرقم التسلسلي للبوابة، أي جهاز التحكم أو المحطة:

الموضوعالاتجاهماذا يحمل
thing/product/{device_sn}/osdمن الجهاز إلى السيرفرالخصائص التي ترسل بتكرار ثابت، مثل الموقع والارتفاع والبطارية
thing/product/{device_sn}/stateمن الجهاز إلى السيرفرالخصائص التي ترسل عند تغيرها فقط
thing/product/{gateway_sn}/services وservices_replyأمر من السيرفر، ورد من الجهازالأوامر، ومنها بدء البث المباشر وإيقافه
thing/product/{gateway_sn}/events وevents_replyحدث من الجهاز، ورد من السيرفرأحداث تحتاج متابعة، مثل امتلاء بطاقة الذاكرة
thing/product/{gateway_sn}/requests وrequests_replyطلب من الجهاز، ورد من السيرفرمثل طلب بيانات دخول مؤقتة
sys/product/{gateway_sn}/status وstatus_replyمن الجهاز، ورد من السيرفراتصال الجهاز وانقطاعه وتغير الأجهزة المرتبطة به
thing/product/{gateway_sn}/property/set وset_replyمن السيرفر، ورد من الجهازضبط خاصية في الجهاز
thing/product/{gateway_sn}/drc/up وdrc/downفي الاتجاهينبروتوكول التحكم المباشر DRC

وكل رسالة تحمل الحقول المشتركة tid وbid وtimestamp بالملي ثانية وgateway وdata. ولاحظ أن هذا المستودع آخر تحديث له في نوفمبر 2024، والصفحة نفسها في موقع DJI تعرض بمتصفح فقط، فراجع الموقع الرسمي للأجهزة الأحدث قبل أن تبني عليه. وتذكر ما ذكرناه في مقال المنصات: الكود التجريبي للسيرفر الذي نشرته DJI متوقفة صيانته وغير مخصص للإنتاج.

مصنع بواجهة خاصة

المصنع الثالث قد يعطيك JSON عبر MQTT أو REST بصيغة لا يشبهها شيء، وهنا يأتي أكثر ما يكسر المنصات بصمت: الوحدات والوقت. قدم بالأقدام بدل الأمتار، وسرعة بالكيلومتر في الساعة، وبطارية من 0 إلى 1 بدل النسبة، ووقت محلي بلا منطقة زمنية. والقاعدة كما يلي: المحول يحول كل شيء إلى وحدات المخطط ويكتب الوحدة صراحة في الكود، وإذا وصلت رسالة لا يعرف وحدتها أو صيغتها فإنه يعزلها في مكان منفصل ولا يخمن، والسبب أن الرقم الخاطئ على الخريطة أخطر من الرقم الغائب، وقد رأينا ذلك في التجربة عندما غير «مصنع X» وحدة الارتفاع.

المخطط الموحد

والقراءة التالية قراءة حقيقية من طائرة ArduPilot في التجربة كما أعادها السيرفر، وقد وزعنا حقولها على أسطر لتقرأ بسهولة:

{
  "v": 1, "drone_id": "ap-c", "vendor": "ardupilot", "seq": 3514,
  "ts": "2026-10-13T16:59:28.003Z", "rx_ts": "2026-10-13T17:07:11.777Z",
  "lat": 47.3968753, "lon": 8.5420001, "alt_amsl_m": 538.07, "alt_rel_m": 49.997,
  "gs_ms": 9.90256404876709, "vs_ms": 0.01, "heading_deg": 219.54,
  "battery_pct": 0, "mode": "GUIDED", "armed": true,
  "gnss_fix": 6, "gnss_sats": 10, "gnss_eph_m": 0.3
}

في المخطط أعلاه لاحظ التالي:

  • الحقل v هو رقم إصدار المخطط، فعندما تضيف حقلاً تعرف الخدمات أي صيغة تقرأ.
  • الوحدة في اسم الحقل نفسه، مثل _m و_ms و_pct، فلا يحتاج أحد أن يسأل.
  • كل الأوقات UTC بصيغة ISO 8601 مع الملي ثانية، وقد شرحنا لماذا في مقال التاريخ والوقت.
  • لاحظ أن ts قبل rx_ts بقرابة ثماني دقائق، والسبب أن ساعة محاكاة ArduPilot كانت تتأخر عن الساعة الحقيقية على جهاز مزدحم، وأن battery_pct صفر لأن البطارية المحاكاة نفدت بعد ساعات من الطيران دون أن يتغير شيء في المحاكاة، وهذان بالضبط ما يجب أن تنبه إليهما المنصة.

الوسيط: MQTT أم NATS أم Kafka؟

شرحنا الفرق بين الطابور وسجل الأحداث في مقال الطابور أم بث الأحداث، وما يهمنا هنا هو ما تحتاجه منصة طائرات بالذات:

الحاجةMQTT وحدهNATS مع JetStreamKafka
أجهزة لا تعرف إلا MQTT، مثل DJI Cloud APIنعمنعم، بمستمع MQTT داخلييحتاج جسراً منفصلاً
حفظ القراءات وإعادة قراءتهالا، إلا الرسالة الأخيرة المحفوظة Retainedنعمنعم، وهذه قوته
منع التكرار عند إعادة الإرساللانعم، بمعرف الرسالة خلال نافذة زمنيةنعم، بالمنتج الآمن عند التكرار
الحجم التشغيلي لفريق صغيرصغيرصغيرأكبر، كما في مقال Kafka

لذلك اخترنا NATS في التجربة، ويبقى Kafka خياراً عندما تصبح القراءات مصدراً لفرق تحليل كثيرة تقرأ التاريخ الطويل.

الوقت: ثلاثة أوقات على كل قراءة

الطيار الآلي يعد الوقت من لحظة تشغيله، ولا يعرف التاريخ إلا من إشارة GNSS، والبوابة تعرف الوقت من NTP، والسيرفر من NTP أو PTP، وهذه الساعات لا تتفق دائماً، والصورة التالية تبين الأوقات الثلاثة ولماذا نحتاجها كلها:

ثلاث بطاقات: وقت القياس ts من ساعة الطائرة منذ التشغيل ووقت GNSS، ووقت الاستلام rx_ts من ساعة البوابة عبر NTP، ووقت الحفظ stored_at من ساعة قاعدة البيانات، وتحتها خط زمني: القياس 10:00:00.000 ثم الاستلام 10:00:00.180 ثم الحفظ 10:04:12.900 بعد انقطاع الرابط، وفي الأسفل تحذير أحمر من ساعة بلا منطقة زمنية تجعل القراءة في المستقبل
رتب بوقت القياس، وقس الرابط بالفرق بين القياس والاستلام

والقواعد كما يلي:

  • رتب وارسم بوقت القياس: القراءة التي وصلت متأخرة بعد عودة الرابط تخزن في مكانها من الزمن، ولا تحرك علامة الطائرة على الخريطة إلى الوراء.
  • اربط ساعة الطائرة بالتاريخ: مع MAVLink ترسل الطائرة SYSTEM_TIME وفيها الوقت منذ التشغيل والتاريخ معاً، فتحسب البوابة الفرق مرة وتطبقه على كل رسالة.
  • لا تقبل وقتاً في المستقبل: إذا كان وقت القياس بعد وقت الاستلام بأكثر من ثوان، فالساعة خاطئة، ولا تجعلها تقرر ما هو «الأحدث»، وسوف ترى في التجربة كيف تجمدت طائرة على الخريطة بسبب ساعة متقدمة ثلاث ساعات.
  • راقب الفرق بين الساعات: الفرق بين rx_ts وts هو تأخير الرابط، فإذا كبر فجأة أو أصبح سالباً فهناك مشكلة في الرابط أو في الساعة، وهذا التنبيه وحده كشف لنا في التجربة ساعة محاكاة متأخرة 8 دقائق.

السجلات بعد الهبوط

سجل الطيران هو الصندوق الأسود، وكل منصة تكتبه بصيغتها:

المنصةالصيغةما يهمك منها
PX4ULog بامتداد .ulgتصف صفحة الصيغة أنها صيغة تصف نفسها، أي أن تعريف كل رسالة داخل الملف، والأوقات بالميكروثانية، وفيها رسائل نصية وقيم المعاملات وعلامات الفقد Dropout. وقرأناها بالكود في مقال التطوير.
ArduPilotDataFlash بامتداد .binتقول صفحة السجلات إنها تحفظ على بطاقة SD أو شريحة ذاكرة أو تبث عبر منفذ MAVLink سريع، وإن LOG_BITMASK يحدد ما يسجل، وLOG_DISARMED يبدأ التسجيل من التشغيل بدل أول تسليح.
محطة التحكمسجل القياس عن بعد .tlogما وصل عبر الراديو فقط، فهو أقل من سجل الطائرة.
DJIسجل الطيران FlightRecordيقول مستودع مكتبة قراءة السجلات إن المكتبة تقرأ الإصدار 13 من السجلات، وإنها تحتاج مفتاح تطبيق App Key، وإن جزءاً منها يتصل بخدمة DJI ليحصل على مفتاح فك التشفير. أي أن قراءة سجلات DJI في منصتك تحتاج حساب مطور واتصالاً بالشركة.

والطريق في المنصة كما يلي: بعد الهبوط يرفع تطبيق المحطة الملف إلى POST /api/logs، فيحفظ الملف كما هو في S3، ويحلله محلل حسب صيغته، ويكتب ملخصاً موحداً في flight_logs، مثل المدة وأقصى ارتفاع وتغيرات الوضع والتحذيرات. ولا ترفع سجلات طيران جهتك إلى مواقع تحليل عامة، والسبب أن السجل فيه كل مواقع الطيران بدقة.

الفيديو: نسخة مباشرة وملف أصلي

شرحنا كيف يعمل البث وما الذي يسبب التأخير في مقال بث الفيديو، وشرحنا WebRTC في مقال WebRTC من الداخل، وما يخص الطائرات هنا ثلاثة أمور.

الأمر الأول أن البث من الطائرة يمر على رابط ضعيف، لذلك يهمك بروتوكول الاستقبال أكثر من بروتوكول العرض، والمقارنة كما يلي:

البروتوكولفوق ماذا يعملعلى الرابط الضعيف
RTSP عبر TCPTCPلا يفقد شيئاً، ولكن كل حزمة ضائعة تؤخر ما بعدها، فيتراكم التأخير
RTSP عبر UDPUDPلا يتراكم التأخير، ولكن الحزمة الضائعة تظهر تشوهاً في الصورة
RTMPTCPمثل RTSP عبر TCP، وهو الخيار المدعوم في كثير من الأجهزة
SRTUDP مع إعادة إرساليعيد إرسال الضائع خلال مهلة تحددها، ويتخلى عما تأخر بعدها
WebRTC مع WHIPUDPتأخير منخفض جداً، وتقدير للسرعة يخفض الجودة عند الضعف

وقد قسنا التأخير لكل بروتوكول على نفس الجهاز في التجربة العملية.

الأمر الثاني خيارات DJI، فتحدد صفحة البث المباشر لـ DJI Dock 2 في مستودع التوثيق الأمر live_start_push، وفيه الحقل url_type بالقيم التالية:

القيمةالبروتوكول
0خدمة Agora
1RTMP
3GB28181
4WebRTC، وتقول الصفحة إنه يدعم WHIP فقط

وفيه الحقل video_quality، والجودات كما في الصفحة نفسها:

الجودةالدقةمعدل البت
Smooth960×540512 كيلوبت
Standard Definition1280×7201 ميجابت
High Definition1280×7201.5 ميجابت
Ultra High Definition1920×10803 ميجابت

ولاحظ أن صفحة شرح الميزة في المستودع نفسه تذكر في موضع أن البث يدعم Agora وRTMP وGB28181 فقط، وتذكر WebRTC في جدول الأنواع، فاختبر الخيار الذي تريده على جهازك وإصدار برنامجه قبل أن تعتمد عليه. وبالتالي يكفي سيرفر وسائط يستقبل RTMP وWHIP، مثل MediaMTX، ليستقبل بث DJI مع غيره.

الأمر الثالث هو البيانات الوصفية Metadata المرتبطة بالفيديو: في الأنظمة الحكومية والعسكرية يحمل بث الطائرة موقعها واتجاه الكاميرا مع كل إطار بصيغة KLV بحسب المعيار MISB ST 0601، فتعرف من الإطار نفسه أين كانت الطائرة وإلى أين تنظر الكاميرا. ولم نستطع فتح نص المعيار من موقع NGA الرسمي وقت كتابة المقال، لأن الموقع يطلب تحققاً بشرياً، لذلك لا نذكر تفاصيل حقوله هنا. والبديل العملي في المنصات المدنية أن تربط الفيديو بالقياس عن بعد بالوقت، فكل تسجيل يعرف وقت بدايته، والقراءات تعرف أوقاتها، فتعرف موقع الطائرة عند أي ثانية من الفيديو.

وأما الملف الأصلي فيبقى على بطاقة الذاكرة بدقته الكاملة، ويرفع بعد الهبوط إلى S3 مع الإشارة إلى الطيران الذي ينتمي إليه، والنسخة المباشرة المسجلة تحذف بعد مدة قصيرة لأنها أقل جودة.

غرفة العمليات: الخريطة والتنبيهات والتحليل

الشاشة الحية لا تقرأ من قاعدة البيانات، وإنما من الحالة الحية في الذاكرة عبر WebSocket، والسبب أن قاعدة البيانات للتاريخ، أما «أين الطائرة الآن» فيجب أن يصل في أقل من ثانية حتى لو كانت قاعدة البيانات مشغولة. وقد شرحنا توصيل الموقع لكثير من المشاهدين في مقال تتبع المندوبين، والتنبيهات التي تحتاجها من اليوم الأول قليلة:

  • انقطاع البيانات من طائرة أكثر من مدة محددة.
  • بطارية تحت حد معين.
  • ارتفاع فوق الحد المسموح، فالحد في الأنظمة السعودية كما في مقال الأنظمة.
  • فرق كبير بين وقت القياس ووقت الاستلام.
  • تغير مفاجئ في موقع GNSS لا تفسره سرعة الطائرة.

أما التحليل بعد الطيران فيقرأ من TimescaleDB ومن ملخصات السجلات، وGrafana يكفي له في البداية.

الأمان: من يستطيع أن يأمر الطائرة؟

السؤال الأخطر في هذه المنصة ليس من يرى البيانات، وإنما من يستطيع أن يرسل أمراً إلى طائرة في الجو، والصورة التالية تبين أين توضع الحماية:

سلسلة من اليمين: الطيار الآلي ثم رابط الراديو ثم البوابة ثم الوسيط ثم الخدمات وقاعدة البيانات ثم المشغل، وتحتها أربع بطاقات: توقيع MAVLink 2 على الراديو، وشهادة mTLS لكل بوابة، وفصل العملاء Tenant isolation، والتدقيق Audit لكل أمر
كل طبقة تحمي من شيء مختلف، ولا تغني واحدة عن الأخرى

بحسب صفحة توقيع الرسائل في توثيق MAVLink، الرسالة الموقعة تحمل العلم 0x01 في حقل التوافق، وتضاف إليها 13 بايتاً كما يلي:

  • رقم الرابط Link ID في بايت واحد.
  • طابع زمني Timestamp في 48 بت، بوحدات 10 ميكروثانية منذ 1 يناير 2015.
  • التوقيع في 48 بت، وهو أول 48 بت من SHA-256 على المفتاح السري ثم رأس الرسالة ومحتواها و CRC ورقم الرابط والطابع الزمني.

والمفتاح السري 32 بايتاً موجود في الطرفين، وتوصي الصفحة بأن يرسل إلى الطائرة برسالة SETUP_SIGNING على رابط آمن مثل USB فقط، وألا يبث أبداً، وألا يظهر في المعاملات أو السجلات. والطابع الزمني هو ما يحمي من إعادة الرسائل Replay، فالرسالة التي طابعها أقدم من السابقة في نفس الرابط، أو أقدم من الوقت المحلي بأكثر من دقيقة، ترفض.

وهل هذا يكفي؟ لا، والتفصيل فيما يلي:

  • التوقيع لا يشفر: تقول صفحة التوقيع في ArduPilot صراحة إنه لا يشفر البيانات، وإنه يتحكم فقط في قبول الطائرة للأوامر، أي أن القراءات تبقى مقروءة لمن يستقبل الراديو.
  • ليس على كل المنافذ: في ArduPilot يطبق على منافذ MAVLink2 التسلسلية غير USB، واتصال USB غير متأثر، والروابط التي تعمل بـ MAVLink 1 غير محمية.
  • ليس في كل طيار آلي بنفس الشكل: لم نجد في كود PX4 v1.17.0 ولا في توثيق هذا الإصدار معاملاً أو صفحة لتوقيع الرسائل، فتحقق من الإصدار الذي تستخدمه قبل أن تعتمد على التوقيع في أسطول PX4.

الشهادات وفصل العملاء والتدقيق

بعد الراديو تأتي بقية الطبقات:

  • شهادة لكل بوابة mTLS: كل بوابة ومحطة تتصل بالوسيط بشهادة خاصة بها، فإذا سرقت محطة سحبت شهادتها وحدها، وقد شرحنا الشهادات في مقال الشهادات. ولا تعط كل الأجهزة كلمة مرور واحدة للوسيط، فتسريبها من جهاز واحد يفتح الوسيط كله.
  • فصل العملاء: إذا كانت المنصة تخدم أكثر من جهة، فكل جهة لها حساب Account منفصل في الوسيط ومواضيع لا يراها غيرها، وفصل في قاعدة البيانات كما في مقال تعدد المستأجرين، والسبب أن الفلترة في الكود وحدها تفشل من أول خطأ.
  • التدقيق: كل أمر يرسل إلى طائرة يكتب في جدول commands قبل إرساله، مع من أرسله ومتى، ولا يملك من يرسل الأوامر صلاحية تعديل هذا الجدول.

التعريف عن بعد Remote ID وإدارة الحركة UTM

التعريف عن بعد Remote ID هو أن تعلن الطائرة أثناء الطيران هويتها وموقعها لمن حولها، والمعيار الأشهر له هو ASTM F3411، وتقول صفحة الإصدار F3411-22a إنه يحدد صيغ الرسائل وطرق الإرسال والحد الأدنى للأداء لشكلين:

  • البث Broadcast Remote ID: إشارة راديو من الطائرة مباشرة إلى المستقبلات القريبة.
  • الشبكة Network Remote ID: عبر الإنترنت من مزود خدمة يتصل بالطائرة أو بمحطتها.

وفي الولايات المتحدة تذكر صفحة FAA ثلاث طرق للالتزام: طائرة فيها Remote ID مدمج، أو وحدة بث تركب على الطائرة، أو الطيران داخل منطقة معترف بها FRIA دون Remote ID. وأما ما تطلبه هيئة الطيران المدني في السعودية فقد جمعناه في مقال الأنظمة.

وماذا يعني هذا لمنصتك؟ أمران: الأول أن رقم Remote ID لكل طائرة يجب أن يكون في جدول drones مع رقمها التسلسلي، والثاني أن المستقبلات القريبة من مواقعك تستطيع أن تقارن ما تعلنه طائراتك بما تراه منصتك. وفوق ذلك تأتي أنظمة إدارة حركة الطائرات المسيرة UTM، وفي أوروبا تسمى U-space، وهي خدمات تتبادل فيها المنصات خطط الطيران والمواقع مع جهات الطيران، ولا ندخل في تفاصيلها هنا لأنها تختلف من دولة إلى أخرى.

أخطاء شائعة

  • اعتبار رقم النظام في MAVLink هوية الطائرة: طائرتان PX4 جديدتان تحملان الرقم 1، فاربط الهوية بالرابط والرقم التسلسلي.
  • الترتيب بوقت الوصول: القراءات المتأخرة بعد عودة الرابط تعيد الطائرة إلى الوراء على الخريطة، فرتب بوقت القياس.
  • قبول أي وقت تعطيه الطائرة: ساعة متقدمة ساعات تجعل القراءة «الأحدث» إلى الأبد.
  • تخمين الوحدات: المحول الذي يخمن يعطيك أرقاماً خاطئة بصمت، والمحول الذي يعزل ما لا يعرفه يعطيك تنبيهاً.
  • البث المباشر بديلاً عن السجل: البث جزء صغير من السجل، والسجل يرفع بعد الهبوط دائماً.
  • الفيديو في نفس طريق القياس عن بعد: الفيديو يملأ الرابط ويؤخر القراءات الصغيرة التي تحتاجها الآن.
  • الاعتماد على توقيع MAVLink للسرية: التوقيع يحمي الأوامر ولا يشفر القراءات.

الخلاصة

  • ما يخرج من الطائرة ثلاثة أنواع: قياس عن بعد صغير يريد السرعة، وسجل كامل يصل بعد الهبوط، وفيديو له طريقه الخاص.
  • البوابة عند الطرف تحول لغة كل مصنع إلى مخطط واحد بوحدات ثابتة ووقت UTC، وتخزن ثم ترسل عندما ينقطع الرابط.
  • لكل قراءة ثلاثة أوقات: القياس للترتيب، والاستلام لقياس الرابط، والحفظ لقياس التخزين ثم الإرسال.
  • القياس عن بعد صغير حتى مع مئات الطائرات، والفيديو هو الذي يحدد الشبكة والتخزين.
  • توقيع MAVLink 2 يحمي الأوامر على الراديو ولا يشفر، والشهادة لكل بوابة وفصل العملاء والتدقيق طبقات منفصلة.

وفي المقال التالي بإذن الله نبني هذه المعمارية على جهاز واحد ونكسرها عن قصد.

دليلأسطول طائرات من مصنعين مختلفين على لوحة مراقبة واحدةنشغل طائرات PX4 وArduPilot ومصنع ثالث في محاكاة، ونجمعها على خريطة حية واحدة، ثم نقطع الرابط ونغير الوحدات لنرى ما ينكسر.دليلPX4 وArduPilot أم DJI: أي منصة تناسب طائرتك؟ما الفرق بين منصات الطائرات المسيرة المفتوحة ومنصة DJI المغلقة ومكتباتها، وما القيود الأمريكية الرسمية التي تدخل في القرار اليوم.دليلبرمجة الطائرات المسيرة مع PX4 وMAVSDKكيف تكتب برنامجاً يقلع بالطائرة المسيرة ويطير مهمة ويعود، وتختبره في محاكاة، وترى كيف يحميها الطيار الآلي عند الأعطال.

سجل التحديثات

  • أكتوبر 2026: كتابة المقال، والمعلومات من توثيق MAVLink وPX4 v1.17 وArduPilot ومستودعي توثيق DJI Cloud API ومكتبة سجلات DJI وصفحتي ASTM وFAA كما قرأناها في 13 أكتوبر 2026.
نشرة عرب رووت | ArabRoot

معرفة تستحق مكاناً في بريدك.

مقالات مختارة وأدوات مفيدة وأفكار لمشروعك القادم، في رسالة واحدة كل أسبوع.

يمكنك إلغاء الاشتراك متى شئت. الخصوصية

تم استلام طلبك. افتح بريدك واضغط رابط التأكيد لإتمام الاشتراك.