Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ISDN2601 pre-lab — ESP32-S3 三自由度機械臂控制器

ESP32-S3 為運動控制器,3 個 MG90S 驅動三段活動臂(肩 / 肘 / 腕), 1 個 MG90S 驅動抓夾,每段活動臂長 40 cm,控制器與執行器分處 兩條獨立的 RS485 總線

                       ┌───────────────────────────────────────┐
                       │            ESP32-S3                   │
                       │                                       │
   ┌────────────┐      │  Bus A (UART1)      Bus B (UART2)     │
   │ 上位機/PLC │◄────►│  從站 115200        主站 250000       │
   │  (PC)      │  A/B │                                       │
   └────────────┘      │  MCPWM ──► GPIO4/5/6/7                │
        USB-RS485      │         (本地 PWM 後端時直接驅動舵機)│
                       └───────────────────────────────────────┘
                                       │ A/B
        ┌──────────────┬───────────────┼──────────────┐
    ┌───┴───┐      ┌───┴───┐      ┌───┴───┐      ┌───┴───┐
    │Node 1 │      │Node 2 │      │Node 3 │      │Node 4 │
    │肩 J1  │      │肘 J2  │      │腕 J3  │      │抓夾   │
    │MG90S  │      │MG90S  │      │MG90S  │      │MG90S  │
    └───────┘      └───────┘      └───────┘      └───────┘
     0x01           0x02           0x03           0x04

1. 為什麼是兩條 RS485 總線

Bus A(控制器總線) Bus B(執行器總線)
角色 ESP32-S3 作從站 ESP32-S3 作主站
對端 上位機 / PLC / HMI 4 個執行器節點
波特率 115200 250000
內容 「做什麼」:關節角、位姿、抓夾、使能、回零 「怎麼執行」:4 路脈寬 + 節點反饋
週期 事件驅動(上位機輪詢) 100 Hz 廣播同步寫 + 輪詢讀

分開的理由:

  1. 實時性隔離——執行器側要 100 Hz 同步下發,不能因為上位機的長幀或 偶爾的通信阻塞而抖動。
  2. 波特率差異——上位機距離遠、速率低;執行器距離近、要求高吞吐。
  3. 終端與隔離——兩條總線可以各自加 120 Ω 終端電阻與隔離收發器, 一條短路不會拖垮另一條。
  4. 帶寬核算——Bus B 每週期廣播 1 幀(18 B)+ 輪詢 1 個節點(請求 8 B
    • 應答 ~12 B),100 Hz 下約 7.6 kbps,250 kbps 上有充足裕量。

2. ⚠️ 首要問題:40 cm 臂長 + MG90S 的力矩核算

按目前的機械參數,這套臂在水平伸出時無法舉起自身重量。 這是結構性問題, 不是軟體能解決的,請務必先看這一節。

2.1 計算

水平全伸(最惡劣工況)時肩關節的靜態力矩:

τ₁ = g · [ m₁·L₁/2 + m₂·(L₁+L₂/2) + m₃·(L₁+L₂+L₃/2) + m_p·(L₁+L₂+L₃) ]

當 L₁ = L₂ = L₃ = L、三桿等質量 m 時可化簡為:

τ₁ = g · L · (4.5·m + 3·m_p)

取默認參數 m = 0.1 kgm_p = 0.1 kg這兩個數值是估算,務必實測修正):

單段臂長 L 肩部所需力矩 肘部 腕部 MG90S 連續可用
400 mm(本項目) 30.0 kgf·cm 16.0 6.0 0.59
300 mm 22.5 kgf·cm 12.0 4.5 0.59
200 mm 15.0 kgf·cm 8.0 3.0 0.59
100 mm 7.5 kgf·cm 4.0 1.5 0.59

MG90S 堵轉力矩約 1.8 kgf·cm(4.8 V),連續工作按 1/3 降額僅 0.59 kgf·cm。 也就是說 40 cm 臂在水平伸出時超載約 50 倍

固件上電時會自動打印這份核算(arm_log_torque_report()),改完 ARM_LINK*_MASS_KG 後看串口日誌即可,不必自己手算:

W (1234) arm: ===== 靜力矩核算 (估算值,務必實測桿件後修正) =====
W (1234) arm: MG90S: 堵轉 1.80 kgf·cm,連續可用按 1/3 計 = 0.59 kgf·cm
W (1234) arm: 全伸水平 (0,0,0)    肩 30.0 / 肘 16.0 / 腕  6.0 kgf·cm  <<< 超出 MG90S 能力
W (1234) arm: 待機姿態            肩  6.7 / 肘 13.7 / 腕  6.0 kgf·cm  <<< 超出 MG90S 能力
W (1234) arm: 豎直停放            肩  0.0 / 肘  0.0 / 腕  0.0 kgf·cm

