Maritime Sensors Integration

เมื่อโลกเบื้องหลังของ Server ไม่ได้ทำงานอยู่ลำพัง แต่ต้องรับมือกับข้อมูลจากทะเลที่ส่งมาแบบไม่มีวันหยุด
20 มกราคม 2026เวลาอ่าน 7 นาที#softwareengineer#gis

สวัสดีครับวัยรุ่น วันนี้เรามาคุยกันเรื่องที่หลายคนอาจจะไม่ค่อยได้นึกถึง นั่นก็คือ... เซ็นเซอร์ทางทะเล ถ้าคุณเคยเขียนโค้ดรับข้อมูลจาก Form ในเว็บ หรือจากฐานข้อมูลปกติๆ แล้วละก็ เตรียมพบกับมิติใหม่ที่ว่า... ข้อมูลมันไม่ได้เกิดจากการที่คนนั่งกรอกด้วยมือเท่านั้น แต่มันวิ่งมาจากเรือที่อยู่กลางทะเล บอกตำแหน่ง ความเร็ว ทิศทาง อุณหภูมิ ความกดอากาศ และอีกเพียบ... แบบ Real-Time 24/7 ไม่มีวันหยุด

ครั้งแรกที่ผมได้เจอระบบแบบนี้ (เมื่อสิบกว่าปีก่อนน่ะ) เอาจริงๆ ตะลึงเลยครับ เพราะมันไม่ใช่แค่การเขียน CRUD (Create, Read, Update, Delete) ธรรมดา แต่มันคือการต้องเตรียม Server ให้พร้อมรับมือกับข้อมูลที่ขยันโคตรๆ ส่งมาไม่หยุดหย่อน (Streaming Data) ส่งมาจากหลายแหล่งสลับไปสลับมา (Out of Order) บางทีก็หาย บางทีก็ผิด (Package Loss/Drift/Mulfunction) และที่สำคัญคือ มาเกิดเยอะโคตรๆ แบบว่าวันต้องหาใส่กันเป็นล้าน Record ซึ่งมันมีที่มา...

AIS: เมื่อเรือทุกลำต้อง Check-in ตลอดเวลา

ถ้าคุณเคยได้ยินคำว่า AIS มาบ้างล่ะก็ มันก็คือค่ายโทรศัพท์สีเขียน ผ่ามม! มันคือ Automatic Identification System เป็นระบบที่เรือทุกลำ (โดยเฉพาะเรือขนาดใหญ่) ต้องติดตั้งเครื่องส่งสัญญาณ เพื่อบอกตำแหน่ง ความเร็ว ทิศทาง ชื่อเรือ และข้อมูลอื่นๆ เพื่อเป็นการบอกว่าตัวเองคือใคร กำลังจะไปไหน มีวัตถุประสงค์หลักก็คือแจ้งเรือลำอื่นในพื้นที่ใกล้เคียงเพื่อความปลอดภัย ถ้าจะถามว่าทะเลตั้งกว้างใหญ่ มันจะชนกันได้ยังไง แต่เชื่อสิ เหตุเรือชนกันมีเป็นร้อยๆ พันๆ เคสในแต่ละปี ซึ่งสัญญาณ AIS นี้จะแบ่งย่อยออกเป็น Class ซึ่งที่ใช้งานบนเรือเป็นหลักคือ Class A (สามารถส่งขึ้นดาวเทียมและบริเวณใกล้เคียง) กับ Class B (สามารถส่งในบริเวณใกล้เคียงได้เท่านั้น) แต่มันมีผลพลอยได้ก็คือ มันจะมีหน่วยงานยามฝั่ง Sensor ไปดักรับสัญญาณเหล่านั้น เพื่อมาเฝ้าระวังป้องกันและดูพฤติกรรมเรือที่มีแนวโน้มเกิดอันตรายหรือกระทำความผิด (ในบางกรณี)

ทีนี้ลองนึกภาพนะครับ เรือหนึ่งลำส่งข้อมูลมาทุกๆ 2-10 วินาที สำหรับ Class A, ClassB และ 2 ชั่วโมง สำหรับ Class A ในกรณีส่งขึ้นดาวเทียม (ขึ้นอยู่กับความเร็วและสถานะ) ถ้ามีเรือในพื้นที่ของคุณหลักร้อยลำ คุณก็ต้องรับข้อมูลเป็นหลักพันหลักหมื่นข้อมูลต่อชั่วโมง แต่... เรือที่วนเวียนเข้าออกชายฝั่งทั่วประเทศไทยมันไม่ได้มีหลักร้อยน่ะสิฮะ

Various Sensors

