--- /dev/null
+#include "stage.hh"
+using namespace Stg;
+
+#define PATH_LEN 17
+#define MIN_LADIST 0.5
+#define WHEELBASE 0.32
+
+struct point {
+ double x;
+ double y;
+};
+
+struct pose {
+ double x;
+ double y;
+ double h;
+};
+
+struct point PATH[] = {
+ {3.5, -8.5},
+ {5, -6},
+ {3, -4},
+ {-4, -3.5},
+ {-5.5, -0.5},
+ {-4, 1.5},
+ {-1, 1},
+ {6, -2},
+ {8.5, 3},
+ {6, 4.5},
+ {3, 5.5},
+ {0, 8},
+ {-6, 8},
+ {-8, 0},
+ {-8, -6},
+ {-7, -8},
+ {-5, -8.5},
+};
+
+class ControlledRobot {
+private:
+ ModelPosition *pos = nullptr;
+ unsigned int tpi = 0; // target pose index
+public:
+ ControlledRobot(Model *m) : pos(dynamic_cast<ModelPosition *>(m))
+ {
+ assert(this->pos != nullptr);
+ this->pos->Subscribe();
+ }
+ ModelPosition *get_pos() const { return this->pos; }
+ unsigned int get_tpi() const { return this->tpi; }
+ void incr_tpi() { this->tpi = (this->tpi + 1) % PATH_LEN; }
+};
+
+struct pose point_to_pose(struct point const p)
+{
+ return {p.x, p.y, 0.0};
+}
+
+double edist(struct pose const p1, struct pose const p2)
+{
+ double dx = p2.x - p1.x;
+ double dy = p2.y - p1.y;
+ return sqrt(dx * dx + dy * dy);
+}
+
+double pp(struct pose const ep, struct pose const tp)
+// see https://thomasfermi.github.io/Algorithms-for-Automated-Driving/Control/PurePursuit.html
+{
+ double alpha = atan2(tp.y - ep.y, tp.x - ep.x);
+ alpha -= ep.h;
+ return atan(2 * WHEELBASE * sin(alpha) / edist(ep, tp));
+}
+
+int steer(Model *m, ControlledRobot *r)
+{
+ auto p = r->get_pos()->GetPose();
+ struct pose ep = {p.x, p.y, p.a};
+ assert(r->get_tpi() < PATH_LEN);
+ struct pose tp = point_to_pose(PATH[r->get_tpi()]);
+ if (edist(ep, tp) < MIN_LADIST) {
+ r->incr_tpi();
+ tp = point_to_pose(PATH[r->get_tpi()]);
+ m->GetWorld()->GetModel("tp")->SetPose({tp.x, tp.y, 0.0, 0.0});
+ }
+ r->get_pos()->SetTurnSpeed(pp(ep, tp));
+ return 0;
+}
+
+extern "C" int Init(Model *model, CtrlArgs *)
+{
+ auto r = new ControlledRobot(model);
+ r->get_pos()->AddCallback(Model::CB_UPDATE, model_callback_t(steer), r);
+ r->get_pos()->SetXSpeed(2.0);
+ return 0;
+}