لنفرض أن جهتك تملك أسطولاً من الطائرات المسيرة، بعضها يعمل بالطيار الآلي PX4، وبعضها بـ ArduPilot، وبعضها من DJI مع محطة الإقلاع والهبوط الآلية DJI Dock، وطائرتان من شركة ثالثة لها سحابتها الخاصة، والمطلوب شاشة واحدة في غرفة العمليات ترى عليها كل طائرة في مكانها الآن، وتسمع منها التنبيه قبل أن تنفد البطارية، وترى فيديو الكاميرا مباشرة، ثم بعد الهبوط تجد سجل كل طيران وملف الفيديو الكامل في مكان واحد. وقد يبدو الأمر من الوهلة الأولى برنامجاً يقرأ من كل طائرة ويرسم على خريطة، ولكن في الواقع كل مصنع يتحدث لغة مختلفة، والرابط بين الطائرة والأرض يضعف وينقطع، والساعات لا تتفق، والفيديو وحده أثقل من كل شيء آخر مجتمعاً.
وهذا المقال هو الأول في سلسلة عن منصات عمليات الطائرات المسيرة:
- كيف تبني منصة قيادة وتحكم لأسطول طائرات مسيرة (الموضوع الحالي): المعمارية والقرارات.
- التجربة العملية: طائرات PX4 وArduPilot في محاكاة، وطائرة من «مصنع X» عبر MQTT، حتى الخريطة الحية وقياس تأخير الفيديو.
ونفترض أنك قرأت مقال برمجة الطائرات مع PX4 وMAVSDK الذي شرحنا فيه بروتوكول MAVLink، ومقال المنصات الذي عرفنا فيه مكتبات DJI الثلاث، فلن نعيد شرحهما هنا، وإنما نبني فوقهما.
وسوف نناقش في هذا المقال ما يلي:
- أنواع البيانات الثلاثة التي تأتي من الطائرة، ولماذا لا تمر كلها في طريق واحد.
- المعمارية الكاملة: المكونات والجداول والواجهات وتقدير السعة ومراحل النمو والأعطال.
- الطرف Edge والاتصال والبوابة التي تترجم لغة كل مصنع إلى مخطط واحد.
- الوقت: لماذا تحمل كل قراءة ثلاثة أوقات.
- السجلات بعد الهبوط، والفيديو المباشر والملف الأصلي.
- الأمان: توقيع رسائل MAVLink 2 والشهادات وفصل العملاء والتدقيق، ثم التعريف عن بعد Remote ID.
ثلاثة أنواع من البيانات، وثلاثة طرق
أول خطأ في تصميم هذه المنصات أن تعامل كل ما يخرج من الطائرة كأنه نوع واحد، والسبب أن ما يخرج منها في الحقيقة ثلاثة أنواع تختلف في الحجم والسرعة المطلوبة وما يعنيه ضياعها، والصورة التالية تبين الطرق الثلاثة:

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

المكونات ومسؤولية كل منها
| المكون | مسؤوليته | أداة مفتوحة المصدر مقترحة | لماذا هي |
|---|---|---|---|
| البوابة 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 × 2 | 200 رسالة في الثانية |
| عرض النطاق للقياس عن بعد | 200 × 400 بايت | قرابة 80 كيلوبايت في الثانية، أي أقل من 1 ميجابت |
| صفوف في اليوم | 100 × 2 × 3600 × 6 | 4.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 2 لطائرات PX4 وArduPilot
الطياران الآليان يتحدثان 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 مع JetStream | Kafka |
|---|---|---|---|
| أجهزة لا تعرف إلا MQTT، مثل DJI Cloud API | نعم | نعم، بمستمع MQTT داخلي | يحتاج جسراً منفصلاً |
| حفظ القراءات وإعادة قراءتها | لا، إلا الرسالة الأخيرة المحفوظة Retained | نعم | نعم، وهذه قوته |
| منع التكرار عند إعادة الإرسال | لا | نعم، بمعرف الرسالة خلال نافذة زمنية | نعم، بالمنتج الآمن عند التكرار |
| الحجم التشغيلي لفريق صغير | صغير | صغير | أكبر، كما في مقال Kafka |
لذلك اخترنا NATS في التجربة، ويبقى Kafka خياراً عندما تصبح القراءات مصدراً لفرق تحليل كثيرة تقرأ التاريخ الطويل.
الوقت: ثلاثة أوقات على كل قراءة
الطيار الآلي يعد الوقت من لحظة تشغيله، ولا يعرف التاريخ إلا من إشارة GNSS، والبوابة تعرف الوقت من NTP، والسيرفر من NTP أو PTP، وهذه الساعات لا تتفق دائماً، والصورة التالية تبين الأوقات الثلاثة ولماذا نحتاجها كلها:

والقواعد كما يلي:
- رتب وارسم بوقت القياس: القراءة التي وصلت متأخرة بعد عودة الرابط تخزن في مكانها من الزمن، ولا تحرك علامة الطائرة على الخريطة إلى الوراء.
- اربط ساعة الطائرة بالتاريخ: مع MAVLink ترسل الطائرة
SYSTEM_TIMEوفيها الوقت منذ التشغيل والتاريخ معاً، فتحسب البوابة الفرق مرة وتطبقه على كل رسالة. - لا تقبل وقتاً في المستقبل: إذا كان وقت القياس بعد وقت الاستلام بأكثر من ثوان، فالساعة خاطئة، ولا تجعلها تقرر ما هو «الأحدث»، وسوف ترى في التجربة كيف تجمدت طائرة على الخريطة بسبب ساعة متقدمة ثلاث ساعات.
- راقب الفرق بين الساعات: الفرق بين
rx_tsوtsهو تأخير الرابط، فإذا كبر فجأة أو أصبح سالباً فهناك مشكلة في الرابط أو في الساعة، وهذا التنبيه وحده كشف لنا في التجربة ساعة محاكاة متأخرة 8 دقائق.
السجلات بعد الهبوط
سجل الطيران هو الصندوق الأسود، وكل منصة تكتبه بصيغتها:
| المنصة | الصيغة | ما يهمك منها |
|---|---|---|
| PX4 | ULog بامتداد .ulg | تصف صفحة الصيغة أنها صيغة تصف نفسها، أي أن تعريف كل رسالة داخل الملف، والأوقات بالميكروثانية، وفيها رسائل نصية وقيم المعاملات وعلامات الفقد Dropout. وقرأناها بالكود في مقال التطوير. |
| ArduPilot | DataFlash بامتداد .bin | تقول صفحة السجلات إنها تحفظ على بطاقة SD أو شريحة ذاكرة أو تبث عبر منفذ MAVLink سريع، وإن LOG_BITMASK يحدد ما يسجل، وLOG_DISARMED يبدأ التسجيل من التشغيل بدل أول تسليح. |
| محطة التحكم | سجل القياس عن بعد .tlog | ما وصل عبر الراديو فقط، فهو أقل من سجل الطائرة. |
| DJI | سجل الطيران FlightRecord | يقول مستودع مكتبة قراءة السجلات إن المكتبة تقرأ الإصدار 13 من السجلات، وإنها تحتاج مفتاح تطبيق App Key، وإن جزءاً منها يتصل بخدمة DJI ليحصل على مفتاح فك التشفير. أي أن قراءة سجلات DJI في منصتك تحتاج حساب مطور واتصالاً بالشركة. |
والطريق في المنصة كما يلي: بعد الهبوط يرفع تطبيق المحطة الملف إلى POST /api/logs، فيحفظ الملف كما هو في S3، ويحلله محلل حسب صيغته، ويكتب ملخصاً موحداً في flight_logs، مثل المدة وأقصى ارتفاع وتغيرات الوضع والتحذيرات. ولا ترفع سجلات طيران جهتك إلى مواقع تحليل عامة، والسبب أن السجل فيه كل مواقع الطيران بدقة.
الفيديو: نسخة مباشرة وملف أصلي
شرحنا كيف يعمل البث وما الذي يسبب التأخير في مقال بث الفيديو، وشرحنا WebRTC في مقال WebRTC من الداخل، وما يخص الطائرات هنا ثلاثة أمور.
الأمر الأول أن البث من الطائرة يمر على رابط ضعيف، لذلك يهمك بروتوكول الاستقبال أكثر من بروتوكول العرض، والمقارنة كما يلي:
| البروتوكول | فوق ماذا يعمل | على الرابط الضعيف |
|---|---|---|
| RTSP عبر TCP | TCP | لا يفقد شيئاً، ولكن كل حزمة ضائعة تؤخر ما بعدها، فيتراكم التأخير |
| RTSP عبر UDP | UDP | لا يتراكم التأخير، ولكن الحزمة الضائعة تظهر تشوهاً في الصورة |
| RTMP | TCP | مثل RTSP عبر TCP، وهو الخيار المدعوم في كثير من الأجهزة |
| SRT | UDP مع إعادة إرسال | يعيد إرسال الضائع خلال مهلة تحددها، ويتخلى عما تأخر بعدها |
| WebRTC مع WHIP | UDP | تأخير منخفض جداً، وتقدير للسرعة يخفض الجودة عند الضعف |
وقد قسنا التأخير لكل بروتوكول على نفس الجهاز في التجربة العملية.
الأمر الثاني خيارات DJI، فتحدد صفحة البث المباشر لـ DJI Dock 2 في مستودع التوثيق الأمر live_start_push، وفيه الحقل url_type بالقيم التالية:
| القيمة | البروتوكول |
|---|---|
| 0 | خدمة Agora |
| 1 | RTMP |
| 3 | GB28181 |
| 4 | WebRTC، وتقول الصفحة إنه يدعم WHIP فقط |
وفيه الحقل video_quality، والجودات كما في الصفحة نفسها:
| الجودة | الدقة | معدل البت |
|---|---|---|
| Smooth | 960×540 | 512 كيلوبت |
| Standard Definition | 1280×720 | 1 ميجابت |
| High Definition | 1280×720 | 1.5 ميجابت |
| Ultra High Definition | 1920×1080 | 3 ميجابت |
ولاحظ أن صفحة شرح الميزة في المستودع نفسه تذكر في موضع أن البث يدعم Agora وRTMP وGB28181 فقط، وتذكر WebRTC في جدول الأنواع، فاختبر الخيار الذي تريده على جهازك وإصدار برنامجه قبل أن تعتمد عليه. وبالتالي يكفي سيرفر وسائط يستقبل RTMP وWHIP، مثل MediaMTX، ليستقبل بث DJI مع غيره.
الأمر الثالث هو البيانات الوصفية Metadata المرتبطة بالفيديو: في الأنظمة الحكومية والعسكرية يحمل بث الطائرة موقعها واتجاه الكاميرا مع كل إطار بصيغة KLV بحسب المعيار MISB ST 0601، فتعرف من الإطار نفسه أين كانت الطائرة وإلى أين تنظر الكاميرا. ولم نستطع فتح نص المعيار من موقع NGA الرسمي وقت كتابة المقال، لأن الموقع يطلب تحققاً بشرياً، لذلك لا نذكر تفاصيل حقوله هنا. والبديل العملي في المنصات المدنية أن تربط الفيديو بالقياس عن بعد بالوقت، فكل تسجيل يعرف وقت بدايته، والقراءات تعرف أوقاتها، فتعرف موقع الطائرة عند أي ثانية من الفيديو.
وأما الملف الأصلي فيبقى على بطاقة الذاكرة بدقته الكاملة، ويرفع بعد الهبوط إلى S3 مع الإشارة إلى الطيران الذي ينتمي إليه، والنسخة المباشرة المسجلة تحذف بعد مدة قصيرة لأنها أقل جودة.
غرفة العمليات: الخريطة والتنبيهات والتحليل
الشاشة الحية لا تقرأ من قاعدة البيانات، وإنما من الحالة الحية في الذاكرة عبر WebSocket، والسبب أن قاعدة البيانات للتاريخ، أما «أين الطائرة الآن» فيجب أن يصل في أقل من ثانية حتى لو كانت قاعدة البيانات مشغولة. وقد شرحنا توصيل الموقع لكثير من المشاهدين في مقال تتبع المندوبين، والتنبيهات التي تحتاجها من اليوم الأول قليلة:
- انقطاع البيانات من طائرة أكثر من مدة محددة.
- بطارية تحت حد معين.
- ارتفاع فوق الحد المسموح، فالحد في الأنظمة السعودية كما في مقال الأنظمة.
- فرق كبير بين وقت القياس ووقت الاستلام.
- تغير مفاجئ في موقع GNSS لا تفسره سرعة الطائرة.
أما التحليل بعد الطيران فيقرأ من TimescaleDB ومن ملخصات السجلات، وGrafana يكفي له في البداية.
الأمان: من يستطيع أن يأمر الطائرة؟
السؤال الأخطر في هذه المنصة ليس من يرى البيانات، وإنما من يستطيع أن يرسل أمراً إلى طائرة في الجو، والصورة التالية تبين أين توضع الحماية:

توقيع رسائل MAVLink 2
بحسب صفحة توقيع الرسائل في توثيق 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.