Skip to content

Commit 20502b9

Browse files
committed
Fix NAN issue, fix pointers
1 parent c031013 commit 20502b9

9 files changed

Lines changed: 138 additions & 58 deletions

File tree

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# CHANGELOG
2+
3+
## v0.1 | Initial dev
4+
5+
### v0.1.0 - Unknown
6+
- [x] Control laterally (no rev, no brake)
7+
8+
### v0.1.1 - WIP
9+
- [x] Fix control issue. Does not accelerate in 3D.
10+
- [ ] Add braking
11+
- [ ] Add reversing
12+
- [ ] Add turn output
13+
14+
### v0.1.2 - WIP
15+
- [ ] Add MPC

config/control_node_cfg.csv

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ wheelbase,2.5
22
a_max,3.0
33
delta_max,0.5
44
throttle_gain,1.0
5+
brake_gain,1.0

include/ap1/control/ackermann_controller.hpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,16 @@ class AckermannController
2121
double L; // wheelbase in meters
2222
double a_max; // max longitudinal accleration (ms^-2)
2323
double delta_max; // max steering angle
24-
double throttle_gain; // scales accel to throttle [-1, 1]
24+
double throttle_gain; // scales accel to throttle [0, 1]
25+
double brake_gain; // scales -accel to brake [0, 1]
2526
};
2627

2728
explicit AckermannController(const Config &cfg);
2829

2930
struct Command
3031
{
31-
double throttle; // normalized [-1, 1]
32+
double throttle; // normalized [0, 1]
33+
double brake; // normalized [0, 1]
3234
double steering; // in rads
3335
};
3436

include/ap1/control/control_node.hpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,10 @@ class ControlNode : public rclcpp::Node
3434
AckermannController ackermann_controller_;
3535

3636
// Memory
37-
ap1_msgs::msg::SpeedProfileStamped speed_profile_;
38-
ap1_msgs::msg::TargetPathStamped target_path_;
39-
ap1_msgs::msg::FloatStamped vehicle_speed_;
40-
ap1_msgs::msg::FloatStamped vehicle_turn_angle;
37+
ap1_msgs::msg::SpeedProfileStamped::SharedPtr speed_profile_;
38+
ap1_msgs::msg::TargetPathStamped::SharedPtr target_path_;
39+
ap1_msgs::msg::FloatStamped::SharedPtr vehicle_speed_;
40+
ap1_msgs::msg::FloatStamped::SharedPtr vehicle_turn_angle;
4141

4242
// Subs
4343
rclcpp::Subscription<ap1_msgs::msg::TargetPathStamped>::SharedPtr target_path_sub_;
@@ -50,10 +50,10 @@ class ControlNode : public rclcpp::Node
5050
rclcpp::Publisher<ap1_msgs::msg::FloatStamped>::SharedPtr motor_power_pub_; // between -1 and 1? probably
5151

5252
// Methods
53-
void on_speed_profile(const ap1_msgs::msg::SpeedProfileStamped speed_profile);
54-
void on_path(const ap1_msgs::msg::TargetPathStamped target_path);
55-
void on_speed(const ap1_msgs::msg::FloatStamped speed);
56-
void on_turn_angle(const ap1_msgs::msg::FloatStamped turn_angle);
53+
void on_speed_profile(const ap1_msgs::msg::SpeedProfileStamped::SharedPtr speed_profile);
54+
void on_path(const ap1_msgs::msg::TargetPathStamped::SharedPtr target_path);
55+
void on_speed(const ap1_msgs::msg::FloatStamped::SharedPtr speed);
56+
void on_turn_angle(const ap1_msgs::msg::FloatStamped::SharedPtr turn_angle);
5757
void control_loop_callback();
5858

5959
public:

include/ap1/control/icontroller.hpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,18 @@
77
#ifndef AP1_CONTROLLER_HPP
88
#define AP1_CONTROLLER_HPP
99

10-
#include <vector>
11-
1210
#include "vectors.hpp"
13-
#include "geometry_msgs/msg/point.hpp"
11+
#include "ap1_msgs/msg/target_path_stamped.hpp"
12+
13+
using ap1_msgs::msg::TargetPathStamped;
1414

1515
namespace ap1::control
1616
{
1717
class IController
1818
{
1919
public:
2020
~IController() = default;
21-
virtual vec3f compute_acceleration(const vec3f& vel, const vec2f& target_pos, const float target_speed) = 0;
21+
virtual vec3f compute_acceleration(const vec3f& vel, const TargetPathStamped::ConstSharedPtr path, const float target_speed) = 0;
2222
};
2323
} // namespace ap1::control
2424

include/ap1/control/pd_controller.hpp

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,51 @@
99

1010
#include <cmath>
1111
#include <iostream>
12-
#include <stdexcept>
13-
#include <vector>
14-
15-
#include "geometry_msgs/msg/point.hpp"
12+
#include <sys/types.h>
1613

1714
#include "ap1/control/icontroller.hpp"
15+
#include "ap1_msgs/msg/target_path_stamped.hpp"
1816
#include "vectors.hpp"
1917

2018
#define EPSILON 1e-6
19+
#define MAX_WAYPOINT_SEARCH 5 // how far into the planned path to search for a path far from the current position
20+
21+
using ap1_msgs::msg::TargetPathStamped;
22+
23+
/**
24+
* @brief Figure's out the first, next up target position.
25+
* @return vec2f Returns a vec containing the first waypoint that is further than EPSILON from the origin.
26+
*/
27+
inline vec2f get_target_position(const TargetPathStamped::ConstSharedPtr path) {
28+
uint wpt_idx = 0;
29+
30+
while (wpt_idx < MAX_WAYPOINT_SEARCH) {
31+
const vec2f next{(float) path->path[wpt_idx].x, (float) path->path[wpt_idx].y};
32+
const vec2f origin{0, 0};
33+
34+
// if the waypoint and the origin are far enough apart
35+
if (distance(next, origin) > EPSILON) {
36+
// return it
37+
return next;
38+
}
39+
40+
// otherwise increment and try again
41+
wpt_idx++;
42+
}
43+
44+
// OPERATION FAILED!
45+
// at this point in development I'm just trying to get it working
46+
// this leaves me with 2 very bad options
47+
// option 1: I return 0 here and let jesus take the wheel
48+
// option 2: I can throw a runtime error and just take all of control down
49+
// both options are kind of shit I can't lie
50+
// really this should propagate upwards to some kind of system that takes care of it, decelerating the car or keeping it stopped depending
51+
// on if we're starting up or moving or whatever
52+
// but considering we don't have that I'm just going to take option 1.
53+
return {0, 0};
54+
// option 2 for fun:
55+
// throw std::runtime_error("MAX_WAYPOINT_SEARCH exceeded! Couldn't find a waypoint far enough.");
56+
}
2157

2258
namespace ap1::control
2359
{
@@ -33,10 +69,19 @@ class PDController : public IController
3369

3470
// Computes acceleration based on current velocity, target position, and target speed. Returns
3571
// acceleration vector.
36-
virtual vec3f compute_acceleration(const vec3f& vel,
37-
const vec2f& target_pos,
38-
const float target_speed) override
39-
{
72+
virtual vec3f compute_acceleration(
73+
const vec3f& vel,
74+
const TargetPathStamped::ConstSharedPtr path,
75+
const float target_speed
76+
) override {
77+
if (!path) {
78+
printf("Unc is null twin");
79+
return {0, 0, 0};
80+
}
81+
82+
const auto target_pos = get_target_position(path);
83+
84+
// Get the very next target position
4085
// Convert target position to vec3f and calculate distance
4186
const float distance = target_pos.length();
4287

include/vectors.hpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,17 @@
88
#define AP1_VECTOR_HPP
99

1010
#include <format>
11-
#include <iostream>
1211
#include <math.h>
1312
#include <stdexcept>
1413
#include <stdlib.h>
1514

15+
template <typename T>
16+
float distance(const T &v1, const T &v2) {
17+
return std::sqrt(
18+
std::pow(v1.x - v2.x, 2) + std::pow(v1.y - v2.y, 2)
19+
);
20+
}
21+
1622
// 2D vector class
1723
class vec2f
1824
{

src/ackermann_controller.cpp

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -54,33 +54,35 @@ std::unordered_map<std::string, double> load_csv_config(const std::string& path)
5454

5555
AckermannController::AckermannController(const Config& cfg) : cfg_(cfg)
5656
{
57-
printf("Created Ackermann Controller with config: a_max=%f, delta_max=%f, L=%f, "
58-
"throttle_gain=%f\n",
59-
cfg_.a_max, cfg_.delta_max, cfg_.L, cfg_.throttle_gain);
57+
printf("Created Ackermann Controller with config: a_max=%f, delta_max=%f, L=%f, throttle_gain=%f, brake_gain=%f\n",
58+
cfg_.a_max, cfg_.delta_max, cfg_.L, cfg_.throttle_gain, cfg_.brake_gain);
6059
}
6160

62-
// we should move everything over to double but I already wrote all the message types in float
63-
// and I'm too lazy to switch so we'll do it later
6461
AckermannController::Command AckermannController::compute_command(const vec3f& acc, const vec3f& vel)
6562
{
6663
Command cmd{};
6764

68-
double a_long = std::clamp(
69-
(double)acc.x,
70-
-cfg_.a_max,
71-
cfg_.a_max
72-
);
73-
65+
double a_long = std::clamp((double)acc.x, -cfg_.a_max, cfg_.a_max);
7466
double a_lat = acc.y;
7567

7668
double speed = vel.length();
7769
double kappa = (speed > EPSILON) ? a_lat / (speed * speed) : 0.0;
7870
double delta = std::atan(cfg_.L * kappa);
7971
delta = std::clamp(delta, -cfg_.delta_max, cfg_.delta_max);
8072

81-
double throttle = std::clamp(a_long / cfg_.a_max * cfg_.throttle_gain, -1.0, 1.0);
73+
double throttle = 0.0;
74+
double brake = 0.0;
75+
76+
if (a_long >= 0.0) {
77+
throttle = std::clamp(a_long / cfg_.a_max * cfg_.throttle_gain, 0.0, 1.0);
78+
brake = 0.0;
79+
} else {
80+
throttle = 0.0;
81+
brake = std::clamp(-a_long / cfg_.a_max * cfg_.brake_gain, 0.0, 1.0);
82+
}
8283

8384
cmd.throttle = throttle;
85+
cmd.brake = brake;
8486
cmd.steering = delta;
8587

8688
return cmd;

src/control_node.cpp

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
* Author(s): Aly Ashour
44
*/
55

6-
#include <format>
7-
#include <iostream>
6+
#include <cmath>
7+
#include <stdexcept>
88
#include <string>
99

1010
#include "rclcpp/rclcpp.hpp"
@@ -21,69 +21,78 @@
2121
using namespace ap1_msgs::msg;
2222
using namespace ap1::control;
2323

24-
void ControlNode::on_speed_profile(const SpeedProfileStamped speed_profile)
24+
void ControlNode::on_speed_profile(const SpeedProfileStamped::SharedPtr speed_profile)
2525
{
2626
speed_profile_ = speed_profile;
2727
}
2828

29-
void ControlNode::on_path(const TargetPathStamped target_path)
29+
void ControlNode::on_path(const TargetPathStamped::SharedPtr target_path)
3030
{
3131
target_path_ = target_path;
3232
}
3333

34-
void ControlNode::on_speed(const FloatStamped speed)
34+
void ControlNode::on_speed(const FloatStamped::SharedPtr speed)
3535
{
36+
RCLCPP_INFO_THROTTLE(this->get_logger(), *this->get_clock(), 1000, "Received speed from actuation: %.2f", speed->value);
3637
vehicle_speed_ = speed;
3738
}
3839

39-
void ControlNode::on_turn_angle(const FloatStamped turn_angle)
40+
void ControlNode::on_turn_angle(const FloatStamped::SharedPtr turn_angle)
4041
{
4142
vehicle_turn_angle = turn_angle;
4243
}
4344

45+
// TODO: refactor. This is just a mess of spaghetti code vro.
4446
void ControlNode::control_loop_callback()
4547
{
48+
// check that we have the car's velocity yet
49+
if (!this->vehicle_speed_) { // TODO: really we should safety stop all of ap1 on a NAN
50+
RCLCPP_WARN_THROTTLE(this->get_logger(), *this->get_clock(), 2000, "Velocity is null or nan fam. Skipping update.");
51+
return;
52+
}
53+
54+
// TEMP: DEBUG
55+
if (std::isnan(this->vehicle_speed_->value)) {
56+
throw std::runtime_error("Vehicle speed is nan. Crashing");
57+
}
58+
4659
// the car's current velocity. we only support moving forward atp
47-
const vec3f velocity(this->vehicle_speed_.value, 0, 0); // +x is always forward on the car
60+
const vec3f velocity(this->vehicle_speed_->value, 0, 0); // +x is always forward on the car
4861

4962
const bool PATH_IS_STALE = false, SPEED_PROFILE_IS_STALE = false; // TEMP
5063

5164
// if path has no waypoints OR path is too old
52-
if (target_path_.path.size() < 1 || PATH_IS_STALE)
53-
return; // don't update
54-
// todo: ideally this should actually still
55-
// send through the speed control
56-
// but we'll implement that later
65+
if (!target_path_ || target_path_->path.size() < 1 || PATH_IS_STALE) {
66+
RCLCPP_WARN(this->get_logger(), "Target path is cooked fam. Null, not enough waypoints, or old, skipping."); // TODO: should be throttled
67+
return;
68+
}
5769

5870
// if speed profile has no waypoints or speed profile is too old
59-
if (speed_profile_.speeds.size() < 1 || SPEED_PROFILE_IS_STALE)
71+
if (!speed_profile_ || speed_profile_->speeds.size() < 1 || SPEED_PROFILE_IS_STALE) {
72+
RCLCPP_WARN(this->get_logger(), "Speed profile is cooked fam. Null, not enough waypoints, or old, skipping.");
6073
return;
74+
}
6175

6276
// ask the controller to calculate the acceleration needed
63-
// for now we'll only consider the very next waypoint & speed value
64-
auto next_waypoint = target_path_.path.at(0);
65-
const vec3f acc = controller_->compute_acceleration(
66-
velocity, vec2f(next_waypoint.x, next_waypoint.y), speed_profile_.speeds.at(0));
77+
const vec3f acc = controller_->compute_acceleration(velocity, target_path_, speed_profile_->speeds.at(0));
6778

68-
// log
69-
// RCLCPP_INFO(this->get_logger(), "ACC: %.2f, %.2f, %.2f", acc.x, acc.y, acc.z);
79+
// ALY'S FAVOURITE DEBUG CMD 1
80+
RCLCPP_INFO_THROTTLE(this->get_logger(), *this->get_clock(), 700, "ACC: %.2f, %.2f, %.2f", acc.x, acc.y, acc.z);
7081

7182
// compute acc and throttle using ackermann controller
7283
AckermannController::Command cmd = ackermann_controller_.compute_command(acc, velocity);
7384

74-
// DEBUG
75-
// RCLCPP_INFO(this->get_logger(), "CMD: {throttle: %.2f, steering: %.2f}", cmd.throttle, cmd.steering);
85+
// ALY'S FAVOURITE DEBUG CMD 2
86+
RCLCPP_INFO_THROTTLE(this->get_logger(), *this->get_clock(), 700, "CMD: {throttle: %.2f, steering: %.2f}", cmd.throttle, cmd.steering);
7687

7788
// pack the turn angle into a message
7889
FloatStamped turn_msg;
7990
turn_msg.header.stamp = this->now();
80-
turn_msg.header.frame_id = "base_link";
8191
turn_msg.value = cmd.steering; // rads
8292

8393
// pack the power into a message
8494
FloatStamped pwr_msg;
8595
pwr_msg.header.stamp = this->now();
86-
pwr_msg.header.frame_id = "base_link";
8796
pwr_msg.value = cmd.throttle; // [-1, 1]
8897

8998
// send both messages out

0 commit comments

Comments
 (0)