# preface

The last blog used pure C++ program to write the LQR simulation process, and the effect was OK. Then this blog was used to deploy this program to the ROS system of the experimental platform for simulation and experimental verification.

It is suggested to take a brief look at a program idea in the previous article: Visual studio C++:LQR trajectory tracking simulation

# thinking In fact, the idea is very simple, that is, path_planning to LQR_node can send the target points. It mainly depends on the ones in the red circle, and the others are used for visualization.

path_planning generation path is passed to LQR through topic /path_ In node, LQR_node for tracking.

# Code deployment

## 1, Custom library functions (LQR, tool, trajectory)

Based on the previous blog, I have written customized related library functions, including LQR, tool, trajectory, and matplot. However, matplot can be used in ROS instead of rviz, so it is discarded.

### 1.Tool Optimization

In order to make the path generation process more organized, the library function is optimized. In the Tool, the path point parameters are only set to x, y, yaw, and the curvature K selection is calculated for the obtained path in the controller. Added a function to calculate curvature K.

Tool.h

```#pragma once
#include <iostream>
#include <math.h>
#include <vector>
using namespace std;
#define pi acos(-1)

//Define path points
typedef struct waypoint {
int ID;
double x, y, yaw;//x,y
}waypoint;

//Define trolley status
typedef struct vehicleState {
double x, y, yaw, v, kesi;//x. Y, yaw, front wheel deflection angle kesi
}vehicleState;

//Define control quantity
typedef struct U {
double v;
double kesi;//Speed v, front wheel deflection angle kesi
}U;

double cal_K(vector<waypoint> waypoints, int index);//Calculate curvature K
```

Tool.cpp

```#include<iostream>
#include<LQR_track/Tool.h>

double cal_K(vector<waypoint> waypoints, int index){
double res;
//Solving first-order derivative and second-order derivative by difference method
double dx, dy, ddx, ddy;
if (index == 0) {
dx = waypoints.x - waypoints.x;
dy = waypoints.y - waypoints.y;
ddx = waypoints.x + waypoints.x - 2 * waypoints.x;
ddy = waypoints.y + waypoints.y - 2 * waypoints.y;
}
else if (index == (waypoints.size() - 1)) {
dx = waypoints[index].x - waypoints[index - 1].x;
dy = waypoints[index].y - waypoints[index - 1].y;
ddx = waypoints[index].x + waypoints[index - 2].x - 2 * waypoints[index].x;
ddy = waypoints[index].y + waypoints[index - 2].y - 2 * waypoints[index].y;
}
else {
dx = waypoints[index + 1].x - waypoints[index].x;
dy = waypoints[index + 1].y - waypoints[index].y;
ddx = waypoints[index + 1].x + waypoints[index - 1].x - 2 * waypoints[index].x;
ddy = waypoints[index + 1].y + waypoints[index - 1].y - 2 * waypoints[index].y;
}
//res.yaw = atan2(dy, dx);//yaw
//Calculate the curvature: let the curve r(t) =(x(t),y(t)), then the curvature k= (X'Y "- X" Y') / ((X') ^2 + (Y') ^2) ^ (3/2)
res = (ddy * dx - ddx * dy) / (sqrt(pow((pow(dx, 2) + pow(dy, 2)), 3)));
return res;
}
```

### 2.trajectory optimization

The parameter interface that can be imported from the launch file is set to design the track starting point, length limit and other information, and the user-defined track custom is added_ Path, just operate slowly according to the notes in trajectory.h.

trajectory.h

```#include <iostream>
#include <vector>
#include "LQR_track/Tool.h"
#include <string>
using namespace std;

class trajectory {
private:
vector<waypoint> waypoints;
double x_start,y_start,limit_x,limit_y;
string trajectory_type;
public:
trajectory(double initial_x_,double initial_y_,string type_,double limit_x_,double limit_y_):
x_start(initial_x_),y_start(initial_y_),trajectory_type(type_),limit_x(limit_x_),limit_y(limit_y_){};
//set reference trajectory
void refer_path();
vector<waypoint> get_path();
//void custom_path();
void line();
void wave1();
void wave2();

};```

trajectory.cpp

```#include <iostream>
#include <vector>
#include "LQR_track/trajectory.h"
#include <math.h>
using namespace std;
double dt = 0.01;//Trajectory calculation frequency

void trajectory::line(){
waypoint PP;
for (int i = 0; i < limit_x/dt; i++)
{
PP.ID = i;
PP.x = x_start + dt * i;//x:10 meters
PP.y = y_start ;//y
waypoints.push_back(PP);
}

}

void trajectory::wave1(){
waypoint PP;
for (int i = 0; i < limit_x/dt; i++)
{
PP.ID = i;
PP.x = x_start + dt * i;//x
PP.y = y_start + 1.0 * sin(dt*i / 1.5) + 0.5 * cos(dt*i / 1.0);//y
waypoints.push_back(PP);
}
}

void trajectory::wave2(){
waypoint PP;
for (int i = 0; i < limit_x/dt; i++)
{
PP.ID = i;
PP.x = x_start + dt * i;//x
PP.y = y_start - 0.2 * sin(dt*i / 0.4);//y
waypoints.push_back(PP);
}
}

//write the path you design
/*void trajectory::custom_path(){
waypoint PP;
for (int i = 0; i < limit_x/dt; i++)
{
PP.ID = i;
PP.x = ...;//x
PP.y = ...;//y
waypoints.push_back(PP);
}
}*/

void trajectory::refer_path() {
if(trajectory_type == "wave1")wave1();
else if(trajectory_type == "line")line();
else if(trajectory_type == "wave2")wave2();
//else if(trajectory_type == "custom_path")custom_path();//set the index

//Calculate tangent direction and store
for (int j=0;j<waypoints.size();j++){
double dx, dy, yaw;
if (j == 0) {
dx = waypoints.x - waypoints.x;
dy = waypoints.y - waypoints.y;
}
else if (j == (waypoints.size() - 1)) {
dx = waypoints[j].x - waypoints[j - 1].x;
dy = waypoints[j].y - waypoints[j - 1].y;
}
else {
dx = waypoints[j + 1].x - waypoints[j].x;
dy = waypoints[j + 1].y - waypoints[j].y;
}
yaw = atan2(dy, dx);//yaw
waypoints[j].yaw = yaw;
}
}

vector<waypoint> trajectory::get_path() {
return waypoints;
}
```

### 3.LQR optimization

It doesn't seem to be optimized. It's the same as the original. It's still attached with code:

LQR.h

