11 Commits

Author SHA1 Message Date
f71dad5344 feat(arch): migrate all STM32/Mamba/BlackPill refs to ESP32 BALANCE/IO + fix roslib@1.4.0
Architecture change (2026-04-03): Mamba F722S (STM32F722) and BlackPill
replaced by ESP32 BALANCE (PID loop) and ESP32 IO (motors/sensors/comms).

- Update CLAUDE.md, docs, chassis BOM/ASSEMBLY, pinout, power-budget,
  wiring-diagram, TEAM.md, AUTONOMOUS_ARMING.md, docker-compose
- Update all ROS2 package comments, config labels, launch args
  (stm32_port→esp32_port, /dev/stm32-bridge→/dev/esp32-bridge)
- Update WebUI: stm32Mode→esp32Mode, stm32Version→esp32Version,
  "STM32 State/Mode" labels → "ESP32 State/Mode" (ControlMode, SettingsPanel)
- Add TODO(esp32-migration) markers on stm32_protocol.py and mamba_protocol.py
  binary frame layouts — pending ESP32 protocol spec from max
- Fix roslib CDN 1.3.0→1.4.0 in all 11 HTML panels (fixes ROS2 Humble
  rosbridge "Received a message without an op" incompatibility)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 08:25:24 -04:00
a9b2242a2c feat(social): Orin dev environment — JetPack 6 + TRT conversion + systemd (#88)
- Dockerfile.social: social-bot container with faster-whisper, llama-cpp-python
  (CUDA), piper-tts, insightface, pyannote.audio, OpenWakeWord, pyaudio
- scripts/convert_models.sh: TRT FP16 conversion for SCRFD-10GF, ArcFace-R100,
  ECAPA-TDNN; CTranslate2 setup for Whisper; Piper voice download; benchmark suite
- config/asound.conf: ALSA USB mic (card1) + USB speaker (card2) config
- models/README.md: version-pinned model table, /models/ layout, perf targets
- systemd/: saltybot-social.service + saltybot.target + install_systemd.sh
- docker-compose.yml: saltybot-social service with GPU, audio device passthrough,
  NVMe volume mounts for /models and /social_db

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 08:08:57 -05:00
d41a9dfe10 feat(safety): remote e-stop over 4G MQTT (Issue #63)
STM32 firmware:
- safety.h/c: EstopSource enum, safety_remote_estop/clear/get/active()
  CDC 'E'=ESTOP_REMOTE, 'F'=ESTOP_CELLULAR_TIMEOUT, 'Z'=clear latch
- usbd_cdc_if: cdc_estop_request/cdc_estop_clear_request volatile flags
- status: status_update() +remote_estop param; both LEDs fast-blink 200ms
- main.c: immediate motor cutoff highest-priority; arming gated by
  !safety_remote_estop_active(); motor estop auto-clear gated; telemetry
  'es' field 0-4; status_update() updated to 5 args

Safety: IMMEDIATE motor cutoff, latched until explicit Z + DISARMED,
cannot re-arm via MQTT alone (requires RC arm hold). IWDG-safe.

Jetson bridge:
- remote_estop_node.py: paho-mqtt + pyserial, cellular watchdog 5s
- estop_params.yaml, remote_estop.launch.py
- setup.py / package.xml: register node + paho-mqtt dep
- docker-compose.yml: remote-estop service
- test_remote_estop.py: kill/clear/watchdog/latency unit tests

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 04:55:54 -05:00
e0987fcec8 feat: outdoor nav — OSM routing + GPS waypoints + geofence (#59)
Implements Phase 2d outdoor autonomous navigation for SaltyBot.
GPS source: SIM7600X /gps/fix from PR #65 (saltybot_cellular).

saltybot_outdoor package:
- osm_router_node: Overpass API + A* haversine graph + Douglas-Peucker
  simplification, /outdoor/route (Path) + /outdoor/waypoints (PoseArray)
- gps_waypoint_follower_node: GPS->Nav2 navigate_through_poses bridge,
  quality-adaptive tolerances (2m cellular / 0.30m RTK)
- geofence_node: ray-casting polygon safety, emergency stop on violation
- outdoor_nav.launch.py: dual-EKF + navsat_transform + all nodes
- outdoor_nav_params.yaml: 1.5m/s, no static_layer, 2m GPS tolerance
- ekf_outdoor.yaml: robot_localization dual-EKF + navsat_transform
- geofence_vertices.yaml: template with usage instructions

docker-compose.yml: fix malformed saltybot-surround block; add
saltybot-outdoor service (depends on saltybot-nav2, OSM NVMe cache)

SLAM-SETUP-PLAN.md: Phase 2d done

RTK upgrade: SIM7600X (+-2.5m) -> ZED-F9P (+-2cm), set use_rtk:=true

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 00:52:54 -05:00
6420e07487 feat: rosbridge WebSocket server for web UI (port 9090)
Adds rosbridge_suite to the Jetson stack so the browser dashboard can
subscribe to ROS2 topics via roslibjs over ws://jetson:9090.

docker-compose.yml
  New service: saltybot-rosbridge
  - Runs saltybot_bringup/launch/rosbridge.launch.py
  - network_mode: host → port 9090 directly reachable on Jetson LAN
  - Depends on saltybot-ros2, stm32-bridge, csi-cameras

saltybot_bringup/launch/rosbridge.launch.py
  - rosbridge_websocket node (port 9090, params from rosbridge_params.yaml)
  - 4× image_transport/republish nodes: compress CSI camera streams
    /camera/<name>/image_raw → /camera/<name>/image_raw/compressed (JPEG 75%)

saltybot_bringup/config/rosbridge_params.yaml
  Whitelisted topics:
    /map  /scan  /tf  /tf_static
    /saltybot/imu  /saltybot/balance_state
    /cmd_vel
    /person/*
    /camera/*/image_raw/compressed
  max_message_size: 10 MB (OccupancyGrid headroom)

saltybot_bringup/SENSORS.md
  Added rosbridge connection section with roslibjs snippet,
  topic reference table, bandwidth estimates, and throttle_rate tips.

saltybot_bringup/package.xml
  Added exec_depend: rosbridge_server, image_transport,
  image_transport_plugins (all already installed in Docker image).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 00:22:02 -05:00
dc01efe323 feat: 4x IMX219 surround vision + Nav2 camera obstacle layer (Phase 2c)
New ROS2 package saltybot_surround:

surround_costmap_node
  - Subscribes to /camera/{front,left,rear,right}/image_raw
  - Detects obstacles via Canny edge detection + ground projection
  - Pinhole back-projection: pixel row → forward distance (d = h*fy/(v-cy))
  - Rotates per-camera points to base_link frame using known camera yaws
  - Publishes /surround_vision/obstacles (PointCloud2, 5 Hz)
  - Catches chairs, glass walls, people that RPLIDAR misses
  - Placeholder IMX219 fisheye calibration (hook for real cal via cv2.fisheye)

surround_vision_node
  - IPM homography computed from camera height + pinhole model
  - 4× bird's-eye patches composited into 240×240px 360° overhead view
  - Publishes /surround_vision/birdseye (Image, 10 Hz)
  - Robot footprint + compass overlay

surround_vision.launch.py
  - Launches both nodes with surround_vision_params.yaml
  - start_cameras arg: set false when csi-cameras container runs separately

Updated:
- jetson/config/nav2_params.yaml   add surround_cameras PointCloud2 source
                                    to local + global costmap obstacle_layer
- jetson/docker-compose.yml        add saltybot-surround service
                                    (depends_on: csi-cameras, start_cameras:=false)
- projects/saltybot/SLAM-SETUP-PLAN.md  Phase 2c  Done

Calibration TODO (run after hardware assembly):
  ros2 run camera_calibration cameracalibrator --size 8x6 --square 0.025 \
    image:=/camera/front/image_raw camera:=/camera/front
  Replace placeholder K/D in surround_costmap_node._undistort()

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28 23:19:23 -05:00
3755e235aa feat: Orin Nano Super platform update + 4x IMX219 CSI cameras
Task A — Orin Nano Super platform update:
- docker-compose.yml: update header/comments, switch all service image tags
  to jetson-orin, update devices to udev symlinks (/dev/rplidar,
  /dev/stm32-bridge, i2c-7), add NVMe volume mounts (/mnt/nvme/saltybot),
  update stm32-bridge to saltybot_bridge launch, add csi-cameras service
- docs/pinout.md: full rewrite for Orin Nano Super — i2c-7, ttyTHS0,
  CSI-A/B connectors, M.2 NVMe slot, IMX219 15-pin FFC pinout, V4L2 nodes,
  GStreamer test commands, updated udev rules
- docs/power-budget.md: full rewrite — 25W TDP, 8GB LPDDR5, 67 TOPS,
  4-camera CSI bandwidth analysis, nvpmodel modes, Nano vs Orin comparison,
  5V 6A PSU recommendation, 4S LiPo architecture
- scripts/setup-jetson.sh: full rewrite — JetPack 6 / Ubuntu 22.04,
  nvidia-container-toolkit new keyring method, NVMe partition/format/fstab,
  CSI driver check (imx219 modprobe), video group, jtop install, 8GB swap

Task B — saltybot_cameras ROS2 package:
- launch/csi_cameras.launch.py: 4x v4l2_camera nodes, namespace per camera
  (front/left/rear/right), 640x480x30fps, includes TF launch automatically
- launch/camera_tf.launch.py: static TF for 4 cameras at 90deg intervals
  on sensor_head_link (r=5cm offset), yaw 0/90/180/-90 deg
- package.xml, setup.py, setup.cfg, __init__.py, resource marker
- config/cameras_params.yaml: per-camera device/frame/offset configuration

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28 22:59:13 -05:00
772a70b545 feat: Nav2 path planning + obstacle avoidance (Phase 2b)
Integrates Nav2 autonomous navigation stack with RTAB-Map SLAM on Orin
Nano Super. No AMCL/map_server needed — RTAB-Map provides /map + TF.

New files:
- jetson/config/nav2_params.yaml                           DWB controller,
  NavFn planner, RPLIDAR obstacle layer, RealSense voxel layer;
  10Hz local / 5Hz global costmap; robot_radius 0.15m, max_vel 1.0 m/s
- jetson/ros2_ws/src/saltybot_bringup/launch/nav2.launch.py
  wraps nav2_bringup navigation_launch with saltybot params + BT XML
- jetson/ros2_ws/src/saltybot_bringup/behavior_trees/
    navigate_to_pose_with_recovery.xml  BT: replan@1Hz, DWB follow,
    recovery: clear maps → spin 90° → wait 5s → back up 0.30m

Updated:
- jetson/docker-compose.yml             add saltybot-nav2 service
                                        (depends_on: saltybot-ros2)
- jetson/ros2_ws/src/saltybot_bringup/setup.py   install behavior_trees/*.xml
- jetson/ros2_ws/src/saltybot_bringup/package.xml add rtabmap_ros + nav2_bringup
- projects/saltybot/SLAM-SETUP-PLAN.md  Phase 2b  Done

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28 22:54:24 -05:00
c5d6a72d39 feat: update SLAM stack for Jetson Orin Nano Super (67 TOPS, JetPack 6)
Platform upgrade: Jetson Nano 4GB → Orin Nano Super 8GB (March 1, 2026)
All Nano-era constraints removed — power/rate/resolution limits obsolete.

Dockerfile: l4t-jetpack:r36.2.0 (JetPack 6 / Ubuntu 22.04 / CUDA 12.x),
  ROS2 Humble via native apt, added ros-humble-rtabmap-ros,
  ros-humble-v4l2-camera for future IMX219 CSI (Phase 2c)

New: slam_rtabmap.launch.py — Orin primary SLAM entry point
  RTAB-Map with subscribe_scan (RPLIDAR) + subscribe_rgbd (D435i)
  Replaces slam_toolbox as docker-compose default

New: config/rtabmap_params.yaml — Orin-optimized
  DetectionRate 10Hz, MaxFeatures 1000, Grid/3D true,
  TimeThr 0 (no limit), Mem/STMSize 0 (unlimited)

Updated: config/realsense_d435i.yaml — 848x480x30, pointcloud enabled
Updated: config/slam_toolbox_params.yaml — 10Hz rate, 1s map interval
Updated: SLAM-SETUP-PLAN.md — full rewrite for Orin: arch diagram,
  Phase 2c IMX219 plan (4x 160° CSI surround), 25W power budget

docker-compose.yml: image tag jetson-orin, default → slam_rtabmap.launch.py

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28 21:46:27 -05:00
76067d6d89 feat(bd-a2j): RealSense D435i + RPLIDAR A1M8 ROS2 driver integration
Adds saltybot_bringup ROS2 package with four launch files:
  - realsense.launch.py  — D435i at 640x480x15fps, IMU unified topic
  - rplidar.launch.py    — RPLIDAR A1M8 via /dev/rplidar udev symlink
  - sensors.launch.py    — both sensors + static TF (base_link→laser/camera)
  - slam.launch.py       — sensors + slam_toolbox online_async (compose entry point)

Sensor config YAMLs (mounted at /config/ in container):
  - realsense_d435i.yaml  — Nano power-budget settings (15fps, no pointcloud)
  - rplidar_a1m8.yaml     — Standard scan mode, 115200 baud, laser frame
  - slam_toolbox_params.yaml — Nano-tuned (2Hz processing, 5cm resolution)

Fixes docker-compose volume mount: ./ros2_ws/src:/ros2_ws/src
(was ./ros2_ws:/ros2_ws/src — would have double-nested the src directory)

Topic reference and verification commands in SENSORS.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28 17:14:21 -05:00
c47ac41573 feat: Jetson Nano platform setup and Docker env (bd-1hcg)
- Dockerfile: L4T R32.6.1 (JetPack 4.6) base + ROS2 Humble + SLAM stack
  (slam_toolbox, Nav2, rplidar_ros, realsense2_camera, robot_localization)
- docker-compose.yml: multi-service stack (ROS2, RPLIDAR A1M8, D435i, STM32 bridge)
  with device passthrough, host networking for DDS, persistent map volume
- docs/pinout.md: full GPIO/I2C/UART pinout for STM32F722 bridge (USB CDC +
  UART fallback), RealSense D435i (USB3), RPLIDAR A1M8, udev rules
- docs/power-budget.md: 10W envelope analysis with per-component breakdown,
  mitigation strategies (RPLIDAR gating, D435i 640p, nvpmodel modes)
- scripts/setup-jetson.sh: host one-shot setup (Docker, nvidia-container-runtime,
  udev rules, MAXN power mode, swap)
- scripts/build-and-run.sh: build/up/down/shell/slam/status helper

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28 12:46:14 -05:00