注意「豎直停放」(90°, 0°, 0°) 才是零重力矩姿態。若寫成 (90°, 0°, −90°), 腕部連桿是水平的、末端甩出 400 mm,肩部仍有 6 kgf·cm—— 這是很容易踩的坑,app_config.h 裡已註明。

2.2 怎麼辦(三選一或組合)

方案 A — 保留 40 cm,換大力矩舵機(推薦) 肩/肘換 20–35 kgf·cm 級數字舵機(如 DS3218、RDS3225、35 kg 級總線舵機), MG90S 只留作腕部與抓夾。注意 20 kg 級舵機尺寸與 MG90S 不同,需重新設計法蘭。

方案 B — 保留 MG90S,大幅減重並加重力平衡

  • 桿件改用碳纖管 + 薄壁 3D 打印件,做到每桿 ≤ 20 g;
  • 肩部加拉簧或配重抵消靜態重力矩——這對靜態力矩極其有效,是性價比最高的做法;
  • 運動範圍限制在接近豎直的區域(重力矩 ∝ cos θ)。

方案 C — 縮短臂長 臂長縮到 10–12 cm 且每桿 ≤ 20 g 時,MG90S 才勉強夠用(L=100 mm、 m=15 g、m_p=20 g 時肩部約 0.51 kgf·cm,接近 0.59 的連續可用值)。

無論選哪個方案,都請先實測桿件質量與質心位置,填回 main/include/app_config.hARM_LINK*_MASS_KG / ARM_PAYLOAD_MASS_KG, 讓過載告警(FAULT_OVERLOAD)有正確的判據。


3. 硬件連接

完整硬件說明書(BOM / 接線 / 電源 / 標定 / 安全)見 docs/HARDWARE.md

3.1 引腳分配(ESP32-S3-DevKitC-1)

功能 引腳 說明
舵機 J1(肩) GPIO4 MCPWM,50 Hz
舵機 J2(肘) GPIO5 MCPWM
舵機 J3(腕) GPIO6 MCPWM
舵機 抓夾 GPIO7 MCPWM
Bus A TXD GPIO17 UART1 → 收發器 DI
Bus A RXD GPIO18 UART1 ← 收發器 RO
Bus A DE GPIO8 收發器 DE 與 ~RE 並接
Bus B TXD GPIO12 UART2 → 收發器 DI
Bus B RXD GPIO13 UART2 ← 收發器 RO
Bus B DE GPIO9 收發器 DE 與 ~RE 並接
舵機電源使能 可選 SERVO_PWR_EN_GPIO,默認 −1(不使用)

避開了 strapping 腳(GPIO0/3/45/46)、USB-JTAG(GPIO19/20)以及 Octal PSRAM/Flash 佔用的 GPIO26–37。改腳位請直接編輯 main/include/app_config.h

3.2 舵機接線

ESP32-S3 GPIOx ──► 舵機信號線(橙/白)
外部 5 V 穩壓 ──► 舵機 VCC(紅)   ← 千萬不要用開發板的 3V3/5V 引腳供電
共地 GND      ──► 舵機 GND(棕)   ← ESP32 與外部電源必須共地

4 個 MG90S 同時堵轉可達 2 A 以上,必須用獨立 5 V 電源(建議 ≥ 3 A), 並在舵機側就近並聯 100–470 µF 電解電容。

3.3 RS485 總線接線

GPIO(TXD) ──► DI
GPIO(RXD) ◄── RO
GPIO(DE)  ──► DE ─┬─ 並接
             ~RE ─┘
VCC        ──► 3V3(收發器邏輯電平)
A / B      ──► 總線
  • 總線兩端各接一個 120 Ω 終端電阻(中間節點不接);
  • 加失效保護偏置:A 上拉 4.7 kΩ 到 3V3、B 下拉 4.7 kΩ 到 GND, 避免總線空閒時 RX 收到亂碼;
  • 兩條總線各自成鏈,不要交叉或形成星型分支。

4. 目錄結構

