Skip to content

Commit 855d79b

Browse files
authored
Different encoder sensor algorithm (#500)
* Fix PWMSensor reorder warning * Change both ring buffer and encoder sensor implementation a bit * Remove execution period check * Add direction and make position, speed and acceleration output parameters on the constructor * Small fixes to properly calculate speed * Remove encoder registration check from get_counter method
1 parent cd8ef79 commit 855d79b

5 files changed

Lines changed: 145 additions & 181 deletions

File tree

Inc/C++Utilities/RingBuffer.hpp

Lines changed: 59 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -2,79 +2,64 @@
22

33
#include "CppImports.hpp"
44

5+
template <typename T, size_t N>
6+
class RingBuffer {
7+
std::array<T, N> buffer{};
58

6-
/**
7-
* @brief a Buffer class that acts as a RingBuffer using FIFO behaviour
8-
*/
9-
template <typename item_type, size_t N>
10-
class RingBuffer{
11-
public:
12-
RingBuffer(): msize(0), mcapacity(N), mhead_index(0), mlast_index(N){}
13-
14-
15-
/**
16-
* @brief introduces a new item on the Ring Buffer unless it is full. Returns false when the Ring Buffer is full.
17-
*/
18-
bool push(item_type item){
19-
if(msize == mcapacity){
20-
return false;
21-
}
22-
23-
msize++;
24-
mlast_index++;
25-
if(mlast_index >= mcapacity){
26-
mlast_index = 0;
27-
}
28-
29-
buffer[mlast_index] = item;
30-
return true;
31-
}
32-
33-
34-
bool is_empty(){return msize == 0;} /**< @brief returns true when RingBuffer is empty, false otherwise.*/
35-
bool is_full(){return msize == mcapacity;} /**< @brief returns true when RingBuffer is full, false otherwise.*/
36-
uint32_t size(){return msize;} /**< @brief returns the occupied size of the RingBuffer.*/
37-
38-
39-
40-
41-
/**
42-
* @brief checks the next variable to read without removing it from the buffer. Do not use on empty buffer.
43-
*/
44-
item_type peek(){
45-
return buffer[mhead_index];
46-
}
47-
48-
/**
49-
* @brief checks the next variable to read and removes it from the buffer. Do not use on empty buffer.
50-
*/
51-
item_type pop(){
52-
uint32_t return_index = mhead_index;
53-
mhead_index++;
54-
msize--;
55-
if(mhead_index >= mcapacity){
56-
mhead_index = 0;
57-
}
58-
return buffer[return_index];
59-
}
60-
61-
/**
62-
* @brief pop later and push new. Don't care size of buffer, needs to be initialize before
63-
*/
64-
void push_pop(item_type item){
65-
mhead_index = (mhead_index+1)%mcapacity;
66-
mlast_index = (mlast_index+1)%mcapacity;
67-
buffer[mhead_index] = item;
68-
}
69-
70-
item_type latest()const{
71-
return buffer[mlast_index];
72-
}
73-
74-
private:
75-
std::array<item_type, N> buffer;
76-
uint32_t msize;
77-
uint32_t mcapacity;
78-
uint32_t mhead_index;
79-
uint32_t mlast_index;
9+
size_t stored_items{0};
10+
11+
size_t front{0};
12+
size_t back{0};
13+
14+
size_t move_forward(size_t origin, size_t amount) {
15+
return (origin + amount) % N;
16+
}
17+
18+
size_t move_backward(size_t origin, size_t amount) {
19+
// N * ((amount / N) + 1) makes it so that the operation doesn't
20+
// overflow (size_t is unsigned) and with the help of modular
21+
// arithmetic, this won't change the result
22+
return ((origin + (N * ((amount / N) + 1))) - amount) % N;
23+
}
24+
25+
public:
26+
RingBuffer() {}
27+
28+
bool push(T item) {
29+
if (is_full()) return false;
30+
31+
buffer[front] = item;
32+
front = move_forward(front, 1);
33+
++stored_items;
34+
35+
return true;
36+
}
37+
38+
bool pop() {
39+
if (is_empty()) return false;
40+
41+
back = move_forward(back, 1);
42+
--stored_items;
43+
44+
return true;
45+
}
46+
47+
bool push_pop(T item) {
48+
if (is_empty()) return false;
49+
50+
buffer[front] = item;
51+
front = move_forward(front, 1);
52+
back = move_forward(back, 1);
53+
54+
return true;
55+
}
56+
57+
T &operator[](size_t index) {
58+
return buffer[move_backward(front, index + 1)];
59+
}
60+
61+
constexpr size_t capacity() { return N; }
62+
size_t size() { return stored_items; }
63+
bool is_full() { return size() >= capacity(); }
64+
bool is_empty() { return size() == 0; }
8065
};

Inc/ST-LIB_LOW/Sensors/EncoderSensor/EncoderSensor.hpp

Lines changed: 74 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -7,40 +7,80 @@
77
*/
88

99
#pragma once
10-
#include "HALAL/HALAL.hpp"
1110
#include "ErrorHandler/ErrorHandler.hpp"
11+
#include "HALAL/HALAL.hpp"
12+
13+
template <size_t SAMPLES>
14+
class EncoderSensor {
15+
public:
16+
enum Direction : uint8_t { FORWARD = 0, BACKWARDS = 1 };
17+
18+
private:
19+
constexpr static int64_t START_COUNTER{UINT32_MAX / 2};
20+
21+
const double counter_distance_m;
22+
const double sample_time_s;
23+
24+
uint8_t encoder_id;
25+
26+
// We want to get the last buffer element and the midpoint, if the number of
27+
// elements is odd, these points and the present won't be evenly spaced
28+
// across time. The SAMPLES computation rounds it down to make it even
29+
RingBuffer<int64_t, (SAMPLES / 2) * 2> past_delta_counters{};
30+
31+
Direction *direction;
32+
double *position;
33+
double *speed;
34+
double *acceleration;
35+
36+
public:
37+
EncoderSensor(Pin &pin1, Pin &pin2, const double counter_distance_m,
38+
const double sample_time_s, Direction *direction,
39+
double *position, double *speed, double *acceleration)
40+
: counter_distance_m(counter_distance_m),
41+
sample_time_s(sample_time_s),
42+
encoder_id(Encoder::inscribe(pin1, pin2)),
43+
direction(direction),
44+
position(position),
45+
speed(speed),
46+
acceleration(acceleration) {
47+
for (size_t i{0}; i < SAMPLES; ++i) past_delta_counters.push(0);
48+
}
49+
50+
void turn_on() { Encoder::turn_on(encoder_id); }
51+
void turn_off() { Encoder::turn_off(encoder_id); }
52+
53+
void reset() {
54+
Encoder::reset(encoder_id);
55+
for (size_t i{0}; i < SAMPLES; ++i) past_delta_counters.push_pop(0);
56+
}
57+
58+
// must be called on equally spaced time periods
59+
void read() {
60+
uint32_t counter{Encoder::get_counter(encoder_id)};
61+
62+
int64_t delta_counter{(int64_t)counter - START_COUNTER};
63+
const int64_t &previous_delta_counter{
64+
past_delta_counters[past_delta_counters.size() / 2 - 1]};
65+
const int64_t &previous_previous_delta_counter{
66+
past_delta_counters[past_delta_counters.size() - 1]};
67+
68+
*position = delta_counter * counter_distance_m;
69+
70+
// https://en.wikipedia.org/wiki/Finite_difference_coefficient#Backward_finite_difference
71+
*speed = ((3.0 * delta_counter / 2.0) - (2.0 * previous_delta_counter) +
72+
(previous_previous_delta_counter / 2.0)) *
73+
counter_distance_m /
74+
(sample_time_s * past_delta_counters.size() / 2);
75+
76+
*acceleration = (delta_counter - (2.0 * previous_delta_counter) +
77+
previous_previous_delta_counter) *
78+
counter_distance_m /
79+
((sample_time_s * past_delta_counters.size() / 2) *
80+
(sample_time_s * past_delta_counters.size() / 2));
81+
82+
*direction = Encoder::get_direction(encoder_id) ? FORWARD : BACKWARDS;
1283

13-
#define COUNTER_DISTANCE_IN_METERS 0.0001
14-
#define N_FRAMES 100
15-
#define FRAME_SIZE_IN_SECONDS 0.005
16-
#define START_COUNTER UINT32_MAX / 2
17-
18-
19-
20-
class EncoderSensor{
21-
public:
22-
EncoderSensor() = default;
23-
EncoderSensor(Pin pin1, Pin pin2, double* position, bool* direction, double* speed, double* acceleration);
24-
void start();
25-
void read();
26-
void reset();
27-
uint8_t get_id();
28-
29-
protected:
30-
uint8_t id;
31-
double* position;
32-
bool* direction;
33-
double* speed;
34-
double* acceleration;
35-
double time;
36-
// double positions[N_FRAMES];
37-
// double times[N_FRAMES];
38-
// double speeds[N_FRAMES];
39-
RingBuffer<double, N_FRAMES> positions;
40-
RingBuffer<double, N_FRAMES> times;
41-
RingBuffer<double, N_FRAMES> speeds;
42-
uint64_t last_clock_time;
43-
44-
private:
45-
void update_arrays();
84+
past_delta_counters.push_pop(delta_counter);
85+
}
4686
};

Inc/ST-LIB_LOW/Sensors/PWMSensor/PWMSensor.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class PWMSensor {
2727

2828
template<class Type>
2929
PWMSensor<Type>::PWMSensor(Pin &pin, Type &frequency, Type &duty_cycle) :
30-
frequency(&frequency), duty_cycle(&duty_cycle) {
30+
duty_cycle(&duty_cycle), frequency(&frequency) {
3131
id = InputCapture::inscribe(pin);
3232
Sensor::inputcapture_id_list.push_back(id);
3333
}

Src/HALAL/Services/Encoder/Encoder.cpp

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,6 @@ void Encoder::reset(uint8_t id) {
8686
}
8787

8888
uint32_t Encoder::get_counter(uint8_t id) {
89-
// if (not Encoder::registered_encoder.contains(id)) {
90-
// ErrorHandler("No encoder registered with id %u", id);
91-
// return 0;
92-
// }
93-
9489
TimerPeripheral* timer = pin_timer_map[registered_encoder[id]];
9590

9691
return timer->handle->Instance->CNT;
@@ -146,12 +141,16 @@ void Encoder::init(TimerPeripheral* encoder) {
146141
}
147142
}
148143

149-
int64_t Encoder::get_delta_clock(uint64_t clock_time, uint64_t last_clock_time){
150-
int64_t delta_clock = clock_time - last_clock_time;
151-
if(clock_time < last_clock_time){ //overflow handle
152-
delta_clock = clock_time + CLOCK_MAX_VALUE * NANO_SECOND / HAL_RCC_GetPCLK1Freq()*2 - last_clock_time;
153-
}
154-
return delta_clock;
155-
}
144+
int64_t Encoder::get_delta_clock(uint64_t clock_time,
145+
uint64_t last_clock_time) {
146+
int64_t delta_clock = clock_time - last_clock_time;
147+
if (clock_time < last_clock_time) { // overflow handle
148+
delta_clock =
149+
clock_time +
150+
CLOCK_MAX_VALUE * NANO_SECOND / HAL_RCC_GetPCLK1Freq() * 2 -
151+
last_clock_time;
152+
}
153+
return delta_clock;
154+
}
156155

157156
#endif

Src/ST-LIB_LOW/Sensors/EncoderSensor/EncoderSensor.cpp

Lines changed: 0 additions & 60 deletions
This file was deleted.

0 commit comments

Comments
 (0)