-
Notifications
You must be signed in to change notification settings - Fork 4.7k
/
Copy pathAngleLevelController.hpp
143 lines (112 loc) · 4 KB
/
AngleLevelController.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#pragma once
#include "interfaces/IUpdatable.hpp"
#include "interfaces/IGoal.hpp"
#include "interfaces/IBoardClock.hpp"
#include "interfaces/CommonStructs.hpp"
#include "interfaces/IAxisController.hpp"
#include "AngleRateController.hpp"
#include "Params.hpp"
#include "PidController.hpp"
#include "common/common_utils/Utils.hpp"
#include <string>
#include <exception>
namespace simple_flight
{
class AngleLevelController : public IAxisController
,
public IGoal //for internal rate controller
{
public:
AngleLevelController(Params* params, const IBoardClock* clock = nullptr)
: params_(params), clock_(clock)
{
}
virtual void initialize(unsigned int axis, const IGoal* goal, const IStateEstimator* state_estimator) override
{
if (axis > 2)
throw std::invalid_argument("AngleLevelController only supports axis 0-2 but it was " + std::to_string(axis));
axis_ = axis;
goal_ = goal;
state_estimator_ = state_estimator;
//initialize level PID
pid_.reset(new PidController<float>(clock_,
PidConfig<float>(params_->angle_level_pid.p[axis], params_->angle_level_pid.i[axis], params_->angle_level_pid.d[axis])));
//initialize rate controller
rate_controller_.reset(new AngleRateController(params_, clock_));
rate_controller_->initialize(axis, this, state_estimator_);
//we will be setting goal for rate controller so we need these two things
rate_mode_ = GoalMode::getUnknown();
rate_mode_[axis] = GoalModeType::AngleRate;
}
virtual void reset() override
{
IAxisController::reset();
pid_->reset();
rate_controller_->reset();
rate_goal_ = Axis4r();
output_ = TReal();
}
virtual void update() override
{
IAxisController::update();
//get response of level PID
const auto& level_goal = goal_->getGoalValue();
TReal goal_angle = level_goal[axis_];
TReal measured_angle = state_estimator_->getAngles()[axis_];
adjustToMinDistanceAngles(measured_angle, goal_angle);
pid_->setGoal(goal_angle);
pid_->setMeasured(measured_angle);
pid_->update();
//use this to drive rate controller
rate_goal_[axis_] = pid_->getOutput() * params_->angle_rate_pid.max_limit[axis_];
rate_controller_->update();
//rate controller's output is final output
output_ = rate_controller_->getOutput();
}
virtual TReal getOutput() override
{
return output_;
}
/******************** IGoal ********************/
virtual const Axis4r& getGoalValue() const override
{
return rate_goal_;
}
virtual const GoalMode& getGoalMode() const override
{
return rate_mode_;
}
private:
static void adjustToMinDistanceAngles(TReal& angle1, TReal& angle2)
{
static constexpr TReal TwoPi = 2 * M_PIf;
//first make sure both angles are restricted from -360 to +360
angle1 = static_cast<TReal>(std::fmod(angle1, TwoPi));
angle2 = static_cast<TReal>(std::fmod(angle2, TwoPi));
//now make sure both angles are restricted from 0 to 360
if (angle1 < 0)
angle1 = TwoPi + angle1;
if (angle2 < 0)
angle2 = TwoPi + angle2;
//measure distance between two angles
auto dist = angle1 - angle2;
//if its > 180 then invert first angle
if (dist > M_PIf)
angle1 = angle1 - TwoPi;
//if two much on other side then invert second angle
else if (dist < -M_PIf)
angle2 = angle2 - TwoPi;
}
private:
unsigned int axis_;
const IGoal* goal_;
const IStateEstimator* state_estimator_;
GoalMode rate_mode_;
Axis4r rate_goal_;
TReal output_;
Params* params_;
const IBoardClock* clock_;
std::unique_ptr<PidController<float>> pid_;
std::unique_ptr<AngleRateController> rate_controller_;
};
} //namespace