59#include <piksel/baseapp.hpp>
74const glm::vec4 BLACK = glm::vec4(0, 0, 0, 1);
75const glm::vec4 WHITE = glm::vec4(1, 1, 1, 1);
76const glm::vec4 RED = glm::vec4(0.8, 0, 0, 1);
77const glm::vec4 ORANGE = glm::vec4(0.8, 0.4, 0, 1);
78const glm::vec4 LIGHT_GRAY = glm::vec4(0.8f, 0.8f, 0.8f, 1);
79const glm::vec4 DARK_GRAY = glm::vec4(0.2f, 0.2f, 0.2f, 1.0f);
86 Robot(
double x,
double y,
double theta=0.0);
89 void update(VectorXd phi_dot);
92 void draw(piksel::Graphics& g);
95 VectorXd head()
const;
98 MatrixXd jacobian()
const;
100 static constexpr double wheel_radius = 5;
101 static constexpr double wheels_distance = 24;
102 static constexpr double half_length = 12;
103 static constexpr double phi_max = 30;
104 static constexpr double dt = 0.02;
109 std::deque<std::pair<double, double>> trace;
110 static constexpr unsigned int trace_length = 1000;
111 static constexpr unsigned int trace_subsample = 3;
125 Omega(0, 0) = wheel_radius / 2;
126 Omega(0, 1) = wheel_radius / 2;
127 Omega(1, 0) = wheel_radius / (2 * wheels_distance);
128 Omega(1, 1) = -wheel_radius / (2 * wheels_distance);
129 trace.push_back({x, y});
133void Robot::update(VectorXd phi_dot) {
135 static ArrayXd max_phi = ArrayXd::Constant(2, phi_max);
136 phi_dot = (phi_dot.array().max(-max_phi)).min(max_phi);
139 VectorXd vw = Omega * phi_dot;
142 position(0) += dt * vw(0) * std::cos(theta);
143 position(1) += dt * vw(0) * std::sin(theta);
147 trace.push_back({position(0), position(1)});
148 if(trace.size() > trace_length)
153void Robot::draw(piksel::Graphics& g) {
155 if(trace.size() > trace_subsample) {
156 g.stroke(LIGHT_GRAY);
157 for(
unsigned int i=0; i<trace.size()-trace_subsample; i+=trace_subsample) {
158 g.line(trace[i].first, trace[i].second, trace[i+trace_subsample].first, trace[i+trace_subsample].second);
164 g.translate(position(0), position(1));
167 g.ellipse(0, 0, 2*half_length, wheels_distance);
169 g.ellipse(half_length, 0, 5, 5);
174VectorXd Robot::head()
const {
175 VectorXd p(position);
176 p(0) += half_length * std::cos(theta);
177 p(1) += half_length * std::sin(theta);
182MatrixXd Robot::jacobian()
const {
184 double c = std::cos(theta);
185 double s = std::sin(theta);
186 M << c, -half_length*s,
196 Obstacle(
double x,
double y,
double size);
199 void draw(piksel::Graphics& g);
202 VectorXd distance(
const VectorXd& p) {
return p - position; };
205 double radius()
const {
return size / 2 + safety_distance; }
208 static constexpr double safety_distance = 15;
215Obstacle::Obstacle(
double x,
double y,
double size)
224void Obstacle::draw(piksel::Graphics& g) {
226 g.ellipse(position(0), position(1), size, size);
231class App :
public piksel::BaseApp {
234 App() : piksel::BaseApp(1000, 1000) {}
240 void draw(piksel::Graphics& g);
243 void mouseMoved(
int x,
int y);
246 void mousePressed(
int);
249 std::unique_ptr<Robot> bot;
250 std::vector<std::unique_ptr<Obstacle>> obstacles;
251 std::unique_ptr<QPSolver> solver;
258 bot = std::make_unique<Robot>(500, 500);
261 for(
unsigned int x=0; x<=1000; x+=200) {
262 for(
unsigned int y=0; y<=1000; y+=200) {
263 auto new_obstacle = std::make_unique<Obstacle>(x, y, 80);
264 if(new_obstacle->distance(bot->head()).norm() > new_obstacle->radius()) {
265 obstacles.push_back(std::move(new_obstacle));
271 for(
unsigned int x=100; x<=1000; x+=200) {
272 for(
unsigned int y=100; y<=1000; y+=200) {
273 auto new_obstacle = std::make_unique<Obstacle>(x, y, 50);
274 if(new_obstacle->distance(bot->head()).norm() > new_obstacle->radius()) {
275 obstacles.push_back(std::move(new_obstacle));
281 solver = std::make_unique<QPSolver>(2, 2, 1e-9);
284 target = VectorXd::Zero(2);
289void App::mouseMoved(
int x,
int y) {
297void App::mousePressed(
int) {
303void App::draw(piksel::Graphics& g) {
305 g.background(DARK_GRAY);
306 for(
auto& obstacle : obstacles)
317 double control_gain = 10;
318 MatrixXd J = bot->jacobian();
319 VectorXd r = control_gain * (target - bot->head());
320 solver->updateObjective(J, r);
324 MatrixXd C(obstacles.size() + 4, 2);
325 VectorXd d(obstacles.size() + 4);
328 const double avoidance_gain = 200;
329 for(
unsigned int i=0; i<obstacles.size(); i++) {
330 VectorXd distance = obstacles[i]->distance(bot->head());
331 C.row(i) = - 2 * distance.transpose() * J;
332 d(i) = avoidance_gain * (distance.norm() - obstacles[i]->radius());
336 C.bottomRows(4).topRows(2).setIdentity();
337 C.bottomRows(2) = -MatrixXd::Identity(2, 2);
338 d.tail(4) = VectorXd::Constant(4, Robot::phi_max);
342 if(solver->updateInequalities(C, d) && solver->solve(phi_dot)) {
343 bot->update(phi_dot);
348int main(
int argc,
char** argv) {
Quadratic Programming solver using active set and null-space projections.
int main(int argc, char **argv)