Last active
June 12, 2017 20:12
-
-
Save AakashKumarNain/6aac829950475344aa35d69285a099c5 to your computer and use it in GitHub Desktop.
MPC
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <math.h> | |
#include <uWS/uWS.h> | |
#include <chrono> | |
#include <iostream> | |
#include <thread> | |
#include <vector> | |
#include "Eigen-3.3/Eigen/Core" | |
#include "Eigen-3.3/Eigen/QR" | |
#include "MPC.h" | |
#include "json.hpp" | |
#include <cppad/cppad.hpp> | |
#include <cppad/ipopt/solve.hpp> | |
using CppAD :: AD; | |
// for convenience | |
using json = nlohmann::json; | |
// For converting back and forth between radians and degrees. | |
constexpr double pi() { return M_PI; } | |
double deg2rad(double x) { return x * pi() / 180; } | |
double rad2deg(double x) { return x * 180 / pi(); } | |
// Checks if the SocketIO event has JSON data. | |
// If there is data the JSON object in string format will be returned, | |
// else the empty string "" will be returned. | |
string hasData(string s) { | |
auto found_null = s.find("null"); | |
auto b1 = s.find_first_of("["); | |
auto b2 = s.rfind("}]"); | |
if (found_null != string::npos) { | |
return ""; | |
} else if (b1 != string::npos && b2 != string::npos) { | |
return s.substr(b1, b2 - b1 + 2); | |
} | |
return ""; | |
} | |
// Evaluate a polynomial. | |
double polyeval(Eigen::VectorXd coeffs, double x) { | |
double result = 0.0; | |
for (int i = 0; i < coeffs.size(); i++) { | |
result += coeffs[i] * pow(x, i); | |
} | |
return result; | |
} | |
// Fit a polynomial. | |
// Adapted from | |
// https://github.com/JuliaMath/Polynomials.jl/blob/master/src/Polynomials.jl#L676-L716 | |
Eigen::VectorXd polyfit(Eigen::VectorXd xvals, Eigen::VectorXd yvals, | |
int order) { | |
assert(xvals.size() == yvals.size()); | |
assert(order >= 1 && order <= xvals.size() - 1); | |
Eigen::MatrixXd A(xvals.size(), order + 1); | |
for (int i = 0; i < xvals.size(); i++) { | |
A(i, 0) = 1.0; | |
} | |
for (int j = 0; j < xvals.size(); j++) { | |
for (int i = 0; i < order; i++) { | |
A(j, i + 1) = A(j, i) * xvals(j); | |
} | |
} | |
auto Q = A.householderQr(); | |
auto result = Q.solve(yvals); | |
return result; | |
} | |
int main() { | |
uWS::Hub h; | |
// MPC is initialized here! | |
MPC mpc; | |
h.onMessage([&mpc](uWS::WebSocket<uWS::SERVER> ws, char *data, size_t length, | |
uWS::OpCode opCode) { | |
// "42" at the start of the message means there's a websocket message event. | |
// The 4 signifies a websocket message | |
// The 2 signifies a websocket event | |
string sdata = string(data).substr(0, length); | |
cout << sdata << endl; | |
if (sdata.size() > 2 && sdata[0] == '4' && sdata[1] == '2') { | |
string s = hasData(sdata); | |
if (s != "") { | |
auto j = json::parse(s); | |
string event = j[0].get<string>(); | |
if (event == "telemetry") { | |
// j[1] is the data JSON object | |
vector<double> ptsx = j[1]["ptsx"]; | |
vector<double> ptsy = j[1]["ptsy"]; | |
double px = j[1]["x"]; | |
double py = j[1]["y"]; | |
double psi = j[1]["psi"]; | |
double v = j[1]["speed"]; | |
// convert to car centered coordinates | |
vector<double> temp_ptsx; | |
vector<double> temp_ptsy; | |
temp_ptsx.resize(ptsx.size()); | |
temp_ptsy.resize(ptsy.size()); | |
for (int i = 0; i < ptsx.size(); i++) | |
{ | |
auto x = ptsx[i] - px; | |
auto y = ptsy[i] - py; | |
temp_ptsx[i] = x * CppAD::cos(psi) + y * CppAD::sin(psi); | |
temp_ptsy[i] = -x * CppAD::sin(psi) + y * CppAD::cos(psi); | |
} | |
// convert from vector<double> to Eigen::VectorXd to match input to polyfit | |
Eigen::VectorXd x (temp_ptsx.size()); | |
Eigen::VectorXd y (temp_ptsy.size()); | |
for (size_t i = 0; i < temp_ptsx.size(); i++) | |
{ | |
x(i) = temp_ptsx[i]; | |
y(i) = temp_ptsy[i]; | |
} | |
Eigen::VectorXd coeffs; | |
coeffs = polyfit(x, y, 3); | |
// The cross track error is calculated by evaluating at polynomial at x, f(x) | |
// and subtracting y. x and y are at 0 to represent the car. | |
double cte = polyeval(coeffs, 0); | |
// Due to the sign starting at 0, the orientation error is -f'(x). | |
// derivative of coeffs[0] + coeffs[1] * x + coeffs[2] * x^2 + coeffs[3] * x^3 -> coeffs[1] when x = 0 | |
double epsi = -atan(coeffs[1]); | |
// in the car centered coordinate, we have a new state: | |
Eigen::VectorXd new_state(6); | |
new_state << 0.0, 0.0, 0.0, v, cte, epsi; | |
// run solver to get the next state and the value of the actuators | |
vector<double> next_and_actuators = mpc.Solve(new_state, coeffs); | |
/* | |
* TODO: Calculate steeering angle and throttle using MPC. | |
* | |
* Both are in between [-1, 1]. | |
* | |
*/ | |
double steer_value; | |
double throttle_value; | |
steer_value = next_and_actuators[6]; | |
throttle_value = next_and_actuators[7]; | |
json msgJson; | |
// NOTE: Remember to divide by deg2rad(25) before you send the steering value back. | |
// Otherwise the values will be in between [-deg2rad(25), deg2rad(25] instead of [-1, 1]. | |
msgJson["steering_angle"] = -steer_value; | |
msgJson["throttle"] = throttle_value; | |
//Display the MPC predicted trajectory | |
vector<double> mpc_x_vals; | |
vector<double> mpc_y_vals; | |
//.. add (x,y) points to list here, points are in reference to the vehicle's coordinate system | |
// the points in the simulator are connected by a Green line | |
msgJson["mpc_x"] = mpc_x_vals; | |
msgJson["mpc_y"] = mpc_y_vals; | |
//Display the waypoints/reference line | |
vector<double> next_x_vals; | |
vector<double> next_y_vals; | |
//.. add (x,y) points to list here, points are in reference to the vehicle's coordinate system | |
// the points in the simulator are connected by a Yellow line | |
msgJson["next_x"] = temp_ptsx; | |
msgJson["next_y"] = temp_ptsy; | |
auto msg = "42[\"steer\"," + msgJson.dump() + "]"; | |
std::cout << msg << std::endl; | |
// Latency | |
// The purpose is to mimic real driving conditions where | |
// the car does actuate the commands instantly. | |
// | |
// Feel free to play around with this value but should be to drive | |
// around the track with 100ms latency. | |
// | |
// NOTE: REMEMBER TO SET THIS TO 100 MILLISECONDS BEFORE | |
// SUBMITTING. | |
this_thread::sleep_for(chrono::milliseconds(100)); | |
ws.send(msg.data(), msg.length(), uWS::OpCode::TEXT); | |
} | |
} else { | |
// Manual driving | |
std::string msg = "42[\"manual\",{}]"; | |
ws.send(msg.data(), msg.length(), uWS::OpCode::TEXT); | |
} | |
} | |
}); | |
// We don't need this since we're not using HTTP but if it's removed the | |
// program | |
// doesn't compile :-( | |
h.onHttpRequest([](uWS::HttpResponse *res, uWS::HttpRequest req, char *data, | |
size_t, size_t) { | |
const std::string s = "<h1>Hello world!</h1>"; | |
if (req.getUrl().valueLength == 1) { | |
res->end(s.data(), s.length()); | |
} else { | |
// i guess this should be done more gracefully? | |
res->end(nullptr, 0); | |
} | |
}); | |
h.onConnection([&h](uWS::WebSocket<uWS::SERVER> ws, uWS::HttpRequest req) { | |
std::cout << "Connected!!!" << std::endl; | |
}); | |
h.onDisconnection([&h](uWS::WebSocket<uWS::SERVER> ws, int code, | |
char *message, size_t length) { | |
ws.close(); | |
std::cout << "Disconnected" << std::endl; | |
}); | |
int port = 4567; | |
if (h.listen(port)) { | |
std::cout << "Listening to port " << port << std::endl; | |
} else { | |
std::cerr << "Failed to listen to port" << std::endl; | |
return -1; | |
} | |
h.run(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "MPC.h" | |
#include <cppad/cppad.hpp> | |
#include <cppad/ipopt/solve.hpp> | |
#include "Eigen-3.3/Eigen/Core" | |
using CppAD::AD; | |
// TODO: Set the timestep length and duration | |
size_t N = 0; | |
double dt = 0; | |
// This value assumes the model presented in the classroom is used. | |
// | |
// It was obtained by measuring the radius formed by running the vehicle in the | |
// simulator around in a circle with a constant steering angle and velocity on a | |
// flat terrain. | |
// | |
// Lf was tuned until the the radius formed by the simulating the model | |
// presented in the classroom matched the previous radius. | |
// | |
// This is the length from front to CoG that has a similar radius. | |
const double Lf = 2.67; | |
//Set the reference cross track error, orientation error and the velocity | |
double ref_cte = 0; | |
double ref_epsi = 0; | |
double ref_velocity = 40; | |
//Define all the states(start and end) of the variables that are used by solver | |
size_t x_start = 0; | |
size_t y_start = x_start + N; | |
size_t psi_start = y_start + N; | |
size_t v_start = psi_start + N; | |
size_t cte_start = v_start + N; | |
size_t epsi_start = cte_start + N; | |
size_t delta_start = epsi_start + N; | |
size_t a_start = delta_start + N - 1; | |
class FG_eval { | |
public: | |
// Fitted polynomial coefficients | |
Eigen::VectorXd coeffs; | |
FG_eval(Eigen::VectorXd coeffs) { this->coeffs = coeffs; } | |
typedef CPPAD_TESTVECTOR(AD<double>) ADvector; | |
void operator()(ADvector& fg, const ADvector& vars) { | |
// TODO: implement MPC | |
// fg a vector of constraints, x is a vector of constraints. | |
// NOTE: You'll probably go back and forth between this function and | |
// the Solver function below. | |
fg[0] = 0; | |
// Cost based on the reference state. | |
for (int i = 0; i < N; i++) | |
{ | |
fg[0] += CppAD::pow(vars[cte_start + i] - ref_cte, 2); | |
fg[0] += CppAD::pow(vars[epsi_start + i] - ref_epsi, 2); | |
fg[0] += CppAD::pow(vars[v_start + i] - ref_velocity, 2); | |
} | |
// Make sure to have minimum actuatir usage. Yay baby!! | |
for (int i = 0; i < N - 1; i++) | |
{ | |
fg[0] += CppAD::pow(vars[delta_start + i], 2); | |
fg[0] += CppAD::pow(vars[a_start + i], 2); | |
} | |
// To many sequential actuations. What the hell!! Minimize it | |
for (int i = 0; i < N - 2; i++) | |
{ | |
fg[0] += 100*CppAD::pow(vars[delta_start + i + 1] - vars[delta_start + i], 2); | |
fg[0] += 100*CppAD::pow(vars[a_start + i + 1] - vars[a_start + i], 2); | |
//fg[0] += 200*CppAD::pow(vars[delta_start + i + 1] - vars[delta_start + i], 2); | |
//fg[0] += 10*CppAD::pow(vars[a_start + i + 1] - vars[a_start + i], 2); | |
} | |
//Add some initial constraints | |
fg[1 + x_start] = vars[x_start]; | |
fg[1 + y_start] = vars[y_start]; | |
fg[1 + psi_start] = vars[psi_start]; | |
fg[1 + v_start] = vars[v_start]; | |
fg[1 + cte_start] = vars[cte_start]; | |
fg[1 + epsi_start] = vars[epsi_start]; | |
for (int i = 0; i < N - 1; i++) | |
{ | |
// State at time t+1 . | |
AD<double> x1 = vars[x_start + i + 1]; | |
AD<double> y1 = vars[y_start + i + 1]; | |
AD<double> psi1 = vars[psi_start + i + 1]; | |
AD<double> v1 = vars[v_start + i + 1]; | |
AD<double> cte1 = vars[cte_start + i + 1]; | |
AD<double> epsi1 = vars[epsi_start + i + 1]; | |
// State at time t. | |
AD<double> x0 = vars[x_start + i]; | |
AD<double> y0 = vars[y_start + i]; | |
AD<double> psi0 = vars[psi_start + i]; | |
AD<double> v0 = vars[v_start + i]; | |
AD<double> cte0 = vars[cte_start + i]; | |
AD<double> epsi0 = vars[epsi_start + i]; | |
// We need to consider the actuation at time t only. | |
AD<double> delta0 = vars[delta_start + i]; | |
AD<double> a0 = vars[a_start + i]; | |
AD<double> f0 = coeffs[0] + coeffs[1] * x0 + coeffs[2] * CppAD::pow(x0,2) + coeffs[3] * CppAD::pow(x0,3); | |
AD<double> psides0 = CppAD::atan(coeffs[1] + 2 * coeffs[2] * x0 + 3 * coeffs[3] * CppAD::pow(x0,2)); | |
//Implement the equations of the model as describe in the lessons | |
fg[2 + x_start + i] = x1 - (x0 + v0 * CppAD::cos(psi0) * dt); // x_[t+1] = x[t] + v[t] * cos(psi[t]) * dt | |
fg[2 + y_start + i] = y1 - (y0 + v0 * CppAD::sin(psi0) * dt); // y_[t+1] = y[t] + v[t] * sin(psi[t]) * dt | |
fg[2 + psi_start + i] = psi1 - (psi0 + v0 * delta0 / Lf * dt); // psi_[t+1] = psi[t] + v[t] / Lf * delta[t] * dt | |
fg[2 + v_start + i] = v1 - (v0 + a0 * dt); | |
fg[2 + cte_start + i] = cte1 - ((f0 - y0) + (v0 * CppAD::sin(epsi0) * dt)); | |
fg[2 + epsi_start + i] = epsi1 - ((psi0 - psides0) + v0 * delta0 / Lf * dt); | |
} | |
} | |
}; | |
// | |
// MPC class definition implementation. | |
// | |
MPC::MPC() {} | |
MPC::~MPC() {} | |
vector<double> MPC::Solve(Eigen::VectorXd state, Eigen::VectorXd coeffs) { | |
bool ok = true; | |
size_t i; | |
typedef CPPAD_TESTVECTOR(double) Dvector; | |
// TODO: Set the number of model variables (includes both states and inputs). | |
// For example: If the state is a 4 element vector, the actuators is a 2 | |
// element vector and there are 10 timesteps. The number of variables is: | |
// | |
// 4 * 10 + 2 * 9 | |
//elements of state vector | |
double x = state[0]; | |
double y = state[1]; | |
double psi = state[2]; | |
double v = state[3]; | |
double cte = state[4]; | |
double epsi = state[5]; | |
size_t n_vars = N*6 + (N-1)*2; //6 state elements and 2 actuators | |
// TODO: Set the number of constraints | |
size_t n_constraints = N*6; | |
// Initial value of the independent variables. | |
// SHOULD BE 0 besides initial state. | |
Dvector vars(n_vars); | |
for (int i = 0; i < n_vars; i++) | |
{ | |
vars[i] = 0; | |
} | |
vars[x_start] = x; | |
vars[y_start] = y; | |
vars[psi_start] = psi; | |
vars[v_start] = v; | |
vars[cte_start] = cte; | |
vars[epsi_start] = epsi; | |
Dvector vars_lowerbound(n_vars); | |
Dvector vars_upperbound(n_vars); | |
// TODO: Set lower and upper limits for variables. | |
for (int i = 0; i < delta_start; i++) | |
{ | |
vars_lowerbound[i] = -1.0e19; | |
vars_upperbound[i] = 1.0e19; | |
} | |
// Lower and upper limits for the constraints | |
// Should be 0 besides initial state. | |
Dvector constraints_lowerbound(n_constraints); | |
Dvector constraints_upperbound(n_constraints); | |
for (int i = 0; i < n_constraints; i++) | |
{ | |
constraints_lowerbound[i] = 0; | |
constraints_upperbound[i] = 0; | |
} | |
// The upper and lower limits of delta are set to -25 and 25 degrees (values in radians). | |
for (int i = delta_start; i < a_start; i++) | |
{ | |
vars_lowerbound[i] = -0.436332; //try multiplying LF | |
vars_upperbound[i] = 0.436332; | |
} | |
// Acceleration/decceleration upper and lower limits. | |
for (int i = a_start; i < n_vars; i++) | |
{ | |
vars_lowerbound[i] = -1.0; | |
vars_upperbound[i] = 1.0; | |
} | |
constraints_lowerbound[x_start] = x; | |
constraints_lowerbound[y_start] = y; | |
constraints_lowerbound[psi_start] = psi; | |
constraints_lowerbound[v_start] = v; | |
constraints_lowerbound[cte_start] = cte; | |
constraints_lowerbound[epsi_start] = epsi; | |
constraints_upperbound[x_start] = x; | |
constraints_upperbound[y_start] = y; | |
constraints_upperbound[psi_start] = psi; | |
constraints_upperbound[v_start] = v; | |
constraints_upperbound[cte_start] = cte; | |
constraints_upperbound[epsi_start] = epsi; | |
// object that computes objective and constraints | |
FG_eval fg_eval(coeffs); | |
// | |
// NOTE: You don't have to worry about these options | |
// | |
// options for IPOPT solver | |
std::string options; | |
// Uncomment this if you'd like more print information | |
options += "Integer print_level 0\n"; | |
// NOTE: Setting sparse to true allows the solver to take advantage | |
// of sparse routines, this makes the computation MUCH FASTER. If you | |
// can uncomment 1 of these and see if it makes a difference or not but | |
// if you uncomment both the computation time should go up in orders of | |
// magnitude. | |
options += "Sparse true forward\n"; | |
options += "Sparse true reverse\n"; | |
// NOTE: Currently the solver has a maximum time limit of 0.5 seconds. | |
// Change this as you see fit. | |
options += "Numeric max_cpu_time 0.5\n"; | |
// place to return solution | |
CppAD::ipopt::solve_result<Dvector> solution; | |
// solve the problem | |
CppAD::ipopt::solve<Dvector, FG_eval>( | |
options, vars, vars_lowerbound, vars_upperbound, constraints_lowerbound, | |
constraints_upperbound, fg_eval, solution); | |
// Check some of the solution values | |
ok &= solution.status == CppAD::ipopt::solve_result<Dvector>::success; | |
// Cost | |
auto cost = solution.obj_value; | |
std::cout << "Cost " << cost << std::endl; | |
// TODO: Return the first actuator values. The variables can be accessed with | |
// `solution.x[i]`. | |
// | |
// {...} is shorthand for creating a vector, so auto x1 = {1.0,2.0} | |
// creates a 2 element double vector. | |
std::vector<double> result; | |
result.push_back(solution.x[delta_start]); | |
result.push_back(solution.x[a_start]); | |
for (int i = 0; i < N-1; ++i) | |
{ | |
result.push_back(solution.x[x_start + i + 1]); | |
result.push_back(solution.x[y_start + i + 1]); | |
} | |
return result; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef MPC_H | |
#define MPC_H | |
#include <vector> | |
#include "Eigen-3.3/Eigen/Core" | |
using namespace std; | |
class MPC { | |
public: | |
MPC(); | |
virtual ~MPC(); | |
// Solve the model given an initial state and polynomial coefficients. | |
// Return the first actuatotions. | |
vector<double> Solve(Eigen::VectorXd state, Eigen::VectorXd coeffs); | |
}; | |
#endif /* MPC_H */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment