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;
}
|