ISDN2601_code/
├── CMakeLists.txt              ESP-IDF 專案根
├── sdkconfig.defaults          目標晶片 / FreeRTOS 節拍等
├── docs/
│   └── HARDWARE.md             硬件說明書(BOM/接線/電源/標定/安全)
├── main/
│   ├── CMakeLists.txt
│   ├── app_main.c              入口:初始化兩條總線、建控制任務
│   ├── arm_controller.c/h      協議解析 + 狀態機 + 運動控制 + 安全監控
│   ├── kinematics.c/h          3R 正逆運動學、脈寬映射、靜力矩估算
│   ├── motion.c/h              逐軸梯形速度規劃
│   ├── protocol.c/h            幀格式、CRC16、流式解碼器
│   ├── rs485.c/h               半雙工總線驅動(DE 軟件控制)
│   ├── servo.c/h               MG90S MCPWM 驅動
│   └── include/app_config.h    全部硬件引腳與機械參數
├── tests/
│   ├── Makefile                原生測試(不需要 ESP-IDF)
│   └── test_native.c
└── tools/
    └── host_console.py         上位機調試工具

protocol.c / kinematics.c / motion.c 刻意不引用任何 ESP-IDF 頭文件, 因此可以直接在 PC 上編譯測試——這是整個項目能在沒有硬件的情況下被驗證的關鍵。


5. 構建與燒錄

需要 ESP-IDF v5.0+(建議 v5.2 / v5.3):

. $HOME/esp/esp-idf/export.sh          # 或 source 你的 IDF 路徑
cd ISDN2601_code
idf.py set-target esp32s3
idf.py build
idf.py -p /dev/cu.usbmodem1101 flash monitor

本工作區當前未安裝 ESP-IDF,因此固件未經實機編譯。已做的驗證見第 8 節。

切換執行器後端(默認本地 PWM):

# 舵機由 Bus B 上的智能節點驅動,而不是直接接 ESP32-S3 的 MCPWM
idf.py build -DAPP_ACT_BACKEND=1

或直接在 main/CMakeLists.txt 裡加:

target_compile_definitions(${COMPONENT_LIB} PRIVATE APP_ACT_BACKEND=1)

6. 通信協議

6.1 幀格式(兩條總線共用)

┌──────┬──────┬─────┬─────┬────┬─────┬──────────┬───────┐
│ SOF0 │ SOF1 │ DST │ SRC │ FC │ LEN │ PAYLOAD  │ CRC16 │
│ 0xAA │ 0x55 │ 1B  │ 1B  │ 1B │ 1B  │ 0..64 B  │ 2B LE │
└──────┴──────┴─────┴─────┴────┴─────┴──────────┴───────┘
         CRC16 計算範圍(DST → PAYLOAD 末尾)
  • CRC16 = CRC-16/MODBUS(多項式 0xA001,初值 0xFFFF,低字節在前), 標準向量 "123456789"0x4B37
  • 站號:0x00 廣播、0x010x04 執行器節點、0x10 機械臂控制器、0x40 上位機;
  • 解碼器是逐字節狀態機,幀被拆包、前面有垃圾數據或出現假 SOF 都能自動重同步。

6.2 站號與功能碼

功能碼 名稱 請求載荷 應答載荷
0x01 PING err u8, uptime u32, seq u16, tick_hz u16
0x02 READ_STATUS state u8, fault u16, warn u16, grip_mode u8, grip u16, vin u16, uptime u32, seq u16, nodes u16
0x03 WRITE_JOINTS mask u16, j1/j2/j3 i16, grip u16, move_time u16 ACK/NACK
0x04 READ_JOINTS j1/j2/j3 i16, load1..3 i16, grip u16, nodes u16
0x05 WRITE_POSE x i16, y i16, phi i16, grip u16, move_time u16 ACK/NACK
0x06 READ_POSE x i16, y i16, phi i16, grip u16
0x10 SET_ENABLE enable u8 ACK/NACK
0x11 HOME ACK/NACK
0x12 SET_GRIP cmd u16, mode u8(可選) ACK/NACK
0x13 SET_LIMITS axis u8, vmax u16, amax u16 ACK/NACK
0x20 READ_FAULT fault u16, warn u16, state u8
0x21 CLEAR_FAULT ACK/NACK
0x7E / 0x7F ACK / NACK err u8, uptime u32

線路單位:

單位 編碼
笛卡爾座標 0.1 mm i16
關節角 / 俯仰角 0.01 deg i16
負載估算 0.1 %(堵轉力矩佔比) i16
抓夾開合 0..1000 ‰ u16
時間 ms u16
限速 0.1 單位/s、0.1 單位/s² u16

所有多字節字段均為小端

6.3 錯誤碼(ACK/NACK 載荷首字節)

