EST. 2026 — บันทึกงานเซิร์ฟเวอร์จริง SYSADMIN · SECURITY · AI · DIRECTADMIN เล่มที่ 01
THE SYSADMIN ALMANAC
หน้าหลัก / Linux / เล่นกล้อง AI reCamera + gimbal: cross-compile RISC-V + CAN + Node-RED
Linux

เล่นกล้อง AI reCamera + gimbal: cross-compile RISC-V + CAN + Node-RED

โดย Ton-Or ·21 ก.ค. 2026·อ่าน 10 นาที
เล่นกล้อง AI reCamera + gimbal: cross-compile RISC-V + CAN + Node-RED

กล้อง AI ตัวเล็กบนแกน brushless สองแกน สั่งให้หันได้ด้วยการเขียนมุมลงไฟล์ข้อความบรรทัดเดียว เบื้องหลังคือ RISC-V ที่ต้อง cross-compile เอง กับ CAN bus ที่คุยกับมอเตอร์ถึงระดับ 0.01 องศา

พอดีได้กล่องชุด reCamera Gimbal มาเล่น เป็นกล้อง AI ตัวเล็กที่วางอยู่บนแกน brushless สองแกน (yaw กับ pitch) ตัวประมวลผลเป็นชิป RISC-V รันโมเดลตรวจจับวัตถุอยู่ในตัว แล้วสั่งมอเตอร์ให้หันตามวัตถุที่เจอ ของเล่นแนวนี้ปกติจะปิดเฟิร์มแวร์หมด แต่ตัวนี้เปิดให้เขียนโปรแกรมคุมมอเตอร์เองผ่าน CAN bus ได้ ผมเลยรื้อดูตั้งแต่การ cross-compile ไปจนถึงการสั่งมอเตอร์ทีละองศา เลยเอามาเรียงเป็นขั้นตอนให้อ่านกันครับ

ชื่อ user, path บางส่วน และค่า PID ในบทความเป็นตัวอย่างที่ปรับให้อ่านง่าย ส่วน IP 192.168.42.1 เป็นค่า default ของ device จริง คำสั่งและโครงสร้าง CAN frame ทั้งหมดคือของจริงจากชุดพัฒนา

ของจริงในกล่อง: RISC-V + กล้อง AI + มอเตอร์ brushless

ตัวประมวลผลเป็น SoC ตระกูล SG2002 ข้างในมี core RISC-V C906 สองตัว (ตัวใหญ่ 1GHz อีกตัว 700MHz) มี NPU แรงราว 1 Tops ที่ Int8 พอวิ่งโมเดลตรวจจับวัตถุบนตัวกล้องได้เลยโดยไม่ต้องพึ่งเครื่องนอก RAM 256MB รันลินุกซ์ (reCamera OS) กล้องเป็น OV5647 5MP ป้อนไฟ 12V เข้าทาง XT30

กล้องบนแกน brushless สองแกน แกนล่างหมุนซ้ายขวา แกนข้างก้มเงย สายจากมอเตอร์รวมที่บอร์ดในฐาน
reCamera บนแกน brushless สองแกน แกนหนึ่งหมุนซ้ายขวา อีกแกนก้มเงย และสายจากมอเตอร์ทั้งสองไปรวมที่บอร์ดในฐาน

จุดสำคัญที่ทำให้สั่งมอเตอร์ได้คือต้องเปลี่ยน base board เป็นรุ่น B4_CAN เพื่อเปิดช่อง CAN ออกมา มอเตอร์เป็น brushless สองรุ่น MS3008 กับ MS3506 ต่อกันบน CAN bus เดียว ตัวละ ID ตั้งไว้ 01 กับ 02 แต่ละตัวมี encoder แม่เหล็ก 15-bit อยู่ในตัว คุยกันที่ CAN 1Mbps โหมดที่ผมใช้ตลอดคือ position loop 2KHz

