পাঠ ৩৫ · ৫৭-এর মধ্যে · মডিউল ৮
Home / Courses / Microprocessors, Embedded Systems & IoT / রিলায়েবিলিটি ও ফল্ট টলারেন্স

এমবেডেড সিস্টেম রিলায়েবিলিটি ও ফল্ট টলারেন্স

Embedded system reliability and fault tolerance
১০ মিনিট পড়া মধ্যম · Intermediate Python সিমুলেশনসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • রিডানডেন্সি ও ভোটিং লজিক দিয়ে একটি ফল্টি সেন্সরকে কীভাবে সিস্টেম-স্তরে সামলানো যায়
  • গ্রেসফুল ডিগ্রেডেশনের ধারণা এবং কেন এটি "সব-অথবা-কিছুই-না" ডিজাইনের চেয়ে ভালো
  • চেকসাম/CRC দিয়ে ডেটা ইন্টিগ্রিটি যাচাইয়ের মূলনীতি এবং একটি সত্যিকারের গণনা
  • কীভাবে ওয়াচডগ টাইমার (L33), রিডানডেন্সি ও চেকসাম একসাথে একটি সামগ্রিক রিলায়েবিলিটি স্ট্র্যাটেজি গঠন করে

১ · কেন এমবেডেড সিস্টেমে রিলায়েবিলিটি আলাদাভাবে গুরুত্বপূর্ণ

একটি ডেস্কটপ অ্যাপ্লিকেশন ক্র্যাশ করলে ব্যবহারকারী সেটি আবার চালু করেন। কিন্তু একটি এমবেডেড সিস্টেম প্রায়ই এমন জায়গায় বসানো থাকে যেখানে কোনো মানুষ সরাসরি হস্তক্ষেপ করতে পারে না — একটি রিমোট সেন্সর নোড, একটি গাড়ির কন্ট্রোল ইউনিট, বা একটি ইন্ডাস্ট্রিয়াল মেশিনের কন্ট্রোলার (L03-এ এমবেডেড সিস্টেমের এই রিলায়েবিলিটি-প্রয়োজনীয়তা সংক্ষেপে উল্লেখ করা হয়েছিল)। তাই এমবেডেড ডিজাইনে ব্যর্থতা প্রতিরোধ করার পাশাপাশি ব্যর্থতা মোকাবিলা করার সুনির্দিষ্ট মেকানিজম রাখা হয় — এই পাঠে তিনটি প্রধান মেকানিজম দেখা হবে।

২ · রিডানডেন্সি ও ভোটিং

রিডানডেন্সিRedundancyএকটি গুরুত্বপূর্ণ উপাদানের একাধিক কপি রাখা, যাতে একটি ব্যর্থ হলেও বাকিগুলো কাজ চালিয়ে যেতে পারে। মানে একই কাজের জন্য একাধিক (dual = দুই, triple = তিন) উপাদান রাখা — যেমন একই মান মাপার জন্য দুই বা তিনটি আলাদা সেন্সর। যদি একটি সেন্সর ফল্টি হয়ে যায় (হার্ডওয়্যার ত্রুটি, লুজ কানেকশন, বা পরিবেশগত হস্তক্ষেপ), সিস্টেম একটি ভোটিং লজিক দিয়ে বাকি সুস্থ সেন্সরগুলোর মান থেকেই একটি বিশ্বাসযোগ্য ফলাফল বের করতে পারে — একক সেন্সরের উপর সম্পূর্ণ নির্ভরশীল না থেকে।

সেন্সর ১ ২৪.৮ সেন্সর ২ ২৫.১ সেন্সর ৩ ৯৯.৯ (ফল্টি!) ভোটার লজিক আউটলায়ার বাদ দেয় বিশ্বাসযোগ্য মান ~২৫.০ (৯৯.৯ বাদ)
তিনটি সেন্সরের মধ্যে একটি (সেন্সর ৩) স্পষ্টভাবে ফল্টি একটি মান দিচ্ছে — ভোটার লজিক সেটিকে আউটলায়ার হিসেবে শনাক্ত করে বাদ দিয়ে বাকি দুটির গড় থেকে একটি নির্ভরযোগ্য মান বের করে।

নিচের কোড সেলে ঠিক এই ভোটিং লজিকটি বাস্তবায়ন করা হয়েছে — মিডিয়ানের কাছাকাছি (একটি নির্দিষ্ট টলারেন্সের মধ্যে) থাকা রিডিংগুলোকে "একমত" ধরা হয় ও তাদের গড় নেওয়া হয়, আর মিডিয়ান থেকে অনেক দূরের রিডিংকে আউটলায়ার হিসেবে বাদ দেওয়া হয়।