แล้วข้อมูล Sensor ที่ได้มามันมีโครงสร้างยังไงล่ะ... ซึ่งแน่นอนว่าต้นทางแล้วมันไม่ได้เป็น JSON อะนะ ปกติจะมาในรูปแบบของ NMEA (National Marine Electronics Association) Messages ซึ่งเป็น Protocol มาตรฐานสากล ซึ่งมันจะ Stream เป็น Byte String ยาวๆ ออกมาจาก Serial Port ของอุปกรณ์รับสัญญาณ AIS มันก็จะมีหน้าตาข้อมูลอยู่ 27 Messages ด้วยกัน (ดูเพิ่มเติมได้ที่นี่ https://www.navcen.uscg.gov/ais-messages) แต่ก็มีตัวอย่างข้อมูล AIS Message ตามนี้

text
!AIVDM,1,1,,B,139K?4?01;7<bb`7Bk2b5`4J0H88,0*2D\r\n

แน่นอนว่าเจอครั้งแรกผมแตก งง มันคือไรฟระ แล้วเราจะรู้ได้ไงว่าต้องเริ่มแก้จากตรงไหน แล้วจะแก้ Hash ตรงกลางนี่ยังไง แต่ก็แน่นอนว่ามันมีเอกสารกำกับอยู่แล้ว เพราะมันเป็นมาตรฐานที่เผยแพร่กันทั่วไป ซึ่งรายละเอียดของ Message คร่าวๆ อันดับแรกเลย ข้อความชุดแรก !AIVDM มันก็คือหัวข้อที่บอกว่าอันนี้ Message อะไร (Identification) และถ้าเราใช้ Comma Separate มันก็จะได้แต่ละชุดข้อมูลตามนี้

  • !AIVDM ! บอกว่า Payload นี้เข้ารหัส Base64 6-bit ASCII, AI ประเภทอุปกรณ์, VDM (VHF Data-link Message)
  • 1,1, จำนวนส่วนของข้อความ เช่น 1/1, 1/2
  • B ช่องสัญญาณ (Radio Channel B) หรือ Class น่ะแหละ
  • 139K?... Base64 6-bit ASCII Encoded Payload
  • 0*2D Checksum

และเมื่อแกะ Payload จาก 6-bit ASCII เป็น Binary แล้วนั้น...

  • Message ID (6 bits) 1 - Scheduled Position Report
  • Repeat Indicator (2 bits) 0 - ส่งว่าข้อความนี้เป็นข้อความทวนซ้ำกี่รอบ
  • User ID (30 bits) 211... - หมายเลข MMSI เป็นหมายเลขประจำอุปกรณ์ AIS สำหรับระบุตัวตน (สำคัญโคตรๆ)
  • Navigational Status (4 bits) 15 - แปลว่า กูไม่บอก 555 (Not defined ไม่กำหนด)
  • Rate of Turn (8 bits) 0 - ณ ขณะนี้ไม่ได้เลี้ยว
  • Speed Over Ground (10 bits) 7.5 - ความเร็ว หน่วยเป็นน็อต (Knot)
  • Position accuracy (1 bit) 0 - ไม่ได้กำหนดความแม่นยำ ซึ่งปกติแล้วจะมากกว่า 10 เมตร
  • Longitude (28 bits) 100.634166666667 - องศาตะวันออก
  • Latitude (27 bits) 12.7466833333333 - องศาเหนือ
  • Course Over Ground (12 bits) 258.2 - ทิศทางของเรือ
  • True heading (9 bits) 258 - ทิศหัวเรือ
  • Time stamp (6 bits) 13 - เวลาของ Message 13 วินาที จาก UTC
  • special maneuver indicator (2 bits) 0 - ไม่ได้แจ้งข้อมูลการเดินเรือ
  • Spare (3 bits) ว่างงงงงง
  • RAIM-flag (1 bit) 0 - ไม่ใช้ Receiver Autonomous Integrity Monitoring (RAIM)
  • Communication State (19 bits) เกี่ยวกับข้อมูล Slot สำหรับ TDMA

ข้อมูลเหล่านี้แหละ ที่ต้องเอามาแปลงลง Database เพื่อที่จะเอาไปใช้งานต่อไป ทั้งการแสดงผล วิเคราะห์ข้อมูล รวมถึงการดูพฤติกรรมต่างๆ แต่ส่วนที่สำคัญก็คือ ไอ้ข้อมูลเหล่านี้แลหะ มันส่งมาแบบไม่รอใครน่ะสิ และไม่มี Request-Response แบบ REST API ที่เราคุ้นเคย แต่มันคือ Data Stream ที่ Server เราต้องพร้อมรับตลอดเวลา

ปัญหาที่เจอจริงและวิธีแก้

แน่นอนว่าข้อมูลพวกนี้อาจมีความผิดพลาดจากตัว Sensor เอง หรือตัว Parser ก็แล้วแต่ มันก็จะกลายเป็นปัญหาที่เราจะต้องแก้

GPS Drift - เรือเดินขึ้นบก คุณรู้ไหมว่า GPS มันไม่ได้แม่นยำ 100% เสมอไป บางทีสัญญาณ GPS ของเรืออาจจะบอกว่าเรืออยู่บนบก หรือกระโดดตำแหน่งไปมาแบบผิดธรรมชาติ (อ้อ ลืมบอกไป AIS มันไม่สามารถทำงานด้วยตัวมันเองได้ในการบอกตำแหน่ง ความเร็ว หรือทิศทาง แต่จะใช้ข้อมูลจาก GPS ซึ่งเป็น NMEA 0183 เหมือนกัน อาจจะขึ้นด้วย $GPGGA หรือ $GPGLL) วิธีแก้ก็คือ Algorithm ตรวจสอบความสมเหตุสมผล เช่น ตรวจสอบว่าความเร็วที่คำนวณได้มันเป็นไปได้ไหม (เรือ Cargo หรือตู้คอนเทนเนอร์ ไม่มีทางวิ่ง 100 Knots ได้) ตรวจสอบว่าตำแหน่งใหม่อยู่บนน้ำหรือบนบก (ใช้ GIS spatial query) และถ้าข้อมูลผิดปกติ ให้ใช้ค่าจาก ญrediction Model แทน หรือตั้ง Flag ไว้ว่าข้อมูลนี้ไม่น่าเชื่อถือ

Out of Order Messages เพราะข้อมูลมาจากหลายช่องทาง (หลายสถานีรับสัญญาณ) บางที Message ที่ส่งทีหลังอาจจะมาถึง server ก่อน ดังนั้น จะต้องใส่ Timestamp ทุก Message รวมทั้งใช้ Sliding Window Buffer เก็บข้อมูลไว้สัก 30 วินาที ก่อนจะ Process และ Sort ตาม timestamp ก่อนจะนำไปใช้

Data Volume ใหญ่เกินไป ถ้าเก็บทุก Message ลง Database แบบไม่มีการคัดกรองพื้นที่เต็มแน่นอน แนวทางคือ ใช้ Downsampling ซึ่งถ้าเรือจอดนิ่ง เราก็ไม่จำเป็นต้องเก็บทุกวินาที เก็บแค่ทุกนาทีหรือทุกชั่วโมงก็พอ ใช้ Data Retention Policy ในข้อมูลแบบ Raw ให้เก็บไว้แค่ 7-30 วัน ส่วนข้อมูลที่ Aggregate แล้วเก็บไว้นานกว่า

การนำไปประยุกต์ใช้กับ GIS

ที่กล่าวมานี่คือการแปลงข้อมูลเข้ามาเพื่อประมวลผลตาม Data Analysis และเก็บลง Database แต่การเอาไปใช้นี่ก็อีกเรื่องนึงเลยหละ โดยเฉพาะการวิเคราะห์พฤติกรรม และอื่นๆ อีกมากมาย แต่ขอกั๊กไว้ก่อนดีกว่า แล้วจะมาเขียนให้คราวหน้า เพราะการเอาไปใช้งานมันเยอะมาก ส่วนฟีเจอร์หลักๆ ก็ประมาณ

  • Geofencing: กำหนดเขตพื้นที่ แล้วแจ้งเตือน เมื่อเรือเข้าหรือออกจากพื้นที่นั้น เช่น เรือประมงเข้า Exclusive Economic Zone (EEZ) ของประเทศอื่น เรือเข้าพื้นที่อนุรักษ์ทางทะเล หรือเรือเข้าเขตอันตราย (พายุ, สงคราม)
  • Collision Detection การคำนวณเส้นทางของเรือ และเตือนถ้ามีโอกาสชนกัน (เห็นมะ)
  • Port Management ติดตามเรือที่กำลังเข้า-ออกท่าเรือ วางแผนการจัดการเรียงคิว เพราะร่องน้ำมันมีจำกัด เหมือนบัตรจับมือ
  • Maritime Surveillance ตรวจจับพฤติกรรมผิดปกติ เช่น เรือที่ปิด AIS ในพื้นที่ที่ควรจะเปิด (อาจจะเป็นการทำผิดกฎหมาย) เรือที่แล่นไปมาในรูปแบบแปลกๆ (อาจจะเป็นการลักลอบขนของผิดกฎหมาย) ซึ่งโดยปกติแล้วเรือจะวิ่งตามเส้นทางประจำ หรือเรือที่มา Rendezvous (นัดพบ) กลางทะเล (อาจจะเป็นการถ่ายน้ำมันเถื่อน)
  • Environmental Monitoring เป็นการรวมข้อมูลจาก Weather Sensors, Wave sensors เพื่อสร้างแผนที่สภาพทะเล ข้อมูลเหล่านี้ก็เป็น Sensor อีกชนิดหนึ่ง ที่มีหน่วยงานรับผิดชอบ ให้เดาไปก่อนว่าหน่วยงานไหน
  • Route Optimization วิเคราะห์ข้อมูลเส้นทางเรือในอดีต เพื่อแนะนำเส้นทางที่ประหยัดเชื้อเพลิงที่สุด

Maritime Map

ตัวอย่างการ Process AIS Message

และแล้วก็มาถึงตัวอย่าง Code ซึ่งผมขอแชร์โค้ดง่ายๆ ในการ Decode AIS Message แบบ Type 1 (Position Report) ที่บอกว่าง่ายคือเอาไปทดสอบแล้วมันเริ่มจากอย่างงี้ก่อนแหละ เสร็จแล้วก็เอาพวกนี้ประกอบร่างเป็น Service ใหญ่สำหรับ Sensor Integration อีกที

python
import math

class AISDecoder :
  @staticmethod
  def asciiToBinary(payload) :
    binaryStr = ""
    for char in payload :
      val = ord(char) - 48
      if val > 40 :
        val -= 8
      binaryStr += format(val, '06b')
    return binaryStr

  @staticmethod
  def decodeSignedInt(binaryStr) :
    val = int(binaryStr, 2)
    if val & (1 << (len(binaryStr) - 1)):
      val -= (1 << len(binaryStr))
    return val

  @staticmethod
  def decodeRot(bits) :
    raw = AISDecoder.decodeSignedInt(bits)
    return (raw/4.733)**2 if raw != -128 else 0

  @staticmethod
  def decodePositionReport(payload) :
    binary = AISDecoder.asciiToBinary(payload)
    if len(binary) < 143 :
      return None
    return {
      "messageType": int(binary[0:6], 2),
      "mmsi": int(binary[8:38], 2),
      "navigationStatus": int(binary[38:42], 2),
      "ROT": AISDecoder.decodeRot(binary[42:50]),
      "SOG": int(binary[50:60], 2) / 10.0,
      "longitude": AISDecoder.decodeSignedInt(binary[61:89]) / 600000.0,
      "latitude": AISDecoder.decodeSignedInt(binary[89:116]) / 600000.0,
      "COG": int(binary[116:128], 2) / 10.0,
      "trueHeading": int(binary[128:137], 2),
      "timestamp": int(binary[137:143], 2)
    }
สรุปว่าทำไมมันสำคัญและมันส์

การทำงานกับ Maritime Sensors Integration มันทำให้ผมได้เห็นว่าการเขียนโค้ดไม่ได้จบแค่ในโลกของ Server และ Database แต่มันเชื่อมโยงไปถึงโลกกายภาพ ถึงเรือที่แล่นอยู่ในทะเล ถึงคลื่นลมที่กำลังเปลี่ยนแปลง ถึงชีวิตของคนบนเรือที่อาจจะพึ่งพาข้อมูลที่เราประมวลผล และที่สำคัญคือ มันทำให้เห็นว่า Full Stack Dev ในยุคนี้ไม่ใช่แค่ Frontend กับ Backend แล้วเลือก Tools แต่มันรวมถึง

  • Hardware Layer - Sensors, IoT devices
  • Network Layer - Data Link, Collision Avoidance
  • Data Layer - Streaming, Storage, Processing
  • Application Layer - APIs, Business Logic
  • Presentation Layer - Web, Mobile, Dashboard
  • Analytics Layer - AI/ML, Reporting, Insights

และถ้าคุณมี Background ด้าน GIS ด้วย คุณจะมีความได้เปรียบอย่างมาก เพราะข้อมูลส่วนใหญ่มันเกี่ยวกับตำแหน่ง และเวลา ซึ่งเป็นรากฐานของ Spatial-Temporal Analysis และอะไรต่อมิอะไรอื่นๆ อีก สุดท้ายก็เจอกันใหม่ตอนต่อไปนะครับวัยรุ่น ส่วนจะมาเขียนอะไรอีกก็แนวๆ นี้แหละฮะ ส่วนถ้ามัน Geek ไปก็ Comment มาบอกได้ครับ ส่วนช่องทางก็หาเอา 555