2× C906RISC-V (1GHz + 700MHz)
1 TopsNPU @ Int8
256 MBRAM รันลินุกซ์
15-bitmagnetic encoder ต่อมอเตอร์
1 MbpsCAN bus (B4_CAN board)
0.01°ความละเอียดสั่งมุม

ภาพรวมของทั้งระบบเป็นแบบนี้ครับ เขียนโค้ดบนเครื่อง PC แล้วยิงคำสั่งข้ามไปให้ RISC-V บนกล้องเป็นคนสั่งมอเตอร์จริง

เส้นทางจาก PC ถึงมอเตอร์PC (x86)cross-compile→scp / ssh192.168.42.1→RISC-V บนกล้องอ่าน Angle.txt→CAN 1Mbpsframe 0xA4→มอเตอร์ brushlessหมุนตามมุม

cross-compile ภาษา C ให้รันบน RISC-V musl

กล้องเป็น RISC-V ไม่ใช่ x86 เพราะฉะนั้น gcc บนเครื่องเราคอมไพล์แล้วเอาไปรันไม่ได้ ต้องใช้ cross-compiler ที่ออกไบนารีเป็น riscv64 และที่สำคัญคือต้องเป็นสาย musl ไม่ใช่ glibc เพราะ userland บนกล้องเป็น musl ถ้าหยิบ toolchain ผิดสาย ไบนารีจะคอมไพล์ผ่านแต่พอเอาไปรันบนกล้องจะเด้ง error ทันที

ผมใช้ host-tools ของ sophgo โคลนมาไว้บนเครื่องลินุกซ์ตัวไหนก็ได้ (ผมใช้ Ubuntu):

git clone https://github.com/sophgo/host-tools.git

ตัว gcc ที่ต้องการอยู่ใน host-tools/gcc/riscv64-linux-musl-x86_64/bin เอา path เต็มของโฟลเดอร์นั้น (ดูด้วย pwd) มาผูกเข้า PATH ในไฟล์ .bashrc ตัวเอง:

# ต่อท้าย ~/.bashrc — เปลี่ยน path ให้ตรงกับที่โคลนไว้
export PATH=$PATH:/home/user/host-tools/gcc/riscv64-linux-musl-x86_64/bin
source ~/.bashrc

พอ PATH มาแล้วก็คอมไพล์ตรงๆ ได้เลย ชื่อ compiler เต็มๆ คือ riscv64-unknown-linux-musl-gcc:

riscv64-unknown-linux-musl-gcc can_send.c -o can_send
ผิดสาย toolchain = คอมไพล์ผ่านแต่รันไม่ได้

ต้องเป็น riscv64 + musl เท่านั้น ถ้าเผลอใช้ gcc ปกติของเครื่อง หรือ toolchain riscv สาย glibc ไบนารีจะสร้างได้แต่พอ scp ขึ้นกล้องแล้วสั่งรันจะขึ้น not found / segfault แบบงงๆ เช็คด้วย file can_send ต้องเห็นคำว่า RISC-V กับ musl ก่อนเอาขึ้น device

ส่งไฟล์ขึ้น device แล้วสั่งมอเตอร์ตัวแรก

ได้ไบนารีแล้วก็ scp ขึ้นกล้องผ่าน IP default 192.168.42.1 แล้ว ssh เข้าไปรัน ตัวโปรแกรม can_send จะคอยอ่านไฟล์ชื่อ Angle.txt วนไปเรื่อยๆ ที่เหลือเราแค่เขียนมุมลงไฟล์นั้น มอเตอร์ก็หัน

  1. ส่งไบนารีขึ้นกล้อง: scp ./can_send [email protected]:~/
  2. ล็อกอินเข้ากล้อง: ssh [email protected]
  3. สร้างไฟล์คุมมุมแล้วรันโปรแกรม: touch Angle.txt แล้ว ./can_send
  4. เปิดอีกหน้าต่างหนึ่ง เขียนมุมลงไฟล์: echo "1_90_90+2_90_90" >> Angle.txt
scp ./can_send [email protected]:~/
ssh [email protected]

