Thông báo

Collapse
No announcement yet.

Hiệu ứng LED Cube 8x8x8 (LED 3D)

Collapse
X
 
  • Lọc
  • Giờ
  • Show
Clear All
new posts

  • Hiệu ứng LED Cube 8x8x8 (LED 3D)

    Hiện nay, e thấy LED Cube khá là hot, và hầu hết các bạn đều pí ở một chỗ là hiệu ứng, nên e mở 2pic này nhằm giúp một số bạn biết một số hiệu ứng cơ bản cho LED Cube 8x8x8 (có thể mở rộng nếu bạn thông minh hơn một chút). Video LED Cube của mình và các hiệu ứng:

    Có nhiều project sử dụng nhiều loại MCU khác nhau: 89xx, ATMEL, PIC, hay thậm chí là kết nối PC giống e, vì vậy, e sẽ mô tả thuật toán cơ bản trước, và sau đó là code bằng C (bạn nào xài ASM thì đổi sớm đi nhá).
    Kết quả xuất ra của mình sẽ là một mảng chứa các điểm LED sáng, và mỗi phần tử sẽ có 3 số là x, y, z tương ứng tọa độ của LED sáng đó. Mã thêm LED sáng sẽ là Add(x, y, z). [Hy vọng các bạn hỉu đc phần này]
    Ngoài ra mình còn sử dụng lệnh Get_time() để lấy thời gian, tùy vào compiler của bạn mà thay đổi.

    Hiệu ứng sóng:

    Hiệu ứng này mô tả một điểm dao động theo hàm sin làm mặt phẳng xung quanh dao động theo gây nên cảm giác sóng. Hiệu ứng này e góp nhặt trên trang instructable và đc nhiều ng xài vì tính đơn giản nhưng đẹp của nó.

    Cơ bản về thuật toán:
    Gọi chu kỳ sóng là T, bước sóng L, biên độ A.
    • Chọn một điểm trong mặt phẳng xy làm gốc, đặt tên là O
    • Quét tất cả mọi điểm trong mặt phẳng xy, gọi từng điểm là A
    • Tính khoảng cách AO theo định lý Pytago, gọi là R
    • Dùng công thức sóng u = A*Cos (2*PI*t/T - 2*PI*R/L)
    • Add từng điểm vào mảng sáng

    Vs 8x8x8, L là 7, A là 3.5, tọa độ O là (3.5, 3.5), còn T là 1s. Việc lấy t tùy vào cách các bạn lấy, có thể dùng timer, có thể đếm frame.

    Code C:
    Code:
    float L = 7; // Bước sóng
    float T = 1; // Chu kỳ
    float A = 3.5; // Biên độ
    float xo = 3.5; // Tọa độ điểm gốc O
    float yo = 3.5;
    
    for (byte y = 0; y < 8; y++) // Quét Y
    {
        for (byte x = 0; x < 8; x++) // Quét X
        {
            float r = Sqrt(Pow(x - xo, 2) + Pow(y - yo, 2)); // Lấy bán kính, Sqrt là căn bậc 2, Pow(x, y) là x mũ y
            float t = get_time(); // Lấy thời gian
            byte z = (byte)(A * Cos(2*PI*t/T - 2*PI*r/L) + 3.5); // Lấy cao độ z, 3.5 là vị trí cân bằng
            Add(x, y, z); // Lưu vị trí LED sáng
        }
    }
    Ngoài ra, bạn có thể tạo ra 1 hiệu ứng sóng khác bằng một thay đổi nho nhỏ. Bằng việc di chuyển điểm O ra rất xa (cỡ 5000), chúng ta có sóng song song (điểm phát sóng rất xa), và nếu quay điểm O quay một điểm nào đó, chúng ta có hiệu ứng thú vị như sau. (Trong video là hiệu ứng số 2)

    Code:
    float L = 7; // Bước sóng
    float T = 1; // Chu kỳ
    float A = 3.5; // Biên độ
    float ro = 5000; // Điểm O rất xa LED Cube
    float To = 8; // Chu kỳ quay của O quanh trục
    float xo, yo;
    
    float t = get_time(); //Lấy thời gian
    xo = ro*Cos(2*PI*t/To) + 3.5; // Cho điểm O quay
    yo = ro*Sin(2*PI*t/To) + 3.5;
    for (byte y = 0; y < 8; y++) // Quét Y
    {
        for (byte x = 0; x < 8; x++) // Quét X
        {
            float r = Sqrt(Pow(x - xo, 2) + Pow(y - yo, 2)); // Lấy bán kính, Sqrt là căn bậc 2, Pow(x, y) là x mũ y
            byte z = (byte)(A * Cos(2*PI*t/T - 2*PI*r/L) + 3.5); // Lấy cao độ z, 3.5 là vị trí cân bằng
            Add(x, y, z); // Lưu vị trí LED sáng
        }
    }
    Hum nay mệt quá, mai viết típ nha mọi người.
    Computer Science major - Vietnamese-German University
    Sponsored by

  • #2
    cố gắn phát huy !

    Comment


    • #3
      Có thể các bạn sẽ thắc mắc, vì sao trong hiệu ứng trên lại dùng 7 và 3.5, trong khi LED Cube là 8x8x8. Bởi vì con số 8 là chỉ số LED 1 hàng, còn khoảng cách giữa chúng chỉ có 7 thoy, và bạn cũng có thể tự hỏi: "Hàng LED nào là hàng LED ngay chính giữa". Kết quả ko phải 3, cũng ko phải 4, nó là 3.5. Bạn nên lưu ý điều này trước khi lập trình hiệu ứng mới.
      Có thể code mình bị lai một chút của C# và mã .NET, nhưng cơ bản nó là thế. Các bạn thông cảm cho mình là mình lập trình mấy cái đó bằng C#.

      Hiệu ứng Pháo hoa (Firework)
      Lại thêm một hiệu ứng trên trang instructable, nhưng hiệu ứng này mình đã rút ra công thức chung cho các bạn (trên trang đó nó chỉ nói dùng vật lý giải ra =="). Hiệu ứng này đẹp bởi vì sự ngẫu nhiên của mỗi lần bắn pháo hoa.

      Thuật toán cơ bản:
      Chúng ta có các thông số cơ bản sau: G là gia tốc trọng lực, B là hệ số cản không khí (lực cản F/m = Bv, có B nhìn mới đẹp ^^), n là số phần tử pháo hoa, v_max là vận tốc tối đa của một phần tử.
      • Dùng Random xác định một điểm xo, yo bất kỳ để phóng pháo hoa. (ko quá sát cạnh)
      • Điểm đó bay từ từ đến một điểm zo thì bắt đầu nổ. (ko quá cao cũng ko quá thấp)
      • Tạo một mảng gồm n phần tử, mỗi phần tử đặc trưng cho một phần tử pháo hoa. Dùng Random để xác định vận tốc (từ 0 tới v_max), hướng (gồm 2 thành phần góc cực (vĩ độ) và góc phương vị (kinh độ) trong hệ tọa độ cầu).
      • Vòng lặp n phần tử, sự dụng cái công thức vật lý sau:
        • x = xo + (vx / B)*(1-e^(-B*t)) , vx là v theo phương x, trong đó e^ là e mũ, e là hằng số 2.72.
        • y = yo + (vy / B)*(1-e^(-B*t))
        • z = zo + (vz / B + G / B^2)*(1-e^(-B*t)) - G*t/B , các bạn có thể tính 1-e^(-B*t) trước sau đó mới lắp vào

      • Khi tất cả các phần tử đang rơi xuống đất hết (z < 0) thì reset hiệu ứng.


      Vs LED Cube 8x8x8, đẹp nhất là G = 18, B = 1.05, v_max = 15, n = 40 (MCU nào chậm thì giảm xuống một chút), 2 <= xo, yo <= 6, 5 <= zo <= 7, t là thời gian tính bằng giây, e = 2.718281828.

      Code C:
      Chú ý: hàm Random() của mình sẽ tạo ra một số float từ 0 cho đến 1, các bạn tự điều chỉnh vs các compiler khác nhau.
      Code:
      float G = 18, B = 1.05, v_max = 15;
      int n = 40;
      float vx[40], vy[40], vz[40]; //Ko đủ RAM thì giảm xuống
      byte xo, yo, zo;
      float t_ref; // Thời gian bắt đầu
      bool state = false; //Trạng thái, false là phóng pháo hoa, true là nổ, bool có thể là int1 trong một số compiler
      
      // Một vài chương trình con
      void RandomO() {
      xo = (byte)(Random() * 4 + 2); // Lấy giá trị từ 2 đến 6 yo = (byte)(Random() * 4 + 2); zo = (byte)(Random() * 2 + 5); // Lấy gia trị từ 5 đến 7
      } void RandomParticle() {
      for (int i = 0; i < n; i ++) {
      float v = Random() * v_max; float pol = Random() * PI / 2; // Góc cực float azi = Random() * 2 * PI; // Góc phương vị vx[i] = v * Cos(pol) * Cos(azi); // Tính toán vx, vy, vz vy[i] = v * Cos(pol) * Sin(azi); vz[i] = v * Sin(pol);
      }
      } //Chương trình chính RandomO(); while (true) {
      t = get_time() - t_ref; //Lấy thời gian if (!state) {
      float z = t * 15; Add(xo, yo, (byte)z); // Vẽ pháo hoa đang phóng if (z >= zo) {
      RandomParticle(); // Tạo ra các phần tử random t_ref = get_time(); // Đổi thời gian bắt đầu state = true; // Chuyển trạng thái sang trạng thái nổ
      }
      } else {
      bool reset = true; // Kiểm tra xem có reset hay ko float t_const = 1 - Exp(-B * t); // Hằng số thời gian là như nhau for (int i = 0; i < n; i++) {
      byte x = (byte)(xo + (vx[i] / B) * t_const); byte y = (byte)(yo + (vy[i] / B) * t_const); byte z = (byte)(zo + (vy[i] / B + G / (B * B)) * t_const - G * t / B); if (z >= 0) reset = false; // Chưa reset vì còn điểm trên 0 if ((x >= 0) && (x <= 7) && (y >= 0) && (y <= 7) && (z >= 0) && (z <= 7)) Add(x, y, z);
      } if (reset) { // Được reset
      RandomO(); // Tạo điểm phóng random t_ref = get_time(); // Đổi thời gian bắt đầu state = false; // Chuyển trạng thái sang trạng thái phóng
      }
      }
      }
      Hy vọng mọi người làm được hiệu ứng này ^^.
      Computer Science major - Vietnamese-German University
      Sponsored by

      Comment


      • #4
        Hiệu ứng giãn và co (Grow and Shrink)
        Hiệu ứng đơn giản và đẹp mắt trên trang instructable, nhưng mình đã cải biên một chút để cho nó hay hơn 1 chút. Thích hợp vs những MCU ko mạnh cho lắm.

        Thuật toán cơ bản:
        Chu kỳ cho mỗi lần giản và co là T (giãn T/2 và co T/2). Trong mỗi chu kỳ
        • Random một điểm xo, yo, zo bất kỳ
        • Tính toán các giá trị xmax, xmin, ymax, ymin và zmax, zmin
        • Lặp tất cả các cạnh trên hình lập phương và vẽ chúng (tất cả 12 cạnh)

        Vs cube 8x8x8, T là 1s.

        Code C:
        Code:
        float T = 1;
        byte xo, yo, zo;
        float t_ref;
        
        //Chương trình con
        void RandomO() { // Lấy điểm random
        xo = (byte)(Random() * 7); yo = (byte)(Random() * 7); zo = (byte)(Random() * 7);
        } //Chương trình chính RandomO(); t_ref = get_time(); while (true) {
        t = get_time() - t_ref; byte xmin, xmax, ymin, ymax, zmin, zmax; if (t > T/2) t = T - t; // Nếu đang co thì đảo ngược lại
        xmin = (byte)(2 * xo / T * t); xmax = (byte)(7 - 2 * (7 - xo) / T * t); ymin = (byte)(2 * yo / T * t); ymax = (byte)(7 - 2 * (7 - yo) / T * t); zmin = (byte)(2 * zo / T * t); zmax = (byte)(7 - 2 * (7 - zo) / T * t); for (int x = xmin; x <= xmax; x++) { // Add 4 cạnh theo chiều x
        Add((byte)x, ymin, zmin); Add((byte)x, ymax, zmin); Add((byte)x, ymin, zmax); Add((byte)x, ymax, zmax);
        } for (int y = ymin; y <= ymax; y++) { // Add 4 cạnh theo chiều y
        Add(xmin, (byte)y, zmin); Add(xmax, (byte)y, zmin); Add(xmin, (byte)y, zmax); Add(xmax, (byte)y, zmax);
        } for (int z = xmin; z <= zmax; z++) { // Add 4 cạnh theo chiều z
        Add(xmin, ymin, (byte)z); Add(xmax, ymin, (byte)z); Add(xmin, ymax, (byte)z); Add(xmax, ymax, (byte)z);
        } if (t > T) t_ref = get_time(); // Reset time lại khi xong 1 chu kỳ
        }
        Đơn giản đúng ko .
        Computer Science major - Vietnamese-German University
        Sponsored by

        Comment


        • #5
          rất hay. tiếp tục đi bác
          Nguyễn Huy Biên - 01649 802 694
          gmail :

          Comment


          • #6
            Hiệu ứng chữ (String)
            Hầu hết mọi người ai cũng muốn chạy chữ, vì như vậy, LED Cube mới có ý nghĩa đc 1 chút ^^.

            Bạn cần 1 font chữ 8x8, bạn có thể lên trên mạng để tìm cho mình 1 cái. Bạn nên lưu nó vào EEPROM của MCU nếu có, hay ROM ngoài nếu cần.
            Thuật toán cơ bản:
            Chữ chạy tịnh tiến (String fly)
            • Truy xuất một chữ trong ROM
            • Đặt vị trí ban đầu vs y = 0
            • Từ từ theo thời gian tăng y
            • Khi chữ bay đến hết thì thay chữ khác vào

            Chữ quay (String spin)
            • Truy xuất một chữ trong ROM, gọi một pixel trong chữ là (xo, zo)
            • Đặt góc ban đầu của chữ ở góc a (radian), gốc quay (x, y) là (cx, cy)
            • Dùng các công thức sau
              • x = (xo-ci-cx)*Cos(w*t - a) + cx, với w là tốc độ góc của chữ (đơn vị radian/s, nếu w = PI thì chữ sẽ quay 180 độ theo chiều ngược kim đồng hồ trong 1 s), ci là tâm của ảnh, ví dụ 3.5 là ảnh sẽ quay quanh trục chính giữa.
              • y = (xo-ci-cy)*Sin(w*t - a) + cy
              • z = zo

            • Add mọi điểm sáng vào và hiển thị


            Code C:
            Hàm truy xuất chữ của mình sẽ là bool[,] getLetter(byte ascii), trong đó bool[,] là mảng 2 chiều (xo, zo), ascii là mã ascii của chữ.
            Mình nghĩ string fly ai cũg có thể làm đc, mình sẽ làm string spin.

            Stringspin
            Code:
            byte[] string = { 0x4D, 0x49, 0x4E, 0x48 }; // Các chữ này các bạn tự thêm theo mã ascii, như trong trường hợp này là MINH
            float w = 2*PI; // 1 chữ quay 1 vòng hết 1 s
            float T = 1; //Thời gian xuất hiện của 1 chữ
            float a = 0; // Vị trí ban đầu góc 0 độ
            float cx = 3.5, cy = 3.5, ci = 3.5; // Quay quanh trục chính giữa
            
            float t_ref = get_time(); //Thời gian bắt đầu
            while (true) {
            for (int i = 0; i < 4; i++) { // 4 là độ dài của chuỗi
            bool[,] letter = getLetter(string[i]); while (true) {
            float t = get_time() - t_ref; if (t > T) { //Nếu chữ xuất hiện hơn T giây
            t_ref = get_time(); // Reset time
            break; // Qua chữ mới
            }
            for (int xo = 0; xo < 8; xo++) { // Quét mọi pixel trong chữ
            for (int zo = 0; zo < 8; zo++) {
            byte x = (byte)((xo-ci-cx)*Cos(w*t - a) + cx); //Tính vị trí của pixel đó sau khi transform byte y = (byte)((xo-ci-cy)*Sin(w*t - a) + cy); byte z = (byte)zo; if ((x >= 0) && (x <= 7) && (y >= 0) && (y <= 7) && (z >= 0) && (z <= 7))
            Add(x, y, z);
            }
            }
            }
            }
            }
            Nếu bạn thay cx, cy, ci, bạn sẽ có nhiều kiểm quay khác nữa.
            P/S: Hum nay mình mệt quá rồi, mai sẽ dành trọn bài cho nháy theo nhạc.
            Last edited by minh_cly; 15-06-2012, 15:27.
            Computer Science major - Vietnamese-German University
            Sponsored by

            Comment


            • #7
              Nháy theo nhạc
              Tất cả các người làm LED Cube đều mún cube mình phải có chức năng nháy theo nhạc đúng ko. Điều đó cần yêu cầu phần cứng của các bạn phải có hỗ trợ đưa nhạc vào, có thể từ microphone, có thể từ sound jack từ PC. Một phần cứng đơn giản có thể miêu tả như hình.
              Click image for larger version