含義
0 OK
1 逆解失敗:目標不可達
2 超出關節限位
3 尚未回零
4 處於故障閉鎖態,需 CLEAR_FAULT
5 舵機未使能,需 SET_ENABLE 1
6 載荷長度/格式錯誤
7 不支持的功能碼

6.4 故障位

fault閉鎖類(會停止運動,需 CLEAR_FAULT 解除): BUS_A_TIMEOUT(0x0004)ESTOP(0x0080)

warn告警類(不停止運動,僅上報): IK_UNREACHABLE(0x0001)JOINT_LIMIT(0x0002)BUS_B_TIMEOUT(0x0008)NODE_CRC(0x0010)OVERLOAD(0x0020)

OVERLOAD 只告警不閉鎖是有意的:按當前的機械參數它幾乎常亮, 若做成閉鎖機械臂將完全無法動作。

6.5 Bus B 的執行器總線行為

  • 每 10 ms 廣播一次 WRITE_JOINTSdst=0x00,mask=0x0F), 4 個節點在同一時刻動作——這比逐節點點對點下發更實時、帶寬更省;
  • 每 10 ms 輪詢一個節點READ_JOINTS,4 個節點輪轉 → 每節點 25 Hz 反饋;
  • 節點無應答時置 BUS_B_TIMEOUT 告警並標記離線。

7. 上位機調試工具

pip install pyserial

python3 tools/host_console.py /dev/cu.usbserial-A50285BI ping
python3 tools/host_console.py PORT status
python3 tools/host_console.py PORT enable 1
python3 tools/host_console.py PORT joints 120 -80 -40 1000 1500
python3 tools/host_console.py PORT pose 500 600 0 500 2000
python3 tools/host_console.py PORT grip 800 mirror
python3 tools/host_console.py PORT home
python3 tools/host_console.py PORT watch     # 20 Hz 連續監視
python3 tools/host_console.py PORT demo      # 搬運演示

若 USB-RS485 適配器會自環收到自己發出的字節,加 --echo


8. 標定步驟

MG90S 是無位置反饋的模擬舵機,不標定直接上電會直接撞限位。請按順序做:

  1. 先不裝臂,只接舵機,ARM_HOME_* 設成中位對應的關節角;
  2. enable 1 使能,用 joints 逐軸小幅度點動,觀察實際轉向;
  3. 某軸方向反了 → 把 app_config.h 裡對應的 SERVO_Jx_US_PER_DEG 取負 (改符號即可,不必改機械或接線);
  4. 機械裝配的零位誤差 → 調 SERVO_Jx_TRIM_DEG
  5. 行程不匹配(脈寬 500–2500 µs 對應的角度不是 180°)→ 調 SERVO_MIN_US / SERVO_MAX_US
  6. 實測桿件質量與質心,填回 ARM_LINK*_MASS_KG / ARM_PAYLOAD_MASS_KG
  7. 抓夾:用 grip 找出完全閉合與完全張開的脈寬,填入 GRIP_CLOSED_US / GRIP_OPEN_US

回零:MG90S 沒有絕對位置反饋,HOME 命令只是「規劃到待機姿態」, 不是真正意義上的回零。需要真正的回零請加限位開關或絕對編碼器。


9. 測試與已完成的驗證

9.1 原生單元測試(不需要硬件 / ESP-IDF)

cd tests && make

會在 PC 上編譯 protocol.c / kinematics.c / motion.c 並運行 94 項斷言:

覆蓋範圍 驗證內容
CRC16 標準向量 0x4B37
組幀/解幀 往返一致、單位翻轉被 CRC 攔下、垃圾前綴後能重同步
正運動學 (0,0,0)→1200mm、(90,0,0)→1200mm 高等解析解
逆運動學 4 組姿態 × 2 個肘部分支,FK(IK(pose)) == pose 閉合且落在限位內
軟限位 越限判定與鉗位
靜力矩 水平全伸手算值 30/16/6 kgf·cm;並把超載固化成一條斷言
速度規劃 90° 長行程 = 梯形剖面 1.75 s、峰值速度 60°/s;5° 短行程 = 三角剖面 0.29 s;正反兩個方向都無過衝
脈寬映射 中位 1500 µs、±85° → 2444/556 µs、溢出鉗位、方向反轉
抓夾映射 三種模式的脈寬與雙顎鏡像張角

9.2 固件端做了哪些檢查

本機沒有 ESP-IDF 工具鏈,因此用一套 ESP-IDF 頭文件樁對全部 7 個源文件做了 gcc + clang 雙編譯器的 -fsyntax-only -Wall -Wextra 語法與類型檢查, 兩種執行器後端(LOCAL / RS485)均零警告通過。

9.3 跨語言協議互操作

Python 上位機生成的 WRITE_POSE 幀,餵給 C 固件解碼器解析出的字段 (x=500.0mm y=600.0mm phi=0.00deg grip=500 ms=2000)與 Python 端完全一致, 證明兩側的字節序、單位換算、CRC 實現對齊。

上述三項都不能替代實機聯調。上電前務必先做第 2 節的力矩核算與第 8 節的標定。


10. 設計要點

為什麼用 MCPWM 而不是 LEDC 驅動舵機 MCPWM 時基可設成 1 MHz,比較值直接就是脈寬微秒數,分辨率 1 µs(≈0.09°@MG90S); LEDC 在 50 Hz 下受限於定時器位寬,脈寬分辨率只有 2–4 µs,且週期有 0.1%–0.4% 誤差。

為什麼 DE 用普通 GPIO 軟件控制 便於在發送前後插入收發轉換延時(RS485_DE_SETTLE_US),也便於換用不同收發器。 發送流程:DE=1 → uart_write_bytes → uart_wait_tx_done → 延時 → DE=0, 既避免總線衝突,也避免自己的發送被自己回收。

運動規劃為什麼這樣寫 逐軸梯形規劃,用「步末速度必須落在剎車曲線上」的隱式解限速 (v_new = (√(b² + 8ad − 4bv) − b) / 2b = a·dt)。若改用「剎停距離 ≥ 剩餘距離 就減速」這種二值判據,離散時間下會先施加整步加速、下一步才發現要減速, 峰值速度衝過理論切換點,短行程可提前約 10%–13% 到位。配合梯形積分 (v_old+v_new)/2·dt,長短行程的到位時間都與連續時間解析解吻合。

抓夾的三種映射模式

  • DIRECT:開合指令 0–1000 ‰ 線性映射到脈寬;
  • MIRROR(默認):單舵機經連桿帶動雙顎對稱開合,左顎 +δ / 右顎 −δ 鏡像;
  • LEVEL映射補償——把腕部俯仰角 φ 映射進抓夾伺服,使顎口在世界座標中的 指向保持 GRIP_LEVEL_REF_DEG 不變。若只用一個舵機同時負責開合與調平, 兩者會互相耦合,需要按行程比例分配。

安全設計

  • 控制器總線看門狗(默認 1500 ms):超時凍結在當前設定值並閉鎖,需 CLEAR_FAULT
  • 關節軟限位:笛卡爾指令逆解後會再校驗限位,超限直接 NACK,不下發;
  • 失能時把所有 PWM 腳強制拉低(mcpwm_generator_set_force_level(gen, 0, true)), 舵機失去脈衝即不出力;
  • 故障閉鎖期只放行只讀命令與 CLEAR_FAULT
  • 週期性主動遙測默認關閉CTRL_UNSOLICITED_TELEMETRY=0)——半雙工從站 主動上報會與上位機的請求撞車,僅點對點時才建議開啟。

11. 已知限制

  1. 未經實機驗證——本機沒有 ESP-IDF,固件只做了語法/類型檢查與數學驗證, 尚未在 ESP32-S3 上跑過。
  2. 無真實位置反饋——MG90S 是開環模擬舵機,READ_JOINTS 回傳的是規劃設定值, 不是實際角度。本地 PWM 後端下的「負載」是靜力矩估算值,不是實測電流。
  3. 無回零傳感器——HOME 只是規劃到待機姿態。
  4. 無基座回轉軸——3 個活動關節構成豎直平面內的 3R 臂(x, y, φ), 工作空間局限在一個平面內。如需水平回轉需增加第 5 個舵機與第 4 個關節軸。
  5. 關節行程受舵機限制——MG90S 只有約 180°,單關節行程被限制在 170° 以內 (見 ARM_Jx_MIN/MAX_DEG)。
  6. Bus B 節點固件未提供——控制器側的主站邏輯(廣播同步寫 + 輪詢讀)已實現, 但執行器節點本身的固件需要另行開發(可用 ESP32-C3/C6 或 STM32, 協議見第 6 節)。默認使用本地 PWM 後端,不需要節點即可運行。
  7. 無電壓/電流採集——vin_mV 恆為 0。如需電池供電下的低壓保護, 需加 ADC 分壓採樣。

About

ESP32-S3 三自由度机械臂控制器:RS485 总线舵机驱动、逆运动学求解、串口通信协议、运动规划(Keil 上位机配套)

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages