├── trajectory_tracking
├── msg
│ └── Traj.msg
├── src
│ ├── obstacles.txt
│ └── tracking_controller.cpp
├── launch
│ └── main.launch
├── trajectories
│ ├── linear.cpp
│ ├── eight_shape.cpp
│ └── circular.cpp
├── scripts
│ └── plotter.py
├── package.xml
└── CMakeLists.txt
└── README.md
/trajectory_tracking/msg/Traj.msg:
--------------------------------------------------------------------------------
1 | geometry_msgs/Pose2D pose
2 | geometry_msgs/Twist ref_vel
3 |
--------------------------------------------------------------------------------
/trajectory_tracking/src/obstacles.txt:
--------------------------------------------------------------------------------
1 | 0.100696 2.88356 0.182349
2 | -2.34187 1.82967 0.163313
3 | -0.845611 -2.76587 0.192746
4 | 2.99339 -0.0522498 0.180914
5 |
--------------------------------------------------------------------------------
/trajectory_tracking/launch/main.launch:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | center_x: 0
12 | center_y: 0
13 | radius: 3
14 | period: 150
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | robot_radius: 0.089
23 |
24 |
25 |
26 |
49 |
50 |
--------------------------------------------------------------------------------
/trajectory_tracking/trajectories/linear.cpp:
--------------------------------------------------------------------------------
1 | #include "ros/ros.h"
2 | #include "trajectory_tracking/Traj.h"
3 | #include
4 | #include
5 |
6 | class LinearTrajectory{
7 | private:
8 | ros::NodeHandle nh;
9 | ros::Publisher traj_pub;
10 |
11 | public:
12 | LinearTrajectory(){
13 | traj_pub = nh.advertise("/linear", 10); // define publisher topic
14 | }
15 | double period;
16 | void publish_trajectory(double t){
17 | trajectory_tracking::Traj traj;
18 |
19 | double x_start, y_start, gradient; // declare line parameters
20 | // get node name
21 | std::string node_name = ros::this_node::getName();
22 | //get line parameters
23 | nh.getParam(node_name + "/x_start", x_start);
24 | nh.getParam(node_name + "/y_start", y_start);
25 | nh.getParam(node_name + "/gradient", gradient);
26 | nh.getParam(node_name + "/period", period);
27 |
28 |
29 | double k = 6 * M_PI / period; // define constant K = 6Pi/T
30 | // define equations for positions x and y
31 | traj.pose.x = x_start + (1/gradient) * (k * t - y_start);
32 | traj.pose.y = y_start + gradient * (k * t - x_start);
33 | // compute the first derivatives of the positions (velocity)
34 | double x_dot = (1/gradient) * k;
35 | double y_dot = gradient * k;
36 | // compute theta
37 | traj.pose.theta = atan2(y_dot, x_dot);
38 | // compute the second derivatives of the positions (acceleration)
39 | double x_dotdot = 0;
40 | double y_dotdot = 0;
41 | // compute reference velocities
42 | traj.ref_vel.linear.x = sqrt(pow(x_dot, 2) + pow(y_dot, 2));
43 | traj.ref_vel.angular.z = (x_dot * y_dotdot + y_dot * x_dotdot) / (pow(x_dot, 2) + pow(y_dot, 2));
44 | // publish trajectory information
45 | traj_pub.publish(traj);
46 | }
47 | };
48 |
49 | int main(int argc, char** argv){
50 | ros::init(argc, argv, "linear");
51 |
52 | LinearTrajectory linear_trajectory;
53 |
54 | ros::Rate loop_rate(10);
55 | while (ros::ok()){
56 | time_t begin, now;
57 | time(&begin);
58 |
59 | double t = 0;
60 | while(t <= linear_trajectory.period){
61 | linear_trajectory.publish_trajectory(t);
62 | t = time(&now) - begin;
63 | loop_rate.sleep();
64 | }
65 | }
66 | return 0;
67 | }
68 |
--------------------------------------------------------------------------------
/trajectory_tracking/trajectories/eight_shape.cpp:
--------------------------------------------------------------------------------
1 | #include "ros/ros.h"
2 | #include "trajectory_tracking/Traj.h"
3 | #include
4 | #include
5 |
6 | class EightShapeTrajectory{
7 | private:
8 | ros::NodeHandle nh;
9 | ros::Publisher traj_pub;
10 |
11 | public:
12 | EightShapeTrajectory(){
13 | traj_pub = nh.advertise("/eight_shape", 10); // define publisher topic
14 | }
15 | double period;
16 | void publish_trajectory(double t){
17 | trajectory_tracking::Traj traj;
18 |
19 | double center_x, center_y, radius; // declare eight_shape parameters
20 | // get node name
21 | std::string node_name = ros::this_node::getName();
22 | //get eight_shape parameters
23 | nh.getParam(node_name + "/center_x", center_x);
24 | nh.getParam(node_name + "/center_y", center_y);
25 | nh.getParam(node_name + "/radius", radius);
26 | nh.getParam(node_name + "/period", period);
27 |
28 |
29 | double k = 2 * M_PI / period; // define constant K = 2Pi/T
30 | // define equations for positions x and y
31 | traj.pose.x = center_x + 1.5*radius * sin(k * t);
32 | traj.pose.y = center_y + radius * sin(2*k * t);
33 | // compute the first derivatives of the positions (velocity)
34 | double x_dot = k * 1.5*radius * cos(k * t);
35 | double y_dot = 2*k * radius * cos(2*k * t);
36 | // compute theta
37 | traj.pose.theta = atan2(y_dot, x_dot);
38 | // compute the second derivatives of the positions (acceleration)
39 | double x_dotdot = -pow(k, 2) * 1.5*radius * cos(k * t);
40 | double y_dotdot = -pow(2*k, 2) * radius * sin(2*k * t);
41 | // compute reference velocities
42 | traj.ref_vel.linear.x = sqrt(pow(x_dot, 2) + pow(y_dot, 2));
43 | traj.ref_vel.angular.z = (x_dot * y_dotdot + y_dot * x_dotdot) / (pow(x_dot, 2) + pow(y_dot, 2));
44 | // publish trajectory information
45 | traj_pub.publish(traj);
46 | }
47 | };
48 |
49 | int main(int argc, char** argv){
50 | ros::init(argc, argv, "eight_shape");
51 |
52 | EightShapeTrajectory eight_shape_trajectory;
53 |
54 | ros::Rate loop_rate(10);
55 | while (ros::ok()){
56 | time_t begin, now;
57 | time(&begin);
58 |
59 | double t = 0;
60 | while(t <= eight_shape_trajectory.period){
61 | eight_shape_trajectory.publish_trajectory(t);
62 | t = time(&now) - begin;
63 | loop_rate.sleep();
64 | }
65 | }
66 | return 0;
67 | }
68 |
--------------------------------------------------------------------------------
/trajectory_tracking/scripts/plotter.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # import ROS Libraries
4 | import rospy
5 | from nav_msgs.msg import Odometry
6 | from trajectory_tracking.msg import Traj
7 |
8 | import rospkg
9 |
10 | # import Matplotlib
11 | import matplotlib.pyplot as plt
12 | import matplotlib.animation as animation
13 | import time
14 | import math
15 | import random
16 |
17 | # global variables decalration
18 | global traj, robot_x, robot_y
19 |
20 | traj = Traj()
21 |
22 | xd = []
23 | yd = []
24 | xrobot = []
25 | yrobot = []
26 | colors = [
27 | (0.078, 0.804, 0.784, 1), (0.000, 0.412, 0.573, 1), (0.671, 0.718, 0.718, 1),
28 | (0.463, 0.365, 0.412, 1), (1.000, 0.580, 0.439, 1), (0.424, 0.478, 0.537, 1),
29 | (0.686, 0.255, 0.329, 1), (0.945, 0.353, 0.133, 1), (0.431, 0.188, 0.294, 1),
30 | (0.635, 0.871, 0.817, 1)
31 | ]
32 |
33 | def obstacles(path):
34 | global obsts
35 | with open(path + '/src/obstacles.txt') as f:
36 | obsts = f.readlines()
37 | obsts = [obst.strip() for obst in obsts]
38 | for i in range(len(obsts)):
39 | obsts[i] = [float(x) for x in obsts[i].split()]
40 |
41 | # callback function for odom data
42 | def odom_callbk(msg):
43 | global robot_x, robot_y
44 |
45 | robot_x = msg.pose.pose.position.x
46 | robot_y = msg.pose.pose.position.y
47 |
48 | # callback function to get trajectory
49 | def circular_traj_callbk(msg):
50 | global traj
51 |
52 | traj.pose.x = msg.pose.x
53 | traj.pose.y = msg.pose.y
54 |
55 | # animate function to plot data
56 | def animate1(i):
57 |
58 | xd.append(float(traj.pose.x))
59 | yd.append(float(traj.pose.y))
60 | ax1.plot(xd,yd, 'r',linestyle='solid',label="reference trajectory",lw=1)
61 |
62 | xrobot.append(float(robot_x))
63 | yrobot.append(float(robot_y))
64 | ax1.plot(xrobot, yrobot, 'k', linestyle='dashdot', label="robot's trajectory", lw=1)
65 |
66 | for obst in obsts:
67 | ax1.add_artist( plt.Circle((obst[0], obst[1]), obst[2], color=colors[random.randint(0, 9)], alpha=0.9))
68 |
69 | ax1.grid(True)
70 | ax1.legend()
71 | ax1.axis("equal") # comment this line out if you'd like to set limits for x and y axis
72 |
73 |
74 | if __name__ == '__main__':
75 |
76 | # initialization of ROS node
77 | rospy.init_node('plotter', anonymous=True) #make node
78 | rospy.Subscriber("/traj_topic", Traj, circular_traj_callbk, queue_size=10)
79 | rospy.Subscriber("/odom", Odometry, odom_callbk, queue_size=10)
80 |
81 | fig1 = plt.figure()
82 | ax1 = fig1.add_subplot(111)
83 |
84 | rospack = rospkg.RosPack()
85 | path = rospack.get_path('trajectory_tracking')
86 | obstacles(path)
87 |
88 | # uncomment to set axis limits
89 | # ax1.set_xlim([-17, 17])
90 | # ax1.set_ylim([-17, 17])
91 |
92 | time.sleep(0.1)
93 | ani1 = animation.FuncAnimation(fig1, animate1, interval=100)
94 | plt.show()
95 | rospy.spin()
96 |
--------------------------------------------------------------------------------
/trajectory_tracking/package.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | trajectory_tracking
4 | 0.0.0
5 | The trajectory_tracking package
6 |
7 |
8 |
9 |
10 | yusah
11 |
12 |
13 |
14 |
15 |
16 | TODO
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 | message_runtime
47 |
48 |
49 |
50 |
51 | catkin
52 | roscpp
53 | rospy
54 | std_msgs
55 | geometry_msgs
56 | roscpp
57 | rospy
58 | std_msgs
59 | geometry_msgs
60 | roscpp
61 | rospy
62 | std_msgs
63 | geometry_msgs
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/trajectory_tracking/trajectories/circular.cpp:
--------------------------------------------------------------------------------
1 | #include "ros/ros.h"
2 | #include "ros/package.h"
3 | #include "trajectory_tracking/Traj.h"
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | class CircularTrajectory{
12 | private:
13 | ros::NodeHandle nh;
14 | ros::Publisher traj_pub;
15 |
16 | public:
17 | CircularTrajectory(){
18 | traj_pub = nh.advertise("/circular", 10); // define publisher topic
19 | }
20 | double center_x, center_y, radius; // declare circle parameters
21 | double period;
22 | void publish_trajectory(double t){
23 | trajectory_tracking::Traj traj;
24 |
25 | // get node name
26 | std::string node_name = ros::this_node::getName();
27 | //get circle parameters
28 | nh.getParam(node_name + "/center_x", center_x);
29 | nh.getParam(node_name + "/center_y", center_y);
30 | nh.getParam(node_name + "/radius", radius);
31 | nh.getParam(node_name + "/period", period);
32 |
33 | double k = 2 * M_PI / period; // define constant K = 2Pi/T
34 | // define equations for positions x and y
35 | traj.pose.x = center_x + radius * cos(k * t);
36 | traj.pose.y = center_y + radius * sin(k * t);
37 | // compute the first derivatives of the positions (velocity)
38 | double x_dot = -k * radius * sin(k * t);
39 | double y_dot = k * radius * cos(k * t);
40 | // compute theta
41 | traj.pose.theta = atan2(y_dot, x_dot);
42 | // compute the second derivatives of the positions (acceleration)
43 | double x_dotdot = -pow(k, 2) * radius * cos(k * t);
44 | double y_dotdot = -pow(k, 2) * radius * sin(k * t);
45 | // compute reference velocities
46 | traj.ref_vel.linear.x = sqrt(pow(x_dot, 2) + pow(y_dot, 2));
47 | traj.ref_vel.angular.z = (x_dot * y_dotdot + y_dot * x_dotdot) / (pow(x_dot, 2) + pow(y_dot, 2));
48 | // publish trajectory information
49 | traj_pub.publish(traj);
50 | }
51 |
52 | void random_obstacles(std::string path){
53 | std::vector angles;
54 | srand(time(0));
55 | angles.push_back(rand()%(90-50) + 50);
56 | angles.push_back(rand()%(180-140) + 140);
57 | angles.push_back(rand()%(270-230) + 230);
58 | angles.push_back(rand()%(360-320) + 320);
59 |
60 | std::ofstream file;
61 | file.open(path);
62 | for(auto i = 0; i != angles.size(); ++i){
63 | radius = ((0.26) * rand()) / RAND_MAX + radius - 0.15;
64 |
65 | double x = center_x + radius * cos(angles[i]*M_PI/180);
66 | double y = center_y + radius * sin(angles[i]*M_PI/180);
67 | double r = ((0.22 - 0.12) * rand()) / RAND_MAX + 0.12;
68 |
69 | file << x << " " << y << " " << r << "\n";
70 | }
71 | file.close();
72 | }
73 | };
74 |
75 | int main(int argc, char** argv){
76 | ros::init(argc, argv, "circular");
77 | ros::NodeHandle private_node_handle("~");
78 |
79 | std::string path = private_node_handle.param("path", "default");
80 |
81 | CircularTrajectory circular_trajectory;
82 |
83 | ros::Rate loop_rate(10);
84 |
85 | while (ros::ok()){
86 | time_t begin, now;
87 | time(&begin);
88 |
89 | double t = 0;
90 | circular_trajectory.publish_trajectory(t);
91 | circular_trajectory.random_obstacles(path);
92 | while(t <= circular_trajectory.period){
93 | circular_trajectory.publish_trajectory(t);
94 | t = time(&now) - begin;
95 | loop_rate.sleep();
96 | }
97 | }
98 | return 0;
99 | }
100 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Trajectory tracking with obstacle avoidance
2 |
3 | ## 1. Overview
4 | This ROS package uses the tracking controller proposed in this [paper](https://www.sciencedirect.com/science/article/abs/pii/S0005109897000551) coupled with a new variant of the limit-cycle obstacle avoidance strategy to implement trajectory tracking with obstacle avoidance for nonholonomic constrained mobile robots. It uses hierarchical action selection to activate the avoidance controller when a disturbing obstacle is detected, and then switch to tracking controller in an obstacle free location.
5 |
6 | ## 2. Build
7 | * Clone the repo to the src folder of your catkin workspace
8 | ```
9 | $ git clone https://github.com/yabdulra/Trajectory-tracking-with-obstacle-avoidance.git
10 | ```
11 | To use the tracking controller without obstacle avoidance, clone the repo using:
12 | ```
13 | $ git clone -b v1.0 https://github.com/yabdulra/Trajectory-tracking-with-obstacle-avoidance.git
14 | ```
15 |
16 | * Change directory to `catkin_ws` and build.
17 | ```
18 | $ cd ..
19 | $ catkin_make
20 | ```
21 |
22 | ## 3. Configuration
23 | A default launch file for the tracking controller, reference trajectory, and the plotter can be found in the `trajectory_tracking` package directory. The launch file contains the following configurable parameters:
24 | * path: lets you define where to store obstacles' data. The avoidance controller in the robot node uses this data to determine the obstacle the robot must avoid, while the plotter node uses it to plot a representation of the obstacle.
25 | * center_x, center_y, radius, period: used to define parameters for the circular trajectory.
26 | * robot_radius: the avoidance controller uses this along with other predefined parameters to define safe margin for collision avoidance.
27 |
28 | Below is the sample configuration which can be modified in the launch file.
29 | ```
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | center_x: 0
41 | center_y: 0
42 | radius: 3
43 | period: 150
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | robot_radius: 0.089
52 |
53 |
54 |
55 | ```
56 |
57 | ## 4. Nodes
58 | * robot:
59 | - subscribed topics:
60 | * traj_topic (trajectory_tracking/Traj): The custom message contains the 2D pose of the trajectory and a referecne linear and angular velocities. This topic is remapped to subscribe to desired trajectory topic (circular, linear, 8_shape) of the same message type. The tracking controller uses this message to calculate appropriate control inputs to the robot.
61 | * odom ([nav_msgs/Odometry](http://docs.ros.org/en/noetic/api/nav_msgs/html/msg/Odometry.html)): Contains robot's pose data used by both controllers to calculate appropriate control inputs to the robot.
62 | - published topics:
63 | * cmd_vel ([geometry_msgs/Twist](http://docs.ros.org/en/noetic/api/geometry_msgs/html/msg/Twist.html)): the node uses this topic to publish velocity commands to drive the robot.
64 | * circular:
65 | - published topics:
66 | * circular (trajectory_tracking/Traj): contains pose and reference velocity of the circular trajectory.
67 | * plotter:
68 | - sbscribed topics:
69 | * odom ([nav_msgs/Odometry](http://docs.ros.org/en/noetic/api/nav_msgs/html/msg/Odometry.html)): The node uses this data to plot the robot's trajectory.
70 | * traj_topic (trajectory_tracking/Traj): The node uses thid data to plot the reference trajectory. This topic is remapped to subscribe to desired trajectory topic (circular, linear, 8_shape) of the same message type.
71 | ## 5. Launch
72 | * Source your workspace and launch your robot.
73 |
74 | * In a new terminal, source your workspace and launch the trajectory_tracking package.
75 | ```
76 | $ source devel/setup.bash
77 | $ roslaunch trajectory_tracking main.launch
78 | ```
79 |
80 | ## 6. Visualization
81 | Once the nodes are launched, you can visualize the simulated obstacles on the plotter as well as the robot's and reference trajectories. You can see the robot deviating from the referecne trajectory to avoid any obstacle that can obstruct its motion, and then switching back to tracking the trajectory when the obstacle is completely avoided.
82 | Below are sample views of a robot tracking a circular trajectory of radius 3m centered at the origin. The plots depict the robot avoiding some randomly generated disturbing obstacles.
83 | 
84 | 
85 |
--------------------------------------------------------------------------------
/trajectory_tracking/src/tracking_controller.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include "trajectory_tracking/Traj.h"
5 | #include
6 | #include
7 |
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | using std::vector;
15 |
16 | class Trajectory{
17 | private:
18 | ros::NodeHandle n;
19 | ros::Subscriber traj;
20 | public:
21 | Trajectory(){
22 | traj = n.subscribe("/traj_topic", 10, &Trajectory::traj_callback, this);
23 | }
24 | double x_traj, y_traj, theta_traj, v_r, w_r;
25 |
26 | void traj_callback(const trajectory_tracking::Traj &msg){
27 | // get trajectory info
28 | x_traj = msg.pose.x;
29 | y_traj = msg.pose.y;
30 | theta_traj = msg.pose.theta;
31 | v_r = msg.ref_vel.linear.x;
32 | w_r = msg.ref_vel.angular.z;
33 | }
34 | };
35 |
36 | class Robot{
37 | private:
38 | ros::NodeHandle nh;
39 | ros::Publisher vel_pub;
40 | ros::Subscriber odom;
41 | double x_pos = 0.0, y_pos = 0.0, theta = 0.0, r_radius, xs = 0.0, ys = 0.0;
42 |
43 | public:
44 | Robot(){
45 | vel_pub = nh.advertise("/cmd_vel", 10);
46 | odom = nh.subscribe("odom", 10, &Robot::odom_callback, this);
47 | std::string node_name = ros::this_node::getName();
48 | nh.getParam(node_name + "/robot_radius", r_radius);
49 | }
50 | vector sign_xs, sign_ys;
51 | double R_i;
52 | bool avoidance_suceeded = false;
53 |
54 | double get_robot_radius(){
55 | return r_radius;
56 | }
57 |
58 | void odom_callback(const nav_msgs::Odometry::ConstPtr &msg){
59 | // get robot's x and y position
60 | x_pos = msg->pose.pose.position.x;
61 | y_pos = msg->pose.pose.position.y;
62 | // convert theta from Quaternion to Euler and get robot's yaw
63 | tf::Quaternion quat;
64 | tf::quaternionMsgToTF(msg->pose.pose.orientation, quat);
65 | double roll, pitch, yaw;
66 | tf::Matrix3x3(quat).getRPY(roll, pitch, yaw);
67 | theta = yaw;
68 | }
69 |
70 | void tracking_controller(const Trajectory &t){
71 | geometry_msgs::Twist vel;
72 | // error dynamics
73 | double e_x = (t.x_traj - x_pos) * cos(theta) + (t.y_traj - y_pos) * sin(theta);
74 | double e_y = -(t.x_traj - x_pos) * sin(theta) + (t.y_traj - y_pos) * cos(theta);
75 | double e_theta = t.theta_traj - theta;
76 | e_theta = atan2(sin(e_theta), cos(e_theta)); // normalize theta between -PI and PI
77 | // controller parameters
78 | double ep = 0.9, b = 18;
79 | double a = sqrt(pow(t.v_r, 2) + pow(t.w_r, 2));
80 | double kx = 2 * ep * a, ky = b * abs(t.v_r), kz = kx;
81 | // compute control inputs
82 | vel.linear.x = t.v_r * cos(e_theta) + kx * e_x;
83 | vel.angular.z = t.w_r + (ky * e_y * sin(e_theta) / e_theta) + kx * e_theta;
84 | vel_pub.publish(vel);
85 | }
86 | // takes in a vector of obstacles and compute the distance of each obstacle from the robot
87 | // returns a vector of an obstacle with the least distance to the robot
88 | vector closest_obstacle(vector > &obstacles){
89 | vector dist_to_robot(obstacles.size());
90 | vector obstacle;
91 | for(auto i = 0; i != obstacles.size(); ++i ){
92 | dist_to_robot[i] = sqrt(pow((obstacles[i][0] - x_pos), 2) + pow((obstacles[i][1] - y_pos), 2));
93 | }
94 |
95 | int index = std::min_element(dist_to_robot.begin(), dist_to_robot.end()) - dist_to_robot.begin();
96 | obstacle.push_back(obstacles[index][0]);
97 | obstacle.push_back(obstacles[index][1]);
98 | obstacle.push_back(obstacles[index][2]);
99 | obstacle.push_back(dist_to_robot[index]);
100 |
101 | return obstacle; // {x_obst, y_obst, r_obst, distance to robot}
102 | }
103 |
104 | void escape_critetion(){
105 | auto i = sign_xs.size(), j = sign_ys.size();
106 | // checks if the vectors are not empty, track sign changes and set correct boolean for
107 | // whether an obstacle if successfully avoided or not.
108 | if(i > 0 && j > 0){
109 | if((sign_xs[i-1] != sign_xs[0]) && (sign_ys[j-1] != sign_ys[0])){
110 | avoidance_suceeded = true;
111 | }
112 | else{
113 | avoidance_suceeded = false;
114 | }
115 | }
116 | (std::signbit(xs) == false)? sign_xs.push_back(1) : sign_xs.push_back(-1);
117 | (std::signbit(ys) == false)? sign_ys.push_back(1) : sign_ys.push_back(-1);
118 | }
119 |
120 | void avoidance_controller(vector & obst){
121 | geometry_msgs::Twist vel;
122 | double k = 1.8; //controller parameter 1.8
123 | double Rc = R_i - 0.2; // radius of limit-cycle
124 | // define robot position with respect to the limit-cycle
125 | xs = x_pos - obst[0];
126 | ys = y_pos - obst[1];
127 |
128 | double xsdot = -ys + xs * (pow(Rc, 2) - pow(xs, 2) - pow(ys, 2));
129 | double ysdot = xs + ys * (pow(Rc, 2) - pow(xs, 2) - pow(ys, 2));
130 | double thetadot = atan2(ysdot, xsdot);
131 | double thetaerr = thetadot - theta;
132 | thetaerr = atan2(sin(thetaerr), cos(thetaerr));
133 |
134 | double xsdotdot = -ysdot - 2 * xs * (xs * xsdot + ys * ysdot) + xsdot * (pow(Rc, 2) - pow(xs, 2) - pow(ys, 2));
135 | double ysdotdot = xsdot - 2 * ys * (xs * xsdot + ys * ysdot) + ysdot * (pow(Rc, 2) - pow(xs, 2) - pow(ys, 2));
136 | double thetadotdot = (xsdot * ysdotdot - ysdot * xsdotdot) / (pow(xsdot, 2) + pow(ysdot, 2));
137 |
138 | vel.linear.x = 0.14;
139 | vel.angular.z = 1 * (thetadotdot + k * thetaerr);
140 | vel_pub.publish(vel);
141 |
142 | this->escape_critetion();
143 | }
144 | };
145 | // take in path to obstacles' txt file and return a vector of obstacles
146 | // each obstacle is defined by its x and y coordinates and radius.
147 | vector > obstacles(std::string path){
148 | double x, y, radius;
149 | vector > obsts;
150 | std::ifstream file(path);
151 | while(file >> x >> y >> radius){
152 | obsts.push_back({x, y, radius});
153 | }
154 |
155 | return obsts;
156 | }
157 |
158 |
159 | int main(int argc, char** argv){
160 | ros::init(argc, argv, "robot");
161 | ros::NodeHandle private_node_handle("~");
162 |
163 | Trajectory trajectory;
164 | Robot robot;
165 | ros::Rate loop_rate(10);
166 |
167 | std::string path = private_node_handle.param("path", "default");
168 |
169 | vector > obsts = obstacles(path);
170 |
171 | vector obst;
172 | while(ros::ok()){
173 | obst = robot.closest_obstacle(obsts);
174 |
175 | robot.R_i = obst[2] + robot.get_robot_radius() + 0.6;
176 | if(obst[3] <= robot.R_i){
177 | if (robot.avoidance_suceeded == false){
178 | robot.avoidance_controller(obst);
179 | }
180 | else{
181 | robot.tracking_controller(trajectory);
182 | }
183 | }
184 | else{
185 | robot.tracking_controller(trajectory);
186 | robot.avoidance_suceeded = false;
187 | robot.sign_xs.clear();
188 | robot.sign_ys.clear();
189 | }
190 |
191 | ros::spinOnce();
192 | loop_rate.sleep();
193 | }
194 |
195 | return 0;
196 | }
--------------------------------------------------------------------------------
/trajectory_tracking/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.0.2)
2 | project(trajectory_tracking)
3 |
4 | ## Compile as C++11, supported in ROS Kinetic and newer
5 | add_compile_options(-std=c++11)
6 |
7 | ## Find catkin macros and libraries
8 | ## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz)
9 | ## is used, also find other catkin packages
10 | find_package(catkin REQUIRED COMPONENTS
11 | roscpp
12 | rospy
13 | std_msgs
14 | geometry_msgs
15 | message_generation
16 | )
17 |
18 | ## System dependencies are found with CMake's conventions
19 | # find_package(Boost REQUIRED COMPONENTS system)
20 |
21 |
22 | ## Uncomment this if the package has a setup.py. This macro ensures
23 | ## modules and global scripts declared therein get installed
24 | ## See http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html
25 | # catkin_python_setup()
26 |
27 | ################################################
28 | ## Declare ROS messages, services and actions ##
29 | ################################################
30 |
31 | ## To declare and build messages, services or actions from within this
32 | ## package, follow these steps:
33 | ## * Let MSG_DEP_SET be the set of packages whose message types you use in
34 | ## your messages/services/actions (e.g. std_msgs, actionlib_msgs, ...).
35 | ## * In the file package.xml:
36 | ## * add a build_depend tag for "message_generation"
37 | ## * add a build_depend and a exec_depend tag for each package in MSG_DEP_SET
38 | ## * If MSG_DEP_SET isn't empty the following dependency has been pulled in
39 | ## but can be declared for certainty nonetheless:
40 | ## * add a exec_depend tag for "message_runtime"
41 | ## * In this file (CMakeLists.txt):
42 | ## * add "message_generation" and every package in MSG_DEP_SET to
43 | ## find_package(catkin REQUIRED COMPONENTS ...)
44 | ## * add "message_runtime" and every package in MSG_DEP_SET to
45 | ## catkin_package(CATKIN_DEPENDS ...)
46 | ## * uncomment the add_*_files sections below as needed
47 | ## and list every .msg/.srv/.action file to be processed
48 | ## * uncomment the generate_messages entry below
49 | ## * add every package in MSG_DEP_SET to generate_messages(DEPENDENCIES ...)
50 |
51 | ## Generate messages in the 'msg' folder
52 | add_message_files(
53 | FILES
54 | Traj.msg
55 | )
56 |
57 | ## Generate services in the 'srv' folder
58 | # add_service_files(
59 | # FILES
60 | # Service1.srv
61 | # Service2.srv
62 | # )
63 |
64 | ## Generate actions in the 'action' folder
65 | # add_action_files(
66 | # FILES
67 | # Action1.action
68 | # Action2.action
69 | # )
70 |
71 | ## Generate added messages and services with any dependencies listed here
72 | generate_messages(
73 | DEPENDENCIES
74 | std_msgs
75 | geometry_msgs
76 | )
77 |
78 | ################################################
79 | ## Declare ROS dynamic reconfigure parameters ##
80 | ################################################
81 |
82 | ## To declare and build dynamic reconfigure parameters within this
83 | ## package, follow these steps:
84 | ## * In the file package.xml:
85 | ## * add a build_depend and a exec_depend tag for "dynamic_reconfigure"
86 | ## * In this file (CMakeLists.txt):
87 | ## * add "dynamic_reconfigure" to
88 | ## find_package(catkin REQUIRED COMPONENTS ...)
89 | ## * uncomment the "generate_dynamic_reconfigure_options" section below
90 | ## and list every .cfg file to be processed
91 |
92 | ## Generate dynamic reconfigure parameters in the 'cfg' folder
93 | # generate_dynamic_reconfigure_options(
94 | # cfg/DynReconf1.cfg
95 | # cfg/DynReconf2.cfg
96 | # )
97 |
98 | ###################################
99 | ## catkin specific configuration ##
100 | ###################################
101 | ## The catkin_package macro generates cmake config files for your package
102 | ## Declare things to be passed to dependent projects
103 | ## INCLUDE_DIRS: uncomment this if your package contains header files
104 | ## LIBRARIES: libraries you create in this project that dependent projects also need
105 | ## CATKIN_DEPENDS: catkin_packages dependent projects also need
106 | ## DEPENDS: system dependencies of this project that dependent projects also need
107 |
108 | catkin_package(
109 | CATKIN_DEPENDS message_runtime
110 | )
111 |
112 | catkin_package(
113 | # INCLUDE_DIRS include
114 | # LIBRARIES trajectory_tracking
115 | # CATKIN_DEPENDS roscpp rospy std_msgs
116 | # DEPENDS system_lib
117 | )
118 |
119 | ###########
120 | ## Build ##
121 | ###########
122 |
123 | ## Specify additional locations of header files
124 | ## Your package locations should be listed before other locations
125 | include_directories(
126 | include
127 | ${catkin_INCLUDE_DIRS}
128 | )
129 |
130 | ## Declare a C++ library
131 | # add_library(${PROJECT_NAME}
132 | # src/${PROJECT_NAME}/trajectory_tracking.cpp
133 | # )
134 |
135 | ## Add cmake target dependencies of the library
136 | ## as an example, code may need to be generated before libraries
137 | ## either from message generation or dynamic reconfigure
138 | # add_dependencies(${PROJECT_NAME} ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
139 |
140 | ## Declare a C++ executable
141 | ## With catkin_make all packages are built within a single CMake context
142 | ## The recommended prefix ensures that target names across packages don't collide
143 | # add_executable(${PROJECT_NAME}_node src/trajectory_tracking_node.cpp)
144 |
145 | ## Rename C++ executable without prefix
146 | ## The above recommended prefix causes long target names, the following renames the
147 | ## target back to the shorter version for ease of user use
148 | ## e.g. "rosrun someones_pkg node" instead of "rosrun someones_pkg someones_pkg_node"
149 | # set_target_properties(${PROJECT_NAME}_node PROPERTIES OUTPUT_NAME node PREFIX "")
150 |
151 | ## Add cmake target dependencies of the executable
152 | ## same as for the library above
153 | # add_dependencies(${PROJECT_NAME}_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
154 |
155 | ## Specify libraries to link a library or executable target against
156 | # target_link_libraries(${PROJECT_NAME}_node
157 | # ${catkin_LIBRARIES}
158 | # )
159 |
160 | #############
161 | ## Install ##
162 | #############
163 |
164 | # all install targets should use catkin DESTINATION variables
165 | # See http://ros.org/doc/api/catkin/html/adv_user_guide/variables.html
166 |
167 | ## Mark executable scripts (Python etc.) for installation
168 | ## in contrast to setup.py, you can choose the destination
169 | # catkin_install_python(PROGRAMS
170 | # scripts/my_python_script
171 | # DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
172 | # )
173 |
174 | ## Mark executables for installation
175 | ## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_executables.html
176 | # install(TARGETS ${PROJECT_NAME}_node
177 | # RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
178 | # )
179 |
180 | ## Mark libraries for installation
181 | ## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_libraries.html
182 | # install(TARGETS ${PROJECT_NAME}
183 | # ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
184 | # LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
185 | # RUNTIME DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION}
186 | # )
187 |
188 | ## Mark cpp header files for installation
189 | # install(DIRECTORY include/${PROJECT_NAME}/
190 | # DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
191 | # FILES_MATCHING PATTERN "*.h"
192 | # PATTERN ".svn" EXCLUDE
193 | # )
194 |
195 | ## Mark other files for installation (e.g. launch and bag files, etc.)
196 | # install(FILES
197 | # # myfile1
198 | # # myfile2
199 | # DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
200 | # )
201 |
202 | #############
203 | ## Testing ##
204 | #############
205 |
206 | ## Add gtest based cpp test target and link libraries
207 | # catkin_add_gtest(${PROJECT_NAME}-test test/test_trajectory_tracking.cpp)
208 | # if(TARGET ${PROJECT_NAME}-test)
209 | # target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME})
210 | # endif()
211 |
212 | ## Add folders to be run by python nosetests
213 | # catkin_add_nosetests(test)
214 |
215 |
216 | add_executable(circular trajectories/circular.cpp)
217 | target_link_libraries(circular ${catkin_LIBRARIES})
218 | add_dependencies(circular trajectory_tracking_generate_messages_cpp)
219 |
220 | add_executable(linear trajectories/linear.cpp)
221 | target_link_libraries(linear ${catkin_LIBRARIES})
222 | add_dependencies(linear trajectory_tracking_generate_messages_cpp)
223 |
224 | add_executable(eight_shape trajectories/eight_shape.cpp)
225 | target_link_libraries(eight_shape ${catkin_LIBRARIES})
226 | add_dependencies(eight_shape trajectory_tracking_generate_messages_cpp)
227 |
228 | add_executable(tracking_controller src/tracking_controller.cpp)
229 | target_link_libraries(tracking_controller ${catkin_LIBRARIES})
230 | add_dependencies(tracking_controller trajectory_tracking_generate_messages_cpp)
231 |
232 |
233 |
234 |
235 |
--------------------------------------------------------------------------------