中文
🤖 LateAI
HomeBuild Your Own Robot › NeuralNexus Arm 6-DOF
🧠

NeuralNexus Arm: a 6-DOF Arm Built from Scratch, from CAD to Digital Twin

机械结构 · 固件 · 运动学 · 数字孪生 · 视觉,全部自研
📶 Advanced ⏱ Mechanical + firmware + host software 🦾 6 stepper axes + AS5047P encoders 🧠 STM32H743 @ 420 MHz

📌 What this guide covers

This build breaks from the pattern of the other guides here: it is not a copy of an existing open-source arm. The mechanics, the firmware, the kinematics, the digital twin and the vision pipeline were all built from scratch — no kit, no off-the-shelf motion stack. The project has a single thesis: one command makes the physical arm and its 3D digital twin move together.

Because the authors (Lasan Perera and four co-authors) carried the whole chain through, it works extremely well as a textbook for what a robot arm is actually made of: SolidWorks CAD → Simscape Multibody digital twin → MATLAB inverse kinematics → USB CDC at 115200 → STM32H743 real-time servo loop → six steppers plus a servo gripper → AS5047P absolute encoder feedback.

The guide runs system chain → hardware stack → joint transmission → control & protocol → digital twin → vision → downloads → pitfalls. The value is not "build this exact machine" but learning how each layer interfaces and how to verify it.

📦 Every official file is mirrored on this site — firmware project, final report PDFs, schematics, Gerber, STL/STEP models, MATLAB scripts and vision scripts. See the downloads section; no VPN needed.

🧭 Part 1: the system chain (understand the data flow first)

Hold this chain in mind and every later section snaps into place: the host solves the kinematics, sends joint angles over serial, and the MCU does the real-time servo work. A deliberately minimal text protocol decouples the two sides, so each can be developed and debugged alone.

SolidWorks CAD  ->  Simscape Multibody digital twin  ->  MATLAB inverse-kinematics solve
                                     |
                        USB CDC @ 115200 baud (joint-angle text protocol)
                                     v
        STM32H743 @ 420 MHz  ->  6 stepper motors + gripper servo (2 kHz control loop)
                                     |
                    AS5047P absolute encoders (SPI feedback, 14 bit)
                                     |
        Browser Web Serial control panel (Chrome / Edge, no MATLAB license needed)

🔧 Part 2: the hardware stack (selection logic beats part numbers)

ItemConfiguration
MCUSTM32H743VITx (Cortex-M7 @ 420 MHz, 25 MHz HSE → PLL: M=5 / N=168 / P=2)
Steppers2 × NEMA 24 (J2 / J3) · 1 × NEMA 23 (J1) · 3 × NEMA 17 (J4 / J5 / J6)
Drivers2 × CL57T closed-loop (J2 / J3) · 1 × DM542 open-loop (J1) · 3 × onboard TMC2209 at 1/8 microstep (J4 / J5 / J6)
Encoders6 × AS5047P magnetic absolute encoders, 14 bit (16384 counts/rev), SPI1 mode 1
GripperHobby servo · TIM1_CH3 · 50 Hz PWM (930 µs open / 1100 µs close)
Peripheral mapTIM1_CH3 gripper · TIM2_CH2 buzzer · TIM6 stepper ISR @ 2 kHz · USB CDC virtual COM
Control loopTIM6 ISR @ 2 kHz (PSC 74 / ARR 499), 500 µs tick
Wiring topologyM1–M3 common-anode (STEP+/DIR+/ENA+ to external +5 V, STM32 sinks via open-drain) · M4–M6 push-pull 3.3 V direct
⚠️ The driver topology is the easiest thing to misread in this project. The onboard TMC2209s are driven push-pull (OUTPUT_PP, 3.3 V direct); the external CL57T / DM542s are open-drain (OUTPUT_OD, common-anode, active low). That is why Stepper_SetEnableM4M5M6(false) is in fact the call that enables the external drivers. It looks like a bug during code review — it is not. Think through the polarity before you "fix" it.

⚙️ Part 3: joints, gearing and pulse scaling

JointRole / motor / ratio / pulses per joint rev
J1Base rotation · NEMA 23 (DM542) · 1:7 · 2800
J2Shoulder · NEMA 24 (CL57T closed-loop) · 1:30 · 5000
J3Elbow · NEMA 24 (CL57T closed-loop) · 1:30 · 5000
J4Wrist pitch · NEMA 17 (TMC2209 1/8) · 1:1 · design 1600
J5Wrist roll · NEMA 17 (TMC2209 1/8) · 1:1 · design 1600
J6Tool rotation · NEMA 17 (TMC2209 1/8) · 1:1 · design 1600, measured 3200
Joint limitsJ1 ±180° · J2 −70°…70° · J3 120°…240° · J4 ±180° · J5 60°…300° · J6 ±180°
Index mappingjointToMotor[6]logical joint numbering is not motor wiring order; the map lives in firmware and must be updated whenever wiring changes
💡 Measure the pulse scaling per axis. The project status table records J6 at 3200 pulses per joint revolution rather than the designed 1600 — microstep settings and real drivers do not always agree with the datasheet. Get pulsesPerJointRev wrong and every angle scales wrong, which is exactly why absolute positioning currently works only on the 1:1 wrist joints.

🖥️ Part 4: the serial protocol and control options

CommandFormat and meaning
Move jointsSix comma-separated angles in degrees → 0,-50,60,-80,-90,50; firmware converts to steps and echoes steps: ...
GripperG,1 close / G,0 open (TIM1_CH3 servo PWM)
HomingHOME — encoder zero calibration
Self testTEST — 30° encoder calibration routine
Operating modeRelative mode: every command is a delta from the current pose. Absolute positioning and the J3/J5 180° offsets are implemented but disabled until encoder homing is finished.

On the host side you have two options: a MATLAB session (solve IK, drive the twin, send angles) or the browser control panel (open the HTML in Chrome / Edge, click Connect, pick the COM port — no MATLAB license required). The standard MATLAB opening sequence is below; the very first step must create jointData, because the Simscape From Workspace blocks depend on it:

% Run this block at the start of every MATLAB session
jointData = zeros(2,7);            % must exist before loading the model
load_system('Assem1');
robot = importrobot('Assem1');
robot.DataFormat = 'row';
limitsDeg = [-180 180; -70 70; 120 240; -180 180; 60 300; -180 180];
for i = 1:6
    robot.Bodies{i}.Joint.PositionLimits = deg2rad(limitsDeg(i,:));
end
ik = inverseKinematics('RigidBodyTree', robot);
weights = [1 1 1 1 1 1];
q0 = homeConfiguration(robot);
s  = serialport("COM9", 115200);   % check Device Manager for the real port

% Typical actions
armIK(robot, ik, weights, q0)                         % interactive IK slider tool
goToXYZ(robot, ik, weights, q0, [-0.15 0.30 0.10], s) % XYZ to hardware + synced twin
moveBoth(s, [0 10 0 0 0 0])                           % move hardware and sim together
armWave(s)                                            % wave routine
writeline(s, "HOME")                                  % encoder homing
⚠️ Never share one bus between telemetry and command handshakes. The periodic enc | ... print from firmware floods the port, so MATLAB's readline() == "END" waits forever and both the simulation and the hardware freeze. Pick one mode at a time, or add a timeout on the reading side.
⚠️ Keep single moves under 10°. A 60° move takes roughly four seconds at 400 steps/s, but the demo scripts fire the next command after one second and cut it short. Split 60° into six 10° chunks — no firmware change and no handshake needed.

🧪 Part 5: the digital twin and the demos

The Simscape Multibody model in Mechanics Explorer is a full-size copy of the real arm. Every time MATLAB solves IK it writes joint angles into jointData, the From Workspace blocks read that two-column time table and drive the six revolute joints — press Run and the on-screen arm performs the same motion as the hardware on the bench.

🧠 A kinematic result worth remembering: a 6-DOF arm cannot hold both tip position and tool orientation while the links reconfigure — that needs a seventh DOF. Every "lock" demo above therefore pins one constraint and lets the other vary. Once you internalise this, you stop expecting a six-axis arm to keep its tool orientation in every pose.
⚠️ Over-constraint is the number one reason Simscape models refuse to move. Redundant parallel or perpendicular mates in SolidWorks weld a revolute joint into a rigid connection and remove its DOF. The rule: exactly one concentric mate plus one coincident (or distance) mate per joint — anything extra locks it.

👁️ Part 6: the vision pipeline (pixels → world coordinates → IK)

StageMethod and figures
Intrinsics20 chessboard images → reprojection error 0.6863 px (calibrate.py / calibrate picam.py)
MountingCamera rigidly fixed at 41 cm height; the extrinsics assume exactly that pose
Extrinsics3×3 homography mapping pixel coordinates to millimetre-scale world coordinates (homography.py)
DetectionColour segmentation (segmentation.py) and YOLO detection with custom-trained models (cartons, candy, …)
LoopDetection → world coordinates → inverse kinematics → joint angles → gripper action
⚠️ A homography is only valid for the camera pose it was calibrated at. Move the camera and you must recalibrate the extrinsics (the intrinsics stay good). This is the classic vision-picking trap: "it was accurate yesterday, today it is centimetres off" is almost always this.