```#include <iostream>
#include <Eigen/Dense>
#include "LQR_track/Tool.h"
using namespace std;

typedef Eigen::Matrix<double, 3, 3> Matrix3x3;
typedef Eigen::Matrix<double, 3, 1> Matrix3x1;
typedef Eigen::Matrix<double, 2, 1> Matrix2x1;
typedef Eigen::Matrix<double, 2, 2> Matrix2x2;
typedef Eigen::Matrix<double, 3, 2> Matrix3x2;
typedef Eigen::Matrix<double, 2, 3> Matrix2x3;

//Equation of state variable: x = [x_e y_e yaw_e]^t
//Equation of state control input: u = [v_e kesi_e]^t

class LQR
{
private:
Matrix3x3 A_d;
Matrix3x2 B_d;
Matrix3x3 Q;
Matrix2x2 R;
Matrix3x1 X_e;
Matrix2x1 U_e;

double L;//Vehicle wheelbase
double T;//Sampling interval
double x_car, y_car, yaw_car, x_d, y_d, yaw_d;//Vehicle pose and target point pose
double v_d, kesi_d;//Desired speed and front wheel deflection
double Q3;//Q weight, three items
double R2;//R weight, two items
int temp = 0;
public:
void initial(double L_, double T_, vehicleState car, waypoint waypoint, U U_r, double* Q_, double* R_);//initialization
void param_struct();//Structural equation of state parameters
Matrix2x3 cal_Riccati();//Solution of Riccati equation
U cal_vel();//LQR controller calculation speed
void test();
};

```

LQR.cpp

```#include <iostream>
#include "LQR_track/LQR.h"

using namespace std;

void LQR::initial(double L_, double T_, vehicleState car, waypoint waypoint, U U_r, double *Q_, double *R_) {

L = L_;
T = T_;
x_car = car.x; y_car = car.y; yaw_car = car.yaw;
x_d = waypoint.x; y_d = waypoint.y; yaw_d = waypoint.yaw;
v_d = U_r.v;kesi_d = U_r.kesi;

for (int i = 0; i < 3; i++) {
Q3[i] = Q_[i];
}
for (int j = 0; j < 2; j++) {
R2[j] = R_[j];
}
}

void LQR::param_struct() {
Q << Q3, 0.0, 0.0,
0.0, Q3, 0.0,
0.0, 0.0, Q3;
//Cout < < "Q matrix is: \n" < < Q < < endl;
R << R2, 0.0,
0.0, R2;
//Cout < < "R matrix is: \n" < < R < < endl;
A_d << 1.0, 0.0, -v_d * T * sin(yaw_d),
0.0, 1.0, v_d* T* cos(yaw_d),
0.0, 0.0, 1.0;
//Cout < < "a _d matrix is: \n" < < A_ d << endl;
B_d << T * cos(yaw_d), 0.0,
T* sin(yaw_d), 0.0,
T* tan(kesi_d), v_d* T / (L * cos(kesi_d) * cos(kesi_d));
//Cout < < "B _d matrix is: \n" < < B_ d << endl;
X_e << x_car - x_d, y_car - y_d, yaw_car - yaw_d;
//Cout < < "x _e matrix is: \n" < < x_ e << endl;

}

Matrix2x3 LQR::cal_Riccati() {
int N = 150;//Iteration termination times
double err = 100;//Error value
double err_tolerance = 0.01;//Error convergence threshold
Matrix3x3 Qf = Q;
Matrix3x3 P = Qf;//Iteration initial value
//Cout < < "P initial matrix is \n" < < p < < endl;
Matrix3x3 Pn;//Calculated latest P matrix
for (int iter_num = 0; iter_num < N; iter_num++) {
Pn = Q + A_d.transpose() * P * A_d - A_d.transpose() * P * B_d * (R + B_d.transpose() * P * B_d).inverse() * B_d.transpose() * P * A_d;//Iterative formula
//Cout < < "convergence error is" < < (PN - P).Array().Abs() maxCoeff() << endl;
//err = (Pn - P).array().abs().maxCoeff();//
err = (Pn - P).lpNorm<Eigen::Infinity>();
if(err < err_tolerance)//
{
P = Pn;
//Cout < < iterations < < ITER_ num << endl;
break;
}
P = Pn;

}

//Cout < < "P matrix is \n" < < p < < endl;
//P = Q;
Matrix2x3 K = -(R + B_d.transpose() * P * B_d).inverse() * B_d.transpose() * P * A_d;//Feedback rate K
return K;
}

U LQR::cal_vel() {
U output;
param_struct();
Matrix2x3 K = cal_Riccati();
Matrix2x1 U = K * X_e;
//Cout < < "feedback gain K is: \n" < < K < < endl;
//Cout < < control input u is: \n < < U < < endl;
output.v = U + v_d;
output.kesi = U + kesi_d;
return output;
}

void LQR::test() //Controller effect test
{
/*param_struct();
while (temp < 1000) {
Matrix2x3 K = cal_Riccati();
Matrix2x1 U = K * X_e;
//cout <<"state variable is:\n" <<X_e << endl;
//cout <<"control input is:\n"<< U << endl;
Matrix3x1 X_e_ = A_d * X_e + B_d * U;
X_e = X_e_;
temp++;
}*/
Matrix3x3 C,D,F;
C << 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0;
F << 1.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 7.0;
D = (C - F);
double BBBB = D.lpNorm<Eigen::Infinity>();
cout << BBBB << endl;
}```

### 4. Compilation and generation of custom libraries in ROS

You can refer to this article: 5. Call of user-defined header file and source file for ROS learning , if you don't want to see it, you can follow my process.

First, refer to the previous blog: Specific operation steps of ROS C++ calling osqp eigen Library , complete: add the Eigen module of ROS.

Then write the following code under Build:

Attention!!!: The absolute path of header file and source file must be written as your own!!