# บนกล้อง
touch Angle.txt
./can_send

# อีกหน้าต่าง — สั่งมอเตอร์ ID1 ไป 90° และ ID2 ไป 90° เพดานความเร็ว 90 dps
echo "1_90_90+2_90_90" >> Angle.txt

รูปแบบในไฟล์คือ motorID_มุม_ความเร็ว ถ้าจะสั่งพร้อมกันสองตัวก็คั่นด้วยเครื่องหมาย + เป็น 1_90_90+2_90_90 ตัวเลขความเร็วมีหน่วยเป็น dps (องศาต่อวินาที) เป็นเพดานว่าจะให้หันเร็วสุดแค่ไหน ถ้าอยากหยุดโปรแกรมก็เขียนตัว q ลงไฟล์ หรือกด q ที่คีย์บอร์ด มันจะสั่ง can0 ลงให้เองก่อนออก

เบื้องหลัง: CAN frame ที่คุมมอเตอร์

ทีนี้มาดูของจริงว่าเวลาเขียน 1_90_90 ลงไฟล์แล้วเกิดอะไรขึ้น ตัวโปรแกรมใช้ SocketCAN ของลินุกซ์ตรงๆ เปิด socket แบบ PF_CAN ผูกกับ can0 ก่อนใช้งานต้องตั้ง bitrate ให้ตรงกับมอเตอร์คือ 1Mbps ไม่งั้นคุยกันไม่รู้เรื่อง ส่วนนี้โปรแกรมสั่งให้เองผ่าน system():

// ตั้งความเร็ว CAN ให้ตรงกับมอเตอร์ แล้วยกอินเทอร์เฟซขึ้น
system("sudo ip link set can0 type can bitrate 1000000");
system("sudo ip link set can0 up");

หัวใจอยู่ที่การประกอบ frame มอเตอร์แต่ละตัวมี CAN ID เป็น 0x140 + motor_id (ตัว ID1 ก็ 0x141) คำสั่งหมุนไปมุมสัมบูรณ์ใช้ไบต์แรกเป็น 0xA4 (position loop แบบมีเพดานความเร็ว) มุมที่ส่งคูณ 100 เพราะหน่วยของมอเตอร์คือ 0.01 องศาต่อ 1 LSB นี่คือเหตุผลว่าทำไมสั่งได้ละเอียดถึงเศษองศา:

CAN ID = 0x140 + motor_id (ID1 = 0x141) DLC = 8data[0]0xA4data[1]0x00data[2]speed lodata[3]speed hidata[4]angle b0data[5]angle b1data[6]angle b2data[7]angle b3โหมดคุมมุมไม่ใช้เพดานความเร็ว (dps)มุม x 100 (หน่วย 0.01 องศา)ตัวอย่างจากบทความA400780050460000ความเร็ว 0x0078 = 120 dps มุม 0x00004650 = 18000 หรือ +180.00 องศา (little-endian)
คำสั่งหมุนไปมุมสัมบูรณ์ใช้ไบต์แรกเป็น 0xA4 ตามด้วยไบต์ว่าง เพดานความเร็วสองไบต์ และมุมคูณ 100 อีกสี่ไบต์
// angle = องศาที่ต้องการ, maxSpeed = เพดานความเร็ว dps
int32_t angleControl = angle * 100;   // 0.01 องศา/LSB
struct can_frame rotate_frame;
rotate_frame.can_id  = 0x140 + motor_id;
rotate_frame.can_dlc = 8;
rotate_frame.data[0] = 0xA4;                          // position loop 2
rotate_frame.data[1] = 0x00;
rotate_frame.data[2] = (uint8_t)(maxSpeed & 0xFF);   // เพดานความเร็ว low byte
rotate_frame.data[3] = (uint8_t)(maxSpeed >> 8);     // เพดานความเร็ว high byte
rotate_frame.data[4] = (uint8_t)(angleControl);      // มุม byte 0
rotate_frame.data[5] = (uint8_t)(angleControl >> 8); // มุม byte 1
rotate_frame.data[6] = (uint8_t)(angleControl >> 16);// มุม byte 2
rotate_frame.data[7] = (uint8_t)(angleControl >> 24);// มุม byte 3

