Emakefun Encoder Motor Arduino Lib 1.1.1
Loading...
Searching...
No Matches
encoder_motor.cpp
Go to the documentation of this file.
1/**
2 * @file encoder_motor.cpp
3 */
4
5#include "encoder_motor.h"
6
7#include <Arduino.h>
8
9#include <thread>
10#include <utility>
11
12#include "driver/gpio.h"
13#include "driver/ledc.h"
14
15namespace em {
16
17namespace {
18constexpr float kDefaultPositionP = 20.0;
19constexpr float kDefaultPositionI = 0.1;
20constexpr float kDefaultPositionD = 5.0;
21constexpr float kDefaultSpeedP = 3.0;
22constexpr float kDefaultSpeedI = 1.0;
23constexpr float kDefaultSpeedD = 1.0;
24constexpr int32_t kDeadRpmZone = 10;
25} // namespace
26
27#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
28EncoderMotor::EncoderMotor(const uint8_t positive_pin,
29 const uint8_t negative_pin,
30 const uint8_t a_pin,
31 const uint8_t b_pin,
32 const uint32_t ppr,
33 const uint32_t reduction_ration,
34 const PhaseRelation phase_relation)
35 : pin_a_(a_pin),
36 pin_b_(b_pin),
37 motor_driver_(positive_pin, negative_pin),
38 total_ppr_(ppr * reduction_ration),
39 b_level_at_a_falling_edge_(phase_relation == PhaseRelation::kAPhaseLeads ? HIGH : LOW),
40 pulse_count_(0) {
41 rpm_pid_.p = kDefaultSpeedP;
42 rpm_pid_.i = kDefaultSpeedI;
43 rpm_pid_.d = kDefaultSpeedD;
44 rpm_pid_.max_integral = ceil(Motor::kMaxPwmDuty / rpm_pid_.i);
45}
46#endif
47
48EncoderMotor::EncoderMotor(const uint8_t positive_pin,
49 const uint8_t positive_pin_ledc_channel,
50 const uint8_t negative_pin,
51 const uint8_t negative_pin_ledc_channel,
52 const uint8_t a_pin,
53 const uint8_t b_pin,
54 const uint32_t ppr,
55 const uint32_t reduction_ration,
56 const PhaseRelation phase_relation)
57 : pin_a_(a_pin),
58 pin_b_(b_pin),
59 motor_driver_(positive_pin, positive_pin_ledc_channel, negative_pin, negative_pin_ledc_channel),
60 total_ppr_(ppr * reduction_ration),
61 b_level_at_a_falling_edge_(phase_relation == PhaseRelation::kAPhaseLeads ? HIGH : LOW),
62 pulse_count_(0) {
63 rpm_pid_.p = kDefaultSpeedP;
64 rpm_pid_.i = kDefaultSpeedI;
65 rpm_pid_.d = kDefaultSpeedD;
66 rpm_pid_.max_integral = ceil(Motor::kMaxPwmDuty / rpm_pid_.i);
67}
68
70 std::lock_guard<std::mutex> lock(mutex_);
71 if (update_rpm_thread_ != nullptr) {
72 return;
73 }
74
75 motor_driver_.Init();
76
77 pinMode(pin_a_, INPUT_PULLUP);
78 pinMode(pin_b_, INPUT_PULLUP);
79
80 attachInterruptArg(pin_a_, EncoderMotor::OnPinAFalling, this, FALLING);
81 update_rpm_thread_ = new std::thread(&EncoderMotor::UpdateRpm, this);
82}
83
84void EncoderMotor::SetSpeedPid(const float p, const float i, const float d) {
85 std::lock_guard<std::mutex> lock(mutex_);
86 rpm_pid_.p = p;
87 rpm_pid_.i = i;
88 rpm_pid_.d = d;
89
90 if (i > 0) {
91 rpm_pid_.max_integral = ceil(Motor::kMaxPwmDuty / rpm_pid_.i);
92 } else {
93 rpm_pid_.max_integral = 0;
94 }
95}
96
97void EncoderMotor::GetSpeedPid(float* const p, float* const i, float* const d) const {
98 std::lock_guard<std::mutex> lock(mutex_);
99 if (p != nullptr) {
100 *p = rpm_pid_.p;
101 }
102 if (i != nullptr) {
103 *i = rpm_pid_.i;
104 }
105 if (d != nullptr) {
106 *d = rpm_pid_.d;
107 }
108}
109
110EncoderMotor::~EncoderMotor() {
111 std::unique_lock<std::mutex> lock(mutex_);
112 DeleteThread(driving_thread_);
113 DeleteThread(update_rpm_thread_);
114}
115
116void EncoderMotor::RunPwmDuty(const int16_t duty) {
117 std::unique_lock<std::mutex> lock(mutex_);
118 DeleteThread(driving_thread_);
119 target_speed_rpm_ = 0;
120
121 if (motor_driver_.PwmDuty() == duty) {
122 return;
123 }
124
125 motor_driver_.RunPwmDuty(duty);
126}
127
128void EncoderMotor::RunSpeed(const int16_t speed_rpm) {
129 std::lock_guard<std::mutex> lock(mutex_);
130 if (driving_thread_ == nullptr) {
131 rpm_pid_.integral = 0;
132 driving_thread_ = new std::thread(&EncoderMotor::Driving, this);
133 }
134
135 if (speed_rpm == target_speed_rpm_) {
136 return;
137 }
138
139 target_speed_rpm_ = speed_rpm;
140 drive_ = true;
141 condition_.notify_all();
142}
143
145 std::unique_lock<std::mutex> lock(mutex_);
146 DeleteThread(driving_thread_);
147 motor_driver_.Stop();
148 target_speed_rpm_ = 0;
149 rpm_pid_.integral = 0;
150}
151
153 return pulse_count_;
154}
155
157 pulse_count_ = 0;
158}
159
160int32_t EncoderMotor::SpeedRpm() const {
161 std::unique_lock<std::mutex> lock(mutex_);
162 return speed_rpm_;
163}
164
165int16_t EncoderMotor::PwmDuty() const {
166 std::lock_guard<std::mutex> lock(mutex_);
167 return motor_driver_.PwmDuty();
168}
169
170int32_t EncoderMotor::TargetRpm() const {
171 std::lock_guard<std::mutex> lock(mutex_);
172 return target_speed_rpm_;
173}
174
175void EncoderMotor::OnPinAFalling(void* self) {
176 reinterpret_cast<EncoderMotor*>(self)->OnPinAFalling();
177}
178
179void EncoderMotor::OnPinAFalling() {
180 if (gpio_get_level(static_cast<gpio_num_t>(pin_b_)) == b_level_at_a_falling_edge_) {
181 ++pulse_count_;
182 } else {
183 --pulse_count_;
184 }
185}
186
187void EncoderMotor::UpdateRpm() {
188 std::unique_lock<std::mutex> lock(mutex_);
189 last_update_speed_time_ = std::chrono::system_clock::now();
190 while (!condition_.wait_until(
191 lock, last_update_speed_time_ + std::chrono::milliseconds(50), [this]() { return update_rpm_thread_ == nullptr; })) {
192 const auto now = std::chrono::system_clock::now();
193 const double duration = std::chrono::duration_cast<std::chrono::milliseconds>(now - last_update_speed_time_).count();
194 const double pulse_count = pulse_count_;
195 speed_rpm_ = (pulse_count - previous_pulse_count_) * 60000.0 / duration / total_ppr_;
196 previous_pulse_count_ = pulse_count;
197 last_update_speed_time_ = now;
198 if (driving_thread_ != nullptr) {
199 drive_ = true;
200 condition_.notify_all();
201 }
202 }
203}
204
205void EncoderMotor::Driving() {
206 std::unique_lock<std::mutex> lock(mutex_);
207
208 while (driving_thread_ != nullptr) {
209 condition_.wait(lock, [this]() { return driving_thread_ == nullptr || drive_; });
210
211 if (driving_thread_ == nullptr) {
212 return;
213 }
214
215 if (target_speed_rpm_ < kDeadRpmZone && target_speed_rpm_ > -kDeadRpmZone) {
216 motor_driver_.RunPwmDuty(0);
217 } else {
218 const float speed_error = target_speed_rpm_ - speed_rpm_;
219 rpm_pid_.integral = constrain(rpm_pid_.integral + speed_error, -rpm_pid_.max_integral, rpm_pid_.max_integral);
220 const int16_t duty = round(rpm_pid_.p * speed_error + rpm_pid_.i * rpm_pid_.integral);
221 motor_driver_.RunPwmDuty(duty);
222 }
223 drive_ = false;
224 }
225}
226
227void EncoderMotor::DeleteThread(std::thread*& thread) {
228 if (thread != nullptr) {
229#if __cplusplus >= 201402L
230 const auto temp = std::exchange(thread, nullptr);
231#else
232 const auto temp = thread;
233 thread = nullptr;
234#endif
235 condition_.notify_all();
236 mutex_.unlock();
237 temp->join();
238 delete temp;
239 mutex_.lock();
240 }
241}
242
243} // namespace em
Encoder Motor Class.
void Init()
Initialize.
void Stop()
Stop motor.
int16_t PwmDuty() const
Get the PWM pwm_duty cycle of the motor driver.
void RunSpeed(const int16_t speed_rpm)
Run motor at speed setpoint.
void ResetPulseCount()
Reset the encoder pulse count to 0.
PhaseRelation
Used to clarify the phase relationship between phase A and phase B of the encoder when the motor is r...
@ kAPhaseLeads
Represents the situation where phase A leads phase B when the motor is rotating forward.
void RunPwmDuty(const int16_t pwm_duty)
Set motor PWM directly.
int64_t EncoderPulseCount() const
Get encoder pulse count. The count value is incremented by one during forward rotation and decremente...
int32_t TargetRpm() const
Get the target speed of the motor in RPM.
void SetSpeedPid(const float p, const float i, const float d)
Set the parameters of the speed PID controller with the given Proportional (P), Integral (I),...
void GetSpeedPid(float *const p, float *const i, float *const d) const
Get the Proportional (P), Integral (I), and Derivative (D) parameter values of the speed PID controll...
EncoderMotor(const uint8_t positive_pin, const uint8_t negative_pin, const uint8_t a_pin, const uint8_t b_pin, const uint32_t ppr, const uint32_t reduction_ration, const PhaseRelation phase_relation)
Constructor for creating an EncoderMotor object.
int32_t SpeedRpm() const
Get the current speed of the motor.
static constexpr int16_t kMaxPwmDuty
The maximum PWM duty cycle value calculated based on the PWM resolution.
Definition motor.h:59