r/robotics • • 9h ago

Community Showcase I Built a Drone With Only One Motor – And a Poor Man's High Speed Camera

453 Upvotes

I've been working on an under-actuated drone that can fly with only one motor for the last few months. The propeller hub is 3D printed out of PPA-CF and has a hinge, so when the motor accelerates the blades lag behind and one pitches up while the other pitches down. By pulsing the motor torque 300 times a second, timed to the rotor angle, the drone controls its pitch and roll with the same motor that produces its thrust.

Along the way I also built a "poor man's high speed camera" - a camera flash synced to my camera and motor controller, so I could take images of the propeller at precise angles to analyze its behavior.

The mechanical simplicity hides a lot of software. At almost 20,000 RPM, a 0.2 ms control loop is already 20 degrees of rotation late, so I wrote custom firmware for the motor controller to compute the torque pulses on board. The body also slowly spins, so the drone gets its heading from an infrared "lighthouse" beacon in the corner of the room that it sees once per revolution.

The full video explaining how it all works is available on youtube: https://www.youtube.com/watch?v=Ndftcft6XhU

The code and CAD are open source on github: https://github.com/jyjblrd/single-propeller-drone


r/robotics • • 9h ago

Humor Development over 8 months yay

Thumbnail
gallery
325 Upvotes

I posted here 8 months ago and am only now posting again, haha. This is how I've improved over the past 8 months:

MK5 V1:

The appearance was impressive, but... Yes, I lacked torque.

MK5 V2:

Hmm...

MK6 V1:

it walks!! it walks!! it walks!! 🐕🐕

MK6 V2:

Laser cut leg

Hell yeah😎😎

Two-arm guy

Two-leg guy

: w.i.p

DMs are welcome, and those who really want to see them can do so in Seoul😎

T h a n k y o u !


r/robotics • • 6h ago

Humor Spot stepped on it.

39 Upvotes

MK6 got stepped on by Spot.

It said it didn't hurt much lol.

I think this was the evening of the day I developed the MK6 V2 and participated in the Maker Faire.

2026.09.19 😎👍


r/robotics • • 8h ago

News Humanoid robot Digit 5 targets 20-hour workdays with 9-minute charging

Thumbnail
interestingengineering.com
28 Upvotes

Agility Robotics says its upcoming Digit 5 humanoid will use Nyobolt batteries to deliver 90 minutes of operation after a nine-minute charge. The targeted 10:1 run-to-charge ratio could support more than 20 productive hours per day in warehouses and manufacturing facilities. Early-access deployment is planned for the first half of 2027, but the company has not yet disclosed the battery capacity, cycle-life rating, or independent real-world test results.


r/robotics • • 22h ago

Community Showcase DIY robot arm that plays Nine Men's Morris

27 Upvotes

This little robot arm was designed for educational purposes.

The goal was to build a small, simple 3D-printed arm capable of precise, smooth movements. The first version has 4 DOF, with room for future expansion.

The J1, J2 and J3 joints use factory-made POM gears from the main gear of a Tarot 450 model helicopter. These mesh with the factory-made metal gears on the round NEMA 14 stepper motors, which are designed for 3D printer extruders. Torque is transmitted to the J3 joint through a GT2 belt, allowing all the steppers to remain at the base of the arm.

J4 is driven by an RC servo. It has some backlash, which could be eliminated with spring preload.

The controller is an inexpensive 3D printer board, the BTT SKR Pico, equipped with quiet TMC2209 stepper drivers. These can detect motor stalls, allowing sensorless homing.

The gripper is made of flexible TPU. The servo pulls a central filament, causing the fingers to bend inward. It's a very simple design that tolerates small positioning errors without putting much strain on the servo.

To make the programming more interesting, we set ourselves the goal of building a robot that could play Nine Men's Morris by recognizing its opponent's moves using a camera.

This video was recorded the first time the arm was able to play a game. Its movements will become smoother and much faster as development continues.

We developed custom Arduino-based firmware for the Pico board. By feeding motion commands into a queue, it allows the arm to move continuously, with smoothly rounded transitions between movements.

All higher-level tasks are handled by Python code running on a PC.

When powered on, the arm first moves to its home position and then moves out of the camera's field of view. The software calibrates itself using the camera image and identifies the positions on the board. During the game, it watches the opponent's moves, calculates a response, and executes its own move.


r/robotics • • 4h ago

Community Showcase 3D Printed Rocker-Bogie Mars Rover | ESP32-CAM | 6-Wheel Robotic Platform

Post image
20 Upvotes

r/robotics • • 21h ago

Electronics & Integration [Open-source] One-Class SGD (SONAR)

2 Upvotes

One-Class SGD learns where the normal readings are and draws a boundary around them. For every new reading it gives back a number: negative means inside the boundary (normal), positive means outside (strange), and the bigger the number, the further outside. It learns one reading at a time, so it can follow the robot while it works.

One-Class SGD learns a boundary around the healthy joint residual and scores each new sample by its signed distance from that boundary. In a control loop it answers one question per reading: does this residual look like the healthy run, or does it fall outside the region the detector has learned to call normal?



  1. What it is¶

The healthy residuals of a robot joint cluster around zero. When the payload changes, or a joint wears, the residuals move outside that cluster. A One-Class SVM finds the smallest region of the joint space that contains the healthy cluster, with the boundary at a fixed margin from the cluster's edge. A new sample is flagged when it falls outside.

The classical One-Class SVM has two problems for a control loop. First, it needs the whole healthy dataset in memory: the kernel expansion uses every training point. Second, it uses a kernel that maps the data into an infinite-dimensional space, so a plain gradient step would need the full Gram matrix at every update.

SONAR fixes both. It replaces the kernel with random Fourier features (RFF), a finite d-dimensional approximation of the RBF kernel, so each gradient step depends only on the current sample. It also adds a strongly convex regularisation to the objective, which turns the SGD convergence into a 1/T guarantee instead of the slower 1/sqrt(T) of the plain formulation. The resulting algorithm is called SONAR: SGD-based One-Class Novelty detection with Approximate RBFs.

  1. How it works¶

The objective, from Suk and Kpotufe (2025), equation 9, is

F(w, rho) = (||w||^2 + rho^2) / 2 - nu * rho + E[ max(0, rho - w^T z(x)) ]

where w is the hyperplane normal, rho is its offset, z(x) is the RFF embedding of the sample, and nu is the expected fraction of outliers, in (0, 1). The first two terms are the strongly convex regulariser that keeps the solution well-behaved; the last is the hinge loss on the healthy samples, which pushes them inside the boundary.

The RFF embedding, from Rahimi and Recht (2007), is a cos-sin pair:

z(x) = sqrt(2/d) * [sin(omega_1^T x), cos(omega_1^T x), ..., sin(omega_d^T x), cos(omega_d^T x)]

with the frequencies omega_j drawn from N(0, 2 * gamma * I). With enough features, the dot product z(x)^T z(y) approximates the RBF kernel exp(-gamma * ||x - y||^2).

The SGD step, from Algorithm 1 of the paper, is

Z_t = 1{ w_{t-1}^T z(X_t) <= rho_{t-1} } w_t = w_{t-1} - eta_{t-1} * (w_{t-1} - z(X_t) * Z_t) rho_t = rho_{t-1} - eta_{t-1} * (rho_{t-1} - nu + Z_t)

with eta_t = step / t. Each sample updates the boundary by a small amount, so the detector learns as it goes.

The score of a sample is the signed distance from the boundary:

score(x) = rho - w^T z(x)

Positive means the sample is outside the boundary (anomalous), negative means inside (normal). The class flag threshold defaults to 0.0: any point on the wrong side of the boundary is flagged.

  1. Smallest example¶

import numpy as np from dense_armor.utility.anomaly.ocsvm import OneClassSGD rng = np.random.default_rng(0) det = OneClassSGD(n_features_rff=64, nu=0.05, n_init=50, seed=0) for _ in range(2000): det.learn_one({ "a": float(rng.normal(0.5, 0.05)), "b": float(rng.normal(0.5, 0.05)), }) print(f"healthy {det.score_one({'a': 0.5, 'b': 0.5}):+.4f}") print(f"anomaly {det.score_one({'a': 5.0, 'b': 5.0}):+.4f}")

healthy -0.0013 anomaly +0.0088

The healthy point lies just inside the boundary: its signed distance is slightly negative (-0.0013). The anomaly lies outside: its score is positive (+0.0088). OCSVM returns a signed distance, not a probability, so the numerical contrast is small even when the two points are on opposite sides of the boundary.

  1. On a real arm¶

The residual of joints 1-4 on a SyntheticArm payload fault. The fault (5 kg added at t = 2 s) shifts the residual on joints 1-5 by tens of N·m.

from pathlib import Path from dense_armor.utility.anomaly.ocsvm import OneClassSGD from dense_armor.utility.datasets import SyntheticArm ds = SyntheticArm( Path("test/fixtures/urdf/panda.urdf"), period_s=2.0, rate_hz=50.0, n_cycles=2, fault_at_s=2.0, fault="payload", payload_mass=5.0, noise_std=(1e-3, 5e-3, 5e-2), seed=0, ) det = OneClassSGD(feature_keys=["r0", "r1", "r2", "r3"], seed=0) for sig, _ in ds.stream(): q, qd, qdd = ds.trajectory(sig.t) tau_nom = ds.nominal_torque(q, qd, qdd) x = {f"r{j}": float(sig[f"tau_{j}"] - tau_nom[j]) for j in range(4)} _ = det.score_one(x) _ = det.learn_one(x)



Top: SyntheticArm with n_cycles=2 (200 samples, 100 healthy, 100 fault). Bottom: DriftStream with one sudden drift at half. The boundary separates the fault on both streams: the score of the faulty samples is above the adaptive 0.95-quantile threshold from the very first post-fault sample.

  1. Convergence¶

The paper proves (Lemma 5, Theorem 6, Corollary 7) that the distance between the SGD iterate and the population minimiser shrinks like 1/T, and Lemma 5 gives the explicit schedule eta_t = 1/t that makes the bound hold. The default step=1.0 of the detector reproduces exactly that schedule.



The score on one fixed healthy point, plotted against the number of samples learned. Before n_init the detector returns zero: it is still collecting the standardisation statistics. After that the score oscillates around a value that slowly moves toward its asymptotic position.

  1. Numbers¶

SyntheticArm (200 samples, 100 healthy, 100 fault) and DriftStream (2000 samples, one sudden drift at half), every detector built with its defaults.

StreamProtocolROC-AUCFalse alarms on the healthy partThreshold at the last healthy sampleSyntheticArmlearn-on-healthy0.8560.010-0.0008SyntheticArmlearn-on-all0.6710.010-0.0008DriftStreamlearn-on-healthy0.9870.054-0.0002DriftStreamlearn-on-all0.7690.054-0.0002

The default step of 1.0 reproduces the paper's schedule eta_t = 1/t: the boundary moves on every healthy sample and the fault is separated on both streams. The default threshold of None uses the 0.95 quantile of the last 500 scores; while fewer than 50 scores have been seen the effective threshold is 1e9 and nothing is flagged.

The arm run has 100 healthy samples: after n_init = 20 they give 80 SGD steps, not the few thousand the paper's experiments use, and the ROC-AUC is 0.856. On DriftStream the 1000 healthy samples before the drift give the boundary enough updates to reach 0.987. SONAR is included here because its guarantee is the strongest of the five detectors — it is the only one with a formal bound on both Type I and Type II errors — and because when the healthy stream is long enough the separation is crisp.

Details¶

Where the rules come from¶

Suk and Kpotufe (2025) introduce SONAR in the context of one-pass non-stationary outlier detection. Equation 9 is the objective, Algorithm 1 is the SGD update, Lemma 5 and Theorems 3, 4, 6, 8 are the theoretical guarantees on the Type I and Type II errors of the final iterate. The RFF embedding is from Rahimi and Recht (2007).

The standardisation and the threshold rule are this module's choices; the paper assumes the data lie on the unit sphere, but that is a hypothesis on the data, not a preprocessing step.

Choices stated explicitly¶

Standardisation. The paper assumes the RBF kernel's data lie on the unit sphere. This module standardises each feature with running mean and standard deviation, learned from the first n_init samples and frozen afterwards. Dividing each sample by its Euclidean norm, the alternative, maps a large fault in the same direction as a healthy sample onto the same point, and cannot be used.

RFF feature count. A hyper-parameter n_features_rff (default 128). The paper's bound on the number of features (Appendix A) depends on the dimension and the assumed margin; this module does not use it as a formula.

Step size. eta_t = step / t, with step a user constant. 1.0 recovers the paper's eta_t = 1/t; the paper's own experiments use AdaGrad.

Embedding. The prefactor sqrt(2/d) is the one printed by Suk and Kpotufe (Section 3). With d cos-sin pairs it gives ||z(x)||^2 = 2 and z(x)^T z(y) approximates 2 K(x, y), while the paper states z(x)^T z(y) = K(x, y) in expectation; for d = 1 the printed prefactor gives z^T z = 2 where that identity needs 1. The implementation keeps the printed prefactor. With the prefactor sqrt(1/d), which gives z(x)^T z(y) close to K(x, y), the benchmark ROC-AUC (learn-on-healthy) moves from 0.856 to 0.871 on SyntheticArm and from 0.987 to 0.972 on DriftStream, so the printed prefactor stays.

Threshold. Default None: the detector uses the 0.95 quantile of the last 500 scores, with 1e9 while fewer than 50 scores have been seen. A single float can be passed instead (e.g. 0.0, the signed distance from the boundary).

Measured cost¶

At the benchmark defaults (n_features_rff=128, n_init=20), on one CPU thread:

0.131 ms per sample for learn_one + score_one on the arm stream;

memory flat: two 2 * n_features_rff vectors, independent of the stream length.

The budget_s declared by the class is 0.001 s.

What the detector does not do¶

It does not adapt quickly. The kernel approximation is fixed at construction: if the healthy distribution moves, the standardisation becomes stale and the boundary must be re-learned from scratch. For non-stationary streams the paper proposes SONARC, a variant with change-point detection and restarts; this module does not implement it.

OnlineRobustMahalanobis is the alternative when the fault is a shift of the mean and the healthy stream is short.

Link : https://tatopenn-cell.github.io/Dense-Armor/anomaly/ocsvm/


r/robotics • • 13h ago

Tech Question Same payload, same reach, very different behavior in production — wrist geometry is why

0 Upvotes

I keep running into robot selection discussions where the word "configuration" gets used for two completely different things, and the mixup causes real problems downstream.

Under ISO 9787, configuration technically means the set of joint values that determines the robot shape at any time. In selection discussions, engineers usually mean the mechanism architecture: inline wrist, offset wrist, or the cobot-style layout with parallel middle axes. Those are different questions, and a datasheet will not sort them out for you.

How I break it down:

The first three joints place the wrist center. The last three set tool attitude. If joints 4, 5, 6 intersect at one point, you have the classical inline wrist: partially decoupled position and orientation, analytic inverse kinematics, and decades of offline programming support. That is a big reason the PUMA-type architecture still dominates industrial robotics.

Offset wrists give up that geometric convenience for clear process reasons: more orientation envelope and better obstacle avoidance for long tools such as spray guns, torches, and abrasive heads. The cost lands in IK complexity, singularity analysis, and trajectory continuity.

Cobot layouts reorder the middle axes (2, 3, 4 parallel) around compact joint modules, not around the classical wrist-center geometry. Fine for light tending, screwdriving, and lab automation, but it should not be treated as identical to the traditional industrial arm.

The layer most people skip: one TCP pose on a typical inline-wrist robot can correspond to up to eight IK solutions. Front/back, elbow-up/elbow-down, flip/no-flip. A lot of "programming problems" are wrong-branch problems for the path, not wrong-robot problems.

And singularity behavior follows the geometry, not the catalog page. Shoulder, elbow, and wrist singularities all shift when the wrist geometry changes.

Question for people who do this for a living: do you evaluate posture-branch availability during selection, or does it usually surface at commissioning?

I wrote up the full breakdown with the geometric details here, if useful: https://zanerobotics.substack.com/p/3-serial-six-axis-robot-arm-configurations?r=6ysx3o


r/robotics • • 13h ago

Tech Question PCB mount servo drives vs traditional panel mount for compact robotic joints

0 Upvotes

i Want to know how roboticists here handle the packaging headaches on tight builds.Panel mounted drives make servicing and thermal heatsinking easy, but routing high-current motor phases and encoder lines through 6 articulated joints is a nightmare. Lately we’ve looked at ditching the central cabinet and dropping miniature PCB mount drives (like Elmo and Synapticon) right onto custom joint carrier boards. Is the wiring reduction worth the inevitable thermal dissipation and serviceability trade-offs?