File size: 11,624 Bytes
15b8951
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
# Unitree Go2 β€” ROS 2 Navigation, Mapping and Low-level Control & Basic Vision-Language-Action

A ROS 2 workspace for the Unitree Go2 quadruped that pairs a classic navigation
stack (SLAM Toolbox mapping + Nav2) with a **Vision-Language-Action (VLA)** layer:
the robot can be driven by typing a natural-language instruction instead of a
joystick or a Nav2 goal. Two interchangeable VLA brains are included β€” a local
**Qwen2.5-VL** reasoning loop and **NaVILA** (VILA-8B), a model purpose-trained
for language-guided navigation.

|Systems|ROS2 distro|Build status|
|--|--|--|
|Ubuntu 24.04|jazzy|![ROS2 CI](https://github.com/abizovnuralem/go2_ros2_sdk/actions/workflows/ros_build.yaml/badge.svg)|


## Repository map

| Path | Role |
|---|---|
| [`go2_robot_sdk/`](go2_robot_sdk) | WebRTC/LAN driver β€” camera, odom, lidar, URDF |
| [`go2_navigation/`](go2_navigation) | Bringup, mapping, Nav2, C++ lidar filtering pipeline |
| [`go2_control/`](go2_control) | Keyboard teleop node |
| [`go2_interfaces/`](go2_interfaces) | Shared ROS 2 message definitions |
| [`vlm/`](vlm) | Local VLA stack: Qwen2.5-VL engine, YOLO-assisted servoing, web console |
| [`navila/`](navila) | NaVILA (VILA-8B) inference server + setup/eval tooling (bare metal, not containerized) |
| [`docker/`](docker) | Containerized driver + navigation + local VLA bringup |

## Installation

```shell
mkdir -p ros2_ws
cd ros2_ws
git clone --recurse-submodules https://github.com/StrikeRobot/Go2-Nav_System src
sudo apt install ros-$ROS_DISTRO-image-tools ros-$ROS_DISTRO-vision-msgs
sudo apt install python3-pip clang portaudio19-dev
cd src
pip install -r requirements.txt
cd ..
source /opt/ros/$ROS_DISTRO/setup.bash
rosdep install --from-paths src --ignore-src -r -y
colcon build
```

## Docker

```shell
cd docker
cp .env.example .env    # if you keep one locally; otherwise export ROBOT_IP inline
ROBOT_IP=<go2-lan-ip> docker compose up go2                 # driver + Nav2 stack
ROBOT_IP=<go2-lan-ip> docker compose --profile vla up vlm   # + local VLA web console (GPU)
```
The image builds on `ros:jazzy-ros-base`, matching the distro this project is
developed against. `network_mode: host` is required for ROS 2 DDS discovery and
for the driver's WebRTC session with the robot on the LAN. The `vlm` service
requests a GPU via the [NVIDIA Container Toolkit](https://github.com/NVIDIA/nvidia-container-toolkit) β€”
install that on the host first. **NaVILA is not containerized**: it needs its
own conda environment, a hand-patched `transformers` build for Blackwell GPUs,
and a ~16 GB checkpoint pulled at setup time, so it's run on bare metal via
[`navila/setup_navila_blackwell.sh`](navila/setup_navila_blackwell.sh) β€” see the
[NaVILA section](#brain-2--navila-vila-8b) below.

Never bake a real `ROBOT_IP`/`ROBOT_TOKEN` into the image or into
`go2_navigation/config/robot.yaml` β€” pass them as environment variables at run
time instead.

## Usage

Put the Go2 in Wi-Fi/STA mode and note its LAN IP (mobile app β†’ Device β†’ Data β†’
Automatic Machine Inspection β†’ STA Network: `wlan0`). Set it once in
[`go2_navigation/config/robot.yaml`](go2_navigation/config/robot.yaml) or pass
`ROBOT_IP=<ip>` on any launch command (env always wins).

```shell
source install/setup.bash
```

### Mapping

```shell
ros2 launch go2_navigation mapping.launch.py
```
Mark a dock rectangle with tape as a fixed starting point. In the SlamToolbox
panel in `rviz`, select **Start At Dock**, drive the robot around with a
controller to build the map, then **Save Map** / **Serialize Map**.

### Autonomous navigation (Nav2)

```shell
ros2 launch go2_navigation navigation.launch.py
```
Loads a saved map and drives the robot with Nav2's standard global/local
planner stack + AMCL localization β€” set goals from `rviz` as usual.

### Manual teleop

```shell
ros2 run go2_control keyboard_teleop
```
Arrows = move, `q`/`e` = rotate, space = stop, `x` = quit. Releasing all keys
for >0.4 s triggers a dead-man stop.

## VLA navigation

Instead of a Nav2 goal, type an instruction and let a vision-language model
decide the motion step by step from the live camera feed. **No RL or learned
locomotion is involved anywhere in this stack** β€” every brain below only ever
emits a discrete motion command (`move forward 25 cm`, `turn left 30Β°`,
`stop`); the Go2's own built-in gait and this repo's closed-loop
`MotionController` (odom + lidar + E-Stop) execute it.

### Brain 1 β€” local Qwen2.5-VL (`vlm` package, "Demo1")

```shell
ros2 launch go2_navigation bringup.launch.py teleop:=true
```
```shell
cd ros2_ws/src
./vlm/scripts/run_demo1.sh
```
Open `http://localhost:8001`, type a goal in natural language (e.g. *"go to
the water bottle, turn right, stop at the chair"*) or a manual command (`move
forward 75 cm`, `turn left 90 deg`). Each step: camera frame β†’ Qwen2.5-VL
prompt (with live odom/lidar/bbox metrics injected as state) β†’ JSON
`{action, value, unit, is_finished, ...}` β†’ `MotionController` executes β†’
repeat until `is_finished` or **Stop/E-Stop**.

Two control modes (`VLA_CONTROL`):
- `vlm` (default) β€” the model reasons over injected state and returns the next action.
- `servo` β€” continuous closed-loop visual servoing driven by YOLO detections,
  bypassing the LLM once a target is locked on.

Swap in a stronger model instead of the local 3B:
```shell
VLA_BRAIN=api VLA_API_URL=<endpoint> VLA_API_KEY=<key> VLA_MODEL=qwen2.5-vl-7b-instruct \
  ./vlm/scripts/run_demo1.sh
```

<details>
<summary>Configuration reference β€” local VLM brain</summary>

| Env | Default | Meaning |
|---|---|---|
| `VLA_BRAIN` | `local` | `local` (Qwen2.5-VL-3B) \| `api` (OpenAI-compatible endpoint) \| `navila` |
| `VLA_CONTROL` | `servo` | `servo` (visual servo, YOLO-driven) \| `vlm` (JSON reasoning loop) |
| `USE_YOLO` | `1` | `0` disables the YOLO detector |
| `VLA_MAX_STEPS` | `20` | max steps per instruction |
| `VLA_MAX_TURN_DEG` | `10` | hard cap on turn angle per step (all brains) |
| `VLA_SEARCH_MAX_DEG` | `360` | give up searching after this much cumulative scan rotation |
| `VLA_HFOV_DEG` | `90` | camera horizontal FOV, used to convert pixel offset β†’ angle |
| `VLA_CENTER_TOL_DEG` | `5` | (servo) angular tolerance considered "centered" |
| `VLA_SERVO_HZ` / `VLA_SERVO_VX` / `VLA_SERVO_WZ_MAX` / `VLA_SERVO_KP` | `20` / `0.25` / `0.175` / `1.5` | servo loop rate, forward speed, max turn rate, turn gain |
| `VLA_STOP_BOTTOM_PX` | `20` | stop once the target bbox bottom is within this many px of the frame edge |
| `MOTION_LIN_SPEED` / `MOTION_ANG_SPEED` / `MOTION_ANG_MAX_DEG` | `0.3` / `0.35` / `10` | discrete move/turn command speeds and turn-rate ceiling |
| `MOTION_FRONT_STOP` | `0.30` | lidar obstacle distance (m) that halts forward motion |
| `MOTION_TURN_SIGN` | `1` | set to `-1` if the robot turns opposite to the commanded direction |
| `VLA_YOLO_WEIGHTS` / `VLA_YOLO_MIN_CONF` / `VLA_YOLO_IMGSZ` | `yolo11n.pt` / `0.3` / `960` | detector weights, confidence threshold, inference resolution |
| `VLA_LOG_DIR` | `vla_logs` | per-step JSONL log directory |
| `DEMO_MOCK` / `VLM_SKIP_MODEL` | β€” | run the UI without a robot / without loading the model |

Full source of truth for these lives in [`vlm/webconsole/demo1/navloop.py`](vlm/webconsole/demo1/navloop.py)
and [`vlm/webconsole/demo1/motion.py`](vlm/webconsole/demo1/motion.py).
</details>

### Brain 2 β€” NaVILA (VILA-8B)

[NaVILA](https://github.com/AnjieCheng/NaVILA) is an 8B vision-language-action
model trained specifically for language-guided navigation. It's integrated as a
**System-2 / System-1 split brain**:

```
camera β†’ 8-frame sliding buffer β†’ HTTP β†’ NaVILA (VILA-8B, System-2, GPU)
      β†’ mid-level action text β†’ parser β†’ MotionCmd
      β†’ MotionController β†’ twist_mux β†’ go2_driver β†’ Go2 built-in gait (System-1)
```

- **System-2** (`navila_server.py`, its own conda env, port `8100`) loads the
  8B checkpoint once and turns 8 camera frames + the instruction into an
  action sentence (`"The next action is move forward 25 cm."`) β€” a pure
  action policy, no chain-of-thought.
- **System-1** is the same odom/lidar-closed-loop `MotionController` used
  above; NaVILA never touches locomotion or gait, only high-level intent.

First-time setup (downloads the code repo + checkpoint, patches `transformers`
for Blackwell GPUs):
```shell
bash navila/setup_navila_blackwell.sh
```

One-command run (driver + NaVILA server + web console):
```shell
cd ros2_ws/src/navila
./scripts/run.sh
```
Open `http://localhost:8001` and type an instruction **in English** (NaVILA is
trained on English R2R-style instructions), e.g. *"Walk forward down the
hallway and stop near the chair."* The reasoning panel shows the raw NaVILA
output, per-token confidence, and a link to the exact 8 frames the model
received.

<details>
<summary>Configuration reference β€” NaVILA brain</summary>

| Env / arg | Default | Meaning |
|---|---|---|
| `VLA_BRAIN=navila` | β€” | routes control to the NaVILA server |
| `VLA_NAVILA_URL` | `http://127.0.0.1:8100` | NaVILA server address |
| `VLA_NAVILA_FRAMES` | `8` | frames sent per request (must match server `--num-frames`) |
| `VLA_NAVILA_HISTORY_MAX` | `64` | raw history frames kept per instruction; uniformly resampled down to `VLA_NAVILA_FRAMES`, always including the first and current frame (matches the paper's sampling scheme) |
| `NAVILA_FALLBACK` | `stop` | behavior when the model output fails to parse: `stop` \| `scan` |
| `NAVILA_PREPROC` | `pad` | frame preprocessing: `fov` (undistort + forward crop, closest to training distribution) \| `pad` (full fisheye) \| `resize` \| `crop` |
| server `--model-path` | β€” | path to the downloaded checkpoint |
| server `--port` | `8100` | HTTP port |
| server `--num-frames` | `8` | frames processed per request |

Full parameter tables, the three Blackwell inference fixes (8-bit + eager
attention + 4D causal mask β€” see [`navila/setup_navila_blackwell.sh`](navila/setup_navila_blackwell.sh)),
and eval/debug tooling (`navila/tools/`) are documented inline in that script
and in [`navila/scripts/`](navila/scripts).
</details>

## Acknowledgements & citations

- Built on [abizovnuralem/go2_ros2_sdk](https://github.com/abizovnuralem/go2_ros2_sdk)
  (RoboVerse community) β€” see [`LICENSE`](LICENSE).
- **NaVILA** β€” An-Chieh Cheng et al., *"NaVILA: Legged Robot Vision-Language-Action
  Model for Navigation"*, 2024. Code: [AnjieCheng/NaVILA](https://github.com/AnjieCheng/NaVILA),
  checkpoint: `a8cheng/navila-llama3-8b-8f` on Hugging Face. See the official
  repository for the paper and up-to-date citation.
- **Qwen2.5-VL** β€” Alibaba Qwen team, model `Qwen/Qwen2.5-VL-3B-Instruct`.
- **Ultralytics YOLO11** β€” [ultralytics/ultralytics](https://github.com/ultralytics/ultralytics).
- **Nav2** and **slam_toolbox** β€” the ROS 2 Navigation Working Group /
  [ros-navigation](https://github.com/ros-navigation) and
  [SteveMacenski/slam_toolbox](https://github.com/SteveMacenski/slam_toolbox).
- **Point Cloud Library (PCL)** β€” used for the SOR/ROR filtering pipeline in `go2_navigation`.
- **twist_mux** β€” [ros-teleop/twist_mux](https://github.com/ros-teleop/twist_mux),
  used to arbitrate teleop/VLA vs. Nav2 velocity commands.
- **aiortc** β€” vendored WebRTC implementation used by the Go2 driver.

## License

BSD 2-Clause β€” see [`LICENSE`](LICENSE). Note: some vendored/derived source
files under `go2_navigation/` carry a BSD-3-Clause header inherited from
upstream; if you plan to redistribute, reconcile this with project counsel
before publishing rather than assuming one license governs the whole tree.