File size: 5,710 Bytes
4c75661
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
// closed-loop μ„œλ²„ ν”„λ‘œν† μ½œ(serve.hpp)Β·μ œμ–΄ ν™˜μ‚°(drive.hpp control_for, step_with_contact) μ‹œν—˜ β€” tests/test_serve_cpp.py κ°€
// μ»΄νŒŒμΌν•΄ λŒλ¦°λ‹€. μ°½Β·GPU 없이.
//   test_serve self                       자체 μ‹œν—˜ (μ‹€νŒ¨ 수 = μ’…λ£Œ μ½”λ“œ)
//   test_serve contact OBST.pgm DRIV.txt  ν‘œμ€€ μž…λ ₯ μ€„λ§ˆλ‹€ 'x y yaw' β†’ 'μž₯μ• λ¬Όμ μˆ˜ μ—°μ„μ μˆ˜' (Python geometry 와 λŒ€μ‘°)
#include "drive.hpp"
#include "serve.hpp"

#include <cmath>
#include <cstdio>
#include <iostream>
#include <string>

using namespace vkgs;

static int fails = 0;
#define CHECK(c, msg)                          \
    do {                                       \
        if (!(c)) {                            \
            std::printf("FAIL %s\n", msg);     \
            ++fails;                           \
        }                                      \
    } while (0)

static bool throws(const std::string& line) {
    try {
        parse_command(line);
    } catch (const std::exception&) {
        return true;
    }
    return false;
}