```###########
## Build ##
###########

include_directories(
include
\${catkin_INCLUDE_DIRS}
\${Eigen_INCLUDE_DIRS}
)

####The following absolute path must be written as your own!!!

/home/dlrobot/dlrobot_robot/src/617_robot/lqr_track/include/LQR_track/Tool.h
/home/dlrobot/dlrobot_robot/src/617_robot/lqr_track/include/LQR_track/trajectory.h
/home/dlrobot/dlrobot_robot/src/617_robot/lqr_track/include/LQR_track/LQR.h
/home/dlrobot/dlrobot_robot/src/617_robot/lqr_track/src/Tool.cpp
/home/dlrobot/dlrobot_robot/src/617_robot/lqr_track/src/trajectory.cpp
/home/dlrobot/dlrobot_robot/src/617_robot/lqr_track/src/LQR.cpp
)

That's good. Compile it to generate LQR_ The name of the track feature pack is LQR_LIBRARIES custom library.

## 2, Call library functions to complete track generation (path_planning.cpp)

Write a path publisher based on ROS publisher mechanism, and the path data type is nav_msgs::Path. You can call the trajectory directly to obtain the path, and then convert the data type (from vector<waypoint> to nav_msgs::Path) to publish it.

At the same time, the process of importing parameters from the launch file is: launch → path_planning→trajectory.h

path_planning.cpp

```#include <ros/ros.h>
#include <nav_msgs/Path.h>
#include <geometry_msgs/PoseStamped.h>
#include <iostream>
#include <vector>
#include "LQR_track/trajectory.h"
#include "ros/init.h"
#include "ros/node_handle.h"
#include "ros/publisher.h"
#include "ros/rate.h"
#include "ros/time.h"
#include <math.h>
#include <tf/tf.h>

double x_start,y_start,limit_x,limit_y;
string trajectory_type;
nav_msgs::Path produce_path(){
trajectory* trajec = new trajectory(x_start,y_start,trajectory_type,limit_x,limit_y);//Instantiate the trajectory class;
vector<waypoint> waypoint_vec;//Define the vector class to receive the path generated by trajec and get several groups of [ID,x,y]
nav_msgs::Path waypoints;//Define nav_msgs::Path class is used to construct the Path message of ROS

//Get path
trajec->refer_path();
waypoint_vec = trajec->get_path();

//Construct Path message for ROS
for(int i =0;i<waypoint_vec.size();i++){
geometry_msgs::PoseStamped this_pose;
this_pose.pose.position.x = waypoint_vec[i].x;
this_pose.pose.position.y = waypoint_vec[i].y;
this_pose.pose.position.z = 0;
geometry_msgs::Quaternion goal_quat = tf::createQuaternionMsgFromYaw(waypoint_vec[i].yaw);
//ROS_INFO("the yaw is %f",waypoint_vec[i].yaw);
this_pose.pose.orientation.x = goal_quat.x;
this_pose.pose.orientation.y = goal_quat.y;
this_pose.pose.orientation.z = goal_quat.z;
this_pose.pose.orientation.w = goal_quat.w;

waypoints.poses.push_back(this_pose);
}
return waypoints;//Return Path message for ROS
}

int main(int argc,char **argv){
ros::init(argc, argv, "path_produce");
ros::NodeHandle n;
ros::NodeHandle n_prv("~");
n_prv.param<double>("x_start",x_start,0.0);
n_prv.param<double>("y_start",y_start,0.0);
n_prv.param<string>("trajectory_type",trajectory_type,"line");
n_prv.param<double>("limit_x",limit_x,10);
n_prv.param<double>("limit_y",limit_y,0.0);
ros::Rate loop_rate(1);
while(ros::ok()){
nav_msgs::Path path = produce_path();
ROS_INFO("the trajectory size is: %ld",path.poses.size());
path_pub.publish(path);
loop_rate.sleep();
}
}```

## 3, Call the library function to complete the track tracking of LQR controller

### 1. Simulation program (LQR_node.cpp)

This file defines two classes, one is the class that processes the subscribed path (Class Path), and the other is the class that is responsible for track tracking (Class LQR_node). The implementation steps of its tracking function are as follows:

Implementation steps of tracking function:
1. Get the path point, which is completed when you subscribe to the callback function addpointcallback in the ros::spinOnce() phase under the while(ros::ok()) function
2. Search for path points
3. Obtain the path point information and construct the expected control quantity
4. First make a deceleration judgment to determine the longitudinal expected speed v_r. And the current action of the robot: tracking or reached the goal
5. Construct the weight matrix from the parameter Q of the launch file_ set,R_ Set import
6. Use LQR controller to calculate the speed and front wheel angle, in which the finite amplitude process
7. Post topics, including the calculation of angular velocity

8. Calculate the position and posture of the trolley at the next moment with the kinematic model
8. Judge whether it is necessary to turn off the controller. If it reaches the end point, turn it off
9.loop_rate.sleep(), end a cycle and prepare to start the next tracking work

Put the code directly:

LQR_node.cpp:

```#include <ros/ros.h>
#include <iostream>
#include "LQR_track/LQR.h"
#include <vector>
#include "ros/node_handle.h"
#include "ros/publisher.h"
#include "tf/LinearMath/Quaternion.h"
#include "visualization_msgs/Marker.h"
#include <nav_msgs/Path.h>
#include <geometry_msgs/Twist.h>
#include <geometry_msgs/Pose2D.h>
#include <tf/tf.h>
using namespace std;

double freq,L,V_DESIRED;//Sampling frequency, vehicle wheelbase, desired speed
double v_max;//Maximum speed
bool limit_v_and_kesi;//Whether the amplitude is limited (for akaman steering vehicles, the amplitude is limited, and the omni-directional vehicle is OK)
double initial_x,initial_y,initial_yaw,initial_v,initial_kesi;//Initialize vehicle posture, speed and front wheel angle
std::vector<double> Q_set;
std::vector<double> R_set;
double slow_LEVE1_DISTANCE,slow_LEVE2_DISTANCE,slow_LEVE1_V,slow_LEVE2_V,goal_tolerance_DISTANCE;//Define secondary deceleration distance and speed
#define pi acos(-1)
#define T 1/freq / / sampling time

vehicleState update_state(U control, vehicleState car) {
car.v = control.v;
car.kesi = control.kesi;
car.x += car.v * cos(car.yaw) * T;
car.y += car.v * sin(car.yaw) * T;
car.yaw += car.v / L * tan(car.kesi) * T;
return car;
}

class Path {
private:
vector<waypoint> path;
public:
{
path.push_back(p);
}

{
path = p;
}

//Number of path points
unsigned int Size()
{
return path.size();
}

//Get path points
waypoint Get_waypoint(int index)
{
waypoint p;
p.ID = path[index].ID;
p.x = path[index].x;
p.y = path[index].y;
p.yaw = path[index].yaw;
return p;
}

vector<waypoint> Get_waypoints(){
return path;
}

// Search the path points, compare the distance from the car to the starting point with the distance from the car to each point, and find the nearest target point index value
int Find_target_index(vehicleState state)
{
double min = abs(sqrt(pow(state.x - path.x, 2) + pow(state.y - path.y, 2)));
int index = 0;
for (int i = 0; i < path.size(); i++)
{
double d = abs(sqrt(pow(state.x - path[i].x, 2) + pow(state.y - path[i].y, 2)));
if (d < min)
{
min = d;
index = i;
}
}

//Before indexing to the end point, when (the distance Lf between the robot and the next target point) is less than (the distance L between the current target point and the next target point), index the next target point
if ((index + 1) < path.size())
{
double current_x = path[index].x; double current_y = path[index].y;
double next_x = path[index + 1].x; double next_y = path[index + 1].y;
double L_ = abs(sqrt(pow(next_x - current_x, 2) + pow(next_y - current_y, 2)));
double L_1 = abs(sqrt(pow(state.x - next_x, 2) + pow(state.y - next_y, 2)));
//ROS_INFO("L is %f,Lf is %f",L,Lf);
if (L_1 < L_)
{
index += 1;
}
}
return index;
}

};

class LQR_node {
private:
//car
vehicleState car;//Trolley status
U control;//Trolley control quantity [v,kesi]
double Q;
double R;
int lastIndex;//Last point index value
waypoint lastPoint;//Last point information
string action;//Current action of trolley: tracking or reach goal

//ROS
ros::Subscriber path_sub;//Subscription path, message type nav_msgs::Path
ros::Publisher vel_pub;//Publish speed information. The message type is geometry_msgs::Twist
ros::Publisher actual_state_pub;//Publish the actual position and posture of the car, and the message type is geometry_msgs::Pose2D
ros::Publisher visual_state_pub;//Publish the virtual track of the car to rviz, and the message type is visualization_msgs::Marker
geometry_msgs::Point visual_state_pose;
visualization_msgs::Marker visual_state_trajectory;
geometry_msgs::Pose2D actual_pose;
geometry_msgs::Twist vel_msg;
int temp;//Counting, used to judge whether the controller needs to be turned off when the end point is reached

public:
LQR* controller;
Path* path;

LQR_node(ros::NodeHandle& nh)//Add trajectory and initial position and posture of the car in initialization
{
controller = new LQR();
path = new Path();

//ROS:

//robot state initialize:
car.x = initial_x;car.y = initial_y;car.yaw = initial_yaw;car.v = initial_v;car.kesi = initial_kesi;
action = "the car is tracking!!";
}

~LQR_node() {
delete(controller);
delete(path);
}

vector<waypoint> waypoints;
for(int i=0;i<msg->poses.size();i++){
waypoint waypoint;
waypoint.x = msg->poses[i].pose.position.x;
waypoint.y = msg->poses[i].pose.position.y;
//Get angle
double roll,pitch,yaw;
tf::Quaternion quat;
tf::quaternionMsgToTF(msg->poses[i].pose.orientation, quat);
tf::Matrix3x3(quat).getRPY(roll, pitch, yaw);
waypoint.yaw = yaw;
waypoints.push_back(waypoint);
}
path->Add_new_point(waypoints);//Pass the vector array of path points to the path class
lastIndex = path->Size() - 1;
lastPoint = path->Get_waypoint(lastIndex);
}

double slow_judge(double distance) {
if (distance>=slow_LEVE2_DISTANCE&&distance <= slow_LEVE1_DISTANCE) {
return slow_LEVE1_V;
}
else if (distance>=goal_tolerance_DISTANCE&&distance < slow_LEVE2_DISTANCE) {
return slow_LEVE2_V;
}
else if (distance < goal_tolerance_DISTANCE) {
action = "the car has reached the goal!";
return 0.0;
}
else
{
return V_DESIRED;
}
}

//Controller process
void LQR_track() {
U U_r;
waypoint Point;
double K;
//ROS_INFO("path size is: %d",path->Size());
if(path->Size()!=0){
//Search path points
int target_index = path->Find_target_index(car);
//ROS_INFO("target index is : %d", target_index);

//Obtain path point information and construct expected control quantity
Point = path->Get_waypoint(target_index);//Get x,y
//ROS_INFO("waypoint information is x:%f,y:%f,yaw:%f", Point.x, Point.y, Point.yaw);
K = cal_K(path->Get_waypoints(),target_index);//Calculate curvature

//Deceleration judgment
double kesi = atan2(L * K, 1);
double v_distance = abs(sqrt(pow(car.x - lastPoint.x, 2) + pow(car.y - lastPoint.y, 2)));
//ROS_INFO("the distance is %f\n", v_distance);
U_r.v = slow_judge(v_distance);U_r.kesi = kesi;
ROS_INFO("%s",action.c_str());//Robot action
//ROS_INFO("the desired v is: %f,the desired kesi is: %f", U_r.v,U_r.kesi);

//Weight matrix
for(int q =0;q<Q_set.size();q++)Q[q] = Q_set[q];
for(int r =0;r<R_set.size();r++)R[r] = R_set[r];
if(Q>=R||Q>=R)
ROS_WARN("Q >= R, the calculation result may be abnormal,please set R < Q ");

//Using LQR controller,
controller->initial(L, T, car, Point, U_r, Q, R);//Initialize controller
control = controller->cal_vel();//Calculation input [v, kesi]
if(U_r.v==0)control.v = 0;//Judge that if the expected speed is 0, the robot will stop
if(limit_v_and_kesi)control = v_and_kesi_limit(control);//Speed and front wheel angle limiting
ROS_INFO("the speed is: %f,the kesi is: %f", control.v, control.kesi);
//ROS_INFO("the car position is x: %f, y: %f", car.x, car.y);

//Topic release
PUB();

//Trolley pose status update
car = update_state(control, car);

//Controller shutdown judgment
shutdown_controller();

ROS_INFO("--------------------------------------");
}
}

//Control start stop function
void node_control() {
ros::Rate loop_rate(freq);
Marker_set();//Set Marker attribute
//Set tf coordinate conversion
tf::Transform transform;
tf::Quaternion q;

while (ros::ok()) {
transform.setOrigin(tf::Vector3(car.x, car.y, 0));
q.setRPY(0, 0, car.yaw);
transform.setRotation(q);
br.sendTransform(tf::StampedTransform(transform, ros::Time::now(), "map", "car"));

ros::spinOnce();
LQR_track();
loop_rate.sleep();
}
}

void PUB(){
visual_state_pose.x = car.x; visual_state_pose.y = car.y;
actual_pose.x = car.x; actual_pose.y = car.y; actual_pose.theta = car.yaw;
vel_msg.linear.x = control.v; vel_msg.angular.z = control.v*tan(control.kesi)/L;//Yaw rate is w = v*tan(kesi)/L
visual_state_trajectory.points.push_back(visual_state_pose);//visualization_msgs::Marker is a container, so now you need to push it inside_ Back structure, and then publish
visual_state_pub.publish(visual_state_trajectory);//Publish virtual tracks
vel_pub.publish(vel_msg);//Release speed
actual_state_pub.publish(actual_pose);//Release pose
}

