chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,236 @@
|
||||
# OOMWOO Architecture Brief
|
||||
|
||||
> *Status: DRAFT / skeleton.* This document defines the system so that modules
|
||||
> can be built in parallel without colliding. Sections marked *TBD* are the
|
||||
> gating decisions; until they are filled in, hardware modules that must fit
|
||||
> together cannot be finalized. Treat the interface specs as the contract every
|
||||
> module agrees to.
|
||||
|
||||
## 1. Purpose/Goal and scope
|
||||
|
||||
OOMWOO is an open-source, 3D-printed, ROS2-based home robot vacuum with 2D LiDAR
|
||||
and Home Assistant support. It is designed to be *built from scratch by
|
||||
the community*, module by module, clean well and to double as an affordable ROS2
|
||||
development and learning platform.
|
||||
|
||||
*North star (not MVP):* OOMWOO is also the reference hardware for a broader
|
||||
robot application platform. Architectural boundaries (especially the app layer in
|
||||
§6) are drawn with that future in mind, but the MVP below deliberately excludes it.
|
||||
|
||||
## 2. Design principles
|
||||
|
||||
- *Open and swappable.* Every module has a defined interface. Any compliant
|
||||
implementation can replace another. No module depends on the internals of
|
||||
another, only on its published interface.
|
||||
- *Simulation-first.* Software must run in Gazebo before it runs on hardware,
|
||||
so contributors with no robot can still build and test.
|
||||
- *Affordable and printable.* Target off-the-shelf parts (a CM4/CM5-class compute
|
||||
module, common vacuum LiDARs, sourced Roborock/Dreame/Xiaomi motors and wear
|
||||
parts) and FDM-printable chassis parts.
|
||||
- *Safety is reviewed, not crowd-trusted.* Battery, charging, and motor-driver
|
||||
modules pass a maintainer safety review before merge (see §8).
|
||||
- *Reference-design backed.* A known-working vacuum (see the references in the
|
||||
[README](../README.md)) anchors the geometry and proves feasibility.
|
||||
|
||||
## 3. System overview
|
||||
|
||||
```
|
||||
LiDAR (UART, ~5 Hz) · MIPI camera(s) · IMU · serial audio
|
||||
|
|
||||
+-------------v-------------------------------+
|
||||
| CPU - CM4 / CM5 (or pin-compatible module) |
|
||||
| ROS2 · SLAM (slam_toolbox) · Nav2 · behavior |
|
||||
| educational variant: ESP32-S3 + micro-ROS |
|
||||
| (SLAM offboard on a dev PC over Wi-Fi) |
|
||||
+-------------^----------------+---------------+
|
||||
serial (cmds/telemetry) + | custom serial protocol
|
||||
CPU-reset / health GPIO | (NOT micro-ROS)
|
||||
+-------------+----------------v---------------+
|
||||
| MCU - STM32G070 (FreeRTOS, static alloc) |
|
||||
| motors · encoders · sensors · charging ctrl |
|
||||
| SAFETY (no Linux/ROS2): bumper/cliff/wheel- |
|
||||
| drop stop · current limit · CPU watchdog |
|
||||
+-------------+--------------------+-----------+
|
||||
| |
|
||||
+----------+----+ +--------+---------+
|
||||
| L/R drive | | suction fan, |
|
||||
| wheels, brush | | bumper, cliff, |
|
||||
| | | IR, wheel-drop |
|
||||
+---------------+ +------------------+
|
||||
|
||||
Power: off-the-shelf 4S2P Li-ion pack (built-in BMS).
|
||||
The CPU module + MCU sit on one carrier I/O board.
|
||||
```
|
||||
|
||||
> The CPU/MCU split keeps *all hard safety on the MCU*, independent of Linux/ROS2.
|
||||
> Interfaces are largely decided (see §5); refine as the io-pcb spec settles.
|
||||
|
||||
## 4. Coordinate frames and conventions
|
||||
|
||||
- *TBD:* Define `base_link` origin and orientation (REP-103: x-forward,
|
||||
y-left, z-up). All mechanical mounting points and URDF frames reference this.
|
||||
- *TBD:* Define the reference plane (floor contact), robot diameter, and height
|
||||
envelope. *These two numbers gate every hardware module.* Source these from the *sourced
|
||||
parts* + a 3D-scanned donor (see [source-3d-models](../contributions/source-3d-models));
|
||||
the current baseline is a ~349 mm round body ([oomwoo-one URDF](https://github.com/makerspet/oomwoo-one)).
|
||||
- Units: millimeters, kilograms, SI. Right-handed frames. Angles in radians.
|
||||
|
||||
## 5. Hardware architecture
|
||||
|
||||
### 5.1 Chassis and reference frame
|
||||
The chassis is the *integration backbone*. It publishes the mounting interface
|
||||
every other hardware module targets. Reference geometry (dimensions, wheelbase,
|
||||
motor specs, mass) comes from the *sourced parts* ([BOM](../BOM.md)) + 3D scans of
|
||||
a donor vacuum (see [source-3d-models](../contributions/source-3d-models)); the
|
||||
[oomwoo-one URDF](https://github.com/makerspet/oomwoo-one) carries the current
|
||||
~349 mm round-body baseline.
|
||||
|
||||
- *TBD:* Overall diameter and height budget.
|
||||
- *TBD:* Mounting grid / bolt pattern standard (e.g., M3 on a defined pitch).
|
||||
- *TBD:* Mass budget per module and total target mass.
|
||||
|
||||
### 5.2 Mechanical interface standard (the contract)
|
||||
Every hardware module's RFC must specify, against this standard:
|
||||
- Mounting points (bolt pattern, location relative to `base_link`).
|
||||
- Bounding envelope (max size the module may occupy).
|
||||
- Mass budget.
|
||||
- Mating tolerances and print orientation.
|
||||
- *TBD:* Define the standard connector/fastener set (screw sizes, heat-set
|
||||
inserts, etc.) so parts from different authors actually mate.
|
||||
|
||||
### 5.3 Electrical interface standard (the contract)
|
||||
- *Battery:* off-the-shelf pack with a *built-in BMS* — *4S2P Li-ion*, ~14.4 V
|
||||
nominal, ~5200 mAh / ~75 Wh (OEM BRR-2P4S-5200 class), charged 16.8 V CC/CV with
|
||||
NTC temperature sense. Chemistry is now decided; see the [BOM](../BOM.md).
|
||||
- *TBD:* Power rails distributed to modules (VBAT, 5V, 3.3V) and connector types/pinouts.
|
||||
- *CPU ↔ MCU:* a *custom high-speed serial protocol* (not micro-ROS) carries
|
||||
commands/telemetry, plus discrete GPIOs (CPU power on/off, and a CPU-reset line
|
||||
the MCU asserts on missed health packets). The *MCU owns motors and sensors*; the
|
||||
CPU never drives them directly.
|
||||
- *Sensors:* bumper/cliff/wheel-drop and analog IR are *MCU-side* (digital in / ADC);
|
||||
the *LiDAR (UART, ~5 Hz)*, MIPI camera(s), IMU, and serial audio attach to the *CPU*.
|
||||
|
||||
### 5.4 Compute (CPU) and real-time controller (MCU)
|
||||
|
||||
OOMWOO splits compute across two processors — mirroring how consumer vacuums are
|
||||
built, and, crucially, so that *safety never depends on Linux/ROS2*.
|
||||
|
||||
*CPU (compute module).* The I/O board is a *carrier* that accepts a *Raspberry Pi
|
||||
Compute Module 4 or 5* and — because the CM4 pinout is a de-facto standard — the
|
||||
many *pin-compatible alternative modules* (Radxa CM3/CM4, Pine64 SOQuartz, LuckFox
|
||||
Core3566, …), several with an *NPU* for future on-device vision. The CPU runs
|
||||
*ROS2, SLAM (slam_toolbox), Nav2, LiDAR processing, and high-level behavior*. CM
|
||||
modules are low-profile (helps the height budget) and swappable (hackable, cheaper,
|
||||
NPU options).
|
||||
- *Minimum target: a 4 GB CM4/CM5 (or Pi 4).* Realistic prior art runs
|
||||
slam_toolbox + Nav2 onboard a 4 GB Pi 4. Getting the floor to *2 GB* is a goal
|
||||
(ROS2 composable nodes; selectively rewriting heavy Python nodes in Rust/C++) —
|
||||
*no guarantee*, to be settled by the compute-benchmark. No 8–16 GB module needed.
|
||||
- *Cooling:* no dedicated CPU fan — the suction fan's airflow cools the compute
|
||||
board, as in consumer vacuums.
|
||||
- The earlier bespoke *RK3562* reference schematic is *dropped* in favour of the
|
||||
CM4/CM5 carrier.
|
||||
|
||||
*MCU (real-time / safety controller).* A dedicated microcontroller — *tentatively
|
||||
the STM32G070RBT6* (~56 GPIO incl. 16 ADC channels, ~$1 at JLCPCB, LQFP not BGA) —
|
||||
owns *motors, encoders, all sensors, battery-charging control, and safety*. Its
|
||||
role is *fixed*: functionality does not migrate onto the CPU, and CPU work does not
|
||||
migrate onto the MCU.
|
||||
- Firmware is *tentatively FreeRTOS* (static allocation, watchdog, guaranteed
|
||||
reaction times, CE-oriented) speaking a *custom serial protocol — not micro-ROS*
|
||||
(the tried-and-true consumer-vacuum approach; cf. the reverse-engineered
|
||||
[3irobotix protocol](https://github.com/codetiger/VacuumRobot)).
|
||||
- *Hard safety lives here, independent of Linux/ROS2:* the MCU stops all motors on
|
||||
a *bumper hit, cliff detection, or wheel-drop*, current-limits a *stuck brush*,
|
||||
and *watchdogs the CPU* — if the CPU's health packets stop, it stops the motors
|
||||
and can *reset the CPU*.
|
||||
- Tentative ~60-signal pin budget: see the [io-pcb RFC](../contributions/io-pcb)
|
||||
appendix (why the MCU needs a high-GPIO part).
|
||||
|
||||
*CPU ↔ MCU link.* A *high-speed serial* channel carries commands/telemetry both
|
||||
ways, plus discrete GPIOs — notably the *CPU-reset* line the MCU asserts on missed
|
||||
health packets, and CPU power on/off. Any LiDAR supported by `kaiaai/LDS` /
|
||||
`lds2d` is interface-compatible.
|
||||
|
||||
### 5.5 Two build profiles
|
||||
|
||||
The same carrier I/O board + MCU supports two swappable compute configurations:
|
||||
|
||||
| | *Consumer / regular* | *Educational / lower-cost* |
|
||||
|---|---|---|
|
||||
| CPU-slot module | CM4 / CM5 (or pin-compatible alt) | *ESP32-S3* board in the CM4 form factor |
|
||||
| Where ROS2 / SLAM runs | *onboard* (ROS2 + slam_toolbox + Nav2) | *offboard* on a local dev PC; ESP32-S3 runs *micro-ROS* |
|
||||
| Link | self-contained robot | robot ↔ dev PC over *Wi-Fi* |
|
||||
| Trade-off | plug-and-play for non-experts | cheaper, but Wi-Fi congestion / dead-zones — a learning platform, not a polished consumer product |
|
||||
|
||||
Onboard SLAM is the default for the consumer version *so non-experts can build and
|
||||
use it* without setting up a separate ROS2 dev machine. The ESP32-S3 can only be
|
||||
the *CPU-slot* option — it lacks the ~60 GPIO the MCU role needs, so it never
|
||||
replaces the STM32.
|
||||
|
||||
## 6. Software architecture
|
||||
|
||||
### 6.1 ROS2 graph (MVP)
|
||||
- Core nodes (MVP): LiDAR driver, base controller (diff-drive), odometry,
|
||||
teleop, SLAM (manual mapping), TF/URDF publisher.
|
||||
- *Interface contract:* each software module's RFC declares the ROS2 topics,
|
||||
services, message types, and parameters it publishes/consumes. Modules depend
|
||||
on these interfaces, not on each other's code. See
|
||||
[SOFTWARE_INTERFACES.md](SOFTWARE_INTERFACES.md) for the current draft ROS2
|
||||
graph contract.
|
||||
|
||||
### 6.2 Simulation
|
||||
- Gazebo + URDF, with a set of residential-layout worlds for navigation and
|
||||
coverage testing. Sim parity is a first-class requirement, not an afterthought.
|
||||
|
||||
### 6.3 Application layer (Phase 2 — north star, NOT in MVP)
|
||||
A ROS2-agnostic layer that runs third-party apps locally in isolated *Podman*
|
||||
containers, so app developers need no ROS2 expertise. Documented here only to
|
||||
keep its boundary clean; *explicitly out of scope for the Aug 31 MVP.*
|
||||
|
||||
## 7. MVP definition (target: 2026-08-31)
|
||||
|
||||
*In scope:* ROS2 on a CM4/CM5-class compute module · LiDAR · manual SLAM/mapping · teleop drive ·
|
||||
3D-printed chassis · Gazebo sim with URDF · evaluation + demo video. No dock,
|
||||
no autonomous exploration, no Home Assistant, no app layer.
|
||||
|
||||
*Explicit non-goals for MVP:* autonomous coverage, docking, auto-empty, mopping,
|
||||
Home Assistant, the app platform, accessories. These are later phases.
|
||||
|
||||
*Critical-path ownership:* the maintainer (+ small core) own the chassis,
|
||||
interface specs, and integration so the MVP does not depend on volunteer delivery
|
||||
timing. Community modules accelerate and improve the MVP; they do not block it.
|
||||
|
||||
## 8. Safety review gate
|
||||
|
||||
Battery, charging, motor-driver, and mains-adjacent modules require maintainer
|
||||
safety review before merge. RFCs for these modules must include a hazard note
|
||||
(over-current, thermal, short, mechanical pinch).
|
||||
|
||||
The battery risk is *reduced* by using an *off-the-shelf 4S2P Li-ion pack with a
|
||||
built-in BMS* (over-charge / over-discharge / short protection); the review then
|
||||
focuses on the *16.8 V CC/CV charging path + NTC temperature sense*. *Hard safety
|
||||
lives on the MCU, never on Linux/ROS2* — it independently stops motors on
|
||||
bumper/cliff/wheel-drop, current-limits a stuck brush, and watchdog-resets the CPU.
|
||||
|
||||
## 9. Roadmap (phases after MVP)
|
||||
|
||||
1. Rechargeable battery + basic dock + autonomous floor mapping.
|
||||
2. Home Assistant integration.
|
||||
3. Application layer (Podman app runtime) + first delightful apps.
|
||||
4. Accessories and novel apps; integrations (e.g., LeRobot arm).
|
||||
|
||||
## 10. Open questions
|
||||
|
||||
- *Resolved:* the MCU runs a *custom serial protocol (not micro-ROS)*; the CPU runs
|
||||
*onboard ROS2/SLAM/Nav2*. micro-ROS is used only in the *educational* ESP32-S3
|
||||
profile (SLAM offboard on a dev PC). See §5.4–5.5.
|
||||
- *Resolved:* battery is an off-the-shelf *4S2P Li-ion pack with a built-in BMS*,
|
||||
charged 16.8 V CC/CV. See §5.3, §8.
|
||||
- Can OOMWOO's onboard ROS2 stack fit in *2 GB* (composable nodes, selective Rust)
|
||||
rather than 4 GB? To be answered by the compute-benchmark.
|
||||
- MCU family: *STM32G070RBT6* is the tentative pick (GPIO/ADC count, ~$1 at JLCPCB,
|
||||
LQFP) — open to alternatives.
|
||||
- One hardware-agnostic HAL covering reference vacuum + DIY builds (community idea)?
|
||||
- Module selection process: who decides which competing implementation wins, and
|
||||
on what criteria? (See each module's acceptance criteria.)
|
||||
@@ -0,0 +1,103 @@
|
||||
# Contributing to OOMWOO
|
||||
|
||||
Thanks for your interest. OOMWOO is an open-source robot vacuum you build
|
||||
yourself, and it's at a very early stage. That's the best time to get involved,
|
||||
the foundations are still being laid and your input can shape the direction.
|
||||
|
||||
The project is built *module by module* so many people can work in parallel.
|
||||
Browse the [module list in the README](../README.md#requests-for-contributions),
|
||||
and see [ARCHITECTURE.md](ARCHITECTURE.md) for how the pieces fit together.
|
||||
|
||||
## Ways to help right now
|
||||
|
||||
You don't need to be a roboticist to contribute:
|
||||
|
||||
- *Ideas and feedback* — open a [Discussion](https://github.com/makerspet/oomwoo/discussions)
|
||||
about features, design choices, or what would make you build one.
|
||||
- *Code* — firmware, ROS2 packages, Home Assistant integration.
|
||||
- *Hardware* — 3D-printable chassis design, mechanical parts, PCB.
|
||||
- *Documentation* — build guides, wiring diagrams, troubleshooting notes.
|
||||
- *Testing* — once there's something to build, real-world build reports are gold.
|
||||
- *Spread the word* — star the repo, share your build, post a demo.
|
||||
|
||||
## Getting started
|
||||
|
||||
1. *Pick a module.* Browse the
|
||||
[module list in the README](../README.md#requests-for-contributions) and choose a
|
||||
hardware or software module. Software and simulation modules can start
|
||||
immediately; hardware modules wait on the interface specs in
|
||||
[ARCHITECTURE.md](ARCHITECTURE.md). Read the module's `README.md` so you know
|
||||
the contract.
|
||||
2. *Start a conversation first.* Claim or ask about the module in its
|
||||
[Issue](https://github.com/makerspet/oomwoo/issues) or
|
||||
[Discussion](https://github.com/makerspet/oomwoo/discussions) before writing
|
||||
code, so we align on the approach and avoid wasted effort.
|
||||
3. *Build it in your own repo.* For code and simulation modules, develop your
|
||||
package in your **own public repository** — you own it, version it, and keep the
|
||||
credit. Build against the ROS2 interface contract in
|
||||
[SOFTWARE_INTERFACES.md](SOFTWARE_INTERFACES.md) so your work stays interoperable
|
||||
with other modules. (Docs and small reference material are handled differently —
|
||||
see below.)
|
||||
4. *Submit a pointer PR.* Add a link to your repo in the module's entry with a
|
||||
one-line description. It's small, easy to review, and lets several
|
||||
implementations of the same module sit side by side. Keep the PR focused.
|
||||
5. *Iterate in the open.* Modules are swappable — the best implementation surfaces
|
||||
over time, with the maintainer having the last call. A design that isn't
|
||||
selected is still a useful fallback.
|
||||
|
||||
## How contributions are structured
|
||||
|
||||
OOMWOO keeps the core small and lets the community grow around it:
|
||||
|
||||
- *Canonical / reference code stays first-party.* [oomwoo-one](https://github.com/makerspet/oomwoo-one)
|
||||
(robot description + sim), [oomwoo-install](https://github.com/makerspet/oomwoo-install)
|
||||
(dev environment), and the `kaiaai_*` packages are maintained by the project so the
|
||||
out-of-the-box build always works.
|
||||
- *Module implementations (code) live in your repo.* You build a competing
|
||||
implementation of a module — a sim, a navigation stack, a behavior — in your own
|
||||
repository and submit a *link*. The project features accepted work from the
|
||||
module's page, credited to you. When a contribution is *featured*, we pin a
|
||||
specific commit or tag (and may fork it into the makerspet org) so the reference
|
||||
build stays reproducible even if the upstream repo moves.
|
||||
- *Docs, specs and small reference material stay in-tree.* Part specifications,
|
||||
datasheets, STEP-model sourcing notes, PCB notes, and benchmarks are lightweight
|
||||
and best kept alongside the project — contribute those under
|
||||
`contributions/<module>/<your-username>/` as files in a PR.
|
||||
|
||||
Why links for code? You keep ownership, credit, and freedom to iterate; the project
|
||||
stays lean and avoids absorbing third-party code and its licensing; and multiple
|
||||
implementations of a module can coexist and be compared. The shared
|
||||
[SOFTWARE_INTERFACES.md](SOFTWARE_INTERFACES.md) contract is what keeps
|
||||
independently-built modules compatible.
|
||||
|
||||
## Hardware contributions
|
||||
|
||||
For CAD and mechanical work, please include source files (not just exported STLs)
|
||||
where possible, so others can modify your design. Note the tool and version you
|
||||
used. If your change affects the bill of materials, mention it in the PR. Each
|
||||
hardware module must stay within the mechanical/electrical interfaces in
|
||||
[ARCHITECTURE.md](ARCHITECTURE.md).
|
||||
|
||||
*Safety:* battery, charging, motor-driver, and mains-adjacent modules require a
|
||||
maintainer safety review before merge. Include a hazard note in your submission.
|
||||
|
||||
## Code style
|
||||
|
||||
Conventions are still being established. For now: keep it simple, readable, and
|
||||
consistent with the surrounding code. ROS2 packages should follow standard ROS2
|
||||
layout and naming. We'll formalize linting and style as the codebase grows.
|
||||
|
||||
## Licensing
|
||||
|
||||
By contributing, you agree that your contributions are licensed under the
|
||||
project's [Apache License 2.0](../LICENSE). Hardware design files will be released
|
||||
under an open hardware license (to be finalized); contributions of hardware
|
||||
files are made on that same open basis.
|
||||
|
||||
## Community and conduct
|
||||
|
||||
Be respectful, helpful, and welcoming. We want OOMWOO to be an easy, friendly
|
||||
place for makers of every skill level. Harassment or hostility isn't tolerated.
|
||||
|
||||
Questions? Open a [Discussion](https://github.com/makerspet/oomwoo/discussions?discussions_q=)
|
||||
or join us on [Discord](https://discord.gg/3y2JKz5T25).
|
||||
@@ -0,0 +1,64 @@
|
||||
# RFC Backlog (planned / not-yet-active)
|
||||
|
||||
Planned modules that are *not yet active RFCs*. The RFCs ready to work on *now* live in the
|
||||
[README status table](../README.md#requests-for-contributions), and each active RFC's full
|
||||
spec is under [contributions/](../contributions). An item here graduates into an active RFC
|
||||
(its own `contributions/<module>/` folder) once it is unblocked and ready.
|
||||
|
||||
*Phase legend:* `MVP` = targeted for the bare-bones build · `P2` = next phase ·
|
||||
`P3+` = later. *Safety* = requires a maintainer safety review.
|
||||
|
||||
## On hold (have an RFC, paused pending sourced parts + a 3D reference design)
|
||||
|
||||
| Module | RFC | Notes |
|
||||
|---|---|---|
|
||||
| Dust bin 3D design | [dust-bin](../contributions/dust-bin) | Design / print / test the dust bin — waits on sourced parts + a 3D design |
|
||||
| Blower fan assembly | [vacuum-fan](../contributions/vacuum-fan) | Fans already sourced (see BOM); the volute / gasket housing waits on the 3D design |
|
||||
|
||||
## Planned hardware (mechanical design)
|
||||
|
||||
Waits on sourced parts + a 3D reference-design sketch, then becomes an active
|
||||
`contributions/` RFC. Part *specs* and *STEP models* are already active — see
|
||||
[part-specs](../contributions/part-specs) and [source-3d-models](../contributions/source-3d-models).
|
||||
The motor-driver / power PCB and battery charging are now the active
|
||||
[io-pcb](../contributions/io-pcb) RFC.
|
||||
|
||||
| Module | ID | Phase | Notes |
|
||||
|---|---|---|---|
|
||||
| Chassis / base frame (reference) | `hw-chassis` | MVP | Integration backbone; defines the mechanical interface. Maintainer-owned. |
|
||||
| Drive-wheel mounts (L/R) | `hw-wheel-mount` | MVP | Mount + suspension around the sourced wheel modules. |
|
||||
| Compute mount (RPi 5) | `hw-compute-mount` | MVP | Mount + airflow for the Pi 5. |
|
||||
| LiDAR mount | `hw-lidar-mount` | MVP | Centered turret; parametric for other models. |
|
||||
| Main brush assembly | `hw-main-brush` | MVP | *Tapered rubber anti-tangle roller* + drive. |
|
||||
| Bumper (mechanical + switches) | `hw-bumper` | MVP | Contact detection. |
|
||||
| Cliff-sensor mounts | `hw-cliff` | MVP | IR drop detection at edges / stairs. |
|
||||
| Top cover / shell | `hw-shell` | MVP | Cosmetic + protective; LiDAR clearance. |
|
||||
| Wiring harness | `hw-harness` | MVP | Connector pinouts per [io-pcb](../contributions/io-pcb) + [part-specs](../contributions/part-specs). |
|
||||
| Side brush | `hw-side-brush` | P2 | Edge cleaning. |
|
||||
| Mop module (dual-spinning) | `hw-mop` | P2 | 3D-printed *dual-spinning* pads; skip the self-washing roller. |
|
||||
| Charging dock (basic) | `hw-dock` | MVP | *Safety.* Contacts + alignment. |
|
||||
| Charging dock (auto-empty) | `hw-dock` | P2 | |
|
||||
| Charging dock (mop wash/dry) | `hw-dock` | P2 | |
|
||||
|
||||
## Planned software (later phase)
|
||||
|
||||
Foundational software (URDF, sim, SLAM, Nav2, coverage, teleop, sensors) is already covered by
|
||||
the active RFCs. Remaining, later-phase work:
|
||||
|
||||
| Module | ID | Phase | Notes |
|
||||
|---|---|---|---|
|
||||
| Diagnostics / telemetry | `sw-diagnostics` | P2 | Health, logs. |
|
||||
| Regression / CI tests | `sw-regression-tests` | P2 | Sim-based tests gating PRs. |
|
||||
| Home Assistant integration | `sw-homeassistant` | P2 | MQTT / HA entity, map, control. |
|
||||
| App runtime layer | `sw-app-runtime` | P3+ | ROS2-agnostic app sandbox. North star. |
|
||||
| Web UI / dashboard | `sw-webui` | P3+ | Local control + map view. |
|
||||
|
||||
## Non-engineering contributions (also wanted)
|
||||
|
||||
| Track | Phase | Notes |
|
||||
|---|---|---|
|
||||
| 3D-print validation | MVP | Confirm parts print cleanly on common FDM printers. |
|
||||
| Real-home testing | MVP/P2 | Build and report on real floors. |
|
||||
| Docs / build guides | MVP | Turn working modules into step-by-step instructions. |
|
||||
| Posts / videos / demos | ongoing | Content that grows the project (highly valued). |
|
||||
| SOTA research | ongoing | Best-in-class prior art per module (part of each RFC). |
|
||||
@@ -0,0 +1,159 @@
|
||||
# OOMWOO ROS2 Software Interfaces
|
||||
|
||||
> Status: DRAFT. This is the shared software contract for simulation-first
|
||||
> contributions. It reflects the current `urdf-gazebo-sim` package and should be
|
||||
> updated whenever a module needs a new public topic, service, action, frame, or
|
||||
> parameter.
|
||||
|
||||
## Purpose
|
||||
|
||||
OOMWOO software modules are meant to be swappable. A contributor should be able
|
||||
to build `clean-and-map`, `nav-localize`, `recovery-safety`, `dock-cycle`, or
|
||||
`cleaning-jobs` without depending on another module's private implementation.
|
||||
|
||||
This document defines the public ROS2 surface area those modules can share while
|
||||
the hardware is still evolving. It is not a final hardware API.
|
||||
|
||||
## Naming rules
|
||||
|
||||
- Public names below are shown as root topics, for example `/scan`. Launch files
|
||||
and node configs may use relative names such as `scan` when they resolve to
|
||||
the same root topic in the default launch.
|
||||
- Use REP-103 frames and SI units.
|
||||
- Prefer standard ROS2/Nav2 message types before adding custom OOMWOO messages.
|
||||
- If a module introduces a new public interface, document the producer, consumer,
|
||||
message type, QoS expectation, and failure behavior in its README and update
|
||||
this file.
|
||||
- Simulation-only details, such as Gazebo collision entity names, must not leak
|
||||
into cross-module contracts unless they are explicitly marked as simulation
|
||||
diagnostics.
|
||||
|
||||
## Frames
|
||||
|
||||
| Frame | Owner | Meaning |
|
||||
|---|---|---|
|
||||
| `map` | SLAM/localization | Global map frame used by SLAM, AMCL, Nav2, and saved maps. |
|
||||
| `odom` | Base odometry | Locally continuous odometry frame. |
|
||||
| `base_footprint` | Robot description / odometry | Planar base frame for navigation. |
|
||||
| `base_link` | Robot description | Main robot body frame. Hardware modules should reference this once geometry is frozen. |
|
||||
| `base_scan` | Robot description | 2D LiDAR frame. |
|
||||
|
||||
Open decision: `base_link` origin, reference plane, robot diameter, and height
|
||||
envelope are still defined in `ARCHITECTURE.md`.
|
||||
|
||||
## Baseline Topics
|
||||
|
||||
These topics are provided by the current Gazebo simulation or standard Nav2/SLAM
|
||||
bringup and should be treated as the MVP baseline.
|
||||
|
||||
| Topic | Type | Direction | Producer | Consumers |
|
||||
|---|---|---|---|---|
|
||||
| `/cmd_vel` | `geometry_msgs/msg/Twist` | Command | Teleop, Nav2 velocity smoother, recovery nodes | Gazebo diff-drive / base controller |
|
||||
| `/odom` | `nav_msgs/msg/Odometry` | State | Gazebo odometry / base controller | SLAM, AMCL, Nav2, recovery and job logic |
|
||||
| `/tf` | `tf2_msgs/msg/TFMessage` | State | Robot state publisher, odometry, SLAM/localization | All pose-aware modules |
|
||||
| `/joint_states` | `sensor_msgs/msg/JointState` | State | Gazebo joint state publisher / hardware base | Robot state publisher, diagnostics |
|
||||
| `/scan` | `sensor_msgs/msg/LaserScan` | Sensor | 2D LiDAR / Gazebo LiDAR | SLAM, AMCL, Nav2 costmaps, wall following |
|
||||
| `/map` | `nav_msgs/msg/OccupancyGrid` | State | SLAM or map server | Nav2, cleaning, zones, visualization |
|
||||
| `/bumper_left` | `ros_gz_interfaces/msg/Contacts` in Gazebo | Sensor | Gazebo left contact sensor | Recovery, safety, clean-and-map obstacle handling |
|
||||
| `/bumper_right` | `ros_gz_interfaces/msg/Contacts` in Gazebo | Sensor | Gazebo right contact sensor | Recovery, safety, clean-and-map obstacle handling |
|
||||
|
||||
### Bumper events
|
||||
|
||||
The current simulation publishes raw Gazebo contact messages:
|
||||
|
||||
- Message type: `ros_gz_interfaces/msg/Contacts`
|
||||
- Contact list field: `contacts`
|
||||
- Per-contact fields: `collision1`, `collision2`, `positions`, `normals`,
|
||||
`depths`, `wrenches`
|
||||
|
||||
Consumers should treat `len(msg.contacts) > 0` as a bumper event after filtering
|
||||
out ground-plane contacts. Do not read a `collisions` field; that belongs to the
|
||||
single-contact message type and is not what the bridge publishes.
|
||||
|
||||
Hardware may eventually replace raw Gazebo contacts with a normalized bumper
|
||||
message. Until that decision is made, module submissions should isolate the
|
||||
Gazebo-specific parsing behind a small adapter.
|
||||
|
||||
## Nav2 Interfaces
|
||||
|
||||
Modules should reuse Nav2 actions and servers where possible.
|
||||
|
||||
| Interface | Type | Typical consumer |
|
||||
|---|---|---|
|
||||
| `/navigate_to_pose` | `nav2_msgs/action/NavigateToPose` | `nav-localize`, `cleaning-jobs`, `dock-cycle` |
|
||||
| `/navigate_through_poses` | `nav2_msgs/action/NavigateThroughPoses` | Coverage, room jobs, dock approach |
|
||||
| Nav2 behavior server | `spin`, `backup`, `drive_on_heading`, `wait` behaviors | Recovery and local fallback logic |
|
||||
| Costmaps | Nav2 local/global costmap topics | Obstacle handling, zones, diagnostics |
|
||||
| Map saver | Nav2 map saver service/CLI | `clean-and-map`, `nav-localize` |
|
||||
|
||||
If a module needs to command motion directly, it must define how it arbitrates
|
||||
with Nav2 and recovery nodes so two nodes do not fight over `/cmd_vel`.
|
||||
|
||||
## Module Contracts
|
||||
|
||||
| Module | Inputs | Outputs / public behavior |
|
||||
|---|---|---|
|
||||
| `urdf-gazebo-sim` | `/cmd_vel` | Publishes `/scan`, `/odom`, `/tf`, `/joint_states`, `/bumper_left`, `/bumper_right`; provides worlds and robot description. |
|
||||
| `clean-and-map` | `/scan`, `/odom`, `/tf`, bumper events, optional Nav2 actions | Drives first-pass coverage, produces a complete map, defines a done condition, saves map artifacts. |
|
||||
| `nav-localize` | Saved map, `/scan`, `/odom`, `/tf`, Nav2 bringup | Provides known-map navigation, relocalization, and map-resume behavior. |
|
||||
| `recovery-safety` | Bumper events, future cliff/wheel-drop/pickup/e-stop signals, Nav2 failures | Stops or gates motion, runs bounded recoveries, publishes clear pause/error status. |
|
||||
| `floor-care` | `/scan`, map/coverage context, future surface sensor | Provides wall/edge following, surface classification, and mop actuator decisions. |
|
||||
| `cleaning-jobs` | Saved map, zones, coverage progress, battery/bin/mop status, Nav2 actions | Provides start/pause/resume/cancel/status job behavior suitable for a future Home Assistant layer. |
|
||||
| `dock-cycle` | Nav2/localization, dock marker, battery/service state | Provides undock, return-to-dock, precise docking, recharge/service completion, and find-dock fallback. |
|
||||
| `live-robot-bringup` | Hardware drivers, same logical topics | Validates that hardware exposes the same public interfaces as the simulation. |
|
||||
|
||||
## Status and Errors
|
||||
|
||||
The final robot status API is still open. Until it is selected, modules that
|
||||
need status reporting should document:
|
||||
|
||||
- `state`: short machine-readable state, for example `cleaning`, `recovering`,
|
||||
`paused`, `docked`, or `error`
|
||||
- `reason_code`: stable machine-readable reason, for example `BUMPER_STUCK`,
|
||||
`LOCALIZATION_LOST`, or `LOW_BATTERY`
|
||||
- `message`: human-readable explanation
|
||||
- `recoverable`: whether a resume command is expected to work
|
||||
- `source`: module name that produced the status
|
||||
|
||||
Open decision: choose the transport and type for cross-module status, likely a
|
||||
standard diagnostic message or a small OOMWOO-specific message package.
|
||||
|
||||
## QoS and Parameters
|
||||
|
||||
- `use_sim_time` should be true in simulation launch files.
|
||||
- Sensor streams such as `/scan` should use sensor-data QoS where configurable.
|
||||
- `/map` and saved-map metadata should be available to late joiners where the
|
||||
producer supports transient-local durability.
|
||||
- Command topics should use small queues and should fail safe: stale commands
|
||||
must not keep the robot moving.
|
||||
|
||||
## Validation Checklist
|
||||
|
||||
A module submission that depends on the MVP simulation should document how to
|
||||
check the interfaces it uses. At minimum:
|
||||
|
||||
```bash
|
||||
ros2 topic list
|
||||
ros2 topic echo /scan --once
|
||||
ros2 topic echo /odom --once
|
||||
ros2 topic echo /bumper_left
|
||||
ros2 topic echo /bumper_right
|
||||
ros2 run tf2_tools view_frames
|
||||
```
|
||||
|
||||
For Nav2-based modules, also document how to send a `NavigateToPose` goal and
|
||||
how to confirm `/cmd_vel` arbitration is safe.
|
||||
|
||||
## Miscellaneous
|
||||
|
||||
- PR your packages into the official distribution [makerspet/oomwoo-install](https://github.com/makerspet/oomwoo-install)
|
||||
- put your ROS2 packages under `/ros_ws/src` (don't create another colcon workspace under `~/`)
|
||||
- follow conventions of being able to select a robot package using `kaia config robot.model oomwoo_one`, see [tutorial](https://makerspet.com/blog/simulate-oomwoo-one-robot-vacuum-in-gazebo-with-ros-2/)
|
||||
|
||||
## Open Decisions
|
||||
|
||||
- Final hardware bumper/cliff/wheel-drop message shape.
|
||||
- Robot-wide status/error message type and topic.
|
||||
- Battery, dust-bin, mop, and dock service state interfaces.
|
||||
- Localization-confidence interface for relocalization and kidnap detection.
|
||||
- Job action/service API for start, pause, resume, cancel, and status.
|
||||
@@ -0,0 +1,169 @@
|
||||
# Write Your First OOMWOO ROS 2 Package: Cover the Floor While Mapping
|
||||
|
||||
> *Draft for makerspet.com (WordPress / Gutenberg).* Post 2 of 2. Builds on Post 1
|
||||
> (*Simulate the oomwoo-one Robot Vacuum in Gazebo with ROS 2*). Here you'll write a small
|
||||
> *pure-ROS 2* package that drives `oomwoo-one` on a coverage path *while* SLAM maps the
|
||||
> room, and launch it all with one `ros2 launch`.
|
||||
|
||||
This is the "hello world" of developing for OOMWOO. It's deliberately simple — a reactive
|
||||
"drive forward, turn when blocked" coverage that, combined with SLAM, maps and roughly covers
|
||||
a room. Proper boustrophedon coverage is the [clean-and-map
|
||||
RFC](https://github.com/makerspet/oomwoo/tree/main/contributions/clean-and-map); this teaches
|
||||
you the mechanics of writing and launching an OOMWOO node.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
The dev environment from *Post 1* (the `makerspet/oomwoo:jazzy-dev` container running, with
|
||||
`kaia config robot.model oomwoo_one` set). Everything below runs *inside the container*.
|
||||
|
||||
## 1. Create a ROS 2 package
|
||||
|
||||
```
|
||||
mkdir -p ~/ros2_ws/src && cd ~/ros2_ws/src
|
||||
ros2 pkg create --build-type ament_python oomwoo_coverage --dependencies rclpy sensor_msgs geometry_msgs
|
||||
```
|
||||
|
||||
## 2. Write the coverage node
|
||||
|
||||
Create `~/ros2_ws/src/oomwoo_coverage/oomwoo_coverage/coverage_node.py`:
|
||||
|
||||
```python
|
||||
import math
|
||||
import rclpy
|
||||
from rclpy.node import Node
|
||||
from sensor_msgs.msg import LaserScan
|
||||
from geometry_msgs.msg import Twist
|
||||
|
||||
|
||||
class Coverage(Node):
|
||||
"""Drive forward; when the path ahead is blocked, turn until it clears.
|
||||
Combined with SLAM, this bounces around a room and maps it."""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__('coverage')
|
||||
self.pub = self.create_publisher(Twist, 'cmd_vel', 10)
|
||||
self.create_subscription(LaserScan, 'scan', self.on_scan, 10)
|
||||
self.clear = True
|
||||
self.turn_dir = 1.0
|
||||
self.create_timer(0.1, self.tick) # 10 Hz control loop
|
||||
|
||||
def on_scan(self, msg):
|
||||
n = len(msg.ranges)
|
||||
if n == 0:
|
||||
return
|
||||
# Check the forward +/- 25 deg sector (forward = scan index 0 for this LiDAR;
|
||||
# adjust 'center' if your scan's zero points elsewhere).
|
||||
sector = int(math.radians(25) / msg.angle_increment)
|
||||
idxs = [(i) % n for i in range(-sector, sector + 1)]
|
||||
fwd = [msg.ranges[i] for i in idxs
|
||||
if msg.range_min < msg.ranges[i] < msg.range_max]
|
||||
was_clear = self.clear
|
||||
self.clear = (min(fwd) > 0.35) if fwd else True
|
||||
if was_clear and not self.clear:
|
||||
self.turn_dir *= -1.0 # alternate turn direction each time we hit something
|
||||
|
||||
def tick(self):
|
||||
cmd = Twist()
|
||||
if self.clear:
|
||||
cmd.linear.x = 0.20 # m/s forward
|
||||
else:
|
||||
cmd.angular.z = 0.8 * self.turn_dir # rad/s turn until clear
|
||||
self.pub.publish(cmd)
|
||||
|
||||
|
||||
def main():
|
||||
rclpy.init()
|
||||
node = Coverage()
|
||||
try:
|
||||
rclpy.spin(node)
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
node.destroy_node()
|
||||
rclpy.shutdown()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
```
|
||||
|
||||
## 3. Register the node
|
||||
|
||||
In `~/ros2_ws/src/oomwoo_coverage/setup.py`, add the console script:
|
||||
|
||||
```python
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
'coverage = oomwoo_coverage.coverage_node:main',
|
||||
],
|
||||
},
|
||||
```
|
||||
|
||||
## 4. Add a launch file (sim + SLAM + your node, in one command)
|
||||
|
||||
Create `~/ros2_ws/src/oomwoo_coverage/launch/coverage.launch.py`:
|
||||
|
||||
```python
|
||||
import os
|
||||
from launch import LaunchDescription
|
||||
from launch.actions import IncludeLaunchDescription, TimerAction
|
||||
from launch.launch_description_sources import PythonLaunchDescriptionSource
|
||||
from launch_ros.actions import Node
|
||||
from ament_index_python.packages import get_package_share_directory
|
||||
|
||||
|
||||
def generate_launch_description():
|
||||
gazebo = IncludeLaunchDescription(PythonLaunchDescriptionSource(
|
||||
os.path.join(get_package_share_directory('kaiaai_gazebo'),
|
||||
'launch', 'world.launch.py')))
|
||||
|
||||
nav = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource(
|
||||
os.path.join(get_package_share_directory('kaiaai_bringup'),
|
||||
'launch', 'navigation.launch.py')),
|
||||
launch_arguments={'use_sim_time': 'true', 'slam': 'True'}.items())
|
||||
|
||||
coverage = Node(package='oomwoo_coverage', executable='coverage',
|
||||
name='coverage', parameters=[{'use_sim_time': True}])
|
||||
|
||||
# give Gazebo + SLAM ~12 s to come up before the robot starts moving
|
||||
return LaunchDescription([gazebo, nav, TimerAction(period=12.0, actions=[coverage])])
|
||||
```
|
||||
|
||||
Reference the launch folder in `setup.py` `data_files` so it installs:
|
||||
```python
|
||||
(os.path.join('share', package_name, 'launch'), glob('launch/*.launch.py')),
|
||||
```
|
||||
(add `import os` and `from glob import glob` at the top of `setup.py`).
|
||||
|
||||
## 5. Build and run
|
||||
|
||||
```
|
||||
cd ~/ros2_ws
|
||||
colcon build --packages-select oomwoo_coverage
|
||||
source install/setup.bash
|
||||
ros2 launch oomwoo_coverage coverage.launch.py
|
||||
```
|
||||
|
||||
Gazebo and RViz open, SLAM starts, and after ~12 s oomwoo-one begins driving itself —
|
||||
forward until it's blocked, then turning — while the map fills in. Open RViz
|
||||
(`ros2 launch kaiaai_bringup monitor_robot.launch.py use_sim_time:=true` in another shell) to
|
||||
watch the map grow.
|
||||
|
||||
## 6. Save the map
|
||||
|
||||
```
|
||||
ros2 run nav2_map_server map_saver_cli -f ~/maps/map
|
||||
```
|
||||
|
||||
## What you just learned
|
||||
|
||||
- created a ROS 2 package, wrote a node that *subscribes to `/scan` and publishes `/cmd_vel`*,
|
||||
- combined *your code + SLAM* in a single `ros2 launch`,
|
||||
- produced a real map from an autonomous run.
|
||||
|
||||
That's the whole loop of developing for OOMWOO. From here, the natural next step is *real
|
||||
coverage path planning* (boustrophedon, wall-following, frontier exploration) — which is
|
||||
exactly the [clean-and-map RFC](https://github.com/makerspet/oomwoo/tree/main/contributions/clean-and-map).
|
||||
Pick it up (or another module) from the [Requests for
|
||||
Contributions](https://github.com/makerspet/oomwoo#requests-for-contributions), and come build
|
||||
with us on [Discord](https://discord.gg/3y2JKz5T25).
|
||||
@@ -0,0 +1,127 @@
|
||||
# Simulate the oomwoo-one Robot Vacuum in Gazebo with ROS 2
|
||||
|
||||
> *Draft for makerspet.com (WordPress / Gutenberg).* Post 1 of 2: set up the OOMWOO
|
||||
> software dev environment and drive `oomwoo-one` in simulation — no robot required.
|
||||
> (Post 2 teaches you to write your own OOMWOO code.)
|
||||
|
||||
[OOMWOO](https://github.com/makerspet/oomwoo) is an open-source robot vacuum you build
|
||||
yourself. *oomwoo-one* is the first model. This tutorial gets its ROS 2 simulation running
|
||||
in *Gazebo* so you can develop mapping, navigation, and cleaning behaviours with *no
|
||||
hardware* — everything runs in Docker on *Ubuntu or Windows*.
|
||||
|
||||
You'll get: SLAM mapping, autonomous Nav2 navigation, manual driving, and bumper sensors —
|
||||
the same interfaces the real robot will expose.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- *Docker* — Docker Desktop on Windows/macOS, Docker Engine on Linux.
|
||||
- *An X server* for the GUI windows (Gazebo, RViz):
|
||||
- *Windows:* [VcXsrv](https://sourceforge.net/projects/vcxsrv/) (XLaunch).
|
||||
- *Linux:* native X — nothing to install.
|
||||
- No physical robot.
|
||||
|
||||
## 1. Start the X server (Windows only)
|
||||
|
||||
Launch *XLaunch* (from VcXsrv) and accept the defaults *except*:
|
||||
|
||||
> On the *"Display settings"* page, set *Display number = `0`* (not `-1`).
|
||||
> The Docker container connects to `host.docker.internal:0.0`, so the display number *must
|
||||
> be 0* or no GUI windows will appear.
|
||||
|
||||
Also tick *"Disable access control"* on the "Extra settings" page so the container can
|
||||
connect. Finish the wizard — a tiny X icon appears in your tray.
|
||||
|
||||
On *Linux*, instead allow local Docker to reach your X server:
|
||||
```bash
|
||||
xhost +local:docker
|
||||
```
|
||||
|
||||
## 2. Pull the OOMWOO Docker image
|
||||
|
||||
```
|
||||
docker pull makerspet/oomwoo:jazzy-dev
|
||||
```
|
||||
|
||||
## 3. Start the container
|
||||
|
||||
*Windows (PowerShell):*
|
||||
```powershell
|
||||
docker run --name makerspet -it --rm -v c:\maps:/root/maps -p 8888:8888/udp -p 5555:5555/udp -e DISPLAY=host.docker.internal:0.0 -e LIBGL_ALWAYS_INDIRECT=0 --add-host=host.docker.internal:host-gateway makerspet/oomwoo:jazzy-dev
|
||||
```
|
||||
(`DISPLAY=...:0.0` matches the XLaunch *display 0* from step 1.)
|
||||
|
||||
*Ubuntu / Linux:*
|
||||
```bash
|
||||
docker run --name makerspet -it --rm -v ~/maps:/root/maps -p 8888:8888/udp -p 5555:5555/udp -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix --network host makerspet/oomwoo:jazzy-dev
|
||||
```
|
||||
|
||||
Need more terminals into the same container? Open another PowerShell/terminal and run:
|
||||
```
|
||||
docker exec -it makerspet bash
|
||||
```
|
||||
|
||||
## 4. Select the oomwoo-one model
|
||||
|
||||
Inside the container:
|
||||
```
|
||||
kaia config robot.model oomwoo_one
|
||||
```
|
||||
|
||||
## 5. Launch the Gazebo world
|
||||
|
||||
```
|
||||
ros2 launch kaiaai_gazebo world.launch.py
|
||||
```
|
||||
A Gazebo window opens with oomwoo-one in a living-room world.
|
||||
|
||||
## 6. Start SLAM mapping
|
||||
|
||||
In a new container shell (`docker exec -it makerspet bash`):
|
||||
```
|
||||
ros2 launch kaiaai_bringup navigation.launch.py use_sim_time:=true slam:=True
|
||||
```
|
||||
|
||||
## 7. Open the RViz monitor
|
||||
|
||||
```
|
||||
ros2 launch kaiaai_bringup monitor_robot.launch.py use_sim_time:=true
|
||||
```
|
||||
Watch the map build as the robot moves.
|
||||
|
||||
## 8. Drive it manually
|
||||
|
||||
```
|
||||
ros2 run kaiaai_teleop teleop_keyboard
|
||||
```
|
||||
Use the keyboard to drive oomwoo-one around and fill in the map.
|
||||
|
||||
## 9. Autonomous navigation
|
||||
|
||||
In RViz, click *"Nav2 Goal"* and click-drag a destination — oomwoo-one plans a path and
|
||||
drives there on its own.
|
||||
|
||||
## 10. Check the bumper sensors
|
||||
|
||||
```
|
||||
ros2 topic echo /bumper_left
|
||||
ros2 topic echo /bumper_right
|
||||
```
|
||||
Drive into a wall and watch the left/right contact events fire.
|
||||
|
||||
## 11. Save your map
|
||||
|
||||
```
|
||||
ros2 run nav2_map_server map_saver_cli -f ~/maps/map
|
||||
```
|
||||
On Windows the map lands in `c:\maps`; on Linux in `~/maps`.
|
||||
|
||||
## What's next
|
||||
|
||||
You now have a full oomwoo-one simulation: SLAM, Nav2, teleop, and bumpers, exactly the
|
||||
interfaces the real robot exposes. In *Post 2* you'll write your *first OOMWOO ROS 2
|
||||
package* — a node that drives a coverage path *while* mapping — and launch it with
|
||||
`ros2 launch`.
|
||||
|
||||
Want to help build OOMWOO? Grab a module from the [Requests for
|
||||
Contributions](https://github.com/makerspet/oomwoo#requests-for-contributions) or say hi on
|
||||
[Discord](https://discord.gg/3y2JKz5T25).
|
||||
@@ -0,0 +1,332 @@
|
||||
# OOMWOO Design Document
|
||||
|
||||
> *Working design doc.* This accumulates research-backed design decisions before
|
||||
> they're split into per-module [RFCs](../contributions) and the [RFC backlog](RFC_BACKLOG.md), and/or migrated into
|
||||
> the [README](../README.md). A condensed version of §1 already lives in the
|
||||
> README's [Design research](../README.md#design-research) section. Expect this to
|
||||
> grow as remaining components are researched.
|
||||
|
||||
---
|
||||
|
||||
## 1. Design research — what makes vacuum users happy
|
||||
|
||||
We reviewed the 2025–2026 consumer robot vacuum landscape (global + China-sourceable
|
||||
brands, budget → flagship) across RTINGS, VacuumWars, Reddit r/robotvacuums, and
|
||||
reviewer blogs, then adversarially fact-checked the key claims. The point: copy the
|
||||
solutions that correlate with happy users, skip the ones that need commercial scale.
|
||||
|
||||
### Suction — a sourcing problem, not an engineering one
|
||||
Real-world cleaning does *not* track advertised suction (Pa). ~$500 mid-tier
|
||||
models beat flagships in pickup tests. A moderate *sealed* sourced motor + a good
|
||||
brush + a *tight airflow seal* matches flagship cleaning — *no custom-molded
|
||||
impeller needed.* Airflow sealing (bin/fan/brush seams) matters more than raw Pa.
|
||||
[(source)](https://www.thesmarthomehookup.com/the-best-300-600-robot-vacuums-they-beat-the-flagships/)
|
||||
|
||||
### Navigation & "never gets stuck" — the #1 user pain, hardest to replicate
|
||||
Best obstacle avoidance comes from *sensor fusion* (LiDAR + a floor-level 3D-ToF
|
||||
or RGB camera + AI object recognition), not any single sensor. LiDAR is structurally
|
||||
*blind below its ~10 cm turret*, which is exactly why robots eat cables and socks.
|
||||
The Eufy Omni S2 ($1,599) was the only model in one test to pass all 24 obstacles —
|
||||
and it has the full vision stack. *Never-stuck is commercial-scale.*
|
||||
- *For OOMWOO:* v1 leans on the *bumper* for low/LiDAR-invisible obstacles (this
|
||||
is already how the [clean-and-map RFC](../contributions/clean-and-map) handles it).
|
||||
Camera + AI vision avoidance is a *later / experimental* goal, not an MVP promise.
|
||||
Don't position OOMWOO as out-navigating commercial flagships; position it as an
|
||||
open platform to *experiment* with navigation and vision.
|
||||
[(source)](https://vacuumwars.com/best-robot-vacuums-with-obstacle-avoidance/)
|
||||
|
||||
### Brush — anti-tangle is what users notice
|
||||
Rubber beats bristle, and a *tapered, one-side-mounted roller* resists hair-wrap
|
||||
best (hair tangling is a top complaint). Easy to 3D-print or source compatible.
|
||||
|
||||
### Mop — dual-spinning is the replicable sweet spot
|
||||
Performance ladder: flat drag pad (worst) → *dual spinning pads* (mid) →
|
||||
self-washing roller (best). But the roller mop's "better stains / less residual
|
||||
water" edge was *refuted* under fact-checking — it's overstated. A 3D-printed
|
||||
*dual-spinning* mop is competitive and DIY-able; *skip the self-washing roller*
|
||||
(and its multifunction wash/dry dock) for now.
|
||||
[(source)](https://vacuumwars.com/robot-vacuum-mop-systems/)
|
||||
|
||||
### Dock — basic is DIY-able, full-service is not
|
||||
A *basic charging dock* is well within reach (print the housing; source contacts +
|
||||
adapter + IR beacon). *Auto-empty / mop-wash / hot-dry all-in-one docks* are
|
||||
commercial-scale — defer, or use an off-the-shelf corded vac for emptying.
|
||||
|
||||
### Cloud-free / local control — the real differentiator
|
||||
[Valetudo](https://github.com/Hypfer/Valetudo) gives cloud-free MQTT/REST local
|
||||
control across ~10 brands. *Dreame* is the most rootable (≈16 models) and the
|
||||
safest donor to study. Cloud-free local operation is OOMWOO's positioning advantage.
|
||||
|
||||
### Well-loved models worth studying
|
||||
Eufy Omni S2 (obstacle avoidance), Narwal Flow (roller mop), Ecovacs Deebot T90 Pro
|
||||
Omni (~$499 all-rounder), Dreame X40 Ultra (dual-spinning mop; Dreame = best donor).
|
||||
|
||||
> *Caveats:* the top-level dimensions (suction-decoupling, sensor-fusion, mop
|
||||
> ladder) are primary-source and verified. Per-model rankings are *directional*,
|
||||
> from single-run reviewer tests. Rootability is *per hardware revision* —
|
||||
> re-verify any specific donor unit before buying to root.
|
||||
|
||||
---
|
||||
|
||||
## 2. Print vs source strategy
|
||||
|
||||
*Rule of thumb: print geometry, source mechanisms and wear items.* Anything with a
|
||||
gearbox, encoder, rubber compound, spring, pump, or bearing is precision you can buy
|
||||
for a few dollars; anything custom-shaped that mates with the OOMWOO chassis, print.
|
||||
|
||||
| Component | Source or 3D print | Why / how |
|
||||
|---|---|---|
|
||||
| *Driving wheel assemblies* | *Source (whole module)* | Complete drive modules (gearmotor + encoder + suspension + rubber tire). 3D print at most an adapter bracket. Why? Requires advanced skill - possibly SLA for gearbox, FDM TPU tire. The #1 "don't print it" part. |
|
||||
| *Universal / caster wheel* | *Print* or source wheel/ball caster. | Likely a simple passive swivel, possibly TPU. |
|
||||
| *Side brush assembly* | *Hybrid* | Source brush + small gearmotor; print the mount. Fit a *common replaceable brush*. Fixed (not extendable) for v1. |
|
||||
| *Main brush* | *Source / hybrid* | Tapered rubber anti-tangle roller in a *common wear-part size*; source compatible, or print core + rubber. |
|
||||
| *Bumper* | *Hybrid* | Print floating shroud; source lever microswitches + return springs. |
|
||||
| *Dust bin / water tank* | *Print body + source guts* | Print custom body (mates airflow); source filter (common HEPA size), gasket (or TPU print), latch spring. Water tank adds a sourced *pump + solenoid valve + tubing*. |
|
||||
| *Mop lift* | *Hybrid (P2)* | Print cam/linkage; source a small *servo or geared motor*. |
|
||||
| Mop disposable cloths | *Source* | Source (easier) or DIY sew. |
|
||||
| Mop dryer | *Source* | Source the mop dryer. |
|
||||
| *Enclosure / top shell* | *Print* | Custom cosmetic/structural; no off-the-shelf equivalent. Design for splitting to fit common print beds. |
|
||||
| *Dock (basic charge)* | *Print housing + source contacts* | Source *pogo pins / spring contacts / magnets* (magnets can carry [10 A](https://xdaforums.com/t/home-made-pogo-pin-charging-dock.2019847/)) + wall adapter + IR-beacon LEDs. Plenty of [DIY precedent](https://www.instructables.com/Roamer-the-Self-Charging-Companion-Robot/). |
|
||||
| *Auto-empty dock* | *3D print enclosure + source bin/fan* | Needs its own fan + bin (commercial-scale). Off-the-shelf corded vac bolted to a printed dock is the DIY path. |
|
||||
| *Mop dock* | *3D print enclosure + source water tanks/hookups* | Needs its own fan + bin (commercial-scale). Off-the-shelf corded vac bolted to a printed dock is the DIY path. |
|
||||
| Battery, LiDAR, motors, PCB, fasteners, bearings, gaskets | *Source* | Standard sourced parts (custom PCB + LiDAR aside). |
|
||||
| Single Board Computer | *Source* | Raspberry Pi 5 4GB or better for first model. |
|
||||
| Input/Output board | *Custom* | No DIY-vacuum I/O PCBs I'm aware of. I'll design a custom PCB for sensors and motor drivers. |
|
||||
| Cameras, sensors, LiDAR | *Source* | Color + distance cameras for top-tier obstacle avoidance. IR cliff, side proximity sensors. Ultrasonic carpet sensor. |
|
||||
|
||||
*Sourcing strategy:* deliberately spec sourced wear parts (brushes, filters, wheel
|
||||
modules) in *common, abundant sizes* so users buy cheap "universal" / Roomba-style
|
||||
replacements anywhere. A selling feature *and* less inventory to stock.
|
||||
|
||||
---
|
||||
|
||||
## 3. Feature decisions
|
||||
|
||||
### 3.1 Extendable side brush — *fixed for v1*
|
||||
The extendable arm (e.g. Roborock FlexiArm) is a *genuinely loved* feature — best
|
||||
corner-cleaning scores and strong reviews
|
||||
[(source)](https://vacuumwars.com/vacuum-wars-best-robot-vacuums/). But it's a
|
||||
mechanically complex actuated mechanism. A well-placed *fixed* side brush captures
|
||||
most of the corner benefit at a fraction of the complexity. Extendable is a great
|
||||
*P2+ community mod*, not an MVP requirement.
|
||||
|
||||
### 3.2 Body shape — *round*
|
||||
D-shape cleans corners/edges better, but the real-world advantage is "smaller than
|
||||
most people expect," and a well-engineered round robot with good side brushes
|
||||
performs as well or better in most homes
|
||||
[(source)](https://www.ecovacs.com/us/blog/round-vs-square-robot-vacuum). Round wins
|
||||
for a DIY/printable platform:
|
||||
1. Simpler to design, print, and seal.
|
||||
2. Navigates better — rotates in place, backs out the way it came (D-shape
|
||||
complicates every nav/recovery RFC).
|
||||
3. Natural fit for the spinning 2D LiDAR turret.
|
||||
4. Matches the round teardown reference we're porting.
|
||||
5. Corners are better solved by the side brush (later extendable) than by body shape.
|
||||
|
||||
*Decision: round body + fixed side brush now, extendable side brush later.*
|
||||
|
||||
---
|
||||
|
||||
## 4. Compute (SBC)
|
||||
|
||||
*v1: Raspberry Pi 5 (4 GB).* Chosen to onboard the large Raspberry Pi community —
|
||||
the biggest contributor wedge. Runs ROS2 + LiDAR SLAM + Nav2 comfortably. No on-board NPU.
|
||||
|
||||
- *ML vision option — Hailo AI HAT.* Add the RPi M.2 AI Kit (Hailo-8L, ~13 TOPS) for
|
||||
real-time camera obstacle detection: keeps the entire RPi ecosystem advantage *and*
|
||||
gains an NPU. *Check the board-stack height* — a vacuum is only ~10 cm tall and
|
||||
the M.2 HAT adds Z. Evaluate alternatives (Coral USB/M.2 accelerator, or lighter
|
||||
CPU-only models) against the height budget.
|
||||
- *Later: Rockchip RK3588 / RK3576.* Orange Pi 5, Radxa Rock 5B, Banana Pi CM5 —
|
||||
built-in *6 TOPS NPU*, RKNN toolkit, YOLO ~65 ms/image. Cheaper and integrated, but
|
||||
driver / ROS2 / kernel maturity *lags RPi*, so it's a *later* move, not the
|
||||
community-onboarding one.
|
||||
|
||||
---
|
||||
|
||||
## 5. I/O board (custom, JLCPCB)
|
||||
|
||||
No off-the-shelf DIY-vacuum I/O board exists, so we design one. It carries an *MCU
|
||||
running micro-ROS* that talks to the SBC over a fast serial / USB link: the SBC does
|
||||
ROS2 / SLAM / nav / vision; the board does real-time motor + sensor I/O.
|
||||
|
||||
*MCU:* STM32G070RBT6 (LQFP64, ~$0.93 @ 100 pcs) — cheap, lots of peripherals.
|
||||
*Pin-budget risk:* the full peripheral set below is a lot for 64 pins. Do a
|
||||
pin-allocation spreadsheet before committing. Offload the *BLDC fan to an external
|
||||
ESC* (1 PWM pin instead of in-MCU FOC) to save pins and complexity. If still tight,
|
||||
consider the STM32G0B1RET6 (same family, more peripherals/RAM) or a 100-pin part.
|
||||
|
||||
*LiDAR (3irobotix CRL-200S):* wires to the I/O board. The board *drives the LiDAR
|
||||
motor* (MOSFET + closed-loop speed control using the LiDAR's RPM feedback) and *passes
|
||||
raw LiDAR data through the MCU to the SBC* over the fast serial link. *Confirm the MCU
|
||||
UART bandwidth handles the CRL-200S data rate.*
|
||||
|
||||
Cross-checked peripheral list (your running list + `[+]` = additions to consider):
|
||||
|
||||
*Sensors*
|
||||
- 2D LiDAR (CRL-200S) — motor-driven + data passthrough to SBC
|
||||
- IR cliff / proximity / docking
|
||||
- Bumper micro-switches (+ optional optical/IR bumper)
|
||||
- Wheel encoders
|
||||
- Carpet ultrasonic sensor
|
||||
- Color camera → *connects to the SBC's CSI/USB, not this board*
|
||||
- VL53L7CX distance sensor (I²C)
|
||||
- Water tank level (full/empty)
|
||||
- Dust bin present / lid open-closed
|
||||
- Battery level / fuel gauge
|
||||
- Dock-connected / charge sense
|
||||
- IMU
|
||||
- `[+]` Wheel-drop / lift sensor (robot picked up) — distinct from cliff
|
||||
|
||||
*Motor drivers*
|
||||
- Main wheels ×2
|
||||
- Main brush
|
||||
- Side brush(es)
|
||||
- `[+]` *Mop pad spin motor(s)* — for dual-spinning pads (you listed mop *lift* but not *spin*)
|
||||
- Mop lift servo
|
||||
- Water pump (low-side MOSFET)
|
||||
- Vacuum fan (BLDC) — via *external ESC* (recommended)
|
||||
- LiDAR motor (MOSFET, closed-loop)
|
||||
|
||||
*Power*
|
||||
- Battery connector
|
||||
- Charging circuit (charge-controller IC)
|
||||
- Dock contacts
|
||||
- BMS interface
|
||||
- Current sense (per-rail and/or per-motor for stall / tangle detection)
|
||||
- DC-DC converters (5 V for SBC, 3.3 V logic, motor rails)
|
||||
- Power on/off button + soft-latch power circuit
|
||||
- `[+]` Protection: reverse-polarity, over-current fuse / eFuse, inrush limiting
|
||||
|
||||
*Audio / UI*
|
||||
- Speaker amp + connector (audio in from the SBC)
|
||||
- Mic placeholder
|
||||
- Power / status LEDs
|
||||
- Buttons (power, dock, clean)
|
||||
- `[+]` Buzzer (cheap fallback if the speaker amp is deferred)
|
||||
|
||||
*Host link:* USB or high-speed UART between MCU and SBC, carrying micro-ROS *and*
|
||||
the LiDAR passthrough — confirm bandwidth covers both.
|
||||
|
||||
> *Scope note:* the *wash/dry dock has its own controller* (ESP32 + WiFi) for its
|
||||
> pumps / heater / fan / water-level. Those are *not* on the robot I/O board.
|
||||
|
||||
---
|
||||
|
||||
## 6. Electrical / sensor BoM sketch
|
||||
|
||||
Rough *robot* BoM at prototype / low-qty China-sourcing prices (compresses at volume).
|
||||
Excludes the dock.
|
||||
|
||||
| Item | Qty | ~USD | Notes |
|
||||
|---|---|---|---|
|
||||
| Drive wheel modules | 2 | 12–27 | sourced complete module |
|
||||
| Caster wheel | 1 | 0–3 | print or ball caster |
|
||||
| Suction blower (BLDC) | 1 | 8–20 | sealed sourced motor |
|
||||
| Main brush + motor | 1 | 5–12 | tapered rubber roller |
|
||||
| Side brush + motor | 1–2 | 3–8 | |
|
||||
| Mop spin motor(s) + pads | 1–2 | 6–15 | mopping models |
|
||||
| Water pump + valve + tubing | 1 | 4–10 | mopping models |
|
||||
| Mop lift servo | 1 | 2–6 | mopping models |
|
||||
| Battery pack (~14.8 V Li-ion) + BMS | 1 | 15–30 | safety review |
|
||||
| LiDAR (CRL-200S / LDS) | 1 | 30–40 | your cost |
|
||||
| VL53L7CX ToF | 1 | 8–15 | obstacle detection |
|
||||
| Color camera | 1 | 5–15 | to SBC |
|
||||
| IMU | 1 | 2–5 | |
|
||||
| IR cliff / proximity | 3–4 | 3–8 | |
|
||||
| Bumper micro-switches | 2–3 | 1–3 | |
|
||||
| Ultrasonic carpet sensor | 1 | 2–5 | |
|
||||
| Speaker + amp, mic, LEDs, buttons | — | 3–8 | |
|
||||
| Custom I/O PCB (JLCPCB assembled, low qty) | 1 | 15–40 | |
|
||||
| Wiring, connectors, fasteners, magnets, gaskets, filter | — | 12–25 | |
|
||||
| Printed parts (filament) | — | 5–15 | |
|
||||
| *Robot subtotal (sourced parts)* | | *~$130–270* | excludes SBC |
|
||||
| Raspberry Pi 5 4 GB | 1 | ~60 | |
|
||||
| Hailo AI HAT (optional, premium vision) | 1 | ~70 | |
|
||||
|
||||
---
|
||||
|
||||
## 7. Water system
|
||||
|
||||
*Robot:* an onboard *clean-water tank* (printed) → *solenoid diaphragm pump* (what
|
||||
commercial units use) → mop head. For spinning pads, dirty water stays in the pads until
|
||||
the dock washes them.
|
||||
|
||||
*Dock tiers (where the water complexity lives):*
|
||||
- *Auto-empty:* dock fan + bin/bag suck the robot's dustbin out through a sealed port.
|
||||
The off-the-shelf-corded-vac approach works here.
|
||||
- *Wash + dry:* *clean tank + dirty tank* (printed) + *two pumps* + a wash
|
||||
tray/roller + a *hot-air blower (heater + fan)*. Wash *must* include hot-air dry —
|
||||
wash-without-dry breeds mildew/odor. Needs its *own ESP32 + WiFi controller*.
|
||||
- *Plumbing hookup:* offer as an *option* on the premium dock (pump + supply/drain
|
||||
lines + valve); default to tank-based (direct plumbing needs pro install). Skip exotic
|
||||
water-recycling (e.g. silver-ion distillation).
|
||||
- DIY parts are cheap/common: 12 V diaphragm or peristaltic pumps, solenoid valves,
|
||||
silicone tubing, float / capacitive level sensors.
|
||||
|
||||
---
|
||||
|
||||
## 8. Budget target
|
||||
|
||||
*Target: ~$100–200 sourced parts + Raspberry Pi 5 4 GB*, aiming at the capability of a
|
||||
mid-range ($500–600) commercial vacuum.
|
||||
|
||||
- *Verdict: realistic for the mechanicals + core sensors* at the low–mid end, and it
|
||||
compresses at volume. But *mopping + premium obstacle detection* (ToF + color camera
|
||||
+ an NPU accelerator) are exactly the line items that push toward / past $200 — the
|
||||
Hailo HAT alone is ~$70. So $100–200 holds for a capable vacuum; "premium vision now"
|
||||
wants a +$70-ish allowance (or defer the NPU to the Rockchip generation).
|
||||
- *Sanity check vs commercial:* a $500–600 LiDAR vacuum has roughly a *$120–180 FOB
|
||||
BoM at 5,000 MOQ*. Our low-qty numbers are coherent — we trade *no tooling/molds*
|
||||
(3D print) for *higher per-part cost* (no volume).
|
||||
- *Set builder expectations honestly:* total DIY spend (kit + RPi 5 + LiDAR + margin)
|
||||
lands *above* a commercial unit's BoM. The value proposition is *openness, local
|
||||
control, and hackability — not beating Roborock on price.*
|
||||
|
||||
---
|
||||
|
||||
## 9. Product lineup — shared base, three dock tiers
|
||||
|
||||
One robot base, three dock tiers, released in order:
|
||||
1. *Basic charging dock* (first release — simplest, MVP-aligned).
|
||||
2. *Auto-empty dock* (dust).
|
||||
3. *Auto-empty + mop-wash + hot-dry dock* (the full-service tier).
|
||||
|
||||
Notes:
|
||||
- Mirrors how commercial brands segment (same robot, dock tiers) and fits the
|
||||
swappable-module philosophy.
|
||||
- The *mop hardware (spinning pads + onboard water tank) is an add-on module* on the
|
||||
shared base — only the mopping models carry it; the base is otherwise constant.
|
||||
- The *wash+dry dock is almost its own mini-product* (pumps, heater, tanks, ESP32).
|
||||
Correctly the last and hardest deliverable after the robot itself.
|
||||
- Skip a wash-*only* dock — drying is mandatory (odor).
|
||||
|
||||
---
|
||||
|
||||
## 10. Obstacle detection strategy (premium hardware + community ML)
|
||||
|
||||
Targets the #1 user pain (getting stuck / eating cables). Strategy: *premium hardware
|
||||
now, ML maturity via community contribution over time.*
|
||||
|
||||
- *Sensors:* *VL53L7CX* multizone ToF (8×8, *90° FoV*, ~350 cm — wider FoV than the
|
||||
L5CX's 63°, better coverage) *+ color camera.* The ToF alone detects the low / small /
|
||||
cliff objects the *LiDAR is blind to*, so v1 gets real "doesn't eat cables" value
|
||||
*before* any ML matures — de-risks the feature.
|
||||
- *ML:* the *camera + model* is where the NPU matters (Hailo HAT now, Rockchip NPU
|
||||
later); real-time RGB detection won't run well on the RPi 5 CPU.
|
||||
- *Community-labeled household-obstacle dataset* is a legitimately novel contribution
|
||||
track — every contributor's home is training data. Treat it as a first-class
|
||||
contribution track alongside the RFCs; it doubles as community-building.
|
||||
|
||||
---
|
||||
|
||||
## 11. Still to research / open decisions
|
||||
|
||||
- *Battery:* chemistry (Li-ion 3S/4S?), specific cells + BMS, charge profile — *safety review*.
|
||||
- *BLDC fan ESC:* select an off-the-shelf ESC vs in-MCU drive.
|
||||
- *Filter:* pick a common, abundant filter size as the interface standard.
|
||||
- *Gasket sourcing* vs TPU printing for airflow seals.
|
||||
- *Dock docking signal:* IR beacon protocol / fiducial / reflective marker.
|
||||
- *Fastener / heat-set insert standard* (ties to ARCHITECTURE §5.2).
|
||||
- *Confirm:* MCU pin budget, host-link bandwidth (micro-ROS + LiDAR passthrough), Hailo stack height.
|
||||
- Remaining mechanical parts not yet covered above.
|
||||
Reference in New Issue
Block a user