elmoghany commited on
Commit
a3549fd
·
verified ·
1 Parent(s): 7c85fa3

Point card at the renamed GitHub repos (crowd-nav = package, crowd-nav-legacy = research)

Browse files
Files changed (1) hide show
  1. README.md +132 -131
README.md CHANGED
@@ -1,131 +1,132 @@
1
- ---
2
- license: mit
3
- library_name: pytorch
4
- pipeline_tag: robotics
5
- tags:
6
- - robotics
7
- - social-navigation
8
- - human-robot-interaction
9
- - pedestrian-trajectory-prediction
10
- - model-predictive-control
11
- - convex-optimization
12
- ---
13
-
14
- # crowd-nav — human-response models for the Foresight controller
15
-
16
- Trained weights for **Foresight**, a convex receding-horizon controller that predicts how each
17
- nearby pedestrian will respond to the robot's *candidate plan*, and then chooses that plan by
18
- exact convex optimization. These are the response models the controller calls; the planner is
19
- plain code (no learned policy, no RL).
20
-
21
- | file | model | role |
22
- |---|---|---|
23
- | `linres_head.pt` | **Linear-response operator** (headline) | best closed-loop performance; makes planning an exact convex QP |
24
- | `residual_predictor_k1.pt` | Residual predictor | most accurate on real data; the safest variant in closed loop |
25
- | `residual_predictor.pt` | *legacy* | **deprecated** — trained with a robot-force prior of 0 but deployed at 1; superseded by `residual_predictor_k1.pt` |
26
-
27
- ## Which one should I use?
28
-
29
- **`linres_head.pt`** unless you have a reason not to. It is a few centimetres less accurate as a
30
- pure forecaster, but its prediction is *exactly affine* in the robot's planned displacement, so the
31
- planner gets an exact constant Jacobian and the safety constraints stay linear — the whole plan
32
- selection becomes a convex QP that solves in ~17 ms with a checkable feasibility certificate.
33
- That trade wins where it counts: it leads our benchmark, and it is the only variant that crossed a
34
- 90-pedestrian oncoming corridor with zero contacts.
35
-
36
- ## Measured results
37
-
38
- Isaac Sim, 16 scenarios × 10 paired seeds (160 episodes per method), identical hardware:
39
-
40
- | model | goal reach | hard collisions / ep | plan latency (mean) |
41
- |---|---|---|---|
42
- | **Foresight-LinRes** (`linres_head.pt`) | **94.4 %** | 0.013 | **16.6 ms** |
43
- | Foresight-Residual (`residual_predictor_k1.pt`) | 90.0 % | **0.006** | 19.7 ms |
44
- | Foresight-SFM (physics only, ablation) | 93.8 % | 0.013 | 17.2 ms |
45
- | Foresight-CV (straight-line, ablation) | 90.0 % | 0.019 | 16.4 ms |
46
- | SICNav [T-RO '24] (nonconvex bilevel MPC) | 89.2 % | 0.042 | 1843 ms |
47
-
48
- Stress test — a 4.5 m × 105 m corridor with 90 pedestrians *all walking against* the robot and a
49
- continuous inflow so it never empties: LinRes reached the goal in 98.5 s with **zero contacts**;
50
- against pedestrian models it was never tuned on (SRFM, social-force) it reached in 85.0 s and
51
- 91.7 s, also contact-free.
52
-
53
- Prediction accuracy on the held-out real split (factual ADE, metres — lower is better):
54
-
55
- | model | no robot | stationary robot | moving robot |
56
- |---|---|---|---|
57
- | Constant velocity | 0.240 | 0.253 | 0.354 |
58
- | Social force | 0.343 | 0.358 | 0.416 |
59
- | Residual (k1) | **0.192** | **0.217** | 0.285 |
60
- | Linear response | 0.204 | 0.248 | 0.319 |
61
-
62
- ## Architecture
63
-
64
- Both models are a **physics prior plus a learned correction** — small by design, because the real
65
- moving-robot data is scarce.
66
-
67
- ```
68
- prior (no parameters, per pedestrian per 0.25 s step)
69
- f_goal = (1.3·(g−p)/‖g−p‖ − v)/0.5
70
- f_ped = Σ 2.0·exp(−d/0.4)·d̂ (d < 2.0 m)
71
- f_robot = k·exp(−d/0.5)·d̂ (d < 3.0 m)
72
- v ← clip(v + f·Δt, 1.8); p ← p + v·Δt
73
-
74
- residual_predictor_k1.pt ŷ = prior(k=1) + f_θ(x)
75
- f_θ : MLP 22 → 128 → 128 → 16 (21,520 params)
76
-
77
- linres_head.pt ŷ = prior(k=0) + f_θ(x) + G_φ(x)·ΔR
78
- f_θ : MLP 22 → 128 → 128 → 16 (21,520 params)
79
- G_φ : MLP 22 → 64 → 96 (7,712 params) ⇒ rank-3 16×16 operator
80
- G = Σ_{r=1..3} a_r b_rᵀ, ΔR = (R − 1⊗r₀)/5.0
81
- ⇒ ∂ŷ/∂R = G/5.0 — exact, constant, no differentiation needed
82
- ```
83
-
84
- Input `x` (22-d, pedestrian local frame): 1 s of past positions (4 @ 0.25 s), velocity, goal
85
- direction, robot relative position, a robot-nearby flag, the two nearest neighbours, and a 3-way
86
- condition one-hot. Output: 8 × 2 future displacements — 2.0 s at 0.25 s.
87
-
88
- Training: supervised (Adam, lr 1e-3, batch 128, ~25 epochs), synthetic pre-train then fine-tune on
89
- the real **PeRoI** robot–pedestrian recordings. No reinforcement learning anywhere in this stack.
90
- Both run in well under 1 ms per call on CPU.
91
-
92
- ## Load
93
-
94
- ```bash
95
- pip install huggingface_hub torch
96
- hf download elmoghany/crowd-nav linres_head.pt residual_predictor_k1.pt --local-dir .
97
- ```
98
-
99
- ```python
100
- import torch
101
- ck = torch.load("linres_head.pt", map_location="cpu")
102
- ck["config"]
103
- # {'in_dim': 22, 'horizon_dim': 16, 'rank': 3, 'hidden': 64, 'dr_scale': 5.0,
104
- # 'sfm_k_robot': 0.0, 'convention': 'y = sfm(k=0) + f_resid(x_ctx) + head(x_ctx, dR)'}
105
- ck["f_resid"], ck["head"] # two state_dicts
106
- ```
107
-
108
- The `config` block is authoritative: `sfm_k_robot` **must** match the robot-force strength used in
109
- your prior at deployment. Mismatching them is a real bug we shipped once — the residual then
110
- corrects an error that is not there. `residual_predictor.pt` is the artifact of that mistake and is
111
- kept only for reproducibility.
112
-
113
- Model classes (`ResidualPredictor`, `LinearResponseHead`) and the full controller live in the
114
- research repo: **[github.com/elmoghany/crowd-nav](https://github.com/elmoghany/crowd-nav)**.
115
- A sim-free packaging of the controller for real robots is at
116
- [github.com/elmoghany/peroi-controller](https://github.com/elmoghany/peroi-controller).
117
-
118
- ## Honest limitations
119
-
120
- - **Real-data forecasting is a tie.** On four real robot–pedestrian datasets (including JRDB),
121
- every learned model here ties the analytic baselines on factual accuracy. Our measured
122
- explanation: the counterfactual proxy those benchmarks rely on (constant-velocity extrapolation)
123
- correlates only r 0.30 with exact simulated counterfactuals — the target is mostly noise. The
124
- gains above are **closed-loop** gains, not forecasting gains.
125
- - **Compliance bias.** The training recordings contain cooperative pedestrians. On deliberately
126
- stubborn crowds the k1 residual is the weakest learned variant; compliance-randomized training
127
- data is the known fix, not yet applied.
128
- - **Cross-dataset transfer.** Moving `residual_predictor_k1.pt` to JRDB over-predicts robot-induced
129
- deviation by ~6× (its k=1 prior is calibrated to our robot). It makes a controller conservative,
130
- not unsafe it still threaded real JRDB crowds contact-free but retune `sfm_k_robot` for a
131
- different platform.
 
 
1
+ ---
2
+ license: mit
3
+ library_name: pytorch
4
+ pipeline_tag: robotics
5
+ tags:
6
+ - robotics
7
+ - social-navigation
8
+ - human-robot-interaction
9
+ - pedestrian-trajectory-prediction
10
+ - model-predictive-control
11
+ - convex-optimization
12
+ ---
13
+
14
+ # crowd-nav — human-response models for the Foresight controller
15
+
16
+ Trained weights for **Foresight**, a convex receding-horizon controller that predicts how each
17
+ nearby pedestrian will respond to the robot's *candidate plan*, and then chooses that plan by
18
+ exact convex optimization. These are the response models the controller calls; the planner is
19
+ plain code (no learned policy, no RL).
20
+
21
+ | file | model | role |
22
+ |---|---|---|
23
+ | `linres_head.pt` | **Linear-response operator** (headline) | best closed-loop performance; makes planning an exact convex QP |
24
+ | `residual_predictor_k1.pt` | Residual predictor | most accurate on real data; the safest variant in closed loop |
25
+ | `residual_predictor.pt` | *legacy* | **deprecated** — trained with a robot-force prior of 0 but deployed at 1; superseded by `residual_predictor_k1.pt` |
26
+
27
+ ## Which one should I use?
28
+
29
+ **`linres_head.pt`** unless you have a reason not to. It is a few centimetres less accurate as a
30
+ pure forecaster, but its prediction is *exactly affine* in the robot's planned displacement, so the
31
+ planner gets an exact constant Jacobian and the safety constraints stay linear — the whole plan
32
+ selection becomes a convex QP that solves in ~17 ms with a checkable feasibility certificate.
33
+ That trade wins where it counts: it leads our benchmark, and it is the only variant that crossed a
34
+ 90-pedestrian oncoming corridor with zero contacts.
35
+
36
+ ## Measured results
37
+
38
+ Isaac Sim, 16 scenarios × 10 paired seeds (160 episodes per method), identical hardware:
39
+
40
+ | model | goal reach | hard collisions / ep | plan latency (mean) |
41
+ |---|---|---|---|
42
+ | **Foresight-LinRes** (`linres_head.pt`) | **94.4 %** | 0.013 | **16.6 ms** |
43
+ | Foresight-Residual (`residual_predictor_k1.pt`) | 90.0 % | **0.006** | 19.7 ms |
44
+ | Foresight-SFM (physics only, ablation) | 93.8 % | 0.013 | 17.2 ms |
45
+ | Foresight-CV (straight-line, ablation) | 90.0 % | 0.019 | 16.4 ms |
46
+ | SICNav [T-RO '24] (nonconvex bilevel MPC) | 89.2 % | 0.042 | 1843 ms |
47
+
48
+ Stress test — a 4.5 m × 105 m corridor with 90 pedestrians *all walking against* the robot and a
49
+ continuous inflow so it never empties: LinRes reached the goal in 98.5 s with **zero contacts**;
50
+ against pedestrian models it was never tuned on (SRFM, social-force) it reached in 85.0 s and
51
+ 91.7 s, also contact-free.
52
+
53
+ Prediction accuracy on the held-out real split (factual ADE, metres — lower is better):
54
+
55
+ | model | no robot | stationary robot | moving robot |
56
+ |---|---|---|---|
57
+ | Constant velocity | 0.240 | 0.253 | 0.354 |
58
+ | Social force | 0.343 | 0.358 | 0.416 |
59
+ | Residual (k1) | **0.192** | **0.217** | 0.285 |
60
+ | Linear response | 0.204 | 0.248 | 0.319 |
61
+
62
+ ## Architecture
63
+
64
+ Both models are a **physics prior plus a learned correction** — small by design, because the real
65
+ moving-robot data is scarce.
66
+
67
+ ```
68
+ prior (no parameters, per pedestrian per 0.25 s step)
69
+ f_goal = (1.3·(g−p)/‖g−p‖ − v)/0.5
70
+ f_ped = Σ 2.0·exp(−d/0.4)·d̂ (d < 2.0 m)
71
+ f_robot = k·exp(−d/0.5)·d̂ (d < 3.0 m)
72
+ v ← clip(v + f·Δt, 1.8); p ← p + v·Δt
73
+
74
+ residual_predictor_k1.pt ŷ = prior(k=1) + f_θ(x)
75
+ f_θ : MLP 22 → 128 → 128 → 16 (21,520 params)
76
+
77
+ linres_head.pt ŷ = prior(k=0) + f_θ(x) + G_φ(x)·ΔR
78
+ f_θ : MLP 22 → 128 → 128 → 16 (21,520 params)
79
+ G_φ : MLP 22 → 64 → 96 (7,712 params) ⇒ rank-3 16×16 operator
80
+ G = Σ_{r=1..3} a_r b_rᵀ, ΔR = (R − 1⊗r₀)/5.0
81
+ ⇒ ∂ŷ/∂R = G/5.0 — exact, constant, no differentiation needed
82
+ ```
83
+
84
+ Input `x` (22-d, pedestrian local frame): 1 s of past positions (4 @ 0.25 s), velocity, goal
85
+ direction, robot relative position, a robot-nearby flag, the two nearest neighbours, and a 3-way
86
+ condition one-hot. Output: 8 × 2 future displacements — 2.0 s at 0.25 s.
87
+
88
+ Training: supervised (Adam, lr 1e-3, batch 128, ~25 epochs), synthetic pre-train then fine-tune on
89
+ the real **PeRoI** robot–pedestrian recordings. No reinforcement learning anywhere in this stack.
90
+ Both run in well under 1 ms per call on CPU.
91
+
92
+ ## Load
93
+
94
+ ```bash
95
+ pip install huggingface_hub torch
96
+ hf download elmoghany/crowd-nav linres_head.pt residual_predictor_k1.pt --local-dir .
97
+ ```
98
+
99
+ ```python
100
+ import torch
101
+ ck = torch.load("linres_head.pt", map_location="cpu")
102
+ ck["config"]
103
+ # {'in_dim': 22, 'horizon_dim': 16, 'rank': 3, 'hidden': 64, 'dr_scale': 5.0,
104
+ # 'sfm_k_robot': 0.0, 'convention': 'y = sfm(k=0) + f_resid(x_ctx) + head(x_ctx, dR)'}
105
+ ck["f_resid"], ck["head"] # two state_dicts
106
+ ```
107
+
108
+ The `config` block is authoritative: `sfm_k_robot` **must** match the robot-force strength used in
109
+ your prior at deployment. Mismatching them is a real bug we shipped once — the residual then
110
+ corrects an error that is not there. `residual_predictor.pt` is the artifact of that mistake and is
111
+ kept only for reproducibility.
112
+
113
+ Model classes (`ResidualPredictor`, `LinearResponseHead`) and the full controller live in the
114
+ **[github.com/elmoghany/crowd-nav](https://github.com/elmoghany/crowd-nav)** -- a one-file,
115
+ simulator-free controller with a `quickstart.py` that fetches these weights and verifies your
116
+ install in one command. The full research stack (convex-QP planner, Isaac benchmarks, paper) is at
117
+ [github.com/elmoghany/crowd-nav-legacy](https://github.com/elmoghany/crowd-nav-legacy).
118
+
119
+ ## Honest limitations
120
+
121
+ - **Real-data forecasting is a tie.** On four real robot–pedestrian datasets (including JRDB),
122
+ every learned model here ties the analytic baselines on factual accuracy. Our measured
123
+ explanation: the counterfactual proxy those benchmarks rely on (constant-velocity extrapolation)
124
+ correlates only r 0.30 with exact simulated counterfactuals — the target is mostly noise. The
125
+ gains above are **closed-loop** gains, not forecasting gains.
126
+ - **Compliance bias.** The training recordings contain cooperative pedestrians. On deliberately
127
+ stubborn crowds the k1 residual is the weakest learned variant; compliance-randomized training
128
+ data is the known fix, not yet applied.
129
+ - **Cross-dataset transfer.** Moving `residual_predictor_k1.pt` to JRDB over-predicts robot-induced
130
+ deviation by ~6× (its k=1 prior is calibrated to our robot). It makes a controller conservative,
131
+ not unsafe — it still threaded real JRDB crowds contact-free — but retune `sfm_k_robot` for a
132
+ different platform.