Python
def redundant_read(sensor_readings, tolerance=0.5):
    """তিনটি (বা তার বেশি) রিডানডেন্ট সেন্সরের রিডিং থেকে ভোটিং করে একটি বিশ্বাসযোগ্য মান বের করে।
    মিডিয়ানের tolerance-এর মধ্যে থাকা রিডিংগুলোকে 'একমত' ধরে তাদের গড় নেওয়া হয়;
    মিডিয়ান থেকে tolerance-এর বাইরের রিডিংকে আউটলায়ার হিসেবে বাদ দেওয়া হয়।"""
    median_val = sorted(sensor_readings)[len(sensor_readings) // 2]
    agreeing = [r for r in sensor_readings if abs(r - median_val) <= tolerance]
    outliers = [r for r in sensor_readings if abs(r - median_val) > tolerance]
    voted_value = sum(agreeing) / len(agreeing)
    return voted_value, agreeing, outliers


print("=== দৃশ্য ১: তিনটি সেন্সরই সুস্থ, সামান্য স্বাভাবিক নয়েজসহ ===")
readings_normal = [24.8, 25.1, 24.9]  # °C, তিনটি তাপমাত্রা সেন্সরের রিডিং
voted1, agree1, out1 = redundant_read(readings_normal)
print(f"রিডিং: {readings_normal}")
print(f"ভোটেড মান: {voted1:.4f}°C  |  একমত রিডিং: {agree1}  |  আউটলায়ার: {out1}")

print("\n=== দৃশ্য ২: একটি সেন্সর ফল্টি (অনেক দূরের একটি ভুল মান দিচ্ছে) ===")
readings_faulty = [24.8, 99.9, 25.0]  # দ্বিতীয় সেন্সরটি সম্ভবত ডিসকানেক্টেড/ফল্টি
voted2, agree2, out2 = redundant_read(readings_faulty)
print(f"রিডিং: {readings_faulty}")
print(f"ভোটেড মান: {voted2:.4f}°C  |  একমত রিডিং: {agree2}  |  বাদ দেওয়া আউটলায়ার: {out2}")

print(f"\nদৃশ্য ২-তেও ভোটেড মান ({voted2:.2f}°C) সত্যিকারের তাপমাত্রার কাছাকাছি থেকে গেছে -- "
      f"একটি সেন্সর সম্পূর্ণ ব্যর্থ হলেও সিস্টেম কাজ করা বন্ধ করেনি, শুধু কমিয়ে-আনা "
      f"রিডানডেন্সি নিয়ে (২টি সেন্সর) চলছে -- এটাই 'গ্রেসফুল ডিগ্রেডেশন'।")

    

৩ · গ্রেসফুল ডিগ্রেডেশন

গ্রেসফুল ডিগ্রেডেশনGraceful Degradationএকটি উপাদান ব্যর্থ হলে সিস্টেম সম্পূর্ণ বন্ধ না হয়ে, কমিয়ে-আনা সক্ষমতা নিয়ে হলেও কাজ চালিয়ে যাওয়ার ডিজাইন নীতি। উপরের কোড সেলের দৃশ্য ২ ঠিক এই ধারণারই একটি প্রকৃত উদাহরণ — একটি সেন্সর ব্যর্থ হওয়া সত্ত্বেও সিস্টেম সম্পূর্ণ থেমে যায়নি বা ভুল ফলাফল দেয়নি, বরং কমিয়ে-আনা রিডানডেন্সি নিয়ে (৩টির বদলে ২টি সেন্সর) নির্ভুল কাজ চালিয়ে গেছে। বাস্তব সিস্টেমে এই নীতিটি আরও বড় পরিসরেও প্রয়োগ করা হয় — যেমন একটি স্মার্ট থার্মোস্ট্যাট তার WiFi সংযোগ হারালেও (M41-M42) স্থানীয়ভাবে শেষ জানা সেটিং অনুযায়ী কাজ চালিয়ে যেতে পারে, শুধু ক্লাউড ড্যাশবোর্ড আপডেট বন্ধ থাকে — পুরো ডিভাইস অচল হয়ে যায় না।

৪ · চেকসাম ও CRC দিয়ে ডেটা ইন্টিগ্রিটি

সেন্সর ফল্ট ছাড়াও, ডেটা ট্রান্সমিশনের সময় (L22-এর UART, বা M6-এর অন্যান্য প্রোটোকলে) নয়েজ বা ইন্টারফেয়ারেন্সের কারণে বিট করাপ্ট হয়ে যেতে পারে। চেকসাম হলো একটি সরল গাণিতিক কৌশল — পাঠানোর আগে পুরো ডেটার উপর একটি হিসাব (যেমন সব বাইটের XOR) করে একটি অতিরিক্ত "চেক বাইট" যোগ করা হয়; রিসিভার প্রান্তে একই হিসাব আবার করে সেটি চেক বাইটের সাথে মিলিয়ে দেখা হয় — না মিললে ডেটা করাপ্ট হয়েছে বলে ধরে নেওয়া হয়। বাস্তব প্রোটোকলে (Ethernet, অনেক ফাইল ফরম্যাট) সাধারণত এর চেয়ে শক্তিশালী CRC (Cyclic Redundancy Check) অ্যালগরিদম ব্যবহৃত হয়, যা XOR চেকসামের চেয়ে অনেক বেশি ধরনের করাপশন প্যাটার্ন ধরতে পারে — এই পাঠে ধারণাটি বোঝানোর জন্য সরল XOR চেকসাম ব্যবহার করা হয়েছে।

Python
def xor_checksum(data_bytes):
    """একটি বাইট সিকোয়েন্সের উপর সরল XOR চেকসাম -- সবগুলো বাইট একসাথে XOR করে একটি চেক-বাইট বানায়।"""
    checksum = 0
    for b in data_bytes:
        checksum ^= b
    return checksum


def make_frame(payload_bytes):
    """payload-এর শেষে checksum বাইট যোগ করে একটি 'ফ্রেম' বানায় (বাস্তব প্রোটোকলে ট্রান্সমিট হওয়া প্যাকেটের মতো)।"""
    return list(payload_bytes) + [xor_checksum(payload_bytes)]


def verify_frame(frame_bytes):
    """ফ্রেমের শেষ বাইট (checksum) বাদে বাকি অংশের উপর আবার checksum হিসাব করে,
    রিসিভড checksum-এর সাথে মিলিয়ে দেখে -- মিললে ফ্রেম অক্ষত, না মিললে করাপশন শনাক্ত।"""
    *payload, received_checksum = frame_bytes
    computed_checksum = xor_checksum(payload)
    return computed_checksum == received_checksum, computed_checksum


payload = [0x3F, 0x7A, 0x01, 0xE4, 0x88]  # সেন্সর থেকে পাঠানো ৫ বাইট ডেটা (উদাহরণ)
frame = make_frame(payload)

print("মূল payload:                     ", [format(b, '08b') for b in payload])
print("পাঠানো ফ্রেম (payload + checksum):", [format(b, '08b') for b in frame])

print("\n=== দৃশ্য ১: অক্ষত (uncorrupted) ফ্রেম রিসিভ হলো ===")
ok1, computed1 = verify_frame(frame)
print(f"রিসিভার computed checksum: {format(computed1, '08b')}, ফ্রেমে থাকা checksum: {format(frame[-1], '08b')}")
print(f"ভেরিফিকেশন পাশ করলো? {ok1}")

# একটি bit corrupt করা হচ্ছে -- ট্রান্সমিশনে নয়েজ/ইন্টারফেয়ারেন্সের সিমুলেশন
corrupted_frame = frame.copy()
corrupted_frame[2] ^= (1 << 3)  # payload-এর ৩নং বাইটের (index 2) bit 3 ফ্লিপ হলো

print("\n=== দৃশ্য ২: করাপ্টেড ফ্রেম রিসিভ হলো (byte 2-এর bit 3 ফ্লিপড) ===")
print("করাপ্টেড ফ্রেম:", [format(b, '08b') for b in corrupted_frame])
ok2, computed2 = verify_frame(corrupted_frame)
print(f"রিসিভার computed checksum: {format(computed2, '08b')}, ফ্রেমে থাকা checksum: {format(corrupted_frame[-1], '08b')}")
print(f"ভেরিফিকেশন পাশ করলো? {ok2}")

    
দৃশ্য ১-এ কোনো করাপশন হয়নি, তাই রিসিভারের হিসাব করা checksum ফ্রেমে থাকা checksum-এর সাথে হুবহু মিলে যায় — ভেরিফিকেশন পাশ করে। দৃশ্য ২-এ শুধু একটি বিট ফ্লিপ করা হয়েছে, তাতেই XOR checksum সম্পূর্ণ ভিন্ন একটি মান দেয় (কারণ XOR-এ একটি ইনপুট বিট বদলালে আউটপুট বিটও বদলে যায়) — ফলে রিসিভারের হিসাব ফ্রেমের পুরনো checksum-এর সাথে মেলে না, এবং কোড সেলটি সঠিকভাবে করাপশন শনাক্ত করে। প্রসঙ্গত, XOR checksum একটি সরল কিন্তু সীমাবদ্ধ কৌশল — যদি দুটি ভিন্ন বাইটের ঠিক একই বিট পজিশন একসাথে ফ্লিপ হয়, তাদের প্রভাব XOR-এ একে অপরকে কাটাকাটি করে ফেলতে পারে এবং করাপশন অধরা থেকে যেতে পারে; বাস্তব প্রোটোকলে CRC এই দুর্বলতাগুলো অনেকাংশে কাটিয়ে ওঠার জন্যই ব্যবহৃত হয়।

৫ · ওয়াচডগ — চতুর্থ স্তম্ভ

L33-এ দেখা ওয়াচডগ টাইমারও এই পাঠের রিলায়েবিলিটি স্ট্র্যাটেজিরই একটি অংশ — তবে এটি ভিন্ন ধরনের ব্যর্থতা সামলায়। রিডানডেন্সি/ভোটিং সামলায় সেন্সর/হার্ডওয়্যার ডেটার ভুল মান, চেকসাম সামলায় ট্রান্সমিশন/স্টোরেজে ডেটা করাপশন, আর ওয়াচডগ সামলায় ফার্মওয়্যার নিজেই হ্যাং করে যাওয়া (একটি অসীম লুপ বা ডেডলক)। একটি সুরক্ষিত, বাণিজ্যিক-মানের এমবেডেড ডিজাইনে সাধারণত এই তিনটি মেকানিজমই একসাথে থাকে — একটি একা যথেষ্ট নয়, কারণ প্রতিটি ভিন্ন ধরনের ব্যর্থতা থেকে রক্ষা করে।

মূল কথা · Key takeaway

এমবেডেড সিস্টেম রিলায়েবিলিটি একটি একক কৌশল নয়, বরং একাধিক পরিপূরক মেকানিজমের সমষ্টি — রিডানডেন্সি ও ভোটিং দিয়ে সেন্সর/হার্ডওয়্যার ফল্ট সামলানো, গ্রেসফুল ডিগ্রেডেশন দিয়ে আংশিক ব্যর্থতাতেও সিস্টেমকে সচল রাখা, চেকসাম/CRC দিয়ে ডেটা ইন্টিগ্রিটি যাচাই করা, এবং ওয়াচডগ টাইমার (L33) দিয়ে ফার্মওয়্যার হ্যাং থেকে রক্ষা পাওয়া। এই সবগুলো মেকানিজমই M34-এর পাওয়ার বাজেটের মধ্যে থেকেই কাজ করতে হয় — অতিরিক্ত রিডানডেন্সি বা ঘন ঘন চেকসাম গণনাও কিছু বাড়তি কারেন্ট খরচ করে, তাই বাস্তব ডিজাইনে রিলায়েবিলিটি ও পাওয়ার বাজেট একসাথে ভারসাম্য রেখে ঠিক করতে হয়।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।

প্র ০১ উপরের redundant_read() ফাংশনে যদি শুধু দুটি সেন্সর থাকতো (তিনটির বদলে), এবং তারা দুজন সম্পূর্ণ ভিন্ন মান দিতো, তাহলে "মিডিয়ান" ধারণাটি কি এখনো ঠিকভাবে কাজ করবে?

না, দুটি সেন্সরের ক্ষেত্রে মিডিয়ানের ধারণাটি অস্পষ্ট হয়ে যায় — কারণ দুটি ভিন্ন মানের মধ্যে কোনটি "প্রকৃত" মিডিয়ান তা বলা যায় না, দুজনেই সমানভাবে বিশ্বাসযোগ্য বা অবিশ্বাসযোগ্য দেখাতে পারে। এই কারণেই বাস্তব রিডানডেন্ট সিস্টেমে প্রায়ই বিজোড় সংখ্যক (৩, ৫...) উপাদান ব্যবহার করা হয় — তথাকথিত ট্রিপল মডুলার রিডানডেন্সি (TMR) — যাতে সবসময় একটি স্পষ্ট সংখ্যাগরিষ্ঠ ("majority") পাওয়া যায়।

প্র ০২ একটি checksum ভেরিফিকেশন ব্যর্থ হলে (দৃশ্য ২-এর মতো), রিসিভার প্রান্তের ফার্মওয়্যার আসলে কী করা উচিত?

করাপ্টেড ডেটাটি ব্যবহার করা উচিত নয় — সাধারণ প্র্যাকটিস হলো সেই ফ্রেমটি বাতিল করে দেওয়া এবং (প্রোটোকল সাপোর্ট করলে) পাঠানো প্রান্তকে পুনরায় পাঠানোর অনুরোধ করা (retransmission — M48-এ CoAP-এর প্রসঙ্গে এই প্যাটার্নটি আরও বিস্তারিত দেখা যাবে), অথবা যদি রিয়েল-টাইম রিডিং হয়, শুধু সেই নির্দিষ্ট স্যাম্পলটি বাদ দিয়ে পরের রিডিং-এর জন্য অপেক্ষা করা — ভুল ডেটাকে সঠিক ডেটা হিসেবে ব্যবহার করার চেয়ে একটি রিডিং হারানো ঢের নিরাপদ।

প্র ০৩ রিডানডেন্সি, চেকসাম ও ওয়াচডগ — এই তিনটির মধ্যে কোনটি সবচেয়ে "সস্তা" (সবচেয়ে কম বাড়তি হার্ডওয়্যার/এনার্জি প্রয়োজন) বলে আপনার মনে হয়, এবং কেন?

সাধারণভাবে ওয়াচডগ সবচেয়ে সস্তা — এটি প্রায় সব মাইক্রোকন্ট্রোলারে বিল্ট-ইন থাকে, আলাদা কোনো হার্ডওয়্যার লাগে না, এবং কারেন্ট খরচও নগণ্য। চেকসাম শুধু কিছু বাড়তি CPU সাইকেল ও এক বা কয়েক বাইট বাড়তি ডেটা লাগে। রিডানডেন্সি সবচেয়ে ব্যয়বহুল — এতে সত্যিকারের অতিরিক্ত ফিজিক্যাল সেন্সর/হার্ডওয়্যার লাগে (বাড়তি খরচ, বাড়তি বোর্ড স্পেস, বাড়তি পাওয়ার), তাই এটি সাধারণত শুধু নিরাপত্তা-জটিল (safety-critical) অ্যাপ্লিকেশনেই ব্যবহৃত হয় যেখানে খরচ যৌক্তিক।

অনুশীলন

  1. চিন্তা করুন: উপরের redundant_read() কোড সেলে tolerance-এর মান ০.৫ থেকে ০.০৫-এ কমালে দৃশ্য ১-এর ফলাফলে কী প্রভাব পড়বে বলে আপনার ধারণা (যেখানে রিডিংগুলো ছিল ২৪.৮, ২৫.১, ২৪.৯)?

    মিডিয়ান ২৪.৯ থেকে ২৫.১-এর দূরত্ব ০.২, যা ০.০৫ টলারেন্সের চেয়ে বেশি — তাই এত কড়া টলারেন্সে ২৫.১ও এখন আউটলায়ার হিসেবে বাদ পড়ে যাবে, যদিও এটি একটি স্বাভাবিক নয়েজি রিডিং, প্রকৃত ফল্ট নয়। এটি দেখায় টলারেন্স খুব ছোট রাখলে স্বাভাবিক সেন্সর নয়েজকেও ভুলভাবে "ফল্ট" হিসেবে ধরে ফেলার ঝুঁকি থাকে — টলারেন্স মান বাছাই করাও একটি বাস্তব ডিজাইন ট্রেড-অফ, ঠিক L33-এর ওয়াচডগ টাইমআউটের মতোই।

  2. পরীক্ষা করুন: চেকসাম কোড সেলে corrupted_frame[2] ^= (1 << 3) লাইনটি corrupted_frame[-1] ^= (1 << 3)-এ বদলে দিন (অর্থাৎ payload-এর বদলে checksum বাইট নিজেই করাপ্ট করুন) — Run চেপে দেখুন ok2-এর মান তখনও False থাকে কিনা।

    হ্যাঁ, তখনও False থাকবে — কারণ verify_frame() রিসিভড checksum বাইটকেই (এখন করাপ্টেড) মূল payload থেকে নতুন করে হিসাব করা checksum-এর সাথে তুলনা করে, আর payload অপরিবর্তিত থাকায় নতুন হিসাব আগের মতোই থাকবে, যা করাপ্টেড checksum বাইটের সাথে মিলবে না — এটি দেখায় checksum যাচাই শুধু payload-এর করাপশন নয়, checksum বাইট নিজে করাপ্ট হলেও তা সমানভাবে ধরতে পারে।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

আগের পাঠ
পাওয়ার বাজেটিং ও ব্যাটারি লাইফ এস্টিমেশন