int self_test() {
    // 1) λͺ…λ Ή νŒŒμ„œ
    ServeCommand c = parse_command("step accel=1.5 delta=-0.1 dt=0.05 render=1");
    CHECK(c.name == "step", "name");
    CHECK(std::fabs(c.num("accel") - 1.5f) < 1e-6f && std::fabs(c.num("delta") + 0.1f) < 1e-6f, "numbers");
    CHECK(c.flag("render", false) && !c.flag("sensors", false), "flags");
    CHECK(std::fabs(c.num("throttle", 0.25f) - 0.25f) < 1e-6f, "default");
    CHECK(parse_command("").name.empty() && parse_command("   # 주석").name.empty(), "empty/comment");
    CHECK(throws("step accel"), "token without =");
    CHECK(throws("step =3"), "empty key");
    bool bad_num = false;
    try { parse_command("step dt=0.05x").num("dt"); } catch (const std::exception&) { bad_num = true; }
    CHECK(bad_num, "trailing garbage number");
    bool missing = false;
    try { parse_command("step").num("dt"); } catch (const std::exception&) { missing = true; }
    CHECK(missing, "missing key");
    bool bad_flag = false;
    try { parse_command("render sensors=yes").flag("sensors", false); } catch (const std::exception&) { bad_flag = true; }
    CHECK(bad_flag, "bad flag");
    CHECK(parse_command("reset x=1 x=2").num("x") == 2.0f, "last key wins");

    // 2) μƒμž λͺ©λ‘
    auto b = parse_boxes("1,2,0.5,2.2,0.9;3,4,0,1,1;");
    CHECK(b.size() == 2 && b[1][0] == 3.0f && b[0][3] == 2.2f, "boxes");
    CHECK(parse_boxes("").empty(), "no boxes");
    bool bad_box = false;
    try { parse_boxes("1,2,3"); } catch (const std::exception&) { bad_box = true; }
    CHECK(bad_box, "short box");

    // 3) 응닡 쀄
    std::string l = ServeReply().add("t", 0.5, 2).add("cams", "A,B").add("obs", "").add("n", 3).line();
    CHECK(l == "ok t=0.50 cams=A,B obs=- n=3\n", "reply line");
    CHECK(serve_error("a\nb") == "err a b\n", "error line");

    // 4) 가속·쑰ν–₯ λͺ…λ Ή β†’ νŽ˜λ‹¬Β·ν•Έλ“€: 정상 μƒνƒœμ—μ„œ λͺ…λ Ήν•œ 가속이 κ·ΈλŒ€λ‘œ λ‚˜μ˜¨λ‹€
    Vehicle car;
    car.v = 5;
    for (float a : {1.5f, 0.0f, -2.0f, -6.5f}) {
        Vehicle t = car;
        float v0 = t.v;
        t.step(control_for(t, a, 0), 0.01f);
        float got = (t.v - v0) / 0.01f;
        std::printf("accel cmd %.2f -> %.3f\n", a, got);
        CHECK(std::fabs(got - a) < 0.01f, "accel tracking");
    }
    // μ„œ μžˆμ„ λ•Œ 음의 가속은 후진이 μ•„λ‹ˆλΌ μ •μ§€ μœ μ§€
    Vehicle st;
    for (int i = 0; i < 100; ++i) st.step(control_for(st, -3.0f, 0), 0.01f);
    CHECK(std::fabs(st.v) < 1e-6f && std::fabs(st.x) < 1e-6f, "hold at standstill");
    // μ‘°ν–₯각 λͺ…λ Ή: μ‘°ν–₯ 속도 μ œν•œ(1.2 rad/s) λ’€ λͺ©ν‘œκ°μ— 수렴
    Vehicle s2;
    s2.v = 4;
    for (int i = 0; i < 100; ++i) s2.step(control_for(s2, 0.0f, 0.2f), 0.01f);
    std::printf("delta after 1 s: %.4f (cmd 0.2)\n", s2.delta);
    CHECK(std::fabs(s2.delta - 0.2f) < 2e-3f, "steer angle");

    // 5) step_with_contact: λ²½(x = 10 m) μ•žμ—μ„œ λ©ˆμΆ”κ³  λ§‰νž˜μ„ μ•Œλ¦°λ‹€ (빈 지도·주행가λŠ₯μ˜μ—­ = 막지 μ•ŠμŒ)
    ObstacleGrid none;
    Drivable road;
    ContactParams cp;
    cp.curb = false;
    Vehicle w;
    ContactInfo cur;
    int blocked = 0;
    for (int i = 0; i < 300; ++i) blocked += step_with_contact(w, control_for(w, 2.0f, 0), 0.01f, cur, none, road, cp).blocked;
    CHECK(blocked == 0 && w.x > 5.0f, "free driving");
    // λ‹€λ₯Έ μ°¨λŸ‰ μƒμž: μ•ž 8 m 에 μ •μ§€ μ°¨λŸ‰ β†’ λ§‰ν˜€ λ©ˆμΆ˜λ‹€
    std::vector<Footprint> others{{w.x + 3.43f + 1.0f + 2.2f, w.y, 0, 2.2f, 0.9f}};
    int vb = 0;
    Contact kind = Contact::None;
    for (int i = 0; i < 300; ++i) {
        SubstepResult r = step_with_contact(w, control_for(w, 2.0f, 0), 0.01f, cur, none, road, cp, &others);
        if (r.blocked) ++vb, kind = r.kind;
    }
    std::printf("vehicle blocked steps %d, front %.2f, other rear %.2f\n", vb, w.x + w.geom.front, others[0].x - 2.2f);
    CHECK(vb > 0 && kind == Contact::Vehicle, "blocked by vehicle");
    CHECK(w.x + w.geom.front < others[0].x - 2.2f + 0.3f, "stopped at vehicle");
    return fails;
}

int contact_dump(const char* pgm, const char* driv) {
    ObstacleGrid og;
    og.load(pgm);
    Drivable road;
    road.load(driv);
    float x, y, yaw;
    while (std::cin >> x >> y >> yaw) {
        Vehicle car;
        car.x = x, car.y = y, car.yaw = yaw;
        ContactInfo ci = vehicle_contact(car, og, road, true, 0.15f, 0.1f);
        std::printf("%d %d\n", ci.obstacle, ci.curb);
    }
    return 0;
}

int main(int argc, char** argv) {
    std::string mode = argc > 1 ? argv[1] : "self";
    if (mode == "contact" && argc > 3) return contact_dump(argv[2], argv[3]);
    int f = self_test();
    std::printf("%s (%d)\n", f ? "FAILED" : "OK", f);
    return f;
}