120 lines
4.9 KiB
Markdown
120 lines
4.9 KiB
Markdown
# Firmware simulation (software-in-the-loop)
|
||||
|
|
|
|||
|
|
This folder compiles the **unchanged** robot firmware (`robot/*.cpp` plus
|
|||
|
|
`robot.ino`) into a Windows DLL. The Unity project
|
|||
|
|
(`Hexapod_Simulation_Unity/`) runs that DLL, so the code you test in the
|
|||
|
|
simulation is exactly the code you flash to the Teensy. `robot/` has no
|
|||
|
|
`#ifdef SIM` anywhere.
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
robot/ (unchanged) ──► PlatformIO + Teensy core ──► Teensy 4.0 firmware
|
|||
|
|
└─► CMake + g++ + sim/shim/ ──► sim/build/hexapod_sim.dll ──► Unity
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## How it works
|
|||
|
|
|
|||
|
|
`shim/` provides PC stand-ins for the only hardware the firmware touches:
|
|||
|
|
|
|||
|
|
| Firmware uses | Simulated as |
|
|||
|
|
|---|---|
|
|||
|
|
| `Servo::writeMicroseconds()` | the pulse stored per pin, decoded back to angles with `Config.h`'s `SERVO_US_*` |
|
|||
|
|
| `Wire` + MPU6050 registers | a register-level fake MPU6050 at 0x68, fed from the simulated body's tilt and rotation |
|
|||
|
|
| `RF24` | a simulated radio: packets are received only if channel, address, data rate and **payload size** match, and the RX FIFO holds 3 |
|
|||
|
|
| `millis()` / `micros()` / `Serial` | a simulated clock that Unity advances, and captured serial I/O |
|
|||
|
|
|
|||
|
|
`robot_ino.cpp` compiles `robot.ino` itself and exposes read-only views of
|
|||
|
|
its globals (state machine, heading, IMU filter, gait phase) for status
|
|||
|
|
display. `HexapodSimApi.h` is the C API. Its forward kinematics is written
|
|||
|
|
separately from `robot/Kinematics.cpp` on purpose, so it can catch IK
|
|||
|
|
mistakes.
|
|||
|
|
|
|||
|
|
## Build
|
|||
|
|
|
|||
|
|
Needs a 64-bit g++ and CMake. Strawberry Perl's `C:\Strawberry\c\bin` has
|
|||
|
|
g++, cmake and ninja, and the script falls back to it automatically. From
|
|||
|
|
the repo root in PowerShell:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
.\sim\build.ps1 # configure (first time) + build + smoke test
|
|||
|
|
.\sim\build.ps1 -NoTest # build only
|
|||
|
|
.\sim\build.ps1 -Clean # rebuild from scratch
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
To run the steps by hand, use separate lines. Windows PowerShell 5.1 has
|
|||
|
|
no `&&`:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
cmake -S sim -B sim\build -G Ninja
|
|||
|
|
cmake --build sim\build
|
|||
|
|
.\sim\build\sim_smoke.exe
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Rebuild after any change in `robot/` or `shared/`. In Unity, press **Play**
|
|||
|
|
(or **Reload firmware** in the panel) to pick up the new DLL. Unity copies
|
|||
|
|
the DLL before loading it, so it never locks the build output and you
|
|||
|
|
don't need to restart the editor.
|
|||
|
|
|
|||
|
|
## Headless smoke test (`tests/sim_smoke.cpp`)
|
|||
|
|
|
|||
|
|
It drives the firmware the way the remote would and checks:
|
|||
|
|
- disarmed at power-on,
|
|||
|
|
- arm lands in SIT,
|
|||
|
|
- stand reaches STAND,
|
|||
|
|
- **IK gives valid solutions and the feet end up at `STANCE_HEIGHT`**,
|
|||
|
|
- `FLAG_WALK` leads to WALKING,
|
|||
|
|
- **`+vx` moves the body forward**,
|
|||
|
|
- link loss disarms,
|
|||
|
|
- a wrong-size packet is ignored,
|
|||
|
|
- the serial harness still works.
|
|||
|
|
|
|||
|
|
## Using it in Unity
|
|||
|
|
|
|||
|
|
Open `Hexapod_Simulation_Unity` and press Play in `Main.unity`. No scene
|
|||
|
|
edits are needed: `HexapodSimBootstrap` adds a `HexapodSimulation` object,
|
|||
|
|
disables the old movement scripts (`Walk`, `RotationControl`, `Leg`,
|
|||
|
|
`HandleHoystick`), and binds the existing leg rig by name (`Leg` → `hip1`
|
|||
|
|
/ `Hip2` / `Knie`). The on-screen panel (F1 toggles it) offers three input
|
|||
|
|
sources:
|
|||
|
|
|
|||
|
|
- **Joystick:** the real remote as a USB controller, on input axes
|
|||
|
|
`"1"`–`"6"` like the old `HandleHoystick`. Axis assignment and inversion
|
|||
|
|
are set on the `SimRemote` component. The panel shows the raw axes so you
|
|||
|
|
can fix them.
|
|||
|
|
- **Keyboard:** test without a remote. WASD walks, Q/E turns, IJKL + U/O
|
|||
|
|
drive the right stick, 1–7 flip the toggles, Backspace disarms, +/-
|
|||
|
|
change speed.
|
|||
|
|
- **Robot serial:** no radio at all. Keystrokes go to `robot.ino`'s own
|
|||
|
|
`TEST_SERIAL_INPUT` harness.
|
|||
|
|
|
|||
|
|
The panel also has every remote toggle, the speed slider, simulated
|
|||
|
|
handset tilt (remote gyro), a **ground tilt** slider (to test the robot
|
|||
|
|
gyro), and live firmware state, joint angles, foot contact, the packet on
|
|||
|
|
air and robot serial output.
|
|||
|
|
|
|||
|
|
The body moves **kinematically**, computed in the DLL (`SimBody.cpp`), so
|
|||
|
|
Unity shows what the smoke test checks:
|
|||
|
|
- The body rests on the support plane of its feet, the plane all feet are
|
|||
|
|
on or above.
|
|||
|
|
- Grounded feet don't slide.
|
|||
|
|
- The simulated MPU6050 reads that body's tilt and rotation rate.
|
|||
|
|
|
|||
|
|
The sphere at each foot (`Point`) is placed by the DLL's forward
|
|||
|
|
kinematics. If it doesn't sit on the tibia tip, the rig and `Config.h`
|
|||
|
|
disagree.
|
|||
|
|
|
|||
|
|
Setup that lives in the Inspector on `HexapodSimulation`:
|
|||
|
|
- `forwardAxis`: robot forward = Unity −X by default, taken from the old
|
|||
|
|
scene's `invertForLeftLeg` naming.
|
|||
|
|
- `unitsPerMm`: 0.1, since the model is in cm.
|
|||
|
|
- `kneeRestDeg`: −90, the rig's knee pose at rotation 0.
|
|||
|
|
|
|||
|
|
For a custom setup instead of the bootstrap, add a `HexapodSimulation`
|
|||
|
|
component to the scene yourself.
|
|||
|
|
|
|||
|
|
## What is not simulated
|
|||
|
|
|
|||
|
|
- Physics (mass, falling, slipping, servo torque and speed).
|
|||
|
|
- Servo calibration. The sim decodes pulses with `Config.h`'s single
|
|||
|
|
`SERVO_US_AT_0DEG` / `SERVO_US_PER_DEG`, i.e. what the firmware *intends*,
|
|||
|
|
not per-servo offsets or directions.
|
|||
|
|
- The remote firmware. `SimRemote.cs` mirrors its packet rules instead.
|