ไบต์แรกนี่แหละที่กำหนดโหมดควบคุมทั้งหมด มอเตอร์ตระกูลนี้มีชุดคำสั่งพอสมควร ที่ใช้บ่อยมีประมาณนี้ ผมดึงมาจากไฟล์ตัวอย่างคำสั่ง CAN ของมอเตอร์โดยตรง:

0x88  เปิดมอเตอร์ (run)          0x81  หยุดมอเตอร์ (stop)
0x80  ปิดมอเตอร์ (off)           0xA2  speed loop (คุมความเร็ว)
0xA4  position loop 2 (คุมมุม + เพดานความเร็ว)
0x90  อ่านค่า encoder            0x9C  อ่านสถานะ 2 (อุณหภูมิ/กระแส/ความเร็ว/encoder)
0x19  ตั้งตำแหน่งปัจจุบันเป็นศูนย์ ลง ROM
0x30  อ่าน PID   0x31  เขียน PID ลง RAM   0x32  เขียน PID ลง ROM

# ตัวอย่าง: A4 00 78 00 50 46 00 00 = position loop2 ความเร็ว 120dps มุม +180°
0xA2 speed loop

คุมความเร็ว ไม่รู้จบที่มุมไหน

สั่งให้หมุนไปเรื่อยๆ ด้วยความเร็วที่กำหนด ใช้ตอนหาศูนย์ (ไล่มอเตอร์ไปชนสุด) ไม่เหมาะกับการเล็งมุม

0xA4 position loop 2

ไปมุมสัมบูรณ์ + จำกัดความเร็ว

บอกมุมเป้าหมายกับเพดานความเร็ว มอเตอร์คุม PID เองให้ไปหยุดที่มุมนั้น นี่คือโหมดหลักที่ใช้เล็งกล้อง

Emergency stop คือ frame เดียว

เวลาทดสอบครั้งแรกให้เผื่อทางหนีไว้เสมอ ถ้ามอเตอร์วิ่งเพี้ยน frame หยุดคือ ID 0x140+id ไบต์แรก 0x81 (stop) หรือ 0x80 (off ปลดแรงบิดเลย) ควรผูกปุ่มนี้ไว้ก่อนลองสั่งมุมแรงๆ ไม่งั้นแกนอาจหมุนชนจนสายขาดได้

Angle.txt: อินเทอร์เฟซที่ทำให้สั่งง่าย

จุดที่ผมชอบในดีไซน์ของชุดนี้คือการใช้ไฟล์ข้อความเป็นตัวกลาง ตัว can_send ไม่ได้เปิด socket ให้ใครมายิงคำสั่ง แต่วน loop อ่าน Angle.txt แล้ว ล้างไฟล์ทิ้งทุกรอบ พอมีใครเขียนบรรทัดใหม่เข้าไป รอบถัดไปมันก็หยิบไปแปลงเป็น CAN frame แล้วล้างต่อ กลไกนี้ทำให้เราจะสั่งจากอะไรก็ได้ที่เขียนไฟล์เป็น ไม่ว่าจะ shell, Node-RED หรือสคริปต์ Python

char buffer[256] = {0};
fgets(buffer, sizeof(buffer), file);
int motor_id, angle, maxSpeed, motor_id2, angle2, maxSpeed2;
int n = sscanf(buffer, "%d_%d_%d+%d_%d_%d",
               &motor_id, &angle, &maxSpeed,
               &motor_id2, &angle2, &maxSpeed2);
// n == 3 สั่งตัวเดียว, n == 6 สั่งพร้อมกันสองตัว (หน่วง 30ms ระหว่างสองเฟรม)