void shutdown_controller(){
if(action == "the car has reached the goal!"){
temp+=1;
if(temp ==50){
ROS_WARN("shutdown the LQR controller!");
temp = 0;
ros::shutdown();
}
}
}

void Marker_set(){
//Set message type parameters
visual_state_trajectory.ns = "LQR";
//Set point properties
visual_state_trajectory.id = 0;
visual_state_trajectory.type = visualization_msgs::Marker::POINTS;
visual_state_trajectory.scale.x = 0.02;
visual_state_trajectory.scale.y = 0.02;
visual_state_trajectory.color.r = 1.0;
visual_state_trajectory.color.a = 1.0;
}

U v_and_kesi_limit(U control_value){
if(control_value.v>=v_max)//Speed limiting
{
control_value.v = v_max;
ROS_WARN("The calculated value may be inaccurate ");
}
else if(control_value.v<=-v_max){
control_value.v = -v_max;
ROS_WARN("The calculated value may be inaccurate ");
}

if(control_value.kesi>=pi/2)//Front wheel angle limiting
{
control_value.kesi = pi/2;
ROS_WARN("The calculated value may be inaccurate ");
}
else if(control_value.kesi<=-pi/2){
control_value.kesi = -pi/2;
ROS_WARN("The calculated value may be inaccurate ");
}
return control_value;
}
};

int main(int argc, char** argv)
{
ros::init(argc, argv, "LQR_node");
ros::NodeHandle n;
ros::NodeHandle n_prv("~");

n_prv.param<double>("freq",freq,20);
n_prv.param<double>("L",L,0.2);
n_prv.param<double>("V_DESIRED",V_DESIRED,0.5);
n_prv.param<double>("v_max",v_max,1.0);
n_prv.param<double>("initial_x",initial_x,0.0);
n_prv.param<double>("initial_y",initial_y,2.0);
n_prv.param<double>("initial_yaw",initial_yaw,0.0);
n_prv.param<double>("initial_v",initial_v,0.0);
n_prv.param<double>("initial_kesi",initial_kesi,0.1);
n_prv.param<double>("slow_LEVE1_DISTANCE",slow_LEVE1_DISTANCE,5.0);
n_prv.param<double>("slow_LEVE2_DISTANCE",slow_LEVE2_DISTANCE,2.0);
n_prv.param<double>("goal_tolerance_DISTANCE",goal_tolerance_DISTANCE,0.1);
n_prv.param<double>("slow_LEVE1_V",slow_LEVE1_V,0.35);
n_prv.param<double>("slow_LEVE2_V",slow_LEVE2_V,0.15);
n_prv.param<bool>("limit_v_and_kesi",limit_v_and_kesi,false);
n_prv.param("Q_set",Q_set,Q_set);
n_prv.param("R_set",R_set,R_set);

LQR_node* node = new LQR_node(n);
node->node_control();
return (0);
}

```

### 2. Experimental procedure (LQR_node_world.cpp)

This experimental program is very similar to the simulation program, but instead of using the kinematic model to calculate the position and posture of the car, it samples the position and posture of the car, so a subscription interface (odom_sub) is added

Implementation steps of tracking function:
1. Get the current position, posture and path point of the car, and complete it when you subscribe to the callback functions odomcallback and addpointcallback in the ros::spinOnce() stage under the while(ros::ok()) function
2. Search for path points
3. Obtain the path point information and construct the expected control quantity
4. First make a deceleration judgment to determine the longitudinal expected speed v_r. And the current action of the robot: tracking or reached the goal
5. Construct the weight matrix and import it from the parameters Q=[],R=[] of the launch file
6. Use LQR controller to calculate the speed and front wheel angle, in which the finite amplitude process
7. Post topics, including the calculation of angular velocity
8. Judge whether it is necessary to turn off the controller. If it reaches the end point, turn it off
9.loop_rate.sleep(), end a cycle and prepare to start the next tracking work

Then the tf that releases the map to the car in the program is also removed. After positioning, there will be tf in rviz, so it is no longer needed here.

LQR_node_world.cpp

```#include <ros/ros.h>
#include <iostream>
#include "LQR_track/LQR.h"
#include <vector>
#include "ros/node_handle.h"
#include "ros/publisher.h"
#include "tf/LinearMath/Quaternion.h"
#include "visualization_msgs/Marker.h"
#include <nav_msgs/Path.h>
#include <geometry_msgs/Twist.h>
#include <geometry_msgs/Pose2D.h>
#include <nav_msgs/Odometry.h>
#include <tf/tf.h>
using namespace std;

double freq,L,V_DESIRED;//Sampling frequency, vehicle wheelbase, desired speed
double v_max;//Maximum speed
bool limit_v_and_kesi;//Whether to limit the amplitude (for akaman steering vehicles, it is necessary to limit the amplitude, and the omni-directional vehicle is optional)
std::vector<double> Q_set;//Q matrix
std::vector<double> R_set;//R matrix
double slow_LEVE1_DISTANCE,slow_LEVE2_DISTANCE,slow_LEVE1_V,slow_LEVE2_V,goal_tolerance_DISTANCE;//Define secondary deceleration distance and speed
#define pi acos(-1)
#define T 1/freq / / sampling time

class Path {
private:
vector<waypoint> path;
public:
{
path.push_back(p);
}

{
path = p;
}

//Number of path points
unsigned int Size()
{
return path.size();
}

//Get path points
waypoint Get_waypoint(int index)
{
waypoint p;
p.ID = path[index].ID;
p.x = path[index].x;
p.y = path[index].y;
p.yaw = path[index].yaw;
return p;
}

vector<waypoint> Get_waypoints(){
return path;
}

// Search the path points, compare the distance from the car to the starting point with the distance from the car to each point, and find the nearest target point index value
int Find_target_index(vehicleState state)
{
double min = abs(sqrt(pow(state.x - path.x, 2) + pow(state.y - path.y, 2)));
int index = 0;
for (int i = 0; i < path.size(); i++)
{
double d = abs(sqrt(pow(state.x - path[i].x, 2) + pow(state.y - path[i].y, 2)));
if (d < min)
{
min = d;
index = i;
}
}

//Before indexing to the end point, when (the distance L_1between the robot and the next target point) is less than (the distance L_1between the current target point and the next target point) Index the next target point when
if ((index + 1) < path.size())
{
double current_x = path[index].x; double current_y = path[index].y;
double next_x = path[index + 1].x; double next_y = path[index + 1].y;
double L_ = abs(sqrt(pow(next_x - current_x, 2) + pow(next_y - current_y, 2)));
double L_1 = abs(sqrt(pow(state.x - next_x, 2) + pow(state.y - next_y, 2)));
//ROS_INFO("L is %f,Lf is %f",L,Lf);
if (L_1 < L_)
{
index += 1;
}
}
return index;
}

};