📥 Downloads (full official mirror, no VPN needed)

Everything below is a mirror of the original repository files, grouped by purpose and downloadable directly. Total: 248 files / about 27.7 MB.

docs · reports and presentations (EDR Documentation)

FileSizeNotes
Neural_Nexus_Final_Report.pdf3.8 MBFinal technical report covering mechanics, electronics, firmware, kinematics and vision — start here
Neural_Nexus_Final_Presentation.pdf6.9 MBFinal presentation with photos, exploded views and result screenshots
Neural_Nexus_Project_Pitch.pdf0.4 MBProject pitch: design goals and selection rationale

schematics · electrical

FileSizeNotes
MCU_Schematic.pdf0.3 MBMain board: STM32H743 core, driver interfaces, power
Encoder_Schematic.pdf0.12 MBAS5047P encoder board (SPI wiring, magnet placement)

gerber · PCB fabrication files

FileSizeNotes
MCU.rar0.24 MBMain board Gerber package, ready to order
nema17.rar21 KBNEMA 17 mount / adapter board
nema17pancake.rar20 KBNEMA 17 pancake adapter board
nema23.rar22 KBNEMA 23 (J1) mount
nema24.rar22 KBNEMA 24 (J2 / J3) mounts
📦 The Gerber packages are .rar archives — extract with WinRAR / 7-Zip, then upload the folder to any PCB service.

eda · EasyEDA (LCEDA) Pro project (added by this site · editable source)

FileSizeNotes
NeuralNexus_Arm.eprj21.3 MBEasyEDA Pro (LCEDA) project package with one schematic (Schematic1 / P1) and one board (PCB1). Open it via File → Open → Local project to keep editing, then order straight from the fab
preview_schematic.webp · preview_pcb.webp23 KB / 112 KBBuilt-in thumbnails of the schematic and the PCB — preview before downloading
💡 .eprj2 is the project format of EasyEDA Pro (LCEDA professional edition) — essentially a SQLite database; the standard edition cannot open it.
⚠️ This project was created by this site and is not part of the official repository; it may differ from the official schematics, so use the files in gerber/ for real fabrication.

ik-matlab · MATLAB / Simscape project and models

FileSizeNotes
GUIDE.txt6 KBCommand cheat sheet: session setup, IK solve, slider tool, hardware commands, demo order
REQUIREMENTS.txt0.6 KBMATLAB release and toolbox requirements (Robotics System Toolbox, …)
Assem1.slx0.24 MBSimscape Multibody digital twin with six revolute joints and From Workspace inputs
Assem1_DataFile.m10 KBAuto-generated model parameters (link lengths, masses, inertias)
Part1–Part6.STL · endeff.STL0.9 MB totalSeven link / end-effector STL meshes for visualisation
Part1–Part6 / endeff · STEP0.58 MB totalMatching STEP solids for redesigns or format conversion (this site's online STL → STEP converter handles that class of model too)
armIK.m · goToXYZ.m · moveBoth.m1–2 KB eachInteractive IK tool, XYZ target dispatch, synced relative motion
drawSquare.m · armWave.m · objectPicking.m1–3 KB eachMotion library: vertical square, wave, pick-and-place
demoPositionLock.m · demoOrientationLock_XYZsweep.m1.5–1.7 KBThe two lock demos
nullspaceDemo.m · findDemoPoint.m1.4–1.8 KBRedundancy demo and reachable-point search

vision · scripts and calibration data

FileSizeNotes
calibrate.py · calibrate picam.py5.6–6.2 KBChessboard intrinsic calibration (regular camera and Raspberry Pi camera)
homography.py3.7 KBHomography solve: pixels → world coordinates
segmentation.py · detect_picam.py4.6–4.8 KBColour segmentation and the detection main loop (YOLO call included)
camera_matrix.npy · dist_coeffs.npy200 / 168 BCalibration results: 3×3 intrinsics and distortion coefficients
candy.yaml183 BColour threshold parameters (candy example)
calib_photos/ (20 images)1.4 MB totalChessboard photos so the calibration can be reproduced

firmware · STM32H743 project (170 files / 11.8 MB)

PathNotes
REQUIREMENTS.txtBuild and hardware notes: toolchain, pin map, pulses per rev, the three USB CDC requirements, full serial protocol
Test1.0.iocSTM32CubeMX configuration (clock tree, TIM6 at 2 kHz, USB CDC, SPI1, …)
Core/Src/main.cMain program: stepper stepping, gripper PWM, protocol parsing, encoder reads
Core/Inc/main.h · stm32h7xx_it.cPin definitions and interrupt handlers
NeuralNexusArm1.0 Debug.launchSTM32CubeIDE flash / debug configuration — import and build straight away
STM32H743VITX_FLASH.ld · Drivers/ + Middlewares/ + USB_DEVICE/Linker script plus ST HAL / CMSIS / USB middleware, so the project builds without installing a separate firmware pack
💡 Where to read first: skim firmware/REQUIREMENTS.txt (one page for every assignment), then the stepping and protocol code in Core/Src/main.c, and finally cross-check with ik-matlab/GUIDE.txt to see how the host drives it.

🚀 Part 7: quick start (three steps to a first motion)

  1. Firmware: import firmware/ into STM32CubeIDE (the .launch config is included) → build → flash via ST-Link → confirm the board enumerates as a USB virtual COM port. If you regenerate code from CubeMX, comment out MX_SDMMC1_SD_Init() again — with no SD card inserted it drops straight into Error_Handler() and the board hangs at boot.
  2. Host: open ik-matlab/ in MATLAB and follow section 0 of GUIDE.txt (jointDataimportrobot → joint limits → IK solver → serial), then run goToXYZ or armIK.
  3. No-MATLAB route: open the official control panel HTML in Chrome / Edge → Connect arm → choose the port → send angles directly. Run clear s first to release the port from MATLAB, otherwise it will not open.
⚠️ If USB does not enumerate, check three things in order: ① HSI48 is routed as the USB clock (RCC_USBCLKSOURCE_HSI48); ② HAL_PWREx_EnableUSBVoltageDetector() is called before MX_USB_DEVICE_Init(); ③ you are on the board's native USB-C (PA11 / PA12) with a data-capable cable, not the ST-Link USB. If the COM number keeps changing, uninstall the greyed-out hidden entries in Device Manager and replug.

🐛 Part 8: pitfalls (the section that saves the most time)

📊 Current project status (per the authors)

SubsystemStatus
6-axis stepper control / USB CDC / joint mapping / direction signs✅ Done
MATLAB IK pipeline / synced digital twin / gripper servo✅ Done
Encoder reads (AS5047P, ±0.01° repeatability)✅ Done
Motion library (5 sequences + wave + square) and browser control panel✅ Done
Homing J4 / J5 / J6🟡 Working, scale verification ongoing
Homing J1 / J2 / J3🔴 Blocked (encoder mounting ratio creates ambiguity)
pulsesPerJointRev calibration🟡 J6 measured 3200 (not 1600); the rest to be measured
Absolute positioning🟡 Only viable on the 1:1 wrist joints so far
3.3 V LDO replacement🔴 Needs a measurement of the actual VIN at the LDO pad
Closed-loop stepper correction / vision integration⚪ Planned
📌 This status table is itself good study material: it states exactly which layer works and what it is still waiting on. The realistic order for your own build is relative mode + twin sync first, absolute positioning later — rather than chasing a closed loop from day one.

⚖️ License and usage notes

🔗 Official sources

❓ FAQ

Should I build this arm as-is?

Treat it as a full-stack study reference rather than a copy-and-build kit: firmware, simulation and vision are under active development and absolute positioning is unfinished. If you want a mature six-axis build that works once assembled, start with our Faze4 / PAROL6 guides; if you want to learn how each layer of an arm is written, NeuralNexus is unusually complete.

Can I use it without a MATLAB license?

You can drive the hardware: the project ships a Web Serial API control panel, so Chrome / Edge can command the arm over serial. The digital twin and inverse kinematics do depend on MATLAB (Robotics System Toolbox + Simscape Multibody). You can reimplement IK in Python, but you would have to measure the model parameters yourself.

Why is motion relative?

Absolute positioning needs a trustworthy zero, and zero comes from AS5047P encoder homing. J1–J3 are not homed yet because the encoder mounting ratio makes the reading ambiguous, so the firmware currently exposes only relative deltas. J4–J6 homing already works.

The encoder readings jump — is the sensor dead?

Suspect the software first: failed reads return a -1 sentinel, and averaging those into the sample window makes a stationary joint drift 60–70°. Filter them out and repeatability reaches ±0.01°.

Can this code run a different arm?

The architecture transfers well: a 2 kHz unified stepper ISR, a text protocol, and IK solved on the host is a solid pattern. Expect to rewrite the joint-to-motor map, pulses per rev per axis, joint limits and link lengths, plus either replace or recalibrate the Simscape model. Apache-2.0 permits this as long as you keep the notices.

✅ Next steps

Suggested order: read docs/Neural_Nexus_Final_Report.pdf for the big picture → run the simulation and IK in MATLAB using ik-matlab/GUIDE.txt → flash the firmware and try relative moves over serial → only then touch vision calibration. Change one layer at a time and faults stay easy to localise.