Draft: shared protocol, remote firmware, project docs
Not yet verified on hardware. - shared/HexapodProtocol: CmdPacket (now 20 bytes, adds remote_roll / remote_pitch), FLAG_* bits and radio settings as the single protocol definition; static payload size = sizeof(CmdPacket) on both radios. - robot: use the shared protocol; vx = forward (harness w/x <-> q/e); InputMapper adds remote tilt on top of the right stick when FLAG_REMOTE_GYRO is set, and robot.ino no longer zeroes stick roll/pitch when it's off. - remote (Teensy 4.1): Sticks (calibration, EEPROM), RemoteIMU, RemoteState (interlocks, link-loss disarm, smoothing), PacketBuilder, RadioLink, NextionUI, SerialConsole. - CLAUDE.md, README.md, remote/README.md, remote/NEXTION.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ad77b06c36
commit
91dac16fca
30 files changed
+1706
-57
No files matched your search
@@ -0,0 +1,174 @@
|
||||
# Hexapod: project memory
|
||||
|
||||
Hexapod walking robot plus a custom handheld remote, both Teensy-based and
|
||||
linked one-way over nRF24L01 (remote → robot, with hardware auto-ack only).
|
||||
This file is the persistent context for working on either side. Read it
|
||||
fully before changing anything.
|
||||
|
||||
## Layout and toolchain
|
||||
|
||||
PlatformIO, one `platformio.ini`, two environments:
|
||||
|
||||
| Env | Board | Sketch dir | Purpose |
|
||||
|-----|-------|------------|---------|
|
||||
| `robot` | `teensy40` | `robot/` | walking robot |
|
||||
| `remote` | `teensy41` | `remote/` | handheld transmitter |
|
||||
|
||||
- `shared/` is PlatformIO's `lib_dir`. `shared/HexapodProtocol/` is a
|
||||
header-only library both sketches `#include <HexapodProtocol.h>`.
|
||||
- PlatformIO has a single global `src_dir` and only converts `.ino` files at
|
||||
its root, so `scripts/select_src_dir.py` (a `pre:` extra script) swaps
|
||||
`PROJECT_SRC_DIR` to each env's `custom_src_dir`. That keeps `robot/` and
|
||||
`remote/` plain Arduino sketch folders: one `.ino` plus paired `.h/.cpp`
|
||||
per concern. **Keep that structure**. No giant files, no `src/` folders.
|
||||
- RF24 (nrf24/RF24, resolved 1.6.x) comes from `lib_deps`. Servo, Wire,
|
||||
SPI and EEPROM ship with the Teensy framework.
|
||||
- Build: `pio run` (both), `pio run -e robot`, `pio run -e remote`. On this
|
||||
machine pio lives at `C:\Users\patri\.platformio\penv\Scripts\pio.exe`.
|
||||
Both envs build clean. Build both after touching `shared/`.
|
||||
|
||||
## Communication contract (`shared/HexapodProtocol/HexapodProtocol.h`)
|
||||
|
||||
`CmdPacket`, 20 bytes (static_asserted), sent remote → robot at 100 Hz
|
||||
(robot `CONTROL_HZ` is 60):
|
||||
|
||||
| Field | Range | Meaning |
|
||||
|-------|-------|---------|
|
||||
| `vx` | ±1000 | + forward |
|
||||
| `vy` | ±1000 | + strafe **left** |
|
||||
| `yaw_rate` | ±1000 | + turn left (CCW). Ignored by the robot in mode B |
|
||||
| `rx, ry, rz` | ±1000 | right stick: roll/pitch/yaw, or X/Y/Z offset when `FLAG_RSTICK_XLATE` |
|
||||
| `remote_roll, remote_pitch` | ±1000 | handset tilt feedforward, used only when `FLAG_REMOTE_GYRO` |
|
||||
| `speed_scale` | 0–255 | multiplies vx/vy on the robot |
|
||||
| `flags` | bits | `ARMED, STAND, WALK, MODE_B, RSTICK_XLATE, GAIT_WAVE, REMOTE_GYRO, ROBOT_GYRO` (bits 0–7) |
|
||||
| `seq` | 0–255 | increments per packet (robot doesn't check it yet) |
|
||||
|
||||
- **Axis convention** (ROS REP-103 style): body +X forward, +Y left, +Z up.
|
||||
+roll = right side down, +pitch = nose down, +yaw = CCW.
|
||||
- **Normalized fields.** The remote never knows robot geometry. The robot's
|
||||
`InputMapper` owns the scaling: `MAX_LINEAR_MM_S` 150,
|
||||
`MAX_TURN_RATE_DEG_S` 90, `MAX_TILT_DEG` 20, `MAX_POSE_OFFSET_MM` 40.
|
||||
- **Radio settings** live in the same header: address `"HXPD1"`, channel
|
||||
96, 250 kbps, PA LOW, **static payload size = sizeof(CmdPacket)** on both
|
||||
radios. That makes version mismatches fail closed: packets are not
|
||||
received, the robot times out, and it disarms.
|
||||
- **Changing the protocol means reflashing both boards.** Keep it ≤ 32
|
||||
bytes and update the `static_assert`.
|
||||
- **Failsafes:** the robot sets `linkOk()` false after `LINK_TIMEOUT_SEC`
|
||||
(0.3 s) without packets and then uses a default `CmdPacket` (flags 0 =
|
||||
disarmed, servos detached). The remote disarms itself after
|
||||
`LINK_LOST_MS` (300 ms) without acks, so the robot can't re-arm itself
|
||||
when the link returns.
|
||||
- **Division of labor:** the remote only fills fields per this contract.
|
||||
Mode A/B interpretation, heading-hold, pose scaling and all smoothing of
|
||||
pose happen on the robot. Don't duplicate `InputMapper` logic in the
|
||||
remote.
|
||||
|
||||
## Robot architecture (`robot/`, Teensy 4.0)
|
||||
|
||||
Loop at `CONTROL_HZ` (60): comms → InputMapper → IMU → heading integrate →
|
||||
StateMachine → gait/sit-stand targets → BodyPose → Limits clamp → IK →
|
||||
joint-rate clamp → servos.
|
||||
|
||||
| Module | Role |
|
||||
|--------|------|
|
||||
| `robot.ino` | setup/loop wiring, open-loop heading estimate, sit/stand blend, `TEST_SERIAL_INPUT` harness |
|
||||
| `Config.h` | **all placeholder geometry/pins/rates** |
|
||||
| `SmoothedValue.h` | rate-limited setpoint smoother used for every discrete command |
|
||||
| `Kinematics` | per-leg 3-DOF analytic IK, `neutralStance()` |
|
||||
| `GaitEngine` | tripod/wave patterns, per-leg phase, pattern swap only at touchdown |
|
||||
| `BodyPose` | 6-axis body pose (smoothed command + IMU roll/pitch correction) applied to foot targets |
|
||||
| `RobotIMU` | MPU6050 complementary filter (α 0.98), gated by `FLAG_ROBOT_GYRO` |
|
||||
| `StateMachine` | DISARMED → SIT → STAND → WALKING, sit/stand blend at 1/s. Disarm is instant |
|
||||
| `InputMapper` | CmdPacket → Twist + PoseCommand + mode flags. Mode B = heading-hold from stick angle. Adds remote tilt on top of stick pose when `FLAG_REMOTE_GYRO`, clamped to `MAX_TILT_DEG` |
|
||||
| `Limits` | workspace clamp (70 mm horiz / 60 mm vert), joint rate clamp, ≥3-legs-in-stance check (not acted on yet) |
|
||||
| `Comms` | nRF24 RX, link timeout, `injectPacket()` for the harness |
|
||||
| `ServoDriver` | attach/detach/write, skips invalid IK solutions |
|
||||
|
||||
## Remote architecture (`remote/`, Teensy 4.1)
|
||||
|
||||
Loop: drain Nextion + USB serial input every pass. On each `TX_HZ` (100)
|
||||
tick: sticks → IMU → radio link status → RemoteState (smoothers,
|
||||
link-loss disarm) → PacketBuilder → `radio.write` → Nextion render.
|
||||
|
||||
| Module | Role |
|
||||
|--------|------|
|
||||
| `remote.ino` | loop wiring, status print, packet dump |
|
||||
| `Config.h` | **all placeholder pins, calibration defaults, deadzones, rates** |
|
||||
| `UiTypes.h` | `UiCmd`/`UiEvent` (operator requests), `UiView` (what the display shows) |
|
||||
| `Sticks` | 6 analog axes: ADC averaging + IIR, piecewise calibration around center, invert, deadzone with rescale, expo → ±1000. Boot-time center capture, sweep calibration saved to EEPROM (magic/version/checksum) |
|
||||
| `RemoteIMU` | MPU6050, same complementary filter as the robot, axis remap for mounting, boot gyro-bias capture, "level" zero |
|
||||
| `RemoteState` | authoritative operator state and interlocks: arm needs centered sticks, disarm clears stand, stand needs armed, calibrate needs disarmed, link-loss auto-disarm. Smooths speed scale and fades remote tilt in/out |
|
||||
| `PacketBuilder` | the inverse of InputMapper: stick frame → body frame per the contract, per-submode right-stick mapping, `FLAG_WALK` with 250 ms linger (twist ignored in mode B), tilt deadzone/scale × fade, zeros motion when disarmed |
|
||||
| `RadioLink` | nRF24 TX, short retries (1, 3), ack-based link status and 32-packet quality % |
|
||||
| `NextionUI` | library-free Nextion driver. HMI sends `'#' len cmd [val]` frames via `printh`. Remote pushes state by component name with diffing, a 2 s full refresh, and a redraw on the Nextion 0x88 "ready" frame |
|
||||
| `SerialConsole` | USB keystrokes that mirror every UI command plus diagnostics |
|
||||
| `SmoothedValue.h` | copy of the robot's (kept separate so robot/ stays untouched) |
|
||||
| `NEXTION.md` | HMI build guide: component names, txt_maxl, touch event code |
|
||||
|
||||
Remote stick-frame convention: logical X + = right, Y + = forward, twist +
|
||||
= CCW. The mapping into the packet is in `PacketBuilder.cpp` (e.g.
|
||||
`vy = -L_X`, translate mode `ry = -R_X`).
|
||||
|
||||
## Testing without the other board
|
||||
|
||||
- **Robot without remote:** `#define TEST_SERIAL_INPUT` in `robot.ino` (on
|
||||
by default). Serial 115200, single keys: `a` arm, `s` sit/stand, `w/x`
|
||||
vx ±200, `q/e` vy ±200 (left/right), `z/c` yaw_rate ∓/±200, `g` gait,
|
||||
`m` mode, space = zero velocity. Keys inject through
|
||||
`Comms::injectPacket()`, the same path radio packets take. Order: arm →
|
||||
stand → walk. The harness does not exercise the right stick or remote
|
||||
tilt. Add keys if needed.
|
||||
- **Remote without robot or display:** the USB serial console (see
|
||||
`remote/README.md`). `p` shows raw/mapped sticks, IMU, link and state.
|
||||
`d` dumps packets at 10 Hz. With no robot, the link shows DOWN and
|
||||
arming still works; the link-loss disarm only fires after the link was
|
||||
up once.
|
||||
|
||||
## Placeholders and bench-check items (not yet verified on hardware)
|
||||
|
||||
Robot (`robot/README.md` has detail):
|
||||
- All of `Config.h`: leg mount angles/positions, link lengths, joint
|
||||
limits, stance radius/height, servo pins, µs/deg calibration,
|
||||
`MAX_JOINT_DEG_S` (in robot.ino), Limits envelope, InputMapper maxima.
|
||||
- **Pin conflict:** `SERVO_PINS` leg 3 uses pins 11 and 12, which are
|
||||
Teensy 4.0 SPI MOSI/MISO needed by the nRF24. Must be moved. Some servo
|
||||
pins (26, 27, 30…) may also not be PWM-capable on the 4.0. Verify.
|
||||
- IK knee direction (`tibia_rad` sign), BodyPose roll/pitch sign, IMU
|
||||
register map, and the sit pose assuming feet stay grounded.
|
||||
- **Suspected gait direction inversion:** in `GaitEngine::update` the
|
||||
stance phase moves the foot from `neutral - half` to `neutral + half`,
|
||||
i.e. with the commanded direction. That pushes the body *backward* for
|
||||
+vx, and the same happens for yaw. Fix = swap start/end. Flagged to the
|
||||
owner, **not changed**. The harness `z`/`c` yaw keys currently rely on
|
||||
this behavior and need rechecking after the fix.
|
||||
- Heading for mode B is open-loop (integrated commanded ω). Fusing IMU yaw
|
||||
is a planned upgrade.
|
||||
- `Limits::stancePolygonOk()` is computed but not acted on.
|
||||
|
||||
Remote (`remote/README.md` has detail):
|
||||
- All pins in `remote/Config.h` (radio CE 9 / CSN 10, sticks A0–A3/A6/A7,
|
||||
MPU6050 on 18/19, Nextion on Serial2 7/8).
|
||||
- `STICK_INVERT[]`, calibration defaults, deadzones, expo.
|
||||
- `IMU_AXIS_FROM_CHIP` / `IMU_AXIS_SIGN` depend on how the MPU6050 is
|
||||
mounted in the handset.
|
||||
- The Nextion must have `bauds=115200` in Program.s, and the HMI must match
|
||||
`NEXTION.md`. No `.HMI` file is in the repo yet. The compiled `.tft` is
|
||||
gitignored.
|
||||
- Radio retry timing (`RADIO_RETRY_*`) is untested under real link loss.
|
||||
|
||||
## Working rules for this repo
|
||||
|
||||
- The **robot side is owner-verified code**. Don't edit `robot/` unless
|
||||
something is factually wrong relative to the protocol, and ask the owner
|
||||
first even then. Flag suspected robot bugs instead of fixing them.
|
||||
- Protocol changes go in `shared/HexapodProtocol/` only. Update the size
|
||||
`static_assert`, this file's contract table, and both READMEs.
|
||||
- Match the existing style: config constants at the top of `Config.h`
|
||||
with BENCH comments for anything hardware-dependent, `SmoothedValue`
|
||||
rate limiting on any setpoint that can jump, and paired `.h/.cpp` per
|
||||
concern.
|
||||
- Planned or possible next steps: an ack-payload telemetry channel from
|
||||
robot → remote (state, battery, link) to show real robot state on the
|
||||
Nextion, battery monitoring on both sides, a `.HMI` file checked into
|
||||
`remote/`.
|
||||
Reference in new issue
Block a user