class LQR_node {
private:
//car
vehicleState car;//Trolley status
U control;//Trolley control quantity [v,kesi]
double Q;
double R;
int lastIndex;//Last point index value
waypoint lastPoint;//Last point information
string action;//Current action of trolley: tracking or reach goal

//ROS
ros::Subscriber path_sub;//Subscription path, message type nav_msgs::Path
ros::Subscriber odom_sub;//Subscribe to the current position and attitude information of the trolley, and the message type is nav_msgs::Odometry
ros::Publisher vel_pub;//Publish speed information. The message type is geometry_msgs::Twist
ros::Publisher actual_state_pub;//Publish the actual position and posture of the car, and the message type is geometry_msgs::Pose2D
ros::Publisher visual_state_pub;//Publish the virtual track of the car to rviz, and the message type is visualization_msgs::Marker
geometry_msgs::Point visual_state_pose;
visualization_msgs::Marker visual_state_trajectory;
geometry_msgs::Pose2D actual_pose;
geometry_msgs::Twist vel_msg;
geometry_msgs::Pose2D pose2d_robot;
int temp;//Counting, used to judge whether the controller needs to be turned off when the end point is reached

public:
LQR* controller;
Path* path;

LQR_node(ros::NodeHandle& nh)//Add trajectory and initial position and posture of the car in initialization
{
controller = new LQR();
path = new Path();

//ROS:
odom_sub = nh.subscribe<nav_msgs::Odometry>("odom_tf", 1, &LQR_node::odomcallback, this);

//robot state initialize:
//car.x = car.y = car.yaw = car.v = car.kesi = 0;
action = "the car is tracking!!";
}

~LQR_node() {
delete(controller);
delete(path);
}

void odomcallback(const nav_msgs::Odometry::ConstPtr& odom_value)
{
pose2d_robot.x = odom_value->pose.pose.position.x;
pose2d_robot.y = odom_value->pose.pose.position.y;
pose2d_robot.theta = tf::getYaw(odom_value->pose.pose.orientation);
car.x = pose2d_robot.x;
car.y = pose2d_robot.y;
car.yaw = pose2d_robot.theta;
}

vector<waypoint> waypoints;
for(int i=0;i<msg->poses.size();i++){
waypoint waypoint;
waypoint.x = msg->poses[i].pose.position.x;
waypoint.y = msg->poses[i].pose.position.y;
//Get angle
double roll,pitch,yaw;
tf::Quaternion quat;
tf::quaternionMsgToTF(msg->poses[i].pose.orientation, quat);
tf::Matrix3x3(quat).getRPY(roll, pitch, yaw);
waypoint.yaw = yaw;
waypoints.push_back(waypoint);
}
path->Add_new_point(waypoints);//Pass the vector array of path points to the path class
lastIndex = path->Size() - 1;
lastPoint = path->Get_waypoint(lastIndex);
}

double slow_judge(double distance) {
if (distance>=slow_LEVE2_DISTANCE&&distance <= slow_LEVE1_DISTANCE) {
return slow_LEVE1_V;
}
else if (distance>=goal_tolerance_DISTANCE&&distance < slow_LEVE2_DISTANCE) {
return slow_LEVE2_V;
}
else if (distance < goal_tolerance_DISTANCE) {
action = "the car has reached the goal!";
return 0.0;
}
else
{
return V_DESIRED;
}
}

//Controller process
/*Steps:
1.Obtain the current posture and path point of the car, and complete it when the callback functions odomcallback and addpointcallback are subscribed in the ros::spinOnce() stage under the while(ros::ok()) function
2.Search path points
3.Obtain path point information and construct expected control quantity
4.First make a deceleration judgment to determine the longitudinal expected speed v_r. And the current action of the robot: tracking or reached the goal
5.Construct the weight matrix and import it from the parameters Q=[],R=[] of the launch file
6.The LQR controller is used to calculate the speed and front wheel angle, in which the finite amplitude process
7.Post topics, including the calculation of angular velocity
8.Judge whether the controller needs to be shut down. If it reaches the end point, it will be shut down
9.loop_rate.sleep()，End one cycle and prepare for the next.
*/
void LQR_track() {
U U_r;
waypoint Point;
double K;
//ROS_INFO("path size is: %d",path->Size());
if(path->Size()!=0){
//Search path points
int target_index = path->Find_target_index(car);
//ROS_INFO("target index is : %d", target_index);

//Obtain path point information and construct expected control quantity
Point = path->Get_waypoint(target_index);//Get x,y
//ROS_INFO("waypoint information is x:%f,y:%f,yaw:%f", Point.x, Point.y, Point.yaw);
K = cal_K(path->Get_waypoints(),target_index);//Calculate curvature

//Deceleration judgment
double kesi = atan2(L * K, 1);
double v_distance = abs(sqrt(pow(car.x - lastPoint.x, 2) + pow(car.y - lastPoint.y, 2)));
//ROS_INFO("the distance is %f\n", v_distance);
U_r.v = slow_judge(v_distance);U_r.kesi = kesi;
ROS_INFO("%s",action.c_str());//Robot action
//ROS_INFO("the desired v is: %f,the desired kesi is: %f", U_r.v,U_r.kesi);

//Weight matrix
for(int q =0;q<Q_set.size();q++)Q[q] = Q_set[q];
for(int r =0;r<R_set.size();r++)R[r] = R_set[r];
if(Q>=R||Q>=R)
ROS_WARN("Q >= R, the calculation result may be abnormal,please set R < Q ");

//Using LQR controller,
controller->initial(L, T, car, Point, U_r, Q, R);//Initialize controller
control = controller->cal_vel();//Calculation input [v, kesi]
if(U_r.v==0)control.v = 0;//Judge that if the expected speed is 0, the robot will stop
if(limit_v_and_kesi)control = v_and_kesi_limit(control);//Speed and front wheel angle limiting
ROS_INFO("the speed is: %f,the kesi is: %f", control.v, control.kesi);
//ROS_INFO("the car position is x: %f, y: %f", car.x, car.y);

//Topic release
PUB();

//Controller shutdown judgment
shutdown_controller();

ROS_INFO("--------------------------------------");
}
}

//Control start stop function
void node_control() {
ros::Rate loop_rate(freq);
Marker_set();//Set Marker attribute
while (ros::ok()) {
ros::spinOnce();
LQR_track();
loop_rate.sleep();
}
}

void PUB(){
visual_state_pose.x = car.x; visual_state_pose.y = car.y;
actual_pose.x = car.x; actual_pose.y = car.y; actual_pose.theta = car.yaw;
vel_msg.linear.x = control.v; vel_msg.angular.z = control.v*tan(control.kesi)/L;//Yaw rate is w = v*tan(kesi)/L
visual_state_trajectory.points.push_back(visual_state_pose);//visualization_msgs::Marker is a container, so now you need to push it inside_ Back structure, and then publish
visual_state_pub.publish(visual_state_trajectory);//Publish virtual tracks
vel_pub.publish(vel_msg);//Release speed
actual_state_pub.publish(actual_pose);//Release pose
}

void shutdown_controller(){
if(action == "the car has reached the goal!"){
temp+=1;
if(temp ==50){
ROS_WARN("shutdown the LQR controller!");
temp = 0;
ros::shutdown();
}
}
}

void Marker_set(){
//Set message type parameters
visual_state_trajectory.ns = "LQR";
//Set point properties
visual_state_trajectory.id = 0;
visual_state_trajectory.type = visualization_msgs::Marker::POINTS;
visual_state_trajectory.scale.x = 0.02;
visual_state_trajectory.scale.y = 0.02;
visual_state_trajectory.color.r = 1.0;
visual_state_trajectory.color.a = 1.0;
}

U v_and_kesi_limit(U control_value){
if(control_value.v>=v_max)//Speed limiting
{
control_value.v = v_max;
ROS_WARN("The calculated value may be inaccurate ");
}
else if(control_value.v<=-v_max){
control_value.v = -v_max;
ROS_WARN("The calculated value may be inaccurate ");
}

if(control_value.kesi>=pi/2)//Front wheel angle limiting
{
control_value.kesi = pi/2;
ROS_WARN("The calculated value may be inaccurate ");
}
else if(control_value.kesi<=-pi/2){
control_value.kesi = -pi/2;
ROS_WARN("The calculated value may be inaccurate ");
}
return control_value;
}
};

int main(int argc, char** argv)
{
ros::init(argc, argv, "LQR_node");
ros::NodeHandle n;
ros::NodeHandle n_prv("~");

n_prv.param<double>("freq",freq,20);
n_prv.param<double>("L",L,0.2);
n_prv.param<double>("V_DESIRED",V_DESIRED,0.5);
n_prv.param<double>("v_max",v_max,1.0);
n_prv.param<double>("slow_LEVE1_DISTANCE",slow_LEVE1_DISTANCE,5.0);
n_prv.param<double>("slow_LEVE2_DISTANCE",slow_LEVE2_DISTANCE,2.0);
n_prv.param<double>("goal_tolerance_DISTANCE",goal_tolerance_DISTANCE,0.1);
n_prv.param<double>("slow_LEVE1_V",slow_LEVE1_V,0.35);
n_prv.param<double>("slow_LEVE2_V",slow_LEVE2_V,0.15);
n_prv.param<bool>("limit_v_and_kesi",limit_v_and_kesi,false);
n_prv.param("Q_set",Q_set,Q_set);
n_prv.param("R_set",R_set,R_set);

LQR_node* node = new LQR_node(n);
node->node_control();
return (0);
}

```

### 3. Position and posture acquisition of small cars in the experiment (odom_listen_from_tf.cpp)

Everyone may have different positioning topics on the experimental platform. I'll simply write a posture acquisition program here, which is to monitor map to base_link or base_ It's good to output the coordinate transformation relationship of footprint. The output topic is odom_tf, the message type is nav_msgs::Odometry, this topic is LQR_ node_ The car posture subscribed in world.cpp.

odom_listen_from_tf.cpp

```#include <ros/ros.h>
#include <tf2/LinearMath/Vector3.h>
#include <tf2/LinearMath/Quaternion.h>
#include <tf2/LinearMath/Matrix3x3.h>
#include <tf2_ros/transform_listener.h>
#include <geometry_msgs/TransformStamped.h>
#include <nav_msgs/Odometry.h>

std::string base_frame;

int main(int argc, char** argv)
{
ros::init(argc, argv, "odom_listener");

ros::NodeHandle n;
ros::NodeHandle nh("~");

tf2_ros::Buffer tfBuffer;
tf2_ros::TransformListener odomListener(tfBuffer);
ros::Rate r(20);

while(ros::ok()){
geometry_msgs::TransformStamped transform;
try{
transform = tfBuffer.lookupTransform("map", base_frame, ros::Time(0));
}
catch (tf2::TransformException &ex){
ROS_ERROR("%s", ex.what());
ros::Duration(1.0).sleep();
continue;
}

tf2::Vector3 translation;
translation.setValue(transform.transform.translation.x,
transform.transform.translation.y,
transform.transform.translation.z);

tf2::Quaternion rotation;
rotation.setValue(transform.transform.rotation.x,
transform.transform.rotation.y,
transform.transform.rotation.z,
transform.transform.rotation.w);

tf2::Matrix3x3 m(rotation);
double roll, pitch, yaw;
m.getRPY(roll, pitch, yaw);

/*ROS_INFO("\n=== Got Transform ===\n"
" Translation\n"
" x : %f\n y : %f\n z : %f\n"
" Quaternion\n"
" x : %f\n y : %f\n z : %f\n w : %f\n"
" RPY\n"
" R : %f\n P : %f\n Y : %f",
translation.x(), translation.y(), translation.z(),
rotation.x(), rotation.y(), rotation.z(), rotation.w(),
roll, pitch, yaw);*/

nav_msgs::Odometry odom;

odom.pose.pose.position.x = translation.x();
odom.pose.pose.position.y = translation.y();
odom.pose.pose.position.z = translation.z();

odom.pose.pose.orientation.x = rotation.x();
odom.pose.pose.orientation.y = rotation.y();
odom.pose.pose.orientation.z = rotation.z();
odom.pose.pose.orientation.w = rotation.w();

odom.child_frame_id = base_frame;
odom.twist.twist.linear.x = 0;
odom.twist.twist.linear.y = 0;
odom.twist.twist.linear.z = 0;

odom_pub.publish(odom);
ros::spinOnce();
r.sleep();
}
return 0;
}
```

## 4, Compile all files

Don't forget on target_ link_ Add the previously generated library name LQR to libraries_ LIBRARIES:

```###########
## Build ##
###########

include_directories(
include
\${catkin_INCLUDE_DIRS}
\${Eigen_INCLUDE_DIRS}
)

/home/dlrobot/dlrobot_robot/src/617_robot/lqr_track/include/LQR_track/Tool.h
/home/dlrobot/dlrobot_robot/src/617_robot/lqr_track/include/LQR_track/trajectory.h
/home/dlrobot/dlrobot_robot/src/617_robot/lqr_track/include/LQR_track/LQR.h
/home/dlrobot/dlrobot_robot/src/617_robot/lqr_track/src/Tool.cpp
/home/dlrobot/dlrobot_robot/src/617_robot/lqr_track/src/trajectory.cpp
/home/dlrobot/dlrobot_robot/src/617_robot/lqr_track/src/LQR.cpp
)

\${catkin_LIB_DIRS}
\${Eigen_LIB_DIRS}
)

## 5, Configure rviz files

You can refer to the previous blog: Traditional model predictive control trajectory tracking - circular trajectory (function package has been updated)

## 6, Configure launch file

The chassis radar function and positioning function can be enabled without starting from the launch file of the function package, but there are three functions that must be enabled from the function package:

path_planning.launch

trajectory_ The type parameter is determined by the three track names written in trajectory.cpp. By default, three types are written, namely line, wave1, and wave2.

```<launch>
<node pkg="lqr_track" type="path_planning" name="path_planning" output="screen">
<param name="x_start" value="0.0"/>
<param name="y_start" value="-0.25"/>
<param name="trajectory_type" value="line"/><!--choose the trajectory type default:line,wave1,wave2-->
<param name="limit_x" value="4.0"/>
<param name="limit_y" value="0.0"/>
</node>

<!--rviz-->
<!--node name="path_rviz" pkg="rviz" type="rviz" required="true"
args="-d \$(find lqr_track)/rviz/path_show.rviz">
</node-->

</launch>
```

LQR_track.launch

There are vehicle information, initialization posture, controller parameter configuration, and relevant explanations or precautions are also written in the notes

```<launch>
<node pkg="lqr_track" type="LQR_node" name="LQR_node" output="screen">

<!--Basic vehicle information--><!--kesi is the Vehicle front wheel deflection angle-->
<param name="L" value="0.2"/><!--Vehicle wheelbase-->
<param name="V_DESIRED" value="0.5"/>
<param name="v_max" value="1.0"/>

<!--car initial state-->
<param name="initial_x" value="-0.127"/>
<param name="initial_y" value="-0.1474"/>
<param name="initial_yaw" value="0.0138"/>
<param name="initial_kesi" value="0.0"/>

<!--controller information-->
<param name="freq" value="20"/><!--control freq-->
<param name="slow_LEVE1_DISTANCE" value="4.0"/><!--First stage deceleration distance-->
<param name="slow_LEVE2_DISTANCE" value="1.0"/><!--Secondary deceleration distance-->
<param name="goal_tolerance_DISTANCE" value="0.1"/><!--Tracking stop distance-->
<param name="slow_LEVE1_V" value="0.5"/>
<param name="slow_LEVE2_V" value="0.15"/>
<param name="limit_v_and_kesi" value="true"/><!--If it is an akaman steering car, it must be limited. If it is an omni-directional car, it is optional-->
<rosparam param="Q_set">[1.0,1.0,1.0]</rosparam><!--State weight matrix Q = diag{Q1,Q2,Q3},please set Q<R-->
<rosparam param="R_set">[5.0,5.0]</rosparam><!--Control input weight matrix R = diag{R1,R2},please set Q<R-->
</node>
<!--rviz-->
<node name="lqr_track_rviz" pkg="rviz" type="rviz" required="true" args="-d \$(find lqr_track)/rviz/track.rviz"/>
</launch>
```

LQR_track_world.launch

```<launch>
<node pkg="lqr_track" type="LQR_node_world" name="LQR_node_world" output="screen">

<!--Basic vehicle information--><!--kesi is the Vehicle front wheel deflection angle-->
<param name="L" value="0.24"/><!--Vehicle wheelbase-->
<param name="V_DESIRED" value="0.5"/>
<param name="v_max" value="1.0"/>

<!--controller information-->
<param name="freq" value="20"/><!--control freq-->
<param name="slow_LEVE1_DISTANCE" value="4.0"/><!--First stage deceleration distance-->
<param name="slow_LEVE2_DISTANCE" value="1.0"/><!--Secondary deceleration distance-->
<param name="goal_tolerance_DISTANCE" value="0.2"/><!--Tracking stop distance-->
<param name="slow_LEVE1_V" value="0.5"/>
<param name="slow_LEVE2_V" value="0.15"/>
<param name="limit_v_and_kesi" value="true"/><!--If it is an akaman steering car, it must be limited. If it is an omni-directional car, it is optional-->
<rosparam param="Q_set">[1.0,1.0,1.0]</rosparam><!--State weight matrix Q = diag{Q1,Q2,Q3},please set Q<R-->
<rosparam param="R_set">[4.0,4.0]</rosparam><!--Control input weight matrix R = diag{R1,R2},please set Q<R-->
</node>

<node pkg="lqr_track" type="lqr_odom_listen" name="lqr_odom_listen" output="screen"/>

</launch>
```

# simulation result

This project has been simulated for three times:

## 1, Linear simulation trajectory_type = line LQR_line_simulation

## 2, Wave line 1 simulation trajectory_type = wave1 LQR_wave1_simulation

## 3, Wave line 2 simulation trajectory_type = wave2 LQR_wave2_simulation

# experimental result

Experimental process:

You can configure launch/turn in the function package_ on_ base_ and_ Ladier.launch file to start the territory radar at the same time

2. Turn on the robot positioning function to ensure that there is a map to base_ tf conversion of link (or base_footprint),
If you have a cartographer, you can run the following command: roslaunch lqr_track carto_robot_localization.launch

3. Carry out robot positioning work and go near the starting point of the path. Don't put it one meter beyond the starting point of the path. The tracking effect will become worse, which is related to the controller parameters, such as the selection of Q and P matrices

4.roslaunch lqr_track LQR_track_world.launch

5.roslaunch lqr_track path_planning.launch Among them, green is the desired trajectory, red is the actual trajectory of the tracking process, and the tracking error may be related to vehicle parameters (such as front and rear wheelbase) or controller parameters.

The specific experiments are as follows:

line_track_experiment

# Complete program

For the complete procedure, see: Bitbucket

git clone to the workspace, just follow the README.md operation in the function package

Posted by Gruzin on Wed, 10 Aug 2022 22:35:01 +0530