ข้อดีคือ debug ง่ายมาก อยากรู้ว่าสั่งอะไรก็แค่ echo เข้าไฟล์แล้วดูมอเตอร์ ข้อเสียคือมันคือ fire-and-forget ไม่มี ack กลับมาว่าไปถึงมุมจริงหรือยัง เพราะฉะนั้นถ้าจะทำ closed-loop จริงจังต้องอ่านสถานะกลับมาเองอีกทาง ซึ่งเป็นเรื่องของหัวข้อถัดไป

อ่านสถานะกลับมา: encoder 15-bit, มุม, อุณหภูมิ, กระแส

การสั่งอย่างเดียวไม่พอ ผมอยากรู้ด้วยว่ามอเตอร์อยู่มุมไหน ร้อนแค่ไหน กินกระแสเท่าไหร่ ตัวชุดพัฒนามีโปรแกรม can_receive แยกมาให้ ยิงคำสั่งอ่าน 0x90 (ค่า encoder) หรือ 0x9C (สถานะ 2) แล้วแกะไบต์ที่ตอบกลับมา รันแบบวนอ่านตามความถี่ก็ได้:

./can_receive --help              # ดูวิธีใช้ทั้งหมด
./can_receive --state 1           # อ่านอุณหภูมิ กระแส ความเร็ว encoder
./can_receive --fre 10 --state 0  # อ่านมุมซ้ำ 10 ครั้ง/วินาที (กด q เพื่อออก)

การแกะ frame ตอบกลับก็ตรงไปตรงมา อุณหภูมิอยู่ไบต์ที่ 1, กระแสแรงบิดสองไบต์ถัดมา, ความเร็วเป็น dps, และ encoder สองไบต์ท้าย ตัว encoder เป็น 15-bit โค้ดตัวอย่างแมปช่วง 0–16383 เป็นมุม 0–180 องศา:

int8_t   temperature = recv_frame.data[1];
int16_t  power   = (recv_frame.data[3] << 8) | recv_frame.data[2]; // กระแสแรงบิด
int16_t  speed   = (recv_frame.data[5] << 8) | recv_frame.data[4]; // dps
uint16_t encoder = (recv_frame.data[7] << 8) | recv_frame.data[6]; // 15-bit
float    angle   = encoder / 16383.0 * 180.0;                      // แปลงเป็นองศา
กระแสแรงบิดใช้เป็นเซนเซอร์แรงกดได้

ค่าดิบของ power เอามาสเกล power / 2048 * 33 จะได้เป็นหน่วยกระแสโดยประมาณ ค่านี้ไม่ได้ไว้ดูเล่นอย่างเดียว มันคือตัวบอกว่ามอเตอร์กำลังออกแรงมากผิดปกติไหม ซึ่งเดี๋ยวจะเอาไปใช้หาศูนย์ในหัวข้อถัดไป

หาศูนย์มอเตอร์ด้วยการชนสุด (auto calibration)

ปัญหาของ gimbal ประกอบเองคือมอเตอร์ไม่รู้ว่า "ตรงกลาง" อยู่ตรงไหน ไม่มี limit switch ให้ด้วย ตัวชุดพัฒนาแก้ด้วยลูกเล่นที่ผมว่าฉลาด คือใช้กระแสแรงบิดเป็นตัวจับขอบ ในโปรแกรม Gimbal_init มันสั่งมอเตอร์ด้วย speed loop ให้ค่อยๆ หมุนไปทางเดียว แล้วอ่านสถานะ 0x9C วนไปเรื่อยๆ พอแกนหมุนไปชนสุดทางกลไก มอเตอร์จะออกแรงดันสุดตัว กระแสแรงบิดจะพุ่งขึ้น พอเกินเกณฑ์ (ราว 5) ก็รู้ได้ว่าถึงขอบแล้ว จากนั้นเขียนตำแหน่งนั้นเป็นศูนย์ลง ROM ด้วยคำสั่ง 0x19 แล้วค่อยสั่งกลับมาตรงกลาง

power = power / 2048.0 * 33;      // แปลงกระแสแรงบิดเป็นหน่วยจริงโดยประมาณ
if (power >= 5.00) {              // แรงบิดพุ่ง = แกนชนสุดแล้ว
    send_frame(s, &zero_frame);  // 0x19 ตั้งตำแหน่งนี้เป็นศูนย์ ลง ROM
    if (motor_id == 1)  send_frame(s, &center_frame_1); // 0xA4 กลับมา 180°
    else if (motor_id == 2) send_frame(s, &center_frame_2); // กลับมา 90°
    break;                       // จบการหาศูนย์
}
ลำดับการหาศูนย์speed loopค่อยๆ หมุน→อ่าน 0x9Cเฝ้ากระแส→แรงบิดพุ่งชนสุด power≥5→0x19 ตั้งศูนย์เขียน ROM→0xA4 กลับกลางพร้อมใช้งาน

ส่วนตัวผมชอบวิธีนี้เพราะไม่ต้องเพิ่มฮาร์ดแวร์อะไรเลย ใช้เซนเซอร์ที่มีอยู่แล้วในมอเตอร์ทำงานสองหน้าที่ แต่มีข้อควรระวังคือเกณฑ์กระแสต้องตั้งให้พอดี ถ้าตั้งต่ำไปยังไม่ทันชนก็หยุด ถ้าสูงไปมอเตอร์จะดันชนแรงเกินจนเคสพลาสติกล้าได้ ตรงนี้ปรับตามน้ำหนักกล้องและความฝืดของแกนที่เราประกอบเองครับ

Node-RED dashboard + PID + auto-track

ถ้าไม่อยากสั่งจาก shell ตลอด ชุดนี้มี flow ของ Node-RED มาให้ import แล้วได้หน้า dashboard ครบ มีทั้งภาพจากกล้องสด, ตั้งค่า confidence/IoU ของโมเดล, ปุ่มบังคับมือ, auto-track ให้กล้องหันตามวัตถุ, ปุ่มลัด sleep / standby / calibration / Emergency Stop และหน้าปรับ PID แยกต่อแกน เพราะแต่ละคนประกอบเองน้ำหนักไม่เท่ากัน ค่าจูนเลยต้องปรับเอง

PID แยกเป็นสองชั้นต่อแกน คือ angle loop (นอก) กับ speed loop (ใน) ค่าเริ่มต้นที่ flow แนะนำมาเป็นแบบนี้:

# แกน Yaw
angle:  Kp = 50   Ki = 0    Kd = 0
speed:  Kp = 200  Ki = 20   Kd = 0     (แนะนำ ~260 / 20 / 0)

# แกน Pitch
angle:  Kp = 50   Ki = 0    Kd = 0
speed:  Kp = 100  Ki = 20   Kd = 0

เวลากด save flow จะเขียนค่าลงเป็นไฟล์คอนฟิกในบ้าน user ของกล้อง รูปแบบแต่ละบรรทัดคือ motorID axis Kp Ki Kd โดย 0A คือ angle loop และ 0B คือ speed loop:

1 0A 50 0 0      # motor1 (yaw) angle PID
1 0B 260 20 0    # motor1 (yaw) speed PID
2 0A 50 0 0      # motor2 (pitch) angle PID
2 0B 260 20 0    # motor2 (pitch) speed PID

ส่วน auto-track จริงๆ ก็คือวนอ่านกล่องวัตถุจากโมเดล เอาระยะห่างจากจุดกลางภาพมาผ่าน gain แล้วเขียนมุมเป้าหมายลง Angle.txt ตัวเดิม เพราะ can_send คอยอ่านไฟล์นั้นอยู่แล้ว เราเลยทำ controller เป็นภาษาอะไรก็ได้ ผมลองเขียนเป็น Python สั้นๆ ให้เห็นภาพว่ามันต่อกับไฟล์เดียวกันยังไง (ค่า gain กับ deadzone ปรับเอาเองตามหน้างาน):

# auto_track.py — host-side controller เขียนมุมลง Angle.txt ให้ can_send อ่านต่อ
FRAME_W, FRAME_H = 640, 480          # ขนาดภาพจากโมเดล
KP_YAW, KP_PITCH = 0.03, 0.03        # แปลง error พิกเซล -> องศา
DEADZONE = 20                        # นิ่งได้ ไม่ต้องขยับ
yaw, pitch = 90, 90                  # มุมกลางเริ่มต้น

def track(box_cx, box_cy):
    global yaw, pitch
    ex = box_cx - FRAME_W / 2        # error แนวนอน
    ey = box_cy - FRAME_H / 2        # error แนวตั้ง
    if abs(ex) > DEADZONE:
        yaw = clamp(yaw - ex * KP_YAW, 0, 180)
    if abs(ey) > DEADZONE:
        pitch = clamp(pitch + ey * KP_PITCH, 0, 180)
    with open("Angle.txt", "a") as f:
        f.write(f"1_{int(yaw)}_90+2_{int(pitch)}_90\n")

def clamp(v, lo, hi):
    return max(lo, min(hi, v))

จะเห็นว่าสุดท้ายมันก็มาลงที่บรรทัดเดียวกันกับที่เราพิมพ์มือตอนแรก คือ 1_มุม_ความเร็ว+2_มุม_ความเร็ว ต่างกันแค่ว่าคราวนี้มีโมเดล AI เป็นคนเลือกมุมให้แทนเรา

พิมพ์เคส 3D จากไฟล์ STP

ตัวโครง gimbal ทั้งชุดมีไฟล์ STP มาให้พิมพ์เอง ตั้งแต่แขนยึดกล้อง, กล่องฐาน, ฝาครอบเซนเซอร์, ไปจนถึงเคสสำหรับบอร์ด CAN แต่ละชิ้นแยกไฟล์ โหลดไปเปิดในโปรแกรม slice แล้วสั่งพิมพ์ วัสดุเดิมของตัวสำเร็จเป็นไนลอน PA แต่พิมพ์ด้วย PETG หรือ PLA+ ก็พอไหวถ้าไม่ได้ใช้งานหนัก เสร็จแล้วประกอบมอเตอร์สองตัวเข้าแกน ตั้ง ID เป็น 01 กับ 02 ด้วยเครื่องมือปรับมอเตอร์ก่อน แล้วค่อยรัน Gimbal_init หาศูนย์อีกที เป็นอันครบวงจรตั้งแต่โค้ดยันโครง

สรุปทั้งเส้นทาง

cross-compile C สาย riscv64 musl -> scp ขึ้นกล้องที่ 192.168.42.1 -> เขียนมุมลง Angle.txt -> โปรแกรมแปลงเป็น CAN frame 0xA4 ยิงเข้ามอเตอร์ -> อ่าน encoder 15-bit กลับมาปิดลูป -> ครอบด้วย Node-RED + PID + auto-track ให้ AI เล็งเอง ทุกชิ้นต่อกันผ่านไฟล์ข้อความไฟล์เดียว

ที่เล่ามาเป็นแกนหลักพอให้เอากล้องขึ้นแกนแล้วสั่งหันได้จริง ส่วนที่ผมยังเล่นไม่จบคือการจูน PID ให้ตามวัตถุเร็วๆ แล้วไม่แกว่ง กับการอัปเดตเฟิร์มแวร์มอเตอร์ ใครจะเอาโครงนี้ไปต่อ ปรับ gain, เกณฑ์กระแสหาศูนย์ และค่า PID ตามฮาร์ดแวร์ที่ประกอบเองได้ตามสะดวกครับ ตรงไหนผมอธิบายตกหล่นหรือพิมพ์ผิด เดี๋ยวกลับมาแก้เพิ่มให้ภายหลังครับ

เคยเจองานแบบนี้ไหม? ติดตรงไหน?

มาแลกวิธีกันต่อในบอร์ด LinuxThai

→ คุยต่อในบอร์ด

ความคิดเห็น 0

ยังไม่มีความคิดเห็น — เป็นคนแรกเลย