Name:	opamp.jpg
Views:	1
Size:	30.8 KB
ID:	1362811
              Input của bạn sau khi đưa vào được khuếch đại nhờ một opamp và sau đó đưa vào ADC của vi xử lý. Bạn có thể điều chỉnh R1 và R2 để thay đổi độ khuếch đại của mạch.

              Việc lấy mẫu bằng ADC trong ví dụ này là hàm get_adc(), các bạn tự thay đổi theo từng lọai MCU.

              Các loại nháy theo nhạc:
              Nháy theo biên độ: Khi biên độ của âm càng lớn, hiệu ứng nháy càng mãnh liệt (hay ngược lại), đơn giản chỉ cần lấy kết quả ADC, càng cao (so vs mức cân bằng 1/2Vcc) thì càng nháy mạnh.
              Nháy theo tần số: Khi tần số âm càng cao thì càng nháy mạnh (hoặc ngược lại). Khi mỗi lần lấy mẫu ADC (so vs mức cân bằng), ta so sánh vs kết quả ngay trước đó, nếu trái dấu thì tăng 1 cho tần số.
              Âm phổ (Audio Spectrum): Phân tích âm thanh thành tập hợp của nhiều sóng harmonic, thành phần sóng nào càng nhiều thì thành phần đó nháy càng mạnh (hay ngược lại). Chúng ta phải dùng biến đổi Fourier nhanh (FFT) để lấy đc kết quả.

              Audio Spectrum:
              Thuật toán FFT khá nặng và khá rắc rối, MCU khuyên sử dụng là PIC 18F4550, 16F877 hoặc tương đương. Bạn cần có bộ nhận âm thanh được mô tả ở trên (hoặc tương đương). Thuật tóan này hiện nay mình mới thử trên máy tính thôi.

              Tần số lấy mẫu:
              Nếu gọi tần số lấy mẫu là F (tần số lấy ADC), chúng ta chỉ tính toán đc chính xác đến tần số F/2. Và LED Cube có 8 cột để hiện thị, cộng thêm một cột bị bỏ ban đầu, tức 9 cột tương ứng vs tần số F/2 ở trên. Vậy mỗi cột tương đương vs một tần số F/18.
              Ví dụ: Tần số F = 7200 Hz, tức cột 0 (cột này bỏ do ko có nghĩa) có tần số từ 0 - 400 Hz, cột 1 là 400 - 800 Hz, cột 2 là 800 - 1200Hz và tiếp tục đến cột 8 là 3200 - 3600 Hz, cột 9 đến cột 17 là sự đối xứng của cột 0 - 8 nên chúng ta bỏ (do tính đối xứng của bến đổi Fourier)
              Và để được 18 cột, chúng ta phải lấy được 18 mẫu, tức tần số tính FFT một lần là 400 Hz.
              Tuy nhiên, chúng ta có thể tăng số lượng mẫu để tăng độ chính xác, giảm số lần tính FFT, nhưng mỗi lần tính sẽ nặng hơn. Số lượng mẫu của mình thường là 24 đến 32. Các bạn có thể lấy trung bình của các lần tính FFT để ra một giá trị chậm hơn. Ví dụ, vs tốc độ lấy mẫu là 24, tần số lấy FFT là 300 Hz, và mình muốn hiển thị trên LED Cube chỉ 20Hz thôi, thì chúng ta sẽ lấy trung bình 15 giá trị FFT để hiển thị.

              Cơ số của FFT:
              FFT là thuật toán phát triển dựa trên DFT (biến đổi Fourier rời rạc), bằng cách chia nhỏ các mẫu thành từng phần và tính từng phần đó, sau đó ghép lại. Số phần đó dựa trên thừa số của thuật toán. Cơ số chọn sao cho tổng của chúng là nhỏ nhất.
              Ví dụ: lấy số mẫu là 18, chúng ta có các ước số: 2x9 = 18, 3x6 = 18, nhưng 2+9 = 11, 3+6 = 9, vậy chúng ta chọn thừa số 3 và 6.
              Còn số mẫu là 24, chúng ta có 2x12 = 24, 3x8 = 24, 4x6 = 24, nhưng 2+12 = 14, 3+8 = 11, 4+6 = 10, vậy chúng ta chọn thừa số 4 và 6.
              Số mẫu chúng ta gọi là N, số hàng là L và số cột là M = N / L.

              Xây dựng hàm FFT:
              FFT là một thuật toán tính toán trên số phức, vì vậy chúng ta cần 2 biến global là res_r chứa kết quả phần thực, res_i chứa kết quả phần ảo.
              Code:
              int N = 24; //Số mẫu
              int L = 4, M = 6;
              float res_i, res_r;
              float[6] dft1_r, dft1_i; //Kết quả DFT có số phần tử là M = 6
              float[24] fft_r, fft_i; // Kết quả FFT có số phần tử N = 24
              // Một số hàm tính toán số phức cần thiết
              void mul_comp(float r1, float i1, float r2, float i2) { // Hàm nhân phức
              res_r = r1 * r2 - i1 * i2; res_i = r1 * i2 + r2 * i1;
              } void exp_comp(float angle) { // Đổi từ dạng e mũ phức sang số phức, bạn nên xác lập hàm này là separate, nếu inline nó sẽ rất nặng
              res_r = Cos(angle); res_i = Sin(angle);
              } // Biến đổi Fourier rời rạc, bạn có thể dùng trực tiếp DFT nếu số mẫu của bạn ít void DFT(float[6] in_r, float[6] in_i, byte num) { //num là số mẫu để DFT
              for (int k = 0; k < num; k++) {
              dft_r[k] = 0; dft_i[k] = 0;
              for (int n = 0; n < num; n++) {
              exp_comp(-2 * PI * k * n / num); // Kết quả exp_comp lưu trong res_r và res_i
              mul_comp(in_r, in_i, res_r, res_i); // Lấy tích rồi lưu lại vào res_r và res_i dft_r[k] += res_r; // Cộng lại dft_i[k] += res_i;
              }
              } return outcom;
              } // Biến đổi Fourier nhanh void FFT(float[24] in_r, float[24] in_i) {
              float[24] F_r, F_i; // Mảng phụ for (int l = 0; l < L; l++) {
              float[6] comp_r, comp_i; // Biến đưa vào tính DFT lần 1 for (int m = 0; m < M; m++) {
              comp_r[m] = in_r[l + m * L]; // Tách mảng comp_i[m] = in_i[l + m * L];
              } DFT(comp_r, comp_i, M); // Tính DFT vs M = 6 mẫu for (int q = 0; q < M; q++) {
              exp_comp(-2 * PI * l * q / N); mul_comp(dft_r[q], dft_i[q], res_r, res_i);
              F_r[l + q * L] = res_r; F_i[l + q * L] = res_i;
              }
              } for (int q = 0; q < M; q++) {
              float[6] comp_r, comp_i; // Biến đưa vào tính DFT lần 2 for (int l = 0; l < L; l++) {
              comp_r[l] = F_r[l + q * L]; // Tách mảng comp_i[l] = F_i[l + q * L];
              } DFT(comp_r, comp_i, 4); //Tính DFT lần 2 vs L = 4 mẫu for (int p = 0; p < L; p++) {
              F_r[p + q * L] = dft_r[p]; F_i[p + q * L] = dft_i[p];
              }
              }
              for (int p = 0; p < L; p++) { // Biến đổi mảng
              for (int q = 0; q < M; q++) {
              fft_r[M * p + q] = F_r[p + q * L]; fft_i[M * p + q] = F_i[p + q * L];
              }
              }
              }
              Thế là xong phần FFT, chìu mình viết típ nha. Xin lỗi mọi người.
              Last edited by minh_cly; 16-06-2012, 14:23.
              Computer Science major - Vietnamese-German University
              Sponsored by

              Comment


              • #8
                Em viết típ nha mọi người:
                Về phần FFT, nếu compiler bạn nào có struct thì xài tốt hơn nhá. Bạn tạo 1 struct Complex có 2 số real và imag để lưu giá trị số phức trong đó.

                Sau đâu là code Audio Spectrum, Audio Spectrum một lần tính FFT sẽ lấy 24 mẫu, và lấy từ cột 1 đến 8 (bỏ đi cột 0 và các cột cao hơn, tần số lấy mẫu tùy các bạn, vs mình là F = 7200 lần / s, tương ứng 1 cột là 300 Mhz).

                Có 1 số cách sử lý số liệu FFT như sau:
                • Tuyến tính: các số liệu tỉ lệ với nhau như cũ.
                • Logarit: các số liệu tỉ lệ với nhau theo 10 mũ, ví dụ, nếu cột 1 có giá trị gấp 10 lần cột 2 thì hiển thị cột 1 hơn cột 2 1 đơn vị, nhưng nếu gấp 100 thì 2 đơn vị, 1000 thì 3 đơn vị.


                Thuật toán cơ bản:
                • Tạo ra một mảng 64 phần tử (mặt xy), mỗi phần tử sẽ lưu cao độ của trục z
                • Trong mỗi lần hiển thị
                  • Lùi mảng 1 đơn vị theo trục y (hàng cuối cùng thì bỏ)
                  • Lấy FFT trung bình
                  • Lưu vào hàng đầu các giá trị z mới
                  • Lặp từng cột, add các led trong khoảng giữa 0 đến z


                Code C:
                Code:
                float t_ref, t_ref_show;
                int n = 24; //số mẫu
                float sample[24]; //mẫu
                int n_count = 0; // Đếm số mẫu
                int f_show = 15; // Tần số hiển thị
                int f = 7200; // Tần số lấy mẫu
                byte z[8, 8]; // Mảng 2 chiều x, y
                byte fft_total[8]; // FFT tổng cộng của nhiều lần tính FFT vì tần số tính FFT > tần số hiển thị
                
                //Chương trình chính
                t_ref = get_time();
                t_ref_show = get_time();
                while (true) {
                float t = get_time() - t_ref; float t_show = get_time() - t_ref_show; if (t_show > 1.0 / f_show) {
                for (int y = 6; y >= 0; y--) { //Lùi 1 theo trục y, bỏ hàng cuối
                for (int x = 0; x < 8; x++) {
                z[x, y+1] = z[x, y];
                }
                } // Phần tính toán dữ liệu fft_total ở đây, các bạn tự thực nghiệm để ra cho mình biểu thức thích hợp để hiển thị đẹp nhất, vì phần cứng nhận âm thanh của các bạn sẽ khác nhau, nên biểu thức của mỗi người cũng ko giống nhau for (int i = 0; i < 8; i++) {
                z[i, 0] = (byte)( F(fft_total[i]) ); // Các bạn thay biểu thức vào hàm F(fft_total[i]) fft_total[i] = 0;
                } for (int x = 0; x < 8; x++) {
                for (int y = 0; y < 8; y++) {
                for (int z_t = 0; z_t <= z[x, y]; z++) {
                Add(x, y, z_t); // Add các điểm giữa 0 và z
                }
                }
                } t_ref_show = get_time(); //Reset timer
                } if (t > 1.0 / f) {
                sample[n_count] = get_adc(); // Tối đa là 1, tối thiểu là 0 n_count++; if (n_count == 24) { // Nếu đủ 24 mẫu
                FFT(sample); for (int i = 0; i < 8; i++) { //cộng thêm 8 cột đầu tiên
                fft_total[i] += Sqrt(fft_i[i+1] * fft_i[i+1] + fft_r[i+1] * fft_r[i+1]); Lấy module của kết quả
                } n_count = 0; t_ref = get_time();
                }
                }
                }
                Computer Science major - Vietnamese-German University
                Sponsored by

                Comment


                • #9
                  Trời.. Hay quá.. Cảm ơn anh nhiều ! Những bài viết của anh cho em một nguồn cảm hứng mới với cái led cube này !!

                  P/S: Trời tập 2.. lớp 11.. Chúc bạn thành công !!
                  Last edited by UenX; 17-06-2012, 18:12.

                  Comment


                  • #10
                    em hỏi tí bác minh_cly ơi, led bác hàn là catot hay anot chung thế , bác điều khiển các lớp led bằng cái gì(ic dịch hay...),hướng dẫn em làm phần cứng đi

                    Comment


                    • #11
                      Nguyên văn bởi hungtrinh36 Xem bài viết
                      em hỏi tí bác minh_cly ơi, led bác hàn là catot hay anot chung thế , bác điều khiển các lớp led bằng cái gì(ic dịch hay...),hướng dẫn em làm phần cứng đi
                      Phần cứng cũng có nhìu trên mạng, việc hàn chung anode hay cathode ko co gì là wan trọng (trừ khi bạn có sẵn transistor ở nhà =)) thì điều đó sẽ tiết kiệm cho bạn 15k tiền mua trans), điều khiển 8 lớp trực tiếp từ MCU thông qua transistor, 64 cột thông qua IC ghi dịch 74HC595.
                      Last edited by minh_cly; 17-06-2012, 20:05.
                      Computer Science major - Vietnamese-German University
                      Sponsored by

                      Comment


                      • #12
                        Nguyên văn bởi UenX Xem bài viết
                        Trời.. Hay quá.. Cảm ơn anh nhiều ! Những bài viết của anh cho em một nguồn cảm hứng mới với cái led cube này !!

                        P/S: Trời tập 2.. lớp 11.. Chúc bạn thành công !!
                        Dạ năm nay e lên lớp 12 ròy ạh =)), chúc anh làm thành công.
                        Computer Science major - Vietnamese-German University
                        Sponsored by

                        Comment


                        • #13
                          Hiệu ứng đa giác 3D
                          Hiệu ứng này do e tự chế
                          . Hiệu ứng này cơ bản được mô tả như sau:

                          Thuật toán cơ bản:
                          • Một mảng lưu vị trí các điểm tuyệt đối (position) trong ko gian (chưa qua bộ biến hình - transform), và các đường thẳng nối giữa chúng.
                          • Tính toán bộ biến hình theo thời gian
                          • Sau bộ biến hình, các điểm đã biến đổi được lưu vào mảng khác (transformed)
                          • Nối những điểm đó bằng thuật toán Bresenham 3D
                          • Hiển thị


                          Thuật toán Bresenham 3D:
                          Thuật toán này phát triển từ thuật toán Bresenham 2D, cho phép nối 2 điểm trong ko gian lại với nhau. Các bạn chú ý là nối 2 điểm ko nhất thiết là nguyên nhá, là thập phân cũng được.
                          Trong code sẽ sử dụng hàm swap(a, b) tức hoán đổi a, b lại với nhau (tức tmp = a; a = b; b = tmp).
                          Code:
                          void Bresenham(float x1, float y1, float z1, float x2, float y2, float z2) {
                          bool swap_xy = false, swap_xz = false; // Các biến xem việc hoán đổi có xảy ra ko if (Abs(y2 - y1) > Abs(x2 - x1)) { // Abs(a) có nghĩa là lấy giá trị tuyệt đối của a, nếu delta_y > delta_x thì hoán đổi xy
                          swap_xy = true; swap(x1, y1); swap(x2, y2);
                          } if (Abs(z2 - z1) > Abs(x2 - x1)) { // Tương tự vs xz
                          swap_xz = true; swap(x1, z1); swap(x2, z2);
                          } if (x1 > x2) { // Nếu đường thẳng đi ngược thì chiều x thì hoán đổi 2 điểm
                          swap(x1, x2); swap(y1, y2); swap(z1, z2);
                          } float deltax = x2 - x1; // Ko cần lấy giá trị tuyệt đối nữa, do đã hoán đổi ở trên float deltay = Abs(y2 - y1); // Độ chênh lệch y float deltaz = Abs(z2 - z1); // Độ chênh lệch z float drift_xy = deltax / 2; // Sai số tích lũy theo xy (các bạn tham khảo thêm thuật toán Bresenham 2D trên wiki) float drift_xz = deltax / 2; // Tương tự vs xz int stepy = 1; if (y1 > y2) stepy = -1; // Bước tiến của y, nếu y đi ngược thì bước tiến là âm 1 int stepz = 1; if (z1 > z2) stepz = -1; // Tương tự float y = y1; // Biến y tạm thời float z = z1; // Biến z tạm thời int cx, cy, cz; // Biến nguyên x, y, z for (float x = x1; x <= x2; x++) // Lắp x theo phương x {
                          cx = (int)x; cy = (int)y; cz = (int)z; // Biến đỏi thành nguyên if (cx >= x0) {
                          if ((cx < 8) && (cy >= 0) && (cy < 8) && (cz >= 0) && (cz < 8)) { // Xem điểm đó có nằm ở trong cube?
                          if (swap_xz) swap(cx, cz); // Nếu bị swap xz thì đảo lại (cái nào bị đảo sau thì bây giờ phải đảo trước) if (swap_xy) swap(cx, cy); // Tương tự vs xy Add(cx, cy, cz); // Add điểm sáng đó
                          }
                          drift_xy -= deltay; // Tích lũy sai số drift_xz -= deltaz;
                          if (drift_xy < 0) { // Nếu sai số âm thì tăng y lên 1, đồng thời reset lại sai số
                          y += stepy; drift_xy += deltax;
                          }
                          if (drift_xz < 0) { // Tương tự vs xz
                          z += stepz; drift_xz += deltax;
                          }
                          }
                          }
                          }
                          Đó là thuật toán Bresenham 2D, và sau đâu và code của một số hiệu ứng 3D:
                          Tetrahedron (tứ diện)
                          Một tứ diện đều sẽ quay quanh trục thẳng đứng (Trong clip ko phải là tứ diện đều).
                          Code:
                          float x[4], y[4], z[4]; // Mảng chứa các điểm tuyệt đối
                          float xT[4], yT[4], zT[4]; // Mảng chứa các điểm đã biến đổi
                          int numvertex = 4; // Số điểm
                          int period = 3; // Chu kỳ quay là 3s
                          
                          // Khai báo các điểm
                          void defvertex() {
                          x[0] = 0.5; y[0] = 1.768; z[0] = 1; x[1] = 6.5; y[1] = 1.768; z[1] = 1; x[2] = 3.5; y[2] = 6.964; z[2] = 1; x[3] = 3.5; y[3] = 3.5; z[3] = 5.899;
                          } // Nối điểm void connect(int ver_1, int ver_2) {
                          Bresenham(xT[ver_1], yT[ver_1], zT[ver_1], xT[ver_2], yT[ver_2], zT[ver_2]);
                          } //Chương trình chính defvertex(); float t; // Thời gian while (true) {
                          t = get_time(); for (int i = 0; i < numvertex; i++) { // Biến đổi
                          xT[i] = ((x[i] - 3.5) * Cos(2*PI*t/period) - (y[i] - 3.5) * Sin(2*PI*t/period) + 3.5); yT[i] = ((x[i] - 3.5) * Sin(2*PI*t/period) + (y[i] - 3.5) * Cos(2*PI*t/period) + 3.5); zT[i] = vertex[i].z;
                          } connect(0, 1); connect(0, 2); connect(0, 3); // Nối 6 đoạn thẳng giữa 4 điểm connect(1, 2); connect(1, 3); connect(2, 3);
                          }
                          The cube (hộp lập phương)
                          Một lập phương sẽ xoay theo mọi phương
                          Code:
                          float x[8], y[8], z[8]; // Mảng chứa các điểm tuyệt đối
                          float xT[8], yT[8], zT[8]; // Mảng chứa các điểm đã biến đổi
                          int numvertex = 8; // Số điểm
                          int period = 7; // Chu kỳ là 7s
                          int sub = 1; // Khoảng cách cạnh lập phương đến led cube, nếu 1 tức lập phương có chiều dài là 7 - 2*1 = 5
                          float maxangxy = Math.PI / 2; // Tối đa góc quay theo chiều xy và xz
                          float maxangxz = Math.PI / 2;
                          
                          // Khai báo các điểm
                          void defvertex() {
                          x[0] = sub; y[0] = sub; z[0] = sub; x[1] = sub; y[1] = 7 - sub; z[1] = sub; x[2] = 7 - sub; y[2] = 7 - sub; z[2] = sub; x[3] = 7 - sub; y[3] = sub; z[3] = sub; x[4] = sub; y[4] = sub; z[4] = 7 - sub; x[5] = sub; y[5] = 7 - sub; z[5] = 7 -sub; x[6] = 7 - sub; y[6] = 7 - sub; z[6] = 7 -sub; x[7] = 7 - sub; y[7] = sub; z[7] = 7 -sub;
                          } // Nối điểm void connect(int ver_1, int ver_2) {
                          Bresenham(xT[ver_1], yT[ver_1], zT[ver_1], xT[ver_2], yT[ver_2], zT[ver_2]);
                          } //Chương trình chính defvertex(); float t; // Thời gian while (true) {
                          t = get_time(); float posxy = maxangxy * Sin(2*PI*t/period); // Vị trí quay của trục xy và xz float posxz = maxangxz * Cos(2*PI*t/period); for (int i = 0; i < numvertex; i++) { // Biến đổi
                          xT[i] = ((x[i] - 3.5) * Cos(posxy) - (y[i] - 3.5) * Sin(posxy) + 3.5); yT[i] = ((x[i] - 3.5) * Sin(posxy) + (y[i] - 3.5) * Cos(posxy) + 3.5); zT[i] = ((xT[i] - 3.5) * Sin(posxz) + (z[i] - 3.5) * Cos(posxz) + 3.5); // Phải cho z biến đổi trước để ko làm thay đổi xT xT[i] = ((xT[i] - 3.5) * Cos(posxz) - (z[i] - 3.5) * Sin(posxz) + 3.5);
                          } connect(0, 1); connect(1, 2); connect(2, 3); connect(3, 0); // Nối 12 đoạn thẳng giữa 8 điểm connect(4, 5); connect(5, 6); connect(6, 7); connect(7, 4); connect(0, 4); connect(1, 5); connect(2, 6); connect(3, 7);
                          }
                          Computer Science major - Vietnamese-German University
                          Sponsored by

                          Comment


                          • #14
                            Một số lưu ý khi lập trình trên MCU (not for Computer)
                            1. Hãy giới hạn tốc độ hiển thị
                            Mỗi khung hình hiển thị được gọi là một frame, và con người bình thường chúng ta chỉ có thể nhìn thấy 24-30 frame/s, vì vậy hãy giới hạn tốc độ này ít nhất có thể để có tài nguyên thực hiện những tác vụ khác.
                            2. Hãy tính toán frame tiếp theo ngay sau khi chuyển frame
                            Một số bạn thường có 1 thói quen thế này:
                            Code:
                            int fps = 30; // Tốc độ hiển thị
                            float t_ref = get_time();
                            while (true) { // Bạn nên dùng ngắt timer để giới hạn tốc độ hiển thị
                            float t = get_time() - t_ref; if (t > 1.0 / fps) { // Giới hạn tốc độ hiển thị
                            // Tính toán hiệu ứng // Thay frame t_ref = get_time();
                            }
                            }
                            Hãy thay nó bằng thế này:
                            Code:
                            t_ref = get_time();
                            // Thay frame
                            // Tính toán hiệu ứng
                            t_ref và lệnh thay frame cần phải sớm càng tốt để hiển thị đúng thời gian bởi vì chúng ta ko thể bik chính xác đc thuật toán của bạn nặng bao nhiu.
                            Một số bạn có thể nói là thuật toán của "tôi" tính sau sau 10ms sau đó hiển thị thì cũng đều thoy, tôi chỉ cần thay t_ref lên đầu, còn lệnh thay frame để cuối vẫn đc. Nhưng các bạn có chắc chắn rằng thuật toán của bạn có tính toán đúng 10ms, vì trong thuật toán của bạn có rất nhìu lệnh if, lệnh for, và những lệnh này có thể làm thay đổi thời gian tính toán rất nhiều.
                            3. Nếu thuật toán có SIN hoặc COS, hãy tạo một mảng chứa những giá trị có sẵn
                            Việc tính Sin và Cos trên MCU tốn rất nhìu tài nguyên, nhưng chúng ta ko cần sự chính xác cho lắm, bởi vì LED Cube 8x8x8 thì cũng có 8 đơn vị mỗi chìu. Chúng ta nên vào excel, lập ra 1 mảng tên là Sin, Cos để lưu những giá trị tính sẵn. Đừng lo về RAM, vì nếu bạn khai báo đó là const (hằng số), thì dữ liệu sẽ đc lưu trong program memory.
                            Kích cỡ của mảng tùy thuộc vào thuật toán và tốc độ hiển thị, có thể đc sử dụng cho nhìu thuật toán khác nhau. Hãy phân tích tham số trong hàm SIN xem có bao nhiu phần tử rời rạc có thể, hay bạn có thể làm gần đúng nó để lấy giá trị gần đúng.
                            Ví dụ trong hiệu ứng sóng, SIN có tham số là 2*PI*t/T, với tốc độ hiển thị là 30, và T có giá trị là 2 chẳng hạn, thì 1 chu kỳ mấy 60 frame tương ứng vs các giá trị khác nhau của t/T. Vì vậy hãy tạo 1 mảng 60 để lưu giá trị của Sin và Cos.
                            Một ví dụ khác, thuật toán sử dụng mảng Sin 100 phần tử, thuật toán 2 chỉ sử dụng 60. 2 thuật toán có thể share cho nhau một mảng Sin 100 để tiết kiệm tài nguyên. Nếu thuật toán 1 ko yêu cầu độ chính xác cao, 2 thuật toán có thể sử dụng chung mảng 60 hay thấp hơn cũng được.
                            Computer Science major - Vietnamese-German University
                            Sponsored by

                            Comment


                            • #15
                              Rất hay ,mình dang nghien cứu cái Audio spectrum analyzer tren con pic mà dặt biẹt là 16f nhưng thử nhièu giải thuật FFT 8 bit rồi nhưng dèu chẳng ra làm sao cả.
                              còn vè 18f thì thật sự cũng dã thử rồi thành công khi tính DFT thôi , còn FFT thì cũng bị như 16f mặc cho N = 1024 thì cũng chả ra làm sao cả . Gặp bài này hay quá phải thử bíen dổi sang cho 18f thôi . Cám ơn nhièu

                              Comment

                              Về tác giả

                              Collapse

                              minh_cly Tìm hiểu thêm về minh_cly

                              Bài viết mới nhất

                              Collapse

                              Đang tải...
                              X