[{"content":"The background model tells the safety node what\u0026rsquo;s always there. What it doesn\u0026rsquo;t know is what kind of thing just appeared in a novel voxel.\nA person walking through the workspace and a box being pushed across it look similar at the zone classifier level: both are novel, both have range, both can cross zone boundaries. Before Phase 6, both would trigger CAUTION and STOP. That\u0026rsquo;s technically safe, but it\u0026rsquo;s also wrong — and a system that cries wolf too often gets disabled.\nPhase 6 adds a layer between background filtering and zone classification: cluster the novel returns, extract features, classify each cluster as PERSON, OBJECT, or UNKNOWN, and only pass PERSON detections into the zone logic. The classifier can suppress false alarms. It cannot suppress genuine threats.\nCluster builder cluster.py runs DBSCAN on the novel point cloud each frame. Each cluster becomes a Cluster object with extracted features:\ncentroid — spatial center of mass range — distance from sensor origin point_count — number of returns in cluster velocity_spread — range of doppler velocities across the cluster height_span — vertical extent of the cluster doppler_asymmetry — imbalance between positive and negative velocity components snr_mean — average signal quality across returns temporal_variance — how much the centroid has moved across the last N frames Clusters persist across frames via centroid matching. A cluster that was at position X last frame and is at position X+delta this frame is the same cluster — it gets a stable ID and an accumulating history.\nRule-based classifier classifier.py scores each cluster using four feature votes. The default threshold requires 2 of 4 votes for a PERSON label. Everything below threshold is OBJECT. Everything that doesn\u0026rsquo;t fit either cleanly is UNKNOWN.\nVote 1 — velocity spread. Humans have mixed doppler: arms, torso, and legs all move differently. A person walking produces a wide spread of velocities across the cluster. A moving box produces a narrow, coherent velocity signature. High spread → PERSON vote.\nVote 2 — height span. A person is tall. A box is not. Returns from a person span significant vertical range even at the resolution of the IWR6843AOP. Low vertical span → OBJECT vote.\nVote 3 — point count. A person is a large, complex reflector. But this vote is gated — it only fires when a corroborating feature (velocity spread or height span) also supports PERSON. Alone, point count is too ambiguous.\nVote 4 — doppler asymmetry. Human gait is asymmetric: the leading foot approaches, the trailing foot recedes. Returns from a walking person will have both positive and negative doppler simultaneously. A rigid object moving in one direction won\u0026rsquo;t. Asymmetry → PERSON vote.\nAll thresholds are YAML-tunable. The defaults are conservative — they\u0026rsquo;re designed to minimize false negatives (missed people) rather than false positives (spurious stops). Tune them from real workspace data.\nFail-safe by design UNKNOWN is not a third safe state. The classifier has three possible outputs:\nPERSON — passes to zone logic, can trigger CAUTION/STOP OBJECT — suppressed, logged, never reaches zone logic UNKNOWN — passes through as PERSON If the classifier sees something it can\u0026rsquo;t confidently label, the detection passes through. The pipeline is never less safe than the baseline without the classifier. The only thing the classifier can do to zone behavior is suppress OBJECT-labeled returns.\n# Bypass classifier entirely — restores Phase 3 behavior instantly classifier_enabled: false Training data pipeline Every OBJECT suppression gets logged to ~/mmwave/logs/classifier_training.csv:\ntimestamp, centroid_x, centroid_y, centroid_z, range_m, point_count, velocity_spread, height_span, doppler_asymmetry, snr_mean, temporal_variance, label The label field is OBJECT — these are suppressed detections the classifier was confident about. Every hour of operation with a real arm in a real workspace builds a labeled dataset automatically.\nWhen Phase 6b arrives — a sklearn RandomForest or lightweight tflite drop-in — this CSV becomes the fine-tuning dataset. The rule-based classifier ships to everyone. The trained model becomes an optional upgrade for those who\u0026rsquo;ve run enough hours to generate meaningful data.\nlogs/ is gitignored. Training data stays on the Jetson.\nWhat changed in the pipeline Novel points → cluster (DBSCAN) → classifier (PERSON/OBJECT/UNKNOWN) → OBJECT suppressed + logged → PERSON/UNKNOWN → zone logic → CLEAR/CAUTION/STOP The architecture between background model and zone logic now has a deliberate decision point. The zone classifier still makes the safety call. The micro-doppler classifier just filters what it sees.\n","permalink":"https://dntddynamics.com/lab-notes/mmwave-phase6-microdoppler-classifier/","summary":"Phase 6 adds a DBSCAN cluster builder and a rule-based micro-doppler classifier to the pipeline. Novel returns are now labeled PERSON, OBJECT, or UNKNOWN before they reach the zone logic. UNKNOWN passes through as PERSON — the classifier can never make the system less safe than the baseline.","title":"Micro-doppler classifier — not everything that moves is a person"},{"content":"The previous stack worked. Background learning masked out the walls, the sensor mount, the static clutter. The motionless person problem was solved. Zone transitions were clean.\nBut every time the Jetson rebooted — or the stack restarted — the sensor spent 15 seconds in a mandatory STOP while it relearned a room it had already mapped. In a real deployment that\u0026rsquo;s friction. In a safety system it\u0026rsquo;s a 15-second window where the arm is frozen for no reason.\nPhase 5 fixes that. The background map now saves to disk after the first learn, and reloads on every subsequent boot. The 15-second STOP is a one-time event.\nPersistent map After the learning phase completes, background_model.py writes the voxel grid to ~/mmwave/configs/background_map.npz. Atomic write — tmp file first, then rename — so a crash mid-write doesn\u0026rsquo;t corrupt the saved map.\nOn the next boot, the safety node looks for background_map.npz. If it finds one:\nValidates the saved voxel size matches the current config Validates the version field Loads the voxel scores directly into the background model Skips the 15-second STOP entirely If validation fails (config changed, file corrupt), it falls back to a fresh learn. Fail-safe default.\nThe map file is gitignored. It\u0026rsquo;s environment-specific — a voxel map of one workspace is meaningless in another. What ships in the repo is the code and the config. The map lives on the Jetson.\n# Force a fresh learn (workspace rearranged, new mount position, etc.) ros2 topic pub --once /dntd/relearn_background std_msgs/Bool \u0026#34;data: true\u0026#34; # Deletes the saved map, relearns the room, saves a new map The novelty-aware refresh gate Background learning works by counting returns per voxel. A voxel that gets hit in most frames becomes \u0026ldquo;background\u0026rdquo; and gets suppressed. The problem is what happens when someone stands still long enough.\nIn the original implementation, the background scores refreshed continuously. A person standing motionless in the workspace would accumulate background hits over time. Given enough frames, they\u0026rsquo;d cross the threshold and get absorbed into the background model. The system would stop seeing them.\nThe fix is a novelty gate in the voxel update logic. Before a voxel\u0026rsquo;s score is incremented, the model checks whether it was novel at the last observe pass. If it was — if it contained points that passed through as detections — it doesn\u0026rsquo;t get refreshed. Novel voxels and background voxels are tracked separately.\nA stationary person stays in novel voxels. They are never promoted to background. They hold their zone indefinitely.\nThe wall has been there since learning started. The person has not. The model now treats those two things differently.\nLocal config override One housekeeping fix that\u0026rsquo;s been on the list since the beginning: the MQTT broker IP was hardcoded in dntd_mmwave_config.yaml, which is committed to the public repo. That\u0026rsquo;s a small thing until it isn\u0026rsquo;t.\nThe pattern now:\ndntd_mmwave_config.yaml — committed, safe for public GitHub, no IPs dntd_mmwave_config.local.yaml — gitignored, Jetson-only overrides configs/*.local.yaml in .gitignore The safety node takes both --params-file flags in order. Local wins on any key that appears in both files. No code changes, no secrets in the repo.\nWhat\u0026rsquo;s next Phase 5 closes the background model. The sensor now knows the room, remembers it, and can\u0026rsquo;t be fooled by someone standing still.\nThe next gap in the pipeline is discrimination: the sensor sees novel returns, but it can\u0026rsquo;t tell the difference between a person walking through the workspace and a cardboard box on a cart. That\u0026rsquo;s Phase 6.\n","permalink":"https://dntddynamics.com/lab-notes/mmwave-phase5-persistent-background/","summary":"The 15-second background learning phase is necessary — but only once. Phase 5 adds a persistent voxel map saved to disk after the first learn, reloaded on every subsequent boot. Also: a novelty-aware refresh gate that means a stationary person can never be silently absorbed into the background.","title":"Persistent background map — the sensor shouldn't relearn the room every boot"},{"content":"Every mmWave safety system shipping today — including the enterprise solutions — mounts the sensor somewhere fixed: a wall, a post, a ceiling. The sensor sees the whole room. Detections anywhere in the monitored zone trigger a stop.\nThat\u0026rsquo;s safe. It\u0026rsquo;s also blunt. A person 3 meters away from an arm with a 1-meter reach isn\u0026rsquo;t in danger. The arm can\u0026rsquo;t get there. A fixed-mount sensor can\u0026rsquo;t make that distinction — it doesn\u0026rsquo;t know where the arm is, and it doesn\u0026rsquo;t know what the arm can reach.\nPhase 7 makes the system understand the arm\u0026rsquo;s reachable envelope. Detections outside the workspace get suppressed before they ever reach zone logic. The safety zones are no longer uniform spheres — they\u0026rsquo;re shaped to what the arm can physically threaten.\nThe architecture swept_volume.py runs SweptVolumeClipper after the micro-doppler classifier and before zone classification in the safety node pipeline:\nNovel points → cluster → classifier → swept-volume clip → zone logic Each frame, the clipper does three things:\nComputes the mount point — the world-frame position of the forearm link (joint 3) from the current kinematic chain. This is where the sensors live.\nSelf-exclusion sphere — a configurable radius around the mount point (default 0.15m) that suppresses returns from the arm body itself. Without this, the arm\u0026rsquo;s own structure would constantly show up as novel detections near the sensor.\nMax reach sphere — auto-computed by summing the link lengths from joint 3 outward through joint 4, joint 5, joint 6, and the end effector, plus a configurable margin (default 0.20m). Detections beyond this radius are outside the envelope. They\u0026rsquo;re suppressed.\nThe reach computation is live. As the arm moves to different joint configurations, the Jacobian updates the world-frame mount position every frame. The workspace boundary moves with the arm.\nThis matters more than it might seem. Reachability is direction-dependent — a person standing directly in front of the arm at full extension is in danger. The same person standing behind the base is not reachable regardless of range. A fixed sphere around the sensor can\u0026rsquo;t make that distinction. Because the clipper recomputes the envelope from actual joint angles every frame, it can. The zones are continuously shaped to what the arm can physically threaten at that moment.\nWhy the forearm link The sensor mount location was decided during Phase 7, not after. The forearm link — the straight segment between joint 3 and joint 4 — is the right place for three reasons.\nFirst, geometry: mounting on the forearm link gives the sensor a fixed orientation relative to the distal workspace. The wrist and end effector are the part of the arm that actually enters the human workspace during operation. The sensors need to see that zone clearly and consistently.\nSecond, stability: the forearm link doesn\u0026rsquo;t rotate on its own axis the way the wrist links do. A flat mounting surface, stable during most operating moves, predictable sensor coverage.\nThird, coverage: three sensors at 120° spacing on the forearm link gives 360° azimuth coverage of the distal workspace with no overlapping housekeeping. A fourth sensor would require an arbitration MCU, complicate USB enumeration, and add failure modes for minimal safety gain. Three covers the geometry cleanly.\nFail-safe behavior The clipper has two explicit failure modes, both of which default to passing all detections through.\nPlaceholder chain detected. During stick testing, the kinematic chain has no real joint data — the fake joint states publisher zeros everything. The clipper detects when joint positions look like a placeholder configuration and bypasses workspace clipping entirely. All novel detections pass to zone logic. This is correct: during testing without a real arm, there is no workspace to clip to.\nAll points suppressed. If the clipper\u0026rsquo;s geometry math is wrong and would suppress every detection in a frame, it aborts the suppression, logs a warning, and passes the original points through. The clipper can never make the system blind.\n# Bypass swept-volume clipping entirely swept_volume_enabled: false # Tune to match actual arm link diameter swept_volume_self_radius_m: 0.15 # Override auto-computed reach (leave at 0.0 for auto) swept_volume_max_reach_override_m: 0.0 # Safety buffer added to auto-computed reach swept_volume_reach_margin_m: 0.20 # Index of mount joint (forearm link = joint 3) swept_volume_mount_joint_idx: 3 The startup log prints the computed max reach every time the safety node starts. First thing to check when the arm is assembled and physical measurements are in the YAML.\nHardware configuration locked Phase 7 work is what locked the hardware configuration:\nIndustrial fixed arm: 3× IWR6843AOP, 120° spacing, forearm link mount. The swept-volume clipper is designed around this geometry. The sensor positions in the kinematic chain YAML map directly to physical mounting positions.\nMobile robot: 3× IWRL6432AOP, separate product configuration. The mobile variant has a different ego-motion problem — the whole robot body is moving, not just a sensor on an arm — and the IWRL6432AOP gives access to the raw range-doppler heatmap via the Udopproc DPU in MMWAVE-L-SDK. That\u0026rsquo;s Phase 9 territory and it\u0026rsquo;ll get its own pipeline.\nWhat the pipeline looks like now 3× IWR6843AOP UART (120° spacing, forearm mount) ↓ Driver node — TLV decode → PointCloud2 ↓ Safety node: Ego-motion compensation (Jacobian × q_dot) ↓ Background model (novel voxel filter) ↓ DBSCAN cluster builder ↓ Micro-doppler classifier (PERSON / OBJECT / UNKNOWN) ↓ Swept-volume clipper (workspace boundary enforcement) ↓ Zone logic → CLEAR / CAUTION / STOP ↓ Serial + GPIO + MQTT outputs The arm is printing. When it\u0026rsquo;s assembled, the link lengths go into the YAML and the swept-volume geometry becomes real. The demo video becomes possible after that.\n","permalink":"https://dntddynamics.com/lab-notes/mmwave-phase7-swept-volume-clipper/","summary":"Phase 7 adds a swept-volume workspace clipper to the pipeline. Detections outside the arm\u0026rsquo;s reachable envelope are suppressed before they reach zone logic. This is the primary technical differentiator over fixed-mount sensors — and the reason the hardware configuration locked at 3× IWR6843AOP on the forearm link.","title":"Swept-volume workspace clipper — the sensor finally knows what the arm can reach"},{"content":"The safety stack is complete enough to demo. The IWR6843AOP is running a full ROS 2 pipeline on the Jetson Orin Nano Super — real-time zone detection with ego-motion compensation, background scene learning, fault handling, and a heartbeat watchdog. If a robot arm were connected right now, it would stop when a person entered the zone.\nHere\u0026rsquo;s what got built, what broke, and what the non-obvious parts actually were.\nArchitecture Two ROS 2 nodes, cleanly separated:\ndntd_mmwave_driver_node.py — owns the hardware. Reads TLV frames from the sensor over UART, decodes them, publishes raw point clouds to /mmwave/raw_points and sensor health to /mmwave/diagnostics. Nothing in here knows about safety zones or robot arms.\ndntd_mmwave_safety_node.py — owns the safety logic. Subscribes to point clouds and joint states, runs ego-motion compensation, filters background, classifies zones, publishes CLEAR / CAUTION / STOP to /dntd/safety_zone. Also publishes a 5Hz heartbeat — if the safety node dies for any reason, the arm controller can self-stop independently.\nThe separation matters. The driver is hardware-specific. The safety node is hardware-agnostic — it works with any sensor that publishes PointCloud2. When the IWRL6432AOP arrives, the driver changes, the safety logic doesn\u0026rsquo;t.\nEgo-motion compensation The sensor is arm-mounted, which means every point in the cloud is in sensor frame — a moving reference. When the arm swings, the walls appear to move. Without compensation, the zone classifier sees phantom objects everywhere.\nThe fix:\nv_world = v_measured - J(q) · q_dot Where J(q) is the Jacobian of the sensor position with respect to joint angles, and q_dot is joint velocities from /joint_states. During the stick test (no real arm), all joints are zero so compensation is effectively disabled — which is correct, the sensor isn\u0026rsquo;t moving.\nWith a real arm, ros2_control feeds actual joint velocities and the compensation becomes meaningful. The URDF geometry lives in dntd_mmwave_config.yaml under joint_geometry — swap in real DH parameters when the arm arrives, no code changes required.\nBackground learning The sensor sees the world before it sees danger. Walls, the mount, furniture — all of it shows up as point cloud returns that aren\u0026rsquo;t threats. Without background masking, the zone classifier is useless.\nThe background model uses a voxel grid (10cm cubes). At startup it runs a 15-second learning phase: counts returns per voxel, builds a map of what\u0026rsquo;s always there. Once active, returns in background voxels are ignored. Returns in novel voxels go to the zone classifier.\nStand outside the field of view during the learning phase. If you\u0026rsquo;re in frame during learning, you become part of the background and the system will ignore you — exactly the wrong outcome.\nNew persistent objects (a box left on the floor, a chair moved into the workspace) gradually become background after 30–60 seconds of consistent returns. The voxels continuously decay if not refreshed, so objects that move back out of frame are eventually forgotten.\nThe motionless person problem This was the session\u0026rsquo;s main bug and it was completely non-obvious.\nEarly versions of the zone classifier used a velocity filter — points with near-zero doppler velocity were discarded as static clutter. The logic seemed sound: static clutter is the walls and floor, moving returns are potential hazards.\nThe problem: a person standing still in the workspace has near-zero doppler velocity. The velocity filter was silently discarding them. Walk into the zone, freeze — the arm would resume. Exactly backwards from safe behavior.\nThe fix was to remove the velocity filter from the zone classifier entirely and let the background model handle static clutter instead. The background model knows the difference between a wall (always there, every frame, consistent position) and a person standing still (novel voxel, wasn\u0026rsquo;t there before learning completed). The wall is background. The person is not.\nAfter removing the velocity filter:\nPerson walks in → CAUTION → STOP ✅ Person stops moving → stays STOP ✅ Person leaves → CLEAR ✅ The background model is doing the work the velocity filter was supposed to do, but correctly.\nFault architecture Two fault modes, both fail-safe:\nFault 1 — joint states timeout. If /joint_states stops publishing (arm controller crash, cable pulled, whatever), the safety node immediately publishes STOP and a fault reason to /dntd/safety_fault. The arm can\u0026rsquo;t resume until an explicit True is published to /dntd/safety_resume. Joint states recovering does not auto-resume — explicit operator acknowledgment required.\nFault 2 — safety node dies. The 5Hz heartbeat on /dntd/heartbeat stops. The arm controller monitors this and self-stops independently of whatever the safety node was doing. The safety stack failing safe is more important than the safety stack staying up.\nHysteresis Zone transitions without hysteresis produce oscillation at the boundary — CAUTION, STOP, CAUTION, STOP, three times a second, on a person standing exactly at the zone edge. Annoying and potentially damaging to the arm.\nTwo configurable parameters in dntd_mmwave_config.yaml:\nhysteresis_frames: 3 — number of consecutive frames required to upgrade to a more dangerous zone (CLEAR → CAUTION, CAUTION → STOP). Higher = less sensitive, less bouncy. clear_hysteresis_frames: 6 — frames required to downgrade to a safer zone. Intentionally asymmetric — the system is faster to escalate than to clear. These are starting values. Tune them during stick testing against the actual environment.\nWhat\u0026rsquo;s next The stack is ready for a real arm. The immediate next steps:\nFix the topic remap in dntd_mmwave_launch.py so it doesn\u0026rsquo;t need to be specified manually at startup Push everything to Gitea before the Jetson SD card has an opinion about it Order a cheap 6-DOF arm — the kinematic chain needs real DH parameters to validate ego-motion compensation properly Begin micro-doppler classifier research — person vs. object discrimination, TI range-doppler matrix dataset The demo video becomes possible once the arm is connected and the remap is fixed. That\u0026rsquo;s the next gate.\n","permalink":"https://dntddynamics.com/lab-notes/mmwave-full-safety-stack-live/","summary":"The IWR6843AOP is now running a complete ROS 2 safety pipeline on the Jetson — CLEAR/CAUTION/STOP zone detection with ego-motion compensation, voxel background learning, fault handling, and a heartbeat watchdog. One non-obvious bug dominated the session: velocity filtering was silently eating motionless people.","title":"Full safety stack live — background learning, ego-motion compensation, and the motionless person problem"},{"content":"Last updated: May 6, 2026\nDNTD Dynamics LLC (\u0026ldquo;DNTD Dynamics,\u0026rdquo; \u0026ldquo;we,\u0026rdquo; \u0026ldquo;us,\u0026rdquo; or \u0026ldquo;our\u0026rdquo;) operates dntddynamics.com. This Privacy Policy explains what information we collect, how we use it, and what rights you have regarding your information.\nWe are a small independent hardware company. We collect the minimum information necessary to process your order and communicate with you. We do not sell your data.\nInformation We Collect Information you provide directly:\nName and shipping address when placing an order Email address when placing an order or signing up for our mailing list Payment information — processed directly by Stripe, Inc. We never see or store your full card number Information collected automatically:\nBasic analytics data when you visit dntddynamics.com (pages visited, browser type, general location by country/region) This data is aggregated and not tied to individual identities Information we do not collect:\nWe do not collect sensitive personal information beyond what is required to fulfill your order We do not use tracking pixels, behavioral advertising, or third-party ad networks How We Use Your Information We use the information we collect to:\nProcess and fulfill your order Send order confirmations and shipping updates Respond to your questions and support requests Send product announcements and lab notes if you have subscribed to our mailing list Improve our website and products Payment Processing All payment processing is handled by Stripe, Inc. When you place an order, your payment information is transmitted directly to Stripe using industry-standard encryption. DNTD Dynamics does not store your credit card number, CVV, or full payment details on our servers.\nStripe\u0026rsquo;s privacy policy is available at stripe.com/privacy.\nEmail Communications If you subscribe to our mailing list, we use Klaviyo to manage email communications. You can unsubscribe at any time using the unsubscribe link included in every email. We will not send you marketing emails if you have not opted in.\nData Sharing We do not sell, rent, or trade your personal information to third parties.\nWe share information only with service providers necessary to operate our business:\nStripe — payment processing Klaviyo — email communications GitHub Pages / Cloudflare — website hosting and delivery These providers are contractually required to protect your information and may only use it to provide services to us.\nWe may disclose information if required by law, court order, or governmental authority.\nData Retention We retain order information for seven years as required for tax and accounting purposes. Email list data is retained until you unsubscribe. You may request deletion of your personal data at any time by emailing us at info@dntddynamics.com.\nYour Rights You have the right to:\nRequest a copy of the personal information we hold about you Request correction of inaccurate information Request deletion of your personal information (subject to legal retention requirements) Opt out of marketing communications at any time To exercise any of these rights, contact us at info@dntddynamics.com.\nCookies Our website uses minimal cookies necessary for basic site functionality. We do not use advertising cookies or cross-site tracking cookies.\nChildren\u0026rsquo;s Privacy Our products and website are not directed at children under 13. We do not knowingly collect personal information from children under 13.\nChanges to This Policy We may update this Privacy Policy from time to time. We will post the updated policy on this page with a revised date. Continued use of our website or services after changes constitutes acceptance of the updated policy.\nContact DNTD Dynamics LLC Sultan, Washington info@dntddynamics.com\n","permalink":"https://dntddynamics.com/privacy/","summary":"Last updated: May 6, 2026\nDNTD Dynamics LLC (\u0026ldquo;DNTD Dynamics,\u0026rdquo; \u0026ldquo;we,\u0026rdquo; \u0026ldquo;us,\u0026rdquo; or \u0026ldquo;our\u0026rdquo;) operates dntddynamics.com. This Privacy Policy explains what information we collect, how we use it, and what rights you have regarding your information.\nWe are a small independent hardware company. We collect the minimum information necessary to process your order and communicate with you. We do not sell your data.\nInformation We Collect Information you provide directly:","title":"Privacy Policy"},{"content":"Last updated: May 6, 2026\nWe want you to be satisfied with your purchase. This policy covers returns, replacements, and refunds for hardware kits purchased from DNTD Dynamics LLC.\nPre-Order Cancellations Pre-orders may be cancelled at any time before your order ships for a full refund. No questions asked. Email us at support@dntddynamics.com with your order number and we will process the refund within 5 business days.\nUnopened Kit Returns If you receive your kit and decide it is not right for your project, you may return it within 30 days of delivery under the following conditions:\nThe kit must be unopened — original packaging, sealed You must email support@dntddynamics.com with your order number to initiate the return You are responsible for return shipping costs Refund will be issued within 5 business days of receiving the returned item in original condition We inspect all returned items. If a returned item shows signs of having been opened, used, or damaged, we reserve the right to decline the refund or apply a restocking fee.\nDefective or Damaged Units (DOA) If your kit arrives defective or damaged, we will replace it at no cost to you.\nWhat qualifies:\nUnit does not power on or enumerate over USB on arrival Physical damage caused during shipping Missing components from the kit What to do:\nEmail support@dntddynamics.com within 14 days of delivery Include your order number and a description of the issue Include photos if there is visible physical damage We will send a replacement unit and a prepaid return label for the defective unit What does not qualify as DOA:\nConfiguration issues, software errors, or driver problems — these are support issues, not hardware defects, and we will help you resolve them Damage caused by improper handling, incorrect voltage, or user modifications Support Before Returning Before initiating a return for a technical issue, please email us. Many issues with mmWave EVMs are configuration or firmware related and can be resolved quickly. We have seen most of the common failure modes and can usually get you working within one or two email exchanges.\nEmail: support@dntddynamics.com\nRefund Processing Refunds are issued to the original payment method via Stripe. Processing time is 5 business days on our end. Your bank or card issuer may take an additional 3–10 business days to post the refund.\nContact DNTD Dynamics LLC Sultan, Washington support@dntddynamics.com\n","permalink":"https://dntddynamics.com/returns/","summary":"Last updated: May 6, 2026\nWe want you to be satisfied with your purchase. This policy covers returns, replacements, and refunds for hardware kits purchased from DNTD Dynamics LLC.\nPre-Order Cancellations Pre-orders may be cancelled at any time before your order ships for a full refund. No questions asked. Email us at support@dntddynamics.com with your order number and we will process the refund within 5 business days.\nUnopened Kit Returns If you receive your kit and decide it is not right for your project, you may return it within 30 days of delivery under the following conditions:","title":"Returns \u0026 Refunds"},{"content":"Last updated: May 6, 2026\nWhere We Ship We currently ship to the United States and Canada.\nWe do not ship internationally at this time. If you are outside the US or Canada and interested in our products, email us at support@dntddynamics.com — we are tracking international demand and will expand shipping regions in the future.\nShipping Costs United States: Shipping is included in the product price. No additional shipping charge at checkout.\nCanada: Shipping costs will be calculated and added at checkout. Canadian orders may also be subject to customs duties, import taxes, and brokerage fees upon delivery. These charges are the responsibility of the buyer and are not included in our pricing. We recommend checking with Canada Border Services Agency (CBSA) for estimated import costs before ordering.\nProcessing Time Pre-orders: We are currently accepting pre-orders with an estimated ship date of Q3 2026. Pre-orders will ship in the order they were received. We will email you with a tracking number when your order ships.\nIn-stock orders (future): Once we have inventory available, in-stock orders will be processed and shipped within 3–5 business days.\nCarriers We ship via USPS, UPS, or FedEx depending on package weight, destination, and availability. Carrier selection is at our discretion. All shipments include tracking.\nDelivery Estimates Once shipped, estimated transit times are:\nUS domestic: 3–7 business days Canada: 7–14 business days, plus customs processing time These are estimates from carriers and are not guaranteed. We are not responsible for carrier delays.\nTracking You will receive a shipping confirmation email with a tracking number when your order ships. If you do not receive tracking information within 2 business days of your expected ship date, email us at support@dntddynamics.com.\nLost or Stolen Packages If your tracking shows delivered but you have not received your package:\nCheck with neighbors and building management Wait 2 business days — packages are occasionally marked delivered early If still missing after 2 business days, email us at support@dntddynamics.com We will work with the carrier to investigate. We are not responsible for packages stolen after confirmed delivery, but we will do our best to help resolve the situation.\nAddress Accuracy Please ensure your shipping address is correct when placing your order. We ship to the address provided at checkout. If you need to change your shipping address after placing an order, email us immediately at support@dntddynamics.com. We cannot guarantee address changes can be made once an order has shipped.\nContact DNTD Dynamics LLC Sultan, Washington support@dntddynamics.com\n","permalink":"https://dntddynamics.com/shipping/","summary":"Last updated: May 6, 2026\nWhere We Ship We currently ship to the United States and Canada.\nWe do not ship internationally at this time. If you are outside the US or Canada and interested in our products, email us at support@dntddynamics.com — we are tracking international demand and will expand shipping regions in the future.\nShipping Costs United States: Shipping is included in the product price. No additional shipping charge at checkout.","title":"Shipping Policy"},{"content":"Last updated: May 6, 2026\nThese Terms of Service govern your use of dntddynamics.com and your purchase of products from DNTD Dynamics LLC (\u0026ldquo;DNTD Dynamics,\u0026rdquo; \u0026ldquo;we,\u0026rdquo; \u0026ldquo;us,\u0026rdquo; or \u0026ldquo;our\u0026rdquo;), a Washington State limited liability company. By using our website or placing an order, you agree to these terms.\nProducts Evaluation boards and development use only. All hardware products sold by DNTD Dynamics are evaluation boards intended for development and research use. They are not FCC-certified consumer products. Purchasers are responsible for ensuring their use complies with all applicable laws and regulations in their jurisdiction, including but not limited to FCC Part 15 regulations in the United States and equivalent regulations in other countries.\nProduct descriptions. We make reasonable efforts to accurately describe our products, including specifications, compatibility, and what is included. Specifications are subject to change as we improve our products. We will notify pre-order customers of any material changes before shipment.\nSoftware and documentation. ROS2 driver packages, configuration files, and documentation included with kits are provided as-is. We provide support via email and maintain a private repository for kit owners, but we do not guarantee specific software functionality beyond what is described at time of purchase.\nPre-Orders When you place a pre-order, you are reserving a unit from a future production batch.\nPayment is charged at the time of order, not at shipment Ship dates are estimates. We provide conservative estimates and communicate updates every two weeks via email Cancellations of pre-orders are accepted at any time before shipment for a full refund If we are unable to fulfill your pre-order for any reason, you will receive a full refund Pricing All prices are listed in US dollars. Prices are subject to change without notice, but the price you pay is locked at the time you place your order. We reserve the right to correct pricing errors.\nPayment We accept payment via credit and debit card through Stripe, Inc. By placing an order you agree to Stripe\u0026rsquo;s terms of service. All transactions are encrypted and processed securely.\nShipping We ship to the United States and Canada. Shipping costs are included in the listed product price for domestic US orders. Canadian orders may be subject to additional shipping charges, customs duties, and import taxes, which are the responsibility of the buyer.\nWe are not responsible for delays caused by carriers, customs, or events outside our control.\nLimitation of Liability TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, DNTD DYNAMICS LLC SHALL NOT BE LIABLE FOR ANY INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL, OR PUNITIVE DAMAGES, INCLUDING BUT NOT LIMITED TO LOSS OF PROFITS, DATA, OR GOODWILL, ARISING FROM YOUR USE OF OUR PRODUCTS OR SERVICES.\nOUR TOTAL LIABILITY FOR ANY CLAIM ARISING FROM OR RELATED TO THESE TERMS OR YOUR PURCHASE SHALL NOT EXCEED THE AMOUNT YOU PAID FOR THE PRODUCT IN QUESTION.\nThese products are evaluation boards for development use. You assume all responsibility for safe and legal deployment in your applications.\nIntellectual Property Software, documentation, configuration files, and written content provided with DNTD Dynamics products are the intellectual property of DNTD Dynamics LLC unless explicitly stated otherwise. Open-source components are subject to their respective licenses.\nLab notes and website content are copyright DNTD Dynamics LLC. You may share links and brief excerpts with attribution. Reproduction of full articles without permission is not permitted.\nGoverning Law These Terms of Service are governed by the laws of the State of Washington, United States, without regard to its conflict of law provisions. Any disputes shall be resolved in the courts of Snohomish County, Washington.\nChanges to These Terms We may update these Terms of Service from time to time. We will post the updated terms on this page with a revised date. Continued use of our website or services after changes constitutes acceptance of the updated terms.\nContact DNTD Dynamics LLC Sultan, Washington info@dntddynamics.com\n","permalink":"https://dntddynamics.com/terms/","summary":"Last updated: May 6, 2026\nThese Terms of Service govern your use of dntddynamics.com and your purchase of products from DNTD Dynamics LLC (\u0026ldquo;DNTD Dynamics,\u0026rdquo; \u0026ldquo;we,\u0026rdquo; \u0026ldquo;us,\u0026rdquo; or \u0026ldquo;our\u0026rdquo;), a Washington State limited liability company. By using our website or placing an order, you agree to these terms.\nProducts Evaluation boards and development use only. All hardware products sold by DNTD Dynamics are evaluation boards intended for development and research use.","title":"Terms of Service"},{"content":"Two days. Every config command returning Done. queryDemoStatus reporting Sensor State 1. Data port emitting exactly 16 bytes of 0xFF and nothing else.\nThe 16 bytes is an ackPing — the firmware\u0026rsquo;s way of saying \u0026ldquo;I heard you start the sensor.\u0026rdquo; It\u0026rsquo;s not TLV data. The sensor was never actually streaming.\nThe root cause xwr68xx_mmw_demo.bin is the ISK variant of the mmW demo firmware. ISK = Industrial Single-chip — a different board with a discrete linear antenna array. The IWR6843AOP has a completely different antenna geometry: a 3TX/4RX patch array in an L-configuration, all on-chip.\nWhen you flash ISK firmware onto an AOP board, the internal angle-of-arrival tables don\u0026rsquo;t match the physical antenna positions. The sensor boots, accepts config, starts — and then the DSP can\u0026rsquo;t reconcile the geometry, so the data port produces nothing useful.\nTI doesn\u0026rsquo;t make this obvious in the SDK directory structure. The xwr68xx/mmw/ folder contains ISK firmware. The AOP firmware lives in the OOB demo folder: xwr68xx/out_of_box_6843_aop/.\nThe fix Flash out_of_box_6843_aop.bin (401KB) via UniFlash in SOP2 mode. The ISK binary is 538KB — if you\u0026rsquo;re looking at that file, you have the wrong one.\nAfter reflashing and sending the corrected config:\nSensor State: 2 Data port baud rate: 921600 124,960 bytes captured in ~15 seconds. Magic word at byte 0:\n02 01 04 03 06 05 08 07 First decoded point: (+0.04, +0.61, +0.24) at 16.0 dB SNR — consistent with the desk edge ~60 cm in front of the sensor.\nWhat\u0026rsquo;s next The OOB and mmW demo firmwares accept the same CLI command set and emit TLV frames with the same magic word. For SDK 3.5.x, the OOB binary is the only one TI ships with AOP support — there is no AOP mmW demo variant.\nNext up: building uart_reader.py for continuous dual-UART reads, then wiring point clouds into zone_logic.py for live STOP / CAUTION / CLEAR output on the arm.\n","permalink":"https://dntddynamics.com/lab-notes/mmwave-tlv-live-wrong-firmware/","summary":"Two days chasing a sensor that accepted every config command, then emitted 16 bytes of 0xFF. Root cause: ISK firmware on AOP hardware. The fix was one file.","title":"TLV frames live on the Jetson — wrong firmware was the whole problem"},{"content":"All 12 boot-time calibrations passed. The debug output confirmed it:\nDebug: Init Calibration Status = 0x1ffe 0x1FFE means bits 1–12 are all set — every RF calibration succeeded. The sensor advanced to State 1. Then sensorStart returned Error -1 and the data port stayed silent.\nState 1 means configured but not running. The error was happening in the chirp engine, after calibration, before any data could flow.\nThe math I didn\u0026rsquo;t check The regulatory band for the IWR6843AOP is 60.0–64.0 GHz.\nMy profileCfg had:\nprofileCfg 0 60.75 30 7 57.14 0 0 70 1 256 5209 0 0 30 Bandwidth = freqSlopeConst × rampEndTime = 70 MHz/μs × 57.14 μs = 4.0 GHz.\nEnd frequency = 60.75 + 4.0 = 64.75 GHz.\nThat\u0026rsquo;s 750 MHz past the top of the band. The calibrations run at boot and don\u0026rsquo;t care — they test the RF chain independently of the chirp profile. But the chirp engine validates the sweep boundaries before starting, and it refused.\nNo error message explaining this. Just Error -1.\nThe fix Change startFreq from 60.75 to 60.0 so the chirp ends exactly at 64.0 GHz. Sensor advanced to State 2 in under a second.\nprofileCfg 0 60.0 30 7 57.14 0 0 70 1 256 5209 0 0 30 The check to run before every config change endFreq = startFreq + (freqSlopeConst_MHz_us * rampEndTime_us / 1000) For the IWR6843AOP: endFreq must be ≤ 64.0 GHz. If it\u0026rsquo;s over, sensorStart will silently fail with Error -1 every time, regardless of calibration status.\nThe TI E2E forums have multiple threads about this exact error. The answer is buried in replies. Saving you the search.\n","permalink":"https://dntddynamics.com/lab-notes/mmwave-sensorstart-error-chirp-out-of-band/","summary":"All 12 calibrations passed. Sensor State 1. Then nothing. The chirp engine silently refused to run because the end frequency exceeded the regulatory band by 750 MHz.","title":"sensorStart Error -1 — the chirp was running outside the 60–64 GHz band"},{"content":"The IWR6843AOPEVM arrived from DigiKey. $178.80. Smaller than expected — the antenna array is the whole top face of the board, which is the point.\nUSB detection — immediate Plugged into the Jetson Orin Nano Super. Two devices enumerated right away:\n$ lsusb | grep Silicon Bus 001 Device 004: ID 10c4:ea70 Silicon Labs CP2105 Dual UART Bridge $ ls /dev/ttyUSB* /dev/ttyUSB0 /dev/ttyUSB1 The CP2105 is a dual UART bridge — one chip, two serial ports. On the Jetson:\n/dev/ttyUSB0 — CLI port (115200 baud, send config commands here) /dev/ttyUSB1 — Data port (921600 baud, point cloud TLV frames come out here) User permissions: add yourself to the dialout group once, never think about it again:\nsudo usermod -a -G dialout $USER # log out and back in Switch configuration The EVM has three DIP switch banks. For normal operation (not flashing):\nSwitch Setting S1.1–S1.4 Off S2.1 Off S2.2–S2.4 On S3 On For flashing (SOP2 mode):\nSwitch Setting S1.1–S1.4 Off S2.1 On S2.2 On S2.3–S2.4 Off S3 On Always power-cycle after changing switches. The SOP mode is latched at boot.\nThe mmWave Studio trap mmWave Studio is TI\u0026rsquo;s GUI tool for the mmWave EVM family. It\u0026rsquo;s the obvious first thing to try. Don\u0026rsquo;t.\nmmWave Studio requires the MMWAVEICBOOST carrier board for full functionality at an additional cost. The BOOST board has FTDI GPIO lines that Studio uses to control the SOP pins and issue hardware resets in software. The IWR6843AOPEVM standalone board doesn\u0026rsquo;t have this, so Studio always fails at Calling_ConnectTarget.\nThere\u0026rsquo;s also a FTD2XX.dll dependency that isn\u0026rsquo;t included in the installer — you\u0026rsquo;ll spend time fixing that first, then hit the connection failure anyway.\nFor the standalone AOP EVM: use UniFlash for flashing, use Python serial for everything else. That\u0026rsquo;s it.\nWhat actually works UniFlash flashing the correct AOP firmware. Python pyserial for sending config and reading data. That combination works completely and doesn\u0026rsquo;t require any TI GUI software after the initial flash.\nNext post covers the two bugs that kept the sensor from streaming after the firmware was correct.\n","permalink":"https://dntddynamics.com/lab-notes/iwr6843aopevm-arrived-bringup-notes/","summary":"EVM in hand, USB detection working immediately. mmWave Studio looked like the obvious next step. It was a trap — the AOP standalone board can\u0026rsquo;t connect. UniFlash is the right tool.","title":"IWR6843AOPEVM arrived — bringup notes and the mmWave Studio trap"},{"content":"There\u0026rsquo;s a display project running in parallel with the mmWave work.\nIt involves optics, a light source, and a geometry that produces something we haven\u0026rsquo;t seen done quite this way before. Parts are in hand. First light has happened. It\u0026rsquo;s not ready to talk about in detail yet.\nWhen it is, it\u0026rsquo;ll be worth the wait.\n","permalink":"https://dntddynamics.com/lab-notes/lumina-something-is-taking-shape/","summary":"A display project. More when it\u0026rsquo;s further along.","title":"Project Lumina — something is taking shape"},{"content":"RadarGuard — mmWave Cobot Safety System Context \u0026amp; Session State Last updated: May 21, 2026 (Phase 7 complete, arm build in progress)\nProject Identity Product name: RadarGuard Repo name: RadarGuard-mmwave-cobot-safety-system GitHub org: DNTD-Dynamics GitHub URL: github.com/DNTD-Dynamics/RadarGuard-mmwave-cobot-safety-system Repo visibility: PUBLIC as of May 20, 2026 Company: DNTD Dynamics (Snohomish, Washington) Contact: contact@dntddynamics.com Website: dntddynamics.com (GitHub Pages + Cloudflare, repo separate from RadarGuard)\nLicense: Business Source License 1.1 (BSL 1.1)\nFree for non-commercial use (research, education, personal projects) Commercial use requires license from DNTD Dynamics Change date: 4 years from first public release → Apache 2.0 LICENSE file manually added (GitHub picker does not include BSL 1.1) Do NOT use \u0026ldquo;Boost Software License\u0026rdquo; — that is a different, unrelated license GitHub repo topics: mmwave, ros2, cobot-safety, robotics, ti-radar, iwr6843aop, iwrl6432aop, robot-safety, human-detection\nGit remotes (on Jetson):\ngithub → git@github.com:DNTD-Dynamics/RadarGuard-mmwave-cobot-safety-system.git gitea → http://192.168.254.117:3000/ShireFolk/dntd-mmwave.git (local backup) Push commands:\ngit push github main — public, always reachable git push gitea main — local backup, only when on home network Hardware IWR6843AOPEVM — PRIMARY (fully validated) Connection: Jetson Orin Nano Super via USB Ports: /dev/ttyUSB0 (CLI, 115200) · /dev/ttyUSB1 (Data, 921600) Firmware: out_of_box_6843_aop.bin — version 03.06.00.00 Config: ~/mmwave/configs/profile_AOP.cfg — validated, 10Hz, ±60° FOV Mounting: Vertical, heat shield (antenna face) pointing outward into workspace Switch config (normal): S1 all off, S2.1 off, S2.2/3/4 on, S3 on Switch config (flash): S1 all off, S2.1+S2.2 on, S2.3+S2.4 off, S3 on IWRL6432AOPEVM — IN PROGRESS (out of stock, 3 units on order) Battery-powered / mobile robot variant — primary product vision Identical TLV format to 6843 — same parser, same driver node Main new challenge: platform ego-motion (whole robot body moving) SDK: MMWAVE-L-SDK v1.4.0 (released Aug 2025) — recently stable Almost no open source work on this chip — wide open territory Out of stock due to demand outpacing supply — market signal NOTE: Udopproc (micro-doppler DPU) IS available for IWRL6432AOP via MMWAVE-L-SDK — not available for IWR6843. Future opportunity to use raw range-doppler heatmap on the 6432 for richer classification. Planned sensor array Industrial fixed arm (IWR6843AOP):\n3× IWR6843AOPEVM — mounted 120° apart on forearm link (between joint 3 and joint 4) for full 360° azimuth coverage of the distal workspace Mount point is the straight forearm link — stable orientation, doesn\u0026rsquo;t rotate on its own axis, flat mounting surface for clean sensor placement 4th sensor deliberately NOT added — introduces housekeeping MCU for signal arbitration, USB enumeration complexity, and additional failure modes with minimal safety gain. 3 sensors cover the geometry cleanly. Wrist close-range gap: Phase 9 problem, handled by mobile variant sensor Mobile robot (IWRL6432AOP):\n3× IWRL6432AOP — mobile/battery robot applications Separate product configuration — same core pipeline, different ego-motion No wrist sensor needed on mobile variant — 3x 6432AOP covers the geometry Dynamic self-model replaces static background map (whole robot body moves) Demo / Validation Arm — ToolBox Robotics EB300 Design: Open source 6-DOF collaborative arm Thingiverse: https://www.thingiverse.com/thing:6283770 ToolBox Robotics site has STEP files for exact link length measurements Print status: Printing now in PLA on two printers PLA acceptable for demo and validation — not production Joints near Nema 23s are highest stress — reprint in PETG/ABS for permanent build Aesthetic flanges skipped — get it moving first, pretty later Forearm link (sensor mount location) is low stress — PLA fine permanently Motors: Nema 17 (joints 1-3) + Nema 23 (joints 4-6) stepper motors Drivers: TB6600 stepper drivers (one per motor, up to 4A) Motion controller: ESP32 (on hand) Link lengths: To be measured after assembly — populate joint_geometry in dntd_mmwave_config.yaml from physical measurements with calipers IMPORTANT — joint geometry measurement guide (when assembled): For each joint, measure straight-line distance from center of that joint\u0026rsquo;s rotation axis to center of next joint\u0026rsquo;s rotation axis along each dimension. Record as origin_xyz for each joint in the YAML.\njoint1: base rotation → shoulder pivot Z height (vertical) joint2: shoulder pivot → elbow pivot X along upper arm joint3: elbow pivot → forearm sensor mount X along forearm joint4: sensor mount → wrist bend X remaining forearm joint5: wrist bend → wrist rotation Y short offset joint6: wrist rotation → end effector Y or Z short offset Bring measurements to next session — will translate directly to YAML.\nMotion Control Architecture (ESP32 → TB6600 → ROS 2) Why position feedback is needed: Swept-volume clipper and ego-motion compensator both call update(q) every frame with current joint angles. Without real position, q=zeros — system thinks arm is always at home, swept-volume geometry is wrong, ego-motion compensation is blind to actual motion.\nChosen approach: Stepper dead reckoning (no encoders for demo)\nSteppers don\u0026rsquo;t slip under normal load at demo speeds Count steps from known home position, convert to angles Homing routine on startup drives each joint to limit switch, zeros count Good enough for demo and early validation Add AS5600 magnetic encoders (I2C, ~$5-10 each) for production build Architecture:\nJetson (ROS 2) ↕ USB serial (same pattern as mmWave UART pipeline) ESP32 — Generates STEP/DIR pulses to 6× TB6600 — Tracks cumulative step count per motor — Homing routine on startup (limit switches) — Publishes joint angles at 10Hz over serial — Accepts velocity/position commands from Jetson ↕ STEP/DIR signals 6× TB6600 ↕ 6× Nema 17/23 Jetson ROS 2 bridge node (to be written):\nReads joint angles from ESP32 over serial Publishes to /joint_states — feeds safety node directly Forwards motion commands from ROS 2 to ESP32 ESP32 GPIO allocation (avoids strapping pins and ADC2/WiFi conflicts):\nMotor 1 (base) STEP: GPIO13 DIR: GPIO12 LIMIT: GPIO34 Motor 2 (shoulder) STEP: GPIO14 DIR: GPIO27 LIMIT: GPIO35 Motor 3 (elbow) STEP: GPIO26 DIR: GPIO25 LIMIT: GPIO32 Motor 4 (forearm) STEP: GPIO33 DIR: GPIO19 LIMIT: GPIO39 Motor 5 (wrist1) STEP: GPIO18 DIR: GPIO17 LIMIT: GPIO36 Motor 6 (wrist2) STEP: GPIO16 DIR: GPIO4 LIMIT: GPIO23 Outstanding questions before writing ESP32 firmware:\nLimit switches: on hand or ordering with motors this week? Confirm ESP32 board variant (DevKit v1, S3, etc.) for pin validation Future upgrade: AS5600 magnetic encoders\nOne per joint, mounted on motor shaft Absolute position, no homing needed I2C direct to ESP32 (~$5-10 each, ~$40 total for 6 joints) Feeds both safety system and future closed-loop control Natural Sterling integration via MQTT (same pattern as other robot work) Compute Platforms Primary: Jetson Orin Nano Super (JetPack 6.2.2, Ubuntu 22.04 jammy) IP: 192.168.254.117 (local) · 100.67.44.69 (Tailscale) Compatible: Raspberry Pi 5 (Ubuntu 22.04 jammy) Handles 1–3 sensors with current zone logic + background model Limit: micro-doppler classifier + 3-sensor fusion at 10Hz → needs Jetson Any Ubuntu 22.04 ARM/x86 — no Jetson-specific dependencies in stack Software Stack ROS 2 Distro: Humble (jammy) IMPORTANT: JetPack reports Ubuntu codename as jammy not noble Jazzy requires noble — always use Humble on this hardware Source: added to ~/.bashrc — auto-sourced on every login Packages: ros-humble-ros-base, ros-humble-rosbridge-suite, ros-humble-sensor-msgs, ros-humble-sensor-msgs-py SSH / GitHub Jetson SSH key added to github.com (nicd85 account / DNTD-Dynamics org) ssh -T git@github.com confirms authentication working Use SSH remote (git@github.com:...) not HTTPS for push Local config override pattern dntd_mmwave_config.yaml — committed, safe for public GitHub dntd_mmwave_config.local.yaml — gitignored, Jetson-only overrides Contains: output_mqtt_broker: \u0026quot;192.168.254.117\u0026quot; Pattern: configs/*.local.yaml in .gitignore Run command includes both --params-file flags in order (local wins) File Structure ~/mmwave/ ├── configs/ │ ├── profile_AOP.cfg ✅ Validated chirp config │ ├── dntd_mmwave_config.yaml ✅ Safety node parameters (public) │ ├── dntd_mmwave_config.local.yaml 🔒 Local overrides (gitignored) │ ├── dntd_mmwave_driver_config.yaml ✅ Driver node parameters │ └── background_map.npz 🔒 Persistent background map (gitignored) ├── logs/ │ └── classifier_training.csv 🔒 Auto-logged OBJECT suppressions │ (gitignored — training data) ├── src/ │ ├── tlv_parser.py ✅ TLV frame decoder (original) │ ├── uart_reader.py ✅ MmwaveReader UART reader (original) │ ├── zone_logic.py ✅ Zone classifier + ZoneOutputs │ ├── background_model.py ✅ Voxel background + persistence │ ├── cluster.py ✅ DBSCAN cluster builder + features │ ├── classifier.py ✅ Micro-doppler rule-based classifier │ ├── swept_volume.py ✅ Swept-volume workspace clipper │ ├── dntd_mmwave_driver_node.py ✅ UART → PointCloud2 ROS 2 node │ ├── dntd_mmwave_safety_node.py ✅ Full pipeline ROS 2 node │ ├── dntd_mmwave_launch.py ✅ Multi-sensor launch file │ ├── fake_joint_states.py ✅ Stick test / no-arm helper │ ├── arm_controller_node.py 🔧 ESP32 → /joint_states bridge (TODO) │ ├── live_angle.py ✅ FOV validation tool (original) │ └── main.py ✅ Standalone runner (no ROS 2) ├── context.md ← this file ├── README.md ✅ Public-facing docs └── LICENSE ✅ BSL 1.1 Files removed (no longer needed) uart_reader_adapter.py — deleted, driver uses MmwaveReader directly tlv_parser_adapter.py — deleted, no longer in pipeline Architecture Full Pipeline (as of Phase 7) 3× IWR6843AOP UART (forearm mount, 120° spacing) ↓ dntd_mmwave_driver_node.py (one per sensor, separate namespace) MmwaveReader (uart_reader.py) → Frame objects → PointCloud2 Publishes: /mmwave/raw_points, /mmwave/diagnostics ↓ [topic remap: /dntd/mmwave/raw_points → /mmwave/raw_points] ↓ dntd_mmwave_safety_node.py ← /joint_states (fake_joint_states.py during stick test) → Ego-motion compensation (Jacobian × q_dot) → Background model (observe → filter_novel) — Persistent map: loads from disk on boot, skips 15s STOP — Novelty-aware refresh: stationary people never absorbed → Cluster builder (DBSCAN → Cluster objects with features) → Micro-doppler classifier (PERSON / OBJECT / UNKNOWN) — Fail-safe: UNKNOWN passes through as PERSON — OBJECT suppressed + logged to classifier_training.csv → Swept-volume workspace clip — Mount point: world-frame position of joint 3 (forearm link) — Self-exclusion sphere: arm body returns suppressed — Max reach sphere: auto-computed from distal link lengths — Fail-safe: placeholder chain or disabled → all points pass → Zone classification (CLEAR / CAUTION / STOP) Publishes: /dntd/safety_zone, /dntd/safety_fault, /dntd/heartbeat, /dntd/compensated_points Subscribes: /dntd/safety_resume, /dntd/relearn_background Downstream: Serial / GPIO / MQTT (ZoneOutputs) Future Pipeline (Phase 8+) Novel points → cluster → classifier → swept-volume clip ↓ Occlusion mask (blind spots behind links → fail-safe CAUTION) ← Future ↓ 3-sensor fusion (union of 3 point clouds, coord-aligned) ← Phase 8 ↓ Zone decision Topic Map Topic Direction Type Notes /mmwave/raw_points driver → safety PointCloud2 raw sensor frames ~19Hz (measured; chirp config is 10Hz, driver publishes per-TLV) /mmwave/diagnostics driver → DiagnosticStatus sensor health /joint_states arm/fake → safety JointState ego-motion input /dntd/safety_zone safety → String CLEAR/CAUTION/STOP /dntd/safety_fault safety → String fault reason or empty /dntd/heartbeat safety → Header 5Hz watchdog pulse /dntd/compensated_points safety → PointCloud2 world-frame cloud /dntd/safety_resume → safety Bool clear fault, resume arm /dntd/relearn_background → safety Bool retrigger background learning Current Status Validated ✅ IWR6843AOP hardware — firmware, config, UART, TLV parsing Full ROS 2 Humble pipeline end-to-end — confirmed live zone transitions CLEAR → CAUTION → STOP tracking person moving through space Background learning — 15s startup, walls and mount masked out Motionless person detection — holds zone when movement stops Fast approach detection — STOP on stumble/fall velocity from caution zone Fault handling — joint_states timeout → STOP → explicit resume required Heartbeat watchdog — 5Hz, arm controller can self-stop if node dies Serial / GPIO / MQTT outputs (ZoneOutputs — all simultaneous) Configurable hysteresis via YAML README.md written and committed LICENSE (BSL 1.1) added and committed Repo public at github.com/DNTD-Dynamics/RadarGuard-mmwave-cobot-safety-system Phase 5 Complete ✅ Persistent background map — voxel grid saved to ~/mmwave/configs/background_map.npz after learning, reloaded on boot. Atomic write (tmp + rename). Version + voxel_size validated on load. Skips 15s STOP every boot after first learn. Novelty-aware refresh gate — novel voxels (people) are never refreshed into the background score. A stationary person stays detected indefinitely and is never absorbed into the background. Local config override — dntd_mmwave_config.local.yaml gitignored, loaded as second --params-file (overrides main config). No secrets in committed files. Driver config path fix — removed hardcoded /home/nic/ path, replaced with ~/mmwave/configs/ for portability. Phase 6 Complete ✅ cluster.py — DBSCAN cluster builder. Groups novel DetectedPoints into Cluster objects with features: centroid, range, point_count, velocity_spread, height_span, doppler_asymmetry, snr_mean, temporal_variance (centroid movement history across frames). Persistent cluster IDs across frames via centroid matching. classifier.py — Rule-based micro-doppler classifier. Labels each cluster PERSON / OBJECT / UNKNOWN. Fail-safe: UNKNOWN passes through. Soft voting (4 feature votes): velocity_spread, height_span, point_count (gated by corroborating features), doppler_asymmetry. All thresholds YAML-tunable. Automatically logs suppressed OBJECT detections to ~/mmwave/logs/classifier_training.csv for future ML training data collection. dntd_mmwave_safety_node.py — updated to wire cluster builder and classifier into the pipeline. classifier_enabled: false in YAML restores original behavior instantly. dntd_mmwave_config.yaml — classifier parameters added with full tuning guide comments. Phase 7 Complete ✅ swept_volume.py — SweptVolumeClipper computes reachable workspace from sensor mount joint (joint 3, forearm link) each frame. Defines: Mount position: world-frame origin of mount joint at current q Self-exclusion sphere: suppresses arm body returns (default 0.15m) Max reach sphere: auto-computed from distal link lengths (joints 4→6 end effector + sensor mount offset) or YAML override Reach margin: 0.20m safety buffer added to max reach Fail-safe: placeholder chain detected and bypassed automatically Fail-safe: if all points suppressed, originals pass through with warning swept_volume_enabled: false bypasses entirely dntd_mmwave_safety_node.py — SweptVolumeClipper wired in after classifier, before zone logic. Startup log prints computed max reach. dntd_mmwave_config.yaml — swept-volume parameters added with full tuning guide. swept_volume_mount_joint_idx: 3 for forearm mount. Configuration-space awareness — clipper updates with q every frame, so envelope tracks arm motion (person reachable at full extension vs. not reachable behind the base, automatically). Hardware configuration locked:\nIndustrial fixed arm: 3× IWR6843AOP, 120° spacing, forearm link mount Mobile robot: 3× IWRL6432AOP (Phase 9) — separate product configuration No 4th sensor on industrial arm — housekeeping MCU overhead not justified Known Issues / Outstanding Work Zone boundary oscillation — tune hysteresis_frames and clear_hysteresis_frames in YAML. Relearn from outside FOV first. Classifier thresholds need real-world tuning — defaults are conservative (fail-safe). Run with real arm + real workspace and review classifier_training.csv to dial in. Running the Stack Every Session (4 terminals) # Terminal 1 cd ~/mmwave/src \u0026amp;\u0026amp; python3 fake_joint_states.py # Terminal 2 cd ~/mmwave/src \u0026amp;\u0026amp; python3 dntd_mmwave_driver_node.py # Terminal 3 cd ~/mmwave/src \u0026amp;\u0026amp; python3 dntd_mmwave_safety_node.py \\ --ros-args \\ --params-file ~/mmwave/configs/dntd_mmwave_config.yaml \\ --params-file ~/mmwave/configs/dntd_mmwave_config.local.yaml \\ -r /dntd/mmwave/raw_points:=/mmwave/raw_points # Terminal 4 ros2 topic echo /dntd/safety_zone After First Boot (background map not yet saved) # Clear initial joint_states fault ros2 topic pub --once /dntd/safety_resume std_msgs/Bool \u0026#34;data: true\u0026#34; # Trigger clean relearn (stand outside FOV first) ros2 topic pub --once /dntd/relearn_background std_msgs/Bool \u0026#34;data: true\u0026#34; # Map saves automatically after 15s — subsequent boots skip this After Workspace Change (force fresh learn) ros2 topic pub --once /dntd/relearn_background std_msgs/Bool \u0026#34;data: true\u0026#34; # Deletes saved map, relearns, saves new map Useful Diagnostics ros2 topic list ros2 topic hz /mmwave/raw_points # expect ~19Hz ros2 topic hz /dntd/safety_zone # expect ~10Hz ros2 topic echo /mmwave/diagnostics tail -f ~/mmwave/logs/classifier_training.csv # watch suppressions live Key YAML Parameters dntd_mmwave_config.yaml (safety node) Parameter Default Notes sensor_mount_link \u0026ldquo;tool0\u0026rdquo; URDF link sensor attaches to stop_range_m 0.5 Hard stop radius caution_range_m 1.2 Slow-down radius fast_approach_mps -0.8 Emergency stop velocity min_snr_db 8.0 Detection quality threshold background_learning_s 15.0 Scene learning duration background_voxel_size_m 0.10 10cm voxel resolution background_hit_threshold 0.30 Fraction of frames → background hysteresis_frames 3 Frames to confirm zone upgrade clear_hysteresis_frames 6 Frames to confirm zone downgrade classifier_enabled true Set false to bypass classifier classifier_eps_m 0.40 DBSCAN cluster radius classifier_velocity_spread_min 0.08 Person vel spread threshold classifier_height_span_min 0.10 Person height threshold classifier_score_threshold 2 Votes needed for PERSON (1–4) classifier_log_enabled true Log suppressed objects to CSV swept_volume_enabled true Bypass with false swept_volume_mount_joint_idx 3 Forearm link mount swept_volume_self_radius_m 0.15 Arm body self-exclusion swept_volume_reach_margin_m 0.20 Safety buffer on max reach output_mqtt_broker \u0026quot;\u0026quot; Set in .local.yaml — never commit IP Future Work — Detailed Notes Phase 6b — ML Classifier (Drop-in upgrade to Phase 6 rule-based) Design intent: Drop-in replacement for the rule-based classifier in classifier.py. Same Cluster feature vector input, same PERSON/OBJECT/UNKNOWN output. No pipeline changes required.\nTraining data: classifier_training.csv auto-logs every suppressed OBJECT detection with full feature vector. Every hour of operation with a real arm builds labeled training data automatically.\nRecommended dataset: RadIOCD — 76,821 samples, sparse point cloud from mmWave radar, 5 classes (backpack, chair, desk, human, wall), 3 environments. Published Aug 2024. Use for initial model training, fine-tune on workspace-specific data from classifier_training.csv. URL: https://www.nature.com/articles/s41597-024-03678-2\nModel target: sklearn RandomForest or lightweight tflite model. Must run at 10Hz on Jetson AND Raspberry Pi 5. No GPU inference. Target: \u0026lt;5ms inference per frame.\nCommercial opportunity: Pre-trained models as paid optional add-on. Rule-based (free, ships to everyone) + ML upgrade (paid, for those without time/resources to train). Natural tiered offering under BSL.\nIWRL6432AOP opportunity: Udopproc DPU available in MMWAVE-L-SDK for IWRL6432AOP (not available for IWR6843). Raw range-doppler heatmap accessible on the 6432 → richer micro-doppler features possible → better classifier accuracy. Design Phase 6b to support both feature vectors (point cloud features for 6843, heatmap + point cloud for 6432).\nPhase 8 — 3-Sensor 360° Fusion 3× IWR6843AOPEVM arriving — coverage gaps closed Each sensor driver node in separate namespace (/dntd/sensor_0/1/2) Safety node subscribes to all three raw_points topics Fusion: union of novel point clouds from all sensors Cluster builder handles merged cloud (DBSCAN naturally handles it) Key challenge: coordinate frame alignment between sensor mounts Phase 9 — IWRL6432AOP Pipeline Same TLV format → same driver node, same parser New challenge: whole-platform ego-motion (robot body moving, not just sensor on arm) Dynamic background model replaces static learned map Udopproc DPU available → raw range-doppler heatmap accessible Almost no open-source work on this chip — first-mover territory Custom PCB target: IWRL6432AOP, power-optimized, robot flange mount Phase — Heartbeat Detection During STOP Status: Unblocked (Phase 7 swept-volume work complete). Not yet started.\nConcept: When a STOP zone is triggered and the arm halts, shift to a heartbeat/vital-signs detection mode to maintain high-confidence presence detection even when the person is completely stationary. Holds the STOP until a clear is given rather than risking false CLEAR from a motionless person.\nArchitecture:\nHeartbeat detection triggers on STOP zone entry Separate chirp config required (slower chirp for vital signs — different from current 10Hz motion config) Uses the vital signs TLV output from IWR6843AOP firmware Detection pipeline shifts: motion classification → presence confirmation via micro-motion (breathing: 2–5mm chest displacement, range ~1–2m) STOP held until both zone = CLEAR AND heartbeat signal absent On CLEAR from heartbeat mode, explicit resume still required Implementation notes:\nRequires runtime firmware config switch or dual-mode config file Short range limitation (~1–2m) — complements rather than replaces zone-based detection at longer range Natural integration: heartbeat plugin in Sterling for ambient presence awareness (separate use case from safety) Phase — Payload / Tool Extension Awareness Problem: Arm holding a 2m pipe has effective reach of arm_reach + 2m. Current swept-volume calculation would underestimate danger zone.\nApproach:\npayload_length_m and payload_direction parameters in YAML Operator declares payload before operation starts Kinematic envelope expands by payload vector Eventually: wrist-mounted sensor (already planned) auto-detects payload geometry from returns near end effector Mobile robot version (Phase 9+):\nRobot body + payload must be excluded from detections Dynamic self-model replaces static background map (robot is moving) IWRL6432AOP on mobile robot — ego-motion problem extends to full body Phase — Occlusion Awareness Problem: Sensor has blind spots behind arm links. System should know when a zone is occluded and fail-safe to CAUTION rather than CLEAR.\nApproach:\nRay-cast from sensor origin through arm link geometry Voxels behind occluding links marked as UNKNOWN (not CLEAR) Zone logic treats UNKNOWN voxels as CAUTION-level Pairs naturally with swept-volume work (same geometry model) Phase — Asymmetric Zone Shapes Concept: STOP/CAUTION zones currently uniform spheres around mount. Real arm reachability is direction-dependent — full extension forward vs. blocked by base behind. Shape zones to actual reach envelope (builds on swept-volume foundation).\nMarket Context Competitive Landscape Algorized + KUKA (announced CES Jan 2026) — IWR6843AOP, enterprise pricing, closed source, SIL2-certified. Validates the market entirely. DNTD position: open-source, accessible, plug-and-play alternative. TI reference designs — raw demos only, no safety logic, no ego-motion GitHub mmwave ecosystem — home automation, academic code, one ROS 1 Melodic Docker wrapper. Nothing deployable for arm safety. IWRL6432AOP — one general PCB project (0 stars). No pipeline, no safety. Why This Is Novel Ego-motion compensation via URDF Jacobian for arm-mounted mmWave — not published Background learning + motionless person detection — not published Novelty-aware refresh gate — stationary people never absorbed — not published Persistent background map with atomic save/load — not available DBSCAN cluster + micro-doppler rule-based classifier — not available Auto-logging training data pipeline for ML upgrade — not available Hardware-agnostic outputs (serial/GPIO/MQTT + ROS 2) — not available YAML-driven config for any arm — not available IWRL6432AOP pipeline — essentially no competition exists yet Swept-volume workspace clipping from arm mount point — no fixed-mount sensor can do this Automatic distal reach computation from kinematic chain — YAML override supported Two distinct product configs: 3× IWR6843AOP industrial, 3× IWRL6432AOP mobile Window IWR6843AOP: 6–12 months before Algorized wave creates more competition IWRL6432AOP: 12–24 months, chip only became stable Aug 2025 Build Roadmap ✅ Phase 1 — Hardware validation (IWR6843AOP, TLV, FOV) ✅ Phase 2 — ROS 2 pipeline (driver, safety node, ego-motion architecture) ✅ Phase 3 — Background learning, motionless detection, YAML config ✅ Phase 4 — README, BSL 1.1 license, GitHub push (repo now PUBLIC) ✅ Phase 5 — Persistent background map, novelty-aware refresh, local config ✅ Phase 6 — Micro-doppler classifier (rule-based, fail-safe, training logger) ✅ Phase 7 — Swept-volume workspace clipper (forearm mount, 3× IWR6843AOP) ── Phase 6b — ML classifier (drop-in upgrade, RadIOCD + workspace data) ── Phase 8 — 3-sensor 360° fusion (when EVMs arrive) ── Phase 9 — IWRL6432AOP pipeline (when back in stock) ── Phase 10 — Custom PCB (IWRL6432AOP, power-optimized, robot flange mount) ── Phase 11 — FCC Part 15.255 + ISO 13849 functional safety path ── Future — Heartbeat detection during STOP zone (unblocked) ── Future — Payload/tool extension awareness ── Future — Occlusion awareness (blind spots → fail-safe CAUTION) ── Future — Asymmetric zone shapes (arm-reach-direction aware) Immediate Next Session Priorities Unblocked right now (no arm needed):\nAdd logs/ to .gitignore so training CSV never hits GitHub (30s fix) Confirm limit switch situation — on hand or ordering with motors? Confirm ESP32 board variant (DevKit v1, S3, etc.) for GPIO pin validation Write ESP32 firmware — step/dir pulse gen, homing, serial joint state publisher Update fake_joint_states.py with realistic EB300 joint angle ranges for testing Order 2× more IWR6843AOPEVMs for Phase 8 — long lead time, get in pipeline now Needs assembled arm: 7. Measure all joint link lengths with calipers — bring to next session for YAML update 8. Write arm_controller_node.py — serial → /joint_states ROS 2 bridge against real ESP32 9. Verify Phase 7 swept-volume max reach computation vs. physical arm 10. Run stack with swept_volume_enabled: false first — verify classifier behavior 11. Enable swept volume, tune swept_volume_self_radius_m to match arm link diameter 12. Tune classifier_score_threshold and classifier_eps_m from real data\nDemo prep (after arm moves): 13. Shoot zone transition video — person walks in, CAUTION, STOP, arm halts 14. Add GIF/screenshot to README 15. Draft Hackaday + Reddit post (two-part series: current state + 3-sensor follow-on)\nRegulatory / Commercial Notes FCC Part 15.255 required before US commercial hardware sales (~$5–8k, 6–12 weeks) TIDEP-01033 reference design BOM → reuse TI test data for FCC IEC 62061 / ISO 13849 for industrial channel sales Export control: EAR ECCN for international hardware sales BSL license enables commercial licensing revenue before hardware certification Pre-commercial: evaluation board + software licensing OK now Commercial tiering opportunity: rule-based classifier (free/BSL) + pre-trained ML model (paid license) + workspace-tuned model (professional services) ","permalink":"https://dntddynamics.com/lab-notes/mmwave_phase7done_cleaned_context/","summary":"RadarGuard — mmWave Cobot Safety System Context \u0026amp; Session State Last updated: May 21, 2026 (Phase 7 complete, arm build in progress)\nProject Identity Product name: RadarGuard Repo name: RadarGuard-mmwave-cobot-safety-system GitHub org: DNTD-Dynamics GitHub URL: github.com/DNTD-Dynamics/RadarGuard-mmwave-cobot-safety-system Repo visibility: PUBLIC as of May 20, 2026 Company: DNTD Dynamics (Snohomish, Washington) Contact: contact@dntddynamics.com Website: dntddynamics.com (GitHub Pages + Cloudflare, repo separate from RadarGuard)\nLicense: Business Source License 1.1 (BSL 1.1)\nFree for non-commercial use (research, education, personal projects) Commercial use requires license from DNTD Dynamics Change date: 4 years from first public release → Apache 2.","title":""},{"content":"RadarGuard — mmWave Cobot Safety System Real-time human presence detection and collision avoidance for robot arms and mobile robots, powered by mmWave radar.\nBuilt by DNTD Dynamics · Licensed under BSL 1.1\nWhat is this? RadarGuard is an open-source safety system that uses mmWave radar to detect people in a robot\u0026rsquo;s workspace and output CLEAR / CAUTION / STOP zone commands in real time.\nUnlike camera-based safety systems, mmWave radar:\nWorks in complete darkness, dust, smoke, and welding flash Detects stationary people — not just movement Carries no PII — a radar return is not a face Runs at 10Hz with sub-100ms zone transition latency Unlike fixed-mount safety scanners, RadarGuard mounts on the arm itself and understands the arm\u0026rsquo;s reachable workspace via forward kinematics — so it only triggers on detections the arm can actually reach.\nRadarGuard is designed to be plug-and-play with any robot arm. Drop in your URDF, set your zone distances in a YAML file, and run. No code changes required.\nHardware Requirements Component Part Source mmWave sensor Texas Instruments IWR6843AOPEVM DigiKey / Mouser (~$179) Compute Jetson Orin Nano / NX, Raspberry Pi 5, or any Ubuntu 22.04 ARM/x86 board — Cable USB-A to USB-B (standard) — Single-sensor configurations work out of the box. The full industrial configuration is 3× IWR6843AOPEVM mounted 120° apart on the forearm link for 360° azimuth coverage — see Roadmap (Phase 8).\nComing soon: IWRL6432AOP support for battery-powered mobile robots and humanoids.\nHow it works IWR6843AOP sensor (UART, forearm-mounted) ↓ Driver node — decodes TLV frames → PointCloud2 ↓ Safety node ├── Ego-motion compensation (Jacobian × q_dot from /joint_states) ├── Background learning + persistent map (walls, fixtures masked) ├── DBSCAN cluster builder ├── Micro-doppler classifier (PERSON / OBJECT / UNKNOWN, fail-safe) ├── Swept-volume workspace clip (kinematic reachability) └── Zone classification + hysteresis ↓ CLEAR / CAUTION / STOP ├── ROS 2 topic (/dntd/safety_zone) ├── Serial UART (Arduino, any microcontroller) ├── GPIO pins (Raspberry Pi) └── MQTT (home automation, custom integrations) Ego-motion compensation — the sensor mounts on the moving arm itself. RadarGuard reads /joint_states from your ROS 2 controller and subtracts the arm\u0026rsquo;s own velocity from every radar return, so only real-world motion triggers zone changes.\nBackground learning — on startup, RadarGuard learns the static environment (walls, fixtures, the arm mount). After learning, only novel objects trigger responses. A person standing still in the danger zone holds CAUTION — they don\u0026rsquo;t disappear when they stop moving. The learned map is saved to disk and reloaded on subsequent boots, so the 15-second learning phase only runs once per workspace.\nMicro-doppler classifier — DBSCAN clusters novel returns and a rule-based classifier labels each cluster PERSON / OBJECT / UNKNOWN using velocity spread, height span, point count, and doppler asymmetry. UNKNOWN passes through as PERSON (fail-safe). Suppressed OBJECT detections are auto-logged for future ML training. Drop-in ML upgrade planned.\nSwept-volume workspace clip — RadarGuard computes the arm\u0026rsquo;s reachable envelope from the kinematic chain every frame. Detections outside the arm\u0026rsquo;s reach are suppressed — a person 3m away in front of a 1m arm doesn\u0026rsquo;t trigger STOP. As the arm moves, the envelope updates with it. This is the primary differentiator over fixed-mount safety scanners.\nHardware-agnostic outputs — your arm controller doesn\u0026rsquo;t need to speak ROS 2. Serial, GPIO, and MQTT outputs work simultaneously so any downstream controller can consume the safety signal.\nQuickstart 1. Hardware setup Mount the IWR6843AOPEVM with the heat shield (antenna face) pointing outward into the workspace. Connect via USB.\nVerify detection:\nls /dev/ttyUSB* # Should show /dev/ttyUSB0 (CLI) and /dev/ttyUSB1 (data) 2. Install dependencies # ROS 2 Humble (Ubuntu 22.04) sudo apt install ros-humble-ros-base ros-humble-rosbridge-suite \\ ros-humble-sensor-msgs ros-humble-sensor-msgs-py echo \u0026#34;source /opt/ros/humble/setup.bash\u0026#34; \u0026gt;\u0026gt; ~/.bashrc source ~/.bashrc # Python dependencies pip3 install pyserial numpy 3. Clone and configure git clone https://github.com/DNTD-Dynamics/RadarGuard-mmwave-cobot-safety-system.git cd RadarGuard-mmwave-cobot-safety-system Edit configs/dntd_mmwave_config.yaml:\n# Set your URDF link name for the sensor mount sensor_mount_link: \u0026#34;tool0\u0026#34; # UR5/UR10 default # Tune zone distances for your arm stop_range_m: 0.5 # hard stop radius caution_range_m: 1.2 # slow-down radius # Background learning duration (seconds) # Longer = more thorough static environment mask background_learning_s: 15.0 # Swept-volume clipper (set false to disable kinematic clipping) swept_volume_enabled: true swept_volume_mount_joint_idx: 3 # forearm link mount 4. Run # Terminal 1 — sensor driver cd src \u0026amp;\u0026amp; python3 dntd_mmwave_driver_node.py # Terminal 2 — safety node cd src \u0026amp;\u0026amp; python3 dntd_mmwave_safety_node.py \\ --ros-args \\ --params-file ../configs/dntd_mmwave_config.yaml \\ -r /dntd/mmwave/raw_points:=/mmwave/raw_points # Terminal 3 — watch zone output ros2 topic echo /dntd/safety_zone Stand clear during the 15-second background learning phase. After learning completes, walk toward the sensor — you will see CLEAR → CAUTION → STOP transitions.\nSend resume after any fault:\nros2 topic pub --once /dntd/safety_resume std_msgs/Bool \u0026#34;data: true\u0026#34; Force a fresh background relearn (after moving the arm or rearranging the workspace):\nros2 topic pub --once /dntd/relearn_background std_msgs/Bool \u0026#34;data: true\u0026#34; Adapting to your arm All arm-specific geometry lives in configs/dntd_mmwave_config.yaml. No Python changes required.\nStep 1 — Set sensor_mount_link to the URDF link the sensor is attached to:\nsensor_mount_link: \u0026#34;tool0\u0026#34; # UR5, UR10 sensor_mount_link: \u0026#34;link6\u0026#34; # xArm6 sensor_mount_link: \u0026#34;torso_link\u0026#34; # humanoid chest mount Step 2 — Set sensor_mount_xyz and sensor_mount_rpy to the physical offset from that link to the sensor face (measure with calipers or read from CAD).\nStep 3 — Copy your joint names and geometry into the joint_geometry block. Values come directly from your URDF \u0026lt;joint\u0026gt; elements. The swept-volume clipper uses this to compute reachability — if it sees a placeholder chain, it bypasses automatically (fail-safe).\nStep 4 — Tune stop_range_m and caution_range_m to match your arm\u0026rsquo;s reach envelope and maximum speed.\nStep 5 — Set swept_volume_mount_joint_idx to the index of the joint your sensor mounts on (3 = forearm link for a 6-DOF arm). The max reach sphere is auto-computed from distal link lengths, or override with swept_volume_max_reach_m.\nKey parameters Parameter Default Description stop_range_m 0.5 m Hard stop — anything inside this range triggers immediate STOP caution_range_m 1.2 m Slow down — anything inside this range triggers CAUTION fast_approach_mps -0.8 m/s Emergency stop velocity threshold — catches stumbles and falls background_learning_s 15 s Scene learning duration. Increase for cluttered environments hysteresis_frames 3 Frames to confirm zone upgrade. Increase to reduce boundary oscillation clear_hysteresis_frames 6 Frames to confirm zone downgrade. Higher = safer resume after STOP min_snr_db 8.0 dB Minimum SNR for valid detection classifier_enabled true Set false to bypass micro-doppler classifier classifier_score_threshold 2 Votes (1–4) needed to label a cluster PERSON swept_volume_enabled true Set false to disable kinematic workspace clipping swept_volume_self_radius_m 0.15 m Arm body self-exclusion sphere swept_volume_reach_margin_m 0.20 m Safety buffer added to computed max reach ROS 2 topics Topic Type Description /dntd/safety_zone String CLEAR / CAUTION / STOP /dntd/safety_fault String Fault reason, empty when healthy /dntd/heartbeat Header 5Hz watchdog pulse — subscribe in your controller /dntd/compensated_points PointCloud2 Ego-motion compensated world-frame point cloud /dntd/safety_resume Bool Send true to resume after fault /dntd/relearn_background Bool Send true to retrigger background learning /mmwave/raw_points PointCloud2 Raw driver output (~19Hz) /mmwave/diagnostics DiagnosticStatus Sensor health Supported hardware Sensor Status Notes IWR6843AOPEVM ✅ Validated Primary development platform IWRL6432AOPEVM 🔲 In progress Battery-powered / mobile robot variant (Phase 9) Compute platform Status Jetson Orin Nano Super (JetPack 6.2.2) ✅ Validated Raspberry Pi 5 (Ubuntu 22.04) ✅ Compatible (1–3 sensors with current pipeline) Any Ubuntu 22.04 ARM/x86 ✅ Compatible Fault handling RadarGuard fails safe. If /joint_states stops publishing (arm controller crash, E-stop, cable fault), the system immediately publishes STOP and raises a fault on /dntd/safety_fault.\nRecovery requires an explicit resume — the arm will not restart automatically when the fault clears:\nros2 topic pub --once /dntd/safety_resume std_msgs/Bool \u0026#34;data: true\u0026#34; Your arm controller should also subscribe to /dntd/heartbeat. If the heartbeat stops (RadarGuard process dies), the controller should independently STOP without waiting to be told.\nThe classifier is also fail-safe — clusters labeled UNKNOWN pass through as PERSON. The swept-volume clipper is fail-safe — if it detects a placeholder kinematic chain or suppresses all points, originals pass through with a warning.\nRoadmap IWR6843AOP driver and TLV parser ROS 2 pipeline (Humble) Ego-motion compensation via URDF Jacobian Background scene learning with novelty-aware refresh Motionless person detection Persistent background map (no relearn on boot) Hardware-agnostic outputs (serial / GPIO / MQTT) Configurable hysteresis and zone parameters Micro-doppler classifier — person vs. object discrimination (rule-based) Swept-volume workspace clip (forearm mount, kinematic reachability) ML classifier — drop-in upgrade to rule-based (RadIOCD + workspace data) 3-sensor 360° array fusion IWRL6432AOP pipeline (battery-powered and mobile robots) Heartbeat / vital-signs detection during STOP zone Occlusion awareness (blind spots fail-safe to CAUTION) Asymmetric zone shapes (reach-direction aware) Custom PCB (IWRL6432AOP, power-optimized, robot flange mount) systemd auto-start on boot FCC Part 15.255 certification Commercial use RadarGuard is free for research, education, and non-commercial projects under the Business Source License 1.1.\nCommercial use requires a license from DNTD Dynamics. Contact: contact@dntddynamics.com\nCommercial use includes any product, service, or internal tooling that generates revenue or is deployed in a production environment.\nContributing Issues, pull requests, and hardware compatibility reports are welcome.\nIf you\u0026rsquo;ve validated RadarGuard on a new arm or platform, open a PR to add it to the supported hardware table. If you hit a blocker getting it running, open an issue — the setup process has sharp edges and your report helps the next person.\nAbout RadarGuard is developed by DNTD Dynamics, a robotics and dynamics engineering company based in Snohomish, Washington.\nBuilt because the gap between \u0026ldquo;mmWave chip exists\u0026rdquo; and \u0026ldquo;working safety system a researcher can deploy in an afternoon\u0026rdquo; was too wide and too important to leave open.\nLicense Business Source License 1.1\nNon-commercial use: Free — research, education, personal projects Commercial use: Requires a license from DNTD Dynamics Change date: Four years from first tagged release Change license: Apache 2.0 (becomes fully open after change date) See LICENSE for full terms.\n","permalink":"https://dntddynamics.com/lab-notes/readme_521/","summary":"RadarGuard — mmWave Cobot Safety System Real-time human presence detection and collision avoidance for robot arms and mobile robots, powered by mmWave radar.\nBuilt by DNTD Dynamics · Licensed under BSL 1.1\nWhat is this? RadarGuard is an open-source safety system that uses mmWave radar to detect people in a robot\u0026rsquo;s workspace and output CLEAR / CAUTION / STOP zone commands in real time.\nUnlike camera-based safety systems, mmWave radar:","title":""},{"content":"Mission DNTD Dynamics is an independent hardware and biosystems research company founded in the Pacific Northwest built on a simple belief: the physical world still has more to reveal than we\u0026rsquo;ve imagined, and the tools to reveal it haven\u0026rsquo;t been built yet. We build sensing and analysis systems at the intersection of embedded hardware, machine learning, and physical science — pushing beyond what\u0026rsquo;s currently possible to find the questions worth answering. The interesting problems are rarely being explored in the open. This is the place that changes that.\nSelf-funded. No investors. Long time horizons.\nPrinciples The data stays on the machine that generated it. No cloud dependency required for any DNTD product to function — that\u0026rsquo;s not a feature, it\u0026rsquo;s a constraint we design around from the start.\nThe lab notes include the failures. The wrong turns are often the most useful thing we can publish. If you can reproduce our results or learn from our mistakes, that\u0026rsquo;s worth more than protecting the process.\nThe sensing middleware is hardware-agnostic by design. If mmWave fades, the platform survives. Long time horizons require structural independence from any single technology.\nNo venture capital. No external investors. The research agenda belongs to the people doing the research — and that\u0026rsquo;s the only way it stays pointed at the interesting problems.\nThe purpose is to see further into what\u0026rsquo;s possible, into what\u0026rsquo;s not yet visible. Every product DNTD ships sits above a much larger body of work — research that isn\u0026rsquo;t ready to ship, questions that don\u0026rsquo;t have answers yet, disciplines that don\u0026rsquo;t obviously connect to anything sellable today.\nThat\u0026rsquo;s intentional. The depth is the point.\nAbove the surface — Platform and Products. Sensing fusion middleware and hardware kits. Real tools, built to work, designed to last beyond any single technology cycle.\nBelow the surface — The Research Mass. Mycology, genetics, quantum chemistry simulation, biosystems sensing. Long bets with no guaranteed return. The work that seems disconnected from the products today is often what makes entirely new products possible ten years from now.\nContact Questions, collaboration, or just want to talk hardware — info@dntddynamics.com\n","permalink":"https://dntddynamics.com/about/","summary":"Mission DNTD Dynamics is an independent hardware and biosystems research company founded in the Pacific Northwest built on a simple belief: the physical world still has more to reveal than we\u0026rsquo;ve imagined, and the tools to reveal it haven\u0026rsquo;t been built yet. We build sensing and analysis systems at the intersection of embedded hardware, machine learning, and physical science — pushing beyond what\u0026rsquo;s currently possible to find the questions worth answering.","title":"About"},{"content":"mmWave 360° Sensing Kit — IWR6843AOP Pre-order open — ships Q3 2026\n$279 Free domestic shipping included · Domestic orders only · Evaluation board for development use · You are charged at time of order · Ship date Q3 2026 (conservative estimate)\nPre-order now → Secure checkout via Stripe · Order confirmations within 24 hours · Questions: info@dntddynamics.com What you\u0026rsquo;re ordering IWR6843AOPEVM evaluation module ROS2 driver package (Python, tested on JetPack 6.2.2 / Jetson Orin Nano Super) Working config file — tuned for arm-mounted use, not a generic example Zone detection library with STOP / CAUTION / CLEAR output Getting started guide Private repository access for kit owners Key specs Frequency60–64 GHz FMCW Range0.1 – 8.9 m Field of view±90° azimuth / ±40° elevation Outputx/y/z point cloud at 10 Hz via USB Tested platformJetson Orin Nano Super, JetPack 6.2.2 Ship dateQ3 2026 Not ready to order? Waiting for the IWRL6432AOP mobile kit ($199) or just want updates on when the 6843 ships?\nGet notified — no spam, just ship updates and new kit announcements. Lab notes, kit announcements, ship date updates. No noise.\nJoin the list Legal notes This product ships as an evaluation board for development use only. It is not FCC-certified for commercial consumer use. Purchasers are responsible for compliance with applicable regulations in their jurisdiction.\nFor return and refund policy, see Returns. For full terms, see Terms of Service.\n","permalink":"https://dntddynamics.com/store/","summary":"mmWave 360° Sensing Kit — IWR6843AOP Pre-order open — ships Q3 2026\n$279 Free domestic shipping included · Domestic orders only · Evaluation board for development use · You are charged at time of order · Ship date Q3 2026 (conservative estimate)\nPre-order now → Secure checkout via Stripe · Order confirmations within 24 hours · Questions: info@dntddynamics.com What you\u0026rsquo;re ordering IWR6843AOPEVM evaluation module ROS2 driver package (Python, tested on JetPack 6.","title":"Store"}]