Tư duy hệ thống qua lập trình: thành phần, quan hệ, vòng phản hồi và mô phỏng bể nước trên ESP32

Rèn tư duy hệ thống cho trẻ qua lập trình: thành phần, quan hệ, vòng phản hồi cân bằng và tăng cường, kèm mô phỏng bể nước có bơm tự động trên ESP32 không cần linh kiện.

Nhiều vấn đề trong đời sống không có một nguyên nhân duy nhất: giao thông ùn tắc, cây trong chậu héo, một bạn trong lớp ngày càng ngại học. Muốn hiểu chúng, cần nhìn thấy các thành phần, quan hệ giữa chúng và cách chúng thay đổi theo thời gian. Cách nhìn đó gọi là tư duy hệ thống.

Lập trình là nơi trẻ được "làm ra" hệ thống và thấy nó chạy. Bài viết giải thích tư duy hệ thống bằng ví dụ bể nước có bơm tự động, phân biệt hai loại vòng phản hồi, rồi hướng dẫn một mô phỏng chạy trên mạch ESP32 không cần linh kiện, để trẻ tự thí nghiệm và rút ra quy luật.

1. Tư duy hệ thống là gì

Theo Donella Meadows (Thinking in Systems, 2008), một hệ thống gồm các thành phần, các mối quan hệ nối chúng với nhau và một mục đích hay chức năng. Tư duy hệ thống khác cách nghĩ theo đường thẳng "A gây ra B": nó tìm các vòng mà B quay lại tác động lên A.

Sơ đồ hệ thống bể nước: bơm cho nước vào, vòi xả cho nước ra, cảm biến đo mực nước gửi số đo về chương trình, chương trình bật bơm khi dưới 40 lít và tắt khi trên 80 lít, tạo thành vòng phản hồi
Hình minh họa: thành phần và quan hệ của hệ thống bể nước có bơm tự động.

Bể nước có bơm tự động là một hệ thống nhỏ: bể chứa nước (trạng thái của hệ thống), bơm cho nước vào, vòi cho nước ra, cảm biến đo mực nước và chương trình ra quyết định. Mục đích: luôn có nước dùng nhưng không tràn.

2. Hai loại vòng phản hồi

Hai sơ đồ vòng: vòng cân bằng giữa mực nước và bơm chạy với một dấu trừ và một dấu cộng; vòng tăng cường giữa luyện tập, làm giỏi hơn, thích hơn với ba dấu cộng
Hình minh họa: vòng cân bằng (C) và vòng tăng cường (T).
  • Vòng cân bằng giữ hệ thống quanh một mức: nước càng thấp thì bơm càng được bật, bơm chạy làm nước dâng lên. Tủ lạnh, máy điều hòa, cơ thể đổ mồ hôi khi nóng đều có vòng cân bằng.
  • Vòng tăng cường làm thay đổi lớn dần: càng luyện càng giỏi, càng giỏi càng thích, càng thích càng luyện. Theo chiều ngược lại cũng vậy, và đó là lý do một khó khăn nhỏ lúc đầu có thể lớn dần nếu không ai can thiệp.

Với trẻ, chỉ cần tập trả lời hai câu: "cái này tăng thì cái kia tăng hay giảm?" và "vòng này giữ cho ổn định hay làm thay đổi lớn dần?".

3. Lập trình rèn tư duy hệ thống thế nào

  • Chương trình là một hệ thống. Các biến là trạng thái, các hàm là thành phần, lời gọi hàm là quan hệ. Trẻ phải biết phần nào ảnh hưởng tới phần nào.
  • Robot và mạch điện chạy theo vòng đọc, quyết định, hành động. Hàm loop() của Arduino lặp mãi: đọc cảm biến, so sánh, điều khiển, rồi đọc lại. Đó chính là vòng phản hồi.
  • Thay một con số, cả hệ thống đổi cách chạy. Mô phỏng cho phép thử điều không làm được ngoài đời (bơm 1 giờ trong 6 phút, cho bể tràn mà không ướt sàn) và thấy hành vi theo thời gian trên đồ thị.

Về bằng chứng: nghiên cứu về tư duy hệ thống ở học sinh đã có từ lâu. Ví dụ, Orit Ben-Zvi Assaraf và Nir Orion (2005) mô tả các khả năng tư duy hệ thống của học sinh trung học cơ sở khi học về hệ Trái Đất. Cindy Hmelo-Silver và Merav Pfeffer (2004) thấy người mới học nhìn bể cá chủ yếu thấy các bộ phận, còn chuyên gia thấy hoạt động và chức năng của cả hệ thống. Tuy vậy, nghiên cứu đo trực tiếp tác động của việc học lập trình lên tư duy hệ thống ở trẻ nhỏ còn ít. Các hoạt động trong bài này là cách luyện các khả năng nói trên, chưa phải kết quả đã được đo đếm.

4. Thực hành: mô phỏng bể nước trên ESP32

Mô phỏng chạy trên một mạch ESP32 DevKit V1 cắm cổng USB máy tính, không cần thêm linh kiện:

  • Mực nước, bơm, vòi là các con số trong chương trình; mỗi 100 ms thật ứng với 1 giây mô phỏng.
  • Giữ nút BOOT trên mạch là mở thêm vòi (nước ra 4 lít/giây thay vì 2 lít/giây).
  • Đèn trên mạch (GPIO2) sáng khi bơm đang chạy.
  • Mở Tools → Serial Plotter ở 115200 baud để xem bốn đường: mực nước, bơm, ngưỡng bật, ngưỡng tắt.
Lệnh gõ trên Serial Monitor (kiểu xuống dòng "New Line")
LệnhTác dụng
1Chế độ phản hồi: bơm bật, tắt theo mực nước
2Chế độ hẹn giờ: bơm chạy 20 giây, nghỉ 20 giây, không nhìn mực nước
b40, t80Đặt ngưỡng bật bơm, ngưỡng tắt bơm (lít)
rLàm lại từ đầu (bể 50 lít)
pĐổi giữa kiểu Serial Plotter và kiểu chữ có thống kê mỗi phút
/*
  PYWORLD.VN - Mô phỏng bể nước có bơm tự động trên ESP32: học tư duy hệ thống và vòng phản hồi
  Không cần linh kiện: giữ nút BOOT (GPIO0) là mở thêm vòi xả, đèn trên mạch (GPIO2) sáng khi bơm chạy
  Mở Tools > Serial Plotter ở 115200 baud; lệnh: 1 phản hồi, 2 hẹn giờ, b<số> ngưỡng bật, t<số> ngưỡng tắt, r, p
*/

#define CHAN_NUT_XA 0            // nút BOOT trên mạch ESP32 DevKit V1
#define CHAN_DEN_BOM 2           // đèn xanh trên mạch
#define CHU_KY_MS 100            // mỗi 100 ms thật là 1 giây trong mô phỏng
#define DUNG_TICH 100.0          // lít
#define LUU_LUONG_BOM 5.0        // lít mỗi giây khi bơm chạy
#define XA_THUONG 2.0            // lít mỗi giây: nước dùng hằng ngày
#define XA_MANH 4.0              // lít mỗi giây khi giữ nút BOOT (mở thêm vòi)
#define HEN_GIO_BAT 20           // chế độ hẹn giờ: bơm chạy 20 giây
#define HEN_GIO_TAT 20           // rồi nghỉ 20 giây, không nhìn mực nước

float mucNuoc = 50.0;            // trạng thái của hệ thống: lượng nước trong bể
int nguongBat = 40;              // dưới mức này thì bật bơm
int nguongTat = 80;              // trên mức này thì tắt bơm
int cheDo = 1;                   // 1: phản hồi theo mực nước, 2: hẹn giờ
bool bomChay = false;
bool kieuPlotter = true;
unsigned long giay = 0;          // thời gian mô phỏng
unsigned long lanTruoc = 0;
int soLanBat = 0, giayTran = 0, giayCan = 0;   // thống kê trong mỗi phút mô phỏng

void inCheDo() {
  Serial.printf("Che do: %s | nguong bat %d | nguong tat %d\n", cheDo == 1 ? "phan hoi" : "hen gio", nguongBat, nguongTat);
}

void xuLyLenh() {
  if (!Serial.available()) {
    return;
  }
  String lenh = Serial.readStringUntil('\n');
  lenh.trim();
  if (lenh.length() == 0) {
    return;
  }
  char ma = lenh.charAt(0);
  int so = lenh.substring(1).toInt();
  if (ma == '1' || ma == '2') {
    cheDo = ma - '0';
  } else if (ma == 'b' && so >= 0 && so < nguongTat) {
    nguongBat = so;
  } else if (ma == 't' && so > nguongBat && so <= DUNG_TICH) {
    nguongTat = so;
  } else if (ma == 'r') {                 // làm lại từ đầu
    mucNuoc = 50.0;
    giay = 0;
    bomChay = false;
    soLanBat = giayTran = giayCan = 0;
  } else if (ma == 'p') {
    kieuPlotter = !kieuPlotter;
  } else {
    Serial.println("Lenh: 1 phan hoi | 2 hen gio | b<so> nguong bat | t<so> nguong tat | r lam lai | p doi kieu in");
  }
  inCheDo();
}

// Một bước mô phỏng ứng với 1 giây: quyết định bật/tắt bơm rồi tính lượng nước vào, ra
void buocMoPhong() {
  float xa = (digitalRead(CHAN_NUT_XA) == LOW) ? XA_MANH : XA_THUONG;
  bool truoc = bomChay;
  if (cheDo == 1) {
    // Vòng phản hồi: chương trình nhìn mực nước rồi mới quyết định
    if (!bomChay && mucNuoc < nguongBat) {
      bomChay = true;
    } else if (bomChay && mucNuoc > nguongTat) {
      bomChay = false;
    }
  } else {
    // Không có phản hồi: bơm chạy theo đồng hồ, mặc kệ bể đầy hay cạn
    bomChay = (giay % (HEN_GIO_BAT + HEN_GIO_TAT)) < HEN_GIO_BAT;
  }
  if (bomChay && !truoc) {
    soLanBat++;
  }
  mucNuoc += (bomChay ? LUU_LUONG_BOM : 0.0) - xa;
  if (mucNuoc >= DUNG_TICH) {
    mucNuoc = DUNG_TICH;                  // nước tràn ra ngoài
    giayTran++;
  } else if (mucNuoc <= 0) {
    mucNuoc = 0;                          // bể cạn, vòi không có nước
    giayCan++;
  }
  digitalWrite(CHAN_DEN_BOM, bomChay ? HIGH : LOW);
  giay++;
}

void setup() {
  Serial.begin(115200);
  pinMode(CHAN_NUT_XA, INPUT_PULLUP);
  pinMode(CHAN_DEN_BOM, OUTPUT);
  delay(200);
  Serial.println();
  Serial.println("Mo phong be nuoc 100 lit: bom 5 lit/giay, xa 2 lit/giay (giu BOOT: 4 lit/giay)");
  inCheDo();
  lanTruoc = millis();
}

void loop() {
  xuLyLenh();
  if (millis() - lanTruoc < CHU_KY_MS) {
    return;
  }
  lanTruoc += CHU_KY_MS;
  buocMoPhong();
  if (kieuPlotter) {
    // Bốn đường trên Serial Plotter; bơm vẽ ở mức 10 khi chạy cho dễ nhìn
    Serial.printf("muc_nuoc:%.1f,bom:%d,nguong_bat:%d,nguong_tat:%d\n", mucNuoc, bomChay ? 10 : 0, nguongBat, nguongTat);
  } else if (giay % 5 == 0) {
    Serial.printf("Giay %4lu | muc nuoc %5.1f lit | bom %s\n", giay, mucNuoc, bomChay ? "CHAY" : "nghi");
  }
  if (giay % 60 == 0) {
    if (!kieuPlotter) {
      Serial.printf("Phut %lu: bom khoi dong %d lan, tran %d giay, can %d giay\n", giay / 60, soLanBat, giayTran, giayCan);
    }
    soLanBat = giayTran = giayCan = 0;
  }
}

Nạp chương trình bằng Arduino IDE, chọn bo mạch DOIT ESP32 DEVKIT V1 và đúng cổng COM. Hàm buocMoPhong() là toàn bộ "luật" của hệ thống: quyết định bật hay tắt bơm, rồi cộng nước vào, trừ nước ra.

5. Năm thí nghiệm cho trẻ

Hai đồ thị mực nước trong 3 phút: chế độ phản hồi dao động đều giữa 40 và 80 lít, bơm bật khoảng 2 lần mỗi phút; chế độ hẹn giờ làm bể tràn 32 giây trong 3 phút; với ngưỡng 49 và 51 bơm bật khoảng 12 lần mỗi phút
Hình minh họa: mô phỏng theo đúng thuật toán của chương trình.
  1. Quan sát vòng cân bằng. Để chế độ 1 chạy 2 phút. Mực nước lên xuống đều trong khoảng 40–80 lít. Hỏi trẻ: vì sao nước không bao giờ tràn, không bao giờ cạn?
  2. Nhiễu từ bên ngoài. Giữ nút BOOT khoảng 1 phút. Nước ra nhiều hơn nên bơm phải chạy lâu hơn mới đầy, nhưng hệ thống vẫn tự giữ được mực nước. Đây là sức mạnh của phản hồi.
  3. Bỏ phản hồi. Gõ 2. Bơm chạy theo đồng hồ, mặc kệ bể đầy hay vơi: sau vài chu kỳ bể tràn. Hỏi: bơm hẹn giờ thiếu điều gì so với chế độ 1?
  4. Hai ngưỡng quá gần nhau. Gõ 1, rồi b49 và t51. Mực nước rất ổn định nhưng bơm bật tắt khoảng 12 lần mỗi phút thay vì 2 lần. Bơm thật bật tắt liên tục sẽ mau hỏng, vì vậy bộ điều khiển thật luôn để hai ngưỡng cách nhau.
  5. Giới hạn của hệ thống (học sinh lớn). Sửa XA_MANH thành 6 rồi nạp lại. Giữ nút BOOT: nước ra nhiều hơn bơm đưa vào, mực nước tụt dần dù bơm chạy liên tục. Phản hồi tốt cũng không vượt được giới hạn của thiết bị.
Hình minh họa Serial Monitor kiểu chữ: dòng giới thiệu bể 100 lít, chế độ phản hồi ngưỡng 40 và 80, mực nước mỗi 5 giây kèm trạng thái bơm chạy hay nghỉ, thống kê phút 1 bơm khởi động 2 lần, tràn 0 giây, cạn 0 giây
Hình minh họa: kết quả ở kiểu chữ sau khi gõ p.

Sau mỗi thí nghiệm, cho trẻ dự đoán trước rồi mới chạy, và vẽ lại đồ thị mực nước bằng tay. Dự đoán sai là cơ hội tốt nhất để hỏi "điều gì trong hệ thống làm nó chạy khác ý mình?".

6. Các khả năng tư duy hệ thống và cách luyện

Bảng sáu khả năng tư duy hệ thống: nhận ra thành phần, thấy quan hệ, nhận ra vòng phản hồi, nghĩ theo thời gian, thấy điều không nhìn thấy ngay, khái quát sang hệ thống khác; mỗi khả năng kèm hoạt động với bài bể nước
Hình minh họa: sáu khả năng và hoạt động tương ứng.

Từ bể nước, mở rộng sang các hệ thống quen thuộc. Cho trẻ vẽ sơ đồ: thành phần nào, mũi tên nào, có vòng cân bằng hay tăng cường không?

Hệ thống quanh trẻ
Hệ thốngThành phầnVòng phản hồi
Tủ lạnhngăn lạnh, cảm biến nhiệt, máy nénCân bằng: nóng lên thì máy nén chạy, đủ lạnh thì nghỉ
Cơ thể khi trời nóngda, mồ hôi, nãoCân bằng: nóng thì đổ mồ hôi, mồ hôi bay hơi làm mát
Chậu câyđất, nước, lá, ánh nắngĐất khô thì người tưới; quên tưới thì lá héo
Một kênh video được nhiều người xemvideo, người xem, đề xuấtTăng cường: nhiều người xem thì được đề xuất nhiều hơn
Thói quen họcthời gian luyện, kết quả, hứng thúTăng cường: theo chiều tốt hoặc chiều xấu

7. Xử lý lỗi thường gặp

Lỗi thường gặp
Hiện tượngNguyên nhân thường gặpCách xử lý
Serial Plotter không có đường nàoSai tốc độ baud; đang ở kiểu chữChọn 115200 baud; gõ p để về kiểu Plotter
Gõ lệnh không có tác dụngSerial Monitor chưa chọn "New Line"Chọn kiểu xuống dòng "New Line" rồi gõ lại
Đèn trên mạch không sáng khi bơm chạyMạch dùng chân khác cho đènĐổi CHAN_DEN_BOM theo chân đèn in trên mạch
Lệnh b hoặc t bị bỏ quaNgưỡng bật phải nhỏ hơn ngưỡng tắtĐặt ngưỡng tắt trước khi hạ ngưỡng bật, hoặc ngược lại

8. Nguồn tham khảo

  • Meadows, D. H. (2008). Thinking in Systems: A Primer. Chelsea Green Publishing.
  • Ben-Zvi Assaraf, O., & Orion, N. (2005). Development of system thinking skills in the context of earth system education. Journal of Research in Science Teaching, 42(5), 518–560.
  • Hmelo-Silver, C. E., & Pfeffer, M. G. (2004). Comparing expert and novice understanding of a complex system from the perspective of structures, behaviors, and functions. Cognitive Science, 28(1), 127–138.

9. Xem thêm

Cùng nhóm bài: học lập trình từ nhỏ và bằng chứng nghiên cứu; lập trình rèn cách giải quyết vấn đề có hệ thống; lập trình nuôi dưỡng tư duy sáng tạo; điều kiện để học lập trình phát triển tư duy toàn diện.


Tin tức liên quan

Cấu hình GRBL: các thông số $, steps/mm, tốc độ, gia tốc, homing và chế độ laser
Cấu hình GRBL: các thông số $, steps/mm, tốc độ, gia tốc, homing và chế độ laser

Giải thích các thông số $ của GRBL 1.1: steps/mm, tốc độ, gia tốc, đảo chiều bằng mặt nạ trục, về gốc homing, giới hạn mềm, giới hạn cứng, chế độ laser $32 với M3, M4 và các mã ALARM.

Dự án tổng hợp: đèn tự bật khi trời tối với quang trở, MOSFET và ESP32
Dự án tổng hợp: đèn tự bật khi trời tối với quang trở, MOSFET và ESP32

Dự án cuối loạt điện tử căn bản: quang trở trong cầu phân áp, lọc nhiễu, ngưỡng có trễ và chờ xác nhận, MOSFET điều khiển dải LED 12 V sáng dần bằng PWM, nguồn 12 V qua mạch hạ áp cho ESP32.

Đo biến dạng bằng cảm biến strain gauge
Đo biến dạng bằng cảm biến strain gauge

Giải thích strain, hệ số gauge, cầu Wheatstone, cấu hình quarter/half/full bridge và các bước thiết lập phép đo biến dạng tin cậy.

Giáo dục STEM là gì? STEM, STEAM khác nhau thế nào và vì sao giúp trẻ phát triển tư duy
Giáo dục STEM là gì? STEM, STEAM khác nhau thế nào và vì sao giúp trẻ phát triển tư duy

Giáo dục STEM là gì, gồm những lĩnh vực nào, STEM và STEAM khác nhau ra sao, giáo dục STEM ở trường phổ thông Việt Nam và cách bắt đầu STEM với trẻ ở nhà theo độ tuổi.

Hiệu chuẩn transmitter áp suất 4–20 mA: sơ đồ vòng, sai số theo khoảng đo, lúc nhận và sau chỉnh
Hiệu chuẩn transmitter áp suất 4–20 mA: sơ đồ vòng, sai số theo khoảng đo, lúc nhận và sau chỉnh

Hiệu chuẩn transmitter áp suất 4–20 mA: sơ đồ vòng dòng điện, công thức dòng và sai số theo phần trăm khoảng đo, trình tự lúc nhận và sau chỉnh, ví dụ 0–16 bar, lưu ý về HART và điểm 0.

I2C là gì: START, STOP, địa chỉ, ACK và cách đọc ghi thanh ghi trên ESP32
I2C là gì: START, STOP, địa chỉ, ACK và cách đọc ghi thanh ghi trên ESP32

Giải thích giao tiếp I2C ở mức từng bit: hai dây SDA, SCL, điện trở kéo lên, START, địa chỉ, ACK, STOP. Kèm hai chương trình ESP32: tự tạo tín hiệu I2C bằng GPIO và đọc ghi thanh ghi bằng Wire.

Tự học điện tử cho người mới bắt đầu: lộ trình 30 bài, dụng cụ và linh kiện cần có
Tự học điện tử cho người mới bắt đầu: lộ trình 30 bài, dụng cụ và linh kiện cần có

Lộ trình tự học điện tử căn bản trong 30 bài: điện là gì, linh kiện thụ động, diode, transistor, IC; dụng cụ cần chuẩn bị, cách học từng bài, an toàn và kế hoạch 10 tuần.

Góc STEM tại nhà và câu lạc bộ STEM: dụng cụ, an toàn, kế hoạch 8 buổi, ngày hội STEM
Góc STEM tại nhà và câu lạc bộ STEM: dụng cụ, an toàn, kế hoạch 8 buổi, ngày hội STEM

Cẩm nang cho phụ huynh và giáo viên: bố trí góc STEM tại nhà, dụng cụ theo độ tuổi, quy tắc an toàn, kế hoạch 8 buổi cho câu lạc bộ STEM và cách tổ chức ngày hội STEM ở trường.


Bình luận
Đã thêm vào giỏ hàng